CMake/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c
Brad King d4a9e334d3 Include bzlib.h consistently across CMake build (#10950)
Use the approach originally used in commit f91b3c1d (Add options to
build with system utility libraries, 2006-10-19) for all other
third-party libraries.  Create a "cm_bzlib.h" header wrapper that
robustly includes the header from the bzip2 library chosen for the CMake
build (either builtin or system version).  Include the header wrapper
anywhere we need the API provided by <bzlib.h>.
2012-01-13 14:05:36 -05:00

3172 lines
77 KiB
C

/*-
* Copyright (c) 2010-2011 Michihiro NAKAJIMA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "archive_platform.h"
__FBSDID("$FreeBSD$");
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include <stdlib.h>
#if HAVE_LIBXML_XMLWRITER_H
#include <libxml/xmlwriter.h>
#endif
#ifdef HAVE_BZLIB_H
#include <cm_bzlib.h>
#endif
#if HAVE_LZMA_H
#include <lzma.h>
#endif
#ifdef HAVE_ZLIB_H
#include <cm_zlib.h>
#endif
#include "archive.h"
#include "archive_crypto_private.h"
#include "archive_endian.h"
#include "archive_entry.h"
#include "archive_entry_locale.h"
#include "archive_private.h"
#include "archive_rb.h"
#include "archive_string.h"
#include "archive_write_private.h"
/*
* Differences to xar utility.
* - Subdocument is not supported yet.
* - ACL is not supported yet.
* - When writing an XML element <link type="<file-type>">, <file-type>
* which is a file type a symbolic link is referencing is always marked
* as "broken". Xar utility uses stat(2) to get the file type, but, in
* libarcive format writer, we should not use it; if it is needed, we
* should get about it at archive_read_disk.c.
* - It is possible to appear both <flags> and <ext2> elements.
* Xar utility generates <flags> on BSD platform and <ext2> on Linux
* platform.
*
*/
#if !(defined(HAVE_LIBXML_XMLWRITER_H) && defined(LIBXML_VERSION) &&\
LIBXML_VERSION >= 20703) ||\
!defined(HAVE_ZLIB_H) || \
!defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1)
/*
* xar needs several external libraries.
* o libxml2
* o openssl or MD5/SHA1 hash function
* o zlib
* o bzlib2 (option)
* o liblzma (option)
*/
int
archive_write_set_format_xar(struct archive *_a)
{
struct archive_write *a = (struct archive_write *)_a;
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Xar not supported on this platform");
return (ARCHIVE_WARN);
}
#else /* Support xar format */
/*#define DEBUG_PRINT_TOC 1 */
#define HEADER_MAGIC 0x78617221
#define HEADER_SIZE 28
#define HEADER_VERSION 1
enum sumalg {
CKSUM_NONE = 0,
CKSUM_SHA1 = 1,
CKSUM_MD5 = 2
};
#define MD5_SIZE 16
#define SHA1_SIZE 20
#define MAX_SUM_SIZE 20
#define MD5_NAME "md5"
#define SHA1_NAME "sha1"
enum enctype {
NONE,
GZIP,
BZIP2,
LZMA,
XZ,
};
struct chksumwork {
enum sumalg alg;
#ifdef ARCHIVE_HAS_MD5
archive_md5_ctx md5ctx;
#endif
#ifdef ARCHIVE_HAS_SHA1
archive_sha1_ctx sha1ctx;
#endif
};
enum la_zaction {
ARCHIVE_Z_FINISH,
ARCHIVE_Z_RUN
};
/*
* Universal zstream.
*/
struct la_zstream {
const unsigned char *next_in;
size_t avail_in;
uint64_t total_in;
unsigned char *next_out;
size_t avail_out;
uint64_t total_out;
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);
};
struct chksumval {
enum sumalg alg;
size_t len;
unsigned char val[MAX_SUM_SIZE];
};
struct heap_data {
int id;
struct heap_data *next;
uint64_t temp_offset;
uint64_t length; /* archived size. */
uint64_t size; /* extracted size. */
enum enctype compression;
struct chksumval a_sum; /* archived checksum. */
struct chksumval e_sum; /* extracted checksum. */
};
struct file {
struct archive_rb_node rbnode;
int id;
struct archive_entry *entry;
struct archive_rb_tree rbtree;
struct file *next;
struct file *chnext;
struct file *hlnext;
/* For hardlinked files.
* Use only when archive_entry_nlink() > 1 */
struct file *hardlink_target;
struct file *parent; /* parent directory entry */
/*
* To manage sub directory files.
* We use 'chnext' a menber of struct file to chain.
*/
struct {
struct file *first;
struct file **last;
} children;
/* For making a directory tree. */
struct archive_string parentdir;
struct archive_string basename;
struct archive_string symlink;
int ea_idx;
struct {
struct heap_data *first;
struct heap_data **last;
} xattr;
struct heap_data data;
struct archive_string script;
int virtual:1;
int dir:1;
};
struct hardlink {
struct archive_rb_node rbnode;
int nlink;
struct {
struct file *first;
struct file **last;
} file_list;
};
struct xar {
int temp_fd;
uint64_t temp_offset;
int file_idx;
struct file *root;
struct file *cur_dirent;
struct archive_string cur_dirstr;
struct file *cur_file;
uint64_t bytes_remaining;
struct archive_string tstr;
struct archive_string vstr;
enum sumalg opt_toc_sumalg;
enum sumalg opt_sumalg;
enum enctype opt_compression;
int opt_compression_level;
struct chksumwork a_sumwrk; /* archived checksum. */
struct chksumwork e_sumwrk; /* extracted checksum. */
struct la_zstream stream;
struct archive_string_conv *sconv;
/*
* Compressed data buffer.
*/
unsigned char wbuff[1024 * 64];
size_t wbuff_remaining;
struct heap_data toc;
/*
* The list of all file entries 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;
/*
* The list of hard-linked file entries.
* We use 'hlnext' a menber of struct file to chain.
*/
struct archive_rb_tree hardlink_rbtree;
};
static int xar_options(struct archive_write *,
const char *, const char *);
static int xar_write_header(struct archive_write *,
struct archive_entry *);
static ssize_t xar_write_data(struct archive_write *,
const void *, size_t);
static int xar_finish_entry(struct archive_write *);
static int xar_close(struct archive_write *);
static int xar_free(struct archive_write *);
static struct file *file_new(struct archive_write *a, struct archive_entry *);
static void file_free(struct file *);
static struct file *file_create_virtual_dir(struct archive_write *a, struct xar *,
const char *);
static int file_add_child_tail(struct file *, struct file *);
static struct file *file_find_child(struct file *, const char *);
static int file_gen_utility_names(struct archive_write *,
struct file *);
static int get_path_component(char *, int, const char *);
static int file_tree(struct archive_write *, struct file **);
static void file_register(struct xar *, struct file *);
static void file_init_register(struct xar *);
static void file_free_register(struct xar *);
static int file_register_hardlink(struct archive_write *,
struct file *);
static void file_connect_hardlink_files(struct xar *);
static void file_init_hardlinks(struct xar *);
static void file_free_hardlinks(struct xar *);
static void checksum_init(struct chksumwork *, enum sumalg);
static void checksum_update(struct chksumwork *, const void *, size_t);
static void checksum_final(struct chksumwork *, struct chksumval *);
static int compression_init_encoder_gzip(struct archive *,
struct la_zstream *, int, int);
static int compression_code_gzip(struct archive *,
struct la_zstream *, enum la_zaction);
static int compression_end_gzip(struct archive *, struct la_zstream *);
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_lzma(struct archive *,
struct la_zstream *, int);
static int compression_init_encoder_xz(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 xar_compression_init_encoder(struct archive_write *);
static int compression_code(struct archive *,
struct la_zstream *, enum la_zaction);
static int compression_end(struct archive *,
struct la_zstream *);
static int save_xattrs(struct archive_write *, struct file *);
static int getalgsize(enum sumalg);
static const char *getalgname(enum sumalg);
int
archive_write_set_format_xar(struct archive *_a)
{
struct archive_write *a = (struct archive_write *)_a;
struct xar *xar;
archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
ARCHIVE_STATE_NEW, "archive_write_set_format_xar");
/* If another format was already registered, unregister it. */
if (a->format_free != NULL)
(a->format_free)(a);
xar = calloc(1, sizeof(*xar));
if (xar == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate xar data");
return (ARCHIVE_FATAL);
}
xar->temp_fd = -1;
file_init_register(xar);
file_init_hardlinks(xar);
archive_string_init(&(xar->tstr));
archive_string_init(&(xar->vstr));
/*
* Create the root directory.
*/
xar->root = file_create_virtual_dir(a, xar, "");
if (xar->root == NULL) {
free(xar);
archive_set_error(&a->archive, ENOMEM,
"Can't allocate xar data");
return (ARCHIVE_FATAL);
}
xar->root->parent = xar->root;
file_register(xar, xar->root);
xar->cur_dirent = xar->root;
archive_string_init(&(xar->cur_dirstr));
archive_string_ensure(&(xar->cur_dirstr), 1);
xar->cur_dirstr.s[0] = 0;
/*
* Initialize option.
*/
/* Set default checksum type. */
xar->opt_toc_sumalg = CKSUM_SHA1;
xar->opt_sumalg = CKSUM_SHA1;
/* Set default compression type and level. */
xar->opt_compression = GZIP;
xar->opt_compression_level = 6;
a->format_data = xar;
a->format_name = "xar";
a->format_options = xar_options;
a->format_write_header = xar_write_header;
a->format_write_data = xar_write_data;
a->format_finish_entry = xar_finish_entry;
a->format_close = xar_close;
a->format_free = xar_free;
a->archive.archive_format = ARCHIVE_FORMAT_XAR;
a->archive.archive_format_name = "xar";
return (ARCHIVE_OK);
}
static int
xar_options(struct archive_write *a, const char *key, const char *value)
{
struct xar *xar;
xar = (struct xar *)a->format_data;
if (strcmp(key, "checksum") == 0) {
if (value == NULL)
xar->opt_sumalg = CKSUM_NONE;
else if (strcmp(value, "sha1") == 0)
xar->opt_sumalg = CKSUM_SHA1;
else if (strcmp(value, "md5") == 0)
xar->opt_sumalg = CKSUM_MD5;
else {
archive_set_error(&(a->archive),
ARCHIVE_ERRNO_MISC,
"Unkonwn checksum name: `%s'",
value);
return (ARCHIVE_FAILED);
}
return (ARCHIVE_OK);
}
if (strcmp(key, "compression") == 0) {
const char *name = NULL;
if (value == NULL)
xar->opt_compression = NONE;
else if (strcmp(value, "gzip") == 0)
xar->opt_compression = GZIP;
else if (strcmp(value, "bzip2") == 0)
#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
xar->opt_compression = BZIP2;
#else
name = "bzip2";
#endif
else if (strcmp(value, "lzma") == 0)
#if HAVE_LZMA_H
xar->opt_compression = LZMA;
#else
name = "lzma";
#endif
else if (strcmp(value, "xz") == 0)
#if HAVE_LZMA_H
xar->opt_compression = XZ;
#else
name = "xz";
#endif
else {
archive_set_error(&(a->archive),
ARCHIVE_ERRNO_MISC,
"Unkonwn 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,
"Illeagal value `%s'",
value);
return (ARCHIVE_FAILED);
}
xar->opt_compression_level = value[0] - '0';
return (ARCHIVE_OK);
}
if (strcmp(key, "toc-checksum") == 0) {
if (value == NULL)
xar->opt_toc_sumalg = CKSUM_NONE;
else if (strcmp(value, "sha1") == 0)
xar->opt_toc_sumalg = CKSUM_SHA1;
else if (strcmp(value, "md5") == 0)
xar->opt_toc_sumalg = CKSUM_MD5;
else {
archive_set_error(&(a->archive),
ARCHIVE_ERRNO_MISC,
"Unkonwn checksum name: `%s'",
value);
return (ARCHIVE_FAILED);
}
return (ARCHIVE_OK);
}
return (ARCHIVE_FAILED);
}
static int
xar_write_header(struct archive_write *a, struct archive_entry *entry)
{
struct xar *xar;
struct file *file;
struct archive_entry *file_entry;
int r, r2;
xar = (struct xar *)a->format_data;
xar->cur_file = NULL;
xar->bytes_remaining = 0;
if (xar->sconv == NULL) {
xar->sconv = archive_string_conversion_to_charset(
&a->archive, "UTF-8", 1);
if (xar->sconv == NULL)
return (ARCHIVE_FATAL);
}
file = file_new(a, entry);
if (file == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate data");
return (ARCHIVE_FATAL);
}
r2 = file_gen_utility_names(a, file);
if (r2 < ARCHIVE_WARN)
return (r2);
/*
* Ignore a path which looks like the top of directory name
* since we have already made the root directory of an Xar archive.
*/
if (archive_strlen(&(file->parentdir)) == 0 &&
archive_strlen(&(file->basename)) == 0) {
file_free(file);
return (r2);
}
/* Add entry into tree */
file_entry = file->entry;
r = file_tree(a, &file);
if (r != ARCHIVE_OK)
return (r);
/* There is the same file in tree and
* the current file is older than the file in tree.
* So we don't need the current file data anymore. */
if (file->entry != file_entry)
return (r2);
if (file->id == 0)
file_register(xar, file);
/* A virtual file, which is a directory, does not have
* any contents and we won't store it into a archive
* file other than its name. */
if (file->virtual)
return (r2);
/*
* Prepare to save the contents of the file.
*/
if (xar->temp_fd == -1) {
int algsize;
xar->temp_offset = 0;
xar->temp_fd = __archive_mktemp(NULL);
if (xar->temp_fd < 0) {
archive_set_error(&a->archive, errno,
"Couldn't create temporary file");
return (ARCHIVE_FATAL);
}
algsize = getalgsize(xar->opt_toc_sumalg);
if (algsize > 0) {
if (lseek(xar->temp_fd, algsize, SEEK_SET) < 0) {
archive_set_error(&(a->archive), errno,
"lseek failed");
return (ARCHIVE_FATAL);
}
xar->temp_offset = algsize;
}
}
if (archive_entry_hardlink(file->entry) == NULL) {
r = save_xattrs(a, file);
if (r != ARCHIVE_OK)
return (ARCHIVE_FATAL);
}
/* Non regular files contents are unneeded to be saved to
* a temporary file. */
if (archive_entry_filetype(file->entry) != AE_IFREG)
return (r2);
/*
* Set the current file to cur_file to read its contents.
*/
xar->cur_file = file;
if (archive_entry_nlink(file->entry) > 1) {
r = file_register_hardlink(a, file);
if (r != ARCHIVE_OK)
return (r);
if (archive_entry_hardlink(file->entry) != NULL) {
archive_entry_unset_size(file->entry);
return (r2);
}
}
/* Save a offset of current file in temporary file. */
file->data.temp_offset = xar->temp_offset;
file->data.size = archive_entry_size(file->entry);
file->data.compression = xar->opt_compression;
xar->bytes_remaining = archive_entry_size(file->entry);
checksum_init(&(xar->a_sumwrk), xar->opt_sumalg);
checksum_init(&(xar->e_sumwrk), xar->opt_sumalg);
r = xar_compression_init_encoder(a);
if (r != ARCHIVE_OK)
return (r);
else
return (r2);
}
static int
write_to_temp(struct archive_write *a, const void *buff, size_t s)
{
struct xar *xar;
unsigned char *p;
ssize_t ws;
xar = (struct xar *)a->format_data;
p = (unsigned char *)buff;
while (s) {
ws = write(xar->temp_fd, p, s);
if (ws < 0) {
archive_set_error(&(a->archive), errno,
"fwrite function failed");
return (ARCHIVE_FATAL);
}
s -= ws;
p += ws;
xar->temp_offset += ws;
}
return (ARCHIVE_OK);
}
static ssize_t
xar_write_data(struct archive_write *a, const void *buff, size_t s)
{
struct xar *xar;
enum la_zaction run;
size_t size, rsize;
int r;
xar = (struct xar *)a->format_data;
if (s > xar->bytes_remaining)
s = xar->bytes_remaining;
if (s == 0 || xar->cur_file == NULL)
return (0);
if (xar->cur_file->data.compression == NONE) {
checksum_update(&(xar->e_sumwrk), buff, s);
checksum_update(&(xar->a_sumwrk), buff, s);
size = rsize = s;
} else {
xar->stream.next_in = (const unsigned char *)buff;
xar->stream.avail_in = s;
if (xar->bytes_remaining > s)
run = ARCHIVE_Z_RUN;
else
run = ARCHIVE_Z_FINISH;
/* Compress file data. */
r = compression_code(&(a->archive), &(xar->stream), run);
if (r != ARCHIVE_OK && r != ARCHIVE_EOF)
return (ARCHIVE_FATAL);
rsize = s - xar->stream.avail_in;
checksum_update(&(xar->e_sumwrk), buff, rsize);
size = sizeof(xar->wbuff) - xar->stream.avail_out;
checksum_update(&(xar->a_sumwrk), xar->wbuff, size);
}
#if !defined(_WIN32) || defined(__CYGWIN__)
if (xar->bytes_remaining ==
archive_entry_size(xar->cur_file->entry)) {
/*
* Get the path of a shell script if so.
*/
const unsigned char *b = (const unsigned char *)buff;
archive_string_empty(&(xar->cur_file->script));
if (rsize > 2 && b[0] == '#' && b[1] == '!') {
size_t i, end, off;
off = 2;
if (b[off] == ' ')
off++;
#ifdef PATH_MAX
if ((rsize - off) > PATH_MAX)
end = off + PATH_MAX;
else
#endif
end = rsize;
/* Find the end of a script path. */
for (i = off; i < end && b[i] != '\0' &&
b[i] != '\n' && b[i] != '\r' &&
b[i] != ' ' && b[i] != '\t'; i++)
;
archive_strncpy(&(xar->cur_file->script), b + off,
i - off);
}
}
#endif
if (xar->cur_file->data.compression == NONE) {
if (write_to_temp(a, buff, size) != ARCHIVE_OK)
return (ARCHIVE_FATAL);
} else {
if (write_to_temp(a, xar->wbuff, size) != ARCHIVE_OK)
return (ARCHIVE_FATAL);
}
xar->bytes_remaining -= rsize;
xar->cur_file->data.length += size;
return (rsize);
}
static int
xar_finish_entry(struct archive_write *a)
{
struct xar *xar;
struct file *file;
size_t s;
ssize_t w;
xar = (struct xar *)a->format_data;
if (xar->cur_file == NULL)
return (ARCHIVE_OK);
while (xar->bytes_remaining > 0) {
s = xar->bytes_remaining;
if (s > a->null_length)
s = a->null_length;
w = xar_write_data(a, a->nulls, s);
if (w > 0)
xar->bytes_remaining -= w;
else
return (w);
}
file = xar->cur_file;
checksum_final(&(xar->e_sumwrk), &(file->data.e_sum));
checksum_final(&(xar->a_sumwrk), &(file->data.a_sum));
xar->cur_file = NULL;
return (ARCHIVE_OK);
}
static int
xmlwrite_string_attr(struct archive_write *a, xmlTextWriterPtr writer,
const char *key, const char *value,
const char *attrkey, const char *attrvalue)
{
int r;
r = xmlTextWriterStartElement(writer, BAD_CAST(key));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterStartElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
if (attrkey != NULL && attrvalue != NULL) {
r = xmlTextWriterWriteAttribute(writer,
BAD_CAST(attrkey), BAD_CAST(attrvalue));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterWriteAttribute() failed: %d", r);
return (ARCHIVE_FATAL);
}
}
if (value != NULL) {
r = xmlTextWriterWriteString(writer, BAD_CAST(value));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterWriteString() failed: %d", r);
return (ARCHIVE_FATAL);
}
}
r = xmlTextWriterEndElement(writer);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterEndElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
return (ARCHIVE_OK);
}
static int
xmlwrite_string(struct archive_write *a, xmlTextWriterPtr writer,
const char *key, const char *value)
{
int r;
if (value == NULL)
return (ARCHIVE_OK);
r = xmlTextWriterStartElement(writer, BAD_CAST(key));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterStartElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
if (value != NULL) {
r = xmlTextWriterWriteString(writer, BAD_CAST(value));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterWriteString() failed: %d", r);
return (ARCHIVE_FATAL);
}
}
r = xmlTextWriterEndElement(writer);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterEndElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
return (ARCHIVE_OK);
}
static int
xmlwrite_fstring(struct archive_write *a, xmlTextWriterPtr writer,
const char *key, const char *fmt, ...)
{
struct xar *xar;
va_list ap;
xar = (struct xar *)a->format_data;
va_start(ap, fmt);
archive_string_empty(&xar->vstr);
archive_string_vsprintf(&xar->vstr, fmt, ap);
va_end(ap);
return (xmlwrite_string(a, writer, key, xar->vstr.s));
}
static int
xmlwrite_time(struct archive_write *a, xmlTextWriterPtr writer,
const char *key, time_t t, int z)
{
char timestr[100];
struct tm tm;
#if defined(HAVE_GMTIME_R)
gmtime_r(&t, &tm);
#elif defined(HAVE__GMTIME64_S)
_gmtime64_s(&tm, &t);
#else
memcpy(&tm, gmtime(&t), sizeof(tm));
#endif
memset(&timestr, 0, sizeof(timestr));
/* Do not use %F and %T for portability. */
strftime(timestr, sizeof(timestr), "%Y-%m-%dT%H:%M:%S", &tm);
if (z)
strcat(timestr, "Z");
return (xmlwrite_string(a, writer, key, timestr));
}
static int
xmlwrite_mode(struct archive_write *a, xmlTextWriterPtr writer,
const char *key, mode_t mode)
{
char ms[5];
ms[0] = '0';
ms[1] = '0' + ((mode >> 6) & 07);
ms[2] = '0' + ((mode >> 3) & 07);
ms[3] = '0' + (mode & 07);
ms[4] = '\0';
return (xmlwrite_string(a, writer, key, ms));
}
static int
xmlwrite_sum(struct archive_write *a, xmlTextWriterPtr writer,
const char *key, struct chksumval *sum)
{
const char *algname;
int algsize;
char buff[MAX_SUM_SIZE*2 + 1];
char *p;
unsigned char *s;
int i, r;
if (sum->len > 0) {
algname = getalgname(sum->alg);
algsize = getalgsize(sum->alg);
if (algname != NULL) {
const char *hex = "0123456789abcdef";
p = buff;
s = sum->val;
for (i = 0; i < algsize; i++) {
*p++ = hex[(*s >> 4)];
*p++ = hex[(*s & 0x0f)];
s++;
}
*p = '\0';
r = xmlwrite_string_attr(a, writer,
key, buff,
"style", algname);
if (r < 0)
return (ARCHIVE_FATAL);
}
}
return (ARCHIVE_OK);
}
static int
xmlwrite_heap(struct archive_write *a, xmlTextWriterPtr writer,
struct heap_data *heap)
{
const char *encname;
int r;
r = xmlwrite_fstring(a, writer, "length", "%ju", heap->length);
if (r < 0)
return (ARCHIVE_FATAL);
r = xmlwrite_fstring(a, writer, "offset", "%ju", heap->temp_offset);
if (r < 0)
return (ARCHIVE_FATAL);
r = xmlwrite_fstring(a, writer, "size", "%ju", heap->size);
if (r < 0)
return (ARCHIVE_FATAL);
switch (heap->compression) {
case GZIP:
encname = "application/x-gzip"; break;
case BZIP2:
encname = "application/x-bzip2"; break;
case LZMA:
encname = "application/x-lzma"; break;
case XZ:
encname = "application/x-xz"; break;
default:
encname = "application/octet-stream"; break;
}
r = xmlwrite_string_attr(a, writer, "encoding", NULL,
"style", encname);
if (r < 0)
return (ARCHIVE_FATAL);
r = xmlwrite_sum(a, writer, "archived-checksum", &(heap->a_sum));
if (r < 0)
return (ARCHIVE_FATAL);
r = xmlwrite_sum(a, writer, "extracted-checksum", &(heap->e_sum));
if (r < 0)
return (ARCHIVE_FATAL);
return (ARCHIVE_OK);
}
/*
* xar utility records fflags as following xml elements:
* <flags>
* <UserNoDump/>
* .....
* </flags>
* or
* <ext2>
* <NoDump/>
* .....
* </ext2>
* If xar is running on BSD platform, records <flags>..</flags>;
* if xar is running on linux platform, records <ext2>..</ext2>;
* otherwise does not record.
*
* Our implements records both <flags> and <ext2> if it's necessary.
*/
static int
make_fflags_entry(struct archive_write *a, xmlTextWriterPtr writer,
const char *element, const char *fflags_text)
{
static const struct flagentry {
const char *name;
const char *xarname;
}
flagbsd[] = {
{ "sappnd", "SystemAppend"},
{ "sappend", "SystemAppend"},
{ "arch", "SystemArchived"},
{ "archived", "SystemArchived"},
{ "schg", "SystemImmutable"},
{ "schange", "SystemImmutable"},
{ "simmutable", "SystemImmutable"},
{ "nosunlnk", "SystemNoUnlink"},
{ "nosunlink", "SystemNoUnlink"},
{ "snapshot", "SystemSnapshot"},
{ "uappnd", "UserAppend"},
{ "uappend", "UserAppend"},
{ "uchg", "UserImmutable"},
{ "uchange", "UserImmutable"},
{ "uimmutable", "UserImmutable"},
{ "nodump", "UserNoDump"},
{ "noopaque", "UserOpaque"},
{ "nouunlnk", "UserNoUnlink"},
{ "nouunlink", "UserNoUnlink"},
{ NULL, NULL}
},
flagext2[] = {
{ "sappnd", "AppendOnly"},
{ "sappend", "AppendOnly"},
{ "schg", "Immutable"},
{ "schange", "Immutable"},
{ "simmutable", "Immutable"},
{ "nodump", "NoDump"},
{ "nouunlnk", "Undelete"},
{ "nouunlink", "Undelete"},
{ "btree", "BTree"},
{ "comperr", "CompError"},
{ "compress", "Compress"},
{ "noatime", "NoAtime"},
{ "compdirty", "CompDirty"},
{ "comprblk", "CompBlock"},
{ "dirsync", "DirSync"},
{ "hashidx", "HashIndexed"},
{ "imagic", "iMagic"},
{ "journal", "Journaled"},
{ "securedeletion", "SecureDeletion"},
{ "sync", "Synchronous"},
{ "notail", "NoTail"},
{ "topdir", "TopDir"},
{ "reserved", "Reserved"},
{ NULL, NULL}
};
const struct flagentry *fe, *flagentry;
#define FLAGENTRY_MAXSIZE ((sizeof(flagbsd)+sizeof(flagext2))/sizeof(flagbsd))
const struct flagentry *avail[FLAGENTRY_MAXSIZE];
const char *p;
int i, n, r;
if (strcmp(element, "ext2") == 0)
flagentry = flagext2;
else
flagentry = flagbsd;
n = 0;
p = fflags_text;
do {
const char *cp;
cp = strchr(p, ',');
if (cp == NULL)
cp = p + strlen(p);
for (fe = flagentry; fe->name != NULL; fe++) {
if (fe->name[cp - p] != '\0'
|| p[0] != fe->name[0])
continue;
if (strncmp(p, fe->name, cp - p) == 0) {
avail[n++] = fe;
break;
}
}
if (*cp == ',')
p = cp + 1;
else
p = NULL;
} while (p != NULL);
if (n > 0) {
r = xmlTextWriterStartElement(writer, BAD_CAST(element));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterStartElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
for (i = 0; i < n; i++) {
r = xmlwrite_string(a, writer,
avail[i]->xarname, NULL);
if (r != ARCHIVE_OK)
return (r);
}
r = xmlTextWriterEndElement(writer);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterEndElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
}
return (ARCHIVE_OK);
}
static int
make_file_entry(struct archive_write *a, xmlTextWriterPtr writer,
struct file *file)
{
struct xar *xar;
const char *filetype, *filelink, *fflags;
struct archive_string linkto;
struct heap_data *heap;
unsigned char *tmp;
const char *p;
size_t len;
int r, r2, l, ll;
xar = (struct xar *)a->format_data;
r2 = ARCHIVE_OK;
/*
* Make a file name entry, "<name>".
*/
l = ll = archive_strlen(&(file->basename));
tmp = malloc(l);
if (tmp == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory");
return (ARCHIVE_FATAL);
}
r = UTF8Toisolat1(tmp, &l, BAD_CAST(file->basename.s), &ll);
free(tmp);
if (r < 0) {
r = xmlTextWriterStartElement(writer, BAD_CAST("name"));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterStartElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
r = xmlTextWriterWriteAttribute(writer,
BAD_CAST("enctype"), BAD_CAST("base64"));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterWriteAttribute() failed: %d", r);
return (ARCHIVE_FATAL);
}
r = xmlTextWriterWriteBase64(writer, file->basename.s,
0, archive_strlen(&(file->basename)));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterWriteBase64() failed: %d", r);
return (ARCHIVE_FATAL);
}
r = xmlTextWriterEndElement(writer);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterEndElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
} else {
r = xmlwrite_string(a, writer, "name", file->basename.s);
if (r < 0)
return (ARCHIVE_FATAL);
}
/*
* Make a file type entry, "<type>".
*/
filelink = NULL;
archive_string_init(&linkto);
switch (archive_entry_filetype(file->entry)) {
case AE_IFDIR:
filetype = "directory"; break;
case AE_IFLNK:
filetype = "symlink"; break;
case AE_IFCHR:
filetype = "character special"; break;
case AE_IFBLK:
filetype = "block special"; break;
case AE_IFSOCK:
filetype = "socket"; break;
case AE_IFIFO:
filetype = "fifo"; break;
case AE_IFREG:
default:
if (file->hardlink_target != NULL) {
filetype = "hardlink";
filelink = "link";
if (file->hardlink_target == file)
archive_strcpy(&linkto, "original");
else
archive_string_sprintf(&linkto, "%d",
file->hardlink_target->id);
} else
filetype = "file";
break;
}
r = xmlwrite_string_attr(a, writer, "type", filetype,
filelink, linkto.s);
archive_string_free(&linkto);
if (r < 0)
return (ARCHIVE_FATAL);
/*
* On a virtual directory, we record "name" and "type" only.
*/
if (file->virtual)
return (ARCHIVE_OK);
switch (archive_entry_filetype(file->entry)) {
case AE_IFLNK:
/*
* xar utility has checked a file type, which
* a symblic-link file has referenced.
* For example:
* <link type="directory">../ref/</link>
* The symlink target file is "../ref/" and its
* file type is a directory.
*
* <link type="file">../f</link>
* The symlink target file is "../f" and its
* file type is a regular file.
*
* But our implemention cannot do it, and then we
* always record that a attribute "type" is "borken",
* for example:
* <link type="broken">foo/bar</link>
* It means "foo/bar" is not reachable.
*/
r = xmlwrite_string_attr(a, writer, "link",
file->symlink.s,
"type", "broken");
if (r < 0)
return (ARCHIVE_FATAL);
break;
case AE_IFCHR:
case AE_IFBLK:
r = xmlTextWriterStartElement(writer, BAD_CAST("device"));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterStartElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
r = xmlwrite_fstring(a, writer, "major",
"%d", archive_entry_rdevmajor(file->entry));
if (r < 0)
return (ARCHIVE_FATAL);
r = xmlwrite_fstring(a, writer, "minor",
"%d", archive_entry_rdevminor(file->entry));
if (r < 0)
return (ARCHIVE_FATAL);
r = xmlTextWriterEndElement(writer);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterEndElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
break;
default:
break;
}
/*
* Make a inode entry, "<inode>".
*/
r = xmlwrite_fstring(a, writer, "inode",
"%jd", archive_entry_ino64(file->entry));
if (r < 0)
return (ARCHIVE_FATAL);
if (archive_entry_dev(file->entry) != 0) {
r = xmlwrite_fstring(a, writer, "deviceno",
"%d", archive_entry_dev(file->entry));
if (r < 0)
return (ARCHIVE_FATAL);
}
/*
* Make a file mode entry, "<mode>".
*/
r = xmlwrite_mode(a, writer, "mode",
archive_entry_mode(file->entry));
if (r < 0)
return (ARCHIVE_FATAL);
/*
* Make a user entry, "<uid>" and "<user>.
*/
r = xmlwrite_fstring(a, writer, "uid",
"%d", archive_entry_uid(file->entry));
if (r < 0)
return (ARCHIVE_FATAL);
r = archive_entry_uname_l(file->entry, &p, &len, xar->sconv);
if (r != 0) {
if (errno == ENOMEM) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for Uname");
return (ARCHIVE_FATAL);
}
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Can't translate uname '%s' to UTF-8",
archive_entry_uname(file->entry));
r2 = ARCHIVE_WARN;
}
if (len > 0) {
r = xmlwrite_string(a, writer, "user", p);
if (r < 0)
return (ARCHIVE_FATAL);
}
/*
* Make a group entry, "<gid>" and "<group>.
*/
r = xmlwrite_fstring(a, writer, "gid",
"%d", archive_entry_gid(file->entry));
if (r < 0)
return (ARCHIVE_FATAL);
r = archive_entry_gname_l(file->entry, &p, &len, xar->sconv);
if (r != 0) {
if (errno == ENOMEM) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for Gname");
return (ARCHIVE_FATAL);
}
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Can't translate gname '%s' to UTF-8",
archive_entry_gname(file->entry));
r2 = ARCHIVE_WARN;
}
if (len > 0) {
r = xmlwrite_string(a, writer, "group", p);
if (r < 0)
return (ARCHIVE_FATAL);
}
/*
* Make a ctime entry, "<ctime>".
*/
if (archive_entry_ctime_is_set(file->entry)) {
r = xmlwrite_time(a, writer, "ctime",
archive_entry_ctime(file->entry), 1);
if (r < 0)
return (ARCHIVE_FATAL);
}
/*
* Make a mtime entry, "<mtime>".
*/
if (archive_entry_mtime_is_set(file->entry)) {
r = xmlwrite_time(a, writer, "mtime",
archive_entry_mtime(file->entry), 1);
if (r < 0)
return (ARCHIVE_FATAL);
}
/*
* Make a atime entry, "<atime>".
*/
if (archive_entry_atime_is_set(file->entry)) {
r = xmlwrite_time(a, writer, "atime",
archive_entry_atime(file->entry), 1);
if (r < 0)
return (ARCHIVE_FATAL);
}
/*
* Make fflags entries, "<flags>" and "<ext2>".
*/
fflags = archive_entry_fflags_text(file->entry);
if (fflags != NULL) {
r = make_fflags_entry(a, writer, "flags", fflags);
if (r < 0)
return (r);
r = make_fflags_entry(a, writer, "ext2", fflags);
if (r < 0)
return (r);
}
/*
* Make extended attribute entries, "<ea>".
*/
archive_entry_xattr_reset(file->entry);
for (heap = file->xattr.first; heap != NULL; heap = heap->next) {
const char *name;
const void *value;
size_t size;
archive_entry_xattr_next(file->entry,
&name, &value, &size);
r = xmlTextWriterStartElement(writer, BAD_CAST("ea"));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterStartElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
r = xmlTextWriterWriteFormatAttribute(writer,
BAD_CAST("id"), "%d", heap->id);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterWriteAttribute() failed: %d", r);
return (ARCHIVE_FATAL);
}
r = xmlwrite_heap(a, writer, heap);
if (r < 0)
return (ARCHIVE_FATAL);
r = xmlwrite_string(a, writer, "name", name);
if (r < 0)
return (ARCHIVE_FATAL);
r = xmlTextWriterEndElement(writer);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterEndElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
}
/*
* Make a file data entry, "<data>".
*/
if (file->data.length > 0) {
r = xmlTextWriterStartElement(writer, BAD_CAST("data"));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterStartElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
r = xmlwrite_heap(a, writer, &(file->data));
if (r < 0)
return (ARCHIVE_FATAL);
r = xmlTextWriterEndElement(writer);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterEndElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
}
if (archive_strlen(&file->script) > 0) {
r = xmlTextWriterStartElement(writer, BAD_CAST("content"));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterStartElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
r = xmlwrite_string(a, writer,
"interpreter", file->script.s);
if (r < 0)
return (ARCHIVE_FATAL);
r = xmlwrite_string(a, writer, "type", "script");
if (r < 0)
return (ARCHIVE_FATAL);
r = xmlTextWriterEndElement(writer);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterEndElement() failed: %d", r);
return (ARCHIVE_FATAL);
}
}
return (r2);
}
/*
* Make the TOC
*/
static int
make_toc(struct archive_write *a)
{
struct xar *xar;
struct file *np;
xmlBufferPtr bp;
xmlTextWriterPtr writer;
int algsize;
int r, ret;
xar = (struct xar *)a->format_data;
ret = ARCHIVE_FATAL;
/*
* Initialize xml writer.
*/
writer = NULL;
bp = xmlBufferCreate();
if (bp == NULL) {
archive_set_error(&a->archive, ENOMEM,
"xmlBufferCreate() "
"couldn't create xml buffer");
goto exit_toc;
}
writer = xmlNewTextWriterMemory(bp, 0);
if (writer == NULL) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlNewTextWriterMemory() "
"couldn't create xml writer");
goto exit_toc;
}
r = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterStartDocument() failed: %d", r);
goto exit_toc;
}
r = xmlTextWriterSetIndent(writer, 4);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterSetIndent() failed: %d", r);
goto exit_toc;
}
/*
* Start recoding TOC
*/
r = xmlTextWriterStartElement(writer, BAD_CAST("xar"));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterStartElement() failed: %d", r);
goto exit_toc;
}
r = xmlTextWriterStartElement(writer, BAD_CAST("toc"));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterStartDocument() failed: %d", r);
goto exit_toc;
}
/*
* Record the creation time of the archive file.
*/
r = xmlwrite_time(a, writer, "creation-time", time(NULL), 0);
if (r < 0)
goto exit_toc;
/*
* Record the checksum value of TOC
*/
algsize = getalgsize(xar->opt_toc_sumalg);
if (algsize) {
/*
* Record TOC checksum
*/
r = xmlTextWriterStartElement(writer, BAD_CAST("checksum"));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterStartElement() failed: %d", r);
goto exit_toc;
}
r = xmlTextWriterWriteAttribute(writer, BAD_CAST("style"),
BAD_CAST(getalgname(xar->opt_toc_sumalg)));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterWriteAttribute() failed: %d", r);
goto exit_toc;
}
/*
* Record the offset of the value of checksum of TOC
*/
r = xmlwrite_string(a, writer, "offset", "0");
if (r < 0)
goto exit_toc;
/*
* Record the size of the value of checksum of TOC
*/
r = xmlwrite_fstring(a, writer, "size", "%d", algsize);
if (r < 0)
goto exit_toc;
r = xmlTextWriterEndElement(writer);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterEndElement() failed: %d", r);
goto exit_toc;
}
}
np = xar->root;
do {
if (np != np->parent) {
r = make_file_entry(a, writer, np);
if (r != ARCHIVE_OK)
goto exit_toc;
}
if (np->dir && np->children.first != NULL) {
/* Enter to sub directories. */
np = np->children.first;
r = xmlTextWriterStartElement(writer,
BAD_CAST("file"));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterStartElement() "
"failed: %d", r);
goto exit_toc;
}
r = xmlTextWriterWriteFormatAttribute(
writer, BAD_CAST("id"), "%d", np->id);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterWriteAttribute() "
"failed: %d", r);
goto exit_toc;
}
continue;
}
while (np != np->parent) {
r = xmlTextWriterEndElement(writer);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterEndElement() "
"failed: %d", r);
goto exit_toc;
}
if (np->chnext == NULL) {
/* Return to the parent directory. */
np = np->parent;
} else {
np = np->chnext;
r = xmlTextWriterStartElement(writer,
BAD_CAST("file"));
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterStartElement() "
"failed: %d", r);
goto exit_toc;
}
r = xmlTextWriterWriteFormatAttribute(
writer, BAD_CAST("id"), "%d", np->id);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterWriteAttribute() "
"failed: %d", r);
goto exit_toc;
}
break;
}
}
} while (np != np->parent);
r = xmlTextWriterEndDocument(writer);
if (r < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"xmlTextWriterEndDocument() failed: %d", r);
goto exit_toc;
}
#if DEBUG_PRINT_TOC
fprintf(stderr, "\n---TOC-- %d bytes --\n%s\n",
strlen((const char *)bp->content), bp->content);
#endif
/*
* Compress the TOC and calculate the sum of the TOC.
*/
xar->toc.temp_offset = xar->temp_offset;
xar->toc.size = bp->use;
checksum_init(&(xar->a_sumwrk), xar->opt_toc_sumalg);
r = compression_init_encoder_gzip(&(a->archive),
&(xar->stream), 6, 1);
if (r != ARCHIVE_OK)
goto exit_toc;
xar->stream.next_in = bp->content;
xar->stream.avail_in = bp->use;
xar->stream.total_in = 0;
xar->stream.next_out = xar->wbuff;
xar->stream.avail_out = sizeof(xar->wbuff);
xar->stream.total_out = 0;
for (;;) {
size_t size;
r = compression_code(&(a->archive),
&(xar->stream), ARCHIVE_Z_FINISH);
if (r != ARCHIVE_OK && r != ARCHIVE_EOF)
goto exit_toc;
size = sizeof(xar->wbuff) - xar->stream.avail_out;
checksum_update(&(xar->a_sumwrk), xar->wbuff, size);
if (write_to_temp(a, xar->wbuff, size) != ARCHIVE_OK)
goto exit_toc;
if (r == ARCHIVE_EOF)
break;
xar->stream.next_out = xar->wbuff;
xar->stream.avail_out = sizeof(xar->wbuff);
}
r = compression_end(&(a->archive), &(xar->stream));
if (r != ARCHIVE_OK)
goto exit_toc;
xar->toc.length = xar->stream.total_out;
xar->toc.compression = GZIP;
checksum_final(&(xar->a_sumwrk), &(xar->toc.a_sum));
ret = ARCHIVE_OK;
exit_toc:
if (writer)
xmlFreeTextWriter(writer);
if (bp)
xmlBufferFree(bp);
return (ret);
}
static int
flush_wbuff(struct archive_write *a)
{
struct xar *xar;
int r;
size_t s;
xar = (struct xar *)a->format_data;
s = sizeof(xar->wbuff) - xar->wbuff_remaining;
r = __archive_write_output(a, xar->wbuff, s);
if (r != ARCHIVE_OK)
return (r);
xar->wbuff_remaining = sizeof(xar->wbuff);
return (r);
}
static int
copy_out(struct archive_write *a, uint64_t offset, uint64_t length)
{
struct xar *xar;
int r;
xar = (struct xar *)a->format_data;
if (lseek(xar->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 > xar->wbuff_remaining)
rsize = xar->wbuff_remaining;
else
rsize = (size_t)length;
wb = xar->wbuff + (sizeof(xar->wbuff) - xar->wbuff_remaining);
rs = read(xar->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 xar archive");
return (ARCHIVE_FATAL);
}
xar->wbuff_remaining -= rs;
length -= rs;
if (xar->wbuff_remaining == 0) {
r = flush_wbuff(a);
if (r != ARCHIVE_OK)
return (r);
}
}
return (ARCHIVE_OK);
}
static int
xar_close(struct archive_write *a)
{
struct xar *xar;
unsigned char *wb;
uint64_t length;
int r;
xar = (struct xar *)a->format_data;
/* Empty! */
if (xar->root->children.first == NULL)
return (ARCHIVE_OK);
/* Save the length of all file extended attributes and contents. */
length = xar->temp_offset;
/* Connect hardlinked files */
file_connect_hardlink_files(xar);
/* Make the TOC */
r = make_toc(a);
if (r != ARCHIVE_OK)
return (r);
/*
* Make the xar header on wbuff(write buffer).
*/
wb = xar->wbuff;
xar->wbuff_remaining = sizeof(xar->wbuff);
archive_be32enc(&wb[0], HEADER_MAGIC);
archive_be16enc(&wb[4], HEADER_SIZE);
archive_be16enc(&wb[6], HEADER_VERSION);
archive_be64enc(&wb[8], xar->toc.length);
archive_be64enc(&wb[16], xar->toc.size);
archive_be32enc(&wb[24], xar->toc.a_sum.alg);
xar->wbuff_remaining -= HEADER_SIZE;
/*
* Write the TOC
*/
r = copy_out(a, xar->toc.temp_offset, xar->toc.length);
if (r != ARCHIVE_OK)
return (r);
/* Write the checksum value of the TOC. */
if (xar->toc.a_sum.len) {
if (xar->wbuff_remaining < xar->toc.a_sum.len) {
r = flush_wbuff(a);
if (r != ARCHIVE_OK)
return (r);
}
wb = xar->wbuff + (sizeof(xar->wbuff) - xar->wbuff_remaining);
memcpy(wb, xar->toc.a_sum.val, xar->toc.a_sum.len);
xar->wbuff_remaining -= xar->toc.a_sum.len;
}
/*
* Write all file extended attributes and contents.
*/
r = copy_out(a, xar->toc.a_sum.len, length);
if (r != ARCHIVE_OK)
return (r);
r = flush_wbuff(a);
return (r);
}
static int
xar_free(struct archive_write *a)
{
struct xar *xar;
xar = (struct xar *)a->format_data;
archive_string_free(&(xar->cur_dirstr));
archive_string_free(&(xar->tstr));
archive_string_free(&(xar->vstr));
file_free_hardlinks(xar);
file_free_register(xar);
compression_end(&(a->archive), &(xar->stream));
free(xar);
return (ARCHIVE_OK);
}
static int
file_cmp_node(const struct archive_rb_node *n1,
const struct archive_rb_node *n2)
{
struct file *f1 = (struct file *)n1;
struct file *f2 = (struct file *)n2;
return (strcmp(f1->basename.s, f2->basename.s));
}
static int
file_cmp_key(const struct archive_rb_node *n, const void *key)
{
struct file *f = (struct file *)n;
return (strcmp(f->basename.s, (const char *)key));
}
static struct file *
file_new(struct archive_write *a, struct archive_entry *entry)
{
struct file *file;
static const struct archive_rb_tree_ops rb_ops = {
file_cmp_node, file_cmp_key
};
file = calloc(1, sizeof(*file));
if (file == NULL)
return (NULL);
if (entry != NULL)
file->entry = archive_entry_clone(entry);
else
file->entry = archive_entry_new2(&a->archive);
if (file->entry == NULL) {
free(file);
return (NULL);
}
__archive_rb_tree_init(&(file->rbtree), &rb_ops);
file->children.first = NULL;
file->children.last = &(file->children.first);
file->xattr.first = NULL;
file->xattr.last = &(file->xattr.first);
archive_string_init(&(file->parentdir));
archive_string_init(&(file->basename));
archive_string_init(&(file->symlink));
archive_string_init(&(file->script));
if (entry != NULL && archive_entry_filetype(entry) == AE_IFDIR)
file->dir = 1;
return (file);
}
static void
file_free(struct file *file)
{
struct heap_data *heap, *next_heap;
heap = file->xattr.first;
while (heap != NULL) {
next_heap = heap->next;
free(heap);
heap = next_heap;
}
archive_string_free(&(file->parentdir));
archive_string_free(&(file->basename));
archive_string_free(&(file->symlink));
archive_string_free(&(file->script));
free(file);
}
static struct file *
file_create_virtual_dir(struct archive_write *a, struct xar *xar,
const char *pathname)
{
struct file *file;
file = file_new(a, NULL);
if (file == NULL)
return (NULL);
archive_entry_set_pathname(file->entry, pathname);
archive_entry_set_mode(file->entry, 0555 | AE_IFDIR);
file->dir = 1;
file->virtual = 1;
return (file);
}
static int
file_add_child_tail(struct file *parent, struct file *child)
{
if (!__archive_rb_tree_insert_node(
&(parent->rbtree), (struct archive_rb_node *)child))
return (0);
child->chnext = NULL;
*parent->children.last = child;
parent->children.last = &(child->chnext);
child->parent = parent;
return (1);
}
/*
* Find a entry from `parent'
*/
static struct file *
file_find_child(struct file *parent, const char *child_name)
{
struct file *np;
np = (struct file *)__archive_rb_tree_find_node(
&(parent->rbtree), child_name);
return (np);
}
#if defined(_WIN32) || defined(__CYGWIN__)
static void
cleanup_backslash(char *utf8, size_t len)
{
/* Convert a path-separator from '\' to '/' */
while (*utf8 != '\0' && len) {
if (*utf8 == '\\')
*utf8 = '/';
++utf8;
--len;
}
}
#else
#define cleanup_backslash(p, len) /* nop */
#endif
/*
* Generate a parent directory name and a base name from a pathname.
*/
static int
file_gen_utility_names(struct archive_write *a, struct file *file)
{
struct xar *xar;
const char *pp;
char *p, *dirname, *slash;
size_t len;
int r = ARCHIVE_OK;
xar = (struct xar *)a->format_data;
archive_string_empty(&(file->parentdir));
archive_string_empty(&(file->basename));
archive_string_empty(&(file->symlink));
if (file->parent == file)/* virtual root */
return (ARCHIVE_OK);
if (archive_entry_pathname_l(file->entry, &pp, &len, xar->sconv)
!= 0) {
if (errno == ENOMEM) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for Pathname");
return (ARCHIVE_FATAL);
}
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Can't translate pathname '%s' to UTF-8",
archive_entry_pathname(file->entry));
r = ARCHIVE_WARN;
}
archive_strncpy(&(file->parentdir), pp, len);
len = file->parentdir.length;
p = dirname = file->parentdir.s;
/*
* Convert a path-separator from '\' to '/'
*/
cleanup_backslash(p, len);
/*
* Remove leading '/', '../' and './' elements
*/
while (*p) {
if (p[0] == '/') {
p++;
len--;
} else if (p[0] != '.')
break;
else if (p[1] == '.' && p[2] == '/') {
p += 3;
len -= 3;
} else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) {
p += 2;
len -= 2;
} else if (p[1] == '\0') {
p++;
len--;
} else
break;
}
if (p != dirname) {
memmove(dirname, p, len+1);
p = dirname;
}
/*
* Remove "/","/." and "/.." elements from tail.
*/
while (len > 0) {
size_t ll = len;
if (len > 0 && p[len-1] == '/') {
p[len-1] = '\0';
len--;
}
if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {
p[len-2] = '\0';
len -= 2;
}
if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&
p[len-1] == '.') {
p[len-3] = '\0';
len -= 3;
}
if (ll == len)
break;
}
while (*p) {
if (p[0] == '/') {
if (p[1] == '/')
/* Convert '//' --> '/' */
strcpy(p, p+1);
else if (p[1] == '.' && p[2] == '/')
/* Convert '/./' --> '/' */
strcpy(p, p+2);
else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {
/* Convert 'dir/dir1/../dir2/'
* --> 'dir/dir2/'
*/
char *rp = p -1;
while (rp >= dirname) {
if (*rp == '/')
break;
--rp;
}
if (rp > dirname) {
strcpy(rp, p+3);
p = rp;
} else {
strcpy(dirname, p+4);
p = dirname;
}
} else
p++;
} else
p++;
}
p = dirname;
len = strlen(p);
if (archive_entry_filetype(file->entry) == AE_IFLNK) {
size_t len2;
/* Convert symlink name too. */
if (archive_entry_symlink_l(file->entry, &pp, &len2,
xar->sconv) != 0) {
if (errno == ENOMEM) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for Linkname");
return (ARCHIVE_FATAL);
}
archive_set_error(&a->archive,
ARCHIVE_ERRNO_FILE_FORMAT,
"Can't translate symlink '%s' to UTF-8",
archive_entry_symlink(file->entry));
r = ARCHIVE_WARN;
}
archive_strncpy(&(file->symlink), pp, len2);
cleanup_backslash(file->symlink.s, file->symlink.length);
}
/*
* - Count up directory elements.
* - Find out the position which points the last position of
* path separator('/').
*/
slash = NULL;
for (; *p != '\0'; p++)
if (*p == '/')
slash = p;
if (slash == NULL) {
/* The pathname doesn't have a parent directory. */
file->parentdir.length = len;
archive_string_copy(&(file->basename), &(file->parentdir));
archive_string_empty(&(file->parentdir));
file->parentdir.s = '\0';
return (r);
}
/* Make a basename from dirname and slash */
*slash = '\0';
file->parentdir.length = slash - dirname;
archive_strcpy(&(file->basename), slash + 1);
return (r);
}
static int
get_path_component(char *name, int n, const char *fn)
{
char *p;
int l;
p = strchr(fn, '/');
if (p == NULL) {
if ((l = strlen(fn)) == 0)
return (0);
} else
l = p - fn;
if (l > n -1)
return (-1);
memcpy(name, fn, l);
name[l] = '\0';
return (l);
}
/*
* Add a new entry into the tree.
*/
static int
file_tree(struct archive_write *a, struct file **filepp)
{
#if defined(_WIN32) && !defined(__CYGWIN__)
char name[_MAX_FNAME];/* Included null terminator size. */
#elif defined(NAME_MAX) && NAME_MAX >= 255
char name[NAME_MAX+1];
#else
char name[256];
#endif
struct xar *xar = (struct xar *)a->format_data;
struct file *dent, *file, *np;
struct archive_entry *ent;
const char *fn, *p;
int l;
file = *filepp;
dent = xar->root;
if (file->parentdir.length > 0)
fn = p = file->parentdir.s;
else
fn = p = "";
/*
* If the path of the parent directory of `file' entry is
* the same as the path of `cur_dirent', add isoent to
* `cur_dirent'.
*/
if (archive_strlen(&(xar->cur_dirstr))
== archive_strlen(&(file->parentdir)) &&
strcmp(xar->cur_dirstr.s, fn) == 0) {
if (!file_add_child_tail(xar->cur_dirent, file)) {
np = (struct file *)__archive_rb_tree_find_node(
&(xar->cur_dirent->rbtree),
file->basename.s);
goto same_entry;
}
return (ARCHIVE_OK);
}
for (;;) {
l = get_path_component(name, sizeof(name), fn);
if (l == 0) {
np = NULL;
break;
}
if (l < 0) {
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"A name buffer is too small");
file_free(file);
*filepp = NULL;
return (ARCHIVE_FATAL);
}
np = file_find_child(dent, name);
if (np == NULL || fn[0] == '\0')
break;
/* Find next subdirectory. */
if (!np->dir) {
/* NOT Directory! */
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"`%s' is not directory, we cannot insert `%s' ",
archive_entry_pathname(np->entry),
archive_entry_pathname(file->entry));
file_free(file);
*filepp = NULL;
return (ARCHIVE_FAILED);
}
fn += l;
if (fn[0] == '/')
fn++;
dent = np;
}
if (np == NULL) {
/*
* Create virtual parent directories.
*/
while (fn[0] != '\0') {
struct file *vp;
struct archive_string as;
archive_string_init(&as);
archive_strncat(&as, p, fn - p + l);
if (as.s[as.length-1] == '/') {
as.s[as.length-1] = '\0';
as.length--;
}
vp = file_create_virtual_dir(a, xar, as.s);
if (vp == NULL) {
archive_string_free(&as);
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory");
file_free(file);
*filepp = NULL;
return (ARCHIVE_FATAL);
}
archive_string_free(&as);
if (file_gen_utility_names(a, vp) <= ARCHIVE_FAILED)
return (ARCHIVE_FATAL);
file_add_child_tail(dent, vp);
file_register(xar, vp);
np = vp;
fn += l;
if (fn[0] == '/')
fn++;
l = get_path_component(name, sizeof(name), fn);
if (l < 0) {
archive_string_free(&as);
archive_set_error(&a->archive,
ARCHIVE_ERRNO_MISC,
"A name buffer is too small");
file_free(file);
*filepp = NULL;
return (ARCHIVE_FATAL);
}
dent = np;
}
/* Found out the parent directory where isoent can be
* inserted. */
xar->cur_dirent = dent;
archive_string_empty(&(xar->cur_dirstr));
archive_string_ensure(&(xar->cur_dirstr),
archive_strlen(&(dent->parentdir)) +
archive_strlen(&(dent->basename)) + 2);
if (archive_strlen(&(dent->parentdir)) +
archive_strlen(&(dent->basename)) == 0)
xar->cur_dirstr.s[0] = 0;
else {
if (archive_strlen(&(dent->parentdir)) > 0) {
archive_string_copy(&(xar->cur_dirstr),
&(dent->parentdir));
archive_strappend_char(&(xar->cur_dirstr), '/');
}
archive_string_concat(&(xar->cur_dirstr),
&(dent->basename));
}
if (!file_add_child_tail(dent, file)) {
np = (struct file *)__archive_rb_tree_find_node(
&(dent->rbtree), file->basename.s);
goto same_entry;
}
return (ARCHIVE_OK);
}
same_entry:
/*
* We have already has the entry the filename of which is
* the same.
*/
if (archive_entry_filetype(np->entry) !=
archive_entry_filetype(file->entry)) {
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
"Found duplicate entries `%s' and its file type is "
"different",
archive_entry_pathname(np->entry));
file_free(file);
*filepp = NULL;
return (ARCHIVE_FAILED);
}
/* Swap files. */
ent = np->entry;
np->entry = file->entry;
file->entry = ent;
np->virtual = 0;
file_free(file);
*filepp = np;
return (ARCHIVE_OK);
}
static void
file_register(struct xar *xar, struct file *file)
{
file->id = xar->file_idx++;
file->next = NULL;
*xar->file_list.last = file;
xar->file_list.last = &(file->next);
}
static void
file_init_register(struct xar *xar)
{
xar->file_list.first = NULL;
xar->file_list.last = &(xar->file_list.first);
}
static void
file_free_register(struct xar *xar)
{
struct file *file, *file_next;
file = xar->file_list.first;
while (file != NULL) {
file_next = file->next;
file_free(file);
file = file_next;
}
}
/*
* Register entry to get a hardlink target.
*/
static int
file_register_hardlink(struct archive_write *a, struct file *file)
{
struct xar *xar = (struct xar *)a->format_data;
struct hardlink *hl;
const char *pathname;
archive_entry_set_nlink(file->entry, 1);
pathname = archive_entry_hardlink(file->entry);
if (pathname == NULL) {
/* This `file` is a hardlink target. */
hl = malloc(sizeof(*hl));
if (hl == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory");
return (ARCHIVE_FATAL);
}
hl->nlink = 1;
/* A hardlink target must be the first position. */
file->hlnext = NULL;
hl->file_list.first = file;
hl->file_list.last = &(file->hlnext);
__archive_rb_tree_insert_node(&(xar->hardlink_rbtree),
(struct archive_rb_node *)hl);
} else {
hl = (struct hardlink *)__archive_rb_tree_find_node(
&(xar->hardlink_rbtree), pathname);
if (hl != NULL) {
/* Insert `file` entry into the tail. */
file->hlnext = NULL;
*hl->file_list.last = file;
hl->file_list.last = &(file->hlnext);
hl->nlink++;
}
archive_entry_unset_size(file->entry);
}
return (ARCHIVE_OK);
}
/*
* Hardlinked files have to have the same location of extent.
* We have to find out hardlink target entries for entries which
* have a hardlink target name.
*/
static void
file_connect_hardlink_files(struct xar *xar)
{
struct archive_rb_node *n;
struct hardlink *hl;
struct file *target, *nf;
ARCHIVE_RB_TREE_FOREACH(n, &(xar->hardlink_rbtree)) {
hl = (struct hardlink *)n;
/* The first entry must be a hardlink target. */
target = hl->file_list.first;
archive_entry_set_nlink(target->entry, hl->nlink);
if (hl->nlink > 1)
/* It means this file is a hardlink
* targe itself. */
target->hardlink_target = target;
for (nf = target->hlnext;
nf != NULL; nf = nf->hlnext) {
nf->hardlink_target = target;
archive_entry_set_nlink(nf->entry, hl->nlink);
}
}
}
static int
file_hd_cmp_node(const struct archive_rb_node *n1,
const struct archive_rb_node *n2)
{
struct hardlink *h1 = (struct hardlink *)n1;
struct hardlink *h2 = (struct hardlink *)n2;
return (strcmp(archive_entry_pathname(h1->file_list.first->entry),
archive_entry_pathname(h2->file_list.first->entry)));
}
static int
file_hd_cmp_key(const struct archive_rb_node *n, const void *key)
{
struct hardlink *h = (struct hardlink *)n;
return (strcmp(archive_entry_pathname(h->file_list.first->entry),
(const char *)key));
}
static void
file_init_hardlinks(struct xar *xar)
{
static const struct archive_rb_tree_ops rb_ops = {
file_hd_cmp_node, file_hd_cmp_key,
};
__archive_rb_tree_init(&(xar->hardlink_rbtree), &rb_ops);
}
static void
file_free_hardlinks(struct xar *xar)
{
struct archive_rb_node *n, *next;
for (n = ARCHIVE_RB_TREE_MIN(&(xar->hardlink_rbtree)); n;) {
next = __archive_rb_tree_iterate(&(xar->hardlink_rbtree),
n, ARCHIVE_RB_DIR_RIGHT);
free(n);
n = next;
}
}
static void
checksum_init(struct chksumwork *sumwrk, enum sumalg sum_alg)
{
sumwrk->alg = sum_alg;
switch (sum_alg) {
case CKSUM_NONE:
break;
case CKSUM_SHA1:
archive_sha1_init(&(sumwrk->sha1ctx));
break;
case CKSUM_MD5:
archive_md5_init(&(sumwrk->md5ctx));
break;
}
}
static void
checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size)
{
switch (sumwrk->alg) {
case CKSUM_NONE:
break;
case CKSUM_SHA1:
archive_sha1_update(&(sumwrk->sha1ctx), buff, size);
break;
case CKSUM_MD5:
archive_md5_update(&(sumwrk->md5ctx), buff, size);
break;
}
}
static void
checksum_final(struct chksumwork *sumwrk, struct chksumval *sumval)
{
switch (sumwrk->alg) {
case CKSUM_NONE:
sumval->len = 0;
break;
case CKSUM_SHA1:
archive_sha1_final(&(sumwrk->sha1ctx), sumval->val);
sumval->len = SHA1_SIZE;
break;
case CKSUM_MD5:
archive_md5_final(&(sumwrk->md5ctx), sumval->val);
sumval->len = MD5_SIZE;
break;
}
sumval->alg = sumwrk->alg;
}
#if !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
static int
compression_init_encoder_gzip(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 = 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;
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_gzip;
lastrm->end = compression_end_gzip;
return (ARCHIVE_OK);
}
static int
compression_code_gzip(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 = 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 = 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_gzip(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);
}
#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
#if defined(HAVE_LZMA_H)
static int
compression_init_encoder_lzma(struct archive *a,
struct la_zstream *lastrm, int level)
{
static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;
lzma_stream *strm;
lzma_options_lzma lzma_opt;
int r;
if (lastrm->valid)
compression_end(a, lastrm);
if (lzma_lzma_preset(&lzma_opt, level)) {
lastrm->real_stream = NULL;
archive_set_error(a, ENOMEM,
"Internal error initializing compression library");
return (ARCHIVE_FATAL);
}
strm = calloc(1, sizeof(*strm));
if (strm == NULL) {
archive_set_error(a, ENOMEM,
"Can't allocate memory for lzma stream");
return (ARCHIVE_FATAL);
}
*strm = lzma_init_data;
r = lzma_alone_encoder(strm, &lzma_opt);
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_xz(struct archive *a,
struct la_zstream *lastrm, int level)
{
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 xz stream");
return (ARCHIVE_FATAL);
}
lzmafilters = (lzma_filter *)(strm+1);
if (level > 6)
level = 6;
if (lzma_lzma_preset(&lzma_opt, level)) {
lastrm->real_stream = NULL;
archive_set_error(a, ENOMEM,
"Internal error initializing compression library");
return (ARCHIVE_FATAL);
}
lzmafilters[0].id = LZMA_FILTER_LZMA2;
lzmafilters[0].options = &lzma_opt;
lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
*strm = lzma_init_data;
r = lzma_stream_encoder(strm, lzmafilters, LZMA_CHECK_CRC64);
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_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_lzma(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_xz(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, "xz"));
}
#endif
static int
xar_compression_init_encoder(struct archive_write *a)
{
struct xar *xar;
int r;
xar = (struct xar *)a->format_data;
switch (xar->opt_compression) {
case GZIP:
r = compression_init_encoder_gzip(
&(a->archive), &(xar->stream),
xar->opt_compression_level, 1);
break;
case BZIP2:
r = compression_init_encoder_bzip2(
&(a->archive), &(xar->stream),
xar->opt_compression_level);
break;
case LZMA:
r = compression_init_encoder_lzma(
&(a->archive), &(xar->stream),
xar->opt_compression_level);
break;
case XZ:
r = compression_init_encoder_xz(
&(a->archive), &(xar->stream),
xar->opt_compression_level);
break;
default:
r = ARCHIVE_OK;
break;
}
if (r == ARCHIVE_OK) {
xar->stream.total_in = 0;
xar->stream.next_out = xar->wbuff;
xar->stream.avail_out = sizeof(xar->wbuff);
xar->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)
return (lastrm->end(a, lastrm));
return (ARCHIVE_OK);
}
static int
save_xattrs(struct archive_write *a, struct file *file)
{
struct xar *xar;
const char *name;
const void *value;
struct heap_data *heap;
size_t size;
int count, r;
xar = (struct xar *)a->format_data;
count = archive_entry_xattr_reset(file->entry);
if (count == 0)
return (ARCHIVE_OK);
while (count--) {
archive_entry_xattr_next(file->entry,
&name, &value, &size);
checksum_init(&(xar->a_sumwrk), xar->opt_sumalg);
checksum_init(&(xar->e_sumwrk), xar->opt_sumalg);
heap = calloc(1, sizeof(*heap));
if (heap == NULL) {
archive_set_error(&a->archive, ENOMEM,
"Can't allocate memory for xattr");
return (ARCHIVE_FATAL);
}
heap->id = file->ea_idx++;
heap->temp_offset = xar->temp_offset;
heap->size = size;/* save a extracted size */
heap->compression = xar->opt_compression;
/* Get a extracted sumcheck value. */
checksum_update(&(xar->e_sumwrk), value, size);
checksum_final(&(xar->e_sumwrk), &(heap->e_sum));
/*
* Not compression to xattr is simple way.
*/
if (heap->compression == NONE) {
checksum_update(&(xar->a_sumwrk), value, size);
checksum_final(&(xar->a_sumwrk), &(heap->a_sum));
if (write_to_temp(a, value, size)
!= ARCHIVE_OK)
return (ARCHIVE_FATAL);
heap->length = size;
/* Add heap to the tail of file->xattr. */
heap->next = NULL;
*file->xattr.last = heap;
file->xattr.last = &(heap->next);
/* Next xattr */
continue;
}
/*
* Init compression library.
*/
r = xar_compression_init_encoder(a);
if (r != ARCHIVE_OK) {
free(heap);
return (ARCHIVE_FATAL);
}
xar->stream.next_in = (const unsigned char *)value;
xar->stream.avail_in = size;
for (;;) {
r = compression_code(&(a->archive),
&(xar->stream), ARCHIVE_Z_FINISH);
if (r != ARCHIVE_OK && r != ARCHIVE_EOF) {
free(heap);
return (ARCHIVE_FATAL);
}
size = sizeof(xar->wbuff) - xar->stream.avail_out;
checksum_update(&(xar->a_sumwrk),
xar->wbuff, size);
if (write_to_temp(a, xar->wbuff, size)
!= ARCHIVE_OK)
return (ARCHIVE_FATAL);
if (r == ARCHIVE_OK) {
xar->stream.next_out = xar->wbuff;
xar->stream.avail_out = sizeof(xar->wbuff);
} else {
checksum_final(&(xar->a_sumwrk),
&(heap->a_sum));
heap->length = xar->stream.total_out;
/* Add heap to the tail of file->xattr. */
heap->next = NULL;
*file->xattr.last = heap;
file->xattr.last = &(heap->next);
break;
}
}
/* Clean up compression library. */
r = compression_end(&(a->archive), &(xar->stream));
if (r != ARCHIVE_OK)
return (ARCHIVE_FATAL);
}
return (ARCHIVE_OK);
}
static int
getalgsize(enum sumalg sumalg)
{
switch (sumalg) {
default:
case CKSUM_NONE:
return (0);
case CKSUM_SHA1:
return (SHA1_SIZE);
case CKSUM_MD5:
return (MD5_SIZE);
}
}
static const char *
getalgname(enum sumalg sumalg)
{
switch (sumalg) {
default:
case CKSUM_NONE:
return (NULL);
case CKSUM_SHA1:
return (SHA1_NAME);
case CKSUM_MD5:
return (MD5_NAME);
}
}
#endif /* Support xar format */