#define _FILE_OFFSET_BITS 64
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <libgen.h>
#include <errno.h>
#include <limits.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <inttypes.h>
#include <time.h>
#include <libxml/xmlwriter.h>
#include <libxml/xmlreader.h>
#include <libxml/xmlstring.h>
#ifndef HAVE_ASPRINTF
#include "asprintf.h"
#endif
#include "xar.h"
#include "filetree.h"
#include "archive.h"
#include "signature.h"
#include "arcmod.h"
#include "io.h"
#include "util.h"
#include "subdoc.h"
#include "darwinattr.h"
#ifdef __APPLE__
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonDigestSPI.h>
#include "hash.h"
#endif
#ifndef O_EXLOCK
#define O_EXLOCK 0
#endif
#ifndef O_SHLOCK
#define O_SHLOCK 0
#endif
#ifndef LONG_MAX
#define LONG_MAX INT32_MAX
#endif
#ifndef LONG_MIN
#define LONG_MIN INT32_MIN
#endif
#if LIBXML_VERSION < 20618
#define xmlDictCleanup()
#endif
static int32_t xar_unserialize(xar_t x);
void xar_serialize(xar_t x, const char *file);
static xar_t xar_new() {
xar_t ret;
ret = malloc(sizeof(struct __xar_t));
if(!ret) return NULL;
memset(XAR(ret), 0, sizeof(struct __xar_t));
XAR(ret)->readbuf_len = 4096;
XAR(ret)->readbuf = malloc(XAR(ret)->readbuf_len);
if(!XAR(ret)->readbuf) {
free((void *)ret);
return NULL;
}
XAR(ret)->offset = 0;
XAR(ret)->zs.zalloc = Z_NULL;
XAR(ret)->zs.zfree = Z_NULL;
XAR(ret)->zs.opaque = Z_NULL;
XAR(ret)->ino_hash = xmlHashCreate(0);
XAR(ret)->link_hash = xmlHashCreate(0);
XAR(ret)->csum_hash = xmlHashCreate(0);
XAR(ret)->subdocs = NULL;
return ret;
}
static int32_t xar_parse_header(xar_t x) {
ssize_t r;
int off = 0;
int sz2read = 0;
r = xar_read_fd(XAR(x)->fd, (char *)&XAR(x)->header.magic+off, sizeof(XAR(x)->header.magic)-off);
if ( r == -1 )
return r;
XAR(x)->header.magic = ntohl(XAR(x)->header.magic);
if( XAR(x)->header.magic != XAR_HEADER_MAGIC ) {
return -1;
}
r = xar_read_fd(XAR(x)->fd, (char *)&XAR(x)->header.size+off, sizeof(XAR(x)->header.size)-off);
if ( r == -1 )
return r;
XAR(x)->header.size = ntohs(XAR(x)->header.size);
if( XAR(x)->header.size > sizeof(xar_header_t) )
sz2read = sizeof(xar_header_t);
else
sz2read = XAR(x)->header.size;
off = sizeof(XAR(x)->header.magic) + sizeof(XAR(x)->header.size);
r = xar_read_fd(XAR(x)->fd, ((char *)&XAR(x)->header)+off, sizeof(xar_header_t)-off);
if ( r == -1 )
return r;
XAR(x)->header.version = ntohs(XAR(x)->header.version);
XAR(x)->header.toc_length_compressed = xar_ntoh64(XAR(x)->header.toc_length_compressed);
XAR(x)->header.toc_length_uncompressed = xar_ntoh64(XAR(x)->header.toc_length_uncompressed);
XAR(x)->header.cksum_alg = ntohl(XAR(x)->header.cksum_alg);
off = XAR(x)->header.size - sz2read;
if( off > 0 )
r = lseek(XAR(x)->fd, (off_t)off, SEEK_CUR);
if ( (r == -1) && (errno != ESPIPE) )
;
return 0;
}
xar_t xar_open(const char *file, int32_t flags) {
xar_t ret;
ret = xar_new();
if( !ret ) return NULL;
if( !file )
file = "-";
XAR(ret)->filename = strdup(file);
#ifndef __APPLE__
OpenSSL_add_all_digests();
#endif // __APPLE__
if( flags ) {
char *tmp1, *tmp2, *tmp3, *tmp4;
tmp1 = tmp2 = strdup(file);
tmp3 = dirname(tmp2);
XAR(ret)->dirname = strdup(tmp3);
asprintf(&tmp4, "%s/xar.heap.XXXXXX", tmp3);
free(tmp1);
if( strcmp(file, "-") == 0 )
XAR(ret)->fd = 1;
else{
XAR(ret)->fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_EXLOCK, 0644);
if( (-1 == XAR(ret)->fd ) && (ENOTSUP == errno) ){
XAR(ret)->fd = open(file, O_WRONLY | O_CREAT | O_TRUNC , 0644);
}
}
XAR(ret)->heap_fd = mkstemp(tmp4);
if( XAR(ret)->heap_fd < 0 ) {
close(XAR(ret)->fd);
free(XAR(ret));
return NULL;
}
unlink(tmp4);
free(tmp4);
deflateInit(&XAR(ret)->zs, Z_BEST_COMPRESSION);
if( XAR(ret)->fd < 0 ) {
xar_close(ret);
return NULL;
}
XAR(ret)->heap_offset += 20;
XAR(ret)->heap_len += 20;
xar_opt_set(ret, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP);
xar_opt_set(ret, XAR_OPT_FILECKSUM, XAR_OPT_VAL_SHA1);
} else {
#ifdef __APPLE__
unsigned char toccksum[CC_SHA512_DIGEST_LENGTH]; unsigned char cval[CC_SHA512_DIGEST_LENGTH]; #else
unsigned char toccksum[EVP_MAX_MD_SIZE];
unsigned char cval[EVP_MAX_MD_SIZE];
#endif
unsigned int tlen;
#ifndef __APPLE__
const EVP_MD *md;
#endif // !__APPLE__
if( strcmp(file, "-") == 0 )
XAR(ret)->fd = 0;
else{
XAR(ret)->fd = open(file, O_RDONLY | O_SHLOCK);
if( (-1 == XAR(ret)->fd ) && (ENOTSUP == errno) ){
XAR(ret)->fd = open(file, O_RDONLY);
}
}
XAR(ret)->heap_fd = -1;
inflateInit(&XAR(ret)->zs);
if( XAR(ret)->fd < 0 ) {
xar_close(ret);
return NULL;
}
if( xar_parse_header(ret) != 0 ) {
xar_close(ret);
return NULL;
}
switch(XAR(ret)->header.cksum_alg) {
case XAR_CKSUM_NONE:
break;
case XAR_CKSUM_SHA1:
XAR(ret)->docksum = 1;
#ifdef __APPLE__
XAR(ret)->toc_ctx = digestRef_from_name("sha1", &XAR(ret)->toc_ctx_length);
#else
md = EVP_get_digestbyname("sha1");
EVP_DigestInit(&XAR(ret)->toc_ctx, md);
#endif
break;
case XAR_CKSUM_MD5:
XAR(ret)->docksum = 1;
#ifdef __APPLE__
XAR(ret)->toc_ctx = digestRef_from_name("md5", &XAR(ret)->toc_ctx_length);
#else
md = EVP_get_digestbyname("md5");
EVP_DigestInit(&XAR(ret)->toc_ctx, md);
#endif
break;
default:
fprintf(stderr, "Unknown hashing algorithm, skipping\n");
break;
};
if( xar_unserialize(ret) != 0 ) {
xar_close(ret);
return NULL;
}
int cksum_match = 0;
const char* cksum_style = xar_attr_get(XAR_FILE(ret), "checksum", "style");
switch(XAR(ret)->header.cksum_alg) {
case XAR_CKSUM_NONE:
cksum_match = (cksum_style == NULL || strcmp(cksum_style, XAR_OPT_VAL_NONE) == 0);
break;
case XAR_CKSUM_SHA1:
cksum_match = (cksum_style != NULL && strcmp(cksum_style, XAR_OPT_VAL_SHA1) == 0);
break;
case XAR_CKSUM_MD5:
cksum_match = (cksum_style != NULL && strcmp(cksum_style, XAR_OPT_VAL_MD5) == 0);
break;
default:
cksum_match = 0;
break;
}
if( !cksum_match ) {
fprintf(stderr, "Checksum style mismatch!\n");
xar_close(ret);
return NULL;
}
if( xar_signature_first(ret) != NULL && XAR(ret)->header.cksum_alg == XAR_CKSUM_NONE ) {
fprintf(stderr, "Checksum/signature mismatch!\n");
xar_close(ret);
return NULL;
}
if( !XAR(ret)->docksum )
return ret;
#ifdef __APPLE__
CCDigestFinal(XAR(ret)->toc_ctx, toccksum);
CCDigestDestroy(XAR(ret)->toc_ctx);
XAR(ret)->toc_ctx = NULL;
tlen = XAR(ret)->toc_ctx_length;
#else
EVP_DigestFinal(&XAR(ret)->toc_ctx, toccksum, &tlen);
#endif
const char *value;
uint64_t offset = 0;
uint64_t length = tlen;
if( xar_prop_get( XAR_FILE(ret) , "checksum/offset", &value) == 0 ) {
errno = 0;
offset = strtoull( value, (char **)NULL, 10);
if( errno != 0 ) {
xar_close(ret);
return NULL;
}
} else if( xar_signature_first(ret) != NULL ) {
xar_close(ret);
return NULL;
}
XAR(ret)->heap_offset = xar_get_heap_offset(ret) + offset;
if( lseek(XAR(ret)->fd, XAR(ret)->heap_offset, SEEK_SET) == -1 ) {
xar_close(ret);
return NULL;
}
if( xar_prop_get( XAR_FILE(ret) , "checksum/size", &value) == 0 ) {
errno = 0;
length = strtoull( value, (char **)NULL, 10);
if( errno != 0 ) {
xar_close(ret);
return NULL;
}
} else if( xar_signature_first(ret) != NULL ) {
xar_close(ret);
return NULL;
}
if( length != tlen ) {
xar_close(ret);
return NULL;
}
xar_read_fd(XAR(ret)->fd, cval, tlen);
XAR(ret)->heap_offset += tlen;
if( memcmp(cval, toccksum, tlen) != 0 ) {
fprintf(stderr, "Checksums do not match!\n");
xar_close(ret);
return NULL;
}
}
return ret;
}
int xar_close(xar_t x) {
xar_attr_t a;
xar_file_t f;
int ret, retval = 0;
if( XAR(x)->heap_fd != -1 ) {
char *tmpser;
void *rbuf, *wbuf = NULL;
int fd, r, off, wbytes, rbytes;
long rsize, wsize;
z_stream zs;
uint64_t ungztoc, gztoc;
#ifdef __APPLE__
unsigned char chkstr[CC_SHA512_DIGEST_LENGTH]; #else
unsigned char chkstr[EVP_MAX_MD_SIZE];
#endif
int tocfd;
char timestr[128];
struct tm tmptm;
time_t t;
tmpser = (char *)xar_opt_get(x, XAR_OPT_TOCCKSUM);
if( !tmpser ) tmpser = XAR_OPT_VAL_SHA1;
if( (strcmp(tmpser, XAR_OPT_VAL_NONE) != 0) ) {
#ifndef __APPLE__
const EVP_MD *md;
#endif // __APPLE__
xar_prop_set(XAR_FILE(x), "checksum", NULL);
if( strcmp(tmpser, XAR_OPT_VAL_SHA1) == 0 ) {
#ifdef __APPLE__
XAR(x)->toc_ctx = digestRef_from_name("sha1", &XAR(x)->toc_ctx_length);
#else
md = EVP_get_digestbyname("sha1");
EVP_DigestInit(&XAR(x)->toc_ctx, md);
#endif
XAR(x)->header.cksum_alg = htonl(XAR_CKSUM_SHA1);
xar_attr_set(XAR_FILE(x), "checksum", "style", XAR_OPT_VAL_SHA1);
xar_prop_set(XAR_FILE(x), "checksum/size", "20");
}
if( strcmp(tmpser, XAR_OPT_VAL_MD5) == 0 ) {
#ifdef __APPLE__
XAR(x)->toc_ctx = digestRef_from_name("md5", &XAR(x)->toc_ctx_length);
#else
md = EVP_get_digestbyname("md5");
EVP_DigestInit(&XAR(x)->toc_ctx, md);
#endif
XAR(x)->header.cksum_alg = htonl(XAR_CKSUM_MD5);
xar_attr_set(XAR_FILE(x), "checksum", "style", XAR_OPT_VAL_MD5);
xar_prop_set(XAR_FILE(x), "checksum/size", "16");
}
xar_prop_set(XAR_FILE(x), "checksum/offset", "0");
XAR(x)->docksum = 1;
} else {
XAR(x)->docksum = 0;
XAR(x)->header.cksum_alg = XAR_CKSUM_NONE;
}
t = time(NULL);
gmtime_r(&t, &tmptm);
memset(timestr, 0, sizeof(timestr));
strftime(timestr, sizeof(timestr), "%FT%T", &tmptm);
xar_prop_set(XAR_FILE(x), "creation-time", timestr);
asprintf(&tmpser, "%s/xar.toc.XXXXXX", XAR(x)->dirname);
fd = mkstemp(tmpser);
xar_serialize(x, tmpser);
unlink(tmpser);
free(tmpser);
asprintf(&tmpser, "%s/xar.toc.XXXXXX", XAR(x)->dirname);
tocfd = mkstemp(tmpser);
unlink(tmpser);
free(tmpser);
rsize = wsize = 4096;
const char * opt = xar_opt_get(x, XAR_OPT_RSIZE);
if ( opt ) {
rsize = strtol(opt, NULL, 0);
if ( ((rsize == LONG_MAX) || (rsize == LONG_MIN)) && (errno == ERANGE) ) {
rsize = wsize;
}
}
rbuf = malloc(rsize);
if( !rbuf ) {
retval = -1;
close(fd);
close(tocfd);
goto CLOSE_BAIL;
}
zs.zalloc = Z_NULL;
zs.zfree = Z_NULL;
zs.opaque = Z_NULL;
deflateInit(&zs, Z_BEST_COMPRESSION);
ungztoc = gztoc = 0;
while(1) {
r = read(fd, rbuf, rsize);
if( (r < 0) && (errno == EINTR) )
continue;
if( r == 0 )
break;
ungztoc += r;
zs.avail_in = r;
zs.next_in = (void *)rbuf;
zs.next_out = NULL;
zs.avail_out = 0;
wsize = rsize/2;
off = 0;
while( zs.avail_in != 0 ) {
wsize *= 2;
wbuf = realloc(wbuf, wsize);
zs.next_out = ((unsigned char *)wbuf) + off;
zs.avail_out = wsize - off;
ret = deflate(&zs, Z_SYNC_FLUSH);
off = wsize - zs.avail_out;
}
wbytes = off;
off = 0;
do {
r = write(tocfd, ((char *)wbuf)+off, wbytes-off);
if( (r < 0) && (errno == EINTR) )
continue;
if( r < 0 ) {
xar_err_new(x);
xar_err_set_string(x, "Error closing xar archive");
retval = -1;
goto CLOSEEND;
}
if( XAR(x)->docksum )
#ifdef __APPLE__
CCDigestUpdate(XAR(x)->toc_ctx, ((char*)wbuf)+off, r);
#else
EVP_DigestUpdate(&XAR(x)->toc_ctx, ((char*)wbuf)+off, r);
#endif // __APPLE__
off += r;
gztoc += r;
} while( off < wbytes );
}
zs.next_in = NULL;
zs.avail_in = 0;
zs.next_out = wbuf;
zs.avail_out = wsize;
deflate(&zs, Z_FINISH);
r = write(tocfd, wbuf, wsize - zs.avail_out);
gztoc += r;
if( XAR(x)->docksum )
#ifdef __APPLE__
CCDigestUpdate(XAR(x)->toc_ctx, wbuf, r);
#else
EVP_DigestUpdate(&XAR(x)->toc_ctx, wbuf, r);
#endif // __APPLE__
deflateEnd(&zs);
XAR(x)->header.magic = htonl(XAR_HEADER_MAGIC);
XAR(x)->header.size = ntohs(sizeof(xar_header_t));
XAR(x)->header.version = ntohs(1);
XAR(x)->header.toc_length_uncompressed = xar_ntoh64(ungztoc);
XAR(x)->header.toc_length_compressed = xar_ntoh64(gztoc);
write(XAR(x)->fd, &XAR(x)->header, sizeof(xar_header_t));
lseek(tocfd, (off_t)0, SEEK_SET);
while(1) {
r = read(tocfd, rbuf, rsize);
if( (r < 0) && (errno == EINTR) )
continue;
if( r == 0 )
break;
wbytes = r;
off = 0;
do {
r = write(XAR(x)->fd, ((char *)rbuf)+off, wbytes-off);
if( (r < 0) && (errno == EINTR) )
continue;
if( r < 0 ) {
xar_err_new(x);
xar_err_set_string(x, "Error closing xar archive");
retval = -1;
goto CLOSEEND;
}
off += r;
} while( off < wbytes );
}
if( XAR(x)->docksum ) {
unsigned int l = r;
memset(chkstr, 0, sizeof(chkstr));
#ifdef __APPLE__
CCDigestFinal(XAR(x)->toc_ctx, chkstr);
CCDigestDestroy(XAR(x)->toc_ctx);
XAR(x)->toc_ctx = NULL;
l = XAR(x)->toc_ctx_length;
#else
EVP_DigestFinal(&XAR(x)->toc_ctx, chkstr, &l);
#endif
r = l;
write(XAR(x)->fd, chkstr, r);
}
if( XAR(x)->docksum && XAR(x)->signatures ) {
xar_signature_t sig;
uint32_t data_len = r;
uint32_t signed_len = 0;
uint8_t *signed_data = NULL;
for(sig = XAR(x)->signatures; sig; sig = XAR_SIGNATURE(sig)->next ){
signed_len = XAR_SIGNATURE(sig)->len;
if( 0 != sig->signer_callback( sig, sig->callback_context, chkstr, data_len, &signed_data, &signed_len ) ){
fprintf(stderr, "Error signing data.\n");
retval = -1;
close(fd);
close(tocfd);
goto CLOSE_BAIL;
}
if( signed_len != XAR_SIGNATURE(sig)->len ){
fprintf(stderr, "Signed data not the proper length. %i should be %i.\n",signed_len,XAR_SIGNATURE(sig)->len);
retval = -1;
close(fd);
close(tocfd);
goto CLOSE_BAIL;
}
write(XAR(x)->fd, signed_data,XAR_SIGNATURE(sig)->len);
free(signed_data);
}
xar_signature_remove( XAR(x)->signatures );
XAR(x)->signatures = NULL;
}
if( lseek(XAR(x)->heap_fd, (off_t)0, SEEK_SET) < 0 ) {
fprintf(stderr, "Error lseeking to offset 0: %s\n", strerror(errno));
exit(1);
}
rbytes = 0;
while(1) {
if( (XAR(x)->heap_len - rbytes) < rsize )
rsize = XAR(x)->heap_len - rbytes;
r = read(XAR(x)->heap_fd, rbuf, rsize);
if( (r < 0 ) && (errno == EINTR) )
continue;
if( r == 0 )
break;
rbytes += r;
wbytes = r;
off = 0;
do {
r = write(XAR(x)->fd, ((char *)rbuf)+off, wbytes);
if( (r < 0 ) && (errno == EINTR) )
continue;
if( r < 0 ) {
retval = -1;
close(fd);
close(tocfd);
goto CLOSEEND;
}
off += r;
} while( off < wbytes );
if( rbytes >= XAR(x)->heap_len )
break;
}
CLOSEEND:
close(fd);
close(tocfd);
free(rbuf);
free(wbuf);
deflateEnd(&XAR(x)->zs);
} else {
xar_signature_remove( XAR(x)->signatures );
XAR(x)->signatures = NULL;
inflateEnd(&XAR(x)->zs);
}
CLOSE_BAIL:
while(XAR(x)->subdocs) {
xar_subdoc_remove(XAR(x)->subdocs);
}
while(XAR(x)->attrs) {
a = XAR(x)->attrs;
XAR(x)->attrs = XAR_ATTR(a)->next;
xar_attr_free(a);
}
while(XAR(x)->props) {
xar_prop_t p;
p = XAR(x)->props;
XAR(x)->props = XAR_PROP(p)->next;
xar_prop_free(p);
}
while(XAR(x)->files) {
f = XAR(x)->files;
XAR(x)->files = XAR_FILE(f)->next;
xar_file_free(f);
}
xmlHashFree(XAR(x)->ino_hash, NULL);
xmlHashFree(XAR(x)->link_hash, NULL);
xmlHashFree(XAR(x)->csum_hash, NULL);
close(XAR(x)->fd);
if( XAR(x)->heap_fd >= 0 )
close(XAR(x)->heap_fd);
free((char *)XAR(x)->filename);
free((char *)XAR(x)->dirname);
free(XAR(x)->readbuf);
free((void *)x);
return retval;
}
const char *xar_opt_get(xar_t x, const char *option) {
xar_attr_t i;
for(i = XAR(x)->attrs; i && XAR_ATTR(i)->next; i = XAR_ATTR(i)->next) {
if(strcmp(XAR_ATTR(i)->key, option)==0)
return XAR_ATTR(i)->value;
}
if( i && (strcmp(XAR_ATTR(i)->key, option)==0) )
return XAR_ATTR(i)->value;
return NULL;
}
int32_t xar_opt_set(xar_t x, const char *option, const char *value) {
xar_attr_t currentAttr, a;
if( (strcmp(option, XAR_OPT_TOCCKSUM) == 0) ) {
if( strcmp(value, XAR_OPT_VAL_NONE) == 0 ) {
XAR(x)->heap_offset = 0;
}
if( strcmp(value, XAR_OPT_VAL_SHA1) == 0 ) {
XAR(x)->heap_offset = 20;
}
if( strcmp(value, XAR_OPT_VAL_MD5) == 0 ) {
XAR(x)->heap_offset = 16;
}
}
a = xar_attr_new();
XAR_ATTR(a)->key = strdup(option);
XAR_ATTR(a)->value = strdup(value);
XAR_ATTR(a)->next = XAR(x)->attrs;
XAR(x)->attrs = a;
return 0;
}
int32_t xar_opt_unset(xar_t x, const char *option) {
xar_attr_t currentAttr, previousAttr = NULL;
for(currentAttr = XAR(x)->attrs;
currentAttr ;
previousAttr = currentAttr, currentAttr = XAR_ATTR(currentAttr)->next) {
if(strcmp(XAR_ATTR(currentAttr)->key, option)==0) {
if( previousAttr == NULL )
XAR(previousAttr)->attrs = XAR_ATTR(currentAttr)->next;
else
XAR_ATTR(previousAttr)->next = XAR_ATTR(currentAttr)->next;
xar_attr_free(currentAttr);
currentAttr = previousAttr;
}
}
return 0;
}
static xar_file_t xar_add_node(xar_t x, xar_file_t f, const char *name, const char *prefix, const char *realpath, int srcpath) {
xar_file_t ret;
const char *path;
char *tmp;
char idstr[32];
if( !f ) {
if( realpath )
asprintf(&tmp, "%s", realpath);
else
asprintf(&tmp, "%s%s%s", XAR(x)->path_prefix, prefix, name);
if( lstat(tmp, &XAR(x)->sbcache) != 0 ) {
free(tmp);
return NULL;
}
ret = xar_file_new(NULL);
if( !ret )
return NULL;
memset(idstr, 0, sizeof(idstr));
snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
xar_attr_set(ret, NULL, "id", idstr);
XAR_FILE(ret)->parent = NULL;
XAR_FILE(ret)->fspath = tmp;
if( XAR(x)->files == NULL )
XAR(x)->files = ret;
else {
XAR_FILE(ret)->next = XAR(x)->files;
XAR(x)->files = ret;
}
} else {
path = XAR_FILE(f)->fspath;
if( strcmp(prefix, "../") == 0 ) {
int len1, len2;
len1 = strlen(path);
len2 = strlen(name);
if( (len1>=len2) && (strcmp(path+(len1-len2), name) == 0) ) {
return f;
}
}
if( realpath ){
asprintf(&tmp, "%s", realpath);
}else
asprintf(&tmp, "%s/%s%s", path, prefix, name);
if( lstat(tmp, &XAR(x)->sbcache) != 0 ) {
free(tmp);
return NULL;
}
ret = xar_file_new(f);
if( !ret )
return NULL;
memset(idstr, 0, sizeof(idstr));
snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
xar_attr_set(ret, NULL, "id", idstr);
XAR_FILE(ret)->fspath = tmp;
}
xar_prop_set(ret, "name", name);
if( xar_arcmod_archive(x, ret, XAR_FILE(ret)->fspath, NULL, 0) < 0 ) {
xar_file_t i = NULL;
if( f ) {
if( ret == XAR_FILE(f)->children )
XAR_FILE(f)->children = XAR_FILE(ret)->next;
else
for( i = XAR_FILE(f)->children; i && (XAR_FILE(i)->next != ret); i = XAR_FILE(i)->next );
} else {
if( ret == XAR(x)->files )
XAR(x)->files = XAR_FILE(ret)->next;
else
for( i = XAR(x)->files; i && (XAR_FILE(i)->next != ret); i = XAR_FILE(i)->next );
}
if( i )
XAR_FILE(i)->next = XAR_FILE(ret)->next;
xar_file_free(ret);
return NULL;
}
return ret;
}
static xar_file_t xar_add_pseudodir(xar_t x, xar_file_t f, const char *name, const char *prefix, const char *realpath)
{
xar_file_t ret;
const char *path;
char *tmp;
char idstr[32];
if( !f ) {
if( realpath )
asprintf(&tmp, "%s", realpath);
else
asprintf(&tmp, "%s%s%s", XAR(x)->path_prefix, prefix, name);
if( lstat(tmp, &XAR(x)->sbcache) != 0 ) {
free(tmp);
return NULL;
}
ret = xar_file_new(NULL);
if( !ret )
return NULL;
memset(idstr, 0, sizeof(idstr));
snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
xar_attr_set(ret, NULL, "id", idstr);
XAR_FILE(ret)->parent = NULL;
XAR_FILE(ret)->fspath = tmp;
if( XAR(x)->files == NULL )
XAR(x)->files = ret;
else {
XAR_FILE(ret)->next = XAR(x)->files;
XAR(x)->files = ret;
}
} else {
path = XAR_FILE(f)->fspath;
if( strcmp(prefix, "../") == 0 ) {
int len1, len2;
len1 = strlen(path);
len2 = strlen(name);
if( (len1>=len2) && (strcmp(path+(len1-len2), name) == 0) ) {
return f;
}
}
if( realpath ){
asprintf(&tmp, "%s", realpath);
}else
asprintf(&tmp, "%s/%s%s", path, prefix, name);
if( lstat(tmp, &XAR(x)->sbcache) != 0 ) {
free(tmp);
return NULL;
}
ret = xar_file_new(f);
if( !ret )
return NULL;
memset(idstr, 0, sizeof(idstr));
snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
xar_attr_set(ret, NULL, "id", idstr);
XAR_FILE(ret)->fspath = tmp;
}
xar_prop_set(ret, "name", name);
xar_prop_set(ret, "type", "directory");
return ret;
}
static xar_file_t xar_add_r(xar_t x, xar_file_t f, const char *path, const char *prefix) {
xar_file_t i = NULL, ret, ret2, start = NULL;
char *tmp1, *tmp2, *tmp3;
if( path && (path[0] == '\0') ) {
return f;
}
tmp1 = tmp2 = strdup(path);
tmp3 = strsep(&tmp2, "/");
if( tmp3 && tmp2 && (tmp3[0] == '\0') ) {
ret2 = xar_add_r(x, f, tmp2, "");
free(tmp1);
return ret2;
}
if( strcmp(tmp3, "..") == 0 ) {
char *prefixstr;
if( !XAR(x)->skipwarn ) {
xar_err_new(x);
xar_err_set_string(x, "Skipping .. in path");
xar_err_callback(x, XAR_SEVERITY_WARNING, XAR_ERR_ARCHIVE_CREATION);
XAR(x)->skipwarn = 1;
}
asprintf(&prefixstr, "%s../", prefix);
ret2 = xar_add_r(x, f, tmp2, prefixstr);
free(prefixstr);
free(tmp1);
return ret2;
}
if( strcmp(tmp3, ".") == 0 ) {
if( tmp2 )
ret2 = xar_add_r(x, f, tmp2, prefix);
else
ret2 = NULL;
free(tmp1);
return ret2;
}
if( !f ) {
start = XAR(x)->files;
} else {
start = XAR_FILE(f)->children;
}
for( i = start; i; i = XAR_FILE(i)->next ) {
const char *n;
xar_prop_get(i, "name", &n);
if( strcmp(n, tmp3) == 0 ) {
if( !tmp2 ) {
free(tmp1);
return i;
}
ret2 = xar_add_r(x, i, tmp2, "");
free(tmp1);
return ret2;
}
}
if( tmp2 ) {
ret = xar_add_pseudodir(x, f, tmp3, prefix, NULL);
} else {
ret = xar_add_node(x, f, tmp3, prefix, NULL, 0);
}
if( !ret ) {
free(tmp1);
return NULL;
}
if( !tmp2 ) {
free(tmp1);
return ret;
}
ret2 = xar_add_r(x, ret, tmp2, "");
free(tmp1);
return ret2;
}
xar_file_t xar_add(xar_t x, const char *path) {
#ifdef __APPLE__
xar_file_t ret;
if( (ret = xar_underbar_check(x, NULL, path)) )
return ret;
#endif
if( path[0] == '/' ) {
XAR(x)->path_prefix = "/";
path++;
} else
XAR(x)->path_prefix = "";
return xar_add_r(x, NULL, path, "");
}
xar_file_t xar_add_frombuffer(xar_t x, xar_file_t parent, const char *name, char *buffer, size_t length) {
xar_file_t ret;
char idstr[32];
if( !parent ) {
ret = xar_file_new(NULL);
if( !ret )
return NULL;
memset(idstr, 0, sizeof(idstr));
snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
xar_attr_set(ret, NULL, "id", idstr);
XAR_FILE(ret)->parent = NULL;
if( XAR(x)->files == NULL )
XAR(x)->files = ret;
else {
XAR_FILE(ret)->next = XAR(x)->files;
XAR(x)->files = ret;
}
} else {
ret = xar_file_new(parent);
if( !ret )
return NULL;
memset(idstr, 0, sizeof(idstr));
snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
xar_attr_set(ret, NULL, "id", idstr);
XAR_FILE(ret)->fspath = NULL;
}
xar_prop_set(ret, "name", name);
if( xar_arcmod_archive(x, ret, NULL , buffer , length) < 0 ) {
xar_file_t i;
if( parent ) {
for( i = XAR_FILE(parent)->children; i && (XAR_FILE(i)->next != ret); i = XAR_FILE(i)->next );
} else {
for( i = XAR(x)->files; i && (XAR_FILE(i)->next != ret); i = XAR_FILE(i)->next );
}
if( i )
XAR_FILE(i)->next = XAR_FILE(ret)->next;
xar_file_free(ret);
return NULL;
}
return ret;
}
xar_file_t xar_add_folder(xar_t x, xar_file_t f, const char *name, struct stat *info)
{
xar_file_t ret;
char idstr[32];
if( info )
memcpy(&XAR(x)->sbcache,info,sizeof(struct stat));
ret = xar_file_new(f);
if( !ret )
return NULL;
memset(idstr, 0, sizeof(idstr));
snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
xar_attr_set(ret, NULL, "id", idstr);
XAR_FILE(ret)->fspath = NULL;
if( !f ) {
XAR_FILE(ret)->parent = NULL;
if( XAR(x)->files == NULL )
XAR(x)->files = ret;
else {
XAR_FILE(ret)->next = XAR(x)->files;
XAR(x)->files = ret;
}
}
xar_prop_set(ret, "name", name);
if( xar_arcmod_archive(x, ret, XAR_FILE(ret)->fspath, NULL, 0) < 0 ) {
xar_file_t i;
if( f ) {
for( i = XAR_FILE(f)->children; i && (XAR_FILE(i)->next != ret); i = XAR_FILE(i)->next );
} else {
for( i = XAR(x)->files; i && (XAR_FILE(i)->next != ret); i = XAR_FILE(i)->next );
}
if( i )
XAR_FILE(i)->next = XAR_FILE(ret)->next;
xar_file_free(ret);
return NULL;
}
return ret;
}
xar_file_t xar_add_frompath(xar_t x, xar_file_t parent, const char *name, const char *realpath)
{
return xar_add_node(x, parent, name , "" , realpath, 1);
}
xar_file_t xar_add_from_archive(xar_t x, xar_file_t parent, const char *name, xar_t sourcearchive, xar_file_t sourcefile)
{
xar_file_t ret;
char idstr[32];
ret = xar_file_replicate(sourcefile, parent);
if( !ret )
return NULL;
memset(idstr, 0, sizeof(idstr));
snprintf(idstr, sizeof(idstr)-1, "%"PRIu64, ++XAR(x)->last_fileid);
xar_attr_set(ret, NULL, "id", idstr);
XAR_FILE(ret)->fspath = NULL;
if( !parent ) {
XAR_FILE(ret)->parent = NULL;
if( XAR(x)->files == NULL )
XAR(x)->files = ret;
else {
XAR_FILE(ret)->next = XAR(x)->files;
XAR(x)->files = ret;
}
}
xar_prop_set(ret, "name", name);
xar_prop_t p = xar_prop_pfirst(ret);
do{
xar_prop_t tmpp;
tmpp = xar_prop_pget(p, "offset");
if(tmpp) {
if( 0 != xar_attrcopy_from_heap_to_heap(sourcearchive, sourcefile, p, x, ret)){
xar_file_free(ret);
ret = NULL;
break;
}
}
}while( (p = xar_prop_pnext(p)) );
return ret;
}
int32_t xar_extract_tofile(xar_t x, xar_file_t f, const char *path) {
return xar_arcmod_extract(x, f, path,NULL, 0);
}
int32_t xar_extract_tobuffer(xar_t x, xar_file_t f, char **buffer) {
size_t size;
return xar_extract_tobuffersz(x, f, buffer, &size);
}
int32_t xar_extract_tobuffersz(xar_t x, xar_file_t f, char **buffer, size_t *size) {
const char *sizestring = NULL;
int32_t ret;
if(0 != xar_prop_get(f,"data/size",&sizestring)){
if(0 != xar_prop_get(f, "type", &sizestring))
return -1;
if(strcmp(sizestring, "file") == 0) {
*size = 0;
return 0;
}
return -1;
}
*size = strtoull(sizestring, (char **)NULL, 10);
*buffer = malloc(*size);
if(!(*buffer)){
return -1;
}
ret = xar_arcmod_extract(x,f,NULL,*buffer,*size);
if( ret ) {
*size = 0;
free(*buffer);
*buffer = NULL;
}
return ret;
}
int32_t xar_extract_tostream_init(xar_t x, xar_file_t f, xar_stream *stream) {
xar_prop_t tmpp;
if( !xar_check_prop(x, "data") )
return XAR_STREAM_OK;
tmpp = xar_prop_pfirst(f);
if( tmpp )
tmpp = xar_prop_find(tmpp, "data");
if( !tmpp )
return XAR_STREAM_OK;
return xar_attrcopy_from_heap_to_stream_init(x, f, tmpp, stream);
}
int32_t xar_extract_tostream(xar_stream *stream) {
return xar_attrcopy_from_heap_to_stream(stream);
}
int32_t xar_extract_tostream_end(xar_stream *stream) {
return xar_attrcopy_from_heap_to_stream_end(stream);
}
int32_t xar_extract(xar_t x, xar_file_t f) {
struct stat sb;
char *tmp1, *dname;
xar_file_t tmpf;
if( (strstr(XAR_FILE(f)->fspath, "/") != NULL) && (stat(XAR_FILE(f)->fspath, &sb)) && (XAR_FILE(f)->parent_extracted == 0) ) {
tmp1 = strdup(XAR_FILE(f)->fspath);
dname = dirname(tmp1);
tmpf = xar_file_find(XAR(x)->files, dname);
if( !tmpf ) {
xar_err_set_string(x, "Unable to find file");
xar_err_callback(x, XAR_SEVERITY_NONFATAL, XAR_ERR_ARCHIVE_EXTRACTION);
return -1;
}
free(tmp1);
XAR_FILE(f)->parent_extracted++;
xar_extract(x, tmpf);
}
return xar_extract_tofile(x, f, XAR_FILE(f)->fspath);
}
int32_t xar_verify(xar_t x, xar_file_t f) {
return xar_arcmod_verify(x,f);
}
static int toc_read_callback(void *context, char *buffer, int len) {
xar_t x = (xar_t)context;
int ret, off = 0;
if ( ((!XAR(x)->offset) || (XAR(x)->offset == XAR(x)->readbuf_len)) && (XAR(x)->toc_count != XAR(x)->header.toc_length_compressed) ) {
XAR(x)->offset = 0;
if( (XAR(x)->readbuf_len - off) + XAR(x)->toc_count > XAR(x)->header.toc_length_compressed )
ret = xar_read_fd(XAR(x)->fd, XAR(x)->readbuf, XAR(x)->header.toc_length_compressed - XAR(x)->toc_count);
else
ret = read(XAR(x)->fd, XAR(x)->readbuf, XAR(x)->readbuf_len);
if ( ret == -1 )
return ret;
if ( XAR(x)->docksum )
#ifdef __APPLE__
CCDigestUpdate(XAR(x)->toc_ctx, XAR(x)->readbuf, ret);
#else
EVP_DigestUpdate(&XAR(x)->toc_ctx, XAR(x)->readbuf, ret);
#endif
XAR(x)->toc_count += ret;
off += ret;
}
if( off && (off < XAR(x)->readbuf_len) )
XAR(x)->readbuf_len = off;
XAR(x)->zs.next_in = ((unsigned char *)XAR(x)->readbuf) + XAR(x)->offset;
XAR(x)->zs.avail_in = XAR(x)->readbuf_len - XAR(x)->offset;
XAR(x)->zs.next_out = (void *)buffer;
XAR(x)->zs.avail_out = len;
ret = inflate(&XAR(x)->zs, Z_SYNC_FLUSH);
if( ret < 0 )
return -1;
XAR(x)->offset = XAR(x)->readbuf_len - XAR(x)->zs.avail_in;
return len - XAR(x)->zs.avail_out;
}
static int close_callback(void *context) {
return 0;
}
void xar_serialize(xar_t x, const char *file) {
xmlTextWriterPtr writer;
xar_subdoc_t i;
writer = xmlNewTextWriterFilename(file, 0);
xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
xmlTextWriterSetIndent(writer, 4);
xmlTextWriterStartElement(writer, BAD_CAST("xar"));
for( i = XAR(x)->subdocs; i; i = xar_subdoc_next(i) )
xar_subdoc_serialize(i, writer, 1);
xmlTextWriterStartElement(writer, BAD_CAST("toc"));
if( XAR(x)->props )
xar_prop_serialize(XAR(x)->props, writer);
if( XAR(x)->signatures )
xar_signature_serialize(XAR(x)->signatures,writer);
if( XAR(x)->files )
xar_file_serialize(XAR(x)->files, writer);
xmlTextWriterEndDocument(writer);
xmlFreeTextWriter(writer);
return;
}
static int32_t xar_unserialize(xar_t x) {
xmlTextReaderPtr reader;
xar_file_t f = NULL;
const xmlChar *name, *prefix, *uri;
int type, noattr, ret;
reader = xmlReaderForIO(toc_read_callback, close_callback, XAR(x), NULL, NULL, 0);
if( !reader ) return -1;
while( (ret = xmlTextReaderRead(reader)) == 1 ) {
type = xmlTextReaderNodeType(reader);
noattr = xmlTextReaderAttributeCount(reader);
name = xmlTextReaderConstLocalName(reader);
if( type != XML_READER_TYPE_ELEMENT )
continue;
if(strcmp((const char*)name, "xar") != 0)
continue;
while( (ret = xmlTextReaderRead(reader)) == 1 ) {
type = xmlTextReaderNodeType(reader);
noattr = xmlTextReaderAttributeCount(reader);
name = xmlTextReaderConstLocalName(reader);
if( type == XML_READER_TYPE_ELEMENT ) {
if(strcmp((const char*)name, "toc") == 0) {
while( (ret = xmlTextReaderRead(reader)) == 1 ) {
type = xmlTextReaderNodeType(reader);
noattr = xmlTextReaderAttributeCount(reader);
name = xmlTextReaderConstLocalName(reader);
if( type == XML_READER_TYPE_ELEMENT ) {
if(strcmp((const char*)name, "file") == 0) {
f = xar_file_unserialize(x, NULL, reader);
XAR_FILE(f)->next = XAR(x)->files;
XAR(x)->files = f;
} else if( strcmp((const char*)name, "signature") == 0
#ifdef __APPLE__
|| strcmp((const char*)name, "x-signature") == 0
#endif
){
xar_signature_t sig = NULL;
sig = xar_signature_unserialize(x, reader );
if( !sig ) {
xmlFreeTextReader(reader);
xmlDictCleanup();
xmlCleanupCharEncodingHandlers();
return -1;
}
if( XAR(x)->signatures )
XAR_SIGNATURE(XAR(x)->signatures)->next = XAR_SIGNATURE(sig);
else
XAR(x)->signatures = sig;
} else {
xar_prop_unserialize(XAR_FILE(x), NULL, reader);
}
}
}
if( ret == -1 ) {
xmlFreeTextReader(reader);
xmlDictCleanup();
xmlCleanupCharEncodingHandlers();
return -1;
}
} else {
xar_subdoc_t s;
int i;
prefix = xmlTextReaderPrefix(reader);
uri = xmlTextReaderNamespaceUri(reader);
i = xmlTextReaderAttributeCount(reader);
if( i > 0 ) {
for(i = xmlTextReaderMoveToFirstAttribute(reader); i == 1; i = xmlTextReaderMoveToNextAttribute(reader)) {
xar_attr_t a;
const char *aname = (const char *)xmlTextReaderConstLocalName(reader);
const char *avalue = (const char *)xmlTextReaderConstValue(reader);
if( aname && (strcmp("subdoc_name", aname) == 0) ) {
name = (const unsigned char *)avalue;
} else {
a = xar_attr_new();
XAR_ATTR(a)->key = strdup(aname);
XAR_ATTR(a)->value = strdup(avalue);
XAR_ATTR(a)->next = XAR_SUBDOC(s)->attrs;
XAR_SUBDOC(s)->attrs = XAR_ATTR(a);
}
}
}
s = xar_subdoc_new(x, (const char *)name);
xar_subdoc_unserialize(s, reader);
}
}
if( (type == XML_READER_TYPE_END_ELEMENT) && (strcmp((const char *)name, "toc")==0) ) {
break;
}
}
if( ret == -1 ) {
xmlFreeTextReader(reader);
xmlDictCleanup();
xmlCleanupCharEncodingHandlers();
return -1;
}
}
if( ret == -1 ) {
xmlFreeTextReader(reader);
xmlDictCleanup();
xmlCleanupCharEncodingHandlers();
return -1;
}
xmlFreeTextReader(reader);
xmlDictCleanup();
xmlCleanupCharEncodingHandlers();
return 0;
}