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