Index: head/contrib/libarchive/FREEBSD-Xlist =================================================================== --- head/contrib/libarchive/FREEBSD-Xlist (revision 316094) +++ head/contrib/libarchive/FREEBSD-Xlist (revision 316095) @@ -1,38 +1,41 @@ .git .gitattributes .gitignore .travis.yml CMakeLists.txt CONTRIBUTING.md CTestConfig.cmake INSTALL Makefile.am build cat/CMakeLists.txt cat/test/CMakeLists.txt configure.ac contrib cpio/CMakeLists.txt cpio/cpio_windows.c cpio/cpio_windows.h cpio/config_freebsd.h cpio/test/CMakeLists.txt doc examples libarchive/CMakeLists.txt libarchive/archive_entry_copy_bhfi.c +libarchive/archive_disk_acl_darwin.c +libarchive/archive_disk_acl_linux.c +libarchive/archive_disk_acl_sunos.c libarchive/archive_read_disk_windows.c libarchive/archive_windows.c libarchive/archive_windows.h libarchive/archive_write_disk_windows.c libarchive/config_freebsd.h libarchive/filter_fork_windows.c libarchive/mtree.5 libarchive/test/.cvsignore libarchive/test/CMakeLists.txt tar/CMakeLists.txt tar/bsdtar_windows.c tar/bsdtar_windows.h tar/config_freebsd.h tar/test/CMakeLists.txt tar/test/test_windows.c Index: head/contrib/libarchive/libarchive/archive_read_disk_acl_freebsd.c =================================================================== --- head/contrib/libarchive/libarchive/archive_read_disk_acl_freebsd.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_read_disk_acl_freebsd.c (nonexistent) @@ -1,371 +0,0 @@ -/*- - * Copyright (c) 2003-2009 Tim Kientzle - * Copyright (c) 2010-2012 Michihiro NAKAJIMA - * Copyright (c) 2016-2017 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" - -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_FCNTL_H -#include -#endif -#ifdef HAVE_SYS_TYPES_H -#include -#endif -#ifdef HAVE_SYS_ACL_H -#define _ACL_PRIVATE /* For debugging */ -#include -#endif - -#include "archive_entry.h" -#include "archive_private.h" -#include "archive_read_disk_private.h" -#include "archive_acl_maps.h" - -/* - * Translate FreeBSD ACLs into libarchive internal structure - */ -static int -translate_acl(struct archive_read_disk *a, - struct archive_entry *entry, acl_t acl, int default_entry_acl_type) -{ -#if ARCHIVE_ACL_FREEBSD_NFS4 - int brand; - acl_flagset_t acl_flagset; -#endif - acl_tag_t acl_tag; - acl_entry_t acl_entry; - acl_permset_t acl_permset; - acl_entry_type_t acl_type; - int i, entry_acl_type, perm_map_size; - const acl_perm_map_t *perm_map; - int r, s, ae_id, ae_tag, ae_perm; - void *q; - const char *ae_name; - -#if ARCHIVE_ACL_FREEBSD_NFS4 - // FreeBSD "brands" ACLs as POSIX.1e or NFSv4 - // Make sure the "brand" on this ACL is consistent - // with the default_entry_acl_type bits provided. - if (acl_get_brand_np(acl, &brand) != 0) { - archive_set_error(&a->archive, errno, - "Failed to read ACL brand"); - return (ARCHIVE_WARN); - } - switch (brand) { - case ACL_BRAND_POSIX: - switch (default_entry_acl_type) { - case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: - case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: - break; - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Invalid ACL entry type for POSIX.1e ACL"); - return (ARCHIVE_WARN); - } - break; - case ACL_BRAND_NFS4: - if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Invalid ACL entry type for NFSv4 ACL"); - return (ARCHIVE_WARN); - } - break; - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Unknown ACL brand"); - return (ARCHIVE_WARN); - } -#endif - - s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); - if (s == -1) { - archive_set_error(&a->archive, errno, - "Failed to get first ACL entry"); - return (ARCHIVE_WARN); - } - - while (s == 1) { - ae_id = -1; - ae_name = NULL; - ae_perm = 0; - - if (acl_get_tag_type(acl_entry, &acl_tag) != 0) { - archive_set_error(&a->archive, errno, - "Failed to get ACL tag type"); - return (ARCHIVE_WARN); - } - switch (acl_tag) { - case ACL_USER: - q = acl_get_qualifier(acl_entry); - if (q != NULL) { - ae_id = (int)*(uid_t *)q; - acl_free(q); - ae_name = archive_read_disk_uname(&a->archive, - ae_id); - } - ae_tag = ARCHIVE_ENTRY_ACL_USER; - break; - case ACL_GROUP: - q = acl_get_qualifier(acl_entry); - if (q != NULL) { - ae_id = (int)*(gid_t *)q; - acl_free(q); - ae_name = archive_read_disk_gname(&a->archive, - ae_id); - } - ae_tag = ARCHIVE_ENTRY_ACL_GROUP; - break; - case ACL_MASK: - ae_tag = ARCHIVE_ENTRY_ACL_MASK; - break; - case ACL_USER_OBJ: - ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; - break; - case ACL_GROUP_OBJ: - ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - break; - case ACL_OTHER: - ae_tag = ARCHIVE_ENTRY_ACL_OTHER; - break; -#if ARCHIVE_ACL_FREEBSD_NFS4 - case ACL_EVERYONE: - ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE; - break; -#endif - default: - /* Skip types that libarchive can't support. */ - s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); - continue; - } - - // XXX acl_type maps to allow/deny/audit/YYYY bits - entry_acl_type = default_entry_acl_type; - -#if ARCHIVE_ACL_FREEBSD_NFS4 - if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { - /* - * acl_get_entry_type_np() fails with non-NFSv4 ACLs - */ - if (acl_get_entry_type_np(acl_entry, &acl_type) != 0) { - archive_set_error(&a->archive, errno, "Failed " - "to get ACL type from a NFSv4 ACL entry"); - return (ARCHIVE_WARN); - } - switch (acl_type) { - case ACL_ENTRY_TYPE_ALLOW: - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; - break; - case ACL_ENTRY_TYPE_DENY: - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY; - break; - case ACL_ENTRY_TYPE_AUDIT: - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; - break; - case ACL_ENTRY_TYPE_ALARM: - entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; - break; - default: - archive_set_error(&a->archive, errno, - "Invalid NFSv4 ACL entry type"); - return (ARCHIVE_WARN); - } - - /* - * Libarchive stores "flag" (NFSv4 inheritance bits) - * in the ae_perm bitmap. - * - * acl_get_flagset_np() fails with non-NFSv4 ACLs - */ - if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { - archive_set_error(&a->archive, errno, - "Failed to get flagset from a NFSv4 " - "ACL entry"); - return (ARCHIVE_WARN); - } - for (i = 0; i < acl_nfs4_flag_map_size; ++i) { - r = acl_get_flag_np(acl_flagset, - acl_nfs4_flag_map[i].p_perm); - if (r == -1) { - archive_set_error(&a->archive, errno, - "Failed to check flag in a NFSv4 " - "ACL flagset"); - return (ARCHIVE_WARN); - } else if (r) - ae_perm |= acl_nfs4_flag_map[i].a_perm; - } - } -#endif - - if (acl_get_permset(acl_entry, &acl_permset) != 0) { - archive_set_error(&a->archive, errno, - "Failed to get ACL permission set"); - return (ARCHIVE_WARN); - } - -#if ARCHIVE_ACL_FREEBSD_NFS4 - if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { - perm_map_size = acl_nfs4_perm_map_size; - perm_map = acl_nfs4_perm_map; - } else { -#endif - perm_map_size = acl_posix_perm_map_size; - perm_map = acl_posix_perm_map; -#if ARCHIVE_ACL_FREEBSD_NFS4 - } -#endif - - for (i = 0; i < perm_map_size; ++i) { - r = acl_get_perm_np(acl_permset, perm_map[i].p_perm); - if (r == -1) { - archive_set_error(&a->archive, errno, - "Failed to check permission in an ACL " - "permission set"); - return (ARCHIVE_WARN); - } else if (r) - ae_perm |= perm_map[i].a_perm; - } - - archive_entry_acl_add_entry(entry, entry_acl_type, - ae_perm, ae_tag, - ae_id, ae_name); - - s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); - if (s == -1) { - archive_set_error(&a->archive, errno, - "Failed to get next ACL entry"); - return (ARCHIVE_WARN); - } - } - return (ARCHIVE_OK); -} - -int -archive_read_disk_entry_setup_acls(struct archive_read_disk *a, - struct archive_entry *entry, int *fd) -{ - const char *accpath; - acl_t acl; - int r; - - accpath = NULL; - - if (*fd < 0) { - accpath = archive_read_disk_entry_setup_path(a, entry, fd); - if (accpath == NULL) - return (ARCHIVE_WARN); - } - - archive_entry_acl_clear(entry); - - acl = NULL; - -#if ARCHIVE_ACL_FREEBSD_NFS4 - /* Try NFSv4 ACL first. */ - if (*fd >= 0) - acl = acl_get_fd_np(*fd, ACL_TYPE_NFS4); - else if (!a->follow_symlinks) - acl = acl_get_link_np(accpath, ACL_TYPE_NFS4); - else - acl = acl_get_file(accpath, ACL_TYPE_NFS4); - - /* Ignore "trivial" ACLs that just mirror the file mode. */ - if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) { - acl_free(acl); - acl = NULL; - return (ARCHIVE_OK); - } - - if (acl != NULL) { - r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4); - acl_free(acl); - acl = NULL; - - if (r != ARCHIVE_OK) { - archive_set_error(&a->archive, errno, - "Couldn't translate NFSv4 ACLs"); - } - - return (r); - } -#endif - - /* Retrieve access ACL from file. */ - if (*fd >= 0) - acl = acl_get_fd_np(*fd, ACL_TYPE_ACCESS); -#if HAVE_ACL_GET_LINK_NP - else if (!a->follow_symlinks) - acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS); -#else - else if ((!a->follow_symlinks) - && (archive_entry_filetype(entry) == AE_IFLNK)) - /* We can't get the ACL of a symlink, so we assume it can't - have one. */ - acl = NULL; -#endif - else - acl = acl_get_file(accpath, ACL_TYPE_ACCESS); - -#if HAVE_ACL_IS_TRIVIAL_NP - /* Ignore "trivial" ACLs that just mirror the file mode. */ - if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) { - acl_free(acl); - acl = NULL; - } -#endif - - if (acl != NULL) { - r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); - acl_free(acl); - acl = NULL; - - if (r != ARCHIVE_OK) { - archive_set_error(&a->archive, errno, - "Couldn't translate access ACLs"); - return (r); - } - } - - /* Only directories can have default ACLs. */ - if (S_ISDIR(archive_entry_mode(entry))) { - if (*fd >= 0) - acl = acl_get_fd_np(*fd, ACL_TYPE_DEFAULT); - else - acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); - if (acl != NULL) { - r = translate_acl(a, entry, acl, - ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); - acl_free(acl); - if (r != ARCHIVE_OK) { - archive_set_error(&a->archive, errno, - "Couldn't translate default ACLs"); - return (r); - } - } - } - return (ARCHIVE_OK); -} Property changes on: head/contrib/libarchive/libarchive/archive_read_disk_acl_freebsd.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/libarchive/libarchive/archive_acl_maps_freebsd.c =================================================================== --- head/contrib/libarchive/libarchive/archive_acl_maps_freebsd.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_acl_maps_freebsd.c (nonexistent) @@ -1,86 +0,0 @@ -/*- - * Copyright (c) 2017 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" - -#ifdef HAVE_SYS_TYPES_H -#include -#endif -#ifdef HAVE_SYS_ACL_H -#define _ACL_PRIVATE /* For debugging */ -#include -#endif - -#include "archive_entry.h" -#include "archive_private.h" -#include "archive_read_disk_private.h" -#include "archive_acl_maps.h" - -const acl_perm_map_t acl_posix_perm_map[] = { - {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, - {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE}, - {ARCHIVE_ENTRY_ACL_READ, ACL_READ}, -}; - -const int acl_posix_perm_map_size = - (int)(sizeof(acl_posix_perm_map)/sizeof(acl_posix_perm_map[0])); - -#if ARCHIVE_ACL_FREEBSD_NFS4 -const acl_perm_map_t acl_nfs4_perm_map[] = { - {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, - {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA}, - {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY}, - {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA}, - {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE}, - {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA}, - {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY}, - {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, - {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, - {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD}, - {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, - {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE}, - {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL}, - {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL}, - {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER}, - {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} -}; - -const int acl_nfs4_perm_map_size = - (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0])); - -const acl_perm_map_t acl_nfs4_flag_map[] = { - {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT}, - {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT}, - {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT}, - {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY}, - {ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_SUCCESSFUL_ACCESS}, - {ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_FAILED_ACCESS}, - {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED} -}; - -const int acl_nfs4_flag_map_size = - (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0])); -#endif /* ARCHIVE_ACL_FREEBSD_NFS4 */ Property changes on: head/contrib/libarchive/libarchive/archive_acl_maps_freebsd.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/libarchive/libarchive/archive_write_disk_acl_freebsd.c =================================================================== --- head/contrib/libarchive/libarchive/archive_write_disk_acl_freebsd.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_write_disk_acl_freebsd.c (nonexistent) @@ -1,321 +0,0 @@ -/*- - * Copyright (c) 2003-2010 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_TYPES_H -#include -#endif -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_SYS_ACL_H -#define _ACL_PRIVATE /* For debugging */ -#include -#endif - -#include "archive.h" -#include "archive_entry.h" -#include "archive_write_disk_private.h" -#include "archive_acl_maps.h" - -static int -set_acl(struct archive *a, int fd, const char *name, - struct archive_acl *abstract_acl, - int ae_requested_type, const char *tname) -{ - int acl_type = 0; - acl_t acl; - acl_entry_t acl_entry; - acl_permset_t acl_permset; -#if ARCHIVE_ACL_FREEBSD_NFS4 - acl_flagset_t acl_flagset; - int r; -#endif - int ret; - int ae_type, ae_permset, ae_tag, ae_id; - int perm_map_size; - const acl_perm_map_t *perm_map; - uid_t ae_uid; - gid_t ae_gid; - const char *ae_name; - int entries; - int i; - - ret = ARCHIVE_OK; - entries = archive_acl_reset(abstract_acl, ae_requested_type); - if (entries == 0) - return (ARCHIVE_OK); - - - switch (ae_requested_type) { - case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: - acl_type = ACL_TYPE_ACCESS; - break; - case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: - acl_type = ACL_TYPE_DEFAULT; - break; -#if ARCHIVE_ACL_FREEBSD_NFS4 - case ARCHIVE_ENTRY_ACL_TYPE_NFS4: - acl_type = ACL_TYPE_NFS4; - break; -#endif - default: - errno = ENOENT; - archive_set_error(a, errno, "Unsupported ACL type"); - return (ARCHIVE_FAILED); - } - - acl = acl_init(entries); - if (acl == (acl_t)NULL) { - archive_set_error(a, errno, - "Failed to initialize ACL working storage"); - return (ARCHIVE_FAILED); - } - - while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type, - &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) { - if (acl_create_entry(&acl, &acl_entry) != 0) { - archive_set_error(a, errno, - "Failed to create a new ACL entry"); - ret = ARCHIVE_FAILED; - goto exit_free; - } - switch (ae_tag) { - case ARCHIVE_ENTRY_ACL_USER: - ae_uid = archive_write_disk_uid(a, ae_name, ae_id); - acl_set_tag_type(acl_entry, ACL_USER); - acl_set_qualifier(acl_entry, &ae_uid); - break; - case ARCHIVE_ENTRY_ACL_GROUP: - ae_gid = archive_write_disk_gid(a, ae_name, ae_id); - acl_set_tag_type(acl_entry, ACL_GROUP); - acl_set_qualifier(acl_entry, &ae_gid); - break; - case ARCHIVE_ENTRY_ACL_USER_OBJ: - acl_set_tag_type(acl_entry, ACL_USER_OBJ); - break; - case ARCHIVE_ENTRY_ACL_GROUP_OBJ: - acl_set_tag_type(acl_entry, ACL_GROUP_OBJ); - break; - case ARCHIVE_ENTRY_ACL_MASK: - acl_set_tag_type(acl_entry, ACL_MASK); - break; - case ARCHIVE_ENTRY_ACL_OTHER: - acl_set_tag_type(acl_entry, ACL_OTHER); - break; -#if ARCHIVE_ACL_FREEBSD_NFS4 - case ARCHIVE_ENTRY_ACL_EVERYONE: - acl_set_tag_type(acl_entry, ACL_EVERYONE); - break; -#endif - default: - archive_set_error(a, ARCHIVE_ERRNO_MISC, - "Unsupported ACL tag"); - ret = ARCHIVE_FAILED; - goto exit_free; - } - -#if ARCHIVE_ACL_FREEBSD_NFS4 - r = 0; - switch (ae_type) { - case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: - r = acl_set_entry_type_np(acl_entry, - ACL_ENTRY_TYPE_ALLOW); - break; - case ARCHIVE_ENTRY_ACL_TYPE_DENY: - r = acl_set_entry_type_np(acl_entry, - ACL_ENTRY_TYPE_DENY); - break; - case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: - r = acl_set_entry_type_np(acl_entry, - ACL_ENTRY_TYPE_AUDIT); - break; - case ARCHIVE_ENTRY_ACL_TYPE_ALARM: - r = acl_set_entry_type_np(acl_entry, - ACL_ENTRY_TYPE_ALARM); - break; - case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: - case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: - // These don't translate directly into the system ACL. - break; - default: - archive_set_error(a, ARCHIVE_ERRNO_MISC, - "Unsupported ACL entry type"); - ret = ARCHIVE_FAILED; - goto exit_free; - } - - if (r != 0) { - archive_set_error(a, errno, - "Failed to set ACL entry type"); - ret = ARCHIVE_FAILED; - goto exit_free; - } -#endif - - if (acl_get_permset(acl_entry, &acl_permset) != 0) { - archive_set_error(a, errno, - "Failed to get ACL permission set"); - ret = ARCHIVE_FAILED; - goto exit_free; - } - if (acl_clear_perms(acl_permset) != 0) { - archive_set_error(a, errno, - "Failed to clear ACL permissions"); - ret = ARCHIVE_FAILED; - goto exit_free; - } -#if ARCHIVE_ACL_FREEBSD_NFS4 - if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { - perm_map_size = acl_nfs4_perm_map_size; - perm_map = acl_nfs4_perm_map; - } else { -#endif - perm_map_size = acl_posix_perm_map_size; - perm_map = acl_posix_perm_map; -#if ARCHIVE_ACL_FREEBSD_NFS4 - } -#endif - - for (i = 0; i < perm_map_size; ++i) { - if (ae_permset & perm_map[i].a_perm) { - if (acl_add_perm(acl_permset, - perm_map[i].p_perm) != 0) { - archive_set_error(a, errno, - "Failed to add ACL permission"); - ret = ARCHIVE_FAILED; - goto exit_free; - } - } - } - -#if ARCHIVE_ACL_FREEBSD_NFS4 - if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { - /* - * acl_get_flagset_np() fails with non-NFSv4 ACLs - */ - if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { - archive_set_error(a, errno, - "Failed to get flagset from an NFSv4 " - "ACL entry"); - ret = ARCHIVE_FAILED; - goto exit_free; - } - if (acl_clear_flags_np(acl_flagset) != 0) { - archive_set_error(a, errno, - "Failed to clear flags from an NFSv4 " - "ACL flagset"); - ret = ARCHIVE_FAILED; - goto exit_free; - } - for (i = 0; i < acl_nfs4_flag_map_size; ++i) { - if (ae_permset & acl_nfs4_flag_map[i].a_perm) { - if (acl_add_flag_np(acl_flagset, - acl_nfs4_flag_map[i].p_perm) != 0) { - archive_set_error(a, errno, - "Failed to add flag to " - "NFSv4 ACL flagset"); - ret = ARCHIVE_FAILED; - goto exit_free; - } - } - } - } -#endif - } - - /* Try restoring the ACL through 'fd' if we can. */ - if (fd >= 0) { - if (acl_set_fd_np(fd, acl, acl_type) == 0) - ret = ARCHIVE_OK; - else { - if (errno == EOPNOTSUPP) { - /* Filesystem doesn't support ACLs */ - ret = ARCHIVE_OK; - } else { - archive_set_error(a, errno, - "Failed to set acl on fd: %s", tname); - ret = ARCHIVE_WARN; - } - } - } -#if HAVE_ACL_SET_LINK_NP - else if (acl_set_link_np(name, acl_type, acl) != 0) -#else - /* FreeBSD older than 8.0 */ - else if (acl_set_file(name, acl_type, acl) != 0) -#endif - { - if (errno == EOPNOTSUPP) { - /* Filesystem doesn't support ACLs */ - ret = ARCHIVE_OK; - } else { - archive_set_error(a, errno, "Failed to set acl: %s", - tname); - ret = ARCHIVE_WARN; - } - } -exit_free: - acl_free(acl); - return (ret); -} - -int -archive_write_disk_set_acls(struct archive *a, int fd, const char *name, - struct archive_acl *abstract_acl, __LA_MODE_T mode) -{ - int ret = ARCHIVE_OK; - - (void)mode; /* UNUSED */ - - if ((archive_acl_types(abstract_acl) - & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { - if ((archive_acl_types(abstract_acl) - & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { - ret = set_acl(a, fd, name, abstract_acl, - ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access"); - if (ret != ARCHIVE_OK) - return (ret); - } - if ((archive_acl_types(abstract_acl) - & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) - ret = set_acl(a, fd, name, abstract_acl, - ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default"); - - /* Simultaneous POSIX.1e and NFSv4 is not supported */ - return (ret); - } -#if ARCHIVE_ACL_FREEBSD_NFS4 - else if ((archive_acl_types(abstract_acl) & - ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { - ret = set_acl(a, fd, name, abstract_acl, - ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4"); - } -#endif - return (ret); -} Index: head/contrib/libarchive/libarchive/archive_acl_maps.h =================================================================== --- head/contrib/libarchive/libarchive/archive_acl_maps.h (revision 316094) +++ head/contrib/libarchive/libarchive/archive_acl_maps.h (nonexistent) @@ -1,50 +0,0 @@ -/*- - * Copyright (c) 2017 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. - */ - -#ifndef __LIBARCHIVE_BUILD -#error This header is only to be used internally to libarchive. -#endif - -#ifndef ARCHIVE_ACL_MAPS_H_INCLUDED -#define ARCHIVE_ACL_MAPS_H_INCLUDED - -#include "archive_platform_acl.h" - -typedef struct { - const int a_perm; /* Libarchive permission or flag */ - const int p_perm; /* Platform permission or flag */ -} acl_perm_map_t; - -#if ARCHIVE_ACL_POSIX1E -extern const acl_perm_map_t acl_posix_perm_map[]; -extern const int acl_posix_perm_map_size; -#endif -#if ARCHIVE_ACL_NFS4 -extern const acl_perm_map_t acl_nfs4_perm_map[]; -extern const int acl_nfs4_perm_map_size; -extern const acl_perm_map_t acl_nfs4_flag_map[]; -extern const int acl_nfs4_flag_map_size; -#endif -#endif /* ARCHIVE_ACL_MAPS_H_INCLUDED */ Property changes on: head/contrib/libarchive/libarchive/archive_acl_maps.h ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: head/contrib/libarchive/libarchive/archive_disk_acl_freebsd.c =================================================================== --- head/contrib/libarchive/libarchive/archive_disk_acl_freebsd.c (nonexistent) +++ head/contrib/libarchive/libarchive/archive_disk_acl_freebsd.c (revision 316095) @@ -0,0 +1,700 @@ +/*- + * Copyright (c) 2003-2009 Tim Kientzle + * Copyright (c) 2010-2012 Michihiro NAKAJIMA + * Copyright (c) 2017 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" + +#if ARCHIVE_ACL_FREEBSD + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_ACL_H +#define _ACL_PRIVATE /* For debugging */ +#include +#endif + +#include "archive_entry.h" +#include "archive_private.h" +#include "archive_read_disk_private.h" +#include "archive_write_disk_private.h" + +typedef struct { + const int a_perm; /* Libarchive permission or flag */ + const int p_perm; /* Platform permission or flag */ +} acl_perm_map_t; + +static const acl_perm_map_t acl_posix_perm_map[] = { + {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, + {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE}, + {ARCHIVE_ENTRY_ACL_READ, ACL_READ}, +}; + +static const int acl_posix_perm_map_size = + (int)(sizeof(acl_posix_perm_map)/sizeof(acl_posix_perm_map[0])); + +#if ARCHIVE_ACL_FREEBSD_NFS4 +static const acl_perm_map_t acl_nfs4_perm_map[] = { + {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, + {ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA}, + {ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY}, + {ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA}, + {ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE}, + {ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA}, + {ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY}, + {ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, + {ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, + {ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD}, + {ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, + {ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, + {ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE}, + {ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL}, + {ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL}, + {ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER}, + {ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE} +}; + +static const int acl_nfs4_perm_map_size = + (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0])); + +static const acl_perm_map_t acl_nfs4_flag_map[] = { + {ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT}, + {ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT}, + {ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT}, + {ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY}, + {ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_SUCCESSFUL_ACCESS}, + {ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_FAILED_ACCESS}, + {ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED} +}; + +static const int acl_nfs4_flag_map_size = + (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0])); +#endif /* ARCHIVE_ACL_FREEBSD_NFS4 */ + +static int +translate_acl(struct archive_read_disk *a, + struct archive_entry *entry, acl_t acl, int default_entry_acl_type) +{ +#if ARCHIVE_ACL_FREEBSD_NFS4 + int brand; + acl_flagset_t acl_flagset; + acl_entry_type_t acl_type; +#endif + acl_tag_t acl_tag; + acl_entry_t acl_entry; + acl_permset_t acl_permset; + int i, entry_acl_type, perm_map_size; + const acl_perm_map_t *perm_map; + int r, s, ae_id, ae_tag, ae_perm; + void *q; + const char *ae_name; + +#if ARCHIVE_ACL_FREEBSD_NFS4 + // FreeBSD "brands" ACLs as POSIX.1e or NFSv4 + // Make sure the "brand" on this ACL is consistent + // with the default_entry_acl_type bits provided. + if (acl_get_brand_np(acl, &brand) != 0) { + archive_set_error(&a->archive, errno, + "Failed to read ACL brand"); + return (ARCHIVE_WARN); + } + switch (brand) { + case ACL_BRAND_POSIX: + switch (default_entry_acl_type) { + case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: + case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Invalid ACL entry type for POSIX.1e ACL"); + return (ARCHIVE_WARN); + } + break; + case ACL_BRAND_NFS4: + if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Invalid ACL entry type for NFSv4 ACL"); + return (ARCHIVE_WARN); + } + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Unknown ACL brand"); + return (ARCHIVE_WARN); + } +#endif + + s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); + if (s == -1) { + archive_set_error(&a->archive, errno, + "Failed to get first ACL entry"); + return (ARCHIVE_WARN); + } + + while (s == 1) { + ae_id = -1; + ae_name = NULL; + ae_perm = 0; + + if (acl_get_tag_type(acl_entry, &acl_tag) != 0) { + archive_set_error(&a->archive, errno, + "Failed to get ACL tag type"); + return (ARCHIVE_WARN); + } + switch (acl_tag) { + case ACL_USER: + q = acl_get_qualifier(acl_entry); + if (q != NULL) { + ae_id = (int)*(uid_t *)q; + acl_free(q); + ae_name = archive_read_disk_uname(&a->archive, + ae_id); + } + ae_tag = ARCHIVE_ENTRY_ACL_USER; + break; + case ACL_GROUP: + q = acl_get_qualifier(acl_entry); + if (q != NULL) { + ae_id = (int)*(gid_t *)q; + acl_free(q); + ae_name = archive_read_disk_gname(&a->archive, + ae_id); + } + ae_tag = ARCHIVE_ENTRY_ACL_GROUP; + break; + case ACL_MASK: + ae_tag = ARCHIVE_ENTRY_ACL_MASK; + break; + case ACL_USER_OBJ: + ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; + break; + case ACL_GROUP_OBJ: + ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; + break; + case ACL_OTHER: + ae_tag = ARCHIVE_ENTRY_ACL_OTHER; + break; +#if ARCHIVE_ACL_FREEBSD_NFS4 + case ACL_EVERYONE: + ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE; + break; +#endif + default: + /* Skip types that libarchive can't support. */ + s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); + continue; + } + + // XXX acl_type maps to allow/deny/audit/YYYY bits + entry_acl_type = default_entry_acl_type; + +#if ARCHIVE_ACL_FREEBSD_NFS4 + if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + /* + * acl_get_entry_type_np() fails with non-NFSv4 ACLs + */ + if (acl_get_entry_type_np(acl_entry, &acl_type) != 0) { + archive_set_error(&a->archive, errno, "Failed " + "to get ACL type from a NFSv4 ACL entry"); + return (ARCHIVE_WARN); + } + switch (acl_type) { + case ACL_ENTRY_TYPE_ALLOW: + entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW; + break; + case ACL_ENTRY_TYPE_DENY: + entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY; + break; + case ACL_ENTRY_TYPE_AUDIT: + entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT; + break; + case ACL_ENTRY_TYPE_ALARM: + entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM; + break; + default: + archive_set_error(&a->archive, errno, + "Invalid NFSv4 ACL entry type"); + return (ARCHIVE_WARN); + } + + /* + * Libarchive stores "flag" (NFSv4 inheritance bits) + * in the ae_perm bitmap. + * + * acl_get_flagset_np() fails with non-NFSv4 ACLs + */ + if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { + archive_set_error(&a->archive, errno, + "Failed to get flagset from a NFSv4 " + "ACL entry"); + return (ARCHIVE_WARN); + } + for (i = 0; i < acl_nfs4_flag_map_size; ++i) { + r = acl_get_flag_np(acl_flagset, + acl_nfs4_flag_map[i].p_perm); + if (r == -1) { + archive_set_error(&a->archive, errno, + "Failed to check flag in a NFSv4 " + "ACL flagset"); + return (ARCHIVE_WARN); + } else if (r) + ae_perm |= acl_nfs4_flag_map[i].a_perm; + } + } +#endif + + if (acl_get_permset(acl_entry, &acl_permset) != 0) { + archive_set_error(&a->archive, errno, + "Failed to get ACL permission set"); + return (ARCHIVE_WARN); + } + +#if ARCHIVE_ACL_FREEBSD_NFS4 + if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + perm_map_size = acl_nfs4_perm_map_size; + perm_map = acl_nfs4_perm_map; + } else { +#endif + perm_map_size = acl_posix_perm_map_size; + perm_map = acl_posix_perm_map; +#if ARCHIVE_ACL_FREEBSD_NFS4 + } +#endif + + for (i = 0; i < perm_map_size; ++i) { + r = acl_get_perm_np(acl_permset, perm_map[i].p_perm); + if (r == -1) { + archive_set_error(&a->archive, errno, + "Failed to check permission in an ACL " + "permission set"); + return (ARCHIVE_WARN); + } else if (r) + ae_perm |= perm_map[i].a_perm; + } + + archive_entry_acl_add_entry(entry, entry_acl_type, + ae_perm, ae_tag, + ae_id, ae_name); + + s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); + if (s == -1) { + archive_set_error(&a->archive, errno, + "Failed to get next ACL entry"); + return (ARCHIVE_WARN); + } + } + return (ARCHIVE_OK); +} + +static int +set_acl(struct archive *a, int fd, const char *name, + struct archive_acl *abstract_acl, + int ae_requested_type, const char *tname) +{ + int acl_type = 0; + acl_t acl; + acl_entry_t acl_entry; + acl_permset_t acl_permset; +#if ARCHIVE_ACL_FREEBSD_NFS4 + acl_flagset_t acl_flagset; + int r; +#endif + int ret; + int ae_type, ae_permset, ae_tag, ae_id; + int perm_map_size; + const acl_perm_map_t *perm_map; + uid_t ae_uid; + gid_t ae_gid; + const char *ae_name; + int entries; + int i; + + ret = ARCHIVE_OK; + entries = archive_acl_reset(abstract_acl, ae_requested_type); + if (entries == 0) + return (ARCHIVE_OK); + + + switch (ae_requested_type) { + case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: + acl_type = ACL_TYPE_ACCESS; + break; + case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: + acl_type = ACL_TYPE_DEFAULT; + break; +#if ARCHIVE_ACL_FREEBSD_NFS4 + case ARCHIVE_ENTRY_ACL_TYPE_NFS4: + acl_type = ACL_TYPE_NFS4; + break; +#endif + default: + errno = ENOENT; + archive_set_error(a, errno, "Unsupported ACL type"); + return (ARCHIVE_FAILED); + } + + acl = acl_init(entries); + if (acl == (acl_t)NULL) { + archive_set_error(a, errno, + "Failed to initialize ACL working storage"); + return (ARCHIVE_FAILED); + } + + while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type, + &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) { + if (acl_create_entry(&acl, &acl_entry) != 0) { + archive_set_error(a, errno, + "Failed to create a new ACL entry"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + switch (ae_tag) { + case ARCHIVE_ENTRY_ACL_USER: + ae_uid = archive_write_disk_uid(a, ae_name, ae_id); + acl_set_tag_type(acl_entry, ACL_USER); + acl_set_qualifier(acl_entry, &ae_uid); + break; + case ARCHIVE_ENTRY_ACL_GROUP: + ae_gid = archive_write_disk_gid(a, ae_name, ae_id); + acl_set_tag_type(acl_entry, ACL_GROUP); + acl_set_qualifier(acl_entry, &ae_gid); + break; + case ARCHIVE_ENTRY_ACL_USER_OBJ: + acl_set_tag_type(acl_entry, ACL_USER_OBJ); + break; + case ARCHIVE_ENTRY_ACL_GROUP_OBJ: + acl_set_tag_type(acl_entry, ACL_GROUP_OBJ); + break; + case ARCHIVE_ENTRY_ACL_MASK: + acl_set_tag_type(acl_entry, ACL_MASK); + break; + case ARCHIVE_ENTRY_ACL_OTHER: + acl_set_tag_type(acl_entry, ACL_OTHER); + break; +#if ARCHIVE_ACL_FREEBSD_NFS4 + case ARCHIVE_ENTRY_ACL_EVERYONE: + acl_set_tag_type(acl_entry, ACL_EVERYONE); + break; +#endif + default: + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "Unsupported ACL tag"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + +#if ARCHIVE_ACL_FREEBSD_NFS4 + r = 0; + switch (ae_type) { + case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: + r = acl_set_entry_type_np(acl_entry, + ACL_ENTRY_TYPE_ALLOW); + break; + case ARCHIVE_ENTRY_ACL_TYPE_DENY: + r = acl_set_entry_type_np(acl_entry, + ACL_ENTRY_TYPE_DENY); + break; + case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: + r = acl_set_entry_type_np(acl_entry, + ACL_ENTRY_TYPE_AUDIT); + break; + case ARCHIVE_ENTRY_ACL_TYPE_ALARM: + r = acl_set_entry_type_np(acl_entry, + ACL_ENTRY_TYPE_ALARM); + break; + case ARCHIVE_ENTRY_ACL_TYPE_ACCESS: + case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT: + // These don't translate directly into the system ACL. + break; + default: + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "Unsupported ACL entry type"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + + if (r != 0) { + archive_set_error(a, errno, + "Failed to set ACL entry type"); + ret = ARCHIVE_FAILED; + goto exit_free; + } +#endif + + if (acl_get_permset(acl_entry, &acl_permset) != 0) { + archive_set_error(a, errno, + "Failed to get ACL permission set"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + if (acl_clear_perms(acl_permset) != 0) { + archive_set_error(a, errno, + "Failed to clear ACL permissions"); + ret = ARCHIVE_FAILED; + goto exit_free; + } +#if ARCHIVE_ACL_FREEBSD_NFS4 + if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + perm_map_size = acl_nfs4_perm_map_size; + perm_map = acl_nfs4_perm_map; + } else { +#endif + perm_map_size = acl_posix_perm_map_size; + perm_map = acl_posix_perm_map; +#if ARCHIVE_ACL_FREEBSD_NFS4 + } +#endif + + for (i = 0; i < perm_map_size; ++i) { + if (ae_permset & perm_map[i].a_perm) { + if (acl_add_perm(acl_permset, + perm_map[i].p_perm) != 0) { + archive_set_error(a, errno, + "Failed to add ACL permission"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + } + } + +#if ARCHIVE_ACL_FREEBSD_NFS4 + if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + /* + * acl_get_flagset_np() fails with non-NFSv4 ACLs + */ + if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { + archive_set_error(a, errno, + "Failed to get flagset from an NFSv4 " + "ACL entry"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + if (acl_clear_flags_np(acl_flagset) != 0) { + archive_set_error(a, errno, + "Failed to clear flags from an NFSv4 " + "ACL flagset"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + for (i = 0; i < acl_nfs4_flag_map_size; ++i) { + if (ae_permset & acl_nfs4_flag_map[i].a_perm) { + if (acl_add_flag_np(acl_flagset, + acl_nfs4_flag_map[i].p_perm) != 0) { + archive_set_error(a, errno, + "Failed to add flag to " + "NFSv4 ACL flagset"); + ret = ARCHIVE_FAILED; + goto exit_free; + } + } + } + } +#endif + } + + /* Try restoring the ACL through 'fd' if we can. */ + if (fd >= 0) { + if (acl_set_fd_np(fd, acl, acl_type) == 0) + ret = ARCHIVE_OK; + else { + if (errno == EOPNOTSUPP) { + /* Filesystem doesn't support ACLs */ + ret = ARCHIVE_OK; + } else { + archive_set_error(a, errno, + "Failed to set acl on fd: %s", tname); + ret = ARCHIVE_WARN; + } + } + } +#if HAVE_ACL_SET_LINK_NP + else if (acl_set_link_np(name, acl_type, acl) != 0) +#else + /* FreeBSD older than 8.0 */ + else if (acl_set_file(name, acl_type, acl) != 0) +#endif + { + if (errno == EOPNOTSUPP) { + /* Filesystem doesn't support ACLs */ + ret = ARCHIVE_OK; + } else { + archive_set_error(a, errno, "Failed to set acl: %s", + tname); + ret = ARCHIVE_WARN; + } + } +exit_free: + acl_free(acl); + return (ret); +} + +int +archive_read_disk_entry_setup_acls(struct archive_read_disk *a, + struct archive_entry *entry, int *fd) +{ + const char *accpath; + acl_t acl; + int r; + + accpath = NULL; + + if (*fd < 0) { + accpath = archive_read_disk_entry_setup_path(a, entry, fd); + if (accpath == NULL) + return (ARCHIVE_WARN); + } + + archive_entry_acl_clear(entry); + + acl = NULL; + +#if ARCHIVE_ACL_FREEBSD_NFS4 + /* Try NFSv4 ACL first. */ + if (*fd >= 0) + acl = acl_get_fd_np(*fd, ACL_TYPE_NFS4); + else if (!a->follow_symlinks) + acl = acl_get_link_np(accpath, ACL_TYPE_NFS4); + else + acl = acl_get_file(accpath, ACL_TYPE_NFS4); + + /* Ignore "trivial" ACLs that just mirror the file mode. */ + if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) { + acl_free(acl); + acl = NULL; + return (ARCHIVE_OK); + } + + if (acl != NULL) { + r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4); + acl_free(acl); + acl = NULL; + + if (r != ARCHIVE_OK) { + archive_set_error(&a->archive, errno, + "Couldn't translate NFSv4 ACLs"); + } + + return (r); + } +#endif + + /* Retrieve access ACL from file. */ + if (*fd >= 0) + acl = acl_get_fd_np(*fd, ACL_TYPE_ACCESS); +#if HAVE_ACL_GET_LINK_NP + else if (!a->follow_symlinks) + acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS); +#else + else if ((!a->follow_symlinks) + && (archive_entry_filetype(entry) == AE_IFLNK)) + /* We can't get the ACL of a symlink, so we assume it can't + have one. */ + acl = NULL; +#endif + else + acl = acl_get_file(accpath, ACL_TYPE_ACCESS); + +#if HAVE_ACL_IS_TRIVIAL_NP + /* Ignore "trivial" ACLs that just mirror the file mode. */ + if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) { + acl_free(acl); + acl = NULL; + } +#endif + + if (acl != NULL) { + r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); + acl_free(acl); + acl = NULL; + + if (r != ARCHIVE_OK) { + archive_set_error(&a->archive, errno, + "Couldn't translate access ACLs"); + return (r); + } + } + + /* Only directories can have default ACLs. */ + if (S_ISDIR(archive_entry_mode(entry))) { + if (*fd >= 0) + acl = acl_get_fd_np(*fd, ACL_TYPE_DEFAULT); + else + acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); + if (acl != NULL) { + r = translate_acl(a, entry, acl, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); + acl_free(acl); + if (r != ARCHIVE_OK) { + archive_set_error(&a->archive, errno, + "Couldn't translate default ACLs"); + return (r); + } + } + } + return (ARCHIVE_OK); +} + +int +archive_write_disk_set_acls(struct archive *a, int fd, const char *name, + struct archive_acl *abstract_acl, __LA_MODE_T mode) +{ + int ret = ARCHIVE_OK; + + (void)mode; /* UNUSED */ + + if ((archive_acl_types(abstract_acl) + & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { + if ((archive_acl_types(abstract_acl) + & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { + ret = set_acl(a, fd, name, abstract_acl, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access"); + if (ret != ARCHIVE_OK) + return (ret); + } + if ((archive_acl_types(abstract_acl) + & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) + ret = set_acl(a, fd, name, abstract_acl, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default"); + + /* Simultaneous POSIX.1e and NFSv4 is not supported */ + return (ret); + } +#if ARCHIVE_ACL_FREEBSD_NFS4 + else if ((archive_acl_types(abstract_acl) & + ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { + ret = set_acl(a, fd, name, abstract_acl, + ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4"); + } +#endif + return (ret); +} +#endif /* ARCHIVE_ACL_FREEBSD */ Property changes on: head/contrib/libarchive/libarchive/archive_disk_acl_freebsd.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: head/contrib/libarchive/libarchive/archive_entry.c =================================================================== --- head/contrib/libarchive/libarchive/archive_entry.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_entry.c (revision 316095) @@ -1,2038 +1,2038 @@ /*- * 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, 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, 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; } 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; } 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; } 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. * 3. 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 struct flag { +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; - struct flag *flag; + 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; - struct flag *flag; + 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; - struct flag *flag; + 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: head/contrib/libarchive/libarchive/archive_getdate.c =================================================================== --- head/contrib/libarchive/libarchive/archive_getdate.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_getdate.c (revision 316095) @@ -1,1038 +1,1038 @@ /* * This code is in the public domain and has no copyright. * * This is a plain C recursive-descent translation of an old * public-domain YACC grammar that has been used for parsing dates in * very many open-source projects. * * Since the original authors were generous enough to donate their * work to the public domain, I feel compelled to match their * generosity. * * Tim Kientzle, February 2009. */ /* * Header comment from original getdate.y: */ /* ** Originally written by Steven M. Bellovin while ** at the University of North Carolina at Chapel Hill. Later tweaked by ** a couple of people on Usenet. Completely overhauled by Rich $alz ** and Jim Berets in August, 1990; ** ** This grammar has 10 shift/reduce conflicts. ** ** This code is in the public domain and has no copyright. */ #ifdef __FreeBSD__ #include __FBSDID("$FreeBSD$"); #endif #include #include #include #include #include #define __LIBARCHIVE_BUILD 1 #include "archive_getdate.h" /* Basic time units. */ #define EPOCH 1970 #define MINUTE (60L) #define HOUR (60L * MINUTE) #define DAY (24L * HOUR) /* Daylight-savings mode: on, off, or not yet known. */ enum DSTMODE { DSTon, DSToff, DSTmaybe }; /* Meridian: am or pm. */ enum { tAM, tPM }; /* Token types returned by nexttoken() */ enum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT, tUNUMBER, tZONE, tDST }; struct token { int token; time_t value; }; /* * Parser state. */ struct gdstate { struct token *tokenp; /* Pointer to next token. */ /* HaveXxxx counts how many of this kind of phrase we've seen; * it's a fatal error to have more than one time, zone, day, * or date phrase. */ int HaveYear; int HaveMonth; int HaveDay; int HaveWeekDay; /* Day of week */ int HaveTime; /* Hour/minute/second */ int HaveZone; /* timezone and/or DST info */ int HaveRel; /* time offset; we can have more than one */ /* Absolute time values. */ time_t Timezone; /* Seconds offset from GMT */ time_t Day; time_t Hour; time_t Minutes; time_t Month; time_t Seconds; time_t Year; /* DST selection */ enum DSTMODE DSTmode; /* Day of week accounting, e.g., "3rd Tuesday" */ time_t DayOrdinal; /* "3" in "3rd Tuesday" */ time_t DayNumber; /* "Tuesday" in "3rd Tuesday" */ /* Relative time values: hour/day/week offsets are measured in * seconds, month/year are counted in months. */ time_t RelMonth; time_t RelSeconds; }; /* * A series of functions that recognize certain common time phrases. * Each function returns 1 if it managed to make sense of some of the * tokens, zero otherwise. */ /* * hour:minute or hour:minute:second with optional AM, PM, or numeric * timezone offset */ static int timephrase(struct gdstate *gds) { if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == ':' && gds->tokenp[2].token == tUNUMBER && gds->tokenp[3].token == ':' && gds->tokenp[4].token == tUNUMBER) { /* "12:14:18" or "22:08:07" */ ++gds->HaveTime; gds->Hour = gds->tokenp[0].value; gds->Minutes = gds->tokenp[2].value; gds->Seconds = gds->tokenp[4].value; gds->tokenp += 5; } else if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == ':' && gds->tokenp[2].token == tUNUMBER) { /* "12:14" or "22:08" */ ++gds->HaveTime; gds->Hour = gds->tokenp[0].value; gds->Minutes = gds->tokenp[2].value; gds->Seconds = 0; gds->tokenp += 3; } else if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tAMPM) { /* "7" is a time if it's followed by "am" or "pm" */ ++gds->HaveTime; gds->Hour = gds->tokenp[0].value; gds->Minutes = gds->Seconds = 0; /* We'll handle the AM/PM below. */ gds->tokenp += 1; } else { /* We can't handle this. */ return 0; } if (gds->tokenp[0].token == tAMPM) { /* "7:12pm", "12:20:13am" */ if (gds->Hour == 12) gds->Hour = 0; if (gds->tokenp[0].value == tPM) gds->Hour += 12; gds->tokenp += 1; } if (gds->tokenp[0].token == '+' && gds->tokenp[1].token == tUNUMBER) { /* "7:14+0700" */ gds->HaveZone++; gds->DSTmode = DSToff; gds->Timezone = - ((gds->tokenp[1].value / 100) * HOUR + (gds->tokenp[1].value % 100) * MINUTE); gds->tokenp += 2; } if (gds->tokenp[0].token == '-' && gds->tokenp[1].token == tUNUMBER) { /* "19:14:12-0530" */ gds->HaveZone++; gds->DSTmode = DSToff; gds->Timezone = + ((gds->tokenp[1].value / 100) * HOUR + (gds->tokenp[1].value % 100) * MINUTE); gds->tokenp += 2; } return 1; } /* * Timezone name, possibly including DST. */ static int zonephrase(struct gdstate *gds) { if (gds->tokenp[0].token == tZONE && gds->tokenp[1].token == tDST) { gds->HaveZone++; gds->Timezone = gds->tokenp[0].value; gds->DSTmode = DSTon; gds->tokenp += 1; return 1; } if (gds->tokenp[0].token == tZONE) { gds->HaveZone++; gds->Timezone = gds->tokenp[0].value; gds->DSTmode = DSToff; gds->tokenp += 1; return 1; } if (gds->tokenp[0].token == tDAYZONE) { gds->HaveZone++; gds->Timezone = gds->tokenp[0].value; gds->DSTmode = DSTon; gds->tokenp += 1; return 1; } return 0; } /* * Year/month/day in various combinations. */ static int datephrase(struct gdstate *gds) { if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == '/' && gds->tokenp[2].token == tUNUMBER && gds->tokenp[3].token == '/' && gds->tokenp[4].token == tUNUMBER) { gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; if (gds->tokenp[0].value >= 13) { /* First number is big: 2004/01/29, 99/02/17 */ gds->Year = gds->tokenp[0].value; gds->Month = gds->tokenp[2].value; gds->Day = gds->tokenp[4].value; } else if ((gds->tokenp[4].value >= 13) || (gds->tokenp[2].value >= 13)) { /* Last number is big: 01/07/98 */ /* Middle number is big: 01/29/04 */ gds->Month = gds->tokenp[0].value; gds->Day = gds->tokenp[2].value; gds->Year = gds->tokenp[4].value; } else { /* No significant clues: 02/03/04 */ gds->Month = gds->tokenp[0].value; gds->Day = gds->tokenp[2].value; gds->Year = gds->tokenp[4].value; } gds->tokenp += 5; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == '/' && gds->tokenp[2].token == tUNUMBER) { /* "1/15" */ gds->HaveMonth++; gds->HaveDay++; gds->Month = gds->tokenp[0].value; gds->Day = gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == '-' && gds->tokenp[2].token == tUNUMBER && gds->tokenp[3].token == '-' && gds->tokenp[4].token == tUNUMBER) { /* ISO 8601 format. yyyy-mm-dd. */ gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; gds->Year = gds->tokenp[0].value; gds->Month = gds->tokenp[2].value; gds->Day = gds->tokenp[4].value; gds->tokenp += 5; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == '-' && gds->tokenp[2].token == tMONTH && gds->tokenp[3].token == '-' && gds->tokenp[4].token == tUNUMBER) { gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; if (gds->tokenp[0].value > 31) { /* e.g. 1992-Jun-17 */ gds->Year = gds->tokenp[0].value; gds->Month = gds->tokenp[2].value; gds->Day = gds->tokenp[4].value; } else { /* e.g. 17-JUN-1992. */ gds->Day = gds->tokenp[0].value; gds->Month = gds->tokenp[2].value; gds->Year = gds->tokenp[4].value; } gds->tokenp += 5; return 1; } if (gds->tokenp[0].token == tMONTH && gds->tokenp[1].token == tUNUMBER && gds->tokenp[2].token == ',' && gds->tokenp[3].token == tUNUMBER) { /* "June 17, 2001" */ gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; gds->Month = gds->tokenp[0].value; gds->Day = gds->tokenp[1].value; gds->Year = gds->tokenp[3].value; gds->tokenp += 4; return 1; } if (gds->tokenp[0].token == tMONTH && gds->tokenp[1].token == tUNUMBER) { /* "May 3" */ gds->HaveMonth++; gds->HaveDay++; gds->Month = gds->tokenp[0].value; gds->Day = gds->tokenp[1].value; gds->tokenp += 2; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tMONTH && gds->tokenp[2].token == tUNUMBER) { /* "12 Sept 1997" */ gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; gds->Day = gds->tokenp[0].value; gds->Month = gds->tokenp[1].value; gds->Year = gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tMONTH) { /* "12 Sept" */ gds->HaveMonth++; gds->HaveDay++; gds->Day = gds->tokenp[0].value; gds->Month = gds->tokenp[1].value; gds->tokenp += 2; return 1; } return 0; } /* * Relative time phrase: "tomorrow", "yesterday", "+1 hour", etc. */ static int relunitphrase(struct gdstate *gds) { if (gds->tokenp[0].token == '-' && gds->tokenp[1].token == tUNUMBER && gds->tokenp[2].token == tSEC_UNIT) { /* "-3 hours" */ gds->HaveRel++; gds->RelSeconds -= gds->tokenp[1].value * gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == '+' && gds->tokenp[1].token == tUNUMBER && gds->tokenp[2].token == tSEC_UNIT) { /* "+1 minute" */ gds->HaveRel++; gds->RelSeconds += gds->tokenp[1].value * gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tSEC_UNIT) { /* "1 day" */ gds->HaveRel++; gds->RelSeconds += gds->tokenp[0].value * gds->tokenp[1].value; gds->tokenp += 2; return 1; } if (gds->tokenp[0].token == '-' && gds->tokenp[1].token == tUNUMBER && gds->tokenp[2].token == tMONTH_UNIT) { /* "-3 months" */ gds->HaveRel++; gds->RelMonth -= gds->tokenp[1].value * gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == '+' && gds->tokenp[1].token == tUNUMBER && gds->tokenp[2].token == tMONTH_UNIT) { /* "+5 years" */ gds->HaveRel++; gds->RelMonth += gds->tokenp[1].value * gds->tokenp[2].value; gds->tokenp += 3; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tMONTH_UNIT) { /* "2 years" */ gds->HaveRel++; gds->RelMonth += gds->tokenp[0].value * gds->tokenp[1].value; gds->tokenp += 2; return 1; } if (gds->tokenp[0].token == tSEC_UNIT) { /* "now", "tomorrow" */ gds->HaveRel++; gds->RelSeconds += gds->tokenp[0].value; gds->tokenp += 1; return 1; } if (gds->tokenp[0].token == tMONTH_UNIT) { /* "month" */ gds->HaveRel++; gds->RelMonth += gds->tokenp[0].value; gds->tokenp += 1; return 1; } return 0; } /* * Day of the week specification. */ static int dayphrase(struct gdstate *gds) { if (gds->tokenp[0].token == tDAY) { /* "tues", "wednesday," */ gds->HaveWeekDay++; gds->DayOrdinal = 1; gds->DayNumber = gds->tokenp[0].value; gds->tokenp += 1; if (gds->tokenp[0].token == ',') gds->tokenp += 1; return 1; } if (gds->tokenp[0].token == tUNUMBER && gds->tokenp[1].token == tDAY) { /* "second tues" "3 wed" */ gds->HaveWeekDay++; gds->DayOrdinal = gds->tokenp[0].value; gds->DayNumber = gds->tokenp[1].value; gds->tokenp += 2; return 1; } return 0; } /* * Try to match a phrase using one of the above functions. * This layer also deals with a couple of generic issues. */ static int phrase(struct gdstate *gds) { if (timephrase(gds)) return 1; if (zonephrase(gds)) return 1; if (datephrase(gds)) return 1; if (dayphrase(gds)) return 1; if (relunitphrase(gds)) { if (gds->tokenp[0].token == tAGO) { gds->RelSeconds = -gds->RelSeconds; gds->RelMonth = -gds->RelMonth; gds->tokenp += 1; } return 1; } /* Bare numbers sometimes have meaning. */ if (gds->tokenp[0].token == tUNUMBER) { if (gds->HaveTime && !gds->HaveYear && !gds->HaveRel) { gds->HaveYear++; gds->Year = gds->tokenp[0].value; gds->tokenp += 1; return 1; } if(gds->tokenp[0].value > 10000) { /* "20040301" */ gds->HaveYear++; gds->HaveMonth++; gds->HaveDay++; gds->Day= (gds->tokenp[0].value)%100; gds->Month= (gds->tokenp[0].value/100)%100; gds->Year = gds->tokenp[0].value/10000; gds->tokenp += 1; return 1; } if (gds->tokenp[0].value < 24) { gds->HaveTime++; gds->Hour = gds->tokenp[0].value; gds->Minutes = 0; gds->Seconds = 0; gds->tokenp += 1; return 1; } if ((gds->tokenp[0].value / 100 < 24) && (gds->tokenp[0].value % 100 < 60)) { /* "513" is same as "5:13" */ gds->Hour = gds->tokenp[0].value / 100; gds->Minutes = gds->tokenp[0].value % 100; gds->Seconds = 0; gds->tokenp += 1; return 1; } } return 0; } /* * A dictionary of time words. */ static struct LEXICON { size_t abbrev; const char *name; int type; time_t value; } const TimeWords[] = { /* am/pm */ { 0, "am", tAMPM, tAM }, { 0, "pm", tAMPM, tPM }, /* Month names. */ { 3, "january", tMONTH, 1 }, { 3, "february", tMONTH, 2 }, { 3, "march", tMONTH, 3 }, { 3, "april", tMONTH, 4 }, { 3, "may", tMONTH, 5 }, { 3, "june", tMONTH, 6 }, { 3, "july", tMONTH, 7 }, { 3, "august", tMONTH, 8 }, { 3, "september", tMONTH, 9 }, { 3, "october", tMONTH, 10 }, { 3, "november", tMONTH, 11 }, { 3, "december", tMONTH, 12 }, /* Days of the week. */ { 2, "sunday", tDAY, 0 }, { 3, "monday", tDAY, 1 }, { 2, "tuesday", tDAY, 2 }, { 3, "wednesday", tDAY, 3 }, { 2, "thursday", tDAY, 4 }, { 2, "friday", tDAY, 5 }, { 2, "saturday", tDAY, 6 }, /* Timezones: Offsets are in seconds. */ { 0, "gmt", tZONE, 0*HOUR }, /* Greenwich Mean */ { 0, "ut", tZONE, 0*HOUR }, /* Universal (Coordinated) */ { 0, "utc", tZONE, 0*HOUR }, { 0, "wet", tZONE, 0*HOUR }, /* Western European */ { 0, "bst", tDAYZONE, 0*HOUR }, /* British Summer */ { 0, "wat", tZONE, 1*HOUR }, /* West Africa */ { 0, "at", tZONE, 2*HOUR }, /* Azores */ /* { 0, "bst", tZONE, 3*HOUR }, */ /* Brazil Standard: Conflict */ /* { 0, "gst", tZONE, 3*HOUR }, */ /* Greenland Standard: Conflict*/ { 0, "nft", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland */ { 0, "nst", tZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Standard */ { 0, "ndt", tDAYZONE, 3*HOUR+30*MINUTE }, /* Newfoundland Daylight */ { 0, "ast", tZONE, 4*HOUR }, /* Atlantic Standard */ { 0, "adt", tDAYZONE, 4*HOUR }, /* Atlantic Daylight */ { 0, "est", tZONE, 5*HOUR }, /* Eastern Standard */ { 0, "edt", tDAYZONE, 5*HOUR }, /* Eastern Daylight */ { 0, "cst", tZONE, 6*HOUR }, /* Central Standard */ { 0, "cdt", tDAYZONE, 6*HOUR }, /* Central Daylight */ { 0, "mst", tZONE, 7*HOUR }, /* Mountain Standard */ { 0, "mdt", tDAYZONE, 7*HOUR }, /* Mountain Daylight */ { 0, "pst", tZONE, 8*HOUR }, /* Pacific Standard */ { 0, "pdt", tDAYZONE, 8*HOUR }, /* Pacific Daylight */ { 0, "yst", tZONE, 9*HOUR }, /* Yukon Standard */ { 0, "ydt", tDAYZONE, 9*HOUR }, /* Yukon Daylight */ { 0, "hst", tZONE, 10*HOUR }, /* Hawaii Standard */ { 0, "hdt", tDAYZONE, 10*HOUR }, /* Hawaii Daylight */ { 0, "cat", tZONE, 10*HOUR }, /* Central Alaska */ { 0, "ahst", tZONE, 10*HOUR }, /* Alaska-Hawaii Standard */ { 0, "nt", tZONE, 11*HOUR }, /* Nome */ { 0, "idlw", tZONE, 12*HOUR }, /* Intl Date Line West */ { 0, "cet", tZONE, -1*HOUR }, /* Central European */ { 0, "met", tZONE, -1*HOUR }, /* Middle European */ { 0, "mewt", tZONE, -1*HOUR }, /* Middle European Winter */ { 0, "mest", tDAYZONE, -1*HOUR }, /* Middle European Summer */ { 0, "swt", tZONE, -1*HOUR }, /* Swedish Winter */ { 0, "sst", tDAYZONE, -1*HOUR }, /* Swedish Summer */ { 0, "fwt", tZONE, -1*HOUR }, /* French Winter */ { 0, "fst", tDAYZONE, -1*HOUR }, /* French Summer */ { 0, "eet", tZONE, -2*HOUR }, /* Eastern Eur, USSR Zone 1 */ { 0, "bt", tZONE, -3*HOUR }, /* Baghdad, USSR Zone 2 */ { 0, "it", tZONE, -3*HOUR-30*MINUTE },/* Iran */ { 0, "zp4", tZONE, -4*HOUR }, /* USSR Zone 3 */ { 0, "zp5", tZONE, -5*HOUR }, /* USSR Zone 4 */ { 0, "ist", tZONE, -5*HOUR-30*MINUTE },/* Indian Standard */ { 0, "zp6", tZONE, -6*HOUR }, /* USSR Zone 5 */ /* { 0, "nst", tZONE, -6.5*HOUR }, */ /* North Sumatra: Conflict */ /* { 0, "sst", tZONE, -7*HOUR }, */ /* So Sumatra, USSR 6: Conflict */ { 0, "wast", tZONE, -7*HOUR }, /* West Australian Standard */ { 0, "wadt", tDAYZONE, -7*HOUR }, /* West Australian Daylight */ { 0, "jt", tZONE, -7*HOUR-30*MINUTE },/* Java (3pm in Cronusland!)*/ { 0, "cct", tZONE, -8*HOUR }, /* China Coast, USSR Zone 7 */ { 0, "jst", tZONE, -9*HOUR }, /* Japan Std, USSR Zone 8 */ { 0, "cast", tZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Std */ { 0, "cadt", tDAYZONE, -9*HOUR-30*MINUTE },/* Ctrl Australian Daylt */ { 0, "east", tZONE, -10*HOUR }, /* Eastern Australian Std */ { 0, "eadt", tDAYZONE, -10*HOUR }, /* Eastern Australian Daylt */ { 0, "gst", tZONE, -10*HOUR }, /* Guam Std, USSR Zone 9 */ { 0, "nzt", tZONE, -12*HOUR }, /* New Zealand */ { 0, "nzst", tZONE, -12*HOUR }, /* New Zealand Standard */ { 0, "nzdt", tDAYZONE, -12*HOUR }, /* New Zealand Daylight */ { 0, "idle", tZONE, -12*HOUR }, /* Intl Date Line East */ { 0, "dst", tDST, 0 }, /* Time units. */ { 4, "years", tMONTH_UNIT, 12 }, { 5, "months", tMONTH_UNIT, 1 }, { 9, "fortnights", tSEC_UNIT, 14 * DAY }, { 4, "weeks", tSEC_UNIT, 7 * DAY }, { 3, "days", tSEC_UNIT, DAY }, { 4, "hours", tSEC_UNIT, HOUR }, { 3, "minutes", tSEC_UNIT, MINUTE }, { 3, "seconds", tSEC_UNIT, 1 }, /* Relative-time words. */ { 0, "tomorrow", tSEC_UNIT, DAY }, { 0, "yesterday", tSEC_UNIT, -DAY }, { 0, "today", tSEC_UNIT, 0 }, { 0, "now", tSEC_UNIT, 0 }, { 0, "last", tUNUMBER, -1 }, { 0, "this", tSEC_UNIT, 0 }, { 0, "next", tUNUMBER, 2 }, { 0, "first", tUNUMBER, 1 }, { 0, "1st", tUNUMBER, 1 }, /* { 0, "second", tUNUMBER, 2 }, */ { 0, "2nd", tUNUMBER, 2 }, { 0, "third", tUNUMBER, 3 }, { 0, "3rd", tUNUMBER, 3 }, { 0, "fourth", tUNUMBER, 4 }, { 0, "4th", tUNUMBER, 4 }, { 0, "fifth", tUNUMBER, 5 }, { 0, "5th", tUNUMBER, 5 }, { 0, "sixth", tUNUMBER, 6 }, { 0, "seventh", tUNUMBER, 7 }, { 0, "eighth", tUNUMBER, 8 }, { 0, "ninth", tUNUMBER, 9 }, { 0, "tenth", tUNUMBER, 10 }, { 0, "eleventh", tUNUMBER, 11 }, { 0, "twelfth", tUNUMBER, 12 }, { 0, "ago", tAGO, 1 }, /* Military timezones. */ { 0, "a", tZONE, 1*HOUR }, { 0, "b", tZONE, 2*HOUR }, { 0, "c", tZONE, 3*HOUR }, { 0, "d", tZONE, 4*HOUR }, { 0, "e", tZONE, 5*HOUR }, { 0, "f", tZONE, 6*HOUR }, { 0, "g", tZONE, 7*HOUR }, { 0, "h", tZONE, 8*HOUR }, { 0, "i", tZONE, 9*HOUR }, { 0, "k", tZONE, 10*HOUR }, { 0, "l", tZONE, 11*HOUR }, { 0, "m", tZONE, 12*HOUR }, { 0, "n", tZONE, -1*HOUR }, { 0, "o", tZONE, -2*HOUR }, { 0, "p", tZONE, -3*HOUR }, { 0, "q", tZONE, -4*HOUR }, { 0, "r", tZONE, -5*HOUR }, { 0, "s", tZONE, -6*HOUR }, { 0, "t", tZONE, -7*HOUR }, { 0, "u", tZONE, -8*HOUR }, { 0, "v", tZONE, -9*HOUR }, { 0, "w", tZONE, -10*HOUR }, { 0, "x", tZONE, -11*HOUR }, { 0, "y", tZONE, -12*HOUR }, { 0, "z", tZONE, 0*HOUR }, /* End of table. */ { 0, NULL, 0, 0 } }; /* * Year is either: * = A number from 0 to 99, which means a year from 1970 to 2069, or * = The actual year (>=100). */ static time_t Convert(time_t Month, time_t Day, time_t Year, time_t Hours, time_t Minutes, time_t Seconds, time_t Timezone, enum DSTMODE DSTmode) { - static int DaysInMonth[12] = { + int DaysInMonth[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; time_t Julian; int i; if (Year < 69) Year += 2000; else if (Year < 100) Year += 1900; DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 29 : 28; /* Checking for 2038 bogusly assumes that time_t is 32 bits. But I'm too lazy to try to check for time_t overflow in another way. */ if (Year < EPOCH || Year > 2038 || Month < 1 || Month > 12 /* Lint fluff: "conversion from long may lose accuracy" */ || Day < 1 || Day > DaysInMonth[(int)--Month] || Hours < 0 || Hours > 23 || Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) return -1; Julian = Day - 1; for (i = 0; i < Month; i++) Julian += DaysInMonth[i]; for (i = EPOCH; i < Year; i++) Julian += 365 + (i % 4 == 0); Julian *= DAY; Julian += Timezone; Julian += Hours * HOUR + Minutes * MINUTE + Seconds; if (DSTmode == DSTon || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) Julian -= HOUR; return Julian; } static time_t DSTcorrect(time_t Start, time_t Future) { time_t StartDay; time_t FutureDay; StartDay = (localtime(&Start)->tm_hour + 1) % 24; FutureDay = (localtime(&Future)->tm_hour + 1) % 24; return (Future - Start) + (StartDay - FutureDay) * HOUR; } static time_t RelativeDate(time_t Start, time_t zone, int dstmode, time_t DayOrdinal, time_t DayNumber) { struct tm *tm; time_t t, now; t = Start - zone; tm = gmtime(&t); now = Start; now += DAY * ((DayNumber - tm->tm_wday + 7) % 7); now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); if (dstmode == DSTmaybe) return DSTcorrect(Start, now); return now - Start; } static time_t RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth) { struct tm *tm; time_t Month; time_t Year; if (RelMonth == 0) return 0; tm = localtime(&Start); Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth; Year = Month / 12; Month = Month % 12 + 1; return DSTcorrect(Start, Convert(Month, (time_t)tm->tm_mday, Year, (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, Timezone, DSTmaybe)); } /* * Tokenizer. */ static int nexttoken(const char **in, time_t *value) { char c; char buff[64]; for ( ; ; ) { while (isspace((unsigned char)**in)) ++*in; /* Skip parenthesized comments. */ if (**in == '(') { int Count = 0; do { c = *(*in)++; if (c == '\0') return c; if (c == '(') Count++; else if (c == ')') Count--; } while (Count > 0); continue; } /* Try the next token in the word table first. */ /* This allows us to match "2nd", for example. */ { const char *src = *in; const struct LEXICON *tp; unsigned i = 0; /* Force to lowercase and strip '.' characters. */ while (*src != '\0' && (isalnum((unsigned char)*src) || *src == '.') && i < sizeof(buff)-1) { if (*src != '.') { if (isupper((unsigned char)*src)) buff[i++] = tolower((unsigned char)*src); else buff[i++] = *src; } src++; } buff[i] = '\0'; /* * Find the first match. If the word can be * abbreviated, make sure we match at least * the minimum abbreviation. */ for (tp = TimeWords; tp->name; tp++) { size_t abbrev = tp->abbrev; if (abbrev == 0) abbrev = strlen(tp->name); if (strlen(buff) >= abbrev && strncmp(tp->name, buff, strlen(buff)) == 0) { /* Skip over token. */ *in = src; /* Return the match. */ *value = tp->value; return tp->type; } } } /* * Not in the word table, maybe it's a number. Note: * Because '-' and '+' have other special meanings, I * don't deal with signed numbers here. */ if (isdigit((unsigned char)(c = **in))) { for (*value = 0; isdigit((unsigned char)(c = *(*in)++)); ) *value = 10 * *value + c - '0'; (*in)--; return (tUNUMBER); } return *(*in)++; } } #define TM_YEAR_ORIGIN 1900 /* Yield A - B, measured in seconds. */ static long difftm (struct tm *a, struct tm *b) { int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); int by = b->tm_year + (TM_YEAR_ORIGIN - 1); int days = ( /* difference in day of year */ a->tm_yday - b->tm_yday /* + intervening leap days */ + ((ay >> 2) - (by >> 2)) - (ay/100 - by/100) + ((ay/100 >> 2) - (by/100 >> 2)) /* + difference in years * 365 */ + (long)(ay-by) * 365 ); return (days * DAY + (a->tm_hour - b->tm_hour) * HOUR + (a->tm_min - b->tm_min) * MINUTE + (a->tm_sec - b->tm_sec)); } /* * * The public function. * * TODO: tokens[] array should be dynamically sized. */ time_t __archive_get_date(time_t now, const char *p) { struct token tokens[256]; struct gdstate _gds; struct token *lasttoken; struct gdstate *gds; struct tm local, *tm; struct tm gmt, *gmt_ptr; time_t Start; time_t tod; long tzone; /* Clear out the parsed token array. */ memset(tokens, 0, sizeof(tokens)); /* Initialize the parser state. */ memset(&_gds, 0, sizeof(_gds)); gds = &_gds; /* Look up the current time. */ memset(&local, 0, sizeof(local)); tm = localtime (&now); if (tm == NULL) return -1; local = *tm; /* Look up UTC if we can and use that to determine the current * timezone offset. */ memset(&gmt, 0, sizeof(gmt)); gmt_ptr = gmtime (&now); if (gmt_ptr != NULL) { /* Copy, in case localtime and gmtime use the same buffer. */ gmt = *gmt_ptr; } if (gmt_ptr != NULL) tzone = difftm (&gmt, &local); else /* This system doesn't understand timezones; fake it. */ tzone = 0; if(local.tm_isdst) tzone += HOUR; /* Tokenize the input string. */ lasttoken = tokens; while ((lasttoken->token = nexttoken(&p, &lasttoken->value)) != 0) { ++lasttoken; if (lasttoken > tokens + 255) return -1; } gds->tokenp = tokens; /* Match phrases until we run out of input tokens. */ while (gds->tokenp < lasttoken) { if (!phrase(gds)) return -1; } /* Use current local timezone if none was specified. */ if (!gds->HaveZone) { gds->Timezone = tzone; gds->DSTmode = DSTmaybe; } /* If a timezone was specified, use that for generating the default * time components instead of the local timezone. */ if (gds->HaveZone && gmt_ptr != NULL) { now -= gds->Timezone; gmt_ptr = gmtime (&now); if (gmt_ptr != NULL) local = *gmt_ptr; now += gds->Timezone; } if (!gds->HaveYear) gds->Year = local.tm_year + 1900; if (!gds->HaveMonth) gds->Month = local.tm_mon + 1; if (!gds->HaveDay) gds->Day = local.tm_mday; /* Note: No default for hour/min/sec; a specifier that just * gives date always refers to 00:00 on that date. */ /* If we saw more than one time, timezone, weekday, year, month, * or day, then give up. */ if (gds->HaveTime > 1 || gds->HaveZone > 1 || gds->HaveWeekDay > 1 || gds->HaveYear > 1 || gds->HaveMonth > 1 || gds->HaveDay > 1) return -1; /* Compute an absolute time based on whatever absolute information * we collected. */ if (gds->HaveYear || gds->HaveMonth || gds->HaveDay || gds->HaveTime || gds->HaveWeekDay) { Start = Convert(gds->Month, gds->Day, gds->Year, gds->Hour, gds->Minutes, gds->Seconds, gds->Timezone, gds->DSTmode); if (Start < 0) return -1; } else { Start = now; if (!gds->HaveRel) Start -= local.tm_hour * HOUR + local.tm_min * MINUTE + local.tm_sec; } /* Add the relative offset. */ Start += gds->RelSeconds; Start += RelativeMonth(Start, gds->Timezone, gds->RelMonth); /* Adjust for day-of-week offsets. */ if (gds->HaveWeekDay && !(gds->HaveYear || gds->HaveMonth || gds->HaveDay)) { tod = RelativeDate(Start, gds->Timezone, gds->DSTmode, gds->DayOrdinal, gds->DayNumber); Start += tod; } /* -1 is an error indicator, so return 0 instead of -1 if * that's the actual time. */ return Start == -1 ? 0 : Start; } #if defined(TEST) /* ARGSUSED */ int main(int argc, char **argv) { time_t d; time_t now = time(NULL); while (*++argv != NULL) { (void)printf("Input: %s\n", *argv); d = get_date(now, *argv); if (d == -1) (void)printf("Bad format - couldn't convert.\n"); else (void)printf("Output: %s\n", ctime(&d)); } exit(0); /* NOTREACHED */ } #endif /* defined(TEST) */ Index: head/contrib/libarchive/libarchive/archive_pack_dev.c =================================================================== --- head/contrib/libarchive/libarchive/archive_pack_dev.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_pack_dev.c (revision 316095) @@ -1,329 +1,329 @@ /* $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_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 struct format { +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: head/contrib/libarchive/libarchive/archive_read_support_format_cab.c =================================================================== --- head/contrib/libarchive/libarchive/archive_read_support_format_cab.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_read_support_format_cab.c (revision 316095) @@ -1,3353 +1,3353 @@ /*- * 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" #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_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_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_read_private.h" #include "archive_endian.h" struct lzx_dec { /* Decoding status. */ int state; /* * Window to see last decoded data, from 32KBi to 2MBi. */ int w_size; int w_mask; /* Window buffer, which is a loop buffer. */ unsigned char *w_buff; /* The insert position to the window. */ int w_pos; /* The position where we can copy decoded code from the window. */ int copy_pos; /* The length how many bytes we can copy decoded code from * the window. */ int copy_len; /* Translation reversal for x86 processor CALL byte sequence(E8). * This is used for LZX only. */ uint32_t translation_size; char translation; char block_type; #define VERBATIM_BLOCK 1 #define ALIGNED_OFFSET_BLOCK 2 #define UNCOMPRESSED_BLOCK 3 size_t block_size; size_t block_bytes_avail; /* Repeated offset. */ int r0, r1, r2; unsigned char rbytes[4]; int rbytes_avail; int length_header; int position_slot; int offset_bits; struct lzx_pos_tbl { int base; int footer_bits; } *pos_tbl; /* * Bit stream reader. */ struct lzx_br { #define CACHE_TYPE uint64_t #define CACHE_BITS (8 * sizeof(CACHE_TYPE)) /* Cache buffer. */ CACHE_TYPE cache_buffer; /* Indicates how many bits avail in cache_buffer. */ int cache_avail; unsigned char odd; char have_odd; } br; /* * Huffman coding. */ struct huffman { int len_size; int freq[17]; unsigned char *bitlen; /* * Use a index table. It's faster than searching a huffman * coding tree, which is a binary tree. But a use of a large * index table causes L1 cache read miss many times. */ #define HTBL_BITS 10 int max_bits; int shift_bits; int tbl_bits; int tree_used; int tree_avail; /* Direct access table. */ uint16_t *tbl; /* Binary tree table for extra bits over the direct access. */ struct htree_t { uint16_t left; uint16_t right; } *tree; } at, lt, mt, pt; int loop; int error; }; static const int slots[] = { 30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290 }; #define SLOT_BASE 15 #define SLOT_MAX 21/*->25*/ struct lzx_stream { 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; struct lzx_dec *ds; }; /* * Cabinet file definitions. */ /* CFHEADER offset */ #define CFHEADER_signature 0 #define CFHEADER_cbCabinet 8 #define CFHEADER_coffFiles 16 #define CFHEADER_versionMinor 24 #define CFHEADER_versionMajor 25 #define CFHEADER_cFolders 26 #define CFHEADER_cFiles 28 #define CFHEADER_flags 30 #define CFHEADER_setID 32 #define CFHEADER_iCabinet 34 #define CFHEADER_cbCFHeader 36 #define CFHEADER_cbCFFolder 38 #define CFHEADER_cbCFData 39 /* CFFOLDER offset */ #define CFFOLDER_coffCabStart 0 #define CFFOLDER_cCFData 4 #define CFFOLDER_typeCompress 6 #define CFFOLDER_abReserve 8 /* CFFILE offset */ #define CFFILE_cbFile 0 #define CFFILE_uoffFolderStart 4 #define CFFILE_iFolder 8 #define CFFILE_date_time 10 #define CFFILE_attribs 14 /* CFDATA offset */ #define CFDATA_csum 0 #define CFDATA_cbData 4 #define CFDATA_cbUncomp 6 -static const char *compression_name[] = { +static const char * const compression_name[] = { "NONE", "MSZIP", "Quantum", "LZX", }; struct cfdata { /* Sum value of this CFDATA. */ uint32_t sum; uint16_t compressed_size; uint16_t compressed_bytes_remaining; uint16_t uncompressed_size; uint16_t uncompressed_bytes_remaining; /* To know how many bytes we have decompressed. */ uint16_t uncompressed_avail; /* Offset from the beginning of compressed data of this CFDATA */ uint16_t read_offset; int64_t unconsumed; /* To keep memory image of this CFDATA to compute the sum. */ size_t memimage_size; unsigned char *memimage; /* Result of calculation of sum. */ uint32_t sum_calculated; unsigned char sum_extra[4]; int sum_extra_avail; const void *sum_ptr; }; struct cffolder { uint32_t cfdata_offset_in_cab; uint16_t cfdata_count; uint16_t comptype; #define COMPTYPE_NONE 0x0000 #define COMPTYPE_MSZIP 0x0001 #define COMPTYPE_QUANTUM 0x0002 #define COMPTYPE_LZX 0x0003 uint16_t compdata; const char *compname; /* At the time reading CFDATA */ struct cfdata cfdata; int cfdata_index; /* Flags to mark progress of decompression. */ char decompress_init; }; struct cffile { uint32_t uncompressed_size; uint32_t offset; time_t mtime; uint16_t folder; #define iFoldCONTINUED_FROM_PREV 0xFFFD #define iFoldCONTINUED_TO_NEXT 0xFFFE #define iFoldCONTINUED_PREV_AND_NEXT 0xFFFF unsigned char attr; #define ATTR_RDONLY 0x01 #define ATTR_NAME_IS_UTF 0x80 struct archive_string pathname; }; struct cfheader { /* Total bytes of all file size in a Cabinet. */ uint32_t total_bytes; uint32_t files_offset; uint16_t folder_count; uint16_t file_count; uint16_t flags; #define PREV_CABINET 0x0001 #define NEXT_CABINET 0x0002 #define RESERVE_PRESENT 0x0004 uint16_t setid; uint16_t cabinet; /* Version number. */ unsigned char major; unsigned char minor; unsigned char cffolder; unsigned char cfdata; /* All folders in a cabinet. */ struct cffolder *folder_array; /* All files in a cabinet. */ struct cffile *file_array; int file_index; }; struct cab { /* entry_bytes_remaining is the number of bytes we expect. */ int64_t entry_offset; int64_t entry_bytes_remaining; int64_t entry_unconsumed; int64_t entry_compressed_bytes_read; int64_t entry_uncompressed_bytes_read; struct cffolder *entry_cffolder; struct cffile *entry_cffile; struct cfdata *entry_cfdata; /* Offset from beginning of a cabinet file. */ int64_t cab_offset; struct cfheader cfheader; struct archive_wstring ws; /* Flag to mark progress that an archive was read their first header.*/ char found_header; char end_of_archive; char end_of_entry; char end_of_entry_cleanup; char read_data_invoked; int64_t bytes_skipped; unsigned char *uncompressed_buffer; size_t uncompressed_buffer_size; int init_default_conversion; struct archive_string_conv *sconv; struct archive_string_conv *sconv_default; struct archive_string_conv *sconv_utf8; char format_name[64]; #ifdef HAVE_ZLIB_H z_stream stream; char stream_valid; #endif struct lzx_stream xstrm; }; static int archive_read_format_cab_bid(struct archive_read *, int); static int archive_read_format_cab_options(struct archive_read *, const char *, const char *); static int archive_read_format_cab_read_header(struct archive_read *, struct archive_entry *); static int archive_read_format_cab_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_cab_read_data_skip(struct archive_read *); static int archive_read_format_cab_cleanup(struct archive_read *); static int cab_skip_sfx(struct archive_read *); static time_t cab_dos_time(const unsigned char *); static int cab_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int cab_read_header(struct archive_read *); static uint32_t cab_checksum_cfdata_4(const void *, size_t bytes, uint32_t); static uint32_t cab_checksum_cfdata(const void *, size_t bytes, uint32_t); static void cab_checksum_update(struct archive_read *, size_t); static int cab_checksum_finish(struct archive_read *); static int cab_next_cfdata(struct archive_read *); static const void *cab_read_ahead_cfdata(struct archive_read *, ssize_t *); static const void *cab_read_ahead_cfdata_none(struct archive_read *, ssize_t *); static const void *cab_read_ahead_cfdata_deflate(struct archive_read *, ssize_t *); static const void *cab_read_ahead_cfdata_lzx(struct archive_read *, ssize_t *); static int64_t cab_consume_cfdata(struct archive_read *, int64_t); static int64_t cab_minimum_consume_cfdata(struct archive_read *, int64_t); static int lzx_decode_init(struct lzx_stream *, int); static int lzx_read_blocks(struct lzx_stream *, int); static int lzx_decode_blocks(struct lzx_stream *, int); static void lzx_decode_free(struct lzx_stream *); static void lzx_translation(struct lzx_stream *, void *, size_t, uint32_t); static void lzx_cleanup_bitstream(struct lzx_stream *); static int lzx_decode(struct lzx_stream *, int); static int lzx_read_pre_tree(struct lzx_stream *); static int lzx_read_bitlen(struct lzx_stream *, struct huffman *, int); static int lzx_huffman_init(struct huffman *, size_t, int); static void lzx_huffman_free(struct huffman *); static int lzx_make_huffman_table(struct huffman *); static inline int lzx_decode_huffman(struct huffman *, unsigned); static int lzx_decode_huffman_tree(struct huffman *, unsigned, int); int archive_read_support_format_cab(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct cab *cab; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_cab"); cab = (struct cab *)calloc(1, sizeof(*cab)); if (cab == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate CAB data"); return (ARCHIVE_FATAL); } archive_string_init(&cab->ws); archive_wstring_ensure(&cab->ws, 256); r = __archive_read_register_format(a, cab, "cab", archive_read_format_cab_bid, archive_read_format_cab_options, archive_read_format_cab_read_header, archive_read_format_cab_read_data, archive_read_format_cab_read_data_skip, NULL, archive_read_format_cab_cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(cab); return (ARCHIVE_OK); } static int find_cab_magic(const char *p) { switch (p[4]) { case 0: /* * Note: Self-Extraction program has 'MSCF' string in their * program. If we were finding 'MSCF' string only, we got * wrong place for Cabinet header, thus, we have to check * following four bytes which are reserved and must be set * to zero. */ if (memcmp(p, "MSCF\0\0\0\0", 8) == 0) return 0; return 5; case 'F': return 1; case 'C': return 2; case 'S': return 3; case 'M': return 4; default: return 5; } } static int archive_read_format_cab_bid(struct archive_read *a, int best_bid) { const char *p; ssize_t bytes_avail, offset, window; /* If there's already a better bid than we can ever make, don't bother testing. */ if (best_bid > 64) return (-1); if ((p = __archive_read_ahead(a, 8, NULL)) == NULL) return (-1); if (memcmp(p, "MSCF\0\0\0\0", 8) == 0) return (64); /* * Attempt to handle self-extracting archives * by noting a PE header and searching forward * up to 128k for a 'MSCF' marker. */ if (p[0] == 'M' && p[1] == 'Z') { offset = 0; window = 4096; while (offset < (1024 * 128)) { const char *h = __archive_read_ahead(a, offset + window, &bytes_avail); if (h == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < 128) return (0); continue; } p = h + offset; while (p + 8 < h + bytes_avail) { int next; if ((next = find_cab_magic(p)) == 0) return (64); p += next; } offset = p - h; } } return (0); } static int archive_read_format_cab_options(struct archive_read *a, const char *key, const char *val) { struct cab *cab; int ret = ARCHIVE_FAILED; cab = (struct cab *)(a->format->data); if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "cab: hdrcharset option needs a character-set name"); else { cab->sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (cab->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 cab_skip_sfx(struct archive_read *a) { const char *p, *q; size_t skip; ssize_t bytes, window; window = 4096; for (;;) { const char *h = __archive_read_ahead(a, window, &bytes); if (h == NULL) { /* Remaining size are less than window. */ window >>= 1; if (window < 128) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Couldn't find out CAB header"); return (ARCHIVE_FATAL); } continue; } p = h; q = p + bytes; /* * Scan ahead until we find something that looks * like the cab header. */ while (p + 8 < q) { int next; if ((next = find_cab_magic(p)) == 0) { skip = p - h; __archive_read_consume(a, skip); return (ARCHIVE_OK); } p += next; } skip = p - h; __archive_read_consume(a, skip); } } static int truncated_error(struct archive_read *a) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated CAB header"); return (ARCHIVE_FATAL); } static ssize_t cab_strnlen(const unsigned char *p, size_t maxlen) { size_t i; for (i = 0; i <= maxlen; i++) { if (p[i] == 0) break; } if (i > maxlen) return (-1);/* invalid */ return ((ssize_t)i); } /* Read bytes as much as remaining. */ static const void * cab_read_ahead_remaining(struct archive_read *a, size_t min, ssize_t *avail) { const void *p; while (min > 0) { p = __archive_read_ahead(a, min, avail); if (p != NULL) return (p); min--; } return (NULL); } /* Convert a path separator '\' -> '/' */ static int cab_convert_path_separator_1(struct archive_string *fn, unsigned char attr) { size_t i; int mb; /* Easy check if we have '\' in multi-byte string. */ mb = 0; for (i = 0; i < archive_strlen(fn); i++) { if (fn->s[i] == '\\') { if (mb) { /* This may be second byte of multi-byte * character. */ break; } fn->s[i] = '/'; mb = 0; } else if ((fn->s[i] & 0x80) && !(attr & ATTR_NAME_IS_UTF)) mb = 1; else mb = 0; } if (i == archive_strlen(fn)) return (0); return (-1); } /* * Replace a character '\' with '/' in wide character. */ static void cab_convert_path_separator_2(struct cab *cab, struct archive_entry *entry) { const wchar_t *wp; size_t i; /* If a conversion to wide character failed, force the replacement. */ if ((wp = archive_entry_pathname_w(entry)) != NULL) { archive_wstrcpy(&(cab->ws), wp); for (i = 0; i < archive_strlen(&(cab->ws)); i++) { if (cab->ws.s[i] == L'\\') cab->ws.s[i] = L'/'; } archive_entry_copy_pathname_w(entry, cab->ws.s); } } /* * Read CFHEADER, CFFOLDER and CFFILE. */ static int cab_read_header(struct archive_read *a) { const unsigned char *p; struct cab *cab; struct cfheader *hd; size_t bytes, used; ssize_t len; int64_t skip; int err, i; int cur_folder, prev_folder; uint32_t offset32; a->archive.archive_format = ARCHIVE_FORMAT_CAB; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "CAB"; if ((p = __archive_read_ahead(a, 42, NULL)) == NULL) return (truncated_error(a)); cab = (struct cab *)(a->format->data); if (cab->found_header == 0 && p[0] == 'M' && p[1] == 'Z') { /* This is an executable? Must be self-extracting... */ err = cab_skip_sfx(a); if (err < ARCHIVE_WARN) return (err); /* Re-read header after processing the SFX. */ if ((p = __archive_read_ahead(a, 42, NULL)) == NULL) return (truncated_error(a)); } cab->cab_offset = 0; /* * Read CFHEADER. */ hd = &cab->cfheader; if (p[CFHEADER_signature+0] != 'M' || p[CFHEADER_signature+1] != 'S' || p[CFHEADER_signature+2] != 'C' || p[CFHEADER_signature+3] != 'F') { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Couldn't find out CAB header"); return (ARCHIVE_FATAL); } hd->total_bytes = archive_le32dec(p + CFHEADER_cbCabinet); hd->files_offset = archive_le32dec(p + CFHEADER_coffFiles); hd->minor = p[CFHEADER_versionMinor]; hd->major = p[CFHEADER_versionMajor]; hd->folder_count = archive_le16dec(p + CFHEADER_cFolders); if (hd->folder_count == 0) goto invalid; hd->file_count = archive_le16dec(p + CFHEADER_cFiles); if (hd->file_count == 0) goto invalid; hd->flags = archive_le16dec(p + CFHEADER_flags); hd->setid = archive_le16dec(p + CFHEADER_setID); hd->cabinet = archive_le16dec(p + CFHEADER_iCabinet); used = CFHEADER_iCabinet + 2; if (hd->flags & RESERVE_PRESENT) { uint16_t cfheader; cfheader = archive_le16dec(p + CFHEADER_cbCFHeader); if (cfheader > 60000U) goto invalid; hd->cffolder = p[CFHEADER_cbCFFolder]; hd->cfdata = p[CFHEADER_cbCFData]; used += 4;/* cbCFHeader, cbCFFolder and cbCFData */ used += cfheader;/* abReserve */ } else hd->cffolder = 0;/* Avoid compiling warning. */ if (hd->flags & PREV_CABINET) { /* How many bytes are used for szCabinetPrev. */ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) return (truncated_error(a)); if ((len = cab_strnlen(p + used, 255)) <= 0) goto invalid; used += len + 1; /* How many bytes are used for szDiskPrev. */ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) return (truncated_error(a)); if ((len = cab_strnlen(p + used, 255)) <= 0) goto invalid; used += len + 1; } if (hd->flags & NEXT_CABINET) { /* How many bytes are used for szCabinetNext. */ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) return (truncated_error(a)); if ((len = cab_strnlen(p + used, 255)) <= 0) goto invalid; used += len + 1; /* How many bytes are used for szDiskNext. */ if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL) return (truncated_error(a)); if ((len = cab_strnlen(p + used, 255)) <= 0) goto invalid; used += len + 1; } __archive_read_consume(a, used); cab->cab_offset += used; used = 0; /* * Read CFFOLDER. */ hd->folder_array = (struct cffolder *)calloc( hd->folder_count, sizeof(struct cffolder)); if (hd->folder_array == NULL) goto nomem; bytes = 8; if (hd->flags & RESERVE_PRESENT) bytes += hd->cffolder; bytes *= hd->folder_count; if ((p = __archive_read_ahead(a, bytes, NULL)) == NULL) return (truncated_error(a)); offset32 = 0; for (i = 0; i < hd->folder_count; i++) { struct cffolder *folder = &(hd->folder_array[i]); folder->cfdata_offset_in_cab = archive_le32dec(p + CFFOLDER_coffCabStart); folder->cfdata_count = archive_le16dec(p+CFFOLDER_cCFData); folder->comptype = archive_le16dec(p+CFFOLDER_typeCompress) & 0x0F; folder->compdata = archive_le16dec(p+CFFOLDER_typeCompress) >> 8; /* Get a compression name. */ if (folder->comptype < sizeof(compression_name) / sizeof(compression_name[0])) folder->compname = compression_name[folder->comptype]; else folder->compname = "UNKNOWN"; p += 8; used += 8; if (hd->flags & RESERVE_PRESENT) { p += hd->cffolder;/* abReserve */ used += hd->cffolder; } /* * Sanity check if each data is acceptable. */ if (offset32 >= folder->cfdata_offset_in_cab) goto invalid; offset32 = folder->cfdata_offset_in_cab; /* Set a request to initialize zlib for the CFDATA of * this folder. */ folder->decompress_init = 0; } __archive_read_consume(a, used); cab->cab_offset += used; /* * Read CFFILE. */ /* Seek read pointer to the offset of CFFILE if needed. */ skip = (int64_t)hd->files_offset - cab->cab_offset; if (skip < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid offset of CFFILE %jd < %jd", (intmax_t)hd->files_offset, (intmax_t)cab->cab_offset); return (ARCHIVE_FATAL); } if (skip) { __archive_read_consume(a, skip); cab->cab_offset += skip; } /* Allocate memory for CFDATA */ hd->file_array = (struct cffile *)calloc( hd->file_count, sizeof(struct cffile)); if (hd->file_array == NULL) goto nomem; prev_folder = -1; for (i = 0; i < hd->file_count; i++) { struct cffile *file = &(hd->file_array[i]); ssize_t avail; if ((p = __archive_read_ahead(a, 16, NULL)) == NULL) return (truncated_error(a)); file->uncompressed_size = archive_le32dec(p + CFFILE_cbFile); file->offset = archive_le32dec(p + CFFILE_uoffFolderStart); file->folder = archive_le16dec(p + CFFILE_iFolder); file->mtime = cab_dos_time(p + CFFILE_date_time); file->attr = (uint8_t)archive_le16dec(p + CFFILE_attribs); __archive_read_consume(a, 16); cab->cab_offset += 16; if ((p = cab_read_ahead_remaining(a, 256, &avail)) == NULL) return (truncated_error(a)); if ((len = cab_strnlen(p, avail-1)) <= 0) goto invalid; /* Copy a pathname. */ archive_string_init(&(file->pathname)); archive_strncpy(&(file->pathname), p, len); __archive_read_consume(a, len + 1); cab->cab_offset += len + 1; /* * Sanity check if each data is acceptable. */ if (file->uncompressed_size > 0x7FFF8000) goto invalid;/* Too large */ if ((int64_t)file->offset + (int64_t)file->uncompressed_size > ARCHIVE_LITERAL_LL(0x7FFF8000)) goto invalid;/* Too large */ switch (file->folder) { case iFoldCONTINUED_TO_NEXT: /* This must be last file in a folder. */ if (i != hd->file_count -1) goto invalid; cur_folder = hd->folder_count -1; break; case iFoldCONTINUED_PREV_AND_NEXT: /* This must be only one file in a folder. */ if (hd->file_count != 1) goto invalid; /* FALL THROUGH */ case iFoldCONTINUED_FROM_PREV: /* This must be first file in a folder. */ if (i != 0) goto invalid; prev_folder = cur_folder = 0; offset32 = file->offset; break; default: if (file->folder >= hd->folder_count) goto invalid; cur_folder = file->folder; break; } /* Dot not back track. */ if (cur_folder < prev_folder) goto invalid; if (cur_folder != prev_folder) offset32 = 0; prev_folder = cur_folder; /* Make sure there are not any blanks from last file * contents. */ if (offset32 != file->offset) goto invalid; offset32 += file->uncompressed_size; /* CFDATA is available for file contents. */ if (file->uncompressed_size > 0 && hd->folder_array[cur_folder].cfdata_count == 0) goto invalid; } if (hd->cabinet != 0 || hd->flags & (PREV_CABINET | NEXT_CABINET)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Multivolume cabinet file is unsupported"); return (ARCHIVE_WARN); } return (ARCHIVE_OK); invalid: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CAB header"); return (ARCHIVE_FATAL); nomem: archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for CAB data"); return (ARCHIVE_FATAL); } static int archive_read_format_cab_read_header(struct archive_read *a, struct archive_entry *entry) { struct cab *cab; struct cfheader *hd; struct cffolder *prev_folder; struct cffile *file; struct archive_string_conv *sconv; int err = ARCHIVE_OK, r; cab = (struct cab *)(a->format->data); if (cab->found_header == 0) { err = cab_read_header(a); if (err < ARCHIVE_WARN) return (err); /* We've found the header. */ cab->found_header = 1; } hd = &cab->cfheader; if (hd->file_index >= hd->file_count) { cab->end_of_archive = 1; return (ARCHIVE_EOF); } file = &hd->file_array[hd->file_index++]; cab->end_of_entry = 0; cab->end_of_entry_cleanup = 0; cab->entry_compressed_bytes_read = 0; cab->entry_uncompressed_bytes_read = 0; cab->entry_unconsumed = 0; cab->entry_cffile = file; /* * Choose a proper folder. */ prev_folder = cab->entry_cffolder; switch (file->folder) { case iFoldCONTINUED_FROM_PREV: case iFoldCONTINUED_PREV_AND_NEXT: cab->entry_cffolder = &hd->folder_array[0]; break; case iFoldCONTINUED_TO_NEXT: cab->entry_cffolder = &hd->folder_array[hd->folder_count-1]; break; default: cab->entry_cffolder = &hd->folder_array[file->folder]; break; } /* If a cffolder of this file is changed, reset a cfdata to read * file contents from next cfdata. */ if (prev_folder != cab->entry_cffolder) cab->entry_cfdata = NULL; /* If a pathname is UTF-8, prepare a string conversion object * for UTF-8 and use it. */ if (file->attr & ATTR_NAME_IS_UTF) { if (cab->sconv_utf8 == NULL) { cab->sconv_utf8 = archive_string_conversion_from_charset( &(a->archive), "UTF-8", 1); if (cab->sconv_utf8 == NULL) return (ARCHIVE_FATAL); } sconv = cab->sconv_utf8; } else if (cab->sconv != NULL) { /* Choose the conversion specified by the option. */ sconv = cab->sconv; } else { /* Choose the default conversion. */ if (!cab->init_default_conversion) { cab->sconv_default = archive_string_default_conversion_for_read( &(a->archive)); cab->init_default_conversion = 1; } sconv = cab->sconv_default; } /* * Set a default value and common data */ r = cab_convert_path_separator_1(&(file->pathname), file->attr); if (archive_entry_copy_pathname_l(entry, file->pathname.s, archive_strlen(&(file->pathname)), 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)); err = ARCHIVE_WARN; } if (r < 0) { /* Convert a path separator '\' -> '/' */ cab_convert_path_separator_2(cab, entry); } archive_entry_set_size(entry, file->uncompressed_size); if (file->attr & ATTR_RDONLY) archive_entry_set_mode(entry, AE_IFREG | 0555); else archive_entry_set_mode(entry, AE_IFREG | 0666); archive_entry_set_mtime(entry, file->mtime, 0); cab->entry_bytes_remaining = file->uncompressed_size; cab->entry_offset = 0; /* We don't need compress data. */ if (file->uncompressed_size == 0) cab->end_of_entry_cleanup = cab->end_of_entry = 1; /* Set up a more descriptive format name. */ sprintf(cab->format_name, "CAB %d.%d (%s)", hd->major, hd->minor, cab->entry_cffolder->compname); a->archive.archive_format_name = cab->format_name; return (err); } static int archive_read_format_cab_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct cab *cab = (struct cab *)(a->format->data); int r; switch (cab->entry_cffile->folder) { case iFoldCONTINUED_FROM_PREV: case iFoldCONTINUED_TO_NEXT: case iFoldCONTINUED_PREV_AND_NEXT: *buff = NULL; *size = 0; *offset = 0; archive_clear_error(&a->archive); archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Cannot restore this file split in multivolume."); return (ARCHIVE_FAILED); default: break; } if (cab->read_data_invoked == 0) { if (cab->bytes_skipped) { if (cab->entry_cfdata == NULL) { r = cab_next_cfdata(a); if (r < 0) return (r); } if (cab_consume_cfdata(a, cab->bytes_skipped) < 0) return (ARCHIVE_FATAL); cab->bytes_skipped = 0; } cab->read_data_invoked = 1; } if (cab->entry_unconsumed) { /* Consume as much as the compressor actually used. */ r = (int)cab_consume_cfdata(a, cab->entry_unconsumed); cab->entry_unconsumed = 0; if (r < 0) return (r); } if (cab->end_of_archive || cab->end_of_entry) { if (!cab->end_of_entry_cleanup) { /* End-of-entry cleanup done. */ cab->end_of_entry_cleanup = 1; } *offset = cab->entry_offset; *size = 0; *buff = NULL; return (ARCHIVE_EOF); } return (cab_read_data(a, buff, size, offset)); } static uint32_t cab_checksum_cfdata_4(const void *p, size_t bytes, uint32_t seed) { const unsigned char *b; unsigned u32num; uint32_t sum; u32num = (unsigned)bytes / 4; sum = seed; b = p; for (;u32num > 0; --u32num) { sum ^= archive_le32dec(b); b += 4; } return (sum); } static uint32_t cab_checksum_cfdata(const void *p, size_t bytes, uint32_t seed) { const unsigned char *b; uint32_t sum; uint32_t t; sum = cab_checksum_cfdata_4(p, bytes, seed); b = p; b += bytes & ~3; t = 0; switch (bytes & 3) { case 3: t |= ((uint32_t)(*b++)) << 16; /* FALL THROUGH */ case 2: t |= ((uint32_t)(*b++)) << 8; /* FALL THROUGH */ case 1: t |= *b; /* FALL THROUGH */ default: break; } sum ^= t; return (sum); } static void cab_checksum_update(struct archive_read *a, size_t bytes) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata = cab->entry_cfdata; const unsigned char *p; size_t sumbytes; if (cfdata->sum == 0 || cfdata->sum_ptr == NULL) return; /* * Calculate the sum of this CFDATA. * Make sure CFDATA must be calculated in four bytes. */ p = cfdata->sum_ptr; sumbytes = bytes; if (cfdata->sum_extra_avail) { while (cfdata->sum_extra_avail < 4 && sumbytes > 0) { cfdata->sum_extra[ cfdata->sum_extra_avail++] = *p++; sumbytes--; } if (cfdata->sum_extra_avail == 4) { cfdata->sum_calculated = cab_checksum_cfdata_4( cfdata->sum_extra, 4, cfdata->sum_calculated); cfdata->sum_extra_avail = 0; } } if (sumbytes) { int odd = sumbytes & 3; if (sumbytes - odd > 0) cfdata->sum_calculated = cab_checksum_cfdata_4( p, sumbytes - odd, cfdata->sum_calculated); if (odd) memcpy(cfdata->sum_extra, p + sumbytes - odd, odd); cfdata->sum_extra_avail = odd; } cfdata->sum_ptr = NULL; } static int cab_checksum_finish(struct archive_read *a) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata = cab->entry_cfdata; int l; /* Do not need to compute a sum. */ if (cfdata->sum == 0) return (ARCHIVE_OK); /* * Calculate the sum of remaining CFDATA. */ if (cfdata->sum_extra_avail) { cfdata->sum_calculated = cab_checksum_cfdata(cfdata->sum_extra, cfdata->sum_extra_avail, cfdata->sum_calculated); cfdata->sum_extra_avail = 0; } l = 4; if (cab->cfheader.flags & RESERVE_PRESENT) l += cab->cfheader.cfdata; cfdata->sum_calculated = cab_checksum_cfdata( cfdata->memimage + CFDATA_cbData, l, cfdata->sum_calculated); if (cfdata->sum_calculated != cfdata->sum) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Checksum error CFDATA[%d] %x:%x in %d bytes", cab->entry_cffolder->cfdata_index -1, cfdata->sum, cfdata->sum_calculated, cfdata->compressed_size); return (ARCHIVE_FAILED); } return (ARCHIVE_OK); } /* * Read CFDATA if needed. */ static int cab_next_cfdata(struct archive_read *a) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata = cab->entry_cfdata; /* There are remaining bytes in current CFDATA, use it first. */ if (cfdata != NULL && cfdata->uncompressed_bytes_remaining > 0) return (ARCHIVE_OK); if (cfdata == NULL) { int64_t skip; cab->entry_cffolder->cfdata_index = 0; /* Seek read pointer to the offset of CFDATA if needed. */ skip = cab->entry_cffolder->cfdata_offset_in_cab - cab->cab_offset; if (skip < 0) { int folder_index; switch (cab->entry_cffile->folder) { case iFoldCONTINUED_FROM_PREV: case iFoldCONTINUED_PREV_AND_NEXT: folder_index = 0; break; case iFoldCONTINUED_TO_NEXT: folder_index = cab->cfheader.folder_count-1; break; default: folder_index = cab->entry_cffile->folder; break; } archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid offset of CFDATA in folder(%d) %jd < %jd", folder_index, (intmax_t)cab->entry_cffolder->cfdata_offset_in_cab, (intmax_t)cab->cab_offset); return (ARCHIVE_FATAL); } if (skip > 0) { if (__archive_read_consume(a, skip) < 0) return (ARCHIVE_FATAL); cab->cab_offset = cab->entry_cffolder->cfdata_offset_in_cab; } } /* * Read a CFDATA. */ if (cab->entry_cffolder->cfdata_index < cab->entry_cffolder->cfdata_count) { const unsigned char *p; int l; cfdata = &(cab->entry_cffolder->cfdata); cab->entry_cffolder->cfdata_index++; cab->entry_cfdata = cfdata; cfdata->sum_calculated = 0; cfdata->sum_extra_avail = 0; cfdata->sum_ptr = NULL; l = 8; if (cab->cfheader.flags & RESERVE_PRESENT) l += cab->cfheader.cfdata; if ((p = __archive_read_ahead(a, l, NULL)) == NULL) return (truncated_error(a)); cfdata->sum = archive_le32dec(p + CFDATA_csum); cfdata->compressed_size = archive_le16dec(p + CFDATA_cbData); cfdata->compressed_bytes_remaining = cfdata->compressed_size; cfdata->uncompressed_size = archive_le16dec(p + CFDATA_cbUncomp); cfdata->uncompressed_bytes_remaining = cfdata->uncompressed_size; cfdata->uncompressed_avail = 0; cfdata->read_offset = 0; cfdata->unconsumed = 0; /* * Sanity check if data size is acceptable. */ if (cfdata->compressed_size == 0 || cfdata->compressed_size > (0x8000+6144)) goto invalid; if (cfdata->uncompressed_size > 0x8000) goto invalid; if (cfdata->uncompressed_size == 0) { switch (cab->entry_cffile->folder) { case iFoldCONTINUED_PREV_AND_NEXT: case iFoldCONTINUED_TO_NEXT: break; case iFoldCONTINUED_FROM_PREV: default: goto invalid; } } /* If CFDATA is not last in a folder, an uncompressed * size must be 0x8000(32KBi) */ if ((cab->entry_cffolder->cfdata_index < cab->entry_cffolder->cfdata_count) && cfdata->uncompressed_size != 0x8000) goto invalid; /* A compressed data size and an uncompressed data size must * be the same in no compression mode. */ if (cab->entry_cffolder->comptype == COMPTYPE_NONE && cfdata->compressed_size != cfdata->uncompressed_size) goto invalid; /* * Save CFDATA image for sum check. */ if (cfdata->memimage_size < (size_t)l) { free(cfdata->memimage); cfdata->memimage = malloc(l); if (cfdata->memimage == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for CAB data"); return (ARCHIVE_FATAL); } cfdata->memimage_size = l; } memcpy(cfdata->memimage, p, l); /* Consume bytes as much as we used. */ __archive_read_consume(a, l); cab->cab_offset += l; } else if (cab->entry_cffolder->cfdata_count > 0) { /* Run out of all CFDATA in a folder. */ cfdata->compressed_size = 0; cfdata->uncompressed_size = 0; cfdata->compressed_bytes_remaining = 0; cfdata->uncompressed_bytes_remaining = 0; } else { /* Current folder does not have any CFDATA. */ cfdata = &(cab->entry_cffolder->cfdata); cab->entry_cfdata = cfdata; memset(cfdata, 0, sizeof(*cfdata)); } return (ARCHIVE_OK); invalid: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA"); return (ARCHIVE_FATAL); } /* * Read ahead CFDATA. */ static const void * cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail) { struct cab *cab = (struct cab *)(a->format->data); int err; err = cab_next_cfdata(a); if (err < ARCHIVE_OK) { *avail = err; return (NULL); } switch (cab->entry_cffolder->comptype) { case COMPTYPE_NONE: return (cab_read_ahead_cfdata_none(a, avail)); case COMPTYPE_MSZIP: return (cab_read_ahead_cfdata_deflate(a, avail)); case COMPTYPE_LZX: return (cab_read_ahead_cfdata_lzx(a, avail)); default: /* Unsupported compression. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported CAB compression : %s", cab->entry_cffolder->compname); *avail = ARCHIVE_FAILED; return (NULL); } } /* * Read ahead CFDATA as uncompressed data. */ static const void * cab_read_ahead_cfdata_none(struct archive_read *a, ssize_t *avail) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata; const void *d; cfdata = cab->entry_cfdata; /* * 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. */ d = __archive_read_ahead(a, 1, avail); if (*avail <= 0) { *avail = truncated_error(a); return (NULL); } if (*avail > cfdata->uncompressed_bytes_remaining) *avail = cfdata->uncompressed_bytes_remaining; cfdata->uncompressed_avail = cfdata->uncompressed_size; cfdata->unconsumed = *avail; cfdata->sum_ptr = d; return (d); } /* * Read ahead CFDATA as deflate data. */ #ifdef HAVE_ZLIB_H static const void * cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata; const void *d; int r, mszip; uint16_t uavail; char eod = 0; cfdata = cab->entry_cfdata; /* If the buffer hasn't been allocated, allocate it now. */ if (cab->uncompressed_buffer == NULL) { cab->uncompressed_buffer_size = 0x8000; cab->uncompressed_buffer = (unsigned char *)malloc(cab->uncompressed_buffer_size); if (cab->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for CAB reader"); *avail = ARCHIVE_FATAL; return (NULL); } } uavail = cfdata->uncompressed_avail; if (uavail == cfdata->uncompressed_size) { d = cab->uncompressed_buffer + cfdata->read_offset; *avail = uavail - cfdata->read_offset; return (d); } if (!cab->entry_cffolder->decompress_init) { cab->stream.next_in = NULL; cab->stream.avail_in = 0; cab->stream.total_in = 0; cab->stream.next_out = NULL; cab->stream.avail_out = 0; cab->stream.total_out = 0; if (cab->stream_valid) r = inflateReset(&cab->stream); else r = inflateInit2(&cab->stream, -15 /* Don't check for zlib header */); if (r != Z_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't initialize deflate decompression."); *avail = ARCHIVE_FATAL; return (NULL); } /* Stream structure has been set up. */ cab->stream_valid = 1; /* We've initialized decompression for this stream. */ cab->entry_cffolder->decompress_init = 1; } if (cfdata->compressed_bytes_remaining == cfdata->compressed_size) mszip = 2; else mszip = 0; eod = 0; cab->stream.total_out = uavail; /* * We always uncompress all data in current CFDATA. */ while (!eod && cab->stream.total_out < cfdata->uncompressed_size) { ssize_t bytes_avail; cab->stream.next_out = cab->uncompressed_buffer + cab->stream.total_out; cab->stream.avail_out = cfdata->uncompressed_size - cab->stream.total_out; d = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { *avail = truncated_error(a); return (NULL); } if (bytes_avail > cfdata->compressed_bytes_remaining) bytes_avail = cfdata->compressed_bytes_remaining; /* * 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'. */ cab->stream.next_in = (Bytef *)(uintptr_t)d; cab->stream.avail_in = (uInt)bytes_avail; cab->stream.total_in = 0; /* Cut out a tow-byte MSZIP signature(0x43, 0x4b). */ if (mszip > 0) { if (bytes_avail <= 0) goto nomszip; if (bytes_avail <= mszip) { if (mszip == 2) { if (cab->stream.next_in[0] != 0x43) goto nomszip; if (bytes_avail > 1 && cab->stream.next_in[1] != 0x4b) goto nomszip; } else if (cab->stream.next_in[0] != 0x4b) goto nomszip; cfdata->unconsumed = bytes_avail; cfdata->sum_ptr = d; if (cab_minimum_consume_cfdata( a, cfdata->unconsumed) < 0) { *avail = ARCHIVE_FATAL; return (NULL); } mszip -= (int)bytes_avail; continue; } if (mszip == 1 && cab->stream.next_in[0] != 0x4b) goto nomszip; else if (cab->stream.next_in[0] != 0x43 || cab->stream.next_in[1] != 0x4b) goto nomszip; cab->stream.next_in += mszip; cab->stream.avail_in -= mszip; cab->stream.total_in += mszip; mszip = 0; } r = inflate(&cab->stream, 0); switch (r) { case Z_OK: break; case Z_STREAM_END: eod = 1; break; default: goto zlibfailed; } cfdata->unconsumed = cab->stream.total_in; cfdata->sum_ptr = d; if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) { *avail = ARCHIVE_FATAL; return (NULL); } } uavail = (uint16_t)cab->stream.total_out; if (uavail < cfdata->uncompressed_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid uncompressed size (%d < %d)", uavail, cfdata->uncompressed_size); *avail = ARCHIVE_FATAL; return (NULL); } /* * Note: I suspect there is a bug in makecab.exe because, in rare * case, compressed bytes are still remaining regardless we have * gotten all uncompressed bytes, which size is recorded in CFDATA, * as much as we need, and we have to use the garbage so as to * correctly compute the sum of CFDATA accordingly. */ if (cfdata->compressed_bytes_remaining > 0) { ssize_t bytes_avail; d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining, &bytes_avail); if (bytes_avail <= 0) { *avail = truncated_error(a); return (NULL); } cfdata->unconsumed = cfdata->compressed_bytes_remaining; cfdata->sum_ptr = d; if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) { *avail = ARCHIVE_FATAL; return (NULL); } } /* * Set dictionary data for decompressing of next CFDATA, which * in the same folder. This is why we always do decompress CFDATA * even if beginning CFDATA or some of CFDATA are not used in * skipping file data. */ if (cab->entry_cffolder->cfdata_index < cab->entry_cffolder->cfdata_count) { r = inflateReset(&cab->stream); if (r != Z_OK) goto zlibfailed; r = inflateSetDictionary(&cab->stream, cab->uncompressed_buffer, cfdata->uncompressed_size); if (r != Z_OK) goto zlibfailed; } d = cab->uncompressed_buffer + cfdata->read_offset; *avail = uavail - cfdata->read_offset; cfdata->uncompressed_avail = uavail; return (d); zlibfailed: switch (r) { case Z_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Out of memory for deflate decompression"); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Deflate decompression failed (%d)", r); break; } *avail = ARCHIVE_FATAL; return (NULL); nomszip: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "CFDATA incorrect(no MSZIP signature)"); *avail = ARCHIVE_FATAL; return (NULL); } #else /* HAVE_ZLIB_H */ static const void * cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) { *avail = ARCHIVE_FATAL; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "libarchive compiled without deflate support (no libz)"); return (NULL); } #endif /* HAVE_ZLIB_H */ static const void * cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata; const void *d; int r; uint16_t uavail; cfdata = cab->entry_cfdata; /* If the buffer hasn't been allocated, allocate it now. */ if (cab->uncompressed_buffer == NULL) { cab->uncompressed_buffer_size = 0x8000; cab->uncompressed_buffer = (unsigned char *)malloc(cab->uncompressed_buffer_size); if (cab->uncompressed_buffer == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for CAB reader"); *avail = ARCHIVE_FATAL; return (NULL); } } uavail = cfdata->uncompressed_avail; if (uavail == cfdata->uncompressed_size) { d = cab->uncompressed_buffer + cfdata->read_offset; *avail = uavail - cfdata->read_offset; return (d); } if (!cab->entry_cffolder->decompress_init) { r = lzx_decode_init(&cab->xstrm, cab->entry_cffolder->compdata); if (r != ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't initialize LZX decompression."); *avail = ARCHIVE_FATAL; return (NULL); } /* We've initialized decompression for this stream. */ cab->entry_cffolder->decompress_init = 1; } /* Clean up remaining bits of previous CFDATA. */ lzx_cleanup_bitstream(&cab->xstrm); cab->xstrm.total_out = uavail; while (cab->xstrm.total_out < cfdata->uncompressed_size) { ssize_t bytes_avail; cab->xstrm.next_out = cab->uncompressed_buffer + cab->xstrm.total_out; cab->xstrm.avail_out = cfdata->uncompressed_size - cab->xstrm.total_out; d = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated CAB file data"); *avail = ARCHIVE_FATAL; return (NULL); } if (bytes_avail > cfdata->compressed_bytes_remaining) bytes_avail = cfdata->compressed_bytes_remaining; cab->xstrm.next_in = d; cab->xstrm.avail_in = bytes_avail; cab->xstrm.total_in = 0; r = lzx_decode(&cab->xstrm, cfdata->compressed_bytes_remaining == bytes_avail); switch (r) { case ARCHIVE_OK: case ARCHIVE_EOF: break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LZX decompression failed (%d)", r); *avail = ARCHIVE_FATAL; return (NULL); } cfdata->unconsumed = cab->xstrm.total_in; cfdata->sum_ptr = d; if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) { *avail = ARCHIVE_FATAL; return (NULL); } } uavail = (uint16_t)cab->xstrm.total_out; /* * Make sure a read pointer advances to next CFDATA. */ if (cfdata->compressed_bytes_remaining > 0) { ssize_t bytes_avail; d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining, &bytes_avail); if (bytes_avail <= 0) { *avail = truncated_error(a); return (NULL); } cfdata->unconsumed = cfdata->compressed_bytes_remaining; cfdata->sum_ptr = d; if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) { *avail = ARCHIVE_FATAL; return (NULL); } } /* * Translation reversal of x86 processor CALL byte sequence(E8). */ lzx_translation(&cab->xstrm, cab->uncompressed_buffer, cfdata->uncompressed_size, (cab->entry_cffolder->cfdata_index-1) * 0x8000); d = cab->uncompressed_buffer + cfdata->read_offset; *avail = uavail - cfdata->read_offset; cfdata->uncompressed_avail = uavail; return (d); } /* * Consume CFDATA. * We always decompress CFDATA to consume CFDATA as much as we need * in uncompressed bytes because all CFDATA in a folder are related * so we do not skip any CFDATA without decompressing. * Note: If the folder of a CFFILE is iFoldCONTINUED_PREV_AND_NEXT or * iFoldCONTINUED_FROM_PREV, we won't decompress because a CFDATA for * the CFFILE is remaining bytes of previous Multivolume CAB file. */ static int64_t cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata; int64_t cbytes, rbytes; int err; rbytes = cab_minimum_consume_cfdata(a, consumed_bytes); if (rbytes < 0) return (ARCHIVE_FATAL); cfdata = cab->entry_cfdata; while (rbytes > 0) { ssize_t avail; if (cfdata->compressed_size == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA"); return (ARCHIVE_FATAL); } cbytes = cfdata->uncompressed_bytes_remaining; if (cbytes > rbytes) cbytes = rbytes; rbytes -= cbytes; if (cfdata->uncompressed_avail == 0 && (cab->entry_cffile->folder == iFoldCONTINUED_PREV_AND_NEXT || cab->entry_cffile->folder == iFoldCONTINUED_FROM_PREV)) { /* We have not read any data yet. */ if (cbytes == cfdata->uncompressed_bytes_remaining) { /* Skip whole current CFDATA. */ __archive_read_consume(a, cfdata->compressed_size); cab->cab_offset += cfdata->compressed_size; cfdata->compressed_bytes_remaining = 0; cfdata->uncompressed_bytes_remaining = 0; err = cab_next_cfdata(a); if (err < 0) return (err); cfdata = cab->entry_cfdata; if (cfdata->uncompressed_size == 0) { switch (cab->entry_cffile->folder) { case iFoldCONTINUED_PREV_AND_NEXT: case iFoldCONTINUED_TO_NEXT: case iFoldCONTINUED_FROM_PREV: rbytes = 0; break; default: break; } } continue; } cfdata->read_offset += (uint16_t)cbytes; cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes; break; } else if (cbytes == 0) { err = cab_next_cfdata(a); if (err < 0) return (err); cfdata = cab->entry_cfdata; if (cfdata->uncompressed_size == 0) { switch (cab->entry_cffile->folder) { case iFoldCONTINUED_PREV_AND_NEXT: case iFoldCONTINUED_TO_NEXT: case iFoldCONTINUED_FROM_PREV: return (ARCHIVE_FATAL); default: break; } } continue; } while (cbytes > 0) { (void)cab_read_ahead_cfdata(a, &avail); if (avail <= 0) return (ARCHIVE_FATAL); if (avail > cbytes) avail = (ssize_t)cbytes; if (cab_minimum_consume_cfdata(a, avail) < 0) return (ARCHIVE_FATAL); cbytes -= avail; } } return (consumed_bytes); } /* * Consume CFDATA as much as we have already gotten and * compute the sum of CFDATA. */ static int64_t cab_minimum_consume_cfdata(struct archive_read *a, int64_t consumed_bytes) { struct cab *cab = (struct cab *)(a->format->data); struct cfdata *cfdata; int64_t cbytes, rbytes; int err; cfdata = cab->entry_cfdata; rbytes = consumed_bytes; if (cab->entry_cffolder->comptype == COMPTYPE_NONE) { if (consumed_bytes < cfdata->unconsumed) cbytes = consumed_bytes; else cbytes = cfdata->unconsumed; rbytes -= cbytes; cfdata->read_offset += (uint16_t)cbytes; cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes; cfdata->unconsumed -= cbytes; } else { cbytes = cfdata->uncompressed_avail - cfdata->read_offset; if (cbytes > 0) { if (consumed_bytes < cbytes) cbytes = consumed_bytes; rbytes -= cbytes; cfdata->read_offset += (uint16_t)cbytes; cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes; } if (cfdata->unconsumed) { cbytes = cfdata->unconsumed; cfdata->unconsumed = 0; } else cbytes = 0; } if (cbytes) { /* Compute the sum. */ cab_checksum_update(a, (size_t)cbytes); /* Consume as much as the compressor actually used. */ __archive_read_consume(a, cbytes); cab->cab_offset += cbytes; cfdata->compressed_bytes_remaining -= (uint16_t)cbytes; if (cfdata->compressed_bytes_remaining == 0) { err = cab_checksum_finish(a); if (err < 0) return (err); } } return (rbytes); } /* * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets * cab->end_of_entry if it consumes all of the data. */ static int cab_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct cab *cab = (struct cab *)(a->format->data); ssize_t bytes_avail; if (cab->entry_bytes_remaining == 0) { *buff = NULL; *size = 0; *offset = cab->entry_offset; cab->end_of_entry = 1; return (ARCHIVE_OK); } *buff = cab_read_ahead_cfdata(a, &bytes_avail); if (bytes_avail <= 0) { *buff = NULL; *size = 0; *offset = 0; if (bytes_avail == 0 && cab->entry_cfdata->uncompressed_size == 0) { /* All of CFDATA in a folder has been handled. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA"); return (ARCHIVE_FATAL); } else return ((int)bytes_avail); } if (bytes_avail > cab->entry_bytes_remaining) bytes_avail = (ssize_t)cab->entry_bytes_remaining; *size = bytes_avail; *offset = cab->entry_offset; cab->entry_offset += bytes_avail; cab->entry_bytes_remaining -= bytes_avail; if (cab->entry_bytes_remaining == 0) cab->end_of_entry = 1; cab->entry_unconsumed = bytes_avail; if (cab->entry_cffolder->comptype == COMPTYPE_NONE) { /* Don't consume more than current entry used. */ if (cab->entry_cfdata->unconsumed > cab->entry_unconsumed) cab->entry_cfdata->unconsumed = cab->entry_unconsumed; } return (ARCHIVE_OK); } static int archive_read_format_cab_read_data_skip(struct archive_read *a) { struct cab *cab; int64_t bytes_skipped; int r; cab = (struct cab *)(a->format->data); if (cab->end_of_archive) return (ARCHIVE_EOF); if (!cab->read_data_invoked) { cab->bytes_skipped += cab->entry_bytes_remaining; cab->entry_bytes_remaining = 0; /* This entry is finished and done. */ cab->end_of_entry_cleanup = cab->end_of_entry = 1; return (ARCHIVE_OK); } if (cab->entry_unconsumed) { /* Consume as much as the compressor actually used. */ r = (int)cab_consume_cfdata(a, cab->entry_unconsumed); cab->entry_unconsumed = 0; if (r < 0) return (r); } else if (cab->entry_cfdata == NULL) { r = cab_next_cfdata(a); if (r < 0) return (r); } /* if we've already read to end of data, we're done. */ if (cab->end_of_entry_cleanup) return (ARCHIVE_OK); /* * If the length is at the beginning, we can skip the * compressed data much more quickly. */ bytes_skipped = cab_consume_cfdata(a, cab->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); /* If the compression type is none(uncompressed), we've already * consumed data as much as the current entry size. */ if (cab->entry_cffolder->comptype == COMPTYPE_NONE && cab->entry_cfdata != NULL) cab->entry_cfdata->unconsumed = 0; /* This entry is finished and done. */ cab->end_of_entry_cleanup = cab->end_of_entry = 1; return (ARCHIVE_OK); } static int archive_read_format_cab_cleanup(struct archive_read *a) { struct cab *cab = (struct cab *)(a->format->data); struct cfheader *hd = &cab->cfheader; int i; if (hd->folder_array != NULL) { for (i = 0; i < hd->folder_count; i++) free(hd->folder_array[i].cfdata.memimage); free(hd->folder_array); } if (hd->file_array != NULL) { for (i = 0; i < cab->cfheader.file_count; i++) archive_string_free(&(hd->file_array[i].pathname)); free(hd->file_array); } #ifdef HAVE_ZLIB_H if (cab->stream_valid) inflateEnd(&cab->stream); #endif lzx_decode_free(&cab->xstrm); archive_wstring_free(&cab->ws); free(cab->uncompressed_buffer); free(cab); (a->format->data) = NULL; return (ARCHIVE_OK); } /* Convert an MSDOS-style date/time into Unix-style time. */ static time_t cab_dos_time(const unsigned char *p) { int msTime, msDate; struct tm ts; msDate = archive_le16dec(p); msTime = archive_le16dec(p+2); 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)); } /***************************************************************** * * LZX decompression code. * *****************************************************************/ /* * Initialize LZX decoder. * * Returns ARCHIVE_OK if initialization was successful. * Returns ARCHIVE_FAILED if w_bits has unsupported value. * Returns ARCHIVE_FATAL if initialization failed; memory allocation * error occurred. */ static int lzx_decode_init(struct lzx_stream *strm, int w_bits) { struct lzx_dec *ds; int slot, w_size, w_slot; int base, footer; int base_inc[18]; if (strm->ds == NULL) { strm->ds = calloc(1, sizeof(*strm->ds)); if (strm->ds == NULL) return (ARCHIVE_FATAL); } ds = strm->ds; ds->error = ARCHIVE_FAILED; /* Allow bits from 15(32KBi) up to 21(2MBi) */ if (w_bits < SLOT_BASE || w_bits > SLOT_MAX) return (ARCHIVE_FAILED); ds->error = ARCHIVE_FATAL; /* * Alloc window */ w_size = ds->w_size; w_slot = slots[w_bits - SLOT_BASE]; ds->w_size = 1U << w_bits; ds->w_mask = ds->w_size -1; if (ds->w_buff == NULL || w_size != ds->w_size) { free(ds->w_buff); ds->w_buff = malloc(ds->w_size); if (ds->w_buff == NULL) return (ARCHIVE_FATAL); free(ds->pos_tbl); ds->pos_tbl = malloc(sizeof(ds->pos_tbl[0]) * w_slot); if (ds->pos_tbl == NULL) return (ARCHIVE_FATAL); lzx_huffman_free(&(ds->mt)); } for (footer = 0; footer < 18; footer++) base_inc[footer] = 1 << footer; base = footer = 0; for (slot = 0; slot < w_slot; slot++) { int n; if (footer == 0) base = slot; else base += base_inc[footer]; if (footer < 17) { footer = -2; for (n = base; n; n >>= 1) footer++; if (footer <= 0) footer = 0; } ds->pos_tbl[slot].base = base; ds->pos_tbl[slot].footer_bits = footer; } ds->w_pos = 0; ds->state = 0; ds->br.cache_buffer = 0; ds->br.cache_avail = 0; ds->r0 = ds->r1 = ds->r2 = 1; /* Initialize aligned offset tree. */ if (lzx_huffman_init(&(ds->at), 8, 8) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Initialize pre-tree. */ if (lzx_huffman_init(&(ds->pt), 20, 10) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Initialize Main tree. */ if (lzx_huffman_init(&(ds->mt), 256+(w_slot<<3), 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Initialize Length tree. */ if (lzx_huffman_init(&(ds->lt), 249, 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); ds->error = 0; return (ARCHIVE_OK); } /* * Release LZX decoder. */ static void lzx_decode_free(struct lzx_stream *strm) { if (strm->ds == NULL) return; free(strm->ds->w_buff); free(strm->ds->pos_tbl); lzx_huffman_free(&(strm->ds->at)); lzx_huffman_free(&(strm->ds->pt)); lzx_huffman_free(&(strm->ds->mt)); lzx_huffman_free(&(strm->ds->lt)); free(strm->ds); strm->ds = NULL; } /* * E8 Call Translation reversal. */ static void lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset) { struct lzx_dec *ds = strm->ds; unsigned char *b, *end; if (!ds->translation || size <= 10) return; b = p; end = b + size - 10; while (b < end && (b = memchr(b, 0xE8, end - b)) != NULL) { size_t i = b - (unsigned char *)p; int32_t cp, displacement, value; cp = (int32_t)(offset + (uint32_t)i); value = archive_le32dec(&b[1]); if (value >= -cp && value < (int32_t)ds->translation_size) { if (value >= 0) displacement = value - cp; else displacement = value + ds->translation_size; archive_le32enc(&b[1], (uint32_t)displacement); } b += 5; } } /* * Bit stream reader. */ /* Check that the cache buffer has enough bits. */ #define lzx_br_has(br, n) ((br)->cache_avail >= n) /* Get compressed data by bit. */ #define lzx_br_bits(br, n) \ (((uint32_t)((br)->cache_buffer >> \ ((br)->cache_avail - (n)))) & cache_masks[n]) #define lzx_br_bits_forced(br, n) \ (((uint32_t)((br)->cache_buffer << \ ((n) - (br)->cache_avail))) & cache_masks[n]) /* Read ahead to make sure the cache buffer has enough compressed data we * will use. * True : completed, there is enough data in the cache buffer. * False : we met that strm->next_in is empty, we have to get following * bytes. */ #define lzx_br_read_ahead_0(strm, br, n) \ (lzx_br_has((br), (n)) || lzx_br_fillup(strm, br)) /* True : the cache buffer has some bits as much as we need. * False : there are no enough bits in the cache buffer to be used, * we have to get following bytes if we could. */ #define lzx_br_read_ahead(strm, br, n) \ (lzx_br_read_ahead_0((strm), (br), (n)) || lzx_br_has((br), (n))) /* Notify how many bits we consumed. */ #define lzx_br_consume(br, n) ((br)->cache_avail -= (n)) #define lzx_br_consume_unaligned_bits(br) ((br)->cache_avail &= ~0x0f) #define lzx_br_is_unaligned(br) ((br)->cache_avail & 0x0f) static const uint32_t cache_masks[] = { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; /* * Shift away used bits in the cache data and fill it up with following bits. * Call this when cache buffer does not have enough bits you need. * * Returns 1 if the cache buffer is full. * Returns 0 if the cache buffer is not full; input buffer is empty. */ static int lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br) { /* * x86 processor family can read misaligned data without an access error. */ int n = CACHE_BITS - br->cache_avail; for (;;) { switch (n >> 4) { case 4: if (strm->avail_in >= 8) { br->cache_buffer = ((uint64_t)strm->next_in[1]) << 56 | ((uint64_t)strm->next_in[0]) << 48 | ((uint64_t)strm->next_in[3]) << 40 | ((uint64_t)strm->next_in[2]) << 32 | ((uint32_t)strm->next_in[5]) << 24 | ((uint32_t)strm->next_in[4]) << 16 | ((uint32_t)strm->next_in[7]) << 8 | (uint32_t)strm->next_in[6]; strm->next_in += 8; strm->avail_in -= 8; br->cache_avail += 8 * 8; return (1); } break; case 3: if (strm->avail_in >= 6) { br->cache_buffer = (br->cache_buffer << 48) | ((uint64_t)strm->next_in[1]) << 40 | ((uint64_t)strm->next_in[0]) << 32 | ((uint32_t)strm->next_in[3]) << 24 | ((uint32_t)strm->next_in[2]) << 16 | ((uint32_t)strm->next_in[5]) << 8 | (uint32_t)strm->next_in[4]; strm->next_in += 6; strm->avail_in -= 6; br->cache_avail += 6 * 8; return (1); } break; case 0: /* We have enough compressed data in * the cache buffer.*/ return (1); default: break; } if (strm->avail_in < 2) { /* There is not enough compressed data to * fill up the cache buffer. */ if (strm->avail_in == 1) { br->odd = *strm->next_in++; strm->avail_in--; br->have_odd = 1; } return (0); } br->cache_buffer = (br->cache_buffer << 16) | archive_le16dec(strm->next_in); strm->next_in += 2; strm->avail_in -= 2; br->cache_avail += 16; n -= 16; } } static void lzx_br_fixup(struct lzx_stream *strm, struct lzx_br *br) { int n = CACHE_BITS - br->cache_avail; if (br->have_odd && n >= 16 && strm->avail_in > 0) { br->cache_buffer = (br->cache_buffer << 16) | ((uint16_t)(*strm->next_in)) << 8 | br->odd; strm->next_in++; strm->avail_in--; br->cache_avail += 16; br->have_odd = 0; } } static void lzx_cleanup_bitstream(struct lzx_stream *strm) { strm->ds->br.cache_avail = 0; strm->ds->br.have_odd = 0; } /* * Decode LZX. * * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty. * Please set available buffer and call this function again. * 2. Returns ARCHIVE_EOF if decompression has been completed. * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data * is broken or you do not set 'last' flag properly. */ #define ST_RD_TRANSLATION 0 #define ST_RD_TRANSLATION_SIZE 1 #define ST_RD_BLOCK_TYPE 2 #define ST_RD_BLOCK_SIZE 3 #define ST_RD_ALIGNMENT 4 #define ST_RD_R0 5 #define ST_RD_R1 6 #define ST_RD_R2 7 #define ST_COPY_UNCOMP1 8 #define ST_COPY_UNCOMP2 9 #define ST_RD_ALIGNED_OFFSET 10 #define ST_RD_VERBATIM 11 #define ST_RD_PRE_MAIN_TREE_256 12 #define ST_MAIN_TREE_256 13 #define ST_RD_PRE_MAIN_TREE_REM 14 #define ST_MAIN_TREE_REM 15 #define ST_RD_PRE_LENGTH_TREE 16 #define ST_LENGTH_TREE 17 #define ST_MAIN 18 #define ST_LENGTH 19 #define ST_OFFSET 20 #define ST_REAL_POS 21 #define ST_COPY 22 static int lzx_decode(struct lzx_stream *strm, int last) { struct lzx_dec *ds = strm->ds; int64_t avail_in; int r; if (ds->error) return (ds->error); avail_in = strm->avail_in; lzx_br_fixup(strm, &(ds->br)); do { if (ds->state < ST_MAIN) r = lzx_read_blocks(strm, last); else { int64_t bytes_written = strm->avail_out; r = lzx_decode_blocks(strm, last); bytes_written -= strm->avail_out; strm->next_out += bytes_written; strm->total_out += bytes_written; } } while (r == 100); strm->total_in += avail_in - strm->avail_in; return (r); } static int lzx_read_blocks(struct lzx_stream *strm, int last) { struct lzx_dec *ds = strm->ds; struct lzx_br *br = &(ds->br); int i, r; for (;;) { switch (ds->state) { case ST_RD_TRANSLATION: if (!lzx_br_read_ahead(strm, br, 1)) { ds->state = ST_RD_TRANSLATION; if (last) goto failed; return (ARCHIVE_OK); } ds->translation = lzx_br_bits(br, 1); lzx_br_consume(br, 1); /* FALL THROUGH */ case ST_RD_TRANSLATION_SIZE: if (ds->translation) { if (!lzx_br_read_ahead(strm, br, 32)) { ds->state = ST_RD_TRANSLATION_SIZE; if (last) goto failed; return (ARCHIVE_OK); } ds->translation_size = lzx_br_bits(br, 16); lzx_br_consume(br, 16); ds->translation_size <<= 16; ds->translation_size |= lzx_br_bits(br, 16); lzx_br_consume(br, 16); } /* FALL THROUGH */ case ST_RD_BLOCK_TYPE: if (!lzx_br_read_ahead(strm, br, 3)) { ds->state = ST_RD_BLOCK_TYPE; if (last) goto failed; return (ARCHIVE_OK); } ds->block_type = lzx_br_bits(br, 3); lzx_br_consume(br, 3); /* Check a block type. */ switch (ds->block_type) { case VERBATIM_BLOCK: case ALIGNED_OFFSET_BLOCK: case UNCOMPRESSED_BLOCK: break; default: goto failed;/* Invalid */ } /* FALL THROUGH */ case ST_RD_BLOCK_SIZE: if (!lzx_br_read_ahead(strm, br, 24)) { ds->state = ST_RD_BLOCK_SIZE; if (last) goto failed; return (ARCHIVE_OK); } ds->block_size = lzx_br_bits(br, 8); lzx_br_consume(br, 8); ds->block_size <<= 16; ds->block_size |= lzx_br_bits(br, 16); lzx_br_consume(br, 16); if (ds->block_size == 0) goto failed; ds->block_bytes_avail = ds->block_size; if (ds->block_type != UNCOMPRESSED_BLOCK) { if (ds->block_type == VERBATIM_BLOCK) ds->state = ST_RD_VERBATIM; else ds->state = ST_RD_ALIGNED_OFFSET; break; } /* FALL THROUGH */ case ST_RD_ALIGNMENT: /* * Handle an Uncompressed Block. */ /* Skip padding to align following field on * 16-bit boundary. */ if (lzx_br_is_unaligned(br)) lzx_br_consume_unaligned_bits(br); else { if (lzx_br_read_ahead(strm, br, 16)) lzx_br_consume(br, 16); else { ds->state = ST_RD_ALIGNMENT; if (last) goto failed; return (ARCHIVE_OK); } } /* Preparation to read repeated offsets R0,R1 and R2. */ ds->rbytes_avail = 0; ds->state = ST_RD_R0; /* FALL THROUGH */ case ST_RD_R0: case ST_RD_R1: case ST_RD_R2: do { uint16_t u16; /* Drain bits in the cache buffer of * bit-stream. */ if (lzx_br_has(br, 32)) { u16 = lzx_br_bits(br, 16); lzx_br_consume(br, 16); archive_le16enc(ds->rbytes, u16); u16 = lzx_br_bits(br, 16); lzx_br_consume(br, 16); archive_le16enc(ds->rbytes+2, u16); ds->rbytes_avail = 4; } else if (lzx_br_has(br, 16)) { u16 = lzx_br_bits(br, 16); lzx_br_consume(br, 16); archive_le16enc(ds->rbytes, u16); ds->rbytes_avail = 2; } if (ds->rbytes_avail < 4 && ds->br.have_odd) { ds->rbytes[ds->rbytes_avail++] = ds->br.odd; ds->br.have_odd = 0; } while (ds->rbytes_avail < 4) { if (strm->avail_in <= 0) { if (last) goto failed; return (ARCHIVE_OK); } ds->rbytes[ds->rbytes_avail++] = *strm->next_in++; strm->avail_in--; } ds->rbytes_avail = 0; if (ds->state == ST_RD_R0) { ds->r0 = archive_le32dec(ds->rbytes); if (ds->r0 < 0) goto failed; ds->state = ST_RD_R1; } else if (ds->state == ST_RD_R1) { ds->r1 = archive_le32dec(ds->rbytes); if (ds->r1 < 0) goto failed; ds->state = ST_RD_R2; } else if (ds->state == ST_RD_R2) { ds->r2 = archive_le32dec(ds->rbytes); if (ds->r2 < 0) goto failed; /* We've gotten all repeated offsets. */ ds->state = ST_COPY_UNCOMP1; } } while (ds->state != ST_COPY_UNCOMP1); /* FALL THROUGH */ case ST_COPY_UNCOMP1: /* * Copy bytes form next_in to next_out directly. */ while (ds->block_bytes_avail) { int l; if (strm->avail_out <= 0) /* Output buffer is empty. */ return (ARCHIVE_OK); if (strm->avail_in <= 0) { /* Input buffer is empty. */ if (last) goto failed; return (ARCHIVE_OK); } l = (int)ds->block_bytes_avail; if (l > ds->w_size - ds->w_pos) l = ds->w_size - ds->w_pos; if (l > strm->avail_out) l = (int)strm->avail_out; if (l > strm->avail_in) l = (int)strm->avail_in; memcpy(strm->next_out, strm->next_in, l); memcpy(&(ds->w_buff[ds->w_pos]), strm->next_in, l); strm->next_in += l; strm->avail_in -= l; strm->next_out += l; strm->avail_out -= l; strm->total_out += l; ds->w_pos = (ds->w_pos + l) & ds->w_mask; ds->block_bytes_avail -= l; } /* FALL THROUGH */ case ST_COPY_UNCOMP2: /* Re-align; skip padding byte. */ if (ds->block_size & 1) { if (strm->avail_in <= 0) { /* Input buffer is empty. */ ds->state = ST_COPY_UNCOMP2; if (last) goto failed; return (ARCHIVE_OK); } strm->next_in++; strm->avail_in --; } /* This block ended. */ ds->state = ST_RD_BLOCK_TYPE; return (ARCHIVE_EOF); /********************/ case ST_RD_ALIGNED_OFFSET: /* * Read Aligned offset tree. */ if (!lzx_br_read_ahead(strm, br, 3 * ds->at.len_size)) { ds->state = ST_RD_ALIGNED_OFFSET; if (last) goto failed; return (ARCHIVE_OK); } memset(ds->at.freq, 0, sizeof(ds->at.freq)); for (i = 0; i < ds->at.len_size; i++) { ds->at.bitlen[i] = lzx_br_bits(br, 3); ds->at.freq[ds->at.bitlen[i]]++; lzx_br_consume(br, 3); } if (!lzx_make_huffman_table(&ds->at)) goto failed; /* FALL THROUGH */ case ST_RD_VERBATIM: ds->loop = 0; /* FALL THROUGH */ case ST_RD_PRE_MAIN_TREE_256: /* * Read Pre-tree for first 256 elements of main tree. */ if (!lzx_read_pre_tree(strm)) { ds->state = ST_RD_PRE_MAIN_TREE_256; if (last) goto failed; return (ARCHIVE_OK); } if (!lzx_make_huffman_table(&ds->pt)) goto failed; ds->loop = 0; /* FALL THROUGH */ case ST_MAIN_TREE_256: /* * Get path lengths of first 256 elements of main tree. */ r = lzx_read_bitlen(strm, &ds->mt, 256); if (r < 0) goto failed; else if (!r) { ds->state = ST_MAIN_TREE_256; if (last) goto failed; return (ARCHIVE_OK); } ds->loop = 0; /* FALL THROUGH */ case ST_RD_PRE_MAIN_TREE_REM: /* * Read Pre-tree for remaining elements of main tree. */ if (!lzx_read_pre_tree(strm)) { ds->state = ST_RD_PRE_MAIN_TREE_REM; if (last) goto failed; return (ARCHIVE_OK); } if (!lzx_make_huffman_table(&ds->pt)) goto failed; ds->loop = 256; /* FALL THROUGH */ case ST_MAIN_TREE_REM: /* * Get path lengths of remaining elements of main tree. */ r = lzx_read_bitlen(strm, &ds->mt, -1); if (r < 0) goto failed; else if (!r) { ds->state = ST_MAIN_TREE_REM; if (last) goto failed; return (ARCHIVE_OK); } if (!lzx_make_huffman_table(&ds->mt)) goto failed; ds->loop = 0; /* FALL THROUGH */ case ST_RD_PRE_LENGTH_TREE: /* * Read Pre-tree for remaining elements of main tree. */ if (!lzx_read_pre_tree(strm)) { ds->state = ST_RD_PRE_LENGTH_TREE; if (last) goto failed; return (ARCHIVE_OK); } if (!lzx_make_huffman_table(&ds->pt)) goto failed; ds->loop = 0; /* FALL THROUGH */ case ST_LENGTH_TREE: /* * Get path lengths of remaining elements of main tree. */ r = lzx_read_bitlen(strm, &ds->lt, -1); if (r < 0) goto failed; else if (!r) { ds->state = ST_LENGTH_TREE; if (last) goto failed; return (ARCHIVE_OK); } if (!lzx_make_huffman_table(&ds->lt)) goto failed; ds->state = ST_MAIN; return (100); } } failed: return (ds->error = ARCHIVE_FAILED); } static int lzx_decode_blocks(struct lzx_stream *strm, int last) { struct lzx_dec *ds = strm->ds; struct lzx_br bre = ds->br; struct huffman *at = &(ds->at), *lt = &(ds->lt), *mt = &(ds->mt); const struct lzx_pos_tbl *pos_tbl = ds->pos_tbl; unsigned char *noutp = strm->next_out; unsigned char *endp = noutp + strm->avail_out; unsigned char *w_buff = ds->w_buff; unsigned char *at_bitlen = at->bitlen; unsigned char *lt_bitlen = lt->bitlen; unsigned char *mt_bitlen = mt->bitlen; size_t block_bytes_avail = ds->block_bytes_avail; int at_max_bits = at->max_bits; int lt_max_bits = lt->max_bits; int mt_max_bits = mt->max_bits; int c, copy_len = ds->copy_len, copy_pos = ds->copy_pos; int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size; int length_header = ds->length_header; int offset_bits = ds->offset_bits; int position_slot = ds->position_slot; int r0 = ds->r0, r1 = ds->r1, r2 = ds->r2; int state = ds->state; char block_type = ds->block_type; for (;;) { switch (state) { case ST_MAIN: for (;;) { if (block_bytes_avail == 0) { /* This block ended. */ ds->state = ST_RD_BLOCK_TYPE; ds->br = bre; ds->block_bytes_avail = block_bytes_avail; ds->copy_len = copy_len; ds->copy_pos = copy_pos; ds->length_header = length_header; ds->position_slot = position_slot; ds->r0 = r0; ds->r1 = r1; ds->r2 = r2; ds->w_pos = w_pos; strm->avail_out = endp - noutp; return (ARCHIVE_EOF); } if (noutp >= endp) /* Output buffer is empty. */ goto next_data; if (!lzx_br_read_ahead(strm, &bre, mt_max_bits)) { if (!last) goto next_data; /* Remaining bits are less than * maximum bits(mt.max_bits) but maybe * it still remains as much as we need, * so we should try to use it with * dummy bits. */ c = lzx_decode_huffman(mt, lzx_br_bits_forced( &bre, mt_max_bits)); lzx_br_consume(&bre, mt_bitlen[c]); if (!lzx_br_has(&bre, 0)) goto failed;/* Over read. */ } else { c = lzx_decode_huffman(mt, lzx_br_bits(&bre, mt_max_bits)); lzx_br_consume(&bre, mt_bitlen[c]); } if (c > UCHAR_MAX) break; /* * 'c' is exactly literal code. */ /* Save a decoded code to reference it * afterward. */ w_buff[w_pos] = c; w_pos = (w_pos + 1) & w_mask; /* Store the decoded code to output buffer. */ *noutp++ = c; block_bytes_avail--; } /* * Get a match code, its length and offset. */ c -= UCHAR_MAX + 1; length_header = c & 7; position_slot = c >> 3; /* FALL THROUGH */ case ST_LENGTH: /* * Get a length. */ if (length_header == 7) { if (!lzx_br_read_ahead(strm, &bre, lt_max_bits)) { if (!last) { state = ST_LENGTH; goto next_data; } c = lzx_decode_huffman(lt, lzx_br_bits_forced( &bre, lt_max_bits)); lzx_br_consume(&bre, lt_bitlen[c]); if (!lzx_br_has(&bre, 0)) goto failed;/* Over read. */ } else { c = lzx_decode_huffman(lt, lzx_br_bits(&bre, lt_max_bits)); lzx_br_consume(&bre, lt_bitlen[c]); } copy_len = c + 7 + 2; } else copy_len = length_header + 2; if ((size_t)copy_len > block_bytes_avail) goto failed; /* * Get an offset. */ switch (position_slot) { case 0: /* Use repeated offset 0. */ copy_pos = r0; state = ST_REAL_POS; continue; case 1: /* Use repeated offset 1. */ copy_pos = r1; /* Swap repeated offset. */ r1 = r0; r0 = copy_pos; state = ST_REAL_POS; continue; case 2: /* Use repeated offset 2. */ copy_pos = r2; /* Swap repeated offset. */ r2 = r0; r0 = copy_pos; state = ST_REAL_POS; continue; default: offset_bits = pos_tbl[position_slot].footer_bits; break; } /* FALL THROUGH */ case ST_OFFSET: /* * Get the offset, which is a distance from * current window position. */ if (block_type == ALIGNED_OFFSET_BLOCK && offset_bits >= 3) { int offbits = offset_bits - 3; if (!lzx_br_read_ahead(strm, &bre, offbits)) { state = ST_OFFSET; if (last) goto failed; goto next_data; } copy_pos = lzx_br_bits(&bre, offbits) << 3; /* Get an aligned number. */ if (!lzx_br_read_ahead(strm, &bre, offbits + at_max_bits)) { if (!last) { state = ST_OFFSET; goto next_data; } lzx_br_consume(&bre, offbits); c = lzx_decode_huffman(at, lzx_br_bits_forced(&bre, at_max_bits)); lzx_br_consume(&bre, at_bitlen[c]); if (!lzx_br_has(&bre, 0)) goto failed;/* Over read. */ } else { lzx_br_consume(&bre, offbits); c = lzx_decode_huffman(at, lzx_br_bits(&bre, at_max_bits)); lzx_br_consume(&bre, at_bitlen[c]); } /* Add an aligned number. */ copy_pos += c; } else { if (!lzx_br_read_ahead(strm, &bre, offset_bits)) { state = ST_OFFSET; if (last) goto failed; goto next_data; } copy_pos = lzx_br_bits(&bre, offset_bits); lzx_br_consume(&bre, offset_bits); } copy_pos += pos_tbl[position_slot].base -2; /* Update repeated offset LRU queue. */ r2 = r1; r1 = r0; r0 = copy_pos; /* FALL THROUGH */ case ST_REAL_POS: /* * Compute a real position in window. */ copy_pos = (w_pos - copy_pos) & w_mask; /* FALL THROUGH */ case ST_COPY: /* * Copy several bytes as extracted data from the window * into the output buffer. */ for (;;) { const unsigned char *s; int l; l = copy_len; if (copy_pos > w_pos) { if (l > w_size - copy_pos) l = w_size - copy_pos; } else { if (l > w_size - w_pos) l = w_size - w_pos; } if (noutp + l >= endp) l = (int)(endp - noutp); s = w_buff + copy_pos; if (l >= 8 && ((copy_pos + l < w_pos) || (w_pos + l < copy_pos))) { memcpy(w_buff + w_pos, s, l); memcpy(noutp, s, l); } else { unsigned char *d; int li; d = w_buff + w_pos; for (li = 0; li < l; li++) noutp[li] = d[li] = s[li]; } noutp += l; copy_pos = (copy_pos + l) & w_mask; w_pos = (w_pos + l) & w_mask; block_bytes_avail -= l; if (copy_len <= l) /* A copy of current pattern ended. */ break; copy_len -= l; if (noutp >= endp) { /* Output buffer is empty. */ state = ST_COPY; goto next_data; } } state = ST_MAIN; break; } } failed: return (ds->error = ARCHIVE_FAILED); next_data: ds->br = bre; ds->block_bytes_avail = block_bytes_avail; ds->copy_len = copy_len; ds->copy_pos = copy_pos; ds->length_header = length_header; ds->offset_bits = offset_bits; ds->position_slot = position_slot; ds->r0 = r0; ds->r1 = r1; ds->r2 = r2; ds->state = state; ds->w_pos = w_pos; strm->avail_out = endp - noutp; return (ARCHIVE_OK); } static int lzx_read_pre_tree(struct lzx_stream *strm) { struct lzx_dec *ds = strm->ds; struct lzx_br *br = &(ds->br); int i; if (ds->loop == 0) memset(ds->pt.freq, 0, sizeof(ds->pt.freq)); for (i = ds->loop; i < ds->pt.len_size; i++) { if (!lzx_br_read_ahead(strm, br, 4)) { ds->loop = i; return (0); } ds->pt.bitlen[i] = lzx_br_bits(br, 4); ds->pt.freq[ds->pt.bitlen[i]]++; lzx_br_consume(br, 4); } ds->loop = i; return (1); } /* * Read a bunch of bit-lengths from pre-tree. */ static int lzx_read_bitlen(struct lzx_stream *strm, struct huffman *d, int end) { struct lzx_dec *ds = strm->ds; struct lzx_br *br = &(ds->br); int c, i, j, ret, same; unsigned rbits; i = ds->loop; if (i == 0) memset(d->freq, 0, sizeof(d->freq)); ret = 0; if (end < 0) end = d->len_size; while (i < end) { ds->loop = i; if (!lzx_br_read_ahead(strm, br, ds->pt.max_bits)) goto getdata; rbits = lzx_br_bits(br, ds->pt.max_bits); c = lzx_decode_huffman(&(ds->pt), rbits); switch (c) { case 17:/* several zero lengths, from 4 to 19. */ if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+4)) goto getdata; lzx_br_consume(br, ds->pt.bitlen[c]); same = lzx_br_bits(br, 4) + 4; if (i + same > end) return (-1);/* Invalid */ lzx_br_consume(br, 4); for (j = 0; j < same; j++) d->bitlen[i++] = 0; break; case 18:/* many zero lengths, from 20 to 51. */ if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+5)) goto getdata; lzx_br_consume(br, ds->pt.bitlen[c]); same = lzx_br_bits(br, 5) + 20; if (i + same > end) return (-1);/* Invalid */ lzx_br_consume(br, 5); memset(d->bitlen + i, 0, same); i += same; break; case 19:/* a few same lengths. */ if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+1+ds->pt.max_bits)) goto getdata; lzx_br_consume(br, ds->pt.bitlen[c]); same = lzx_br_bits(br, 1) + 4; if (i + same > end) return (-1); lzx_br_consume(br, 1); rbits = lzx_br_bits(br, ds->pt.max_bits); c = lzx_decode_huffman(&(ds->pt), rbits); lzx_br_consume(br, ds->pt.bitlen[c]); c = (d->bitlen[i] - c + 17) % 17; if (c < 0) return (-1);/* Invalid */ for (j = 0; j < same; j++) d->bitlen[i++] = c; d->freq[c] += same; break; default: lzx_br_consume(br, ds->pt.bitlen[c]); c = (d->bitlen[i] - c + 17) % 17; if (c < 0) return (-1);/* Invalid */ d->freq[c]++; d->bitlen[i++] = c; break; } } ret = 1; getdata: ds->loop = i; return (ret); } static int lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits) { int bits; if (hf->bitlen == NULL || hf->len_size != (int)len_size) { free(hf->bitlen); hf->bitlen = calloc(len_size, sizeof(hf->bitlen[0])); if (hf->bitlen == NULL) return (ARCHIVE_FATAL); hf->len_size = (int)len_size; } else memset(hf->bitlen, 0, len_size * sizeof(hf->bitlen[0])); if (hf->tbl == NULL) { if (tbl_bits < HTBL_BITS) bits = tbl_bits; else bits = HTBL_BITS; hf->tbl = malloc(((size_t)1 << bits) * sizeof(hf->tbl[0])); if (hf->tbl == NULL) return (ARCHIVE_FATAL); hf->tbl_bits = tbl_bits; } if (hf->tree == NULL && tbl_bits > HTBL_BITS) { hf->tree_avail = 1 << (tbl_bits - HTBL_BITS + 4); hf->tree = malloc(hf->tree_avail * sizeof(hf->tree[0])); if (hf->tree == NULL) return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } static void lzx_huffman_free(struct huffman *hf) { free(hf->bitlen); free(hf->tbl); free(hf->tree); } /* * Make a huffman coding table. */ static int lzx_make_huffman_table(struct huffman *hf) { uint16_t *tbl; const unsigned char *bitlen; int bitptn[17], weight[17]; int i, maxbits = 0, ptn, tbl_size, w; int diffbits, len_avail; /* * Initialize bit patterns. */ ptn = 0; for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) { bitptn[i] = ptn; weight[i] = w; if (hf->freq[i]) { ptn += hf->freq[i] * w; maxbits = i; } } if ((ptn & 0xffff) != 0 || maxbits > hf->tbl_bits) return (0);/* Invalid */ hf->max_bits = maxbits; /* * Cut out extra bits which we won't house in the table. * This preparation reduces the same calculation in the for-loop * making the table. */ if (maxbits < 16) { int ebits = 16 - maxbits; for (i = 1; i <= maxbits; i++) { bitptn[i] >>= ebits; weight[i] >>= ebits; } } if (maxbits > HTBL_BITS) { int htbl_max; uint16_t *p; diffbits = maxbits - HTBL_BITS; for (i = 1; i <= HTBL_BITS; i++) { bitptn[i] >>= diffbits; weight[i] >>= diffbits; } htbl_max = bitptn[HTBL_BITS] + weight[HTBL_BITS] * hf->freq[HTBL_BITS]; p = &(hf->tbl[htbl_max]); while (p < &hf->tbl[1U<shift_bits = diffbits; /* * Make the table. */ tbl_size = 1 << HTBL_BITS; tbl = hf->tbl; bitlen = hf->bitlen; len_avail = hf->len_size; hf->tree_used = 0; for (i = 0; i < len_avail; i++) { uint16_t *p; int len, cnt; uint16_t bit; int extlen; struct htree_t *ht; if (bitlen[i] == 0) continue; /* Get a bit pattern */ len = bitlen[i]; ptn = bitptn[len]; cnt = weight[len]; if (len <= HTBL_BITS) { /* Calculate next bit pattern */ if ((bitptn[len] = ptn + cnt) > tbl_size) return (0);/* Invalid */ /* Update the table */ p = &(tbl[ptn]); while (--cnt >= 0) p[cnt] = (uint16_t)i; continue; } /* * A bit length is too big to be housed to a direct table, * so we use a tree model for its extra bits. */ bitptn[len] = ptn + cnt; bit = 1U << (diffbits -1); extlen = len - HTBL_BITS; p = &(tbl[ptn >> diffbits]); if (*p == 0) { *p = len_avail + hf->tree_used; ht = &(hf->tree[hf->tree_used++]); if (hf->tree_used > hf->tree_avail) return (0);/* Invalid */ ht->left = 0; ht->right = 0; } else { if (*p < len_avail || *p >= (len_avail + hf->tree_used)) return (0);/* Invalid */ ht = &(hf->tree[*p - len_avail]); } while (--extlen > 0) { if (ptn & bit) { if (ht->left < len_avail) { ht->left = len_avail + hf->tree_used; ht = &(hf->tree[hf->tree_used++]); if (hf->tree_used > hf->tree_avail) return (0);/* Invalid */ ht->left = 0; ht->right = 0; } else { ht = &(hf->tree[ht->left - len_avail]); } } else { if (ht->right < len_avail) { ht->right = len_avail + hf->tree_used; ht = &(hf->tree[hf->tree_used++]); if (hf->tree_used > hf->tree_avail) return (0);/* Invalid */ ht->left = 0; ht->right = 0; } else { ht = &(hf->tree[ht->right - len_avail]); } } bit >>= 1; } if (ptn & bit) { if (ht->left != 0) return (0);/* Invalid */ ht->left = (uint16_t)i; } else { if (ht->right != 0) return (0);/* Invalid */ ht->right = (uint16_t)i; } } return (1); } static int lzx_decode_huffman_tree(struct huffman *hf, unsigned rbits, int c) { struct htree_t *ht; int extlen; ht = hf->tree; extlen = hf->shift_bits; while (c >= hf->len_size) { c -= hf->len_size; if (extlen-- <= 0 || c >= hf->tree_used) return (0); if (rbits & (1U << extlen)) c = ht[c].left; else c = ht[c].right; } return (c); } static inline int lzx_decode_huffman(struct huffman *hf, unsigned rbits) { int c; /* * At first search an index table for a bit pattern. * If it fails, search a huffman tree for. */ c = hf->tbl[rbits >> hf->shift_bits]; if (c < hf->len_size) return (c); /* This bit pattern needs to be found out at a huffman tree. */ return (lzx_decode_huffman_tree(hf, rbits, c)); } Index: head/contrib/libarchive/libarchive/archive_read_support_format_lha.c =================================================================== --- head/contrib/libarchive/libarchive/archive_read_support_format_lha.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_read_support_format_lha.c (revision 316095) @@ -1,2814 +1,2814 @@ /*- * Copyright (c) 2008-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. */ #include "archive_platform.h" #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_LIMITS_H #include #endif #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" #include "archive_endian.h" #define MAXMATCH 256 /* Maximum match length. */ #define MINMATCH 3 /* Minimum match length. */ /* * Literal table format: * +0 +256 +510 * +---------------+-------------------------+ * | literal code | match length | * | 0 ... 255 | MINMATCH ... MAXMATCH | * +---------------+-------------------------+ * <--- LT_BITLEN_SIZE ---> */ /* Literal table size. */ #define LT_BITLEN_SIZE (UCHAR_MAX + 1 + MAXMATCH - MINMATCH + 1) /* Position table size. * Note: this used for both position table and pre literal table.*/ #define PT_BITLEN_SIZE (3 + 16) struct lzh_dec { /* Decoding status. */ int state; /* * Window to see last 8Ki(lh5),32Ki(lh6),64Ki(lh7) bytes of decoded * data. */ int w_size; int w_mask; /* Window buffer, which is a loop buffer. */ unsigned char *w_buff; /* The insert position to the window. */ int w_pos; /* The position where we can copy decoded code from the window. */ int copy_pos; /* The length how many bytes we can copy decoded code from * the window. */ int copy_len; /* * Bit stream reader. */ struct lzh_br { #define CACHE_TYPE uint64_t #define CACHE_BITS (8 * sizeof(CACHE_TYPE)) /* Cache buffer. */ CACHE_TYPE cache_buffer; /* Indicates how many bits avail in cache_buffer. */ int cache_avail; } br; /* * Huffman coding. */ struct huffman { int len_size; int len_avail; int len_bits; int freq[17]; unsigned char *bitlen; /* * Use a index table. It's faster than searching a huffman * coding tree, which is a binary tree. But a use of a large * index table causes L1 cache read miss many times. */ #define HTBL_BITS 10 int max_bits; int shift_bits; int tbl_bits; int tree_used; int tree_avail; /* Direct access table. */ uint16_t *tbl; /* Binary tree table for extra bits over the direct access. */ struct htree_t { uint16_t left; uint16_t right; } *tree; } lt, pt; int blocks_avail; int pos_pt_len_size; int pos_pt_len_bits; int literal_pt_len_size; int literal_pt_len_bits; int reading_position; int loop; int error; }; struct lzh_stream { const unsigned char *next_in; int avail_in; int64_t total_in; const unsigned char *ref_ptr; int avail_out; int64_t total_out; struct lzh_dec *ds; }; struct lha { /* entry_bytes_remaining is the number of bytes we expect. */ int64_t entry_offset; int64_t entry_bytes_remaining; int64_t entry_unconsumed; uint16_t entry_crc_calculated; size_t header_size; /* header size */ unsigned char level; /* header level */ char method[3]; /* compress type */ int64_t compsize; /* compressed data size */ int64_t origsize; /* original file size */ int setflag; #define BIRTHTIME_IS_SET 1 #define ATIME_IS_SET 2 #define UNIX_MODE_IS_SET 4 #define CRC_IS_SET 8 time_t birthtime; long birthtime_tv_nsec; time_t mtime; long mtime_tv_nsec; time_t atime; long atime_tv_nsec; mode_t mode; int64_t uid; int64_t gid; struct archive_string uname; struct archive_string gname; uint16_t header_crc; uint16_t crc; struct archive_string_conv *sconv; struct archive_string_conv *opt_sconv; struct archive_string dirname; struct archive_string filename; struct archive_wstring ws; unsigned char dos_attr; /* Flag to mark progress that an archive was read their first header.*/ char found_first_header; /* Flag to mark that indicates an empty directory. */ char directory; /* Flags to mark progress of decompression. */ char decompress_init; char end_of_entry; char end_of_entry_cleanup; char entry_is_compressed; char format_name[64]; struct lzh_stream strm; }; /* * LHA header common member offset. */ #define H_METHOD_OFFSET 2 /* Compress type. */ #define H_ATTR_OFFSET 19 /* DOS attribute. */ #define H_LEVEL_OFFSET 20 /* Header Level. */ #define H_SIZE 22 /* Minimum header size. */ static int archive_read_format_lha_bid(struct archive_read *, int); static int archive_read_format_lha_options(struct archive_read *, const char *, const char *); static int archive_read_format_lha_read_header(struct archive_read *, struct archive_entry *); static int archive_read_format_lha_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_lha_read_data_skip(struct archive_read *); static int archive_read_format_lha_cleanup(struct archive_read *); static void lha_replace_path_separator(struct lha *, struct archive_entry *); static int lha_read_file_header_0(struct archive_read *, struct lha *); static int lha_read_file_header_1(struct archive_read *, struct lha *); static int lha_read_file_header_2(struct archive_read *, struct lha *); static int lha_read_file_header_3(struct archive_read *, struct lha *); static int lha_read_file_extended_header(struct archive_read *, struct lha *, uint16_t *, int, size_t, size_t *); static size_t lha_check_header_format(const void *); static int lha_skip_sfx(struct archive_read *); static time_t lha_dos_time(const unsigned char *); static time_t lha_win_time(uint64_t, long *); static unsigned char lha_calcsum(unsigned char, const void *, int, size_t); static int lha_parse_linkname(struct archive_string *, struct archive_string *); static int lha_read_data_none(struct archive_read *, const void **, size_t *, int64_t *); static int lha_read_data_lzh(struct archive_read *, const void **, size_t *, int64_t *); static void lha_crc16_init(void); static uint16_t lha_crc16(uint16_t, const void *, size_t); static int lzh_decode_init(struct lzh_stream *, const char *); static void lzh_decode_free(struct lzh_stream *); static int lzh_decode(struct lzh_stream *, int); static int lzh_br_fillup(struct lzh_stream *, struct lzh_br *); static int lzh_huffman_init(struct huffman *, size_t, int); static void lzh_huffman_free(struct huffman *); static int lzh_read_pt_bitlen(struct lzh_stream *, int start, int end); static int lzh_make_fake_table(struct huffman *, uint16_t); static int lzh_make_huffman_table(struct huffman *); static inline int lzh_decode_huffman(struct huffman *, unsigned); static int lzh_decode_huffman_tree(struct huffman *, unsigned, int); int archive_read_support_format_lha(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct lha *lha; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_lha"); lha = (struct lha *)calloc(1, sizeof(*lha)); if (lha == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate lha data"); return (ARCHIVE_FATAL); } archive_string_init(&lha->ws); r = __archive_read_register_format(a, lha, "lha", archive_read_format_lha_bid, archive_read_format_lha_options, archive_read_format_lha_read_header, archive_read_format_lha_read_data, archive_read_format_lha_read_data_skip, NULL, archive_read_format_lha_cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(lha); return (ARCHIVE_OK); } static size_t lha_check_header_format(const void *h) { const unsigned char *p = h; size_t next_skip_bytes; switch (p[H_METHOD_OFFSET+3]) { /* * "-lh0-" ... "-lh7-" "-lhd-" * "-lzs-" "-lz5-" */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case 'd': case 's': next_skip_bytes = 4; /* b0 == 0 means the end of an LHa archive file. */ if (p[0] == 0) break; if (p[H_METHOD_OFFSET] != '-' || p[H_METHOD_OFFSET+1] != 'l' || p[H_METHOD_OFFSET+4] != '-') break; if (p[H_METHOD_OFFSET+2] == 'h') { /* "-lh?-" */ if (p[H_METHOD_OFFSET+3] == 's') break; if (p[H_LEVEL_OFFSET] == 0) return (0); if (p[H_LEVEL_OFFSET] <= 3 && p[H_ATTR_OFFSET] == 0x20) return (0); } if (p[H_METHOD_OFFSET+2] == 'z') { /* LArc extensions: -lzs-,-lz4- and -lz5- */ if (p[H_LEVEL_OFFSET] != 0) break; if (p[H_METHOD_OFFSET+3] == 's' || p[H_METHOD_OFFSET+3] == '4' || p[H_METHOD_OFFSET+3] == '5') return (0); } break; case 'h': next_skip_bytes = 1; break; case 'z': next_skip_bytes = 1; break; case 'l': next_skip_bytes = 2; break; case '-': next_skip_bytes = 3; break; default : next_skip_bytes = 4; break; } return (next_skip_bytes); } static int archive_read_format_lha_bid(struct archive_read *a, int best_bid) { const char *p; const void *buff; ssize_t bytes_avail, offset, window; size_t next; /* If there's already a better bid than we can ever make, don't bother testing. */ if (best_bid > 30) return (-1); if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL) return (-1); if (lha_check_header_format(p) == 0) return (30); if (p[0] == 'M' && p[1] == 'Z') { /* PE file */ offset = 0; window = 4096; while (offset < (1024 * 20)) { buff = __archive_read_ahead(a, offset + window, &bytes_avail); if (buff == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < (H_SIZE + 3)) return (0); continue; } p = (const char *)buff + offset; while (p + H_SIZE < (const char *)buff + bytes_avail) { if ((next = lha_check_header_format(p)) == 0) return (30); p += next; } offset = p - (const char *)buff; } } return (0); } static int archive_read_format_lha_options(struct archive_read *a, const char *key, const char *val) { struct lha *lha; int ret = ARCHIVE_FAILED; lha = (struct lha *)(a->format->data); if (strcmp(key, "hdrcharset") == 0) { if (val == NULL || val[0] == 0) archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "lha: hdrcharset option needs a character-set name"); else { lha->opt_sconv = archive_string_conversion_from_charset( &a->archive, val, 0); if (lha->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 lha_skip_sfx(struct archive_read *a) { const void *h; const char *p, *q; size_t next, skip; ssize_t bytes, window; window = 4096; for (;;) { h = __archive_read_ahead(a, window, &bytes); if (h == NULL) { /* Remaining bytes are less than window. */ window >>= 1; if (window < (H_SIZE + 3)) goto fatal; continue; } if (bytes < H_SIZE) goto fatal; p = h; q = p + bytes; /* * Scan ahead until we find something that looks * like the lha header. */ while (p + H_SIZE < q) { if ((next = lha_check_header_format(p)) == 0) { skip = p - (const char *)h; __archive_read_consume(a, skip); return (ARCHIVE_OK); } p += next; } skip = p - (const char *)h; __archive_read_consume(a, skip); } fatal: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Couldn't find out LHa header"); return (ARCHIVE_FATAL); } static int truncated_error(struct archive_read *a) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated LHa header"); return (ARCHIVE_FATAL); } static int archive_read_format_lha_read_header(struct archive_read *a, struct archive_entry *entry) { struct archive_string linkname; struct archive_string pathname; struct lha *lha; const unsigned char *p; const char *signature; int err; lha_crc16_init(); a->archive.archive_format = ARCHIVE_FORMAT_LHA; if (a->archive.archive_format_name == NULL) a->archive.archive_format_name = "lha"; lha = (struct lha *)(a->format->data); lha->decompress_init = 0; lha->end_of_entry = 0; lha->end_of_entry_cleanup = 0; lha->entry_unconsumed = 0; if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL) { /* * LHa archiver added 0 to the tail of its archive file as * the mark of the end of the archive. */ signature = __archive_read_ahead(a, sizeof(signature[0]), NULL); if (signature == NULL || signature[0] == 0) return (ARCHIVE_EOF); return (truncated_error(a)); } signature = (const char *)p; if (lha->found_first_header == 0 && signature[0] == 'M' && signature[1] == 'Z') { /* This is an executable? Must be self-extracting... */ err = lha_skip_sfx(a); if (err < ARCHIVE_WARN) return (err); if ((p = __archive_read_ahead(a, sizeof(*p), NULL)) == NULL) return (truncated_error(a)); signature = (const char *)p; } /* signature[0] == 0 means the end of an LHa archive file. */ if (signature[0] == 0) return (ARCHIVE_EOF); /* * Check the header format and method type. */ if (lha_check_header_format(p) != 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad LHa file"); return (ARCHIVE_FATAL); } /* We've found the first header. */ lha->found_first_header = 1; /* Set a default value and common data */ lha->header_size = 0; lha->level = p[H_LEVEL_OFFSET]; lha->method[0] = p[H_METHOD_OFFSET+1]; lha->method[1] = p[H_METHOD_OFFSET+2]; lha->method[2] = p[H_METHOD_OFFSET+3]; if (memcmp(lha->method, "lhd", 3) == 0) lha->directory = 1; else lha->directory = 0; if (memcmp(lha->method, "lh0", 3) == 0 || memcmp(lha->method, "lz4", 3) == 0) lha->entry_is_compressed = 0; else lha->entry_is_compressed = 1; lha->compsize = 0; lha->origsize = 0; lha->setflag = 0; lha->birthtime = 0; lha->birthtime_tv_nsec = 0; lha->mtime = 0; lha->mtime_tv_nsec = 0; lha->atime = 0; lha->atime_tv_nsec = 0; lha->mode = (lha->directory)? 0777 : 0666; lha->uid = 0; lha->gid = 0; archive_string_empty(&lha->dirname); archive_string_empty(&lha->filename); lha->dos_attr = 0; if (lha->opt_sconv != NULL) lha->sconv = lha->opt_sconv; else lha->sconv = NULL; switch (p[H_LEVEL_OFFSET]) { case 0: err = lha_read_file_header_0(a, lha); break; case 1: err = lha_read_file_header_1(a, lha); break; case 2: err = lha_read_file_header_2(a, lha); break; case 3: err = lha_read_file_header_3(a, lha); break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported LHa header level %d", p[H_LEVEL_OFFSET]); err = ARCHIVE_FATAL; break; } if (err < ARCHIVE_WARN) return (err); if (!lha->directory && archive_strlen(&lha->filename) == 0) /* The filename has not been set */ return (truncated_error(a)); /* * Make a pathname from a dirname and a filename. */ archive_string_concat(&lha->dirname, &lha->filename); archive_string_init(&pathname); archive_string_init(&linkname); archive_string_copy(&pathname, &lha->dirname); if ((lha->mode & AE_IFMT) == AE_IFLNK) { /* * Extract the symlink-name if it's included in the pathname. */ if (!lha_parse_linkname(&linkname, &pathname)) { /* We couldn't get the symlink-name. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown symlink-name"); archive_string_free(&pathname); archive_string_free(&linkname); return (ARCHIVE_FAILED); } } else { /* * Make sure a file-type is set. * The mode has been overridden if it is in the extended data. */ lha->mode = (lha->mode & ~AE_IFMT) | ((lha->directory)? AE_IFDIR: AE_IFREG); } if ((lha->setflag & UNIX_MODE_IS_SET) == 0 && (lha->dos_attr & 1) != 0) lha->mode &= ~(0222);/* read only. */ /* * Set basic file parameters. */ if (archive_entry_copy_pathname_l(entry, pathname.s, pathname.length, lha->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(lha->sconv)); err = ARCHIVE_WARN; } archive_string_free(&pathname); if (archive_strlen(&linkname) > 0) { if (archive_entry_copy_symlink_l(entry, linkname.s, linkname.length, lha->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(lha->sconv)); err = ARCHIVE_WARN; } } else archive_entry_set_symlink(entry, NULL); archive_string_free(&linkname); /* * When a header level is 0, there is a possibility that * a pathname and a symlink has '\' character, a directory * separator in DOS/Windows. So we should convert it to '/'. */ if (p[H_LEVEL_OFFSET] == 0) lha_replace_path_separator(lha, entry); archive_entry_set_mode(entry, lha->mode); archive_entry_set_uid(entry, lha->uid); archive_entry_set_gid(entry, lha->gid); if (archive_strlen(&lha->uname) > 0) archive_entry_set_uname(entry, lha->uname.s); if (archive_strlen(&lha->gname) > 0) archive_entry_set_gname(entry, lha->gname.s); if (lha->setflag & BIRTHTIME_IS_SET) { archive_entry_set_birthtime(entry, lha->birthtime, lha->birthtime_tv_nsec); archive_entry_set_ctime(entry, lha->birthtime, lha->birthtime_tv_nsec); } else { archive_entry_unset_birthtime(entry); archive_entry_unset_ctime(entry); } archive_entry_set_mtime(entry, lha->mtime, lha->mtime_tv_nsec); if (lha->setflag & ATIME_IS_SET) archive_entry_set_atime(entry, lha->atime, lha->atime_tv_nsec); else archive_entry_unset_atime(entry); if (lha->directory || archive_entry_symlink(entry) != NULL) archive_entry_unset_size(entry); else archive_entry_set_size(entry, lha->origsize); /* * Prepare variables used to read a file content. */ lha->entry_bytes_remaining = lha->compsize; lha->entry_offset = 0; lha->entry_crc_calculated = 0; /* * This file does not have a content. */ if (lha->directory || lha->compsize == 0) lha->end_of_entry = 1; sprintf(lha->format_name, "lha -%c%c%c-", lha->method[0], lha->method[1], lha->method[2]); a->archive.archive_format_name = lha->format_name; return (err); } /* * Replace a DOS path separator '\' by a character '/'. * Some multi-byte character set have a character '\' in its second byte. */ static void lha_replace_path_separator(struct lha *lha, struct archive_entry *entry) { const wchar_t *wp; size_t i; if ((wp = archive_entry_pathname_w(entry)) != NULL) { archive_wstrcpy(&(lha->ws), wp); for (i = 0; i < archive_strlen(&(lha->ws)); i++) { if (lha->ws.s[i] == L'\\') lha->ws.s[i] = L'/'; } archive_entry_copy_pathname_w(entry, lha->ws.s); } if ((wp = archive_entry_symlink_w(entry)) != NULL) { archive_wstrcpy(&(lha->ws), wp); for (i = 0; i < archive_strlen(&(lha->ws)); i++) { if (lha->ws.s[i] == L'\\') lha->ws.s[i] = L'/'; } archive_entry_copy_symlink_w(entry, lha->ws.s); } } /* * Header 0 format * * +0 +1 +2 +7 +11 * +---------------+----------+----------------+-------------------+ * |header size(*1)|header sum|compression type|compressed size(*2)| * +---------------+----------+----------------+-------------------+ * <---------------------(*1)----------* * * +11 +15 +17 +19 +20 +21 * +-----------------+---------+---------+--------------+----------------+ * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=0)| * +-----------------+---------+---------+--------------+----------------+ * *--------------------------------(*1)---------------------------------* * * +21 +22 +22+(*3) +22+(*3)+2 +22+(*3)+2+(*4) * +---------------+---------+----------+----------------+------------------+ * |name length(*3)|file name|file CRC16|extra header(*4)| compressed data | * +---------------+---------+----------+----------------+------------------+ * <--(*3)-> <------(*2)------> * *----------------------(*1)--------------------------> * */ #define H0_HEADER_SIZE_OFFSET 0 #define H0_HEADER_SUM_OFFSET 1 #define H0_COMP_SIZE_OFFSET 7 #define H0_ORIG_SIZE_OFFSET 11 #define H0_DOS_TIME_OFFSET 15 #define H0_NAME_LEN_OFFSET 21 #define H0_FILE_NAME_OFFSET 22 #define H0_FIXED_SIZE 24 static int lha_read_file_header_0(struct archive_read *a, struct lha *lha) { const unsigned char *p; int extdsize, namelen; unsigned char headersum, sum_calculated; if ((p = __archive_read_ahead(a, H0_FIXED_SIZE, NULL)) == NULL) return (truncated_error(a)); lha->header_size = p[H0_HEADER_SIZE_OFFSET] + 2; headersum = p[H0_HEADER_SUM_OFFSET]; lha->compsize = archive_le32dec(p + H0_COMP_SIZE_OFFSET); lha->origsize = archive_le32dec(p + H0_ORIG_SIZE_OFFSET); lha->mtime = lha_dos_time(p + H0_DOS_TIME_OFFSET); namelen = p[H0_NAME_LEN_OFFSET]; extdsize = (int)lha->header_size - H0_FIXED_SIZE - namelen; if ((namelen > 221 || extdsize < 0) && extdsize != -2) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid LHa header"); return (ARCHIVE_FATAL); } if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL) return (truncated_error(a)); archive_strncpy(&lha->filename, p + H0_FILE_NAME_OFFSET, namelen); /* When extdsize == -2, A CRC16 value is not present in the header. */ if (extdsize >= 0) { lha->crc = archive_le16dec(p + H0_FILE_NAME_OFFSET + namelen); lha->setflag |= CRC_IS_SET; } sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2); /* Read an extended header */ if (extdsize > 0) { /* This extended data is set by 'LHa for UNIX' only. * Maybe fixed size. */ p += H0_FILE_NAME_OFFSET + namelen + 2; if (p[0] == 'U' && extdsize == 12) { /* p[1] is a minor version. */ lha->mtime = archive_le32dec(&p[2]); lha->mode = archive_le16dec(&p[6]); lha->uid = archive_le16dec(&p[8]); lha->gid = archive_le16dec(&p[10]); lha->setflag |= UNIX_MODE_IS_SET; } } __archive_read_consume(a, lha->header_size); if (sum_calculated != headersum) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LHa header sum error"); return (ARCHIVE_FATAL); } return (ARCHIVE_OK); } /* * Header 1 format * * +0 +1 +2 +7 +11 * +---------------+----------+----------------+-------------+ * |header size(*1)|header sum|compression type|skip size(*2)| * +---------------+----------+----------------+-------------+ * <---------------(*1)----------* * * +11 +15 +17 +19 +20 +21 * +-----------------+---------+---------+--------------+----------------+ * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=1)| * +-----------------+---------+---------+--------------+----------------+ * *-------------------------------(*1)----------------------------------* * * +21 +22 +22+(*3) +22+(*3)+2 +22+(*3)+3 +22+(*3)+3+(*4) * +---------------+---------+----------+-----------+-----------+ * |name length(*3)|file name|file CRC16| creator |padding(*4)| * +---------------+---------+----------+-----------+-----------+ * <--(*3)-> * *----------------------------(*1)----------------------------* * * +22+(*3)+3+(*4) +22+(*3)+3+(*4)+2 +22+(*3)+3+(*4)+2+(*5) * +----------------+---------------------+------------------------+ * |next header size| extended header(*5) | compressed data | * +----------------+---------------------+------------------------+ * *------(*1)-----> <--------------------(*2)--------------------> */ #define H1_HEADER_SIZE_OFFSET 0 #define H1_HEADER_SUM_OFFSET 1 #define H1_COMP_SIZE_OFFSET 7 #define H1_ORIG_SIZE_OFFSET 11 #define H1_DOS_TIME_OFFSET 15 #define H1_NAME_LEN_OFFSET 21 #define H1_FILE_NAME_OFFSET 22 #define H1_FIXED_SIZE 27 static int lha_read_file_header_1(struct archive_read *a, struct lha *lha) { const unsigned char *p; size_t extdsize; int i, err, err2; int namelen, padding; unsigned char headersum, sum_calculated; err = ARCHIVE_OK; if ((p = __archive_read_ahead(a, H1_FIXED_SIZE, NULL)) == NULL) return (truncated_error(a)); lha->header_size = p[H1_HEADER_SIZE_OFFSET] + 2; headersum = p[H1_HEADER_SUM_OFFSET]; /* Note: An extended header size is included in a compsize. */ lha->compsize = archive_le32dec(p + H1_COMP_SIZE_OFFSET); lha->origsize = archive_le32dec(p + H1_ORIG_SIZE_OFFSET); lha->mtime = lha_dos_time(p + H1_DOS_TIME_OFFSET); namelen = p[H1_NAME_LEN_OFFSET]; /* Calculate a padding size. The result will be normally 0 only(?) */ padding = ((int)lha->header_size) - H1_FIXED_SIZE - namelen; if (namelen > 230 || padding < 0) goto invalid; if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL) return (truncated_error(a)); for (i = 0; i < namelen; i++) { if (p[i + H1_FILE_NAME_OFFSET] == 0xff) goto invalid;/* Invalid filename. */ } archive_strncpy(&lha->filename, p + H1_FILE_NAME_OFFSET, namelen); lha->crc = archive_le16dec(p + H1_FILE_NAME_OFFSET + namelen); lha->setflag |= CRC_IS_SET; sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2); /* Consume used bytes but not include `next header size' data * since it will be consumed in lha_read_file_extended_header(). */ __archive_read_consume(a, lha->header_size - 2); /* Read extended headers */ err2 = lha_read_file_extended_header(a, lha, NULL, 2, (size_t)(lha->compsize + 2), &extdsize); if (err2 < ARCHIVE_WARN) return (err2); if (err2 < err) err = err2; /* Get a real compressed file size. */ lha->compsize -= extdsize - 2; if (lha->compsize < 0) goto invalid; /* Invalid compressed file size */ if (sum_calculated != headersum) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LHa header sum error"); return (ARCHIVE_FATAL); } return (err); invalid: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid LHa header"); return (ARCHIVE_FATAL); } /* * Header 2 format * * +0 +2 +7 +11 +15 * +---------------+----------------+-------------------+-----------------+ * |header size(*1)|compression type|compressed size(*2)|uncompressed size| * +---------------+----------------+-------------------+-----------------+ * <--------------------------------(*1)---------------------------------* * * +15 +19 +20 +21 +23 +24 * +-----------------+------------+----------------+----------+-----------+ * |data/time(time_t)| 0x20 fixed |header level(=2)|file CRC16| creator | * +-----------------+------------+----------------+----------+-----------+ * *---------------------------------(*1)---------------------------------* * * +24 +26 +26+(*3) +26+(*3)+(*4) * +----------------+-------------------+-------------+-------------------+ * |next header size|extended header(*3)| padding(*4) | compressed data | * +----------------+-------------------+-------------+-------------------+ * *--------------------------(*1)-------------------> <------(*2)-------> * */ #define H2_HEADER_SIZE_OFFSET 0 #define H2_COMP_SIZE_OFFSET 7 #define H2_ORIG_SIZE_OFFSET 11 #define H2_TIME_OFFSET 15 #define H2_CRC_OFFSET 21 #define H2_FIXED_SIZE 24 static int lha_read_file_header_2(struct archive_read *a, struct lha *lha) { const unsigned char *p; size_t extdsize; int err, padding; uint16_t header_crc; if ((p = __archive_read_ahead(a, H2_FIXED_SIZE, NULL)) == NULL) return (truncated_error(a)); lha->header_size =archive_le16dec(p + H2_HEADER_SIZE_OFFSET); lha->compsize = archive_le32dec(p + H2_COMP_SIZE_OFFSET); lha->origsize = archive_le32dec(p + H2_ORIG_SIZE_OFFSET); lha->mtime = archive_le32dec(p + H2_TIME_OFFSET); lha->crc = archive_le16dec(p + H2_CRC_OFFSET); lha->setflag |= CRC_IS_SET; if (lha->header_size < H2_FIXED_SIZE) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid LHa header size"); return (ARCHIVE_FATAL); } header_crc = lha_crc16(0, p, H2_FIXED_SIZE); __archive_read_consume(a, H2_FIXED_SIZE); /* Read extended headers */ err = lha_read_file_extended_header(a, lha, &header_crc, 2, lha->header_size - H2_FIXED_SIZE, &extdsize); if (err < ARCHIVE_WARN) return (err); /* Calculate a padding size. The result will be normally 0 or 1. */ padding = (int)lha->header_size - (int)(H2_FIXED_SIZE + extdsize); if (padding > 0) { if ((p = __archive_read_ahead(a, padding, NULL)) == NULL) return (truncated_error(a)); header_crc = lha_crc16(header_crc, p, padding); __archive_read_consume(a, padding); } if (header_crc != lha->header_crc) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "LHa header CRC error"); return (ARCHIVE_FATAL); } return (err); } /* * Header 3 format * * +0 +2 +7 +11 +15 * +------------+----------------+-------------------+-----------------+ * | 0x04 fixed |compression type|compressed size(*2)|uncompressed size| * +------------+----------------+-------------------+-----------------+ * <-------------------------------(*1)-------------------------------* * * +15 +19 +20 +21 +23 +24 * +-----------------+------------+----------------+----------+-----------+ * |date/time(time_t)| 0x20 fixed |header level(=3)|file CRC16| creator | * +-----------------+------------+----------------+----------+-----------+ * *--------------------------------(*1)----------------------------------* * * +24 +28 +32 +32+(*3) * +---------------+----------------+-------------------+-----------------+ * |header size(*1)|next header size|extended header(*3)| compressed data | * +---------------+----------------+-------------------+-----------------+ * *------------------------(*1)-----------------------> <------(*2)-----> * */ #define H3_FIELD_LEN_OFFSET 0 #define H3_COMP_SIZE_OFFSET 7 #define H3_ORIG_SIZE_OFFSET 11 #define H3_TIME_OFFSET 15 #define H3_CRC_OFFSET 21 #define H3_HEADER_SIZE_OFFSET 24 #define H3_FIXED_SIZE 28 static int lha_read_file_header_3(struct archive_read *a, struct lha *lha) { const unsigned char *p; size_t extdsize; int err; uint16_t header_crc; if ((p = __archive_read_ahead(a, H3_FIXED_SIZE, NULL)) == NULL) return (truncated_error(a)); if (archive_le16dec(p + H3_FIELD_LEN_OFFSET) != 4) goto invalid; lha->header_size =archive_le32dec(p + H3_HEADER_SIZE_OFFSET); lha->compsize = archive_le32dec(p + H3_COMP_SIZE_OFFSET); lha->origsize = archive_le32dec(p + H3_ORIG_SIZE_OFFSET); lha->mtime = archive_le32dec(p + H3_TIME_OFFSET); lha->crc = archive_le16dec(p + H3_CRC_OFFSET); lha->setflag |= CRC_IS_SET; if (lha->header_size < H3_FIXED_SIZE + 4) goto invalid; header_crc = lha_crc16(0, p, H3_FIXED_SIZE); __archive_read_consume(a, H3_FIXED_SIZE); /* Read extended headers */ err = lha_read_file_extended_header(a, lha, &header_crc, 4, lha->header_size - H3_FIXED_SIZE, &extdsize); if (err < ARCHIVE_WARN) return (err); if (header_crc != lha->header_crc) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "LHa header CRC error"); return (ARCHIVE_FATAL); } return (err); invalid: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid LHa header"); return (ARCHIVE_FATAL); } /* * Extended header format * * +0 +2 +3 -- used in header 1 and 2 * +0 +4 +5 -- used in header 3 * +--------------+---------+-------------------+--------------+-- * |ex-header size|header id| data |ex-header size| ....... * +--------------+---------+-------------------+--------------+-- * <-------------( ex-header size)------------> <-- next extended header --* * * If the ex-header size is zero, it is the make of the end of extended * headers. * */ static int lha_read_file_extended_header(struct archive_read *a, struct lha *lha, uint16_t *crc, int sizefield_length, size_t limitsize, size_t *total_size) { const void *h; const unsigned char *extdheader; size_t extdsize; size_t datasize; unsigned int i; unsigned char extdtype; #define EXT_HEADER_CRC 0x00 /* Header CRC and information*/ #define EXT_FILENAME 0x01 /* Filename */ #define EXT_DIRECTORY 0x02 /* Directory name */ #define EXT_DOS_ATTR 0x40 /* MS-DOS attribute */ #define EXT_TIMESTAMP 0x41 /* Windows time stamp */ #define EXT_FILESIZE 0x42 /* Large file size */ #define EXT_TIMEZONE 0x43 /* Time zone */ #define EXT_UTF16_FILENAME 0x44 /* UTF-16 filename */ #define EXT_UTF16_DIRECTORY 0x45 /* UTF-16 directory name */ #define EXT_CODEPAGE 0x46 /* Codepage */ #define EXT_UNIX_MODE 0x50 /* File permission */ #define EXT_UNIX_GID_UID 0x51 /* gid,uid */ #define EXT_UNIX_GNAME 0x52 /* Group name */ #define EXT_UNIX_UNAME 0x53 /* User name */ #define EXT_UNIX_MTIME 0x54 /* Modified time */ #define EXT_OS2_NEW_ATTR 0x7f /* new attribute(OS/2 only) */ #define EXT_NEW_ATTR 0xff /* new attribute */ *total_size = sizefield_length; for (;;) { /* Read an extended header size. */ if ((h = __archive_read_ahead(a, sizefield_length, NULL)) == NULL) return (truncated_error(a)); /* Check if the size is the zero indicates the end of the * extended header. */ if (sizefield_length == sizeof(uint16_t)) extdsize = archive_le16dec(h); else extdsize = archive_le32dec(h); if (extdsize == 0) { /* End of extended header */ if (crc != NULL) *crc = lha_crc16(*crc, h, sizefield_length); __archive_read_consume(a, sizefield_length); return (ARCHIVE_OK); } /* Sanity check to the extended header size. */ if (((uint64_t)*total_size + extdsize) > (uint64_t)limitsize || extdsize <= (size_t)sizefield_length) goto invalid; /* Read the extended header. */ if ((h = __archive_read_ahead(a, extdsize, NULL)) == NULL) return (truncated_error(a)); *total_size += extdsize; extdheader = (const unsigned char *)h; /* Get the extended header type. */ extdtype = extdheader[sizefield_length]; /* Calculate an extended data size. */ datasize = extdsize - (1 + sizefield_length); /* Skip an extended header size field and type field. */ extdheader += sizefield_length + 1; if (crc != NULL && extdtype != EXT_HEADER_CRC) *crc = lha_crc16(*crc, h, extdsize); switch (extdtype) { case EXT_HEADER_CRC: /* We only use a header CRC. Following data will not * be used. */ if (datasize >= 2) { lha->header_crc = archive_le16dec(extdheader); if (crc != NULL) { static const char zeros[2] = {0, 0}; *crc = lha_crc16(*crc, h, extdsize - datasize); /* CRC value itself as zero */ *crc = lha_crc16(*crc, zeros, 2); *crc = lha_crc16(*crc, extdheader+2, datasize - 2); } } break; case EXT_FILENAME: if (datasize == 0) { /* maybe directory header */ archive_string_empty(&lha->filename); break; } if (extdheader[0] == '\0') goto invalid; archive_strncpy(&lha->filename, (const char *)extdheader, datasize); break; case EXT_DIRECTORY: if (datasize == 0 || extdheader[0] == '\0') /* no directory name data. exit this case. */ goto invalid; archive_strncpy(&lha->dirname, (const char *)extdheader, datasize); /* * Convert directory delimiter from 0xFF * to '/' for local system. */ for (i = 0; i < lha->dirname.length; i++) { if ((unsigned char)lha->dirname.s[i] == 0xFF) lha->dirname.s[i] = '/'; } /* Is last character directory separator? */ if (lha->dirname.s[lha->dirname.length-1] != '/') /* invalid directory data */ goto invalid; break; case EXT_DOS_ATTR: if (datasize == 2) lha->dos_attr = (unsigned char) (archive_le16dec(extdheader) & 0xff); break; case EXT_TIMESTAMP: if (datasize == (sizeof(uint64_t) * 3)) { lha->birthtime = lha_win_time( archive_le64dec(extdheader), &lha->birthtime_tv_nsec); extdheader += sizeof(uint64_t); lha->mtime = lha_win_time( archive_le64dec(extdheader), &lha->mtime_tv_nsec); extdheader += sizeof(uint64_t); lha->atime = lha_win_time( archive_le64dec(extdheader), &lha->atime_tv_nsec); lha->setflag |= BIRTHTIME_IS_SET | ATIME_IS_SET; } break; case EXT_FILESIZE: if (datasize == sizeof(uint64_t) * 2) { lha->compsize = archive_le64dec(extdheader); extdheader += sizeof(uint64_t); lha->origsize = archive_le64dec(extdheader); } break; case EXT_CODEPAGE: /* Get an archived filename charset from codepage. * This overwrites the charset specified by * hdrcharset option. */ if (datasize == sizeof(uint32_t)) { struct archive_string cp; const char *charset; archive_string_init(&cp); switch (archive_le32dec(extdheader)) { case 65001: /* UTF-8 */ charset = "UTF-8"; break; default: archive_string_sprintf(&cp, "CP%d", (int)archive_le32dec(extdheader)); charset = cp.s; break; } lha->sconv = archive_string_conversion_from_charset( &(a->archive), charset, 1); archive_string_free(&cp); if (lha->sconv == NULL) return (ARCHIVE_FATAL); } break; case EXT_UNIX_MODE: if (datasize == sizeof(uint16_t)) { lha->mode = archive_le16dec(extdheader); lha->setflag |= UNIX_MODE_IS_SET; } break; case EXT_UNIX_GID_UID: if (datasize == (sizeof(uint16_t) * 2)) { lha->gid = archive_le16dec(extdheader); lha->uid = archive_le16dec(extdheader+2); } break; case EXT_UNIX_GNAME: if (datasize > 0) archive_strncpy(&lha->gname, (const char *)extdheader, datasize); break; case EXT_UNIX_UNAME: if (datasize > 0) archive_strncpy(&lha->uname, (const char *)extdheader, datasize); break; case EXT_UNIX_MTIME: if (datasize == sizeof(uint32_t)) lha->mtime = archive_le32dec(extdheader); break; case EXT_OS2_NEW_ATTR: /* This extended header is OS/2 depend. */ if (datasize == 16) { lha->dos_attr = (unsigned char) (archive_le16dec(extdheader) & 0xff); lha->mode = archive_le16dec(extdheader+2); lha->gid = archive_le16dec(extdheader+4); lha->uid = archive_le16dec(extdheader+6); lha->birthtime = archive_le32dec(extdheader+8); lha->atime = archive_le32dec(extdheader+12); lha->setflag |= UNIX_MODE_IS_SET | BIRTHTIME_IS_SET | ATIME_IS_SET; } break; case EXT_NEW_ATTR: if (datasize == 20) { lha->mode = (mode_t)archive_le32dec(extdheader); lha->gid = archive_le32dec(extdheader+4); lha->uid = archive_le32dec(extdheader+8); lha->birthtime = archive_le32dec(extdheader+12); lha->atime = archive_le32dec(extdheader+16); lha->setflag |= UNIX_MODE_IS_SET | BIRTHTIME_IS_SET | ATIME_IS_SET; } break; case EXT_TIMEZONE: /* Not supported */ case EXT_UTF16_FILENAME: /* Not supported */ case EXT_UTF16_DIRECTORY: /* Not supported */ default: break; } __archive_read_consume(a, extdsize); } invalid: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid extended LHa header"); return (ARCHIVE_FATAL); } static int lha_end_of_entry(struct archive_read *a) { struct lha *lha = (struct lha *)(a->format->data); int r = ARCHIVE_EOF; if (!lha->end_of_entry_cleanup) { if ((lha->setflag & CRC_IS_SET) && lha->crc != lha->entry_crc_calculated) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "LHa data CRC error"); r = ARCHIVE_WARN; } /* End-of-entry cleanup done. */ lha->end_of_entry_cleanup = 1; } return (r); } static int archive_read_format_lha_read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct lha *lha = (struct lha *)(a->format->data); int r; if (lha->entry_unconsumed) { /* Consume as much as the decompressor actually used. */ __archive_read_consume(a, lha->entry_unconsumed); lha->entry_unconsumed = 0; } if (lha->end_of_entry) { *offset = lha->entry_offset; *size = 0; *buff = NULL; return (lha_end_of_entry(a)); } if (lha->entry_is_compressed) r = lha_read_data_lzh(a, buff, size, offset); else /* No compression. */ r = lha_read_data_none(a, buff, size, offset); return (r); } /* * Read a file content in no compression. * * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets * lha->end_of_entry if it consumes all of the data. */ static int lha_read_data_none(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct lha *lha = (struct lha *)(a->format->data); ssize_t bytes_avail; if (lha->entry_bytes_remaining == 0) { *buff = NULL; *size = 0; *offset = lha->entry_offset; lha->end_of_entry = 1; return (ARCHIVE_OK); } /* * 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); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated LHa file data"); return (ARCHIVE_FATAL); } if (bytes_avail > lha->entry_bytes_remaining) bytes_avail = (ssize_t)lha->entry_bytes_remaining; lha->entry_crc_calculated = lha_crc16(lha->entry_crc_calculated, *buff, bytes_avail); *size = bytes_avail; *offset = lha->entry_offset; lha->entry_offset += bytes_avail; lha->entry_bytes_remaining -= bytes_avail; if (lha->entry_bytes_remaining == 0) lha->end_of_entry = 1; lha->entry_unconsumed = bytes_avail; return (ARCHIVE_OK); } /* * Read a file content in LZHUFF encoding. * * Returns ARCHIVE_OK if successful, returns ARCHIVE_WARN if compression is * unsupported, ARCHIVE_FATAL otherwise, sets lha->end_of_entry if it consumes * all of the data. */ static int lha_read_data_lzh(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { struct lha *lha = (struct lha *)(a->format->data); ssize_t bytes_avail; int r; /* If we haven't yet read any data, initialize the decompressor. */ if (!lha->decompress_init) { r = lzh_decode_init(&(lha->strm), lha->method); switch (r) { case ARCHIVE_OK: break; case ARCHIVE_FAILED: /* Unsupported compression. */ *buff = NULL; *size = 0; *offset = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unsupported lzh compression method -%c%c%c-", lha->method[0], lha->method[1], lha->method[2]); /* We know compressed size; just skip it. */ archive_read_format_lha_read_data_skip(a); return (ARCHIVE_WARN); default: archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory " "for lzh decompression"); return (ARCHIVE_FATAL); } /* We've initialized decompression for this stream. */ lha->decompress_init = 1; lha->strm.avail_out = 0; lha->strm.total_out = 0; } /* * 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. */ lha->strm.next_in = __archive_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated LHa file body"); return (ARCHIVE_FATAL); } if (bytes_avail > lha->entry_bytes_remaining) bytes_avail = (ssize_t)lha->entry_bytes_remaining; lha->strm.avail_in = (int)bytes_avail; lha->strm.total_in = 0; lha->strm.avail_out = 0; r = lzh_decode(&(lha->strm), bytes_avail == lha->entry_bytes_remaining); switch (r) { case ARCHIVE_OK: break; case ARCHIVE_EOF: lha->end_of_entry = 1; break; default: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Bad lzh data"); return (ARCHIVE_FAILED); } lha->entry_unconsumed = lha->strm.total_in; lha->entry_bytes_remaining -= lha->strm.total_in; if (lha->strm.avail_out) { *offset = lha->entry_offset; *size = lha->strm.avail_out; *buff = lha->strm.ref_ptr; lha->entry_crc_calculated = lha_crc16(lha->entry_crc_calculated, *buff, *size); lha->entry_offset += *size; } else { *offset = lha->entry_offset; *size = 0; *buff = NULL; if (lha->end_of_entry) return (lha_end_of_entry(a)); } return (ARCHIVE_OK); } /* * Skip a file content. */ static int archive_read_format_lha_read_data_skip(struct archive_read *a) { struct lha *lha; int64_t bytes_skipped; lha = (struct lha *)(a->format->data); if (lha->entry_unconsumed) { /* Consume as much as the decompressor actually used. */ __archive_read_consume(a, lha->entry_unconsumed); lha->entry_unconsumed = 0; } /* if we've already read to end of data, we're done. */ if (lha->end_of_entry_cleanup) return (ARCHIVE_OK); /* * If the length is at the beginning, we can skip the * compressed data much more quickly. */ bytes_skipped = __archive_read_consume(a, lha->entry_bytes_remaining); if (bytes_skipped < 0) return (ARCHIVE_FATAL); /* This entry is finished and done. */ lha->end_of_entry_cleanup = lha->end_of_entry = 1; return (ARCHIVE_OK); } static int archive_read_format_lha_cleanup(struct archive_read *a) { struct lha *lha = (struct lha *)(a->format->data); lzh_decode_free(&(lha->strm)); archive_string_free(&(lha->dirname)); archive_string_free(&(lha->filename)); archive_string_free(&(lha->uname)); archive_string_free(&(lha->gname)); archive_wstring_free(&(lha->ws)); free(lha); (a->format->data) = NULL; return (ARCHIVE_OK); } /* * 'LHa for UNIX' utility has archived a symbolic-link name after * a pathname with '|' character. * This function extracts the symbolic-link name from the pathname. * * example. * 1. a symbolic-name is 'aaa/bb/cc' * 2. a filename is 'xxx/bbb' * then a archived pathname is 'xxx/bbb|aaa/bb/cc' */ static int lha_parse_linkname(struct archive_string *linkname, struct archive_string *pathname) { char * linkptr; size_t symlen; linkptr = strchr(pathname->s, '|'); if (linkptr != NULL) { symlen = strlen(linkptr + 1); archive_strncpy(linkname, linkptr+1, symlen); *linkptr = 0; pathname->length = strlen(pathname->s); return (1); } return (0); } /* Convert an MSDOS-style date/time into Unix-style time. */ static time_t lha_dos_time(const unsigned char *p) { int msTime, msDate; struct tm ts; msTime = archive_le16dec(p); msDate = archive_le16dec(p+2); 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)); } /* Convert an MS-Windows-style date/time into Unix-style time. */ static time_t lha_win_time(uint64_t wintime, long *ns) { #define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000) if (wintime >= EPOC_TIME) { wintime -= EPOC_TIME; /* 1970-01-01 00:00:00 (UTC) */ if (ns != NULL) *ns = (long)(wintime % 10000000) * 100; return (wintime / 10000000); } else { if (ns != NULL) *ns = 0; return (0); } } static unsigned char lha_calcsum(unsigned char sum, const void *pp, int offset, size_t size) { unsigned char const *p = (unsigned char const *)pp; p += offset; for (;size > 0; --size) sum += *p++; return (sum); } static uint16_t crc16tbl[2][256]; static void lha_crc16_init(void) { unsigned int i; static int crc16init = 0; if (crc16init) return; crc16init = 1; for (i = 0; i < 256; i++) { unsigned int j; uint16_t crc = (uint16_t)i; for (j = 8; j; j--) crc = (crc >> 1) ^ ((crc & 1) * 0xA001); crc16tbl[0][i] = crc; } for (i = 0; i < 256; i++) { crc16tbl[1][i] = (crc16tbl[0][i] >> 8) ^ crc16tbl[0][crc16tbl[0][i] & 0xff]; } } static uint16_t lha_crc16(uint16_t crc, const void *pp, size_t len) { const unsigned char *p = (const unsigned char *)pp; const uint16_t *buff; const union { uint32_t i; char c[4]; } u = { 0x01020304 }; if (len == 0) return crc; /* Process unaligned address. */ if (((uintptr_t)p) & (uintptr_t)0x1) { crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff]; len--; } buff = (const uint16_t *)p; /* * Modern C compiler such as GCC does not unroll automatically yet * without unrolling pragma, and Clang is so. So we should * unroll this loop for its performance. */ for (;len >= 8; len -= 8) { /* This if statement expects compiler optimization will * remove the statement which will not be executed. */ #undef bswap16 #if defined(_MSC_VER) && _MSC_VER >= 1400 /* Visual Studio */ # define bswap16(x) _byteswap_ushort(x) #elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ > 4) /* GCC 4.8 and later has __builtin_bswap16() */ # define bswap16(x) __builtin_bswap16(x) #elif defined(__clang__) /* All clang versions have __builtin_bswap16() */ # define bswap16(x) __builtin_bswap16(x) #else # define bswap16(x) ((((x) >> 8) & 0xff) | ((x) << 8)) #endif #define CRC16W do { \ if(u.c[0] == 1) { /* Big endian */ \ crc ^= bswap16(*buff); buff++; \ } else \ crc ^= *buff++; \ crc = crc16tbl[1][crc & 0xff] ^ crc16tbl[0][crc >> 8];\ } while (0) CRC16W; CRC16W; CRC16W; CRC16W; #undef CRC16W #undef bswap16 } p = (const unsigned char *)buff; for (;len; len--) { crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff]; } return crc; } /* * Initialize LZHUF decoder. * * Returns ARCHIVE_OK if initialization was successful. * Returns ARCHIVE_FAILED if method is unsupported. * Returns ARCHIVE_FATAL if initialization failed; memory allocation * error occurred. */ static int lzh_decode_init(struct lzh_stream *strm, const char *method) { struct lzh_dec *ds; int w_bits, w_size; if (strm->ds == NULL) { strm->ds = calloc(1, sizeof(*strm->ds)); if (strm->ds == NULL) return (ARCHIVE_FATAL); } ds = strm->ds; ds->error = ARCHIVE_FAILED; if (method == NULL || method[0] != 'l' || method[1] != 'h') return (ARCHIVE_FAILED); switch (method[2]) { case '5': w_bits = 13;/* 8KiB for window */ break; case '6': w_bits = 15;/* 32KiB for window */ break; case '7': w_bits = 16;/* 64KiB for window */ break; default: return (ARCHIVE_FAILED);/* Not supported. */ } ds->error = ARCHIVE_FATAL; /* Expand a window size up to 128 KiB for decompressing process * performance whatever its original window size is. */ ds->w_size = 1U << 17; ds->w_mask = ds->w_size -1; if (ds->w_buff == NULL) { ds->w_buff = malloc(ds->w_size); if (ds->w_buff == NULL) return (ARCHIVE_FATAL); } w_size = 1U << w_bits; memset(ds->w_buff + ds->w_size - w_size, 0x20, w_size); ds->w_pos = 0; ds->state = 0; ds->pos_pt_len_size = w_bits + 1; ds->pos_pt_len_bits = (w_bits == 15 || w_bits == 16)? 5: 4; ds->literal_pt_len_size = PT_BITLEN_SIZE; ds->literal_pt_len_bits = 5; ds->br.cache_buffer = 0; ds->br.cache_avail = 0; if (lzh_huffman_init(&(ds->lt), LT_BITLEN_SIZE, 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); ds->lt.len_bits = 9; if (lzh_huffman_init(&(ds->pt), PT_BITLEN_SIZE, 16) != ARCHIVE_OK) return (ARCHIVE_FATAL); ds->error = 0; return (ARCHIVE_OK); } /* * Release LZHUF decoder. */ static void lzh_decode_free(struct lzh_stream *strm) { if (strm->ds == NULL) return; free(strm->ds->w_buff); lzh_huffman_free(&(strm->ds->lt)); lzh_huffman_free(&(strm->ds->pt)); free(strm->ds); strm->ds = NULL; } /* * Bit stream reader. */ /* Check that the cache buffer has enough bits. */ #define lzh_br_has(br, n) ((br)->cache_avail >= n) /* Get compressed data by bit. */ #define lzh_br_bits(br, n) \ (((uint16_t)((br)->cache_buffer >> \ ((br)->cache_avail - (n)))) & cache_masks[n]) #define lzh_br_bits_forced(br, n) \ (((uint16_t)((br)->cache_buffer << \ ((n) - (br)->cache_avail))) & cache_masks[n]) /* Read ahead to make sure the cache buffer has enough compressed data we * will use. * True : completed, there is enough data in the cache buffer. * False : we met that strm->next_in is empty, we have to get following * bytes. */ #define lzh_br_read_ahead_0(strm, br, n) \ (lzh_br_has(br, (n)) || lzh_br_fillup(strm, br)) /* True : the cache buffer has some bits as much as we need. * False : there are no enough bits in the cache buffer to be used, * we have to get following bytes if we could. */ #define lzh_br_read_ahead(strm, br, n) \ (lzh_br_read_ahead_0((strm), (br), (n)) || lzh_br_has((br), (n))) /* Notify how many bits we consumed. */ #define lzh_br_consume(br, n) ((br)->cache_avail -= (n)) #define lzh_br_unconsume(br, n) ((br)->cache_avail += (n)) static const uint16_t cache_masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; /* * Shift away used bits in the cache data and fill it up with following bits. * Call this when cache buffer does not have enough bits you need. * * Returns 1 if the cache buffer is full. * Returns 0 if the cache buffer is not full; input buffer is empty. */ static int lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br) { int n = CACHE_BITS - br->cache_avail; for (;;) { const int x = n >> 3; if (strm->avail_in >= x) { switch (x) { case 8: br->cache_buffer = ((uint64_t)strm->next_in[0]) << 56 | ((uint64_t)strm->next_in[1]) << 48 | ((uint64_t)strm->next_in[2]) << 40 | ((uint64_t)strm->next_in[3]) << 32 | ((uint32_t)strm->next_in[4]) << 24 | ((uint32_t)strm->next_in[5]) << 16 | ((uint32_t)strm->next_in[6]) << 8 | (uint32_t)strm->next_in[7]; strm->next_in += 8; strm->avail_in -= 8; br->cache_avail += 8 * 8; return (1); case 7: br->cache_buffer = (br->cache_buffer << 56) | ((uint64_t)strm->next_in[0]) << 48 | ((uint64_t)strm->next_in[1]) << 40 | ((uint64_t)strm->next_in[2]) << 32 | ((uint32_t)strm->next_in[3]) << 24 | ((uint32_t)strm->next_in[4]) << 16 | ((uint32_t)strm->next_in[5]) << 8 | (uint32_t)strm->next_in[6]; strm->next_in += 7; strm->avail_in -= 7; br->cache_avail += 7 * 8; return (1); case 6: br->cache_buffer = (br->cache_buffer << 48) | ((uint64_t)strm->next_in[0]) << 40 | ((uint64_t)strm->next_in[1]) << 32 | ((uint32_t)strm->next_in[2]) << 24 | ((uint32_t)strm->next_in[3]) << 16 | ((uint32_t)strm->next_in[4]) << 8 | (uint32_t)strm->next_in[5]; strm->next_in += 6; strm->avail_in -= 6; br->cache_avail += 6 * 8; return (1); case 0: /* We have enough compressed data in * the cache buffer.*/ return (1); default: break; } } if (strm->avail_in == 0) { /* There is not enough compressed data to fill up the * cache buffer. */ return (0); } br->cache_buffer = (br->cache_buffer << 8) | *strm->next_in++; strm->avail_in--; br->cache_avail += 8; n -= 8; } } /* * Decode LZHUF. * * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty. * Please set available buffer and call this function again. * 2. Returns ARCHIVE_EOF if decompression has been completed. * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data * is broken or you do not set 'last' flag properly. * 4. 'last' flag is very important, you must set 1 to the flag if there * is no input data. The lha compressed data format does not provide how * to know the compressed data is really finished. * Note: lha command utility check if the total size of output bytes is * reached the uncompressed size recorded in its header. it does not mind * that the decoding process is properly finished. * GNU ZIP can decompress another compressed file made by SCO LZH compress. * it handles EOF as null to fill read buffer with zero until the decoding * process meet 2 bytes of zeros at reading a size of a next chunk, so the * zeros are treated as the mark of the end of the data although the zeros * is dummy, not the file data. */ static int lzh_read_blocks(struct lzh_stream *, int); static int lzh_decode_blocks(struct lzh_stream *, int); #define ST_RD_BLOCK 0 #define ST_RD_PT_1 1 #define ST_RD_PT_2 2 #define ST_RD_PT_3 3 #define ST_RD_PT_4 4 #define ST_RD_LITERAL_1 5 #define ST_RD_LITERAL_2 6 #define ST_RD_LITERAL_3 7 #define ST_RD_POS_DATA_1 8 #define ST_GET_LITERAL 9 #define ST_GET_POS_1 10 #define ST_GET_POS_2 11 #define ST_COPY_DATA 12 static int lzh_decode(struct lzh_stream *strm, int last) { struct lzh_dec *ds = strm->ds; int avail_in; int r; if (ds->error) return (ds->error); avail_in = strm->avail_in; do { if (ds->state < ST_GET_LITERAL) r = lzh_read_blocks(strm, last); else r = lzh_decode_blocks(strm, last); } while (r == 100); strm->total_in += avail_in - strm->avail_in; return (r); } static void lzh_emit_window(struct lzh_stream *strm, size_t s) { strm->ref_ptr = strm->ds->w_buff; strm->avail_out = (int)s; strm->total_out += s; } static int lzh_read_blocks(struct lzh_stream *strm, int last) { struct lzh_dec *ds = strm->ds; struct lzh_br *br = &(ds->br); int c = 0, i; unsigned rbits; for (;;) { switch (ds->state) { case ST_RD_BLOCK: /* * Read a block number indicates how many blocks * we will handle. The block is composed of a * literal and a match, sometimes a literal only * in particular, there are no reference data at * the beginning of the decompression. */ if (!lzh_br_read_ahead_0(strm, br, 16)) { if (!last) /* We need following data. */ return (ARCHIVE_OK); if (lzh_br_has(br, 8)) { /* * It seems there are extra bits. * 1. Compressed data is broken. * 2. `last' flag does not properly * set. */ goto failed; } if (ds->w_pos > 0) { lzh_emit_window(strm, ds->w_pos); ds->w_pos = 0; return (ARCHIVE_OK); } /* End of compressed data; we have completely * handled all compressed data. */ return (ARCHIVE_EOF); } ds->blocks_avail = lzh_br_bits(br, 16); if (ds->blocks_avail == 0) goto failed; lzh_br_consume(br, 16); /* * Read a literal table compressed in huffman * coding. */ ds->pt.len_size = ds->literal_pt_len_size; ds->pt.len_bits = ds->literal_pt_len_bits; ds->reading_position = 0; /* FALL THROUGH */ case ST_RD_PT_1: /* Note: ST_RD_PT_1, ST_RD_PT_2 and ST_RD_PT_4 are * used in reading both a literal table and a * position table. */ if (!lzh_br_read_ahead(strm, br, ds->pt.len_bits)) { if (last) goto failed;/* Truncated data. */ ds->state = ST_RD_PT_1; return (ARCHIVE_OK); } ds->pt.len_avail = lzh_br_bits(br, ds->pt.len_bits); lzh_br_consume(br, ds->pt.len_bits); /* FALL THROUGH */ case ST_RD_PT_2: if (ds->pt.len_avail == 0) { /* There is no bitlen. */ if (!lzh_br_read_ahead(strm, br, ds->pt.len_bits)) { if (last) goto failed;/* Truncated data.*/ ds->state = ST_RD_PT_2; return (ARCHIVE_OK); } if (!lzh_make_fake_table(&(ds->pt), lzh_br_bits(br, ds->pt.len_bits))) goto failed;/* Invalid data. */ lzh_br_consume(br, ds->pt.len_bits); if (ds->reading_position) ds->state = ST_GET_LITERAL; else ds->state = ST_RD_LITERAL_1; break; } else if (ds->pt.len_avail > ds->pt.len_size) goto failed;/* Invalid data. */ ds->loop = 0; memset(ds->pt.freq, 0, sizeof(ds->pt.freq)); if (ds->pt.len_avail < 3 || ds->pt.len_size == ds->pos_pt_len_size) { ds->state = ST_RD_PT_4; break; } /* FALL THROUGH */ case ST_RD_PT_3: ds->loop = lzh_read_pt_bitlen(strm, ds->loop, 3); if (ds->loop < 3) { if (ds->loop < 0 || last) goto failed;/* Invalid data. */ /* Not completed, get following data. */ ds->state = ST_RD_PT_3; return (ARCHIVE_OK); } /* There are some null in bitlen of the literal. */ if (!lzh_br_read_ahead(strm, br, 2)) { if (last) goto failed;/* Truncated data. */ ds->state = ST_RD_PT_3; return (ARCHIVE_OK); } c = lzh_br_bits(br, 2); lzh_br_consume(br, 2); if (c > ds->pt.len_avail - 3) goto failed;/* Invalid data. */ for (i = 3; c-- > 0 ;) ds->pt.bitlen[i++] = 0; ds->loop = i; /* FALL THROUGH */ case ST_RD_PT_4: ds->loop = lzh_read_pt_bitlen(strm, ds->loop, ds->pt.len_avail); if (ds->loop < ds->pt.len_avail) { if (ds->loop < 0 || last) goto failed;/* Invalid data. */ /* Not completed, get following data. */ ds->state = ST_RD_PT_4; return (ARCHIVE_OK); } if (!lzh_make_huffman_table(&(ds->pt))) goto failed;/* Invalid data */ if (ds->reading_position) { ds->state = ST_GET_LITERAL; break; } /* FALL THROUGH */ case ST_RD_LITERAL_1: if (!lzh_br_read_ahead(strm, br, ds->lt.len_bits)) { if (last) goto failed;/* Truncated data. */ ds->state = ST_RD_LITERAL_1; return (ARCHIVE_OK); } ds->lt.len_avail = lzh_br_bits(br, ds->lt.len_bits); lzh_br_consume(br, ds->lt.len_bits); /* FALL THROUGH */ case ST_RD_LITERAL_2: if (ds->lt.len_avail == 0) { /* There is no bitlen. */ if (!lzh_br_read_ahead(strm, br, ds->lt.len_bits)) { if (last) goto failed;/* Truncated data.*/ ds->state = ST_RD_LITERAL_2; return (ARCHIVE_OK); } if (!lzh_make_fake_table(&(ds->lt), lzh_br_bits(br, ds->lt.len_bits))) goto failed;/* Invalid data */ lzh_br_consume(br, ds->lt.len_bits); ds->state = ST_RD_POS_DATA_1; break; } else if (ds->lt.len_avail > ds->lt.len_size) goto failed;/* Invalid data */ ds->loop = 0; memset(ds->lt.freq, 0, sizeof(ds->lt.freq)); /* FALL THROUGH */ case ST_RD_LITERAL_3: i = ds->loop; while (i < ds->lt.len_avail) { if (!lzh_br_read_ahead(strm, br, ds->pt.max_bits)) { if (last) goto failed;/* Truncated data.*/ ds->loop = i; ds->state = ST_RD_LITERAL_3; return (ARCHIVE_OK); } rbits = lzh_br_bits(br, ds->pt.max_bits); c = lzh_decode_huffman(&(ds->pt), rbits); if (c > 2) { /* Note: 'c' will never be more than * eighteen since it's limited by * PT_BITLEN_SIZE, which is being set * to ds->pt.len_size through * ds->literal_pt_len_size. */ lzh_br_consume(br, ds->pt.bitlen[c]); c -= 2; ds->lt.freq[c]++; ds->lt.bitlen[i++] = c; } else if (c == 0) { lzh_br_consume(br, ds->pt.bitlen[c]); ds->lt.bitlen[i++] = 0; } else { /* c == 1 or c == 2 */ int n = (c == 1)?4:9; if (!lzh_br_read_ahead(strm, br, ds->pt.bitlen[c] + n)) { if (last) /* Truncated data. */ goto failed; ds->loop = i; ds->state = ST_RD_LITERAL_3; return (ARCHIVE_OK); } lzh_br_consume(br, ds->pt.bitlen[c]); c = lzh_br_bits(br, n); lzh_br_consume(br, n); c += (n == 4)?3:20; if (i + c > ds->lt.len_avail) goto failed;/* Invalid data */ memset(&(ds->lt.bitlen[i]), 0, c); i += c; } } if (i > ds->lt.len_avail || !lzh_make_huffman_table(&(ds->lt))) goto failed;/* Invalid data */ /* FALL THROUGH */ case ST_RD_POS_DATA_1: /* * Read a position table compressed in huffman * coding. */ ds->pt.len_size = ds->pos_pt_len_size; ds->pt.len_bits = ds->pos_pt_len_bits; ds->reading_position = 1; ds->state = ST_RD_PT_1; break; case ST_GET_LITERAL: return (100); } } failed: return (ds->error = ARCHIVE_FAILED); } static int lzh_decode_blocks(struct lzh_stream *strm, int last) { struct lzh_dec *ds = strm->ds; struct lzh_br bre = ds->br; struct huffman *lt = &(ds->lt); struct huffman *pt = &(ds->pt); unsigned char *w_buff = ds->w_buff; unsigned char *lt_bitlen = lt->bitlen; unsigned char *pt_bitlen = pt->bitlen; int blocks_avail = ds->blocks_avail, c = 0; int copy_len = ds->copy_len, copy_pos = ds->copy_pos; int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size; int lt_max_bits = lt->max_bits, pt_max_bits = pt->max_bits; int state = ds->state; for (;;) { switch (state) { case ST_GET_LITERAL: for (;;) { if (blocks_avail == 0) { /* We have decoded all blocks. * Let's handle next blocks. */ ds->state = ST_RD_BLOCK; ds->br = bre; ds->blocks_avail = 0; ds->w_pos = w_pos; ds->copy_pos = 0; return (100); } /* lzh_br_read_ahead() always try to fill the * cache buffer up. In specific situation we * are close to the end of the data, the cache * buffer will not be full and thus we have to * determine if the cache buffer has some bits * as much as we need after lzh_br_read_ahead() * failed. */ if (!lzh_br_read_ahead(strm, &bre, lt_max_bits)) { if (!last) goto next_data; /* Remaining bits are less than * maximum bits(lt.max_bits) but maybe * it still remains as much as we need, * so we should try to use it with * dummy bits. */ c = lzh_decode_huffman(lt, lzh_br_bits_forced(&bre, lt_max_bits)); lzh_br_consume(&bre, lt_bitlen[c]); if (!lzh_br_has(&bre, 0)) goto failed;/* Over read. */ } else { c = lzh_decode_huffman(lt, lzh_br_bits(&bre, lt_max_bits)); lzh_br_consume(&bre, lt_bitlen[c]); } blocks_avail--; if (c > UCHAR_MAX) /* Current block is a match data. */ break; /* * 'c' is exactly a literal code. */ /* Save a decoded code to reference it * afterward. */ w_buff[w_pos] = c; if (++w_pos >= w_size) { w_pos = 0; lzh_emit_window(strm, w_size); goto next_data; } } /* 'c' is the length of a match pattern we have * already extracted, which has be stored in * window(ds->w_buff). */ copy_len = c - (UCHAR_MAX + 1) + MINMATCH; /* FALL THROUGH */ case ST_GET_POS_1: /* * Get a reference position. */ if (!lzh_br_read_ahead(strm, &bre, pt_max_bits)) { if (!last) { state = ST_GET_POS_1; ds->copy_len = copy_len; goto next_data; } copy_pos = lzh_decode_huffman(pt, lzh_br_bits_forced(&bre, pt_max_bits)); lzh_br_consume(&bre, pt_bitlen[copy_pos]); if (!lzh_br_has(&bre, 0)) goto failed;/* Over read. */ } else { copy_pos = lzh_decode_huffman(pt, lzh_br_bits(&bre, pt_max_bits)); lzh_br_consume(&bre, pt_bitlen[copy_pos]); } /* FALL THROUGH */ case ST_GET_POS_2: if (copy_pos > 1) { /* We need an additional adjustment number to * the position. */ int p = copy_pos - 1; if (!lzh_br_read_ahead(strm, &bre, p)) { if (last) goto failed;/* Truncated data.*/ state = ST_GET_POS_2; ds->copy_len = copy_len; ds->copy_pos = copy_pos; goto next_data; } copy_pos = (1 << p) + lzh_br_bits(&bre, p); lzh_br_consume(&bre, p); } /* The position is actually a distance from the last * code we had extracted and thus we have to convert * it to a position of the window. */ copy_pos = (w_pos - copy_pos - 1) & w_mask; /* FALL THROUGH */ case ST_COPY_DATA: /* * Copy `copy_len' bytes as extracted data from * the window into the output buffer. */ for (;;) { int l; l = copy_len; if (copy_pos > w_pos) { if (l > w_size - copy_pos) l = w_size - copy_pos; } else { if (l > w_size - w_pos) l = w_size - w_pos; } if ((copy_pos + l < w_pos) || (w_pos + l < copy_pos)) { /* No overlap. */ memcpy(w_buff + w_pos, w_buff + copy_pos, l); } else { const unsigned char *s; unsigned char *d; int li; d = w_buff + w_pos; s = w_buff + copy_pos; for (li = 0; li < l-1;) { d[li] = s[li];li++; d[li] = s[li];li++; } if (li < l) d[li] = s[li]; } w_pos += l; if (w_pos == w_size) { w_pos = 0; lzh_emit_window(strm, w_size); if (copy_len <= l) state = ST_GET_LITERAL; else { state = ST_COPY_DATA; ds->copy_len = copy_len - l; ds->copy_pos = (copy_pos + l) & w_mask; } goto next_data; } if (copy_len <= l) /* A copy of current pattern ended. */ break; copy_len -= l; copy_pos = (copy_pos + l) & w_mask; } state = ST_GET_LITERAL; break; } } failed: return (ds->error = ARCHIVE_FAILED); next_data: ds->br = bre; ds->blocks_avail = blocks_avail; ds->state = state; ds->w_pos = w_pos; return (ARCHIVE_OK); } static int lzh_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits) { int bits; if (hf->bitlen == NULL) { hf->bitlen = malloc(len_size * sizeof(hf->bitlen[0])); if (hf->bitlen == NULL) return (ARCHIVE_FATAL); } if (hf->tbl == NULL) { if (tbl_bits < HTBL_BITS) bits = tbl_bits; else bits = HTBL_BITS; hf->tbl = malloc(((size_t)1 << bits) * sizeof(hf->tbl[0])); if (hf->tbl == NULL) return (ARCHIVE_FATAL); } if (hf->tree == NULL && tbl_bits > HTBL_BITS) { hf->tree_avail = 1 << (tbl_bits - HTBL_BITS + 4); hf->tree = malloc(hf->tree_avail * sizeof(hf->tree[0])); if (hf->tree == NULL) return (ARCHIVE_FATAL); } hf->len_size = (int)len_size; hf->tbl_bits = tbl_bits; return (ARCHIVE_OK); } static void lzh_huffman_free(struct huffman *hf) { free(hf->bitlen); free(hf->tbl); free(hf->tree); } -static char bitlen_tbl[0x400] = { +static const char bitlen_tbl[0x400] = { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 16, 0 }; static int lzh_read_pt_bitlen(struct lzh_stream *strm, int start, int end) { struct lzh_dec *ds = strm->ds; struct lzh_br *br = &(ds->br); int c, i; for (i = start; i < end; ) { /* * bit pattern the number we need * 000 -> 0 * 001 -> 1 * 010 -> 2 * ... * 110 -> 6 * 1110 -> 7 * 11110 -> 8 * ... * 1111111111110 -> 16 */ if (!lzh_br_read_ahead(strm, br, 3)) return (i); if ((c = lzh_br_bits(br, 3)) == 7) { if (!lzh_br_read_ahead(strm, br, 13)) return (i); c = bitlen_tbl[lzh_br_bits(br, 13) & 0x3FF]; if (c) lzh_br_consume(br, c - 3); else return (-1);/* Invalid data. */ } else lzh_br_consume(br, 3); ds->pt.bitlen[i++] = c; ds->pt.freq[c]++; } return (i); } static int lzh_make_fake_table(struct huffman *hf, uint16_t c) { if (c >= hf->len_size) return (0); hf->tbl[0] = c; hf->max_bits = 0; hf->shift_bits = 0; hf->bitlen[hf->tbl[0]] = 0; return (1); } /* * Make a huffman coding table. */ static int lzh_make_huffman_table(struct huffman *hf) { uint16_t *tbl; const unsigned char *bitlen; int bitptn[17], weight[17]; int i, maxbits = 0, ptn, tbl_size, w; int diffbits, len_avail; /* * Initialize bit patterns. */ ptn = 0; for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) { bitptn[i] = ptn; weight[i] = w; if (hf->freq[i]) { ptn += hf->freq[i] * w; maxbits = i; } } if (ptn != 0x10000 || maxbits > hf->tbl_bits) return (0);/* Invalid */ hf->max_bits = maxbits; /* * Cut out extra bits which we won't house in the table. * This preparation reduces the same calculation in the for-loop * making the table. */ if (maxbits < 16) { int ebits = 16 - maxbits; for (i = 1; i <= maxbits; i++) { bitptn[i] >>= ebits; weight[i] >>= ebits; } } if (maxbits > HTBL_BITS) { unsigned htbl_max; uint16_t *p; diffbits = maxbits - HTBL_BITS; for (i = 1; i <= HTBL_BITS; i++) { bitptn[i] >>= diffbits; weight[i] >>= diffbits; } htbl_max = bitptn[HTBL_BITS] + weight[HTBL_BITS] * hf->freq[HTBL_BITS]; p = &(hf->tbl[htbl_max]); while (p < &hf->tbl[1U<shift_bits = diffbits; /* * Make the table. */ tbl_size = 1 << HTBL_BITS; tbl = hf->tbl; bitlen = hf->bitlen; len_avail = hf->len_avail; hf->tree_used = 0; for (i = 0; i < len_avail; i++) { uint16_t *p; int len, cnt; uint16_t bit; int extlen; struct htree_t *ht; if (bitlen[i] == 0) continue; /* Get a bit pattern */ len = bitlen[i]; ptn = bitptn[len]; cnt = weight[len]; if (len <= HTBL_BITS) { /* Calculate next bit pattern */ if ((bitptn[len] = ptn + cnt) > tbl_size) return (0);/* Invalid */ /* Update the table */ p = &(tbl[ptn]); if (cnt > 7) { uint16_t *pc; cnt -= 8; pc = &p[cnt]; pc[0] = (uint16_t)i; pc[1] = (uint16_t)i; pc[2] = (uint16_t)i; pc[3] = (uint16_t)i; pc[4] = (uint16_t)i; pc[5] = (uint16_t)i; pc[6] = (uint16_t)i; pc[7] = (uint16_t)i; if (cnt > 7) { cnt -= 8; memcpy(&p[cnt], pc, 8 * sizeof(uint16_t)); pc = &p[cnt]; while (cnt > 15) { cnt -= 16; memcpy(&p[cnt], pc, 16 * sizeof(uint16_t)); } } if (cnt) memcpy(p, pc, cnt * sizeof(uint16_t)); } else { while (cnt > 1) { p[--cnt] = (uint16_t)i; p[--cnt] = (uint16_t)i; } if (cnt) p[--cnt] = (uint16_t)i; } continue; } /* * A bit length is too big to be housed to a direct table, * so we use a tree model for its extra bits. */ bitptn[len] = ptn + cnt; bit = 1U << (diffbits -1); extlen = len - HTBL_BITS; p = &(tbl[ptn >> diffbits]); if (*p == 0) { *p = len_avail + hf->tree_used; ht = &(hf->tree[hf->tree_used++]); if (hf->tree_used > hf->tree_avail) return (0);/* Invalid */ ht->left = 0; ht->right = 0; } else { if (*p < len_avail || *p >= (len_avail + hf->tree_used)) return (0);/* Invalid */ ht = &(hf->tree[*p - len_avail]); } while (--extlen > 0) { if (ptn & bit) { if (ht->left < len_avail) { ht->left = len_avail + hf->tree_used; ht = &(hf->tree[hf->tree_used++]); if (hf->tree_used > hf->tree_avail) return (0);/* Invalid */ ht->left = 0; ht->right = 0; } else { ht = &(hf->tree[ht->left - len_avail]); } } else { if (ht->right < len_avail) { ht->right = len_avail + hf->tree_used; ht = &(hf->tree[hf->tree_used++]); if (hf->tree_used > hf->tree_avail) return (0);/* Invalid */ ht->left = 0; ht->right = 0; } else { ht = &(hf->tree[ht->right - len_avail]); } } bit >>= 1; } if (ptn & bit) { if (ht->left != 0) return (0);/* Invalid */ ht->left = (uint16_t)i; } else { if (ht->right != 0) return (0);/* Invalid */ ht->right = (uint16_t)i; } } return (1); } static int lzh_decode_huffman_tree(struct huffman *hf, unsigned rbits, int c) { struct htree_t *ht; int extlen; ht = hf->tree; extlen = hf->shift_bits; while (c >= hf->len_avail) { c -= hf->len_avail; if (extlen-- <= 0 || c >= hf->tree_used) return (0); if (rbits & (1U << extlen)) c = ht[c].left; else c = ht[c].right; } return (c); } static inline int lzh_decode_huffman(struct huffman *hf, unsigned rbits) { int c; /* * At first search an index table for a bit pattern. * If it fails, search a huffman tree for. */ c = hf->tbl[rbits >> hf->shift_bits]; if (c < hf->len_avail || hf->len_avail == 0) return (c); /* This bit pattern needs to be found out at a huffman tree. */ return (lzh_decode_huffman_tree(hf, rbits, c)); } Index: head/contrib/libarchive/libarchive/archive_read_support_format_mtree.c =================================================================== --- head/contrib/libarchive/libarchive/archive_read_support_format_mtree.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_read_support_format_mtree.c (revision 316095) @@ -1,2045 +1,2045 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2008 Joerg Sonnenberger * 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_SYS_STAT_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #ifdef HAVE_FCNTL_H #include #endif #include /* #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_private.h" #include "archive_read_private.h" #include "archive_string.h" #include "archive_pack_dev.h" #ifndef O_BINARY #define O_BINARY 0 #endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif #define MTREE_HAS_DEVICE 0x0001 #define MTREE_HAS_FFLAGS 0x0002 #define MTREE_HAS_GID 0x0004 #define MTREE_HAS_GNAME 0x0008 #define MTREE_HAS_MTIME 0x0010 #define MTREE_HAS_NLINK 0x0020 #define MTREE_HAS_PERM 0x0040 #define MTREE_HAS_SIZE 0x0080 #define MTREE_HAS_TYPE 0x0100 #define MTREE_HAS_UID 0x0200 #define MTREE_HAS_UNAME 0x0400 #define MTREE_HAS_OPTIONAL 0x0800 #define MTREE_HAS_NOCHANGE 0x1000 /* FreeBSD specific */ #define MTREE_HASHTABLE_SIZE 1024 struct mtree_option { struct mtree_option *next; char *value; }; struct mtree_entry { struct mtree_entry *next; struct mtree_option *options; char *name; char full; char used; unsigned int name_hash; struct mtree_entry *hashtable_next; }; struct mtree { struct archive_string line; size_t buffsize; char *buff; int64_t offset; int fd; int archive_format; const char *archive_format_name; struct mtree_entry *entries; struct mtree_entry *this_entry; struct mtree_entry *entry_hashtable[MTREE_HASHTABLE_SIZE]; struct archive_string current_dir; struct archive_string contents_name; struct archive_entry_linkresolver *resolver; int64_t cur_size; char checkfs; }; static int bid_keycmp(const char *, const char *, ssize_t); static int cleanup(struct archive_read *); static int detect_form(struct archive_read *, int *); static unsigned int hash(const char *); static int mtree_bid(struct archive_read *, int); static int parse_file(struct archive_read *, struct archive_entry *, struct mtree *, struct mtree_entry *, int *); static void parse_escapes(char *, struct mtree_entry *); static int parse_line(struct archive_read *, struct archive_entry *, struct mtree *, struct mtree_entry *, int *); static int parse_keyword(struct archive_read *, struct mtree *, struct archive_entry *, struct mtree_option *, int *); static int read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset); static ssize_t readline(struct archive_read *, struct mtree *, char **, ssize_t); static int skip(struct archive_read *a); static int read_header(struct archive_read *, struct archive_entry *); static int64_t mtree_atol10(char **); static int64_t mtree_atol8(char **); static int64_t mtree_atol(char **); /* * There's no standard for TIME_T_MAX/TIME_T_MIN. So we compute them * here. TODO: Move this to configure time, but be careful * about cross-compile environments. */ static int64_t get_time_t_max(void) { #if defined(TIME_T_MAX) return TIME_T_MAX; #else /* ISO C allows time_t to be a floating-point type, but POSIX requires an integer type. The following should work on any system that follows the POSIX conventions. */ if (((time_t)0) < ((time_t)-1)) { /* Time_t is unsigned */ return (~(time_t)0); } else { /* Time_t is signed. */ /* Assume it's the same as int64_t or int32_t */ if (sizeof(time_t) == sizeof(int64_t)) { return (time_t)INT64_MAX; } else { return (time_t)INT32_MAX; } } #endif } static int64_t get_time_t_min(void) { #if defined(TIME_T_MIN) return TIME_T_MIN; #else if (((time_t)0) < ((time_t)-1)) { /* Time_t is unsigned */ return (time_t)0; } else { /* Time_t is signed. */ if (sizeof(time_t) == sizeof(int64_t)) { return (time_t)INT64_MIN; } else { return (time_t)INT32_MIN; } } #endif } static int archive_read_format_mtree_options(struct archive_read *a, const char *key, const char *val) { struct mtree *mtree; mtree = (struct mtree *)(a->format->data); if (strcmp(key, "checkfs") == 0) { /* Allows to read information missing from the mtree from the file system */ if (val == NULL || val[0] == 0) { mtree->checkfs = 0; } else { mtree->checkfs = 1; } 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 void free_options(struct mtree_option *head) { struct mtree_option *next; for (; head != NULL; head = next) { next = head->next; free(head->value); free(head); } } int archive_read_support_format_mtree(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; struct mtree *mtree; int r; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_support_format_mtree"); mtree = (struct mtree *)calloc(1, sizeof(*mtree)); if (mtree == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate mtree data"); return (ARCHIVE_FATAL); } mtree->fd = -1; r = __archive_read_register_format(a, mtree, "mtree", mtree_bid, archive_read_format_mtree_options, read_header, read_data, skip, NULL, cleanup, NULL, NULL); if (r != ARCHIVE_OK) free(mtree); return (ARCHIVE_OK); } static int cleanup(struct archive_read *a) { struct mtree *mtree; struct mtree_entry *p, *q; mtree = (struct mtree *)(a->format->data); p = mtree->entries; while (p != NULL) { q = p->next; free(p->name); free_options(p->options); free(p); p = q; } archive_string_free(&mtree->line); archive_string_free(&mtree->current_dir); archive_string_free(&mtree->contents_name); archive_entry_linkresolver_free(mtree->resolver); free(mtree->buff); free(mtree); (a->format->data) = NULL; return (ARCHIVE_OK); } static ssize_t get_line_size(const char *b, ssize_t avail, ssize_t *nlsize) { ssize_t len; len = 0; while (len < avail) { switch (*b) { case '\0':/* Non-ascii character or control character. */ if (nlsize != NULL) *nlsize = 0; return (-1); case '\r': if (avail-len > 1 && b[1] == '\n') { if (nlsize != NULL) *nlsize = 2; return (len+2); } /* FALL THROUGH */ case '\n': if (nlsize != NULL) *nlsize = 1; return (len+1); default: b++; len++; break; } } if (nlsize != NULL) *nlsize = 0; return (avail); } /* * <---------------- ravail ---------------------> * <-- diff ------> <--- avail -----------------> * <---- len -----------> * | Previous lines | line being parsed nl extra | * ^ * b * */ static ssize_t next_line(struct archive_read *a, const char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl) { ssize_t len; int quit; quit = 0; if (*avail == 0) { *nl = 0; len = 0; } else len = get_line_size(*b, *avail, nl); /* * Read bytes more while it does not reach the end of line. */ while (*nl == 0 && len == *avail && !quit) { ssize_t diff = *ravail - *avail; size_t nbytes_req = (*ravail+1023) & ~1023U; ssize_t tested; /* Increase reading bytes if it is not enough to at least * new two lines. */ if (nbytes_req < (size_t)*ravail + 160) nbytes_req <<= 1; *b = __archive_read_ahead(a, nbytes_req, avail); if (*b == NULL) { if (*ravail >= *avail) return (0); /* Reading bytes reaches the end of file. */ *b = __archive_read_ahead(a, *avail, avail); quit = 1; } *ravail = *avail; *b += diff; *avail -= diff; tested = len;/* Skip some bytes we already determinated. */ len = get_line_size(*b + len, *avail - len, nl); if (len >= 0) len += tested; } return (len); } /* * Compare characters with a mtree keyword. * Returns the length of a mtree keyword if matched. * Returns 0 if not matched. */ static int bid_keycmp(const char *p, const char *key, ssize_t len) { int match_len = 0; while (len > 0 && *p && *key) { if (*p == *key) { --len; ++p; ++key; ++match_len; continue; } return (0);/* Not match */ } if (*key != '\0') return (0);/* Not match */ /* A following character should be specified characters */ if (p[0] == '=' || p[0] == ' ' || p[0] == '\t' || p[0] == '\n' || p[0] == '\r' || (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r'))) return (match_len); return (0);/* Not match */ } /* * Test whether the characters 'p' has is mtree keyword. * Returns the length of a detected keyword. * Returns 0 if any keywords were not found. */ static int bid_keyword(const char *p, ssize_t len) { - static const char *keys_c[] = { + static const char * const keys_c[] = { "content", "contents", "cksum", NULL }; - static const char *keys_df[] = { + static const char * const keys_df[] = { "device", "flags", NULL }; - static const char *keys_g[] = { + static const char * const keys_g[] = { "gid", "gname", NULL }; - static const char *keys_il[] = { + static const char * const keys_il[] = { "ignore", "inode", "link", NULL }; - static const char *keys_m[] = { + static const char * const keys_m[] = { "md5", "md5digest", "mode", NULL }; - static const char *keys_no[] = { + static const char * const keys_no[] = { "nlink", "nochange", "optional", NULL }; - static const char *keys_r[] = { + static const char * const keys_r[] = { "resdevice", "rmd160", "rmd160digest", NULL }; - static const char *keys_s[] = { + static const char * const keys_s[] = { "sha1", "sha1digest", "sha256", "sha256digest", "sha384", "sha384digest", "sha512", "sha512digest", "size", NULL }; - static const char *keys_t[] = { + static const char * const keys_t[] = { "tags", "time", "type", NULL }; - static const char *keys_u[] = { + static const char * const keys_u[] = { "uid", "uname", NULL }; - const char **keys; + const char * const *keys; int i; switch (*p) { case 'c': keys = keys_c; break; case 'd': case 'f': keys = keys_df; break; case 'g': keys = keys_g; break; case 'i': case 'l': keys = keys_il; break; case 'm': keys = keys_m; break; case 'n': case 'o': keys = keys_no; break; case 'r': keys = keys_r; break; case 's': keys = keys_s; break; case 't': keys = keys_t; break; case 'u': keys = keys_u; break; default: return (0);/* Unknown key */ } for (i = 0; keys[i] != NULL; i++) { int l = bid_keycmp(p, keys[i], len); if (l > 0) return (l); } return (0);/* Unknown key */ } /* * Test whether there is a set of mtree keywords. * Returns the number of keyword. * Returns -1 if we got incorrect sequence. * This function expects a set of "keyword=value". * When "unset" is specified, expects a set of "keyword". */ static int bid_keyword_list(const char *p, ssize_t len, int unset, int last_is_path) { int l; int keycnt = 0; while (len > 0 && *p) { int blank = 0; /* Test whether there are blank characters in the line. */ while (len >0 && (*p == ' ' || *p == '\t')) { ++p; --len; blank = 1; } if (*p == '\n' || *p == '\r') break; if (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r')) break; if (!blank && !last_is_path) /* No blank character. */ return (-1); if (last_is_path && len == 0) return (keycnt); if (unset) { l = bid_keycmp(p, "all", len); if (l > 0) return (1); } /* Test whether there is a correct key in the line. */ l = bid_keyword(p, len); if (l == 0) return (-1);/* Unknown keyword was found. */ p += l; len -= l; keycnt++; /* Skip value */ if (*p == '=') { int value = 0; ++p; --len; while (len > 0 && *p != ' ' && *p != '\t') { ++p; --len; value = 1; } /* A keyword should have a its value unless * "/unset" operation. */ if (!unset && value == 0) return (-1); } } return (keycnt); } static int bid_entry(const char *p, ssize_t len, ssize_t nl, int *last_is_path) { int f = 0; static const unsigned char safe_char[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ /* !"$%&'()*+,-./ EXCLUSION:( )(#) */ 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ /* 0123456789:;<>? EXCLUSION:(=) */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */ /* @ABCDEFGHIJKLMNO */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ /* PQRSTUVWXYZ[\]^_ */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */ /* `abcdefghijklmno */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */ /* pqrstuvwxyz{|}~ */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ }; ssize_t ll; const char *pp = p; const char * const pp_end = pp + len; *last_is_path = 0; /* * Skip the path-name which is quoted. */ for (;pp < pp_end; ++pp) { if (!safe_char[*(const unsigned char *)pp]) { if (*pp != ' ' && *pp != '\t' && *pp != '\r' && *pp != '\n') f = 0; break; } f = 1; } ll = pp_end - pp; /* If a path-name was not found at the first, try to check * a mtree format(a.k.a form D) ``NetBSD's mtree -D'' creates, * which places the path-name at the last. */ if (f == 0) { const char *pb = p + len - nl; int name_len = 0; int slash; /* The form D accepts only a single line for an entry. */ if (pb-2 >= p && pb[-1] == '\\' && (pb[-2] == ' ' || pb[-2] == '\t')) return (-1); if (pb-1 >= p && pb[-1] == '\\') return (-1); slash = 0; while (p <= --pb && *pb != ' ' && *pb != '\t') { if (!safe_char[*(const unsigned char *)pb]) return (-1); name_len++; /* The pathname should have a slash in this * format. */ if (*pb == '/') slash = 1; } if (name_len == 0 || slash == 0) return (-1); /* If '/' is placed at the first in this field, this is not * a valid filename. */ if (pb[1] == '/') return (-1); ll = len - nl - name_len; pp = p; *last_is_path = 1; } return (bid_keyword_list(pp, ll, 0, *last_is_path)); } #define MAX_BID_ENTRY 3 static int mtree_bid(struct archive_read *a, int best_bid) { const char *signature = "#mtree"; const char *p; (void)best_bid; /* UNUSED */ /* Now let's look at the actual header and see if it matches. */ p = __archive_read_ahead(a, strlen(signature), NULL); if (p == NULL) return (-1); if (memcmp(p, signature, strlen(signature)) == 0) return (8 * (int)strlen(signature)); /* * There is not a mtree signature. Let's try to detect mtree format. */ return (detect_form(a, NULL)); } static int detect_form(struct archive_read *a, int *is_form_d) { const char *p; ssize_t avail, ravail; ssize_t detected_bytes = 0, len, nl; int entry_cnt = 0, multiline = 0; int form_D = 0;/* The archive is generated by `NetBSD mtree -D' * (In this source we call it `form D') . */ if (is_form_d != NULL) *is_form_d = 0; p = __archive_read_ahead(a, 1, &avail); if (p == NULL) return (-1); ravail = avail; for (;;) { len = next_line(a, &p, &avail, &ravail, &nl); /* The terminal character of the line should be * a new line character, '\r\n' or '\n'. */ if (len <= 0 || nl == 0) break; if (!multiline) { /* Leading whitespace is never significant, * ignore it. */ while (len > 0 && (*p == ' ' || *p == '\t')) { ++p; --avail; --len; } /* Skip comment or empty line. */ if (p[0] == '#' || p[0] == '\n' || p[0] == '\r') { p += len; avail -= len; continue; } } else { /* A continuance line; the terminal * character of previous line was '\' character. */ if (bid_keyword_list(p, len, 0, 0) <= 0) break; if (multiline == 1) detected_bytes += len; if (p[len-nl-1] != '\\') { if (multiline == 1 && ++entry_cnt >= MAX_BID_ENTRY) break; multiline = 0; } p += len; avail -= len; continue; } if (p[0] != '/') { int last_is_path, keywords; keywords = bid_entry(p, len, nl, &last_is_path); if (keywords >= 0) { detected_bytes += len; if (form_D == 0) { if (last_is_path) form_D = 1; else if (keywords > 0) /* This line is not `form D'. */ form_D = -1; } else if (form_D == 1) { if (!last_is_path && keywords > 0) /* This this is not `form D' * and We cannot accept mixed * format. */ break; } if (!last_is_path && p[len-nl-1] == '\\') /* This line continues. */ multiline = 1; else { /* We've got plenty of correct lines * to assume that this file is a mtree * format. */ if (++entry_cnt >= MAX_BID_ENTRY) break; } } else break; } else if (len > 4 && strncmp(p, "/set", 4) == 0) { if (bid_keyword_list(p+4, len-4, 0, 0) <= 0) break; /* This line continues. */ if (p[len-nl-1] == '\\') multiline = 2; } else if (len > 6 && strncmp(p, "/unset", 6) == 0) { if (bid_keyword_list(p+6, len-6, 1, 0) <= 0) break; /* This line continues. */ if (p[len-nl-1] == '\\') multiline = 2; } else break; /* Test next line. */ p += len; avail -= len; } if (entry_cnt >= MAX_BID_ENTRY || (entry_cnt > 0 && len == 0)) { if (is_form_d != NULL) { if (form_D == 1) *is_form_d = 1; } return (32); } return (0); } /* * The extended mtree format permits multiple lines specifying * attributes for each file. For those entries, only the last line * is actually used. Practically speaking, that means we have * to read the entire mtree file into memory up front. * * The parsing is done in two steps. First, it is decided if a line * changes the global defaults and if it is, processed accordingly. * Otherwise, the options of the line are merged with the current * global options. */ static int add_option(struct archive_read *a, struct mtree_option **global, const char *value, size_t len) { struct mtree_option *opt; if ((opt = malloc(sizeof(*opt))) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } if ((opt->value = malloc(len + 1)) == NULL) { free(opt); archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(opt->value, value, len); opt->value[len] = '\0'; opt->next = *global; *global = opt; return (ARCHIVE_OK); } static void remove_option(struct mtree_option **global, const char *value, size_t len) { struct mtree_option *iter, *last; last = NULL; for (iter = *global; iter != NULL; last = iter, iter = iter->next) { if (strncmp(iter->value, value, len) == 0 && (iter->value[len] == '\0' || iter->value[len] == '=')) break; } if (iter == NULL) return; if (last == NULL) *global = iter->next; else last->next = iter->next; free(iter->value); free(iter); } static int process_global_set(struct archive_read *a, struct mtree_option **global, const char *line) { const char *next, *eq; size_t len; int r; line += 4; for (;;) { next = line + strspn(line, " \t\r\n"); if (*next == '\0') return (ARCHIVE_OK); line = next; next = line + strcspn(line, " \t\r\n"); eq = strchr(line, '='); if (eq > next) len = next - line; else len = eq - line; remove_option(global, line, len); r = add_option(a, global, line, next - line); if (r != ARCHIVE_OK) return (r); line = next; } } static int process_global_unset(struct archive_read *a, struct mtree_option **global, const char *line) { const char *next; size_t len; line += 6; if (strchr(line, '=') != NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "/unset shall not contain `='"); return ARCHIVE_FATAL; } for (;;) { next = line + strspn(line, " \t\r\n"); if (*next == '\0') return (ARCHIVE_OK); line = next; len = strcspn(line, " \t\r\n"); if (len == 3 && strncmp(line, "all", 3) == 0) { free_options(*global); *global = NULL; } else { remove_option(global, line, len); } line += len; } } static int process_add_entry(struct archive_read *a, struct mtree *mtree, struct mtree_option **global, const char *line, ssize_t line_len, struct mtree_entry **last_entry, int is_form_d) { struct mtree_entry *entry, *ht_iter; struct mtree_option *iter; const char *next, *eq, *name, *end; size_t name_len, len; int r, i; unsigned int ht_idx; if ((entry = malloc(sizeof(*entry))) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } entry->next = NULL; entry->options = NULL; entry->name = NULL; entry->used = 0; entry->full = 0; entry->name_hash = 0; entry->hashtable_next = NULL; /* Add this entry to list. */ if (*last_entry == NULL) mtree->entries = entry; else (*last_entry)->next = entry; *last_entry = entry; if (is_form_d) { /* Filename is last item on line. */ /* Adjust line_len to trim trailing whitespace */ while (line_len > 0) { char last_character = line[line_len - 1]; if (last_character == '\r' || last_character == '\n' || last_character == '\t' || last_character == ' ') { line_len--; } else { break; } } /* Name starts after the last whitespace separator */ name = line; for (i = 0; i < line_len; i++) { if (line[i] == '\r' || line[i] == '\n' || line[i] == '\t' || line[i] == ' ') { name = line + i + 1; } } name_len = line + line_len - name; end = name; } else { /* Filename is first item on line */ name_len = strcspn(line, " \t\r\n"); name = line; line += name_len; end = line + line_len; } /* name/name_len is the name within the line. */ /* line..end brackets the entire line except the name */ if ((entry->name = malloc(name_len + 1)) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } memcpy(entry->name, name, name_len); entry->name[name_len] = '\0'; parse_escapes(entry->name, entry); entry->name_hash = hash(entry->name); ht_idx = entry->name_hash % MTREE_HASHTABLE_SIZE; if ((ht_iter = mtree->entry_hashtable[ht_idx]) != NULL) { while (ht_iter->hashtable_next) ht_iter = ht_iter->hashtable_next; ht_iter->hashtable_next = entry; } else { mtree->entry_hashtable[ht_idx] = entry; } for (iter = *global; iter != NULL; iter = iter->next) { r = add_option(a, &entry->options, iter->value, strlen(iter->value)); if (r != ARCHIVE_OK) return (r); } for (;;) { next = line + strspn(line, " \t\r\n"); if (*next == '\0') return (ARCHIVE_OK); if (next >= end) return (ARCHIVE_OK); line = next; next = line + strcspn(line, " \t\r\n"); eq = strchr(line, '='); if (eq == NULL || eq > next) len = next - line; else len = eq - line; remove_option(&entry->options, line, len); r = add_option(a, &entry->options, line, next - line); if (r != ARCHIVE_OK) return (r); line = next; } } static int read_mtree(struct archive_read *a, struct mtree *mtree) { ssize_t len; uintmax_t counter; char *p; struct mtree_option *global; struct mtree_entry *last_entry; int r, is_form_d; mtree->archive_format = ARCHIVE_FORMAT_MTREE; mtree->archive_format_name = "mtree"; global = NULL; last_entry = NULL; (void)detect_form(a, &is_form_d); for (counter = 1; ; ++counter) { len = readline(a, mtree, &p, 65536); if (len == 0) { mtree->this_entry = mtree->entries; free_options(global); return (ARCHIVE_OK); } if (len < 0) { free_options(global); return ((int)len); } /* Leading whitespace is never significant, ignore it. */ while (*p == ' ' || *p == '\t') { ++p; --len; } /* Skip content lines and blank lines. */ if (*p == '#') continue; if (*p == '\r' || *p == '\n' || *p == '\0') continue; if (*p != '/') { r = process_add_entry(a, mtree, &global, p, len, &last_entry, is_form_d); } else if (len > 4 && strncmp(p, "/set", 4) == 0) { if (p[4] != ' ' && p[4] != '\t') break; r = process_global_set(a, &global, p); } else if (len > 6 && strncmp(p, "/unset", 6) == 0) { if (p[6] != ' ' && p[6] != '\t') break; r = process_global_unset(a, &global, p); } else break; if (r != ARCHIVE_OK) { free_options(global); return r; } } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't parse line %ju", counter); free_options(global); return (ARCHIVE_FATAL); } /* * Read in the entire mtree file into memory on the first request. * Then use the next unused file to satisfy each header request. */ static int read_header(struct archive_read *a, struct archive_entry *entry) { struct mtree *mtree; char *p; int r, use_next; mtree = (struct mtree *)(a->format->data); if (mtree->fd >= 0) { close(mtree->fd); mtree->fd = -1; } if (mtree->entries == NULL) { mtree->resolver = archive_entry_linkresolver_new(); if (mtree->resolver == NULL) return ARCHIVE_FATAL; archive_entry_linkresolver_set_strategy(mtree->resolver, ARCHIVE_FORMAT_MTREE); r = read_mtree(a, mtree); if (r != ARCHIVE_OK) return (r); } a->archive.archive_format = mtree->archive_format; a->archive.archive_format_name = mtree->archive_format_name; for (;;) { if (mtree->this_entry == NULL) return (ARCHIVE_EOF); if (strcmp(mtree->this_entry->name, "..") == 0) { mtree->this_entry->used = 1; if (archive_strlen(&mtree->current_dir) > 0) { /* Roll back current path. */ p = mtree->current_dir.s + mtree->current_dir.length - 1; while (p >= mtree->current_dir.s && *p != '/') --p; if (p >= mtree->current_dir.s) --p; mtree->current_dir.length = p - mtree->current_dir.s + 1; } } if (!mtree->this_entry->used) { use_next = 0; r = parse_file(a, entry, mtree, mtree->this_entry, &use_next); if (use_next == 0) return (r); } mtree->this_entry = mtree->this_entry->next; } } /* * A single file can have multiple lines contribute specifications. * Parse as many lines as necessary, then pull additional information * from a backing file on disk as necessary. */ static int parse_file(struct archive_read *a, struct archive_entry *entry, struct mtree *mtree, struct mtree_entry *mentry, int *use_next) { const char *path; struct stat st_storage, *st; struct mtree_entry *mp; struct archive_entry *sparse_entry; int r = ARCHIVE_OK, r1, parsed_kws; mentry->used = 1; /* Initialize reasonable defaults. */ archive_entry_set_filetype(entry, AE_IFREG); archive_entry_set_size(entry, 0); archive_string_empty(&mtree->contents_name); /* Parse options from this line. */ parsed_kws = 0; r = parse_line(a, entry, mtree, mentry, &parsed_kws); if (mentry->full) { archive_entry_copy_pathname(entry, mentry->name); /* * "Full" entries are allowed to have multiple lines * and those lines aren't required to be adjacent. We * don't support multiple lines for "relative" entries * nor do we make any attempt to merge data from * separate "relative" and "full" entries. (Merging * "relative" and "full" entries would require dealing * with pathname canonicalization, which is a very * tricky subject.) */ for (mp = mentry->hashtable_next; mp != NULL; mp = mp->hashtable_next) { if (mp->full && !mp->used && mentry->name_hash == mp->name_hash && strcmp(mentry->name, mp->name) == 0) { /* Later lines override earlier ones. */ mp->used = 1; r1 = parse_line(a, entry, mtree, mp, &parsed_kws); if (r1 < r) r = r1; } } } else { /* * Relative entries require us to construct * the full path and possibly update the * current directory. */ size_t n = archive_strlen(&mtree->current_dir); if (n > 0) archive_strcat(&mtree->current_dir, "/"); archive_strcat(&mtree->current_dir, mentry->name); archive_entry_copy_pathname(entry, mtree->current_dir.s); if (archive_entry_filetype(entry) != AE_IFDIR) mtree->current_dir.length = n; } if (mtree->checkfs) { /* * Try to open and stat the file to get the real size * and other file info. It would be nice to avoid * this here so that getting a listing of an mtree * wouldn't require opening every referenced contents * file. But then we wouldn't know the actual * contents size, so I don't see a really viable way * around this. (Also, we may want to someday pull * other unspecified info from the contents file on * disk.) */ mtree->fd = -1; if (archive_strlen(&mtree->contents_name) > 0) path = mtree->contents_name.s; else path = archive_entry_pathname(entry); if (archive_entry_filetype(entry) == AE_IFREG || archive_entry_filetype(entry) == AE_IFDIR) { mtree->fd = open(path, O_RDONLY | O_BINARY | O_CLOEXEC); __archive_ensure_cloexec_flag(mtree->fd); if (mtree->fd == -1 && (errno != ENOENT || archive_strlen(&mtree->contents_name) > 0)) { archive_set_error(&a->archive, errno, "Can't open %s", path); r = ARCHIVE_WARN; } } st = &st_storage; if (mtree->fd >= 0) { if (fstat(mtree->fd, st) == -1) { archive_set_error(&a->archive, errno, "Could not fstat %s", path); r = ARCHIVE_WARN; /* If we can't stat it, don't keep it open. */ close(mtree->fd); mtree->fd = -1; st = NULL; } } else if (lstat(path, st) == -1) { st = NULL; } /* * Check for a mismatch between the type in the specification * and the type of the contents object on disk. */ if (st != NULL) { if (((st->st_mode & S_IFMT) == S_IFREG && archive_entry_filetype(entry) == AE_IFREG) #ifdef S_IFLNK ||((st->st_mode & S_IFMT) == S_IFLNK && archive_entry_filetype(entry) == AE_IFLNK) #endif #ifdef S_IFSOCK ||((st->st_mode & S_IFSOCK) == S_IFSOCK && archive_entry_filetype(entry) == AE_IFSOCK) #endif #ifdef S_IFCHR ||((st->st_mode & S_IFMT) == S_IFCHR && archive_entry_filetype(entry) == AE_IFCHR) #endif #ifdef S_IFBLK ||((st->st_mode & S_IFMT) == S_IFBLK && archive_entry_filetype(entry) == AE_IFBLK) #endif ||((st->st_mode & S_IFMT) == S_IFDIR && archive_entry_filetype(entry) == AE_IFDIR) #ifdef S_IFIFO ||((st->st_mode & S_IFMT) == S_IFIFO && archive_entry_filetype(entry) == AE_IFIFO) #endif ) { /* Types match. */ } else { /* Types don't match; bail out gracefully. */ if (mtree->fd >= 0) close(mtree->fd); mtree->fd = -1; if (parsed_kws & MTREE_HAS_OPTIONAL) { /* It's not an error for an optional * entry to not match disk. */ *use_next = 1; } else if (r == ARCHIVE_OK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "mtree specification has different" " type for %s", archive_entry_pathname(entry)); r = ARCHIVE_WARN; } return (r); } } /* * If there is a contents file on disk, pick some of the * metadata from that file. For most of these, we only * set it from the contents if it wasn't already parsed * from the specification. */ if (st != NULL) { if (((parsed_kws & MTREE_HAS_DEVICE) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) && (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK)) archive_entry_set_rdev(entry, st->st_rdev); if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME)) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_gid(entry, st->st_gid); if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_uid(entry, st->st_uid); if ((parsed_kws & MTREE_HAS_MTIME) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) { #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtimespec.tv_nsec); #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec); #elif HAVE_STRUCT_STAT_ST_MTIME_N archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_n); #elif HAVE_STRUCT_STAT_ST_UMTIME archive_entry_set_mtime(entry, st->st_mtime, st->st_umtime*1000); #elif HAVE_STRUCT_STAT_ST_MTIME_USEC archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_usec*1000); #else archive_entry_set_mtime(entry, st->st_mtime, 0); #endif } if ((parsed_kws & MTREE_HAS_NLINK) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_nlink(entry, st->st_nlink); if ((parsed_kws & MTREE_HAS_PERM) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_perm(entry, st->st_mode); if ((parsed_kws & MTREE_HAS_SIZE) == 0 || (parsed_kws & MTREE_HAS_NOCHANGE) != 0) archive_entry_set_size(entry, st->st_size); archive_entry_set_ino(entry, st->st_ino); archive_entry_set_dev(entry, st->st_dev); archive_entry_linkify(mtree->resolver, &entry, &sparse_entry); } else if (parsed_kws & MTREE_HAS_OPTIONAL) { /* * Couldn't open the entry, stat it or the on-disk type * didn't match. If this entry is optional, just * ignore it and read the next header entry. */ *use_next = 1; return ARCHIVE_OK; } } mtree->cur_size = archive_entry_size(entry); mtree->offset = 0; return r; } /* * Each line contains a sequence of keywords. */ static int parse_line(struct archive_read *a, struct archive_entry *entry, struct mtree *mtree, struct mtree_entry *mp, int *parsed_kws) { struct mtree_option *iter; int r = ARCHIVE_OK, r1; for (iter = mp->options; iter != NULL; iter = iter->next) { r1 = parse_keyword(a, mtree, entry, iter, parsed_kws); if (r1 < r) r = r1; } if (r == ARCHIVE_OK && (*parsed_kws & MTREE_HAS_TYPE) == 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Missing type keyword in mtree specification"); return (ARCHIVE_WARN); } return (r); } /* * Device entries have one of the following forms: * - raw dev_t * - format,major,minor[,subdevice] * When parsing succeeded, `pdev' will contain the appropriate dev_t value. */ /* strsep() is not in C90, but strcspn() is. */ /* Taken from http://unixpapa.com/incnote/string.html */ static char * la_strsep(char **sp, const char *sep) { char *p, *s; if (sp == NULL || *sp == NULL || **sp == '\0') return(NULL); s = *sp; p = s + strcspn(s, sep); if (*p != '\0') *p++ = '\0'; *sp = p; return(s); } static int parse_device(dev_t *pdev, struct archive *a, char *val) { #define MAX_PACK_ARGS 3 unsigned long numbers[MAX_PACK_ARGS]; char *p, *dev; int argc; pack_t *pack; dev_t result; const char *error = NULL; memset(pdev, 0, sizeof(*pdev)); if ((dev = strchr(val, ',')) != NULL) { /* * Device's major/minor are given in a specified format. * Decode and pack it accordingly. */ *dev++ = '\0'; if ((pack = pack_find(val)) == NULL) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Unknown format `%s'", val); return ARCHIVE_WARN; } argc = 0; while ((p = la_strsep(&dev, ",")) != NULL) { if (*p == '\0') { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Missing number"); return ARCHIVE_WARN; } if (argc >= MAX_PACK_ARGS) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Too many arguments"); return ARCHIVE_WARN; } numbers[argc++] = (unsigned long)mtree_atol(&p); } if (argc < 2) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "Not enough arguments"); return ARCHIVE_WARN; } result = (*pack)(argc, numbers, &error); if (error != NULL) { archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT, "%s", error); return ARCHIVE_WARN; } } else { /* file system raw value. */ result = (dev_t)mtree_atol(&val); } *pdev = result; return ARCHIVE_OK; #undef MAX_PACK_ARGS } /* * Parse a single keyword and its value. */ static int parse_keyword(struct archive_read *a, struct mtree *mtree, struct archive_entry *entry, struct mtree_option *opt, int *parsed_kws) { char *val, *key; key = opt->value; if (*key == '\0') return (ARCHIVE_OK); if (strcmp(key, "nochange") == 0) { *parsed_kws |= MTREE_HAS_NOCHANGE; return (ARCHIVE_OK); } if (strcmp(key, "optional") == 0) { *parsed_kws |= MTREE_HAS_OPTIONAL; return (ARCHIVE_OK); } if (strcmp(key, "ignore") == 0) { /* * The mtree processing is not recursive, so * recursion will only happen for explicitly listed * entries. */ return (ARCHIVE_OK); } val = strchr(key, '='); if (val == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Malformed attribute \"%s\" (%d)", key, key[0]); return (ARCHIVE_WARN); } *val = '\0'; ++val; switch (key[0]) { case 'c': if (strcmp(key, "content") == 0 || strcmp(key, "contents") == 0) { parse_escapes(val, NULL); archive_strcpy(&mtree->contents_name, val); break; } if (strcmp(key, "cksum") == 0) break; case 'd': if (strcmp(key, "device") == 0) { /* stat(2) st_rdev field, e.g. the major/minor IDs * of a char/block special file */ int r; dev_t dev; *parsed_kws |= MTREE_HAS_DEVICE; r = parse_device(&dev, &a->archive, val); if (r == ARCHIVE_OK) archive_entry_set_rdev(entry, dev); return r; } case 'f': if (strcmp(key, "flags") == 0) { *parsed_kws |= MTREE_HAS_FFLAGS; archive_entry_copy_fflags_text(entry, val); break; } case 'g': if (strcmp(key, "gid") == 0) { *parsed_kws |= MTREE_HAS_GID; archive_entry_set_gid(entry, mtree_atol10(&val)); break; } if (strcmp(key, "gname") == 0) { *parsed_kws |= MTREE_HAS_GNAME; archive_entry_copy_gname(entry, val); break; } case 'i': if (strcmp(key, "inode") == 0) { archive_entry_set_ino(entry, mtree_atol10(&val)); break; } case 'l': if (strcmp(key, "link") == 0) { archive_entry_copy_symlink(entry, val); break; } case 'm': if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0) break; if (strcmp(key, "mode") == 0) { if (val[0] >= '0' && val[0] <= '9') { *parsed_kws |= MTREE_HAS_PERM; archive_entry_set_perm(entry, (mode_t)mtree_atol8(&val)); } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Symbolic mode \"%s\" unsupported", val); return ARCHIVE_WARN; } break; } case 'n': if (strcmp(key, "nlink") == 0) { *parsed_kws |= MTREE_HAS_NLINK; archive_entry_set_nlink(entry, (unsigned int)mtree_atol10(&val)); break; } case 'r': if (strcmp(key, "resdevice") == 0) { /* stat(2) st_dev field, e.g. the device ID where the * inode resides */ int r; dev_t dev; r = parse_device(&dev, &a->archive, val); if (r == ARCHIVE_OK) archive_entry_set_dev(entry, dev); return r; } if (strcmp(key, "rmd160") == 0 || strcmp(key, "rmd160digest") == 0) break; case 's': if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0) break; if (strcmp(key, "sha256") == 0 || strcmp(key, "sha256digest") == 0) break; if (strcmp(key, "sha384") == 0 || strcmp(key, "sha384digest") == 0) break; if (strcmp(key, "sha512") == 0 || strcmp(key, "sha512digest") == 0) break; if (strcmp(key, "size") == 0) { archive_entry_set_size(entry, mtree_atol10(&val)); break; } case 't': if (strcmp(key, "tags") == 0) { /* * Comma delimited list of tags. * Ignore the tags for now, but the interface * should be extended to allow inclusion/exclusion. */ break; } if (strcmp(key, "time") == 0) { int64_t m; int64_t my_time_t_max = get_time_t_max(); int64_t my_time_t_min = get_time_t_min(); long ns = 0; *parsed_kws |= MTREE_HAS_MTIME; m = mtree_atol10(&val); /* Replicate an old mtree bug: * 123456789.1 represents 123456789 * seconds and 1 nanosecond. */ if (*val == '.') { ++val; ns = (long)mtree_atol10(&val); if (ns < 0) ns = 0; else if (ns > 999999999) ns = 999999999; } if (m > my_time_t_max) m = my_time_t_max; else if (m < my_time_t_min) m = my_time_t_min; archive_entry_set_mtime(entry, (time_t)m, ns); break; } if (strcmp(key, "type") == 0) { switch (val[0]) { case 'b': if (strcmp(val, "block") == 0) { archive_entry_set_filetype(entry, AE_IFBLK); break; } case 'c': if (strcmp(val, "char") == 0) { archive_entry_set_filetype(entry, AE_IFCHR); break; } case 'd': if (strcmp(val, "dir") == 0) { archive_entry_set_filetype(entry, AE_IFDIR); break; } case 'f': if (strcmp(val, "fifo") == 0) { archive_entry_set_filetype(entry, AE_IFIFO); break; } if (strcmp(val, "file") == 0) { archive_entry_set_filetype(entry, AE_IFREG); break; } case 'l': if (strcmp(val, "link") == 0) { archive_entry_set_filetype(entry, AE_IFLNK); break; } default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized file type \"%s\"; " "assuming \"file\"", val); archive_entry_set_filetype(entry, AE_IFREG); return (ARCHIVE_WARN); } *parsed_kws |= MTREE_HAS_TYPE; break; } case 'u': if (strcmp(key, "uid") == 0) { *parsed_kws |= MTREE_HAS_UID; archive_entry_set_uid(entry, mtree_atol10(&val)); break; } if (strcmp(key, "uname") == 0) { *parsed_kws |= MTREE_HAS_UNAME; archive_entry_copy_uname(entry, val); break; } default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Unrecognized key %s=%s", key, val); return (ARCHIVE_WARN); } return (ARCHIVE_OK); } static int read_data(struct archive_read *a, const void **buff, size_t *size, int64_t *offset) { size_t bytes_to_read; ssize_t bytes_read; struct mtree *mtree; mtree = (struct mtree *)(a->format->data); if (mtree->fd < 0) { *buff = NULL; *offset = 0; *size = 0; return (ARCHIVE_EOF); } if (mtree->buff == NULL) { mtree->buffsize = 64 * 1024; mtree->buff = malloc(mtree->buffsize); if (mtree->buff == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } } *buff = mtree->buff; *offset = mtree->offset; if ((int64_t)mtree->buffsize > mtree->cur_size - mtree->offset) bytes_to_read = (size_t)(mtree->cur_size - mtree->offset); else bytes_to_read = mtree->buffsize; bytes_read = read(mtree->fd, mtree->buff, bytes_to_read); if (bytes_read < 0) { archive_set_error(&a->archive, errno, "Can't read"); return (ARCHIVE_WARN); } if (bytes_read == 0) { *size = 0; return (ARCHIVE_EOF); } mtree->offset += bytes_read; *size = bytes_read; return (ARCHIVE_OK); } /* Skip does nothing except possibly close the contents file. */ static int skip(struct archive_read *a) { struct mtree *mtree; mtree = (struct mtree *)(a->format->data); if (mtree->fd >= 0) { close(mtree->fd); mtree->fd = -1; } return (ARCHIVE_OK); } /* * Since parsing backslash sequences always makes strings shorter, * we can always do this conversion in-place. */ static void parse_escapes(char *src, struct mtree_entry *mentry) { char *dest = src; char c; if (mentry != NULL && strcmp(src, ".") == 0) mentry->full = 1; while (*src != '\0') { c = *src++; if (c == '/' && mentry != NULL) mentry->full = 1; if (c == '\\') { switch (src[0]) { case '0': if (src[1] < '0' || src[1] > '7') { c = 0; ++src; break; } /* FALLTHROUGH */ case '1': case '2': case '3': if (src[1] >= '0' && src[1] <= '7' && src[2] >= '0' && src[2] <= '7') { c = (src[0] - '0') << 6; c |= (src[1] - '0') << 3; c |= (src[2] - '0'); src += 3; } break; case 'a': c = '\a'; ++src; break; case 'b': c = '\b'; ++src; break; case 'f': c = '\f'; ++src; break; case 'n': c = '\n'; ++src; break; case 'r': c = '\r'; ++src; break; case 's': c = ' '; ++src; break; case 't': c = '\t'; ++src; break; case 'v': c = '\v'; ++src; break; case '\\': c = '\\'; ++src; break; } } *dest++ = c; } *dest = '\0'; } /* * 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 mtree_atol8(char **p) { int64_t l, limit, last_digit_limit; int digit, base; base = 8; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; l = 0; digit = **p - '0'; while (digit >= 0 && digit < base) { if (l>limit || (l == limit && digit > last_digit_limit)) { l = INT64_MAX; /* Truncate on overflow. */ break; } l = (l * base) + digit; digit = *++(*p) - '0'; } return (l); } /* * Note that this implementation does not (and should not!) obey * locale settings; you cannot simply substitute strtol here, since * it does obey locale. * * Convert the number pointed to by 'p' into a 64-bit signed integer. * On return, 'p' points to the first non-digit following the number. * On overflow, the function returns INT64_MIN or INT64_MAX. */ static int64_t mtree_atol10(char **p) { const int base = 10; const int64_t limit = INT64_MAX / base; const int64_t last_digit_limit = INT64_MAX % base; int64_t l; int sign; if (**p == '-') { sign = -1; ++(*p); } else { sign = 1; } l = 0; while (**p >= '0' && **p < '0' + base) { int digit = **p - '0'; if (l > limit || (l == limit && digit > last_digit_limit)) { while (**p >= '0' && **p < '0' + base) { ++(*p); } return (sign < 0) ? INT64_MIN : INT64_MAX; } l = (l * base) + digit; ++(*p); } return (sign < 0) ? -l : l; } /* Parse a hex digit. */ static int parsehex(char c) { if (c >= '0' && c <= '9') return c - '0'; else if (c >= 'a' && c <= 'f') return c - 'a'; else if (c >= 'A' && c <= 'F') return c - 'A'; else return -1; } /* * 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 mtree_atol16(char **p) { int64_t l, limit, last_digit_limit; int base, digit, sign; base = 16; if (**p == '-') { sign = -1; limit = ((uint64_t)(INT64_MAX) + 1) / base; last_digit_limit = ((uint64_t)(INT64_MAX) + 1) % base; ++(*p); } else { sign = 1; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; } l = 0; digit = parsehex(**p); while (digit >= 0 && digit < base) { if (l > limit || (l == limit && digit > last_digit_limit)) return (sign < 0) ? INT64_MIN : INT64_MAX; l = (l * base) + digit; digit = parsehex(*++(*p)); } return (sign < 0) ? -l : l; } static int64_t mtree_atol(char **p) { if (**p != '0') return mtree_atol10(p); if ((*p)[1] == 'x' || (*p)[1] == 'X') { *p += 2; return mtree_atol16(p); } return mtree_atol8(p); } /* * Returns length of line (including trailing newline) * or negative on error. 'start' argument is updated to * point to first character of line. */ static ssize_t readline(struct archive_read *a, struct mtree *mtree, char **start, ssize_t limit) { ssize_t bytes_read; ssize_t total_size = 0; ssize_t find_off = 0; const void *t; void *nl; char *u; /* Accumulate line in a line buffer. */ for (;;) { /* Read some more. */ t = __archive_read_ahead(a, 1, &bytes_read); if (t == NULL) return (0); if (bytes_read < 0) return (ARCHIVE_FATAL); nl = memchr(t, '\n', bytes_read); /* If we found '\n', trim the read to end exactly there. */ if (nl != NULL) { bytes_read = ((const char *)nl) - ((const char *)t) + 1; } if (total_size + bytes_read + 1 > limit) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Line too long"); return (ARCHIVE_FATAL); } if (archive_string_ensure(&mtree->line, total_size + bytes_read + 1) == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate working buffer"); return (ARCHIVE_FATAL); } /* Append new bytes to string. */ memcpy(mtree->line.s + total_size, t, bytes_read); __archive_read_consume(a, bytes_read); total_size += bytes_read; mtree->line.s[total_size] = '\0'; for (u = mtree->line.s + find_off; *u; ++u) { if (u[0] == '\n') { /* Ends with unescaped newline. */ *start = mtree->line.s; return total_size; } else if (u[0] == '#') { /* Ends with comment sequence #...\n */ if (nl == NULL) { /* But we've not found the \n yet */ break; } } else if (u[0] == '\\') { if (u[1] == '\n') { /* Trim escaped newline. */ total_size -= 2; mtree->line.s[total_size] = '\0'; break; } else if (u[1] != '\0') { /* Skip the two-char escape sequence */ ++u; } } } find_off = u - mtree->line.s; } } 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: head/contrib/libarchive/libarchive/archive_read_support_format_zip.c =================================================================== --- head/contrib/libarchive/libarchive/archive_read_support_format_zip.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_read_support_format_zip.c (revision 316095) @@ -1,3113 +1,3113 @@ /*- * 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 #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" #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; z_stream stream; char stream_valid; #endif 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)) /* ------------------------------------------------------------------------ */ /* 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 struct { +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) */ {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 = 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 (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; } /* 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_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); } #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; 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_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)); /* 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 (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)); 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); } Index: head/contrib/libarchive/libarchive/archive_string_sprintf.c =================================================================== --- head/contrib/libarchive/libarchive/archive_string_sprintf.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_string_sprintf.c (revision 316095) @@ -1,192 +1,192 @@ /*- * 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$"); /* * The use of printf()-family functions can be troublesome * for space-constrained applications. In addition, correctly * implementing this function in terms of vsnprintf() requires * two calls (one to determine the size, another to format the * result), which in turn requires duplicating the argument list * using va_copy, which isn't yet universally available. * * So, I've implemented a bare minimum of printf()-like capability * here. This is only used to format error messages, so doesn't * require any floating-point support or field-width handling. */ #ifdef HAVE_ERRNO_H #include #endif #include #include "archive_string.h" #include "archive_private.h" /* * Utility functions to format signed/unsigned integers and append * them to an archive_string. */ static void append_uint(struct archive_string *as, uintmax_t d, unsigned base) { - static const char *digits = "0123456789abcdef"; + static const char digits[] = "0123456789abcdef"; if (d >= base) append_uint(as, d/base, base); archive_strappend_char(as, digits[d % base]); } static void append_int(struct archive_string *as, intmax_t d, unsigned base) { uintmax_t ud; if (d < 0) { archive_strappend_char(as, '-'); ud = (d == INTMAX_MIN) ? (uintmax_t)(INTMAX_MAX) + 1 : (uintmax_t)(-d); } else ud = d; append_uint(as, ud, base); } void archive_string_sprintf(struct archive_string *as, const char *fmt, ...) { va_list ap; va_start(ap, fmt); archive_string_vsprintf(as, fmt, ap); va_end(ap); } /* * Like 'vsprintf', but ensures the target is big enough, resizing if * necessary. */ void archive_string_vsprintf(struct archive_string *as, const char *fmt, va_list ap) { char long_flag; intmax_t s; /* Signed integer temp. */ uintmax_t u; /* Unsigned integer temp. */ const char *p, *p2; const wchar_t *pw; if (archive_string_ensure(as, 64) == NULL) __archive_errx(1, "Out of memory"); if (fmt == NULL) { as->s[0] = 0; return; } for (p = fmt; *p != '\0'; p++) { const char *saved_p = p; if (*p != '%') { archive_strappend_char(as, *p); continue; } p++; long_flag = '\0'; switch(*p) { case 'j': case 'l': case 'z': long_flag = *p; p++; break; } switch (*p) { case '%': archive_strappend_char(as, '%'); break; case 'c': s = va_arg(ap, int); archive_strappend_char(as, (char)s); break; case 'd': switch(long_flag) { case 'j': s = va_arg(ap, intmax_t); break; case 'l': s = va_arg(ap, long); break; case 'z': s = va_arg(ap, ssize_t); break; default: s = va_arg(ap, int); break; } append_int(as, s, 10); break; case 's': switch(long_flag) { case 'l': pw = va_arg(ap, wchar_t *); if (pw == NULL) pw = L"(null)"; if (archive_string_append_from_wcs(as, pw, wcslen(pw)) != 0 && errno == ENOMEM) __archive_errx(1, "Out of memory"); break; default: p2 = va_arg(ap, char *); if (p2 == NULL) p2 = "(null)"; archive_strcat(as, p2); break; } break; case 'S': pw = va_arg(ap, wchar_t *); if (pw == NULL) pw = L"(null)"; if (archive_string_append_from_wcs(as, pw, wcslen(pw)) != 0 && errno == ENOMEM) __archive_errx(1, "Out of memory"); break; case 'o': case 'u': case 'x': case 'X': /* Common handling for unsigned integer formats. */ switch(long_flag) { case 'j': u = va_arg(ap, uintmax_t); break; case 'l': u = va_arg(ap, unsigned long); break; case 'z': u = va_arg(ap, size_t); break; default: u = va_arg(ap, unsigned int); break; } /* Format it in the correct base. */ switch (*p) { case 'o': append_uint(as, u, 8); break; case 'u': append_uint(as, u, 10); break; default: append_uint(as, u, 16); break; } break; default: /* Rewind and print the initial '%' literally. */ p = saved_p; archive_strappend_char(as, *p); } } } Index: head/contrib/libarchive/libarchive/archive_util.c =================================================================== --- head/contrib/libarchive/libarchive/archive_util.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_util.c (revision 316095) @@ -1,585 +1,585 @@ /*- * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA * 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_FCNTL_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__) #include #endif #ifdef HAVE_ZLIB_H #include #endif #ifdef HAVE_LZMA_H #include #endif #ifdef HAVE_BZLIB_H #include #endif #ifdef HAVE_LZ4_H #include #endif #include "archive.h" #include "archive_private.h" #include "archive_random_private.h" #include "archive_string.h" #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif static int archive_utility_string_sort_helper(char **, unsigned int); /* Generic initialization of 'struct archive' objects. */ int __archive_clean(struct archive *a) { archive_string_conversion_free(a); return (ARCHIVE_OK); } int archive_version_number(void) { return (ARCHIVE_VERSION_NUMBER); } const char * archive_version_string(void) { return (ARCHIVE_VERSION_STRING); } int archive_errno(struct archive *a) { return (a->archive_error_number); } const char * archive_error_string(struct archive *a) { if (a->error != NULL && *a->error != '\0') return (a->error); else return (NULL); } int archive_file_count(struct archive *a) { return (a->file_count); } int archive_format(struct archive *a) { return (a->archive_format); } const char * archive_format_name(struct archive *a) { return (a->archive_format_name); } int archive_compression(struct archive *a) { return archive_filter_code(a, 0); } const char * archive_compression_name(struct archive *a) { return archive_filter_name(a, 0); } /* * Return a count of the number of compressed bytes processed. */ int64_t archive_position_compressed(struct archive *a) { return archive_filter_bytes(a, -1); } /* * Return a count of the number of uncompressed bytes processed. */ int64_t archive_position_uncompressed(struct archive *a) { return archive_filter_bytes(a, 0); } void archive_clear_error(struct archive *a) { archive_string_empty(&a->error_string); a->error = NULL; a->archive_error_number = 0; } void archive_set_error(struct archive *a, int error_number, const char *fmt, ...) { va_list ap; a->archive_error_number = error_number; if (fmt == NULL) { a->error = NULL; return; } archive_string_empty(&(a->error_string)); va_start(ap, fmt); archive_string_vsprintf(&(a->error_string), fmt, ap); va_end(ap); a->error = a->error_string.s; } void archive_copy_error(struct archive *dest, struct archive *src) { dest->archive_error_number = src->archive_error_number; archive_string_copy(&dest->error_string, &src->error_string); dest->error = dest->error_string.s; } void __archive_errx(int retvalue, const char *msg) { - static const char *msg1 = "Fatal Internal Error in libarchive: "; + static const char msg1[] = "Fatal Internal Error in libarchive: "; size_t s; s = write(2, msg1, strlen(msg1)); (void)s; /* UNUSED */ s = write(2, msg, strlen(msg)); (void)s; /* UNUSED */ s = write(2, "\n", 1); (void)s; /* UNUSED */ exit(retvalue); } /* * Create a temporary file */ #if defined(_WIN32) && !defined(__CYGWIN__) /* * Do not use Windows tmpfile() function. * It will make a temporary file under the root directory * and it'll cause permission error if a user who is * non-Administrator creates temporary files. * Also Windows version of mktemp family including _mktemp_s * are not secure. */ int __archive_mktemp(const char *tmpdir) { - static const wchar_t *prefix = L"libarchive_"; - static const wchar_t *suffix = L"XXXXXXXXXX"; + static const wchar_t prefix[] = L"libarchive_"; + static const wchar_t suffix[] = L"XXXXXXXXXX"; static const wchar_t num[] = { L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7', L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F', L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N', L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V', L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd', L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l', L'm', L'n', L'o', L'p', L'q', L'r', L's', L't', L'u', L'v', L'w', L'x', L'y', L'z' }; HCRYPTPROV hProv; struct archive_wstring temp_name; wchar_t *ws; DWORD attr; wchar_t *xp, *ep; int fd; hProv = (HCRYPTPROV)NULL; fd = -1; ws = NULL; archive_string_init(&temp_name); /* Get a temporary directory. */ if (tmpdir == NULL) { size_t l; wchar_t *tmp; l = GetTempPathW(0, NULL); if (l == 0) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } tmp = malloc(l*sizeof(wchar_t)); if (tmp == NULL) { errno = ENOMEM; goto exit_tmpfile; } GetTempPathW((DWORD)l, tmp); archive_wstrcpy(&temp_name, tmp); free(tmp); } else { if (archive_wstring_append_from_mbs(&temp_name, tmpdir, strlen(tmpdir)) < 0) goto exit_tmpfile; if (temp_name.s[temp_name.length-1] != L'/') archive_wstrappend_wchar(&temp_name, L'/'); } /* Check if temp_name is a directory. */ attr = GetFileAttributesW(temp_name.s); if (attr == (DWORD)-1) { if (GetLastError() != ERROR_FILE_NOT_FOUND) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } ws = __la_win_permissive_name_w(temp_name.s); if (ws == NULL) { errno = EINVAL; goto exit_tmpfile; } attr = GetFileAttributesW(ws); if (attr == (DWORD)-1) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } } if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) { errno = ENOTDIR; goto exit_tmpfile; } /* * Create a temporary file. */ archive_wstrcat(&temp_name, prefix); archive_wstrcat(&temp_name, suffix); ep = temp_name.s + archive_strlen(&temp_name); xp = ep - wcslen(suffix); if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } for (;;) { wchar_t *p; HANDLE h; /* Generate a random file name through CryptGenRandom(). */ p = xp; if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t), (BYTE*)p)) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } for (; p < ep; p++) *p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))]; free(ws); ws = __la_win_permissive_name_w(temp_name.s); if (ws == NULL) { errno = EINVAL; goto exit_tmpfile; } /* Specifies FILE_FLAG_DELETE_ON_CLOSE flag is to * delete this temporary file immediately when this * file closed. */ h = CreateFileW(ws, GENERIC_READ | GENERIC_WRITE | DELETE, 0,/* Not share */ NULL, CREATE_NEW,/* Create a new file only */ FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (h == INVALID_HANDLE_VALUE) { /* The same file already exists. retry with * a new filename. */ if (GetLastError() == ERROR_FILE_EXISTS) continue; /* Otherwise, fail creation temporary file. */ la_dosmaperr(GetLastError()); goto exit_tmpfile; } fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR); if (fd == -1) { CloseHandle(h); goto exit_tmpfile; } else break;/* success! */ } exit_tmpfile: if (hProv != (HCRYPTPROV)NULL) CryptReleaseContext(hProv, 0); free(ws); archive_wstring_free(&temp_name); return (fd); } #else static int get_tempdir(struct archive_string *temppath) { const char *tmp; tmp = getenv("TMPDIR"); if (tmp == NULL) #ifdef _PATH_TMP tmp = _PATH_TMP; #else tmp = "/tmp"; #endif archive_strcpy(temppath, tmp); if (temppath->s[temppath->length-1] != '/') archive_strappend_char(temppath, '/'); return (ARCHIVE_OK); } #if defined(HAVE_MKSTEMP) /* * We can use mkstemp(). */ int __archive_mktemp(const char *tmpdir) { struct archive_string temp_name; int fd = -1; archive_string_init(&temp_name); if (tmpdir == NULL) { if (get_tempdir(&temp_name) != ARCHIVE_OK) goto exit_tmpfile; } else { archive_strcpy(&temp_name, tmpdir); if (temp_name.s[temp_name.length-1] != '/') archive_strappend_char(&temp_name, '/'); } archive_strcat(&temp_name, "libarchive_XXXXXX"); fd = mkstemp(temp_name.s); if (fd < 0) goto exit_tmpfile; __archive_ensure_cloexec_flag(fd); unlink(temp_name.s); exit_tmpfile: archive_string_free(&temp_name); return (fd); } #else /* * We use a private routine. */ int __archive_mktemp(const char *tmpdir) { static const char num[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; struct archive_string temp_name; struct stat st; int fd; char *tp, *ep; fd = -1; archive_string_init(&temp_name); if (tmpdir == NULL) { if (get_tempdir(&temp_name) != ARCHIVE_OK) goto exit_tmpfile; } else archive_strcpy(&temp_name, tmpdir); if (temp_name.s[temp_name.length-1] == '/') { temp_name.s[temp_name.length-1] = '\0'; temp_name.length --; } if (stat(temp_name.s, &st) < 0) goto exit_tmpfile; if (!S_ISDIR(st.st_mode)) { errno = ENOTDIR; goto exit_tmpfile; } archive_strcat(&temp_name, "/libarchive_"); tp = temp_name.s + archive_strlen(&temp_name); archive_strcat(&temp_name, "XXXXXXXXXX"); ep = temp_name.s + archive_strlen(&temp_name); do { char *p; p = tp; archive_random(p, ep - p); while (p < ep) { int d = *((unsigned char *)p) % sizeof(num); *p++ = num[d]; } fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600); } while (fd < 0 && errno == EEXIST); if (fd < 0) goto exit_tmpfile; __archive_ensure_cloexec_flag(fd); unlink(temp_name.s); exit_tmpfile: archive_string_free(&temp_name); return (fd); } #endif /* HAVE_MKSTEMP */ #endif /* !_WIN32 || __CYGWIN__ */ /* * Set FD_CLOEXEC flag to a file descriptor if it is not set. * We have to set the flag if the platform does not provide O_CLOEXEC * or F_DUPFD_CLOEXEC flags. * * Note: This function is absolutely called after creating a new file * descriptor even if the platform seemingly provides O_CLOEXEC or * F_DUPFD_CLOEXEC macros because it is possible that the platform * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it. */ void __archive_ensure_cloexec_flag(int fd) { #if defined(_WIN32) && !defined(__CYGWIN__) (void)fd; /* UNUSED */ #else int flags; if (fd >= 0) { flags = fcntl(fd, F_GETFD); if (flags != -1 && (flags & FD_CLOEXEC) == 0) fcntl(fd, F_SETFD, flags | FD_CLOEXEC); } #endif } /* * Utility function to sort a group of strings using quicksort. */ static int archive_utility_string_sort_helper(char **strings, unsigned int n) { unsigned int i, lesser_count, greater_count; char **lesser, **greater, **tmp, *pivot; int retval1, retval2; /* A list of 0 or 1 elements is already sorted */ if (n <= 1) return (ARCHIVE_OK); lesser_count = greater_count = 0; lesser = greater = NULL; pivot = strings[0]; for (i = 1; i < n; i++) { if (strcmp(strings[i], pivot) < 0) { lesser_count++; tmp = (char **)realloc(lesser, lesser_count * sizeof(char *)); if (!tmp) { free(greater); free(lesser); return (ARCHIVE_FATAL); } lesser = tmp; lesser[lesser_count - 1] = strings[i]; } else { greater_count++; tmp = (char **)realloc(greater, greater_count * sizeof(char *)); if (!tmp) { free(greater); free(lesser); return (ARCHIVE_FATAL); } greater = tmp; greater[greater_count - 1] = strings[i]; } } /* quicksort(lesser) */ retval1 = archive_utility_string_sort_helper(lesser, lesser_count); for (i = 0; i < lesser_count; i++) strings[i] = lesser[i]; free(lesser); /* pivot */ strings[lesser_count] = pivot; /* quicksort(greater) */ retval2 = archive_utility_string_sort_helper(greater, greater_count); for (i = 0; i < greater_count; i++) strings[lesser_count + 1 + i] = greater[i]; free(greater); return (retval1 < retval2) ? retval1 : retval2; } int archive_utility_string_sort(char **strings) { unsigned int size = 0; while (strings[size] != NULL) size++; return archive_utility_string_sort_helper(strings, size); } Index: head/contrib/libarchive/libarchive/archive_write_add_filter.c =================================================================== --- head/contrib/libarchive/libarchive/archive_write_add_filter.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_write_add_filter.c (revision 316095) @@ -1,71 +1,71 @@ /*- * Copyright (c) 2012 Ondrej Holy * 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 #include "archive.h" #include "archive_private.h" /* A table that maps filter codes to functions. */ -static +static const struct { int code; int (*setter)(struct archive *); } codes[] = { { ARCHIVE_FILTER_NONE, archive_write_add_filter_none }, { ARCHIVE_FILTER_GZIP, archive_write_add_filter_gzip }, { ARCHIVE_FILTER_BZIP2, archive_write_add_filter_bzip2 }, { ARCHIVE_FILTER_COMPRESS, archive_write_add_filter_compress }, { ARCHIVE_FILTER_GRZIP, archive_write_add_filter_grzip }, { ARCHIVE_FILTER_LRZIP, archive_write_add_filter_lrzip }, { ARCHIVE_FILTER_LZ4, archive_write_add_filter_lz4 }, { ARCHIVE_FILTER_LZIP, archive_write_add_filter_lzip }, { ARCHIVE_FILTER_LZMA, archive_write_add_filter_lzma }, { ARCHIVE_FILTER_LZOP, archive_write_add_filter_lzip }, { ARCHIVE_FILTER_UU, archive_write_add_filter_uuencode }, { ARCHIVE_FILTER_XZ, archive_write_add_filter_xz }, { -1, NULL } }; int archive_write_add_filter(struct archive *a, int code) { int i; for (i = 0; codes[i].code != -1; i++) { if (code == codes[i].code) return ((codes[i].setter)(a)); } archive_set_error(a, EINVAL, "No such filter"); return (ARCHIVE_FATAL); } Index: head/contrib/libarchive/libarchive/archive_write_add_filter_by_name.c =================================================================== --- head/contrib/libarchive/libarchive/archive_write_add_filter_by_name.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_write_add_filter_by_name.c (revision 316095) @@ -1,76 +1,76 @@ /*- * Copyright (c) 2003-2007 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. * 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_STRING_H #include #endif #include "archive.h" #include "archive_private.h" /* A table that maps names to functions. */ -static +static const struct { const char *name; int (*setter)(struct archive *); } names[] = { { "b64encode", archive_write_add_filter_b64encode }, { "bzip2", archive_write_add_filter_bzip2 }, { "compress", archive_write_add_filter_compress }, { "grzip", archive_write_add_filter_grzip }, { "gzip", archive_write_add_filter_gzip }, { "lrzip", archive_write_add_filter_lrzip }, { "lz4", archive_write_add_filter_lz4 }, { "lzip", archive_write_add_filter_lzip }, { "lzma", archive_write_add_filter_lzma }, { "lzop", archive_write_add_filter_lzop }, { "uuencode", archive_write_add_filter_uuencode }, { "xz", archive_write_add_filter_xz }, { NULL, NULL } }; int archive_write_add_filter_by_name(struct archive *a, const char *name) { int i; for (i = 0; names[i].name != NULL; i++) { if (strcmp(name, names[i].name) == 0) return ((names[i].setter)(a)); } archive_set_error(a, EINVAL, "No such filter '%s'", name); a->state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } Index: head/contrib/libarchive/libarchive/archive_write_add_filter_lz4.c =================================================================== --- head/contrib/libarchive/libarchive/archive_write_add_filter_lz4.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_write_add_filter_lz4.c (revision 316095) @@ -1,707 +1,707 @@ /*- * Copyright (c) 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. */ #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 #ifdef HAVE_LZ4_H #include #endif #ifdef HAVE_LZ4HC_H #include #endif #include "archive.h" #include "archive_endian.h" #include "archive_private.h" #include "archive_write_private.h" #include "archive_xxhash.h" #define LZ4_MAGICNUMBER 0x184d2204 struct private_data { int compression_level; unsigned header_written:1; unsigned version_number:1; unsigned block_independence:1; unsigned block_checksum:1; unsigned stream_size:1; unsigned stream_checksum:1; unsigned preset_dictionary:1; unsigned block_maximum_size:3; #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 int64_t total_in; char *out; char *out_buffer; size_t out_buffer_size; size_t out_block_size; char *in; char *in_buffer_allocated; char *in_buffer; size_t in_buffer_size; size_t block_size; void *xxh32_state; void *lz4_stream; #else struct archive_write_program_data *pdata; #endif }; static int archive_filter_lz4_close(struct archive_write_filter *); static int archive_filter_lz4_free(struct archive_write_filter *); static int archive_filter_lz4_open(struct archive_write_filter *); static int archive_filter_lz4_options(struct archive_write_filter *, const char *, const char *); static int archive_filter_lz4_write(struct archive_write_filter *, const void *, size_t); /* * Add a lz4 compression filter to this write handle. */ int archive_write_add_filter_lz4(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct archive_write_filter *f = __archive_write_allocate_filter(_a); struct private_data *data; archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_lz4"); data = calloc(1, sizeof(*data)); if (data == NULL) { archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } /* * Setup default settings. */ data->compression_level = 1; data->version_number = 0x01; data->block_independence = 1; data->block_checksum = 0; data->stream_size = 0; data->stream_checksum = 1; data->preset_dictionary = 0; data->block_maximum_size = 7; /* * Setup a filter setting. */ f->data = data; f->options = &archive_filter_lz4_options; f->close = &archive_filter_lz4_close; f->free = &archive_filter_lz4_free; f->open = &archive_filter_lz4_open; f->code = ARCHIVE_FILTER_LZ4; f->name = "lz4"; #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 return (ARCHIVE_OK); #else /* * We don't have lz4 library, and execute external lz4 program * instead. */ data->pdata = __archive_write_program_allocate("lz4"); if (data->pdata == NULL) { free(data); archive_set_error(&a->archive, ENOMEM, "Out of memory"); return (ARCHIVE_FATAL); } data->compression_level = 0; archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Using external lz4 program"); return (ARCHIVE_WARN); #endif } /* * Set write options. */ static int archive_filter_lz4_options(struct archive_write_filter *f, const char *key, const char *value) { struct private_data *data = (struct private_data *)f->data; if (strcmp(key, "compression-level") == 0) { int val; if (value == NULL || !((val = value[0] - '0') >= 1 && val <= 9) || value[1] != '\0') return (ARCHIVE_WARN); #ifndef HAVE_LZ4HC_H if(val >= 3) { archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER, "High compression not included in this build"); return (ARCHIVE_FATAL); } #endif data->compression_level = val; return (ARCHIVE_OK); } if (strcmp(key, "stream-checksum") == 0) { data->stream_checksum = value != NULL; return (ARCHIVE_OK); } if (strcmp(key, "block-checksum") == 0) { data->block_checksum = value != NULL; return (ARCHIVE_OK); } if (strcmp(key, "block-size") == 0) { if (value == NULL || !(value[0] >= '4' && value[0] <= '7') || value[1] != '\0') return (ARCHIVE_WARN); data->block_maximum_size = value[0] - '0'; return (ARCHIVE_OK); } if (strcmp(key, "block-dependence") == 0) { data->block_independence = value == 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); } #if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2 /* Don't compile this if we don't have liblz4. */ static int drive_compressor(struct archive_write_filter *, const char *, size_t); static int drive_compressor_independence(struct archive_write_filter *, const char *, size_t); static int drive_compressor_dependence(struct archive_write_filter *, const char *, size_t); static int lz4_write_stream_descriptor(struct archive_write_filter *); static ssize_t lz4_write_one_block(struct archive_write_filter *, const char *, size_t); /* * Setup callback. */ static int archive_filter_lz4_open(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; int ret; size_t required_size; - static size_t bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024, + static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024, 4 * 1024 * 1024 }; size_t pre_block_size; ret = __archive_write_open_filter(f->next_filter); if (ret != 0) return (ret); if (data->block_maximum_size < 4) data->block_size = bkmap[0]; else data->block_size = bkmap[data->block_maximum_size - 4]; required_size = 4 + 15 + 4 + data->block_size + 4 + 4; if (data->out_buffer_size < required_size) { size_t bs = required_size, bpb; free(data->out_buffer); if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { /* Buffer size should be a multiple number of * the of bytes per block for performance. */ bpb = archive_write_get_bytes_per_block(f->archive); if (bpb > bs) bs = bpb; else if (bpb != 0) { bs += bpb; bs -= bs % bpb; } } data->out_block_size = bs; bs += required_size; data->out_buffer = malloc(bs); data->out = data->out_buffer; data->out_buffer_size = bs; } pre_block_size = (data->block_independence)? 0: 64 * 1024; if (data->in_buffer_size < data->block_size + pre_block_size) { free(data->in_buffer_allocated); data->in_buffer_size = data->block_size; data->in_buffer_allocated = malloc(data->in_buffer_size + pre_block_size); data->in_buffer = data->in_buffer_allocated + pre_block_size; if (!data->block_independence && data->compression_level >= 3) data->in_buffer = data->in_buffer_allocated; data->in = data->in_buffer; data->in_buffer_size = data->block_size; } if (data->out_buffer == NULL || data->in_buffer_allocated == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression buffer"); return (ARCHIVE_FATAL); } f->write = archive_filter_lz4_write; return (ARCHIVE_OK); } /* * Write data to the out stream. * * Returns ARCHIVE_OK if all data written, error otherwise. */ static int archive_filter_lz4_write(struct archive_write_filter *f, const void *buff, size_t length) { struct private_data *data = (struct private_data *)f->data; int ret = ARCHIVE_OK; const char *p; size_t remaining; ssize_t size; /* If we haven't written a stream descriptor, we have to do it first. */ if (!data->header_written) { ret = lz4_write_stream_descriptor(f); if (ret != ARCHIVE_OK) return (ret); data->header_written = 1; } /* Update statistics */ data->total_in += length; p = (const char *)buff; remaining = length; while (remaining) { size_t l; /* Compress input data to output buffer */ size = lz4_write_one_block(f, p, remaining); if (size < ARCHIVE_OK) return (ARCHIVE_FATAL); l = data->out - data->out_buffer; if (l >= data->out_block_size) { ret = __archive_write_filter(f->next_filter, data->out_buffer, data->out_block_size); l -= data->out_block_size; memcpy(data->out_buffer, data->out_buffer + data->out_block_size, l); data->out = data->out_buffer + l; if (ret < ARCHIVE_WARN) break; } p += size; remaining -= size; } return (ret); } /* * Finish the compression. */ static int archive_filter_lz4_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; int ret, r1; /* Finish compression cycle. */ ret = (int)lz4_write_one_block(f, NULL, 0); if (ret >= 0) { /* * Write the last block and the end of the stream data. */ /* Write End Of Stream. */ memset(data->out, 0, 4); data->out += 4; /* Write Stream checksum if needed. */ if (data->stream_checksum) { unsigned int checksum; checksum = __archive_xxhash.XXH32_digest( data->xxh32_state); data->xxh32_state = NULL; archive_le32enc(data->out, checksum); data->out += 4; } ret = __archive_write_filter(f->next_filter, data->out_buffer, data->out - data->out_buffer); } r1 = __archive_write_close_filter(f->next_filter); return (r1 < ret ? r1 : ret); } static int archive_filter_lz4_free(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; if (data->lz4_stream != NULL) { #ifdef HAVE_LZ4HC_H if (data->compression_level >= 3) #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 LZ4_freeStreamHC(data->lz4_stream); #else LZ4_freeHC(data->lz4_stream); #endif else #endif #if LZ4_VERSION_MINOR >= 3 LZ4_freeStream(data->lz4_stream); #else LZ4_free(data->lz4_stream); #endif } free(data->out_buffer); free(data->in_buffer_allocated); free(data->xxh32_state); free(data); f->data = NULL; return (ARCHIVE_OK); } static int lz4_write_stream_descriptor(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; uint8_t *sd; sd = (uint8_t *)data->out; /* Write Magic Number. */ archive_le32enc(&sd[0], LZ4_MAGICNUMBER); /* FLG */ sd[4] = (data->version_number << 6) | (data->block_independence << 5) | (data->block_checksum << 4) | (data->stream_size << 3) | (data->stream_checksum << 2) | (data->preset_dictionary << 0); /* BD */ sd[5] = (data->block_maximum_size << 4); sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff; data->out += 7; if (data->stream_checksum) data->xxh32_state = __archive_xxhash.XXH32_init(0); else data->xxh32_state = NULL; return (ARCHIVE_OK); } static ssize_t lz4_write_one_block(struct archive_write_filter *f, const char *p, size_t length) { struct private_data *data = (struct private_data *)f->data; ssize_t r; if (p == NULL) { /* Compress remaining uncompressed data. */ if (data->in_buffer == data->in) return 0; else { size_t l = data->in - data->in_buffer; r = drive_compressor(f, data->in_buffer, l); if (r == ARCHIVE_OK) r = (ssize_t)l; } } else if ((data->block_independence || data->compression_level < 3) && data->in_buffer == data->in && length >= data->block_size) { r = drive_compressor(f, p, data->block_size); if (r == ARCHIVE_OK) r = (ssize_t)data->block_size; } else { size_t remaining_size = data->in_buffer_size - (data->in - data->in_buffer); size_t l = (remaining_size > length)? length: remaining_size; memcpy(data->in, p, l); data->in += l; if (l == remaining_size) { r = drive_compressor(f, data->in_buffer, data->block_size); if (r == ARCHIVE_OK) r = (ssize_t)l; data->in = data->in_buffer; } else r = (ssize_t)l; } return (r); } /* * Utility function to push input data through compressor, writing * full output blocks as necessary. * * Note that this handles both the regular write case (finishing == * false) and the end-of-archive case (finishing == true). */ static int drive_compressor(struct archive_write_filter *f, const char *p, size_t length) { struct private_data *data = (struct private_data *)f->data; if (data->stream_checksum) __archive_xxhash.XXH32_update(data->xxh32_state, p, (int)length); if (data->block_independence) return drive_compressor_independence(f, p, length); else return drive_compressor_dependence(f, p, length); } static int drive_compressor_independence(struct archive_write_filter *f, const char *p, size_t length) { struct private_data *data = (struct private_data *)f->data; unsigned int outsize; #ifdef HAVE_LZ4HC_H if (data->compression_level >= 3) #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 outsize = LZ4_compress_HC(p, data->out + 4, (int)length, (int)data->block_size, data->compression_level); #else outsize = LZ4_compressHC2_limitedOutput(p, data->out + 4, (int)length, (int)data->block_size, data->compression_level); #endif else #endif #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 outsize = LZ4_compress_default(p, data->out + 4, (int)length, (int)data->block_size); #else outsize = LZ4_compress_limitedOutput(p, data->out + 4, (int)length, (int)data->block_size); #endif if (outsize) { /* The buffer is compressed. */ archive_le32enc(data->out, outsize); data->out += 4; } else { /* The buffer is not compressed. The compressed size was * bigger than its uncompressed size. */ archive_le32enc(data->out, length | 0x80000000); data->out += 4; memcpy(data->out, p, length); outsize = length; } data->out += outsize; if (data->block_checksum) { unsigned int checksum = __archive_xxhash.XXH32(data->out - outsize, outsize, 0); archive_le32enc(data->out, checksum); data->out += 4; } return (ARCHIVE_OK); } static int drive_compressor_dependence(struct archive_write_filter *f, const char *p, size_t length) { struct private_data *data = (struct private_data *)f->data; int outsize; #define DICT_SIZE (64 * 1024) #ifdef HAVE_LZ4HC_H if (data->compression_level >= 3) { if (data->lz4_stream == NULL) { #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 data->lz4_stream = LZ4_createStreamHC(); LZ4_resetStreamHC(data->lz4_stream, data->compression_level); #else data->lz4_stream = LZ4_createHC(data->in_buffer_allocated); #endif if (data->lz4_stream == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression" " buffer"); return (ARCHIVE_FATAL); } } else LZ4_loadDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 outsize = LZ4_compress_HC_continue( data->lz4_stream, p, data->out + 4, (int)length, (int)data->block_size); #else outsize = LZ4_compressHC2_limitedOutput_continue( data->lz4_stream, p, data->out + 4, (int)length, (int)data->block_size, data->compression_level); #endif } else #endif { if (data->lz4_stream == NULL) { data->lz4_stream = LZ4_createStream(); if (data->lz4_stream == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression" " buffer"); return (ARCHIVE_FATAL); } } else LZ4_loadDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 outsize = LZ4_compress_fast_continue( data->lz4_stream, p, data->out + 4, (int)length, (int)data->block_size, 1); #else outsize = LZ4_compress_limitedOutput_continue( data->lz4_stream, p, data->out + 4, (int)length, (int)data->block_size); #endif } if (outsize) { /* The buffer is compressed. */ archive_le32enc(data->out, outsize); data->out += 4; } else { /* The buffer is not compressed. The compressed size was * bigger than its uncompressed size. */ archive_le32enc(data->out, length | 0x80000000); data->out += 4; memcpy(data->out, p, length); outsize = length; } data->out += outsize; if (data->block_checksum) { unsigned int checksum = __archive_xxhash.XXH32(data->out - outsize, outsize, 0); archive_le32enc(data->out, checksum); data->out += 4; } if (length == data->block_size) { #ifdef HAVE_LZ4HC_H if (data->compression_level >= 3) { #if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7 LZ4_saveDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); #else LZ4_slideInputBufferHC(data->lz4_stream); #endif data->in_buffer = data->in_buffer_allocated + DICT_SIZE; } else #endif LZ4_saveDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE); #undef DICT_SIZE } return (ARCHIVE_OK); } #else /* HAVE_LIBLZ4 */ static int archive_filter_lz4_open(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; struct archive_string as; int r; archive_string_init(&as); archive_strcpy(&as, "lz4 -z -q -q"); /* Specify a compression level. */ if (data->compression_level > 0) { archive_strcat(&as, " -"); archive_strappend_char(&as, '0' + data->compression_level); } /* Specify a block size. */ archive_strcat(&as, " -B"); archive_strappend_char(&as, '0' + data->block_maximum_size); if (data->block_checksum) archive_strcat(&as, " -BX"); if (data->stream_checksum == 0) archive_strcat(&as, " --no-frame-crc"); if (data->block_independence == 0) archive_strcat(&as, " -BD"); f->write = archive_filter_lz4_write; r = __archive_write_program_open(f, data->pdata, as.s); archive_string_free(&as); return (r); } static int archive_filter_lz4_write(struct archive_write_filter *f, const void *buff, size_t length) { struct private_data *data = (struct private_data *)f->data; return __archive_write_program_write(f, data->pdata, buff, length); } static int archive_filter_lz4_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; return __archive_write_program_close(f, data->pdata); } static int archive_filter_lz4_free(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; __archive_write_program_free(data->pdata); free(data); return (ARCHIVE_OK); } #endif /* HAVE_LIBLZ4 */ Index: head/contrib/libarchive/libarchive/archive_write_add_filter_program.c =================================================================== --- head/contrib/libarchive/libarchive/archive_write_add_filter_program.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_write_add_filter_program.c (revision 316095) @@ -1,415 +1,415 @@ /*- * Copyright (c) 2007 Joerg Sonnenberger * 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. * 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_compression_program.c 201104 2009-12-28 03:14:30Z kientzle $"); #ifdef HAVE_SYS_WAIT_H # include #endif #ifdef HAVE_ERRNO_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #include "archive.h" #include "archive_private.h" #include "archive_string.h" #include "archive_write_private.h" #include "filter_fork.h" #if ARCHIVE_VERSION_NUMBER < 4000000 int archive_write_set_compression_program(struct archive *a, const char *cmd) { __archive_write_filters_free(a); return (archive_write_add_filter_program(a, cmd)); } #endif struct archive_write_program_data { #if defined(_WIN32) && !defined(__CYGWIN__) HANDLE child; #else pid_t child; #endif int child_stdin, child_stdout; char *child_buf; size_t child_buf_len, child_buf_avail; char *program_name; }; struct private_data { struct archive_write_program_data *pdata; struct archive_string description; char *cmd; }; static int archive_compressor_program_open(struct archive_write_filter *); static int archive_compressor_program_write(struct archive_write_filter *, const void *, size_t); static int archive_compressor_program_close(struct archive_write_filter *); static int archive_compressor_program_free(struct archive_write_filter *); /* * Add a filter to this write handle that passes all data through an * external program. */ int archive_write_add_filter_program(struct archive *_a, const char *cmd) { struct archive_write_filter *f = __archive_write_allocate_filter(_a); struct private_data *data; - static const char *prefix = "Program: "; + static const char prefix[] = "Program: "; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_program"); f->data = calloc(1, sizeof(*data)); if (f->data == NULL) goto memerr; data = (struct private_data *)f->data; data->cmd = strdup(cmd); if (data->cmd == NULL) goto memerr; data->pdata = __archive_write_program_allocate(cmd); if (data->pdata == NULL) goto memerr; /* Make up a description string. */ if (archive_string_ensure(&data->description, strlen(prefix) + strlen(cmd) + 1) == NULL) goto memerr; archive_strcpy(&data->description, prefix); archive_strcat(&data->description, cmd); f->name = data->description.s; f->code = ARCHIVE_FILTER_PROGRAM; f->open = archive_compressor_program_open; f->write = archive_compressor_program_write; f->close = archive_compressor_program_close; f->free = archive_compressor_program_free; return (ARCHIVE_OK); memerr: archive_compressor_program_free(f); archive_set_error(_a, ENOMEM, "Can't allocate memory for filter program"); return (ARCHIVE_FATAL); } static int archive_compressor_program_open(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; return __archive_write_program_open(f, data->pdata, data->cmd); } static int archive_compressor_program_write(struct archive_write_filter *f, const void *buff, size_t length) { struct private_data *data = (struct private_data *)f->data; return __archive_write_program_write(f, data->pdata, buff, length); } static int archive_compressor_program_close(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; return __archive_write_program_close(f, data->pdata); } static int archive_compressor_program_free(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; if (data) { free(data->cmd); archive_string_free(&data->description); __archive_write_program_free(data->pdata); free(data); f->data = NULL; } return (ARCHIVE_OK); } /* * Allocate resources for executing an external program. */ struct archive_write_program_data * __archive_write_program_allocate(const char *program) { struct archive_write_program_data *data; data = calloc(1, sizeof(struct archive_write_program_data)); if (data == NULL) return (data); data->child_stdin = -1; data->child_stdout = -1; data->program_name = strdup(program); return (data); } /* * Release the resources. */ int __archive_write_program_free(struct archive_write_program_data *data) { if (data) { #if defined(_WIN32) && !defined(__CYGWIN__) if (data->child) CloseHandle(data->child); #endif free(data->program_name); free(data->child_buf); free(data); } return (ARCHIVE_OK); } int __archive_write_program_open(struct archive_write_filter *f, struct archive_write_program_data *data, const char *cmd) { pid_t child; int ret; ret = __archive_write_open_filter(f->next_filter); if (ret != ARCHIVE_OK) return (ret); if (data->child_buf == NULL) { data->child_buf_len = 65536; data->child_buf_avail = 0; data->child_buf = malloc(data->child_buf_len); if (data->child_buf == NULL) { archive_set_error(f->archive, ENOMEM, "Can't allocate compression buffer"); return (ARCHIVE_FATAL); } } child = __archive_create_child(cmd, &data->child_stdin, &data->child_stdout); if (child == -1) { archive_set_error(f->archive, EINVAL, "Can't launch external program: %s", cmd); return (ARCHIVE_FATAL); } #if defined(_WIN32) && !defined(__CYGWIN__) data->child = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, child); if (data->child == NULL) { close(data->child_stdin); data->child_stdin = -1; close(data->child_stdout); data->child_stdout = -1; archive_set_error(f->archive, EINVAL, "Can't launch external program: %s", cmd); return (ARCHIVE_FATAL); } #else data->child = child; #endif return (ARCHIVE_OK); } static ssize_t child_write(struct archive_write_filter *f, struct archive_write_program_data *data, const char *buf, size_t buf_len) { ssize_t ret; if (data->child_stdin == -1) return (-1); if (buf_len == 0) return (-1); for (;;) { do { ret = write(data->child_stdin, buf, buf_len); } while (ret == -1 && errno == EINTR); if (ret > 0) return (ret); if (ret == 0) { close(data->child_stdin); data->child_stdin = -1; fcntl(data->child_stdout, F_SETFL, 0); return (0); } if (ret == -1 && errno != EAGAIN) return (-1); if (data->child_stdout == -1) { fcntl(data->child_stdin, F_SETFL, 0); __archive_check_child(data->child_stdin, data->child_stdout); continue; } do { ret = read(data->child_stdout, data->child_buf + data->child_buf_avail, data->child_buf_len - data->child_buf_avail); } while (ret == -1 && errno == EINTR); if (ret == 0 || (ret == -1 && errno == EPIPE)) { close(data->child_stdout); data->child_stdout = -1; fcntl(data->child_stdin, F_SETFL, 0); continue; } if (ret == -1 && errno == EAGAIN) { __archive_check_child(data->child_stdin, data->child_stdout); continue; } if (ret == -1) return (-1); data->child_buf_avail += ret; ret = __archive_write_filter(f->next_filter, data->child_buf, data->child_buf_avail); if (ret != ARCHIVE_OK) return (-1); data->child_buf_avail = 0; } } /* * Write data to the filter stream. */ int __archive_write_program_write(struct archive_write_filter *f, struct archive_write_program_data *data, const void *buff, size_t length) { ssize_t ret; const char *buf; if (data->child == 0) return (ARCHIVE_OK); buf = buff; while (length > 0) { ret = child_write(f, data, buf, length); if (ret == -1 || ret == 0) { archive_set_error(f->archive, EIO, "Can't write to program: %s", data->program_name); return (ARCHIVE_FATAL); } length -= ret; buf += ret; } return (ARCHIVE_OK); } /* * Finish the filtering... */ int __archive_write_program_close(struct archive_write_filter *f, struct archive_write_program_data *data) { int ret, r1, status; ssize_t bytes_read; if (data->child == 0) return __archive_write_close_filter(f->next_filter); ret = 0; close(data->child_stdin); data->child_stdin = -1; fcntl(data->child_stdout, F_SETFL, 0); for (;;) { do { bytes_read = read(data->child_stdout, data->child_buf + data->child_buf_avail, data->child_buf_len - data->child_buf_avail); } while (bytes_read == -1 && errno == EINTR); if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE)) break; if (bytes_read == -1) { archive_set_error(f->archive, errno, "Error reading from program: %s", data->program_name); ret = ARCHIVE_FATAL; goto cleanup; } data->child_buf_avail += bytes_read; ret = __archive_write_filter(f->next_filter, data->child_buf, data->child_buf_avail); if (ret != ARCHIVE_OK) { ret = ARCHIVE_FATAL; goto cleanup; } data->child_buf_avail = 0; } cleanup: /* Shut down the child. */ if (data->child_stdin != -1) close(data->child_stdin); if (data->child_stdout != -1) close(data->child_stdout); while (waitpid(data->child, &status, 0) == -1 && errno == EINTR) continue; #if defined(_WIN32) && !defined(__CYGWIN__) CloseHandle(data->child); #endif data->child = 0; if (status != 0) { archive_set_error(f->archive, EIO, "Error closing program: %s", data->program_name); ret = ARCHIVE_FATAL; } r1 = __archive_write_close_filter(f->next_filter); return (r1 < ret ? r1 : ret); } Index: head/contrib/libarchive/libarchive/archive_write_set_format.c =================================================================== --- head/contrib/libarchive/libarchive/archive_write_set_format.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_write_set_format.c (revision 316095) @@ -1,78 +1,78 @@ /*- * 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 #include "archive.h" #include "archive_private.h" /* A table that maps format codes to functions. */ -static +static const struct { int code; int (*setter)(struct archive *); } codes[] = { { ARCHIVE_FORMAT_7ZIP, archive_write_set_format_7zip }, { ARCHIVE_FORMAT_CPIO, archive_write_set_format_cpio }, { ARCHIVE_FORMAT_CPIO_POSIX, archive_write_set_format_cpio }, { ARCHIVE_FORMAT_CPIO_SVR4_NOCRC, archive_write_set_format_cpio_newc }, { ARCHIVE_FORMAT_ISO9660, archive_write_set_format_iso9660 }, { ARCHIVE_FORMAT_MTREE, archive_write_set_format_mtree }, { ARCHIVE_FORMAT_RAW, archive_write_set_format_raw }, { ARCHIVE_FORMAT_SHAR, archive_write_set_format_shar }, { ARCHIVE_FORMAT_SHAR_BASE, archive_write_set_format_shar }, { ARCHIVE_FORMAT_SHAR_DUMP, archive_write_set_format_shar_dump }, { ARCHIVE_FORMAT_TAR, archive_write_set_format_pax_restricted }, { ARCHIVE_FORMAT_TAR_GNUTAR, archive_write_set_format_gnutar }, { ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, archive_write_set_format_pax }, { ARCHIVE_FORMAT_TAR_PAX_RESTRICTED, archive_write_set_format_pax_restricted }, { ARCHIVE_FORMAT_TAR_USTAR, archive_write_set_format_ustar }, { ARCHIVE_FORMAT_WARC, archive_write_set_format_warc }, { ARCHIVE_FORMAT_XAR, archive_write_set_format_xar }, { ARCHIVE_FORMAT_ZIP, archive_write_set_format_zip }, { 0, NULL } }; int archive_write_set_format(struct archive *a, int code) { int i; for (i = 0; codes[i].code != 0; i++) { if (code == codes[i].code) return ((codes[i].setter)(a)); } archive_set_error(a, EINVAL, "No such format"); return (ARCHIVE_FATAL); } Index: head/contrib/libarchive/libarchive/archive_write_set_format_by_name.c =================================================================== --- head/contrib/libarchive/libarchive/archive_write_set_format_by_name.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_write_set_format_by_name.c (revision 316095) @@ -1,92 +1,92 @@ /*- * 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_STRING_H #include #endif #include "archive.h" #include "archive_private.h" /* A table that maps names to functions. */ -static +static const struct { const char *name; int (*setter)(struct archive *); } names[] = { { "7zip", archive_write_set_format_7zip }, { "ar", archive_write_set_format_ar_bsd }, { "arbsd", archive_write_set_format_ar_bsd }, { "argnu", archive_write_set_format_ar_svr4 }, { "arsvr4", archive_write_set_format_ar_svr4 }, { "bsdtar", archive_write_set_format_pax_restricted }, { "cd9660", archive_write_set_format_iso9660 }, { "cpio", archive_write_set_format_cpio }, { "gnutar", archive_write_set_format_gnutar }, { "iso", archive_write_set_format_iso9660 }, { "iso9660", archive_write_set_format_iso9660 }, { "mtree", archive_write_set_format_mtree }, { "mtree-classic", archive_write_set_format_mtree_classic }, { "newc", archive_write_set_format_cpio_newc }, { "odc", archive_write_set_format_cpio }, { "oldtar", archive_write_set_format_v7tar }, { "pax", archive_write_set_format_pax }, { "paxr", archive_write_set_format_pax_restricted }, { "posix", archive_write_set_format_pax }, { "raw", archive_write_set_format_raw }, { "rpax", archive_write_set_format_pax_restricted }, { "shar", archive_write_set_format_shar }, { "shardump", archive_write_set_format_shar_dump }, { "ustar", archive_write_set_format_ustar }, { "v7tar", archive_write_set_format_v7tar }, { "v7", archive_write_set_format_v7tar }, { "warc", archive_write_set_format_warc }, { "xar", archive_write_set_format_xar }, { "zip", archive_write_set_format_zip }, { NULL, NULL } }; int archive_write_set_format_by_name(struct archive *a, const char *name) { int i; for (i = 0; names[i].name != NULL; i++) { if (strcmp(name, names[i].name) == 0) return ((names[i].setter)(a)); } archive_set_error(a, EINVAL, "No such format '%s'", name); a->state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } Index: head/contrib/libarchive/libarchive/archive_write_set_format_filter_by_ext.c =================================================================== --- head/contrib/libarchive/libarchive/archive_write_set_format_filter_by_ext.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_write_set_format_filter_by_ext.c (revision 316095) @@ -1,142 +1,142 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2015 Okhotnikov Kirill * 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_STRING_H #include #endif #include "archive.h" #include "archive_private.h" /* A table that maps names to functions. */ -static +static const struct { const char *name; int (*format)(struct archive *); int (*filter)(struct archive *); } names[] = { { ".7z", archive_write_set_format_7zip, archive_write_add_filter_none}, { ".zip", archive_write_set_format_zip, archive_write_add_filter_none}, { ".jar", archive_write_set_format_zip, archive_write_add_filter_none}, { ".cpio", archive_write_set_format_cpio, archive_write_add_filter_none}, { ".iso", archive_write_set_format_iso9660, archive_write_add_filter_none}, #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) { ".a", archive_write_set_format_ar_bsd, archive_write_add_filter_none}, { ".ar", archive_write_set_format_ar_bsd, archive_write_add_filter_none}, #else { ".a", archive_write_set_format_ar_svr4, archive_write_add_filter_none}, { ".ar", archive_write_set_format_ar_svr4, archive_write_add_filter_none}, #endif { ".tar", archive_write_set_format_pax_restricted, archive_write_add_filter_none}, { ".tgz", archive_write_set_format_pax_restricted, archive_write_add_filter_gzip}, { ".tar.gz", archive_write_set_format_pax_restricted, archive_write_add_filter_gzip}, { ".tar.bz2", archive_write_set_format_pax_restricted, archive_write_add_filter_bzip2}, { ".tar.xz", archive_write_set_format_pax_restricted, archive_write_add_filter_xz}, { NULL, NULL, NULL } }; static int cmpsuff(const char *str, const char *suffix) { size_t length_str, length_suffix; if ((str == NULL) || (suffix == NULL)) return -1; length_str = strlen(str); length_suffix = strlen(suffix); if (length_str >= length_suffix) { return strcmp(str + (length_str - length_suffix), suffix); } else { return -1; } } static int get_array_index(const char *name) { int i; for (i = 0; names[i].name != NULL; i++) { if (cmpsuff(name, names[i].name) == 0) return i; } return -1; } int archive_write_set_format_filter_by_ext(struct archive *a, const char *filename) { int names_index = get_array_index(filename); if (names_index >= 0) { int format_state = (names[names_index].format)(a); if (format_state == ARCHIVE_OK) return ((names[names_index].filter)(a)); else return format_state; } archive_set_error(a, EINVAL, "No such format '%s'", filename); a->state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } int archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext) { int names_index = get_array_index(filename); if (names_index < 0) names_index = get_array_index(def_ext); if (names_index >= 0) { int format_state = (names[names_index].format)(a); if (format_state == ARCHIVE_OK) return ((names[names_index].filter)(a)); else return format_state; } archive_set_error(a, EINVAL, "No such format '%s'", filename); a->state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } Index: head/contrib/libarchive/libarchive/archive_write_set_format_warc.c =================================================================== --- head/contrib/libarchive/libarchive/archive_write_set_format_warc.c (revision 316094) +++ head/contrib/libarchive/libarchive/archive_write_set_format_warc.c (revision 316095) @@ -1,445 +1,445 @@ /*- * Copyright (c) 2014 Sebastian Freundt * Author: Sebastian Freundt * * 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 #ifdef HAVE_TIME_H #include #endif #include "archive.h" #include "archive_entry.h" #include "archive_entry_locale.h" #include "archive_private.h" #include "archive_random_private.h" #include "archive_write_private.h" struct warc_s { unsigned int omit_warcinfo:1; time_t now; mode_t typ; unsigned int rng; /* populated size */ uint64_t populz; }; static const char warcinfo[] = "software: libarchive/" ARCHIVE_VERSION_ONLY_STRING "\r\n" "format: WARC file version 1.0\r\n"; typedef enum { WT_NONE, /* warcinfo */ WT_INFO, /* metadata */ WT_META, /* resource */ WT_RSRC, /* request, unsupported */ WT_REQ, /* response, unsupported */ WT_RSP, /* revisit, unsupported */ WT_RVIS, /* conversion, unsupported */ WT_CONV, /* continuation, unsupported at the moment */ WT_CONT, /* invalid type */ LAST_WT } warc_type_t; typedef struct { warc_type_t type; const char *tgturi; const char *recid; time_t rtime; time_t mtime; const char *cnttyp; uint64_t cntlen; } warc_essential_hdr_t; typedef struct { unsigned int u[4U]; } warc_uuid_t; static int _warc_options(struct archive_write*, const char *key, const char *v); static int _warc_header(struct archive_write *a, struct archive_entry *entry); static ssize_t _warc_data(struct archive_write *a, const void *buf, size_t sz); static int _warc_finish_entry(struct archive_write *a); static int _warc_close(struct archive_write *a); static int _warc_free(struct archive_write *a); /* private routines */ static ssize_t _popul_ehdr(struct archive_string *t, size_t z, warc_essential_hdr_t); static int _gen_uuid(warc_uuid_t *tgt); /* * Set output format to ISO 28500 (aka WARC) format. */ int archive_write_set_format_warc(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; struct warc_s *w; archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_format_warc"); /* If another format was already registered, unregister it. */ if (a->format_free != NULL) { (a->format_free)(a); } w = malloc(sizeof(*w)); if (w == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate warc data"); return (ARCHIVE_FATAL); } /* by default we're emitting a file wide header */ w->omit_warcinfo = 0U; /* obtain current time for date fields */ w->now = time(NULL); /* reset file type info */ w->typ = 0; /* also initialise our rng */ w->rng = (unsigned int)w->now; a->format_data = w; a->format_name = "WARC/1.0"; a->format_options = _warc_options; a->format_write_header = _warc_header; a->format_write_data = _warc_data; a->format_close = _warc_close; a->format_free = _warc_free; a->format_finish_entry = _warc_finish_entry; a->archive.archive_format = ARCHIVE_FORMAT_WARC; a->archive.archive_format_name = "WARC/1.0"; return (ARCHIVE_OK); } /* archive methods */ static int _warc_options(struct archive_write *a, const char *key, const char *val) { struct warc_s *w = a->format_data; if (strcmp(key, "omit-warcinfo") == 0) { if (val == NULL || strcmp(val, "true") == 0) { /* great */ w->omit_warcinfo = 1U; 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 _warc_header(struct archive_write *a, struct archive_entry *entry) { struct warc_s *w = a->format_data; struct archive_string hdr; #define MAX_HDR_SIZE 512 /* check whether warcinfo record needs outputting */ if (!w->omit_warcinfo) { ssize_t r; warc_essential_hdr_t wi = { WT_INFO, /*uri*/NULL, /*urn*/NULL, /*rtm*/0, /*mtm*/0, /*cty*/"application/warc-fields", /*len*/sizeof(warcinfo) - 1U, }; wi.rtime = w->now; wi.mtime = w->now; archive_string_init(&hdr); r = _popul_ehdr(&hdr, MAX_HDR_SIZE, wi); if (r >= 0) { /* jackpot! */ /* now also use HDR buffer for the actual warcinfo */ archive_strncat(&hdr, warcinfo, sizeof(warcinfo) -1); /* append end-of-record indicator */ archive_strncat(&hdr, "\r\n\r\n", 4); /* write to output stream */ __archive_write_output(a, hdr.s, archive_strlen(&hdr)); } /* indicate we're done with file header writing */ w->omit_warcinfo = 1U; archive_string_free(&hdr); } if (archive_entry_pathname(entry) == NULL) { archive_set_error(&a->archive, EINVAL, "Invalid filename"); return (ARCHIVE_WARN); } w->typ = archive_entry_filetype(entry); w->populz = 0U; if (w->typ == AE_IFREG) { warc_essential_hdr_t rh = { WT_RSRC, /*uri*/NULL, /*urn*/NULL, /*rtm*/0, /*mtm*/0, /*cty*/NULL, /*len*/0, }; ssize_t r; rh.tgturi = archive_entry_pathname(entry); rh.rtime = w->now; rh.mtime = archive_entry_mtime(entry); rh.cntlen = (size_t)archive_entry_size(entry); archive_string_init(&hdr); r = _popul_ehdr(&hdr, MAX_HDR_SIZE, rh); if (r < 0) { /* don't bother */ archive_set_error( &a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "cannot archive file"); return (ARCHIVE_WARN); } /* otherwise append to output stream */ __archive_write_output(a, hdr.s, r); /* and let subsequent calls to _data() know about the size */ w->populz = rh.cntlen; archive_string_free(&hdr); return (ARCHIVE_OK); } /* just resort to erroring as per Tim's advice */ archive_set_error( &a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "WARC can only process regular files"); return (ARCHIVE_FAILED); } static ssize_t _warc_data(struct archive_write *a, const void *buf, size_t len) { struct warc_s *w = a->format_data; if (w->typ == AE_IFREG) { int rc; /* never write more bytes than announced */ if (len > w->populz) { len = (size_t)w->populz; } /* now then, out we put the whole shebang */ rc = __archive_write_output(a, buf, len); if (rc != ARCHIVE_OK) { return rc; } } return len; } static int _warc_finish_entry(struct archive_write *a) { static const char _eor[] = "\r\n\r\n"; struct warc_s *w = a->format_data; if (w->typ == AE_IFREG) { int rc = __archive_write_output(a, _eor, sizeof(_eor) - 1U); if (rc != ARCHIVE_OK) { return rc; } } /* reset type info */ w->typ = 0; return (ARCHIVE_OK); } static int _warc_close(struct archive_write *a) { (void)a; /* UNUSED */ return (ARCHIVE_OK); } static int _warc_free(struct archive_write *a) { struct warc_s *w = a->format_data; free(w); a->format_data = NULL; return (ARCHIVE_OK); } /* private routines */ static void xstrftime(struct archive_string *as, const char *fmt, time_t t) { /** like strftime(3) but for time_t objects */ struct tm *rt; #if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S) struct tm timeHere; #endif char strtime[100]; size_t len; #ifdef HAVE_GMTIME_R if ((rt = gmtime_r(&t, &timeHere)) == NULL) return; #elif defined(HAVE__GMTIME64_S) _gmtime64_s(&timeHere, &t); #else if ((rt = gmtime(&t)) == NULL) return; #endif /* leave the hard yacker to our role model strftime() */ len = strftime(strtime, sizeof(strtime)-1, fmt, rt); archive_strncat(as, strtime, len); } static ssize_t _popul_ehdr(struct archive_string *tgt, size_t tsz, warc_essential_hdr_t hdr) { static const char _ver[] = "WARC/1.0\r\n"; - static const char *_typ[LAST_WT] = { + static const char * const _typ[LAST_WT] = { NULL, "warcinfo", "metadata", "resource", NULL }; char std_uuid[48U]; if (hdr.type == WT_NONE || hdr.type > WT_RSRC) { /* brilliant, how exactly did we get here? */ return -1; } archive_strcpy(tgt, _ver); archive_string_sprintf(tgt, "WARC-Type: %s\r\n", _typ[hdr.type]); if (hdr.tgturi != NULL) { /* check if there's a xyz:// */ static const char _uri[] = ""; static const char _fil[] = "file://"; const char *u; char *chk = strchr(hdr.tgturi, ':'); if (chk != NULL && chk[1U] == '/' && chk[2U] == '/') { /* yep, it's definitely a URI */ u = _uri; } else { /* hm, best to prepend file:// then */ u = _fil; } archive_string_sprintf(tgt, "WARC-Target-URI: %s%s\r\n", u, hdr.tgturi); } /* record time is usually when the http is sent off, * just treat the archive writing as such for a moment */ xstrftime(tgt, "WARC-Date: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.rtime); /* while we're at it, record the mtime */ xstrftime(tgt, "Last-Modified: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.mtime); if (hdr.recid == NULL) { /* generate one, grrrr */ warc_uuid_t u; _gen_uuid(&u); /* Unfortunately, archive_string_sprintf does not * handle the minimum number following '%'. * So we have to use snprintf function here instead * of archive_string_snprintf function. */ #if defined(_WIN32) && !defined(__CYGWIN__) && !( defined(_MSC_VER) && _MSC_VER >= 1900) #define snprintf _snprintf #endif snprintf( std_uuid, sizeof(std_uuid), "", u.u[0U], u.u[1U] >> 16U, u.u[1U] & 0xffffU, u.u[2U] >> 16U, u.u[2U] & 0xffffU, u.u[3U]); hdr.recid = std_uuid; } /* record-id is mandatory, fingers crossed we won't fail */ archive_string_sprintf(tgt, "WARC-Record-ID: %s\r\n", hdr.recid); if (hdr.cnttyp != NULL) { archive_string_sprintf(tgt, "Content-Type: %s\r\n", hdr.cnttyp); } /* next one is mandatory */ archive_string_sprintf(tgt, "Content-Length: %ju\r\n", (uintmax_t)hdr.cntlen); /**/ archive_strncat(tgt, "\r\n", 2); return (archive_strlen(tgt) >= tsz)? -1: (ssize_t)archive_strlen(tgt); } static int _gen_uuid(warc_uuid_t *tgt) { archive_random(tgt->u, sizeof(tgt->u)); /* obey uuid version 4 rules */ tgt->u[1U] &= 0xffff0fffU; tgt->u[1U] |= 0x4000U; tgt->u[2U] &= 0x3fffffffU; tgt->u[2U] |= 0x80000000U; return 0; } /* archive_write_set_format_warc.c ends here */ Index: head/contrib/libarchive =================================================================== --- head/contrib/libarchive (revision 316094) +++ head/contrib/libarchive (revision 316095) Property changes on: head/contrib/libarchive ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /vendor/libarchive/dist:r316083,316094 Index: head/lib/libarchive/Makefile =================================================================== --- head/lib/libarchive/Makefile (revision 316094) +++ head/lib/libarchive/Makefile (revision 316095) @@ -1,422 +1,420 @@ # $FreeBSD$ .include PACKAGE=lib${LIB} _LIBARCHIVEDIR= ${SRCTOP}/contrib/libarchive LIB= archive LIBADD= z bz2 lzma bsdxml 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 LIBADD+= crypto .else LIBADD+= md .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:Mpowerpc*} != "" 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_acl_maps_freebsd.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_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_acl_freebsd.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_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_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_acl_freebsd.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_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