--- libarchive-2.6.0/configure.ac.xz~ 2008-12-28 21:09:16.000000000 +0100 +++ libarchive-2.6.0/configure.ac 2009-01-21 22:11:51.000000000 +0100 @@ -186,14 +186,29 @@ if test "x$with_bz2lib" != "xno"; then AC_CHECK_LIB(bz2,BZ2_bzDecompressInit) fi +AC_ARG_WITH([liblzma], + AS_HELP_STRING([--without-liblzma], [Don't build support for lzma/xz through liblzma])) + +# This is a bit clumsy, room for improval.. +# The issue is that liblzmadec & liblzma cannot both be used at once, +# so in case of liblzma being found we disable libzlmadec. +WITH_LIBLZMA=false +if test "x$with_liblzma" != "xno"; then + AC_CHECK_HEADERS([lzma.h]) + AC_CHECK_LIB(lzma,lzma_auto_decoder, [AC_DEFINE(HAVE_LIBLZMA, 1, [Define to 1 if you have the liblzma library.]) + WITH_LIBLZMA=true + LIBS="$LIBS -llzma"]) +fi + AC_ARG_WITH([lzmadec], AS_HELP_STRING([--without-lzmadec], [Don't build support for lzma through lzmadec])) -if test "x$with_lzmadec" != "xno"; then +if test "x$with_lzmadec" != "xno" && test "x$WITH_LIBLZMA" != "xtrue"; then AC_CHECK_HEADERS([lzmadec.h]) AC_CHECK_LIB(lzmadec,lzmadec_decode) fi + # TODO: Give the user the option of using a pre-existing system # libarchive. This will define HAVE_LIBARCHIVE which will cause # bsdtar_platform.h to use #include <...> for the libarchive headers. --- libarchive-2.6.0/libarchive/archive.h.xz~ 2008-12-28 21:09:16.000000000 +0100 +++ libarchive-2.6.0/libarchive/archive.h 2009-01-21 22:11:51.000000000 +0100 @@ -226,6 +226,8 @@ typedef int archive_close_callback(struc #define ARCHIVE_COMPRESSION_COMPRESS 3 #define ARCHIVE_COMPRESSION_PROGRAM 4 #define ARCHIVE_COMPRESSION_LZMA 5 +#define ARCHIVE_COMPRESSION_XZ 6 + /* * Codes returned by archive_format. --- libarchive-2.6.0/libarchive/archive_read_support_compression_all.c.xz~ 2008-12-28 21:08:32.000000000 +0100 +++ libarchive-2.6.0/libarchive/archive_read_support_compression_all.c 2009-01-21 22:11:51.000000000 +0100 @@ -39,11 +39,14 @@ archive_read_support_compression_all(str #if HAVE_ZLIB_H archive_read_support_compression_gzip(a); #endif -#if HAVE_LZMADEC_H +#if HAVE_LZMADEC_H || HAVE_LZMA_H /* LZMA bidding is subject to false positives because * the LZMA file format has a very weak signature. It * may not be feasible to include LZMA detection here. */ /* archive_read_support_compression_lzma(a); */ #endif +#if HAVE_LZMA_H + archive_read_support_compression_xz(a); +#endif return (ARCHIVE_OK); } --- libarchive-2.6.0/libarchive/archive_read_support_compression_lzma.c.xz~ 2008-12-28 21:08:32.000000000 +0100 +++ libarchive-2.6.0/libarchive/archive_read_support_compression_lzma.c 2009-01-21 22:11:51.000000000 +0100 @@ -74,6 +74,7 @@ static struct archive_read_source *lzma_ const void *, size_t); static int lzma_reader_free(struct archive_reader *); +#ifndef HAVE_LZMA_H int archive_read_support_compression_lzma(struct archive *_a) { @@ -89,6 +90,7 @@ archive_read_support_compression_lzma(st reader->free = lzma_reader_free; return (ARCHIVE_OK); } +#endif static int lzma_reader_free(struct archive_reader *self){ --- libarchive-2.6.0/libarchive/archive_read_support_compression_xz.c.xz~ 2009-01-21 22:11:51.000000000 +0100 +++ libarchive-2.6.0/libarchive/archive_read_support_compression_xz.c 2009-01-21 22:11:51.000000000 +0100 @@ -0,0 +1,464 @@ +/*- + * Copyright (c) 2003-2008 Tim Kientzle and Miklos Vajna + * Copyright (c) 2009 Per Øyvind Karlsen + * 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 <stdio.h> +#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_LZMA_H +#include <lzma.h> +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_read_private.h" + +#if HAVE_LZMA_H +struct private_data { + lzma_stream stream; + unsigned char *out_block; + size_t out_block_size; + int64_t total_out; + char eof; /* True = found end of compressed data. */ +}; + +/* Lzma source */ +static ssize_t xz_source_read(struct archive_read_source *, const void **); +static int xz_source_close(struct archive_read_source *); +#endif + +/* + * Note that we can detect lzma archives even if we can't decompress + * them. (In fact, we like detecting them because we can give better + * error messages.) So the bid framework here gets compiled even + * if liblzma is unavailable. + */ +static int lzma_reader_bid(struct archive_reader *, const void *, size_t); +static int xz_reader_bid(struct archive_reader *, const void *, size_t); +static struct archive_read_source *lzma_reader_init(struct archive_read *, + struct archive_reader *, struct archive_read_source *, + const void *, size_t); +static struct archive_read_source *xz_reader_init(struct archive_read *, + struct archive_reader *, struct archive_read_source *, + const void *, size_t); +static struct archive_read_source *liblzma_reader_init(struct archive_read *, + struct archive_reader *, struct archive_read_source *, + const void *, size_t); +static int xz_reader_free(struct archive_reader *); + +#ifndef HAVE_LZMADEC_H +int +archive_read_support_compression_lzma(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_reader *reader = __archive_read_get_reader(a); + + if (reader == NULL) + return (ARCHIVE_FATAL); + + reader->data = NULL; + reader->bid = lzma_reader_bid; + reader->init = lzma_reader_init; + reader->free = xz_reader_free; + return (ARCHIVE_OK); +} +#endif + +int +archive_read_support_compression_xz(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_reader *reader = __archive_read_get_reader(a); + + if (reader == NULL) + return (ARCHIVE_FATAL); + + reader->data = NULL; + reader->bid = xz_reader_bid; + reader->init = xz_reader_init; + reader->free = xz_reader_free; + return (ARCHIVE_OK); +} + +static int +xz_reader_free(__attribute__((unused)) struct archive_reader *self){ + return (ARCHIVE_OK); +} + +/* + * Test whether we can handle this data. + * + * This logic returns zero if any part of the signature fails. It + * also tries to Do The Right Thing if a very short buffer prevents us + * from verifying as much as we would like. + * + * <sigh> LZMA has a rather poor file signature. Zeros do not + * make good signature bytes as a rule, and the only non-zero byte + * here is an ASCII character. For example, an uncompressed tar + * archive whose first file is ']' would satisfy this check. It may + * be necessary to exclude LZMA from compression_all() because of + * this. Clients of libarchive would then have to explicitly enable + * LZMA checking instead of (or in addition to) compression_all() when + * they have other evidence (file name, command-line option) to go on. + */ +static int +lzma_reader_bid(__attribute__((unused)) struct archive_reader *self, + const void *buff, size_t len) +{ + const unsigned char *buffer; + int bits_checked; + + buffer = (const unsigned char *)buff; + + + /* First byte of raw LZMA stream is always 0x5d. */ + if (len < 1) + return (0); + bits_checked = 0; + if (buffer[0] != 0x5d) + return (0); + bits_checked += 8; + + /* Second through fifth bytes are dictionary code, stored in + * little-endian order. The two least-significant bytes are + * always zero. */ + if (len < 2) + return (bits_checked); + if (buffer[1] != 0) + return (0); + bits_checked += 8; + + if (len < 3) + return (bits_checked); + if (buffer[2] != 0) + return (0); + bits_checked += 8; + + /* peroyvind: the LZMA_Alone format doesn't really have any signature at all, + * the header data is affected by lzma compression options, thus arbitrary. + * The first byte doesn't even have to be 0x5d, ie. with the new liblzma + * library when using extreme mode this won't be true.. + */ + /* NSIS format check uses this, but I've seen tar.lzma + * archives where this byte is 0xff, not 0. */ +#if 0 + if (len < 6) + return (bits_checked); + if (buffer[5] != 0) + return (0); + bits_checked += 8; +#endif + + /* TODO: The above test is still very weak. It would be + * good to do better. */ + + return (bits_checked); +} + +static int +xz_reader_bid(__attribute__((unused)) struct archive_reader *self, + const void *buff, size_t len) +{ + const unsigned char *buffer; + int bits_checked; + + if (len < 1) + return (0); + + buffer = (const unsigned char *)buff; + bits_checked = 0; + if (buffer[0] != 0xFD) /* Verify first ID byte. */ + return (0); + bits_checked += 8; + if (len < 2) + return (bits_checked); + + if (buffer[1] != '7') /* Verify second ID byte. */ + return (0); + bits_checked += 8; + if (len < 3) + return (bits_checked); + + if (buffer[2] != 'z') /* Verify third ID byte. */ + return (0); + bits_checked += 8; + if (len < 4) + return (bits_checked); + + if (buffer[3] != 'X') /* Verify fourth ID byte. */ + return (0); + bits_checked += 8; + if (len < 5) + return (bits_checked); + + if (buffer[4] != 'Z') /* Verify fifth ID byte. */ + return (0); + bits_checked += 8; + if (len < 6) + return (bits_checked); + + if (buffer[5] != 0x00) /* Verify sixth ID byte. */ + return (0); + bits_checked += 8; + if (len < 7) + return (bits_checked); + + /* + * TODO: Verify more; xz has more data in both the header and footer that + * would be nice to verify (ie. CRC). + * See section 2.1 of the xz format description. + */ + + return (bits_checked); +} + +#ifndef HAVE_LZMA_H + +/* + * If we don't have the library on this system, we can't actually do the + * decompression. We can, however, still detect compressed archives + * and emit a useful message. + */ +static struct archive_read_source * +lzma_reader_init(struct archive_read *a, + __attribute__((unused)) struct archive_reader *reader, + __attribute__((unused)) struct archive_read_source *upstream, + __attribute__((unused)) const void *buff, + __attribute__((unused)) size_t n) +{ + archive_set_error(&a->archive, -1, + "This version of libarchive was compiled without lzma support"); + return (NULL); +} + +static struct archive_read_source * +xz_reader_init(struct archive_read *a, + __attribute__((unused)) struct archive_reader *reader, + __attribute__((unused)) struct archive_read_source *upstream, + __attribute__((unused)) const void *buff, + __attribute__((unused)) size_t n) +{ + archive_set_error(&a->archive, -1, + "This version of libarchive was compiled without xz support"); + return (NULL); +} + +#else + +/* + * Setup the callbacks. + */ +static struct archive_read_source * +lzma_reader_init(struct archive_read *a, struct archive_reader *reader, + struct archive_read_source *upstream, const void *buff, size_t n) +{ + a->archive.compression_code = ARCHIVE_COMPRESSION_LZMA; + a->archive.compression_name = "lzma"; + return liblzma_reader_init(a, reader, upstream, buff, n); +} + +static struct archive_read_source * +xz_reader_init(struct archive_read *a, struct archive_reader *reader, + struct archive_read_source *upstream, const void *buff, size_t n) +{ + a->archive.compression_code = ARCHIVE_COMPRESSION_XZ; + a->archive.compression_name = "xz"; + return liblzma_reader_init(a, reader, upstream, buff, n); +} + +static struct archive_read_source * +liblzma_reader_init(struct archive_read *a, __attribute__((unused)) struct archive_reader *reader, + struct archive_read_source *upstream, const void *buff, size_t n) +{ + static const size_t out_block_size = 64 * 1024; + void *out_block; + struct archive_read_source *self; + struct private_data *state; + lzma_ret ret; + + self = calloc(sizeof(*self), 1); + state = (struct private_data *)calloc(sizeof(*state), 1); + out_block = (unsigned char *)malloc(out_block_size); + if (self == NULL || state == NULL || out_block == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for %s decompression", + a->archive.compression_name); + free(out_block); + free(state); + free(self); + return (NULL); + } + + + self->archive = a; + self->data = state; + state->out_block_size = out_block_size; + state->out_block = out_block; + self->upstream = upstream; + self->read = xz_source_read; + self->skip = NULL; /* not supported */ + self->close = xz_source_close; + + state->stream.next_in = buff; + state->stream.avail_in = n; + + state->stream.next_out = state->out_block; + state->stream.avail_out = state->out_block_size; + + /* Initialize compression library. */ + ret = lzma_auto_decoder(&(state->stream), -1, 0); + + if (ret == LZMA_OK) + return (self); + + /* Library setup failed: Clean up. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing %s library", + a->archive.compression_name); + + /* Override the error message if we know what really went wrong. */ + switch (ret) { + case LZMA_DATA_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "invalid data"); + break; + case LZMA_MEM_ERROR: + archive_set_error(&a->archive, ENOMEM, + "Internal error initializing compression library: " + "out of memory"); + break; + } + + free(state->out_block); + free(state); + free(self); + return (NULL); +} + +/* + * Return the next block of decompressed data. + */ +static ssize_t +xz_source_read(struct archive_read_source *self, const void **p) +{ + struct private_data *state; + size_t read_avail, decompressed; + const void *read_buf; + lzma_ret ret; + + state = (struct private_data *)self->data; + read_avail = 0; + + /* Empty our output buffer. */ + state->stream.next_out = state->out_block; + state->stream.avail_out = state->out_block_size; + + /* Try to fill the output buffer. */ + for (;;) { + /* If the last upstream block is done, get another one. */ + if (state->stream.avail_in == 0) { + ret = (self->upstream->read)(self->upstream, + &read_buf); + /* stream.next_in is really const, but lzmadec + * doesn't declare it so. <sigh> */ + state->stream.next_in + = (unsigned char *)(uintptr_t)read_buf; + if (ret < 0) + return (ARCHIVE_FATAL); + /* There is no more data, return whatever we have. */ + if (ret == 0) { + *p = state->out_block; + decompressed = state->stream.next_out + - state->out_block; + state->total_out += decompressed; + return (decompressed); + } + state->stream.avail_in = ret; + } + + /* Decompress as much as we can in one pass. */ + ret = lzma_code(&(state->stream), LZMA_RUN); + switch (ret) { + case LZMA_STREAM_END: /* Found end of stream. */ + /* TODO: Peek ahead to see if there's another + * stream so we can mimic the behavior of gunzip + * on concatenated streams. */ + state->eof = 1; + case LZMA_OK: /* Decompressor made some progress. */ + /* If we filled our buffer, update stats and return. */ + if (state->eof || state->stream.avail_out == 0) { + *p = state->out_block; + decompressed = state->stream.next_out + - state->out_block; + state->total_out += decompressed; + return (decompressed); + } + break; + default: + /* Return an error. */ + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "%s decompression failed", + self->archive->archive.compression_name); + return (ARCHIVE_FATAL); + } + } +} + +/* + * Clean up the decompressor. + */ +static int +xz_source_close(struct archive_read_source *self) +{ + struct private_data *state; + int ret; + + state = (struct private_data *)self->data; + ret = ARCHIVE_OK; + lzma_end(&(state->stream)); + + free(state->out_block); + free(state); + free(self); + return (ret); +} + +#endif /* HAVE_LZMA_H */ --- libarchive-2.6.0/libarchive/archive_write_set_compression_xz.c.xz~ 2009-01-21 22:11:51.000000000 +0100 +++ libarchive-2.6.0/libarchive/archive_write_set_compression_xz.c 2009-01-21 22:11:51.000000000 +0100 @@ -0,0 +1,336 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2009 Per Øyvind Karlsen + * 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" + +/* Don't compile this if we don't have bzlib. */ +#if HAVE_LZMA_H + +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_LZMA_H +#include <lzma.h> +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_write_private.h" + +struct private_data { + lzma_stream stream; + int64_t total_in; + char *compressed; + size_t compressed_buffer_size; +}; + + +static int archive_compressor_xz_finish(struct archive_write *); +static int archive_compressor_xz_init(struct archive_write *); +static int archive_compressor_xz_write(struct archive_write *, + const void *, size_t); +static int drive_compressor(struct archive_write *, struct private_data *, + int finishing); + +/* + * Allocate, initialize and return an archive object. + */ +int +archive_write_set_compression_xz(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_set_compression_xz"); + a->compressor.init = &archive_compressor_xz_init; + return (ARCHIVE_OK); +} + +/* + * Setup callback. + */ +static int +archive_compressor_xz_init(struct archive_write *a) +{ + int ret; + struct private_data *state; + + a->archive.compression_code = ARCHIVE_COMPRESSION_XZ; + a->archive.compression_name = "xz"; + + if (a->client_opener != NULL) { + ret = (a->client_opener)(&a->archive, a->client_data); + if (ret != 0) + return (ret); + } + + state = (struct private_data *)malloc(sizeof(*state)); + if (state == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for compression"); + return (ARCHIVE_FATAL); + } + memset(state, 0, sizeof(*state)); + + state->compressed_buffer_size = a->bytes_per_block; + state->compressed = (char *)malloc(state->compressed_buffer_size); + + if (state->compressed == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for compression buffer"); + free(state); + return (ARCHIVE_FATAL); + } + + state->stream.next_out = state->compressed; + state->stream.avail_out = state->compressed_buffer_size; + a->compressor.write = archive_compressor_xz_write; + a->compressor.finish = archive_compressor_xz_finish; + + /* Initialize compression library */ + ret = lzma_easy_encoder(&(state->stream), LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC32); + if (ret == LZMA_OK) { + a->compressor.data = state; + return (ARCHIVE_OK); + } + + /* Library setup failed: clean up. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library"); + free(state->compressed); + free(state); + + /* Override the error message if we know what really went wrong. */ + switch (ret) { + case LZMA_PROG_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "invalid setup parameter"); + break; + case LZMA_MEM_ERROR: + archive_set_error(&a->archive, ENOMEM, + "Internal error initializing compression library: " + "out of memory"); + break; + } + + return (ARCHIVE_FATAL); + +} + +/* + * Write data to the compressed stream. + * + * Returns ARCHIVE_OK if all data written, error otherwise. + */ +static int +archive_compressor_xz_write(struct archive_write *a, const void *buff, + size_t length) +{ + struct private_data *state; + + state = (struct private_data *)a->compressor.data; + if (a->client_writer == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "No write callback is registered? " + "This is probably an internal programming error."); + return (ARCHIVE_FATAL); + } + + /* Update statistics */ + state->total_in += length; + + /* Compress input data to output buffer */ + state->stream.next_in = buff; + state->stream.avail_in = length; + if (drive_compressor(a, state, 0)) + return (ARCHIVE_FATAL); + a->archive.file_position += length; + return (ARCHIVE_OK); +} + + +/* + * Finish the compression. + */ +static int +archive_compressor_xz_finish(struct archive_write *a) +{ + ssize_t block_length; + int ret; + struct private_data *state; + ssize_t target_block_length; + ssize_t bytes_written; + unsigned tocopy; + + state = (struct private_data *)a->compressor.data; + ret = ARCHIVE_OK; + if (a->client_writer == NULL) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "No write callback is registered?\n" + "This is probably an internal programming error."); + ret = ARCHIVE_FATAL; + goto cleanup; + } + + /* By default, always pad the uncompressed data. */ + if (a->pad_uncompressed) { + tocopy = a->bytes_per_block - + (state->total_in % a->bytes_per_block); + while (tocopy > 0 && tocopy < (unsigned)a->bytes_per_block) { + state->stream.next_in, a->nulls; + state->stream.avail_in = tocopy < a->null_length ? + tocopy : a->null_length; + state->total_in += state->stream.avail_in; + tocopy -= state->stream.avail_in; + ret = drive_compressor(a, state, 0); + if (ret != ARCHIVE_OK) + goto cleanup; + } + } + + /* Finish compression cycle. */ + if ((ret = drive_compressor(a, state, 1))) + goto cleanup; + + /* Optionally, pad the final compressed block. */ + block_length = (char *)state->stream.next_out - state->compressed; + + + /* Tricky calculation to determine size of last block. */ + target_block_length = block_length; + if (a->bytes_in_last_block <= 0) + /* Default or Zero: pad to full block */ + target_block_length = a->bytes_per_block; + else + /* Round length to next multiple of bytes_in_last_block. */ + target_block_length = a->bytes_in_last_block * + ( (block_length + a->bytes_in_last_block - 1) / + a->bytes_in_last_block); + if (target_block_length > a->bytes_per_block) + target_block_length = a->bytes_per_block; + if (block_length < target_block_length) { + memset(state->stream.next_out, 0, + target_block_length - block_length); + block_length = target_block_length; + } + + /* Write the last block */ + bytes_written = (a->client_writer)(&a->archive, a->client_data, + state->compressed, block_length); + + /* TODO: Handle short write of final block. */ + if (bytes_written <= 0) + ret = ARCHIVE_FATAL; + else { + a->archive.raw_position += ret; + ret = ARCHIVE_OK; + } + + /* Cleanup: shut down compressor, release memory, etc. */ +cleanup: + lzma_end(&(state->stream)); + + free(state->compressed); + free(state); + return (ret); +} + +/* + * Utility function to push input data through compressor, writing + * full output blocks as necessary. + * + * Note that this handles both the regular write case (finishing == + * false) and the end-of-archive case (finishing == true). + */ +static int +drive_compressor(struct archive_write *a, struct private_data *state, int finishing) +{ + ssize_t bytes_written; + int ret; + + for (;;) { + if (state->stream.avail_out == 0) { + bytes_written = (a->client_writer)(&a->archive, + a->client_data, state->compressed, + state->compressed_buffer_size); + if (bytes_written <= 0) { + /* TODO: Handle this write failure */ + return (ARCHIVE_FATAL); + } else if ((size_t)bytes_written < state->compressed_buffer_size) { + /* Short write: Move remainder to + * front and keep filling */ + memmove(state->compressed, + state->compressed + bytes_written, + state->compressed_buffer_size - bytes_written); + } + + a->archive.raw_position += bytes_written; + state->stream.next_out = state->compressed + + state->compressed_buffer_size - bytes_written; + state->stream.avail_out = bytes_written; + } + + /* If there's nothing to do, we're done. */ + if (!finishing && state->stream.avail_in == 0) + return (ARCHIVE_OK); + + ret = lzma_code(&(state->stream), + finishing ? LZMA_FINISH : LZMA_RUN); + + switch (ret) { + case LZMA_RUN: + /* In non-finishing case, did compressor + * consume everything? */ + if (!finishing && state->stream.avail_in == 0) + return (ARCHIVE_OK); + break; + case LZMA_FINISH: /* Finishing: There's more work to do */ + break; + case LZMA_STREAM_END: /* Finishing: all done */ + /* Only occurs in finishing case */ + return (ARCHIVE_OK); + default: + /* Any other return value indicates an error */ + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "XZ compression failed;" + " lzma_code() returned %d", + ret); + return (ARCHIVE_FATAL); + } + } +} + +#endif /* HAVE_LZMA_H */ --- libarchive-2.6.0/libarchive/test/test_compat_xz_1.tar.xz.uu.xz~ 2009-01-21 22:27:43.000000000 +0100 +++ libarchive-2.6.0/libarchive/test/test_compat_xz_1.tar.xz.uu 2009-01-21 22:25:41.000000000 +0100 @@ -0,0 +1,9 @@ +begin 644 test_compat_xz_1.tar.xz +M_3=Z6%H```3FUK1&`@`A`18```!T+^6CX!O_`*%=`#,,/!NGC#0&C6L"2_R2 +M/O9*^(7KX=WM^(=KA(RH"\09$$)!Q_+JUHQ*`]R;ITL_F3/I6:^Q0550A&)B +MHS@=K]7@K1-9FOIP#PU!I<PUHW+W#<F(6FSL/<?5:4*>?E5&IHH&Q=N>_C&G +M-$G]+L[\,B<7%8&$NO5K31*Y>"D^+<YY3.*21VQ'3VXK3<KW>P,ES086$*.. +MC?A%?!$<8ZD8]A--`````/,<.:MU#&'6``&]`8`X``#5_T"?L<1G^P(````` +#!%E: +` +end --- libarchive-2.6.0/libarchive/test/test_compat_xz_2.tar.xz.uu.xz~ 2009-01-21 22:27:41.000000000 +0100 +++ libarchive-2.6.0/libarchive/test/test_compat_xz_2.tar.xz.uu 2009-01-21 22:27:21.000000000 +0100 @@ -0,0 +1,9 @@ +begin 644 test_compat_xz_2.tar.xz +M_3=Z6%H```3FUK1&`@`A`18```!T+^6CX!O_`*%=`#,,/!NGC#0&C6L"2_R2 +M/O9*^(7KX=WM^(=KA(RH"\09$$)!Q_+JUHQ*`]R;ITL_F3/I6:^Q0550A&)B +MHS@=K]7@K1-9FOIP#PU!I<PUHW+W#<F(6FSL/<?5:4*>?E5&IHH&Q=N>_C&G +M-$G]+L[\,B<7%8&$NO5K31*Y>"D^+<YY3.*21VQ'3VXK3<KW>P,ES086$*.. +MC?A%?!$<8ZD8]A--`````/,<.:MU#&'6``&]`8`X``#5_T"?L<1G^P(````` +#!%E: +` +end --- libarchive-2.6.0/libarchive/test/test_compat_xz.c.xz~ 2009-01-21 22:20:18.000000000 +0100 +++ libarchive-2.6.0/libarchive/test/test_compat_xz.c 2009-01-21 22:21:39.000000000 +0100 @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 2003-2008 Tim Kientzle + * Copyright (c) 2009 Per Øyvind Karlsen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +/* + * Verify our ability to read sample files compatibly with unxz. + * + * In particular: + * * unxz will read multiple xz streams, concatenating the output + * * unxz will stop at the end of a stream if the following data + * doesn't start with a xz signature. + */ + +/* + * All of the sample files have the same contents; they're just + * compressed in different ways. + */ +static void +compat_xz(const char *name) +{ + const char *n[7] = { "f1", "f2", "f3", "d1/f1", "d1/f2", "d1/f3", NULL }; + struct archive_entry *ae; + struct archive *a; + int i,r; + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_compression_all(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); + extract_reference_file(name); + assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 200)); + + /* Read entries, match up names with list above. */ + for (i = 0; i < 6; ++i) { + r = archive_read_next_header(a, &ae); + failure("Could not read file %d (%s) from %s", i, n[i], name); + assertEqualIntA(a, ARCHIVE_OK, r); + if (r != ARCHIVE_OK) { + archive_read_finish(a); + return; + } + assertEqualString(n[i], archive_entry_pathname(ae)); + } + + /* Verify the end-of-archive. */ + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + + /* Verify that the format detection worked. */ + assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_XZ); + assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR); + + assertEqualInt(ARCHIVE_OK, archive_read_close(a)); +#if ARCHIVE_VERSION_NUMBER < 2000000 + archive_read_finish(a); +#else + assertEqualInt(ARCHIVE_OK, archive_read_finish(a)); +#endif +} + + +DEFINE_TEST(test_compat_xz) +{ + compat_xz("test_compat_xz_1.tar.xz"); + compat_xz("test_compat_xz_2.tar.xz"); +} + + --- libarchive-2.6.0/libarchive/test/test_read_format_gtar_xz.c.xz~ 2009-01-21 22:11:51.000000000 +0100 +++ libarchive-2.6.0/libarchive/test/test_read_format_gtar_xz.c 2009-01-21 22:11:51.000000000 +0100 @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2008 Miklos Vajna + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +static unsigned char archive[] = { +0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x04, 0xe6, 0xd6, 0xb4, 0x46, 0x02, +0x00, 0x21, 0x01, 0x16, 0x00, 0x00, 0x00, 0x74, 0x2f, 0xe5, 0xa3, 0xe0, 0x27, +0xff, 0x00, 0x62, 0x5d, 0x00, 0x17, 0x0b, 0xbc, 0x1c, 0x7d, 0x01, 0x95, 0xc0, +0x1d, 0x4a, 0x46, 0x9c, 0x1c, 0xc5, 0x08, 0x83, 0x9b, 0x8a, 0x01, 0xc4, 0x5a, +0x08, 0xe8, 0xf1, 0xac, 0xc0, 0x24, 0x8a, 0xfd, 0x60, 0xc9, 0xcd, 0xdc, 0xaa, +0x1b, 0xab, 0x28, 0x63, 0xfe, 0xda, 0x71, 0x9a, 0x84, 0x94, 0xa2, 0x85, 0x96, +0x59, 0x26, 0x48, 0x4d, 0x6f, 0x70, 0x24, 0x8d, 0x4a, 0x75, 0x07, 0x67, 0xcc, +0xad, 0x7a, 0x6d, 0xd3, 0xb2, 0xb2, 0x8e, 0x61, 0xaa, 0xc2, 0xc3, 0xbe, 0xc8, +0x9e, 0x75, 0x9d, 0xbb, 0x8a, 0x1f, 0x5c, 0xdd, 0xcf, 0xe2, 0x21, 0xb2, 0x13, +0xe5, 0x1e, 0xd4, 0x49, 0xbc, 0x55, 0x90, 0x88, 0x3a, 0xc4, 0x05, 0x00, 0x00, +0x00, 0x00, 0x3a, 0x18, 0xfa, 0xd5, 0x35, 0x88, 0x18, 0x90, 0x00, 0x01, 0x7e, +0x80, 0x50, 0x00, 0x00, 0x00, 0x5c, 0x07, 0x96, 0x88, 0xb1, 0xc4, 0x67, 0xfb, +0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x59, 0x5a}; + +DEFINE_TEST(test_read_format_gtar_xz) +{ + int r; + + struct archive_entry *ae; + struct archive *a; + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_compression_all(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_compression_xz(a)); + assertEqualIntA(a, ARCHIVE_OK, + archive_read_support_format_all(a)); + r = archive_read_open_memory(a, archive, sizeof(archive)); + if (r != ARCHIVE_OK) { + skipping("Skipping LZMA compression check: %s", + archive_error_string(a)); + goto finish; + } + assertEqualIntA(a, ARCHIVE_OK, + archive_read_next_header(a, &ae)); + assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_XZ); + assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_GNUTAR); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); +finish: +#if ARCHIVE_VERSION_NUMBER < 2000000 + archive_read_finish(a); +#else + assertEqualInt(ARCHIVE_OK, archive_read_finish(a)); +#endif +} + + --- libarchive-2.6.0/Makefile.am.xz~ 2008-12-28 21:08:32.000000000 +0100 +++ libarchive-2.6.0/Makefile.am 2009-01-21 22:28:17.000000000 +0100 @@ -99,6 +99,7 @@ libarchive_la_SOURCES= \ libarchive/archive_read_support_compression_none.c \ libarchive/archive_read_support_compression_program.c \ libarchive/archive_read_support_compression_lzma.c \ + libarchive/archive_read_support_compression_xz.c \ libarchive/archive_read_support_format_all.c \ libarchive/archive_read_support_format_ar.c \ libarchive/archive_read_support_format_cpio.c \ @@ -126,6 +127,7 @@ libarchive_la_SOURCES= \ libarchive/archive_write_set_compression_gzip.c \ libarchive/archive_write_set_compression_none.c \ libarchive/archive_write_set_compression_program.c \ + libarchive/archive_write_set_compression_xz.c \ libarchive/archive_write_set_format.c \ libarchive/archive_write_set_format_ar.c \ libarchive/archive_write_set_format_by_name.c \ @@ -185,6 +187,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_compat_gtar.c \ libarchive/test/test_compat_gzip.c \ libarchive/test/test_compat_tar_hardlink.c \ + libarchive/test/test_compat_xz.c \ libarchive/test/test_compat_zip.c \ libarchive/test/test_empty_write.c \ libarchive/test/test_entry.c \ @@ -207,6 +210,7 @@ libarchive_test_SOURCES= \ libarchive/test/test_read_format_gtar_gz.c \ libarchive/test/test_read_format_gtar_lzma.c \ libarchive/test/test_read_format_gtar_sparse.c \ + libarchive/test/test_read_format_gtar_xz.c \ libarchive/test/test_read_format_iso_gz.c \ libarchive/test/test_read_format_isorr_bz2.c \ libarchive/test/test_read_format_mtree.c \ @@ -260,6 +264,8 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_compat_gzip_1.tgz.uu \ libarchive/test/test_compat_gzip_2.tgz.uu \ libarchive/test/test_compat_tar_hardlink_1.tar.uu \ + libarchive/test/test_compat_xz_1.tar.xz.uu \ + libarchive/test/test_compat_xz_2.tar.xz.uu \ libarchive/test/test_compat_zip_1.zip.uu \ libarchive/test/test_fuzz_1.iso.uu \ libarchive/test/test_pax_filename_encoding.tar.gz.uu \ --- libarchive-2.6.0/tar/bsdtar.1.xz~ 2008-12-28 21:08:12.000000000 +0100 +++ libarchive-2.6.0/tar/bsdtar.1 2009-01-21 22:11:51.000000000 +0100 @@ -434,6 +434,15 @@ Note that, unlike other .Nm tar implementations, this implementation recognizes bzip2 compression automatically when reading archives. +.It Fl Y +(c mode only) +Compress the resulting archive with +.Xr xz 1 . +In extract or list modes, this option is ignored. +Note that, unlike other +.Nm tar +implementations, this implementation recognizes xz compression +automatically when reading archives. .It Fl z (c mode only) Compress the resulting archive with @@ -694,7 +703,8 @@ components, or symlinks to other directo .Xr shar 1 , .Xr libarchive 3 , .Xr libarchive-formats 5 , -.Xr tar 5 +.Xr tar 5 , +.Xr xz 1 .Sh STANDARDS There is no current POSIX standard for the tar command; it appeared in --- libarchive-2.6.0/tar/bsdtar.c.xz~ 2008-12-28 21:08:12.000000000 +0100 +++ libarchive-2.6.0/tar/bsdtar.c 2009-01-21 22:11:51.000000000 +0100 @@ -417,6 +417,19 @@ main(int argc, char **argv) case 'x': /* SUSv2 */ set_mode(bsdtar, opt); break; + case 'Y': +#if HAVE_LIBLZMA + if (bsdtar->create_compression != '\0') + bsdtar_errc(bsdtar, 1, 0, + "Can't specify both -%c and -%c", opt, + bsdtar->create_compression); + bsdtar->create_compression = opt; +#else + bsdtar_warnc(bsdtar, 0, + "xz compression not supported by this version of bsdtar"); + usage(bsdtar); +#endif + break; case 'y': /* FreeBSD version of GNU tar */ #if HAVE_LIBBZ2 if (bsdtar->create_compression != '\0') --- libarchive-2.6.0/tar/cmdline.c.xz~ 2008-12-28 21:08:12.000000000 +0100 +++ libarchive-2.6.0/tar/cmdline.c 2009-01-21 22:11:51.000000000 +0100 @@ -46,7 +46,7 @@ __FBSDID("$FreeBSD$"); * Short options for tar. Please keep this sorted. */ static const char *short_options - = "Bb:C:cf:HhI:jkLlmnOoPpqrSs:T:tUuvW:wX:xyZz"; + = "Bb:C:cf:HhI:jkLlmnOoPpqrSs:T:tUuvW:wX:xYyZz"; /* * Long options for tar. Please keep this list sorted. @@ -117,10 +117,12 @@ static struct option { { "uncompress", 0, 'Z' }, { "unlink", 0, 'U' }, { "unlink-first", 0, 'U' }, + { "unxz", 0, 'Y' }, { "update", 0, 'u' }, { "use-compress-program", 1, OPTION_USE_COMPRESS_PROGRAM }, { "verbose", 0, 'v' }, { "version", 0, OPTION_VERSION }, + { "xz", 0, 'Y' }, { NULL, 0, 0 } }; --- libarchive-2.6.0/tar/write.c.xz~ 2008-12-28 21:08:12.000000000 +0100 +++ libarchive-2.6.0/tar/write.c 2009-01-21 22:11:51.000000000 +0100 @@ -197,6 +197,11 @@ tar_mode_c(struct bsdtar *bsdtar) archive_write_set_compression_bzip2(a); break; #endif +#ifdef HAVE_LIBLZMA + case 'Y': + archive_write_set_compression_xz(a); + break; +#endif #ifdef HAVE_LIBZ case 'z': archive_write_set_compression_gzip(a);