Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

2325 lines (2089 sloc) 56.542 kb
/*-
* Copyright (c) 2011-2012 Michihiro NAKAJIMA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "archive_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#include <stdlib.h>
#ifdef HAVE_BZLIB_H
#include <bzlib.h>
#endif
#if HAVE_LZMA_H
#include <lzma.h>
#endif
#ifdef HAVE_ZLIB_H
#include <zlib.h>
#endif
#include "archive.h"
#ifndef HAVE_ZLIB_H
#include "archive_crc32.h"
#endif
#include "archive_endian.h"
#include "archive_entry.h"
#include "archive_entry_locale.h"
#include "archive_ppmd7_private.h"
#include "archive_private.h"
#include "archive_rb.h"
#include "archive_string.h"
#include "archive_write_private.h"
/*
* Codec ID
*/
#define _7Z_COPY 0
#define _7Z_LZMA1 0x030101
#define _7Z_LZMA2 0x21
#define _7Z_DEFLATE 0x040108
#define _7Z_BZIP2 0x040202
#define _7Z_PPMD 0x030401
/*
* 7-Zip header property IDs.
*/
#define kEnd 0x00
#define kHeader 0x01
#define kArchiveProperties 0x02
#define kAdditionalStreamsInfo 0x03
#define kMainStreamsInfo 0x04
#define kFilesInfo 0x05
#define kPackInfo 0x06
#define kUnPackInfo 0x07
#define kSubStreamsInfo 0x08
#define kSize 0x09
#define kCRC 0x0A
#define kFolder 0x0B
#define kCodersUnPackSize 0x0C
#define kNumUnPackStream 0x0D
#define kEmptyStream 0x0E
#define kEmptyFile 0x0F
#define kAnti 0x10
#define kName 0x11
#define kCTime 0x12
#define kATime 0x13
#define kMTime 0x14
#define kAttributes 0x15
#define kEncodedHeader 0x17
enum la_zaction {
ARCHIVE_Z_FINISH,
ARCHIVE_Z_RUN
};
/*
* A stream object of universal compressor.
*/
struct la_zstream {
const uint8_t *next_in;
size_t avail_in;
uint64_t total_in;
uint8_t *next_out;
size_t avail_out;
uint64_t total_out;
uint32_t prop_size;
uint8_t *props;
int valid;
void *real_stream;
int (*code) (struct archive *a,
struct la_zstream *lastrm,
enum la_zaction action);
int (*end)(struct archive *a,
struct la_zstream *lastrm);
};
#define PPMD7_DEFAULT_ORDER 6
#define PPMD7_DEFAULT_MEM_SIZE (1 << 24)
struct ppmd_stream {
int stat;
CPpmd7 ppmd7_context;
CPpmd7z_RangeEnc range_enc;
IByteOut byteout;
uint8_t *buff;
uint8_t *buff_ptr;
uint8_t *buff_end;
size_t buff_bytes;
};
struct coder {
unsigned codec;
size_t prop_size;
uint8_t *props;
};
struct file {
struct archive_rb_node rbnode;
struct file *next;
unsigned name_len;
uint8_t *utf16name;/* UTF16-LE name. */
uint64_t size;
unsigned flg;
#define MTIME_IS_SET (1<<0)
#define ATIME_IS_SET (1<<1)
#define CTIME_IS_SET (1<<2)
#define CRC32_IS_SET (1<<3)
#define HAS_STREAM (1<<4)
struct {
time_t time;
long time_ns;
} times[3];
#define MTIME 0
#define ATIME 1
#define CTIME 2
mode_t mode;
uint32_t crc32;
int dir:1;
};
struct _7zip {
int temp_fd;
uint64_t temp_offset;
struct file *cur_file;
size_t total_number_entry;
size_t total_number_nonempty_entry;
size_t total_number_empty_entry;
size_t total_number_dir_entry;
size_t total_bytes_entry_name;
size_t total_number_time_defined[3];
uint64_t total_bytes_compressed;
uint64_t total_bytes_uncompressed;
uint64_t entry_bytes_remaining;
uint32_t entry_crc32;
uint32_t precode_crc32;
uint32_t encoded_crc32;
int crc32flg;
#define PRECODE_CRC32 1
#define ENCODED_CRC32 2
unsigned opt_compression;
int opt_compression_level;
struct la_zstream stream;
struct coder coder;
struct archive_string_conv *sconv;
/*
* Compressed data buffer.
*/
unsigned char wbuff[512 * 20 * 6];
size_t wbuff_remaining;
/*
* The list of the file entries which has its contents is used to
* manage struct file objects.
* We use 'next' a menber of struct file to chain.
*/
struct {
struct file *first;
struct file **last;
} file_list, empty_list;
struct archive_rb_tree rbtree;/* for empty files */
};
static int _7z_options(struct archive_write *,
const char *, const char *);
static int _7z_write_header(struct archive_write *,
struct archive_entry *);
static ssize_t _7z_write_data(struct archive_write *,
const void *, size_t);
static int _7z_finish_entry(struct archive_write *);
static int _7z_close(struct archive_write *);
static int _7z_free(struct archive_write *);
static int file_cmp_node(const struct archive_rb_node *,
const struct archive_rb_node *);
static int file_cmp_key(const struct archive_rb_node *, const void *);
static int file_new(struct archive_write *a, struct archive_entry *,
struct file **);
static void file_free(struct file *);
static void file_register(struct _7zip *, struct file *);
static void file_register_empty(struct _7zip *, struct file *);
static void file_init_register(struct _7zip *);
static void file_init_register_empty(struct _7zip *);
static void file_free_register(struct _7zip *);
static ssize_t compress_out(struct archive_write *, const void *, size_t ,
enum la_zaction);
static int compression_init_encoder_copy(struct archive *,
struct la_zstream *);
static int compression_code_copy(struct archive *,
struct la_zstream *, enum la_zaction);
static int compression_end_copy(struct archive *, struct la_zstream *);
static int compression_init_encoder_deflate(struct archive *,
struct la_zstream *, int, int);
#ifdef HAVE_ZLIB_H
static int compression_code_deflate(struct archive *,
struct la_zstream *, enum la_zaction);
static int compression_end_deflate(struct archive *, struct la_zstream *);
#endif
static int compression_init_encoder_bzip2(struct archive *,
struct la_zstream *, int);
#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
static int compression_code_bzip2(struct archive *,
struct la_zstream *, enum la_zaction);
static int compression_end_bzip2(struct archive *, struct la_zstream *);
#endif
static int compression_init_encoder_lzma1(struct archive *,
struct la_zstream *, int);
static int compression_init_encoder_lzma2(struct archive *,
struct la_zstream *, int);
#if defined(HAVE_LZMA_H)
static int compression_code_lzma(struct archive *,
struct la_zstream *, enum la_zaction);
static int compression_end_lzma(struct archive *, struct la_zstream *);
#endif
static int compression_init_encoder_ppmd(struct archive *,
struct la_zstream *, unsigned, uint32_t);
static int compression_code_ppmd(struct archive *,
struct la_zstream *, enum la_zaction);
static int compression_end_ppmd(struct archive *, struct la_zstream *);
static int _7z_compression_init_encoder(struct archive_write *, unsigned,
int);
static int compression_code(struct archive *,
struct la_zstream *, enum la_zaction);
static int compression_end(struct archive *,
struct la_zstream *);
static int enc_uint64(struct archive_write *, uint64_t);
static int make_header(struct archive_write *, uint64_t, uint64_t,
uint64_t, int, struct coder *);
static int make_streamsInfo(struct archive_write *, uint64_t, uint64_t,
uint64_t, int, struct coder *, int, uint32_t);
int
archive_write_set_format_7zip(struct archive *_a)
{
static const struct archive_rb_tree_ops rb_ops = {
file_cmp_node, file_cmp_key
};
struct archive_write *a = (struct archive_write *)_a;
struct _7zip *zip;
archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_set_format_7zip");
/* If another format was already registered, unregister it. */
if (a->format_free != NULL)
(a->format_free)(a);
zip = calloc(1, sizeof(*zip));
if (zip == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate 7-Zip data");
return (ARCHIVE_FATAL);
}
zip->temp_fd = -1;
__archive_rb_tree_init(&(zip->rbtree), &rb_ops);
file_init_register(zip);
file_init_register_empty(zip);
/* Set default compression type and its level. */
#if HAVE_LZMA_H
zip->opt_compression = _7Z_LZMA1;
#elif defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
zip->opt_compression = _7Z_BZIP2;
#elif defined(HAVE_ZLIB_H)
zip->opt_compression = _7Z_DEFLATE;
#else
zip->opt_compression = _7Z_COPY;
#endif
zip->opt_compression_level = 6;
a->format_data = zip;
a->format_name = "7zip";
a->format_options = _7z_options;
a->format_write_header = _7z_write_header;
a->format_write_data = _7z_write_data;
a->format_finish_entry = _7z_finish_entry;
a->format_close = _7z_close;
a->format_free = _7z_free;
a->archive.archive_format = ARCHIVE_FORMAT_7ZIP;
a->archive.archive_format_name = "7zip";
return (ARCHIVE_OK);
}
static int
_7z_options(struct archive_write *a, const char *key, const char *value)
{
struct _7zip *zip;
zip = (struct _7zip *)a->format_data;
if (strcmp(key, "compression") == 0) {
const char *name = NULL;
if (value == NULL || strcmp(value, "copy") == 0 ||
strcmp(value, "COPY") == 0 ||
strcmp(value, "store") == 0 ||
strcmp(value, "STORE") == 0)
zip->opt_compression = _7Z_COPY;
else if (strcmp(value, "deflate") == 0 ||
strcmp(value, "DEFLATE") == 0)
#if HAVE_ZLIB_H
zip->opt_compression = _7Z_DEFLATE;
#else
name = "deflate";
#endif
else if (strcmp(value, "bzip2") == 0 ||
strcmp(value, "BZIP2") == 0)
#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
zip->opt_compression = _7Z_BZIP2;
#else
name = "bzip2";
#endif
else if (strcmp(value, "lzma1") == 0 ||
strcmp(value, "LZMA1") == 0)
#if HAVE_LZMA_H
zip->opt_compression = _7Z_LZMA1;
#else
name = "lzma1";
#endif
else if (strcmp(value, "lzma2") == 0 ||
strcmp(value, "LZMA2") == 0)
#if HAVE_LZMA_H
zip->opt_compression = _7Z_LZMA2;
#else
name = "lzma2";
#endif
else if (strcmp(value, "ppmd") == 0 ||
strcmp(value, "PPMD") == 0 ||
strcmp(value, "PPMd") == 0)
zip->opt_compression = _7Z_PPMD;
else {
archive_set_error(&(a->archive),
ARCHIVE_ERRNO_MISC,
"Unknown compression name: `%s'",
value);
return (ARCHIVE_FAILED);
}
if (name != NULL) {
archive_set_error(&(a->archive),
ARCHIVE_ERRNO_MISC,
"`%s' compression not supported "
"on this platform",
name);
return (ARCHIVE_FAILED);
}
return (ARCHIVE_OK);
}
if (strcmp(key, "compression-level") == 0) {
if (value == NULL ||
!(value[0] >= '0' && value[0] <= '9') ||
value[1] != '\0') {
archive_set_error(&(a->archive),
ARCHIVE_ERRNO_MISC,
"Illegal value `%s'",
value);
return (ARCHIVE_FAILED);
}
zip->opt_compression_level = value[0] - '0';
return (ARCHIVE_OK);
}
/* Note: The "warn" return is just to inform the options
* supervisor that we didn't handle it. It will generate
* a suitable error if no one used this option. */
return (ARCHIVE_WARN);
}
static int
_7z_write_header(struct archive_write *a, struct archive_entry *entry)
{
struct _7zip *zip;
struct file *file;
int r;
zip = (struct _7zip *)a->format_data;
zip->cur_file = NULL;
zip->entry_bytes_remaining = 0;
if (zip->sconv == NULL) {
zip->sconv = archive_string_conversion_to_charset(
&a->archive, "UTF-16LE", 1);
if (zip->sconv == NULL)
return (ARCHIVE_FATAL);
}
r = file_new(a, entry, &file);
if (r < ARCHIVE_WARN) {
file_free(file);
return (r);
}
if (file->size == 0 && file->dir) {
if (!__archive_rb_tree_insert_node(&(zip->rbtree),
(struct archive_rb_node *)file)) {
/* We have already had the same file. */
file_free(file);
return (ARCHIVE_OK);
}
}
if (file->flg & MTIME_IS_SET)
zip->total_number_time_defined[MTIME]++;
if (file->flg & CTIME_IS_SET)
zip->total_number_time_defined[CTIME]++;
if (file->flg & ATIME_IS_SET)
zip->total_number_time_defined[ATIME]++;
zip->total_number_entry++;
zip->total_bytes_entry_name += file->name_len + 2;
if (file->size == 0) {
/* Count up the number of empty files. */
zip->total_number_empty_entry++;
if (file->dir)
zip->total_number_dir_entry++;
else
file_register_empty(zip, file);
return (r);
}
/*
* Init compression.
*/
if ((zip->total_number_entry - zip->total_number_empty_entry) == 1) {
r = _7z_compression_init_encoder(a, zip->opt_compression,
zip->opt_compression_level);
if (r < 0) {
file_free(file);
return (ARCHIVE_FATAL);
}
}
/* Register a non-empty file. */
file_register(zip, file);
/*
* Set the current file to cur_file to read its contents.
*/
zip->cur_file = file;
/* Save a offset of current file in temporary file. */
zip->entry_bytes_remaining = file->size;
zip->entry_crc32 = 0;
/*
* Store a symbolic link name as file contents.
*/
if (archive_entry_filetype(entry) == AE_IFLNK) {
ssize_t bytes;
const void *p = (const void *)archive_entry_symlink(entry);
bytes = compress_out(a, p, (size_t)file->size, ARCHIVE_Z_RUN);
if (bytes < 0)
return ((int)bytes);
zip->entry_crc32 = crc32(zip->entry_crc32, p, (unsigned)bytes);
zip->entry_bytes_remaining -= bytes;
}
return (r);
}
/*
* Write data to a temporary file.
*/
static int
write_to_temp(struct archive_write *a, const void *buff, size_t s)
{
struct _7zip *zip;
const unsigned char *p;
ssize_t ws;
zip = (struct _7zip *)a->format_data;
/*
* Open a temporary file.
*/
if (zip->temp_fd == -1) {
zip->temp_offset = 0;
zip->temp_fd = __archive_mktemp(NULL);
if (zip->temp_fd < 0) {
archive_set_error(&a->archive, errno,
"Couldn't create temporary file");
return (ARCHIVE_FATAL);
}
}
p = (const unsigned char *)buff;
while (s) {
ws = write(zip->temp_fd, p, s);
if (ws < 0) {
archive_set_error(&(a->archive), errno,
"fwrite function failed");
return (ARCHIVE_FATAL);
}
s -= ws;
p += ws;
zip->temp_offset += ws;
}
return (ARCHIVE_OK);
}
static ssize_t
compress_out(struct archive_write *a, const void *buff, size_t s,
enum la_zaction run)
{
struct _7zip *zip = (struct _7zip *)a->format_data;
int r;
if (run == ARCHIVE_Z_FINISH && zip->stream.total_in == 0 && s == 0)
return (0);
if ((zip->crc32flg & PRECODE_CRC32) && s)
zip->precode_crc32 = crc32(zip->precode_crc32, buff,
(unsigned)s);
zip->stream.next_in = (const unsigned char *)buff;
zip->stream.avail_in = s;
for (;;) {
/* Compress file data. */
r = compression_code(&(a->archive), &(zip->stream), run);
if (r != ARCHIVE_OK && r != ARCHIVE_EOF)
return (ARCHIVE_FATAL);
if (zip->stream.avail_out == 0) {
if (write_to_temp(a, zip->wbuff, sizeof(zip->wbuff))
!= ARCHIVE_OK)
return (ARCHIVE_FATAL);
zip->stream.next_out = zip->wbuff;
zip->stream.avail_out = sizeof(zip->wbuff);
if (zip->crc32flg & ENCODED_CRC32)
zip->encoded_crc32 = crc32(zip->encoded_crc32,
zip->wbuff, sizeof(zip->wbuff));
if (run == ARCHIVE_Z_FINISH && r != ARCHIVE_EOF)
continue;
}
if (zip->stream.avail_in == 0)
break;
}
if (run == ARCHIVE_Z_FINISH) {
uint64_t bytes = sizeof(zip->wbuff) - zip->stream.avail_out;
if (write_to_temp(a, zip->wbuff, (size_t)bytes) != ARCHIVE_OK)
return (ARCHIVE_FATAL);
if ((zip->crc32flg & ENCODED_CRC32) && bytes)
zip->encoded_crc32 = crc32(zip->encoded_crc32,
zip->wbuff, (unsigned)bytes);
}
return (s);
}
static ssize_t
_7z_write_data(struct archive_write *a, const void *buff, size_t s)
{
struct _7zip *zip;
ssize_t bytes;
zip = (struct _7zip *)a->format_data;
if (s > zip->entry_bytes_remaining)
s = (size_t)zip->entry_bytes_remaining;
if (s == 0 || zip->cur_file == NULL)
return (0);
bytes = compress_out(a, buff, s, ARCHIVE_Z_RUN);
if (bytes < 0)
return (bytes);
zip->entry_crc32 = crc32(zip->entry_crc32, buff, (unsigned)bytes);
zip->entry_bytes_remaining -= bytes;
return (bytes);
}
static int
_7z_finish_entry(struct archive_write *a)
{
struct _7zip *zip;
size_t s;
ssize_t r;
zip = (struct _7zip *)a->format_data;
if (zip->cur_file == NULL)
return (ARCHIVE_OK);
while (zip->entry_bytes_remaining > 0) {
s = (size_t)zip->entry_bytes_remaining;
if (s > a->null_length)
s = a->null_length;
r = _7z_write_data(a, a->nulls, s);
if (r < 0)
return ((int)r);
}
zip->total_bytes_compressed += zip->stream.total_in;
zip->total_bytes_uncompressed += zip->stream.total_out;
zip->cur_file->crc32 = zip->entry_crc32;
zip->cur_file = NULL;
return (ARCHIVE_OK);
}
static int
flush_wbuff(struct archive_write *a)
{
struct _7zip *zip;
int r;
size_t s;
zip = (struct _7zip *)a->format_data;
s = sizeof(zip->wbuff) - zip->wbuff_remaining;
r = __archive_write_output(a, zip->wbuff, s);
if (r != ARCHIVE_OK)
return (r);
zip->wbuff_remaining = sizeof(zip->wbuff);
return (r);
}
static int
copy_out(struct archive_write *a, uint64_t offset, uint64_t length)
{
struct _7zip *zip;
int r;
zip = (struct _7zip *)a->format_data;
if (zip->temp_offset > 0 &&
lseek(zip->temp_fd, offset, SEEK_SET) < 0) {
archive_set_error(&(a->archive), errno, "lseek failed");
return (ARCHIVE_FATAL);
}
while (length) {
size_t rsize;
ssize_t rs;
unsigned char *wb;
if (length > zip->wbuff_remaining)
rsize = zip->wbuff_remaining;
else
rsize = (size_t)length;
wb = zip->wbuff + (sizeof(zip->wbuff) - zip->wbuff_remaining);
rs = read(zip->temp_fd, wb, rsize);
if (rs < 0) {
archive_set_error(&(a->archive), errno,
"Can't read temporary file(%jd)",
(intmax_t)rs);
return (ARCHIVE_FATAL);
}
if (rs == 0) {
archive_set_error(&(a->archive), 0,
"Truncated 7-Zip archive");
return (ARCHIVE_FATAL);
}
zip->wbuff_remaining -= rs;
length -= rs;
if (zip->wbuff_remaining == 0) {
r = flush_wbuff(a);
if (r != ARCHIVE_OK)
return (r);
}
}
return (ARCHIVE_OK);
}
static int
_7z_close(struct archive_write *a)
{
struct _7zip *zip;
unsigned char *wb;
uint64_t header_offset, header_size, header_unpacksize;
uint64_t length;
uint32_t header_crc32;
int r;
zip = (struct _7zip *)a->format_data;
if (zip->total_number_entry > 0) {
struct archive_rb_node *n;
uint64_t data_offset, data_size, data_unpacksize;
unsigned header_compression;
r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH);
if (r < 0)
return (r);
data_offset = 0;
data_size = zip->stream.total_out;
data_unpacksize = zip->stream.total_in;
zip->coder.codec = zip->opt_compression;
zip->coder.prop_size = zip->stream.prop_size;
zip->coder.props = zip->stream.props;
zip->stream.prop_size = 0;
zip->stream.props = NULL;
zip->total_number_nonempty_entry =
zip->total_number_entry - zip->total_number_empty_entry;
/* Connect an empty file list. */
if (zip->empty_list.first != NULL) {
*zip->file_list.last = zip->empty_list.first;
zip->file_list.last = zip->empty_list.last;
}
/* Connect a directory file list. */
ARCHIVE_RB_TREE_FOREACH(n, &(zip->rbtree)) {
file_register(zip, (struct file *)n);
}
/*
* NOTE: 7z command supports just LZMA1, LZMA2 and COPY for
* the compression type for encoding the header.
*/
#if HAVE_LZMA_H
header_compression = _7Z_LZMA1;
/* If the stored file is only one, do not encode the header.
* This is the same way 7z command does. */
if (zip->total_number_entry == 1)
header_compression = _7Z_COPY;
#else
header_compression = _7Z_COPY;
#endif
r = _7z_compression_init_encoder(a, header_compression, 6);
if (r < 0)
return (r);
zip->crc32flg = PRECODE_CRC32;
zip->precode_crc32 = 0;
r = make_header(a, data_offset, data_size, data_unpacksize,
1, &(zip->coder));
if (r < 0)
return (r);
r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH);
if (r < 0)
return (r);
header_offset = data_offset + data_size;
header_size = zip->stream.total_out;
header_crc32 = zip->precode_crc32;
header_unpacksize = zip->stream.total_in;
if (header_compression != _7Z_COPY) {
/*
* Encode the header in order to reduce the size
* of the archive.
*/
free(zip->coder.props);
zip->coder.codec = header_compression;
zip->coder.prop_size = zip->stream.prop_size;
zip->coder.props = zip->stream.props;
zip->stream.prop_size = 0;
zip->stream.props = NULL;
r = _7z_compression_init_encoder(a, _7Z_COPY, 0);
if (r < 0)
return (r);
zip->crc32flg = ENCODED_CRC32;
zip->encoded_crc32 = 0;
/*
* Make EncodedHeader.
*/
r = enc_uint64(a, kEncodedHeader);
if (r < 0)
return (r);
r = make_streamsInfo(a, header_offset, header_size,
header_unpacksize, 1, &(zip->coder), 0,
header_crc32);
if (r < 0)
return (r);
r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH);
if (r < 0)
return (r);
header_offset = header_offset + header_size;
header_size = zip->stream.total_out;
header_crc32 = zip->encoded_crc32;
}
zip->crc32flg = 0;
} else {
header_offset = header_size = 0;
header_crc32 = 0;
}
length = zip->temp_offset;
/*
* Make the zip header on wbuff(write buffer).
*/
wb = zip->wbuff;
zip->wbuff_remaining = sizeof(zip->wbuff);
memcpy(&wb[0], "7z\xBC\xAF\x27\x1C", 6);
wb[6] = 0;/* Major version. */
wb[7] = 3;/* Minor version. */
archive_le64enc(&wb[12], header_offset);/* Next Header Offset */
archive_le64enc(&wb[20], header_size);/* Next Header Size */
archive_le32enc(&wb[28], header_crc32);/* Next Header CRC */
archive_le32enc(&wb[8], crc32(0, &wb[12], 20));/* Start Header CRC */
zip->wbuff_remaining -= 32;
/*
* Read all file contents and an encoded header from the temporary
* file and write out it.
*/
r = copy_out(a, 0, length);
if (r != ARCHIVE_OK)
return (r);
r = flush_wbuff(a);
return (r);
}
/*
* Encode 64 bits value into 7-Zip's encoded UINT64 value.
*/
static int
enc_uint64(struct archive_write *a, uint64_t val)
{
unsigned mask = 0x80;
uint8_t numdata[9];
int i;
numdata[0] = 0;
for (i = 1; i < (int)sizeof(numdata); i++) {
if (val < mask) {
numdata[0] |= (uint8_t)val;
break;
}
numdata[i] = (uint8_t)val;
val >>= 8;
numdata[0] |= mask;
mask >>= 1;
}
return ((int)compress_out(a, numdata, i, ARCHIVE_Z_RUN));
}
static int
make_substreamsInfo(struct archive_write *a, struct coder *coders)
{
struct _7zip *zip = (struct _7zip *)a->format_data;
struct file *file;
int r;
/*
* Make SubStreamsInfo.
*/
r = enc_uint64(a, kSubStreamsInfo);
if (r < 0)
return (r);
if (zip->total_number_nonempty_entry > 1 && coders->codec != _7Z_COPY) {
/*
* Make NumUnPackStream.
*/
r = enc_uint64(a, kNumUnPackStream);
if (r < 0)
return (r);
/* Write numUnpackStreams */
r = enc_uint64(a, zip->total_number_nonempty_entry);
if (r < 0)
return (r);
/*
* Make kSize.
*/
r = enc_uint64(a, kSize);
if (r < 0)
return (r);
file = zip->file_list.first;
for (;file != NULL; file = file->next) {
if (file->next == NULL ||
file->next->size == 0)
break;
r = enc_uint64(a, file->size);
if (r < 0)
return (r);
}
}
/*
* Make CRC.
*/
r = enc_uint64(a, kCRC);
if (r < 0)
return (r);
/* All are defined */
r = enc_uint64(a, 1);
if (r < 0)
return (r);
file = zip->file_list.first;
for (;file != NULL; file = file->next) {
uint8_t crc[4];
if (file->size == 0)
break;
archive_le32enc(crc, file->crc32);
r = (int)compress_out(a, crc, 4, ARCHIVE_Z_RUN);
if (r < 0)
return (r);
}
/* Write End. */
r = enc_uint64(a, kEnd);
if (r < 0)
return (r);
return (ARCHIVE_OK);
}
static int
make_streamsInfo(struct archive_write *a, uint64_t offset, uint64_t pack_size,
uint64_t unpack_size, int num_coder, struct coder *coders, int substrm,
uint32_t header_crc)
{
struct _7zip *zip = (struct _7zip *)a->format_data;
uint8_t codec_buff[8];
int numFolders, fi;
int codec_size;
int i, r;
if (coders->codec == _7Z_COPY)
numFolders = (int)zip->total_number_nonempty_entry;
else
numFolders = 1;
/*
* Make PackInfo.
*/
r = enc_uint64(a, kPackInfo);
if (r < 0)
return (r);
/* Write PackPos. */
r = enc_uint64(a, offset);
if (r < 0)
return (r);
/* Write NumPackStreams. */
r = enc_uint64(a, numFolders);
if (r < 0)
return (r);
/* Make Size. */
r = enc_uint64(a, kSize);
if (r < 0)
return (r);
if (numFolders > 1) {
struct file *file = zip->file_list.first;
for (;file != NULL; file = file->next) {
if (file->size == 0)
break;
r = enc_uint64(a, file->size);
if (r < 0)
return (r);
}
} else {
/* Write size. */
r = enc_uint64(a, pack_size);
if (r < 0)
return (r);
}
r = enc_uint64(a, kEnd);
if (r < 0)
return (r);
/*
* Make UnPackInfo.
*/
r = enc_uint64(a, kUnPackInfo);
if (r < 0)
return (r);
/*
* Make Folder.
*/
r = enc_uint64(a, kFolder);
if (r < 0)
return (r);
/* Write NumFolders. */
r = enc_uint64(a, numFolders);
if (r < 0)
return (r);
/* Write External. */
r = enc_uint64(a, 0);
if (r < 0)
return (r);
for (fi = 0; fi < numFolders; fi++) {
/* Write NumCoders. */
r = enc_uint64(a, num_coder);
if (r < 0)
return (r);
for (i = 0; i < num_coder; i++) {
unsigned codec_id = coders[i].codec;
/* Write Codec flag. */
archive_be64enc(codec_buff, codec_id);
for (codec_size = 8; codec_size > 0; codec_size--) {
if (codec_buff[8 - codec_size])
break;
}
if (codec_size == 0)
codec_size = 1;
if (coders[i].prop_size)
r = enc_uint64(a, codec_size | 0x20);
else
r = enc_uint64(a, codec_size);
if (r < 0)
return (r);
/* Write Codec ID. */
codec_size &= 0x0f;
r = (int)compress_out(a, &codec_buff[8-codec_size],
codec_size, ARCHIVE_Z_RUN);
if (r < 0)
return (r);
if (coders[i].prop_size) {
/* Write Codec property size. */
r = enc_uint64(a, coders[i].prop_size);
if (r < 0)
return (r);
/* Write Codec properties. */
r = (int)compress_out(a, coders[i].props,
coders[i].prop_size, ARCHIVE_Z_RUN);
if (r < 0)
return (r);
}
}
}
/*
* Make CodersUnPackSize.
*/
r = enc_uint64(a, kCodersUnPackSize);
if (r < 0)
return (r);
if (numFolders > 1) {
struct file *file = zip->file_list.first;
for (;file != NULL; file = file->next) {
if (file->size == 0)
break;
r = enc_uint64(a, file->size);
if (r < 0)
return (r);
}
} else {
/* Write UnPackSize. */
r = enc_uint64(a, unpack_size);
if (r < 0)
return (r);
}
if (!substrm) {
uint8_t crc[4];
/*
* Make CRC.
*/
r = enc_uint64(a, kCRC);
if (r < 0)
return (r);
/* All are defined */
r = enc_uint64(a, 1);
if (r < 0)
return (r);
archive_le32enc(crc, header_crc);
r = (int)compress_out(a, crc, 4, ARCHIVE_Z_RUN);
if (r < 0)
return (r);
}
/* Write End. */
r = enc_uint64(a, kEnd);
if (r < 0)
return (r);
if (substrm) {
/*
* Make SubStreamsInfo.
*/
r = make_substreamsInfo(a, coders);
if (r < 0)
return (r);
}
/* Write End. */
r = enc_uint64(a, kEnd);
if (r < 0)
return (r);
return (ARCHIVE_OK);
}
#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
static uint64_t
utcToFiletime(time_t t, long ns)
{
uint64_t fileTime;
fileTime = t;
fileTime *= 10000000;
fileTime += ns / 100;
fileTime += EPOC_TIME;
return (fileTime);
}
static int
make_time(struct archive_write *a, uint8_t type, unsigned flg, int ti)
{
uint8_t filetime[8];
struct _7zip *zip = (struct _7zip *)a->format_data;
struct file *file;
int r;
uint8_t b, mask;
/*
* Make Time Bools.
*/
if (zip->total_number_time_defined[ti] == zip->total_number_entry) {
/* Write Time Type. */
r = enc_uint64(a, type);
if (r < 0)
return (r);
/* Write EmptyStream Size. */
r = enc_uint64(a, 2 + zip->total_number_entry * 8);
if (r < 0)
return (r);
/* All are defined. */
r = enc_uint64(a, 1);
if (r < 0)
return (r);
} else {
if (zip->total_number_time_defined[ti] == 0)
return (ARCHIVE_OK);
/* Write Time Type. */
r = enc_uint64(a, type);
if (r < 0)
return (r);
/* Write EmptyStream Size. */
r = enc_uint64(a, 2 + ((zip->total_number_entry + 7) >> 3)
+ zip->total_number_time_defined[ti] * 8);
if (r < 0)
return (r);
/* All are not defined. */
r = enc_uint64(a, 0);
if (r < 0)
return (r);
b = 0;
mask = 0x80;
file = zip->file_list.first;
for (;file != NULL; file = file->next) {
if (file->flg & flg)
b |= mask;
mask >>= 1;
if (mask == 0) {
r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
if (r < 0)
return (r);
mask = 0x80;
b = 0;
}
}
if (mask != 0x80) {
r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
if (r < 0)
return (r);
}
}
/* External. */
r = enc_uint64(a, 0);
if (r < 0)
return (r);
/*
* Make Times.
*/
file = zip->file_list.first;
for (;file != NULL; file = file->next) {
if ((file->flg & flg) == 0)
continue;
archive_le64enc(filetime, utcToFiletime(file->times[ti].time,
file->times[ti].time_ns));
r = (int)compress_out(a, filetime, 8, ARCHIVE_Z_RUN);
if (r < 0)
return (r);
}
return (ARCHIVE_OK);
}
static int
make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size,
uint64_t unpack_size, int codernum, struct coder *coders)
{
struct _7zip *zip = (struct _7zip *)a->format_data;
struct file *file;
int r;
uint8_t b, mask;
/*
* Make FilesInfo.
*/
r = enc_uint64(a, kHeader);
if (r < 0)
return (r);
/*
* If there are empty files only, do not write MainStreamInfo.
*/
if (zip->total_number_nonempty_entry) {
/*
* Make MainStreamInfo.
*/
r = enc_uint64(a, kMainStreamsInfo);
if (r < 0)
return (r);
r = make_streamsInfo(a, offset, pack_size, unpack_size,
codernum, coders, 1, 0);
if (r < 0)
return (r);
}
/*
* Make FilesInfo.
*/
r = enc_uint64(a, kFilesInfo);
if (r < 0)
return (r);
/* Write numFiles. */
r = enc_uint64(a, zip->total_number_entry);
if (r < 0)
return (r);
if (zip->total_number_empty_entry > 0) {
/* Make EmptyStream. */
r = enc_uint64(a, kEmptyStream);
if (r < 0)
return (r);
/* Write EmptyStream Size. */
r = enc_uint64(a, (zip->total_number_entry+7)>>3);
if (r < 0)
return (r);
b = 0;
mask = 0x80;
file = zip->file_list.first;
for (;file != NULL; file = file->next) {
if (file->size == 0)
b |= mask;
mask >>= 1;
if (mask == 0) {
r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
if (r < 0)
return (r);
mask = 0x80;
b = 0;
}
}
if (mask != 0x80) {
r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
if (r < 0)
return (r);
}
}
if (zip->total_number_empty_entry > zip->total_number_dir_entry) {
/* Make EmptyFile. */
r = enc_uint64(a, kEmptyFile);
if (r < 0)
return (r);
/* Write EmptyFile Size. */
r = enc_uint64(a, (zip->total_number_empty_entry + 7) >> 3);
if (r < 0)
return (r);
b = 0;
mask = 0x80;
file = zip->file_list.first;
for (;file != NULL; file = file->next) {
if (file->size)
continue;
if (!file->dir)
b |= mask;
mask >>= 1;
if (mask == 0) {
r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
if (r < 0)
return (r);
mask = 0x80;
b = 0;
}
}
if (mask != 0x80) {
r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
if (r < 0)
return (r);
}
}
/* Make Name. */
r = enc_uint64(a, kName);
if (r < 0)
return (r);
/* Write Nume size. */
r = enc_uint64(a, zip->total_bytes_entry_name+1);
if (r < 0)
return (r);
/* Write dmy byte. */
r = enc_uint64(a, 0);
if (r < 0)
return (r);
file = zip->file_list.first;
for (;file != NULL; file = file->next) {
r = (int)compress_out(a, file->utf16name, file->name_len+2,
ARCHIVE_Z_RUN);
if (r < 0)
return (r);
}
/* Make MTime. */
r = make_time(a, kMTime, MTIME_IS_SET, MTIME);
if (r < 0)
return (r);
/* Make CTime. */
r = make_time(a, kCTime, CTIME_IS_SET, CTIME);
if (r < 0)
return (r);
/* Make ATime. */
r = make_time(a, kATime, ATIME_IS_SET, ATIME);
if (r < 0)
return (r);
/* Make Attributes. */
r = enc_uint64(a, kAttributes);
if (r < 0)
return (r);
/* Write Attributes size. */
r = enc_uint64(a, 2 + zip->total_number_entry * 4);
if (r < 0)
return (r);
/* Write "All Are Defined". */
r = enc_uint64(a, 1);
if (r < 0)
return (r);
/* Write dmy byte. */
r = enc_uint64(a, 0);
if (r < 0)
return (r);
file = zip->file_list.first;
for (;file != NULL; file = file->next) {
/*
* High 16bits is unix mode.
* Low 16bits is Windows attributes.
*/
uint32_t encattr, attr;
if (file->dir)
attr = 0x8010;
else
attr = 0x8020;
if ((file->mode & 0222) == 0)
attr |= 1;/* Read Only. */
attr |= ((uint32_t)file->mode) << 16;
archive_le32enc(&encattr, attr);
r = (int)compress_out(a, &encattr, 4, ARCHIVE_Z_RUN);
if (r < 0)
return (r);
}
/* Write End. */
r = enc_uint64(a, kEnd);
if (r < 0)
return (r);
/* Write End. */
r = enc_uint64(a, kEnd);
if (r < 0)
return (r);
return (ARCHIVE_OK);
}
static int
_7z_free(struct archive_write *a)
{
struct _7zip *zip = (struct _7zip *)a->format_data;
file_free_register(zip);
compression_end(&(a->archive), &(zip->stream));
free(zip->coder.props);
free(zip);
return (ARCHIVE_OK);
}
static int
file_cmp_node(const struct archive_rb_node *n1,
const struct archive_rb_node *n2)
{
const struct file *f1 = (const struct file *)n1;
const struct file *f2 = (const struct file *)n2;
if (f1->name_len == f2->name_len)
return (memcmp(f1->utf16name, f2->utf16name, f1->name_len));
return (f1->name_len > f2->name_len)?1:-1;
}
static int
file_cmp_key(const struct archive_rb_node *n, const void *key)
{
const struct file *f = (const struct file *)n;
return (f->name_len - *(const char *)key);
}
static int
file_new(struct archive_write *a, struct archive_entry *entry,
struct file **newfile)
{
struct _7zip *zip;
struct file *file;
const char *u16;
size_t u16len;
int ret = ARCHIVE_OK;
zip = (struct _7zip *)a->format_data;
*newfile = NULL;
file = calloc(1, sizeof(*file));
if (file == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory");
return (ARCHIVE_FATAL);
}
if (0 > archive_entry_pathname_l(entry, &u16, &u16len, zip->sconv)) {
if (errno == ENOMEM) {
free(file);
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for UTF-16LE");
return (ARCHIVE_FATAL);
}
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"A filename cannot be converted to UTF-16LE;"
"You should disable making Joliet extension");
ret = ARCHIVE_WARN;
}
file->utf16name = malloc(u16len + 2);
if (file->utf16name == NULL) {
free(file);
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for Name");
return (ARCHIVE_FATAL);
}
memcpy(file->utf16name, u16, u16len);
file->utf16name[u16len+0] = 0;
file->utf16name[u16len+1] = 0;
file->name_len = (unsigned)u16len;
file->mode = archive_entry_mode(entry);
if (archive_entry_filetype(entry) == AE_IFREG)
file->size = archive_entry_size(entry);
else
archive_entry_set_size(entry, 0);
if (archive_entry_filetype(entry) == AE_IFDIR)
file->dir = 1;
else if (archive_entry_filetype(entry) == AE_IFLNK)
file->size = strlen(archive_entry_symlink(entry));
if (archive_entry_mtime_is_set(entry)) {
file->flg |= MTIME_IS_SET;
file->times[MTIME].time = archive_entry_mtime(entry);
file->times[MTIME].time_ns = archive_entry_mtime_nsec(entry);
}
if (archive_entry_atime_is_set(entry)) {
file->flg |= ATIME_IS_SET;
file->times[ATIME].time = archive_entry_atime(entry);
file->times[ATIME].time_ns = archive_entry_atime_nsec(entry);
}
if (archive_entry_ctime_is_set(entry)) {
file->flg |= CTIME_IS_SET;
file->times[CTIME].time = archive_entry_ctime(entry);
file->times[CTIME].time_ns = archive_entry_ctime_nsec(entry);
}
*newfile = file;
return (ret);
}
static void
file_free(struct file *file)
{
free(file->utf16name);
free(file);
}
static void
file_register(struct _7zip *zip, struct file *file)
{
file->next = NULL;
*zip->file_list.last = file;
zip->file_list.last = &(file->next);
}
static void
file_init_register(struct _7zip *zip)
{
zip->file_list.first = NULL;
zip->file_list.last = &(zip->file_list.first);
}
static void
file_free_register(struct _7zip *zip)
{
struct file *file, *file_next;
file = zip->file_list.first;
while (file != NULL) {
file_next = file->next;
file_free(file);
file = file_next;
}
}
static void
file_register_empty(struct _7zip *zip, struct file *file)
{
file->next = NULL;
*zip->empty_list.last = file;
zip->empty_list.last = &(file->next);
}
static void
file_init_register_empty(struct _7zip *zip)
{
zip->empty_list.first = NULL;
zip->empty_list.last = &(zip->empty_list.first);
}
#if !defined(HAVE_ZLIB_H) || !defined(HAVE_BZLIB_H) ||\
!defined(BZ_CONFIG_ERROR) || !defined(HAVE_LZMA_H)
static int
compression_unsupported_encoder(struct archive *a,
struct la_zstream *lastrm, const char *name)
{
archive_set_error(a, ARCHIVE_ERRNO_MISC,
"%s compression not supported on this platform", name);
lastrm->valid = 0;
lastrm->real_stream = NULL;
return (ARCHIVE_FAILED);
}
#endif
/*
* _7_COPY compressor.
*/
static int
compression_init_encoder_copy(struct archive *a, struct la_zstream *lastrm)
{
if (lastrm->valid)
compression_end(a, lastrm);
lastrm->valid = 1;
lastrm->code = compression_code_copy;
lastrm->end = compression_end_copy;
return (ARCHIVE_OK);
}
static int
compression_code_copy(struct archive *a,
struct la_zstream *lastrm, enum la_zaction action)
{
size_t bytes;
(void)a; /* UNUSED */
if (lastrm->avail_out > lastrm->avail_in)
bytes = lastrm->avail_in;
else
bytes = lastrm->avail_out;
if (bytes) {
memcpy(lastrm->next_out, lastrm->next_in, bytes);
lastrm->next_in += bytes;
lastrm->avail_in -= bytes;
lastrm->total_in += bytes;
lastrm->next_out += bytes;
lastrm->avail_out -= bytes;
lastrm->total_out += bytes;
}
if (action == ARCHIVE_Z_FINISH && lastrm->avail_in == 0)
return (ARCHIVE_EOF);
return (ARCHIVE_OK);
}
static int
compression_end_copy(struct archive *a, struct la_zstream *lastrm)
{
(void)a; /* UNUSED */
lastrm->valid = 0;
return (ARCHIVE_OK);
}
/*
* _7_DEFLATE compressor.
*/
#ifdef HAVE_ZLIB_H
static int
compression_init_encoder_deflate(struct archive *a,
struct la_zstream *lastrm, int level, int withheader)
{
z_stream *strm;
if (lastrm->valid)
compression_end(a, lastrm);
strm = calloc(1, sizeof(*strm));
if (strm == NULL) {
archive_set_error(a, ENOMEM,
"Can't allocate memory for gzip stream");
return (ARCHIVE_FATAL);
}
/* zlib.h is not const-correct, so we need this one bit
* of ugly hackery to convert a const * pointer to
* a non-const pointer. */
strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
strm->avail_in = (uInt)lastrm->avail_in;
strm->total_in = (uLong)lastrm->total_in;
strm->next_out = lastrm->next_out;
strm->avail_out = (uInt)lastrm->avail_out;
strm->total_out = (uLong)lastrm->total_out;
if (deflateInit2(strm, level, Z_DEFLATED,
(withheader)?15:-15,
8, Z_DEFAULT_STRATEGY) != Z_OK) {
free(strm);
lastrm->real_stream = NULL;
archive_set_error(a, ARCHIVE_ERRNO_MISC,
"Internal error initializing compression library");
return (ARCHIVE_FATAL);
}
lastrm->real_stream = strm;
lastrm->valid = 1;
lastrm->code = compression_code_deflate;
lastrm->end = compression_end_deflate;
return (ARCHIVE_OK);
}
static int
compression_code_deflate(struct archive *a,
struct la_zstream *lastrm, enum la_zaction action)
{
z_stream *strm;
int r;
strm = (z_stream *)lastrm->real_stream;
/* zlib.h is not const-correct, so we need this one bit
* of ugly hackery to convert a const * pointer to
* a non-const pointer. */
strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
strm->avail_in = (uInt)lastrm->avail_in;
strm->total_in = (uLong)lastrm->total_in;
strm->next_out = lastrm->next_out;
strm->avail_out = (uInt)lastrm->avail_out;
strm->total_out = (uLong)lastrm->total_out;
r = deflate(strm,
(action == ARCHIVE_Z_FINISH)? Z_FINISH: Z_NO_FLUSH);
lastrm->next_in = strm->next_in;
lastrm->avail_in = strm->avail_in;
lastrm->total_in = strm->total_in;
lastrm->next_out = strm->next_out;
lastrm->avail_out = strm->avail_out;
lastrm->total_out = strm->total_out;
switch (r) {
case Z_OK:
return (ARCHIVE_OK);
case Z_STREAM_END:
return (ARCHIVE_EOF);
default:
archive_set_error(a, ARCHIVE_ERRNO_MISC,
"GZip compression failed:"
" deflate() call returned status %d", r);
return (ARCHIVE_FATAL);
}
}
static int
compression_end_deflate(struct archive *a, struct la_zstream *lastrm)
{
z_stream *strm;
int r;
strm = (z_stream *)lastrm->real_stream;
r = deflateEnd(strm);
free(strm);
lastrm->real_stream = NULL;
lastrm->valid = 0;
if (r != Z_OK) {
archive_set_error(a, ARCHIVE_ERRNO_MISC,
"Failed to clean up compressor");
return (ARCHIVE_FATAL);
}
return (ARCHIVE_OK);
}
#else
static int
compression_init_encoder_deflate(struct archive *a,
struct la_zstream *lastrm, int level, int withheader)
{
(void) level; /* UNUSED */
(void) withheader; /* UNUSED */
if (lastrm->valid)
compression_end(a, lastrm);
return (compression_unsupported_encoder(a, lastrm, "deflate"));
}
#endif
/*
* _7_BZIP2 compressor.
*/
#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
static int
compression_init_encoder_bzip2(struct archive *a,
struct la_zstream *lastrm, int level)
{
bz_stream *strm;
if (lastrm->valid)
compression_end(a, lastrm);
strm = calloc(1, sizeof(*strm));
if (strm == NULL) {
archive_set_error(a, ENOMEM,
"Can't allocate memory for bzip2 stream");
return (ARCHIVE_FATAL);
}
/* bzlib.h is not const-correct, so we need this one bit
* of ugly hackery to convert a const * pointer to
* a non-const pointer. */
strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
strm->avail_in = lastrm->avail_in;
strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
strm->next_out = (char *)lastrm->next_out;
strm->avail_out = lastrm->avail_out;
strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) {
free(strm);
lastrm->real_stream = NULL;
archive_set_error(a, ARCHIVE_ERRNO_MISC,
"Internal error initializing compression library");
return (ARCHIVE_FATAL);
}
lastrm->real_stream = strm;
lastrm->valid = 1;
lastrm->code = compression_code_bzip2;
lastrm->end = compression_end_bzip2;
return (ARCHIVE_OK);
}
static int
compression_code_bzip2(struct archive *a,
struct la_zstream *lastrm, enum la_zaction action)
{
bz_stream *strm;
int r;
strm = (bz_stream *)lastrm->real_stream;
/* bzlib.h is not const-correct, so we need this one bit
* of ugly hackery to convert a const * pointer to
* a non-const pointer. */
strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
strm->avail_in = lastrm->avail_in;
strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
strm->next_out = (char *)lastrm->next_out;
strm->avail_out = lastrm->avail_out;
strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
r = BZ2_bzCompress(strm,
(action == ARCHIVE_Z_FINISH)? BZ_FINISH: BZ_RUN);
lastrm->next_in = (const unsigned char *)strm->next_in;
lastrm->avail_in = strm->avail_in;
lastrm->total_in =
(((uint64_t)(uint32_t)strm->total_in_hi32) << 32)
+ (uint64_t)(uint32_t)strm->total_in_lo32;
lastrm->next_out = (unsigned char *)strm->next_out;
lastrm->avail_out = strm->avail_out;
lastrm->total_out =
(((uint64_t)(uint32_t)strm->total_out_hi32) << 32)
+ (uint64_t)(uint32_t)strm->total_out_lo32;
switch (r) {
case BZ_RUN_OK: /* Non-finishing */
case BZ_FINISH_OK: /* Finishing: There's more work to do */
return (ARCHIVE_OK);
case BZ_STREAM_END: /* Finishing: all done */
/* Only occurs in finishing case */
return (ARCHIVE_EOF);
default:
/* Any other return value indicates an error */
archive_set_error(a, ARCHIVE_ERRNO_MISC,
"Bzip2 compression failed:"
" BZ2_bzCompress() call returned status %d", r);
return (ARCHIVE_FATAL);
}
}
static int
compression_end_bzip2(struct archive *a, struct la_zstream *lastrm)
{
bz_stream *strm;
int r;
strm = (bz_stream *)lastrm->real_stream;
r = BZ2_bzCompressEnd(strm);
free(strm);
lastrm->real_stream = NULL;
lastrm->valid = 0;
if (r != BZ_OK) {
archive_set_error(a, ARCHIVE_ERRNO_MISC,
"Failed to clean up compressor");
return (ARCHIVE_FATAL);
}
return (ARCHIVE_OK);
}
#else
static int
compression_init_encoder_bzip2(struct archive *a,
struct la_zstream *lastrm, int level)
{
(void) level; /* UNUSED */
if (lastrm->valid)
compression_end(a, lastrm);
return (compression_unsupported_encoder(a, lastrm, "bzip2"));
}
#endif
/*
* _7_LZMA1, _7_LZMA2 compressor.
*/
#if defined(HAVE_LZMA_H)
static int
compression_init_encoder_lzma(struct archive *a,
struct la_zstream *lastrm, int level, uint64_t filter_id)
{
static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;
lzma_stream *strm;
lzma_filter *lzmafilters;
lzma_options_lzma lzma_opt;
int r;
if (lastrm->valid)
compression_end(a, lastrm);
strm = calloc(1, sizeof(*strm) + sizeof(*lzmafilters) * 2);
if (strm == NULL) {
archive_set_error(a, ENOMEM,
"Can't allocate memory for lzma stream");
return (ARCHIVE_FATAL);
}
lzmafilters = (lzma_filter *)(strm+1);
if (level > 6)
level = 6;
if (lzma_lzma_preset(&lzma_opt, level)) {
free(strm);
lastrm->real_stream = NULL;
archive_set_error(a, ENOMEM,
"Internal error initializing compression library");
return (ARCHIVE_FATAL);
}
lzmafilters[0].id = filter_id;
lzmafilters[0].options = &lzma_opt;
lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
r = lzma_properties_size(&(lastrm->prop_size), lzmafilters);
if (r != LZMA_OK) {
free(strm);
lastrm->real_stream = NULL;
archive_set_error(a, ARCHIVE_ERRNO_MISC,
"lzma_properties_size failed");
return (ARCHIVE_FATAL);
}
if (lastrm->prop_size) {
lastrm->props = malloc(lastrm->prop_size);
if (lastrm->props == NULL) {
free(strm);
lastrm->real_stream = NULL;
archive_set_error(a, ENOMEM,
"Cannot allocate memory");
return (ARCHIVE_FATAL);
}
r = lzma_properties_encode(lzmafilters, lastrm->props);
if (r != LZMA_OK) {
free(strm);
lastrm->real_stream = NULL;
archive_set_error(a, ARCHIVE_ERRNO_MISC,
"lzma_properties_encode failed");
return (ARCHIVE_FATAL);
}
}
*strm = lzma_init_data;
r = lzma_raw_encoder(strm, lzmafilters);
switch (r) {
case LZMA_OK:
lastrm->real_stream = strm;
lastrm->valid = 1;
lastrm->code = compression_code_lzma;
lastrm->end = compression_end_lzma;
r = ARCHIVE_OK;
break;
case LZMA_MEM_ERROR:
free(strm);
lastrm->real_stream = NULL;
archive_set_error(a, ENOMEM,
"Internal error initializing compression library: "
"Cannot allocate memory");
r = ARCHIVE_FATAL;
break;
default:
free(strm);
lastrm->real_stream = NULL;
archive_set_error(a, ARCHIVE_ERRNO_MISC,
"Internal error initializing compression library: "
"It's a bug in liblzma");
r = ARCHIVE_FATAL;
break;
}
return (r);
}
static int
compression_init_encoder_lzma1(struct archive *a,
struct la_zstream *lastrm, int level)
{
return compression_init_encoder_lzma(a, lastrm, level,
LZMA_FILTER_LZMA1);
}
static int
compression_init_encoder_lzma2(struct archive *a,
struct la_zstream *lastrm, int level)
{
return compression_init_encoder_lzma(a, lastrm, level,
LZMA_FILTER_LZMA2);
}
static int
compression_code_lzma(struct archive *a,
struct la_zstream *lastrm, enum la_zaction action)
{
lzma_stream *strm;
int r;
strm = (lzma_stream *)lastrm->real_stream;
strm->next_in = lastrm->next_in;
strm->avail_in = lastrm->avail_in;
strm->total_in = lastrm->total_in;
strm->next_out = lastrm->next_out;
strm->avail_out = lastrm->avail_out;
strm->total_out = lastrm->total_out;
r = lzma_code(strm,
(action == ARCHIVE_Z_FINISH)? LZMA_FINISH: LZMA_RUN);
lastrm->next_in = strm->next_in;
lastrm->avail_in = strm->avail_in;
lastrm->total_in = strm->total_in;
lastrm->next_out = strm->next_out;
lastrm->avail_out = strm->avail_out;
lastrm->total_out = strm->total_out;
switch (r) {
case LZMA_OK:
/* Non-finishing case */
return (ARCHIVE_OK);
case LZMA_STREAM_END:
/* This return can only occur in finishing case. */
return (ARCHIVE_EOF);
case LZMA_MEMLIMIT_ERROR:
archive_set_error(a, ENOMEM,
"lzma compression error:"
" %ju MiB would have been needed",
(uintmax_t)((lzma_memusage(strm) + 1024 * 1024 -1)
/ (1024 * 1024)));
return (ARCHIVE_FATAL);
default:
/* Any other return value indicates an error */
archive_set_error(a, ARCHIVE_ERRNO_MISC,
"lzma compression failed:"
" lzma_code() call returned status %d", r);
return (ARCHIVE_FATAL);
}
}
static int
compression_end_lzma(struct archive *a, struct la_zstream *lastrm)
{
lzma_stream *strm;
(void)a; /* UNUSED */
strm = (lzma_stream *)lastrm->real_stream;
lzma_end(strm);
free(strm);
lastrm->valid = 0;
lastrm->real_stream = NULL;
return (ARCHIVE_OK);
}
#else
static int
compression_init_encoder_lzma1(struct archive *a,
struct la_zstream *lastrm, int level)
{
(void) level; /* UNUSED */
if (lastrm->valid)
compression_end(a, lastrm);
return (compression_unsupported_encoder(a, lastrm, "lzma"));
}
static int
compression_init_encoder_lzma2(struct archive *a,
struct la_zstream *lastrm, int level)
{
(void) level; /* UNUSED */
if (lastrm->valid)
compression_end(a, lastrm);
return (compression_unsupported_encoder(a, lastrm, "lzma"));
}
#endif
/*
* _7_PPMD compressor.
*/
static void *
ppmd_alloc(void *p, size_t size)
{
(void)p;
return malloc(size);
}
static void
ppmd_free(void *p, void *address)
{
(void)p;
free(address);
}
static ISzAlloc g_szalloc = { ppmd_alloc, ppmd_free };
static void
ppmd_write(void *p, Byte b)
{
struct archive_write *a = ((IByteOut *)p)->a;
struct _7zip *zip = (struct _7zip *)(a->format_data);
struct la_zstream *lastrm = &(zip->stream);
struct ppmd_stream *strm;
if (lastrm->avail_out) {
*lastrm->next_out++ = b;
lastrm->avail_out--;
lastrm->total_out++;
return;
}
strm = (struct ppmd_stream *)lastrm->real_stream;
if (strm->buff_ptr < strm->buff_end) {
*strm->buff_ptr++ = b;
strm->buff_bytes++;
}
}
static int
compression_init_encoder_ppmd(struct archive *a,
struct la_zstream *lastrm, unsigned maxOrder, uint32_t msize)
{
struct ppmd_stream *strm;
uint8_t *props;
int r;
if (lastrm->valid)
compression_end(a, lastrm);
strm = calloc(1, sizeof(*strm));
if (strm == NULL) {
archive_set_error(a, ENOMEM,
"Can't allocate memory for PPMd");
return (ARCHIVE_FATAL);
}
strm->buff = malloc(32);
if (strm->buff == NULL) {
free(strm);
archive_set_error(a, ENOMEM,
"Can't allocate memory for PPMd");
return (ARCHIVE_FATAL);
}
strm->buff_ptr = strm->buff;
strm->buff_end = strm->buff + 32;
props = malloc(1+4);
if (props == NULL) {
free(strm->buff);
free(strm);
archive_set_error(a, ENOMEM,
"Coludn't allocate memory for PPMd");
return (ARCHIVE_FATAL);
}
props[0] = maxOrder;
archive_le32enc(props+1, msize);
__archive_ppmd7_functions.Ppmd7_Construct(&strm->ppmd7_context);
r = __archive_ppmd7_functions.Ppmd7_Alloc(
&strm->ppmd7_context, msize, &g_szalloc);
if (r == 0) {
free(strm->buff);
free(strm);
free(props);
archive_set_error(a, ENOMEM,
"Coludn't allocate memory for PPMd");
return (ARCHIVE_FATAL);
}
__archive_ppmd7_functions.Ppmd7_Init(&(strm->ppmd7_context), maxOrder);
strm->byteout.a = (struct archive_write *)a;
strm->byteout.Write = ppmd_write;
strm->range_enc.Stream = &(strm->byteout);
__archive_ppmd7_functions.Ppmd7z_RangeEnc_Init(&(strm->range_enc));
strm->stat = 0;
lastrm->real_stream = strm;
lastrm->valid = 1;
lastrm->code = compression_code_ppmd;
lastrm->end = compression_end_ppmd;
lastrm->prop_size = 5;
lastrm->props = props;
return (ARCHIVE_OK);
}
static int
compression_code_ppmd(struct archive *a,
struct la_zstream *lastrm, enum la_zaction action)
{
struct ppmd_stream *strm;
(void)a; /* UNUSED */
strm = (struct ppmd_stream *)lastrm->real_stream;
/* Copy encoded data if there are remaining bytes from previous call. */
if (strm->buff_bytes) {
uint8_t *p = strm->buff_ptr - strm->buff_bytes;
while (lastrm->avail_out && strm->buff_bytes) {
*lastrm->next_out++ = *p++;
lastrm->avail_out--;
lastrm->total_out++;
strm->buff_bytes--;
}
if (strm->buff_bytes)
return (ARCHIVE_OK);
if (strm->stat == 1)
return (ARCHIVE_EOF);
strm->buff_ptr = strm->buff;
}
while (lastrm->avail_in && lastrm->avail_out) {
__archive_ppmd7_functions.Ppmd7_EncodeSymbol(
&(strm->ppmd7_context), &(strm->range_enc),
*lastrm->next_in++);
lastrm->avail_in--;
lastrm->total_in++;
}
if (lastrm->avail_in == 0 && action == ARCHIVE_Z_FINISH) {
__archive_ppmd7_functions.Ppmd7z_RangeEnc_FlushData(
&(strm->range_enc));
strm->stat = 1;
/* Return EOF if there are no remaining bytes. */
if (strm->buff_bytes == 0)
return (ARCHIVE_EOF);
}
return (ARCHIVE_OK);
}
static int
compression_end_ppmd(struct archive *a, struct la_zstream *lastrm)
{
struct ppmd_stream *strm;
(void)a; /* UNUSED */
strm = (struct ppmd_stream *)lastrm->real_stream;
__archive_ppmd7_functions.Ppmd7_Free(&strm->ppmd7_context, &g_szalloc);
free(strm->buff);
free(strm);
lastrm->real_stream = NULL;
lastrm->valid = 0;
return (ARCHIVE_OK);
}
/*
* Universal compressor initializer.
*/
static int
_7z_compression_init_encoder(struct archive_write *a, unsigned compression,
int compression_level)
{
struct _7zip *zip;
int r;
zip = (struct _7zip *)a->format_data;
switch (compression) {
case _7Z_DEFLATE:
r = compression_init_encoder_deflate(
&(a->archive), &(zip->stream),
compression_level, 0);
break;
case _7Z_BZIP2:
r = compression_init_encoder_bzip2(
&(a->archive), &(zip->stream),
compression_level);
break;
case _7Z_LZMA1:
r = compression_init_encoder_lzma1(
&(a->archive), &(zip->stream),
compression_level);
break;
case _7Z_LZMA2:
r = compression_init_encoder_lzma2(
&(a->archive), &(zip->stream),
compression_level);
break;
case _7Z_PPMD:
r = compression_init_encoder_ppmd(
&(a->archive), &(zip->stream),
PPMD7_DEFAULT_ORDER, PPMD7_DEFAULT_MEM_SIZE);
break;
case _7Z_COPY:
default:
r = compression_init_encoder_copy(
&(a->archive), &(zip->stream));
break;
}
if (r == ARCHIVE_OK) {
zip->stream.total_in = 0;
zip->stream.next_out = zip->wbuff;
zip->stream.avail_out = sizeof(zip->wbuff);
zip->stream.total_out = 0;
}
return (r);
}
static int
compression_code(struct archive *a, struct la_zstream *lastrm,
enum la_zaction action)
{
if (lastrm->valid)
return (lastrm->code(a, lastrm, action));
return (ARCHIVE_OK);
}
static int
compression_end(struct archive *a, struct la_zstream *lastrm)
{
if (lastrm->valid) {
lastrm->prop_size = 0;
free(lastrm->props);
lastrm->props = NULL;
return (lastrm->end(a, lastrm));
}
return (ARCHIVE_OK);
}
Jump to Line
Something went wrong with that request. Please try again.