1 /* $Id: vndcompress.c,v 1.6 2009/04/14 07:36:16 lukem Exp $ */
4 * Copyright (c) 2005 by Florian Stoehr <netbsd@wolfnode.de>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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
50 #include "vndcompress.h"
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";
66 * Print usage information, then exit program
71 if (opmode
== OM_COMPRESS
) {
72 printf("usage: vndcompress [-cd] disk/fs-image compressed-image [blocksize]\n");
74 printf("usage: vnduncompress [-cd] compressed-image disk/fs-image\n");
81 * Compress a given file system
84 vndcompress(const char *fs
, const char *comp
, uint32_t blocksize
)
87 int total_blocks
, offtable_size
;
90 off_t fsize
, diffatom
, cursize
;
91 struct cloop_header clh
;
94 unsigned long complen
;
95 unsigned char *cb
, *ucb
;
97 fd_in
= open(fs
, O_RDONLY
);
100 err(EXIT_FAILURE
, "Cannot open input file \"%s\"", fs
);
103 fd_out
= open(comp
, O_CREAT
| O_TRUNC
| O_WRONLY
,
104 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
107 err(EXIT_FAILURE
, "Cannot create output file \"%s\"", comp
);
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
);
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
);
130 printf("(%d complete blocks)\n", total_blocks
);
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);
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
);
166 if (write(fd_out
, offtable
, offtable_size
) < offtable_size
)
167 err(EXIT_FAILURE
, "Cannot write to output file \"%s\"", comp
);
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
);
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
);
204 if ((unsigned long)write(fd_out
, cb
, complen
) != complen
)
205 err(EXIT_FAILURE
, "Cannot write to output file \"%s\"", comp
);
208 *(offtable
+ i
) = SWAPPER(curoff
);
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
);
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
);
244 * Read in header and offset table from compressed image
247 readheader(int fd
, struct cloop_header
*clh
, off_t
*dstart
)
249 uint32_t offtable_size
;
252 if ((size_t)read(fd
, clh
, sizeof(struct cloop_header
))
253 != sizeof(struct cloop_header
))
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
) {
268 *dstart
= offtable_size
+ sizeof(struct cloop_header
);
274 * Decompress a given file system image
277 vnduncompress(const char *comp
, const char *fs
)
281 struct cloop_header clh
;
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
);
293 err(EXIT_FAILURE
, "Cannot open input file \"%s\"", comp
);
296 fd_out
= open(fs
, O_CREAT
| O_TRUNC
| O_WRONLY
,
297 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
300 err(EXIT_FAILURE
, "Cannot create output file \"%s\"", fs
);
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
);
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
++) {
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
);
329 uncomplen
= clh
.block_size
;
330 rc
= uncompress(ucb
, &uncomplen
, cb
, complen
);
332 errx(EXIT_FAILURE
, "Cannot decompress block %"PRIu32
" from \"%s\" (rc=%d)",
336 write(fd_out
, ucb
, clh
.block_size
);
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
)
358 setprogname(argv
[0]);
360 if ((p
= strrchr(argv
[0], '/')) == NULL
)
364 if (strcmp(p
, "vnduncompress") == 0)
365 opmode
= OM_UNCOMPRESS
;
366 else if (strcmp(p
, "vndcompress") == 0)
367 opmode
= OM_COMPRESS
;
369 warnx("unknown program name '%s'", p
);
371 /* Process command-line options */
372 while ((ch
= getopt(argc
, argv
, "cd")) != -1) {
375 opmode
= OM_COMPRESS
;
379 opmode
= OM_UNCOMPRESS
;
399 vndcompress(argv
[0], argv
[1], strtoul(argv
[2], &ep
, 10));
401 vndcompress(argv
[0], argv
[1], DEF_BLOCKSIZE
);
406 vnduncompress(argv
[0], argv
[1]);