1 /* $NetBSD: vnduncompress.c,v 1.2 2013/05/06 22:53:24 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2013 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Taylor R. Campbell. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __RCSID("$NetBSD: vnduncompress.c,v 1.2 2013/05/06 22:53:24 riastradh Exp $"); 34 35 #include <sys/endian.h> 36 37 #include <assert.h> 38 #include <err.h> 39 #include <fcntl.h> 40 #include <inttypes.h> 41 #include <limits.h> 42 #include <stdint.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <unistd.h> 46 #include <zlib.h> 47 48 #include "common.h" 49 50 int 51 vnduncompress(int argc, char **argv, const struct options *O __unused) 52 { 53 54 if (argc != 2) 55 usage(); 56 57 const char *const cloop2_pathname = argv[0]; 58 const char *const image_pathname = argv[1]; 59 60 /* Open the cloop2 and image files. */ 61 const int cloop2_fd = open(cloop2_pathname, O_RDONLY); 62 if (cloop2_fd == -1) 63 err(1, "open(%s)", cloop2_pathname); 64 65 const int image_fd = open(image_pathname, 66 /* XXX O_EXCL, not O_TRUNC */ 67 (O_WRONLY | O_CREAT | O_TRUNC), 0777); 68 if (image_fd == -1) 69 err(1, "open(%s)", image_pathname); 70 71 /* Read the header. */ 72 struct cloop2_header header; 73 const ssize_t h_read = read(cloop2_fd, &header, sizeof(header)); 74 if (h_read == -1) 75 err(1, "read header"); 76 assert(h_read >= 0); 77 if ((size_t)h_read != sizeof(header)) 78 errx(1, "partial read of header: %zu != %zu", 79 (size_t)h_read, sizeof(header)); 80 81 const uint32_t blocksize = be32toh(header.cl2h_blocksize); 82 const uint32_t n_blocks = be32toh(header.cl2h_n_blocks); 83 84 /* Sanity-check the header parameters. */ 85 __CTASSERT(MIN_BLOCKSIZE <= UINT32_MAX); 86 if (blocksize < MIN_BLOCKSIZE) 87 errx(1, "blocksize too small: %"PRIu32 88 " (must be at least %"PRIu32")", 89 blocksize, (uint32_t)MIN_BLOCKSIZE); 90 __CTASSERT(MAX_BLOCKSIZE <= UINT32_MAX); 91 if (MAX_BLOCKSIZE < blocksize) 92 errx(1, "blocksize too large: %"PRIu32 93 " (must be at most %"PRIu32")", 94 blocksize, (uint32_t)MAX_BLOCKSIZE); 95 __CTASSERT(DEV_BSIZE <= UINT32_MAX); 96 if ((blocksize % DEV_BSIZE) != 0) 97 errx(1, "bad blocksize: %"PRIu32 98 " (not a multiple of %"PRIu32")", 99 blocksize, (uint32_t)DEV_BSIZE); 100 __CTASSERT(MAX_N_BLOCKS <= UINT32_MAX); 101 if (MAX_N_BLOCKS < n_blocks) 102 errx(1, "too many blocks: %"PRIu32" (max %"PRIu32")", 103 n_blocks, (uint32_t)MAX_N_BLOCKS); 104 105 /* Allocate an offset table. */ 106 __CTASSERT(MAX_N_BLOCKS <= (UINT32_MAX - 1)); 107 __CTASSERT((MAX_N_BLOCKS + 1) == MAX_N_OFFSETS); 108 const uint32_t n_offsets = (n_blocks + 1); 109 110 __CTASSERT(MAX_N_OFFSETS <= (SIZE_MAX / sizeof(uint64_t))); 111 uint64_t *const offset_table = malloc(n_offsets * sizeof(uint64_t)); 112 113 /* Read the offset table in. */ 114 const ssize_t ot_read = read(cloop2_fd, offset_table, 115 (n_offsets * sizeof(uint64_t))); 116 if (ot_read == -1) 117 err(1, "read offset table"); 118 assert(ot_read >= 0); 119 if ((size_t)ot_read != (n_offsets * sizeof(uint64_t))) 120 errx(1, "partial read of offset table: %zu != %zu", 121 (size_t)ot_read, (size_t)(n_offsets * sizeof(uint64_t))); 122 123 /* Allocate compression buffers. */ 124 /* XXX compression ratio bound */ 125 __CTASSERT(MAX_BLOCKSIZE <= (SIZE_MAX / 2)); 126 void *const compbuf = malloc(2 * (size_t)blocksize); 127 if (compbuf == NULL) 128 err(1, "malloc compressed buffer"); 129 130 __CTASSERT(MAX_BLOCKSIZE <= SIZE_MAX); 131 void *const uncompbuf = malloc(blocksize); 132 if (uncompbuf == NULL) 133 err(1, "malloc uncompressed buffer"); 134 135 /* 136 * Uncompress the blocks. 137 */ 138 __CTASSERT(MAX_N_OFFSETS <= (OFF_MAX / sizeof(uint64_t))); 139 __CTASSERT(sizeof(header) <= 140 (OFF_MAX - (MAX_N_OFFSETS * sizeof(uint64_t)))); 141 __CTASSERT(OFF_MAX <= UINT64_MAX); 142 uint64_t offset = (sizeof(header) + (n_offsets * sizeof(uint64_t))); 143 uint32_t blkno; 144 for (blkno = 0; blkno < n_blocks; blkno++) { 145 const uint64_t start = be64toh(offset_table[blkno]); 146 const uint64_t end = be64toh(offset_table[blkno + 1]); 147 148 /* Sanity-check the offsets. */ 149 if (start != offset) 150 errx(1, "strange offset for block %"PRIu32": %"PRIu64, 151 blkno, start); 152 /* XXX compression ratio bound */ 153 __CTASSERT(MAX_BLOCKSIZE <= (SIZE_MAX / 2)); 154 if ((2 * (size_t)blocksize) <= (end - start)) 155 errx(1, "block %"PRIu32" too large: %"PRIu64" bytes", 156 blkno, (end - start)); 157 assert(offset <= MIN(OFF_MAX, UINT64_MAX)); 158 if ((MIN(OFF_MAX, UINT64_MAX) - offset) < (end - start)) 159 errx(1, "block %"PRIu32" overflows offset:" 160 " %"PRIu64" + %"PRIu64, 161 blkno, offset, (end - start)); 162 163 /* Read the compressed block. */ 164 const ssize_t n_read = read(cloop2_fd, compbuf, (end - start)); 165 if (n_read == -1) 166 err(1, "read block %"PRIu32, blkno); 167 assert(n_read >= 0); 168 if ((size_t)n_read != (end - start)) 169 errx(1, "partial read of block %"PRIu32": %zu != %zu", 170 blkno, (size_t)n_read, (size_t)(end - start)); 171 172 /* Uncompress the block. */ 173 const unsigned long complen = (end - start); 174 unsigned long uncomplen = blocksize; 175 const int zerror = uncompress(uncompbuf, &uncomplen, compbuf, 176 complen); 177 if (zerror != Z_OK) 178 errx(1, "block %"PRIu32" decompression failure (%d)" 179 ": %s", blkno, zerror, zError(zerror)); 180 181 /* Sanity-check the uncompressed length. */ 182 assert(uncomplen <= blocksize); 183 if (((blkno + 1) < n_blocks) && (uncomplen != blocksize)) 184 errx(1, "truncated non-final block %"PRIu32 185 ": %lu bytes", blkno, uncomplen); 186 187 /* Write the uncompressed block. */ 188 const ssize_t n_written = write(image_fd, uncompbuf, 189 uncomplen); 190 if (n_written == -1) 191 err(1, "write block %"PRIu32, blkno); 192 assert(n_written >= 0); 193 if ((size_t)n_written != uncomplen) 194 errx(1, "partial write of block %"PRIu32": %zu != %lu", 195 blkno, (size_t)n_written, uncomplen); 196 197 /* Advance our position. */ 198 assert((size_t)n_read <= (MIN(OFF_MAX, UINT64_MAX) - offset)); 199 offset += (size_t)n_read; 200 } 201 202 /* Free the offset table and compression buffers. */ 203 free(offset_table); 204 free(uncompbuf); 205 free(compbuf); 206 207 /* Close the files. */ 208 if (close(image_fd) == -1) 209 warn("close(image fd)"); 210 if (close(cloop2_fd) == -1) 211 warn("close(cloop2 fd)"); 212 213 return 0; 214 } 215