1229538Sray/* 2229538Sray * ---------------------------------------------------------------------------- 3229538Sray * Derived from mkuzip.c by Aleksandr Rybalko <ray@ddteam.net> 4229538Sray * ---------------------------------------------------------------------------- 5229538Sray * "THE BEER-WARE LICENSE" (Revision 42): 6229538Sray * <sobomax@FreeBSD.ORG> wrote this file. As long as you retain this notice you 7229538Sray * can do whatever you want with this stuff. If we meet some day, and you think 8229538Sray * this stuff is worth it, you can buy me a beer in return. Maxim Sobolev 9229538Sray * ---------------------------------------------------------------------------- 10229538Sray * 11229538Sray * $FreeBSD$ 12229538Sray * 13229538Sray */ 14229538Sray 15229538Sray#include <sys/disk.h> 16229538Sray#include <sys/endian.h> 17229538Sray#include <sys/param.h> 18229538Sray#include <sys/stat.h> 19229538Sray#include <sys/uio.h> 20229538Sray#include <netinet/in.h> 21229538Sray#include <err.h> 22229538Sray#include <fcntl.h> 23229538Sray#include <stdio.h> 24229538Sray#include <stdlib.h> 25229538Sray#include <string.h> 26229538Sray#include <unistd.h> 27229538Sray 28229538Sray#include <lzma.h> 29229538Sray 30229538Sray#define CLSTSIZE 16384 31229538Sray#define DEFAULT_SUFX ".ulzma" 32229538Sray 33229538Sray#define USED_BLOCKSIZE DEV_BSIZE 34229538Sray 35229538Sray#define CLOOP_MAGIC_LEN 128 36229538Sray/* Format L3.0, since we move to XZ API */ 37229538Sraystatic char CLOOP_MAGIC_START[] = 38229538Sray "#!/bin/sh\n" 39229538Sray "#L3.0\n" 40229538Sray "n=uncompress\n" 41229538Sray "m=geom_$n\n" 42229538Sray "(kldstat -m $m 2>&-||kldload $m)>&-&&" 43229538Sray "mount_cd9660 /dev/`mdconfig -af $0`.$n $1\n" 44229538Sray "exit $?\n"; 45229538Sray 46229538Sraystatic char *readblock(int, char *, u_int32_t); 47229538Sraystatic void usage(void); 48229538Sraystatic void *safe_malloc(size_t); 49229538Sraystatic void cleanup(void); 50229538Sray 51229538Sraystatic char *cleanfile = NULL; 52229538Sray 53229538Srayint main(int argc, char **argv) 54229538Sray{ 55229538Sray char *iname, *oname, *obuf, *ibuf; 56229538Sray int fdr, fdw, i, opt, verbose, tmp; 57229538Sray struct iovec iov[2]; 58229538Sray struct stat sb; 59229538Sray uint32_t destlen; 60229538Sray uint64_t offset; 61229538Sray uint64_t *toc; 62229538Sray lzma_filter filters[2]; 63229538Sray lzma_options_lzma opt_lzma; 64229538Sray lzma_ret ret; 65229538Sray lzma_stream strm = LZMA_STREAM_INIT; 66229538Sray struct cloop_header { 67229538Sray char magic[CLOOP_MAGIC_LEN]; /* cloop magic */ 68229538Sray uint32_t blksz; /* block size */ 69229538Sray uint32_t nblocks; /* number of blocks */ 70229538Sray } hdr; 71229538Sray 72229538Sray memset(&hdr, 0, sizeof(hdr)); 73229538Sray hdr.blksz = CLSTSIZE; 74229538Sray strcpy(hdr.magic, CLOOP_MAGIC_START); 75229538Sray oname = NULL; 76229538Sray verbose = 0; 77229538Sray 78229538Sray while((opt = getopt(argc, argv, "o:s:v")) != -1) { 79229538Sray switch(opt) { 80229538Sray case 'o': 81229538Sray oname = optarg; 82229538Sray break; 83229538Sray 84229538Sray case 's': 85229538Sray tmp = atoi(optarg); 86229538Sray if (tmp <= 0) { 87229538Sray errx(1, 88229538Sray "invalid cluster size specified: %s", 89229538Sray optarg); 90229538Sray /* Not reached */ 91229538Sray } 92229538Sray if (tmp % USED_BLOCKSIZE != 0) { 93229538Sray errx(1, 94229538Sray "cluster size should be multiple of %d", 95229538Sray USED_BLOCKSIZE); 96229538Sray /* Not reached */ 97229538Sray } 98229538Sray if ( tmp > MAXPHYS) { 99229538Sray errx(1, "cluster size is too large"); 100229538Sray /* Not reached */ 101229538Sray } 102229538Sray hdr.blksz = tmp; 103229538Sray break; 104229538Sray 105229538Sray case 'v': 106229538Sray verbose = 1; 107229538Sray break; 108229538Sray 109229538Sray default: 110229538Sray usage(); 111229538Sray /* Not reached */ 112229538Sray } 113229538Sray } 114229538Sray argc -= optind; 115229538Sray argv += optind; 116229538Sray 117229538Sray if (argc != 1) { 118229538Sray usage(); 119229538Sray /* Not reached */ 120229538Sray } 121229538Sray 122229538Sray iname = argv[0]; 123229538Sray if (oname == NULL) { 124229538Sray asprintf(&oname, "%s%s", iname, DEFAULT_SUFX); 125229538Sray if (oname == NULL) { 126229538Sray err(1, "can't allocate memory"); 127229538Sray /* Not reached */ 128229538Sray } 129229538Sray } 130229538Sray 131229538Sray obuf = safe_malloc(hdr.blksz*2); 132229538Sray ibuf = safe_malloc(hdr.blksz); 133229538Sray 134229538Sray signal(SIGHUP, exit); 135229538Sray signal(SIGINT, exit); 136229538Sray signal(SIGTERM, exit); 137229538Sray signal(SIGXCPU, exit); 138229538Sray signal(SIGXFSZ, exit); 139229538Sray atexit(cleanup); 140229538Sray 141229538Sray fdr = open(iname, O_RDONLY); 142229538Sray if (fdr < 0) { 143229538Sray err(1, "open(%s)", iname); 144229538Sray /* Not reached */ 145229538Sray } 146229538Sray if (fstat(fdr, &sb) != 0) { 147229538Sray err(1, "fstat(%s)", iname); 148229538Sray /* Not reached */ 149229538Sray } 150229538Sray if (S_ISCHR(sb.st_mode)) { 151229538Sray off_t ms; 152229538Sray 153229538Sray if (ioctl(fdr, DIOCGMEDIASIZE, &ms) < 0) { 154229538Sray err(1, "ioctl(DIOCGMEDIASIZE)"); 155229538Sray /* Not reached */ 156229538Sray } 157229538Sray sb.st_size = ms; 158229538Sray } else if (!S_ISREG(sb.st_mode)) { 159229538Sray fprintf(stderr, 160229538Sray "%s: not a character device or regular file\n", 161229538Sray iname); 162229538Sray exit(1); 163229538Sray } 164229538Sray hdr.nblocks = sb.st_size / hdr.blksz; 165229538Sray if ((sb.st_size % hdr.blksz) != 0) { 166229538Sray if (verbose != 0) 167229538Sray fprintf(stderr, "file size is not multiple " 168229538Sray "of %d, padding data\n", hdr.blksz); 169229538Sray hdr.nblocks++; 170229538Sray } 171229538Sray toc = safe_malloc((hdr.nblocks + 1) * sizeof(*toc)); 172229538Sray 173229538Sray fdw = open(oname, O_WRONLY | O_TRUNC | O_CREAT, 174229538Sray S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 175229538Sray if (fdw < 0) { 176229538Sray err(1, "open(%s)", oname); 177229538Sray /* Not reached */ 178229538Sray } 179229538Sray cleanfile = oname; 180229538Sray 181229538Sray /* 182229538Sray * Prepare header that we will write later when we have index ready. 183229538Sray */ 184229538Sray iov[0].iov_base = (char *)&hdr; 185229538Sray iov[0].iov_len = sizeof(hdr); 186229538Sray iov[1].iov_base = (char *)toc; 187229538Sray iov[1].iov_len = (hdr.nblocks + 1) * sizeof(*toc); 188229538Sray offset = iov[0].iov_len + iov[1].iov_len; 189229538Sray 190229538Sray /* Reserve space for header */ 191229538Sray lseek(fdw, offset, SEEK_SET); 192229538Sray 193229538Sray if (verbose != 0) 194229538Sray fprintf(stderr, "data size %ju bytes, number of clusters " 195229538Sray "%u, index length %zu bytes\n", sb.st_size, 196229538Sray hdr.nblocks, iov[1].iov_len); 197229538Sray 198229538Sray /* Init lzma encoder */ 199229538Sray if (lzma_lzma_preset(&opt_lzma, LZMA_PRESET_DEFAULT)) 200229538Sray errx(1, "Error loading LZMA preset"); 201229538Sray 202229538Sray filters[0].id = LZMA_FILTER_LZMA2; 203229538Sray filters[0].options = &opt_lzma; 204229538Sray filters[1].id = LZMA_VLI_UNKNOWN; 205229538Sray 206229538Sray for(i = 0; i == 0 || ibuf != NULL; i++) { 207229538Sray ibuf = readblock(fdr, ibuf, hdr.blksz); 208229538Sray if (ibuf != NULL) { 209229538Sray destlen = hdr.blksz*2; 210229538Sray 211229538Sray ret = lzma_stream_encoder(&strm, filters, 212229538Sray LZMA_CHECK_CRC32); 213229538Sray if (ret != LZMA_OK) { 214229538Sray if (ret == LZMA_MEMLIMIT_ERROR) 215229538Sray errx(1, "can't compress data: " 216229538Sray "LZMA_MEMLIMIT_ERROR"); 217229538Sray 218229538Sray errx(1, "can't compress data: " 219229538Sray "LZMA compressor ERROR"); 220229538Sray } 221229538Sray 222229538Sray strm.next_in = ibuf; 223229538Sray strm.avail_in = hdr.blksz; 224229538Sray strm.next_out = obuf; 225229538Sray strm.avail_out = hdr.blksz*2; 226229538Sray 227229538Sray ret = lzma_code(&strm, LZMA_FINISH); 228229538Sray 229229538Sray if (ret != LZMA_STREAM_END) { 230229538Sray /* Error */ 231229538Sray errx(1, "lzma_code FINISH failed, code=%d, " 232229538Sray "pos(in=%zd, out=%zd)", 233229538Sray ret, 234229538Sray (hdr.blksz - strm.avail_in), 235229538Sray (hdr.blksz*2 - strm.avail_out)); 236229538Sray } 237229538Sray 238229538Sray destlen -= strm.avail_out; 239229538Sray 240229538Sray lzma_end(&strm); 241229538Sray 242229538Sray if (verbose != 0) 243229538Sray fprintf(stderr, "cluster #%d, in %u bytes, " 244229538Sray "out %u bytes\n", i, hdr.blksz, destlen); 245229538Sray } else { 246229538Sray destlen = USED_BLOCKSIZE - (offset % USED_BLOCKSIZE); 247229538Sray memset(obuf, 0, destlen); 248229538Sray if (verbose != 0) 249229538Sray fprintf(stderr, "padding data with %u bytes" 250229538Sray " so that file size is multiple of %d\n", 251229538Sray destlen, 252229538Sray USED_BLOCKSIZE); 253229538Sray } 254229538Sray if (write(fdw, obuf, destlen) < 0) { 255229538Sray err(1, "write(%s)", oname); 256229538Sray /* Not reached */ 257229538Sray } 258229538Sray toc[i] = htobe64(offset); 259229538Sray offset += destlen; 260229538Sray } 261229538Sray close(fdr); 262229538Sray 263229538Sray if (verbose != 0) 264229538Sray fprintf(stderr, "compressed data to %ju bytes, saved %lld " 265229538Sray "bytes, %.2f%% decrease.\n", offset, 266229538Sray (long long)(sb.st_size - offset), 267229538Sray 100.0 * (long long)(sb.st_size - offset) / 268229538Sray (float)sb.st_size); 269229538Sray 270229538Sray /* Convert to big endian */ 271229538Sray hdr.blksz = htonl(hdr.blksz); 272229538Sray hdr.nblocks = htonl(hdr.nblocks); 273229538Sray /* Write headers into pre-allocated space */ 274229538Sray lseek(fdw, 0, SEEK_SET); 275229538Sray if (writev(fdw, iov, 2) < 0) { 276229538Sray err(1, "writev(%s)", oname); 277229538Sray /* Not reached */ 278229538Sray } 279229538Sray cleanfile = NULL; 280229538Sray close(fdw); 281229538Sray 282229538Sray exit(0); 283229538Sray} 284229538Sray 285229538Sraystatic char * 286229538Srayreadblock(int fd, char *ibuf, u_int32_t clstsize) 287229538Sray{ 288229538Sray int numread; 289229538Sray 290229538Sray bzero(ibuf, clstsize); 291229538Sray numread = read(fd, ibuf, clstsize); 292229538Sray if (numread < 0) { 293229538Sray err(1, "read() failed"); 294229538Sray /* Not reached */ 295229538Sray } 296229538Sray if (numread == 0) { 297229538Sray return NULL; 298229538Sray } 299229538Sray return ibuf; 300229538Sray} 301229538Sray 302229538Sraystatic void 303229538Srayusage(void) 304229538Sray{ 305229538Sray 306229538Sray fprintf(stderr, "usage: mkulzma [-v] [-o outfile] [-s cluster_size] " 307229538Sray "infile\n"); 308229538Sray exit(1); 309229538Sray} 310229538Sray 311229538Sraystatic void * 312229538Sraysafe_malloc(size_t size) 313229538Sray{ 314229538Sray void *retval; 315229538Sray 316229538Sray retval = malloc(size); 317229538Sray if (retval == NULL) { 318229538Sray err(1, "can't allocate memory"); 319229538Sray /* Not reached */ 320229538Sray } 321229538Sray return retval; 322229538Sray} 323229538Sray 324229538Sraystatic void 325229538Sraycleanup(void) 326229538Sray{ 327229538Sray 328229538Sray if (cleanfile != NULL) 329229538Sray unlink(cleanfile); 330229538Sray} 331