Index: stable/10/contrib/libarchive/cpio/test/test_option_t.c =================================================================== --- stable/10/contrib/libarchive/cpio/test/test_option_t.c (revision 344673) +++ stable/10/contrib/libarchive/cpio/test/test_option_t.c (revision 344674) @@ -1,104 +1,104 @@ /*- * 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$"); #ifdef HAVE_LOCALE_H #include #endif DEFINE_TEST(test_option_t) { char *p; int r; time_t mtime; char date[32]; char date2[32]; /* List reference archive, make sure the TOC is correct. */ extract_reference_file("test_option_t.cpio"); r = systemf("%s -it < test_option_t.cpio >it.out 2>it.err", testprog); assertEqualInt(r, 0); assertTextFileContents("1 block\n", "it.err"); extract_reference_file("test_option_t.stdout"); p = slurpfile(NULL, "test_option_t.stdout"); assertTextFileContents(p, "it.out"); free(p); /* We accept plain "-t" as a synonym for "-it" */ r = systemf("%s -t < test_option_t.cpio >t.out 2>t.err", testprog); assertEqualInt(r, 0); assertTextFileContents("1 block\n", "t.err"); extract_reference_file("test_option_t.stdout"); p = slurpfile(NULL, "test_option_t.stdout"); assertTextFileContents(p, "t.out"); free(p); /* But "-ot" is an error. */ assert(0 != systemf("%s -ot < test_option_t.cpio >ot.out 2>ot.err", testprog)); assertEmptyFile("ot.out"); /* List reference archive verbosely, make sure the TOC is correct. */ r = systemf("%s -itv < test_option_t.cpio >tv.out 2>tv.err", testprog); assertEqualInt(r, 0); assertTextFileContents("1 block\n", "tv.err"); extract_reference_file("test_option_tv.stdout"); /* This doesn't work because the usernames on different systems * are different and cpio now looks up numeric UIDs on * the local system. */ /* assertEqualFile("tv.out", "test_option_tv.stdout"); */ /* List reference archive with numeric IDs, verify TOC is correct. */ r = systemf("%s -itnv < test_option_t.cpio >itnv.out 2>itnv.err", testprog); assertEqualInt(r, 0); assertTextFileContents("1 block\n", "itnv.err"); p = slurpfile(NULL, "itnv.out"); /* Since -n uses numeric UID/GID, this part should be the * same on every system. */ assertEqualMem(p, "-rw-r--r-- 1 1000 1000 0 ",42); /* Date varies depending on local timezone and locale. */ mtime = 1; #ifdef HAVE_LOCALE_H setlocale(LC_ALL, ""); #endif #if defined(_WIN32) && !defined(__CYGWIN__) - strftime(date2, sizeof(date), "%b %d %Y", localtime(&mtime)); - _snprintf(date, sizeof(date)-1, "%12s file", date2); + strftime(date2, sizeof(date2)-1, "%b %d %Y", localtime(&mtime)); + _snprintf(date, sizeof(date)-1, "%12.12s file", date2); #else - strftime(date2, sizeof(date), "%b %e %Y", localtime(&mtime)); - snprintf(date, sizeof(date)-1, "%12s file", date2); + strftime(date2, sizeof(date2)-1, "%b %e %Y", localtime(&mtime)); + snprintf(date, sizeof(date)-1, "%12.12s file", date2); #endif assertEqualMem(p + 42, date, strlen(date)); free(p); /* But "-n" without "-t" is an error. */ assert(0 != systemf("%s -in < test_option_t.cpio >in.out 2>in.err", testprog)); assertEmptyFile("in.out"); } Index: stable/10/contrib/libarchive/libarchive/archive_acl.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_acl.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_acl.c (revision 344674) @@ -1,2077 +1,2069 @@ /*- * 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 * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_WCHAR_H #include #endif #include "archive_acl_private.h" #include "archive_entry.h" #include "archive_private.h" #undef max #define max(a, b) ((a)>(b)?(a):(b)) #ifndef HAVE_WMEMCMP /* Good enough for simple equality testing, but not for sorting. */ #define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t)) #endif static int acl_special(struct archive_acl *acl, int type, int permset, int tag); static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl, int type, int permset, int tag, int id); 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 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 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); static const struct { const int perm; const char c; const wchar_t wc; } nfsv4_acl_perm_map[] = { { ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r', L'r' }, { ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w', L'w' }, { ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' }, { ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, 'p', L'p' }, { ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' }, { ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' }, { ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' }, { ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' }, { ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' }, { ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' }, { ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' }, { ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' }, { ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' }, { ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' } }; static const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) / sizeof(nfsv4_acl_perm_map[0])); static const struct { const int perm; const char c; const wchar_t wc; } nfsv4_acl_flag_map[] = { { ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' }, { ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' }, { ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' }, { ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' }, { ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' }, { ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' }, { ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' } }; static const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) / sizeof(nfsv4_acl_flag_map[0])); void archive_acl_clear(struct archive_acl *acl) { struct archive_acl_entry *ap; while (acl->acl_head != NULL) { ap = acl->acl_head->next; archive_mstring_clean(&acl->acl_head->name); free(acl->acl_head); acl->acl_head = ap; } - if (acl->acl_text_w != NULL) { - free(acl->acl_text_w); - acl->acl_text_w = NULL; - } - if (acl->acl_text != NULL) { - free(acl->acl_text); - acl->acl_text = NULL; - } + free(acl->acl_text_w); + acl->acl_text_w = NULL; + free(acl->acl_text); + acl->acl_text = NULL; acl->acl_p = NULL; acl->acl_types = 0; acl->acl_state = 0; /* Not counting. */ } void archive_acl_copy(struct archive_acl *dest, struct archive_acl *src) { struct archive_acl_entry *ap, *ap2; archive_acl_clear(dest); dest->mode = src->mode; ap = src->acl_head; while (ap != NULL) { ap2 = acl_new_entry(dest, ap->type, ap->permset, ap->tag, ap->id); if (ap2 != NULL) archive_mstring_copy(&ap2->name, &ap->name); ap = ap->next; } } int archive_acl_add_entry(struct archive_acl *acl, int type, int permset, int tag, int id, const char *name) { struct archive_acl_entry *ap; if (acl_special(acl, type, permset, tag) == 0) return ARCHIVE_OK; ap = acl_new_entry(acl, type, permset, tag, id); if (ap == NULL) { /* XXX Error XXX */ return ARCHIVE_FAILED; } if (name != NULL && *name != '\0') archive_mstring_copy_mbs(&ap->name, name); else archive_mstring_clean(&ap->name); return ARCHIVE_OK; } int archive_acl_add_entry_w_len(struct archive_acl *acl, int type, int permset, int tag, int id, const wchar_t *name, size_t len) { struct archive_acl_entry *ap; if (acl_special(acl, type, permset, tag) == 0) return ARCHIVE_OK; ap = acl_new_entry(acl, type, permset, tag, id); if (ap == NULL) { /* XXX Error XXX */ return ARCHIVE_FAILED; } if (name != NULL && *name != L'\0' && len > 0) archive_mstring_copy_wcs_len(&ap->name, name, len); else archive_mstring_clean(&ap->name); return ARCHIVE_OK; } 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) { struct archive_acl_entry *ap; int r; if (acl_special(acl, type, permset, tag) == 0) return ARCHIVE_OK; ap = acl_new_entry(acl, type, permset, tag, id); if (ap == NULL) { /* XXX Error XXX */ return ARCHIVE_FAILED; } if (name != NULL && *name != '\0' && len > 0) { r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc); } else { r = 0; archive_mstring_clean(&ap->name); } if (r == 0) return (ARCHIVE_OK); else if (errno == ENOMEM) return (ARCHIVE_FATAL); else return (ARCHIVE_WARN); } /* * If this ACL entry is part of the standard POSIX permissions set, * store the permissions in the stat structure and return zero. */ static int acl_special(struct archive_acl *acl, int type, int permset, int tag) { if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS && ((permset & ~007) == 0)) { switch (tag) { case ARCHIVE_ENTRY_ACL_USER_OBJ: acl->mode &= ~0700; acl->mode |= (permset & 7) << 6; return (0); case ARCHIVE_ENTRY_ACL_GROUP_OBJ: acl->mode &= ~0070; acl->mode |= (permset & 7) << 3; return (0); case ARCHIVE_ENTRY_ACL_OTHER: acl->mode &= ~0007; acl->mode |= permset & 7; return (0); } } return (1); } /* * Allocate and populate a new ACL entry with everything but the * name. */ static struct archive_acl_entry * acl_new_entry(struct archive_acl *acl, int type, int permset, int tag, int id) { struct archive_acl_entry *ap, *aq; /* Type argument must be a valid NFS4 or POSIX.1e type. * The type must agree with anything already set and * the permset must be compatible. */ if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { return (NULL); } if (permset & ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4 | ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) { return (NULL); } } else if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { return (NULL); } if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) { return (NULL); } } else { return (NULL); } /* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */ switch (tag) { case ARCHIVE_ENTRY_ACL_USER: case ARCHIVE_ENTRY_ACL_USER_OBJ: case ARCHIVE_ENTRY_ACL_GROUP: case ARCHIVE_ENTRY_ACL_GROUP_OBJ: /* Tags valid in both NFS4 and POSIX.1e */ break; case ARCHIVE_ENTRY_ACL_MASK: case ARCHIVE_ENTRY_ACL_OTHER: /* Tags valid only in POSIX.1e. */ if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) { return (NULL); } break; case ARCHIVE_ENTRY_ACL_EVERYONE: /* Tags valid only in NFS4. */ if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { return (NULL); } break; default: /* No other values are valid. */ return (NULL); } - if (acl->acl_text_w != NULL) { - free(acl->acl_text_w); - acl->acl_text_w = NULL; - } - if (acl->acl_text != NULL) { - free(acl->acl_text); - acl->acl_text = NULL; - } + free(acl->acl_text_w); + acl->acl_text_w = NULL; + free(acl->acl_text); + acl->acl_text = NULL; /* * If there's a matching entry already in the list, overwrite it. * NFSv4 entries may be repeated and are not overwritten. * * TODO: compare names of no id is provided (needs more rework) */ ap = acl->acl_head; aq = NULL; while (ap != NULL) { if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) && ap->type == type && ap->tag == tag && ap->id == id) { if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER && tag != ARCHIVE_ENTRY_ACL_GROUP)) { ap->permset = permset; return (ap); } } aq = ap; ap = ap->next; } /* Add a new entry to the end of the list. */ ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap)); if (ap == NULL) return (NULL); if (aq == NULL) acl->acl_head = ap; else aq->next = ap; ap->type = type; ap->tag = tag; ap->id = id; ap->permset = permset; acl->acl_types |= type; return (ap); } /* * Return a count of entries matching "want_type". */ int archive_acl_count(struct archive_acl *acl, int want_type) { int count; struct archive_acl_entry *ap; count = 0; ap = acl->acl_head; while (ap != NULL) { if ((ap->type & want_type) != 0) count++; ap = ap->next; } if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) count += 3; return (count); } /* * Return a bitmask of stored ACL types in an ACL list */ int archive_acl_types(struct archive_acl *acl) { return (acl->acl_types); } /* * Prepare for reading entries from the ACL data. Returns a count * of entries matching "want_type", or zero if there are no * non-extended ACL entries of that type. */ int archive_acl_reset(struct archive_acl *acl, int want_type) { int count, cutoff; count = archive_acl_count(acl, want_type); /* * If the only entries are the three standard ones, * then don't return any ACL data. (In this case, * client can just use chmod(2) to set permissions.) */ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) cutoff = 3; else cutoff = 0; if (count > cutoff) acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ; else acl->acl_state = 0; acl->acl_p = acl->acl_head; return (count); } /* * Return the next ACL entry in the list. Fake entries for the * 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) { *name = NULL; *id = -1; /* * The acl_state is either zero (no entries available), -1 * (reading from list), or an entry type (retrieve that type * from ae_stat.aest_mode). */ if (acl->acl_state == 0) return (ARCHIVE_WARN); /* The first three access entries are special. */ if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { switch (acl->acl_state) { case ARCHIVE_ENTRY_ACL_USER_OBJ: *permset = (acl->mode >> 6) & 7; *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; *tag = ARCHIVE_ENTRY_ACL_USER_OBJ; acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ; return (ARCHIVE_OK); case ARCHIVE_ENTRY_ACL_GROUP_OBJ: *permset = (acl->mode >> 3) & 7; *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER; return (ARCHIVE_OK); case ARCHIVE_ENTRY_ACL_OTHER: *permset = acl->mode & 7; *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; *tag = ARCHIVE_ENTRY_ACL_OTHER; acl->acl_state = -1; acl->acl_p = acl->acl_head; return (ARCHIVE_OK); default: break; } } while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0) acl->acl_p = acl->acl_p->next; if (acl->acl_p == NULL) { acl->acl_state = 0; *type = 0; *permset = 0; *tag = 0; *id = -1; *name = NULL; return (ARCHIVE_EOF); /* End of ACL entries. */ } *type = acl->acl_p->type; *permset = acl->acl_p->permset; *tag = acl->acl_p->tag; *id = acl->acl_p->id; if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) { if (errno == ENOMEM) return (ARCHIVE_FATAL); *name = NULL; } acl->acl_p = acl->acl_p->next; return (ARCHIVE_OK); } /* * Determine what type of ACL do we want */ static int archive_acl_text_want_type(struct archive_acl *acl, int flags) { int want_type; /* 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); } /* 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; 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 */ /* ID digit count */ idlen = 1; tmp = ap->id; while (tmp > 9) { tmp = tmp / 10; idlen++; } length += idlen; } length ++; /* entry separator */ } /* 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); } /* * 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 = ws = (wchar_t *)malloc(length * sizeof(wchar_t)); if (wp == NULL) { if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } count = 0; 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++ = separator; append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL, acl->mode & 0070, -1); *wp++ = separator; append_entry_w(&wp, 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 & 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; 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); } /* 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) { if (id < 0) id = 0; if (id > 9) append_id_w(wp, id / 10); *(*wp)++ = L"0123456789"[id % 10]; } 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) { int i; if (prefix != NULL) { wcscpy(*wp, prefix); *wp += wcslen(*wp); } switch (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"); break; 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"); break; case ARCHIVE_ENTRY_ACL_MASK: wcscpy(*wp, L"mask"); wname = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_OTHER: wcscpy(*wp, L"other"); 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 (((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); if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) 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 { /* NFSv4 ACL perms */ for (i = 0; i < nfsv4_acl_perm_map_size; i++) { if (perm & nfsv4_acl_perm_map[i].perm) *(*wp)++ = nfsv4_acl_perm_map[i].wc; else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) *(*wp)++ = L'-'; } *(*wp)++ = L':'; for (i = 0; i < nfsv4_acl_flag_map_size; i++) { if (perm & nfsv4_acl_flag_map[i].perm) *(*wp)++ = nfsv4_acl_flag_map[i].wc; else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) *(*wp)++ = 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); } if (id != -1) { *(*wp)++ = L':'; append_id_w(wp, id); } } /* * 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; ssize_t length; size_t len; const char *name; const char *prefix; char separator; struct archive_acl_entry *ap; int id, r, want_type; char *p, *s; 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, 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 = s = (char *)malloc(length * sizeof(char)); if (p == NULL) { if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } count = 0; 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++ = separator; append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS, ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL, acl->mode & 0070, -1); *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 & 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; 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++; } /* 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 append_id(char **p, int id) { if (id < 0) id = 0; if (id > 9) append_id(p, id / 10); *(*p)++ = "0123456789"[id % 10]; } static void append_entry(char **p, const char *prefix, int type, int tag, int flags, const char *name, int perm, int id) { int i; if (prefix != NULL) { strcpy(*p, prefix); *p += strlen(*p); } switch (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"); break; 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"); break; case ARCHIVE_ENTRY_ACL_MASK: strcpy(*p, "mask"); name = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_OTHER: strcpy(*p, "other"); name = NULL; id = -1; break; case ARCHIVE_ENTRY_ACL_EVERYONE: strcpy(*p, "everyone@"); name = NULL; id = -1; break; } *p += strlen(*p); *(*p)++ = ':'; 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); if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) 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 { /* NFSv4 ACL perms */ for (i = 0; i < nfsv4_acl_perm_map_size; i++) { if (perm & nfsv4_acl_perm_map[i].perm) *(*p)++ = nfsv4_acl_perm_map[i].c; else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) *(*p)++ = '-'; } *(*p)++ = ':'; for (i = 0; i < nfsv4_acl_flag_map_size; i++) { if (perm & nfsv4_acl_flag_map[i].perm) *(*p)++ = nfsv4_acl_flag_map[i].c; else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0) *(*p)++ = '-'; } *(*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); } if (id != -1) { *(*p)++ = ':'; append_id(p, id); } } /* * 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_from_text_w(struct archive_acl *acl, const wchar_t *text, int want_type) { struct { const wchar_t *start; const wchar_t *end; } field[6], name; const wchar_t *s, *st; int numfields, fields, n, r, sol, ret; int type, types, tag, permset, id; size_t len; wchar_t sep; ret = ARCHIVE_OK; types = 0; switch (want_type) { case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; __LA_FALLTHROUGH; 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. */ fields = 0; do { const wchar_t *start, *end; next_field_w(&text, &start, &end, &sep); if (fields < numfields) { field[fields].start = start; field[fields].end = end; } ++fields; } while (sep == L':'); /* Set remaining fields to blank. */ for (n = fields; n < numfields; ++n) field[n].start = field[n].end = NULL; if (field[0].start != NULL && *(field[0].start) == L'#') { /* Comment, skip entry */ continue; } n = 0; sol = 0; id = -1; permset = 0; name.start = name.end = NULL; 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 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" */ sol = 1; } 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 * Exception: Solaris other and mask fields */ if (permset == 0 && !ismode_w(field[n + 2 - sol].start, field[n + 2 - sol].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]; isint_w(name.start, name.end, &id); } else 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. */ 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; } /* Reset ACL */ archive_acl_reset(acl, types); return (ret); } /* * 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 / 10 && (*start - '0') > INT_MAX % 10)) { n = INT_MAX; } else { n *= 10; n += *start - '0'; } start++; } *result = n; 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; if (start >= end) return (0); p = start; *permset = 0; while (p < end) { switch (*p++) { case L'r': case L'R': *permset |= ARCHIVE_ENTRY_ACL_READ; break; case L'w': case L'W': *permset |= ARCHIVE_ENTRY_ACL_WRITE; break; case L'x': case L'X': *permset |= ARCHIVE_ENTRY_ACL_EXECUTE; break; 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 = 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 = 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); } } 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 * character of the matched identifier. In particular *end - *start * is the length of the field body, not including leading or trailing * whitespace. */ static void next_field_w(const wchar_t **wp, const wchar_t **start, const wchar_t **end, wchar_t *sep) { /* Skip leading whitespace to find start of field. */ while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') { (*wp)++; } *start = *wp; /* Scan for the separator. */ while (**wp != L'\0' && **wp != L',' && **wp != L':' && **wp != L'\n') { (*wp)++; } *sep = **wp; /* Trim trailing whitespace to locate end of field. */ *end = *wp - 1; while (**end == L' ' || **end == L'\t' || **end == L'\n') { (*end)--; } (*end)++; /* Adjust scanner location. */ if (**wp != L'\0') (*wp)++; } /* * 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_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[6], name; const char *s, *st; int numfields, fields, n, r, sol, 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; __LA_FALLTHROUGH; 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, * advance 'text' to start of next entry. */ fields = 0; do { const char *start, *end; next_field(&text, &start, &end, &sep); if (fields < numfields) { field[fields].start = start; field[fields].end = end; } ++fields; } while (sep == ':'); /* Set remaining fields to blank. */ for (n = fields; n < numfields; ++n) field[n].start = field[n].end = NULL; if (field[0].start != NULL && *(field[0].start) == '#') { /* Comment, skip entry */ continue; } n = 0; sol = 0; id = -1; permset = 0; name.start = name.end = NULL; 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 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" */ sol = 1; } 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 3 * Exception: Solaris other and mask fields */ if (permset == 0 && !ismode(field[n + 2 - sol].start, field[n + 2 - sol].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]; isint(name.start, name.end, &id); } else 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, tag, id, name.start, name.end - name.start, sc); if (r < ARCHIVE_WARN) return (r); if (r != ARCHIVE_OK) ret = ARCHIVE_WARN; types |= type; } /* Reset ACL */ archive_acl_reset(acl, types); return (ret); } /* * 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(const char *start, const char *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 / 10 && (*start - '0') > INT_MAX % 10)) { n = INT_MAX; } else { n *= 10; n += *start - '0'; } start++; } *result = n; 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(const char *start, const char *end, int *permset) { const char *p; if (start >= end) return (0); 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); } /* * 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 = 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 = 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 * character of the matched text and *end just after the last * character of the matched identifier. In particular *end - *start * is the length of the field body, not including leading or trailing * whitespace. */ static void next_field(const char **p, const char **start, const char **end, char *sep) { /* Skip leading whitespace to find start of field. */ while (**p == ' ' || **p == '\t' || **p == '\n') { (*p)++; } *start = *p; /* Scan for the separator. */ while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n') { (*p)++; } *sep = **p; /* If the field is only whitespace, bail out now. */ if (**p == '\0') { *end = *p; return; } /* Trim trailing whitespace to locate end of field. */ *end = *p - 1; while (**end == ' ' || **end == '\t' || **end == '\n') { (*end)--; } (*end)++; /* Adjust scanner location. */ if (**p != '\0') (*p)++; } Index: stable/10/contrib/libarchive/libarchive/archive_entry.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_entry.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_entry.c (revision 344674) @@ -1,2038 +1,2032 @@ /*- * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #if MAJOR_IN_MKDEV #include #define HAVE_MAJOR #elif MAJOR_IN_SYSMACROS #include #define HAVE_MAJOR #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_LINUX_FS_H #include /* for Linux file flags */ #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* for Linux file flags */ #endif #include #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_WCHAR_H #include #endif #include "archive.h" #include "archive_acl_private.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_entry_private.h" #if !defined(HAVE_MAJOR) && !defined(major) /* Replacement for major/minor/makedev. */ #define major(x) ((int)(0x00ff & ((x) >> 8))) #define minor(x) ((int)(0xffff00ff & (x))) #define makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min))) #endif /* Play games to come up with a suitable makedev() definition. */ #ifdef __QNXNTO__ /* QNX. */ #include #define ae_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min)) #elif defined makedev /* There's a "makedev" macro. */ #define ae_makedev(maj, min) makedev((maj), (min)) #elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__)) /* Windows. */ #define ae_makedev(maj, min) mkdev((maj), (min)) #else /* There's a "makedev" function. */ #define ae_makedev(maj, min) makedev((maj), (min)) #endif /* * This adjustment is needed to support the following idiom for adding * 1000ns to the stored time: * archive_entry_set_atime(archive_entry_atime(), * archive_entry_atime_nsec() + 1000) * The additional if() here compensates for ambiguity in the C standard, * which permits two possible interpretations of a % b when a is negative. */ #define FIX_NS(t,ns) \ do { \ t += ns / 1000000000; \ ns %= 1000000000; \ if (ns < 0) { --t; ns += 1000000000; } \ } while (0) static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear); static const wchar_t *ae_wcstofflags(const wchar_t *stringp, unsigned long *setp, unsigned long *clrp); static const char *ae_strtofflags(const char *stringp, unsigned long *setp, unsigned long *clrp); #ifndef HAVE_WCSCPY static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2) { wchar_t *dest = s1; while ((*s1 = *s2) != L'\0') ++s1, ++s2; return dest; } #endif #ifndef HAVE_WCSLEN static size_t wcslen(const wchar_t *s) { const wchar_t *p = s; while (*p != L'\0') ++p; return p - s; } #endif #ifndef HAVE_WMEMCMP /* Good enough for simple equality testing, but not for sorting. */ #define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t)) #endif /**************************************************************************** * * Public Interface * ****************************************************************************/ struct archive_entry * archive_entry_clear(struct archive_entry *entry) { if (entry == NULL) return (NULL); archive_mstring_clean(&entry->ae_fflags_text); archive_mstring_clean(&entry->ae_gname); archive_mstring_clean(&entry->ae_hardlink); archive_mstring_clean(&entry->ae_pathname); archive_mstring_clean(&entry->ae_sourcepath); archive_mstring_clean(&entry->ae_symlink); archive_mstring_clean(&entry->ae_uname); archive_entry_copy_mac_metadata(entry, NULL, 0); archive_acl_clear(&entry->acl); archive_entry_xattr_clear(entry); archive_entry_sparse_clear(entry); free(entry->stat); memset(entry, 0, sizeof(*entry)); return entry; } struct archive_entry * archive_entry_clone(struct archive_entry *entry) { struct archive_entry *entry2; struct ae_xattr *xp; struct ae_sparse *sp; size_t s; const void *p; /* Allocate new structure and copy over all of the fields. */ /* TODO: Should we copy the archive over? Or require a new archive * as an argument? */ entry2 = archive_entry_new2(entry->archive); if (entry2 == NULL) return (NULL); entry2->ae_stat = entry->ae_stat; entry2->ae_fflags_set = entry->ae_fflags_set; entry2->ae_fflags_clear = entry->ae_fflags_clear; /* TODO: XXX If clone can have a different archive, what do we do here if * character sets are different? XXX */ archive_mstring_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text); archive_mstring_copy(&entry2->ae_gname, &entry->ae_gname); archive_mstring_copy(&entry2->ae_hardlink, &entry->ae_hardlink); archive_mstring_copy(&entry2->ae_pathname, &entry->ae_pathname); archive_mstring_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath); archive_mstring_copy(&entry2->ae_symlink, &entry->ae_symlink); entry2->ae_set = entry->ae_set; archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname); /* Copy encryption status */ entry2->encryption = entry->encryption; /* Copy ACL data over. */ archive_acl_copy(&entry2->acl, &entry->acl); /* Copy Mac OS metadata. */ p = archive_entry_mac_metadata(entry, &s); archive_entry_copy_mac_metadata(entry2, p, s); /* Copy xattr data over. */ xp = entry->xattr_head; while (xp != NULL) { archive_entry_xattr_add_entry(entry2, xp->name, xp->value, xp->size); xp = xp->next; } /* Copy sparse data over. */ sp = entry->sparse_head; while (sp != NULL) { archive_entry_sparse_add_entry(entry2, sp->offset, sp->length); sp = sp->next; } return (entry2); } void archive_entry_free(struct archive_entry *entry) { archive_entry_clear(entry); free(entry); } struct archive_entry * archive_entry_new(void) { return archive_entry_new2(NULL); } struct archive_entry * archive_entry_new2(struct archive *a) { struct archive_entry *entry; entry = (struct archive_entry *)calloc(1, sizeof(*entry)); if (entry == NULL) return (NULL); entry->archive = a; return (entry); } /* * Functions for reading fields from an archive_entry. */ time_t archive_entry_atime(struct archive_entry *entry) { return (entry->ae_stat.aest_atime); } long archive_entry_atime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_atime_nsec); } int archive_entry_atime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_ATIME); } time_t archive_entry_birthtime(struct archive_entry *entry) { return (entry->ae_stat.aest_birthtime); } long archive_entry_birthtime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_birthtime_nsec); } int archive_entry_birthtime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_BIRTHTIME); } time_t archive_entry_ctime(struct archive_entry *entry) { return (entry->ae_stat.aest_ctime); } int archive_entry_ctime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_CTIME); } long archive_entry_ctime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_ctime_nsec); } dev_t archive_entry_dev(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return ae_makedev(entry->ae_stat.aest_devmajor, entry->ae_stat.aest_devminor); else return (entry->ae_stat.aest_dev); } int archive_entry_dev_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_DEV); } dev_t archive_entry_devmajor(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return (entry->ae_stat.aest_devmajor); else return major(entry->ae_stat.aest_dev); } dev_t archive_entry_devminor(struct archive_entry *entry) { if (entry->ae_stat.aest_dev_is_broken_down) return (entry->ae_stat.aest_devminor); else return minor(entry->ae_stat.aest_dev); } mode_t archive_entry_filetype(struct archive_entry *entry) { return (AE_IFMT & entry->acl.mode); } void archive_entry_fflags(struct archive_entry *entry, unsigned long *set, unsigned long *clear) { *set = entry->ae_fflags_set; *clear = entry->ae_fflags_clear; } /* * Note: if text was provided, this just returns that text. If you * really need the text to be rebuilt in a canonical form, set the * text, ask for the bitmaps, then set the bitmaps. (Setting the * bitmaps clears any stored text.) This design is deliberate: if * we're editing archives, we don't want to discard flags just because * they aren't supported on the current system. The bitmap<->text * conversions are platform-specific (see below). */ const char * archive_entry_fflags_text(struct archive_entry *entry) { const char *f; char *p; if (archive_mstring_get_mbs(entry->archive, &entry->ae_fflags_text, &f) == 0) { if (f != NULL) return (f); } else if (errno == ENOMEM) __archive_errx(1, "No memory"); if (entry->ae_fflags_set == 0 && entry->ae_fflags_clear == 0) return (NULL); p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear); if (p == NULL) return (NULL); archive_mstring_copy_mbs(&entry->ae_fflags_text, p); free(p); if (archive_mstring_get_mbs(entry->archive, &entry->ae_fflags_text, &f) == 0) return (f); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } la_int64_t archive_entry_gid(struct archive_entry *entry) { return (entry->ae_stat.aest_gid); } const char * archive_entry_gname(struct archive_entry *entry) { const char *p; if (archive_mstring_get_mbs(entry->archive, &entry->ae_gname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_gname_utf8(struct archive_entry *entry) { const char *p; if (archive_mstring_get_utf8(entry->archive, &entry->ae_gname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_gname_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs(entry->archive, &entry->ae_gname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_gname_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { return (archive_mstring_get_mbs_l(&entry->ae_gname, p, len, sc)); } const char * archive_entry_hardlink(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_mbs( entry->archive, &entry->ae_hardlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_hardlink_utf8(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_utf8( entry->archive, &entry->ae_hardlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_hardlink_w(struct archive_entry *entry) { const wchar_t *p; if ((entry->ae_set & AE_SET_HARDLINK) == 0) return (NULL); if (archive_mstring_get_wcs( entry->archive, &entry->ae_hardlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_hardlink_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { if ((entry->ae_set & AE_SET_HARDLINK) == 0) { *p = NULL; *len = 0; return (0); } return (archive_mstring_get_mbs_l(&entry->ae_hardlink, p, len, sc)); } la_int64_t archive_entry_ino(struct archive_entry *entry) { return (entry->ae_stat.aest_ino); } int archive_entry_ino_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_INO); } la_int64_t archive_entry_ino64(struct archive_entry *entry) { return (entry->ae_stat.aest_ino); } mode_t archive_entry_mode(struct archive_entry *entry) { return (entry->acl.mode); } time_t archive_entry_mtime(struct archive_entry *entry) { return (entry->ae_stat.aest_mtime); } long archive_entry_mtime_nsec(struct archive_entry *entry) { return (entry->ae_stat.aest_mtime_nsec); } int archive_entry_mtime_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_MTIME); } unsigned int archive_entry_nlink(struct archive_entry *entry) { return (entry->ae_stat.aest_nlink); } const char * archive_entry_pathname(struct archive_entry *entry) { const char *p; if (archive_mstring_get_mbs( entry->archive, &entry->ae_pathname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_pathname_utf8(struct archive_entry *entry) { const char *p; if (archive_mstring_get_utf8( entry->archive, &entry->ae_pathname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_pathname_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs( entry->archive, &entry->ae_pathname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_pathname_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { return (archive_mstring_get_mbs_l(&entry->ae_pathname, p, len, sc)); } mode_t archive_entry_perm(struct archive_entry *entry) { return (~AE_IFMT & entry->acl.mode); } dev_t archive_entry_rdev(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return ae_makedev(entry->ae_stat.aest_rdevmajor, entry->ae_stat.aest_rdevminor); else return (entry->ae_stat.aest_rdev); } dev_t archive_entry_rdevmajor(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return (entry->ae_stat.aest_rdevmajor); else return major(entry->ae_stat.aest_rdev); } dev_t archive_entry_rdevminor(struct archive_entry *entry) { if (entry->ae_stat.aest_rdev_is_broken_down) return (entry->ae_stat.aest_rdevminor); else return minor(entry->ae_stat.aest_rdev); } la_int64_t archive_entry_size(struct archive_entry *entry) { return (entry->ae_stat.aest_size); } int archive_entry_size_is_set(struct archive_entry *entry) { return (entry->ae_set & AE_SET_SIZE); } const char * archive_entry_sourcepath(struct archive_entry *entry) { const char *p; if (archive_mstring_get_mbs( entry->archive, &entry->ae_sourcepath, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_sourcepath_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs( entry->archive, &entry->ae_sourcepath, &p) == 0) return (p); return (NULL); } const char * archive_entry_symlink(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_mbs( entry->archive, &entry->ae_symlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_symlink_utf8(struct archive_entry *entry) { const char *p; if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_utf8( entry->archive, &entry->ae_symlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_symlink_w(struct archive_entry *entry) { const wchar_t *p; if ((entry->ae_set & AE_SET_SYMLINK) == 0) return (NULL); if (archive_mstring_get_wcs( entry->archive, &entry->ae_symlink, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_symlink_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { if ((entry->ae_set & AE_SET_SYMLINK) == 0) { *p = NULL; *len = 0; return (0); } return (archive_mstring_get_mbs_l( &entry->ae_symlink, p, len, sc)); } la_int64_t archive_entry_uid(struct archive_entry *entry) { return (entry->ae_stat.aest_uid); } const char * archive_entry_uname(struct archive_entry *entry) { const char *p; if (archive_mstring_get_mbs(entry->archive, &entry->ae_uname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const char * archive_entry_uname_utf8(struct archive_entry *entry) { const char *p; if (archive_mstring_get_utf8(entry->archive, &entry->ae_uname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } const wchar_t * archive_entry_uname_w(struct archive_entry *entry) { const wchar_t *p; if (archive_mstring_get_wcs(entry->archive, &entry->ae_uname, &p) == 0) return (p); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (NULL); } int _archive_entry_uname_l(struct archive_entry *entry, const char **p, size_t *len, struct archive_string_conv *sc) { return (archive_mstring_get_mbs_l(&entry->ae_uname, p, len, sc)); } int archive_entry_is_data_encrypted(struct archive_entry *entry) { return ((entry->encryption & AE_ENCRYPTION_DATA) == AE_ENCRYPTION_DATA); } int archive_entry_is_metadata_encrypted(struct archive_entry *entry) { return ((entry->encryption & AE_ENCRYPTION_METADATA) == AE_ENCRYPTION_METADATA); } int archive_entry_is_encrypted(struct archive_entry *entry) { return (entry->encryption & (AE_ENCRYPTION_DATA|AE_ENCRYPTION_METADATA)); } /* * Functions to set archive_entry properties. */ void archive_entry_set_filetype(struct archive_entry *entry, unsigned int type) { entry->stat_valid = 0; entry->acl.mode &= ~AE_IFMT; entry->acl.mode |= AE_IFMT & type; } void archive_entry_set_fflags(struct archive_entry *entry, unsigned long set, unsigned long clear) { archive_mstring_clean(&entry->ae_fflags_text); entry->ae_fflags_set = set; entry->ae_fflags_clear = clear; } const char * archive_entry_copy_fflags_text(struct archive_entry *entry, const char *flags) { archive_mstring_copy_mbs(&entry->ae_fflags_text, flags); return (ae_strtofflags(flags, &entry->ae_fflags_set, &entry->ae_fflags_clear)); } const wchar_t * archive_entry_copy_fflags_text_w(struct archive_entry *entry, const wchar_t *flags) { archive_mstring_copy_wcs(&entry->ae_fflags_text, flags); return (ae_wcstofflags(flags, &entry->ae_fflags_set, &entry->ae_fflags_clear)); } void archive_entry_set_gid(struct archive_entry *entry, la_int64_t g) { entry->stat_valid = 0; entry->ae_stat.aest_gid = g; } void archive_entry_set_gname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_gname, name); } void archive_entry_set_gname_utf8(struct archive_entry *entry, const char *name) { archive_mstring_copy_utf8(&entry->ae_gname, name); } void archive_entry_copy_gname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_gname, name); } void archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name) { archive_mstring_copy_wcs(&entry->ae_gname, name); } int archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name) { if (archive_mstring_update_utf8(entry->archive, &entry->ae_gname, name) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_gname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) { return (archive_mstring_copy_mbs_len_l(&entry->ae_gname, name, len, sc)); } void archive_entry_set_ino(struct archive_entry *entry, la_int64_t ino) { entry->stat_valid = 0; entry->ae_set |= AE_SET_INO; entry->ae_stat.aest_ino = ino; } void archive_entry_set_ino64(struct archive_entry *entry, la_int64_t ino) { entry->stat_valid = 0; entry->ae_set |= AE_SET_INO; entry->ae_stat.aest_ino = ino; } void archive_entry_set_hardlink(struct archive_entry *entry, const char *target) { archive_mstring_copy_mbs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_set_hardlink_utf8(struct archive_entry *entry, const char *target) { archive_mstring_copy_utf8(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_copy_hardlink(struct archive_entry *entry, const char *target) { archive_mstring_copy_mbs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } void archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target) { archive_mstring_copy_wcs(&entry->ae_hardlink, target); if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; } int archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *target) { if (target != NULL) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; if (archive_mstring_update_utf8(entry->archive, &entry->ae_hardlink, target) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_hardlink_l(struct archive_entry *entry, const char *target, size_t len, struct archive_string_conv *sc) { int r; r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink, target, len, sc); if (target != NULL && r == 0) entry->ae_set |= AE_SET_HARDLINK; else entry->ae_set &= ~AE_SET_HARDLINK; return (r); } void archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns) { FIX_NS(t, ns); entry->stat_valid = 0; entry->ae_set |= AE_SET_ATIME; entry->ae_stat.aest_atime = t; entry->ae_stat.aest_atime_nsec = ns; } void archive_entry_unset_atime(struct archive_entry *entry) { archive_entry_set_atime(entry, 0, 0); entry->ae_set &= ~AE_SET_ATIME; } void archive_entry_set_birthtime(struct archive_entry *entry, time_t t, long ns) { FIX_NS(t, ns); entry->stat_valid = 0; entry->ae_set |= AE_SET_BIRTHTIME; entry->ae_stat.aest_birthtime = t; entry->ae_stat.aest_birthtime_nsec = ns; } void archive_entry_unset_birthtime(struct archive_entry *entry) { archive_entry_set_birthtime(entry, 0, 0); entry->ae_set &= ~AE_SET_BIRTHTIME; } void archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns) { FIX_NS(t, ns); entry->stat_valid = 0; entry->ae_set |= AE_SET_CTIME; entry->ae_stat.aest_ctime = t; entry->ae_stat.aest_ctime_nsec = ns; } void archive_entry_unset_ctime(struct archive_entry *entry) { archive_entry_set_ctime(entry, 0, 0); entry->ae_set &= ~AE_SET_CTIME; } void archive_entry_set_dev(struct archive_entry *entry, dev_t d) { entry->stat_valid = 0; entry->ae_set |= AE_SET_DEV; entry->ae_stat.aest_dev_is_broken_down = 0; entry->ae_stat.aest_dev = d; } void archive_entry_set_devmajor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_set |= AE_SET_DEV; entry->ae_stat.aest_dev_is_broken_down = 1; entry->ae_stat.aest_devmajor = m; } void archive_entry_set_devminor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_set |= AE_SET_DEV; entry->ae_stat.aest_dev_is_broken_down = 1; entry->ae_stat.aest_devminor = m; } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_set_link(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) archive_mstring_copy_mbs(&entry->ae_symlink, target); else archive_mstring_copy_mbs(&entry->ae_hardlink, target); } void archive_entry_set_link_utf8(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) archive_mstring_copy_utf8(&entry->ae_symlink, target); else archive_mstring_copy_utf8(&entry->ae_hardlink, target); } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_copy_link(struct archive_entry *entry, const char *target) { if (entry->ae_set & AE_SET_SYMLINK) archive_mstring_copy_mbs(&entry->ae_symlink, target); else archive_mstring_copy_mbs(&entry->ae_hardlink, target); } /* Set symlink if symlink is already set, else set hardlink. */ void archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target) { if (entry->ae_set & AE_SET_SYMLINK) archive_mstring_copy_wcs(&entry->ae_symlink, target); else archive_mstring_copy_wcs(&entry->ae_hardlink, target); } int archive_entry_update_link_utf8(struct archive_entry *entry, const char *target) { int r; if (entry->ae_set & AE_SET_SYMLINK) r = archive_mstring_update_utf8(entry->archive, &entry->ae_symlink, target); else r = archive_mstring_update_utf8(entry->archive, &entry->ae_hardlink, target); if (r == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_link_l(struct archive_entry *entry, const char *target, size_t len, struct archive_string_conv *sc) { int r; if (entry->ae_set & AE_SET_SYMLINK) r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink, target, len, sc); else r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink, target, len, sc); return (r); } void archive_entry_set_mode(struct archive_entry *entry, mode_t m) { entry->stat_valid = 0; entry->acl.mode = m; } void archive_entry_set_mtime(struct archive_entry *entry, time_t t, long ns) { FIX_NS(t, ns); entry->stat_valid = 0; entry->ae_set |= AE_SET_MTIME; entry->ae_stat.aest_mtime = t; entry->ae_stat.aest_mtime_nsec = ns; } void archive_entry_unset_mtime(struct archive_entry *entry) { archive_entry_set_mtime(entry, 0, 0); entry->ae_set &= ~AE_SET_MTIME; } void archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink) { entry->stat_valid = 0; entry->ae_stat.aest_nlink = nlink; } void archive_entry_set_pathname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_pathname, name); } void archive_entry_set_pathname_utf8(struct archive_entry *entry, const char *name) { archive_mstring_copy_utf8(&entry->ae_pathname, name); } void archive_entry_copy_pathname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_pathname, name); } void archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name) { archive_mstring_copy_wcs(&entry->ae_pathname, name); } int archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name) { if (archive_mstring_update_utf8(entry->archive, &entry->ae_pathname, name) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_pathname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) { return (archive_mstring_copy_mbs_len_l(&entry->ae_pathname, name, len, sc)); } void archive_entry_set_perm(struct archive_entry *entry, mode_t p) { entry->stat_valid = 0; entry->acl.mode &= AE_IFMT; entry->acl.mode |= ~AE_IFMT & p; } void archive_entry_set_rdev(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev = m; entry->ae_stat.aest_rdev_is_broken_down = 0; } void archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev_is_broken_down = 1; entry->ae_stat.aest_rdevmajor = m; } void archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m) { entry->stat_valid = 0; entry->ae_stat.aest_rdev_is_broken_down = 1; entry->ae_stat.aest_rdevminor = m; } void archive_entry_set_size(struct archive_entry *entry, la_int64_t s) { entry->stat_valid = 0; entry->ae_stat.aest_size = s; entry->ae_set |= AE_SET_SIZE; } void archive_entry_unset_size(struct archive_entry *entry) { archive_entry_set_size(entry, 0); entry->ae_set &= ~AE_SET_SIZE; } void archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path) { archive_mstring_copy_mbs(&entry->ae_sourcepath, path); } void archive_entry_copy_sourcepath_w(struct archive_entry *entry, const wchar_t *path) { archive_mstring_copy_wcs(&entry->ae_sourcepath, path); } void archive_entry_set_symlink(struct archive_entry *entry, const char *linkname) { archive_mstring_copy_mbs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_set_symlink_utf8(struct archive_entry *entry, const char *linkname) { archive_mstring_copy_utf8(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname) { archive_mstring_copy_mbs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } void archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname) { archive_mstring_copy_wcs(&entry->ae_symlink, linkname); if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; } int archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkname) { if (linkname != NULL) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; if (archive_mstring_update_utf8(entry->archive, &entry->ae_symlink, linkname) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } int _archive_entry_copy_symlink_l(struct archive_entry *entry, const char *linkname, size_t len, struct archive_string_conv *sc) { int r; r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink, linkname, len, sc); if (linkname != NULL && r == 0) entry->ae_set |= AE_SET_SYMLINK; else entry->ae_set &= ~AE_SET_SYMLINK; return (r); } void archive_entry_set_uid(struct archive_entry *entry, la_int64_t u) { entry->stat_valid = 0; entry->ae_stat.aest_uid = u; } void archive_entry_set_uname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_uname, name); } void archive_entry_set_uname_utf8(struct archive_entry *entry, const char *name) { archive_mstring_copy_utf8(&entry->ae_uname, name); } void archive_entry_copy_uname(struct archive_entry *entry, const char *name) { archive_mstring_copy_mbs(&entry->ae_uname, name); } void archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name) { archive_mstring_copy_wcs(&entry->ae_uname, name); } int archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name) { if (archive_mstring_update_utf8(entry->archive, &entry->ae_uname, name) == 0) return (1); if (errno == ENOMEM) __archive_errx(1, "No memory"); return (0); } void archive_entry_set_is_data_encrypted(struct archive_entry *entry, char is_encrypted) { if (is_encrypted) { entry->encryption |= AE_ENCRYPTION_DATA; } else { entry->encryption &= ~AE_ENCRYPTION_DATA; } } void archive_entry_set_is_metadata_encrypted(struct archive_entry *entry, char is_encrypted) { if (is_encrypted) { entry->encryption |= AE_ENCRYPTION_METADATA; } else { entry->encryption &= ~AE_ENCRYPTION_METADATA; } } int _archive_entry_copy_uname_l(struct archive_entry *entry, const char *name, size_t len, struct archive_string_conv *sc) { return (archive_mstring_copy_mbs_len_l(&entry->ae_uname, name, len, sc)); } const void * archive_entry_mac_metadata(struct archive_entry *entry, size_t *s) { *s = entry->mac_metadata_size; return entry->mac_metadata; } void archive_entry_copy_mac_metadata(struct archive_entry *entry, const void *p, size_t s) { free(entry->mac_metadata); if (p == NULL || s == 0) { entry->mac_metadata = NULL; entry->mac_metadata_size = 0; } else { entry->mac_metadata_size = s; entry->mac_metadata = malloc(s); if (entry->mac_metadata == NULL) abort(); memcpy(entry->mac_metadata, p, s); } } /* * ACL management. The following would, of course, be a lot simpler * if: 1) the last draft of POSIX.1e were a really thorough and * complete standard that addressed the needs of ACL archiving and 2) * everyone followed it faithfully. Alas, neither is true, so the * following is a lot more complex than might seem necessary to the * uninitiated. */ struct archive_acl * archive_entry_acl(struct archive_entry *entry) { return &entry->acl; } void archive_entry_acl_clear(struct archive_entry *entry) { archive_acl_clear(&entry->acl); } /* * Add a single ACL entry to the internal list of ACL data. */ int archive_entry_acl_add_entry(struct archive_entry *entry, int type, int permset, int tag, int id, const char *name) { return archive_acl_add_entry(&entry->acl, type, permset, tag, id, name); } /* * As above, but with a wide-character name. */ int archive_entry_acl_add_entry_w(struct archive_entry *entry, int type, int permset, int tag, int id, const wchar_t *name) { return archive_acl_add_entry_w_len(&entry->acl, type, permset, tag, id, name, wcslen(name)); } /* * Return a bitmask of ACL types in an archive entry ACL list */ int archive_entry_acl_types(struct archive_entry *entry) { return (archive_acl_types(&entry->acl)); } /* * Return a count of entries matching "want_type". */ int archive_entry_acl_count(struct archive_entry *entry, int want_type) { return archive_acl_count(&entry->acl, want_type); } /* * Prepare for reading entries from the ACL data. Returns a count * of entries matching "want_type", or zero if there are no * non-extended ACL entries of that type. */ int archive_entry_acl_reset(struct archive_entry *entry, int want_type) { return archive_acl_reset(&entry->acl, want_type); } /* * Return the next ACL entry in the list. Fake entries for the * standard permissions and include them in the returned list. */ int archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type, int *permset, int *tag, int *id, const char **name) { int r; r = archive_acl_next(entry->archive, &entry->acl, want_type, type, permset, tag, id, name); if (r == ARCHIVE_FATAL && errno == ENOMEM) __archive_errx(1, "No memory"); return (r); } /* * 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, la_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, la_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) { - if (entry->acl.acl_text_w != NULL) { - free(entry->acl.acl_text_w); - 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) { - if (entry->acl.acl_text != NULL) { - free(entry->acl.acl_text); - 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) { - if (entry->acl.acl_text != NULL) { - free(entry->acl.acl_text); - 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); } /* * Following code is modified from UC Berkeley sources, and * is subject to the following copyright notice. */ /*- * Copyright (c) 1993 * The Regents of the University of California. 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. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. */ static const struct flag { const char *name; const wchar_t *wname; unsigned long set; unsigned long clear; } flags[] = { /* Preferred (shorter) names per flag first, all prefixed by "no" */ #ifdef SF_APPEND { "nosappnd", L"nosappnd", SF_APPEND, 0 }, { "nosappend", L"nosappend", SF_APPEND, 0 }, #endif #if defined(FS_APPEND_FL) /* 'a' */ { "nosappnd", L"nosappnd", FS_APPEND_FL, 0 }, { "nosappend", L"nosappend", FS_APPEND_FL, 0 }, #elif defined(EXT2_APPEND_FL) /* 'a' */ { "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0 }, { "nosappend", L"nosappend", EXT2_APPEND_FL, 0 }, #endif #ifdef SF_ARCHIVED { "noarch", L"noarch", SF_ARCHIVED, 0 }, { "noarchived", L"noarchived", SF_ARCHIVED, 0 }, #endif #ifdef SF_IMMUTABLE { "noschg", L"noschg", SF_IMMUTABLE, 0 }, { "noschange", L"noschange", SF_IMMUTABLE, 0 }, { "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0 }, #endif #if defined(FS_IMMUTABLE_FL) /* 'i' */ { "noschg", L"noschg", FS_IMMUTABLE_FL, 0 }, { "noschange", L"noschange", FS_IMMUTABLE_FL, 0 }, { "nosimmutable", L"nosimmutable", FS_IMMUTABLE_FL, 0 }, #elif defined(EXT2_IMMUTABLE_FL) /* 'i' */ { "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0 }, { "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0 }, { "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0 }, #endif #ifdef SF_NOUNLINK { "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0 }, { "nosunlink", L"nosunlink", SF_NOUNLINK, 0 }, #endif #ifdef SF_SNAPSHOT { "nosnapshot", L"nosnapshot", SF_SNAPSHOT, 0 }, #endif #ifdef UF_APPEND { "nouappnd", L"nouappnd", UF_APPEND, 0 }, { "nouappend", L"nouappend", UF_APPEND, 0 }, #endif #ifdef UF_IMMUTABLE { "nouchg", L"nouchg", UF_IMMUTABLE, 0 }, { "nouchange", L"nouchange", UF_IMMUTABLE, 0 }, { "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0 }, #endif #ifdef UF_NODUMP { "nodump", L"nodump", 0, UF_NODUMP}, #endif #if defined(FS_NODUMP_FL) /* 'd' */ { "nodump", L"nodump", 0, FS_NODUMP_FL}, #elif defined(EXT2_NODUMP_FL) /* 'd' */ { "nodump", L"nodump", 0, EXT2_NODUMP_FL}, #endif #ifdef UF_OPAQUE { "noopaque", L"noopaque", UF_OPAQUE, 0 }, #endif #ifdef UF_NOUNLINK { "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0 }, { "nouunlink", L"nouunlink", UF_NOUNLINK, 0 }, #endif #ifdef UF_COMPRESSED { "nocompressed",L"nocompressed", UF_COMPRESSED, 0 }, #endif #ifdef UF_HIDDEN { "nohidden", L"nohidden", UF_HIDDEN, 0 }, #endif #if defined(FS_UNRM_FL) { "nouunlink", L"nouunlink", FS_UNRM_FL, 0}, #elif defined(EXT2_UNRM_FL) { "nouunlink", L"nouunlink", EXT2_UNRM_FL, 0}, #endif #if defined(FS_BTREE_FL) { "nobtree", L"nobtree", FS_BTREE_FL, 0 }, #elif defined(EXT2_BTREE_FL) { "nobtree", L"nobtree", EXT2_BTREE_FL, 0 }, #endif #if defined(FS_ECOMPR_FL) { "nocomperr", L"nocomperr", FS_ECOMPR_FL, 0 }, #elif defined(EXT2_ECOMPR_FL) { "nocomperr", L"nocomperr", EXT2_ECOMPR_FL, 0 }, #endif #if defined(FS_COMPR_FL) /* 'c' */ { "nocompress", L"nocompress", FS_COMPR_FL, 0 }, #elif defined(EXT2_COMPR_FL) /* 'c' */ { "nocompress", L"nocompress", EXT2_COMPR_FL, 0 }, #endif #if defined(FS_NOATIME_FL) /* 'A' */ { "noatime", L"noatime", 0, FS_NOATIME_FL}, #elif defined(EXT2_NOATIME_FL) /* 'A' */ { "noatime", L"noatime", 0, EXT2_NOATIME_FL}, #endif #if defined(FS_DIRTY_FL) { "nocompdirty",L"nocompdirty", FS_DIRTY_FL, 0}, #elif defined(EXT2_DIRTY_FL) { "nocompdirty",L"nocompdirty", EXT2_DIRTY_FL, 0}, #endif #if defined(FS_COMPRBLK_FL) #if defined(FS_NOCOMPR_FL) { "nocomprblk", L"nocomprblk", FS_COMPRBLK_FL, FS_NOCOMPR_FL}, #else { "nocomprblk", L"nocomprblk", FS_COMPRBLK_FL, 0}, #endif #elif defined(EXT2_COMPRBLK_FL) #if defined(EXT2_NOCOMPR_FL) { "nocomprblk", L"nocomprblk", EXT2_COMPRBLK_FL, EXT2_NOCOMPR_FL}, #else { "nocomprblk", L"nocomprblk", EXT2_COMPRBLK_FL, 0}, #endif #endif #if defined(FS_DIRSYNC_FL) { "nodirsync", L"nodirsync", FS_DIRSYNC_FL, 0}, #elif defined(EXT2_DIRSYNC_FL) { "nodirsync", L"nodirsync", EXT2_DIRSYNC_FL, 0}, #endif #if defined(FS_INDEX_FL) { "nohashidx", L"nohashidx", FS_INDEX_FL, 0}, #elif defined(EXT2_INDEX_FL) { "nohashidx", L"nohashidx", EXT2_INDEX_FL, 0}, #endif #if defined(FS_IMAGIC_FL) { "noimagic", L"noimagic", FS_IMAGIC_FL, 0}, #elif defined(EXT2_IMAGIC_FL) { "noimagic", L"noimagic", EXT2_IMAGIC_FL, 0}, #endif #if defined(FS_JOURNAL_DATA_FL) { "nojournal", L"nojournal", FS_JOURNAL_DATA_FL, 0}, #elif defined(EXT3_JOURNAL_DATA_FL) { "nojournal", L"nojournal", EXT3_JOURNAL_DATA_FL, 0}, #endif #if defined(FS_SECRM_FL) { "nosecuredeletion",L"nosecuredeletion",FS_SECRM_FL, 0}, #elif defined(EXT2_SECRM_FL) { "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL, 0}, #endif #if defined(FS_SYNC_FL) { "nosync", L"nosync", FS_SYNC_FL, 0}, #elif defined(EXT2_SYNC_FL) { "nosync", L"nosync", EXT2_SYNC_FL, 0}, #endif #if defined(FS_NOTAIL_FL) { "notail", L"notail", 0, FS_NOTAIL_FL}, #elif defined(EXT2_NOTAIL_FL) { "notail", L"notail", 0, EXT2_NOTAIL_FL}, #endif #if defined(FS_TOPDIR_FL) { "notopdir", L"notopdir", FS_TOPDIR_FL, 0}, #elif defined(EXT2_TOPDIR_FL) { "notopdir", L"notopdir", EXT2_TOPDIR_FL, 0}, #endif #ifdef FS_ENCRYPT_FL { "noencrypt", L"noencrypt", FS_ENCRYPT_FL, 0}, #endif #ifdef FS_HUGE_FILE_FL { "nohugefile", L"nohugefile", FS_HUGE_FILE_FL, 0}, #endif #ifdef FS_EXTENT_FL { "noextent", L"noextent", FS_EXTENT_FL, 0}, #endif #ifdef FS_EA_INODE_FL { "noeainode", L"noeainode", FS_EA_INODE_FL, 0}, #endif #ifdef FS_EOFBLOCKS_FL { "noeofblocks",L"noeofblocks", FS_EOFBLOCKS_FL, 0}, #endif #ifdef FS_NOCOW_FL { "nocow", L"nocow", FS_NOCOW_FL, 0}, #endif #ifdef FS_INLINE_DATA_FL { "noinlinedata",L"noinlinedata", FS_INLINE_DATA_FL, 0}, #endif #ifdef FS_PROJINHERIT_FL { "noprojinherit",L"noprojinherit", FS_PROJINHERIT_FL, 0}, #endif #if defined(FS_RESERVED_FL) { "noreserved", L"noreserved", FS_RESERVED_FL, 0}, #elif defined(EXT2_RESERVED_FL) { "noreserved", L"noreserved", EXT2_RESERVED_FL, 0}, #endif { NULL, NULL, 0, 0 } }; /* * fflagstostr -- * Convert file flags to a comma-separated string. If no flags * are set, return the empty string. */ static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear) { char *string, *dp; const char *sp; unsigned long bits; const struct flag *flag; size_t length; bits = bitset | bitclear; length = 0; for (flag = flags; flag->name != NULL; flag++) if (bits & (flag->set | flag->clear)) { length += strlen(flag->name) + 1; bits &= ~(flag->set | flag->clear); } if (length == 0) return (NULL); string = (char *)malloc(length); if (string == NULL) return (NULL); dp = string; for (flag = flags; flag->name != NULL; flag++) { if (bitset & flag->set || bitclear & flag->clear) { sp = flag->name + 2; } else if (bitset & flag->clear || bitclear & flag->set) { sp = flag->name; } else continue; bitset &= ~(flag->set | flag->clear); bitclear &= ~(flag->set | flag->clear); if (dp > string) *dp++ = ','; while ((*dp++ = *sp++) != '\0') ; dp--; } *dp = '\0'; return (string); } /* * strtofflags -- * Take string of arguments and return file flags. This * version works a little differently than strtofflags(3). * In particular, it always tests every token, skipping any * unrecognized tokens. It returns a pointer to the first * unrecognized token, or NULL if every token was recognized. * This version is also const-correct and does not modify the * provided string. */ static const char * ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp) { const char *start, *end; const struct flag *flag; unsigned long set, clear; const char *failed; set = clear = 0; start = s; failed = NULL; /* Find start of first token. */ while (*start == '\t' || *start == ' ' || *start == ',') start++; while (*start != '\0') { size_t length; /* Locate end of token. */ end = start; while (*end != '\0' && *end != '\t' && *end != ' ' && *end != ',') end++; length = end - start; for (flag = flags; flag->name != NULL; flag++) { size_t flag_length = strlen(flag->name); if (length == flag_length && memcmp(start, flag->name, length) == 0) { /* Matched "noXXXX", so reverse the sense. */ clear |= flag->set; set |= flag->clear; break; } else if (length == flag_length - 2 && memcmp(start, flag->name + 2, length) == 0) { /* Matched "XXXX", so don't reverse. */ set |= flag->set; clear |= flag->clear; break; } } /* Ignore unknown flag names. */ if (flag->name == NULL && failed == NULL) failed = start; /* Find start of next token. */ start = end; while (*start == '\t' || *start == ' ' || *start == ',') start++; } if (setp) *setp = set; if (clrp) *clrp = clear; /* Return location of first failure. */ return (failed); } /* * wcstofflags -- * Take string of arguments and return file flags. This * version works a little differently than strtofflags(3). * In particular, it always tests every token, skipping any * unrecognized tokens. It returns a pointer to the first * unrecognized token, or NULL if every token was recognized. * This version is also const-correct and does not modify the * provided string. */ static const wchar_t * ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp) { const wchar_t *start, *end; const struct flag *flag; unsigned long set, clear; const wchar_t *failed; set = clear = 0; start = s; failed = NULL; /* Find start of first token. */ while (*start == L'\t' || *start == L' ' || *start == L',') start++; while (*start != L'\0') { size_t length; /* Locate end of token. */ end = start; while (*end != L'\0' && *end != L'\t' && *end != L' ' && *end != L',') end++; length = end - start; for (flag = flags; flag->wname != NULL; flag++) { size_t flag_length = wcslen(flag->wname); if (length == flag_length && wmemcmp(start, flag->wname, length) == 0) { /* Matched "noXXXX", so reverse the sense. */ clear |= flag->set; set |= flag->clear; break; } else if (length == flag_length - 2 && wmemcmp(start, flag->wname + 2, length) == 0) { /* Matched "XXXX", so don't reverse. */ set |= flag->set; clear |= flag->clear; break; } } /* Ignore unknown flag names. */ if (flag->wname == NULL && failed == NULL) failed = start; /* Find start of next token. */ start = end; while (*start == L'\t' || *start == L' ' || *start == L',') start++; } if (setp) *setp = set; if (clrp) *clrp = clear; /* Return location of first failure. */ return (failed); } #ifdef TEST #include int main(int argc, char **argv) { struct archive_entry *entry = archive_entry_new(); unsigned long set, clear; const wchar_t *remainder; remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,"); archive_entry_fflags(entry, &set, &clear); wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder); wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry)); return (0); } #endif Index: stable/10/contrib/libarchive/libarchive/archive_pack_dev.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_pack_dev.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_pack_dev.c (revision 344674) @@ -1,332 +1,335 @@ /* $NetBSD: pack_dev.c,v 1.12 2013/06/14 16:28:20 tsutsui Exp $ */ /*- * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Charles M. Hannum. * * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``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 FOUNDATION OR CONTRIBUTORS * 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. */ /* Originally from NetBSD's mknod(8) source. */ #include "archive_platform.h" #if HAVE_SYS_CDEFS_H #include #endif #if !defined(lint) __RCSID("$NetBSD$"); #endif /* not lint */ #ifdef HAVE_LIMITS_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_SYSMACROS_H #include #endif +#ifdef HAVE_SYS_MKDEV_H +#include +#endif #ifdef HAVE_UNISTD_H #include #endif #include "archive_pack_dev.h" static pack_t pack_netbsd; static pack_t pack_freebsd; static pack_t pack_8_8; static pack_t pack_12_20; static pack_t pack_14_18; static pack_t pack_8_24; static pack_t pack_bsdos; static int compare_format(const void *, const void *); static const char iMajorError[] = "invalid major number"; static const char iMinorError[] = "invalid minor number"; static const char tooManyFields[] = "too many fields for format"; /* This is blatantly stolen from libarchive/archive_entry.c, * in an attempt to get this to play nice on MinGW... */ #if !defined(HAVE_MAJOR) && !defined(major) /* Replacement for major/minor/makedev. */ #define major(x) ((int)(0x00ff & ((x) >> 8))) #define minor(x) ((int)(0xffff00ff & (x))) #define makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min))) #endif /* Play games to come up with a suitable makedev() definition. */ #ifdef __QNXNTO__ /* QNX. */ #include #define apd_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min)) #elif defined makedev /* There's a "makedev" macro. */ #define apd_makedev(maj, min) makedev((maj), (min)) #elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__)) /* Windows. */ #define apd_makedev(maj, min) mkdev((maj), (min)) #else /* There's a "makedev" function. */ #define apd_makedev(maj, min) makedev((maj), (min)) #endif /* exported */ dev_t pack_native(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = apd_makedev(numbers[0], numbers[1]); if ((unsigned long)major(dev) != numbers[0]) *error = iMajorError; else if ((unsigned long)minor(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } static dev_t pack_netbsd(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = makedev_netbsd(numbers[0], numbers[1]); if ((unsigned long)major_netbsd(dev) != numbers[0]) *error = iMajorError; else if ((unsigned long)minor_netbsd(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_freebsd(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) #define minor_freebsd(x) ((int32_t)(((x) & 0xffff00ff) >> 0)) #define makedev_freebsd(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \ (((y) << 0) & 0xffff00ff))) static dev_t pack_freebsd(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = makedev_freebsd(numbers[0], numbers[1]); if ((unsigned long)major_freebsd(dev) != numbers[0]) *error = iMajorError; if ((unsigned long)minor_freebsd(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_8_8(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) #define minor_8_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) #define makedev_8_8(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \ (((y) << 0) & 0x000000ff))) static dev_t pack_8_8(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = makedev_8_8(numbers[0], numbers[1]); if ((unsigned long)major_8_8(dev) != numbers[0]) *error = iMajorError; if ((unsigned long)minor_8_8(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_12_20(x) ((int32_t)(((x) & 0xfff00000) >> 20)) #define minor_12_20(x) ((int32_t)(((x) & 0x000fffff) >> 0)) #define makedev_12_20(x,y) ((dev_t)((((x) << 20) & 0xfff00000) | \ (((y) << 0) & 0x000fffff))) static dev_t pack_12_20(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = makedev_12_20(numbers[0], numbers[1]); if ((unsigned long)major_12_20(dev) != numbers[0]) *error = iMajorError; if ((unsigned long)minor_12_20(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_14_18(x) ((int32_t)(((x) & 0xfffc0000) >> 18)) #define minor_14_18(x) ((int32_t)(((x) & 0x0003ffff) >> 0)) #define makedev_14_18(x,y) ((dev_t)((((x) << 18) & 0xfffc0000) | \ (((y) << 0) & 0x0003ffff))) static dev_t pack_14_18(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = makedev_14_18(numbers[0], numbers[1]); if ((unsigned long)major_14_18(dev) != numbers[0]) *error = iMajorError; if ((unsigned long)minor_14_18(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_8_24(x) ((int32_t)(((x) & 0xff000000) >> 24)) #define minor_8_24(x) ((int32_t)(((x) & 0x00ffffff) >> 0)) #define makedev_8_24(x,y) ((dev_t)((((x) << 24) & 0xff000000) | \ (((y) << 0) & 0x00ffffff))) static dev_t pack_8_24(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = makedev_8_24(numbers[0], numbers[1]); if ((unsigned long)major_8_24(dev) != numbers[0]) *error = iMajorError; if ((unsigned long)minor_8_24(dev) != numbers[1]) *error = iMinorError; } else *error = tooManyFields; return (dev); } #define major_12_12_8(x) ((int32_t)(((x) & 0xfff00000) >> 20)) #define unit_12_12_8(x) ((int32_t)(((x) & 0x000fff00) >> 8)) #define subunit_12_12_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) #define makedev_12_12_8(x,y,z) ((dev_t)((((x) << 20) & 0xfff00000) | \ (((y) << 8) & 0x000fff00) | \ (((z) << 0) & 0x000000ff))) static dev_t pack_bsdos(int n, unsigned long numbers[], const char **error) { dev_t dev = 0; if (n == 2) { dev = makedev_12_20(numbers[0], numbers[1]); if ((unsigned long)major_12_20(dev) != numbers[0]) *error = iMajorError; if ((unsigned long)minor_12_20(dev) != numbers[1]) *error = iMinorError; } else if (n == 3) { dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]); if ((unsigned long)major_12_12_8(dev) != numbers[0]) *error = iMajorError; if ((unsigned long)unit_12_12_8(dev) != numbers[1]) *error = "invalid unit number"; if ((unsigned long)subunit_12_12_8(dev) != numbers[2]) *error = "invalid subunit number"; } else *error = tooManyFields; return (dev); } /* list of formats and pack functions */ /* this list must be sorted lexically */ static const struct format { const char *name; pack_t *pack; } formats[] = { {"386bsd", pack_8_8}, {"4bsd", pack_8_8}, {"bsdos", pack_bsdos}, {"freebsd", pack_freebsd}, {"hpux", pack_8_24}, {"isc", pack_8_8}, {"linux", pack_8_8}, {"native", pack_native}, {"netbsd", pack_netbsd}, {"osf1", pack_12_20}, {"sco", pack_8_8}, {"solaris", pack_14_18}, {"sunos", pack_8_8}, {"svr3", pack_8_8}, {"svr4", pack_14_18}, {"ultrix", pack_8_8}, }; static int compare_format(const void *key, const void *element) { const char *name; const struct format *format; name = key; format = element; return (strcmp(name, format->name)); } pack_t * pack_find(const char *name) { struct format *format; format = bsearch(name, formats, sizeof(formats)/sizeof(formats[0]), sizeof(formats[0]), compare_format); if (format == 0) return (NULL); return (format->pack); } Index: stable/10/contrib/libarchive/libarchive/archive_ppmd8.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_ppmd8.c (nonexistent) +++ stable/10/contrib/libarchive/libarchive/archive_ppmd8.c (revision 344674) @@ -0,0 +1,1287 @@ +/* Ppmd8.c -- PPMdI codec +2016-05-21 : Igor Pavlov : Public domain +This code is based on PPMd var.I (2002): Dmitry Shkarin : Public domain */ + +#include "archive_platform.h" + +#include + +#include "archive_ppmd8_private.h" + +const Byte PPMD8_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 }; +static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051}; + +#define MAX_FREQ 124 +#define UNIT_SIZE 12 + +#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE) +#define U2I(nu) (p->Units2Indx[(nu) - 1]) +#define I2U(indx) (p->Indx2Units[indx]) + +#ifdef PPMD_32BIT + #define REF(ptr) (ptr) +#else + #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base)) +#endif + +#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr)) + +#define CTX(ref) ((CPpmd8_Context *)Ppmd8_GetContext(p, ref)) +#define STATS(ctx) Ppmd8_GetStats(p, ctx) +#define ONE_STATE(ctx) Ppmd8Context_OneState(ctx) +#define SUFFIX(ctx) CTX((ctx)->Suffix) + +#define kTop (1 << 24) +#define kBot (1 << 15) + +typedef CPpmd8_Context * CTX_PTR; + +struct CPpmd8_Node_; + +typedef + #ifdef PPMD_32BIT + struct CPpmd8_Node_ * + #else + UInt32 + #endif + CPpmd8_Node_Ref; + +typedef struct CPpmd8_Node_ +{ + UInt32 Stamp; + CPpmd8_Node_Ref Next; + UInt32 NU; +} CPpmd8_Node; + +#ifdef PPMD_32BIT + #define NODE(ptr) (ptr) +#else + #define NODE(offs) ((CPpmd8_Node *)(p->Base + (offs))) +#endif + +#define EMPTY_NODE 0xFFFFFFFF + +void Ppmd8_Construct(CPpmd8 *p) +{ + unsigned i, k, m; + + p->Base = 0; + + for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++) + { + unsigned step = (i >= 12 ? 4 : (i >> 2) + 1); + do { p->Units2Indx[k++] = (Byte)i; } while (--step); + p->Indx2Units[i] = (Byte)k; + } + + p->NS2BSIndx[0] = (0 << 1); + p->NS2BSIndx[1] = (1 << 1); + memset(p->NS2BSIndx + 2, (2 << 1), 9); + memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11); + + for (i = 0; i < 5; i++) + p->NS2Indx[i] = (Byte)i; + for (m = i, k = 1; i < 260; i++) + { + p->NS2Indx[i] = (Byte)m; + if (--k == 0) + k = (++m) - 4; + } +} + +void Ppmd8_Free(CPpmd8 *p) +{ + free(p->Base); + p->Size = 0; + p->Base = 0; +} + +Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size) +{ + if (p->Base == 0 || p->Size != size) + { + Ppmd8_Free(p); + p->AlignOffset = + #ifdef PPMD_32BIT + (4 - size) & 3; + #else + 4 - (size & 3); + #endif + if ((p->Base = (Byte *)malloc(p->AlignOffset + size)) == 0) + return False; + p->Size = size; + } + return True; +} + +static void InsertNode(CPpmd8 *p, void *node, unsigned indx) +{ + ((CPpmd8_Node *)node)->Stamp = EMPTY_NODE; + ((CPpmd8_Node *)node)->Next = (CPpmd8_Node_Ref)p->FreeList[indx]; + ((CPpmd8_Node *)node)->NU = I2U(indx); + p->FreeList[indx] = REF(node); + p->Stamps[indx]++; +} + +static void *RemoveNode(CPpmd8 *p, unsigned indx) +{ + CPpmd8_Node *node = NODE((CPpmd8_Node_Ref)p->FreeList[indx]); + p->FreeList[indx] = node->Next; + p->Stamps[indx]--; + return node; +} + +static void SplitBlock(CPpmd8 *p, void *ptr, unsigned oldIndx, unsigned newIndx) +{ + unsigned i, nu = I2U(oldIndx) - I2U(newIndx); + ptr = (Byte *)ptr + U2B(I2U(newIndx)); + if (I2U(i = U2I(nu)) != nu) + { + unsigned k = I2U(--i); + InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1); + } + InsertNode(p, ptr, i); +} + +static void GlueFreeBlocks(CPpmd8 *p) +{ + CPpmd8_Node_Ref head = 0; + CPpmd8_Node_Ref *prev = &head; + unsigned i; + + p->GlueCount = 1 << 13; + memset(p->Stamps, 0, sizeof(p->Stamps)); + + /* Order-0 context is always at top UNIT, so we don't need guard NODE at the end. + All blocks up to p->LoUnit can be free, so we need guard NODE at LoUnit. */ + if (p->LoUnit != p->HiUnit) + ((CPpmd8_Node *)p->LoUnit)->Stamp = 0; + + /* Glue free blocks */ + for (i = 0; i < PPMD_NUM_INDEXES; i++) + { + CPpmd8_Node_Ref next = (CPpmd8_Node_Ref)p->FreeList[i]; + p->FreeList[i] = 0; + while (next != 0) + { + CPpmd8_Node *node = NODE(next); + if (node->NU != 0) + { + CPpmd8_Node *node2; + *prev = next; + prev = &(node->Next); + while ((node2 = node + node->NU)->Stamp == EMPTY_NODE) + { + node->NU += node2->NU; + node2->NU = 0; + } + } + next = node->Next; + } + } + *prev = 0; + + /* Fill lists of free blocks */ + while (head != 0) + { + CPpmd8_Node *node = NODE(head); + unsigned nu; + head = node->Next; + nu = node->NU; + if (nu == 0) + continue; + for (; nu > 128; nu -= 128, node += 128) + InsertNode(p, node, PPMD_NUM_INDEXES - 1); + if (I2U(i = U2I(nu)) != nu) + { + unsigned k = I2U(--i); + InsertNode(p, node + k, nu - k - 1); + } + InsertNode(p, node, i); + } +} + +static void *AllocUnitsRare(CPpmd8 *p, unsigned indx) +{ + unsigned i; + void *retVal; + if (p->GlueCount == 0) + { + GlueFreeBlocks(p); + if (p->FreeList[indx] != 0) + return RemoveNode(p, indx); + } + i = indx; + do + { + if (++i == PPMD_NUM_INDEXES) + { + UInt32 numBytes = U2B(I2U(indx)); + p->GlueCount--; + return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL); + } + } + while (p->FreeList[i] == 0); + retVal = RemoveNode(p, i); + SplitBlock(p, retVal, i, indx); + return retVal; +} + +static void *AllocUnits(CPpmd8 *p, unsigned indx) +{ + UInt32 numBytes; + if (p->FreeList[indx] != 0) + return RemoveNode(p, indx); + numBytes = U2B(I2U(indx)); + if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit)) + { + void *retVal = p->LoUnit; + p->LoUnit += numBytes; + return retVal; + } + return AllocUnitsRare(p, indx); +} + +#define MyMem12Cpy(dest, src, num) \ + { UInt32 *d = (UInt32 *)dest; const UInt32 *z = (const UInt32 *)src; UInt32 n = num; \ + do { d[0] = z[0]; d[1] = z[1]; d[2] = z[2]; z += 3; d += 3; } while (--n); } + +static void *ShrinkUnits(CPpmd8 *p, void *oldPtr, unsigned oldNU, unsigned newNU) +{ + unsigned i0 = U2I(oldNU); + unsigned i1 = U2I(newNU); + if (i0 == i1) + return oldPtr; + if (p->FreeList[i1] != 0) + { + void *ptr = RemoveNode(p, i1); + MyMem12Cpy(ptr, oldPtr, newNU); + InsertNode(p, oldPtr, i0); + return ptr; + } + SplitBlock(p, oldPtr, i0, i1); + return oldPtr; +} + +static void FreeUnits(CPpmd8 *p, void *ptr, unsigned nu) +{ + InsertNode(p, ptr, U2I(nu)); +} + +static void SpecialFreeUnit(CPpmd8 *p, void *ptr) +{ + if ((Byte *)ptr != p->UnitsStart) + InsertNode(p, ptr, 0); + else + { + #ifdef PPMD8_FREEZE_SUPPORT + *(UInt32 *)ptr = EMPTY_NODE; /* it's used for (Flags == 0xFF) check in RemoveBinContexts */ + #endif + p->UnitsStart += UNIT_SIZE; + } +} + +static void *MoveUnitsUp(CPpmd8 *p, void *oldPtr, unsigned nu) +{ + unsigned indx = U2I(nu); + void *ptr; + if ((Byte *)oldPtr > p->UnitsStart + 16 * 1024 || REF(oldPtr) > p->FreeList[indx]) + return oldPtr; + ptr = RemoveNode(p, indx); + MyMem12Cpy(ptr, oldPtr, nu); + if ((Byte*)oldPtr != p->UnitsStart) + InsertNode(p, oldPtr, indx); + else + p->UnitsStart += U2B(I2U(indx)); + return ptr; +} + +static void ExpandTextArea(CPpmd8 *p) +{ + UInt32 count[PPMD_NUM_INDEXES]; + unsigned i; + memset(count, 0, sizeof(count)); + if (p->LoUnit != p->HiUnit) + ((CPpmd8_Node *)p->LoUnit)->Stamp = 0; + + { + CPpmd8_Node *node = (CPpmd8_Node *)p->UnitsStart; + for (; node->Stamp == EMPTY_NODE; node += node->NU) + { + node->Stamp = 0; + count[U2I(node->NU)]++; + } + p->UnitsStart = (Byte *)node; + } + + for (i = 0; i < PPMD_NUM_INDEXES; i++) + { + CPpmd8_Node_Ref *next = (CPpmd8_Node_Ref *)&p->FreeList[i]; + while (count[i] != 0) + { + CPpmd8_Node *node = NODE(*next); + while (node->Stamp == 0) + { + *next = node->Next; + node = NODE(*next); + p->Stamps[i]--; + if (--count[i] == 0) + break; + } + next = &node->Next; + } + } +} + +#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16))) + +static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v) +{ + (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF); + (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF); +} + +#define RESET_TEXT(offs) { p->Text = p->Base + p->AlignOffset + (offs); } + +static void RestartModel(CPpmd8 *p) +{ + unsigned i, k, m, r; + + memset(p->FreeList, 0, sizeof(p->FreeList)); + memset(p->Stamps, 0, sizeof(p->Stamps)); + RESET_TEXT(0); + p->HiUnit = p->Text + p->Size; + p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE; + p->GlueCount = 0; + + p->OrderFall = p->MaxOrder; + p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1; + p->PrevSuccess = 0; + + p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */ + p->MinContext->Suffix = 0; + p->MinContext->NumStats = 255; + p->MinContext->Flags = 0; + p->MinContext->SummFreq = 256 + 1; + p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */ + p->LoUnit += U2B(256 / 2); + p->MinContext->Stats = REF(p->FoundState); + for (i = 0; i < 256; i++) + { + CPpmd_State *s = &p->FoundState[i]; + s->Symbol = (Byte)i; + s->Freq = 1; + SetSuccessor(s, 0); + } + + for (i = m = 0; m < 25; m++) + { + while (p->NS2Indx[i] == m) + i++; + for (k = 0; k < 8; k++) + { + UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 1)); + UInt16 *dest = p->BinSumm[m] + k; + for (r = 0; r < 64; r += 8) + dest[r] = val; + } + } + + for (i = m = 0; m < 24; m++) + { + while (p->NS2Indx[i + 3] == m + 3) + i++; + for (k = 0; k < 32; k++) + { + CPpmd_See *s = &p->See[m][k]; + s->Summ = (UInt16)((2 * i + 5) << (s->Shift = PPMD_PERIOD_BITS - 4)); + s->Count = 7; + } + } +} + +void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod) +{ + p->MaxOrder = maxOrder; + p->RestoreMethod = restoreMethod; + RestartModel(p); + p->DummySee.Shift = PPMD_PERIOD_BITS; + p->DummySee.Summ = 0; /* unused */ + p->DummySee.Count = 64; /* unused */ +} + +static void Refresh(CPpmd8 *p, CTX_PTR ctx, unsigned oldNU, unsigned scale) +{ + unsigned i = ctx->NumStats, escFreq, sumFreq, flags; + CPpmd_State *s = (CPpmd_State *)ShrinkUnits(p, STATS(ctx), oldNU, (i + 2) >> 1); + ctx->Stats = REF(s); + #ifdef PPMD8_FREEZE_SUPPORT + /* fixed over Shkarin's code. Fixed code is not compatible with original code for some files in FREEZE mode. */ + scale |= (ctx->SummFreq >= ((UInt32)1 << 15)); + #endif + flags = (ctx->Flags & (0x10 + 0x04 * scale)) + 0x08 * (s->Symbol >= 0x40); + escFreq = ctx->SummFreq - s->Freq; + sumFreq = (s->Freq = (Byte)((s->Freq + scale) >> scale)); + do + { + escFreq -= (++s)->Freq; + sumFreq += (s->Freq = (Byte)((s->Freq + scale) >> scale)); + flags |= 0x08 * (s->Symbol >= 0x40); + } + while (--i); + ctx->SummFreq = (UInt16)(sumFreq + ((escFreq + scale) >> scale)); + ctx->Flags = (Byte)flags; +} + +static void SwapStates(CPpmd_State *t1, CPpmd_State *t2) +{ + CPpmd_State tmp = *t1; + *t1 = *t2; + *t2 = tmp; +} + +static CPpmd_Void_Ref CutOff(CPpmd8 *p, CTX_PTR ctx, unsigned order) +{ + int i; + unsigned tmp; + CPpmd_State *s; + + if (!ctx->NumStats) + { + s = ONE_STATE(ctx); + if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart) + { + if (order < p->MaxOrder) + SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1)); + else + SetSuccessor(s, 0); + if (SUCCESSOR(s) || order <= 9) /* O_BOUND */ + return REF(ctx); + } + SpecialFreeUnit(p, ctx); + return 0; + } + + ctx->Stats = STATS_REF(MoveUnitsUp(p, STATS(ctx), tmp = ((unsigned)ctx->NumStats + 2) >> 1)); + + for (s = STATS(ctx) + (i = ctx->NumStats); s >= STATS(ctx); s--) + if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) < p->UnitsStart) + { + CPpmd_State *s2 = STATS(ctx) + (i--); + SetSuccessor(s, 0); + SwapStates(s, s2); + } + else if (order < p->MaxOrder) + SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1)); + else + SetSuccessor(s, 0); + + if (i != ctx->NumStats && order) + { + ctx->NumStats = (Byte)i; + s = STATS(ctx); + if (i < 0) + { + FreeUnits(p, s, tmp); + SpecialFreeUnit(p, ctx); + return 0; + } + if (i == 0) + { + ctx->Flags = (Byte)((ctx->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40)); + *ONE_STATE(ctx) = *s; + FreeUnits(p, s, tmp); + /* 9.31: the code was fixed. It's was not BUG, if Freq <= MAX_FREQ = 124 */ + ONE_STATE(ctx)->Freq = (Byte)(((unsigned)ONE_STATE(ctx)->Freq + 11) >> 3); + } + else + Refresh(p, ctx, tmp, ctx->SummFreq > 16 * i); + } + return REF(ctx); +} + +#ifdef PPMD8_FREEZE_SUPPORT +static CPpmd_Void_Ref RemoveBinContexts(CPpmd8 *p, CTX_PTR ctx, unsigned order) +{ + CPpmd_State *s; + if (!ctx->NumStats) + { + s = ONE_STATE(ctx); + if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder) + SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1)); + else + SetSuccessor(s, 0); + /* Suffix context can be removed already, since different (high-order) + Successors may refer to same context. So we check Flags == 0xFF (Stamp == EMPTY_NODE) */ + if (!SUCCESSOR(s) && (!SUFFIX(ctx)->NumStats || SUFFIX(ctx)->Flags == 0xFF)) + { + FreeUnits(p, ctx, 1); + return 0; + } + else + return REF(ctx); + } + + for (s = STATS(ctx) + ctx->NumStats; s >= STATS(ctx); s--) + if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder) + SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1)); + else + SetSuccessor(s, 0); + + return REF(ctx); +} +#endif + +static UInt32 GetUsedMemory(const CPpmd8 *p) +{ + UInt32 v = 0; + unsigned i; + for (i = 0; i < PPMD_NUM_INDEXES; i++) + v += p->Stamps[i] * I2U(i); + return p->Size - (UInt32)(p->HiUnit - p->LoUnit) - (UInt32)(p->UnitsStart - p->Text) - U2B(v); +} + +#ifdef PPMD8_FREEZE_SUPPORT + #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1, fSuccessor) +#else + #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1) +#endif + +static void RestoreModel(CPpmd8 *p, CTX_PTR c1 + #ifdef PPMD8_FREEZE_SUPPORT + , CTX_PTR fSuccessor + #endif + ) +{ + CTX_PTR c; + CPpmd_State *s; + RESET_TEXT(0); + for (c = p->MaxContext; c != c1; c = SUFFIX(c)) + if (--(c->NumStats) == 0) + { + s = STATS(c); + c->Flags = (Byte)((c->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40)); + *ONE_STATE(c) = *s; + SpecialFreeUnit(p, s); + ONE_STATE(c)->Freq = (Byte)(((unsigned)ONE_STATE(c)->Freq + 11) >> 3); + } + else + Refresh(p, c, (c->NumStats+3) >> 1, 0); + + for (; c != p->MinContext; c = SUFFIX(c)) + if (!c->NumStats) + ONE_STATE(c)->Freq = (Byte)(ONE_STATE(c)->Freq - (ONE_STATE(c)->Freq >> 1)); + else if ((c->SummFreq += 4) > 128 + 4 * c->NumStats) + Refresh(p, c, (c->NumStats + 2) >> 1, 1); + + #ifdef PPMD8_FREEZE_SUPPORT + if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) + { + p->MaxContext = fSuccessor; + p->GlueCount += !(p->Stamps[1] & 1); + } + else if (p->RestoreMethod == PPMD8_RESTORE_METHOD_FREEZE) + { + while (p->MaxContext->Suffix) + p->MaxContext = SUFFIX(p->MaxContext); + RemoveBinContexts(p, p->MaxContext, 0); + p->RestoreMethod++; + p->GlueCount = 0; + p->OrderFall = p->MaxOrder; + } + else + #endif + if (p->RestoreMethod == PPMD8_RESTORE_METHOD_RESTART || GetUsedMemory(p) < (p->Size >> 1)) + RestartModel(p); + else + { + while (p->MaxContext->Suffix) + p->MaxContext = SUFFIX(p->MaxContext); + do + { + CutOff(p, p->MaxContext, 0); + ExpandTextArea(p); + } + while (GetUsedMemory(p) > 3 * (p->Size >> 2)); + p->GlueCount = 0; + p->OrderFall = p->MaxOrder; + } +} + +static CTX_PTR CreateSuccessors(CPpmd8 *p, Bool skip, CPpmd_State *s1, CTX_PTR c) +{ + CPpmd_State upState; + Byte flags; + CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState); + /* fixed over Shkarin's code. Maybe it could work without + 1 too. */ + CPpmd_State *ps[PPMD8_MAX_ORDER + 1]; + unsigned numPs = 0; + + if (!skip) + ps[numPs++] = p->FoundState; + + while (c->Suffix) + { + CPpmd_Void_Ref successor; + CPpmd_State *s; + c = SUFFIX(c); + if (s1) + { + s = s1; + s1 = NULL; + } + else if (c->NumStats != 0) + { + for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++); + if (s->Freq < MAX_FREQ - 9) + { + s->Freq++; + c->SummFreq++; + } + } + else + { + s = ONE_STATE(c); + s->Freq = (Byte)(s->Freq + (!SUFFIX(c)->NumStats & (s->Freq < 24))); + } + successor = SUCCESSOR(s); + if (successor != upBranch) + { + c = CTX(successor); + if (numPs == 0) + return c; + break; + } + ps[numPs++] = s; + } + + upState.Symbol = *(const Byte *)Ppmd8_GetPtr(p, upBranch); + SetSuccessor(&upState, upBranch + 1); + flags = (Byte)(0x10 * (p->FoundState->Symbol >= 0x40) + 0x08 * (upState.Symbol >= 0x40)); + + if (c->NumStats == 0) + upState.Freq = ONE_STATE(c)->Freq; + else + { + UInt32 cf, s0; + CPpmd_State *s; + for (s = STATS(c); s->Symbol != upState.Symbol; s++); + cf = s->Freq - 1; + s0 = c->SummFreq - c->NumStats - cf; + upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((cf + 2 * s0 - 3) / s0))); + } + + do + { + /* Create Child */ + CTX_PTR c1; /* = AllocContext(p); */ + if (p->HiUnit != p->LoUnit) + c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); + else if (p->FreeList[0] != 0) + c1 = (CTX_PTR)RemoveNode(p, 0); + else + { + c1 = (CTX_PTR)AllocUnitsRare(p, 0); + if (!c1) + return NULL; + } + c1->NumStats = 0; + c1->Flags = flags; + *ONE_STATE(c1) = upState; + c1->Suffix = REF(c); + SetSuccessor(ps[--numPs], REF(c1)); + c = c1; + } + while (numPs != 0); + + return c; +} + +static CTX_PTR ReduceOrder(CPpmd8 *p, CPpmd_State *s1, CTX_PTR c) +{ + CPpmd_State *s = NULL; + CTX_PTR c1 = c; + CPpmd_Void_Ref upBranch = REF(p->Text); + + #ifdef PPMD8_FREEZE_SUPPORT + /* The BUG in Shkarin's code was fixed: ps could overflow in CUT_OFF mode. */ + CPpmd_State *ps[PPMD8_MAX_ORDER + 1]; + unsigned numPs = 0; + ps[numPs++] = p->FoundState; + #endif + + SetSuccessor(p->FoundState, upBranch); + p->OrderFall++; + + for (;;) + { + if (s1) + { + c = SUFFIX(c); + s = s1; + s1 = NULL; + } + else + { + if (!c->Suffix) + { + #ifdef PPMD8_FREEZE_SUPPORT + if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) + { + do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs); + RESET_TEXT(1); + p->OrderFall = 1; + } + #endif + return c; + } + c = SUFFIX(c); + if (c->NumStats) + { + if ((s = STATS(c))->Symbol != p->FoundState->Symbol) + do { s++; } while (s->Symbol != p->FoundState->Symbol); + if (s->Freq < MAX_FREQ - 9) + { + s->Freq += 2; + c->SummFreq += 2; + } + } + else + { + s = ONE_STATE(c); + s->Freq = (Byte)(s->Freq + (s->Freq < 32)); + } + } + if (SUCCESSOR(s)) + break; + #ifdef PPMD8_FREEZE_SUPPORT + ps[numPs++] = s; + #endif + SetSuccessor(s, upBranch); + p->OrderFall++; + } + + #ifdef PPMD8_FREEZE_SUPPORT + if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) + { + c = CTX(SUCCESSOR(s)); + do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs); + RESET_TEXT(1); + p->OrderFall = 1; + return c; + } + else + #endif + if (SUCCESSOR(s) <= upBranch) + { + CTX_PTR successor; + CPpmd_State *s2 = p->FoundState; + p->FoundState = s; + + successor = CreateSuccessors(p, False, NULL, c); + if (successor == NULL) + SetSuccessor(s, 0); + else + SetSuccessor(s, REF(successor)); + p->FoundState = s2; + } + + if (p->OrderFall == 1 && c1 == p->MaxContext) + { + SetSuccessor(p->FoundState, SUCCESSOR(s)); + p->Text--; + } + if (SUCCESSOR(s) == 0) + return NULL; + return CTX(SUCCESSOR(s)); +} + +static void UpdateModel(CPpmd8 *p) +{ + CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState); + CTX_PTR c; + unsigned s0, ns, fFreq = p->FoundState->Freq; + Byte flag, fSymbol = p->FoundState->Symbol; + CPpmd_State *s = NULL; + + if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0) + { + c = SUFFIX(p->MinContext); + + if (c->NumStats == 0) + { + s = ONE_STATE(c); + if (s->Freq < 32) + s->Freq++; + } + else + { + s = STATS(c); + if (s->Symbol != p->FoundState->Symbol) + { + do { s++; } while (s->Symbol != p->FoundState->Symbol); + if (s[0].Freq >= s[-1].Freq) + { + SwapStates(&s[0], &s[-1]); + s--; + } + } + if (s->Freq < MAX_FREQ - 9) + { + s->Freq += 2; + c->SummFreq += 2; + } + } + } + + c = p->MaxContext; + if (p->OrderFall == 0 && fSuccessor) + { + CTX_PTR cs = CreateSuccessors(p, True, s, p->MinContext); + if (cs == 0) + { + SetSuccessor(p->FoundState, 0); + RESTORE_MODEL(c, CTX(fSuccessor)); + } + else + { + SetSuccessor(p->FoundState, REF(cs)); + p->MaxContext = cs; + } + return; + } + + *p->Text++ = p->FoundState->Symbol; + successor = REF(p->Text); + if (p->Text >= p->UnitsStart) + { + RESTORE_MODEL(c, CTX(fSuccessor)); /* check it */ + return; + } + + if (!fSuccessor) + { + CTX_PTR cs = ReduceOrder(p, s, p->MinContext); + if (cs == NULL) + { + RESTORE_MODEL(c, 0); + return; + } + fSuccessor = REF(cs); + } + else if ((Byte *)Ppmd8_GetPtr(p, fSuccessor) < p->UnitsStart) + { + CTX_PTR cs = CreateSuccessors(p, False, s, p->MinContext); + if (cs == NULL) + { + RESTORE_MODEL(c, 0); + return; + } + fSuccessor = REF(cs); + } + + if (--p->OrderFall == 0) + { + successor = fSuccessor; + p->Text -= (p->MaxContext != p->MinContext); + } + #ifdef PPMD8_FREEZE_SUPPORT + else if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE) + { + successor = fSuccessor; + RESET_TEXT(0); + p->OrderFall = 0; + } + #endif + + s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - fFreq; + flag = (Byte)(0x08 * (fSymbol >= 0x40)); + + for (; c != p->MinContext; c = SUFFIX(c)) + { + unsigned ns1; + UInt32 cf, sf; + if ((ns1 = c->NumStats) != 0) + { + if ((ns1 & 1) != 0) + { + /* Expand for one UNIT */ + unsigned oldNU = (ns1 + 1) >> 1; + unsigned i = U2I(oldNU); + if (i != U2I(oldNU + 1)) + { + void *ptr = AllocUnits(p, i + 1); + void *oldPtr; + if (!ptr) + { + RESTORE_MODEL(c, CTX(fSuccessor)); + return; + } + oldPtr = STATS(c); + MyMem12Cpy(ptr, oldPtr, oldNU); + InsertNode(p, oldPtr, i); + c->Stats = STATS_REF(ptr); + } + } + c->SummFreq = (UInt16)(c->SummFreq + (3 * ns1 + 1 < ns)); + } + else + { + CPpmd_State *s2 = (CPpmd_State*)AllocUnits(p, 0); + if (!s2) + { + RESTORE_MODEL(c, CTX(fSuccessor)); + return; + } + *s2 = *ONE_STATE(c); + c->Stats = REF(s2); + if (s2->Freq < MAX_FREQ / 4 - 1) + s2->Freq <<= 1; + else + s2->Freq = MAX_FREQ - 4; + c->SummFreq = (UInt16)(s2->Freq + p->InitEsc + (ns > 2)); + } + cf = 2 * fFreq * (c->SummFreq + 6); + sf = (UInt32)s0 + c->SummFreq; + if (cf < 6 * sf) + { + cf = 1 + (cf > sf) + (cf >= 4 * sf); + c->SummFreq += 4; + } + else + { + cf = 4 + (cf > 9 * sf) + (cf > 12 * sf) + (cf > 15 * sf); + c->SummFreq = (UInt16)(c->SummFreq + cf); + } + { + CPpmd_State *s2 = STATS(c) + ns1 + 1; + SetSuccessor(s2, successor); + s2->Symbol = fSymbol; + s2->Freq = (Byte)cf; + c->Flags |= flag; + c->NumStats = (Byte)(ns1 + 1); + } + } + p->MaxContext = p->MinContext = CTX(fSuccessor); +} + +static void Rescale(CPpmd8 *p) +{ + unsigned i, adder, sumFreq, escFreq; + CPpmd_State *stats = STATS(p->MinContext); + CPpmd_State *s = p->FoundState; + { + CPpmd_State tmp = *s; + for (; s != stats; s--) + s[0] = s[-1]; + *s = tmp; + } + escFreq = p->MinContext->SummFreq - s->Freq; + s->Freq += 4; + adder = (p->OrderFall != 0 + #ifdef PPMD8_FREEZE_SUPPORT + || p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE + #endif + ); + s->Freq = (Byte)((s->Freq + adder) >> 1); + sumFreq = s->Freq; + + i = p->MinContext->NumStats; + do + { + escFreq -= (++s)->Freq; + s->Freq = (Byte)((s->Freq + adder) >> 1); + sumFreq += s->Freq; + if (s[0].Freq > s[-1].Freq) + { + CPpmd_State *s1 = s; + CPpmd_State tmp = *s1; + do + s1[0] = s1[-1]; + while (--s1 != stats && tmp.Freq > s1[-1].Freq); + *s1 = tmp; + } + } + while (--i); + + if (s->Freq == 0) + { + unsigned numStats = p->MinContext->NumStats; + unsigned n0, n1; + do { i++; } while ((--s)->Freq == 0); + escFreq += i; + p->MinContext->NumStats = (Byte)(p->MinContext->NumStats - i); + if (p->MinContext->NumStats == 0) + { + CPpmd_State tmp = *stats; + tmp.Freq = (Byte)((2 * tmp.Freq + escFreq - 1) / escFreq); + if (tmp.Freq > MAX_FREQ / 3) + tmp.Freq = MAX_FREQ / 3; + InsertNode(p, stats, U2I((numStats + 2) >> 1)); + p->MinContext->Flags = (Byte)((p->MinContext->Flags & 0x10) + 0x08 * (tmp.Symbol >= 0x40)); + *(p->FoundState = ONE_STATE(p->MinContext)) = tmp; + return; + } + n0 = (numStats + 2) >> 1; + n1 = (p->MinContext->NumStats + 2) >> 1; + if (n0 != n1) + p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1)); + p->MinContext->Flags &= ~0x08; + p->MinContext->Flags |= 0x08 * ((s = STATS(p->MinContext))->Symbol >= 0x40); + i = p->MinContext->NumStats; + do { p->MinContext->Flags |= 0x08*((++s)->Symbol >= 0x40); } while (--i); + } + p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1)); + p->MinContext->Flags |= 0x4; + p->FoundState = STATS(p->MinContext); +} + +CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked1, UInt32 *escFreq) +{ + CPpmd_See *see; + if (p->MinContext->NumStats != 0xFF) + { + see = p->See[(unsigned)p->NS2Indx[(unsigned)p->MinContext->NumStats + 2] - 3] + + (p->MinContext->SummFreq > 11 * ((unsigned)p->MinContext->NumStats + 1)) + + 2 * (unsigned)(2 * (unsigned)p->MinContext->NumStats < + ((unsigned)SUFFIX(p->MinContext)->NumStats + numMasked1)) + + p->MinContext->Flags; + { + unsigned r = (see->Summ >> see->Shift); + see->Summ = (UInt16)(see->Summ - r); + *escFreq = r + (r == 0); + } + } + else + { + see = &p->DummySee; + *escFreq = 1; + } + return see; +} + +static void NextContext(CPpmd8 *p) +{ + CTX_PTR c = CTX(SUCCESSOR(p->FoundState)); + if (p->OrderFall == 0 && (Byte *)c >= p->UnitsStart) + p->MinContext = p->MaxContext = c; + else + { + UpdateModel(p); + p->MinContext = p->MaxContext; + } +} + +void Ppmd8_Update1(CPpmd8 *p) +{ + CPpmd_State *s = p->FoundState; + s->Freq += 4; + p->MinContext->SummFreq += 4; + if (s[0].Freq > s[-1].Freq) + { + SwapStates(&s[0], &s[-1]); + p->FoundState = --s; + if (s->Freq > MAX_FREQ) + Rescale(p); + } + NextContext(p); +} + +void Ppmd8_Update1_0(CPpmd8 *p) +{ + p->PrevSuccess = (2 * p->FoundState->Freq >= p->MinContext->SummFreq); + p->RunLength += p->PrevSuccess; + p->MinContext->SummFreq += 4; + if ((p->FoundState->Freq += 4) > MAX_FREQ) + Rescale(p); + NextContext(p); +} + +void Ppmd8_UpdateBin(CPpmd8 *p) +{ + p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 196)); + p->PrevSuccess = 1; + p->RunLength++; + NextContext(p); +} + +void Ppmd8_Update2(CPpmd8 *p) +{ + p->MinContext->SummFreq += 4; + if ((p->FoundState->Freq += 4) > MAX_FREQ) + Rescale(p); + p->RunLength = p->InitRL; + UpdateModel(p); + p->MinContext = p->MaxContext; +} + +/* Ppmd8Dec.c -- PPMdI Decoder +2010-04-16 : Igor Pavlov : Public domain +This code is based on: + PPMd var.I (2002): Dmitry Shkarin : Public domain + Carryless rangecoder (1999): Dmitry Subbotin : Public domain */ + +Bool Ppmd8_RangeDec_Init(CPpmd8 *p) +{ + unsigned i; + p->Low = 0; + p->Range = 0xFFFFFFFF; + p->Code = 0; + for (i = 0; i < 4; i++) + p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In); + return (p->Code < 0xFFFFFFFF); +} + +static UInt32 RangeDec_GetThreshold(CPpmd8 *p, UInt32 total) +{ + return p->Code / (p->Range /= total); +} + +static void RangeDec_Decode(CPpmd8 *p, UInt32 start, UInt32 size) +{ + start *= p->Range; + p->Low += start; + p->Code -= start; + p->Range *= size; + + while ((p->Low ^ (p->Low + p->Range)) < kTop || + (p->Range < kBot && ((p->Range = (0 - p->Low) & (kBot - 1)), 1))) + { + p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In); + p->Range <<= 8; + p->Low <<= 8; + } +} + +#define MASK(sym) ((signed char *)charMask)[sym] + +int Ppmd8_DecodeSymbol(CPpmd8 *p) +{ + size_t charMask[256 / sizeof(size_t)]; + if (p->MinContext->NumStats != 0) + { + CPpmd_State *s = Ppmd8_GetStats(p, p->MinContext); + unsigned i; + UInt32 count, hiCnt; + if ((count = RangeDec_GetThreshold(p, p->MinContext->SummFreq)) < (hiCnt = s->Freq)) + { + Byte symbol; + RangeDec_Decode(p, 0, s->Freq); + p->FoundState = s; + symbol = s->Symbol; + Ppmd8_Update1_0(p); + return symbol; + } + p->PrevSuccess = 0; + i = p->MinContext->NumStats; + do + { + if ((hiCnt += (++s)->Freq) > count) + { + Byte symbol; + RangeDec_Decode(p, hiCnt - s->Freq, s->Freq); + p->FoundState = s; + symbol = s->Symbol; + Ppmd8_Update1(p); + return symbol; + } + } + while (--i); + if (count >= p->MinContext->SummFreq) + return -2; + RangeDec_Decode(p, hiCnt, p->MinContext->SummFreq - hiCnt); + PPMD_SetAllBitsIn256Bytes(charMask); + MASK(s->Symbol) = 0; + i = p->MinContext->NumStats; + do { MASK((--s)->Symbol) = 0; } while (--i); + } + else + { + UInt16 *prob = Ppmd8_GetBinSumm(p); + if (((p->Code / (p->Range >>= 14)) < *prob)) + { + Byte symbol; + RangeDec_Decode(p, 0, *prob); + *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob); + symbol = (p->FoundState = Ppmd8Context_OneState(p->MinContext))->Symbol; + Ppmd8_UpdateBin(p); + return symbol; + } + RangeDec_Decode(p, *prob, (1 << 14) - *prob); + *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob); + p->InitEsc = PPMD8_kExpEscape[*prob >> 10]; + PPMD_SetAllBitsIn256Bytes(charMask); + MASK(Ppmd8Context_OneState(p->MinContext)->Symbol) = 0; + p->PrevSuccess = 0; + } + for (;;) + { + CPpmd_State *ps[256], *s; + UInt32 freqSum, count, hiCnt; + CPpmd_See *see; + unsigned i, num, numMasked = p->MinContext->NumStats; + do + { + p->OrderFall++; + if (!p->MinContext->Suffix) + return -1; + p->MinContext = Ppmd8_GetContext(p, p->MinContext->Suffix); + } + while (p->MinContext->NumStats == numMasked); + hiCnt = 0; + s = Ppmd8_GetStats(p, p->MinContext); + i = 0; + num = p->MinContext->NumStats - numMasked; + do + { + int k = (int)(MASK(s->Symbol)); + hiCnt += (s->Freq & k); + ps[i] = s++; + i -= k; + } + while (i != num); + + see = Ppmd8_MakeEscFreq(p, numMasked, &freqSum); + freqSum += hiCnt; + count = RangeDec_GetThreshold(p, freqSum); + + if (count < hiCnt) + { + Byte symbol; + CPpmd_State **pps = ps; + for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++); + s = *pps; + RangeDec_Decode(p, hiCnt - s->Freq, s->Freq); + Ppmd_See_Update(see); + p->FoundState = s; + symbol = s->Symbol; + Ppmd8_Update2(p); + return symbol; + } + if (count >= freqSum) + return -2; + RangeDec_Decode(p, hiCnt, freqSum - hiCnt); + see->Summ = (UInt16)(see->Summ + freqSum); + do { MASK(ps[--i]->Symbol) = 0; } while (i != 0); + } +} + +/* H->I changes: + NS2Indx + GlewCount, and Glue method + BinSum + See / EscFreq + CreateSuccessors updates more suffix contexts + UpdateModel consts. + PrevSuccess Update +*/ + +const IPpmd8 __archive_ppmd8_functions = +{ + &Ppmd8_Construct, + &Ppmd8_Alloc, + &Ppmd8_Free, + &Ppmd8_Init, + &Ppmd8_RangeDec_Init, + &Ppmd8_DecodeSymbol, +}; Property changes on: stable/10/contrib/libarchive/libarchive/archive_ppmd8.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/contrib/libarchive/libarchive/archive_ppmd8_private.h =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_ppmd8_private.h (nonexistent) +++ stable/10/contrib/libarchive/libarchive/archive_ppmd8_private.h (revision 344674) @@ -0,0 +1,148 @@ +/* Ppmd8.h -- PPMdI codec +2011-01-27 : Igor Pavlov : Public domain +This code is based on: + PPMd var.I (2002): Dmitry Shkarin : Public domain + Carryless rangecoder (1999): Dmitry Subbotin : Public domain */ + +#ifndef __PPMD8_H +#define __PPMD8_H + +#include "archive_ppmd_private.h" + +#define PPMD8_MIN_ORDER 2 +#define PPMD8_MAX_ORDER 16 + +struct CPpmd8_Context_; + +typedef + #ifdef PPMD_32BIT + struct CPpmd8_Context_ * + #else + UInt32 + #endif + CPpmd8_Context_Ref; + +#pragma pack(push, 1) + +typedef struct CPpmd8_Context_ +{ + Byte NumStats; + Byte Flags; + UInt16 SummFreq; + CPpmd_State_Ref Stats; + CPpmd8_Context_Ref Suffix; +} CPpmd8_Context; + +#pragma pack(pop) + +#define Ppmd8Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq) + +/* The BUG in Shkarin's code for FREEZE mode was fixed, but that fixed + code is not compatible with original code for some files compressed + in FREEZE mode. So we disable FREEZE mode support. */ + +enum +{ + PPMD8_RESTORE_METHOD_RESTART, + PPMD8_RESTORE_METHOD_CUT_OFF + #ifdef PPMD8_FREEZE_SUPPORT + , PPMD8_RESTORE_METHOD_FREEZE + #endif +}; + +typedef struct +{ + CPpmd8_Context *MinContext, *MaxContext; + CPpmd_State *FoundState; + unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder; + Int32 RunLength, InitRL; /* must be 32-bit at least */ + + UInt32 Size; + UInt32 GlueCount; + Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart; + UInt32 AlignOffset; + unsigned RestoreMethod; + + /* Range Coder */ + UInt32 Range; + UInt32 Code; + UInt32 Low; + union + { + IByteIn *In; + IByteOut *Out; + } Stream; + + Byte Indx2Units[PPMD_NUM_INDEXES]; + Byte Units2Indx[128]; + CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES]; + UInt32 Stamps[PPMD_NUM_INDEXES]; + + Byte NS2BSIndx[256], NS2Indx[260]; + CPpmd_See DummySee, See[24][32]; + UInt16 BinSumm[25][64]; +} CPpmd8; + +void Ppmd8_Construct(CPpmd8 *p); +Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size); +void Ppmd8_Free(CPpmd8 *p); +void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod); +#define Ppmd8_WasAllocated(p) ((p)->Base != NULL) + + +/* ---------- Internal Functions ---------- */ + +extern const Byte PPMD8_kExpEscape[16]; + +#ifdef PPMD_32BIT + #define Ppmd8_GetPtr(p, ptr) (ptr) + #define Ppmd8_GetContext(p, ptr) (ptr) + #define Ppmd8_GetStats(p, ctx) ((ctx)->Stats) +#else + #define Ppmd8_GetPtr(p, offs) ((void *)((p)->Base + (offs))) + #define Ppmd8_GetContext(p, offs) ((CPpmd8_Context *)Ppmd8_GetPtr((p), (offs))) + #define Ppmd8_GetStats(p, ctx) ((CPpmd_State *)Ppmd8_GetPtr((p), ((ctx)->Stats))) +#endif + +void Ppmd8_Update1(CPpmd8 *p); +void Ppmd8_Update1_0(CPpmd8 *p); +void Ppmd8_Update2(CPpmd8 *p); +void Ppmd8_UpdateBin(CPpmd8 *p); + +#define Ppmd8_GetBinSumm(p) \ + &p->BinSumm[p->NS2Indx[Ppmd8Context_OneState(p->MinContext)->Freq - 1]][ \ + p->NS2BSIndx[Ppmd8_GetContext(p, p->MinContext->Suffix)->NumStats] + \ + p->PrevSuccess + p->MinContext->Flags + ((p->RunLength >> 26) & 0x20)] + +CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked, UInt32 *scale); + + +/* ---------- Decode ---------- */ + +Bool Ppmd8_RangeDec_Init(CPpmd8 *p); +#define Ppmd8_RangeDec_IsFinishedOK(p) ((p)->Code == 0) +int Ppmd8_DecodeSymbol(CPpmd8 *p); /* returns: -1 as EndMarker, -2 as DataError */ + +/* ---------- Encode ---------- */ + +#define Ppmd8_RangeEnc_Init(p) { (p)->Low = 0; (p)->Range = 0xFFFFFFFF; } +void Ppmd8_RangeEnc_FlushData(CPpmd8 *p); +void Ppmd8_EncodeSymbol(CPpmd8 *p, int symbol); /* symbol = -1 means EndMarker */ + +typedef struct +{ + /* Base Functions */ + void (*Ppmd8_Construct)(CPpmd8 *p); + Bool (*Ppmd8_Alloc)(CPpmd8 *p, UInt32 size); + void (*Ppmd8_Free)(CPpmd8 *p); + void (*Ppmd8_Init)(CPpmd8 *p, unsigned max_order, unsigned restore_method); + #define Ppmd7_WasAllocated(p) ((p)->Base != NULL) + + /* Decode Functions */ + int (*Ppmd8_RangeDec_Init)(CPpmd8 *p); + int (*Ppmd8_DecodeSymbol)(CPpmd8 *p); +} IPpmd8; + +extern const IPpmd8 __archive_ppmd8_functions; + +#endif Property changes on: stable/10/contrib/libarchive/libarchive/archive_ppmd8_private.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: stable/10/contrib/libarchive/libarchive/archive_read_disk_posix.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_disk_posix.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_read_disk_posix.c (revision 344674) @@ -1,2661 +1,2690 @@ /*- * Copyright (c) 2003-2009 Tim Kientzle * Copyright (c) 2010-2012 Michihiro NAKAJIMA * 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 * in this position and unchanged. * 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. */ /* This is the tree-walking code for POSIX systems. */ #if !defined(_WIN32) || defined(__CYGWIN__) #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_LINUX_MAGIC_H #include #endif #ifdef HAVE_LINUX_FS_H #include #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #ifdef HAVE_DIRECT_H #include #endif #ifdef HAVE_DIRENT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #include "archive.h" #include "archive_string.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_disk_private.h" #ifndef HAVE_FCHDIR #error fchdir function required. #endif #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif /*- * This is a new directory-walking system that addresses a number * of problems I've had with fts(3). In particular, it has no * pathname-length limits (other than the size of 'int'), handles * deep logical traversals, uses considerably less memory, and has * an opaque interface (easier to modify in the future). * * Internally, it keeps a single list of "tree_entry" items that * represent filesystem objects that require further attention. * Non-directories are not kept in memory: they are pulled from * readdir(), returned to the client, then freed as soon as possible. * Any directory entry to be traversed gets pushed onto the stack. * * There is surprisingly little information that needs to be kept for * each item on the stack. Just the name, depth (represented here as the * string length of the parent directory's pathname), and some markers * indicating how to get back to the parent (via chdir("..") for a * regular dir or via fchdir(2) for a symlink). */ /* * TODO: * 1) Loop checking. * 3) Arbitrary logical traversals by closing/reopening intermediate fds. */ struct restore_time { const char *name; time_t mtime; long mtime_nsec; time_t atime; long atime_nsec; mode_t filetype; int noatime; }; struct tree_entry { int depth; struct tree_entry *next; struct tree_entry *parent; struct archive_string name; size_t dirname_length; int64_t dev; int64_t ino; int flags; int filesystem_id; /* How to return back to the parent of a symlink. */ int symlink_parent_fd; /* How to restore time of a directory. */ struct restore_time restore_time; }; struct filesystem { int64_t dev; int synthetic; int remote; int noatime; #if defined(USE_READDIR_R) size_t name_max; #endif long incr_xfer_size; long max_xfer_size; long min_xfer_size; long xfer_align; /* * Buffer used for reading file contents. */ /* Exactly allocated memory pointer. */ unsigned char *allocation_ptr; /* Pointer adjusted to the filesystem alignment . */ unsigned char *buff; size_t buff_size; }; /* Definitions for tree_entry.flags bitmap. */ #define isDir 1 /* This entry is a regular directory. */ #define isDirLink 2 /* This entry is a symbolic link to a directory. */ #define needsFirstVisit 4 /* This is an initial entry. */ #define needsDescent 8 /* This entry needs to be previsited. */ #define needsOpen 16 /* This is a directory that needs to be opened. */ #define needsAscent 32 /* This entry needs to be postvisited. */ /* * Local data for this package. */ struct tree { struct tree_entry *stack; struct tree_entry *current; DIR *d; #define INVALID_DIR_HANDLE NULL struct dirent *de; #if defined(USE_READDIR_R) struct dirent *dirent; size_t dirent_allocated; #endif int flags; int visit_type; /* Error code from last failed operation. */ int tree_errno; /* Dynamically-sized buffer for holding path */ struct archive_string path; /* Last path element */ const char *basename; /* Leading dir length */ size_t dirname_length; int depth; int openCount; int maxOpenCount; int initial_dir_fd; int working_dir_fd; struct stat lst; struct stat st; int descend; int nlink; /* How to restore time of a file. */ struct restore_time restore_time; struct entry_sparse { int64_t length; int64_t offset; } *sparse_list, *current_sparse; int sparse_count; int sparse_list_size; char initial_symlink_mode; char symlink_mode; struct filesystem *current_filesystem; struct filesystem *filesystem_table; int initial_filesystem_id; int current_filesystem_id; int max_filesystem_id; int allocated_filesystem; int entry_fd; int entry_eof; int64_t entry_remaining_bytes; int64_t entry_total; unsigned char *entry_buff; size_t entry_buff_size; }; /* Definitions for tree.flags bitmap. */ #define hasStat 16 /* The st entry is valid. */ #define hasLstat 32 /* The lst entry is valid. */ #define onWorkingDir 64 /* We are on the working dir where we are * reading directory entry at this time. */ #define needsRestoreTimes 128 #define onInitialDir 256 /* We are on the initial dir. */ static int tree_dir_next_posix(struct tree *t); #ifdef HAVE_DIRENT_D_NAMLEN /* BSD extension; avoids need for a strlen() call. */ #define D_NAMELEN(dp) (dp)->d_namlen #else #define D_NAMELEN(dp) (strlen((dp)->d_name)) #endif /* Initiate/terminate a tree traversal. */ static struct tree *tree_open(const char *, int, int); static struct tree *tree_reopen(struct tree *, const char *, int); static void tree_close(struct tree *); static void tree_free(struct tree *); static void tree_push(struct tree *, const char *, int, int64_t, int64_t, struct restore_time *); static int tree_enter_initial_dir(struct tree *); static int tree_enter_working_dir(struct tree *); static int tree_current_dir_fd(struct tree *); /* * tree_next() returns Zero if there is no next entry, non-zero if * there is. Note that directories are visited three times. * Directories are always visited first as part of enumerating their * parent; that is a "regular" visit. If tree_descend() is invoked at * that time, the directory is added to a work list and will * subsequently be visited two more times: once just after descending * into the directory ("postdescent") and again just after ascending * back to the parent ("postascent"). * * TREE_ERROR_DIR is returned if the descent failed (because the * directory couldn't be opened, for instance). This is returned * instead of TREE_POSTDESCENT/TREE_POSTASCENT. TREE_ERROR_DIR is not a * fatal error, but it does imply that the relevant subtree won't be * visited. TREE_ERROR_FATAL is returned for an error that left the * traversal completely hosed. Right now, this is only returned for * chdir() failures during ascent. */ #define TREE_REGULAR 1 #define TREE_POSTDESCENT 2 #define TREE_POSTASCENT 3 #define TREE_ERROR_DIR -1 #define TREE_ERROR_FATAL -2 static int tree_next(struct tree *); /* * Return information about the current entry. */ /* * The current full pathname, length of the full pathname, and a name * that can be used to access the file. Because tree does use chdir * extensively, the access path is almost never the same as the full * current path. * * TODO: On platforms that support it, use openat()-style operations * to eliminate the chdir() operations entirely while still supporting * arbitrarily deep traversals. This makes access_path troublesome to * support, of course, which means we'll need a rich enough interface * that clients can function without it. (In particular, we'll need * tree_current_open() that returns an open file descriptor.) * */ static const char *tree_current_path(struct tree *); static const char *tree_current_access_path(struct tree *); /* * Request the lstat() or stat() data for the current path. Since the * tree package needs to do some of this anyway, and caches the * results, you should take advantage of it here if you need it rather * than make a redundant stat() or lstat() call of your own. */ static const struct stat *tree_current_stat(struct tree *); static const struct stat *tree_current_lstat(struct tree *); static int tree_current_is_symblic_link_target(struct tree *); /* The following functions use tricks to avoid a certain number of * stat()/lstat() calls. */ /* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */ static int tree_current_is_physical_dir(struct tree *); /* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */ static int tree_current_is_dir(struct tree *); static int update_current_filesystem(struct archive_read_disk *a, int64_t dev); static int setup_current_filesystem(struct archive_read_disk *); static int tree_target_is_same_as_parent(struct tree *, const struct stat *); static int _archive_read_disk_open(struct archive *, const char *); static int _archive_read_free(struct archive *); static int _archive_read_close(struct archive *); static int _archive_read_data_block(struct archive *, const void **, size_t *, int64_t *); static int _archive_read_next_header(struct archive *, struct archive_entry **); static int _archive_read_next_header2(struct archive *, struct archive_entry *); static const char *trivial_lookup_gname(void *, int64_t gid); static const char *trivial_lookup_uname(void *, int64_t uid); static int setup_sparse(struct archive_read_disk *, struct archive_entry *); static int close_and_restore_time(int fd, struct tree *, struct restore_time *); static int open_on_current_dir(struct tree *, const char *, int); static int tree_dup(int); static struct archive_vtable * archive_read_disk_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_free = _archive_read_free; av.archive_close = _archive_read_close; av.archive_read_data_block = _archive_read_data_block; av.archive_read_next_header = _archive_read_next_header; av.archive_read_next_header2 = _archive_read_next_header2; inited = 1; } return (&av); } const char * archive_read_disk_gname(struct archive *_a, la_int64_t gid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_gname")) return (NULL); if (a->lookup_gname == NULL) return (NULL); return ((*a->lookup_gname)(a->lookup_gname_data, gid)); } const char * archive_read_disk_uname(struct archive *_a, la_int64_t uid) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_uname")) return (NULL); if (a->lookup_uname == NULL) return (NULL); return ((*a->lookup_uname)(a->lookup_uname_data, uid)); } int archive_read_disk_set_gname_lookup(struct archive *_a, void *private_data, const char * (*lookup_gname)(void *private, la_int64_t gid), void (*cleanup_gname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup"); if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); a->lookup_gname = lookup_gname; a->cleanup_gname = cleanup_gname; a->lookup_gname_data = private_data; return (ARCHIVE_OK); } int archive_read_disk_set_uname_lookup(struct archive *_a, void *private_data, const char * (*lookup_uname)(void *private, la_int64_t uid), void (*cleanup_uname)(void *private)) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup"); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); a->lookup_uname = lookup_uname; a->cleanup_uname = cleanup_uname; a->lookup_uname_data = private_data; return (ARCHIVE_OK); } /* * Create a new archive_read_disk object and initialize it with global state. */ struct archive * archive_read_disk_new(void) { struct archive_read_disk *a; a = (struct archive_read_disk *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); a->archive.magic = ARCHIVE_READ_DISK_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; a->archive.vtable = archive_read_disk_vtable(); a->entry = archive_entry_new2(&a->archive); a->lookup_uname = trivial_lookup_uname; a->lookup_gname = trivial_lookup_gname; a->flags = ARCHIVE_READDISK_MAC_COPYFILE; a->open_on_current_dir = open_on_current_dir; a->tree_current_dir_fd = tree_current_dir_fd; a->tree_enter_working_dir = tree_enter_working_dir; return (&a->archive); } static int _archive_read_free(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; int r; if (_a == NULL) return (ARCHIVE_OK); archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free"); if (a->archive.state != ARCHIVE_STATE_CLOSED) r = _archive_read_close(&a->archive); else r = ARCHIVE_OK; tree_free(a->tree); if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL) (a->cleanup_gname)(a->lookup_gname_data); if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL) (a->cleanup_uname)(a->lookup_uname_data); archive_string_free(&a->archive.error_string); archive_entry_free(a->entry); a->archive.magic = 0; __archive_clean(&a->archive); free(a); return (r); } static int _archive_read_close(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close"); if (a->archive.state != ARCHIVE_STATE_FATAL) a->archive.state = ARCHIVE_STATE_CLOSED; tree_close(a->tree); return (ARCHIVE_OK); } static void setup_symlink_mode(struct archive_read_disk *a, char symlink_mode, int follow_symlinks) { a->symlink_mode = symlink_mode; a->follow_symlinks = follow_symlinks; if (a->tree != NULL) { a->tree->initial_symlink_mode = a->symlink_mode; a->tree->symlink_mode = a->symlink_mode; } } int archive_read_disk_set_symlink_logical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_logical"); setup_symlink_mode(a, 'L', 1); return (ARCHIVE_OK); } int archive_read_disk_set_symlink_physical(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_physical"); setup_symlink_mode(a, 'P', 0); return (ARCHIVE_OK); } int archive_read_disk_set_symlink_hybrid(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_hybrid"); setup_symlink_mode(a, 'H', 1);/* Follow symlinks initially. */ return (ARCHIVE_OK); } int archive_read_disk_set_atime_restored(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime"); #ifdef HAVE_UTIMES a->flags |= ARCHIVE_READDISK_RESTORE_ATIME; if (a->tree != NULL) a->tree->flags |= needsRestoreTimes; return (ARCHIVE_OK); #else /* Display warning and unset flag */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Cannot restore access time on this system"); a->flags &= ~ARCHIVE_READDISK_RESTORE_ATIME; return (ARCHIVE_WARN); #endif } int archive_read_disk_set_behavior(struct archive *_a, int flags) { struct archive_read_disk *a = (struct archive_read_disk *)_a; int r = ARCHIVE_OK; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump"); a->flags = flags; if (flags & ARCHIVE_READDISK_RESTORE_ATIME) r = archive_read_disk_set_atime_restored(_a); else { if (a->tree != NULL) a->tree->flags &= ~needsRestoreTimes; } return (r); } /* * Trivial implementations of gname/uname lookup functions. * These are normally overridden by the client, but these stub * versions ensure that we always have something that works. */ static const char * trivial_lookup_gname(void *private_data, int64_t gid) { (void)private_data; /* UNUSED */ (void)gid; /* UNUSED */ return (NULL); } static const char * trivial_lookup_uname(void *private_data, int64_t uid) { (void)private_data; /* UNUSED */ (void)uid; /* UNUSED */ return (NULL); } /* * Allocate memory for the reading buffer adjusted to the filesystem * alignment. */ static int setup_suitable_read_buffer(struct archive_read_disk *a) { struct tree *t = a->tree; struct filesystem *cf = t->current_filesystem; size_t asize; size_t s; if (cf->allocation_ptr == NULL) { /* If we couldn't get a filesystem alignment, * we use 4096 as default value but we won't use * O_DIRECT to open() and openat() operations. */ long xfer_align = (cf->xfer_align == -1)?4096:cf->xfer_align; if (cf->max_xfer_size != -1) asize = cf->max_xfer_size + xfer_align; else { long incr = cf->incr_xfer_size; /* Some platform does not set a proper value to * incr_xfer_size.*/ if (incr < 0) incr = cf->min_xfer_size; if (cf->min_xfer_size < 0) { incr = xfer_align; asize = xfer_align; } else asize = cf->min_xfer_size; /* Increase a buffer size up to 64K bytes in * a proper increment size. */ while (asize < 1024*64) asize += incr; /* Take a margin to adjust to the filesystem * alignment. */ asize += xfer_align; } cf->allocation_ptr = malloc(asize); if (cf->allocation_ptr == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } /* * Calculate proper address for the filesystem. */ s = (uintptr_t)cf->allocation_ptr; s %= xfer_align; if (s > 0) s = xfer_align - s; /* * Set a read buffer pointer in the proper alignment of * the current filesystem. */ cf->buff = cf->allocation_ptr + s; cf->buff_size = asize - xfer_align; } return (ARCHIVE_OK); } static int _archive_read_data_block(struct archive *_a, const void **buff, size_t *size, int64_t *offset) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; int r; ssize_t bytes; size_t buffbytes; int empty_sparse_region = 0; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block"); if (t->entry_eof || t->entry_remaining_bytes <= 0) { r = ARCHIVE_EOF; goto abort_read_data; } /* * Open the current file. */ if (t->entry_fd < 0) { int flags = O_RDONLY | O_BINARY | O_CLOEXEC; /* * Eliminate or reduce cache effects if we can. * * Carefully consider this to be enabled. */ #if defined(O_DIRECT) && 0/* Disabled for now */ if (t->current_filesystem->xfer_align != -1 && t->nlink == 1) flags |= O_DIRECT; #endif #if defined(O_NOATIME) /* * Linux has O_NOATIME flag; use it if we need. */ if ((t->flags & needsRestoreTimes) != 0 && t->restore_time.noatime == 0) flags |= O_NOATIME; do { #endif t->entry_fd = open_on_current_dir(t, tree_current_access_path(t), flags); __archive_ensure_cloexec_flag(t->entry_fd); #if defined(O_NOATIME) /* * When we did open the file with O_NOATIME flag, * if successful, set 1 to t->restore_time.noatime * not to restore an atime of the file later. * if failed by EPERM, retry it without O_NOATIME flag. */ if (flags & O_NOATIME) { if (t->entry_fd >= 0) t->restore_time.noatime = 1; else if (errno == EPERM) { flags &= ~O_NOATIME; continue; } } } while (0); #endif if (t->entry_fd < 0) { archive_set_error(&a->archive, errno, "Couldn't open %s", tree_current_path(t)); r = ARCHIVE_FAILED; tree_enter_initial_dir(t); goto abort_read_data; } tree_enter_initial_dir(t); } /* * Allocate read buffer if not allocated. */ if (t->current_filesystem->allocation_ptr == NULL) { r = setup_suitable_read_buffer(a); if (r != ARCHIVE_OK) { a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } } t->entry_buff = t->current_filesystem->buff; t->entry_buff_size = t->current_filesystem->buff_size; buffbytes = t->entry_buff_size; if ((int64_t)buffbytes > t->current_sparse->length) buffbytes = t->current_sparse->length; if (t->current_sparse->length == 0) empty_sparse_region = 1; /* * Skip hole. * TODO: Should we consider t->current_filesystem->xfer_align? */ if (t->current_sparse->offset > t->entry_total) { if (lseek(t->entry_fd, (off_t)t->current_sparse->offset, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek error"); r = ARCHIVE_FATAL; a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } bytes = t->current_sparse->offset - t->entry_total; t->entry_remaining_bytes -= bytes; t->entry_total += bytes; } /* * Read file contents. */ if (buffbytes > 0) { bytes = read(t->entry_fd, t->entry_buff, buffbytes); if (bytes < 0) { archive_set_error(&a->archive, errno, "Read error"); r = ARCHIVE_FATAL; a->archive.state = ARCHIVE_STATE_FATAL; goto abort_read_data; } } else bytes = 0; /* * Return an EOF unless we've read a leading empty sparse region, which * is used to represent fully-sparse files. */ if (bytes == 0 && !empty_sparse_region) { /* Get EOF */ t->entry_eof = 1; r = ARCHIVE_EOF; goto abort_read_data; } *buff = t->entry_buff; *size = bytes; *offset = t->entry_total; t->entry_total += bytes; t->entry_remaining_bytes -= bytes; if (t->entry_remaining_bytes == 0) { /* Close the current file descriptor */ close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; t->entry_eof = 1; } t->current_sparse->offset += bytes; t->current_sparse->length -= bytes; if (t->current_sparse->length == 0 && !t->entry_eof) t->current_sparse++; return (ARCHIVE_OK); abort_read_data: *buff = NULL; *size = 0; *offset = t->entry_total; if (t->entry_fd >= 0) { /* Close the current file descriptor */ close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; } return (r); } static int next_entry(struct archive_read_disk *a, struct tree *t, struct archive_entry *entry) { const struct stat *st; /* info to use for this entry */ const struct stat *lst;/* lstat() information */ const char *name; - int descend, r; + int delayed, delayed_errno, descend, r; + struct archive_string delayed_str; + delayed = ARCHIVE_OK; + delayed_errno = 0; + archive_string_init(&delayed_str); + st = NULL; lst = NULL; t->descend = 0; do { switch (tree_next(t)) { case TREE_ERROR_FATAL: archive_set_error(&a->archive, t->tree_errno, "%s: Unable to continue traversing directory tree", tree_current_path(t)); a->archive.state = ARCHIVE_STATE_FATAL; tree_enter_initial_dir(t); return (ARCHIVE_FATAL); case TREE_ERROR_DIR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: Couldn't visit directory", tree_current_path(t)); tree_enter_initial_dir(t); return (ARCHIVE_FAILED); case 0: tree_enter_initial_dir(t); return (ARCHIVE_EOF); case TREE_POSTDESCENT: case TREE_POSTASCENT: break; case TREE_REGULAR: lst = tree_current_lstat(t); if (lst == NULL) { + if (errno == ENOENT && t->depth > 0) { + delayed = ARCHIVE_WARN; + delayed_errno = errno; + if (delayed_str.length == 0) { + archive_string_sprintf(&delayed_str, + "%s", tree_current_path(t)); + } else { + archive_string_sprintf(&delayed_str, + " %s", tree_current_path(t)); + } + } else { archive_set_error(&a->archive, errno, "%s: Cannot stat", tree_current_path(t)); tree_enter_initial_dir(t); return (ARCHIVE_FAILED); + } } break; } } while (lst == NULL); #ifdef __APPLE__ if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) { /* If we're using copyfile(), ignore "._XXX" files. */ const char *bname = strrchr(tree_current_path(t), '/'); if (bname == NULL) bname = tree_current_path(t); else ++bname; if (bname[0] == '.' && bname[1] == '_') return (ARCHIVE_RETRY); } #endif archive_entry_copy_pathname(entry, tree_current_path(t)); /* * Perform path matching. */ if (a->matching) { r = archive_match_path_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* * Distinguish 'L'/'P'/'H' symlink following. */ switch(t->symlink_mode) { case 'H': /* 'H': After the first item, rest like 'P'. */ t->symlink_mode = 'P'; /* 'H': First item (from command line) like 'L'. */ /* FALLTHROUGH */ case 'L': /* 'L': Do descend through a symlink to dir. */ descend = tree_current_is_dir(t); /* 'L': Follow symlinks to files. */ a->symlink_mode = 'L'; a->follow_symlinks = 1; /* 'L': Archive symlinks as targets, if we can. */ st = tree_current_stat(t); if (st != NULL && !tree_target_is_same_as_parent(t, st)) break; /* If stat fails, we have a broken symlink; * in that case, don't follow the link. */ /* FALLTHROUGH */ default: /* 'P': Don't descend through a symlink to dir. */ descend = tree_current_is_physical_dir(t); /* 'P': Don't follow symlinks to files. */ a->symlink_mode = 'P'; a->follow_symlinks = 0; /* 'P': Archive symlinks as symlinks. */ st = lst; break; } if (update_current_filesystem(a, st->st_dev) != ARCHIVE_OK) { a->archive.state = ARCHIVE_STATE_FATAL; tree_enter_initial_dir(t); return (ARCHIVE_FATAL); } if (t->initial_filesystem_id == -1) t->initial_filesystem_id = t->current_filesystem_id; if (a->flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) { if (t->initial_filesystem_id != t->current_filesystem_id) descend = 0; } t->descend = descend; /* * Honor nodump flag. * If the file is marked with nodump flag, do not return this entry. */ if (a->flags & ARCHIVE_READDISK_HONOR_NODUMP) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) if (st->st_flags & UF_NODUMP) return (ARCHIVE_RETRY); #elif (defined(FS_IOC_GETFLAGS) && defined(FS_NODUMP_FL) && \ defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) && \ defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)) if (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) { int stflags; t->entry_fd = open_on_current_dir(t, tree_current_access_path(t), O_RDONLY | O_NONBLOCK | O_CLOEXEC); __archive_ensure_cloexec_flag(t->entry_fd); if (t->entry_fd >= 0) { r = ioctl(t->entry_fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &stflags); #ifdef FS_NODUMP_FL if (r == 0 && (stflags & FS_NODUMP_FL) != 0) #else if (r == 0 && (stflags & EXT2_NODUMP_FL) != 0) #endif return (ARCHIVE_RETRY); } } #endif } archive_entry_copy_stat(entry, st); /* Save the times to be restored. This must be in before * calling archive_read_disk_descend() or any chance of it, * especially, invoking a callback. */ t->restore_time.mtime = archive_entry_mtime(entry); t->restore_time.mtime_nsec = archive_entry_mtime_nsec(entry); t->restore_time.atime = archive_entry_atime(entry); t->restore_time.atime_nsec = archive_entry_atime_nsec(entry); t->restore_time.filetype = archive_entry_filetype(entry); t->restore_time.noatime = t->current_filesystem->noatime; /* * Perform time matching. */ if (a->matching) { r = archive_match_time_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* Lookup uname/gname */ name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); /* * Perform owner matching. */ if (a->matching) { r = archive_match_owner_excluded(a->matching, entry); if (r < 0) { archive_set_error(&(a->archive), errno, "Failed : %s", archive_error_string(a->matching)); return (r); } if (r) { if (a->excluded_cb_func) a->excluded_cb_func(&(a->archive), a->excluded_cb_data, entry); return (ARCHIVE_RETRY); } } /* * Invoke a meta data filter callback. */ if (a->metadata_filter_func) { if (!a->metadata_filter_func(&(a->archive), a->metadata_filter_data, entry)) return (ARCHIVE_RETRY); } /* * Populate the archive_entry with metadata from the disk. */ archive_entry_copy_sourcepath(entry, tree_current_access_path(t)); r = archive_read_disk_entry_from_file(&(a->archive), entry, t->entry_fd, st); + + if (r == ARCHIVE_OK) { + r = delayed; + if (r != ARCHIVE_OK) { + archive_string_sprintf(&delayed_str, ": %s", + "File removed before we read it"); + archive_set_error(&(a->archive), delayed_errno, + "%s", delayed_str.s); + } + } + if (!archive_string_empty(&delayed_str)) + archive_string_free(&delayed_str); return (r); } static int _archive_read_next_header(struct archive *_a, struct archive_entry **entryp) { int ret; struct archive_read_disk *a = (struct archive_read_disk *)_a; *entryp = NULL; ret = _archive_read_next_header2(_a, a->entry); *entryp = a->entry; return ret; } static int _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t; int r; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_next_header2"); t = a->tree; if (t->entry_fd >= 0) { close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; } for (;;) { r = next_entry(a, t, entry); if (t->entry_fd >= 0) { close(t->entry_fd); t->entry_fd = -1; } if (r == ARCHIVE_RETRY) { archive_entry_clear(entry); continue; } break; } /* Return to the initial directory. */ tree_enter_initial_dir(t); /* * EOF and FATAL are persistent at this layer. By * modifying the state, we guarantee that future calls to * read a header or read data will fail. */ switch (r) { case ARCHIVE_EOF: a->archive.state = ARCHIVE_STATE_EOF; break; case ARCHIVE_OK: case ARCHIVE_WARN: /* Overwrite the sourcepath based on the initial directory. */ archive_entry_copy_sourcepath(entry, tree_current_path(t)); t->entry_total = 0; if (archive_entry_filetype(entry) == AE_IFREG) { t->nlink = archive_entry_nlink(entry); t->entry_remaining_bytes = archive_entry_size(entry); t->entry_eof = (t->entry_remaining_bytes == 0)? 1: 0; if (!t->entry_eof && setup_sparse(a, entry) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { t->entry_remaining_bytes = 0; t->entry_eof = 1; } a->archive.state = ARCHIVE_STATE_DATA; break; case ARCHIVE_RETRY: break; case ARCHIVE_FATAL: a->archive.state = ARCHIVE_STATE_FATAL; break; } __archive_reset_read_data(&a->archive); return (r); } static int setup_sparse(struct archive_read_disk *a, struct archive_entry *entry) { struct tree *t = a->tree; int64_t length, offset; int i; t->sparse_count = archive_entry_sparse_reset(entry); if (t->sparse_count+1 > t->sparse_list_size) { free(t->sparse_list); t->sparse_list_size = t->sparse_count + 1; t->sparse_list = malloc(sizeof(t->sparse_list[0]) * t->sparse_list_size); if (t->sparse_list == NULL) { t->sparse_list_size = 0; archive_set_error(&a->archive, ENOMEM, "Can't allocate data"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } } for (i = 0; i < t->sparse_count; i++) { archive_entry_sparse_next(entry, &offset, &length); t->sparse_list[i].offset = offset; t->sparse_list[i].length = length; } if (i == 0) { t->sparse_list[i].offset = 0; t->sparse_list[i].length = archive_entry_size(entry); } else { t->sparse_list[i].offset = archive_entry_size(entry); t->sparse_list[i].length = 0; } t->current_sparse = t->sparse_list; return (ARCHIVE_OK); } int archive_read_disk_set_matching(struct archive *_a, struct archive *_ma, void (*_excluded_func)(struct archive *, void *, struct archive_entry *), void *_client_data) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_matching"); a->matching = _ma; a->excluded_cb_func = _excluded_func; a->excluded_cb_data = _client_data; return (ARCHIVE_OK); } int archive_read_disk_set_metadata_filter_callback(struct archive *_a, int (*_metadata_filter_func)(struct archive *, void *, struct archive_entry *), void *_client_data) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_read_disk_set_metadata_filter_callback"); a->metadata_filter_func = _metadata_filter_func; a->metadata_filter_data = _client_data; return (ARCHIVE_OK); } int archive_read_disk_can_descend(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_disk_can_descend"); return (t->visit_type == TREE_REGULAR && t->descend); } /* * Called by the client to mark the directory just returned from * tree_next() as needing to be visited. */ int archive_read_disk_descend(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct tree *t = a->tree; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_read_disk_descend"); if (t->visit_type != TREE_REGULAR || !t->descend) return (ARCHIVE_OK); if (tree_current_is_physical_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->lst.st_dev, t->lst.st_ino, &t->restore_time); t->stack->flags |= isDir; } else if (tree_current_is_dir(t)) { tree_push(t, t->basename, t->current_filesystem_id, t->st.st_dev, t->st.st_ino, &t->restore_time); t->stack->flags |= isDirLink; } t->descend = 0; return (ARCHIVE_OK); } int archive_read_disk_open(struct archive *_a, const char *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED, "archive_read_disk_open"); archive_clear_error(&a->archive); return (_archive_read_disk_open(_a, pathname)); } int archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; struct archive_string path; int ret; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED, "archive_read_disk_open_w"); archive_clear_error(&a->archive); /* Make a char string from a wchar_t string. */ archive_string_init(&path); if (archive_string_append_from_wcs(&path, pathname, wcslen(pathname)) != 0) { if (errno == ENOMEM) archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't convert a path to a char string"); a->archive.state = ARCHIVE_STATE_FATAL; ret = ARCHIVE_FATAL; } else ret = _archive_read_disk_open(_a, path.s); archive_string_free(&path); return (ret); } static int _archive_read_disk_open(struct archive *_a, const char *pathname) { struct archive_read_disk *a = (struct archive_read_disk *)_a; if (a->tree != NULL) a->tree = tree_reopen(a->tree, pathname, a->flags & ARCHIVE_READDISK_RESTORE_ATIME); else a->tree = tree_open(pathname, a->symlink_mode, a->flags & ARCHIVE_READDISK_RESTORE_ATIME); if (a->tree == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } a->archive.state = ARCHIVE_STATE_HEADER; return (ARCHIVE_OK); } /* * Return a current filesystem ID which is index of the filesystem entry * you've visited through archive_read_disk. */ int archive_read_disk_current_filesystem(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem_id); } static int update_current_filesystem(struct archive_read_disk *a, int64_t dev) { struct tree *t = a->tree; int i, fid; if (t->current_filesystem != NULL && t->current_filesystem->dev == dev) return (ARCHIVE_OK); for (i = 0; i < t->max_filesystem_id; i++) { if (t->filesystem_table[i].dev == dev) { /* There is the filesystem ID we've already generated. */ t->current_filesystem_id = i; t->current_filesystem = &(t->filesystem_table[i]); return (ARCHIVE_OK); } } /* * This is the new filesystem which we have to generate a new ID for. */ fid = t->max_filesystem_id++; if (t->max_filesystem_id > t->allocated_filesystem) { size_t s; void *p; s = t->max_filesystem_id * 2; p = realloc(t->filesystem_table, s * sizeof(*t->filesystem_table)); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); return (ARCHIVE_FATAL); } t->filesystem_table = (struct filesystem *)p; t->allocated_filesystem = s; } t->current_filesystem_id = fid; t->current_filesystem = &(t->filesystem_table[fid]); t->current_filesystem->dev = dev; t->current_filesystem->allocation_ptr = NULL; t->current_filesystem->buff = NULL; /* Setup the current filesystem properties which depend on * platform specific. */ return (setup_current_filesystem(a)); } /* * Returns 1 if current filesystem is generated filesystem, 0 if it is not * or -1 if it is unknown. */ int archive_read_disk_current_filesystem_is_synthetic(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem->synthetic); } /* * Returns 1 if current filesystem is remote filesystem, 0 if it is not * or -1 if it is unknown. */ int archive_read_disk_current_filesystem_is_remote(struct archive *_a) { struct archive_read_disk *a = (struct archive_read_disk *)_a; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_disk_current_filesystem"); return (a->tree->current_filesystem->remote); } #if defined(_PC_REC_INCR_XFER_SIZE) && defined(_PC_REC_MAX_XFER_SIZE) &&\ defined(_PC_REC_MIN_XFER_SIZE) && defined(_PC_REC_XFER_ALIGN) static int get_xfer_size(struct tree *t, int fd, const char *path) { t->current_filesystem->xfer_align = -1; errno = 0; if (fd >= 0) { t->current_filesystem->incr_xfer_size = fpathconf(fd, _PC_REC_INCR_XFER_SIZE); t->current_filesystem->max_xfer_size = fpathconf(fd, _PC_REC_MAX_XFER_SIZE); t->current_filesystem->min_xfer_size = fpathconf(fd, _PC_REC_MIN_XFER_SIZE); t->current_filesystem->xfer_align = fpathconf(fd, _PC_REC_XFER_ALIGN); } else if (path != NULL) { t->current_filesystem->incr_xfer_size = pathconf(path, _PC_REC_INCR_XFER_SIZE); t->current_filesystem->max_xfer_size = pathconf(path, _PC_REC_MAX_XFER_SIZE); t->current_filesystem->min_xfer_size = pathconf(path, _PC_REC_MIN_XFER_SIZE); t->current_filesystem->xfer_align = pathconf(path, _PC_REC_XFER_ALIGN); } /* At least we need an alignment size. */ if (t->current_filesystem->xfer_align == -1) return ((errno == EINVAL)?1:-1); else return (0); } #else static int get_xfer_size(struct tree *t, int fd, const char *path) { (void)t; /* UNUSED */ (void)fd; /* UNUSED */ (void)path; /* UNUSED */ return (1);/* Not supported */ } #endif #if defined(HAVE_STATFS) && defined(HAVE_FSTATFS) && defined(MNT_LOCAL) \ && !defined(ST_LOCAL) /* * Gather current filesystem properties on FreeBSD, OpenBSD and Mac OS X. */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statfs sfs; #if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC) /* TODO: configure should set GETVFSBYNAME_ARG_TYPE to make * this accurate; some platforms have both and we need the one that's * used by getvfsbyname() * * Then the following would become: * #if defined(GETVFSBYNAME_ARG_TYPE) * GETVFSBYNAME_ARG_TYPE vfc; * #endif */ # if defined(HAVE_STRUCT_XVFSCONF) struct xvfsconf vfc; # else struct vfsconf vfc; # endif #endif int r, xr = 0; #if !defined(HAVE_STRUCT_STATFS_F_NAMEMAX) long nm; #endif t->current_filesystem->synthetic = -1; t->current_filesystem->remote = -1; if (tree_current_is_symblic_link_target(t)) { #if defined(HAVE_OPENAT) /* * Get file system statistics on any directory * where current is. */ int fd = openat(tree_current_dir_fd(t), tree_current_access_path(t), O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); return (ARCHIVE_FAILED); } r = fstatfs(fd, &sfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); close(fd); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } r = statfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { r = fstatfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); } if (r == -1 || xr == -1) { archive_set_error(&a->archive, errno, "statfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ t->current_filesystem->xfer_align = sfs.f_bsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = sfs.f_iosize; t->current_filesystem->incr_xfer_size = sfs.f_iosize; } if (sfs.f_flags & MNT_LOCAL) t->current_filesystem->remote = 0; else t->current_filesystem->remote = 1; #if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC) r = getvfsbyname(sfs.f_fstypename, &vfc); if (r == -1) { archive_set_error(&a->archive, errno, "getvfsbyname failed"); return (ARCHIVE_FAILED); } if (vfc.vfc_flags & VFCF_SYNTHETIC) t->current_filesystem->synthetic = 1; else t->current_filesystem->synthetic = 0; #endif #if defined(MNT_NOATIME) if (sfs.f_flags & MNT_NOATIME) t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; #if defined(USE_READDIR_R) /* Set maximum filename length. */ #if defined(HAVE_STRUCT_STATFS_F_NAMEMAX) t->current_filesystem->name_max = sfs.f_namemax; #else # if defined(_PC_NAME_MAX) /* Mac OS X does not have f_namemax in struct statfs. */ if (tree_current_is_symblic_link_target(t)) { if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX); } else nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX); # else nm = -1; # endif if (nm == -1) t->current_filesystem->name_max = NAME_MAX; else t->current_filesystem->name_max = nm; #endif #endif /* USE_READDIR_R */ return (ARCHIVE_OK); } #elif (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS)) && defined(ST_LOCAL) /* * Gather current filesystem properties on NetBSD */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statvfs sfs; int r, xr = 0; t->current_filesystem->synthetic = -1; if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } if (tree_current_is_symblic_link_target(t)) { r = statvfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); } else { #ifdef HAVE_FSTATVFS r = fstatvfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); #else r = statvfs(".", &sfs); if (r == 0) xr = get_xfer_size(t, -1, "."); #endif } if (r == -1 || xr == -1) { t->current_filesystem->remote = -1; archive_set_error(&a->archive, errno, "statvfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* Usually come here unless NetBSD supports _PC_REC_XFER_ALIGN * for pathconf() function. */ t->current_filesystem->xfer_align = sfs.f_frsize; t->current_filesystem->max_xfer_size = -1; #if defined(HAVE_STRUCT_STATVFS_F_IOSIZE) t->current_filesystem->min_xfer_size = sfs.f_iosize; t->current_filesystem->incr_xfer_size = sfs.f_iosize; #else t->current_filesystem->min_xfer_size = sfs.f_bsize; t->current_filesystem->incr_xfer_size = sfs.f_bsize; #endif } if (sfs.f_flag & ST_LOCAL) t->current_filesystem->remote = 0; else t->current_filesystem->remote = 1; #if defined(ST_NOATIME) if (sfs.f_flag & ST_NOATIME) t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; /* Set maximum filename length. */ t->current_filesystem->name_max = sfs.f_namemax; return (ARCHIVE_OK); } #elif defined(HAVE_SYS_STATFS_H) && defined(HAVE_LINUX_MAGIC_H) &&\ defined(HAVE_STATFS) && defined(HAVE_FSTATFS) /* * Note: statfs is deprecated since LSB 3.2 */ #ifndef CIFS_SUPER_MAGIC #define CIFS_SUPER_MAGIC 0xFF534D42 #endif #ifndef DEVFS_SUPER_MAGIC #define DEVFS_SUPER_MAGIC 0x1373 #endif /* * Gather current filesystem properties on Linux */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statfs sfs; #if defined(HAVE_STATVFS) struct statvfs svfs; #endif int r, vr = 0, xr = 0; if (tree_current_is_symblic_link_target(t)) { #if defined(HAVE_OPENAT) /* * Get file system statistics on any directory * where current is. */ int fd = openat(tree_current_dir_fd(t), tree_current_access_path(t), O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); return (ARCHIVE_FAILED); } #if defined(HAVE_FSTATVFS) vr = fstatvfs(fd, &svfs);/* for f_flag, mount flags */ #endif r = fstatfs(fd, &sfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); close(fd); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } #if defined(HAVE_STATVFS) vr = statvfs(tree_current_access_path(t), &svfs); #endif r = statfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { #ifdef HAVE_FSTATFS #if defined(HAVE_FSTATVFS) vr = fstatvfs(tree_current_dir_fd(t), &svfs); #endif r = fstatfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } #if defined(HAVE_STATVFS) vr = statvfs(".", &svfs); #endif r = statfs(".", &sfs); if (r == 0) xr = get_xfer_size(t, -1, "."); #endif } if (r == -1 || xr == -1 || vr == -1) { t->current_filesystem->synthetic = -1; t->current_filesystem->remote = -1; archive_set_error(&a->archive, errno, "statfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ #if defined(HAVE_STATVFS) t->current_filesystem->xfer_align = svfs.f_frsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = svfs.f_bsize; t->current_filesystem->incr_xfer_size = svfs.f_bsize; #else t->current_filesystem->xfer_align = sfs.f_frsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = sfs.f_bsize; t->current_filesystem->incr_xfer_size = sfs.f_bsize; #endif } switch (sfs.f_type) { case AFS_SUPER_MAGIC: case CIFS_SUPER_MAGIC: case CODA_SUPER_MAGIC: case NCP_SUPER_MAGIC:/* NetWare */ case NFS_SUPER_MAGIC: case SMB_SUPER_MAGIC: t->current_filesystem->remote = 1; t->current_filesystem->synthetic = 0; break; case DEVFS_SUPER_MAGIC: case PROC_SUPER_MAGIC: case USBDEVICE_SUPER_MAGIC: t->current_filesystem->remote = 0; t->current_filesystem->synthetic = 1; break; default: t->current_filesystem->remote = 0; t->current_filesystem->synthetic = 0; break; } #if defined(ST_NOATIME) #if defined(HAVE_STATVFS) if (svfs.f_flag & ST_NOATIME) #else if (sfs.f_flag & ST_NOATIME) #endif t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; #if defined(USE_READDIR_R) /* Set maximum filename length. */ t->current_filesystem->name_max = sfs.f_namelen; #endif return (ARCHIVE_OK); } #elif defined(HAVE_SYS_STATVFS_H) &&\ (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS)) /* * Gather current filesystem properties on other posix platform. */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; struct statvfs sfs; int r, xr = 0; t->current_filesystem->synthetic = -1;/* Not supported */ t->current_filesystem->remote = -1;/* Not supported */ if (tree_current_is_symblic_link_target(t)) { #if defined(HAVE_OPENAT) /* * Get file system statistics on any directory * where current is. */ int fd = openat(tree_current_dir_fd(t), tree_current_access_path(t), O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); return (ARCHIVE_FAILED); } r = fstatvfs(fd, &sfs); if (r == 0) xr = get_xfer_size(t, fd, NULL); close(fd); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } r = statvfs(tree_current_access_path(t), &sfs); if (r == 0) xr = get_xfer_size(t, -1, tree_current_access_path(t)); #endif } else { #ifdef HAVE_FSTATVFS r = fstatvfs(tree_current_dir_fd(t), &sfs); if (r == 0) xr = get_xfer_size(t, tree_current_dir_fd(t), NULL); #else if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } r = statvfs(".", &sfs); if (r == 0) xr = get_xfer_size(t, -1, "."); #endif } if (r == -1 || xr == -1) { t->current_filesystem->synthetic = -1; t->current_filesystem->remote = -1; archive_set_error(&a->archive, errno, "statvfs failed"); return (ARCHIVE_FAILED); } else if (xr == 1) { /* pathconf(_PC_REX_*) operations are not supported. */ t->current_filesystem->xfer_align = sfs.f_frsize; t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = sfs.f_bsize; t->current_filesystem->incr_xfer_size = sfs.f_bsize; } #if defined(ST_NOATIME) if (sfs.f_flag & ST_NOATIME) t->current_filesystem->noatime = 1; else #endif t->current_filesystem->noatime = 0; #if defined(USE_READDIR_R) /* Set maximum filename length. */ t->current_filesystem->name_max = sfs.f_namemax; #endif return (ARCHIVE_OK); } #else /* * Generic: Gather current filesystem properties. * TODO: Is this generic function really needed? */ static int setup_current_filesystem(struct archive_read_disk *a) { struct tree *t = a->tree; #if defined(_PC_NAME_MAX) && defined(USE_READDIR_R) long nm; #endif t->current_filesystem->synthetic = -1;/* Not supported */ t->current_filesystem->remote = -1;/* Not supported */ t->current_filesystem->noatime = 0; (void)get_xfer_size(t, -1, ".");/* Dummy call to avoid build error. */ t->current_filesystem->xfer_align = -1;/* Unknown */ t->current_filesystem->max_xfer_size = -1; t->current_filesystem->min_xfer_size = -1; t->current_filesystem->incr_xfer_size = -1; #if defined(USE_READDIR_R) /* Set maximum filename length. */ # if defined(_PC_NAME_MAX) if (tree_current_is_symblic_link_target(t)) { if (tree_enter_working_dir(t) != 0) { archive_set_error(&a->archive, errno, "fchdir failed"); return (ARCHIVE_FAILED); } nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX); } else nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX); if (nm == -1) # endif /* _PC_NAME_MAX */ /* * Some systems (HP-UX or others?) incorrectly defined * NAME_MAX macro to be a smaller value. */ # if defined(NAME_MAX) && NAME_MAX >= 255 t->current_filesystem->name_max = NAME_MAX; # else /* No way to get a trusted value of maximum filename * length. */ t->current_filesystem->name_max = PATH_MAX; # endif /* NAME_MAX */ # if defined(_PC_NAME_MAX) else t->current_filesystem->name_max = nm; # endif /* _PC_NAME_MAX */ #endif /* USE_READDIR_R */ return (ARCHIVE_OK); } #endif static int close_and_restore_time(int fd, struct tree *t, struct restore_time *rt) { #ifndef HAVE_UTIMES (void)t; /* UNUSED */ (void)rt; /* UNUSED */ return (close(fd)); #else #if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__) struct timespec timespecs[2]; #endif struct timeval times[2]; if ((t->flags & needsRestoreTimes) == 0 || rt->noatime) { if (fd >= 0) return (close(fd)); else return (0); } #if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__) timespecs[1].tv_sec = rt->mtime; timespecs[1].tv_nsec = rt->mtime_nsec; timespecs[0].tv_sec = rt->atime; timespecs[0].tv_nsec = rt->atime_nsec; /* futimens() is defined in POSIX.1-2008. */ if (futimens(fd, timespecs) == 0) return (close(fd)); #endif times[1].tv_sec = rt->mtime; times[1].tv_usec = rt->mtime_nsec / 1000; times[0].tv_sec = rt->atime; times[0].tv_usec = rt->atime_nsec / 1000; #if !defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES) && !defined(__CYGWIN__) if (futimes(fd, times) == 0) return (close(fd)); #endif close(fd); #if defined(HAVE_FUTIMESAT) if (futimesat(tree_current_dir_fd(t), rt->name, times) == 0) return (0); #endif #ifdef HAVE_LUTIMES if (lutimes(rt->name, times) != 0) #else if (AE_IFLNK != rt->filetype && utimes(rt->name, times) != 0) #endif return (-1); #endif return (0); } static int open_on_current_dir(struct tree *t, const char *path, int flags) { #ifdef HAVE_OPENAT return (openat(tree_current_dir_fd(t), path, flags)); #else if (tree_enter_working_dir(t) != 0) return (-1); return (open(path, flags)); #endif } static int tree_dup(int fd) { int new_fd; #ifdef F_DUPFD_CLOEXEC static volatile int can_dupfd_cloexec = 1; if (can_dupfd_cloexec) { new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); if (new_fd != -1) return (new_fd); /* Linux 2.6.18 - 2.6.23 declare F_DUPFD_CLOEXEC, * but it cannot be used. So we have to try dup(). */ /* We won't try F_DUPFD_CLOEXEC. */ can_dupfd_cloexec = 0; } #endif /* F_DUPFD_CLOEXEC */ new_fd = dup(fd); __archive_ensure_cloexec_flag(new_fd); return (new_fd); } /* * Add a directory path to the current stack. */ static void tree_push(struct tree *t, const char *path, int filesystem_id, int64_t dev, int64_t ino, struct restore_time *rt) { struct tree_entry *te; te = calloc(1, sizeof(*te)); te->next = t->stack; te->parent = t->current; if (te->parent) te->depth = te->parent->depth + 1; t->stack = te; archive_string_init(&te->name); te->symlink_parent_fd = -1; archive_strcpy(&te->name, path); te->flags = needsDescent | needsOpen | needsAscent; te->filesystem_id = filesystem_id; te->dev = dev; te->ino = ino; te->dirname_length = t->dirname_length; te->restore_time.name = te->name.s; if (rt != NULL) { te->restore_time.mtime = rt->mtime; te->restore_time.mtime_nsec = rt->mtime_nsec; te->restore_time.atime = rt->atime; te->restore_time.atime_nsec = rt->atime_nsec; te->restore_time.filetype = rt->filetype; te->restore_time.noatime = rt->noatime; } } /* * Append a name to the current dir path. */ static void tree_append(struct tree *t, const char *name, size_t name_length) { size_t size_needed; t->path.s[t->dirname_length] = '\0'; t->path.length = t->dirname_length; /* Strip trailing '/' from name, unless entire name is "/". */ while (name_length > 1 && name[name_length - 1] == '/') name_length--; /* Resize pathname buffer as needed. */ size_needed = name_length + t->dirname_length + 2; archive_string_ensure(&t->path, size_needed); /* Add a separating '/' if it's needed. */ if (t->dirname_length > 0 && t->path.s[archive_strlen(&t->path)-1] != '/') archive_strappend_char(&t->path, '/'); t->basename = t->path.s + archive_strlen(&t->path); archive_strncat(&t->path, name, name_length); t->restore_time.name = t->basename; } /* * Open a directory tree for traversal. */ static struct tree * tree_open(const char *path, int symlink_mode, int restore_time) { struct tree *t; if ((t = calloc(1, sizeof(*t))) == NULL) return (NULL); archive_string_init(&t->path); archive_string_ensure(&t->path, 31); t->initial_symlink_mode = symlink_mode; return (tree_reopen(t, path, restore_time)); } static struct tree * tree_reopen(struct tree *t, const char *path, int restore_time) { t->flags = (restore_time != 0)?needsRestoreTimes:0; t->flags |= onInitialDir; t->visit_type = 0; t->tree_errno = 0; t->dirname_length = 0; t->depth = 0; t->descend = 0; t->current = NULL; t->d = INVALID_DIR_HANDLE; t->symlink_mode = t->initial_symlink_mode; archive_string_empty(&t->path); t->entry_fd = -1; t->entry_eof = 0; t->entry_remaining_bytes = 0; t->initial_filesystem_id = -1; /* First item is set up a lot like a symlink traversal. */ tree_push(t, path, 0, 0, 0, NULL); t->stack->flags = needsFirstVisit; t->maxOpenCount = t->openCount = 1; t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(t->initial_dir_fd); t->working_dir_fd = tree_dup(t->initial_dir_fd); return (t); } static int tree_descent(struct tree *t) { int flag, new_fd, r = 0; t->dirname_length = archive_strlen(&t->path); flag = O_RDONLY | O_CLOEXEC; #if defined(O_DIRECTORY) flag |= O_DIRECTORY; #endif new_fd = open_on_current_dir(t, t->stack->name.s, flag); __archive_ensure_cloexec_flag(new_fd); if (new_fd < 0) { t->tree_errno = errno; r = TREE_ERROR_DIR; } else { t->depth++; /* If it is a link, set up fd for the ascent. */ if (t->stack->flags & isDirLink) { t->stack->symlink_parent_fd = t->working_dir_fd; t->openCount++; if (t->openCount > t->maxOpenCount) t->maxOpenCount = t->openCount; } else close(t->working_dir_fd); /* Renew the current working directory. */ t->working_dir_fd = new_fd; t->flags &= ~onWorkingDir; } return (r); } /* * We've finished a directory; ascend back to the parent. */ static int tree_ascend(struct tree *t) { struct tree_entry *te; int new_fd, r = 0, prev_dir_fd; te = t->stack; prev_dir_fd = t->working_dir_fd; if (te->flags & isDirLink) new_fd = te->symlink_parent_fd; else { new_fd = open_on_current_dir(t, "..", O_RDONLY | O_CLOEXEC); __archive_ensure_cloexec_flag(new_fd); } if (new_fd < 0) { t->tree_errno = errno; r = TREE_ERROR_FATAL; } else { /* Renew the current working directory. */ t->working_dir_fd = new_fd; t->flags &= ~onWorkingDir; /* Current directory has been changed, we should * close an fd of previous working directory. */ close_and_restore_time(prev_dir_fd, t, &te->restore_time); if (te->flags & isDirLink) { t->openCount--; te->symlink_parent_fd = -1; } t->depth--; } return (r); } /* * Return to the initial directory where tree_open() was performed. */ static int tree_enter_initial_dir(struct tree *t) { int r = 0; if ((t->flags & onInitialDir) == 0) { r = fchdir(t->initial_dir_fd); if (r == 0) { t->flags &= ~onWorkingDir; t->flags |= onInitialDir; } } return (r); } /* * Restore working directory of directory traversals. */ static int tree_enter_working_dir(struct tree *t) { int r = 0; /* * Change the current directory if really needed. * Sometimes this is unneeded when we did not do * descent. */ if (t->depth > 0 && (t->flags & onWorkingDir) == 0) { r = fchdir(t->working_dir_fd); if (r == 0) { t->flags &= ~onInitialDir; t->flags |= onWorkingDir; } } return (r); } static int tree_current_dir_fd(struct tree *t) { return (t->working_dir_fd); } /* * Pop the working stack. */ static void tree_pop(struct tree *t) { struct tree_entry *te; t->path.s[t->dirname_length] = '\0'; t->path.length = t->dirname_length; if (t->stack == t->current && t->current != NULL) t->current = t->current->parent; te = t->stack; t->stack = te->next; t->dirname_length = te->dirname_length; t->basename = t->path.s + t->dirname_length; while (t->basename[0] == '/') t->basename++; archive_string_free(&te->name); free(te); } /* * Get the next item in the tree traversal. */ static int tree_next(struct tree *t) { int r; while (t->stack != NULL) { /* If there's an open dir, get the next entry from there. */ if (t->d != INVALID_DIR_HANDLE) { r = tree_dir_next_posix(t); if (r == 0) continue; return (r); } if (t->stack->flags & needsFirstVisit) { /* Top stack item needs a regular visit. */ t->current = t->stack; tree_append(t, t->stack->name.s, archive_strlen(&(t->stack->name))); /* t->dirname_length = t->path_length; */ /* tree_pop(t); */ t->stack->flags &= ~needsFirstVisit; return (t->visit_type = TREE_REGULAR); } else if (t->stack->flags & needsDescent) { /* Top stack item is dir to descend into. */ t->current = t->stack; tree_append(t, t->stack->name.s, archive_strlen(&(t->stack->name))); t->stack->flags &= ~needsDescent; r = tree_descent(t); if (r != 0) { tree_pop(t); t->visit_type = r; } else t->visit_type = TREE_POSTDESCENT; return (t->visit_type); } else if (t->stack->flags & needsOpen) { t->stack->flags &= ~needsOpen; r = tree_dir_next_posix(t); if (r == 0) continue; return (r); } else if (t->stack->flags & needsAscent) { /* Top stack item is dir and we're done with it. */ r = tree_ascend(t); tree_pop(t); t->visit_type = r != 0 ? r : TREE_POSTASCENT; return (t->visit_type); } else { /* Top item on stack is dead. */ tree_pop(t); t->flags &= ~hasLstat; t->flags &= ~hasStat; } } return (t->visit_type = 0); } static int tree_dir_next_posix(struct tree *t) { int r; const char *name; size_t namelen; if (t->d == NULL) { #if defined(USE_READDIR_R) size_t dirent_size; #endif #if defined(HAVE_FDOPENDIR) t->d = fdopendir(tree_dup(t->working_dir_fd)); #else /* HAVE_FDOPENDIR */ if (tree_enter_working_dir(t) == 0) { t->d = opendir("."); #if HAVE_DIRFD || defined(dirfd) __archive_ensure_cloexec_flag(dirfd(t->d)); #endif } #endif /* HAVE_FDOPENDIR */ if (t->d == NULL) { r = tree_ascend(t); /* Undo "chdir" */ tree_pop(t); t->tree_errno = errno; t->visit_type = r != 0 ? r : TREE_ERROR_DIR; return (t->visit_type); } #if defined(USE_READDIR_R) dirent_size = offsetof(struct dirent, d_name) + t->filesystem_table[t->current->filesystem_id].name_max + 1; if (t->dirent == NULL || t->dirent_allocated < dirent_size) { free(t->dirent); t->dirent = malloc(dirent_size); if (t->dirent == NULL) { closedir(t->d); t->d = INVALID_DIR_HANDLE; (void)tree_ascend(t); tree_pop(t); t->tree_errno = ENOMEM; t->visit_type = TREE_ERROR_DIR; return (t->visit_type); } t->dirent_allocated = dirent_size; } #endif /* USE_READDIR_R */ } for (;;) { errno = 0; #if defined(USE_READDIR_R) r = readdir_r(t->d, t->dirent, &t->de); #ifdef _AIX /* Note: According to the man page, return value 9 indicates * that the readdir_r was not successful and the error code * is set to the global errno variable. And then if the end * of directory entries was reached, the return value is 9 * and the third parameter is set to NULL and errno is * unchanged. */ if (r == 9) r = errno; #endif /* _AIX */ if (r != 0 || t->de == NULL) { #else t->de = readdir(t->d); if (t->de == NULL) { r = errno; #endif closedir(t->d); t->d = INVALID_DIR_HANDLE; if (r != 0) { t->tree_errno = r; t->visit_type = TREE_ERROR_DIR; return (t->visit_type); } else return (0); } name = t->de->d_name; namelen = D_NAMELEN(t->de); t->flags &= ~hasLstat; t->flags &= ~hasStat; if (name[0] == '.' && name[1] == '\0') continue; if (name[0] == '.' && name[1] == '.' && name[2] == '\0') continue; tree_append(t, name, namelen); return (t->visit_type = TREE_REGULAR); } } /* * Get the stat() data for the entry just returned from tree_next(). */ static const struct stat * tree_current_stat(struct tree *t) { if (!(t->flags & hasStat)) { #ifdef HAVE_FSTATAT if (fstatat(tree_current_dir_fd(t), tree_current_access_path(t), &t->st, 0) != 0) #else if (tree_enter_working_dir(t) != 0) return NULL; if (stat(tree_current_access_path(t), &t->st) != 0) #endif return NULL; t->flags |= hasStat; } return (&t->st); } /* * Get the lstat() data for the entry just returned from tree_next(). */ static const struct stat * tree_current_lstat(struct tree *t) { if (!(t->flags & hasLstat)) { #ifdef HAVE_FSTATAT if (fstatat(tree_current_dir_fd(t), tree_current_access_path(t), &t->lst, AT_SYMLINK_NOFOLLOW) != 0) #else if (tree_enter_working_dir(t) != 0) return NULL; if (lstat(tree_current_access_path(t), &t->lst) != 0) #endif return NULL; t->flags |= hasLstat; } return (&t->lst); } /* * Test whether current entry is a dir or link to a dir. */ static int tree_current_is_dir(struct tree *t) { const struct stat *st; /* * If we already have lstat() info, then try some * cheap tests to determine if this is a dir. */ if (t->flags & hasLstat) { /* If lstat() says it's a dir, it must be a dir. */ st = tree_current_lstat(t); if (st == NULL) return 0; if (S_ISDIR(st->st_mode)) return 1; /* Not a dir; might be a link to a dir. */ /* If it's not a link, then it's not a link to a dir. */ if (!S_ISLNK(st->st_mode)) return 0; /* * It's a link, but we don't know what it's a link to, * so we'll have to use stat(). */ } st = tree_current_stat(t); /* If we can't stat it, it's not a dir. */ if (st == NULL) return 0; /* Use the definitive test. Hopefully this is cached. */ return (S_ISDIR(st->st_mode)); } /* * Test whether current entry is a physical directory. Usually, we * already have at least one of stat() or lstat() in memory, so we * use tricks to try to avoid an extra trip to the disk. */ static int tree_current_is_physical_dir(struct tree *t) { const struct stat *st; /* * If stat() says it isn't a dir, then it's not a dir. * If stat() data is cached, this check is free, so do it first. */ if (t->flags & hasStat) { st = tree_current_stat(t); if (st == NULL) return (0); if (!S_ISDIR(st->st_mode)) return (0); } /* * Either stat() said it was a dir (in which case, we have * to determine whether it's really a link to a dir) or * stat() info wasn't available. So we use lstat(), which * hopefully is already cached. */ st = tree_current_lstat(t); /* If we can't stat it, it's not a dir. */ if (st == NULL) return 0; /* Use the definitive test. Hopefully this is cached. */ return (S_ISDIR(st->st_mode)); } /* * Test whether the same file has been in the tree as its parent. */ static int tree_target_is_same_as_parent(struct tree *t, const struct stat *st) { struct tree_entry *te; for (te = t->current->parent; te != NULL; te = te->parent) { if (te->dev == (int64_t)st->st_dev && te->ino == (int64_t)st->st_ino) return (1); } return (0); } /* * Test whether the current file is symbolic link target and * on the other filesystem. */ static int tree_current_is_symblic_link_target(struct tree *t) { static const struct stat *lst, *st; lst = tree_current_lstat(t); st = tree_current_stat(t); return (st != NULL && lst != NULL && (int64_t)st->st_dev == t->current_filesystem->dev && st->st_dev != lst->st_dev); } /* * Return the access path for the entry just returned from tree_next(). */ static const char * tree_current_access_path(struct tree *t) { return (t->basename); } /* * Return the full path for the entry just returned from tree_next(). */ static const char * tree_current_path(struct tree *t) { return (t->path.s); } /* * Terminate the traversal. */ static void tree_close(struct tree *t) { if (t == NULL) return; if (t->entry_fd >= 0) { close_and_restore_time(t->entry_fd, t, &t->restore_time); t->entry_fd = -1; } /* Close the handle of readdir(). */ if (t->d != INVALID_DIR_HANDLE) { closedir(t->d); t->d = INVALID_DIR_HANDLE; } /* Release anything remaining in the stack. */ while (t->stack != NULL) { if (t->stack->flags & isDirLink) close(t->stack->symlink_parent_fd); tree_pop(t); } if (t->working_dir_fd >= 0) { close(t->working_dir_fd); t->working_dir_fd = -1; } if (t->initial_dir_fd >= 0) { close(t->initial_dir_fd); t->initial_dir_fd = -1; } } /* * Release any resources. */ static void tree_free(struct tree *t) { int i; if (t == NULL) return; archive_string_free(&t->path); #if defined(USE_READDIR_R) free(t->dirent); #endif free(t->sparse_list); for (i = 0; i < t->max_filesystem_id; i++) free(t->filesystem_table[i].allocation_ptr); free(t->filesystem_table); free(t); } #endif Index: stable/10/contrib/libarchive/libarchive/archive_read_open_file.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_open_file.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_read_open_file.c (revision 344674) @@ -1,181 +1,180 @@ /*- * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_IO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "archive.h" struct read_FILE_data { FILE *f; size_t block_size; void *buffer; char can_skip; }; static int file_close(struct archive *, void *); static ssize_t file_read(struct archive *, void *, const void **buff); static int64_t file_skip(struct archive *, void *, int64_t request); int archive_read_open_FILE(struct archive *a, FILE *f) { struct stat st; struct read_FILE_data *mine; size_t block_size = 128 * 1024; void *b; archive_clear_error(a); mine = (struct read_FILE_data *)malloc(sizeof(*mine)); b = malloc(block_size); if (mine == NULL || b == NULL) { archive_set_error(a, ENOMEM, "No memory"); free(mine); free(b); return (ARCHIVE_FATAL); } mine->block_size = block_size; mine->buffer = b; mine->f = f; /* * If we can't fstat() the file, it may just be that it's not * a file. (On some platforms, FILE * objects can wrap I/O * streams that don't support fileno()). As a result, fileno() * should be used cautiously.) */ if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) { archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); /* Enable the seek optimization only for regular files. */ mine->can_skip = 1; } else mine->can_skip = 0; #if defined(__CYGWIN__) || defined(_WIN32) setmode(fileno(mine->f), O_BINARY); #endif archive_read_set_read_callback(a, file_read); archive_read_set_skip_callback(a, file_skip); archive_read_set_close_callback(a, file_close); archive_read_set_callback_data(a, mine); return (archive_read_open1(a)); } static ssize_t file_read(struct archive *a, void *client_data, const void **buff) { struct read_FILE_data *mine = (struct read_FILE_data *)client_data; size_t bytes_read; *buff = mine->buffer; bytes_read = fread(mine->buffer, 1, mine->block_size, mine->f); if (bytes_read < mine->block_size && ferror(mine->f)) { archive_set_error(a, errno, "Error reading file"); } return (bytes_read); } static int64_t file_skip(struct archive *a, void *client_data, int64_t request) { struct read_FILE_data *mine = (struct read_FILE_data *)client_data; #if HAVE_FSEEKO off_t skip = (off_t)request; #elif HAVE__FSEEKI64 int64_t skip = request; #else long skip = (long)request; #endif int skip_bits = sizeof(skip) * 8 - 1; (void)a; /* UNUSED */ /* * If we can't skip, return 0 as the amount we did step and * the caller will work around by reading and discarding. */ if (!mine->can_skip) return (0); if (request == 0) return (0); /* If request is too big for a long or an off_t, reduce it. */ if (sizeof(request) > sizeof(skip)) { int64_t max_skip = (((int64_t)1 << (skip_bits - 1)) - 1) * 2 + 1; if (request > max_skip) skip = max_skip; } #ifdef __ANDROID__ /* fileno() isn't safe on all platforms ... see above. */ if (lseek(fileno(mine->f), skip, SEEK_CUR) < 0) #elif HAVE_FSEEKO if (fseeko(mine->f, skip, SEEK_CUR) != 0) #elif HAVE__FSEEKI64 if (_fseeki64(mine->f, skip, SEEK_CUR) != 0) #else if (fseek(mine->f, skip, SEEK_CUR) != 0) #endif { mine->can_skip = 0; return (0); } return (request); } static int file_close(struct archive *a, void *client_data) { struct read_FILE_data *mine = (struct read_FILE_data *)client_data; (void)a; /* UNUSED */ - if (mine->buffer != NULL) - free(mine->buffer); + free(mine->buffer); free(mine); return (ARCHIVE_OK); } Index: stable/10/contrib/libarchive/libarchive/archive_read_support_format_7zip.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_support_format_7zip.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_read_support_format_7zip.c (revision 344674) @@ -1,3873 +1,3866 @@ /*- * Copyright (c) 2011 Michihiro NAKAJIMA * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_BZLIB_H #include #endif #ifdef HAVE_LZMA_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_ppmd7_private.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_endian.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" #endif #define _7ZIP_SIGNATURE "7z\xBC\xAF\x27\x1C" #define SFX_MIN_ADDR 0x27000 #define SFX_MAX_ADDR 0x60000 /* * Codec ID */ #define _7Z_COPY 0 #define _7Z_LZMA 0x030101 #define _7Z_LZMA2 0x21 #define _7Z_DEFLATE 0x040108 #define _7Z_BZ2 0x040202 #define _7Z_PPMD 0x030401 #define _7Z_DELTA 0x03 #define _7Z_CRYPTO_MAIN_ZIP 0x06F10101 /* Main Zip crypto algo */ #define _7Z_CRYPTO_RAR_29 0x06F10303 /* Rar29 AES-128 + (modified SHA-1) */ #define _7Z_CRYPTO_AES_256_SHA_256 0x06F10701 /* AES-256 + SHA-256 */ #define _7Z_X86 0x03030103 #define _7Z_X86_BCJ2 0x0303011B #define _7Z_POWERPC 0x03030205 #define _7Z_IA64 0x03030401 #define _7Z_ARM 0x03030501 #define _7Z_ARMTHUMB 0x03030701 #define _7Z_SPARC 0x03030805 /* * 7-Zip header property IDs. */ #define kEnd 0x00 #define kHeader 0x01 #define kArchiveProperties 0x02 #define kAdditionalStreamsInfo 0x03 #define kMainStreamsInfo 0x04 #define kFilesInfo 0x05 #define kPackInfo 0x06 #define kUnPackInfo 0x07 #define kSubStreamsInfo 0x08 #define kSize 0x09 #define kCRC 0x0A #define kFolder 0x0B #define kCodersUnPackSize 0x0C #define kNumUnPackStream 0x0D #define kEmptyStream 0x0E #define kEmptyFile 0x0F #define kAnti 0x10 #define kName 0x11 #define kCTime 0x12 #define kATime 0x13 #define kMTime 0x14 #define kAttributes 0x15 #define kEncodedHeader 0x17 #define kDummy 0x19 struct _7z_digests { unsigned char *defineds; uint32_t *digests; }; struct _7z_folder { uint64_t numCoders; struct _7z_coder { unsigned long codec; uint64_t numInStreams; uint64_t numOutStreams; uint64_t propertiesSize; unsigned char *properties; } *coders; uint64_t numBindPairs; struct { uint64_t inIndex; uint64_t outIndex; } *bindPairs; uint64_t numPackedStreams; uint64_t *packedStreams; uint64_t numInStreams; uint64_t numOutStreams; uint64_t *unPackSize; unsigned char digest_defined; uint32_t digest; uint64_t numUnpackStreams; uint32_t packIndex; /* Unoperated bytes. */ uint64_t skipped_bytes; }; struct _7z_coders_info { uint64_t numFolders; struct _7z_folder *folders; uint64_t dataStreamIndex; }; struct _7z_pack_info { uint64_t pos; uint64_t numPackStreams; uint64_t *sizes; struct _7z_digests digest; /* Calculated from pos and numPackStreams. */ uint64_t *positions; }; struct _7z_substream_info { size_t unpack_streams; uint64_t *unpackSizes; unsigned char *digestsDefined; uint32_t *digests; }; struct _7z_stream_info { struct _7z_pack_info pi; struct _7z_coders_info ci; struct _7z_substream_info ss; }; struct _7z_header_info { uint64_t dataIndex; unsigned char *emptyStreamBools; unsigned char *emptyFileBools; unsigned char *antiBools; unsigned char *attrBools; }; struct _7zip_entry { size_t name_len; unsigned char *utf16name; #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) const wchar_t *wname; #endif uint32_t folderIndex; uint32_t ssIndex; unsigned flg; #define MTIME_IS_SET (1<<0) #define ATIME_IS_SET (1<<1) #define CTIME_IS_SET (1<<2) #define CRC32_IS_SET (1<<3) #define HAS_STREAM (1<<4) time_t mtime; time_t atime; time_t ctime; long mtime_ns; long atime_ns; long ctime_ns; uint32_t mode; uint32_t attr; }; struct _7zip { /* Structural information about the archive. */ struct _7z_stream_info si; int header_is_being_read; int header_is_encoded; uint64_t header_bytes_remaining; unsigned long header_crc32; /* Header offset to check that reading points of the file contents * will not exceed the header. */ uint64_t header_offset; /* Base offset of the archive file for a seek in case reading SFX. */ uint64_t seek_base; /* List of entries */ size_t entries_remaining; uint64_t numFiles; struct _7zip_entry *entries; struct _7zip_entry *entry; unsigned char *entry_names; /* entry_bytes_remaining is the number of bytes we expect. */ int64_t entry_offset; uint64_t entry_bytes_remaining; /* Running CRC32 of the decompressed data */ unsigned long entry_crc32; /* Flags to mark progress of decompression. */ char end_of_entry; /* Uncompressed buffer control. */ #define UBUFF_SIZE (64 * 1024) unsigned char *uncompressed_buffer; unsigned char *uncompressed_buffer_pointer; size_t uncompressed_buffer_size; size_t uncompressed_buffer_bytes_remaining; /* Offset of the compressed data. */ int64_t stream_offset; /* * Decompressing control data. */ unsigned folder_index; uint64_t folder_outbytes_remaining; unsigned pack_stream_index; unsigned pack_stream_remaining; uint64_t pack_stream_inbytes_remaining; size_t pack_stream_bytes_unconsumed; /* The codec information of a folder. */ unsigned long codec; unsigned long codec2; /* * Decompressor controllers. */ /* Decoding LZMA1 and LZMA2 data. */ #ifdef HAVE_LZMA_H lzma_stream lzstream; int lzstream_valid; #endif /* Decoding bzip2 data. */ #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) bz_stream bzstream; int bzstream_valid; #endif /* Decoding deflate data. */ #ifdef HAVE_ZLIB_H z_stream stream; int stream_valid; #endif /* Decoding PPMd data. */ int ppmd7_stat; CPpmd7 ppmd7_context; CPpmd7z_RangeDec range_dec; IByteIn bytein; struct { const unsigned char *next_in; int64_t avail_in; int64_t total_in; unsigned char *next_out; int64_t avail_out; int64_t total_out; int overconsumed; } ppstream; int ppmd7_valid; /* Decoding BCJ and BCJ2 data. */ uint32_t bcj_state; size_t odd_bcj_size; unsigned char odd_bcj[4]; /* Decoding BCJ data. */ size_t bcj_prevPosT; uint32_t bcj_prevMask; uint32_t bcj_ip; /* Decoding BCJ2 data. */ size_t main_stream_bytes_remaining; unsigned char *sub_stream_buff[3]; size_t sub_stream_size[3]; size_t sub_stream_bytes_remaining[3]; unsigned char *tmp_stream_buff; size_t tmp_stream_buff_size; size_t tmp_stream_bytes_avail; size_t tmp_stream_bytes_remaining; #ifdef _LZMA_PROB32 #define CProb uint32_t #else #define CProb uint16_t #endif CProb bcj2_p[256 + 2]; uint8_t bcj2_prevByte; uint32_t bcj2_range; uint32_t bcj2_code; uint64_t bcj2_outPos; /* Filename character-set conversion data. */ struct archive_string_conv *sconv; char format_name[64]; /* Custom value that is non-zero if this archive contains encrypted entries. */ int has_encrypted_entries; }; /* Maximum entry size. This limitation prevents reading intentional * corrupted 7-zip files on assuming there are not so many entries in * the files. */ #define UMAX_ENTRY ARCHIVE_LITERAL_ULL(100000000) static int archive_read_format_7zip_has_encrypted_entries(struct archive_read *); static int archive_read_support_format_7zip_capabilities(struct archive_read *a); static int archive_read_format_7zip_bid(struct archive_read *, int); static int archive_read_format_7zip_cleanup(struct archive_read *); static int archive_read_format_7zip_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_7zip_read_data_skip(struct archive_read *); static int archive_read_format_7zip_read_header(struct archive_read *, struct archive_entry *); static int check_7zip_header_in_sfx(const char *); static unsigned long decode_codec_id(const unsigned char *, size_t); static int decode_encoded_header_info(struct archive_read *, struct _7z_stream_info *); static int decompress(struct archive_read *, struct _7zip *, void *, size_t *, const void *, size_t *); static ssize_t extract_pack_stream(struct archive_read *, size_t); static void fileTimeToUtc(uint64_t, time_t *, long *); static uint64_t folder_uncompressed_size(struct _7z_folder *); static void free_CodersInfo(struct _7z_coders_info *); static void free_Digest(struct _7z_digests *); static void free_Folder(struct _7z_folder *); static void free_Header(struct _7z_header_info *); static void free_PackInfo(struct _7z_pack_info *); static void free_StreamsInfo(struct _7z_stream_info *); static void free_SubStreamsInfo(struct _7z_substream_info *); static int free_decompression(struct archive_read *, struct _7zip *); static ssize_t get_uncompressed_data(struct archive_read *, const void **, size_t, size_t); static const unsigned char * header_bytes(struct archive_read *, size_t); static int init_decompression(struct archive_read *, struct _7zip *, const struct _7z_coder *, const struct _7z_coder *); static int parse_7zip_uint64(struct archive_read *, uint64_t *); static int read_Bools(struct archive_read *, unsigned char *, size_t); static int read_CodersInfo(struct archive_read *, struct _7z_coders_info *); static int read_Digests(struct archive_read *, struct _7z_digests *, size_t); static int read_Folder(struct archive_read *, struct _7z_folder *); static int read_Header(struct archive_read *, struct _7z_header_info *, int); static int read_PackInfo(struct archive_read *, struct _7z_pack_info *); static int read_StreamsInfo(struct archive_read *, struct _7z_stream_info *); static int read_SubStreamsInfo(struct archive_read *, struct _7z_substream_info *, struct _7z_folder *, size_t); static int read_Times(struct archive_read *, struct _7z_header_info *, int); static void read_consume(struct archive_read *); static ssize_t read_stream(struct archive_read *, const void **, size_t, size_t); static int seek_pack(struct archive_read *); static int64_t skip_stream(struct archive_read *, size_t); static int skip_sfx(struct archive_read *, ssize_t); static int slurp_central_directory(struct archive_read *, struct _7zip *, struct _7z_header_info *); static int setup_decode_folder(struct archive_read *, struct _7z_folder *, int); static void x86_Init(struct _7zip *); static size_t x86_Convert(struct _7zip *, uint8_t *, size_t); static ssize_t Bcj2_Decode(struct _7zip *, uint8_t *, size_t); int archive_read_support_format_7zip(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct _7zip *zip; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_7zip"); zip = calloc(1, sizeof(*zip)); if (zip == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate 7zip data"); return (ARCHIVE_FATAL); } /* * Until enough data has been read, we cannot tell about * any encrypted entries yet. */ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; r = __archive_read_register_format(a, zip, "7zip", archive_read_format_7zip_bid, NULL, archive_read_format_7zip_read_header, archive_read_format_7zip_read_data, archive_read_format_7zip_read_data_skip, NULL, archive_read_format_7zip_cleanup, archive_read_support_format_7zip_capabilities, archive_read_format_7zip_has_encrypted_entries); if (r != ARCHIVE_OK) free(zip); return (ARCHIVE_OK); } static int archive_read_support_format_7zip_capabilities(struct archive_read * a) { (void)a; /* UNUSED */ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); } static int archive_read_format_7zip_has_encrypted_entries(struct archive_read *_a) { if (_a && _a->format) { struct _7zip * zip = (struct _7zip *)_a->format->data; if (zip) { return zip->has_encrypted_entries; } } return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; } static int archive_read_format_7zip_bid(struct archive_read *a, int best_bid) { const char *p; /* If someone has already bid more than 32, then avoid trashing the look-ahead buffers with a seek. */ if (best_bid > 32) return (-1); if ((p = __archive_read_ahead(a, 6, NULL)) == NULL) return (0); /* If first six bytes are the 7-Zip signature, * return the bid right now. */ if (memcmp(p, _7ZIP_SIGNATURE, 6) == 0) return (48); /* * It may a 7-Zip SFX archive file. If first two bytes are * 'M' and 'Z' available on Windows or first four bytes are * "\x7F\x45LF" available on posix like system, seek the 7-Zip * signature. Although we will perform a seek when reading * a header, what we do not use __archive_read_seek() here is * due to a bidding performance. */ if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) { ssize_t offset = SFX_MIN_ADDR; ssize_t window = 4096; ssize_t bytes_avail; while (offset + window <= (SFX_MAX_ADDR)) { const char *buff = __archive_read_ahead(a, offset + window, &bytes_avail); if (buff == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < 0x40) return (0); continue; } p = buff + offset; while (p + 32 < buff + bytes_avail) { int step = check_7zip_header_in_sfx(p); if (step == 0) return (48); p += step; } offset = p - buff; } } return (0); } static int check_7zip_header_in_sfx(const char *p) { switch ((unsigned char)p[5]) { case 0x1C: if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) return (6); /* * Test the CRC because its extraction code has 7-Zip * Magic Code, so we should do this in order not to * make a mis-detection. */ if (crc32(0, (const unsigned char *)p + 12, 20) != archive_le32dec(p + 8)) return (6); /* Hit the header! */ return (0); case 0x37: return (5); case 0x7A: return (4); case 0xBC: return (3); case 0xAF: return (2); case 0x27: return (1); default: return (6); } } static int skip_sfx(struct archive_read *a, ssize_t bytes_avail) { const void *h; const char *p, *q; size_t skip, offset; ssize_t bytes, window; /* * If bytes_avail > SFX_MIN_ADDR we do not have to call * __archive_read_seek() at this time since we have * already had enough data. */ if (bytes_avail > SFX_MIN_ADDR) __archive_read_consume(a, SFX_MIN_ADDR); else if (__archive_read_seek(a, SFX_MIN_ADDR, SEEK_SET) < 0) return (ARCHIVE_FATAL); offset = 0; window = 1; while (offset + window <= SFX_MAX_ADDR - SFX_MIN_ADDR) { h = __archive_read_ahead(a, window, &bytes); if (h == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < 0x40) goto fatal; continue; } if (bytes < 6) { /* This case might happen when window == 1. */ window = 4096; continue; } p = (const char *)h; q = p + bytes; /* * Scan ahead until we find something that looks * like the 7-Zip header. */ while (p + 32 < q) { int step = check_7zip_header_in_sfx(p); if (step == 0) { struct _7zip *zip = (struct _7zip *)a->format->data; skip = p - (const char *)h; __archive_read_consume(a, skip); zip->seek_base = SFX_MIN_ADDR + offset + skip; return (ARCHIVE_OK); } p += step; } skip = p - (const char *)h; __archive_read_consume(a, skip); offset += skip; if (window == 1) window = 4096; } fatal: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Couldn't find out 7-Zip header"); return (ARCHIVE_FATAL); } static int archive_read_format_7zip_read_header(struct archive_read *a, struct archive_entry *entry) { struct _7zip *zip = (struct _7zip *)a->format->data; struct _7zip_entry *zip_entry; int r, ret = ARCHIVE_OK; struct _7z_folder *folder = 0; uint64_t fidx = 0; /* * It should be sufficient to call archive_read_next_header() for * a reader to determine if an entry is encrypted or not. If the * encryption of an entry is only detectable when calling * archive_read_data(), so be it. We'll do the same check there * as well. */ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } a->archive.archive_format = ARCHIVE_FORMAT_7ZIP; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "7-Zip"; if (zip->entries == NULL) { struct _7z_header_info header; memset(&header, 0, sizeof(header)); r = slurp_central_directory(a, zip, &header); free_Header(&header); if (r != ARCHIVE_OK) return (r); zip->entries_remaining = (size_t)zip->numFiles; zip->entry = zip->entries; } else { ++zip->entry; } zip_entry = zip->entry; if (zip->entries_remaining <= 0 || zip_entry == NULL) return ARCHIVE_EOF; --zip->entries_remaining; zip->entry_offset = 0; zip->end_of_entry = 0; zip->entry_crc32 = crc32(0, NULL, 0); /* Setup a string conversion for a filename. */ if (zip->sconv == NULL) { zip->sconv = archive_string_conversion_from_charset( &a->archive, "UTF-16LE", 1); if (zip->sconv == NULL) return (ARCHIVE_FATAL); } /* Figure out if the entry is encrypted by looking at the folder that is associated to the current 7zip entry. If the folder has a coder with a _7Z_CRYPTO codec then the folder is encrypted. Hence the entry must also be encrypted. */ if (zip_entry && zip_entry->folderIndex < zip->si.ci.numFolders) { folder = &(zip->si.ci.folders[zip_entry->folderIndex]); for (fidx=0; folder && fidxnumCoders; fidx++) { switch(folder->coders[fidx].codec) { case _7Z_CRYPTO_MAIN_ZIP: case _7Z_CRYPTO_RAR_29: case _7Z_CRYPTO_AES_256_SHA_256: { archive_entry_set_is_data_encrypted(entry, 1); zip->has_encrypted_entries = 1; break; } } } } /* Now that we've checked for encryption, if there were still no * encrypted entries found we can say for sure that there are none. */ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } if (archive_entry_copy_pathname_l(entry, (const char *)zip_entry->utf16name, zip_entry->name_len, zip->sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name(zip->sconv)); ret = ARCHIVE_WARN; } /* Populate some additional entry fields: */ archive_entry_set_mode(entry, zip_entry->mode); if (zip_entry->flg & MTIME_IS_SET) archive_entry_set_mtime(entry, zip_entry->mtime, zip_entry->mtime_ns); if (zip_entry->flg & CTIME_IS_SET) archive_entry_set_ctime(entry, zip_entry->ctime, zip_entry->ctime_ns); if (zip_entry->flg & ATIME_IS_SET) archive_entry_set_atime(entry, zip_entry->atime, zip_entry->atime_ns); if (zip_entry->ssIndex != (uint32_t)-1) { zip->entry_bytes_remaining = zip->si.ss.unpackSizes[zip_entry->ssIndex]; archive_entry_set_size(entry, zip->entry_bytes_remaining); } else { zip->entry_bytes_remaining = 0; archive_entry_set_size(entry, 0); } /* If there's no body, force read_data() to return EOF immediately. */ if (zip->entry_bytes_remaining < 1) zip->end_of_entry = 1; if ((zip_entry->mode & AE_IFMT) == AE_IFLNK) { unsigned char *symname = NULL; size_t symsize = 0; /* * Symbolic-name is recorded as its contents. We have to * read the contents at this time. */ while (zip->entry_bytes_remaining > 0) { const void *buff; unsigned char *mem; size_t size; int64_t offset; r = archive_read_format_7zip_read_data(a, &buff, &size, &offset); if (r < ARCHIVE_WARN) { free(symname); return (r); } mem = realloc(symname, symsize + size + 1); if (mem == NULL) { free(symname); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Symname"); return (ARCHIVE_FATAL); } symname = mem; memcpy(symname+symsize, buff, size); symsize += size; } if (symsize == 0) { /* If there is no symname, handle it as a regular * file. */ zip_entry->mode &= ~AE_IFMT; zip_entry->mode |= AE_IFREG; archive_entry_set_mode(entry, zip_entry->mode); } else { symname[symsize] = '\0'; archive_entry_copy_symlink(entry, (const char *)symname); } free(symname); archive_entry_set_size(entry, 0); } /* Set up a more descriptive format name. */ sprintf(zip->format_name, "7-Zip"); a->archive.archive_format_name = zip->format_name; return (ret); } static int archive_read_format_7zip_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct _7zip *zip; ssize_t bytes; int ret = ARCHIVE_OK; zip = (struct _7zip *)(a->format->data); if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } if (zip->pack_stream_bytes_unconsumed) read_consume(a); *offset = zip->entry_offset; *size = 0; *buff = NULL; /* * If we hit end-of-entry last time, clean up and return * ARCHIVE_EOF this time. */ if (zip->end_of_entry) return (ARCHIVE_EOF); bytes = read_stream(a, buff, (size_t)zip->entry_bytes_remaining, 0); if (bytes < 0) return ((int)bytes); if (bytes == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } zip->entry_bytes_remaining -= bytes; if (zip->entry_bytes_remaining == 0) zip->end_of_entry = 1; /* Update checksum */ if ((zip->entry->flg & CRC32_IS_SET) && bytes) zip->entry_crc32 = crc32(zip->entry_crc32, *buff, (unsigned)bytes); /* If we hit the end, swallow any end-of-data marker. */ if (zip->end_of_entry) { /* Check computed CRC against file contents. */ if ((zip->entry->flg & CRC32_IS_SET) && zip->si.ss.digests[zip->entry->ssIndex] != zip->entry_crc32) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "7-Zip bad CRC: 0x%lx should be 0x%lx", (unsigned long)zip->entry_crc32, (unsigned long)zip->si.ss.digests[ zip->entry->ssIndex]); ret = ARCHIVE_WARN; } } *size = bytes; *offset = zip->entry_offset; zip->entry_offset += bytes; return (ret); } static int archive_read_format_7zip_read_data_skip(struct archive_read *a) { struct _7zip *zip; int64_t bytes_skipped; zip = (struct _7zip *)(a->format->data); if (zip->pack_stream_bytes_unconsumed) read_consume(a); /* If we've already read to end of data, we're done. */ if (zip->end_of_entry) return (ARCHIVE_OK); /* * If the length is at the beginning, we can skip the * compressed data much more quickly. */ bytes_skipped = skip_stream(a, (size_t)zip->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); zip->entry_bytes_remaining = 0; /* This entry is finished and done. */ zip->end_of_entry = 1; return (ARCHIVE_OK); } static int archive_read_format_7zip_cleanup(struct archive_read *a) { struct _7zip *zip; zip = (struct _7zip *)(a->format->data); free_StreamsInfo(&(zip->si)); free(zip->entries); free(zip->entry_names); free_decompression(a, zip); free(zip->uncompressed_buffer); free(zip->sub_stream_buff[0]); free(zip->sub_stream_buff[1]); free(zip->sub_stream_buff[2]); free(zip->tmp_stream_buff); free(zip); (a->format->data) = NULL; return (ARCHIVE_OK); } static void read_consume(struct archive_read *a) { struct _7zip *zip = (struct _7zip *)a->format->data; if (zip->pack_stream_bytes_unconsumed) { __archive_read_consume(a, zip->pack_stream_bytes_unconsumed); zip->stream_offset += zip->pack_stream_bytes_unconsumed; zip->pack_stream_bytes_unconsumed = 0; } } #ifdef HAVE_LZMA_H /* * Set an error code and choose an error message for liblzma. */ static void set_error(struct archive_read *a, int ret) { switch (ret) { case LZMA_STREAM_END: /* Found end of stream. */ case LZMA_OK: /* Decompressor made some progress. */ break; case LZMA_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Lzma library error: Cannot allocate memory"); break; case LZMA_MEMLIMIT_ERROR: archive_set_error(&a->archive, ENOMEM, "Lzma library error: Out of memory"); break; case LZMA_FORMAT_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: format not recognized"); break; case LZMA_OPTIONS_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: Invalid options"); break; case LZMA_DATA_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: Corrupted input data"); break; case LZMA_BUF_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Lzma library error: No progress is possible"); break; default: /* Return an error. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Lzma decompression failed: Unknown error"); break; } } #endif static unsigned long decode_codec_id(const unsigned char *codecId, size_t id_size) { unsigned i; unsigned long id = 0; for (i = 0; i < id_size; i++) { id <<= 8; id += codecId[i]; } return (id); } static Byte ppmd_read(void *p) { struct archive_read *a = ((IByteIn*)p)->a; struct _7zip *zip = (struct _7zip *)(a->format->data); Byte b; if (zip->ppstream.avail_in == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); zip->ppstream.overconsumed = 1; return (0); } b = *zip->ppstream.next_in++; zip->ppstream.avail_in--; zip->ppstream.total_in++; return (b); } static int init_decompression(struct archive_read *a, struct _7zip *zip, const struct _7z_coder *coder1, const struct _7z_coder *coder2) { int r; zip->codec = coder1->codec; zip->codec2 = -1; switch (zip->codec) { case _7Z_COPY: case _7Z_BZ2: case _7Z_DEFLATE: case _7Z_PPMD: if (coder2 != NULL) { if (coder2->codec != _7Z_X86 && coder2->codec != _7Z_X86_BCJ2) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unsupported filter %lx for %lx", coder2->codec, coder1->codec); return (ARCHIVE_FAILED); } zip->codec2 = coder2->codec; zip->bcj_state = 0; if (coder2->codec == _7Z_X86) x86_Init(zip); } break; default: break; } switch (zip->codec) { case _7Z_COPY: break; case _7Z_LZMA: case _7Z_LZMA2: #ifdef HAVE_LZMA_H #if LZMA_VERSION_MAJOR >= 5 /* Effectively disable the limiter. */ #define LZMA_MEMLIMIT UINT64_MAX #else /* NOTE: This needs to check memory size which running system has. */ #define LZMA_MEMLIMIT (1U << 30) #endif { lzma_options_delta delta_opt; lzma_filter filters[LZMA_FILTERS_MAX], *ff; int fi = 0; if (zip->lzstream_valid) { lzma_end(&(zip->lzstream)); zip->lzstream_valid = 0; } /* * NOTE: liblzma incompletely handle the BCJ+LZMA compressed * data made by 7-Zip because 7-Zip does not add End-Of- * Payload Marker(EOPM) at the end of LZMA compressed data, * and so liblzma cannot know the end of the compressed data * without EOPM. So consequently liblzma will not return last * three or four bytes of uncompressed data because * LZMA_FILTER_X86 filter does not handle input data if its * data size is less than five bytes. If liblzma detect EOPM * or know the uncompressed data size, liblzma will flush out * the remaining that three or four bytes of uncompressed * data. That is why we have to use our converting program * for BCJ+LZMA. If we were able to tell the uncompressed * size to liblzma when using lzma_raw_decoder() liblzma * could correctly deal with BCJ+LZMA. But unfortunately * there is no way to do that. * Discussion about this can be found at XZ Utils forum. */ if (coder2 != NULL) { zip->codec2 = coder2->codec; filters[fi].options = NULL; switch (zip->codec2) { case _7Z_X86: if (zip->codec == _7Z_LZMA2) { filters[fi].id = LZMA_FILTER_X86; fi++; } else /* Use our filter. */ x86_Init(zip); break; case _7Z_X86_BCJ2: /* Use our filter. */ zip->bcj_state = 0; break; case _7Z_DELTA: filters[fi].id = LZMA_FILTER_DELTA; memset(&delta_opt, 0, sizeof(delta_opt)); delta_opt.type = LZMA_DELTA_TYPE_BYTE; delta_opt.dist = 1; filters[fi].options = &delta_opt; fi++; break; /* Following filters have not been tested yet. */ case _7Z_POWERPC: filters[fi].id = LZMA_FILTER_POWERPC; fi++; break; case _7Z_IA64: filters[fi].id = LZMA_FILTER_IA64; fi++; break; case _7Z_ARM: filters[fi].id = LZMA_FILTER_ARM; fi++; break; case _7Z_ARMTHUMB: filters[fi].id = LZMA_FILTER_ARMTHUMB; fi++; break; case _7Z_SPARC: filters[fi].id = LZMA_FILTER_SPARC; fi++; break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unexpected codec ID: %lX", zip->codec2); return (ARCHIVE_FAILED); } } if (zip->codec == _7Z_LZMA2) filters[fi].id = LZMA_FILTER_LZMA2; else filters[fi].id = LZMA_FILTER_LZMA1; filters[fi].options = NULL; ff = &filters[fi]; r = lzma_properties_decode(&filters[fi], NULL, coder1->properties, (size_t)coder1->propertiesSize); if (r != LZMA_OK) { set_error(a, r); return (ARCHIVE_FAILED); } fi++; filters[fi].id = LZMA_VLI_UNKNOWN; filters[fi].options = NULL; r = lzma_raw_decoder(&(zip->lzstream), filters); free(ff->options); if (r != LZMA_OK) { set_error(a, r); return (ARCHIVE_FAILED); } zip->lzstream_valid = 1; zip->lzstream.total_in = 0; zip->lzstream.total_out = 0; break; } #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LZMA codec is unsupported"); return (ARCHIVE_FAILED); #endif case _7Z_BZ2: #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) if (zip->bzstream_valid) { BZ2_bzDecompressEnd(&(zip->bzstream)); zip->bzstream_valid = 0; } r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 0); if (r == BZ_MEM_ERROR) r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 1); if (r != BZ_OK) { int err = ARCHIVE_ERRNO_MISC; const char *detail = NULL; switch (r) { case BZ_PARAM_ERROR: detail = "invalid setup parameter"; break; case BZ_MEM_ERROR: err = ENOMEM; detail = "out of memory"; break; case BZ_CONFIG_ERROR: detail = "mis-compiled library"; break; } archive_set_error(&a->archive, err, "Internal error initializing decompressor: %s", detail != NULL ? detail : "??"); zip->bzstream_valid = 0; return (ARCHIVE_FAILED); } zip->bzstream_valid = 1; zip->bzstream.total_in_lo32 = 0; zip->bzstream.total_in_hi32 = 0; zip->bzstream.total_out_lo32 = 0; zip->bzstream.total_out_hi32 = 0; break; #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "BZ2 codec is unsupported"); return (ARCHIVE_FAILED); #endif case _7Z_DEFLATE: #ifdef HAVE_ZLIB_H if (zip->stream_valid) r = inflateReset(&(zip->stream)); else r = inflateInit2(&(zip->stream), -15 /* Don't check for zlib header */); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Couldn't initialize zlib stream."); return (ARCHIVE_FAILED); } zip->stream_valid = 1; zip->stream.total_in = 0; zip->stream.total_out = 0; break; #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "DEFLATE codec is unsupported"); return (ARCHIVE_FAILED); #endif case _7Z_PPMD: { unsigned order; uint32_t msize; if (zip->ppmd7_valid) { __archive_ppmd7_functions.Ppmd7_Free( &zip->ppmd7_context); zip->ppmd7_valid = 0; } if (coder1->propertiesSize < 5) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed PPMd parameter"); return (ARCHIVE_FAILED); } order = coder1->properties[0]; msize = archive_le32dec(&(coder1->properties[1])); if (order < PPMD7_MIN_ORDER || order > PPMD7_MAX_ORDER || msize < PPMD7_MIN_MEM_SIZE || msize > PPMD7_MAX_MEM_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed PPMd parameter"); return (ARCHIVE_FAILED); } __archive_ppmd7_functions.Ppmd7_Construct(&zip->ppmd7_context); r = __archive_ppmd7_functions.Ppmd7_Alloc( &zip->ppmd7_context, msize); if (r == 0) { archive_set_error(&a->archive, ENOMEM, "Coludn't allocate memory for PPMd"); return (ARCHIVE_FATAL); } __archive_ppmd7_functions.Ppmd7_Init( &zip->ppmd7_context, order); __archive_ppmd7_functions.Ppmd7z_RangeDec_CreateVTable( &zip->range_dec); zip->ppmd7_valid = 1; zip->ppmd7_stat = 0; zip->ppstream.overconsumed = 0; zip->ppstream.total_in = 0; zip->ppstream.total_out = 0; break; } case _7Z_X86: case _7Z_X86_BCJ2: case _7Z_POWERPC: case _7Z_IA64: case _7Z_ARM: case _7Z_ARMTHUMB: case _7Z_SPARC: case _7Z_DELTA: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unexpected codec ID: %lX", zip->codec); return (ARCHIVE_FAILED); case _7Z_CRYPTO_MAIN_ZIP: case _7Z_CRYPTO_RAR_29: case _7Z_CRYPTO_AES_256_SHA_256: if (a->entry) { archive_entry_set_is_metadata_encrypted(a->entry, 1); archive_entry_set_is_data_encrypted(a->entry, 1); zip->has_encrypted_entries = 1; } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Crypto codec not supported yet (ID: 0x%lX)", zip->codec); return (ARCHIVE_FAILED); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Unknown codec ID: %lX", zip->codec); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } static int decompress(struct archive_read *a, struct _7zip *zip, void *buff, size_t *outbytes, const void *b, size_t *used) { const uint8_t *t_next_in; uint8_t *t_next_out; size_t o_avail_in, o_avail_out; size_t t_avail_in, t_avail_out; uint8_t *bcj2_next_out; size_t bcj2_avail_out; int r, ret = ARCHIVE_OK; t_avail_in = o_avail_in = *used; t_avail_out = o_avail_out = *outbytes; t_next_in = b; t_next_out = buff; if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) { int i; /* Do not copy out the BCJ remaining bytes when the output * buffer size is less than five bytes. */ if (o_avail_in != 0 && t_avail_out < 5 && zip->odd_bcj_size) { *used = 0; *outbytes = 0; return (ret); } for (i = 0; zip->odd_bcj_size > 0 && t_avail_out; i++) { *t_next_out++ = zip->odd_bcj[i]; t_avail_out--; zip->odd_bcj_size--; } if (o_avail_in == 0 || t_avail_out == 0) { *used = o_avail_in - t_avail_in; *outbytes = o_avail_out - t_avail_out; if (o_avail_in == 0) ret = ARCHIVE_EOF; return (ret); } } bcj2_next_out = t_next_out; bcj2_avail_out = t_avail_out; if (zip->codec2 == _7Z_X86_BCJ2) { /* * Decord a remaining decompressed main stream for BCJ2. */ if (zip->tmp_stream_bytes_remaining) { ssize_t bytes; size_t remaining = zip->tmp_stream_bytes_remaining; bytes = Bcj2_Decode(zip, t_next_out, t_avail_out); if (bytes < 0) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "BCJ2 conversion Failed"); return (ARCHIVE_FAILED); } zip->main_stream_bytes_remaining -= remaining - zip->tmp_stream_bytes_remaining; t_avail_out -= bytes; if (o_avail_in == 0 || t_avail_out == 0) { *used = 0; *outbytes = o_avail_out - t_avail_out; if (o_avail_in == 0 && zip->tmp_stream_bytes_remaining) ret = ARCHIVE_EOF; return (ret); } t_next_out += bytes; bcj2_next_out = t_next_out; bcj2_avail_out = t_avail_out; } t_next_out = zip->tmp_stream_buff; t_avail_out = zip->tmp_stream_buff_size; } switch (zip->codec) { case _7Z_COPY: { size_t bytes = (t_avail_in > t_avail_out)?t_avail_out:t_avail_in; memcpy(t_next_out, t_next_in, bytes); t_avail_in -= bytes; t_avail_out -= bytes; if (o_avail_in == 0) ret = ARCHIVE_EOF; break; } #ifdef HAVE_LZMA_H case _7Z_LZMA: case _7Z_LZMA2: zip->lzstream.next_in = t_next_in; zip->lzstream.avail_in = t_avail_in; zip->lzstream.next_out = t_next_out; zip->lzstream.avail_out = t_avail_out; r = lzma_code(&(zip->lzstream), LZMA_RUN); switch (r) { case LZMA_STREAM_END: /* Found end of stream. */ lzma_end(&(zip->lzstream)); zip->lzstream_valid = 0; ret = ARCHIVE_EOF; break; case LZMA_OK: /* Decompressor made some progress. */ break; default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Decompression failed(%d)", r); return (ARCHIVE_FAILED); } t_avail_in = zip->lzstream.avail_in; t_avail_out = zip->lzstream.avail_out; break; #endif #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) case _7Z_BZ2: zip->bzstream.next_in = (char *)(uintptr_t)t_next_in; zip->bzstream.avail_in = t_avail_in; zip->bzstream.next_out = (char *)(uintptr_t)t_next_out; zip->bzstream.avail_out = t_avail_out; r = BZ2_bzDecompress(&(zip->bzstream)); switch (r) { case BZ_STREAM_END: /* Found end of stream. */ switch (BZ2_bzDecompressEnd(&(zip->bzstream))) { case BZ_OK: break; default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Failed to clean up decompressor"); return (ARCHIVE_FAILED); } zip->bzstream_valid = 0; ret = ARCHIVE_EOF; break; case BZ_OK: /* Decompressor made some progress. */ break; default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "bzip decompression failed"); return (ARCHIVE_FAILED); } t_avail_in = zip->bzstream.avail_in; t_avail_out = zip->bzstream.avail_out; break; #endif #ifdef HAVE_ZLIB_H case _7Z_DEFLATE: zip->stream.next_in = (Bytef *)(uintptr_t)t_next_in; zip->stream.avail_in = (uInt)t_avail_in; zip->stream.next_out = t_next_out; zip->stream.avail_out = (uInt)t_avail_out; r = inflate(&(zip->stream), 0); switch (r) { case Z_STREAM_END: /* Found end of stream. */ ret = ARCHIVE_EOF; break; case Z_OK: /* Decompressor made some progress.*/ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "File decompression failed (%d)", r); return (ARCHIVE_FAILED); } t_avail_in = zip->stream.avail_in; t_avail_out = zip->stream.avail_out; break; #endif case _7Z_PPMD: { uint64_t flush_bytes; if (!zip->ppmd7_valid || zip->ppmd7_stat < 0 || t_avail_out <= 0) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Decompression internal error"); return (ARCHIVE_FAILED); } zip->ppstream.next_in = t_next_in; zip->ppstream.avail_in = t_avail_in; zip->ppstream.next_out = t_next_out; zip->ppstream.avail_out = t_avail_out; if (zip->ppmd7_stat == 0) { zip->bytein.a = a; zip->bytein.Read = &ppmd_read; zip->range_dec.Stream = &zip->bytein; r = __archive_ppmd7_functions.Ppmd7z_RangeDec_Init( &(zip->range_dec)); if (r == 0) { zip->ppmd7_stat = -1; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to initialize PPMd range decorder"); return (ARCHIVE_FAILED); } if (zip->ppstream.overconsumed) { zip->ppmd7_stat = -1; return (ARCHIVE_FAILED); } zip->ppmd7_stat = 1; } if (t_avail_in == 0) /* XXX Flush out remaining decoded data XXX */ flush_bytes = zip->folder_outbytes_remaining; else flush_bytes = 0; do { int sym; sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol( &(zip->ppmd7_context), &(zip->range_dec.p)); if (sym < 0) { zip->ppmd7_stat = -1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to decode PPMd"); return (ARCHIVE_FAILED); } if (zip->ppstream.overconsumed) { zip->ppmd7_stat = -1; return (ARCHIVE_FAILED); } *zip->ppstream.next_out++ = (unsigned char)sym; zip->ppstream.avail_out--; zip->ppstream.total_out++; if (flush_bytes) flush_bytes--; } while (zip->ppstream.avail_out && (zip->ppstream.avail_in || flush_bytes)); t_avail_in = (size_t)zip->ppstream.avail_in; t_avail_out = (size_t)zip->ppstream.avail_out; break; } default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Decompression internal error"); return (ARCHIVE_FAILED); } if (ret != ARCHIVE_OK && ret != ARCHIVE_EOF) return (ret); *used = o_avail_in - t_avail_in; *outbytes = o_avail_out - t_avail_out; /* * Decord BCJ. */ if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) { size_t l = x86_Convert(zip, buff, *outbytes); zip->odd_bcj_size = *outbytes - l; if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 && o_avail_in && ret != ARCHIVE_EOF) { memcpy(zip->odd_bcj, ((unsigned char *)buff) + l, zip->odd_bcj_size); *outbytes = l; } else zip->odd_bcj_size = 0; } /* * Decord BCJ2 with a decompressed main stream. */ if (zip->codec2 == _7Z_X86_BCJ2) { ssize_t bytes; zip->tmp_stream_bytes_avail = zip->tmp_stream_buff_size - t_avail_out; if (zip->tmp_stream_bytes_avail > zip->main_stream_bytes_remaining) zip->tmp_stream_bytes_avail = zip->main_stream_bytes_remaining; zip->tmp_stream_bytes_remaining = zip->tmp_stream_bytes_avail; bytes = Bcj2_Decode(zip, bcj2_next_out, bcj2_avail_out); if (bytes < 0) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "BCJ2 conversion Failed"); return (ARCHIVE_FAILED); } zip->main_stream_bytes_remaining -= zip->tmp_stream_bytes_avail - zip->tmp_stream_bytes_remaining; bcj2_avail_out -= bytes; *outbytes = o_avail_out - bcj2_avail_out; } return (ret); } static int free_decompression(struct archive_read *a, struct _7zip *zip) { int r = ARCHIVE_OK; #if !defined(HAVE_ZLIB_H) &&\ !(defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)) (void)a;/* UNUSED */ #endif #ifdef HAVE_LZMA_H if (zip->lzstream_valid) lzma_end(&(zip->lzstream)); #endif #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) if (zip->bzstream_valid) { if (BZ2_bzDecompressEnd(&(zip->bzstream)) != BZ_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up bzip2 decompressor"); r = ARCHIVE_FATAL; } zip->bzstream_valid = 0; } #endif #ifdef HAVE_ZLIB_H if (zip->stream_valid) { if (inflateEnd(&(zip->stream)) != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up zlib decompressor"); r = ARCHIVE_FATAL; } zip->stream_valid = 0; } #endif if (zip->ppmd7_valid) { __archive_ppmd7_functions.Ppmd7_Free( &zip->ppmd7_context); zip->ppmd7_valid = 0; } return (r); } static int parse_7zip_uint64(struct archive_read *a, uint64_t *val) { const unsigned char *p; unsigned char avail, mask; int i; if ((p = header_bytes(a, 1)) == NULL) return (-1); avail = *p; mask = 0x80; *val = 0; for (i = 0; i < 8; i++) { if (avail & mask) { if ((p = header_bytes(a, 1)) == NULL) return (-1); *val |= ((uint64_t)*p) << (8 * i); mask >>= 1; continue; } *val += ((uint64_t)(avail & (mask -1))) << (8 * i); break; } return (0); } static int read_Bools(struct archive_read *a, unsigned char *data, size_t num) { const unsigned char *p; unsigned i, mask = 0, avail = 0; for (i = 0; i < num; i++) { if (mask == 0) { if ((p = header_bytes(a, 1)) == NULL) return (-1); avail = *p; mask = 0x80; } data[i] = (avail & mask)?1:0; mask >>= 1; } return (0); } static void free_Digest(struct _7z_digests *d) { free(d->defineds); free(d->digests); } static int read_Digests(struct archive_read *a, struct _7z_digests *d, size_t num) { const unsigned char *p; unsigned i; if (num == 0) return (-1); memset(d, 0, sizeof(*d)); d->defineds = malloc(num); if (d->defineds == NULL) return (-1); /* * Read Bools. */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == 0) { if (read_Bools(a, d->defineds, num) < 0) return (-1); } else /* All are defined */ memset(d->defineds, 1, num); d->digests = calloc(num, sizeof(*d->digests)); if (d->digests == NULL) return (-1); for (i = 0; i < num; i++) { if (d->defineds[i]) { if ((p = header_bytes(a, 4)) == NULL) return (-1); d->digests[i] = archive_le32dec(p); } } return (0); } static void free_PackInfo(struct _7z_pack_info *pi) { free(pi->sizes); free(pi->positions); free_Digest(&(pi->digest)); } static int read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi) { const unsigned char *p; unsigned i; memset(pi, 0, sizeof(*pi)); /* * Read PackPos. */ if (parse_7zip_uint64(a, &(pi->pos)) < 0) return (-1); /* * Read NumPackStreams. */ if (parse_7zip_uint64(a, &(pi->numPackStreams)) < 0) return (-1); if (pi->numPackStreams == 0) return (-1); if (UMAX_ENTRY < pi->numPackStreams) return (-1); /* * Read PackSizes[num] */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == kEnd) /* PackSizes[num] are not present. */ return (0); if (*p != kSize) return (-1); pi->sizes = calloc((size_t)pi->numPackStreams, sizeof(uint64_t)); pi->positions = calloc((size_t)pi->numPackStreams, sizeof(uint64_t)); if (pi->sizes == NULL || pi->positions == NULL) return (-1); for (i = 0; i < pi->numPackStreams; i++) { if (parse_7zip_uint64(a, &(pi->sizes[i])) < 0) return (-1); } /* * Read PackStreamDigests[num] */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == kEnd) { /* PackStreamDigests[num] are not present. */ pi->digest.defineds = calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.defineds)); pi->digest.digests = calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.digests)); if (pi->digest.defineds == NULL || pi->digest.digests == NULL) return (-1); return (0); } if (*p != kSize) return (-1); if (read_Digests(a, &(pi->digest), (size_t)pi->numPackStreams) < 0) return (-1); /* * Must be marked by kEnd. */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p != kEnd) return (-1); return (0); } static void free_Folder(struct _7z_folder *f) { unsigned i; if (f->coders) { for (i = 0; i< f->numCoders; i++) { free(f->coders[i].properties); } free(f->coders); } free(f->bindPairs); free(f->packedStreams); free(f->unPackSize); } static int read_Folder(struct archive_read *a, struct _7z_folder *f) { struct _7zip *zip = (struct _7zip *)a->format->data; const unsigned char *p; uint64_t numInStreamsTotal = 0; uint64_t numOutStreamsTotal = 0; unsigned i; memset(f, 0, sizeof(*f)); /* * Read NumCoders. */ if (parse_7zip_uint64(a, &(f->numCoders)) < 0) return (-1); if (f->numCoders > 4) /* Too many coders. */ return (-1); f->coders = calloc((size_t)f->numCoders, sizeof(*f->coders)); if (f->coders == NULL) return (-1); for (i = 0; i< f->numCoders; i++) { size_t codec_size; int simple, attr; if ((p = header_bytes(a, 1)) == NULL) return (-1); /* * 0:3 CodecIdSize * 4: 0 - IsSimple * 1 - Is not Simple * 5: 0 - No Attributes * 1 - There are Attributes; * 7: Must be zero. */ codec_size = *p & 0xf; simple = (*p & 0x10)?0:1; attr = *p & 0x20; if (*p & 0x80) return (-1);/* Not supported. */ /* * Read Decompression Method IDs. */ if ((p = header_bytes(a, codec_size)) == NULL) return (-1); f->coders[i].codec = decode_codec_id(p, codec_size); if (simple) { f->coders[i].numInStreams = 1; f->coders[i].numOutStreams = 1; } else { if (parse_7zip_uint64( a, &(f->coders[i].numInStreams)) < 0) return (-1); if (UMAX_ENTRY < f->coders[i].numInStreams) return (-1); if (parse_7zip_uint64( a, &(f->coders[i].numOutStreams)) < 0) return (-1); if (UMAX_ENTRY < f->coders[i].numOutStreams) return (-1); } if (attr) { if (parse_7zip_uint64( a, &(f->coders[i].propertiesSize)) < 0) return (-1); if ((p = header_bytes( a, (size_t)f->coders[i].propertiesSize)) == NULL) return (-1); f->coders[i].properties = malloc((size_t)f->coders[i].propertiesSize); if (f->coders[i].properties == NULL) return (-1); memcpy(f->coders[i].properties, p, (size_t)f->coders[i].propertiesSize); } numInStreamsTotal += f->coders[i].numInStreams; numOutStreamsTotal += f->coders[i].numOutStreams; } if (numOutStreamsTotal == 0 || numInStreamsTotal < numOutStreamsTotal-1) return (-1); f->numBindPairs = numOutStreamsTotal - 1; if (zip->header_bytes_remaining < f->numBindPairs) return (-1); if (f->numBindPairs > 0) { f->bindPairs = calloc((size_t)f->numBindPairs, sizeof(*f->bindPairs)); if (f->bindPairs == NULL) return (-1); } else f->bindPairs = NULL; for (i = 0; i < f->numBindPairs; i++) { if (parse_7zip_uint64(a, &(f->bindPairs[i].inIndex)) < 0) return (-1); if (UMAX_ENTRY < f->bindPairs[i].inIndex) return (-1); if (parse_7zip_uint64(a, &(f->bindPairs[i].outIndex)) < 0) return (-1); if (UMAX_ENTRY < f->bindPairs[i].outIndex) return (-1); } f->numPackedStreams = numInStreamsTotal - f->numBindPairs; f->packedStreams = calloc((size_t)f->numPackedStreams, sizeof(*f->packedStreams)); if (f->packedStreams == NULL) return (-1); if (f->numPackedStreams == 1) { for (i = 0; i < numInStreamsTotal; i++) { unsigned j; for (j = 0; j < f->numBindPairs; j++) { if (f->bindPairs[j].inIndex == i) break; } if (j == f->numBindPairs) break; } if (i == numInStreamsTotal) return (-1); f->packedStreams[0] = i; } else { for (i = 0; i < f->numPackedStreams; i++) { if (parse_7zip_uint64(a, &(f->packedStreams[i])) < 0) return (-1); if (UMAX_ENTRY < f->packedStreams[i]) return (-1); } } f->numInStreams = numInStreamsTotal; f->numOutStreams = numOutStreamsTotal; return (0); } static void free_CodersInfo(struct _7z_coders_info *ci) { unsigned i; if (ci->folders) { for (i = 0; i < ci->numFolders; i++) free_Folder(&(ci->folders[i])); free(ci->folders); } } static int read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci) { const unsigned char *p; struct _7z_digests digest; unsigned i; memset(ci, 0, sizeof(*ci)); memset(&digest, 0, sizeof(digest)); if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p != kFolder) goto failed; /* * Read NumFolders. */ if (parse_7zip_uint64(a, &(ci->numFolders)) < 0) goto failed; if (UMAX_ENTRY < ci->numFolders) return (-1); /* * Read External. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; switch (*p) { case 0: ci->folders = calloc((size_t)ci->numFolders, sizeof(*ci->folders)); if (ci->folders == NULL) return (-1); for (i = 0; i < ci->numFolders; i++) { if (read_Folder(a, &(ci->folders[i])) < 0) goto failed; } break; case 1: if (parse_7zip_uint64(a, &(ci->dataStreamIndex)) < 0) return (-1); if (UMAX_ENTRY < ci->dataStreamIndex) return (-1); if (ci->numFolders > 0) { archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); goto failed; } break; default: archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); goto failed; } if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p != kCodersUnPackSize) goto failed; for (i = 0; i < ci->numFolders; i++) { struct _7z_folder *folder = &(ci->folders[i]); unsigned j; folder->unPackSize = calloc((size_t)folder->numOutStreams, sizeof(*folder->unPackSize)); if (folder->unPackSize == NULL) goto failed; for (j = 0; j < folder->numOutStreams; j++) { if (parse_7zip_uint64(a, &(folder->unPackSize[j])) < 0) goto failed; } } /* * Read CRCs. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p == kEnd) return (0); if (*p != kCRC) goto failed; if (read_Digests(a, &digest, (size_t)ci->numFolders) < 0) goto failed; for (i = 0; i < ci->numFolders; i++) { ci->folders[i].digest_defined = digest.defineds[i]; ci->folders[i].digest = digest.digests[i]; } /* * Must be kEnd. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p != kEnd) goto failed; free_Digest(&digest); return (0); failed: free_Digest(&digest); return (-1); } static uint64_t folder_uncompressed_size(struct _7z_folder *f) { int n = (int)f->numOutStreams; unsigned pairs = (unsigned)f->numBindPairs; while (--n >= 0) { unsigned i; for (i = 0; i < pairs; i++) { if (f->bindPairs[i].outIndex == (uint64_t)n) break; } if (i >= pairs) return (f->unPackSize[n]); } return (0); } static void free_SubStreamsInfo(struct _7z_substream_info *ss) { free(ss->unpackSizes); free(ss->digestsDefined); free(ss->digests); } static int read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss, struct _7z_folder *f, size_t numFolders) { const unsigned char *p; uint64_t *usizes; size_t unpack_streams; int type; unsigned i; uint32_t numDigests; memset(ss, 0, sizeof(*ss)); for (i = 0; i < numFolders; i++) f[i].numUnpackStreams = 1; if ((p = header_bytes(a, 1)) == NULL) return (-1); type = *p; if (type == kNumUnPackStream) { unpack_streams = 0; for (i = 0; i < numFolders; i++) { if (parse_7zip_uint64(a, &(f[i].numUnpackStreams)) < 0) return (-1); if (UMAX_ENTRY < f[i].numUnpackStreams) return (-1); if (unpack_streams > SIZE_MAX - UMAX_ENTRY) { return (-1); } unpack_streams += (size_t)f[i].numUnpackStreams; } if ((p = header_bytes(a, 1)) == NULL) return (-1); type = *p; } else unpack_streams = numFolders; ss->unpack_streams = unpack_streams; if (unpack_streams) { ss->unpackSizes = calloc(unpack_streams, sizeof(*ss->unpackSizes)); ss->digestsDefined = calloc(unpack_streams, sizeof(*ss->digestsDefined)); ss->digests = calloc(unpack_streams, sizeof(*ss->digests)); if (ss->unpackSizes == NULL || ss->digestsDefined == NULL || ss->digests == NULL) return (-1); } usizes = ss->unpackSizes; for (i = 0; i < numFolders; i++) { unsigned pack; uint64_t sum; if (f[i].numUnpackStreams == 0) continue; sum = 0; if (type == kSize) { for (pack = 1; pack < f[i].numUnpackStreams; pack++) { if (parse_7zip_uint64(a, usizes) < 0) return (-1); sum += *usizes++; } } *usizes++ = folder_uncompressed_size(&f[i]) - sum; } if (type == kSize) { if ((p = header_bytes(a, 1)) == NULL) return (-1); type = *p; } for (i = 0; i < unpack_streams; i++) { ss->digestsDefined[i] = 0; ss->digests[i] = 0; } numDigests = 0; for (i = 0; i < numFolders; i++) { if (f[i].numUnpackStreams != 1 || !f[i].digest_defined) numDigests += (uint32_t)f[i].numUnpackStreams; } if (type == kCRC) { struct _7z_digests tmpDigests; unsigned char *digestsDefined = ss->digestsDefined; uint32_t * digests = ss->digests; int di = 0; memset(&tmpDigests, 0, sizeof(tmpDigests)); if (read_Digests(a, &(tmpDigests), numDigests) < 0) { free_Digest(&tmpDigests); return (-1); } for (i = 0; i < numFolders; i++) { if (f[i].numUnpackStreams == 1 && f[i].digest_defined) { *digestsDefined++ = 1; *digests++ = f[i].digest; } else { unsigned j; for (j = 0; j < f[i].numUnpackStreams; j++, di++) { *digestsDefined++ = tmpDigests.defineds[di]; *digests++ = tmpDigests.digests[di]; } } } free_Digest(&tmpDigests); if ((p = header_bytes(a, 1)) == NULL) return (-1); type = *p; } /* * Must be kEnd. */ if (type != kEnd) return (-1); return (0); } static void free_StreamsInfo(struct _7z_stream_info *si) { free_PackInfo(&(si->pi)); free_CodersInfo(&(si->ci)); free_SubStreamsInfo(&(si->ss)); } static int read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si) { struct _7zip *zip = (struct _7zip *)a->format->data; const unsigned char *p; unsigned i; memset(si, 0, sizeof(*si)); if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == kPackInfo) { uint64_t packPos; if (read_PackInfo(a, &(si->pi)) < 0) return (-1); if (si->pi.positions == NULL || si->pi.sizes == NULL) return (-1); /* * Calculate packed stream positions. */ packPos = si->pi.pos; for (i = 0; i < si->pi.numPackStreams; i++) { si->pi.positions[i] = packPos; packPos += si->pi.sizes[i]; if (packPos > zip->header_offset) return (-1); } if ((p = header_bytes(a, 1)) == NULL) return (-1); } if (*p == kUnPackInfo) { uint32_t packIndex; struct _7z_folder *f; if (read_CodersInfo(a, &(si->ci)) < 0) return (-1); /* * Calculate packed stream indexes. */ packIndex = 0; f = si->ci.folders; for (i = 0; i < si->ci.numFolders; i++) { f[i].packIndex = packIndex; packIndex += (uint32_t)f[i].numPackedStreams; if (packIndex > si->pi.numPackStreams) return (-1); } if ((p = header_bytes(a, 1)) == NULL) return (-1); } if (*p == kSubStreamsInfo) { if (read_SubStreamsInfo(a, &(si->ss), si->ci.folders, (size_t)si->ci.numFolders) < 0) return (-1); if ((p = header_bytes(a, 1)) == NULL) return (-1); } /* * Must be kEnd. */ if (*p != kEnd) return (-1); return (0); } static void free_Header(struct _7z_header_info *h) { free(h->emptyStreamBools); free(h->emptyFileBools); free(h->antiBools); free(h->attrBools); } static int read_Header(struct archive_read *a, struct _7z_header_info *h, int check_header_id) { struct _7zip *zip = (struct _7zip *)a->format->data; const unsigned char *p; struct _7z_folder *folders; struct _7z_stream_info *si = &(zip->si); struct _7zip_entry *entries; uint32_t folderIndex, indexInFolder; unsigned i; int eindex, empty_streams, sindex; if (check_header_id) { /* * Read Header. */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p != kHeader) return (-1); } /* * Read ArchiveProperties. */ if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == kArchiveProperties) { for (;;) { uint64_t size; if ((p = header_bytes(a, 1)) == NULL) return (-1); if (*p == 0) break; if (parse_7zip_uint64(a, &size) < 0) return (-1); } if ((p = header_bytes(a, 1)) == NULL) return (-1); } /* * Read MainStreamsInfo. */ if (*p == kMainStreamsInfo) { if (read_StreamsInfo(a, &(zip->si)) < 0) return (-1); if ((p = header_bytes(a, 1)) == NULL) return (-1); } if (*p == kEnd) return (0); /* * Read FilesInfo. */ if (*p != kFilesInfo) return (-1); if (parse_7zip_uint64(a, &(zip->numFiles)) < 0) return (-1); if (UMAX_ENTRY < zip->numFiles) return (-1); zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries)); if (zip->entries == NULL) return (-1); entries = zip->entries; empty_streams = 0; for (;;) { int type; uint64_t size; size_t ll; if ((p = header_bytes(a, 1)) == NULL) return (-1); type = *p; if (type == kEnd) break; if (parse_7zip_uint64(a, &size) < 0) return (-1); if (zip->header_bytes_remaining < size) return (-1); ll = (size_t)size; switch (type) { case kEmptyStream: if (h->emptyStreamBools != NULL) return (-1); h->emptyStreamBools = calloc((size_t)zip->numFiles, sizeof(*h->emptyStreamBools)); if (h->emptyStreamBools == NULL) return (-1); if (read_Bools( a, h->emptyStreamBools, (size_t)zip->numFiles) < 0) return (-1); empty_streams = 0; for (i = 0; i < zip->numFiles; i++) { if (h->emptyStreamBools[i]) empty_streams++; } break; case kEmptyFile: if (empty_streams <= 0) { /* Unexcepted sequence. Skip this. */ if (header_bytes(a, ll) == NULL) return (-1); break; } if (h->emptyFileBools != NULL) return (-1); h->emptyFileBools = calloc(empty_streams, sizeof(*h->emptyFileBools)); if (h->emptyFileBools == NULL) return (-1); if (read_Bools(a, h->emptyFileBools, empty_streams) < 0) return (-1); break; case kAnti: if (empty_streams <= 0) { /* Unexcepted sequence. Skip this. */ if (header_bytes(a, ll) == NULL) return (-1); break; } if (h->antiBools != NULL) return (-1); h->antiBools = calloc(empty_streams, sizeof(*h->antiBools)); if (h->antiBools == NULL) return (-1); if (read_Bools(a, h->antiBools, empty_streams) < 0) return (-1); break; case kCTime: case kATime: case kMTime: if (read_Times(a, h, type) < 0) return (-1); break; case kName: { unsigned char *np; size_t nl, nb; /* Skip one byte. */ if ((p = header_bytes(a, 1)) == NULL) return (-1); ll--; if ((ll & 1) || ll < zip->numFiles * 4) return (-1); if (zip->entry_names != NULL) return (-1); zip->entry_names = malloc(ll); if (zip->entry_names == NULL) return (-1); np = zip->entry_names; nb = ll; /* * Copy whole file names. * NOTE: This loop prevents from expanding * the uncompressed buffer in order not to * use extra memory resource. */ while (nb) { size_t b; if (nb > UBUFF_SIZE) b = UBUFF_SIZE; else b = nb; if ((p = header_bytes(a, b)) == NULL) return (-1); memcpy(np, p, b); np += b; nb -= b; } np = zip->entry_names; nl = ll; for (i = 0; i < zip->numFiles; i++) { entries[i].utf16name = np; #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) entries[i].wname = (wchar_t *)np; #endif /* Find a terminator. */ while (nl >= 2 && (np[0] || np[1])) { np += 2; nl -= 2; } if (nl < 2) return (-1);/* Terminator not found */ entries[i].name_len = np - entries[i].utf16name; np += 2; nl -= 2; } break; } case kAttributes: { int allAreDefined; if ((p = header_bytes(a, 2)) == NULL) return (-1); allAreDefined = *p; if (h->attrBools != NULL) return (-1); h->attrBools = calloc((size_t)zip->numFiles, sizeof(*h->attrBools)); if (h->attrBools == NULL) return (-1); if (allAreDefined) memset(h->attrBools, 1, (size_t)zip->numFiles); else { if (read_Bools(a, h->attrBools, (size_t)zip->numFiles) < 0) return (-1); } for (i = 0; i < zip->numFiles; i++) { if (h->attrBools[i]) { if ((p = header_bytes(a, 4)) == NULL) return (-1); entries[i].attr = archive_le32dec(p); } } break; } case kDummy: if (ll == 0) break; __LA_FALLTHROUGH; default: if (header_bytes(a, ll) == NULL) return (-1); break; } } /* * Set up entry's attributes. */ folders = si->ci.folders; eindex = sindex = 0; folderIndex = indexInFolder = 0; for (i = 0; i < zip->numFiles; i++) { if (h->emptyStreamBools == NULL || h->emptyStreamBools[i] == 0) entries[i].flg |= HAS_STREAM; /* The high 16 bits of attributes is a posix file mode. */ entries[i].mode = entries[i].attr >> 16; if (entries[i].flg & HAS_STREAM) { if ((size_t)sindex >= si->ss.unpack_streams) return (-1); if (entries[i].mode == 0) entries[i].mode = AE_IFREG | 0666; if (si->ss.digestsDefined[sindex]) entries[i].flg |= CRC32_IS_SET; entries[i].ssIndex = sindex; sindex++; } else { int dir; if (h->emptyFileBools == NULL) dir = 1; else { if (h->emptyFileBools[eindex]) dir = 0; else dir = 1; eindex++; } if (entries[i].mode == 0) { if (dir) entries[i].mode = AE_IFDIR | 0777; else entries[i].mode = AE_IFREG | 0666; } else if (dir && (entries[i].mode & AE_IFMT) != AE_IFDIR) { entries[i].mode &= ~AE_IFMT; entries[i].mode |= AE_IFDIR; } if ((entries[i].mode & AE_IFMT) == AE_IFDIR && entries[i].name_len >= 2 && (entries[i].utf16name[entries[i].name_len-2] != '/' || entries[i].utf16name[entries[i].name_len-1] != 0)) { entries[i].utf16name[entries[i].name_len] = '/'; entries[i].utf16name[entries[i].name_len+1] = 0; entries[i].name_len += 2; } entries[i].ssIndex = -1; } if (entries[i].attr & 0x01) entries[i].mode &= ~0222;/* Read only. */ if ((entries[i].flg & HAS_STREAM) == 0 && indexInFolder == 0) { /* * The entry is an empty file or a directory file, * those both have no contents. */ entries[i].folderIndex = -1; continue; } if (indexInFolder == 0) { for (;;) { if (folderIndex >= si->ci.numFolders) return (-1); if (folders[folderIndex].numUnpackStreams) break; folderIndex++; } } entries[i].folderIndex = folderIndex; if ((entries[i].flg & HAS_STREAM) == 0) continue; indexInFolder++; if (indexInFolder >= folders[folderIndex].numUnpackStreams) { folderIndex++; indexInFolder = 0; } } return (0); } #define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000) static void fileTimeToUtc(uint64_t fileTime, time_t *timep, long *ns) { if (fileTime >= EPOC_TIME) { fileTime -= EPOC_TIME; /* milli seconds base */ *timep = (time_t)(fileTime / 10000000); /* nano seconds base */ *ns = (long)(fileTime % 10000000) * 100; } else { *timep = 0; *ns = 0; } } static int read_Times(struct archive_read *a, struct _7z_header_info *h, int type) { struct _7zip *zip = (struct _7zip *)a->format->data; const unsigned char *p; struct _7zip_entry *entries = zip->entries; unsigned char *timeBools; int allAreDefined; unsigned i; timeBools = calloc((size_t)zip->numFiles, sizeof(*timeBools)); if (timeBools == NULL) return (-1); /* Read allAreDefined. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; allAreDefined = *p; if (allAreDefined) memset(timeBools, 1, (size_t)zip->numFiles); else { if (read_Bools(a, timeBools, (size_t)zip->numFiles) < 0) goto failed; } /* Read external. */ if ((p = header_bytes(a, 1)) == NULL) goto failed; if (*p) { if (parse_7zip_uint64(a, &(h->dataIndex)) < 0) goto failed; if (UMAX_ENTRY < h->dataIndex) goto failed; } for (i = 0; i < zip->numFiles; i++) { if (!timeBools[i]) continue; if ((p = header_bytes(a, 8)) == NULL) goto failed; switch (type) { case kCTime: fileTimeToUtc(archive_le64dec(p), &(entries[i].ctime), &(entries[i].ctime_ns)); entries[i].flg |= CTIME_IS_SET; break; case kATime: fileTimeToUtc(archive_le64dec(p), &(entries[i].atime), &(entries[i].atime_ns)); entries[i].flg |= ATIME_IS_SET; break; case kMTime: fileTimeToUtc(archive_le64dec(p), &(entries[i].mtime), &(entries[i].mtime_ns)); entries[i].flg |= MTIME_IS_SET; break; } } free(timeBools); return (0); failed: free(timeBools); return (-1); } static int decode_encoded_header_info(struct archive_read *a, struct _7z_stream_info *si) { struct _7zip *zip = (struct _7zip *)a->format->data; errno = 0; if (read_StreamsInfo(a, si) < 0) { if (errno == ENOMEM) archive_set_error(&a->archive, -1, "Couldn't allocate memory"); else archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } if (si->pi.numPackStreams == 0 || si->ci.numFolders == 0) { archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } if (zip->header_offset < si->pi.pos + si->pi.sizes[0] || (int64_t)(si->pi.pos + si->pi.sizes[0]) < 0 || si->pi.sizes[0] == 0 || (int64_t)si->pi.pos < 0) { archive_set_error(&a->archive, -1, "Malformed Header offset"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } static const unsigned char * header_bytes(struct archive_read *a, size_t rbytes) { struct _7zip *zip = (struct _7zip *)a->format->data; const unsigned char *p; if (zip->header_bytes_remaining < rbytes) return (NULL); if (zip->pack_stream_bytes_unconsumed) read_consume(a); if (zip->header_is_encoded == 0) { p = __archive_read_ahead(a, rbytes, NULL); if (p == NULL) return (NULL); zip->header_bytes_remaining -= rbytes; zip->pack_stream_bytes_unconsumed = rbytes; } else { const void *buff; ssize_t bytes; bytes = read_stream(a, &buff, rbytes, rbytes); if (bytes <= 0) return (NULL); zip->header_bytes_remaining -= bytes; p = buff; } /* Update checksum */ zip->header_crc32 = crc32(zip->header_crc32, p, (unsigned)rbytes); return (p); } static int slurp_central_directory(struct archive_read *a, struct _7zip *zip, struct _7z_header_info *header) { const unsigned char *p; uint64_t next_header_offset; uint64_t next_header_size; uint32_t next_header_crc; ssize_t bytes_avail; int check_header_crc, r; if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL) return (ARCHIVE_FATAL); if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) { /* This is an executable ? Must be self-extracting... */ r = skip_sfx(a, bytes_avail); if (r < ARCHIVE_WARN) return (r); if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL) return (ARCHIVE_FATAL); } zip->seek_base += 32; if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) { archive_set_error(&a->archive, -1, "Not 7-Zip archive file"); return (ARCHIVE_FATAL); } /* CRC check. */ if (crc32(0, (const unsigned char *)p + 12, 20) != archive_le32dec(p + 8)) { archive_set_error(&a->archive, -1, "Header CRC error"); return (ARCHIVE_FATAL); } next_header_offset = archive_le64dec(p + 12); next_header_size = archive_le64dec(p + 20); next_header_crc = archive_le32dec(p + 28); if (next_header_size == 0) /* There is no entry in an archive file. */ return (ARCHIVE_EOF); if (((int64_t)next_header_offset) < 0) { archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } __archive_read_consume(a, 32); if (next_header_offset != 0) { if (bytes_avail >= (ssize_t)next_header_offset) __archive_read_consume(a, next_header_offset); else if (__archive_read_seek(a, next_header_offset + zip->seek_base, SEEK_SET) < 0) return (ARCHIVE_FATAL); } zip->stream_offset = next_header_offset; zip->header_offset = next_header_offset; zip->header_bytes_remaining = next_header_size; zip->header_crc32 = 0; zip->header_is_encoded = 0; zip->header_is_being_read = 1; zip->has_encrypted_entries = 0; check_header_crc = 1; if ((p = header_bytes(a, 1)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } /* Parse ArchiveProperties. */ switch (p[0]) { case kEncodedHeader: /* * The archive has an encoded header and we have to decode it * in order to parse the header correctly. */ r = decode_encoded_header_info(a, &(zip->si)); /* Check the EncodedHeader CRC.*/ if (r == 0 && zip->header_crc32 != next_header_crc) { archive_set_error(&a->archive, -1, "Damaged 7-Zip archive"); r = -1; } if (r == 0) { if (zip->si.ci.folders[0].digest_defined) next_header_crc = zip->si.ci.folders[0].digest; else check_header_crc = 0; if (zip->pack_stream_bytes_unconsumed) read_consume(a); r = setup_decode_folder(a, zip->si.ci.folders, 1); if (r == 0) { zip->header_bytes_remaining = zip->folder_outbytes_remaining; r = seek_pack(a); } } /* Clean up StreamsInfo. */ free_StreamsInfo(&(zip->si)); memset(&(zip->si), 0, sizeof(zip->si)); if (r < 0) return (ARCHIVE_FATAL); zip->header_is_encoded = 1; zip->header_crc32 = 0; /* FALL THROUGH */ case kHeader: /* * Parse the header. */ errno = 0; r = read_Header(a, header, zip->header_is_encoded); if (r < 0) { if (errno == ENOMEM) archive_set_error(&a->archive, -1, "Couldn't allocate memory"); else archive_set_error(&a->archive, -1, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); } /* * Must be kEnd. */ if ((p = header_bytes(a, 1)) == NULL ||*p != kEnd) { archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } /* Check the Header CRC.*/ if (check_header_crc && zip->header_crc32 != next_header_crc) { archive_set_error(&a->archive, -1, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } break; default: archive_set_error(&a->archive, -1, "Unexpected Property ID = %X", p[0]); return (ARCHIVE_FATAL); } /* Clean up variables be used for decoding the archive header */ zip->pack_stream_remaining = 0; zip->pack_stream_index = 0; zip->folder_outbytes_remaining = 0; zip->uncompressed_buffer_bytes_remaining = 0; zip->pack_stream_bytes_unconsumed = 0; zip->header_is_being_read = 0; return (ARCHIVE_OK); } static ssize_t get_uncompressed_data(struct archive_read *a, const void **buff, size_t size, size_t minimum) { struct _7zip *zip = (struct _7zip *)a->format->data; ssize_t bytes_avail; if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) { /* Copy mode. */ - /* - * Note: '1' here is a performance optimization. - * Recall that the decompression layer returns a count of - * available bytes; asking for more than that forces the - * decompressor to combine reads by copying data. - */ - *buff = __archive_read_ahead(a, 1, &bytes_avail); + *buff = __archive_read_ahead(a, minimum, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file data"); return (ARCHIVE_FATAL); } if ((size_t)bytes_avail > zip->uncompressed_buffer_bytes_remaining) bytes_avail = (ssize_t) zip->uncompressed_buffer_bytes_remaining; if ((size_t)bytes_avail > size) bytes_avail = (ssize_t)size; zip->pack_stream_bytes_unconsumed = bytes_avail; } else if (zip->uncompressed_buffer_pointer == NULL) { /* Decompression has failed. */ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); } else { /* Packed mode. */ if (minimum > zip->uncompressed_buffer_bytes_remaining) { /* * If remaining uncompressed data size is less than * the minimum size, fill the buffer up to the * minimum size. */ if (extract_pack_stream(a, minimum) < 0) return (ARCHIVE_FATAL); } if (size > zip->uncompressed_buffer_bytes_remaining) bytes_avail = (ssize_t) zip->uncompressed_buffer_bytes_remaining; else bytes_avail = (ssize_t)size; *buff = zip->uncompressed_buffer_pointer; zip->uncompressed_buffer_pointer += bytes_avail; } zip->uncompressed_buffer_bytes_remaining -= bytes_avail; return (bytes_avail); } static ssize_t extract_pack_stream(struct archive_read *a, size_t minimum) { struct _7zip *zip = (struct _7zip *)a->format->data; ssize_t bytes_avail; int r; if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) { if (minimum == 0) minimum = 1; if (__archive_read_ahead(a, minimum, &bytes_avail) == NULL || bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } if (bytes_avail > (ssize_t)zip->pack_stream_inbytes_remaining) bytes_avail = (ssize_t)zip->pack_stream_inbytes_remaining; zip->pack_stream_inbytes_remaining -= bytes_avail; if (bytes_avail > (ssize_t)zip->folder_outbytes_remaining) bytes_avail = (ssize_t)zip->folder_outbytes_remaining; zip->folder_outbytes_remaining -= bytes_avail; zip->uncompressed_buffer_bytes_remaining = bytes_avail; return (ARCHIVE_OK); } /* If the buffer hasn't been allocated, allocate it now. */ if (zip->uncompressed_buffer == NULL) { zip->uncompressed_buffer_size = UBUFF_SIZE; if (zip->uncompressed_buffer_size < minimum) { zip->uncompressed_buffer_size = minimum + 1023; zip->uncompressed_buffer_size &= ~0x3ff; } zip->uncompressed_buffer = malloc(zip->uncompressed_buffer_size); if (zip->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for 7-Zip decompression"); return (ARCHIVE_FATAL); } zip->uncompressed_buffer_bytes_remaining = 0; } else if (zip->uncompressed_buffer_size < minimum || zip->uncompressed_buffer_bytes_remaining < minimum) { /* * Make sure the uncompressed buffer can have bytes * at least `minimum' bytes. * NOTE: This case happen when reading the header. */ size_t used; if (zip->uncompressed_buffer_pointer != 0) used = zip->uncompressed_buffer_pointer - zip->uncompressed_buffer; else used = 0; if (zip->uncompressed_buffer_size < minimum) { /* * Expand the uncompressed buffer up to * the minimum size. */ void *p; size_t new_size; new_size = minimum + 1023; new_size &= ~0x3ff; p = realloc(zip->uncompressed_buffer, new_size); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for 7-Zip decompression"); return (ARCHIVE_FATAL); } zip->uncompressed_buffer = (unsigned char *)p; zip->uncompressed_buffer_size = new_size; } /* * Move unconsumed bytes to the head. */ if (used) { memmove(zip->uncompressed_buffer, zip->uncompressed_buffer + used, zip->uncompressed_buffer_bytes_remaining); } } else zip->uncompressed_buffer_bytes_remaining = 0; zip->uncompressed_buffer_pointer = NULL; for (;;) { size_t bytes_in, bytes_out; const void *buff_in; unsigned char *buff_out; int end_of_data; /* * Note: '1' here is a performance optimization. * Recall that the decompression layer returns a count of * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ buff_in = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } buff_out = zip->uncompressed_buffer + zip->uncompressed_buffer_bytes_remaining; bytes_out = zip->uncompressed_buffer_size - zip->uncompressed_buffer_bytes_remaining; bytes_in = bytes_avail; if (bytes_in > zip->pack_stream_inbytes_remaining) bytes_in = (size_t)zip->pack_stream_inbytes_remaining; /* Drive decompression. */ r = decompress(a, zip, buff_out, &bytes_out, buff_in, &bytes_in); switch (r) { case ARCHIVE_OK: end_of_data = 0; break; case ARCHIVE_EOF: end_of_data = 1; break; default: return (ARCHIVE_FATAL); } zip->pack_stream_inbytes_remaining -= bytes_in; if (bytes_out > zip->folder_outbytes_remaining) bytes_out = (size_t)zip->folder_outbytes_remaining; zip->folder_outbytes_remaining -= bytes_out; zip->uncompressed_buffer_bytes_remaining += bytes_out; zip->pack_stream_bytes_unconsumed = bytes_in; /* * Continue decompression until uncompressed_buffer is full. */ if (zip->uncompressed_buffer_bytes_remaining == zip->uncompressed_buffer_size) break; if (zip->codec2 == _7Z_X86 && zip->odd_bcj_size && zip->uncompressed_buffer_bytes_remaining + 5 > zip->uncompressed_buffer_size) break; if (zip->pack_stream_inbytes_remaining == 0 && zip->folder_outbytes_remaining == 0) break; if (end_of_data || (bytes_in == 0 && bytes_out == 0)) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); } read_consume(a); } if (zip->uncompressed_buffer_bytes_remaining < minimum) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); } zip->uncompressed_buffer_pointer = zip->uncompressed_buffer; return (ARCHIVE_OK); } static int seek_pack(struct archive_read *a) { struct _7zip *zip = (struct _7zip *)a->format->data; int64_t pack_offset; if (zip->pack_stream_remaining <= 0) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive"); return (ARCHIVE_FATAL); } zip->pack_stream_inbytes_remaining = zip->si.pi.sizes[zip->pack_stream_index]; pack_offset = zip->si.pi.positions[zip->pack_stream_index]; if (zip->stream_offset != pack_offset) { if (0 > __archive_read_seek(a, pack_offset + zip->seek_base, SEEK_SET)) return (ARCHIVE_FATAL); zip->stream_offset = pack_offset; } zip->pack_stream_index++; zip->pack_stream_remaining--; return (ARCHIVE_OK); } static ssize_t read_stream(struct archive_read *a, const void **buff, size_t size, size_t minimum) { struct _7zip *zip = (struct _7zip *)a->format->data; uint64_t skip_bytes = 0; ssize_t r; if (zip->uncompressed_buffer_bytes_remaining == 0) { if (zip->pack_stream_inbytes_remaining > 0) { r = extract_pack_stream(a, 0); if (r < 0) return (r); return (get_uncompressed_data(a, buff, size, minimum)); } else if (zip->folder_outbytes_remaining > 0) { /* Extract a remaining pack stream. */ r = extract_pack_stream(a, 0); if (r < 0) return (r); return (get_uncompressed_data(a, buff, size, minimum)); } } else return (get_uncompressed_data(a, buff, size, minimum)); /* * Current pack stream has been consumed. */ if (zip->pack_stream_remaining == 0) { if (zip->header_is_being_read) { /* Invalid sequence. This might happen when * reading a malformed archive. */ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Malformed 7-Zip archive"); return (ARCHIVE_FATAL); } /* * All current folder's pack streams have been * consumed. Switch to next folder. */ if (zip->folder_index == 0 && (zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes || zip->folder_index != zip->entry->folderIndex)) { zip->folder_index = zip->entry->folderIndex; skip_bytes = zip->si.ci.folders[zip->folder_index].skipped_bytes; } if (zip->folder_index >= zip->si.ci.numFolders) { /* * We have consumed all folders and its pack streams. */ *buff = NULL; return (0); } r = setup_decode_folder(a, &(zip->si.ci.folders[zip->folder_index]), 0); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->folder_index++; } /* * Switch to next pack stream. */ r = seek_pack(a); if (r < 0) return (r); /* Extract a new pack stream. */ r = extract_pack_stream(a, 0); if (r < 0) return (r); /* * Skip the bytes we already has skipped in skip_stream(). */ while (skip_bytes) { ssize_t skipped; if (zip->uncompressed_buffer_bytes_remaining == 0) { if (zip->pack_stream_inbytes_remaining > 0) { r = extract_pack_stream(a, 0); if (r < 0) return (r); } else if (zip->folder_outbytes_remaining > 0) { /* Extract a remaining pack stream. */ r = extract_pack_stream(a, 0); if (r < 0) return (r); } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } } skipped = get_uncompressed_data( a, buff, (size_t)skip_bytes, 0); if (skipped < 0) return (skipped); skip_bytes -= skipped; if (zip->pack_stream_bytes_unconsumed) read_consume(a); } return (get_uncompressed_data(a, buff, size, minimum)); } static int setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, int header) { struct _7zip *zip = (struct _7zip *)a->format->data; const struct _7z_coder *coder1, *coder2; const char *cname = (header)?"archive header":"file content"; unsigned i; int r, found_bcj2 = 0; /* * Release the memory which the previous folder used for BCJ2. */ for (i = 0; i < 3; i++) { - if (zip->sub_stream_buff[i] != NULL) - free(zip->sub_stream_buff[i]); + free(zip->sub_stream_buff[i]); zip->sub_stream_buff[i] = NULL; } /* * Initialize a stream reader. */ zip->pack_stream_remaining = (unsigned)folder->numPackedStreams; zip->pack_stream_index = (unsigned)folder->packIndex; zip->folder_outbytes_remaining = folder_uncompressed_size(folder); zip->uncompressed_buffer_bytes_remaining = 0; /* * Check coder types. */ for (i = 0; i < folder->numCoders; i++) { switch(folder->coders[i].codec) { case _7Z_CRYPTO_MAIN_ZIP: case _7Z_CRYPTO_RAR_29: case _7Z_CRYPTO_AES_256_SHA_256: { /* For entry that is associated with this folder, mark it as encrypted (data+metadata). */ zip->has_encrypted_entries = 1; if (a->entry) { archive_entry_set_is_data_encrypted(a->entry, 1); archive_entry_set_is_metadata_encrypted(a->entry, 1); } archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "The %s is encrypted, " "but currently not supported", cname); return (ARCHIVE_FATAL); } case _7Z_X86_BCJ2: { found_bcj2++; break; } } } /* Now that we've checked for encryption, if there were still no * encrypted entries found we can say for sure that there are none. */ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } if ((folder->numCoders > 2 && !found_bcj2) || found_bcj2 > 1) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "The %s is encoded with many filters, " "but currently not supported", cname); return (ARCHIVE_FATAL); } coder1 = &(folder->coders[0]); if (folder->numCoders == 2) coder2 = &(folder->coders[1]); else coder2 = NULL; if (found_bcj2) { /* * Preparation to decode BCJ2. * Decoding BCJ2 requires four sources. Those are at least, * as far as I know, two types of the storage form. */ const struct _7z_coder *fc = folder->coders; static const struct _7z_coder coder_copy = {0, 1, 1, 0, NULL}; const struct _7z_coder *scoder[3] = {&coder_copy, &coder_copy, &coder_copy}; const void *buff; ssize_t bytes; unsigned char *b[3] = {NULL, NULL, NULL}; uint64_t sunpack[3] ={-1, -1, -1}; size_t s[3] = {0, 0, 0}; int idx[3] = {0, 1, 2}; if (folder->numCoders == 4 && fc[3].codec == _7Z_X86_BCJ2 && folder->numInStreams == 7 && folder->numOutStreams == 4 && zip->pack_stream_remaining == 4) { /* Source type 1 made by 7zr or 7z with -m options. */ if (folder->bindPairs[0].inIndex == 5) { /* The form made by 7zr */ idx[0] = 1; idx[1] = 2; idx[2] = 0; scoder[1] = &(fc[1]); scoder[2] = &(fc[0]); sunpack[1] = folder->unPackSize[1]; sunpack[2] = folder->unPackSize[0]; coder1 = &(fc[2]); } else { /* * NOTE: Some patterns do not work. * work: * 7z a -m0=BCJ2 -m1=COPY -m2=COPY * -m3=(any) * 7z a -m0=BCJ2 -m1=COPY -m2=(any) * -m3=COPY * 7z a -m0=BCJ2 -m1=(any) -m2=COPY * -m3=COPY * not work: * other patterns. * * We have to handle this like `pipe' or * our libarchive7s filter frame work, * decoding the BCJ2 main stream sequentially, * m3 -> m2 -> m1 -> BCJ2. * */ if (fc[0].codec == _7Z_COPY && fc[1].codec == _7Z_COPY) coder1 = &(folder->coders[2]); else if (fc[0].codec == _7Z_COPY && fc[2].codec == _7Z_COPY) coder1 = &(folder->coders[1]); else if (fc[1].codec == _7Z_COPY && fc[2].codec == _7Z_COPY) coder1 = &(folder->coders[0]); else { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Unsupported form of " "BCJ2 streams"); return (ARCHIVE_FATAL); } } coder2 = &(fc[3]); zip->main_stream_bytes_remaining = (size_t)folder->unPackSize[2]; } else if (coder2 != NULL && coder2->codec == _7Z_X86_BCJ2 && zip->pack_stream_remaining == 4 && folder->numInStreams == 5 && folder->numOutStreams == 2) { /* Source type 0 made by 7z */ zip->main_stream_bytes_remaining = (size_t)folder->unPackSize[0]; } else { /* We got an unexpected form. */ archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Unsupported form of BCJ2 streams"); return (ARCHIVE_FATAL); } /* Skip the main stream at this time. */ if ((r = seek_pack(a)) < 0) return (r); zip->pack_stream_bytes_unconsumed = (size_t)zip->pack_stream_inbytes_remaining; read_consume(a); /* Read following three sub streams. */ for (i = 0; i < 3; i++) { const struct _7z_coder *coder = scoder[i]; if ((r = seek_pack(a)) < 0) { free(b[0]); free(b[1]); free(b[2]); return (r); } if (sunpack[i] == (uint64_t)-1) zip->folder_outbytes_remaining = zip->pack_stream_inbytes_remaining; else zip->folder_outbytes_remaining = sunpack[i]; r = init_decompression(a, zip, coder, NULL); if (r != ARCHIVE_OK) { free(b[0]); free(b[1]); free(b[2]); return (ARCHIVE_FATAL); } /* Allocate memory for the decoded data of a sub * stream. */ b[i] = malloc((size_t)zip->folder_outbytes_remaining); if (b[i] == NULL) { free(b[0]); free(b[1]); free(b[2]); archive_set_error(&a->archive, ENOMEM, "No memory for 7-Zip decompression"); return (ARCHIVE_FATAL); } /* Extract a sub stream. */ while (zip->pack_stream_inbytes_remaining > 0) { r = (int)extract_pack_stream(a, 0); if (r < 0) { free(b[0]); free(b[1]); free(b[2]); return (r); } bytes = get_uncompressed_data(a, &buff, zip->uncompressed_buffer_bytes_remaining, 0); if (bytes < 0) { free(b[0]); free(b[1]); free(b[2]); return ((int)bytes); } memcpy(b[i]+s[i], buff, bytes); s[i] += bytes; if (zip->pack_stream_bytes_unconsumed) read_consume(a); } } /* Set the sub streams to the right place. */ for (i = 0; i < 3; i++) { zip->sub_stream_buff[i] = b[idx[i]]; zip->sub_stream_size[i] = s[idx[i]]; zip->sub_stream_bytes_remaining[i] = s[idx[i]]; } /* Allocate memory used for decoded main stream bytes. */ if (zip->tmp_stream_buff == NULL) { zip->tmp_stream_buff_size = 32 * 1024; zip->tmp_stream_buff = malloc(zip->tmp_stream_buff_size); if (zip->tmp_stream_buff == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for 7-Zip decompression"); return (ARCHIVE_FATAL); } } zip->tmp_stream_bytes_avail = 0; zip->tmp_stream_bytes_remaining = 0; zip->odd_bcj_size = 0; zip->bcj2_outPos = 0; /* * Reset a stream reader in order to read the main stream * of BCJ2. */ zip->pack_stream_remaining = 1; zip->pack_stream_index = (unsigned)folder->packIndex; zip->folder_outbytes_remaining = folder_uncompressed_size(folder); zip->uncompressed_buffer_bytes_remaining = 0; } /* * Initialize the decompressor for the new folder's pack streams. */ r = init_decompression(a, zip, coder1, coder2); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); return (ARCHIVE_OK); } static int64_t skip_stream(struct archive_read *a, size_t skip_bytes) { struct _7zip *zip = (struct _7zip *)a->format->data; const void *p; int64_t skipped_bytes; size_t bytes = skip_bytes; if (zip->folder_index == 0) { /* * Optimization for a list mode. * Avoid unnecessary decoding operations. */ zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes += skip_bytes; return (skip_bytes); } while (bytes) { skipped_bytes = read_stream(a, &p, bytes, 0); if (skipped_bytes < 0) return (skipped_bytes); if (skipped_bytes == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated 7-Zip file body"); return (ARCHIVE_FATAL); } bytes -= (size_t)skipped_bytes; if (zip->pack_stream_bytes_unconsumed) read_consume(a); } return (skip_bytes); } /* * Brought from LZMA SDK. * * Bra86.c -- Converter for x86 code (BCJ) * 2008-10-04 : Igor Pavlov : Public domain * */ #define Test86MSByte(b) ((b) == 0 || (b) == 0xFF) static void x86_Init(struct _7zip *zip) { zip->bcj_state = 0; zip->bcj_prevPosT = (size_t)0 - 1; zip->bcj_prevMask = 0; zip->bcj_ip = 5; } static size_t x86_Convert(struct _7zip *zip, uint8_t *data, size_t size) { static const uint8_t kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0}; static const uint8_t kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3}; size_t bufferPos, prevPosT; uint32_t ip, prevMask; if (size < 5) return 0; bufferPos = 0; prevPosT = zip->bcj_prevPosT; prevMask = zip->bcj_prevMask; ip = zip->bcj_ip; for (;;) { uint8_t *p = data + bufferPos; uint8_t *limit = data + size - 4; for (; p < limit; p++) if ((*p & 0xFE) == 0xE8) break; bufferPos = (size_t)(p - data); if (p >= limit) break; prevPosT = bufferPos - prevPosT; if (prevPosT > 3) prevMask = 0; else { prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7; if (prevMask != 0) { unsigned char b = p[4 - kMaskToBitNumber[prevMask]]; if (!kMaskToAllowedStatus[prevMask] || Test86MSByte(b)) { prevPosT = bufferPos; prevMask = ((prevMask << 1) & 0x7) | 1; bufferPos++; continue; } } } prevPosT = bufferPos; if (Test86MSByte(p[4])) { uint32_t src = ((uint32_t)p[4] << 24) | ((uint32_t)p[3] << 16) | ((uint32_t)p[2] << 8) | ((uint32_t)p[1]); uint32_t dest; for (;;) { uint8_t b; int b_index; dest = src - (ip + (uint32_t)bufferPos); if (prevMask == 0) break; b_index = kMaskToBitNumber[prevMask] * 8; b = (uint8_t)(dest >> (24 - b_index)); if (!Test86MSByte(b)) break; src = dest ^ ((1 << (32 - b_index)) - 1); } p[4] = (uint8_t)(~(((dest >> 24) & 1) - 1)); p[3] = (uint8_t)(dest >> 16); p[2] = (uint8_t)(dest >> 8); p[1] = (uint8_t)dest; bufferPos += 5; } else { prevMask = ((prevMask << 1) & 0x7) | 1; bufferPos++; } } zip->bcj_prevPosT = prevPosT; zip->bcj_prevMask = prevMask; zip->bcj_ip += (uint32_t)bufferPos; return (bufferPos); } /* * Brought from LZMA SDK. * * Bcj2.c -- Converter for x86 code (BCJ2) * 2008-10-04 : Igor Pavlov : Public domain * */ #define SZ_ERROR_DATA ARCHIVE_FAILED #define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80) #define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)) #define kNumTopBits 24 #define kTopValue ((uint32_t)1 << kNumTopBits) #define kNumBitModelTotalBits 11 #define kBitModelTotal (1 << kNumBitModelTotalBits) #define kNumMoveBits 5 #define RC_READ_BYTE (*buffer++) #define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; } #define RC_INIT2 zip->bcj2_code = 0; zip->bcj2_range = 0xFFFFFFFF; \ { int ii; for (ii = 0; ii < 5; ii++) { RC_TEST; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }} #define NORMALIZE if (zip->bcj2_range < kTopValue) { RC_TEST; zip->bcj2_range <<= 8; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; } #define IF_BIT_0(p) ttt = *(p); bound = (zip->bcj2_range >> kNumBitModelTotalBits) * ttt; if (zip->bcj2_code < bound) #define UPDATE_0(p) zip->bcj2_range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE; #define UPDATE_1(p) zip->bcj2_range -= bound; zip->bcj2_code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE; static ssize_t Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize) { size_t inPos = 0, outPos = 0; const uint8_t *buf0, *buf1, *buf2, *buf3; size_t size0, size1, size2, size3; const uint8_t *buffer, *bufferLim; unsigned int i, j; size0 = zip->tmp_stream_bytes_remaining; buf0 = zip->tmp_stream_buff + zip->tmp_stream_bytes_avail - size0; size1 = zip->sub_stream_bytes_remaining[0]; buf1 = zip->sub_stream_buff[0] + zip->sub_stream_size[0] - size1; size2 = zip->sub_stream_bytes_remaining[1]; buf2 = zip->sub_stream_buff[1] + zip->sub_stream_size[1] - size2; size3 = zip->sub_stream_bytes_remaining[2]; buf3 = zip->sub_stream_buff[2] + zip->sub_stream_size[2] - size3; buffer = buf3; bufferLim = buffer + size3; if (zip->bcj_state == 0) { /* * Initialize. */ zip->bcj2_prevByte = 0; for (i = 0; i < sizeof(zip->bcj2_p) / sizeof(zip->bcj2_p[0]); i++) zip->bcj2_p[i] = kBitModelTotal >> 1; RC_INIT2; zip->bcj_state = 1; } /* * Gather the odd bytes of a previous call. */ for (i = 0; zip->odd_bcj_size > 0 && outPos < outSize; i++) { outBuf[outPos++] = zip->odd_bcj[i]; zip->odd_bcj_size--; } if (outSize == 0) { zip->bcj2_outPos += outPos; return (outPos); } for (;;) { uint8_t b; CProb *prob; uint32_t bound; uint32_t ttt; size_t limit = size0 - inPos; if (outSize - outPos < limit) limit = outSize - outPos; if (zip->bcj_state == 1) { while (limit != 0) { uint8_t bb = buf0[inPos]; outBuf[outPos++] = bb; if (IsJ(zip->bcj2_prevByte, bb)) { zip->bcj_state = 2; break; } inPos++; zip->bcj2_prevByte = bb; limit--; } } if (limit == 0 || outPos == outSize) break; zip->bcj_state = 1; b = buf0[inPos++]; if (b == 0xE8) prob = zip->bcj2_p + zip->bcj2_prevByte; else if (b == 0xE9) prob = zip->bcj2_p + 256; else prob = zip->bcj2_p + 257; IF_BIT_0(prob) { UPDATE_0(prob) zip->bcj2_prevByte = b; } else { uint32_t dest; const uint8_t *v; uint8_t out[4]; UPDATE_1(prob) if (b == 0xE8) { v = buf1; if (size1 < 4) return SZ_ERROR_DATA; buf1 += 4; size1 -= 4; } else { v = buf2; if (size2 < 4) return SZ_ERROR_DATA; buf2 += 4; size2 -= 4; } dest = (((uint32_t)v[0] << 24) | ((uint32_t)v[1] << 16) | ((uint32_t)v[2] << 8) | ((uint32_t)v[3])) - ((uint32_t)zip->bcj2_outPos + (uint32_t)outPos + 4); out[0] = (uint8_t)dest; out[1] = (uint8_t)(dest >> 8); out[2] = (uint8_t)(dest >> 16); out[3] = zip->bcj2_prevByte = (uint8_t)(dest >> 24); for (i = 0; i < 4 && outPos < outSize; i++) outBuf[outPos++] = out[i]; if (i < 4) { /* * Save odd bytes which we could not add into * the output buffer because of out of space. */ zip->odd_bcj_size = 4 -i; for (; i < 4; i++) { j = i - 4 + (unsigned)zip->odd_bcj_size; zip->odd_bcj[j] = out[i]; } break; } } } zip->tmp_stream_bytes_remaining -= inPos; zip->sub_stream_bytes_remaining[0] = size1; zip->sub_stream_bytes_remaining[1] = size2; zip->sub_stream_bytes_remaining[2] = bufferLim - buffer; zip->bcj2_outPos += outPos; return ((ssize_t)outPos); } Index: stable/10/contrib/libarchive/libarchive/archive_read_support_format_ar.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_support_format_ar.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_read_support_format_ar.c (revision 344674) @@ -1,638 +1,638 @@ /*- * Copyright (c) 2007 Kai Wang * Copyright (c) 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 * in this position and unchanged. * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_read_private.h" struct ar { int64_t entry_bytes_remaining; /* unconsumed is purely to track data we've gotten from readahead, * but haven't yet marked as consumed. Must be paired with * entry_bytes_remaining usage/modification. */ size_t entry_bytes_unconsumed; int64_t entry_offset; int64_t entry_padding; char *strtab; size_t strtab_size; char read_global_header; }; /* * Define structure of the "ar" header. */ #define AR_name_offset 0 #define AR_name_size 16 #define AR_date_offset 16 #define AR_date_size 12 #define AR_uid_offset 28 #define AR_uid_size 6 #define AR_gid_offset 34 #define AR_gid_size 6 #define AR_mode_offset 40 #define AR_mode_size 8 #define AR_size_offset 48 #define AR_size_size 10 #define AR_fmag_offset 58 #define AR_fmag_size 2 static int archive_read_format_ar_bid(struct archive_read *a, int); static int archive_read_format_ar_cleanup(struct archive_read *a); static int archive_read_format_ar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset); static int archive_read_format_ar_skip(struct archive_read *a); static int archive_read_format_ar_read_header(struct archive_read *a, struct archive_entry *e); static uint64_t ar_atol8(const char *p, unsigned char_cnt); static uint64_t ar_atol10(const char *p, unsigned char_cnt); static int ar_parse_gnu_filename_table(struct archive_read *a); static int ar_parse_common_header(struct ar *ar, struct archive_entry *, const char *h); int archive_read_support_format_ar(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct ar *ar; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_ar"); ar = (struct ar *)calloc(1, sizeof(*ar)); if (ar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data"); return (ARCHIVE_FATAL); } ar->strtab = NULL; r = __archive_read_register_format(a, ar, "ar", archive_read_format_ar_bid, NULL, archive_read_format_ar_read_header, archive_read_format_ar_read_data, archive_read_format_ar_skip, NULL, archive_read_format_ar_cleanup, NULL, NULL); if (r != ARCHIVE_OK) { free(ar); return (r); } return (ARCHIVE_OK); } static int archive_read_format_ar_cleanup(struct archive_read *a) { struct ar *ar; ar = (struct ar *)(a->format->data); - if (ar->strtab) - free(ar->strtab); + free(ar->strtab); free(ar); (a->format->data) = NULL; return (ARCHIVE_OK); } static int archive_read_format_ar_bid(struct archive_read *a, int best_bid) { const void *h; (void)best_bid; /* UNUSED */ /* * Verify the 8-byte file signature. * TODO: Do we need to check more than this? */ if ((h = __archive_read_ahead(a, 8, NULL)) == NULL) return (-1); if (memcmp(h, "!\n", 8) == 0) { return (64); } return (-1); } static int _ar_read_header(struct archive_read *a, struct archive_entry *entry, struct ar *ar, const char *h, size_t *unconsumed) { char filename[AR_name_size + 1]; uint64_t number; /* Used to hold parsed numbers before validation. */ size_t bsd_name_length, entry_size; char *p, *st; const void *b; int r; /* Verify the magic signature on the file header. */ if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) { archive_set_error(&a->archive, EINVAL, "Incorrect file header signature"); return (ARCHIVE_FATAL); } /* Copy filename into work buffer. */ strncpy(filename, h + AR_name_offset, AR_name_size); filename[AR_name_size] = '\0'; /* * Guess the format variant based on the filename. */ if (a->archive.archive_format == ARCHIVE_FORMAT_AR) { /* We don't already know the variant, so let's guess. */ /* * Biggest clue is presence of '/': GNU starts special * filenames with '/', appends '/' as terminator to * non-special names, so anything with '/' should be * GNU except for BSD long filenames. */ if (strncmp(filename, "#1/", 3) == 0) a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD; else if (strchr(filename, '/') != NULL) a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU; else if (strncmp(filename, "__.SYMDEF", 9) == 0) a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD; /* * XXX Do GNU/SVR4 'ar' programs ever omit trailing '/' * if name exactly fills 16-byte field? If so, we * can't assume entries without '/' are BSD. XXX */ } /* Update format name from the code. */ if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) a->archive.archive_format_name = "ar (GNU/SVR4)"; else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD) a->archive.archive_format_name = "ar (BSD)"; else a->archive.archive_format_name = "ar"; /* * Remove trailing spaces from the filename. GNU and BSD * variants both pad filename area out with spaces. * This will only be wrong if GNU/SVR4 'ar' implementations * omit trailing '/' for 16-char filenames and we have * a 16-char filename that ends in ' '. */ p = filename + AR_name_size - 1; while (p >= filename && *p == ' ') { *p = '\0'; p--; } /* * Remove trailing slash unless first character is '/'. * (BSD entries never end in '/', so this will only trim * GNU-format entries. GNU special entries start with '/' * and are not terminated in '/', so we don't trim anything * that starts with '/'.) */ if (filename[0] != '/' && p > filename && *p == '/') { *p = '\0'; } if (p < filename) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Found entry with empty filename"); return (ARCHIVE_FATAL); } /* * '//' is the GNU filename table. * Later entries can refer to names in this table. */ if (strcmp(filename, "//") == 0) { /* This must come before any call to _read_ahead. */ ar_parse_common_header(ar, entry, h); archive_entry_copy_pathname(entry, filename); archive_entry_set_filetype(entry, AE_IFREG); /* Get the size of the filename table. */ number = ar_atol10(h + AR_size_offset, AR_size_size); if (number > SIZE_MAX || number > 1024 * 1024 * 1024) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Filename table too large"); return (ARCHIVE_FATAL); } entry_size = (size_t)number; if (entry_size == 0) { archive_set_error(&a->archive, EINVAL, "Invalid string table"); return (ARCHIVE_FATAL); } if (ar->strtab != NULL) { archive_set_error(&a->archive, EINVAL, "More than one string tables exist"); return (ARCHIVE_FATAL); } /* Read the filename table into memory. */ st = malloc(entry_size); if (st == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate filename table buffer"); return (ARCHIVE_FATAL); } ar->strtab = st; ar->strtab_size = entry_size; if (*unconsumed) { __archive_read_consume(a, *unconsumed); *unconsumed = 0; } if ((b = __archive_read_ahead(a, entry_size, NULL)) == NULL) return (ARCHIVE_FATAL); memcpy(st, b, entry_size); __archive_read_consume(a, entry_size); /* All contents are consumed. */ ar->entry_bytes_remaining = 0; archive_entry_set_size(entry, ar->entry_bytes_remaining); /* Parse the filename table. */ return (ar_parse_gnu_filename_table(a)); } /* * GNU variant handles long filenames by storing / * to indicate a name stored in the filename table. * XXX TODO: Verify that it's all digits... Don't be fooled * by "/9xyz" XXX */ if (filename[0] == '/' && filename[1] >= '0' && filename[1] <= '9') { number = ar_atol10(h + AR_name_offset + 1, AR_name_size - 1); /* * If we can't look up the real name, warn and return * the entry with the wrong name. */ if (ar->strtab == NULL || number >= ar->strtab_size) { archive_set_error(&a->archive, EINVAL, "Can't find long filename for GNU/SVR4 archive entry"); archive_entry_copy_pathname(entry, filename); /* Parse the time, owner, mode, size fields. */ ar_parse_common_header(ar, entry, h); return (ARCHIVE_FATAL); } archive_entry_copy_pathname(entry, &ar->strtab[(size_t)number]); /* Parse the time, owner, mode, size fields. */ return (ar_parse_common_header(ar, entry, h)); } /* * BSD handles long filenames by storing "#1/" followed by the * length of filename as a decimal number, then prepends the * the filename to the file contents. */ if (strncmp(filename, "#1/", 3) == 0) { /* Parse the time, owner, mode, size fields. */ /* This must occur before _read_ahead is called again. */ ar_parse_common_header(ar, entry, h); /* Parse the size of the name, adjust the file size. */ number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3); /* Sanity check the filename length: * = Must be <= SIZE_MAX - 1 * = Must be <= 1MB * = Cannot be bigger than the entire entry */ if (number > SIZE_MAX - 1 || number > 1024 * 1024 || (int64_t)number > ar->entry_bytes_remaining) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Bad input file size"); return (ARCHIVE_FATAL); } bsd_name_length = (size_t)number; ar->entry_bytes_remaining -= bsd_name_length; /* Adjust file size reported to client. */ archive_entry_set_size(entry, ar->entry_bytes_remaining); if (*unconsumed) { __archive_read_consume(a, *unconsumed); *unconsumed = 0; } /* Read the long name into memory. */ if ((b = __archive_read_ahead(a, bsd_name_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated input file"); return (ARCHIVE_FATAL); } /* Store it in the entry. */ p = (char *)malloc(bsd_name_length + 1); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate fname buffer"); return (ARCHIVE_FATAL); } strncpy(p, b, bsd_name_length); p[bsd_name_length] = '\0'; __archive_read_consume(a, bsd_name_length); archive_entry_copy_pathname(entry, p); free(p); return (ARCHIVE_OK); } /* * "/" is the SVR4/GNU archive symbol table. + * "/SYM64/" is the SVR4/GNU 64-bit variant archive symbol table. */ - if (strcmp(filename, "/") == 0) { - archive_entry_copy_pathname(entry, "/"); + if (strcmp(filename, "/") == 0 || strcmp(filename, "/SYM64/") == 0) { + archive_entry_copy_pathname(entry, filename); /* Parse the time, owner, mode, size fields. */ r = ar_parse_common_header(ar, entry, h); /* Force the file type to a regular file. */ archive_entry_set_filetype(entry, AE_IFREG); return (r); } /* * "__.SYMDEF" is a BSD archive symbol table. */ if (strcmp(filename, "__.SYMDEF") == 0) { archive_entry_copy_pathname(entry, filename); /* Parse the time, owner, mode, size fields. */ return (ar_parse_common_header(ar, entry, h)); } /* * Otherwise, this is a standard entry. The filename * has already been trimmed as much as possible, based * on our current knowledge of the format. */ archive_entry_copy_pathname(entry, filename); return (ar_parse_common_header(ar, entry, h)); } static int archive_read_format_ar_read_header(struct archive_read *a, struct archive_entry *entry) { struct ar *ar = (struct ar*)(a->format->data); size_t unconsumed; const void *header_data; int ret; if (!ar->read_global_header) { /* * We are now at the beginning of the archive, * so we need first consume the ar global header. */ __archive_read_consume(a, 8); ar->read_global_header = 1; /* Set a default format code for now. */ a->archive.archive_format = ARCHIVE_FORMAT_AR; } /* Read the header for the next file entry. */ if ((header_data = __archive_read_ahead(a, 60, NULL)) == NULL) /* Broken header. */ return (ARCHIVE_EOF); unconsumed = 60; ret = _ar_read_header(a, entry, ar, (const char *)header_data, &unconsumed); if (unconsumed) __archive_read_consume(a, unconsumed); return ret; } static int ar_parse_common_header(struct ar *ar, struct archive_entry *entry, const char *h) { uint64_t n; /* Copy remaining header */ archive_entry_set_filetype(entry, AE_IFREG); archive_entry_set_mtime(entry, (time_t)ar_atol10(h + AR_date_offset, AR_date_size), 0L); archive_entry_set_uid(entry, (uid_t)ar_atol10(h + AR_uid_offset, AR_uid_size)); archive_entry_set_gid(entry, (gid_t)ar_atol10(h + AR_gid_offset, AR_gid_size)); archive_entry_set_mode(entry, (mode_t)ar_atol8(h + AR_mode_offset, AR_mode_size)); n = ar_atol10(h + AR_size_offset, AR_size_size); ar->entry_offset = 0; ar->entry_padding = n % 2; archive_entry_set_size(entry, n); ar->entry_bytes_remaining = n; return (ARCHIVE_OK); } static int archive_read_format_ar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { ssize_t bytes_read; struct ar *ar; ar = (struct ar *)(a->format->data); if (ar->entry_bytes_unconsumed) { __archive_read_consume(a, ar->entry_bytes_unconsumed); ar->entry_bytes_unconsumed = 0; } if (ar->entry_bytes_remaining > 0) { *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated ar archive"); return (ARCHIVE_FATAL); } if (bytes_read < 0) return (ARCHIVE_FATAL); if (bytes_read > ar->entry_bytes_remaining) bytes_read = (ssize_t)ar->entry_bytes_remaining; *size = bytes_read; ar->entry_bytes_unconsumed = bytes_read; *offset = ar->entry_offset; ar->entry_offset += bytes_read; ar->entry_bytes_remaining -= bytes_read; return (ARCHIVE_OK); } else { int64_t skipped = __archive_read_consume(a, ar->entry_padding); if (skipped >= 0) { ar->entry_padding -= skipped; } if (ar->entry_padding) { if (skipped >= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated ar archive- failed consuming padding"); } return (ARCHIVE_FATAL); } *buff = NULL; *size = 0; *offset = ar->entry_offset; return (ARCHIVE_EOF); } } static int archive_read_format_ar_skip(struct archive_read *a) { int64_t bytes_skipped; struct ar* ar; ar = (struct ar *)(a->format->data); bytes_skipped = __archive_read_consume(a, ar->entry_bytes_remaining + ar->entry_padding + ar->entry_bytes_unconsumed); if (bytes_skipped < 0) return (ARCHIVE_FATAL); ar->entry_bytes_remaining = 0; ar->entry_bytes_unconsumed = 0; ar->entry_padding = 0; return (ARCHIVE_OK); } static int ar_parse_gnu_filename_table(struct archive_read *a) { struct ar *ar; char *p; size_t size; ar = (struct ar*)(a->format->data); size = ar->strtab_size; for (p = ar->strtab; p < ar->strtab + size - 1; ++p) { if (*p == '/') { *p++ = '\0'; if (*p != '\n') goto bad_string_table; *p = '\0'; } } /* * GNU ar always pads the table to an even size. * The pad character is either '\n' or '`'. */ if (p != ar->strtab + size && *p != '\n' && *p != '`') goto bad_string_table; /* Enforce zero termination. */ ar->strtab[size - 1] = '\0'; return (ARCHIVE_OK); bad_string_table: archive_set_error(&a->archive, EINVAL, "Invalid string table"); free(ar->strtab); ar->strtab = NULL; return (ARCHIVE_FATAL); } static uint64_t ar_atol8(const char *p, unsigned char_cnt) { uint64_t l, limit, last_digit_limit; unsigned int digit, base; base = 8; limit = UINT64_MAX / base; last_digit_limit = UINT64_MAX % base; while ((*p == ' ' || *p == '\t') && char_cnt-- > 0) p++; l = 0; digit = *p - '0'; while (*p >= '0' && digit < base && char_cnt-- > 0) { if (l>limit || (l == limit && digit > last_digit_limit)) { l = UINT64_MAX; /* Truncate on overflow. */ break; } l = (l * base) + digit; digit = *++p - '0'; } return (l); } static uint64_t ar_atol10(const char *p, unsigned char_cnt) { uint64_t l, limit, last_digit_limit; unsigned int base, digit; base = 10; limit = UINT64_MAX / base; last_digit_limit = UINT64_MAX % base; while ((*p == ' ' || *p == '\t') && char_cnt-- > 0) p++; l = 0; digit = *p - '0'; while (*p >= '0' && digit < base && char_cnt-- > 0) { if (l > limit || (l == limit && digit > last_digit_limit)) { l = UINT64_MAX; /* Truncate on overflow. */ break; } l = (l * base) + digit; digit = *++p - '0'; } return (l); } Index: stable/10/contrib/libarchive/libarchive/archive_read_support_format_cpio.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_support_format_cpio.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_read_support_format_cpio.c (revision 344674) @@ -1,1087 +1,1086 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2010-2012 Michihiro NAKAJIMA * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif /* #include */ /* See archive_platform.h */ #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_read_private.h" #define bin_magic_offset 0 #define bin_magic_size 2 #define bin_dev_offset 2 #define bin_dev_size 2 #define bin_ino_offset 4 #define bin_ino_size 2 #define bin_mode_offset 6 #define bin_mode_size 2 #define bin_uid_offset 8 #define bin_uid_size 2 #define bin_gid_offset 10 #define bin_gid_size 2 #define bin_nlink_offset 12 #define bin_nlink_size 2 #define bin_rdev_offset 14 #define bin_rdev_size 2 #define bin_mtime_offset 16 #define bin_mtime_size 4 #define bin_namesize_offset 20 #define bin_namesize_size 2 #define bin_filesize_offset 22 #define bin_filesize_size 4 #define bin_header_size 26 #define odc_magic_offset 0 #define odc_magic_size 6 #define odc_dev_offset 6 #define odc_dev_size 6 #define odc_ino_offset 12 #define odc_ino_size 6 #define odc_mode_offset 18 #define odc_mode_size 6 #define odc_uid_offset 24 #define odc_uid_size 6 #define odc_gid_offset 30 #define odc_gid_size 6 #define odc_nlink_offset 36 #define odc_nlink_size 6 #define odc_rdev_offset 42 #define odc_rdev_size 6 #define odc_mtime_offset 48 #define odc_mtime_size 11 #define odc_namesize_offset 59 #define odc_namesize_size 6 #define odc_filesize_offset 65 #define odc_filesize_size 11 #define odc_header_size 76 #define newc_magic_offset 0 #define newc_magic_size 6 #define newc_ino_offset 6 #define newc_ino_size 8 #define newc_mode_offset 14 #define newc_mode_size 8 #define newc_uid_offset 22 #define newc_uid_size 8 #define newc_gid_offset 30 #define newc_gid_size 8 #define newc_nlink_offset 38 #define newc_nlink_size 8 #define newc_mtime_offset 46 #define newc_mtime_size 8 #define newc_filesize_offset 54 #define newc_filesize_size 8 #define newc_devmajor_offset 62 #define newc_devmajor_size 8 #define newc_devminor_offset 70 #define newc_devminor_size 8 #define newc_rdevmajor_offset 78 #define newc_rdevmajor_size 8 #define newc_rdevminor_offset 86 #define newc_rdevminor_size 8 #define newc_namesize_offset 94 #define newc_namesize_size 8 #define newc_checksum_offset 102 #define newc_checksum_size 8 #define newc_header_size 110 /* * An afio large ASCII header, which they named itself. * afio utility uses this header, if a file size is larger than 2G bytes * or inode/uid/gid is bigger than 65535(0xFFFF) or mtime is bigger than * 0x7fffffff, which we cannot record to odc header because of its limit. * If not, uses odc header. */ #define afiol_magic_offset 0 #define afiol_magic_size 6 #define afiol_dev_offset 6 #define afiol_dev_size 8 /* hex */ #define afiol_ino_offset 14 #define afiol_ino_size 16 /* hex */ #define afiol_ino_m_offset 30 /* 'm' */ #define afiol_mode_offset 31 #define afiol_mode_size 6 /* oct */ #define afiol_uid_offset 37 #define afiol_uid_size 8 /* hex */ #define afiol_gid_offset 45 #define afiol_gid_size 8 /* hex */ #define afiol_nlink_offset 53 #define afiol_nlink_size 8 /* hex */ #define afiol_rdev_offset 61 #define afiol_rdev_size 8 /* hex */ #define afiol_mtime_offset 69 #define afiol_mtime_size 16 /* hex */ #define afiol_mtime_n_offset 85 /* 'n' */ #define afiol_namesize_offset 86 #define afiol_namesize_size 4 /* hex */ #define afiol_flag_offset 90 #define afiol_flag_size 4 /* hex */ #define afiol_xsize_offset 94 #define afiol_xsize_size 4 /* hex */ #define afiol_xsize_s_offset 98 /* 's' */ #define afiol_filesize_offset 99 #define afiol_filesize_size 16 /* hex */ #define afiol_filesize_c_offset 115 /* ':' */ #define afiol_header_size 116 struct links_entry { struct links_entry *next; struct links_entry *previous; unsigned int links; dev_t dev; int64_t ino; char *name; }; #define CPIO_MAGIC 0x13141516 struct cpio { int magic; int (*read_header)(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); struct links_entry *links_head; int64_t entry_bytes_remaining; int64_t entry_bytes_unconsumed; int64_t entry_offset; int64_t entry_padding; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_default; int init_default_conversion; }; static int64_t atol16(const char *, unsigned); static int64_t atol8(const char *, unsigned); static int archive_read_format_cpio_bid(struct archive_read *, int); static int archive_read_format_cpio_options(struct archive_read *, const char *, const char *); static int archive_read_format_cpio_cleanup(struct archive_read *); static int archive_read_format_cpio_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_cpio_read_header(struct archive_read *, struct archive_entry *); static int archive_read_format_cpio_skip(struct archive_read *); static int64_t be4(const unsigned char *); static int find_odc_header(struct archive_read *); static int find_newc_header(struct archive_read *); static int header_bin_be(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_bin_le(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_newc(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_odc(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int header_afiol(struct archive_read *, struct cpio *, struct archive_entry *, size_t *, size_t *); static int is_octal(const char *, size_t); static int is_hex(const char *, size_t); static int64_t le4(const unsigned char *); static int record_hardlink(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry); int archive_read_support_format_cpio(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct cpio *cpio; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_cpio"); cpio = (struct cpio *)calloc(1, sizeof(*cpio)); if (cpio == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data"); return (ARCHIVE_FATAL); } cpio->magic = CPIO_MAGIC; r = __archive_read_register_format(a, cpio, "cpio", archive_read_format_cpio_bid, archive_read_format_cpio_options, archive_read_format_cpio_read_header, archive_read_format_cpio_read_data, archive_read_format_cpio_skip, NULL, archive_read_format_cpio_cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(cpio); return (ARCHIVE_OK); } static int archive_read_format_cpio_bid(struct archive_read *a, int best_bid) { const unsigned char *p; struct cpio *cpio; int bid; (void)best_bid; /* UNUSED */ cpio = (struct cpio *)(a->format->data); if ((p = __archive_read_ahead(a, 6, NULL)) == NULL) return (-1); bid = 0; if (memcmp(p, "070707", 6) == 0) { /* ASCII cpio archive (odc, POSIX.1) */ cpio->read_header = header_odc; bid += 48; /* * XXX TODO: More verification; Could check that only octal * digits appear in appropriate header locations. XXX */ } else if (memcmp(p, "070727", 6) == 0) { /* afio large ASCII cpio archive */ cpio->read_header = header_odc; bid += 48; /* * XXX TODO: More verification; Could check that almost hex * digits appear in appropriate header locations. XXX */ } else if (memcmp(p, "070701", 6) == 0) { /* ASCII cpio archive (SVR4 without CRC) */ cpio->read_header = header_newc; bid += 48; /* * XXX TODO: More verification; Could check that only hex * digits appear in appropriate header locations. XXX */ } else if (memcmp(p, "070702", 6) == 0) { /* ASCII cpio archive (SVR4 with CRC) */ /* XXX TODO: Flag that we should check the CRC. XXX */ cpio->read_header = header_newc; bid += 48; /* * XXX TODO: More verification; Could check that only hex * digits appear in appropriate header locations. XXX */ } else if (p[0] * 256 + p[1] == 070707) { /* big-endian binary cpio archives */ cpio->read_header = header_bin_be; bid += 16; /* Is more verification possible here? */ } else if (p[0] + p[1] * 256 == 070707) { /* little-endian binary cpio archives */ cpio->read_header = header_bin_le; bid += 16; /* Is more verification possible here? */ } else return (ARCHIVE_WARN); return (bid); } static int archive_read_format_cpio_options(struct archive_read *a, const char *key, const char *val) { struct cpio *cpio; int ret = ARCHIVE_FAILED; cpio = (struct cpio *)(a->format->data); if (strcmp(key, "compat-2x") == 0) { /* Handle filenames as libarchive 2.x */ cpio->init_default_conversion = (val != NULL)?1:0; return (ARCHIVE_OK); } else if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "cpio: hdrcharset option needs a character-set name"); else { cpio->opt_sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (cpio->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int archive_read_format_cpio_read_header(struct archive_read *a, struct archive_entry *entry) { struct cpio *cpio; const void *h, *hl; struct archive_string_conv *sconv; size_t namelength; size_t name_pad; int r; cpio = (struct cpio *)(a->format->data); sconv = cpio->opt_sconv; if (sconv == NULL) { if (!cpio->init_default_conversion) { cpio->sconv_default = archive_string_default_conversion_for_read( &(a->archive)); cpio->init_default_conversion = 1; } sconv = cpio->sconv_default; } r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad)); if (r < ARCHIVE_WARN) return (r); /* Read name from buffer. */ h = __archive_read_ahead(a, namelength + name_pad, NULL); if (h == NULL) return (ARCHIVE_FATAL); if (archive_entry_copy_pathname_l(entry, (const char *)h, namelength, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname can't be converted from %s to current locale.", archive_string_conversion_charset_name(sconv)); r = ARCHIVE_WARN; } cpio->entry_offset = 0; __archive_read_consume(a, namelength + name_pad); /* If this is a symlink, read the link contents. */ if (archive_entry_filetype(entry) == AE_IFLNK) { if (cpio->entry_bytes_remaining > 1024 * 1024) { archive_set_error(&a->archive, ENOMEM, "Rejecting malformed cpio archive: symlink contents exceed 1 megabyte"); return (ARCHIVE_FATAL); } hl = __archive_read_ahead(a, (size_t)cpio->entry_bytes_remaining, NULL); if (hl == NULL) return (ARCHIVE_FATAL); if (archive_entry_copy_symlink_l(entry, (const char *)hl, (size_t)cpio->entry_bytes_remaining, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Linkname can't be converted from %s to " "current locale.", archive_string_conversion_charset_name(sconv)); r = ARCHIVE_WARN; } __archive_read_consume(a, cpio->entry_bytes_remaining); cpio->entry_bytes_remaining = 0; } /* XXX TODO: If the full mode is 0160200, then this is a Solaris * ACL description for the following entry. Read this body * and parse it as a Solaris-style ACL, then read the next * header. XXX */ /* Compare name to "TRAILER!!!" to test for end-of-archive. */ if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!", 11) == 0) { /* TODO: Store file location of start of block. */ archive_clear_error(&a->archive); return (ARCHIVE_EOF); } /* Detect and record hardlinks to previously-extracted entries. */ if (record_hardlink(a, cpio, entry) != ARCHIVE_OK) { return (ARCHIVE_FATAL); } return (r); } static int archive_read_format_cpio_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { ssize_t bytes_read; struct cpio *cpio; cpio = (struct cpio *)(a->format->data); if (cpio->entry_bytes_unconsumed) { __archive_read_consume(a, cpio->entry_bytes_unconsumed); cpio->entry_bytes_unconsumed = 0; } if (cpio->entry_bytes_remaining > 0) { *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) return (ARCHIVE_FATAL); if (bytes_read > cpio->entry_bytes_remaining) bytes_read = (ssize_t)cpio->entry_bytes_remaining; *size = bytes_read; cpio->entry_bytes_unconsumed = bytes_read; *offset = cpio->entry_offset; cpio->entry_offset += bytes_read; cpio->entry_bytes_remaining -= bytes_read; return (ARCHIVE_OK); } else { if (cpio->entry_padding != __archive_read_consume(a, cpio->entry_padding)) { return (ARCHIVE_FATAL); } cpio->entry_padding = 0; *buff = NULL; *size = 0; *offset = cpio->entry_offset; return (ARCHIVE_EOF); } } static int archive_read_format_cpio_skip(struct archive_read *a) { struct cpio *cpio = (struct cpio *)(a->format->data); int64_t to_skip = cpio->entry_bytes_remaining + cpio->entry_padding + cpio->entry_bytes_unconsumed; if (to_skip != __archive_read_consume(a, to_skip)) { return (ARCHIVE_FATAL); } cpio->entry_bytes_remaining = 0; cpio->entry_padding = 0; cpio->entry_bytes_unconsumed = 0; return (ARCHIVE_OK); } /* * Skip forward to the next cpio newc header by searching for the * 07070[12] string. This should be generalized and merged with * find_odc_header below. */ static int is_hex(const char *p, size_t len) { while (len-- > 0) { if ((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || (*p >= 'A' && *p <= 'F')) ++p; else return (0); } return (1); } static int find_newc_header(struct archive_read *a) { const void *h; const char *p, *q; size_t skip, skipped = 0; ssize_t bytes; for (;;) { h = __archive_read_ahead(a, newc_header_size, &bytes); if (h == NULL) return (ARCHIVE_FATAL); p = h; q = p + bytes; /* Try the typical case first, then go into the slow search.*/ if (memcmp("07070", p, 5) == 0 && (p[5] == '1' || p[5] == '2') && is_hex(p, newc_header_size)) return (ARCHIVE_OK); /* * Scan ahead until we find something that looks * like a newc header. */ while (p + newc_header_size <= q) { switch (p[5]) { case '1': case '2': if (memcmp("07070", p, 5) == 0 && is_hex(p, newc_header_size)) { skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; if (skipped > 0) { archive_set_error(&a->archive, 0, "Skipped %d bytes before " "finding valid header", (int)skipped); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } p += 2; break; case '0': p++; break; default: p += 6; break; } } skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; } } static int header_newc(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const char *header; int r; r = find_newc_header(a); if (r < ARCHIVE_WARN) return (r); /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, newc_header_size, NULL); if (h == NULL) return (ARCHIVE_FATAL); /* Parse out hex fields. */ header = (const char *)h; if (memcmp(header + newc_magic_offset, "070701", 6) == 0) { a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC; a->archive.archive_format_name = "ASCII cpio (SVR4 with no CRC)"; } else if (memcmp(header + newc_magic_offset, "070702", 6) == 0) { a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_CRC; a->archive.archive_format_name = "ASCII cpio (SVR4 with CRC)"; } else { /* TODO: Abort here? */ } archive_entry_set_devmajor(entry, (dev_t)atol16(header + newc_devmajor_offset, newc_devmajor_size)); archive_entry_set_devminor(entry, (dev_t)atol16(header + newc_devminor_offset, newc_devminor_size)); archive_entry_set_ino(entry, atol16(header + newc_ino_offset, newc_ino_size)); archive_entry_set_mode(entry, (mode_t)atol16(header + newc_mode_offset, newc_mode_size)); archive_entry_set_uid(entry, atol16(header + newc_uid_offset, newc_uid_size)); archive_entry_set_gid(entry, atol16(header + newc_gid_offset, newc_gid_size)); archive_entry_set_nlink(entry, (unsigned int)atol16(header + newc_nlink_offset, newc_nlink_size)); archive_entry_set_rdevmajor(entry, (dev_t)atol16(header + newc_rdevmajor_offset, newc_rdevmajor_size)); archive_entry_set_rdevminor(entry, (dev_t)atol16(header + newc_rdevminor_offset, newc_rdevminor_size)); archive_entry_set_mtime(entry, atol16(header + newc_mtime_offset, newc_mtime_size), 0); *namelength = (size_t)atol16(header + newc_namesize_offset, newc_namesize_size); /* Pad name to 2 more than a multiple of 4. */ *name_pad = (2 - *namelength) & 3; /* Make sure that the padded name length fits into size_t. */ if (*name_pad > SIZE_MAX - *namelength) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "cpio archive has invalid namelength"); return (ARCHIVE_FATAL); } /* * Note: entry_bytes_remaining is at least 64 bits and * therefore guaranteed to be big enough for a 33-bit file * size. */ cpio->entry_bytes_remaining = atol16(header + newc_filesize_offset, newc_filesize_size); archive_entry_set_size(entry, cpio->entry_bytes_remaining); /* Pad file contents to a multiple of 4. */ cpio->entry_padding = 3 & -cpio->entry_bytes_remaining; __archive_read_consume(a, newc_header_size); return (r); } /* * Skip forward to the next cpio odc header by searching for the * 070707 string. This is a hand-optimized search that could * probably be easily generalized to handle all character-based * cpio variants. */ static int is_octal(const char *p, size_t len) { while (len-- > 0) { if (*p < '0' || *p > '7') return (0); ++p; } return (1); } static int is_afio_large(const char *h, size_t len) { if (len < afiol_header_size) return (0); if (h[afiol_ino_m_offset] != 'm' || h[afiol_mtime_n_offset] != 'n' || h[afiol_xsize_s_offset] != 's' || h[afiol_filesize_c_offset] != ':') return (0); if (!is_hex(h + afiol_dev_offset, afiol_ino_m_offset - afiol_dev_offset)) return (0); if (!is_hex(h + afiol_mode_offset, afiol_mtime_n_offset - afiol_mode_offset)) return (0); if (!is_hex(h + afiol_namesize_offset, afiol_xsize_s_offset - afiol_namesize_offset)) return (0); if (!is_hex(h + afiol_filesize_offset, afiol_filesize_size)) return (0); return (1); } static int find_odc_header(struct archive_read *a) { const void *h; const char *p, *q; size_t skip, skipped = 0; ssize_t bytes; for (;;) { h = __archive_read_ahead(a, odc_header_size, &bytes); if (h == NULL) return (ARCHIVE_FATAL); p = h; q = p + bytes; /* Try the typical case first, then go into the slow search.*/ if (memcmp("070707", p, 6) == 0 && is_octal(p, odc_header_size)) return (ARCHIVE_OK); if (memcmp("070727", p, 6) == 0 && is_afio_large(p, bytes)) { a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE; return (ARCHIVE_OK); } /* * Scan ahead until we find something that looks * like an odc header. */ while (p + odc_header_size <= q) { switch (p[5]) { case '7': if ((memcmp("070707", p, 6) == 0 && is_octal(p, odc_header_size)) || (memcmp("070727", p, 6) == 0 && is_afio_large(p, q - p))) { skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; if (p[4] == '2') a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE; if (skipped > 0) { archive_set_error(&a->archive, 0, "Skipped %d bytes before " "finding valid header", (int)skipped); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } p += 2; break; case '0': p++; break; default: p += 6; break; } } skip = p - (const char *)h; __archive_read_consume(a, skip); skipped += skip; } } static int header_odc(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; int r; const char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX; a->archive.archive_format_name = "POSIX octet-oriented cpio"; /* Find the start of the next header. */ r = find_odc_header(a); if (r < ARCHIVE_WARN) return (r); if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_AFIO_LARGE) { int r2 = (header_afiol(a, cpio, entry, namelength, name_pad)); if (r2 == ARCHIVE_OK) return (r); else return (r2); } /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, odc_header_size, NULL); if (h == NULL) return (ARCHIVE_FATAL); /* Parse out octal fields. */ header = (const char *)h; archive_entry_set_dev(entry, (dev_t)atol8(header + odc_dev_offset, odc_dev_size)); archive_entry_set_ino(entry, atol8(header + odc_ino_offset, odc_ino_size)); archive_entry_set_mode(entry, (mode_t)atol8(header + odc_mode_offset, odc_mode_size)); archive_entry_set_uid(entry, atol8(header + odc_uid_offset, odc_uid_size)); archive_entry_set_gid(entry, atol8(header + odc_gid_offset, odc_gid_size)); archive_entry_set_nlink(entry, (unsigned int)atol8(header + odc_nlink_offset, odc_nlink_size)); archive_entry_set_rdev(entry, (dev_t)atol8(header + odc_rdev_offset, odc_rdev_size)); archive_entry_set_mtime(entry, atol8(header + odc_mtime_offset, odc_mtime_size), 0); *namelength = (size_t)atol8(header + odc_namesize_offset, odc_namesize_size); *name_pad = 0; /* No padding of filename. */ /* * Note: entry_bytes_remaining is at least 64 bits and * therefore guaranteed to be big enough for a 33-bit file * size. */ cpio->entry_bytes_remaining = atol8(header + odc_filesize_offset, odc_filesize_size); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = 0; __archive_read_consume(a, odc_header_size); return (r); } /* * NOTE: if a filename suffix is ".z", it is the file gziped by afio. * it would be nice that we can show uncompressed file size and we can * uncompressed file contents automatically, unfortunately we have nothing * to get a uncompressed file size while reading each header. It means * we also cannot uncompress file contents under our framework. */ static int header_afiol(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE; a->archive.archive_format_name = "afio large ASCII"; /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, afiol_header_size, NULL); if (h == NULL) return (ARCHIVE_FATAL); /* Parse out octal fields. */ header = (const char *)h; archive_entry_set_dev(entry, (dev_t)atol16(header + afiol_dev_offset, afiol_dev_size)); archive_entry_set_ino(entry, atol16(header + afiol_ino_offset, afiol_ino_size)); archive_entry_set_mode(entry, (mode_t)atol8(header + afiol_mode_offset, afiol_mode_size)); archive_entry_set_uid(entry, atol16(header + afiol_uid_offset, afiol_uid_size)); archive_entry_set_gid(entry, atol16(header + afiol_gid_offset, afiol_gid_size)); archive_entry_set_nlink(entry, (unsigned int)atol16(header + afiol_nlink_offset, afiol_nlink_size)); archive_entry_set_rdev(entry, (dev_t)atol16(header + afiol_rdev_offset, afiol_rdev_size)); archive_entry_set_mtime(entry, atol16(header + afiol_mtime_offset, afiol_mtime_size), 0); *namelength = (size_t)atol16(header + afiol_namesize_offset, afiol_namesize_size); *name_pad = 0; /* No padding of filename. */ cpio->entry_bytes_remaining = atol16(header + afiol_filesize_offset, afiol_filesize_size); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = 0; __archive_read_consume(a, afiol_header_size); return (ARCHIVE_OK); } static int header_bin_le(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const unsigned char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE; a->archive.archive_format_name = "cpio (little-endian binary)"; /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, bin_header_size, NULL); if (h == NULL) { archive_set_error(&a->archive, 0, "End of file trying to read next cpio header"); return (ARCHIVE_FATAL); } /* Parse out binary fields. */ header = (const unsigned char *)h; archive_entry_set_dev(entry, header[bin_dev_offset] + header[bin_dev_offset + 1] * 256); archive_entry_set_ino(entry, header[bin_ino_offset] + header[bin_ino_offset + 1] * 256); archive_entry_set_mode(entry, header[bin_mode_offset] + header[bin_mode_offset + 1] * 256); archive_entry_set_uid(entry, header[bin_uid_offset] + header[bin_uid_offset + 1] * 256); archive_entry_set_gid(entry, header[bin_gid_offset] + header[bin_gid_offset + 1] * 256); archive_entry_set_nlink(entry, header[bin_nlink_offset] + header[bin_nlink_offset + 1] * 256); archive_entry_set_rdev(entry, header[bin_rdev_offset] + header[bin_rdev_offset + 1] * 256); archive_entry_set_mtime(entry, le4(header + bin_mtime_offset), 0); *namelength = header[bin_namesize_offset] + header[bin_namesize_offset + 1] * 256; *name_pad = *namelength & 1; /* Pad to even. */ cpio->entry_bytes_remaining = le4(header + bin_filesize_offset); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ __archive_read_consume(a, bin_header_size); return (ARCHIVE_OK); } static int header_bin_be(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry, size_t *namelength, size_t *name_pad) { const void *h; const unsigned char *header; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE; a->archive.archive_format_name = "cpio (big-endian binary)"; /* Read fixed-size portion of header. */ h = __archive_read_ahead(a, bin_header_size, NULL); if (h == NULL) { archive_set_error(&a->archive, 0, "End of file trying to read next cpio header"); return (ARCHIVE_FATAL); } /* Parse out binary fields. */ header = (const unsigned char *)h; archive_entry_set_dev(entry, header[bin_dev_offset] * 256 + header[bin_dev_offset + 1]); archive_entry_set_ino(entry, header[bin_ino_offset] * 256 + header[bin_ino_offset + 1]); archive_entry_set_mode(entry, header[bin_mode_offset] * 256 + header[bin_mode_offset + 1]); archive_entry_set_uid(entry, header[bin_uid_offset] * 256 + header[bin_uid_offset + 1]); archive_entry_set_gid(entry, header[bin_gid_offset] * 256 + header[bin_gid_offset + 1]); archive_entry_set_nlink(entry, header[bin_nlink_offset] * 256 + header[bin_nlink_offset + 1]); archive_entry_set_rdev(entry, header[bin_rdev_offset] * 256 + header[bin_rdev_offset + 1]); archive_entry_set_mtime(entry, be4(header + bin_mtime_offset), 0); *namelength = header[bin_namesize_offset] * 256 + header[bin_namesize_offset + 1]; *name_pad = *namelength & 1; /* Pad to even. */ cpio->entry_bytes_remaining = be4(header + bin_filesize_offset); archive_entry_set_size(entry, cpio->entry_bytes_remaining); cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */ __archive_read_consume(a, bin_header_size); return (ARCHIVE_OK); } static int archive_read_format_cpio_cleanup(struct archive_read *a) { struct cpio *cpio; cpio = (struct cpio *)(a->format->data); /* Free inode->name map */ while (cpio->links_head != NULL) { struct links_entry *lp = cpio->links_head->next; - if (cpio->links_head->name) - free(cpio->links_head->name); + free(cpio->links_head->name); free(cpio->links_head); cpio->links_head = lp; } free(cpio); (a->format->data) = NULL; return (ARCHIVE_OK); } static int64_t le4(const unsigned char *p) { return ((p[0] << 16) + (((int64_t)p[1]) << 24) + (p[2] << 0) + (p[3] << 8)); } static int64_t be4(const unsigned char *p) { return ((((int64_t)p[0]) << 24) + (p[1] << 16) + (p[2] << 8) + (p[3])); } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static int64_t atol8(const char *p, unsigned char_cnt) { int64_t l; int digit; l = 0; while (char_cnt-- > 0) { if (*p >= '0' && *p <= '7') digit = *p - '0'; else return (l); p++; l <<= 3; l |= digit; } return (l); } static int64_t atol16(const char *p, unsigned char_cnt) { int64_t l; int digit; l = 0; while (char_cnt-- > 0) { if (*p >= 'a' && *p <= 'f') digit = *p - 'a' + 10; else if (*p >= 'A' && *p <= 'F') digit = *p - 'A' + 10; else if (*p >= '0' && *p <= '9') digit = *p - '0'; else return (l); p++; l <<= 4; l |= digit; } return (l); } static int record_hardlink(struct archive_read *a, struct cpio *cpio, struct archive_entry *entry) { struct links_entry *le; dev_t dev; int64_t ino; if (archive_entry_nlink(entry) <= 1) return (ARCHIVE_OK); dev = archive_entry_dev(entry); ino = archive_entry_ino64(entry); /* * First look in the list of multiply-linked files. If we've * already dumped it, convert this entry to a hard link entry. */ for (le = cpio->links_head; le; le = le->next) { if (le->dev == dev && le->ino == ino) { archive_entry_copy_hardlink(entry, le->name); if (--le->links <= 0) { if (le->previous != NULL) le->previous->next = le->next; if (le->next != NULL) le->next->previous = le->previous; if (cpio->links_head == le) cpio->links_head = le->next; free(le->name); free(le); } return (ARCHIVE_OK); } } le = (struct links_entry *)malloc(sizeof(struct links_entry)); if (le == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory adding file to list"); return (ARCHIVE_FATAL); } if (cpio->links_head != NULL) cpio->links_head->previous = le; le->next = cpio->links_head; le->previous = NULL; cpio->links_head = le; le->dev = dev; le->ino = ino; le->links = archive_entry_nlink(entry) - 1; le->name = strdup(archive_entry_pathname(entry)); if (le->name == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory adding file to list"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } Index: stable/10/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_read_support_format_iso9660.c (revision 344674) @@ -1,3271 +1,3278 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2009 Andreas Henriksson * Copyright (c) 2009-2012 Michihiro NAKAJIMA * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif /* #include */ /* See archive_platform.h */ #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include #ifdef HAVE_ZLIB_H #include #endif #include "archive.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_string.h" /* * An overview of ISO 9660 format: * * Each disk is laid out as follows: * * 32k reserved for private use * * Volume descriptor table. Each volume descriptor * is 2k and specifies basic format information. * The "Primary Volume Descriptor" (PVD) is defined by the * standard and should always be present; other volume * descriptors include various vendor-specific extensions. * * Files and directories. Each file/dir is specified by * an "extent" (starting sector and length in bytes). * Dirs are just files with directory records packed one * after another. The PVD contains a single dir entry * specifying the location of the root directory. Everything * else follows from there. * * This module works by first reading the volume descriptors, then * building a list of directory entries, sorted by starting * sector. At each step, I look for the earliest dir entry that * hasn't yet been read, seek forward to that location and read * that entry. If it's a dir, I slurp in the new dir entries and * add them to the heap; if it's a regular file, I return the * corresponding archive_entry and wait for the client to request * the file body. This strategy allows us to read most compliant * CDs with a single pass through the data, as required by libarchive. */ #define LOGICAL_BLOCK_SIZE 2048 #define SYSTEM_AREA_BLOCK 16 /* Structure of on-disk primary volume descriptor. */ #define PVD_type_offset 0 #define PVD_type_size 1 #define PVD_id_offset (PVD_type_offset + PVD_type_size) #define PVD_id_size 5 #define PVD_version_offset (PVD_id_offset + PVD_id_size) #define PVD_version_size 1 #define PVD_reserved1_offset (PVD_version_offset + PVD_version_size) #define PVD_reserved1_size 1 #define PVD_system_id_offset (PVD_reserved1_offset + PVD_reserved1_size) #define PVD_system_id_size 32 #define PVD_volume_id_offset (PVD_system_id_offset + PVD_system_id_size) #define PVD_volume_id_size 32 #define PVD_reserved2_offset (PVD_volume_id_offset + PVD_volume_id_size) #define PVD_reserved2_size 8 #define PVD_volume_space_size_offset (PVD_reserved2_offset + PVD_reserved2_size) #define PVD_volume_space_size_size 8 #define PVD_reserved3_offset (PVD_volume_space_size_offset + PVD_volume_space_size_size) #define PVD_reserved3_size 32 #define PVD_volume_set_size_offset (PVD_reserved3_offset + PVD_reserved3_size) #define PVD_volume_set_size_size 4 #define PVD_volume_sequence_number_offset (PVD_volume_set_size_offset + PVD_volume_set_size_size) #define PVD_volume_sequence_number_size 4 #define PVD_logical_block_size_offset (PVD_volume_sequence_number_offset + PVD_volume_sequence_number_size) #define PVD_logical_block_size_size 4 #define PVD_path_table_size_offset (PVD_logical_block_size_offset + PVD_logical_block_size_size) #define PVD_path_table_size_size 8 #define PVD_type_1_path_table_offset (PVD_path_table_size_offset + PVD_path_table_size_size) #define PVD_type_1_path_table_size 4 #define PVD_opt_type_1_path_table_offset (PVD_type_1_path_table_offset + PVD_type_1_path_table_size) #define PVD_opt_type_1_path_table_size 4 #define PVD_type_m_path_table_offset (PVD_opt_type_1_path_table_offset + PVD_opt_type_1_path_table_size) #define PVD_type_m_path_table_size 4 #define PVD_opt_type_m_path_table_offset (PVD_type_m_path_table_offset + PVD_type_m_path_table_size) #define PVD_opt_type_m_path_table_size 4 #define PVD_root_directory_record_offset (PVD_opt_type_m_path_table_offset + PVD_opt_type_m_path_table_size) #define PVD_root_directory_record_size 34 #define PVD_volume_set_id_offset (PVD_root_directory_record_offset + PVD_root_directory_record_size) #define PVD_volume_set_id_size 128 #define PVD_publisher_id_offset (PVD_volume_set_id_offset + PVD_volume_set_id_size) #define PVD_publisher_id_size 128 #define PVD_preparer_id_offset (PVD_publisher_id_offset + PVD_publisher_id_size) #define PVD_preparer_id_size 128 #define PVD_application_id_offset (PVD_preparer_id_offset + PVD_preparer_id_size) #define PVD_application_id_size 128 #define PVD_copyright_file_id_offset (PVD_application_id_offset + PVD_application_id_size) #define PVD_copyright_file_id_size 37 #define PVD_abstract_file_id_offset (PVD_copyright_file_id_offset + PVD_copyright_file_id_size) #define PVD_abstract_file_id_size 37 #define PVD_bibliographic_file_id_offset (PVD_abstract_file_id_offset + PVD_abstract_file_id_size) #define PVD_bibliographic_file_id_size 37 #define PVD_creation_date_offset (PVD_bibliographic_file_id_offset + PVD_bibliographic_file_id_size) #define PVD_creation_date_size 17 #define PVD_modification_date_offset (PVD_creation_date_offset + PVD_creation_date_size) #define PVD_modification_date_size 17 #define PVD_expiration_date_offset (PVD_modification_date_offset + PVD_modification_date_size) #define PVD_expiration_date_size 17 #define PVD_effective_date_offset (PVD_expiration_date_offset + PVD_expiration_date_size) #define PVD_effective_date_size 17 #define PVD_file_structure_version_offset (PVD_effective_date_offset + PVD_effective_date_size) #define PVD_file_structure_version_size 1 #define PVD_reserved4_offset (PVD_file_structure_version_offset + PVD_file_structure_version_size) #define PVD_reserved4_size 1 #define PVD_application_data_offset (PVD_reserved4_offset + PVD_reserved4_size) #define PVD_application_data_size 512 #define PVD_reserved5_offset (PVD_application_data_offset + PVD_application_data_size) #define PVD_reserved5_size (2048 - PVD_reserved5_offset) /* TODO: It would make future maintenance easier to just hardcode the * above values. In particular, ECMA119 states the offsets as part of * the standard. That would eliminate the need for the following check.*/ #if PVD_reserved5_offset != 1395 #error PVD offset and size definitions are wrong. #endif /* Structure of optional on-disk supplementary volume descriptor. */ #define SVD_type_offset 0 #define SVD_type_size 1 #define SVD_id_offset (SVD_type_offset + SVD_type_size) #define SVD_id_size 5 #define SVD_version_offset (SVD_id_offset + SVD_id_size) #define SVD_version_size 1 /* ... */ #define SVD_reserved1_offset 72 #define SVD_reserved1_size 8 #define SVD_volume_space_size_offset 80 #define SVD_volume_space_size_size 8 #define SVD_escape_sequences_offset (SVD_volume_space_size_offset + SVD_volume_space_size_size) #define SVD_escape_sequences_size 32 /* ... */ #define SVD_logical_block_size_offset 128 #define SVD_logical_block_size_size 4 #define SVD_type_L_path_table_offset 140 #define SVD_type_M_path_table_offset 148 /* ... */ #define SVD_root_directory_record_offset 156 #define SVD_root_directory_record_size 34 #define SVD_file_structure_version_offset 881 #define SVD_reserved2_offset 882 #define SVD_reserved2_size 1 #define SVD_reserved3_offset 1395 #define SVD_reserved3_size 653 /* ... */ /* FIXME: validate correctness of last SVD entry offset. */ /* Structure of an on-disk directory record. */ /* Note: ISO9660 stores each multi-byte integer twice, once in * each byte order. The sizes here are the size of just one * of the two integers. (This is why the offset of a field isn't * the same as the offset+size of the previous field.) */ #define DR_length_offset 0 #define DR_length_size 1 #define DR_ext_attr_length_offset 1 #define DR_ext_attr_length_size 1 #define DR_extent_offset 2 #define DR_extent_size 4 #define DR_size_offset 10 #define DR_size_size 4 #define DR_date_offset 18 #define DR_date_size 7 #define DR_flags_offset 25 #define DR_flags_size 1 #define DR_file_unit_size_offset 26 #define DR_file_unit_size_size 1 #define DR_interleave_offset 27 #define DR_interleave_size 1 #define DR_volume_sequence_number_offset 28 #define DR_volume_sequence_number_size 2 #define DR_name_len_offset 32 #define DR_name_len_size 1 #define DR_name_offset 33 #ifdef HAVE_ZLIB_H static const unsigned char zisofs_magic[8] = { 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07 }; struct zisofs { /* Set 1 if this file compressed by paged zlib */ int pz; int pz_log2_bs; /* Log2 of block size */ uint64_t pz_uncompressed_size; int initialized; unsigned char *uncompressed_buffer; size_t uncompressed_buffer_size; uint32_t pz_offset; unsigned char header[16]; size_t header_avail; int header_passed; unsigned char *block_pointers; size_t block_pointers_alloc; size_t block_pointers_size; size_t block_pointers_avail; size_t block_off; uint32_t block_avail; z_stream stream; int stream_valid; }; #else struct zisofs { /* Set 1 if this file compressed by paged zlib */ int pz; }; #endif struct content { uint64_t offset;/* Offset on disk. */ uint64_t size; /* File size in bytes. */ struct content *next; }; /* In-memory storage for a directory record. */ struct file_info { struct file_info *use_next; struct file_info *parent; struct file_info *next; struct file_info *re_next; int subdirs; uint64_t key; /* Heap Key. */ uint64_t offset; /* Offset on disk. */ uint64_t size; /* File size in bytes. */ uint32_t ce_offset; /* Offset of CE. */ uint32_t ce_size; /* Size of CE. */ char rr_moved; /* Flag to rr_moved. */ char rr_moved_has_re_only; char re; /* Having RRIP "RE" extension. */ char re_descendant; uint64_t cl_offset; /* Having RRIP "CL" extension. */ int birthtime_is_set; time_t birthtime; /* File created time. */ time_t mtime; /* File last modified time. */ time_t atime; /* File last accessed time. */ time_t ctime; /* File attribute change time. */ uint64_t rdev; /* Device number. */ mode_t mode; uid_t uid; gid_t gid; int64_t number; int nlinks; struct archive_string name; /* Pathname */ unsigned char *utf16be_name; size_t utf16be_bytes; char name_continues; /* Non-zero if name continues */ struct archive_string symlink; char symlink_continues; /* Non-zero if link continues */ /* Set 1 if this file compressed by paged zlib(zisofs) */ int pz; int pz_log2_bs; /* Log2 of block size */ uint64_t pz_uncompressed_size; /* Set 1 if this file is multi extent. */ int multi_extent; struct { struct content *first; struct content **last; } contents; struct { struct file_info *first; struct file_info **last; } rede_files; }; struct heap_queue { struct file_info **files; int allocated; int used; }; struct iso9660 { int magic; #define ISO9660_MAGIC 0x96609660 int opt_support_joliet; int opt_support_rockridge; struct archive_string pathname; char seenRockridge; /* Set true if RR extensions are used. */ char seenSUSP; /* Set true if SUSP is being used. */ char seenJoliet; unsigned char suspOffset; struct file_info *rr_moved; struct read_ce_queue { struct read_ce_req { uint64_t offset;/* Offset of CE on disk. */ struct file_info *file; } *reqs; int cnt; int allocated; } read_ce_req; int64_t previous_number; struct archive_string previous_pathname; struct file_info *use_files; struct heap_queue pending_files; struct { struct file_info *first; struct file_info **last; } cache_files; struct { struct file_info *first; struct file_info **last; } re_files; uint64_t current_position; ssize_t logical_block_size; uint64_t volume_size; /* Total size of volume in bytes. */ int32_t volume_block;/* Total size of volume in logical blocks. */ struct vd { int location; /* Location of Extent. */ uint32_t size; } primary, joliet; int64_t entry_sparse_offset; int64_t entry_bytes_remaining; size_t entry_bytes_unconsumed; struct zisofs entry_zisofs; struct content *entry_content; struct archive_string_conv *sconv_utf16be; /* * Buffers for a full pathname in UTF-16BE in Joliet extensions. */ #define UTF16_NAME_MAX 1024 unsigned char *utf16be_path; size_t utf16be_path_len; unsigned char *utf16be_previous_path; size_t utf16be_previous_path_len; /* Null buffer used in bidder to improve its performance. */ unsigned char null[2048]; }; static int archive_read_format_iso9660_bid(struct archive_read *, int); static int archive_read_format_iso9660_options(struct archive_read *, const char *, const char *); static int archive_read_format_iso9660_cleanup(struct archive_read *); static int archive_read_format_iso9660_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_iso9660_read_data_skip(struct archive_read *); static int archive_read_format_iso9660_read_header(struct archive_read *, struct archive_entry *); static const char *build_pathname(struct archive_string *, struct file_info *, int); static int build_pathname_utf16be(unsigned char *, size_t, size_t *, struct file_info *); #if DEBUG static void dump_isodirrec(FILE *, const unsigned char *isodirrec); #endif static time_t time_from_tm(struct tm *); static time_t isodate17(const unsigned char *); static time_t isodate7(const unsigned char *); static int isBootRecord(struct iso9660 *, const unsigned char *); static int isVolumePartition(struct iso9660 *, const unsigned char *); static int isVDSetTerminator(struct iso9660 *, const unsigned char *); static int isJolietSVD(struct iso9660 *, const unsigned char *); static int isSVD(struct iso9660 *, const unsigned char *); static int isEVD(struct iso9660 *, const unsigned char *); static int isPVD(struct iso9660 *, const unsigned char *); static int next_cache_entry(struct archive_read *, struct iso9660 *, struct file_info **); static int next_entry_seek(struct archive_read *, struct iso9660 *, struct file_info **); static struct file_info * parse_file_info(struct archive_read *a, struct file_info *parent, const unsigned char *isodirrec, size_t reclen); static int parse_rockridge(struct archive_read *a, struct file_info *file, const unsigned char *start, const unsigned char *end); static int register_CE(struct archive_read *a, int32_t location, struct file_info *file); static int read_CE(struct archive_read *a, struct iso9660 *iso9660); static void parse_rockridge_NM1(struct file_info *, const unsigned char *, int); static void parse_rockridge_SL1(struct file_info *, const unsigned char *, int); static void parse_rockridge_TF1(struct file_info *, const unsigned char *, int); static void parse_rockridge_ZF1(struct file_info *, const unsigned char *, int); static void register_file(struct iso9660 *, struct file_info *); static void release_files(struct iso9660 *); static unsigned toi(const void *p, int n); static inline void re_add_entry(struct iso9660 *, struct file_info *); static inline struct file_info * re_get_entry(struct iso9660 *); static inline int rede_add_entry(struct file_info *); static inline struct file_info * rede_get_entry(struct file_info *); static inline void cache_add_entry(struct iso9660 *iso9660, struct file_info *file); static inline struct file_info *cache_get_entry(struct iso9660 *iso9660); static int heap_add_entry(struct archive_read *a, struct heap_queue *heap, struct file_info *file, uint64_t key); static struct file_info *heap_get_entry(struct heap_queue *heap); #define add_entry(arch, iso9660, file) \ heap_add_entry(arch, &((iso9660)->pending_files), file, file->offset) #define next_entry(iso9660) \ heap_get_entry(&((iso9660)->pending_files)) int archive_read_support_format_iso9660(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct iso9660 *iso9660; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_iso9660"); iso9660 = (struct iso9660 *)calloc(1, sizeof(*iso9660)); if (iso9660 == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate iso9660 data"); return (ARCHIVE_FATAL); } iso9660->magic = ISO9660_MAGIC; iso9660->cache_files.first = NULL; iso9660->cache_files.last = &(iso9660->cache_files.first); iso9660->re_files.first = NULL; iso9660->re_files.last = &(iso9660->re_files.first); /* Enable to support Joliet extensions by default. */ iso9660->opt_support_joliet = 1; /* Enable to support Rock Ridge extensions by default. */ iso9660->opt_support_rockridge = 1; r = __archive_read_register_format(a, iso9660, "iso9660", archive_read_format_iso9660_bid, archive_read_format_iso9660_options, archive_read_format_iso9660_read_header, archive_read_format_iso9660_read_data, archive_read_format_iso9660_read_data_skip, NULL, archive_read_format_iso9660_cleanup, NULL, NULL); if (r != ARCHIVE_OK) { free(iso9660); return (r); } return (ARCHIVE_OK); } static int archive_read_format_iso9660_bid(struct archive_read *a, int best_bid) { struct iso9660 *iso9660; ssize_t bytes_read; const unsigned char *p; int seenTerminator; /* If there's already a better bid than we can ever make, don't bother testing. */ if (best_bid > 48) return (-1); iso9660 = (struct iso9660 *)(a->format->data); /* * Skip the first 32k (reserved area) and get the first * 8 sectors of the volume descriptor table. Of course, * if the I/O layer gives us more, we'll take it. */ #define RESERVED_AREA (SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE) p = __archive_read_ahead(a, RESERVED_AREA + 8 * LOGICAL_BLOCK_SIZE, &bytes_read); if (p == NULL) return (-1); /* Skip the reserved area. */ bytes_read -= RESERVED_AREA; p += RESERVED_AREA; /* Check each volume descriptor. */ seenTerminator = 0; for (; bytes_read > LOGICAL_BLOCK_SIZE; bytes_read -= LOGICAL_BLOCK_SIZE, p += LOGICAL_BLOCK_SIZE) { /* Do not handle undefined Volume Descriptor Type. */ if (p[0] >= 4 && p[0] <= 254) return (0); /* Standard Identifier must be "CD001" */ if (memcmp(p + 1, "CD001", 5) != 0) return (0); if (isPVD(iso9660, p)) continue; if (!iso9660->joliet.location) { if (isJolietSVD(iso9660, p)) continue; } if (isBootRecord(iso9660, p)) continue; if (isEVD(iso9660, p)) continue; if (isSVD(iso9660, p)) continue; if (isVolumePartition(iso9660, p)) continue; if (isVDSetTerminator(iso9660, p)) { seenTerminator = 1; break; } return (0); } /* * ISO 9660 format must have Primary Volume Descriptor and * Volume Descriptor Set Terminator. */ if (seenTerminator && iso9660->primary.location > 16) return (48); /* We didn't find a valid PVD; return a bid of zero. */ return (0); } static int archive_read_format_iso9660_options(struct archive_read *a, const char *key, const char *val) { struct iso9660 *iso9660; iso9660 = (struct iso9660 *)(a->format->data); if (strcmp(key, "joliet") == 0) { if (val == NULL || strcmp(val, "off") == 0 || strcmp(val, "ignore") == 0 || strcmp(val, "disable") == 0 || strcmp(val, "0") == 0) iso9660->opt_support_joliet = 0; else iso9660->opt_support_joliet = 1; return (ARCHIVE_OK); } if (strcmp(key, "rockridge") == 0 || strcmp(key, "Rockridge") == 0) { iso9660->opt_support_rockridge = val != NULL; return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int isNull(struct iso9660 *iso9660, const unsigned char *h, unsigned offset, unsigned bytes) { while (bytes >= sizeof(iso9660->null)) { if (!memcmp(iso9660->null, h + offset, sizeof(iso9660->null))) return (0); offset += sizeof(iso9660->null); bytes -= sizeof(iso9660->null); } if (bytes) return memcmp(iso9660->null, h + offset, bytes) == 0; else return (1); } static int isBootRecord(struct iso9660 *iso9660, const unsigned char *h) { (void)iso9660; /* UNUSED */ /* Type of the Volume Descriptor Boot Record must be 0. */ if (h[0] != 0) return (0); /* Volume Descriptor Version must be 1. */ if (h[6] != 1) return (0); return (1); } static int isVolumePartition(struct iso9660 *iso9660, const unsigned char *h) { int32_t location; /* Type of the Volume Partition Descriptor must be 3. */ if (h[0] != 3) return (0); /* Volume Descriptor Version must be 1. */ if (h[6] != 1) return (0); /* Unused Field */ if (h[7] != 0) return (0); location = archive_le32dec(h + 72); if (location <= SYSTEM_AREA_BLOCK || location >= iso9660->volume_block) return (0); if ((uint32_t)location != archive_be32dec(h + 76)) return (0); return (1); } static int isVDSetTerminator(struct iso9660 *iso9660, const unsigned char *h) { (void)iso9660; /* UNUSED */ /* Type of the Volume Descriptor Set Terminator must be 255. */ if (h[0] != 255) return (0); /* Volume Descriptor Version must be 1. */ if (h[6] != 1) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, 7, 2048-7)) return (0); return (1); } static int isJolietSVD(struct iso9660 *iso9660, const unsigned char *h) { const unsigned char *p; ssize_t logical_block_size; int32_t volume_block; /* Check if current sector is a kind of Supplementary Volume * Descriptor. */ if (!isSVD(iso9660, h)) return (0); /* FIXME: do more validations according to joliet spec. */ /* check if this SVD contains joliet extension! */ p = h + SVD_escape_sequences_offset; /* N.B. Joliet spec says p[1] == '\\', but.... */ if (p[0] == '%' && p[1] == '/') { int level = 0; if (p[2] == '@') level = 1; else if (p[2] == 'C') level = 2; else if (p[2] == 'E') level = 3; else /* not joliet */ return (0); iso9660->seenJoliet = level; } else /* not joliet */ return (0); logical_block_size = archive_le16dec(h + SVD_logical_block_size_offset); volume_block = archive_le32dec(h + SVD_volume_space_size_offset); iso9660->logical_block_size = logical_block_size; iso9660->volume_block = volume_block; iso9660->volume_size = logical_block_size * (uint64_t)volume_block; /* Read Root Directory Record in Volume Descriptor. */ p = h + SVD_root_directory_record_offset; iso9660->joliet.location = archive_le32dec(p + DR_extent_offset); iso9660->joliet.size = archive_le32dec(p + DR_size_offset); return (48); } static int isSVD(struct iso9660 *iso9660, const unsigned char *h) { const unsigned char *p; ssize_t logical_block_size; int32_t volume_block; int32_t location; (void)iso9660; /* UNUSED */ /* Type 2 means it's a SVD. */ if (h[SVD_type_offset] != 2) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, SVD_reserved1_offset, SVD_reserved1_size)) return (0); if (!isNull(iso9660, h, SVD_reserved2_offset, SVD_reserved2_size)) return (0); if (!isNull(iso9660, h, SVD_reserved3_offset, SVD_reserved3_size)) return (0); /* File structure version must be 1 for ISO9660/ECMA119. */ if (h[SVD_file_structure_version_offset] != 1) return (0); logical_block_size = archive_le16dec(h + SVD_logical_block_size_offset); if (logical_block_size <= 0) return (0); volume_block = archive_le32dec(h + SVD_volume_space_size_offset); if (volume_block <= SYSTEM_AREA_BLOCK+4) return (0); /* Location of Occurrence of Type L Path Table must be * available location, * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */ location = archive_le32dec(h+SVD_type_L_path_table_offset); if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block) return (0); /* The Type M Path Table must be at a valid location (WinISO * and probably other programs omit this, so we allow zero) * * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */ location = archive_be32dec(h+SVD_type_M_path_table_offset); if ((location > 0 && location < SYSTEM_AREA_BLOCK+2) || location >= volume_block) return (0); /* Read Root Directory Record in Volume Descriptor. */ p = h + SVD_root_directory_record_offset; if (p[DR_length_offset] != 34) return (0); return (48); } static int isEVD(struct iso9660 *iso9660, const unsigned char *h) { const unsigned char *p; ssize_t logical_block_size; int32_t volume_block; int32_t location; (void)iso9660; /* UNUSED */ /* Type of the Enhanced Volume Descriptor must be 2. */ if (h[PVD_type_offset] != 2) return (0); /* EVD version must be 2. */ if (h[PVD_version_offset] != 2) return (0); /* Reserved field must be 0. */ if (h[PVD_reserved1_offset] != 0) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size)) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size)) return (0); /* Logical block size must be > 0. */ /* I've looked at Ecma 119 and can't find any stronger * restriction on this field. */ logical_block_size = archive_le16dec(h + PVD_logical_block_size_offset); if (logical_block_size <= 0) return (0); volume_block = archive_le32dec(h + PVD_volume_space_size_offset); if (volume_block <= SYSTEM_AREA_BLOCK+4) return (0); /* File structure version must be 2 for ISO9660:1999. */ if (h[PVD_file_structure_version_offset] != 2) return (0); /* Location of Occurrence of Type L Path Table must be * available location, * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */ location = archive_le32dec(h+PVD_type_1_path_table_offset); if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block) return (0); /* Location of Occurrence of Type M Path Table must be * available location, * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */ location = archive_be32dec(h+PVD_type_m_path_table_offset); if ((location > 0 && location < SYSTEM_AREA_BLOCK+2) || location >= volume_block) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, PVD_reserved4_offset, PVD_reserved4_size)) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size)) return (0); /* Read Root Directory Record in Volume Descriptor. */ p = h + PVD_root_directory_record_offset; if (p[DR_length_offset] != 34) return (0); return (48); } static int isPVD(struct iso9660 *iso9660, const unsigned char *h) { const unsigned char *p; ssize_t logical_block_size; int32_t volume_block; int32_t location; int i; /* Type of the Primary Volume Descriptor must be 1. */ if (h[PVD_type_offset] != 1) return (0); /* PVD version must be 1. */ if (h[PVD_version_offset] != 1) return (0); /* Reserved field must be 0. */ if (h[PVD_reserved1_offset] != 0) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size)) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size)) return (0); /* Logical block size must be > 0. */ /* I've looked at Ecma 119 and can't find any stronger * restriction on this field. */ logical_block_size = archive_le16dec(h + PVD_logical_block_size_offset); if (logical_block_size <= 0) return (0); volume_block = archive_le32dec(h + PVD_volume_space_size_offset); if (volume_block <= SYSTEM_AREA_BLOCK+4) return (0); /* File structure version must be 1 for ISO9660/ECMA119. */ if (h[PVD_file_structure_version_offset] != 1) return (0); /* Location of Occurrence of Type L Path Table must be * available location, * > SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */ location = archive_le32dec(h+PVD_type_1_path_table_offset); if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block) return (0); /* The Type M Path Table must also be at a valid location * (although ECMA 119 requires a Type M Path Table, WinISO and * probably other programs omit it, so we permit a zero here) * * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */ location = archive_be32dec(h+PVD_type_m_path_table_offset); if ((location > 0 && location < SYSTEM_AREA_BLOCK+2) || location >= volume_block) return (0); /* Reserved field must be 0. */ /* But accept NetBSD/FreeBSD "makefs" images with 0x20 here. */ for (i = 0; i < PVD_reserved4_size; ++i) if (h[PVD_reserved4_offset + i] != 0 && h[PVD_reserved4_offset + i] != 0x20) return (0); /* Reserved field must be 0. */ if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size)) return (0); /* XXX TODO: Check other values for sanity; reject more * malformed PVDs. XXX */ /* Read Root Directory Record in Volume Descriptor. */ p = h + PVD_root_directory_record_offset; if (p[DR_length_offset] != 34) return (0); if (!iso9660->primary.location) { iso9660->logical_block_size = logical_block_size; iso9660->volume_block = volume_block; iso9660->volume_size = logical_block_size * (uint64_t)volume_block; iso9660->primary.location = archive_le32dec(p + DR_extent_offset); iso9660->primary.size = archive_le32dec(p + DR_size_offset); } return (48); } static int read_children(struct archive_read *a, struct file_info *parent) { struct iso9660 *iso9660; const unsigned char *b, *p; struct file_info *multi; size_t step, skip_size; iso9660 = (struct iso9660 *)(a->format->data); /* flush any remaining bytes from the last round to ensure * we're positioned */ if (iso9660->entry_bytes_unconsumed) { __archive_read_consume(a, iso9660->entry_bytes_unconsumed); iso9660->entry_bytes_unconsumed = 0; } if (iso9660->current_position > parent->offset) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignoring out-of-order directory (%s) %jd > %jd", parent->name.s, (intmax_t)iso9660->current_position, (intmax_t)parent->offset); return (ARCHIVE_WARN); } if (parent->offset + parent->size > iso9660->volume_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Directory is beyond end-of-media: %s", parent->name.s); return (ARCHIVE_WARN); } if (iso9660->current_position < parent->offset) { int64_t skipsize; skipsize = parent->offset - iso9660->current_position; skipsize = __archive_read_consume(a, skipsize); if (skipsize < 0) return ((int)skipsize); iso9660->current_position = parent->offset; } step = (size_t)(((parent->size + iso9660->logical_block_size -1) / iso9660->logical_block_size) * iso9660->logical_block_size); b = __archive_read_ahead(a, step, NULL); if (b == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to read full block when scanning " "ISO9660 directory list"); return (ARCHIVE_FATAL); } iso9660->current_position += step; multi = NULL; skip_size = step; while (step) { p = b; b += iso9660->logical_block_size; step -= iso9660->logical_block_size; for (; *p != 0 && p < b && p + *p <= b; p += *p) { struct file_info *child; /* N.B.: these special directory identifiers * are 8 bit "values" even on a * Joliet CD with UCS-2 (16bit) encoding. */ /* Skip '.' entry. */ if (*(p + DR_name_len_offset) == 1 && *(p + DR_name_offset) == '\0') continue; /* Skip '..' entry. */ if (*(p + DR_name_len_offset) == 1 && *(p + DR_name_offset) == '\001') continue; child = parse_file_info(a, parent, p, b - p); if (child == NULL) { __archive_read_consume(a, skip_size); return (ARCHIVE_FATAL); } if (child->cl_offset == 0 && (child->multi_extent || multi != NULL)) { struct content *con; if (multi == NULL) { multi = child; multi->contents.first = NULL; multi->contents.last = &(multi->contents.first); } con = malloc(sizeof(struct content)); if (con == NULL) { archive_set_error( &a->archive, ENOMEM, "No memory for multi extent"); __archive_read_consume(a, skip_size); return (ARCHIVE_FATAL); } con->offset = child->offset; con->size = child->size; con->next = NULL; *multi->contents.last = con; multi->contents.last = &(con->next); if (multi == child) { if (add_entry(a, iso9660, child) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else { multi->size += child->size; if (!child->multi_extent) multi = NULL; } } else if (add_entry(a, iso9660, child) != ARCHIVE_OK) return (ARCHIVE_FATAL); } } __archive_read_consume(a, skip_size); /* Read data which recorded by RRIP "CE" extension. */ if (read_CE(a, iso9660) != ARCHIVE_OK) return (ARCHIVE_FATAL); return (ARCHIVE_OK); } static int choose_volume(struct archive_read *a, struct iso9660 *iso9660) { struct file_info *file; int64_t skipsize; struct vd *vd; const void *block; char seenJoliet; vd = &(iso9660->primary); if (!iso9660->opt_support_joliet) iso9660->seenJoliet = 0; if (iso9660->seenJoliet && vd->location > iso9660->joliet.location) /* This condition is unlikely; by way of caution. */ vd = &(iso9660->joliet); skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location; skipsize = __archive_read_consume(a, skipsize); if (skipsize < 0) return ((int)skipsize); iso9660->current_position = skipsize; block = __archive_read_ahead(a, vd->size, NULL); if (block == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to read full block when scanning " "ISO9660 directory list"); return (ARCHIVE_FATAL); } /* * While reading Root Directory, flag seenJoliet must be zero to * avoid converting special name 0x00(Current Directory) and * next byte to UCS2. */ seenJoliet = iso9660->seenJoliet;/* Save flag. */ iso9660->seenJoliet = 0; file = parse_file_info(a, NULL, block, vd->size); if (file == NULL) return (ARCHIVE_FATAL); iso9660->seenJoliet = seenJoliet; /* * If the iso image has both RockRidge and Joliet, we preferentially * use RockRidge Extensions rather than Joliet ones. */ if (vd == &(iso9660->primary) && iso9660->seenRockridge && iso9660->seenJoliet) iso9660->seenJoliet = 0; if (vd == &(iso9660->primary) && !iso9660->seenRockridge && iso9660->seenJoliet) { /* Switch reading data from primary to joliet. */ vd = &(iso9660->joliet); skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location; skipsize -= iso9660->current_position; skipsize = __archive_read_consume(a, skipsize); if (skipsize < 0) return ((int)skipsize); iso9660->current_position += skipsize; block = __archive_read_ahead(a, vd->size, NULL); if (block == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to read full block when scanning " "ISO9660 directory list"); return (ARCHIVE_FATAL); } iso9660->seenJoliet = 0; file = parse_file_info(a, NULL, block, vd->size); if (file == NULL) return (ARCHIVE_FATAL); iso9660->seenJoliet = seenJoliet; } /* Store the root directory in the pending list. */ if (add_entry(a, iso9660, file) != ARCHIVE_OK) return (ARCHIVE_FATAL); if (iso9660->seenRockridge) { a->archive.archive_format = ARCHIVE_FORMAT_ISO9660_ROCKRIDGE; a->archive.archive_format_name = "ISO9660 with Rockridge extensions"; } return (ARCHIVE_OK); } static int archive_read_format_iso9660_read_header(struct archive_read *a, struct archive_entry *entry) { struct iso9660 *iso9660; struct file_info *file; int r, rd_r = ARCHIVE_OK; iso9660 = (struct iso9660 *)(a->format->data); if (!a->archive.archive_format) { a->archive.archive_format = ARCHIVE_FORMAT_ISO9660; a->archive.archive_format_name = "ISO9660"; } if (iso9660->current_position == 0) { r = choose_volume(a, iso9660); if (r != ARCHIVE_OK) return (r); } file = NULL;/* Eliminate a warning. */ /* Get the next entry that appears after the current offset. */ r = next_entry_seek(a, iso9660, &file); if (r != ARCHIVE_OK) return (r); if (iso9660->seenJoliet) { /* * Convert UTF-16BE of a filename to local locale MBS * and store the result into a filename field. */ if (iso9660->sconv_utf16be == NULL) { iso9660->sconv_utf16be = archive_string_conversion_from_charset( &(a->archive), "UTF-16BE", 1); if (iso9660->sconv_utf16be == NULL) /* Couldn't allocate memory */ return (ARCHIVE_FATAL); } if (iso9660->utf16be_path == NULL) { iso9660->utf16be_path = malloc(UTF16_NAME_MAX); if (iso9660->utf16be_path == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } } if (iso9660->utf16be_previous_path == NULL) { iso9660->utf16be_previous_path = malloc(UTF16_NAME_MAX); if (iso9660->utf16be_previous_path == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory"); return (ARCHIVE_FATAL); } } iso9660->utf16be_path_len = 0; if (build_pathname_utf16be(iso9660->utf16be_path, UTF16_NAME_MAX, &(iso9660->utf16be_path_len), file) != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname is too long"); return (ARCHIVE_FATAL); } r = archive_entry_copy_pathname_l(entry, (const char *)iso9660->utf16be_path, iso9660->utf16be_path_len, iso9660->sconv_utf16be); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "No memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name( iso9660->sconv_utf16be)); rd_r = ARCHIVE_WARN; } } else { const char *path = build_pathname(&iso9660->pathname, file, 0); if (path == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname is too long"); return (ARCHIVE_FATAL); } else { archive_string_empty(&iso9660->pathname); archive_entry_set_pathname(entry, path); } } iso9660->entry_bytes_remaining = file->size; /* Offset for sparse-file-aware clients. */ iso9660->entry_sparse_offset = 0; if (file->offset + file->size > iso9660->volume_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "File is beyond end-of-media: %s", archive_entry_pathname(entry)); iso9660->entry_bytes_remaining = 0; return (ARCHIVE_WARN); } /* Set up the entry structure with information about this entry. */ archive_entry_set_mode(entry, file->mode); archive_entry_set_uid(entry, file->uid); archive_entry_set_gid(entry, file->gid); archive_entry_set_nlink(entry, file->nlinks); if (file->birthtime_is_set) archive_entry_set_birthtime(entry, file->birthtime, 0); else archive_entry_unset_birthtime(entry); archive_entry_set_mtime(entry, file->mtime, 0); archive_entry_set_ctime(entry, file->ctime, 0); archive_entry_set_atime(entry, file->atime, 0); /* N.B.: Rock Ridge supports 64-bit device numbers. */ archive_entry_set_rdev(entry, (dev_t)file->rdev); archive_entry_set_size(entry, iso9660->entry_bytes_remaining); if (file->symlink.s != NULL) archive_entry_copy_symlink(entry, file->symlink.s); /* Note: If the input isn't seekable, we can't rewind to * return the same body again, so if the next entry refers to * the same data, we have to return it as a hardlink to the * original entry. */ if (file->number != -1 && file->number == iso9660->previous_number) { if (iso9660->seenJoliet) { r = archive_entry_copy_hardlink_l(entry, (const char *)iso9660->utf16be_previous_path, iso9660->utf16be_previous_path_len, iso9660->sconv_utf16be); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "No memory for Linkname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Linkname cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name( iso9660->sconv_utf16be)); rd_r = ARCHIVE_WARN; } } else archive_entry_set_hardlink(entry, iso9660->previous_pathname.s); archive_entry_unset_size(entry); iso9660->entry_bytes_remaining = 0; return (rd_r); } if ((file->mode & AE_IFMT) != AE_IFDIR && file->offset < iso9660->current_position) { int64_t r64; r64 = __archive_read_seek(a, file->offset, SEEK_SET); if (r64 != (int64_t)file->offset) { /* We can't seek backwards to extract it, so issue * a warning. Note that this can only happen if * this entry was added to the heap after we passed * this offset, that is, only if the directory * mentioning this entry is later than the body of * the entry. Such layouts are very unusual; most * ISO9660 writers lay out and record all directory * information first, then store all file bodies. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignoring out-of-order file @%jx (%s) %jd < %jd", (intmax_t)file->number, iso9660->pathname.s, (intmax_t)file->offset, (intmax_t)iso9660->current_position); iso9660->entry_bytes_remaining = 0; return (ARCHIVE_WARN); } iso9660->current_position = (uint64_t)r64; } /* Initialize zisofs variables. */ iso9660->entry_zisofs.pz = file->pz; if (file->pz) { #ifdef HAVE_ZLIB_H struct zisofs *zisofs; zisofs = &iso9660->entry_zisofs; zisofs->initialized = 0; zisofs->pz_log2_bs = file->pz_log2_bs; zisofs->pz_uncompressed_size = file->pz_uncompressed_size; zisofs->pz_offset = 0; zisofs->header_avail = 0; zisofs->header_passed = 0; zisofs->block_pointers_avail = 0; #endif archive_entry_set_size(entry, file->pz_uncompressed_size); } iso9660->previous_number = file->number; if (iso9660->seenJoliet) { memcpy(iso9660->utf16be_previous_path, iso9660->utf16be_path, iso9660->utf16be_path_len); iso9660->utf16be_previous_path_len = iso9660->utf16be_path_len; } else archive_strcpy( &iso9660->previous_pathname, iso9660->pathname.s); /* Reset entry_bytes_remaining if the file is multi extent. */ iso9660->entry_content = file->contents.first; if (iso9660->entry_content != NULL) iso9660->entry_bytes_remaining = iso9660->entry_content->size; if (archive_entry_filetype(entry) == AE_IFDIR) { /* Overwrite nlinks by proper link number which is * calculated from number of sub directories. */ archive_entry_set_nlink(entry, 2 + file->subdirs); /* Directory data has been read completely. */ iso9660->entry_bytes_remaining = 0; } if (rd_r != ARCHIVE_OK) return (rd_r); return (ARCHIVE_OK); } static int archive_read_format_iso9660_read_data_skip(struct archive_read *a) { /* Because read_next_header always does an explicit skip * to the next entry, we don't need to do anything here. */ (void)a; /* UNUSED */ return (ARCHIVE_OK); } #ifdef HAVE_ZLIB_H static int zisofs_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct iso9660 *iso9660; struct zisofs *zisofs; const unsigned char *p; size_t avail; ssize_t bytes_read; size_t uncompressed_size; int r; iso9660 = (struct iso9660 *)(a->format->data); zisofs = &iso9660->entry_zisofs; p = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated zisofs file body"); return (ARCHIVE_FATAL); } if (bytes_read > iso9660->entry_bytes_remaining) bytes_read = (ssize_t)iso9660->entry_bytes_remaining; avail = bytes_read; uncompressed_size = 0; if (!zisofs->initialized) { size_t ceil, xsize; /* Allocate block pointers buffer. */ ceil = (size_t)((zisofs->pz_uncompressed_size + (((int64_t)1) << zisofs->pz_log2_bs) - 1) >> zisofs->pz_log2_bs); xsize = (ceil + 1) * 4; if (zisofs->block_pointers_alloc < xsize) { size_t alloc; if (zisofs->block_pointers != NULL) free(zisofs->block_pointers); alloc = ((xsize >> 10) + 1) << 10; zisofs->block_pointers = malloc(alloc); if (zisofs->block_pointers == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for zisofs decompression"); return (ARCHIVE_FATAL); } zisofs->block_pointers_alloc = alloc; } zisofs->block_pointers_size = xsize; /* Allocate uncompressed data buffer. */ xsize = (size_t)1UL << zisofs->pz_log2_bs; if (zisofs->uncompressed_buffer_size < xsize) { if (zisofs->uncompressed_buffer != NULL) free(zisofs->uncompressed_buffer); zisofs->uncompressed_buffer = malloc(xsize); if (zisofs->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for zisofs decompression"); return (ARCHIVE_FATAL); } } zisofs->uncompressed_buffer_size = xsize; /* * Read the file header, and check the magic code of zisofs. */ if (zisofs->header_avail < sizeof(zisofs->header)) { xsize = sizeof(zisofs->header) - zisofs->header_avail; if (avail < xsize) xsize = avail; memcpy(zisofs->header + zisofs->header_avail, p, xsize); zisofs->header_avail += xsize; avail -= xsize; p += xsize; } if (!zisofs->header_passed && zisofs->header_avail == sizeof(zisofs->header)) { int err = 0; if (memcmp(zisofs->header, zisofs_magic, sizeof(zisofs_magic)) != 0) err = 1; if (archive_le32dec(zisofs->header + 8) != zisofs->pz_uncompressed_size) err = 1; if (zisofs->header[12] != 4) err = 1; if (zisofs->header[13] != zisofs->pz_log2_bs) err = 1; if (err) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs file body"); return (ARCHIVE_FATAL); } zisofs->header_passed = 1; } /* * Read block pointers. */ if (zisofs->header_passed && zisofs->block_pointers_avail < zisofs->block_pointers_size) { xsize = zisofs->block_pointers_size - zisofs->block_pointers_avail; if (avail < xsize) xsize = avail; memcpy(zisofs->block_pointers + zisofs->block_pointers_avail, p, xsize); zisofs->block_pointers_avail += xsize; avail -= xsize; p += xsize; if (zisofs->block_pointers_avail == zisofs->block_pointers_size) { /* We've got all block pointers and initialize * related variables. */ zisofs->block_off = 0; zisofs->block_avail = 0; /* Complete a initialization */ zisofs->initialized = 1; } } if (!zisofs->initialized) goto next_data; /* We need more data. */ } /* * Get block offsets from block pointers. */ if (zisofs->block_avail == 0) { uint32_t bst, bed; if (zisofs->block_off + 4 >= zisofs->block_pointers_size) { /* There isn't a pair of offsets. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs block pointers"); return (ARCHIVE_FATAL); } bst = archive_le32dec( zisofs->block_pointers + zisofs->block_off); if (bst != zisofs->pz_offset + (bytes_read - avail)) { /* TODO: Should we seek offset of current file * by bst ? */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs block pointers(cannot seek)"); return (ARCHIVE_FATAL); } bed = archive_le32dec( zisofs->block_pointers + zisofs->block_off + 4); if (bed < bst) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Illegal zisofs block pointers"); return (ARCHIVE_FATAL); } zisofs->block_avail = bed - bst; zisofs->block_off += 4; /* Initialize compression library for new block. */ if (zisofs->stream_valid) r = inflateReset(&zisofs->stream); else r = inflateInit(&zisofs->stream); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't initialize zisofs decompression."); return (ARCHIVE_FATAL); } zisofs->stream_valid = 1; zisofs->stream.total_in = 0; zisofs->stream.total_out = 0; } /* * Make uncompressed data. */ if (zisofs->block_avail == 0) { memset(zisofs->uncompressed_buffer, 0, zisofs->uncompressed_buffer_size); uncompressed_size = zisofs->uncompressed_buffer_size; } else { zisofs->stream.next_in = (Bytef *)(uintptr_t)(const void *)p; if (avail > zisofs->block_avail) zisofs->stream.avail_in = zisofs->block_avail; else zisofs->stream.avail_in = (uInt)avail; zisofs->stream.next_out = zisofs->uncompressed_buffer; zisofs->stream.avail_out = (uInt)zisofs->uncompressed_buffer_size; r = inflate(&zisofs->stream, 0); switch (r) { case Z_OK: /* Decompressor made some progress.*/ case Z_STREAM_END: /* Found end of stream. */ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "zisofs decompression failed (%d)", r); return (ARCHIVE_FATAL); } uncompressed_size = zisofs->uncompressed_buffer_size - zisofs->stream.avail_out; avail -= zisofs->stream.next_in - p; zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p); } next_data: bytes_read -= avail; *buff = zisofs->uncompressed_buffer; *size = uncompressed_size; *offset = iso9660->entry_sparse_offset; iso9660->entry_sparse_offset += uncompressed_size; iso9660->entry_bytes_remaining -= bytes_read; iso9660->current_position += bytes_read; zisofs->pz_offset += (uint32_t)bytes_read; iso9660->entry_bytes_unconsumed += bytes_read; return (ARCHIVE_OK); } #else /* HAVE_ZLIB_H */ static int zisofs_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { (void)buff;/* UNUSED */ (void)size;/* UNUSED */ (void)offset;/* UNUSED */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "zisofs is not supported on this platform."); return (ARCHIVE_FAILED); } #endif /* HAVE_ZLIB_H */ static int archive_read_format_iso9660_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { ssize_t bytes_read; struct iso9660 *iso9660; iso9660 = (struct iso9660 *)(a->format->data); if (iso9660->entry_bytes_unconsumed) { __archive_read_consume(a, iso9660->entry_bytes_unconsumed); iso9660->entry_bytes_unconsumed = 0; } if (iso9660->entry_bytes_remaining <= 0) { if (iso9660->entry_content != NULL) iso9660->entry_content = iso9660->entry_content->next; if (iso9660->entry_content == NULL) { *buff = NULL; *size = 0; *offset = iso9660->entry_sparse_offset; return (ARCHIVE_EOF); } /* Seek forward to the start of the entry. */ if (iso9660->current_position < iso9660->entry_content->offset) { int64_t step; step = iso9660->entry_content->offset - iso9660->current_position; step = __archive_read_consume(a, step); if (step < 0) return ((int)step); iso9660->current_position = iso9660->entry_content->offset; } if (iso9660->entry_content->offset < iso9660->current_position) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignoring out-of-order file (%s) %jd < %jd", iso9660->pathname.s, (intmax_t)iso9660->entry_content->offset, (intmax_t)iso9660->current_position); *buff = NULL; *size = 0; *offset = iso9660->entry_sparse_offset; return (ARCHIVE_WARN); } iso9660->entry_bytes_remaining = iso9660->entry_content->size; } if (iso9660->entry_zisofs.pz) return (zisofs_read_data(a, buff, size, offset)); *buff = __archive_read_ahead(a, 1, &bytes_read); if (bytes_read == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated input file"); if (*buff == NULL) return (ARCHIVE_FATAL); if (bytes_read > iso9660->entry_bytes_remaining) bytes_read = (ssize_t)iso9660->entry_bytes_remaining; *size = bytes_read; *offset = iso9660->entry_sparse_offset; iso9660->entry_sparse_offset += bytes_read; iso9660->entry_bytes_remaining -= bytes_read; iso9660->entry_bytes_unconsumed = bytes_read; iso9660->current_position += bytes_read; return (ARCHIVE_OK); } static int archive_read_format_iso9660_cleanup(struct archive_read *a) { struct iso9660 *iso9660; int r = ARCHIVE_OK; iso9660 = (struct iso9660 *)(a->format->data); release_files(iso9660); free(iso9660->read_ce_req.reqs); archive_string_free(&iso9660->pathname); archive_string_free(&iso9660->previous_pathname); - if (iso9660->pending_files.files) - free(iso9660->pending_files.files); + free(iso9660->pending_files.files); #ifdef HAVE_ZLIB_H free(iso9660->entry_zisofs.uncompressed_buffer); free(iso9660->entry_zisofs.block_pointers); if (iso9660->entry_zisofs.stream_valid) { if (inflateEnd(&iso9660->entry_zisofs.stream) != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up zlib decompressor"); r = ARCHIVE_FATAL; } } #endif free(iso9660->utf16be_path); free(iso9660->utf16be_previous_path); free(iso9660); (a->format->data) = NULL; return (r); } /* * This routine parses a single ISO directory record, makes sense * of any extensions, and stores the result in memory. */ static struct file_info * parse_file_info(struct archive_read *a, struct file_info *parent, const unsigned char *isodirrec, size_t reclen) { struct iso9660 *iso9660; struct file_info *file, *filep; size_t name_len; const unsigned char *rr_start, *rr_end; const unsigned char *p; size_t dr_len; uint64_t fsize, offset; int32_t location; int flags; iso9660 = (struct iso9660 *)(a->format->data); if (reclen != 0) dr_len = (size_t)isodirrec[DR_length_offset]; /* * Sanity check that reclen is not zero and dr_len is greater than * reclen but at least 34 */ if (reclen == 0 || reclen < dr_len || dr_len < 34) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid length of directory record"); return (NULL); } name_len = (size_t)isodirrec[DR_name_len_offset]; location = archive_le32dec(isodirrec + DR_extent_offset); fsize = toi(isodirrec + DR_size_offset, DR_size_size); /* Sanity check that name_len doesn't exceed dr_len. */ if (dr_len - 33 < name_len || name_len == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid length of file identifier"); return (NULL); } /* Sanity check that location doesn't exceed volume block. * Don't check lower limit of location; it's possibility * the location has negative value when file type is symbolic * link or file size is zero. As far as I know latest mkisofs * do that. */ if (location > 0 && (location + ((fsize + iso9660->logical_block_size -1) / iso9660->logical_block_size)) > (uint32_t)iso9660->volume_block) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid location of extent of file"); return (NULL); } /* Sanity check that location doesn't have a negative value * when the file is not empty. it's too large. */ if (fsize != 0 && location < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid location of extent of file"); return (NULL); } /* Sanity check that this entry does not create a cycle. */ offset = iso9660->logical_block_size * (uint64_t)location; for (filep = parent; filep != NULL; filep = filep->parent) { if (filep->offset == offset) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Directory structure contains loop"); return (NULL); } } /* Create a new file entry and copy data from the ISO dir record. */ file = (struct file_info *)calloc(1, sizeof(*file)); if (file == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for file entry"); return (NULL); } file->parent = parent; file->offset = offset; file->size = fsize; file->mtime = isodate7(isodirrec + DR_date_offset); file->ctime = file->atime = file->mtime; file->rede_files.first = NULL; file->rede_files.last = &(file->rede_files.first); p = isodirrec + DR_name_offset; /* Rockridge extensions (if any) follow name. Compute this * before fidgeting the name_len below. */ rr_start = p + name_len + (name_len & 1 ? 0 : 1); rr_end = isodirrec + dr_len; if (iso9660->seenJoliet) { /* Joliet names are max 64 chars (128 bytes) according to spec, * but genisoimage/mkisofs allows recording longer Joliet * names which are 103 UCS2 characters(206 bytes) by their * option '-joliet-long'. */ if (name_len > 206) name_len = 206; name_len &= ~1; /* trim trailing first version and dot from filename. * * Remember we were in UTF-16BE land! * SEPARATOR 1 (.) and SEPARATOR 2 (;) are both * 16 bits big endian characters on Joliet. * * TODO: sanitize filename? * Joliet allows any UCS-2 char except: * *, /, :, ;, ? and \. */ /* Chop off trailing ';1' from files. */ if (name_len > 4 && p[name_len-4] == 0 && p[name_len-3] == ';' && p[name_len-2] == 0 && p[name_len-1] == '1') name_len -= 4; #if 0 /* XXX: this somehow manages to strip of single-character file extensions, like '.c'. */ /* Chop off trailing '.' from filenames. */ if (name_len > 2 && p[name_len-2] == 0 && p[name_len-1] == '.') name_len -= 2; #endif if ((file->utf16be_name = malloc(name_len)) == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for file name"); goto fail; } memcpy(file->utf16be_name, p, name_len); file->utf16be_bytes = name_len; } else { /* Chop off trailing ';1' from files. */ if (name_len > 2 && p[name_len - 2] == ';' && p[name_len - 1] == '1') name_len -= 2; /* Chop off trailing '.' from filenames. */ if (name_len > 1 && p[name_len - 1] == '.') --name_len; archive_strncpy(&file->name, (const char *)p, name_len); } flags = isodirrec[DR_flags_offset]; if (flags & 0x02) file->mode = AE_IFDIR | 0700; else file->mode = AE_IFREG | 0400; if (flags & 0x80) file->multi_extent = 1; else file->multi_extent = 0; /* * Use a location for the file number, which is treated as an inode * number to find out hardlink target. If Rockridge extensions is * being used, the file number will be overwritten by FILE SERIAL * NUMBER of RRIP "PX" extension. * Note: Old mkisofs did not record that FILE SERIAL NUMBER * in ISO images. * Note2: xorriso set 0 to the location of a symlink file. */ if (file->size == 0 && location >= 0) { /* If file->size is zero, its location points wrong place, * and so we should not use it for the file number. * When the location has negative value, it can be used * for the file number. */ file->number = -1; /* Do not appear before any directory entries. */ file->offset = -1; } else file->number = (int64_t)(uint32_t)location; /* Rockridge extensions overwrite information from above. */ if (iso9660->opt_support_rockridge) { if (parent == NULL && rr_end - rr_start >= 7) { p = rr_start; if (memcmp(p, "SP\x07\x01\xbe\xef", 6) == 0) { /* * SP extension stores the suspOffset * (Number of bytes to skip between * filename and SUSP records.) * It is mandatory by the SUSP standard * (IEEE 1281). * * It allows SUSP to coexist with * non-SUSP uses of the System * Use Area by placing non-SUSP data * before SUSP data. * * SP extension must be in the root * directory entry, disable all SUSP * processing if not found. */ iso9660->suspOffset = p[6]; iso9660->seenSUSP = 1; rr_start += 7; } } if (iso9660->seenSUSP) { int r; file->name_continues = 0; file->symlink_continues = 0; rr_start += iso9660->suspOffset; r = parse_rockridge(a, file, rr_start, rr_end); if (r != ARCHIVE_OK) goto fail; /* * A file size of symbolic link files in ISO images * made by makefs is not zero and its location is * the same as those of next regular file. That is * the same as hard like file and it causes unexpected * error. */ if (file->size > 0 && (file->mode & AE_IFMT) == AE_IFLNK) { file->size = 0; file->number = -1; file->offset = -1; } } else /* If there isn't SUSP, disable parsing * rock ridge extensions. */ iso9660->opt_support_rockridge = 0; } file->nlinks = 1;/* Reset nlink. we'll calculate it later. */ /* Tell file's parent how many children that parent has. */ if (parent != NULL && (flags & 0x02)) parent->subdirs++; if (iso9660->seenRockridge) { if (parent != NULL && parent->parent == NULL && (flags & 0x02) && iso9660->rr_moved == NULL && file->name.s && (strcmp(file->name.s, "rr_moved") == 0 || strcmp(file->name.s, ".rr_moved") == 0)) { iso9660->rr_moved = file; file->rr_moved = 1; file->rr_moved_has_re_only = 1; file->re = 0; parent->subdirs--; } else if (file->re) { /* * Sanity check: file's parent is rr_moved. */ if (parent == NULL || parent->rr_moved == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge RE"); goto fail; } /* * Sanity check: file does not have "CL" extension. */ if (file->cl_offset) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge RE and CL"); goto fail; } /* * Sanity check: The file type must be a directory. */ if ((flags & 0x02) == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge RE"); goto fail; } } else if (parent != NULL && parent->rr_moved) file->rr_moved_has_re_only = 0; else if (parent != NULL && (flags & 0x02) && (parent->re || parent->re_descendant)) file->re_descendant = 1; if (file->cl_offset) { struct file_info *r; if (parent == NULL || parent->parent == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge CL"); goto fail; } /* * Sanity check: The file type must be a regular file. */ if ((flags & 0x02) != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge CL"); goto fail; } parent->subdirs++; /* Overwrite an offset and a number of this "CL" entry * to appear before other dirs. "+1" to those is to * make sure to appear after "RE" entry which this * "CL" entry should be connected with. */ file->offset = file->number = file->cl_offset + 1; /* * Sanity check: cl_offset does not point at its * the parents or itself. */ for (r = parent; r; r = r->parent) { if (r->offset == file->cl_offset) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge CL"); goto fail; } } if (file->cl_offset == file->offset || parent->rr_moved) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid Rockridge CL"); goto fail; } } } #if DEBUG /* DEBUGGING: Warn about attributes I don't yet fully support. */ if ((flags & ~0x02) != 0) { fprintf(stderr, "\n ** Unrecognized flag: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } else if (toi(isodirrec + DR_volume_sequence_number_offset, 2) != 1) { fprintf(stderr, "\n ** Unrecognized sequence number: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } else if (*(isodirrec + DR_file_unit_size_offset) != 0) { fprintf(stderr, "\n ** Unexpected file unit size: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } else if (*(isodirrec + DR_interleave_offset) != 0) { fprintf(stderr, "\n ** Unexpected interleave: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } else if (*(isodirrec + DR_ext_attr_length_offset) != 0) { fprintf(stderr, "\n ** Unexpected extended attribute length: "); dump_isodirrec(stderr, isodirrec); fprintf(stderr, "\n"); } #endif register_file(iso9660, file); return (file); fail: archive_string_free(&file->name); free(file); return (NULL); } static int parse_rockridge(struct archive_read *a, struct file_info *file, const unsigned char *p, const unsigned char *end) { struct iso9660 *iso9660; + int entry_seen = 0; iso9660 = (struct iso9660 *)(a->format->data); while (p + 4 <= end /* Enough space for another entry. */ && p[0] >= 'A' && p[0] <= 'Z' /* Sanity-check 1st char of name. */ && p[1] >= 'A' && p[1] <= 'Z' /* Sanity-check 2nd char of name. */ && p[2] >= 4 /* Sanity-check length. */ && p + p[2] <= end) { /* Sanity-check length. */ const unsigned char *data = p + 4; int data_length = p[2] - 4; int version = p[3]; switch(p[0]) { case 'C': if (p[1] == 'E') { if (version == 1 && data_length == 24) { /* * CE extension comprises: * 8 byte sector containing extension * 8 byte offset w/in above sector * 8 byte length of continuation */ int32_t location = archive_le32dec(data); file->ce_offset = archive_le32dec(data+8); file->ce_size = archive_le32dec(data+16); if (register_CE(a, location, file) != ARCHIVE_OK) return (ARCHIVE_FATAL); } } else if (p[1] == 'L') { if (version == 1 && data_length == 8) { file->cl_offset = (uint64_t) iso9660->logical_block_size * (uint64_t)archive_le32dec(data); iso9660->seenRockridge = 1; } } break; case 'N': if (p[1] == 'M') { if (version == 1) { parse_rockridge_NM1(file, data, data_length); iso9660->seenRockridge = 1; } } break; case 'P': /* * PD extension is padding; * contents are always ignored. * * PL extension won't appear; * contents are always ignored. */ if (p[1] == 'N') { if (version == 1 && data_length == 16) { file->rdev = toi(data,4); file->rdev <<= 32; file->rdev |= toi(data + 8, 4); iso9660->seenRockridge = 1; } } else if (p[1] == 'X') { /* * PX extension comprises: * 8 bytes for mode, * 8 bytes for nlinks, * 8 bytes for uid, * 8 bytes for gid, * 8 bytes for inode. */ if (version == 1) { if (data_length >= 8) file->mode = toi(data, 4); if (data_length >= 16) file->nlinks = toi(data + 8, 4); if (data_length >= 24) file->uid = toi(data + 16, 4); if (data_length >= 32) file->gid = toi(data + 24, 4); if (data_length >= 40) file->number = toi(data + 32, 4); iso9660->seenRockridge = 1; } } break; case 'R': if (p[1] == 'E' && version == 1) { file->re = 1; iso9660->seenRockridge = 1; } else if (p[1] == 'R' && version == 1) { /* * RR extension comprises: * one byte flag value * This extension is obsolete, * so contents are always ignored. */ } break; case 'S': if (p[1] == 'L') { if (version == 1) { parse_rockridge_SL1(file, data, data_length); iso9660->seenRockridge = 1; } } else if (p[1] == 'T' && data_length == 0 && version == 1) { /* * ST extension marks end of this * block of SUSP entries. * * It allows SUSP to coexist with * non-SUSP uses of the System * Use Area by placing non-SUSP data * after SUSP data. */ iso9660->seenSUSP = 0; iso9660->seenRockridge = 0; return (ARCHIVE_OK); } break; case 'T': if (p[1] == 'F') { if (version == 1) { parse_rockridge_TF1(file, data, data_length); iso9660->seenRockridge = 1; } } break; case 'Z': if (p[1] == 'F') { if (version == 1) parse_rockridge_ZF1(file, data, data_length); } break; default: break; } p += p[2]; + entry_seen = 1; } - return (ARCHIVE_OK); + + if (entry_seen) + return (ARCHIVE_OK); + else { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Tried to parse Rockridge extensions, but none found"); + return (ARCHIVE_WARN); + } } static int register_CE(struct archive_read *a, int32_t location, struct file_info *file) { struct iso9660 *iso9660; struct read_ce_queue *heap; struct read_ce_req *p; uint64_t offset, parent_offset; int hole, parent; iso9660 = (struct iso9660 *)(a->format->data); offset = ((uint64_t)location) * (uint64_t)iso9660->logical_block_size; if (((file->mode & AE_IFMT) == AE_IFREG && offset >= file->offset) || offset < iso9660->current_position || (((uint64_t)file->ce_offset) + file->ce_size) > (uint64_t)iso9660->logical_block_size || offset + file->ce_offset + file->ce_size > iso9660->volume_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid parameter in SUSP \"CE\" extension"); return (ARCHIVE_FATAL); } /* Expand our CE list as necessary. */ heap = &(iso9660->read_ce_req); if (heap->cnt >= heap->allocated) { int new_size; if (heap->allocated < 16) new_size = 16; else new_size = heap->allocated * 2; /* Overflow might keep us from growing the list. */ if (new_size <= heap->allocated) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } p = calloc(new_size, sizeof(p[0])); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } if (heap->reqs != NULL) { memcpy(p, heap->reqs, heap->cnt * sizeof(*p)); free(heap->reqs); } heap->reqs = p; heap->allocated = new_size; } /* * Start with hole at end, walk it up tree to find insertion point. */ hole = heap->cnt++; while (hole > 0) { parent = (hole - 1)/2; parent_offset = heap->reqs[parent].offset; if (offset >= parent_offset) { heap->reqs[hole].offset = offset; heap->reqs[hole].file = file; return (ARCHIVE_OK); } /* Move parent into hole <==> move hole up tree. */ heap->reqs[hole] = heap->reqs[parent]; hole = parent; } heap->reqs[0].offset = offset; heap->reqs[0].file = file; return (ARCHIVE_OK); } static void next_CE(struct read_ce_queue *heap) { uint64_t a_offset, b_offset, c_offset; int a, b, c; struct read_ce_req tmp; if (heap->cnt < 1) return; /* * Move the last item in the heap to the root of the tree */ heap->reqs[0] = heap->reqs[--(heap->cnt)]; /* * Rebalance the heap. */ a = 0; /* Starting element and its offset */ a_offset = heap->reqs[a].offset; for (;;) { b = a + a + 1; /* First child */ if (b >= heap->cnt) return; b_offset = heap->reqs[b].offset; c = b + 1; /* Use second child if it is smaller. */ if (c < heap->cnt) { c_offset = heap->reqs[c].offset; if (c_offset < b_offset) { b = c; b_offset = c_offset; } } if (a_offset <= b_offset) return; tmp = heap->reqs[a]; heap->reqs[a] = heap->reqs[b]; heap->reqs[b] = tmp; a = b; } } static int read_CE(struct archive_read *a, struct iso9660 *iso9660) { struct read_ce_queue *heap; const unsigned char *b, *p, *end; struct file_info *file; size_t step; int r; /* Read data which RRIP "CE" extension points. */ heap = &(iso9660->read_ce_req); step = iso9660->logical_block_size; while (heap->cnt && heap->reqs[0].offset == iso9660->current_position) { b = __archive_read_ahead(a, step, NULL); if (b == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to read full block when scanning " "ISO9660 directory list"); return (ARCHIVE_FATAL); } do { file = heap->reqs[0].file; if (file->ce_offset + file->ce_size > step) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed CE information"); return (ARCHIVE_FATAL); } p = b + file->ce_offset; end = p + file->ce_size; next_CE(heap); r = parse_rockridge(a, file, p, end); if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); } while (heap->cnt && heap->reqs[0].offset == iso9660->current_position); /* NOTE: Do not move this consume's code to front of * do-while loop. Registration of nested CE extension * might cause error because of current position. */ __archive_read_consume(a, step); iso9660->current_position += step; } return (ARCHIVE_OK); } static void parse_rockridge_NM1(struct file_info *file, const unsigned char *data, int data_length) { if (!file->name_continues) archive_string_empty(&file->name); file->name_continues = 0; if (data_length < 1) return; /* * NM version 1 extension comprises: * 1 byte flag, value is one of: * = 0: remainder is name * = 1: remainder is name, next NM entry continues name * = 2: "." * = 4: ".." * = 32: Implementation specific * All other values are reserved. */ switch(data[0]) { case 0: if (data_length < 2) return; archive_strncat(&file->name, (const char *)data + 1, data_length - 1); break; case 1: if (data_length < 2) return; archive_strncat(&file->name, (const char *)data + 1, data_length - 1); file->name_continues = 1; break; case 2: archive_strcat(&file->name, "."); break; case 4: archive_strcat(&file->name, ".."); break; default: return; } } static void parse_rockridge_TF1(struct file_info *file, const unsigned char *data, int data_length) { char flag; /* * TF extension comprises: * one byte flag * create time (optional) * modify time (optional) * access time (optional) * attribute time (optional) * Time format and presence of fields * is controlled by flag bits. */ if (data_length < 1) return; flag = data[0]; ++data; --data_length; if (flag & 0x80) { /* Use 17-byte time format. */ if ((flag & 1) && data_length >= 17) { /* Create time. */ file->birthtime_is_set = 1; file->birthtime = isodate17(data); data += 17; data_length -= 17; } if ((flag & 2) && data_length >= 17) { /* Modify time. */ file->mtime = isodate17(data); data += 17; data_length -= 17; } if ((flag & 4) && data_length >= 17) { /* Access time. */ file->atime = isodate17(data); data += 17; data_length -= 17; } if ((flag & 8) && data_length >= 17) { /* Attribute change time. */ file->ctime = isodate17(data); } } else { /* Use 7-byte time format. */ if ((flag & 1) && data_length >= 7) { /* Create time. */ file->birthtime_is_set = 1; file->birthtime = isodate7(data); data += 7; data_length -= 7; } if ((flag & 2) && data_length >= 7) { /* Modify time. */ file->mtime = isodate7(data); data += 7; data_length -= 7; } if ((flag & 4) && data_length >= 7) { /* Access time. */ file->atime = isodate7(data); data += 7; data_length -= 7; } if ((flag & 8) && data_length >= 7) { /* Attribute change time. */ file->ctime = isodate7(data); } } } static void parse_rockridge_SL1(struct file_info *file, const unsigned char *data, int data_length) { const char *separator = ""; if (!file->symlink_continues || file->symlink.length < 1) archive_string_empty(&file->symlink); file->symlink_continues = 0; /* * Defined flag values: * 0: This is the last SL record for this symbolic link * 1: this symbolic link field continues in next SL entry * All other values are reserved. */ if (data_length < 1) return; switch(*data) { case 0: break; case 1: file->symlink_continues = 1; break; default: return; } ++data; /* Skip flag byte. */ --data_length; /* * SL extension body stores "components". * Basically, this is a complicated way of storing * a POSIX path. It also interferes with using * symlinks for storing non-path data. * * Each component is 2 bytes (flag and length) * possibly followed by name data. */ while (data_length >= 2) { unsigned char flag = *data++; unsigned char nlen = *data++; data_length -= 2; archive_strcat(&file->symlink, separator); separator = "/"; switch(flag) { case 0: /* Usual case, this is text. */ if (data_length < nlen) return; archive_strncat(&file->symlink, (const char *)data, nlen); break; case 0x01: /* Text continues in next component. */ if (data_length < nlen) return; archive_strncat(&file->symlink, (const char *)data, nlen); separator = ""; break; case 0x02: /* Current dir. */ archive_strcat(&file->symlink, "."); break; case 0x04: /* Parent dir. */ archive_strcat(&file->symlink, ".."); break; case 0x08: /* Root of filesystem. */ archive_strcat(&file->symlink, "/"); separator = ""; break; case 0x10: /* Undefined (historically "volume root" */ archive_string_empty(&file->symlink); archive_strcat(&file->symlink, "ROOT"); break; case 0x20: /* Undefined (historically "hostname") */ archive_strcat(&file->symlink, "hostname"); break; default: /* TODO: issue a warning ? */ return; } data += nlen; data_length -= nlen; } } static void parse_rockridge_ZF1(struct file_info *file, const unsigned char *data, int data_length) { if (data[0] == 0x70 && data[1] == 0x7a && data_length == 12) { /* paged zlib */ file->pz = 1; file->pz_log2_bs = data[3]; file->pz_uncompressed_size = archive_le32dec(&data[4]); } } static void register_file(struct iso9660 *iso9660, struct file_info *file) { file->use_next = iso9660->use_files; iso9660->use_files = file; } static void release_files(struct iso9660 *iso9660) { struct content *con, *connext; struct file_info *file; file = iso9660->use_files; while (file != NULL) { struct file_info *next = file->use_next; archive_string_free(&file->name); archive_string_free(&file->symlink); free(file->utf16be_name); con = file->contents.first; while (con != NULL) { connext = con->next; free(con); con = connext; } free(file); file = next; } } static int next_entry_seek(struct archive_read *a, struct iso9660 *iso9660, struct file_info **pfile) { struct file_info *file; int r; r = next_cache_entry(a, iso9660, pfile); if (r != ARCHIVE_OK) return (r); file = *pfile; /* Don't waste time seeking for zero-length bodies. */ if (file->size == 0) file->offset = iso9660->current_position; /* flush any remaining bytes from the last round to ensure * we're positioned */ if (iso9660->entry_bytes_unconsumed) { __archive_read_consume(a, iso9660->entry_bytes_unconsumed); iso9660->entry_bytes_unconsumed = 0; } /* Seek forward to the start of the entry. */ if (iso9660->current_position < file->offset) { int64_t step; step = file->offset - iso9660->current_position; step = __archive_read_consume(a, step); if (step < 0) return ((int)step); iso9660->current_position = file->offset; } /* We found body of file; handle it now. */ return (ARCHIVE_OK); } static int next_cache_entry(struct archive_read *a, struct iso9660 *iso9660, struct file_info **pfile) { struct file_info *file; struct { struct file_info *first; struct file_info **last; } empty_files; int64_t number; int count; file = cache_get_entry(iso9660); if (file != NULL) { *pfile = file; return (ARCHIVE_OK); } for (;;) { struct file_info *re, *d; *pfile = file = next_entry(iso9660); if (file == NULL) { /* * If directory entries all which are descendant of * rr_moved are still remaining, expose their. */ if (iso9660->re_files.first != NULL && iso9660->rr_moved != NULL && iso9660->rr_moved->rr_moved_has_re_only) /* Expose "rr_moved" entry. */ cache_add_entry(iso9660, iso9660->rr_moved); while ((re = re_get_entry(iso9660)) != NULL) { /* Expose its descendant dirs. */ while ((d = rede_get_entry(re)) != NULL) cache_add_entry(iso9660, d); } if (iso9660->cache_files.first != NULL) return (next_cache_entry(a, iso9660, pfile)); return (ARCHIVE_EOF); } if (file->cl_offset) { struct file_info *first_re = NULL; int nexted_re = 0; /* * Find "RE" dir for the current file, which * has "CL" flag. */ while ((re = re_get_entry(iso9660)) != first_re) { if (first_re == NULL) first_re = re; if (re->offset == file->cl_offset) { re->parent->subdirs--; re->parent = file->parent; re->re = 0; if (re->parent->re_descendant) { nexted_re = 1; re->re_descendant = 1; if (rede_add_entry(re) < 0) goto fatal_rr; /* Move a list of descendants * to a new ancestor. */ while ((d = rede_get_entry( re)) != NULL) if (rede_add_entry(d) < 0) goto fatal_rr; break; } /* Replace the current file * with "RE" dir */ *pfile = file = re; /* Expose its descendant */ while ((d = rede_get_entry( file)) != NULL) cache_add_entry( iso9660, d); break; } else re_add_entry(iso9660, re); } if (nexted_re) { /* * Do not expose this at this time * because we have not gotten its full-path * name yet. */ continue; } } else if ((file->mode & AE_IFMT) == AE_IFDIR) { int r; /* Read file entries in this dir. */ r = read_children(a, file); if (r != ARCHIVE_OK) return (r); /* * Handle a special dir of Rockridge extensions, * "rr_moved". */ if (file->rr_moved) { /* * If this has only the subdirectories which * have "RE" flags, do not expose at this time. */ if (file->rr_moved_has_re_only) continue; /* Otherwise expose "rr_moved" entry. */ } else if (file->re) { /* * Do not expose this at this time * because we have not gotten its full-path * name yet. */ re_add_entry(iso9660, file); continue; } else if (file->re_descendant) { /* * If the top level "RE" entry of this entry * is not exposed, we, accordingly, should not * expose this entry at this time because * we cannot make its proper full-path name. */ if (rede_add_entry(file) == 0) continue; /* Otherwise we can expose this entry because * it seems its top level "RE" has already been * exposed. */ } } break; } if ((file->mode & AE_IFMT) != AE_IFREG || file->number == -1) return (ARCHIVE_OK); count = 0; number = file->number; iso9660->cache_files.first = NULL; iso9660->cache_files.last = &(iso9660->cache_files.first); empty_files.first = NULL; empty_files.last = &empty_files.first; /* Collect files which has the same file serial number. * Peek pending_files so that file which number is different * is not put back. */ while (iso9660->pending_files.used > 0 && (iso9660->pending_files.files[0]->number == -1 || iso9660->pending_files.files[0]->number == number)) { if (file->number == -1) { /* This file has the same offset * but it's wrong offset which empty files * and symlink files have. * NOTE: This wrong offset was recorded by * old mkisofs utility. If ISO images is * created by latest mkisofs, this does not * happen. */ file->next = NULL; *empty_files.last = file; empty_files.last = &(file->next); } else { count++; cache_add_entry(iso9660, file); } file = next_entry(iso9660); } if (count == 0) { *pfile = file; return ((file == NULL)?ARCHIVE_EOF:ARCHIVE_OK); } if (file->number == -1) { file->next = NULL; *empty_files.last = file; empty_files.last = &(file->next); } else { count++; cache_add_entry(iso9660, file); } if (count > 1) { /* The count is the same as number of hardlink, * so much so that each nlinks of files in cache_file * is overwritten by value of the count. */ for (file = iso9660->cache_files.first; file != NULL; file = file->next) file->nlinks = count; } /* If there are empty files, that files are added * to the tail of the cache_files. */ if (empty_files.first != NULL) { *iso9660->cache_files.last = empty_files.first; iso9660->cache_files.last = empty_files.last; } *pfile = cache_get_entry(iso9660); return ((*pfile == NULL)?ARCHIVE_EOF:ARCHIVE_OK); fatal_rr: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to connect 'CL' pointer to 'RE' rr_moved pointer of " "Rockridge extensions: current position = %jd, CL offset = %jd", (intmax_t)iso9660->current_position, (intmax_t)file->cl_offset); return (ARCHIVE_FATAL); } static inline void re_add_entry(struct iso9660 *iso9660, struct file_info *file) { file->re_next = NULL; *iso9660->re_files.last = file; iso9660->re_files.last = &(file->re_next); } static inline struct file_info * re_get_entry(struct iso9660 *iso9660) { struct file_info *file; if ((file = iso9660->re_files.first) != NULL) { iso9660->re_files.first = file->re_next; if (iso9660->re_files.first == NULL) iso9660->re_files.last = &(iso9660->re_files.first); } return (file); } static inline int rede_add_entry(struct file_info *file) { struct file_info *re; /* * Find "RE" entry. */ re = file->parent; while (re != NULL && !re->re) re = re->parent; if (re == NULL) return (-1); file->re_next = NULL; *re->rede_files.last = file; re->rede_files.last = &(file->re_next); return (0); } static inline struct file_info * rede_get_entry(struct file_info *re) { struct file_info *file; if ((file = re->rede_files.first) != NULL) { re->rede_files.first = file->re_next; if (re->rede_files.first == NULL) re->rede_files.last = &(re->rede_files.first); } return (file); } static inline void cache_add_entry(struct iso9660 *iso9660, struct file_info *file) { file->next = NULL; *iso9660->cache_files.last = file; iso9660->cache_files.last = &(file->next); } static inline struct file_info * cache_get_entry(struct iso9660 *iso9660) { struct file_info *file; if ((file = iso9660->cache_files.first) != NULL) { iso9660->cache_files.first = file->next; if (iso9660->cache_files.first == NULL) iso9660->cache_files.last = &(iso9660->cache_files.first); } return (file); } static int heap_add_entry(struct archive_read *a, struct heap_queue *heap, struct file_info *file, uint64_t key) { uint64_t file_key, parent_key; int hole, parent; /* Expand our pending files list as necessary. */ if (heap->used >= heap->allocated) { struct file_info **new_pending_files; int new_size = heap->allocated * 2; if (heap->allocated < 1024) new_size = 1024; /* Overflow might keep us from growing the list. */ if (new_size <= heap->allocated) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } new_pending_files = (struct file_info **) malloc(new_size * sizeof(new_pending_files[0])); if (new_pending_files == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } if (heap->allocated) memcpy(new_pending_files, heap->files, heap->allocated * sizeof(new_pending_files[0])); - if (heap->files != NULL) - free(heap->files); + free(heap->files); heap->files = new_pending_files; heap->allocated = new_size; } file_key = file->key = key; /* * Start with hole at end, walk it up tree to find insertion point. */ hole = heap->used++; while (hole > 0) { parent = (hole - 1)/2; parent_key = heap->files[parent]->key; if (file_key >= parent_key) { heap->files[hole] = file; return (ARCHIVE_OK); } /* Move parent into hole <==> move hole up tree. */ heap->files[hole] = heap->files[parent]; hole = parent; } heap->files[0] = file; return (ARCHIVE_OK); } static struct file_info * heap_get_entry(struct heap_queue *heap) { uint64_t a_key, b_key, c_key; int a, b, c; struct file_info *r, *tmp; if (heap->used < 1) return (NULL); /* * The first file in the list is the earliest; we'll return this. */ r = heap->files[0]; /* * Move the last item in the heap to the root of the tree */ heap->files[0] = heap->files[--(heap->used)]; /* * Rebalance the heap. */ a = 0; /* Starting element and its heap key */ a_key = heap->files[a]->key; for (;;) { b = a + a + 1; /* First child */ if (b >= heap->used) return (r); b_key = heap->files[b]->key; c = b + 1; /* Use second child if it is smaller. */ if (c < heap->used) { c_key = heap->files[c]->key; if (c_key < b_key) { b = c; b_key = c_key; } } if (a_key <= b_key) return (r); tmp = heap->files[a]; heap->files[a] = heap->files[b]; heap->files[b] = tmp; a = b; } } static unsigned int toi(const void *p, int n) { const unsigned char *v = (const unsigned char *)p; if (n > 1) return v[0] + 256 * toi(v + 1, n - 1); if (n == 1) return v[0]; return (0); } static time_t isodate7(const unsigned char *v) { struct tm tm; int offset; time_t t; memset(&tm, 0, sizeof(tm)); tm.tm_year = v[0]; tm.tm_mon = v[1] - 1; tm.tm_mday = v[2]; tm.tm_hour = v[3]; tm.tm_min = v[4]; tm.tm_sec = v[5]; /* v[6] is the signed timezone offset, in 1/4-hour increments. */ offset = ((const signed char *)v)[6]; if (offset > -48 && offset < 52) { tm.tm_hour -= offset / 4; tm.tm_min -= (offset % 4) * 15; } t = time_from_tm(&tm); if (t == (time_t)-1) return ((time_t)0); return (t); } static time_t isodate17(const unsigned char *v) { struct tm tm; int offset; time_t t; memset(&tm, 0, sizeof(tm)); tm.tm_year = (v[0] - '0') * 1000 + (v[1] - '0') * 100 + (v[2] - '0') * 10 + (v[3] - '0') - 1900; tm.tm_mon = (v[4] - '0') * 10 + (v[5] - '0'); tm.tm_mday = (v[6] - '0') * 10 + (v[7] - '0'); tm.tm_hour = (v[8] - '0') * 10 + (v[9] - '0'); tm.tm_min = (v[10] - '0') * 10 + (v[11] - '0'); tm.tm_sec = (v[12] - '0') * 10 + (v[13] - '0'); /* v[16] is the signed timezone offset, in 1/4-hour increments. */ offset = ((const signed char *)v)[16]; if (offset > -48 && offset < 52) { tm.tm_hour -= offset / 4; tm.tm_min -= (offset % 4) * 15; } t = time_from_tm(&tm); if (t == (time_t)-1) return ((time_t)0); return (t); } static time_t time_from_tm(struct tm *t) { #if HAVE_TIMEGM /* Use platform timegm() if available. */ return (timegm(t)); #elif HAVE__MKGMTIME64 return (_mkgmtime64(t)); #else /* Else use direct calculation using POSIX assumptions. */ /* First, fix up tm_yday based on the year/month/day. */ if (mktime(t) == (time_t)-1) return ((time_t)-1); /* Then we can compute timegm() from first principles. */ return (t->tm_sec + t->tm_min * 60 + t->tm_hour * 3600 + t->tm_yday * 86400 + (t->tm_year - 70) * 31536000 + ((t->tm_year - 69) / 4) * 86400 - ((t->tm_year - 1) / 100) * 86400 + ((t->tm_year + 299) / 400) * 86400); #endif } static const char * build_pathname(struct archive_string *as, struct file_info *file, int depth) { // Plain ISO9660 only allows 8 dir levels; if we get // to 1000, then something is very, very wrong. if (depth > 1000) { return NULL; } if (file->parent != NULL && archive_strlen(&file->parent->name) > 0) { if (build_pathname(as, file->parent, depth + 1) == NULL) { return NULL; } archive_strcat(as, "/"); } if (archive_strlen(&file->name) == 0) archive_strcat(as, "."); else archive_string_concat(as, &file->name); return (as->s); } static int build_pathname_utf16be(unsigned char *p, size_t max, size_t *len, struct file_info *file) { if (file->parent != NULL && file->parent->utf16be_bytes > 0) { if (build_pathname_utf16be(p, max, len, file->parent) != 0) return (-1); p[*len] = 0; p[*len + 1] = '/'; *len += 2; } if (file->utf16be_bytes == 0) { if (*len + 2 > max) return (-1);/* Path is too long! */ p[*len] = 0; p[*len + 1] = '.'; *len += 2; } else { if (*len + file->utf16be_bytes > max) return (-1);/* Path is too long! */ memcpy(p + *len, file->utf16be_name, file->utf16be_bytes); *len += file->utf16be_bytes; } return (0); } #if DEBUG static void dump_isodirrec(FILE *out, const unsigned char *isodirrec) { fprintf(out, " l %d,", toi(isodirrec + DR_length_offset, DR_length_size)); fprintf(out, " a %d,", toi(isodirrec + DR_ext_attr_length_offset, DR_ext_attr_length_size)); fprintf(out, " ext 0x%x,", toi(isodirrec + DR_extent_offset, DR_extent_size)); fprintf(out, " s %d,", toi(isodirrec + DR_size_offset, DR_extent_size)); fprintf(out, " f 0x%x,", toi(isodirrec + DR_flags_offset, DR_flags_size)); fprintf(out, " u %d,", toi(isodirrec + DR_file_unit_size_offset, DR_file_unit_size_size)); fprintf(out, " ilv %d,", toi(isodirrec + DR_interleave_offset, DR_interleave_size)); fprintf(out, " seq %d,", toi(isodirrec + DR_volume_sequence_number_offset, DR_volume_sequence_number_size)); fprintf(out, " nl %d:", toi(isodirrec + DR_name_len_offset, DR_name_len_size)); fprintf(out, " `%.*s'", toi(isodirrec + DR_name_len_offset, DR_name_len_size), isodirrec + DR_name_offset); } #endif Index: stable/10/contrib/libarchive/libarchive/archive_read_support_format_rar5.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_support_format_rar5.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_read_support_format_rar5.c (revision 344674) @@ -1,3457 +1,3468 @@ /*- * Copyright (c) 2018 Grzegorz Antoniak (http://antoniak.org) * 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 "archive_platform.h" #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_ZLIB_H #include /* crc32 */ #endif #include "archive.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" #endif #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_ppmd7_private.h" #include "archive_entry_private.h" #ifdef HAVE_BLAKE2_H #include #else #include "archive_blake2.h" #endif /*#define CHECK_CRC_ON_SOLID_SKIP*/ /*#define DONT_FAIL_ON_CRC_ERROR*/ /*#define DEBUG*/ #define rar5_min(a, b) (((a) > (b)) ? (b) : (a)) #define rar5_max(a, b) (((a) > (b)) ? (a) : (b)) #define rar5_countof(X) ((const ssize_t) (sizeof(X) / sizeof(*X))) #if defined DEBUG #define DEBUG_CODE if(1) #else #define DEBUG_CODE if(0) #endif /* Real RAR5 magic number is: * * 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 * "Rar!→•☺·\x00" * * It's stored in `rar5_signature` after XOR'ing it with 0xA1, because I don't * want to put this magic sequence in each binary that uses libarchive, so * applications that scan through the file for this marker won't trigger on * this "false" one. * * The array itself is decrypted in `rar5_init` function. */ static unsigned char rar5_signature[] = { 243, 192, 211, 128, 187, 166, 160, 161 }; static const ssize_t rar5_signature_size = sizeof(rar5_signature); /* static const size_t g_unpack_buf_chunk_size = 1024; */ static const size_t g_unpack_window_size = 0x20000; struct file_header { ssize_t bytes_remaining; ssize_t unpacked_size; int64_t last_offset; /* Used in sanity checks. */ int64_t last_size; /* Used in sanity checks. */ uint8_t solid : 1; /* Is this a solid stream? */ uint8_t service : 1; /* Is this file a service data? */ /* Optional time fields. */ uint64_t e_mtime; uint64_t e_ctime; uint64_t e_atime; uint32_t e_unix_ns; /* Optional hash fields. */ uint32_t stored_crc32; uint32_t calculated_crc32; uint8_t blake2sp[32]; blake2sp_state b2state; char has_blake2; }; enum FILTER_TYPE { FILTER_DELTA = 0, /* Generic pattern. */ FILTER_E8 = 1, /* Intel x86 code. */ FILTER_E8E9 = 2, /* Intel x86 code. */ FILTER_ARM = 3, /* ARM code. */ FILTER_AUDIO = 4, /* Audio filter, not used in RARv5. */ FILTER_RGB = 5, /* Color palette, not used in RARv5. */ FILTER_ITANIUM = 6, /* Intel's Itanium, not used in RARv5. */ FILTER_PPM = 7, /* Predictive pattern matching, not used in RARv5. */ FILTER_NONE = 8, }; struct filter_info { int type; int channels; int pos_r; int64_t block_start; ssize_t block_length; uint16_t width; }; struct data_ready { char used; const uint8_t* buf; size_t size; int64_t offset; }; struct cdeque { uint16_t beg_pos; uint16_t end_pos; uint16_t cap_mask; uint16_t size; size_t* arr; }; struct decode_table { uint32_t size; int32_t decode_len[16]; uint32_t decode_pos[16]; uint32_t quick_bits; uint8_t quick_len[1 << 10]; uint16_t quick_num[1 << 10]; uint16_t decode_num[306]; }; struct comp_state { /* Flag used to specify if unpacker needs to reinitialize the uncompression * context. */ uint8_t initialized : 1; /* Flag used when applying filters. */ uint8_t all_filters_applied : 1; /* Flag used to skip file context reinitialization, used when unpacker is * skipping through different multivolume archives. */ uint8_t switch_multivolume : 1; /* Flag used to specify if unpacker has processed the whole data block or * just a part of it. */ uint8_t block_parsing_finished : 1; int notused : 4; int flags; /* Uncompression flags. */ int method; /* Uncompression algorithm method. */ int version; /* Uncompression algorithm version. */ ssize_t window_size; /* Size of window_buf. */ uint8_t* window_buf; /* Circular buffer used during decompression. */ uint8_t* filtered_buf; /* Buffer used when applying filters. */ const uint8_t* block_buf; /* Buffer used when merging blocks. */ size_t window_mask; /* Convinience field; window_size - 1. */ int64_t write_ptr; /* This amount of data has been unpacked in the window buffer. */ int64_t last_write_ptr; /* This amount of data has been stored in the output file. */ int64_t last_unstore_ptr; /* Counter of bytes extracted during unstoring. This is separate from last_write_ptr because of how SERVICE base blocks are handled during skipping in solid multiarchive archives. */ int64_t solid_offset; /* Additional offset inside the window buffer, used in unpacking solid archives. */ ssize_t cur_block_size; /* Size of current data block. */ int last_len; /* Flag used in lzss decompression. */ /* Decode tables used during lzss uncompression. */ #define HUFF_BC 20 struct decode_table bd; /* huffman bit lengths */ #define HUFF_NC 306 struct decode_table ld; /* literals */ #define HUFF_DC 64 struct decode_table dd; /* distances */ #define HUFF_LDC 16 struct decode_table ldd; /* lower bits of distances */ #define HUFF_RC 44 struct decode_table rd; /* repeating distances */ #define HUFF_TABLE_SIZE (HUFF_NC + HUFF_DC + HUFF_RC + HUFF_LDC) /* Circular deque for storing filters. */ struct cdeque filters; int64_t last_block_start; /* Used for sanity checking. */ ssize_t last_block_length; /* Used for sanity checking. */ /* Distance cache used during lzss uncompression. */ int dist_cache[4]; /* Data buffer stack. */ struct data_ready dready[2]; }; /* Bit reader state. */ struct bit_reader { int8_t bit_addr; /* Current bit pointer inside current byte. */ int in_addr; /* Current byte pointer. */ }; /* RARv5 block header structure. */ struct compressed_block_header { union { struct { uint8_t bit_size : 3; uint8_t byte_count : 3; uint8_t is_last_block : 1; uint8_t is_table_present : 1; } block_flags; uint8_t block_flags_u8; }; uint8_t block_cksum; }; /* RARv5 main header structure. */ struct main_header { /* Does the archive contain solid streams? */ uint8_t solid : 1; /* If this a multi-file archive? */ uint8_t volume : 1; uint8_t endarc : 1; uint8_t notused : 5; int vol_no; }; struct generic_header { uint8_t split_after : 1; uint8_t split_before : 1; uint8_t padding : 6; int size; int last_header_id; }; struct multivolume { int expected_vol_no; uint8_t* push_buf; }; /* Main context structure. */ struct rar5 { int header_initialized; /* Set to 1 if current file is positioned AFTER the magic value * of the archive file. This is used in header reading functions. */ int skipped_magic; /* Set to not zero if we're in skip mode (either by calling rar5_data_skip * function or when skipping over solid streams). Set to 0 when in * extraction mode. This is used during checksum calculation functions. */ int skip_mode; /* An offset to QuickOpen list. This is not supported by this unpacker, * becuase we're focusing on streaming interface. QuickOpen is designed * to make things quicker for non-stream interfaces, so it's not our * use case. */ uint64_t qlist_offset; /* An offset to additional Recovery data. This is not supported by this * unpacker. Recovery data are additional Reed-Solomon codes that could * be used to calculate bytes that are missing in archive or are * corrupted. */ uint64_t rr_offset; /* Various context variables grouped to different structures. */ struct generic_header generic; struct main_header main; struct comp_state cstate; struct file_header file; struct bit_reader bits; struct multivolume vol; /* The header of currently processed RARv5 block. Used in main * decompression logic loop. */ struct compressed_block_header last_block_hdr; }; /* Forward function declarations. */ static int verify_global_checksums(struct archive_read* a); static int rar5_read_data_skip(struct archive_read *a); static int push_data_ready(struct archive_read* a, struct rar5* rar, const uint8_t* buf, size_t size, int64_t offset); /* CDE_xxx = Circular Double Ended (Queue) return values. */ enum CDE_RETURN_VALUES { CDE_OK, CDE_ALLOC, CDE_PARAM, CDE_OUT_OF_BOUNDS, }; /* Clears the contents of this circular deque. */ static void cdeque_clear(struct cdeque* d) { d->size = 0; d->beg_pos = 0; d->end_pos = 0; } /* Creates a new circular deque object. Capacity must be power of 2: 8, 16, 32, * 64, 256, etc. When the user will add another item above current capacity, * the circular deque will overwrite the oldest entry. */ static int cdeque_init(struct cdeque* d, int max_capacity_power_of_2) { if(d == NULL || max_capacity_power_of_2 == 0) return CDE_PARAM; d->cap_mask = max_capacity_power_of_2 - 1; d->arr = NULL; if((max_capacity_power_of_2 & d->cap_mask) > 0) return CDE_PARAM; cdeque_clear(d); d->arr = malloc(sizeof(void*) * max_capacity_power_of_2); return d->arr ? CDE_OK : CDE_ALLOC; } /* Return the current size (not capacity) of circular deque `d`. */ static size_t cdeque_size(struct cdeque* d) { return d->size; } /* Returns the first element of current circular deque. Note that this function * doesn't perform any bounds checking. If you need bounds checking, use * `cdeque_front()` function instead. */ static void cdeque_front_fast(struct cdeque* d, void** value) { *value = (void*) d->arr[d->beg_pos]; } /* Returns the first element of current circular deque. This function * performs bounds checking. */ static int cdeque_front(struct cdeque* d, void** value) { if(d->size > 0) { cdeque_front_fast(d, value); return CDE_OK; } else return CDE_OUT_OF_BOUNDS; } /* Pushes a new element into the end of this circular deque object. If current * size will exceed capacity, the oldest element will be overwritten. */ static int cdeque_push_back(struct cdeque* d, void* item) { if(d == NULL) return CDE_PARAM; if(d->size == d->cap_mask + 1) return CDE_OUT_OF_BOUNDS; d->arr[d->end_pos] = (size_t) item; d->end_pos = (d->end_pos + 1) & d->cap_mask; d->size++; return CDE_OK; } /* Pops a front element of this circular deque object and returns its value. * This function doesn't perform any bounds checking. */ static void cdeque_pop_front_fast(struct cdeque* d, void** value) { *value = (void*) d->arr[d->beg_pos]; d->beg_pos = (d->beg_pos + 1) & d->cap_mask; d->size--; } /* Pops a front element of this cicrular deque object and returns its value. * This function performs bounds checking. */ static int cdeque_pop_front(struct cdeque* d, void** value) { if(!d || !value) return CDE_PARAM; if(d->size == 0) return CDE_OUT_OF_BOUNDS; cdeque_pop_front_fast(d, value); return CDE_OK; } /* Convinience function to cast filter_info** to void **. */ static void** cdeque_filter_p(struct filter_info** f) { return (void**) (size_t) f; } /* Convinience function to cast filter_info* to void *. */ static void* cdeque_filter(struct filter_info* f) { return (void**) (size_t) f; } /* Destroys this circular deque object. Dellocates the memory of the collection * buffer, but doesn't deallocate the memory of any pointer passed to this * deque as a value. */ static void cdeque_free(struct cdeque* d) { if(!d) return; if(!d->arr) return; free(d->arr); d->arr = NULL; d->beg_pos = -1; d->end_pos = -1; d->cap_mask = 0; } static inline struct rar5* get_context(struct archive_read* a) { return (struct rar5*) a->format->data; } // TODO: make sure these functions return a little endian number /* Convinience functions used by filter implementations. */ static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) { uint32_t* dptr = (uint32_t*) &rar->cstate.window_buf[offset]; // TODO: bswap if big endian return *dptr; } static void write_filter_data(struct rar5* rar, uint32_t offset, uint32_t value) { uint32_t* dptr = (uint32_t*) &rar->cstate.filtered_buf[offset]; // TODO: bswap if big endian *dptr = value; } static void circular_memcpy(uint8_t* dst, uint8_t* window, const int mask, int64_t start, int64_t end) { if((start & mask) > (end & mask)) { ssize_t len1 = mask + 1 - (start & mask); ssize_t len2 = end & mask; memcpy(dst, &window[start & mask], len1); memcpy(dst + len1, window, len2); } else { memcpy(dst, &window[start & mask], (size_t) (end - start)); } } /* Allocates a new filter descriptor and adds it to the filter array. */ static struct filter_info* add_new_filter(struct rar5* rar) { struct filter_info* f = (struct filter_info*) calloc(1, sizeof(struct filter_info)); if(!f) { return NULL; } cdeque_push_back(&rar->cstate.filters, cdeque_filter(f)); return f; } static int run_delta_filter(struct rar5* rar, struct filter_info* flt) { int i; ssize_t dest_pos, src_pos = 0; for(i = 0; i < flt->channels; i++) { uint8_t prev_byte = 0; for(dest_pos = i; dest_pos < flt->block_length; dest_pos += flt->channels) { uint8_t byte; byte = rar->cstate.window_buf[(rar->cstate.solid_offset + flt->block_start + src_pos) & rar->cstate.window_mask]; prev_byte -= byte; rar->cstate.filtered_buf[dest_pos] = prev_byte; src_pos++; } } return ARCHIVE_OK; } static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt, int extended) { const uint32_t file_size = 0x1000000; ssize_t i; circular_memcpy(rar->cstate.filtered_buf, rar->cstate.window_buf, rar->cstate.window_mask, rar->cstate.solid_offset + flt->block_start, rar->cstate.solid_offset + flt->block_start + flt->block_length); for(i = 0; i < flt->block_length - 4;) { uint8_t b = rar->cstate.window_buf[(rar->cstate.solid_offset + flt->block_start + i++) & rar->cstate.window_mask]; /* 0xE8 = x86's call (function call) * 0xE9 = x86's jmp (unconditional jump) */ if(b == 0xE8 || (extended && b == 0xE9)) { uint32_t addr; uint32_t offset = (i + flt->block_start) % file_size; addr = read_filter_data(rar, (rar->cstate.solid_offset + flt->block_start + i) & rar->cstate.window_mask); if(addr & 0x80000000) { if(((addr + offset) & 0x80000000) == 0) { write_filter_data(rar, i, addr + file_size); } } else { if((addr - file_size) & 0x80000000) { uint32_t naddr = addr - offset; write_filter_data(rar, i, naddr); } } i += 4; } } return ARCHIVE_OK; } static int run_arm_filter(struct rar5* rar, struct filter_info* flt) { ssize_t i = 0; uint32_t offset; const int mask = rar->cstate.window_mask; circular_memcpy(rar->cstate.filtered_buf, rar->cstate.window_buf, rar->cstate.window_mask, rar->cstate.solid_offset + flt->block_start, rar->cstate.solid_offset + flt->block_start + flt->block_length); for(i = 0; i < flt->block_length - 3; i += 4) { uint8_t* b = &rar->cstate.window_buf[(rar->cstate.solid_offset + flt->block_start + i) & mask]; if(b[3] == 0xEB) { /* 0xEB = ARM's BL (branch + link) instruction. */ offset = read_filter_data(rar, (rar->cstate.solid_offset + flt->block_start + i) & mask) & 0x00ffffff; offset -= (uint32_t) ((i + flt->block_start) / 4); offset = (offset & 0x00ffffff) | 0xeb000000; write_filter_data(rar, i, offset); } } return ARCHIVE_OK; } static int run_filter(struct archive_read* a, struct filter_info* flt) { int ret; struct rar5* rar = get_context(a); - if(rar->cstate.filtered_buf) - free(rar->cstate.filtered_buf); + free(rar->cstate.filtered_buf); rar->cstate.filtered_buf = malloc(flt->block_length); if(!rar->cstate.filtered_buf) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for " "filter data."); return ARCHIVE_FATAL; } switch(flt->type) { case FILTER_DELTA: ret = run_delta_filter(rar, flt); break; case FILTER_E8: /* fallthrough */ case FILTER_E8E9: ret = run_e8e9_filter(rar, flt, flt->type == FILTER_E8E9); break; case FILTER_ARM: ret = run_arm_filter(rar, flt); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported filter type: 0x%02x", flt->type); return ARCHIVE_FATAL; } if(ret != ARCHIVE_OK) { /* Filter has failed. */ return ret; } if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf, flt->block_length, rar->cstate.last_write_ptr)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Stack overflow when submitting unpacked data"); return ARCHIVE_FATAL; } rar->cstate.last_write_ptr += flt->block_length; return ARCHIVE_OK; } /* The `push_data` function submits the selected data range to the user. * Next call of `use_data` will use the pointer, size and offset arguments * that are specified here. These arguments are pushed to the FIFO stack here, * and popped from the stack by the `use_data` function. */ static void push_data(struct archive_read* a, struct rar5* rar, const uint8_t* buf, int64_t idx_begin, int64_t idx_end) { const int wmask = rar->cstate.window_mask; const ssize_t solid_write_ptr = (rar->cstate.solid_offset + rar->cstate.last_write_ptr) & wmask; idx_begin += rar->cstate.solid_offset; idx_end += rar->cstate.solid_offset; /* Check if our unpacked data is wrapped inside the window circular buffer. * If it's not wrapped, it can be copied out by using a single memcpy, * but when it's wrapped, we need to copy the first part with one * memcpy, and the second part with another memcpy. */ if((idx_begin & wmask) > (idx_end & wmask)) { /* The data is wrapped (begin offset sis bigger than end offset). */ const ssize_t frag1_size = rar->cstate.window_size - (idx_begin & wmask); const ssize_t frag2_size = idx_end & wmask; /* Copy the first part of the buffer first. */ push_data_ready(a, rar, buf + solid_write_ptr, frag1_size, rar->cstate.last_write_ptr); /* Copy the second part of the buffer. */ push_data_ready(a, rar, buf, frag2_size, rar->cstate.last_write_ptr + frag1_size); rar->cstate.last_write_ptr += frag1_size + frag2_size; } else { /* Data is not wrapped, so we can just use one call to copy the * data. */ push_data_ready(a, rar, buf + solid_write_ptr, (idx_end - idx_begin) & wmask, rar->cstate.last_write_ptr); rar->cstate.last_write_ptr += idx_end - idx_begin; } } /* Convinience function that submits the data to the user. It uses the * unpack window buffer as a source location. */ static void push_window_data(struct archive_read* a, struct rar5* rar, int64_t idx_begin, int64_t idx_end) { push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end); } static int apply_filters(struct archive_read* a) { struct filter_info* flt; struct rar5* rar = get_context(a); int ret; rar->cstate.all_filters_applied = 0; /* Get the first filter that can be applied to our data. The data needs to * be fully unpacked before the filter can be run. */ if(CDE_OK == cdeque_front(&rar->cstate.filters, cdeque_filter_p(&flt))) { /* Check if our unpacked data fully covers this filter's range. */ if(rar->cstate.write_ptr > flt->block_start && rar->cstate.write_ptr >= flt->block_start + flt->block_length) { /* Check if we have some data pending to be written right before * the filter's start offset. */ if(rar->cstate.last_write_ptr == flt->block_start) { /* Run the filter specified by descriptor `flt`. */ ret = run_filter(a, flt); if(ret != ARCHIVE_OK) { /* Filter failure, return error. */ return ret; } /* Filter descriptor won't be needed anymore after it's used, * so remove it from the filter list and free its memory. */ (void) cdeque_pop_front(&rar->cstate.filters, cdeque_filter_p(&flt)); free(flt); } else { /* We can't run filters yet, dump the memory right before the * filter. */ push_window_data(a, rar, rar->cstate.last_write_ptr, flt->block_start); } /* Return 'filter applied or not needed' state to the caller. */ return ARCHIVE_RETRY; } } rar->cstate.all_filters_applied = 1; return ARCHIVE_OK; } static void dist_cache_push(struct rar5* rar, int value) { int* q = rar->cstate.dist_cache; q[3] = q[2]; q[2] = q[1]; q[1] = q[0]; q[0] = value; } static int dist_cache_touch(struct rar5* rar, int index) { int* q = rar->cstate.dist_cache; int i, dist = q[index]; for(i = index; i > 0; i--) q[i] = q[i - 1]; q[0] = dist; return dist; } static void free_filters(struct rar5* rar) { struct cdeque* d = &rar->cstate.filters; /* Free any remaining filters. All filters should be naturally consumed by * the unpacking function, so remaining filters after unpacking normally * mean that unpacking wasn't successfull. But still of course we shouldn't * leak memory in such case. */ /* cdeque_size() is a fast operation, so we can use it as a loop * expression. */ while(cdeque_size(d) > 0) { struct filter_info* f = NULL; /* Pop_front will also decrease the collection's size. */ - if(CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f)) && f != NULL) + if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f))) free(f); } cdeque_clear(d); /* Also clear out the variables needed for sanity checking. */ rar->cstate.last_block_start = 0; rar->cstate.last_block_length = 0; } static void reset_file_context(struct rar5* rar) { memset(&rar->file, 0, sizeof(rar->file)); blake2sp_init(&rar->file.b2state, 32); if(rar->main.solid) { rar->cstate.solid_offset += rar->cstate.write_ptr; } else { rar->cstate.solid_offset = 0; } rar->cstate.write_ptr = 0; rar->cstate.last_write_ptr = 0; rar->cstate.last_unstore_ptr = 0; free_filters(rar); } static inline int get_archive_read(struct archive* a, struct archive_read** ar) { *ar = (struct archive_read*) a; archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_rar5"); return ARCHIVE_OK; } static int read_ahead(struct archive_read* a, size_t how_many, const uint8_t** ptr) { if(!ptr) return 0; ssize_t avail = -1; *ptr = __archive_read_ahead(a, how_many, &avail); if(*ptr == NULL) { return 0; } return 1; } static int consume(struct archive_read* a, int64_t how_many) { int ret; ret = how_many == __archive_read_consume(a, how_many) ? ARCHIVE_OK : ARCHIVE_FATAL; return ret; } /** * Read a RAR5 variable sized numeric value. This value will be stored in * `pvalue`. The `pvalue_len` argument points to a variable that will receive * the byte count that was consumed in order to decode the `pvalue` value, plus * one. * * pvalue_len is optional and can be NULL. * * NOTE: if `pvalue_len` is NOT NULL, the caller needs to manually consume * the number of bytes that `pvalue_len` value contains. If the `pvalue_len` * is NULL, this consuming operation is done automatically. * * Returns 1 if *pvalue was successfully read. * Returns 0 if there was an error. In this case, *pvalue contains an * invalid value. */ static int read_var(struct archive_read* a, uint64_t* pvalue, uint64_t* pvalue_len) { uint64_t result = 0; size_t shift, i; const uint8_t* p; uint8_t b; /* We will read maximum of 8 bytes. We don't have to handle the situation * to read the RAR5 variable-sized value stored at the end of the file, * because such situation will never happen. */ if(!read_ahead(a, 8, &p)) return 0; for(shift = 0, i = 0; i < 8; i++, shift += 7) { b = p[i]; /* Strip the MSB from the input byte and add the resulting number * to the `result`. */ - result += (b & 0x7F) << shift; + result += (b & (uint64_t)0x7F) << shift; /* MSB set to 1 means we need to continue decoding process. MSB set * to 0 means we're done. * * This conditional checks for the second case. */ if((b & 0x80) == 0) { if(pvalue) { *pvalue = result; } /* If the caller has passed the `pvalue_len` pointer, store the * number of consumed bytes in it and do NOT consume those bytes, * since the caller has all the information it needs to perform * the consuming process itself. */ if(pvalue_len) { *pvalue_len = 1 + i; } else { /* If the caller did not provide the `pvalue_len` pointer, * it will not have the possibility to advance the file * pointer, because it will not know how many bytes it needs * to consume. This is why we handle such situation here * autmatically. */ if(ARCHIVE_OK != consume(a, 1 + i)) { return 0; } } /* End of decoding process, return success. */ return 1; } } /* The decoded value takes the maximum number of 8 bytes. It's a maximum * number of bytes, so end decoding process here even if the first bit * of last byte is 1. */ if(pvalue) { *pvalue = result; } if(pvalue_len) { *pvalue_len = 9; } else { if(ARCHIVE_OK != consume(a, 9)) { return 0; } } return 1; } static int read_var_sized(struct archive_read* a, size_t* pvalue, size_t* pvalue_len) { uint64_t v; uint64_t v_size; const int ret = pvalue_len ? read_var(a, &v, &v_size) : read_var(a, &v, NULL); if(ret == 1 && pvalue) { *pvalue = (size_t) v; } if(pvalue_len) { /* Possible data truncation should be safe. */ *pvalue_len = (size_t) v_size; } return ret; } static int read_bits_32(struct rar5* rar, const uint8_t* p, uint32_t* value) { uint32_t bits = p[rar->bits.in_addr] << 24; bits |= p[rar->bits.in_addr + 1] << 16; bits |= p[rar->bits.in_addr + 2] << 8; bits |= p[rar->bits.in_addr + 3]; bits <<= rar->bits.bit_addr; bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr); *value = bits; return ARCHIVE_OK; } static int read_bits_16(struct rar5* rar, const uint8_t* p, uint16_t* value) { int bits = (int) p[rar->bits.in_addr] << 16; bits |= (int) p[rar->bits.in_addr + 1] << 8; bits |= (int) p[rar->bits.in_addr + 2]; bits >>= (8 - rar->bits.bit_addr); *value = bits & 0xffff; return ARCHIVE_OK; } static void skip_bits(struct rar5* rar, int bits) { const int new_bits = rar->bits.bit_addr + bits; rar->bits.in_addr += new_bits >> 3; rar->bits.bit_addr = new_bits & 7; } /* n = up to 16 */ static int read_consume_bits(struct rar5* rar, const uint8_t* p, int n, int* value) { uint16_t v; int ret, num; if(n == 0 || n > 16) { /* This is a programmer error and should never happen in runtime. */ return ARCHIVE_FATAL; } ret = read_bits_16(rar, p, &v); if(ret != ARCHIVE_OK) return ret; num = (int) v; num >>= 16 - n; skip_bits(rar, n); if(value) *value = num; return ARCHIVE_OK; } static int read_u32(struct archive_read* a, uint32_t* pvalue) { const uint8_t* p; if(!read_ahead(a, 4, &p)) return 0; *pvalue = *(const uint32_t*)p; return ARCHIVE_OK == consume(a, 4) ? 1 : 0; } static int read_u64(struct archive_read* a, uint64_t* pvalue) { const uint8_t* p; if(!read_ahead(a, 8, &p)) return 0; *pvalue = *(const uint64_t*)p; return ARCHIVE_OK == consume(a, 8) ? 1 : 0; } static int bid_standard(struct archive_read* a) { const uint8_t* p; if(!read_ahead(a, rar5_signature_size, &p)) return -1; if(!memcmp(rar5_signature, p, rar5_signature_size)) return 30; return -1; } static int rar5_bid(struct archive_read* a, int best_bid) { int my_bid; if(best_bid > 30) return -1; my_bid = bid_standard(a); if(my_bid > -1) { return my_bid; } return -1; } static int rar5_options(struct archive_read *a, const char *key, const char *val) { (void) a; (void) key; (void) val; /* No options supported in this version. Return the ARCHIVE_WARN code to * signal the options supervisor that the unpacker didn't handle setting * this option. */ return ARCHIVE_WARN; } static void init_header(struct archive_read* a) { a->archive.archive_format = ARCHIVE_FORMAT_RAR_V5; a->archive.archive_format_name = "RAR5"; } enum HEADER_FLAGS { HFL_EXTRA_DATA = 0x0001, HFL_DATA = 0x0002, HFL_SKIP_IF_UNKNOWN = 0x0004, HFL_SPLIT_BEFORE = 0x0008, HFL_SPLIT_AFTER = 0x0010, HFL_CHILD = 0x0020, HFL_INHERITED = 0x0040 }; static int process_main_locator_extra_block(struct archive_read* a, struct rar5* rar) { uint64_t locator_flags; if(!read_var(a, &locator_flags, NULL)) { return ARCHIVE_EOF; } enum LOCATOR_FLAGS { QLIST = 0x01, RECOVERY = 0x02, }; if(locator_flags & QLIST) { if(!read_var(a, &rar->qlist_offset, NULL)) { return ARCHIVE_EOF; } /* qlist is not used */ } if(locator_flags & RECOVERY) { if(!read_var(a, &rar->rr_offset, NULL)) { return ARCHIVE_EOF; } /* rr is not used */ } return ARCHIVE_OK; } static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar, ssize_t* extra_data_size) { size_t hash_type; size_t value_len; if(!read_var_sized(a, &hash_type, &value_len)) return ARCHIVE_EOF; *extra_data_size -= value_len; if(ARCHIVE_OK != consume(a, value_len)) { return ARCHIVE_EOF; } enum HASH_TYPE { BLAKE2sp = 0x00 }; /* The file uses BLAKE2sp checksum algorithm instead of plain old * CRC32. */ if(hash_type == BLAKE2sp) { const uint8_t* p; const int hash_size = sizeof(rar->file.blake2sp); if(!read_ahead(a, hash_size, &p)) return ARCHIVE_EOF; rar->file.has_blake2 = 1; memcpy(&rar->file.blake2sp, p, hash_size); if(ARCHIVE_OK != consume(a, hash_size)) { return ARCHIVE_EOF; } *extra_data_size -= hash_size; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported hash type (0x%02x)", (int) hash_type); return ARCHIVE_FATAL; } return ARCHIVE_OK; } static uint64_t time_win_to_unix(uint64_t win_time) { const size_t ns_in_sec = 10000000; const uint64_t sec_to_unix = 11644473600LL; return win_time / ns_in_sec - sec_to_unix; } static int parse_htime_item(struct archive_read* a, char unix_time, uint64_t* where, ssize_t* extra_data_size) { if(unix_time) { uint32_t time_val; if(!read_u32(a, &time_val)) return ARCHIVE_EOF; *extra_data_size -= 4; *where = (uint64_t) time_val; } else { uint64_t windows_time; if(!read_u64(a, &windows_time)) return ARCHIVE_EOF; *where = time_win_to_unix(windows_time); *extra_data_size -= 8; } return ARCHIVE_OK; } static int parse_file_extra_htime(struct archive_read* a, struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size) { char unix_time = 0; size_t flags; size_t value_len; enum HTIME_FLAGS { IS_UNIX = 0x01, HAS_MTIME = 0x02, HAS_CTIME = 0x04, HAS_ATIME = 0x08, HAS_UNIX_NS = 0x10, }; if(!read_var_sized(a, &flags, &value_len)) return ARCHIVE_EOF; *extra_data_size -= value_len; if(ARCHIVE_OK != consume(a, value_len)) { return ARCHIVE_EOF; } unix_time = flags & IS_UNIX; if(flags & HAS_MTIME) { parse_htime_item(a, unix_time, &rar->file.e_mtime, extra_data_size); archive_entry_set_mtime(e, rar->file.e_mtime, 0); } if(flags & HAS_CTIME) { parse_htime_item(a, unix_time, &rar->file.e_ctime, extra_data_size); archive_entry_set_ctime(e, rar->file.e_ctime, 0); } if(flags & HAS_ATIME) { parse_htime_item(a, unix_time, &rar->file.e_atime, extra_data_size); archive_entry_set_atime(e, rar->file.e_atime, 0); } if(flags & HAS_UNIX_NS) { if(!read_u32(a, &rar->file.e_unix_ns)) return ARCHIVE_EOF; *extra_data_size -= 4; } return ARCHIVE_OK; } static int process_head_file_extra(struct archive_read* a, struct archive_entry* e, struct rar5* rar, ssize_t extra_data_size) { size_t extra_field_size; size_t extra_field_id; int ret = ARCHIVE_FATAL; size_t var_size; enum EXTRA { CRYPT = 0x01, HASH = 0x02, HTIME = 0x03, VERSION_ = 0x04, REDIR = 0x05, UOWNER = 0x06, SUBDATA = 0x07 }; while(extra_data_size > 0) { if(!read_var_sized(a, &extra_field_size, &var_size)) return ARCHIVE_EOF; extra_data_size -= var_size; if(ARCHIVE_OK != consume(a, var_size)) { return ARCHIVE_EOF; } if(!read_var_sized(a, &extra_field_id, &var_size)) return ARCHIVE_EOF; extra_data_size -= var_size; if(ARCHIVE_OK != consume(a, var_size)) { return ARCHIVE_EOF; } switch(extra_field_id) { case HASH: ret = parse_file_extra_hash(a, rar, &extra_data_size); break; case HTIME: ret = parse_file_extra_htime(a, e, rar, &extra_data_size); break; case CRYPT: /* fallthrough */ case VERSION_: /* fallthrough */ case REDIR: /* fallthrough */ case UOWNER: /* fallthrough */ case SUBDATA: /* fallthrough */ default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown extra field in file/service block: 0x%02x", (int) extra_field_id); return ARCHIVE_FATAL; } } if(ret != ARCHIVE_OK) { /* Attribute not implemented. */ return ret; } return ARCHIVE_OK; } static int process_head_file(struct archive_read* a, struct rar5* rar, struct archive_entry* entry, size_t block_flags) { ssize_t extra_data_size = 0; size_t data_size, file_flags, file_attr, compression_info, host_os, name_size; uint64_t unpacked_size; uint32_t mtime = 0, crc; int c_method = 0, c_version = 0, is_dir; char name_utf8_buf[2048 * 4]; const uint8_t* p; - memset(entry, 0, sizeof(struct archive_entry)); + archive_entry_clear(entry); /* Do not reset file context if we're switching archives. */ if(!rar->cstate.switch_multivolume) { reset_file_context(rar); } if(block_flags & HFL_EXTRA_DATA) { size_t edata_size; if(!read_var_sized(a, &edata_size, NULL)) return ARCHIVE_EOF; /* Intentional type cast from unsigned to signed. */ extra_data_size = (ssize_t) edata_size; } if(block_flags & HFL_DATA) { if(!read_var_sized(a, &data_size, NULL)) return ARCHIVE_EOF; rar->file.bytes_remaining = data_size; } else { rar->file.bytes_remaining = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "no data found in file/service block"); return ARCHIVE_FATAL; } enum FILE_FLAGS { DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004, UNKNOWN_UNPACKED_SIZE = 0x0008, }; enum COMP_INFO_FLAGS { SOLID = 0x0040, }; if(!read_var_sized(a, &file_flags, NULL)) return ARCHIVE_EOF; if(!read_var(a, &unpacked_size, NULL)) return ARCHIVE_EOF; if(file_flags & UNKNOWN_UNPACKED_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Files with unknown unpacked size are not supported"); return ARCHIVE_FATAL; } is_dir = (int) (file_flags & DIRECTORY); if(!read_var_sized(a, &file_attr, NULL)) return ARCHIVE_EOF; if(file_flags & UTIME) { if(!read_u32(a, &mtime)) return ARCHIVE_EOF; } if(file_flags & CRC32) { if(!read_u32(a, &crc)) return ARCHIVE_EOF; } if(!read_var_sized(a, &compression_info, NULL)) return ARCHIVE_EOF; c_method = (int) (compression_info >> 7) & 0x7; c_version = (int) (compression_info & 0x3f); rar->cstate.window_size = is_dir ? 0 : g_unpack_window_size << ((compression_info >> 10) & 15); rar->cstate.method = c_method; rar->cstate.version = c_version + 50; rar->file.solid = (compression_info & SOLID) > 0; rar->file.service = 0; if(!read_var_sized(a, &host_os, NULL)) return ARCHIVE_EOF; enum HOST_OS { HOST_WINDOWS = 0, HOST_UNIX = 1, }; if(host_os == HOST_WINDOWS) { /* Host OS is Windows */ unsigned short mode = 0660; if(is_dir) mode |= AE_IFDIR; else mode |= AE_IFREG; archive_entry_set_mode(entry, mode); } else if(host_os == HOST_UNIX) { /* Host OS is Unix */ archive_entry_set_mode(entry, (unsigned short) file_attr); } else { /* Unknown host OS */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported Host OS: 0x%02x", (int) host_os); return ARCHIVE_FATAL; } if(!read_var_sized(a, &name_size, NULL)) return ARCHIVE_EOF; if(!read_ahead(a, name_size, &p)) return ARCHIVE_EOF; if(name_size > 2047) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Filename is too long"); return ARCHIVE_FATAL; } if(name_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "No filename specified"); return ARCHIVE_FATAL; } memcpy(name_utf8_buf, p, name_size); name_utf8_buf[name_size] = 0; if(ARCHIVE_OK != consume(a, name_size)) { return ARCHIVE_EOF; } if(extra_data_size > 0) { int ret = process_head_file_extra(a, entry, rar, extra_data_size); /* Sanity check. */ if(extra_data_size < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "File extra data size is not zero"); return ARCHIVE_FATAL; } if(ret != ARCHIVE_OK) return ret; } if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) { rar->file.unpacked_size = (ssize_t) unpacked_size; archive_entry_set_size(entry, unpacked_size); } if(file_flags & UTIME) { archive_entry_set_mtime(entry, (time_t) mtime, 0); } if(file_flags & CRC32) { rar->file.stored_crc32 = crc; } archive_entry_update_pathname_utf8(entry, name_utf8_buf); if(!rar->cstate.switch_multivolume) { /* Do not reinitialize unpacking state if we're switching archives. */ rar->cstate.block_parsing_finished = 1; rar->cstate.all_filters_applied = 1; rar->cstate.initialized = 0; } if(rar->generic.split_before > 0) { /* If now we're standing on a header that has a 'split before' mark, * it means we're standing on a 'continuation' file header. Signal * the caller that if it wants to move to another file, it must call * rar5_read_header() function again. */ return ARCHIVE_RETRY; } else { return ARCHIVE_OK; } } static int process_head_service(struct archive_read* a, struct rar5* rar, struct archive_entry* entry, size_t block_flags) { /* Process this SERVICE block the same way as FILE blocks. */ int ret = process_head_file(a, rar, entry, block_flags); if(ret != ARCHIVE_OK) return ret; rar->file.service = 1; /* But skip the data part automatically. It's no use for the user anyway. * It contains only service data, not even needed to properly unpack the * file. */ ret = rar5_read_data_skip(a); if(ret != ARCHIVE_OK) return ret; /* After skipping, try parsing another block automatically. */ return ARCHIVE_RETRY; } static int process_head_main(struct archive_read* a, struct rar5* rar, struct archive_entry* entry, size_t block_flags) { (void) entry; int ret; size_t extra_data_size, extra_field_size, extra_field_id, archive_flags; if(block_flags & HFL_EXTRA_DATA) { if(!read_var_sized(a, &extra_data_size, NULL)) return ARCHIVE_EOF; } else { extra_data_size = 0; } if(!read_var_sized(a, &archive_flags, NULL)) { return ARCHIVE_EOF; } enum MAIN_FLAGS { VOLUME = 0x0001, /* multi-volume archive */ VOLUME_NUMBER = 0x0002, /* volume number, first vol doesnt have it */ SOLID = 0x0004, /* solid archive */ PROTECT = 0x0008, /* contains Recovery info */ LOCK = 0x0010, /* readonly flag, not used */ }; rar->main.volume = (archive_flags & VOLUME) > 0; rar->main.solid = (archive_flags & SOLID) > 0; if(archive_flags & VOLUME_NUMBER) { size_t v; if(!read_var_sized(a, &v, NULL)) { return ARCHIVE_EOF; } rar->main.vol_no = (int) v; } else { rar->main.vol_no = 0; } if(rar->vol.expected_vol_no > 0 && rar->main.vol_no != rar->vol.expected_vol_no) { /* Returning EOF instead of FATAL because of strange libarchive * behavior. When opening multiple files via * archive_read_open_filenames(), after reading up the whole last file, * the __archive_read_ahead function wraps up to the first archive * instead of returning EOF. */ return ARCHIVE_EOF; } if(extra_data_size == 0) { /* Early return. */ return ARCHIVE_OK; } if(!read_var_sized(a, &extra_field_size, NULL)) { return ARCHIVE_EOF; } if(!read_var_sized(a, &extra_field_id, NULL)) { return ARCHIVE_EOF; } if(extra_field_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid extra field size"); return ARCHIVE_FATAL; } enum MAIN_EXTRA { // Just one attribute here. LOCATOR = 0x01, }; switch(extra_field_id) { case LOCATOR: ret = process_main_locator_extra_block(a, rar); if(ret != ARCHIVE_OK) { /* Error while parsing main locator extra block. */ return ret; } break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported extra type (0x%02x)", (int) extra_field_id); return ARCHIVE_FATAL; } return ARCHIVE_OK; } static int scan_for_signature(struct archive_read* a); /* Base block processing function. A 'base block' is a RARv5 header block * that tells the reader what kind of data is stored inside the block. * * From the birds-eye view a RAR file looks file this: * * ... * * There are a few types of base blocks. Those types are specified inside * the 'switch' statement in this function. For example purposes, I'll write * how a standard RARv5 file could look like here: * *
* * The structure above could describe an archive file with 3 files in it, * one service "QuickOpen" block (that is ignored by this parser), and an * end of file base block marker. * * If the file is stored in multiple archive files ("multiarchive"), it might * look like this: * * .part01.rar:
* .part02.rar:
* .part03.rar:
* * This example could describe 3 RAR files that contain ONE archived file. * Or it could describe 3 RAR files that contain 3 different files. Or 3 * RAR files than contain 2 files. It all depends what metadata is stored in * the headers of blocks. * * Each block contains info about its size, the name of the file it's * storing inside, and whether this FILE block is a continuation block of * previous archive ('split before'), and is this FILE block should be * continued in another archive ('split after'). By parsing the 'split before' * and 'split after' flags, we're able to tell if multiple base blocks * are describing one file, or multiple files (with the same filename, for * example). * * One thing to note is that if we're parsing the first block, and * we see 'split after' flag, then we need to jump over to another * block to be able to decompress rest of the data. To do this, we need * to skip the block, then switch to another file, then skip the * block,
block, and then we're standing on the proper * block. */ static int process_base_block(struct archive_read* a, struct archive_entry* entry) { struct rar5* rar = get_context(a); uint32_t hdr_crc, computed_crc; size_t raw_hdr_size, hdr_size_len, hdr_size; size_t header_id, header_flags; const uint8_t* p; int ret; /* Skip any unprocessed data for this file. */ if(rar->file.bytes_remaining) { ret = rar5_read_data_skip(a); if(ret != ARCHIVE_OK) { return ret; } } /* Read the expected CRC32 checksum. */ if(!read_u32(a, &hdr_crc)) { return ARCHIVE_EOF; } /* Read header size. */ if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) { return ARCHIVE_EOF; } /* Sanity check, maximum header size for RAR5 is 2MB. */ if(raw_hdr_size > (2 * 1024 * 1024)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Base block header is too large"); return ARCHIVE_FATAL; } hdr_size = raw_hdr_size + hdr_size_len; /* Read the whole header data into memory, maximum memory use here is * 2MB. */ if(!read_ahead(a, hdr_size, &p)) { return ARCHIVE_EOF; } /* Verify the CRC32 of the header data. */ computed_crc = (uint32_t) crc32(0, p, (int) hdr_size); if(computed_crc != hdr_crc) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header CRC error"); return ARCHIVE_FATAL; } /* If the checksum is OK, we proceed with parsing. */ if(ARCHIVE_OK != consume(a, hdr_size_len)) { return ARCHIVE_EOF; } if(!read_var_sized(a, &header_id, NULL)) return ARCHIVE_EOF; if(!read_var_sized(a, &header_flags, NULL)) return ARCHIVE_EOF; rar->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0; rar->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0; rar->generic.size = hdr_size; rar->generic.last_header_id = header_id; rar->main.endarc = 0; /* Those are possible header ids in RARv5. */ enum HEADER_TYPE { HEAD_MARK = 0x00, HEAD_MAIN = 0x01, HEAD_FILE = 0x02, HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05, HEAD_UNKNOWN = 0xff, }; switch(header_id) { case HEAD_MAIN: ret = process_head_main(a, rar, entry, header_flags); /* Main header doesn't have any files in it, so it's pointless * to return to the caller. Retry to next header, which should be * HEAD_FILE/HEAD_SERVICE. */ if(ret == ARCHIVE_OK) return ARCHIVE_RETRY; return ret; case HEAD_SERVICE: ret = process_head_service(a, rar, entry, header_flags); return ret; case HEAD_FILE: ret = process_head_file(a, rar, entry, header_flags); return ret; case HEAD_CRYPT: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Encryption is not supported"); return ARCHIVE_FATAL; case HEAD_ENDARC: rar->main.endarc = 1; /* After encountering an end of file marker, we need to take * into consideration if this archive is continued in another * file (i.e. is it part01.rar: is there a part02.rar?) */ if(rar->main.volume) { /* In case there is part02.rar, position the read pointer * in a proper place, so we can resume parsing. */ ret = scan_for_signature(a); if(ret == ARCHIVE_FATAL) { return ARCHIVE_EOF; } else { rar->vol.expected_vol_no = rar->main.vol_no + 1; return ARCHIVE_OK; } } else { return ARCHIVE_EOF; } case HEAD_MARK: return ARCHIVE_EOF; default: if((header_flags & HFL_SKIP_IF_UNKNOWN) == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header type error"); return ARCHIVE_FATAL; } else { /* If the block is marked as 'skip if unknown', do as the flag * says: skip the block instead on failing on it. */ return ARCHIVE_RETRY; } } #if !defined WIN32 // Not reached. archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Internal unpacker error"); return ARCHIVE_FATAL; #endif } static int skip_base_block(struct archive_read* a) { int ret; struct rar5* rar = get_context(a); - struct archive_entry entry; - ret = process_base_block(a, &entry); + /* Create a new local archive_entry structure that will be operated on + * by header reader; operations on this archive_entry will be discarded. + */ + struct archive_entry* entry = archive_entry_new(); + ret = process_base_block(a, entry); + /* Discard operations on this archive_entry structure. */ + archive_entry_free(entry); + if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0) return ARCHIVE_OK; if(ret == ARCHIVE_OK) return ARCHIVE_RETRY; else return ret; } static int rar5_read_header(struct archive_read *a, struct archive_entry *entry) { struct rar5* rar = get_context(a); int ret; if(rar->header_initialized == 0) { init_header(a); rar->header_initialized = 1; } if(rar->skipped_magic == 0) { if(ARCHIVE_OK != consume(a, rar5_signature_size)) { return ARCHIVE_EOF; } rar->skipped_magic = 1; } do { ret = process_base_block(a, entry); } while(ret == ARCHIVE_RETRY || (rar->main.endarc > 0 && ret == ARCHIVE_OK)); return ret; } static void init_unpack(struct rar5* rar) { rar->file.calculated_crc32 = 0; - rar->cstate.window_mask = rar->cstate.window_size - 1; + if (rar->cstate.window_size) + rar->cstate.window_mask = rar->cstate.window_size - 1; + else + rar->cstate.window_mask = 0; - if(rar->cstate.window_buf) - free(rar->cstate.window_buf); + free(rar->cstate.window_buf); - if(rar->cstate.filtered_buf) - free(rar->cstate.filtered_buf); + free(rar->cstate.filtered_buf); rar->cstate.window_buf = calloc(1, rar->cstate.window_size); rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size); rar->cstate.write_ptr = 0; rar->cstate.last_write_ptr = 0; memset(&rar->cstate.bd, 0, sizeof(rar->cstate.bd)); memset(&rar->cstate.ld, 0, sizeof(rar->cstate.ld)); memset(&rar->cstate.dd, 0, sizeof(rar->cstate.dd)); memset(&rar->cstate.ldd, 0, sizeof(rar->cstate.ldd)); memset(&rar->cstate.rd, 0, sizeof(rar->cstate.rd)); } static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) { int verify_crc; if(rar->skip_mode) { #if defined CHECK_CRC_ON_SOLID_SKIP verify_crc = 1; #else verify_crc = 0; #endif } else verify_crc = 1; if(verify_crc) { /* Don't update CRC32 if the file doesn't have the `stored_crc32` info filled in. */ if(rar->file.stored_crc32 > 0) { rar->file.calculated_crc32 = crc32(rar->file.calculated_crc32, p, to_read); } /* Check if the file uses an optional BLAKE2sp checksum algorithm. */ if(rar->file.has_blake2 > 0) { /* Return value of the `update` function is always 0, so we can * explicitly ignore it here. */ (void) blake2sp_update(&rar->file.b2state, p, to_read); } } } static int create_decode_tables(uint8_t* bit_length, struct decode_table* table, int size) { int code, upper_limit = 0, i, lc[16]; uint32_t decode_pos_clone[rar5_countof(table->decode_pos)]; ssize_t cur_len, quick_data_size; memset(&lc, 0, sizeof(lc)); memset(table->decode_num, 0, sizeof(table->decode_num)); table->size = size; table->quick_bits = size == HUFF_NC ? 10 : 7; for(i = 0; i < size; i++) { lc[bit_length[i] & 15]++; } lc[0] = 0; table->decode_pos[0] = 0; table->decode_len[0] = 0; for(i = 1; i < 16; i++) { upper_limit += lc[i]; table->decode_len[i] = upper_limit << (16 - i); table->decode_pos[i] = table->decode_pos[i - 1] + lc[i - 1]; upper_limit <<= 1; } memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone)); for(i = 0; i < size; i++) { uint8_t clen = bit_length[i] & 15; if(clen > 0) { int last_pos = decode_pos_clone[clen]; table->decode_num[last_pos] = i; decode_pos_clone[clen]++; } } quick_data_size = 1 << table->quick_bits; cur_len = 1; for(code = 0; code < quick_data_size; code++) { int bit_field = code << (16 - table->quick_bits); int dist, pos; while(cur_len < rar5_countof(table->decode_len) && bit_field >= table->decode_len[cur_len]) { cur_len++; } table->quick_len[code] = (uint8_t) cur_len; dist = bit_field - table->decode_len[cur_len - 1]; dist >>= (16 - cur_len); pos = table->decode_pos[cur_len] + dist; if(cur_len < rar5_countof(table->decode_pos) && pos < size) { table->quick_num[code] = table->decode_num[pos]; } else { table->quick_num[code] = 0; } } return ARCHIVE_OK; } static int decode_number(struct archive_read* a, struct decode_table* table, const uint8_t* p, uint16_t* num) { int i, bits, dist; uint16_t bitfield; uint32_t pos; struct rar5* rar = get_context(a); if(ARCHIVE_OK != read_bits_16(rar, p, &bitfield)) { return ARCHIVE_EOF; } bitfield &= 0xfffe; if(bitfield < table->decode_len[table->quick_bits]) { int code = bitfield >> (16 - table->quick_bits); skip_bits(rar, table->quick_len[code]); *num = table->quick_num[code]; return ARCHIVE_OK; } bits = 15; for(i = table->quick_bits + 1; i < 15; i++) { if(bitfield < table->decode_len[i]) { bits = i; break; } } skip_bits(rar, bits); dist = bitfield - table->decode_len[bits - 1]; dist >>= (16 - bits); pos = table->decode_pos[bits] + dist; if(pos >= table->size) pos = 0; *num = table->decode_num[pos]; return ARCHIVE_OK; } /* Reads and parses Huffman tables from the beginning of the block. */ static int parse_tables(struct archive_read* a, struct rar5* rar, const uint8_t* p) { int ret, value, i, w, idx = 0; uint8_t bit_length[HUFF_BC], table[HUFF_TABLE_SIZE], nibble_mask = 0xF0, nibble_shift = 4; enum { ESCAPE = 15 }; /* The data for table generation is compressed using a simple RLE-like * algorithm when storing zeroes, so we need to unpack it first. */ for(w = 0, i = 0; w < HUFF_BC;) { value = (p[i] & nibble_mask) >> nibble_shift; if(nibble_mask == 0x0F) ++i; nibble_mask ^= 0xFF; nibble_shift ^= 4; /* Values smaller than 15 is data, so we write it directly. Value 15 * is a flag telling us that we need to unpack more bytes. */ if(value == ESCAPE) { value = (p[i] & nibble_mask) >> nibble_shift; if(nibble_mask == 0x0F) ++i; nibble_mask ^= 0xFF; nibble_shift ^= 4; if(value == 0) { /* We sometimes need to write the actual value of 15, so this * case handles that. */ bit_length[w++] = ESCAPE; } else { int k; /* Fill zeroes. */ for(k = 0; k < value + 2; k++) { bit_length[w++] = 0; } } } else { bit_length[w++] = value; } } rar->bits.in_addr = i; rar->bits.bit_addr = nibble_shift ^ 4; ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Decoding huffman tables failed"); return ARCHIVE_FATAL; } for(i = 0; i < HUFF_TABLE_SIZE;) { uint16_t num; ret = decode_number(a, &rar->cstate.bd, p, &num); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Decoding huffman tables failed"); return ARCHIVE_FATAL; } if(num < 16) { /* 0..15: store directly */ table[i] = (uint8_t) num; i++; continue; } if(num < 18) { /* 16..17: repeat previous code */ uint16_t n; if(ARCHIVE_OK != read_bits_16(rar, p, &n)) return ARCHIVE_EOF; if(num == 16) { n >>= 13; n += 3; skip_bits(rar, 3); } else { n >>= 9; n += 11; skip_bits(rar, 7); } if(i > 0) { while(n-- > 0 && i < HUFF_TABLE_SIZE) { table[i] = table[i - 1]; i++; } } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unexpected error when decoding huffman tables"); return ARCHIVE_FATAL; } continue; } /* other codes: fill with zeroes `n` times */ uint16_t n; if(ARCHIVE_OK != read_bits_16(rar, p, &n)) return ARCHIVE_EOF; if(num == 18) { n >>= 13; n += 3; skip_bits(rar, 3); } else { n >>= 9; n += 11; skip_bits(rar, 7); } while(n-- > 0 && i < HUFF_TABLE_SIZE) table[i++] = 0; } ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create literal table"); return ARCHIVE_FATAL; } idx += HUFF_NC; ret = create_decode_tables(&table[idx], &rar->cstate.dd, HUFF_DC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create distance table"); return ARCHIVE_FATAL; } idx += HUFF_DC; ret = create_decode_tables(&table[idx], &rar->cstate.ldd, HUFF_LDC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create lower bits of distances table"); return ARCHIVE_FATAL; } idx += HUFF_LDC; ret = create_decode_tables(&table[idx], &rar->cstate.rd, HUFF_RC); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Failed to create repeating distances table"); return ARCHIVE_FATAL; } return ARCHIVE_OK; } /* Parses the block header, verifies its CRC byte, and saves the header * fields inside the `hdr` pointer. */ static int parse_block_header(struct archive_read* a, const uint8_t* p, ssize_t* block_size, struct compressed_block_header* hdr) { memcpy(hdr, p, sizeof(struct compressed_block_header)); if(hdr->block_flags.byte_count > 2) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported block header size (was %d, max is 2)", hdr->block_flags.byte_count); return ARCHIVE_FATAL; } /* This should probably use bit reader interface in order to be more * future-proof. */ *block_size = 0; switch(hdr->block_flags.byte_count) { /* 1-byte block size */ case 0: *block_size = *(const uint8_t*) &p[2]; break; /* 2-byte block size */ case 1: *block_size = *(const uint16_t*) &p[2]; break; /* 3-byte block size */ case 2: *block_size = *(const uint32_t*) &p[2]; *block_size &= 0x00FFFFFF; break; /* Other block sizes are not supported. This case is not reached, * because we have an 'if' guard before the switch that makes sure * of it. */ default: return ARCHIVE_FATAL; } /* Verify the block header checksum. 0x5A is a magic value and is always * constant. */ uint8_t calculated_cksum = 0x5A ^ (uint8_t) hdr->block_flags_u8 ^ (uint8_t) *block_size ^ (uint8_t) (*block_size >> 8) ^ (uint8_t) (*block_size >> 16); if(calculated_cksum != hdr->block_cksum) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Block checksum error: got 0x%02x, expected 0x%02x", hdr->block_cksum, calculated_cksum); return ARCHIVE_FATAL; } return ARCHIVE_OK; } /* Convinience function used during filter processing. */ static int parse_filter_data(struct rar5* rar, const uint8_t* p, uint32_t* filter_data) { int i, bytes; uint32_t data = 0; if(ARCHIVE_OK != read_consume_bits(rar, p, 2, &bytes)) return ARCHIVE_EOF; bytes++; for(i = 0; i < bytes; i++) { uint16_t byte; if(ARCHIVE_OK != read_bits_16(rar, p, &byte)) { return ARCHIVE_EOF; } data += (byte >> 8) << (i * 8); skip_bits(rar, 8); } *filter_data = data; return ARCHIVE_OK; } /* Function is used during sanity checking. */ static int is_valid_filter_block_start(struct rar5* rar, uint32_t start) { const int64_t block_start = (ssize_t) start + rar->cstate.write_ptr; const int64_t last_bs = rar->cstate.last_block_start; const ssize_t last_bl = rar->cstate.last_block_length; if(last_bs == 0 || last_bl == 0) { /* We didn't have any filters yet, so accept this offset. */ return 1; } if(block_start >= last_bs + last_bl) { /* Current offset is bigger than last block's end offset, so * accept current offset. */ return 1; } /* Any other case is not a normal situation and we should fail. */ return 0; } /* The function will create a new filter, read its parameters from the input * stream and add it to the filter collection. */ static int parse_filter(struct archive_read* ar, const uint8_t* p) { uint32_t block_start, block_length; uint16_t filter_type; struct rar5* rar = get_context(ar); /* Read the parameters from the input stream. */ if(ARCHIVE_OK != parse_filter_data(rar, p, &block_start)) return ARCHIVE_EOF; if(ARCHIVE_OK != parse_filter_data(rar, p, &block_length)) return ARCHIVE_EOF; if(ARCHIVE_OK != read_bits_16(rar, p, &filter_type)) return ARCHIVE_EOF; filter_type >>= 13; skip_bits(rar, 3); /* Perform some sanity checks on this filter parameters. Note that we * allow only DELTA, E8/E9 and ARM filters here, because rest of filters * are not used in RARv5. */ if(block_length < 4 || block_length > 0x400000 || filter_type > FILTER_ARM || !is_valid_filter_block_start(rar, block_start)) { archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid " "filter encountered"); return ARCHIVE_FATAL; } /* Allocate a new filter. */ struct filter_info* filt = add_new_filter(rar); if(filt == NULL) { archive_set_error(&ar->archive, ENOMEM, "Can't allocate memory for a " "filter descriptor."); return ARCHIVE_FATAL; } filt->type = filter_type; filt->block_start = rar->cstate.write_ptr + block_start; filt->block_length = block_length; rar->cstate.last_block_start = filt->block_start; rar->cstate.last_block_length = filt->block_length; /* Read some more data in case this is a DELTA filter. Other filter types * don't require any additional data over what was already read. */ if(filter_type == FILTER_DELTA) { int channels; if(ARCHIVE_OK != read_consume_bits(rar, p, 5, &channels)) return ARCHIVE_EOF; filt->channels = channels + 1; } return ARCHIVE_OK; } static int decode_code_length(struct rar5* rar, const uint8_t* p, uint16_t code) { int lbits, length = 2; if(code < 8) { lbits = 0; length += code; } else { lbits = code / 4 - 1; length += (4 | (code & 3)) << lbits; } if(lbits > 0) { int add; if(ARCHIVE_OK != read_consume_bits(rar, p, lbits, &add)) return -1; length += add; } return length; } static int copy_string(struct archive_read* a, int len, int dist) { struct rar5* rar = get_context(a); const int cmask = rar->cstate.window_mask; const int64_t write_ptr = rar->cstate.write_ptr + rar->cstate.solid_offset; int i; /* The unpacker spends most of the time in this function. It would be * a good idea to introduce some optimizations here. * * Just remember that this loop treats buffers that overlap differently * than buffers that do not overlap. This is why a simple memcpy(3) call * will not be enough. */ for(i = 0; i < len; i++) { const ssize_t write_idx = (write_ptr + i) & cmask; const ssize_t read_idx = (write_ptr + i - dist) & cmask; rar->cstate.window_buf[write_idx] = rar->cstate.window_buf[read_idx]; } rar->cstate.write_ptr += len; return ARCHIVE_OK; } static int do_uncompress_block(struct archive_read* a, const uint8_t* p) { struct rar5* rar = get_context(a); uint16_t num; int ret; const int cmask = rar->cstate.window_mask; const struct compressed_block_header* hdr = &rar->last_block_hdr; const uint8_t bit_size = 1 + hdr->block_flags.bit_size; while(1) { if(rar->cstate.write_ptr - rar->cstate.last_write_ptr > (rar->cstate.window_size >> 1)) { /* Don't allow growing data by more than half of the window size * at a time. In such case, break the loop; next call to this * function will continue processing from this moment. */ break; } if(rar->bits.in_addr > rar->cstate.cur_block_size - 1 || (rar->bits.in_addr == rar->cstate.cur_block_size - 1 && rar->bits.bit_addr >= bit_size)) { /* If the program counter is here, it means the function has * finished processing the block. */ rar->cstate.block_parsing_finished = 1; break; } /* Decode the next literal. */ if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) { return ARCHIVE_EOF; } /* Num holds a decompression literal, or 'command code'. * * - Values lower than 256 are just bytes. Those codes can be stored * in the output buffer directly. * * - Code 256 defines a new filter, which is later used to transform * the data block accordingly to the filter type. The data block * needs to be fully uncompressed first. * * - Code bigger than 257 and smaller than 262 define a repetition * pattern that should be copied from an already uncompressed chunk * of data. */ if(num < 256) { /* Directly store the byte. */ int64_t write_idx = rar->cstate.solid_offset + rar->cstate.write_ptr++; rar->cstate.window_buf[write_idx & cmask] = (uint8_t) num; continue; } else if(num >= 262) { uint16_t dist_slot; int len = decode_code_length(rar, p, num - 262), dbits, dist = 1; if(len == -1) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Failed to decode the code length"); return ARCHIVE_FATAL; } if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p, &dist_slot)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Failed to decode the distance slot"); return ARCHIVE_FATAL; } if(dist_slot < 4) { dbits = 0; dist += dist_slot; } else { dbits = dist_slot / 2 - 1; dist += (2 | (dist_slot & 1)) << dbits; } if(dbits > 0) { if(dbits >= 4) { uint32_t add = 0; uint16_t low_dist; if(dbits > 4) { if(ARCHIVE_OK != read_bits_32(rar, p, &add)) { /* Return EOF if we can't read more data. */ return ARCHIVE_EOF; } skip_bits(rar, dbits - 4); add = (add >> (36 - dbits)) << 4; dist += add; } if(ARCHIVE_OK != decode_number(a, &rar->cstate.ldd, p, &low_dist)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Failed to decode the distance slot"); return ARCHIVE_FATAL; } dist += low_dist; } else { /* dbits is one of [0,1,2,3] */ int add; if(ARCHIVE_OK != read_consume_bits(rar, p, dbits, &add)) { /* Return EOF if we can't read more data. */ return ARCHIVE_EOF; } dist += add; } } if(dist > 0x100) { len++; if(dist > 0x2000) { len++; if(dist > 0x40000) { len++; } } } dist_cache_push(rar, dist); rar->cstate.last_len = len; if(ARCHIVE_OK != copy_string(a, len, dist)) return ARCHIVE_FATAL; continue; } else if(num == 256) { /* Create a filter. */ ret = parse_filter(a, p); if(ret != ARCHIVE_OK) return ret; continue; } else if(num == 257) { if(rar->cstate.last_len != 0) { if(ARCHIVE_OK != copy_string(a, rar->cstate.last_len, rar->cstate.dist_cache[0])) { return ARCHIVE_FATAL; } } continue; } else if(num < 262) { const int index = num - 258; const int dist = dist_cache_touch(rar, index); uint16_t len_slot; int len; if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p, &len_slot)) { return ARCHIVE_FATAL; } len = decode_code_length(rar, p, len_slot); rar->cstate.last_len = len; if(ARCHIVE_OK != copy_string(a, len, dist)) return ARCHIVE_FATAL; continue; } /* The program counter shouldn't reach here. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported block code: 0x%02x", num); return ARCHIVE_FATAL; } return ARCHIVE_OK; } /* Binary search for the RARv5 signature. */ static int scan_for_signature(struct archive_read* a) { const uint8_t* p; const int chunk_size = 512; ssize_t i; /* If we're here, it means we're on an 'unknown territory' data. * There's no indication what kind of data we're reading here. It could be * some text comment, any kind of binary data, digital sign, dragons, etc. * * We want to find a valid RARv5 magic header inside this unknown data. */ /* Is it possible in libarchive to just skip everything until the * end of the file? If so, it would be a better approach than the * current implementation of this function. */ while(1) { if(!read_ahead(a, chunk_size, &p)) return ARCHIVE_EOF; for(i = 0; i < chunk_size - rar5_signature_size; i++) { if(memcmp(&p[i], rar5_signature, rar5_signature_size) == 0) { /* Consume the number of bytes we've used to search for the * signature, as well as the number of bytes used by the * signature itself. After this we should be standing on a * valid base block header. */ (void) consume(a, i + rar5_signature_size); return ARCHIVE_OK; } } consume(a, chunk_size); } return ARCHIVE_FATAL; } /* This function will switch the multivolume archive file to another file, * i.e. from part03 to part 04. */ static int advance_multivolume(struct archive_read* a) { int lret; struct rar5* rar = get_context(a); /* A small state machine that will skip unnecessary data, needed to * switch from one multivolume to another. Such skipping is needed if * we want to be an stream-oriented (instead of file-oriented) * unpacker. * * The state machine starts with `rar->main.endarc` == 0. It also * assumes that current stream pointer points to some base block header. * * The `endarc` field is being set when the base block parsing function * encounters the 'end of archive' marker. */ while(1) { if(rar->main.endarc == 1) { rar->main.endarc = 0; while(ARCHIVE_RETRY == skip_base_block(a)); break; } else { /* Skip current base block. In order to properly skip it, * we really need to simply parse it and discard the results. */ lret = skip_base_block(a); /* The `skip_base_block` function tells us if we should continue * with skipping, or we should stop skipping. We're trying to skip * everything up to a base FILE block. */ if(lret != ARCHIVE_RETRY) { /* If there was an error during skipping, or we have just * skipped a FILE base block... */ if(rar->main.endarc == 0) { return lret; } else { continue; } } } } return ARCHIVE_OK; } /* Merges the partial block from the first multivolume archive file, and * partial block from the second multivolume archive file. The result is * a chunk of memory containing the whole block, and the stream pointer * is advanced to the next block in the second multivolume archive file. */ static int merge_block(struct archive_read* a, ssize_t block_size, const uint8_t** p) { struct rar5* rar = get_context(a); ssize_t cur_block_size, partial_offset = 0; const uint8_t* lp; int ret; /* Set a flag that we're in the switching mode. */ rar->cstate.switch_multivolume = 1; /* Reallocate the memory which will hold the whole block. */ if(rar->vol.push_buf) free((void*) rar->vol.push_buf); - rar->vol.push_buf = malloc(block_size); + /* Increasing the allocation block by 8 is due to bit reading functions, + * which are using additional 2 or 4 bytes. Allocating the block size + * by exact value would make bit reader perform reads from invalid memory + * block when reading the last byte from the buffer. */ + rar->vol.push_buf = malloc(block_size + 8); if(!rar->vol.push_buf) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for a " "merge block buffer."); return ARCHIVE_FATAL; } + /* Valgrind complains if the extension block for bit reader is not + * initialized, so initialize it. */ + memset(&rar->vol.push_buf[block_size], 0, 8); + /* A single block can span across multiple multivolume archive files, * so we use a loop here. This loop will consume enough multivolume * archive files until the whole block is read. */ while(1) { /* Get the size of current block chunk in this multivolume archive * file and read it. */ cur_block_size = rar5_min(rar->file.bytes_remaining, block_size - partial_offset); if(!read_ahead(a, cur_block_size, &lp)) return ARCHIVE_EOF; /* Sanity check; there should never be a situation where this function * reads more data than the block's size. */ if(partial_offset + cur_block_size > block_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Consumed too much data when merging blocks."); return ARCHIVE_FATAL; } /* Merge previous block chunk with current block chunk, or create * first block chunk if this is our first iteration. */ memcpy(&rar->vol.push_buf[partial_offset], lp, cur_block_size); /* Advance the stream read pointer by this block chunk size. */ if(ARCHIVE_OK != consume(a, cur_block_size)) return ARCHIVE_EOF; /* Update the pointers. `partial_offset` contains information about * the sum of merged block chunks. */ partial_offset += cur_block_size; rar->file.bytes_remaining -= cur_block_size; /* If `partial_offset` is the same as `block_size`, this means we've * merged all block chunks and we have a valid full block. */ if(partial_offset == block_size) { break; } /* If we don't have any bytes to read, this means we should switch * to another multivolume archive file. */ if(rar->file.bytes_remaining == 0) { ret = advance_multivolume(a); if(ret != ARCHIVE_OK) return ret; } } *p = rar->vol.push_buf; /* If we're here, we can resume unpacking by processing the block pointed * to by the `*p` memory pointer. */ return ARCHIVE_OK; } static int process_block(struct archive_read* a) { const uint8_t* p; struct rar5* rar = get_context(a); int ret; /* If we don't have any data to be processed, this most probably means * we need to switch to the next volume. */ if(rar->main.volume && rar->file.bytes_remaining == 0) { ret = advance_multivolume(a); if(ret != ARCHIVE_OK) return ret; } if(rar->cstate.block_parsing_finished) { ssize_t block_size; rar->cstate.block_parsing_finished = 0; /* The header size won't be bigger than 6 bytes. */ if(!read_ahead(a, 6, &p)) { /* Failed to prefetch data block header. */ return ARCHIVE_EOF; } /* * Read block_size by parsing block header. Validate the header by * calculating CRC byte stored inside the header. Size of the header is * not constant (block size can be stored either in 1 or 2 bytes), * that's why block size is left out from the `compressed_block_header` * structure and returned by `parse_block_header` as the second * argument. */ ret = parse_block_header(a, p, &block_size, &rar->last_block_hdr); if(ret != ARCHIVE_OK) return ret; /* Skip block header. Next data is huffman tables, if present. */ ssize_t to_skip = sizeof(struct compressed_block_header) + rar->last_block_hdr.block_flags.byte_count + 1; if(ARCHIVE_OK != consume(a, to_skip)) return ARCHIVE_EOF; rar->file.bytes_remaining -= to_skip; /* The block size gives information about the whole block size, but * the block could be stored in split form when using multi-volume * archives. In this case, the block size will be bigger than the * actual data stored in this file. Remaining part of the data will * be in another file. */ ssize_t cur_block_size = rar5_min(rar->file.bytes_remaining, block_size); if(block_size > rar->file.bytes_remaining) { /* If current blocks' size is bigger than our data size, this * means we have a multivolume archive. In this case, skip * all base headers until the end of the file, proceed to next * "partXXX.rar" volume, find its signature, skip all headers up * to the first FILE base header, and continue from there. * * Note that `merge_block` will update the `rar` context structure * quite extensively. */ ret = merge_block(a, block_size, &p); if(ret != ARCHIVE_OK) { return ret; } cur_block_size = block_size; /* Current stream pointer should be now directly *after* the * block that spanned through multiple archive files. `p` pointer * should have the data of the *whole* block (merged from * partial blocks stored in multiple archives files). */ } else { rar->cstate.switch_multivolume = 0; /* Read the whole block size into memory. This can take up to * 8 megabytes of memory in theoretical cases. Might be worth to * optimize this and use a standard chunk of 4kb's. */ if(!read_ahead(a, 4 + cur_block_size, &p)) { /* Failed to prefetch block data. */ return ARCHIVE_EOF; } } rar->cstate.block_buf = p; rar->cstate.cur_block_size = cur_block_size; rar->bits.in_addr = 0; rar->bits.bit_addr = 0; if(rar->last_block_hdr.block_flags.is_table_present) { /* Load Huffman tables. */ ret = parse_tables(a, rar, p); if(ret != ARCHIVE_OK) { /* Error during decompression of Huffman tables. */ return ret; } } } else { p = rar->cstate.block_buf; } /* Uncompress the block, or a part of it, depending on how many bytes * will be generated by uncompressing the block. * * In case too many bytes will be generated, calling this function again * will resume the uncompression operation. */ ret = do_uncompress_block(a, p); if(ret != ARCHIVE_OK) { return ret; } if(rar->cstate.block_parsing_finished && rar->cstate.switch_multivolume == 0 && rar->cstate.cur_block_size > 0) { /* If we're processing a normal block, consume the whole block. We * can do this because we've already read the whole block to memory. */ if(ARCHIVE_OK != consume(a, rar->cstate.cur_block_size)) return ARCHIVE_FATAL; rar->file.bytes_remaining -= rar->cstate.cur_block_size; } else if(rar->cstate.switch_multivolume) { /* Don't consume the block if we're doing multivolume processing. * The volume switching function will consume the proper count of * bytes instead. */ rar->cstate.switch_multivolume = 0; } return ARCHIVE_OK; } /* Pops the `buf`, `size` and `offset` from the "data ready" stack. * * Returns ARCHIVE_OK when those arguments can be used, ARCHIVE_RETRY * when there is no data on the stack. */ static int use_data(struct rar5* rar, const void** buf, size_t* size, int64_t* offset) { int i; for(i = 0; i < rar5_countof(rar->cstate.dready); i++) { struct data_ready *d = &rar->cstate.dready[i]; if(d->used) { if(buf) *buf = d->buf; if(size) *size = d->size; if(offset) *offset = d->offset; d->used = 0; return ARCHIVE_OK; } } return ARCHIVE_RETRY; } /* Pushes the `buf`, `size` and `offset` arguments to the rar->cstate.dready * FIFO stack. Those values will be popped from this stack by the `use_data` * function. */ static int push_data_ready(struct archive_read* a, struct rar5* rar, const uint8_t* buf, size_t size, int64_t offset) { int i; /* Don't push if we're in skip mode. This is needed because solid * streams need full processing even if we're skipping data. After fully * processing the stream, we need to discard the generated bytes, because * we're interested only in the side effect: building up the internal * window circular buffer. This window buffer will be used later during * unpacking of requested data. */ if(rar->skip_mode) return ARCHIVE_OK; /* Sanity check. */ if(offset != rar->file.last_offset + rar->file.last_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Sanity " "check error: output stream is not continuous"); return ARCHIVE_FATAL; } for(i = 0; i < rar5_countof(rar->cstate.dready); i++) { struct data_ready* d = &rar->cstate.dready[i]; if(!d->used) { d->used = 1; d->buf = buf; d->size = size; d->offset = offset; /* These fields are used only in sanity checking. */ rar->file.last_offset = offset; rar->file.last_size = size; /* Calculate the checksum of this new block before submitting * data to libarchive's engine. */ update_crc(rar, d->buf, d->size); return ARCHIVE_OK; } } /* Program counter will reach this code if the `rar->cstate.data_ready` * stack will be filled up so that no new entries will be allowed. The * code shouldn't allow such situation to occur. So we treat this case * as an internal error. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Error: " "premature end of data_ready stack"); return ARCHIVE_FATAL; } /* This function uncompresses the data that is stored in the base * block. * * The FILE base block looks like this: * *
... * * The
is a block header, that is parsed in parse_block_header(). * It's a "compressed_block_header" structure, containing metadata needed * to know when we should stop looking for more blocks. * * contain data needed to set up the huffman tables, needed * for the actual decompression. * * Each consists of series of literals: * * ... * * Those literals generate the uncompression data. They operate on a circular * buffer, sometimes writing raw data into it, sometimes referencing * some previous data inside this buffer, and sometimes declaring a filter * that will need to be executed on the data stored in the circular buffer. * It all depends on the literal that is used. * * Sometimes blocks produce output data, sometimes they don't. For example, for * some huge files that use lots of filters, sometimes a block is filled with * only filter declaration literals. Such blocks won't produce any data in the * circular buffer. * * Sometimes blocks will produce 4 bytes of data, and sometimes 1 megabyte, * because a literal can reference previously decompressed data. For example, * there can be a literal that says: 'append a byte 0xFE here', and after * it another literal can say 'append 1 megabyte of data from circular buffer * offset 0x12345'. This is how RAR format handles compressing repeated * patterns. * * The RAR compressor creates those literals and the actual efficiency of * compression depends on what those literals are. The literals can also * be seen as a kind of a non-turing-complete virtual machine that simply * tells the decompressor what it should do. * */ static int do_uncompress_file(struct archive_read* a) { struct rar5* rar = get_context(a); int ret; int64_t max_end_pos; if(!rar->cstate.initialized) { /* Don't perform full context reinitialization if we're processing * a solid archive. */ if(!rar->main.solid || !rar->cstate.window_buf) { init_unpack(rar); } rar->cstate.initialized = 1; } if(rar->cstate.all_filters_applied == 1) { /* We use while(1) here, but standard case allows for just 1 iteration. * The loop will iterate if process_block() didn't generate any data at * all. This can happen if the block contains only filter definitions * (this is common in big files). */ while(1) { ret = process_block(a); if(ret == ARCHIVE_EOF || ret == ARCHIVE_FATAL) return ret; if(rar->cstate.last_write_ptr == rar->cstate.write_ptr) { /* The block didn't generate any new data, so just process * a new block. */ continue; } /* The block has generated some new data, so break the loop. */ break; } } /* Try to run filters. If filters won't be applied, it means that * insufficient data was generated. */ ret = apply_filters(a); if(ret == ARCHIVE_RETRY) { return ARCHIVE_OK; } else if(ret == ARCHIVE_FATAL) { return ARCHIVE_FATAL; } /* If apply_filters() will return ARCHIVE_OK, we can continue here. */ if(cdeque_size(&rar->cstate.filters) > 0) { /* Check if we can write something before hitting first filter. */ struct filter_info* flt; /* Get the block_start offset from the first filter. */ if(CDE_OK != cdeque_front(&rar->cstate.filters, cdeque_filter_p(&flt))) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Can't read first filter"); return ARCHIVE_FATAL; } max_end_pos = rar5_min(flt->block_start, rar->cstate.write_ptr); } else { /* There are no filters defined, or all filters were applied. This * means we can just store the data without any postprocessing. */ max_end_pos = rar->cstate.write_ptr; } if(max_end_pos == rar->cstate.last_write_ptr) { /* We can't write anything yet. The block uncompression function did * not generate enough data, and no filter can be applied. At the same * time we don't have any data that can be stored without filter * postprocessing. This means we need to wait for more data to be * generated, so we can apply the filters. * * Signal the caller that we need more data to be able to do anything. */ return ARCHIVE_RETRY; } else { /* We can write the data before hitting the first filter. So let's * do it. The push_window_data() function will effectively return * the selected data block to the user application. */ push_window_data(a, rar, rar->cstate.last_write_ptr, max_end_pos); rar->cstate.last_write_ptr = max_end_pos; } return ARCHIVE_OK; } static int uncompress_file(struct archive_read* a) { int ret; while(1) { /* Sometimes the uncompression function will return a 'retry' signal. * If this will happen, we have to retry the function. */ ret = do_uncompress_file(a); if(ret != ARCHIVE_RETRY) return ret; } } static int do_unstore_file(struct archive_read* a, struct rar5* rar, const void** buf, size_t* size, int64_t* offset) { const uint8_t* p; if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 && rar->generic.split_after > 0) { int ret; rar->cstate.switch_multivolume = 1; ret = advance_multivolume(a); rar->cstate.switch_multivolume = 0; if(ret != ARCHIVE_OK) { /* Failed to advance to next multivolume archive file. */ return ret; } } size_t to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024); if(!read_ahead(a, to_read, &p)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "I/O error " "when unstoring file"); return ARCHIVE_FATAL; } if(ARCHIVE_OK != consume(a, to_read)) { return ARCHIVE_EOF; } if(buf) *buf = p; if(size) *size = to_read; if(offset) *offset = rar->cstate.last_unstore_ptr; rar->file.bytes_remaining -= to_read; rar->cstate.last_unstore_ptr += to_read; update_crc(rar, p, to_read); return ARCHIVE_OK; } static int do_unpack(struct archive_read* a, struct rar5* rar, const void** buf, size_t* size, int64_t* offset) { enum COMPRESSION_METHOD { STORE = 0, FASTEST = 1, FAST = 2, NORMAL = 3, GOOD = 4, BEST = 5 }; if(rar->file.service > 0) { return do_unstore_file(a, rar, buf, size, offset); } else { switch(rar->cstate.method) { case STORE: return do_unstore_file(a, rar, buf, size, offset); case FASTEST: /* fallthrough */ case FAST: /* fallthrough */ case NORMAL: /* fallthrough */ case GOOD: /* fallthrough */ case BEST: return uncompress_file(a); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Compression method not supported: 0x%08x", rar->cstate.method); return ARCHIVE_FATAL; } } #if !defined WIN32 /* Not reached. */ return ARCHIVE_OK; #endif } static int verify_checksums(struct archive_read* a) { int verify_crc; struct rar5* rar = get_context(a); /* Check checksums only when actually unpacking the data. There's no need * to calculate checksum when we're skipping data in solid archives * (skipping in solid archives is the same thing as unpacking compressed * data and discarding the result). */ if(!rar->skip_mode) { /* Always check checkums if we're not in skip mode */ verify_crc = 1; } else { /* We can override the logic above with a compile-time option * NO_CRC_ON_SOLID_SKIP. This option is used during debugging, and it * will check checksums of unpacked data even when we're skipping it. */ #if defined CHECK_CRC_ON_SOLID_SKIP /* Debug case */ verify_crc = 1; #else /* Normal case */ verify_crc = 0; #endif } if(verify_crc) { /* During unpacking, on each unpacked block we're calling the * update_crc() function. Since we are here, the unpacking process is * already over and we can check if calculated checksum (CRC32 or * BLAKE2sp) is the same as what is stored in the archive. */ if(rar->file.stored_crc32 > 0) { /* Check CRC32 only when the file contains a CRC32 value for this * file. */ if(rar->file.calculated_crc32 != rar->file.stored_crc32) { /* Checksums do not match; the unpacked file is corrupted. */ DEBUG_CODE { printf("Checksum error: CRC32 (was: %08x, expected: %08x)\n", rar->file.calculated_crc32, rar->file.stored_crc32); } #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Checksum error: CRC32"); return ARCHIVE_FATAL; #endif } else { DEBUG_CODE { printf("Checksum OK: CRC32 (%08x/%08x)\n", rar->file.stored_crc32, rar->file.calculated_crc32); } } } if(rar->file.has_blake2 > 0) { /* BLAKE2sp is an optional checksum algorithm that is added to * RARv5 archives when using the `-htb` switch during creation of * archive. * * We now finalize the hash calculation by calling the `final` * function. This will generate the final hash value we can use to * compare it with the BLAKE2sp checksum that is stored in the * archive. * * The return value of this `final` function is not very helpful, * as it guards only against improper use. This is why we're * explicitly ignoring it. */ uint8_t b2_buf[32]; (void) blake2sp_final(&rar->file.b2state, b2_buf, 32); if(memcmp(&rar->file.blake2sp, b2_buf, 32) != 0) { #ifndef DONT_FAIL_ON_CRC_ERROR archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Checksum error: BLAKE2"); return ARCHIVE_FATAL; #endif } } } /* Finalization for this file has been successfully completed. */ return ARCHIVE_OK; } static int verify_global_checksums(struct archive_read* a) { return verify_checksums(a); } static int rar5_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { int ret; struct rar5* rar = get_context(a); if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, "Unpacker has written too many bytes"); return ARCHIVE_FATAL; } ret = use_data(rar, buff, size, offset); if(ret == ARCHIVE_OK) return ret; ret = do_unpack(a, rar, buff, size, offset); if(ret != ARCHIVE_OK) { return ret; } if(rar->file.bytes_remaining == 0 && rar->cstate.last_write_ptr == rar->file.unpacked_size) { /* If all bytes of current file were processed, run finalization. * * Finalization will check checksum against proper values. If * some of the checksums will not match, we'll return an error * value in the last `archive_read_data` call to signal an error * to the user. */ return verify_global_checksums(a); } return ARCHIVE_OK; } static int rar5_read_data_skip(struct archive_read *a) { struct rar5* rar = get_context(a); if(rar->main.solid) { /* In solid archives, instead of skipping the data, we need to extract * it, and dispose the result. The side effect of this operation will * be setting up the initial window buffer state needed to be able to * extract the selected file. */ int ret; /* Make sure to process all blocks in the compressed stream. */ while(rar->file.bytes_remaining > 0) { /* Setting the "skip mode" will allow us to skip checksum checks * during data skipping. Checking the checksum of skipped data * isn't really necessary and it's only slowing things down. * * This is incremented instead of setting to 1 because this data * skipping function can be called recursively. */ rar->skip_mode++; /* We're disposing 1 block of data, so we use triple NULLs in * arguments. */ ret = rar5_read_data(a, NULL, NULL, NULL); /* Turn off "skip mode". */ rar->skip_mode--; if(ret < 0) { /* Propagate any potential error conditions to the caller. */ return ret; } } } else { /* In standard archives, we can just jump over the compressed stream. * Each file in non-solid archives starts from an empty window buffer. */ if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) { return ARCHIVE_FATAL; } rar->file.bytes_remaining = 0; } return ARCHIVE_OK; } static int64_t rar5_seek_data(struct archive_read *a, int64_t offset, int whence) { (void) a; (void) offset; (void) whence; /* We're a streaming unpacker, and we don't support seeking. */ return ARCHIVE_FATAL; } static int rar5_cleanup(struct archive_read *a) { struct rar5* rar = get_context(a); - if(rar->cstate.window_buf) - free(rar->cstate.window_buf); + free(rar->cstate.window_buf); - if(rar->cstate.filtered_buf) - free(rar->cstate.filtered_buf); + free(rar->cstate.filtered_buf); - if(rar->vol.push_buf) - free(rar->vol.push_buf); + free(rar->vol.push_buf); free_filters(rar); cdeque_free(&rar->cstate.filters); free(rar); a->format->data = NULL; return ARCHIVE_OK; } static int rar5_capabilities(struct archive_read * a) { (void) a; return 0; } static int rar5_has_encrypted_entries(struct archive_read *_a) { (void) _a; /* Unsupported for now. */ return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED; } static int rar5_init(struct rar5* rar) { ssize_t i; memset(rar, 0, sizeof(struct rar5)); /* Decrypt the magic signature pattern. Check the comment near the * `rar5_signature` symbol to read the rationale behind this. */ if(rar5_signature[0] == 243) { for(i = 0; i < rar5_signature_size; i++) { rar5_signature[i] ^= 0xA1; } } if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192)) return ARCHIVE_FATAL; return ARCHIVE_OK; } int archive_read_support_format_rar5(struct archive *_a) { struct archive_read* ar; int ret; struct rar5* rar; if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar))) return ret; rar = malloc(sizeof(*rar)); if(rar == NULL) { archive_set_error(&ar->archive, ENOMEM, "Can't allocate rar5 data"); return ARCHIVE_FATAL; } if(ARCHIVE_OK != rar5_init(rar)) { archive_set_error(&ar->archive, ENOMEM, "Can't allocate rar5 filter " "buffer"); return ARCHIVE_FATAL; } ret = __archive_read_register_format(ar, rar, "rar5", rar5_bid, rar5_options, rar5_read_header, rar5_read_data, rar5_read_data_skip, rar5_seek_data, rar5_cleanup, rar5_capabilities, rar5_has_encrypted_entries); if(ret != ARCHIVE_OK) { (void) rar5_cleanup(ar); } return ret; } Index: stable/10/contrib/libarchive/libarchive/archive_read_support_format_xar.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_support_format_xar.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_read_support_format_xar.c (revision 344674) @@ -1,3310 +1,3309 @@ /*- * Copyright (c) 2009 Michihiro NAKAJIMA * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #if HAVE_LIBXML_XMLREADER_H #include #elif HAVE_BSDXML_H #include #elif HAVE_EXPAT_H #include #endif #ifdef HAVE_BZLIB_H #include #endif #if HAVE_LZMA_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #include "archive.h" #include "archive_digest_private.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_read_private.h" #if (!defined(HAVE_LIBXML_XMLREADER_H) && \ !defined(HAVE_BSDXML_H) && !defined(HAVE_EXPAT_H)) ||\ !defined(HAVE_ZLIB_H) || \ !defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1) /* * xar needs several external libraries. * o libxml2 or expat --- XML parser * o openssl or MD5/SHA1 hash function * o zlib * o bzlib2 (option) * o liblzma (option) */ int archive_read_support_format_xar(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_xar"); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Xar not supported on this platform"); return (ARCHIVE_WARN); } #else /* Support xar format */ /* #define DEBUG 1 */ /* #define DEBUG_PRINT_TOC 1 */ #if DEBUG_PRINT_TOC #define PRINT_TOC(d, outbytes) do { \ unsigned char *x = (unsigned char *)(uintptr_t)d; \ unsigned char c = x[outbytes-1]; \ x[outbytes - 1] = 0; \ fprintf(stderr, "%s", x); \ fprintf(stderr, "%c", c); \ x[outbytes - 1] = c; \ } while (0) #else #define PRINT_TOC(d, outbytes) #endif #define HEADER_MAGIC 0x78617221 #define HEADER_SIZE 28 #define HEADER_VERSION 1 #define CKSUM_NONE 0 #define CKSUM_SHA1 1 #define CKSUM_MD5 2 #define MD5_SIZE 16 #define SHA1_SIZE 20 #define MAX_SUM_SIZE 20 enum enctype { NONE, GZIP, BZIP2, LZMA, XZ, }; struct chksumval { int alg; size_t len; unsigned char val[MAX_SUM_SIZE]; }; struct chksumwork { int alg; #ifdef ARCHIVE_HAS_MD5 archive_md5_ctx md5ctx; #endif #ifdef ARCHIVE_HAS_SHA1 archive_sha1_ctx sha1ctx; #endif }; struct xattr { struct xattr *next; struct archive_string name; uint64_t id; uint64_t length; uint64_t offset; uint64_t size; enum enctype encoding; struct chksumval a_sum; struct chksumval e_sum; struct archive_string fstype; }; struct xar_file { struct xar_file *next; struct xar_file *hdnext; struct xar_file *parent; int subdirs; unsigned int has; #define HAS_DATA 0x00001 #define HAS_PATHNAME 0x00002 #define HAS_SYMLINK 0x00004 #define HAS_TIME 0x00008 #define HAS_UID 0x00010 #define HAS_GID 0x00020 #define HAS_MODE 0x00040 #define HAS_TYPE 0x00080 #define HAS_DEV 0x00100 #define HAS_DEVMAJOR 0x00200 #define HAS_DEVMINOR 0x00400 #define HAS_INO 0x00800 #define HAS_FFLAGS 0x01000 #define HAS_XATTR 0x02000 #define HAS_ACL 0x04000 #define HAS_CTIME 0x08000 #define HAS_MTIME 0x10000 #define HAS_ATIME 0x20000 uint64_t id; uint64_t length; uint64_t offset; uint64_t size; enum enctype encoding; struct chksumval a_sum; struct chksumval e_sum; struct archive_string pathname; struct archive_string symlink; time_t ctime; time_t mtime; time_t atime; struct archive_string uname; int64_t uid; struct archive_string gname; int64_t gid; mode_t mode; dev_t dev; dev_t devmajor; dev_t devminor; int64_t ino64; struct archive_string fflags_text; unsigned int link; unsigned int nlink; struct archive_string hardlink; struct xattr *xattr_list; }; struct hdlink { struct hdlink *next; unsigned int id; int cnt; struct xar_file *files; }; struct heap_queue { struct xar_file **files; int allocated; int used; }; enum xmlstatus { INIT, XAR, TOC, TOC_CREATION_TIME, TOC_CHECKSUM, TOC_CHECKSUM_OFFSET, TOC_CHECKSUM_SIZE, TOC_FILE, FILE_DATA, FILE_DATA_LENGTH, FILE_DATA_OFFSET, FILE_DATA_SIZE, FILE_DATA_ENCODING, FILE_DATA_A_CHECKSUM, FILE_DATA_E_CHECKSUM, FILE_DATA_CONTENT, FILE_EA, FILE_EA_LENGTH, FILE_EA_OFFSET, FILE_EA_SIZE, FILE_EA_ENCODING, FILE_EA_A_CHECKSUM, FILE_EA_E_CHECKSUM, FILE_EA_NAME, FILE_EA_FSTYPE, FILE_CTIME, FILE_MTIME, FILE_ATIME, FILE_GROUP, FILE_GID, FILE_USER, FILE_UID, FILE_MODE, FILE_DEVICE, FILE_DEVICE_MAJOR, FILE_DEVICE_MINOR, FILE_DEVICENO, FILE_INODE, FILE_LINK, FILE_TYPE, FILE_NAME, FILE_ACL, FILE_ACL_DEFAULT, FILE_ACL_ACCESS, FILE_ACL_APPLEEXTENDED, /* BSD file flags. */ FILE_FLAGS, FILE_FLAGS_USER_NODUMP, FILE_FLAGS_USER_IMMUTABLE, FILE_FLAGS_USER_APPEND, FILE_FLAGS_USER_OPAQUE, FILE_FLAGS_USER_NOUNLINK, FILE_FLAGS_SYS_ARCHIVED, FILE_FLAGS_SYS_IMMUTABLE, FILE_FLAGS_SYS_APPEND, FILE_FLAGS_SYS_NOUNLINK, FILE_FLAGS_SYS_SNAPSHOT, /* Linux file flags. */ FILE_EXT2, FILE_EXT2_SecureDeletion, FILE_EXT2_Undelete, FILE_EXT2_Compress, FILE_EXT2_Synchronous, FILE_EXT2_Immutable, FILE_EXT2_AppendOnly, FILE_EXT2_NoDump, FILE_EXT2_NoAtime, FILE_EXT2_CompDirty, FILE_EXT2_CompBlock, FILE_EXT2_NoCompBlock, FILE_EXT2_CompError, FILE_EXT2_BTree, FILE_EXT2_HashIndexed, FILE_EXT2_iMagic, FILE_EXT2_Journaled, FILE_EXT2_NoTail, FILE_EXT2_DirSync, FILE_EXT2_TopDir, FILE_EXT2_Reserved, UNKNOWN, }; struct unknown_tag { struct unknown_tag *next; struct archive_string name; }; struct xar { uint64_t offset; /* Current position in the file. */ int64_t total; uint64_t h_base; int end_of_file; #define OUTBUFF_SIZE (1024 * 64) unsigned char *outbuff; enum xmlstatus xmlsts; enum xmlstatus xmlsts_unknown; struct unknown_tag *unknowntags; int base64text; /* * TOC */ uint64_t toc_remaining; uint64_t toc_total; uint64_t toc_chksum_offset; uint64_t toc_chksum_size; /* * For Decoding data. */ enum enctype rd_encoding; z_stream stream; int stream_valid; #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) bz_stream bzstream; int bzstream_valid; #endif #if HAVE_LZMA_H && HAVE_LIBLZMA lzma_stream lzstream; int lzstream_valid; #endif /* * For Checksum data. */ struct chksumwork a_sumwrk; struct chksumwork e_sumwrk; struct xar_file *file; /* current reading file. */ struct xattr *xattr; /* current reading extended attribute. */ struct heap_queue file_queue; struct xar_file *hdlink_orgs; struct hdlink *hdlink_list; int entry_init; uint64_t entry_total; uint64_t entry_remaining; size_t entry_unconsumed; uint64_t entry_size; enum enctype entry_encoding; struct chksumval entry_a_sum; struct chksumval entry_e_sum; struct archive_string_conv *sconv; }; struct xmlattr { struct xmlattr *next; char *name; char *value; }; struct xmlattr_list { struct xmlattr *first; struct xmlattr **last; }; static int xar_bid(struct archive_read *, int); static int xar_read_header(struct archive_read *, struct archive_entry *); static int xar_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int xar_read_data_skip(struct archive_read *); static int xar_cleanup(struct archive_read *); static int move_reading_point(struct archive_read *, uint64_t); static int rd_contents_init(struct archive_read *, enum enctype, int, int); static int rd_contents(struct archive_read *, const void **, size_t *, size_t *, uint64_t); static uint64_t atol10(const char *, size_t); static int64_t atol8(const char *, size_t); static size_t atohex(unsigned char *, size_t, const char *, size_t); static time_t parse_time(const char *p, size_t n); static int heap_add_entry(struct archive_read *a, struct heap_queue *, struct xar_file *); static struct xar_file *heap_get_entry(struct heap_queue *); static int add_link(struct archive_read *, struct xar *, struct xar_file *); static void checksum_init(struct archive_read *, int, int); static void checksum_update(struct archive_read *, const void *, size_t, const void *, size_t); static int checksum_final(struct archive_read *, const void *, size_t, const void *, size_t); static void checksum_cleanup(struct archive_read *); static int decompression_init(struct archive_read *, enum enctype); static int decompress(struct archive_read *, const void **, size_t *, const void *, size_t *); static int decompression_cleanup(struct archive_read *); static void xmlattr_cleanup(struct xmlattr_list *); static int file_new(struct archive_read *, struct xar *, struct xmlattr_list *); static void file_free(struct xar_file *); static int xattr_new(struct archive_read *, struct xar *, struct xmlattr_list *); static void xattr_free(struct xattr *); static int getencoding(struct xmlattr_list *); static int getsumalgorithm(struct xmlattr_list *); static int unknowntag_start(struct archive_read *, struct xar *, const char *); static void unknowntag_end(struct xar *, const char *); static int xml_start(struct archive_read *, const char *, struct xmlattr_list *); static void xml_end(void *, const char *); static void xml_data(void *, const char *, int); static int xml_parse_file_flags(struct xar *, const char *); static int xml_parse_file_ext2(struct xar *, const char *); #if defined(HAVE_LIBXML_XMLREADER_H) static int xml2_xmlattr_setup(struct archive_read *, struct xmlattr_list *, xmlTextReaderPtr); static int xml2_read_cb(void *, char *, int); static int xml2_close_cb(void *); static void xml2_error_hdr(void *, const char *, xmlParserSeverities, xmlTextReaderLocatorPtr); static int xml2_read_toc(struct archive_read *); #elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) struct expat_userData { int state; struct archive_read *archive; }; static int expat_xmlattr_setup(struct archive_read *, struct xmlattr_list *, const XML_Char **); static void expat_start_cb(void *, const XML_Char *, const XML_Char **); static void expat_end_cb(void *, const XML_Char *); static void expat_data_cb(void *, const XML_Char *, int); static int expat_read_toc(struct archive_read *); #endif int archive_read_support_format_xar(struct archive *_a) { struct xar *xar; struct archive_read *a = (struct archive_read *)_a; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_xar"); xar = (struct xar *)calloc(1, sizeof(*xar)); if (xar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate xar data"); return (ARCHIVE_FATAL); } r = __archive_read_register_format(a, xar, "xar", xar_bid, NULL, xar_read_header, xar_read_data, xar_read_data_skip, NULL, xar_cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(xar); return (r); } static int xar_bid(struct archive_read *a, int best_bid) { const unsigned char *b; int bid; (void)best_bid; /* UNUSED */ b = __archive_read_ahead(a, HEADER_SIZE, NULL); if (b == NULL) return (-1); bid = 0; /* * Verify magic code */ if (archive_be32dec(b) != HEADER_MAGIC) return (0); bid += 32; /* * Verify header size */ if (archive_be16dec(b+4) != HEADER_SIZE) return (0); bid += 16; /* * Verify header version */ if (archive_be16dec(b+6) != HEADER_VERSION) return (0); bid += 16; /* * Verify type of checksum */ switch (archive_be32dec(b+24)) { case CKSUM_NONE: case CKSUM_SHA1: case CKSUM_MD5: bid += 32; break; default: return (0); } return (bid); } static int read_toc(struct archive_read *a) { struct xar *xar; struct xar_file *file; const unsigned char *b; uint64_t toc_compressed_size; uint64_t toc_uncompressed_size; uint32_t toc_chksum_alg; ssize_t bytes; int r; xar = (struct xar *)(a->format->data); /* * Read xar header. */ b = __archive_read_ahead(a, HEADER_SIZE, &bytes); if (bytes < 0) return ((int)bytes); if (bytes < HEADER_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated archive header"); return (ARCHIVE_FATAL); } if (archive_be32dec(b) != HEADER_MAGIC) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid header magic"); return (ARCHIVE_FATAL); } if (archive_be16dec(b+6) != HEADER_VERSION) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported header version(%d)", archive_be16dec(b+6)); return (ARCHIVE_FATAL); } toc_compressed_size = archive_be64dec(b+8); xar->toc_remaining = toc_compressed_size; toc_uncompressed_size = archive_be64dec(b+16); toc_chksum_alg = archive_be32dec(b+24); __archive_read_consume(a, HEADER_SIZE); xar->offset += HEADER_SIZE; xar->toc_total = 0; /* * Read TOC(Table of Contents). */ /* Initialize reading contents. */ r = move_reading_point(a, HEADER_SIZE); if (r != ARCHIVE_OK) return (r); r = rd_contents_init(a, GZIP, toc_chksum_alg, CKSUM_NONE); if (r != ARCHIVE_OK) return (r); #ifdef HAVE_LIBXML_XMLREADER_H r = xml2_read_toc(a); #elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) r = expat_read_toc(a); #endif if (r != ARCHIVE_OK) return (r); /* Set 'The HEAP' base. */ xar->h_base = xar->offset; if (xar->toc_total != toc_uncompressed_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "TOC uncompressed size error"); return (ARCHIVE_FATAL); } /* * Checksum TOC */ if (toc_chksum_alg != CKSUM_NONE) { r = move_reading_point(a, xar->toc_chksum_offset); if (r != ARCHIVE_OK) return (r); b = __archive_read_ahead(a, (size_t)xar->toc_chksum_size, &bytes); if (bytes < 0) return ((int)bytes); if ((uint64_t)bytes < xar->toc_chksum_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated archive file"); return (ARCHIVE_FATAL); } r = checksum_final(a, b, (size_t)xar->toc_chksum_size, NULL, 0); __archive_read_consume(a, xar->toc_chksum_size); xar->offset += xar->toc_chksum_size; if (r != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* * Connect hardlinked files. */ for (file = xar->hdlink_orgs; file != NULL; file = file->hdnext) { struct hdlink **hdlink; for (hdlink = &(xar->hdlink_list); *hdlink != NULL; hdlink = &((*hdlink)->next)) { if ((*hdlink)->id == file->id) { struct hdlink *hltmp; struct xar_file *f2; int nlink = (*hdlink)->cnt + 1; file->nlink = nlink; for (f2 = (*hdlink)->files; f2 != NULL; f2 = f2->hdnext) { f2->nlink = nlink; archive_string_copy( &(f2->hardlink), &(file->pathname)); } /* Remove resolved files from hdlist_list. */ hltmp = *hdlink; *hdlink = hltmp->next; free(hltmp); break; } } } a->archive.archive_format = ARCHIVE_FORMAT_XAR; a->archive.archive_format_name = "xar"; return (ARCHIVE_OK); } static int xar_read_header(struct archive_read *a, struct archive_entry *entry) { struct xar *xar; struct xar_file *file; struct xattr *xattr; int r; xar = (struct xar *)(a->format->data); r = ARCHIVE_OK; if (xar->offset == 0) { /* Create a character conversion object. */ if (xar->sconv == NULL) { xar->sconv = archive_string_conversion_from_charset( &(a->archive), "UTF-8", 1); if (xar->sconv == NULL) return (ARCHIVE_FATAL); } /* Read TOC. */ r = read_toc(a); if (r != ARCHIVE_OK) return (r); } for (;;) { file = xar->file = heap_get_entry(&(xar->file_queue)); if (file == NULL) { xar->end_of_file = 1; return (ARCHIVE_EOF); } if ((file->mode & AE_IFMT) != AE_IFDIR) break; if (file->has != (HAS_PATHNAME | HAS_TYPE)) break; /* * If a file type is a directory and it does not have * any metadata, do not export. */ file_free(file); } if (file->has & HAS_ATIME) { archive_entry_set_atime(entry, file->atime, 0); } if (file->has & HAS_CTIME) { archive_entry_set_ctime(entry, file->ctime, 0); } if (file->has & HAS_MTIME) { archive_entry_set_mtime(entry, file->mtime, 0); } archive_entry_set_gid(entry, file->gid); if (file->gname.length > 0 && archive_entry_copy_gname_l(entry, file->gname.s, archive_strlen(&(file->gname)), xar->sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Gname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Gname cannot be converted from %s to current locale.", archive_string_conversion_charset_name(xar->sconv)); r = ARCHIVE_WARN; } archive_entry_set_uid(entry, file->uid); if (file->uname.length > 0 && archive_entry_copy_uname_l(entry, file->uname.s, archive_strlen(&(file->uname)), xar->sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Uname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Uname cannot be converted from %s to current locale.", archive_string_conversion_charset_name(xar->sconv)); r = ARCHIVE_WARN; } archive_entry_set_mode(entry, file->mode); if (archive_entry_copy_pathname_l(entry, file->pathname.s, archive_strlen(&(file->pathname)), xar->sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted from %s to current locale.", archive_string_conversion_charset_name(xar->sconv)); r = ARCHIVE_WARN; } if (file->symlink.length > 0 && archive_entry_copy_symlink_l(entry, file->symlink.s, archive_strlen(&(file->symlink)), xar->sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Linkname cannot be converted from %s to current locale.", archive_string_conversion_charset_name(xar->sconv)); r = ARCHIVE_WARN; } /* Set proper nlink. */ if ((file->mode & AE_IFMT) == AE_IFDIR) archive_entry_set_nlink(entry, file->subdirs + 2); else archive_entry_set_nlink(entry, file->nlink); archive_entry_set_size(entry, file->size); if (archive_strlen(&(file->hardlink)) > 0) archive_entry_set_hardlink(entry, file->hardlink.s); archive_entry_set_ino64(entry, file->ino64); if (file->has & HAS_DEV) archive_entry_set_dev(entry, file->dev); if (file->has & HAS_DEVMAJOR) archive_entry_set_devmajor(entry, file->devmajor); if (file->has & HAS_DEVMINOR) archive_entry_set_devminor(entry, file->devminor); if (archive_strlen(&(file->fflags_text)) > 0) archive_entry_copy_fflags_text(entry, file->fflags_text.s); xar->entry_init = 1; xar->entry_total = 0; xar->entry_remaining = file->length; xar->entry_size = file->size; xar->entry_encoding = file->encoding; xar->entry_a_sum = file->a_sum; xar->entry_e_sum = file->e_sum; /* * Read extended attributes. */ xattr = file->xattr_list; while (xattr != NULL) { const void *d; size_t outbytes, used; r = move_reading_point(a, xattr->offset); if (r != ARCHIVE_OK) break; r = rd_contents_init(a, xattr->encoding, xattr->a_sum.alg, xattr->e_sum.alg); if (r != ARCHIVE_OK) break; d = NULL; r = rd_contents(a, &d, &outbytes, &used, xattr->length); if (r != ARCHIVE_OK) break; if (outbytes != xattr->size) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Decompressed size error"); r = ARCHIVE_FATAL; break; } r = checksum_final(a, xattr->a_sum.val, xattr->a_sum.len, xattr->e_sum.val, xattr->e_sum.len); if (r != ARCHIVE_OK) break; archive_entry_xattr_add_entry(entry, xattr->name.s, d, outbytes); xattr = xattr->next; } if (r != ARCHIVE_OK) { file_free(file); return (r); } if (xar->entry_remaining > 0) /* Move reading point to the beginning of current * file contents. */ r = move_reading_point(a, file->offset); else r = ARCHIVE_OK; file_free(file); return (r); } static int xar_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct xar *xar; size_t used; int r; xar = (struct xar *)(a->format->data); if (xar->entry_unconsumed) { __archive_read_consume(a, xar->entry_unconsumed); xar->entry_unconsumed = 0; } if (xar->end_of_file || xar->entry_remaining <= 0) { r = ARCHIVE_EOF; goto abort_read_data; } if (xar->entry_init) { r = rd_contents_init(a, xar->entry_encoding, xar->entry_a_sum.alg, xar->entry_e_sum.alg); if (r != ARCHIVE_OK) { xar->entry_remaining = 0; return (r); } xar->entry_init = 0; } *buff = NULL; r = rd_contents(a, buff, size, &used, xar->entry_remaining); if (r != ARCHIVE_OK) goto abort_read_data; *offset = xar->entry_total; xar->entry_total += *size; xar->total += *size; xar->offset += used; xar->entry_remaining -= used; xar->entry_unconsumed = used; if (xar->entry_remaining == 0) { if (xar->entry_total != xar->entry_size) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Decompressed size error"); r = ARCHIVE_FATAL; goto abort_read_data; } r = checksum_final(a, xar->entry_a_sum.val, xar->entry_a_sum.len, xar->entry_e_sum.val, xar->entry_e_sum.len); if (r != ARCHIVE_OK) goto abort_read_data; } return (ARCHIVE_OK); abort_read_data: *buff = NULL; *size = 0; *offset = xar->total; return (r); } static int xar_read_data_skip(struct archive_read *a) { struct xar *xar; int64_t bytes_skipped; xar = (struct xar *)(a->format->data); if (xar->end_of_file) return (ARCHIVE_EOF); bytes_skipped = __archive_read_consume(a, xar->entry_remaining + xar->entry_unconsumed); if (bytes_skipped < 0) return (ARCHIVE_FATAL); xar->offset += bytes_skipped; xar->entry_unconsumed = 0; return (ARCHIVE_OK); } static int xar_cleanup(struct archive_read *a) { struct xar *xar; struct hdlink *hdlink; int i; int r; xar = (struct xar *)(a->format->data); checksum_cleanup(a); r = decompression_cleanup(a); hdlink = xar->hdlink_list; while (hdlink != NULL) { struct hdlink *next = hdlink->next; free(hdlink); hdlink = next; } for (i = 0; i < xar->file_queue.used; i++) file_free(xar->file_queue.files[i]); free(xar->file_queue.files); while (xar->unknowntags != NULL) { struct unknown_tag *tag; tag = xar->unknowntags; xar->unknowntags = tag->next; archive_string_free(&(tag->name)); free(tag); } free(xar->outbuff); free(xar); a->format->data = NULL; return (r); } static int move_reading_point(struct archive_read *a, uint64_t offset) { struct xar *xar; xar = (struct xar *)(a->format->data); if (xar->offset - xar->h_base != offset) { /* Seek forward to the start of file contents. */ int64_t step; step = offset - (xar->offset - xar->h_base); if (step > 0) { step = __archive_read_consume(a, step); if (step < 0) return ((int)step); xar->offset += step; } else { int64_t pos = __archive_read_seek(a, xar->h_base + offset, SEEK_SET); if (pos == ARCHIVE_FAILED) { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Cannot seek."); return (ARCHIVE_FAILED); } xar->offset = pos; } } return (ARCHIVE_OK); } static int rd_contents_init(struct archive_read *a, enum enctype encoding, int a_sum_alg, int e_sum_alg) { int r; /* Init decompress library. */ if ((r = decompression_init(a, encoding)) != ARCHIVE_OK) return (r); /* Init checksum library. */ checksum_init(a, a_sum_alg, e_sum_alg); return (ARCHIVE_OK); } static int rd_contents(struct archive_read *a, const void **buff, size_t *size, size_t *used, uint64_t remaining) { const unsigned char *b; ssize_t bytes; /* Get whatever bytes are immediately available. */ b = __archive_read_ahead(a, 1, &bytes); if (bytes < 0) return ((int)bytes); if (bytes == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated archive file"); return (ARCHIVE_FATAL); } if ((uint64_t)bytes > remaining) bytes = (ssize_t)remaining; /* * Decompress contents of file. */ *used = bytes; if (decompress(a, buff, size, b, used) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* * Update checksum of a compressed data and a extracted data. */ checksum_update(a, b, *used, *buff, *size); return (ARCHIVE_OK); } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. */ static uint64_t atol10(const char *p, size_t char_cnt) { uint64_t l; int digit; if (char_cnt == 0) return (0); l = 0; digit = *p - '0'; while (digit >= 0 && digit < 10 && char_cnt-- > 0) { l = (l * 10) + digit; digit = *++p - '0'; } return (l); } static int64_t atol8(const char *p, size_t char_cnt) { int64_t l; int digit; if (char_cnt == 0) return (0); l = 0; while (char_cnt-- > 0) { if (*p >= '0' && *p <= '7') digit = *p - '0'; else break; p++; l <<= 3; l |= digit; } return (l); } static size_t atohex(unsigned char *b, size_t bsize, const char *p, size_t psize) { size_t fbsize = bsize; while (bsize && psize > 1) { unsigned char x; if (p[0] >= 'a' && p[0] <= 'z') x = (p[0] - 'a' + 0x0a) << 4; else if (p[0] >= 'A' && p[0] <= 'Z') x = (p[0] - 'A' + 0x0a) << 4; else if (p[0] >= '0' && p[0] <= '9') x = (p[0] - '0') << 4; else return (-1); if (p[1] >= 'a' && p[1] <= 'z') x |= p[1] - 'a' + 0x0a; else if (p[1] >= 'A' && p[1] <= 'Z') x |= p[1] - 'A' + 0x0a; else if (p[1] >= '0' && p[1] <= '9') x |= p[1] - '0'; else return (-1); *b++ = x; bsize--; p += 2; psize -= 2; } return (fbsize - bsize); } static time_t time_from_tm(struct tm *t) { #if HAVE_TIMEGM /* Use platform timegm() if available. */ return (timegm(t)); #elif HAVE__MKGMTIME64 return (_mkgmtime64(t)); #else /* Else use direct calculation using POSIX assumptions. */ /* First, fix up tm_yday based on the year/month/day. */ mktime(t); /* Then we can compute timegm() from first principles. */ return (t->tm_sec + t->tm_min * 60 + t->tm_hour * 3600 + t->tm_yday * 86400 + (t->tm_year - 70) * 31536000 + ((t->tm_year - 69) / 4) * 86400 - ((t->tm_year - 1) / 100) * 86400 + ((t->tm_year + 299) / 400) * 86400); #endif } static time_t parse_time(const char *p, size_t n) { struct tm tm; time_t t = 0; int64_t data; memset(&tm, 0, sizeof(tm)); if (n != 20) return (t); data = atol10(p, 4); if (data < 1900) return (t); tm.tm_year = (int)data - 1900; p += 4; if (*p++ != '-') return (t); data = atol10(p, 2); if (data < 1 || data > 12) return (t); tm.tm_mon = (int)data -1; p += 2; if (*p++ != '-') return (t); data = atol10(p, 2); if (data < 1 || data > 31) return (t); tm.tm_mday = (int)data; p += 2; if (*p++ != 'T') return (t); data = atol10(p, 2); if (data < 0 || data > 23) return (t); tm.tm_hour = (int)data; p += 2; if (*p++ != ':') return (t); data = atol10(p, 2); if (data < 0 || data > 59) return (t); tm.tm_min = (int)data; p += 2; if (*p++ != ':') return (t); data = atol10(p, 2); if (data < 0 || data > 60) return (t); tm.tm_sec = (int)data; #if 0 p += 2; if (*p != 'Z') return (t); #endif t = time_from_tm(&tm); return (t); } static int heap_add_entry(struct archive_read *a, struct heap_queue *heap, struct xar_file *file) { uint64_t file_id, parent_id; int hole, parent; /* Expand our pending files list as necessary. */ if (heap->used >= heap->allocated) { struct xar_file **new_pending_files; int new_size = heap->allocated * 2; if (heap->allocated < 1024) new_size = 1024; /* Overflow might keep us from growing the list. */ if (new_size <= heap->allocated) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } new_pending_files = (struct xar_file **) malloc(new_size * sizeof(new_pending_files[0])); if (new_pending_files == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } memcpy(new_pending_files, heap->files, heap->allocated * sizeof(new_pending_files[0])); - if (heap->files != NULL) - free(heap->files); + free(heap->files); heap->files = new_pending_files; heap->allocated = new_size; } file_id = file->id; /* * Start with hole at end, walk it up tree to find insertion point. */ hole = heap->used++; while (hole > 0) { parent = (hole - 1)/2; parent_id = heap->files[parent]->id; if (file_id >= parent_id) { heap->files[hole] = file; return (ARCHIVE_OK); } /* Move parent into hole <==> move hole up tree. */ heap->files[hole] = heap->files[parent]; hole = parent; } heap->files[0] = file; return (ARCHIVE_OK); } static struct xar_file * heap_get_entry(struct heap_queue *heap) { uint64_t a_id, b_id, c_id; int a, b, c; struct xar_file *r, *tmp; if (heap->used < 1) return (NULL); /* * The first file in the list is the earliest; we'll return this. */ r = heap->files[0]; /* * Move the last item in the heap to the root of the tree */ heap->files[0] = heap->files[--(heap->used)]; /* * Rebalance the heap. */ a = 0; /* Starting element and its heap key */ a_id = heap->files[a]->id; for (;;) { b = a + a + 1; /* First child */ if (b >= heap->used) return (r); b_id = heap->files[b]->id; c = b + 1; /* Use second child if it is smaller. */ if (c < heap->used) { c_id = heap->files[c]->id; if (c_id < b_id) { b = c; b_id = c_id; } } if (a_id <= b_id) return (r); tmp = heap->files[a]; heap->files[a] = heap->files[b]; heap->files[b] = tmp; a = b; } } static int add_link(struct archive_read *a, struct xar *xar, struct xar_file *file) { struct hdlink *hdlink; for (hdlink = xar->hdlink_list; hdlink != NULL; hdlink = hdlink->next) { if (hdlink->id == file->link) { file->hdnext = hdlink->files; hdlink->cnt++; hdlink->files = file; return (ARCHIVE_OK); } } hdlink = malloc(sizeof(*hdlink)); if (hdlink == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } file->hdnext = NULL; hdlink->id = file->link; hdlink->cnt = 1; hdlink->files = file; hdlink->next = xar->hdlink_list; xar->hdlink_list = hdlink; return (ARCHIVE_OK); } static void _checksum_init(struct chksumwork *sumwrk, int sum_alg) { sumwrk->alg = sum_alg; switch (sum_alg) { case CKSUM_NONE: break; case CKSUM_SHA1: archive_sha1_init(&(sumwrk->sha1ctx)); break; case CKSUM_MD5: archive_md5_init(&(sumwrk->md5ctx)); break; } } static void _checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size) { switch (sumwrk->alg) { case CKSUM_NONE: break; case CKSUM_SHA1: archive_sha1_update(&(sumwrk->sha1ctx), buff, size); break; case CKSUM_MD5: archive_md5_update(&(sumwrk->md5ctx), buff, size); break; } } static int _checksum_final(struct chksumwork *sumwrk, const void *val, size_t len) { unsigned char sum[MAX_SUM_SIZE]; int r = ARCHIVE_OK; switch (sumwrk->alg) { case CKSUM_NONE: break; case CKSUM_SHA1: archive_sha1_final(&(sumwrk->sha1ctx), sum); if (len != SHA1_SIZE || memcmp(val, sum, SHA1_SIZE) != 0) r = ARCHIVE_FAILED; break; case CKSUM_MD5: archive_md5_final(&(sumwrk->md5ctx), sum); if (len != MD5_SIZE || memcmp(val, sum, MD5_SIZE) != 0) r = ARCHIVE_FAILED; break; } return (r); } static void checksum_init(struct archive_read *a, int a_sum_alg, int e_sum_alg) { struct xar *xar; xar = (struct xar *)(a->format->data); _checksum_init(&(xar->a_sumwrk), a_sum_alg); _checksum_init(&(xar->e_sumwrk), e_sum_alg); } static void checksum_update(struct archive_read *a, const void *abuff, size_t asize, const void *ebuff, size_t esize) { struct xar *xar; xar = (struct xar *)(a->format->data); _checksum_update(&(xar->a_sumwrk), abuff, asize); _checksum_update(&(xar->e_sumwrk), ebuff, esize); } static int checksum_final(struct archive_read *a, const void *a_sum_val, size_t a_sum_len, const void *e_sum_val, size_t e_sum_len) { struct xar *xar; int r; xar = (struct xar *)(a->format->data); r = _checksum_final(&(xar->a_sumwrk), a_sum_val, a_sum_len); if (r == ARCHIVE_OK) r = _checksum_final(&(xar->e_sumwrk), e_sum_val, e_sum_len); if (r != ARCHIVE_OK) archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Sumcheck error"); return (r); } static int decompression_init(struct archive_read *a, enum enctype encoding) { struct xar *xar; const char *detail; int r; xar = (struct xar *)(a->format->data); xar->rd_encoding = encoding; switch (encoding) { case NONE: break; case GZIP: if (xar->stream_valid) r = inflateReset(&(xar->stream)); else r = inflateInit(&(xar->stream)); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Couldn't initialize zlib stream."); return (ARCHIVE_FATAL); } xar->stream_valid = 1; xar->stream.total_in = 0; xar->stream.total_out = 0; break; #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) case BZIP2: if (xar->bzstream_valid) { BZ2_bzDecompressEnd(&(xar->bzstream)); xar->bzstream_valid = 0; } r = BZ2_bzDecompressInit(&(xar->bzstream), 0, 0); if (r == BZ_MEM_ERROR) r = BZ2_bzDecompressInit(&(xar->bzstream), 0, 1); if (r != BZ_OK) { int err = ARCHIVE_ERRNO_MISC; detail = NULL; switch (r) { case BZ_PARAM_ERROR: detail = "invalid setup parameter"; break; case BZ_MEM_ERROR: err = ENOMEM; detail = "out of memory"; break; case BZ_CONFIG_ERROR: detail = "mis-compiled library"; break; } archive_set_error(&a->archive, err, "Internal error initializing decompressor: %s", detail == NULL ? "??" : detail); xar->bzstream_valid = 0; return (ARCHIVE_FATAL); } xar->bzstream_valid = 1; xar->bzstream.total_in_lo32 = 0; xar->bzstream.total_in_hi32 = 0; xar->bzstream.total_out_lo32 = 0; xar->bzstream.total_out_hi32 = 0; break; #endif #if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA) #if LZMA_VERSION_MAJOR >= 5 /* Effectively disable the limiter. */ #define LZMA_MEMLIMIT UINT64_MAX #else /* NOTE: This needs to check memory size which running system has. */ #define LZMA_MEMLIMIT (1U << 30) #endif case XZ: case LZMA: if (xar->lzstream_valid) { lzma_end(&(xar->lzstream)); xar->lzstream_valid = 0; } if (xar->entry_encoding == XZ) r = lzma_stream_decoder(&(xar->lzstream), LZMA_MEMLIMIT,/* memlimit */ LZMA_CONCATENATED); else r = lzma_alone_decoder(&(xar->lzstream), LZMA_MEMLIMIT);/* memlimit */ if (r != LZMA_OK) { switch (r) { case LZMA_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Internal error initializing " "compression library: " "Cannot allocate memory"); break; case LZMA_OPTIONS_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing " "compression library: " "Invalid or unsupported options"); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal error initializing " "lzma library"); break; } return (ARCHIVE_FATAL); } xar->lzstream_valid = 1; xar->lzstream.total_in = 0; xar->lzstream.total_out = 0; break; #endif /* * Unsupported compression. */ default: #if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) case BZIP2: #endif #if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA) case LZMA: case XZ: #endif switch (xar->entry_encoding) { case BZIP2: detail = "bzip2"; break; case LZMA: detail = "lzma"; break; case XZ: detail = "xz"; break; default: detail = "??"; break; } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s compression not supported on this platform", detail); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } static int decompress(struct archive_read *a, const void **buff, size_t *outbytes, const void *b, size_t *used) { struct xar *xar; void *outbuff; size_t avail_in, avail_out; int r; xar = (struct xar *)(a->format->data); avail_in = *used; outbuff = (void *)(uintptr_t)*buff; if (outbuff == NULL) { if (xar->outbuff == NULL) { xar->outbuff = malloc(OUTBUFF_SIZE); if (xar->outbuff == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory for out buffer"); return (ARCHIVE_FATAL); } } outbuff = xar->outbuff; *buff = outbuff; avail_out = OUTBUFF_SIZE; } else avail_out = *outbytes; switch (xar->rd_encoding) { case GZIP: xar->stream.next_in = (Bytef *)(uintptr_t)b; xar->stream.avail_in = avail_in; xar->stream.next_out = (unsigned char *)outbuff; xar->stream.avail_out = avail_out; r = inflate(&(xar->stream), 0); switch (r) { case Z_OK: /* Decompressor made some progress.*/ case Z_STREAM_END: /* Found end of stream. */ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "File decompression failed (%d)", r); return (ARCHIVE_FATAL); } *used = avail_in - xar->stream.avail_in; *outbytes = avail_out - xar->stream.avail_out; break; #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) case BZIP2: xar->bzstream.next_in = (char *)(uintptr_t)b; xar->bzstream.avail_in = avail_in; xar->bzstream.next_out = (char *)outbuff; xar->bzstream.avail_out = avail_out; r = BZ2_bzDecompress(&(xar->bzstream)); switch (r) { case BZ_STREAM_END: /* Found end of stream. */ switch (BZ2_bzDecompressEnd(&(xar->bzstream))) { case BZ_OK: break; default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "Failed to clean up decompressor"); return (ARCHIVE_FATAL); } xar->bzstream_valid = 0; /* FALLTHROUGH */ case BZ_OK: /* Decompressor made some progress. */ break; default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "bzip decompression failed"); return (ARCHIVE_FATAL); } *used = avail_in - xar->bzstream.avail_in; *outbytes = avail_out - xar->bzstream.avail_out; break; #endif #if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA) case LZMA: case XZ: xar->lzstream.next_in = b; xar->lzstream.avail_in = avail_in; xar->lzstream.next_out = (unsigned char *)outbuff; xar->lzstream.avail_out = avail_out; r = lzma_code(&(xar->lzstream), LZMA_RUN); switch (r) { case LZMA_STREAM_END: /* Found end of stream. */ lzma_end(&(xar->lzstream)); xar->lzstream_valid = 0; /* FALLTHROUGH */ case LZMA_OK: /* Decompressor made some progress. */ break; default: archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, "%s decompression failed(%d)", (xar->entry_encoding == XZ)?"xz":"lzma", r); return (ARCHIVE_FATAL); } *used = avail_in - xar->lzstream.avail_in; *outbytes = avail_out - xar->lzstream.avail_out; break; #endif #if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) case BZIP2: #endif #if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA) case LZMA: case XZ: #endif case NONE: default: if (outbuff == xar->outbuff) { *buff = b; *used = avail_in; *outbytes = avail_in; } else { if (avail_out > avail_in) avail_out = avail_in; memcpy(outbuff, b, avail_out); *used = avail_out; *outbytes = avail_out; } break; } return (ARCHIVE_OK); } static int decompression_cleanup(struct archive_read *a) { struct xar *xar; int r; xar = (struct xar *)(a->format->data); r = ARCHIVE_OK; if (xar->stream_valid) { if (inflateEnd(&(xar->stream)) != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up zlib decompressor"); r = ARCHIVE_FATAL; } } #if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) if (xar->bzstream_valid) { if (BZ2_bzDecompressEnd(&(xar->bzstream)) != BZ_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up bzip2 decompressor"); r = ARCHIVE_FATAL; } } #endif #if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA) if (xar->lzstream_valid) lzma_end(&(xar->lzstream)); #elif defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA) if (xar->lzstream_valid) { if (lzmadec_end(&(xar->lzstream)) != LZMADEC_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up lzmadec decompressor"); r = ARCHIVE_FATAL; } } #endif return (r); } static void checksum_cleanup(struct archive_read *a) { struct xar *xar; xar = (struct xar *)(a->format->data); _checksum_final(&(xar->a_sumwrk), NULL, 0); _checksum_final(&(xar->e_sumwrk), NULL, 0); } static void xmlattr_cleanup(struct xmlattr_list *list) { struct xmlattr *attr, *next; attr = list->first; while (attr != NULL) { next = attr->next; free(attr->name); free(attr->value); free(attr); attr = next; } list->first = NULL; list->last = &(list->first); } static int file_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list) { struct xar_file *file; struct xmlattr *attr; file = calloc(1, sizeof(*file)); if (file == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } file->parent = xar->file; file->mode = 0777 | AE_IFREG; file->atime = 0; file->mtime = 0; xar->file = file; xar->xattr = NULL; for (attr = list->first; attr != NULL; attr = attr->next) { if (strcmp(attr->name, "id") == 0) file->id = atol10(attr->value, strlen(attr->value)); } file->nlink = 1; if (heap_add_entry(a, &(xar->file_queue), file) != ARCHIVE_OK) return (ARCHIVE_FATAL); return (ARCHIVE_OK); } static void file_free(struct xar_file *file) { struct xattr *xattr; archive_string_free(&(file->pathname)); archive_string_free(&(file->symlink)); archive_string_free(&(file->uname)); archive_string_free(&(file->gname)); archive_string_free(&(file->hardlink)); xattr = file->xattr_list; while (xattr != NULL) { struct xattr *next; next = xattr->next; xattr_free(xattr); xattr = next; } free(file); } static int xattr_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list) { struct xattr *xattr, **nx; struct xmlattr *attr; xattr = calloc(1, sizeof(*xattr)); if (xattr == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } xar->xattr = xattr; for (attr = list->first; attr != NULL; attr = attr->next) { if (strcmp(attr->name, "id") == 0) xattr->id = atol10(attr->value, strlen(attr->value)); } /* Chain to xattr list. */ for (nx = &(xar->file->xattr_list); *nx != NULL; nx = &((*nx)->next)) { if (xattr->id < (*nx)->id) break; } xattr->next = *nx; *nx = xattr; return (ARCHIVE_OK); } static void xattr_free(struct xattr *xattr) { archive_string_free(&(xattr->name)); free(xattr); } static int getencoding(struct xmlattr_list *list) { struct xmlattr *attr; enum enctype encoding = NONE; for (attr = list->first; attr != NULL; attr = attr->next) { if (strcmp(attr->name, "style") == 0) { if (strcmp(attr->value, "application/octet-stream") == 0) encoding = NONE; else if (strcmp(attr->value, "application/x-gzip") == 0) encoding = GZIP; else if (strcmp(attr->value, "application/x-bzip2") == 0) encoding = BZIP2; else if (strcmp(attr->value, "application/x-lzma") == 0) encoding = LZMA; else if (strcmp(attr->value, "application/x-xz") == 0) encoding = XZ; } } return (encoding); } static int getsumalgorithm(struct xmlattr_list *list) { struct xmlattr *attr; int alg = CKSUM_NONE; for (attr = list->first; attr != NULL; attr = attr->next) { if (strcmp(attr->name, "style") == 0) { const char *v = attr->value; if ((v[0] == 'S' || v[0] == 's') && (v[1] == 'H' || v[1] == 'h') && (v[2] == 'A' || v[2] == 'a') && v[3] == '1' && v[4] == '\0') alg = CKSUM_SHA1; if ((v[0] == 'M' || v[0] == 'm') && (v[1] == 'D' || v[1] == 'd') && v[2] == '5' && v[3] == '\0') alg = CKSUM_MD5; } } return (alg); } static int unknowntag_start(struct archive_read *a, struct xar *xar, const char *name) { struct unknown_tag *tag; tag = malloc(sizeof(*tag)); if (tag == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } tag->next = xar->unknowntags; archive_string_init(&(tag->name)); archive_strcpy(&(tag->name), name); if (xar->unknowntags == NULL) { #if DEBUG fprintf(stderr, "UNKNOWNTAG_START:%s\n", name); #endif xar->xmlsts_unknown = xar->xmlsts; xar->xmlsts = UNKNOWN; } xar->unknowntags = tag; return (ARCHIVE_OK); } static void unknowntag_end(struct xar *xar, const char *name) { struct unknown_tag *tag; tag = xar->unknowntags; if (tag == NULL || name == NULL) return; if (strcmp(tag->name.s, name) == 0) { xar->unknowntags = tag->next; archive_string_free(&(tag->name)); free(tag); if (xar->unknowntags == NULL) { #if DEBUG fprintf(stderr, "UNKNOWNTAG_END:%s\n", name); #endif xar->xmlsts = xar->xmlsts_unknown; } } } static int xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list) { struct xar *xar; struct xmlattr *attr; xar = (struct xar *)(a->format->data); #if DEBUG fprintf(stderr, "xml_sta:[%s]\n", name); for (attr = list->first; attr != NULL; attr = attr->next) fprintf(stderr, " attr:\"%s\"=\"%s\"\n", attr->name, attr->value); #endif xar->base64text = 0; switch (xar->xmlsts) { case INIT: if (strcmp(name, "xar") == 0) xar->xmlsts = XAR; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case XAR: if (strcmp(name, "toc") == 0) xar->xmlsts = TOC; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case TOC: if (strcmp(name, "creation-time") == 0) xar->xmlsts = TOC_CREATION_TIME; else if (strcmp(name, "checksum") == 0) xar->xmlsts = TOC_CHECKSUM; else if (strcmp(name, "file") == 0) { if (file_new(a, xar, list) != ARCHIVE_OK) return (ARCHIVE_FATAL); xar->xmlsts = TOC_FILE; } else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case TOC_CHECKSUM: if (strcmp(name, "offset") == 0) xar->xmlsts = TOC_CHECKSUM_OFFSET; else if (strcmp(name, "size") == 0) xar->xmlsts = TOC_CHECKSUM_SIZE; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case TOC_FILE: if (strcmp(name, "file") == 0) { if (file_new(a, xar, list) != ARCHIVE_OK) return (ARCHIVE_FATAL); } else if (strcmp(name, "data") == 0) xar->xmlsts = FILE_DATA; else if (strcmp(name, "ea") == 0) { if (xattr_new(a, xar, list) != ARCHIVE_OK) return (ARCHIVE_FATAL); xar->xmlsts = FILE_EA; } else if (strcmp(name, "ctime") == 0) xar->xmlsts = FILE_CTIME; else if (strcmp(name, "mtime") == 0) xar->xmlsts = FILE_MTIME; else if (strcmp(name, "atime") == 0) xar->xmlsts = FILE_ATIME; else if (strcmp(name, "group") == 0) xar->xmlsts = FILE_GROUP; else if (strcmp(name, "gid") == 0) xar->xmlsts = FILE_GID; else if (strcmp(name, "user") == 0) xar->xmlsts = FILE_USER; else if (strcmp(name, "uid") == 0) xar->xmlsts = FILE_UID; else if (strcmp(name, "mode") == 0) xar->xmlsts = FILE_MODE; else if (strcmp(name, "device") == 0) xar->xmlsts = FILE_DEVICE; else if (strcmp(name, "deviceno") == 0) xar->xmlsts = FILE_DEVICENO; else if (strcmp(name, "inode") == 0) xar->xmlsts = FILE_INODE; else if (strcmp(name, "link") == 0) xar->xmlsts = FILE_LINK; else if (strcmp(name, "type") == 0) { xar->xmlsts = FILE_TYPE; for (attr = list->first; attr != NULL; attr = attr->next) { if (strcmp(attr->name, "link") != 0) continue; if (strcmp(attr->value, "original") == 0) { xar->file->hdnext = xar->hdlink_orgs; xar->hdlink_orgs = xar->file; } else { xar->file->link = (unsigned)atol10(attr->value, strlen(attr->value)); if (xar->file->link > 0) if (add_link(a, xar, xar->file) != ARCHIVE_OK) { return (ARCHIVE_FATAL); }; } } } else if (strcmp(name, "name") == 0) { xar->xmlsts = FILE_NAME; for (attr = list->first; attr != NULL; attr = attr->next) { if (strcmp(attr->name, "enctype") == 0 && strcmp(attr->value, "base64") == 0) xar->base64text = 1; } } else if (strcmp(name, "acl") == 0) xar->xmlsts = FILE_ACL; else if (strcmp(name, "flags") == 0) xar->xmlsts = FILE_FLAGS; else if (strcmp(name, "ext2") == 0) xar->xmlsts = FILE_EXT2; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case FILE_DATA: if (strcmp(name, "length") == 0) xar->xmlsts = FILE_DATA_LENGTH; else if (strcmp(name, "offset") == 0) xar->xmlsts = FILE_DATA_OFFSET; else if (strcmp(name, "size") == 0) xar->xmlsts = FILE_DATA_SIZE; else if (strcmp(name, "encoding") == 0) { xar->xmlsts = FILE_DATA_ENCODING; xar->file->encoding = getencoding(list); } else if (strcmp(name, "archived-checksum") == 0) { xar->xmlsts = FILE_DATA_A_CHECKSUM; xar->file->a_sum.alg = getsumalgorithm(list); } else if (strcmp(name, "extracted-checksum") == 0) { xar->xmlsts = FILE_DATA_E_CHECKSUM; xar->file->e_sum.alg = getsumalgorithm(list); } else if (strcmp(name, "content") == 0) xar->xmlsts = FILE_DATA_CONTENT; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case FILE_DEVICE: if (strcmp(name, "major") == 0) xar->xmlsts = FILE_DEVICE_MAJOR; else if (strcmp(name, "minor") == 0) xar->xmlsts = FILE_DEVICE_MINOR; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case FILE_DATA_CONTENT: if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case FILE_EA: if (strcmp(name, "length") == 0) xar->xmlsts = FILE_EA_LENGTH; else if (strcmp(name, "offset") == 0) xar->xmlsts = FILE_EA_OFFSET; else if (strcmp(name, "size") == 0) xar->xmlsts = FILE_EA_SIZE; else if (strcmp(name, "encoding") == 0) { xar->xmlsts = FILE_EA_ENCODING; xar->xattr->encoding = getencoding(list); } else if (strcmp(name, "archived-checksum") == 0) xar->xmlsts = FILE_EA_A_CHECKSUM; else if (strcmp(name, "extracted-checksum") == 0) xar->xmlsts = FILE_EA_E_CHECKSUM; else if (strcmp(name, "name") == 0) xar->xmlsts = FILE_EA_NAME; else if (strcmp(name, "fstype") == 0) xar->xmlsts = FILE_EA_FSTYPE; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case FILE_ACL: if (strcmp(name, "appleextended") == 0) xar->xmlsts = FILE_ACL_APPLEEXTENDED; else if (strcmp(name, "default") == 0) xar->xmlsts = FILE_ACL_DEFAULT; else if (strcmp(name, "access") == 0) xar->xmlsts = FILE_ACL_ACCESS; else if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case FILE_FLAGS: if (!xml_parse_file_flags(xar, name)) if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case FILE_EXT2: if (!xml_parse_file_ext2(xar, name)) if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; case TOC_CREATION_TIME: case TOC_CHECKSUM_OFFSET: case TOC_CHECKSUM_SIZE: case FILE_DATA_LENGTH: case FILE_DATA_OFFSET: case FILE_DATA_SIZE: case FILE_DATA_ENCODING: case FILE_DATA_A_CHECKSUM: case FILE_DATA_E_CHECKSUM: case FILE_EA_LENGTH: case FILE_EA_OFFSET: case FILE_EA_SIZE: case FILE_EA_ENCODING: case FILE_EA_A_CHECKSUM: case FILE_EA_E_CHECKSUM: case FILE_EA_NAME: case FILE_EA_FSTYPE: case FILE_CTIME: case FILE_MTIME: case FILE_ATIME: case FILE_GROUP: case FILE_GID: case FILE_USER: case FILE_UID: case FILE_INODE: case FILE_DEVICE_MAJOR: case FILE_DEVICE_MINOR: case FILE_DEVICENO: case FILE_MODE: case FILE_TYPE: case FILE_LINK: case FILE_NAME: case FILE_ACL_DEFAULT: case FILE_ACL_ACCESS: case FILE_ACL_APPLEEXTENDED: case FILE_FLAGS_USER_NODUMP: case FILE_FLAGS_USER_IMMUTABLE: case FILE_FLAGS_USER_APPEND: case FILE_FLAGS_USER_OPAQUE: case FILE_FLAGS_USER_NOUNLINK: case FILE_FLAGS_SYS_ARCHIVED: case FILE_FLAGS_SYS_IMMUTABLE: case FILE_FLAGS_SYS_APPEND: case FILE_FLAGS_SYS_NOUNLINK: case FILE_FLAGS_SYS_SNAPSHOT: case FILE_EXT2_SecureDeletion: case FILE_EXT2_Undelete: case FILE_EXT2_Compress: case FILE_EXT2_Synchronous: case FILE_EXT2_Immutable: case FILE_EXT2_AppendOnly: case FILE_EXT2_NoDump: case FILE_EXT2_NoAtime: case FILE_EXT2_CompDirty: case FILE_EXT2_CompBlock: case FILE_EXT2_NoCompBlock: case FILE_EXT2_CompError: case FILE_EXT2_BTree: case FILE_EXT2_HashIndexed: case FILE_EXT2_iMagic: case FILE_EXT2_Journaled: case FILE_EXT2_NoTail: case FILE_EXT2_DirSync: case FILE_EXT2_TopDir: case FILE_EXT2_Reserved: case UNKNOWN: if (unknowntag_start(a, xar, name) != ARCHIVE_OK) return (ARCHIVE_FATAL); break; } return (ARCHIVE_OK); } static void xml_end(void *userData, const char *name) { struct archive_read *a; struct xar *xar; a = (struct archive_read *)userData; xar = (struct xar *)(a->format->data); #if DEBUG fprintf(stderr, "xml_end:[%s]\n", name); #endif switch (xar->xmlsts) { case INIT: break; case XAR: if (strcmp(name, "xar") == 0) xar->xmlsts = INIT; break; case TOC: if (strcmp(name, "toc") == 0) xar->xmlsts = XAR; break; case TOC_CREATION_TIME: if (strcmp(name, "creation-time") == 0) xar->xmlsts = TOC; break; case TOC_CHECKSUM: if (strcmp(name, "checksum") == 0) xar->xmlsts = TOC; break; case TOC_CHECKSUM_OFFSET: if (strcmp(name, "offset") == 0) xar->xmlsts = TOC_CHECKSUM; break; case TOC_CHECKSUM_SIZE: if (strcmp(name, "size") == 0) xar->xmlsts = TOC_CHECKSUM; break; case TOC_FILE: if (strcmp(name, "file") == 0) { if (xar->file->parent != NULL && ((xar->file->mode & AE_IFMT) == AE_IFDIR)) xar->file->parent->subdirs++; xar->file = xar->file->parent; if (xar->file == NULL) xar->xmlsts = TOC; } break; case FILE_DATA: if (strcmp(name, "data") == 0) xar->xmlsts = TOC_FILE; break; case FILE_DATA_LENGTH: if (strcmp(name, "length") == 0) xar->xmlsts = FILE_DATA; break; case FILE_DATA_OFFSET: if (strcmp(name, "offset") == 0) xar->xmlsts = FILE_DATA; break; case FILE_DATA_SIZE: if (strcmp(name, "size") == 0) xar->xmlsts = FILE_DATA; break; case FILE_DATA_ENCODING: if (strcmp(name, "encoding") == 0) xar->xmlsts = FILE_DATA; break; case FILE_DATA_A_CHECKSUM: if (strcmp(name, "archived-checksum") == 0) xar->xmlsts = FILE_DATA; break; case FILE_DATA_E_CHECKSUM: if (strcmp(name, "extracted-checksum") == 0) xar->xmlsts = FILE_DATA; break; case FILE_DATA_CONTENT: if (strcmp(name, "content") == 0) xar->xmlsts = FILE_DATA; break; case FILE_EA: if (strcmp(name, "ea") == 0) { xar->xmlsts = TOC_FILE; xar->xattr = NULL; } break; case FILE_EA_LENGTH: if (strcmp(name, "length") == 0) xar->xmlsts = FILE_EA; break; case FILE_EA_OFFSET: if (strcmp(name, "offset") == 0) xar->xmlsts = FILE_EA; break; case FILE_EA_SIZE: if (strcmp(name, "size") == 0) xar->xmlsts = FILE_EA; break; case FILE_EA_ENCODING: if (strcmp(name, "encoding") == 0) xar->xmlsts = FILE_EA; break; case FILE_EA_A_CHECKSUM: if (strcmp(name, "archived-checksum") == 0) xar->xmlsts = FILE_EA; break; case FILE_EA_E_CHECKSUM: if (strcmp(name, "extracted-checksum") == 0) xar->xmlsts = FILE_EA; break; case FILE_EA_NAME: if (strcmp(name, "name") == 0) xar->xmlsts = FILE_EA; break; case FILE_EA_FSTYPE: if (strcmp(name, "fstype") == 0) xar->xmlsts = FILE_EA; break; case FILE_CTIME: if (strcmp(name, "ctime") == 0) xar->xmlsts = TOC_FILE; break; case FILE_MTIME: if (strcmp(name, "mtime") == 0) xar->xmlsts = TOC_FILE; break; case FILE_ATIME: if (strcmp(name, "atime") == 0) xar->xmlsts = TOC_FILE; break; case FILE_GROUP: if (strcmp(name, "group") == 0) xar->xmlsts = TOC_FILE; break; case FILE_GID: if (strcmp(name, "gid") == 0) xar->xmlsts = TOC_FILE; break; case FILE_USER: if (strcmp(name, "user") == 0) xar->xmlsts = TOC_FILE; break; case FILE_UID: if (strcmp(name, "uid") == 0) xar->xmlsts = TOC_FILE; break; case FILE_MODE: if (strcmp(name, "mode") == 0) xar->xmlsts = TOC_FILE; break; case FILE_DEVICE: if (strcmp(name, "device") == 0) xar->xmlsts = TOC_FILE; break; case FILE_DEVICE_MAJOR: if (strcmp(name, "major") == 0) xar->xmlsts = FILE_DEVICE; break; case FILE_DEVICE_MINOR: if (strcmp(name, "minor") == 0) xar->xmlsts = FILE_DEVICE; break; case FILE_DEVICENO: if (strcmp(name, "deviceno") == 0) xar->xmlsts = TOC_FILE; break; case FILE_INODE: if (strcmp(name, "inode") == 0) xar->xmlsts = TOC_FILE; break; case FILE_LINK: if (strcmp(name, "link") == 0) xar->xmlsts = TOC_FILE; break; case FILE_TYPE: if (strcmp(name, "type") == 0) xar->xmlsts = TOC_FILE; break; case FILE_NAME: if (strcmp(name, "name") == 0) xar->xmlsts = TOC_FILE; break; case FILE_ACL: if (strcmp(name, "acl") == 0) xar->xmlsts = TOC_FILE; break; case FILE_ACL_DEFAULT: if (strcmp(name, "default") == 0) xar->xmlsts = FILE_ACL; break; case FILE_ACL_ACCESS: if (strcmp(name, "access") == 0) xar->xmlsts = FILE_ACL; break; case FILE_ACL_APPLEEXTENDED: if (strcmp(name, "appleextended") == 0) xar->xmlsts = FILE_ACL; break; case FILE_FLAGS: if (strcmp(name, "flags") == 0) xar->xmlsts = TOC_FILE; break; case FILE_FLAGS_USER_NODUMP: if (strcmp(name, "UserNoDump") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_USER_IMMUTABLE: if (strcmp(name, "UserImmutable") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_USER_APPEND: if (strcmp(name, "UserAppend") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_USER_OPAQUE: if (strcmp(name, "UserOpaque") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_USER_NOUNLINK: if (strcmp(name, "UserNoUnlink") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_SYS_ARCHIVED: if (strcmp(name, "SystemArchived") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_SYS_IMMUTABLE: if (strcmp(name, "SystemImmutable") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_SYS_APPEND: if (strcmp(name, "SystemAppend") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_SYS_NOUNLINK: if (strcmp(name, "SystemNoUnlink") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_FLAGS_SYS_SNAPSHOT: if (strcmp(name, "SystemSnapshot") == 0) xar->xmlsts = FILE_FLAGS; break; case FILE_EXT2: if (strcmp(name, "ext2") == 0) xar->xmlsts = TOC_FILE; break; case FILE_EXT2_SecureDeletion: if (strcmp(name, "SecureDeletion") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_Undelete: if (strcmp(name, "Undelete") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_Compress: if (strcmp(name, "Compress") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_Synchronous: if (strcmp(name, "Synchronous") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_Immutable: if (strcmp(name, "Immutable") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_AppendOnly: if (strcmp(name, "AppendOnly") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_NoDump: if (strcmp(name, "NoDump") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_NoAtime: if (strcmp(name, "NoAtime") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_CompDirty: if (strcmp(name, "CompDirty") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_CompBlock: if (strcmp(name, "CompBlock") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_NoCompBlock: if (strcmp(name, "NoCompBlock") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_CompError: if (strcmp(name, "CompError") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_BTree: if (strcmp(name, "BTree") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_HashIndexed: if (strcmp(name, "HashIndexed") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_iMagic: if (strcmp(name, "iMagic") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_Journaled: if (strcmp(name, "Journaled") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_NoTail: if (strcmp(name, "NoTail") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_DirSync: if (strcmp(name, "DirSync") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_TopDir: if (strcmp(name, "TopDir") == 0) xar->xmlsts = FILE_EXT2; break; case FILE_EXT2_Reserved: if (strcmp(name, "Reserved") == 0) xar->xmlsts = FILE_EXT2; break; case UNKNOWN: unknowntag_end(xar, name); break; } } static const int base64[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00 - 0F */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10 - 1F */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 20 - 2F */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 30 - 3F */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 40 - 4F */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 50 - 5F */ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 70 - 7F */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 8F */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90 - 9F */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0 - AF */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0 - BF */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0 - CF */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0 - DF */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0 - EF */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* F0 - FF */ }; static void strappend_base64(struct xar *xar, struct archive_string *as, const char *s, size_t l) { unsigned char buff[256]; unsigned char *out; const unsigned char *b; size_t len; (void)xar; /* UNUSED */ len = 0; out = buff; b = (const unsigned char *)s; while (l > 0) { int n = 0; if (l > 0) { if (base64[b[0]] < 0 || base64[b[1]] < 0) break; n = base64[*b++] << 18; n |= base64[*b++] << 12; *out++ = n >> 16; len++; l -= 2; } if (l > 0) { if (base64[*b] < 0) break; n |= base64[*b++] << 6; *out++ = (n >> 8) & 0xFF; len++; --l; } if (l > 0) { if (base64[*b] < 0) break; n |= base64[*b++]; *out++ = n & 0xFF; len++; --l; } if (len+3 >= sizeof(buff)) { archive_strncat(as, (const char *)buff, len); len = 0; out = buff; } } if (len > 0) archive_strncat(as, (const char *)buff, len); } static int is_string(const char *known, const char *data, size_t len) { if (strlen(known) != len) return -1; return memcmp(data, known, len); } static void xml_data(void *userData, const char *s, int len) { struct archive_read *a; struct xar *xar; a = (struct archive_read *)userData; xar = (struct xar *)(a->format->data); #if DEBUG { char buff[1024]; if (len > (int)(sizeof(buff)-1)) len = (int)(sizeof(buff)-1); strncpy(buff, s, len); buff[len] = 0; fprintf(stderr, "\tlen=%d:\"%s\"\n", len, buff); } #endif switch (xar->xmlsts) { case TOC_CHECKSUM_OFFSET: xar->toc_chksum_offset = atol10(s, len); break; case TOC_CHECKSUM_SIZE: xar->toc_chksum_size = atol10(s, len); break; default: break; } if (xar->file == NULL) return; switch (xar->xmlsts) { case FILE_NAME: if (xar->file->parent != NULL) { archive_string_concat(&(xar->file->pathname), &(xar->file->parent->pathname)); archive_strappend_char(&(xar->file->pathname), '/'); } xar->file->has |= HAS_PATHNAME; if (xar->base64text) { strappend_base64(xar, &(xar->file->pathname), s, len); } else archive_strncat(&(xar->file->pathname), s, len); break; case FILE_LINK: xar->file->has |= HAS_SYMLINK; archive_strncpy(&(xar->file->symlink), s, len); break; case FILE_TYPE: if (is_string("file", s, len) == 0 || is_string("hardlink", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFREG; if (is_string("directory", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFDIR; if (is_string("symlink", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFLNK; if (is_string("character special", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFCHR; if (is_string("block special", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFBLK; if (is_string("socket", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFSOCK; if (is_string("fifo", s, len) == 0) xar->file->mode = (xar->file->mode & ~AE_IFMT) | AE_IFIFO; xar->file->has |= HAS_TYPE; break; case FILE_INODE: xar->file->has |= HAS_INO; xar->file->ino64 = atol10(s, len); break; case FILE_DEVICE_MAJOR: xar->file->has |= HAS_DEVMAJOR; xar->file->devmajor = (dev_t)atol10(s, len); break; case FILE_DEVICE_MINOR: xar->file->has |= HAS_DEVMINOR; xar->file->devminor = (dev_t)atol10(s, len); break; case FILE_DEVICENO: xar->file->has |= HAS_DEV; xar->file->dev = (dev_t)atol10(s, len); break; case FILE_MODE: xar->file->has |= HAS_MODE; xar->file->mode = (xar->file->mode & AE_IFMT) | ((mode_t)(atol8(s, len)) & ~AE_IFMT); break; case FILE_GROUP: xar->file->has |= HAS_GID; archive_strncpy(&(xar->file->gname), s, len); break; case FILE_GID: xar->file->has |= HAS_GID; xar->file->gid = atol10(s, len); break; case FILE_USER: xar->file->has |= HAS_UID; archive_strncpy(&(xar->file->uname), s, len); break; case FILE_UID: xar->file->has |= HAS_UID; xar->file->uid = atol10(s, len); break; case FILE_CTIME: xar->file->has |= HAS_TIME | HAS_CTIME; xar->file->ctime = parse_time(s, len); break; case FILE_MTIME: xar->file->has |= HAS_TIME | HAS_MTIME; xar->file->mtime = parse_time(s, len); break; case FILE_ATIME: xar->file->has |= HAS_TIME | HAS_ATIME; xar->file->atime = parse_time(s, len); break; case FILE_DATA_LENGTH: xar->file->has |= HAS_DATA; xar->file->length = atol10(s, len); break; case FILE_DATA_OFFSET: xar->file->has |= HAS_DATA; xar->file->offset = atol10(s, len); break; case FILE_DATA_SIZE: xar->file->has |= HAS_DATA; xar->file->size = atol10(s, len); break; case FILE_DATA_A_CHECKSUM: xar->file->a_sum.len = atohex(xar->file->a_sum.val, sizeof(xar->file->a_sum.val), s, len); break; case FILE_DATA_E_CHECKSUM: xar->file->e_sum.len = atohex(xar->file->e_sum.val, sizeof(xar->file->e_sum.val), s, len); break; case FILE_EA_LENGTH: xar->file->has |= HAS_XATTR; xar->xattr->length = atol10(s, len); break; case FILE_EA_OFFSET: xar->file->has |= HAS_XATTR; xar->xattr->offset = atol10(s, len); break; case FILE_EA_SIZE: xar->file->has |= HAS_XATTR; xar->xattr->size = atol10(s, len); break; case FILE_EA_A_CHECKSUM: xar->file->has |= HAS_XATTR; xar->xattr->a_sum.len = atohex(xar->xattr->a_sum.val, sizeof(xar->xattr->a_sum.val), s, len); break; case FILE_EA_E_CHECKSUM: xar->file->has |= HAS_XATTR; xar->xattr->e_sum.len = atohex(xar->xattr->e_sum.val, sizeof(xar->xattr->e_sum.val), s, len); break; case FILE_EA_NAME: xar->file->has |= HAS_XATTR; archive_strncpy(&(xar->xattr->name), s, len); break; case FILE_EA_FSTYPE: xar->file->has |= HAS_XATTR; archive_strncpy(&(xar->xattr->fstype), s, len); break; break; case FILE_ACL_DEFAULT: case FILE_ACL_ACCESS: case FILE_ACL_APPLEEXTENDED: xar->file->has |= HAS_ACL; /* TODO */ break; case INIT: case XAR: case TOC: case TOC_CREATION_TIME: case TOC_CHECKSUM: case TOC_CHECKSUM_OFFSET: case TOC_CHECKSUM_SIZE: case TOC_FILE: case FILE_DATA: case FILE_DATA_ENCODING: case FILE_DATA_CONTENT: case FILE_DEVICE: case FILE_EA: case FILE_EA_ENCODING: case FILE_ACL: case FILE_FLAGS: case FILE_FLAGS_USER_NODUMP: case FILE_FLAGS_USER_IMMUTABLE: case FILE_FLAGS_USER_APPEND: case FILE_FLAGS_USER_OPAQUE: case FILE_FLAGS_USER_NOUNLINK: case FILE_FLAGS_SYS_ARCHIVED: case FILE_FLAGS_SYS_IMMUTABLE: case FILE_FLAGS_SYS_APPEND: case FILE_FLAGS_SYS_NOUNLINK: case FILE_FLAGS_SYS_SNAPSHOT: case FILE_EXT2: case FILE_EXT2_SecureDeletion: case FILE_EXT2_Undelete: case FILE_EXT2_Compress: case FILE_EXT2_Synchronous: case FILE_EXT2_Immutable: case FILE_EXT2_AppendOnly: case FILE_EXT2_NoDump: case FILE_EXT2_NoAtime: case FILE_EXT2_CompDirty: case FILE_EXT2_CompBlock: case FILE_EXT2_NoCompBlock: case FILE_EXT2_CompError: case FILE_EXT2_BTree: case FILE_EXT2_HashIndexed: case FILE_EXT2_iMagic: case FILE_EXT2_Journaled: case FILE_EXT2_NoTail: case FILE_EXT2_DirSync: case FILE_EXT2_TopDir: case FILE_EXT2_Reserved: case UNKNOWN: break; } } /* * BSD file flags. */ static int xml_parse_file_flags(struct xar *xar, const char *name) { const char *flag = NULL; if (strcmp(name, "UserNoDump") == 0) { xar->xmlsts = FILE_FLAGS_USER_NODUMP; flag = "nodump"; } else if (strcmp(name, "UserImmutable") == 0) { xar->xmlsts = FILE_FLAGS_USER_IMMUTABLE; flag = "uimmutable"; } else if (strcmp(name, "UserAppend") == 0) { xar->xmlsts = FILE_FLAGS_USER_APPEND; flag = "uappend"; } else if (strcmp(name, "UserOpaque") == 0) { xar->xmlsts = FILE_FLAGS_USER_OPAQUE; flag = "opaque"; } else if (strcmp(name, "UserNoUnlink") == 0) { xar->xmlsts = FILE_FLAGS_USER_NOUNLINK; flag = "nouunlink"; } else if (strcmp(name, "SystemArchived") == 0) { xar->xmlsts = FILE_FLAGS_SYS_ARCHIVED; flag = "archived"; } else if (strcmp(name, "SystemImmutable") == 0) { xar->xmlsts = FILE_FLAGS_SYS_IMMUTABLE; flag = "simmutable"; } else if (strcmp(name, "SystemAppend") == 0) { xar->xmlsts = FILE_FLAGS_SYS_APPEND; flag = "sappend"; } else if (strcmp(name, "SystemNoUnlink") == 0) { xar->xmlsts = FILE_FLAGS_SYS_NOUNLINK; flag = "nosunlink"; } else if (strcmp(name, "SystemSnapshot") == 0) { xar->xmlsts = FILE_FLAGS_SYS_SNAPSHOT; flag = "snapshot"; } if (flag == NULL) return (0); xar->file->has |= HAS_FFLAGS; if (archive_strlen(&(xar->file->fflags_text)) > 0) archive_strappend_char(&(xar->file->fflags_text), ','); archive_strcat(&(xar->file->fflags_text), flag); return (1); } /* * Linux file flags. */ static int xml_parse_file_ext2(struct xar *xar, const char *name) { const char *flag = NULL; if (strcmp(name, "SecureDeletion") == 0) { xar->xmlsts = FILE_EXT2_SecureDeletion; flag = "securedeletion"; } else if (strcmp(name, "Undelete") == 0) { xar->xmlsts = FILE_EXT2_Undelete; flag = "nouunlink"; } else if (strcmp(name, "Compress") == 0) { xar->xmlsts = FILE_EXT2_Compress; flag = "compress"; } else if (strcmp(name, "Synchronous") == 0) { xar->xmlsts = FILE_EXT2_Synchronous; flag = "sync"; } else if (strcmp(name, "Immutable") == 0) { xar->xmlsts = FILE_EXT2_Immutable; flag = "simmutable"; } else if (strcmp(name, "AppendOnly") == 0) { xar->xmlsts = FILE_EXT2_AppendOnly; flag = "sappend"; } else if (strcmp(name, "NoDump") == 0) { xar->xmlsts = FILE_EXT2_NoDump; flag = "nodump"; } else if (strcmp(name, "NoAtime") == 0) { xar->xmlsts = FILE_EXT2_NoAtime; flag = "noatime"; } else if (strcmp(name, "CompDirty") == 0) { xar->xmlsts = FILE_EXT2_CompDirty; flag = "compdirty"; } else if (strcmp(name, "CompBlock") == 0) { xar->xmlsts = FILE_EXT2_CompBlock; flag = "comprblk"; } else if (strcmp(name, "NoCompBlock") == 0) { xar->xmlsts = FILE_EXT2_NoCompBlock; flag = "nocomprblk"; } else if (strcmp(name, "CompError") == 0) { xar->xmlsts = FILE_EXT2_CompError; flag = "comperr"; } else if (strcmp(name, "BTree") == 0) { xar->xmlsts = FILE_EXT2_BTree; flag = "btree"; } else if (strcmp(name, "HashIndexed") == 0) { xar->xmlsts = FILE_EXT2_HashIndexed; flag = "hashidx"; } else if (strcmp(name, "iMagic") == 0) { xar->xmlsts = FILE_EXT2_iMagic; flag = "imagic"; } else if (strcmp(name, "Journaled") == 0) { xar->xmlsts = FILE_EXT2_Journaled; flag = "journal"; } else if (strcmp(name, "NoTail") == 0) { xar->xmlsts = FILE_EXT2_NoTail; flag = "notail"; } else if (strcmp(name, "DirSync") == 0) { xar->xmlsts = FILE_EXT2_DirSync; flag = "dirsync"; } else if (strcmp(name, "TopDir") == 0) { xar->xmlsts = FILE_EXT2_TopDir; flag = "topdir"; } else if (strcmp(name, "Reserved") == 0) { xar->xmlsts = FILE_EXT2_Reserved; flag = "reserved"; } if (flag == NULL) return (0); if (archive_strlen(&(xar->file->fflags_text)) > 0) archive_strappend_char(&(xar->file->fflags_text), ','); archive_strcat(&(xar->file->fflags_text), flag); return (1); } #ifdef HAVE_LIBXML_XMLREADER_H static int xml2_xmlattr_setup(struct archive_read *a, struct xmlattr_list *list, xmlTextReaderPtr reader) { struct xmlattr *attr; int r; list->first = NULL; list->last = &(list->first); r = xmlTextReaderMoveToFirstAttribute(reader); while (r == 1) { attr = malloc(sizeof*(attr)); if (attr == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } attr->name = strdup( (const char *)xmlTextReaderConstLocalName(reader)); if (attr->name == NULL) { free(attr); archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } attr->value = strdup( (const char *)xmlTextReaderConstValue(reader)); if (attr->value == NULL) { free(attr->name); free(attr); archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } attr->next = NULL; *list->last = attr; list->last = &(attr->next); r = xmlTextReaderMoveToNextAttribute(reader); } return (r); } static int xml2_read_cb(void *context, char *buffer, int len) { struct archive_read *a; struct xar *xar; const void *d; size_t outbytes; size_t used = 0; int r; a = (struct archive_read *)context; xar = (struct xar *)(a->format->data); if (xar->toc_remaining <= 0) return (0); d = buffer; outbytes = len; r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining); if (r != ARCHIVE_OK) return (r); __archive_read_consume(a, used); xar->toc_remaining -= used; xar->offset += used; xar->toc_total += outbytes; PRINT_TOC(buffer, len); return ((int)outbytes); } static int xml2_close_cb(void *context) { (void)context; /* UNUSED */ return (0); } static void xml2_error_hdr(void *arg, const char *msg, xmlParserSeverities severity, xmlTextReaderLocatorPtr locator) { struct archive_read *a; (void)locator; /* UNUSED */ a = (struct archive_read *)arg; switch (severity) { case XML_PARSER_SEVERITY_VALIDITY_WARNING: case XML_PARSER_SEVERITY_WARNING: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "XML Parsing error: %s", msg); break; case XML_PARSER_SEVERITY_VALIDITY_ERROR: case XML_PARSER_SEVERITY_ERROR: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "XML Parsing error: %s", msg); break; } } static int xml2_read_toc(struct archive_read *a) { xmlTextReaderPtr reader; struct xmlattr_list list; int r; reader = xmlReaderForIO(xml2_read_cb, xml2_close_cb, a, NULL, NULL, 0); if (reader == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory for xml parser"); return (ARCHIVE_FATAL); } xmlTextReaderSetErrorHandler(reader, xml2_error_hdr, a); while ((r = xmlTextReaderRead(reader)) == 1) { const char *name, *value; int type, empty; type = xmlTextReaderNodeType(reader); name = (const char *)xmlTextReaderConstLocalName(reader); switch (type) { case XML_READER_TYPE_ELEMENT: empty = xmlTextReaderIsEmptyElement(reader); r = xml2_xmlattr_setup(a, &list, reader); if (r == ARCHIVE_OK) r = xml_start(a, name, &list); xmlattr_cleanup(&list); if (r != ARCHIVE_OK) return (r); if (empty) xml_end(a, name); break; case XML_READER_TYPE_END_ELEMENT: xml_end(a, name); break; case XML_READER_TYPE_TEXT: value = (const char *)xmlTextReaderConstValue(reader); xml_data(a, value, strlen(value)); break; case XML_READER_TYPE_SIGNIFICANT_WHITESPACE: default: break; } if (r < 0) break; } xmlFreeTextReader(reader); xmlCleanupParser(); return ((r == 0)?ARCHIVE_OK:ARCHIVE_FATAL); } #elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) static int expat_xmlattr_setup(struct archive_read *a, struct xmlattr_list *list, const XML_Char **atts) { struct xmlattr *attr; char *name, *value; list->first = NULL; list->last = &(list->first); if (atts == NULL) return (ARCHIVE_OK); while (atts[0] != NULL && atts[1] != NULL) { attr = malloc(sizeof*(attr)); name = strdup(atts[0]); value = strdup(atts[1]); if (attr == NULL || name == NULL || value == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); free(attr); free(name); free(value); return (ARCHIVE_FATAL); } attr->name = name; attr->value = value; attr->next = NULL; *list->last = attr; list->last = &(attr->next); atts += 2; } return (ARCHIVE_OK); } static void expat_start_cb(void *userData, const XML_Char *name, const XML_Char **atts) { struct expat_userData *ud = (struct expat_userData *)userData; struct archive_read *a = ud->archive; struct xmlattr_list list; int r; r = expat_xmlattr_setup(a, &list, atts); if (r == ARCHIVE_OK) r = xml_start(a, (const char *)name, &list); xmlattr_cleanup(&list); ud->state = r; } static void expat_end_cb(void *userData, const XML_Char *name) { struct expat_userData *ud = (struct expat_userData *)userData; xml_end(ud->archive, (const char *)name); } static void expat_data_cb(void *userData, const XML_Char *s, int len) { struct expat_userData *ud = (struct expat_userData *)userData; xml_data(ud->archive, s, len); } static int expat_read_toc(struct archive_read *a) { struct xar *xar; XML_Parser parser; struct expat_userData ud; ud.state = ARCHIVE_OK; ud.archive = a; xar = (struct xar *)(a->format->data); /* Initialize XML Parser library. */ parser = XML_ParserCreate(NULL); if (parser == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory for xml parser"); return (ARCHIVE_FATAL); } XML_SetUserData(parser, &ud); XML_SetElementHandler(parser, expat_start_cb, expat_end_cb); XML_SetCharacterDataHandler(parser, expat_data_cb); xar->xmlsts = INIT; while (xar->toc_remaining && ud.state == ARCHIVE_OK) { enum XML_Status xr; const void *d; size_t outbytes; size_t used; int r; d = NULL; r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining); if (r != ARCHIVE_OK) return (r); xar->toc_remaining -= used; xar->offset += used; xar->toc_total += outbytes; PRINT_TOC(d, outbytes); xr = XML_Parse(parser, d, outbytes, xar->toc_remaining == 0); __archive_read_consume(a, used); if (xr == XML_STATUS_ERROR) { XML_ParserFree(parser); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "XML Parsing failed"); return (ARCHIVE_FATAL); } } XML_ParserFree(parser); return (ud.state); } #endif /* defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) */ #endif /* Support xar format */ Index: stable/10/contrib/libarchive/libarchive/archive_read_support_format_zip.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_read_support_format_zip.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_read_support_format_zip.c (revision 344674) @@ -1,3148 +1,3888 @@ /*- * Copyright (c) 2004-2013 Tim Kientzle * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA * Copyright (c) 2013 Konrad Kleine * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); /* * The definitive documentation of the Zip file format is: * http://www.pkware.com/documents/casestudies/APPNOTE.TXT * * The Info-Zip project has pioneered various extensions to better * support Zip on Unix, including the 0x5455 "UT", 0x5855 "UX", 0x7855 * "Ux", and 0x7875 "ux" extensions for time and ownership * information. * * History of this code: The streaming Zip reader was first added to * libarchive in January 2005. Support for seekable input sources was * added in Nov 2011. Zip64 support (including a significant code * refactoring) was added in 2014. */ #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_ZLIB_H #include #endif +#ifdef HAVE_BZLIB_H +#include +#endif +#ifdef HAVE_LZMA_H +#include +#endif #include "archive.h" #include "archive_digest_private.h" #include "archive_cryptor_private.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_hmac_private.h" #include "archive_private.h" #include "archive_rb.h" #include "archive_read_private.h" +#include "archive_ppmd8_private.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" #endif struct zip_entry { struct archive_rb_node node; struct zip_entry *next; int64_t local_header_offset; int64_t compressed_size; int64_t uncompressed_size; int64_t gid; int64_t uid; struct archive_string rsrcname; time_t mtime; time_t atime; time_t ctime; uint32_t crc32; uint16_t mode; uint16_t zip_flags; /* From GP Flags Field */ unsigned char compression; unsigned char system; /* From "version written by" */ unsigned char flags; /* Our extra markers. */ unsigned char decdat;/* Used for Decryption check */ /* WinZip AES encryption extra field should be available * when compression is 99. */ struct { /* Vendor version: AE-1 - 0x0001, AE-2 - 0x0002 */ unsigned vendor; #define AES_VENDOR_AE_1 0x0001 #define AES_VENDOR_AE_2 0x0002 /* AES encryption strength: * 1 - 128 bits, 2 - 192 bits, 2 - 256 bits. */ unsigned strength; /* Actual compression method. */ unsigned char compression; } aes_extra; }; struct trad_enc_ctx { uint32_t keys[3]; }; /* Bits used in zip_flags. */ #define ZIP_ENCRYPTED (1 << 0) #define ZIP_LENGTH_AT_END (1 << 3) #define ZIP_STRONG_ENCRYPTED (1 << 6) #define ZIP_UTF8_NAME (1 << 11) /* See "7.2 Single Password Symmetric Encryption Method" in http://www.pkware.com/documents/casestudies/APPNOTE.TXT */ #define ZIP_CENTRAL_DIRECTORY_ENCRYPTED (1 << 13) /* Bits used in flags. */ #define LA_USED_ZIP64 (1 << 0) #define LA_FROM_CENTRAL_DIRECTORY (1 << 1) /* * See "WinZip - AES Encryption Information" * http://www.winzip.com/aes_info.htm */ /* Value used in compression method. */ #define WINZIP_AES_ENCRYPTION 99 /* Authentication code size. */ #define AUTH_CODE_SIZE 10 /**/ #define MAX_DERIVED_KEY_BUF_SIZE (AES_MAX_KEY_SIZE * 2 + 2) struct zip { /* Structural information about the archive. */ struct archive_string format_name; int64_t central_directory_offset; size_t central_directory_entries_total; size_t central_directory_entries_on_this_disk; int has_encrypted_entries; /* List of entries (seekable Zip only) */ struct zip_entry *zip_entries; struct archive_rb_tree tree; struct archive_rb_tree tree_rsrc; /* Bytes read but not yet consumed via __archive_read_consume() */ size_t unconsumed; /* Information about entry we're currently reading. */ struct zip_entry *entry; int64_t entry_bytes_remaining; /* These count the number of bytes actually read for the entry. */ int64_t entry_compressed_bytes_read; int64_t entry_uncompressed_bytes_read; /* Running CRC32 of the decompressed data */ unsigned long entry_crc32; unsigned long (*crc32func)(unsigned long, const void *, size_t); char ignore_crc32; /* Flags to mark progress of decompression. */ char decompress_init; char end_of_entry; -#ifdef HAVE_ZLIB_H unsigned char *uncompressed_buffer; size_t uncompressed_buffer_size; + +#ifdef HAVE_ZLIB_H z_stream stream; char stream_valid; #endif +#if HAVE_LZMA_H && HAVE_LIBLZMA + lzma_stream zipx_lzma_stream; + char zipx_lzma_valid; +#endif + +#ifdef HAVE_BZLIB_H + bz_stream bzstream; + char bzstream_valid; +#endif + + IByteIn zipx_ppmd_stream; + ssize_t zipx_ppmd_read_compressed; + CPpmd8 ppmd8; + char ppmd8_valid; + struct archive_string_conv *sconv; struct archive_string_conv *sconv_default; struct archive_string_conv *sconv_utf8; int init_default_conversion; int process_mac_extensions; char init_decryption; /* Decryption buffer. */ /* * The decrypted data starts at decrypted_ptr and * extends for decrypted_bytes_remaining. Decryption * adds new data to the end of this block, data is returned * to clients from the beginning. When the block hits the * end of decrypted_buffer, it has to be shuffled back to * the beginning of the buffer. */ unsigned char *decrypted_buffer; unsigned char *decrypted_ptr; size_t decrypted_buffer_size; size_t decrypted_bytes_remaining; size_t decrypted_unconsumed_bytes; /* Traditional PKWARE decryption. */ struct trad_enc_ctx tctx; char tctx_valid; /* WinZip AES decryption. */ /* Contexts used for AES decryption. */ archive_crypto_ctx cctx; char cctx_valid; archive_hmac_sha1_ctx hctx; char hctx_valid; /* Strong encryption's decryption header information. */ unsigned iv_size; unsigned alg_id; unsigned bit_len; unsigned flags; unsigned erd_size; unsigned v_size; unsigned v_crc32; uint8_t *iv; uint8_t *erd; uint8_t *v_data; }; /* Many systems define min or MIN, but not all. */ #define zipmin(a,b) ((a) < (b) ? (a) : (b)) +/* This function is used by Ppmd8_DecodeSymbol during decompression of Ppmd8 + * streams inside ZIP files. It has 2 purposes: one is to fetch the next + * compressed byte from the stream, second one is to increase the counter how + * many compressed bytes were read. */ +static Byte +ppmd_read(void* p) { + /* Get the handle to current decompression context. */ + struct archive_read *a = ((IByteIn*)p)->a; + struct zip *zip = (struct zip*) a->format->data; + + /* Fetch next byte. */ + const uint8_t* data = __archive_read_ahead(a, 1, NULL); + __archive_read_consume(a, 1); + + /* Increment the counter. */ + ++zip->zipx_ppmd_read_compressed; + + /* Return the next compressed byte. */ + return data[0]; +} + /* ------------------------------------------------------------------------ */ /* Traditional PKWARE Decryption functions. */ static void trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c) { uint8_t t; #define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL) ctx->keys[0] = CRC32(ctx->keys[0], c); ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1; t = (ctx->keys[1] >> 24) & 0xff; ctx->keys[2] = CRC32(ctx->keys[2], t); #undef CRC32 } static uint8_t trad_enc_decrypt_byte(struct trad_enc_ctx *ctx) { unsigned temp = ctx->keys[2] | 2; return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff; } static void trad_enc_decrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in, size_t in_len, uint8_t *out, size_t out_len) { unsigned i, max; max = (unsigned)((in_len < out_len)? in_len: out_len); for (i = 0; i < max; i++) { uint8_t t = in[i] ^ trad_enc_decrypt_byte(ctx); out[i] = t; trad_enc_update_keys(ctx, t); } } static int trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len, const uint8_t *key, size_t key_len, uint8_t *crcchk) { uint8_t header[12]; if (key_len < 12) { *crcchk = 0xff; return -1; } ctx->keys[0] = 305419896L; ctx->keys[1] = 591751049L; ctx->keys[2] = 878082192L; for (;pw_len; --pw_len) trad_enc_update_keys(ctx, *pw++); trad_enc_decrypt_update(ctx, key, 12, header, 12); /* Return the last byte for CRC check. */ *crcchk = header[11]; return 0; } #if 0 static void crypt_derive_key_sha1(const void *p, int size, unsigned char *key, int key_size) { #define MD_SIZE 20 archive_sha1_ctx ctx; unsigned char md1[MD_SIZE]; unsigned char md2[MD_SIZE * 2]; unsigned char mkb[64]; int i; archive_sha1_init(&ctx); archive_sha1_update(&ctx, p, size); archive_sha1_final(&ctx, md1); memset(mkb, 0x36, sizeof(mkb)); for (i = 0; i < MD_SIZE; i++) mkb[i] ^= md1[i]; archive_sha1_init(&ctx); archive_sha1_update(&ctx, mkb, sizeof(mkb)); archive_sha1_final(&ctx, md2); memset(mkb, 0x5C, sizeof(mkb)); for (i = 0; i < MD_SIZE; i++) mkb[i] ^= md1[i]; archive_sha1_init(&ctx); archive_sha1_update(&ctx, mkb, sizeof(mkb)); archive_sha1_final(&ctx, md2 + MD_SIZE); if (key_size > 32) key_size = 32; memcpy(key, md2, key_size); #undef MD_SIZE } #endif /* * Common code for streaming or seeking modes. * * Includes code to read local file headers, decompress data * from entry bodies, and common API. */ static unsigned long real_crc32(unsigned long crc, const void *buff, size_t len) { return crc32(crc, buff, (unsigned int)len); } /* Used by "ignorecrc32" option to speed up tests. */ static unsigned long fake_crc32(unsigned long crc, const void *buff, size_t len) { (void)crc; /* UNUSED */ (void)buff; /* UNUSED */ (void)len; /* UNUSED */ return 0; } static const struct { int id; const char * name; } compression_methods[] = { {0, "uncompressed"}, /* The file is stored (no compression) */ {1, "shrinking"}, /* The file is Shrunk */ {2, "reduced-1"}, /* The file is Reduced with compression factor 1 */ {3, "reduced-2"}, /* The file is Reduced with compression factor 2 */ {4, "reduced-3"}, /* The file is Reduced with compression factor 3 */ {5, "reduced-4"}, /* The file is Reduced with compression factor 4 */ {6, "imploded"}, /* The file is Imploded */ {7, "reserved"}, /* Reserved for Tokenizing compression algorithm */ {8, "deflation"}, /* The file is Deflated */ {9, "deflation-64-bit"}, /* Enhanced Deflating using Deflate64(tm) */ {10, "ibm-terse"},/* PKWARE Data Compression Library Imploding * (old IBM TERSE) */ {11, "reserved"}, /* Reserved by PKWARE */ {12, "bzip"}, /* File is compressed using BZIP2 algorithm */ {13, "reserved"}, /* Reserved by PKWARE */ {14, "lzma"}, /* LZMA (EFS) */ {15, "reserved"}, /* Reserved by PKWARE */ {16, "reserved"}, /* Reserved by PKWARE */ {17, "reserved"}, /* Reserved by PKWARE */ {18, "ibm-terse-new"}, /* File is compressed using IBM TERSE (new) */ {19, "ibm-lz777"},/* IBM LZ77 z Architecture (PFS) */ + {95, "xz"}, /* XZ compressed data */ + {96, "jpeg"}, /* JPEG compressed data */ {97, "wav-pack"}, /* WavPack compressed data */ {98, "ppmd-1"}, /* PPMd version I, Rev 1 */ {99, "aes"} /* WinZip AES encryption */ }; static const char * compression_name(const int compression) { static const int num_compression_methods = sizeof(compression_methods)/sizeof(compression_methods[0]); int i=0; while(compression >= 0 && i < num_compression_methods) { if (compression_methods[i].id == compression) return compression_methods[i].name; i++; } return "??"; } /* Convert an MSDOS-style date/time into Unix-style time. */ static time_t zip_time(const char *p) { int msTime, msDate; struct tm ts; msTime = (0xff & (unsigned)p[0]) + 256 * (0xff & (unsigned)p[1]); msDate = (0xff & (unsigned)p[2]) + 256 * (0xff & (unsigned)p[3]); memset(&ts, 0, sizeof(ts)); ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */ ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */ ts.tm_mday = msDate & 0x1f; /* Day of month. */ ts.tm_hour = (msTime >> 11) & 0x1f; ts.tm_min = (msTime >> 5) & 0x3f; ts.tm_sec = (msTime << 1) & 0x3e; ts.tm_isdst = -1; return mktime(&ts); } /* * The extra data is stored as a list of * id1+size1+data1 + id2+size2+data2 ... * triplets. id and size are 2 bytes each. */ static int process_extra(struct archive_read *a, const char *p, size_t extra_length, struct zip_entry* zip_entry) { unsigned offset = 0; if (extra_length == 0) { return ARCHIVE_OK; } if (extra_length < 4) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Too-small extra data: Need at least 4 bytes, but only found %d bytes", (int)extra_length); return ARCHIVE_FAILED; } while (offset <= extra_length - 4) { unsigned short headerid = archive_le16dec(p + offset); unsigned short datasize = archive_le16dec(p + offset + 2); offset += 4; if (offset + datasize > extra_length) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Extra data overflow: Need %d bytes but only found %d bytes", (int)datasize, (int)(extra_length - offset)); return ARCHIVE_FAILED; } #ifdef DEBUG fprintf(stderr, "Header id 0x%04x, length %d\n", headerid, datasize); #endif switch (headerid) { case 0x0001: /* Zip64 extended information extra field. */ zip_entry->flags |= LA_USED_ZIP64; if (zip_entry->uncompressed_size == 0xffffffff) { uint64_t t = 0; if (datasize < 8 || (t = archive_le64dec(p + offset)) > INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed 64-bit uncompressed size"); return ARCHIVE_FAILED; } zip_entry->uncompressed_size = t; offset += 8; datasize -= 8; } if (zip_entry->compressed_size == 0xffffffff) { uint64_t t = 0; if (datasize < 8 || (t = archive_le64dec(p + offset)) > INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed 64-bit compressed size"); return ARCHIVE_FAILED; } zip_entry->compressed_size = t; offset += 8; datasize -= 8; } if (zip_entry->local_header_offset == 0xffffffff) { uint64_t t = 0; if (datasize < 8 || (t = archive_le64dec(p + offset)) > INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed 64-bit local header offset"); return ARCHIVE_FAILED; } zip_entry->local_header_offset = t; offset += 8; datasize -= 8; } /* archive_le32dec(p + offset) gives disk * on which file starts, but we don't handle * multi-volume Zip files. */ break; #ifdef DEBUG case 0x0017: { /* Strong encryption field. */ if (archive_le16dec(p + offset) == 2) { unsigned algId = archive_le16dec(p + offset + 2); unsigned bitLen = archive_le16dec(p + offset + 4); int flags = archive_le16dec(p + offset + 6); fprintf(stderr, "algId=0x%04x, bitLen=%u, " "flgas=%d\n", algId, bitLen,flags); } break; } #endif case 0x5455: { /* Extended time field "UT". */ int flags; if (datasize == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Incomplete extended time field"); return ARCHIVE_FAILED; } flags = p[offset]; offset++; datasize--; /* Flag bits indicate which dates are present. */ if (flags & 0x01) { #ifdef DEBUG fprintf(stderr, "mtime: %lld -> %d\n", (long long)zip_entry->mtime, archive_le32dec(p + offset)); #endif if (datasize < 4) break; zip_entry->mtime = archive_le32dec(p + offset); offset += 4; datasize -= 4; } if (flags & 0x02) { if (datasize < 4) break; zip_entry->atime = archive_le32dec(p + offset); offset += 4; datasize -= 4; } if (flags & 0x04) { if (datasize < 4) break; zip_entry->ctime = archive_le32dec(p + offset); offset += 4; datasize -= 4; } break; } case 0x5855: { /* Info-ZIP Unix Extra Field (old version) "UX". */ if (datasize >= 8) { zip_entry->atime = archive_le32dec(p + offset); zip_entry->mtime = archive_le32dec(p + offset + 4); } if (datasize >= 12) { zip_entry->uid = archive_le16dec(p + offset + 8); zip_entry->gid = archive_le16dec(p + offset + 10); } break; } case 0x6c78: { /* Experimental 'xl' field */ /* * Introduced Dec 2013 to provide a way to * include external file attributes (and other * fields that ordinarily appear only in * central directory) in local file header. * This provides file type and permission * information necessary to support full * streaming extraction. Currently being * discussed with other Zip developers * ... subject to change. * * Format: * The field starts with a bitmap that specifies * which additional fields are included. The * bitmap is variable length and can be extended in * the future. * * n bytes - feature bitmap: first byte has low-order * 7 bits. If high-order bit is set, a subsequent * byte holds the next 7 bits, etc. * * if bitmap & 1, 2 byte "version made by" * if bitmap & 2, 2 byte "internal file attributes" * if bitmap & 4, 4 byte "external file attributes" * if bitmap & 8, 2 byte comment length + n byte comment */ int bitmap, bitmap_last; if (datasize < 1) break; bitmap_last = bitmap = 0xff & p[offset]; offset += 1; datasize -= 1; /* We only support first 7 bits of bitmap; skip rest. */ while ((bitmap_last & 0x80) != 0 && datasize >= 1) { bitmap_last = p[offset]; offset += 1; datasize -= 1; } if (bitmap & 1) { /* 2 byte "version made by" */ if (datasize < 2) break; zip_entry->system = archive_le16dec(p + offset) >> 8; offset += 2; datasize -= 2; } if (bitmap & 2) { /* 2 byte "internal file attributes" */ uint32_t internal_attributes; if (datasize < 2) break; internal_attributes = archive_le16dec(p + offset); /* Not used by libarchive at present. */ (void)internal_attributes; /* UNUSED */ offset += 2; datasize -= 2; } if (bitmap & 4) { /* 4 byte "external file attributes" */ uint32_t external_attributes; if (datasize < 4) break; external_attributes = archive_le32dec(p + offset); if (zip_entry->system == 3) { zip_entry->mode = external_attributes >> 16; } else if (zip_entry->system == 0) { // Interpret MSDOS directory bit if (0x10 == (external_attributes & 0x10)) { zip_entry->mode = AE_IFDIR | 0775; } else { zip_entry->mode = AE_IFREG | 0664; } if (0x01 == (external_attributes & 0x01)) { // Read-only bit; strip write permissions zip_entry->mode &= 0555; } } else { zip_entry->mode = 0; } offset += 4; datasize -= 4; } if (bitmap & 8) { /* 2 byte comment length + comment */ uint32_t comment_length; if (datasize < 2) break; comment_length = archive_le16dec(p + offset); offset += 2; datasize -= 2; if (datasize < comment_length) break; /* Comment is not supported by libarchive */ offset += comment_length; datasize -= comment_length; } break; } case 0x7855: /* Info-ZIP Unix Extra Field (type 2) "Ux". */ #ifdef DEBUG fprintf(stderr, "uid %d gid %d\n", archive_le16dec(p + offset), archive_le16dec(p + offset + 2)); #endif if (datasize >= 2) zip_entry->uid = archive_le16dec(p + offset); if (datasize >= 4) zip_entry->gid = archive_le16dec(p + offset + 2); break; case 0x7875: { /* Info-Zip Unix Extra Field (type 3) "ux". */ int uidsize = 0, gidsize = 0; /* TODO: support arbitrary uidsize/gidsize. */ if (datasize >= 1 && p[offset] == 1) {/* version=1 */ if (datasize >= 4) { /* get a uid size. */ uidsize = 0xff & (int)p[offset+1]; if (uidsize == 2) zip_entry->uid = archive_le16dec( p + offset + 2); else if (uidsize == 4 && datasize >= 6) zip_entry->uid = archive_le32dec( p + offset + 2); } if (datasize >= (2 + uidsize + 3)) { /* get a gid size. */ gidsize = 0xff & (int)p[offset+2+uidsize]; if (gidsize == 2) zip_entry->gid = archive_le16dec( p+offset+2+uidsize+1); else if (gidsize == 4 && datasize >= (2 + uidsize + 5)) zip_entry->gid = archive_le32dec( p+offset+2+uidsize+1); } } break; } case 0x9901: /* WinZip AES extra data field. */ if (datasize < 6) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Incomplete AES field"); return ARCHIVE_FAILED; } if (p[offset + 2] == 'A' && p[offset + 3] == 'E') { /* Vendor version. */ zip_entry->aes_extra.vendor = archive_le16dec(p + offset); /* AES encryption strength. */ zip_entry->aes_extra.strength = p[offset + 4]; /* Actual compression method. */ zip_entry->aes_extra.compression = p[offset + 5]; } break; default: break; } offset += datasize; } if (offset != extra_length) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed extra data: Consumed %d bytes of %d bytes", (int)offset, (int)extra_length); return ARCHIVE_FAILED; } return ARCHIVE_OK; } /* * Assumes file pointer is at beginning of local file header. */ static int zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, struct zip *zip) { const char *p; const void *h; const wchar_t *wp; const char *cp; size_t len, filename_length, extra_length; struct archive_string_conv *sconv; struct zip_entry *zip_entry = zip->entry; struct zip_entry zip_entry_central_dir; int ret = ARCHIVE_OK; char version; /* Save a copy of the original for consistency checks. */ zip_entry_central_dir = *zip_entry; zip->decompress_init = 0; zip->end_of_entry = 0; zip->entry_uncompressed_bytes_read = 0; zip->entry_compressed_bytes_read = 0; zip->entry_crc32 = zip->crc32func(0, NULL, 0); /* Setup default conversion. */ if (zip->sconv == NULL && !zip->init_default_conversion) { zip->sconv_default = archive_string_default_conversion_for_read(&(a->archive)); zip->init_default_conversion = 1; } if ((p = __archive_read_ahead(a, 30, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } if (memcmp(p, "PK\003\004", 4) != 0) { archive_set_error(&a->archive, -1, "Damaged Zip archive"); return ARCHIVE_FATAL; } version = p[4]; zip_entry->system = p[5]; zip_entry->zip_flags = archive_le16dec(p + 6); if (zip_entry->zip_flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)) { zip->has_encrypted_entries = 1; archive_entry_set_is_data_encrypted(entry, 1); if (zip_entry->zip_flags & ZIP_CENTRAL_DIRECTORY_ENCRYPTED && zip_entry->zip_flags & ZIP_ENCRYPTED && zip_entry->zip_flags & ZIP_STRONG_ENCRYPTED) { archive_entry_set_is_metadata_encrypted(entry, 1); return ARCHIVE_FATAL; } } zip->init_decryption = (zip_entry->zip_flags & ZIP_ENCRYPTED); zip_entry->compression = (char)archive_le16dec(p + 8); zip_entry->mtime = zip_time(p + 10); zip_entry->crc32 = archive_le32dec(p + 14); if (zip_entry->zip_flags & ZIP_LENGTH_AT_END) zip_entry->decdat = p[11]; else zip_entry->decdat = p[17]; zip_entry->compressed_size = archive_le32dec(p + 18); zip_entry->uncompressed_size = archive_le32dec(p + 22); filename_length = archive_le16dec(p + 26); extra_length = archive_le16dec(p + 28); __archive_read_consume(a, 30); /* Read the filename. */ if ((h = __archive_read_ahead(a, filename_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } if (zip_entry->zip_flags & ZIP_UTF8_NAME) { /* The filename is stored to be UTF-8. */ if (zip->sconv_utf8 == NULL) { zip->sconv_utf8 = archive_string_conversion_from_charset( &a->archive, "UTF-8", 1); if (zip->sconv_utf8 == NULL) return (ARCHIVE_FATAL); } sconv = zip->sconv_utf8; } else if (zip->sconv != NULL) sconv = zip->sconv; else sconv = zip->sconv_default; if (archive_entry_copy_pathname_l(entry, h, filename_length, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Pathname cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; } __archive_read_consume(a, filename_length); /* Read the extra data. */ if ((h = __archive_read_ahead(a, extra_length, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_FATAL); } if (ARCHIVE_OK != process_extra(a, h, extra_length, zip_entry)) { return ARCHIVE_FATAL; } __archive_read_consume(a, extra_length); /* Work around a bug in Info-Zip: When reading from a pipe, it * stats the pipe instead of synthesizing a file entry. */ if ((zip_entry->mode & AE_IFMT) == AE_IFIFO) { zip_entry->mode &= ~ AE_IFMT; zip_entry->mode |= AE_IFREG; } /* If the mode is totally empty, set some sane default. */ if (zip_entry->mode == 0) { zip_entry->mode |= 0664; } /* Windows archivers sometimes use backslash as the directory separator. Normalize to slash. */ if (zip_entry->system == 0 && (wp = archive_entry_pathname_w(entry)) != NULL) { if (wcschr(wp, L'/') == NULL && wcschr(wp, L'\\') != NULL) { size_t i; struct archive_wstring s; archive_string_init(&s); archive_wstrcpy(&s, wp); for (i = 0; i < archive_strlen(&s); i++) { if (s.s[i] == '\\') s.s[i] = '/'; } archive_entry_copy_pathname_w(entry, s.s); archive_wstring_free(&s); } } /* Make sure that entries with a trailing '/' are marked as directories * even if the External File Attributes contains bogus values. If this * is not a directory and there is no type, assume regularfile. */ if ((zip_entry->mode & AE_IFMT) != AE_IFDIR) { int has_slash; wp = archive_entry_pathname_w(entry); if (wp != NULL) { len = wcslen(wp); has_slash = len > 0 && wp[len - 1] == L'/'; } else { cp = archive_entry_pathname(entry); len = (cp != NULL)?strlen(cp):0; has_slash = len > 0 && cp[len - 1] == '/'; } /* Correct file type as needed. */ if (has_slash) { zip_entry->mode &= ~AE_IFMT; zip_entry->mode |= AE_IFDIR; zip_entry->mode |= 0111; } else if ((zip_entry->mode & AE_IFMT) == 0) { zip_entry->mode |= AE_IFREG; } } /* Make sure directories end in '/' */ if ((zip_entry->mode & AE_IFMT) == AE_IFDIR) { wp = archive_entry_pathname_w(entry); if (wp != NULL) { len = wcslen(wp); if (len > 0 && wp[len - 1] != L'/') { struct archive_wstring s; archive_string_init(&s); archive_wstrcat(&s, wp); archive_wstrappend_wchar(&s, L'/'); archive_entry_copy_pathname_w(entry, s.s); archive_wstring_free(&s); } } else { cp = archive_entry_pathname(entry); len = (cp != NULL)?strlen(cp):0; if (len > 0 && cp[len - 1] != '/') { struct archive_string s; archive_string_init(&s); archive_strcat(&s, cp); archive_strappend_char(&s, '/'); archive_entry_set_pathname(entry, s.s); archive_string_free(&s); } } } if (zip_entry->flags & LA_FROM_CENTRAL_DIRECTORY) { /* If this came from the central dir, it's size info * is definitive, so ignore the length-at-end flag. */ zip_entry->zip_flags &= ~ZIP_LENGTH_AT_END; /* If local header is missing a value, use the one from the central directory. If both have it, warn about mismatches. */ if (zip_entry->crc32 == 0) { zip_entry->crc32 = zip_entry_central_dir.crc32; } else if (!zip->ignore_crc32 && zip_entry->crc32 != zip_entry_central_dir.crc32) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Inconsistent CRC32 values"); ret = ARCHIVE_WARN; } if (zip_entry->compressed_size == 0) { zip_entry->compressed_size = zip_entry_central_dir.compressed_size; } else if (zip_entry->compressed_size != zip_entry_central_dir.compressed_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Inconsistent compressed size: " "%jd in central directory, %jd in local header", (intmax_t)zip_entry_central_dir.compressed_size, (intmax_t)zip_entry->compressed_size); ret = ARCHIVE_WARN; } if (zip_entry->uncompressed_size == 0) { zip_entry->uncompressed_size = zip_entry_central_dir.uncompressed_size; } else if (zip_entry->uncompressed_size != zip_entry_central_dir.uncompressed_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Inconsistent uncompressed size: " "%jd in central directory, %jd in local header", (intmax_t)zip_entry_central_dir.uncompressed_size, (intmax_t)zip_entry->uncompressed_size); ret = ARCHIVE_WARN; } } /* Populate some additional entry fields: */ archive_entry_set_mode(entry, zip_entry->mode); archive_entry_set_uid(entry, zip_entry->uid); archive_entry_set_gid(entry, zip_entry->gid); archive_entry_set_mtime(entry, zip_entry->mtime, 0); archive_entry_set_ctime(entry, zip_entry->ctime, 0); archive_entry_set_atime(entry, zip_entry->atime, 0); if ((zip->entry->mode & AE_IFMT) == AE_IFLNK) { size_t linkname_length; if (zip_entry->compressed_size > 64 * 1024) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Zip file with oversized link entry"); return ARCHIVE_FATAL; } linkname_length = (size_t)zip_entry->compressed_size; archive_entry_set_size(entry, 0); p = __archive_read_ahead(a, linkname_length, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Truncated Zip file"); return ARCHIVE_FATAL; } sconv = zip->sconv; if (sconv == NULL && (zip->entry->zip_flags & ZIP_UTF8_NAME)) sconv = zip->sconv_utf8; if (sconv == NULL) sconv = zip->sconv_default; if (archive_entry_copy_symlink_l(entry, p, linkname_length, sconv) != 0) { if (errno != ENOMEM && sconv == zip->sconv_utf8 && (zip->entry->zip_flags & ZIP_UTF8_NAME)) archive_entry_copy_symlink_l(entry, p, linkname_length, NULL); if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Symlink"); return (ARCHIVE_FATAL); } /* * Since there is no character-set regulation for * symlink name, do not report the conversion error * in an automatic conversion. */ if (sconv != zip->sconv_utf8 || (zip->entry->zip_flags & ZIP_UTF8_NAME) == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Symlink cannot be converted " "from %s to current locale.", archive_string_conversion_charset_name( sconv)); ret = ARCHIVE_WARN; } } zip_entry->uncompressed_size = zip_entry->compressed_size = 0; if (__archive_read_consume(a, linkname_length) < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Read error skipping symlink target name"); return ARCHIVE_FATAL; } } else if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END) || zip_entry->uncompressed_size > 0) { /* Set the size only if it's meaningful. */ archive_entry_set_size(entry, zip_entry->uncompressed_size); } zip->entry_bytes_remaining = zip_entry->compressed_size; /* If there's no body, force read_data() to return EOF immediately. */ if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining < 1) zip->end_of_entry = 1; /* Set up a more descriptive format name. */ archive_string_empty(&zip->format_name); archive_string_sprintf(&zip->format_name, "ZIP %d.%d (%s)", version / 10, version % 10, compression_name(zip->entry->compression)); a->archive.archive_format_name = zip->format_name.s; return (ret); } static int check_authentication_code(struct archive_read *a, const void *_p) { struct zip *zip = (struct zip *)(a->format->data); /* Check authentication code. */ if (zip->hctx_valid) { const void *p; uint8_t hmac[20]; size_t hmac_len = 20; int cmp; archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len); if (_p == NULL) { /* Read authentication code. */ p = __archive_read_ahead(a, AUTH_CODE_SIZE, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } } else { p = _p; } cmp = memcmp(hmac, p, AUTH_CODE_SIZE); __archive_read_consume(a, AUTH_CODE_SIZE); if (cmp != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP bad Authentication code"); return (ARCHIVE_WARN); } } return (ARCHIVE_OK); } /* * Read "uncompressed" data. There are three cases: * 1) We know the size of the data. This is always true for the * seeking reader (we've examined the Central Directory already). * 2) ZIP_LENGTH_AT_END was set, but only the CRC was deferred. * Info-ZIP seems to do this; we know the size but have to grab * the CRC from the data descriptor afterwards. * 3) We're streaming and ZIP_LENGTH_AT_END was specified and * we have no size information. In this case, we can do pretty * well by watching for the data descriptor record. The data * descriptor is 16 bytes and includes a computed CRC that should * provide a strong check. * * TODO: Technically, the PK\007\010 signature is optional. * In the original spec, the data descriptor contained CRC * and size fields but had no leading signature. In practice, * newer writers seem to provide the signature pretty consistently. * * For uncompressed data, the PK\007\010 marker seems essential * to be sure we've actually seen the end of the entry. * * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets * zip->end_of_entry if it consumes all of the data. */ static int zip_read_data_none(struct archive_read *a, const void **_buff, size_t *size, int64_t *offset) { struct zip *zip; const char *buff; ssize_t bytes_avail; int r; (void)offset; /* UNUSED */ zip = (struct zip *)(a->format->data); if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) { const char *p; ssize_t grabbing_bytes = 24; if (zip->hctx_valid) grabbing_bytes += AUTH_CODE_SIZE; /* Grab at least 24 bytes. */ buff = __archive_read_ahead(a, grabbing_bytes, &bytes_avail); if (bytes_avail < grabbing_bytes) { /* Zip archives have end-of-archive markers that are longer than this, so a failure to get at least 24 bytes really does indicate a truncated file. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } /* Check for a complete PK\007\010 signature, followed * by the correct 4-byte CRC. */ p = buff; if (zip->hctx_valid) p += AUTH_CODE_SIZE; if (p[0] == 'P' && p[1] == 'K' && p[2] == '\007' && p[3] == '\010' && (archive_le32dec(p + 4) == zip->entry_crc32 || zip->ignore_crc32 || (zip->hctx_valid && zip->entry->aes_extra.vendor == AES_VENDOR_AE_2))) { if (zip->entry->flags & LA_USED_ZIP64) { uint64_t compressed, uncompressed; zip->entry->crc32 = archive_le32dec(p + 4); compressed = archive_le64dec(p + 8); uncompressed = archive_le64dec(p + 16); if (compressed > INT64_MAX || uncompressed > INT64_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Overflow of 64-bit file sizes"); return ARCHIVE_FAILED; } zip->entry->compressed_size = compressed; zip->entry->uncompressed_size = uncompressed; zip->unconsumed = 24; } else { zip->entry->crc32 = archive_le32dec(p + 4); zip->entry->compressed_size = archive_le32dec(p + 8); zip->entry->uncompressed_size = archive_le32dec(p + 12); zip->unconsumed = 16; } if (zip->hctx_valid) { r = check_authentication_code(a, buff); if (r != ARCHIVE_OK) return (r); } zip->end_of_entry = 1; return (ARCHIVE_OK); } /* If not at EOF, ensure we consume at least one byte. */ ++p; /* Scan forward until we see where a PK\007\010 signature * might be. */ /* Return bytes up until that point. On the next call, * the code above will verify the data descriptor. */ while (p < buff + bytes_avail - 4) { if (p[3] == 'P') { p += 3; } else if (p[3] == 'K') { p += 2; } else if (p[3] == '\007') { p += 1; } else if (p[3] == '\010' && p[2] == '\007' && p[1] == 'K' && p[0] == 'P') { if (zip->hctx_valid) p -= AUTH_CODE_SIZE; break; } else { p += 4; } } bytes_avail = p - buff; } else { if (zip->entry_bytes_remaining == 0) { zip->end_of_entry = 1; if (zip->hctx_valid) { r = check_authentication_code(a, NULL); if (r != ARCHIVE_OK) return (r); } return (ARCHIVE_OK); } /* Grab a bunch of bytes. */ buff = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } if (bytes_avail > zip->entry_bytes_remaining) bytes_avail = (ssize_t)zip->entry_bytes_remaining; } if (zip->tctx_valid || zip->cctx_valid) { size_t dec_size = bytes_avail; if (dec_size > zip->decrypted_buffer_size) dec_size = zip->decrypted_buffer_size; if (zip->tctx_valid) { trad_enc_decrypt_update(&zip->tctx, (const uint8_t *)buff, dec_size, zip->decrypted_buffer, dec_size); } else { size_t dsize = dec_size; archive_hmac_sha1_update(&zip->hctx, (const uint8_t *)buff, dec_size); archive_decrypto_aes_ctr_update(&zip->cctx, (const uint8_t *)buff, dec_size, zip->decrypted_buffer, &dsize); } bytes_avail = dec_size; buff = (const char *)zip->decrypted_buffer; } *size = bytes_avail; zip->entry_bytes_remaining -= bytes_avail; zip->entry_uncompressed_bytes_read += bytes_avail; zip->entry_compressed_bytes_read += bytes_avail; zip->unconsumed += bytes_avail; *_buff = buff; return (ARCHIVE_OK); } +static int +consume_optional_marker(struct archive_read *a, struct zip *zip) +{ + if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { + const char *p; + + if (NULL == (p = __archive_read_ahead(a, 24, NULL))) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated ZIP end-of-file record"); + return (ARCHIVE_FATAL); + } + /* Consume the optional PK\007\010 marker. */ + if (p[0] == 'P' && p[1] == 'K' && + p[2] == '\007' && p[3] == '\010') { + p += 4; + zip->unconsumed = 4; + } + if (zip->entry->flags & LA_USED_ZIP64) { + uint64_t compressed, uncompressed; + zip->entry->crc32 = archive_le32dec(p); + compressed = archive_le64dec(p + 4); + uncompressed = archive_le64dec(p + 12); + if (compressed > INT64_MAX || uncompressed > INT64_MAX) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, + "Overflow of 64-bit file sizes"); + return ARCHIVE_FAILED; + } + zip->entry->compressed_size = compressed; + zip->entry->uncompressed_size = uncompressed; + zip->unconsumed += 20; + } else { + zip->entry->crc32 = archive_le32dec(p); + zip->entry->compressed_size = archive_le32dec(p + 4); + zip->entry->uncompressed_size = archive_le32dec(p + 8); + zip->unconsumed += 12; + } + } + + return (ARCHIVE_OK); +} + +#if HAVE_LZMA_H && HAVE_LIBLZMA +static int +zipx_xz_init(struct archive_read *a, struct zip *zip) +{ + lzma_ret r; + + if(zip->zipx_lzma_valid) { + lzma_end(&zip->zipx_lzma_stream); + zip->zipx_lzma_valid = 0; + } + + memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream)); + r = lzma_stream_decoder(&zip->zipx_lzma_stream, UINT64_MAX, 0); + if (r != LZMA_OK) { + archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, + "xz initialization failed(%d)", + r); + + return (ARCHIVE_FAILED); + } + + zip->zipx_lzma_valid = 1; + + free(zip->uncompressed_buffer); + + zip->uncompressed_buffer_size = 256 * 1024; + zip->uncompressed_buffer = + (uint8_t*) malloc(zip->uncompressed_buffer_size); + if (zip->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for xz decompression"); + return (ARCHIVE_FATAL); + } + + zip->decompress_init = 1; + return (ARCHIVE_OK); +} + +static int +zipx_lzma_alone_init(struct archive_read *a, struct zip *zip) +{ + lzma_ret r; + const uint8_t* p; + +#pragma pack(push) +#pragma pack(1) + struct _alone_header { + uint8_t bytes[5]; + uint64_t uncompressed_size; + } alone_header; +#pragma pack(pop) + + /* To unpack ZIPX's "LZMA" (id 14) stream we can use standard liblzma that + * is a part of XZ Utils. The stream format stored inside ZIPX file is a + * modified "lzma alone" file format, that was used by the `lzma` utility + * which was later deprecated in favour of `xz` utility. Since those + * formats are nearly the same, we can use a standard "lzma alone" decoder + * from XZ Utils. */ + + memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream)); + r = lzma_alone_decoder(&zip->zipx_lzma_stream, UINT64_MAX); + if (r != LZMA_OK) { + archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, + "lzma initialization failed(%d)", r); + + return (ARCHIVE_FAILED); + } + + /* Flag the cleanup function that we want our lzma-related structures + * to be freed later. */ + zip->zipx_lzma_valid = 1; + + /* The "lzma alone" file format and the stream format inside ZIPx are + * almost the same. Here's an example of a structure of "lzma alone" + * format: + * + * $ cat /bin/ls | lzma | xxd | head -n 1 + * 00000000: 5d00 0080 00ff ffff ffff ffff ff00 2814 + * + * 5 bytes 8 bytes n bytes + * + * + * lzma_params is a 5-byte blob that has to be decoded to extract + * parameters of this LZMA stream. The uncompressed_size field is an + * uint64_t value that contains information about the size of the + * uncompressed file, or UINT64_MAX if this value is unknown. The + * part is the actual lzma-compressed data stream. + * + * Now here's the structure of the stream inside the ZIPX file: + * + * $ cat stream_inside_zipx | xxd | head -n 1 + * 00000000: 0914 0500 5d00 8000 0000 2814 .... .... + * + * 2byte 2byte 5 bytes n bytes + * + * + * This means that the ZIPX file contains an additional magic1 and magic2 + * headers, the lzma_params field contains the same parameter set as in the + * "lzma alone" format, and the field is the same as in the "lzma + * alone" format as well. Note that also the zipx format is missing the + * uncompressed_size field. + * + * So, in order to use the "lzma alone" decoder for the zipx lzma stream, + * we simply need to shuffle around some fields, prepare a new lzma alone + * header, feed it into lzma alone decoder so it will initialize itself + * properly, and then we can start feeding normal zipx lzma stream into the + * decoder. + */ + + /* Read magic1,magic2,lzma_params from the ZIPX stream. */ + if((p = __archive_read_ahead(a, 9, NULL)) == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated lzma data"); + return (ARCHIVE_FATAL); + } + + if(p[2] != 0x05 || p[3] != 0x00) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid lzma data"); + return (ARCHIVE_FATAL); + } + + /* Prepare an lzma alone header: copy the lzma_params blob into a proper + * place into the lzma alone header. */ + memcpy(&alone_header.bytes[0], p + 4, 5); + + /* Initialize the 'uncompressed size' field to unknown; we'll manually + * monitor how many bytes there are still to be uncompressed. */ + alone_header.uncompressed_size = UINT64_MAX; + + if(!zip->uncompressed_buffer) { + zip->uncompressed_buffer_size = 256 * 1024; + zip->uncompressed_buffer = + (uint8_t*) malloc(zip->uncompressed_buffer_size); + + if (zip->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for lzma decompression"); + return (ARCHIVE_FATAL); + } + } + + zip->zipx_lzma_stream.next_in = (void*) &alone_header; + zip->zipx_lzma_stream.avail_in = sizeof(alone_header); + zip->zipx_lzma_stream.total_in = 0; + zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer; + zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size; + zip->zipx_lzma_stream.total_out = 0; + + /* Feed only the header into the lzma alone decoder. This will effectively + * initialize the decoder, and will not produce any output bytes yet. */ + r = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN); + if (r != LZMA_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "lzma stream initialization error"); + return ARCHIVE_FATAL; + } + + /* We've already consumed some bytes, so take this into account. */ + __archive_read_consume(a, 9); + zip->entry_bytes_remaining -= 9; + zip->entry_compressed_bytes_read += 9; + + zip->decompress_init = 1; + return (ARCHIVE_OK); +} + +static int +zip_read_data_zipx_xz(struct archive_read *a, const void **buff, + size_t *size, int64_t *offset) +{ + struct zip* zip = (struct zip *)(a->format->data); + int ret; + lzma_ret lz_ret; + const void* compressed_buf; + ssize_t bytes_avail, in_bytes, to_consume = 0; + + (void) offset; /* UNUSED */ + + /* Initialize decompressor if not yet initialized. */ + if (!zip->decompress_init) { + ret = zipx_xz_init(a, zip); + if (ret != ARCHIVE_OK) + return (ret); + } + + compressed_buf = __archive_read_ahead(a, 1, &bytes_avail); + if (bytes_avail < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated xz file body"); + return (ARCHIVE_FATAL); + } + + in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); + zip->zipx_lzma_stream.next_in = compressed_buf; + zip->zipx_lzma_stream.avail_in = in_bytes; + zip->zipx_lzma_stream.total_in = 0; + zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer; + zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size; + zip->zipx_lzma_stream.total_out = 0; + + /* Perform the decompression. */ + lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN); + switch(lz_ret) { + case LZMA_DATA_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "xz data error (error %d)", (int) lz_ret); + return (ARCHIVE_FATAL); + + case LZMA_NO_CHECK: + case LZMA_OK: + break; + + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "xz unknown error %d", (int) lz_ret); + return (ARCHIVE_FATAL); + + case LZMA_STREAM_END: + lzma_end(&zip->zipx_lzma_stream); + zip->zipx_lzma_valid = 0; + + if((int64_t) zip->zipx_lzma_stream.total_in != + zip->entry_bytes_remaining) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "xz premature end of stream"); + return (ARCHIVE_FATAL); + } + + zip->end_of_entry = 1; + break; + } + + to_consume = zip->zipx_lzma_stream.total_in; + + __archive_read_consume(a, to_consume); + zip->entry_bytes_remaining -= to_consume; + zip->entry_compressed_bytes_read += to_consume; + zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out; + + *size = zip->zipx_lzma_stream.total_out; + *buff = zip->uncompressed_buffer; + + ret = consume_optional_marker(a, zip); + if (ret != ARCHIVE_OK) + return (ret); + + return (ARCHIVE_OK); +} + +static int +zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff, + size_t *size, int64_t *offset) +{ + struct zip* zip = (struct zip *)(a->format->data); + int ret; + lzma_ret lz_ret; + const void* compressed_buf; + ssize_t bytes_avail, in_bytes, to_consume; + + (void) offset; /* UNUSED */ + + /* Initialize decompressor if not yet initialized. */ + if (!zip->decompress_init) { + ret = zipx_lzma_alone_init(a, zip); + if (ret != ARCHIVE_OK) + return (ret); + } + + /* Fetch more compressed data. The same note as in deflate handler applies + * here as well: + * + * Note: '1' here is a performance optimization. Recall that the + * decompression layer returns a count of available bytes; asking for more + * than that forces the decompressor to combine reads by copying data. + */ + compressed_buf = __archive_read_ahead(a, 1, &bytes_avail); + if (bytes_avail < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated lzma file body"); + return (ARCHIVE_FATAL); + } + + /* Set decompressor parameters. */ + in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); + + zip->zipx_lzma_stream.next_in = compressed_buf; + zip->zipx_lzma_stream.avail_in = in_bytes; + zip->zipx_lzma_stream.total_in = 0; + zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer; + zip->zipx_lzma_stream.avail_out = + /* These lzma_alone streams lack end of stream marker, so let's make + * sure the unpacker won't try to unpack more than it's supposed to. */ + zipmin((int64_t) zip->uncompressed_buffer_size, + zip->entry->uncompressed_size - + zip->entry_uncompressed_bytes_read); + zip->zipx_lzma_stream.total_out = 0; + + /* Perform the decompression. */ + lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN); + switch(lz_ret) { + case LZMA_DATA_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "lzma data error (error %d)", (int) lz_ret); + return (ARCHIVE_FATAL); + + case LZMA_OK: + break; + + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "lzma unknown error %d", (int) lz_ret); + return (ARCHIVE_FATAL); + } + + to_consume = zip->zipx_lzma_stream.total_in; + + /* Update pointers. */ + __archive_read_consume(a, to_consume); + zip->entry_bytes_remaining -= to_consume; + zip->entry_compressed_bytes_read += to_consume; + zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out; + + if(zip->entry_bytes_remaining == 0) { + zip->end_of_entry = 1; + } + + /* Return values. */ + *size = zip->zipx_lzma_stream.total_out; + *buff = zip->uncompressed_buffer; + + /* Behave the same way as during deflate decompression. */ + ret = consume_optional_marker(a, zip); + if (ret != ARCHIVE_OK) + return (ret); + + /* Free lzma decoder handle because we'll no longer need it. */ + if(zip->end_of_entry) { + lzma_end(&zip->zipx_lzma_stream); + zip->zipx_lzma_valid = 0; + } + + /* If we're here, then we're good! */ + return (ARCHIVE_OK); +} +#endif /* HAVE_LZMA_H && HAVE_LIBLZMA */ + +static int +zipx_ppmd8_init(struct archive_read *a, struct zip *zip) +{ + const void* p; + uint32_t val; + uint32_t order; + uint32_t mem; + uint32_t restore_method; + + /* Remove previous decompression context if it exists. */ + if(zip->ppmd8_valid) { + __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8); + zip->ppmd8_valid = 0; + } + + /* Create a new decompression context. */ + __archive_ppmd8_functions.Ppmd8_Construct(&zip->ppmd8); + + /* Setup function pointers required by Ppmd8 decompressor. The + * 'ppmd_read' function will feed new bytes to the decompressor, + * and will increment the 'zip->zipx_ppmd_read_compressed' counter. */ + zip->ppmd8.Stream.In = &zip->zipx_ppmd_stream; + zip->zipx_ppmd_stream.a = a; + zip->zipx_ppmd_stream.Read = &ppmd_read; + + /* Reset number of read bytes to 0. */ + zip->zipx_ppmd_read_compressed = 0; + + /* Read Ppmd8 header (2 bytes). */ + p = __archive_read_ahead(a, 2, NULL); + if(!p) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated file data in PPMd8 stream"); + return (ARCHIVE_FATAL); + } + __archive_read_consume(a, 2); + + /* Decode the stream's compression parameters. */ + val = archive_le16dec(p); + order = (val & 15) + 1; + mem = ((val >> 4) & 0xff) + 1; + restore_method = (val >> 12); + + if(order < 2 || restore_method > 2) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Invalid parameter set in PPMd8 stream (order=%d, " + "restore=%d)", order, restore_method); + return (ARCHIVE_FAILED); + } + + /* Allocate the memory needed to properly decompress the file. */ + if(!__archive_ppmd8_functions.Ppmd8_Alloc(&zip->ppmd8, mem << 20)) { + archive_set_error(&a->archive, ENOMEM, + "Unable to allocate memory for PPMd8 stream: %d bytes", + mem << 20); + return (ARCHIVE_FATAL); + } + + /* Signal the cleanup function to release Ppmd8 context in the + * cleanup phase. */ + zip->ppmd8_valid = 1; + + /* Perform further Ppmd8 initialization. */ + if(!__archive_ppmd8_functions.Ppmd8_RangeDec_Init(&zip->ppmd8)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "PPMd8 stream range decoder initialization error"); + return (ARCHIVE_FATAL); + } + + __archive_ppmd8_functions.Ppmd8_Init(&zip->ppmd8, order, restore_method); + + /* Allocate the buffer that will hold uncompressed data. */ + free(zip->uncompressed_buffer); + + zip->uncompressed_buffer_size = 256 * 1024; + zip->uncompressed_buffer = + (uint8_t*) malloc(zip->uncompressed_buffer_size); + + if(zip->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for PPMd8 decompression"); + return ARCHIVE_FATAL; + } + + /* Ppmd8 initialization is done. */ + zip->decompress_init = 1; + + /* We've already read 2 bytes in the output stream. Additionally, + * Ppmd8 initialization code could read some data as well. So we + * are advancing the stream by 2 bytes plus whatever number of + * bytes Ppmd8 init function used. */ + zip->entry_compressed_bytes_read += 2 + zip->zipx_ppmd_read_compressed; + + return ARCHIVE_OK; +} + +static int +zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff, + size_t *size, int64_t *offset) +{ + struct zip* zip = (struct zip *)(a->format->data); + int ret; + size_t consumed_bytes = 0; + ssize_t bytes_avail = 0; + + (void) offset; /* UNUSED */ + + /* If we're here for the first time, initialize Ppmd8 decompression + * context first. */ + if(!zip->decompress_init) { + ret = zipx_ppmd8_init(a, zip); + if(ret != ARCHIVE_OK) + return ret; + } + + /* Fetch for more data. We're reading 1 byte here, but libarchive should + * prefetch more bytes. */ + (void) __archive_read_ahead(a, 1, &bytes_avail); + if(bytes_avail < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated PPMd8 file body"); + return (ARCHIVE_FATAL); + } + + /* This counter will be updated inside ppmd_read(), which at one + * point will be called by Ppmd8_DecodeSymbol. */ + zip->zipx_ppmd_read_compressed = 0; + + /* Decompression loop. */ + do { + int sym = __archive_ppmd8_functions.Ppmd8_DecodeSymbol(&zip->ppmd8); + if(sym < 0) { + zip->end_of_entry = 1; + break; + } + + zip->uncompressed_buffer[consumed_bytes] = (uint8_t) sym; + ++consumed_bytes; + } while(consumed_bytes < zip->uncompressed_buffer_size); + + /* Update pointers for libarchive. */ + *buff = zip->uncompressed_buffer; + *size = consumed_bytes; + + /* Update pointers so we can continue decompression in another call. */ + zip->entry_bytes_remaining -= zip->zipx_ppmd_read_compressed; + zip->entry_compressed_bytes_read += zip->zipx_ppmd_read_compressed; + zip->entry_uncompressed_bytes_read += consumed_bytes; + + /* If we're at the end of stream, deinitialize Ppmd8 context. */ + if(zip->end_of_entry) { + __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8); + zip->ppmd8_valid = 0; + } + + /* Seek for optional marker, same way as in each zip entry. */ + ret = consume_optional_marker(a, zip); + if (ret != ARCHIVE_OK) + return ret; + + return ARCHIVE_OK; +} + +#ifdef HAVE_BZLIB_H +static int +zipx_bzip2_init(struct archive_read *a, struct zip *zip) +{ + int r; + + /* Deallocate already existing BZ2 decompression context if it + * exists. */ + if(zip->bzstream_valid) { + BZ2_bzDecompressEnd(&zip->bzstream); + zip->bzstream_valid = 0; + } + + /* Allocate a new BZ2 decompression context. */ + memset(&zip->bzstream, 0, sizeof(bz_stream)); + r = BZ2_bzDecompressInit(&zip->bzstream, 0, 1); + if(r != BZ_OK) { + archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, + "bzip2 initialization failed(%d)", + r); + + return ARCHIVE_FAILED; + } + + /* Mark the bzstream field to be released in cleanup phase. */ + zip->bzstream_valid = 1; + + /* (Re)allocate the buffer that will contain decompressed bytes. */ + free(zip->uncompressed_buffer); + + zip->uncompressed_buffer_size = 256 * 1024; + zip->uncompressed_buffer = + (uint8_t*) malloc(zip->uncompressed_buffer_size); + if (zip->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory for bzip2 decompression"); + return ARCHIVE_FATAL; + } + + /* Initialization done. */ + zip->decompress_init = 1; + return ARCHIVE_OK; +} + +static int +zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff, + size_t *size, int64_t *offset) +{ + struct zip *zip = (struct zip *)(a->format->data); + ssize_t bytes_avail = 0, in_bytes, to_consume; + const void *compressed_buff; + int r; + uint64_t total_out; + + (void) offset; /* UNUSED */ + + /* Initialize decompression context if we're here for the first time. */ + if(!zip->decompress_init) { + r = zipx_bzip2_init(a, zip); + if(r != ARCHIVE_OK) + return r; + } + + /* Fetch more compressed bytes. */ + compressed_buff = __archive_read_ahead(a, 1, &bytes_avail); + if(bytes_avail < 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Truncated bzip2 file body"); + return (ARCHIVE_FATAL); + } + + in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail); + + /* Setup buffer boundaries. */ + zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff; + zip->bzstream.avail_in = in_bytes; + zip->bzstream.total_in_hi32 = 0; + zip->bzstream.total_in_lo32 = 0; + zip->bzstream.next_out = (char*) zip->uncompressed_buffer; + zip->bzstream.avail_out = zip->uncompressed_buffer_size; + zip->bzstream.total_out_hi32 = 0; + zip->bzstream.total_out_lo32 = 0; + + /* Perform the decompression. */ + r = BZ2_bzDecompress(&zip->bzstream); + switch(r) { + case BZ_STREAM_END: + /* If we're at the end of the stream, deinitialize the + * decompression context now. */ + switch(BZ2_bzDecompressEnd(&zip->bzstream)) { + case BZ_OK: + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Failed to clean up bzip2 decompressor"); + return ARCHIVE_FATAL; + } + + zip->end_of_entry = 1; + break; + case BZ_OK: + /* The decompressor has successfully decoded this chunk of + * data, but more data is still in queue. */ + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "bzip2 decompression failed"); + return ARCHIVE_FATAL; + } + + /* Update the pointers so decompressor can continue decoding. */ + to_consume = zip->bzstream.total_in_lo32; + __archive_read_consume(a, to_consume); + + total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) + + zip->bzstream.total_out_lo32; + + zip->entry_bytes_remaining -= to_consume; + zip->entry_compressed_bytes_read += to_consume; + zip->entry_uncompressed_bytes_read += total_out; + + /* Give libarchive its due. */ + *size = total_out; + *buff = zip->uncompressed_buffer; + + /* Seek for optional marker, like in other entries. */ + r = consume_optional_marker(a, zip); + if(r != ARCHIVE_OK) + return r; + + return ARCHIVE_OK; +} + +#endif + #ifdef HAVE_ZLIB_H static int zip_deflate_init(struct archive_read *a, struct zip *zip) { int r; /* If we haven't yet read any data, initialize the decompressor. */ if (!zip->decompress_init) { if (zip->stream_valid) r = inflateReset(&zip->stream); else r = inflateInit2(&zip->stream, -15 /* Don't check for zlib header */); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't initialize ZIP decompression."); return (ARCHIVE_FATAL); } /* Stream structure has been set up. */ zip->stream_valid = 1; /* We've initialized decompression for this stream. */ zip->decompress_init = 1; } return (ARCHIVE_OK); } static int zip_read_data_deflate(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct zip *zip; ssize_t bytes_avail; const void *compressed_buff, *sp; int r; (void)offset; /* UNUSED */ zip = (struct zip *)(a->format->data); /* If the buffer hasn't been allocated, allocate it now. */ if (zip->uncompressed_buffer == NULL) { zip->uncompressed_buffer_size = 256 * 1024; zip->uncompressed_buffer = (unsigned char *)malloc(zip->uncompressed_buffer_size); if (zip->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for ZIP decompression"); return (ARCHIVE_FATAL); } } r = zip_deflate_init(a, zip); if (r != ARCHIVE_OK) return (r); /* * Note: '1' here is a performance optimization. * Recall that the decompression layer returns a count of * available bytes; asking for more than that forces the * decompressor to combine reads by copying data. */ compressed_buff = sp = __archive_read_ahead(a, 1, &bytes_avail); if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && bytes_avail > zip->entry_bytes_remaining) { bytes_avail = (ssize_t)zip->entry_bytes_remaining; } if (bytes_avail < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file body"); return (ARCHIVE_FATAL); } if (zip->tctx_valid || zip->cctx_valid) { if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) { size_t buff_remaining = (zip->decrypted_buffer + zip->decrypted_buffer_size) - (zip->decrypted_ptr + zip->decrypted_bytes_remaining); if (buff_remaining > (size_t)bytes_avail) buff_remaining = (size_t)bytes_avail; if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining > 0) { if ((int64_t)(zip->decrypted_bytes_remaining + buff_remaining) > zip->entry_bytes_remaining) { if (zip->entry_bytes_remaining < (int64_t)zip->decrypted_bytes_remaining) buff_remaining = 0; else buff_remaining = (size_t)zip->entry_bytes_remaining - zip->decrypted_bytes_remaining; } } if (buff_remaining > 0) { if (zip->tctx_valid) { trad_enc_decrypt_update(&zip->tctx, compressed_buff, buff_remaining, zip->decrypted_ptr + zip->decrypted_bytes_remaining, buff_remaining); } else { size_t dsize = buff_remaining; archive_decrypto_aes_ctr_update( &zip->cctx, compressed_buff, buff_remaining, zip->decrypted_ptr + zip->decrypted_bytes_remaining, &dsize); } zip->decrypted_bytes_remaining += buff_remaining; } } bytes_avail = zip->decrypted_bytes_remaining; compressed_buff = (const char *)zip->decrypted_ptr; } /* * A bug in zlib.h: stream.next_in should be marked 'const' * but isn't (the library never alters data through the * next_in pointer, only reads it). The result: this ugly * cast to remove 'const'. */ zip->stream.next_in = (Bytef *)(uintptr_t)(const void *)compressed_buff; zip->stream.avail_in = (uInt)bytes_avail; zip->stream.total_in = 0; zip->stream.next_out = zip->uncompressed_buffer; zip->stream.avail_out = (uInt)zip->uncompressed_buffer_size; zip->stream.total_out = 0; r = inflate(&zip->stream, 0); switch (r) { case Z_OK: break; case Z_STREAM_END: zip->end_of_entry = 1; break; case Z_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Out of memory for ZIP decompression"); return (ARCHIVE_FATAL); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP decompression failed (%d)", r); return (ARCHIVE_FATAL); } /* Consume as much as the compressor actually used. */ bytes_avail = zip->stream.total_in; if (zip->tctx_valid || zip->cctx_valid) { zip->decrypted_bytes_remaining -= bytes_avail; if (zip->decrypted_bytes_remaining == 0) zip->decrypted_ptr = zip->decrypted_buffer; else zip->decrypted_ptr += bytes_avail; } /* Calculate compressed data as much as we used.*/ if (zip->hctx_valid) archive_hmac_sha1_update(&zip->hctx, sp, bytes_avail); __archive_read_consume(a, bytes_avail); zip->entry_bytes_remaining -= bytes_avail; zip->entry_compressed_bytes_read += bytes_avail; *size = zip->stream.total_out; zip->entry_uncompressed_bytes_read += zip->stream.total_out; *buff = zip->uncompressed_buffer; if (zip->end_of_entry && zip->hctx_valid) { r = check_authentication_code(a, NULL); if (r != ARCHIVE_OK) return (r); } - if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { - const char *p; + r = consume_optional_marker(a, zip); + if (r != ARCHIVE_OK) + return (r); - if (NULL == (p = __archive_read_ahead(a, 24, NULL))) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated ZIP end-of-file record"); - return (ARCHIVE_FATAL); - } - /* Consume the optional PK\007\010 marker. */ - if (p[0] == 'P' && p[1] == 'K' && - p[2] == '\007' && p[3] == '\010') { - p += 4; - zip->unconsumed = 4; - } - if (zip->entry->flags & LA_USED_ZIP64) { - uint64_t compressed, uncompressed; - zip->entry->crc32 = archive_le32dec(p); - compressed = archive_le64dec(p + 4); - uncompressed = archive_le64dec(p + 12); - if (compressed > INT64_MAX || uncompressed > INT64_MAX) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Overflow of 64-bit file sizes"); - return ARCHIVE_FAILED; - } - zip->entry->compressed_size = compressed; - zip->entry->uncompressed_size = uncompressed; - zip->unconsumed += 20; - } else { - zip->entry->crc32 = archive_le32dec(p); - zip->entry->compressed_size = archive_le32dec(p + 4); - zip->entry->uncompressed_size = archive_le32dec(p + 8); - zip->unconsumed += 12; - } - } - return (ARCHIVE_OK); } #endif static int read_decryption_header(struct archive_read *a) { struct zip *zip = (struct zip *)(a->format->data); const char *p; unsigned int remaining_size; unsigned int ts; /* * Read an initialization vector data field. */ p = __archive_read_ahead(a, 2, NULL); if (p == NULL) goto truncated; ts = zip->iv_size; zip->iv_size = archive_le16dec(p); __archive_read_consume(a, 2); if (ts < zip->iv_size) { free(zip->iv); zip->iv = NULL; } p = __archive_read_ahead(a, zip->iv_size, NULL); if (p == NULL) goto truncated; if (zip->iv == NULL) { zip->iv = malloc(zip->iv_size); if (zip->iv == NULL) goto nomem; } memcpy(zip->iv, p, zip->iv_size); __archive_read_consume(a, zip->iv_size); /* * Read a size of remaining decryption header field. */ p = __archive_read_ahead(a, 14, NULL); if (p == NULL) goto truncated; remaining_size = archive_le32dec(p); if (remaining_size < 16 || remaining_size > (1 << 18)) goto corrupted; /* Check if format version is supported. */ if (archive_le16dec(p+4) != 3) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported encryption format version: %u", archive_le16dec(p+4)); return (ARCHIVE_FAILED); } /* * Read an encryption algorithm field. */ zip->alg_id = archive_le16dec(p+6); switch (zip->alg_id) { case 0x6601:/* DES */ case 0x6602:/* RC2 */ case 0x6603:/* 3DES 168 */ case 0x6609:/* 3DES 112 */ case 0x660E:/* AES 128 */ case 0x660F:/* AES 192 */ case 0x6610:/* AES 256 */ case 0x6702:/* RC2 (version >= 5.2) */ case 0x6720:/* Blowfish */ case 0x6721:/* Twofish */ case 0x6801:/* RC4 */ /* Supported encryption algorithm. */ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown encryption algorithm: %u", zip->alg_id); return (ARCHIVE_FAILED); } /* * Read a bit length field. */ zip->bit_len = archive_le16dec(p+8); /* * Read a flags field. */ zip->flags = archive_le16dec(p+10); switch (zip->flags & 0xf000) { case 0x0001: /* Password is required to decrypt. */ case 0x0002: /* Certificates only. */ case 0x0003: /* Password or certificate required to decrypt. */ break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown encryption flag: %u", zip->flags); return (ARCHIVE_FAILED); } if ((zip->flags & 0xf000) == 0 || (zip->flags & 0xf000) == 0x4000) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown encryption flag: %u", zip->flags); return (ARCHIVE_FAILED); } /* * Read an encrypted random data field. */ ts = zip->erd_size; zip->erd_size = archive_le16dec(p+12); __archive_read_consume(a, 14); if ((zip->erd_size & 0xf) != 0 || (zip->erd_size + 16) > remaining_size || (zip->erd_size + 16) < zip->erd_size) goto corrupted; if (ts < zip->erd_size) { free(zip->erd); zip->erd = NULL; } p = __archive_read_ahead(a, zip->erd_size, NULL); if (p == NULL) goto truncated; if (zip->erd == NULL) { zip->erd = malloc(zip->erd_size); if (zip->erd == NULL) goto nomem; } memcpy(zip->erd, p, zip->erd_size); __archive_read_consume(a, zip->erd_size); /* * Read a reserved data field. */ p = __archive_read_ahead(a, 4, NULL); if (p == NULL) goto truncated; /* Reserved data size should be zero. */ if (archive_le32dec(p) != 0) goto corrupted; __archive_read_consume(a, 4); /* * Read a password validation data field. */ p = __archive_read_ahead(a, 2, NULL); if (p == NULL) goto truncated; ts = zip->v_size; zip->v_size = archive_le16dec(p); __archive_read_consume(a, 2); if ((zip->v_size & 0x0f) != 0 || (zip->erd_size + zip->v_size + 16) > remaining_size || (zip->erd_size + zip->v_size + 16) < (zip->erd_size + zip->v_size)) goto corrupted; if (ts < zip->v_size) { free(zip->v_data); zip->v_data = NULL; } p = __archive_read_ahead(a, zip->v_size, NULL); if (p == NULL) goto truncated; if (zip->v_data == NULL) { zip->v_data = malloc(zip->v_size); if (zip->v_data == NULL) goto nomem; } memcpy(zip->v_data, p, zip->v_size); __archive_read_consume(a, zip->v_size); p = __archive_read_ahead(a, 4, NULL); if (p == NULL) goto truncated; zip->v_crc32 = archive_le32dec(p); __archive_read_consume(a, 4); /*return (ARCHIVE_OK); * This is not fully implemented yet.*/ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Encrypted file is unsupported"); return (ARCHIVE_FAILED); truncated: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); corrupted: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted ZIP file data"); return (ARCHIVE_FATAL); nomem: archive_set_error(&a->archive, ENOMEM, "No memory for ZIP decryption"); return (ARCHIVE_FATAL); } static int zip_alloc_decryption_buffer(struct archive_read *a) { struct zip *zip = (struct zip *)(a->format->data); size_t bs = 256 * 1024; if (zip->decrypted_buffer == NULL) { zip->decrypted_buffer_size = bs; zip->decrypted_buffer = malloc(bs); if (zip->decrypted_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for ZIP decryption"); return (ARCHIVE_FATAL); } } zip->decrypted_ptr = zip->decrypted_buffer; return (ARCHIVE_OK); } static int init_traditional_PKWARE_decryption(struct archive_read *a) { struct zip *zip = (struct zip *)(a->format->data); const void *p; int retry; int r; if (zip->tctx_valid) return (ARCHIVE_OK); /* Read the 12 bytes encryption header stored at the start of the data area. */ #define ENC_HEADER_SIZE 12 if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining < ENC_HEADER_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated Zip encrypted body: only %jd bytes available", (intmax_t)zip->entry_bytes_remaining); return (ARCHIVE_FATAL); } p = __archive_read_ahead(a, ENC_HEADER_SIZE, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } for (retry = 0;; retry++) { const char *passphrase; uint8_t crcchk; passphrase = __archive_read_next_passphrase(a); if (passphrase == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, (retry > 0)? "Incorrect passphrase": "Passphrase required for this entry"); return (ARCHIVE_FAILED); } /* * Initialize ctx for Traditional PKWARE Decryption. */ r = trad_enc_init(&zip->tctx, passphrase, strlen(passphrase), p, ENC_HEADER_SIZE, &crcchk); if (r == 0 && crcchk == zip->entry->decdat) break;/* The passphrase is OK. */ if (retry > 10000) { /* Avoid infinity loop. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Too many incorrect passphrases"); return (ARCHIVE_FAILED); } } __archive_read_consume(a, ENC_HEADER_SIZE); zip->tctx_valid = 1; if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) { zip->entry_bytes_remaining -= ENC_HEADER_SIZE; } /*zip->entry_uncompressed_bytes_read += ENC_HEADER_SIZE;*/ zip->entry_compressed_bytes_read += ENC_HEADER_SIZE; zip->decrypted_bytes_remaining = 0; return (zip_alloc_decryption_buffer(a)); #undef ENC_HEADER_SIZE } static int init_WinZip_AES_decryption(struct archive_read *a) { struct zip *zip = (struct zip *)(a->format->data); const void *p; const uint8_t *pv; size_t key_len, salt_len; uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE]; int retry; int r; if (zip->cctx_valid || zip->hctx_valid) return (ARCHIVE_OK); switch (zip->entry->aes_extra.strength) { case 1: salt_len = 8; key_len = 16; break; case 2: salt_len = 12; key_len = 24; break; case 3: salt_len = 16; key_len = 32; break; default: goto corrupted; } p = __archive_read_ahead(a, salt_len + 2, NULL); if (p == NULL) goto truncated; for (retry = 0;; retry++) { const char *passphrase; passphrase = __archive_read_next_passphrase(a); if (passphrase == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, (retry > 0)? "Incorrect passphrase": "Passphrase required for this entry"); return (ARCHIVE_FAILED); } memset(derived_key, 0, sizeof(derived_key)); r = archive_pbkdf2_sha1(passphrase, strlen(passphrase), p, salt_len, 1000, derived_key, key_len * 2 + 2); if (r != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Decryption is unsupported due to lack of " "crypto library"); return (ARCHIVE_FAILED); } /* Check password verification value. */ pv = ((const uint8_t *)p) + salt_len; if (derived_key[key_len * 2] == pv[0] && derived_key[key_len * 2 + 1] == pv[1]) break;/* The passphrase is OK. */ if (retry > 10000) { /* Avoid infinity loop. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Too many incorrect passphrases"); return (ARCHIVE_FAILED); } } r = archive_decrypto_aes_ctr_init(&zip->cctx, derived_key, key_len); if (r != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Decryption is unsupported due to lack of crypto library"); return (ARCHIVE_FAILED); } r = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len, key_len); if (r != 0) { archive_decrypto_aes_ctr_release(&zip->cctx); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to initialize HMAC-SHA1"); return (ARCHIVE_FAILED); } zip->cctx_valid = zip->hctx_valid = 1; __archive_read_consume(a, salt_len + 2); zip->entry_bytes_remaining -= salt_len + 2 + AUTH_CODE_SIZE; if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) && zip->entry_bytes_remaining < 0) goto corrupted; zip->entry_compressed_bytes_read += salt_len + 2 + AUTH_CODE_SIZE; zip->decrypted_bytes_remaining = 0; zip->entry->compression = zip->entry->aes_extra.compression; return (zip_alloc_decryption_buffer(a)); truncated: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); corrupted: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted ZIP file data"); return (ARCHIVE_FATAL); } static int archive_read_format_zip_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { int r; struct zip *zip = (struct zip *)(a->format->data); if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) { zip->has_encrypted_entries = 0; } *offset = zip->entry_uncompressed_bytes_read; *size = 0; *buff = NULL; /* If we hit end-of-entry last time, return ARCHIVE_EOF. */ if (zip->end_of_entry) return (ARCHIVE_EOF); /* Return EOF immediately if this is a non-regular file. */ if (AE_IFREG != (zip->entry->mode & AE_IFMT)) return (ARCHIVE_EOF); __archive_read_consume(a, zip->unconsumed); zip->unconsumed = 0; if (zip->init_decryption) { zip->has_encrypted_entries = 1; if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED) r = read_decryption_header(a); else if (zip->entry->compression == WINZIP_AES_ENCRYPTION) r = init_WinZip_AES_decryption(a); else r = init_traditional_PKWARE_decryption(a); if (r != ARCHIVE_OK) return (r); zip->init_decryption = 0; } switch(zip->entry->compression) { case 0: /* No compression. */ r = zip_read_data_none(a, buff, size, offset); break; +#ifdef HAVE_BZLIB_H + case 12: /* ZIPx bzip2 compression. */ + r = zip_read_data_zipx_bzip2(a, buff, size, offset); + break; +#endif +#if HAVE_LZMA_H && HAVE_LIBLZMA + case 14: /* ZIPx LZMA compression. */ + r = zip_read_data_zipx_lzma_alone(a, buff, size, offset); + break; + case 95: /* ZIPx XZ compression. */ + r = zip_read_data_zipx_xz(a, buff, size, offset); + break; +#endif + /* PPMd support is built-in, so we don't need any #if guards. */ + case 98: /* ZIPx PPMd compression. */ + r = zip_read_data_zipx_ppmd(a, buff, size, offset); + break; + #ifdef HAVE_ZLIB_H case 8: /* Deflate compression. */ r = zip_read_data_deflate(a, buff, size, offset); break; #endif default: /* Unsupported compression. */ /* Return a warning. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "Unsupported ZIP compression method (%s)", - compression_name(zip->entry->compression)); + "Unsupported ZIP compression method (%d: %s)", + zip->entry->compression, compression_name(zip->entry->compression)); /* We can't decompress this entry, but we will * be able to skip() it and try the next entry. */ return (ARCHIVE_FAILED); break; } if (r != ARCHIVE_OK) return (r); /* Update checksum */ if (*size) zip->entry_crc32 = zip->crc32func(zip->entry_crc32, *buff, (unsigned)*size); /* If we hit the end, swallow any end-of-data marker. */ if (zip->end_of_entry) { /* Check file size, CRC against these values. */ if (zip->entry->compressed_size != zip->entry_compressed_bytes_read) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP compressed data is wrong size " "(read %jd, expected %jd)", (intmax_t)zip->entry_compressed_bytes_read, (intmax_t)zip->entry->compressed_size); return (ARCHIVE_WARN); } /* Size field only stores the lower 32 bits of the actual * size. */ if ((zip->entry->uncompressed_size & UINT32_MAX) != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP uncompressed data is wrong size " "(read %jd, expected %jd)\n", (intmax_t)zip->entry_uncompressed_bytes_read, (intmax_t)zip->entry->uncompressed_size); return (ARCHIVE_WARN); } /* Check computed CRC against header */ if ((!zip->hctx_valid || zip->entry->aes_extra.vendor != AES_VENDOR_AE_2) && zip->entry->crc32 != zip->entry_crc32 && !zip->ignore_crc32) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP bad CRC: 0x%lx should be 0x%lx", (unsigned long)zip->entry_crc32, (unsigned long)zip->entry->crc32); return (ARCHIVE_WARN); } } return (ARCHIVE_OK); } static int archive_read_format_zip_cleanup(struct archive_read *a) { struct zip *zip; struct zip_entry *zip_entry, *next_zip_entry; zip = (struct zip *)(a->format->data); + #ifdef HAVE_ZLIB_H if (zip->stream_valid) inflateEnd(&zip->stream); - free(zip->uncompressed_buffer); #endif + +#if HAVA_LZMA_H && HAVE_LIBLZMA + if (zip->zipx_lzma_valid) { + lzma_end(&zip->zipx_lzma_stream); + } +#endif + +#ifdef HAVE_BZLIB_H + if (zip->bzstream_valid) { + BZ2_bzDecompressEnd(&zip->bzstream); + } +#endif + + free(zip->uncompressed_buffer); + + if (zip->ppmd8_valid) + __archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8); + if (zip->zip_entries) { zip_entry = zip->zip_entries; while (zip_entry != NULL) { next_zip_entry = zip_entry->next; archive_string_free(&zip_entry->rsrcname); free(zip_entry); zip_entry = next_zip_entry; } } free(zip->decrypted_buffer); if (zip->cctx_valid) archive_decrypto_aes_ctr_release(&zip->cctx); if (zip->hctx_valid) archive_hmac_sha1_cleanup(&zip->hctx); free(zip->iv); free(zip->erd); free(zip->v_data); archive_string_free(&zip->format_name); free(zip); (a->format->data) = NULL; return (ARCHIVE_OK); } static int archive_read_format_zip_has_encrypted_entries(struct archive_read *_a) { if (_a && _a->format) { struct zip * zip = (struct zip *)_a->format->data; if (zip) { return zip->has_encrypted_entries; } } return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; } static int archive_read_format_zip_options(struct archive_read *a, const char *key, const char *val) { struct zip *zip; int ret = ARCHIVE_FAILED; zip = (struct zip *)(a->format->data); if (strcmp(key, "compat-2x") == 0) { /* Handle filenames as libarchive 2.x */ zip->init_default_conversion = (val != NULL) ? 1 : 0; return (ARCHIVE_OK); } else if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "zip: hdrcharset option needs a character-set name" ); else { zip->sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (zip->sconv != NULL) { if (strcmp(val, "UTF-8") == 0) zip->sconv_utf8 = zip->sconv; ret = ARCHIVE_OK; } else ret = ARCHIVE_FATAL; } return (ret); } else if (strcmp(key, "ignorecrc32") == 0) { /* Mostly useful for testing. */ if (val == NULL || val[0] == 0) { zip->crc32func = real_crc32; zip->ignore_crc32 = 0; } else { zip->crc32func = fake_crc32; zip->ignore_crc32 = 1; } return (ARCHIVE_OK); } else if (strcmp(key, "mac-ext") == 0) { zip->process_mac_extensions = (val != NULL && val[0] != 0); return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } int archive_read_support_format_zip(struct archive *a) { int r; r = archive_read_support_format_zip_streamable(a); if (r != ARCHIVE_OK) return r; return (archive_read_support_format_zip_seekable(a)); } /* ------------------------------------------------------------------------ */ /* * Streaming-mode support */ static int archive_read_support_format_zip_capabilities_streamable(struct archive_read * a) { (void)a; /* UNUSED */ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); } static int archive_read_format_zip_streamable_bid(struct archive_read *a, int best_bid) { const char *p; (void)best_bid; /* UNUSED */ if ((p = __archive_read_ahead(a, 4, NULL)) == NULL) return (-1); /* * Bid of 29 here comes from: * + 16 bits for "PK", * + next 16-bit field has 6 options so contributes * about 16 - log_2(6) ~= 16 - 2.6 ~= 13 bits * * So we've effectively verified ~29 total bits of check data. */ if (p[0] == 'P' && p[1] == 'K') { if ((p[2] == '\001' && p[3] == '\002') || (p[2] == '\003' && p[3] == '\004') || (p[2] == '\005' && p[3] == '\006') || (p[2] == '\006' && p[3] == '\006') || (p[2] == '\007' && p[3] == '\010') || (p[2] == '0' && p[3] == '0')) return (29); } /* TODO: It's worth looking ahead a little bit for a valid * PK signature. In particular, that would make it possible * to read some UUEncoded SFX files or SFX files coming from * a network socket. */ return (0); } static int archive_read_format_zip_streamable_read_header(struct archive_read *a, struct archive_entry *entry) { struct zip *zip; a->archive.archive_format = ARCHIVE_FORMAT_ZIP; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "ZIP"; zip = (struct zip *)(a->format->data); /* * It should be sufficient to call archive_read_next_header() for * a reader to determine if an entry is encrypted or not. If the * encryption of an entry is only detectable when calling * archive_read_data(), so be it. We'll do the same check there * as well. */ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) zip->has_encrypted_entries = 0; /* Make sure we have a zip_entry structure to use. */ if (zip->zip_entries == NULL) { zip->zip_entries = malloc(sizeof(struct zip_entry)); if (zip->zip_entries == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return ARCHIVE_FATAL; } } zip->entry = zip->zip_entries; memset(zip->entry, 0, sizeof(struct zip_entry)); if (zip->cctx_valid) archive_decrypto_aes_ctr_release(&zip->cctx); if (zip->hctx_valid) archive_hmac_sha1_cleanup(&zip->hctx); zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0; __archive_read_reset_passphrase(a); /* Search ahead for the next local file header. */ __archive_read_consume(a, zip->unconsumed); zip->unconsumed = 0; for (;;) { int64_t skipped = 0; const char *p, *end; ssize_t bytes; p = __archive_read_ahead(a, 4, &bytes); if (p == NULL) return (ARCHIVE_FATAL); end = p + bytes; while (p + 4 <= end) { if (p[0] == 'P' && p[1] == 'K') { if (p[2] == '\003' && p[3] == '\004') { /* Regular file entry. */ __archive_read_consume(a, skipped); return zip_read_local_file_header(a, entry, zip); } /* * TODO: We cannot restore permissions * based only on the local file headers. * Consider scanning the central * directory and returning additional * entries for at least directories. * This would allow us to properly set * directory permissions. * * This won't help us fix symlinks * and may not help with regular file * permissions, either. */ if (p[2] == '\001' && p[3] == '\002') { return (ARCHIVE_EOF); } /* End of central directory? Must be an * empty archive. */ if ((p[2] == '\005' && p[3] == '\006') || (p[2] == '\006' && p[3] == '\006')) return (ARCHIVE_EOF); } ++p; ++skipped; } __archive_read_consume(a, skipped); } } static int archive_read_format_zip_read_data_skip_streamable(struct archive_read *a) { struct zip *zip; int64_t bytes_skipped; zip = (struct zip *)(a->format->data); bytes_skipped = __archive_read_consume(a, zip->unconsumed); zip->unconsumed = 0; if (bytes_skipped < 0) return (ARCHIVE_FATAL); /* If we've already read to end of data, we're done. */ if (zip->end_of_entry) return (ARCHIVE_OK); /* So we know we're streaming... */ if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) || zip->entry->compressed_size > 0) { /* We know the compressed length, so we can just skip. */ bytes_skipped = __archive_read_consume(a, zip->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); return (ARCHIVE_OK); } if (zip->init_decryption) { int r; zip->has_encrypted_entries = 1; if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED) r = read_decryption_header(a); else if (zip->entry->compression == WINZIP_AES_ENCRYPTION) r = init_WinZip_AES_decryption(a); else r = init_traditional_PKWARE_decryption(a); if (r != ARCHIVE_OK) return (r); zip->init_decryption = 0; } /* We're streaming and we don't know the length. */ /* If the body is compressed and we know the format, we can * find an exact end-of-entry by decompressing it. */ switch (zip->entry->compression) { #ifdef HAVE_ZLIB_H case 8: /* Deflate compression. */ while (!zip->end_of_entry) { int64_t offset = 0; const void *buff = NULL; size_t size = 0; int r; r = zip_read_data_deflate(a, &buff, &size, &offset); if (r != ARCHIVE_OK) return (r); } return ARCHIVE_OK; #endif default: /* Uncompressed or unknown. */ /* Scan for a PK\007\010 signature. */ for (;;) { const char *p, *buff; ssize_t bytes_avail; buff = __archive_read_ahead(a, 16, &bytes_avail); if (bytes_avail < 16) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } p = buff; while (p <= buff + bytes_avail - 16) { if (p[3] == 'P') { p += 3; } else if (p[3] == 'K') { p += 2; } else if (p[3] == '\007') { p += 1; } else if (p[3] == '\010' && p[2] == '\007' && p[1] == 'K' && p[0] == 'P') { if (zip->entry->flags & LA_USED_ZIP64) __archive_read_consume(a, p - buff + 24); else __archive_read_consume(a, p - buff + 16); return ARCHIVE_OK; } else { p += 4; } } __archive_read_consume(a, p - buff); } } } int archive_read_support_format_zip_streamable(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct zip *zip; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_zip"); zip = (struct zip *)calloc(1, sizeof(*zip)); if (zip == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data"); return (ARCHIVE_FATAL); } /* Streamable reader doesn't support mac extensions. */ zip->process_mac_extensions = 0; /* * Until enough data has been read, we cannot tell about * any encrypted entries yet. */ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; zip->crc32func = real_crc32; r = __archive_read_register_format(a, zip, "zip", archive_read_format_zip_streamable_bid, archive_read_format_zip_options, archive_read_format_zip_streamable_read_header, archive_read_format_zip_read_data, archive_read_format_zip_read_data_skip_streamable, NULL, archive_read_format_zip_cleanup, archive_read_support_format_zip_capabilities_streamable, archive_read_format_zip_has_encrypted_entries); if (r != ARCHIVE_OK) free(zip); return (ARCHIVE_OK); } /* ------------------------------------------------------------------------ */ /* * Seeking-mode support */ static int archive_read_support_format_zip_capabilities_seekable(struct archive_read * a) { (void)a; /* UNUSED */ return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA); } /* * TODO: This is a performance sink because it forces the read core to * drop buffered data from the start of file, which will then have to * be re-read again if this bidder loses. * * We workaround this a little by passing in the best bid so far so * that later bidders can do nothing if they know they'll never * outbid. But we can certainly do better... */ static int read_eocd(struct zip *zip, const char *p, int64_t current_offset) { /* Sanity-check the EOCD we've found. */ /* This must be the first volume. */ if (archive_le16dec(p + 4) != 0) return 0; /* Central directory must be on this volume. */ if (archive_le16dec(p + 4) != archive_le16dec(p + 6)) return 0; /* All central directory entries must be on this volume. */ if (archive_le16dec(p + 10) != archive_le16dec(p + 8)) return 0; /* Central directory can't extend beyond start of EOCD record. */ if (archive_le32dec(p + 16) + archive_le32dec(p + 12) > current_offset) return 0; /* Save the central directory location for later use. */ zip->central_directory_offset = archive_le32dec(p + 16); /* This is just a tiny bit higher than the maximum returned by the streaming Zip bidder. This ensures that the more accurate seeking Zip parser wins whenever seek is available. */ return 32; } /* * Examine Zip64 EOCD locator: If it's valid, store the information * from it. */ static int read_zip64_eocd(struct archive_read *a, struct zip *zip, const char *p) { int64_t eocd64_offset; int64_t eocd64_size; /* Sanity-check the locator record. */ /* Central dir must be on first volume. */ if (archive_le32dec(p + 4) != 0) return 0; /* Must be only a single volume. */ if (archive_le32dec(p + 16) != 1) return 0; /* Find the Zip64 EOCD record. */ eocd64_offset = archive_le64dec(p + 8); if (__archive_read_seek(a, eocd64_offset, SEEK_SET) < 0) return 0; if ((p = __archive_read_ahead(a, 56, NULL)) == NULL) return 0; /* Make sure we can read all of it. */ eocd64_size = archive_le64dec(p + 4) + 12; if (eocd64_size < 56 || eocd64_size > 16384) return 0; if ((p = __archive_read_ahead(a, (size_t)eocd64_size, NULL)) == NULL) return 0; /* Sanity-check the EOCD64 */ if (archive_le32dec(p + 16) != 0) /* Must be disk #0 */ return 0; if (archive_le32dec(p + 20) != 0) /* CD must be on disk #0 */ return 0; /* CD can't be split. */ if (archive_le64dec(p + 24) != archive_le64dec(p + 32)) return 0; /* Save the central directory offset for later use. */ zip->central_directory_offset = archive_le64dec(p + 48); return 32; } static int archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid) { struct zip *zip = (struct zip *)a->format->data; int64_t file_size, current_offset; const char *p; int i, tail; /* If someone has already bid more than 32, then avoid trashing the look-ahead buffers with a seek. */ if (best_bid > 32) return (-1); file_size = __archive_read_seek(a, 0, SEEK_END); if (file_size <= 0) return 0; /* Search last 16k of file for end-of-central-directory * record (which starts with PK\005\006) */ tail = (int)zipmin(1024 * 16, file_size); current_offset = __archive_read_seek(a, -tail, SEEK_END); if (current_offset < 0) return 0; if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL) return 0; /* Boyer-Moore search backwards from the end, since we want * to match the last EOCD in the file (there can be more than * one if there is an uncompressed Zip archive as a member * within this Zip archive). */ for (i = tail - 22; i > 0;) { switch (p[i]) { case 'P': if (memcmp(p + i, "PK\005\006", 4) == 0) { int ret = read_eocd(zip, p + i, current_offset + i); /* Zip64 EOCD locator precedes * regular EOCD if present. */ if (i >= 20 && memcmp(p + i - 20, "PK\006\007", 4) == 0) { int ret_zip64 = read_zip64_eocd(a, zip, p + i - 20); if (ret_zip64 > ret) ret = ret_zip64; } return (ret); } i -= 4; break; case 'K': i -= 1; break; case 005: i -= 2; break; case 006: i -= 3; break; default: i -= 4; break; } } return 0; } /* The red-black trees are only used in seeking mode to manage * the in-memory copy of the central directory. */ static int cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct zip_entry *e1 = (const struct zip_entry *)n1; const struct zip_entry *e2 = (const struct zip_entry *)n2; if (e1->local_header_offset > e2->local_header_offset) return -1; if (e1->local_header_offset < e2->local_header_offset) return 1; return 0; } static int cmp_key(const struct archive_rb_node *n, const void *key) { /* This function won't be called */ (void)n; /* UNUSED */ (void)key; /* UNUSED */ return 1; } static const struct archive_rb_tree_ops rb_ops = { &cmp_node, &cmp_key }; static int rsrc_cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2) { const struct zip_entry *e1 = (const struct zip_entry *)n1; const struct zip_entry *e2 = (const struct zip_entry *)n2; return (strcmp(e2->rsrcname.s, e1->rsrcname.s)); } static int rsrc_cmp_key(const struct archive_rb_node *n, const void *key) { const struct zip_entry *e = (const struct zip_entry *)n; return (strcmp((const char *)key, e->rsrcname.s)); } static const struct archive_rb_tree_ops rb_rsrc_ops = { &rsrc_cmp_node, &rsrc_cmp_key }; static const char * rsrc_basename(const char *name, size_t name_length) { const char *s, *r; r = s = name; for (;;) { s = memchr(s, '/', name_length - (s - name)); if (s == NULL) break; r = ++s; } return (r); } static void expose_parent_dirs(struct zip *zip, const char *name, size_t name_length) { struct archive_string str; struct zip_entry *dir; char *s; archive_string_init(&str); archive_strncpy(&str, name, name_length); for (;;) { s = strrchr(str.s, '/'); if (s == NULL) break; *s = '\0'; /* Transfer the parent directory from zip->tree_rsrc RB * tree to zip->tree RB tree to expose. */ dir = (struct zip_entry *) __archive_rb_tree_find_node(&zip->tree_rsrc, str.s); if (dir == NULL) break; __archive_rb_tree_remove_node(&zip->tree_rsrc, &dir->node); archive_string_free(&dir->rsrcname); __archive_rb_tree_insert_node(&zip->tree, &dir->node); } archive_string_free(&str); } static int slurp_central_directory(struct archive_read *a, struct zip *zip) { ssize_t i; unsigned found; int64_t correction; ssize_t bytes_avail; const char *p; /* * Find the start of the central directory. The end-of-CD * record has our starting point, but there are lots of * Zip archives which have had other data prepended to the * file, which makes the recorded offsets all too small. * So we search forward from the specified offset until we * find the real start of the central directory. Then we * know the correction we need to apply to account for leading * padding. */ if (__archive_read_seek(a, zip->central_directory_offset, SEEK_SET) < 0) return ARCHIVE_FATAL; found = 0; while (!found) { if ((p = __archive_read_ahead(a, 20, &bytes_avail)) == NULL) return ARCHIVE_FATAL; for (found = 0, i = 0; !found && i < bytes_avail - 4;) { switch (p[i + 3]) { case 'P': i += 3; break; case 'K': i += 2; break; case 001: i += 1; break; case 002: if (memcmp(p + i, "PK\001\002", 4) == 0) { p += i; found = 1; } else i += 4; break; case 005: i += 1; break; case 006: if (memcmp(p + i, "PK\005\006", 4) == 0) { p += i; found = 1; } else if (memcmp(p + i, "PK\006\006", 4) == 0) { p += i; found = 1; } else i += 1; break; default: i += 4; break; } } __archive_read_consume(a, i); } correction = archive_filter_bytes(&a->archive, 0) - zip->central_directory_offset; __archive_rb_tree_init(&zip->tree, &rb_ops); __archive_rb_tree_init(&zip->tree_rsrc, &rb_rsrc_ops); zip->central_directory_entries_total = 0; while (1) { struct zip_entry *zip_entry; size_t filename_length, extra_length, comment_length; uint32_t external_attributes; const char *name, *r; if ((p = __archive_read_ahead(a, 4, NULL)) == NULL) return ARCHIVE_FATAL; if (memcmp(p, "PK\006\006", 4) == 0 || memcmp(p, "PK\005\006", 4) == 0) { break; } else if (memcmp(p, "PK\001\002", 4) != 0) { archive_set_error(&a->archive, -1, "Invalid central directory signature"); return ARCHIVE_FATAL; } if ((p = __archive_read_ahead(a, 46, NULL)) == NULL) return ARCHIVE_FATAL; zip_entry = calloc(1, sizeof(struct zip_entry)); if (zip_entry == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip entry"); return ARCHIVE_FATAL; } zip_entry->next = zip->zip_entries; zip_entry->flags |= LA_FROM_CENTRAL_DIRECTORY; zip->zip_entries = zip_entry; zip->central_directory_entries_total++; /* version = p[4]; */ zip_entry->system = p[5]; /* version_required = archive_le16dec(p + 6); */ zip_entry->zip_flags = archive_le16dec(p + 8); if (zip_entry->zip_flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)){ zip->has_encrypted_entries = 1; } zip_entry->compression = (char)archive_le16dec(p + 10); zip_entry->mtime = zip_time(p + 12); zip_entry->crc32 = archive_le32dec(p + 16); if (zip_entry->zip_flags & ZIP_LENGTH_AT_END) zip_entry->decdat = p[13]; else zip_entry->decdat = p[19]; zip_entry->compressed_size = archive_le32dec(p + 20); zip_entry->uncompressed_size = archive_le32dec(p + 24); filename_length = archive_le16dec(p + 28); extra_length = archive_le16dec(p + 30); comment_length = archive_le16dec(p + 32); /* disk_start = archive_le16dec(p + 34); */ /* Better be zero. */ /* internal_attributes = archive_le16dec(p + 36); */ /* text bit */ external_attributes = archive_le32dec(p + 38); zip_entry->local_header_offset = archive_le32dec(p + 42) + correction; /* If we can't guess the mode, leave it zero here; when we read the local file header we might get more information. */ if (zip_entry->system == 3) { zip_entry->mode = external_attributes >> 16; } else if (zip_entry->system == 0) { // Interpret MSDOS directory bit if (0x10 == (external_attributes & 0x10)) { zip_entry->mode = AE_IFDIR | 0775; } else { zip_entry->mode = AE_IFREG | 0664; } if (0x01 == (external_attributes & 0x01)) { // Read-only bit; strip write permissions zip_entry->mode &= 0555; } } else { zip_entry->mode = 0; } /* We're done with the regular data; get the filename and * extra data. */ __archive_read_consume(a, 46); p = __archive_read_ahead(a, filename_length + extra_length, NULL); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return ARCHIVE_FATAL; } if (ARCHIVE_OK != process_extra(a, p + filename_length, extra_length, zip_entry)) { return ARCHIVE_FATAL; } /* * Mac resource fork files are stored under the * "__MACOSX/" directory, so we should check if * it is. */ if (!zip->process_mac_extensions) { /* Treat every entry as a regular entry. */ __archive_rb_tree_insert_node(&zip->tree, &zip_entry->node); } else { name = p; r = rsrc_basename(name, filename_length); if (filename_length >= 9 && strncmp("__MACOSX/", name, 9) == 0) { /* If this file is not a resource fork nor * a directory. We should treat it as a non * resource fork file to expose it. */ if (name[filename_length-1] != '/' && (r - name < 3 || r[0] != '.' || r[1] != '_')) { __archive_rb_tree_insert_node( &zip->tree, &zip_entry->node); /* Expose its parent directories. */ expose_parent_dirs(zip, name, filename_length); } else { /* This file is a resource fork file or * a directory. */ archive_strncpy(&(zip_entry->rsrcname), name, filename_length); __archive_rb_tree_insert_node( &zip->tree_rsrc, &zip_entry->node); } } else { /* Generate resource fork name to find its * resource file at zip->tree_rsrc. */ archive_strcpy(&(zip_entry->rsrcname), "__MACOSX/"); archive_strncat(&(zip_entry->rsrcname), name, r - name); archive_strcat(&(zip_entry->rsrcname), "._"); archive_strncat(&(zip_entry->rsrcname), name + (r - name), filename_length - (r - name)); /* Register an entry to RB tree to sort it by * file offset. */ __archive_rb_tree_insert_node(&zip->tree, &zip_entry->node); } } /* Skip the comment too ... */ __archive_read_consume(a, filename_length + extra_length + comment_length); } return ARCHIVE_OK; } static ssize_t zip_get_local_file_header_size(struct archive_read *a, size_t extra) { const char *p; ssize_t filename_length, extra_length; if ((p = __archive_read_ahead(a, extra + 30, NULL)) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); return (ARCHIVE_WARN); } p += extra; if (memcmp(p, "PK\003\004", 4) != 0) { archive_set_error(&a->archive, -1, "Damaged Zip archive"); return ARCHIVE_WARN; } filename_length = archive_le16dec(p + 26); extra_length = archive_le16dec(p + 28); return (30 + filename_length + extra_length); } static int zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry, struct zip_entry *rsrc) { struct zip *zip = (struct zip *)a->format->data; unsigned char *metadata, *mp; int64_t offset = archive_filter_bytes(&a->archive, 0); size_t remaining_bytes, metadata_bytes; ssize_t hsize; int ret = ARCHIVE_OK, eof; switch(rsrc->compression) { case 0: /* No compression. */ if (rsrc->uncompressed_size != rsrc->compressed_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed OS X metadata entry: inconsistent size"); return (ARCHIVE_FATAL); } #ifdef HAVE_ZLIB_H case 8: /* Deflate compression. */ #endif break; default: /* Unsupported compression. */ /* Return a warning. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported ZIP compression method (%s)", compression_name(rsrc->compression)); /* We can't decompress this entry, but we will * be able to skip() it and try the next entry. */ return (ARCHIVE_WARN); } if (rsrc->uncompressed_size > (4 * 1024 * 1024)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Mac metadata is too large: %jd > 4M bytes", (intmax_t)rsrc->uncompressed_size); return (ARCHIVE_WARN); } if (rsrc->compressed_size > (4 * 1024 * 1024)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Mac metadata is too large: %jd > 4M bytes", (intmax_t)rsrc->compressed_size); return (ARCHIVE_WARN); } metadata = malloc((size_t)rsrc->uncompressed_size); if (metadata == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Mac metadata"); return (ARCHIVE_FATAL); } if (offset < rsrc->local_header_offset) __archive_read_consume(a, rsrc->local_header_offset - offset); else if (offset != rsrc->local_header_offset) { __archive_read_seek(a, rsrc->local_header_offset, SEEK_SET); } hsize = zip_get_local_file_header_size(a, 0); __archive_read_consume(a, hsize); remaining_bytes = (size_t)rsrc->compressed_size; metadata_bytes = (size_t)rsrc->uncompressed_size; mp = metadata; eof = 0; while (!eof && remaining_bytes) { const unsigned char *p; ssize_t bytes_avail; size_t bytes_used; p = __archive_read_ahead(a, 1, &bytes_avail); if (p == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file header"); ret = ARCHIVE_WARN; goto exit_mac_metadata; } if ((size_t)bytes_avail > remaining_bytes) bytes_avail = remaining_bytes; switch(rsrc->compression) { case 0: /* No compression. */ if ((size_t)bytes_avail > metadata_bytes) bytes_avail = metadata_bytes; memcpy(mp, p, bytes_avail); bytes_used = (size_t)bytes_avail; metadata_bytes -= bytes_used; mp += bytes_used; if (metadata_bytes == 0) eof = 1; break; #ifdef HAVE_ZLIB_H case 8: /* Deflate compression. */ { int r; ret = zip_deflate_init(a, zip); if (ret != ARCHIVE_OK) goto exit_mac_metadata; zip->stream.next_in = (Bytef *)(uintptr_t)(const void *)p; zip->stream.avail_in = (uInt)bytes_avail; zip->stream.total_in = 0; zip->stream.next_out = mp; zip->stream.avail_out = (uInt)metadata_bytes; zip->stream.total_out = 0; r = inflate(&zip->stream, 0); switch (r) { case Z_OK: break; case Z_STREAM_END: eof = 1; break; case Z_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Out of memory for ZIP decompression"); ret = ARCHIVE_FATAL; goto exit_mac_metadata; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "ZIP decompression failed (%d)", r); ret = ARCHIVE_FATAL; goto exit_mac_metadata; } bytes_used = zip->stream.total_in; metadata_bytes -= zip->stream.total_out; mp += zip->stream.total_out; break; } #endif default: bytes_used = 0; break; } __archive_read_consume(a, bytes_used); remaining_bytes -= bytes_used; } archive_entry_copy_mac_metadata(entry, metadata, (size_t)rsrc->uncompressed_size - metadata_bytes); exit_mac_metadata: __archive_read_seek(a, offset, SEEK_SET); zip->decompress_init = 0; free(metadata); return (ret); } static int archive_read_format_zip_seekable_read_header(struct archive_read *a, struct archive_entry *entry) { struct zip *zip = (struct zip *)a->format->data; struct zip_entry *rsrc; int64_t offset; int r, ret = ARCHIVE_OK; /* * It should be sufficient to call archive_read_next_header() for * a reader to determine if an entry is encrypted or not. If the * encryption of an entry is only detectable when calling * archive_read_data(), so be it. We'll do the same check there * as well. */ if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) zip->has_encrypted_entries = 0; a->archive.archive_format = ARCHIVE_FORMAT_ZIP; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "ZIP"; if (zip->zip_entries == NULL) { r = slurp_central_directory(a, zip); if (r != ARCHIVE_OK) return r; /* Get first entry whose local header offset is lower than * other entries in the archive file. */ zip->entry = (struct zip_entry *)ARCHIVE_RB_TREE_MIN(&zip->tree); } else if (zip->entry != NULL) { /* Get next entry in local header offset order. */ zip->entry = (struct zip_entry *)__archive_rb_tree_iterate( &zip->tree, &zip->entry->node, ARCHIVE_RB_DIR_RIGHT); } if (zip->entry == NULL) return ARCHIVE_EOF; if (zip->entry->rsrcname.s) rsrc = (struct zip_entry *)__archive_rb_tree_find_node( &zip->tree_rsrc, zip->entry->rsrcname.s); else rsrc = NULL; if (zip->cctx_valid) archive_decrypto_aes_ctr_release(&zip->cctx); if (zip->hctx_valid) archive_hmac_sha1_cleanup(&zip->hctx); zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0; __archive_read_reset_passphrase(a); /* File entries are sorted by the header offset, we should mostly * use __archive_read_consume to advance a read point to avoid redundant * data reading. */ offset = archive_filter_bytes(&a->archive, 0); if (offset < zip->entry->local_header_offset) __archive_read_consume(a, zip->entry->local_header_offset - offset); else if (offset != zip->entry->local_header_offset) { __archive_read_seek(a, zip->entry->local_header_offset, SEEK_SET); } zip->unconsumed = 0; r = zip_read_local_file_header(a, entry, zip); if (r != ARCHIVE_OK) return r; if (rsrc) { int ret2 = zip_read_mac_metadata(a, entry, rsrc); if (ret2 < ret) ret = ret2; } return (ret); } /* * We're going to seek for the next header anyway, so we don't * need to bother doing anything here. */ static int archive_read_format_zip_read_data_skip_seekable(struct archive_read *a) { struct zip *zip; zip = (struct zip *)(a->format->data); zip->unconsumed = 0; return (ARCHIVE_OK); } int archive_read_support_format_zip_seekable(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct zip *zip; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_zip_seekable"); zip = (struct zip *)calloc(1, sizeof(*zip)); if (zip == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data"); return (ARCHIVE_FATAL); } #ifdef HAVE_COPYFILE_H /* Set this by default on Mac OS. */ zip->process_mac_extensions = 1; #endif /* * Until enough data has been read, we cannot tell about * any encrypted entries yet. */ zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW; zip->crc32func = real_crc32; r = __archive_read_register_format(a, zip, "zip", archive_read_format_zip_seekable_bid, archive_read_format_zip_options, archive_read_format_zip_seekable_read_header, archive_read_format_zip_read_data, archive_read_format_zip_read_data_skip_seekable, NULL, archive_read_format_zip_cleanup, archive_read_support_format_zip_capabilities_seekable, archive_read_format_zip_has_encrypted_entries); if (r != ARCHIVE_OK) free(zip); return (ARCHIVE_OK); } + +/*# vim:set noet:*/ Index: stable/10/contrib/libarchive/libarchive/archive_write_disk_posix.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_disk_posix.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_write_disk_posix.c (revision 344674) @@ -1,4299 +1,4296 @@ /*- * Copyright (c) 2003-2010 Tim Kientzle * Copyright (c) 2012 Michihiro NAKAJIMA * 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 * in this position and unchanged. * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #if !defined(_WIN32) || defined(__CYGWIN__) #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_ACL_H #include #endif #ifdef HAVE_SYS_EXTATTR_H #include #endif #if HAVE_SYS_XATTR_H #include #elif HAVE_ATTR_XATTR_H #include #endif #ifdef HAVE_SYS_EA_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_SYS_UTIME_H #include #endif #ifdef HAVE_COPYFILE_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_LANGINFO_H #include #endif #ifdef HAVE_LINUX_FS_H #include /* for Linux file flags */ #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #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 #ifdef HAVE_UTIME_H #include #endif #ifdef F_GETTIMES /* Tru64 specific */ #include #endif /* * Macro to cast st_mtime and time_t to an int64 so that 2 numbers can reliably be compared. * * It assumes that the input is an integer type of no more than 64 bits. * If the number is less than zero, t must be a signed type, so it fits in * int64_t. Otherwise, it's a nonnegative value so we can cast it to uint64_t * without loss. But it could be a large unsigned value, so we have to clip it * to INT64_MAX.* */ #define to_int64_time(t) \ ((t) < 0 ? (int64_t)(t) : (uint64_t)(t) > (uint64_t)INT64_MAX ? INT64_MAX : (int64_t)(t)) #if __APPLE__ #include #if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && HAVE_QUARANTINE_H #include #define HAVE_QUARANTINE 1 #endif #endif #ifdef HAVE_ZLIB_H #include #endif /* TODO: Support Mac OS 'quarantine' feature. This is really just a * standard tag to mark files that have been downloaded as "tainted". * On Mac OS, we should mark the extracted files as tainted if the * archive being read was tainted. Windows has a similar feature; we * should investigate ways to support this generically. */ #include "archive.h" #include "archive_acl_private.h" #include "archive_string.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_write_disk_private.h" #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif /* Ignore non-int O_NOFOLLOW constant. */ /* gnulib's fcntl.h does this on AIX, but it seems practical everywhere */ #if defined O_NOFOLLOW && !(INT_MIN <= O_NOFOLLOW && O_NOFOLLOW <= INT_MAX) #undef O_NOFOLLOW #endif #ifndef O_NOFOLLOW #define O_NOFOLLOW 0 #endif struct fixup_entry { struct fixup_entry *next; struct archive_acl acl; mode_t mode; int64_t atime; int64_t birthtime; int64_t mtime; int64_t ctime; unsigned long atime_nanos; unsigned long birthtime_nanos; unsigned long mtime_nanos; unsigned long ctime_nanos; unsigned long fflags_set; size_t mac_metadata_size; void *mac_metadata; int fixup; /* bitmask of what needs fixing */ char *name; }; /* * We use a bitmask to track which operations remain to be done for * this file. In particular, this helps us avoid unnecessary * operations when it's possible to take care of one step as a * side-effect of another. For example, mkdir() can specify the mode * for the newly-created object but symlink() cannot. This means we * can skip chmod() if mkdir() succeeded, but we must explicitly * chmod() if we're trying to create a directory that already exists * (mkdir() failed) or if we're restoring a symlink. Similarly, we * need to verify UID/GID before trying to restore SUID/SGID bits; * that verification can occur explicitly through a stat() call or * implicitly because of a successful chown() call. */ #define TODO_MODE_FORCE 0x40000000 #define TODO_MODE_BASE 0x20000000 #define TODO_SUID 0x10000000 #define TODO_SUID_CHECK 0x08000000 #define TODO_SGID 0x04000000 #define TODO_SGID_CHECK 0x02000000 #define TODO_APPLEDOUBLE 0x01000000 #define TODO_MODE (TODO_MODE_BASE|TODO_SUID|TODO_SGID) #define TODO_TIMES ARCHIVE_EXTRACT_TIME #define TODO_OWNER ARCHIVE_EXTRACT_OWNER #define TODO_FFLAGS ARCHIVE_EXTRACT_FFLAGS #define TODO_ACLS ARCHIVE_EXTRACT_ACL #define TODO_XATTR ARCHIVE_EXTRACT_XATTR #define TODO_MAC_METADATA ARCHIVE_EXTRACT_MAC_METADATA #define TODO_HFS_COMPRESSION ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED struct archive_write_disk { struct archive archive; mode_t user_umask; struct fixup_entry *fixup_list; struct fixup_entry *current_fixup; int64_t user_uid; int skip_file_set; int64_t skip_file_dev; int64_t skip_file_ino; time_t start_time; int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid); void (*cleanup_gid)(void *private); void *lookup_gid_data; int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid); void (*cleanup_uid)(void *private); void *lookup_uid_data; /* * Full path of last file to satisfy symlink checks. */ struct archive_string path_safe; /* * Cached stat data from disk for the current entry. * If this is valid, pst points to st. Otherwise, * pst is null. */ struct stat st; struct stat *pst; /* Information about the object being restored right now. */ struct archive_entry *entry; /* Entry being extracted. */ char *name; /* Name of entry, possibly edited. */ struct archive_string _name_data; /* backing store for 'name' */ /* Tasks remaining for this object. */ int todo; /* Tasks deferred until end-of-archive. */ int deferred; /* Options requested by the client. */ int flags; /* Handle for the file we're restoring. */ int fd; /* Current offset for writing data to the file. */ int64_t offset; /* Last offset actually written to disk. */ int64_t fd_offset; /* Total bytes actually written to files. */ int64_t total_bytes_written; /* Maximum size of file, -1 if unknown. */ int64_t filesize; /* Dir we were in before this restore; only for deep paths. */ int restore_pwd; /* Mode we should use for this entry; affected by _PERM and umask. */ mode_t mode; /* UID/GID to use in restoring this entry. */ int64_t uid; int64_t gid; /* * HFS+ Compression. */ /* Xattr "com.apple.decmpfs". */ uint32_t decmpfs_attr_size; unsigned char *decmpfs_header_p; /* ResourceFork set options used for fsetxattr. */ int rsrc_xattr_options; /* Xattr "com.apple.ResourceFork". */ unsigned char *resource_fork; size_t resource_fork_allocated_size; unsigned int decmpfs_block_count; uint32_t *decmpfs_block_info; /* Buffer for compressed data. */ unsigned char *compressed_buffer; size_t compressed_buffer_size; size_t compressed_buffer_remaining; /* The offset of the ResourceFork where compressed data will * be placed. */ uint32_t compressed_rsrc_position; uint32_t compressed_rsrc_position_v; /* Buffer for uncompressed data. */ char *uncompressed_buffer; size_t block_remaining_bytes; size_t file_remaining_bytes; #ifdef HAVE_ZLIB_H z_stream stream; int stream_valid; int decmpfs_compression_level; #endif }; /* * Default mode for dirs created automatically (will be modified by umask). * Note that POSIX specifies 0777 for implicitly-created dirs, "modified * by the process' file creation mask." */ #define DEFAULT_DIR_MODE 0777 /* * Dir modes are restored in two steps: During the extraction, the permissions * in the archive are modified to match the following limits. During * the post-extract fixup pass, the permissions from the archive are * applied. */ #define MINIMUM_DIR_MODE 0700 #define MAXIMUM_DIR_MODE 0775 /* * Maximum uncompressed size of a decmpfs block. */ #define MAX_DECMPFS_BLOCK_SIZE (64 * 1024) /* * HFS+ compression type. */ #define CMP_XATTR 3/* Compressed data in xattr. */ #define CMP_RESOURCE_FORK 4/* Compressed data in resource fork. */ /* * HFS+ compression resource fork. */ #define RSRC_H_SIZE 260 /* Base size of Resource fork header. */ #define RSRC_F_SIZE 50 /* Size of Resource fork footer. */ /* Size to write compressed data to resource fork. */ #define COMPRESSED_W_SIZE (64 * 1024) /* decmpfs definitions. */ #define MAX_DECMPFS_XATTR_SIZE 3802 #ifndef DECMPFS_XATTR_NAME #define DECMPFS_XATTR_NAME "com.apple.decmpfs" #endif #define DECMPFS_MAGIC 0x636d7066 #define DECMPFS_COMPRESSION_MAGIC 0 #define DECMPFS_COMPRESSION_TYPE 4 #define DECMPFS_UNCOMPRESSED_SIZE 8 #define DECMPFS_HEADER_SIZE 16 #define HFS_BLOCKS(s) ((s) >> 12) static void fsobj_error(int *, struct archive_string *, int, const char *, const char *); static int check_symlinks_fsobj(char *, int *, struct archive_string *, int); static int check_symlinks(struct archive_write_disk *); static int create_filesystem_object(struct archive_write_disk *); static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname); #if defined(HAVE_FCHDIR) && defined(PATH_MAX) static void edit_deep_directories(struct archive_write_disk *ad); #endif static int cleanup_pathname_fsobj(char *, int *, struct archive_string *, int); static int cleanup_pathname(struct archive_write_disk *); static int create_dir(struct archive_write_disk *, char *); static int create_parent_dir(struct archive_write_disk *, char *); static ssize_t hfs_write_data_block(struct archive_write_disk *, const char *, size_t); static int fixup_appledouble(struct archive_write_disk *, const char *); static int older(struct stat *, struct archive_entry *); static int restore_entry(struct archive_write_disk *); static int set_mac_metadata(struct archive_write_disk *, const char *, const void *, size_t); static int set_xattrs(struct archive_write_disk *); static int clear_nochange_fflags(struct archive_write_disk *); static int set_fflags(struct archive_write_disk *); static int set_fflags_platform(struct archive_write_disk *, int fd, const char *name, mode_t mode, unsigned long fflags_set, unsigned long fflags_clear); static int set_ownership(struct archive_write_disk *); static int set_mode(struct archive_write_disk *, int mode); static int set_time(int, int, const char *, time_t, long, time_t, long); static int set_times(struct archive_write_disk *, int, int, const char *, time_t, long, time_t, long, time_t, long, time_t, long); static int set_times_from_entry(struct archive_write_disk *); static struct fixup_entry *sort_dir_list(struct fixup_entry *p); static ssize_t write_data_block(struct archive_write_disk *, const char *, size_t); static struct archive_vtable *archive_write_disk_vtable(void); static int _archive_write_disk_close(struct archive *); static int _archive_write_disk_free(struct archive *); static int _archive_write_disk_header(struct archive *, struct archive_entry *); static int64_t _archive_write_disk_filter_bytes(struct archive *, int); static int _archive_write_disk_finish_entry(struct archive *); static ssize_t _archive_write_disk_data(struct archive *, const void *, size_t); static ssize_t _archive_write_disk_data_block(struct archive *, const void *, size_t, int64_t); static int lazy_stat(struct archive_write_disk *a) { if (a->pst != NULL) { /* Already have stat() data available. */ return (ARCHIVE_OK); } #ifdef HAVE_FSTAT if (a->fd >= 0 && fstat(a->fd, &a->st) == 0) { a->pst = &a->st; return (ARCHIVE_OK); } #endif /* * XXX At this point, symlinks should not be hit, otherwise * XXX a race occurred. Do we want to check explicitly for that? */ if (lstat(a->name, &a->st) == 0) { a->pst = &a->st; return (ARCHIVE_OK); } archive_set_error(&a->archive, errno, "Couldn't stat file"); return (ARCHIVE_WARN); } static struct archive_vtable * archive_write_disk_vtable(void) { static struct archive_vtable av; static int inited = 0; if (!inited) { av.archive_close = _archive_write_disk_close; av.archive_filter_bytes = _archive_write_disk_filter_bytes; av.archive_free = _archive_write_disk_free; av.archive_write_header = _archive_write_disk_header; av.archive_write_finish_entry = _archive_write_disk_finish_entry; av.archive_write_data = _archive_write_disk_data; av.archive_write_data_block = _archive_write_disk_data_block; inited = 1; } return (&av); } static int64_t _archive_write_disk_filter_bytes(struct archive *_a, int n) { struct archive_write_disk *a = (struct archive_write_disk *)_a; (void)n; /* UNUSED */ if (n == -1 || n == 0) return (a->total_bytes_written); return (-1); } int archive_write_disk_set_options(struct archive *_a, int flags) { struct archive_write_disk *a = (struct archive_write_disk *)_a; a->flags = flags; return (ARCHIVE_OK); } /* * Extract this entry to disk. * * TODO: Validate hardlinks. According to the standards, we're * supposed to check each extracted hardlink and squawk if it refers * to a file that we didn't restore. I'm not entirely convinced this * is a good idea, but more importantly: Is there any way to validate * hardlinks without keeping a complete list of filenames from the * entire archive?? Ugh. * */ static int _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) { struct archive_write_disk *a = (struct archive_write_disk *)_a; struct fixup_entry *fe; int ret, r; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_disk_header"); archive_clear_error(&a->archive); if (a->archive.state & ARCHIVE_STATE_DATA) { r = _archive_write_disk_finish_entry(&a->archive); if (r == ARCHIVE_FATAL) return (r); } /* Set up for this particular entry. */ a->pst = NULL; a->current_fixup = NULL; a->deferred = 0; if (a->entry) { archive_entry_free(a->entry); a->entry = NULL; } a->entry = archive_entry_clone(entry); a->fd = -1; a->fd_offset = 0; a->offset = 0; a->restore_pwd = -1; a->uid = a->user_uid; a->mode = archive_entry_mode(a->entry); if (archive_entry_size_is_set(a->entry)) a->filesize = archive_entry_size(a->entry); else a->filesize = -1; archive_strcpy(&(a->_name_data), archive_entry_pathname(a->entry)); a->name = a->_name_data.s; archive_clear_error(&a->archive); /* * Clean up the requested path. This is necessary for correct * dir restores; the dir restore logic otherwise gets messed * up by nonsense like "dir/.". */ ret = cleanup_pathname(a); if (ret != ARCHIVE_OK) return (ret); /* * Query the umask so we get predictable mode settings. * This gets done on every call to _write_header in case the * user edits their umask during the extraction for some * reason. */ umask(a->user_umask = umask(0)); /* Figure out what we need to do for this entry. */ a->todo = TODO_MODE_BASE; if (a->flags & ARCHIVE_EXTRACT_PERM) { a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */ /* * SGID requires an extra "check" step because we * cannot easily predict the GID that the system will * assign. (Different systems assign GIDs to files * based on a variety of criteria, including process * credentials and the gid of the enclosing * directory.) We can only restore the SGID bit if * the file has the right GID, and we only know the * GID if we either set it (see set_ownership) or if * we've actually called stat() on the file after it * was restored. Since there are several places at * which we might verify the GID, we need a TODO bit * to keep track. */ if (a->mode & S_ISGID) a->todo |= TODO_SGID | TODO_SGID_CHECK; /* * Verifying the SUID is simpler, but can still be * done in multiple ways, hence the separate "check" bit. */ if (a->mode & S_ISUID) a->todo |= TODO_SUID | TODO_SUID_CHECK; } else { /* * User didn't request full permissions, so don't * restore SUID, SGID bits and obey umask. */ a->mode &= ~S_ISUID; a->mode &= ~S_ISGID; a->mode &= ~S_ISVTX; a->mode &= ~a->user_umask; } if (a->flags & ARCHIVE_EXTRACT_OWNER) a->todo |= TODO_OWNER; if (a->flags & ARCHIVE_EXTRACT_TIME) a->todo |= TODO_TIMES; if (a->flags & ARCHIVE_EXTRACT_ACL) { #if ARCHIVE_ACL_DARWIN /* * On MacOS, platform ACLs get stored in mac_metadata, too. * If we intend to extract mac_metadata and it is present * we skip extracting libarchive NFSv4 ACLs. */ size_t metadata_size; if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 || archive_entry_mac_metadata(a->entry, &metadata_size) == NULL || metadata_size == 0) #endif #if ARCHIVE_ACL_LIBRICHACL /* * RichACLs are stored in an extended attribute. * If we intend to extract extended attributes and have this * attribute we skip extracting libarchive NFSv4 ACLs. */ short extract_acls = 1; if (a->flags & ARCHIVE_EXTRACT_XATTR && ( archive_entry_acl_types(a->entry) & ARCHIVE_ENTRY_ACL_TYPE_NFS4)) { const char *attr_name; const void *attr_value; size_t attr_size; int i = archive_entry_xattr_reset(a->entry); while (i--) { archive_entry_xattr_next(a->entry, &attr_name, &attr_value, &attr_size); if (attr_name != NULL && attr_value != NULL && attr_size > 0 && strcmp(attr_name, "trusted.richacl") == 0) { extract_acls = 0; break; } } } if (extract_acls) #endif #if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL { #endif if (archive_entry_filetype(a->entry) == AE_IFDIR) a->deferred |= TODO_ACLS; else a->todo |= TODO_ACLS; #if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL } #endif } if (a->flags & ARCHIVE_EXTRACT_MAC_METADATA) { if (archive_entry_filetype(a->entry) == AE_IFDIR) a->deferred |= TODO_MAC_METADATA; else a->todo |= TODO_MAC_METADATA; } #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H) if ((a->flags & ARCHIVE_EXTRACT_NO_HFS_COMPRESSION) == 0) { unsigned long set, clear; archive_entry_fflags(a->entry, &set, &clear); if ((set & ~clear) & UF_COMPRESSED) { a->todo |= TODO_HFS_COMPRESSION; a->decmpfs_block_count = (unsigned)-1; } } if ((a->flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED) != 0 && (a->mode & AE_IFMT) == AE_IFREG && a->filesize > 0) { a->todo |= TODO_HFS_COMPRESSION; a->decmpfs_block_count = (unsigned)-1; } { const char *p; /* Check if the current file name is a type of the * resource fork file. */ p = strrchr(a->name, '/'); if (p == NULL) p = a->name; else p++; if (p[0] == '.' && p[1] == '_') { /* Do not compress "._XXX" files. */ a->todo &= ~TODO_HFS_COMPRESSION; if (a->filesize > 0) a->todo |= TODO_APPLEDOUBLE; } } #endif if (a->flags & ARCHIVE_EXTRACT_XATTR) { #if ARCHIVE_XATTR_DARWIN /* * On MacOS, extended attributes get stored in mac_metadata, * too. If we intend to extract mac_metadata and it is present * we skip extracting extended attributes. */ size_t metadata_size; if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 || archive_entry_mac_metadata(a->entry, &metadata_size) == NULL || metadata_size == 0) #endif a->todo |= TODO_XATTR; } if (a->flags & ARCHIVE_EXTRACT_FFLAGS) a->todo |= TODO_FFLAGS; if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) { ret = check_symlinks(a); if (ret != ARCHIVE_OK) return (ret); } #if defined(HAVE_FCHDIR) && defined(PATH_MAX) /* If path exceeds PATH_MAX, shorten the path. */ edit_deep_directories(a); #endif ret = restore_entry(a); #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H) /* * Check if the filesystem the file is restoring on supports * HFS+ Compression. If not, cancel HFS+ Compression. */ if (a->todo | TODO_HFS_COMPRESSION) { /* * NOTE: UF_COMPRESSED is ignored even if the filesystem * supports HFS+ Compression because the file should * have at least an extended attribute "com.apple.decmpfs" * before the flag is set to indicate that the file have * been compressed. If the filesystem does not support * HFS+ Compression the system call will fail. */ if (a->fd < 0 || fchflags(a->fd, UF_COMPRESSED) != 0) a->todo &= ~TODO_HFS_COMPRESSION; } #endif /* * TODO: There are rumours that some extended attributes must * be restored before file data is written. If this is true, * then we either need to write all extended attributes both * before and after restoring the data, or find some rule for * determining which must go first and which last. Due to the * many ways people are using xattrs, this may prove to be an * intractable problem. */ #ifdef HAVE_FCHDIR /* If we changed directory above, restore it here. */ if (a->restore_pwd >= 0) { r = fchdir(a->restore_pwd); if (r != 0) { archive_set_error(&a->archive, errno, "chdir() failure"); ret = ARCHIVE_FATAL; } close(a->restore_pwd); a->restore_pwd = -1; } #endif /* * Fixup uses the unedited pathname from archive_entry_pathname(), * because it is relative to the base dir and the edited path * might be relative to some intermediate dir as a result of the * deep restore logic. */ if (a->deferred & TODO_MODE) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->fixup |= TODO_MODE_BASE; fe->mode = a->mode; } if ((a->deferred & TODO_TIMES) && (archive_entry_mtime_is_set(entry) || archive_entry_atime_is_set(entry))) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->mode = a->mode; fe->fixup |= TODO_TIMES; if (archive_entry_atime_is_set(entry)) { fe->atime = archive_entry_atime(entry); fe->atime_nanos = archive_entry_atime_nsec(entry); } else { /* If atime is unset, use start time. */ fe->atime = a->start_time; fe->atime_nanos = 0; } if (archive_entry_mtime_is_set(entry)) { fe->mtime = archive_entry_mtime(entry); fe->mtime_nanos = archive_entry_mtime_nsec(entry); } else { /* If mtime is unset, use start time. */ fe->mtime = a->start_time; fe->mtime_nanos = 0; } if (archive_entry_birthtime_is_set(entry)) { fe->birthtime = archive_entry_birthtime(entry); fe->birthtime_nanos = archive_entry_birthtime_nsec( entry); } else { /* If birthtime is unset, use mtime. */ fe->birthtime = fe->mtime; fe->birthtime_nanos = fe->mtime_nanos; } } if (a->deferred & TODO_ACLS) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->fixup |= TODO_ACLS; archive_acl_copy(&fe->acl, archive_entry_acl(entry)); } if (a->deferred & TODO_MAC_METADATA) { const void *metadata; size_t metadata_size; metadata = archive_entry_mac_metadata(a->entry, &metadata_size); if (metadata != NULL && metadata_size > 0) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->mac_metadata = malloc(metadata_size); if (fe->mac_metadata != NULL) { memcpy(fe->mac_metadata, metadata, metadata_size); fe->mac_metadata_size = metadata_size; fe->fixup |= TODO_MAC_METADATA; } } } if (a->deferred & TODO_FFLAGS) { fe = current_fixup(a, archive_entry_pathname(entry)); if (fe == NULL) return (ARCHIVE_FATAL); fe->fixup |= TODO_FFLAGS; /* TODO: Complete this.. defer fflags from below. */ } /* We've created the object and are ready to pour data into it. */ if (ret >= ARCHIVE_WARN) a->archive.state = ARCHIVE_STATE_DATA; /* * If it's not open, tell our client not to try writing. * In particular, dirs, links, etc, don't get written to. */ if (a->fd < 0) { archive_entry_set_size(entry, 0); a->filesize = 0; } return (ret); } int archive_write_disk_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file"); a->skip_file_set = 1; a->skip_file_dev = d; a->skip_file_ino = i; return (ARCHIVE_OK); } static ssize_t write_data_block(struct archive_write_disk *a, const char *buff, size_t size) { uint64_t start_size = size; ssize_t bytes_written = 0; ssize_t block_size = 0, bytes_to_write; if (size == 0) return (ARCHIVE_OK); if (a->filesize == 0 || a->fd < 0) { archive_set_error(&a->archive, 0, "Attempt to write to an empty file"); return (ARCHIVE_WARN); } if (a->flags & ARCHIVE_EXTRACT_SPARSE) { #if HAVE_STRUCT_STAT_ST_BLKSIZE int r; if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); block_size = a->pst->st_blksize; #else /* XXX TODO XXX Is there a more appropriate choice here ? */ /* This needn't match the filesystem allocation size. */ block_size = 16*1024; #endif } /* If this write would run beyond the file size, truncate it. */ if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize) start_size = size = (size_t)(a->filesize - a->offset); /* Write the data. */ while (size > 0) { if (block_size == 0) { bytes_to_write = size; } else { /* We're sparsifying the file. */ const char *p, *end; int64_t block_end; /* Skip leading zero bytes. */ for (p = buff, end = buff + size; p < end; ++p) { if (*p != '\0') break; } a->offset += p - buff; size -= p - buff; buff = p; if (size == 0) break; /* Calculate next block boundary after offset. */ block_end = (a->offset / block_size + 1) * block_size; /* If the adjusted write would cross block boundary, * truncate it to the block boundary. */ bytes_to_write = size; if (a->offset + bytes_to_write > block_end) bytes_to_write = block_end - a->offset; } /* Seek if necessary to the specified offset. */ if (a->offset != a->fd_offset) { if (lseek(a->fd, a->offset, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek failed"); return (ARCHIVE_FATAL); } a->fd_offset = a->offset; } bytes_written = write(a->fd, buff, bytes_to_write); if (bytes_written < 0) { archive_set_error(&a->archive, errno, "Write failed"); return (ARCHIVE_WARN); } buff += bytes_written; size -= bytes_written; a->total_bytes_written += bytes_written; a->offset += bytes_written; a->fd_offset = a->offset; } return (start_size - size); } #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\ && defined(HAVE_ZLIB_H) /* * Set UF_COMPRESSED file flag. * This have to be called after hfs_write_decmpfs() because if the * file does not have "com.apple.decmpfs" xattr the flag is ignored. */ static int hfs_set_compressed_fflag(struct archive_write_disk *a) { int r; if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); a->st.st_flags |= UF_COMPRESSED; if (fchflags(a->fd, a->st.st_flags) != 0) { archive_set_error(&a->archive, errno, "Failed to set UF_COMPRESSED file flag"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } /* * HFS+ Compression decmpfs * * +------------------------------+ +0 * | Magic(LE 4 bytes) | * +------------------------------+ * | Type(LE 4 bytes) | * +------------------------------+ * | Uncompressed size(LE 8 bytes)| * +------------------------------+ +16 * | | * | Compressed data | * | (Placed only if Type == 3) | * | | * +------------------------------+ +3802 = MAX_DECMPFS_XATTR_SIZE * * Type is 3: decmpfs has compressed data. * Type is 4: Resource Fork has compressed data. */ /* * Write "com.apple.decmpfs" */ static int hfs_write_decmpfs(struct archive_write_disk *a) { int r; uint32_t compression_type; r = fsetxattr(a->fd, DECMPFS_XATTR_NAME, a->decmpfs_header_p, a->decmpfs_attr_size, 0, 0); if (r < 0) { archive_set_error(&a->archive, errno, "Cannot restore xattr:%s", DECMPFS_XATTR_NAME); compression_type = archive_le32dec( &a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE]); if (compression_type == CMP_RESOURCE_FORK) fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME, XATTR_SHOWCOMPRESSION); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } /* * HFS+ Compression Resource Fork * * +-----------------------------+ * | Header(260 bytes) | * +-----------------------------+ * | Block count(LE 4 bytes) | * +-----------------------------+ --+ * +-- | Offset (LE 4 bytes) | | * | | [distance from Block count] | | Block 0 * | +-----------------------------+ | * | | Compressed size(LE 4 bytes) | | * | +-----------------------------+ --+ * | | | * | | .................. | * | | | * | +-----------------------------+ --+ * | | Offset (LE 4 bytes) | | * | +-----------------------------+ | Block (Block count -1) * | | Compressed size(LE 4 bytes) | | * +-> +-----------------------------+ --+ * | Compressed data(n bytes) | Block 0 * +-----------------------------+ * | | * | .................. | * | | * +-----------------------------+ * | Compressed data(n bytes) | Block (Block count -1) * +-----------------------------+ * | Footer(50 bytes) | * +-----------------------------+ * */ /* * Write the header of "com.apple.ResourceFork" */ static int hfs_write_resource_fork(struct archive_write_disk *a, unsigned char *buff, size_t bytes, uint32_t position) { int ret; ret = fsetxattr(a->fd, XATTR_RESOURCEFORK_NAME, buff, bytes, position, a->rsrc_xattr_options); if (ret < 0) { archive_set_error(&a->archive, errno, "Cannot restore xattr: %s at %u pos %u bytes", XATTR_RESOURCEFORK_NAME, (unsigned)position, (unsigned)bytes); return (ARCHIVE_WARN); } a->rsrc_xattr_options &= ~XATTR_CREATE; return (ARCHIVE_OK); } static int hfs_write_compressed_data(struct archive_write_disk *a, size_t bytes_compressed) { int ret; ret = hfs_write_resource_fork(a, a->compressed_buffer, bytes_compressed, a->compressed_rsrc_position); if (ret == ARCHIVE_OK) a->compressed_rsrc_position += bytes_compressed; return (ret); } static int hfs_write_resource_fork_header(struct archive_write_disk *a) { unsigned char *buff; uint32_t rsrc_bytes; uint32_t rsrc_header_bytes; /* * Write resource fork header + block info. */ buff = a->resource_fork; rsrc_bytes = a->compressed_rsrc_position - RSRC_F_SIZE; rsrc_header_bytes = RSRC_H_SIZE + /* Header base size. */ 4 + /* Block count. */ (a->decmpfs_block_count * 8);/* Block info */ archive_be32enc(buff, 0x100); archive_be32enc(buff + 4, rsrc_bytes); archive_be32enc(buff + 8, rsrc_bytes - 256); archive_be32enc(buff + 12, 0x32); memset(buff + 16, 0, 240); archive_be32enc(buff + 256, rsrc_bytes - 260); return hfs_write_resource_fork(a, buff, rsrc_header_bytes, 0); } static size_t hfs_set_resource_fork_footer(unsigned char *buff, size_t buff_size) { static const char rsrc_footer[RSRC_F_SIZE] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 'c', 'm', 'p', 'f', 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; if (buff_size < sizeof(rsrc_footer)) return (0); memcpy(buff, rsrc_footer, sizeof(rsrc_footer)); return (sizeof(rsrc_footer)); } static int hfs_reset_compressor(struct archive_write_disk *a) { int ret; if (a->stream_valid) ret = deflateReset(&a->stream); else ret = deflateInit(&a->stream, a->decmpfs_compression_level); if (ret != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to initialize compressor"); return (ARCHIVE_FATAL); } else a->stream_valid = 1; return (ARCHIVE_OK); } static int hfs_decompress(struct archive_write_disk *a) { uint32_t *block_info; unsigned int block_count; uint32_t data_pos, data_size; ssize_t r; ssize_t bytes_written, bytes_to_write; unsigned char *b; block_info = (uint32_t *)(a->resource_fork + RSRC_H_SIZE); block_count = archive_le32dec(block_info++); while (block_count--) { data_pos = RSRC_H_SIZE + archive_le32dec(block_info++); data_size = archive_le32dec(block_info++); r = fgetxattr(a->fd, XATTR_RESOURCEFORK_NAME, a->compressed_buffer, data_size, data_pos, 0); if (r != data_size) { archive_set_error(&a->archive, (r < 0)?errno:ARCHIVE_ERRNO_MISC, "Failed to read resource fork"); return (ARCHIVE_WARN); } if (a->compressed_buffer[0] == 0xff) { bytes_to_write = data_size -1; b = a->compressed_buffer + 1; } else { uLong dest_len = MAX_DECMPFS_BLOCK_SIZE; int zr; zr = uncompress((Bytef *)a->uncompressed_buffer, &dest_len, a->compressed_buffer, data_size); if (zr != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to decompress resource fork"); return (ARCHIVE_WARN); } bytes_to_write = dest_len; b = (unsigned char *)a->uncompressed_buffer; } do { bytes_written = write(a->fd, b, bytes_to_write); if (bytes_written < 0) { archive_set_error(&a->archive, errno, "Write failed"); return (ARCHIVE_WARN); } bytes_to_write -= bytes_written; b += bytes_written; } while (bytes_to_write > 0); } r = fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME, 0); if (r == -1) { archive_set_error(&a->archive, errno, "Failed to remove resource fork"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int hfs_drive_compressor(struct archive_write_disk *a, const char *buff, size_t size) { unsigned char *buffer_compressed; size_t bytes_compressed; size_t bytes_used; int ret; ret = hfs_reset_compressor(a); if (ret != ARCHIVE_OK) return (ret); if (a->compressed_buffer == NULL) { size_t block_size; block_size = COMPRESSED_W_SIZE + RSRC_F_SIZE + + compressBound(MAX_DECMPFS_BLOCK_SIZE); a->compressed_buffer = malloc(block_size); if (a->compressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Resource Fork"); return (ARCHIVE_FATAL); } a->compressed_buffer_size = block_size; a->compressed_buffer_remaining = block_size; } buffer_compressed = a->compressed_buffer + a->compressed_buffer_size - a->compressed_buffer_remaining; a->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff; a->stream.avail_in = size; a->stream.next_out = buffer_compressed; a->stream.avail_out = a->compressed_buffer_remaining; do { ret = deflate(&a->stream, Z_FINISH); switch (ret) { case Z_OK: case Z_STREAM_END: break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to compress data"); return (ARCHIVE_FAILED); } } while (ret == Z_OK); bytes_compressed = a->compressed_buffer_remaining - a->stream.avail_out; /* * If the compressed size is larger than the original size, * throw away compressed data, use uncompressed data instead. */ if (bytes_compressed > size) { buffer_compressed[0] = 0xFF;/* uncompressed marker. */ memcpy(buffer_compressed + 1, buff, size); bytes_compressed = size + 1; } a->compressed_buffer_remaining -= bytes_compressed; /* * If the compressed size is smaller than MAX_DECMPFS_XATTR_SIZE * and the block count in the file is only one, store compressed * data to decmpfs xattr instead of the resource fork. */ if (a->decmpfs_block_count == 1 && (a->decmpfs_attr_size + bytes_compressed) <= MAX_DECMPFS_XATTR_SIZE) { archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE], CMP_XATTR); memcpy(a->decmpfs_header_p + DECMPFS_HEADER_SIZE, buffer_compressed, bytes_compressed); a->decmpfs_attr_size += bytes_compressed; a->compressed_buffer_remaining = a->compressed_buffer_size; /* * Finish HFS+ Compression. * - Write the decmpfs xattr. * - Set the UF_COMPRESSED file flag. */ ret = hfs_write_decmpfs(a); if (ret == ARCHIVE_OK) ret = hfs_set_compressed_fflag(a); return (ret); } /* Update block info. */ archive_le32enc(a->decmpfs_block_info++, a->compressed_rsrc_position_v - RSRC_H_SIZE); archive_le32enc(a->decmpfs_block_info++, bytes_compressed); a->compressed_rsrc_position_v += bytes_compressed; /* * Write the compressed data to the resource fork. */ bytes_used = a->compressed_buffer_size - a->compressed_buffer_remaining; while (bytes_used >= COMPRESSED_W_SIZE) { ret = hfs_write_compressed_data(a, COMPRESSED_W_SIZE); if (ret != ARCHIVE_OK) return (ret); bytes_used -= COMPRESSED_W_SIZE; if (bytes_used > COMPRESSED_W_SIZE) memmove(a->compressed_buffer, a->compressed_buffer + COMPRESSED_W_SIZE, bytes_used); else memcpy(a->compressed_buffer, a->compressed_buffer + COMPRESSED_W_SIZE, bytes_used); } a->compressed_buffer_remaining = a->compressed_buffer_size - bytes_used; /* * If the current block is the last block, write the remaining * compressed data and the resource fork footer. */ if (a->file_remaining_bytes == 0) { size_t rsrc_size; int64_t bk; /* Append the resource footer. */ rsrc_size = hfs_set_resource_fork_footer( a->compressed_buffer + bytes_used, a->compressed_buffer_remaining); ret = hfs_write_compressed_data(a, bytes_used + rsrc_size); a->compressed_buffer_remaining = a->compressed_buffer_size; /* If the compressed size is not enough smaller than * the uncompressed size. cancel HFS+ compression. * TODO: study a behavior of ditto utility and improve * the condition to fall back into no HFS+ compression. */ bk = HFS_BLOCKS(a->compressed_rsrc_position); bk += bk >> 7; if (bk > HFS_BLOCKS(a->filesize)) return hfs_decompress(a); /* * Write the resourcefork header. */ if (ret == ARCHIVE_OK) ret = hfs_write_resource_fork_header(a); /* * Finish HFS+ Compression. * - Write the decmpfs xattr. * - Set the UF_COMPRESSED file flag. */ if (ret == ARCHIVE_OK) ret = hfs_write_decmpfs(a); if (ret == ARCHIVE_OK) ret = hfs_set_compressed_fflag(a); } return (ret); } static ssize_t hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff, size_t size) { const char *buffer_to_write; size_t bytes_to_write; int ret; if (a->decmpfs_block_count == (unsigned)-1) { void *new_block; size_t new_size; unsigned int block_count; if (a->decmpfs_header_p == NULL) { new_block = malloc(MAX_DECMPFS_XATTR_SIZE + sizeof(uint32_t)); if (new_block == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for decmpfs"); return (ARCHIVE_FATAL); } a->decmpfs_header_p = new_block; } a->decmpfs_attr_size = DECMPFS_HEADER_SIZE; archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_MAGIC], DECMPFS_MAGIC); archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE], CMP_RESOURCE_FORK); archive_le64enc(&a->decmpfs_header_p[DECMPFS_UNCOMPRESSED_SIZE], a->filesize); /* Calculate a block count of the file. */ block_count = (a->filesize + MAX_DECMPFS_BLOCK_SIZE -1) / MAX_DECMPFS_BLOCK_SIZE; /* * Allocate buffer for resource fork. * Set up related pointers; */ new_size = RSRC_H_SIZE + /* header */ 4 + /* Block count */ (block_count * sizeof(uint32_t) * 2) + RSRC_F_SIZE; /* footer */ if (new_size > a->resource_fork_allocated_size) { new_block = realloc(a->resource_fork, new_size); if (new_block == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for ResourceFork"); return (ARCHIVE_FATAL); } a->resource_fork_allocated_size = new_size; a->resource_fork = new_block; } /* Allocate uncompressed buffer */ if (a->uncompressed_buffer == NULL) { new_block = malloc(MAX_DECMPFS_BLOCK_SIZE); if (new_block == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for decmpfs"); return (ARCHIVE_FATAL); } a->uncompressed_buffer = new_block; } a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE; a->file_remaining_bytes = a->filesize; a->compressed_buffer_remaining = a->compressed_buffer_size; /* * Set up a resource fork. */ a->rsrc_xattr_options = XATTR_CREATE; /* Get the position where we are going to set a bunch * of block info. */ a->decmpfs_block_info = (uint32_t *)(a->resource_fork + RSRC_H_SIZE); /* Set the block count to the resource fork. */ archive_le32enc(a->decmpfs_block_info++, block_count); /* Get the position where we are going to set compressed * data. */ a->compressed_rsrc_position = RSRC_H_SIZE + 4 + (block_count * 8); a->compressed_rsrc_position_v = a->compressed_rsrc_position; a->decmpfs_block_count = block_count; } /* Ignore redundant bytes. */ if (a->file_remaining_bytes == 0) return ((ssize_t)size); /* Do not overrun a block size. */ if (size > a->block_remaining_bytes) bytes_to_write = a->block_remaining_bytes; else bytes_to_write = size; /* Do not overrun the file size. */ if (bytes_to_write > a->file_remaining_bytes) bytes_to_write = a->file_remaining_bytes; /* For efficiency, if a copy length is full of the uncompressed * buffer size, do not copy writing data to it. */ if (bytes_to_write == MAX_DECMPFS_BLOCK_SIZE) buffer_to_write = buff; else { memcpy(a->uncompressed_buffer + MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes, buff, bytes_to_write); buffer_to_write = a->uncompressed_buffer; } a->block_remaining_bytes -= bytes_to_write; a->file_remaining_bytes -= bytes_to_write; if (a->block_remaining_bytes == 0 || a->file_remaining_bytes == 0) { ret = hfs_drive_compressor(a, buffer_to_write, MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes); if (ret < 0) return (ret); a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE; } /* Ignore redundant bytes. */ if (a->file_remaining_bytes == 0) return ((ssize_t)size); return (bytes_to_write); } static ssize_t hfs_write_data_block(struct archive_write_disk *a, const char *buff, size_t size) { uint64_t start_size = size; ssize_t bytes_written = 0; ssize_t bytes_to_write; if (size == 0) return (ARCHIVE_OK); if (a->filesize == 0 || a->fd < 0) { archive_set_error(&a->archive, 0, "Attempt to write to an empty file"); return (ARCHIVE_WARN); } /* If this write would run beyond the file size, truncate it. */ if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize) start_size = size = (size_t)(a->filesize - a->offset); /* Write the data. */ while (size > 0) { bytes_to_write = size; /* Seek if necessary to the specified offset. */ if (a->offset < a->fd_offset) { /* Can't support backward move. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Seek failed"); return (ARCHIVE_FATAL); } else if (a->offset > a->fd_offset) { int64_t skip = a->offset - a->fd_offset; char nullblock[1024]; memset(nullblock, 0, sizeof(nullblock)); while (skip > 0) { if (skip > (int64_t)sizeof(nullblock)) bytes_written = hfs_write_decmpfs_block( a, nullblock, sizeof(nullblock)); else bytes_written = hfs_write_decmpfs_block( a, nullblock, skip); if (bytes_written < 0) { archive_set_error(&a->archive, errno, "Write failed"); return (ARCHIVE_WARN); } skip -= bytes_written; } a->fd_offset = a->offset; } bytes_written = hfs_write_decmpfs_block(a, buff, bytes_to_write); if (bytes_written < 0) return (bytes_written); buff += bytes_written; size -= bytes_written; a->total_bytes_written += bytes_written; a->offset += bytes_written; a->fd_offset = a->offset; } return (start_size - size); } #else static ssize_t hfs_write_data_block(struct archive_write_disk *a, const char *buff, size_t size) { return (write_data_block(a, buff, size)); } #endif static ssize_t _archive_write_disk_data_block(struct archive *_a, const void *buff, size_t size, int64_t offset) { struct archive_write_disk *a = (struct archive_write_disk *)_a; ssize_t r; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data_block"); a->offset = offset; if (a->todo & TODO_HFS_COMPRESSION) r = hfs_write_data_block(a, buff, size); else r = write_data_block(a, buff, size); if (r < ARCHIVE_OK) return (r); if ((size_t)r < size) { archive_set_error(&a->archive, 0, "Too much data: Truncating file at %ju bytes", (uintmax_t)a->filesize); return (ARCHIVE_WARN); } #if ARCHIVE_VERSION_NUMBER < 3999000 return (ARCHIVE_OK); #else return (size); #endif } static ssize_t _archive_write_disk_data(struct archive *_a, const void *buff, size_t size) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data"); if (a->todo & TODO_HFS_COMPRESSION) return (hfs_write_data_block(a, buff, size)); return (write_data_block(a, buff, size)); } static int _archive_write_disk_finish_entry(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; int ret = ARCHIVE_OK; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_finish_entry"); if (a->archive.state & ARCHIVE_STATE_HEADER) return (ARCHIVE_OK); archive_clear_error(&a->archive); /* Pad or truncate file to the right size. */ if (a->fd < 0) { /* There's no file. */ } else if (a->filesize < 0) { /* File size is unknown, so we can't set the size. */ } else if (a->fd_offset == a->filesize) { /* Last write ended at exactly the filesize; we're done. */ /* Hopefully, this is the common case. */ #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H) } else if (a->todo & TODO_HFS_COMPRESSION) { char null_d[1024]; ssize_t r; if (a->file_remaining_bytes) memset(null_d, 0, sizeof(null_d)); while (a->file_remaining_bytes) { if (a->file_remaining_bytes > sizeof(null_d)) r = hfs_write_data_block( a, null_d, sizeof(null_d)); else r = hfs_write_data_block( a, null_d, a->file_remaining_bytes); if (r < 0) return ((int)r); } #endif } else { #if HAVE_FTRUNCATE if (ftruncate(a->fd, a->filesize) == -1 && a->filesize == 0) { archive_set_error(&a->archive, errno, "File size could not be restored"); return (ARCHIVE_FAILED); } #endif /* * Not all platforms implement the XSI option to * extend files via ftruncate. Stat() the file again * to see what happened. */ a->pst = NULL; if ((ret = lazy_stat(a)) != ARCHIVE_OK) return (ret); /* We can use lseek()/write() to extend the file if * ftruncate didn't work or isn't available. */ if (a->st.st_size < a->filesize) { const char nul = '\0'; if (lseek(a->fd, a->filesize - 1, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, "Seek failed"); return (ARCHIVE_FATAL); } if (write(a->fd, &nul, 1) < 0) { archive_set_error(&a->archive, errno, "Write to restore size failed"); return (ARCHIVE_FATAL); } a->pst = NULL; } } /* Restore metadata. */ /* * This is specific to Mac OS X. * If the current file is an AppleDouble file, it should be * linked with the data fork file and remove it. */ if (a->todo & TODO_APPLEDOUBLE) { int r2 = fixup_appledouble(a, a->name); if (r2 == ARCHIVE_EOF) { /* The current file has been successfully linked * with the data fork file and removed. So there * is nothing to do on the current file. */ goto finish_metadata; } if (r2 < ret) ret = r2; } /* * Look up the "real" UID only if we're going to need it. * TODO: the TODO_SGID condition can be dropped here, can't it? */ if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) { a->uid = archive_write_disk_uid(&a->archive, archive_entry_uname(a->entry), archive_entry_uid(a->entry)); } /* Look up the "real" GID only if we're going to need it. */ /* TODO: the TODO_SUID condition can be dropped here, can't it? */ if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) { a->gid = archive_write_disk_gid(&a->archive, archive_entry_gname(a->entry), archive_entry_gid(a->entry)); } /* * Restore ownership before set_mode tries to restore suid/sgid * bits. If we set the owner, we know what it is and can skip * a stat() call to examine the ownership of the file on disk. */ if (a->todo & TODO_OWNER) { int r2 = set_ownership(a); if (r2 < ret) ret = r2; } /* * set_mode must precede ACLs on systems such as Solaris and * FreeBSD where setting the mode implicitly clears extended ACLs */ if (a->todo & TODO_MODE) { int r2 = set_mode(a, a->mode); if (r2 < ret) ret = r2; } /* * Security-related extended attributes (such as * security.capability on Linux) have to be restored last, * since they're implicitly removed by other file changes. */ if (a->todo & TODO_XATTR) { int r2 = set_xattrs(a); if (r2 < ret) ret = r2; } /* * Some flags prevent file modification; they must be restored after * file contents are written. */ if (a->todo & TODO_FFLAGS) { int r2 = set_fflags(a); if (r2 < ret) ret = r2; } /* * Time must follow most other metadata; * otherwise atime will get changed. */ if (a->todo & TODO_TIMES) { int r2 = set_times_from_entry(a); if (r2 < ret) ret = r2; } /* * Mac extended metadata includes ACLs. */ if (a->todo & TODO_MAC_METADATA) { const void *metadata; size_t metadata_size; metadata = archive_entry_mac_metadata(a->entry, &metadata_size); if (metadata != NULL && metadata_size > 0) { int r2 = set_mac_metadata(a, archive_entry_pathname( a->entry), metadata, metadata_size); if (r2 < ret) ret = r2; } } /* * ACLs must be restored after timestamps because there are * ACLs that prevent attribute changes (including time). */ if (a->todo & TODO_ACLS) { int r2; r2 = archive_write_disk_set_acls(&a->archive, a->fd, archive_entry_pathname(a->entry), archive_entry_acl(a->entry), archive_entry_mode(a->entry)); if (r2 < ret) ret = r2; } finish_metadata: /* If there's an fd, we can close it now. */ if (a->fd >= 0) { close(a->fd); a->fd = -1; } /* If there's an entry, we can release it now. */ - if (a->entry) { - archive_entry_free(a->entry); - a->entry = NULL; - } + archive_entry_free(a->entry); + a->entry = NULL; a->archive.state = ARCHIVE_STATE_HEADER; return (ret); } int archive_write_disk_set_group_lookup(struct archive *_a, void *private_data, la_int64_t (*lookup_gid)(void *private, const char *gname, la_int64_t gid), void (*cleanup_gid)(void *private)) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_set_group_lookup"); if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL) (a->cleanup_gid)(a->lookup_gid_data); a->lookup_gid = lookup_gid; a->cleanup_gid = cleanup_gid; a->lookup_gid_data = private_data; return (ARCHIVE_OK); } int archive_write_disk_set_user_lookup(struct archive *_a, void *private_data, int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid), void (*cleanup_uid)(void *private)) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_set_user_lookup"); if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL) (a->cleanup_uid)(a->lookup_uid_data); a->lookup_uid = lookup_uid; a->cleanup_uid = cleanup_uid; a->lookup_uid_data = private_data; return (ARCHIVE_OK); } int64_t archive_write_disk_gid(struct archive *_a, const char *name, la_int64_t id) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_gid"); if (a->lookup_gid) return (a->lookup_gid)(a->lookup_gid_data, name, id); return (id); } int64_t archive_write_disk_uid(struct archive *_a, const char *name, la_int64_t id) { struct archive_write_disk *a = (struct archive_write_disk *)_a; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY, "archive_write_disk_uid"); if (a->lookup_uid) return (a->lookup_uid)(a->lookup_uid_data, name, id); return (id); } /* * Create a new archive_write_disk object and initialize it with global state. */ struct archive * archive_write_disk_new(void) { struct archive_write_disk *a; a = (struct archive_write_disk *)calloc(1, sizeof(*a)); if (a == NULL) return (NULL); a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC; /* We're ready to write a header immediately. */ a->archive.state = ARCHIVE_STATE_HEADER; a->archive.vtable = archive_write_disk_vtable(); a->start_time = time(NULL); /* Query and restore the umask. */ umask(a->user_umask = umask(0)); #ifdef HAVE_GETEUID a->user_uid = geteuid(); #endif /* HAVE_GETEUID */ if (archive_string_ensure(&a->path_safe, 512) == NULL) { free(a); return (NULL); } #ifdef HAVE_ZLIB_H a->decmpfs_compression_level = 5; #endif return (&a->archive); } /* * If pathname is longer than PATH_MAX, chdir to a suitable * intermediate dir and edit the path down to a shorter suffix. Note * that this routine never returns an error; if the chdir() attempt * fails for any reason, we just go ahead with the long pathname. The * object creation is likely to fail, but any error will get handled * at that time. */ #if defined(HAVE_FCHDIR) && defined(PATH_MAX) static void edit_deep_directories(struct archive_write_disk *a) { int ret; char *tail = a->name; /* If path is short, avoid the open() below. */ if (strlen(tail) < PATH_MAX) return; /* Try to record our starting dir. */ a->restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(a->restore_pwd); if (a->restore_pwd < 0) return; /* As long as the path is too long... */ while (strlen(tail) >= PATH_MAX) { /* Locate a dir prefix shorter than PATH_MAX. */ tail += PATH_MAX - 8; while (tail > a->name && *tail != '/') tail--; /* Exit if we find a too-long path component. */ if (tail <= a->name) return; /* Create the intermediate dir and chdir to it. */ *tail = '\0'; /* Terminate dir portion */ ret = create_dir(a, a->name); if (ret == ARCHIVE_OK && chdir(a->name) != 0) ret = ARCHIVE_FAILED; *tail = '/'; /* Restore the / we removed. */ if (ret != ARCHIVE_OK) return; tail++; /* The chdir() succeeded; we've now shortened the path. */ a->name = tail; } return; } #endif /* * The main restore function. */ static int restore_entry(struct archive_write_disk *a) { int ret = ARCHIVE_OK, en; if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) { /* * TODO: Fix this. Apparently, there are platforms * that still allow root to hose the entire filesystem * by unlinking a dir. The S_ISDIR() test above * prevents us from using unlink() here if the new * object is a dir, but that doesn't mean the old * object isn't a dir. */ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) (void)clear_nochange_fflags(a); if (unlink(a->name) == 0) { /* We removed it, reset cached stat. */ a->pst = NULL; } else if (errno == ENOENT) { /* File didn't exist, that's just as good. */ } else if (rmdir(a->name) == 0) { /* It was a dir, but now it's gone. */ a->pst = NULL; } else { /* We tried, but couldn't get rid of it. */ archive_set_error(&a->archive, errno, "Could not unlink"); return(ARCHIVE_FAILED); } } /* Try creating it first; if this fails, we'll try to recover. */ en = create_filesystem_object(a); if ((en == ENOTDIR || en == ENOENT) && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) { /* If the parent dir doesn't exist, try creating it. */ create_parent_dir(a, a->name); /* Now try to create the object again. */ en = create_filesystem_object(a); } if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) { archive_set_error(&a->archive, en, "Hard-link target '%s' does not exist.", archive_entry_hardlink(a->entry)); return (ARCHIVE_FAILED); } if ((en == EISDIR || en == EEXIST) && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { /* If we're not overwriting, we're done. */ if (S_ISDIR(a->mode)) { /* Don't overwrite any settings on existing directories. */ a->todo = 0; } archive_entry_unset_size(a->entry); return (ARCHIVE_OK); } /* * Some platforms return EISDIR if you call * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some * return EEXIST. POSIX is ambiguous, requiring EISDIR * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT) * on an existing item. */ if (en == EISDIR) { /* A dir is in the way of a non-dir, rmdir it. */ if (rmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't remove already-existing dir"); return (ARCHIVE_FAILED); } a->pst = NULL; /* Try again. */ en = create_filesystem_object(a); } else if (en == EEXIST) { /* * We know something is in the way, but we don't know what; * we need to find out before we go any further. */ int r = 0; /* * The SECURE_SYMLINKS logic has already removed a * symlink to a dir if the client wants that. So * follow the symlink if we're creating a dir. */ if (S_ISDIR(a->mode)) r = stat(a->name, &a->st); /* * If it's not a dir (or it's a broken symlink), * then don't follow it. */ if (r != 0 || !S_ISDIR(a->mode)) r = lstat(a->name, &a->st); if (r != 0) { archive_set_error(&a->archive, errno, "Can't stat existing object"); return (ARCHIVE_FAILED); } /* * NO_OVERWRITE_NEWER doesn't apply to directories. */ if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER) && !S_ISDIR(a->st.st_mode)) { if (!older(&(a->st), a->entry)) { archive_entry_unset_size(a->entry); return (ARCHIVE_OK); } } /* If it's our archive, we're done. */ if (a->skip_file_set && a->st.st_dev == (dev_t)a->skip_file_dev && a->st.st_ino == (ino_t)a->skip_file_ino) { archive_set_error(&a->archive, 0, "Refusing to overwrite archive"); return (ARCHIVE_FAILED); } if (!S_ISDIR(a->st.st_mode)) { /* A non-dir is in the way, unlink it. */ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) (void)clear_nochange_fflags(a); if (unlink(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't unlink already-existing object"); return (ARCHIVE_FAILED); } a->pst = NULL; /* Try again. */ en = create_filesystem_object(a); } else if (!S_ISDIR(a->mode)) { /* A dir is in the way of a non-dir, rmdir it. */ if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) (void)clear_nochange_fflags(a); if (rmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't replace existing directory with non-directory"); return (ARCHIVE_FAILED); } /* Try again. */ en = create_filesystem_object(a); } else { /* * There's a dir in the way of a dir. Don't * waste time with rmdir()/mkdir(), just fix * up the permissions on the existing dir. * Note that we don't change perms on existing * dirs unless _EXTRACT_PERM is specified. */ if ((a->mode != a->st.st_mode) && (a->todo & TODO_MODE_FORCE)) a->deferred |= (a->todo & TODO_MODE); /* Ownership doesn't need deferred fixup. */ en = 0; /* Forget the EEXIST. */ } } if (en) { /* Everything failed; give up here. */ if ((&a->archive)->error == NULL) archive_set_error(&a->archive, en, "Can't create '%s'", a->name); return (ARCHIVE_FAILED); } a->pst = NULL; /* Cached stat data no longer valid. */ return (ret); } /* * Returns 0 if creation succeeds, or else returns errno value from * the failed system call. Note: This function should only ever perform * a single system call. */ static int create_filesystem_object(struct archive_write_disk *a) { /* Create the entry. */ const char *linkname; mode_t final_mode, mode; int r; /* these for check_symlinks_fsobj */ char *linkname_copy; /* non-const copy of linkname */ struct stat st; struct archive_string error_string; int error_number; /* We identify hard/symlinks according to the link names. */ /* Since link(2) and symlink(2) don't handle modes, we're done here. */ linkname = archive_entry_hardlink(a->entry); if (linkname != NULL) { #if !HAVE_LINK return (EPERM); #else archive_string_init(&error_string); linkname_copy = strdup(linkname); if (linkname_copy == NULL) { return (EPERM); } /* * TODO: consider using the cleaned-up path as the link * target? */ r = cleanup_pathname_fsobj(linkname_copy, &error_number, &error_string, a->flags); if (r != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); free(linkname_copy); archive_string_free(&error_string); /* * EPERM is more appropriate than error_number for our * callers */ return (EPERM); } r = check_symlinks_fsobj(linkname_copy, &error_number, &error_string, a->flags); if (r != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); free(linkname_copy); archive_string_free(&error_string); /* * EPERM is more appropriate than error_number for our * callers */ return (EPERM); } free(linkname_copy); archive_string_free(&error_string); r = link(linkname, a->name) ? errno : 0; /* * New cpio and pax formats allow hardlink entries * to carry data, so we may have to open the file * for hardlink entries. * * If the hardlink was successfully created and * the archive doesn't have carry data for it, * consider it to be non-authoritative for meta data. * This is consistent with GNU tar and BSD pax. * If the hardlink does carry data, let the last * archive entry decide ownership. */ if (r == 0 && a->filesize <= 0) { a->todo = 0; a->deferred = 0; } else if (r == 0 && a->filesize > 0) { #ifdef HAVE_LSTAT r = lstat(a->name, &st); #else r = stat(a->name, &st); #endif if (r != 0) r = errno; else if ((st.st_mode & AE_IFMT) == AE_IFREG) { a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY | O_CLOEXEC | O_NOFOLLOW); __archive_ensure_cloexec_flag(a->fd); if (a->fd < 0) r = errno; } } return (r); #endif } linkname = archive_entry_symlink(a->entry); if (linkname != NULL) { #if HAVE_SYMLINK return symlink(linkname, a->name) ? errno : 0; #else return (EPERM); #endif } /* * The remaining system calls all set permissions, so let's * try to take advantage of that to avoid an extra chmod() * call. (Recall that umask is set to zero right now!) */ /* Mode we want for the final restored object (w/o file type bits). */ final_mode = a->mode & 07777; /* * The mode that will actually be restored in this step. Note * that SUID, SGID, etc, require additional work to ensure * security, so we never restore them at this point. */ mode = final_mode & 0777 & ~a->user_umask; switch (a->mode & AE_IFMT) { default: /* POSIX requires that we fall through here. */ /* FALLTHROUGH */ case AE_IFREG: a->fd = open(a->name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, mode); __archive_ensure_cloexec_flag(a->fd); r = (a->fd < 0); break; case AE_IFCHR: #ifdef HAVE_MKNOD /* Note: we use AE_IFCHR for the case label, and * S_IFCHR for the mknod() call. This is correct. */ r = mknod(a->name, mode | S_IFCHR, archive_entry_rdev(a->entry)); break; #else /* TODO: Find a better way to warn about our inability * to restore a char device node. */ return (EINVAL); #endif /* HAVE_MKNOD */ case AE_IFBLK: #ifdef HAVE_MKNOD r = mknod(a->name, mode | S_IFBLK, archive_entry_rdev(a->entry)); break; #else /* TODO: Find a better way to warn about our inability * to restore a block device node. */ return (EINVAL); #endif /* HAVE_MKNOD */ case AE_IFDIR: mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE; r = mkdir(a->name, mode); if (r == 0) { /* Defer setting dir times. */ a->deferred |= (a->todo & TODO_TIMES); a->todo &= ~TODO_TIMES; /* Never use an immediate chmod(). */ /* We can't avoid the chmod() entirely if EXTRACT_PERM * because of SysV SGID inheritance. */ if ((mode != final_mode) || (a->flags & ARCHIVE_EXTRACT_PERM)) a->deferred |= (a->todo & TODO_MODE); a->todo &= ~TODO_MODE; } break; case AE_IFIFO: #ifdef HAVE_MKFIFO r = mkfifo(a->name, mode); break; #else /* TODO: Find a better way to warn about our inability * to restore a fifo. */ return (EINVAL); #endif /* HAVE_MKFIFO */ } /* All the system calls above set errno on failure. */ if (r) return (errno); /* If we managed to set the final mode, we've avoided a chmod(). */ if (mode == final_mode) a->todo &= ~TODO_MODE; return (0); } /* * Cleanup function for archive_extract. Mostly, this involves processing * the fixup list, which is used to address a number of problems: * * Dir permissions might prevent us from restoring a file in that * dir, so we restore the dir with minimum 0700 permissions first, * then correct the mode at the end. * * Similarly, the act of restoring a file touches the directory * and changes the timestamp on the dir, so we have to touch-up dir * timestamps at the end as well. * * Some file flags can interfere with the restore by, for example, * preventing the creation of hardlinks to those files. * * Mac OS extended metadata includes ACLs, so must be deferred on dirs. * * Note that tar/cpio do not require that archives be in a particular * order; there is no way to know when the last file has been restored * within a directory, so there's no way to optimize the memory usage * here by fixing up the directory any earlier than the * end-of-archive. * * XXX TODO: Directory ACLs should be restored here, for the same * reason we set directory perms here. XXX */ static int _archive_write_disk_close(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; struct fixup_entry *next, *p; int ret; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_disk_close"); ret = _archive_write_disk_finish_entry(&a->archive); /* Sort dir list so directories are fixed up in depth-first order. */ p = sort_dir_list(a->fixup_list); while (p != NULL) { a->pst = NULL; /* Mark stat cache as out-of-date. */ if (p->fixup & TODO_TIMES) { set_times(a, -1, p->mode, p->name, p->atime, p->atime_nanos, p->birthtime, p->birthtime_nanos, p->mtime, p->mtime_nanos, p->ctime, p->ctime_nanos); } if (p->fixup & TODO_MODE_BASE) chmod(p->name, p->mode); if (p->fixup & TODO_ACLS) archive_write_disk_set_acls(&a->archive, -1, p->name, &p->acl, p->mode); if (p->fixup & TODO_FFLAGS) set_fflags_platform(a, -1, p->name, p->mode, p->fflags_set, 0); if (p->fixup & TODO_MAC_METADATA) set_mac_metadata(a, p->name, p->mac_metadata, p->mac_metadata_size); next = p->next; archive_acl_clear(&p->acl); free(p->mac_metadata); free(p->name); free(p); p = next; } a->fixup_list = NULL; return (ret); } static int _archive_write_disk_free(struct archive *_a) { struct archive_write_disk *a; int ret; if (_a == NULL) return (ARCHIVE_OK); archive_check_magic(_a, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_disk_free"); a = (struct archive_write_disk *)_a; ret = _archive_write_disk_close(&a->archive); archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL); archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL); - if (a->entry) - archive_entry_free(a->entry); + archive_entry_free(a->entry); archive_string_free(&a->_name_data); archive_string_free(&a->archive.error_string); archive_string_free(&a->path_safe); a->archive.magic = 0; __archive_clean(&a->archive); free(a->decmpfs_header_p); free(a->resource_fork); free(a->compressed_buffer); free(a->uncompressed_buffer); #if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\ && defined(HAVE_ZLIB_H) if (a->stream_valid) { switch (deflateEnd(&a->stream)) { case Z_OK: break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to clean up compressor"); ret = ARCHIVE_FATAL; break; } } #endif free(a); return (ret); } /* * Simple O(n log n) merge sort to order the fixup list. In * particular, we want to restore dir timestamps depth-first. */ static struct fixup_entry * sort_dir_list(struct fixup_entry *p) { struct fixup_entry *a, *b, *t; if (p == NULL) return (NULL); /* A one-item list is already sorted. */ if (p->next == NULL) return (p); /* Step 1: split the list. */ t = p; a = p->next->next; while (a != NULL) { /* Step a twice, t once. */ a = a->next; if (a != NULL) a = a->next; t = t->next; } /* Now, t is at the mid-point, so break the list here. */ b = t->next; t->next = NULL; a = p; /* Step 2: Recursively sort the two sub-lists. */ a = sort_dir_list(a); b = sort_dir_list(b); /* Step 3: Merge the returned lists. */ /* Pick the first element for the merged list. */ if (strcmp(a->name, b->name) > 0) { t = p = a; a = a->next; } else { t = p = b; b = b->next; } /* Always put the later element on the list first. */ while (a != NULL && b != NULL) { if (strcmp(a->name, b->name) > 0) { t->next = a; a = a->next; } else { t->next = b; b = b->next; } t = t->next; } /* Only one list is non-empty, so just splice it on. */ if (a != NULL) t->next = a; if (b != NULL) t->next = b; return (p); } /* * Returns a new, initialized fixup entry. * * TODO: Reduce the memory requirements for this list by using a tree * structure rather than a simple list of names. */ static struct fixup_entry * new_fixup(struct archive_write_disk *a, const char *pathname) { struct fixup_entry *fe; fe = (struct fixup_entry *)calloc(1, sizeof(struct fixup_entry)); if (fe == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for a fixup"); return (NULL); } fe->next = a->fixup_list; a->fixup_list = fe; fe->fixup = 0; fe->name = strdup(pathname); return (fe); } /* * Returns a fixup structure for the current entry. */ static struct fixup_entry * current_fixup(struct archive_write_disk *a, const char *pathname) { if (a->current_fixup == NULL) a->current_fixup = new_fixup(a, pathname); return (a->current_fixup); } /* Error helper for new *_fsobj functions */ static void fsobj_error(int *a_eno, struct archive_string *a_estr, int err, const char *errstr, const char *path) { if (a_eno) *a_eno = err; if (a_estr) archive_string_sprintf(a_estr, "%s%s", errstr, path); } /* * TODO: Someday, integrate this with the deep dir support; they both * scan the path and both can be optimized by comparing against other * recent paths. */ /* TODO: Extend this to support symlinks on Windows Vista and later. */ /* * Checks the given path to see if any elements along it are symlinks. Returns * ARCHIVE_OK if there are none, otherwise puts an error in errmsg. */ static int check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, int flags) { #if !defined(HAVE_LSTAT) /* Platform doesn't have lstat, so we can't look for symlinks. */ (void)path; /* UNUSED */ (void)error_number; /* UNUSED */ (void)error_string; /* UNUSED */ (void)flags; /* UNUSED */ return (ARCHIVE_OK); #else int res = ARCHIVE_OK; char *tail; char *head; int last; char c; int r; struct stat st; int restore_pwd; /* Nothing to do here if name is empty */ if(path[0] == '\0') return (ARCHIVE_OK); /* * Guard against symlink tricks. Reject any archive entry whose * destination would be altered by a symlink. * * Walk the filename in chunks separated by '/'. For each segment: * - if it doesn't exist, continue * - if it's symlink, abort or remove it * - if it's a directory and it's not the last chunk, cd into it * As we go: * head points to the current (relative) path * tail points to the temporary \0 terminating the segment we're * currently examining * c holds what used to be in *tail * last is 1 if this is the last tail */ restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(restore_pwd); if (restore_pwd < 0) return (ARCHIVE_FATAL); head = path; tail = path; last = 0; /* TODO: reintroduce a safe cache here? */ /* Skip the root directory if the path is absolute. */ if(tail == path && tail[0] == '/') ++tail; /* Keep going until we've checked the entire name. * head, tail, path all alias the same string, which is * temporarily zeroed at tail, so be careful restoring the * stashed (c=tail[0]) for error messages. * Exiting the loop with break is okay; continue is not. */ while (!last) { /* * Skip the separator we just consumed, plus any adjacent ones */ while (*tail == '/') ++tail; /* Skip the next path element. */ while (*tail != '\0' && *tail != '/') ++tail; /* is this the last path component? */ last = (tail[0] == '\0') || (tail[0] == '/' && tail[1] == '\0'); /* temporarily truncate the string here */ c = tail[0]; tail[0] = '\0'; /* Check that we haven't hit a symlink. */ r = lstat(head, &st); if (r != 0) { tail[0] = c; /* We've hit a dir that doesn't exist; stop now. */ if (errno == ENOENT) { break; } else { /* * Treat any other error as fatal - best to be * paranoid here. * Note: This effectively disables deep * directory support when security checks are * enabled. Otherwise, very long pathnames that * trigger an error here could evade the * sandbox. * TODO: We could do better, but it would * probably require merging the symlink checks * with the deep-directory editing. */ fsobj_error(a_eno, a_estr, errno, "Could not stat ", path); res = ARCHIVE_FAILED; break; } } else if (S_ISDIR(st.st_mode)) { if (!last) { if (chdir(head) != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, "Could not chdir ", path); res = (ARCHIVE_FATAL); break; } /* Our view is now from inside this dir: */ head = tail + 1; } } else if (S_ISLNK(st.st_mode)) { if (last) { /* * Last element is symlink; remove it * so we can overwrite it with the * item being extracted. */ if (unlink(head)) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, "Could not remove symlink ", path); res = ARCHIVE_FAILED; break; } /* * Even if we did remove it, a warning * is in order. The warning is silly, * though, if we're just replacing one * symlink with another symlink. */ tail[0] = c; /* * FIXME: not sure how important this is to * restore */ /* if (!S_ISLNK(path)) { fsobj_error(a_eno, a_estr, 0, "Removing symlink ", path); } */ /* Symlink gone. No more problem! */ res = ARCHIVE_OK; break; } else if (flags & ARCHIVE_EXTRACT_UNLINK) { /* User asked us to remove problems. */ if (unlink(head) != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, 0, "Cannot remove intervening " "symlink ", path); res = ARCHIVE_FAILED; break; } tail[0] = c; } else if ((flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) == 0) { /* * We are not the last element and we want to * follow symlinks if they are a directory. * * This is needed to extract hardlinks over * symlinks. */ r = stat(head, &st); if (r != 0) { tail[0] = c; if (errno == ENOENT) { break; } else { fsobj_error(a_eno, a_estr, errno, "Could not stat ", path); res = (ARCHIVE_FAILED); break; } } else if (S_ISDIR(st.st_mode)) { if (chdir(head) != 0) { tail[0] = c; fsobj_error(a_eno, a_estr, errno, "Could not chdir ", path); res = (ARCHIVE_FATAL); break; } /* * Our view is now from inside * this dir: */ head = tail + 1; } else { tail[0] = c; fsobj_error(a_eno, a_estr, 0, "Cannot extract through " "symlink ", path); res = ARCHIVE_FAILED; break; } } else { tail[0] = c; fsobj_error(a_eno, a_estr, 0, "Cannot extract through symlink ", path); res = ARCHIVE_FAILED; break; } } /* be sure to always maintain this */ tail[0] = c; if (tail[0] != '\0') tail++; /* Advance to the next segment. */ } /* Catches loop exits via break */ tail[0] = c; #ifdef HAVE_FCHDIR /* If we changed directory above, restore it here. */ if (restore_pwd >= 0) { r = fchdir(restore_pwd); if (r != 0) { fsobj_error(a_eno, a_estr, errno, "chdir() failure", ""); } close(restore_pwd); restore_pwd = -1; if (r != 0) { res = (ARCHIVE_FATAL); } } #endif /* TODO: reintroduce a safe cache here? */ return res; #endif } /* * Check a->name for symlinks, returning ARCHIVE_OK if its clean, otherwise * calls archive_set_error and returns ARCHIVE_{FATAL,FAILED} */ static int check_symlinks(struct archive_write_disk *a) { struct archive_string error_string; int error_number; int rc; archive_string_init(&error_string); rc = check_symlinks_fsobj(a->name, &error_number, &error_string, a->flags); if (rc != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); } archive_string_free(&error_string); a->pst = NULL; /* to be safe */ return rc; } #if defined(__CYGWIN__) /* * 1. Convert a path separator from '\' to '/' . * We shouldn't check multibyte character directly because some * character-set have been using the '\' character for a part of * its multibyte character code. * 2. Replace unusable characters in Windows with underscore('_'). * See also : http://msdn.microsoft.com/en-us/library/aa365247.aspx */ static void cleanup_pathname_win(char *path) { wchar_t wc; char *p; size_t alen, l; int mb, complete, utf8; alen = 0; mb = 0; complete = 1; utf8 = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)? 1: 0; for (p = path; *p != '\0'; p++) { ++alen; if (*p == '\\') { /* If previous byte is smaller than 128, * this is not second byte of multibyte characters, * so we can replace '\' with '/'. */ if (utf8 || !mb) *p = '/'; else complete = 0;/* uncompleted. */ } else if (*(unsigned char *)p > 127) mb = 1; else mb = 0; /* Rewrite the path name if its next character is unusable. */ if (*p == ':' || *p == '*' || *p == '?' || *p == '"' || *p == '<' || *p == '>' || *p == '|') *p = '_'; } if (complete) return; /* * Convert path separator in wide-character. */ p = path; while (*p != '\0' && alen) { l = mbtowc(&wc, p, alen); if (l == (size_t)-1) { while (*p != '\0') { if (*p == '\\') *p = '/'; ++p; } break; } if (l == 1 && wc == L'\\') *p = '/'; p += l; alen -= l; } } #endif /* * Canonicalize the pathname. In particular, this strips duplicate * '/' characters, '.' elements, and trailing '/'. It also raises an * error for an empty path, a trailing '..', (if _SECURE_NODOTDOT is * set) any '..' in the path or (if ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS * is set) if the path is absolute. */ static int cleanup_pathname_fsobj(char *path, int *a_eno, struct archive_string *a_estr, int flags) { char *dest, *src; char separator = '\0'; dest = src = path; if (*src == '\0') { fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, "Invalid empty ", "pathname"); return (ARCHIVE_FAILED); } #if defined(__CYGWIN__) cleanup_pathname_win(path); #endif /* Skip leading '/'. */ if (*src == '/') { if (flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) { fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, "Path is ", "absolute"); return (ARCHIVE_FAILED); } separator = *src++; } /* Scan the pathname one element at a time. */ for (;;) { /* src points to first char after '/' */ if (src[0] == '\0') { break; } else if (src[0] == '/') { /* Found '//', ignore second one. */ src++; continue; } else if (src[0] == '.') { if (src[1] == '\0') { /* Ignore trailing '.' */ break; } else if (src[1] == '/') { /* Skip './'. */ src += 2; continue; } else if (src[1] == '.') { if (src[2] == '/' || src[2] == '\0') { /* Conditionally warn about '..' */ if (flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC, "Path contains ", "'..'"); return (ARCHIVE_FAILED); } } /* * Note: Under no circumstances do we * remove '..' elements. In * particular, restoring * '/foo/../bar/' should create the * 'foo' dir as a side-effect. */ } } /* Copy current element, including leading '/'. */ if (separator) *dest++ = '/'; while (*src != '\0' && *src != '/') { *dest++ = *src++; } if (*src == '\0') break; /* Skip '/' separator. */ separator = *src++; } /* * We've just copied zero or more path elements, not including the * final '/'. */ if (dest == path) { /* * Nothing got copied. The path must have been something * like '.' or '/' or './' or '/././././/./'. */ if (separator) *dest++ = '/'; else *dest++ = '.'; } /* Terminate the result. */ *dest = '\0'; return (ARCHIVE_OK); } static int cleanup_pathname(struct archive_write_disk *a) { struct archive_string error_string; int error_number; int rc; archive_string_init(&error_string); rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string, a->flags); if (rc != ARCHIVE_OK) { archive_set_error(&a->archive, error_number, "%s", error_string.s); } archive_string_free(&error_string); return rc; } /* * Create the parent directory of the specified path, assuming path * is already in mutable storage. */ static int create_parent_dir(struct archive_write_disk *a, char *path) { char *slash; int r; /* Remove tail element to obtain parent name. */ slash = strrchr(path, '/'); if (slash == NULL) return (ARCHIVE_OK); *slash = '\0'; r = create_dir(a, path); *slash = '/'; return (r); } /* * Create the specified dir, recursing to create parents as necessary. * * Returns ARCHIVE_OK if the path exists when we're done here. * Otherwise, returns ARCHIVE_FAILED. * Assumes path is in mutable storage; path is unchanged on exit. */ static int create_dir(struct archive_write_disk *a, char *path) { struct stat st; struct fixup_entry *le; char *slash, *base; mode_t mode_final, mode; int r; /* Check for special names and just skip them. */ slash = strrchr(path, '/'); if (slash == NULL) base = path; else base = slash + 1; if (base[0] == '\0' || (base[0] == '.' && base[1] == '\0') || (base[0] == '.' && base[1] == '.' && base[2] == '\0')) { /* Don't bother trying to create null path, '.', or '..'. */ if (slash != NULL) { *slash = '\0'; r = create_dir(a, path); *slash = '/'; return (r); } return (ARCHIVE_OK); } /* * Yes, this should be stat() and not lstat(). Using lstat() * here loses the ability to extract through symlinks. Also note * that this should not use the a->st cache. */ if (stat(path, &st) == 0) { if (S_ISDIR(st.st_mode)) return (ARCHIVE_OK); if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { archive_set_error(&a->archive, EEXIST, "Can't create directory '%s'", path); return (ARCHIVE_FAILED); } if (unlink(path) != 0) { archive_set_error(&a->archive, errno, "Can't create directory '%s': " "Conflicting file cannot be removed", path); return (ARCHIVE_FAILED); } } else if (errno != ENOENT && errno != ENOTDIR) { /* Stat failed? */ archive_set_error(&a->archive, errno, "Can't test directory '%s'", path); return (ARCHIVE_FAILED); } else if (slash != NULL) { *slash = '\0'; r = create_dir(a, path); *slash = '/'; if (r != ARCHIVE_OK) return (r); } /* * Mode we want for the final restored directory. Per POSIX, * implicitly-created dirs must be created obeying the umask. * There's no mention whether this is different for privileged * restores (which the rest of this code handles by pretending * umask=0). I've chosen here to always obey the user's umask for * implicit dirs, even if _EXTRACT_PERM was specified. */ mode_final = DEFAULT_DIR_MODE & ~a->user_umask; /* Mode we want on disk during the restore process. */ mode = mode_final; mode |= MINIMUM_DIR_MODE; mode &= MAXIMUM_DIR_MODE; if (mkdir(path, mode) == 0) { if (mode != mode_final) { le = new_fixup(a, path); if (le == NULL) return (ARCHIVE_FATAL); le->fixup |=TODO_MODE_BASE; le->mode = mode_final; } return (ARCHIVE_OK); } /* * Without the following check, a/b/../b/c/d fails at the * second visit to 'b', so 'd' can't be created. Note that we * don't add it to the fixup list here, as it's already been * added. */ if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Failed to create dir '%s'", path); return (ARCHIVE_FAILED); } /* * Note: Although we can skip setting the user id if the desired user * id matches the current user, we cannot skip setting the group, as * many systems set the gid based on the containing directory. So * we have to perform a chown syscall if we want to set the SGID * bit. (The alternative is to stat() and then possibly chown(); it's * more efficient to skip the stat() and just always chown().) Note * that a successful chown() here clears the TODO_SGID_CHECK bit, which * allows set_mode to skip the stat() check for the GID. */ static int set_ownership(struct archive_write_disk *a) { #ifndef __CYGWIN__ /* unfortunately, on win32 there is no 'root' user with uid 0, so we just have to try the chown and see if it works */ /* If we know we can't change it, don't bother trying. */ if (a->user_uid != 0 && a->user_uid != a->uid) { archive_set_error(&a->archive, errno, "Can't set UID=%jd", (intmax_t)a->uid); return (ARCHIVE_WARN); } #endif #ifdef HAVE_FCHOWN /* If we have an fd, we can avoid a race. */ if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0) { /* We've set owner and know uid/gid are correct. */ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK); return (ARCHIVE_OK); } #endif /* We prefer lchown() but will use chown() if that's all we have. */ /* Of course, if we have neither, this will always fail. */ #ifdef HAVE_LCHOWN if (lchown(a->name, a->uid, a->gid) == 0) { /* We've set owner and know uid/gid are correct. */ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK); return (ARCHIVE_OK); } #elif HAVE_CHOWN if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0) { /* We've set owner and know uid/gid are correct. */ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK); return (ARCHIVE_OK); } #endif archive_set_error(&a->archive, errno, "Can't set user=%jd/group=%jd for %s", (intmax_t)a->uid, (intmax_t)a->gid, a->name); return (ARCHIVE_WARN); } /* * Note: Returns 0 on success, non-zero on failure. */ static int set_time(int fd, int mode, const char *name, time_t atime, long atime_nsec, time_t mtime, long mtime_nsec) { /* Select the best implementation for this platform. */ #if defined(HAVE_UTIMENSAT) && defined(HAVE_FUTIMENS) /* * utimensat() and futimens() are defined in * POSIX.1-2008. They support ns resolution and setting times * on fds and symlinks. */ struct timespec ts[2]; (void)mode; /* UNUSED */ ts[0].tv_sec = atime; ts[0].tv_nsec = atime_nsec; ts[1].tv_sec = mtime; ts[1].tv_nsec = mtime_nsec; if (fd >= 0) return futimens(fd, ts); return utimensat(AT_FDCWD, name, ts, AT_SYMLINK_NOFOLLOW); #elif HAVE_UTIMES /* * The utimes()-family functions support µs-resolution and * setting times fds and symlinks. utimes() is documented as * LEGACY by POSIX, futimes() and lutimes() are not described * in POSIX. */ struct timeval times[2]; times[0].tv_sec = atime; times[0].tv_usec = atime_nsec / 1000; times[1].tv_sec = mtime; times[1].tv_usec = mtime_nsec / 1000; #ifdef HAVE_FUTIMES if (fd >= 0) return (futimes(fd, times)); #else (void)fd; /* UNUSED */ #endif #ifdef HAVE_LUTIMES (void)mode; /* UNUSED */ return (lutimes(name, times)); #else if (S_ISLNK(mode)) return (0); return (utimes(name, times)); #endif #elif defined(HAVE_UTIME) /* * utime() is POSIX-standard but only supports 1s resolution and * does not support fds or symlinks. */ struct utimbuf times; (void)fd; /* UNUSED */ (void)name; /* UNUSED */ (void)atime_nsec; /* UNUSED */ (void)mtime_nsec; /* UNUSED */ times.actime = atime; times.modtime = mtime; if (S_ISLNK(mode)) return (ARCHIVE_OK); return (utime(name, ×)); #else /* * We don't know how to set the time on this platform. */ (void)fd; /* UNUSED */ (void)mode; /* UNUSED */ (void)name; /* UNUSED */ (void)atime_nsec; /* UNUSED */ (void)mtime_nsec; /* UNUSED */ return (ARCHIVE_WARN); #endif } #ifdef F_SETTIMES static int set_time_tru64(int fd, int mode, const char *name, time_t atime, long atime_nsec, time_t mtime, long mtime_nsec, time_t ctime, long ctime_nsec) { struct attr_timbuf tstamp; tstamp.atime.tv_sec = atime; tstamp.mtime.tv_sec = mtime; tstamp.ctime.tv_sec = ctime; #if defined (__hpux) && defined (__ia64) tstamp.atime.tv_nsec = atime_nsec; tstamp.mtime.tv_nsec = mtime_nsec; tstamp.ctime.tv_nsec = ctime_nsec; #else tstamp.atime.tv_usec = atime_nsec / 1000; tstamp.mtime.tv_usec = mtime_nsec / 1000; tstamp.ctime.tv_usec = ctime_nsec / 1000; #endif return (fcntl(fd,F_SETTIMES,&tstamp)); } #endif /* F_SETTIMES */ static int set_times(struct archive_write_disk *a, int fd, int mode, const char *name, time_t atime, long atime_nanos, time_t birthtime, long birthtime_nanos, time_t mtime, long mtime_nanos, time_t cctime, long ctime_nanos) { /* Note: set_time doesn't use libarchive return conventions! * It uses syscall conventions. So 0 here instead of ARCHIVE_OK. */ int r1 = 0, r2 = 0; #ifdef F_SETTIMES /* * on Tru64 try own fcntl first which can restore even the * ctime, fall back to default code path below if it fails * or if we are not running as root */ if (a->user_uid == 0 && set_time_tru64(fd, mode, name, atime, atime_nanos, mtime, mtime_nanos, cctime, ctime_nanos) == 0) { return (ARCHIVE_OK); } #else /* Tru64 */ (void)cctime; /* UNUSED */ (void)ctime_nanos; /* UNUSED */ #endif /* Tru64 */ #ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME /* * If you have struct stat.st_birthtime, we assume BSD * birthtime semantics, in which {f,l,}utimes() updates * birthtime to earliest mtime. So we set the time twice, * first using the birthtime, then using the mtime. If * birthtime == mtime, this isn't necessary, so we skip it. * If birthtime > mtime, then this won't work, so we skip it. */ if (birthtime < mtime || (birthtime == mtime && birthtime_nanos < mtime_nanos)) r1 = set_time(fd, mode, name, atime, atime_nanos, birthtime, birthtime_nanos); #else (void)birthtime; /* UNUSED */ (void)birthtime_nanos; /* UNUSED */ #endif r2 = set_time(fd, mode, name, atime, atime_nanos, mtime, mtime_nanos); if (r1 != 0 || r2 != 0) { archive_set_error(&a->archive, errno, "Can't restore time"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int set_times_from_entry(struct archive_write_disk *a) { time_t atime, birthtime, mtime, cctime; long atime_nsec, birthtime_nsec, mtime_nsec, ctime_nsec; /* Suitable defaults. */ atime = birthtime = mtime = cctime = a->start_time; atime_nsec = birthtime_nsec = mtime_nsec = ctime_nsec = 0; /* If no time was provided, we're done. */ if (!archive_entry_atime_is_set(a->entry) #if HAVE_STRUCT_STAT_ST_BIRTHTIME && !archive_entry_birthtime_is_set(a->entry) #endif && !archive_entry_mtime_is_set(a->entry)) return (ARCHIVE_OK); if (archive_entry_atime_is_set(a->entry)) { atime = archive_entry_atime(a->entry); atime_nsec = archive_entry_atime_nsec(a->entry); } if (archive_entry_birthtime_is_set(a->entry)) { birthtime = archive_entry_birthtime(a->entry); birthtime_nsec = archive_entry_birthtime_nsec(a->entry); } if (archive_entry_mtime_is_set(a->entry)) { mtime = archive_entry_mtime(a->entry); mtime_nsec = archive_entry_mtime_nsec(a->entry); } if (archive_entry_ctime_is_set(a->entry)) { cctime = archive_entry_ctime(a->entry); ctime_nsec = archive_entry_ctime_nsec(a->entry); } return set_times(a, a->fd, a->mode, a->name, atime, atime_nsec, birthtime, birthtime_nsec, mtime, mtime_nsec, cctime, ctime_nsec); } static int set_mode(struct archive_write_disk *a, int mode) { int r = ARCHIVE_OK; mode &= 07777; /* Strip off file type bits. */ if (a->todo & TODO_SGID_CHECK) { /* * If we don't know the GID is right, we must stat() * to verify it. We can't just check the GID of this * process, since systems sometimes set GID from * the enclosing dir or based on ACLs. */ if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); if (a->pst->st_gid != a->gid) { mode &= ~ S_ISGID; if (a->flags & ARCHIVE_EXTRACT_OWNER) { /* * This is only an error if you * requested owner restore. If you * didn't, we'll try to restore * sgid/suid, but won't consider it a * problem if we can't. */ archive_set_error(&a->archive, -1, "Can't restore SGID bit"); r = ARCHIVE_WARN; } } /* While we're here, double-check the UID. */ if (a->pst->st_uid != a->uid && (a->todo & TODO_SUID)) { mode &= ~ S_ISUID; if (a->flags & ARCHIVE_EXTRACT_OWNER) { archive_set_error(&a->archive, -1, "Can't restore SUID bit"); r = ARCHIVE_WARN; } } a->todo &= ~TODO_SGID_CHECK; a->todo &= ~TODO_SUID_CHECK; } else if (a->todo & TODO_SUID_CHECK) { /* * If we don't know the UID is right, we can just check * the user, since all systems set the file UID from * the process UID. */ if (a->user_uid != a->uid) { mode &= ~ S_ISUID; if (a->flags & ARCHIVE_EXTRACT_OWNER) { archive_set_error(&a->archive, -1, "Can't make file SUID"); r = ARCHIVE_WARN; } } a->todo &= ~TODO_SUID_CHECK; } if (S_ISLNK(a->mode)) { #ifdef HAVE_LCHMOD /* * If this is a symlink, use lchmod(). If the * platform doesn't support lchmod(), just skip it. A * platform that doesn't provide a way to set * permissions on symlinks probably ignores * permissions on symlinks, so a failure here has no * impact. */ if (lchmod(a->name, mode) != 0) { switch (errno) { case ENOTSUP: case ENOSYS: #if ENOTSUP != EOPNOTSUPP case EOPNOTSUPP: #endif /* * if lchmod is defined but the platform * doesn't support it, silently ignore * error */ break; default: archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; } } #endif } else if (!S_ISDIR(a->mode)) { /* * If it's not a symlink and not a dir, then use * fchmod() or chmod(), depending on whether we have * an fd. Dirs get their perms set during the * post-extract fixup, which is handled elsewhere. */ #ifdef HAVE_FCHMOD if (a->fd >= 0) { if (fchmod(a->fd, mode) != 0) { archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; } } else #endif /* If this platform lacks fchmod(), then * we'll just use chmod(). */ if (chmod(a->name, mode) != 0) { archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; } } return (r); } static int set_fflags(struct archive_write_disk *a) { struct fixup_entry *le; unsigned long set, clear; int r; int critical_flags; mode_t mode = archive_entry_mode(a->entry); /* * Make 'critical_flags' hold all file flags that can't be * immediately restored. For example, on BSD systems, * SF_IMMUTABLE prevents hardlinks from being created, so * should not be set until after any hardlinks are created. To * preserve some semblance of portability, this uses #ifdef * extensively. Ugly, but it works. * * Yes, Virginia, this does create a security race. It's mitigated * somewhat by the practice of creating dirs 0700 until the extract * is done, but it would be nice if we could do more than that. * People restoring critical file systems should be wary of * other programs that might try to muck with files as they're * being restored. */ /* Hopefully, the compiler will optimize this mess into a constant. */ critical_flags = 0; #ifdef SF_IMMUTABLE critical_flags |= SF_IMMUTABLE; #endif #ifdef UF_IMMUTABLE critical_flags |= UF_IMMUTABLE; #endif #ifdef SF_APPEND critical_flags |= SF_APPEND; #endif #ifdef UF_APPEND critical_flags |= UF_APPEND; #endif #if defined(FS_APPEND_FL) critical_flags |= FS_APPEND_FL; #elif defined(EXT2_APPEND_FL) critical_flags |= EXT2_APPEND_FL; #endif #if defined(FS_IMMUTABLE_FL) critical_flags |= FS_IMMUTABLE_FL; #elif defined(EXT2_IMMUTABLE_FL) critical_flags |= EXT2_IMMUTABLE_FL; #endif #ifdef FS_JOURNAL_DATA_FL critical_flags |= FS_JOURNAL_DATA_FL; #endif if (a->todo & TODO_FFLAGS) { archive_entry_fflags(a->entry, &set, &clear); /* * The first test encourages the compiler to eliminate * all of this if it's not necessary. */ if ((critical_flags != 0) && (set & critical_flags)) { le = current_fixup(a, a->name); if (le == NULL) return (ARCHIVE_FATAL); le->fixup |= TODO_FFLAGS; le->fflags_set = set; /* Store the mode if it's not already there. */ if ((le->fixup & TODO_MODE) == 0) le->mode = mode; } else { r = set_fflags_platform(a, a->fd, a->name, mode, set, clear); if (r != ARCHIVE_OK) return (r); } } return (ARCHIVE_OK); } static int clear_nochange_fflags(struct archive_write_disk *a) { int nochange_flags; mode_t mode = archive_entry_mode(a->entry); /* Hopefully, the compiler will optimize this mess into a constant. */ nochange_flags = 0; #ifdef SF_IMMUTABLE nochange_flags |= SF_IMMUTABLE; #endif #ifdef UF_IMMUTABLE nochange_flags |= UF_IMMUTABLE; #endif #ifdef SF_APPEND nochange_flags |= SF_APPEND; #endif #ifdef UF_APPEND nochange_flags |= UF_APPEND; #endif #ifdef EXT2_APPEND_FL nochange_flags |= EXT2_APPEND_FL; #endif #ifdef EXT2_IMMUTABLE_FL nochange_flags |= EXT2_IMMUTABLE_FL; #endif return (set_fflags_platform(a, a->fd, a->name, mode, 0, nochange_flags)); } #if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && defined(HAVE_STRUCT_STAT_ST_FLAGS) /* * BSD reads flags using stat() and sets them with one of {f,l,}chflags() */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { int r; (void)mode; /* UNUSED */ if (set == 0 && clear == 0) return (ARCHIVE_OK); /* * XXX Is the stat here really necessary? Or can I just use * the 'set' flags directly? In particular, I'm not sure * about the correct approach if we're overwriting an existing * file that already has flags on it. XXX */ if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); a->st.st_flags &= ~clear; a->st.st_flags |= set; #ifdef HAVE_FCHFLAGS /* If platform has fchflags() and we were given an fd, use it. */ if (fd >= 0 && fchflags(fd, a->st.st_flags) == 0) return (ARCHIVE_OK); #endif /* * If we can't use the fd to set the flags, we'll use the * pathname to set flags. We prefer lchflags() but will use * chflags() if we must. */ #ifdef HAVE_LCHFLAGS if (lchflags(name, a->st.st_flags) == 0) return (ARCHIVE_OK); #elif defined(HAVE_CHFLAGS) if (S_ISLNK(a->st.st_mode)) { archive_set_error(&a->archive, errno, "Can't set file flags on symlink."); return (ARCHIVE_WARN); } if (chflags(name, a->st.st_flags) == 0) return (ARCHIVE_OK); #endif archive_set_error(&a->archive, errno, "Failed to set file flags"); return (ARCHIVE_WARN); } #elif (defined(FS_IOC_GETFLAGS) && defined(FS_IOC_SETFLAGS) && \ defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) && \ defined(HAVE_WORKING_EXT2_IOC_GETFLAGS)) /* * Linux uses ioctl() to read and write file flags. */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { int ret; int myfd = fd; int newflags, oldflags; int sf_mask = 0; if (set == 0 && clear == 0) return (ARCHIVE_OK); /* Only regular files and dirs can have flags. */ if (!S_ISREG(mode) && !S_ISDIR(mode)) return (ARCHIVE_OK); /* If we weren't given an fd, open it ourselves. */ if (myfd < 0) { myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(myfd); } if (myfd < 0) return (ARCHIVE_OK); /* * Linux has no define for the flags that are only settable by * the root user. This code may seem a little complex, but * there seem to be some Linux systems that lack these * defines. (?) The code below degrades reasonably gracefully * if sf_mask is incomplete. */ #if defined(FS_IMMUTABLE_FL) sf_mask |= FS_IMMUTABLE_FL; #elif defined(EXT2_IMMUTABLE_FL) sf_mask |= EXT2_IMMUTABLE_FL; #endif #if defined(FS_APPEND_FL) sf_mask |= FS_APPEND_FL; #elif defined(EXT2_APPEND_FL) sf_mask |= EXT2_APPEND_FL; #endif #if defined(FS_JOURNAL_DATA_FL) sf_mask |= FS_JOURNAL_DATA_FL; #endif /* * XXX As above, this would be way simpler if we didn't have * to read the current flags from disk. XXX */ ret = ARCHIVE_OK; /* Read the current file flags. */ if (ioctl(myfd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &oldflags) < 0) goto fail; /* Try setting the flags as given. */ newflags = (oldflags & ~clear) | set; if (ioctl(myfd, #ifdef FS_IOC_SETFLAGS FS_IOC_SETFLAGS, #else EXT2_IOC_SETFLAGS, #endif &newflags) >= 0) goto cleanup; if (errno != EPERM) goto fail; /* If we couldn't set all the flags, try again with a subset. */ newflags &= ~sf_mask; oldflags &= sf_mask; newflags |= oldflags; if (ioctl(myfd, #ifdef FS_IOC_SETFLAGS FS_IOC_SETFLAGS, #else EXT2_IOC_SETFLAGS, #endif &newflags) >= 0) goto cleanup; /* We couldn't set the flags, so report the failure. */ fail: archive_set_error(&a->archive, errno, "Failed to set file flags"); ret = ARCHIVE_WARN; cleanup: if (fd < 0) close(myfd); return (ret); } #else /* * Of course, some systems have neither BSD chflags() nor Linux' flags * support through ioctl(). */ static int set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, mode_t mode, unsigned long set, unsigned long clear) { (void)a; /* UNUSED */ (void)fd; /* UNUSED */ (void)name; /* UNUSED */ (void)mode; /* UNUSED */ (void)set; /* UNUSED */ (void)clear; /* UNUSED */ return (ARCHIVE_OK); } #endif /* __linux */ #ifndef HAVE_COPYFILE_H /* Default is to simply drop Mac extended metadata. */ static int set_mac_metadata(struct archive_write_disk *a, const char *pathname, const void *metadata, size_t metadata_size) { (void)a; /* UNUSED */ (void)pathname; /* UNUSED */ (void)metadata; /* UNUSED */ (void)metadata_size; /* UNUSED */ return (ARCHIVE_OK); } static int fixup_appledouble(struct archive_write_disk *a, const char *pathname) { (void)a; /* UNUSED */ (void)pathname; /* UNUSED */ return (ARCHIVE_OK); } #else /* * On Mac OS, we use copyfile() to unpack the metadata and * apply it to the target file. */ #if defined(HAVE_SYS_XATTR_H) static int copy_xattrs(struct archive_write_disk *a, int tmpfd, int dffd) { ssize_t xattr_size; char *xattr_names = NULL, *xattr_val = NULL; int ret = ARCHIVE_OK, xattr_i; xattr_size = flistxattr(tmpfd, NULL, 0, 0); if (xattr_size == -1) { archive_set_error(&a->archive, errno, "Failed to read metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } xattr_names = malloc(xattr_size); if (xattr_names == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for metadata(xattr)"); ret = ARCHIVE_FATAL; goto exit_xattr; } xattr_size = flistxattr(tmpfd, xattr_names, xattr_size, 0); if (xattr_size == -1) { archive_set_error(&a->archive, errno, "Failed to read metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } for (xattr_i = 0; xattr_i < xattr_size; xattr_i += strlen(xattr_names + xattr_i) + 1) { char *xattr_val_saved; ssize_t s; int f; s = fgetxattr(tmpfd, xattr_names + xattr_i, NULL, 0, 0, 0); if (s == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } xattr_val_saved = xattr_val; xattr_val = realloc(xattr_val, s); if (xattr_val == NULL) { archive_set_error(&a->archive, ENOMEM, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; free(xattr_val_saved); goto exit_xattr; } s = fgetxattr(tmpfd, xattr_names + xattr_i, xattr_val, s, 0, 0); if (s == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } f = fsetxattr(dffd, xattr_names + xattr_i, xattr_val, s, 0, 0); if (f == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(xattr)"); ret = ARCHIVE_WARN; goto exit_xattr; } } exit_xattr: free(xattr_names); free(xattr_val); return (ret); } #endif static int copy_acls(struct archive_write_disk *a, int tmpfd, int dffd) { #ifndef HAVE_SYS_ACL_H return 0; #else acl_t acl, dfacl = NULL; int acl_r, ret = ARCHIVE_OK; acl = acl_get_fd(tmpfd); if (acl == NULL) { if (errno == ENOENT) /* There are not any ACLs. */ return (ret); archive_set_error(&a->archive, errno, "Failed to get metadata(acl)"); ret = ARCHIVE_WARN; goto exit_acl; } dfacl = acl_dup(acl); acl_r = acl_set_fd(dffd, dfacl); if (acl_r == -1) { archive_set_error(&a->archive, errno, "Failed to get metadata(acl)"); ret = ARCHIVE_WARN; goto exit_acl; } exit_acl: if (acl) acl_free(acl); if (dfacl) acl_free(dfacl); return (ret); #endif } static int create_tempdatafork(struct archive_write_disk *a, const char *pathname) { struct archive_string tmpdatafork; int tmpfd; archive_string_init(&tmpdatafork); archive_strcpy(&tmpdatafork, "tar.md.XXXXXX"); tmpfd = mkstemp(tmpdatafork.s); if (tmpfd < 0) { archive_set_error(&a->archive, errno, "Failed to mkstemp"); archive_string_free(&tmpdatafork); return (-1); } if (copyfile(pathname, tmpdatafork.s, 0, COPYFILE_UNPACK | COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR) < 0) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); close(tmpfd); tmpfd = -1; } unlink(tmpdatafork.s); archive_string_free(&tmpdatafork); return (tmpfd); } static int copy_metadata(struct archive_write_disk *a, const char *metadata, const char *datafork, int datafork_compressed) { int ret = ARCHIVE_OK; if (datafork_compressed) { int dffd, tmpfd; tmpfd = create_tempdatafork(a, metadata); if (tmpfd == -1) return (ARCHIVE_WARN); /* * Do not open the data fork compressed by HFS+ compression * with at least a writing mode(O_RDWR or O_WRONLY). it * makes the data fork uncompressed. */ dffd = open(datafork, 0); if (dffd == -1) { archive_set_error(&a->archive, errno, "Failed to open the data fork for metadata"); close(tmpfd); return (ARCHIVE_WARN); } #if defined(HAVE_SYS_XATTR_H) ret = copy_xattrs(a, tmpfd, dffd); if (ret == ARCHIVE_OK) #endif ret = copy_acls(a, tmpfd, dffd); close(tmpfd); close(dffd); } else { if (copyfile(metadata, datafork, 0, COPYFILE_UNPACK | COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR) < 0) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); ret = ARCHIVE_WARN; } } return (ret); } static int set_mac_metadata(struct archive_write_disk *a, const char *pathname, const void *metadata, size_t metadata_size) { struct archive_string tmp; ssize_t written; int fd; int ret = ARCHIVE_OK; /* This would be simpler if copyfile() could just accept the * metadata as a block of memory; then we could sidestep this * silly dance of writing the data to disk just so that * copyfile() can read it back in again. */ archive_string_init(&tmp); archive_strcpy(&tmp, pathname); archive_strcat(&tmp, ".XXXXXX"); fd = mkstemp(tmp.s); if (fd < 0) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); archive_string_free(&tmp); return (ARCHIVE_WARN); } written = write(fd, metadata, metadata_size); close(fd); if ((size_t)written != metadata_size) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); ret = ARCHIVE_WARN; } else { int compressed; #if defined(UF_COMPRESSED) if ((a->todo & TODO_HFS_COMPRESSION) != 0 && (ret = lazy_stat(a)) == ARCHIVE_OK) compressed = a->st.st_flags & UF_COMPRESSED; else #endif compressed = 0; ret = copy_metadata(a, tmp.s, pathname, compressed); } unlink(tmp.s); archive_string_free(&tmp); return (ret); } static int fixup_appledouble(struct archive_write_disk *a, const char *pathname) { char buff[8]; struct stat st; const char *p; struct archive_string datafork; int fd = -1, ret = ARCHIVE_OK; archive_string_init(&datafork); /* Check if the current file name is a type of the resource * fork file. */ p = strrchr(pathname, '/'); if (p == NULL) p = pathname; else p++; if (p[0] != '.' || p[1] != '_') goto skip_appledouble; /* * Check if the data fork file exists. * * TODO: Check if this write disk object has handled it. */ archive_strncpy(&datafork, pathname, p - pathname); archive_strcat(&datafork, p + 2); if (lstat(datafork.s, &st) == -1 || (st.st_mode & AE_IFMT) != AE_IFREG) goto skip_appledouble; /* * Check if the file is in the AppleDouble form. */ fd = open(pathname, O_RDONLY | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); if (fd == -1) { archive_set_error(&a->archive, errno, "Failed to open a restoring file"); ret = ARCHIVE_WARN; goto skip_appledouble; } if (read(fd, buff, 8) == -1) { archive_set_error(&a->archive, errno, "Failed to read a restoring file"); close(fd); ret = ARCHIVE_WARN; goto skip_appledouble; } close(fd); /* Check AppleDouble Magic Code. */ if (archive_be32dec(buff) != 0x00051607) goto skip_appledouble; /* Check AppleDouble Version. */ if (archive_be32dec(buff+4) != 0x00020000) goto skip_appledouble; ret = copy_metadata(a, pathname, datafork.s, #if defined(UF_COMPRESSED) st.st_flags & UF_COMPRESSED); #else 0); #endif if (ret == ARCHIVE_OK) { unlink(pathname); ret = ARCHIVE_EOF; } skip_appledouble: archive_string_free(&datafork); return (ret); } #endif #if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX /* * Restore extended attributes - Linux, Darwin and AIX implementations: * AIX' ea interface is syntaxwise identical to the Linux xattr interface. */ static int set_xattrs(struct archive_write_disk *a) { struct archive_entry *entry = a->entry; static int warning_done = 0; int ret = ARCHIVE_OK; int i = archive_entry_xattr_reset(entry); while (i--) { const char *name; const void *value; size_t size; archive_entry_xattr_next(entry, &name, &value, &size); if (name != NULL && strncmp(name, "xfsroot.", 8) != 0 && strncmp(name, "system.", 7) != 0) { int e; if (a->fd >= 0) { #if ARCHIVE_XATTR_LINUX e = fsetxattr(a->fd, name, value, size, 0); #elif ARCHIVE_XATTR_DARWIN e = fsetxattr(a->fd, name, value, size, 0, 0); #elif ARCHIVE_XATTR_AIX e = fsetea(a->fd, name, value, size, 0); #endif } else { #if ARCHIVE_XATTR_LINUX e = lsetxattr(archive_entry_pathname(entry), name, value, size, 0); #elif ARCHIVE_XATTR_DARWIN e = setxattr(archive_entry_pathname(entry), name, value, size, 0, XATTR_NOFOLLOW); #elif ARCHIVE_XATTR_AIX e = lsetea(archive_entry_pathname(entry), name, value, size, 0); #endif } if (e == -1) { if (errno == ENOTSUP || errno == ENOSYS) { if (!warning_done) { warning_done = 1; archive_set_error(&a->archive, errno, "Cannot restore extended " "attributes on this file " "system"); } } else archive_set_error(&a->archive, errno, "Failed to set extended attribute"); ret = ARCHIVE_WARN; } } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid extended attribute encountered"); ret = ARCHIVE_WARN; } } return (ret); } #elif ARCHIVE_XATTR_FREEBSD /* * Restore extended attributes - FreeBSD implementation */ static int set_xattrs(struct archive_write_disk *a) { struct archive_entry *entry = a->entry; static int warning_done = 0; int ret = ARCHIVE_OK; int i = archive_entry_xattr_reset(entry); while (i--) { const char *name; const void *value; size_t size; archive_entry_xattr_next(entry, &name, &value, &size); if (name != NULL) { ssize_t e; int namespace; if (strncmp(name, "user.", 5) == 0) { /* "user." attributes go to user namespace */ name += 5; namespace = EXTATTR_NAMESPACE_USER; } else { /* Warn about other extended attributes. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't restore extended attribute ``%s''", name); ret = ARCHIVE_WARN; continue; } errno = 0; if (a->fd >= 0) { e = extattr_set_fd(a->fd, namespace, name, value, size); } else { e = extattr_set_link( archive_entry_pathname(entry), namespace, name, value, size); } if (e != (ssize_t)size) { if (errno == ENOTSUP || errno == ENOSYS) { if (!warning_done) { warning_done = 1; archive_set_error(&a->archive, errno, "Cannot restore extended " "attributes on this file " "system"); } } else { archive_set_error(&a->archive, errno, "Failed to set extended attribute"); } ret = ARCHIVE_WARN; } } } return (ret); } #else /* * Restore extended attributes - stub implementation for unsupported systems */ static int set_xattrs(struct archive_write_disk *a) { static int warning_done = 0; /* If there aren't any extended attributes, then it's okay not * to extract them, otherwise, issue a single warning. */ if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) { warning_done = 1; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Cannot restore extended attributes on this system"); return (ARCHIVE_WARN); } /* Warning was already emitted; suppress further warnings. */ return (ARCHIVE_OK); } #endif /* * Test if file on disk is older than entry. */ static int older(struct stat *st, struct archive_entry *entry) { /* First, test the seconds and return if we have a definite answer. */ /* Definitely older. */ if (to_int64_time(st->st_mtime) < to_int64_time(archive_entry_mtime(entry))) return (1); /* Definitely younger. */ if (to_int64_time(st->st_mtime) > to_int64_time(archive_entry_mtime(entry))) return (0); /* If this platform supports fractional seconds, try those. */ #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC /* Definitely older. */ if (st->st_mtimespec.tv_nsec < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC /* Definitely older. */ if (st->st_mtim.tv_nsec < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_MTIME_N /* older. */ if (st->st_mtime_n < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_UMTIME /* older. */ if (st->st_umtime * 1000 < archive_entry_mtime_nsec(entry)) return (1); #elif HAVE_STRUCT_STAT_ST_MTIME_USEC /* older. */ if (st->st_mtime_usec * 1000 < archive_entry_mtime_nsec(entry)) return (1); #else /* This system doesn't have high-res timestamps. */ #endif /* Same age or newer, so not older. */ return (0); } #ifndef ARCHIVE_ACL_SUPPORT int archive_write_disk_set_acls(struct archive *a, int fd, const char *name, struct archive_acl *abstract_acl, __LA_MODE_T mode) { (void)a; /* UNUSED */ (void)fd; /* UNUSED */ (void)name; /* UNUSED */ (void)abstract_acl; /* UNUSED */ (void)mode; /* UNUSED */ return (ARCHIVE_OK); } #endif #endif /* !_WIN32 || __CYGWIN__ */ Index: stable/10/contrib/libarchive/libarchive/archive_write_disk_set_standard_lookup.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_disk_set_standard_lookup.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_write_disk_set_standard_lookup.c (revision 344674) @@ -1,265 +1,263 @@ /*- * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_GRP_H #include #endif #ifdef HAVE_PWD_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_write_disk_private.h" struct bucket { char *name; int hash; id_t id; }; static const size_t cache_size = 127; static unsigned int hash(const char *); static int64_t lookup_gid(void *, const char *uname, int64_t); static int64_t lookup_uid(void *, const char *uname, int64_t); static void cleanup(void *); /* * Installs functions that use getpwnam()/getgrnam()---along with * a simple cache to accelerate such lookups---into the archive_write_disk * object. This is in a separate file because getpwnam()/getgrnam() * can pull in a LOT of library code (including NIS/LDAP functions, which * pull in DNS resolvers, etc). This can easily top 500kB, which makes * it inappropriate for some space-constrained applications. * * Applications that are size-sensitive may want to just use the * real default functions (defined in archive_write_disk.c) that just * use the uid/gid without the lookup. Or define your own custom functions * if you prefer. * * TODO: Replace these hash tables with simpler move-to-front LRU * lists with a bounded size (128 items?). The hash is a bit faster, * but has a bad pathology in which it thrashes a single bucket. Even * walking a list of 128 items is a lot faster than calling * getpwnam()! */ int archive_write_disk_set_standard_lookup(struct archive *a) { struct bucket *ucache = calloc(cache_size, sizeof(struct bucket)); struct bucket *gcache = calloc(cache_size, sizeof(struct bucket)); if (ucache == NULL || gcache == NULL) { free(ucache); free(gcache); return (ARCHIVE_FATAL); } archive_write_disk_set_group_lookup(a, gcache, lookup_gid, cleanup); archive_write_disk_set_user_lookup(a, ucache, lookup_uid, cleanup); return (ARCHIVE_OK); } static int64_t lookup_gid(void *private_data, const char *gname, int64_t gid) { int h; struct bucket *b; struct bucket *gcache = (struct bucket *)private_data; /* If no gname, just use the gid provided. */ if (gname == NULL || *gname == '\0') return (gid); /* Try to find gname in the cache. */ h = hash(gname); b = &gcache[h % cache_size ]; if (b->name != NULL && b->hash == h && strcmp(gname, b->name) == 0) return ((gid_t)b->id); /* Free the cache slot for a new entry. */ - if (b->name != NULL) - free(b->name); + free(b->name); b->name = strdup(gname); /* Note: If strdup fails, that's okay; we just won't cache. */ b->hash = h; #if HAVE_GRP_H # if HAVE_GETGRNAM_R { char _buffer[128]; size_t bufsize = 128; char *buffer = _buffer; char *allocated = NULL; struct group grent, *result; int r; for (;;) { result = &grent; /* Old getgrnam_r ignores last arg. */ r = getgrnam_r(gname, &grent, buffer, bufsize, &result); if (r == 0) break; if (r != ERANGE) break; bufsize *= 2; free(allocated); allocated = malloc(bufsize); if (allocated == NULL) break; buffer = allocated; } if (result != NULL) gid = result->gr_gid; free(allocated); } # else /* HAVE_GETGRNAM_R */ { struct group *result; result = getgrnam(gname); if (result != NULL) gid = result->gr_gid; } # endif /* HAVE_GETGRNAM_R */ #elif defined(_WIN32) && !defined(__CYGWIN__) /* TODO: do a gname->gid lookup for Windows. */ #else #error No way to perform gid lookups on this platform #endif b->id = (gid_t)gid; return (gid); } static int64_t lookup_uid(void *private_data, const char *uname, int64_t uid) { int h; struct bucket *b; struct bucket *ucache = (struct bucket *)private_data; /* If no uname, just use the uid provided. */ if (uname == NULL || *uname == '\0') return (uid); /* Try to find uname in the cache. */ h = hash(uname); b = &ucache[h % cache_size ]; if (b->name != NULL && b->hash == h && strcmp(uname, b->name) == 0) return ((uid_t)b->id); /* Free the cache slot for a new entry. */ - if (b->name != NULL) - free(b->name); + free(b->name); b->name = strdup(uname); /* Note: If strdup fails, that's okay; we just won't cache. */ b->hash = h; #if HAVE_PWD_H # if HAVE_GETPWNAM_R { char _buffer[128]; size_t bufsize = 128; char *buffer = _buffer; char *allocated = NULL; struct passwd pwent, *result; int r; for (;;) { result = &pwent; /* Old getpwnam_r ignores last arg. */ r = getpwnam_r(uname, &pwent, buffer, bufsize, &result); if (r == 0) break; if (r != ERANGE) break; bufsize *= 2; free(allocated); allocated = malloc(bufsize); if (allocated == NULL) break; buffer = allocated; } if (result != NULL) uid = result->pw_uid; free(allocated); } # else /* HAVE_GETPWNAM_R */ { struct passwd *result; result = getpwnam(uname); if (result != NULL) uid = result->pw_uid; } #endif /* HAVE_GETPWNAM_R */ #elif defined(_WIN32) && !defined(__CYGWIN__) /* TODO: do a uname->uid lookup for Windows. */ #else #error No way to look up uids on this platform #endif b->id = (uid_t)uid; return (uid); } static void cleanup(void *private) { size_t i; struct bucket *cache = (struct bucket *)private; for (i = 0; i < cache_size; i++) free(cache[i].name); free(cache); } static unsigned int hash(const char *p) { /* A 32-bit version of Peter Weinberger's (PJW) hash algorithm, as used by ELF for hashing function names. */ unsigned g, h = 0; while (*p != '\0') { h = (h << 4) + *p++; if ((g = h & 0xF0000000) != 0) { h ^= g >> 24; h &= 0x0FFFFFFF; } } return h; } Index: stable/10/contrib/libarchive/libarchive/archive_write_set_format_ar.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_set_format_ar.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_write_set_format_ar.c (revision 344674) @@ -1,564 +1,569 @@ /*- * Copyright (c) 2007 Kai Wang * Copyright (c) 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 * in this position and unchanged. * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_write_private.h" struct ar_w { uint64_t entry_bytes_remaining; uint64_t entry_padding; int is_strtab; int has_strtab; char wrote_global_header; char *strtab; }; /* * Define structure of the "ar" header. */ #define AR_name_offset 0 #define AR_name_size 16 #define AR_date_offset 16 #define AR_date_size 12 #define AR_uid_offset 28 #define AR_uid_size 6 #define AR_gid_offset 34 #define AR_gid_size 6 #define AR_mode_offset 40 #define AR_mode_size 8 #define AR_size_offset 48 #define AR_size_size 10 #define AR_fmag_offset 58 #define AR_fmag_size 2 static int archive_write_set_format_ar(struct archive_write *); static int archive_write_ar_header(struct archive_write *, struct archive_entry *); static ssize_t archive_write_ar_data(struct archive_write *, const void *buff, size_t s); static int archive_write_ar_free(struct archive_write *); static int archive_write_ar_close(struct archive_write *); static int archive_write_ar_finish_entry(struct archive_write *); static const char *ar_basename(const char *path); static int format_octal(int64_t v, char *p, int s); static int format_decimal(int64_t v, char *p, int s); int archive_write_set_format_ar_bsd(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; int r; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_ar_bsd"); r = archive_write_set_format_ar(a); if (r == ARCHIVE_OK) { a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD; a->archive.archive_format_name = "ar (BSD)"; } return (r); } int archive_write_set_format_ar_svr4(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; int r; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_ar_svr4"); r = archive_write_set_format_ar(a); if (r == ARCHIVE_OK) { a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU; a->archive.archive_format_name = "ar (GNU/SVR4)"; } return (r); } /* * Generic initialization. */ static int archive_write_set_format_ar(struct archive_write *a) { struct ar_w *ar; /* If someone else was already registered, unregister them. */ if (a->format_free != NULL) (a->format_free)(a); ar = (struct ar_w *)calloc(1, sizeof(*ar)); if (ar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data"); return (ARCHIVE_FATAL); } a->format_data = ar; a->format_name = "ar"; a->format_write_header = archive_write_ar_header; a->format_write_data = archive_write_ar_data; a->format_close = archive_write_ar_close; a->format_free = archive_write_ar_free; a->format_finish_entry = archive_write_ar_finish_entry; return (ARCHIVE_OK); } static int archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) { int ret, append_fn; char buff[60]; char *ss, *se; struct ar_w *ar; const char *pathname; const char *filename; int64_t size; append_fn = 0; ar = (struct ar_w *)a->format_data; ar->is_strtab = 0; filename = NULL; size = archive_entry_size(entry); /* * Reject files with empty name. */ pathname = archive_entry_pathname(entry); if (pathname == NULL || *pathname == '\0') { archive_set_error(&a->archive, EINVAL, "Invalid filename"); return (ARCHIVE_WARN); } /* * If we are now at the beginning of the archive, * we need first write the ar global header. */ if (!ar->wrote_global_header) { __archive_write_output(a, "!\n", 8); ar->wrote_global_header = 1; } memset(buff, ' ', 60); memcpy(&buff[AR_fmag_offset], "`\n", 2); if (strcmp(pathname, "/") == 0 ) { /* Entry is archive symbol table in GNU format */ buff[AR_name_offset] = '/'; goto stat; } + if (strcmp(pathname, "/SYM64/") == 0) { + /* Entry is archive symbol table in GNU 64-bit format */ + memcpy(buff + AR_name_offset, "/SYM64/", 7); + goto stat; + } if (strcmp(pathname, "__.SYMDEF") == 0) { /* Entry is archive symbol table in BSD format */ memcpy(buff + AR_name_offset, "__.SYMDEF", 9); goto stat; } if (strcmp(pathname, "//") == 0) { /* * Entry is archive filename table, inform that we should * collect strtab in next _data call. */ ar->is_strtab = 1; buff[AR_name_offset] = buff[AR_name_offset + 1] = '/'; /* * For archive string table, only ar_size field should * be set. */ goto size; } /* * Otherwise, entry is a normal archive member. * Strip leading paths from filenames, if any. */ if ((filename = ar_basename(pathname)) == NULL) { /* Reject filenames with trailing "/" */ archive_set_error(&a->archive, EINVAL, "Invalid filename"); return (ARCHIVE_WARN); } if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) { /* * SVR4/GNU variant use a "/" to mark then end of the filename, * make it possible to have embedded spaces in the filename. * So, the longest filename here (without extension) is * actually 15 bytes. */ if (strlen(filename) <= 15) { memcpy(&buff[AR_name_offset], filename, strlen(filename)); buff[AR_name_offset + strlen(filename)] = '/'; } else { /* * For filename longer than 15 bytes, GNU variant * makes use of a string table and instead stores the * offset of the real filename to in the ar_name field. * The string table should have been written before. */ if (ar->has_strtab <= 0) { archive_set_error(&a->archive, EINVAL, "Can't find string table"); return (ARCHIVE_WARN); } se = (char *)malloc(strlen(filename) + 3); if (se == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate filename buffer"); return (ARCHIVE_FATAL); } memcpy(se, filename, strlen(filename)); strcpy(se + strlen(filename), "/\n"); ss = strstr(ar->strtab, se); free(se); if (ss == NULL) { archive_set_error(&a->archive, EINVAL, "Invalid string table"); return (ARCHIVE_WARN); } /* * GNU variant puts "/" followed by digits into * ar_name field. These digits indicates the real * filename string's offset to the string table. */ buff[AR_name_offset] = '/'; if (format_decimal(ss - ar->strtab, buff + AR_name_offset + 1, AR_name_size - 1)) { archive_set_error(&a->archive, ERANGE, "string table offset too large"); return (ARCHIVE_WARN); } } } else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD) { /* * BSD variant: for any file name which is more than * 16 chars or contains one or more embedded space(s), the * string "#1/" followed by the ASCII length of the name is * put into the ar_name field. The file size (stored in the * ar_size field) is incremented by the length of the name. * The name is then written immediately following the * archive header. */ if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) { memcpy(&buff[AR_name_offset], filename, strlen(filename)); buff[AR_name_offset + strlen(filename)] = ' '; } else { memcpy(buff + AR_name_offset, "#1/", 3); if (format_decimal(strlen(filename), buff + AR_name_offset + 3, AR_name_size - 3)) { archive_set_error(&a->archive, ERANGE, "File name too long"); return (ARCHIVE_WARN); } append_fn = 1; size += strlen(filename); } } stat: if (format_decimal(archive_entry_mtime(entry), buff + AR_date_offset, AR_date_size)) { archive_set_error(&a->archive, ERANGE, "File modification time too large"); return (ARCHIVE_WARN); } if (format_decimal(archive_entry_uid(entry), buff + AR_uid_offset, AR_uid_size)) { archive_set_error(&a->archive, ERANGE, "Numeric user ID too large"); return (ARCHIVE_WARN); } if (format_decimal(archive_entry_gid(entry), buff + AR_gid_offset, AR_gid_size)) { archive_set_error(&a->archive, ERANGE, "Numeric group ID too large"); return (ARCHIVE_WARN); } if (format_octal(archive_entry_mode(entry), buff + AR_mode_offset, AR_mode_size)) { archive_set_error(&a->archive, ERANGE, "Numeric mode too large"); return (ARCHIVE_WARN); } /* * Sanity Check: A non-pseudo archive member should always be * a regular file. */ if (filename != NULL && archive_entry_filetype(entry) != AE_IFREG) { archive_set_error(&a->archive, EINVAL, "Regular file required for non-pseudo member"); return (ARCHIVE_WARN); } size: if (format_decimal(size, buff + AR_size_offset, AR_size_size)) { archive_set_error(&a->archive, ERANGE, "File size out of range"); return (ARCHIVE_WARN); } ret = __archive_write_output(a, buff, 60); if (ret != ARCHIVE_OK) return (ret); ar->entry_bytes_remaining = size; ar->entry_padding = ar->entry_bytes_remaining % 2; if (append_fn > 0) { ret = __archive_write_output(a, filename, strlen(filename)); if (ret != ARCHIVE_OK) return (ret); ar->entry_bytes_remaining -= strlen(filename); } return (ARCHIVE_OK); } static ssize_t archive_write_ar_data(struct archive_write *a, const void *buff, size_t s) { struct ar_w *ar; int ret; ar = (struct ar_w *)a->format_data; if (s > ar->entry_bytes_remaining) s = (size_t)ar->entry_bytes_remaining; if (ar->is_strtab > 0) { if (ar->has_strtab > 0) { archive_set_error(&a->archive, EINVAL, "More than one string tables exist"); return (ARCHIVE_WARN); } ar->strtab = (char *)malloc(s + 1); if (ar->strtab == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate strtab buffer"); return (ARCHIVE_FATAL); } memcpy(ar->strtab, buff, s); ar->strtab[s] = '\0'; ar->has_strtab = 1; } ret = __archive_write_output(a, buff, s); if (ret != ARCHIVE_OK) return (ret); ar->entry_bytes_remaining -= s; return (s); } static int archive_write_ar_free(struct archive_write *a) { struct ar_w *ar; ar = (struct ar_w *)a->format_data; if (ar == NULL) return (ARCHIVE_OK); if (ar->has_strtab > 0) { free(ar->strtab); ar->strtab = NULL; } free(ar); a->format_data = NULL; return (ARCHIVE_OK); } static int archive_write_ar_close(struct archive_write *a) { struct ar_w *ar; int ret; /* * If we haven't written anything yet, we need to write * the ar global header now to make it a valid ar archive. */ ar = (struct ar_w *)a->format_data; if (!ar->wrote_global_header) { ar->wrote_global_header = 1; ret = __archive_write_output(a, "!\n", 8); return (ret); } return (ARCHIVE_OK); } static int archive_write_ar_finish_entry(struct archive_write *a) { struct ar_w *ar; int ret; ar = (struct ar_w *)a->format_data; if (ar->entry_bytes_remaining != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Entry remaining bytes larger than 0"); return (ARCHIVE_WARN); } if (ar->entry_padding == 0) { return (ARCHIVE_OK); } if (ar->entry_padding != 1) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Padding wrong size: %ju should be 1 or 0", (uintmax_t)ar->entry_padding); return (ARCHIVE_WARN); } ret = __archive_write_output(a, "\n", 1); return (ret); } /* * Format a number into the specified field using base-8. * NB: This version is slightly different from the one in * _ustar.c */ static int format_octal(int64_t v, char *p, int s) { int len; char *h; len = s; h = p; /* Octal values can't be negative, so use 0. */ if (v < 0) { while (len-- > 0) *p++ = '0'; return (-1); } p += s; /* Start at the end and work backwards. */ do { *--p = (char)('0' + (v & 7)); v >>= 3; } while (--s > 0 && v > 0); if (v == 0) { memmove(h, p, len - s); p = h + len - s; while (s-- > 0) *p++ = ' '; return (0); } /* If it overflowed, fill field with max value. */ while (len-- > 0) *p++ = '7'; return (-1); } /* * Format a number into the specified field using base-10. */ static int format_decimal(int64_t v, char *p, int s) { int len; char *h; len = s; h = p; /* Negative values in ar header are meaningless, so use 0. */ if (v < 0) { while (len-- > 0) *p++ = '0'; return (-1); } p += s; do { *--p = (char)('0' + (v % 10)); v /= 10; } while (--s > 0 && v > 0); if (v == 0) { memmove(h, p, len - s); p = h + len - s; while (s-- > 0) *p++ = ' '; return (0); } /* If it overflowed, fill field with max value. */ while (len-- > 0) *p++ = '9'; return (-1); } static const char * ar_basename(const char *path) { const char *endp, *startp; endp = path + strlen(path) - 1; /* * For filename with trailing slash(es), we return * NULL indicating an error. */ if (*endp == '/') return (NULL); /* Find the start of the base */ startp = endp; while (startp > path && *(startp - 1) != '/') startp--; return (startp); } Index: stable/10/contrib/libarchive/libarchive/archive_write_set_format_cpio.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_set_format_cpio.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_write_set_format_cpio.c (revision 344674) @@ -1,500 +1,499 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2011-2012 Michihiro NAKAJIMA * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_write_private.h" static ssize_t archive_write_cpio_data(struct archive_write *, const void *buff, size_t s); static int archive_write_cpio_close(struct archive_write *); static int archive_write_cpio_free(struct archive_write *); static int archive_write_cpio_finish_entry(struct archive_write *); static int archive_write_cpio_header(struct archive_write *, struct archive_entry *); static int archive_write_cpio_options(struct archive_write *, const char *, const char *); static int format_octal(int64_t, void *, int); static int64_t format_octal_recursive(int64_t, char *, int); static int write_header(struct archive_write *, struct archive_entry *); struct cpio { uint64_t entry_bytes_remaining; int64_t ino_next; struct { int64_t old; int new;} *ino_list; size_t ino_list_size; size_t ino_list_next; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_default; int init_default_conversion; }; #define c_magic_offset 0 #define c_magic_size 6 #define c_dev_offset 6 #define c_dev_size 6 #define c_ino_offset 12 #define c_ino_size 6 #define c_mode_offset 18 #define c_mode_size 6 #define c_uid_offset 24 #define c_uid_size 6 #define c_gid_offset 30 #define c_gid_size 6 #define c_nlink_offset 36 #define c_nlink_size 6 #define c_rdev_offset 42 #define c_rdev_size 6 #define c_mtime_offset 48 #define c_mtime_size 11 #define c_namesize_offset 59 #define c_namesize_size 6 #define c_filesize_offset 65 #define c_filesize_size 11 /* * Set output format to 'cpio' format. */ int archive_write_set_format_cpio(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct cpio *cpio; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_cpio"); /* If someone else was already registered, unregister them. */ if (a->format_free != NULL) (a->format_free)(a); cpio = (struct cpio *)calloc(1, sizeof(*cpio)); if (cpio == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data"); return (ARCHIVE_FATAL); } a->format_data = cpio; a->format_name = "cpio"; a->format_options = archive_write_cpio_options; a->format_write_header = archive_write_cpio_header; a->format_write_data = archive_write_cpio_data; a->format_finish_entry = archive_write_cpio_finish_entry; a->format_close = archive_write_cpio_close; a->format_free = archive_write_cpio_free; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX; a->archive.archive_format_name = "POSIX cpio"; return (ARCHIVE_OK); } static int archive_write_cpio_options(struct archive_write *a, const char *key, const char *val) { struct cpio *cpio = (struct cpio *)a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: hdrcharset option needs a character-set name", a->format_name); else { cpio->opt_sconv = archive_string_conversion_to_charset( &a->archive, val, 0); if (cpio->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } /* * Ino values are as long as 64 bits on some systems; cpio format * only allows 18 bits and relies on the ino values to identify hardlinked * files. So, we can't merely "hash" the ino numbers since collisions * would corrupt the archive. Instead, we generate synthetic ino values * to store in the archive and maintain a map of original ino values to * synthetic ones so we can preserve hardlink information. * * TODO: Make this more efficient. It's not as bad as it looks (most * files don't have any hardlinks and we don't do any work here for those), * but it wouldn't be hard to do better. * * TODO: Work with dev/ino pairs here instead of just ino values. */ static int synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry) { int64_t ino = archive_entry_ino64(entry); int ino_new; size_t i; /* * If no index number was given, don't assign one. In * particular, this handles the end-of-archive marker * correctly by giving it a zero index value. (This is also * why we start our synthetic index numbers with one below.) */ if (ino == 0) return (0); /* Don't store a mapping if we don't need to. */ if (archive_entry_nlink(entry) < 2) { return (int)(++cpio->ino_next); } /* Look up old ino; if we have it, this is a hardlink * and we reuse the same value. */ for (i = 0; i < cpio->ino_list_next; ++i) { if (cpio->ino_list[i].old == ino) return (cpio->ino_list[i].new); } /* Assign a new index number. */ ino_new = (int)(++cpio->ino_next); /* Ensure space for the new mapping. */ if (cpio->ino_list_size <= cpio->ino_list_next) { size_t newsize = cpio->ino_list_size < 512 ? 512 : cpio->ino_list_size * 2; void *newlist = realloc(cpio->ino_list, sizeof(cpio->ino_list[0]) * newsize); if (newlist == NULL) return (-1); cpio->ino_list_size = newsize; cpio->ino_list = newlist; } /* Record and return the new value. */ cpio->ino_list[cpio->ino_list_next].old = ino; cpio->ino_list[cpio->ino_list_next].new = ino_new; ++cpio->ino_list_next; return (ino_new); } static struct archive_string_conv * get_sconv(struct archive_write *a) { struct cpio *cpio; struct archive_string_conv *sconv; cpio = (struct cpio *)a->format_data; sconv = cpio->opt_sconv; if (sconv == NULL) { if (!cpio->init_default_conversion) { cpio->sconv_default = archive_string_default_conversion_for_write( &(a->archive)); cpio->init_default_conversion = 1; } sconv = cpio->sconv_default; } return (sconv); } static int archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry) { const char *path; size_t len; if (archive_entry_filetype(entry) == 0) { archive_set_error(&a->archive, -1, "Filetype required"); return (ARCHIVE_FAILED); } if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0 && errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } if (len == 0 || path == NULL || path[0] == '\0') { archive_set_error(&a->archive, -1, "Pathname required"); return (ARCHIVE_FAILED); } if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0) { archive_set_error(&a->archive, -1, "Size required"); return (ARCHIVE_FAILED); } return write_header(a, entry); } static int write_header(struct archive_write *a, struct archive_entry *entry) { struct cpio *cpio; const char *p, *path; int pathlength, ret, ret_final; int64_t ino; char h[76]; struct archive_string_conv *sconv; struct archive_entry *entry_main; size_t len; cpio = (struct cpio *)a->format_data; ret_final = ARCHIVE_OK; sconv = get_sconv(a); #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); return(ARCHIVE_FATAL); } if (entry != entry_main) entry = entry_main; else entry_main = NULL; #else entry_main = NULL; #endif ret = archive_entry_pathname_l(entry, &path, &len, sconv); if (ret != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); ret_final = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to %s", archive_entry_pathname(entry), archive_string_conversion_charset_name(sconv)); ret_final = ARCHIVE_WARN; } /* Include trailing null. */ pathlength = (int)len + 1; memset(h, 0, sizeof(h)); format_octal(070707, h + c_magic_offset, c_magic_size); format_octal(archive_entry_dev(entry), h + c_dev_offset, c_dev_size); ino = synthesize_ino_value(cpio, entry); if (ino < 0) { archive_set_error(&a->archive, ENOMEM, "No memory for ino translation table"); ret_final = ARCHIVE_FATAL; goto exit_write_header; } else if (ino > 0777777) { archive_set_error(&a->archive, ERANGE, "Too many files for this cpio format"); ret_final = ARCHIVE_FATAL; goto exit_write_header; } format_octal(ino & 0777777, h + c_ino_offset, c_ino_size); /* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */ format_octal(archive_entry_mode(entry), h + c_mode_offset, c_mode_size); format_octal(archive_entry_uid(entry), h + c_uid_offset, c_uid_size); format_octal(archive_entry_gid(entry), h + c_gid_offset, c_gid_size); format_octal(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size); if (archive_entry_filetype(entry) == AE_IFBLK || archive_entry_filetype(entry) == AE_IFCHR) format_octal(archive_entry_dev(entry), h + c_rdev_offset, c_rdev_size); else format_octal(0, h + c_rdev_offset, c_rdev_size); format_octal(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size); format_octal(pathlength, h + c_namesize_offset, c_namesize_size); /* Non-regular files don't store bodies. */ if (archive_entry_filetype(entry) != AE_IFREG) archive_entry_set_size(entry, 0); /* Symlinks get the link written as the body of the entry. */ ret = archive_entry_symlink_l(entry, &p, &len, sconv); if (ret != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); ret_final = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", archive_entry_symlink(entry), archive_string_conversion_charset_name(sconv)); ret_final = ARCHIVE_WARN; } if (len > 0 && p != NULL && *p != '\0') ret = format_octal(strlen(p), h + c_filesize_offset, c_filesize_size); else ret = format_octal(archive_entry_size(entry), h + c_filesize_offset, c_filesize_size); if (ret) { archive_set_error(&a->archive, ERANGE, "File is too large for cpio format."); ret_final = ARCHIVE_FAILED; goto exit_write_header; } ret = __archive_write_output(a, h, sizeof(h)); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } ret = __archive_write_output(a, path, pathlength); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } cpio->entry_bytes_remaining = archive_entry_size(entry); /* Write the symlink now. */ if (p != NULL && *p != '\0') { ret = __archive_write_output(a, p, strlen(p)); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } } exit_write_header: - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret_final); } static ssize_t archive_write_cpio_data(struct archive_write *a, const void *buff, size_t s) { struct cpio *cpio; int ret; cpio = (struct cpio *)a->format_data; if (s > cpio->entry_bytes_remaining) s = (size_t)cpio->entry_bytes_remaining; ret = __archive_write_output(a, buff, s); cpio->entry_bytes_remaining -= s; if (ret >= 0) return (s); else return (ret); } /* * Format a number into the specified field. */ static int format_octal(int64_t v, void *p, int digits) { int64_t max; int ret; max = (((int64_t)1) << (digits * 3)) - 1; if (v >= 0 && v <= max) { format_octal_recursive(v, (char *)p, digits); ret = 0; } else { format_octal_recursive(max, (char *)p, digits); ret = -1; } return (ret); } static int64_t format_octal_recursive(int64_t v, char *p, int s) { if (s == 0) return (v); v = format_octal_recursive(v, p+1, s-1); *p = '0' + ((char)v & 7); return (v >> 3); } static int archive_write_cpio_close(struct archive_write *a) { int er; struct archive_entry *trailer; trailer = archive_entry_new2(NULL); /* nlink = 1 here for GNU cpio compat. */ archive_entry_set_nlink(trailer, 1); archive_entry_set_size(trailer, 0); archive_entry_set_pathname(trailer, "TRAILER!!!"); er = write_header(a, trailer); archive_entry_free(trailer); return (er); } static int archive_write_cpio_free(struct archive_write *a) { struct cpio *cpio; cpio = (struct cpio *)a->format_data; free(cpio->ino_list); free(cpio); a->format_data = NULL; return (ARCHIVE_OK); } static int archive_write_cpio_finish_entry(struct archive_write *a) { struct cpio *cpio; cpio = (struct cpio *)a->format_data; return (__archive_write_nulls(a, (size_t)cpio->entry_bytes_remaining)); } Index: stable/10/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_write_set_format_cpio_newc.c (revision 344674) @@ -1,457 +1,456 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2006 Rudolf Marek SYSGO s.r.o. * Copyright (c) 2011-2012 Michihiro NAKAJIMA * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_write_private.h" static ssize_t archive_write_newc_data(struct archive_write *, const void *buff, size_t s); static int archive_write_newc_close(struct archive_write *); static int archive_write_newc_free(struct archive_write *); static int archive_write_newc_finish_entry(struct archive_write *); static int archive_write_newc_header(struct archive_write *, struct archive_entry *); static int archive_write_newc_options(struct archive_write *, const char *, const char *); static int format_hex(int64_t, void *, int); static int64_t format_hex_recursive(int64_t, char *, int); static int write_header(struct archive_write *, struct archive_entry *); struct cpio { uint64_t entry_bytes_remaining; int padding; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_default; int init_default_conversion; }; #define c_magic_offset 0 #define c_magic_size 6 #define c_ino_offset 6 #define c_ino_size 8 #define c_mode_offset 14 #define c_mode_size 8 #define c_uid_offset 22 #define c_uid_size 8 #define c_gid_offset 30 #define c_gid_size 8 #define c_nlink_offset 38 #define c_nlink_size 8 #define c_mtime_offset 46 #define c_mtime_size 8 #define c_filesize_offset 54 #define c_filesize_size 8 #define c_devmajor_offset 62 #define c_devmajor_size 8 #define c_devminor_offset 70 #define c_devminor_size 8 #define c_rdevmajor_offset 78 #define c_rdevmajor_size 8 #define c_rdevminor_offset 86 #define c_rdevminor_size 8 #define c_namesize_offset 94 #define c_namesize_size 8 #define c_checksum_offset 102 #define c_checksum_size 8 #define c_header_size 110 /* Logic trick: difference between 'n' and next multiple of 4 */ #define PAD4(n) (3 & (1 + ~(n))) /* * Set output format to 'cpio' format. */ int archive_write_set_format_cpio_newc(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct cpio *cpio; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_newc"); /* If someone else was already registered, unregister them. */ if (a->format_free != NULL) (a->format_free)(a); cpio = (struct cpio *)calloc(1, sizeof(*cpio)); if (cpio == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data"); return (ARCHIVE_FATAL); } a->format_data = cpio; a->format_name = "cpio"; a->format_options = archive_write_newc_options; a->format_write_header = archive_write_newc_header; a->format_write_data = archive_write_newc_data; a->format_finish_entry = archive_write_newc_finish_entry; a->format_close = archive_write_newc_close; a->format_free = archive_write_newc_free; a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC; a->archive.archive_format_name = "SVR4 cpio nocrc"; return (ARCHIVE_OK); } static int archive_write_newc_options(struct archive_write *a, const char *key, const char *val) { struct cpio *cpio = (struct cpio *)a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: hdrcharset option needs a character-set name", a->format_name); else { cpio->opt_sconv = archive_string_conversion_to_charset( &a->archive, val, 0); if (cpio->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static struct archive_string_conv * get_sconv(struct archive_write *a) { struct cpio *cpio; struct archive_string_conv *sconv; cpio = (struct cpio *)a->format_data; sconv = cpio->opt_sconv; if (sconv == NULL) { if (!cpio->init_default_conversion) { cpio->sconv_default = archive_string_default_conversion_for_write( &(a->archive)); cpio->init_default_conversion = 1; } sconv = cpio->sconv_default; } return (sconv); } static int archive_write_newc_header(struct archive_write *a, struct archive_entry *entry) { const char *path; size_t len; if (archive_entry_filetype(entry) == 0) { archive_set_error(&a->archive, -1, "Filetype required"); return (ARCHIVE_FAILED); } if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0 && errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } if (len == 0 || path == NULL || path[0] == '\0') { archive_set_error(&a->archive, -1, "Pathname required"); return (ARCHIVE_FAILED); } if (archive_entry_hardlink(entry) == NULL && (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0)) { archive_set_error(&a->archive, -1, "Size required"); return (ARCHIVE_FAILED); } return write_header(a, entry); } static int write_header(struct archive_write *a, struct archive_entry *entry) { int64_t ino; struct cpio *cpio; const char *p, *path; int pathlength, ret, ret_final; char h[c_header_size]; struct archive_string_conv *sconv; struct archive_entry *entry_main; size_t len; int pad; cpio = (struct cpio *)a->format_data; ret_final = ARCHIVE_OK; sconv = get_sconv(a); #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); return(ARCHIVE_FATAL); } if (entry != entry_main) entry = entry_main; else entry_main = NULL; #else entry_main = NULL; #endif ret = archive_entry_pathname_l(entry, &path, &len, sconv); if (ret != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); ret_final = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to %s", archive_entry_pathname(entry), archive_string_conversion_charset_name(sconv)); ret_final = ARCHIVE_WARN; } pathlength = (int)len + 1; /* Include trailing null. */ memset(h, 0, c_header_size); format_hex(0x070701, h + c_magic_offset, c_magic_size); format_hex(archive_entry_devmajor(entry), h + c_devmajor_offset, c_devmajor_size); format_hex(archive_entry_devminor(entry), h + c_devminor_offset, c_devminor_size); ino = archive_entry_ino64(entry); if (ino > 0xffffffff) { archive_set_error(&a->archive, ERANGE, "large inode number truncated"); ret_final = ARCHIVE_WARN; } /* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */ format_hex(ino & 0xffffffff, h + c_ino_offset, c_ino_size); format_hex(archive_entry_mode(entry), h + c_mode_offset, c_mode_size); format_hex(archive_entry_uid(entry), h + c_uid_offset, c_uid_size); format_hex(archive_entry_gid(entry), h + c_gid_offset, c_gid_size); format_hex(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size); if (archive_entry_filetype(entry) == AE_IFBLK || archive_entry_filetype(entry) == AE_IFCHR) { format_hex(archive_entry_rdevmajor(entry), h + c_rdevmajor_offset, c_rdevmajor_size); format_hex(archive_entry_rdevminor(entry), h + c_rdevminor_offset, c_rdevminor_size); } else { format_hex(0, h + c_rdevmajor_offset, c_rdevmajor_size); format_hex(0, h + c_rdevminor_offset, c_rdevminor_size); } format_hex(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size); format_hex(pathlength, h + c_namesize_offset, c_namesize_size); format_hex(0, h + c_checksum_offset, c_checksum_size); /* Non-regular files don't store bodies. */ if (archive_entry_filetype(entry) != AE_IFREG) archive_entry_set_size(entry, 0); /* Symlinks get the link written as the body of the entry. */ ret = archive_entry_symlink_l(entry, &p, &len, sconv); if (ret != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Likname"); ret_final = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", archive_entry_symlink(entry), archive_string_conversion_charset_name(sconv)); ret_final = ARCHIVE_WARN; } if (len > 0 && p != NULL && *p != '\0') ret = format_hex(strlen(p), h + c_filesize_offset, c_filesize_size); else ret = format_hex(archive_entry_size(entry), h + c_filesize_offset, c_filesize_size); if (ret) { archive_set_error(&a->archive, ERANGE, "File is too large for this format."); ret_final = ARCHIVE_FAILED; goto exit_write_header; } ret = __archive_write_output(a, h, c_header_size); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } /* Pad pathname to even length. */ ret = __archive_write_output(a, path, pathlength); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } pad = PAD4(pathlength + c_header_size); if (pad) { ret = __archive_write_output(a, "\0\0\0", pad); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } } cpio->entry_bytes_remaining = archive_entry_size(entry); cpio->padding = (int)PAD4(cpio->entry_bytes_remaining); /* Write the symlink now. */ if (p != NULL && *p != '\0') { ret = __archive_write_output(a, p, strlen(p)); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } pad = PAD4(strlen(p)); ret = __archive_write_output(a, "\0\0\0", pad); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } } exit_write_header: - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret_final); } static ssize_t archive_write_newc_data(struct archive_write *a, const void *buff, size_t s) { struct cpio *cpio; int ret; cpio = (struct cpio *)a->format_data; if (s > cpio->entry_bytes_remaining) s = (size_t)cpio->entry_bytes_remaining; ret = __archive_write_output(a, buff, s); cpio->entry_bytes_remaining -= s; if (ret >= 0) return (s); else return (ret); } /* * Format a number into the specified field. */ static int format_hex(int64_t v, void *p, int digits) { int64_t max; int ret; max = (((int64_t)1) << (digits * 4)) - 1; if (v >= 0 && v <= max) { format_hex_recursive(v, (char *)p, digits); ret = 0; } else { format_hex_recursive(max, (char *)p, digits); ret = -1; } return (ret); } static int64_t format_hex_recursive(int64_t v, char *p, int s) { if (s == 0) return (v); v = format_hex_recursive(v, p+1, s-1); *p = "0123456789abcdef"[v & 0xf]; return (v >> 4); } static int archive_write_newc_close(struct archive_write *a) { int er; struct archive_entry *trailer; trailer = archive_entry_new(); archive_entry_set_nlink(trailer, 1); archive_entry_set_size(trailer, 0); archive_entry_set_pathname(trailer, "TRAILER!!!"); /* Bypass the required data checks. */ er = write_header(a, trailer); archive_entry_free(trailer); return (er); } static int archive_write_newc_free(struct archive_write *a) { struct cpio *cpio; cpio = (struct cpio *)a->format_data; free(cpio); a->format_data = NULL; return (ARCHIVE_OK); } static int archive_write_newc_finish_entry(struct archive_write *a) { struct cpio *cpio; cpio = (struct cpio *)a->format_data; return (__archive_write_nulls(a, (size_t)cpio->entry_bytes_remaining + cpio->padding)); } Index: stable/10/contrib/libarchive/libarchive/archive_write_set_format_gnutar.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_set_format_gnutar.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_write_set_format_gnutar.c (revision 344674) @@ -1,763 +1,762 @@ /*- * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). * Author: Jonas Gastal * Copyright (c) 2011-2012 Michihiro NAKAJIMA * * 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 "archive_platform.h" __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_gnu_tar.c 191579 2009-04-27 18:35:03Z gastal $"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_write_private.h" struct gnutar { uint64_t entry_bytes_remaining; uint64_t entry_padding; const char * linkname; size_t linkname_length; const char * pathname; size_t pathname_length; const char * uname; size_t uname_length; const char * gname; size_t gname_length; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_default; int init_default_conversion; }; /* * Define structure of GNU tar header. */ #define GNUTAR_name_offset 0 #define GNUTAR_name_size 100 #define GNUTAR_mode_offset 100 #define GNUTAR_mode_size 7 #define GNUTAR_mode_max_size 8 #define GNUTAR_uid_offset 108 #define GNUTAR_uid_size 7 #define GNUTAR_uid_max_size 8 #define GNUTAR_gid_offset 116 #define GNUTAR_gid_size 7 #define GNUTAR_gid_max_size 8 #define GNUTAR_size_offset 124 #define GNUTAR_size_size 11 #define GNUTAR_size_max_size 12 #define GNUTAR_mtime_offset 136 #define GNUTAR_mtime_size 11 #define GNUTAR_mtime_max_size 11 #define GNUTAR_checksum_offset 148 #define GNUTAR_checksum_size 8 #define GNUTAR_typeflag_offset 156 #define GNUTAR_typeflag_size 1 #define GNUTAR_linkname_offset 157 #define GNUTAR_linkname_size 100 #define GNUTAR_magic_offset 257 #define GNUTAR_magic_size 6 #define GNUTAR_version_offset 263 #define GNUTAR_version_size 2 #define GNUTAR_uname_offset 265 #define GNUTAR_uname_size 32 #define GNUTAR_gname_offset 297 #define GNUTAR_gname_size 32 #define GNUTAR_rdevmajor_offset 329 #define GNUTAR_rdevmajor_size 6 #define GNUTAR_rdevmajor_max_size 8 #define GNUTAR_rdevminor_offset 337 #define GNUTAR_rdevminor_size 6 #define GNUTAR_rdevminor_max_size 8 /* * A filled-in copy of the header for initialization. */ static const char template_header[] = { /* name: 100 bytes */ 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,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,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0, /* Mode, null termination: 8 bytes */ '0','0','0','0','0','0', '0','\0', /* uid, null termination: 8 bytes */ '0','0','0','0','0','0', '0','\0', /* gid, null termination: 8 bytes */ '0','0','0','0','0','0', '0','\0', /* size, space termination: 12 bytes */ '0','0','0','0','0','0','0','0','0','0','0', '\0', /* mtime, space termination: 12 bytes */ '0','0','0','0','0','0','0','0','0','0','0', '\0', /* Initial checksum value: 8 spaces */ ' ',' ',' ',' ',' ',' ',' ',' ', /* Typeflag: 1 byte */ '0', /* '0' = regular file */ /* Linkname: 100 bytes */ 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,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,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0, /* Magic: 8 bytes */ 'u','s','t','a','r',' ', ' ','\0', /* Uname: 32 bytes */ 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, /* Gname: 32 bytes */ 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, /* rdevmajor + null padding: 8 bytes */ '\0','\0','\0','\0','\0','\0', '\0','\0', /* rdevminor + null padding: 8 bytes */ '\0','\0','\0','\0','\0','\0', '\0','\0', /* Padding: 167 bytes */ 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,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,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,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 }; static int archive_write_gnutar_options(struct archive_write *, const char *, const char *); static int archive_format_gnutar_header(struct archive_write *, char h[512], struct archive_entry *, int tartype); static int archive_write_gnutar_header(struct archive_write *, struct archive_entry *entry); static ssize_t archive_write_gnutar_data(struct archive_write *a, const void *buff, size_t s); static int archive_write_gnutar_free(struct archive_write *); static int archive_write_gnutar_close(struct archive_write *); static int archive_write_gnutar_finish_entry(struct archive_write *); static int format_256(int64_t, char *, int); static int format_number(int64_t, char *, int size, int maxsize); static int format_octal(int64_t, char *, int); /* * Set output format to 'GNU tar' format. */ int archive_write_set_format_gnutar(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct gnutar *gnutar; gnutar = (struct gnutar *)calloc(1, sizeof(*gnutar)); if (gnutar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate gnutar data"); return (ARCHIVE_FATAL); } a->format_data = gnutar; a->format_name = "gnutar"; a->format_options = archive_write_gnutar_options; a->format_write_header = archive_write_gnutar_header; a->format_write_data = archive_write_gnutar_data; a->format_close = archive_write_gnutar_close; a->format_free = archive_write_gnutar_free; a->format_finish_entry = archive_write_gnutar_finish_entry; a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR; a->archive.archive_format_name = "GNU tar"; return (ARCHIVE_OK); } static int archive_write_gnutar_options(struct archive_write *a, const char *key, const char *val) { struct gnutar *gnutar = (struct gnutar *)a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: hdrcharset option needs a character-set name", a->format_name); else { gnutar->opt_sconv = archive_string_conversion_to_charset( &a->archive, val, 0); if (gnutar->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int archive_write_gnutar_close(struct archive_write *a) { return (__archive_write_nulls(a, 512*2)); } static int archive_write_gnutar_free(struct archive_write *a) { struct gnutar *gnutar; gnutar = (struct gnutar *)a->format_data; free(gnutar); a->format_data = NULL; return (ARCHIVE_OK); } static int archive_write_gnutar_finish_entry(struct archive_write *a) { struct gnutar *gnutar; int ret; gnutar = (struct gnutar *)a->format_data; ret = __archive_write_nulls(a, (size_t) (gnutar->entry_bytes_remaining + gnutar->entry_padding)); gnutar->entry_bytes_remaining = gnutar->entry_padding = 0; return (ret); } static ssize_t archive_write_gnutar_data(struct archive_write *a, const void *buff, size_t s) { struct gnutar *gnutar; int ret; gnutar = (struct gnutar *)a->format_data; if (s > gnutar->entry_bytes_remaining) s = (size_t)gnutar->entry_bytes_remaining; ret = __archive_write_output(a, buff, s); gnutar->entry_bytes_remaining -= s; if (ret != ARCHIVE_OK) return (ret); return (s); } static int archive_write_gnutar_header(struct archive_write *a, struct archive_entry *entry) { char buff[512]; int r, ret, ret2 = ARCHIVE_OK; int tartype; struct gnutar *gnutar; struct archive_string_conv *sconv; struct archive_entry *entry_main; gnutar = (struct gnutar *)a->format_data; /* Setup default string conversion. */ if (gnutar->opt_sconv == NULL) { if (!gnutar->init_default_conversion) { gnutar->sconv_default = archive_string_default_conversion_for_write( &(a->archive)); gnutar->init_default_conversion = 1; } sconv = gnutar->sconv_default; } else sconv = gnutar->opt_sconv; /* Only regular files (not hardlinks) have data. */ if (archive_entry_hardlink(entry) != NULL || archive_entry_symlink(entry) != NULL || !(archive_entry_filetype(entry) == AE_IFREG)) archive_entry_set_size(entry, 0); if (AE_IFDIR == archive_entry_filetype(entry)) { const char *p; size_t path_length; /* * Ensure a trailing '/'. Modify the entry so * the client sees the change. */ #if defined(_WIN32) && !defined(__CYGWIN__) const wchar_t *wp; wp = archive_entry_pathname_w(entry); if (wp != NULL && wp[wcslen(wp) -1] != L'/') { struct archive_wstring ws; archive_string_init(&ws); path_length = wcslen(wp); if (archive_wstring_ensure(&ws, path_length + 2) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); archive_wstring_free(&ws); return(ARCHIVE_FATAL); } /* Should we keep '\' ? */ if (wp[path_length -1] == L'\\') path_length--; archive_wstrncpy(&ws, wp, path_length); archive_wstrappend_wchar(&ws, L'/'); archive_entry_copy_pathname_w(entry, ws.s); archive_wstring_free(&ws); p = NULL; } else #endif p = archive_entry_pathname(entry); /* * On Windows, this is a backup operation just in * case getting WCS failed. On POSIX, this is a * normal operation. */ if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') { struct archive_string as; archive_string_init(&as); path_length = strlen(p); if (archive_string_ensure(&as, path_length + 2) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); archive_string_free(&as); return(ARCHIVE_FATAL); } #if defined(_WIN32) && !defined(__CYGWIN__) /* NOTE: This might break the pathname * if the current code page is CP932 and * the pathname includes a character '\' * as a part of its multibyte pathname. */ if (p[strlen(p) -1] == '\\') path_length--; else #endif archive_strncpy(&as, p, path_length); archive_strappend_char(&as, '/'); archive_entry_copy_pathname(entry, as.s); archive_string_free(&as); } } #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); return(ARCHIVE_FATAL); } if (entry != entry_main) entry = entry_main; else entry_main = NULL; #else entry_main = NULL; #endif r = archive_entry_pathname_l(entry, &(gnutar->pathname), &(gnutar->pathname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathame"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to %s", archive_entry_pathname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } r = archive_entry_uname_l(entry, &(gnutar->uname), &(gnutar->uname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Uname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate uname '%s' to %s", archive_entry_uname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } r = archive_entry_gname_l(entry, &(gnutar->gname), &(gnutar->gname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Gname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate gname '%s' to %s", archive_entry_gname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } /* If linkname is longer than 100 chars we need to add a 'K' header. */ r = archive_entry_hardlink_l(entry, &(gnutar->linkname), &(gnutar->linkname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", archive_entry_hardlink(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } if (gnutar->linkname_length == 0) { r = archive_entry_symlink_l(entry, &(gnutar->linkname), &(gnutar->linkname_length), sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); ret = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", archive_entry_hardlink(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } } if (gnutar->linkname_length > GNUTAR_linkname_size) { size_t length = gnutar->linkname_length + 1; struct archive_entry *temp = archive_entry_new2(&a->archive); /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); archive_entry_set_gname(temp, "wheel"); archive_entry_set_pathname(temp, "././@LongLink"); archive_entry_set_size(temp, length); ret = archive_format_gnutar_header(a, buff, temp, 'K'); archive_entry_free(temp); if (ret < ARCHIVE_WARN) goto exit_write_header; ret = __archive_write_output(a, buff, 512); if (ret < ARCHIVE_WARN) goto exit_write_header; /* Write name and trailing null byte. */ ret = __archive_write_output(a, gnutar->linkname, length); if (ret < ARCHIVE_WARN) goto exit_write_header; /* Pad to 512 bytes */ ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length)); if (ret < ARCHIVE_WARN) goto exit_write_header; } /* If pathname is longer than 100 chars we need to add an 'L' header. */ if (gnutar->pathname_length > GNUTAR_name_size) { const char *pathname = gnutar->pathname; size_t length = gnutar->pathname_length + 1; struct archive_entry *temp = archive_entry_new2(&a->archive); /* Uname/gname here don't really matter since no one reads them; * these are the values that GNU tar happens to use on FreeBSD. */ archive_entry_set_uname(temp, "root"); archive_entry_set_gname(temp, "wheel"); archive_entry_set_pathname(temp, "././@LongLink"); archive_entry_set_size(temp, length); ret = archive_format_gnutar_header(a, buff, temp, 'L'); archive_entry_free(temp); if (ret < ARCHIVE_WARN) goto exit_write_header; ret = __archive_write_output(a, buff, 512); if(ret < ARCHIVE_WARN) goto exit_write_header; /* Write pathname + trailing null byte. */ ret = __archive_write_output(a, pathname, length); if(ret < ARCHIVE_WARN) goto exit_write_header; /* Pad to multiple of 512 bytes. */ ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length)); if (ret < ARCHIVE_WARN) goto exit_write_header; } if (archive_entry_hardlink(entry) != NULL) { tartype = '1'; } else switch (archive_entry_filetype(entry)) { case AE_IFREG: tartype = '0' ; break; case AE_IFLNK: tartype = '2' ; break; case AE_IFCHR: tartype = '3' ; break; case AE_IFBLK: tartype = '4' ; break; case AE_IFDIR: tartype = '5' ; break; case AE_IFIFO: tartype = '6' ; break; case AE_IFSOCK: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive socket"); ret = ARCHIVE_FAILED; goto exit_write_header; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive this (mode=0%lo)", (unsigned long)archive_entry_mode(entry)); ret = ARCHIVE_FAILED; goto exit_write_header; } ret = archive_format_gnutar_header(a, buff, entry, tartype); if (ret < ARCHIVE_WARN) goto exit_write_header; if (ret2 < ret) ret = ret2; ret2 = __archive_write_output(a, buff, 512); if (ret2 < ARCHIVE_WARN) { ret = ret2; goto exit_write_header; } if (ret2 < ret) ret = ret2; gnutar->entry_bytes_remaining = archive_entry_size(entry); gnutar->entry_padding = 0x1ff & (-(int64_t)gnutar->entry_bytes_remaining); exit_write_header: - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret); } static int archive_format_gnutar_header(struct archive_write *a, char h[512], struct archive_entry *entry, int tartype) { unsigned int checksum; int i, ret; size_t copy_length; const char *p; struct gnutar *gnutar; gnutar = (struct gnutar *)a->format_data; ret = 0; /* * The "template header" already includes the signature, * various end-of-field markers, and other required elements. */ memcpy(h, &template_header, 512); /* * Because the block is already null-filled, and strings * are allowed to exactly fill their destination (without null), * I use memcpy(dest, src, strlen()) here a lot to copy strings. */ if (tartype == 'K' || tartype == 'L') { p = archive_entry_pathname(entry); copy_length = strlen(p); } else { p = gnutar->pathname; copy_length = gnutar->pathname_length; } if (copy_length > GNUTAR_name_size) copy_length = GNUTAR_name_size; memcpy(h + GNUTAR_name_offset, p, copy_length); if ((copy_length = gnutar->linkname_length) > 0) { if (copy_length > GNUTAR_linkname_size) copy_length = GNUTAR_linkname_size; memcpy(h + GNUTAR_linkname_offset, gnutar->linkname, copy_length); } /* TODO: How does GNU tar handle unames longer than GNUTAR_uname_size? */ if (tartype == 'K' || tartype == 'L') { p = archive_entry_uname(entry); copy_length = strlen(p); } else { p = gnutar->uname; copy_length = gnutar->uname_length; } if (copy_length > 0) { if (copy_length > GNUTAR_uname_size) copy_length = GNUTAR_uname_size; memcpy(h + GNUTAR_uname_offset, p, copy_length); } /* TODO: How does GNU tar handle gnames longer than GNUTAR_gname_size? */ if (tartype == 'K' || tartype == 'L') { p = archive_entry_gname(entry); copy_length = strlen(p); } else { p = gnutar->gname; copy_length = gnutar->gname_length; } if (copy_length > 0) { if (strlen(p) > GNUTAR_gname_size) copy_length = GNUTAR_gname_size; memcpy(h + GNUTAR_gname_offset, p, copy_length); } /* By truncating the mode here, we ensure it always fits. */ format_octal(archive_entry_mode(entry) & 07777, h + GNUTAR_mode_offset, GNUTAR_mode_size); /* GNU tar supports base-256 here, so should never overflow. */ if (format_number(archive_entry_uid(entry), h + GNUTAR_uid_offset, GNUTAR_uid_size, GNUTAR_uid_max_size)) { archive_set_error(&a->archive, ERANGE, "Numeric user ID %jd too large", (intmax_t)archive_entry_uid(entry)); ret = ARCHIVE_FAILED; } /* GNU tar supports base-256 here, so should never overflow. */ if (format_number(archive_entry_gid(entry), h + GNUTAR_gid_offset, GNUTAR_gid_size, GNUTAR_gid_max_size)) { archive_set_error(&a->archive, ERANGE, "Numeric group ID %jd too large", (intmax_t)archive_entry_gid(entry)); ret = ARCHIVE_FAILED; } /* GNU tar supports base-256 here, so should never overflow. */ if (format_number(archive_entry_size(entry), h + GNUTAR_size_offset, GNUTAR_size_size, GNUTAR_size_max_size)) { archive_set_error(&a->archive, ERANGE, "File size out of range"); ret = ARCHIVE_FAILED; } /* Shouldn't overflow before 2106, since mtime field is 33 bits. */ format_octal(archive_entry_mtime(entry), h + GNUTAR_mtime_offset, GNUTAR_mtime_size); if (archive_entry_filetype(entry) == AE_IFBLK || archive_entry_filetype(entry) == AE_IFCHR) { if (format_octal(archive_entry_rdevmajor(entry), h + GNUTAR_rdevmajor_offset, GNUTAR_rdevmajor_size)) { archive_set_error(&a->archive, ERANGE, "Major device number too large"); ret = ARCHIVE_FAILED; } if (format_octal(archive_entry_rdevminor(entry), h + GNUTAR_rdevminor_offset, GNUTAR_rdevminor_size)) { archive_set_error(&a->archive, ERANGE, "Minor device number too large"); ret = ARCHIVE_FAILED; } } h[GNUTAR_typeflag_offset] = tartype; checksum = 0; for (i = 0; i < 512; i++) checksum += 255 & (unsigned int)h[i]; h[GNUTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */ /* h[GNUTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */ format_octal(checksum, h + GNUTAR_checksum_offset, 6); return (ret); } /* * Format a number into a field, falling back to base-256 if necessary. */ static int format_number(int64_t v, char *p, int s, int maxsize) { int64_t limit = ((int64_t)1 << (s*3)); if (v < limit) return (format_octal(v, p, s)); return (format_256(v, p, maxsize)); } /* * Format a number into the specified field using base-256. */ static int format_256(int64_t v, char *p, int s) { p += s; while (s-- > 0) { *--p = (char)(v & 0xff); v >>= 8; } *p |= 0x80; /* Set the base-256 marker bit. */ return (0); } /* * Format a number into the specified field using octal. */ static int format_octal(int64_t v, char *p, int s) { int len = s; /* Octal values can't be negative, so use 0. */ if (v < 0) v = 0; p += s; /* Start at the end and work backwards. */ while (s-- > 0) { *--p = (char)('0' + (v & 7)); v >>= 3; } if (v == 0) return (0); /* If it overflowed, fill field with max value. */ while (len-- > 0) *p++ = '7'; return (-1); } Index: stable/10/contrib/libarchive/libarchive/archive_write_set_format_shar.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_set_format_shar.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_write_set_format_shar.c (revision 344674) @@ -1,642 +1,640 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2008 Joerg Sonnenberger * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_private.h" #include "archive_write_private.h" struct shar { int dump; int end_of_line; struct archive_entry *entry; int has_data; char *last_dir; /* Line buffer for uuencoded dump format */ char outbuff[45]; size_t outpos; int wrote_header; struct archive_string work; struct archive_string quoted_name; }; static int archive_write_shar_close(struct archive_write *); static int archive_write_shar_free(struct archive_write *); static int archive_write_shar_header(struct archive_write *, struct archive_entry *); static ssize_t archive_write_shar_data_sed(struct archive_write *, const void * buff, size_t); static ssize_t archive_write_shar_data_uuencode(struct archive_write *, const void * buff, size_t); static int archive_write_shar_finish_entry(struct archive_write *); /* * Copy the given string to the buffer, quoting all shell meta characters * found. */ static void shar_quote(struct archive_string *buf, const char *str, int in_shell) { static const char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; size_t len; while (*str != '\0') { if ((len = strcspn(str, meta)) != 0) { archive_strncat(buf, str, len); str += len; } else if (*str == '\n') { if (in_shell) archive_strcat(buf, "\"\n\""); else archive_strcat(buf, "\\n"); ++str; } else { archive_strappend_char(buf, '\\'); archive_strappend_char(buf, *str); ++str; } } } /* * Set output format to 'shar' format. */ int archive_write_set_format_shar(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct shar *shar; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_shar"); /* If someone else was already registered, unregister them. */ if (a->format_free != NULL) (a->format_free)(a); shar = (struct shar *)calloc(1, sizeof(*shar)); if (shar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate shar data"); return (ARCHIVE_FATAL); } archive_string_init(&shar->work); archive_string_init(&shar->quoted_name); a->format_data = shar; a->format_name = "shar"; a->format_write_header = archive_write_shar_header; a->format_close = archive_write_shar_close; a->format_free = archive_write_shar_free; a->format_write_data = archive_write_shar_data_sed; a->format_finish_entry = archive_write_shar_finish_entry; a->archive.archive_format = ARCHIVE_FORMAT_SHAR_BASE; a->archive.archive_format_name = "shar"; return (ARCHIVE_OK); } /* * An alternate 'shar' that uses uudecode instead of 'sed' to encode * file contents and can therefore be used to archive binary files. * In addition, this variant also attempts to restore ownership, file modes, * and other extended file information. */ int archive_write_set_format_shar_dump(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct shar *shar; archive_write_set_format_shar(&a->archive); shar = (struct shar *)a->format_data; shar->dump = 1; a->format_write_data = archive_write_shar_data_uuencode; a->archive.archive_format = ARCHIVE_FORMAT_SHAR_DUMP; a->archive.archive_format_name = "shar dump"; return (ARCHIVE_OK); } static int archive_write_shar_header(struct archive_write *a, struct archive_entry *entry) { const char *linkname; const char *name; char *p, *pp; struct shar *shar; shar = (struct shar *)a->format_data; if (!shar->wrote_header) { archive_strcat(&shar->work, "#!/bin/sh\n"); archive_strcat(&shar->work, "# This is a shell archive\n"); shar->wrote_header = 1; } /* Save the entry for the closing. */ - if (shar->entry) - archive_entry_free(shar->entry); + archive_entry_free(shar->entry); shar->entry = archive_entry_clone(entry); name = archive_entry_pathname(entry); /* Handle some preparatory issues. */ switch(archive_entry_filetype(entry)) { case AE_IFREG: /* Only regular files have non-zero size. */ break; case AE_IFDIR: archive_entry_set_size(entry, 0); /* Don't bother trying to recreate '.' */ if (strcmp(name, ".") == 0 || strcmp(name, "./") == 0) return (ARCHIVE_OK); break; case AE_IFIFO: case AE_IFCHR: case AE_IFBLK: /* All other file types have zero size in the archive. */ archive_entry_set_size(entry, 0); break; default: archive_entry_set_size(entry, 0); if (archive_entry_hardlink(entry) == NULL && archive_entry_symlink(entry) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "shar format cannot archive this"); return (ARCHIVE_WARN); } } archive_string_empty(&shar->quoted_name); shar_quote(&shar->quoted_name, name, 1); /* Stock preparation for all file types. */ archive_string_sprintf(&shar->work, "echo x %s\n", shar->quoted_name.s); if (archive_entry_filetype(entry) != AE_IFDIR) { /* Try to create the dir. */ p = strdup(name); pp = strrchr(p, '/'); /* If there is a / character, try to create the dir. */ if (pp != NULL) { *pp = '\0'; /* Try to avoid a lot of redundant mkdir commands. */ if (strcmp(p, ".") == 0) { /* Don't try to "mkdir ." */ free(p); } else if (shar->last_dir == NULL) { archive_strcat(&shar->work, "mkdir -p "); shar_quote(&shar->work, p, 1); archive_strcat(&shar->work, " > /dev/null 2>&1\n"); shar->last_dir = p; } else if (strcmp(p, shar->last_dir) == 0) { /* We've already created this exact dir. */ free(p); } else if (strlen(p) < strlen(shar->last_dir) && strncmp(p, shar->last_dir, strlen(p)) == 0) { /* We've already created a subdir. */ free(p); } else { archive_strcat(&shar->work, "mkdir -p "); shar_quote(&shar->work, p, 1); archive_strcat(&shar->work, " > /dev/null 2>&1\n"); shar->last_dir = p; } } else { free(p); } } /* Handle file-type specific issues. */ shar->has_data = 0; if ((linkname = archive_entry_hardlink(entry)) != NULL) { archive_strcat(&shar->work, "ln -f "); shar_quote(&shar->work, linkname, 1); archive_string_sprintf(&shar->work, " %s\n", shar->quoted_name.s); } else if ((linkname = archive_entry_symlink(entry)) != NULL) { archive_strcat(&shar->work, "ln -fs "); shar_quote(&shar->work, linkname, 1); archive_string_sprintf(&shar->work, " %s\n", shar->quoted_name.s); } else { switch(archive_entry_filetype(entry)) { case AE_IFREG: if (archive_entry_size(entry) == 0) { /* More portable than "touch." */ archive_string_sprintf(&shar->work, "test -e \"%s\" || :> \"%s\"\n", shar->quoted_name.s, shar->quoted_name.s); } else { if (shar->dump) { unsigned int mode = archive_entry_mode(entry) & 0777; archive_string_sprintf(&shar->work, "uudecode -p > %s << 'SHAR_END'\n", shar->quoted_name.s); archive_string_sprintf(&shar->work, "begin %o ", mode); shar_quote(&shar->work, name, 0); archive_strcat(&shar->work, "\n"); } else { archive_string_sprintf(&shar->work, "sed 's/^X//' > %s << 'SHAR_END'\n", shar->quoted_name.s); } shar->has_data = 1; shar->end_of_line = 1; shar->outpos = 0; } break; case AE_IFDIR: archive_string_sprintf(&shar->work, "mkdir -p %s > /dev/null 2>&1\n", shar->quoted_name.s); /* Record that we just created this directory. */ - if (shar->last_dir != NULL) - free(shar->last_dir); + free(shar->last_dir); shar->last_dir = strdup(name); /* Trim a trailing '/'. */ pp = strrchr(shar->last_dir, '/'); if (pp != NULL && pp[1] == '\0') *pp = '\0'; /* * TODO: Put dir name/mode on a list to be fixed * up at end of archive. */ break; case AE_IFIFO: archive_string_sprintf(&shar->work, "mkfifo %s\n", shar->quoted_name.s); break; case AE_IFCHR: archive_string_sprintf(&shar->work, "mknod %s c %ju %ju\n", shar->quoted_name.s, (uintmax_t)archive_entry_rdevmajor(entry), (uintmax_t)archive_entry_rdevminor(entry)); break; case AE_IFBLK: archive_string_sprintf(&shar->work, "mknod %s b %ju %ju\n", shar->quoted_name.s, (uintmax_t)archive_entry_rdevmajor(entry), (uintmax_t)archive_entry_rdevminor(entry)); break; default: return (ARCHIVE_WARN); } } return (ARCHIVE_OK); } static ssize_t archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n) { static const size_t ensured = 65533; struct shar *shar; const char *src; char *buf, *buf_end; int ret; size_t written = n; shar = (struct shar *)a->format_data; if (!shar->has_data || n == 0) return (0); src = (const char *)buff; /* * ensure is the number of bytes in buffer before expanding the * current character. Each operation writes the current character * and optionally the start-of-new-line marker. This can happen * twice before entering the loop, so make sure three additional * bytes can be written. */ if (archive_string_ensure(&shar->work, ensured + 3) == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } if (shar->work.length > ensured) { ret = __archive_write_output(a, shar->work.s, shar->work.length); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); archive_string_empty(&shar->work); } buf = shar->work.s + shar->work.length; buf_end = shar->work.s + ensured; if (shar->end_of_line) { *buf++ = 'X'; shar->end_of_line = 0; } while (n-- != 0) { if ((*buf++ = *src++) == '\n') { if (n == 0) shar->end_of_line = 1; else *buf++ = 'X'; } if (buf >= buf_end) { shar->work.length = buf - shar->work.s; ret = __archive_write_output(a, shar->work.s, shar->work.length); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); archive_string_empty(&shar->work); buf = shar->work.s; } } shar->work.length = buf - shar->work.s; return (written); } #define UUENC(c) (((c)!=0) ? ((c) & 077) + ' ': '`') static void uuencode_group(const char _in[3], char out[4]) { const unsigned char *in = (const unsigned char *)_in; int t; t = (in[0] << 16) | (in[1] << 8) | in[2]; out[0] = UUENC( 0x3f & (t >> 18) ); out[1] = UUENC( 0x3f & (t >> 12) ); out[2] = UUENC( 0x3f & (t >> 6) ); out[3] = UUENC( 0x3f & t ); } static int _uuencode_line(struct archive_write *a, struct shar *shar, const char *inbuf, size_t len) { char *buf; size_t alloc_len; /* len <= 45 -> expanded to 60 + len byte + new line */ alloc_len = shar->work.length + 62; if (archive_string_ensure(&shar->work, alloc_len) == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } buf = shar->work.s + shar->work.length; *buf++ = UUENC(len); while (len >= 3) { uuencode_group(inbuf, buf); len -= 3; inbuf += 3; buf += 4; } if (len != 0) { char tmp_buf[3]; tmp_buf[0] = inbuf[0]; if (len == 1) tmp_buf[1] = '\0'; else tmp_buf[1] = inbuf[1]; tmp_buf[2] = '\0'; uuencode_group(tmp_buf, buf); buf += 4; } *buf++ = '\n'; if ((buf - shar->work.s) > (ptrdiff_t)(shar->work.length + 62)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Buffer overflow"); return (ARCHIVE_FATAL); } shar->work.length = buf - shar->work.s; return (ARCHIVE_OK); } #define uuencode_line(__a, __shar, __inbuf, __len) \ do { \ int r = _uuencode_line(__a, __shar, __inbuf, __len); \ if (r != ARCHIVE_OK) \ return (ARCHIVE_FATAL); \ } while (0) static ssize_t archive_write_shar_data_uuencode(struct archive_write *a, const void *buff, size_t length) { struct shar *shar; const char *src; size_t n; int ret; shar = (struct shar *)a->format_data; if (!shar->has_data) return (ARCHIVE_OK); src = (const char *)buff; if (shar->outpos != 0) { n = 45 - shar->outpos; if (n > length) n = length; memcpy(shar->outbuff + shar->outpos, src, n); if (shar->outpos + n < 45) { shar->outpos += n; return length; } uuencode_line(a, shar, shar->outbuff, 45); src += n; n = length - n; } else { n = length; } while (n >= 45) { uuencode_line(a, shar, src, 45); src += 45; n -= 45; if (shar->work.length < 65536) continue; ret = __archive_write_output(a, shar->work.s, shar->work.length); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); archive_string_empty(&shar->work); } if (n != 0) { memcpy(shar->outbuff, src, n); shar->outpos = n; } return (length); } static int archive_write_shar_finish_entry(struct archive_write *a) { const char *g, *p, *u; struct shar *shar; int ret; shar = (struct shar *)a->format_data; if (shar->entry == NULL) return (0); if (shar->dump) { /* Finish uuencoded data. */ if (shar->has_data) { if (shar->outpos > 0) uuencode_line(a, shar, shar->outbuff, shar->outpos); archive_strcat(&shar->work, "`\nend\n"); archive_strcat(&shar->work, "SHAR_END\n"); } /* Restore file mode, owner, flags. */ /* * TODO: Don't immediately restore mode for * directories; defer that to end of script. */ archive_string_sprintf(&shar->work, "chmod %o ", (unsigned int)(archive_entry_mode(shar->entry) & 07777)); shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1); archive_strcat(&shar->work, "\n"); u = archive_entry_uname(shar->entry); g = archive_entry_gname(shar->entry); if (u != NULL || g != NULL) { archive_strcat(&shar->work, "chown "); if (u != NULL) shar_quote(&shar->work, u, 1); if (g != NULL) { archive_strcat(&shar->work, ":"); shar_quote(&shar->work, g, 1); } archive_strcat(&shar->work, " "); shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1); archive_strcat(&shar->work, "\n"); } if ((p = archive_entry_fflags_text(shar->entry)) != NULL) { archive_string_sprintf(&shar->work, "chflags %s ", p); shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1); archive_strcat(&shar->work, "\n"); } /* TODO: restore ACLs */ } else { if (shar->has_data) { /* Finish sed-encoded data: ensure last line ends. */ if (!shar->end_of_line) archive_strappend_char(&shar->work, '\n'); archive_strcat(&shar->work, "SHAR_END\n"); } } archive_entry_free(shar->entry); shar->entry = NULL; if (shar->work.length < 65536) return (ARCHIVE_OK); ret = __archive_write_output(a, shar->work.s, shar->work.length); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); archive_string_empty(&shar->work); return (ARCHIVE_OK); } static int archive_write_shar_close(struct archive_write *a) { struct shar *shar; int ret; /* * TODO: Accumulate list of directory names/modes and * fix them all up at end-of-archive. */ shar = (struct shar *)a->format_data; /* * Only write the end-of-archive markers if the archive was * actually started. This avoids problems if someone sets * shar format, then sets another format (which would invoke * shar_finish to free the format-specific data). */ if (shar->wrote_header == 0) return (ARCHIVE_OK); archive_strcat(&shar->work, "exit\n"); ret = __archive_write_output(a, shar->work.s, shar->work.length); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Shar output is never padded. */ archive_write_set_bytes_in_last_block(&a->archive, 1); /* * TODO: shar should also suppress padding of * uncompressed data within gzip/bzip2 streams. */ return (ARCHIVE_OK); } static int archive_write_shar_free(struct archive_write *a) { struct shar *shar; shar = (struct shar *)a->format_data; if (shar == NULL) return (ARCHIVE_OK); archive_entry_free(shar->entry); free(shar->last_dir); archive_string_free(&(shar->work)); archive_string_free(&(shar->quoted_name)); free(shar); a->format_data = NULL; return (ARCHIVE_OK); } Index: stable/10/contrib/libarchive/libarchive/archive_write_set_format_ustar.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_set_format_ustar.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_write_set_format_ustar.c (revision 344674) @@ -1,763 +1,760 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2011-2012 Michihiro NAKAJIMA * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_write_private.h" struct ustar { uint64_t entry_bytes_remaining; uint64_t entry_padding; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_default; int init_default_conversion; }; /* * Define structure of POSIX 'ustar' tar header. */ #define USTAR_name_offset 0 #define USTAR_name_size 100 #define USTAR_mode_offset 100 #define USTAR_mode_size 6 #define USTAR_mode_max_size 8 #define USTAR_uid_offset 108 #define USTAR_uid_size 6 #define USTAR_uid_max_size 8 #define USTAR_gid_offset 116 #define USTAR_gid_size 6 #define USTAR_gid_max_size 8 #define USTAR_size_offset 124 #define USTAR_size_size 11 #define USTAR_size_max_size 12 #define USTAR_mtime_offset 136 #define USTAR_mtime_size 11 #define USTAR_mtime_max_size 11 #define USTAR_checksum_offset 148 #define USTAR_checksum_size 8 #define USTAR_typeflag_offset 156 #define USTAR_typeflag_size 1 #define USTAR_linkname_offset 157 #define USTAR_linkname_size 100 #define USTAR_magic_offset 257 #define USTAR_magic_size 6 #define USTAR_version_offset 263 #define USTAR_version_size 2 #define USTAR_uname_offset 265 #define USTAR_uname_size 32 #define USTAR_gname_offset 297 #define USTAR_gname_size 32 #define USTAR_rdevmajor_offset 329 #define USTAR_rdevmajor_size 6 #define USTAR_rdevmajor_max_size 8 #define USTAR_rdevminor_offset 337 #define USTAR_rdevminor_size 6 #define USTAR_rdevminor_max_size 8 #define USTAR_prefix_offset 345 #define USTAR_prefix_size 155 #define USTAR_padding_offset 500 #define USTAR_padding_size 12 /* * A filled-in copy of the header for initialization. */ static const char template_header[] = { /* name: 100 bytes */ 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,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,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0, /* Mode, space-null termination: 8 bytes */ '0','0','0','0','0','0', ' ','\0', /* uid, space-null termination: 8 bytes */ '0','0','0','0','0','0', ' ','\0', /* gid, space-null termination: 8 bytes */ '0','0','0','0','0','0', ' ','\0', /* size, space termination: 12 bytes */ '0','0','0','0','0','0','0','0','0','0','0', ' ', /* mtime, space termination: 12 bytes */ '0','0','0','0','0','0','0','0','0','0','0', ' ', /* Initial checksum value: 8 spaces */ ' ',' ',' ',' ',' ',' ',' ',' ', /* Typeflag: 1 byte */ '0', /* '0' = regular file */ /* Linkname: 100 bytes */ 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,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,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0, /* Magic: 6 bytes, Version: 2 bytes */ 'u','s','t','a','r','\0', '0','0', /* Uname: 32 bytes */ 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, /* Gname: 32 bytes */ 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, /* rdevmajor + space/null padding: 8 bytes */ '0','0','0','0','0','0', ' ','\0', /* rdevminor + space/null padding: 8 bytes */ '0','0','0','0','0','0', ' ','\0', /* Prefix: 155 bytes */ 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,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,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,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, /* Padding: 12 bytes */ 0,0,0,0,0,0,0,0, 0,0,0,0 }; static ssize_t archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s); static int archive_write_ustar_free(struct archive_write *); static int archive_write_ustar_close(struct archive_write *); static int archive_write_ustar_finish_entry(struct archive_write *); static int archive_write_ustar_header(struct archive_write *, struct archive_entry *entry); static int archive_write_ustar_options(struct archive_write *, const char *, const char *); static int format_256(int64_t, char *, int); static int format_number(int64_t, char *, int size, int max, int strict); static int format_octal(int64_t, char *, int); /* * Set output format to 'ustar' format. */ int archive_write_set_format_ustar(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct ustar *ustar; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_ustar"); /* If someone else was already registered, unregister them. */ if (a->format_free != NULL) (a->format_free)(a); /* Basic internal sanity test. */ if (sizeof(template_header) != 512) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal: template_header wrong size: %zu should be 512", sizeof(template_header)); return (ARCHIVE_FATAL); } ustar = (struct ustar *)calloc(1, sizeof(*ustar)); if (ustar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); return (ARCHIVE_FATAL); } a->format_data = ustar; a->format_name = "ustar"; a->format_options = archive_write_ustar_options; a->format_write_header = archive_write_ustar_header; a->format_write_data = archive_write_ustar_data; a->format_close = archive_write_ustar_close; a->format_free = archive_write_ustar_free; a->format_finish_entry = archive_write_ustar_finish_entry; a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR; a->archive.archive_format_name = "POSIX ustar"; return (ARCHIVE_OK); } static int archive_write_ustar_options(struct archive_write *a, const char *key, const char *val) { struct ustar *ustar = (struct ustar *)a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: hdrcharset option needs a character-set name", a->format_name); else { ustar->opt_sconv = archive_string_conversion_to_charset( &a->archive, val, 0); if (ustar->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) { char buff[512]; int ret, ret2; struct ustar *ustar; struct archive_entry *entry_main; struct archive_string_conv *sconv; ustar = (struct ustar *)a->format_data; /* Setup default string conversion. */ if (ustar->opt_sconv == NULL) { if (!ustar->init_default_conversion) { ustar->sconv_default = archive_string_default_conversion_for_write(&(a->archive)); ustar->init_default_conversion = 1; } sconv = ustar->sconv_default; } else sconv = ustar->opt_sconv; /* Sanity check. */ if (archive_entry_pathname(entry) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't record entry in tar file without pathname"); return (ARCHIVE_FAILED); } /* Only regular files (not hardlinks) have data. */ if (archive_entry_hardlink(entry) != NULL || archive_entry_symlink(entry) != NULL || !(archive_entry_filetype(entry) == AE_IFREG)) archive_entry_set_size(entry, 0); if (AE_IFDIR == archive_entry_filetype(entry)) { const char *p; size_t path_length; /* * Ensure a trailing '/'. Modify the entry so * the client sees the change. */ #if defined(_WIN32) && !defined(__CYGWIN__) const wchar_t *wp; wp = archive_entry_pathname_w(entry); if (wp != NULL && wp[wcslen(wp) -1] != L'/') { struct archive_wstring ws; archive_string_init(&ws); path_length = wcslen(wp); if (archive_wstring_ensure(&ws, path_length + 2) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); archive_wstring_free(&ws); return(ARCHIVE_FATAL); } /* Should we keep '\' ? */ if (wp[path_length -1] == L'\\') path_length--; archive_wstrncpy(&ws, wp, path_length); archive_wstrappend_wchar(&ws, L'/'); archive_entry_copy_pathname_w(entry, ws.s); archive_wstring_free(&ws); p = NULL; } else #endif p = archive_entry_pathname(entry); /* * On Windows, this is a backup operation just in * case getting WCS failed. On POSIX, this is a * normal operation. */ if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') { struct archive_string as; archive_string_init(&as); path_length = strlen(p); if (archive_string_ensure(&as, path_length + 2) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); archive_string_free(&as); return(ARCHIVE_FATAL); } #if defined(_WIN32) && !defined(__CYGWIN__) /* NOTE: This might break the pathname * if the current code page is CP932 and * the pathname includes a character '\' * as a part of its multibyte pathname. */ if (p[strlen(p) -1] == '\\') path_length--; else #endif archive_strncpy(&as, p, path_length); archive_strappend_char(&as, '/'); archive_entry_copy_pathname(entry, as.s); archive_string_free(&as); } } #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); return(ARCHIVE_FATAL); } if (entry != entry_main) entry = entry_main; else entry_main = NULL; #else entry_main = NULL; #endif ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1, sconv); if (ret < ARCHIVE_WARN) { - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret); } ret2 = __archive_write_output(a, buff, 512); if (ret2 < ARCHIVE_WARN) { - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret2); } if (ret2 < ret) ret = ret2; ustar->entry_bytes_remaining = archive_entry_size(entry); ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining); - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret); } /* * Format a basic 512-byte "ustar" header. * * Returns -1 if format failed (due to field overflow). * Note that this always formats as much of the header as possible. * If "strict" is set to zero, it will extend numeric fields as * necessary (overwriting terminators or using base-256 extensions). * * This is exported so that other 'tar' formats can use it. */ int __archive_write_format_header_ustar(struct archive_write *a, char h[512], struct archive_entry *entry, int tartype, int strict, struct archive_string_conv *sconv) { unsigned int checksum; int i, r, ret; size_t copy_length; const char *p, *pp; int mytartype; ret = 0; mytartype = -1; /* * The "template header" already includes the "ustar" * signature, various end-of-field markers and other required * elements. */ memcpy(h, &template_header, 512); /* * Because the block is already null-filled, and strings * are allowed to exactly fill their destination (without null), * I use memcpy(dest, src, strlen()) here a lot to copy strings. */ r = archive_entry_pathname_l(entry, &pp, ©_length, sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to %s", pp, archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; } if (copy_length <= USTAR_name_size) memcpy(h + USTAR_name_offset, pp, copy_length); else { /* Store in two pieces, splitting at a '/'. */ p = strchr(pp + copy_length - USTAR_name_size - 1, '/'); /* * Look for the next '/' if we chose the first character * as the separator. (ustar format doesn't permit * an empty prefix.) */ if (p == pp) p = strchr(p + 1, '/'); /* Fail if the name won't fit. */ if (!p) { /* No separator. */ archive_set_error(&a->archive, ENAMETOOLONG, "Pathname too long"); ret = ARCHIVE_FAILED; } else if (p[1] == '\0') { /* * The only feasible separator is a final '/'; * this would result in a non-empty prefix and * an empty name, which POSIX doesn't * explicitly forbid, but it just feels wrong. */ archive_set_error(&a->archive, ENAMETOOLONG, "Pathname too long"); ret = ARCHIVE_FAILED; } else if (p > pp + USTAR_prefix_size) { /* Prefix is too long. */ archive_set_error(&a->archive, ENAMETOOLONG, "Pathname too long"); ret = ARCHIVE_FAILED; } else { /* Copy prefix and remainder to appropriate places */ memcpy(h + USTAR_prefix_offset, pp, p - pp); memcpy(h + USTAR_name_offset, p + 1, pp + copy_length - p - 1); } } r = archive_entry_hardlink_l(entry, &p, ©_length, sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", p, archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; } if (copy_length > 0) mytartype = '1'; else { r = archive_entry_symlink_l(entry, &p, ©_length, sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", p, archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; } } if (copy_length > 0) { if (copy_length > USTAR_linkname_size) { archive_set_error(&a->archive, ENAMETOOLONG, "Link contents too long"); ret = ARCHIVE_FAILED; copy_length = USTAR_linkname_size; } memcpy(h + USTAR_linkname_offset, p, copy_length); } r = archive_entry_uname_l(entry, &p, ©_length, sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Uname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate uname '%s' to %s", p, archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; } if (copy_length > 0) { if (copy_length > USTAR_uname_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Username too long"); ret = ARCHIVE_FAILED; copy_length = USTAR_uname_size; } memcpy(h + USTAR_uname_offset, p, copy_length); } r = archive_entry_gname_l(entry, &p, ©_length, sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Gname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate gname '%s' to %s", p, archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; } if (copy_length > 0) { if (strlen(p) > USTAR_gname_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Group name too long"); ret = ARCHIVE_FAILED; copy_length = USTAR_gname_size; } memcpy(h + USTAR_gname_offset, p, copy_length); } if (format_number(archive_entry_mode(entry) & 07777, h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Numeric mode too large"); ret = ARCHIVE_FAILED; } if (format_number(archive_entry_uid(entry), h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Numeric user ID too large"); ret = ARCHIVE_FAILED; } if (format_number(archive_entry_gid(entry), h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Numeric group ID too large"); ret = ARCHIVE_FAILED; } if (format_number(archive_entry_size(entry), h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "File size out of range"); ret = ARCHIVE_FAILED; } if (format_number(archive_entry_mtime(entry), h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "File modification time too large"); ret = ARCHIVE_FAILED; } if (archive_entry_filetype(entry) == AE_IFBLK || archive_entry_filetype(entry) == AE_IFCHR) { if (format_number(archive_entry_rdevmajor(entry), h + USTAR_rdevmajor_offset, USTAR_rdevmajor_size, USTAR_rdevmajor_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Major device number too large"); ret = ARCHIVE_FAILED; } if (format_number(archive_entry_rdevminor(entry), h + USTAR_rdevminor_offset, USTAR_rdevminor_size, USTAR_rdevminor_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Minor device number too large"); ret = ARCHIVE_FAILED; } } if (tartype >= 0) { h[USTAR_typeflag_offset] = tartype; } else if (mytartype >= 0) { h[USTAR_typeflag_offset] = mytartype; } else { switch (archive_entry_filetype(entry)) { case AE_IFREG: h[USTAR_typeflag_offset] = '0' ; break; case AE_IFLNK: h[USTAR_typeflag_offset] = '2' ; break; case AE_IFCHR: h[USTAR_typeflag_offset] = '3' ; break; case AE_IFBLK: h[USTAR_typeflag_offset] = '4' ; break; case AE_IFDIR: h[USTAR_typeflag_offset] = '5' ; break; case AE_IFIFO: h[USTAR_typeflag_offset] = '6' ; break; case AE_IFSOCK: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive socket"); return (ARCHIVE_FAILED); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive this (mode=0%lo)", (unsigned long)archive_entry_mode(entry)); ret = ARCHIVE_FAILED; } } checksum = 0; for (i = 0; i < 512; i++) checksum += 255 & (unsigned int)h[i]; h[USTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */ /* h[USTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */ format_octal(checksum, h + USTAR_checksum_offset, 6); return (ret); } /* * Format a number into a field, with some intelligence. */ static int format_number(int64_t v, char *p, int s, int maxsize, int strict) { int64_t limit; limit = ((int64_t)1 << (s*3)); /* "Strict" only permits octal values with proper termination. */ if (strict) return (format_octal(v, p, s)); /* * In non-strict mode, we allow the number to overwrite one or * more bytes of the field termination. Even old tar * implementations should be able to handle this with no * problem. */ if (v >= 0) { while (s <= maxsize) { if (v < limit) return (format_octal(v, p, s)); s++; limit <<= 3; } } /* Base-256 can handle any number, positive or negative. */ return (format_256(v, p, maxsize)); } /* * Format a number into the specified field using base-256. */ static int format_256(int64_t v, char *p, int s) { p += s; while (s-- > 0) { *--p = (char)(v & 0xff); v >>= 8; } *p |= 0x80; /* Set the base-256 marker bit. */ return (0); } /* * Format a number into the specified field. */ static int format_octal(int64_t v, char *p, int s) { int len; len = s; /* Octal values can't be negative, so use 0. */ if (v < 0) { while (len-- > 0) *p++ = '0'; return (-1); } p += s; /* Start at the end and work backwards. */ while (s-- > 0) { *--p = (char)('0' + (v & 7)); v >>= 3; } if (v == 0) return (0); /* If it overflowed, fill field with max value. */ while (len-- > 0) *p++ = '7'; return (-1); } static int archive_write_ustar_close(struct archive_write *a) { return (__archive_write_nulls(a, 512*2)); } static int archive_write_ustar_free(struct archive_write *a) { struct ustar *ustar; ustar = (struct ustar *)a->format_data; free(ustar); a->format_data = NULL; return (ARCHIVE_OK); } static int archive_write_ustar_finish_entry(struct archive_write *a) { struct ustar *ustar; int ret; ustar = (struct ustar *)a->format_data; ret = __archive_write_nulls(a, (size_t)(ustar->entry_bytes_remaining + ustar->entry_padding)); ustar->entry_bytes_remaining = ustar->entry_padding = 0; return (ret); } static ssize_t archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s) { struct ustar *ustar; int ret; ustar = (struct ustar *)a->format_data; if (s > ustar->entry_bytes_remaining) s = (size_t)ustar->entry_bytes_remaining; ret = __archive_write_output(a, buff, s); ustar->entry_bytes_remaining -= s; if (ret != ARCHIVE_OK) return (ret); return (s); } Index: stable/10/contrib/libarchive/libarchive/archive_write_set_format_v7tar.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_set_format_v7tar.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_write_set_format_v7tar.c (revision 344674) @@ -1,660 +1,657 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2011-2012 Michihiro NAKAJIMA * 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 "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_write_private.h" struct v7tar { uint64_t entry_bytes_remaining; uint64_t entry_padding; struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_default; int init_default_conversion; }; /* * Define structure of POSIX 'v7tar' tar header. */ #define V7TAR_name_offset 0 #define V7TAR_name_size 100 #define V7TAR_mode_offset 100 #define V7TAR_mode_size 6 #define V7TAR_mode_max_size 8 #define V7TAR_uid_offset 108 #define V7TAR_uid_size 6 #define V7TAR_uid_max_size 8 #define V7TAR_gid_offset 116 #define V7TAR_gid_size 6 #define V7TAR_gid_max_size 8 #define V7TAR_size_offset 124 #define V7TAR_size_size 11 #define V7TAR_size_max_size 12 #define V7TAR_mtime_offset 136 #define V7TAR_mtime_size 11 #define V7TAR_mtime_max_size 12 #define V7TAR_checksum_offset 148 #define V7TAR_checksum_size 8 #define V7TAR_typeflag_offset 156 #define V7TAR_typeflag_size 1 #define V7TAR_linkname_offset 157 #define V7TAR_linkname_size 100 #define V7TAR_padding_offset 257 #define V7TAR_padding_size 255 /* * A filled-in copy of the header for initialization. */ static const char template_header[] = { /* name: 100 bytes */ 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,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,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0, /* Mode, space-null termination: 8 bytes */ '0','0','0','0','0','0', ' ','\0', /* uid, space-null termination: 8 bytes */ '0','0','0','0','0','0', ' ','\0', /* gid, space-null termination: 8 bytes */ '0','0','0','0','0','0', ' ','\0', /* size, space termination: 12 bytes */ '0','0','0','0','0','0','0','0','0','0','0', ' ', /* mtime, space termination: 12 bytes */ '0','0','0','0','0','0','0','0','0','0','0', ' ', /* Initial checksum value: 8 spaces */ ' ',' ',' ',' ',' ',' ',' ',' ', /* Typeflag: 1 byte */ 0, /* Linkname: 100 bytes */ 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,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,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0, /* Padding: 255 bytes */ 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,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,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,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, 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,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,0,0,0 }; static ssize_t archive_write_v7tar_data(struct archive_write *a, const void *buff, size_t s); static int archive_write_v7tar_free(struct archive_write *); static int archive_write_v7tar_close(struct archive_write *); static int archive_write_v7tar_finish_entry(struct archive_write *); static int archive_write_v7tar_header(struct archive_write *, struct archive_entry *entry); static int archive_write_v7tar_options(struct archive_write *, const char *, const char *); static int format_256(int64_t, char *, int); static int format_number(int64_t, char *, int size, int max, int strict); static int format_octal(int64_t, char *, int); static int format_header_v7tar(struct archive_write *, char h[512], struct archive_entry *, int, struct archive_string_conv *); /* * Set output format to 'v7tar' format. */ int archive_write_set_format_v7tar(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct v7tar *v7tar; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_v7tar"); /* If someone else was already registered, unregister them. */ if (a->format_free != NULL) (a->format_free)(a); /* Basic internal sanity test. */ if (sizeof(template_header) != 512) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Internal: template_header wrong size: %zu should be 512", sizeof(template_header)); return (ARCHIVE_FATAL); } v7tar = (struct v7tar *)calloc(1, sizeof(*v7tar)); if (v7tar == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate v7tar data"); return (ARCHIVE_FATAL); } a->format_data = v7tar; a->format_name = "tar (non-POSIX)"; a->format_options = archive_write_v7tar_options; a->format_write_header = archive_write_v7tar_header; a->format_write_data = archive_write_v7tar_data; a->format_close = archive_write_v7tar_close; a->format_free = archive_write_v7tar_free; a->format_finish_entry = archive_write_v7tar_finish_entry; a->archive.archive_format = ARCHIVE_FORMAT_TAR; a->archive.archive_format_name = "tar (non-POSIX)"; return (ARCHIVE_OK); } static int archive_write_v7tar_options(struct archive_write *a, const char *key, const char *val) { struct v7tar *v7tar = (struct v7tar *)a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: hdrcharset option needs a character-set name", a->format_name); else { v7tar->opt_sconv = archive_string_conversion_to_charset( &a->archive, val, 0); if (v7tar->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } static int archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry) { char buff[512]; int ret, ret2; struct v7tar *v7tar; struct archive_entry *entry_main; struct archive_string_conv *sconv; v7tar = (struct v7tar *)a->format_data; /* Setup default string conversion. */ if (v7tar->opt_sconv == NULL) { if (!v7tar->init_default_conversion) { v7tar->sconv_default = archive_string_default_conversion_for_write( &(a->archive)); v7tar->init_default_conversion = 1; } sconv = v7tar->sconv_default; } else sconv = v7tar->opt_sconv; /* Sanity check. */ if (archive_entry_pathname(entry) == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't record entry in tar file without pathname"); return (ARCHIVE_FAILED); } /* Only regular files (not hardlinks) have data. */ if (archive_entry_hardlink(entry) != NULL || archive_entry_symlink(entry) != NULL || !(archive_entry_filetype(entry) == AE_IFREG)) archive_entry_set_size(entry, 0); if (AE_IFDIR == archive_entry_filetype(entry)) { const char *p; size_t path_length; /* * Ensure a trailing '/'. Modify the entry so * the client sees the change. */ #if defined(_WIN32) && !defined(__CYGWIN__) const wchar_t *wp; wp = archive_entry_pathname_w(entry); if (wp != NULL && wp[wcslen(wp) -1] != L'/') { struct archive_wstring ws; archive_string_init(&ws); path_length = wcslen(wp); if (archive_wstring_ensure(&ws, path_length + 2) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate v7tar data"); archive_wstring_free(&ws); return(ARCHIVE_FATAL); } /* Should we keep '\' ? */ if (wp[path_length -1] == L'\\') path_length--; archive_wstrncpy(&ws, wp, path_length); archive_wstrappend_wchar(&ws, L'/'); archive_entry_copy_pathname_w(entry, ws.s); archive_wstring_free(&ws); p = NULL; } else #endif p = archive_entry_pathname(entry); /* * On Windows, this is a backup operation just in * case getting WCS failed. On POSIX, this is a * normal operation. */ if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') { struct archive_string as; archive_string_init(&as); path_length = strlen(p); if (archive_string_ensure(&as, path_length + 2) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate v7tar data"); archive_string_free(&as); return(ARCHIVE_FATAL); } #if defined(_WIN32) && !defined(__CYGWIN__) /* NOTE: This might break the pathname * if the current code page is CP932 and * the pathname includes a character '\' * as a part of its multibyte pathname. */ if (p[strlen(p) -1] == '\\') path_length--; else #endif archive_strncpy(&as, p, path_length); archive_strappend_char(&as, '/'); archive_entry_copy_pathname(entry, as.s); archive_string_free(&as); } } #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate v7tar data"); return(ARCHIVE_FATAL); } if (entry != entry_main) entry = entry_main; else entry_main = NULL; #else entry_main = NULL; #endif ret = format_header_v7tar(a, buff, entry, 1, sconv); if (ret < ARCHIVE_WARN) { - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret); } ret2 = __archive_write_output(a, buff, 512); if (ret2 < ARCHIVE_WARN) { - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret2); } if (ret2 < ret) ret = ret2; v7tar->entry_bytes_remaining = archive_entry_size(entry); v7tar->entry_padding = 0x1ff & (-(int64_t)v7tar->entry_bytes_remaining); - if (entry_main) - archive_entry_free(entry_main); + archive_entry_free(entry_main); return (ret); } /* * Format a basic 512-byte "v7tar" header. * * Returns -1 if format failed (due to field overflow). * Note that this always formats as much of the header as possible. * If "strict" is set to zero, it will extend numeric fields as * necessary (overwriting terminators or using base-256 extensions). * */ static int format_header_v7tar(struct archive_write *a, char h[512], struct archive_entry *entry, int strict, struct archive_string_conv *sconv) { unsigned int checksum; int i, r, ret; size_t copy_length; const char *p, *pp; int mytartype; ret = 0; mytartype = -1; /* * The "template header" already includes the "v7tar" * signature, various end-of-field markers and other required * elements. */ memcpy(h, &template_header, 512); /* * Because the block is already null-filled, and strings * are allowed to exactly fill their destination (without null), * I use memcpy(dest, src, strlen()) here a lot to copy strings. */ r = archive_entry_pathname_l(entry, &pp, ©_length, sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to %s", pp, archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; } if (strict && copy_length < V7TAR_name_size) memcpy(h + V7TAR_name_offset, pp, copy_length); else if (!strict && copy_length <= V7TAR_name_size) memcpy(h + V7TAR_name_offset, pp, copy_length); else { /* Prefix is too long. */ archive_set_error(&a->archive, ENAMETOOLONG, "Pathname too long"); ret = ARCHIVE_FAILED; } r = archive_entry_hardlink_l(entry, &p, ©_length, sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", p, archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; } if (copy_length > 0) mytartype = '1'; else { r = archive_entry_symlink_l(entry, &p, ©_length, sconv); if (r != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Linkname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", p, archive_string_conversion_charset_name(sconv)); ret = ARCHIVE_WARN; } } if (copy_length > 0) { if (copy_length >= V7TAR_linkname_size) { archive_set_error(&a->archive, ENAMETOOLONG, "Link contents too long"); ret = ARCHIVE_FAILED; copy_length = V7TAR_linkname_size; } memcpy(h + V7TAR_linkname_offset, p, copy_length); } if (format_number(archive_entry_mode(entry) & 07777, h + V7TAR_mode_offset, V7TAR_mode_size, V7TAR_mode_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Numeric mode too large"); ret = ARCHIVE_FAILED; } if (format_number(archive_entry_uid(entry), h + V7TAR_uid_offset, V7TAR_uid_size, V7TAR_uid_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Numeric user ID too large"); ret = ARCHIVE_FAILED; } if (format_number(archive_entry_gid(entry), h + V7TAR_gid_offset, V7TAR_gid_size, V7TAR_gid_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "Numeric group ID too large"); ret = ARCHIVE_FAILED; } if (format_number(archive_entry_size(entry), h + V7TAR_size_offset, V7TAR_size_size, V7TAR_size_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "File size out of range"); ret = ARCHIVE_FAILED; } if (format_number(archive_entry_mtime(entry), h + V7TAR_mtime_offset, V7TAR_mtime_size, V7TAR_mtime_max_size, strict)) { archive_set_error(&a->archive, ERANGE, "File modification time too large"); ret = ARCHIVE_FAILED; } if (mytartype >= 0) { h[V7TAR_typeflag_offset] = mytartype; } else { switch (archive_entry_filetype(entry)) { case AE_IFREG: case AE_IFDIR: break; case AE_IFLNK: h[V7TAR_typeflag_offset] = '2'; break; case AE_IFCHR: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive character device"); return (ARCHIVE_FAILED); case AE_IFBLK: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive block device"); return (ARCHIVE_FAILED); case AE_IFIFO: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive fifo"); return (ARCHIVE_FAILED); case AE_IFSOCK: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive socket"); return (ARCHIVE_FAILED); default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "tar format cannot archive this (mode=0%lo)", (unsigned long)archive_entry_mode(entry)); ret = ARCHIVE_FAILED; } } checksum = 0; for (i = 0; i < 512; i++) checksum += 255 & (unsigned int)h[i]; format_octal(checksum, h + V7TAR_checksum_offset, 6); /* Can't be pre-set in the template. */ h[V7TAR_checksum_offset + 6] = '\0'; return (ret); } /* * Format a number into a field, with some intelligence. */ static int format_number(int64_t v, char *p, int s, int maxsize, int strict) { int64_t limit; limit = ((int64_t)1 << (s*3)); /* "Strict" only permits octal values with proper termination. */ if (strict) return (format_octal(v, p, s)); /* * In non-strict mode, we allow the number to overwrite one or * more bytes of the field termination. Even old tar * implementations should be able to handle this with no * problem. */ if (v >= 0) { while (s <= maxsize) { if (v < limit) return (format_octal(v, p, s)); s++; limit <<= 3; } } /* Base-256 can handle any number, positive or negative. */ return (format_256(v, p, maxsize)); } /* * Format a number into the specified field using base-256. */ static int format_256(int64_t v, char *p, int s) { p += s; while (s-- > 0) { *--p = (char)(v & 0xff); v >>= 8; } *p |= 0x80; /* Set the base-256 marker bit. */ return (0); } /* * Format a number into the specified field. */ static int format_octal(int64_t v, char *p, int s) { int len; len = s; /* Octal values can't be negative, so use 0. */ if (v < 0) { while (len-- > 0) *p++ = '0'; return (-1); } p += s; /* Start at the end and work backwards. */ while (s-- > 0) { *--p = (char)('0' + (v & 7)); v >>= 3; } if (v == 0) return (0); /* If it overflowed, fill field with max value. */ while (len-- > 0) *p++ = '7'; return (-1); } static int archive_write_v7tar_close(struct archive_write *a) { return (__archive_write_nulls(a, 512*2)); } static int archive_write_v7tar_free(struct archive_write *a) { struct v7tar *v7tar; v7tar = (struct v7tar *)a->format_data; free(v7tar); a->format_data = NULL; return (ARCHIVE_OK); } static int archive_write_v7tar_finish_entry(struct archive_write *a) { struct v7tar *v7tar; int ret; v7tar = (struct v7tar *)a->format_data; ret = __archive_write_nulls(a, (size_t)(v7tar->entry_bytes_remaining + v7tar->entry_padding)); v7tar->entry_bytes_remaining = v7tar->entry_padding = 0; return (ret); } static ssize_t archive_write_v7tar_data(struct archive_write *a, const void *buff, size_t s) { struct v7tar *v7tar; int ret; v7tar = (struct v7tar *)a->format_data; if (s > v7tar->entry_bytes_remaining) s = (size_t)v7tar->entry_bytes_remaining; ret = __archive_write_output(a, buff, s); v7tar->entry_bytes_remaining -= s; if (ret != ARCHIVE_OK) return (ret); return (s); } Index: stable/10/contrib/libarchive/libarchive/archive_write_set_format_zip.c =================================================================== --- stable/10/contrib/libarchive/libarchive/archive_write_set_format_zip.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/archive_write_set_format_zip.c (revision 344674) @@ -1,1678 +1,1679 @@ /*- * Copyright (c) 2008 Anselm Strauss * Copyright (c) 2009 Joerg Sonnenberger * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA * 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. */ /* * Development supported by Google Summer of Code 2008. */ #include "archive_platform.h" __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LANGINFO_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_ZLIB_H #include #endif #include "archive.h" #include "archive_cryptor_private.h" #include "archive_endian.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_hmac_private.h" #include "archive_private.h" #include "archive_random_private.h" #include "archive_write_private.h" #ifndef HAVE_ZLIB_H #include "archive_crc32.h" #endif #define ZIP_ENTRY_FLAG_ENCRYPTED (1<<0) #define ZIP_ENTRY_FLAG_LENGTH_AT_END (1<<3) #define ZIP_ENTRY_FLAG_UTF8_NAME (1 << 11) #define ZIP_4GB_MAX ARCHIVE_LITERAL_LL(0xffffffff) #define ZIP_4GB_MAX_UNCOMPRESSED ARCHIVE_LITERAL_LL(0xff000000) enum compression { COMPRESSION_UNSPECIFIED = -1, COMPRESSION_STORE = 0, COMPRESSION_DEFLATE = 8 }; #ifdef HAVE_ZLIB_H #define COMPRESSION_DEFAULT COMPRESSION_DEFLATE #else #define COMPRESSION_DEFAULT COMPRESSION_STORE #endif enum encryption { ENCRYPTION_NONE = 0, ENCRYPTION_TRADITIONAL, /* Traditional PKWARE encryption. */ ENCRYPTION_WINZIP_AES128, /* WinZIP AES-128 encryption. */ ENCRYPTION_WINZIP_AES256, /* WinZIP AES-256 encryption. */ }; #define TRAD_HEADER_SIZE 12 /* * See "WinZip - AES Encryption Information" * http://www.winzip.com/aes_info.htm */ /* Value used in compression method. */ #define WINZIP_AES_ENCRYPTION 99 /* A WinZip AES header size which is stored at the beginning of * file contents. */ #define WINZIP_AES128_HEADER_SIZE (8 + 2) #define WINZIP_AES256_HEADER_SIZE (16 + 2) /* AES vendor version. */ #define AES_VENDOR_AE_1 0x0001 #define AES_VENDOR_AE_2 0x0002 /* Authentication code size. */ #define AUTH_CODE_SIZE 10 /**/ #define MAX_DERIVED_KEY_BUF_SIZE (AES_MAX_KEY_SIZE * 2 + 2) struct cd_segment { struct cd_segment *next; size_t buff_size; unsigned char *buff; unsigned char *p; }; struct trad_enc_ctx { uint32_t keys[3]; }; struct zip { int64_t entry_offset; int64_t entry_compressed_size; int64_t entry_uncompressed_size; int64_t entry_compressed_written; int64_t entry_uncompressed_written; int64_t entry_uncompressed_limit; struct archive_entry *entry; uint32_t entry_crc32; enum compression entry_compression; enum encryption entry_encryption; int entry_flags; int entry_uses_zip64; int experiments; struct trad_enc_ctx tctx; char tctx_valid; unsigned char trad_chkdat; unsigned aes_vendor; archive_crypto_ctx cctx; char cctx_valid; archive_hmac_sha1_ctx hctx; char hctx_valid; unsigned char *file_header; size_t file_header_extra_offset; unsigned long (*crc32func)(unsigned long crc, const void *buff, size_t len); struct cd_segment *central_directory; struct cd_segment *central_directory_last; size_t central_directory_bytes; size_t central_directory_entries; int64_t written_bytes; /* Overall position in file. */ struct archive_string_conv *opt_sconv; struct archive_string_conv *sconv_default; enum compression requested_compression; int deflate_compression_level; int init_default_conversion; enum encryption encryption_type; #define ZIP_FLAG_AVOID_ZIP64 1 #define ZIP_FLAG_FORCE_ZIP64 2 #define ZIP_FLAG_EXPERIMENT_xl 4 int flags; #ifdef HAVE_ZLIB_H z_stream stream; #endif size_t len_buf; unsigned char *buf; }; /* Don't call this min or MIN, since those are already defined on lots of platforms (but not all). */ #define zipmin(a, b) ((a) > (b) ? (b) : (a)) static ssize_t archive_write_zip_data(struct archive_write *, const void *buff, size_t s); static int archive_write_zip_close(struct archive_write *); static int archive_write_zip_free(struct archive_write *); static int archive_write_zip_finish_entry(struct archive_write *); static int archive_write_zip_header(struct archive_write *, struct archive_entry *); static int archive_write_zip_options(struct archive_write *, const char *, const char *); static unsigned int dos_time(const time_t); static size_t path_length(struct archive_entry *); static int write_path(struct archive_entry *, struct archive_write *); static void copy_path(struct archive_entry *, unsigned char *); static struct archive_string_conv *get_sconv(struct archive_write *, struct zip *); static int trad_enc_init(struct trad_enc_ctx *, const char *, size_t); static unsigned trad_enc_encrypt_update(struct trad_enc_ctx *, const uint8_t *, size_t, uint8_t *, size_t); static int init_traditional_pkware_encryption(struct archive_write *); static int is_traditional_pkware_encryption_supported(void); static int init_winzip_aes_encryption(struct archive_write *); static int is_winzip_aes_encryption_supported(int encryption); static unsigned char * cd_alloc(struct zip *zip, size_t length) { unsigned char *p; if (zip->central_directory == NULL || (zip->central_directory_last->p + length > zip->central_directory_last->buff + zip->central_directory_last->buff_size)) { struct cd_segment *segment = calloc(1, sizeof(*segment)); if (segment == NULL) return NULL; segment->buff_size = 64 * 1024; segment->buff = malloc(segment->buff_size); if (segment->buff == NULL) { free(segment); return NULL; } segment->p = segment->buff; if (zip->central_directory == NULL) { zip->central_directory = zip->central_directory_last = segment; } else { zip->central_directory_last->next = segment; zip->central_directory_last = segment; } } p = zip->central_directory_last->p; zip->central_directory_last->p += length; zip->central_directory_bytes += length; return (p); } static unsigned long real_crc32(unsigned long crc, const void *buff, size_t len) { return crc32(crc, buff, (unsigned int)len); } static unsigned long fake_crc32(unsigned long crc, const void *buff, size_t len) { (void)crc; /* UNUSED */ (void)buff; /* UNUSED */ (void)len; /* UNUSED */ return 0; } static int archive_write_zip_options(struct archive_write *a, const char *key, const char *val) { struct zip *zip = a->format_data; int ret = ARCHIVE_FAILED; if (strcmp(key, "compression") == 0) { /* * Set compression to use on all future entries. * This only affects regular files. */ if (val == NULL || val[0] == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: compression option needs a compression name", a->format_name); } else if (strcmp(val, "deflate") == 0) { #ifdef HAVE_ZLIB_H zip->requested_compression = COMPRESSION_DEFLATE; ret = ARCHIVE_OK; #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "deflate compression not supported"); #endif } else if (strcmp(val, "store") == 0) { zip->requested_compression = COMPRESSION_STORE; ret = ARCHIVE_OK; } return (ret); } else if (strcmp(key, "compression-level") == 0) { if (val == NULL || !(val[0] >= '0' && val[0] <= '9') || val[1] != '\0') { return ARCHIVE_WARN; } if (val[0] == '0') { zip->requested_compression = COMPRESSION_STORE; return ARCHIVE_OK; } else { #ifdef HAVE_ZLIB_H zip->requested_compression = COMPRESSION_DEFLATE; zip->deflate_compression_level = val[0] - '0'; return ARCHIVE_OK; #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "deflate compression not supported"); #endif } } else if (strcmp(key, "encryption") == 0) { if (val == NULL) { zip->encryption_type = ENCRYPTION_NONE; ret = ARCHIVE_OK; } else if (val[0] == '1' || strcmp(val, "traditional") == 0 || strcmp(val, "zipcrypt") == 0 || strcmp(val, "ZipCrypt") == 0) { if (is_traditional_pkware_encryption_supported()) { zip->encryption_type = ENCRYPTION_TRADITIONAL; ret = ARCHIVE_OK; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "encryption not supported"); } } else if (strcmp(val, "aes128") == 0) { if (is_winzip_aes_encryption_supported( ENCRYPTION_WINZIP_AES128)) { zip->encryption_type = ENCRYPTION_WINZIP_AES128; ret = ARCHIVE_OK; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "encryption not supported"); } } else if (strcmp(val, "aes256") == 0) { if (is_winzip_aes_encryption_supported( ENCRYPTION_WINZIP_AES256)) { zip->encryption_type = ENCRYPTION_WINZIP_AES256; ret = ARCHIVE_OK; } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "encryption not supported"); } } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: unknown encryption '%s'", a->format_name, val); } return (ret); } else if (strcmp(key, "experimental") == 0) { if (val == NULL || val[0] == 0) { zip->flags &= ~ ZIP_FLAG_EXPERIMENT_xl; } else { zip->flags |= ZIP_FLAG_EXPERIMENT_xl; } return (ARCHIVE_OK); } else if (strcmp(key, "fakecrc32") == 0) { /* * FOR TESTING ONLY: disable CRC calculation to speed up * certain complex tests. */ if (val == NULL || val[0] == 0) { zip->crc32func = real_crc32; } else { zip->crc32func = fake_crc32; } return (ARCHIVE_OK); } else if (strcmp(key, "hdrcharset") == 0) { /* * Set the character set used in translating filenames. */ if (val == NULL || val[0] == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "%s: hdrcharset option needs a character-set name", a->format_name); } else { zip->opt_sconv = archive_string_conversion_to_charset( &a->archive, val, 0); if (zip->opt_sconv != NULL) ret = ARCHIVE_OK; else ret = ARCHIVE_FATAL; } return (ret); } else if (strcmp(key, "zip64") == 0) { /* * Bias decisions about Zip64: force them to be * generated in certain cases where they are not * forbidden or avoid them in certain cases where they * are not strictly required. */ if (val != NULL && *val != '\0') { zip->flags |= ZIP_FLAG_FORCE_ZIP64; zip->flags &= ~ZIP_FLAG_AVOID_ZIP64; } else { zip->flags &= ~ZIP_FLAG_FORCE_ZIP64; zip->flags |= ZIP_FLAG_AVOID_ZIP64; } return (ARCHIVE_OK); } /* Note: The "warn" return is just to inform the options * supervisor that we didn't handle it. It will generate * a suitable error if no one used this option. */ return (ARCHIVE_WARN); } int archive_write_zip_set_compression_deflate(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; int ret = ARCHIVE_FAILED; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_zip_set_compression_deflate"); if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can only use archive_write_zip_set_compression_deflate" " with zip format"); ret = ARCHIVE_FATAL; } else { #ifdef HAVE_ZLIB_H struct zip *zip = a->format_data; zip->requested_compression = COMPRESSION_DEFLATE; ret = ARCHIVE_OK; #else archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "deflate compression not supported"); ret = ARCHIVE_FAILED; #endif } return (ret); } int archive_write_zip_set_compression_store(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct zip *zip = a->format_data; int ret = ARCHIVE_FAILED; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_zip_set_compression_deflate"); if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can only use archive_write_zip_set_compression_store" " with zip format"); ret = ARCHIVE_FATAL; } else { zip->requested_compression = COMPRESSION_STORE; ret = ARCHIVE_OK; } return (ret); } int archive_write_set_format_zip(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct zip *zip; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_zip"); /* If another format was already registered, unregister it. */ if (a->format_free != NULL) (a->format_free)(a); zip = (struct zip *) calloc(1, sizeof(*zip)); if (zip == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data"); return (ARCHIVE_FATAL); } /* "Unspecified" lets us choose the appropriate compression. */ zip->requested_compression = COMPRESSION_UNSPECIFIED; #ifdef HAVE_ZLIB_H zip->deflate_compression_level = Z_DEFAULT_COMPRESSION; #endif zip->crc32func = real_crc32; /* A buffer used for both compression and encryption. */ zip->len_buf = 65536; zip->buf = malloc(zip->len_buf); if (zip->buf == NULL) { free(zip); archive_set_error(&a->archive, ENOMEM, "Can't allocate compression buffer"); return (ARCHIVE_FATAL); } a->format_data = zip; a->format_name = "zip"; a->format_options = archive_write_zip_options; a->format_write_header = archive_write_zip_header; a->format_write_data = archive_write_zip_data; a->format_finish_entry = archive_write_zip_finish_entry; a->format_close = archive_write_zip_close; a->format_free = archive_write_zip_free; a->archive.archive_format = ARCHIVE_FORMAT_ZIP; a->archive.archive_format_name = "ZIP"; return (ARCHIVE_OK); } static int is_all_ascii(const char *p) { const unsigned char *pp = (const unsigned char *)p; while (*pp) { if (*pp++ > 127) return (0); } return (1); } static int archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) { unsigned char local_header[32]; unsigned char local_extra[144]; struct zip *zip = a->format_data; unsigned char *e; unsigned char *cd_extra; size_t filename_length; const char *slink = NULL; size_t slink_size = 0; struct archive_string_conv *sconv = get_sconv(a, zip); int ret, ret2 = ARCHIVE_OK; mode_t type; int version_needed = 10; /* Ignore types of entries that we don't support. */ type = archive_entry_filetype(entry); if (type != AE_IFREG && type != AE_IFDIR && type != AE_IFLNK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Filetype not supported"); return ARCHIVE_FAILED; }; /* If we're not using Zip64, reject large files. */ if (zip->flags & ZIP_FLAG_AVOID_ZIP64) { /* Reject entries over 4GB. */ if (archive_entry_size_is_set(entry) && (archive_entry_size(entry) > ZIP_4GB_MAX)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Files > 4GB require Zip64 extensions"); return ARCHIVE_FAILED; } /* Reject entries if archive is > 4GB. */ if (zip->written_bytes > ZIP_4GB_MAX) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Archives > 4GB require Zip64 extensions"); return ARCHIVE_FAILED; } } /* Only regular files can have size > 0. */ if (type != AE_IFREG) archive_entry_set_size(entry, 0); /* Reset information from last entry. */ zip->entry_offset = zip->written_bytes; zip->entry_uncompressed_limit = INT64_MAX; zip->entry_compressed_size = 0; zip->entry_uncompressed_size = 0; zip->entry_compressed_written = 0; zip->entry_uncompressed_written = 0; zip->entry_flags = 0; zip->entry_uses_zip64 = 0; zip->entry_crc32 = zip->crc32func(0, NULL, 0); zip->entry_encryption = 0; - if (zip->entry != NULL) { - archive_entry_free(zip->entry); - zip->entry = NULL; - } + archive_entry_free(zip->entry); + zip->entry = NULL; if (zip->cctx_valid) archive_encrypto_aes_ctr_release(&zip->cctx); if (zip->hctx_valid) archive_hmac_sha1_cleanup(&zip->hctx); zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0; if (type == AE_IFREG &&(!archive_entry_size_is_set(entry) || archive_entry_size(entry) > 0)) { switch (zip->encryption_type) { case ENCRYPTION_TRADITIONAL: case ENCRYPTION_WINZIP_AES128: case ENCRYPTION_WINZIP_AES256: zip->entry_flags |= ZIP_ENTRY_FLAG_ENCRYPTED; zip->entry_encryption = zip->encryption_type; break; default: break; } } #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure the path separators in pathname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ zip->entry = __la_win_entry_in_posix_pathseparator(entry); if (zip->entry == entry) zip->entry = archive_entry_clone(entry); #else zip->entry = archive_entry_clone(entry); #endif if (zip->entry == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip header data"); return (ARCHIVE_FATAL); } if (sconv != NULL) { const char *p; size_t len; if (archive_entry_pathname_l(entry, &p, &len, sconv) != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate Pathname '%s' to %s", archive_entry_pathname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } if (len > 0) archive_entry_set_pathname(zip->entry, p); /* * There is no standard for symlink handling; we convert * it using the same character-set translation that we use * for filename. */ if (type == AE_IFLNK) { if (archive_entry_symlink_l(entry, &p, &len, sconv)) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory " " for Symlink"); return (ARCHIVE_FATAL); } /* No error if we can't convert. */ } else if (len > 0) archive_entry_set_symlink(zip->entry, p); } } /* If filename isn't ASCII and we can use UTF-8, set the UTF-8 flag. */ if (!is_all_ascii(archive_entry_pathname(zip->entry))) { if (zip->opt_sconv != NULL) { if (strcmp(archive_string_conversion_charset_name( zip->opt_sconv), "UTF-8") == 0) zip->entry_flags |= ZIP_ENTRY_FLAG_UTF8_NAME; #if HAVE_NL_LANGINFO } else if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) { zip->entry_flags |= ZIP_ENTRY_FLAG_UTF8_NAME; #endif } } filename_length = path_length(zip->entry); /* Determine appropriate compression and size for this entry. */ if (type == AE_IFLNK) { slink = archive_entry_symlink(zip->entry); if (slink != NULL) slink_size = strlen(slink); else slink_size = 0; zip->entry_uncompressed_limit = slink_size; zip->entry_compressed_size = slink_size; zip->entry_uncompressed_size = slink_size; zip->entry_crc32 = zip->crc32func(zip->entry_crc32, (const unsigned char *)slink, slink_size); zip->entry_compression = COMPRESSION_STORE; version_needed = 20; } else if (type != AE_IFREG) { zip->entry_compression = COMPRESSION_STORE; zip->entry_uncompressed_limit = 0; version_needed = 20; } else if (archive_entry_size_is_set(zip->entry)) { int64_t size = archive_entry_size(zip->entry); int64_t additional_size = 0; zip->entry_uncompressed_limit = size; zip->entry_compression = zip->requested_compression; if (zip->entry_compression == COMPRESSION_UNSPECIFIED) { zip->entry_compression = COMPRESSION_DEFAULT; } if (zip->entry_compression == COMPRESSION_STORE) { zip->entry_compressed_size = size; zip->entry_uncompressed_size = size; version_needed = 10; } else { zip->entry_uncompressed_size = size; version_needed = 20; } if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) { switch (zip->entry_encryption) { case ENCRYPTION_TRADITIONAL: additional_size = TRAD_HEADER_SIZE; version_needed = 20; break; case ENCRYPTION_WINZIP_AES128: additional_size = WINZIP_AES128_HEADER_SIZE + AUTH_CODE_SIZE; version_needed = 20; break; case ENCRYPTION_WINZIP_AES256: additional_size = WINZIP_AES256_HEADER_SIZE + AUTH_CODE_SIZE; version_needed = 20; break; default: break; } if (zip->entry_compression == COMPRESSION_STORE) zip->entry_compressed_size += additional_size; } /* * Set Zip64 extension in any of the following cases * (this was suggested by discussion on info-zip-dev * mailing list): * = Zip64 is being forced by user * = File is over 4GiB uncompressed * (including encryption header, if any) * = File is close to 4GiB and is being compressed * (compression might make file larger) */ if ((zip->flags & ZIP_FLAG_FORCE_ZIP64) || (zip->entry_uncompressed_size + additional_size > ZIP_4GB_MAX) || (zip->entry_uncompressed_size > ZIP_4GB_MAX_UNCOMPRESSED && zip->entry_compression != COMPRESSION_STORE)) { zip->entry_uses_zip64 = 1; version_needed = 45; } /* We may know the size, but never the CRC. */ zip->entry_flags |= ZIP_ENTRY_FLAG_LENGTH_AT_END; } else { /* We don't know the size. In this case, we prefer * deflate (it has a clear end-of-data marker which * makes length-at-end more reliable) and will * enable Zip64 extensions unless we're told not to. */ zip->entry_compression = COMPRESSION_DEFAULT; zip->entry_flags |= ZIP_ENTRY_FLAG_LENGTH_AT_END; if ((zip->flags & ZIP_FLAG_AVOID_ZIP64) == 0) { zip->entry_uses_zip64 = 1; version_needed = 45; } else if (zip->entry_compression == COMPRESSION_STORE) { version_needed = 10; } else { version_needed = 20; } if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) { switch (zip->entry_encryption) { case ENCRYPTION_TRADITIONAL: case ENCRYPTION_WINZIP_AES128: case ENCRYPTION_WINZIP_AES256: if (version_needed < 20) version_needed = 20; break; default: break; } } } /* Format the local header. */ memset(local_header, 0, sizeof(local_header)); memcpy(local_header, "PK\003\004", 4); archive_le16enc(local_header + 4, version_needed); archive_le16enc(local_header + 6, zip->entry_flags); if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128 || zip->entry_encryption == ENCRYPTION_WINZIP_AES256) archive_le16enc(local_header + 8, WINZIP_AES_ENCRYPTION); else archive_le16enc(local_header + 8, zip->entry_compression); archive_le32enc(local_header + 10, dos_time(archive_entry_mtime(zip->entry))); archive_le32enc(local_header + 14, zip->entry_crc32); if (zip->entry_uses_zip64) { /* Zip64 data in the local header "must" include both * compressed and uncompressed sizes AND those fields * are included only if these are 0xffffffff; * THEREFORE these must be set this way, even if we * know one of them is smaller. */ archive_le32enc(local_header + 18, ZIP_4GB_MAX); archive_le32enc(local_header + 22, ZIP_4GB_MAX); } else { archive_le32enc(local_header + 18, (uint32_t)zip->entry_compressed_size); archive_le32enc(local_header + 22, (uint32_t)zip->entry_uncompressed_size); } archive_le16enc(local_header + 26, (uint16_t)filename_length); if (zip->entry_encryption == ENCRYPTION_TRADITIONAL) { if (zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END) zip->trad_chkdat = local_header[11]; else zip->trad_chkdat = local_header[17]; } /* Format as much of central directory file header as we can: */ zip->file_header = cd_alloc(zip, 46); /* If (zip->file_header == NULL) XXXX */ ++zip->central_directory_entries; memset(zip->file_header, 0, 46); memcpy(zip->file_header, "PK\001\002", 4); /* "Made by PKZip 2.0 on Unix." */ archive_le16enc(zip->file_header + 4, 3 * 256 + version_needed); archive_le16enc(zip->file_header + 6, version_needed); archive_le16enc(zip->file_header + 8, zip->entry_flags); if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128 || zip->entry_encryption == ENCRYPTION_WINZIP_AES256) archive_le16enc(zip->file_header + 10, WINZIP_AES_ENCRYPTION); else archive_le16enc(zip->file_header + 10, zip->entry_compression); archive_le32enc(zip->file_header + 12, dos_time(archive_entry_mtime(zip->entry))); archive_le16enc(zip->file_header + 28, (uint16_t)filename_length); /* Following Info-Zip, store mode in the "external attributes" field. */ archive_le32enc(zip->file_header + 38, ((uint32_t)archive_entry_mode(zip->entry)) << 16); e = cd_alloc(zip, filename_length); /* If (e == NULL) XXXX */ copy_path(zip->entry, e); /* Format extra data. */ memset(local_extra, 0, sizeof(local_extra)); e = local_extra; /* First, extra blocks that are the same between * the local file header and the central directory. * We format them once and then duplicate them. */ /* UT timestamp, length depends on what timestamps are set. */ memcpy(e, "UT", 2); archive_le16enc(e + 2, 1 + (archive_entry_mtime_is_set(entry) ? 4 : 0) + (archive_entry_atime_is_set(entry) ? 4 : 0) + (archive_entry_ctime_is_set(entry) ? 4 : 0)); e += 4; *e++ = (archive_entry_mtime_is_set(entry) ? 1 : 0) | (archive_entry_atime_is_set(entry) ? 2 : 0) | (archive_entry_ctime_is_set(entry) ? 4 : 0); if (archive_entry_mtime_is_set(entry)) { archive_le32enc(e, (uint32_t)archive_entry_mtime(entry)); e += 4; } if (archive_entry_atime_is_set(entry)) { archive_le32enc(e, (uint32_t)archive_entry_atime(entry)); e += 4; } if (archive_entry_ctime_is_set(entry)) { archive_le32enc(e, (uint32_t)archive_entry_ctime(entry)); e += 4; } /* ux Unix extra data, length 11, version 1 */ /* TODO: If uid < 64k, use 2 bytes, ditto for gid. */ memcpy(e, "ux\013\000\001", 5); e += 5; *e++ = 4; /* Length of following UID */ archive_le32enc(e, (uint32_t)archive_entry_uid(entry)); e += 4; *e++ = 4; /* Length of following GID */ archive_le32enc(e, (uint32_t)archive_entry_gid(entry)); e += 4; /* AES extra data field: WinZIP AES information, ID=0x9901 */ if ((zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) && (zip->entry_encryption == ENCRYPTION_WINZIP_AES128 || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)) { memcpy(e, "\001\231\007\000\001\000AE", 8); /* AES vendor version AE-2 does not store a CRC. * WinZip 11 uses AE-1, which does store the CRC, * but it does not store the CRC when the file size * is less than 20 bytes. So we simulate what * WinZip 11 does. * NOTE: WinZip 9.0 and 10.0 uses AE-2 by default. */ if (archive_entry_size_is_set(zip->entry) && archive_entry_size(zip->entry) < 20) { archive_le16enc(e+4, AES_VENDOR_AE_2); zip->aes_vendor = AES_VENDOR_AE_2;/* no CRC. */ } else zip->aes_vendor = AES_VENDOR_AE_1; e += 8; /* AES encryption strength. */ *e++ = (zip->entry_encryption == ENCRYPTION_WINZIP_AES128)?1:3; /* Actual compression method. */ archive_le16enc(e, zip->entry_compression); e += 2; } /* Copy UT ,ux, and AES-extra into central directory as well. */ zip->file_header_extra_offset = zip->central_directory_bytes; cd_extra = cd_alloc(zip, e - local_extra); memcpy(cd_extra, local_extra, e - local_extra); /* * Following extra blocks vary between local header and * central directory. These are the local header versions. * Central directory versions get formatted in * archive_write_zip_finish_entry() below. */ /* "[Zip64 entry] in the local header MUST include BOTH * original [uncompressed] and compressed size fields." */ if (zip->entry_uses_zip64) { unsigned char *zip64_start = e; memcpy(e, "\001\000\020\000", 4); e += 4; archive_le64enc(e, zip->entry_uncompressed_size); e += 8; archive_le64enc(e, zip->entry_compressed_size); e += 8; archive_le16enc(zip64_start + 2, (uint16_t)(e - (zip64_start + 4))); } if (zip->flags & ZIP_FLAG_EXPERIMENT_xl) { /* Experimental 'xl' extension to improve streaming. */ unsigned char *external_info = e; int included = 7; memcpy(e, "xl\000\000", 4); // 0x6c65 + 2-byte length e += 4; e[0] = included; /* bitmap of included fields */ e += 1; if (included & 1) { archive_le16enc(e, /* "Version created by" */ 3 * 256 + version_needed); e += 2; } if (included & 2) { archive_le16enc(e, 0); /* internal file attributes */ e += 2; } if (included & 4) { archive_le32enc(e, /* external file attributes */ ((uint32_t)archive_entry_mode(zip->entry)) << 16); e += 4; } if (included & 8) { // Libarchive does not currently support file comments. } archive_le16enc(external_info + 2, (uint16_t)(e - (external_info + 4))); } /* Update local header with size of extra data and write it all out: */ archive_le16enc(local_header + 28, (uint16_t)(e - local_extra)); ret = __archive_write_output(a, local_header, 30); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += 30; ret = write_path(zip->entry, a); if (ret <= ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += ret; ret = __archive_write_output(a, local_extra, e - local_extra); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += e - local_extra; /* For symlinks, write the body now. */ if (slink != NULL) { ret = __archive_write_output(a, slink, slink_size); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->entry_compressed_written += slink_size; zip->entry_uncompressed_written += slink_size; zip->written_bytes += slink_size; } #ifdef HAVE_ZLIB_H if (zip->entry_compression == COMPRESSION_DEFLATE) { zip->stream.zalloc = Z_NULL; zip->stream.zfree = Z_NULL; zip->stream.opaque = Z_NULL; zip->stream.next_out = zip->buf; zip->stream.avail_out = (uInt)zip->len_buf; if (deflateInit2(&zip->stream, zip->deflate_compression_level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) { archive_set_error(&a->archive, ENOMEM, "Can't init deflate compressor"); return (ARCHIVE_FATAL); } } #endif return (ret2); } static ssize_t archive_write_zip_data(struct archive_write *a, const void *buff, size_t s) { int ret; struct zip *zip = a->format_data; if ((int64_t)s > zip->entry_uncompressed_limit) s = (size_t)zip->entry_uncompressed_limit; zip->entry_uncompressed_written += s; if (s == 0) return 0; if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) { switch (zip->entry_encryption) { case ENCRYPTION_TRADITIONAL: /* Initialize traditional PKWARE encryption context. */ if (!zip->tctx_valid) { ret = init_traditional_pkware_encryption(a); if (ret != ARCHIVE_OK) return (ret); zip->tctx_valid = 1; } break; case ENCRYPTION_WINZIP_AES128: case ENCRYPTION_WINZIP_AES256: if (!zip->cctx_valid) { ret = init_winzip_aes_encryption(a); if (ret != ARCHIVE_OK) return (ret); zip->cctx_valid = zip->hctx_valid = 1; } break; default: break; } } switch (zip->entry_compression) { case COMPRESSION_STORE: if (zip->tctx_valid || zip->cctx_valid) { const uint8_t *rb = (const uint8_t *)buff; const uint8_t * const re = rb + s; while (rb < re) { size_t l; if (zip->tctx_valid) { l = trad_enc_encrypt_update(&zip->tctx, rb, re - rb, zip->buf, zip->len_buf); } else { l = zip->len_buf; ret = archive_encrypto_aes_ctr_update( &zip->cctx, rb, re - rb, zip->buf, &l); if (ret < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to encrypt file"); return (ARCHIVE_FAILED); } archive_hmac_sha1_update(&zip->hctx, zip->buf, l); } ret = __archive_write_output(a, zip->buf, l); if (ret != ARCHIVE_OK) return (ret); zip->entry_compressed_written += l; zip->written_bytes += l; rb += l; } } else { ret = __archive_write_output(a, buff, s); if (ret != ARCHIVE_OK) return (ret); zip->written_bytes += s; zip->entry_compressed_written += s; } break; #if HAVE_ZLIB_H case COMPRESSION_DEFLATE: zip->stream.next_in = (unsigned char*)(uintptr_t)buff; zip->stream.avail_in = (uInt)s; do { ret = deflate(&zip->stream, Z_NO_FLUSH); if (ret == Z_STREAM_ERROR) return (ARCHIVE_FATAL); if (zip->stream.avail_out == 0) { if (zip->tctx_valid) { trad_enc_encrypt_update(&zip->tctx, zip->buf, zip->len_buf, zip->buf, zip->len_buf); } else if (zip->cctx_valid) { size_t outl = zip->len_buf; ret = archive_encrypto_aes_ctr_update( &zip->cctx, zip->buf, zip->len_buf, zip->buf, &outl); if (ret < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to encrypt file"); return (ARCHIVE_FAILED); } archive_hmac_sha1_update(&zip->hctx, zip->buf, zip->len_buf); } ret = __archive_write_output(a, zip->buf, zip->len_buf); if (ret != ARCHIVE_OK) return (ret); zip->entry_compressed_written += zip->len_buf; zip->written_bytes += zip->len_buf; zip->stream.next_out = zip->buf; zip->stream.avail_out = (uInt)zip->len_buf; } } while (zip->stream.avail_in != 0); break; #endif default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid ZIP compression type"); return ARCHIVE_FATAL; } zip->entry_uncompressed_limit -= s; if (!zip->cctx_valid || zip->aes_vendor != AES_VENDOR_AE_2) zip->entry_crc32 = zip->crc32func(zip->entry_crc32, buff, (unsigned)s); return (s); } static int archive_write_zip_finish_entry(struct archive_write *a) { struct zip *zip = a->format_data; int ret; #if HAVE_ZLIB_H if (zip->entry_compression == COMPRESSION_DEFLATE) { for (;;) { size_t remainder; ret = deflate(&zip->stream, Z_FINISH); if (ret == Z_STREAM_ERROR) return (ARCHIVE_FATAL); remainder = zip->len_buf - zip->stream.avail_out; if (zip->tctx_valid) { trad_enc_encrypt_update(&zip->tctx, zip->buf, remainder, zip->buf, remainder); } else if (zip->cctx_valid) { size_t outl = remainder; ret = archive_encrypto_aes_ctr_update( &zip->cctx, zip->buf, remainder, zip->buf, &outl); if (ret < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to encrypt file"); return (ARCHIVE_FAILED); } archive_hmac_sha1_update(&zip->hctx, zip->buf, remainder); } ret = __archive_write_output(a, zip->buf, remainder); if (ret != ARCHIVE_OK) return (ret); zip->entry_compressed_written += remainder; zip->written_bytes += remainder; zip->stream.next_out = zip->buf; if (zip->stream.avail_out != 0) break; zip->stream.avail_out = (uInt)zip->len_buf; } deflateEnd(&zip->stream); } #endif if (zip->hctx_valid) { uint8_t hmac[20]; size_t hmac_len = 20; archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len); ret = __archive_write_output(a, hmac, AUTH_CODE_SIZE); if (ret != ARCHIVE_OK) return (ret); zip->entry_compressed_written += AUTH_CODE_SIZE; zip->written_bytes += AUTH_CODE_SIZE; } /* Write trailing data descriptor. */ if ((zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END) != 0) { char d[24]; memcpy(d, "PK\007\010", 4); if (zip->cctx_valid && zip->aes_vendor == AES_VENDOR_AE_2) archive_le32enc(d + 4, 0);/* no CRC.*/ else archive_le32enc(d + 4, zip->entry_crc32); if (zip->entry_uses_zip64) { archive_le64enc(d + 8, (uint64_t)zip->entry_compressed_written); archive_le64enc(d + 16, (uint64_t)zip->entry_uncompressed_written); ret = __archive_write_output(a, d, 24); zip->written_bytes += 24; } else { archive_le32enc(d + 8, (uint32_t)zip->entry_compressed_written); archive_le32enc(d + 12, (uint32_t)zip->entry_uncompressed_written); ret = __archive_write_output(a, d, 16); zip->written_bytes += 16; } if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } /* Append Zip64 extra data to central directory information. */ if (zip->entry_compressed_written > ZIP_4GB_MAX || zip->entry_uncompressed_written > ZIP_4GB_MAX || zip->entry_offset > ZIP_4GB_MAX) { unsigned char zip64[32]; unsigned char *z = zip64, *zd; memcpy(z, "\001\000\000\000", 4); z += 4; if (zip->entry_uncompressed_written >= ZIP_4GB_MAX) { archive_le64enc(z, zip->entry_uncompressed_written); z += 8; } if (zip->entry_compressed_written >= ZIP_4GB_MAX) { archive_le64enc(z, zip->entry_compressed_written); z += 8; } if (zip->entry_offset >= ZIP_4GB_MAX) { archive_le64enc(z, zip->entry_offset); z += 8; } archive_le16enc(zip64 + 2, (uint16_t)(z - (zip64 + 4))); zd = cd_alloc(zip, z - zip64); if (zd == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip data"); return (ARCHIVE_FATAL); } memcpy(zd, zip64, z - zip64); /* Zip64 means version needs to be set to at least 4.5 */ if (archive_le16dec(zip->file_header + 6) < 45) archive_le16enc(zip->file_header + 6, 45); } /* Fix up central directory file header. */ if (zip->cctx_valid && zip->aes_vendor == AES_VENDOR_AE_2) archive_le32enc(zip->file_header + 16, 0);/* no CRC.*/ else archive_le32enc(zip->file_header + 16, zip->entry_crc32); archive_le32enc(zip->file_header + 20, (uint32_t)zipmin(zip->entry_compressed_written, ZIP_4GB_MAX)); archive_le32enc(zip->file_header + 24, (uint32_t)zipmin(zip->entry_uncompressed_written, ZIP_4GB_MAX)); archive_le16enc(zip->file_header + 30, (uint16_t)(zip->central_directory_bytes - zip->file_header_extra_offset)); archive_le32enc(zip->file_header + 42, (uint32_t)zipmin(zip->entry_offset, ZIP_4GB_MAX)); return (ARCHIVE_OK); } static int archive_write_zip_close(struct archive_write *a) { uint8_t buff[64]; int64_t offset_start, offset_end; struct zip *zip = a->format_data; struct cd_segment *segment; int ret; offset_start = zip->written_bytes; segment = zip->central_directory; while (segment != NULL) { ret = __archive_write_output(a, segment->buff, segment->p - segment->buff); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += segment->p - segment->buff; segment = segment->next; } offset_end = zip->written_bytes; /* If central dir info is too large, write Zip64 end-of-cd */ if (offset_end - offset_start > ZIP_4GB_MAX || offset_start > ZIP_4GB_MAX || zip->central_directory_entries > 0xffffUL || (zip->flags & ZIP_FLAG_FORCE_ZIP64)) { /* Zip64 end-of-cd record */ memset(buff, 0, 56); memcpy(buff, "PK\006\006", 4); archive_le64enc(buff + 4, 44); archive_le16enc(buff + 12, 45); archive_le16enc(buff + 14, 45); /* This is disk 0 of 0. */ archive_le64enc(buff + 24, zip->central_directory_entries); archive_le64enc(buff + 32, zip->central_directory_entries); archive_le64enc(buff + 40, offset_end - offset_start); archive_le64enc(buff + 48, offset_start); ret = __archive_write_output(a, buff, 56); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += 56; /* Zip64 end-of-cd locator record. */ memset(buff, 0, 20); memcpy(buff, "PK\006\007", 4); archive_le32enc(buff + 4, 0); archive_le64enc(buff + 8, offset_end); archive_le32enc(buff + 16, 1); ret = __archive_write_output(a, buff, 20); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += 20; } /* Format and write end of central directory. */ memset(buff, 0, sizeof(buff)); memcpy(buff, "PK\005\006", 4); archive_le16enc(buff + 8, (uint16_t)zipmin(0xffffU, zip->central_directory_entries)); archive_le16enc(buff + 10, (uint16_t)zipmin(0xffffU, zip->central_directory_entries)); archive_le32enc(buff + 12, (uint32_t)zipmin(ZIP_4GB_MAX, (offset_end - offset_start))); archive_le32enc(buff + 16, (uint32_t)zipmin(ZIP_4GB_MAX, offset_start)); ret = __archive_write_output(a, buff, 22); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += 22; return (ARCHIVE_OK); } static int archive_write_zip_free(struct archive_write *a) { struct zip *zip; struct cd_segment *segment; zip = a->format_data; while (zip->central_directory != NULL) { segment = zip->central_directory; zip->central_directory = segment->next; free(segment->buff); free(segment); } free(zip->buf); archive_entry_free(zip->entry); if (zip->cctx_valid) archive_encrypto_aes_ctr_release(&zip->cctx); if (zip->hctx_valid) archive_hmac_sha1_cleanup(&zip->hctx); /* TODO: Free opt_sconv, sconv_default */ free(zip); a->format_data = NULL; return (ARCHIVE_OK); } /* Convert into MSDOS-style date/time. */ static unsigned int dos_time(const time_t unix_time) { struct tm *t; unsigned int dt; /* This will not preserve time when creating/extracting the archive * on two systems with different time zones. */ t = localtime(&unix_time); /* MSDOS-style date/time is only between 1980-01-01 and 2107-12-31 */ if (t->tm_year < 1980 - 1900) /* Set minimum date/time '1980-01-01 00:00:00'. */ dt = 0x00210000U; else if (t->tm_year > 2107 - 1900) /* Set maximum date/time '2107-12-31 23:59:58'. */ dt = 0xff9fbf7dU; else { dt = 0; dt += ((t->tm_year - 80) & 0x7f) << 9; dt += ((t->tm_mon + 1) & 0x0f) << 5; dt += (t->tm_mday & 0x1f); dt <<= 16; dt += (t->tm_hour & 0x1f) << 11; dt += (t->tm_min & 0x3f) << 5; dt += (t->tm_sec & 0x3e) >> 1; /* Only counting every 2 seconds. */ } return dt; } static size_t path_length(struct archive_entry *entry) { mode_t type; const char *path; type = archive_entry_filetype(entry); path = archive_entry_pathname(entry); if (path == NULL) return (0); if (type == AE_IFDIR && (path[0] == '\0' || path[strlen(path) - 1] != '/')) { return strlen(path) + 1; } else { return strlen(path); } } static int write_path(struct archive_entry *entry, struct archive_write *archive) { int ret; const char *path; mode_t type; size_t written_bytes; path = archive_entry_pathname(entry); type = archive_entry_filetype(entry); written_bytes = 0; + + if (path == NULL) + return (ARCHIVE_FATAL); ret = __archive_write_output(archive, path, strlen(path)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); written_bytes += strlen(path); /* Folders are recognized by a trailing slash. */ if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) { ret = __archive_write_output(archive, "/", 1); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); written_bytes += 1; } return ((int)written_bytes); } static void copy_path(struct archive_entry *entry, unsigned char *p) { const char *path; size_t pathlen; mode_t type; path = archive_entry_pathname(entry); pathlen = strlen(path); type = archive_entry_filetype(entry); memcpy(p, path, pathlen); /* Folders are recognized by a trailing slash. */ if ((type == AE_IFDIR) & (path[pathlen - 1] != '/')) { p[pathlen] = '/'; p[pathlen + 1] = '\0'; } } static struct archive_string_conv * get_sconv(struct archive_write *a, struct zip *zip) { if (zip->opt_sconv != NULL) return (zip->opt_sconv); if (!zip->init_default_conversion) { zip->sconv_default = archive_string_default_conversion_for_write(&(a->archive)); zip->init_default_conversion = 1; } return (zip->sconv_default); } /* Traditional PKWARE Decryption functions. */ static void trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c) { uint8_t t; #define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL) ctx->keys[0] = CRC32(ctx->keys[0], c); ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1; t = (ctx->keys[1] >> 24) & 0xff; ctx->keys[2] = CRC32(ctx->keys[2], t); #undef CRC32 } static uint8_t trad_enc_decrypt_byte(struct trad_enc_ctx *ctx) { unsigned temp = ctx->keys[2] | 2; return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff; } static unsigned trad_enc_encrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in, size_t in_len, uint8_t *out, size_t out_len) { unsigned i, max; max = (unsigned)((in_len < out_len)? in_len: out_len); for (i = 0; i < max; i++) { uint8_t t = in[i]; out[i] = t ^ trad_enc_decrypt_byte(ctx); trad_enc_update_keys(ctx, t); } return i; } static int trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len) { ctx->keys[0] = 305419896L; ctx->keys[1] = 591751049L; ctx->keys[2] = 878082192L; for (;pw_len; --pw_len) trad_enc_update_keys(ctx, *pw++); return 0; } static int is_traditional_pkware_encryption_supported(void) { uint8_t key[TRAD_HEADER_SIZE]; if (archive_random(key, sizeof(key)-1) != ARCHIVE_OK) return (0); return (1); } static int init_traditional_pkware_encryption(struct archive_write *a) { struct zip *zip = a->format_data; const char *passphrase; uint8_t key[TRAD_HEADER_SIZE]; uint8_t key_encrypted[TRAD_HEADER_SIZE]; int ret; passphrase = __archive_write_get_passphrase(a); if (passphrase == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Encryption needs passphrase"); return ARCHIVE_FAILED; } if (archive_random(key, sizeof(key)-1) != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't generate random number for encryption"); return ARCHIVE_FATAL; } trad_enc_init(&zip->tctx, passphrase, strlen(passphrase)); /* Set the last key code which will be used as a check code * for verifying passphrase in decryption. */ key[TRAD_HEADER_SIZE-1] = zip->trad_chkdat; trad_enc_encrypt_update(&zip->tctx, key, TRAD_HEADER_SIZE, key_encrypted, TRAD_HEADER_SIZE); /* Write encrypted keys in the top of the file content. */ ret = __archive_write_output(a, key_encrypted, TRAD_HEADER_SIZE); if (ret != ARCHIVE_OK) return (ret); zip->written_bytes += TRAD_HEADER_SIZE; zip->entry_compressed_written += TRAD_HEADER_SIZE; return (ret); } static int init_winzip_aes_encryption(struct archive_write *a) { struct zip *zip = a->format_data; const char *passphrase; size_t key_len, salt_len; uint8_t salt[16 + 2]; uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE]; int ret; passphrase = __archive_write_get_passphrase(a); if (passphrase == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Encryption needs passphrase"); return (ARCHIVE_FAILED); } if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128) { salt_len = 8; key_len = 16; } else { /* AES 256 */ salt_len = 16; key_len = 32; } if (archive_random(salt, salt_len) != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't generate random number for encryption"); return (ARCHIVE_FATAL); } archive_pbkdf2_sha1(passphrase, strlen(passphrase), salt, salt_len, 1000, derived_key, key_len * 2 + 2); ret = archive_encrypto_aes_ctr_init(&zip->cctx, derived_key, key_len); if (ret != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Decryption is unsupported due to lack of crypto library"); return (ARCHIVE_FAILED); } ret = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len, key_len); if (ret != 0) { archive_encrypto_aes_ctr_release(&zip->cctx); archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to initialize HMAC-SHA1"); return (ARCHIVE_FAILED); } /* Set a password verification value after the 'salt'. */ salt[salt_len] = derived_key[key_len * 2]; salt[salt_len + 1] = derived_key[key_len * 2 + 1]; /* Write encrypted keys in the top of the file content. */ ret = __archive_write_output(a, salt, salt_len + 2); if (ret != ARCHIVE_OK) return (ret); zip->written_bytes += salt_len + 2; zip->entry_compressed_written += salt_len + 2; return (ARCHIVE_OK); } static int is_winzip_aes_encryption_supported(int encryption) { size_t key_len, salt_len; uint8_t salt[16 + 2]; uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE]; archive_crypto_ctx cctx; archive_hmac_sha1_ctx hctx; int ret; if (encryption == ENCRYPTION_WINZIP_AES128) { salt_len = 8; key_len = 16; } else { /* AES 256 */ salt_len = 16; key_len = 32; } if (archive_random(salt, salt_len) != ARCHIVE_OK) return (0); ret = archive_pbkdf2_sha1("p", 1, salt, salt_len, 1000, derived_key, key_len * 2 + 2); if (ret != 0) return (0); ret = archive_encrypto_aes_ctr_init(&cctx, derived_key, key_len); if (ret != 0) return (0); ret = archive_hmac_sha1_init(&hctx, derived_key + key_len, key_len); archive_encrypto_aes_ctr_release(&cctx); if (ret != 0) return (0); archive_hmac_sha1_cleanup(&hctx); return (1); } Index: stable/10/contrib/libarchive/libarchive/test/test_compat_pax_libarchive_2x.c =================================================================== --- stable/10/contrib/libarchive/libarchive/test/test_compat_pax_libarchive_2x.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/test/test_compat_pax_libarchive_2x.c (nonexistent) @@ -1,153 +0,0 @@ -/*- - * Copyright (c) 2011 Michihiro NAKAJIMA - * 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"); - -#include - -/* - * Test "tar:compat-2x" option that enables the string conversion of - * libarchive 2.x, which made incorrect UTF-8 form filenames for the - * pax format on some platform the wchar_t of which was not Unicode form. - * The option is unneeded if people have been using UTF-8 locale during - * making tar files(in pax format). - * - * NOTE: The sample tar file was made with bsdtar 2.x in LANG=KOI8-R on - * FreeBSD. - */ - -DEFINE_TEST(test_compat_pax_libarchive_2x) -{ -#if (defined(_WIN32) && !defined(__CYGWIN__)) \ - || defined(__STDC_ISO_10646__) || defined(__APPLE__) - skipping("This test only for the platform the WCS of which is " - "not Unicode."); -#else - struct archive *a; - struct archive_entry *ae; - char c; - wchar_t wc; - const char *refname = "test_compat_pax_libarchive_2x.tar.Z"; - - /* - * Read incorrect format UTF-8 filename in ru_RU.KOI8-R with - * "tar:compat-2x" option. We should correctly - * read two filenames. - */ - if (NULL == setlocale(LC_ALL, "ru_RU.KOI8-R")) { - skipping("ru_RU.KOI8-R locale not available on this system."); - return; - } - - /* - * Test if wchar_t format is the same as FreeBSD wchar_t. - */ - assert(-1 != wctomb(NULL, L'\0')); - wc = (wchar_t)0xd0; - c = 0; - if (wctomb(&c, wc) != 1 || (unsigned char)c != 0xd0) { - skipping("wchar_t format is different on this platform."); - return; - } - - extract_reference_file(refname); - - assert((a = archive_read_new()) != NULL); - assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); - assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); - assertEqualIntA(a, ARCHIVE_OK, - archive_read_set_options(a, "tar:compat-2x")); - assertEqualIntA(a, ARCHIVE_OK, - archive_read_open_filename(a, refname, 10240)); - - /* Verify regular first file. */ - assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); - assertEqualString("\xd0\xd2\xc9\xd7\xc5\xd4", - archive_entry_pathname(ae)); - assertEqualInt(6, archive_entry_size(ae)); - - /* Verify regular second file. */ - assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); - assertEqualString("\xf0\xf2\xe9\xf7\xe5\xf4", - archive_entry_pathname(ae)); - assertEqualInt(6, archive_entry_size(ae)); - - - /* End of archive. */ - assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); - - /* Verify archive format. */ - assertEqualIntA(a, ARCHIVE_FILTER_COMPRESS, archive_filter_code(a, 0)); - assertEqualIntA(a, ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, - archive_format(a)); - - /* Close the archive. */ - assertEqualInt(ARCHIVE_OK, archive_read_close(a)); - assertEqualInt(ARCHIVE_OK, archive_read_free(a)); - - /* - * Without "tar:compat-2x" option. - * Neither first file name nor second file name can be translated - * to KOI8-R. - */ - assert((a = archive_read_new()) != NULL); - assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); - assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); - assertEqualIntA(a, ARCHIVE_OK, - archive_read_open_filename(a, refname, 10240)); - - /* We cannot correctly read the filename. */ - // This test used to look for WARN here coming from a - // character-conversion failure. But: Newer iconv tables are - // more tolerant so we can't always detect the conversion - // failures. - assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); - assert(strcmp("\xd0\xd2\xc9\xd7\xc5\xd4", - archive_entry_pathname(ae)) != 0); - assertEqualInt(6, archive_entry_size(ae)); - - /* We cannot correctly read the filename. */ - // Same here: The test is still valid (it sill verifies that - // the converted pathname is different), but we can no longer - // rely on WARN here. - assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); - assert(strcmp("\xf0\xf2\xe9\xf7\xe5\xf4", - archive_entry_pathname(ae)) != 0); - assertEqualInt(6, archive_entry_size(ae)); - - - /* End of archive. */ - assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); - - /* Verify archive format. */ - assertEqualIntA(a, ARCHIVE_FILTER_COMPRESS, archive_filter_code(a, 0)); - assertEqualIntA(a, ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, - archive_format(a)); - - /* Close the archive. */ - assertEqualInt(ARCHIVE_OK, archive_read_close(a)); - assertEqualInt(ARCHIVE_OK, archive_read_free(a)); -#endif -} Property changes on: stable/10/contrib/libarchive/libarchive/test/test_compat_pax_libarchive_2x.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: stable/10/contrib/libarchive/libarchive/test/test_compat_pax_libarchive_2x.tar.Z.uu =================================================================== --- stable/10/contrib/libarchive/libarchive/test/test_compat_pax_libarchive_2x.tar.Z.uu (revision 344673) +++ stable/10/contrib/libarchive/libarchive/test/test_compat_pax_libarchive_2x.tar.Z.uu (nonexistent) @@ -1,15 +0,0 @@ -begin 644 test_compat_pax_libarchive_2x.tar.Z -M'YV04,+@05(F#)DR3.FQ94D;-D#$B%&#QHT9,CS.B`DCADT;-P"`P,.QJ-&C2)-:K#.' -M3A@Y)&&,J5-&:<:I5:U>=.F1IV5P]@Q:=!HW;P87IE'C!@W-.%9_#NW"#9O7:P8K,$N\ -MN/'CR),K7\Z\N?.)"QL^?$[=:$N0(J.:K(%2^\JH7%O&G%GS9DX8.T'T+`PC -MJ/KJ\)$R=0K5(];B]XF']]H2;/S_`"H'CSSIW%,./0$FJ.""##;HX(,01@A` -M0`,5=%!"`Q9XH(3Z?1022]MUIQ)W_+D4`TPRT6033CKQ1),,0`E%%(?PS?=4 -M5/F9E6-9^X'7'XU`-H@6"&JQY18LP\@R3"K#W#),*.*E6!Z+Z+DX0V'NP4!HJ,<%NB&IJ*:JZJJLMNKJJ[#&*NNLM-9JZZVXYDH< -` -end Property changes on: stable/10/contrib/libarchive/libarchive/test/test_compat_pax_libarchive_2x.tar.Z.uu ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Index: stable/10/contrib/libarchive/libarchive/test/test_read_format_zip.c =================================================================== --- stable/10/contrib/libarchive/libarchive/test/test_read_format_zip.c (revision 344673) +++ stable/10/contrib/libarchive/libarchive/test/test_read_format_zip.c (revision 344674) @@ -1,314 +1,761 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2011 Michihiro NAKAJIMA * 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$"); +#define __LIBARCHIVE_BUILD +#include + +static +int extract_one(struct archive* a, struct archive_entry* ae, uint32_t crc) +{ + la_ssize_t fsize, bytes_read; + uint8_t* buf; + int ret = 1; + uint32_t computed_crc; + + fsize = archive_entry_size(ae); + buf = malloc(fsize); + if(buf == NULL) + return 1; + + bytes_read = archive_read_data(a, buf, fsize); + if(bytes_read != fsize) { + assertEqualInt(bytes_read, fsize); + goto fn_exit; + } + + computed_crc = crc32(0, buf, fsize); + assertEqualInt(computed_crc, crc); + ret = 0; + +fn_exit: + free(buf); + return ret; +} + +static +int extract_one_using_blocks(struct archive* a, int block_size, uint32_t crc) +{ + uint8_t* buf; + int ret = 1; + uint32_t computed_crc = 0; + la_ssize_t bytes_read; + + buf = malloc(block_size); + if(buf == NULL) + return 1; + + while(1) { + bytes_read = archive_read_data(a, buf, block_size); + if(bytes_read == ARCHIVE_RETRY) + continue; + else if(bytes_read == 0) + break; + else if(bytes_read < 0) { + /* If we're here, it means the decompressor has failed + * to properly decode test file. */ + assertA(0); + ret = 1; + goto fn_exit; + } else { + /* ok */ + } + + computed_crc = crc32(computed_crc, buf, bytes_read); + } + + assertEqualInt(computed_crc, crc); + ret = 0; + +fn_exit: + free(buf); + return ret; +} + /* * The reference file for this has been manually tweaked so that: * * file2 has length-at-end but file1 does not * * file2 has an invalid CRC */ static void verify_basic(struct archive *a, int seek_checks) { struct archive_entry *ae; char *buff[128]; const void *pv; size_t s; int64_t o; assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 1.0 (uncompressed)", archive_format_name(a)); assertEqualString("dir/", archive_entry_pathname(ae)); assertEqualInt(1179604249, archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); if (seek_checks) assertEqualInt(AE_IFDIR | 0755, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualIntA(a, ARCHIVE_EOF, archive_read_data_block(a, &pv, &s, &o)); assertEqualInt((int)s, 0); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 2.0 (deflation)", archive_format_name(a)); assertEqualString("file1", archive_entry_pathname(ae)); assertEqualInt(1179604289, archive_entry_mtime(ae)); if (seek_checks) assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae)); assertEqualInt(18, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); failure("archive_read_data() returns number of bytes read"); if (archive_zlib_version() != NULL) { assertEqualInt(18, archive_read_data(a, buff, 19)); assertEqualMem(buff, "hello\nhello\nhello\n", 18); } else { assertEqualInt(ARCHIVE_FAILED, archive_read_data(a, buff, 19)); assertEqualString(archive_error_string(a), "Unsupported ZIP compression method (deflation)"); assert(archive_errno(a) != 0); } assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 2.0 (deflation)", archive_format_name(a)); assertEqualString("file2", archive_entry_pathname(ae)); assertEqualInt(1179605932, archive_entry_mtime(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); if (seek_checks) { assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae)); } assert(archive_entry_size_is_set(ae)); assertEqualInt(18, archive_entry_size(ae)); if (archive_zlib_version() != NULL) { failure("file2 has a bad CRC, so read should fail and not change buff"); memset(buff, 'a', 19); assertEqualInt(ARCHIVE_WARN, archive_read_data(a, buff, 19)); assertEqualMem(buff, "aaaaaaaaaaaaaaaaaaa", 19); } else { assertEqualInt(ARCHIVE_FAILED, archive_read_data(a, buff, 19)); assertEqualString(archive_error_string(a), "Unsupported ZIP compression method (deflation)"); assert(archive_errno(a) != 0); } assertEqualInt(ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualString("ZIP 2.0 (deflation)", archive_format_name(a)); /* Verify the number of files read. */ failure("the archive file has three files"); assertEqualInt(3, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_ZIP, archive_format(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } static void test_basic(void) { const char *refname = "test_read_format_zip.zip"; struct archive *a; char *p; size_t s; extract_reference_file(refname); /* Verify with seeking reader. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 10240)); verify_basic(a, 1); /* Verify with streaming reader. */ p = slurpfile(&s, refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, p, s, 31)); verify_basic(a, 0); free(p); } /* * Read Info-ZIP New Unix Extra Field 0x7875 "ux". * Currently stores Unix UID/GID up to 32 bits. */ static void verify_info_zip_ux(struct archive *a, int seek_checks) { struct archive_entry *ae; char *buff[128]; assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("file1", archive_entry_pathname(ae)); assertEqualInt(1300668680, archive_entry_mtime(ae)); assertEqualInt(18, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); if (seek_checks) assertEqualInt(AE_IFREG | 0644, archive_entry_mode(ae)); failure("zip reader should read Info-ZIP New Unix Extra Field"); assertEqualInt(1001, archive_entry_uid(ae)); assertEqualInt(1001, archive_entry_gid(ae)); if (archive_zlib_version() != NULL) { failure("archive_read_data() returns number of bytes read"); assertEqualInt(18, archive_read_data(a, buff, 19)); assertEqualMem(buff, "hello\nhello\nhello\n", 18); } else { assertEqualInt(ARCHIVE_FAILED, archive_read_data(a, buff, 19)); assertEqualString(archive_error_string(a), "Unsupported ZIP compression method (deflation)"); assert(archive_errno(a) != 0); } assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify the number of files read. */ failure("the archive file has just one file"); assertEqualInt(1, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_ZIP, archive_format(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); } static void test_info_zip_ux(void) { const char *refname = "test_read_format_zip_ux.zip"; struct archive *a; char *p; size_t s; extract_reference_file(refname); /* Verify with seeking reader. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 10240)); verify_info_zip_ux(a, 1); /* Verify with streaming reader. */ p = slurpfile(&s, refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, p, s, 108)); verify_info_zip_ux(a, 0); free(p); } /* * Verify that test_read_extract correctly works with * Zip entries that use length-at-end. */ static void verify_extract_length_at_end(struct archive *a, int seek_checks) { struct archive_entry *ae; assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualString("hello.txt", archive_entry_pathname(ae)); if (seek_checks) { assertEqualInt(AE_IFREG | 0644, archive_entry_mode(ae)); assert(archive_entry_size_is_set(ae)); assertEqualInt(6, archive_entry_size(ae)); } else { assert(!archive_entry_size_is_set(ae)); assertEqualInt(0, archive_entry_size(ae)); } if (archive_zlib_version() != NULL) { assertEqualIntA(a, ARCHIVE_OK, archive_read_extract(a, ae, 0)); assertFileContents("hello\x0A", 6, "hello.txt"); } else { assertEqualIntA(a, ARCHIVE_FAILED, archive_read_extract(a, ae, 0)); assertEqualString(archive_error_string(a), "Unsupported ZIP compression method (deflation)"); assert(archive_errno(a) != 0); } assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } static void test_extract_length_at_end(void) { const char *refname = "test_read_format_zip_length_at_end.zip"; char *p; size_t s; struct archive *a; extract_reference_file(refname); /* Verify extraction with seeking reader. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 10240)); verify_extract_length_at_end(a, 1); /* Verify extraction with streaming reader. */ p = slurpfile(&s, refname); assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, p, s, 108)); verify_extract_length_at_end(a, 0); free(p); } static void test_symlink(void) { const char *refname = "test_read_format_zip_symlink.zip"; char *p; size_t s; struct archive *a; struct archive_entry *ae; extract_reference_file(refname); p = slurpfile(&s, refname); /* Symlinks can only be extracted with the seeking reader. */ assert((a = archive_read_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, p, s, 1)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("file", archive_entry_pathname(ae)); assertEqualInt(AE_IFREG, archive_entry_filetype(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("symlink", archive_entry_pathname(ae)); assertEqualInt(AE_IFLNK, archive_entry_filetype(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualString("file", archive_entry_symlink(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); free(p); } DEFINE_TEST(test_read_format_zip) { test_basic(); test_info_zip_ux(); test_extract_length_at_end(); test_symlink(); +} + +DEFINE_TEST(test_read_format_zip_ppmd_one_file) +{ + const char *refname = "test_read_format_zip_ppmd8.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_ppmd_one_file_blockread) +{ + const char *refname = "test_read_format_zip_ppmd8.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0xBA8E3BAA)); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_ppmd_multi) +{ + const char *refname = "test_read_format_zip_ppmd8_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("smartd.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0x8DD7379E)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("ts.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0x7AE59B31)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_ppmd_multi_blockread) +{ + const char *refname = "test_read_format_zip_ppmd8_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("smartd.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 12, 0x8DD7379E)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("ts.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0x7AE59B31)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (ppmd-1)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 14, 0xBA8E3BAA)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_lzma_one_file) +{ + const char *refname = "test_read_format_zip_lzma.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_lzma_one_file_blockread) +{ + const char *refname = "test_read_format_zip_lzma.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0xBA8E3BAA)); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_lzma_multi) +{ + const char *refname = "test_read_format_zip_lzma_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("smartd.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0x8DD7379E)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("ts.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0x7AE59B31)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_lzma_multi_blockread) +{ + const char *refname = "test_read_format_zip_lzma_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("smartd.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 12, 0x8DD7379E)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("ts.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0x7AE59B31)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 6.3 (lzma)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 14, 0xBA8E3BAA)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + + +DEFINE_TEST(test_read_format_zip_bzip2_one_file) +{ + const char *refname = "test_read_format_zip_bzip2.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_bzip2_one_file_blockread) +{ + const char *refname = "test_read_format_zip_bzip2.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0xBA8E3BAA)); + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_bzip2_multi) +{ + const char *refname = "test_read_format_zip_bzip2_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("smartd.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0x8DD7379E)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("ts.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0x7AE59B31)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xBA8E3BAA)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_bzip2_multi_blockread) +{ + const char *refname = "test_read_format_zip_bzip2_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("smartd.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 12, 0x8DD7379E)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("ts.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0x7AE59B31)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 4.6 (bzip)", archive_format_name(a)); + assertEqualString("vimrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 14, 0xBA8E3BAA)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_xz_multi) +{ + const char *refname = "test_read_format_zip_xz_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); + assertEqualString("bash.bashrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xF751B8C9)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); + assertEqualString("pacman.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0xB20B7F88)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); + assertEqualString("profile", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one(a, ae, 0x2329F054)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +DEFINE_TEST(test_read_format_zip_xz_multi_blockread) +{ + const char *refname = "test_read_format_zip_xz_multi.zipx"; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); + assertEqualString("bash.bashrc", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 12, 0xF751B8C9)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); + assertEqualString("pacman.conf", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 13, 0xB20B7F88)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("ZIP 2.0 (xz)", archive_format_name(a)); + assertEqualString("profile", archive_entry_pathname(ae)); + assertEqualIntA(a, 0, extract_one_using_blocks(a, 14, 0x2329F054)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); } Index: stable/10/contrib/libarchive/libarchive/test/test_read_format_zip_bzip2.zipx.uu =================================================================== --- stable/10/contrib/libarchive/libarchive/test/test_read_format_zip_bzip2.zipx.uu (nonexistent) +++ stable/10/contrib/libarchive/libarchive/test/test_read_format_zip_bzip2.zipx.uu (revision 344674) @@ -0,0 +1,19 @@ +begin 644 bzip2.zipx +M4$L#!"X#```,`#TQD4VJ.XZZ-`(``)`#```%````=FEME,ID:&U-'E`T&C1IIIZ +M@8&J>$9":`IZ@``'J:/2&#```````!@`2FB$T:31D9"F@8FC0`\H^:+Y;81F +M6OH?UN^\U&IFQOP9(.Z29 +MPY-_:/D\-$@RAM/2I>(FNV$2K!!&6I"Q+LXG0-YLG/4-JQE +M=%^\,6JL1H*""1!1W]4\/.E))/*(3J)$L1_D*SPD +MAZY)I5-#7@)=-5H06@2PP-)YSB5D!.3J63\EJ*\X%RMDE]>>,6XG5O@<^@:T +MT671Y,.:/=ICJ(=@\88>!>M^W)$;,QY(*`F*%Q2YZ^%TF$V^/85:V;L\W53/ +M?9[-IX&M.O38C\;C@Q';$E9[,C'?<#304$$9O&4U<2"$^;O(0X:)KR2UT?=.E*K!>,09(4\>BYUO&*8;D4 +MY=51BK5L/;$+WB"S8Z-?)M+GT/]^;,7$S'N0)))KF]$Z(GL[.L=F*G#!4NC) +M4)I$P%W)%.%"0YPC!DA02P$"/P,N`P``#``],9%-JCN.NC0"``"0`P``!0`D +M`````````""`I($`````=FEM-]>-Q`H``"L:```+````H'J-##(C0U&1)Y*>F +MF4:'E-&0:!H``-``)30B!0]3TFDVD])ZF(::`!Z@`T9#30,)"0@FBGD*9-IZ +MIZGE,F@9``-``&0'`````#````````)$00FC1,(TE/T:,HTVJ?J3/5-#,4]3 +MU#TADVAYI!A\^>RTQ(BD3$3I$[^,/; +MLS`?JD6R.G5Z9H!*U7PV]92S^P:OR\HL0A0_3>$BI8)(/R)[BOOLC2%8YJD( +MA.HIB.`H&#CG`1Q7XZ?+[_/^7_[H%_86(A9>\@A,_,XZ=,XL5W'7 +MHP4M+H9TD.>'-*H@_0+C17D%)P0)RW]IX%[$"C`LP1H=G0_&E!1":MS%AMXQ +M.;)R8XI%.5\07+:U'.MSO0WA +M_'JHZ5GH`I2IS+,0&673'.$+E#O<4'!QIOJ(=A]MS$!DF&L:[UWD`N@'J&ON +MK"9F:A.4,3W/HWR,FFXKDW_PF?19FN: +M8JZ13*&(2TCX35$",$])GPS2BLBY&_H+ZVL;>UD^#7W[$P11S-:X+;I&4P_2 +MY2FF=^Q>)*S'RAW&B*<0LART09,I)(!+E@9";HI&D+G>(HXB:0(%/23%!+S@ +M:NR/5*I%\9CK3:PVI^LQ +MG?A&-RQT6VZ[1QPG2Y9UVU<7W0N_@W]>`:ZV*-IMC1AC'"5PL)0>9PPF\/OD +MXXHMUZ\%[^LVOK%3L]),1F)*MOQ5)H +MBOICX8!01L:?$[E<$R]4`!$&F!"N^Z]2A2JH&\;#/"R\MG<`!XM)L2.#%FT@ +MI$"[T#%/63SY#K=LX#RSQ*%:^J%L1'HN2F*:.2LQ2X?"^Y^<9@U@PV(.4E%C +MNS^(@N"/+X8L:9,0T^E]7FZXBP#;&@K7#\.W[MOUG8-`-,<'O._DJ$2@7*?N +M#ZO8W/#Q7.=4KXGTY)7:1_NJ7-3FF\<6*$<^N]+-!C)W4+U60F$@>:,&%)NE +M6U9J0*F"F4!.R,Y`.G*"*P_*(P>-<7WV6=I7*E]98+HG":L0P4Y,?%RY^X[* +MDSY./:P;H^X)PU&L=#"2/IS$=1]X>MB:/!;68(&.EX`-X^"ZW_F;B,/8(F45 +M`_?U2XOBQ%&$HQ1YCTEC[_A42U:75Z>4-MI3.*/X>72/`C"+5Z>0B4C49O3A +M'N8'1IOJC95M$"-@1RC6(E"P.0[ZZ:;J?E0+#0?LP-6OA,=$^`?$]R)HW%K-',>W@ZBCI&CX:1\Q,YOY&7QLLV_J,<-^S+:AAO&'.A;*V +M3AQROQ<,BPB$?,M'!#1QR@/4P#TM5"<*D.$6U5>6NB0:@2#RD1AD +M(26E6M&U7;I&M?><,-FD48N@:,:2[U9AD#8DO`F8G`NH2VG:1>/.?`E4V(J^ +M#5)1$;.G/E?/U7S,RZJL\[5*#)Y+2^&5-1.L.G9B!:4)5`06C6IFT)6E9!%+ +M2NC400RE=1Y`*MU7P;V=>_94Q:13C>/8$;NI99OUE6QCKTH&-VKEJ+?SICS8UQ>&55 +MS!AN47T#J*?QEQ6,M2+`@9;Z#.*GM;18!J.<)J@Q;$1#.I*.KS4[,-,#$>-\ +M)PO&"I/*D7`.K'08;Q%TA8:$`G^HT8=;C3N5_4;IF)[F0N$3TDH*Q0-P<[42 +M,33G53,R)8(253>'`I(/(`C4Y$P=LZC+Z#,LC!<`8.=$0&& +MHL)>D\@AT(1A;6.-(URT`6J-@0P%Y020G3VP:QRCZPM[@QB;&[&]I'1L3HG6 +MHMV_<`9H[#0AJH/K\46RSPQC,;0Z:L!U8.MI-D=NTHM!=_;/K:@AM+BTH-:,C49$_(R)IC&^1Y +M[EI=1!L8O8:A+.F,:91'!%LGX.Y*[9W'8Q>2DN$BIBQ(Y()FJNVR"%T-;E6?@6^'&XMHR)AAWR$C9972+M#HSN- +M);+!>$HVAW9>(FP:U,1BUS180R6S:<$UD99OL@!``!4`P``!P```'1S+F-O;F9" +M6F@Y,4%9)E-9[2!3B0``35^``!1(A>=")0$"`+__W^!``=V;:TML-3U*>D]I +M3`F]4]-(T9`T:&@T8I`F(U'ZIZ@!H`>IZ&`````P``"4T4R9-3U`$T]$`,0: +M9KMEA9T)[OUF#<]_H_:R!%VW(Z5<8GI,92Q$\@_99'.V;0!9M_JQW(Q83@(> +MU(&RK]97O.4,(?9Q\O->C6^K@,2&/*Q^ +MQ&&;:95MR[%C&6KSOTJXV':CWZ0#H?FO+]L:K$L-@\J??66K"[C''`Q(05A5 +MAA`H<]68]Z8WD3,92%/*1-[+TH^&FHBX0;F(=TXT`&\A85\I0".^@K=W'6BV +M6W7I=FG(:-GP/>J!T.)K9&E&`70E\*O7<@B"XC8N],ESUC>J<#0$&LF`$Q"I +MPFSXM(AMXP]!B.B$A)+*A@FQI7N,888C3<<1)$V8APW#69B,^)R&)J6R?!9U +MVQ?F1G9TTEH&P"PV"U$F)!()!JGQ4/1670D)VLF!P-;I.\BHY"F((+!C$AZJ +MZ4L(AR$T$'4N]0NK3DSCOKGQ@9(O?$J:V8>5Q'*QHI5Q[SIJ>$D/XNY(IPH2 +M':0*<2!02P,$+@,```P`/3&13:H[CKHT`@``D`,```4```!V:6UR8T)::#DQ +M05DF4UGG",&2``!,7X``$'3GX%(B(Y<`O__?X4`",3M`&&IZ4RF1H;4T>4#0 +M:-&FFGJ!@:IX1D)H"GJ```>IH](8,```````&`!*:(31I-&1D*:!B:-`#RCY +MHOEMA&9:^A_6YQ\4L6YH5-&(7`A#+:]%S/&IQ$[W/8H;ZFA%01)[[S4:F;&_ +M!D@[I)G#ES0BEILMLL482*!*U'E=+=]T@I+>3,SRC1"`R6N_&W"N!*6`ETU6A!:!+#`TGG.)60$Y.I9/R6HKS@7*V27UYXQ;B=6 +M^!SZ!K319='DPYH]VF.HAV#QAAX%ZW[ +M2:!4(E-IS87%O<(M4.)02QAK:[^IR!Q:E(+-CHU\FTN?0_WYLQ<3,>Y`DDFN;T3HB>SLZQV8J +M<,%2Z,E0FD3`7-]>-Q`H``"L: +M```+`"0`````````(("D@0````!S;6%R=&0N8V]N9@H`(````````0`8`(#N +M7F'.E=0!`,YV<T*``!T)82Q1PWAL +M+U`,N0L_$]^&650C/X$D6#4QFD$\A/"_![4!O/5O/!KH`WCQ*4?T2*]4P#/D +M0'9I?EZG=N69Z0V;H0I=CP*$?".I\ +MGMG/80.A'^W>R4J'S/CZ%P`8`>F=R>R&R$2T@EM#X)"OQH1?A7,`:4IU9WV! +M#2W*DXT',;.4YIN4A:-X)O=IREL201ZSOC=YSAU[C4-::/YV8\)%"L17+>VC +M%/'B]ZCQN$2(Q*9*\KJZ`Y131`]5C&G';@1S-QES_RZF!2OX45@58+??ES%( +MUJ<(\`11M$NO)HK#/MK-9RT"15.2I:IZN8VTM1_?$G\L#BH67]$S%[4 +M%C-$\Q<+./&HV](4,7)OL-@C^M0F"2O!0N$OHOW54H87^QLBQVH*D%A<#SI% +M/#+-5U(W';:KC)RE>0Y^5YI!RECQNR"R4.UW9IR!@:B!UB8?_D5$FT8YCJHJ +M2[2"-&-_D2BJ6#XK[6G=%K"%;'^-+0]FHCY4ER#`^-]>-A`H``"L:```+````?C+)VT(T,`U9;I1/'N)!6C.3M-O9LO]H<^=OZF\ +MXD\QEZIMP!JM4)W+(F]%N;U#&+F]WZ%S9>>]&^75:X)0Q-5>)IRT/%INC887 +ML0UF,PS8J@3;WGH95EW4\KBH,6ZK9,'![3$-(WL/C<[0.6%L9@]0;]_>&[A'#Q^##`6+*PM@YT+]E[B9 +M;QK38:)CO+S]4X2KF'=)BVO-GM+_#]PB0'*J89+$Q11IG\\+84M](58^RW.E +MGM3S6RH]#73P.KV0V,2=,/A!;G>2Y4R!!.4(0U+D<.'5V*%./ZZYD")=W4*4 +M\SL"IUJLN])WM`-&4L&!9&U_-1%%SPU0N4*.^L5&33"A5MZ;@8F+I^D7JB'K +ME&,P>"UI]NT;ZX`UU->]V$?KZV(DX&-6L>? +ME,(J<;DNA6/2.<9$>?-P]_!S8L3/2P@[C'H$FOA$WUOB>;X$)E7;K+7#X9M( +MM+H^AK#CML0LXTO#P#I)J_5"@O +MMGH9+OXD^P7AG@<%QGH<>FIWE]/>L)6Q2Q\CC=[?TCG262"1EO)'_W_C_K4< +M8.7YNR"3\9WTQ"0-!>^,M>WT.Y0A&3AK.4/YN5EW]"&HQ1=8Q-NHQABVH7$% +M24Q:J/<8%F1EI)-!HND&FP_;]E,JI$*AGE&]Y#FJ5R6<(UM;;>U?KU@OQ,M_ +MI/+:;0^0'8JA"==H5/;]Y10P&PS,1_M+D1I>`%>_Y\&F[_R/##784#0AQ1!O +M'8>IBY?;&;(VAOI$+Z[VMI>]WC!['W$&Z5/D+"6["[[.[P!/?9C/;CD66^J/OC) +MO0JXP'Y6]PGW8N@X(#-XXWMP"A%E$A8L+X>QIC[0P,UT0A!IYQ/#+>E60KX/ +M2JC'F-Z@_,-/$7+F5F$1\#^"CTSY;H2EAE,/8>F$^'5H!M2@D?2O@+NJ2&-O +M6IV-[<6F-JG0(,W-YJ%PJ<7T.F")'/+F_`F6B-HG436 +MJM-VK0?>9*R?K#]OHC90!]BCG'^D&%!JZ2T_&[RWV?0Z?T:#_\B`?:MRC$D5 +M1Z.)!/;3/=GT*T;DC/XLR;7S%VEP+U-M)-*V,1?M<377S9-`J]S4[OI4SR>V +M_?_H2WWH>\@*$V@`LZO?-'Z[4_4"T2R@=%! +M!\1]:4M((1B=ATAJ)23HE7>]=,7I#SK5'2OUZLX0LX*_67!+6S:*0UL)^GS6 +MM;$2!(-TY%4ME1>*'M)S'[0JP^&J%"-&A[C1YE;1:X_4Y&K9*2 +M'14NC*]O(JXNLX#M_(LDE6$G<0,+V/;44]-*^S%7G*.\$W&>2=9,EYB)/@XK +MX+E4?>NIP=*X]Z>3T,V?!"5:/M@X-H%DZEV,*9WIK;T_C18UC5$%^Z5PY8;USNB?S]`D-K2V2 +M-C#0-45C'YFF>#*$9JZ!$>%0;=VQ@LGY"J<.C.10"1'JL^B(VXPNK^]Q97E\`HX0MG2)F=>38R,IFGK\R +MVH!.V3_)_D,II8A=SL;X1!2D9E67=.$P+_D6&'X^1;4%:`ERV>!,''7<#=NE +MP')[\,A89G35\;ZN:IIY5DDSB$(8D$[G>7-L!(+(-7=LQ0Q3=S`L-)R9YT;S +M;%ZYVD%6)M=I]*QS>!3='"SB'E/4>J#EQ.&]0*U0H/Y1%DM].)E/UFD0?Q)B;_;;)-K_]6Z<)D_>^[=?49%4D4B@REC` +M,_KU,KX-N,44CL/:*^D>7X(;6_1&C;<;&`K3+4NY[=4%K2K:U"Q%T9)',7," +M+PC``PBBDVNY9V-LN'V')B4@4;OH(\/M%UBX$S."4_!;&=9W\L`!2O\!ANM5 +M8+""#5J&/M[2;M?47WN=0[4C*$7D#QB&`!*@5_0 +M9(5SZ7,Q"N?0W!\0WGQF,0JKH[G-(<6^PMJZV&VHQIT?VKJ^7^!EW?)`EC`$ +M0%,=?X0ME@T_&5UXJIJ9)5(<[=W]M#Y.$.96:_T8WM/H+"T_Q;QZN"`5DC&' +M.+(^")0XO=#&#@KVL2R9+.#F)P02=`6PT[21&>T*Q3Y2WVL><:PHB)R?*'+3 +M+,OK(7PNXHXMVFT"!)??8!H^I,&V +M8BOE(Z/VYY(5WLSG$.+8$;.A>]+PVB`WV,9M@YL='Q_<%2STU?_IG.I17G^L +M>#-=')K6T?C[^&,7Z)0!B[2-)2:&?*5, +M,;&-HI_/O(/V"4-:_E>0Z)=C=1/9HQJO$R:(&H@4RP"3%T[BOBZ`3_2>)K49 +M\ZL-26)MQ\%(>T0S5A4>EUCU[),JI"_/9<5)*6X\97`N:8YSR0%)@*#_'9'? +M/E=RFK"L&(#RS$S_0[?/!DW.`\FH$M\#"JSQ@*&C*W\`4$L#!#\#```.`(@X +MD4TQF^5ZO0$``%0#```'````=',N8V]N9A`"!0!=`"`````1B`2H,L'S88)4 +M*WW.8!PX^&C9J^P?<4.RSJ^WF7L-W#+OB4@(A-83?&.PVF@$.:VM<4\8K(_6 +M`CRI/@MR:,)C8$&U*MZKA\W'KCI%E2GDYYE/SB'*\S@5]NO,ZBF!;HY@ +M3Q=F"]@P02:6^LC>JT$T7BB(.:*G`I.?%/53CC#4I0N_ARFXFG`DZSI*$)UM +M1^X:IZ^^)T*5E,C6L<,L%=ASF-1:0CP6*FX_;?R;#]C:/ +M)C@@M]\FLU4*%H^>8VW4@@%D0U!02P,$/P,```X`/3&13:H[CKHY`@``D`,` +M``4```!V:6UR8Q`"!0!=`"`````1"`2HPGR1L@-2P5SX3!72_Z.U"NE"C>=! +MB(;_TY1PR.H(G3<^QXEA+%'#>&PO4`RY"S\3WX995",_@218-3&:03R$\+\' +MM0&\]6\\&N@#>/$I1_1(KU3`,^1`=FE^7J=VY9GI#9NA"EV-P&-RI:_YA-3= +M!E?#E$05TTLL[G6$<%[`H1\(ZGR>V<]A`Z$?[=[)2H?,^/H7`!@!Z9W)[(;( +M1+2"6T/@D*_&A%^%+WJ/&X1(C$IDKRNKH#E%-$#U6,:<=N +M!',W&7/_+J8%*_A16!5@M]^7,4C6IPCP!%&T2Z\FBL,^VLUG+0)%4Y*EJGJY +MAS2K)[;2U']\2?RP.*A9?T3,7M06,T3S%PLX\:C;TA0Q; +MZC0FI6JUP3:]D^3L->OKP>:G2P_FF8)V>\LVQ^;MC'-2N-,GXD"Z]PZI8A79+7ED*DB,K&N'OH,DT/C4$L!`C\# +M/P,```X`D3B139XWUXV$"@``*QH```L`)``````````@@*2!`````'-M87)T +M9"YC;VYF"@`@```````!`!@`@.Y>8EJ>3<8@F_*<(\B>K]4_(WC8#)`_QSG+`7`B&_11VIJ)@#(K)_I8R^=`VUO2_S,1C=1CAU>-*`]CC+&6Q;EE'#CG-W=^[,F,+UR-TE(9,G +M1_&$NYD`,5WY5QX7@%5XX-*7+7&#W'1#XF+"?6*U!H=B063.]NTA`1+&\J(K +M&U]DE8$ZYUU'UT\$!88I,6+(M/"=_+3@V5H+24 +MBA0>^0?[W07K"B%SQ&OA-R)S9W>DH,<4##E@?5YF:%K$?5U@W[IVG+2#8FC4'GT3-$L%_N,B$^)UY1GAD=.Z(HI#3[T"979&<`+ +M`S]9G+PI';5"==`18UE,>N"?>"_C"MSHX<[!&8$+A6U_7][TK*6.^\-O-UK! +M`)>[*.D1:1H!I,?PEIPW,NQ5CCX2NCY+%UH"T5X!O$&=6+X#"()33:]FPF:F +ME-O)\652#KD$^ZFBJ7K`55]R/A"OA"T7\R6K%B(2&6*/H0>*-:@< +MJ2V9^KJSL+P4A&$`4$L!`C\#/P,``&(`/3&13:H[CKK8`0``D`,```4`)``` +M```````@@*2!`````'9I;7)C"@`@```````!`!@``)=1P<:5U`&`PJ3,QI74 +?`0"74<'&E=0!4$L%!@`````!``$`5P```/L!```````` +` +end Index: stable/10/contrib/libarchive/libarchive/test/test_read_format_zip_ppmd8_multi.zipx.uu =================================================================== --- stable/10/contrib/libarchive/libarchive/test/test_read_format_zip_ppmd8_multi.zipx.uu (nonexistent) +++ stable/10/contrib/libarchive/libarchive/test/test_read_format_zip_ppmd8_multi.zipx.uu (revision 344674) @@ -0,0 +1,84 @@ +begin 644 test_read_format_zip_ppmd8_multi.zipx +M4$L#!#\#``!B`)$XD4V>-]>-.0D``"L:```+````X%[[99#N5_N2Y@T-?"T%*8O@,-K>QSOR>-).,0-Q:=7)+:*Z&:B9' +M-YXDKOWK"T?"(.;F$3'A>TW+.W#W%Y0`:_5XI+35&4_L<0>95Q!XP,\T=VCM +MZB%S4G,4ED*-4T#5Y'BUCMS!0Z)0*50U=EQWM_F]2)<8#VF(432?^.!'O`I/ +MLP!A8AN1>8;MQ:S?/4-$K07X@["K?T]0<=84:OJ]Y$.D)3MNMK`U.(ORV3M& +MV`2EDVHT&D'F-=\,_"^;UGWW!=,KJI4^NZAF72$S=N.*PW!#ZJ?KQ0;$7E!] +MFC,:ST/]Y(,:K3SK7,%&A_%1`($9H@/\G.RN$J7BA?C\P"[U&[8MITK*`U5E +MVH'&CJ8?%1+H-H0=B*8]_(F;;/P`371(@?QN2T<4C52L;%S-XV),E4-V05&I +MA.V?U+HM63;JH=1]LJ_E"@JM?QH\6#NQ7I\I*%-!)LWB?U):O^--2R=1C):^ +M^@GFC0GETH%>LXD8_D*J'KS5[/M5+89M;6@3?]I.`M=;(S7*1?(?.9G!`S26 +M([JDK/(RCM%WYC`>G*L7:NJ!5TS[7MI^E[4/\RU9$&1XJ5#V;<'F^AX^R@P7< +M/2?UB;-2+I11]^X;,J\:,'^R=&8"@[EI0$J:":*LQ-F(IOSXC?1W>/;?4$P* +M;=V"OEY@S\%3'&F-^K0FME.]%1DTO*OP8.:M('3W)\S"1.@8# +M6Y1&@L+#!YVL]8.,[UP/\HMAR1"O!M/2?DY-8[N'P[T&2J7-V5%=XWMRQ,EM +M7TL^99!FXY0+B1^7K36"#!85"WXE,WFEU=B!+GX2$D<)MW>&A,8A9RPL]CR( +M!=1JX99-:5D;PM@S4(>?BKTFAQ>"%9\Q^Y\6T\ +MTS"*[M.TW7AE:#\(:M_JLJ#*-9-O,UW\T:PVW8G01OR!B59P=[:XNH0Y=OC9`KJ_%.>^)RW-,G^%%(U9HC957G/\@ +MW1\J*,JLX*CF*,-!'^O*E7=V#S8_#G`9YQ)#%,AJ8"-4B,OR8RW4@L)8F$JB +M.3QD;;M_L<:*T3B>8\OECY@*'6?[2VYXB3B!`<:65*HB/.^@;]\`IE)HTIG=_X:*3A'#M\P0L*]^ZFWC\UL`O;WL@#?"!(_]OZ4=B<\QL/ +MWD%/TL5[P\,^_3G\]WD-Q`?XU]K5LFW +MI6J$G\=W(Q+2_U&THGA'_6)`"WK=X74-OD_B%WJU+JH/A"K#JQ@)A<`>A6[H +MC2A>S7]ZV/H?/1KV&OR8B]IS\>Y:\5;B-:+E^V`:5_O[+(!,6LW0&[5(F4UH +MKM;R]_V\?PI6I+"7M1H%E':WL<1#J"O`>]S*UKS02ME,S7]X66@[B`_#?H$^) +MT&07IY[)18RK+`@#?UXT=8B=XC$J'SSJ>XP2RJ]>02[1J:R;0"M&L>KY(3*9QLH1N@K?LL%3J%O? +MR!>+JHS'L3_<*?;*81D1'/A-A"%%P..0%#Q32?L88\C'/'W?D +MG766\,!14W_TY,.;G,\C.+L1LR__7UR*_H!IE,1LZO,_+#?C[OW4+[4I`[7] +MZ;U%`5#@;6A5#JU]H+\E;^^LQBHQ$-T.P5.V/JS69'PI06.%#D4 +M%B.K!F(.G*6@/>[P-FU3-#%N.EY%W*]P_]<>?07:7 +MK&JYBO\0HXN^[/OA#_MP/QQG-+1?Z)7*WRL14*DMWL2795*UHMN/'POIN@Z= +M&&>Z/LT#HX00EWLZ%/G7)X<":!ST&P+Q0R)=V6!#D]TY1RYO!C&5&''Y<9UK +MK=LY46W;=`;(^L<>RMMG%(;??BF+AY,7=+H)6XSZ:2#^`W=RR7KIE>XQ/[_^ +M^$F4*967)DE3/^&\(7W?:EKRJ,PN@HOT:U_.!1-"7A-=LX\Y.=-37_355)/2H +M5V[LT(83$V*>?^YW7'6.SD=MYSM<`[M'IU]?H2ZU8\SLD+R-Q__8G +M2A&2#H5-J+*NQHQ&ZX$YO_(.OC\W[3B6WB#"MKV?*/<=(JBTKB`D_X'X+7"Q +MJ$B]N)L:OTXC':GK/!QYSR.,%%T3XI6(C(3&XZV`.ZLS=\O7>0 +M#N;$J[W)(Q.5<_I5G1K^FB10(.'.#Z!,B8Y'WX_=XOM8\+X/;Z\!"0HTWZG# +M[:0<%(2)`J>6>9=.B5&CWX.,@3RF>FN>'-0))N"!IY,\_?'B`DNYJ4'+!JK] +M\NX-@,,Y%)>#36Z7>CBBL=0S<@W(QRXPD3:VF-W)CS52HO!/T%NV[$0^B01. +MZH\`/9ZTF=$,",`0J\ANSPF&X.0O!?$8\^M%XSM$`D)W".C60(#6;('?*'.` +MTN9>^YI5M0P:-7MCSU2HCCU'`[!+^"9P9BH%1<=K_B#UZ3CDJWX+V\C^S$GD +M]M3M2VJY4''"]RN@>]D^K6B7ZGQG!<4'LZCK,BU]QH,1G(P$AB-W/G%7[.`! +M"P[DH--W#`"^#];7=H09``!02P,$/P,``&(`B#B133&;Y7IM`0``5`,```<` +M``!TCE(/-F<.B*/H_C$3G..AVGS/6(LHZG/Y[UI +MO1E2,[]ZU6`C\+WOIWU\>8)Z?4>]`A@"D*I3B52YGN&M&ME*-/]I!1,P.R@8#3-@,D#_'.`57C@TIL*(7/$:^$W(G-G=Z2@QQ0,.6!]7F9H +M6MRJ>G_FVK['10^FM,>V7UX1]76#?NG:?1,T2P7^XR(3XG7E&>& +M1T[HBBD-/O0)E=D9P`L#/UFL!57W(^$*^$+1?S):L6(A(9 +M8H^A!XHUJ!S!KSA%4.]<`XB*.5^_Q`%$6ON%MH"W>Z9/P^MG.I)@N0WI*EE+ +MX2HV+?E8)N&["2\:UYRI+9GZNK.PO!2$80!02P$"/P,_`P``8@"1.)%-GC?7 +MC3D)```K&@``"P`D`````````""`I($`````8FT!``!4`P``!P`D`````````""`I(%B"0``=',N8V]N9@H`(``````` +M`0`8``#P/%?.E=0!`,YV<5U`$`-+O[QI74`5!+!08``````P`#``T!``#O#``` +"```` +` +end Index: stable/10/contrib/libarchive/libarchive/test/test_read_format_zip_xz_multi.zipx.uu =================================================================== --- stable/10/contrib/libarchive/libarchive/test/test_read_format_zip_xz_multi.zipx.uu (nonexistent) +++ stable/10/contrib/libarchive/libarchive/test/test_read_format_zip_xz_multi.zipx.uu (revision 344674) @@ -0,0 +1,125 @@ +begin 644 test_read_format_zip_xz_multi.zipx +M4$L#!!0```!?`$JCP4K)N%'WP`0``,4(```+````8F%S:"YB87-H%H<2H0"6A +MIYB7=GGPH!_8+Q:$I09*"\)&%HUP2'X^1_;,\T5=!'^A#5>U*#)2^Z>L/*E5 +M%Y=SZRY&`Q_\(3_!//$I\E.SDZXL)WCSE7*0<#ZW/W*WM?^3CV,,)YEK01]T +MT,<7Y)@5O0':C!S_0^WG/9:,_0!\NG>*V2&:&Y6HU["3TUJ[%2F5'QVDJ49T +MXGT,!*J&0,&"B11L_MOP3I7+N6GR9P(1N,#/>TI'$'P9K@^7G77Y&\%&7G#U +M.(1KPE@G:PEDVEKT='HZIP\2CZXN#7@$BKO\_/`A&S$A2;^3%0VJ0B<>6Z7* +M'(W4;J5SB-RW4&#Z`/N2.@8_6$ +M@.Y1A%M"F_0Y9)K*3PGD$TRK,@"9C'8J9SEZHM`Y$LI/?PG%_42+$3TP@+I` +M+:FR6X1[JU^A\'RX,7__;W>6RC#^PQ8?M\"W+\9N+5D6E<,I6YVTKS#\$JVA +MZ;K0DY^(!*H9_',HH[YJO:)!+$6'J^.N'A`#`W;24DHV[N/$M_I"<*S-?4"H#MX!C`%KOF),."`K?#U%$R3O$^-3L$B +M4!;(W2EW.X`!YA8N;NJ=9MQ+0L:V/,NE<$C'^^VK:'5:ISTE3397ZF]A-RNV +M_0['T/F3^RM,C)6LKU.-W*KX4K%_0E/L1N*_EGPJ.$1^?N[5CQ%1QVWW7=PQ +MRBO*[C$);8)1VXE3/D#0,<6UN<]Z2^" +MDK"/$_T4Y*0'L%^0ZU9V*83*/A7Q58HO'/N:&4-[4C;Z"VG4F*YB&&-DUJDN>P.WW_W/(C8EWN-?5&H` +ME(KXMK!7NU+"*$'9`APUBFPA)L:VI),V)&P9=E5XX@LEL0`6F[B]V<#9XN[& +MRCL*9L(.?C[(@XE%VY>?B<'!&6!:!_F-/_H10MNL_AK01%_7!^2_ +MK-;P"DF0A;4'A4:'>AL55U%9:8*0`4^?W8+9DT"V0RD8LG/39(VE@V_M&V+K +MAMS@CU$V!O(Z>CG.!!.(;UXV'W%\:.5'^#G(S+5'+V!>C07US+?DIUL/62Z: +M&B4VHH.FA1S9(^$W)M3*NTHC:"FCSF;3$8P4Z@>L4)!K1Y*\<:F0-]$9L=HB +MY)E#TDD5?I!S4M5OOCCC'R/!D.`9%'K&$F[4E5VH"YP$-,%9!F'3#+:9192$ +M+UB^3S-M%M@;228EH`````````&9"<41``!4)ENHJ``*_`(``````%E:4$L# +M!!0```!?`$DZ/4N(?PNR0`4```8*```+````<&%C;6%N+F-O;F;]-WI86@`` +M`/\2V4$"`"$!`````#[C.DH'JHS8W0 +M#?$V8!+]F!+4KI<$@5HR#OF&_!+?L]N#167^N/Z\RR?GT6KQ]?CNM8>==$!O +M=?@QVU.*S@O@?_!ZYES=OXHQYJ$K_DR%MMLC^2[U):T3="8KH6E\^#TM[(#:A4]+,_]N-'V@&OOT`,J)*`1$6H.A\AQ<:=18H%@ +ML;39WZ/[&[?/?SG0:`)Z@@QK2DRGY04F&/;-K*Z2%^"N_BP_F6O6MO(I/U.T +M,L^0$-SQ!\_E:AA7X(VUV.Y`_]<]=`A6(D?^?S$N7OB; +MT(0U)^NL7%.Y4Y=[UNW8JWFUU2Z%N5G/Z:2V?%%%6/';]$%?%<3`>;=\3"TZ +MOQH>DR`T^1`8>\&>-+PCS8;)$M]WJ2O3&-M5NR) +M$WG>P*D3/:ZM,!QAAXTJ^?^DMH4:%0+):![`U`''+%K.;9,DM-OWB^)49!83 +MO-XVJ-N?)]_]87HXQ6(V0%4!R+DSR&(F=2*,KFTT2V$[`UZVVMLSI3T^B.%" +M3D->'YYCN2Z9EC%"/S^=W%139%7:"`%-3":\73B.TK]C)8:O)4=W6=.WU7J2 +M@Y#"B90[N&9_-Z@-5-#!_EI#N]D^NX!(OD[^I4G/L<]:Y&KD/P[5G?Q)6SR. +MDWT*.9G^KK"3^&1]@*+P6$#\Z4SCR,)E-_G'AVC:U4C%35O[,9B\$>QS9A// +M4-6)6TL?R;D":$KYVE+4" +M&J><3=ZH:XM&"9ZG5-4HEZVM9YO'8X@P#G1HSFI) +M2W.U-!_N1K4*.N#AAUV?:`+5)2Q\ST04<\TLD-92B`V7$2.54`>G]G_GE!`] +M_IBNF"]3MS#G]1S*N!MZE$UQX#1ICZMB[T'>_#A&8:3CP,B^UU\D$[3QNZ\P +M=]=4'BLWD`?H@Z@Z1;^A03I/VJ2$9]*>-&-,(=@TI22.6-'S#KS*+AT&AY64ZN7$Q'B,&TN<$OCO2W@';2/5 +MEKGSV4@OF10@NP4EBO7\Z_Z/ET#+&R::*OH_-<8/2\VF!^Y["EA#=>3&>K=? +M3^N#$@0V@=Y#K\T!ZU-B?E02#N6U>="G!A6&D?[?!.55MDR7@#`#^[T2I%,A +M+@+0O`/8^4,FN!XRSE;S@C93NICEP.5CEB_787X1B`&3L2FW@E,@?%HW,)36 +M&;CMA--B'7O,LRD\RJ3&]S+/5/3#/?F.Y/5_]51?5DUT3'R5UY.+M +M3HA.NI":EB`FI,"L@%9/?/J.E'FE+AIF8,A&N_G:M,4%DNJM5&/OK\3N="=5 +MYWM9VORAD0```9P*AA0```_X7#"H``K\`@``````65I02P,$%````%\`2J/! +M2E3P*2/$"0``R1D```<```!P`!&(!PA/3V#P('SF^\W3.1N]J/".J]^]]U@ZBN`&LJSNNZ7/6,;7A:'$J$`EH:>8EW9Y\*`?V"\6A*4&2@O" +M1A:-<$A^/D?VS/-%701_H0U7M2@R4ONGK#RI51>7<^LN1@,?_"$_P3SQ*?)3 +MLY.N+"=X\Y5RD'`^MS]RM[7_DX]C#">9:T$?=-#'%^28%;T!VHP<_T/MYSV6 +MC/T`?+IWBMDAFAN5J->PD]-:NQ4IE1\=I*E&=.)]#`2JAD#!@HD4;/[;\$Z5 +MR[EI\F<"$;C`SWM*1Q!\&:X/EYUU^1O!1EYP]3B$:\)8)VL)9-I:]'1Z.J-8JFWEH.CT0J&T-]!<(2-ZX3@)RN\J +MD0J4H/6N:=KFT$;\&+`]@#Q%F@SF+-%(65*CB7!$HAZK&W*TS\4$TS&_-!XUFUI$7*A-;',>)+^5/P,ZD`[HWR.A9G^#C +M-6KTNK(S\@+HL$6:2FA5[-R2LQ[3WK4@G[G^^#%XET%6G82[QS;7(S,^%>P. +MYKA-5S)0G[)N+EKS^1K@1W;SU#4':SY-Q*(HA+6WR\"M,7!$$QQVKNVE\P[0 +MBBWM8'\!):GY@^BZZ/>2OU-EQ9/907=8"CMA,Y1-O?0VM$[ +M,QLUL^VK@(FD@-<=6)L`J(Q]$R2&TIS@=/S2CA0:)DA&H\H`$K>#+BZ#L5!& +M]8O1Y2#,@L6`*Z_D^1@3[G%I48$DK(:8@ZH;WXDD0/L?(I^Z'Y4Y+\D8XG`( +MSAG!!38.H:+L%5M[]Y^`-CTFJ#CX<7/IC$#M`L)E=Z68`.Z15.!\)150B=YZ +M<&XZF>5(BYL_(:&P;,V^D';WU2KR?A0+4-E:0%O1F9`\";RR09,+G:>77.C7 +M]KV`K:4D9*B)Q7-\>:1&]N#JUDJ(<;NGJG2;_\!+&29%3S'E+/YN\/.CP_`+ +M2C&6BOI"R]9V%EB^`F/J&*.2?`KS5D\7+CM*1],_W<7&0.MI\<*%EQY7Q@SQ +M]J35[E'P\:C"YF9--<<]IB4/#I9@9S3^CAVB3]UU"M=C#6^!]'`R!N<"BA3D +MN!$ISF;-DTGCY>18H,[#4UN),"U;=7J/F]7_C]K`/LRZ> +ML--U<[]+X!IV+&GCQ]@_E!=D2Y1SD_+@R,2:B,26E+GCGX,S56]3OA<@OBVL +MP`N1,^ZJXI1U"XKCE:I(CGLU!K\^_VU7+TJ2/;81PC?GHW(SPC^[!L2+DA`2 +MY\B!@>P$N?Z%:.G%!3DJDM;N*"DN*/0 +M.&`?D?G/MZ-/H^LA*3<3>4'L">#EY&"!H9TB4M/1;]:<*%/-*%*ZZ2,FF"T` +M1-VLYD+N +M@QH1*4'INB@E6X<9,1=\T'`K<_PIF!F*CV&59B&:2$-.E\%OM2!\T"\X#?W) +M_\E.2DS4TN&FVR:GZQ#7+A4-!3L*KI`(NX#-0JA_OH3'J@OJ/G'C\>/BB@8\ +MO9AR=;IRPX06AZX1'^R/RE>AE,'6#4](`V?FM0=BN]-3P7I*Y1N1VF>PL"IJ +MGF]0@PM>JH[PYGC*F^0"*N9OI-E(9C^LSD&NHWHEQ=_$X4]3I1L][E,)5C%OKU.]ZXIDSD]%)$8-'2>$@08,C&F")$0X!^:[H:2 +M77[$CGQ]J0)I&=%/9U>ZZ!2Y93+,3^<38JQEC-]%YBW9?`S,U]*_N)5*MF1; +M6\2$>DXDY!G'+D3!)U47?ELRTEI9>)&;2]..PL(AM-=O/69L[N:Y:FYU$4OV +M^,N@H"C'ZH#0&TGK?'HWAY=!0Y!^[6!S_P:>A)^@YZ;N82NGABF]X@0;_,-? +M>;?A^0=`'34QXH8VW08:$;&O]MLZDV-KC!/$@J-K$9[N0&GOS+2GI;?LN)L!M0MJD)X0D:^^ +M)C-S@Y6IMQ)#AE,[..:E&_PK.Y=^1&=;H[0VN#H>-LQAW3+GYKV6I=X6B3=8 +M.@Q5EULK;>80C>Q2*R&+D7DQ0E3,-2X+C]$9NN9R62RYTV[PC,4H>F[\5?A# +ML..XN!<+40H+7WGS&4<3H)3#?4%;XFXM&K'OQ*Z'L*"N<J@/Y%[H1GLJ4SH:YM^-V'+5CKLSP:+]J--=`-B,9Z+53^H2Q]->KE@3ZYY +M\OZ`-KB^'V-<@\MC.J++G-TJ>RS-M>)-3OU&NT5CLUM2@\\A+I;`GDR'T11U +MAW1H\ZECZZD*=!@DMO3,60R-`\W7_X6C,8FB3C-2V +M(@S*@2LK1T%!';G7&%\&D>=1/;=64G$92-FPMB(<+37:DLED52:_VFEY47*O +M3ZKVJ;/.\O<_2Z?IB'U9W2:6S6_,X$F=(#VZO.@V-4CY<68\B@A`UKG0$AR1 +MC3-VV!4W/,85QD39F&!67\SGM$KS:=TRN]$V"EC=+O7\PN8.A__Z)$@0>8,] +M!NHPA*?U=-A#U>OEK!#NG,RB2Y@@`5JNHJR0;9A'3FP'V/.UYMG+D*L*">.% +MKY&BW>NHE92\QT;V$D8_X^)[*WA-4GBAS.]:Q0"SGL>J*KNDX)76,LF["/13 +M[SM72F%\\//WRT1^2GJA553G>V*&$Z9ZK.*:"0K;06@IC_N`Z$&Z4?!2=`CN071_Z=#```````!G1/),P``5\:%:*@`"OP"```` +M``!96E!+`0(4`!0```!?`$JCP4K)N%'WP`0``,4(```+`"0```````$`(``` +M``````!B87-H+F)A #endif #ifdef HAVE_SYS_TIME_H #include #endif #include #ifdef HAVE_ICONV_H #include #endif /* * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h. * As the include guards don't agree, the order of include is important. */ #ifdef HAVE_LINUX_EXT2_FS_H #include /* for Linux file flags */ #endif #if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__) #include /* Linux file flags, broken on Cygwin */ #endif #ifdef HAVE_LINUX_FS_H #include #endif #include #include #ifdef HAVE_SIGNAL_H #include #endif #include #include #ifdef HAVE_SIGNAL_H #endif #ifdef HAVE_ACL_LIBACL_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_ACL_H #include #endif #ifdef HAVE_SYS_EA_H #include #endif #ifdef HAVE_SYS_EXTATTR_H #include #endif #if HAVE_SYS_XATTR_H #include #elif HAVE_ATTR_XATTR_H #include #endif #ifdef HAVE_SYS_RICHACL_H #include #endif #if HAVE_MEMBERSHIP_H #include #endif /* * * Windows support routines * * Note: Configuration is a tricky issue. Using HAVE_* feature macros * in the test harness is dangerous because they cover up * configuration errors. The classic example of this is omitting a * configure check. If libarchive and libarchive_test both look for * the same feature macro, such errors are hard to detect. Platform * macros (e.g., _WIN32 or __GNUC__) are a little better, but can * easily lead to very messy code. It's best to limit yourself * to only the most generic programming techniques in the test harness * and thus avoid conditionals altogether. Where that's not possible, * try to minimize conditionals by grouping platform-specific tests in * one place (e.g., test_acl_freebsd) or by adding new assert() * functions (e.g., assertMakeHardlink()) to cover up platform * differences. Platform-specific coding in libarchive_test is often * a symptom that some capability is missing from libarchive itself. */ #if defined(_WIN32) && !defined(__CYGWIN__) #include #include #include #ifndef F_OK #define F_OK (0) #endif #ifndef S_ISDIR #define S_ISDIR(m) ((m) & _S_IFDIR) #endif #ifndef S_ISREG #define S_ISREG(m) ((m) & _S_IFREG) #endif #if !defined(__BORLANDC__) #define access _access #undef chdir #define chdir _chdir #endif #ifndef fileno #define fileno _fileno #endif /*#define fstat _fstat64*/ #if !defined(__BORLANDC__) #define getcwd _getcwd #endif #define lstat stat /*#define lstat _stat64*/ /*#define stat _stat64*/ #define rmdir _rmdir #if !defined(__BORLANDC__) #define strdup _strdup #define umask _umask #endif #define int64_t __int64 #endif #if defined(HAVE__CrtSetReportMode) # include #endif mode_t umasked(mode_t expected_mode) { mode_t mode = umask(0); umask(mode); return expected_mode & ~mode; } /* Path to working directory for current test */ const char *testworkdir; #ifdef PROGRAM /* Pathname of exe to be tested. */ const char *testprogfile; /* Name of exe to use in printf-formatted command strings. */ /* On Windows, this includes leading/trailing quotes. */ const char *testprog; #endif #if defined(_WIN32) && !defined(__CYGWIN__) static void *GetFunctionKernel32(const char *); static int my_CreateSymbolicLinkA(const char *, const char *, int); static int my_CreateHardLinkA(const char *, const char *); static int my_GetFileInformationByName(const char *, BY_HANDLE_FILE_INFORMATION *); static void * GetFunctionKernel32(const char *name) { static HINSTANCE lib; static int set; if (!set) { set = 1; lib = LoadLibrary("kernel32.dll"); } if (lib == NULL) { fprintf(stderr, "Can't load kernel32.dll?!\n"); exit(1); } return (void *)GetProcAddress(lib, name); } static int my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); static int set; if (!set) { set = 1; f = GetFunctionKernel32("CreateSymbolicLinkA"); } return f == NULL ? 0 : (*f)(linkname, target, flags); } static int my_CreateHardLinkA(const char *linkname, const char *target) { static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES); static int set; if (!set) { set = 1; f = GetFunctionKernel32("CreateHardLinkA"); } return f == NULL ? 0 : (*f)(linkname, target, NULL); } static int my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) { HANDLE h; int r; memset(bhfi, 0, sizeof(*bhfi)); h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) return (0); r = GetFileInformationByHandle(h, bhfi); CloseHandle(h); return (r); } #endif #if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) static void invalid_parameter_handler(const wchar_t * expression, const wchar_t * function, const wchar_t * file, unsigned int line, uintptr_t pReserved) { /* nop */ // Silence unused-parameter compiler warnings. (void)expression; (void)function; (void)file; (void)line; (void)pReserved; } #endif /* * * OPTIONS FLAGS * */ /* Enable core dump on failure. */ static int dump_on_failure = 0; /* Default is to remove temp dirs and log data for successful tests. */ static int keep_temp_files = 0; /* Default is to run the specified tests once and report errors. */ static int until_failure = 0; /* Default is to just report pass/fail for each test. */ static int verbosity = 0; #define VERBOSITY_SUMMARY_ONLY -1 /* -q */ #define VERBOSITY_PASSFAIL 0 /* Default */ #define VERBOSITY_LIGHT_REPORT 1 /* -v */ #define VERBOSITY_FULL 2 /* -vv */ /* A few places generate even more output for verbosity > VERBOSITY_FULL, * mostly for debugging the test harness itself. */ /* Cumulative count of assertion failures. */ static int failures = 0; /* Cumulative count of reported skips. */ static int skips = 0; /* Cumulative count of assertions checked. */ static int assertions = 0; /* Directory where uuencoded reference files can be found. */ static const char *refdir; /* * Report log information selectively to console and/or disk log. */ static int log_console = 0; static FILE *logfile; static void vlogprintf(const char *fmt, va_list ap) { #ifdef va_copy va_list lfap; va_copy(lfap, ap); #endif if (log_console) vfprintf(stdout, fmt, ap); if (logfile != NULL) #ifdef va_copy vfprintf(logfile, fmt, lfap); va_end(lfap); #else vfprintf(logfile, fmt, ap); #endif } static void logprintf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vlogprintf(fmt, ap); va_end(ap); } /* Set up a message to display only if next assertion fails. */ static char msgbuff[4096]; static const char *msg, *nextmsg; void failure(const char *fmt, ...) { va_list ap; if (fmt == NULL) { nextmsg = NULL; } else { va_start(ap, fmt); vsprintf(msgbuff, fmt, ap); va_end(ap); nextmsg = msgbuff; } } /* * Copy arguments into file-local variables. * This was added to permit vararg assert() functions without needing * variadic wrapper macros. Turns out that the vararg capability is almost * never used, so almost all of the vararg assertions can be simplified * by removing the vararg capability and reworking the wrapper macro to * pass __FILE__, __LINE__ directly into the function instead of using * this hook. I suspect this machinery is used so rarely that we * would be better off just removing it entirely. That would simplify * the code here noticeably. */ static const char *skipping_filename; static int skipping_line; void skipping_setup(const char *filename, int line) { skipping_filename = filename; skipping_line = line; } /* Called at the beginning of each assert() function. */ static void assertion_count(const char *file, int line) { (void)file; /* UNUSED */ (void)line; /* UNUSED */ ++assertions; /* Proper handling of "failure()" message. */ msg = nextmsg; nextmsg = NULL; /* Uncomment to print file:line after every assertion. * Verbose, but occasionally useful in tracking down crashes. */ /* printf("Checked %s:%d\n", file, line); */ } /* * For each test source file, we remember how many times each * assertion was reported. Cleared before each new test, * used by test_summarize(). */ static struct line { int count; int skip; } failed_lines[10000]; const char *failed_filename; /* Count this failure, setup up log destination and handle initial report. */ static void failure_start(const char *filename, int line, const char *fmt, ...) { va_list ap; /* Record another failure for this line. */ ++failures; failed_filename = filename; failed_lines[line].count++; /* Determine whether to log header to console. */ switch (verbosity) { case VERBOSITY_LIGHT_REPORT: log_console = (failed_lines[line].count < 2); break; default: log_console = (verbosity >= VERBOSITY_FULL); } /* Log file:line header for this failure */ va_start(ap, fmt); #if _MSC_VER logprintf("%s(%d): ", filename, line); #else logprintf("%s:%d: ", filename, line); #endif vlogprintf(fmt, ap); va_end(ap); logprintf("\n"); if (msg != NULL && msg[0] != '\0') { logprintf(" Description: %s\n", msg); msg = NULL; } /* Determine whether to log details to console. */ if (verbosity == VERBOSITY_LIGHT_REPORT) log_console = 0; } /* Complete reporting of failed tests. */ /* * The 'extra' hook here is used by libarchive to include libarchive * error messages with assertion failures. It could also be used * to add strerror() output, for example. Just define the EXTRA_DUMP() * macro appropriately. */ static void failure_finish(void *extra) { (void)extra; /* UNUSED (maybe) */ #ifdef EXTRA_DUMP if (extra != NULL) { logprintf(" errno: %d\n", EXTRA_ERRNO(extra)); logprintf(" detail: %s\n", EXTRA_DUMP(extra)); } #endif if (dump_on_failure) { fprintf(stderr, " *** forcing core dump so failure can be debugged ***\n"); abort(); } } /* Inform user that we're skipping some checks. */ void test_skipping(const char *fmt, ...) { char buff[1024]; va_list ap; va_start(ap, fmt); vsprintf(buff, fmt, ap); va_end(ap); /* Use failure() message if set. */ msg = nextmsg; nextmsg = NULL; /* failure_start() isn't quite right, but is awfully convenient. */ failure_start(skipping_filename, skipping_line, "SKIPPING: %s", buff); --failures; /* Undo failures++ in failure_start() */ /* Don't failure_finish() here. */ /* Mark as skip, so doesn't count as failed test. */ failed_lines[skipping_line].skip = 1; ++skips; } /* * * ASSERTIONS * */ /* Generic assert() just displays the failed condition. */ int assertion_assert(const char *file, int line, int value, const char *condition, void *extra) { assertion_count(file, line); if (!value) { failure_start(file, line, "Assertion failed: %s", condition); failure_finish(extra); } return (value); } /* chdir() and report any errors */ int assertion_chdir(const char *file, int line, const char *pathname) { assertion_count(file, line); if (chdir(pathname) == 0) return (1); failure_start(file, line, "chdir(\"%s\")", pathname); failure_finish(NULL); return (0); } /* Verify two integers are equal. */ int assertion_equal_int(const char *file, int line, long long v1, const char *e1, long long v2, const char *e2, void *extra) { assertion_count(file, line); if (v1 == v2) return (1); failure_start(file, line, "%s != %s", e1, e2); logprintf(" %s=%lld (0x%llx, 0%llo)\n", e1, v1, v1, v1); logprintf(" %s=%lld (0x%llx, 0%llo)\n", e2, v2, v2, v2); failure_finish(extra); return (0); } /* * Utility to convert a single UTF-8 sequence. */ static int _utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) { static const char utf8_count[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ }; int ch; int cnt; uint32_t wc; *pwc = 0; /* Sanity check. */ if (n == 0) return (0); /* * Decode 1-4 bytes depending on the value of the first byte. */ ch = (unsigned char)*s; if (ch == 0) return (0); /* Standard: return 0 for end-of-string. */ cnt = utf8_count[ch]; /* Invalid sequence or there are not plenty bytes. */ if (n < (size_t)cnt) return (-1); /* Make a Unicode code point from a single UTF-8 sequence. */ switch (cnt) { case 1: /* 1 byte sequence. */ *pwc = ch & 0x7f; return (cnt); case 2: /* 2 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) return (-1); *pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f); return (cnt); case 3: /* 3 bytes sequence. */ if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); wc = ((ch & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f); if (wc < 0x800) return (-1);/* Overlong sequence. */ break; case 4: /* 4 bytes sequence. */ if (n < 4) return (-1); if ((s[1] & 0xc0) != 0x80) return (-1); if ((s[2] & 0xc0) != 0x80) return (-1); if ((s[3] & 0xc0) != 0x80) return (-1); wc = ((ch & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); if (wc < 0x10000) return (-1);/* Overlong sequence. */ break; default: return (-1); } /* The code point larger than 0x10FFFF is not legal * Unicode values. */ if (wc > 0x10FFFF) return (-1); /* Correctly gets a Unicode, returns used bytes. */ *pwc = wc; return (cnt); } static void strdump(const char *e, const char *p, int ewidth, int utf8) { const char *q = p; logprintf(" %*s = ", ewidth, e); if (p == NULL) { logprintf("NULL\n"); return; } logprintf("\""); while (*p != '\0') { unsigned int c = 0xff & *p++; switch (c) { case '\a': logprintf("\\a"); break; case '\b': logprintf("\\b"); break; case '\n': logprintf("\\n"); break; case '\r': logprintf("\\r"); break; default: if (c >= 32 && c < 127) logprintf("%c", c); else logprintf("\\x%02X", c); } } logprintf("\""); logprintf(" (length %d)", q == NULL ? -1 : (int)strlen(q)); /* * If the current string is UTF-8, dump its code points. */ if (utf8) { size_t len; uint32_t uc; int n; int cnt = 0; p = q; len = strlen(p); logprintf(" ["); while ((n = _utf8_to_unicode(&uc, p, len)) > 0) { if (p != q) logprintf(" "); logprintf("%04X", uc); p += n; len -= n; cnt++; } logprintf("]"); logprintf(" (count %d", cnt); if (n < 0) { logprintf(",unknown %d bytes", len); } logprintf(")"); } logprintf("\n"); } /* Verify two strings are equal, dump them if not. */ int assertion_equal_string(const char *file, int line, const char *v1, const char *e1, const char *v2, const char *e2, void *extra, int utf8) { int l1, l2; assertion_count(file, line); if (v1 == v2 || (v1 != NULL && v2 != NULL && strcmp(v1, v2) == 0)) return (1); failure_start(file, line, "%s != %s", e1, e2); l1 = (int)strlen(e1); l2 = (int)strlen(e2); if (l1 < l2) l1 = l2; strdump(e1, v1, l1, utf8); strdump(e2, v2, l1, utf8); failure_finish(extra); return (0); } static void wcsdump(const char *e, const wchar_t *w) { logprintf(" %s = ", e); if (w == NULL) { logprintf("(null)"); return; } logprintf("\""); while (*w != L'\0') { unsigned int c = *w++; if (c >= 32 && c < 127) logprintf("%c", c); else if (c < 256) logprintf("\\x%02X", c); else if (c < 0x10000) logprintf("\\u%04X", c); else logprintf("\\U%08X", c); } logprintf("\"\n"); } #ifndef HAVE_WCSCMP static int wcscmp(const wchar_t *s1, const wchar_t *s2) { while (*s1 == *s2++) { if (*s1++ == L'\0') return 0; } if (*s1 > *--s2) return 1; else return -1; } #endif /* Verify that two wide strings are equal, dump them if not. */ int assertion_equal_wstring(const char *file, int line, const wchar_t *v1, const char *e1, const wchar_t *v2, const char *e2, void *extra) { assertion_count(file, line); if (v1 == v2) return (1); if (v1 != NULL && v2 != NULL && wcscmp(v1, v2) == 0) return (1); failure_start(file, line, "%s != %s", e1, e2); wcsdump(e1, v1); wcsdump(e2, v2); failure_finish(extra); return (0); } /* * Pretty standard hexdump routine. As a bonus, if ref != NULL, then * any bytes in p that differ from ref will be highlighted with '_' * before and after the hex value. */ static void hexdump(const char *p, const char *ref, size_t l, size_t offset) { size_t i, j; char sep; if (p == NULL) { logprintf("(null)\n"); return; } for(i=0; i < l; i+=16) { logprintf("%04x", (unsigned)(i + offset)); sep = ' '; for (j = 0; j < 16 && i + j < l; j++) { if (ref != NULL && p[i + j] != ref[i + j]) sep = '_'; logprintf("%c%02x", sep, 0xff & (int)p[i+j]); if (ref != NULL && p[i + j] == ref[i + j]) sep = ' '; } for (; j < 16; j++) { logprintf("%c ", sep); sep = ' '; } logprintf("%c", sep); for (j=0; j < 16 && i + j < l; j++) { int c = p[i + j]; if (c >= ' ' && c <= 126) logprintf("%c", c); else logprintf("."); } logprintf("\n"); } } /* Verify that two blocks of memory are the same, display the first * block of differences if they're not. */ int assertion_equal_mem(const char *file, int line, const void *_v1, const char *e1, const void *_v2, const char *e2, size_t l, const char *ld, void *extra) { const char *v1 = (const char *)_v1; const char *v2 = (const char *)_v2; size_t offset; assertion_count(file, line); if (v1 == v2 || (v1 != NULL && v2 != NULL && memcmp(v1, v2, l) == 0)) return (1); if (v1 == NULL || v2 == NULL) return (0); failure_start(file, line, "%s != %s", e1, e2); logprintf(" size %s = %d\n", ld, (int)l); /* Dump 48 bytes (3 lines) so that the first difference is * in the second line. */ offset = 0; while (l > 64 && memcmp(v1, v2, 32) == 0) { /* Two lines agree, so step forward one line. */ v1 += 16; v2 += 16; l -= 16; offset += 16; } logprintf(" Dump of %s\n", e1); hexdump(v1, v2, l < 128 ? l : 128, offset); logprintf(" Dump of %s\n", e2); hexdump(v2, v1, l < 128 ? l : 128, offset); logprintf("\n"); failure_finish(extra); return (0); } /* Verify that a block of memory is filled with the specified byte. */ int assertion_memory_filled_with(const char *file, int line, const void *_v1, const char *vd, size_t l, const char *ld, char b, const char *bd, void *extra) { const char *v1 = (const char *)_v1; size_t c = 0; size_t i; (void)ld; /* UNUSED */ assertion_count(file, line); for (i = 0; i < l; ++i) { if (v1[i] == b) { ++c; } } if (c == l) return (1); failure_start(file, line, "%s (size %d) not filled with %s", vd, (int)l, bd); logprintf(" Only %d bytes were correct\n", (int)c); failure_finish(extra); return (0); } /* Verify that the named file exists and is empty. */ int assertion_empty_file(const char *filename, int line, const char *f1) { char buff[1024]; struct stat st; ssize_t s; FILE *f; assertion_count(filename, line); if (stat(f1, &st) != 0) { failure_start(filename, line, "Stat failed: %s", f1); failure_finish(NULL); return (0); } if (st.st_size == 0) return (1); failure_start(filename, line, "File should be empty: %s", f1); logprintf(" File size: %d\n", (int)st.st_size); logprintf(" Contents:\n"); f = fopen(f1, "rb"); if (f == NULL) { logprintf(" Unable to open %s\n", f1); } else { s = ((off_t)sizeof(buff) < st.st_size) ? (ssize_t)sizeof(buff) : (ssize_t)st.st_size; s = fread(buff, 1, s, f); hexdump(buff, NULL, s, 0); fclose(f); } failure_finish(NULL); return (0); } /* Verify that the named file exists and is not empty. */ int assertion_non_empty_file(const char *filename, int line, const char *f1) { struct stat st; assertion_count(filename, line); if (stat(f1, &st) != 0) { failure_start(filename, line, "Stat failed: %s", f1); failure_finish(NULL); return (0); } if (st.st_size == 0) { failure_start(filename, line, "File empty: %s", f1); failure_finish(NULL); return (0); } return (1); } /* Verify that two files have the same contents. */ /* TODO: hexdump the first bytes that actually differ. */ int assertion_equal_file(const char *filename, int line, const char *fn1, const char *fn2) { char buff1[1024]; char buff2[1024]; FILE *f1, *f2; int n1, n2; assertion_count(filename, line); f1 = fopen(fn1, "rb"); f2 = fopen(fn2, "rb"); if (f1 == NULL || f2 == NULL) { if (f1) fclose(f1); if (f2) fclose(f2); return (0); } for (;;) { n1 = (int)fread(buff1, 1, sizeof(buff1), f1); n2 = (int)fread(buff2, 1, sizeof(buff2), f2); if (n1 != n2) break; if (n1 == 0 && n2 == 0) { fclose(f1); fclose(f2); return (1); } if (memcmp(buff1, buff2, n1) != 0) break; } fclose(f1); fclose(f2); failure_start(filename, line, "Files not identical"); logprintf(" file1=\"%s\"\n", fn1); logprintf(" file2=\"%s\"\n", fn2); failure_finish(NULL); return (0); } /* Verify that the named file does exist. */ int assertion_file_exists(const char *filename, int line, const char *f) { assertion_count(filename, line); #if defined(_WIN32) && !defined(__CYGWIN__) if (!_access(f, 0)) return (1); #else if (!access(f, F_OK)) return (1); #endif failure_start(filename, line, "File should exist: %s", f); failure_finish(NULL); return (0); } /* Verify that the named file doesn't exist. */ int assertion_file_not_exists(const char *filename, int line, const char *f) { assertion_count(filename, line); #if defined(_WIN32) && !defined(__CYGWIN__) if (_access(f, 0)) return (1); #else if (access(f, F_OK)) return (1); #endif failure_start(filename, line, "File should not exist: %s", f); failure_finish(NULL); return (0); } /* Compare the contents of a file to a block of memory. */ int assertion_file_contents(const char *filename, int line, const void *buff, int s, const char *fn) { char *contents; FILE *f; int n; assertion_count(filename, line); f = fopen(fn, "rb"); if (f == NULL) { failure_start(filename, line, "File should exist: %s", fn); failure_finish(NULL); return (0); } contents = malloc(s * 2); n = (int)fread(contents, 1, s * 2, f); fclose(f); if (n == s && memcmp(buff, contents, s) == 0) { free(contents); return (1); } failure_start(filename, line, "File contents don't match"); logprintf(" file=\"%s\"\n", fn); if (n > 0) hexdump(contents, buff, n > 512 ? 512 : n, 0); else { logprintf(" File empty, contents should be:\n"); hexdump(buff, NULL, s > 512 ? 512 : s, 0); } failure_finish(NULL); free(contents); return (0); } /* Check the contents of a text file, being tolerant of line endings. */ int assertion_text_file_contents(const char *filename, int line, const char *buff, const char *fn) { char *contents; const char *btxt, *ftxt; FILE *f; int n, s; assertion_count(filename, line); f = fopen(fn, "r"); if (f == NULL) { failure_start(filename, line, "File doesn't exist: %s", fn); failure_finish(NULL); return (0); } s = (int)strlen(buff); contents = malloc(s * 2 + 128); n = (int)fread(contents, 1, s * 2 + 128 - 1, f); if (n >= 0) contents[n] = '\0'; fclose(f); /* Compare texts. */ btxt = buff; ftxt = (const char *)contents; while (*btxt != '\0' && *ftxt != '\0') { if (*btxt == *ftxt) { ++btxt; ++ftxt; continue; } if (btxt[0] == '\n' && ftxt[0] == '\r' && ftxt[1] == '\n') { /* Pass over different new line characters. */ ++btxt; ftxt += 2; continue; } break; } if (*btxt == '\0' && *ftxt == '\0') { free(contents); return (1); } failure_start(filename, line, "Contents don't match"); logprintf(" file=\"%s\"\n", fn); if (n > 0) { hexdump(contents, buff, n, 0); logprintf(" expected\n", fn); hexdump(buff, contents, s, 0); } else { logprintf(" File empty, contents should be:\n"); hexdump(buff, NULL, s, 0); } failure_finish(NULL); free(contents); return (0); } /* Verify that a text file contains the specified lines, regardless of order */ /* This could be more efficient if we sorted both sets of lines, etc, but * since this is used only for testing and only ever deals with a dozen or so * lines at a time, this relatively crude approach is just fine. */ int assertion_file_contains_lines_any_order(const char *file, int line, const char *pathname, const char *lines[]) { char *buff; size_t buff_size; size_t expected_count, actual_count, i, j; char **expected = NULL; char *p, **actual = NULL; char c; int expected_failure = 0, actual_failure = 0; assertion_count(file, line); buff = slurpfile(&buff_size, "%s", pathname); if (buff == NULL) { failure_start(pathname, line, "Can't read file: %s", pathname); failure_finish(NULL); return (0); } /* Make a copy of the provided lines and count up the expected * file size. */ for (i = 0; lines[i] != NULL; ++i) { } expected_count = i; if (expected_count) { expected = malloc(sizeof(char *) * expected_count); if (expected == NULL) { failure_start(pathname, line, "Can't allocate memory"); failure_finish(NULL); free(expected); free(buff); return (0); } for (i = 0; lines[i] != NULL; ++i) { expected[i] = strdup(lines[i]); } } /* Break the file into lines */ actual_count = 0; for (c = '\0', p = buff; p < buff + buff_size; ++p) { if (*p == '\x0d' || *p == '\x0a') *p = '\0'; if (c == '\0' && *p != '\0') ++actual_count; c = *p; } if (actual_count) { actual = calloc(sizeof(char *), actual_count); if (actual == NULL) { failure_start(pathname, line, "Can't allocate memory"); failure_finish(NULL); free(expected); free(buff); return (0); } for (j = 0, p = buff; p < buff + buff_size; p += 1 + strlen(p)) { if (*p != '\0') { actual[j] = p; ++j; } } } /* Erase matching lines from both lists */ for (i = 0; i < expected_count; ++i) { if (expected[i] == NULL) continue; for (j = 0; j < actual_count; ++j) { if (actual[j] == NULL) continue; if (strcmp(expected[i], actual[j]) == 0) { free(expected[i]); expected[i] = NULL; actual[j] = NULL; break; } } } /* If there's anything left, it's a failure */ for (i = 0; i < expected_count; ++i) { if (expected[i] != NULL) ++expected_failure; } for (j = 0; j < actual_count; ++j) { if (actual[j] != NULL) ++actual_failure; } if (expected_failure == 0 && actual_failure == 0) { free(buff); free(expected); free(actual); return (1); } failure_start(file, line, "File doesn't match: %s", pathname); for (i = 0; i < expected_count; ++i) { if (expected[i] != NULL) { logprintf(" Expected but not present: %s\n", expected[i]); free(expected[i]); } } for (j = 0; j < actual_count; ++j) { if (actual[j] != NULL) logprintf(" Present but not expected: %s\n", actual[j]); } failure_finish(NULL); free(buff); free(expected); free(actual); return (0); } /* Verify that a text file does not contains the specified strings */ int assertion_file_contains_no_invalid_strings(const char *file, int line, const char *pathname, const char *strings[]) { char *buff; int i; buff = slurpfile(NULL, "%s", pathname); if (buff == NULL) { failure_start(file, line, "Can't read file: %s", pathname); failure_finish(NULL); return (0); } for (i = 0; strings[i] != NULL; ++i) { if (strstr(buff, strings[i]) != NULL) { failure_start(file, line, "Invalid string in %s: %s", pathname, strings[i]); failure_finish(NULL); free(buff); return(0); } } free(buff); return (0); } /* Test that two paths point to the same file. */ /* As a side-effect, asserts that both files exist. */ static int is_hardlink(const char *file, int line, const char *path1, const char *path2) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; int r; assertion_count(file, line); r = my_GetFileInformationByName(path1, &bhfi1); if (r == 0) { failure_start(file, line, "File %s can't be inspected?", path1); failure_finish(NULL); return (0); } r = my_GetFileInformationByName(path2, &bhfi2); if (r == 0) { failure_start(file, line, "File %s can't be inspected?", path2); failure_finish(NULL); return (0); } return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); #else struct stat st1, st2; int r; assertion_count(file, line); r = lstat(path1, &st1); if (r != 0) { failure_start(file, line, "File should exist: %s", path1); failure_finish(NULL); return (0); } r = lstat(path2, &st2); if (r != 0) { failure_start(file, line, "File should exist: %s", path2); failure_finish(NULL); return (0); } return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); #endif } int assertion_is_hardlink(const char *file, int line, const char *path1, const char *path2) { if (is_hardlink(file, line, path1, path2)) return (1); failure_start(file, line, "Files %s and %s are not hardlinked", path1, path2); failure_finish(NULL); return (0); } int assertion_is_not_hardlink(const char *file, int line, const char *path1, const char *path2) { if (!is_hardlink(file, line, path1, path2)) return (1); failure_start(file, line, "Files %s and %s should not be hardlinked", path1, path2); failure_finish(NULL); return (0); } /* Verify a/b/mtime of 'pathname'. */ /* If 'recent', verify that it's within last 10 seconds. */ static int assertion_file_time(const char *file, int line, const char *pathname, long t, long nsec, char type, int recent) { long long filet, filet_nsec; int r; #if defined(_WIN32) && !defined(__CYGWIN__) #define EPOC_TIME (116444736000000000ULL) FILETIME fxtime, fbirthtime, fatime, fmtime; ULARGE_INTEGER wintm; HANDLE h; fxtime.dwLowDateTime = 0; fxtime.dwHighDateTime = 0; assertion_count(file, line); /* Note: FILE_FLAG_BACKUP_SEMANTICS applies to open * a directory file. If not, CreateFile() will fail when * the pathname is a directory. */ h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { failure_start(file, line, "Can't access %s\n", pathname); failure_finish(NULL); return (0); } r = GetFileTime(h, &fbirthtime, &fatime, &fmtime); switch (type) { case 'a': fxtime = fatime; break; case 'b': fxtime = fbirthtime; break; case 'm': fxtime = fmtime; break; } CloseHandle(h); if (r == 0) { failure_start(file, line, "Can't GetFileTime %s\n", pathname); failure_finish(NULL); return (0); } wintm.LowPart = fxtime.dwLowDateTime; wintm.HighPart = fxtime.dwHighDateTime; filet = (wintm.QuadPart - EPOC_TIME) / 10000000; filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100; nsec = (nsec / 100) * 100; /* Round the request */ #else struct stat st; assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Can't stat %s\n", pathname); failure_finish(NULL); return (0); } switch (type) { case 'a': filet = st.st_atime; break; case 'm': filet = st.st_mtime; break; case 'b': filet = 0; break; default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); exit(1); } #if defined(__FreeBSD__) switch (type) { case 'a': filet_nsec = st.st_atimespec.tv_nsec; break; case 'b': filet = st.st_birthtime; /* FreeBSD filesystems that don't support birthtime * (e.g., UFS1) always return -1 here. */ if (filet == -1) { return (1); } filet_nsec = st.st_birthtimespec.tv_nsec; break; case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break; default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type); exit(1); } /* FreeBSD generally only stores to microsecond res, so round. */ filet_nsec = (filet_nsec / 1000) * 1000; nsec = (nsec / 1000) * 1000; #else filet_nsec = nsec = 0; /* Generic POSIX only has whole seconds. */ if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */ #if defined(__HAIKU__) if (type == 'a') return (1); /* Haiku doesn't have atime. */ #endif #endif #endif if (recent) { /* Check that requested time is up-to-date. */ time_t now = time(NULL); if (filet < now - 10 || filet > now + 1) { failure_start(file, line, "File %s has %ctime %lld, %lld seconds ago\n", pathname, type, filet, now - filet); failure_finish(NULL); return (0); } } else if (filet != t || filet_nsec != nsec) { failure_start(file, line, "File %s has %ctime %lld.%09lld, expected %lld.%09lld", pathname, type, filet, filet_nsec, t, nsec); failure_finish(NULL); return (0); } return (1); } /* Verify atime of 'pathname'. */ int assertion_file_atime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'a', 0); } /* Verify atime of 'pathname' is up-to-date. */ int assertion_file_atime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'a', 1); } /* Verify birthtime of 'pathname'. */ int assertion_file_birthtime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'b', 0); } /* Verify birthtime of 'pathname' is up-to-date. */ int assertion_file_birthtime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'b', 1); } /* Verify mode of 'pathname'. */ int assertion_file_mode(const char *file, int line, const char *pathname, int expected_mode) { int mode; int r; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) failure_start(file, line, "assertFileMode not yet implemented for Windows"); (void)mode; /* UNUSED */ (void)r; /* UNUSED */ (void)pathname; /* UNUSED */ (void)expected_mode; /* UNUSED */ #else { struct stat st; r = lstat(pathname, &st); mode = (int)(st.st_mode & 0777); } if (r == 0 && mode == expected_mode) return (1); failure_start(file, line, "File %s has mode %o, expected %o", pathname, mode, expected_mode); #endif failure_finish(NULL); return (0); } /* Verify mtime of 'pathname'. */ int assertion_file_mtime(const char *file, int line, const char *pathname, long t, long nsec) { return assertion_file_time(file, line, pathname, t, nsec, 'm', 0); } /* Verify mtime of 'pathname' is up-to-date. */ int assertion_file_mtime_recent(const char *file, int line, const char *pathname) { return assertion_file_time(file, line, pathname, 0, 0, 'm', 1); } /* Verify number of links to 'pathname'. */ int assertion_file_nlinks(const char *file, int line, const char *pathname, int nlinks) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi; int r; assertion_count(file, line); r = my_GetFileInformationByName(pathname, &bhfi); if (r != 0 && bhfi.nNumberOfLinks == (DWORD)nlinks) return (1); failure_start(file, line, "File %s has %d links, expected %d", pathname, bhfi.nNumberOfLinks, nlinks); failure_finish(NULL); return (0); #else struct stat st; int r; assertion_count(file, line); r = lstat(pathname, &st); if (r == 0 && (int)st.st_nlink == nlinks) return (1); failure_start(file, line, "File %s has %d links, expected %d", pathname, st.st_nlink, nlinks); failure_finish(NULL); return (0); #endif } /* Verify size of 'pathname'. */ int assertion_file_size(const char *file, int line, const char *pathname, long size) { int64_t filesize; int r; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) { BY_HANDLE_FILE_INFORMATION bhfi; r = !my_GetFileInformationByName(pathname, &bhfi); filesize = ((int64_t)bhfi.nFileSizeHigh << 32) + bhfi.nFileSizeLow; } #else { struct stat st; r = lstat(pathname, &st); filesize = st.st_size; } #endif if (r == 0 && filesize == size) return (1); failure_start(file, line, "File %s has size %ld, expected %ld", pathname, (long)filesize, (long)size); failure_finish(NULL); return (0); } /* Assert that 'pathname' is a dir. If mode >= 0, verify that too. */ int assertion_is_dir(const char *file, int line, const char *pathname, int mode) { struct stat st; int r; #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ #endif assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Dir should exist: %s", pathname); failure_finish(NULL); return (0); } if (!S_ISDIR(st.st_mode)) { failure_start(file, line, "%s is not a dir", pathname); failure_finish(NULL); return (0); } #if !defined(_WIN32) || defined(__CYGWIN__) /* Windows doesn't handle permissions the same way as POSIX, * so just ignore the mode tests. */ /* TODO: Can we do better here? */ if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { failure_start(file, line, "Dir %s has wrong mode", pathname); logprintf(" Expected: 0%3o\n", mode); logprintf(" Found: 0%3o\n", st.st_mode & 07777); failure_finish(NULL); return (0); } #endif return (1); } /* Verify that 'pathname' is a regular file. If 'mode' is >= 0, * verify that too. */ int assertion_is_reg(const char *file, int line, const char *pathname, int mode) { struct stat st; int r; #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ #endif assertion_count(file, line); r = lstat(pathname, &st); if (r != 0 || !S_ISREG(st.st_mode)) { failure_start(file, line, "File should exist: %s", pathname); failure_finish(NULL); return (0); } #if !defined(_WIN32) || defined(__CYGWIN__) /* Windows doesn't handle permissions the same way as POSIX, * so just ignore the mode tests. */ /* TODO: Can we do better here? */ if (mode >= 0 && (mode_t)mode != (st.st_mode & 07777)) { failure_start(file, line, "File %s has wrong mode", pathname); logprintf(" Expected: 0%3o\n", mode); logprintf(" Found: 0%3o\n", st.st_mode & 07777); failure_finish(NULL); return (0); } #endif return (1); } /* Check whether 'pathname' is a symbolic link. If 'contents' is * non-NULL, verify that the symlink has those contents. */ static int is_symlink(const char *file, int line, const char *pathname, const char *contents) { #if defined(_WIN32) && !defined(__CYGWIN__) (void)pathname; /* UNUSED */ (void)contents; /* UNUSED */ assertion_count(file, line); /* Windows sort-of has real symlinks, but they're only usable * by privileged users and are crippled even then, so there's * really not much point in bothering with this. */ return (0); #else char buff[300]; struct stat st; ssize_t linklen; int r; assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { failure_start(file, line, "Symlink should exist: %s", pathname); failure_finish(NULL); return (0); } if (!S_ISLNK(st.st_mode)) return (0); if (contents == NULL) return (1); linklen = readlink(pathname, buff, sizeof(buff)); if (linklen < 0) { failure_start(file, line, "Can't read symlink %s", pathname); failure_finish(NULL); return (0); } buff[linklen] = '\0'; if (strcmp(buff, contents) != 0) return (0); return (1); #endif } /* Assert that path is a symlink that (optionally) contains contents. */ int assertion_is_symlink(const char *file, int line, const char *path, const char *contents) { if (is_symlink(file, line, path, contents)) return (1); if (contents) failure_start(file, line, "File %s is not a symlink to %s", path, contents); else failure_start(file, line, "File %s is not a symlink", path); failure_finish(NULL); return (0); } /* Create a directory and report any errors. */ int assertion_make_dir(const char *file, int line, const char *dirname, int mode) { assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) (void)mode; /* UNUSED */ if (0 == _mkdir(dirname)) return (1); #else if (0 == mkdir(dirname, mode)) { if (0 == chmod(dirname, mode)) { assertion_file_mode(file, line, dirname, mode); return (1); } } #endif failure_start(file, line, "Could not create directory %s", dirname); failure_finish(NULL); return(0); } /* Create a file with the specified contents and report any failures. */ int assertion_make_file(const char *file, int line, const char *path, int mode, int csize, const void *contents) { #if defined(_WIN32) && !defined(__CYGWIN__) /* TODO: Rework this to set file mode as well. */ FILE *f; (void)mode; /* UNUSED */ assertion_count(file, line); f = fopen(path, "wb"); if (f == NULL) { failure_start(file, line, "Could not create file %s", path); failure_finish(NULL); return (0); } if (contents != NULL) { size_t wsize; if (csize < 0) wsize = strlen(contents); else wsize = (size_t)csize; if (wsize != fwrite(contents, 1, wsize, f)) { fclose(f); failure_start(file, line, "Could not write file %s", path); failure_finish(NULL); return (0); } } fclose(f); return (1); #else int fd; assertion_count(file, line); fd = open(path, O_CREAT | O_WRONLY, mode >= 0 ? mode : 0644); if (fd < 0) { failure_start(file, line, "Could not create %s", path); failure_finish(NULL); return (0); } if (0 != chmod(path, mode)) { failure_start(file, line, "Could not chmod %s", path); failure_finish(NULL); close(fd); return (0); } if (contents != NULL) { ssize_t wsize; if (csize < 0) wsize = (ssize_t)strlen(contents); else wsize = (ssize_t)csize; if (wsize != write(fd, contents, wsize)) { close(fd); failure_start(file, line, "Could not write to %s", path); failure_finish(NULL); close(fd); return (0); } } close(fd); assertion_file_mode(file, line, path, mode); return (1); #endif } /* Create a hardlink and report any failures. */ int assertion_make_hardlink(const char *file, int line, const char *newpath, const char *linkto) { int succeeded; assertion_count(file, line); #if defined(_WIN32) && !defined(__CYGWIN__) succeeded = my_CreateHardLinkA(newpath, linkto); #elif HAVE_LINK succeeded = !link(linkto, newpath); #else succeeded = 0; #endif if (succeeded) return (1); failure_start(file, line, "Could not create hardlink"); logprintf(" New link: %s\n", newpath); logprintf(" Old name: %s\n", linkto); failure_finish(NULL); return(0); } /* Create a symlink and report any failures. */ int assertion_make_symlink(const char *file, int line, const char *newpath, const char *linkto) { #if defined(_WIN32) && !defined(__CYGWIN__) int targetIsDir = 0; /* TODO: Fix this */ assertion_count(file, line); if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir)) return (1); #elif HAVE_SYMLINK assertion_count(file, line); if (0 == symlink(linkto, newpath)) return (1); #endif failure_start(file, line, "Could not create symlink"); logprintf(" New link: %s\n", newpath); logprintf(" Old name: %s\n", linkto); failure_finish(NULL); return(0); } /* Set umask, report failures. */ int assertion_umask(const char *file, int line, int mask) { assertion_count(file, line); (void)file; /* UNUSED */ (void)line; /* UNUSED */ umask(mask); return (1); } /* Set times, report failures. */ int assertion_utimes(const char *file, int line, const char *pathname, long at, long at_nsec, long mt, long mt_nsec) { int r; #if defined(_WIN32) && !defined(__CYGWIN__) #define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\ + (((nsec)/1000)*10)) HANDLE h; ULARGE_INTEGER wintm; FILETIME fatime, fmtime; FILETIME *pat, *pmt; assertion_count(file, line); h = CreateFileA(pathname,GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); if (h == INVALID_HANDLE_VALUE) { failure_start(file, line, "Can't access %s\n", pathname); failure_finish(NULL); return (0); } if (at > 0 || at_nsec > 0) { wintm.QuadPart = WINTIME(at, at_nsec); fatime.dwLowDateTime = wintm.LowPart; fatime.dwHighDateTime = wintm.HighPart; pat = &fatime; } else pat = NULL; if (mt > 0 || mt_nsec > 0) { wintm.QuadPart = WINTIME(mt, mt_nsec); fmtime.dwLowDateTime = wintm.LowPart; fmtime.dwHighDateTime = wintm.HighPart; pmt = &fmtime; } else pmt = NULL; if (pat != NULL || pmt != NULL) r = SetFileTime(h, NULL, pat, pmt); else r = 1; CloseHandle(h); if (r == 0) { failure_start(file, line, "Can't SetFileTime %s\n", pathname); failure_finish(NULL); return (0); } return (1); #else /* defined(_WIN32) && !defined(__CYGWIN__) */ struct stat st; struct timeval times[2]; #if !defined(__FreeBSD__) mt_nsec = at_nsec = 0; /* Generic POSIX only has whole seconds. */ #endif if (mt == 0 && mt_nsec == 0 && at == 0 && at_nsec == 0) return (1); r = lstat(pathname, &st); if (r < 0) { failure_start(file, line, "Can't stat %s\n", pathname); failure_finish(NULL); return (0); } if (mt == 0 && mt_nsec == 0) { mt = st.st_mtime; #if defined(__FreeBSD__) mt_nsec = st.st_mtimespec.tv_nsec; /* FreeBSD generally only stores to microsecond res, so round. */ mt_nsec = (mt_nsec / 1000) * 1000; #endif } if (at == 0 && at_nsec == 0) { at = st.st_atime; #if defined(__FreeBSD__) at_nsec = st.st_atimespec.tv_nsec; /* FreeBSD generally only stores to microsecond res, so round. */ at_nsec = (at_nsec / 1000) * 1000; #endif } times[1].tv_sec = mt; times[1].tv_usec = mt_nsec / 1000; times[0].tv_sec = at; times[0].tv_usec = at_nsec / 1000; #ifdef HAVE_LUTIMES r = lutimes(pathname, times); #else r = utimes(pathname, times); #endif if (r < 0) { failure_start(file, line, "Can't utimes %s\n", pathname); failure_finish(NULL); return (0); } return (1); #endif /* defined(_WIN32) && !defined(__CYGWIN__) */ } /* Compare file flags */ int assertion_compare_fflags(const char *file, int line, const char *patha, const char *pathb, int nomatch) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) struct stat sa, sb; assertion_count(file, line); if (stat(patha, &sa) < 0) return (0); if (stat(pathb, &sb) < 0) return (0); if (!nomatch && sa.st_flags != sb.st_flags) { failure_start(file, line, "File flags should be identical: " "%s=%#010x %s=%#010x", patha, sa.st_flags, pathb, sb.st_flags); failure_finish(NULL); return (0); } if (nomatch && sa.st_flags == sb.st_flags) { failure_start(file, line, "File flags should be different: " "%s=%#010x %s=%#010x", patha, sa.st_flags, pathb, sb.st_flags); failure_finish(NULL); return (0); } #elif (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS) && \ defined(FS_NODUMP_FL)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) \ && defined(EXT2_NODUMP_FL)) int fd, r, flagsa, flagsb; assertion_count(file, line); fd = open(patha, O_RDONLY | O_NONBLOCK); if (fd < 0) { failure_start(file, line, "Can't open %s\n", patha); failure_finish(NULL); return (0); } r = ioctl(fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &flagsa); close(fd); if (r < 0) { failure_start(file, line, "Can't get flags %s\n", patha); failure_finish(NULL); return (0); } fd = open(pathb, O_RDONLY | O_NONBLOCK); if (fd < 0) { failure_start(file, line, "Can't open %s\n", pathb); failure_finish(NULL); return (0); } r = ioctl(fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &flagsb); close(fd); if (r < 0) { failure_start(file, line, "Can't get flags %s\n", pathb); failure_finish(NULL); return (0); } if (!nomatch && flagsa != flagsb) { failure_start(file, line, "File flags should be identical: " "%s=%#010x %s=%#010x", patha, flagsa, pathb, flagsb); failure_finish(NULL); return (0); } if (nomatch && flagsa == flagsb) { failure_start(file, line, "File flags should be different: " "%s=%#010x %s=%#010x", patha, flagsa, pathb, flagsb); failure_finish(NULL); return (0); } #else (void)patha; /* UNUSED */ (void)pathb; /* UNUSED */ (void)nomatch; /* UNUSED */ assertion_count(file, line); #endif return (1); } /* Set nodump, report failures. */ int assertion_set_nodump(const char *file, int line, const char *pathname) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) int r; assertion_count(file, line); r = chflags(pathname, UF_NODUMP); if (r < 0) { failure_start(file, line, "Can't set nodump %s\n", pathname); failure_finish(NULL); return (0); } #elif (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS) && \ defined(FS_NODUMP_FL)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) \ && defined(EXT2_NODUMP_FL)) int fd, r, flags; assertion_count(file, line); fd = open(pathname, O_RDONLY | O_NONBLOCK); if (fd < 0) { failure_start(file, line, "Can't open %s\n", pathname); failure_finish(NULL); return (0); } r = ioctl(fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &flags); if (r < 0) { failure_start(file, line, "Can't get flags %s\n", pathname); failure_finish(NULL); return (0); } #ifdef FS_NODUMP_FL flags |= FS_NODUMP_FL; #else flags |= EXT2_NODUMP_FL; #endif r = ioctl(fd, #ifdef FS_IOC_SETFLAGS FS_IOC_SETFLAGS, #else EXT2_IOC_SETFLAGS, #endif &flags); if (r < 0) { failure_start(file, line, "Can't set nodump %s\n", pathname); failure_finish(NULL); return (0); } close(fd); #else (void)pathname; /* UNUSED */ assertion_count(file, line); #endif return (1); } #ifdef PROGRAM static void assert_version_id(char **qq, size_t *ss) { char *q = *qq; size_t s = *ss; /* Version number is a series of digits and periods. */ while (s > 0 && (*q == '.' || (*q >= '0' && *q <= '9'))) { ++q; --s; } if (q[0] == 'd' && q[1] == 'e' && q[2] == 'v') { q += 3; s -= 3; } /* Skip a single trailing a,b,c, or d. */ if (*q == 'a' || *q == 'b' || *q == 'c' || *q == 'd') ++q; /* Version number terminated by space. */ failure("No space after version: ``%s''", q); assert(s > 1); failure("No space after version: ``%s''", q); assert(*q == ' '); ++q; --s; *qq = q; *ss = s; } /* * Check program version */ void assertVersion(const char *prog, const char *base) { int r; char *p, *q; size_t s; size_t prog_len = strlen(base); r = systemf("%s --version >version.stdout 2>version.stderr", prog); if (r != 0) r = systemf("%s -W version >version.stdout 2>version.stderr", prog); failure("Unable to run either %s --version or %s -W version", prog, prog); if (!assert(r == 0)) return; /* --version should generate nothing to stdout. */ assertEmptyFile("version.stderr"); /* Verify format of version message. */ q = p = slurpfile(&s, "version.stdout"); /* Version message should start with name of program, then space. */ assert(s > prog_len + 1); failure("Version must start with '%s': ``%s''", base, p); if (!assertEqualMem(q, base, prog_len)) { free(p); return; } q += prog_len; s -= prog_len; assert(*q == ' '); q++; s--; assert_version_id(&q, &s); /* Separator. */ failure("No `-' between program name and versions: ``%s''", p); assertEqualMem(q, "- ", 2); q += 2; s -= 2; failure("Not long enough for libarchive version: ``%s''", p); assert(s > 11); failure("Libarchive version must start with `libarchive': ``%s''", p); assertEqualMem(q, "libarchive ", 11); q += 11; s -= 11; assert_version_id(&q, &s); /* Skip arbitrary third-party version numbers. */ while (s > 0 && (*q == ' ' || *q == '-' || *q == '/' || *q == '.' || isalnum((unsigned char)*q))) { ++q; --s; } /* All terminated by end-of-line. */ assert(s >= 1); /* Skip an optional CR character (e.g., Windows) */ failure("Version output must end with \\n or \\r\\n"); if (*q == '\r') { ++q; --s; } assertEqualMem(q, "\n", 1); free(p); } #endif /* PROGRAM */ /* * * UTILITIES for use by tests. * */ /* * Check whether platform supports symlinks. This is intended * for tests to use in deciding whether to bother testing symlink * support; if the platform doesn't support symlinks, there's no point * in checking whether the program being tested can create them. * * Note that the first time this test is called, we actually go out to * disk to create and verify a symlink. This is necessary because * symlink support is actually a property of a particular filesystem * and can thus vary between directories on a single system. After * the first call, this returns the cached result from memory, so it's * safe to call it as often as you wish. */ int canSymlink(void) { /* Remember the test result */ static int value = 0, tested = 0; if (tested) return (value); ++tested; assertion_make_file(__FILE__, __LINE__, "canSymlink.0", 0644, 1, "a"); /* Note: Cygwin has its own symlink() emulation that does not * use the Win32 CreateSymbolicLink() function. */ #if defined(_WIN32) && !defined(__CYGWIN__) value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); #elif HAVE_SYMLINK value = (0 == symlink("canSymlink.0", "canSymlink.1")) && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); #endif return (value); } /* Platform-dependent options for hiding the output of a subcommand. */ #if defined(_WIN32) && !defined(__CYGWIN__) static const char *redirectArgs = ">NUL 2>NUL"; /* Win32 cmd.exe */ #else static const char *redirectArgs = ">/dev/null 2>/dev/null"; /* POSIX 'sh' */ #endif /* * Can this platform run the bzip2 program? */ int canBzip2(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("bzip2 -d -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the grzip program? */ int canGrzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("grzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the gzip program? */ int canGzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("gzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lrzip program? */ int canRunCommand(const char *cmd) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("%s %s", cmd, redirectArgs) == 0) value = 1; } return (value); } int canLrzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lrzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lz4 program? */ int canLz4(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lz4 -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the zstd program? */ int canZstd(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("zstd -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzip program? */ int canLzip(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzip -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzma program? */ int canLzma(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzma -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the lzop program? */ int canLzop(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("lzop -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this platform run the xz program? */ int canXz(void) { static int tested = 0, value = 0; if (!tested) { tested = 1; if (systemf("xz -V %s", redirectArgs) == 0) value = 1; } return (value); } /* * Can this filesystem handle nodump flags. */ int canNodump(void) { #if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) const char *path = "cannodumptest"; struct stat sb; assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); if (chflags(path, UF_NODUMP) < 0) return (0); if (stat(path, &sb) < 0) return (0); if (sb.st_flags & UF_NODUMP) return (1); #elif (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS) \ && defined(FS_NODUMP_FL)) || \ (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) \ && defined(EXT2_NODUMP_FL)) const char *path = "cannodumptest"; int fd, r, flags; assertion_make_file(__FILE__, __LINE__, path, 0644, 0, NULL); fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) return (0); r = ioctl(fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &flags); if (r < 0) return (0); #ifdef FS_NODUMP_FL flags |= FS_NODUMP_FL; #else flags |= EXT2_NODUMP_FL; #endif r = ioctl(fd, #ifdef FS_IOC_SETFLAGS FS_IOC_SETFLAGS, #else EXT2_IOC_SETFLAGS, #endif &flags); if (r < 0) return (0); close(fd); fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) return (0); r = ioctl(fd, #ifdef FS_IOC_GETFLAGS FS_IOC_GETFLAGS, #else EXT2_IOC_GETFLAGS, #endif &flags); if (r < 0) return (0); close(fd); #ifdef FS_NODUMP_FL if (flags & FS_NODUMP_FL) #else if (flags & EXT2_NODUMP_FL) #endif return (1); #endif return (0); } /* Get extended attribute from a path */ const void * getXattr(const char *path, const char *name, size_t *sizep) { void *value = NULL; #if ARCHIVE_XATTR_SUPPORT ssize_t size; #if ARCHIVE_XATTR_LINUX size = lgetxattr(path, name, NULL, 0); #elif ARCHIVE_XATTR_DARWIN size = getxattr(path, name, NULL, 0, 0, XATTR_NOFOLLOW); #elif ARCHIVE_XATTR_AIX size = lgetea(path, name, NULL, 0); #elif ARCHIVE_XATTR_FREEBSD size = extattr_get_link(path, EXTATTR_NAMESPACE_USER, name + 5, NULL, 0); #endif if (size >= 0) { value = malloc(size); #if ARCHIVE_XATTR_LINUX size = lgetxattr(path, name, value, size); #elif ARCHIVE_XATTR_DARWIN size = getxattr(path, name, value, size, 0, XATTR_NOFOLLOW); #elif ARCHIVE_XATTR_AIX size = lgetea(path, name, value, size); #elif ARCHIVE_XATTR_FREEBSD size = extattr_get_link(path, EXTATTR_NAMESPACE_USER, name + 5, value, size); #endif if (size < 0) { free(value); value = NULL; } } if (size < 0) *sizep = 0; else *sizep = (size_t)size; #else /* !ARCHIVE_XATTR_SUPPORT */ (void)path; /* UNUSED */ (void)name; /* UNUSED */ *sizep = 0; #endif /* !ARCHIVE_XATTR_SUPPORT */ return (value); } /* * Set extended attribute on a path * Returns 0 on error, 1 on success */ int setXattr(const char *path, const char *name, const void *value, size_t size) { #if ARCHIVE_XATTR_SUPPORT #if ARCHIVE_XATTR_LINUX if (lsetxattr(path, name, value, size, 0) == 0) #elif ARCHIVE_XATTR_DARWIN if (setxattr(path, name, value, size, 0, XATTR_NOFOLLOW) == 0) #elif ARCHIVE_XATTR_AIX if (lsetea(path, name, value, size, 0) == 0) #elif ARCHIVE_XATTR_FREEBSD if (extattr_set_link(path, EXTATTR_NAMESPACE_USER, name + 5, value, size) > -1) #else if (0) #endif return (1); #else /* !ARCHIVE_XATTR_SUPPORT */ (void)path; /* UNUSED */ (void)name; /* UNUSED */ (void)value; /* UNUSED */ (void)size; /* UNUSED */ #endif /* !ARCHIVE_XATTR_SUPPORT */ return (0); } #if ARCHIVE_ACL_SUNOS /* Fetch ACLs on Solaris using acl() or facl() */ void * sunacl_get(int cmd, int *aclcnt, int fd, const char *path) { int cnt, cntcmd; size_t size; void *aclp; if (cmd == GETACL) { cntcmd = GETACLCNT; size = sizeof(aclent_t); } #if ARCHIVE_ACL_SUNOS_NFS4 else if (cmd == ACE_GETACL) { cntcmd = ACE_GETACLCNT; size = sizeof(ace_t); } #endif else { errno = EINVAL; *aclcnt = -1; return (NULL); } aclp = NULL; cnt = -2; while (cnt == -2 || (cnt == -1 && errno == ENOSPC)) { if (path != NULL) cnt = acl(path, cntcmd, 0, NULL); else cnt = facl(fd, cntcmd, 0, NULL); if (cnt > 0) { if (aclp == NULL) aclp = malloc(cnt * size); else aclp = realloc(NULL, cnt * size); if (aclp != NULL) { if (path != NULL) cnt = acl(path, cmd, cnt, aclp); else cnt = facl(fd, cmd, cnt, aclp); } } else { - if (aclp != NULL) { - free(aclp); - aclp = NULL; - } + free(aclp); + aclp = NULL; break; } } *aclcnt = cnt; return (aclp); } #endif /* ARCHIVE_ACL_SUNOS */ /* * Set test ACLs on a path * Return values: * 0: error setting ACLs * ARCHIVE_TEST_ACL_TYPE_POSIX1E: POSIX.1E ACLs have been set * ARCHIVE_TEST_ACL_TYPE_NFS4: NFSv4 or extended ACLs have been set */ int setTestAcl(const char *path) { #if ARCHIVE_ACL_SUPPORT int r = 1; #if ARCHIVE_ACL_LIBACL || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_DARWIN acl_t acl; #endif #if ARCHIVE_ACL_LIBRICHACL struct richacl *richacl; #endif #if ARCHIVE_ACL_LIBACL || ARCHIVE_ACL_FREEBSD const char *acltext_posix1e = "user:1:rw-," "group:15:r-x," "user::rwx," "group::rwx," "other::r-x," "mask::rwx"; #elif ARCHIVE_ACL_SUNOS /* Solaris POSIX.1e */ aclent_t aclp_posix1e[] = { { USER_OBJ, -1, 4 | 2 | 1 }, { USER, 1, 4 | 2 }, { GROUP_OBJ, -1, 4 | 2 | 1 }, { GROUP, 15, 4 | 1 }, { CLASS_OBJ, -1, 4 | 2 | 1 }, { OTHER_OBJ, -1, 4 | 2 | 1 } }; #endif #if ARCHIVE_ACL_FREEBSD /* FreeBSD NFS4 */ const char *acltext_nfs4 = "user:1:rwpaRcs::allow:1," "group:15:rxaRcs::allow:15," "owner@:rwpxaARWcCos::allow," "group@:rwpxaRcs::allow," "everyone@:rxaRcs::allow"; #elif ARCHIVE_ACL_LIBRICHACL const char *acltext_nfs4 = "owner:rwpxaARWcCoS::mask," "group:rwpxaRcS::mask," "other:rxaRcS::mask," "user:1:rwpaRcS::allow," "group:15:rxaRcS::allow," "owner@:rwpxaARWcCoS::allow," "group@:rwpxaRcS::allow," "everyone@:rxaRcS::allow"; #elif ARCHIVE_ACL_SUNOS_NFS4 /* Solaris NFS4 */ ace_t aclp_nfs4[] = { { 1, ACE_READ_DATA | ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_READ_ACL | ACE_SYNCHRONIZE, 0, ACE_ACCESS_ALLOWED_ACE_TYPE }, { 15, ACE_READ_DATA | ACE_EXECUTE | ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_READ_ACL | ACE_SYNCHRONIZE, ACE_IDENTIFIER_GROUP, ACE_ACCESS_ALLOWED_ACE_TYPE }, { -1, ACE_READ_DATA | ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | ACE_READ_ATTRIBUTES | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_WRITE_NAMED_ATTRS | ACE_READ_ACL | ACE_WRITE_ACL | ACE_WRITE_OWNER | ACE_SYNCHRONIZE, ACE_OWNER, ACE_ACCESS_ALLOWED_ACE_TYPE }, { -1, ACE_READ_DATA | ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_READ_ACL | ACE_SYNCHRONIZE, ACE_GROUP | ACE_IDENTIFIER_GROUP, ACE_ACCESS_ALLOWED_ACE_TYPE }, { -1, ACE_READ_DATA | ACE_EXECUTE | ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS | ACE_READ_ACL | ACE_SYNCHRONIZE, ACE_EVERYONE, ACE_ACCESS_ALLOWED_ACE_TYPE } }; #elif ARCHIVE_ACL_DARWIN /* Mac OS X */ acl_entry_t aclent; acl_permset_t permset; const uid_t uid = 1; uuid_t uuid; int i; const acl_perm_t acl_perms[] = { ACL_READ_DATA, ACL_WRITE_DATA, ACL_APPEND_DATA, ACL_EXECUTE, ACL_READ_ATTRIBUTES, ACL_READ_EXTATTRIBUTES, ACL_READ_SECURITY, #if HAVE_DECL_ACL_SYNCHRONIZE ACL_SYNCHRONIZE #endif }; #endif /* ARCHIVE_ACL_DARWIN */ #if ARCHIVE_ACL_FREEBSD acl = acl_from_text(acltext_nfs4); failure("acl_from_text() error: %s", strerror(errno)); if (assert(acl != NULL) == 0) return (0); #elif ARCHIVE_ACL_LIBRICHACL richacl = richacl_from_text(acltext_nfs4, NULL, NULL); failure("richacl_from_text() error: %s", strerror(errno)); if (assert(richacl != NULL) == 0) return (0); #elif ARCHIVE_ACL_DARWIN acl = acl_init(1); failure("acl_init() error: %s", strerror(errno)); if (assert(acl != NULL) == 0) return (0); r = acl_create_entry(&acl, &aclent); failure("acl_create_entry() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; r = acl_set_tag_type(aclent, ACL_EXTENDED_ALLOW); failure("acl_set_tag_type() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; r = acl_get_permset(aclent, &permset); failure("acl_get_permset() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; for (i = 0; i < (int)(sizeof(acl_perms) / sizeof(acl_perms[0])); i++) { r = acl_add_perm(permset, acl_perms[i]); failure("acl_add_perm() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; } r = acl_set_permset(aclent, permset); failure("acl_set_permset() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; r = mbr_uid_to_uuid(uid, uuid); failure("mbr_uid_to_uuid() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; r = acl_set_qualifier(aclent, uuid); failure("acl_set_qualifier() error: %s", strerror(errno)); if (assertEqualInt(r, 0) == 0) goto testacl_free; #endif /* ARCHIVE_ACL_DARWIN */ #if ARCHIVE_ACL_NFS4 #if ARCHIVE_ACL_FREEBSD r = acl_set_file(path, ACL_TYPE_NFS4, acl); acl_free(acl); #elif ARCHIVE_ACL_LIBRICHACL r = richacl_set_file(path, richacl); richacl_free(richacl); #elif ARCHIVE_ACL_SUNOS_NFS4 r = acl(path, ACE_SETACL, (int)(sizeof(aclp_nfs4)/sizeof(aclp_nfs4[0])), aclp_nfs4); #elif ARCHIVE_ACL_DARWIN r = acl_set_file(path, ACL_TYPE_EXTENDED, acl); acl_free(acl); #endif if (r == 0) return (ARCHIVE_TEST_ACL_TYPE_NFS4); #endif /* ARCHIVE_ACL_NFS4 */ #if ARCHIVE_ACL_POSIX1E #if ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL acl = acl_from_text(acltext_posix1e); failure("acl_from_text() error: %s", strerror(errno)); if (assert(acl != NULL) == 0) return (0); r = acl_set_file(path, ACL_TYPE_ACCESS, acl); acl_free(acl); #elif ARCHIVE_ACL_SUNOS r = acl(path, SETACL, (int)(sizeof(aclp_posix1e)/sizeof(aclp_posix1e[0])), aclp_posix1e); #endif if (r == 0) return (ARCHIVE_TEST_ACL_TYPE_POSIX1E); else return (0); #endif /* ARCHIVE_ACL_POSIX1E */ #if ARCHIVE_ACL_DARWIN testacl_free: acl_free(acl); #endif #endif /* ARCHIVE_ACL_SUPPORT */ (void)path; /* UNUSED */ return (0); } /* * Sleep as needed; useful for verifying disk timestamp changes by * ensuring that the wall-clock time has actually changed before we * go back to re-read something from disk. */ void sleepUntilAfter(time_t t) { while (t >= time(NULL)) #if defined(_WIN32) && !defined(__CYGWIN__) Sleep(500); #else sleep(1); #endif } /* * Call standard system() call, but build up the command line using * sprintf() conventions. */ int systemf(const char *fmt, ...) { char buff[8192]; va_list ap; int r; va_start(ap, fmt); vsprintf(buff, fmt, ap); if (verbosity > VERBOSITY_FULL) logprintf("Cmd: %s\n", buff); r = system(buff); va_end(ap); return (r); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ char * slurpfile(size_t * sizep, const char *fmt, ...) { char filename[8192]; struct stat st; va_list ap; char *p; ssize_t bytes_read; FILE *f; int r; va_start(ap, fmt); vsprintf(filename, fmt, ap); va_end(ap); f = fopen(filename, "rb"); if (f == NULL) { /* Note: No error; non-existent file is okay here. */ return (NULL); } r = fstat(fileno(f), &st); if (r != 0) { logprintf("Can't stat file %s\n", filename); fclose(f); return (NULL); } p = malloc((size_t)st.st_size + 1); if (p == NULL) { logprintf("Can't allocate %ld bytes of memory to read file %s\n", (long int)st.st_size, filename); fclose(f); return (NULL); } bytes_read = fread(p, 1, (size_t)st.st_size, f); if (bytes_read < st.st_size) { logprintf("Can't read file %s\n", filename); fclose(f); free(p); return (NULL); } p[st.st_size] = '\0'; if (sizep != NULL) *sizep = (size_t)st.st_size; fclose(f); return (p); } /* * Slurp a file into memory for ease of comparison and testing. * Returns size of file in 'sizep' if non-NULL, null-terminates * data in memory for ease of use. */ void dumpfile(const char *filename, void *data, size_t len) { ssize_t bytes_written; FILE *f; f = fopen(filename, "wb"); if (f == NULL) { logprintf("Can't open file %s for writing\n", filename); return; } bytes_written = fwrite(data, 1, len, f); if (bytes_written < (ssize_t)len) logprintf("Can't write file %s\n", filename); fclose(f); } /* Read a uuencoded file from the reference directory, decode, and * write the result into the current directory. */ #define VALID_UUDECODE(c) (c >= 32 && c <= 96) #define UUDECODE(c) (((c) - 0x20) & 0x3f) void extract_reference_file(const char *name) { char buff[1024]; FILE *in, *out; sprintf(buff, "%s/%s.uu", refdir, name); in = fopen(buff, "r"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Read up to and including the 'begin' line. */ for (;;) { if (fgets(buff, sizeof(buff), in) == NULL) { /* TODO: This is a failure. */ return; } if (memcmp(buff, "begin ", 6) == 0) break; } /* Now, decode the rest and write it. */ out = fopen(name, "wb"); while (fgets(buff, sizeof(buff), in) != NULL) { char *p = buff; int bytes; if (memcmp(buff, "end", 3) == 0) break; bytes = UUDECODE(*p++); while (bytes > 0) { int n = 0; /* Write out 1-3 bytes from that. */ if (bytes > 0) { assert(VALID_UUDECODE(p[0])); assert(VALID_UUDECODE(p[1])); n = UUDECODE(*p++) << 18; n |= UUDECODE(*p++) << 12; fputc(n >> 16, out); --bytes; } if (bytes > 0) { assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++) << 6; fputc((n >> 8) & 0xFF, out); --bytes; } if (bytes > 0) { assert(VALID_UUDECODE(p[0])); n |= UUDECODE(*p++); fputc(n & 0xFF, out); --bytes; } } } fclose(out); fclose(in); } void copy_reference_file(const char *name) { char buff[1024]; FILE *in, *out; size_t rbytes; sprintf(buff, "%s/%s", refdir, name); in = fopen(buff, "rb"); failure("Couldn't open reference file %s", buff); assert(in != NULL); if (in == NULL) return; /* Now, decode the rest and write it. */ /* Not a lot of error checking here; the input better be right. */ out = fopen(name, "wb"); while ((rbytes = fread(buff, 1, sizeof(buff), in)) > 0) { if (fwrite(buff, 1, rbytes, out) != rbytes) { logprintf("Error: fwrite\n"); break; } } fclose(out); fclose(in); } int is_LargeInode(const char *file) { #if defined(_WIN32) && !defined(__CYGWIN__) BY_HANDLE_FILE_INFORMATION bhfi; int r; r = my_GetFileInformationByName(file, &bhfi); if (r != 0) return (0); return (bhfi.nFileIndexHigh & 0x0000FFFFUL); #else struct stat st; int64_t ino; if (stat(file, &st) < 0) return (0); ino = (int64_t)st.st_ino; return (ino > 0xffffffff); #endif } void extract_reference_files(const char **names) { while (names && *names) extract_reference_file(*names++); } #ifndef PROGRAM /* Set ACLs */ int assertion_entry_set_acls(const char *file, int line, struct archive_entry *ae, struct archive_test_acl_t *acls, int n) { int i, r, ret; assertion_count(file, line); ret = 0; archive_entry_acl_clear(ae); for (i = 0; i < n; i++) { r = archive_entry_acl_add_entry(ae, acls[i].type, acls[i].permset, acls[i].tag, acls[i].qual, acls[i].name); if (r != 0) { ret = 1; failure_start(file, line, "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); failure_finish(NULL); } } return (ret); } 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 */ int assertion_entry_compare_acls(const char *file, int line, struct archive_entry *ae, struct archive_test_acl_t *acls, int cnt, int want_type, int mode) { int *marker; int i, r, n, ret; int type, permset, tag, qual; int matched; const char *name; assertion_count(file, line); ret = 0; 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++; } } if (n == 0) { failure_start(file, line, "No ACL's to compare, type mask: %d", want_type); return (1); } 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) { failure_start(file, line, "No match for " "user_obj perm"); failure_finish(NULL); ret = 1; } if ((permset << 6) != (mode & 0700)) { failure_start(file, line, "USER_OBJ permset " "(%02o) != user mode (%02o)", permset, 07 & (mode >> 6)); failure_finish(NULL); ret = 1; } } else if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS && tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ) { if (!matched) { failure_start(file, line, "No match for " "group_obj perm"); failure_finish(NULL); ret = 1; } if ((permset << 3) != (mode & 0070)) { failure_start(file, line, "GROUP_OBJ permset " "(%02o) != group mode (%02o)", permset, 07 & (mode >> 3)); failure_finish(NULL); ret = 1; } } else if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS && tag == ARCHIVE_ENTRY_ACL_OTHER) { if (!matched) { failure_start(file, line, "No match for " "other perm"); failure_finish(NULL); ret = 1; } if ((permset << 0) != (mode & 0007)) { failure_start(file, line, "OTHER permset " "(%02o) != other mode (%02o)", permset, mode & 07); failure_finish(NULL); ret = 1; } } else if (matched != 1) { failure_start(file, line, "Could not find match for " "ACL (type=%#010x,permset=%#010x,tag=%d,qual=%d," "name=``%s'')", type, permset, tag, qual, name); failure_finish(NULL); ret = 1; } } if (r != ARCHIVE_EOF) { failure_start(file, line, "Should not exit before EOF"); failure_finish(NULL); ret = 1; } if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0 && (mode_t)(mode & 0777) != (archive_entry_mode(ae) & 0777)) { failure_start(file, line, "Mode (%02o) and entry mode (%02o) " "mismatch", mode, archive_entry_mode(ae)); failure_finish(NULL); ret = 1; } if (n != 0) { failure_start(file, line, "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); failure_finish(NULL); ret = 1; /* Number of ACLs not matched should == 0 */ } free(marker); return (ret); } #endif /* !defined(PROGRAM) */ /* * * TEST management * */ /* * "list.h" is simply created by "grep DEFINE_TEST test_*.c"; it has * a line like * DEFINE_TEST(test_function) * for each test. */ /* Use "list.h" to declare all of the test functions. */ #undef DEFINE_TEST #define DEFINE_TEST(name) void name(void); #include "list.h" /* Use "list.h" to create a list of all tests (functions and names). */ #undef DEFINE_TEST #define DEFINE_TEST(n) { n, #n, 0 }, struct test_list_t tests[] = { #include "list.h" }; /* * Summarize repeated failures in the just-completed test. */ static void test_summarize(int failed, int skips_num) { unsigned int i; switch (verbosity) { case VERBOSITY_SUMMARY_ONLY: printf(failed ? "E" : "."); fflush(stdout); break; case VERBOSITY_PASSFAIL: printf(failed ? "FAIL\n" : skips_num ? "ok (S)\n" : "ok\n"); break; } log_console = (verbosity == VERBOSITY_LIGHT_REPORT); for (i = 0; i < sizeof(failed_lines)/sizeof(failed_lines[0]); i++) { if (failed_lines[i].count > 1 && !failed_lines[i].skip) logprintf("%s:%d: Summary: Failed %d times\n", failed_filename, i, failed_lines[i].count); } /* Clear the failure history for the next file. */ failed_filename = NULL; memset(failed_lines, 0, sizeof(failed_lines)); } /* * Actually run a single test, with appropriate setup and cleanup. */ static int test_run(int i, const char *tmpdir) { #ifdef PATH_MAX char workdir[PATH_MAX]; #else char workdir[1024]; #endif char logfilename[64]; int failures_before = failures; int skips_before = skips; int oldumask; switch (verbosity) { case VERBOSITY_SUMMARY_ONLY: /* No per-test reports at all */ break; case VERBOSITY_PASSFAIL: /* rest of line will include ok/FAIL marker */ printf("%3d: %-64s", i, tests[i].name); fflush(stdout); break; default: /* Title of test, details will follow */ printf("%3d: %s\n", i, tests[i].name); } /* Chdir to the top-level work directory. */ if (!assertChdir(tmpdir)) { fprintf(stderr, "ERROR: Can't chdir to top work dir %s\n", tmpdir); exit(1); } /* Create a log file for this test. */ sprintf(logfilename, "%s.log", tests[i].name); logfile = fopen(logfilename, "w"); fprintf(logfile, "%s\n\n", tests[i].name); /* Chdir() to a work dir for this specific test. */ snprintf(workdir, sizeof(workdir), "%s/%s", tmpdir, tests[i].name); testworkdir = workdir; if (!assertMakeDir(testworkdir, 0755) || !assertChdir(testworkdir)) { fprintf(stderr, "ERROR: Can't chdir to work dir %s\n", testworkdir); exit(1); } /* Explicitly reset the locale before each test. */ setlocale(LC_ALL, "C"); /* Record the umask before we run the test. */ umask(oldumask = umask(0)); /* * Run the actual test. */ (*tests[i].func)(); /* * Clean up and report afterwards. */ testworkdir = NULL; /* Restore umask */ umask(oldumask); /* Reset locale. */ setlocale(LC_ALL, "C"); /* Reset directory. */ if (!assertChdir(tmpdir)) { fprintf(stderr, "ERROR: Couldn't chdir to temp dir %s\n", tmpdir); exit(1); } /* Report per-test summaries. */ tests[i].failures = failures - failures_before; test_summarize(tests[i].failures, skips - skips_before); /* Close the per-test log file. */ fclose(logfile); logfile = NULL; /* If there were no failures, we can remove the work dir and logfile. */ if (tests[i].failures == 0) { if (!keep_temp_files && assertChdir(tmpdir)) { #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure not to leave empty directories. * Sometimes a processing of closing files used by tests * is not done, then rmdir will be failed and it will * leave a empty test directory. So we should wait a few * seconds and retry rmdir. */ int r, t; for (t = 0; t < 10; t++) { if (t > 0) Sleep(1000); r = systemf("rmdir /S /Q %s", tests[i].name); if (r == 0) break; } systemf("del %s", logfilename); #else systemf("rm -rf %s", tests[i].name); systemf("rm %s", logfilename); #endif } } /* Return appropriate status. */ return (tests[i].failures); } /* * * * MAIN and support routines. * * */ static void usage(const char *program) { static const int limit = sizeof(tests) / sizeof(tests[0]); int i; printf("Usage: %s [options] ...\n", program); printf("Default is to run all tests.\n"); printf("Otherwise, specify the numbers of the tests you wish to run.\n"); printf("Options:\n"); printf(" -d Dump core after any failure, for debugging.\n"); printf(" -k Keep all temp files.\n"); printf(" Default: temp files for successful tests deleted.\n"); #ifdef PROGRAM printf(" -p Path to executable to be tested.\n"); printf(" Default: path taken from " ENVBASE " environment variable.\n"); #endif printf(" -q Quiet.\n"); printf(" -r Path to dir containing reference files.\n"); printf(" Default: Current directory.\n"); printf(" -u Keep running specifies tests until one fails.\n"); printf(" -v Verbose.\n"); printf("Available tests:\n"); for (i = 0; i < limit; i++) printf(" %d: %s\n", i, tests[i].name); exit(1); } static char * get_refdir(const char *d) { size_t tried_size, buff_size; char *buff, *tried, *pwd = NULL, *p = NULL; #ifdef PATH_MAX buff_size = PATH_MAX; #else buff_size = 8192; #endif buff = calloc(buff_size, 1); if (buff == NULL) { fprintf(stderr, "Unable to allocate memory\n"); exit(1); } /* Allocate a buffer to hold the various directories we checked. */ tried_size = buff_size * 2; tried = calloc(tried_size, 1); if (tried == NULL) { fprintf(stderr, "Unable to allocate memory\n"); exit(1); } /* If a dir was specified, try that */ if (d != NULL) { pwd = NULL; snprintf(buff, buff_size, "%s", d); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); goto failure; } /* Get the current dir. */ #ifdef PATH_MAX pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else pwd = getcwd(NULL, 0); #endif while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; /* Look for a known file. */ snprintf(buff, buff_size, "%s", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); snprintf(buff, buff_size, "%s/test", pwd); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); #if defined(LIBRARY) snprintf(buff, buff_size, "%s/%s/test", pwd, LIBRARY); #else snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM); #endif p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); #if defined(PROGRAM_ALIAS) snprintf(buff, buff_size, "%s/%s/test", pwd, PROGRAM_ALIAS); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); #endif if (memcmp(pwd, "/usr/obj", 8) == 0) { snprintf(buff, buff_size, "%s", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); snprintf(buff, buff_size, "%s/test", pwd + 8); p = slurpfile(NULL, "%s/%s", buff, KNOWNREF); if (p != NULL) goto success; strncat(tried, buff, tried_size - strlen(tried) - 1); strncat(tried, "\n", tried_size - strlen(tried) - 1); } failure: printf("Unable to locate known reference file %s\n", KNOWNREF); printf(" Checked following directories:\n%s\n", tried); printf("Use -r option to specify full path to reference directory\n"); #if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG) DebugBreak(); #endif exit(1); success: free(p); free(pwd); free(tried); /* Copy result into a fresh buffer to reduce memory usage. */ p = strdup(buff); free(buff); return p; } int main(int argc, char **argv) { static const int limit = sizeof(tests) / sizeof(tests[0]); int test_set[sizeof(tests) / sizeof(tests[0])]; int i = 0, j = 0, tests_run = 0, tests_failed = 0, option; time_t now; char *refdir_alloc = NULL; const char *progname; char **saved_argv; const char *tmp, *option_arg, *p; #ifdef PATH_MAX char tmpdir[PATH_MAX]; #else char tmpdir[256]; #endif char *pwd, *testprogdir, *tmp2 = NULL, *vlevel = NULL; char tmpdir_timestamp[32]; (void)argc; /* UNUSED */ /* Get the current dir. */ #ifdef PATH_MAX pwd = getcwd(NULL, PATH_MAX);/* Solaris getcwd needs the size. */ #else pwd = getcwd(NULL, 0); #endif while (pwd[strlen(pwd) - 1] == '\n') pwd[strlen(pwd) - 1] = '\0'; #if defined(HAVE__CrtSetReportMode) && !defined(__WATCOMC__) /* To stop to run the default invalid parameter handler. */ _set_invalid_parameter_handler(invalid_parameter_handler); /* Disable annoying assertion message box. */ _CrtSetReportMode(_CRT_ASSERT, 0); #endif /* * Name of this program, used to build root of our temp directory * tree. */ progname = p = argv[0]; if ((testprogdir = (char *)malloc(strlen(progname) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strcpy(testprogdir, progname); while (*p != '\0') { /* Support \ or / dir separators for Windows compat. */ if (*p == '/' || *p == '\\') { progname = p + 1; i = j; } ++p; j++; } testprogdir[i] = '\0'; #if defined(_WIN32) && !defined(__CYGWIN__) if (testprogdir[0] != '/' && testprogdir[0] != '\\' && !(((testprogdir[0] >= 'a' && testprogdir[0] <= 'z') || (testprogdir[0] >= 'A' && testprogdir[0] <= 'Z')) && testprogdir[1] == ':' && (testprogdir[2] == '/' || testprogdir[2] == '\\'))) #else if (testprogdir[0] != '/') #endif { /* Fixup path for relative directories. */ if ((testprogdir = (char *)realloc(testprogdir, strlen(pwd) + 1 + strlen(testprogdir) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } memmove(testprogdir + strlen(pwd) + 1, testprogdir, strlen(testprogdir) + 1); memcpy(testprogdir, pwd, strlen(pwd)); testprogdir[strlen(pwd)] = '/'; } #ifdef PROGRAM /* Get the target program from environment, if available. */ testprogfile = getenv(ENVBASE); #endif if (getenv("TMPDIR") != NULL) tmp = getenv("TMPDIR"); else if (getenv("TMP") != NULL) tmp = getenv("TMP"); else if (getenv("TEMP") != NULL) tmp = getenv("TEMP"); else if (getenv("TEMPDIR") != NULL) tmp = getenv("TEMPDIR"); else tmp = "/tmp"; /* Allow -d to be controlled through the environment. */ if (getenv(ENVBASE "_DEBUG") != NULL) dump_on_failure = 1; /* Allow -v to be controlled through the environment. */ if (getenv("_VERBOSITY_LEVEL") != NULL) { vlevel = getenv("_VERBOSITY_LEVEL"); verbosity = atoi(vlevel); if (verbosity < VERBOSITY_SUMMARY_ONLY || verbosity > VERBOSITY_FULL) { /* Unsupported verbosity levels are silently ignored */ vlevel = NULL; verbosity = VERBOSITY_PASSFAIL; } } /* Get the directory holding test files from environment. */ refdir = getenv(ENVBASE "_TEST_FILES"); /* * Parse options, without using getopt(), which isn't available * on all platforms. */ ++argv; /* Skip program name */ while (*argv != NULL) { if (**argv != '-') break; p = *argv++; ++p; /* Skip '-' */ while (*p != '\0') { option = *p++; option_arg = NULL; /* If 'opt' takes an argument, parse that. */ if (option == 'p' || option == 'r') { if (*p != '\0') option_arg = p; else if (*argv == NULL) { fprintf(stderr, "Option -%c requires argument.\n", option); usage(progname); } else option_arg = *argv++; p = ""; /* End of this option word. */ } /* Now, handle the option. */ switch (option) { case 'd': dump_on_failure = 1; break; case 'k': keep_temp_files = 1; break; case 'p': #ifdef PROGRAM testprogfile = option_arg; #else fprintf(stderr, "-p option not permitted\n"); usage(progname); #endif break; case 'q': if (!vlevel) verbosity--; break; case 'r': refdir = option_arg; break; case 'u': until_failure++; break; case 'v': if (!vlevel) verbosity++; break; default: fprintf(stderr, "Unrecognized option '%c'\n", option); usage(progname); } } } /* * Sanity-check that our options make sense. */ #ifdef PROGRAM if (testprogfile == NULL) { if ((tmp2 = (char *)malloc(strlen(testprogdir) + 1 + strlen(PROGRAM) + 1)) == NULL) { fprintf(stderr, "ERROR: Out of memory."); exit(1); } strcpy(tmp2, testprogdir); strcat(tmp2, "/"); strcat(tmp2, PROGRAM); testprogfile = tmp2; } { char *testprg; #if defined(_WIN32) && !defined(__CYGWIN__) /* Command.com sometimes rejects '/' separators. */ testprg = strdup(testprogfile); for (i = 0; testprg[i] != '\0'; i++) { if (testprg[i] == '/') testprg[i] = '\\'; } testprogfile = testprg; #endif /* Quote the name that gets put into shell command lines. */ testprg = malloc(strlen(testprogfile) + 3); strcpy(testprg, "\""); strcat(testprg, testprogfile); strcat(testprg, "\""); testprog = testprg; } #endif #if !defined(_WIN32) && defined(SIGPIPE) { /* Ignore SIGPIPE signals */ struct sigaction sa; sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGPIPE, &sa, NULL); } #endif /* * Create a temp directory for the following tests. * Include the time the tests started as part of the name, * to make it easier to track the results of multiple tests. */ now = time(NULL); for (i = 0; ; i++) { strftime(tmpdir_timestamp, sizeof(tmpdir_timestamp), "%Y-%m-%dT%H.%M.%S", localtime(&now)); if ((strlen(tmp) + 1 + strlen(progname) + 1 + strlen(tmpdir_timestamp) + 1 + 3) > (sizeof(tmpdir) / sizeof(char))) { fprintf(stderr, "ERROR: Temp directory pathname too long\n"); exit(1); } snprintf(tmpdir, sizeof(tmpdir), "%s/%s.%s-%03d", tmp, progname, tmpdir_timestamp, i); if (assertMakeDir(tmpdir,0755)) break; if (i >= 999) { fprintf(stderr, "ERROR: Unable to create temp directory %s\n", tmpdir); exit(1); } } /* * If the user didn't specify a directory for locating * reference files, try to find the reference files in * the "usual places." */ refdir = refdir_alloc = get_refdir(refdir); /* * Banner with basic information. */ printf("\n"); printf("If tests fail or crash, details will be in:\n"); printf(" %s\n", tmpdir); printf("\n"); if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("Reference files will be read from: %s\n", refdir); #ifdef PROGRAM printf("Running tests on: %s\n", testprog); #endif printf("Exercising: "); fflush(stdout); printf("%s\n", EXTRA_VERSION); } else { printf("Running "); fflush(stdout); } /* * Run some or all of the individual tests. */ saved_argv = argv; do { argv = saved_argv; do { int test_num; test_num = get_test_set(test_set, limit, *argv, tests); if (test_num < 0) { printf("*** INVALID Test %s\n", *argv); free(refdir_alloc); free(testprogdir); usage(progname); return (1); } for (i = 0; i < test_num; i++) { tests_run++; if (test_run(test_set[i], tmpdir)) { tests_failed++; if (until_failure) goto finish; } } if (*argv != NULL) argv++; } while (*argv != NULL); } while (until_failure); finish: /* Must be freed after all tests run */ free(tmp2); free(testprogdir); free(pwd); /* * Report summary statistics. */ if (verbosity > VERBOSITY_SUMMARY_ONLY) { printf("\n"); printf("Totals:\n"); printf(" Tests run: %8d\n", tests_run); printf(" Tests failed: %8d\n", tests_failed); printf(" Assertions checked:%8d\n", assertions); printf(" Assertions failed: %8d\n", failures); printf(" Skips reported: %8d\n", skips); } if (failures) { printf("\n"); printf("Failing tests:\n"); for (i = 0; i < limit; ++i) { if (tests[i].failures) printf(" %d: %s (%d failures)\n", i, tests[i].name, tests[i].failures); } printf("\n"); printf("Details for failing tests: %s\n", tmpdir); printf("\n"); } else { if (verbosity == VERBOSITY_SUMMARY_ONLY) printf("\n"); printf("%d tests passed, no failures\n", tests_run); } free(refdir_alloc); /* If the final tmpdir is empty, we can remove it. */ /* This should be the usual case when all tests succeed. */ assertChdir(".."); rmdir(tmpdir); return (tests_failed ? 1 : 0); } Index: stable/10/lib/libarchive/Makefile =================================================================== --- stable/10/lib/libarchive/Makefile (revision 344673) +++ stable/10/lib/libarchive/Makefile (revision 344674) @@ -1,426 +1,427 @@ # $FreeBSD$ .include LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive LIB= archive DPADD= ${LIBZ} ${LIBBZ2} ${LIBLZMA} ${LIBPTHREAD} ${LIBBSDXML} LDADD= -lz -lbz2 -llzma -lpthread -lbsdxml CFLAGS+= -DHAVE_BZLIB_H=1 -DHAVE_LIBLZMA=1 -DHAVE_LZMA_H=1 # FreeBSD SHLIB_MAJOR value is managed as part of the FreeBSD system. # It has no real relation to the libarchive version number. SHLIB_MAJOR= 6 CFLAGS+= -DPLATFORM_CONFIG_H=\"${.CURDIR}/config_freebsd.h\" CFLAGS+= -I${.OBJDIR} .if ${MK_OPENSSL} != "no" CFLAGS+= -DWITH_OPENSSL DPADD+= ${LIBCRYPTO} LDADD+= -lcrypto .else DPADD+= ${LIBMD} LDADD+= -lmd .endif .if ${MK_ICONV} != "no" # TODO: This can be changed back to CFLAGS once iconv works correctly # with statically linked binaries. SHARED_CFLAGS+= -DHAVE_ICONV=1 -DHAVE_ICONV_H=1 -DICONV_CONST= .endif .if ${MACHINE_ARCH:Marm*} != "" || ${MACHINE_ARCH:Mmips*} != "" || \ ${MACHINE_ARCH:Msparc64*} != "" || ${MACHINE_ARCH:Mia64*} != "" NO_WCAST_ALIGN= yes .if ${MACHINE_ARCH:M*64*} == "" CFLAGS+= -DPPMD_32BIT .endif .endif NO_WCAST_ALIGN.clang= .PATH: ${LIBARCHIVEDIR}/libarchive # Headers to be installed in /usr/include INCS= archive.h archive_entry.h # Sources to be compiled. SRCS= archive_acl.c \ archive_blake2sp_ref.c \ archive_blake2s_ref.c \ archive_check_magic.c \ archive_cmdline.c \ archive_cryptor.c \ archive_disk_acl_freebsd.c \ archive_digest.c \ archive_entry.c \ archive_entry_copy_stat.c \ archive_entry_link_resolver.c \ archive_entry_sparse.c \ archive_entry_stat.c \ archive_entry_strmode.c \ archive_entry_xattr.c \ archive_getdate.c \ archive_hmac.c \ archive_match.c \ archive_options.c \ archive_pack_dev.c \ archive_pathmatch.c \ archive_ppmd7.c \ + archive_ppmd8.c \ archive_random.c \ archive_rb.c \ archive_read.c \ archive_read_add_passphrase.c \ archive_read_append_filter.c \ archive_read_data_into_fd.c \ archive_read_disk_entry_from_file.c \ archive_read_disk_posix.c \ archive_read_disk_set_standard_lookup.c \ archive_read_extract.c \ archive_read_extract2.c \ archive_read_open_fd.c \ archive_read_open_file.c \ archive_read_open_filename.c \ archive_read_open_memory.c \ archive_read_set_format.c \ archive_read_set_options.c \ archive_read_support_filter_all.c \ archive_read_support_filter_bzip2.c \ archive_read_support_filter_compress.c \ archive_read_support_filter_gzip.c \ archive_read_support_filter_grzip.c \ archive_read_support_filter_lrzip.c \ archive_read_support_filter_lz4.c \ archive_read_support_filter_lzop.c \ archive_read_support_filter_none.c \ archive_read_support_filter_program.c \ archive_read_support_filter_rpm.c \ archive_read_support_filter_uu.c \ archive_read_support_filter_xz.c \ archive_read_support_filter_zstd.c \ archive_read_support_format_7zip.c \ archive_read_support_format_all.c \ archive_read_support_format_ar.c \ archive_read_support_format_by_code.c \ archive_read_support_format_cab.c \ archive_read_support_format_cpio.c \ archive_read_support_format_empty.c \ archive_read_support_format_iso9660.c \ archive_read_support_format_lha.c \ archive_read_support_format_mtree.c \ archive_read_support_format_rar.c \ archive_read_support_format_rar5.c \ archive_read_support_format_raw.c \ archive_read_support_format_tar.c \ archive_read_support_format_warc.c \ archive_read_support_format_xar.c \ archive_read_support_format_zip.c \ archive_string.c \ archive_string_sprintf.c \ archive_util.c \ archive_version_details.c \ archive_virtual.c \ archive_write.c \ archive_write_add_filter.c \ archive_write_disk_set_standard_lookup.c \ archive_write_disk_posix.c \ archive_write_open_fd.c \ archive_write_open_file.c \ archive_write_open_filename.c \ archive_write_open_memory.c \ archive_write_add_filter_b64encode.c \ archive_write_add_filter_by_name.c \ archive_write_add_filter_bzip2.c \ archive_write_add_filter_compress.c \ archive_write_add_filter_grzip.c \ archive_write_add_filter_gzip.c \ archive_write_add_filter_lrzip.c \ archive_write_add_filter_lz4.c \ archive_write_add_filter_lzop.c \ archive_write_add_filter_none.c \ archive_write_add_filter_program.c \ archive_write_add_filter_uuencode.c \ archive_write_add_filter_xz.c \ archive_write_add_filter_zstd.c \ archive_write_set_format.c \ archive_write_set_format_7zip.c \ archive_write_set_format_ar.c \ archive_write_set_format_by_name.c \ archive_write_set_format_cpio.c \ archive_write_set_format_cpio_newc.c \ archive_write_set_format_filter_by_ext.c \ archive_write_set_format_gnutar.c \ archive_write_set_format_iso9660.c \ archive_write_set_format_mtree.c \ archive_write_set_format_pax.c \ archive_write_set_format_raw.c \ archive_write_set_format_shar.c \ archive_write_set_format_ustar.c \ archive_write_set_format_v7tar.c \ archive_write_set_format_warc.c \ archive_write_set_format_xar.c \ archive_write_set_format_zip.c \ archive_write_set_passphrase.c \ archive_write_set_options.c \ filter_fork_posix.c # Man pages to be installed. MAN= archive_entry.3 \ archive_entry_acl.3 \ archive_entry_linkify.3 \ archive_entry_paths.3 \ archive_entry_perms.3 \ archive_entry_stat.3 \ archive_entry_time.3 \ archive_read.3 \ archive_read_data.3 \ archive_read_disk.3 \ archive_read_extract.3 \ archive_read_filter.3 \ archive_read_format.3 \ archive_read_free.3 \ archive_read_header.3 \ archive_read_new.3 \ archive_read_open.3 \ archive_read_set_options.3 \ archive_util.3 \ archive_write.3 \ archive_write_blocksize.3 \ archive_write_data.3 \ archive_write_disk.3 \ archive_write_filter.3 \ archive_write_finish_entry.3 \ archive_write_format.3 \ archive_write_free.3 \ archive_write_header.3 \ archive_write_new.3 \ archive_write_open.3 \ archive_write_set_options.3 \ cpio.5 \ libarchive.3 \ libarchive_changes.3 \ libarchive_internals.3 \ libarchive-formats.5 \ tar.5 # Symlink the man pages under each function name. MLINKS+= archive_entry.3 archive_entry_clear.3 MLINKS+= archive_entry.3 archive_entry_clone.3 MLINKS+= archive_entry.3 archive_entry_free.3 MLINKS+= archive_entry.3 archive_entry_new.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_add_entry.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_add_entry_w.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_clear.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_count.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_next.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_next_w.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_reset.3 MLINKS+= archive_entry_acl.3 archive_entry_acl_text_w.3 MLINKS+= archive_entry_linkify.3 archive_entry_linkresolver.3 MLINKS+= archive_entry_linkify.3 archive_entry_linkresolver_new.3 MLINKS+= archive_entry_linkify.3 archive_entry_linkresolver_set_strategy.3 MLINKS+= archive_entry_linkify.3 archive_entry_linkresolver_free.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_hardlink.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_hardlink_w.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_link.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_link_w.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_pathname.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_pathname_w.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_sourcepath.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_symlink.3 MLINKS+= archive_entry_paths.3 archive_entry_copy_symlink_w.3 MLINKS+= archive_entry_paths.3 archive_entry_hardlink.3 MLINKS+= archive_entry_paths.3 archive_entry_hardlink_w.3 MLINKS+= archive_entry_paths.3 archive_entry_pathname.3 MLINKS+= archive_entry_paths.3 archive_entry_pathname_w.3 MLINKS+= archive_entry_paths.3 archive_entry_set_hardlink.3 MLINKS+= archive_entry_paths.3 archive_entry_set_link.3 MLINKS+= archive_entry_paths.3 archive_entry_set_pathname.3 MLINKS+= archive_entry_paths.3 archive_entry_set_symlink.3 MLINKS+= archive_entry_paths.3 archive_entry_symlink.3 MLINKS+= archive_entry_paths.3 archive_entry_symlink_w.3 MLINKS+= archive_entry_paths.3 archive_entry_update_symlink_utf8.3 MLINKS+= archive_entry_paths.3 archive_entry_update_hardlink_utf8.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_fflags_text.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_fflags_text_w.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_gname.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_gname_w.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_uname.3 MLINKS+= archive_entry_perms.3 archive_entry_copy_uname_w.3 MLINKS+= archive_entry_perms.3 archive_entry_fflags.3 MLINKS+= archive_entry_perms.3 archive_entry_fflags_text.3 MLINKS+= archive_entry_perms.3 archive_entry_gid.3 MLINKS+= archive_entry_perms.3 archive_entry_gname.3 MLINKS+= archive_entry_perms.3 archive_entry_gname_w.3 MLINKS+= archive_entry_perms.3 archive_entry_set_fflags.3 MLINKS+= archive_entry_perms.3 archive_entry_set_gid.3 MLINKS+= archive_entry_perms.3 archive_entry_set_gname.3 MLINKS+= archive_entry_perms.3 archive_entry_perm.3 MLINKS+= archive_entry_perms.3 archive_entry_set_perm.3 MLINKS+= archive_entry_perms.3 archive_entry_set_uid.3 MLINKS+= archive_entry_perms.3 archive_entry_set_uname.3 MLINKS+= archive_entry_perms.3 archive_entry_strmode.3 MLINKS+= archive_entry_perms.3 archive_entry_uid.3 MLINKS+= archive_entry_perms.3 archive_entry_uname.3 MLINKS+= archive_entry_perms.3 archive_entry_uname_w.3 MLINKS+= archive_entry_perms.3 archive_entry_update_gname_utf8.3 MLINKS+= archive_entry_perms.3 archive_entry_update_uname_utf8.3 MLINKS+= archive_entry_stat.3 archive_entry_copy_stat.3 MLINKS+= archive_entry_stat.3 archive_entry_dev.3 MLINKS+= archive_entry_stat.3 archive_entry_dev_is_set.3 MLINKS+= archive_entry_stat.3 archive_entry_devmajor.3 MLINKS+= archive_entry_stat.3 archive_entry_devminor.3 MLINKS+= archive_entry_stat.3 archive_entry_filetype.3 MLINKS+= archive_entry_stat.3 archive_entry_ino.3 MLINKS+= archive_entry_stat.3 archive_entry_ino64.3 MLINKS+= archive_entry_stat.3 archive_entry_ino_is_set.3 MLINKS+= archive_entry_stat.3 archive_entry_mode.3 MLINKS+= archive_entry_stat.3 archive_entry_nlink.3 MLINKS+= archive_entry_stat.3 archive_entry_rdev.3 MLINKS+= archive_entry_stat.3 archive_entry_rdevmajor.3 MLINKS+= archive_entry_stat.3 archive_entry_rdevminor.3 MLINKS+= archive_entry_stat.3 archive_entry_set_dev.3 MLINKS+= archive_entry_stat.3 archive_entry_set_devmajor.3 MLINKS+= archive_entry_stat.3 archive_entry_set_devminor.3 MLINKS+= archive_entry_stat.3 archive_entry_set_filetype.3 MLINKS+= archive_entry_stat.3 archive_entry_set_ino.3 MLINKS+= archive_entry_stat.3 archive_entry_set_ino64.3 MLINKS+= archive_entry_stat.3 archive_entry_set_mode.3 MLINKS+= archive_entry_stat.3 archive_entry_set_nlink.3 MLINKS+= archive_entry_stat.3 archive_entry_set_rdev.3 MLINKS+= archive_entry_stat.3 archive_entry_set_rdevmajor.3 MLINKS+= archive_entry_stat.3 archive_entry_set_rdevminor.3 MLINKS+= archive_entry_stat.3 archive_entry_set_size.3 MLINKS+= archive_entry_stat.3 archive_entry_size.3 MLINKS+= archive_entry_stat.3 archive_entry_size_is_set.3 MLINKS+= archive_entry_stat.3 archive_entry_unset_size.3 MLINKS+= archive_entry_time.3 archive_entry_atime.3 MLINKS+= archive_entry_time.3 archive_entry_atime_is_set.3 MLINKS+= archive_entry_time.3 archive_entry_atime_nsec.3 MLINKS+= archive_entry_time.3 archive_entry_birthtime.3 MLINKS+= archive_entry_time.3 archive_entry_birthtime_is_set.3 MLINKS+= archive_entry_time.3 archive_entry_birthtime_nsec.3 MLINKS+= archive_entry_time.3 archive_entry_ctime.3 MLINKS+= archive_entry_time.3 archive_entry_ctime_is_set.3 MLINKS+= archive_entry_time.3 archive_entry_ctime_nsec.3 MLINKS+= archive_entry_time.3 archive_entry_mtime.3 MLINKS+= archive_entry_time.3 archive_entry_mtime_is_set.3 MLINKS+= archive_entry_time.3 archive_entry_mtime_nsec.3 MLINKS+= archive_entry_time.3 archive_entry_set_atime.3 MLINKS+= archive_entry_time.3 archive_entry_set_birthtime.3 MLINKS+= archive_entry_time.3 archive_entry_set_ctime.3 MLINKS+= archive_entry_time.3 archive_entry_set_mtime.3 MLINKS+= archive_entry_time.3 archive_entry_unset_atime.3 MLINKS+= archive_entry_time.3 archive_entry_unset_birthtime.3 MLINKS+= archive_entry_time.3 archive_entry_unset_ctime.3 MLINKS+= archive_entry_time.3 archive_entry_unset_mtime.3 MLINKS+= archive_read_data.3 archive_read_data_block.3 MLINKS+= archive_read_data.3 archive_read_data_into_fd.3 MLINKS+= archive_read_data.3 archive_read_data_skip.3 MLINKS+= archive_read_header.3 archive_read_next_header.3 MLINKS+= archive_read_header.3 archive_read_next_header2.3 MLINKS+= archive_read_extract.3 archive_read_extract2.3 MLINKS+= archive_read_extract.3 archive_read_extract_set_progress_callback.3 MLINKS+= archive_read_extract.3 archive_read_extract_set_skip_file.3 MLINKS+= archive_read_open.3 archive_read_open2.3 MLINKS+= archive_read_open.3 archive_read_open_FILE.3 MLINKS+= archive_read_open.3 archive_read_open_fd.3 MLINKS+= archive_read_open.3 archive_read_open_file.3 MLINKS+= archive_read_open.3 archive_read_open_filename.3 MLINKS+= archive_read_open.3 archive_read_open_memory.3 MLINKS+= archive_read_free.3 archive_read_close.3 MLINKS+= archive_read_free.3 archive_read_finish.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_all.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_bzip2.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_compress.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_gzip.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_lzma.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_none.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_xz.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_program.3 MLINKS+= archive_read_filter.3 archive_read_support_filter_program_signature.3 MLINKS+= archive_read_format.3 archive_read_support_format_7zip.3 MLINKS+= archive_read_format.3 archive_read_support_format_all.3 MLINKS+= archive_read_format.3 archive_read_support_format_ar.3 MLINKS+= archive_read_format.3 archive_read_support_format_by_code.3 MLINKS+= archive_read_format.3 archive_read_support_format_cab.3 MLINKS+= archive_read_format.3 archive_read_support_format_cpio.3 MLINKS+= archive_read_format.3 archive_read_support_format_empty.3 MLINKS+= archive_read_format.3 archive_read_support_format_iso9660.3 MLINKS+= archive_read_format.3 archive_read_support_format_lha.3 MLINKS+= archive_read_format.3 archive_read_support_format_mtree.3 MLINKS+= archive_read_format.3 archive_read_support_format_rar.3 MLINKS+= archive_read_format.3 archive_read_support_format_raw.3 MLINKS+= archive_read_format.3 archive_read_support_format_tar.3 MLINKS+= archive_read_format.3 archive_read_support_format_xar.3 MLINKS+= archive_read_format.3 archive_read_support_format_zip.3 MLINKS+= archive_read_disk.3 archive_read_disk_entry_from_file.3 MLINKS+= archive_read_disk.3 archive_read_disk_gname.3 MLINKS+= archive_read_disk.3 archive_read_disk_new.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_gname_lookup.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_standard_lookup.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_symlink_hybrid.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_symlink_logical.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_symlink_physical.3 MLINKS+= archive_read_disk.3 archive_read_disk_set_uname_lookup.3 MLINKS+= archive_read_disk.3 archive_read_disk_uname.3 MLINKS+= archive_read_set_options.3 archive_read_set_filter_option.3 MLINKS+= archive_read_set_options.3 archive_read_set_format_option.3 MLINKS+= archive_read_set_options.3 archive_read_set_option.3 MLINKS+= archive_util.3 archive_clear_error.3 MLINKS+= archive_util.3 archive_compression.3 MLINKS+= archive_util.3 archive_compression_name.3 MLINKS+= archive_util.3 archive_copy_error.3 MLINKS+= archive_util.3 archive_errno.3 MLINKS+= archive_util.3 archive_error_string.3 MLINKS+= archive_util.3 archive_file_count.3 MLINKS+= archive_util.3 archive_filter_code.3 MLINKS+= archive_util.3 archive_filter_count.3 MLINKS+= archive_util.3 archive_filter_name.3 MLINKS+= archive_util.3 archive_format.3 MLINKS+= archive_util.3 archive_format_name.3 MLINKS+= archive_util.3 archive_position.3 MLINKS+= archive_util.3 archive_set_error.3 MLINKS+= archive_write_blocksize.3 archive_write_get_bytes_in_last_block.3 MLINKS+= archive_write_blocksize.3 archive_write_get_bytes_per_block.3 MLINKS+= archive_write_blocksize.3 archive_write_set_bytes_in_last_block.3 MLINKS+= archive_write_blocksize.3 archive_write_set_bytes_per_block.3 MLINKS+= archive_write_disk.3 archive_write_data_block.3 MLINKS+= archive_write_disk.3 archive_write_disk_new.3 MLINKS+= archive_write_disk.3 archive_write_disk_set_group_lookup.3 MLINKS+= archive_write_disk.3 archive_write_disk_set_options.3 MLINKS+= archive_write_disk.3 archive_write_disk_set_skip_file.3 MLINKS+= archive_write_disk.3 archive_write_disk_set_standard_lookup.3 MLINKS+= archive_write_disk.3 archive_write_disk_set_user_lookup.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_bzip2.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_compress.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_gzip.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_lzip.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_lzma.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_none.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_program.3 MLINKS+= archive_write_filter.3 archive_write_add_filter_xz.3 MLINKS+= archive_write_format.3 archive_write_set_format_cpio.3 MLINKS+= archive_write_format.3 archive_write_set_format_pax.3 MLINKS+= archive_write_format.3 archive_write_set_format_pax_restricted.3 MLINKS+= archive_write_format.3 archive_write_set_format_shar.3 MLINKS+= archive_write_format.3 archive_write_set_format_shar_dump.3 MLINKS+= archive_write_format.3 archive_write_set_format_ustar.3 MLINKS+= archive_write_free.3 archive_write_close.3 MLINKS+= archive_write_free.3 archive_write_fail.3 MLINKS+= archive_write_free.3 archive_write_finish.3 MLINKS+= archive_write_open.3 archive_write_open_FILE.3 MLINKS+= archive_write_open.3 archive_write_open_fd.3 MLINKS+= archive_write_open.3 archive_write_open_file.3 MLINKS+= archive_write_open.3 archive_write_open_filename.3 MLINKS+= archive_write_open.3 archive_write_open_memory.3 MLINKS+= archive_write_set_options.3 archive_write_set_filter_option.3 MLINKS+= archive_write_set_options.3 archive_write_set_format_option.3 MLINKS+= archive_write_set_options.3 archive_write_set_option.3 MLINKS+= libarchive.3 archive.3 .if ${MK_TESTS} != "no" SUBDIR+= tests .endif .include Index: stable/10/lib/libarchive/tests/Makefile =================================================================== --- stable/10/lib/libarchive/tests/Makefile (revision 344673) +++ stable/10/lib/libarchive/tests/Makefile (revision 344674) @@ -1,597 +1,603 @@ # $FreeBSD$ SRCTOP= ${.CURDIR:H:H:H} TESTSDIR= ${TESTSBASE}/lib/libarchive LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive ATF_TESTS_SH+= functional_test BINDIR= ${TESTSDIR} PROGS+= libarchive_test CFLAGS+= -I${.CURDIR} -I${.CURDIR:H} -I${.OBJDIR} CFLAGS+= -I${LIBARCHIVEDIR}/libarchive -I${LIBARCHIVEDIR}/libarchive/test CFLAGS+= -I${LIBARCHIVEDIR}/test_utils CFLAGS+= -DHAVE_LIBLZMA=1 -DHAVE_LZMA_H=1 # Uncomment to link against dmalloc #LDADD+= -L/usr/local/lib -ldmalloc #CFLAGS+= -I/usr/local/include -DUSE_DMALLOC .PATH: ${LIBARCHIVEDIR}/libarchive/test TESTS_SRCS= \ test_acl_nfs4.c \ test_acl_pax.c \ test_acl_platform_nfs4.c \ test_acl_platform_posix1e.c \ test_acl_posix1e.c \ test_acl_text.c \ test_archive_api_feature.c \ test_archive_clear_error.c \ test_archive_cmdline.c \ test_archive_digest.c \ test_archive_getdate.c \ test_archive_match_time.c \ test_archive_match_owner.c \ test_archive_match_path.c \ test_archive_pathmatch.c \ test_archive_read_add_passphrase.c \ test_archive_read_close_twice.c \ test_archive_read_close_twice_open_fd.c \ test_archive_read_close_twice_open_filename.c \ test_archive_read_multiple_data_objects.c \ test_archive_read_next_header_empty.c \ test_archive_read_next_header_raw.c \ test_archive_read_open2.c \ test_archive_read_set_filter_option.c \ test_archive_read_set_format_option.c \ test_archive_read_set_option.c \ test_archive_read_set_options.c \ test_archive_read_support.c \ test_archive_set_error.c \ test_archive_string.c \ test_archive_string_conversion.c \ test_archive_write_add_filter_by_name.c \ test_archive_write_set_filter_option.c \ test_archive_write_set_format_by_name.c \ test_archive_write_set_format_filter_by_ext.c \ test_archive_write_set_format_option.c \ test_archive_write_set_option.c \ test_archive_write_set_options.c \ test_archive_write_set_passphrase.c \ test_bad_fd.c \ test_compat_bzip2.c \ test_compat_cpio.c \ test_compat_gtar.c \ test_compat_gzip.c \ test_compat_lz4.c \ test_compat_lzip.c \ test_compat_lzma.c \ test_compat_lzop.c \ test_compat_mac.c \ test_compat_pax_libarchive_2x.c \ test_compat_perl_archive_tar.c \ test_compat_plexus_archiver_tar.c \ test_compat_solaris_tar_acl.c \ test_compat_solaris_pax_sparse.c \ test_compat_star_acl.c \ test_compat_tar_hardlink.c \ test_compat_uudecode.c \ test_compat_uudecode_large.c \ test_compat_xz.c \ test_compat_zip.c \ test_compat_zstd.c \ test_empty_write.c \ test_entry.c \ test_entry_strmode.c \ test_extattr_freebsd.c \ test_filter_count.c \ test_fuzz.c \ test_gnutar_filename_encoding.c \ test_link_resolver.c \ test_open_fd.c \ test_open_failure.c \ test_open_file.c \ test_open_filename.c \ test_pax_filename_encoding.c \ test_read_data_large.c \ test_read_disk.c \ test_read_disk_directory_traversals.c \ test_read_disk_entry_from_file.c \ test_read_extract.c \ test_read_file_nonexistent.c \ test_read_filter_compress.c \ test_read_filter_grzip.c \ test_read_filter_lrzip.c \ test_read_filter_lzop.c \ test_read_filter_lzop_multiple_parts.c \ test_read_filter_program.c \ test_read_filter_program_signature.c \ test_read_filter_uudecode.c \ test_read_format_7zip.c \ test_read_format_7zip_encryption_data.c \ test_read_format_7zip_encryption_header.c \ test_read_format_7zip_encryption_partially.c \ test_read_format_7zip_malformed.c \ test_read_format_ar.c \ test_read_format_cab.c \ test_read_format_cab_filename.c \ test_read_format_cpio_afio.c \ test_read_format_cpio_bin.c \ test_read_format_cpio_bin_Z.c \ test_read_format_cpio_bin_be.c \ test_read_format_cpio_bin_bz2.c \ test_read_format_cpio_bin_gz.c \ test_read_format_cpio_bin_le.c \ test_read_format_cpio_bin_lzip.c \ test_read_format_cpio_bin_lzma.c \ test_read_format_cpio_bin_xz.c \ test_read_format_cpio_filename.c \ test_read_format_cpio_odc.c \ test_read_format_cpio_svr4_gzip.c \ test_read_format_cpio_svr4c_Z.c \ test_read_format_cpio_svr4_bzip2_rpm.c \ test_read_format_cpio_svr4_gzip_rpm.c \ test_read_format_empty.c \ test_read_format_gtar_filename.c \ test_read_format_gtar_gz.c \ test_read_format_gtar_lzma.c \ test_read_format_gtar_sparse.c \ test_read_format_gtar_sparse_skip_entry.c \ test_read_format_iso_Z.c \ test_read_format_iso_multi_extent.c \ test_read_format_iso_xorriso.c \ test_read_format_isorr_rr_moved.c \ test_read_format_isojoliet_bz2.c \ test_read_format_isojoliet_long.c \ test_read_format_isojoliet_rr.c \ test_read_format_isojoliet_versioned.c \ test_read_format_isorr_bz2.c \ test_read_format_isorr_ce.c \ test_read_format_isorr_new_bz2.c \ test_read_format_isozisofs_bz2.c \ test_read_format_lha.c \ test_read_format_lha_bugfix_0.c \ test_read_format_lha_filename.c \ test_read_format_mtree.c \ test_read_format_mtree_crash747.c \ test_read_format_pax_bz2.c \ test_read_format_rar.c \ test_read_format_rar5.c \ test_read_format_rar_encryption_data.c \ test_read_format_rar_encryption_header.c \ test_read_format_rar_encryption_partially.c \ test_read_format_rar_invalid1.c \ test_read_format_raw.c \ test_read_format_tar.c \ test_read_format_tar_concatenated.c \ test_read_format_tar_empty_filename.c \ test_read_format_tar_empty_pax.c \ test_read_format_tar_filename.c \ test_read_format_tbz.c \ test_read_format_tgz.c \ test_read_format_tlz.c \ test_read_format_txz.c \ test_read_format_tz.c \ test_read_format_ustar_filename.c \ test_read_format_warc.c \ test_read_format_xar.c \ test_read_format_zip.c \ test_read_format_zip_comment_stored.c \ test_read_format_zip_encryption_data.c \ test_read_format_zip_encryption_header.c \ test_read_format_zip_encryption_partially.c \ test_read_format_zip_filename.c \ test_read_format_zip_high_compression.c \ test_read_format_zip_jar.c \ test_read_format_zip_mac_metadata.c \ test_read_format_zip_malformed.c \ test_read_format_zip_msdos.c \ test_read_format_zip_nested.c \ test_read_format_zip_nofiletype.c \ test_read_format_zip_padded.c \ test_read_format_zip_sfx.c \ test_read_format_zip_traditional_encryption_data.c \ test_read_format_zip_winzip_aes.c \ test_read_format_zip_winzip_aes_large.c \ test_read_format_zip_with_invalid_traditional_eocd.c \ test_read_format_zip_zip64.c \ test_read_large.c \ test_read_pax_truncated.c \ test_read_position.c \ test_read_set_format.c \ test_read_too_many_filters.c \ test_read_truncated.c \ test_read_truncated_filter.c \ test_sparse_basic.c \ test_tar_filenames.c \ test_tar_large.c \ test_warn_missing_hardlink_target.c \ test_ustar_filenames.c \ test_ustar_filename_encoding.c \ test_write_disk.c \ test_write_disk_appledouble.c \ test_write_disk_failures.c \ test_write_disk_hardlink.c \ test_write_disk_hfs_compression.c \ test_write_disk_lookup.c \ test_write_disk_mac_metadata.c \ test_write_disk_no_hfs_compression.c \ test_write_disk_perms.c \ test_write_disk_secure.c \ test_write_disk_secure744.c \ test_write_disk_secure745.c \ test_write_disk_secure746.c \ test_write_disk_sparse.c \ test_write_disk_symlink.c \ test_write_disk_times.c \ test_write_filter_b64encode.c \ test_write_filter_bzip2.c \ test_write_filter_compress.c \ test_write_filter_gzip.c \ test_write_filter_gzip_timestamp.c \ test_write_filter_lrzip.c \ test_write_filter_lz4.c \ test_write_filter_lzip.c \ test_write_filter_lzma.c \ test_write_filter_lzop.c \ test_write_filter_program.c \ test_write_filter_uuencode.c \ test_write_filter_xz.c \ test_write_filter_zstd.c \ test_write_format_7zip.c \ test_write_format_7zip_empty.c \ test_write_format_7zip_large.c \ test_write_format_ar.c \ test_write_format_cpio.c \ test_write_format_cpio_empty.c \ test_write_format_cpio_newc.c \ test_write_format_cpio_odc.c \ test_write_format_gnutar.c \ test_write_format_gnutar_filenames.c \ test_write_format_iso9660.c \ test_write_format_iso9660_boot.c \ test_write_format_iso9660_empty.c \ test_write_format_iso9660_filename.c \ test_write_format_iso9660_zisofs.c \ test_write_format_mtree.c \ test_write_format_mtree_absolute_path.c \ test_write_format_mtree_classic.c \ test_write_format_mtree_classic_indent.c \ test_write_format_mtree_fflags.c \ test_write_format_mtree_no_separator.c \ test_write_format_mtree_quoted_filename.c \ test_write_format_pax.c \ test_write_format_raw.c \ test_write_format_raw_b64.c \ test_write_format_shar_empty.c \ test_write_format_tar.c \ test_write_format_tar_empty.c \ test_write_format_tar_sparse.c \ test_write_format_tar_ustar.c \ test_write_format_tar_v7tar.c \ test_write_format_warc.c \ test_write_format_warc_empty.c \ test_write_format_xar.c \ test_write_format_xar_empty.c \ test_write_format_zip.c \ test_write_format_zip_compression_store.c \ test_write_format_zip_empty.c \ test_write_format_zip_empty_zip64.c \ test_write_format_zip_file.c \ test_write_format_zip_file_zip64.c \ test_write_format_zip_large.c \ test_write_format_zip_zip64.c \ test_write_open_memory.c \ test_write_read_format_zip.c \ test_xattr_platform.c \ test_zip_filename_encoding.c # Deterministic failures: # Crashes with SIGBUS BROKEN_TESTS+= test_archive_rmd160 # Fails with `libarchive/test/test_archive_crypto.c:121: md != actualmd` BROKEN_TESTS+= test_archive_sha384 # Fails with `test_compat_pax_libarchive_2x.c:122: ARCHIVE_WARN != archive_read_next_header(a, &ae)` BROKEN_TESTS+= test_compat_pax_libarchive_2x # Fails with `test_read_disk_directory_traversals.c:1094: File at has atime 886622, 1443306049 seconds ago` BROKEN_TESTS+= test_read_disk_directory_traversals # Non-deterministic failures: # (Times out?) [and] crashes BROKEN_TESTS+= test_fuzz_rar # Build the test program. SRCS.libarchive_test= \ ${TESTS_SRCS} \ read_open_memory.c \ list.h DPADD.libarchive_test= ${LIBARCHIVE} LDADD.libarchive_test= -larchive .PATH: ${LIBARCHIVEDIR}/test_utils SRCS.libarchive_test+= test_main.c \ test_utils.c # list.h is just a list of all tests, as indicated by DEFINE_TEST macro lines list.h: ${TESTS_SRCS} Makefile @(cd ${LIBARCHIVEDIR}/libarchive/test && \ grep -E -h ^DEFINE_TEST ${.ALLSRC:N*Makefile} | \ egrep -v '${BROKEN_TESTS:tW:C/ /|/g}') > ${.TARGET}.tmp @mv ${.TARGET}.tmp ${.TARGET} CLEANFILES+= list.h list.h.tmp FILES+= README FILES+= test_acl_pax_posix1e.tar.uu FILES+= test_acl_pax_nfs4.tar.uu FILES+= test_archive_string_conversion.txt.Z.uu FILES+= test_compat_bzip2_1.tbz.uu FILES+= test_compat_bzip2_2.tbz.uu FILES+= test_compat_cpio_1.cpio.uu FILES+= test_compat_gtar_1.tar.uu FILES+= test_compat_gtar_2.tar.uu FILES+= test_compat_gzip_1.tgz.uu FILES+= test_compat_gzip_2.tgz.uu FILES+= test_compat_lz4_1.tar.lz4.uu FILES+= test_compat_lz4_2.tar.lz4.uu FILES+= test_compat_lz4_3.tar.lz4.uu FILES+= test_compat_lz4_B4.tar.lz4.uu FILES+= test_compat_lz4_B4BD.tar.lz4.uu FILES+= test_compat_lz4_B4BDBX.tar.lz4.uu FILES+= test_compat_lz4_B5.tar.lz4.uu FILES+= test_compat_lz4_B5BD.tar.lz4.uu FILES+= test_compat_lz4_B6.tar.lz4.uu FILES+= test_compat_lz4_B6BD.tar.lz4.uu FILES+= test_compat_lz4_B7.tar.lz4.uu FILES+= test_compat_lz4_B7BD.tar.lz4.uu FILES+= test_compat_lzip_1.tlz.uu FILES+= test_compat_lzip_2.tlz.uu FILES+= test_compat_lzma_1.tlz.uu FILES+= test_compat_lzma_2.tlz.uu FILES+= test_compat_lzma_3.tlz.uu FILES+= test_compat_lzop_1.tar.lzo.uu FILES+= test_compat_lzop_2.tar.lzo.uu FILES+= test_compat_lzop_3.tar.lzo.uu FILES+= test_compat_mac-1.tar.Z.uu FILES+= test_compat_mac-2.tar.Z.uu -FILES+= test_compat_pax_libarchive_2x.tar.Z.uu FILES+= test_compat_perl_archive_tar.tar.uu FILES+= test_compat_plexus_archiver_tar.tar.uu FILES+= test_compat_solaris_pax_sparse_1.pax.Z.uu FILES+= test_compat_solaris_pax_sparse_2.pax.Z.uu FILES+= test_compat_solaris_tar_acl.tar.uu FILES+= test_compat_star_acl_nfs4.tar.uu FILES+= test_compat_star_acl_posix1e.tar.uu FILES+= test_compat_tar_hardlink_1.tar.uu FILES+= test_compat_uudecode_large.tar.Z.uu FILES+= test_compat_xz_1.txz.uu FILES+= test_compat_zip_1.zip.uu FILES+= test_compat_zip_2.zip.uu FILES+= test_compat_zip_3.zip.uu FILES+= test_compat_zip_4.zip.uu FILES+= test_compat_zip_5.zip.uu FILES+= test_compat_zip_6.zip.uu FILES+= test_compat_zip_7.xps.uu FILES+= test_compat_zip_8.zip.uu FILES+= test_compat_zstd_1.tar.zst.uu FILES+= test_fuzz.cab.uu FILES+= test_fuzz.lzh.uu FILES+= test_fuzz_1.iso.Z.uu FILES+= test_pax_filename_encoding.tar.uu FILES+= test_rar_multivolume_multiple_files.part1.rar.uu FILES+= test_rar_multivolume_multiple_files.part2.rar.uu FILES+= test_rar_multivolume_multiple_files.part3.rar.uu FILES+= test_rar_multivolume_multiple_files.part4.rar.uu FILES+= test_rar_multivolume_multiple_files.part5.rar.uu FILES+= test_rar_multivolume_multiple_files.part6.rar.uu FILES+= test_rar_multivolume_single_file.part1.rar.uu FILES+= test_rar_multivolume_single_file.part2.rar.uu FILES+= test_rar_multivolume_single_file.part3.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part01.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part02.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part03.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part04.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part05.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part06.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part07.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part08.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part09.rar.uu FILES+= test_rar_multivolume_uncompressed_files.part10.rar.uu FILES+= test_read_filter_grzip.tar.grz.uu FILES+= test_read_filter_lrzip.tar.lrz.uu FILES+= test_read_filter_lzop.tar.lzo.uu FILES+= test_read_filter_lzop_multiple_parts.tar.lzo.uu FILES+= test_read_format_7zip_bcj2_bzip2.7z.uu FILES+= test_read_format_7zip_bcj2_copy_1.7z.uu FILES+= test_read_format_7zip_bcj2_copy_2.7z.uu FILES+= test_read_format_7zip_bcj2_copy_lzma.7z.uu FILES+= test_read_format_7zip_bcj2_deflate.7z.uu FILES+= test_read_format_7zip_bcj2_lzma1_1.7z.uu FILES+= test_read_format_7zip_bcj2_lzma1_2.7z.uu FILES+= test_read_format_7zip_bcj2_lzma2_1.7z.uu FILES+= test_read_format_7zip_bcj2_lzma2_2.7z.uu FILES+= test_read_format_7zip_bcj_bzip2.7z.uu FILES+= test_read_format_7zip_bcj_copy.7z.uu FILES+= test_read_format_7zip_bcj_deflate.7z.uu FILES+= test_read_format_7zip_bcj_lzma1.7z.uu FILES+= test_read_format_7zip_bcj_lzma2.7z.uu FILES+= test_read_format_7zip_bzip2.7z.uu FILES+= test_read_format_7zip_copy.7z.uu FILES+= test_read_format_7zip_copy_2.7z.uu FILES+= test_read_format_7zip_deflate.7z.uu FILES+= test_read_format_7zip_delta_lzma1.7z.uu FILES+= test_read_format_7zip_delta_lzma2.7z.uu FILES+= test_read_format_7zip_empty_archive.7z.uu FILES+= test_read_format_7zip_empty_file.7z.uu FILES+= test_read_format_7zip_encryption.7z.uu FILES+= test_read_format_7zip_encryption_header.7z.uu FILES+= test_read_format_7zip_encryption_partially.7z.uu FILES+= test_read_format_7zip_lzma1.7z.uu FILES+= test_read_format_7zip_lzma1_2.7z.uu FILES+= test_read_format_7zip_lzma1_lzma2.7z.uu FILES+= test_read_format_7zip_lzma2.7z.uu FILES+= test_read_format_7zip_malformed.7z.uu FILES+= test_read_format_7zip_malformed2.7z.uu FILES+= test_read_format_7zip_ppmd.7z.uu FILES+= test_read_format_7zip_symbolic_name.7z.uu FILES+= test_read_format_ar.ar.uu FILES+= test_read_format_cab_1.cab.uu FILES+= test_read_format_cab_2.cab.uu FILES+= test_read_format_cab_3.cab.uu FILES+= test_read_format_cab_filename_cp932.cab.uu FILES+= test_read_format_cpio_bin_be.cpio.uu FILES+= test_read_format_cpio_bin_le.cpio.uu FILES+= test_read_format_cpio_filename_cp866.cpio.uu FILES+= test_read_format_cpio_filename_eucjp.cpio.uu FILES+= test_read_format_cpio_filename_koi8r.cpio.uu FILES+= test_read_format_cpio_filename_utf8_jp.cpio.uu FILES+= test_read_format_cpio_filename_utf8_ru.cpio.uu FILES+= test_read_format_cpio_svr4_bzip2_rpm.rpm.uu FILES+= test_read_format_cpio_svr4_gzip_rpm.rpm.uu FILES+= test_read_format_gtar_filename_cp866.tar.Z.uu FILES+= test_read_format_gtar_filename_eucjp.tar.Z.uu FILES+= test_read_format_gtar_filename_koi8r.tar.Z.uu FILES+= test_read_format_gtar_sparse_1_13.tar.uu FILES+= test_read_format_gtar_sparse_1_17.tar.uu FILES+= test_read_format_gtar_sparse_1_17_posix00.tar.uu FILES+= test_read_format_gtar_sparse_1_17_posix01.tar.uu FILES+= test_read_format_gtar_sparse_1_17_posix10.tar.uu FILES+= test_read_format_gtar_sparse_1_17_posix10_modified.tar.uu FILES+= test_read_format_gtar_sparse_skip_entry.tar.Z.uu FILES+= test_read_format_iso.iso.Z.uu FILES+= test_read_format_iso_2.iso.Z.uu FILES+= test_read_format_iso_joliet.iso.Z.uu FILES+= test_read_format_iso_joliet_by_nero.iso.Z.uu FILES+= test_read_format_iso_joliet_long.iso.Z.uu FILES+= test_read_format_iso_joliet_rockridge.iso.Z.uu FILES+= test_read_format_iso_multi_extent.iso.Z.uu FILES+= test_read_format_iso_rockridge.iso.Z.uu FILES+= test_read_format_iso_rockridge_ce.iso.Z.uu FILES+= test_read_format_iso_rockridge_new.iso.Z.uu FILES+= test_read_format_iso_rockridge_rr_moved.iso.Z.uu FILES+= test_read_format_iso_xorriso.iso.Z.uu FILES+= test_read_format_iso_zisofs.iso.Z.uu FILES+= test_read_format_lha_bugfix_0.lzh.uu FILES+= test_read_format_lha_filename_cp932.lzh.uu FILES+= test_read_format_lha_header0.lzh.uu FILES+= test_read_format_lha_header1.lzh.uu FILES+= test_read_format_lha_header2.lzh.uu FILES+= test_read_format_lha_header3.lzh.uu FILES+= test_read_format_lha_lh0.lzh.uu FILES+= test_read_format_lha_lh6.lzh.uu FILES+= test_read_format_lha_lh7.lzh.uu FILES+= test_read_format_lha_withjunk.lzh.uu FILES+= test_read_format_mtree.mtree.uu FILES+= test_read_format_mtree_crash747.mtree.bz2.uu FILES+= test_read_format_mtree_nomagic.mtree.uu FILES+= test_read_format_mtree_nomagic2.mtree.uu FILES+= test_read_format_mtree_nomagic3.mtree.uu FILES+= test_read_format_rar.rar.uu FILES+= test_read_format_rar_binary_data.rar.uu FILES+= test_read_format_rar_compress_best.rar.uu FILES+= test_read_format_rar_compress_normal.rar.uu FILES+= test_read_format_rar_encryption_data.rar.uu FILES+= test_read_format_rar_encryption_header.rar.uu FILES+= test_read_format_rar_encryption_partially.rar.uu FILES+= test_read_format_rar_invalid1.rar.uu FILES+= test_read_format_rar_multi_lzss_blocks.rar.uu FILES+= test_read_format_rar_multivolume.part0001.rar.uu FILES+= test_read_format_rar_multivolume.part0002.rar.uu FILES+= test_read_format_rar_multivolume.part0003.rar.uu FILES+= test_read_format_rar_multivolume.part0004.rar.uu FILES+= test_read_format_rar_noeof.rar.uu FILES+= test_read_format_rar_ppmd_lzss_conversion.rar.uu FILES+= test_read_format_rar_sfx.exe.uu FILES+= test_read_format_rar_subblock.rar.uu FILES+= test_read_format_rar_unicode.rar.uu FILES+= test_read_format_rar_windows.rar.uu FILES+= test_read_format_rar5_arm.rar.uu FILES+= test_read_format_rar5_blake2.rar.uu FILES+= test_read_format_rar5_compressed.rar.uu FILES+= test_read_format_rar5_multiarchive.part01.rar.uu FILES+= test_read_format_rar5_multiarchive.part02.rar.uu FILES+= test_read_format_rar5_multiarchive.part03.rar.uu FILES+= test_read_format_rar5_multiarchive.part04.rar.uu FILES+= test_read_format_rar5_multiarchive.part05.rar.uu FILES+= test_read_format_rar5_multiarchive.part06.rar.uu FILES+= test_read_format_rar5_multiarchive.part07.rar.uu FILES+= test_read_format_rar5_multiarchive.part08.rar.uu FILES+= test_read_format_rar5_multiarchive_solid.part01.rar.uu FILES+= test_read_format_rar5_multiarchive_solid.part02.rar.uu FILES+= test_read_format_rar5_multiarchive_solid.part03.rar.uu FILES+= test_read_format_rar5_multiarchive_solid.part04.rar.uu FILES+= test_read_format_rar5_multiple_files.rar.uu FILES+= test_read_format_rar5_multiple_files_solid.rar.uu FILES+= test_read_format_rar5_solid.rar.uu FILES+= test_read_format_rar5_stored.rar.uu FILES+= test_read_format_rar5_stored_manyfiles.rar.uu FILES+= test_read_format_rar5_win32.rar.uu FILES+= test_read_format_raw.bufr.uu FILES+= test_read_format_raw.data.Z.uu FILES+= test_read_format_raw.data.uu FILES+= test_read_format_tar_concatenated.tar.uu FILES+= test_read_format_tar_empty_filename.tar.uu FILES+= test_read_format_tar_empty_pax.tar.Z.uu FILES+= test_read_format_tar_filename_koi8r.tar.Z.uu FILES+= test_read_format_ustar_filename_cp866.tar.Z.uu FILES+= test_read_format_ustar_filename_eucjp.tar.Z.uu FILES+= test_read_format_ustar_filename_koi8r.tar.Z.uu FILES+= test_read_format_warc.warc.uu FILES+= test_read_format_zip.zip.uu +FILES+= test_read_format_zip_bzip2.zipx.uu +FILES+= test_read_format_zip_bzip2_multi.zipx.uu FILES+= test_read_format_zip_comment_stored_1.zip.uu FILES+= test_read_format_zip_comment_stored_2.zip.uu FILES+= test_read_format_zip_encryption_data.zip.uu FILES+= test_read_format_zip_encryption_header.zip.uu FILES+= test_read_format_zip_encryption_partially.zip.uu FILES+= test_read_format_zip_filename_cp866.zip.uu FILES+= test_read_format_zip_filename_cp932.zip.uu FILES+= test_read_format_zip_filename_koi8r.zip.uu FILES+= test_read_format_zip_filename_utf8_jp.zip.uu FILES+= test_read_format_zip_filename_utf8_ru.zip.uu FILES+= test_read_format_zip_filename_utf8_ru2.zip.uu FILES+= test_read_format_zip_high_compression.zip.uu FILES+= test_read_format_zip_jar.jar.uu FILES+= test_read_format_zip_length_at_end.zip.uu +FILES+= test_read_format_zip_lzma.zipx.uu +FILES+= test_read_format_zip_lzma_multi.zipx.uu FILES+= test_read_format_zip_mac_metadata.zip.uu FILES+= test_read_format_zip_malformed1.zip.uu FILES+= test_read_format_zip_msdos.zip.uu FILES+= test_read_format_zip_nested.zip.uu FILES+= test_read_format_zip_nofiletype.zip.uu FILES+= test_read_format_zip_padded1.zip.uu FILES+= test_read_format_zip_padded2.zip.uu FILES+= test_read_format_zip_padded3.zip.uu +FILES+= test_read_format_zip_ppmd8.zipx.uu +FILES+= test_read_format_zip_ppmd8_multi.zipx.uu FILES+= test_read_format_zip_sfx.uu FILES+= test_read_format_zip_symlink.zip.uu FILES+= test_read_format_zip_traditional_encryption_data.zip.uu FILES+= test_read_format_zip_ux.zip.uu FILES+= test_read_format_zip_with_invalid_traditional_eocd.zip.uu FILES+= test_read_format_zip_winzip_aes128.zip.uu FILES+= test_read_format_zip_winzip_aes256.zip.uu FILES+= test_read_format_zip_winzip_aes256_large.zip.uu FILES+= test_read_format_zip_winzip_aes256_stored.zip.uu +FILES+= test_read_format_zip_xz_multi.zipx.uu FILES+= test_read_format_zip_zip64a.zip.uu FILES+= test_read_format_zip_zip64b.zip.uu FILES+= test_read_large_splitted_rar_aa.uu FILES+= test_read_large_splitted_rar_ab.uu FILES+= test_read_large_splitted_rar_ac.uu FILES+= test_read_large_splitted_rar_ad.uu FILES+= test_read_large_splitted_rar_ae.uu FILES+= test_read_pax_schily_xattr.tar.uu FILES+= test_read_splitted_rar_aa.uu FILES+= test_read_splitted_rar_ab.uu FILES+= test_read_splitted_rar_ac.uu FILES+= test_read_splitted_rar_ad.uu FILES+= test_read_too_many_filters.gz.uu FILES+= test_splitted_rar_seek_support_aa.uu FILES+= test_splitted_rar_seek_support_ab.uu FILES+= test_splitted_rar_seek_support_ac.uu FILES+= test_write_disk_appledouble.cpio.gz.uu FILES+= test_write_disk_hfs_compression.tgz.uu FILES+= test_write_disk_mac_metadata.tar.gz.uu FILES+= test_write_disk_no_hfs_compression.tgz.uu .include Index: stable/10 =================================================================== --- stable/10 (revision 344673) +++ stable/10 (revision 344674) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r344065,344089