1/* 2 * ---------------------------------------------------------------------------- 3 * Derived from mkuzip.c by Aleksandr Rybalko <ray@ddteam.net> 4 * ---------------------------------------------------------------------------- 5 * "THE BEER-WARE LICENSE" (Revision 42): 6 * <sobomax@FreeBSD.ORG> wrote this file. As long as you retain this notice you 7 * can do whatever you want with this stuff. If we meet some day, and you think 8 * this stuff is worth it, you can buy me a beer in return. Maxim Sobolev 9 * ---------------------------------------------------------------------------- 10 * 11 * $FreeBSD: releng/10.3/usr.bin/mkulzma/mkulzma.c 229538 2012-01-04 23:45:10Z ray $ 12 * 13 */ 14 15#include <sys/disk.h> 16#include <sys/endian.h> 17#include <sys/param.h> 18#include <sys/stat.h> 19#include <sys/uio.h> 20#include <netinet/in.h> 21#include <err.h> 22#include <fcntl.h> 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26#include <unistd.h> 27 28#include <lzma.h> 29 30#define CLSTSIZE 16384 31#define DEFAULT_SUFX ".ulzma" 32 33#define USED_BLOCKSIZE DEV_BSIZE 34 35#define CLOOP_MAGIC_LEN 128 36/* Format L3.0, since we move to XZ API */ 37static char CLOOP_MAGIC_START[] = 38 "#!/bin/sh\n" 39 "#L3.0\n" 40 "n=uncompress\n" 41 "m=geom_$n\n" 42 "(kldstat -m $m 2>&-||kldload $m)>&-&&" 43 "mount_cd9660 /dev/`mdconfig -af $0`.$n $1\n" 44 "exit $?\n"; 45 46static char *readblock(int, char *, u_int32_t); 47static void usage(void); 48static void *safe_malloc(size_t); 49static void cleanup(void); 50 51static char *cleanfile = NULL; 52 53int main(int argc, char **argv) 54{ 55 char *iname, *oname, *obuf, *ibuf; 56 int fdr, fdw, i, opt, verbose, tmp; 57 struct iovec iov[2]; 58 struct stat sb; 59 uint32_t destlen; 60 uint64_t offset; 61 uint64_t *toc; 62 lzma_filter filters[2]; 63 lzma_options_lzma opt_lzma; 64 lzma_ret ret; 65 lzma_stream strm = LZMA_STREAM_INIT; 66 struct cloop_header { 67 char magic[CLOOP_MAGIC_LEN]; /* cloop magic */ 68 uint32_t blksz; /* block size */ 69 uint32_t nblocks; /* number of blocks */ 70 } hdr; 71 72 memset(&hdr, 0, sizeof(hdr)); 73 hdr.blksz = CLSTSIZE; 74 strcpy(hdr.magic, CLOOP_MAGIC_START); 75 oname = NULL; 76 verbose = 0; 77 78 while((opt = getopt(argc, argv, "o:s:v")) != -1) { 79 switch(opt) { 80 case 'o': 81 oname = optarg; 82 break; 83 84 case 's': 85 tmp = atoi(optarg); 86 if (tmp <= 0) { 87 errx(1, 88 "invalid cluster size specified: %s", 89 optarg); 90 /* Not reached */ 91 } 92 if (tmp % USED_BLOCKSIZE != 0) { 93 errx(1, 94 "cluster size should be multiple of %d", 95 USED_BLOCKSIZE); 96 /* Not reached */ 97 } 98 if ( tmp > MAXPHYS) { 99 errx(1, "cluster size is too large"); 100 /* Not reached */ 101 } 102 hdr.blksz = tmp; 103 break; 104 105 case 'v': 106 verbose = 1; 107 break; 108 109 default: 110 usage(); 111 /* Not reached */ 112 } 113 } 114 argc -= optind; 115 argv += optind; 116 117 if (argc != 1) { 118 usage(); 119 /* Not reached */ 120 } 121 122 iname = argv[0]; 123 if (oname == NULL) { 124 asprintf(&oname, "%s%s", iname, DEFAULT_SUFX); 125 if (oname == NULL) { 126 err(1, "can't allocate memory"); 127 /* Not reached */ 128 } 129 } 130 131 obuf = safe_malloc(hdr.blksz*2); 132 ibuf = safe_malloc(hdr.blksz); 133 134 signal(SIGHUP, exit); 135 signal(SIGINT, exit); 136 signal(SIGTERM, exit); 137 signal(SIGXCPU, exit); 138 signal(SIGXFSZ, exit); 139 atexit(cleanup); 140 141 fdr = open(iname, O_RDONLY); 142 if (fdr < 0) { 143 err(1, "open(%s)", iname); 144 /* Not reached */ 145 } 146 if (fstat(fdr, &sb) != 0) { 147 err(1, "fstat(%s)", iname); 148 /* Not reached */ 149 } 150 if (S_ISCHR(sb.st_mode)) { 151 off_t ms; 152 153 if (ioctl(fdr, DIOCGMEDIASIZE, &ms) < 0) { 154 err(1, "ioctl(DIOCGMEDIASIZE)"); 155 /* Not reached */ 156 } 157 sb.st_size = ms; 158 } else if (!S_ISREG(sb.st_mode)) { 159 fprintf(stderr, 160 "%s: not a character device or regular file\n", 161 iname); 162 exit(1); 163 } 164 hdr.nblocks = sb.st_size / hdr.blksz; 165 if ((sb.st_size % hdr.blksz) != 0) { 166 if (verbose != 0) 167 fprintf(stderr, "file size is not multiple " 168 "of %d, padding data\n", hdr.blksz); 169 hdr.nblocks++; 170 } 171 toc = safe_malloc((hdr.nblocks + 1) * sizeof(*toc)); 172 173 fdw = open(oname, O_WRONLY | O_TRUNC | O_CREAT, 174 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 175 if (fdw < 0) { 176 err(1, "open(%s)", oname); 177 /* Not reached */ 178 } 179 cleanfile = oname; 180 181 /* 182 * Prepare header that we will write later when we have index ready. 183 */ 184 iov[0].iov_base = (char *)&hdr; 185 iov[0].iov_len = sizeof(hdr); 186 iov[1].iov_base = (char *)toc; 187 iov[1].iov_len = (hdr.nblocks + 1) * sizeof(*toc); 188 offset = iov[0].iov_len + iov[1].iov_len; 189 190 /* Reserve space for header */ 191 lseek(fdw, offset, SEEK_SET); 192 193 if (verbose != 0) 194 fprintf(stderr, "data size %ju bytes, number of clusters " 195 "%u, index length %zu bytes\n", sb.st_size, 196 hdr.nblocks, iov[1].iov_len); 197 198 /* Init lzma encoder */ 199 if (lzma_lzma_preset(&opt_lzma, LZMA_PRESET_DEFAULT)) 200 errx(1, "Error loading LZMA preset"); 201 202 filters[0].id = LZMA_FILTER_LZMA2; 203 filters[0].options = &opt_lzma; 204 filters[1].id = LZMA_VLI_UNKNOWN; 205 206 for(i = 0; i == 0 || ibuf != NULL; i++) { 207 ibuf = readblock(fdr, ibuf, hdr.blksz); 208 if (ibuf != NULL) { 209 destlen = hdr.blksz*2; 210 211 ret = lzma_stream_encoder(&strm, filters, 212 LZMA_CHECK_CRC32); 213 if (ret != LZMA_OK) { 214 if (ret == LZMA_MEMLIMIT_ERROR) 215 errx(1, "can't compress data: " 216 "LZMA_MEMLIMIT_ERROR"); 217 218 errx(1, "can't compress data: " 219 "LZMA compressor ERROR"); 220 } 221 222 strm.next_in = ibuf; 223 strm.avail_in = hdr.blksz; 224 strm.next_out = obuf; 225 strm.avail_out = hdr.blksz*2; 226 227 ret = lzma_code(&strm, LZMA_FINISH); 228 229 if (ret != LZMA_STREAM_END) { 230 /* Error */ 231 errx(1, "lzma_code FINISH failed, code=%d, " 232 "pos(in=%zd, out=%zd)", 233 ret, 234 (hdr.blksz - strm.avail_in), 235 (hdr.blksz*2 - strm.avail_out)); 236 } 237 238 destlen -= strm.avail_out; 239 240 lzma_end(&strm); 241 242 if (verbose != 0) 243 fprintf(stderr, "cluster #%d, in %u bytes, " 244 "out %u bytes\n", i, hdr.blksz, destlen); 245 } else { 246 destlen = USED_BLOCKSIZE - (offset % USED_BLOCKSIZE); 247 memset(obuf, 0, destlen); 248 if (verbose != 0) 249 fprintf(stderr, "padding data with %u bytes" 250 " so that file size is multiple of %d\n", 251 destlen, 252 USED_BLOCKSIZE); 253 } 254 if (write(fdw, obuf, destlen) < 0) { 255 err(1, "write(%s)", oname); 256 /* Not reached */ 257 } 258 toc[i] = htobe64(offset); 259 offset += destlen; 260 } 261 close(fdr); 262 263 if (verbose != 0) 264 fprintf(stderr, "compressed data to %ju bytes, saved %lld " 265 "bytes, %.2f%% decrease.\n", offset, 266 (long long)(sb.st_size - offset), 267 100.0 * (long long)(sb.st_size - offset) / 268 (float)sb.st_size); 269 270 /* Convert to big endian */ 271 hdr.blksz = htonl(hdr.blksz); 272 hdr.nblocks = htonl(hdr.nblocks); 273 /* Write headers into pre-allocated space */ 274 lseek(fdw, 0, SEEK_SET); 275 if (writev(fdw, iov, 2) < 0) { 276 err(1, "writev(%s)", oname); 277 /* Not reached */ 278 } 279 cleanfile = NULL; 280 close(fdw); 281 282 exit(0); 283} 284 285static char * 286readblock(int fd, char *ibuf, u_int32_t clstsize) 287{ 288 int numread; 289 290 bzero(ibuf, clstsize); 291 numread = read(fd, ibuf, clstsize); 292 if (numread < 0) { 293 err(1, "read() failed"); 294 /* Not reached */ 295 } 296 if (numread == 0) { 297 return NULL; 298 } 299 return ibuf; 300} 301 302static void 303usage(void) 304{ 305 306 fprintf(stderr, "usage: mkulzma [-v] [-o outfile] [-s cluster_size] " 307 "infile\n"); 308 exit(1); 309} 310 311static void * 312safe_malloc(size_t size) 313{ 314 void *retval; 315 316 retval = malloc(size); 317 if (retval == NULL) { 318 err(1, "can't allocate memory"); 319 /* Not reached */ 320 } 321 return retval; 322} 323 324static void 325cleanup(void) 326{ 327 328 if (cleanfile != NULL) 329 unlink(cleanfile); 330} 331