vndcompress.c revision 1.4 1 1.4 dyoung /* $Id: vndcompress.c,v 1.4 2008/02/18 03:34:04 dyoung Exp $ */
2 1.1 hubertf
3 1.1 hubertf /*
4 1.1 hubertf * Copyright (c) 2005 by Florian Stoehr <netbsd (at) wolfnode.de>
5 1.1 hubertf * All rights reserved.
6 1.1 hubertf *
7 1.1 hubertf * Redistribution and use in source and binary forms, with or without
8 1.1 hubertf * modification, are permitted provided that the following conditions
9 1.1 hubertf * are met:
10 1.1 hubertf * 1. Redistributions of source code must retain the above copyright
11 1.1 hubertf * notice, this list of conditions and the following disclaimer.
12 1.1 hubertf * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 hubertf * notice, this list of conditions and the following disclaimer in the
14 1.1 hubertf * documentation and/or other materials provided with the distribution.
15 1.1 hubertf * 3. All advertising materials mentioning features or use of this software
16 1.1 hubertf * must display the following acknowledgement:
17 1.1 hubertf * This product includes software developed by Florian Stoehr
18 1.1 hubertf * 4. The name of Florian Stoehr may not be used to endorse or promote
19 1.1 hubertf * products derived from this software without specific prior written
20 1.1 hubertf * permission.
21 1.1 hubertf *
22 1.1 hubertf * THIS SOFTWARE IS PROVIDED BY FLORIAN STOEHR ``AS IS'' AND
23 1.1 hubertf * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 1.1 hubertf * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 1.1 hubertf * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 1.1 hubertf * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 1.1 hubertf * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 1.1 hubertf * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 1.1 hubertf * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 1.1 hubertf * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 1.1 hubertf * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 1.1 hubertf * POSSIBILITY OF SUCH DAMAGE.
33 1.1 hubertf */
34 1.1 hubertf
35 1.1 hubertf /*
36 1.1 hubertf * cloop2 - Compressed filesystem images
37 1.1 hubertf * vndcompress program - Compress/decompress filesystem images to
38 1.1 hubertf * the cloop2 format
39 1.1 hubertf */
40 1.1 hubertf #include <err.h>
41 1.1 hubertf #include <fcntl.h>
42 1.1 hubertf #include <stdarg.h>
43 1.1 hubertf #include <stdio.h>
44 1.1 hubertf #include <stdlib.h>
45 1.1 hubertf #include <string.h>
46 1.1 hubertf #include <unistd.h>
47 1.1 hubertf #include <zlib.h>
48 1.1 hubertf
49 1.1 hubertf #include "vndcompress.h"
50 1.1 hubertf
51 1.1 hubertf enum opermodes {
52 1.1 hubertf OM_COMPRESS, /* Compress a fs */
53 1.1 hubertf OM_UNCOMPRESS, /* Uncompress an image */
54 1.1 hubertf };
55 1.1 hubertf
56 1.1 hubertf /*
57 1.1 hubertf * This is the original header of the Linux files. It is useless
58 1.1 hubertf * on NetBSD and integrated for compatibility issues only.
59 1.1 hubertf */
60 1.1 hubertf static const char *cloop_sh = "#!/bin/sh\n" "#V2.0 Format\n" "insmod cloop.o file=$0 && mount -r -t iso9660 /dev/cloop $1\n" "exit $?\n";
61 1.1 hubertf
62 1.1 hubertf int opmode;
63 1.1 hubertf
64 1.1 hubertf /*
65 1.1 hubertf * Print usage information, then exit program
66 1.1 hubertf */
67 1.1 hubertf void
68 1.1 hubertf usage(void)
69 1.1 hubertf {
70 1.1 hubertf if (opmode == OM_COMPRESS) {
71 1.2 wiz printf("usage: vndcompress [-cd] disk/fs-image compressed-image [blocksize]\n");
72 1.1 hubertf } else {
73 1.2 wiz printf("usage: vnduncompress [-cd] compressed-image disk/fs-image\n");
74 1.1 hubertf }
75 1.1 hubertf
76 1.1 hubertf exit(EXIT_FAILURE);
77 1.1 hubertf }
78 1.1 hubertf
79 1.1 hubertf /*
80 1.1 hubertf * Compress a given file system
81 1.1 hubertf */
82 1.1 hubertf void
83 1.1 hubertf vndcompress(const char *fs, const char *comp, uint32_t blocksize)
84 1.1 hubertf {
85 1.1 hubertf int fd_in, fd_out;
86 1.1 hubertf int total_blocks, offtable_size;
87 1.1 hubertf int i;
88 1.1 hubertf int read_blocksize;
89 1.1 hubertf off_t fsize, diffatom, cursize;
90 1.1 hubertf struct cloop_header clh;
91 1.1 hubertf uint64_t *offtable;
92 1.1 hubertf uint64_t curoff;
93 1.1 hubertf unsigned long complen;
94 1.1 hubertf unsigned char *cb, *ucb;
95 1.1 hubertf
96 1.1 hubertf fd_in = open(fs, O_RDONLY);
97 1.1 hubertf
98 1.1 hubertf if (fd_in < 0)
99 1.1 hubertf err(EXIT_FAILURE, "Cannot open input file \"%s\"", fs);
100 1.1 hubertf /* NOTREACHED */
101 1.1 hubertf
102 1.1 hubertf fd_out = open(comp, O_CREAT | O_TRUNC | O_WRONLY,
103 1.1 hubertf S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
104 1.1 hubertf
105 1.1 hubertf if (fd_out < 0)
106 1.1 hubertf err(EXIT_FAILURE, "Cannot create output file \"%s\"", comp);
107 1.1 hubertf /* NOTREACHED */
108 1.1 hubertf
109 1.1 hubertf if ((blocksize % ATOMBLOCK) || (blocksize < ATOMBLOCK))
110 1.1 hubertf errx(EXIT_FAILURE, "Invalid block size: %d (Block size must be "\
111 1.1 hubertf "a multiple of %d Bytes)", blocksize, ATOMBLOCK);
112 1.1 hubertf /* NOTREACHED */
113 1.1 hubertf
114 1.1 hubertf /*
115 1.1 hubertf * Init the compression
116 1.1 hubertf */
117 1.1 hubertf
118 1.1 hubertf /* Determine number of total input blocks, round up to complete blocks */
119 1.1 hubertf fsize = lseek(fd_in, 0, SEEK_END);
120 1.1 hubertf lseek(fd_in, 0, SEEK_SET);
121 1.1 hubertf total_blocks = fsize / blocksize;
122 1.1 hubertf
123 1.1 hubertf printf("Using blocksize: %d ", blocksize);
124 1.1 hubertf
125 1.1 hubertf if (fsize % blocksize) {
126 1.1 hubertf printf("(%d complete and 1 zero-padded blocks)\n", total_blocks);
127 1.1 hubertf total_blocks++;
128 1.1 hubertf } else {
129 1.1 hubertf printf("(%d complete blocks)\n", total_blocks);
130 1.1 hubertf }
131 1.1 hubertf
132 1.1 hubertf /* Struct fillup */
133 1.1 hubertf memset(&clh, 0, sizeof(struct cloop_header));
134 1.1 hubertf memcpy(clh.sh, cloop_sh, strlen(cloop_sh));
135 1.1 hubertf
136 1.1 hubertf /* Remember the header is also in network format! */
137 1.4 dyoung clh.block_size = SWAPPER32(blocksize);
138 1.4 dyoung clh.num_blocks = SWAPPER32(total_blocks);
139 1.1 hubertf
140 1.1 hubertf /* Prepare the offset table (unsigned 64-bit big endian offsets) */
141 1.1 hubertf offtable_size = (total_blocks + 1) * sizeof(uint64_t);
142 1.1 hubertf offtable = (uint64_t *)malloc(offtable_size);
143 1.1 hubertf
144 1.1 hubertf /*
145 1.1 hubertf * Setup block buffers.
146 1.1 hubertf * Since compression may actually stretch a block in bad cases,
147 1.1 hubertf * make the "compressed" space large enough here.
148 1.1 hubertf */
149 1.1 hubertf ucb = (unsigned char *)malloc(blocksize);
150 1.1 hubertf cb = (unsigned char *)malloc(blocksize * 2);
151 1.1 hubertf
152 1.1 hubertf /*
153 1.1 hubertf * Compression
154 1.1 hubertf *
155 1.1 hubertf * We'll leave file caching to the operating system and write
156 1.1 hubertf * first the (fixed-size) header with dummy-data, then the compressed
157 1.1 hubertf * blocks block-by-block to disk. After that, we overwrite the offset
158 1.1 hubertf * table in the image file with the real offset table.
159 1.1 hubertf */
160 1.1 hubertf if (write(fd_out, &clh, sizeof(struct cloop_header))
161 1.1 hubertf < sizeof(struct cloop_header))
162 1.1 hubertf err(EXIT_FAILURE, "Cannot write to output file \"%s\"", comp);
163 1.1 hubertf /* NOTREACHED */
164 1.1 hubertf
165 1.1 hubertf if (write(fd_out, offtable, offtable_size) < offtable_size)
166 1.1 hubertf err(EXIT_FAILURE, "Cannot write to output file \"%s\"", comp);
167 1.1 hubertf /* NOTREACHED */
168 1.1 hubertf
169 1.1 hubertf /*
170 1.1 hubertf * Offsets are relative to the beginning of the file, not
171 1.1 hubertf * relative to offset table start
172 1.1 hubertf */
173 1.1 hubertf curoff = sizeof(struct cloop_header) + offtable_size;
174 1.1 hubertf
175 1.1 hubertf /* Compression loop */
176 1.1 hubertf for (i = 0; i < total_blocks; i++) {
177 1.1 hubertf
178 1.1 hubertf /* By default, assume to read blocksize bytes */
179 1.1 hubertf read_blocksize = blocksize;
180 1.1 hubertf
181 1.1 hubertf /*
182 1.1 hubertf * However, the last block may be smaller than block size.
183 1.1 hubertf * If this is the case, pad uncompressed buffer with zeros
184 1.1 hubertf * (by zero-filling before the read() call)
185 1.1 hubertf */
186 1.1 hubertf if (i == total_blocks - 1) {
187 1.1 hubertf if (fsize % blocksize) {
188 1.1 hubertf read_blocksize = fsize % blocksize;
189 1.1 hubertf memset(ucb, 0x00, blocksize);
190 1.1 hubertf }
191 1.1 hubertf }
192 1.1 hubertf
193 1.1 hubertf if (read(fd_in, ucb, read_blocksize) < read_blocksize)
194 1.1 hubertf err(EXIT_FAILURE, "Cannot read input file \"%s\"", fs);
195 1.1 hubertf /* NOTREACHED */
196 1.1 hubertf
197 1.1 hubertf complen = blocksize * 2;
198 1.1 hubertf
199 1.1 hubertf if (compress2(cb, &complen, ucb, blocksize, Z_BEST_COMPRESSION) != Z_OK)
200 1.1 hubertf errx(EXIT_FAILURE, "Compression failed in block %d", i);
201 1.1 hubertf /* NOTREACHED */
202 1.1 hubertf
203 1.1 hubertf if (write(fd_out, cb, complen) < complen)
204 1.1 hubertf err(EXIT_FAILURE, "Cannot write to output file \"%s\"", comp);
205 1.1 hubertf /* NOTREACHED */
206 1.1 hubertf
207 1.1 hubertf *(offtable + i) = SWAPPER(curoff);
208 1.1 hubertf curoff += complen;
209 1.1 hubertf }
210 1.1 hubertf
211 1.1 hubertf /* Always write +1 block to determine (compressed) block size */
212 1.1 hubertf *(offtable + total_blocks) = SWAPPER(curoff);
213 1.1 hubertf
214 1.1 hubertf /* Fixup compression table */
215 1.1 hubertf lseek(fd_out, sizeof(struct cloop_header), SEEK_SET);
216 1.1 hubertf
217 1.1 hubertf if (write(fd_out, offtable, offtable_size) < offtable_size)
218 1.1 hubertf err(EXIT_FAILURE, "Cannot write to output file \"%s\"", comp);
219 1.1 hubertf /* NOTREACHED */
220 1.1 hubertf
221 1.1 hubertf /* Finally, align the image size to be a multiple of ATOMBLOCK bytes */
222 1.1 hubertf cursize = lseek(fd_out, 0, SEEK_END);
223 1.1 hubertf
224 1.1 hubertf if (cursize % ATOMBLOCK) {
225 1.1 hubertf /*
226 1.1 hubertf * Reusing the cb buffer here. It *IS* large enough since
227 1.1 hubertf * blocksize may not be smaller than ATOMBLOCK
228 1.1 hubertf */
229 1.1 hubertf diffatom = (((cursize / ATOMBLOCK) + 1) * ATOMBLOCK) - cursize;
230 1.1 hubertf memset(cb, 0, blocksize * 2);
231 1.1 hubertf write(fd_out, cb, diffatom);
232 1.1 hubertf }
233 1.1 hubertf
234 1.1 hubertf free(cb);
235 1.1 hubertf free(ucb);
236 1.1 hubertf free(offtable);
237 1.1 hubertf
238 1.1 hubertf close(fd_in);
239 1.1 hubertf close(fd_out);
240 1.1 hubertf }
241 1.1 hubertf
242 1.1 hubertf /*
243 1.1 hubertf * Read in header and offset table from compressed image
244 1.1 hubertf */
245 1.1 hubertf uint64_t *
246 1.1 hubertf readheader(int fd, struct cloop_header *clh, off_t *dstart)
247 1.1 hubertf {
248 1.1 hubertf uint32_t offtable_size;
249 1.1 hubertf uint64_t *offt;
250 1.1 hubertf
251 1.1 hubertf if (read(fd, clh, sizeof(struct cloop_header))
252 1.1 hubertf < sizeof(struct cloop_header))
253 1.1 hubertf return NULL;
254 1.1 hubertf
255 1.1 hubertf /* Convert endianness */
256 1.4 dyoung clh->block_size = SWAPPER32(clh->block_size);
257 1.4 dyoung clh->num_blocks = SWAPPER32(clh->num_blocks);
258 1.1 hubertf
259 1.1 hubertf offtable_size = (clh->num_blocks + 1) * sizeof(uint64_t);
260 1.1 hubertf offt = (uint64_t *)malloc(offtable_size);
261 1.1 hubertf
262 1.1 hubertf if (read(fd, offt, offtable_size) < offtable_size) {
263 1.1 hubertf free(offt);
264 1.1 hubertf return NULL;
265 1.1 hubertf }
266 1.1 hubertf
267 1.1 hubertf *dstart = offtable_size + sizeof(struct cloop_header);
268 1.1 hubertf
269 1.1 hubertf return offt;
270 1.1 hubertf }
271 1.1 hubertf
272 1.1 hubertf /*
273 1.1 hubertf * Decompress a given file system image
274 1.1 hubertf */
275 1.1 hubertf void
276 1.1 hubertf vnduncompress(const char *comp, const char *fs)
277 1.1 hubertf {
278 1.1 hubertf int fd_in, fd_out;
279 1.1 hubertf int i;
280 1.1 hubertf struct cloop_header clh;
281 1.1 hubertf uint64_t *offtable;
282 1.1 hubertf off_t imgofs, datastart;
283 1.1 hubertf unsigned long complen, uncomplen;
284 1.1 hubertf unsigned char *cb, *ucb;
285 1.1 hubertf
286 1.1 hubertf /*
287 1.1 hubertf * Setup decompression, read in header tables
288 1.1 hubertf */
289 1.1 hubertf fd_in = open(comp, O_RDONLY);
290 1.1 hubertf
291 1.1 hubertf if (fd_in < 0)
292 1.1 hubertf err(EXIT_FAILURE, "Cannot open input file \"%s\"", comp);
293 1.1 hubertf /* NOTREACHED */
294 1.1 hubertf
295 1.1 hubertf fd_out = open(fs, O_CREAT | O_TRUNC | O_WRONLY,
296 1.1 hubertf S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
297 1.1 hubertf
298 1.1 hubertf if (fd_out < 0)
299 1.1 hubertf err(EXIT_FAILURE, "Cannot create output file \"%s\"", fs);
300 1.1 hubertf /* NOTREACHED */
301 1.1 hubertf
302 1.1 hubertf offtable = readheader(fd_in, &clh, &datastart);
303 1.1 hubertf
304 1.1 hubertf if (offtable == NULL)
305 1.1 hubertf errx(EXIT_FAILURE, "Input file \"%s\": Size mismatch, too small to be a valid image", comp);
306 1.1 hubertf /* NOTREACHED */
307 1.1 hubertf
308 1.1 hubertf /* As for the compression, alloc the compressed block bigger */
309 1.1 hubertf ucb = (unsigned char *)malloc(clh.block_size);
310 1.1 hubertf cb = (unsigned char *)malloc(clh.block_size * 2);
311 1.1 hubertf
312 1.1 hubertf /*
313 1.1 hubertf * Perform the actual decompression
314 1.1 hubertf */
315 1.1 hubertf for (i = 0; i < clh.num_blocks; i++) {
316 1.1 hubertf int rc;
317 1.1 hubertf
318 1.1 hubertf imgofs = SWAPPER(*(offtable + i));
319 1.1 hubertf lseek(fd_in, imgofs, SEEK_SET);
320 1.1 hubertf
321 1.1 hubertf complen = SWAPPER(*(offtable + i + 1))
322 1.1 hubertf - SWAPPER(*(offtable + i));
323 1.1 hubertf
324 1.1 hubertf if (read(fd_in, cb, complen) < complen)
325 1.1 hubertf err(EXIT_FAILURE, "Cannot read compressed block %d from \"%s\"", i, comp);
326 1.1 hubertf /* NOTREACHED */
327 1.1 hubertf
328 1.1 hubertf uncomplen = clh.block_size;
329 1.1 hubertf rc = uncompress(ucb, &uncomplen, cb, complen);
330 1.1 hubertf if (rc != Z_OK)
331 1.1 hubertf errx(EXIT_FAILURE, "Cannot decompress block %d from \"%s\" (rc=%d)",
332 1.1 hubertf i, comp, rc);
333 1.1 hubertf /* NOTREACHED */
334 1.1 hubertf
335 1.1 hubertf write(fd_out, ucb, clh.block_size);
336 1.1 hubertf }
337 1.1 hubertf
338 1.1 hubertf free(cb);
339 1.1 hubertf free(ucb);
340 1.1 hubertf free(offtable);
341 1.1 hubertf
342 1.1 hubertf close(fd_in);
343 1.1 hubertf close(fd_out);
344 1.1 hubertf }
345 1.1 hubertf
346 1.1 hubertf /*
347 1.1 hubertf * vndcompress: Handle cloop2-compatible compressed file systems; This is the
348 1.1 hubertf * user-level configuration program, to be used in conjunction
349 1.1 hubertf * with the vnd(4) driver.
350 1.1 hubertf */
351 1.1 hubertf int
352 1.1 hubertf main(int argc, char **argv)
353 1.1 hubertf {
354 1.1 hubertf char *ep, *p;
355 1.3 he int ch;
356 1.1 hubertf
357 1.1 hubertf setprogname(argv[0]);
358 1.1 hubertf
359 1.1 hubertf if ((p = strrchr(argv[0], '/')) == NULL)
360 1.1 hubertf p = argv[0];
361 1.1 hubertf else
362 1.1 hubertf ++p;
363 1.1 hubertf if (strcmp(p, "vnduncompress") == 0)
364 1.1 hubertf opmode = OM_UNCOMPRESS;
365 1.1 hubertf else if (strcmp(p, "vndcompress") == 0)
366 1.1 hubertf opmode = OM_COMPRESS;
367 1.1 hubertf else
368 1.1 hubertf warnx("unknown program name '%s'", p);
369 1.1 hubertf
370 1.1 hubertf /* Process command-line options */
371 1.1 hubertf while ((ch = getopt(argc, argv, "cd")) != -1) {
372 1.1 hubertf switch (ch) {
373 1.1 hubertf case 'c':
374 1.1 hubertf opmode = OM_COMPRESS;
375 1.1 hubertf break;
376 1.1 hubertf
377 1.1 hubertf case 'd':
378 1.1 hubertf opmode = OM_UNCOMPRESS;
379 1.1 hubertf break;
380 1.1 hubertf
381 1.1 hubertf default:
382 1.1 hubertf usage();
383 1.1 hubertf /* NOTREACHED */
384 1.1 hubertf }
385 1.1 hubertf }
386 1.1 hubertf
387 1.1 hubertf argc -= optind;
388 1.1 hubertf argv += optind;
389 1.1 hubertf
390 1.1 hubertf if (argc < 2) {
391 1.1 hubertf usage();
392 1.1 hubertf /* NOTREACHED */
393 1.1 hubertf }
394 1.1 hubertf
395 1.1 hubertf switch (opmode) {
396 1.1 hubertf case OM_COMPRESS:
397 1.1 hubertf if (argc > 2) {
398 1.1 hubertf vndcompress(argv[0], argv[1], strtoul(argv[2], &ep, 10));
399 1.1 hubertf } else {
400 1.1 hubertf vndcompress(argv[0], argv[1], DEF_BLOCKSIZE);
401 1.1 hubertf }
402 1.1 hubertf break;
403 1.1 hubertf
404 1.1 hubertf case OM_UNCOMPRESS:
405 1.1 hubertf vnduncompress(argv[0], argv[1]);
406 1.1 hubertf break;
407 1.1 hubertf }
408 1.1 hubertf
409 1.1 hubertf exit(EXIT_SUCCESS);
410 1.1 hubertf }
411