/*
cloop -- compress loopback device support
*/
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <zlib.h>
#include <err.h>
#include <sys/param.h>
#include <sys/endian.h>
#include <netinet/in.h>
#define CLOOP_HEADROOM 128
#define CLOOP_PREAMBLE "#!/bin/sh\n" "#V2.0 Format\n" "exit $?\n"
#define DEBUG 0
struct cloop_head {
char preamble[CLOOP_HEADROOM];
u_int32_t block_size;
u_int32_t num_blocks;
};
struct obuf {
int fd;
char *buf;
uint size;
uint pos;
off_t file_off;
};
struct cloop_sc {
int fd;
uint block_size;
uint num_blocks;
off_t zero_off; /* all zero data */
char *zero_block;
char *zero_block_c;
unsigned long zero_block_c_size;
off_t cur_off; /* input */
off_t data_off; /* out */
struct obuf data, index;
z_stream zstream;
off_t zeros;
};
void
cloop_flush_buffer(struct cloop_sc *sc, struct obuf *b)
{
if (b->pos == 0)
return;
if (pwrite(b->fd, b->buf, (size_t) b->pos, b->file_off) < 0)
err(1, "pwrite failed");
b->file_off += b->pos;
b->pos = 0;
}
__inline void
cloop_slide_index(struct cloop_sc *sc, uint size)
{
sc->index.pos += size;
if (sc->index.pos > sc->index.size) {
err(1, "index buffer overflow");
}
if (sc->index.pos >= sc->index.size) {
cloop_flush_buffer(sc, &sc->index);
}
}
void
cloop_deflate_init(struct cloop_sc *sc)
{
int error;
sc->zstream.zalloc = (alloc_func)0;
sc->zstream.zfree = (free_func)0;
sc->zstream.opaque = (voidpf)0;
sc->zstream.next_out = sc->data.buf;
sc->zstream.avail_out = sc->data.size;
#if 1
error = deflateInit(&sc->zstream, Z_DEFAULT_COMPRESSION);
#else
error = deflateInit(&sc->zstream, Z_BEST_SPEED);
#endif
if (error != Z_OK) {
err(1, "deflateInit");
}
}
void
cloop_deflate(struct cloop_sc *sc, char *buf, size_t size)
{
int error;
sc->zstream.next_in = buf;
sc->zstream.avail_in = size;
while (sc->zstream.avail_in > 0) {
error = deflate(&sc->zstream, Z_NO_FLUSH);
if (error != Z_OK && error != Z_STREAM_END)
err(1, "deflate");
sc->data.pos = sc->data.size - sc->zstream.avail_out;
if (sc->zstream.avail_out == 0) {
cloop_flush_buffer(sc, &sc->data) ;
sc->zstream.next_out = sc->data.buf;
sc->zstream.avail_out = sc->data.size;
}
}
}
void
cloop_deflate_finish(struct cloop_sc *sc)
{
int error;
for (;;) {
error = deflate(&sc->zstream, Z_FINISH);
sc->data.pos = sc->data.size - sc->zstream.avail_out;
if (sc->zstream.avail_out == 0) {
cloop_flush_buffer(sc, &sc->data) ;
sc->zstream.next_out = sc->data.buf;
sc->zstream.avail_out = sc->data.size;
}
if (error == Z_STREAM_END)
break;
if (error != Z_OK)
err(1, "deflate() in cloop_deflate_finish()");
}
#if DEBUG
printf("total in: %lu out: %lu -- %lu%%\n",
sc->zstream.total_in,
sc->zstream.total_out,
100 * sc->zstream.total_out/sc->zstream.total_in);
#endif
sc->data_off += sc->zstream.total_out;
error = deflateReset(&sc->zstream);
if (error != Z_OK)
err(1, "deflateReset");
}
struct cloop_sc *
cloop_create(int fd, off_t size, uint block_size)
{
struct cloop_sc *sc;
struct cloop_head *ch;
int error;
size_t iosize;
char *buf;
/* check seekable */
error = lseek(fd, 0, SEEK_SET);
if (error != 0) {
return (NULL);
}
sc = malloc(sizeof(struct cloop_sc));
if (sc == NULL) {
err(1, "malloc failed\n");
}
sc->block_size = block_size;
sc->num_blocks = (size + block_size - 1) / block_size;
iosize = 4*1024*1024;
buf = malloc(iosize*2);
if (buf == NULL) {
err(1, "malloc failed\n");
}
/* buffer for header and index */
sc->index.fd = fd;
sc->index.buf = buf;
sc->index.size = iosize;
bzero(sc->index.buf, sc->index.size);
sc->index.file_off = 0;
ch = (struct cloop_head *) sc->index.buf;
bcopy(CLOOP_PREAMBLE, ch->preamble, sizeof(CLOOP_PREAMBLE));
ch->block_size = htonl(sc->block_size);
ch->num_blocks = htonl(sc->num_blocks);
sc->index.pos = sizeof(struct cloop_head);
/* buffer for compressed data */
sc->data.fd = fd;
sc->data.buf = buf + iosize;
sc->data.size = iosize;
bzero(sc->data.buf, sc->data.size);
sc->data.file_off = sizeof(struct cloop_head)
+ sizeof(off_t) * (sc->num_blocks + 1);
sc->data.pos = 0;
sc->data_off = sc->zero_off = sc->data.file_off;
sc->cur_off = 0;
/* build all zero block */
sc->zero_block = malloc(block_size * 2);
if (sc->zero_block == NULL) {
err(1, "malloc failed\n");
}
bzero(sc->zero_block, block_size);
sc->zero_block_c = sc->zero_block + sc->block_size;
sc->zero_block_c_size = sc->block_size;
sc->zeros = 0;
cloop_deflate_init(sc);
#if 0
cloop_deflate(sc, sc->zero_block, sc->block_size);
cloop_deflate_finish(sc);
#endif
error = compress2(sc->zero_block_c, &sc->zero_block_c_size,
sc->zero_block, sc->block_size, Z_BEST_COMPRESSION);
if (error != Z_OK)
err(1, "compress2");
return (sc);
}
void
cloop_write_index(struct cloop_sc *sc, off_t offset)
{
off_t *index;
index = (off_t *)(sc->index.buf + sc->index.pos);
*index = htobe64(offset);
cloop_slide_index(sc, sizeof(off_t));
#if DEBUG
printf(" index %ju(block:%ju)", offset, sc->cur_off / sc->block_size);
#endif
}
void
cloop_write_data(struct cloop_sc *sc, char *buf, uint size)
{
uint res;
sc->data_off += size;
while (size > 0) {
res = sc->data.size - sc->data.pos;
if (size < res)
res = size;
bcopy(buf, sc->data.buf + sc->data.pos, res);
size -= res;
buf += res;
sc->data.pos += res;
if (sc->data.pos == sc->data.size)
cloop_flush_buffer(sc, &sc->data);
}
sc->zstream.next_out = sc->data.buf + sc->data.pos;
sc->zstream.avail_out = sc->data.size - sc->data.pos;
}
void
cloop_flush_block(struct cloop_sc *sc)
{
uint block_off, res;
/* pad zero and flush current block */
block_off = sc->cur_off % sc->block_size;
res = sc->block_size - block_off;
cloop_write_index(sc, sc->data_off);
if (block_off == 0) {
#if 1 /* XXX do nothing if geom_uzip is patched */
cloop_write_data(sc, sc->zero_block_c, sc->zero_block_c_size);
sc->zeros += sc->zero_block_c_size;
#endif
#if DEBUG
printf(" all-zero");
#endif
} else {
#if DEBUG
printf(" zeros(%d)", res);
#endif
cloop_deflate(sc, sc->zero_block, res);
cloop_deflate_finish(sc);
}
sc->cur_off += res;
#if DEBUG
printf("\nblock: %ju", sc->cur_off / sc->block_size);
#endif
}
void
cloop_flush(struct cloop_sc *sc)
{
/* flush */
while (sc->cur_off < (off_t)sc->block_size * sc->num_blocks) {
cloop_flush_block(sc);
}
cloop_write_index(sc, sc->data_off);
cloop_flush_buffer(sc, &sc->index);
cloop_flush_buffer(sc, &sc->data);
printf("cloop: %.3lf/%.3lf MBytes (%.1lf%%) written"
" (index: %.3lf MB, zeros: %.3lf MB)\n",
(double)sc->data_off/1024/1024,
(double)sc->cur_off/1024/1024,
(double)sc->data_off/sc->cur_off * 100,
((double)sizeof(struct cloop_head)
+ sizeof(off_t) * (sc->num_blocks + 1))/1024/1024,
(double)sc->zeros/1024/1024
);
free(sc->index.buf);
free(sc->zero_block);
deflateEnd(&sc->zstream);
free(sc);
}
void
cloop_write(struct cloop_sc *sc, off_t off, char *buf, size_t size)
{
uint cur_block, new_block, block_off, res, pad;
off_t data_off;
if (off < sc->cur_off) {
err(1, "offset number backward! %ju -> %ju",
sc->cur_off, off);
}
if (off + size > (off_t)sc->block_size * sc->num_blocks) {
err(1, "data size overflow %ju > %ju",
off + size, (off_t)sc->block_size * sc->num_blocks);
}
cur_block = (uint)(sc->cur_off / sc->block_size);
new_block = (uint)(off / sc->block_size);
/* Padding zero blocks */
while (cur_block < new_block) {
cloop_flush_block(sc);
cur_block = sc->cur_off / sc->block_size;
}
pad = off - sc->cur_off;
if (pad > 0) {
#if DEBUG
printf(" zeros(%d)", pad);
#endif
cloop_deflate(sc, sc->zero_block, pad);
sc->cur_off = off;
}
block_off = sc->cur_off % sc->block_size;
while (size > 0) {
res = sc->block_size - block_off;
if (size >= res) {
data_off = sc->data_off;
#if DEBUG
printf(" data_res(%d)", res);
#endif
cloop_deflate(sc, buf, res);
cloop_deflate_finish(sc);
cloop_write_index(sc, data_off);
sc->cur_off += res;
size -= res;
buf += res;
#if DEBUG
printf("\nblock: %ju", sc->cur_off / sc->block_size);
#endif
} else {
#if DEBUG
printf(" data_size(%d)", size);
#endif
cloop_deflate(sc, buf, size);
sc->cur_off += size;
buf += size;
size = 0;
}
block_off = 0;
}
}
#if 0
int
main()
{
int fd, i;
char buf[1024];
struct cloop_sc *sc;
fd = open("hogeimage", O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR );
if (fd < 0) {
err(1, "open");
}
sc = cloop_create(fd, 16*1024*1024, 16*1024);
#if 0
for (i = 1; i < 64; i ++) {
memset(buf, i, 256);
cloop_write(sc, (off_t)256*i*2 + i, buf, 128);
}
#endif
cloop_flush(sc);
close(fd);
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1