summaryrefslogtreecommitdiff
path: root/3rd-party/zisofs_tools/compress.c
blob: 56f0af05f1313489b62334df98f332afb3aa9f61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
 * This file has been modified for the cdrkit suite.
 *
 * The behaviour and appearence of the program code below can differ to a major
 * extent from the version distributed by the original author(s).
 *
 * For details, see Changelog file distributed with the cdrkit package. If you
 * received this file from another source then ask the distributing person for
 * a log of modifications.
 *
 */

/* $Id: compress.c,v 1.4 2006/07/04 04:57:42 hpa Exp $ */
/* ----------------------------------------------------------------------- *
 *   
 *   Copyright 2001-2006 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 "mkzftree.h"		/* Must be included first! */

#include <stdlib.h>
#include <stdio.h>
#include <utime.h>
#include <unistd.h>
#include <zlib.h>

#include "iso9660.h"


int block_compress_file(FILE *input, FILE *output, off_t size)
{
  struct compressed_file_header hdr;
  Bytef 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;
}