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 297f4caa8cSMaxim Sobolev #include <sys/types.h> 307f4caa8cSMaxim Sobolev #include <sys/endian.h> 317f4caa8cSMaxim Sobolev #include <sys/param.h> 324fc55e3eSMaxim Sobolev #include <sys/sysctl.h> 337f4caa8cSMaxim Sobolev #include <sys/stat.h> 347f4caa8cSMaxim Sobolev #include <sys/uio.h> 357f4caa8cSMaxim Sobolev #include <netinet/in.h> 364fc55e3eSMaxim Sobolev #include <assert.h> 378f8cb840SMaxim Sobolev #include <ctype.h> 387f4caa8cSMaxim Sobolev #include <err.h> 397f4caa8cSMaxim Sobolev #include <fcntl.h> 404fc55e3eSMaxim Sobolev #include <pthread.h> 417f4caa8cSMaxim Sobolev #include <signal.h> 424fc55e3eSMaxim Sobolev #include <stdint.h> 437f4caa8cSMaxim Sobolev #include <stdio.h> 447f4caa8cSMaxim Sobolev #include <stdlib.h> 457f4caa8cSMaxim Sobolev #include <string.h> 467f4caa8cSMaxim Sobolev #include <unistd.h> 477f4caa8cSMaxim Sobolev 488f8cb840SMaxim Sobolev #include "mkuzip.h" 498f8cb840SMaxim Sobolev #include "mkuz_cloop.h" 508f8cb840SMaxim Sobolev #include "mkuz_blockcache.h" 518f8cb840SMaxim Sobolev #include "mkuz_lzma.h" 52eefd8f96SConrad Meyer #include "mkuz_zlib.h" 53eefd8f96SConrad Meyer #include "mkuz_zstd.h" 544fc55e3eSMaxim Sobolev #include "mkuz_blk.h" 554fc55e3eSMaxim Sobolev #include "mkuz_cfg.h" 564fc55e3eSMaxim Sobolev #include "mkuz_conveyor.h" 574fc55e3eSMaxim Sobolev #include "mkuz_format.h" 584fc55e3eSMaxim Sobolev #include "mkuz_fqueue.h" 594fc55e3eSMaxim Sobolev #include "mkuz_time.h" 60bc3b2c55SMaxim Sobolev #include "mkuz_insize.h" 618f8cb840SMaxim Sobolev 628f8cb840SMaxim Sobolev #define DEFAULT_CLSTSIZE 16384 638f8cb840SMaxim Sobolev 64eefd8f96SConrad Meyer enum UZ_ALGORITHM { 65eefd8f96SConrad Meyer UZ_ZLIB = 0, 66eefd8f96SConrad Meyer UZ_LZMA, 67eefd8f96SConrad Meyer UZ_ZSTD, 68eefd8f96SConrad Meyer UZ_INVALID 698f8cb840SMaxim Sobolev }; 708f8cb840SMaxim Sobolev 71eefd8f96SConrad Meyer static const struct mkuz_format uzip_fmts[] = { 72eefd8f96SConrad Meyer [UZ_ZLIB] = { 73eefd8f96SConrad Meyer .option = "zlib", 74eefd8f96SConrad Meyer .magic = CLOOP_MAGIC_ZLIB, 75eefd8f96SConrad Meyer .default_sufx = DEFAULT_SUFX_ZLIB, 76eefd8f96SConrad Meyer .f_compress_bound = mkuz_zlib_cbound, 77eefd8f96SConrad Meyer .f_init = mkuz_zlib_init, 78eefd8f96SConrad Meyer .f_compress = mkuz_zlib_compress, 79eefd8f96SConrad Meyer }, 80eefd8f96SConrad Meyer [UZ_LZMA] = { 81eefd8f96SConrad Meyer .option = "lzma", 828f8cb840SMaxim Sobolev .magic = CLOOP_MAGIC_LZMA, 838f8cb840SMaxim Sobolev .default_sufx = DEFAULT_SUFX_LZMA, 84eefd8f96SConrad Meyer .f_compress_bound = mkuz_lzma_cbound, 85eefd8f96SConrad Meyer .f_init = mkuz_lzma_init, 86eefd8f96SConrad Meyer .f_compress = mkuz_lzma_compress, 87eefd8f96SConrad Meyer }, 88eefd8f96SConrad Meyer [UZ_ZSTD] = { 89eefd8f96SConrad Meyer .option = "zstd", 90eefd8f96SConrad Meyer .magic = CLOOP_MAGIC_ZSTD, 91eefd8f96SConrad Meyer .default_sufx = DEFAULT_SUFX_ZSTD, 92eefd8f96SConrad Meyer .f_compress_bound = mkuz_zstd_cbound, 93eefd8f96SConrad Meyer .f_init = mkuz_zstd_init, 94eefd8f96SConrad Meyer .f_compress = mkuz_zstd_compress, 95eefd8f96SConrad Meyer }, 968f8cb840SMaxim Sobolev }; 977f4caa8cSMaxim Sobolev 984fc55e3eSMaxim Sobolev static struct mkuz_blk *readblock(int, u_int32_t); 991a7ac2bdSAlfonso Gregory static void usage(void) __dead2; 1007f4caa8cSMaxim Sobolev static void cleanup(void); 1017f4caa8cSMaxim Sobolev 1027f4caa8cSMaxim Sobolev static char *cleanfile = NULL; 1037f4caa8cSMaxim Sobolev 1044fc55e3eSMaxim Sobolev static int 1054fc55e3eSMaxim Sobolev cmp_blkno(const struct mkuz_blk *bp, void *p) 1064fc55e3eSMaxim Sobolev { 1074fc55e3eSMaxim Sobolev uint32_t *ap; 1084fc55e3eSMaxim Sobolev 1094fc55e3eSMaxim Sobolev ap = (uint32_t *)p; 1104fc55e3eSMaxim Sobolev 1114fc55e3eSMaxim Sobolev return (bp->info.blkno == *ap); 1124fc55e3eSMaxim Sobolev } 1134fc55e3eSMaxim Sobolev 1147f4caa8cSMaxim Sobolev int main(int argc, char **argv) 1157f4caa8cSMaxim Sobolev { 1164fc55e3eSMaxim Sobolev struct mkuz_cfg cfs; 117bc3b2c55SMaxim Sobolev char *oname; 1187f4caa8cSMaxim Sobolev uint64_t *toc; 1194fc55e3eSMaxim Sobolev int i, io, opt, tmp; 12062ee4b69SMaxim Sobolev struct { 12162ee4b69SMaxim Sobolev int en; 12262ee4b69SMaxim Sobolev FILE *f; 12362ee4b69SMaxim Sobolev } summary; 1247f4caa8cSMaxim Sobolev struct iovec iov[2]; 1258f8cb840SMaxim Sobolev uint64_t offset, last_offset; 1268f8cb840SMaxim Sobolev struct cloop_header hdr; 1274fc55e3eSMaxim Sobolev struct mkuz_conveyor *cvp; 1284fc55e3eSMaxim Sobolev struct mkuz_blk_info *chit; 1290ce59aa8SAlan Somers size_t ncpusz, ncpu, magiclen; 1304fc55e3eSMaxim Sobolev double st, et; 131eefd8f96SConrad Meyer enum UZ_ALGORITHM comp_alg; 132eefd8f96SConrad Meyer int comp_level; 1334fc55e3eSMaxim Sobolev 1344fc55e3eSMaxim Sobolev st = getdtime(); 1354fc55e3eSMaxim Sobolev 1364fc55e3eSMaxim Sobolev ncpusz = sizeof(size_t); 1374fc55e3eSMaxim Sobolev if (sysctlbyname("hw.ncpu", &ncpu, &ncpusz, NULL, 0) < 0) { 1384fc55e3eSMaxim Sobolev ncpu = 1; 1394fc55e3eSMaxim Sobolev } else if (ncpu > MAX_WORKERS_AUTO) { 1404fc55e3eSMaxim Sobolev ncpu = MAX_WORKERS_AUTO; 1414fc55e3eSMaxim Sobolev } 1427f4caa8cSMaxim Sobolev 1437f4caa8cSMaxim Sobolev memset(&hdr, 0, sizeof(hdr)); 1444fc55e3eSMaxim Sobolev cfs.blksz = DEFAULT_CLSTSIZE; 1457f4caa8cSMaxim Sobolev oname = NULL; 1464fc55e3eSMaxim Sobolev cfs.verbose = 0; 1474fc55e3eSMaxim Sobolev cfs.no_zcomp = 0; 1484fc55e3eSMaxim Sobolev cfs.en_dedup = 0; 14962ee4b69SMaxim Sobolev summary.en = 0; 15062ee4b69SMaxim Sobolev summary.f = stderr; 151eefd8f96SConrad Meyer comp_alg = UZ_ZLIB; 152eefd8f96SConrad Meyer comp_level = USE_DEFAULT_LEVEL; 1534fc55e3eSMaxim Sobolev cfs.nworkers = ncpu; 1544fc55e3eSMaxim Sobolev struct mkuz_blk *iblk, *oblk; 1557f4caa8cSMaxim Sobolev 156eefd8f96SConrad Meyer while((opt = getopt(argc, argv, "A:C:o:s:vZdLSj:")) != -1) { 1577f4caa8cSMaxim Sobolev switch(opt) { 158eefd8f96SConrad Meyer case 'A': 159eefd8f96SConrad Meyer for (tmp = UZ_ZLIB; tmp < UZ_INVALID; tmp++) { 160eefd8f96SConrad Meyer if (strcmp(uzip_fmts[tmp].option, optarg) == 0) 161eefd8f96SConrad Meyer break; 162eefd8f96SConrad Meyer } 163eefd8f96SConrad Meyer if (tmp == UZ_INVALID) 164eefd8f96SConrad Meyer errx(1, "invalid algorithm specified: %s", 165eefd8f96SConrad Meyer optarg); 166eefd8f96SConrad Meyer /* Not reached */ 167eefd8f96SConrad Meyer comp_alg = tmp; 168eefd8f96SConrad Meyer break; 169eefd8f96SConrad Meyer case 'C': 170eefd8f96SConrad Meyer comp_level = atoi(optarg); 171eefd8f96SConrad Meyer break; 1727f4caa8cSMaxim Sobolev case 'o': 1737f4caa8cSMaxim Sobolev oname = optarg; 1747f4caa8cSMaxim Sobolev break; 1757f4caa8cSMaxim Sobolev 1767f4caa8cSMaxim Sobolev case 's': 1777f4caa8cSMaxim Sobolev tmp = atoi(optarg); 1787f4caa8cSMaxim Sobolev if (tmp <= 0) { 1797f4caa8cSMaxim Sobolev errx(1, "invalid cluster size specified: %s", 1807f4caa8cSMaxim Sobolev optarg); 1817f4caa8cSMaxim Sobolev /* Not reached */ 1827f4caa8cSMaxim Sobolev } 1834fc55e3eSMaxim Sobolev cfs.blksz = tmp; 1847f4caa8cSMaxim Sobolev break; 1857f4caa8cSMaxim Sobolev 1867f4caa8cSMaxim Sobolev case 'v': 1874fc55e3eSMaxim Sobolev cfs.verbose = 1; 1887f4caa8cSMaxim Sobolev break; 1897f4caa8cSMaxim Sobolev 1908f8cb840SMaxim Sobolev case 'Z': 1914fc55e3eSMaxim Sobolev cfs.no_zcomp = 1; 1928f8cb840SMaxim Sobolev break; 1938f8cb840SMaxim Sobolev 1948f8cb840SMaxim Sobolev case 'd': 1954fc55e3eSMaxim Sobolev cfs.en_dedup = 1; 1968f8cb840SMaxim Sobolev break; 1978f8cb840SMaxim Sobolev 1988f8cb840SMaxim Sobolev case 'L': 199eefd8f96SConrad Meyer comp_alg = UZ_LZMA; 2008f8cb840SMaxim Sobolev break; 2018f8cb840SMaxim Sobolev 202d83e0778SMaxim Sobolev case 'S': 20362ee4b69SMaxim Sobolev summary.en = 1; 20462ee4b69SMaxim Sobolev summary.f = stdout; 205d83e0778SMaxim Sobolev break; 206d83e0778SMaxim Sobolev 2074fc55e3eSMaxim Sobolev case 'j': 2084fc55e3eSMaxim Sobolev tmp = atoi(optarg); 2094fc55e3eSMaxim Sobolev if (tmp <= 0) { 2104fc55e3eSMaxim Sobolev errx(1, "invalid number of compression threads" 2114fc55e3eSMaxim Sobolev " specified: %s", optarg); 2124fc55e3eSMaxim Sobolev /* Not reached */ 2134fc55e3eSMaxim Sobolev } 2144fc55e3eSMaxim Sobolev cfs.nworkers = tmp; 2154fc55e3eSMaxim Sobolev break; 2164fc55e3eSMaxim Sobolev 2177f4caa8cSMaxim Sobolev default: 2187f4caa8cSMaxim Sobolev usage(); 2197f4caa8cSMaxim Sobolev /* Not reached */ 2207f4caa8cSMaxim Sobolev } 2217f4caa8cSMaxim Sobolev } 2227f4caa8cSMaxim Sobolev argc -= optind; 2237f4caa8cSMaxim Sobolev argv += optind; 2247f4caa8cSMaxim Sobolev 2257f4caa8cSMaxim Sobolev if (argc != 1) { 2267f4caa8cSMaxim Sobolev usage(); 2277f4caa8cSMaxim Sobolev /* Not reached */ 2287f4caa8cSMaxim Sobolev } 2297f4caa8cSMaxim Sobolev 230eefd8f96SConrad Meyer cfs.handler = &uzip_fmts[comp_alg]; 231eefd8f96SConrad Meyer 2320ce59aa8SAlan Somers magiclen = strlcpy(hdr.magic, cfs.handler->magic, sizeof(hdr.magic)); 2330ce59aa8SAlan Somers assert(magiclen < sizeof(hdr.magic)); 2348f8cb840SMaxim Sobolev 2354fc55e3eSMaxim Sobolev if (cfs.en_dedup != 0) { 236eefd8f96SConrad Meyer /* 237eefd8f96SConrad Meyer * Dedupe requires a version 3 format. Don't downgrade newer 238eefd8f96SConrad Meyer * formats. 239eefd8f96SConrad Meyer */ 240eefd8f96SConrad Meyer if (hdr.magic[CLOOP_OFS_VERSN] == CLOOP_MAJVER_2) 2418f8cb840SMaxim Sobolev hdr.magic[CLOOP_OFS_VERSN] = CLOOP_MAJVER_3; 2428f8cb840SMaxim Sobolev hdr.magic[CLOOP_OFS_COMPR] = 2438f8cb840SMaxim Sobolev tolower(hdr.magic[CLOOP_OFS_COMPR]); 2448f8cb840SMaxim Sobolev } 2458f8cb840SMaxim Sobolev 246eefd8f96SConrad Meyer if (cfs.blksz % DEV_BSIZE != 0) 247eefd8f96SConrad Meyer errx(1, "cluster size should be multiple of %d", DEV_BSIZE); 248eefd8f96SConrad Meyer 249eefd8f96SConrad Meyer cfs.cbound_blksz = cfs.handler->f_compress_bound(cfs.blksz); 250eefd8f96SConrad Meyer if (cfs.cbound_blksz > MAXPHYS) 251eefd8f96SConrad Meyer errx(1, "maximal compressed cluster size %zu greater than MAXPHYS %zu", 252eefd8f96SConrad Meyer cfs.cbound_blksz, (size_t)MAXPHYS); 253eefd8f96SConrad Meyer 2541291d48fSJohn Baldwin cfs.handler->f_init(&comp_level); 255eefd8f96SConrad Meyer cfs.comp_level = comp_level; 2568f8cb840SMaxim Sobolev 257bc3b2c55SMaxim Sobolev cfs.iname = argv[0]; 2587f4caa8cSMaxim Sobolev if (oname == NULL) { 259bc3b2c55SMaxim Sobolev asprintf(&oname, "%s%s", cfs.iname, cfs.handler->default_sufx); 2607f4caa8cSMaxim Sobolev if (oname == NULL) { 2617f4caa8cSMaxim Sobolev err(1, "can't allocate memory"); 2627f4caa8cSMaxim Sobolev /* Not reached */ 2637f4caa8cSMaxim Sobolev } 2647f4caa8cSMaxim Sobolev } 2657f4caa8cSMaxim Sobolev 2667f4caa8cSMaxim Sobolev signal(SIGHUP, exit); 2677f4caa8cSMaxim Sobolev signal(SIGINT, exit); 2687f4caa8cSMaxim Sobolev signal(SIGTERM, exit); 2697f4caa8cSMaxim Sobolev signal(SIGXCPU, exit); 2707f4caa8cSMaxim Sobolev signal(SIGXFSZ, exit); 2717f4caa8cSMaxim Sobolev atexit(cleanup); 2727f4caa8cSMaxim Sobolev 273bc3b2c55SMaxim Sobolev cfs.fdr = open(cfs.iname, O_RDONLY); 2744fc55e3eSMaxim Sobolev if (cfs.fdr < 0) { 275bc3b2c55SMaxim Sobolev err(1, "open(%s)", cfs.iname); 2767f4caa8cSMaxim Sobolev /* Not reached */ 2777f4caa8cSMaxim Sobolev } 278bc3b2c55SMaxim Sobolev cfs.isize = mkuz_get_insize(&cfs); 279bc3b2c55SMaxim Sobolev if (cfs.isize < 0) { 280bc3b2c55SMaxim Sobolev errx(1, "can't determine input image size"); 28127d0a1a4SMax Khon /* Not reached */ 28227d0a1a4SMax Khon } 283bc3b2c55SMaxim Sobolev hdr.nblocks = cfs.isize / cfs.blksz; 284bc3b2c55SMaxim Sobolev if ((cfs.isize % cfs.blksz) != 0) { 2854fc55e3eSMaxim Sobolev if (cfs.verbose != 0) 2860b99ac63SMaxim Sobolev fprintf(stderr, "file size is not multiple " 2874fc55e3eSMaxim Sobolev "of %d, padding data\n", cfs.blksz); 2880b99ac63SMaxim Sobolev hdr.nblocks++; 2890b99ac63SMaxim Sobolev } 2908f8cb840SMaxim Sobolev toc = mkuz_safe_malloc((hdr.nblocks + 1) * sizeof(*toc)); 2917f4caa8cSMaxim Sobolev 292eefd8f96SConrad Meyer /* 293eefd8f96SConrad Meyer * Initialize last+1 entry with non-heap trash. If final padding is 294eefd8f96SConrad Meyer * added later, it may or may not be overwritten with an offset 295eefd8f96SConrad Meyer * representing the length of the final compressed block. If not, 296eefd8f96SConrad Meyer * initialize to a defined value. 297eefd8f96SConrad Meyer */ 298eefd8f96SConrad Meyer toc[hdr.nblocks] = 0; 299eefd8f96SConrad Meyer 3004fc55e3eSMaxim Sobolev cfs.fdw = open(oname, (cfs.en_dedup ? O_RDWR : O_WRONLY) | O_TRUNC | O_CREAT, 301*525a177cSRobert Wing S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 3024fc55e3eSMaxim Sobolev if (cfs.fdw < 0) { 303d72d8f53SPawel Jakub Dawidek err(1, "open(%s)", oname); 3047f4caa8cSMaxim Sobolev /* Not reached */ 3057f4caa8cSMaxim Sobolev } 3067f4caa8cSMaxim Sobolev cleanfile = oname; 3077f4caa8cSMaxim Sobolev 3087f4caa8cSMaxim Sobolev /* Prepare header that we will write later when we have index ready. */ 3097f4caa8cSMaxim Sobolev iov[0].iov_base = (char *)&hdr; 3107f4caa8cSMaxim Sobolev iov[0].iov_len = sizeof(hdr); 3117f4caa8cSMaxim Sobolev iov[1].iov_base = (char *)toc; 3127f4caa8cSMaxim Sobolev iov[1].iov_len = (hdr.nblocks + 1) * sizeof(*toc); 3137f4caa8cSMaxim Sobolev offset = iov[0].iov_len + iov[1].iov_len; 3147f4caa8cSMaxim Sobolev 3157f4caa8cSMaxim Sobolev /* Reserve space for header */ 3164fc55e3eSMaxim Sobolev lseek(cfs.fdw, offset, SEEK_SET); 3177f4caa8cSMaxim Sobolev 3184fc55e3eSMaxim Sobolev if (cfs.verbose != 0) { 319ed9302fdSMaxim Sobolev fprintf(stderr, "data size %ju bytes, number of clusters " 320bc3b2c55SMaxim Sobolev "%u, index length %zu bytes\n", cfs.isize, 3210b99ac63SMaxim Sobolev hdr.nblocks, iov[1].iov_len); 3224fc55e3eSMaxim Sobolev } 3234fc55e3eSMaxim Sobolev 3244fc55e3eSMaxim Sobolev cvp = mkuz_conveyor_ctor(&cfs); 3257f4caa8cSMaxim Sobolev 3268f8cb840SMaxim Sobolev last_offset = 0; 3274fc55e3eSMaxim Sobolev iblk = oblk = NULL; 3284fc55e3eSMaxim Sobolev for(i = io = 0; iblk != MKUZ_BLK_EOF; i++) { 3294fc55e3eSMaxim Sobolev iblk = readblock(cfs.fdr, cfs.blksz); 3304fc55e3eSMaxim Sobolev mkuz_fqueue_enq(cvp->wrk_queue, iblk); 3314fc55e3eSMaxim Sobolev if (iblk != MKUZ_BLK_EOF && 3324fc55e3eSMaxim Sobolev (i < (cfs.nworkers * ITEMS_PER_WORKER))) { 3334fc55e3eSMaxim Sobolev continue; 3347f4caa8cSMaxim Sobolev } 3354fc55e3eSMaxim Sobolev drain: 3364fc55e3eSMaxim Sobolev oblk = mkuz_fqueue_deq_when(cvp->results, cmp_blkno, &io); 3374fc55e3eSMaxim Sobolev assert(oblk->info.blkno == (unsigned)io); 3384fc55e3eSMaxim Sobolev oblk->info.offset = offset; 3394fc55e3eSMaxim Sobolev chit = NULL; 3404fc55e3eSMaxim Sobolev if (cfs.en_dedup != 0 && oblk->info.len > 0) { 3414fc55e3eSMaxim Sobolev chit = mkuz_blkcache_regblock(cfs.fdw, oblk); 3428f8cb840SMaxim Sobolev /* 3438f8cb840SMaxim Sobolev * There should be at least one non-empty block 3448f8cb840SMaxim Sobolev * between us and the backref'ed offset, otherwise 3458f8cb840SMaxim Sobolev * we won't be able to parse that sequence correctly 3468f8cb840SMaxim Sobolev * as it would be indistinguishible from another 3478f8cb840SMaxim Sobolev * empty block. 3488f8cb840SMaxim Sobolev */ 3498f8cb840SMaxim Sobolev if (chit != NULL && chit->offset == last_offset) { 3508f8cb840SMaxim Sobolev chit = NULL; 3518f8cb840SMaxim Sobolev } 3528f8cb840SMaxim Sobolev } 3538f8cb840SMaxim Sobolev if (chit != NULL) { 3544fc55e3eSMaxim Sobolev toc[io] = htobe64(chit->offset); 3554fc55e3eSMaxim Sobolev oblk->info.len = 0; 3568f8cb840SMaxim Sobolev } else { 3574fc55e3eSMaxim Sobolev if (oblk->info.len > 0 && write(cfs.fdw, oblk->data, 3584fc55e3eSMaxim Sobolev oblk->info.len) < 0) { 359d72d8f53SPawel Jakub Dawidek err(1, "write(%s)", oname); 3607f4caa8cSMaxim Sobolev /* Not reached */ 3617f4caa8cSMaxim Sobolev } 3624fc55e3eSMaxim Sobolev toc[io] = htobe64(offset); 3638f8cb840SMaxim Sobolev last_offset = offset; 3644fc55e3eSMaxim Sobolev offset += oblk->info.len; 3657f4caa8cSMaxim Sobolev } 3664fc55e3eSMaxim Sobolev if (cfs.verbose != 0) { 3678f8cb840SMaxim Sobolev fprintf(stderr, "cluster #%d, in %u bytes, " 3684fc55e3eSMaxim Sobolev "out len=%lu offset=%lu", io, cfs.blksz, 3694fc55e3eSMaxim Sobolev (u_long)oblk->info.len, (u_long)be64toh(toc[io])); 3708f8cb840SMaxim Sobolev if (chit != NULL) { 3718f8cb840SMaxim Sobolev fprintf(stderr, " (backref'ed to #%d)", 3728f8cb840SMaxim Sobolev chit->blkno); 3738f8cb840SMaxim Sobolev } 3748f8cb840SMaxim Sobolev fprintf(stderr, "\n"); 3754fc55e3eSMaxim Sobolev } 3764fc55e3eSMaxim Sobolev free(oblk); 3774fc55e3eSMaxim Sobolev io += 1; 3784fc55e3eSMaxim Sobolev if (iblk == MKUZ_BLK_EOF) { 3794fc55e3eSMaxim Sobolev if (io < i) 3804fc55e3eSMaxim Sobolev goto drain; 3814fc55e3eSMaxim Sobolev /* Last block, see if we need to add some padding */ 3824fc55e3eSMaxim Sobolev if ((offset % DEV_BSIZE) == 0) 3834fc55e3eSMaxim Sobolev continue; 3844fc55e3eSMaxim Sobolev oblk = mkuz_blk_ctor(DEV_BSIZE - (offset % DEV_BSIZE)); 3854fc55e3eSMaxim Sobolev oblk->info.blkno = io; 3864fc55e3eSMaxim Sobolev oblk->info.len = oblk->alen; 3874fc55e3eSMaxim Sobolev if (cfs.verbose != 0) { 3884fc55e3eSMaxim Sobolev fprintf(stderr, "padding data with %lu bytes " 3894fc55e3eSMaxim Sobolev "so that file size is multiple of %d\n", 3904fc55e3eSMaxim Sobolev (u_long)oblk->alen, DEV_BSIZE); 3914fc55e3eSMaxim Sobolev } 3924fc55e3eSMaxim Sobolev mkuz_fqueue_enq(cvp->results, oblk); 3934fc55e3eSMaxim Sobolev goto drain; 3948f8cb840SMaxim Sobolev } 3958f8cb840SMaxim Sobolev } 3967f4caa8cSMaxim Sobolev 3974fc55e3eSMaxim Sobolev close(cfs.fdr); 3984fc55e3eSMaxim Sobolev 3994fc55e3eSMaxim Sobolev if (cfs.verbose != 0 || summary.en != 0) { 4004fc55e3eSMaxim Sobolev et = getdtime(); 40162ee4b69SMaxim Sobolev fprintf(summary.f, "compressed data to %ju bytes, saved %lld " 4024fc55e3eSMaxim Sobolev "bytes, %.2f%% decrease, %.2f bytes/sec.\n", offset, 403bc3b2c55SMaxim Sobolev (long long)(cfs.isize - offset), 404bc3b2c55SMaxim Sobolev 100.0 * (long long)(cfs.isize - offset) / 405bc3b2c55SMaxim Sobolev (float)cfs.isize, (float)cfs.isize / (et - st)); 4064fc55e3eSMaxim Sobolev } 4077f4caa8cSMaxim Sobolev 4087f4caa8cSMaxim Sobolev /* Convert to big endian */ 4094fc55e3eSMaxim Sobolev hdr.blksz = htonl(cfs.blksz); 4107f4caa8cSMaxim Sobolev hdr.nblocks = htonl(hdr.nblocks); 4117f4caa8cSMaxim Sobolev /* Write headers into pre-allocated space */ 4124fc55e3eSMaxim Sobolev lseek(cfs.fdw, 0, SEEK_SET); 4134fc55e3eSMaxim Sobolev if (writev(cfs.fdw, iov, 2) < 0) { 414d72d8f53SPawel Jakub Dawidek err(1, "writev(%s)", oname); 4157f4caa8cSMaxim Sobolev /* Not reached */ 4167f4caa8cSMaxim Sobolev } 4177f4caa8cSMaxim Sobolev cleanfile = NULL; 4184fc55e3eSMaxim Sobolev close(cfs.fdw); 4197f4caa8cSMaxim Sobolev 4207f4caa8cSMaxim Sobolev exit(0); 4217f4caa8cSMaxim Sobolev } 4227f4caa8cSMaxim Sobolev 4234fc55e3eSMaxim Sobolev static struct mkuz_blk * 4244fc55e3eSMaxim Sobolev readblock(int fd, u_int32_t clstsize) 4250b99ac63SMaxim Sobolev { 4267f4caa8cSMaxim Sobolev int numread; 4274fc55e3eSMaxim Sobolev struct mkuz_blk *rval; 4284fc55e3eSMaxim Sobolev static int blockcnt; 4294fc55e3eSMaxim Sobolev off_t cpos; 4307f4caa8cSMaxim Sobolev 4314fc55e3eSMaxim Sobolev rval = mkuz_blk_ctor(clstsize); 4324fc55e3eSMaxim Sobolev 4334fc55e3eSMaxim Sobolev rval->info.blkno = blockcnt; 4344fc55e3eSMaxim Sobolev blockcnt += 1; 4354fc55e3eSMaxim Sobolev cpos = lseek(fd, 0, SEEK_CUR); 4364fc55e3eSMaxim Sobolev if (cpos < 0) { 4374fc55e3eSMaxim Sobolev err(1, "readblock: lseek() failed"); 4384fc55e3eSMaxim Sobolev /* Not reached */ 4394fc55e3eSMaxim Sobolev } 4404fc55e3eSMaxim Sobolev rval->info.offset = cpos; 4414fc55e3eSMaxim Sobolev 4424fc55e3eSMaxim Sobolev numread = read(fd, rval->data, clstsize); 4437f4caa8cSMaxim Sobolev if (numread < 0) { 4444fc55e3eSMaxim Sobolev err(1, "readblock: read() failed"); 4457f4caa8cSMaxim Sobolev /* Not reached */ 4467f4caa8cSMaxim Sobolev } 4477f4caa8cSMaxim Sobolev if (numread == 0) { 4484fc55e3eSMaxim Sobolev free(rval); 4494fc55e3eSMaxim Sobolev return MKUZ_BLK_EOF; 4507f4caa8cSMaxim Sobolev } 4514fc55e3eSMaxim Sobolev rval->info.len = numread; 4524fc55e3eSMaxim Sobolev return rval; 4537f4caa8cSMaxim Sobolev } 4547f4caa8cSMaxim Sobolev 4557f4caa8cSMaxim Sobolev static void 4560b99ac63SMaxim Sobolev usage(void) 4570b99ac63SMaxim Sobolev { 4587f4caa8cSMaxim Sobolev 459d83e0778SMaxim Sobolev fprintf(stderr, "usage: mkuzip [-vZdLS] [-o outfile] [-s cluster_size] " 4604fc55e3eSMaxim Sobolev "[-j ncompr] infile\n"); 4617f4caa8cSMaxim Sobolev exit(1); 4627f4caa8cSMaxim Sobolev } 4637f4caa8cSMaxim Sobolev 4648f8cb840SMaxim Sobolev void * 4658f8cb840SMaxim Sobolev mkuz_safe_malloc(size_t size) 4660b99ac63SMaxim Sobolev { 4677f4caa8cSMaxim Sobolev void *retval; 4687f4caa8cSMaxim Sobolev 4697f4caa8cSMaxim Sobolev retval = malloc(size); 4707f4caa8cSMaxim Sobolev if (retval == NULL) { 4717f4caa8cSMaxim Sobolev err(1, "can't allocate memory"); 4727f4caa8cSMaxim Sobolev /* Not reached */ 4737f4caa8cSMaxim Sobolev } 4747f4caa8cSMaxim Sobolev return retval; 4757f4caa8cSMaxim Sobolev } 4767f4caa8cSMaxim Sobolev 4774fc55e3eSMaxim Sobolev void * 4784fc55e3eSMaxim Sobolev mkuz_safe_zmalloc(size_t size) 4794fc55e3eSMaxim Sobolev { 4804fc55e3eSMaxim Sobolev void *retval; 4814fc55e3eSMaxim Sobolev 4824fc55e3eSMaxim Sobolev retval = mkuz_safe_malloc(size); 4834fc55e3eSMaxim Sobolev bzero(retval, size); 4844fc55e3eSMaxim Sobolev return retval; 4854fc55e3eSMaxim Sobolev } 4864fc55e3eSMaxim Sobolev 4877f4caa8cSMaxim Sobolev static void 4880b99ac63SMaxim Sobolev cleanup(void) 4890b99ac63SMaxim Sobolev { 4907f4caa8cSMaxim Sobolev 4917f4caa8cSMaxim Sobolev if (cleanfile != NULL) 4927f4caa8cSMaxim Sobolev unlink(cleanfile); 4937f4caa8cSMaxim Sobolev } 4948f8cb840SMaxim Sobolev 4954fc55e3eSMaxim Sobolev int 4964fc55e3eSMaxim Sobolev mkuz_memvcmp(const void *memory, unsigned char val, size_t size) 4978f8cb840SMaxim Sobolev { 4988f8cb840SMaxim Sobolev const u_char *mm; 4998f8cb840SMaxim Sobolev 5008f8cb840SMaxim Sobolev mm = (const u_char *)memory; 5018f8cb840SMaxim Sobolev return (*mm == val) && memcmp(mm, mm + 1, size - 1) == 0; 5028f8cb840SMaxim Sobolev } 503