vndcompress.c revision 1.7 1 1.7 joerg /* $Id: vndcompress.c,v 1.7 2011/09/06 18:45:04 joerg 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.5 lukem #include <inttypes.h>
43 1.1 hubertf #include <stdarg.h>
44 1.1 hubertf #include <stdio.h>
45 1.1 hubertf #include <stdlib.h>
46 1.1 hubertf #include <string.h>
47 1.1 hubertf #include <unistd.h>
48 1.1 hubertf #include <zlib.h>
49 1.1 hubertf
50 1.1 hubertf #include "vndcompress.h"
51 1.1 hubertf
52 1.1 hubertf enum opermodes {
53 1.1 hubertf OM_COMPRESS, /* Compress a fs */
54 1.1 hubertf OM_UNCOMPRESS, /* Uncompress an image */
55 1.1 hubertf };
56 1.1 hubertf
57 1.1 hubertf /*
58 1.1 hubertf * This is the original header of the Linux files. It is useless
59 1.1 hubertf * on NetBSD and integrated for compatibility issues only.
60 1.1 hubertf */
61 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";
62 1.1 hubertf
63 1.7 joerg static int opmode;
64 1.1 hubertf
65 1.1 hubertf /*
66 1.1 hubertf * Print usage information, then exit program
67 1.1 hubertf */
68 1.7 joerg __dead static void
69 1.1 hubertf usage(void)
70 1.1 hubertf {
71 1.1 hubertf if (opmode == OM_COMPRESS) {
72 1.2 wiz printf("usage: vndcompress [-cd] disk/fs-image compressed-image [blocksize]\n");
73 1.1 hubertf } else {
74 1.2 wiz printf("usage: vnduncompress [-cd] compressed-image disk/fs-image\n");
75 1.1 hubertf }
76 1.1 hubertf
77 1.1 hubertf exit(EXIT_FAILURE);
78 1.1 hubertf }
79 1.1 hubertf
80 1.1 hubertf /*
81 1.1 hubertf * Compress a given file system
82 1.1 hubertf */
83 1.7 joerg static void
84 1.1 hubertf vndcompress(const char *fs, const char *comp, uint32_t blocksize)
85 1.1 hubertf {
86 1.1 hubertf int fd_in, fd_out;
87 1.1 hubertf int total_blocks, offtable_size;
88 1.1 hubertf int i;
89 1.1 hubertf int read_blocksize;
90 1.1 hubertf off_t fsize, diffatom, cursize;
91 1.1 hubertf struct cloop_header clh;
92 1.1 hubertf uint64_t *offtable;
93 1.1 hubertf uint64_t curoff;
94 1.1 hubertf unsigned long complen;
95 1.1 hubertf unsigned char *cb, *ucb;
96 1.1 hubertf
97 1.1 hubertf fd_in = open(fs, O_RDONLY);
98 1.1 hubertf
99 1.1 hubertf if (fd_in < 0)
100 1.1 hubertf err(EXIT_FAILURE, "Cannot open input file \"%s\"", fs);
101 1.1 hubertf /* NOTREACHED */
102 1.1 hubertf
103 1.1 hubertf fd_out = open(comp, O_CREAT | O_TRUNC | O_WRONLY,
104 1.1 hubertf S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
105 1.1 hubertf
106 1.1 hubertf if (fd_out < 0)
107 1.1 hubertf err(EXIT_FAILURE, "Cannot create output file \"%s\"", comp);
108 1.1 hubertf /* NOTREACHED */
109 1.1 hubertf
110 1.1 hubertf if ((blocksize % ATOMBLOCK) || (blocksize < ATOMBLOCK))
111 1.1 hubertf errx(EXIT_FAILURE, "Invalid block size: %d (Block size must be "\
112 1.1 hubertf "a multiple of %d Bytes)", blocksize, ATOMBLOCK);
113 1.1 hubertf /* NOTREACHED */
114 1.1 hubertf
115 1.1 hubertf /*
116 1.1 hubertf * Init the compression
117 1.1 hubertf */
118 1.1 hubertf
119 1.1 hubertf /* Determine number of total input blocks, round up to complete blocks */
120 1.1 hubertf fsize = lseek(fd_in, 0, SEEK_END);
121 1.1 hubertf lseek(fd_in, 0, SEEK_SET);
122 1.1 hubertf total_blocks = fsize / blocksize;
123 1.1 hubertf
124 1.1 hubertf printf("Using blocksize: %d ", blocksize);
125 1.1 hubertf
126 1.1 hubertf if (fsize % blocksize) {
127 1.1 hubertf printf("(%d complete and 1 zero-padded blocks)\n", total_blocks);
128 1.1 hubertf total_blocks++;
129 1.1 hubertf } else {
130 1.1 hubertf printf("(%d complete blocks)\n", total_blocks);
131 1.1 hubertf }
132 1.1 hubertf
133 1.1 hubertf /* Struct fillup */
134 1.1 hubertf memset(&clh, 0, sizeof(struct cloop_header));
135 1.1 hubertf memcpy(clh.sh, cloop_sh, strlen(cloop_sh));
136 1.1 hubertf
137 1.1 hubertf /* Remember the header is also in network format! */
138 1.4 dyoung clh.block_size = SWAPPER32(blocksize);
139 1.4 dyoung clh.num_blocks = SWAPPER32(total_blocks);
140 1.1 hubertf
141 1.1 hubertf /* Prepare the offset table (unsigned 64-bit big endian offsets) */
142 1.1 hubertf offtable_size = (total_blocks + 1) * sizeof(uint64_t);
143 1.1 hubertf offtable = (uint64_t *)malloc(offtable_size);
144 1.1 hubertf
145 1.1 hubertf /*
146 1.1 hubertf * Setup block buffers.
147 1.1 hubertf * Since compression may actually stretch a block in bad cases,
148 1.1 hubertf * make the "compressed" space large enough here.
149 1.1 hubertf */
150 1.1 hubertf ucb = (unsigned char *)malloc(blocksize);
151 1.1 hubertf cb = (unsigned char *)malloc(blocksize * 2);
152 1.1 hubertf
153 1.1 hubertf /*
154 1.1 hubertf * Compression
155 1.1 hubertf *
156 1.1 hubertf * We'll leave file caching to the operating system and write
157 1.1 hubertf * first the (fixed-size) header with dummy-data, then the compressed
158 1.1 hubertf * blocks block-by-block to disk. After that, we overwrite the offset
159 1.1 hubertf * table in the image file with the real offset table.
160 1.1 hubertf */
161 1.5 lukem if ((size_t)write(fd_out, &clh, sizeof(struct cloop_header))
162 1.5 lukem != sizeof(struct cloop_header))
163 1.1 hubertf err(EXIT_FAILURE, "Cannot write to output file \"%s\"", comp);
164 1.1 hubertf /* NOTREACHED */
165 1.1 hubertf
166 1.1 hubertf if (write(fd_out, offtable, offtable_size) < offtable_size)
167 1.1 hubertf err(EXIT_FAILURE, "Cannot write to output file \"%s\"", comp);
168 1.1 hubertf /* NOTREACHED */
169 1.1 hubertf
170 1.1 hubertf /*
171 1.1 hubertf * Offsets are relative to the beginning of the file, not
172 1.1 hubertf * relative to offset table start
173 1.1 hubertf */
174 1.1 hubertf curoff = sizeof(struct cloop_header) + offtable_size;
175 1.1 hubertf
176 1.1 hubertf /* Compression loop */
177 1.1 hubertf for (i = 0; i < total_blocks; i++) {
178 1.1 hubertf
179 1.1 hubertf /* By default, assume to read blocksize bytes */
180 1.1 hubertf read_blocksize = blocksize;
181 1.1 hubertf
182 1.1 hubertf /*
183 1.1 hubertf * However, the last block may be smaller than block size.
184 1.1 hubertf * If this is the case, pad uncompressed buffer with zeros
185 1.1 hubertf * (by zero-filling before the read() call)
186 1.1 hubertf */
187 1.1 hubertf if (i == total_blocks - 1) {
188 1.1 hubertf if (fsize % blocksize) {
189 1.1 hubertf read_blocksize = fsize % blocksize;
190 1.1 hubertf memset(ucb, 0x00, blocksize);
191 1.1 hubertf }
192 1.1 hubertf }
193 1.1 hubertf
194 1.1 hubertf if (read(fd_in, ucb, read_blocksize) < read_blocksize)
195 1.1 hubertf err(EXIT_FAILURE, "Cannot read input file \"%s\"", fs);
196 1.1 hubertf /* NOTREACHED */
197 1.1 hubertf
198 1.1 hubertf complen = blocksize * 2;
199 1.1 hubertf
200 1.1 hubertf if (compress2(cb, &complen, ucb, blocksize, Z_BEST_COMPRESSION) != Z_OK)
201 1.1 hubertf errx(EXIT_FAILURE, "Compression failed in block %d", i);
202 1.1 hubertf /* NOTREACHED */
203 1.1 hubertf
204 1.5 lukem if ((unsigned long)write(fd_out, cb, complen) != complen)
205 1.1 hubertf err(EXIT_FAILURE, "Cannot write to output file \"%s\"", comp);
206 1.1 hubertf /* NOTREACHED */
207 1.1 hubertf
208 1.1 hubertf *(offtable + i) = SWAPPER(curoff);
209 1.1 hubertf curoff += complen;
210 1.1 hubertf }
211 1.1 hubertf
212 1.1 hubertf /* Always write +1 block to determine (compressed) block size */
213 1.1 hubertf *(offtable + total_blocks) = SWAPPER(curoff);
214 1.1 hubertf
215 1.1 hubertf /* Fixup compression table */
216 1.1 hubertf lseek(fd_out, sizeof(struct cloop_header), SEEK_SET);
217 1.1 hubertf
218 1.1 hubertf if (write(fd_out, offtable, offtable_size) < offtable_size)
219 1.1 hubertf err(EXIT_FAILURE, "Cannot write to output file \"%s\"", comp);
220 1.1 hubertf /* NOTREACHED */
221 1.1 hubertf
222 1.1 hubertf /* Finally, align the image size to be a multiple of ATOMBLOCK bytes */
223 1.1 hubertf cursize = lseek(fd_out, 0, SEEK_END);
224 1.1 hubertf
225 1.1 hubertf if (cursize % ATOMBLOCK) {
226 1.1 hubertf /*
227 1.1 hubertf * Reusing the cb buffer here. It *IS* large enough since
228 1.1 hubertf * blocksize may not be smaller than ATOMBLOCK
229 1.1 hubertf */
230 1.1 hubertf diffatom = (((cursize / ATOMBLOCK) + 1) * ATOMBLOCK) - cursize;
231 1.1 hubertf memset(cb, 0, blocksize * 2);
232 1.1 hubertf write(fd_out, cb, diffatom);
233 1.1 hubertf }
234 1.1 hubertf
235 1.1 hubertf free(cb);
236 1.1 hubertf free(ucb);
237 1.1 hubertf free(offtable);
238 1.1 hubertf
239 1.1 hubertf close(fd_in);
240 1.1 hubertf close(fd_out);
241 1.1 hubertf }
242 1.1 hubertf
243 1.1 hubertf /*
244 1.1 hubertf * Read in header and offset table from compressed image
245 1.1 hubertf */
246 1.7 joerg static uint64_t *
247 1.1 hubertf readheader(int fd, struct cloop_header *clh, off_t *dstart)
248 1.1 hubertf {
249 1.1 hubertf uint32_t offtable_size;
250 1.1 hubertf uint64_t *offt;
251 1.1 hubertf
252 1.5 lukem if ((size_t)read(fd, clh, sizeof(struct cloop_header))
253 1.5 lukem != sizeof(struct cloop_header))
254 1.1 hubertf return NULL;
255 1.1 hubertf
256 1.1 hubertf /* Convert endianness */
257 1.4 dyoung clh->block_size = SWAPPER32(clh->block_size);
258 1.4 dyoung clh->num_blocks = SWAPPER32(clh->num_blocks);
259 1.1 hubertf
260 1.1 hubertf offtable_size = (clh->num_blocks + 1) * sizeof(uint64_t);
261 1.1 hubertf offt = (uint64_t *)malloc(offtable_size);
262 1.1 hubertf
263 1.6 lukem if ((uint32_t)read(fd, offt, offtable_size) != offtable_size) {
264 1.1 hubertf free(offt);
265 1.1 hubertf return NULL;
266 1.1 hubertf }
267 1.1 hubertf
268 1.1 hubertf *dstart = offtable_size + sizeof(struct cloop_header);
269 1.1 hubertf
270 1.1 hubertf return offt;
271 1.1 hubertf }
272 1.1 hubertf
273 1.1 hubertf /*
274 1.1 hubertf * Decompress a given file system image
275 1.1 hubertf */
276 1.7 joerg static void
277 1.1 hubertf vnduncompress(const char *comp, const char *fs)
278 1.1 hubertf {
279 1.1 hubertf int fd_in, fd_out;
280 1.5 lukem uint32_t i;
281 1.1 hubertf struct cloop_header clh;
282 1.1 hubertf uint64_t *offtable;
283 1.1 hubertf off_t imgofs, datastart;
284 1.1 hubertf unsigned long complen, uncomplen;
285 1.1 hubertf unsigned char *cb, *ucb;
286 1.1 hubertf
287 1.1 hubertf /*
288 1.1 hubertf * Setup decompression, read in header tables
289 1.1 hubertf */
290 1.1 hubertf fd_in = open(comp, O_RDONLY);
291 1.1 hubertf
292 1.1 hubertf if (fd_in < 0)
293 1.1 hubertf err(EXIT_FAILURE, "Cannot open input file \"%s\"", comp);
294 1.1 hubertf /* NOTREACHED */
295 1.1 hubertf
296 1.1 hubertf fd_out = open(fs, O_CREAT | O_TRUNC | O_WRONLY,
297 1.1 hubertf S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
298 1.1 hubertf
299 1.1 hubertf if (fd_out < 0)
300 1.1 hubertf err(EXIT_FAILURE, "Cannot create output file \"%s\"", fs);
301 1.1 hubertf /* NOTREACHED */
302 1.1 hubertf
303 1.1 hubertf offtable = readheader(fd_in, &clh, &datastart);
304 1.1 hubertf
305 1.1 hubertf if (offtable == NULL)
306 1.1 hubertf errx(EXIT_FAILURE, "Input file \"%s\": Size mismatch, too small to be a valid image", comp);
307 1.1 hubertf /* NOTREACHED */
308 1.1 hubertf
309 1.1 hubertf /* As for the compression, alloc the compressed block bigger */
310 1.1 hubertf ucb = (unsigned char *)malloc(clh.block_size);
311 1.1 hubertf cb = (unsigned char *)malloc(clh.block_size * 2);
312 1.1 hubertf
313 1.1 hubertf /*
314 1.1 hubertf * Perform the actual decompression
315 1.1 hubertf */
316 1.1 hubertf for (i = 0; i < clh.num_blocks; i++) {
317 1.1 hubertf int rc;
318 1.1 hubertf
319 1.1 hubertf imgofs = SWAPPER(*(offtable + i));
320 1.1 hubertf lseek(fd_in, imgofs, SEEK_SET);
321 1.1 hubertf
322 1.1 hubertf complen = SWAPPER(*(offtable + i + 1))
323 1.1 hubertf - SWAPPER(*(offtable + i));
324 1.1 hubertf
325 1.5 lukem if ((unsigned long)read(fd_in, cb, complen) != complen)
326 1.5 lukem err(EXIT_FAILURE, "Cannot read compressed block %"PRIu32" from \"%s\"", i, comp);
327 1.1 hubertf /* NOTREACHED */
328 1.1 hubertf
329 1.1 hubertf uncomplen = clh.block_size;
330 1.1 hubertf rc = uncompress(ucb, &uncomplen, cb, complen);
331 1.1 hubertf if (rc != Z_OK)
332 1.5 lukem errx(EXIT_FAILURE, "Cannot decompress block %"PRIu32" from \"%s\" (rc=%d)",
333 1.1 hubertf i, comp, rc);
334 1.1 hubertf /* NOTREACHED */
335 1.1 hubertf
336 1.1 hubertf write(fd_out, ucb, clh.block_size);
337 1.1 hubertf }
338 1.1 hubertf
339 1.1 hubertf free(cb);
340 1.1 hubertf free(ucb);
341 1.1 hubertf free(offtable);
342 1.1 hubertf
343 1.1 hubertf close(fd_in);
344 1.1 hubertf close(fd_out);
345 1.1 hubertf }
346 1.1 hubertf
347 1.1 hubertf /*
348 1.1 hubertf * vndcompress: Handle cloop2-compatible compressed file systems; This is the
349 1.1 hubertf * user-level configuration program, to be used in conjunction
350 1.1 hubertf * with the vnd(4) driver.
351 1.1 hubertf */
352 1.1 hubertf int
353 1.1 hubertf main(int argc, char **argv)
354 1.1 hubertf {
355 1.1 hubertf char *ep, *p;
356 1.3 he int ch;
357 1.1 hubertf
358 1.1 hubertf setprogname(argv[0]);
359 1.1 hubertf
360 1.1 hubertf if ((p = strrchr(argv[0], '/')) == NULL)
361 1.1 hubertf p = argv[0];
362 1.1 hubertf else
363 1.1 hubertf ++p;
364 1.1 hubertf if (strcmp(p, "vnduncompress") == 0)
365 1.1 hubertf opmode = OM_UNCOMPRESS;
366 1.1 hubertf else if (strcmp(p, "vndcompress") == 0)
367 1.1 hubertf opmode = OM_COMPRESS;
368 1.1 hubertf else
369 1.1 hubertf warnx("unknown program name '%s'", p);
370 1.1 hubertf
371 1.1 hubertf /* Process command-line options */
372 1.1 hubertf while ((ch = getopt(argc, argv, "cd")) != -1) {
373 1.1 hubertf switch (ch) {
374 1.1 hubertf case 'c':
375 1.1 hubertf opmode = OM_COMPRESS;
376 1.1 hubertf break;
377 1.1 hubertf
378 1.1 hubertf case 'd':
379 1.1 hubertf opmode = OM_UNCOMPRESS;
380 1.1 hubertf break;
381 1.1 hubertf
382 1.1 hubertf default:
383 1.1 hubertf usage();
384 1.1 hubertf /* NOTREACHED */
385 1.1 hubertf }
386 1.1 hubertf }
387 1.1 hubertf
388 1.1 hubertf argc -= optind;
389 1.1 hubertf argv += optind;
390 1.1 hubertf
391 1.1 hubertf if (argc < 2) {
392 1.1 hubertf usage();
393 1.1 hubertf /* NOTREACHED */
394 1.1 hubertf }
395 1.1 hubertf
396 1.1 hubertf switch (opmode) {
397 1.1 hubertf case OM_COMPRESS:
398 1.1 hubertf if (argc > 2) {
399 1.1 hubertf vndcompress(argv[0], argv[1], strtoul(argv[2], &ep, 10));
400 1.1 hubertf } else {
401 1.1 hubertf vndcompress(argv[0], argv[1], DEF_BLOCKSIZE);
402 1.1 hubertf }
403 1.1 hubertf break;
404 1.1 hubertf
405 1.1 hubertf case OM_UNCOMPRESS:
406 1.1 hubertf vnduncompress(argv[0], argv[1]);
407 1.1 hubertf break;
408 1.1 hubertf }
409 1.1 hubertf
410 1.1 hubertf exit(EXIT_SUCCESS);
411 1.1 hubertf }
412