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