11de7b4b8SPedro F. Giffuni /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 31de7b4b8SPedro F. Giffuni * 48f8cb840SMaxim Sobolev * Copyright (c) 2004-2016 Maxim Sobolev <sobomax@FreeBSD.org> 58f8cb840SMaxim Sobolev * All rights reserved. 67f4caa8cSMaxim Sobolev * 78f8cb840SMaxim Sobolev * Redistribution and use in source and binary forms, with or without 88f8cb840SMaxim Sobolev * modification, are permitted provided that the following conditions 98f8cb840SMaxim Sobolev * are met: 108f8cb840SMaxim Sobolev * 1. Redistributions of source code must retain the above copyright 118f8cb840SMaxim Sobolev * notice, this list of conditions and the following disclaimer. 128f8cb840SMaxim Sobolev * 2. Redistributions in binary form must reproduce the above copyright 138f8cb840SMaxim Sobolev * notice, this list of conditions and the following disclaimer in the 148f8cb840SMaxim Sobolev * documentation and/or other materials provided with the distribution. 158f8cb840SMaxim Sobolev * 168f8cb840SMaxim Sobolev * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 178f8cb840SMaxim Sobolev * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 188f8cb840SMaxim Sobolev * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 198f8cb840SMaxim Sobolev * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 208f8cb840SMaxim Sobolev * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 218f8cb840SMaxim Sobolev * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 228f8cb840SMaxim Sobolev * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 238f8cb840SMaxim Sobolev * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 248f8cb840SMaxim Sobolev * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 258f8cb840SMaxim Sobolev * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 268f8cb840SMaxim Sobolev * SUCH DAMAGE. 277f4caa8cSMaxim Sobolev */ 287f4caa8cSMaxim Sobolev 298f8cb840SMaxim Sobolev #include <sys/cdefs.h> 308f8cb840SMaxim Sobolev __FBSDID("$FreeBSD$"); 318f8cb840SMaxim Sobolev 327f4caa8cSMaxim Sobolev #include <sys/types.h> 337f4caa8cSMaxim Sobolev #include <sys/endian.h> 347f4caa8cSMaxim Sobolev #include <sys/param.h> 354fc55e3eSMaxim Sobolev #include <sys/sysctl.h> 367f4caa8cSMaxim Sobolev #include <sys/stat.h> 377f4caa8cSMaxim Sobolev #include <sys/uio.h> 387f4caa8cSMaxim Sobolev #include <netinet/in.h> 394fc55e3eSMaxim Sobolev #include <assert.h> 408f8cb840SMaxim Sobolev #include <ctype.h> 417f4caa8cSMaxim Sobolev #include <err.h> 427f4caa8cSMaxim Sobolev #include <fcntl.h> 434fc55e3eSMaxim Sobolev #include <pthread.h> 447f4caa8cSMaxim Sobolev #include <signal.h> 454fc55e3eSMaxim Sobolev #include <stdint.h> 467f4caa8cSMaxim Sobolev #include <stdio.h> 477f4caa8cSMaxim Sobolev #include <stdlib.h> 487f4caa8cSMaxim Sobolev #include <string.h> 497f4caa8cSMaxim Sobolev #include <unistd.h> 507f4caa8cSMaxim Sobolev 518f8cb840SMaxim Sobolev #include "mkuzip.h" 528f8cb840SMaxim Sobolev #include "mkuz_cloop.h" 538f8cb840SMaxim Sobolev #include "mkuz_blockcache.h" 548f8cb840SMaxim Sobolev #include "mkuz_lzma.h" 55eefd8f96SConrad Meyer #include "mkuz_zlib.h" 56eefd8f96SConrad Meyer #include "mkuz_zstd.h" 574fc55e3eSMaxim Sobolev #include "mkuz_blk.h" 584fc55e3eSMaxim Sobolev #include "mkuz_cfg.h" 594fc55e3eSMaxim Sobolev #include "mkuz_conveyor.h" 604fc55e3eSMaxim Sobolev #include "mkuz_format.h" 614fc55e3eSMaxim Sobolev #include "mkuz_fqueue.h" 624fc55e3eSMaxim Sobolev #include "mkuz_time.h" 63bc3b2c55SMaxim Sobolev #include "mkuz_insize.h" 648f8cb840SMaxim Sobolev 658f8cb840SMaxim Sobolev #define DEFAULT_CLSTSIZE 16384 668f8cb840SMaxim Sobolev 67eefd8f96SConrad Meyer enum UZ_ALGORITHM { 68eefd8f96SConrad Meyer UZ_ZLIB = 0, 69eefd8f96SConrad Meyer UZ_LZMA, 70eefd8f96SConrad Meyer UZ_ZSTD, 71eefd8f96SConrad Meyer UZ_INVALID 728f8cb840SMaxim Sobolev }; 738f8cb840SMaxim Sobolev 74eefd8f96SConrad Meyer static const struct mkuz_format uzip_fmts[] = { 75eefd8f96SConrad Meyer [UZ_ZLIB] = { 76eefd8f96SConrad Meyer .option = "zlib", 77eefd8f96SConrad Meyer .magic = CLOOP_MAGIC_ZLIB, 78eefd8f96SConrad Meyer .default_sufx = DEFAULT_SUFX_ZLIB, 79eefd8f96SConrad Meyer .f_compress_bound = mkuz_zlib_cbound, 80eefd8f96SConrad Meyer .f_init = mkuz_zlib_init, 81eefd8f96SConrad Meyer .f_compress = mkuz_zlib_compress, 82eefd8f96SConrad Meyer }, 83eefd8f96SConrad Meyer [UZ_LZMA] = { 84eefd8f96SConrad Meyer .option = "lzma", 858f8cb840SMaxim Sobolev .magic = CLOOP_MAGIC_LZMA, 868f8cb840SMaxim Sobolev .default_sufx = DEFAULT_SUFX_LZMA, 87eefd8f96SConrad Meyer .f_compress_bound = mkuz_lzma_cbound, 88eefd8f96SConrad Meyer .f_init = mkuz_lzma_init, 89eefd8f96SConrad Meyer .f_compress = mkuz_lzma_compress, 90eefd8f96SConrad Meyer }, 91eefd8f96SConrad Meyer [UZ_ZSTD] = { 92eefd8f96SConrad Meyer .option = "zstd", 93eefd8f96SConrad Meyer .magic = CLOOP_MAGIC_ZSTD, 94eefd8f96SConrad Meyer .default_sufx = DEFAULT_SUFX_ZSTD, 95eefd8f96SConrad Meyer .f_compress_bound = mkuz_zstd_cbound, 96eefd8f96SConrad Meyer .f_init = mkuz_zstd_init, 97eefd8f96SConrad Meyer .f_compress = mkuz_zstd_compress, 98eefd8f96SConrad Meyer }, 998f8cb840SMaxim Sobolev }; 1007f4caa8cSMaxim Sobolev 1014fc55e3eSMaxim Sobolev static struct mkuz_blk *readblock(int, u_int32_t); 102*1a7ac2bdSAlfonso Gregory static void usage(void) __dead2; 1037f4caa8cSMaxim Sobolev static void cleanup(void); 1047f4caa8cSMaxim Sobolev 1057f4caa8cSMaxim Sobolev static char *cleanfile = NULL; 1067f4caa8cSMaxim Sobolev 1074fc55e3eSMaxim Sobolev static int 1084fc55e3eSMaxim Sobolev cmp_blkno(const struct mkuz_blk *bp, void *p) 1094fc55e3eSMaxim Sobolev { 1104fc55e3eSMaxim Sobolev uint32_t *ap; 1114fc55e3eSMaxim Sobolev 1124fc55e3eSMaxim Sobolev ap = (uint32_t *)p; 1134fc55e3eSMaxim Sobolev 1144fc55e3eSMaxim Sobolev return (bp->info.blkno == *ap); 1154fc55e3eSMaxim Sobolev } 1164fc55e3eSMaxim Sobolev 1177f4caa8cSMaxim Sobolev int main(int argc, char **argv) 1187f4caa8cSMaxim Sobolev { 1194fc55e3eSMaxim Sobolev struct mkuz_cfg cfs; 120bc3b2c55SMaxim Sobolev char *oname; 1217f4caa8cSMaxim Sobolev uint64_t *toc; 1224fc55e3eSMaxim Sobolev int i, io, opt, tmp; 12362ee4b69SMaxim Sobolev struct { 12462ee4b69SMaxim Sobolev int en; 12562ee4b69SMaxim Sobolev FILE *f; 12662ee4b69SMaxim Sobolev } summary; 1277f4caa8cSMaxim Sobolev struct iovec iov[2]; 1288f8cb840SMaxim Sobolev uint64_t offset, last_offset; 1298f8cb840SMaxim Sobolev struct cloop_header hdr; 1304fc55e3eSMaxim Sobolev struct mkuz_conveyor *cvp; 1314fc55e3eSMaxim Sobolev struct mkuz_blk_info *chit; 1320ce59aa8SAlan Somers size_t ncpusz, ncpu, magiclen; 1334fc55e3eSMaxim Sobolev double st, et; 134eefd8f96SConrad Meyer enum UZ_ALGORITHM comp_alg; 135eefd8f96SConrad Meyer int comp_level; 1364fc55e3eSMaxim Sobolev 1374fc55e3eSMaxim Sobolev st = getdtime(); 1384fc55e3eSMaxim Sobolev 1394fc55e3eSMaxim Sobolev ncpusz = sizeof(size_t); 1404fc55e3eSMaxim Sobolev if (sysctlbyname("hw.ncpu", &ncpu, &ncpusz, NULL, 0) < 0) { 1414fc55e3eSMaxim Sobolev ncpu = 1; 1424fc55e3eSMaxim Sobolev } else if (ncpu > MAX_WORKERS_AUTO) { 1434fc55e3eSMaxim Sobolev ncpu = MAX_WORKERS_AUTO; 1444fc55e3eSMaxim Sobolev } 1457f4caa8cSMaxim Sobolev 1467f4caa8cSMaxim Sobolev memset(&hdr, 0, sizeof(hdr)); 1474fc55e3eSMaxim Sobolev cfs.blksz = DEFAULT_CLSTSIZE; 1487f4caa8cSMaxim Sobolev oname = NULL; 1494fc55e3eSMaxim Sobolev cfs.verbose = 0; 1504fc55e3eSMaxim Sobolev cfs.no_zcomp = 0; 1514fc55e3eSMaxim Sobolev cfs.en_dedup = 0; 15262ee4b69SMaxim Sobolev summary.en = 0; 15362ee4b69SMaxim Sobolev summary.f = stderr; 154eefd8f96SConrad Meyer comp_alg = UZ_ZLIB; 155eefd8f96SConrad Meyer comp_level = USE_DEFAULT_LEVEL; 1564fc55e3eSMaxim Sobolev cfs.nworkers = ncpu; 1574fc55e3eSMaxim Sobolev struct mkuz_blk *iblk, *oblk; 1587f4caa8cSMaxim Sobolev 159eefd8f96SConrad Meyer while((opt = getopt(argc, argv, "A:C:o:s:vZdLSj:")) != -1) { 1607f4caa8cSMaxim Sobolev switch(opt) { 161eefd8f96SConrad Meyer case 'A': 162eefd8f96SConrad Meyer for (tmp = UZ_ZLIB; tmp < UZ_INVALID; tmp++) { 163eefd8f96SConrad Meyer if (strcmp(uzip_fmts[tmp].option, optarg) == 0) 164eefd8f96SConrad Meyer break; 165eefd8f96SConrad Meyer } 166eefd8f96SConrad Meyer if (tmp == UZ_INVALID) 167eefd8f96SConrad Meyer errx(1, "invalid algorithm specified: %s", 168eefd8f96SConrad Meyer optarg); 169eefd8f96SConrad Meyer /* Not reached */ 170eefd8f96SConrad Meyer comp_alg = tmp; 171eefd8f96SConrad Meyer break; 172eefd8f96SConrad Meyer case 'C': 173eefd8f96SConrad Meyer comp_level = atoi(optarg); 174eefd8f96SConrad Meyer break; 1757f4caa8cSMaxim Sobolev case 'o': 1767f4caa8cSMaxim Sobolev oname = optarg; 1777f4caa8cSMaxim Sobolev break; 1787f4caa8cSMaxim Sobolev 1797f4caa8cSMaxim Sobolev case 's': 1807f4caa8cSMaxim Sobolev tmp = atoi(optarg); 1817f4caa8cSMaxim Sobolev if (tmp <= 0) { 1827f4caa8cSMaxim Sobolev errx(1, "invalid cluster size specified: %s", 1837f4caa8cSMaxim Sobolev optarg); 1847f4caa8cSMaxim Sobolev /* Not reached */ 1857f4caa8cSMaxim Sobolev } 1864fc55e3eSMaxim Sobolev cfs.blksz = tmp; 1877f4caa8cSMaxim Sobolev break; 1887f4caa8cSMaxim Sobolev 1897f4caa8cSMaxim Sobolev case 'v': 1904fc55e3eSMaxim Sobolev cfs.verbose = 1; 1917f4caa8cSMaxim Sobolev break; 1927f4caa8cSMaxim Sobolev 1938f8cb840SMaxim Sobolev case 'Z': 1944fc55e3eSMaxim Sobolev cfs.no_zcomp = 1; 1958f8cb840SMaxim Sobolev break; 1968f8cb840SMaxim Sobolev 1978f8cb840SMaxim Sobolev case 'd': 1984fc55e3eSMaxim Sobolev cfs.en_dedup = 1; 1998f8cb840SMaxim Sobolev break; 2008f8cb840SMaxim Sobolev 2018f8cb840SMaxim Sobolev case 'L': 202eefd8f96SConrad Meyer comp_alg = UZ_LZMA; 2038f8cb840SMaxim Sobolev break; 2048f8cb840SMaxim Sobolev 205d83e0778SMaxim Sobolev case 'S': 20662ee4b69SMaxim Sobolev summary.en = 1; 20762ee4b69SMaxim Sobolev summary.f = stdout; 208d83e0778SMaxim Sobolev break; 209d83e0778SMaxim Sobolev 2104fc55e3eSMaxim Sobolev case 'j': 2114fc55e3eSMaxim Sobolev tmp = atoi(optarg); 2124fc55e3eSMaxim Sobolev if (tmp <= 0) { 2134fc55e3eSMaxim Sobolev errx(1, "invalid number of compression threads" 2144fc55e3eSMaxim Sobolev " specified: %s", optarg); 2154fc55e3eSMaxim Sobolev /* Not reached */ 2164fc55e3eSMaxim Sobolev } 2174fc55e3eSMaxim Sobolev cfs.nworkers = tmp; 2184fc55e3eSMaxim Sobolev break; 2194fc55e3eSMaxim Sobolev 2207f4caa8cSMaxim Sobolev default: 2217f4caa8cSMaxim Sobolev usage(); 2227f4caa8cSMaxim Sobolev /* Not reached */ 2237f4caa8cSMaxim Sobolev } 2247f4caa8cSMaxim Sobolev } 2257f4caa8cSMaxim Sobolev argc -= optind; 2267f4caa8cSMaxim Sobolev argv += optind; 2277f4caa8cSMaxim Sobolev 2287f4caa8cSMaxim Sobolev if (argc != 1) { 2297f4caa8cSMaxim Sobolev usage(); 2307f4caa8cSMaxim Sobolev /* Not reached */ 2317f4caa8cSMaxim Sobolev } 2327f4caa8cSMaxim Sobolev 233eefd8f96SConrad Meyer cfs.handler = &uzip_fmts[comp_alg]; 234eefd8f96SConrad Meyer 2350ce59aa8SAlan Somers magiclen = strlcpy(hdr.magic, cfs.handler->magic, sizeof(hdr.magic)); 2360ce59aa8SAlan Somers assert(magiclen < sizeof(hdr.magic)); 2378f8cb840SMaxim Sobolev 2384fc55e3eSMaxim Sobolev if (cfs.en_dedup != 0) { 239eefd8f96SConrad Meyer /* 240eefd8f96SConrad Meyer * Dedupe requires a version 3 format. Don't downgrade newer 241eefd8f96SConrad Meyer * formats. 242eefd8f96SConrad Meyer */ 243eefd8f96SConrad Meyer if (hdr.magic[CLOOP_OFS_VERSN] == CLOOP_MAJVER_2) 2448f8cb840SMaxim Sobolev hdr.magic[CLOOP_OFS_VERSN] = CLOOP_MAJVER_3; 2458f8cb840SMaxim Sobolev hdr.magic[CLOOP_OFS_COMPR] = 2468f8cb840SMaxim Sobolev tolower(hdr.magic[CLOOP_OFS_COMPR]); 2478f8cb840SMaxim Sobolev } 2488f8cb840SMaxim Sobolev 249eefd8f96SConrad Meyer if (cfs.blksz % DEV_BSIZE != 0) 250eefd8f96SConrad Meyer errx(1, "cluster size should be multiple of %d", DEV_BSIZE); 251eefd8f96SConrad Meyer 252eefd8f96SConrad Meyer cfs.cbound_blksz = cfs.handler->f_compress_bound(cfs.blksz); 253eefd8f96SConrad Meyer if (cfs.cbound_blksz > MAXPHYS) 254eefd8f96SConrad Meyer errx(1, "maximal compressed cluster size %zu greater than MAXPHYS %zu", 255eefd8f96SConrad Meyer cfs.cbound_blksz, (size_t)MAXPHYS); 256eefd8f96SConrad Meyer 2571291d48fSJohn Baldwin cfs.handler->f_init(&comp_level); 258eefd8f96SConrad Meyer cfs.comp_level = comp_level; 2598f8cb840SMaxim Sobolev 260bc3b2c55SMaxim Sobolev cfs.iname = argv[0]; 2617f4caa8cSMaxim Sobolev if (oname == NULL) { 262bc3b2c55SMaxim Sobolev asprintf(&oname, "%s%s", cfs.iname, cfs.handler->default_sufx); 2637f4caa8cSMaxim Sobolev if (oname == NULL) { 2647f4caa8cSMaxim Sobolev err(1, "can't allocate memory"); 2657f4caa8cSMaxim Sobolev /* Not reached */ 2667f4caa8cSMaxim Sobolev } 2677f4caa8cSMaxim Sobolev } 2687f4caa8cSMaxim Sobolev 2697f4caa8cSMaxim Sobolev signal(SIGHUP, exit); 2707f4caa8cSMaxim Sobolev signal(SIGINT, exit); 2717f4caa8cSMaxim Sobolev signal(SIGTERM, exit); 2727f4caa8cSMaxim Sobolev signal(SIGXCPU, exit); 2737f4caa8cSMaxim Sobolev signal(SIGXFSZ, exit); 2747f4caa8cSMaxim Sobolev atexit(cleanup); 2757f4caa8cSMaxim Sobolev 276bc3b2c55SMaxim Sobolev cfs.fdr = open(cfs.iname, O_RDONLY); 2774fc55e3eSMaxim Sobolev if (cfs.fdr < 0) { 278bc3b2c55SMaxim Sobolev err(1, "open(%s)", cfs.iname); 2797f4caa8cSMaxim Sobolev /* Not reached */ 2807f4caa8cSMaxim Sobolev } 281bc3b2c55SMaxim Sobolev cfs.isize = mkuz_get_insize(&cfs); 282bc3b2c55SMaxim Sobolev if (cfs.isize < 0) { 283bc3b2c55SMaxim Sobolev errx(1, "can't determine input image size"); 28427d0a1a4SMax Khon /* Not reached */ 28527d0a1a4SMax Khon } 286bc3b2c55SMaxim Sobolev hdr.nblocks = cfs.isize / cfs.blksz; 287bc3b2c55SMaxim Sobolev if ((cfs.isize % cfs.blksz) != 0) { 2884fc55e3eSMaxim Sobolev if (cfs.verbose != 0) 2890b99ac63SMaxim Sobolev fprintf(stderr, "file size is not multiple " 2904fc55e3eSMaxim Sobolev "of %d, padding data\n", cfs.blksz); 2910b99ac63SMaxim Sobolev hdr.nblocks++; 2920b99ac63SMaxim Sobolev } 2938f8cb840SMaxim Sobolev toc = mkuz_safe_malloc((hdr.nblocks + 1) * sizeof(*toc)); 2947f4caa8cSMaxim Sobolev 295eefd8f96SConrad Meyer /* 296eefd8f96SConrad Meyer * Initialize last+1 entry with non-heap trash. If final padding is 297eefd8f96SConrad Meyer * added later, it may or may not be overwritten with an offset 298eefd8f96SConrad Meyer * representing the length of the final compressed block. If not, 299eefd8f96SConrad Meyer * initialize to a defined value. 300eefd8f96SConrad Meyer */ 301eefd8f96SConrad Meyer toc[hdr.nblocks] = 0; 302eefd8f96SConrad Meyer 3034fc55e3eSMaxim Sobolev cfs.fdw = open(oname, (cfs.en_dedup ? O_RDWR : O_WRONLY) | O_TRUNC | O_CREAT, 3045cf3bf70SMax Khon S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 3054fc55e3eSMaxim Sobolev if (cfs.fdw < 0) { 306d72d8f53SPawel Jakub Dawidek err(1, "open(%s)", oname); 3077f4caa8cSMaxim Sobolev /* Not reached */ 3087f4caa8cSMaxim Sobolev } 3097f4caa8cSMaxim Sobolev cleanfile = oname; 3107f4caa8cSMaxim Sobolev 3117f4caa8cSMaxim Sobolev /* Prepare header that we will write later when we have index ready. */ 3127f4caa8cSMaxim Sobolev iov[0].iov_base = (char *)&hdr; 3137f4caa8cSMaxim Sobolev iov[0].iov_len = sizeof(hdr); 3147f4caa8cSMaxim Sobolev iov[1].iov_base = (char *)toc; 3157f4caa8cSMaxim Sobolev iov[1].iov_len = (hdr.nblocks + 1) * sizeof(*toc); 3167f4caa8cSMaxim Sobolev offset = iov[0].iov_len + iov[1].iov_len; 3177f4caa8cSMaxim Sobolev 3187f4caa8cSMaxim Sobolev /* Reserve space for header */ 3194fc55e3eSMaxim Sobolev lseek(cfs.fdw, offset, SEEK_SET); 3207f4caa8cSMaxim Sobolev 3214fc55e3eSMaxim Sobolev if (cfs.verbose != 0) { 322ed9302fdSMaxim Sobolev fprintf(stderr, "data size %ju bytes, number of clusters " 323bc3b2c55SMaxim Sobolev "%u, index length %zu bytes\n", cfs.isize, 3240b99ac63SMaxim Sobolev hdr.nblocks, iov[1].iov_len); 3254fc55e3eSMaxim Sobolev } 3264fc55e3eSMaxim Sobolev 3274fc55e3eSMaxim Sobolev cvp = mkuz_conveyor_ctor(&cfs); 3287f4caa8cSMaxim Sobolev 3298f8cb840SMaxim Sobolev last_offset = 0; 3304fc55e3eSMaxim Sobolev iblk = oblk = NULL; 3314fc55e3eSMaxim Sobolev for(i = io = 0; iblk != MKUZ_BLK_EOF; i++) { 3324fc55e3eSMaxim Sobolev iblk = readblock(cfs.fdr, cfs.blksz); 3334fc55e3eSMaxim Sobolev mkuz_fqueue_enq(cvp->wrk_queue, iblk); 3344fc55e3eSMaxim Sobolev if (iblk != MKUZ_BLK_EOF && 3354fc55e3eSMaxim Sobolev (i < (cfs.nworkers * ITEMS_PER_WORKER))) { 3364fc55e3eSMaxim Sobolev continue; 3377f4caa8cSMaxim Sobolev } 3384fc55e3eSMaxim Sobolev drain: 3394fc55e3eSMaxim Sobolev oblk = mkuz_fqueue_deq_when(cvp->results, cmp_blkno, &io); 3404fc55e3eSMaxim Sobolev assert(oblk->info.blkno == (unsigned)io); 3414fc55e3eSMaxim Sobolev oblk->info.offset = offset; 3424fc55e3eSMaxim Sobolev chit = NULL; 3434fc55e3eSMaxim Sobolev if (cfs.en_dedup != 0 && oblk->info.len > 0) { 3444fc55e3eSMaxim Sobolev chit = mkuz_blkcache_regblock(cfs.fdw, oblk); 3458f8cb840SMaxim Sobolev /* 3468f8cb840SMaxim Sobolev * There should be at least one non-empty block 3478f8cb840SMaxim Sobolev * between us and the backref'ed offset, otherwise 3488f8cb840SMaxim Sobolev * we won't be able to parse that sequence correctly 3498f8cb840SMaxim Sobolev * as it would be indistinguishible from another 3508f8cb840SMaxim Sobolev * empty block. 3518f8cb840SMaxim Sobolev */ 3528f8cb840SMaxim Sobolev if (chit != NULL && chit->offset == last_offset) { 3538f8cb840SMaxim Sobolev chit = NULL; 3548f8cb840SMaxim Sobolev } 3558f8cb840SMaxim Sobolev } 3568f8cb840SMaxim Sobolev if (chit != NULL) { 3574fc55e3eSMaxim Sobolev toc[io] = htobe64(chit->offset); 3584fc55e3eSMaxim Sobolev oblk->info.len = 0; 3598f8cb840SMaxim Sobolev } else { 3604fc55e3eSMaxim Sobolev if (oblk->info.len > 0 && write(cfs.fdw, oblk->data, 3614fc55e3eSMaxim Sobolev oblk->info.len) < 0) { 362d72d8f53SPawel Jakub Dawidek err(1, "write(%s)", oname); 3637f4caa8cSMaxim Sobolev /* Not reached */ 3647f4caa8cSMaxim Sobolev } 3654fc55e3eSMaxim Sobolev toc[io] = htobe64(offset); 3668f8cb840SMaxim Sobolev last_offset = offset; 3674fc55e3eSMaxim Sobolev offset += oblk->info.len; 3687f4caa8cSMaxim Sobolev } 3694fc55e3eSMaxim Sobolev if (cfs.verbose != 0) { 3708f8cb840SMaxim Sobolev fprintf(stderr, "cluster #%d, in %u bytes, " 3714fc55e3eSMaxim Sobolev "out len=%lu offset=%lu", io, cfs.blksz, 3724fc55e3eSMaxim Sobolev (u_long)oblk->info.len, (u_long)be64toh(toc[io])); 3738f8cb840SMaxim Sobolev if (chit != NULL) { 3748f8cb840SMaxim Sobolev fprintf(stderr, " (backref'ed to #%d)", 3758f8cb840SMaxim Sobolev chit->blkno); 3768f8cb840SMaxim Sobolev } 3778f8cb840SMaxim Sobolev fprintf(stderr, "\n"); 3784fc55e3eSMaxim Sobolev } 3794fc55e3eSMaxim Sobolev free(oblk); 3804fc55e3eSMaxim Sobolev io += 1; 3814fc55e3eSMaxim Sobolev if (iblk == MKUZ_BLK_EOF) { 3824fc55e3eSMaxim Sobolev if (io < i) 3834fc55e3eSMaxim Sobolev goto drain; 3844fc55e3eSMaxim Sobolev /* Last block, see if we need to add some padding */ 3854fc55e3eSMaxim Sobolev if ((offset % DEV_BSIZE) == 0) 3864fc55e3eSMaxim Sobolev continue; 3874fc55e3eSMaxim Sobolev oblk = mkuz_blk_ctor(DEV_BSIZE - (offset % DEV_BSIZE)); 3884fc55e3eSMaxim Sobolev oblk->info.blkno = io; 3894fc55e3eSMaxim Sobolev oblk->info.len = oblk->alen; 3904fc55e3eSMaxim Sobolev if (cfs.verbose != 0) { 3914fc55e3eSMaxim Sobolev fprintf(stderr, "padding data with %lu bytes " 3924fc55e3eSMaxim Sobolev "so that file size is multiple of %d\n", 3934fc55e3eSMaxim Sobolev (u_long)oblk->alen, DEV_BSIZE); 3944fc55e3eSMaxim Sobolev } 3954fc55e3eSMaxim Sobolev mkuz_fqueue_enq(cvp->results, oblk); 3964fc55e3eSMaxim Sobolev goto drain; 3978f8cb840SMaxim Sobolev } 3988f8cb840SMaxim Sobolev } 3997f4caa8cSMaxim Sobolev 4004fc55e3eSMaxim Sobolev close(cfs.fdr); 4014fc55e3eSMaxim Sobolev 4024fc55e3eSMaxim Sobolev if (cfs.verbose != 0 || summary.en != 0) { 4034fc55e3eSMaxim Sobolev et = getdtime(); 40462ee4b69SMaxim Sobolev fprintf(summary.f, "compressed data to %ju bytes, saved %lld " 4054fc55e3eSMaxim Sobolev "bytes, %.2f%% decrease, %.2f bytes/sec.\n", offset, 406bc3b2c55SMaxim Sobolev (long long)(cfs.isize - offset), 407bc3b2c55SMaxim Sobolev 100.0 * (long long)(cfs.isize - offset) / 408bc3b2c55SMaxim Sobolev (float)cfs.isize, (float)cfs.isize / (et - st)); 4094fc55e3eSMaxim Sobolev } 4107f4caa8cSMaxim Sobolev 4117f4caa8cSMaxim Sobolev /* Convert to big endian */ 4124fc55e3eSMaxim Sobolev hdr.blksz = htonl(cfs.blksz); 4137f4caa8cSMaxim Sobolev hdr.nblocks = htonl(hdr.nblocks); 4147f4caa8cSMaxim Sobolev /* Write headers into pre-allocated space */ 4154fc55e3eSMaxim Sobolev lseek(cfs.fdw, 0, SEEK_SET); 4164fc55e3eSMaxim Sobolev if (writev(cfs.fdw, iov, 2) < 0) { 417d72d8f53SPawel Jakub Dawidek err(1, "writev(%s)", oname); 4187f4caa8cSMaxim Sobolev /* Not reached */ 4197f4caa8cSMaxim Sobolev } 4207f4caa8cSMaxim Sobolev cleanfile = NULL; 4214fc55e3eSMaxim Sobolev close(cfs.fdw); 4227f4caa8cSMaxim Sobolev 4237f4caa8cSMaxim Sobolev exit(0); 4247f4caa8cSMaxim Sobolev } 4257f4caa8cSMaxim Sobolev 4264fc55e3eSMaxim Sobolev static struct mkuz_blk * 4274fc55e3eSMaxim Sobolev readblock(int fd, u_int32_t clstsize) 4280b99ac63SMaxim Sobolev { 4297f4caa8cSMaxim Sobolev int numread; 4304fc55e3eSMaxim Sobolev struct mkuz_blk *rval; 4314fc55e3eSMaxim Sobolev static int blockcnt; 4324fc55e3eSMaxim Sobolev off_t cpos; 4337f4caa8cSMaxim Sobolev 4344fc55e3eSMaxim Sobolev rval = mkuz_blk_ctor(clstsize); 4354fc55e3eSMaxim Sobolev 4364fc55e3eSMaxim Sobolev rval->info.blkno = blockcnt; 4374fc55e3eSMaxim Sobolev blockcnt += 1; 4384fc55e3eSMaxim Sobolev cpos = lseek(fd, 0, SEEK_CUR); 4394fc55e3eSMaxim Sobolev if (cpos < 0) { 4404fc55e3eSMaxim Sobolev err(1, "readblock: lseek() failed"); 4414fc55e3eSMaxim Sobolev /* Not reached */ 4424fc55e3eSMaxim Sobolev } 4434fc55e3eSMaxim Sobolev rval->info.offset = cpos; 4444fc55e3eSMaxim Sobolev 4454fc55e3eSMaxim Sobolev numread = read(fd, rval->data, clstsize); 4467f4caa8cSMaxim Sobolev if (numread < 0) { 4474fc55e3eSMaxim Sobolev err(1, "readblock: read() failed"); 4487f4caa8cSMaxim Sobolev /* Not reached */ 4497f4caa8cSMaxim Sobolev } 4507f4caa8cSMaxim Sobolev if (numread == 0) { 4514fc55e3eSMaxim Sobolev free(rval); 4524fc55e3eSMaxim Sobolev return MKUZ_BLK_EOF; 4537f4caa8cSMaxim Sobolev } 4544fc55e3eSMaxim Sobolev rval->info.len = numread; 4554fc55e3eSMaxim Sobolev return rval; 4567f4caa8cSMaxim Sobolev } 4577f4caa8cSMaxim Sobolev 4587f4caa8cSMaxim Sobolev static void 4590b99ac63SMaxim Sobolev usage(void) 4600b99ac63SMaxim Sobolev { 4617f4caa8cSMaxim Sobolev 462d83e0778SMaxim Sobolev fprintf(stderr, "usage: mkuzip [-vZdLS] [-o outfile] [-s cluster_size] " 4634fc55e3eSMaxim Sobolev "[-j ncompr] infile\n"); 4647f4caa8cSMaxim Sobolev exit(1); 4657f4caa8cSMaxim Sobolev } 4667f4caa8cSMaxim Sobolev 4678f8cb840SMaxim Sobolev void * 4688f8cb840SMaxim Sobolev mkuz_safe_malloc(size_t size) 4690b99ac63SMaxim Sobolev { 4707f4caa8cSMaxim Sobolev void *retval; 4717f4caa8cSMaxim Sobolev 4727f4caa8cSMaxim Sobolev retval = malloc(size); 4737f4caa8cSMaxim Sobolev if (retval == NULL) { 4747f4caa8cSMaxim Sobolev err(1, "can't allocate memory"); 4757f4caa8cSMaxim Sobolev /* Not reached */ 4767f4caa8cSMaxim Sobolev } 4777f4caa8cSMaxim Sobolev return retval; 4787f4caa8cSMaxim Sobolev } 4797f4caa8cSMaxim Sobolev 4804fc55e3eSMaxim Sobolev void * 4814fc55e3eSMaxim Sobolev mkuz_safe_zmalloc(size_t size) 4824fc55e3eSMaxim Sobolev { 4834fc55e3eSMaxim Sobolev void *retval; 4844fc55e3eSMaxim Sobolev 4854fc55e3eSMaxim Sobolev retval = mkuz_safe_malloc(size); 4864fc55e3eSMaxim Sobolev bzero(retval, size); 4874fc55e3eSMaxim Sobolev return retval; 4884fc55e3eSMaxim Sobolev } 4894fc55e3eSMaxim Sobolev 4907f4caa8cSMaxim Sobolev static void 4910b99ac63SMaxim Sobolev cleanup(void) 4920b99ac63SMaxim Sobolev { 4937f4caa8cSMaxim Sobolev 4947f4caa8cSMaxim Sobolev if (cleanfile != NULL) 4957f4caa8cSMaxim Sobolev unlink(cleanfile); 4967f4caa8cSMaxim Sobolev } 4978f8cb840SMaxim Sobolev 4984fc55e3eSMaxim Sobolev int 4994fc55e3eSMaxim Sobolev mkuz_memvcmp(const void *memory, unsigned char val, size_t size) 5008f8cb840SMaxim Sobolev { 5018f8cb840SMaxim Sobolev const u_char *mm; 5028f8cb840SMaxim Sobolev 5038f8cb840SMaxim Sobolev mm = (const u_char *)memory; 5048f8cb840SMaxim Sobolev return (*mm == val) && memcmp(mm, mm + 1, size - 1) == 0; 5058f8cb840SMaxim Sobolev } 506