diff options
author | Martin Matuska <mm@FreeBSD.org> | 2013-03-21 18:59:02 +0000 |
---|---|---|
committer | Martin Matuska <mm@FreeBSD.org> | 2013-03-21 18:59:02 +0000 |
commit | 6a316f322a46b3e531726dd2a7de63b9e245878d (patch) | |
tree | 45c2c8840d99cc81725a3970fd3beee587313cf5 /libarchive | |
parent | 81418b36c02b6434acd4b8ae4cfb8c80a3742fd4 (diff) | |
download | src-6a316f322a46b3e531726dd2a7de63b9e245878d.tar.gz src-6a316f322a46b3e531726dd2a7de63b9e245878d.zip |
Update libarchive's vendor dist to version 3.1.2 from release branch.
Git branch: release
Git commit: 19f23e191f9d3e1dd2a518735046100419965804
Obtained from: https://github.com/libarchive/libarchive.git
Notes
Notes:
svn path=/vendor/libarchive/dist/; revision=248590
Diffstat (limited to 'libarchive')
306 files changed, 38937 insertions, 2600 deletions
diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt index 73d9baf01e87..ecb0409bd9d8 100644 --- a/libarchive/CMakeLists.txt +++ b/libarchive/CMakeLists.txt @@ -15,6 +15,9 @@ SET(include_HEADERS SET(libarchive_SOURCES archive_acl.c archive_check_magic.c + archive_cmdline.c + archive_cmdline_private.h + archive_crc32.h archive_crypto.c archive_crypto_private.h archive_endian.h @@ -42,6 +45,7 @@ SET(libarchive_SOURCES archive_rb.c archive_rb.h archive_read.c + archive_read_append_filter.c archive_read_data_into_fd.c archive_read_disk_entry_from_file.c archive_read_disk_posix.c @@ -53,11 +57,15 @@ SET(libarchive_SOURCES archive_read_open_filename.c archive_read_open_memory.c archive_read_private.h + 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_lzop.c archive_read_support_filter_none.c archive_read_support_filter_program.c archive_read_support_filter_rpm.c @@ -85,6 +93,7 @@ SET(libarchive_SOURCES archive_util.c archive_virtual.c archive_write.c + archive_write_disk_acl.c archive_write_disk_posix.c archive_write_disk_private.h archive_write_disk_set_standard_lookup.c @@ -94,11 +103,17 @@ SET(libarchive_SOURCES archive_write_open_filename.c archive_write_open_memory.c archive_write_add_filter.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_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 @@ -112,10 +127,11 @@ SET(libarchive_SOURCES archive_write_set_format_pax.c archive_write_set_format_shar.c archive_write_set_format_ustar.c + archive_write_set_format_v7tar.c archive_write_set_format_xar.c archive_write_set_format_zip.c archive_write_set_options.c - filter_fork.c + filter_fork_posix.c filter_fork.h ) diff --git a/libarchive/archive.h b/libarchive/archive.h index d5316ca93065..f56bc38e5b43 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -97,6 +97,12 @@ #define __LA_PRINTF(fmtarg, firstvararg) /* nothing */ #endif +#if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1 +# define __LA_DEPRECATED __attribute__((deprecated)) +#else +# define __LA_DEPRECATED +#endif + #ifdef __cplusplus extern "C" { #endif @@ -118,13 +124,13 @@ extern "C" { * assert that ARCHIVE_VERSION_NUMBER >= 2012108. */ /* Note: Compiler will complain if this does not match archive_entry.h! */ -#define ARCHIVE_VERSION_NUMBER 3000004 +#define ARCHIVE_VERSION_NUMBER 3001002 __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_STRING "libarchive 3.0.4" +#define ARCHIVE_VERSION_STRING "libarchive 3.1.2" __LA_DECL const char * archive_version_string(void); /* Declare our basic types. */ @@ -194,6 +200,13 @@ typedef int archive_open_callback(struct archive *, void *_client_data); typedef int archive_close_callback(struct archive *, void *_client_data); +/* Switches from one client data object to the next/prev client data object. + * This is useful for reading from different data blocks such as a set of files + * that make up one large file. + */ +typedef int archive_switch_callback(struct archive *, void *_client_data1, + void *_client_data2); + /* * Codes to identify various stream filters. */ @@ -207,6 +220,9 @@ typedef int archive_close_callback(struct archive *, void *_client_data); #define ARCHIVE_FILTER_UU 7 #define ARCHIVE_FILTER_RPM 8 #define ARCHIVE_FILTER_LZIP 9 +#define ARCHIVE_FILTER_LRZIP 10 +#define ARCHIVE_FILTER_LZOP 11 +#define ARCHIVE_FILTER_GRZIP 12 #if ARCHIVE_VERSION_NUMBER < 4000000 #define ARCHIVE_COMPRESSION_NONE ARCHIVE_FILTER_NONE @@ -219,6 +235,7 @@ typedef int archive_close_callback(struct archive *, void *_client_data); #define ARCHIVE_COMPRESSION_UU ARCHIVE_FILTER_UU #define ARCHIVE_COMPRESSION_RPM ARCHIVE_FILTER_RPM #define ARCHIVE_COMPRESSION_LZIP ARCHIVE_FILTER_LZIP +#define ARCHIVE_COMPRESSION_LRZIP ARCHIVE_FILTER_LRZIP #endif /* @@ -291,37 +308,49 @@ __LA_DECL struct archive *archive_read_new(void); */ #if ARCHIVE_VERSION_NUMBER < 4000000 -__LA_DECL int archive_read_support_compression_all(struct archive *); -__LA_DECL int archive_read_support_compression_bzip2(struct archive *); -__LA_DECL int archive_read_support_compression_compress(struct archive *); -__LA_DECL int archive_read_support_compression_gzip(struct archive *); -__LA_DECL int archive_read_support_compression_lzip(struct archive *); -__LA_DECL int archive_read_support_compression_lzma(struct archive *); -__LA_DECL int archive_read_support_compression_none(struct archive *); +__LA_DECL int archive_read_support_compression_all(struct archive *) + __LA_DEPRECATED; +__LA_DECL int archive_read_support_compression_bzip2(struct archive *) + __LA_DEPRECATED; +__LA_DECL int archive_read_support_compression_compress(struct archive *) + __LA_DEPRECATED; +__LA_DECL int archive_read_support_compression_gzip(struct archive *) + __LA_DEPRECATED; +__LA_DECL int archive_read_support_compression_lzip(struct archive *) + __LA_DEPRECATED; +__LA_DECL int archive_read_support_compression_lzma(struct archive *) + __LA_DEPRECATED; +__LA_DECL int archive_read_support_compression_none(struct archive *) + __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_program(struct archive *, - const char *command); + const char *command) __LA_DEPRECATED; __LA_DECL int archive_read_support_compression_program_signature (struct archive *, const char *, - const void * /* match */, size_t); - -__LA_DECL int archive_read_support_compression_rpm(struct archive *); -__LA_DECL int archive_read_support_compression_uu(struct archive *); -__LA_DECL int archive_read_support_compression_xz(struct archive *); + const void * /* match */, size_t) __LA_DEPRECATED; + +__LA_DECL int archive_read_support_compression_rpm(struct archive *) + __LA_DEPRECATED; +__LA_DECL int archive_read_support_compression_uu(struct archive *) + __LA_DEPRECATED; +__LA_DECL int archive_read_support_compression_xz(struct archive *) + __LA_DEPRECATED; #endif __LA_DECL int archive_read_support_filter_all(struct archive *); __LA_DECL int archive_read_support_filter_bzip2(struct archive *); __LA_DECL int archive_read_support_filter_compress(struct archive *); __LA_DECL int archive_read_support_filter_gzip(struct archive *); +__LA_DECL int archive_read_support_filter_grzip(struct archive *); +__LA_DECL int archive_read_support_filter_lrzip(struct archive *); __LA_DECL int archive_read_support_filter_lzip(struct archive *); __LA_DECL int archive_read_support_filter_lzma(struct archive *); +__LA_DECL int archive_read_support_filter_lzop(struct archive *); __LA_DECL int archive_read_support_filter_none(struct archive *); __LA_DECL int archive_read_support_filter_program(struct archive *, const char *command); __LA_DECL int archive_read_support_filter_program_signature - (struct archive *, const char *, + (struct archive *, const char * /* cmd */, const void * /* match */, size_t); - __LA_DECL int archive_read_support_filter_rpm(struct archive *); __LA_DECL int archive_read_support_filter_uu(struct archive *); __LA_DECL int archive_read_support_filter_xz(struct archive *); @@ -343,6 +372,17 @@ __LA_DECL int archive_read_support_format_tar(struct archive *); __LA_DECL int archive_read_support_format_xar(struct archive *); __LA_DECL int archive_read_support_format_zip(struct archive *); +/* Functions to manually set the format and filters to be used. This is + * useful to bypass the bidding process when the format and filters to use + * is known in advance. + */ +__LA_DECL int archive_read_set_format(struct archive *, int); +__LA_DECL int archive_read_append_filter(struct archive *, int); +__LA_DECL int archive_read_append_filter_program(struct archive *, + const char *); +__LA_DECL int archive_read_append_filter_program_signature + (struct archive *, const char *, const void * /* match */, size_t); + /* Set various callbacks. */ __LA_DECL int archive_read_set_open_callback(struct archive *, archive_open_callback *); @@ -354,8 +394,23 @@ __LA_DECL int archive_read_set_skip_callback(struct archive *, archive_skip_callback *); __LA_DECL int archive_read_set_close_callback(struct archive *, archive_close_callback *); -/* The callback data is provided to all of the callbacks above. */ +/* Callback used to switch between one data object to the next */ +__LA_DECL int archive_read_set_switch_callback(struct archive *, + archive_switch_callback *); + +/* This sets the first data object. */ __LA_DECL int archive_read_set_callback_data(struct archive *, void *); +/* This sets data object at specified index */ +__LA_DECL int archive_read_set_callback_data2(struct archive *, void *, + unsigned int); +/* This adds a data object at the specified index. */ +__LA_DECL int archive_read_add_callback_data(struct archive *, void *, + unsigned int); +/* This appends a data object to the end of list */ +__LA_DECL int archive_read_append_callback_data(struct archive *, void *); +/* This prepends a data object to the beginning of list */ +__LA_DECL int archive_read_prepend_callback_data(struct archive *, void *); + /* Opening freezes the callbacks. */ __LA_DECL int archive_read_open1(struct archive *); @@ -375,11 +430,15 @@ __LA_DECL int archive_read_open2(struct archive *, void *_client_data, /* Use this if you know the filename. Note: NULL indicates stdin. */ __LA_DECL int archive_read_open_filename(struct archive *, const char *_filename, size_t _block_size); +/* Use this for reading multivolume files by filenames. + * NOTE: Must be NULL terminated. Sorting is NOT done. */ +__LA_DECL int archive_read_open_filenames(struct archive *, + const char **_filenames, size_t _block_size); __LA_DECL int archive_read_open_filename_w(struct archive *, const wchar_t *_filename, size_t _block_size); /* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */ __LA_DECL int archive_read_open_file(struct archive *, - const char *_filename, size_t _block_size); + const char *_filename, size_t _block_size) __LA_DEPRECATED; /* Read an archive that's stored in memory. */ __LA_DECL int archive_read_open_memory(struct archive *, void * buff, size_t size); @@ -411,6 +470,9 @@ __LA_DECL __LA_INT64_T archive_read_header_position(struct archive *); __LA_DECL __LA_SSIZE_T archive_read_data(struct archive *, void *, size_t); +/* Seek within the body of an entry. Similar to lseek(2). */ +__LA_DECL __LA_INT64_T archive_seek_data(struct archive *, __LA_INT64_T, int); + /* * A zero-copy version of archive_read_data that also exposes the file offset * of each returned block. Note that the client has no way to specify @@ -494,6 +556,12 @@ __LA_DECL int archive_read_set_options(struct archive *_a, /* Default: Do not restore Mac extended metadata. */ /* This has no effect except on Mac OS. */ #define ARCHIVE_EXTRACT_MAC_METADATA (0x2000) +/* Default: Use HFS+ compression if it was compressed. */ +/* This has no effect except on Mac OS v10.6 or later. */ +#define ARCHIVE_EXTRACT_NO_HFS_COMPRESSION (0x4000) +/* Default: Do not use HFS+ compression if it was not compressed. */ +/* This has no effect except on Mac OS v10.6 or later. */ +#define ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED (0x8000) __LA_DECL int archive_read_extract(struct archive *, struct archive_entry *, int flags); @@ -514,7 +582,7 @@ __LA_DECL int archive_read_close(struct archive *); __LA_DECL int archive_read_free(struct archive *); #if ARCHIVE_VERSION_NUMBER < 4000000 /* Synonym for archive_read_free() for backwards compatibility. */ -__LA_DECL int archive_read_finish(struct archive *); +__LA_DECL int archive_read_finish(struct archive *) __LA_DEPRECATED; #endif /*- @@ -547,27 +615,41 @@ __LA_DECL int archive_write_set_skip_file(struct archive *, __LA_INT64_T, __LA_INT64_T); #if ARCHIVE_VERSION_NUMBER < 4000000 -__LA_DECL int archive_write_set_compression_bzip2(struct archive *); -__LA_DECL int archive_write_set_compression_compress(struct archive *); -__LA_DECL int archive_write_set_compression_gzip(struct archive *); -__LA_DECL int archive_write_set_compression_lzip(struct archive *); -__LA_DECL int archive_write_set_compression_lzma(struct archive *); -__LA_DECL int archive_write_set_compression_none(struct archive *); +__LA_DECL int archive_write_set_compression_bzip2(struct archive *) + __LA_DEPRECATED; +__LA_DECL int archive_write_set_compression_compress(struct archive *) + __LA_DEPRECATED; +__LA_DECL int archive_write_set_compression_gzip(struct archive *) + __LA_DEPRECATED; +__LA_DECL int archive_write_set_compression_lzip(struct archive *) + __LA_DEPRECATED; +__LA_DECL int archive_write_set_compression_lzma(struct archive *) + __LA_DEPRECATED; +__LA_DECL int archive_write_set_compression_none(struct archive *) + __LA_DEPRECATED; __LA_DECL int archive_write_set_compression_program(struct archive *, - const char *cmd); -__LA_DECL int archive_write_set_compression_xz(struct archive *); + const char *cmd) __LA_DEPRECATED; +__LA_DECL int archive_write_set_compression_xz(struct archive *) + __LA_DEPRECATED; #endif /* A convenience function to set the filter based on the code. */ __LA_DECL int archive_write_add_filter(struct archive *, int filter_code); +__LA_DECL int archive_write_add_filter_by_name(struct archive *, + const char *name); +__LA_DECL int archive_write_add_filter_b64encode(struct archive *); __LA_DECL int archive_write_add_filter_bzip2(struct archive *); __LA_DECL int archive_write_add_filter_compress(struct archive *); +__LA_DECL int archive_write_add_filter_grzip(struct archive *); __LA_DECL int archive_write_add_filter_gzip(struct archive *); +__LA_DECL int archive_write_add_filter_lrzip(struct archive *); __LA_DECL int archive_write_add_filter_lzip(struct archive *); __LA_DECL int archive_write_add_filter_lzma(struct archive *); +__LA_DECL int archive_write_add_filter_lzop(struct archive *); __LA_DECL int archive_write_add_filter_none(struct archive *); __LA_DECL int archive_write_add_filter_program(struct archive *, const char *cmd); +__LA_DECL int archive_write_add_filter_uuencode(struct archive *); __LA_DECL int archive_write_add_filter_xz(struct archive *); @@ -584,14 +666,18 @@ __LA_DECL int archive_write_set_format_cpio_newc(struct archive *); __LA_DECL int archive_write_set_format_gnutar(struct archive *); __LA_DECL int archive_write_set_format_iso9660(struct archive *); __LA_DECL int archive_write_set_format_mtree(struct archive *); +__LA_DECL int archive_write_set_format_mtree_classic(struct archive *); /* TODO: int archive_write_set_format_old_tar(struct archive *); */ __LA_DECL int archive_write_set_format_pax(struct archive *); __LA_DECL int archive_write_set_format_pax_restricted(struct archive *); __LA_DECL int archive_write_set_format_shar(struct archive *); __LA_DECL int archive_write_set_format_shar_dump(struct archive *); __LA_DECL int archive_write_set_format_ustar(struct archive *); +__LA_DECL int archive_write_set_format_v7tar(struct archive *); __LA_DECL int archive_write_set_format_xar(struct archive *); __LA_DECL int archive_write_set_format_zip(struct archive *); +__LA_DECL int archive_write_zip_set_compression_deflate(struct archive *); +__LA_DECL int archive_write_zip_set_compression_store(struct archive *); __LA_DECL int archive_write_open(struct archive *, void *, archive_open_callback *, archive_write_callback *, archive_close_callback *); @@ -600,7 +686,8 @@ __LA_DECL int archive_write_open_filename(struct archive *, const char *_file); __LA_DECL int archive_write_open_filename_w(struct archive *, const wchar_t *_file); /* A deprecated synonym for archive_write_open_filename() */ -__LA_DECL int archive_write_open_file(struct archive *, const char *_file); +__LA_DECL int archive_write_open_file(struct archive *, const char *_file) + __LA_DEPRECATED; __LA_DECL int archive_write_open_FILE(struct archive *, FILE *); /* _buffSize is the size of the buffer, _used refers to a variable that * will be updated after each write into the buffer. */ @@ -622,12 +709,16 @@ __LA_DECL __LA_SSIZE_T archive_write_data_block(struct archive *, __LA_DECL int archive_write_finish_entry(struct archive *); __LA_DECL int archive_write_close(struct archive *); +/* Marks the archive as FATAL so that a subsequent free() operation + * won't try to close() cleanly. Provides a fast abort capability + * when the client discovers that things have gone wrong. */ +__LA_DECL int archive_write_fail(struct archive *); /* This can fail if the archive wasn't already closed, in which case * archive_write_free() will implicitly call archive_write_close(). */ __LA_DECL int archive_write_free(struct archive *); #if ARCHIVE_VERSION_NUMBER < 4000000 /* Synonym for archive_write_free() for backwards compatibility. */ -__LA_DECL int archive_write_finish(struct archive *); +__LA_DECL int archive_write_finish(struct archive *) __LA_DEPRECATED; #endif /* @@ -806,13 +897,17 @@ __LA_DECL const char * archive_filter_name(struct archive *, int); /* These don't properly handle multiple filters, so are deprecated and * will eventually be removed. */ /* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, -1); */ -__LA_DECL __LA_INT64_T archive_position_compressed(struct archive *); +__LA_DECL __LA_INT64_T archive_position_compressed(struct archive *) + __LA_DEPRECATED; /* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, 0); */ -__LA_DECL __LA_INT64_T archive_position_uncompressed(struct archive *); +__LA_DECL __LA_INT64_T archive_position_uncompressed(struct archive *) + __LA_DEPRECATED; /* As of libarchive 3.0, this is an alias for archive_filter_name(a, 0); */ -__LA_DECL const char *archive_compression_name(struct archive *); +__LA_DECL const char *archive_compression_name(struct archive *) + __LA_DEPRECATED; /* As of libarchive 3.0, this is an alias for archive_filter_code(a, 0); */ -__LA_DECL int archive_compression(struct archive *); +__LA_DECL int archive_compression(struct archive *) + __LA_DEPRECATED; #endif __LA_DECL int archive_errno(struct archive *); diff --git a/libarchive/archive_cmdline.c b/libarchive/archive_cmdline.c new file mode 100644 index 000000000000..7d3bac53bb5c --- /dev/null +++ b/libarchive/archive_cmdline.c @@ -0,0 +1,227 @@ +/*- + * 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_STRING_H +# include <string.h> +#endif +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif + +#include "archive.h" +#include "archive_cmdline_private.h" +#include "archive_string.h" + +static int cmdline_set_path(struct archive_cmdline *, const char *); +static int cmdline_add_arg(struct archive_cmdline *, const char *); + +static ssize_t +extract_quotation(struct archive_string *as, const char *p) +{ + const char *s; + + for (s = p + 1; *s;) { + if (*s == '\\') { + if (s[1] != '\0') { + archive_strappend_char(as, s[1]); + s += 2; + } else + s++; + } else if (*s == '"') + break; + else { + archive_strappend_char(as, s[0]); + s++; + } + } + if (*s != '"') + return (ARCHIVE_FAILED);/* Invalid sequence. */ + return ((ssize_t)(s + 1 - p)); +} + +static ssize_t +get_argument(struct archive_string *as, const char *p) +{ + const char *s = p; + + archive_string_empty(as); + + /* Skip beginning space characters. */ + while (*s != '\0' && *s == ' ') + s++; + /* Copy non-space characters. */ + while (*s != '\0' && *s != ' ') { + if (*s == '\\') { + if (s[1] != '\0') { + archive_strappend_char(as, s[1]); + s += 2; + } else { + s++;/* Ignore this character.*/ + break; + } + } else if (*s == '"') { + ssize_t q = extract_quotation(as, s); + if (q < 0) + return (ARCHIVE_FAILED);/* Invalid sequence. */ + s += q; + } else { + archive_strappend_char(as, s[0]); + s++; + } + } + return ((ssize_t)(s - p)); +} + +/* + * Set up command line arguments. + * Returns ARChIVE_OK if everything okey. + * Returns ARChIVE_FAILED if there is a lack of the `"' terminator or an + * empty command line. + * Returns ARChIVE_FATAL if no memory. + */ +int +__archive_cmdline_parse(struct archive_cmdline *data, const char *cmd) +{ + struct archive_string as; + const char *p; + ssize_t al; + int r; + + archive_string_init(&as); + + /* Get first argument as a command path. */ + al = get_argument(&as, cmd); + if (al < 0) { + r = ARCHIVE_FAILED;/* Invalid sequence. */ + goto exit_function; + } + if (archive_strlen(&as) == 0) { + r = ARCHIVE_FAILED;/* An empty command path. */ + goto exit_function; + } + r = cmdline_set_path(data, as.s); + if (r != ARCHIVE_OK) + goto exit_function; + p = strrchr(as.s, '/'); + if (p == NULL) + p = as.s; + else + p++; + r = cmdline_add_arg(data, p); + if (r != ARCHIVE_OK) + goto exit_function; + cmd += al; + + for (;;) { + al = get_argument(&as, cmd); + if (al < 0) { + r = ARCHIVE_FAILED;/* Invalid sequence. */ + goto exit_function; + } + if (al == 0) + break; + cmd += al; + if (archive_strlen(&as) == 0 && *cmd == '\0') + break; + r = cmdline_add_arg(data, as.s); + if (r != ARCHIVE_OK) + goto exit_function; + } + r = ARCHIVE_OK; +exit_function: + archive_string_free(&as); + return (r); +} + +/* + * Set the program path. + */ +static int +cmdline_set_path(struct archive_cmdline *data, const char *path) +{ + char *newptr; + + newptr = realloc(data->path, strlen(path) + 1); + if (newptr == NULL) + return (ARCHIVE_FATAL); + data->path = newptr; + strcpy(data->path, path); + return (ARCHIVE_OK); +} + +/* + * Add a argument for the program. + */ +static int +cmdline_add_arg(struct archive_cmdline *data, const char *arg) +{ + char **newargv; + + if (data->path == NULL) + return (ARCHIVE_FAILED); + + newargv = realloc(data->argv, (data->argc + 2) * sizeof(char *)); + if (newargv == NULL) + return (ARCHIVE_FATAL); + data->argv = newargv; + data->argv[data->argc] = strdup(arg); + if (data->argv[data->argc] == NULL) + return (ARCHIVE_FATAL); + /* Set the terminator of argv. */ + data->argv[++data->argc] = NULL; + return (ARCHIVE_OK); +} + +struct archive_cmdline * +__archive_cmdline_allocate(void) +{ + return (struct archive_cmdline *) + calloc(1, sizeof(struct archive_cmdline)); +} + +/* + * Release the resources. + */ +int +__archive_cmdline_free(struct archive_cmdline *data) +{ + + if (data) { + free(data->path); + if (data->argv != NULL) { + int i; + for (i = 0; data->argv[i] != NULL; i++) + free(data->argv[i]); + free(data->argv); + } + free(data); + } + return (ARCHIVE_OK); +} + diff --git a/libarchive/archive_cmdline_private.h b/libarchive/archive_cmdline_private.h new file mode 100644 index 000000000000..4e409e814817 --- /dev/null +++ b/libarchive/archive_cmdline_private.h @@ -0,0 +1,47 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#ifndef __LIBARCHIVE_BUILD +#ifndef __LIBARCHIVE_TEST +#error This header is only to be used internally to libarchive. +#endif +#endif + +#ifndef ARCHIVE_CMDLINE_PRIVATE_H +#define ARCHIVE_CMDLINE_PRIVATE_H + +struct archive_cmdline { + char *path; + char **argv; + int argc; +}; + +struct archive_cmdline *__archive_cmdline_allocate(void); +int __archive_cmdline_parse(struct archive_cmdline *, const char *); +int __archive_cmdline_free(struct archive_cmdline *); + +#endif diff --git a/libarchive/archive_crypto.c b/libarchive/archive_crypto.c index 2caf57169296..85aba3ae27cd 100644 --- a/libarchive/archive_crypto.c +++ b/libarchive/archive_crypto.c @@ -90,7 +90,7 @@ win_crypto_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len) static int win_crypto_Final(unsigned char *buf, size_t bufsize, Digest_CTX *ctx) { - DWORD siglen = bufsize; + DWORD siglen = (DWORD)bufsize; if (!ctx->valid) return (ARCHIVE_FAILED); @@ -1222,8 +1222,10 @@ __archive_stub_sha512final(archive_sha512_ctx *ctx, void *md) * 2. libc2 * 3. libc3 * 4. libSystem - * 5. OpenSSL - * 6. Windows API + * 5. Nettle + * 6. OpenSSL + * 7. libmd + * 8. Windows API */ const struct archive_crypto __archive_crypto = { diff --git a/libarchive/archive_entry.c b/libarchive/archive_entry.c index b531c77d2126..386e51d473e8 100644 --- a/libarchive/archive_entry.c +++ b/libarchive/archive_entry.c @@ -1449,6 +1449,9 @@ static struct flag { { "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0 }, { "nouunlink", L"nouunlink", UF_NOUNLINK, 0 }, #endif +#ifdef UF_COMPRESSED + { "nocompressed",L"nocompressed", UF_COMPRESSED, 0 }, +#endif #ifdef EXT2_UNRM_FL { "nouunlink", L"nouunlink", EXT2_UNRM_FL, 0}, #endif diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h index 5fd7cab0dc3f..a9050652e6a4 100644 --- a/libarchive/archive_entry.h +++ b/libarchive/archive_entry.h @@ -29,7 +29,7 @@ #define ARCHIVE_ENTRY_H_INCLUDED /* Note: Compiler will complain if this does not match archive.h! */ -#define ARCHIVE_VERSION_NUMBER 3000004 +#define ARCHIVE_VERSION_NUMBER 3001002 /* * Note: archive_entry.h is for use outside of libarchive; the diff --git a/libarchive/archive_entry_link_resolver.c b/libarchive/archive_entry_link_resolver.c index 07f0d362e48c..c7d59497a7cf 100644 --- a/libarchive/archive_entry_link_resolver.c +++ b/libarchive/archive_entry_link_resolver.c @@ -244,6 +244,9 @@ archive_entry_linkify(struct archive_entry_linkresolver *res, * for future use. */ le = insert_entry(res, *e); + if (le == NULL) + /* XXX We should return an error code XXX */ + return; le->entry = *e; *e = NULL; } diff --git a/libarchive/archive_match.c b/libarchive/archive_match.c index 6b533e6ba94d..6b6be9cb20b5 100644 --- a/libarchive/archive_match.c +++ b/libarchive/archive_match.c @@ -1376,6 +1376,7 @@ add_entry(struct archive_match *a, int flag, archive_mstring_copy_wcs(&(f->pathname), pathname); a->exclusion_tree.rbt_ops = &rb_ops_wcs; #else + (void)rb_ops_wcs; pathname = archive_entry_pathname(entry); if (pathname == NULL) { free(f); @@ -1515,6 +1516,7 @@ time_excluded(struct archive_match *a, struct archive_entry *entry) pathname = archive_entry_pathname_w(entry); a->exclusion_tree.rbt_ops = &rb_ops_wcs; #else + (void)rb_ops_wcs; pathname = archive_entry_pathname(entry); a->exclusion_tree.rbt_ops = &rb_ops_mbs; #endif @@ -1675,13 +1677,16 @@ add_owner_id(struct archive_match *a, struct id_array *ids, int64_t id) unsigned i; if (ids->count + 1 >= ids->size) { + void *p; + if (ids->size == 0) ids->size = 8; else ids->size *= 2; - ids->ids = realloc(ids->ids, sizeof(*ids->ids) * ids->size); - if (ids->ids == NULL) + p = realloc(ids->ids, sizeof(*ids->ids) * ids->size); + if (p == NULL) return (error_nomem(a)); + ids->ids = (int64_t *)p; } /* Find an insert point. */ @@ -1709,7 +1714,7 @@ match_owner_id(struct id_array *ids, int64_t id) unsigned b, m, t; t = 0; - b = ids->count; + b = (unsigned)ids->count; while (t < b) { m = (t + b)>>1; if (ids->ids[m] == id) diff --git a/libarchive/archive_options.c b/libarchive/archive_options.c index 08a348fb3034..8af623931f01 100644 --- a/libarchive/archive_options.c +++ b/libarchive/archive_options.c @@ -87,6 +87,8 @@ _archive_set_either_option(struct archive *a, const char *m, const char *o, cons if (r2 == ARCHIVE_FATAL) return (ARCHIVE_FATAL); + if (r2 == ARCHIVE_WARN - 1) + return r1; return r1 > r2 ? r1 : r2; } @@ -94,7 +96,7 @@ int _archive_set_options(struct archive *a, const char *options, int magic, const char *fn, option_handler use_option) { - int allok = 1, anyok = 0, r; + int allok = 1, anyok = 0, ignore_mod_err = 0, r; char *data; const char *s, *mod, *opt, *val; @@ -111,6 +113,15 @@ _archive_set_options(struct archive *a, const char *options, mod = opt = val = NULL; parse_option(&s, &mod, &opt, &val); + if (mod == NULL && opt != NULL && + strcmp("__ignore_wrong_module_name__", opt) == 0) { + /* Ignore module name error */ + if (val != NULL) { + ignore_mod_err = 1; + anyok = 1; + } + continue; + } r = use_option(a, mod, opt, val); if (r == ARCHIVE_FATAL) { @@ -122,6 +133,8 @@ _archive_set_options(struct archive *a, const char *options, return (ARCHIVE_FAILED); } if (r == ARCHIVE_WARN - 1) { + if (ignore_mod_err) + continue; /* The module name is wrong. */ archive_set_error(a, ARCHIVE_ERRNO_MISC, "Unknown module name: `%s'", mod); diff --git a/libarchive/archive_ppmd7.c b/libarchive/archive_ppmd7.c index 13a258188a61..fe0b0318cc05 100644 --- a/libarchive/archive_ppmd7.c +++ b/libarchive/archive_ppmd7.c @@ -990,7 +990,7 @@ static void RangeEnc_ShiftLow(CPpmd7z_RangeEnc *p) p->Cache = (Byte)((UInt32)p->Low >> 24); } p->CacheSize++; - p->Low = (UInt32)p->Low << 8; + p->Low = ((UInt32)p->Low << 8) & 0xFFFFFFFF; } static void RangeEnc_Encode(CPpmd7z_RangeEnc *p, UInt32 start, UInt32 size, UInt32 total) diff --git a/libarchive/archive_private.h b/libarchive/archive_private.h index 470f50051e70..30d472fcda0f 100644 --- a/libarchive/archive_private.h +++ b/libarchive/archive_private.h @@ -134,6 +134,7 @@ int __archive_check_magic(struct archive *, unsigned int magic, void __archive_errx(int retvalue, const char *msg) __LA_DEAD; +void __archive_ensure_cloexec_flag(int fd); int __archive_mktemp(const char *tmpdir); int __archive_clean(struct archive *); diff --git a/libarchive/archive_rb.c b/libarchive/archive_rb.c index 70bf7e6d3379..5b5da2034497 100644 --- a/libarchive/archive_rb.c +++ b/libarchive/archive_rb.c @@ -237,6 +237,8 @@ __archive_rb_tree_reparent_nodes( struct archive_rb_node * const new_father = old_child; struct archive_rb_node * const new_child = old_father; + if (new_father == NULL) + return; /* * Exchange descendant linkages. */ @@ -552,6 +554,8 @@ __archive_rb_tree_removal_rebalance(struct archive_rb_tree *rbt, unsigned int other = which ^ RB_DIR_OTHER; struct archive_rb_node *brother = parent->rb_nodes[other]; + if (brother == NULL) + return;/* The tree may be broken. */ /* * For cases 1, 2a, and 2b, our brother's children must * be black and our father must be black @@ -573,6 +577,8 @@ __archive_rb_tree_removal_rebalance(struct archive_rb_tree *rbt, */ __archive_rb_tree_reparent_nodes(parent, other); brother = parent->rb_nodes[other]; + if (brother == NULL) + return;/* The tree may be broken. */ } else { /* * Both our parent and brother are black. @@ -656,6 +662,8 @@ __archive_rb_tree_removal_rebalance(struct archive_rb_tree *rbt, * If we had two red nephews, then after the swap, * our former father would have a red grandson. */ + if (brother->rb_nodes[other] == NULL) + return;/* The tree may be broken. */ RB_MARK_BLACK(brother->rb_nodes[other]); __archive_rb_tree_reparent_nodes(parent, other); break; /* We're done! */ diff --git a/libarchive/archive_read.c b/libarchive/archive_read.c index e99906f825ae..048c316c52d4 100644 --- a/libarchive/archive_read.c +++ b/libarchive/archive_read.c @@ -57,8 +57,6 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read.c 201157 2009-12-29 05:30:2 static int choose_filters(struct archive_read *); static int choose_format(struct archive_read *); -static void free_filters(struct archive_read *); -static int close_filters(struct archive_read *); static struct archive_vtable *archive_read_vtable(void); static int64_t _archive_filter_bytes(struct archive *, int); static int _archive_filter_code(struct archive *, int); @@ -194,8 +192,8 @@ client_skip_proxy(struct archive_read_filter *self, int64_t request) int64_t get, ask = request; if (ask > skip_limit) ask = skip_limit; - get = (self->archive->client.skipper)(&self->archive->archive, - self->data, ask); + get = (self->archive->client.skipper) + (&self->archive->archive, self->data, ask); if (get == 0) return (total); request -= get; @@ -215,8 +213,8 @@ client_skip_proxy(struct archive_read_filter *self, int64_t request) * only do this for skips of over 64k. */ int64_t before = self->position; - int64_t after = (self->archive->client.seeker)(&self->archive->archive, - self->data, request, SEEK_CUR); + int64_t after = (self->archive->client.seeker) + (&self->archive->archive, self->data, request, SEEK_CUR); if (after != before + request) return ARCHIVE_FATAL; return after - before; @@ -241,14 +239,64 @@ client_seek_proxy(struct archive_read_filter *self, int64_t offset, int whence) static int client_close_proxy(struct archive_read_filter *self) { - int r = ARCHIVE_OK; + int r = ARCHIVE_OK, r2; + unsigned int i; + + if (self->archive->client.closer == NULL) + return (r); + for (i = 0; i < self->archive->client.nodes; i++) + { + r2 = (self->archive->client.closer) + ((struct archive *)self->archive, + self->archive->client.dataset[i].data); + if (r > r2) + r = r2; + } + return (r); +} - if (self->archive->client.closer != NULL) - r = (self->archive->client.closer)((struct archive *)self->archive, - self->data); +static int +client_open_proxy(struct archive_read_filter *self) +{ + int r = ARCHIVE_OK; + if (self->archive->client.opener != NULL) + r = (self->archive->client.opener)( + (struct archive *)self->archive, self->data); return (r); } +static int +client_switch_proxy(struct archive_read_filter *self, unsigned int iindex) +{ + int r1 = ARCHIVE_OK, r2 = ARCHIVE_OK; + void *data2 = NULL; + + /* Don't do anything if already in the specified data node */ + if (self->archive->client.cursor == iindex) + return (ARCHIVE_OK); + + self->archive->client.cursor = iindex; + data2 = self->archive->client.dataset[self->archive->client.cursor].data; + if (self->archive->client.switcher != NULL) + { + r1 = r2 = (self->archive->client.switcher) + ((struct archive *)self->archive, self->data, data2); + self->data = data2; + } + else + { + /* Attempt to call close and open instead */ + if (self->archive->client.closer != NULL) + r1 = (self->archive->client.closer) + ((struct archive *)self->archive, self->data); + self->data = data2; + if (self->archive->client.opener != NULL) + r2 = (self->archive->client.opener) + ((struct archive *)self->archive, self->data); + } + return (r1 < r2) ? r1 : r2; +} + int archive_read_set_open_callback(struct archive *_a, archive_open_callback *client_opener) @@ -305,21 +353,109 @@ archive_read_set_close_callback(struct archive *_a, } int +archive_read_set_switch_callback(struct archive *_a, + archive_switch_callback *client_switcher) +{ + struct archive_read *a = (struct archive_read *)_a; + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "archive_read_set_switch_callback"); + a->client.switcher = client_switcher; + return ARCHIVE_OK; +} + +int archive_read_set_callback_data(struct archive *_a, void *client_data) { + return archive_read_set_callback_data2(_a, client_data, 0); +} + +int +archive_read_set_callback_data2(struct archive *_a, void *client_data, + unsigned int iindex) +{ struct archive_read *a = (struct archive_read *)_a; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, - "archive_read_set_callback_data"); - a->client.data = client_data; + "archive_read_set_callback_data2"); + + if (a->client.nodes == 0) + { + a->client.dataset = (struct archive_read_data_node *) + calloc(1, sizeof(*a->client.dataset)); + if (a->client.dataset == NULL) + { + archive_set_error(&a->archive, ENOMEM, + "No memory."); + return ARCHIVE_FATAL; + } + a->client.nodes = 1; + } + + if (iindex > a->client.nodes - 1) + { + archive_set_error(&a->archive, EINVAL, + "Invalid index specified."); + return ARCHIVE_FATAL; + } + a->client.dataset[iindex].data = client_data; + a->client.dataset[iindex].begin_position = -1; + a->client.dataset[iindex].total_size = -1; + return ARCHIVE_OK; +} + +int +archive_read_add_callback_data(struct archive *_a, void *client_data, + unsigned int iindex) +{ + struct archive_read *a = (struct archive_read *)_a; + void *p; + unsigned int i; + + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, + "archive_read_add_callback_data"); + if (iindex > a->client.nodes) { + archive_set_error(&a->archive, EINVAL, + "Invalid index specified."); + return ARCHIVE_FATAL; + } + p = realloc(a->client.dataset, sizeof(*a->client.dataset) + * (++(a->client.nodes))); + if (p == NULL) { + archive_set_error(&a->archive, ENOMEM, + "No memory."); + return ARCHIVE_FATAL; + } + a->client.dataset = (struct archive_read_data_node *)p; + for (i = a->client.nodes - 1; i > iindex && i > 0; i--) { + a->client.dataset[i].data = a->client.dataset[i-1].data; + a->client.dataset[i].begin_position = -1; + a->client.dataset[i].total_size = -1; + } + a->client.dataset[iindex].data = client_data; + a->client.dataset[iindex].begin_position = -1; + a->client.dataset[iindex].total_size = -1; return ARCHIVE_OK; } int +archive_read_append_callback_data(struct archive *_a, void *client_data) +{ + struct archive_read *a = (struct archive_read *)_a; + return archive_read_add_callback_data(_a, client_data, a->client.nodes); +} + +int +archive_read_prepend_callback_data(struct archive *_a, void *client_data) +{ + return archive_read_add_callback_data(_a, client_data, 0); +} + +int archive_read_open1(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - struct archive_read_filter *filter; + struct archive_read_filter *filter, *tmp; int slot, e; + unsigned int i; archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_open"); @@ -334,11 +470,14 @@ archive_read_open1(struct archive *_a) /* Open data source. */ if (a->client.opener != NULL) { - e =(a->client.opener)(&a->archive, a->client.data); + e = (a->client.opener)(&a->archive, a->client.dataset[0].data); if (e != 0) { /* If the open failed, call the closer to clean up. */ - if (a->client.closer) - (a->client.closer)(&a->archive, a->client.data); + if (a->client.closer) { + for (i = 0; i < a->client.nodes; i++) + (a->client.closer)(&a->archive, + a->client.dataset[i].data); + } return (e); } } @@ -349,31 +488,51 @@ archive_read_open1(struct archive *_a) filter->bidder = NULL; filter->upstream = NULL; filter->archive = a; - filter->data = a->client.data; + filter->data = a->client.dataset[0].data; + filter->open = client_open_proxy; filter->read = client_read_proxy; filter->skip = client_skip_proxy; filter->seek = client_seek_proxy; filter->close = client_close_proxy; + filter->sswitch = client_switch_proxy; filter->name = "none"; - filter->code = ARCHIVE_COMPRESSION_NONE; - a->filter = filter; + filter->code = ARCHIVE_FILTER_NONE; - /* Build out the input pipeline. */ - e = choose_filters(a); - if (e < ARCHIVE_WARN) { - a->archive.state = ARCHIVE_STATE_FATAL; - return (ARCHIVE_FATAL); + a->client.dataset[0].begin_position = 0; + if (!a->filter || !a->bypass_filter_bidding) + { + a->filter = filter; + /* Build out the input pipeline. */ + e = choose_filters(a); + if (e < ARCHIVE_WARN) { + a->archive.state = ARCHIVE_STATE_FATAL; + return (ARCHIVE_FATAL); + } + } + else + { + /* Need to add "NONE" type filter at the end of the filter chain */ + tmp = a->filter; + while (tmp->upstream) + tmp = tmp->upstream; + tmp->upstream = filter; } - slot = choose_format(a); - if (slot < 0) { - close_filters(a); - a->archive.state = ARCHIVE_STATE_FATAL; - return (ARCHIVE_FATAL); + if (!a->format) + { + slot = choose_format(a); + if (slot < 0) { + __archive_read_close_filters(a); + a->archive.state = ARCHIVE_STATE_FATAL; + return (ARCHIVE_FATAL); + } + a->format = &(a->formats[slot]); } - a->format = &(a->formats[slot]); a->archive.state = ARCHIVE_STATE_HEADER; + + /* Ensure libarchive starts from the first node in a multivolume set */ + client_switch_proxy(a->filter, 0); return (e); } @@ -413,8 +572,8 @@ choose_filters(struct archive_read *a) /* Verify the filter by asking it for some data. */ __archive_read_filter_ahead(a->filter, 1, &avail); if (avail < 0) { - close_filters(a); - free_filters(a); + __archive_read_close_filters(a); + __archive_read_free_filters(a); return (ARCHIVE_FATAL); } a->archive.compression_name = a->filter->name; @@ -432,8 +591,8 @@ choose_filters(struct archive_read *a) a->filter = filter; r = (best_bidder->init)(a->filter); if (r != ARCHIVE_OK) { - close_filters(a); - free_filters(a); + __archive_read_close_filters(a); + __archive_read_free_filters(a); return (ARCHIVE_FATAL); } } @@ -501,6 +660,9 @@ _archive_read_next_header2(struct archive *_a, struct archive_entry *entry) a->read_data_output_offset = 0; a->read_data_remaining = 0; + a->read_data_is_posix_read = 0; + a->read_data_requested = 0; + a->data_start_node = a->client.cursor; /* EOF always wins; otherwise return the worst error. */ return (r2 < r1 || r2 == ARCHIVE_EOF) ? r2 : r1; } @@ -611,6 +773,8 @@ archive_read_data(struct archive *_a, void *buff, size_t s) while (s > 0) { if (a->read_data_remaining == 0) { read_buf = a->read_data_block; + a->read_data_is_posix_read = 1; + a->read_data_requested = s; r = _archive_read_data_block(&a->archive, &read_buf, &a->read_data_remaining, &a->read_data_offset); a->read_data_block = read_buf; @@ -664,6 +828,8 @@ archive_read_data(struct archive *_a, void *buff, size_t s) bytes_read += len; } } + a->read_data_is_posix_read = 0; + a->read_data_requested = 0; return (bytes_read); } @@ -698,6 +864,23 @@ archive_read_data_skip(struct archive *_a) return (r); } +int64_t +archive_seek_data(struct archive *_a, int64_t offset, int whence) +{ + struct archive_read *a = (struct archive_read *)_a; + archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, + "archive_seek_data_block"); + + if (a->format->seek_data == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Internal error: " + "No format_seek_data_block function registered"); + return (ARCHIVE_FATAL); + } + + return (a->format->seek_data)(a, offset, whence); +} + /* * Read the next block of entry data from the archive. * This is a zero-copy interface; the client receives a pointer, @@ -724,8 +907,8 @@ _archive_read_data_block(struct archive *_a, return (a->format->read_data)(a, buff, size, offset); } -static int -close_filters(struct archive_read *a) +int +__archive_read_close_filters(struct archive_read *a) { struct archive_read_filter *f = a->filter; int r = ARCHIVE_OK; @@ -745,8 +928,8 @@ close_filters(struct archive_read *a) return r; } -static void -free_filters(struct archive_read *a) +void +__archive_read_free_filters(struct archive_read *a) { while (a->filter != NULL) { struct archive_read_filter *t = a->filter->upstream; @@ -790,7 +973,7 @@ _archive_read_close(struct archive *_a) /* TODO: Clean up the formatters. */ /* Release the filter objects. */ - r1 = close_filters(a); + r1 = __archive_read_close_filters(a); if (r1 < r) r = r1; @@ -829,7 +1012,7 @@ _archive_read_free(struct archive *_a) } /* Free the filters */ - free_filters(a); + __archive_read_free_filters(a); /* Release the bidder objects. */ n = sizeof(a->bidders)/sizeof(a->bidders[0]); @@ -846,6 +1029,7 @@ _archive_read_free(struct archive *_a) archive_entry_free(a->entry); a->archive.magic = 0; __archive_clean(&a->archive); + free(a->client.dataset); free(a); return (r); } @@ -855,7 +1039,8 @@ get_filter(struct archive *_a, int n) { struct archive_read *a = (struct archive_read *)_a; struct archive_read_filter *f = a->filter; - /* We use n == -1 for 'the last filter', which is always the client proxy. */ + /* We use n == -1 for 'the last filter', which is always the + * client proxy. */ if (n == -1 && f != NULL) { struct archive_read_filter *last = f; f = f->upstream; @@ -908,6 +1093,7 @@ __archive_read_register_format(struct archive_read *a, int (*read_header)(struct archive_read *, struct archive_entry *), int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *), int (*read_data_skip)(struct archive_read *), + int64_t (*seek_data)(struct archive_read *, int64_t, int), int (*cleanup)(struct archive_read *)) { int i, number_slots; @@ -927,6 +1113,7 @@ __archive_read_register_format(struct archive_read *a, a->formats[i].read_header = read_header; a->formats[i].read_data = read_data; a->formats[i].read_data_skip = read_data_skip; + a->formats[i].seek_data = seek_data; a->formats[i].cleanup = cleanup; a->formats[i].data = format_data; a->formats[i].name = name; @@ -1073,7 +1260,8 @@ __archive_read_filter_ahead(struct archive_read_filter *filter, if (filter->next > filter->buffer && filter->next + min > filter->buffer + filter->buffer_size) { if (filter->avail > 0) - memmove(filter->buffer, filter->next, filter->avail); + memmove(filter->buffer, filter->next, + filter->avail); filter->next = filter->buffer; } @@ -1088,15 +1276,26 @@ __archive_read_filter_ahead(struct archive_read_filter *filter, &filter->client_buff); if (bytes_read < 0) { /* Read error. */ filter->client_total = filter->client_avail = 0; - filter->client_next = filter->client_buff = NULL; + filter->client_next = + filter->client_buff = NULL; filter->fatal = 1; if (avail != NULL) *avail = ARCHIVE_FATAL; return (NULL); } - if (bytes_read == 0) { /* Premature end-of-file. */ + if (bytes_read == 0) { + /* Check for another client object first */ + if (filter->archive->client.cursor != + filter->archive->client.nodes - 1) { + if (client_switch_proxy(filter, + filter->archive->client.cursor + 1) + == ARCHIVE_OK) + continue; + } + /* Premature end-of-file. */ filter->client_total = filter->client_avail = 0; - filter->client_next = filter->client_buff = NULL; + filter->client_next = + filter->client_buff = NULL; filter->end_of_file = 1; /* Return whatever we do have. */ if (avail != NULL) @@ -1106,9 +1305,7 @@ __archive_read_filter_ahead(struct archive_read_filter *filter, filter->client_total = bytes_read; filter->client_avail = filter->client_total; filter->client_next = filter->client_buff; - } - else - { + } else { /* * We can't satisfy the request from the copy * buffer or the existing client data, so we @@ -1129,9 +1326,10 @@ __archive_read_filter_ahead(struct archive_read_filter *filter, t *= 2; if (t <= s) { /* Integer overflow! */ archive_set_error( - &filter->archive->archive, - ENOMEM, - "Unable to allocate copy buffer"); + &filter->archive->archive, + ENOMEM, + "Unable to allocate copy" + " buffer"); filter->fatal = 1; if (avail != NULL) *avail = ARCHIVE_FATAL; @@ -1170,8 +1368,8 @@ __archive_read_filter_ahead(struct archive_read_filter *filter, if (tocopy > filter->client_avail) tocopy = filter->client_avail; - memcpy(filter->next + filter->avail, filter->client_next, - tocopy); + memcpy(filter->next + filter->avail, + filter->client_next, tocopy); /* Remove this data from client buffer. */ filter->client_next += tocopy; filter->client_avail -= tocopy; @@ -1274,6 +1472,13 @@ advance_file_pointer(struct archive_read_filter *filter, int64_t request) } if (bytes_read == 0) { + if (filter->archive->client.cursor != + filter->archive->client.nodes - 1) { + if (client_switch_proxy(filter, + filter->archive->client.cursor + 1) + == ARCHIVE_OK) + continue; + } filter->client_buff = NULL; filter->end_of_file = 1; return (total_bytes_skipped); @@ -1305,15 +1510,109 @@ __archive_read_seek(struct archive_read *a, int64_t offset, int whence) } int64_t -__archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset, int whence) +__archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset, + int whence) { + struct archive_read_client *client; int64_t r; + unsigned int cursor; if (filter->closed || filter->fatal) return (ARCHIVE_FATAL); if (filter->seek == NULL) return (ARCHIVE_FAILED); - r = filter->seek(filter, offset, whence); + + client = &(filter->archive->client); + switch (whence) { + case SEEK_CUR: + /* Adjust the offset and use SEEK_SET instead */ + offset += filter->position; + case SEEK_SET: + cursor = 0; + while (1) + { + if (client->dataset[cursor].begin_position < 0 || + client->dataset[cursor].total_size < 0 || + client->dataset[cursor].begin_position + + client->dataset[cursor].total_size - 1 > offset || + cursor + 1 >= client->nodes) + break; + r = client->dataset[cursor].begin_position + + client->dataset[cursor].total_size; + client->dataset[++cursor].begin_position = r; + } + while (1) { + r = client_switch_proxy(filter, cursor); + if (r != ARCHIVE_OK) + return r; + if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0) + return r; + client->dataset[cursor].total_size = r; + if (client->dataset[cursor].begin_position + + client->dataset[cursor].total_size - 1 > offset || + cursor + 1 >= client->nodes) + break; + r = client->dataset[cursor].begin_position + + client->dataset[cursor].total_size; + client->dataset[++cursor].begin_position = r; + } + offset -= client->dataset[cursor].begin_position; + if (offset < 0) + offset = 0; + else if (offset > client->dataset[cursor].total_size - 1) + offset = client->dataset[cursor].total_size - 1; + if ((r = client_seek_proxy(filter, offset, SEEK_SET)) < 0) + return r; + break; + + case SEEK_END: + cursor = 0; + while (1) { + if (client->dataset[cursor].begin_position < 0 || + client->dataset[cursor].total_size < 0 || + cursor + 1 >= client->nodes) + break; + r = client->dataset[cursor].begin_position + + client->dataset[cursor].total_size; + client->dataset[++cursor].begin_position = r; + } + while (1) { + r = client_switch_proxy(filter, cursor); + if (r != ARCHIVE_OK) + return r; + if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0) + return r; + client->dataset[cursor].total_size = r; + r = client->dataset[cursor].begin_position + + client->dataset[cursor].total_size; + if (cursor + 1 >= client->nodes) + break; + client->dataset[++cursor].begin_position = r; + } + while (1) { + if (r + offset >= + client->dataset[cursor].begin_position) + break; + offset += client->dataset[cursor].total_size; + if (cursor == 0) + break; + cursor--; + r = client->dataset[cursor].begin_position + + client->dataset[cursor].total_size; + } + offset = (r + offset) - client->dataset[cursor].begin_position; + if ((r = client_switch_proxy(filter, cursor)) != ARCHIVE_OK) + return r; + r = client_seek_proxy(filter, offset, SEEK_SET); + if (r < ARCHIVE_OK) + return r; + break; + + default: + return (ARCHIVE_FATAL); + } + r += client->dataset[cursor].begin_position; + if (r >= 0) { /* * Ouch. Clearing the buffer like this hurts, especially diff --git a/libarchive/archive_read_append_filter.c b/libarchive/archive_read_append_filter.c new file mode 100644 index 000000000000..017d7c68a4b3 --- /dev/null +++ b/libarchive/archive_read_append_filter.c @@ -0,0 +1,198 @@ +/*- + * Copyright (c) 2003-2012 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_ERRNO_H +#include <errno.h> +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_read_private.h" + +int +archive_read_append_filter(struct archive *_a, int code) +{ + int r1, r2, number_bidders, i; + char str[20]; + struct archive_read_filter_bidder *bidder; + struct archive_read_filter *filter; + struct archive_read *a = (struct archive_read *)_a; + + r1 = r2 = (ARCHIVE_OK); + switch (code) + { + case ARCHIVE_FILTER_NONE: + /* No filter to add, so do nothing. + * NOTE: An initial "NONE" type filter is always set at the end of the + * filter chain. + */ + r1 = (ARCHIVE_OK); + break; + case ARCHIVE_FILTER_GZIP: + strcpy(str, "gzip"); + r1 = archive_read_support_filter_gzip(_a); + break; + case ARCHIVE_FILTER_BZIP2: + strcpy(str, "bzip2"); + r1 = archive_read_support_filter_bzip2(_a); + break; + case ARCHIVE_FILTER_COMPRESS: + strcpy(str, "compress (.Z)"); + r1 = archive_read_support_filter_compress(_a); + break; + case ARCHIVE_FILTER_PROGRAM: + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Cannot append program filter using archive_read_append_filter"); + return (ARCHIVE_FATAL); + case ARCHIVE_FILTER_LZMA: + strcpy(str, "lzma"); + r1 = archive_read_support_filter_lzma(_a); + break; + case ARCHIVE_FILTER_XZ: + strcpy(str, "xz"); + r1 = archive_read_support_filter_xz(_a); + break; + case ARCHIVE_FILTER_UU: + strcpy(str, "uu"); + r1 = archive_read_support_filter_uu(_a); + break; + case ARCHIVE_FILTER_RPM: + strcpy(str, "rpm"); + r1 = archive_read_support_filter_rpm(_a); + break; + case ARCHIVE_FILTER_LZIP: + strcpy(str, "lzip"); + r1 = archive_read_support_filter_lzip(_a); + break; + case ARCHIVE_FILTER_LRZIP: + strcpy(str, "lrzip"); + r1 = archive_read_support_filter_lrzip(_a); + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Invalid filter code specified"); + return (ARCHIVE_FATAL); + } + + if (code != ARCHIVE_FILTER_NONE) + { + number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]); + + bidder = a->bidders; + for (i = 0; i < number_bidders; i++, bidder++) + { + if (!bidder->name || !strcmp(bidder->name, str)) + break; + } + if (!bidder->name || strcmp(bidder->name, str)) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Internal error: Unable to append filter"); + return (ARCHIVE_FATAL); + } + + filter + = (struct archive_read_filter *)calloc(1, sizeof(*filter)); + if (filter == NULL) + { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } + filter->bidder = bidder; + filter->archive = a; + filter->upstream = a->filter; + a->filter = filter; + r2 = (bidder->init)(a->filter); + if (r2 != ARCHIVE_OK) { + __archive_read_close_filters(a); + __archive_read_free_filters(a); + return (ARCHIVE_FATAL); + } + } + + a->bypass_filter_bidding = 1; + return (r1 < r2) ? r1 : r2; +} + +int +archive_read_append_filter_program(struct archive *_a, const char *cmd) +{ + return (archive_read_append_filter_program_signature(_a, cmd, NULL, 0)); +} + +int +archive_read_append_filter_program_signature(struct archive *_a, + const char *cmd, const void *signature, size_t signature_len) +{ + int r, number_bidders, i; + struct archive_read_filter_bidder *bidder; + struct archive_read_filter *filter; + struct archive_read *a = (struct archive_read *)_a; + + if (archive_read_support_filter_program_signature(_a, cmd, signature, + signature_len) != (ARCHIVE_OK)) + return (ARCHIVE_FATAL); + + number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]); + + bidder = a->bidders; + for (i = 0; i < number_bidders; i++, bidder++) + { + /* Program bidder name set to filter name after initialization */ + if (bidder->data && !bidder->name) + break; + } + if (!bidder->data) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Internal error: Unable to append program filter"); + return (ARCHIVE_FATAL); + } + + filter + = (struct archive_read_filter *)calloc(1, sizeof(*filter)); + if (filter == NULL) + { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } + filter->bidder = bidder; + filter->archive = a; + filter->upstream = a->filter; + a->filter = filter; + r = (bidder->init)(a->filter); + if (r != ARCHIVE_OK) { + __archive_read_close_filters(a); + __archive_read_free_filters(a); + return (ARCHIVE_FATAL); + } + bidder->name = a->filter->name; + + a->bypass_filter_bidding = 1; + return r; +} diff --git a/libarchive/archive_read_disk_entry_from_file.c b/libarchive/archive_read_disk_entry_from_file.c index a43c0adf9c2a..e984aaadbeab 100644 --- a/libarchive/archive_read_disk_entry_from_file.c +++ b/libarchive/archive_read_disk_entry_from_file.c @@ -49,8 +49,10 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010 #ifdef HAVE_SYS_STAT_H #include <sys/stat.h> #endif -#ifdef HAVE_SYS_XATTR_H +#if defined(HAVE_SYS_XATTR_H) #include <sys/xattr.h> +#elif defined(HAVE_ATTR_XATTR_H) +#include <attr/xattr.h> #endif #ifdef HAVE_SYS_EA_H #include <sys/ea.h> @@ -58,9 +60,6 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010 #ifdef HAVE_ACL_LIBACL_H #include <acl/libacl.h> #endif -#ifdef HAVE_ATTR_XATTR_H -#include <attr/xattr.h> -#endif #ifdef HAVE_COPYFILE_H #include <copyfile.h> #endif @@ -104,6 +103,10 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010 #include "archive_private.h" #include "archive_read_disk_private.h" +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + /* * Linux and FreeBSD plug this obvious hole in POSIX.1e in * different ways. @@ -114,7 +117,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010 #define ACL_GET_PERM acl_get_perm_np #endif -static int setup_acls_posix1e(struct archive_read_disk *, +static int setup_acls(struct archive_read_disk *, struct archive_entry *, int *fd); static int setup_mac_metadata(struct archive_read_disk *, struct archive_entry *, int *fd); @@ -168,15 +171,16 @@ archive_read_disk_entry_from_file(struct archive *_a, st = &s; } archive_entry_copy_stat(entry, st); - /* Lookup uname/gname */ - name = archive_read_disk_uname(_a, archive_entry_uid(entry)); - if (name != NULL) - archive_entry_copy_uname(entry, name); - name = archive_read_disk_gname(_a, archive_entry_gid(entry)); - if (name != NULL) - archive_entry_copy_gname(entry, name); } + /* Lookup uname/gname */ + name = archive_read_disk_uname(_a, archive_entry_uid(entry)); + if (name != NULL) + archive_entry_copy_uname(entry, name); + name = archive_read_disk_gname(_a, archive_entry_gid(entry)); + if (name != NULL) + archive_entry_copy_gname(entry, name); + #ifdef HAVE_STRUCT_STAT_ST_FLAGS /* On FreeBSD, we get flags for free with the stat. */ /* TODO: Does this belong in copy_stat()? */ @@ -192,12 +196,14 @@ archive_read_disk_entry_from_file(struct archive *_a, if (fd < 0) { if (a->tree != NULL) fd = a->open_on_current_dir(a->tree, path, - O_RDONLY | O_NONBLOCK); + O_RDONLY | O_NONBLOCK | O_CLOEXEC); else - fd = open(path, O_RDONLY | O_NONBLOCK); + fd = open(path, O_RDONLY | O_NONBLOCK | + O_CLOEXEC); + __archive_ensure_cloexec_flag(fd); } if (fd >= 0) { - unsigned long stflags; + int stflags; r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags); if (r == 0 && stflags != 0) archive_entry_set_fflags(entry, stflags, 0); @@ -244,7 +250,7 @@ archive_read_disk_entry_from_file(struct archive *_a, } #endif /* HAVE_READLINK || HAVE_READLINKAT */ - r = setup_acls_posix1e(a, entry, &fd); + r = setup_acls(a, entry, &fd); r1 = setup_xattrs(a, entry, &fd); if (r1 < r) r = r1; @@ -285,9 +291,10 @@ setup_mac_metadata(struct archive_read_disk *a, int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR; struct stat copyfile_stat; int ret = ARCHIVE_OK; - void *buff; + void *buff = NULL; int have_attrs; - const char *name, *tempdir, *tempfile = NULL; + const char *name, *tempdir; + struct archive_string tempfile; (void)fd; /* UNUSED */ name = archive_entry_sourcepath(entry); @@ -322,25 +329,28 @@ setup_mac_metadata(struct archive_read_disk *a, tempdir = getenv("TMPDIR"); if (tempdir == NULL) tempdir = _PATH_TMP; - tempfile = tempnam(tempdir, "tar.md."); + archive_string_init(&tempfile); + archive_strcpy(&tempfile, tempdir); + archive_strcat(&tempfile, "tar.md.XXXXXX"); + tempfd = mkstemp(tempfile.s); + if (tempfd < 0) { + archive_set_error(&a->archive, errno, + "Could not open extended attribute file"); + ret = ARCHIVE_WARN; + goto cleanup; + } + __archive_ensure_cloexec_flag(tempfd); /* XXX I wish copyfile() could pack directly to a memory * buffer; that would avoid the temp file here. For that * matter, it would be nice if fcopyfile() actually worked, * that would reduce the many open/close races here. */ - if (copyfile(name, tempfile, 0, copyfile_flags | COPYFILE_PACK)) { + if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) { archive_set_error(&a->archive, errno, "Could not pack extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } - tempfd = open(tempfile, O_RDONLY); - if (tempfd < 0) { - archive_set_error(&a->archive, errno, - "Could not open extended attribute file"); - ret = ARCHIVE_WARN; - goto cleanup; - } if (fstat(tempfd, ©file_stat)) { archive_set_error(&a->archive, errno, "Could not check size of extended attributes"); @@ -363,10 +373,12 @@ setup_mac_metadata(struct archive_read_disk *a, archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size); cleanup: - if (tempfd >= 0) + if (tempfd >= 0) { close(tempfd); - if (tempfile != NULL) - unlink(tempfile); + unlink(tempfile.s); + } + archive_string_free(&tempfile); + free(buff); return (ret); } @@ -387,16 +399,19 @@ setup_mac_metadata(struct archive_read_disk *a, #endif -#ifdef HAVE_POSIX_ACL -static void setup_acl_posix1e(struct archive_read_disk *a, +#if defined(HAVE_POSIX_ACL) && defined(ACL_TYPE_NFS4) +static int translate_acl(struct archive_read_disk *a, struct archive_entry *entry, acl_t acl, int archive_entry_acl_type); static int -setup_acls_posix1e(struct archive_read_disk *a, +setup_acls(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { const char *accpath; acl_t acl; +#if HAVE_ACL_IS_TRIVIAL_NP + int r; +#endif accpath = archive_entry_sourcepath(entry); if (accpath == NULL) @@ -404,15 +419,33 @@ setup_acls_posix1e(struct archive_read_disk *a, archive_entry_acl_clear(entry); - if (*fd < 0 && a->tree != NULL && - (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK)){ - *fd = a->open_on_current_dir(a->tree, accpath, - O_RDONLY | O_NONBLOCK); - if (*fd < 0) { - archive_set_error(&a->archive, errno, - "Couldn't access %s", accpath); - return (ARCHIVE_FAILED); - } + /* Try NFS4 ACL first. */ + if (*fd >= 0) + acl = acl_get_fd(*fd); +#if HAVE_ACL_GET_LINK_NP + else if (!a->follow_symlinks) + acl = acl_get_link_np(accpath, ACL_TYPE_NFS4); +#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_NFS4); +#if HAVE_ACL_IS_TRIVIAL_NP + /* Ignore "trivial" ACLs that just mirror the file mode. */ + acl_is_trivial_np(acl, &r); + if (r) { + acl_free(acl); + acl = NULL; + } +#endif + if (acl != NULL) { + translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4); + acl_free(acl); + return (ARCHIVE_OK); } /* Retrieve access ACL from file. */ @@ -431,7 +464,7 @@ setup_acls_posix1e(struct archive_read_disk *a, else acl = acl_get_file(accpath, ACL_TYPE_ACCESS); if (acl != NULL) { - setup_acl_posix1e(a, entry, acl, + translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); acl_free(acl); } @@ -440,7 +473,7 @@ setup_acls_posix1e(struct archive_read_disk *a, if (S_ISDIR(archive_entry_mode(entry))) { acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); if (acl != NULL) { - setup_acl_posix1e(a, entry, acl, + translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); acl_free(acl); } @@ -449,68 +482,180 @@ setup_acls_posix1e(struct archive_read_disk *a, } /* - * Translate POSIX.1e ACL into libarchive internal structure. + * Translate system ACL into libarchive internal structure. */ -static void -setup_acl_posix1e(struct archive_read_disk *a, - struct archive_entry *entry, acl_t acl, int archive_entry_acl_type) + +static struct { + int archive_perm; + int platform_perm; +} acl_perm_map[] = { + {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, + {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE}, + {ARCHIVE_ENTRY_ACL_READ, ACL_READ}, + {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 struct { + int archive_inherit; + int platform_inherit; +} acl_inherit_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} +}; + +static int +translate_acl(struct archive_read_disk *a, + struct archive_entry *entry, acl_t acl, int default_entry_acl_type) { acl_tag_t acl_tag; + acl_entry_type_t acl_type; + acl_flagset_t acl_flagset; acl_entry_t acl_entry; acl_permset_t acl_permset; + int brand, i, r, entry_acl_type; int s, ae_id, ae_tag, ae_perm; const char *ae_name; + + // 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. + acl_get_brand_np(acl, &brand); + 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: + // XXX set warning message? + return ARCHIVE_FAILED; + } + break; + case ACL_BRAND_NFS4: + if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) { + // XXX set warning message? + return ARCHIVE_FAILED; + } + break; + default: + // XXX set warning message? + return ARCHIVE_FAILED; + break; + } + + s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry); while (s == 1) { ae_id = -1; ae_name = NULL; + ae_perm = 0; acl_get_tag_type(acl_entry, &acl_tag); - if (acl_tag == ACL_USER) { + switch (acl_tag) { + case ACL_USER: ae_id = (int)*(uid_t *)acl_get_qualifier(acl_entry); ae_name = archive_read_disk_uname(&a->archive, ae_id); ae_tag = ARCHIVE_ENTRY_ACL_USER; - } else if (acl_tag == ACL_GROUP) { + break; + case ACL_GROUP: ae_id = (int)*(gid_t *)acl_get_qualifier(acl_entry); ae_name = archive_read_disk_gname(&a->archive, ae_id); ae_tag = ARCHIVE_ENTRY_ACL_GROUP; - } else if (acl_tag == ACL_MASK) { + break; + case ACL_MASK: ae_tag = ARCHIVE_ENTRY_ACL_MASK; - } else if (acl_tag == ACL_USER_OBJ) { + break; + case ACL_USER_OBJ: ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ; - } else if (acl_tag == ACL_GROUP_OBJ) { + break; + case ACL_GROUP_OBJ: ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ; - } else if (acl_tag == ACL_OTHER) { + break; + case ACL_OTHER: ae_tag = ARCHIVE_ENTRY_ACL_OTHER; - } else { + break; + case ACL_EVERYONE: + ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE; + break; + default: /* Skip types that libarchive can't support. */ + s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry); continue; } - acl_get_permset(acl_entry, &acl_permset); - ae_perm = 0; + // XXX acl type maps to allow/deny/audit/YYYY bits + // XXX acl_get_entry_type_np on FreeBSD returns EINVAL for + // non-NFSv4 ACLs + entry_acl_type = default_entry_acl_type; + r = acl_get_entry_type_np(acl_entry, &acl_type); + if (r == 0) { + 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; + } + } + /* - * acl_get_perm() is spelled differently on different - * platforms; see above. + * Libarchive stores "flag" (NFSv4 inheritance bits) + * in the ae_perm bitmap. */ - if (ACL_GET_PERM(acl_permset, ACL_EXECUTE)) - ae_perm |= ARCHIVE_ENTRY_ACL_EXECUTE; - if (ACL_GET_PERM(acl_permset, ACL_READ)) - ae_perm |= ARCHIVE_ENTRY_ACL_READ; - if (ACL_GET_PERM(acl_permset, ACL_WRITE)) - ae_perm |= ARCHIVE_ENTRY_ACL_WRITE; + acl_get_flagset_np(acl_entry, &acl_flagset); + for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { + if (acl_get_flag_np(acl_flagset, + acl_inherit_map[i].platform_inherit)) + ae_perm |= acl_inherit_map[i].archive_inherit; - archive_entry_acl_add_entry(entry, - archive_entry_acl_type, ae_perm, ae_tag, - ae_id, ae_name); + } + + acl_get_permset(acl_entry, &acl_permset); + for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { + /* + * acl_get_perm() is spelled differently on different + * platforms; see above. + */ + if (ACL_GET_PERM(acl_permset, acl_perm_map[i].platform_perm)) + ae_perm |= acl_perm_map[i].archive_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); } + return (ARCHIVE_OK); } #else static int -setup_acls_posix1e(struct archive_read_disk *a, +setup_acls(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { (void)a; /* UNUSED */ @@ -901,16 +1046,19 @@ setup_sparse(struct archive_read_disk *a, path = archive_entry_pathname(entry); if (a->tree != NULL) *fd = a->open_on_current_dir(a->tree, path, - O_RDONLY | O_NONBLOCK); + O_RDONLY | O_NONBLOCK | O_CLOEXEC); else - *fd = open(path, O_RDONLY | O_NONBLOCK); + *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (*fd < 0) { archive_set_error(&a->archive, errno, "Can't open `%s'", path); return (ARCHIVE_FAILED); } + __archive_ensure_cloexec_flag(*fd); } + /* Initialize buffer to avoid the error valgrind complains about. */ + memset(buff, 0, sizeof(buff)); count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe); fm = (struct fiemap *)buff; fm->fm_start = 0; @@ -1012,12 +1160,13 @@ setup_sparse(struct archive_read_disk *a, if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0) return (ARCHIVE_OK); - *fd = open(path, O_RDONLY | O_NONBLOCK); + *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (*fd < 0) { archive_set_error(&a->archive, errno, "Can't open `%s'", path); return (ARCHIVE_FAILED); } + __archive_ensure_cloexec_flag(*fd); initial_off = 0; } diff --git a/libarchive/archive_read_disk_posix.c b/libarchive/archive_read_disk_posix.c index 698600e9b151..a13dbbf812cf 100644 --- a/libarchive/archive_read_disk_posix.c +++ b/libarchive/archive_read_disk_posix.c @@ -105,6 +105,9 @@ __FBSDID("$FreeBSD$"); #ifndef O_BINARY #define O_BINARY 0 #endif +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif /*- * This is a new directory-walking system that addresses a number @@ -361,6 +364,7 @@ static int setup_sparse(struct archive_read_disk *, struct archive_entry *); static int close_and_restore_time(int fd, struct tree *, struct restore_time *); static int open_on_current_dir(struct tree *, const char *, int); +static int tree_dup(int); static struct archive_vtable * @@ -717,7 +721,7 @@ _archive_read_data_block(struct archive *_a, const void **buff, * Open the current file. */ if (t->entry_fd < 0) { - int flags = O_RDONLY | O_BINARY; + int flags = O_RDONLY | O_BINARY | O_CLOEXEC; /* * Eliminate or reduce cache effects if we can. @@ -740,6 +744,7 @@ _archive_read_data_block(struct archive *_a, const void **buff, #endif t->entry_fd = open_on_current_dir(t, tree_current_access_path(t), flags); + __archive_ensure_cloexec_flag(t->entry_fd); #if defined(O_NOATIME) /* * When we did open the file with O_NOATIME flag, @@ -984,10 +989,12 @@ next_entry(struct archive_read_disk *a, struct tree *t, #elif defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) &&\ defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) if (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) { - unsigned long stflags; + int stflags; t->entry_fd = open_on_current_dir(t, - tree_current_access_path(t), O_RDONLY | O_NONBLOCK); + tree_current_access_path(t), + O_RDONLY | O_NONBLOCK | O_CLOEXEC); + __archive_ensure_cloexec_flag(t->entry_fd); if (t->entry_fd >= 0) { r = ioctl(t->entry_fd, EXT2_IOC_GETFLAGS, &stflags); @@ -1359,15 +1366,17 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) fid = t->max_filesystem_id++; if (t->max_filesystem_id > t->allocated_filesytem) { size_t s; + void *p; s = t->max_filesystem_id * 2; - t->filesystem_table = realloc(t->filesystem_table, - s * sizeof(*t->filesystem_table)); - if (t->filesystem_table == NULL) { + p = realloc(t->filesystem_table, + s * sizeof(*t->filesystem_table)); + if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); return (ARCHIVE_FATAL); } + t->filesystem_table = (struct filesystem *)p; t->allocated_filesytem = s; } t->current_filesystem_id = fid; @@ -1482,7 +1491,8 @@ setup_current_filesystem(struct archive_read_disk *a) * where current is. */ int fd = openat(tree_current_dir_fd(t), - tree_current_access_path(t), O_RDONLY); + tree_current_access_path(t), O_RDONLY | O_CLOEXEC); + __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); @@ -1660,7 +1670,8 @@ setup_current_filesystem(struct archive_read_disk *a) * where current is. */ int fd = openat(tree_current_dir_fd(t), - tree_current_access_path(t), O_RDONLY); + tree_current_access_path(t), O_RDONLY | O_CLOEXEC); + __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); @@ -1768,7 +1779,8 @@ setup_current_filesystem(struct archive_read_disk *a) * where current is. */ int fd = openat(tree_current_dir_fd(t), - tree_current_access_path(t), O_RDONLY); + tree_current_access_path(t), O_RDONLY | O_CLOEXEC); + __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); @@ -1889,7 +1901,8 @@ static int close_and_restore_time(int fd, struct tree *t, struct restore_time *rt) { #ifndef HAVE_UTIMES - (void)a; /* UNUSED */ + (void)t; /* UNUSED */ + (void)rt; /* UNUSED */ return (close(fd)); #else #if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__) @@ -1952,6 +1965,28 @@ open_on_current_dir(struct tree *t, const char *path, int flags) #endif } +static int +tree_dup(int fd) +{ + int new_fd; +#ifdef F_DUPFD_CLOEXEC + static volatile int can_dupfd_cloexec = 1; + + if (can_dupfd_cloexec) { + new_fd = fcntl(fd, F_DUPFD_CLOEXEC); + if (new_fd != -1) + return (new_fd); + /* Linux 2.6.18 - 2.6.23 declare F_DUPFD_CLOEXEC, + * but it cannot be used. So we have to try dup(). */ + /* We won't try F_DUPFD_CLOEXEC. */ + can_dupfd_cloexec = 0; + } +#endif /* F_DUPFD_CLOEXEC */ + new_fd = dup(fd); + __archive_ensure_cloexec_flag(new_fd); + return (new_fd); +} + /* * Add a directory path to the current stack. */ @@ -2052,8 +2087,9 @@ tree_reopen(struct tree *t, const char *path, int restore_time) tree_push(t, path, 0, 0, 0, NULL); t->stack->flags = needsFirstVisit; t->maxOpenCount = t->openCount = 1; - t->initial_dir_fd = open(".", O_RDONLY); - t->working_dir_fd = dup(t->initial_dir_fd); + t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC); + __archive_ensure_cloexec_flag(t->initial_dir_fd); + t->working_dir_fd = tree_dup(t->initial_dir_fd); return (t); } @@ -2063,11 +2099,12 @@ tree_descent(struct tree *t) int flag, new_fd, r = 0; t->dirname_length = archive_strlen(&t->path); - flag = O_RDONLY; + flag = O_RDONLY | O_CLOEXEC; #if defined(O_DIRECTORY) flag |= O_DIRECTORY; #endif new_fd = open_on_current_dir(t, t->stack->name.s, flag); + __archive_ensure_cloexec_flag(new_fd); if (new_fd < 0) { t->tree_errno = errno; r = TREE_ERROR_DIR; @@ -2101,8 +2138,10 @@ tree_ascend(struct tree *t) prev_dir_fd = t->working_dir_fd; if (te->flags & isDirLink) new_fd = te->symlink_parent_fd; - else - new_fd = open_on_current_dir(t, "..", O_RDONLY); + else { + new_fd = open_on_current_dir(t, "..", O_RDONLY | O_CLOEXEC); + __archive_ensure_cloexec_flag(new_fd); + } if (new_fd < 0) { t->tree_errno = errno; r = TREE_ERROR_FATAL; @@ -2265,11 +2304,16 @@ tree_dir_next_posix(struct tree *t) #endif #if defined(HAVE_FDOPENDIR) - if ((t->d = fdopendir(dup(t->working_dir_fd))) == NULL) { -#else - if (tree_enter_working_dir(t) != 0 || - (t->d = opendir(".")) == NULL) { + t->d = fdopendir(tree_dup(t->working_dir_fd)); +#else /* HAVE_FDOPENDIR */ + if (tree_enter_working_dir(t) == 0) { + t->d = opendir("."); +#if HAVE_DIRFD || defined(dirfd) + __archive_ensure_cloexec_flag(dirfd(t->d)); #endif + } +#endif /* HAVE_FDOPENDIR */ + if (t->d == NULL) { r = tree_ascend(t); /* Undo "chdir" */ tree_pop(t); t->tree_errno = errno; @@ -2296,11 +2340,21 @@ tree_dir_next_posix(struct tree *t) #endif /* HAVE_READDIR_R */ } for (;;) { + errno = 0; #if defined(HAVE_READDIR_R) r = readdir_r(t->d, t->dirent, &t->de); +#ifdef _AIX + /* Note: According to the man page, return value 9 indicates + * that the readdir_r was not successful and the error code + * is set to the global errno variable. And then if the end + * of directory entries was reached, the return value is 9 + * and the third parameter is set to NULL and errno is + * unchanged. */ + if (r == 9) + r = errno; +#endif /* _AIX */ if (r != 0 || t->de == NULL) { #else - errno = 0; t->de = readdir(t->d); if (t->de == NULL) { r = errno; @@ -2391,7 +2445,7 @@ tree_current_is_dir(struct tree *t) return 1; /* Not a dir; might be a link to a dir. */ /* If it's not a link, then it's not a link to a dir. */ - if (!S_ISLNK(tree_current_lstat(t)->st_mode)) + if (!S_ISLNK(st->st_mode)) return 0; /* * It's a link, but we don't know what it's a link to, diff --git a/libarchive/archive_read_disk_windows.c b/libarchive/archive_read_disk_windows.c index 6b6108ab1b0c..9c5420d80e77 100644 --- a/libarchive/archive_read_disk_windows.c +++ b/libarchive/archive_read_disk_windows.c @@ -597,7 +597,7 @@ start_next_async_read(struct archive_read_disk *a, struct tree *t) } else ResetEvent(olp->ol.hEvent); - buffbytes = olp->buff_size; + buffbytes = (DWORD)olp->buff_size; if (buffbytes > t->current_sparse->length) buffbytes = (DWORD)t->current_sparse->length; @@ -669,7 +669,7 @@ _archive_read_data_block(struct archive *_a, const void **buff, struct tree *t = a->tree; struct la_overlapped *olp; DWORD bytes_transferred; - int r; + int r = ARCHIVE_FATAL; archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_block"); @@ -698,7 +698,7 @@ _archive_read_data_block(struct archive *_a, const void **buff, olp = &(t->ol[t->ol_idx_done]); t->ol_idx_done = (t->ol_idx_done + 1) % MAX_OVERLAPPED; if (olp->bytes_transferred) - bytes_transferred = olp->bytes_transferred; + bytes_transferred = (DWORD)olp->bytes_transferred; else if (!GetOverlappedResult(t->entry_fh, &(olp->ol), &bytes_transferred, TRUE)) { la_dosmaperr(GetLastError()); @@ -1208,7 +1208,7 @@ _archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname) a->tree = tree_open(pathname, a->symlink_mode, a->restore_time); if (a->tree == NULL) { archive_set_error(&a->archive, ENOMEM, - "Can't allocate direcotry traversal data"); + "Can't allocate directory traversal data"); a->archive.state = ARCHIVE_STATE_FATAL; return (ARCHIVE_FATAL); } @@ -1257,16 +1257,18 @@ update_current_filesystem(struct archive_read_disk *a, int64_t dev) fid = t->max_filesystem_id++; if (t->max_filesystem_id > t->allocated_filesytem) { size_t s; + void *p; s = t->max_filesystem_id * 2; - t->filesystem_table = realloc(t->filesystem_table, - s * sizeof(*t->filesystem_table)); - if (t->filesystem_table == NULL) { + p = realloc(t->filesystem_table, + s * sizeof(*t->filesystem_table)); + if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate tar data"); return (ARCHIVE_FATAL); } - t->allocated_filesytem = s; + t->filesystem_table = (struct filesystem *)p; + t->allocated_filesytem = (int)s; } t->current_filesystem_id = fid; t->current_filesystem = &(t->filesystem_table[fid]); @@ -2040,7 +2042,7 @@ tree_free(struct tree *t) free(t->filesystem_table); for (i = 0; i < MAX_OVERLAPPED; i++) { if (t->ol[i].buff) - VirtualFree(t->ol[i].buff, t->ol[i].buff_size, MEM_DECOMMIT); + VirtualFree(t->ol[i].buff, 0, MEM_RELEASE); CloseHandle(t->ol[i].ol.hEvent); } free(t); @@ -2234,7 +2236,7 @@ setup_sparse_from_disk(struct archive_read_disk *a, ret = DeviceIoControl(handle, FSCTL_QUERY_ALLOCATED_RANGES, &range, sizeof(range), outranges, - outranges_size, &retbytes, NULL); + (DWORD)outranges_size, &retbytes, NULL); if (ret == 0 && GetLastError() == ERROR_MORE_DATA) { free(outranges); outranges_size *= 2; diff --git a/libarchive/archive_read_extract.c b/libarchive/archive_read_extract.c index aad8ac5168df..795f2abea847 100644 --- a/libarchive/archive_read_extract.c +++ b/libarchive/archive_read_extract.c @@ -154,7 +154,7 @@ copy_data(struct archive *ar, struct archive *aw) return (ARCHIVE_OK); if (r != ARCHIVE_OK) return (r); - r = archive_write_data_block(aw, buff, size, offset); + r = (int)archive_write_data_block(aw, buff, size, offset); if (r < ARCHIVE_WARN) r = ARCHIVE_WARN; if (r != ARCHIVE_OK) { diff --git a/libarchive/archive_read_open_fd.c b/libarchive/archive_read_open_fd.c index 7972841976b6..e0f95bf41b65 100644 --- a/libarchive/archive_read_open_fd.c +++ b/libarchive/archive_read_open_fd.c @@ -119,7 +119,8 @@ file_read(struct archive *a, void *client_data, const void **buff) if (bytes_read < 0) { if (errno == EINTR) continue; - archive_set_error(a, errno, "Error reading fd %d", mine->fd); + archive_set_error(a, errno, "Error reading fd %d", + mine->fd); } return (bytes_read); } diff --git a/libarchive/archive_read_open_file.c b/libarchive/archive_read_open_file.c index b1aac0a7cbf0..3a33c258ee70 100644 --- a/libarchive/archive_read_open_file.c +++ b/libarchive/archive_read_open_file.c @@ -108,11 +108,11 @@ static ssize_t file_read(struct archive *a, void *client_data, const void **buff) { struct read_FILE_data *mine = (struct read_FILE_data *)client_data; - ssize_t bytes_read; + size_t bytes_read; *buff = mine->buffer; bytes_read = fread(mine->buffer, 1, mine->block_size, mine->f); - if (bytes_read < 0) { + if (bytes_read < mine->block_size && ferror(mine->f)) { archive_set_error(a, errno, "Error reading file"); } return (bytes_read); diff --git a/libarchive/archive_read_open_filename.c b/libarchive/archive_read_open_filename.c index 69c44ea7e676..fefcd904d2c4 100644 --- a/libarchive/archive_read_open_filename.c +++ b/libarchive/archive_read_open_filename.c @@ -60,11 +60,15 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_open_filename.c 201093 2009 #endif #include "archive.h" +#include "archive_private.h" #include "archive_string.h" #ifndef O_BINARY #define O_BINARY 0 #endif +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif struct read_file_data { int fd; @@ -79,9 +83,10 @@ struct read_file_data { } filename; /* Must be last! */ }; +static int file_open(struct archive *, void *); static int file_close(struct archive *, void *); -static int file_open_filename(struct archive *, enum fnt_e, const void *, - size_t); +static int file_close2(struct archive *, void *); +static int file_switch(struct archive *, void *, void *); static ssize_t file_read(struct archive *, void *, const void **buff); static int64_t file_seek(struct archive *, void *, int64_t request, int); static int64_t file_skip(struct archive *, void *, int64_t request); @@ -98,26 +103,76 @@ int archive_read_open_filename(struct archive *a, const char *filename, size_t block_size) { - enum fnt_e filename_type; + const char *filenames[2] = { filename, NULL }; + return archive_read_open_filenames(a, filenames, block_size); +} + +int +archive_read_open_filenames(struct archive *a, const char **filenames, + size_t block_size) +{ + struct read_file_data *mine; + const char *filename = NULL; + if (filenames) + filename = *(filenames++); - if (filename == NULL || filename[0] == '\0') { - filename_type = FNT_STDIN; - } else - filename_type = FNT_MBS; - return (file_open_filename(a, filename_type, filename, block_size)); + archive_clear_error(a); + do + { + if (filename == NULL) + filename = ""; + mine = (struct read_file_data *)calloc(1, + sizeof(*mine) + strlen(filename)); + if (mine == NULL) + goto no_memory; + strcpy(mine->filename.m, filename); + mine->block_size = block_size; + mine->fd = -1; + mine->buffer = NULL; + mine->st_mode = mine->use_lseek = 0; + if (filename == NULL || filename[0] == '\0') { + mine->filename_type = FNT_STDIN; + } else + mine->filename_type = FNT_MBS; + if (archive_read_append_callback_data(a, mine) != (ARCHIVE_OK)) + return (ARCHIVE_FATAL); + if (filenames == NULL) + break; + filename = *(filenames++); + } while (filename != NULL && filename[0] != '\0'); + archive_read_set_open_callback(a, file_open); + archive_read_set_read_callback(a, file_read); + archive_read_set_skip_callback(a, file_skip); + archive_read_set_close_callback(a, file_close); + archive_read_set_switch_callback(a, file_switch); + archive_read_set_seek_callback(a, file_seek); + + return (archive_read_open1(a)); +no_memory: + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); } int archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename, size_t block_size) { - enum fnt_e filename_type; + struct read_file_data *mine = (struct read_file_data *)calloc(1, + sizeof(*mine) + wcslen(wfilename) * sizeof(wchar_t)); + if (!mine) + { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); + } + mine->fd = -1; + mine->block_size = block_size; if (wfilename == NULL || wfilename[0] == L'\0') { - filename_type = FNT_STDIN; + mine->filename_type = FNT_STDIN; } else { #if defined(_WIN32) && !defined(__CYGWIN__) - filename_type = FNT_WCS; + mine->filename_type = FNT_WCS; + wcscpy(mine->filename.w, wfilename); #else /* * POSIX system does not support a wchar_t interface for @@ -125,7 +180,6 @@ archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename, * filename to multi-byte one and use it. */ struct archive_string fn; - int r; archive_string_init(&fn); if (archive_string_append_from_wcs(&fn, wfilename, @@ -138,22 +192,31 @@ archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename, "Failed to convert a wide-character" " filename to a multi-byte filename"); archive_string_free(&fn); + free(mine); return (ARCHIVE_FATAL); } - r = file_open_filename(a, FNT_MBS, fn.s, block_size); + mine->filename_type = FNT_MBS; + strcpy(mine->filename.m, fn.s); archive_string_free(&fn); - return (r); #endif } - return (file_open_filename(a, filename_type, wfilename, block_size)); + if (archive_read_append_callback_data(a, mine) != (ARCHIVE_OK)) + return (ARCHIVE_FATAL); + archive_read_set_open_callback(a, file_open); + archive_read_set_read_callback(a, file_read); + archive_read_set_skip_callback(a, file_skip); + archive_read_set_close_callback(a, file_close); + archive_read_set_switch_callback(a, file_switch); + archive_read_set_seek_callback(a, file_seek); + + return (archive_read_open1(a)); } static int -file_open_filename(struct archive *a, enum fnt_e filename_type, - const void *_filename, size_t block_size) +file_open(struct archive *a, void *client_data) { struct stat st; - struct read_file_data *mine; + struct read_file_data *mine = (struct read_file_data *)client_data; void *buffer; const char *filename = NULL; const wchar_t *wfilename = NULL; @@ -168,7 +231,7 @@ file_open_filename(struct archive *a, enum fnt_e filename_type, #endif archive_clear_error(a); - if (filename_type == FNT_STDIN) { + if (mine->filename_type == FNT_STDIN) { /* We used to delegate stdin support by * directly calling archive_read_open_fd(a,0,block_size) * here, but that doesn't (and shouldn't) handle the @@ -183,9 +246,10 @@ file_open_filename(struct archive *a, enum fnt_e filename_type, setmode(0, O_BINARY); #endif filename = ""; - } else if (filename_type == FNT_MBS) { - filename = (const char *)_filename; - fd = open(filename, O_RDONLY | O_BINARY); + } else if (mine->filename_type == FNT_MBS) { + filename = mine->filename.m; + fd = open(filename, O_RDONLY | O_BINARY | O_CLOEXEC); + __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(a, errno, "Failed to open '%s'", filename); @@ -193,7 +257,7 @@ file_open_filename(struct archive *a, enum fnt_e filename_type, } } else { #if defined(_WIN32) && !defined(__CYGWIN__) - wfilename = (const wchar_t *)_filename; + wfilename = mine->filename.w; fd = _wopen(wfilename, O_RDONLY | O_BINARY); if (fd < 0 && errno == ENOENT) { wchar_t *fullpath; @@ -215,7 +279,7 @@ file_open_filename(struct archive *a, enum fnt_e filename_type, #endif } if (fstat(fd, &st) != 0) { - if (filename_type == FNT_WCS) + if (mine->filename_type == FNT_WCS) archive_set_error(a, errno, "Can't stat '%S'", wfilename); else @@ -280,50 +344,32 @@ file_open_filename(struct archive *a, enum fnt_e filename_type, #endif /* TODO: Add an "is_tape_like" variable and appropriate tests. */ - if (filename_type == FNT_WCS) - mine = (struct read_file_data *)calloc(1, - sizeof(*mine) + wcslen(wfilename) * sizeof(wchar_t)); - else - mine = (struct read_file_data *)calloc(1, - sizeof(*mine) + strlen(filename)); /* Disk-like devices prefer power-of-two block sizes. */ /* Use provided block_size as a guide so users have some control. */ if (is_disk_like) { size_t new_block_size = 64 * 1024; - while (new_block_size < block_size + while (new_block_size < mine->block_size && new_block_size < 64 * 1024 * 1024) new_block_size *= 2; - block_size = new_block_size; + mine->block_size = new_block_size; } - buffer = malloc(block_size); + buffer = malloc(mine->block_size); if (mine == NULL || buffer == NULL) { archive_set_error(a, ENOMEM, "No memory"); free(mine); free(buffer); return (ARCHIVE_FATAL); } - if (filename_type == FNT_WCS) - wcscpy(mine->filename.w, wfilename); - else - strcpy(mine->filename.m, filename); - mine->filename_type = filename_type; - mine->block_size = block_size; mine->buffer = buffer; mine->fd = fd; /* Remember mode so close can decide whether to flush. */ mine->st_mode = st.st_mode; /* Disk-like inputs can use lseek(). */ - if (is_disk_like) { - archive_read_set_seek_callback(a, file_seek); + if (is_disk_like) mine->use_lseek = 1; - } - archive_read_set_read_callback(a, file_read); - archive_read_set_skip_callback(a, file_skip); - archive_read_set_close_callback(a, file_close); - archive_read_set_callback_data(a, mine); - return (archive_read_open1(a)); + return (ARCHIVE_OK); } static ssize_t @@ -401,9 +447,7 @@ file_skip_lseek(struct archive *a, void *client_data, int64_t request) /* TODO: Deal with case where off_t isn't 64 bits. * This shouldn't be a problem on Linux or other POSIX * systems, since the configuration logic for libarchive - * tries to obtain a 64-bit off_t. It's still an issue - * on Windows, though, so it might suffice to just use - * _lseeki64() on Windows. + * tries to obtain a 64-bit off_t. */ if ((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0 && (new_offset = lseek(mine->fd, request, SEEK_CUR)) >= 0) @@ -475,7 +519,7 @@ file_seek(struct archive *a, void *client_data, int64_t request, int whence) } static int -file_close(struct archive *a, void *client_data) +file_close2(struct archive *a, void *client_data) { struct read_file_data *mine = (struct read_file_data *)client_data; @@ -508,6 +552,23 @@ file_close(struct archive *a, void *client_data) close(mine->fd); } free(mine->buffer); + mine->buffer = NULL; + mine->fd = -1; + return (ARCHIVE_OK); +} + +static int +file_close(struct archive *a, void *client_data) +{ + struct read_file_data *mine = (struct read_file_data *)client_data; + file_close2(a, client_data); free(mine); return (ARCHIVE_OK); } + +static int +file_switch(struct archive *a, void *client_data1, void *client_data2) +{ + file_close2(a, client_data1); + return file_open(a, client_data2); +} diff --git a/libarchive/archive_read_private.h b/libarchive/archive_read_private.h index 445c557f39c2..8a6c859a8a14 100644 --- a/libarchive/archive_read_private.h +++ b/libarchive/archive_read_private.h @@ -58,6 +58,8 @@ struct archive_read_filter; struct archive_read_filter_bidder { /* Configuration data for the bidder. */ void *data; + /* Name of the filter */ + const char *name; /* Taste the upstream filter to see if we handle this. */ int (*bid)(struct archive_read_filter_bidder *, struct archive_read_filter *); @@ -82,6 +84,8 @@ struct archive_read_filter { struct archive_read_filter_bidder *bidder; /* My bidder. */ struct archive_read_filter *upstream; /* Who I read from. */ struct archive_read *archive; /* Associated archive. */ + /* Open a block for reading */ + int (*open)(struct archive_read_filter *self); /* Return next block. */ ssize_t (*read)(struct archive_read_filter *, const void **); /* Skip forward this many bytes. */ @@ -90,6 +94,8 @@ struct archive_read_filter { int64_t (*seek)(struct archive_read_filter *self, int64_t offset, int whence); /* Close (just this filter) and free(self). */ int (*close)(struct archive_read_filter *self); + /* Function that handles switching from reading one block to the next/prev */ + int (*sswitch)(struct archive_read_filter *self, unsigned int iindex); /* My private data. */ void *data; @@ -118,13 +124,22 @@ struct archive_read_filter { * transformation filters. This will probably break the API/ABI and * so should be deferred at least until libarchive 3.0. */ +struct archive_read_data_node { + int64_t begin_position; + int64_t total_size; + void *data; +}; struct archive_read_client { archive_open_callback *opener; archive_read_callback *reader; archive_skip_callback *skipper; archive_seek_callback *seeker; archive_close_callback *closer; - void *data; + archive_switch_callback *switcher; + unsigned int nodes; + unsigned int cursor; + int64_t position; + struct archive_read_data_node *dataset; }; struct archive_read { @@ -146,18 +161,33 @@ struct archive_read { int64_t read_data_output_offset; size_t read_data_remaining; - /* Callbacks to open/read/write/close client archive stream. */ + /* + * Used by formats/filters to determine the amount of data + * requested from a call to archive_read_data(). This is only + * useful when the format/filter has seek support. + */ + char read_data_is_posix_read; + size_t read_data_requested; + + /* Callbacks to open/read/write/close client archive streams. */ struct archive_read_client client; /* Registered filter bidders. */ - struct archive_read_filter_bidder bidders[9]; + struct archive_read_filter_bidder bidders[14]; /* Last filter in chain */ struct archive_read_filter *filter; + /* Whether to bypass filter bidding process */ + int bypass_filter_bidding; + /* File offset of beginning of most recently-read header. */ int64_t header_position; + /* Nodes and offsets of compressed data block */ + unsigned int data_start_node; + unsigned int data_end_node; + /* * Format detection is mostly the same as compression * detection, with one significant difference: The bidders @@ -175,6 +205,7 @@ struct archive_read { int (*read_header)(struct archive_read *, struct archive_entry *); int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *); int (*read_data_skip)(struct archive_read *); + int64_t (*seek_data)(struct archive_read *, int64_t, int); int (*cleanup)(struct archive_read *); } formats[16]; struct archive_format_descriptor *format; /* Active format. */ @@ -194,6 +225,7 @@ int __archive_read_register_format(struct archive_read *a, int (*read_header)(struct archive_read *, struct archive_entry *), int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *), int (*read_data_skip)(struct archive_read *), + int64_t (*seek_data)(struct archive_read *, int64_t, int), int (*cleanup)(struct archive_read *)); int __archive_read_get_bidder(struct archive_read *a, @@ -207,4 +239,6 @@ int64_t __archive_read_filter_seek(struct archive_read_filter *, int64_t, int); int64_t __archive_read_consume(struct archive_read *, int64_t); int64_t __archive_read_filter_consume(struct archive_read_filter *, int64_t); int __archive_read_program(struct archive_read_filter *, const char *); +void __archive_read_free_filters(struct archive_read *); +int __archive_read_close_filters(struct archive_read *); #endif diff --git a/libarchive/archive_read_set_format.c b/libarchive/archive_read_set_format.c new file mode 100644 index 000000000000..190f4369d248 --- /dev/null +++ b/libarchive/archive_read_set_format.c @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 2003-2012 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_ERRNO_H +#include <errno.h> +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_read_private.h" + +int +archive_read_set_format(struct archive *_a, int code) +{ + int r1, r2, slots, i; + char str[10]; + struct archive_read *a = (struct archive_read *)_a; + + if ((r1 = archive_read_support_format_by_code(_a, code)) < (ARCHIVE_OK)) + return r1; + + r1 = r2 = (ARCHIVE_OK); + if (a->format) + r2 = (ARCHIVE_WARN); + switch (code & ARCHIVE_FORMAT_BASE_MASK) + { + case ARCHIVE_FORMAT_7ZIP: + strcpy(str, "7zip"); + break; + case ARCHIVE_FORMAT_AR: + strcpy(str, "ar"); + break; + case ARCHIVE_FORMAT_CAB: + strcpy(str, "cab"); + break; + case ARCHIVE_FORMAT_CPIO: + strcpy(str, "cpio"); + break; + case ARCHIVE_FORMAT_ISO9660: + strcpy(str, "iso9660"); + break; + case ARCHIVE_FORMAT_LHA: + strcpy(str, "lha"); + break; + case ARCHIVE_FORMAT_MTREE: + strcpy(str, "mtree"); + break; + case ARCHIVE_FORMAT_RAR: + strcpy(str, "rar"); + break; + case ARCHIVE_FORMAT_TAR: + strcpy(str, "tar"); + break; + case ARCHIVE_FORMAT_XAR: + strcpy(str, "xar"); + break; + case ARCHIVE_FORMAT_ZIP: + strcpy(str, "zip"); + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Invalid format code specified"); + return (ARCHIVE_FATAL); + } + + slots = sizeof(a->formats) / sizeof(a->formats[0]); + a->format = &(a->formats[0]); + for (i = 0; i < slots; i++, a->format++) { + if (!a->format->name || !strcmp(a->format->name, str)) + break; + } + if (!a->format->name || strcmp(a->format->name, str)) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Internal error: Unable to set format"); + r1 = (ARCHIVE_FATAL); + } + + return (r1 < r2) ? r1 : r2; +} diff --git a/libarchive/archive_read_support_filter_all.c b/libarchive/archive_read_support_filter_all.c index 733d86298434..b778cfb79215 100644 --- a/libarchive/archive_read_support_filter_all.c +++ b/libarchive/archive_read_support_filter_all.c @@ -48,7 +48,7 @@ archive_read_support_filter_all(struct archive *a) archive_read_support_filter_bzip2(a); /* The decompress code doesn't use an outside library. */ archive_read_support_filter_compress(a); - /* Gzip decompress falls back to "gunzip" command-line. */ + /* Gzip decompress falls back to "gzip -d" command-line. */ archive_read_support_filter_gzip(a); /* Lzip falls back to "unlzip" command-line program. */ archive_read_support_filter_lzip(a); @@ -63,6 +63,12 @@ archive_read_support_filter_all(struct archive *a) archive_read_support_filter_uu(a); /* The decode code doesn't use an outside library. */ archive_read_support_filter_rpm(a); + /* The decode code always uses "lrzip -q -d" command-line. */ + archive_read_support_filter_lrzip(a); + /* Lzop decompress falls back to "lzop -d" command-line. */ + archive_read_support_filter_lzop(a); + /* The decode code always uses "grzip -d" command-line. */ + archive_read_support_filter_grzip(a); /* Note: We always return ARCHIVE_OK here, even if some of the * above return ARCHIVE_WARN. The intent here is to enable diff --git a/libarchive/archive_read_support_filter_bzip2.c b/libarchive/archive_read_support_filter_bzip2.c index 8d5bd1c4aca6..3885a7cf6153 100644 --- a/libarchive/archive_read_support_filter_bzip2.c +++ b/libarchive/archive_read_support_filter_bzip2.c @@ -94,6 +94,7 @@ archive_read_support_filter_bzip2(struct archive *_a) return (ARCHIVE_FATAL); reader->data = NULL; + reader->name = "bzip2"; reader->bid = bzip2_reader_bid; reader->init = bzip2_reader_init; reader->options = NULL; @@ -102,7 +103,7 @@ archive_read_support_filter_bzip2(struct archive *_a) return (ARCHIVE_OK); #else archive_set_error(_a, ARCHIVE_ERRNO_MISC, - "Using external bunzip2 program"); + "Using external bzip2 program"); return (ARCHIVE_WARN); #endif } @@ -170,11 +171,11 @@ bzip2_reader_init(struct archive_read_filter *self) { int r; - r = __archive_read_program(self, "bunzip2"); + r = __archive_read_program(self, "bzip2 -d"); /* Note: We set the format here even if __archive_read_program() * above fails. We do, after all, know what the format is * even if we weren't able to read it. */ - self->code = ARCHIVE_COMPRESSION_BZIP2; + self->code = ARCHIVE_FILTER_BZIP2; self->name = "bzip2"; return (r); } @@ -192,7 +193,7 @@ bzip2_reader_init(struct archive_read_filter *self) void *out_block; struct private_data *state; - self->code = ARCHIVE_COMPRESSION_BZIP2; + self->code = ARCHIVE_FILTER_BZIP2; self->name = "bzip2"; state = (struct private_data *)calloc(sizeof(*state), 1); diff --git a/libarchive/archive_read_support_filter_compress.c b/libarchive/archive_read_support_filter_compress.c index 1b8530004fbb..3f5d1f37eab3 100644 --- a/libarchive/archive_read_support_filter_compress.c +++ b/libarchive/archive_read_support_filter_compress.c @@ -163,6 +163,7 @@ archive_read_support_filter_compress(struct archive *_a) return (ARCHIVE_FATAL); bidder->data = NULL; + bidder->name = "compress (.Z)"; bidder->bid = compress_bidder_bid; bidder->init = compress_bidder_init; bidder->options = NULL; @@ -212,7 +213,7 @@ compress_bidder_init(struct archive_read_filter *self) void *out_block; int code; - self->code = ARCHIVE_COMPRESSION_COMPRESS; + self->code = ARCHIVE_FILTER_COMPRESS; self->name = "compress (.Z)"; state = (struct private_data *)calloc(sizeof(*state), 1); diff --git a/libarchive/archive_read_support_filter_grzip.c b/libarchive/archive_read_support_filter_grzip.c new file mode 100644 index 000000000000..84c86aeb4386 --- /dev/null +++ b/libarchive/archive_read_support_filter_grzip.c @@ -0,0 +1,121 @@ +/*- + * 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_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_read_private.h" + +static const unsigned char grzip_magic[] = { + 0x47, 0x52, 0x5a, 0x69, 0x70, 0x49, 0x49, 0x00, + 0x02, 0x04, 0x3a, 0x29 }; + +static int grzip_bidder_bid(struct archive_read_filter_bidder *, + struct archive_read_filter *); +static int grzip_bidder_init(struct archive_read_filter *); + + +static int +grzip_reader_free(struct archive_read_filter_bidder *self) +{ + (void)self; /* UNUSED */ + return (ARCHIVE_OK); +} + +int +archive_read_support_filter_grzip(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_read_filter_bidder *reader; + + archive_check_magic(_a, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_NEW, "archive_read_support_filter_grzip"); + + if (__archive_read_get_bidder(a, &reader) != ARCHIVE_OK) + return (ARCHIVE_FATAL); + + reader->data = NULL; + reader->bid = grzip_bidder_bid; + reader->init = grzip_bidder_init; + reader->options = NULL; + reader->free = grzip_reader_free; + /* This filter always uses an external program. */ + archive_set_error(_a, ARCHIVE_ERRNO_MISC, + "Using external grzip program for grzip decompression"); + return (ARCHIVE_WARN); +} + +/* + * Bidder just verifies the header and returns the number of verified bits. + */ +static int +grzip_bidder_bid(struct archive_read_filter_bidder *self, + struct archive_read_filter *filter) +{ + const unsigned char *p; + ssize_t avail; + + (void)self; /* UNUSED */ + + p = __archive_read_filter_ahead(filter, sizeof(grzip_magic), &avail); + if (p == NULL || avail == 0) + return (0); + + if (memcmp(p, grzip_magic, sizeof(grzip_magic))) + return (0); + + return (sizeof(grzip_magic) * 8); +} + +static int +grzip_bidder_init(struct archive_read_filter *self) +{ + int r; + + r = __archive_read_program(self, "grzip -d"); + /* Note: We set the format here even if __archive_read_program() + * above fails. We do, after all, know what the format is + * even if we weren't able to read it. */ + self->code = ARCHIVE_FILTER_GRZIP; + self->name = "grzip"; + return (r); +} diff --git a/libarchive/archive_read_support_filter_gzip.c b/libarchive/archive_read_support_filter_gzip.c index f6d5595f0b6b..fa8c675de124 100644 --- a/libarchive/archive_read_support_filter_gzip.c +++ b/libarchive/archive_read_support_filter_gzip.c @@ -72,7 +72,7 @@ static int gzip_filter_close(struct archive_read_filter *); * * TODO: If zlib is unavailable, gzip_bidder_init() should * use the compress_program framework to try to fire up an external - * gunzip program. + * gzip program. */ static int gzip_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *); @@ -100,6 +100,7 @@ archive_read_support_filter_gzip(struct archive *_a) return (ARCHIVE_FATAL); bidder->data = NULL; + bidder->name = "gzip"; bidder->bid = gzip_bidder_bid; bidder->init = gzip_bidder_init; bidder->options = NULL; @@ -109,7 +110,7 @@ archive_read_support_filter_gzip(struct archive *_a) return (ARCHIVE_OK); #else archive_set_error(_a, ARCHIVE_ERRNO_MISC, - "Using external gunzip program"); + "Using external gzip program"); return (ARCHIVE_WARN); #endif } @@ -121,7 +122,7 @@ archive_read_support_filter_gzip(struct archive *_a) * number of bytes in header. If pbits is non-NULL, it receives a * count of bits verified, suitable for use by bidder. */ -static int +static ssize_t peek_at_header(struct archive_read_filter *filter, int *pbits) { const unsigned char *p; @@ -223,7 +224,7 @@ gzip_bidder_bid(struct archive_read_filter_bidder *self, /* * If we don't have the library on this system, we can't do the - * decompression directly. We can, however, try to run gunzip + * decompression directly. We can, however, try to run "gzip -d" * in case that's available. */ static int @@ -231,11 +232,11 @@ gzip_bidder_init(struct archive_read_filter *self) { int r; - r = __archive_read_program(self, "gunzip"); + r = __archive_read_program(self, "gzip -d"); /* Note: We set the format here even if __archive_read_program() * above fails. We do, after all, know what the format is * even if we weren't able to read it. */ - self->code = ARCHIVE_COMPRESSION_GZIP; + self->code = ARCHIVE_FILTER_GZIP; self->name = "gzip"; return (r); } @@ -252,7 +253,7 @@ gzip_bidder_init(struct archive_read_filter *self) static const size_t out_block_size = 64 * 1024; void *out_block; - self->code = ARCHIVE_COMPRESSION_GZIP; + self->code = ARCHIVE_FILTER_GZIP; self->name = "gzip"; state = (struct private_data *)calloc(sizeof(*state), 1); @@ -299,7 +300,7 @@ consume_header(struct archive_read_filter *self) /* Initialize compression library. */ state->stream.next_in = (unsigned char *)(uintptr_t) __archive_read_filter_ahead(self->upstream, 1, &avail); - state->stream.avail_in = avail; + state->stream.avail_in = (uInt)avail; ret = inflateInit2(&(state->stream), -15 /* Don't check for zlib header */); @@ -380,7 +381,7 @@ gzip_filter_read(struct archive_read_filter *self, const void **p) /* Empty our output buffer. */ state->stream.next_out = state->out_block; - state->stream.avail_out = state->out_block_size; + state->stream.avail_out = (uInt)state->out_block_size; /* Try to fill the output buffer. */ while (state->stream.avail_out > 0 && !state->eof) { @@ -407,7 +408,7 @@ gzip_filter_read(struct archive_read_filter *self, const void **p) "truncated gzip input"); return (ARCHIVE_FATAL); } - state->stream.avail_in = avail_in; + state->stream.avail_in = (uInt)avail_in; /* Decompress and consume some of that data. */ ret = inflate(&(state->stream), 0); diff --git a/libarchive/archive_read_support_filter_lrzip.c b/libarchive/archive_read_support_filter_lrzip.c new file mode 100644 index 000000000000..c82a8e2f13ea --- /dev/null +++ b/libarchive/archive_read_support_filter_lrzip.c @@ -0,0 +1,132 @@ +/*- + * 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_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_read_private.h" + +#define LRZIP_HEADER_MAGIC "LRZI" +#define LRZIP_HEADER_MAGIC_LEN 4 + +static int lrzip_bidder_bid(struct archive_read_filter_bidder *, + struct archive_read_filter *); +static int lrzip_bidder_init(struct archive_read_filter *); + + +static int +lrzip_reader_free(struct archive_read_filter_bidder *self) +{ + (void)self; /* UNUSED */ + return (ARCHIVE_OK); +} + +int +archive_read_support_filter_lrzip(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_read_filter_bidder *reader; + + archive_check_magic(_a, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_NEW, "archive_read_support_filter_lrzip"); + + if (__archive_read_get_bidder(a, &reader) != ARCHIVE_OK) + return (ARCHIVE_FATAL); + + reader->data = NULL; + reader->name = "lrzip"; + reader->bid = lrzip_bidder_bid; + reader->init = lrzip_bidder_init; + reader->options = NULL; + reader->free = lrzip_reader_free; + /* This filter always uses an external program. */ + archive_set_error(_a, ARCHIVE_ERRNO_MISC, + "Using external lrzip program for lrzip decompression"); + return (ARCHIVE_WARN); +} + +/* + * Bidder just verifies the header and returns the number of verified bits. + */ +static int +lrzip_bidder_bid(struct archive_read_filter_bidder *self, + struct archive_read_filter *filter) +{ + const unsigned char *p; + ssize_t avail, len; + int i; + + (void)self; /* UNUSED */ + /* Start by looking at the first six bytes of the header, which + * is all fixed layout. */ + len = 6; + p = __archive_read_filter_ahead(filter, len, &avail); + if (p == NULL || avail == 0) + return (0); + + if (memcmp(p, LRZIP_HEADER_MAGIC, LRZIP_HEADER_MAGIC_LEN)) + return (0); + + /* current major version is always 0, verify this */ + if (p[LRZIP_HEADER_MAGIC_LEN]) + return 0; + /* support only v0.6+ lrzip for sanity */ + i = p[LRZIP_HEADER_MAGIC_LEN + 1]; + if ((i < 6) || (i > 10)) + return 0; + + return (int)len; +} + +static int +lrzip_bidder_init(struct archive_read_filter *self) +{ + int r; + + r = __archive_read_program(self, "lrzip -d -q"); + /* Note: We set the format here even if __archive_read_program() + * above fails. We do, after all, know what the format is + * even if we weren't able to read it. */ + self->code = ARCHIVE_FILTER_LRZIP; + self->name = "lrzip"; + return (r); +} diff --git a/libarchive/archive_read_support_filter_lzop.c b/libarchive/archive_read_support_filter_lzop.c new file mode 100644 index 000000000000..713af31e99f0 --- /dev/null +++ b/libarchive/archive_read_support_filter_lzop.c @@ -0,0 +1,486 @@ +/*- + * 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_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_LZO_LZOCONF_H +#include <lzo/lzoconf.h> +#endif +#ifdef HAVE_LZO_LZO1X_H +#include <lzo/lzo1x.h> +#endif +#ifdef HAVE_ZLIB_H +#include <zlib.h> /* for crc32 and adler32 */ +#endif + +#include "archive.h" +#if !defined(HAVE_ZLIB_H) &&\ + defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) +#include "archive_crc32.h" +#endif +#include "archive_endian.h" +#include "archive_private.h" +#include "archive_read_private.h" + +#ifndef HAVE_ZLIB_H +#define adler32 lzo_adler32 +#endif + +#define LZOP_HEADER_MAGIC "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a" +#define LZOP_HEADER_MAGIC_LEN 9 + +#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) +struct read_lzop { + unsigned char *out_block; + size_t out_block_size; + int64_t total_out; + int flags; + uint32_t compressed_cksum; + uint32_t uncompressed_cksum; + size_t compressed_size; + size_t uncompressed_size; + size_t unconsumed_bytes; + char in_stream; + char eof; /* True = found end of compressed data. */ +}; + +#define FILTER 0x0800 +#define CRC32_HEADER 0x1000 +#define EXTRA_FIELD 0x0040 +#define ADLER32_UNCOMPRESSED 0x0001 +#define ADLER32_COMPRESSED 0x0002 +#define CRC32_UNCOMPRESSED 0x0100 +#define CRC32_COMPRESSED 0x0200 +#define MAX_BLOCK_SIZE (64 * 1024 * 1024) + +static ssize_t lzop_filter_read(struct archive_read_filter *, const void **); +static int lzop_filter_close(struct archive_read_filter *); +#endif + +static int lzop_bidder_bid(struct archive_read_filter_bidder *, + struct archive_read_filter *); +static int lzop_bidder_init(struct archive_read_filter *); + +int +archive_read_support_filter_lzop(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_read_filter_bidder *reader; + + archive_check_magic(_a, ARCHIVE_READ_MAGIC, + ARCHIVE_STATE_NEW, "archive_read_support_filter_lzop"); + + if (__archive_read_get_bidder(a, &reader) != ARCHIVE_OK) + return (ARCHIVE_FATAL); + + reader->data = NULL; + reader->bid = lzop_bidder_bid; + reader->init = lzop_bidder_init; + reader->options = NULL; + reader->free = NULL; + /* Signal the extent of lzop support with the return value here. */ +#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) + return (ARCHIVE_OK); +#else + /* Return ARCHIVE_WARN since this always uses an external program. */ + archive_set_error(_a, ARCHIVE_ERRNO_MISC, + "Using external lzop program for lzop decompression"); + return (ARCHIVE_WARN); +#endif +} + +/* + * Bidder just verifies the header and returns the number of verified bits. + */ +static int +lzop_bidder_bid(struct archive_read_filter_bidder *self, + struct archive_read_filter *filter) +{ + const unsigned char *p; + ssize_t avail; + + (void)self; /* UNUSED */ + + p = __archive_read_filter_ahead(filter, LZOP_HEADER_MAGIC_LEN, &avail); + if (p == NULL || avail == 0) + return (0); + + if (memcmp(p, LZOP_HEADER_MAGIC, LZOP_HEADER_MAGIC_LEN)) + return (0); + + return (LZOP_HEADER_MAGIC_LEN * 8); +} + +#if !defined(HAVE_LZO_LZOCONF_H) || !defined(HAVE_LZO_LZO1X_H) +/* + * If we don't have the library on this system, we can't do the + * decompression directly. We can, however, try to run "lzop -d" + * in case that's available. + */ +static int +lzop_bidder_init(struct archive_read_filter *self) +{ + int r; + + r = __archive_read_program(self, "lzop -d"); + /* Note: We set the format here even if __archive_read_program() + * above fails. We do, after all, know what the format is + * even if we weren't able to read it. */ + self->code = ARCHIVE_FILTER_LZOP; + self->name = "lzop"; + return (r); +} +#else +/* + * Initialize the filter object. + */ +static int +lzop_bidder_init(struct archive_read_filter *self) +{ + struct read_lzop *state; + + self->code = ARCHIVE_FILTER_LZOP; + self->name = "lzop"; + + state = (struct read_lzop *)calloc(sizeof(*state), 1); + if (state == NULL) { + archive_set_error(&self->archive->archive, ENOMEM, + "Can't allocate data for lzop decompression"); + return (ARCHIVE_FATAL); + } + + self->data = state; + self->read = lzop_filter_read; + self->skip = NULL; /* not supported */ + self->close = lzop_filter_close; + + return (ARCHIVE_OK); +} + +static int +consume_header(struct archive_read_filter *self) +{ + struct read_lzop *state = (struct read_lzop *)self->data; + const unsigned char *p, *_p; + unsigned checksum, flags, len, method, version; + + /* + * Check LZOP magic code. + */ + p = __archive_read_filter_ahead(self->upstream, + LZOP_HEADER_MAGIC_LEN, NULL); + if (p == NULL) + return (ARCHIVE_EOF); + + if (memcmp(p, LZOP_HEADER_MAGIC, LZOP_HEADER_MAGIC_LEN)) + return (ARCHIVE_EOF); + __archive_read_filter_consume(self->upstream, + LZOP_HEADER_MAGIC_LEN); + + p = __archive_read_filter_ahead(self->upstream, 29, NULL); + if (p == NULL) + goto truncated; + _p = p; + version = archive_be16dec(p); + p += 4;/* version(2 bytes) + library version(2 bytes) */ + + if (version >= 0x940) { + unsigned reqversion = archive_be16dec(p); p += 2; + if (reqversion < 0x900) { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, "Invalid required version"); + return (ARCHIVE_FAILED); + } + } + + method = *p++; + if (method < 1 || method > 3) { + archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + "Unsupported method"); + return (ARCHIVE_FAILED); + } + + if (version >= 0x940) { + unsigned level = *p++; + if (method == 1 && level == 0) level = 3; + if (method == 2 && level == 0) level = 1; + if (method == 3 && level == 0) level = 9; + if (level < 1 && level > 9) { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, "Invalid level"); + return (ARCHIVE_FAILED); + } + } + + flags = archive_be32dec(p); p += 4; + + if (flags & FILTER) + p += 4; /* Skip filter */ + p += 4; /* Skip mode */ + if (version >= 0x940) + p += 8; /* Skip mtime */ + else + p += 4; /* Skip mtime */ + len = *p++; /* Read filename length */ + len += p - _p; + /* Make sure we have all bytes we need to calculate checksum. */ + p = __archive_read_filter_ahead(self->upstream, len + 4, NULL); + if (p == NULL) + goto truncated; + if (flags & CRC32_HEADER) + checksum = crc32(crc32(0, NULL, 0), p, len); + else + checksum = adler32(adler32(0, NULL, 0), p, len); + if (archive_be32dec(p + len) != checksum) + goto corrupted; + __archive_read_filter_consume(self->upstream, len + 4); + if (flags & EXTRA_FIELD) { + /* Skip extra field */ + p = __archive_read_filter_ahead(self->upstream, 4, NULL); + if (p == NULL) + goto truncated; + len = archive_be32dec(p); + __archive_read_filter_consume(self->upstream, len + 4 + 4); + } + state->flags = flags; + state->in_stream = 1; + return (ARCHIVE_OK); +truncated: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data"); + return (ARCHIVE_FAILED); +corrupted: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header"); + return (ARCHIVE_FAILED); +} + +static int +consume_block_info(struct archive_read_filter *self) +{ + struct read_lzop *state = (struct read_lzop *)self->data; + const unsigned char *p; + unsigned flags = state->flags; + + p = __archive_read_filter_ahead(self->upstream, 4, NULL); + if (p == NULL) + goto truncated; + state->uncompressed_size = archive_be32dec(p); + __archive_read_filter_consume(self->upstream, 4); + if (state->uncompressed_size == 0) + return (ARCHIVE_EOF); + if (state->uncompressed_size > MAX_BLOCK_SIZE) + goto corrupted; + + p = __archive_read_filter_ahead(self->upstream, 4, NULL); + if (p == NULL) + goto truncated; + state->compressed_size = archive_be32dec(p); + __archive_read_filter_consume(self->upstream, 4); + if (state->compressed_size > state->uncompressed_size) + goto corrupted; + + if (flags & (CRC32_UNCOMPRESSED | ADLER32_UNCOMPRESSED)) { + p = __archive_read_filter_ahead(self->upstream, 4, NULL); + if (p == NULL) + goto truncated; + state->compressed_cksum = state->uncompressed_cksum = + archive_be32dec(p); + __archive_read_filter_consume(self->upstream, 4); + } + if ((flags & (CRC32_COMPRESSED | ADLER32_COMPRESSED)) && + state->compressed_size < state->uncompressed_size) { + p = __archive_read_filter_ahead(self->upstream, 4, NULL); + if (p == NULL) + goto truncated; + state->compressed_cksum = archive_be32dec(p); + __archive_read_filter_consume(self->upstream, 4); + } + return (ARCHIVE_OK); +truncated: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data"); + return (ARCHIVE_FAILED); +corrupted: + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header"); + return (ARCHIVE_FAILED); +} + +static ssize_t +lzop_filter_read(struct archive_read_filter *self, const void **p) +{ + struct read_lzop *state = (struct read_lzop *)self->data; + const void *b; + lzo_uint out_size; + uint32_t cksum; + int ret, r; + + if (state->unconsumed_bytes) { + __archive_read_filter_consume(self->upstream, + state->unconsumed_bytes); + state->unconsumed_bytes = 0; + } + if (state->eof) + return (0); + + for (;;) { + if (!state->in_stream) { + ret = consume_header(self); + if (ret < ARCHIVE_OK) + return (ret); + if (ret == ARCHIVE_EOF) { + state->eof = 1; + return (0); + } + } + ret = consume_block_info(self); + if (ret < ARCHIVE_OK) + return (ret); + if (ret == ARCHIVE_EOF) + state->in_stream = 0; + else + break; + } + + if (state->out_block == NULL || + state->out_block_size < state->uncompressed_size) { + void *new_block; + + new_block = realloc(state->out_block, state->uncompressed_size); + if (new_block == NULL) { + archive_set_error(&self->archive->archive, ENOMEM, + "Can't allocate data for lzop decompression"); + return (ARCHIVE_FATAL); + } + state->out_block = new_block; + state->out_block_size = state->uncompressed_size; + } + + b = __archive_read_filter_ahead(self->upstream, + state->compressed_size, NULL); + if (b == NULL) { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data"); + return (ARCHIVE_FATAL); + } + if (state->flags & CRC32_COMPRESSED) + cksum = crc32(crc32(0, NULL, 0), b, state->compressed_size); + else if (state->flags & ADLER32_COMPRESSED) + cksum = adler32(adler32(0, NULL, 0), b, state->compressed_size); + else + cksum = state->compressed_cksum; + if (cksum != state->compressed_cksum) { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, "Corrupted data"); + return (ARCHIVE_FATAL); + } + + /* + * If the both uncompressed size and compressed size are the same, + * we do not decompress this block. + */ + if (state->uncompressed_size == state->compressed_size) { + *p = b; + state->total_out += state->compressed_size; + state->unconsumed_bytes = state->compressed_size; + return ((ssize_t)state->uncompressed_size); + } + + /* + * Drive lzo uncompresison. + */ + out_size = (lzo_uint)state->uncompressed_size; + r = lzo1x_decompress_safe(b, (lzo_uint)state->compressed_size, + state->out_block, &out_size, NULL); + switch (r) { + case LZO_E_OK: + if (out_size == state->uncompressed_size) + break; + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, "Corrupted data"); + return (ARCHIVE_FATAL); + case LZO_E_OUT_OF_MEMORY: + archive_set_error(&self->archive->archive, ENOMEM, + "lzop decompression failed: out of memory"); + return (ARCHIVE_FATAL); + default: + archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, + "lzop decompression failed: %d", r); + return (ARCHIVE_FATAL); + } + + if (state->flags & CRC32_UNCOMPRESSED) + cksum = crc32(crc32(0, NULL, 0), state->out_block, + state->uncompressed_size); + else if (state->flags & ADLER32_UNCOMPRESSED) + cksum = adler32(adler32(0, NULL, 0), state->out_block, + state->uncompressed_size); + else + cksum = state->uncompressed_cksum; + if (cksum != state->uncompressed_cksum) { + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, "Corrupted data"); + return (ARCHIVE_FATAL); + } + + __archive_read_filter_consume(self->upstream, state->compressed_size); + *p = state->out_block; + state->total_out += out_size; + return ((ssize_t)out_size); +} + +/* + * Clean up the decompressor. + */ +static int +lzop_filter_close(struct archive_read_filter *self) +{ + struct read_lzop *state = (struct read_lzop *)self->data; + + free(state->out_block); + free(state); + return (ARCHIVE_OK); +} + +#endif diff --git a/libarchive/archive_read_support_filter_program.c b/libarchive/archive_read_support_filter_program.c index b05eb03424d7..66dc2f424f85 100644 --- a/libarchive/archive_read_support_filter_program.c +++ b/libarchive/archive_read_support_filter_program.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2007 Joerg Sonnenberger + * Copyright (c) 2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -53,7 +54,9 @@ __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_private.h" +#include "archive_string.h" #include "archive_read_private.h" +#include "filter_fork.h" #if ARCHIVE_VERSION_NUMBER < 4000000 @@ -79,50 +82,13 @@ archive_read_support_filter_program(struct archive *a, const char *cmd) return (archive_read_support_filter_program_signature(a, cmd, NULL, 0)); } - -/* This capability is only available on POSIX systems. */ -#if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \ - !(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__)) - -/* - * On non-Posix systems, allow the program to build, but choke if - * this function is actually invoked. - */ -int -archive_read_support_filter_program_signature(struct archive *_a, - const char *cmd, const void *signature, size_t signature_len) -{ - (void)_a; /* UNUSED */ - (void)cmd; /* UNUSED */ - (void)signature; /* UNUSED */ - (void)signature_len; /* UNUSED */ - - archive_set_error(_a, -1, - "External compression programs not supported on this platform"); - return (ARCHIVE_FATAL); -} - -int -__archive_read_program(struct archive_read_filter *self, const char *cmd) -{ - (void)self; /* UNUSED */ - (void)cmd; /* UNUSED */ - - archive_set_error(&self->archive->archive, -1, - "External compression programs not supported on this platform"); - return (ARCHIVE_FATAL); -} - -#else - -#include "filter_fork.h" - /* * The bidder object stores the command and the signature to watch for. * The 'inhibit' entry here is used to ensure that unchecked filters never * bid twice in the same pipeline. */ struct program_bidder { + char *description; char *cmd; void *signature; size_t signature_len; @@ -138,8 +104,12 @@ static int program_bidder_free(struct archive_read_filter_bidder *); * The actual filter needs to track input and output data. */ struct program_filter { - char *description; + struct archive_string description; +#if defined(_WIN32) && !defined(__CYGWIN__) + HANDLE child; +#else pid_t child; +#endif int exit_status; int waitpid_return; int child_stdin, child_stdout; @@ -151,6 +121,29 @@ struct program_filter { static ssize_t program_filter_read(struct archive_read_filter *, const void **); static int program_filter_close(struct archive_read_filter *); +static void free_state(struct program_bidder *); + +static int +set_bidder_signature(struct archive_read_filter_bidder *bidder, + struct program_bidder *state, const void *signature, size_t signature_len) +{ + + if (signature != NULL && signature_len > 0) { + state->signature_len = signature_len; + state->signature = malloc(signature_len); + memcpy(state->signature, signature, signature_len); + } + + /* + * Fill in the bidder object. + */ + bidder->data = state; + bidder->bid = program_bidder_bid; + bidder->init = program_bidder_init; + bidder->options = NULL; + bidder->free = program_bidder_free; + return (ARCHIVE_OK); +} int archive_read_support_filter_program_signature(struct archive *_a, @@ -169,37 +162,40 @@ archive_read_support_filter_program_signature(struct archive *_a, /* * Allocate our private state. */ - state = (struct program_bidder *)calloc(sizeof (*state), 1); + state = (struct program_bidder *)calloc(1, sizeof (*state)); if (state == NULL) - return (ARCHIVE_FATAL); + goto memerr; state->cmd = strdup(cmd); - if (signature != NULL && signature_len > 0) { - state->signature_len = signature_len; - state->signature = malloc(signature_len); - memcpy(state->signature, signature, signature_len); - } + if (state->cmd == NULL) + goto memerr; - /* - * Fill in the bidder object. - */ - bidder->data = state; - bidder->bid = program_bidder_bid; - bidder->init = program_bidder_init; - bidder->options = NULL; - bidder->free = program_bidder_free; - return (ARCHIVE_OK); + return set_bidder_signature(bidder, state, signature, signature_len); +memerr: + free_state(state); + archive_set_error(_a, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); } static int program_bidder_free(struct archive_read_filter_bidder *self) { struct program_bidder *state = (struct program_bidder *)self->data; - free(state->cmd); - free(state->signature); - free(self->data); + + free_state(state); return (ARCHIVE_OK); } +static void +free_state(struct program_bidder *state) +{ + + if (state) { + free(state->cmd); + free(state->signature); + free(state); + } +} + /* * If we do have a signature, bid only if that matches. * @@ -258,6 +254,9 @@ child_stop(struct archive_read_filter *self, struct program_filter *state) state->waitpid_return = waitpid(state->child, &state->exit_status, 0); } while (state->waitpid_return == -1 && errno == EINTR); +#if defined(_WIN32) && !defined(__CYGWIN__) + CloseHandle(state->child); +#endif state->child = 0; } @@ -310,11 +309,35 @@ child_read(struct archive_read_filter *self, char *buf, size_t buf_len) struct program_filter *state = self->data; ssize_t ret, requested, avail; const char *p; +#if defined(_WIN32) && !defined(__CYGWIN__) + HANDLE handle = (HANDLE)_get_osfhandle(state->child_stdout); +#endif requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len; for (;;) { do { +#if defined(_WIN32) && !defined(__CYGWIN__) + /* Avoid infinity wait. + * Note: If there is no data in the pipe, ReadFile() + * called in read() never returns and so we won't + * write remaining encoded data to the pipe. + * Note: This way may cause performance problem. + * we are looking forward to great code to resolve + * this. */ + DWORD pipe_avail = -1; + int cnt = 2; + + while (PeekNamedPipe(handle, NULL, 0, NULL, + &pipe_avail, NULL) != 0 && pipe_avail == 0 && + cnt--) + Sleep(5); + if (pipe_avail == 0) { + ret = -1; + errno = EAGAIN; + break; + } +#endif ret = read(state->child_stdout, buf, requested); } while (ret == -1 && errno == EINTR); @@ -376,38 +399,57 @@ __archive_read_program(struct archive_read_filter *self, const char *cmd) struct program_filter *state; static const size_t out_buf_len = 65536; char *out_buf; - char *description; const char *prefix = "Program: "; + pid_t child; + size_t l; + l = strlen(prefix) + strlen(cmd) + 1; state = (struct program_filter *)calloc(1, sizeof(*state)); out_buf = (char *)malloc(out_buf_len); - description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); - if (state == NULL || out_buf == NULL || description == NULL) { + if (state == NULL || out_buf == NULL || + archive_string_ensure(&state->description, l) == NULL) { archive_set_error(&self->archive->archive, ENOMEM, "Can't allocate input data"); - free(state); + if (state != NULL) { + archive_string_free(&state->description); + free(state); + } free(out_buf); - free(description); return (ARCHIVE_FATAL); } + archive_strcpy(&state->description, prefix); + archive_strcat(&state->description, cmd); - self->code = ARCHIVE_COMPRESSION_PROGRAM; - state->description = description; - strcpy(state->description, prefix); - strcat(state->description, cmd); - self->name = state->description; + self->code = ARCHIVE_FILTER_PROGRAM; + self->name = state->description.s; state->out_buf = out_buf; state->out_buf_len = out_buf_len; - if ((state->child = __archive_create_child(cmd, - &state->child_stdin, &state->child_stdout)) == -1) { + child = __archive_create_child(cmd, &state->child_stdin, + &state->child_stdout); + if (child == -1) { + free(state->out_buf); + free(state); + archive_set_error(&self->archive->archive, EINVAL, + "Can't initialize filter; unable to run program \"%s\"", + cmd); + return (ARCHIVE_FATAL); + } +#if defined(_WIN32) && !defined(__CYGWIN__) + state->child = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, child); + if (state->child == NULL) { + child_stop(self, state); free(state->out_buf); free(state); archive_set_error(&self->archive->archive, EINVAL, - "Can't initialize filter; unable to run program \"%s\"", cmd); + "Can't initialize filter; unable to run program \"%s\"", + cmd); return (ARCHIVE_FATAL); } +#else + state->child = child; +#endif self->data = state; self->read = program_filter_read; @@ -467,10 +509,8 @@ program_filter_close(struct archive_read_filter *self) /* Release our private data. */ free(state->out_buf); - free(state->description); + archive_string_free(&state->description); free(state); return (e); } - -#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */ diff --git a/libarchive/archive_read_support_filter_rpm.c b/libarchive/archive_read_support_filter_rpm.c index 1b3e124204f1..e7e58e51f3b0 100644 --- a/libarchive/archive_read_support_filter_rpm.c +++ b/libarchive/archive_read_support_filter_rpm.c @@ -85,6 +85,7 @@ archive_read_support_filter_rpm(struct archive *_a) return (ARCHIVE_FATAL); bidder->data = NULL; + bidder->name = "rpm"; bidder->bid = rpm_bidder_bid; bidder->init = rpm_bidder_init; bidder->options = NULL; @@ -137,7 +138,7 @@ rpm_bidder_init(struct archive_read_filter *self) { struct rpm *rpm; - self->code = ARCHIVE_COMPRESSION_RPM; + self->code = ARCHIVE_FILTER_RPM; self->name = "rpm"; self->read = rpm_filter_read; self->skip = NULL; /* not supported */ diff --git a/libarchive/archive_read_support_filter_uu.c b/libarchive/archive_read_support_filter_uu.c index a75ef756081b..471771b6f469 100644 --- a/libarchive/archive_read_support_filter_uu.c +++ b/libarchive/archive_read_support_filter_uu.c @@ -56,6 +56,7 @@ struct uudecode { #define ST_READ_UU 1 #define ST_UUEND 2 #define ST_READ_BASE64 3 +#define ST_IGNORE 4 }; static int uudecode_bidder_bid(struct archive_read_filter_bidder *, @@ -88,6 +89,7 @@ archive_read_support_filter_uu(struct archive *_a) return (ARCHIVE_FATAL); bidder->data = NULL; + bidder->name = "uu"; bidder->bid = uudecode_bidder_bid; bidder->init = uudecode_bidder_init; bidder->options = NULL; @@ -377,7 +379,7 @@ uudecode_bidder_init(struct archive_read_filter *self) void *out_buff; void *in_buff; - self->code = ARCHIVE_COMPRESSION_UU; + self->code = ARCHIVE_FILTER_UU; self->name = "uu"; self->read = uudecode_filter_read; self->skip = NULL; /* not supported */ @@ -470,6 +472,10 @@ read_more: total = 0; out = uudecode->out_buff; ravail = avail_in; + if (uudecode->state == ST_IGNORE) { + used = avail_in; + goto finish; + } if (uudecode->in_cnt) { /* * If there is remaining data which is saved by @@ -485,12 +491,18 @@ read_more: uudecode->in_cnt = 0; } for (;used < avail_in; d += llen, used += llen) { - int l, body; + int64_t l, body; b = d; len = get_line(b, avail_in - used, &nl); if (len < 0) { /* Non-ascii character is found. */ + if (uudecode->state == ST_FIND_HEAD && + (uudecode->total > 0 || total > 0)) { + uudecode->state = ST_IGNORE; + used = avail_in; + goto finish; + } archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC, "Insufficient compressed data"); @@ -507,7 +519,7 @@ read_more: return (ARCHIVE_FATAL); if (uudecode->in_buff != b) memmove(uudecode->in_buff, b, len); - uudecode->in_cnt = len; + uudecode->in_cnt = (int)len; if (total == 0) { /* Do not return 0; it means end-of-file. * We should try to read bytes more. */ @@ -545,7 +557,7 @@ read_more: break; case ST_READ_UU: if (total + len * 2 > OUT_BUFF_SIZE) - break; + goto finish; body = len - nl; if (!uuchar[*b] || body <= 0) { archive_set_error(&self->archive->archive, @@ -611,7 +623,7 @@ read_more: break; case ST_READ_BASE64: if (total + len * 2 > OUT_BUFF_SIZE) - break; + goto finish; l = len - nl; if (l >= 3 && b[0] == '=' && b[1] == '=' && b[2] == '=') { @@ -657,8 +669,10 @@ read_more: break; } } - - __archive_read_filter_consume(self->upstream, ravail); +finish: + if (ravail < avail_in) + used -= avail_in - ravail; + __archive_read_filter_consume(self->upstream, used); *buff = uudecode->out_buff; uudecode->total += total; diff --git a/libarchive/archive_read_support_filter_xz.c b/libarchive/archive_read_support_filter_xz.c index cf762a46d984..15824b1d0974 100644 --- a/libarchive/archive_read_support_filter_xz.c +++ b/libarchive/archive_read_support_filter_xz.c @@ -136,6 +136,7 @@ archive_read_support_filter_xz(struct archive *_a) return (ARCHIVE_FATAL); bidder->data = NULL; + bidder->name = "xz"; bidder->bid = xz_bidder_bid; bidder->init = xz_bidder_init; bidder->options = NULL; @@ -144,7 +145,7 @@ archive_read_support_filter_xz(struct archive *_a) return (ARCHIVE_OK); #else archive_set_error(_a, ARCHIVE_ERRNO_MISC, - "Using external unxz program for xz decompression"); + "Using external xz program for xz decompression"); return (ARCHIVE_WARN); #endif } @@ -170,6 +171,7 @@ archive_read_support_filter_lzma(struct archive *_a) return (ARCHIVE_FATAL); bidder->data = NULL; + bidder->name = "lzma"; bidder->bid = lzma_bidder_bid; bidder->init = lzma_bidder_init; bidder->options = NULL; @@ -180,7 +182,7 @@ archive_read_support_filter_lzma(struct archive *_a) return (ARCHIVE_OK); #else archive_set_error(_a, ARCHIVE_ERRNO_MISC, - "Using external unlzma program for lzma decompression"); + "Using external lzma program for lzma decompression"); return (ARCHIVE_WARN); #endif } @@ -207,6 +209,7 @@ archive_read_support_filter_lzip(struct archive *_a) return (ARCHIVE_FATAL); bidder->data = NULL; + bidder->name = "lzip"; bidder->bid = lzip_bidder_bid; bidder->init = lzip_bidder_init; bidder->options = NULL; @@ -415,7 +418,7 @@ lzip_bidder_bid(struct archive_read_filter_bidder *self, static int xz_bidder_init(struct archive_read_filter *self) { - self->code = ARCHIVE_COMPRESSION_XZ; + self->code = ARCHIVE_FILTER_XZ; self->name = "xz"; return (xz_lzma_bidder_init(self)); } @@ -423,7 +426,7 @@ xz_bidder_init(struct archive_read_filter *self) static int lzma_bidder_init(struct archive_read_filter *self) { - self->code = ARCHIVE_COMPRESSION_LZMA; + self->code = ARCHIVE_FILTER_LZMA; self->name = "lzma"; return (xz_lzma_bidder_init(self)); } @@ -431,7 +434,7 @@ lzma_bidder_init(struct archive_read_filter *self) static int lzip_bidder_init(struct archive_read_filter *self) { - self->code = ARCHIVE_COMPRESSION_LZIP; + self->code = ARCHIVE_FILTER_LZIP; self->name = "lzip"; return (xz_lzma_bidder_init(self)); } @@ -518,7 +521,7 @@ xz_lzma_bidder_init(struct archive_read_filter *self) state->stream.avail_out = state->out_block_size; state->crc32 = 0; - if (self->code == ARCHIVE_COMPRESSION_LZIP) { + if (self->code == ARCHIVE_FILTER_LZIP) { /* * We have to read a lzip header and use it to initialize * compression library, thus we cannot initialize the @@ -530,7 +533,7 @@ xz_lzma_bidder_init(struct archive_read_filter *self) state->in_stream = 1; /* Initialize compression library. */ - if (self->code == ARCHIVE_COMPRESSION_XZ) + if (self->code == ARCHIVE_FILTER_XZ) ret = lzma_stream_decoder(&(state->stream), LZMA_MEMLIMIT,/* memlimit */ LZMA_CONCATENATED); @@ -730,7 +733,7 @@ xz_filter_read(struct archive_read_filter *self, const void **p) *p = NULL; else { *p = state->out_block; - if (self->code == ARCHIVE_COMPRESSION_LZIP) { + if (self->code == ARCHIVE_FILTER_LZIP) { state->crc32 = lzma_crc32(state->out_block, decompressed, state->crc32); if (state->eof) { @@ -778,7 +781,7 @@ lzma_bidder_init(struct archive_read_filter *self) struct private_data *state; ssize_t ret, avail_in; - self->code = ARCHIVE_COMPRESSION_LZMA; + self->code = ARCHIVE_FILTER_LZMA; self->name = "lzma"; state = (struct private_data *)calloc(sizeof(*state), 1); @@ -941,11 +944,11 @@ lzma_bidder_init(struct archive_read_filter *self) { int r; - r = __archive_read_program(self, "unlzma"); + r = __archive_read_program(self, "lzma -d -qq"); /* Note: We set the format here even if __archive_read_program() * above fails. We do, after all, know what the format is * even if we weren't able to read it. */ - self->code = ARCHIVE_COMPRESSION_LZMA; + self->code = ARCHIVE_FILTER_LZMA; self->name = "lzma"; return (r); } @@ -958,11 +961,11 @@ xz_bidder_init(struct archive_read_filter *self) { int r; - r = __archive_read_program(self, "unxz"); + r = __archive_read_program(self, "xz -d -qq"); /* Note: We set the format here even if __archive_read_program() * above fails. We do, after all, know what the format is * even if we weren't able to read it. */ - self->code = ARCHIVE_COMPRESSION_XZ; + self->code = ARCHIVE_FILTER_XZ; self->name = "xz"; return (r); } @@ -972,11 +975,11 @@ lzip_bidder_init(struct archive_read_filter *self) { int r; - r = __archive_read_program(self, "unlzip"); + r = __archive_read_program(self, "lzip -d -q"); /* Note: We set the format here even if __archive_read_program() * above fails. We do, after all, know what the format is * even if we weren't able to read it. */ - self->code = ARCHIVE_COMPRESSION_LZIP; + self->code = ARCHIVE_FILTER_LZIP; self->name = "lzip"; return (r); } diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c index 39a46ed579b9..194b8d51c96a 100644 --- a/libarchive/archive_read_support_format_7zip.c +++ b/libarchive/archive_read_support_format_7zip.c @@ -409,6 +409,7 @@ archive_read_support_format_7zip(struct archive *_a) archive_read_format_7zip_read_header, archive_read_format_7zip_read_data, archive_read_format_7zip_read_data_skip, + NULL, archive_read_format_7zip_cleanup); if (r != ARCHIVE_OK) @@ -684,8 +685,8 @@ archive_read_format_7zip_read_header(struct archive_read *a, symname[symsize] = '\0'; archive_entry_copy_symlink(entry, (const char *)symname); - free(symname); } + free(symname); archive_entry_set_size(entry, 0); } @@ -709,16 +710,15 @@ archive_read_format_7zip_read_data(struct archive_read *a, if (zip->pack_stream_bytes_unconsumed) read_consume(a); + *offset = zip->entry_offset; + *size = 0; + *buff = NULL; /* * If we hit end-of-entry last time, clean up and return * ARCHIVE_EOF this time. */ - if (zip->end_of_entry) { - *offset = zip->entry_offset; - *size = 0; - *buff = NULL; + if (zip->end_of_entry) return (ARCHIVE_EOF); - } bytes = read_stream(a, buff, (size_t)zip->entry_bytes_remaining, 0); @@ -736,7 +736,8 @@ archive_read_format_7zip_read_data(struct archive_read *a, /* Update checksum */ if ((zip->entry->flg & CRC32_IS_SET) && bytes) - zip->entry_crc32 = crc32(zip->entry_crc32, *buff, bytes); + zip->entry_crc32 = crc32(zip->entry_crc32, *buff, + (unsigned)bytes); /* If we hit the end, swallow any end-of-data marker. */ if (zip->end_of_entry) { @@ -1363,9 +1364,9 @@ decompress(struct archive_read *a, struct _7zip *zip, #ifdef HAVE_ZLIB_H case _7Z_DEFLATE: zip->stream.next_in = (Bytef *)(uintptr_t)t_next_in; - zip->stream.avail_in = t_avail_in; + zip->stream.avail_in = (uInt)t_avail_in; zip->stream.next_out = t_next_out; - zip->stream.avail_out = t_avail_out; + zip->stream.avail_out = (uInt)t_avail_out; r = inflate(&(zip->stream), 0); switch (r) { case Z_STREAM_END: /* Found end of stream. */ @@ -1607,9 +1608,10 @@ read_Digests(struct archive_read *a, struct _7z_digests *d, size_t num) const unsigned char *p; unsigned i; + if (num == 0) + return (-1); memset(d, 0, sizeof(*d)); - d->defineds = malloc(num); if (d->defineds == NULL) return (-1); @@ -2687,7 +2689,7 @@ header_bytes(struct archive_read *a, size_t rbytes) } /* Update checksum */ - zip->header_crc32 = crc32(zip->header_crc32, p, rbytes); + zip->header_crc32 = crc32(zip->header_crc32, p, (unsigned)rbytes); return (p); } @@ -2966,16 +2968,19 @@ extract_pack_stream(struct archive_read *a, size_t minimum) * Expand the uncompressed buffer up to * the minimum size. */ - zip->uncompressed_buffer_size = minimum + 1023; - zip->uncompressed_buffer_size &= ~0x3ff; - zip->uncompressed_buffer = - realloc(zip->uncompressed_buffer, - zip->uncompressed_buffer_size); - if (zip->uncompressed_buffer == NULL) { + void *p; + size_t new_size; + + new_size = minimum + 1023; + new_size &= ~0x3ff; + p = realloc(zip->uncompressed_buffer, new_size); + if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "No memory for 7-Zip decompression"); return (ARCHIVE_FATAL); } + zip->uncompressed_buffer = (unsigned char *)p; + zip->uncompressed_buffer_size = new_size; } /* * Move unconsumed bytes to the head. @@ -3095,7 +3100,7 @@ read_stream(struct archive_read *a, const void **buff, size_t size, { struct _7zip *zip = (struct _7zip *)a->format->data; uint64_t skip_bytes = 0; - int r; + ssize_t r; if (zip->uncompressed_buffer_bytes_remaining == 0) { if (zip->pack_stream_inbytes_remaining > 0) { @@ -3346,8 +3351,10 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, for (i = 0; i < 3; i++) { const struct _7z_coder *coder = scoder[i]; - if ((r = seek_pack(a)) < 0) + if ((r = seek_pack(a)) < 0) { + free(b[0]); free(b[1]); free(b[2]); return (r); + } if (sunpack[i] == (uint64_t)-1) zip->folder_outbytes_remaining = @@ -3356,13 +3363,16 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, zip->folder_outbytes_remaining = sunpack[i]; r = init_decompression(a, zip, coder, NULL); - if (r != ARCHIVE_OK) + if (r != ARCHIVE_OK) { + free(b[0]); free(b[1]); free(b[2]); return (ARCHIVE_FATAL); + } /* Allocate memory for the decorded data of a sub * stream. */ b[i] = malloc((size_t)zip->folder_outbytes_remaining); if (b[i] == NULL) { + free(b[0]); free(b[1]); free(b[2]); archive_set_error(&a->archive, ENOMEM, "No memory for 7-Zip decompression"); return (ARCHIVE_FATAL); @@ -3370,14 +3380,18 @@ setup_decode_folder(struct archive_read *a, struct _7z_folder *folder, /* Extract a sub stream. */ while (zip->pack_stream_inbytes_remaining > 0) { - r = extract_pack_stream(a, 0); - if (r < 0) + r = (int)extract_pack_stream(a, 0); + if (r < 0) { + free(b[0]); free(b[1]); free(b[2]); return (r); + } bytes = get_uncompressed_data(a, &buff, zip->uncompressed_buffer_bytes_remaining, 0); - if (bytes < 0) + if (bytes < 0) { + free(b[0]); free(b[1]); free(b[2]); return ((int)bytes); + } memcpy(b[i]+s[i], buff, bytes); s[i] += bytes; if (zip->pack_stream_bytes_unconsumed) @@ -3557,7 +3571,7 @@ x86_Convert(struct _7zip *zip, uint8_t *data, size_t size) } zip->bcj_prevPosT = prevPosT; zip->bcj_prevMask = prevMask; - zip->bcj_ip += bufferPos; + zip->bcj_ip += (uint32_t)bufferPos; return (bufferPos); } @@ -3701,7 +3715,7 @@ Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize) ((uint32_t)v[1] << 16) | ((uint32_t)v[2] << 8) | ((uint32_t)v[3])) - - ((uint32_t)zip->bcj2_outPos + outPos + 4); + ((uint32_t)zip->bcj2_outPos + (uint32_t)outPos + 4); out[0] = (uint8_t)dest; out[1] = (uint8_t)(dest >> 8); out[2] = (uint8_t)(dest >> 16); @@ -3716,7 +3730,7 @@ Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize) */ zip->odd_bcj_size = 4 -i; for (; i < 4; i++) { - j = i - 4 + zip->odd_bcj_size; + j = i - 4 + (unsigned)zip->odd_bcj_size; zip->odd_bcj[j] = out[i]; } break; diff --git a/libarchive/archive_read_support_format_ar.c b/libarchive/archive_read_support_format_ar.c index 9feb547dbbea..40be18c0cce2 100644 --- a/libarchive/archive_read_support_format_ar.c +++ b/libarchive/archive_read_support_format_ar.c @@ -121,6 +121,7 @@ archive_read_support_format_ar(struct archive *_a) archive_read_format_ar_read_header, archive_read_format_ar_read_data, archive_read_format_ar_skip, + NULL, archive_read_format_ar_cleanup); if (r != ARCHIVE_OK) { diff --git a/libarchive/archive_read_support_format_cab.c b/libarchive/archive_read_support_format_cab.c index aa0152a17140..3c9f94ca6ba3 100644 --- a/libarchive/archive_read_support_format_cab.c +++ b/libarchive/archive_read_support_format_cab.c @@ -382,6 +382,7 @@ archive_read_support_format_cab(struct archive *_a) 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); if (r != ARCHIVE_OK) @@ -539,7 +540,7 @@ truncated_error(struct archive_read *a) return (ARCHIVE_FATAL); } -static int +static ssize_t cab_strnlen(const unsigned char *p, size_t maxlen) { size_t i; @@ -550,7 +551,7 @@ cab_strnlen(const unsigned char *p, size_t maxlen) } if (i > maxlen) return (-1);/* invalid */ - return (i); + return ((ssize_t)i); } /* Read bytes as much as remaining. */ @@ -626,8 +627,9 @@ cab_read_header(struct archive_read *a) struct cab *cab; struct cfheader *hd; size_t bytes, used; + ssize_t len; int64_t skip; - int err, i, len; + int err, i; int cur_folder, prev_folder; uint32_t offset32; @@ -1066,13 +1068,13 @@ static uint32_t cab_checksum_cfdata_4(const void *p, size_t bytes, uint32_t seed) { const unsigned char *b; - int u32num; + unsigned u32num; uint32_t sum; - u32num = bytes / 4; + u32num = (unsigned)bytes / 4; sum = seed; b = p; - while (--u32num >= 0) { + for (;u32num > 0; --u32num) { sum ^= archive_le32dec(b); b += 4; } @@ -1485,7 +1487,7 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) * cast to remove 'const'. */ cab->stream.next_in = (Bytef *)(uintptr_t)d; - cab->stream.avail_in = bytes_avail; + cab->stream.avail_in = (uInt)bytes_avail; cab->stream.total_in = 0; /* Cut out a tow-byte MSZIP signature(0x43, 0x4b). */ @@ -1506,7 +1508,7 @@ cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail) *avail = ARCHIVE_FATAL; return (NULL); } - mszip -= bytes_avail; + mszip -= (int)bytes_avail; continue; } if (mszip == 1 && cab->stream.next_in[0] != 0x4b) @@ -1935,7 +1937,7 @@ cab_read_data(struct archive_read *a, const void **buff, ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA"); return (ARCHIVE_FATAL); } else - return (bytes_avail); + return ((int)bytes_avail); } if (bytes_avail > cab->entry_bytes_remaining) bytes_avail = (ssize_t)cab->entry_bytes_remaining; @@ -2001,7 +2003,8 @@ archive_read_format_cab_read_data_skip(struct archive_read *a) /* 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) + if (cab->entry_cffolder->comptype == COMPTYPE_NONE && + cab->entry_cfdata != NULL) cab->entry_cfdata->unconsumed = 0; /* This entry is finished and done. */ @@ -2198,7 +2201,7 @@ lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset) size_t i = b - (unsigned char *)p; int32_t cp, displacement, value; - cp = offset + i; + cp = (int32_t)(offset + (uint32_t)i); value = archive_le32dec(&b[1]); if (value >= -cp && value < (int32_t)ds->translation_size) { if (value >= 0) @@ -2584,7 +2587,7 @@ lzx_read_blocks(struct lzx_stream *strm, int last) goto failed; return (ARCHIVE_OK); } - l = ds->block_bytes_avail; + 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) @@ -2746,8 +2749,8 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) 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 *outp = strm->next_out; - unsigned char *endp = outp + strm->avail_out; + 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; @@ -2781,10 +2784,10 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) ds->position_slot = position_slot; ds->r0 = r0; ds->r1 = r1; ds->r2 = r2; ds->w_pos = w_pos; - strm->avail_out = endp - outp; + strm->avail_out = endp - noutp; return (ARCHIVE_EOF); } - if (outp >= endp) + if (noutp >= endp) /* Output buffer is empty. */ goto next_data; @@ -2818,7 +2821,7 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) w_buff[w_pos] = c; w_pos = (w_pos + 1) & w_mask; /* Store the decoded code to output buffer. */ - *outp++ = c; + *noutp++ = c; block_bytes_avail--; } /* @@ -2963,22 +2966,22 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) if (l > w_size - w_pos) l = w_size - w_pos; } - if (outp + l >= endp) - l = endp - outp; + 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(outp, s, l); + memcpy(noutp, s, l); } else { unsigned char *d; int li; d = w_buff + w_pos; for (li = 0; li < l; li++) - outp[li] = d[li] = s[li]; + noutp[li] = d[li] = s[li]; } - outp += l; + noutp += l; copy_pos = (copy_pos + l) & w_mask; w_pos = (w_pos + l) & w_mask; block_bytes_avail -= l; @@ -2986,7 +2989,7 @@ lzx_decode_blocks(struct lzx_stream *strm, int last) /* A copy of current pattern ended. */ break; copy_len -= l; - if (outp >= endp) { + if (noutp >= endp) { /* Output buffer is empty. */ state = ST_COPY; goto next_data; @@ -3009,7 +3012,7 @@ next_data: ds->r0 = r0; ds->r1 = r1; ds->r2 = r2; ds->state = state; ds->w_pos = w_pos; - strm->avail_out = endp - outp; + strm->avail_out = endp - noutp; return (ARCHIVE_OK); } @@ -3126,7 +3129,7 @@ lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits) hf->bitlen = calloc(len_size, sizeof(hf->bitlen[0])); if (hf->bitlen == NULL) return (ARCHIVE_FATAL); - hf->len_size = len_size; + hf->len_size = (int)len_size; } else memset(hf->bitlen, 0, len_size * sizeof(hf->bitlen[0])); if (hf->tbl == NULL) { @@ -3134,7 +3137,7 @@ lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits) bits = tbl_bits; else bits = HTBL_BITS; - hf->tbl = malloc((1 << bits) * sizeof(hf->tbl[0])); + hf->tbl = malloc(((size_t)1 << bits) * sizeof(hf->tbl[0])); if (hf->tbl == NULL) return (ARCHIVE_FATAL); hf->tbl_bits = tbl_bits; diff --git a/libarchive/archive_read_support_format_cpio.c b/libarchive/archive_read_support_format_cpio.c index ff29e1ffd973..819f4a4f531e 100644 --- a/libarchive/archive_read_support_format_cpio.c +++ b/libarchive/archive_read_support_format_cpio.c @@ -242,6 +242,7 @@ archive_read_support_format_cpio(struct archive *_a) archive_read_format_cpio_read_header, archive_read_format_cpio_read_data, archive_read_format_cpio_skip, + NULL, archive_read_format_cpio_cleanup); if (r != ARCHIVE_OK) diff --git a/libarchive/archive_read_support_format_empty.c b/libarchive/archive_read_support_format_empty.c index 3dc2196cb99e..366073820975 100644 --- a/libarchive/archive_read_support_format_empty.c +++ b/libarchive/archive_read_support_format_empty.c @@ -53,6 +53,7 @@ archive_read_support_format_empty(struct archive *_a) archive_read_format_empty_read_header, archive_read_format_empty_read_data, NULL, + NULL, NULL); return (r); diff --git a/libarchive/archive_read_support_format_iso9660.c b/libarchive/archive_read_support_format_iso9660.c index d38c7cfe7e42..914bc71dfed5 100644 --- a/libarchive/archive_read_support_format_iso9660.c +++ b/libarchive/archive_read_support_format_iso9660.c @@ -1,7 +1,7 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2009 Andreas Henriksson <andreas@fatal.se> - * Copyright (c) 2009-2011 Michihiro NAKAJIMA + * Copyright (c) 2009-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -374,6 +374,8 @@ struct iso9660 { size_t utf16be_path_len; unsigned char *utf16be_previous_path; size_t utf16be_previous_path_len; + /* Null buufer used in bidder to improve its performance. */ + unsigned char null[2048]; }; static int archive_read_format_iso9660_bid(struct archive_read *, int); @@ -475,6 +477,7 @@ archive_read_support_format_iso9660(struct archive *_a) archive_read_format_iso9660_read_header, archive_read_format_iso9660_read_data, archive_read_format_iso9660_read_data_skip, + NULL, archive_read_format_iso9660_cleanup); if (r != ARCHIVE_OK) { @@ -588,6 +591,23 @@ archive_read_format_iso9660_options(struct archive_read *a, } static int +isNull(struct iso9660 *iso9660, const unsigned char *h, unsigned offset, +unsigned bytes) +{ + + while (bytes >= sizeof(iso9660->null)) { + if (!memcmp(iso9660->null, h + offset, sizeof(iso9660->null))) + return (0); + offset += sizeof(iso9660->null); + bytes -= sizeof(iso9660->null); + } + if (bytes) + return memcmp(iso9660->null, h + offset, bytes) == 0; + else + return (1); +} + +static int isBootRecord(struct iso9660 *iso9660, const unsigned char *h) { (void)iso9660; /* UNUSED */ @@ -632,8 +652,6 @@ isVolumePartition(struct iso9660 *iso9660, const unsigned char *h) static int isVDSetTerminator(struct iso9660 *iso9660, const unsigned char *h) { - int i; - (void)iso9660; /* UNUSED */ /* Type of the Volume Descriptor Set Terminator must be 255. */ @@ -645,9 +663,8 @@ isVDSetTerminator(struct iso9660 *iso9660, const unsigned char *h) return (0); /* Reserved field must be 0. */ - for (i = 7; i < 2048; ++i) - if (h[i] != 0) - return (0); + if (!isNull(iso9660, h, 7, 2048-7)) + return (0); return (1); } @@ -708,7 +725,6 @@ isSVD(struct iso9660 *iso9660, const unsigned char *h) ssize_t logical_block_size; int32_t volume_block; int32_t location; - int i; (void)iso9660; /* UNUSED */ @@ -717,15 +733,12 @@ isSVD(struct iso9660 *iso9660, const unsigned char *h) return (0); /* Reserved field must be 0. */ - for (i = 0; i < SVD_reserved1_size; ++i) - if (h[SVD_reserved1_offset + i] != 0) - return (0); - for (i = 0; i < SVD_reserved2_size; ++i) - if (h[SVD_reserved2_offset + i] != 0) - return (0); - for (i = 0; i < SVD_reserved3_size; ++i) - if (h[SVD_reserved3_offset + i] != 0) - return (0); + if (!isNull(iso9660, h, SVD_reserved1_offset, SVD_reserved1_size)) + return (0); + if (!isNull(iso9660, h, SVD_reserved2_offset, SVD_reserved2_size)) + return (0); + if (!isNull(iso9660, h, SVD_reserved3_offset, SVD_reserved3_size)) + return (0); /* File structure version must be 1 for ISO9660/ECMA119. */ if (h[SVD_file_structure_version_offset] != 1) @@ -771,7 +784,6 @@ isEVD(struct iso9660 *iso9660, const unsigned char *h) ssize_t logical_block_size; int32_t volume_block; int32_t location; - int i; (void)iso9660; /* UNUSED */ @@ -788,14 +800,12 @@ isEVD(struct iso9660 *iso9660, const unsigned char *h) return (0); /* Reserved field must be 0. */ - for (i = 0; i < PVD_reserved2_size; ++i) - if (h[PVD_reserved2_offset + i] != 0) - return (0); + if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size)) + return (0); /* Reserved field must be 0. */ - for (i = 0; i < PVD_reserved3_size; ++i) - if (h[PVD_reserved3_offset + i] != 0) - return (0); + if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size)) + return (0); /* Logical block size must be > 0. */ /* I've looked at Ecma 119 and can't find any stronger @@ -830,14 +840,12 @@ isEVD(struct iso9660 *iso9660, const unsigned char *h) return (0); /* Reserved field must be 0. */ - for (i = 0; i < PVD_reserved4_size; ++i) - if (h[PVD_reserved4_offset + i] != 0) - return (0); + if (!isNull(iso9660, h, PVD_reserved4_offset, PVD_reserved4_size)) + return (0); /* Reserved field must be 0. */ - for (i = 0; i < PVD_reserved5_size; ++i) - if (h[PVD_reserved5_offset + i] != 0) - return (0); + if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size)) + return (0); /* Read Root Directory Record in Volume Descriptor. */ p = h + PVD_root_directory_record_offset; @@ -869,14 +877,12 @@ isPVD(struct iso9660 *iso9660, const unsigned char *h) return (0); /* Reserved field must be 0. */ - for (i = 0; i < PVD_reserved2_size; ++i) - if (h[PVD_reserved2_offset + i] != 0) - return (0); + if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size)) + return (0); /* Reserved field must be 0. */ - for (i = 0; i < PVD_reserved3_size; ++i) - if (h[PVD_reserved3_offset + i] != 0) - return (0); + if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size)) + return (0); /* Logical block size must be > 0. */ /* I've looked at Ecma 119 and can't find any stronger @@ -919,9 +925,8 @@ isPVD(struct iso9660 *iso9660, const unsigned char *h) return (0); /* Reserved field must be 0. */ - for (i = 0; i < PVD_reserved5_size; ++i) - if (h[PVD_reserved5_offset + i] != 0) - return (0); + if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size)) + return (0); /* XXX TODO: Check other values for sanity; reject more * malformed PVDs. XXX */ @@ -934,8 +939,10 @@ isPVD(struct iso9660 *iso9660, const unsigned char *h) if (!iso9660->primary.location) { iso9660->logical_block_size = logical_block_size; iso9660->volume_block = volume_block; - iso9660->volume_size = logical_block_size * (uint64_t)volume_block; - iso9660->primary.location = archive_le32dec(p + DR_extent_offset); + iso9660->volume_size = + logical_block_size * (uint64_t)volume_block; + iso9660->primary.location = + archive_le32dec(p + DR_extent_offset); iso9660->primary.size = archive_le32dec(p + DR_size_offset); } @@ -951,6 +958,12 @@ read_children(struct archive_read *a, struct file_info *parent) size_t step, skip_size; iso9660 = (struct iso9660 *)(a->format->data); + /* flush any remaining bytes from the last round to ensure + * we're positioned */ + if (iso9660->entry_bytes_unconsumed) { + __archive_read_consume(a, iso9660->entry_bytes_unconsumed); + iso9660->entry_bytes_unconsumed = 0; + } if (iso9660->current_position > parent->offset) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Ignoring out-of-order directory (%s) %jd > %jd", @@ -1060,101 +1073,112 @@ read_children(struct archive_read *a, struct file_info *parent) } static int -archive_read_format_iso9660_read_header(struct archive_read *a, - struct archive_entry *entry) +choose_volume(struct archive_read *a, struct iso9660 *iso9660) { - struct iso9660 *iso9660; struct file_info *file; - int r, rd_r = ARCHIVE_OK; - - iso9660 = (struct iso9660 *)(a->format->data); + int64_t skipsize; + struct vd *vd; + const void *block; + char seenJoliet; - if (!a->archive.archive_format) { - a->archive.archive_format = ARCHIVE_FORMAT_ISO9660; - a->archive.archive_format_name = "ISO9660"; + vd = &(iso9660->primary); + if (!iso9660->opt_support_joliet) + iso9660->seenJoliet = 0; + if (iso9660->seenJoliet && + vd->location > iso9660->joliet.location) + /* This condition is unlikely; by way of caution. */ + vd = &(iso9660->joliet); + + skipsize = LOGICAL_BLOCK_SIZE * vd->location; + skipsize = __archive_read_consume(a, skipsize); + if (skipsize < 0) + return ((int)skipsize); + iso9660->current_position = skipsize; + + block = __archive_read_ahead(a, vd->size, NULL); + if (block == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Failed to read full block when scanning " + "ISO9660 directory list"); + return (ARCHIVE_FATAL); } - if (iso9660->current_position == 0) { - int64_t skipsize; - struct vd *vd; - const void *block; - char seenJoliet; - - vd = &(iso9660->primary); - if (!iso9660->opt_support_joliet) - iso9660->seenJoliet = 0; - if (iso9660->seenJoliet && - vd->location > iso9660->joliet.location) - /* This condition is unlikely; by way of caution. */ - vd = &(iso9660->joliet); + /* + * While reading Root Directory, flag seenJoliet must be zero to + * avoid converting special name 0x00(Current Directory) and + * next byte to UCS2. + */ + seenJoliet = iso9660->seenJoliet;/* Save flag. */ + iso9660->seenJoliet = 0; + file = parse_file_info(a, NULL, block); + if (file == NULL) + return (ARCHIVE_FATAL); + iso9660->seenJoliet = seenJoliet; + + /* + * If the iso image has both RockRidge and Joliet, we preferentially + * use RockRidge Extensions rather than Joliet ones. + */ + if (vd == &(iso9660->primary) && iso9660->seenRockridge + && iso9660->seenJoliet) + iso9660->seenJoliet = 0; + if (vd == &(iso9660->primary) && !iso9660->seenRockridge + && iso9660->seenJoliet) { + /* Switch reading data from primary to joliet. */ + vd = &(iso9660->joliet); skipsize = LOGICAL_BLOCK_SIZE * vd->location; + skipsize -= iso9660->current_position; skipsize = __archive_read_consume(a, skipsize); if (skipsize < 0) return ((int)skipsize); - iso9660->current_position = skipsize; + iso9660->current_position += skipsize; block = __archive_read_ahead(a, vd->size, NULL); if (block == NULL) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Failed to read full block when scanning " "ISO9660 directory list"); return (ARCHIVE_FATAL); } - - /* - * While reading Root Directory, flag seenJoliet - * must be zero to avoid converting special name - * 0x00(Current Directory) and next byte to UCS2. - */ - seenJoliet = iso9660->seenJoliet;/* Save flag. */ iso9660->seenJoliet = 0; file = parse_file_info(a, NULL, block); if (file == NULL) return (ARCHIVE_FATAL); iso9660->seenJoliet = seenJoliet; - if (vd == &(iso9660->primary) && iso9660->seenRockridge - && iso9660->seenJoliet) - /* - * If iso image has RockRidge and Joliet, - * we use RockRidge Extensions. - */ - iso9660->seenJoliet = 0; - if (vd == &(iso9660->primary) && !iso9660->seenRockridge - && iso9660->seenJoliet) { - /* Switch reading data from primary to joliet. */ - vd = &(iso9660->joliet); - skipsize = LOGICAL_BLOCK_SIZE * vd->location; - skipsize -= iso9660->current_position; - skipsize = __archive_read_consume(a, skipsize); - if (skipsize < 0) - return ((int)skipsize); - iso9660->current_position += skipsize; - - block = __archive_read_ahead(a, vd->size, NULL); - if (block == NULL) { - archive_set_error(&a->archive, - ARCHIVE_ERRNO_MISC, - "Failed to read full block when scanning " - "ISO9660 directory list"); - return (ARCHIVE_FATAL); - } - iso9660->seenJoliet = 0; - file = parse_file_info(a, NULL, block); - if (file == NULL) - return (ARCHIVE_FATAL); - iso9660->seenJoliet = seenJoliet; - } - /* Store the root directory in the pending list. */ - if (add_entry(a, iso9660, file) != ARCHIVE_OK) - return (ARCHIVE_FATAL); - if (iso9660->seenRockridge) { - a->archive.archive_format = - ARCHIVE_FORMAT_ISO9660_ROCKRIDGE; - a->archive.archive_format_name = - "ISO9660 with Rockridge extensions"; - } + } + + /* Store the root directory in the pending list. */ + if (add_entry(a, iso9660, file) != ARCHIVE_OK) + return (ARCHIVE_FATAL); + if (iso9660->seenRockridge) { + a->archive.archive_format = ARCHIVE_FORMAT_ISO9660_ROCKRIDGE; + a->archive.archive_format_name = + "ISO9660 with Rockridge extensions"; + } + + return (ARCHIVE_OK); +} + +static int +archive_read_format_iso9660_read_header(struct archive_read *a, + struct archive_entry *entry) +{ + struct iso9660 *iso9660; + struct file_info *file; + int r, rd_r = ARCHIVE_OK; + + iso9660 = (struct iso9660 *)(a->format->data); + + if (!a->archive.archive_format) { + a->archive.archive_format = ARCHIVE_FORMAT_ISO9660; + a->archive.archive_format_name = "ISO9660"; + } + + if (iso9660->current_position == 0) { + r = choose_volume(a, iso9660); + if (r != ARCHIVE_OK) + return (r); } file = NULL;/* Eliminate a warning. */ @@ -1227,14 +1251,14 @@ archive_read_format_iso9660_read_header(struct archive_read *a, } iso9660->entry_bytes_remaining = file->size; - iso9660->entry_sparse_offset = 0; /* Offset for sparse-file-aware clients. */ + /* Offset for sparse-file-aware clients. */ + iso9660->entry_sparse_offset = 0; if (file->offset + file->size > iso9660->volume_size) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "File is beyond end-of-media: %s", archive_entry_pathname(entry)); iso9660->entry_bytes_remaining = 0; - iso9660->entry_sparse_offset = 0; return (ARCHIVE_WARN); } @@ -1286,36 +1310,33 @@ archive_read_format_iso9660_read_header(struct archive_read *a, iso9660->previous_pathname.s); archive_entry_unset_size(entry); iso9660->entry_bytes_remaining = 0; - iso9660->entry_sparse_offset = 0; return (rd_r); } - /* Except for the hardlink case above, if the offset of the - * next entry is before our current position, we can't seek - * backwards to extract it, so issue a warning. Note that - * this can only happen if this entry was added to the heap - * after we passed this offset, that is, only if the directory - * mentioning this entry is later than the body of the entry. - * Such layouts are very unusual; most ISO9660 writers lay out - * and record all directory information first, then store - * all file bodies. */ - /* TODO: Someday, libarchive's I/O core will support optional - * seeking. When that day comes, this code should attempt to - * seek and only return the error if the seek fails. That - * will give us support for whacky ISO images that require - * seeking while retaining the ability to read almost all ISO - * images in a streaming fashion. */ if ((file->mode & AE_IFMT) != AE_IFDIR && file->offset < iso9660->current_position) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Ignoring out-of-order file @%jx (%s) %jd < %jd", - (intmax_t)file->number, - iso9660->pathname.s, - (intmax_t)file->offset, - (intmax_t)iso9660->current_position); - iso9660->entry_bytes_remaining = 0; - iso9660->entry_sparse_offset = 0; - return (ARCHIVE_WARN); + int64_t r64; + + r64 = __archive_read_seek(a, file->offset, SEEK_SET); + if (r64 != (int64_t)file->offset) { + /* We can't seek backwards to extract it, so issue + * a warning. Note that this can only happen if + * this entry was added to the heap after we passed + * this offset, that is, only if the directory + * mentioning this entry is later than the body of + * the entry. Such layouts are very unusual; most + * ISO9660 writers lay out and record all directory + * information first, then store all file bodies. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Ignoring out-of-order file @%jx (%s) %jd < %jd", + (intmax_t)file->number, + iso9660->pathname.s, + (intmax_t)file->offset, + (intmax_t)iso9660->current_position); + iso9660->entry_bytes_remaining = 0; + return (ARCHIVE_WARN); + } + iso9660->current_position = (uint64_t)r64; } /* Initialize zisofs variables. */ @@ -1356,7 +1377,6 @@ archive_read_format_iso9660_read_header(struct archive_read *a, archive_entry_set_nlink(entry, 2 + file->subdirs); /* Directory data has been read completely. */ iso9660->entry_bytes_remaining = 0; - iso9660->entry_sparse_offset = 0; } if (rd_r != ARCHIVE_OK) @@ -1426,7 +1446,7 @@ zisofs_read_data(struct archive_read *a, zisofs->block_pointers_size = xsize; /* Allocate uncompressed data buffer. */ - xsize = 1UL << zisofs->pz_log2_bs; + xsize = (size_t)1UL << zisofs->pz_log2_bs; if (zisofs->uncompressed_buffer_size < xsize) { if (zisofs->uncompressed_buffer != NULL) free(zisofs->uncompressed_buffer); @@ -1563,9 +1583,10 @@ zisofs_read_data(struct archive_read *a, if (avail > zisofs->block_avail) zisofs->stream.avail_in = zisofs->block_avail; else - zisofs->stream.avail_in = avail; + zisofs->stream.avail_in = (uInt)avail; zisofs->stream.next_out = zisofs->uncompressed_buffer; - zisofs->stream.avail_out = zisofs->uncompressed_buffer_size; + zisofs->stream.avail_out = + (uInt)zisofs->uncompressed_buffer_size; r = inflate(&zisofs->stream, 0); switch (r) { @@ -1580,7 +1601,7 @@ zisofs_read_data(struct archive_read *a, uncompressed_size = zisofs->uncompressed_buffer_size - zisofs->stream.avail_out; avail -= zisofs->stream.next_in - p; - zisofs->block_avail -= zisofs->stream.next_in - p; + zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p); } next_data: bytes_read -= avail; @@ -1590,7 +1611,7 @@ next_data: iso9660->entry_sparse_offset += uncompressed_size; iso9660->entry_bytes_remaining -= bytes_read; iso9660->current_position += bytes_read; - zisofs->pz_offset += bytes_read; + zisofs->pz_offset += (uint32_t)bytes_read; iso9660->entry_bytes_unconsumed += bytes_read; return (ARCHIVE_OK); @@ -1873,9 +1894,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, if (iso9660->opt_support_rockridge) { if (parent == NULL && rr_end - rr_start >= 7) { p = rr_start; - if (p[0] == 'S' && p[1] == 'P' - && p[2] == 7 && p[3] == 1 - && p[4] == 0xBE && p[5] == 0xEF) { + if (memcmp(p, "SP\x07\x01\xbe\xef", 6) == 0) { /* * SP extension stores the suspOffset * (Number of bytes to skip between @@ -1935,6 +1954,7 @@ parse_file_info(struct archive_read *a, struct file_info *parent, if (iso9660->seenRockridge) { if (parent != NULL && parent->parent == NULL && (flags & 0x02) && iso9660->rr_moved == NULL && + file->name.s && (strcmp(file->name.s, "rr_moved") == 0 || strcmp(file->name.s, ".rr_moved") == 0)) { iso9660->rr_moved = file; @@ -2067,14 +2087,9 @@ parse_rockridge(struct archive_read *a, struct file_info *file, int data_length = p[2] - 4; int version = p[3]; - /* - * Yes, each 'if' here does test p[0] again. - * Otherwise, the fall-through handling to catch - * unsupported extensions doesn't work. - */ switch(p[0]) { case 'C': - if (p[0] == 'C' && p[1] == 'E') { + if (p[1] == 'E') { if (version == 1 && data_length == 24) { /* * CE extension comprises: @@ -2092,53 +2107,42 @@ parse_rockridge(struct archive_read *a, struct file_info *file, != ARCHIVE_OK) return (ARCHIVE_FATAL); } - break; } - if (p[0] == 'C' && p[1] == 'L') { + else if (p[1] == 'L') { if (version == 1 && data_length == 8) { file->cl_offset = (uint64_t) iso9660->logical_block_size * (uint64_t)archive_le32dec(data); iso9660->seenRockridge = 1; } - break; } - /* FALLTHROUGH */ + break; case 'N': - if (p[0] == 'N' && p[1] == 'M') { + if (p[1] == 'M') { if (version == 1) { parse_rockridge_NM1(file, data, data_length); iso9660->seenRockridge = 1; } - break; } - /* FALLTHROUGH */ + break; case 'P': - if (p[0] == 'P' && p[1] == 'D') { - /* - * PD extension is padding; - * contents are always ignored. - */ - break; - } - if (p[0] == 'P' && p[1] == 'L') { - /* - * PL extension won't appear; - * contents are always ignored. - */ - break; - } - if (p[0] == 'P' && p[1] == 'N') { + /* + * PD extension is padding; + * contents are always ignored. + * + * PL extension won't appear; + * contents are always ignored. + */ + if (p[1] == 'N') { if (version == 1 && data_length == 16) { file->rdev = toi(data,4); file->rdev <<= 32; file->rdev |= toi(data + 8, 4); iso9660->seenRockridge = 1; } - break; } - if (p[0] == 'P' && p[1] == 'X') { + else if (p[1] == 'X') { /* * PX extension comprises: * 8 bytes for mode, @@ -2165,35 +2169,31 @@ parse_rockridge(struct archive_read *a, struct file_info *file, = toi(data + 32, 4); iso9660->seenRockridge = 1; } - break; } - /* FALLTHROUGH */ + break; case 'R': - if (p[0] == 'R' && p[1] == 'E' && version == 1) { + if (p[1] == 'E' && version == 1) { file->re = 1; iso9660->seenRockridge = 1; - break; } - if (p[0] == 'R' && p[1] == 'R' && version == 1) { + else if (p[1] == 'R' && version == 1) { /* * RR extension comprises: * one byte flag value * This extension is obsolete, * so contents are always ignored. */ - break; } - /* FALLTHROUGH */ + break; case 'S': - if (p[0] == 'S' && p[1] == 'L') { + if (p[1] == 'L') { if (version == 1) { parse_rockridge_SL1(file, data, data_length); iso9660->seenRockridge = 1; } - break; } - if (p[0] == 'S' && p[1] == 'T' + else if (p[1] == 'T' && data_length == 0 && version == 1) { /* * ST extension marks end of this @@ -2208,32 +2208,27 @@ parse_rockridge(struct archive_read *a, struct file_info *file, iso9660->seenRockridge = 0; return (ARCHIVE_OK); } + break; case 'T': - if (p[0] == 'T' && p[1] == 'F') { + if (p[1] == 'F') { if (version == 1) { parse_rockridge_TF1(file, data, data_length); iso9660->seenRockridge = 1; } - break; } - /* FALLTHROUGH */ + break; case 'Z': - if (p[0] == 'Z' && p[1] == 'F') { + if (p[1] == 'F') { if (version == 1) parse_rockridge_ZF1(file, data, data_length); - break; } - /* FALLTHROUGH */ + break; default: - /* The FALLTHROUGHs above leave us here for - * any unsupported extension. */ break; } - - p += p[2]; } return (ARCHIVE_OK); @@ -2893,8 +2888,9 @@ next_cache_entry(struct archive_read *a, struct iso9660 *iso9660, fatal_rr: archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Failed to connect 'CL' pointer to 'RE' rr_moved pointer of" - "Rockridge extensions"); + "Failed to connect 'CL' pointer to 'RE' rr_moved pointer of " + "Rockridge extensions: current position = %jd, CL offset = %jd", + (intmax_t)iso9660->current_position, (intmax_t)file->cl_offset); return (ARCHIVE_FATAL); } @@ -3226,10 +3222,12 @@ dump_isodirrec(FILE *out, const unsigned char *isodirrec) fprintf(out, " ilv %d,", toi(isodirrec + DR_interleave_offset, DR_interleave_size)); fprintf(out, " seq %d,", - toi(isodirrec + DR_volume_sequence_number_offset, DR_volume_sequence_number_size)); + toi(isodirrec + DR_volume_sequence_number_offset, + DR_volume_sequence_number_size)); fprintf(out, " nl %d:", toi(isodirrec + DR_name_len_offset, DR_name_len_size)); fprintf(out, " `%.*s'", - toi(isodirrec + DR_name_len_offset, DR_name_len_size), isodirrec + DR_name_offset); + toi(isodirrec + DR_name_len_offset, DR_name_len_size), + isodirrec + DR_name_offset); } #endif diff --git a/libarchive/archive_read_support_format_lha.c b/libarchive/archive_read_support_format_lha.c index a92b072ab60e..f702949fb8c1 100644 --- a/libarchive/archive_read_support_format_lha.c +++ b/libarchive/archive_read_support_format_lha.c @@ -272,7 +272,7 @@ 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, int); + 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 **, @@ -319,6 +319,7 @@ archive_read_support_format_lha(struct archive *_a) 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); if (r != ARCHIVE_OK) @@ -1634,7 +1635,7 @@ lha_parse_linkname(struct archive_string *linkname, struct archive_string *pathname) { char * linkptr; - int symlen; + size_t symlen; linkptr = strchr(pathname->s, '|'); if (linkptr != NULL) { @@ -1689,12 +1690,12 @@ lha_win_time(uint64_t wintime, long *ns) } static unsigned char -lha_calcsum(unsigned char sum, const void *pp, int offset, int size) +lha_calcsum(unsigned char sum, const void *pp, int offset, size_t size) { unsigned char const *p = (unsigned char const *)pp; p += offset; - while (--size >= 0) + for (;size > 0; --size) sum += *p++; return (sum); } @@ -2019,7 +2020,7 @@ lzh_copy_from_window(struct lzh_stream *strm, struct lzh_dec *ds) copy_bytes = (size_t)strm->avail_out; memcpy(strm->next_out, ds->w_buff + ds->copy_pos, copy_bytes); - ds->copy_pos += copy_bytes; + ds->copy_pos += (int)copy_bytes; } else { if (ds->w_remaining <= strm->avail_out) copy_bytes = ds->w_remaining; @@ -2027,7 +2028,7 @@ lzh_copy_from_window(struct lzh_stream *strm, struct lzh_dec *ds) copy_bytes = (size_t)strm->avail_out; memcpy(strm->next_out, ds->w_buff + ds->w_size - ds->w_remaining, copy_bytes); - ds->w_remaining -= copy_bytes; + ds->w_remaining -= (int)copy_bytes; } strm->next_out += copy_bytes; strm->avail_out -= copy_bytes; @@ -2481,7 +2482,7 @@ lzh_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits) bits = tbl_bits; else bits = HTBL_BITS; - hf->tbl = malloc((1 << bits) * sizeof(hf->tbl[0])); + hf->tbl = malloc(((size_t)1 << bits) * sizeof(hf->tbl[0])); if (hf->tbl == NULL) return (ARCHIVE_FATAL); } @@ -2491,7 +2492,7 @@ lzh_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits) if (hf->tree == NULL) return (ARCHIVE_FATAL); } - hf->len_size = len_size; + hf->len_size = (int)len_size; hf->tbl_bits = tbl_bits; return (ARCHIVE_OK); } diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c index 6fb57629197f..c4e7021a869b 100644 --- a/libarchive/archive_read_support_format_mtree.c +++ b/libarchive/archive_read_support_format_mtree.c @@ -1,7 +1,7 @@ /*- * Copyright (c) 2003-2007 Tim Kientzle * Copyright (c) 2008 Joerg Sonnenberger - * Copyright (c) 2011 Michihiro NAKAJIMA + * Copyright (c) 2011-2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -55,6 +55,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_mtree.c 2011 #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 @@ -69,6 +72,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_mtree.c 2011 #define MTREE_HAS_UNAME 0x0400 #define MTREE_HAS_OPTIONAL 0x0800 +#define MTREE_HAS_NOCHANGE 0x1000 /* FreeBSD specific */ struct mtree_option { struct mtree_option *next; @@ -103,6 +107,7 @@ struct mtree { 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 int mtree_bid(struct archive_read *, int); static int parse_file(struct archive_read *, struct archive_entry *, struct mtree *, struct mtree_entry *, int *); @@ -200,7 +205,7 @@ archive_read_support_format_mtree(struct archive *_a) mtree->fd = -1; r = __archive_read_register_format(a, mtree, "mtree", - mtree_bid, NULL, read_header, read_data, skip, cleanup); + mtree_bid, NULL, read_header, read_data, skip, NULL, cleanup); if (r != ARCHIVE_OK) free(mtree); @@ -349,7 +354,7 @@ bid_keycmp(const char *p, const char *key, ssize_t len) * Returns the length of a detected keyword. * Returns 0 if any keywords were not found. */ -static ssize_t +static int bid_keyword(const char *p, ssize_t len) { static const char *keys_c[] = { @@ -368,7 +373,7 @@ bid_keyword(const char *p, ssize_t len) "md5", "md5digest", "mode", NULL }; static const char *keys_no[] = { - "nlink", "optional", NULL + "nlink", "nochange", "optional", NULL }; static const char *keys_r[] = { "rmd160", "rmd160digest", NULL @@ -419,7 +424,7 @@ bid_keyword(const char *p, ssize_t len) * When "unset" is specified, expects a set of "<space characters>keyword". */ static int -bid_keyword_list(const char *p, ssize_t len, int unset) +bid_keyword_list(const char *p, ssize_t len, int unset, int last_is_path) { int l; int keycnt = 0; @@ -437,8 +442,10 @@ bid_keyword_list(const char *p, ssize_t len, int unset) break; if (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r')) break; - if (!blank) /* No blank character. */ + 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); @@ -473,7 +480,7 @@ bid_keyword_list(const char *p, ssize_t len, int unset) } static int -bid_entry(const char *p, ssize_t len) +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] = { @@ -500,22 +507,60 @@ bid_entry(const char *p, ssize_t len) 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 = len; + const char *pp = p; + *last_is_path = 0; /* * Skip the path-name which is quoted. */ - while (len > 0 && *p != ' ' && *p != '\t') { - if (!safe_char[*(const unsigned char *)p]) - return (-1); - ++p; - --len; + while (ll > 0 && *pp != ' ' &&*pp != '\t' && *pp != '\r' && + *pp != '\n') { + if (!safe_char[*(const unsigned char *)pp]) { + f = 0; + break; + } + ++pp; + --ll; ++f; } - /* If a path-name was not found, returns error. */ - if (f == 0) - return (-1); + /* If a path-name was not found at the first, try to check + * a mtree format ``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; + + /* Do not accept multi lines for form D. */ + if (pb-2 >= p && + pb[-1] == '\\' && (pb[-2] == ' ' || pb[-2] == '\t')) + return (-1); + if (pb-1 >= p && pb[-1] == '\\') + return (-1); - return (bid_keyword_list(p, len, 0)); + 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 @@ -525,14 +570,11 @@ mtree_bid(struct archive_read *a, int best_bid) { const char *signature = "#mtree"; const char *p; - ssize_t avail, ravail; - ssize_t len, nl; - int detected_bytes = 0, entry_cnt = 0, multiline = 0; (void)best_bid; /* UNUSED */ /* Now let's look at the actual header and see if it matches. */ - p = __archive_read_ahead(a, strlen(signature), &avail); + p = __archive_read_ahead(a, strlen(signature), NULL); if (p == NULL) return (-1); @@ -542,6 +584,24 @@ mtree_bid(struct archive_read *a, int best_bid) /* * 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); @@ -566,7 +626,7 @@ mtree_bid(struct archive_read *a, int best_bid) } else { /* A continuance line; the terminal * character of previous line was '\' character. */ - if (bid_keyword_list(p, len, 0) <= 0) + if (bid_keyword_list(p, len, 0, 0) <= 0) break; if (multiline == 1) detected_bytes += len; @@ -581,9 +641,25 @@ mtree_bid(struct archive_read *a, int best_bid) continue; } if (p[0] != '/') { - if (bid_entry(p, len) >= 0) { + int last_is_path, keywords; + + keywords = bid_entry(p, len, nl, &last_is_path); + if (keywords >= 0) { detected_bytes += len; - if (p[len-nl-1] == '\\') + 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 { @@ -596,13 +672,13 @@ mtree_bid(struct archive_read *a, int best_bid) } else break; } else if (strncmp(p, "/set", 4) == 0) { - if (bid_keyword_list(p+4, len-4, 0) <= 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 (strncmp(p, "/unset", 6) == 0) { - if (bid_keyword_list(p+6, len-6, 1) <= 0) + if (bid_keyword_list(p+6, len-6, 1, 0) <= 0) break; /* This line continues. */ if (p[len-nl-1] == '\\') @@ -614,8 +690,13 @@ mtree_bid(struct archive_read *a, int best_bid) p += len; avail -= len; } - if (entry_cnt >= MAX_BID_ENTRY || (entry_cnt > 0 && len == 0)) + 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); } @@ -739,12 +820,12 @@ process_global_unset(struct archive_read *a, static int process_add_entry(struct archive_read *a, struct mtree *mtree, - struct mtree_option **global, const char *line, - struct mtree_entry **last_entry) + struct mtree_option **global, const char *line, ssize_t line_len, + struct mtree_entry **last_entry, int is_form_d) { struct mtree_entry *entry; struct mtree_option *iter; - const char *next, *eq; + const char *next, *eq, *name, *end; size_t len; int r; @@ -765,17 +846,46 @@ process_add_entry(struct archive_read *a, struct mtree *mtree, (*last_entry)->next = entry; *last_entry = entry; - len = strcspn(line, " \t\r\n"); + if (is_form_d) { + /* + * This form places the file name as last parameter. + */ + name = line + line_len -1; + while (line_len > 0) { + if (*name != '\r' && *name != '\n' && + *name != '\t' && *name != ' ') + break; + name--; + line_len--; + } + len = 0; + while (line_len > 0) { + if (*name == '\r' || *name == '\n' || + *name == '\t' || *name == ' ') { + name++; + break; + } + name--; + line_len--; + len++; + } + end = name; + } else { + len = strcspn(line, " \t\r\n"); + name = line; + line += len; + end = line + line_len; + } + if ((entry->name = malloc(len + 1)) == NULL) { archive_set_error(&a->archive, errno, "Can't allocate memory"); return (ARCHIVE_FATAL); } - memcpy(entry->name, line, len); + memcpy(entry->name, name, len); entry->name[len] = '\0'; parse_escapes(entry->name, entry); - line += len; for (iter = *global; iter != NULL; iter = iter->next) { r = add_option(a, &entry->options, iter->value, strlen(iter->value)); @@ -787,6 +897,8 @@ process_add_entry(struct archive_read *a, struct mtree *mtree, 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, '='); @@ -811,7 +923,7 @@ read_mtree(struct archive_read *a, struct mtree *mtree) char *p; struct mtree_option *global; struct mtree_entry *last_entry; - int r; + int r, is_form_d; mtree->archive_format = ARCHIVE_FORMAT_MTREE; mtree->archive_format_name = "mtree"; @@ -819,6 +931,8 @@ read_mtree(struct archive_read *a, struct mtree *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) { @@ -828,7 +942,7 @@ read_mtree(struct archive_read *a, struct mtree *mtree) } if (len < 0) { free_options(global); - return (len); + return ((int)len); } /* Leading whitespace is never significant, ignore it. */ while (*p == ' ' || *p == '\t') { @@ -841,8 +955,8 @@ read_mtree(struct archive_read *a, struct mtree *mtree) if (*p == '\r' || *p == '\n' || *p == '\0') continue; if (*p != '/') { - r = process_add_entry(a, mtree, &global, p, - &last_entry); + r = process_add_entry(a, mtree, &global, p, len, + &last_entry, is_form_d); } else if (strncmp(p, "/set", 4) == 0) { if (p[4] != ' ' && p[4] != '\t') break; @@ -1008,7 +1122,8 @@ parse_file(struct archive_read *a, struct archive_entry *entry, if (archive_entry_filetype(entry) == AE_IFREG || archive_entry_filetype(entry) == AE_IFDIR) { - mtree->fd = open(path, O_RDONLY | O_BINARY); + 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)) { @@ -1091,15 +1206,19 @@ parse_file(struct archive_read *a, struct archive_entry *entry, * if it wasn't already parsed from the specification. */ if (st != NULL) { - if ((parsed_kws & MTREE_HAS_DEVICE) == 0 && + 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) + 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) + 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) { + 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); @@ -1119,11 +1238,14 @@ parse_file(struct archive_read *a, struct archive_entry *entry, archive_entry_set_mtime(entry, st->st_mtime, 0); #endif } - if ((parsed_kws & MTREE_HAS_NLINK) == 0) + 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) + 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) + 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); @@ -1213,6 +1335,10 @@ parse_keyword(struct archive_read *a, struct mtree *mtree, 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); diff --git a/libarchive/archive_read_support_format_rar.c b/libarchive/archive_read_support_format_rar.c index 1e5c5fa72b30..99c57a0fc0eb 100644 --- a/libarchive/archive_read_support_format_rar.c +++ b/libarchive/archive_read_support_format_rar.c @@ -199,6 +199,13 @@ struct lzss int64_t position; }; +struct data_block_offsets +{ + int64_t header_size; + int64_t start_offset; + int64_t end_offset; +}; + struct rar { /* Entries from main RAR header */ @@ -217,6 +224,7 @@ struct rar long mnsec; mode_t mode; char *filename; + char *filename_save; size_t filename_allocated; /* File header optional entries */ @@ -234,6 +242,7 @@ struct rar int64_t bytes_uncopied; int64_t offset; int64_t offset_outgoing; + int64_t offset_seek; char valid; unsigned int unp_offset; unsigned int unp_buffer_size; @@ -243,6 +252,10 @@ struct rar char entry_eof; unsigned long crc_calculated; int found_first_header; + char has_endarc_header; + struct data_block_offsets *dbo; + unsigned int cursor; + unsigned int nodes; /* LZSS members */ struct huffman_code maincode; @@ -301,6 +314,8 @@ static int archive_read_format_rar_read_header(struct archive_read *, static int archive_read_format_rar_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_rar_read_data_skip(struct archive_read *a); +static int64_t archive_read_format_rar_seek_data(struct archive_read *, int64_t, + int); static int archive_read_format_rar_cleanup(struct archive_read *); /* Support functions */ @@ -328,6 +343,7 @@ static int make_table_recurse(struct archive_read *, struct huffman_code *, int, static int64_t expand(struct archive_read *, int64_t); static int copy_from_lzss_window(struct archive_read *, const void **, int64_t, int); +static const void *rar_read_ahead(struct archive_read *, size_t, ssize_t *); /* * Bit stream reader. @@ -449,11 +465,9 @@ rar_br_fillup(struct archive_read *a, struct rar_br *br) __archive_read_consume(a, rar->bytes_unconsumed); rar->bytes_unconsumed = 0; } - br->next_in = __archive_read_ahead(a, 1, &(br->avail_in)); + br->next_in = rar_read_ahead(a, 1, &(br->avail_in)); if (br->next_in == NULL) return (0); - if (br->avail_in > rar->bytes_remaining) - br->avail_in = (ssize_t)rar->bytes_remaining; if (br->avail_in == 0) return (0); } @@ -473,15 +487,13 @@ rar_br_preparation(struct archive_read *a, struct rar_br *br) struct rar *rar = (struct rar *)(a->format->data); if (rar->bytes_remaining > 0) { - br->next_in = __archive_read_ahead(a, 1, &(br->avail_in)); + br->next_in = rar_read_ahead(a, 1, &(br->avail_in)); if (br->next_in == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); return (ARCHIVE_FATAL); } - if (br->avail_in > rar->bytes_remaining) - br->avail_in = (ssize_t)rar->bytes_remaining; if (br->cache_avail == 0) (void)rar_br_fillup(a, br); } @@ -642,6 +654,7 @@ archive_read_support_format_rar(struct archive *_a) archive_read_format_rar_read_header, archive_read_format_rar_read_data, archive_read_format_rar_read_data_skip, + archive_read_format_rar_seek_data, archive_read_format_rar_cleanup); if (r != ARCHIVE_OK) @@ -844,13 +857,6 @@ archive_read_format_rar_read_header(struct archive_read *a, sizeof(rar->reserved2)); } - if (rar->main_flags & MHD_VOLUME || - rar->main_flags & MHD_FIRSTVOLUME) - { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "RAR volume support unavailable."); - return (ARCHIVE_FATAL); - } if (rar->main_flags & MHD_PASSWORD) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, @@ -858,7 +864,7 @@ archive_read_format_rar_read_header(struct archive_read *a, return (ARCHIVE_FATAL); } - crc32_val = crc32(0, (const unsigned char *)p + 2, skip - 2); + crc32_val = crc32(0, (const unsigned char *)p + 2, (unsigned)skip - 2); if ((crc32_val & 0xffff) != archive_le16dec(p)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header CRC error"); @@ -875,6 +881,7 @@ archive_read_format_rar_read_header(struct archive_read *a, case SUB_HEAD: case PROTECT_HEAD: case SIGN_HEAD: + case ENDARC_HEAD: flags = archive_le16dec(p + 3); skip = archive_le16dec(p + 5); if (skip < 7) { @@ -900,13 +907,15 @@ archive_read_format_rar_read_header(struct archive_read *a, p = h; } - crc32_val = crc32(0, (const unsigned char *)p + 2, skip - 2); + crc32_val = crc32(0, (const unsigned char *)p + 2, (unsigned)skip - 2); if ((crc32_val & 0xffff) != archive_le16dec(p)) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Header CRC error"); return (ARCHIVE_FATAL); } __archive_read_consume(a, skip); + if (head_type == ENDARC_HEAD) + return (ARCHIVE_EOF); break; case NEWSUB_HEAD: @@ -914,9 +923,6 @@ archive_read_format_rar_read_header(struct archive_read *a, return ret; break; - case ENDARC_HEAD: - return (ARCHIVE_EOF); - default: archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file"); @@ -938,10 +944,12 @@ archive_read_format_rar_read_data(struct archive_read *a, const void **buff, rar->bytes_unconsumed = 0; } - if (rar->entry_eof) { + if (rar->entry_eof || rar->offset_seek >= rar->unp_size) { *buff = NULL; *size = 0; *offset = rar->offset; + if (*offset < rar->unp_size) + *offset = rar->unp_size; return (ARCHIVE_EOF); } @@ -975,6 +983,7 @@ archive_read_format_rar_read_data_skip(struct archive_read *a) { struct rar *rar; int64_t bytes_skipped; + int ret; rar = (struct rar *)(a->format->data); @@ -989,9 +998,179 @@ archive_read_format_rar_read_data_skip(struct archive_read *a) if (bytes_skipped < 0) return (ARCHIVE_FATAL); } + + /* Compressed data to skip must be read from each header in a multivolume + * archive. + */ + if (rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER) + { + ret = archive_read_format_rar_read_header(a, a->entry); + if (ret == (ARCHIVE_EOF)) + ret = archive_read_format_rar_read_header(a, a->entry); + if (ret != (ARCHIVE_OK)) + return ret; + return archive_read_format_rar_read_data_skip(a); + } + return (ARCHIVE_OK); } +static int64_t +archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset, + int whence) +{ + int64_t client_offset, ret; + unsigned int i; + struct rar *rar = (struct rar *)(a->format->data); + + if (rar->compression_method == COMPRESS_METHOD_STORE) + { + /* Modify the offset for use with SEEK_SET */ + switch (whence) + { + case SEEK_CUR: + client_offset = rar->offset_seek; + break; + case SEEK_END: + client_offset = rar->unp_size; + break; + case SEEK_SET: + default: + client_offset = 0; + } + client_offset += offset; + if (client_offset < 0) + { + /* Can't seek past beginning of data block */ + return -1; + } + else if (client_offset > rar->unp_size) + { + /* + * Set the returned offset but only seek to the end of + * the data block. + */ + rar->offset_seek = client_offset; + client_offset = rar->unp_size; + } + + client_offset += rar->dbo[0].start_offset; + i = 0; + while (i < rar->cursor) + { + i++; + client_offset += rar->dbo[i].start_offset - rar->dbo[i-1].end_offset; + } + if (rar->main_flags & MHD_VOLUME) + { + /* Find the appropriate offset among the multivolume archive */ + while (1) + { + if (client_offset < rar->dbo[rar->cursor].start_offset && + rar->file_flags & FHD_SPLIT_BEFORE) + { + /* Search backwards for the correct data block */ + if (rar->cursor == 0) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Attempt to seek past beginning of RAR data block"); + return (ARCHIVE_FAILED); + } + rar->cursor--; + client_offset -= rar->dbo[rar->cursor+1].start_offset - + rar->dbo[rar->cursor].end_offset; + if (client_offset < rar->dbo[rar->cursor].start_offset) + continue; + ret = __archive_read_seek(a, rar->dbo[rar->cursor].start_offset - + rar->dbo[rar->cursor].header_size, SEEK_SET); + if (ret < (ARCHIVE_OK)) + return ret; + ret = archive_read_format_rar_read_header(a, a->entry); + if (ret != (ARCHIVE_OK)) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Error during seek of RAR file"); + return (ARCHIVE_FAILED); + } + rar->cursor--; + break; + } + else if (client_offset > rar->dbo[rar->cursor].end_offset && + rar->file_flags & FHD_SPLIT_AFTER) + { + /* Search forward for the correct data block */ + rar->cursor++; + if (rar->cursor < rar->nodes && + client_offset > rar->dbo[rar->cursor].end_offset) + { + client_offset += rar->dbo[rar->cursor].start_offset - + rar->dbo[rar->cursor-1].end_offset; + continue; + } + rar->cursor--; + ret = __archive_read_seek(a, rar->dbo[rar->cursor].end_offset, + SEEK_SET); + if (ret < (ARCHIVE_OK)) + return ret; + ret = archive_read_format_rar_read_header(a, a->entry); + if (ret == (ARCHIVE_EOF)) + { + rar->has_endarc_header = 1; + ret = archive_read_format_rar_read_header(a, a->entry); + } + if (ret != (ARCHIVE_OK)) + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Error during seek of RAR file"); + return (ARCHIVE_FAILED); + } + client_offset += rar->dbo[rar->cursor].start_offset - + rar->dbo[rar->cursor-1].end_offset; + continue; + } + break; + } + } + + ret = __archive_read_seek(a, client_offset, SEEK_SET); + if (ret < (ARCHIVE_OK)) + return ret; + rar->bytes_remaining = rar->dbo[rar->cursor].end_offset - ret; + i = rar->cursor; + while (i > 0) + { + i--; + ret -= rar->dbo[i+1].start_offset - rar->dbo[i].end_offset; + } + ret -= rar->dbo[0].start_offset; + + /* Always restart reading the file after a seek */ + a->read_data_block = NULL; + a->read_data_offset = 0; + a->read_data_output_offset = 0; + a->read_data_remaining = 0; + rar->bytes_unconsumed = 0; + rar->offset = 0; + + /* + * If a seek past the end of file was requested, return the requested + * offset. + */ + if (ret == rar->unp_size && rar->offset_seek > rar->unp_size) + return rar->offset_seek; + + /* Return the new offset */ + rar->offset_seek = ret; + return rar->offset_seek; + } + else + { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Seeking of compressed RAR files is unsupported"); + } + return (ARCHIVE_FAILED); +} + static int archive_read_format_rar_cleanup(struct archive_read *a) { @@ -1000,6 +1179,8 @@ archive_read_format_rar_cleanup(struct archive_read *a) rar = (struct rar *)(a->format->data); free_codes(a); free(rar->filename); + free(rar->filename_save); + free(rar->dbo); free(rar->unp_buffer); free(rar->lzss.window); __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context, &g_szalloc); @@ -1138,6 +1319,8 @@ read_header(struct archive_read *a, struct archive_entry *entry, return (ARCHIVE_FATAL); } + rar->bytes_remaining = rar->packed_size; + /* TODO: RARv3 subblocks contain comments. For now the complete block is * consumed at the end. */ @@ -1177,13 +1360,13 @@ read_header(struct archive_read *a, struct archive_entry *entry, { if (filename_size != strlen(filename)) { - unsigned char highbyte, flagbits, flagbyte, offset; - unsigned fn_end; + unsigned char highbyte, flagbits, flagbyte; + unsigned fn_end, offset; end = filename_size; fn_end = filename_size * 2; filename_size = 0; - offset = strlen(filename) + 1; + offset = (unsigned)strlen(filename) + 1; highbyte = *(p + offset++); flagbits = 0; flagbyte = 0; @@ -1284,6 +1467,51 @@ read_header(struct archive_read *a, struct archive_entry *entry, p += filename_size; } + /* Split file in multivolume RAR. No more need to process header. */ + if (rar->filename_save && + !memcmp(rar->filename, rar->filename_save, filename_size + 1)) + { + __archive_read_consume(a, header_size - 7); + rar->cursor++; + if (rar->cursor >= rar->nodes) + { + rar->nodes++; + if ((rar->dbo = + realloc(rar->dbo, sizeof(*rar->dbo) * rar->nodes)) == NULL) + { + archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory."); + return (ARCHIVE_FATAL); + } + rar->dbo[rar->cursor].header_size = header_size; + rar->dbo[rar->cursor].start_offset = -1; + rar->dbo[rar->cursor].end_offset = -1; + } + if (rar->dbo[rar->cursor].start_offset < 0) + { + rar->dbo[rar->cursor].start_offset = a->filter->position; + rar->dbo[rar->cursor].end_offset = rar->dbo[rar->cursor].start_offset + + rar->packed_size; + } + return ret; + } + + rar->filename_save = (char*)realloc(rar->filename_save, + filename_size + 1); + memcpy(rar->filename_save, rar->filename, filename_size + 1); + + /* Set info for seeking */ + free(rar->dbo); + if ((rar->dbo = calloc(1, sizeof(*rar->dbo))) == NULL) + { + archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory."); + return (ARCHIVE_FATAL); + } + rar->dbo[0].header_size = header_size; + rar->dbo[0].start_offset = -1; + rar->dbo[0].end_offset = -1; + rar->cursor = 0; + rar->nodes = 1; + if (rar->file_flags & FHD_SALT) { if (p + 8 > endp) { @@ -1304,6 +1532,8 @@ read_header(struct archive_read *a, struct archive_entry *entry, } __archive_read_consume(a, header_size - 7); + rar->dbo[0].start_offset = a->filter->position; + rar->dbo[0].end_offset = rar->dbo[0].start_offset + rar->packed_size; switch(file_header.host_os) { @@ -1330,9 +1560,9 @@ read_header(struct archive_read *a, struct archive_entry *entry, return (ARCHIVE_FATAL); } - rar->bytes_remaining = rar->packed_size; rar->bytes_uncopied = rar->bytes_unconsumed = 0; rar->lzss.position = rar->offset = 0; + rar->offset_seek = 0; rar->dictionary_size = 0; rar->offset_outgoing = 0; rar->br.cache_avail = 0; @@ -1488,7 +1718,7 @@ read_symlink_stored(struct archive_read *a, struct archive_entry *entry, int ret = (ARCHIVE_OK); rar = (struct rar *)(a->format->data); - if ((h = __archive_read_ahead(a, (size_t)rar->packed_size, NULL)) == NULL) + if ((h = rar_read_ahead(a, (size_t)rar->packed_size, NULL)) == NULL) return (ARCHIVE_FATAL); p = h; @@ -1518,7 +1748,8 @@ read_data_stored(struct archive_read *a, const void **buff, size_t *size, ssize_t bytes_avail; rar = (struct rar *)(a->format->data); - if (rar->bytes_remaining == 0) + if (rar->bytes_remaining == 0 && + !(rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER)) { *buff = NULL; *size = 0; @@ -1532,23 +1763,23 @@ read_data_stored(struct archive_read *a, const void **buff, size_t *size, return (ARCHIVE_EOF); } - *buff = __archive_read_ahead(a, 1, &bytes_avail); + *buff = rar_read_ahead(a, 1, &bytes_avail); if (bytes_avail <= 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Truncated RAR file data"); return (ARCHIVE_FATAL); } - if (bytes_avail > rar->bytes_remaining) - bytes_avail = (ssize_t)rar->bytes_remaining; *size = bytes_avail; *offset = rar->offset; rar->offset += bytes_avail; + rar->offset_seek += bytes_avail; rar->bytes_remaining -= bytes_avail; rar->bytes_unconsumed = bytes_avail; /* Calculate File CRC. */ - rar->crc_calculated = crc32(rar->crc_calculated, *buff, bytes_avail); + rar->crc_calculated = crc32(rar->crc_calculated, *buff, + (unsigned)bytes_avail); return (ARCHIVE_OK); } @@ -1578,7 +1809,8 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, *offset = rar->offset_outgoing; rar->offset_outgoing += *size; /* Calculate File CRC. */ - rar->crc_calculated = crc32(rar->crc_calculated, *buff, *size); + rar->crc_calculated = crc32(rar->crc_calculated, *buff, + (unsigned)*size); rar->unp_offset = 0; return (ARCHIVE_OK); } @@ -1600,7 +1832,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, bs = rar->unp_buffer_size - rar->unp_offset; else bs = (size_t)rar->bytes_uncopied; - ret = copy_from_lzss_window(a, buff, rar->offset, bs); + ret = copy_from_lzss_window(a, buff, rar->offset, (int)bs); if (ret != ARCHIVE_OK) return (ret); rar->offset += bs; @@ -1611,7 +1843,8 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, *offset = rar->offset_outgoing; rar->offset_outgoing += *size; /* Calculate File CRC. */ - rar->crc_calculated = crc32(rar->crc_calculated, *buff, *size); + rar->crc_calculated = crc32(rar->crc_calculated, *buff, + (unsigned)*size); return (ret); } continue; @@ -1728,7 +1961,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, bs = rar->unp_buffer_size - rar->unp_offset; else bs = (size_t)rar->bytes_uncopied; - ret = copy_from_lzss_window(a, buff, rar->offset, bs); + ret = copy_from_lzss_window(a, buff, rar->offset, (int)bs); if (ret != ARCHIVE_OK) return (ret); rar->offset += bs; @@ -1744,7 +1977,7 @@ read_data_compressed(struct archive_read *a, const void **buff, size_t *size, *offset = rar->offset_outgoing; rar->offset_outgoing += *size; /* Calculate File CRC. */ - rar->crc_calculated = crc32(rar->crc_calculated, *buff, *size); + rar->crc_calculated = crc32(rar->crc_calculated, *buff, (unsigned)*size); return ret; } @@ -1987,17 +2220,21 @@ parse_codes(struct archive_read *a) /* Seems as though dictionary sizes are not used. Even so, minimize * memory usage as much as possible. */ + void *new_window; + unsigned int new_size; + if (rar->unp_size >= DICTIONARY_MAX_SIZE) - rar->dictionary_size = DICTIONARY_MAX_SIZE; + new_size = DICTIONARY_MAX_SIZE; else - rar->dictionary_size = rar_fls((unsigned int)rar->unp_size) << 1; - rar->lzss.window = (unsigned char *)realloc(rar->lzss.window, - rar->dictionary_size); - if (rar->lzss.window == NULL) { + new_size = rar_fls((unsigned int)rar->unp_size) << 1; + new_window = realloc(rar->lzss.window, new_size); + if (new_window == NULL) { archive_set_error(&a->archive, ENOMEM, "Unable to allocate memory for uncompressed data."); return (ARCHIVE_FATAL); } + rar->lzss.window = (unsigned char *)new_window; + rar->dictionary_size = new_size; memset(rar->lzss.window, 0, rar->dictionary_size); rar->lzss.mask = rar->dictionary_size - 1; } @@ -2235,10 +2472,12 @@ add_value(struct archive_read *a, struct huffman_code *code, int value, static int new_node(struct huffman_code *code) { - code->tree = (struct huffman_tree_node *)realloc(code->tree, - (code->numentries + 1) * sizeof(*code->tree)); - if (code->tree == NULL) + void *new_tree; + + new_tree = realloc(code->tree, (code->numentries + 1) * sizeof(*code->tree)); + if (new_tree == NULL) return (-1); + code->tree = (struct huffman_tree_node *)new_tree; code->tree[code->numentries].branches[0] = -1; code->tree[code->numentries].branches[1] = -2; return 1; @@ -2253,8 +2492,8 @@ make_table(struct archive_read *a, struct huffman_code *code) code->tablesize = code->maxlength; code->table = - (struct huffman_table_entry *)malloc(sizeof(*code->table) - * (1 << code->tablesize)); + (struct huffman_table_entry *)calloc(1, sizeof(*code->table) + * ((size_t)1 << code->tablesize)); return make_table_recurse(a, code, 0, code->table, 0, code->tablesize); } @@ -2586,3 +2825,34 @@ copy_from_lzss_window(struct archive_read *a, const void **buffer, *buffer = NULL; return (ARCHIVE_OK); } + +static const void * +rar_read_ahead(struct archive_read *a, size_t min, ssize_t *avail) +{ + struct rar *rar = (struct rar *)(a->format->data); + const void *h = __archive_read_ahead(a, min, avail); + int ret; + if (avail) + { + if (a->read_data_is_posix_read && *avail > (ssize_t)a->read_data_requested) + *avail = a->read_data_requested; + if (*avail > rar->bytes_remaining) + *avail = (ssize_t)rar->bytes_remaining; + if (*avail < 0) + return NULL; + else if (*avail == 0 && rar->main_flags & MHD_VOLUME && + rar->file_flags & FHD_SPLIT_AFTER) + { + ret = archive_read_format_rar_read_header(a, a->entry); + if (ret == (ARCHIVE_EOF)) + { + rar->has_endarc_header = 1; + ret = archive_read_format_rar_read_header(a, a->entry); + } + if (ret != (ARCHIVE_OK)) + return NULL; + return rar_read_ahead(a, min, avail); + } + } + return h; +} diff --git a/libarchive/archive_read_support_format_raw.c b/libarchive/archive_read_support_format_raw.c index df2c00c96c32..843497878a36 100644 --- a/libarchive/archive_read_support_format_raw.c +++ b/libarchive/archive_read_support_format_raw.c @@ -77,6 +77,7 @@ archive_read_support_format_raw(struct archive *_a) archive_read_format_raw_read_header, archive_read_format_raw_read_data, archive_read_format_raw_read_data_skip, + NULL, archive_read_format_raw_cleanup); if (r != ARCHIVE_OK) free(info); @@ -157,7 +158,7 @@ archive_read_format_raw_read_data(struct archive_read *a, /* Record and return an error. */ *size = 0; *offset = info->offset; - return (avail); + return ((int)avail); } } diff --git a/libarchive/archive_read_support_format_tar.c b/libarchive/archive_read_support_format_tar.c index 4538331f3d05..e9523cb687c8 100644 --- a/libarchive/archive_read_support_format_tar.c +++ b/libarchive/archive_read_support_format_tar.c @@ -210,10 +210,10 @@ static int read_body_to_string(struct archive_read *, struct tar *, struct archive_string *, const void *h, size_t *); static int solaris_sparse_parse(struct archive_read *, struct tar *, struct archive_entry *, const char *); -static int64_t tar_atol(const char *, unsigned); -static int64_t tar_atol10(const char *, unsigned); -static int64_t tar_atol256(const char *, unsigned); -static int64_t tar_atol8(const char *, unsigned); +static int64_t tar_atol(const char *, size_t); +static int64_t tar_atol10(const char *, size_t); +static int64_t tar_atol256(const char *, size_t); +static int64_t tar_atol8(const char *, size_t); static int tar_read_header(struct archive_read *, struct tar *, struct archive_entry *, size_t *); static int tohex(int c); @@ -253,6 +253,7 @@ archive_read_support_format_tar(struct archive *_a) archive_read_format_tar_read_header, archive_read_format_tar_read_data, archive_read_format_tar_skip, + NULL, archive_read_format_tar_cleanup); if (r != ARCHIVE_OK) @@ -616,13 +617,14 @@ tar_read_header(struct archive_read *a, struct tar *tar, int err; const char *h; const struct archive_entry_header_ustar *header; + const struct archive_entry_header_gnutar *gnuheader; tar_flush_unconsumed(a, unconsumed); /* Read 512-byte header record */ h = __archive_read_ahead(a, 512, &bytes); if (bytes < 0) - return (bytes); + return ((int)bytes); if (bytes == 0) { /* EOF at a block boundary. */ /* Some writers do omit the block of nulls. <sigh> */ return (ARCHIVE_EOF); @@ -703,7 +705,8 @@ tar_read_header(struct archive_read *a, struct tar *tar, err = header_pax_extensions(a, tar, entry, h, unconsumed); break; default: - if (memcmp(header->magic, "ustar \0", 8) == 0) { + gnuheader = (const struct archive_entry_header_gnutar *)h; + if (memcmp(gnuheader->magic, "ustar \0", 8) == 0) { a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR; a->archive.archive_format_name = "GNU tar format"; err = header_gnutar(a, tar, entry, h, unconsumed); @@ -752,7 +755,7 @@ tar_read_header(struct archive_read *a, struct tar *tar, bytes_read = gnu_sparse_10_read(a, tar, unconsumed); tar->entry_bytes_remaining -= bytes_read; if (bytes_read < 0) - return (bytes_read); + return ((int)bytes_read); } else { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -2390,7 +2393,7 @@ solaris_sparse_parse(struct archive_read *a, struct tar *tar, * On read, this implementation supports both extensions. */ static int64_t -tar_atol(const char *p, unsigned char_cnt) +tar_atol(const char *p, size_t char_cnt) { /* * Technically, GNU tar considers a field to be in base-256 @@ -2407,70 +2410,55 @@ tar_atol(const char *p, unsigned char_cnt) * it does obey locale. */ static int64_t -tar_atol8(const char *p, unsigned char_cnt) +tar_atol_base_n(const char *p, size_t char_cnt, int base) { int64_t l, limit, last_digit_limit; - int digit, sign, base; + int digit, sign; - base = 8; limit = INT64_MAX / base; last_digit_limit = INT64_MAX % base; - while (*p == ' ' || *p == '\t') + /* the pointer will not be dereferenced if char_cnt is zero + * due to the way the && operator is evaulated. + */ + while (char_cnt != 0 && (*p == ' ' || *p == '\t')) { p++; - if (*p == '-') { + char_cnt--; + } + + sign = 1; + if (char_cnt != 0 && *p == '-') { sign = -1; p++; - } else - sign = 1; + char_cnt--; + } l = 0; - digit = *p - '0'; - while (digit >= 0 && digit < base && char_cnt-- > 0) { - if (l>limit || (l == limit && digit > last_digit_limit)) { - l = INT64_MAX; /* Truncate on overflow. */ - break; + if (char_cnt != 0) { + digit = *p - '0'; + while (digit >= 0 && digit < base && char_cnt != 0) { + if (l>limit || (l == limit && digit > last_digit_limit)) { + l = INT64_MAX; /* Truncate on overflow. */ + break; + } + l = (l * base) + digit; + digit = *++p - '0'; + char_cnt--; } - l = (l * base) + digit; - digit = *++p - '0'; } return (sign < 0) ? -l : l; } -/* - * 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 -tar_atol10(const char *p, unsigned char_cnt) +tar_atol8(const char *p, size_t char_cnt) { - int64_t l, limit, last_digit_limit; - int base, digit, sign; - - base = 10; - limit = INT64_MAX / base; - last_digit_limit = INT64_MAX % base; - - while (*p == ' ' || *p == '\t') - p++; - if (*p == '-') { - sign = -1; - p++; - } else - sign = 1; + return tar_atol_base_n(p, char_cnt, 8); +} - l = 0; - digit = *p - '0'; - while (digit >= 0 && digit < base && char_cnt-- > 0) { - if (l > limit || (l == limit && digit > last_digit_limit)) { - l = INT64_MAX; /* Truncate on overflow. */ - break; - } - l = (l * base) + digit; - digit = *++p - '0'; - } - return (sign < 0) ? -l : l; +static int64_t +tar_atol10(const char *p, size_t char_cnt) +{ + return tar_atol_base_n(p, char_cnt, 10); } /* @@ -2479,7 +2467,7 @@ tar_atol10(const char *p, unsigned char_cnt) * ignored. */ static int64_t -tar_atol256(const char *_p, unsigned char_cnt) +tar_atol256(const char *_p, size_t char_cnt) { int64_t l, upper_limit, lower_limit; const unsigned char *p = (const unsigned char *)_p; diff --git a/libarchive/archive_read_support_format_xar.c b/libarchive/archive_read_support_format_xar.c index 733011c262fe..780e749d7096 100644 --- a/libarchive/archive_read_support_format_xar.c +++ b/libarchive/archive_read_support_format_xar.c @@ -467,6 +467,7 @@ archive_read_support_format_xar(struct archive *_a) xar_read_header, xar_read_data, xar_read_data_skip, + NULL, xar_cleanup); if (r != ARCHIVE_OK) free(xar); diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c index 2055313a46b7..450a6f7da519 100644 --- a/libarchive/archive_read_support_format_zip.c +++ b/libarchive/archive_read_support_format_zip.c @@ -57,6 +57,7 @@ struct zip_entry { int64_t gid; int64_t uid; struct archive_entry *entry; + struct archive_string rsrcname; time_t mtime; time_t atime; time_t ctime; @@ -69,6 +70,7 @@ struct zip_entry { struct zip { /* Structural information about the archive. */ + int64_t end_of_central_directory_offset; int64_t central_directory_offset; size_t central_directory_size; size_t central_directory_entries; @@ -80,6 +82,7 @@ struct zip { struct zip_entry *zip_entries; struct zip_entry *entry; struct archive_rb_tree tree; + struct archive_rb_tree tree_rsrc; size_t unconsumed; @@ -120,29 +123,33 @@ struct zip { #define ZIP_STRONG_ENCRYPTED (1<<6) #define ZIP_UTF8_NAME (1<<11) -static int archive_read_format_zip_streamable_bid(struct archive_read *, int); -static int archive_read_format_zip_seekable_bid(struct archive_read *, int); +static int archive_read_format_zip_streamable_bid(struct archive_read *, + int); +static int archive_read_format_zip_seekable_bid(struct archive_read *, + int); static int archive_read_format_zip_options(struct archive_read *, const char *, const char *); static int archive_read_format_zip_cleanup(struct archive_read *); static int archive_read_format_zip_read_data(struct archive_read *, const void **, size_t *, int64_t *); static int archive_read_format_zip_read_data_skip(struct archive_read *a); -static int archive_read_format_zip_seekable_read_header(struct archive_read *, - struct archive_entry *); -static int archive_read_format_zip_streamable_read_header(struct archive_read *, - struct archive_entry *); +static int archive_read_format_zip_seekable_read_header( + struct archive_read *, struct archive_entry *); +static int archive_read_format_zip_streamable_read_header( + struct archive_read *, struct archive_entry *); +static ssize_t zip_get_local_file_header_size(struct archive_read *, size_t); #ifdef HAVE_ZLIB_H +static int zip_deflate_init(struct archive_read *, struct zip *); static int zip_read_data_deflate(struct archive_read *a, const void **buff, size_t *size, int64_t *offset); #endif static int zip_read_data_none(struct archive_read *a, const void **buff, size_t *size, int64_t *offset); static int zip_read_local_file_header(struct archive_read *a, - struct archive_entry *entry, struct zip *); + struct archive_entry *entry, struct zip *); static time_t zip_time(const char *); static const char *compression_name(int compression); -static void process_extra(const char *, size_t, struct zip_entry *); +static void process_extra(const char *, size_t, struct zip_entry *); int archive_read_support_format_zip_streamable(struct archive *); int archive_read_support_format_zip_seekable(struct archive *); @@ -173,6 +180,7 @@ archive_read_support_format_zip_streamable(struct archive *_a) archive_read_format_zip_streamable_read_header, archive_read_format_zip_read_data, archive_read_format_zip_read_data_skip, + NULL, archive_read_format_zip_cleanup); if (r != ARCHIVE_OK) @@ -206,6 +214,7 @@ archive_read_support_format_zip_seekable(struct archive *_a) archive_read_format_zip_seekable_read_header, archive_read_format_zip_read_data, archive_read_format_zip_read_data_skip, + NULL, archive_read_format_zip_cleanup); if (r != ARCHIVE_OK) @@ -255,8 +264,48 @@ archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid) /* First four bytes are signature for end of central directory record. Four zero bytes ensure this isn't a multi-volume Zip file (which we don't yet support). */ - if (memcmp(p, "PK\005\006\000\000\000\000", 8) != 0) - return 0; + if (memcmp(p, "PK\005\006\000\000\000\000", 8) != 0) { + int64_t i, tail; + int found; + + /* + * If there is a comment in end of central directory + * record, 22 bytes are too short. we have to read more + * to properly detect the record. Hopefully, a length + * of the comment is not longer than 16362 bytes(16K-22). + */ + if (filesize + 22 > 1024 * 16) { + tail = 1024 * 16; + filesize = __archive_read_seek(a, tail * -1, SEEK_END); + } else { + tail = filesize + 22; + filesize = __archive_read_seek(a, 0, SEEK_SET); + } + if (filesize < 0) + return 0; + if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL) + return 0; + for (found = 0, i = 0;!found && i < tail - 22;) { + switch (p[i]) { + case 'P': + if (memcmp(p+i, + "PK\005\006\000\000\000\000", 8) == 0) { + p += i; + filesize += tail - + (22 + archive_le16dec(p+20)); + found = 1; + } else + i += 8; + break; + case 'K': i += 7; break; + case 005: i += 6; break; + case 006: i += 5; break; + default: i += 1; break; + } + } + if (!found) + return 0; + } /* Since we've already done the hard work of finding the end of central directory record, let's save the important @@ -264,12 +313,14 @@ archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid) zip->central_directory_entries = archive_le16dec(p + 10); zip->central_directory_size = archive_le32dec(p + 12); zip->central_directory_offset = archive_le32dec(p + 16); + zip->end_of_central_directory_offset = filesize; /* Just one volume, so central dir must all be on this volume. */ if (zip->central_directory_entries != archive_le16dec(p + 8)) return 0; /* Central directory can't extend beyond end of this file. */ - if (zip->central_directory_offset + (int64_t)zip->central_directory_size > filesize) + if (zip->central_directory_offset + + (int64_t)zip->central_directory_size > filesize) return 0; /* This is just a tiny bit higher than the maximum returned by @@ -297,16 +348,91 @@ cmp_key(const struct archive_rb_node *n, const void *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 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) { unsigned i; + int64_t correction; static const struct archive_rb_tree_ops rb_ops = { &cmp_node, &cmp_key }; + static const struct archive_rb_tree_ops rb_rsrc_ops = { + &rsrc_cmp_node, &rsrc_cmp_key + }; + + /* + * Consider the archive file we are reading may be SFX. + * So we have to calculate a SFX header size to revise + * ZIP header offsets. + */ + correction = zip->end_of_central_directory_offset - + (zip->central_directory_offset + zip->central_directory_size); + /* The central directory offset is relative value, and so + * we revise this offset for SFX. */ + zip->central_directory_offset += correction; __archive_read_seek(a, zip->central_directory_offset, SEEK_SET); zip->offset = zip->central_directory_offset; __archive_rb_tree_init(&zip->tree, &rb_ops); + __archive_rb_tree_init(&zip->tree_rsrc, &rb_rsrc_ops); zip->zip_entries = calloc(zip->central_directory_entries, sizeof(struct zip_entry)); @@ -314,7 +440,7 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) struct zip_entry *zip_entry = &zip->zip_entries[i]; size_t filename_length, extra_length, comment_length; uint32_t external_attributes; - const char *p; + const char *name, *p, *r; if ((p = __archive_read_ahead(a, 46, NULL)) == NULL) return ARCHIVE_FATAL; @@ -339,7 +465,8 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) /* 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); + 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 @@ -348,8 +475,50 @@ slurp_central_directory(struct archive_read *a, struct zip *zip) if (zip_entry->system == 3) { zip_entry->mode = external_attributes >> 16; } - /* Register an entry to RB tree to sort it by file offset. */ - __archive_rb_tree_insert_node(&zip->tree, &zip_entry->node); + + /* + * Mac resource fork files are stored under the + * "__MACOSX/" directory, so we should check if + * it is. + */ + /* Make sure we have the file name. */ + if ((p = __archive_read_ahead(a, 46 + filename_length, NULL)) + == NULL) + return ARCHIVE_FATAL; + name = p + 46; + 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); + } /* We don't read the filename until we get to the local file header. Reading it here would speed up @@ -379,10 +548,149 @@ zip_read_consume(struct archive_read *a, int64_t bytes) } 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 = zip->offset; + size_t remaining_bytes, metadata_bytes; + ssize_t hsize; + int ret = ARCHIVE_OK, eof; + + switch(rsrc->compression) { + case 0: /* No compression. */ +#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 > (128 * 1024)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "Mac metadata is too large: %jd > 128K bytes", + (intmax_t)rsrc->uncompressed_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 (zip->offset < rsrc->local_header_offset) + zip_read_consume(a, rsrc->local_header_offset - zip->offset); + else if (zip->offset != rsrc->local_header_offset) { + __archive_read_seek(a, rsrc->local_header_offset, SEEK_SET); + zip->offset = zip->entry->local_header_offset; + } + + hsize = zip_get_local_file_header_size(a, 0); + zip_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. */ + 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; + } + zip_read_consume(a, bytes_used); + remaining_bytes -= bytes_used; + } + archive_entry_copy_mac_metadata(entry, metadata, + (size_t)rsrc->uncompressed_size - metadata_bytes); + + __archive_read_seek(a, offset, SEEK_SET); + zip->offset = offset; +exit_mac_metadata: + 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; int r, ret = ARCHIVE_OK; a->archive.archive_format = ARCHIVE_FORMAT_ZIP; @@ -408,7 +716,19 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a, return ARCHIVE_EOF; --zip->entries_remaining; - if (zip->offset != zip->entry->local_header_offset) { + if (zip->entry->rsrcname.s) + rsrc = (struct zip_entry *)__archive_rb_tree_find_node( + &zip->tree_rsrc, zip->entry->rsrcname.s); + else + rsrc = NULL; + + /* File entries are sorted by the header offset, we should mostly + * use zip_read_consume to advance a read point to avoid redundant + * data reading. */ + if (zip->offset < zip->entry->local_header_offset) + zip_read_consume(a, + zip->entry->local_header_offset - zip->offset); + else if (zip->offset != zip->entry->local_header_offset) { __archive_read_seek(a, zip->entry->local_header_offset, SEEK_SET); zip->offset = zip->entry->local_header_offset; @@ -463,6 +783,11 @@ archive_read_format_zip_seekable_read_header(struct archive_read *a, } } } + if (rsrc) { + int ret2 = zip_read_mac_metadata(a, entry, rsrc); + if (ret2 < ret) + ret = ret2; + } return (ret); } @@ -513,7 +838,8 @@ archive_read_format_zip_options(struct archive_read *a, } 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"); + "zip: hdrcharset option needs a character-set name" + ); else { zip->sconv = archive_string_conversion_from_charset( &a->archive, val, 0); @@ -549,7 +875,8 @@ archive_read_format_zip_streamable_read_header(struct archive_read *a, 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"); + archive_set_error(&a->archive, ENOMEM, + "Out of memory"); return ARCHIVE_FATAL; } } @@ -578,7 +905,8 @@ archive_read_format_zip_streamable_read_header(struct archive_read *a, if (p[2] == '\003' && p[3] == '\004') { /* Regular file entry. */ zip_read_consume(a, skipped); - return zip_read_local_file_header(a, entry, zip); + return zip_read_local_file_header(a, + entry, zip); } if (p[2] == '\005' && p[3] == '\006') @@ -592,6 +920,29 @@ archive_read_format_zip_streamable_read_header(struct archive_read *a, } } +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); +} + /* * Assumes file pointer is at beginning of local file header. */ @@ -648,8 +999,8 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, zip_read_consume(a, 30); if (zip->have_central_directory) { - /* If we read the central dir entry, we must have size information - as well, so ignore the length-at-end flag. */ + /* If we read the central dir entry, we must have size + * information as well, so ignore the length-at-end flag. */ zip_entry->flags &= ~ZIP_LENGTH_AT_END; /* If we have values from both the local file header and the central directory, warn about mismatches @@ -657,19 +1008,22 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry, writers always put zero in the local header; don't bother warning about that. */ if (local_crc32 != 0 && local_crc32 != zip_entry->crc32) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Inconsistent CRC32 values"); ret = ARCHIVE_WARN; } if (compressed_size != 0 && compressed_size != zip_entry->compressed_size) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Inconsistent compressed size"); ret = ARCHIVE_WARN; } if (uncompressed_size != 0 && uncompressed_size != zip_entry->uncompressed_size) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Inconsistent uncompressed size"); ret = ARCHIVE_WARN; } @@ -868,13 +1222,16 @@ archive_read_format_zip_read_data(struct archive_read *a, return (r); /* Update checksum */ if (*size) - zip->entry_crc32 = crc32(zip->entry_crc32, *buff, *size); + zip->entry_crc32 = crc32(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) { + 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)", + "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); @@ -884,7 +1241,8 @@ archive_read_format_zip_read_data(struct archive_read *a, 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)", + "ZIP uncompressed data is wrong size " + "(read %jd, expected %jd)", (intmax_t)zip->entry_uncompressed_bytes_read, (intmax_t)zip->entry->uncompressed_size); return (ARCHIVE_WARN); @@ -947,7 +1305,8 @@ zip_read_data_none(struct archive_read *a, const void **_buff, that are longer than this, so a failure to get at least 16 bytes really does indicate a truncated file. */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } @@ -956,8 +1315,10 @@ zip_read_data_none(struct archive_read *a, const void **_buff, if (p[0] == 'P' && p[1] == 'K' && p[2] == '\007' && p[3] == '\010' && archive_le32dec(p + 4) == zip->entry_crc32 - && archive_le32dec(p + 8) == zip->entry_compressed_bytes_read - && archive_le32dec(p + 12) == zip->entry_uncompressed_bytes_read) { + && archive_le32dec(p + 8) == + zip->entry_compressed_bytes_read + && archive_le32dec(p + 12) == + zip->entry_uncompressed_bytes_read) { zip->entry->crc32 = archive_le32dec(p + 4); zip->entry->compressed_size = archive_le32dec(p + 8); zip->entry->uncompressed_size = archive_le32dec(p + 12); @@ -968,9 +1329,10 @@ zip_read_data_none(struct archive_read *a, const void **_buff, /* 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. */ + /* 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; } @@ -989,7 +1351,8 @@ zip_read_data_none(struct archive_read *a, const void **_buff, /* 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, + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } @@ -1007,6 +1370,31 @@ zip_read_data_none(struct archive_read *a, const void **_buff, #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) { @@ -1031,23 +1419,9 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, } } - /* 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; - } + r = zip_deflate_init(a, zip); + if (r != ARCHIVE_OK) + return (r); /* * Note: '1' here is a performance optimization. @@ -1073,10 +1447,10 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, * cast to remove 'const'. */ zip->stream.next_in = (Bytef *)(uintptr_t)(const void *)compressed_buff; - zip->stream.avail_in = bytes_avail; + zip->stream.avail_in = (uInt)bytes_avail; zip->stream.total_in = 0; zip->stream.next_out = zip->uncompressed_buffer; - zip->stream.avail_out = zip->uncompressed_buffer_size; + zip->stream.avail_out = (uInt)zip->uncompressed_buffer_size; zip->stream.total_out = 0; r = inflate(&zip->stream, 0); @@ -1116,7 +1490,8 @@ zip_read_data_deflate(struct archive_read *a, const void **buff, return (ARCHIVE_FATAL); } /* Consume the optional PK\007\010 marker. */ - if (p[0] == 'P' && p[1] == 'K' && p[2] == '\007' && p[3] == '\010') { + if (p[0] == 'P' && p[1] == 'K' && + p[2] == '\007' && p[3] == '\010') { zip->entry->crc32 = archive_le32dec(p + 4); zip->entry->compressed_size = archive_le32dec(p + 8); zip->entry->uncompressed_size = archive_le32dec(p + 12); @@ -1176,7 +1551,8 @@ archive_read_format_zip_read_data_skip(struct archive_read *a) 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, + archive_set_error(&a->archive, + ARCHIVE_ERRNO_FILE_FORMAT, "Truncated ZIP file data"); return (ARCHIVE_FATAL); } @@ -1206,6 +1582,11 @@ archive_read_format_zip_cleanup(struct archive_read *a) if (zip->stream_valid) inflateEnd(&zip->stream); #endif + if (zip->zip_entries && zip->central_directory_entries) { + unsigned i; + for (i = 0; i < zip->central_directory_entries; i++) + archive_string_free(&(zip->zip_entries[i].rsrcname)); + } free(zip->zip_entries); free(zip->uncompressed_buffer); archive_string_free(&(zip->extra)); @@ -1288,11 +1669,14 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) /* 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); + 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); + zip_entry->uid = + archive_le16dec(p + offset + 8); + zip_entry->gid = + archive_le16dec(p + offset + 10); } break; } @@ -1306,7 +1690,8 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) if (datasize >= 2) zip_entry->uid = archive_le16dec(p + offset); if (datasize >= 4) - zip_entry->gid = archive_le16dec(p + offset + 2); + zip_entry->gid = + archive_le16dec(p + offset + 2); break; case 0x7875: { @@ -1318,22 +1703,26 @@ process_extra(const char *p, size_t extra_length, struct zip_entry* zip_entry) /* get a uid size. */ uidsize = p[offset+1]; if (uidsize == 2) - zip_entry->uid = archive_le16dec( - p + offset + 2); + zip_entry->uid = + archive_le16dec( + p + offset + 2); else if (uidsize == 4 && datasize >= 6) - zip_entry->uid = archive_le32dec( - p + offset + 2); + zip_entry->uid = + archive_le32dec( + p + offset + 2); } if (datasize >= (2 + uidsize + 3)) { /* get a gid size. */ gidsize = p[offset+2+uidsize]; if (gidsize == 2) - zip_entry->gid = archive_le16dec( - p+offset+2+uidsize+1); + 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); + zip_entry->gid = + archive_le32dec( + p+offset+2+uidsize+1); } } break; diff --git a/libarchive/archive_string.c b/libarchive/archive_string.c index 2728a376c669..87f9288f1966 100644 --- a/libarchive/archive_string.c +++ b/libarchive/archive_string.c @@ -486,13 +486,13 @@ archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest, * UTF-16BE/LE NFD ===> UTF-16 NFC * UTF-16BE/LE NFC ===> UTF-16 NFD */ - count = utf16nbytes(s, length); + count = (int)utf16nbytes(s, length); } else { /* * UTF-8 NFD ===> UTF-16 NFC * UTF-8 NFC ===> UTF-16 NFD */ - count = mbsnbytes(s, length); + count = (int)mbsnbytes(s, length); } u16.s = (char *)dest->s; u16.length = dest->length << 1;; @@ -507,7 +507,7 @@ archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest, sc->flag = saved_flag;/* restore the saved flag. */ return (ret); } else if (sc != NULL && (sc->flag & SCONV_FROM_UTF16)) { - count = utf16nbytes(s, length); + count = (int)utf16nbytes(s, length); count >>= 1; /* to be WCS length */ /* Allocate memory for WCS. */ if (NULL == archive_wstring_ensure(dest, @@ -550,8 +550,8 @@ archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest, return (-1); /* Convert MBS to WCS. */ count = MultiByteToWideChar(from_cp, - mbflag, s, length, dest->s + dest->length, - (dest->buffer_length >> 1) -1); + mbflag, s, (int)length, dest->s + dest->length, + (int)(dest->buffer_length >> 1) -1); if (count == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { /* Expand the WCS buffer. */ @@ -727,7 +727,7 @@ archive_string_append_from_wcs_in_codepage(struct archive_string *as, else dp = &defchar_used; count = WideCharToMultiByte(to_cp, 0, ws, wslen, - as->s + as->length, as->buffer_length-1, NULL, dp); + as->s + as->length, (int)as->buffer_length-1, NULL, dp); if (count == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { /* Expand the MBS buffer and retry. */ @@ -1120,8 +1120,8 @@ create_sconv_object(const char *fc, const char *tc, } sc->to_charset = strdup(tc); if (sc->to_charset == NULL) { - free(sc); free(sc->from_charset); + free(sc); return (NULL); } archive_string_init(&sc->utftmp); @@ -1997,7 +1997,7 @@ static int iconv_strncat_in_locale(struct archive_string *as, const void *_p, size_t length, struct archive_string_conv *sc) { - ICONV_CONST char *inp; + ICONV_CONST char *itp; size_t remaining; iconv_t cd; char *outp; @@ -2018,12 +2018,12 @@ iconv_strncat_in_locale(struct archive_string *as, const void *_p, return (-1); cd = sc->cd; - inp = (char *)(uintptr_t)_p; + itp = (char *)(uintptr_t)_p; remaining = length; outp = as->s + as->length; avail = as->buffer_length - as->length - to_size; while (remaining >= (size_t)from_size) { - size_t result = iconv(cd, &inp, &remaining, &outp, &avail); + size_t result = iconv(cd, &itp, &remaining, &outp, &avail); if (result != (size_t)-1) break; /* Conversion completed. */ @@ -2065,7 +2065,7 @@ iconv_strncat_in_locale(struct archive_string *as, const void *_p, *outp++ = '?'; avail--; } - inp += from_size; + itp += from_size; remaining -= from_size; return_value = -1; /* failure */ } else { @@ -2145,7 +2145,7 @@ invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc) if (codepage != CP_UTF8) mbflag |= MB_PRECOMPOSED; - if (MultiByteToWideChar(codepage, mbflag, p, n, NULL, 0) == 0) + if (MultiByteToWideChar(codepage, mbflag, p, (int)n, NULL, 0) == 0) return (-1); /* Invalid */ return (0); /* Okay */ } @@ -2202,8 +2202,8 @@ best_effort_strncat_in_locale(struct archive_string *as, const void *_p, size_t length, struct archive_string_conv *sc) { size_t remaining; - char *outp; - const uint8_t *inp; + char *otp; + const uint8_t *itp; size_t avail; int return_value = 0; /* success */ @@ -2227,41 +2227,41 @@ best_effort_strncat_in_locale(struct archive_string *as, const void *_p, return (-1); remaining = length; - inp = (const uint8_t *)_p; - outp = as->s + as->length; + itp = (const uint8_t *)_p; + otp = as->s + as->length; avail = as->buffer_length - as->length -1; - while (*inp && remaining > 0) { - if (*inp > 127 && (sc->flag & SCONV_TO_UTF8)) { + while (*itp && remaining > 0) { + if (*itp > 127 && (sc->flag & SCONV_TO_UTF8)) { if (avail < UTF8_R_CHAR_SIZE) { - as->length = outp - as->s; + as->length = otp - as->s; if (NULL == archive_string_ensure(as, as->buffer_length + remaining + UTF8_R_CHAR_SIZE)) return (-1); - outp = as->s + as->length; + otp = as->s + as->length; avail = as->buffer_length - as->length -1; } /* * When coping a string in UTF-8, unknown character * should be U+FFFD (replacement character). */ - UTF8_SET_R_CHAR(outp); - outp += UTF8_R_CHAR_SIZE; + UTF8_SET_R_CHAR(otp); + otp += UTF8_R_CHAR_SIZE; avail -= UTF8_R_CHAR_SIZE; - inp++; + itp++; remaining--; return_value = -1; - } else if (*inp > 127) { - *outp++ = '?'; - inp++; + } else if (*itp > 127) { + *otp++ = '?'; + itp++; remaining--; return_value = -1; } else { - *outp++ = (char)*inp++; + *otp++ = (char)*itp++; remaining--; } } - as->length = outp - as->s; + as->length = otp - as->s; as->s[as->length] = '\0'; return (return_value); } @@ -2322,7 +2322,7 @@ _utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) /* Invalide sequence or there are not plenty bytes. */ if ((int)n < cnt) { - cnt = n; + cnt = (int)n; for (i = 1; i < cnt; i++) { if ((s[i] & 0xc0) != 0x80) { cnt = i; @@ -2391,7 +2391,7 @@ _utf8_to_unicode(uint32_t *pwc, const char *s, size_t n) else cnt = 1; if ((int)n < cnt) - cnt = n; + cnt = (int)n; for (i = 1; i < cnt; i++) { if ((s[i] & 0xc0) != 0x80) { cnt = i; @@ -2447,11 +2447,12 @@ combine_surrogate_pair(uint32_t uc, uint32_t uc2) static int cesu8_to_unicode(uint32_t *pwc, const char *s, size_t n) { - uint32_t wc, wc2; + uint32_t wc = 0; int cnt; cnt = _utf8_to_unicode(&wc, s, n); if (cnt == 3 && IS_HIGH_SURROGATE_LA(wc)) { + uint32_t wc2 = 0; if (n - 3 < 3) { /* Invalid byte sequence. */ goto invalid_sequence; @@ -3396,7 +3397,6 @@ check_first_code: break; REPLACE_UC_WITH(uc2); n = n2; - ucptr = s; goto check_first_code; } as->length = p - as->s; @@ -3478,9 +3478,9 @@ strncat_from_utf8_libarchive2(struct archive_string *as, * Translates the wide-character into the current locale MBS. */ #if HAVE_WCRTOMB - n = wcrtomb(p, wc, &shift_state); + n = (int)wcrtomb(p, wc, &shift_state); #else - n = wctomb(p, wc); + n = (int)wctomb(p, wc); #endif if (n == -1) return (-1); @@ -3579,13 +3579,13 @@ win_strncat_from_utf16(struct archive_string *as, const void *_p, size_t bytes, do { defchar = 0; ll = WideCharToMultiByte(sc->to_cp, 0, - (LPCWSTR)u16, bytes>>1, mbs, mbs_size, + (LPCWSTR)u16, (int)bytes>>1, mbs, (int)mbs_size, NULL, &defchar); if (ll == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { /* Need more buffer for MBS. */ ll = WideCharToMultiByte(sc->to_cp, 0, - (LPCWSTR)u16, bytes, NULL, 0, NULL, NULL); + (LPCWSTR)u16, (int)bytes, NULL, 0, NULL, NULL); if (archive_string_ensure(as, ll +1) == NULL) return (-1); mbs = as->s + as->length; @@ -3662,12 +3662,12 @@ win_strncat_to_utf16(struct archive_string *as16, const void *_p, } do { count = MultiByteToWideChar(sc->from_cp, - MB_PRECOMPOSED, s, length, (LPWSTR)u16, (int)avail>>1); + MB_PRECOMPOSED, s, (int)length, (LPWSTR)u16, (int)avail>>1); if (count == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { /* Need more buffer for UTF-16 string */ count = MultiByteToWideChar(sc->from_cp, - MB_PRECOMPOSED, s, length, NULL, 0); + MB_PRECOMPOSED, s, (int)length, NULL, 0); if (archive_string_ensure(as16, (count +1) * 2) == NULL) return (-1); diff --git a/libarchive/archive_util.c b/libarchive/archive_util.c index 103c009d5bc8..34d8081cbce9 100644 --- a/libarchive/archive_util.c +++ b/libarchive/archive_util.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009,2010 Michihiro NAKAJIMA + * Copyright (c) 2009-2012 Michihiro NAKAJIMA * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * @@ -50,6 +50,10 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:1 #include "archive_private.h" #include "archive_string.h" +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + /* Generic initialization of 'struct archive' objects. */ int __archive_clean(struct archive *a) @@ -239,7 +243,7 @@ __archive_mktemp(const char *tmpdir) errno = ENOMEM; goto exit_tmpfile; } - GetTempPathW(l, tmp); + GetTempPathW((DWORD)l, tmp); archive_wstrcpy(&temp_name, tmp); free(tmp); } else { @@ -293,7 +297,8 @@ __archive_mktemp(const char *tmpdir) /* Generate a random file name through CryptGenRandom(). */ p = xp; - if (!CryptGenRandom(hProv, (ep - p)*sizeof(wchar_t), (BYTE*)p)) { + if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t), + (BYTE*)p)) { la_dosmaperr(GetLastError()); goto exit_tmpfile; } @@ -385,6 +390,7 @@ __archive_mktemp(const char *tmpdir) 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); @@ -438,7 +444,8 @@ __archive_mktemp(const char *tmpdir) archive_strcat(&temp_name, "XXXXXXXXXX"); ep = temp_name.s + archive_strlen(&temp_name); - fd = open("/dev/random", O_RDONLY); + fd = open("/dev/random", O_RDONLY | O_CLOEXEC); + __archive_ensure_cloexec_flag(fd); if (fd < 0) seed = time(NULL); else { @@ -452,10 +459,12 @@ __archive_mktemp(const char *tmpdir) p = tp; while (p < ep) *p++ = num[((unsigned)rand_r(&seed)) % sizeof(num)]; - fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR, 0600); + 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); @@ -464,3 +473,29 @@ exit_tmpfile: #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; /* UNSED */ +#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 +} diff --git a/libarchive/archive_virtual.c b/libarchive/archive_virtual.c index 83089f27f942..0c4155f21e3b 100644 --- a/libarchive/archive_virtual.c +++ b/libarchive/archive_virtual.c @@ -67,6 +67,13 @@ archive_read_close(struct archive *a) } int +archive_write_fail(struct archive *a) +{ + a->state = ARCHIVE_STATE_FATAL; + return a->state; +} + +int archive_write_free(struct archive *a) { if (a == NULL) diff --git a/libarchive/archive_windows.c b/libarchive/archive_windows.c index 12ad07dc2a74..d3bf758bb39e 100644 --- a/libarchive/archive_windows.c +++ b/libarchive/archive_windows.c @@ -633,35 +633,22 @@ __la_stat(const char *path, struct stat *st) * This waitpid is limited implementation. */ pid_t -__la_waitpid(pid_t wpid, int *status, int option) +__la_waitpid(HANDLE child, int *status, int option) { - HANDLE child; - DWORD cs, ret; + DWORD cs; (void)option;/* UNUSED */ - *status = 0; - child = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, wpid); - if (child == NULL) { - la_dosmaperr(GetLastError()); - return (-1); - } - ret = WaitForSingleObject(child, INFINITE); - if (ret == WAIT_FAILED) { - CloseHandle(child); - la_dosmaperr(GetLastError()); - return (-1); - } - if (GetExitCodeProcess(child, &cs) == 0) { - CloseHandle(child); - la_dosmaperr(GetLastError()); - return (-1); - } - if (cs == STILL_ACTIVE) - *status = 0x100; - else - *status = (int)(cs & 0xff); - CloseHandle(child); - return (wpid); + do { + if (GetExitCodeProcess(child, &cs) == 0) { + CloseHandle(child); + la_dosmaperr(GetLastError()); + *status = 0; + return (-1); + } + } while (cs == STILL_ACTIVE); + + *status = (int)(cs & 0xff); + return (0); } ssize_t diff --git a/libarchive/archive_windows.h b/libarchive/archive_windows.h index a8e54c593d07..c6f5bc510513 100644 --- a/libarchive/archive_windows.h +++ b/libarchive/archive_windows.h @@ -49,6 +49,9 @@ #define LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED /* Start of configuration for native Win32 */ +#ifndef MINGW_HAS_SECURE_API +#define MINGW_HAS_SECURE_API 1 +#endif #include <errno.h> #define set_errno(val) ((errno)=val) @@ -90,8 +93,14 @@ #ifndef fileno #define fileno _fileno #endif +#ifdef fstat +#undef fstat +#endif #define fstat __la_fstat #if !defined(__BORLANDC__) +#ifdef lseek +#undef lseek +#endif #define lseek _lseeki64 #else #define lseek __la_lseek @@ -103,6 +112,9 @@ #if !defined(__BORLANDC__) #define setmode _setmode #endif +#ifdef stat +#undef stat +#endif #define stat(path,stref) __la_stat(path,stref) #if !defined(__BORLANDC__) #define strdup _strdup @@ -252,7 +264,7 @@ extern __int64 __la_lseek(int fd, __int64 offset, int whence); extern int __la_open(const char *path, int flags, ...); extern ssize_t __la_read(int fd, void *buf, size_t nbytes); extern int __la_stat(const char *path, struct stat *st); -extern pid_t __la_waitpid(pid_t wpid, int *status, int option); +extern pid_t __la_waitpid(HANDLE child, int *status, int option); extern ssize_t __la_write(int fd, const void *buf, size_t nbytes); #define _stat64i32(path, st) __la_stat(path, st) diff --git a/libarchive/archive_write.c b/libarchive/archive_write.c index 6fc3907f3631..a3d1a3380c05 100644 --- a/libarchive/archive_write.c +++ b/libarchive/archive_write.c @@ -232,6 +232,10 @@ __archive_write_filter(struct archive_write_filter *f, int r; if (length == 0) return(ARCHIVE_OK); + if (f->write == NULL) + /* If unset, a fatal error has already ocuured, so this filter + * didn't open. We cannot write anything. */ + return(ARCHIVE_FATAL); r = (f->write)(f, buff, length); f->bytes_written += length; return (r); @@ -437,6 +441,8 @@ archive_write_client_close(struct archive_write_filter *f) (*a->client_closer)(&a->archive, a->client_data); free(state->buffer); free(state); + /* Clear the close handler myself not to be called again. */ + f->close = NULL; a->client_data = NULL; return (ret); } diff --git a/libarchive/archive_write_add_filter.c b/libarchive/archive_write_add_filter.c index 915290aaaf99..81dd683aacc7 100644 --- a/libarchive/archive_write_add_filter.c +++ b/libarchive/archive_write_add_filter.c @@ -45,9 +45,13 @@ struct { int code; int (*setter)(struct archive *); } codes[] = { 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_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 }, - { ARCHIVE_FILTER_LZIP, archive_write_add_filter_lzip }, { -1, NULL } }; diff --git a/libarchive/archive_write_add_filter_b64encode.c b/libarchive/archive_write_add_filter_b64encode.c new file mode 100644 index 000000000000..85eb087b0522 --- /dev/null +++ b/libarchive/archive_write_add_filter_b64encode.c @@ -0,0 +1,314 @@ +/*- + * 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_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_string.h" +#include "archive_write_private.h" + +#define LBYTES 57 + +struct private_b64encode { + int mode; + struct archive_string name; + struct archive_string encoded_buff; + size_t bs; + size_t hold_len; + unsigned char hold[LBYTES]; +}; + +static int archive_filter_b64encode_options(struct archive_write_filter *, + const char *, const char *); +static int archive_filter_b64encode_open(struct archive_write_filter *); +static int archive_filter_b64encode_write(struct archive_write_filter *, + const void *, size_t); +static int archive_filter_b64encode_close(struct archive_write_filter *); +static int archive_filter_b64encode_free(struct archive_write_filter *); +static void b64_encode(struct archive_string *, const unsigned char *, size_t); +static int64_t atol8(const char *, size_t); + +static const char base64[] = { + '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', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' +}; + +/* + * Add a compress filter to this write handle. + */ +int +archive_write_add_filter_b64encode(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + struct archive_write_filter *f = __archive_write_allocate_filter(_a); + struct private_b64encode *state; + + archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_add_filter_uu"); + + state = (struct private_b64encode *)calloc(1, sizeof(*state)); + if (state == NULL) { + archive_set_error(f->archive, ENOMEM, + "Can't allocate data for b64encode filter"); + return (ARCHIVE_FATAL); + } + archive_strcpy(&state->name, "-"); + state->mode = 0644; + + f->data = state; + f->name = "b64encode"; + f->code = ARCHIVE_FILTER_UU; + f->open = archive_filter_b64encode_open; + f->options = archive_filter_b64encode_options; + f->write = archive_filter_b64encode_write; + f->close = archive_filter_b64encode_close; + f->free = archive_filter_b64encode_free; + + return (ARCHIVE_OK); +} + +/* + * Set write options. + */ +static int +archive_filter_b64encode_options(struct archive_write_filter *f, const char *key, + const char *value) +{ + struct private_b64encode *state = (struct private_b64encode *)f->data; + + if (strcmp(key, "mode") == 0) { + if (value == NULL) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "mode option requires octal digits"); + return (ARCHIVE_FAILED); + } + state->mode = (int)atol8(value, strlen(value)) & 0777; + return (ARCHIVE_OK); + } else if (strcmp(key, "name") == 0) { + if (value == NULL) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "name option requires a string"); + return (ARCHIVE_FAILED); + } + archive_strcpy(&state->name, value); + 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); +} + +/* + * Setup callback. + */ +static int +archive_filter_b64encode_open(struct archive_write_filter *f) +{ + struct private_b64encode *state = (struct private_b64encode *)f->data; + size_t bs = 65536, bpb; + int ret; + + ret = __archive_write_open_filter(f->next_filter); + if (ret != ARCHIVE_OK) + return (ret); + + 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 -= bs % bpb; + } + + state->bs = bs; + if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) { + archive_set_error(f->archive, ENOMEM, + "Can't allocate data for b64encode buffer"); + return (ARCHIVE_FATAL); + } + + archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n", + state->mode, state->name.s); + + f->data = state; + return (0); +} + +static void +b64_encode(struct archive_string *as, const unsigned char *p, size_t len) +{ + int c; + + for (; len >= 3; p += 3, len -= 3) { + c = p[0] >> 2; + archive_strappend_char(as, base64[c]); + c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4); + archive_strappend_char(as, base64[c]); + c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6); + archive_strappend_char(as, base64[c]); + c = p[2] & 0x3f; + archive_strappend_char(as, base64[c]); + } + if (len > 0) { + c = p[0] >> 2; + archive_strappend_char(as, base64[c]); + c = (p[0] & 0x03) << 4; + if (len == 1) { + archive_strappend_char(as, base64[c]); + archive_strappend_char(as, '='); + archive_strappend_char(as, '='); + } else { + c |= (p[1] & 0xf0) >> 4; + archive_strappend_char(as, base64[c]); + c = (p[1] & 0x0f) << 2; + archive_strappend_char(as, base64[c]); + archive_strappend_char(as, '='); + } + } + archive_strappend_char(as, '\n'); +} + +/* + * Write data to the encoded stream. + */ +static int +archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff, + size_t length) +{ + struct private_b64encode *state = (struct private_b64encode *)f->data; + const unsigned char *p = buff; + int ret = ARCHIVE_OK; + + if (length == 0) + return (ret); + + if (state->hold_len) { + while (state->hold_len < LBYTES && length > 0) { + state->hold[state->hold_len++] = *p++; + length--; + } + if (state->hold_len < LBYTES) + return (ret); + b64_encode(&state->encoded_buff, state->hold, LBYTES); + state->hold_len = 0; + } + + for (; length >= LBYTES; length -= LBYTES, p += LBYTES) + b64_encode(&state->encoded_buff, p, LBYTES); + + /* Save remaining bytes. */ + if (length > 0) { + memcpy(state->hold, p, length); + state->hold_len = length; + } + while (archive_strlen(&state->encoded_buff) >= state->bs) { + ret = __archive_write_filter(f->next_filter, + state->encoded_buff.s, state->bs); + memmove(state->encoded_buff.s, + state->encoded_buff.s + state->bs, + state->encoded_buff.length - state->bs); + state->encoded_buff.length -= state->bs; + } + + return (ret); +} + + +/* + * Finish the compression... + */ +static int +archive_filter_b64encode_close(struct archive_write_filter *f) +{ + struct private_b64encode *state = (struct private_b64encode *)f->data; + int ret, ret2; + + /* Flush remaining bytes. */ + if (state->hold_len != 0) + b64_encode(&state->encoded_buff, state->hold, state->hold_len); + archive_string_sprintf(&state->encoded_buff, "====\n"); + /* Write the last block */ + archive_write_set_bytes_in_last_block(f->archive, 1); + ret = __archive_write_filter(f->next_filter, + state->encoded_buff.s, archive_strlen(&state->encoded_buff)); + ret2 = __archive_write_close_filter(f->next_filter); + if (ret > ret2) + ret = ret2; + return (ret); +} + +static int +archive_filter_b64encode_free(struct archive_write_filter *f) +{ + struct private_b64encode *state = (struct private_b64encode *)f->data; + + archive_string_free(&state->name); + archive_string_free(&state->encoded_buff); + free(state); + return (ARCHIVE_OK); +} + +static int64_t +atol8(const char *p, size_t char_cnt) +{ + int64_t l; + int digit; + + l = 0; + while (char_cnt-- > 0) { + if (*p >= '0' && *p <= '7') + digit = *p - '0'; + else + break; + p++; + l <<= 3; + l |= digit; + } + return (l); +} + diff --git a/libarchive/archive_write_add_filter_by_name.c b/libarchive/archive_write_add_filter_by_name.c new file mode 100644 index 000000000000..e4cba4afa01c --- /dev/null +++ b/libarchive/archive_write_add_filter_by_name.c @@ -0,0 +1,75 @@ +/*- + * 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 <sys/types.h> +#endif + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "archive.h" +#include "archive_private.h" + +/* A table that maps names to functions. */ +static +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 }, + { "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); +} diff --git a/libarchive/archive_write_add_filter_bzip2.c b/libarchive/archive_write_add_filter_bzip2.c index 2b48959ee093..88da803a395a 100644 --- a/libarchive/archive_write_add_filter_bzip2.c +++ b/libarchive/archive_write_add_filter_bzip2.c @@ -1,5 +1,6 @@ /*- * 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 @@ -54,32 +55,18 @@ archive_write_set_compression_bzip2(struct archive *a) } #endif -#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) -int -archive_write_add_filter_bzip2(struct archive *a) -{ - archive_set_error(a, ARCHIVE_ERRNO_MISC, - "bzip2 compression not supported on this platform"); - return (ARCHIVE_FATAL); -} -#else -/* Don't compile this if we don't have bzlib. */ - struct private_data { int compression_level; +#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) bz_stream stream; int64_t total_in; char *compressed; size_t compressed_buffer_size; +#else + struct archive_write_program_data *pdata; +#endif }; -/* - * Yuck. bzlib.h is not const-correct, so I need this one bit - * of ugly hackery to convert a const * pointer to a non-const pointer. - */ -#define SET_NEXT_IN(st,src) \ - (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src) - static int archive_compressor_bzip2_close(struct archive_write_filter *); static int archive_compressor_bzip2_free(struct archive_write_filter *); static int archive_compressor_bzip2_open(struct archive_write_filter *); @@ -87,8 +74,6 @@ static int archive_compressor_bzip2_options(struct archive_write_filter *, const char *, const char *); static int archive_compressor_bzip2_write(struct archive_write_filter *, const void *, size_t); -static int drive_compressor(struct archive_write_filter *, - struct private_data *, int finishing); /* * Add a bzip2 compression filter to this write handle. @@ -115,12 +100,65 @@ archive_write_add_filter_bzip2(struct archive *_a) f->close = &archive_compressor_bzip2_close; f->free = &archive_compressor_bzip2_free; f->open = &archive_compressor_bzip2_open; - f->code = ARCHIVE_COMPRESSION_BZIP2; + f->code = ARCHIVE_FILTER_BZIP2; f->name = "bzip2"; +#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) return (ARCHIVE_OK); +#else + data->pdata = __archive_write_program_allocate(); + 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 bzip2 program"); + return (ARCHIVE_WARN); +#endif } /* + * Set write options. + */ +static int +archive_compressor_bzip2_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) { + if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || + value[1] != '\0') + return (ARCHIVE_WARN); + data->compression_level = value[0] - '0'; + /* Make '0' be a synonym for '1'. */ + /* This way, bzip2 compressor supports the same 0..9 + * range of levels as gzip. */ + if (data->compression_level < 1) + data->compression_level = 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); +} + +#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR) +/* Don't compile this if we don't have bzlib. */ + +/* + * Yuck. bzlib.h is not const-correct, so I need this one bit + * of ugly hackery to convert a const * pointer to a non-const pointer. + */ +#define SET_NEXT_IN(st,src) \ + (st)->stream.next_in = (char *)(uintptr_t)(const void *)(src) +static int drive_compressor(struct archive_write_filter *, + struct private_data *, int finishing); + +/* * Setup callback. */ static int @@ -195,34 +233,6 @@ archive_compressor_bzip2_open(struct archive_write_filter *f) } /* - * Set write options. - */ -static int -archive_compressor_bzip2_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) { - if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || - value[1] != '\0') - return (ARCHIVE_WARN); - data->compression_level = value[0] - '0'; - /* Make '0' be a synonym for '1'. */ - /* This way, bzip2 compressor supports the same 0..9 - * range of levels as gzip. */ - if (data->compression_level < 1) - data->compression_level = 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); -} - -/* * Write data to the compressed stream. * * Returns ARCHIVE_OK if all data written, error otherwise. @@ -343,4 +353,55 @@ drive_compressor(struct archive_write_filter *f, } } +#else /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */ + +static int +archive_compressor_bzip2_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, "bzip2"); + + /* Specify compression level. */ + if (data->compression_level > 0) { + archive_strcat(&as, " -"); + archive_strappend_char(&as, '0' + data->compression_level); + } + f->write = archive_compressor_bzip2_write; + + r = __archive_write_program_open(f, data->pdata, as.s); + archive_string_free(&as); + return (r); +} + +static int +archive_compressor_bzip2_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_bzip2_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_bzip2_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_BZLIB_H && BZ_CONFIG_ERROR */ diff --git a/libarchive/archive_write_add_filter_compress.c b/libarchive/archive_write_add_filter_compress.c index 4923316f5d8a..26fcef4d42bc 100644 --- a/libarchive/archive_write_add_filter_compress.c +++ b/libarchive/archive_write_add_filter_compress.c @@ -135,7 +135,7 @@ archive_write_add_filter_compress(struct archive *_a) archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_compress"); f->open = &archive_compressor_compress_open; - f->code = ARCHIVE_COMPRESSION_COMPRESS; + f->code = ARCHIVE_FILTER_COMPRESS; f->name = "compress"; return (ARCHIVE_OK); } @@ -150,7 +150,7 @@ archive_compressor_compress_open(struct archive_write_filter *f) struct private_data *state; size_t bs = 65536, bpb; - f->code = ARCHIVE_COMPRESSION_COMPRESS; + f->code = ARCHIVE_FILTER_COMPRESS; f->name = "compress"; ret = __archive_write_open_filter(f->next_filter); diff --git a/libarchive/archive_write_add_filter_grzip.c b/libarchive/archive_write_add_filter_grzip.c new file mode 100644 index 000000000000..8dc287eae033 --- /dev/null +++ b/libarchive/archive_write_add_filter_grzip.c @@ -0,0 +1,135 @@ +/*- + * 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_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#include "archive.h" +#include "archive_write_private.h" + +struct write_grzip { + struct archive_write_program_data *pdata; +}; + +static int archive_write_grzip_open(struct archive_write_filter *); +static int archive_write_grzip_options(struct archive_write_filter *, + const char *, const char *); +static int archive_write_grzip_write(struct archive_write_filter *, + const void *, size_t); +static int archive_write_grzip_close(struct archive_write_filter *); +static int archive_write_grzip_free(struct archive_write_filter *); + +int +archive_write_add_filter_grzip(struct archive *_a) +{ + struct archive_write_filter *f = __archive_write_allocate_filter(_a); + struct write_grzip *data; + + archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_add_filter_grzip"); + + data = calloc(1, sizeof(*data)); + if (data == NULL) { + archive_set_error(_a, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + data->pdata = __archive_write_program_allocate(); + if (data->pdata == NULL) { + free(data); + archive_set_error(_a, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + + f->name = "grzip"; + f->code = ARCHIVE_FILTER_GRZIP; + f->data = data; + f->open = archive_write_grzip_open; + f->options = archive_write_grzip_options; + f->write = archive_write_grzip_write; + f->close = archive_write_grzip_close; + f->free = archive_write_grzip_free; + + /* Note: This filter always uses an external program, so we + * return "warn" to inform of the fact. */ + archive_set_error(_a, ARCHIVE_ERRNO_MISC, + "Using external grzip program for grzip compression"); + return (ARCHIVE_WARN); +} + +static int +archive_write_grzip_options(struct archive_write_filter *f, const char *key, + const char *value) +{ + (void)f; /* UNUSED */ + (void)key; /* UNUSED */ + (void)value; /* UNUSED */ + /* Note: The "warn" return is just to inform the options + * supervisor that we didn't handle it. It will generate + * a suitable error if no one used this option. */ + return (ARCHIVE_WARN); +} + +static int +archive_write_grzip_open(struct archive_write_filter *f) +{ + struct write_grzip *data = (struct write_grzip *)f->data; + + return __archive_write_program_open(f, data->pdata, "grzip"); +} + +static int +archive_write_grzip_write(struct archive_write_filter *f, + const void *buff, size_t length) +{ + struct write_grzip *data = (struct write_grzip *)f->data; + + return __archive_write_program_write(f, data->pdata, buff, length); +} + +static int +archive_write_grzip_close(struct archive_write_filter *f) +{ + struct write_grzip *data = (struct write_grzip *)f->data; + + return __archive_write_program_close(f, data->pdata); +} + +static int +archive_write_grzip_free(struct archive_write_filter *f) +{ + struct write_grzip *data = (struct write_grzip *)f->data; + + __archive_write_program_free(data->pdata); + free(data); + return (ARCHIVE_OK); +} diff --git a/libarchive/archive_write_add_filter_gzip.c b/libarchive/archive_write_add_filter_gzip.c index b1564774fd51..da4607bb934c 100644 --- a/libarchive/archive_write_add_filter_gzip.c +++ b/libarchive/archive_write_add_filter_gzip.c @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_gzip.c 201 #include "archive.h" #include "archive_private.h" +#include "archive_string.h" #include "archive_write_private.h" #if ARCHIVE_VERSION_NUMBER < 4000000 @@ -54,24 +55,20 @@ archive_write_set_compression_gzip(struct archive *a) } #endif -#ifndef HAVE_ZLIB_H -int -archive_write_add_filter_gzip(struct archive *a) -{ - archive_set_error(a, ARCHIVE_ERRNO_MISC, - "gzip compression not supported on this platform"); - return (ARCHIVE_FATAL); -} -#else /* Don't compile this if we don't have zlib. */ struct private_data { int compression_level; + int timestamp; +#ifdef HAVE_ZLIB_H z_stream stream; int64_t total_in; unsigned char *compressed; size_t compressed_buffer_size; unsigned long crc; +#else + struct archive_write_program_data *pdata; +#endif }; /* @@ -88,8 +85,10 @@ static int archive_compressor_gzip_write(struct archive_write_filter *, const void *, size_t); static int archive_compressor_gzip_close(struct archive_write_filter *); static int archive_compressor_gzip_free(struct archive_write_filter *); +#ifdef HAVE_ZLIB_H static int drive_compressor(struct archive_write_filter *, struct private_data *, int finishing); +#endif /* @@ -110,17 +109,73 @@ archive_write_add_filter_gzip(struct archive *_a) return (ARCHIVE_FATAL); } f->data = data; - data->compression_level = Z_DEFAULT_COMPRESSION; f->open = &archive_compressor_gzip_open; f->options = &archive_compressor_gzip_options; f->close = &archive_compressor_gzip_close; f->free = &archive_compressor_gzip_free; - f->code = ARCHIVE_COMPRESSION_GZIP; + f->code = ARCHIVE_FILTER_GZIP; f->name = "gzip"; +#ifdef HAVE_ZLIB_H + data->compression_level = Z_DEFAULT_COMPRESSION; + return (ARCHIVE_OK); +#else + data->pdata = __archive_write_program_allocate(); + 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 gzip program"); + return (ARCHIVE_WARN); +#endif +} + +static int +archive_compressor_gzip_free(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; + +#ifdef HAVE_ZLIB_H + free(data->compressed); +#else + __archive_write_program_free(data->pdata); +#endif + free(data); + f->data = NULL; return (ARCHIVE_OK); } /* + * Set write options. + */ +static int +archive_compressor_gzip_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) { + if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || + value[1] != '\0') + return (ARCHIVE_WARN); + data->compression_level = value[0] - '0'; + return (ARCHIVE_OK); + } + if (strcmp(key, "timestamp") == 0) { + data->timestamp = (value == NULL)?-1: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); +} + +#ifdef HAVE_ZLIB_H +/* * Setup callback. */ static int @@ -128,7 +183,6 @@ archive_compressor_gzip_open(struct archive_write_filter *f) { struct private_data *data = (struct private_data *)f->data; int ret; - time_t t; ret = __archive_write_open_filter(f->next_filter); if (ret != ARCHIVE_OK) @@ -137,8 +191,8 @@ archive_compressor_gzip_open(struct archive_write_filter *f) if (data->compressed == NULL) { size_t bs = 65536, bpb; if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { - /* Buffer size should be a multiple number of the of bytes - * per block for performance. */ + /* 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; @@ -157,18 +211,21 @@ archive_compressor_gzip_open(struct archive_write_filter *f) data->crc = crc32(0L, NULL, 0); data->stream.next_out = data->compressed; - data->stream.avail_out = data->compressed_buffer_size; + data->stream.avail_out = (uInt)data->compressed_buffer_size; /* Prime output buffer with a gzip header. */ - t = time(NULL); data->compressed[0] = 0x1f; /* GZip signature bytes */ data->compressed[1] = 0x8b; data->compressed[2] = 0x08; /* "Deflate" compression */ data->compressed[3] = 0; /* No options */ - data->compressed[4] = (uint8_t)(t)&0xff; /* Timestamp */ - data->compressed[5] = (uint8_t)(t>>8)&0xff; - data->compressed[6] = (uint8_t)(t>>16)&0xff; - data->compressed[7] = (uint8_t)(t>>24)&0xff; + if (data->timestamp >= 0) { + time_t t = time(NULL); + data->compressed[4] = (uint8_t)(t)&0xff; /* Timestamp */ + data->compressed[5] = (uint8_t)(t>>8)&0xff; + data->compressed[6] = (uint8_t)(t>>16)&0xff; + data->compressed[7] = (uint8_t)(t>>24)&0xff; + } else + memset(&data->compressed[4], 0, 4); data->compressed[8] = 0; /* No deflate options */ data->compressed[9] = 3; /* OS=Unix */ data->stream.next_out += 10; @@ -201,8 +258,8 @@ archive_compressor_gzip_open(struct archive_write_filter *f) "compression library: invalid setup parameter"); break; case Z_MEM_ERROR: - archive_set_error(f->archive, ENOMEM, "Internal error initializing " - "compression library"); + archive_set_error(f->archive, ENOMEM, + "Internal error initializing compression library"); break; case Z_VERSION_ERROR: archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, @@ -215,29 +272,6 @@ archive_compressor_gzip_open(struct archive_write_filter *f) } /* - * Set write options. - */ -static int -archive_compressor_gzip_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) { - if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || - value[1] != '\0') - return (ARCHIVE_WARN); - data->compression_level = value[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); -} - -/* * Write data to the compressed stream. */ static int @@ -248,12 +282,12 @@ archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff, int ret; /* Update statistics */ - data->crc = crc32(data->crc, (const Bytef *)buff, length); + data->crc = crc32(data->crc, (const Bytef *)buff, (uInt)length); data->total_in += length; /* Compress input data to output buffer */ SET_NEXT_IN(data, buff); - data->stream.avail_in = length; + data->stream.avail_in = (uInt)length; if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK) return (ret); @@ -303,16 +337,6 @@ archive_compressor_gzip_close(struct archive_write_filter *f) return (r1 < ret ? r1 : ret); } -static int -archive_compressor_gzip_free(struct archive_write_filter *f) -{ - struct private_data *data = (struct private_data *)f->data; - free(data->compressed); - free(data); - f->data = NULL; - return (ARCHIVE_OK); -} - /* * Utility function to push input data through compressor, * writing full output blocks as necessary. @@ -334,7 +358,8 @@ drive_compressor(struct archive_write_filter *f, if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); data->stream.next_out = data->compressed; - data->stream.avail_out = data->compressed_buffer_size; + data->stream.avail_out = + (uInt)data->compressed_buffer_size; } /* If there's nothing to do, we're done. */ @@ -367,4 +392,51 @@ drive_compressor(struct archive_write_filter *f, } } +#else /* HAVE_ZLIB_H */ + +static int +archive_compressor_gzip_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, "gzip"); + + /* Specify compression level. */ + if (data->compression_level > 0) { + archive_strcat(&as, " -"); + archive_strappend_char(&as, '0' + data->compression_level); + } + if (data->timestamp < 0) + /* Do not save timestamp. */ + archive_strcat(&as, " -n"); + else if (data->timestamp > 0) + /* Save timestamp. */ + archive_strcat(&as, " -N"); + + f->write = archive_compressor_gzip_write; + r = __archive_write_program_open(f, data->pdata, as.s); + archive_string_free(&as); + return (r); +} + +static int +archive_compressor_gzip_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_gzip_close(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; + + return __archive_write_program_close(f, data->pdata); +} + #endif /* HAVE_ZLIB_H */ diff --git a/libarchive/archive_write_add_filter_lrzip.c b/libarchive/archive_write_add_filter_lrzip.c new file mode 100644 index 000000000000..85fdf6af57f5 --- /dev/null +++ b/libarchive/archive_write_add_filter_lrzip.c @@ -0,0 +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$"); + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "archive.h" +#include "archive_string.h" +#include "archive_write_private.h" + +struct write_lrzip { + struct archive_write_program_data *pdata; + int compression_level; + enum { lzma = 0, bzip2, gzip, lzo, zpaq } compression; +}; + +static int archive_write_lrzip_open(struct archive_write_filter *); +static int archive_write_lrzip_options(struct archive_write_filter *, + const char *, const char *); +static int archive_write_lrzip_write(struct archive_write_filter *, + const void *, size_t); +static int archive_write_lrzip_close(struct archive_write_filter *); +static int archive_write_lrzip_free(struct archive_write_filter *); + +int +archive_write_add_filter_lrzip(struct archive *_a) +{ + struct archive_write_filter *f = __archive_write_allocate_filter(_a); + struct write_lrzip *data; + + archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_add_filter_lrzip"); + + data = calloc(1, sizeof(*data)); + if (data == NULL) { + archive_set_error(_a, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + data->pdata = __archive_write_program_allocate(); + if (data->pdata == NULL) { + free(data); + archive_set_error(_a, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + + f->name = "lrzip"; + f->code = ARCHIVE_FILTER_LRZIP; + f->data = data; + f->open = archive_write_lrzip_open; + f->options = archive_write_lrzip_options; + f->write = archive_write_lrzip_write; + f->close = archive_write_lrzip_close; + f->free = archive_write_lrzip_free; + + /* Note: This filter always uses an external program, so we + * return "warn" to inform of the fact. */ + archive_set_error(_a, ARCHIVE_ERRNO_MISC, + "Using external lrzip program for lrzip compression"); + return (ARCHIVE_WARN); +} + +static int +archive_write_lrzip_options(struct archive_write_filter *f, const char *key, + const char *value) +{ + struct write_lrzip *data = (struct write_lrzip *)f->data; + + if (strcmp(key, "compression") == 0) { + if (value == NULL) + return (ARCHIVE_WARN); + else if (strcmp(value, "bzip2") == 0) + data->compression = bzip2; + else if (strcmp(value, "gzip") == 0) + data->compression = gzip; + else if (strcmp(value, "lzo") == 0) + data->compression = lzo; + else if (strcmp(value, "zpaq") == 0) + data->compression = zpaq; + else + return (ARCHIVE_WARN); + return (ARCHIVE_OK); + } else if (strcmp(key, "compression-level") == 0) { + if (value == NULL || !(value[0] >= '1' && value[0] <= '9') || + value[1] != '\0') + return (ARCHIVE_WARN); + data->compression_level = value[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); +} + +static int +archive_write_lrzip_open(struct archive_write_filter *f) +{ + struct write_lrzip *data = (struct write_lrzip *)f->data; + struct archive_string as; + int r; + + archive_string_init(&as); + archive_strcpy(&as, "lrzip -q"); + + /* Specify compression type. */ + switch (data->compression) { + case lzma:/* default compression */ + break; + case bzip2: + archive_strcat(&as, " -b"); + break; + case gzip: + archive_strcat(&as, " -g"); + break; + case lzo: + archive_strcat(&as, " -l"); + break; + case zpaq: + archive_strcat(&as, " -z"); + break; + } + + /* Specify compression level. */ + if (data->compression_level > 0) { + archive_strcat(&as, " -L "); + archive_strappend_char(&as, '0' + data->compression_level); + } + + r = __archive_write_program_open(f, data->pdata, as.s); + archive_string_free(&as); + return (r); +} + +static int +archive_write_lrzip_write(struct archive_write_filter *f, + const void *buff, size_t length) +{ + struct write_lrzip *data = (struct write_lrzip *)f->data; + + return __archive_write_program_write(f, data->pdata, buff, length); +} + +static int +archive_write_lrzip_close(struct archive_write_filter *f) +{ + struct write_lrzip *data = (struct write_lrzip *)f->data; + + return __archive_write_program_close(f, data->pdata); +} + +static int +archive_write_lrzip_free(struct archive_write_filter *f) +{ + struct write_lrzip *data = (struct write_lrzip *)f->data; + + __archive_write_program_free(data->pdata); + free(data); + return (ARCHIVE_OK); +} diff --git a/libarchive/archive_write_add_filter_lzop.c b/libarchive/archive_write_add_filter_lzop.c new file mode 100644 index 000000000000..088ecea5167a --- /dev/null +++ b/libarchive/archive_write_add_filter_lzop.c @@ -0,0 +1,486 @@ +/*- + * 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$"); +//#undef HAVE_LZO_LZOCONF_H +//#undef HAVE_LZO_LZO1X_H + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#include <time.h> +#ifdef HAVE_LZO_LZOCONF_H +#include <lzo/lzoconf.h> +#endif +#ifdef HAVE_LZO_LZO1X_H +#include <lzo/lzo1x.h> +#endif + +#include "archive.h" +#include "archive_string.h" +#include "archive_endian.h" +#include "archive_write_private.h" + +enum lzo_method { + METHOD_LZO1X_1 = 1, + METHOD_LZO1X_1_15 = 2, + METHOD_LZO1X_999 = 3 +}; +struct write_lzop { + int compression_level; +#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) + unsigned char *uncompressed; + size_t uncompressed_buffer_size; + size_t uncompressed_avail_bytes; + unsigned char *compressed; + size_t compressed_buffer_size; + enum lzo_method method; + unsigned char level; + lzo_voidp work_buffer; + lzo_uint32 work_buffer_size; + char header_written; +#else + struct archive_write_program_data *pdata; +#endif +}; + +static int archive_write_lzop_open(struct archive_write_filter *); +static int archive_write_lzop_options(struct archive_write_filter *, + const char *, const char *); +static int archive_write_lzop_write(struct archive_write_filter *, + const void *, size_t); +static int archive_write_lzop_close(struct archive_write_filter *); +static int archive_write_lzop_free(struct archive_write_filter *); + +#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) +/* Maximum block size. */ +#define BLOCK_SIZE (256 * 1024) +/* Block infomation is composed of uncompressed size(4 bytes), + * compressed size(4 bytes) and the checksum of uncompressed data(4 bytes) + * in this lzop writer. */ +#define BLOCK_INfO_SIZE 12 + +#define HEADER_VERSION 9 +#define HEADER_LIBVERSION 11 +#define HEADER_METHOD 15 +#define HEADER_LEVEL 16 +#define HEADER_MTIME_LOW 25 +#define HEADER_MTIME_HIGH 29 +#define HEADER_H_CHECKSUM 34 + +/* + * Header template. + */ +static const unsigned char header[] = { + /* LZOP Magic code 9 bytes */ + 0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a, + /* LZOP utility version(fake data) 2 bytes */ + 0x10, 0x30, + /* LZO library version 2 bytes */ + 0x09, 0x40, + /* Minimum required LZO library version 2 bytes */ + 0x09, 0x40, + /* Method */ + 1, + /* Level */ + 5, + /* Flags 4 bytes + * -OS Unix + * -Stdout + * -Stdin + * -Adler32 used for uncompressed data 4 bytes */ + 0x03, 0x00, 0x00, 0x0d, + /* Mode (AE_IFREG | 0644) 4 bytes */ + 0x00, 0x00, 0x81, 0xa4, + /* Mtime low 4 bytes */ + 0x00, 0x00, 0x00, 0x00, + /* Mtime high 4 bytes */ + 0x00, 0x00, 0x00, 0x00, + /* Filename length */ + 0x00, + /* Header checksum 4 bytes */ + 0x00, 0x00, 0x00, 0x00, +}; +#endif + +int +archive_write_add_filter_lzop(struct archive *_a) +{ + struct archive_write_filter *f = __archive_write_allocate_filter(_a); + struct write_lzop *data; + + archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_add_filter_lzop"); + + data = calloc(1, sizeof(*data)); + if (data == NULL) { + archive_set_error(_a, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + + f->name = "lzop"; + f->code = ARCHIVE_FILTER_LZOP; + f->data = data; + f->open = archive_write_lzop_open; + f->options = archive_write_lzop_options; + f->write = archive_write_lzop_write; + f->close = archive_write_lzop_close; + f->free = archive_write_lzop_free; +#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) + if (lzo_init() != LZO_E_OK) { + free(data); + archive_set_error(_a, ARCHIVE_ERRNO_MISC, + "lzo_init(type check) failed"); + return (ARCHIVE_FATAL); + } + if (lzo_version() < 0x940) { + free(data); + archive_set_error(_a, ARCHIVE_ERRNO_MISC, + "liblzo library is too old(%s < 0.940)", + lzo_version_string()); + return (ARCHIVE_FATAL); + } + data->compression_level = 5; + return (ARCHIVE_OK); +#else + data->pdata = __archive_write_program_allocate(); + if (data->pdata == NULL) { + free(data); + archive_set_error(_a, ENOMEM, "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + data->compression_level = 0; + /* Note: We return "warn" to inform of using an external lzop + * program. */ + archive_set_error(_a, ARCHIVE_ERRNO_MISC, + "Using external lzop program for lzop compression"); + return (ARCHIVE_WARN); +#endif +} + +static int +archive_write_lzop_free(struct archive_write_filter *f) +{ + struct write_lzop *data = (struct write_lzop *)f->data; + +#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) + free(data->uncompressed); + free(data->compressed); + free(data->work_buffer); +#else + __archive_write_program_free(data->pdata); +#endif + free(data); + return (ARCHIVE_OK); +} + +static int +archive_write_lzop_options(struct archive_write_filter *f, const char *key, + const char *value) +{ + struct write_lzop *data = (struct write_lzop *)f->data; + + if (strcmp(key, "compression-level") == 0) { + if (value == NULL || !(value[0] >= '1' && value[0] <= '9') || + value[1] != '\0') + return (ARCHIVE_WARN); + data->compression_level = value[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); +} + +#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H) +static int +archive_write_lzop_open(struct archive_write_filter *f) +{ + struct write_lzop *data = (struct write_lzop *)f->data; + int ret; + + ret = __archive_write_open_filter(f->next_filter); + if (ret != ARCHIVE_OK) + return (ret); + + switch (data->compression_level) { + case 1: + data->method = METHOD_LZO1X_1_15; data->level = 1; break; + default: + case 2: case 3: case 4: case 5: case 6: + data->method = METHOD_LZO1X_1; data->level = 5; break; + case 7: + data->method = METHOD_LZO1X_999; data->level = 7; break; + case 8: + data->method = METHOD_LZO1X_999; data->level = 8; break; + case 9: + data->method = METHOD_LZO1X_999; data->level = 9; break; + } + switch (data->method) { + case METHOD_LZO1X_1: + data->work_buffer_size = LZO1X_1_MEM_COMPRESS; break; + case METHOD_LZO1X_1_15: + data->work_buffer_size = LZO1X_1_15_MEM_COMPRESS; break; + case METHOD_LZO1X_999: + data->work_buffer_size = LZO1X_999_MEM_COMPRESS; break; + } + if (data->work_buffer == NULL) { + data->work_buffer = (lzo_voidp)malloc(data->work_buffer_size); + if (data->work_buffer == NULL) { + archive_set_error(f->archive, ENOMEM, + "Can't allocate data for compression buffer"); + return (ARCHIVE_FATAL); + } + } + if (data->compressed == NULL) { + data->compressed_buffer_size = sizeof(header) + + BLOCK_SIZE + (BLOCK_SIZE >> 4) + 64 + 3; + data->compressed = (unsigned char *) + malloc(data->compressed_buffer_size); + if (data->compressed == NULL) { + archive_set_error(f->archive, ENOMEM, + "Can't allocate data for compression buffer"); + return (ARCHIVE_FATAL); + } + } + if (data->uncompressed == NULL) { + data->uncompressed_buffer_size = BLOCK_SIZE; + data->uncompressed = (unsigned char *) + malloc(data->uncompressed_buffer_size); + if (data->uncompressed == NULL) { + archive_set_error(f->archive, ENOMEM, + "Can't allocate data for compression buffer"); + return (ARCHIVE_FATAL); + } + data->uncompressed_avail_bytes = BLOCK_SIZE; + } + return (ARCHIVE_OK); +} + +static int +make_header(struct archive_write_filter *f) +{ + struct write_lzop *data = (struct write_lzop *)f->data; + int64_t t; + uint32_t checksum; + + memcpy(data->compressed, header, sizeof(header)); + /* Overwrite library version. */ + data->compressed[HEADER_LIBVERSION] = (unsigned char ) + (lzo_version() >> 8) & 0xff; + data->compressed[HEADER_LIBVERSION + 1] = (unsigned char ) + lzo_version() & 0xff; + /* Overwrite method and level. */ + data->compressed[HEADER_METHOD] = (unsigned char)data->method; + data->compressed[HEADER_LEVEL] = data->level; + /* Overwrite mtime with current time. */ + t = (int64_t)time(NULL); + archive_be32enc(&data->compressed[HEADER_MTIME_LOW], + (uint32_t)(t & 0xffffffff)); + archive_be32enc(&data->compressed[HEADER_MTIME_HIGH], + (uint32_t)((t >> 32) & 0xffffffff)); + /* Overwrite header checksum with calculated value. */ + checksum = lzo_adler32(1, data->compressed + HEADER_VERSION, + (lzo_uint)(HEADER_H_CHECKSUM - HEADER_VERSION)); + archive_be32enc(&data->compressed[HEADER_H_CHECKSUM], checksum); + return (sizeof(header)); +} + +static int +drive_compressor(struct archive_write_filter *f) +{ + struct write_lzop *data = (struct write_lzop *)f->data; + unsigned char *p; + const int block_info_bytes = 12; + int header_bytes, r; + lzo_uint usize, csize; + uint32_t checksum; + + if (!data->header_written) { + header_bytes = make_header(f); + data->header_written = 1; + } else + header_bytes = 0; + p = data->compressed; + + usize = (lzo_uint) + (data->uncompressed_buffer_size - data->uncompressed_avail_bytes); + csize = 0; + switch (data->method) { + default: + case METHOD_LZO1X_1: + r = lzo1x_1_compress(data->uncompressed, usize, + p + header_bytes + block_info_bytes, &csize, + data->work_buffer); + break; + case METHOD_LZO1X_1_15: + r = lzo1x_1_15_compress(data->uncompressed, usize, + p + header_bytes + block_info_bytes, &csize, + data->work_buffer); + break; + case METHOD_LZO1X_999: + r = lzo1x_999_compress_level(data->uncompressed, usize, + p + header_bytes + block_info_bytes, &csize, + data->work_buffer, NULL, 0, 0, data->level); + break; + } + if (r != LZO_E_OK) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "Lzop compression failed: returned status %d", r); + return (ARCHIVE_FATAL); + } + + /* Store uncompressed size. */ + archive_be32enc(p + header_bytes, (uint32_t)usize); + /* Store the checksum of the uncompressed data. */ + checksum = lzo_adler32(1, data->uncompressed, usize); + archive_be32enc(p + header_bytes + 8, checksum); + + if (csize < usize) { + /* Store compressed size. */ + archive_be32enc(p + header_bytes + 4, (uint32_t)csize); + r = __archive_write_filter(f->next_filter, data->compressed, + header_bytes + block_info_bytes + csize); + } else { + /* + * This case, we output uncompressed data instead. + */ + /* Store uncompressed size as compressed size. */ + archive_be32enc(p + header_bytes + 4, (uint32_t)usize); + r = __archive_write_filter(f->next_filter, data->compressed, + header_bytes + block_info_bytes); + if (r != ARCHIVE_OK) + return (ARCHIVE_FATAL); + r = __archive_write_filter(f->next_filter, data->uncompressed, + usize); + } + + if (r != ARCHIVE_OK) + return (ARCHIVE_FATAL); + return (ARCHIVE_OK); +} + +static int +archive_write_lzop_write(struct archive_write_filter *f, + const void *buff, size_t length) +{ + struct write_lzop *data = (struct write_lzop *)f->data; + const char *p = buff; + int r; + + do { + if (data->uncompressed_avail_bytes > length) { + memcpy(data->uncompressed + + data->uncompressed_buffer_size + - data->uncompressed_avail_bytes, + p, length); + data->uncompressed_avail_bytes -= length; + return (ARCHIVE_OK); + } + + memcpy(data->uncompressed + data->uncompressed_buffer_size + - data->uncompressed_avail_bytes, + p, data->uncompressed_avail_bytes); + length -= data->uncompressed_avail_bytes; + p += data->uncompressed_avail_bytes; + data->uncompressed_avail_bytes = 0; + + r = drive_compressor(f); + if (r != ARCHIVE_OK) return (r); + data->uncompressed_avail_bytes = BLOCK_SIZE; + } while (length); + + return (ARCHIVE_OK); +} + +static int +archive_write_lzop_close(struct archive_write_filter *f) +{ + struct write_lzop *data = (struct write_lzop *)f->data; + const uint32_t endmark = 0; + int r; + + if (data->uncompressed_avail_bytes < BLOCK_SIZE) { + /* Compress and output remaining data. */ + r = drive_compressor(f); + if (r != ARCHIVE_OK) + return (r); + } + /* Write a zero uncompressed size as the end mark of the series of + * compressed block. */ + r = __archive_write_filter(f->next_filter, &endmark, sizeof(endmark)); + if (r != ARCHIVE_OK) + return (r); + return (__archive_write_close_filter(f->next_filter)); +} + +#else +static int +archive_write_lzop_open(struct archive_write_filter *f) +{ + struct write_lzop *data = (struct write_lzop *)f->data; + struct archive_string as; + int r; + + archive_string_init(&as); + archive_strcpy(&as, "lzop"); + /* Specify compression level. */ + if (data->compression_level > 0) { + archive_strappend_char(&as, ' '); + archive_strappend_char(&as, '-'); + archive_strappend_char(&as, '0' + data->compression_level); + } + + r = __archive_write_program_open(f, data->pdata, as.s); + archive_string_free(&as); + return (r); +} + +static int +archive_write_lzop_write(struct archive_write_filter *f, + const void *buff, size_t length) +{ + struct write_lzop *data = (struct write_lzop *)f->data; + + return __archive_write_program_write(f, data->pdata, buff, length); +} + +static int +archive_write_lzop_close(struct archive_write_filter *f) +{ + struct write_lzop *data = (struct write_lzop *)f->data; + + return __archive_write_program_close(f, data->pdata); +} +#endif diff --git a/libarchive/archive_write_add_filter_program.c b/libarchive/archive_write_add_filter_program.c index d7d38f7072da..fc232da0cb5e 100644 --- a/libarchive/archive_write_add_filter_program.c +++ b/libarchive/archive_write_add_filter_program.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2007 Joerg Sonnenberger + * Copyright (c) 2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,7 +45,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_program.c #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 @@ -55,36 +58,24 @@ archive_write_set_compression_program(struct archive *a, const char *cmd) } #endif -/* This capability is only available on POSIX systems. */ -#if (!defined(HAVE_PIPE) || !defined(HAVE_FCNTL) || \ - !(defined(HAVE_FORK) || defined(HAVE_VFORK))) && (!defined(_WIN32) || defined(__CYGWIN__)) - -/* - * On non-Posix systems, allow the program to build, but choke if - * this function is actually invoked. - */ -int -archive_write_add_filter_program(struct archive *_a, const char *cmd) -{ - archive_set_error(_a, -1, - "External compression programs not supported on this platform"); - return (ARCHIVE_FATAL); -} - +struct archive_write_program_data { +#if defined(_WIN32) && !defined(__CYGWIN__) + HANDLE child; #else - -#include "filter_fork.h" - -struct private_data { - char *cmd; - char *description; pid_t child; +#endif int child_stdin, child_stdout; char *child_buf; size_t child_buf_len, child_buf_avail; }; +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); @@ -99,35 +90,125 @@ int archive_write_add_filter_program(struct archive *_a, const char *cmd) { struct archive_write_filter *f = __archive_write_allocate_filter(_a); - struct archive_write *a = (struct archive_write *)_a; struct private_data *data; static const char *prefix = "Program: "; - archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + + archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_add_filter_program"); - data = calloc(1, sizeof(*data)); - if (data == NULL) { - archive_set_error(&a->archive, ENOMEM, "Out of memory"); - return (ARCHIVE_FATAL); - } + + f->data = calloc(1, sizeof(*data)); + if (f->data == NULL) + goto memerr; + data = (struct private_data *)f->data; + data->cmd = strdup(cmd); - data->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); - strcpy(data->description, prefix); - strcat(data->description, cmd); - - f->name = data->description; - f->data = data; - f->open = &archive_compressor_program_open; - f->code = ARCHIVE_COMPRESSION_PROGRAM; + if (data->cmd == NULL) + goto memerr; + + data->pdata = __archive_write_program_allocate(); + 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); } -/* - * Setup callback. - */ 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(void) +{ + 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; + 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->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); @@ -146,23 +227,34 @@ archive_compressor_program_open(struct archive_write_filter *f) } } - if ((data->child = __archive_create_child(data->cmd, - &data->child_stdin, &data->child_stdout)) == -1) { + child = __archive_create_child(cmd, &data->child_stdin, + &data->child_stdout); + if (child == -1) { archive_set_error(f->archive, EINVAL, "Can't initialise filter"); return (ARCHIVE_FATAL); } - - f->write = archive_compressor_program_write; - f->close = archive_compressor_program_close; - f->free = archive_compressor_program_free; - return (0); +#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 initialise filter"); + return (ARCHIVE_FATAL); + } +#else + data->child = child; +#endif + return (ARCHIVE_OK); } static ssize_t -child_write(struct archive_write_filter *f, const char *buf, size_t buf_len) +child_write(struct archive_write_filter *f, + struct archive_write_program_data *data, const char *buf, size_t buf_len) { - struct private_data *data = f->data; ssize_t ret; if (data->child_stdin == -1) @@ -218,30 +310,28 @@ child_write(struct archive_write_filter *f, const char *buf, size_t buf_len) ret = __archive_write_filter(f->next_filter, data->child_buf, data->child_buf_avail); - if (ret <= 0) + if (ret != ARCHIVE_OK) return (-1); - - if ((size_t)ret < data->child_buf_avail) { - memmove(data->child_buf, data->child_buf + ret, - data->child_buf_avail - ret); - } - data->child_buf_avail -= ret; + data->child_buf_avail = 0; } } /* - * Write data to the compressed stream. + * Write data to the filter stream. */ -static int -archive_compressor_program_write(struct archive_write_filter *f, - const void *buff, size_t length) +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, buf, length); + ret = child_write(f, data, buf, length); if (ret == -1 || ret == 0) { archive_set_error(f->archive, EIO, "Can't write to filter"); @@ -253,17 +343,19 @@ archive_compressor_program_write(struct archive_write_filter *f, return (ARCHIVE_OK); } - /* - * Finish the compression... + * Finish the filtering... */ -static int -archive_compressor_program_close(struct archive_write_filter *f) +int +__archive_write_program_close(struct archive_write_filter *f, + struct archive_write_program_data *data) { - struct private_data *data = (struct private_data *)f->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; @@ -304,6 +396,10 @@ cleanup: 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, @@ -314,16 +410,3 @@ cleanup: return (r1 < ret ? r1 : ret); } -static int -archive_compressor_program_free(struct archive_write_filter *f) -{ - struct private_data *data = (struct private_data *)f->data; - free(data->cmd); - free(data->description); - free(data->child_buf); - free(data); - f->data = NULL; - return (ARCHIVE_OK); -} - -#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */ diff --git a/libarchive/archive_write_add_filter_uuencode.c b/libarchive/archive_write_add_filter_uuencode.c new file mode 100644 index 000000000000..23d9c150d17f --- /dev/null +++ b/libarchive/archive_write_add_filter_uuencode.c @@ -0,0 +1,305 @@ +/*- + * 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_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_string.h" +#include "archive_write_private.h" + +#define LBYTES 45 + +struct private_uuencode { + int mode; + struct archive_string name; + struct archive_string encoded_buff; + size_t bs; + size_t hold_len; + unsigned char hold[LBYTES]; +}; + +static int archive_filter_uuencode_options(struct archive_write_filter *, + const char *, const char *); +static int archive_filter_uuencode_open(struct archive_write_filter *); +static int archive_filter_uuencode_write(struct archive_write_filter *, + const void *, size_t); +static int archive_filter_uuencode_close(struct archive_write_filter *); +static int archive_filter_uuencode_free(struct archive_write_filter *); +static void uu_encode(struct archive_string *, const unsigned char *, size_t); +static int64_t atol8(const char *, size_t); + +/* + * Add a compress filter to this write handle. + */ +int +archive_write_add_filter_uuencode(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + struct archive_write_filter *f = __archive_write_allocate_filter(_a); + struct private_uuencode *state; + + archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_add_filter_uu"); + + state = (struct private_uuencode *)calloc(1, sizeof(*state)); + if (state == NULL) { + archive_set_error(f->archive, ENOMEM, + "Can't allocate data for uuencode filter"); + return (ARCHIVE_FATAL); + } + archive_strcpy(&state->name, "-"); + state->mode = 0644; + + f->data = state; + f->name = "uuencode"; + f->code = ARCHIVE_FILTER_UU; + f->open = archive_filter_uuencode_open; + f->options = archive_filter_uuencode_options; + f->write = archive_filter_uuencode_write; + f->close = archive_filter_uuencode_close; + f->free = archive_filter_uuencode_free; + + return (ARCHIVE_OK); +} + +/* + * Set write options. + */ +static int +archive_filter_uuencode_options(struct archive_write_filter *f, const char *key, + const char *value) +{ + struct private_uuencode *state = (struct private_uuencode *)f->data; + + if (strcmp(key, "mode") == 0) { + if (value == NULL) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "mode option requires octal digits"); + return (ARCHIVE_FAILED); + } + state->mode = (int)atol8(value, strlen(value)) & 0777; + return (ARCHIVE_OK); + } else if (strcmp(key, "name") == 0) { + if (value == NULL) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "name option requires a string"); + return (ARCHIVE_FAILED); + } + archive_strcpy(&state->name, value); + 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); +} + +/* + * Setup callback. + */ +static int +archive_filter_uuencode_open(struct archive_write_filter *f) +{ + struct private_uuencode *state = (struct private_uuencode *)f->data; + size_t bs = 65536, bpb; + int ret; + + ret = __archive_write_open_filter(f->next_filter); + if (ret != ARCHIVE_OK) + return (ret); + + 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 -= bs % bpb; + } + + state->bs = bs; + if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) { + archive_set_error(f->archive, ENOMEM, + "Can't allocate data for uuencode buffer"); + return (ARCHIVE_FATAL); + } + + archive_string_sprintf(&state->encoded_buff, "begin %o %s\n", + state->mode, state->name.s); + + f->data = state; + return (0); +} + +static void +uu_encode(struct archive_string *as, const unsigned char *p, size_t len) +{ + int c; + + c = (int)len; + archive_strappend_char(as, c?c + 0x20:'`'); + for (; len >= 3; p += 3, len -= 3) { + c = p[0] >> 2; + archive_strappend_char(as, c?c + 0x20:'`'); + c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4); + archive_strappend_char(as, c?c + 0x20:'`'); + c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6); + archive_strappend_char(as, c?c + 0x20:'`'); + c = p[2] & 0x3f; + archive_strappend_char(as, c?c + 0x20:'`'); + } + if (len > 0) { + c = p[0] >> 2; + archive_strappend_char(as, c?c + 0x20:'`'); + c = (p[0] & 0x03) << 4; + if (len == 1) { + archive_strappend_char(as, c?c + 0x20:'`'); + archive_strappend_char(as, '`'); + archive_strappend_char(as, '`'); + } else { + c |= (p[1] & 0xf0) >> 4; + archive_strappend_char(as, c?c + 0x20:'`'); + c = (p[1] & 0x0f) << 2; + archive_strappend_char(as, c?c + 0x20:'`'); + archive_strappend_char(as, '`'); + } + } + archive_strappend_char(as, '\n'); +} + +/* + * Write data to the encoded stream. + */ +static int +archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff, + size_t length) +{ + struct private_uuencode *state = (struct private_uuencode *)f->data; + const unsigned char *p = buff; + int ret = ARCHIVE_OK; + + if (length == 0) + return (ret); + + if (state->hold_len) { + while (state->hold_len < LBYTES && length > 0) { + state->hold[state->hold_len++] = *p++; + length--; + } + if (state->hold_len < LBYTES) + return (ret); + uu_encode(&state->encoded_buff, state->hold, LBYTES); + state->hold_len = 0; + } + + for (; length >= LBYTES; length -= LBYTES, p += LBYTES) + uu_encode(&state->encoded_buff, p, LBYTES); + + /* Save remaining bytes. */ + if (length > 0) { + memcpy(state->hold, p, length); + state->hold_len = length; + } + while (archive_strlen(&state->encoded_buff) >= state->bs) { + ret = __archive_write_filter(f->next_filter, + state->encoded_buff.s, state->bs); + memmove(state->encoded_buff.s, + state->encoded_buff.s + state->bs, + state->encoded_buff.length - state->bs); + state->encoded_buff.length -= state->bs; + } + + return (ret); +} + + +/* + * Finish the compression... + */ +static int +archive_filter_uuencode_close(struct archive_write_filter *f) +{ + struct private_uuencode *state = (struct private_uuencode *)f->data; + int ret, ret2; + + /* Flush remaining bytes. */ + if (state->hold_len != 0) + uu_encode(&state->encoded_buff, state->hold, state->hold_len); + archive_string_sprintf(&state->encoded_buff, "`\nend\n"); + /* Write the last block */ + archive_write_set_bytes_in_last_block(f->archive, 1); + ret = __archive_write_filter(f->next_filter, + state->encoded_buff.s, archive_strlen(&state->encoded_buff)); + ret2 = __archive_write_close_filter(f->next_filter); + if (ret > ret2) + ret = ret2; + return (ret); +} + +static int +archive_filter_uuencode_free(struct archive_write_filter *f) +{ + struct private_uuencode *state = (struct private_uuencode *)f->data; + + archive_string_free(&state->name); + archive_string_free(&state->encoded_buff); + free(state); + return (ARCHIVE_OK); +} + +static int64_t +atol8(const char *p, size_t char_cnt) +{ + int64_t l; + int digit; + + l = 0; + while (char_cnt-- > 0) { + if (*p >= '0' && *p <= '7') + digit = *p - '0'; + else + break; + p++; + l <<= 3; + l |= digit; + } + return (l); +} + diff --git a/libarchive/archive_write_add_filter_xz.c b/libarchive/archive_write_add_filter_xz.c index 834d59670167..fa73311e7ef4 100644 --- a/libarchive/archive_write_add_filter_xz.c +++ b/libarchive/archive_write_add_filter_xz.c @@ -172,7 +172,7 @@ archive_write_add_filter_xz(struct archive *_a) f = __archive_write_allocate_filter(_a); r = common_setup(f); if (r == ARCHIVE_OK) { - f->code = ARCHIVE_COMPRESSION_XZ; + f->code = ARCHIVE_FILTER_XZ; f->name = "xz"; } return (r); @@ -192,7 +192,7 @@ archive_write_add_filter_lzma(struct archive *_a) f = __archive_write_allocate_filter(_a); r = common_setup(f); if (r == ARCHIVE_OK) { - f->code = ARCHIVE_COMPRESSION_LZMA; + f->code = ARCHIVE_FILTER_LZMA; f->name = "lzma"; } return (r); @@ -209,7 +209,7 @@ archive_write_add_filter_lzip(struct archive *_a) f = __archive_write_allocate_filter(_a); r = common_setup(f); if (r == ARCHIVE_OK) { - f->code = ARCHIVE_COMPRESSION_LZIP; + f->code = ARCHIVE_FILTER_LZIP; f->name = "lzip"; } return (r); @@ -225,12 +225,12 @@ archive_compressor_xz_init_stream(struct archive_write_filter *f, data->stream = lzma_stream_init_data; data->stream.next_out = data->compressed; data->stream.avail_out = data->compressed_buffer_size; - if (f->code == ARCHIVE_COMPRESSION_XZ) + if (f->code == ARCHIVE_FILTER_XZ) ret = lzma_stream_encoder(&(data->stream), data->lzmafilters, LZMA_CHECK_CRC64); - else if (f->code == ARCHIVE_COMPRESSION_LZMA) + else if (f->code == ARCHIVE_FILTER_LZMA) ret = lzma_alone_encoder(&(data->stream), &data->lzma_opt); - else { /* ARCHIVE_COMPRESSION_LZIP */ + else { /* ARCHIVE_FILTER_LZIP */ int dict_size = data->lzma_opt.dict_size; int ds, log2dic, wedges; @@ -321,7 +321,7 @@ archive_compressor_xz_open(struct archive_write_filter *f) f->write = archive_compressor_xz_write; /* Initialize compression library. */ - if (f->code == ARCHIVE_COMPRESSION_LZIP) { + if (f->code == ARCHIVE_FILTER_LZIP) { const struct option_value *val = &option_values[data->compression_level]; @@ -393,7 +393,7 @@ archive_compressor_xz_write(struct archive_write_filter *f, /* Update statistics */ data->total_in += length; - if (f->code == ARCHIVE_COMPRESSION_LZIP) + if (f->code == ARCHIVE_FILTER_LZIP) data->crc32 = lzma_crc32(buff, length, data->crc32); /* Compress input data to output buffer */ @@ -422,7 +422,7 @@ archive_compressor_xz_close(struct archive_write_filter *f) ret = __archive_write_filter(f->next_filter, data->compressed, data->compressed_buffer_size - data->stream.avail_out); - if (f->code == ARCHIVE_COMPRESSION_LZIP && ret == ARCHIVE_OK) { + if (f->code == ARCHIVE_FILTER_LZIP && ret == ARCHIVE_OK) { archive_le32enc(data->compressed, data->crc32); archive_le64enc(data->compressed+4, data->total_in); archive_le64enc(data->compressed+12, data->total_out + 20); diff --git a/libarchive/archive_write_disk_acl.c b/libarchive/archive_write_disk_acl.c new file mode 100644 index 000000000000..97972033c080 --- /dev/null +++ b/libarchive/archive_write_disk_acl.c @@ -0,0 +1,249 @@ +/*- + * 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: head/lib/libarchive/archive_write_disk.c 201159 2009-12-29 05:35:40Z kientzle $"); + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_ACL_H +#define _ACL_PRIVATE /* For debugging */ +#include <sys/acl.h> +#endif +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif + +#include "archive.h" +#include "archive_entry.h" +#include "archive_acl_private.h" +#include "archive_write_disk_private.h" + +#if !defined(HAVE_POSIX_ACL) || !defined(ACL_TYPE_NFS4) +/* Default empty function body to satisfy mainline code. */ +int +archive_write_disk_set_acls(struct archive *a, int fd, const char *name, + struct archive_acl *abstract_acl) +{ + (void)a; /* UNUSED */ + (void)fd; /* UNUSED */ + (void)name; /* UNUSED */ + (void)abstract_acl; /* UNUSED */ + return (ARCHIVE_OK); +} + +#else + +static int set_acl(struct archive *, int fd, const char *, + struct archive_acl *, + acl_type_t, int archive_entry_acl_type, const char *tn); + +/* + * XXX TODO: What about ACL types other than ACCESS and DEFAULT? + */ +int +archive_write_disk_set_acls(struct archive *a, int fd, const char *name, + struct archive_acl *abstract_acl) +{ + int ret; + + if (archive_acl_count(abstract_acl, ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) > 0) { + ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_ACCESS, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access"); + if (ret != ARCHIVE_OK) + return (ret); + ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_DEFAULT, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default"); + return (ret); + } else if (archive_acl_count(abstract_acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4) > 0) { + ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_NFS4, + ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4"); + return (ret); + } else + return ARCHIVE_OK; +} + +static struct { + int archive_perm; + int platform_perm; +} acl_perm_map[] = { + {ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE}, + {ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE}, + {ARCHIVE_ENTRY_ACL_READ, ACL_READ}, + {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 struct { + int archive_inherit; + int platform_inherit; +} acl_inherit_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} +}; + +static int +set_acl(struct archive *a, int fd, const char *name, + struct archive_acl *abstract_acl, + acl_type_t acl_type, int ae_requested_type, const char *tname) +{ + acl_t acl; + acl_entry_t acl_entry; + acl_permset_t acl_permset; + acl_flagset_t acl_flagset; + int ret; + int ae_type, ae_permset, ae_tag, ae_id; + 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); + acl = acl_init(entries); + while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type, + &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) { + acl_create_entry(&acl, &acl_entry); + + switch (ae_tag) { + case ARCHIVE_ENTRY_ACL_USER: + acl_set_tag_type(acl_entry, ACL_USER); + ae_uid = archive_write_disk_uid(a, ae_name, ae_id); + acl_set_qualifier(acl_entry, &ae_uid); + break; + case ARCHIVE_ENTRY_ACL_GROUP: + acl_set_tag_type(acl_entry, ACL_GROUP); + ae_gid = archive_write_disk_gid(a, ae_name, ae_id); + 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; + case ARCHIVE_ENTRY_ACL_EVERYONE: + acl_set_tag_type(acl_entry, ACL_EVERYONE); + break; + default: + /* XXX */ + break; + } + + switch (ae_type) { + case ARCHIVE_ENTRY_ACL_TYPE_ALLOW: + acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_ALLOW); + break; + case ARCHIVE_ENTRY_ACL_TYPE_DENY: + acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_DENY); + break; + case ARCHIVE_ENTRY_ACL_TYPE_AUDIT: + acl_set_entry_type_np(acl_entry, ACL_ENTRY_TYPE_AUDIT); + break; + case ARCHIVE_ENTRY_ACL_TYPE_ALARM: + 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: + // XXX error handling here. + break; + } + + acl_get_permset(acl_entry, &acl_permset); + acl_clear_perms(acl_permset); + + for (i = 0; i < (int)(sizeof(acl_perm_map) / sizeof(acl_perm_map[0])); ++i) { + if (ae_permset & acl_perm_map[i].archive_perm) + acl_add_perm(acl_permset, + acl_perm_map[i].platform_perm); + } + + acl_get_flagset_np(acl_entry, &acl_flagset); + acl_clear_flags_np(acl_flagset); + for (i = 0; i < (int)(sizeof(acl_inherit_map) / sizeof(acl_inherit_map[0])); ++i) { + if (ae_permset & acl_inherit_map[i].archive_inherit) + acl_add_flag_np(acl_flagset, + acl_inherit_map[i].platform_inherit); + } + } + + /* Try restoring the ACL through 'fd' if we can. */ +#if HAVE_ACL_SET_FD + if (fd >= 0 && acl_type == ACL_TYPE_ACCESS && acl_set_fd(fd, acl) == 0) + ret = ARCHIVE_OK; + else +#else +#if HAVE_ACL_SET_FD_NP + if (fd >= 0 && acl_set_fd_np(fd, acl, acl_type) == 0) + ret = ARCHIVE_OK; + else +#endif +#endif +#if HAVE_ACL_SET_LINK_NP + if (acl_set_link_np(name, acl_type, acl) != 0) { + archive_set_error(a, errno, "Failed to set %s acl", tname); + ret = ARCHIVE_WARN; + } +#else + /* TODO: Skip this if 'name' is a symlink. */ + if (acl_set_file(name, acl_type, acl) != 0) { + archive_set_error(a, errno, "Failed to set %s acl", tname); + ret = ARCHIVE_WARN; + } +#endif + acl_free(acl); + return (ret); +} +#endif diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c index d79b0f685a3d..bbd50a637648 100644 --- a/libarchive/archive_write_disk_posix.c +++ b/libarchive/archive_write_disk_posix.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2003-2010 Tim Kientzle + * Copyright (c) 2012 Michihiro NAKAJIMA * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,15 +39,14 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_EXTATTR_H #include <sys/extattr.h> #endif -#ifdef HAVE_SYS_XATTR_H +#if defined(HAVE_SYS_XATTR_H) #include <sys/xattr.h> +#elif defined(HAVE_ATTR_XATTR_H) +#include <attr/xattr.h> #endif #ifdef HAVE_SYS_EA_H #include <sys/ea.h> #endif -#ifdef HAVE_ATTR_XATTR_H -#include <attr/xattr.h> -#endif #ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> #endif @@ -118,6 +118,10 @@ __FBSDID("$FreeBSD$"); #endif #endif +#ifdef HAVE_ZLIB_H +#include <zlib.h> +#endif + /* TODO: Support Mac OS 'quarantine' feature. This is really just a * standard tag to mark files that have been downloaded as "tainted". * On Mac OS, we should mark the extracted files as tainted if the @@ -127,12 +131,17 @@ __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_acl_private.h" #include "archive_string.h" +#include "archive_endian.h" #include "archive_entry.h" #include "archive_private.h" +#include "archive_write_disk_private.h" #ifndef O_BINARY #define O_BINARY 0 #endif +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif struct fixup_entry { struct fixup_entry *next; @@ -172,6 +181,7 @@ struct fixup_entry { #define TODO_SUID_CHECK 0x08000000 #define TODO_SGID 0x04000000 #define TODO_SGID_CHECK 0x02000000 +#define TODO_APPLEDOUBLE 0x01000000 #define TODO_MODE (TODO_MODE_BASE|TODO_SUID|TODO_SGID) #define TODO_TIMES ARCHIVE_EXTRACT_TIME #define TODO_OWNER ARCHIVE_EXTRACT_OWNER @@ -179,6 +189,7 @@ struct fixup_entry { #define TODO_ACLS ARCHIVE_EXTRACT_ACL #define TODO_XATTR ARCHIVE_EXTRACT_XATTR #define TODO_MAC_METADATA ARCHIVE_EXTRACT_MAC_METADATA +#define TODO_HFS_COMPRESSION ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED struct archive_write_disk { struct archive archive; @@ -239,6 +250,36 @@ struct archive_write_disk { /* UID/GID to use in restoring this entry. */ int64_t uid; int64_t gid; + /* + * HFS+ Compression. + */ + /* Xattr "com.apple.decmpfs". */ + uint32_t decmpfs_attr_size; + unsigned char *decmpfs_header_p; + /* ResourceFork set options used for fsetxattr. */ + int rsrc_xattr_options; + /* Xattr "com.apple.ResourceFork". */ + unsigned char *resource_fork; + size_t resource_fork_allocated_size; + unsigned int decmpfs_block_count; + uint32_t *decmpfs_block_info; + /* Buffer for compressed data. */ + unsigned char *compressed_buffer; + size_t compressed_buffer_size; + size_t compressed_buffer_remaining; + /* The offset of the ResourceFork where compressed data will + * be placed. */ + uint32_t compressed_rsrc_position; + uint32_t compressed_rsrc_position_v; + /* Buffer for uncompressed data. */ + char *uncompressed_buffer; + size_t block_remaining_bytes; + size_t file_remaining_bytes; +#ifdef HAVE_ZLIB_H + z_stream stream; + int stream_valid; + int decmpfs_compression_level; +#endif }; /* @@ -256,6 +297,35 @@ struct archive_write_disk { #define MINIMUM_DIR_MODE 0700 #define MAXIMUM_DIR_MODE 0775 +/* + * Maxinum uncompressed size of a decmpfs block. + */ +#define MAX_DECMPFS_BLOCK_SIZE (64 * 1024) +/* + * HFS+ compression type. + */ +#define CMP_XATTR 3/* Compressed data in xattr. */ +#define CMP_RESOURCE_FORK 4/* Compressed data in resource fork. */ +/* + * HFS+ compression resource fork. + */ +#define RSRC_H_SIZE 260 /* Base size of Resource fork header. */ +#define RSRC_F_SIZE 50 /* Size of Resource fork footer. */ +/* Size to write compressed data to resource fork. */ +#define COMPRESSED_W_SIZE (64 * 1024) +/* decmpfs difinitions. */ +#define MAX_DECMPFS_XATTR_SIZE 3802 +#ifndef DECMPFS_XATTR_NAME +#define DECMPFS_XATTR_NAME "com.apple.decmpfs" +#endif +#define DECMPFS_MAGIC 0x636d7066 +#define DECMPFS_COMPRESSION_MAGIC 0 +#define DECMPFS_COMPRESSION_TYPE 4 +#define DECMPFS_UNCOMPRESSED_SIZE 8 +#define DECMPFS_HEADER_SIZE 16 + +#define HFS_BLOCKS(s) ((s) >> 12) + static int check_symlinks(struct archive_write_disk *); static int create_filesystem_object(struct archive_write_disk *); static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname); @@ -265,13 +335,11 @@ static void edit_deep_directories(struct archive_write_disk *ad); static int cleanup_pathname(struct archive_write_disk *); static int create_dir(struct archive_write_disk *, char *); static int create_parent_dir(struct archive_write_disk *, char *); +static ssize_t hfs_write_data_block(struct archive_write_disk *, + const char *, size_t); +static int fixup_appledouble(struct archive_write_disk *, const char *); static int older(struct stat *, struct archive_entry *); static int restore_entry(struct archive_write_disk *); -#ifdef HAVE_POSIX_ACL -static int set_acl(struct archive_write_disk *, int fd, const char *, struct archive_acl *, - acl_type_t, int archive_entry_acl_type, const char *tn); -#endif -static int set_acls(struct archive_write_disk *, int fd, const char *, struct archive_acl *); static int set_mac_metadata(struct archive_write_disk *, const char *, const void *, size_t); static int set_xattrs(struct archive_write_disk *); @@ -485,6 +553,39 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) else a->todo |= TODO_MAC_METADATA; } +#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H) + if ((a->flags & ARCHIVE_EXTRACT_NO_HFS_COMPRESSION) == 0) { + unsigned long set, clear; + archive_entry_fflags(a->entry, &set, &clear); + if ((set & ~clear) & UF_COMPRESSED) { + a->todo |= TODO_HFS_COMPRESSION; + a->decmpfs_block_count = (unsigned)-1; + } + } + if ((a->flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED) != 0 && + (a->mode & AE_IFMT) == AE_IFREG && a->filesize > 0) { + a->todo |= TODO_HFS_COMPRESSION; + a->decmpfs_block_count = (unsigned)-1; + } + { + const char *p; + + /* Check if the current file name is a type of the + * resource fork file. */ + p = strrchr(a->name, '/'); + if (p == NULL) + p = a->name; + else + p++; + if (p[0] == '.' && p[1] == '_') { + /* Do not compress "._XXX" files. */ + a->todo &= ~TODO_HFS_COMPRESSION; + if (a->filesize > 0) + a->todo |= TODO_APPLEDOUBLE; + } + } +#endif + if (a->flags & ARCHIVE_EXTRACT_XATTR) a->todo |= TODO_XATTR; if (a->flags & ARCHIVE_EXTRACT_FFLAGS) @@ -501,6 +602,25 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) ret = restore_entry(a); +#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H) + /* + * Check if the filesystem the file is restoring on supports + * HFS+ Compression. If not, cancel HFS+ Compression. + */ + if (a->todo | TODO_HFS_COMPRESSION) { + /* + * NOTE: UF_COMPRESSED is ignored even if the filesystem + * supports HFS+ Compression because the file should + * have at least an extended attriute "com.apple.decmpfs" + * before the flag is set to indicate that the file have + * been compressed. If hte filesystem does not support + * HFS+ Compression the system call will fail. + */ + if (a->fd < 0 || fchflags(a->fd, UF_COMPRESSED) != 0) + a->todo &= ~TODO_HFS_COMPRESSION; + } +#endif + /* * TODO: There are rumours that some extended attributes must * be restored before file data is written. If this is true, @@ -532,6 +652,8 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) */ if (a->deferred & TODO_MODE) { fe = current_fixup(a, archive_entry_pathname(entry)); + if (fe == NULL) + return (ARCHIVE_FATAL); fe->fixup |= TODO_MODE_BASE; fe->mode = a->mode; } @@ -540,6 +662,8 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) && (archive_entry_mtime_is_set(entry) || archive_entry_atime_is_set(entry))) { fe = current_fixup(a, archive_entry_pathname(entry)); + if (fe == NULL) + return (ARCHIVE_FATAL); fe->mode = a->mode; fe->fixup |= TODO_TIMES; if (archive_entry_atime_is_set(entry)) { @@ -570,6 +694,9 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) if (a->deferred & TODO_ACLS) { fe = current_fixup(a, archive_entry_pathname(entry)); + if (fe == NULL) + return (ARCHIVE_FATAL); + fe->fixup |= TODO_ACLS; archive_acl_copy(&fe->acl, archive_entry_acl(entry)); } @@ -579,6 +706,8 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) metadata = archive_entry_mac_metadata(a->entry, &metadata_size); if (metadata != NULL && metadata_size > 0) { fe = current_fixup(a, archive_entry_pathname(entry)); + if (fe == NULL) + return (ARCHIVE_FATAL); fe->mac_metadata = malloc(metadata_size); if (fe->mac_metadata != NULL) { memcpy(fe->mac_metadata, metadata, metadata_size); @@ -590,6 +719,8 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) if (a->deferred & TODO_FFLAGS) { fe = current_fixup(a, archive_entry_pathname(entry)); + if (fe == NULL) + return (ARCHIVE_FATAL); fe->fixup |= TODO_FFLAGS; /* TODO: Complete this.. defer fflags from below. */ } @@ -707,6 +838,616 @@ write_data_block(struct archive_write_disk *a, const char *buff, size_t size) return (start_size - size); } +#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\ + && defined(HAVE_ZLIB_H) + +/* + * Set UF_COMPRESSED file flag. + * This have to be called after hfs_write_decmpfs() because if the + * file does not have "com.apple.decmpfs" xattr the flag is ignored. + */ +static int +hfs_set_compressed_fflag(struct archive_write_disk *a) +{ + int r; + + if ((r = lazy_stat(a)) != ARCHIVE_OK) + return (r); + + a->st.st_flags |= UF_COMPRESSED; + if (fchflags(a->fd, a->st.st_flags) != 0) { + archive_set_error(&a->archive, errno, + "Failed to set UF_COMPRESSED file flag"); + return (ARCHIVE_WARN); + } + return (ARCHIVE_OK); +} + +/* + * HFS+ Compression decmpfs + * + * +------------------------------+ +0 + * | Magic(LE 4 bytes) | + * +------------------------------+ + * | Type(LE 4 bytes) | + * +------------------------------+ + * | Uncompressed size(LE 8 bytes)| + * +------------------------------+ +16 + * | | + * | Compressed data | + * | (Placed only if Type == 3) | + * | | + * +------------------------------+ +3802 = MAX_DECMPFS_XATTR_SIZE + * + * Type is 3: decmpfs has compressed data. + * Type is 4: Resource Fork has compressed data. + */ +/* + * Write "com.apple.decmpfs" + */ +static int +hfs_write_decmpfs(struct archive_write_disk *a) +{ + int r; + uint32_t compression_type; + + r = fsetxattr(a->fd, DECMPFS_XATTR_NAME, a->decmpfs_header_p, + a->decmpfs_attr_size, 0, 0); + if (r < 0) { + archive_set_error(&a->archive, errno, + "Cannot restore xattr:%s", DECMPFS_XATTR_NAME); + compression_type = archive_le32dec( + &a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE]); + if (compression_type == CMP_RESOURCE_FORK) + fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME, + XATTR_SHOWCOMPRESSION); + return (ARCHIVE_WARN); + } + return (ARCHIVE_OK); +} + +/* + * HFS+ Compression Resource Fork + * + * +-----------------------------+ + * | Header(260 bytes) | + * +-----------------------------+ + * | Block count(LE 4 bytes) | + * +-----------------------------+ --+ + * +-- | Offset (LE 4 bytes) | | + * | | [distance from Block count] | | Block 0 + * | +-----------------------------+ | + * | | Compressed size(LE 4 bytes) | | + * | +-----------------------------+ --+ + * | | | + * | | .................. | + * | | | + * | +-----------------------------+ --+ + * | | Offset (LE 4 bytes) | | + * | +-----------------------------+ | Block (Block count -1) + * | | Compressed size(LE 4 bytes) | | + * +-> +-----------------------------+ --+ + * | Compressed data(n bytes) | Block 0 + * +-----------------------------+ + * | | + * | .................. | + * | | + * +-----------------------------+ + * | Compressed data(n bytes) | Block (Block count -1) + * +-----------------------------+ + * | Footer(50 bytes) | + * +-----------------------------+ + * + */ +/* + * Write the header of "com.apple.ResourceFork" + */ +static int +hfs_write_resource_fork(struct archive_write_disk *a, unsigned char *buff, + size_t bytes, uint32_t position) +{ + int ret; + + ret = fsetxattr(a->fd, XATTR_RESOURCEFORK_NAME, buff, bytes, + position, a->rsrc_xattr_options); + if (ret < 0) { + archive_set_error(&a->archive, errno, + "Cannot restore xattr: %s at %u pos %u bytes", + XATTR_RESOURCEFORK_NAME, + (unsigned)position, + (unsigned)bytes); + return (ARCHIVE_WARN); + } + a->rsrc_xattr_options &= ~XATTR_CREATE; + return (ARCHIVE_OK); +} + +static int +hfs_write_compressed_data(struct archive_write_disk *a, size_t bytes_compressed) +{ + int ret; + + ret = hfs_write_resource_fork(a, a->compressed_buffer, + bytes_compressed, a->compressed_rsrc_position); + if (ret == ARCHIVE_OK) + a->compressed_rsrc_position += bytes_compressed; + return (ret); +} + +static int +hfs_write_resource_fork_header(struct archive_write_disk *a) +{ + unsigned char *buff; + uint32_t rsrc_bytes; + uint32_t rsrc_header_bytes; + + /* + * Write resource fork header + block info. + */ + buff = a->resource_fork; + rsrc_bytes = a->compressed_rsrc_position - RSRC_F_SIZE; + rsrc_header_bytes = + RSRC_H_SIZE + /* Header base size. */ + 4 + /* Block count. */ + (a->decmpfs_block_count * 8);/* Block info */ + archive_be32enc(buff, 0x100); + archive_be32enc(buff + 4, rsrc_bytes); + archive_be32enc(buff + 8, rsrc_bytes - 256); + archive_be32enc(buff + 12, 0x32); + memset(buff + 16, 0, 240); + archive_be32enc(buff + 256, rsrc_bytes - 260); + return hfs_write_resource_fork(a, buff, rsrc_header_bytes, 0); +} + +static size_t +hfs_set_resource_fork_footer(unsigned char *buff, size_t buff_size) +{ + static const char rsrc_footer[RSRC_F_SIZE] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 'c', 'm', + 'p', 'f', 0x00, 0x00, 0x00, 0x0a, 0x00, 0x01, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + }; + if (buff_size < sizeof(rsrc_footer)) + return (0); + memcpy(buff, rsrc_footer, sizeof(rsrc_footer)); + return (sizeof(rsrc_footer)); +} + +static int +hfs_reset_compressor(struct archive_write_disk *a) +{ + int ret; + + if (a->stream_valid) + ret = deflateReset(&a->stream); + else + ret = deflateInit(&a->stream, a->decmpfs_compression_level); + + if (ret != Z_OK) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Failed to initialize compressor"); + return (ARCHIVE_FATAL); + } else + a->stream_valid = 1; + + return (ARCHIVE_OK); +} + +static int +hfs_decompress(struct archive_write_disk *a) +{ + uint32_t *block_info; + unsigned int block_count; + uint32_t data_pos, data_size; + ssize_t r; + ssize_t bytes_written, bytes_to_write; + unsigned char *b; + + block_info = (uint32_t *)(a->resource_fork + RSRC_H_SIZE); + block_count = archive_le32dec(block_info++); + while (block_count--) { + data_pos = RSRC_H_SIZE + archive_le32dec(block_info++); + data_size = archive_le32dec(block_info++); + r = fgetxattr(a->fd, XATTR_RESOURCEFORK_NAME, + a->compressed_buffer, data_size, data_pos, 0); + if (r != data_size) { + archive_set_error(&a->archive, + (r < 0)?errno:ARCHIVE_ERRNO_MISC, + "Failed to read resource fork"); + return (ARCHIVE_WARN); + } + if (a->compressed_buffer[0] == 0xff) { + bytes_to_write = data_size -1; + b = a->compressed_buffer + 1; + } else { + uLong dest_len = MAX_DECMPFS_BLOCK_SIZE; + int zr; + + zr = uncompress((Bytef *)a->uncompressed_buffer, + &dest_len, a->compressed_buffer, data_size); + if (zr != Z_OK) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Failed to decompress resource fork"); + return (ARCHIVE_WARN); + } + bytes_to_write = dest_len; + b = (unsigned char *)a->uncompressed_buffer; + } + do { + bytes_written = write(a->fd, b, bytes_to_write); + if (bytes_written < 0) { + archive_set_error(&a->archive, errno, + "Write failed"); + return (ARCHIVE_WARN); + } + bytes_to_write -= bytes_written; + b += bytes_written; + } while (bytes_to_write > 0); + } + r = fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME, 0); + if (r == -1) { + archive_set_error(&a->archive, errno, + "Failed to remove resource fork"); + return (ARCHIVE_WARN); + } + return (ARCHIVE_OK); +} + +static int +hfs_drive_compressor(struct archive_write_disk *a, const char *buff, + size_t size) +{ + unsigned char *buffer_compressed; + size_t bytes_compressed; + size_t bytes_used; + int ret; + + ret = hfs_reset_compressor(a); + if (ret != ARCHIVE_OK) + return (ret); + + if (a->compressed_buffer == NULL) { + size_t block_size; + + block_size = COMPRESSED_W_SIZE + RSRC_F_SIZE + + + compressBound(MAX_DECMPFS_BLOCK_SIZE); + a->compressed_buffer = malloc(block_size); + if (a->compressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for Resource Fork"); + return (ARCHIVE_FATAL); + } + a->compressed_buffer_size = block_size; + a->compressed_buffer_remaining = block_size; + } + + buffer_compressed = a->compressed_buffer + + a->compressed_buffer_size - a->compressed_buffer_remaining; + a->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff; + a->stream.avail_in = size; + a->stream.next_out = buffer_compressed; + a->stream.avail_out = a->compressed_buffer_remaining; + do { + ret = deflate(&a->stream, Z_FINISH); + switch (ret) { + case Z_OK: + case Z_STREAM_END: + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Failed to compress data"); + return (ARCHIVE_FAILED); + } + } while (ret == Z_OK); + bytes_compressed = a->compressed_buffer_remaining - a->stream.avail_out; + + /* + * If the compressed size is larger than the original size, + * throw away compressed data, use uncompressed data instead. + */ + if (bytes_compressed > size) { + buffer_compressed[0] = 0xFF;/* uncompressed marker. */ + memcpy(buffer_compressed + 1, buff, size); + bytes_compressed = size + 1; + } + a->compressed_buffer_remaining -= bytes_compressed; + + /* + * If the compressed size is smaller than MAX_DECMPFS_XATTR_SIZE + * and the block count in the file is only one, store compressed + * data to decmpfs xattr instead of the resource fork. + */ + if (a->decmpfs_block_count == 1 && + (a->decmpfs_attr_size + bytes_compressed) + <= MAX_DECMPFS_XATTR_SIZE) { + archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE], + CMP_XATTR); + memcpy(a->decmpfs_header_p + DECMPFS_HEADER_SIZE, + buffer_compressed, bytes_compressed); + a->decmpfs_attr_size += bytes_compressed; + a->compressed_buffer_remaining = a->compressed_buffer_size; + /* + * Finish HFS+ Compression. + * - Write the decmpfs xattr. + * - Set the UF_COMPRESSED file flag. + */ + ret = hfs_write_decmpfs(a); + if (ret == ARCHIVE_OK) + ret = hfs_set_compressed_fflag(a); + return (ret); + } + + /* Update block info. */ + archive_le32enc(a->decmpfs_block_info++, + a->compressed_rsrc_position_v - RSRC_H_SIZE); + archive_le32enc(a->decmpfs_block_info++, bytes_compressed); + a->compressed_rsrc_position_v += bytes_compressed; + + /* + * Write the compressed data to the resource fork. + */ + bytes_used = a->compressed_buffer_size - a->compressed_buffer_remaining; + while (bytes_used >= COMPRESSED_W_SIZE) { + ret = hfs_write_compressed_data(a, COMPRESSED_W_SIZE); + if (ret != ARCHIVE_OK) + return (ret); + bytes_used -= COMPRESSED_W_SIZE; + if (bytes_used > COMPRESSED_W_SIZE) + memmove(a->compressed_buffer, + a->compressed_buffer + COMPRESSED_W_SIZE, + bytes_used); + else + memcpy(a->compressed_buffer, + a->compressed_buffer + COMPRESSED_W_SIZE, + bytes_used); + } + a->compressed_buffer_remaining = a->compressed_buffer_size - bytes_used; + + /* + * If the current block is the last block, write the remaining + * compressed data and the resource fork footer. + */ + if (a->file_remaining_bytes == 0) { + size_t rsrc_size; + int64_t bk; + + /* Append the resource footer. */ + rsrc_size = hfs_set_resource_fork_footer( + a->compressed_buffer + bytes_used, + a->compressed_buffer_remaining); + ret = hfs_write_compressed_data(a, bytes_used + rsrc_size); + a->compressed_buffer_remaining = a->compressed_buffer_size; + + /* If the compressed size is not enouph smaller than + * the uncompressed size. cancel HFS+ compression. + * TODO: study a behavior of ditto utility and improve + * the condition to fall back into no HFS+ compression. */ + bk = HFS_BLOCKS(a->compressed_rsrc_position); + bk += bk >> 7; + if (bk > HFS_BLOCKS(a->filesize)) + return hfs_decompress(a); + /* + * Write the resourcefork header. + */ + if (ret == ARCHIVE_OK) + ret = hfs_write_resource_fork_header(a); + /* + * Finish HFS+ Compression. + * - Write the decmpfs xattr. + * - Set the UF_COMPRESSED file flag. + */ + if (ret == ARCHIVE_OK) + ret = hfs_write_decmpfs(a); + if (ret == ARCHIVE_OK) + ret = hfs_set_compressed_fflag(a); + } + return (ret); +} + +static ssize_t +hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff, + size_t size) +{ + const char *buffer_to_write; + size_t bytes_to_write; + int ret; + + if (a->decmpfs_block_count == (unsigned)-1) { + void *new_block; + size_t new_size; + unsigned int block_count; + + if (a->decmpfs_header_p == NULL) { + new_block = malloc(MAX_DECMPFS_XATTR_SIZE + + sizeof(uint32_t)); + if (new_block == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for decmpfs"); + return (ARCHIVE_FATAL); + } + a->decmpfs_header_p = new_block; + } + a->decmpfs_attr_size = DECMPFS_HEADER_SIZE; + archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_MAGIC], + DECMPFS_MAGIC); + archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE], + CMP_RESOURCE_FORK); + archive_le64enc(&a->decmpfs_header_p[DECMPFS_UNCOMPRESSED_SIZE], + a->filesize); + + /* Calculate a block count of the file. */ + block_count = + (a->filesize + MAX_DECMPFS_BLOCK_SIZE -1) / + MAX_DECMPFS_BLOCK_SIZE; + /* + * Allocate buffer for resource fork. + * Set up related pointers; + */ + new_size = + RSRC_H_SIZE + /* header */ + 4 + /* Block count */ + (block_count * sizeof(uint32_t) * 2) + + RSRC_F_SIZE; /* footer */ + if (new_size > a->resource_fork_allocated_size) { + new_block = realloc(a->resource_fork, new_size); + if (new_block == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for ResourceFork"); + return (ARCHIVE_FATAL); + } + a->resource_fork_allocated_size = new_size; + a->resource_fork = new_block; + } + + /* Allocate uncompressed buffer */ + if (a->uncompressed_buffer == NULL) { + new_block = malloc(MAX_DECMPFS_BLOCK_SIZE); + if (new_block == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for decmpfs"); + return (ARCHIVE_FATAL); + } + a->uncompressed_buffer = new_block; + } + a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE; + a->file_remaining_bytes = a->filesize; + a->compressed_buffer_remaining = a->compressed_buffer_size; + + /* + * Set up a resource fork. + */ + a->rsrc_xattr_options = XATTR_CREATE; + /* Get the position where we are going to set a bunch + * of block info. */ + a->decmpfs_block_info = + (uint32_t *)(a->resource_fork + RSRC_H_SIZE); + /* Set the block count to the resource fork. */ + archive_le32enc(a->decmpfs_block_info++, block_count); + /* Get the position where we are goint to set compressed + * data. */ + a->compressed_rsrc_position = + RSRC_H_SIZE + 4 + (block_count * 8); + a->compressed_rsrc_position_v = a->compressed_rsrc_position; + a->decmpfs_block_count = block_count; + } + + /* Ignore redundant bytes. */ + if (a->file_remaining_bytes == 0) + return ((ssize_t)size); + + /* Do not overrun a block size. */ + if (size > a->block_remaining_bytes) + bytes_to_write = a->block_remaining_bytes; + else + bytes_to_write = size; + /* Do not overrun the file size. */ + if (bytes_to_write > a->file_remaining_bytes) + bytes_to_write = a->file_remaining_bytes; + + /* For efficiency, if a copy length is full of the uncompressed + * buffer size, do not copy writing data to it. */ + if (bytes_to_write == MAX_DECMPFS_BLOCK_SIZE) + buffer_to_write = buff; + else { + memcpy(a->uncompressed_buffer + + MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes, + buff, bytes_to_write); + buffer_to_write = a->uncompressed_buffer; + } + a->block_remaining_bytes -= bytes_to_write; + a->file_remaining_bytes -= bytes_to_write; + + if (a->block_remaining_bytes == 0 || a->file_remaining_bytes == 0) { + ret = hfs_drive_compressor(a, buffer_to_write, + MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes); + if (ret < 0) + return (ret); + a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE; + } + /* Ignore redundant bytes. */ + if (a->file_remaining_bytes == 0) + return ((ssize_t)size); + return (bytes_to_write); +} + +static ssize_t +hfs_write_data_block(struct archive_write_disk *a, const char *buff, + size_t size) +{ + uint64_t start_size = size; + ssize_t bytes_written = 0; + ssize_t bytes_to_write; + + if (size == 0) + return (ARCHIVE_OK); + + if (a->filesize == 0 || a->fd < 0) { + archive_set_error(&a->archive, 0, + "Attempt to write to an empty file"); + return (ARCHIVE_WARN); + } + + /* If this write would run beyond the file size, truncate it. */ + if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize) + start_size = size = (size_t)(a->filesize - a->offset); + + /* Write the data. */ + while (size > 0) { + bytes_to_write = size; + /* Seek if necessary to the specified offset. */ + if (a->offset < a->fd_offset) { + /* Can't support backword move. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Seek failed"); + return (ARCHIVE_FATAL); + } else if (a->offset > a->fd_offset) { + int64_t skip = a->offset - a->fd_offset; + char nullblock[1024]; + + memset(nullblock, 0, sizeof(nullblock)); + while (skip > 0) { + if (skip > (int64_t)sizeof(nullblock)) + bytes_written = hfs_write_decmpfs_block( + a, nullblock, sizeof(nullblock)); + else + bytes_written = hfs_write_decmpfs_block( + a, nullblock, skip); + if (bytes_written < 0) { + archive_set_error(&a->archive, errno, + "Write failed"); + return (ARCHIVE_WARN); + } + skip -= bytes_written; + } + + a->fd_offset = a->offset; + } + bytes_written = + hfs_write_decmpfs_block(a, buff, bytes_to_write); + if (bytes_written < 0) + return (bytes_written); + buff += bytes_written; + size -= bytes_written; + a->total_bytes_written += bytes_written; + a->offset += bytes_written; + a->fd_offset = a->offset; + } + return (start_size - size); +} +#else +static ssize_t +hfs_write_data_block(struct archive_write_disk *a, const char *buff, + size_t size) +{ + return (write_data_block(a, buff, size)); +} +#endif + static ssize_t _archive_write_disk_data_block(struct archive *_a, const void *buff, size_t size, int64_t offset) @@ -718,7 +1459,10 @@ _archive_write_disk_data_block(struct archive *_a, ARCHIVE_STATE_DATA, "archive_write_data_block"); a->offset = offset; - r = write_data_block(a, buff, size); + if (a->todo & TODO_HFS_COMPRESSION) + r = hfs_write_data_block(a, buff, size); + else + r = write_data_block(a, buff, size); if (r < ARCHIVE_OK) return (r); if ((size_t)r < size) { @@ -737,6 +1481,8 @@ _archive_write_disk_data(struct archive *_a, const void *buff, size_t size) archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_DATA, "archive_write_data"); + if (a->todo & TODO_HFS_COMPRESSION) + return (hfs_write_data_block(a, buff, size)); return (write_data_block(a, buff, size)); } @@ -761,6 +1507,24 @@ _archive_write_disk_finish_entry(struct archive *_a) } else if (a->fd_offset == a->filesize) { /* Last write ended at exactly the filesize; we're done. */ /* Hopefully, this is the common case. */ +#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H) + } else if (a->todo & TODO_HFS_COMPRESSION) { + char null_d[1024]; + ssize_t r; + + if (a->file_remaining_bytes) + memset(null_d, 0, sizeof(null_d)); + while (a->file_remaining_bytes) { + if (a->file_remaining_bytes > sizeof(null_d)) + r = hfs_write_data_block( + a, null_d, sizeof(null_d)); + else + r = hfs_write_data_block( + a, null_d, a->file_remaining_bytes); + if (r < 0) + return ((int)r); + } +#endif } else { #if HAVE_FTRUNCATE if (ftruncate(a->fd, a->filesize) == -1 && @@ -799,6 +1563,22 @@ _archive_write_disk_finish_entry(struct archive *_a) /* Restore metadata. */ /* + * This is specific to Mac OS X. + * If the current file is an AppleDouble file, it should be + * linked with the data fork file and remove it. + */ + if (a->todo & TODO_APPLEDOUBLE) { + int r2 = fixup_appledouble(a, a->name); + if (r2 == ARCHIVE_EOF) { + /* The current file has been successfully linked + * with the data fork file and removed. So there + * is nothing to do on the current file. */ + goto finish_metadata; + } + if (r2 < ret) ret = r2; + } + + /* * Look up the "real" UID only if we're going to need it. * TODO: the TODO_SGID condition can be dropped here, can't it? */ @@ -820,8 +1600,10 @@ _archive_write_disk_finish_entry(struct archive *_a) * bits. If we set the owner, we know what it is and can skip * a stat() call to examine the ownership of the file on disk. */ - if (a->todo & TODO_OWNER) - ret = set_ownership(a); + if (a->todo & TODO_OWNER) { + int r2 = set_ownership(a); + if (r2 < ret) ret = r2; + } /* * set_mode must precede ACLs on systems such as Solaris and @@ -868,7 +1650,8 @@ _archive_write_disk_finish_entry(struct archive *_a) size_t metadata_size; metadata = archive_entry_mac_metadata(a->entry, &metadata_size); if (metadata != NULL && metadata_size > 0) { - int r2 = set_mac_metadata(a, archive_entry_pathname(a->entry), metadata, metadata_size); + int r2 = set_mac_metadata(a, archive_entry_pathname( + a->entry), metadata, metadata_size); if (r2 < ret) ret = r2; } } @@ -878,12 +1661,13 @@ _archive_write_disk_finish_entry(struct archive *_a) * ACLs that prevent attribute changes (including time). */ if (a->todo & TODO_ACLS) { - int r2 = set_acls(a, a->fd, + int r2 = archive_write_disk_set_acls(&a->archive, a->fd, archive_entry_pathname(a->entry), archive_entry_acl(a->entry)); if (r2 < ret) ret = r2; } +finish_metadata: /* If there's an fd, we can close it now. */ if (a->fd >= 0) { close(a->fd); @@ -950,12 +1734,12 @@ archive_write_disk_gid(struct archive *_a, const char *name, int64_t id) int64_t archive_write_disk_uid(struct archive *_a, const char *name, int64_t id) { - struct archive_write_disk *a = (struct archive_write_disk *)_a; - archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, - ARCHIVE_STATE_ANY, "archive_write_disk_uid"); - if (a->lookup_uid) - return (a->lookup_uid)(a->lookup_uid_data, name, id); - return (id); + struct archive_write_disk *a = (struct archive_write_disk *)_a; + archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, + ARCHIVE_STATE_ANY, "archive_write_disk_uid"); + if (a->lookup_uid) + return (a->lookup_uid)(a->lookup_uid_data, name, id); + return (id); } /* @@ -984,6 +1768,9 @@ archive_write_disk_new(void) free(a); return (NULL); } +#ifdef HAVE_ZLIB_H + a->decmpfs_compression_level = 5; +#endif return (&a->archive); } @@ -1008,7 +1795,8 @@ edit_deep_directories(struct archive_write_disk *a) return; /* Try to record our starting dir. */ - a->restore_pwd = open(".", O_RDONLY | O_BINARY); + a->restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC); + __archive_ensure_cloexec_flag(a->restore_pwd); if (a->restore_pwd < 0) return; @@ -1233,7 +2021,9 @@ create_filesystem_object(struct archive_write_disk *a) a->todo = 0; a->deferred = 0; } else if (r == 0 && a->filesize > 0) { - a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY); + a->fd = open(a->name, + O_WRONLY | O_TRUNC | O_BINARY | O_CLOEXEC); + __archive_ensure_cloexec_flag(a->fd); if (a->fd < 0) r = errno; } @@ -1262,7 +2052,7 @@ create_filesystem_object(struct archive_write_disk *a) * that SUID, SGID, etc, require additional work to ensure * security, so we never restore them at this point. */ - mode = final_mode & 0777 & a->user_umask; + mode = final_mode & 0777 & ~a->user_umask; switch (a->mode & AE_IFMT) { default: @@ -1270,7 +2060,8 @@ create_filesystem_object(struct archive_write_disk *a) /* FALLTHROUGH */ case AE_IFREG: a->fd = open(a->name, - O_WRONLY | O_CREAT | O_EXCL | O_BINARY, mode); + O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, mode); + __archive_ensure_cloexec_flag(a->fd); r = (a->fd < 0); break; case AE_IFCHR: @@ -1381,7 +2172,8 @@ _archive_write_disk_close(struct archive *_a) if (p->fixup & TODO_MODE_BASE) chmod(p->name, p->mode); if (p->fixup & TODO_ACLS) - set_acls(a, -1, p->name, &p->acl); + archive_write_disk_set_acls(&a->archive, + -1, p->name, &p->acl); if (p->fixup & TODO_FFLAGS) set_fflags_platform(a, -1, p->name, p->mode, p->fflags_set, 0); @@ -1419,6 +2211,23 @@ _archive_write_disk_free(struct archive *_a) archive_string_free(&a->path_safe); a->archive.magic = 0; __archive_clean(&a->archive); + free(a->decmpfs_header_p); + free(a->resource_fork); + free(a->compressed_buffer); + free(a->uncompressed_buffer); +#ifdef HAVE_ZLIB_H + if (a->stream_valid) { + switch (deflateEnd(&a->stream)) { + case Z_OK: + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Failed to clean up compressor"); + ret = ARCHIVE_FATAL; + break; + } + } +#endif free(a); return (ret); } @@ -1500,8 +2309,11 @@ new_fixup(struct archive_write_disk *a, const char *pathname) struct fixup_entry *fe; fe = (struct fixup_entry *)calloc(1, sizeof(struct fixup_entry)); - if (fe == NULL) + if (fe == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for a fixup"); return (NULL); + } fe->next = a->fixup_list; a->fixup_list = fe; fe->fixup = 0; @@ -1888,6 +2700,8 @@ create_dir(struct archive_write_disk *a, char *path) if (mkdir(path, mode) == 0) { if (mode != mode_final) { le = new_fixup(a, path); + if (le == NULL) + return (ARCHIVE_FATAL); le->fixup |=TODO_MODE_BASE; le->mode = mode_final; } @@ -2325,6 +3139,8 @@ set_fflags(struct archive_write_disk *a) */ if ((critical_flags != 0) && (set & critical_flags)) { le = current_fixup(a, a->name); + if (le == NULL) + return (ARCHIVE_FATAL); le->fixup |= TODO_FFLAGS; le->fflags_set = set; /* Store the mode if it's not already there. */ @@ -2403,8 +3219,8 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, { int ret; int myfd = fd; - unsigned long newflags, oldflags; - unsigned long sf_mask = 0; + int newflags, oldflags; + int sf_mask = 0; if (set == 0 && clear == 0) return (ARCHIVE_OK); @@ -2413,8 +3229,10 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, return (ARCHIVE_OK); /* If we weren't given an fd, open it ourselves. */ - if (myfd < 0) - myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY); + if (myfd < 0) { + myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC); + __archive_ensure_cloexec_flag(myfd); + } if (myfd < 0) return (ARCHIVE_OK); @@ -2499,12 +3317,196 @@ set_mac_metadata(struct archive_write_disk *a, const char *pathname, (void)metadata_size; /* UNUSED */ return (ARCHIVE_OK); } + +static int +fixup_appledouble(struct archive_write_disk *a, const char *pathname) +{ + (void)a; /* UNUSED */ + (void)pathname; /* UNUSED */ + return (ARCHIVE_OK); +} #else /* * On Mac OS, we use copyfile() to unpack the metadata and * apply it to the target file. */ + +#if defined(HAVE_SYS_XATTR_H) +static int +copy_xattrs(struct archive_write_disk *a, int tmpfd, int dffd) +{ + ssize_t xattr_size; + char *xattr_names = NULL, *xattr_val = NULL; + int ret = ARCHIVE_OK, xattr_i; + + xattr_size = flistxattr(tmpfd, NULL, 0, 0); + if (xattr_size == -1) { + archive_set_error(&a->archive, errno, + "Failed to read metadata(xattr)"); + ret = ARCHIVE_WARN; + goto exit_xattr; + } + xattr_names = malloc(xattr_size); + if (xattr_names == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for metadata(xattr)"); + ret = ARCHIVE_FATAL; + goto exit_xattr; + } + xattr_size = flistxattr(tmpfd, xattr_names, xattr_size, 0); + if (xattr_size == -1) { + archive_set_error(&a->archive, errno, + "Failed to read metadata(xattr)"); + ret = ARCHIVE_WARN; + goto exit_xattr; + } + for (xattr_i = 0; xattr_i < xattr_size; + xattr_i += strlen(xattr_names + xattr_i) + 1) { + ssize_t s; + int f; + + s = fgetxattr(tmpfd, xattr_names + xattr_i, NULL, 0, 0, 0); + if (s == -1) { + archive_set_error(&a->archive, errno, + "Failed to get metadata(xattr)"); + ret = ARCHIVE_WARN; + goto exit_xattr; + } + xattr_val = realloc(xattr_val, s); + if (xattr_val == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Failed to get metadata(xattr)"); + ret = ARCHIVE_WARN; + goto exit_xattr; + } + s = fgetxattr(tmpfd, xattr_names + xattr_i, xattr_val, s, 0, 0); + if (s == -1) { + archive_set_error(&a->archive, errno, + "Failed to get metadata(xattr)"); + ret = ARCHIVE_WARN; + goto exit_xattr; + } + f = fsetxattr(dffd, xattr_names + xattr_i, xattr_val, s, 0, 0); + if (f == -1) { + archive_set_error(&a->archive, errno, + "Failed to get metadata(xattr)"); + ret = ARCHIVE_WARN; + goto exit_xattr; + } + } +exit_xattr: + free(xattr_names); + free(xattr_val); + return (ret); +} +#endif + +static int +copy_acls(struct archive_write_disk *a, int tmpfd, int dffd) +{ + acl_t acl, dfacl = NULL; + int acl_r, ret = ARCHIVE_OK; + + acl = acl_get_fd(tmpfd); + if (acl == NULL) { + if (errno == ENOENT) + /* There are not any ACLs. */ + return (ret); + archive_set_error(&a->archive, errno, + "Failed to get metadata(acl)"); + ret = ARCHIVE_WARN; + goto exit_acl; + } + dfacl = acl_dup(acl); + acl_r = acl_set_fd(dffd, dfacl); + if (acl_r == -1) { + archive_set_error(&a->archive, errno, + "Failed to get metadata(acl)"); + ret = ARCHIVE_WARN; + goto exit_acl; + } +exit_acl: + if (acl) + acl_free(acl); + if (dfacl) + acl_free(dfacl); + return (ret); +} + +static int +create_tempdatafork(struct archive_write_disk *a, const char *pathname) +{ + struct archive_string tmpdatafork; + int tmpfd; + + archive_string_init(&tmpdatafork); + archive_strcpy(&tmpdatafork, "tar.md.XXXXXX"); + tmpfd = mkstemp(tmpdatafork.s); + if (tmpfd < 0) { + archive_set_error(&a->archive, errno, + "Failed to mkstemp"); + archive_string_free(&tmpdatafork); + return (-1); + } + if (copyfile(pathname, tmpdatafork.s, 0, + COPYFILE_UNPACK | COPYFILE_NOFOLLOW + | COPYFILE_ACL | COPYFILE_XATTR) < 0) { + archive_set_error(&a->archive, errno, + "Failed to restore metadata"); + close(tmpfd); + tmpfd = -1; + } + unlink(tmpdatafork.s); + archive_string_free(&tmpdatafork); + return (tmpfd); +} + +static int +copy_metadata(struct archive_write_disk *a, const char *metadata, + const char *datafork, int datafork_compressed) +{ + int ret = ARCHIVE_OK; + + if (datafork_compressed) { + int dffd, tmpfd; + + tmpfd = create_tempdatafork(a, metadata); + if (tmpfd == -1) + return (ARCHIVE_WARN); + + /* + * Do not open the data fork compressed by HFS+ compression + * with at least a writing mode(O_RDWR or O_WRONLY). it + * makes the data fork uncompressed. + */ + dffd = open(datafork, 0); + if (dffd == -1) { + archive_set_error(&a->archive, errno, + "Failed to open the data fork for metadata"); + close(tmpfd); + return (ARCHIVE_WARN); + } + +#if defined(HAVE_SYS_XATTR_H) + ret = copy_xattrs(a, tmpfd, dffd); + if (ret == ARCHIVE_OK) +#endif + ret = copy_acls(a, tmpfd, dffd); + close(tmpfd); + close(dffd); + } else { + if (copyfile(metadata, datafork, 0, + COPYFILE_UNPACK | COPYFILE_NOFOLLOW + | COPYFILE_ACL | COPYFILE_XATTR) < 0) { + archive_set_error(&a->archive, errno, + "Failed to restore metadata"); + ret = ARCHIVE_WARN; + } + } + return (ret); +} + static int set_mac_metadata(struct archive_write_disk *a, const char *pathname, const void *metadata, size_t metadata_size) @@ -2526,145 +3528,101 @@ set_mac_metadata(struct archive_write_disk *a, const char *pathname, if (fd < 0) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); + archive_string_free(&tmp); return (ARCHIVE_WARN); } written = write(fd, metadata, metadata_size); close(fd); - if ((size_t)written != metadata_size - || copyfile(tmp.s, pathname, 0, - COPYFILE_UNPACK | COPYFILE_NOFOLLOW - | COPYFILE_ACL | COPYFILE_XATTR)) { + if ((size_t)written != metadata_size) { archive_set_error(&a->archive, errno, "Failed to restore metadata"); ret = ARCHIVE_WARN; + } else { + int compressed; + +#if defined(UF_COMPRESSED) + if ((a->todo & TODO_HFS_COMPRESSION) != 0 && + (ret = lazy_stat(a)) == ARCHIVE_OK) + compressed = a->st.st_flags & UF_COMPRESSED; + else +#endif + compressed = 0; + ret = copy_metadata(a, tmp.s, pathname, compressed); } unlink(tmp.s); - return (ret); -} -#endif - -#ifndef HAVE_POSIX_ACL -/* Default empty function body to satisfy mainline code. */ -static int -set_acls(struct archive_write_disk *a, int fd, const char *name, - struct archive_acl *aacl) -{ - (void)a; /* UNUSED */ - (void)fd; /* UNUSED */ - (void)name; /* UNUSED */ - (void)aacl; /* UNUSED */ - return (ARCHIVE_OK); -} - -#else - -/* - * XXX TODO: What about ACL types other than ACCESS and DEFAULT? - */ -static int -set_acls(struct archive_write_disk *a, int fd, const char *name, - struct archive_acl *abstract_acl) -{ - int ret; - - ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_ACCESS, - ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access"); - if (ret != ARCHIVE_OK) - return (ret); - ret = set_acl(a, fd, name, abstract_acl, ACL_TYPE_DEFAULT, - ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default"); + archive_string_free(&tmp); return (ret); } - static int -set_acl(struct archive_write_disk *a, int fd, const char *name, - struct archive_acl *abstract_acl, - acl_type_t acl_type, int ae_requested_type, const char *tname) +fixup_appledouble(struct archive_write_disk *a, const char *pathname) { - acl_t acl; - acl_entry_t acl_entry; - acl_permset_t acl_permset; - int ret, r; - int ae_type, ae_permset, ae_tag, ae_id; - uid_t ae_uid; - gid_t ae_gid; - const char *ae_name; - int entries; + char buff[8]; + struct stat st; + const char *p; + struct archive_string datafork; + int fd = -1, ret = ARCHIVE_OK; + + archive_string_init(&datafork); + /* Check if the current file name is a type of the resource + * fork file. */ + p = strrchr(pathname, '/'); + if (p == NULL) + p = pathname; + else + p++; + if (p[0] != '.' || p[1] != '_') + goto skip_appledouble; - ret = ARCHIVE_OK; - entries = archive_acl_reset(abstract_acl, ae_requested_type); - if (entries == 0) - return (ARCHIVE_OK); - acl = acl_init(entries); - while ((r = archive_acl_next(&a->archive, abstract_acl, - ae_requested_type, &ae_type, &ae_permset, &ae_tag, &ae_id, - &ae_name)) == ARCHIVE_OK) { - acl_create_entry(&acl, &acl_entry); - - switch (ae_tag) { - case ARCHIVE_ENTRY_ACL_USER: - acl_set_tag_type(acl_entry, ACL_USER); - ae_uid = archive_write_disk_uid(&a->archive, - ae_name, ae_id); - acl_set_qualifier(acl_entry, &ae_uid); - break; - case ARCHIVE_ENTRY_ACL_GROUP: - acl_set_tag_type(acl_entry, ACL_GROUP); - ae_gid = archive_write_disk_gid(&a->archive, - ae_name, ae_id); - 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; - default: - /* XXX */ - break; - } + /* + * Check if the data fork file exists. + * + * TODO: Check if this write disk object has handled it. + */ + archive_strncpy(&datafork, pathname, p - pathname); + archive_strcat(&datafork, p + 2); + if (lstat(datafork.s, &st) == -1 || + (st.st_mode & AE_IFMT) != AE_IFREG) + goto skip_appledouble; - acl_get_permset(acl_entry, &acl_permset); - acl_clear_perms(acl_permset); - if (ae_permset & ARCHIVE_ENTRY_ACL_EXECUTE) - acl_add_perm(acl_permset, ACL_EXECUTE); - if (ae_permset & ARCHIVE_ENTRY_ACL_WRITE) - acl_add_perm(acl_permset, ACL_WRITE); - if (ae_permset & ARCHIVE_ENTRY_ACL_READ) - acl_add_perm(acl_permset, ACL_READ); + /* + * Check if the file is in the AppleDouble form. + */ + fd = open(pathname, O_RDONLY | O_BINARY | O_CLOEXEC); + __archive_ensure_cloexec_flag(fd); + if (fd == -1) { + archive_set_error(&a->archive, errno, + "Failed to open a restoring file"); + ret = ARCHIVE_WARN; + goto skip_appledouble; } - if (r == ARCHIVE_FATAL) { - acl_free(acl); + if (read(fd, buff, 8) == -1) { archive_set_error(&a->archive, errno, - "Failed to archive_acl_next"); - return (r); + "Failed to read a restoring file"); + close(fd); + ret = ARCHIVE_WARN; + goto skip_appledouble; } - - /* Try restoring the ACL through 'fd' if we can. */ -#if HAVE_ACL_SET_FD - if (fd >= 0 && acl_type == ACL_TYPE_ACCESS && acl_set_fd(fd, acl) == 0) - ret = ARCHIVE_OK; - else + close(fd); + /* Check AppleDouble Magic Code. */ + if (archive_be32dec(buff) != 0x00051607) + goto skip_appledouble; + /* Check AppleDouble Version. */ + if (archive_be32dec(buff+4) != 0x00020000) + goto skip_appledouble; + + ret = copy_metadata(a, pathname, datafork.s, +#if defined(UF_COMPRESSED) + st.st_flags & UF_COMPRESSED); #else -#if HAVE_ACL_SET_FD_NP - if (fd >= 0 && acl_set_fd_np(fd, acl, acl_type) == 0) - ret = ARCHIVE_OK; - else -#endif + 0); #endif - if (acl_set_file(name, acl_type, acl) != 0) { - archive_set_error(&a->archive, errno, "Failed to set %s acl", tname); - ret = ARCHIVE_WARN; + if (ret == ARCHIVE_OK) { + unlink(pathname); + ret = ARCHIVE_EOF; } - acl_free(acl); +skip_appledouble: + archive_string_free(&datafork); return (ret); } #endif diff --git a/libarchive/archive_write_disk_private.h b/libarchive/archive_write_disk_private.h index 707c0cf03b2e..d84e7e1cd64c 100644 --- a/libarchive/archive_write_disk_private.h +++ b/libarchive/archive_write_disk_private.h @@ -33,6 +33,11 @@ #ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED #define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED +#include "archive_acl_private.h" + struct archive_write_disk; +int +archive_write_disk_set_acls(struct archive *, int /* fd */, const char * /* pathname */, struct archive_acl *); + #endif diff --git a/libarchive/archive_write_disk_windows.c b/libarchive/archive_write_disk_windows.c index 39fd65fa0b8f..0f0780a8e47e 100644 --- a/libarchive/archive_write_disk_windows.c +++ b/libarchive/archive_write_disk_windows.c @@ -1511,7 +1511,7 @@ create_filesystem_object(struct archive_write_disk *a) * that SUID, SGID, etc, require additional work to ensure * security, so we never restore them at this point. */ - mode = final_mode & 0777 & a->user_umask; + mode = final_mode & 0777 & ~a->user_umask; switch (a->mode & AE_IFMT) { default: diff --git a/libarchive/archive_write_free.3 b/libarchive/archive_write_free.3 index ac01dd4b96dd..1b2d07131d8e 100644 --- a/libarchive/archive_write_free.3 +++ b/libarchive/archive_write_free.3 @@ -28,6 +28,7 @@ .Dt ARCHIVE_WRITE_FREE 3 .Os .Sh NAME +.Nm archive_write_fail , .Nm archive_write_close , .Nm archive_write_finish , .Nm archive_write_free @@ -37,6 +38,8 @@ Streaming Archive Library (libarchive, -larchive) .Sh SYNOPSIS .In archive.h .Ft int +.Fn archive_write_fail "struct archive *" +.Ft int .Fn archive_write_close "struct archive *" .Ft int .Fn archive_write_finish "struct archive *" @@ -44,6 +47,16 @@ Streaming Archive Library (libarchive, -larchive) .Fn archive_write_free "struct archive *" .Sh DESCRIPTION .Bl -tag -width indent +.It Fn archive_write_fail +Always returns +.Cm ARCHIVE_FATAL . +This marks the archive object as being unusable; +after calling this function, the only call that can succeed is +.Fn archive_write_free +to release the resources. +This can be used to speed recovery when the archive creation +must be aborted. +Note that the created archive is likely to be malformed in this case; .It Fn archive_write_close Complete the archive and invoke the close callback. .It Fn archive_write_finish diff --git a/libarchive/archive_write_open_filename.c b/libarchive/archive_write_open_filename.c index bcde200d46b9..196b770e8e83 100644 --- a/libarchive/archive_write_open_filename.c +++ b/libarchive/archive_write_open_filename.c @@ -46,11 +46,15 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_open_filename.c 191165 200 #endif #include "archive.h" +#include "archive_private.h" #include "archive_string.h" #ifndef O_BINARY #define O_BINARY 0 #endif +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif struct write_file_data { int fd; @@ -136,7 +140,7 @@ file_open(struct archive *a, void *client_data) const char *mbs; mine = (struct write_file_data *)client_data; - flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY; + flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC; /* * Open the file. @@ -171,6 +175,7 @@ file_open(struct archive *a, void *client_data) return (ARCHIVE_FATAL); } mine->fd = open(mbs, flags, 0666); + __archive_ensure_cloexec_flag(mine->fd); #endif if (mine->fd < 0) { if (mbs != NULL) diff --git a/libarchive/archive_write_private.h b/libarchive/archive_write_private.h index 06a24a8a611f..e600d54749bb 100644 --- a/libarchive/archive_write_private.h +++ b/libarchive/archive_write_private.h @@ -133,4 +133,13 @@ __archive_write_format_header_ustar(struct archive_write *, char buff[512], struct archive_entry *, int tartype, int strict, struct archive_string_conv *); +struct archive_write_program_data; +struct archive_write_program_data * __archive_write_program_allocate(void); +int __archive_write_program_free(struct archive_write_program_data *); +int __archive_write_program_open(struct archive_write_filter *, + struct archive_write_program_data *, const char *); +int __archive_write_program_close(struct archive_write_filter *, + struct archive_write_program_data *); +int __archive_write_program_write(struct archive_write_filter *, + struct archive_write_program_data *, const void *, size_t); #endif diff --git a/libarchive/archive_write_set_format_7zip.c b/libarchive/archive_write_set_format_7zip.c index 7e1240a2c32c..7847cb3c5994 100644 --- a/libarchive/archive_write_set_format_7zip.c +++ b/libarchive/archive_write_set_format_7zip.c @@ -385,7 +385,7 @@ _7z_options(struct archive_write *a, const char *key, const char *value) else { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, - "Unkonwn compression name: `%s'", + "Unknown compression name: `%s'", value); return (ARCHIVE_FAILED); } @@ -405,7 +405,7 @@ _7z_options(struct archive_write *a, const char *key, const char *value) value[1] != '\0') { archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC, - "Illeagal value `%s'", + "Illegal value `%s'", value); return (ARCHIVE_FAILED); } @@ -442,6 +442,14 @@ _7z_write_header(struct archive_write *a, struct archive_entry *entry) file_free(file); return (r); } + if (file->size == 0 && file->dir) { + if (!__archive_rb_tree_insert_node(&(zip->rbtree), + (struct archive_rb_node *)file)) { + /* We have already had the same file. */ + file_free(file); + return (ARCHIVE_OK); + } + } if (file->flg & MTIME_IS_SET) zip->total_number_time_defined[MTIME]++; @@ -450,11 +458,6 @@ _7z_write_header(struct archive_write *a, struct archive_entry *entry) if (file->flg & ATIME_IS_SET) zip->total_number_time_defined[ATIME]++; - if (file->size == 0 && file->dir) { - if (!__archive_rb_tree_insert_node(&(zip->rbtree), - (struct archive_rb_node *)file)) - file_free(file); - } zip->total_number_entry++; zip->total_bytes_entry_name += file->name_len + 2; if (file->size == 0) { @@ -501,7 +504,7 @@ _7z_write_header(struct archive_write *a, struct archive_entry *entry) bytes = compress_out(a, p, (size_t)file->size, ARCHIVE_Z_RUN); if (bytes < 0) return ((int)bytes); - zip->entry_crc32 = crc32(zip->entry_crc32, p, bytes); + zip->entry_crc32 = crc32(zip->entry_crc32, p, (unsigned)bytes); zip->entry_bytes_remaining -= bytes; } @@ -559,10 +562,11 @@ compress_out(struct archive_write *a, const void *buff, size_t s, return (0); if ((zip->crc32flg & PRECODE_CRC32) && s) - zip->precode_crc32 = crc32(zip->precode_crc32, buff, s); + zip->precode_crc32 = crc32(zip->precode_crc32, buff, + (unsigned)s); zip->stream.next_in = (const unsigned char *)buff; zip->stream.avail_in = s; - do { + for (;;) { /* Compress file data. */ r = compression_code(&(a->archive), &(zip->stream), run); if (r != ARCHIVE_OK && r != ARCHIVE_EOF) @@ -576,8 +580,12 @@ compress_out(struct archive_write *a, const void *buff, size_t s, if (zip->crc32flg & ENCODED_CRC32) zip->encoded_crc32 = crc32(zip->encoded_crc32, zip->wbuff, sizeof(zip->wbuff)); + if (run == ARCHIVE_Z_FINISH && r != ARCHIVE_EOF) + continue; } - } while (zip->stream.avail_in); + if (zip->stream.avail_in == 0) + break; + } if (run == ARCHIVE_Z_FINISH) { uint64_t bytes = sizeof(zip->wbuff) - zip->stream.avail_out; if (write_to_temp(a, zip->wbuff, (size_t)bytes) != ARCHIVE_OK) @@ -605,7 +613,7 @@ _7z_write_data(struct archive_write *a, const void *buff, size_t s) bytes = compress_out(a, buff, s, ARCHIVE_Z_RUN); if (bytes < 0) return (bytes); - zip->entry_crc32 = crc32(zip->entry_crc32, buff, bytes); + zip->entry_crc32 = crc32(zip->entry_crc32, buff, (unsigned)bytes); zip->entry_bytes_remaining -= bytes; return (bytes); } @@ -627,7 +635,7 @@ _7z_finish_entry(struct archive_write *a) s = a->null_length; r = _7z_write_data(a, a->nulls, s); if (r < 0) - return (r); + return ((int)r); } zip->total_bytes_compressed += zip->stream.total_in; zip->total_bytes_uncompressed += zip->stream.total_out; @@ -859,7 +867,7 @@ enc_uint64(struct archive_write *a, uint64_t val) numdata[0] |= mask; mask >>= 1; } - return (compress_out(a, numdata, i, ARCHIVE_Z_RUN)); + return ((int)compress_out(a, numdata, i, ARCHIVE_Z_RUN)); } static int @@ -924,7 +932,7 @@ make_substreamsInfo(struct archive_write *a, struct coder *coders) if (file->size == 0) break; archive_le32enc(crc, file->crc32); - r = compress_out(a, crc, 4, ARCHIVE_Z_RUN); + r = (int)compress_out(a, crc, 4, ARCHIVE_Z_RUN); if (r < 0) return (r); } @@ -948,7 +956,7 @@ make_streamsInfo(struct archive_write *a, uint64_t offset, uint64_t pack_size, int i, r; if (coders->codec == _7Z_COPY) - numFolders = zip->total_number_nonempty_entry; + numFolders = (int)zip->total_number_nonempty_entry; else numFolders = 1; @@ -1044,7 +1052,7 @@ make_streamsInfo(struct archive_write *a, uint64_t offset, uint64_t pack_size, /* Write Codec ID. */ codec_size &= 0x0f; - r = compress_out(a, &codec_buff[8-codec_size], + r = (int)compress_out(a, &codec_buff[8-codec_size], codec_size, ARCHIVE_Z_RUN); if (r < 0) return (r); @@ -1056,7 +1064,7 @@ make_streamsInfo(struct archive_write *a, uint64_t offset, uint64_t pack_size, return (r); /* Write Codec properties. */ - r = compress_out(a, coders[i].props, + r = (int)compress_out(a, coders[i].props, coders[i].prop_size, ARCHIVE_Z_RUN); if (r < 0) return (r); @@ -1102,7 +1110,7 @@ make_streamsInfo(struct archive_write *a, uint64_t offset, uint64_t pack_size, if (r < 0) return (r); archive_le32enc(crc, header_crc); - r = compress_out(a, crc, 4, ARCHIVE_Z_RUN); + r = (int)compress_out(a, crc, 4, ARCHIVE_Z_RUN); if (r < 0) return (r); } @@ -1196,7 +1204,7 @@ make_time(struct archive_write *a, uint8_t type, unsigned flg, int ti) b |= mask; mask >>= 1; if (mask == 0) { - r = compress_out(a, &b, 1, ARCHIVE_Z_RUN); + r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN); if (r < 0) return (r); mask = 0x80; @@ -1204,7 +1212,7 @@ make_time(struct archive_write *a, uint8_t type, unsigned flg, int ti) } } if (mask != 0x80) { - r = compress_out(a, &b, 1, ARCHIVE_Z_RUN); + r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN); if (r < 0) return (r); } @@ -1225,7 +1233,7 @@ make_time(struct archive_write *a, uint8_t type, unsigned flg, int ti) continue; archive_le64enc(filetime, utcToFiletime(file->times[ti].time, file->times[ti].time_ns)); - r = compress_out(a, filetime, 8, ARCHIVE_Z_RUN); + r = (int)compress_out(a, filetime, 8, ARCHIVE_Z_RUN); if (r < 0) return (r); } @@ -1296,7 +1304,7 @@ make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size, b |= mask; mask >>= 1; if (mask == 0) { - r = compress_out(a, &b, 1, ARCHIVE_Z_RUN); + r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN); if (r < 0) return (r); mask = 0x80; @@ -1304,7 +1312,7 @@ make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size, } } if (mask != 0x80) { - r = compress_out(a, &b, 1, ARCHIVE_Z_RUN); + r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN); if (r < 0) return (r); } @@ -1331,7 +1339,7 @@ make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size, b |= mask; mask >>= 1; if (mask == 0) { - r = compress_out(a, &b, 1, ARCHIVE_Z_RUN); + r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN); if (r < 0) return (r); mask = 0x80; @@ -1339,7 +1347,7 @@ make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size, } } if (mask != 0x80) { - r = compress_out(a, &b, 1, ARCHIVE_Z_RUN); + r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN); if (r < 0) return (r); } @@ -1362,7 +1370,7 @@ make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size, file = zip->file_list.first; for (;file != NULL; file = file->next) { - r = compress_out(a, file->utf16name, file->name_len+2, + r = (int)compress_out(a, file->utf16name, file->name_len+2, ARCHIVE_Z_RUN); if (r < 0) return (r); @@ -1418,7 +1426,7 @@ make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size, attr |= 1;/* Read Only. */ attr |= ((uint32_t)file->mode) << 16; archive_le32enc(&encattr, attr); - r = compress_out(a, &encattr, 4, ARCHIVE_Z_RUN); + r = (int)compress_out(a, &encattr, 4, ARCHIVE_Z_RUN); if (r < 0) return (r); } @@ -1512,7 +1520,7 @@ file_new(struct archive_write *a, struct archive_entry *entry, memcpy(file->utf16name, u16, u16len); file->utf16name[u16len+0] = 0; file->utf16name[u16len+1] = 0; - file->name_len = u16len; + file->name_len = (unsigned)u16len; file->mode = archive_entry_mode(entry); if (archive_entry_filetype(entry) == AE_IFREG) file->size = archive_entry_size(entry); @@ -1677,10 +1685,10 @@ compression_init_encoder_deflate(struct archive *a, * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in; - strm->avail_in = lastrm->avail_in; + strm->avail_in = (uInt)lastrm->avail_in; strm->total_in = (uLong)lastrm->total_in; strm->next_out = lastrm->next_out; - strm->avail_out = lastrm->avail_out; + strm->avail_out = (uInt)lastrm->avail_out; strm->total_out = (uLong)lastrm->total_out; if (deflateInit2(strm, level, Z_DEFLATED, (withheader)?15:-15, @@ -1710,10 +1718,10 @@ compression_code_deflate(struct archive *a, * of ugly hackery to convert a const * pointer to * a non-const pointer. */ strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in; - strm->avail_in = lastrm->avail_in; + strm->avail_in = (uInt)lastrm->avail_in; strm->total_in = (uLong)lastrm->total_in; strm->next_out = lastrm->next_out; - strm->avail_out = lastrm->avail_out; + strm->avail_out = (uInt)lastrm->avail_out; strm->total_out = (uLong)lastrm->total_out; r = deflate(strm, (action == ARCHIVE_Z_FINISH)? Z_FINISH: Z_NO_FLUSH); diff --git a/libarchive/archive_write_set_format_ar.c b/libarchive/archive_write_set_format_ar.c index beff49031b23..9f17564c34c3 100644 --- a/libarchive/archive_write_set_format_ar.c +++ b/libarchive/archive_write_set_format_ar.c @@ -165,7 +165,7 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) * Reject files with empty name. */ pathname = archive_entry_pathname(entry); - if (*pathname == '\0') { + if (pathname == NULL || *pathname == '\0') { archive_set_error(&a->archive, EINVAL, "Invalid filename"); return (ARCHIVE_WARN); diff --git a/libarchive/archive_write_set_format_by_name.c b/libarchive/archive_write_set_format_by_name.c index 9671f60f8cd1..af3105e482d6 100644 --- a/libarchive/archive_write_set_format_by_name.c +++ b/libarchive/archive_write_set_format_by_name.c @@ -56,8 +56,10 @@ struct { const char *name; int (*setter)(struct archive *); } names[] = { "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 }, @@ -65,6 +67,8 @@ struct { const char *name; int (*setter)(struct archive *); } names[] = { "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 }, { "xar", archive_write_set_format_xar }, { "zip", archive_write_set_format_zip }, { NULL, NULL } diff --git a/libarchive/archive_write_set_format_iso9660.c b/libarchive/archive_write_set_format_iso9660.c index 483de9086589..591370298d51 100644 --- a/libarchive/archive_write_set_format_iso9660.c +++ b/libarchive/archive_write_set_format_iso9660.c @@ -994,7 +994,7 @@ static void isoent_remove_child(struct isoent *, struct isoent *); static void isoent_setup_directory_location(struct iso9660 *, int, struct vdd *); static void isoent_setup_file_location(struct iso9660 *, int); -static int get_path_component(char *, int, const char *); +static int get_path_component(char *, size_t, const char *); static int isoent_tree(struct archive_write *, struct isoent **); static struct isoent *isoent_find_child(struct isoent *, const char *); static struct isoent *isoent_find_entry(struct isoent *, const char *); @@ -2889,7 +2889,7 @@ set_directory_record_rr(unsigned char *bp, int dr_len, if (nmmax > 0xff) nmmax = 0xff; while (nmlen + 5 > nmmax) { - length = nmmax; + length = (int)nmmax; if (bp != NULL) { bp[3] = length; bp[5] = 0x01;/* Alternate Name continues @@ -2912,7 +2912,7 @@ set_directory_record_rr(unsigned char *bp, int dr_len, bp[4] = 1; /* version */ } } - length = 5 + nmlen; + length = 5 + (int)nmlen; if (bp != NULL) { bp[3] = length; bp[5] = 0; @@ -3511,7 +3511,7 @@ set_directory_record(unsigned char *p, size_t n, struct isoent *isoent, /* Volume Sequence Number */ set_num_723(bp+29, iso9660->volume_sequence_number); /* Length of File Identifier */ - set_num_711(bp+33, fi_len); + set_num_711(bp+33, (unsigned char)fi_len); /* File Identifier */ switch (t) { case DIR_REC_VD: @@ -3542,20 +3542,20 @@ set_directory_record(unsigned char *p, size_t n, struct isoent *isoent, if (t == DIR_REC_VD) { if (p != NULL) /* Length of Directory Record */ - set_num_711(p, dr_len); + set_num_711(p, (unsigned char)dr_len); else - isoent->dr_len.vd = dr_len; - return (dr_len); + isoent->dr_len.vd = (int)dr_len; + return ((int)dr_len); } /* Rockridge */ if (iso9660->opt.rr && vdd_type != VDD_JOLIET) - dr_len = set_directory_record_rr(bp, dr_len, + dr_len = set_directory_record_rr(bp, (int)dr_len, isoent, iso9660, t); if (p != NULL) /* Length of Directory Record */ - set_num_711(p, dr_len); + set_num_711(p, (unsigned char)dr_len); else { /* * Save the size which is needed to write this @@ -3568,15 +3568,15 @@ set_directory_record(unsigned char *p, size_t n, struct isoent *isoent, * in switch .... */ break; case DIR_REC_SELF: - isoent->dr_len.self = dr_len; break; + isoent->dr_len.self = (int)dr_len; break; case DIR_REC_PARENT: - isoent->dr_len.parent = dr_len; break; + isoent->dr_len.parent = (int)dr_len; break; case DIR_REC_NORMAL: - isoent->dr_len.normal = dr_len; break; + isoent->dr_len.normal = (int)dr_len; break; } } - return (dr_len); + return ((int)dr_len); } /* @@ -4255,7 +4255,7 @@ _write_path_table(struct archive_write *a, int type_m, int depth, bp = wb -1; } /* Length of Directory Identifier */ - set_num_711(bp+1, len); + set_num_711(bp+1, (unsigned char)len); /* Extended Attribute Record Length */ set_num_711(bp+2, 0); /* Location of Extent */ @@ -4278,7 +4278,7 @@ _write_path_table(struct archive_write *a, int type_m, int depth, bp[9+len] = 0; len++; } - wsize += 8 + len; + wsize += 8 + (int)len; bp += 8 + len; } if ((bp + 1) > wb) { @@ -5448,7 +5448,8 @@ isoent_setup_file_location(struct iso9660 *iso9660, int location) size = fd_boot_image_size(iso9660->el_torito.media_type); if (size == 0) size = (size_t)archive_entry_size(isoent->file->entry); - block = (size + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS; + block = ((int)size + LOGICAL_BLOCK_SIZE -1) + >> LOGICAL_BLOCK_BITS; location += block; iso9660->total_file_block += block; isoent->file->content.blocks = block; @@ -5509,10 +5510,10 @@ isoent_setup_file_location(struct iso9660 *iso9660, int location) } static int -get_path_component(char *name, int n, const char *fn) +get_path_component(char *name, size_t n, const char *fn) { char *p; - int l; + size_t l; p = strchr(fn, '/'); if (p == NULL) { @@ -5525,7 +5526,7 @@ get_path_component(char *name, int n, const char *fn) memcpy(name, fn, l); name[l] = '\0'; - return (l); + return ((int)l); } /* @@ -5818,17 +5819,18 @@ idr_ensure_poolsize(struct archive_write *a, struct idr *idr, { if (idr->pool_size < cnt) { + void *p; const int bk = (1 << 7) - 1; int psize; psize = (cnt + bk) & ~bk; - idr->idrent_pool = realloc(idr->idrent_pool, - sizeof(struct idrent) * psize); - if (idr->idrent_pool == NULL) { + p = realloc(idr->idrent_pool, sizeof(struct idrent) * psize); + if (p == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory"); return (ARCHIVE_FATAL); } + idr->idrent_pool = (struct idrent *)p; idr->pool_size = psize; } return (ARCHIVE_OK); @@ -6016,7 +6018,7 @@ isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, char *dot, *xdot; int ext_off, noff, weight; - l = np->file->basename.length; + l = (int)np->file->basename.length; p = malloc(l+31+2+1); if (p == NULL) { archive_set_error(&a->archive, ENOMEM, @@ -6080,7 +6082,7 @@ isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, ext_off = l; } else { *dot = '.'; - ext_off = dot - p; + ext_off = (int)(dot - p); if (iso9660->opt.iso_level == 1) { if (dot - p <= 8) { @@ -6107,11 +6109,11 @@ isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, ext_off = dnmax; } } else if (l > ffmax) { - int extlen = strlen(dot); + int extlen = (int)strlen(dot); int xdoff; if (xdot != NULL) - xdoff = xdot - p; + xdoff = (int)(xdot - p); else xdoff = 0; @@ -6148,7 +6150,7 @@ isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent, } /* Save an offset of a file name extension to sort files. */ np->ext_off = ext_off; - np->ext_len = strlen(&p[ext_off]); + np->ext_len = (int)strlen(&p[ext_off]); np->id_len = l = ext_off + np->ext_len; /* Make an offset of the number which is used to be set @@ -6265,10 +6267,10 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, p += 2; lt -= 2; } - ext_off = dot - (unsigned char *)np->identifier; + ext_off = (int)(dot - (unsigned char *)np->identifier); np->ext_off = ext_off; - np->ext_len = l - ext_off; - np->id_len = l; + np->ext_len = (int)l - ext_off; + np->id_len = (int)l; /* * Get a length of MBS of a full-pathname. @@ -6282,11 +6284,11 @@ isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent, "No memory"); return (ARCHIVE_FATAL); } - np->mb_len = iso9660->mbs.length; + np->mb_len = (int)iso9660->mbs.length; if (np->mb_len != (int)np->file->basename.length) weight = np->mb_len; } else - np->mb_len = np->file->basename.length; + np->mb_len = (int)np->file->basename.length; /* If a length of full-pathname is longer than 240 bytes, * it violates Joliet extensions regulation. */ @@ -6916,12 +6918,22 @@ isoent_make_path_table_2(struct archive_write *a, struct vdd *vdd, switch (vdd->vdd_type) { case VDD_PRIMARY: case VDD_ENHANCED: +#ifdef __COMPAR_FN_T + qsort(enttbl, pt->cnt, sizeof(struct isoent *), + (__compar_fn_t)_compare_path_table); +#else qsort(enttbl, pt->cnt, sizeof(struct isoent *), _compare_path_table); +#endif break; case VDD_JOLIET: +#ifdef __COMPAR_FN_T + qsort(enttbl, pt->cnt, sizeof(struct isoent *), + (__compar_fn_t)_compare_path_table_joliet); +#else qsort(enttbl, pt->cnt, sizeof(struct isoent *), _compare_path_table_joliet); +#endif break; } for (i = 0; i < pt->cnt; i++) @@ -7390,7 +7402,7 @@ zisofs_init(struct archive_write *a, struct isofile *file) struct iso9660 *iso9660 = a->format_data; #ifdef HAVE_ZLIB_H uint64_t tsize; - size_t ceil, bpsize; + size_t _ceil, bpsize; int r; #endif @@ -7429,9 +7441,9 @@ zisofs_init(struct archive_write *a, struct isofile *file) (uint32_t)archive_entry_size(file->entry); /* Calculate a size of Block Pointers of zisofs. */ - ceil = (file->zisofs.uncompressed_size + ZF_BLOCK_SIZE -1) + _ceil = (file->zisofs.uncompressed_size + ZF_BLOCK_SIZE -1) >> file->zisofs.log2_bs; - iso9660->zisofs.block_pointers_cnt = ceil + 1; + iso9660->zisofs.block_pointers_cnt = (int)_ceil + 1; iso9660->zisofs.block_pointers_idx = 0; /* Ensure a buffer size used for Block Pointers */ @@ -7483,7 +7495,7 @@ zisofs_detect_magic(struct archive_write *a, const void *buff, size_t s) uint32_t uncompressed_size; unsigned char header_size; unsigned char log2_bs; - size_t ceil, doff; + size_t _ceil, doff; uint32_t bst, bed; int magic_max; int64_t entry_size; @@ -7507,7 +7519,7 @@ zisofs_detect_magic(struct archive_write *a, const void *buff, size_t s) l = s; memcpy(iso9660->zisofs.magic_buffer + iso9660->zisofs.magic_cnt, buff, l); - iso9660->zisofs.magic_cnt += l; + iso9660->zisofs.magic_cnt += (int)l; if (iso9660->zisofs.magic_cnt < magic_max) return; } @@ -7531,16 +7543,16 @@ zisofs_detect_magic(struct archive_write *a, const void *buff, size_t s) return;/* Invalid or not supported header. */ /* Calculate a size of Block Pointers of zisofs. */ - ceil = (uncompressed_size + + _ceil = (uncompressed_size + (ARCHIVE_LITERAL_LL(1) << log2_bs) -1) >> log2_bs; - doff = (ceil + 1) * 4 + 16; + doff = (_ceil + 1) * 4 + 16; if (entry_size < (int64_t)doff) return;/* Invalid data. */ /* Check every Block Pointer has valid value. */ p = magic_buff + 16; endp = magic_buff + magic_max; - while (ceil && p + 8 <= endp) { + while (_ceil && p + 8 <= endp) { bst = archive_le32dec(p); if (bst != doff) return;/* Invalid data. */ @@ -7549,7 +7561,7 @@ zisofs_detect_magic(struct archive_write *a, const void *buff, size_t s) if (bed < bst || bed > entry_size) return;/* Invalid data. */ doff += bed - bst; - ceil--; + _ceil--; } file->zisofs.uncompressed_size = uncompressed_size; @@ -7577,7 +7589,7 @@ zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s) zstrm = &(iso9660->zisofs.stream); zstrm->next_out = wb_buffptr(a); - zstrm->avail_out = wb_remaining(a); + zstrm->avail_out = (uInt)wb_remaining(a); b = (const unsigned char *)buff; do { avail = ZF_BLOCK_SIZE - zstrm->total_in; @@ -7591,7 +7603,7 @@ zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s) flush = Z_FINISH; zstrm->next_in = (Bytef *)(uintptr_t)(const void *)b; - zstrm->avail_in = avail; + zstrm->avail_in = (uInt)avail; /* * Check if current data block are all zero. @@ -7647,7 +7659,7 @@ zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s) iso9660->zisofs.total_size += csize; iso9660->cur_file->cur_content->size += csize; zstrm->next_out = wb_buffptr(a); - zstrm->avail_out = wb_remaining(a); + zstrm->avail_out = (uInt)wb_remaining(a); break; default: archive_set_error(&a->archive, @@ -7792,13 +7804,13 @@ zisofs_extract_init(struct archive_write *a, struct zisofs_extract *zisofs, const unsigned char *p, size_t bytes) { size_t avail = bytes; - size_t ceil, xsize; + size_t _ceil, xsize; /* Allocate block pointers buffer. */ - ceil = (size_t)((zisofs->pz_uncompressed_size + + _ceil = (size_t)((zisofs->pz_uncompressed_size + (((int64_t)1) << zisofs->pz_log2_bs) - 1) >> zisofs->pz_log2_bs); - xsize = (ceil + 1) * 4; + xsize = (_ceil + 1) * 4; if (zisofs->block_pointers == NULL) { size_t alloc = ((xsize >> 10) + 1) << 10; zisofs->block_pointers = malloc(alloc); @@ -7811,7 +7823,7 @@ zisofs_extract_init(struct archive_write *a, struct zisofs_extract *zisofs, zisofs->block_pointers_size = xsize; /* Allocate uncompressed data buffer. */ - zisofs->uncompressed_buffer_size = 1UL << zisofs->pz_log2_bs; + zisofs->uncompressed_buffer_size = (size_t)1UL << zisofs->pz_log2_bs; /* * Read the file header, and check the magic code of zisofs. @@ -7881,7 +7893,7 @@ zisofs_extract(struct archive_write *a, struct zisofs_extract *zisofs, return (rs); if (!zisofs->initialized) { /* We need more data. */ - zisofs->pz_offset += bytes; + zisofs->pz_offset += (uint32_t)bytes; return (bytes); } avail = rs; @@ -7964,9 +7976,9 @@ zisofs_extract(struct archive_write *a, struct zisofs_extract *zisofs, if (avail > zisofs->block_avail) zisofs->stream.avail_in = zisofs->block_avail; else - zisofs->stream.avail_in = avail; + zisofs->stream.avail_in = (uInt)avail; zisofs->stream.next_out = wb_buffptr(a); - zisofs->stream.avail_out = wb_remaining(a); + zisofs->stream.avail_out = (uInt)wb_remaining(a); r = inflate(&zisofs->stream, 0); switch (r) { @@ -7979,12 +7991,12 @@ zisofs_extract(struct archive_write *a, struct zisofs_extract *zisofs, return (ARCHIVE_FATAL); } avail -= zisofs->stream.next_in - p; - zisofs->block_avail -= zisofs->stream.next_in - p; + zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p); r = wb_consume(a, wb_remaining(a) - zisofs->stream.avail_out); if (r < 0) return (r); } - zisofs->pz_offset += bytes; + zisofs->pz_offset += (uint32_t)bytes; return (bytes - avail); } diff --git a/libarchive/archive_write_set_format_mtree.c b/libarchive/archive_write_set_format_mtree.c index 4b300012f7a6..9c0613c9b025 100644 --- a/libarchive/archive_write_set_format_mtree.c +++ b/libarchive/archive_write_set_format_mtree.c @@ -38,6 +38,8 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_mtree.c 201171 #include "archive_crypto_private.h" #include "archive_entry.h" #include "archive_private.h" +#include "archive_rb.h" +#include "archive_string.h" #include "archive_write_private.h" #define INDENTNAMELEN 15 @@ -45,27 +47,39 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_mtree.c 201171 #define SET_KEYS \ (F_FLAGS | F_GID | F_GNAME | F_MODE | F_TYPE | F_UID | F_UNAME) -struct mtree_entry { - struct mtree_entry *next; +struct attr_counter { + struct attr_counter *prev; + struct attr_counter *next; + struct mtree_entry *m_entry; + int count; +}; - char *pathname; - char *symlink; - unsigned int nlink; - mode_t filetype; - mode_t mode; - int64_t uid; - int64_t gid; - char *uname; - char *gname; - char *fflags_text; - unsigned long fflags_set; - unsigned long fflags_clear; - time_t mtime; - long mtime_nsec; - dev_t rdevmajor; - dev_t rdevminor; - int64_t size; +struct att_counter_set { + struct attr_counter *uid_list; + struct attr_counter *gid_list; + struct attr_counter *mode_list; + struct attr_counter *flags_list; +}; + +struct mtree_chain { + struct mtree_entry *first; + struct mtree_entry **last; +}; + +/* + * The Data only for a directory file. + */ +struct dir_info { + struct archive_rb_tree rbtree; + struct mtree_chain children; + struct mtree_entry *chnext; + int virtual; +}; +/* + * The Data only for a regular file. + */ +struct reg_info { int compute_sum; uint32_t crc; #ifdef ARCHIVE_HAS_MD5 @@ -88,23 +102,51 @@ struct mtree_entry { #endif }; -struct attr_counter { - struct attr_counter *prev; - struct attr_counter *next; - int count; - struct mtree_entry *m_entry; +struct mtree_entry { + struct archive_rb_node rbnode; + struct mtree_entry *next; + struct mtree_entry *parent; + struct dir_info *dir_info; + struct reg_info *reg_info; + + struct archive_string parentdir; + struct archive_string basename; + struct archive_string pathname; + struct archive_string symlink; + struct archive_string uname; + struct archive_string gname; + struct archive_string fflags_text; + unsigned int nlink; + mode_t filetype; + mode_t mode; + int64_t size; + int64_t uid; + int64_t gid; + time_t mtime; + long mtime_nsec; + unsigned long fflags_set; + unsigned long fflags_clear; + dev_t rdevmajor; + dev_t rdevminor; }; struct mtree_writer { struct mtree_entry *mtree_entry; + struct mtree_entry *root; + struct mtree_entry *cur_dirent; + struct archive_string cur_dirstr; + struct mtree_chain file_list; + struct archive_string ebuf; struct archive_string buf; int first; uint64_t entry_bytes_remaining; + + /* + * Set global value. + */ struct { - int output; - int processed; - struct archive_string parent; + int processing; mode_t type; int keys; int64_t uid; @@ -112,14 +154,11 @@ struct mtree_writer { mode_t mode; unsigned long fflags_set; unsigned long fflags_clear; - - struct attr_counter *uid_list; - struct attr_counter *gid_list; - struct attr_counter *mode_list; - struct attr_counter *flags_list; - struct mtree_entry *me_first; - struct mtree_entry **me_last; } set; + struct att_counter_set acs; + int classic; + int depth; + /* check sum */ int compute_sum; uint32_t crc; @@ -173,27 +212,51 @@ struct mtree_writer { #define F_SHA512 0x02000000 /* SHA-512 digest */ /* Options */ - int dironly; /* if the dironly is 1, ignore everything except - * directory type files. like mtree(8) -d option. - */ - int indent; /* if the indent is 1, indent writing data. */ + int dironly; /* If it is set, ignore all files except + * directory files, like mtree(8) -d option. */ + int indent; /* If it is set, indent output data. */ + int output_global_set; /* If it is set, use /set keyword to set + * global values. When generating mtree + * classic format, it is set by default. */ }; #define DEFAULT_KEYS (F_DEV | F_FLAGS | F_GID | F_GNAME | F_SLINK | F_MODE\ | F_NLINK | F_SIZE | F_TIME | F_TYPE | F_UID\ | F_UNAME) +#define attr_counter_set_reset attr_counter_set_free -static struct attr_counter * new_attr_count(struct mtree_entry *, - struct attr_counter *); -static void free_attr_count(struct attr_counter **); -static int inc_attr_count(struct attr_counter **, struct attr_counter *, +static void attr_counter_free(struct attr_counter **); +static int attr_counter_inc(struct attr_counter **, struct attr_counter *, struct attr_counter *, struct mtree_entry *); -static int collect_set_values(struct mtree_writer *, struct mtree_entry *); -static int get_keys(struct mtree_writer *, struct mtree_entry *); +static struct attr_counter * attr_counter_new(struct mtree_entry *, + struct attr_counter *); +static int attr_counter_set_collect(struct mtree_writer *, + struct mtree_entry *); +static void attr_counter_set_free(struct mtree_writer *); +static int get_global_set_keys(struct mtree_writer *, struct mtree_entry *); +static int mtree_entry_add_child_tail(struct mtree_entry *, + struct mtree_entry *); +static int mtree_entry_create_virtual_dir(struct archive_write *, const char *, + struct mtree_entry **); +static int mtree_entry_cmp_node(const struct archive_rb_node *, + const struct archive_rb_node *); +static int mtree_entry_cmp_key(const struct archive_rb_node *, const void *); +static int mtree_entry_exchange_same_entry(struct archive_write *, + struct mtree_entry *, struct mtree_entry *); +static void mtree_entry_free(struct mtree_entry *); +static int mtree_entry_new(struct archive_write *, struct archive_entry *, + struct mtree_entry **); +static void mtree_entry_register_free(struct mtree_writer *); +static void mtree_entry_register_init(struct mtree_writer *); +static int mtree_entry_setup_filenames(struct archive_write *, + struct mtree_entry *, struct archive_entry *); +static int mtree_entry_tree_add(struct archive_write *, struct mtree_entry **); static void sum_init(struct mtree_writer *); static void sum_update(struct mtree_writer *, const void *, size_t); -static void sum_final(struct mtree_writer *, struct mtree_entry *); -static void sum_write(struct archive_string *, struct mtree_entry *); +static void sum_final(struct mtree_writer *, struct reg_info *); +static void sum_write(struct archive_string *, struct reg_info *); +static int write_mtree_entry(struct archive_write *, struct mtree_entry *); +static int write_dot_dot_entry(struct archive_write *, struct mtree_entry *); #define COMPUTE_CRC(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)] static const uint32_t crctab[] = { @@ -251,26 +314,30 @@ static const uint32_t crctab[] = { 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 }; -static int -mtree_safe_char(char c) -{ - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) - return 1; - if (c >= '0' && c <= '9') - return 1; - if (c == 35 || c == 61 || c == 92) - return 0; /* #, = and \ are always quoted */ - - if (c >= 33 && c <= 47) /* !"$%&'()*+,-./ */ - return 1; - if (c >= 58 && c <= 64) /* :;<>?@ */ - return 1; - if (c >= 91 && c <= 96) /* []^_` */ - return 1; - if (c >= 123 && c <= 126) /* {|}~ */ - return 1; - return 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:0x20( ) 0x23(#) */ + 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ + /* 0123456789:;<>? EXCLUSION:0x3d(=) */ + 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[]^_ EXCLUSION:0x5c(\) */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 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 */ +}; static void mtree_quote(struct archive_string *s, const char *str) @@ -280,7 +347,7 @@ mtree_quote(struct archive_string *s, const char *str) unsigned char c; for (start = str; *str != '\0'; ++str) { - if (mtree_safe_char(*str)) + if (safe_char[*(const unsigned char *)str]) continue; if (start != str) archive_strncat(s, start, str - start); @@ -303,9 +370,19 @@ mtree_quote(struct archive_string *s, const char *str) static void mtree_indent(struct mtree_writer *mtree) { - int i, fn; + int i, fn, nd, pd; const char *r, *s, *x; + if (mtree->classic) { + if (mtree->indent) { + nd = 0; + pd = mtree->depth * 4; + } else { + nd = mtree->depth?4:0; + pd = 0; + } + } else + nd = pd = 0; fn = 1; s = r = mtree->ebuf.s; x = NULL; @@ -314,37 +391,46 @@ mtree_indent(struct mtree_writer *mtree) while ((r = strchr(r, ' ')) != NULL) { if (fn) { fn = 0; + for (i = 0; i < nd + pd; i++) + archive_strappend_char(&mtree->buf, ' '); archive_strncat(&mtree->buf, s, r - s); - if (r -s > INDENTNAMELEN) { + if (nd + (r -s) > INDENTNAMELEN) { archive_strncat(&mtree->buf, " \\\n", 3); - for (i = 0; i < (INDENTNAMELEN + 1); i++) + for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++) archive_strappend_char(&mtree->buf, ' '); } else { - for (i = r -s; i < (INDENTNAMELEN + 1); i++) + for (i = (int)(r -s + nd); + i < (INDENTNAMELEN + 1); i++) archive_strappend_char(&mtree->buf, ' '); } s = ++r; x = NULL; continue; } - if (r - s <= MAXLINELEN - 3 - INDENTNAMELEN) + if (pd + (r - s) <= MAXLINELEN - 3 - INDENTNAMELEN) x = r++; else { if (x == NULL) x = r; archive_strncat(&mtree->buf, s, x - s); archive_strncat(&mtree->buf, " \\\n", 3); - for (i = 0; i < (INDENTNAMELEN + 1); i++) + for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++) archive_strappend_char(&mtree->buf, ' '); s = r = ++x; x = NULL; } } - if (x != NULL && strlen(s) > MAXLINELEN - 3 - INDENTNAMELEN) { + if (fn) { + for (i = 0; i < nd + pd; i++) + archive_strappend_char(&mtree->buf, ' '); + archive_strcat(&mtree->buf, s); + s += strlen(s); + } + if (x != NULL && pd + strlen(s) > MAXLINELEN - 3 - INDENTNAMELEN) { /* Last keyword is longer. */ archive_strncat(&mtree->buf, s, x - s); archive_strncat(&mtree->buf, " \\\n", 3); - for (i = 0; i < (INDENTNAMELEN + 1); i++) + for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++) archive_strappend_char(&mtree->buf, ' '); s = ++x; } @@ -352,163 +438,70 @@ mtree_indent(struct mtree_writer *mtree) archive_string_empty(&mtree->ebuf); } -#if !defined(_WIN32) || defined(__CYGWIN__) -static size_t -dir_len(struct mtree_entry *me) -{ - const char *path, *r; - - path = me->pathname; - r = strrchr(path, '/'); - if (r == NULL) - return (0); - /* Include a separator size */ - return (r - path + 1); -} - -#else /* _WIN32 && !__CYGWIN__ */ -/* - * Note: We should use wide-character for findng '\' character, - * a directory separator on Windows, because some character-set have - * been using the '\' character for a part of its multibyte character - * code. - */ -static size_t -dir_len(struct mtree_entry *me) -{ - wchar_t wc; - const char *path; - const char *p, *rp; - size_t al, l, size; - - path = me->pathname; - al = l = -1; - for (p = path; *p != '\0'; ++p) { - if (*p == '\\') - al = l = p - path; - else if (*p == '/') - al = p - path; - } - if (l == (size_t)-1) - goto alen; - size = p - path; - rp = p = path; - while (*p != '\0') { - l = mbtowc(&wc, p, size); - if (l == (size_t)-1) - goto alen; - if (l == 1 && (wc == L'/' || wc == L'\\')) - rp = p; - p += l; - size -= l; - } - return (rp - path + 1); -alen: - if (al == (size_t)-1) - return (0); - return (al + 1); -} -#endif /* _WIN32 && !__CYGWIN__ */ - -/* - * Test if a parent directory of the current entry is changed. - */ -static int -parent_dir_changed(struct archive_string *dir, struct mtree_entry *me) -{ - const char *path; - size_t l; - - l = dir_len(me); - path = me->pathname; - if (archive_strlen(dir) > 0) { - if (l == 0) { - archive_string_empty(dir); - return (1); - } - if (strncmp(dir->s, path, l) == 0) - return (0); /* The parent directory is the same. */ - } else if (l == 0) - return (0); /* The parent directory is the same. */ - archive_strncpy(dir, path, l); - return (1); -} - /* * Write /set keyword. * Set most used value of uid,gid,mode and fflags, which are - * collected by collect_set_values() function. + * collected by attr_counter_set_collect() function. */ static void write_global(struct mtree_writer *mtree) { struct archive_string setstr; struct archive_string unsetstr; - const char *name; + struct att_counter_set *acs; int keys, oldkeys, effkeys; - struct attr_counter *ac; archive_string_init(&setstr); archive_string_init(&unsetstr); keys = mtree->keys & SET_KEYS; oldkeys = mtree->set.keys; effkeys = keys; - if (mtree->set.processed) { + acs = &mtree->acs; + if (mtree->set.processing) { /* * Check if the global data needs updating. */ effkeys &= ~F_TYPE; - if (oldkeys & (F_UNAME | F_UID)) { - ac = mtree->set.uid_list; - do { - if (mtree->set.uid == ac->m_entry->uid) { - effkeys &= ~(F_UNAME | F_UID); - break; - } - if (ac->next != NULL && - ac->next->count == ac->count) - continue; - } while (0); + if (acs->uid_list == NULL) + effkeys &= ~(F_UNAME | F_UID); + else if (oldkeys & (F_UNAME | F_UID)) { + if (acs->uid_list->count < 2 || + mtree->set.uid == acs->uid_list->m_entry->uid) + effkeys &= ~(F_UNAME | F_UID); } - if (oldkeys & (F_GNAME | F_GID)) { - ac = mtree->set.gid_list; - do { - if (mtree->set.gid == ac->m_entry->gid) { - effkeys &= ~(F_GNAME | F_GID); - break; - } - if (ac->next != NULL && - ac->next->count == ac->count) - continue; - } while (0); + if (acs->gid_list == NULL) + effkeys &= ~(F_GNAME | F_GID); + else if (oldkeys & (F_GNAME | F_GID)) { + if (acs->gid_list->count < 2 || + mtree->set.gid == acs->gid_list->m_entry->gid) + effkeys &= ~(F_GNAME | F_GID); } - if (oldkeys & F_MODE) { - ac = mtree->set.mode_list; - do { - if (mtree->set.mode == ac->m_entry->mode) { - effkeys &= ~F_MODE; - break; - } - if (ac->next != NULL && - ac->next->count == ac->count) - continue; - } while (0); + if (acs->mode_list == NULL) + effkeys &= ~F_MODE; + else if (oldkeys & F_MODE) { + if (acs->mode_list->count < 2 || + mtree->set.mode == acs->mode_list->m_entry->mode) + effkeys &= ~F_MODE; } - if ((oldkeys & F_FLAGS) != 0) { - ac = mtree->set.flags_list; - do { - if (ac->m_entry->fflags_set == - mtree->set.fflags_set && - ac->m_entry->fflags_clear == - mtree->set.fflags_clear) { - effkeys &= ~F_FLAGS; - break; - } - if (ac->next != NULL && - ac->next->count == ac->count) - continue; - } while (0); + if (acs->flags_list == NULL) + effkeys &= ~F_FLAGS; + else if ((oldkeys & F_FLAGS) != 0) { + if (acs->flags_list->count < 2 || + (acs->flags_list->m_entry->fflags_set == + mtree->set.fflags_set && + acs->flags_list->m_entry->fflags_clear == + mtree->set.fflags_clear)) + effkeys &= ~F_FLAGS; } + } else { + if (acs->uid_list == NULL) + keys &= ~(F_UNAME | F_UID); + if (acs->gid_list == NULL) + keys &= ~(F_GNAME | F_GID); + if (acs->mode_list == NULL) + keys &= ~F_MODE; + if (acs->flags_list == NULL) + keys &= ~F_FLAGS; } if ((keys & effkeys & F_TYPE) != 0) { if (mtree->dironly) { @@ -520,10 +513,9 @@ write_global(struct mtree_writer *mtree) } } if ((keys & effkeys & F_UNAME) != 0) { - name = mtree->set.uid_list->m_entry->uname; - if (name != NULL) { + if (archive_strlen(&(acs->uid_list->m_entry->uname)) > 0) { archive_strcat(&setstr, " uname="); - mtree_quote(&setstr, name); + mtree_quote(&setstr, acs->uid_list->m_entry->uname.s); } else { keys &= ~F_UNAME; if ((oldkeys & F_UNAME) != 0) @@ -531,15 +523,14 @@ write_global(struct mtree_writer *mtree) } } if ((keys & effkeys & F_UID) != 0) { - mtree->set.uid = mtree->set.uid_list->m_entry->uid; + mtree->set.uid = acs->uid_list->m_entry->uid; archive_string_sprintf(&setstr, " uid=%jd", (intmax_t)mtree->set.uid); } if ((keys & effkeys & F_GNAME) != 0) { - name = mtree->set.gid_list->m_entry->gname; - if (name != NULL) { + if (archive_strlen(&(acs->gid_list->m_entry->gname)) > 0) { archive_strcat(&setstr, " gname="); - mtree_quote(&setstr, name); + mtree_quote(&setstr, acs->gid_list->m_entry->gname.s); } else { keys &= ~F_GNAME; if ((oldkeys & F_GNAME) != 0) @@ -547,24 +538,25 @@ write_global(struct mtree_writer *mtree) } } if ((keys & effkeys & F_GID) != 0) { - mtree->set.gid = mtree->set.gid_list->m_entry->gid; + mtree->set.gid = acs->gid_list->m_entry->gid; archive_string_sprintf(&setstr, " gid=%jd", (intmax_t)mtree->set.gid); } if ((keys & effkeys & F_MODE) != 0) { - mtree->set.mode = mtree->set.mode_list->m_entry->mode; + mtree->set.mode = acs->mode_list->m_entry->mode; archive_string_sprintf(&setstr, " mode=%o", (unsigned int)mtree->set.mode); } if ((keys & effkeys & F_FLAGS) != 0) { - name = mtree->set.flags_list->m_entry->fflags_text; - if (name != NULL) { + if (archive_strlen( + &(acs->flags_list->m_entry->fflags_text)) > 0) { archive_strcat(&setstr, " flags="); - mtree_quote(&setstr, name); + mtree_quote(&setstr, + acs->flags_list->m_entry->fflags_text.s); mtree->set.fflags_set = - mtree->set.flags_list->m_entry->fflags_set; + acs->flags_list->m_entry->fflags_set; mtree->set.fflags_clear = - mtree->set.flags_list->m_entry->fflags_clear; + acs->flags_list->m_entry->fflags_clear; } else { keys &= ~F_FLAGS; if ((oldkeys & F_FLAGS) != 0) @@ -578,16 +570,11 @@ write_global(struct mtree_writer *mtree) archive_string_sprintf(&mtree->buf, "/set%s\n", setstr.s); archive_string_free(&setstr); mtree->set.keys = keys; - mtree->set.processed = 1; - - free_attr_count(&mtree->set.uid_list); - free_attr_count(&mtree->set.gid_list); - free_attr_count(&mtree->set.mode_list); - free_attr_count(&mtree->set.flags_list); + mtree->set.processing = 1; } static struct attr_counter * -new_attr_count(struct mtree_entry *me, struct attr_counter *prev) +attr_counter_new(struct mtree_entry *me, struct attr_counter *prev) { struct attr_counter *ac; @@ -602,7 +589,7 @@ new_attr_count(struct mtree_entry *me, struct attr_counter *prev) } static void -free_attr_count(struct attr_counter **top) +attr_counter_free(struct attr_counter **top) { struct attr_counter *ac, *tac; @@ -618,7 +605,7 @@ free_attr_count(struct attr_counter **top) } static int -inc_attr_count(struct attr_counter **top, struct attr_counter *ac, +attr_counter_inc(struct attr_counter **top, struct attr_counter *ac, struct attr_counter *last, struct mtree_entry *me) { struct attr_counter *pac; @@ -647,7 +634,7 @@ inc_attr_count(struct attr_counter **top, struct attr_counter *ac, ac->next->prev = ac; } } else { - ac = new_attr_count(me, last); + ac = attr_counter_new(me, last); if (ac == NULL) return (-1); last->next = ac; @@ -655,93 +642,99 @@ inc_attr_count(struct attr_counter **top, struct attr_counter *ac, return (0); } +/* + * Tabulate uid,gid,mode and fflags of a entry in order to be used for /set. + */ static int -collect_set_values(struct mtree_writer *mtree, struct mtree_entry *me) +attr_counter_set_collect(struct mtree_writer *mtree, struct mtree_entry *me) { - int keys = mtree->keys; struct attr_counter *ac, *last; + struct att_counter_set *acs = &mtree->acs; + int keys = mtree->keys; if (keys & (F_UNAME | F_UID)) { - if (mtree->set.uid_list == NULL) { - mtree->set.uid_list = new_attr_count(me, NULL); - if (mtree->set.uid_list == NULL) + if (acs->uid_list == NULL) { + acs->uid_list = attr_counter_new(me, NULL); + if (acs->uid_list == NULL) return (-1); } else { last = NULL; - for (ac = mtree->set.uid_list; ac; ac = ac->next) { + for (ac = acs->uid_list; ac; ac = ac->next) { if (ac->m_entry->uid == me->uid) break; last = ac; } - if (inc_attr_count( - &mtree->set.uid_list, ac, last, me) < 0) + if (attr_counter_inc(&acs->uid_list, ac, last, me) < 0) return (-1); } } if (keys & (F_GNAME | F_GID)) { - if (mtree->set.gid_list == NULL) { - mtree->set.gid_list = new_attr_count(me, NULL); - if (mtree->set.gid_list == NULL) + if (acs->gid_list == NULL) { + acs->gid_list = attr_counter_new(me, NULL); + if (acs->gid_list == NULL) return (-1); } else { last = NULL; - for (ac = mtree->set.gid_list; ac; ac = ac->next) { + for (ac = acs->gid_list; ac; ac = ac->next) { if (ac->m_entry->gid == me->gid) break; last = ac; } - if (inc_attr_count( - &mtree->set.gid_list, ac, last, me) < 0) + if (attr_counter_inc(&acs->gid_list, ac, last, me) < 0) return (-1); } } if (keys & F_MODE) { - if (mtree->set.mode_list == NULL) { - mtree->set.mode_list = new_attr_count(me, NULL); - if (mtree->set.mode_list == NULL) + if (acs->mode_list == NULL) { + acs->mode_list = attr_counter_new(me, NULL); + if (acs->mode_list == NULL) return (-1); } else { last = NULL; - for (ac = mtree->set.mode_list; ac; ac = ac->next) { + for (ac = acs->mode_list; ac; ac = ac->next) { if (ac->m_entry->mode == me->mode) break; last = ac; } - if (inc_attr_count( - &mtree->set.mode_list, ac, last, me) < 0) + if (attr_counter_inc(&acs->mode_list, ac, last, me) < 0) return (-1); } } if (keys & F_FLAGS) { - if (mtree->set.flags_list == NULL) { - mtree->set.flags_list = new_attr_count(me, NULL); - if (mtree->set.flags_list == NULL) + if (acs->flags_list == NULL) { + acs->flags_list = attr_counter_new(me, NULL); + if (acs->flags_list == NULL) return (-1); } else { last = NULL; - for (ac = mtree->set.flags_list; ac; ac = ac->next) { + for (ac = acs->flags_list; ac; ac = ac->next) { if (ac->m_entry->fflags_set == me->fflags_set && - ac->m_entry->fflags_clear == me->fflags_clear) + ac->m_entry->fflags_clear == + me->fflags_clear) break; last = ac; } - if (inc_attr_count( - &mtree->set.flags_list, ac, last, me) < 0) + if (attr_counter_inc(&acs->flags_list, ac, last, me) < 0) return (-1); } } - /* - * Save a entry. - */ - me->next = NULL; - *mtree->set.me_last = me; - mtree->set.me_last = &me->next; return (0); } +static void +attr_counter_set_free(struct mtree_writer *mtree) +{ + struct att_counter_set *acs = &mtree->acs; + + attr_counter_free(&acs->uid_list); + attr_counter_free(&acs->gid_list); + attr_counter_free(&acs->mode_list); + attr_counter_free(&acs->flags_list); +} + static int -get_keys(struct mtree_writer *mtree, struct mtree_entry *me) +get_global_set_keys(struct mtree_writer *mtree, struct mtree_entry *me) { int keys; @@ -788,56 +781,92 @@ get_keys(struct mtree_writer *mtree, struct mtree_entry *me) return (keys); } -static struct mtree_entry * -new_mtree_entry(struct archive_entry *entry) +static int +mtree_entry_new(struct archive_write *a, struct archive_entry *entry, + struct mtree_entry **m_entry) { struct mtree_entry *me; const char *s; + int r; + static const struct archive_rb_tree_ops rb_ops = { + mtree_entry_cmp_node, mtree_entry_cmp_key + }; me = calloc(1, sizeof(*me)); - if (me == NULL) - return (NULL); - me->pathname = strdup(archive_entry_pathname(entry)); + if (me == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for a mtree entry"); + *m_entry = NULL; + return (ARCHIVE_FATAL); + } + + r = mtree_entry_setup_filenames(a, me, entry); + if (r < ARCHIVE_WARN) { + mtree_entry_free(me); + *m_entry = NULL; + return (r); + } + if ((s = archive_entry_symlink(entry)) != NULL) - me->symlink = strdup(s); - else - me->symlink = NULL; + archive_strcpy(&me->symlink, s); me->nlink = archive_entry_nlink(entry); me->filetype = archive_entry_filetype(entry); me->mode = archive_entry_mode(entry) & 07777; me->uid = archive_entry_uid(entry); me->gid = archive_entry_gid(entry); if ((s = archive_entry_uname(entry)) != NULL) - me->uname = strdup(s); - else - me->uname = NULL; + archive_strcpy(&me->uname, s); if ((s = archive_entry_gname(entry)) != NULL) - me->gname = strdup(s); - else - me->gname = NULL; + archive_strcpy(&me->gname, s); if ((s = archive_entry_fflags_text(entry)) != NULL) - me->fflags_text = strdup(s); - else - me->fflags_text = NULL; + archive_strcpy(&me->fflags_text, s); archive_entry_fflags(entry, &me->fflags_set, &me->fflags_clear); me->mtime = archive_entry_mtime(entry); me->mtime_nsec = archive_entry_mtime_nsec(entry); me->rdevmajor = archive_entry_rdevmajor(entry); me->rdevminor = archive_entry_rdevminor(entry); me->size = archive_entry_size(entry); - me->compute_sum = 0; + if (me->filetype == AE_IFDIR) { + me->dir_info = calloc(1, sizeof(*me->dir_info)); + if (me->dir_info == NULL) { + mtree_entry_free(me); + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for a mtree entry"); + *m_entry = NULL; + return (ARCHIVE_FATAL); + } + __archive_rb_tree_init(&me->dir_info->rbtree, &rb_ops); + me->dir_info->children.first = NULL; + me->dir_info->children.last = &(me->dir_info->children.first); + me->dir_info->chnext = NULL; + } else if (me->filetype == AE_IFREG) { + me->reg_info = calloc(1, sizeof(*me->reg_info)); + if (me->reg_info == NULL) { + mtree_entry_free(me); + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory for a mtree entry"); + *m_entry = NULL; + return (ARCHIVE_FATAL); + } + me->reg_info->compute_sum = 0; + } - return (me); + *m_entry = me; + return (ARCHIVE_OK); } static void -free_mtree_entry(struct mtree_entry *me) +mtree_entry_free(struct mtree_entry *me) { - free(me->pathname); - free(me->symlink); - free(me->uname); - free(me->gname); - free(me->fflags_text); + archive_string_free(&me->parentdir); + archive_string_free(&me->basename); + archive_string_free(&me->pathname); + archive_string_free(&me->symlink); + archive_string_free(&me->uname); + archive_string_free(&me->gname); + archive_string_free(&me->fflags_text); + free(me->dir_info); + free(me->reg_info); free(me); } @@ -846,66 +875,100 @@ archive_write_mtree_header(struct archive_write *a, struct archive_entry *entry) { struct mtree_writer *mtree= a->format_data; + struct mtree_entry *mtree_entry; + int r, r2; if (mtree->first) { mtree->first = 0; archive_strcat(&mtree->buf, "#mtree\n"); if ((mtree->keys & SET_KEYS) == 0) - mtree->set.output = 0;/* Disalbed. */ + mtree->output_global_set = 0;/* Disalbed. */ } mtree->entry_bytes_remaining = archive_entry_size(entry); + + /* While directory only mode, we do not handle non directory files. */ if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR) return (ARCHIVE_OK); - mtree->mtree_entry = new_mtree_entry(entry); - if (mtree->mtree_entry == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate mtree entry"); - return (ARCHIVE_FATAL); + r2 = mtree_entry_new(a, entry, &mtree_entry); + if (r2 < ARCHIVE_WARN) + return (r2); + r = mtree_entry_tree_add(a, &mtree_entry); + if (r < ARCHIVE_WARN) { + mtree_entry_free(mtree_entry); + return (r); } + mtree->mtree_entry = mtree_entry; - mtree->compute_sum = 0; - - /* If current file is not a regular file, we do not have to - * compute the sum of its content. */ - if (archive_entry_filetype(entry) != AE_IFREG) - return (ARCHIVE_OK); - - /* Initialize a bunch of sum check context. */ - sum_init(mtree); + /* If the current file is a regular file, we have to + * compute the sum of its content. + * Initialize a bunch of sum check context. */ + if (mtree_entry->reg_info) + sum_init(mtree); - return (ARCHIVE_OK); + return (r2); } static int -write_entry(struct archive_write *a, struct mtree_entry *me) +write_mtree_entry(struct archive_write *a, struct mtree_entry *me) { struct mtree_writer *mtree = a->format_data; struct archive_string *str; int keys, ret; + if (me->dir_info) { + if (mtree->classic) { + /* + * Output a comment line to describe the full + * pathname of the entry as mtree utility does + * while generating classic format. + */ + if (!mtree->dironly) + archive_strappend_char(&mtree->buf, '\n'); + if (me->parentdir.s) + archive_string_sprintf(&mtree->buf, + "# %s/%s\n", + me->parentdir.s, me->basename.s); + else + archive_string_sprintf(&mtree->buf, + "# %s\n", + me->basename.s); + } + if (mtree->output_global_set) + write_global(mtree); + } archive_string_empty(&mtree->ebuf); - str = (mtree->indent)? &mtree->ebuf : &mtree->buf; - mtree_quote(str, me->pathname); - keys = get_keys(mtree, me); + str = (mtree->indent || mtree->classic)? &mtree->ebuf : &mtree->buf; + + if (!mtree->classic && me->parentdir.s) { + /* + * If generating format is not classic one(v1), output + * a full pathname. + */ + mtree_quote(str, me->parentdir.s); + archive_strappend_char(str, '/'); + } + mtree_quote(str, me->basename.s); + + keys = get_global_set_keys(mtree, me); if ((keys & F_NLINK) != 0 && me->nlink != 1 && me->filetype != AE_IFDIR) archive_string_sprintf(str, " nlink=%u", me->nlink); - if ((keys & F_GNAME) != 0 && me->gname != NULL) { + if ((keys & F_GNAME) != 0 && archive_strlen(&me->gname) > 0) { archive_strcat(str, " gname="); - mtree_quote(str, me->gname); + mtree_quote(str, me->gname.s); } - if ((keys & F_UNAME) != 0 && me->uname != NULL) { + if ((keys & F_UNAME) != 0 && archive_strlen(&me->uname) > 0) { archive_strcat(str, " uname="); - mtree_quote(str, me->uname); + mtree_quote(str, me->uname.s); } if ((keys & F_FLAGS) != 0) { - if (me->fflags_text != NULL) { + if (archive_strlen(&me->fflags_text) > 0) { archive_strcat(str, " flags="); - mtree_quote(str, me->fflags_text); - } else if (mtree->set.processed && + mtree_quote(str, me->fflags_text.s); + } else if (mtree->set.processing && (mtree->set.keys & F_FLAGS) != 0) /* Overwrite the global parameter. */ archive_strcat(str, " flags=none"); @@ -926,7 +989,7 @@ write_entry(struct archive_write *a, struct mtree_entry *me) archive_strcat(str, " type=link"); if ((keys & F_SLINK) != 0) { archive_strcat(str, " link="); - mtree_quote(str, me->symlink); + mtree_quote(str, me->symlink.s); } break; case AE_IFSOCK: @@ -972,15 +1035,48 @@ write_entry(struct archive_write *a, struct mtree_entry *me) } /* Write a bunch of sum. */ - if (me->filetype == AE_IFREG) - sum_write(str, me); + if (me->reg_info) + sum_write(str, me->reg_info); - archive_strcat(str, "\n"); - if (mtree->indent) + archive_strappend_char(str, '\n'); + if (mtree->indent || mtree->classic) mtree_indent(mtree); if (mtree->buf.length > 32768) { - ret = __archive_write_output(a, mtree->buf.s, mtree->buf.length); + ret = __archive_write_output( + a, mtree->buf.s, mtree->buf.length); + archive_string_empty(&mtree->buf); + } else + ret = ARCHIVE_OK; + return (ret); +} + +static int +write_dot_dot_entry(struct archive_write *a, struct mtree_entry *n) +{ + struct mtree_writer *mtree = a->format_data; + int ret; + + if (n->parentdir.s) { + if (mtree->indent) { + int i, pd = mtree->depth * 4; + for (i = 0; i < pd; i++) + archive_strappend_char(&mtree->buf, ' '); + } + archive_string_sprintf(&mtree->buf, "# %s/%s\n", + n->parentdir.s, n->basename.s); + } + + if (mtree->indent) { + archive_string_empty(&mtree->ebuf); + archive_strncat(&mtree->ebuf, "..\n\n", (mtree->dironly)?3:4); + mtree_indent(mtree); + } else + archive_strncat(&mtree->buf, "..\n\n", (mtree->dironly)?3:4); + + if (mtree->buf.length > 32768) { + ret = __archive_write_output( + a, mtree->buf.s, mtree->buf.length); archive_string_empty(&mtree->buf); } else ret = ARCHIVE_OK; @@ -988,29 +1084,106 @@ write_entry(struct archive_write *a, struct mtree_entry *me) } /* - * Write mtree entries saved at collect_set_values() function. + * Write mtree entries saved at attr_counter_set_collect() function. */ static int -write_mtree_entries(struct archive_write *a) +write_mtree_entry_tree(struct archive_write *a) { struct mtree_writer *mtree = a->format_data; - struct mtree_entry *me, *tme; + struct mtree_entry *np = mtree->root; + struct archive_rb_node *n; int ret; - for (me = mtree->set.me_first; me; me = me->next) { - ret = write_entry(a, me); - if (ret != ARCHIVE_OK) - return (ARCHIVE_FATAL); - } + do { + if (mtree->output_global_set) { + /* + * Collect attribute infomation to know which value + * is frequently used among the children. + */ + attr_counter_set_reset(mtree); + ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) { + struct mtree_entry *e = (struct mtree_entry *)n; + if (attr_counter_set_collect(mtree, e) < 0) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + } + } + if (!np->dir_info->virtual || mtree->classic) { + ret = write_mtree_entry(a, np); + if (ret != ARCHIVE_OK) + return (ARCHIVE_FATAL); + } else { + /* Whenever output_global_set is enabled + * output global value(/set keywords) + * even if the directory entry is not allowd + * to be written because the global values + * can be used for the children. */ + if (mtree->output_global_set) + write_global(mtree); + } + /* + * Output the attribute of all files except directory files. + */ + mtree->depth++; + ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) { + struct mtree_entry *e = (struct mtree_entry *)n; + + if (e->dir_info) + mtree_entry_add_child_tail(np, e); + else { + ret = write_mtree_entry(a, e); + if (ret != ARCHIVE_OK) + return (ARCHIVE_FATAL); + } + } + mtree->depth--; + + if (np->dir_info->children.first != NULL) { + /* + * Descend the tree. + */ + np = np->dir_info->children.first; + if (mtree->indent) + mtree->depth++; + continue; + } else if (mtree->classic) { + /* + * While printing mtree classic, if there are not + * any directory files(except "." and "..") in the + * directory, output two dots ".." as returning + * the parent directory. + */ + ret = write_dot_dot_entry(a, np); + if (ret != ARCHIVE_OK) + return (ARCHIVE_FATAL); + } + + while (np != np->parent) { + if (np->dir_info->chnext == NULL) { + /* + * Ascend the tree; go back to the parent. + */ + if (mtree->indent) + mtree->depth--; + if (mtree->classic) { + ret = write_dot_dot_entry(a, + np->parent); + if (ret != ARCHIVE_OK) + return (ARCHIVE_FATAL); + } + np = np->parent; + } else { + /* + * Switch to next mtree entry in the directory. + */ + np = np->dir_info->chnext; + break; + } + } + } while (np != np->parent); - me = mtree->set.me_first; - while (me != NULL) { - tme = me->next; - free_mtree_entry(me); - me = tme; - } - mtree->set.me_first = NULL; - mtree->set.me_last = &mtree->set.me_first; return (ARCHIVE_OK); } @@ -1019,40 +1192,15 @@ archive_write_mtree_finish_entry(struct archive_write *a) { struct mtree_writer *mtree = a->format_data; struct mtree_entry *me; - int ret; if ((me = mtree->mtree_entry) == NULL) return (ARCHIVE_OK); mtree->mtree_entry = NULL; - if (me->filetype == AE_IFREG) - sum_final(mtree, me); + if (me->reg_info) + sum_final(mtree, me->reg_info); - if (mtree->set.output) { - if (!mtree->dironly) { - if (archive_strlen(&mtree->set.parent) == 0) - parent_dir_changed(&mtree->set.parent, me); - if (parent_dir_changed(&mtree->set.parent, me)) { - /* Write /set keyword */ - write_global(mtree); - /* Write entries saved by - * collect_set_values() function. */ - ret = write_mtree_entries(a); - if (ret != ARCHIVE_OK) - return (ARCHIVE_FATAL); - } - } - /* Tabulate uid,gid,mode and fflags of a entry - * in order to be used for /set. and, at this time - * we do not write a entry. */ - collect_set_values(mtree, me); - return (ARCHIVE_OK); - } else { - /* Write the current entry and free it. */ - ret = write_entry(a, me); - free_mtree_entry(me); - } - return (ret == ARCHIVE_OK ? ret : ARCHIVE_FATAL); + return (ARCHIVE_OK); } static int @@ -1061,9 +1209,8 @@ archive_write_mtree_close(struct archive_write *a) struct mtree_writer *mtree= a->format_data; int ret; - if (mtree->set.output && mtree->set.me_first != NULL) { - write_global(mtree); - ret = write_mtree_entries(a); + if (mtree->root != NULL) { + ret = write_mtree_entry_tree(a); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); } @@ -1096,25 +1243,16 @@ static int archive_write_mtree_free(struct archive_write *a) { struct mtree_writer *mtree= a->format_data; - struct mtree_entry *me, *tme; if (mtree == NULL) return (ARCHIVE_OK); /* Make sure we dot not leave any entries. */ - me = mtree->set.me_first; - while (me != NULL) { - tme = me->next; - free_mtree_entry(me); - me = tme; - } + mtree_entry_register_free(mtree); + archive_string_free(&mtree->cur_dirstr); archive_string_free(&mtree->ebuf); archive_string_free(&mtree->buf); - archive_string_free(&mtree->set.parent); - free_attr_count(&mtree->set.uid_list); - free_attr_count(&mtree->set.gid_list); - free_attr_count(&mtree->set.mode_list); - free_attr_count(&mtree->set.flags_list); + attr_counter_set_free(mtree); free(mtree); a->format_data = NULL; return (ARCHIVE_OK); @@ -1209,7 +1347,7 @@ archive_write_mtree_options(struct archive_write *a, const char *key, else if (strcmp(key, "uname") == 0) keybit = F_UNAME; else if (strcmp(key, "use-set") == 0) { - mtree->set.output = (value != NULL)? 1: 0; + mtree->output_global_set = (value != NULL)? 1: 0; return (ARCHIVE_OK); } break; @@ -1228,14 +1366,13 @@ archive_write_mtree_options(struct archive_write *a, const char *key, return (ARCHIVE_WARN); } -int -archive_write_set_format_mtree(struct archive *_a) +static int +archive_write_set_format_mtree_default(struct archive *_a, const char *fn) { struct archive_write *a = (struct archive_write *)_a; struct mtree_writer *mtree; - archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, - ARCHIVE_STATE_NEW, "archive_write_set_format_mtree"); + archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, fn); if (a->format_free != NULL) (a->format_free)(a); @@ -1249,14 +1386,12 @@ archive_write_set_format_mtree(struct archive *_a) mtree->mtree_entry = NULL; mtree->first = 1; memset(&(mtree->set), 0, sizeof(mtree->set)); - archive_string_init(&mtree->set.parent); mtree->keys = DEFAULT_KEYS; mtree->dironly = 0; mtree->indent = 0; archive_string_init(&mtree->ebuf); archive_string_init(&mtree->buf); - mtree->set.me_first = NULL; - mtree->set.me_last = &mtree->set.me_first; + mtree_entry_register_init(mtree); a->format_data = mtree; a->format_free = archive_write_mtree_free; a->format_name = "mtree"; @@ -1271,9 +1406,41 @@ archive_write_set_format_mtree(struct archive *_a) return (ARCHIVE_OK); } +int +archive_write_set_format_mtree(struct archive *_a) +{ + return archive_write_set_format_mtree_default(_a, + "archive_write_set_format_mtree"); +} + +int +archive_write_set_format_mtree_classic(struct archive *_a) +{ + int r; + + r = archive_write_set_format_mtree_default(_a, + "archive_write_set_format_mtree_classic"); + if (r == ARCHIVE_OK) { + struct archive_write *a = (struct archive_write *)_a; + struct mtree_writer *mtree; + + mtree = (struct mtree_writer *)a->format_data; + + /* Set to output a mtree archive in classic format. */ + mtree->classic = 1; + /* Basically, mtree classic format uses '/set' global + * value. */ + mtree->output_global_set = 1; + } + return (r); +} + static void sum_init(struct mtree_writer *mtree) { + + mtree->compute_sum = 0; + if (mtree->keys & F_CKSUM) { mtree->compute_sum |= F_CKSUM; mtree->crc = 0; @@ -1370,7 +1537,7 @@ sum_update(struct mtree_writer *mtree, const void *buff, size_t n) } static void -sum_final(struct mtree_writer *mtree, struct mtree_entry *me) +sum_final(struct mtree_writer *mtree, struct reg_info *reg) { if (mtree->compute_sum & F_CKSUM) { @@ -1378,34 +1545,34 @@ sum_final(struct mtree_writer *mtree, struct mtree_entry *me) /* Include the length of the file. */ for (len = mtree->crc_len; len != 0; len >>= 8) COMPUTE_CRC(mtree->crc, len & 0xff); - me->crc = ~mtree->crc; + reg->crc = ~mtree->crc; } #ifdef ARCHIVE_HAS_MD5 if (mtree->compute_sum & F_MD5) - archive_md5_final(&mtree->md5ctx, me->buf_md5); + archive_md5_final(&mtree->md5ctx, reg->buf_md5); #endif #ifdef ARCHIVE_HAS_RMD160 if (mtree->compute_sum & F_RMD160) - archive_rmd160_final(&mtree->rmd160ctx, me->buf_rmd160); + archive_rmd160_final(&mtree->rmd160ctx, reg->buf_rmd160); #endif #ifdef ARCHIVE_HAS_SHA1 if (mtree->compute_sum & F_SHA1) - archive_sha1_final(&mtree->sha1ctx, me->buf_sha1); + archive_sha1_final(&mtree->sha1ctx, reg->buf_sha1); #endif #ifdef ARCHIVE_HAS_SHA256 if (mtree->compute_sum & F_SHA256) - archive_sha256_final(&mtree->sha256ctx, me->buf_sha256); + archive_sha256_final(&mtree->sha256ctx, reg->buf_sha256); #endif #ifdef ARCHIVE_HAS_SHA384 if (mtree->compute_sum & F_SHA384) - archive_sha384_final(&mtree->sha384ctx, me->buf_sha384); + archive_sha384_final(&mtree->sha384ctx, reg->buf_sha384); #endif #ifdef ARCHIVE_HAS_SHA512 if (mtree->compute_sum & F_SHA512) - archive_sha512_final(&mtree->sha512ctx, me->buf_sha512); + archive_sha512_final(&mtree->sha512ctx, reg->buf_sha512); #endif /* Save what types of sum are computed. */ - me->compute_sum = mtree->compute_sum; + reg->compute_sum = mtree->compute_sum; } #if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) || \ @@ -1425,47 +1592,612 @@ strappend_bin(struct archive_string *s, const unsigned char *bin, int n) #endif static void -sum_write(struct archive_string *str, struct mtree_entry *me) +sum_write(struct archive_string *str, struct reg_info *reg) { - if (me->compute_sum & F_CKSUM) { + if (reg->compute_sum & F_CKSUM) { archive_string_sprintf(str, " cksum=%ju", - (uintmax_t)me->crc); + (uintmax_t)reg->crc); } #ifdef ARCHIVE_HAS_MD5 - if (me->compute_sum & F_MD5) { + if (reg->compute_sum & F_MD5) { archive_strcat(str, " md5digest="); - strappend_bin(str, me->buf_md5, sizeof(me->buf_md5)); + strappend_bin(str, reg->buf_md5, sizeof(reg->buf_md5)); } #endif #ifdef ARCHIVE_HAS_RMD160 - if (me->compute_sum & F_RMD160) { + if (reg->compute_sum & F_RMD160) { archive_strcat(str, " rmd160digest="); - strappend_bin(str, me->buf_rmd160, sizeof(me->buf_rmd160)); + strappend_bin(str, reg->buf_rmd160, sizeof(reg->buf_rmd160)); } #endif #ifdef ARCHIVE_HAS_SHA1 - if (me->compute_sum & F_SHA1) { + if (reg->compute_sum & F_SHA1) { archive_strcat(str, " sha1digest="); - strappend_bin(str, me->buf_sha1, sizeof(me->buf_sha1)); + strappend_bin(str, reg->buf_sha1, sizeof(reg->buf_sha1)); } #endif #ifdef ARCHIVE_HAS_SHA256 - if (me->compute_sum & F_SHA256) { + if (reg->compute_sum & F_SHA256) { archive_strcat(str, " sha256digest="); - strappend_bin(str, me->buf_sha256, sizeof(me->buf_sha256)); + strappend_bin(str, reg->buf_sha256, sizeof(reg->buf_sha256)); } #endif #ifdef ARCHIVE_HAS_SHA384 - if (me->compute_sum & F_SHA384) { + if (reg->compute_sum & F_SHA384) { archive_strcat(str, " sha384digest="); - strappend_bin(str, me->buf_sha384, sizeof(me->buf_sha384)); + strappend_bin(str, reg->buf_sha384, sizeof(reg->buf_sha384)); } #endif #ifdef ARCHIVE_HAS_SHA512 - if (me->compute_sum & F_SHA512) { + if (reg->compute_sum & F_SHA512) { archive_strcat(str, " sha512digest="); - strappend_bin(str, me->buf_sha512, sizeof(me->buf_sha512)); + strappend_bin(str, reg->buf_sha512, sizeof(reg->buf_sha512)); + } +#endif +} + +static int +mtree_entry_cmp_node(const struct archive_rb_node *n1, + const struct archive_rb_node *n2) +{ + const struct mtree_entry *e1 = (const struct mtree_entry *)n1; + const struct mtree_entry *e2 = (const struct mtree_entry *)n2; + + return (strcmp(e2->basename.s, e1->basename.s)); +} + +static int +mtree_entry_cmp_key(const struct archive_rb_node *n, const void *key) +{ + const struct mtree_entry *e = (const struct mtree_entry *)n; + + return (strcmp((const char *)key, e->basename.s)); +} + +#if defined(_WIN32) || defined(__CYGWIN__) +static int +cleanup_backslash_1(char *p) +{ + int mb, dos; + + mb = dos = 0; + while (*p) { + if (*(unsigned char *)p > 127) + mb = 1; + if (*p == '\\') { + /* If we have not met any multi-byte characters, + * we can replace '\' with '/'. */ + if (!mb) + *p = '/'; + dos = 1; + } + p++; + } + if (!mb || !dos) + return (0); + return (-1); +} + +static void +cleanup_backslash_2(wchar_t *p) +{ + + /* Convert a path-separator from '\' to '/' */ + while (*p != L'\0') { + if (*p == L'\\') + *p = L'/'; + p++; + } +} +#endif + +/* + * Generate a parent directory name and a base name from a pathname. + */ +static int +mtree_entry_setup_filenames(struct archive_write *a, struct mtree_entry *file, + struct archive_entry *entry) +{ + const char *pathname; + char *p, *dirname, *slash; + size_t len; + int ret = ARCHIVE_OK; + + archive_strcpy(&file->pathname, archive_entry_pathname(entry)); +#if defined(_WIN32) || defined(__CYGWIN__) + /* + * Convert a path-separator from '\' to '/' + */ + if (cleanup_backslash_1(file->pathname.s) != 0) { + const wchar_t *wp = archive_entry_pathname_w(entry); + struct archive_wstring ws; + + if (wp != NULL) { + int r; + archive_string_init(&ws); + archive_wstrcpy(&ws, wp); + cleanup_backslash_2(ws.s); + archive_string_empty(&(file->pathname)); + r = archive_string_append_from_wcs(&(file->pathname), + ws.s, ws.length); + archive_wstring_free(&ws); + if (r < 0 && errno == ENOMEM) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + } + } +#else + (void)a; /* UNUSED */ +#endif + pathname = file->pathname.s; + if (strcmp(pathname, ".") == 0) { + archive_strcpy(&file->basename, "."); + return (ARCHIVE_OK); + } + + archive_strcpy(&(file->parentdir), pathname); + + len = file->parentdir.length; + p = dirname = file->parentdir.s; + + /* + * Remove leading '/' and '../' elements + */ + while (*p) { + if (p[0] == '/') { + p++; + len--; + } else if (p[0] != '.') + break; + else if (p[1] == '.' && p[2] == '/') { + p += 3; + len -= 3; + } else + break; + } + if (p != dirname) { + memmove(dirname, p, len+1); + p = dirname; + } + /* + * Remove "/","/." and "/.." elements from tail. + */ + while (len > 0) { + size_t ll = len; + + if (len > 0 && p[len-1] == '/') { + p[len-1] = '\0'; + len--; + } + if (len > 1 && p[len-2] == '/' && p[len-1] == '.') { + p[len-2] = '\0'; + len -= 2; + } + if (len > 2 && p[len-3] == '/' && p[len-2] == '.' && + p[len-1] == '.') { + p[len-3] = '\0'; + len -= 3; + } + if (ll == len) + break; + } + while (*p) { + if (p[0] == '/') { + if (p[1] == '/') + /* Convert '//' --> '/' */ + strcpy(p, p+1); + else if (p[1] == '.' && p[2] == '/') + /* Convert '/./' --> '/' */ + strcpy(p, p+2); + else if (p[1] == '.' && p[2] == '.' && p[3] == '/') { + /* Convert 'dir/dir1/../dir2/' + * --> 'dir/dir2/' + */ + char *rp = p -1; + while (rp >= dirname) { + if (*rp == '/') + break; + --rp; + } + if (rp > dirname) { + strcpy(rp, p+3); + p = rp; + } else { + strcpy(dirname, p+4); + p = dirname; + } + } else + p++; + } else + p++; + } + p = dirname; + len = strlen(p); + + /* + * Add "./" prefiex. + * NOTE: If the pathname does not have a path separator, we have + * to add "./" to the head of the pathename because mtree reader + * will suppose that it is v1(a.k.a classic) mtree format and + * change the directory unexpectedly and so it will make a wrong + * path. + */ + if (strcmp(p, ".") != 0 && strncmp(p, "./", 2) != 0) { + struct archive_string as; + archive_string_init(&as); + archive_strcpy(&as, "./"); + archive_strncat(&as, p, len); + archive_string_empty(&file->parentdir); + archive_string_concat(&file->parentdir, &as); + archive_string_free(&as); + p = file->parentdir.s; + len = archive_strlen(&file->parentdir); + } + + /* + * Find out the position which points the last position of + * path separator('/'). + */ + slash = NULL; + for (; *p != '\0'; p++) { + if (*p == '/') + slash = p; + } + if (slash == NULL) { + /* The pathname doesn't have a parent directory. */ + file->parentdir.length = len; + archive_string_copy(&(file->basename), &(file->parentdir)); + archive_string_empty(&(file->parentdir)); + *file->parentdir.s = '\0'; + return (ret); + } + + /* Make a basename from dirname and slash */ + *slash = '\0'; + file->parentdir.length = slash - dirname; + archive_strcpy(&(file->basename), slash + 1); + return (ret); +} + +static int +mtree_entry_create_virtual_dir(struct archive_write *a, const char *pathname, + struct mtree_entry **m_entry) +{ + struct archive_entry *entry; + struct mtree_entry *file; + int r; + + entry = archive_entry_new(); + if (entry == NULL) { + *m_entry = NULL; + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory"); + return (ARCHIVE_FATAL); + } + archive_entry_copy_pathname(entry, pathname); + archive_entry_set_mode(entry, AE_IFDIR | 0755); + archive_entry_set_mtime(entry, time(NULL), 0); + + r = mtree_entry_new(a, entry, &file); + archive_entry_free(entry); + if (r < ARCHIVE_WARN) { + *m_entry = NULL; + archive_set_error(&a->archive, ENOMEM, + "Can't allocate memory"); + return (ARCHIVE_FATAL); } + + file->dir_info->virtual = 1; + + *m_entry = file; + return (ARCHIVE_OK); +} + +static void +mtree_entry_register_add(struct mtree_writer *mtree, struct mtree_entry *file) +{ + file->next = NULL; + *mtree->file_list.last = file; + mtree->file_list.last = &(file->next); +} + +static void +mtree_entry_register_init(struct mtree_writer *mtree) +{ + mtree->file_list.first = NULL; + mtree->file_list.last = &(mtree->file_list.first); +} + +static void +mtree_entry_register_free(struct mtree_writer *mtree) +{ + struct mtree_entry *file, *file_next; + + file = mtree->file_list.first; + while (file != NULL) { + file_next = file->next; + mtree_entry_free(file); + file = file_next; + } +} + +static int +mtree_entry_add_child_tail(struct mtree_entry *parent, + struct mtree_entry *child) +{ + child->dir_info->chnext = NULL; + *parent->dir_info->children.last = child; + parent->dir_info->children.last = &(child->dir_info->chnext); + return (1); +} + +/* + * Find a entry from a parent entry with the name. + */ +static struct mtree_entry * +mtree_entry_find_child(struct mtree_entry *parent, const char *child_name) +{ + struct mtree_entry *np; + + if (parent == NULL) + return (NULL); + np = (struct mtree_entry *)__archive_rb_tree_find_node( + &(parent->dir_info->rbtree), child_name); + return (np); +} + +static int +get_path_component(char *name, size_t n, const char *fn) +{ + char *p; + size_t l; + + p = strchr(fn, '/'); + if (p == NULL) { + if ((l = strlen(fn)) == 0) + return (0); + } else + l = p - fn; + if (l > n -1) + return (-1); + memcpy(name, fn, l); + name[l] = '\0'; + + return ((int)l); +} + +/* + * Add a new entry into the tree. + */ +static int +mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + char name[_MAX_FNAME];/* Included null terminator size. */ +#elif defined(NAME_MAX) && NAME_MAX >= 255 + char name[NAME_MAX+1]; +#else + char name[256]; #endif + struct mtree_writer *mtree = (struct mtree_writer *)a->format_data; + struct mtree_entry *dent, *file, *np; + const char *fn, *p; + int l, r; + + file = *filep; + if (file->parentdir.length == 0 && file->basename.length == 1 && + file->basename.s[0] == '.') { + file->parent = file; + if (mtree->root != NULL) { + np = mtree->root; + goto same_entry; + } + mtree->root = file; + mtree_entry_register_add(mtree, file); + return (ARCHIVE_OK); + } + + if (file->parentdir.length == 0) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal programing error " + "in generating canonical name for %s", + file->pathname.s); + return (ARCHIVE_FAILED); + } + + fn = p = file->parentdir.s; + + /* + * If the path of the parent directory of `file' ent |