From: Tim Kientzle Date: Sat, 20 Feb 2010 05:53:11 +0000 (-0500) Subject: Stackable write filter support. This ended up touching an awful lot X-Git-Tag: v3.0.0a~1243 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=911dc2bff3dc37fa499c17379dcf35f352f88c0b;p=thirdparty%2Flibarchive.git Stackable write filter support. This ended up touching an awful lot of files. But, the old API is supported almost entirely unchanged, which I wasn't certain would be possible. Big changes: * You can add more than one write filter by using archive_write_add_filter_{bzip2,compress,gzip,lzma,xz}. This will be more interesting when we have uuencode, RPM, encryption. * The old archive_write_set_compression_XXXX are shorthands for "remove all the current filters and add this one." They're deprecated and scheduled to be removed in libarchive 4.0. * The internal API and life cycle for write filters has been rationalized: create, set options, open, write, close, free. * New utility functions provide information about each filter when there's more than one: code, name, and number of bytes processed * Old archive_bytes_compressed(), etc, are implemented in terms of the more generic new functions. * The read side was generalized to also support the new utility functions. In particular, the write filters are much simpler since each one doesn't have to deal with blocking. In this version, there's still a "write_add_filter_none" that handles blocking, but I think I'll soon fold that down into the client wrapper and add_filter_none will become a no-op. I think this also gets us a big step closer to multi-volume support on the write side. SVN-Revision: 1920 --- diff --git a/Makefile.am b/Makefile.am index 3ad407d08..e59e7c113 100644 --- a/Makefile.am +++ b/Makefile.am @@ -151,12 +151,12 @@ libarchive_la_SOURCES= \ libarchive/archive_write_open_filename.c \ libarchive/archive_write_open_memory.c \ libarchive/archive_write_private.h \ - libarchive/archive_write_set_compression_bzip2.c \ - libarchive/archive_write_set_compression_compress.c \ - 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_add_filter_bzip2.c \ + libarchive/archive_write_add_filter_compress.c \ + libarchive/archive_write_add_filter_gzip.c \ + libarchive/archive_write_add_filter_none.c \ + libarchive/archive_write_add_filter_program.c \ + libarchive/archive_write_add_filter_xz.c \ libarchive/archive_write_set_format.c \ libarchive/archive_write_set_format_ar.c \ libarchive/archive_write_set_format_by_name.c \ diff --git a/build/autogen.sh b/build/autogen.sh index 8a9c9deb9..dde976fcd 100755 --- a/build/autogen.sh +++ b/build/autogen.sh @@ -40,8 +40,9 @@ cd .. # Clean up the source dir as much as we can. /bin/sh build/clean.sh -# Substitute the integer version into Libarchive's archive.h +# Substitute the versions into Libarchive's archive.h and archive_entry.h perl -p -i -e "s/^(#define\tARCHIVE_VERSION_NUMBER).*/\$1 $VN/" libarchive/archive.h +perl -p -i -e "s/^(#define\tARCHIVE_VERSION_NUMBER).*/\$1 $VN/" libarchive/archive_entry.h perl -p -i -e "s/^(#define\tARCHIVE_VERSION_STRING).*/\$1 \"libarchive $VS\"/" libarchive/archive.h # Substitute versions into configure.ac as well perl -p -i -e 's/(m4_define\(\[LIBARCHIVE_VERSION_S\]),.*\)/$1,['"$VS"'])/' configure.ac diff --git a/libarchive/CMakeLists.txt b/libarchive/CMakeLists.txt index 0ca57e616..82b921b22 100644 --- a/libarchive/CMakeLists.txt +++ b/libarchive/CMakeLists.txt @@ -72,12 +72,12 @@ SET(libarchive_SOURCES archive_write_open_file.c archive_write_open_filename.c archive_write_open_memory.c - archive_write_set_compression_bzip2.c - archive_write_set_compression_compress.c - archive_write_set_compression_gzip.c - archive_write_set_compression_none.c - archive_write_set_compression_program.c - archive_write_set_compression_xz.c + archive_write_add_filter_bzip2.c + archive_write_add_filter_compress.c + archive_write_add_filter_gzip.c + archive_write_add_filter_none.c + archive_write_add_filter_program.c + archive_write_add_filter_xz.c archive_write_set_format.c archive_write_set_format_ar.c archive_write_set_format_by_name.c diff --git a/libarchive/archive.h b/libarchive/archive.h index e2c8b8c1f..40bdbdf70 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2003-2010 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -129,13 +129,13 @@ extern "C" { * (ARCHIVE_API_VERSION * 1000000 + ARCHIVE_API_FEATURE * 1000) * #endif */ -#define ARCHIVE_VERSION_NUMBER 2007902 +#define ARCHIVE_VERSION_NUMBER 3000000 __LA_DECL int archive_version_number(void); /* * Textual name/version of the library, useful for version displays. */ -#define ARCHIVE_VERSION_STRING "libarchive 2.7.902a" +#define ARCHIVE_VERSION_STRING "libarchive 3.0.0a" __LA_DECL const char * archive_version_string(void); #if ARCHIVE_VERSION_NUMBER < 3000000 @@ -221,25 +221,34 @@ typedef __LA_SSIZE_T archive_write_callback(struct archive *, void *_client_data, const void *_buffer, size_t _length); -#if ARCHIVE_VERSION_NUMBER < 3000000 -/* Open callback is actually never needed; remove it in libarchive 3.0. */ typedef int archive_open_callback(struct archive *, void *_client_data); -#endif typedef int archive_close_callback(struct archive *, void *_client_data); /* - * Codes for archive_compression. + * Codes to identify various stream filters. */ -#define ARCHIVE_COMPRESSION_NONE 0 -#define ARCHIVE_COMPRESSION_GZIP 1 -#define ARCHIVE_COMPRESSION_BZIP2 2 -#define ARCHIVE_COMPRESSION_COMPRESS 3 -#define ARCHIVE_COMPRESSION_PROGRAM 4 -#define ARCHIVE_COMPRESSION_LZMA 5 -#define ARCHIVE_COMPRESSION_XZ 6 -#define ARCHIVE_COMPRESSION_UU 7 -#define ARCHIVE_COMPRESSION_RPM 8 +#define ARCHIVE_FILTER_NONE 0 +#define ARCHIVE_FILTER_GZIP 1 +#define ARCHIVE_FILTER_BZIP2 2 +#define ARCHIVE_FILTER_COMPRESS 3 +#define ARCHIVE_FILTER_PROGRAM 4 +#define ARCHIVE_FILTER_LZMA 5 +#define ARCHIVE_FILTER_XZ 6 +#define ARCHIVE_FILTER_UU 7 +#define ARCHIVE_FILTER_RPM 8 + +#if ARCHIVE_VERSION_NUMBER < 4000000 +#define ARCHIVE_COMPRESSION_NONE ARCHIVE_FILTER_NONE +#define ARCHIVE_COMPRESSION_GZIP ARCHIVE_FILTER_GZIP +#define ARCHIVE_COMPRESSION_BZIP2 ARCHIVE_FILTER_BZIP2 +#define ARCHIVE_COMPRESSION_COMPRESS ARCHIVE_FILTER_COMPRESS +#define ARCHIVE_COMPRESSION_PROGRAM ARCHIVE_FILTER_PROGRAM +#define ARCHIVE_COMPRESSION_LZMA ARCHIVE_FILTER_LZMA +#define ARCHIVE_COMPRESSION_XZ ARCHIVE_FILTER_XZ +#define ARCHIVE_COMPRESSION_UU ARCHIVE_FILTER_UU +#define ARCHIVE_COMPRESSION_RPM ARCHIVE_FILTER_RPM +#endif /* * Codes returned by archive_format. @@ -305,40 +314,43 @@ __LA_DECL struct archive *archive_read_new(void); * support_compression_bzip2(). The "all" functions provide the * obvious shorthand. */ -__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_lzma(struct archive *); -__LA_DECL int archive_read_support_compression_none(struct archive *); -__LA_DECL int archive_read_support_compression_program(struct archive *, +/* TODO: Rename 'compression' here to 'filter' for libarchive 3.0, deprecate + * the old names. */ + +__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_lzma(struct archive *); +__LA_DECL int archive_read_support_compression_none(struct archive *); +__LA_DECL int archive_read_support_compression_program(struct archive *, const char *command); -__LA_DECL int archive_read_support_compression_program_signature - (struct archive *, const char *, +__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 *); +__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 *); -__LA_DECL int archive_read_support_format_all(struct archive *); -__LA_DECL int archive_read_support_format_ar(struct archive *); -__LA_DECL int archive_read_support_format_cpio(struct archive *); -__LA_DECL int archive_read_support_format_empty(struct archive *); -__LA_DECL int archive_read_support_format_gnutar(struct archive *); -__LA_DECL int archive_read_support_format_iso9660(struct archive *); -__LA_DECL int archive_read_support_format_mtree(struct archive *); -__LA_DECL int archive_read_support_format_raw(struct archive *); -__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 *); +__LA_DECL int archive_read_support_format_all(struct archive *); +__LA_DECL int archive_read_support_format_ar(struct archive *); +__LA_DECL int archive_read_support_format_cpio(struct archive *); +__LA_DECL int archive_read_support_format_empty(struct archive *); +__LA_DECL int archive_read_support_format_gnutar(struct archive *); +__LA_DECL int archive_read_support_format_iso9660(struct archive *); +__LA_DECL int archive_read_support_format_mtree(struct archive *); +__LA_DECL int archive_read_support_format_raw(struct archive *); +__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 *); /* Open the archive using callbacks for archive I/O. */ -__LA_DECL int archive_read_open(struct archive *, void *_client_data, +__LA_DECL int archive_read_open(struct archive *, void *_client_data, archive_open_callback *, archive_read_callback *, archive_close_callback *); -__LA_DECL int archive_read_open2(struct archive *, void *_client_data, +__LA_DECL int archive_read_open2(struct archive *, void *_client_data, archive_open_callback *, archive_read_callback *, archive_skip_callback *, archive_close_callback *); @@ -348,30 +360,30 @@ __LA_DECL int archive_read_open2(struct archive *, void *_client_data, * accept a block size handle tape blocking correctly. */ /* Use this if you know the filename. Note: NULL indicates stdin. */ -__LA_DECL int archive_read_open_filename(struct archive *, +__LA_DECL int archive_read_open_filename(struct archive *, const char *_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 *, +__LA_DECL int archive_read_open_file(struct archive *, const char *_filename, size_t _block_size); /* Read an archive that's stored in memory. */ -__LA_DECL int archive_read_open_memory(struct archive *, +__LA_DECL int archive_read_open_memory(struct archive *, void * buff, size_t size); /* A more involved version that is only used for internal testing. */ -__LA_DECL int archive_read_open_memory2(struct archive *a, void *buff, +__LA_DECL int archive_read_open_memory2(struct archive *a, void *buff, size_t size, size_t read_size); /* Read an archive that's already open, using the file descriptor. */ -__LA_DECL int archive_read_open_fd(struct archive *, int _fd, +__LA_DECL int archive_read_open_fd(struct archive *, int _fd, size_t _block_size); /* Read an archive that's already open, using a FILE *. */ /* Note: DO NOT use this with tape drives. */ -__LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file); +__LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file); /* Parses and returns next entry header. */ -__LA_DECL int archive_read_next_header(struct archive *, +__LA_DECL int archive_read_next_header(struct archive *, struct archive_entry **); /* Parses and returns next entry header using the archive_entry passed in */ -__LA_DECL int archive_read_next_header2(struct archive *, +__LA_DECL int archive_read_next_header2(struct archive *, struct archive_entry *); /* @@ -391,12 +403,11 @@ __LA_DECL __LA_SSIZE_T archive_read_data(struct archive *, * be strictly increasing and that returned blocks will not overlap. */ #if ARCHIVE_VERSION_NUMBER < 3000000 -__LA_DECL int archive_read_data_block(struct archive *a, - const void **buff, size_t *size, off_t *offset); +__LA_DECL int archive_read_data_block(struct archive *a, + const void **buff, size_t *size, off_t *offset); #else -__LA_DECL int archive_read_data_block(struct archive *a, - const void **buff, size_t *size, - __LA_INT64_T *offset); +__LA_DECL int archive_read_data_block(struct archive *a, + const void **buff, size_t *size, __LA_INT64_T *offset); #endif /*- @@ -405,22 +416,22 @@ __LA_DECL int archive_read_data_block(struct archive *a, * 'into_buffer': writes data into memory buffer that you provide * 'into_fd': writes data to specified filedes */ -__LA_DECL int archive_read_data_skip(struct archive *); -__LA_DECL int archive_read_data_into_buffer(struct archive *, +__LA_DECL int archive_read_data_skip(struct archive *); +__LA_DECL int archive_read_data_into_buffer(struct archive *, void *buffer, __LA_SSIZE_T len); -__LA_DECL int archive_read_data_into_fd(struct archive *, int fd); +__LA_DECL int archive_read_data_into_fd(struct archive *, int fd); /* * Set read options. */ /* Apply option string to the format only. */ -__LA_DECL int archive_read_set_format_options(struct archive *_a, +__LA_DECL int archive_read_set_format_options(struct archive *_a, const char *s); /* Apply option string to the filter only. */ -__LA_DECL int archive_read_set_filter_options(struct archive *_a, +__LA_DECL int archive_read_set_filter_options(struct archive *_a, const char *s); /* Apply option string to both the format and the filter. */ -__LA_DECL int archive_read_set_options(struct archive *_a, +__LA_DECL int archive_read_set_options(struct archive *_a, const char *s); /*- @@ -467,9 +478,9 @@ __LA_DECL int archive_read_set_options(struct archive *_a, /* Detect blocks of 0 and write holes instead. */ #define ARCHIVE_EXTRACT_SPARSE (0x1000) -__LA_DECL int archive_read_extract(struct archive *, struct archive_entry *, +__LA_DECL int archive_read_extract(struct archive *, struct archive_entry *, int flags); -__LA_DECL int archive_read_extract2(struct archive *, struct archive_entry *, +__LA_DECL int archive_read_extract2(struct archive *, struct archive_entry *, struct archive * /* dest */); __LA_DECL void archive_read_extract_set_progress_callback(struct archive *, void (*_progress_func)(void *), void *_user_data); @@ -505,64 +516,76 @@ __LA_DECL int archive_read_finish(struct archive *); * 6) archive_write_free to cleanup the writer and release resources */ __LA_DECL struct archive *archive_write_new(void); -__LA_DECL int archive_write_set_bytes_per_block(struct archive *, +__LA_DECL int archive_write_set_bytes_per_block(struct archive *, int bytes_per_block); -__LA_DECL int archive_write_get_bytes_per_block(struct archive *); +__LA_DECL int archive_write_get_bytes_per_block(struct archive *); /* XXX This is badly misnamed; suggestions appreciated. XXX */ -__LA_DECL int archive_write_set_bytes_in_last_block(struct archive *, +__LA_DECL int archive_write_set_bytes_in_last_block(struct archive *, int bytes_in_last_block); -__LA_DECL int archive_write_get_bytes_in_last_block(struct archive *); +__LA_DECL int archive_write_get_bytes_in_last_block(struct archive *); /* The dev/ino of a file that won't be archived. This is used * to avoid recursively adding an archive to itself. */ -__LA_DECL int archive_write_set_skip_file(struct archive *, dev_t, ino_t); - -__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_lzma(struct archive *); -__LA_DECL int archive_write_set_compression_none(struct archive *); -__LA_DECL int archive_write_set_compression_program(struct archive *, +__LA_DECL int archive_write_set_skip_file(struct archive *, dev_t, ino_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_lzma(struct archive *); +__LA_DECL int archive_write_set_compression_none(struct archive *); +__LA_DECL int archive_write_set_compression_program(struct archive *, + const char *cmd); +__LA_DECL int archive_write_set_compression_xz(struct archive *); +#endif + +__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_gzip(struct archive *); +__LA_DECL int archive_write_add_filter_lzma(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_set_compression_xz(struct archive *); +__LA_DECL int archive_write_add_filter_xz(struct archive *); + + /* A convenience function to set the format based on the code or name. */ -__LA_DECL int archive_write_set_format(struct archive *, int format_code); -__LA_DECL int archive_write_set_format_by_name(struct archive *, +__LA_DECL int archive_write_set_format(struct archive *, int format_code); +__LA_DECL int archive_write_set_format_by_name(struct archive *, const char *name); /* To minimize link pollution, use one or more of the following. */ -__LA_DECL int archive_write_set_format_ar_bsd(struct archive *); -__LA_DECL int archive_write_set_format_ar_svr4(struct archive *); -__LA_DECL int archive_write_set_format_cpio(struct archive *); -__LA_DECL int archive_write_set_format_cpio_newc(struct archive *); -__LA_DECL int archive_write_set_format_mtree(struct archive *); +__LA_DECL int archive_write_set_format_ar_bsd(struct archive *); +__LA_DECL int archive_write_set_format_ar_svr4(struct archive *); +__LA_DECL int archive_write_set_format_cpio(struct archive *); +__LA_DECL int archive_write_set_format_cpio_newc(struct archive *); +__LA_DECL int archive_write_set_format_mtree(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_zip(struct archive *); -__LA_DECL int archive_write_open(struct archive *, void *, +__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_zip(struct archive *); +__LA_DECL int archive_write_open(struct archive *, void *, archive_open_callback *, archive_write_callback *, archive_close_callback *); -__LA_DECL int archive_write_open_fd(struct archive *, int _fd); -__LA_DECL int archive_write_open_filename(struct archive *, const char *_file); +__LA_DECL int archive_write_open_fd(struct archive *, int _fd); +__LA_DECL int archive_write_open_filename(struct archive *, const char *_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 *, FILE *); +__LA_DECL int archive_write_open_file(struct archive *, const char *_file); +__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. */ -__LA_DECL int archive_write_open_memory(struct archive *, +__LA_DECL int archive_write_open_memory(struct archive *, void *_buffer, size_t _buffSize, size_t *_used); /* * Note that the library will truncate writes beyond the size provided * to archive_write_header or pad if the provided data is short. */ -__LA_DECL int archive_write_header(struct archive *, +__LA_DECL int archive_write_header(struct archive *, struct archive_entry *); -/* Libarchive 2.0 and later return ssize_t here. */ -__LA_DECL __LA_SSIZE_T archive_write_data(struct archive *, +__LA_DECL __LA_SSIZE_T archive_write_data(struct archive *, const void *, size_t); #if ARCHIVE_VERSION_NUMBER < 3000000 @@ -598,7 +621,6 @@ __LA_DECL int archive_write_set_compressor_options(struct archive *_a, __LA_DECL int archive_write_set_options(struct archive *_a, const char *s); - /*- * ARCHIVE_WRITE_DISK API * @@ -693,13 +715,29 @@ __LA_DECL int archive_read_disk_set_uname_lookup(struct archive *, * Accessor functions to read/set various information in * the struct archive object: */ -/* Bytes written after compression or read before decompression. */ + +/* Number of filters in the current filter pipeline. */ +/* Filter #0 is the one closest to the format, -1 is a synonym for the + * last filter, which is always the pseudo-filter that wraps the + * client callbacks. */ +__LA_DECL int archive_filter_count(struct archive *); +__LA_DECL __LA_INT64_T archive_filter_bytes(struct archive *, int); +__LA_DECL int archive_filter_code(struct archive *, int); +__LA_DECL const char * archive_filter_name(struct archive *, int); + +#if ARCHIVE_VERSION_NUMBER < 4000000 +/* 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 *); -/* Bytes written to compressor or read from decompressor. */ +/* 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 *); - +/* As of libarchive 3.0, this is an alias for archive_filter_name(a, 0); */ __LA_DECL const char *archive_compression_name(struct archive *); +/* As of libarchive 3.0, this is an alias for archive_filter_code(a, 0); */ __LA_DECL int archive_compression(struct archive *); +#endif + __LA_DECL int archive_errno(struct archive *); __LA_DECL const char *archive_error_string(struct archive *); __LA_DECL const char *archive_format_name(struct archive *); diff --git a/libarchive/archive_entry.h b/libarchive/archive_entry.h index c74bcca29..da6ac8849 100644 --- a/libarchive/archive_entry.h +++ b/libarchive/archive_entry.h @@ -28,6 +28,11 @@ #ifndef ARCHIVE_ENTRY_H_INCLUDED #define ARCHIVE_ENTRY_H_INCLUDED +/* Needed if archive_entry.h is used without archive.h */ +#ifndef ARCHIVE_VERSION_NUMBER +#define ARCHIVE_VERSION_NUMBER 3000000 +#endif + /* * Note: archive_entry.h is for use outside of libarchive; the * configuration headers (config.h, archive_platform.h, etc.) are @@ -272,11 +277,10 @@ __LA_DECL void archive_entry_set_hardlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_hardlink(struct archive_entry *, const char *); __LA_DECL void archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *); __LA_DECL int archive_entry_update_hardlink_utf8(struct archive_entry *, const char *); -#if ARCHIVE_VERSION_NUMBER >= 3000000 -/* Starting with libarchive 3.0, this will be synonym for ino64. */ -__LA_DECL void archive_entry_set_ino(struct archive_entry *, __LA_INT64_T); -#else +#if ARCHIVE_VERSION_NUMBER < 3000000 __LA_DECL void archive_entry_set_ino(struct archive_entry *, unsigned long); +#else +__LA_DECL void archive_entry_set_ino(struct archive_entry *, __LA_INT64_T); #endif __LA_DECL void archive_entry_set_ino64(struct archive_entry *, __LA_INT64_T); __LA_DECL void archive_entry_set_link(struct archive_entry *, const char *); diff --git a/libarchive/archive_entry_copy_stat.c b/libarchive/archive_entry_copy_stat.c index ef59a5e78..1a049ba3b 100644 --- a/libarchive/archive_entry_copy_stat.c +++ b/libarchive/archive_entry_copy_stat.c @@ -30,6 +30,7 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_entry_copy_stat.c 189466 2009-03 #include #endif +#include "archive.h" #include "archive_entry.h" void diff --git a/libarchive/archive_private.h b/libarchive/archive_private.h index dc8857a3b..ea9f20528 100644 --- a/libarchive/archive_private.h +++ b/libarchive/archive_private.h @@ -51,7 +51,6 @@ #define ARCHIVE_STATE_NEW 1U #define ARCHIVE_STATE_HEADER 2U #define ARCHIVE_STATE_DATA 4U -#define ARCHIVE_STATE_DATA_END 8U #define ARCHIVE_STATE_EOF 0x10U #define ARCHIVE_STATE_CLOSED 0x20U #define ARCHIVE_STATE_FATAL 0x8000U @@ -66,6 +65,11 @@ struct archive_vtable { const void *, size_t); ssize_t (*archive_write_data_block)(struct archive *, const void *, size_t, off_t); + + int (*archive_filter_count)(struct archive *); + int64_t (*archive_filter_bytes)(struct archive *, int); + int (*archive_filter_code)(struct archive *, int); + const char * (*archive_filter_name)(struct archive *, int); }; struct archive { @@ -90,10 +94,6 @@ struct archive { int compression_code; /* Currently active compression. */ const char *compression_name; - /* Position in UNCOMPRESSED data stream. */ - int64_t file_position; - /* Position in COMPRESSED data stream. */ - int64_t raw_position; /* Number of file entries processed. */ int file_count; diff --git a/libarchive/archive_read.c b/libarchive/archive_read.c index 9ed61efb9..4edbf2bc1 100644 --- a/libarchive/archive_read.c +++ b/libarchive/archive_read.c @@ -57,8 +57,12 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read.c 201157 2009-12-29 05:30:2 static int build_stream(struct archive_read *); static int choose_format(struct archive_read *); -static int cleanup_filters(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); +static const char *_archive_filter_name(struct archive *, int); static int _archive_read_close(struct archive *); static int _archive_read_free(struct archive *); @@ -69,6 +73,9 @@ archive_read_vtable(void) static int inited = 0; if (!inited) { + av.archive_filter_bytes = _archive_filter_bytes; + av.archive_filter_code = _archive_filter_code; + av.archive_filter_name = _archive_filter_name; av.archive_free = _archive_read_free; av.archive_close = _archive_read_close; } @@ -245,7 +252,6 @@ client_read_proxy(struct archive_read_filter *self, const void **buff) ssize_t r; r = (self->archive->client.reader)(&self->archive->archive, self->data, buff); - self->archive->archive.raw_position += r; return (r); } @@ -269,7 +275,6 @@ client_skip_proxy(struct archive_read_filter *self, int64_t request) if (get == 0) return (total); request -= get; - self->archive->archive.raw_position += get; total += get; } } @@ -398,7 +403,8 @@ build_stream(struct archive_read *a) /* Verify the filter by asking it for some data. */ __archive_read_filter_ahead(filter, 1, &avail); if (avail < 0) { - cleanup_filters(a); + close_filters(a); + free_filters(a); return (ARCHIVE_FATAL); } } @@ -448,8 +454,8 @@ archive_read_next_header2(struct archive *_a, struct archive_entry *entry) return (ret); } - /* Record start-of-header. */ - a->header_position = a->archive.file_position; + /* Record start-of-header offset in uncompressed stream. */ + a->header_position = a->filter->bytes_consumed; ret = (a->format->read_header)(a, entry); @@ -712,69 +718,60 @@ archive_read_data_block(struct archive *_a, return (a->format->read_data)(a, buff, size, offset); } +static int +close_filters(struct archive_read *a) +{ + struct archive_read_filter *f = a->filter; + int r = ARCHIVE_OK; + /* Close each filter in the pipeline. */ + while (f != NULL) { + struct archive_read_filter *t = f->upstream; + if (f->close != NULL) { + int r1 = (f->close)(f); + if (r1 < r) + r = r1; + } + free(f->buffer); + f->buffer = NULL; + f = t; + } + return r; +} + +static void +free_filters(struct archive_read *a) +{ + while (a->filter != NULL) { + struct archive_read_filter *t = a->filter->upstream; + free(a->filter); + a->filter = t; + } +} + /* - * Close the file and release most resources. - * - * Be careful: client might just call read_new and then read_finish. - * Don't assume we actually read anything or performed any non-trivial - * initialization. + * Close the file and all I/O. */ static int _archive_read_close(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; int r = ARCHIVE_OK, r1 = ARCHIVE_OK; - size_t i, n; __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY, "archive_read_close"); archive_clear_error(&a->archive); a->archive.state = ARCHIVE_STATE_CLOSED; - - /* Call cleanup functions registered by optional components. */ - if (a->cleanup_archive_extract != NULL) - r = (a->cleanup_archive_extract)(a); - /* TODO: Clean up the formatters. */ /* Release the filter objects. */ - r1 = cleanup_filters(a); + r1 = close_filters(a); if (r1 < r) r = r1; - /* Release the bidder objects. */ - n = sizeof(a->bidders)/sizeof(a->bidders[0]); - for (i = 0; i < n; i++) { - if (a->bidders[i].free != NULL) { - r1 = (a->bidders[i].free)(&a->bidders[i]); - if (r1 < r) - r = r1; - } - } - return (r); } -static int -cleanup_filters(struct archive_read *a) -{ - int r = ARCHIVE_OK; - /* Clean up the filter pipeline. */ - while (a->filter != NULL) { - struct archive_read_filter *t = a->filter->upstream; - if (a->filter->close != NULL) { - int r1 = (a->filter->close)(a->filter); - if (r1 < r) - r = r1; - } - free(a->filter->buffer); - free(a->filter); - a->filter = t; - } - return r; -} - /* * Release memory and other resources. */ @@ -782,7 +779,7 @@ static int _archive_read_free(struct archive *_a) { struct archive_read *a = (struct archive_read *)_a; - int i; + int i, n; int slots; int r = ARCHIVE_OK; @@ -791,6 +788,10 @@ _archive_read_free(struct archive *_a) if (a->archive.state != ARCHIVE_STATE_CLOSED) r = archive_read_close(&a->archive); + /* Call cleanup functions registered by optional components. */ + if (a->cleanup_archive_extract != NULL) + r = (a->cleanup_archive_extract)(a); + /* Cleanup format-specific data. */ slots = sizeof(a->formats) / sizeof(a->formats[0]); for (i = 0; i < slots; i++) { @@ -799,6 +800,19 @@ _archive_read_free(struct archive *_a) (a->formats[i].cleanup)(a); } + /* Free the filters */ + free_filters(a); + + /* Release the bidder objects. */ + n = sizeof(a->bidders)/sizeof(a->bidders[0]); + for (i = 0; i < n; i++) { + if (a->bidders[i].free != NULL) { + int r1 = (a->bidders[i].free)(&a->bidders[i]); + if (r1 < r) + r = r1; + } + } + archive_string_free(&a->archive.error_string); if (a->entry) archive_entry_free(a->entry); @@ -807,6 +821,42 @@ _archive_read_free(struct archive *_a) return (r); } +static struct archive_read_filter * +get_filter(struct archive *_a, int n) +{ + struct archive_read *a = (struct archive_read *)_a; + struct archive_read_filter *f = a->filter; + /* XXX handle n == -1 */ + if (n < 0) + return NULL; + while (n > 0 && f != NULL) { + f = f->upstream; + --n; + } + return (f); +} + +static int +_archive_filter_code(struct archive *_a, int n) +{ + struct archive_read_filter *f = get_filter(_a, n); + return f == NULL ? -1 : f->code; +} + +static const char * +_archive_filter_name(struct archive *_a, int n) +{ + struct archive_read_filter *f = get_filter(_a, n); + return f == NULL ? NULL : f->name; +} + +static int64_t +_archive_filter_bytes(struct archive *_a, int n) +{ + struct archive_read_filter *f = get_filter(_a, n); + return f == NULL ? -1 : f->bytes_consumed; +} + /* * Used internally by read format handlers to register their bid and * initialization functions. @@ -1125,10 +1175,7 @@ __archive_read_filter_ahead(struct archive_read_filter *filter, ssize_t __archive_read_consume(struct archive_read *a, size_t request) { - ssize_t r; - r = __archive_read_filter_consume(a->filter, request); - a->archive.file_position += r; - return (r); + return (__archive_read_filter_consume(a->filter, request)); } ssize_t @@ -1144,6 +1191,7 @@ __archive_read_filter_consume(struct archive_read_filter * filter, filter->client_next += request; filter->client_avail -= request; } + filter->bytes_consumed += request; return (request); } @@ -1173,10 +1221,7 @@ __archive_read_skip(struct archive_read *a, int64_t request) int64_t __archive_read_skip_lenient(struct archive_read *a, int64_t request) { - int64_t skipped = __archive_read_filter_skip(a->filter, request); - if (skipped > 0) - a->archive.file_position += skipped; - return (skipped); + return (__archive_read_filter_skip(a->filter, request)); } int64_t @@ -1204,6 +1249,7 @@ __archive_read_filter_skip(struct archive_read_filter *filter, int64_t request) } if (request == 0) return (total_bytes_skipped); + /* * If a client_skipper was provided, try that first. */ @@ -1215,6 +1261,7 @@ __archive_read_filter_skip(struct archive_read_filter *filter, int64_t request) filter->fatal = 1; return (bytes_skipped); } + filter->bytes_consumed += bytes_skipped; total_bytes_skipped += bytes_skipped; request -= bytes_skipped; filter->client_next = filter->client_buff; diff --git a/libarchive/archive_read_private.h b/libarchive/archive_read_private.h index 5a8501890..53d9b6545 100644 --- a/libarchive/archive_read_private.h +++ b/libarchive/archive_read_private.h @@ -42,13 +42,18 @@ struct archive_read_filter; /* * How bidding works for filters: - * * The bid manager reads the first block from the current source. - * * It shows that block to each registered bidder. + * * The bid manager initializes the client-provided reader as the + * first filter. + * * It invokes the bidder for each registered filter with the + * current head filter. + * * The bidders can use archive_read_filter_ahead() to peek ahead + * at the incoming data to compose their bids. * * The bid manager creates a new filter structure for the winning * bidder and gives the winning bidder a chance to initialize it. - * * The new filter becomes the top filter in the archive_read structure - * and we repeat the process. - * This ends only when no bidder provides a non-zero bid. + * * The new filter becomes the new top filter and we repeat the + * process. + * This ends only when no bidder provides a non-zero bid. Then + * we perform a similar dance with the registered format handlers. */ struct archive_read_filter_bidder { /* Configuration data for the bidder. */ @@ -71,6 +76,7 @@ struct archive_read_filter_bidder { * corresponding bidder above. */ struct archive_read_filter { + int64_t bytes_consumed; /* Essentially all filters will need these values, so * just declare them here. */ struct archive_read_filter_bidder *bidder; /* My bidder. */ diff --git a/libarchive/archive_read_support_compression_bzip2.c b/libarchive/archive_read_support_compression_bzip2.c index 1e45f2202..d08488950 100644 --- a/libarchive/archive_read_support_compression_bzip2.c +++ b/libarchive/archive_read_support_compression_bzip2.c @@ -343,6 +343,7 @@ bzip2_filter_close(struct archive_read_filter *self) "Failed to clean up decompressor"); ret = ARCHIVE_FATAL; } + state->valid = 0; } free(state->out_block); diff --git a/libarchive/archive_read_support_format_ar.c b/libarchive/archive_read_support_format_ar.c index 7c1ca3a0c..80c014a1f 100644 --- a/libarchive/archive_read_support_format_ar.c +++ b/libarchive/archive_read_support_format_ar.c @@ -55,6 +55,7 @@ struct ar { off_t entry_padding; char *strtab; size_t strtab_size; + char read_global_header; }; /* @@ -172,12 +173,13 @@ archive_read_format_ar_read_header(struct archive_read *a, ar = (struct ar*)(a->format->data); - if (a->archive.file_position == 0) { + if (!ar->read_global_header) { /* * We are now at the beginning of the archive, * so we need first consume the ar global header. */ __archive_read_consume(a, 8); + ar->read_global_header = 1; /* Set a default format code for now. */ a->archive.archive_format = ARCHIVE_FORMAT_AR; } diff --git a/libarchive/archive_util.3 b/libarchive/archive_util.3 index 98609e565..fb04c4fbd 100644 --- a/libarchive/archive_util.3 +++ b/libarchive/archive_util.3 @@ -35,8 +35,12 @@ .Nm archive_errno , .Nm archive_error_string , .Nm archive_file_count , +.Nm archive_filter_code , +.Nm archive_filter_count , +.Nm archive_filter_name , .Nm archive_format , .Nm archive_format_name , +.Nm archive_position , .Nm archive_set_error .Nd libarchive utility functions .Sh SYNOPSIS @@ -56,9 +60,17 @@ .Ft int .Fn archive_file_count "struct archive *" .Ft int +.Fn archive_filter_code "struct archive *" "int" +.Ft int +.Fn archive_filter_count "struct archive *" "int" +.Ft const char * +.Fn archive_filter_name "struct archive *" "int" +.Ft int .Fn archive_format "struct archive *" .Ft const char * .Fn archive_format_name "struct archive *" +.Ft int64_t +.Fn archive_position "struct archive *" "int" .Ft void .Fo archive_set_error .Fa "struct archive *" @@ -77,11 +89,11 @@ library. Clears any error information left over from a previous call. Not generally used in client code. .It Fn archive_compression -Returns a numeric code indicating the current compression. -This value is set by -.Fn archive_read_open . +Synonym for +.Fn archive_filter_code(a, 0) . .It Fn archive_compression_name -Returns a text description of the current compression suitable for display. +Synonym for +.Fn archive_filter_name(a, 0) . .It Fn archive_copy_error Copies error information from one archive to another. .It Fn archive_errno @@ -101,6 +113,42 @@ The count is incremented by calls to .Xr archive_write_header or .Xr archive_read_next_header . +.It Fn archive_filter_code +Returns a numeric code identifying the indicated filter. +See +.Fn archive_filter_count +for details of the numbering. +.It Fn archive_filter_count +Returns the number of filters in the current pipeline. +For read archive handles, these filters are added automatically +by the automatic format detection. +For write archive handles, these filters are added by calls to the various +.Fn archive_write_add_filter_XXX +functions. +Filters in the resulting pipeline are numbered so that filter 0 +is the filter closest to the format handler. +As a convenience, functions that expect a filter number will +accept -1 as a synonym for the highest-numbered filter. +.Pp +For example, when reading a uuencoded gzipped tar archive, there +are three filters: +filter 0 is the gunzip filter, +filter 1 is the uudecode filter, +and filter 2 is the pseudo-filter that wraps the archive read functions. +In this case, requesting +.Fn archive_position(a, -1) +would be a synonym for +.Fn archive_position(a, 2) +which would return the number of bytes currently read from the archive, while +.Fn archive_position(a, 1) +would return the number of bytes after uudecoding, and +.Fn archive_position(a, 0) +would return the number of bytes after decompression. +.It Fn archive_filter_name +Returns a textual name identifying the indicated filter. +See +.Fn archive_filter_count +for details of the numbering. .It Fn archive_format Returns a numeric code indicating the format of the current archive entry. @@ -113,6 +161,16 @@ utilize GNU tar extensions and several entries that do not. These entries will have different format codes. .It Fn archive_format_name A textual description of the format of the current entry. +.It Fn archive_position +Returns the number of bytes read from or written to the indicated filter. +In particular, +.Fn archive_position(a, 0) +returns the number of bytes read or written by the format handler, while +.Fn archive_position(a, -1) +returns the number of bytes read or written to the archive. +See +.Fn archive_filter_count +for details of the numbering here. .It Fn archive_set_error Sets the numeric error code and error description that will be returned by diff --git a/libarchive/archive_util.c b/libarchive/archive_util.c index d00e0b6e5..4cf4de6f7 100644 --- a/libarchive/archive_util.c +++ b/libarchive/archive_util.c @@ -128,13 +128,13 @@ archive_format_name(struct archive *a) int archive_compression(struct archive *a) { - return (a->compression_code); + return archive_filter_code(a, 0); } const char * archive_compression_name(struct archive *a) { - return (a->compression_name); + return archive_filter_name(a, 0); } @@ -144,7 +144,7 @@ archive_compression_name(struct archive *a) int64_t archive_position_compressed(struct archive *a) { - return (a->raw_position); + return archive_filter_bytes(a, -1); } /* @@ -153,7 +153,7 @@ archive_position_compressed(struct archive *a) int64_t archive_position_uncompressed(struct archive *a) { - return (a->file_position); + return archive_filter_bytes(a, 0); } void diff --git a/libarchive/archive_virtual.c b/libarchive/archive_virtual.c index 836886270..db50a2449 100644 --- a/libarchive/archive_virtual.c +++ b/libarchive/archive_virtual.c @@ -30,6 +30,30 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_virtual.c 201098 2009-12-28 02:5 #include "archive_entry.h" #include "archive_private.h" +int +archive_filter_code(struct archive *a, int n) +{ + return ((a->vtable->archive_filter_code)(a, n)); +} + +int +archive_filter_count(struct archive *a) +{ + return ((a->vtable->archive_filter_count)(a)); +} + +const char * +archive_filter_name(struct archive *a, int n) +{ + return ((a->vtable->archive_filter_name)(a, n)); +} + +int64_t +archive_filter_bytes(struct archive *a, int n) +{ + return ((a->vtable->archive_filter_bytes)(a, n)); +} + int archive_write_close(struct archive *a) { diff --git a/libarchive/archive_write.c b/libarchive/archive_write.c index eba722722..26df292c7 100644 --- a/libarchive/archive_write.c +++ b/libarchive/archive_write.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003-2007 Tim Kientzle + * Copyright (c) 2003-2010 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -59,6 +59,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write.c 201099 2009-12-28 03:03: static struct archive_vtable *archive_write_vtable(void); +static int _archive_filter_code(struct archive *, int); +static const char *_archive_filter_name(struct archive *, int); +static int64_t _archive_filter_bytes(struct archive *, int); static int _archive_write_close(struct archive *); static int _archive_write_free(struct archive *); static int _archive_write_header(struct archive *, struct archive_entry *); @@ -73,6 +76,9 @@ archive_write_vtable(void) if (!inited) { av.archive_close = _archive_write_close; + av.archive_filter_bytes = _archive_filter_bytes; + av.archive_filter_code = _archive_filter_code; + av.archive_filter_name = _archive_filter_name; av.archive_free = _archive_write_free; av.archive_write_header = _archive_write_header; av.archive_write_finish_entry = _archive_write_finish_entry; @@ -114,13 +120,6 @@ archive_write_new(void) } memset(nulls, 0, a->null_length); a->nulls = nulls; - /* - * Set default compression, but don't set a default format. - * Were we to set a default format here, we would force every - * client to link in support for that format, even if they didn't - * ever use it. - */ - archive_write_set_compression_none(&a->archive); return (&a->archive); } @@ -174,6 +173,7 @@ int archive_write_set_compressor_options(struct archive *_a, const char *s) { struct archive_write *a = (struct archive_write *)_a; + struct archive_write_filter *filter; char key[64], val[64]; int len, r; int ret = ARCHIVE_OK; @@ -184,27 +184,27 @@ archive_write_set_compressor_options(struct archive *_a, const char *s) if (s == NULL || *s == '\0') return (ARCHIVE_OK); - if (a->compressor.options == NULL) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Unsupported option ``%s''", s); - /* This compressor does not support option. */ - return (ARCHIVE_WARN); - } - - while ((len = __archive_parse_options(s, a->archive.compression_name, - sizeof(key), key, sizeof(val), val)) > 0) { - if (val[0] == '\0') - r = a->compressor.options(a, key, NULL); - else - r = a->compressor.options(a, key, val); - if (r == ARCHIVE_FATAL) - return (r); - if (r < ARCHIVE_OK) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Unsupported option ``%s''", key); - ret = ARCHIVE_WARN; + for (filter = a->filter_first; filter != NULL; filter = filter->next_filter) { + if (filter->options == NULL) + /* This filter does not have any options */ + continue; + while ((len = __archive_parse_options(s, + a->archive.compression_name, + sizeof(key), key, sizeof(val), val)) > 0) { + if (val[0] == '\0') + r = filter->options(filter, key, NULL); + else + r = filter->options(filter, key, val); + if (r == ARCHIVE_FATAL) + return (r); + if (r < ARCHIVE_OK) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_MISC, + "Unsupported option ``%s''", key); + ret = ARCHIVE_WARN; + } + s += len; } - s += len; } if (len < 0) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, @@ -300,6 +300,107 @@ archive_write_set_skip_file(struct archive *_a, dev_t d, ino_t i) return (ARCHIVE_OK); } +/* + * Allocate and return the next filter structure. + */ +struct archive_write_filter * +__archive_write_allocate_filter(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + struct archive_write_filter *f; + __archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_allocate_filter"); + f = calloc(1, sizeof(*f)); + f->archive = _a; + if (a->filter_first == NULL) + a->filter_first = f; + else + a->filter_last->next_filter = f; + a->filter_last = f; + return f; +} + +/* + * Write data to a particular filter. + */ +int +__archive_write_filter(struct archive_write_filter *f, + const void *buff, size_t length) +{ + int r; + r = (f->write)(f, buff, length); + f->bytes_written += length; +// fprintf(stdout, "__archive_write_filter(%s, %d) = %d\n", f->name, (int)length, r); + return (r); +} + +/* + * Open a filter. + */ +int +__archive_write_open_filter(struct archive_write_filter *f) +{ + if (f->open == NULL) + return (ARCHIVE_OK); + return (f->open)(f); +} + +/* + * Close a filter. + */ +int +__archive_write_close_filter(struct archive_write_filter *f) +{ + if (f->close == NULL) + return (ARCHIVE_OK); + return (f->close)(f); +} + +int +__archive_write_output(struct archive_write *a, const void *buff, size_t length) +{ +// fprintf(stdout, "__archive_write_output(%d)\n", (int)length); + return (__archive_write_filter(a->filter_first, buff, length)); +} + +static int +archive_write_client_open(struct archive_write_filter *f) +{ + struct archive_write *a = (struct archive_write *)f->archive; + if (a->client_opener == NULL) + return (ARCHIVE_OK); + return (a->client_opener(f->archive, f->data)); +} + +static int +archive_write_client_write(struct archive_write_filter *f, + const void *_buff, size_t length) +{ + struct archive_write *a = (struct archive_write *)f->archive; + const char *buff = _buff; + + while (length > 0) { + ssize_t written + = (a->client_writer(f->archive, f->data, buff, length)); +// fprintf(stdout, "__archive_write_client_write(%d): written=%d\n", (int)length, (int)written); + if (written < 0) + return ((int)written); + if (written == 0) + return (ARCHIVE_FATAL); + buff += written; + length -= written; + } + return (ARCHIVE_OK); +} + +static int +archive_write_client_close(struct archive_write_filter *f) +{ + struct archive_write *a = (struct archive_write *)f->archive; + if (a->client_closer == NULL) + return (ARCHIVE_OK); + return (a->client_closer(f->archive, f->data)); +} /* * Open the archive using the current settings. @@ -310,29 +411,37 @@ archive_write_open(struct archive *_a, void *client_data, archive_close_callback *closer) { struct archive_write *a = (struct archive_write *)_a; + struct archive_write_filter *client_filter; int ret; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_open"); archive_clear_error(&a->archive); - a->archive.state = ARCHIVE_STATE_HEADER; - a->client_data = client_data; + + if (a->filter_first == NULL) + archive_write_set_compression_none(_a); + a->client_writer = writer; a->client_opener = opener; a->client_closer = closer; - ret = (a->compressor.init)(a); + + client_filter = __archive_write_allocate_filter(_a); + client_filter->data = client_data; + client_filter->open = archive_write_client_open; + client_filter->write = archive_write_client_write; + client_filter->close = archive_write_client_close; + + ret = __archive_write_open_filter(a->filter_first); + + a->archive.state = ARCHIVE_STATE_HEADER; + if (a->format_init && ret == ARCHIVE_OK) ret = (a->format_init)(a); return (ret); } - /* * Close out the archive. - * - * Be careful: user might just call write_new and then write_finish. - * Don't assume we actually wrote anything or performed any non-trivial - * initialization. */ static int _archive_write_close(struct archive *_a) @@ -340,59 +449,93 @@ _archive_write_close(struct archive *_a) struct archive_write *a = (struct archive_write *)_a; int r = ARCHIVE_OK, r1 = ARCHIVE_OK; - __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, - ARCHIVE_STATE_ANY, "archive_write_close"); + /* + * It's perfectly reasonable to call close() as part of + * routine cleanup, even after an error, so we're a little + * tolerant of being called in odd states. + */ + if (a->archive.state & ARCHIVE_STATE_FATAL) + return (ARCHIVE_FATAL); + archive_clear_error(&a->archive); + if (a->archive.state & (ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED)) + return (ARCHIVE_OK); + + __archive_check_magic(&a->archive, + ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, + "archive_write_close"); /* Finish the last entry. */ if (a->archive.state & ARCHIVE_STATE_DATA) r = ((a->format_finish_entry)(a)); /* Finish off the archive. */ + /* TODO: Rename format_finish to format_close, have + * format closers invoke compression close. */ if (a->format_finish != NULL) { r1 = (a->format_finish)(a); if (r1 < r) r = r1; } - /* Release format resources. */ - if (a->format_destroy != NULL) { - r1 = (a->format_destroy)(a); - if (r1 < r) - r = r1; - } - /* Finish the compression and close the stream. */ - if (a->compressor.finish != NULL) { - r1 = (a->compressor.finish)(a); - if (r1 < r) - r = r1; - } - - /* Close out the client stream. */ - if (a->client_closer != NULL) { - r1 = (a->client_closer)(&a->archive, a->client_data); - if (r1 < r) - r = r1; - } + r1 = __archive_write_close_filter(a->filter_first); + if (r1 < r) + r = r1; a->archive.state = ARCHIVE_STATE_CLOSED; return (r); } +void +__archive_write_filters_free(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + int r = ARCHIVE_OK, r1; + + while (a->filter_first != NULL) { + struct archive_write_filter *next + = a->filter_first->next_filter; + if (a->filter_first->free != NULL) { + r1 = (*a->filter_first->free)(a->filter_first); + if (r > r1) + r = r1; + } + free(a->filter_first); + a->filter_first = next; + } + a->filter_last = NULL; +} + /* * Destroy the archive structure. + * + * Be careful: user might just call write_new and then write_free. + * Don't assume we actually wrote anything or performed any non-trivial + * initialization. */ static int _archive_write_free(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; - int r = ARCHIVE_OK; + int r = ARCHIVE_OK, r1; __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_ANY, "archive_write_free"); - if (a->archive.state != ARCHIVE_STATE_CLOSED) + if (a->archive.state != ARCHIVE_STATE_CLOSED + && a->archive.state != ARCHIVE_STATE_FATAL) r = archive_write_close(&a->archive); + /* Release format resources. */ + /* TODO: Rename format_destroy to format_free */ + if (a->format_destroy != NULL) { + r1 = (a->format_destroy)(a); + if (r1 < r) + r = r1; + } + + __archive_write_filters_free(_a); + /* Release various dynamic buffers. */ free((void *)(uintptr_t)(const void *)a->nulls); archive_string_free(&a->archive.error_string); @@ -416,6 +559,10 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) /* In particular, "retry" and "fatal" get returned immediately. */ ret = archive_write_finish_entry(&a->archive); + if (ret == ARCHIVE_FATAL) { + a->archive.state = ARCHIVE_STATE_FATAL; + return (ARCHIVE_FATAL); + } if (ret < ARCHIVE_OK && ret != ARCHIVE_WARN) return (ret); @@ -430,6 +577,10 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) /* Format and write header. */ r2 = ((a->format_write_header)(a, entry)); + if (r2 == ARCHIVE_FATAL) { + a->archive.state = ARCHIVE_STATE_FATAL; + return (ARCHIVE_FATAL); + } if (r2 < ret) ret = r2; @@ -464,3 +615,40 @@ _archive_write_data(struct archive *_a, const void *buff, size_t s) archive_clear_error(&a->archive); return ((a->format_write_data)(a, buff, s)); } + +static struct archive_write_filter * +filter_lookup(struct archive *_a, int n) +{ + struct archive_write *a = (struct archive_write *)_a; + struct archive_write_filter *f = a->filter_first; + if (n == -1) + return a->filter_last; + if (n < 0) + return NULL; + while (n > 0 && f != NULL) { + f = f->next_filter; + --n; + } + return f; +} + +static int +_archive_filter_code(struct archive *_a, int n) +{ + struct archive_write_filter *f = filter_lookup(_a, n); + return f == NULL ? -1 : f->code; +} + +static const char * +_archive_filter_name(struct archive *_a, int n) +{ + struct archive_write_filter *f = filter_lookup(_a, n); + return f == NULL ? NULL : f->name; +} + +static int64_t +_archive_filter_bytes(struct archive *_a, int n) +{ + struct archive_write_filter *f = filter_lookup(_a, n); + return f == NULL ? -1 : f->bytes_written; +} diff --git a/libarchive/archive_write_add_filter_bzip2.c b/libarchive/archive_write_add_filter_bzip2.c new file mode 100644 index 000000000..9bb19082c --- /dev/null +++ b/libarchive/archive_write_add_filter_bzip2.c @@ -0,0 +1,348 @@ +/*- + * 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: head/lib/libarchive/archive_write_set_compression_bzip2.c 201091 2009-12-28 02:22:41Z kientzle $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_BZLIB_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_write_private.h" + +#if ARCHIVE_VERSION_NUMBER < 4000000 +int +archive_write_set_compression_bzip2(struct archive *a) +{ + __archive_write_filters_free(a); + return (archive_write_add_filter_bzip2(a)); +} +#endif + +#ifndef HAVE_BZLIB_H +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; + bz_stream stream; + int64_t total_in; + char *compressed; + size_t compressed_buffer_size; +}; + +/* + * 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 *); +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. + */ +int +archive_write_add_filter_bzip2(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + struct archive_write_filter *f = __archive_write_allocate_filter(_a); + struct private_data *data; + + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2"); + + data = calloc(1, sizeof(*data)); + if (data == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } + data->compression_level = 9; /* default */ + + f->data = data; + f->options = &archive_compressor_bzip2_options; + f->close = &archive_compressor_bzip2_close; + f->free = &archive_compressor_bzip2_free; + f->open = &archive_compressor_bzip2_open; + f->code = ARCHIVE_COMPRESSION_BZIP2; + f->name = "bzip2"; + return (ARCHIVE_OK); +} + +/* + * Setup callback. + */ +static int +archive_compressor_bzip2_open(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; + int ret; + + ret = __archive_write_open_filter(f->next_filter); + if (ret != 0) + return (ret); + + // TODO: Find a better way to size this. (Maybe look at the + // block size expected by the following filter?) + if (data->compressed == NULL) { + data->compressed_buffer_size = 65536; + data->compressed + = (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); + } + } + + memset(&data->stream, 0, sizeof(data->stream)); + data->stream.next_out = data->compressed; + data->stream.avail_out = data->compressed_buffer_size; + f->write = archive_compressor_bzip2_write; + + /* Initialize compression library */ + ret = BZ2_bzCompressInit(&(data->stream), + data->compression_level, 0, 30); + if (ret == BZ_OK) { + f->data = data; + return (ARCHIVE_OK); + } + + /* Library setup failed: clean up. */ + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library"); + + /* Override the error message if we know what really went wrong. */ + switch (ret) { + case BZ_PARAM_ERROR: + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "invalid setup parameter"); + break; + case BZ_MEM_ERROR: + archive_set_error(f->archive, ENOMEM, + "Internal error initializing compression library: " + "out of memory"); + break; + case BZ_CONFIG_ERROR: + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "mis-compiled library"); + break; + } + + return (ARCHIVE_FATAL); + +} + +/* + * 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); + } + + return (ARCHIVE_WARN); +} + +/* + * Write data to the compressed stream. + * + * Returns ARCHIVE_OK if all data written, error otherwise. + */ +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; + + /* Update statistics */ + data->total_in += length; + + /* Compress input data to output buffer */ + SET_NEXT_IN(data, buff); + data->stream.avail_in = length; + if (drive_compressor(f, data, 0)) + return (ARCHIVE_FATAL); + return (ARCHIVE_OK); +} + + +/* + * Finish the compression. + */ +static int +archive_compressor_bzip2_close(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; + ssize_t bytes_written; + int ret; + + if (data == NULL) + return (ARCHIVE_OK); + + /* Finish compression cycle. */ + ret = drive_compressor(f, data, 1); + if (ret == ARCHIVE_OK) { + /* Write the last block */ + bytes_written = __archive_write_filter(f->next_filter, + data->compressed, + data->compressed_buffer_size - data->stream.avail_out); + if (bytes_written <= 0) + ret = ARCHIVE_FATAL; + } + + switch (BZ2_bzCompressEnd(&(data->stream))) { + case BZ_OK: + break; + default: + archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER, + "Failed to clean up compressor"); + ret = ARCHIVE_FATAL; + } + return (ARCHIVE_OK); +} + +static int +archive_compressor_bzip2_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. + * + * Note that this handles both the regular write case (finishing == + * false) and the end-of-archive case (finishing == true). + */ +static int +drive_compressor(struct archive_write_filter *f, + struct private_data *data, int finishing) +{ + ssize_t bytes_written; + int ret; + + for (;;) { + if (data->stream.avail_out == 0) { + bytes_written = __archive_write_filter(f->next_filter, + data->compressed, + data->compressed_buffer_size); + if (bytes_written <= 0) { + /* TODO: Handle this write failure */ + return (ARCHIVE_FATAL); + } else if ((size_t)bytes_written < data->compressed_buffer_size) { + /* Short write: Move remainder to + * front and keep filling */ + memmove(data->compressed, + data->compressed + bytes_written, + data->compressed_buffer_size - bytes_written); + } + + data->stream.next_out = data->compressed + + data->compressed_buffer_size - bytes_written; + data->stream.avail_out = bytes_written; + } + + /* If there's nothing to do, we're done. */ + if (!finishing && data->stream.avail_in == 0) + return (ARCHIVE_OK); + + ret = BZ2_bzCompress(&(data->stream), + finishing ? BZ_FINISH : BZ_RUN); + + switch (ret) { + case BZ_RUN_OK: + /* In non-finishing case, did compressor + * consume everything? */ + if (!finishing && data->stream.avail_in == 0) + return (ARCHIVE_OK); + break; + case BZ_FINISH_OK: /* Finishing: There's more work to do */ + break; + case BZ_STREAM_END: /* Finishing: all done */ + /* Only occurs in finishing case */ + return (ARCHIVE_OK); + default: + /* Any other return value indicates an error */ + archive_set_error(f->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "Bzip2 compression failed;" + " BZ2_bzCompress() returned %d", + ret); + return (ARCHIVE_FATAL); + } + } +} + +#endif /* HAVE_BZLIB_H */ diff --git a/libarchive/archive_write_set_compression_compress.c b/libarchive/archive_write_add_filter_compress.c similarity index 70% rename from libarchive/archive_write_set_compression_compress.c rename to libarchive/archive_write_add_filter_compress.c index 77c73ee5f..e8d6344fd 100644 --- a/libarchive/archive_write_set_compression_compress.c +++ b/libarchive/archive_write_add_filter_compress.c @@ -108,23 +108,35 @@ struct private_data { size_t compressed_offset; }; -static int archive_compressor_compress_finish(struct archive_write *); -static int archive_compressor_compress_init(struct archive_write *); -static int archive_compressor_compress_write(struct archive_write *, +static int archive_compressor_compress_open(struct archive_write_filter *); +static int archive_compressor_compress_write(struct archive_write_filter *, const void *, size_t); +static int archive_compressor_compress_close(struct archive_write_filter *); +static int archive_compressor_compress_free(struct archive_write_filter *); + +#if ARCHIVE_VERSION_NUMBER < 4000000 +int +archive_write_set_compression_compress(struct archive *a) +{ + __archive_write_filters_free(a); + return (archive_write_add_filter_compress(a)); +} +#endif /* - * Allocate, initialize and return a archive object. + * Add a compress filter to this write handle. */ int -archive_write_set_compression_compress(struct archive *_a) +archive_write_add_filter_compress(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; + struct archive_write_filter *f = __archive_write_allocate_filter(_a); + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compression_compress"); - a->compressor.init = &archive_compressor_compress_init; - a->archive.compression_code = ARCHIVE_COMPRESSION_COMPRESS; - a->archive.compression_name = "compress"; + f->open = &archive_compressor_compress_open; + f->code = ARCHIVE_COMPRESSION_COMPRESS; + f->name = "compress"; return (ARCHIVE_OK); } @@ -132,46 +144,38 @@ archive_write_set_compression_compress(struct archive *_a) * Setup callback. */ static int -archive_compressor_compress_init(struct archive_write *a) +archive_compressor_compress_open(struct archive_write_filter *f) { int ret; struct private_data *state; - a->archive.compression_code = ARCHIVE_COMPRESSION_COMPRESS; - a->archive.compression_name = "compress"; - - if (a->bytes_per_block < 4) { - archive_set_error(&a->archive, EINVAL, - "Can't write Compress header as single block"); - return (ARCHIVE_FATAL); - } + f->code = ARCHIVE_COMPRESSION_COMPRESS; + f->name = "compress"; - if (a->client_opener != NULL) { - ret = (a->client_opener)(&a->archive, a->client_data); - if (ret != ARCHIVE_OK) - return (ret); - } + ret = __archive_write_open_filter(f->next_filter); + if (ret != ARCHIVE_OK) + return (ret); - state = (struct private_data *)malloc(sizeof(*state)); + state = (struct private_data *)calloc(1, sizeof(*state)); if (state == NULL) { - archive_set_error(&a->archive, ENOMEM, + archive_set_error(f->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_buffer_size = 65536; state->compressed = malloc(state->compressed_buffer_size); if (state->compressed == NULL) { - archive_set_error(&a->archive, ENOMEM, + archive_set_error(f->archive, ENOMEM, "Can't allocate data for compression buffer"); free(state); return (ARCHIVE_FATAL); } - a->compressor.write = archive_compressor_compress_write; - a->compressor.finish = archive_compressor_compress_finish; + f->write = archive_compressor_compress_write; + f->close = archive_compressor_compress_close; + f->free = archive_compressor_compress_free; state->max_maxcode = 0x10000; /* Should NEVER generate this code. */ state->in_count = 0; /* Length of input. */ @@ -192,7 +196,7 @@ archive_compressor_compress_init(struct archive_write *a) state->compressed[2] = 0x90; /* Block mode, 16bit max */ state->compressed_offset = 3; - a->compressor.data = state; + f->data = state; return (0); } @@ -215,21 +219,19 @@ static unsigned char rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; static int -output_byte(struct archive_write *a, unsigned char c) +output_byte(struct archive_write_filter *f, unsigned char c) { - struct private_data *state = a->compressor.data; + struct private_data *state = f->data; ssize_t bytes_written; state->compressed[state->compressed_offset++] = c; ++state->out_count; if (state->compressed_buffer_size == state->compressed_offset) { - bytes_written = (a->client_writer)(&a->archive, - a->client_data, + bytes_written = __archive_write_filter(f->next_filter, state->compressed, state->compressed_buffer_size); if (bytes_written <= 0) return ARCHIVE_FATAL; - a->archive.raw_position += bytes_written; state->compressed_offset = 0; } @@ -237,9 +239,9 @@ output_byte(struct archive_write *a, unsigned char c) } static int -output_code(struct archive_write *a, int ocode) +output_code(struct archive_write_filter *f, int ocode) { - struct private_data *state = a->compressor.data; + struct private_data *state = f->data; int bits, ret, clear_flg, bit_offset; clear_flg = ocode == CLEAR; @@ -250,13 +252,13 @@ output_code(struct archive_write *a, int ocode) */ bit_offset = state->bit_offset % 8; state->bit_buf |= (ocode << bit_offset) & 0xff; - output_byte(a, state->bit_buf); + output_byte(f, state->bit_buf); bits = state->code_len - (8 - bit_offset); ocode >>= 8 - bit_offset; /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ if (bits >= 8) { - output_byte(a, ocode & 0xff); + output_byte(f, ocode & 0xff); ocode >>= 8; bits -= 8; } @@ -277,7 +279,7 @@ output_code(struct archive_write *a, int ocode) */ if (state->bit_offset > 0) { while (state->bit_offset < state->code_len * 8) { - ret = output_byte(a, state->bit_buf); + ret = output_byte(f, state->bit_buf); if (ret != ARCHIVE_OK) return ret; state->bit_offset += 8; @@ -303,15 +305,15 @@ output_code(struct archive_write *a, int ocode) } static int -output_flush(struct archive_write *a) +output_flush(struct archive_write_filter *f) { - struct private_data *state = a->compressor.data; + struct private_data *state = f->data; int ret; /* At EOF, write the rest of the buffer. */ if (state->bit_offset % 8) { state->code_len = (state->bit_offset % 8 + 7) / 8; - ret = output_byte(a, state->bit_buf); + ret = output_byte(f, state->bit_buf); if (ret != ARCHIVE_OK) return ret; } @@ -323,23 +325,15 @@ output_flush(struct archive_write *a) * Write data to the compressed stream. */ static int -archive_compressor_compress_write(struct archive_write *a, const void *buff, - size_t length) +archive_compressor_compress_write(struct archive_write_filter *f, + const void *buff, size_t length) { - struct private_data *state; + struct private_data *state = (struct private_data *)f->data; int i; int ratio; int c, disp, ret; const unsigned char *bp; - 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); - } - if (length == 0) return ARCHIVE_OK; @@ -368,7 +362,7 @@ archive_compressor_compress_write(struct archive_write *a, const void *buff, disp = 1; else disp = HSIZE - i; - probe: + probe: if ((i -= disp) < 0) i += HSIZE; @@ -378,8 +372,8 @@ archive_compressor_compress_write(struct archive_write *a, const void *buff, } if (state->hashtab[i] >= 0) goto probe; - nomatch: - ret = output_code(a, state->cur_code); + nomatch: + ret = output_code(f, state->cur_code); if (ret != ARCHIVE_OK) return ret; state->cur_code = c; @@ -406,7 +400,7 @@ archive_compressor_compress_write(struct archive_write *a, const void *buff, state->compress_ratio = 0; memset(state->hashtab, 0xff, sizeof(state->hashtab)); state->first_free = FIRST; - ret = output_code(a, CLEAR); + ret = output_code(f, CLEAR); if (ret != ARCHIVE_OK) return ret; } @@ -420,73 +414,30 @@ archive_compressor_compress_write(struct archive_write *a, const void *buff, * Finish the compression... */ static int -archive_compressor_compress_finish(struct archive_write *a) +archive_compressor_compress_close(struct archive_write_filter *f) { - ssize_t block_length, target_block_length, bytes_written; + struct private_data *state = (struct private_data *)f->data; int ret; - struct private_data *state; - size_t tocopy; - - 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."); - ret = ARCHIVE_FATAL; - goto cleanup; - } - - /* By default, always pad the uncompressed data. */ - if (a->pad_uncompressed) { - while (state->in_count % a->bytes_per_block != 0) { - tocopy = a->bytes_per_block - - (state->in_count % a->bytes_per_block); - if (tocopy > a->null_length) - tocopy = a->null_length; - ret = archive_compressor_compress_write(a, a->nulls, - tocopy); - if (ret != ARCHIVE_OK) - goto cleanup; - } - } - ret = output_code(a, state->cur_code); + ret = output_code(f, state->cur_code); if (ret != ARCHIVE_OK) goto cleanup; - ret = output_flush(a); + ret = output_flush(f); if (ret != ARCHIVE_OK) goto cleanup; - /* Optionally, pad the final compressed block. */ - block_length = state->compressed_offset; - - /* Tricky calculation to determine size of last block. */ - 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->compressed + state->compressed_offset, 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); - if (bytes_written <= 0) - ret = ARCHIVE_FATAL; - else - a->archive.raw_position += bytes_written; - + ret = __archive_write_filter(f->next_filter, + state->compressed, state->compressed_offset); cleanup: free(state->compressed); free(state); return (ret); } + +static int +archive_compressor_compress_free(struct archive_write_filter *f) +{ + (void)f; /* UNUSED */ + return (ARCHIVE_OK); +} diff --git a/libarchive/archive_write_add_filter_gzip.c b/libarchive/archive_write_add_filter_gzip.c new file mode 100644 index 000000000..0499bd1f4 --- /dev/null +++ b/libarchive/archive_write_add_filter_gzip.c @@ -0,0 +1,365 @@ +/*- + * 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: head/lib/libarchive/archive_write_set_compression_gzip.c 201081 2009-12-28 02:04:42Z kientzle $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#include +#ifdef HAVE_ZLIB_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_write_private.h" + +#if ARCHIVE_VERSION_NUMBER < 4000000 +int +archive_write_set_compression_gzip(struct archive *a) +{ + __archive_write_filters_free(a); + return (archive_write_add_filter_gzip(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; + z_stream stream; + int64_t total_in; + unsigned char *compressed; + size_t compressed_buffer_size; + unsigned long crc; +}; + +/* + * Yuck. zlib.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 = (Bytef *)(uintptr_t)(const void *)(src) + +static int archive_compressor_gzip_options(struct archive_write_filter *, + const char *, const char *); +static int archive_compressor_gzip_open(struct archive_write_filter *); +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 *); +static int drive_compressor(struct archive_write_filter *, + struct private_data *, int finishing); + + +/* + * Add a gzip compression filter to this write handle. + */ +int +archive_write_add_filter_gzip(struct archive *_a) +{ + struct archive_write *a = (struct archive_write *)_a; + struct archive_write_filter *f = __archive_write_allocate_filter(_a); + struct private_data *data; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip"); + + data = calloc(1, sizeof(*data)); + if (data == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + 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->name = "gzip"; + return (ARCHIVE_OK); +} + +/* + * Setup callback. + */ +static int +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) + return (ret); + + if (data->compressed == NULL) { + data->compressed_buffer_size = 65536; + 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); + } + } + + data->crc = crc32(0L, NULL, 0); + data->stream.next_out = data->compressed; + data->stream.avail_out = 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] = (t)&0xff; /* Timestamp */ + data->compressed[5] = (t>>8)&0xff; + data->compressed[6] = (t>>16)&0xff; + data->compressed[7] = (t>>24)&0xff; + data->compressed[8] = 0; /* No deflate options */ + data->compressed[9] = 3; /* OS=Unix */ + data->stream.next_out += 10; + data->stream.avail_out -= 10; + + f->write = archive_compressor_gzip_write; + + /* Initialize compression library. */ + ret = deflateInit2(&(data->stream), + data->compression_level, + Z_DEFLATED, + -15 /* < 0 to suppress zlib header */, + 8, + Z_DEFAULT_STRATEGY); + + if (ret == Z_OK) { + f->data = data; + return (ARCHIVE_OK); + } + + /* Library setup failed: clean up. */ + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "Internal error " + "initializing compression library"); + + /* Override the error message if we know what really went wrong. */ + switch (ret) { + case Z_STREAM_ERROR: + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing " + "compression library: invalid setup parameter"); + break; + case Z_MEM_ERROR: + archive_set_error(f->archive, ENOMEM, "Internal error initializing " + "compression library"); + break; + case Z_VERSION_ERROR: + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing " + "compression library: invalid library version"); + break; + } + + return (ARCHIVE_FATAL); +} + +/* + * 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); + } + return (ARCHIVE_WARN); +} + +/* + * Write data to the compressed stream. + */ +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; + int ret; + + /* Update statistics */ + data->crc = crc32(data->crc, (const Bytef *)buff, length); + data->total_in += length; + + /* Compress input data to output buffer */ + SET_NEXT_IN(data, buff); + data->stream.avail_in = length; + if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK) + return (ret); + + return (ARCHIVE_OK); +} + +/* + * Finish the compression... + */ +static int +archive_compressor_gzip_close(struct archive_write_filter *f) +{ + unsigned char trailer[8]; + struct private_data *data = (struct private_data *)f->data; + int ret; + + ret = 0; + if (data != NULL) { + /* Finish compression cycle */ + ret = drive_compressor(f, data, 1); + if (ret != ARCHIVE_OK) + goto cleanup; + + /* Write the last compressed data. */ + ret = __archive_write_filter(f->next_filter, + data->compressed, + data->compressed_buffer_size - data->stream.avail_out); + if (ret != ARCHIVE_OK) { + ret = ARCHIVE_FATAL; + goto cleanup; + } + + /* Build and write out 8-byte trailer. */ + trailer[0] = (data->crc)&0xff; + trailer[1] = (data->crc >> 8)&0xff; + trailer[2] = (data->crc >> 16)&0xff; + trailer[3] = (data->crc >> 24)&0xff; + trailer[4] = (data->total_in)&0xff; + trailer[5] = (data->total_in >> 8)&0xff; + trailer[6] = (data->total_in >> 16)&0xff; + trailer[7] = (data->total_in >> 24)&0xff; + ret = __archive_write_filter(f->next_filter, trailer, 8); + if (ret != ARCHIVE_OK) + ret = ARCHIVE_FATAL; + + cleanup: + switch (deflateEnd(&(data->stream))) { + case Z_OK: + break; + default: + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "Failed to clean up compressor"); + ret = ARCHIVE_FATAL; + } + } + return (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. + * + * Note that this handles both the regular write case (finishing == + * false) and the end-of-archive case (finishing == true). + */ +static int +drive_compressor(struct archive_write_filter *f, + struct private_data *data, int finishing) +{ + int ret; + + for (;;) { + if (data->stream.avail_out == 0) { + ret = __archive_write_filter(f->next_filter, + data->compressed, + data->compressed_buffer_size); + if (ret != ARCHIVE_OK) + return (ARCHIVE_FATAL); + data->stream.next_out = data->compressed; + data->stream.avail_out = data->compressed_buffer_size; + } + + /* If there's nothing to do, we're done. */ + if (!finishing && data->stream.avail_in == 0) + return (ARCHIVE_OK); + + ret = deflate(&(data->stream), + finishing ? Z_FINISH : Z_NO_FLUSH ); + + switch (ret) { + case Z_OK: + /* In non-finishing case, check if compressor + * consumed everything */ + if (!finishing && data->stream.avail_in == 0) + return (ARCHIVE_OK); + /* In finishing case, this return always means + * there's more work */ + break; + case Z_STREAM_END: + /* This return can only occur in finishing case. */ + return (ARCHIVE_OK); + default: + /* Any other return value indicates an error. */ + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "GZip compression failed:" + " deflate() call returned status %d", + ret); + return (ARCHIVE_FATAL); + } + } +} + +#endif /* HAVE_ZLIB_H */ diff --git a/libarchive/archive_write_set_compression_none.c b/libarchive/archive_write_add_filter_none.c similarity index 57% rename from libarchive/archive_write_set_compression_none.c rename to libarchive/archive_write_add_filter_none.c index e0216d9e1..80747a4cc 100644 --- a/libarchive/archive_write_set_compression_none.c +++ b/libarchive/archive_write_add_filter_none.c @@ -40,10 +40,20 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_none.c 201 #include "archive_private.h" #include "archive_write_private.h" -static int archive_compressor_none_finish(struct archive_write *a); -static int archive_compressor_none_init(struct archive_write *); -static int archive_compressor_none_write(struct archive_write *, + +int +archive_write_set_compression_none(struct archive *a) +{ + __archive_write_filters_free(a); + return (archive_write_add_filter_none(a)); +} + + +static int archive_compressor_none_open(struct archive_write_filter *); +static int archive_compressor_none_write(struct archive_write_filter *, const void *, size_t); +static int archive_compressor_none_close(struct archive_write_filter *); +static int archive_compressor_none_free(struct archive_write_filter *); struct archive_none { char *buffer; @@ -52,47 +62,51 @@ struct archive_none { ssize_t avail; /* Free space left in buffer */ }; +/* + * TODO: A little refactoring will turn this into a true no-op. + */ int -archive_write_set_compression_none(struct archive *_a) +archive_write_add_filter_none(struct archive *_a) { struct archive_write *a = (struct archive_write *)_a; + struct archive_write_filter *f = __archive_write_allocate_filter(_a); __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, "archive_write_set_compression_none"); - a->compressor.init = &archive_compressor_none_init; - return (0); + f->open = &archive_compressor_none_open; + f->code = ARCHIVE_COMPRESSION_NONE; + f->name = "none"; + + return (ARCHIVE_OK); } /* * Setup callback. */ static int -archive_compressor_none_init(struct archive_write *a) +archive_compressor_none_open(struct archive_write_filter *f) { int ret; struct archive_none *state; - a->archive.compression_code = ARCHIVE_COMPRESSION_NONE; - a->archive.compression_name = "none"; + ret = __archive_write_open_filter(f->next_filter); + if (ret != 0) + return (ret); - if (a->client_opener != NULL) { - ret = (a->client_opener)(&a->archive, a->client_data); - if (ret != 0) - return (ret); - } + f->bytes_per_block = archive_write_get_bytes_per_block(f->archive); + f->bytes_in_last_block = archive_write_get_bytes_in_last_block(f->archive); - state = (struct archive_none *)malloc(sizeof(*state)); + state = (struct archive_none *)calloc(1, sizeof(*state)); if (state == NULL) { - archive_set_error(&a->archive, ENOMEM, + archive_set_error(f->archive, ENOMEM, "Can't allocate data for output buffering"); return (ARCHIVE_FATAL); } - memset(state, 0, sizeof(*state)); - state->buffer_size = a->bytes_per_block; + state->buffer_size = f->bytes_per_block; if (state->buffer_size != 0) { state->buffer = (char *)malloc(state->buffer_size); if (state->buffer == NULL) { - archive_set_error(&a->archive, ENOMEM, + archive_set_error(f->archive, ENOMEM, "Can't allocate output buffer"); free(state); return (ARCHIVE_FATAL); @@ -102,9 +116,10 @@ archive_compressor_none_init(struct archive_write *a) state->next = state->buffer; state->avail = state->buffer_size; - a->compressor.data = state; - a->compressor.write = archive_compressor_none_write; - a->compressor.finish = archive_compressor_none_finish; + f->data = state; + f->write = archive_compressor_none_write; + f->close = archive_compressor_none_close; + f->free = archive_compressor_none_free; return (ARCHIVE_OK); } @@ -112,24 +127,13 @@ archive_compressor_none_init(struct archive_write *a) * Write data to the stream. */ static int -archive_compressor_none_write(struct archive_write *a, const void *vbuff, - size_t length) +archive_compressor_none_write(struct archive_write_filter *f, + const void *vbuff, size_t length) { + struct archive_none *state = (struct archive_none *)f->data; const char *buff; ssize_t remaining, to_copy; - ssize_t bytes_written; - struct archive_none *state; - - state = (struct archive_none *)a->compressor.data; - buff = (const char *)vbuff; - 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); - } - - remaining = length; + int ret; /* * If there is no buffer for blocking, just pass the data @@ -137,19 +141,12 @@ archive_compressor_none_write(struct archive_write *a, const void *vbuff, * particular, this supports "no write delay" operation for * special applications. Just set the block size to zero. */ - if (state->buffer_size == 0) { - while (remaining > 0) { - bytes_written = (a->client_writer)(&a->archive, - a->client_data, buff, remaining); - if (bytes_written <= 0) - return (ARCHIVE_FATAL); - a->archive.raw_position += bytes_written; - remaining -= bytes_written; - buff += bytes_written; - } - a->archive.file_position += length; - return (ARCHIVE_OK); - } + if (state->buffer_size == 0) + return (__archive_write_filter(f->next_filter, + vbuff, length)); + + buff = (const char *)vbuff; + remaining = length; /* If the copy buffer isn't empty, try to fill it. */ if (state->avail < state->buffer_size) { @@ -164,26 +161,23 @@ archive_compressor_none_write(struct archive_write *a, const void *vbuff, remaining -= to_copy; /* ... if it's full, write it out. */ if (state->avail == 0) { - bytes_written = (a->client_writer)(&a->archive, - a->client_data, state->buffer, state->buffer_size); - if (bytes_written <= 0) + ret = __archive_write_filter(f->next_filter, + state->buffer, state->buffer_size); + if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); - /* XXX TODO: if bytes_written < state->buffer_size */ - a->archive.raw_position += bytes_written; state->next = state->buffer; state->avail = state->buffer_size; } } + /* Write out full blocks directly to client. */ while (remaining > state->buffer_size) { - /* Write out full blocks directly to client. */ - bytes_written = (a->client_writer)(&a->archive, - a->client_data, buff, state->buffer_size); - if (bytes_written <= 0) + ret = __archive_write_filter(f->next_filter, + buff, state->buffer_size); + if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); - a->archive.raw_position += bytes_written; - buff += bytes_written; - remaining -= bytes_written; + buff += state->buffer_size; + remaining -= state->buffer_size; } if (remaining > 0) { @@ -193,7 +187,6 @@ archive_compressor_none_write(struct archive_write *a, const void *vbuff, state->avail -= remaining; } - a->archive.file_position += length; return (ARCHIVE_OK); } @@ -202,56 +195,49 @@ archive_compressor_none_write(struct archive_write *a, const void *vbuff, * Finish the compression. */ static int -archive_compressor_none_finish(struct archive_write *a) +archive_compressor_none_close(struct archive_write_filter *f) { + struct archive_none *state = (struct archive_none *)f->data; ssize_t block_length; ssize_t target_block_length; - ssize_t bytes_written; int ret; - struct archive_none *state; - state = (struct archive_none *)a->compressor.data; ret = ARCHIVE_OK; - 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); - } /* If there's pending data, pad and write the last block */ if (state->next != state->buffer) { block_length = state->buffer_size - state->avail; /* Tricky calculation to determine size of last block */ - if (a->bytes_in_last_block <= 0) + if (f->bytes_in_last_block <= 0) /* Default or Zero: pad to full block */ - target_block_length = a->bytes_per_block; + target_block_length = f->bytes_per_block; else /* Round 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; + target_block_length = f->bytes_in_last_block * + ( (block_length + f->bytes_in_last_block - 1) / + f->bytes_in_last_block); + if (target_block_length > f->bytes_per_block) + target_block_length = f->bytes_per_block; if (block_length < target_block_length) { memset(state->next, 0, target_block_length - block_length); block_length = target_block_length; } - bytes_written = (a->client_writer)(&a->archive, - a->client_data, state->buffer, block_length); - if (bytes_written <= 0) - ret = ARCHIVE_FATAL; - else { - a->archive.raw_position += bytes_written; - ret = ARCHIVE_OK; - } + ret = __archive_write_filter(f->next_filter, + state->buffer, block_length); } if (state->buffer) free(state->buffer); free(state); - a->compressor.data = NULL; + f->data = NULL; return (ret); } + +static int +archive_compressor_none_free(struct archive_write_filter *f) +{ + (void)f; /* UNUSED */ + return (ARCHIVE_OK); +} diff --git a/libarchive/archive_write_add_filter_program.c b/libarchive/archive_write_add_filter_program.c new file mode 100644 index 000000000..0efeb7c48 --- /dev/null +++ b/libarchive/archive_write_add_filter_program.c @@ -0,0 +1,326 @@ +/*- + * Copyright (c) 2007 Joerg Sonnenberger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" +__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_program.c 201104 2009-12-28 03:14:30Z kientzle $"); + +#ifdef HAVE_SYS_WAIT_H +# include +#endif +#ifdef HAVE_ERRNO_H +# include +#endif +#ifdef HAVE_FCNTL_H +# include +#endif +#ifdef HAVE_STDLIB_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_write_private.h" + +#if ARCHIVE_VERSION_NUMBER < 4000000 +int +archive_write_set_compression_program(struct archive *a, const char *cmd) +{ + __archive_write_filters_free(a); + return (archive_write_add_filter_program(a, cmd)); +} +#endif + +/* 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); +} + +#else + +#include "filter_fork.h" + +struct private_data { + char *cmd; + char *description; + pid_t child; + int child_stdin, child_stdout; + + char *child_buf; + size_t child_buf_len, child_buf_avail; +}; + +static int archive_compressor_program_open(struct archive_write_filter *); +static int archive_compressor_program_write(struct archive_write_filter *, + const void *, size_t); +static int archive_compressor_program_close(struct archive_write_filter *); +static int archive_compressor_program_free(struct archive_write_filter *); + +/* + * Add a filter to this write handle that passes all data through an + * external program. + */ +int +archive_write_add_filter_program(struct archive *_a, const char *cmd) +{ + struct archive_write_filter *f = __archive_write_allocate_filter(_a); + struct 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_STATE_NEW, "archive_write_set_compression_program"); + data = calloc(1, sizeof(*data)); + if (data == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } + 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; + return (ARCHIVE_OK); +} + +/* + * Setup callback. + */ +static int +archive_compressor_program_open(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; + int ret; + + ret = __archive_write_open_filter(f->next_filter); + if (ret != ARCHIVE_OK) + return (ret); + + if (data->child_buf == NULL) { + data->child_buf_len = 65536; + data->child_buf_avail = 0; + data->child_buf = malloc(data->child_buf_len); + + if (data->child_buf == NULL) { + archive_set_error(f->archive, ENOMEM, + "Can't allocate compression buffer"); + return (ARCHIVE_FATAL); + } + } + + if ((data->child = __archive_create_child(data->cmd, + &data->child_stdin, &data->child_stdout)) == -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); +} + +static ssize_t +child_write(struct archive_write_filter *f, const char *buf, size_t buf_len) +{ + struct private_data *data = f->data; + ssize_t ret; + + if (data->child_stdin == -1) + return (-1); + + if (buf_len == 0) + return (-1); + +restart_write: + do { + ret = write(data->child_stdin, buf, buf_len); + } while (ret == -1 && errno == EINTR); + + if (ret > 0) + return (ret); + if (ret == 0) { + close(data->child_stdin); + data->child_stdin = -1; + fcntl(data->child_stdout, F_SETFL, 0); + return (0); + } + if (ret == -1 && errno != EAGAIN) + return (-1); + + if (data->child_stdout == -1) { + fcntl(data->child_stdin, F_SETFL, 0); + __archive_check_child(data->child_stdin, data->child_stdout); + goto restart_write; + } + + do { + ret = read(data->child_stdout, + data->child_buf + data->child_buf_avail, + data->child_buf_len - data->child_buf_avail); + } while (ret == -1 && errno == EINTR); + + if (ret == 0 || (ret == -1 && errno == EPIPE)) { + close(data->child_stdout); + data->child_stdout = -1; + fcntl(data->child_stdin, F_SETFL, 0); + goto restart_write; + } + if (ret == -1 && errno == EAGAIN) { + __archive_check_child(data->child_stdin, data->child_stdout); + goto restart_write; + } + if (ret == -1) + return (-1); + + data->child_buf_avail += ret; + + ret = __archive_write_filter(f->next_filter, + data->child_buf, data->child_buf_avail); + if (ret <= 0) + 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; + goto restart_write; +} + +/* + * Write data to the compressed stream. + */ +static int +archive_compressor_program_write(struct archive_write_filter *f, + const void *buff, size_t length) +{ + ssize_t ret; + const char *buf; + + buf = buff; + while (length > 0) { + ret = child_write(f, buf, length); + if (ret == -1 || ret == 0) { + archive_set_error(f->archive, EIO, + "Can't write to filter"); + return (ARCHIVE_FATAL); + } + length -= ret; + buf += ret; + } + return (ARCHIVE_OK); +} + + +/* + * Finish the compression... + */ +static int +archive_compressor_program_close(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; + int ret, status; + ssize_t bytes_read; + + ret = 0; + close(data->child_stdin); + data->child_stdin = -1; + fcntl(data->child_stdout, F_SETFL, 0); + + for (;;) { + do { + bytes_read = read(data->child_stdout, + data->child_buf + data->child_buf_avail, + data->child_buf_len - data->child_buf_avail); + } while (bytes_read == -1 && errno == EINTR); + + if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE)) + break; + + if (bytes_read == -1) { + archive_set_error(f->archive, errno, + "Read from filter failed unexpectedly."); + ret = ARCHIVE_FATAL; + goto cleanup; + } + data->child_buf_avail += bytes_read; + + ret = __archive_write_filter(f->next_filter, + data->child_buf, data->child_buf_avail); + if (ret != ARCHIVE_OK) { + ret = ARCHIVE_FATAL; + goto cleanup; + } + data->child_buf_avail = 0; + } + +cleanup: + /* Shut down the child. */ + if (data->child_stdin != -1) + close(data->child_stdin); + if (data->child_stdout != -1) + close(data->child_stdout); + while (waitpid(data->child, &status, 0) == -1 && errno == EINTR) + continue; + + if (status != 0) { + archive_set_error(f->archive, EIO, + "Filter exited with failure."); + ret = ARCHIVE_FATAL; + } + return (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_xz.c b/libarchive/archive_write_add_filter_xz.c new file mode 100644 index 000000000..a54d3a4ca --- /dev/null +++ b/libarchive/archive_write_add_filter_xz.c @@ -0,0 +1,371 @@ +/*- + * Copyright (c) 2009 Michihiro NAKAJIMA + * 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. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" + +__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_xz.c 201108 2009-12-28 03:28:21Z kientzle $"); + +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#include +#ifdef HAVE_LZMA_H +#include +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_write_private.h" + +#if ARCHIVE_VERSION_NUMBER < 4000000 +int +archive_write_set_compression_lzma(struct archive *a) +{ + __archive_write_filters_free(a); + return (archive_write_add_filter_lzma(a)); +} + +int +archive_write_set_compression_xz(struct archive *a) +{ + __archive_write_filters_free(a); + return (archive_write_add_filter_xz(a)); +} +#endif + +#ifndef HAVE_LZMA_H +int +archive_write_add_filter_xz(struct archive *a) +{ + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "xz compression not supported on this platform"); + return (ARCHIVE_FATAL); +} + +int +archive_write_add_filter_lzma(struct archive *a) +{ + archive_set_error(a, ARCHIVE_ERRNO_MISC, + "lzma compression not supported on this platform"); + return (ARCHIVE_FATAL); +} +#else +/* Don't compile this if we don't have liblzma. */ + +struct private_data { + int compression_level; + lzma_stream stream; + lzma_filter lzmafilters[2]; + lzma_options_lzma lzma_opt; + int64_t total_in; + unsigned char *compressed; + size_t compressed_buffer_size; +}; + +static int archive_compressor_xz_options(struct archive_write_filter *, + const char *, const char *); +static int archive_compressor_xz_open(struct archive_write_filter *); +static int archive_compressor_xz_write(struct archive_write_filter *, + const void *, size_t); +static int archive_compressor_xz_close(struct archive_write_filter *); +static int archive_compressor_xz_free(struct archive_write_filter *); +static int drive_compressor(struct archive_write_filter *, + struct private_data *, int finishing); + + +static int +common_setup(struct archive_write_filter *f) +{ + struct private_data *data; + struct archive_write *a = (struct archive_write *)f->archive; + __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, + ARCHIVE_STATE_NEW, "archive_write_add_filter_xz"); + data = calloc(1, sizeof(*data)); + if (data == NULL) { + archive_set_error(&a->archive, ENOMEM, "Out of memory"); + return (ARCHIVE_FATAL); + } + f->data = data; + data->compression_level = LZMA_PRESET_DEFAULT; + f->open = &archive_compressor_xz_open; + f->close = archive_compressor_xz_close; + f->free = archive_compressor_xz_free; + f->options = &archive_compressor_xz_options; + return (ARCHIVE_OK); +} + +/* + * Add an xz compression filter to this write handle. + */ +int +archive_write_add_filter_xz(struct archive *_a) +{ + struct archive_write_filter *f = __archive_write_allocate_filter(_a); + int r = common_setup(f); + if (r == ARCHIVE_OK) { + f->code = ARCHIVE_COMPRESSION_XZ; + f->name = "xz"; + } + return (r); +} + +/* LZMA is handled identically, we just need a different compression + * code set. (The liblzma setup looks at the code to determine + * the one place that XZ and LZMA require different handling.) */ +int +archive_write_add_filter_lzma(struct archive *_a) +{ + struct archive_write_filter *f = __archive_write_allocate_filter(_a); + int r = common_setup(f); + if (r == ARCHIVE_OK) { + f->code = ARCHIVE_COMPRESSION_LZMA; + f->name = "lzma"; + } + return (r); +} + +static int +archive_compressor_xz_init_stream(struct archive_write_filter *f, + struct private_data *data) +{ + int ret; + + data->stream = (lzma_stream)LZMA_STREAM_INIT; + data->stream.next_out = data->compressed; + data->stream.avail_out = data->compressed_buffer_size; + if (f->code == ARCHIVE_COMPRESSION_XZ) + ret = lzma_stream_encoder(&(data->stream), + data->lzmafilters, LZMA_CHECK_CRC64); + else + ret = lzma_alone_encoder(&(data->stream), &data->lzma_opt); + if (ret == LZMA_OK) + return (ARCHIVE_OK); + + switch (ret) { + case LZMA_MEM_ERROR: + archive_set_error(f->archive, ENOMEM, + "Internal error initializing compression library: " + "Cannot allocate memory"); + break; + default: + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "It's a bug in liblzma"); + break; + } + return (ARCHIVE_FATAL); +} + +/* + * Setup callback. + */ +static int +archive_compressor_xz_open(struct archive_write_filter *f) +{ + struct private_data *data = f->data; + int ret; + + ret = __archive_write_open_filter(f->next_filter); + if (ret != ARCHIVE_OK) + return (ret); + + if (data->compressed == NULL) { + data->compressed_buffer_size = 65536; + 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); + } + } + + f->write = archive_compressor_xz_write; + + /* Initialize compression library. */ + if (lzma_lzma_preset(&data->lzma_opt, data->compression_level)) { + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library"); + } + data->lzmafilters[0].id = LZMA_FILTER_LZMA2; + data->lzmafilters[0].options = &data->lzma_opt; + data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */ + ret = archive_compressor_xz_init_stream(f, data); + if (ret == LZMA_OK) { + f->data = data; + return (0); + } + return (ARCHIVE_FATAL); +} + +/* + * Set write options. + */ +static int +archive_compressor_xz_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'; + if (data->compression_level > 6) + data->compression_level = 6; + return (ARCHIVE_OK); + } + + return (ARCHIVE_WARN); +} + +/* + * Write data to the compressed stream. + */ +static int +archive_compressor_xz_write(struct archive_write_filter *f, + const void *buff, size_t length) +{ + struct private_data *data = (struct private_data *)f->data; + int ret; + + /* Update statistics */ + data->total_in += length; + + /* Compress input data to output buffer */ + data->stream.next_in = buff; + data->stream.avail_in = length; + if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK) + return (ret); + + return (ARCHIVE_OK); +} + + +/* + * Finish the compression... + */ +static int +archive_compressor_xz_close(struct archive_write_filter *f) +{ + struct private_data *data = (struct private_data *)f->data; + int ret; + + if (data == NULL) + return (ARCHIVE_OK); + ret = drive_compressor(f, data, 1); + if (ret == ARCHIVE_OK) { + ret = __archive_write_filter(f->next_filter, + data->compressed, + data->compressed_buffer_size - data->stream.avail_out); + } + lzma_end(&(data->stream)); + return (ret); +} + +static int +archive_compressor_xz_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. + * + * Note that this handles both the regular write case (finishing == + * false) and the end-of-archive case (finishing == true). + */ +static int +drive_compressor(struct archive_write_filter *f, + struct private_data *data, int finishing) +{ + int ret; + + for (;;) { + if (data->stream.avail_out == 0) { + ret = __archive_write_filter(f->next_filter, + data->compressed, + data->compressed_buffer_size); + if (ret != ARCHIVE_OK) + return (ARCHIVE_FATAL); + data->stream.next_out = data->compressed; + data->stream.avail_out = data->compressed_buffer_size; + } + + /* If there's nothing to do, we're done. */ + if (!finishing && data->stream.avail_in == 0) + return (ARCHIVE_OK); + + ret = lzma_code(&(data->stream), + finishing ? LZMA_FINISH : LZMA_RUN ); + + switch (ret) { + case LZMA_OK: + /* In non-finishing case, check if compressor + * consumed everything */ + if (!finishing && data->stream.avail_in == 0) + return (ARCHIVE_OK); + /* In finishing case, this return always means + * there's more work */ + break; + case LZMA_STREAM_END: + /* This return can only occur in finishing case. */ + if (finishing) + return (ARCHIVE_OK); + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "lzma compression data error"); + return (ARCHIVE_FATAL); + case LZMA_MEMLIMIT_ERROR: + archive_set_error(f->archive, ENOMEM, + "lzma compression error: " + "%ju MiB would have been needed", + (lzma_memusage(&(data->stream)) + 1024 * 1024 -1) + / (1024 * 1024)); + return (ARCHIVE_FATAL); + default: + /* Any other return value indicates an error. */ + archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, + "lzma compression failed:" + " lzma_code() call returned status %d", + ret); + return (ARCHIVE_FATAL); + } + } +} + +#endif /* HAVE_LZMA_H */ diff --git a/libarchive/archive_write_disk.c b/libarchive/archive_write_disk.c index f734037cb..ee3ad66ff 100644 --- a/libarchive/archive_write_disk.c +++ b/libarchive/archive_write_disk.c @@ -192,11 +192,13 @@ struct archive_write_disk { /* Handle for the file we're restoring. */ int fd; /* Current offset for writing data to the file. */ - off_t offset; + int64_t offset; /* Last offset actually written to disk. */ - off_t fd_offset; + int64_t fd_offset; + /* Total bytes actually written to files. */ + int64_t total_bytes_written; /* Maximum size of file, -1 if unknown. */ - off_t filesize; + int64_t filesize; /* Dir we were in before this restore; only for deep paths. */ int restore_pwd; /* Mode we should use for this entry; affected by _PERM and umask. */ @@ -254,15 +256,16 @@ static ssize_t write_data_block(struct archive_write_disk *, static struct archive_vtable *archive_write_disk_vtable(void); -static int _archive_write_close(struct archive *); -static int _archive_write_free(struct archive *); -static int _archive_write_header(struct archive *, struct archive_entry *); -static int _archive_write_finish_entry(struct archive *); -static ssize_t _archive_write_data(struct archive *, const void *, size_t); -static ssize_t _archive_write_data_block(struct archive *, const void *, size_t, off_t); +static int _archive_write_disk_close(struct archive *); +static int _archive_write_disk_free(struct archive *); +static int _archive_write_disk_header(struct archive *, struct archive_entry *); +static int64_t _archive_write_disk_filter_bytes(struct archive *, int); +static int _archive_write_disk_finish_entry(struct archive *); +static ssize_t _archive_write_disk_data(struct archive *, const void *, size_t); +static ssize_t _archive_write_disk_data_block(struct archive *, const void *, size_t, off_t); static int -_archive_write_disk_lazy_stat(struct archive_write_disk *a) +lazy_stat(struct archive_write_disk *a) { if (a->pst != NULL) { /* Already have stat() data available. */ @@ -293,16 +296,29 @@ archive_write_disk_vtable(void) static int inited = 0; if (!inited) { - av.archive_close = _archive_write_close; - av.archive_free = _archive_write_free; - av.archive_write_header = _archive_write_header; - av.archive_write_finish_entry = _archive_write_finish_entry; - av.archive_write_data = _archive_write_data; - av.archive_write_data_block = _archive_write_data_block; + av.archive_close = _archive_write_disk_close; + av.archive_filter_bytes = _archive_write_disk_filter_bytes; + av.archive_free = _archive_write_disk_free; + av.archive_write_header = _archive_write_disk_header; + av.archive_write_finish_entry + = _archive_write_disk_finish_entry; + av.archive_write_data = _archive_write_disk_data; + av.archive_write_data_block = _archive_write_disk_data_block; } return (&av); } +static int64_t +_archive_write_disk_filter_bytes(struct archive *_a, int n) +{ + (void)n; /* UNUSED */ + struct archive_write_disk *a = (struct archive_write_disk *)_a; +// fprintf(stderr, "archive_write_disk_filter_bytes(%d)=%d\n", n, (int)a->fd_offset); + if (n == -1 || n == 0) + return (a->total_bytes_written); + return (-1); +} + int archive_write_disk_set_options(struct archive *_a, int flags) @@ -326,7 +342,7 @@ archive_write_disk_set_options(struct archive *_a, int flags) * */ static int -_archive_write_header(struct archive *_a, struct archive_entry *entry) +_archive_write_disk_header(struct archive *_a, struct archive_entry *entry) { struct archive_write_disk *a = (struct archive_write_disk *)_a; struct fixup_entry *fe; @@ -337,7 +353,7 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry) "archive_write_disk_header"); archive_clear_error(&a->archive); if (a->archive.state & ARCHIVE_STATE_DATA) { - r = _archive_write_finish_entry(&a->archive); + r = _archive_write_disk_finish_entry(&a->archive); if (r == ARCHIVE_FATAL) return (r); } @@ -591,7 +607,7 @@ write_data_block(struct archive_write_disk *a, const char *buff, size_t size) if (a->flags & ARCHIVE_EXTRACT_SPARSE) { #if HAVE_STRUCT_STAT_ST_BLKSIZE int r; - if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) + if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); block_size = a->pst->st_blksize; #else @@ -602,7 +618,7 @@ write_data_block(struct archive_write_disk *a, const char *buff, size_t size) } /* If this write would run beyond the file size, truncate it. */ - if (a->filesize >= 0 && (off_t)(a->offset + size) > a->filesize) + if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize) start_size = size = (size_t)(a->filesize - a->offset); /* Write the data. */ @@ -612,7 +628,7 @@ write_data_block(struct archive_write_disk *a, const char *buff, size_t size) } else { /* We're sparsifying the file. */ const char *p, *end; - off_t block_end; + int64_t block_end; /* Skip leading zero bytes. */ for (p = buff, end = buff + size; p < end; ++p) { @@ -643,8 +659,6 @@ write_data_block(struct archive_write_disk *a, const char *buff, size_t size) return (ARCHIVE_FATAL); } a->fd_offset = a->offset; - a->archive.file_position = a->offset; - a->archive.raw_position = a->offset; } bytes_written = write(a->fd, buff, bytes_to_write); if (bytes_written < 0) { @@ -653,23 +667,22 @@ write_data_block(struct archive_write_disk *a, const char *buff, size_t size) } buff += bytes_written; size -= bytes_written; + a->total_bytes_written += bytes_written; a->offset += bytes_written; - a->archive.file_position += bytes_written; - a->archive.raw_position += bytes_written; a->fd_offset = a->offset; } return (start_size - size); } static ssize_t -_archive_write_data_block(struct archive *_a, +_archive_write_disk_data_block(struct archive *_a, const void *buff, size_t size, off_t offset) { struct archive_write_disk *a = (struct archive_write_disk *)_a; ssize_t r; __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, - ARCHIVE_STATE_DATA, "archive_write_disk_block"); + ARCHIVE_STATE_DATA, "archive_write_data_block"); a->offset = offset; r = write_data_block(a, buff, size); @@ -684,7 +697,7 @@ _archive_write_data_block(struct archive *_a, } static ssize_t -_archive_write_data(struct archive *_a, const void *buff, size_t size) +_archive_write_disk_data(struct archive *_a, const void *buff, size_t size) { struct archive_write_disk *a = (struct archive_write_disk *)_a; @@ -695,7 +708,7 @@ _archive_write_data(struct archive *_a, const void *buff, size_t size) } static int -_archive_write_finish_entry(struct archive *_a) +_archive_write_disk_finish_entry(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; int ret = ARCHIVE_OK; @@ -730,7 +743,7 @@ _archive_write_finish_entry(struct archive *_a) * to see what happened. */ a->pst = NULL; - if ((ret = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) + if ((ret = lazy_stat(a)) != ARCHIVE_OK) return (ret); /* We can use lseek()/write() to extend the file if * ftruncate didn't work or isn't available. */ @@ -1257,7 +1270,7 @@ create_filesystem_object(struct archive_write_disk *a) * reason we set directory perms here. XXX */ static int -_archive_write_close(struct archive *_a) +_archive_write_disk_close(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; struct fixup_entry *next, *p; @@ -1266,7 +1279,7 @@ _archive_write_close(struct archive *_a) __archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, "archive_write_disk_close"); - ret = _archive_write_finish_entry(&a->archive); + ret = _archive_write_disk_finish_entry(&a->archive); /* Sort dir list so directories are fixed up in depth-first order. */ p = sort_dir_list(a->fixup_list); @@ -1325,11 +1338,11 @@ _archive_write_close(struct archive *_a) } static int -_archive_write_free(struct archive *_a) +_archive_write_disk_free(struct archive *_a) { struct archive_write_disk *a = (struct archive_write_disk *)_a; int ret; - ret = _archive_write_close(&a->archive); + ret = _archive_write_disk_close(&a->archive); if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL) (a->cleanup_gid)(a->lookup_gid_data); if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL) @@ -2031,7 +2044,7 @@ set_mode(struct archive_write_disk *a, int mode) * process, since systems sometimes set GID from * the enclosing dir or based on ACLs. */ - if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) + if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); if (a->pst->st_gid != a->gid) { mode &= ~ S_ISGID; @@ -2216,7 +2229,7 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, * about the correct approach if we're overwriting an existing * file that already has flags on it. XXX */ - if ((r = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK) + if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); a->st.st_flags &= ~clear; diff --git a/libarchive/archive_write_private.h b/libarchive/archive_write_private.h index f9b656555..ae5adfc09 100644 --- a/libarchive/archive_write_private.h +++ b/libarchive/archive_write_private.h @@ -36,6 +36,36 @@ #include "archive_string.h" #include "archive_private.h" +struct archive_write; + +struct archive_write_filter { + int64_t bytes_written; + struct archive *archive; /* Associated archive. */ + struct archive_write_filter *next_filter; /* Who I write to. */ + int (*options)(struct archive_write_filter *, + const char *key, const char *value); + int (*open)(struct archive_write_filter *); + int (*write)(struct archive_write_filter *, const void *, size_t); + int (*close)(struct archive_write_filter *); + int (*free)(struct archive_write_filter *); + void *data; + const char *name; + int code; + int bytes_per_block; + int bytes_in_last_block; +}; + +#if ARCHIVE_VERSION < 4000000 +void __archive_write_filters_free(struct archive *); +#endif + +struct archive_write_filter *__archive_write_allocate_filter(struct archive *); + +int __archive_write_output(struct archive_write *, const void *, size_t); +int __archive_write_filter(struct archive_write_filter *, const void *, size_t); +int __archive_write_open_filter(struct archive_write_filter *); +int __archive_write_close_filter(struct archive_write_filter *); + struct archive_write { struct archive archive; @@ -71,21 +101,12 @@ struct archive_write { * effect on compression "none." */ int pad_uncompressed; - int pad_uncompressed_byte; /* TODO: Support this. */ /* - * On write, the client just invokes an archive_write_set function - * which sets up the data here directly. + * First and last write filters in the pipeline. */ - struct { - void *data; - void *config; - int (*init)(struct archive_write *); - int (*options)(struct archive_write *, - const char *key, const char *value); - int (*finish)(struct archive_write *); - int (*write)(struct archive_write *, const void *, size_t); - } compressor; + struct archive_write_filter *filter_first; + struct archive_write_filter *filter_last; /* * Pointers to format-specific functions for writing. They're diff --git a/libarchive/archive_write_set_compression_bzip2.c b/libarchive/archive_write_set_compression_bzip2.c deleted file mode 100644 index beb2379e5..000000000 --- a/libarchive/archive_write_set_compression_bzip2.c +++ /dev/null @@ -1,408 +0,0 @@ -/*- - * 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: head/lib/libarchive/archive_write_set_compression_bzip2.c 201091 2009-12-28 02:22:41Z kientzle $"); - -#ifdef HAVE_ERRNO_H -#include -#endif -#include -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#ifdef HAVE_BZLIB_H -#include -#endif - -#include "archive.h" -#include "archive_private.h" -#include "archive_write_private.h" - -#ifndef HAVE_BZLIB_H -int -archive_write_set_compression_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 { - bz_stream stream; - int64_t total_in; - char *compressed; - size_t compressed_buffer_size; -}; - -struct private_config { - int compression_level; -}; - -/* - * 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_finish(struct archive_write *); -static int archive_compressor_bzip2_init(struct archive_write *); -static int archive_compressor_bzip2_options(struct archive_write *, - const char *, const char *); -static int archive_compressor_bzip2_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_bzip2(struct archive *_a) -{ - struct archive_write *a = (struct archive_write *)_a; - struct private_config *config; - __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, - ARCHIVE_STATE_NEW, "archive_write_set_compression_bzip2"); - config = malloc(sizeof(*config)); - if (config == NULL) { - archive_set_error(&a->archive, ENOMEM, "Out of memory"); - return (ARCHIVE_FATAL); - } - a->compressor.config = config; - a->compressor.finish = archive_compressor_bzip2_finish; - config->compression_level = 9; /* default */ - a->compressor.init = &archive_compressor_bzip2_init; - a->compressor.options = &archive_compressor_bzip2_options; - a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2; - a->archive.compression_name = "bzip2"; - return (ARCHIVE_OK); -} - -/* - * Setup callback. - */ -static int -archive_compressor_bzip2_init(struct archive_write *a) -{ - int ret; - struct private_data *state; - struct private_config *config; - - config = (struct private_config *)a->compressor.config; - 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_bzip2_write; - - /* Initialize compression library */ - ret = BZ2_bzCompressInit(&(state->stream), - config->compression_level, 0, 30); - if (ret == BZ_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 BZ_PARAM_ERROR: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing compression library: " - "invalid setup parameter"); - break; - case BZ_MEM_ERROR: - archive_set_error(&a->archive, ENOMEM, - "Internal error initializing compression library: " - "out of memory"); - break; - case BZ_CONFIG_ERROR: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing compression library: " - "mis-compiled library"); - break; - } - - return (ARCHIVE_FATAL); - -} - -/* - * Set write options. - */ -static int -archive_compressor_bzip2_options(struct archive_write *a, const char *key, - const char *value) -{ - struct private_config *config; - - config = (struct private_config *)a->compressor.config; - if (strcmp(key, "compression-level") == 0) { - if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || - value[1] != '\0') - return (ARCHIVE_WARN); - config->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 (config->compression_level < 1) - config->compression_level = 1; - return (ARCHIVE_OK); - } - - return (ARCHIVE_WARN); -} - -/* - * Write data to the compressed stream. - * - * Returns ARCHIVE_OK if all data written, error otherwise. - */ -static int -archive_compressor_bzip2_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 */ - SET_NEXT_IN(state, 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_bzip2_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; - - ret = ARCHIVE_OK; - state = (struct private_data *)a->compressor.data; - if (state != NULL) { - 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) { - SET_NEXT_IN(state, 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 = state->stream.next_out - state->compressed; - - /* Tricky calculation to determine size of last block. */ - 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: - switch (BZ2_bzCompressEnd(&(state->stream))) { - case BZ_OK: - break; - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, - "Failed to clean up compressor"); - ret = ARCHIVE_FATAL; - } - - free(state->compressed); - free(state); - } - /* Free configuration data even if we were never fully initialized. */ - free(a->compressor.config); - a->compressor.config = NULL; - 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 = BZ2_bzCompress(&(state->stream), - finishing ? BZ_FINISH : BZ_RUN); - - switch (ret) { - case BZ_RUN_OK: - /* In non-finishing case, did compressor - * consume everything? */ - if (!finishing && state->stream.avail_in == 0) - return (ARCHIVE_OK); - break; - case BZ_FINISH_OK: /* Finishing: There's more work to do */ - break; - case BZ_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, - "Bzip2 compression failed;" - " BZ2_bzCompress() returned %d", - ret); - return (ARCHIVE_FATAL); - } - } -} - -#endif /* HAVE_BZLIB_H */ diff --git a/libarchive/archive_write_set_compression_gzip.c b/libarchive/archive_write_set_compression_gzip.c deleted file mode 100644 index f0176e25f..000000000 --- a/libarchive/archive_write_set_compression_gzip.c +++ /dev/null @@ -1,477 +0,0 @@ -/*- - * 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: head/lib/libarchive/archive_write_set_compression_gzip.c 201081 2009-12-28 02:04:42Z kientzle $"); - -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#include -#ifdef HAVE_ZLIB_H -#include -#endif - -#include "archive.h" -#include "archive_private.h" -#include "archive_write_private.h" - -#ifndef HAVE_ZLIB_H -int -archive_write_set_compression_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 { - z_stream stream; - int64_t total_in; - unsigned char *compressed; - size_t compressed_buffer_size; - unsigned long crc; -}; - -struct private_config { - int compression_level; -}; - - -/* - * Yuck. zlib.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 = (Bytef *)(uintptr_t)(const void *)(src) - -static int archive_compressor_gzip_finish(struct archive_write *); -static int archive_compressor_gzip_init(struct archive_write *); -static int archive_compressor_gzip_options(struct archive_write *, - const char *, const char *); -static int archive_compressor_gzip_write(struct archive_write *, - const void *, size_t); -static int drive_compressor(struct archive_write *, struct private_data *, - int finishing); - - -/* - * Allocate, initialize and return a archive object. - */ -int -archive_write_set_compression_gzip(struct archive *_a) -{ - struct archive_write *a = (struct archive_write *)_a; - struct private_config *config; - __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, - ARCHIVE_STATE_NEW, "archive_write_set_compression_gzip"); - config = malloc(sizeof(*config)); - if (config == NULL) { - archive_set_error(&a->archive, ENOMEM, "Out of memory"); - return (ARCHIVE_FATAL); - } - a->compressor.config = config; - a->compressor.finish = &archive_compressor_gzip_finish; - config->compression_level = Z_DEFAULT_COMPRESSION; - a->compressor.init = &archive_compressor_gzip_init; - a->compressor.options = &archive_compressor_gzip_options; - a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP; - a->archive.compression_name = "gzip"; - return (ARCHIVE_OK); -} - -/* - * Setup callback. - */ -static int -archive_compressor_gzip_init(struct archive_write *a) -{ - int ret; - struct private_data *state; - struct private_config *config; - time_t t; - - config = (struct private_config *)a->compressor.config; - - if (a->client_opener != NULL) { - ret = (a->client_opener)(&a->archive, a->client_data); - if (ret != ARCHIVE_OK) - return (ret); - } - - /* - * The next check is a temporary workaround until the gzip - * code can be overhauled some. The code should not require - * that compressed_buffer_size == bytes_per_block. Removing - * this assumption will allow us to compress larger chunks at - * a time, which should improve overall performance - * marginally. As a minor side-effect, such a cleanup would - * allow us to support truly arbitrary block sizes. - */ - if (a->bytes_per_block < 10) { - archive_set_error(&a->archive, EINVAL, - "GZip compressor requires a minimum 10 byte block size"); - return (ARCHIVE_FATAL); - } - - 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)); - - /* - * See comment above. We should set compressed_buffer_size to - * max(bytes_per_block, 65536), but the code can't handle that yet. - */ - state->compressed_buffer_size = a->bytes_per_block; - state->compressed = (unsigned char *)malloc(state->compressed_buffer_size); - state->crc = crc32(0L, NULL, 0); - - 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; - - /* Prime output buffer with a gzip header. */ - t = time(NULL); - state->compressed[0] = 0x1f; /* GZip signature bytes */ - state->compressed[1] = 0x8b; - state->compressed[2] = 0x08; /* "Deflate" compression */ - state->compressed[3] = 0; /* No options */ - state->compressed[4] = (t)&0xff; /* Timestamp */ - state->compressed[5] = (t>>8)&0xff; - state->compressed[6] = (t>>16)&0xff; - state->compressed[7] = (t>>24)&0xff; - state->compressed[8] = 0; /* No deflate options */ - state->compressed[9] = 3; /* OS=Unix */ - state->stream.next_out += 10; - state->stream.avail_out -= 10; - - a->compressor.write = archive_compressor_gzip_write; - - /* Initialize compression library. */ - ret = deflateInit2(&(state->stream), - config->compression_level, - Z_DEFLATED, - -15 /* < 0 to suppress zlib header */, - 8, - Z_DEFAULT_STRATEGY); - - if (ret == Z_OK) { - a->compressor.data = state; - return (0); - } - - /* 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 Z_STREAM_ERROR: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing " - "compression library: invalid setup parameter"); - break; - case Z_MEM_ERROR: - archive_set_error(&a->archive, ENOMEM, "Internal error initializing " - "compression library"); - break; - case Z_VERSION_ERROR: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing " - "compression library: invalid library version"); - break; - } - - return (ARCHIVE_FATAL); -} - -/* - * Set write options. - */ -static int -archive_compressor_gzip_options(struct archive_write *a, const char *key, - const char *value) -{ - struct private_config *config; - - config = (struct private_config *)a->compressor.config; - if (strcmp(key, "compression-level") == 0) { - if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || - value[1] != '\0') - return (ARCHIVE_WARN); - config->compression_level = value[0] - '0'; - return (ARCHIVE_OK); - } - - return (ARCHIVE_WARN); -} - -/* - * Write data to the compressed stream. - */ -static int -archive_compressor_gzip_write(struct archive_write *a, const void *buff, - size_t length) -{ - struct private_data *state; - int ret; - - 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->crc = crc32(state->crc, (const Bytef *)buff, length); - state->total_in += length; - - /* Compress input data to output buffer */ - SET_NEXT_IN(state, buff); - state->stream.avail_in = length; - if ((ret = drive_compressor(a, state, 0)) != ARCHIVE_OK) - return (ret); - - a->archive.file_position += length; - return (ARCHIVE_OK); -} - -/* - * Finish the compression... - */ -static int -archive_compressor_gzip_finish(struct archive_write *a) -{ - ssize_t block_length, target_block_length, bytes_written; - int ret; - struct private_data *state; - unsigned tocopy; - unsigned char trailer[8]; - - state = (struct private_data *)a->compressor.data; - ret = 0; - if (state != NULL) { - 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."); - 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) { - SET_NEXT_IN(state, a->nulls); - state->stream.avail_in = tocopy < a->null_length ? - tocopy : a->null_length; - state->crc = crc32(state->crc, a->nulls, - state->stream.avail_in); - 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))) != ARCHIVE_OK) - goto cleanup; - - /* Build trailer: 4-byte CRC and 4-byte length. */ - trailer[0] = (state->crc)&0xff; - trailer[1] = (state->crc >> 8)&0xff; - trailer[2] = (state->crc >> 16)&0xff; - trailer[3] = (state->crc >> 24)&0xff; - trailer[4] = (state->total_in)&0xff; - trailer[5] = (state->total_in >> 8)&0xff; - trailer[6] = (state->total_in >> 16)&0xff; - trailer[7] = (state->total_in >> 24)&0xff; - - /* Add trailer to current block. */ - tocopy = 8; - if (tocopy > state->stream.avail_out) - tocopy = state->stream.avail_out; - memcpy(state->stream.next_out, trailer, tocopy); - state->stream.next_out += tocopy; - state->stream.avail_out -= tocopy; - - /* If it overflowed, flush and start a new block. */ - if (tocopy < 8) { - bytes_written = (a->client_writer)(&a->archive, a->client_data, - state->compressed, state->compressed_buffer_size); - if (bytes_written <= 0) { - ret = ARCHIVE_FATAL; - goto cleanup; - } - a->archive.raw_position += bytes_written; - state->stream.next_out = state->compressed; - state->stream.avail_out = state->compressed_buffer_size; - memcpy(state->stream.next_out, trailer + tocopy, 8-tocopy); - state->stream.next_out += 8-tocopy; - state->stream.avail_out -= 8-tocopy; - } - - /* Optionally, pad the final compressed block. */ - block_length = state->stream.next_out - state->compressed; - - /* Tricky calculation to determine size of last block. */ - 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); - if (bytes_written <= 0) { - ret = ARCHIVE_FATAL; - goto cleanup; - } - a->archive.raw_position += bytes_written; - - /* Cleanup: shut down compressor, release memory, etc. */ - cleanup: - switch (deflateEnd(&(state->stream))) { - case Z_OK: - break; - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Failed to clean up compressor"); - ret = ARCHIVE_FATAL; - } - free(state->compressed); - free(state); - } - /* Clean up config area even if we never initialized. */ - free(a->compressor.config); - a->compressor.config = NULL; - 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 remaining to - * front of block 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 = deflate(&(state->stream), - finishing ? Z_FINISH : Z_NO_FLUSH ); - - switch (ret) { - case Z_OK: - /* In non-finishing case, check if compressor - * consumed everything */ - if (!finishing && state->stream.avail_in == 0) - return (ARCHIVE_OK); - /* In finishing case, this return always means - * there's more work */ - break; - case Z_STREAM_END: - /* This return can only occur in finishing case. */ - return (ARCHIVE_OK); - default: - /* Any other return value indicates an error. */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "GZip compression failed:" - " deflate() call returned status %d", - ret); - return (ARCHIVE_FATAL); - } - } -} - -#endif /* HAVE_ZLIB_H */ diff --git a/libarchive/archive_write_set_compression_program.c b/libarchive/archive_write_set_compression_program.c deleted file mode 100644 index 475ba3540..000000000 --- a/libarchive/archive_write_set_compression_program.c +++ /dev/null @@ -1,347 +0,0 @@ -/*- - * Copyright (c) 2007 Joerg Sonnenberger - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "archive_platform.h" - -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_program.c 201104 2009-12-28 03:14:30Z kientzle $"); - -/* 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__)) -#include "archive.h" - -/* - * On non-Posix systems, allow the program to build, but choke if - * this function is actually invoked. - */ -int -archive_write_set_compression_program(struct archive *_a, const char *cmd) -{ - archive_set_error(_a, -1, - "External compression programs not supported on this platform"); - return (ARCHIVE_FATAL); -} - -#else - -#ifdef HAVE_SYS_WAIT_H -# include -#endif -#ifdef HAVE_ERRNO_H -# include -#endif -#ifdef HAVE_FCNTL_H -# include -#endif -#ifdef HAVE_STDLIB_H -# include -#endif -#ifdef HAVE_STRING_H -# include -#endif - -#include "archive.h" -#include "archive_private.h" -#include "archive_write_private.h" - -#include "filter_fork.h" - -struct private_data { - char *description; - pid_t child; - int child_stdin, child_stdout; - - char *child_buf; - size_t child_buf_len, child_buf_avail; -}; - -static int archive_compressor_program_finish(struct archive_write *); -static int archive_compressor_program_init(struct archive_write *); -static int archive_compressor_program_write(struct archive_write *, - const void *, size_t); - -/* - * Allocate, initialize and return a archive object. - */ -int -archive_write_set_compression_program(struct archive *_a, const char *cmd) -{ - struct archive_write *a = (struct archive_write *)_a; - __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, - ARCHIVE_STATE_NEW, "archive_write_set_compression_program"); - a->compressor.init = &archive_compressor_program_init; - a->compressor.config = strdup(cmd); - return (ARCHIVE_OK); -} - -/* - * Setup callback. - */ -static int -archive_compressor_program_init(struct archive_write *a) -{ - int ret; - struct private_data *state; - static const char *prefix = "Program: "; - char *cmd = a->compressor.config; - - if (a->client_opener != NULL) { - ret = (a->client_opener)(&a->archive, a->client_data); - if (ret != ARCHIVE_OK) - 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)); - - a->archive.compression_code = ARCHIVE_COMPRESSION_PROGRAM; - state->description = (char *)malloc(strlen(prefix) + strlen(cmd) + 1); - strcpy(state->description, prefix); - strcat(state->description, cmd); - a->archive.compression_name = state->description; - - state->child_buf_len = a->bytes_per_block; - state->child_buf_avail = 0; - state->child_buf = malloc(state->child_buf_len); - - if (state->child_buf == NULL) { - archive_set_error(&a->archive, ENOMEM, - "Can't allocate data for compression buffer"); - free(state); - return (ARCHIVE_FATAL); - } - - if ((state->child = __archive_create_child(cmd, - &state->child_stdin, &state->child_stdout)) == -1) { - archive_set_error(&a->archive, EINVAL, - "Can't initialise filter"); - free(state->child_buf); - free(state); - return (ARCHIVE_FATAL); - } - - a->compressor.write = archive_compressor_program_write; - a->compressor.finish = archive_compressor_program_finish; - - a->compressor.data = state; - return (0); -} - -static ssize_t -child_write(struct archive_write *a, const char *buf, size_t buf_len) -{ - struct private_data *state = a->compressor.data; - ssize_t ret; - - if (state->child_stdin == -1) - return (-1); - - if (buf_len == 0) - return (-1); - -restart_write: - do { - ret = write(state->child_stdin, buf, buf_len); - } while (ret == -1 && errno == EINTR); - - if (ret > 0) - return (ret); - if (ret == 0) { - close(state->child_stdin); - state->child_stdin = -1; - fcntl(state->child_stdout, F_SETFL, 0); - return (0); - } - if (ret == -1 && errno != EAGAIN) - return (-1); - - if (state->child_stdout == -1) { - fcntl(state->child_stdin, F_SETFL, 0); - __archive_check_child(state->child_stdin, state->child_stdout); - goto restart_write; - } - - do { - ret = read(state->child_stdout, - state->child_buf + state->child_buf_avail, - state->child_buf_len - state->child_buf_avail); - } while (ret == -1 && errno == EINTR); - - if (ret == 0 || (ret == -1 && errno == EPIPE)) { - close(state->child_stdout); - state->child_stdout = -1; - fcntl(state->child_stdin, F_SETFL, 0); - goto restart_write; - } - if (ret == -1 && errno == EAGAIN) { - __archive_check_child(state->child_stdin, state->child_stdout); - goto restart_write; - } - if (ret == -1) - return (-1); - - state->child_buf_avail += ret; - - ret = (a->client_writer)(&a->archive, a->client_data, - state->child_buf, state->child_buf_avail); - if (ret <= 0) - return (-1); - - if ((size_t)ret < state->child_buf_avail) { - memmove(state->child_buf, state->child_buf + ret, - state->child_buf_avail - ret); - } - state->child_buf_avail -= ret; - a->archive.raw_position += ret; - goto restart_write; -} - -/* - * Write data to the compressed stream. - */ -static int -archive_compressor_program_write(struct archive_write *a, const void *buff, - size_t length) -{ - ssize_t ret; - const char *buf; - - 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); - } - - buf = buff; - while (length > 0) { - ret = child_write(a, buf, length); - if (ret == -1 || ret == 0) { - archive_set_error(&a->archive, EIO, - "Can't write to filter"); - return (ARCHIVE_FATAL); - } - length -= ret; - buf += ret; - } - - a->archive.file_position += length; - return (ARCHIVE_OK); -} - - -/* - * Finish the compression... - */ -static int -archive_compressor_program_finish(struct archive_write *a) -{ - int ret, status; - ssize_t bytes_read, bytes_written; - struct private_data *state; - - state = (struct private_data *)a->compressor.data; - ret = 0; - 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."); - ret = ARCHIVE_FATAL; - goto cleanup; - } - - /* XXX pad compressed data. */ - - close(state->child_stdin); - state->child_stdin = -1; - fcntl(state->child_stdout, F_SETFL, 0); - - for (;;) { - do { - bytes_read = read(state->child_stdout, - state->child_buf + state->child_buf_avail, - state->child_buf_len - state->child_buf_avail); - } while (bytes_read == -1 && errno == EINTR); - - if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE)) - break; - - if (bytes_read == -1) { - archive_set_error(&a->archive, errno, - "Read from filter failed unexpectedly."); - ret = ARCHIVE_FATAL; - goto cleanup; - } - state->child_buf_avail += bytes_read; - - bytes_written = (a->client_writer)(&a->archive, a->client_data, - state->child_buf, state->child_buf_avail); - if (bytes_written <= 0) { - ret = ARCHIVE_FATAL; - goto cleanup; - } - if ((size_t)bytes_written < state->child_buf_avail) { - memmove(state->child_buf, - state->child_buf + bytes_written, - state->child_buf_avail - bytes_written); - } - state->child_buf_avail -= bytes_written; - a->archive.raw_position += bytes_written; - } - - /* XXX pad final compressed block. */ - -cleanup: - /* Shut down the child. */ - if (state->child_stdin != -1) - close(state->child_stdin); - if (state->child_stdout != -1) - close(state->child_stdout); - while (waitpid(state->child, &status, 0) == -1 && errno == EINTR) - continue; - - if (status != 0) { - archive_set_error(&a->archive, EIO, - "Filter exited with failure."); - ret = ARCHIVE_FATAL; - } - - /* Release our configuration data. */ - free(a->compressor.config); - a->compressor.config = NULL; - - /* Release our private state data. */ - free(state->child_buf); - free(state->description); - free(state); - return (ret); -} - -#endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */ diff --git a/libarchive/archive_write_set_compression_xz.c b/libarchive/archive_write_set_compression_xz.c deleted file mode 100644 index f82f6db62..000000000 --- a/libarchive/archive_write_set_compression_xz.c +++ /dev/null @@ -1,438 +0,0 @@ -/*- - * Copyright (c) 2009 Michihiro NAKAJIMA - * Copyright (c) 2003-2007 Tim Kientzle - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "archive_platform.h" - -__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_compression_xz.c 201108 2009-12-28 03:28:21Z kientzle $"); - -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_STDLIB_H -#include -#endif -#ifdef HAVE_STRING_H -#include -#endif -#include -#ifdef HAVE_LZMA_H -#include -#endif - -#include "archive.h" -#include "archive_private.h" -#include "archive_write_private.h" - -#ifndef HAVE_LZMA_H -int -archive_write_set_compression_xz(struct archive *a) -{ - archive_set_error(a, ARCHIVE_ERRNO_MISC, - "xz compression not supported on this platform"); - return (ARCHIVE_FATAL); -} - -int -archive_write_set_compression_lzma(struct archive *a) -{ - archive_set_error(a, ARCHIVE_ERRNO_MISC, - "lzma compression not supported on this platform"); - return (ARCHIVE_FATAL); -} -#else -/* Don't compile this if we don't have liblzma. */ - -struct private_data { - lzma_stream stream; - lzma_filter lzmafilters[2]; - lzma_options_lzma lzma_opt; - int64_t total_in; - unsigned char *compressed; - size_t compressed_buffer_size; -}; - -struct private_config { - int compression_level; -}; - -static int archive_compressor_xz_init(struct archive_write *); -static int archive_compressor_xz_options(struct archive_write *, - const char *, const char *); -static int archive_compressor_xz_finish(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 a archive object. - */ -int -archive_write_set_compression_xz(struct archive *_a) -{ - struct private_config *config; - struct archive_write *a = (struct archive_write *)_a; - __archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, - ARCHIVE_STATE_NEW, "archive_write_set_compression_xz"); - config = calloc(1, sizeof(*config)); - if (config == NULL) { - archive_set_error(&a->archive, ENOMEM, "Out of memory"); - return (ARCHIVE_FATAL); - } - a->compressor.config = config; - a->compressor.finish = archive_compressor_xz_finish; - config->compression_level = LZMA_PRESET_DEFAULT; - a->compressor.init = &archive_compressor_xz_init; - a->compressor.options = &archive_compressor_xz_options; - a->archive.compression_code = ARCHIVE_COMPRESSION_XZ; - a->archive.compression_name = "xz"; - return (ARCHIVE_OK); -} - -/* LZMA is handled identically, we just need a different compression - * code set. (The liblzma setup looks at the code to determine - * the one place that XZ and LZMA require different handling.) */ -int -archive_write_set_compression_lzma(struct archive *_a) -{ - struct archive_write *a = (struct archive_write *)_a; - int r = archive_write_set_compression_xz(_a); - if (r != ARCHIVE_OK) - return (r); - a->archive.compression_code = ARCHIVE_COMPRESSION_LZMA; - a->archive.compression_name = "lzma"; - return (ARCHIVE_OK); -} - -static int -archive_compressor_xz_init_stream(struct archive_write *a, - struct private_data *state) -{ - int ret; - - state->stream = (lzma_stream)LZMA_STREAM_INIT; - state->stream.next_out = state->compressed; - state->stream.avail_out = state->compressed_buffer_size; - if (a->archive.compression_code == ARCHIVE_COMPRESSION_XZ) - ret = lzma_stream_encoder(&(state->stream), - state->lzmafilters, LZMA_CHECK_CRC64); - else - ret = lzma_alone_encoder(&(state->stream), &state->lzma_opt); - if (ret == LZMA_OK) - return (ARCHIVE_OK); - - switch (ret) { - case LZMA_MEM_ERROR: - archive_set_error(&a->archive, ENOMEM, - "Internal error initializing compression library: " - "Cannot allocate memory"); - break; - default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing compression library: " - "It's a bug in liblzma"); - break; - } - return (ARCHIVE_FATAL); -} - -/* - * Setup callback. - */ -static int -archive_compressor_xz_init(struct archive_write *a) -{ - int ret; - struct private_data *state; - struct private_config *config; - - if (a->client_opener != NULL) { - ret = (a->client_opener)(&a->archive, a->client_data); - if (ret != ARCHIVE_OK) - 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)); - config = a->compressor.config; - - /* - * See comment above. We should set compressed_buffer_size to - * max(bytes_per_block, 65536), but the code can't handle that yet. - */ - state->compressed_buffer_size = a->bytes_per_block; - state->compressed = (unsigned 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); - } - a->compressor.write = archive_compressor_xz_write; - - /* Initialize compression library. */ - if (lzma_lzma_preset(&state->lzma_opt, config->compression_level)) { - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "Internal error initializing compression library"); - free(state->compressed); - free(state); - } - state->lzmafilters[0].id = LZMA_FILTER_LZMA2; - state->lzmafilters[0].options = &state->lzma_opt; - state->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */ - ret = archive_compressor_xz_init_stream(a, state); - if (ret == LZMA_OK) { - a->compressor.data = state; - return (0); - } - /* Library setup failed: clean up. */ - free(state->compressed); - free(state); - - return (ARCHIVE_FATAL); -} - -/* - * Set write options. - */ -static int -archive_compressor_xz_options(struct archive_write *a, const char *key, - const char *value) -{ - struct private_config *config; - - config = (struct private_config *)a->compressor.config; - if (strcmp(key, "compression-level") == 0) { - if (value == NULL || !(value[0] >= '0' && value[0] <= '9') || - value[1] != '\0') - return (ARCHIVE_WARN); - config->compression_level = value[0] - '0'; - if (config->compression_level > 6) - config->compression_level = 6; - return (ARCHIVE_OK); - } - - return (ARCHIVE_WARN); -} - -/* - * Write data to the compressed stream. - */ -static int -archive_compressor_xz_write(struct archive_write *a, const void *buff, - size_t length) -{ - struct private_data *state; - int ret; - - 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 ((ret = drive_compressor(a, state, 0)) != ARCHIVE_OK) - return (ret); - - 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, target_block_length, bytes_written; - int ret; - struct private_data *state; - unsigned tocopy; - - ret = ARCHIVE_OK; - state = (struct private_data *)a->compressor.data; - if (state != NULL) { - 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."); - 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))) != ARCHIVE_OK) - goto cleanup; - - /* Optionally, pad the final compressed block. */ - block_length = state->stream.next_out - state->compressed; - - /* Tricky calculation to determine size of last block. */ - 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); - if (bytes_written <= 0) { - ret = ARCHIVE_FATAL; - goto cleanup; - } - a->archive.raw_position += bytes_written; - - /* Cleanup: shut down compressor, release memory, etc. */ - cleanup: - lzma_end(&(state->stream)); - free(state->compressed); - free(state); - } - free(a->compressor.config); - a->compressor.config = NULL; - 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 remaining to - * front of block 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_OK: - /* In non-finishing case, check if compressor - * consumed everything */ - if (!finishing && state->stream.avail_in == 0) - return (ARCHIVE_OK); - /* In finishing case, this return always means - * there's more work */ - break; - case LZMA_STREAM_END: - /* This return can only occur in finishing case. */ - if (finishing) - return (ARCHIVE_OK); - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "lzma compression data error"); - return (ARCHIVE_FATAL); - case LZMA_MEMLIMIT_ERROR: - archive_set_error(&a->archive, ENOMEM, - "lzma compression error: " - "%ju MiB would have been needed", - (lzma_memusage(&(state->stream)) + 1024 * 1024 -1) - / (1024 * 1024)); - return (ARCHIVE_FATAL); - default: - /* Any other return value indicates an error. */ - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, - "lzma compression failed:" - " lzma_code() call returned status %d", - ret); - return (ARCHIVE_FATAL); - } - } -} - -#endif /* HAVE_LZMA_H */ diff --git a/libarchive/archive_write_set_format_ar.c b/libarchive/archive_write_set_format_ar.c index b492ceaa5..00c9c7b87 100644 --- a/libarchive/archive_write_set_format_ar.c +++ b/libarchive/archive_write_set_format_ar.c @@ -48,6 +48,7 @@ struct ar_w { uint64_t entry_padding; int is_strtab; int has_strtab; + char wrote_global_header; char *strtab; }; @@ -166,8 +167,10 @@ archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) * If we are now at the beginning of the archive, * we need first write the ar global header. */ - if (a->archive.file_position == 0) - (a->compressor.write)(a, "!\n", 8); + if (!ar->wrote_global_header) { + __archive_write_output(a, "!\n", 8); + ar->wrote_global_header = 1; + } memset(buff, ' ', 60); strncpy(&buff[AR_fmag_offset], "`\n", 2); @@ -330,7 +333,7 @@ size: return (ARCHIVE_WARN); } - ret = (a->compressor.write)(a, buff, 60); + ret = __archive_write_output(a, buff, 60); if (ret != ARCHIVE_OK) return (ret); @@ -338,7 +341,7 @@ size: ar->entry_padding = ar->entry_bytes_remaining % 2; if (append_fn > 0) { - ret = (a->compressor.write)(a, filename, strlen(filename)); + ret = __archive_write_output(a, filename, strlen(filename)); if (ret != ARCHIVE_OK) return (ret); ar->entry_bytes_remaining -= strlen(filename); @@ -374,7 +377,7 @@ archive_write_ar_data(struct archive_write *a, const void *buff, size_t s) ar->has_strtab = 1; } - ret = (a->compressor.write)(a, buff, s); + ret = __archive_write_output(a, buff, s); if (ret != ARCHIVE_OK) return (ret); @@ -405,14 +408,17 @@ archive_write_ar_destroy(struct archive_write *a) static int archive_write_ar_finish(struct archive_write *a) { + struct ar_w *ar; int ret; /* * If we haven't written anything yet, we need to write * the ar global header now to make it a valid ar archive. */ - if (a->archive.file_position == 0) { - ret = (a->compressor.write)(a, "!\n", 8); + ar = (struct ar_w *)a->format_data; + if (!ar->wrote_global_header) { + ar->wrote_global_header = 1; + ret = __archive_write_output(a, "!\n", 8); return (ret); } @@ -444,7 +450,7 @@ archive_write_ar_finish_entry(struct archive_write *a) return (ARCHIVE_WARN); } - ret = (a->compressor.write)(a, "\n", 1); + ret = __archive_write_output(a, "\n", 1); return (ret); } diff --git a/libarchive/archive_write_set_format_cpio.c b/libarchive/archive_write_set_format_cpio.c index 4e8e1e0bd..29a6fb6e0 100644 --- a/libarchive/archive_write_set_format_cpio.c +++ b/libarchive/archive_write_set_format_cpio.c @@ -229,11 +229,11 @@ archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry) format_octal(archive_entry_size(entry), &h.c_filesize, sizeof(h.c_filesize)); - ret = (a->compressor.write)(a, &h, sizeof(h)); + ret = __archive_write_output(a, &h, sizeof(h)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); - ret = (a->compressor.write)(a, path, pathlength); + ret = __archive_write_output(a, path, pathlength); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -241,7 +241,7 @@ archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry) /* Write the symlink now. */ if (p != NULL && *p != '\0') - ret = (a->compressor.write)(a, p, strlen(p)); + ret = __archive_write_output(a, p, strlen(p)); if (ret == ARCHIVE_OK) ret = ret2; @@ -258,7 +258,7 @@ archive_write_cpio_data(struct archive_write *a, const void *buff, size_t s) if (s > cpio->entry_bytes_remaining) s = cpio->entry_bytes_remaining; - ret = (a->compressor.write)(a, buff, s); + ret = __archive_write_output(a, buff, s); cpio->entry_bytes_remaining -= s; if (ret >= 0) return (s); @@ -335,7 +335,7 @@ archive_write_cpio_finish_entry(struct archive_write *a) while (cpio->entry_bytes_remaining > 0) { to_write = cpio->entry_bytes_remaining < a->null_length ? cpio->entry_bytes_remaining : a->null_length; - ret = (a->compressor.write)(a, a->nulls, to_write); + ret = __archive_write_output(a, a->nulls, to_write); if (ret != ARCHIVE_OK) return (ret); cpio->entry_bytes_remaining -= to_write; diff --git a/libarchive/archive_write_set_format_cpio_newc.c b/libarchive/archive_write_set_format_cpio_newc.c index 0af089095..bf4f7435b 100644 --- a/libarchive/archive_write_set_format_cpio_newc.c +++ b/libarchive/archive_write_set_format_cpio_newc.c @@ -170,17 +170,17 @@ archive_write_newc_header(struct archive_write *a, struct archive_entry *entry) format_hex(archive_entry_size(entry), &h.c_filesize, sizeof(h.c_filesize)); - ret = (a->compressor.write)(a, &h, sizeof(h)); + ret = __archive_write_output(a, &h, sizeof(h)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); /* Pad pathname to even length. */ - ret = (a->compressor.write)(a, path, pathlength); + ret = __archive_write_output(a, path, pathlength); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); pad = PAD4(pathlength + sizeof(struct cpio_header_newc)); if (pad) - ret = (a->compressor.write)(a, "\0\0\0", pad); + ret = __archive_write_output(a, "\0\0\0", pad); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -189,11 +189,11 @@ archive_write_newc_header(struct archive_write *a, struct archive_entry *entry) /* Write the symlink now. */ if (p != NULL && *p != '\0') { - ret = (a->compressor.write)(a, p, strlen(p)); + ret = __archive_write_output(a, p, strlen(p)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); pad = PAD4(strlen(p)); - ret = (a->compressor.write)(a, "\0\0\0", pad); + ret = __archive_write_output(a, "\0\0\0", pad); } if (ret == ARCHIVE_OK) @@ -211,7 +211,7 @@ archive_write_newc_data(struct archive_write *a, const void *buff, size_t s) if (s > cpio->entry_bytes_remaining) s = cpio->entry_bytes_remaining; - ret = (a->compressor.write)(a, buff, s); + ret = __archive_write_output(a, buff, s); cpio->entry_bytes_remaining -= s; if (ret >= 0) return (s); @@ -285,11 +285,11 @@ archive_write_newc_finish_entry(struct archive_write *a) while (cpio->entry_bytes_remaining > 0) { to_write = cpio->entry_bytes_remaining < a->null_length ? cpio->entry_bytes_remaining : a->null_length; - ret = (a->compressor.write)(a, a->nulls, to_write); + ret = __archive_write_output(a, a->nulls, to_write); if (ret != ARCHIVE_OK) return (ret); cpio->entry_bytes_remaining -= to_write; } - ret = (a->compressor.write)(a, a->nulls, cpio->padding); + ret = __archive_write_output(a, a->nulls, cpio->padding); return (ret); } diff --git a/libarchive/archive_write_set_format_mtree.c b/libarchive/archive_write_set_format_mtree.c index 7feeca92e..988a7baa6 100644 --- a/libarchive/archive_write_set_format_mtree.c +++ b/libarchive/archive_write_set_format_mtree.c @@ -821,7 +821,7 @@ archive_write_mtree_finish_entry(struct archive_write *a) archive_entry_free(entry); if (mtree->buf.length > 32768) { - ret = (a->compressor.write)(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; @@ -836,7 +836,7 @@ archive_write_mtree_finish(struct archive_write *a) archive_write_set_bytes_in_last_block(&a->archive, 1); - return (a->compressor.write)(a, mtree->buf.s, mtree->buf.length); + return __archive_write_output(a, mtree->buf.s, mtree->buf.length); } static ssize_t diff --git a/libarchive/archive_write_set_format_pax.c b/libarchive/archive_write_set_format_pax.c index 420a018f6..cab8f3756 100644 --- a/libarchive/archive_write_set_format_pax.c +++ b/libarchive/archive_write_set_format_pax.c @@ -1056,7 +1056,7 @@ archive_write_pax_header(struct archive_write *a, (void)u; /* UNUSED */ exit(1); } - r = (a->compressor.write)(a, paxbuff, 512); + r = __archive_write_output(a, paxbuff, 512); if (r != ARCHIVE_OK) { sparse_list_clear(pax); pax->entry_bytes_remaining = 0; @@ -1067,7 +1067,7 @@ archive_write_pax_header(struct archive_write *a, pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header)); pax->entry_padding = 0x1ff & (-(int64_t)pax->entry_bytes_remaining); - r = (a->compressor.write)(a, pax->pax_header.s, + r = __archive_write_output(a, pax->pax_header.s, archive_strlen(&(pax->pax_header))); if (r != ARCHIVE_OK) { /* If a write fails, we're pretty much toast. */ @@ -1083,7 +1083,7 @@ archive_write_pax_header(struct archive_write *a, } /* Write the header for main entry. */ - r = (a->compressor.write)(a, ustarbuff, 512); + r = __archive_write_output(a, ustarbuff, 512); if (r != ARCHIVE_OK) return (r); @@ -1327,7 +1327,7 @@ build_pax_attribute_name(char *dest, const char *src) * /GNUSparseFile./ * * This function is used for only Sparse file, a file type of which - * is regular file. + * is regular file. */ static char * build_gnu_sparse_name(char *dest, const char *src) @@ -1374,13 +1374,7 @@ build_gnu_sparse_name(char *dest, const char *src) static int archive_write_pax_finish(struct archive_write *a) { - int r; - - if (a->compressor.write == NULL) - return (ARCHIVE_OK); - - r = write_nulls(a, 512 * 2); - return (r); + return (write_nulls(a, 512 * 2)); } static int @@ -1431,7 +1425,7 @@ write_nulls(struct archive_write *a, size_t padding) while (padding > 0) { to_write = padding < a->null_length ? padding : a->null_length; - ret = (a->compressor.write)(a, a->nulls, to_write); + ret = __archive_write_output(a, a->nulls, to_write); if (ret != ARCHIVE_OK) return (ret); padding -= to_write; @@ -1451,10 +1445,10 @@ archive_write_pax_data(struct archive_write *a, const void *buff, size_t s) /* * According to GNU PAX format 1.0, write a sparse map - * before the body. + * before the body. */ if (archive_strlen(&(pax->sparse_map))) { - ret = (a->compressor.write)(a, pax->sparse_map.s, + ret = __archive_write_output(a, pax->sparse_map.s, archive_strlen(&(pax->sparse_map))); if (ret != ARCHIVE_OK) return (ret); @@ -1491,7 +1485,7 @@ archive_write_pax_data(struct archive_write *a, const void *buff, size_t s) continue; } - ret = (a->compressor.write)(a, p, ws); + ret = __archive_write_output(a, p, ws); pax->sparse_list->remaining -= ws; total += ws; if (ret != ARCHIVE_OK) diff --git a/libarchive/archive_write_set_format_shar.c b/libarchive/archive_write_set_format_shar.c index 62a875b98..0348b4eb0 100644 --- a/libarchive/archive_write_set_format_shar.c +++ b/libarchive/archive_write_set_format_shar.c @@ -353,7 +353,7 @@ archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n) __archive_errx(1, "Out of memory"); if (shar->work.length > ensured) { - ret = (*a->compressor.write)(a, shar->work.s, + ret = __archive_write_output(a, shar->work.s, shar->work.length); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -377,7 +377,7 @@ archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n) if (buf >= buf_end) { shar->work.length = buf - shar->work.s; - ret = (*a->compressor.write)(a, shar->work.s, + ret = __archive_write_output(a, shar->work.s, shar->work.length); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -478,7 +478,7 @@ archive_write_shar_data_uuencode(struct archive_write *a, const void *buff, if (shar->work.length < 65536) continue; - ret = (*a->compressor.write)(a, shar->work.s, + ret = __archive_write_output(a, shar->work.s, shar->work.length); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); @@ -561,7 +561,7 @@ archive_write_shar_finish_entry(struct archive_write *a) if (shar->work.length < 65536) return (ARCHIVE_OK); - ret = (*a->compressor.write)(a, shar->work.s, shar->work.length); + ret = __archive_write_output(a, shar->work.s, shar->work.length); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); archive_string_empty(&shar->work); @@ -593,7 +593,7 @@ archive_write_shar_finish(struct archive_write *a) archive_strcat(&shar->work, "exit\n"); - ret = (*a->compressor.write)(a, shar->work.s, shar->work.length); + ret = __archive_write_output(a, shar->work.s, shar->work.length); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); diff --git a/libarchive/archive_write_set_format_ustar.c b/libarchive/archive_write_set_format_ustar.c index 1de273fc2..5bd038ee2 100644 --- a/libarchive/archive_write_set_format_ustar.c +++ b/libarchive/archive_write_set_format_ustar.c @@ -232,7 +232,7 @@ archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry) ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1); if (ret < ARCHIVE_WARN) return (ret); - ret2 = (a->compressor.write)(a, buff, 512); + ret2 = __archive_write_output(a, buff, 512); if (ret2 < ARCHIVE_WARN) return (ret2); if (ret2 < ret) @@ -521,13 +521,7 @@ format_octal(int64_t v, char *p, int s) static int archive_write_ustar_finish(struct archive_write *a) { - int r; - - if (a->compressor.write == NULL) - return (ARCHIVE_OK); - - r = write_nulls(a, 512*2); - return (r); + return (write_nulls(a, 512*2)); } static int @@ -562,7 +556,7 @@ write_nulls(struct archive_write *a, size_t padding) while (padding > 0) { to_write = padding < a->null_length ? padding : a->null_length; - ret = (a->compressor.write)(a, a->nulls, to_write); + ret = __archive_write_output(a, a->nulls, to_write); if (ret != ARCHIVE_OK) return (ret); padding -= to_write; @@ -579,7 +573,7 @@ archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s) ustar = (struct ustar *)a->format_data; if (s > ustar->entry_bytes_remaining) s = ustar->entry_bytes_remaining; - ret = (a->compressor.write)(a, buff, s); + ret = __archive_write_output(a, buff, s); ustar->entry_bytes_remaining -= s; if (ret != ARCHIVE_OK) return (ret); diff --git a/libarchive/archive_write_set_format_zip.c b/libarchive/archive_write_set_format_zip.c index 14cc6697d..620dc08b1 100644 --- a/libarchive/archive_write_set_format_zip.c +++ b/libarchive/archive_write_set_format_zip.c @@ -366,7 +366,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) archive_le32enc(&e.mtime, archive_entry_mtime(entry)); archive_le32enc(&e.atime, archive_entry_atime(entry)); archive_le32enc(&e.ctime, archive_entry_ctime(entry)); - + archive_le16enc(&e.unix_id, ZIP_SIGNATURE_EXTRA_UNIX); archive_le16enc(&e.unix_size, sizeof(e.unix_uid) + sizeof(e.unix_gid)); archive_le16enc(&e.unix_uid, archive_entry_uid(entry)); @@ -374,7 +374,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) archive_le32enc(&d->uncompressed_size, size); - ret = (a->compressor.write)(a, &h, sizeof(h)); + ret = __archive_write_output(a, &h, sizeof(h)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += sizeof(h); @@ -384,7 +384,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) return (ARCHIVE_FATAL); zip->written_bytes += ret; - ret = (a->compressor.write)(a, &e, sizeof(e)); + ret = __archive_write_output(a, &e, sizeof(e)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += sizeof(e); @@ -406,7 +406,7 @@ archive_write_zip_data(struct archive_write *a, const void *buff, size_t s) switch (zip->compression) { case COMPRESSION_STORE: - ret = (a->compressor.write)(a, buff, s); + ret = __archive_write_output(a, buff, s); if (ret != ARCHIVE_OK) return (ret); zip->written_bytes += s; zip->remaining_data_bytes -= s; @@ -422,7 +422,7 @@ archive_write_zip_data(struct archive_write *a, const void *buff, size_t s) if (ret == Z_STREAM_ERROR) return (ARCHIVE_FATAL); if (zip->stream.avail_out == 0) { - ret = (a->compressor.write)(a, zip->buf, zip->len_buf); + ret = __archive_write_output(a, zip->buf, zip->len_buf); if (ret != ARCHIVE_OK) return (ret); l->compressed_size += zip->len_buf; @@ -466,7 +466,7 @@ archive_write_zip_finish_entry(struct archive_write *a) if (ret == Z_STREAM_ERROR) return (ARCHIVE_FATAL); reminder = zip->len_buf - zip->stream.avail_out; - ret = (a->compressor.write)(a, zip->buf, reminder); + ret = __archive_write_output(a, zip->buf, reminder); if (ret != ARCHIVE_OK) return (ret); l->compressed_size += reminder; @@ -483,7 +483,7 @@ archive_write_zip_finish_entry(struct archive_write *a) archive_le32enc(&d->crc32, l->crc32); archive_le32enc(&d->compressed_size, l->compressed_size); - ret = (a->compressor.write)(a, d, sizeof(*d)); + ret = __archive_write_output(a, d, sizeof(*d)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += sizeof(*d); @@ -543,7 +543,7 @@ archive_write_zip_finish(struct archive_write *a) archive_le16enc(&e.unix_id, ZIP_SIGNATURE_EXTRA_UNIX); archive_le16enc(&e.unix_size, 0x0000); - ret = (a->compressor.write)(a, &h, sizeof(h)); + ret = __archive_write_output(a, &h, sizeof(h)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += sizeof(h); @@ -553,7 +553,7 @@ archive_write_zip_finish(struct archive_write *a) return (ARCHIVE_FATAL); zip->written_bytes += ret; - ret = (a->compressor.write)(a, &e, sizeof(e)); + ret = __archive_write_output(a, &e, sizeof(e)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += sizeof(e); @@ -572,7 +572,7 @@ archive_write_zip_finish(struct archive_write *a) archive_le32enc(&end.offset, offset_start); /* Writing end of central directory. */ - ret = (a->compressor.write)(a, &end, sizeof(end)); + ret = __archive_write_output(a, &end, sizeof(end)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += sizeof(end); @@ -650,14 +650,14 @@ write_path(struct archive_entry *entry, struct archive_write *archive) type = archive_entry_filetype(entry); written_bytes = 0; - ret = (archive->compressor.write)(archive, path, strlen(path)); + ret = __archive_write_output(archive, path, strlen(path)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); written_bytes += strlen(path); /* Folders are recognized by a traling slash. */ if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) { - ret = (archive->compressor.write)(archive, "/", 1); + ret = __archive_write_output(archive, "/", 1); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); written_bytes += 1;