2014-10-24 15:42:48 +00:00
|
|
|
/*
|
|
|
|
* This file is a part of Pcompress, a chunked parallel multi-
|
|
|
|
* algorithm lossless compression and decompression program.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012-2014 Moinak Ghosh. All rights reserved.
|
|
|
|
* Use is subject to license terms.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 3 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this program.
|
|
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* moinakg@belenix.org, http://moinakg.wordpress.com/
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <strings.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <unistd.h>
|
2014-10-24 18:00:40 +00:00
|
|
|
#include "pcompress.h"
|
|
|
|
#include "delta2/delta2.h"
|
2014-10-24 15:42:48 +00:00
|
|
|
#include "utils/utils.h"
|
2014-10-24 18:00:40 +00:00
|
|
|
#include "lzma/lzma_crc.h"
|
2014-10-24 15:42:48 +00:00
|
|
|
#include "allocator.h"
|
|
|
|
#include "meta_stream.h"
|
|
|
|
|
2014-10-24 18:00:40 +00:00
|
|
|
#define METADATA_CHUNK_SIZE (2 * 1024 * 1024)
|
|
|
|
|
|
|
|
extern int bzip2_compress(void *src, uint64_t srclen, void *dst, uint64_t *dstlen,
|
2014-10-24 15:42:48 +00:00
|
|
|
int level, uchar_t chdr, int btype, void *data);
|
2014-10-24 18:00:40 +00:00
|
|
|
extern int bzip2_decompress(void *src, uint64_t srclen, void *dst, uint64_t *dstlen,
|
|
|
|
int level, uchar_t chdr, int btype, void *data);
|
|
|
|
extern void bzip2_props(algo_props_t *data, int level, uint64_t chunksize);
|
2014-10-24 15:42:48 +00:00
|
|
|
|
|
|
|
enum {
|
|
|
|
SRC_CHANNEL = 0,
|
|
|
|
SINK_CHANNEL
|
|
|
|
};
|
|
|
|
|
2014-10-24 18:00:40 +00:00
|
|
|
struct _meta_ctx {
|
|
|
|
int meta_pipes[2];
|
|
|
|
pc_ctx_t *pctx;
|
|
|
|
pthread_t meta_thread;
|
|
|
|
uchar_t *frombuf, *tobuf;
|
|
|
|
uint64_t frompos, topos, tosize;
|
|
|
|
uchar_t checksum[CKSUM_MAX_BYTES];
|
|
|
|
void *bzip2_dat;
|
|
|
|
int comp_level, id;
|
|
|
|
int comp_fd;
|
|
|
|
int running;
|
|
|
|
int delta2_nstrides;
|
|
|
|
int do_compress;
|
|
|
|
mac_ctx_t chunk_hmac;
|
|
|
|
algo_props_t props;
|
|
|
|
};
|
|
|
|
|
2014-10-24 15:42:48 +00:00
|
|
|
static int
|
|
|
|
compress_and_write(meta_ctx_t *mctx)
|
|
|
|
{
|
2014-10-24 18:00:40 +00:00
|
|
|
pc_ctx_t *pctx = mctx->pctx;
|
2014-10-24 15:42:48 +00:00
|
|
|
uchar_t type;
|
|
|
|
uchar_t *comp_chunk, *tobuf;
|
|
|
|
int rv;
|
|
|
|
uint64_t dstlen;
|
|
|
|
int64_t wbytes;
|
|
|
|
|
2014-11-03 17:52:22 +00:00
|
|
|
/*
|
|
|
|
* Increment metadata chunk id. Useful when encrypting (CTR Mode).
|
|
|
|
*/
|
|
|
|
mctx->id++;
|
|
|
|
|
2014-10-24 15:42:48 +00:00
|
|
|
/*
|
|
|
|
* Plain checksum if not encrypting.
|
|
|
|
* This place will hold HMAC if encrypting.
|
|
|
|
*/
|
|
|
|
if (!pctx->encrypt_type) {
|
|
|
|
compute_checksum(mctx->checksum, pctx->cksum, mctx->frombuf,
|
|
|
|
mctx->frompos, 0, 1);
|
|
|
|
}
|
|
|
|
|
2014-10-24 18:00:40 +00:00
|
|
|
type = 0;
|
|
|
|
/*
|
|
|
|
* This is normally the compressed chunk size for data chunks. Here we
|
|
|
|
* set it to 1 to indicate that this is a metadata chunk. This value is
|
|
|
|
* always big-endian format. The next value is the real compressed
|
|
|
|
* chunk size.
|
|
|
|
*/
|
|
|
|
tobuf = mctx->tobuf;
|
|
|
|
U64_P(tobuf) = htonll(METADATA_INDICATOR);
|
|
|
|
comp_chunk = tobuf + METADATA_HDR_SZ;
|
|
|
|
dstlen = mctx->frompos;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Apply Delta2 filter.
|
|
|
|
*/
|
|
|
|
rv = delta2_encode(mctx->frombuf, mctx->frompos, comp_chunk, &dstlen,
|
|
|
|
mctx->props.delta2_span, mctx->delta2_nstrides);
|
|
|
|
if (rv != -1) {
|
|
|
|
memcpy(mctx->frombuf, comp_chunk, dstlen);
|
|
|
|
mctx->frompos = dstlen;
|
|
|
|
type |= PREPROC_TYPE_DELTA2;
|
|
|
|
} else {
|
|
|
|
dstlen = mctx->frompos;
|
|
|
|
}
|
2014-10-24 15:42:48 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ok, now compress.
|
|
|
|
*/
|
2014-10-24 18:00:40 +00:00
|
|
|
rv = bzip2_compress(mctx->frombuf, mctx->frompos, comp_chunk, &dstlen, mctx->comp_level,
|
|
|
|
0, TYPE_BINARY, mctx->bzip2_dat);
|
2014-10-24 15:42:48 +00:00
|
|
|
|
|
|
|
if (rv < 0) {
|
2014-10-24 18:00:40 +00:00
|
|
|
dstlen = mctx->frompos;
|
|
|
|
memcpy(comp_chunk, mctx->frombuf, dstlen);
|
|
|
|
} else {
|
|
|
|
type |= PREPROC_COMPRESSED;
|
2014-10-24 15:42:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pctx->encrypt_type) {
|
2014-11-03 17:52:22 +00:00
|
|
|
rv = crypto_buf(&(pctx->crypto_ctx), comp_chunk, comp_chunk, dstlen, mctx->id);
|
2014-10-24 15:42:48 +00:00
|
|
|
if (rv == -1) {
|
|
|
|
pctx->main_cancel = 1;
|
|
|
|
pctx->t_errored = 1;
|
2014-10-24 18:00:40 +00:00
|
|
|
log_msg(LOG_ERR, 0, "Metadata Encrypion failed");
|
2014-10-24 15:42:48 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-10-24 18:00:40 +00:00
|
|
|
* Store the compressed length of the data segment. While reading we have to account
|
|
|
|
* for the header.
|
2014-10-24 15:42:48 +00:00
|
|
|
*/
|
2014-10-24 18:00:40 +00:00
|
|
|
U64_P(tobuf + 8) = LE64(dstlen);
|
|
|
|
U64_P(tobuf + 16) = LE64(mctx->frompos);
|
|
|
|
*(tobuf + 24) = type;
|
|
|
|
|
2014-10-24 15:42:48 +00:00
|
|
|
if (!pctx->encrypt_type)
|
2014-10-24 18:00:40 +00:00
|
|
|
serialize_checksum(mctx->checksum, tobuf + 25, pctx->cksum_bytes);
|
2014-10-24 15:42:48 +00:00
|
|
|
|
|
|
|
if (pctx->encrypt_type) {
|
|
|
|
uchar_t chash[pctx->mac_bytes];
|
|
|
|
unsigned int hlen;
|
2014-10-24 18:00:40 +00:00
|
|
|
uchar_t *mac_ptr;
|
2014-10-24 15:42:48 +00:00
|
|
|
|
2014-10-24 18:00:40 +00:00
|
|
|
mac_ptr = tobuf + 25;
|
2014-11-03 17:52:22 +00:00
|
|
|
memset(mac_ptr, 0, pctx->mac_bytes + CRC32_SIZE);
|
2014-10-24 15:42:48 +00:00
|
|
|
hmac_reinit(&mctx->chunk_hmac);
|
2014-10-24 18:00:40 +00:00
|
|
|
hmac_update(&mctx->chunk_hmac, tobuf, dstlen + METADATA_HDR_SZ);
|
|
|
|
hmac_final(&mctx->chunk_hmac, chash, &hlen);
|
2014-10-24 15:42:48 +00:00
|
|
|
serialize_checksum(chash, mac_ptr, hlen);
|
|
|
|
} else {
|
|
|
|
uint32_t crc;
|
2014-10-24 18:00:40 +00:00
|
|
|
uchar_t *mac_ptr;
|
2014-10-24 15:42:48 +00:00
|
|
|
|
2014-10-24 18:00:40 +00:00
|
|
|
mac_ptr = tobuf + 25 + CKSUM_MAX;
|
|
|
|
memset(mac_ptr, 0, CRC32_SIZE);
|
|
|
|
crc = lzma_crc32(tobuf, METADATA_HDR_SZ, 0);
|
|
|
|
U32_P(tobuf + 25 + CKSUM_MAX) = LE32(crc);
|
2014-10-24 15:42:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* All done. Now grab lock and write.
|
|
|
|
*/
|
2014-10-24 18:00:40 +00:00
|
|
|
dstlen += METADATA_HDR_SZ; // The 'full' chunk now
|
2014-10-24 15:42:48 +00:00
|
|
|
pthread_mutex_lock(&pctx->write_mutex);
|
|
|
|
wbytes = Write(mctx->comp_fd, mctx->tobuf, dstlen);
|
|
|
|
pthread_mutex_unlock(&pctx->write_mutex);
|
|
|
|
if (wbytes != dstlen) {
|
|
|
|
log_msg(LOG_ERR, 1, "Metadata Write (expected: %" PRIu64 ", written: %" PRId64 ") : ",
|
|
|
|
dstlen, wbytes);
|
|
|
|
pctx->main_cancel = 1;
|
|
|
|
pctx->t_errored = 1;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
meta_ctx_close_sink_channel(meta_ctx_t *mctx)
|
|
|
|
{
|
2014-10-24 18:00:40 +00:00
|
|
|
if (mctx->meta_pipes[SINK_CHANNEL]) {
|
|
|
|
close(mctx->meta_pipes[SINK_CHANNEL]);
|
|
|
|
mctx->meta_pipes[SINK_CHANNEL] = 0;
|
2014-10-24 15:42:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
meta_ctx_close_src_channel(meta_ctx_t *mctx)
|
|
|
|
{
|
2014-10-24 18:00:40 +00:00
|
|
|
if (mctx->meta_pipes[SRC_CHANNEL]) {
|
|
|
|
close(mctx->meta_pipes[SRC_CHANNEL]);
|
|
|
|
mctx->meta_pipes[SRC_CHANNEL] = 0;
|
2014-10-24 15:42:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-24 18:00:40 +00:00
|
|
|
/*
|
|
|
|
* Accumulate metadata into a memory buffer. Once the buffer gets filled or
|
|
|
|
* data stream ends, the buffer is compressed and written out.
|
|
|
|
*/
|
2014-10-24 15:42:48 +00:00
|
|
|
static void *
|
|
|
|
metadata_compress(void *dat)
|
|
|
|
{
|
|
|
|
meta_ctx_t *mctx = (meta_ctx_t *)dat;
|
2014-10-24 18:00:40 +00:00
|
|
|
meta_msg_t *msgp;
|
2014-10-24 15:42:48 +00:00
|
|
|
int ack;
|
|
|
|
|
2014-10-24 18:00:40 +00:00
|
|
|
mctx->running = 1;
|
2014-11-03 17:52:22 +00:00
|
|
|
mctx->id = -1;
|
2014-10-24 18:00:40 +00:00
|
|
|
while (Read(mctx->meta_pipes[SINK_CHANNEL], &msgp, sizeof (msgp)) == sizeof (msgp)) {
|
2014-10-24 15:42:48 +00:00
|
|
|
ack = 0;
|
2014-10-24 18:00:40 +00:00
|
|
|
if (mctx->frompos + msgp->len > METADATA_CHUNK_SIZE) {
|
|
|
|
/*
|
|
|
|
* Accumulating the metadata block will overflow buffer. Compress
|
|
|
|
* and write the current buffer and then copy the new data into it.
|
|
|
|
*/
|
2014-10-24 15:42:48 +00:00
|
|
|
if (!compress_and_write(mctx)) {
|
|
|
|
Write(mctx->meta_pipes[SINK_CHANNEL], &ack, sizeof (ack));
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
mctx->frompos = 0;
|
2014-10-24 18:00:40 +00:00
|
|
|
memcpy(mctx->frombuf, msgp->buf, msgp->len);
|
|
|
|
mctx->frompos = msgp->len;
|
|
|
|
|
|
|
|
} else if (mctx->frompos + msgp->len == METADATA_CHUNK_SIZE) {
|
|
|
|
/*
|
|
|
|
* Accumulating the metadata block fills the buffer. Fill it then
|
|
|
|
* compress and write the buffer.
|
|
|
|
*/
|
|
|
|
memcpy(mctx->frombuf + mctx->frompos, msgp->buf, msgp->len);
|
2014-10-24 15:42:48 +00:00
|
|
|
if (!compress_and_write(mctx)) {
|
|
|
|
Write(mctx->meta_pipes[SINK_CHANNEL], &ack, sizeof (ack));
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
mctx->frompos = 0;
|
|
|
|
} else {
|
2014-10-24 18:00:40 +00:00
|
|
|
/*
|
|
|
|
* Accumulate the metadata block into the buffer for future
|
|
|
|
* compression.
|
|
|
|
*/
|
|
|
|
memcpy(mctx->frombuf + mctx->frompos, msgp->buf, msgp->len);
|
|
|
|
mctx->frompos += msgp->len;
|
2014-10-24 15:42:48 +00:00
|
|
|
}
|
|
|
|
ack = 1;
|
2014-10-24 18:00:40 +00:00
|
|
|
Write(mctx->meta_pipes[SINK_CHANNEL], &ack, sizeof (ack));
|
2014-10-24 15:42:48 +00:00
|
|
|
}
|
2014-10-24 18:00:40 +00:00
|
|
|
mctx->running = 0;
|
2014-10-24 15:42:48 +00:00
|
|
|
|
|
|
|
/*
|
2014-10-24 18:00:40 +00:00
|
|
|
* Flush any accumulated data in the buffer.
|
2014-10-24 15:42:48 +00:00
|
|
|
*/
|
|
|
|
if (mctx->frompos) {
|
|
|
|
if (!compress_and_write(mctx)) {
|
|
|
|
ack = 0;
|
|
|
|
Write(mctx->meta_pipes[SINK_CHANNEL], &ack, sizeof (ack));
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
mctx->frompos = 0;
|
|
|
|
}
|
2014-10-24 18:00:40 +00:00
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
decompress_data(meta_ctx_t *mctx)
|
|
|
|
{
|
|
|
|
uint64_t origlen, len_cmp, dstlen;
|
|
|
|
uchar_t *cbuf, *cseg, *ubuf, type;
|
|
|
|
pc_ctx_t *pctx = mctx->pctx;
|
|
|
|
uchar_t checksum[CKSUM_MAX_BYTES];
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
cbuf = mctx->frombuf;
|
|
|
|
ubuf = mctx->tobuf;
|
|
|
|
len_cmp = LE64(U64_P(cbuf + 8));
|
|
|
|
origlen = LE64(U64_P(cbuf + 16));
|
|
|
|
type = *(cbuf + 24);
|
|
|
|
cseg = cbuf + METADATA_HDR_SZ;
|
|
|
|
dstlen = origlen;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this was encrypted:
|
|
|
|
* Verify HMAC first before anything else and then decrypt compressed data.
|
|
|
|
*/
|
|
|
|
if (pctx->encrypt_type) {
|
|
|
|
unsigned int len;
|
|
|
|
|
|
|
|
len = pctx->mac_bytes;
|
|
|
|
deserialize_checksum(checksum, cbuf + 25, pctx->mac_bytes);
|
|
|
|
memset(cbuf + 25, 0, pctx->mac_bytes + CRC32_SIZE);
|
|
|
|
hmac_reinit(&mctx->chunk_hmac);
|
2014-11-03 17:52:22 +00:00
|
|
|
hmac_update(&mctx->chunk_hmac, cbuf, len_cmp + METADATA_HDR_SZ);
|
2014-10-24 18:00:40 +00:00
|
|
|
hmac_final(&mctx->chunk_hmac, mctx->checksum, &len);
|
|
|
|
if (memcmp(checksum, mctx->checksum, len) != 0) {
|
|
|
|
log_msg(LOG_ERR, 0, "Metadata chunk %d, HMAC verification failed",
|
|
|
|
mctx->id);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
rv = crypto_buf(&(pctx->crypto_ctx), cseg, cseg, len_cmp, mctx->id);
|
|
|
|
if (rv == -1) {
|
|
|
|
/*
|
|
|
|
* Decryption failure is fatal.
|
|
|
|
*/
|
|
|
|
log_msg(LOG_ERR, 0, "Metadata chunk %d, Decryption failed",
|
|
|
|
mctx->id);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
uint32_t crc1, crc2;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Verify Header CRC32 in non-crypto mode.
|
|
|
|
*/
|
2014-11-03 18:02:12 +00:00
|
|
|
deserialize_checksum(checksum, cbuf + 25, pctx->cksum_bytes);
|
2014-10-24 18:00:40 +00:00
|
|
|
crc1 = U32_P(cbuf + 25 + CKSUM_MAX);
|
|
|
|
memset(cbuf + 25 + CKSUM_MAX, 0, CRC32_SIZE);
|
|
|
|
crc2 = lzma_crc32(cbuf, METADATA_HDR_SZ, 0);
|
|
|
|
|
|
|
|
if (crc1 != crc2) {
|
|
|
|
/*
|
|
|
|
* Header CRC32 verification failure is fatal.
|
|
|
|
*/
|
|
|
|
log_msg(LOG_ERR, 0, "Metadata chunk %d, Header CRC verification failed",
|
|
|
|
mctx->id);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type & PREPROC_COMPRESSED) {
|
|
|
|
rv = bzip2_decompress(cseg, len_cmp, ubuf, &dstlen, mctx->comp_level,
|
|
|
|
0, TYPE_BINARY, mctx->bzip2_dat);
|
|
|
|
if (rv == -1) {
|
|
|
|
log_msg(LOG_ERR, 0, "Metadata chunk %d, decompression failed.", mctx->id);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
memcpy(ubuf, cseg, len_cmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type & PREPROC_TYPE_DELTA2) {
|
|
|
|
dstlen = origlen;
|
|
|
|
rv = delta2_decode(ubuf, len_cmp, cseg, &dstlen);
|
|
|
|
if (rv == -1) {
|
|
|
|
log_msg(LOG_ERR, 0, "Metadata chunk %d, Delta2 decoding failed.", mctx->id);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
memcpy(ubuf, cseg, dstlen);
|
|
|
|
}
|
2014-11-03 18:02:12 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now verify normal checksum if not using encryption.
|
|
|
|
*/
|
|
|
|
if (!pctx->encrypt_type) {
|
|
|
|
compute_checksum(mctx->checksum, pctx->cksum, ubuf, dstlen, 0, 1);
|
|
|
|
if (memcmp(checksum, mctx->checksum, pctx->cksum_bytes) != 0) {
|
|
|
|
log_msg(LOG_ERR, 0, "Metadata chunk %d, Checksum verification failed",
|
|
|
|
mctx->id);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
}
|
2014-10-24 18:00:40 +00:00
|
|
|
mctx->topos = 0;
|
|
|
|
mctx->tosize = dstlen;
|
|
|
|
return (1);
|
2014-10-24 15:42:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void *
|
|
|
|
metadata_decompress(void *dat)
|
|
|
|
{
|
2014-10-24 18:00:40 +00:00
|
|
|
meta_ctx_t *mctx = (meta_ctx_t *)dat;
|
|
|
|
pc_ctx_t *pctx;
|
|
|
|
meta_msg_t *msgp;
|
|
|
|
int ack;
|
|
|
|
|
|
|
|
pctx = mctx->pctx;
|
|
|
|
mctx->running = 1;
|
|
|
|
mctx->topos = mctx->tosize = 0;
|
|
|
|
mctx->id = -1;
|
|
|
|
while (Read(mctx->meta_pipes[SINK_CHANNEL], &msgp, sizeof (msgp)) == sizeof (msgp)) {
|
|
|
|
int64_t rb;
|
|
|
|
uint64_t len_cmp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan to the next metadata chunk and decompress it, if our in-memory data
|
|
|
|
* is fully consumed or not filled.
|
|
|
|
*/
|
|
|
|
if (mctx->topos == mctx->tosize) {
|
|
|
|
uchar_t *frombuf = mctx->frombuf;
|
|
|
|
|
|
|
|
mctx->id++;
|
|
|
|
while ((rb = Read(mctx->comp_fd, &len_cmp, sizeof (len_cmp))
|
|
|
|
== sizeof(len_cmp))) {
|
|
|
|
len_cmp = ntohll(len_cmp);
|
|
|
|
if (len_cmp != METADATA_INDICATOR) {
|
|
|
|
uint64_t skiplen;
|
|
|
|
|
|
|
|
if (len_cmp == 0) {
|
|
|
|
/*
|
|
|
|
* We have reached the end of the file.
|
|
|
|
*/
|
|
|
|
msgp->len = 0;
|
|
|
|
ack = 1;
|
|
|
|
Write(mctx->meta_pipes[SINK_CHANNEL], &ack, sizeof (ack));
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
skiplen = len_cmp + pctx->cksum_bytes + pctx->mac_bytes
|
|
|
|
+ CHUNK_FLAG_SZ;
|
|
|
|
int64_t cpos = lseek(mctx->comp_fd, skiplen, SEEK_CUR);
|
|
|
|
if (cpos == -1) {
|
|
|
|
log_msg(LOG_ERR, 1, "Cannot find/seek next metadata block.");
|
|
|
|
ack = 0;
|
|
|
|
Write(mctx->meta_pipes[SINK_CHANNEL], &ack, sizeof (ack));
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rb == -1) {
|
|
|
|
log_msg(LOG_ERR, 1, "Failed read from metadata fd: ");
|
|
|
|
ack = 0;
|
|
|
|
Write(mctx->meta_pipes[SINK_CHANNEL], &ack, sizeof (ack));
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
} else if (rb == 0) {
|
|
|
|
/*
|
|
|
|
* We have reached the end of the file.
|
|
|
|
*/
|
|
|
|
msgp->len = 0;
|
|
|
|
ack = 1;
|
|
|
|
Write(mctx->meta_pipes[SINK_CHANNEL], &ack, sizeof (ack));
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
U64_P(frombuf) = htonll(len_cmp);
|
|
|
|
frombuf += 8;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We are at the start of a metadata chunk. Read the size.
|
|
|
|
*/
|
|
|
|
if ((rb = Read(mctx->comp_fd, &len_cmp, sizeof (len_cmp)))
|
|
|
|
!= sizeof(len_cmp)) {
|
|
|
|
log_msg(LOG_ERR, 1, "Failed to read size from metadata fd: %lld", rb);
|
|
|
|
ack = 0;
|
|
|
|
Write(mctx->meta_pipes[SINK_CHANNEL], &ack, sizeof (ack));
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
U64_P(frombuf) = len_cmp;
|
|
|
|
frombuf += 8;
|
|
|
|
len_cmp = LE64(len_cmp);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now read the rest of the chunk. This is rest of the header plus the
|
|
|
|
* data segment.
|
|
|
|
*/
|
|
|
|
len_cmp = len_cmp + (METADATA_HDR_SZ - (frombuf - mctx->frombuf));
|
|
|
|
rb = Read(mctx->comp_fd, frombuf, len_cmp);
|
|
|
|
if (rb != len_cmp) {
|
|
|
|
log_msg(LOG_ERR, 1, "Failed to read chunk from metadata fd: ");
|
|
|
|
ack = 0;
|
|
|
|
Write(mctx->meta_pipes[SINK_CHANNEL], &ack, sizeof (ack));
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
mctx->topos = 0;
|
|
|
|
mctx->frompos = len_cmp + METADATA_HDR_SZ;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now decompress.
|
|
|
|
*/
|
|
|
|
if (!decompress_data(mctx)) {
|
|
|
|
ack = 0;
|
|
|
|
Write(mctx->meta_pipes[SINK_CHANNEL], &ack, sizeof (ack));
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
msgp->buf = mctx->tobuf;
|
|
|
|
msgp->len = mctx->tosize;
|
|
|
|
mctx->topos = mctx->tosize;
|
|
|
|
ack = 1;
|
|
|
|
Write(mctx->meta_pipes[SINK_CHANNEL], &ack, sizeof (ack));
|
|
|
|
}
|
|
|
|
|
|
|
|
return (NULL);
|
2014-10-24 15:42:48 +00:00
|
|
|
}
|
|
|
|
|
2014-10-24 18:00:40 +00:00
|
|
|
/*
|
|
|
|
* Create the metadata thread and associated buffers. This writes out compressed
|
|
|
|
* metadata chunks into the archive. This is libarchive metadata.
|
|
|
|
*/
|
2014-10-24 15:42:48 +00:00
|
|
|
meta_ctx_t *
|
2014-10-24 18:00:40 +00:00
|
|
|
meta_ctx_create(void *pc, int file_version, int comp_fd)
|
2014-10-24 15:42:48 +00:00
|
|
|
{
|
2014-10-24 18:00:40 +00:00
|
|
|
pc_ctx_t *pctx = (pc_ctx_t *)pc;
|
2014-10-24 15:42:48 +00:00
|
|
|
meta_ctx_t *mctx;
|
|
|
|
|
|
|
|
mctx = (meta_ctx_t *)malloc(sizeof (meta_ctx_t));
|
|
|
|
if (!mctx) {
|
|
|
|
log_msg(LOG_ERR, 1, "Failed to allocate metadata context.");
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
2014-10-24 18:00:40 +00:00
|
|
|
mctx->running = 0;
|
2014-10-24 15:42:48 +00:00
|
|
|
if (pctx->encrypt_type) {
|
2014-10-24 18:00:40 +00:00
|
|
|
if (hmac_init(&mctx->chunk_hmac, pctx->cksum,
|
|
|
|
&(pctx->crypto_ctx)) == -1) {
|
2014-10-24 15:42:48 +00:00
|
|
|
(void) free(mctx);
|
|
|
|
log_msg(LOG_ERR, 0, "Cannot initialize metadata hmac.");
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mctx->comp_fd = comp_fd;
|
|
|
|
mctx->frompos = 0;
|
|
|
|
mctx->frombuf = slab_alloc(NULL, METADATA_CHUNK_SIZE + METADATA_HDR_SZ);
|
|
|
|
if (!mctx->frombuf) {
|
|
|
|
(void) free(mctx);
|
|
|
|
log_msg(LOG_ERR, 1, "Failed to allocate metadata buffer.");
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
mctx->tobuf = slab_alloc(NULL, METADATA_CHUNK_SIZE + METADATA_HDR_SZ);
|
|
|
|
if (!mctx->tobuf) {
|
|
|
|
(void) free(mctx->frombuf);
|
|
|
|
(void) free(mctx);
|
|
|
|
log_msg(LOG_ERR, 1, "Failed to allocate metadata buffer.");
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
2014-10-24 18:00:40 +00:00
|
|
|
/*
|
|
|
|
* The archiver passes metadata via this socketpair. Memory buffer pointers
|
|
|
|
* are passed through the socket for speed rather than the contents.
|
|
|
|
*/
|
2014-10-24 15:42:48 +00:00
|
|
|
mctx->pctx = pctx;
|
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, mctx->meta_pipes) == -1) {
|
|
|
|
(void) free(mctx->frombuf);
|
|
|
|
(void) free(mctx->tobuf);
|
|
|
|
(void) free(mctx);
|
|
|
|
log_msg(LOG_ERR, 1, "Unable to create a metadata ctx channel.");
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
2014-10-24 18:00:40 +00:00
|
|
|
if (pctx->level > 9)
|
|
|
|
mctx->delta2_nstrides = NSTRIDES_EXTRA;
|
|
|
|
else
|
|
|
|
mctx->delta2_nstrides = NSTRIDES_STANDARD;
|
2014-10-24 15:42:48 +00:00
|
|
|
if (pctx->do_compress) {
|
2014-10-24 18:00:40 +00:00
|
|
|
int rv;
|
2014-10-24 15:42:48 +00:00
|
|
|
|
|
|
|
mctx->comp_level = pctx->level;
|
2014-10-24 18:00:40 +00:00
|
|
|
rv = bzip2_init(&mctx->bzip2_dat, &mctx->comp_level, 1, METADATA_CHUNK_SIZE,
|
2014-10-24 15:42:48 +00:00
|
|
|
file_version, COMPRESS);
|
2014-10-24 18:00:40 +00:00
|
|
|
bzip2_props(&mctx->props, pctx->level, METADATA_CHUNK_SIZE);
|
2014-10-24 15:42:48 +00:00
|
|
|
if (rv != 0 || pthread_create(&(mctx->meta_thread), NULL, metadata_compress,
|
|
|
|
(void *)mctx) != 0) {
|
2014-10-24 18:00:40 +00:00
|
|
|
(void) close(mctx->meta_pipes[0]);
|
|
|
|
(void) close(mctx->meta_pipes[1]);
|
2014-10-24 15:42:48 +00:00
|
|
|
(void) free(mctx->frombuf);
|
|
|
|
(void) free(mctx->tobuf);
|
|
|
|
(void) free(mctx);
|
|
|
|
if (rv == 0)
|
2014-10-24 18:00:40 +00:00
|
|
|
log_msg(LOG_ERR, 0, "Lzma init failed.");
|
2014-10-24 15:42:48 +00:00
|
|
|
else
|
|
|
|
log_msg(LOG_ERR, 1, "Unable to create metadata thread.");
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
} else {
|
2014-10-24 18:00:40 +00:00
|
|
|
int rv;
|
2014-10-24 15:42:48 +00:00
|
|
|
|
|
|
|
mctx->comp_level = pctx->level;
|
2014-10-24 18:00:40 +00:00
|
|
|
rv = bzip2_init(&mctx->bzip2_dat, &mctx->comp_level, 1, METADATA_CHUNK_SIZE,
|
2014-10-24 15:42:48 +00:00
|
|
|
file_version, DECOMPRESS);
|
|
|
|
if (rv != 0 || pthread_create(&(mctx->meta_thread), NULL, metadata_decompress,
|
|
|
|
(void *)mctx) != 0) {
|
2014-10-24 18:00:40 +00:00
|
|
|
(void) close(mctx->meta_pipes[0]);
|
|
|
|
(void) close(mctx->meta_pipes[1]);
|
2014-10-24 15:42:48 +00:00
|
|
|
(void) free(mctx->frombuf);
|
|
|
|
(void) free(mctx->tobuf);
|
|
|
|
(void) free(mctx);
|
|
|
|
if (rv == 0)
|
2014-10-24 18:00:40 +00:00
|
|
|
log_msg(LOG_ERR, 0, "Lzma init failed.");
|
2014-10-24 15:42:48 +00:00
|
|
|
else
|
|
|
|
log_msg(LOG_ERR, 1, "Unable to create metadata thread.");
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-24 18:00:40 +00:00
|
|
|
mctx->do_compress = pctx->do_compress;
|
2014-10-24 15:42:48 +00:00
|
|
|
return (mctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2014-10-24 18:00:40 +00:00
|
|
|
meta_ctx_send(meta_ctx_t *mctx, const void **buf, size_t *len)
|
2014-10-24 15:42:48 +00:00
|
|
|
{
|
|
|
|
int ack;
|
2014-10-24 18:00:40 +00:00
|
|
|
meta_msg_t msg, *msgp;
|
2014-10-24 15:42:48 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the message buffer to the pipe.
|
|
|
|
*/
|
2014-10-24 18:00:40 +00:00
|
|
|
msg.buf = *buf;
|
|
|
|
msg.len = *len;
|
|
|
|
msgp = &msg;
|
|
|
|
if (Write(mctx->meta_pipes[SRC_CHANNEL], &msgp, sizeof (msgp)) <
|
|
|
|
sizeof (msgp)) {
|
2014-10-24 15:42:48 +00:00
|
|
|
log_msg(LOG_ERR, 1, "Meta socket write error.");
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wait for ACK.
|
|
|
|
*/
|
2014-10-24 18:00:40 +00:00
|
|
|
if (Read(mctx->meta_pipes[SRC_CHANNEL], &ack, sizeof (ack)) <
|
|
|
|
sizeof (ack)) {
|
2014-10-24 15:42:48 +00:00
|
|
|
log_msg(LOG_ERR, 1, "Meta socket read error.");
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ack) {
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
2014-10-24 18:00:40 +00:00
|
|
|
*len = msg.len;
|
|
|
|
*buf = msg.buf;
|
2014-10-24 15:42:48 +00:00
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
meta_ctx_done(meta_ctx_t *mctx)
|
|
|
|
{
|
2014-10-24 18:00:40 +00:00
|
|
|
meta_ctx_close_src_channel(mctx);
|
|
|
|
meta_ctx_close_sink_channel(mctx);
|
|
|
|
if (!mctx->do_compress)
|
|
|
|
close(mctx->comp_fd);
|
|
|
|
pthread_join(mctx->meta_thread, NULL);
|
|
|
|
return (0);
|
2014-10-24 15:42:48 +00:00
|
|
|
}
|
|
|
|
|