/* Extracts a filesystem back from a compressed fs file */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <zlib.h>
#if defined(__FreeBSD__)
#include <sys/endian.h>
#include <netinet/in.h>
typedef uint64_t loff_t;
#ifndef be64toh
static __inline __uint64_t
__bswap64(__uint64_t _x)
{

	return ((_x >> 56) | ((_x >> 40) & 0xff00) | ((_x >> 24) & 0xff0000) |
	    ((_x >> 8) & 0xff000000) | ((_x << 8) & ((__uint64_t)0xff << 32)) |
	    ((_x << 24) & ((__uint64_t)0xff << 40)) |
	    ((_x << 40) & ((__uint64_t)0xff << 48)) | ((_x << 56)));
}
#if BYTE_ORDER == LITTLE_ENDIAN
#define be64toh(x)	__bswap64(x)
#else
#define be64toh(x)
#endif
#endif
#define __be64_to_cpu be64toh
#else
#include <asm/byteorder.h>
#endif
#include "compressed_loop.h"

struct compressed_block
{
	size_t size;
	void *data;
};

int main(int argc, char *argv[])
{
	int handle;
	struct cloop_head head;
	unsigned int i;
	unsigned char *buffer, *clear_buffer;

	if (argc != 2) {
		fprintf(stderr, "Need filename\n");
		exit(1);
	}

	handle = open(argv[1], O_RDONLY);
	if (handle < 0) {
		perror("Opening compressed file\n");
		exit(1);
	}

	if (read(handle, &head, sizeof(head)) != sizeof(head)) {
		perror("Reading compressed file header\n");
		exit(1);
	}

	buffer = malloc(ntohl(head.block_size) + ntohl(head.block_size)/1000
			+ 12 + 4);
	clear_buffer = malloc(ntohl(head.block_size));
	fprintf(stderr, "%u blocks of size %u. Preamble:\n%s\n", 
		ntohl(head.num_blocks), ntohl(head.block_size), head.preamble);

	for (i = 0; i < ntohl(head.num_blocks); i++) {
		int currpos;
		unsigned long destlen = ntohl(head.block_size);
		loff_t offset[2];
		unsigned int size;

		read(handle, &offset, 2*sizeof(loff_t));
                lseek(handle, -sizeof(loff_t), SEEK_CUR);
                
		currpos = lseek(handle, 0, SEEK_CUR);
		if (lseek(handle, __be64_to_cpu(offset[0]), SEEK_SET) < 0) {
			fprintf(stderr, "lseek to %Lu: %s\n",
				__be64_to_cpu(offset[0]), strerror(errno));
			exit(1);
		}
                
                size=__be64_to_cpu(offset[1])-__be64_to_cpu(offset[0]);
		if (size > ntohl(head.block_size) + ntohl(head.block_size)/1000
		    + 12 + 4) {
			fprintf(stderr, 
				"Size %u for block %u (offset %Lu) too big\n",
				size, i, __be64_to_cpu(offset[0]));
			exit(1);
		}
		read(handle, buffer, size);
		if (lseek(handle, currpos, SEEK_SET) < 0) {
			perror("seeking");
			exit(1);
		}

		fprintf(stderr, "Block %u length %u => %lu\n",
			i, size, destlen);
		if (i == 3) {
			fprintf(stderr,
				"Block head:%02X%02X%02X%02X%02X%02X%02X%02X\n",
				buffer[0],
				buffer[1],
				buffer[2],
				buffer[3],
				buffer[4],
				buffer[5],
				buffer[6],
				buffer[7]);
			fprintf(stderr,
				"Block tail:%02X%02X%02X%02X%02X%02X%02X%02X\n",
				buffer[3063],
				buffer[3064],
				buffer[3065],
				buffer[3066],
				buffer[3067],
				buffer[3068],
				buffer[3069],
				buffer[3070]);
		}
		switch (uncompress(clear_buffer, &destlen,
				   buffer, size)) {
		case Z_OK:
			break;

		case Z_MEM_ERROR:
			fprintf(stderr, "Uncomp: oom block %u\n", i);
			exit(1);

		case Z_BUF_ERROR:
			fprintf(stderr, "Uncomp: not enough out room %u\n", i);
			exit(1);

		case Z_DATA_ERROR:
			fprintf(stderr, "Uncomp: input corrupt %u\n", i);
			exit(1);

		default:
			fprintf(stderr, "Uncomp: unknown error %u\n", i);
			exit(1);
		}
		if (destlen != ntohl(head.block_size)) {
			fprintf(stderr, "Uncomp: bad len %u (%lu not %u)\n", i,
				destlen, ntohl(head.block_size));
			exit(1);
		}
		write(STDOUT_FILENO, clear_buffer, ntohl(head.block_size));
	}
	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1