blob: fc85f44f16df7a6057fe7d58dabcde23768d1afe [file] [log] [blame]
/* $Id$ */
/* ----------------------------------------------------------------------- *
*
* Copyright 2001 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
* USA; either version 2 of the License, or (at your option) any later
* version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
#include <stdlib.h>
#include <stdio.h>
#include <utime.h>
#include <unistd.h>
#include <zlib.h>
#include "mkzftree.h"
#include "iso9660.h"
int block_compress_file(FILE *input, FILE *output, off_t size)
{
struct compressed_file_header hdr;
char inbuf[CBLOCK_SIZE], outbuf[2*CBLOCK_SIZE];
size_t bytes, pointer_bytes, nblocks, block;
uLong cbytes; /* uLong is a zlib datatype */
char *pointer_block, *curptr;
off_t position;
int i;
int force_compress = opt.force;
int zerr;
int err = EX_SOFTWARE;
if ( (sizeof hdr) & 3 ) {
fputs("INTERNAL ERROR: header is not a multiple of 4\n", stderr);
abort();
}
memset(&hdr, 0, sizeof hdr);
memcpy(&hdr.magic, zisofs_magic, sizeof zisofs_magic);
hdr.header_size = (sizeof hdr) >> 2;
hdr.block_size = CBLOCK_SIZE_LG2;
set_731(&hdr.uncompressed_len, size);
if ( fwrite(&hdr, sizeof hdr, 1, output) != 1 )
return EX_CANTCREAT;
nblocks = (size+CBLOCK_SIZE-1) >> CBLOCK_SIZE_LG2;
pointer_bytes = 4*(nblocks+1);
pointer_block = xmalloc(pointer_bytes);
memset(pointer_block, 0, pointer_bytes);
if ( fseek(output, pointer_bytes, SEEK_CUR) == -1 ) {
err = EX_CANTCREAT;
goto free_ptr_bail;
}
curptr = pointer_block;
position = sizeof hdr + pointer_bytes;
block = 0;
while ( (bytes = fread(inbuf, 1, CBLOCK_SIZE, input)) > 0 ) {
if ( bytes < CBLOCK_SIZE && block < nblocks-1 ) {
err = EX_IOERR;
goto free_ptr_bail;
}
/* HACK: If the file has our magic number, always compress */
if ( block == 0 && bytes >= sizeof zisofs_magic ) {
if ( !memcmp(inbuf, zisofs_magic, sizeof zisofs_magic) )
force_compress = 1;
}
set_731(curptr, position); curptr += 4;
/* We have two special cases: a zero-length block is defined as all zero,
and a block the length of which is equal to the block size is unencoded. */
for ( i = 0 ; i < (int)CBLOCK_SIZE ; i++ ) {
if ( inbuf[i] ) break;
}
if ( i == CBLOCK_SIZE ) {
/* All-zero block. No output */
} else {
cbytes = 2*CBLOCK_SIZE;
if ( (zerr = compress2(outbuf, &cbytes, inbuf, bytes, opt.level))
!= Z_OK ) {
err = (zerr == Z_MEM_ERROR) ? EX_OSERR : EX_SOFTWARE;
goto free_ptr_bail; /* Compression failure */
}
if ( fwrite(outbuf, 1, cbytes, output) != cbytes ) {
err = EX_CANTCREAT;
goto free_ptr_bail;
}
position += cbytes;
}
block++;
}
/* Set pointer to the end of the final block */
set_731(curptr, position);
/* Now write the pointer table */
if ( fseek(output, sizeof hdr, SEEK_SET) == -1 ||
fwrite(pointer_block, 1, pointer_bytes, output) != pointer_bytes ) {
err = EX_CANTCREAT;
goto free_ptr_bail;
}
free(pointer_block);
/* Now make sure that this was actually the right thing to do */
if ( !force_compress && position >= size ) {
/* Incompressible file, just copy it */
rewind(input);
rewind(output);
position = 0;
while ( (bytes = fread(inbuf, 1, CBLOCK_SIZE, input)) > 0 ) {
if ( fwrite(inbuf, 1, bytes, output) != bytes )
return EX_CANTCREAT;
position += bytes;
}
/* Truncate the file to the correct size */
fflush(output);
ftruncate(fileno(output), position);
}
/* If we get here, we're done! */
return 0;
/* Common bailout code */
free_ptr_bail:
free(pointer_block);
return err;
}