2325 lines
55 KiB
C
2325 lines
55 KiB
C
/*-
|
|
* 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 <cm_bzlib.h>
|
|
#endif
|
|
#if HAVE_LZMA_H
|
|
#include <cm_lzma.h>
|
|
#endif
|
|
#ifdef HAVE_ZLIB_H
|
|
#include <cm_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);
|
|
}
|
|
|
|
|