17f4caa8cSMaxim Sobolev /* 27f4caa8cSMaxim Sobolev * ---------------------------------------------------------------------------- 37f4caa8cSMaxim Sobolev * "THE BEER-WARE LICENSE" (Revision 42): 47f4caa8cSMaxim Sobolev * <sobomax@FreeBSD.ORG> wrote this file. As long as you retain this notice you 57f4caa8cSMaxim Sobolev * can do whatever you want with this stuff. If we meet some day, and you think 67f4caa8cSMaxim Sobolev * this stuff is worth it, you can buy me a beer in return. Maxim Sobolev 77f4caa8cSMaxim Sobolev * ---------------------------------------------------------------------------- 87f4caa8cSMaxim Sobolev * 97f4caa8cSMaxim Sobolev * $FreeBSD$ 107f4caa8cSMaxim Sobolev * 117f4caa8cSMaxim Sobolev */ 127f4caa8cSMaxim Sobolev 137f4caa8cSMaxim Sobolev #include <sys/types.h> 147f4caa8cSMaxim Sobolev #include <sys/endian.h> 157f4caa8cSMaxim Sobolev #include <sys/param.h> 167f4caa8cSMaxim Sobolev #include <sys/stat.h> 177f4caa8cSMaxim Sobolev #include <sys/uio.h> 187f4caa8cSMaxim Sobolev #include <netinet/in.h> 197f4caa8cSMaxim Sobolev #include <zlib.h> 207f4caa8cSMaxim Sobolev #include <err.h> 217f4caa8cSMaxim Sobolev #include <fcntl.h> 227f4caa8cSMaxim Sobolev #include <signal.h> 237f4caa8cSMaxim Sobolev #include <stdio.h> 247f4caa8cSMaxim Sobolev #include <stdlib.h> 257f4caa8cSMaxim Sobolev #include <string.h> 267f4caa8cSMaxim Sobolev #include <unistd.h> 277f4caa8cSMaxim Sobolev 287f4caa8cSMaxim Sobolev #define CLSTSIZE 16384 297f4caa8cSMaxim Sobolev #define DEFAULT_SUFX ".uzip" 307f4caa8cSMaxim Sobolev 317f4caa8cSMaxim Sobolev #define CLOOP_MAGIC_LEN 128 327f4caa8cSMaxim Sobolev static char CLOOP_MAGIC_START[] = "#!/bin/sh\n#V2.0 Format\n" 337f4caa8cSMaxim Sobolev "m=geom_uzip\n(kldstat -n $m 2>&-||kldload $m)>&-&&" 347f4caa8cSMaxim Sobolev "mount_cd9660 /dev/`mdconfig -af $0`.uzip $1\nexit $?\n"; 357f4caa8cSMaxim Sobolev 367f4caa8cSMaxim Sobolev /* 377f4caa8cSMaxim Sobolev * Maximum allowed valid block size (to prevent foot-shooting) 387f4caa8cSMaxim Sobolev */ 397f4caa8cSMaxim Sobolev #define MAX_BLKSZ (MAXPHYS - MAXPHYS / 1000 - 12) 407f4caa8cSMaxim Sobolev 417f4caa8cSMaxim Sobolev static char *readblock(int, char *, u_int32_t); 427f4caa8cSMaxim Sobolev static void usage(void); 437f4caa8cSMaxim Sobolev static void *safe_malloc(size_t); 447f4caa8cSMaxim Sobolev static void cleanup(void); 457f4caa8cSMaxim Sobolev 467f4caa8cSMaxim Sobolev static char *cleanfile = NULL; 477f4caa8cSMaxim Sobolev 487f4caa8cSMaxim Sobolev int main(int argc, char **argv) 497f4caa8cSMaxim Sobolev { 507f4caa8cSMaxim Sobolev char *iname, *oname, *obuf, *ibuf; 517f4caa8cSMaxim Sobolev uint64_t *toc; 527f4caa8cSMaxim Sobolev int fdr, fdw, i, opt, verbose, tmp; 537f4caa8cSMaxim Sobolev struct iovec iov[2]; 547f4caa8cSMaxim Sobolev struct stat sb; 557f4caa8cSMaxim Sobolev uLongf destlen; 567f4caa8cSMaxim Sobolev uint64_t offset; 577f4caa8cSMaxim Sobolev struct cloop_header { 587f4caa8cSMaxim Sobolev char magic[CLOOP_MAGIC_LEN]; /* cloop magic */ 597f4caa8cSMaxim Sobolev uint32_t blksz; /* block size */ 607f4caa8cSMaxim Sobolev uint32_t nblocks; /* number of blocks */ 617f4caa8cSMaxim Sobolev } hdr; 627f4caa8cSMaxim Sobolev 637f4caa8cSMaxim Sobolev memset(&hdr, 0, sizeof(hdr)); 647f4caa8cSMaxim Sobolev hdr.blksz = CLSTSIZE; 657f4caa8cSMaxim Sobolev strcpy(hdr.magic, CLOOP_MAGIC_START); 667f4caa8cSMaxim Sobolev oname = NULL; 677f4caa8cSMaxim Sobolev verbose = 0; 687f4caa8cSMaxim Sobolev 697f4caa8cSMaxim Sobolev while((opt = getopt(argc, argv, "o:s:v")) != -1) { 707f4caa8cSMaxim Sobolev switch(opt) { 717f4caa8cSMaxim Sobolev case 'o': 727f4caa8cSMaxim Sobolev oname = optarg; 737f4caa8cSMaxim Sobolev break; 747f4caa8cSMaxim Sobolev 757f4caa8cSMaxim Sobolev case 's': 767f4caa8cSMaxim Sobolev tmp = atoi(optarg); 777f4caa8cSMaxim Sobolev if (tmp <= 0) { 787f4caa8cSMaxim Sobolev errx(1, "invalid cluster size specified: %s", 797f4caa8cSMaxim Sobolev optarg); 807f4caa8cSMaxim Sobolev /* Not reached */ 817f4caa8cSMaxim Sobolev } 827f4caa8cSMaxim Sobolev if (tmp % DEV_BSIZE != 0) { 837f4caa8cSMaxim Sobolev errx(1, "cluster size should be multiple of %d", 847f4caa8cSMaxim Sobolev DEV_BSIZE); 857f4caa8cSMaxim Sobolev /* Not reached */ 867f4caa8cSMaxim Sobolev } 877f4caa8cSMaxim Sobolev if (tmp > MAX_BLKSZ) { 887f4caa8cSMaxim Sobolev errx(1, "cluster size can't be more than %d", 897f4caa8cSMaxim Sobolev MAX_BLKSZ); 907f4caa8cSMaxim Sobolev /* Not reached */ 917f4caa8cSMaxim Sobolev } 927f4caa8cSMaxim Sobolev hdr.blksz = tmp; 937f4caa8cSMaxim Sobolev break; 947f4caa8cSMaxim Sobolev 957f4caa8cSMaxim Sobolev case 'v': 967f4caa8cSMaxim Sobolev verbose = 1; 977f4caa8cSMaxim Sobolev break; 987f4caa8cSMaxim Sobolev 997f4caa8cSMaxim Sobolev default: 1007f4caa8cSMaxim Sobolev usage(); 1017f4caa8cSMaxim Sobolev /* Not reached */ 1027f4caa8cSMaxim Sobolev } 1037f4caa8cSMaxim Sobolev } 1047f4caa8cSMaxim Sobolev argc -= optind; 1057f4caa8cSMaxim Sobolev argv += optind; 1067f4caa8cSMaxim Sobolev 1077f4caa8cSMaxim Sobolev if (argc != 1) { 1087f4caa8cSMaxim Sobolev usage(); 1097f4caa8cSMaxim Sobolev /* Not reached */ 1107f4caa8cSMaxim Sobolev } 1117f4caa8cSMaxim Sobolev 1127f4caa8cSMaxim Sobolev iname = argv[0]; 1137f4caa8cSMaxim Sobolev if (oname == NULL) { 1147f4caa8cSMaxim Sobolev asprintf(&oname, "%s%s", iname, DEFAULT_SUFX); 1157f4caa8cSMaxim Sobolev if (oname == NULL) { 1167f4caa8cSMaxim Sobolev err(1, "can't allocate memory"); 1177f4caa8cSMaxim Sobolev /* Not reached */ 1187f4caa8cSMaxim Sobolev } 1197f4caa8cSMaxim Sobolev } 1207f4caa8cSMaxim Sobolev 1217f4caa8cSMaxim Sobolev obuf = safe_malloc(compressBound(hdr.blksz)); 1227f4caa8cSMaxim Sobolev ibuf = safe_malloc(hdr.blksz); 1237f4caa8cSMaxim Sobolev 1247f4caa8cSMaxim Sobolev signal(SIGHUP, exit); 1257f4caa8cSMaxim Sobolev signal(SIGINT, exit); 1267f4caa8cSMaxim Sobolev signal(SIGTERM, exit); 1277f4caa8cSMaxim Sobolev signal(SIGXCPU, exit); 1287f4caa8cSMaxim Sobolev signal(SIGXFSZ, exit); 1297f4caa8cSMaxim Sobolev atexit(cleanup); 1307f4caa8cSMaxim Sobolev 1317f4caa8cSMaxim Sobolev if (stat(iname, &sb) != 0) { 1327f4caa8cSMaxim Sobolev err(1, "%s", iname); 1337f4caa8cSMaxim Sobolev /* Not reached */ 1347f4caa8cSMaxim Sobolev } 1357f4caa8cSMaxim Sobolev if ((sb.st_size % hdr.blksz) != 0) { 1367f4caa8cSMaxim Sobolev errx(1, "%s: incorrect image: file size is not multiple of %d", 1377f4caa8cSMaxim Sobolev iname, hdr.blksz); 1387f4caa8cSMaxim Sobolev /* Not reached */ 1397f4caa8cSMaxim Sobolev } 1407f4caa8cSMaxim Sobolev hdr.nblocks = sb.st_size / hdr.blksz; 1417f4caa8cSMaxim Sobolev toc = safe_malloc((hdr.nblocks + 1) * sizeof(*toc)); 1427f4caa8cSMaxim Sobolev 1437f4caa8cSMaxim Sobolev fdr = open(iname, O_RDONLY); 1447f4caa8cSMaxim Sobolev if (fdr < 0) { 1457f4caa8cSMaxim Sobolev err(1, "%s", iname); 1467f4caa8cSMaxim Sobolev /* Not reached */ 1477f4caa8cSMaxim Sobolev } 1487f4caa8cSMaxim Sobolev fdw = open(oname, O_WRONLY | O_TRUNC | O_CREAT, 1497f4caa8cSMaxim Sobolev S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 1507f4caa8cSMaxim Sobolev if (fdw < 0) { 1517f4caa8cSMaxim Sobolev err(1, "%s", oname); 1527f4caa8cSMaxim Sobolev /* Not reached */ 1537f4caa8cSMaxim Sobolev } 1547f4caa8cSMaxim Sobolev cleanfile = oname; 1557f4caa8cSMaxim Sobolev 1567f4caa8cSMaxim Sobolev /* Prepare header that we will write later when we have index ready. */ 1577f4caa8cSMaxim Sobolev iov[0].iov_base = (char *)&hdr; 1587f4caa8cSMaxim Sobolev iov[0].iov_len = sizeof(hdr); 1597f4caa8cSMaxim Sobolev iov[1].iov_base = (char *)toc; 1607f4caa8cSMaxim Sobolev iov[1].iov_len = (hdr.nblocks + 1) * sizeof(*toc); 1617f4caa8cSMaxim Sobolev offset = iov[0].iov_len + iov[1].iov_len; 1627f4caa8cSMaxim Sobolev 1637f4caa8cSMaxim Sobolev /* Reserve space for header */ 1647f4caa8cSMaxim Sobolev lseek(fdw, offset, SEEK_SET); 1657f4caa8cSMaxim Sobolev 1667f4caa8cSMaxim Sobolev if (verbose != 0) 1677f4caa8cSMaxim Sobolev fprintf(stderr, "Data size: %ld bytes, number of clusters: " 1687f4caa8cSMaxim Sobolev "%ld, index lengh: %ld bytes\n", (long)sb.st_size, 1697f4caa8cSMaxim Sobolev (long)(hdr.nblocks), ((long)hdr.nblocks + 1) * sizeof(*toc)); 1707f4caa8cSMaxim Sobolev 1717f4caa8cSMaxim Sobolev for(i = 0; i == 0 || ibuf != NULL; i++) { 1727f4caa8cSMaxim Sobolev ibuf = readblock(fdr, ibuf, hdr.blksz); 1737f4caa8cSMaxim Sobolev if (ibuf != NULL) { 1747f4caa8cSMaxim Sobolev destlen = compressBound(hdr.blksz); 1757f4caa8cSMaxim Sobolev memset(obuf, 0, destlen); 1767f4caa8cSMaxim Sobolev if (compress2(obuf, &destlen, ibuf, hdr.blksz, Z_BEST_COMPRESSION) != Z_OK) { 1777f4caa8cSMaxim Sobolev errx(1, "can't compress data: compress2() failed"); 1787f4caa8cSMaxim Sobolev /* Not reached */ 1797f4caa8cSMaxim Sobolev } 1807f4caa8cSMaxim Sobolev #if 0 1817f4caa8cSMaxim Sobolev /* 1827f4caa8cSMaxim Sobolev * We don't really need those two leading bytes. Moreover, they 1837f4caa8cSMaxim Sobolev * confuse oldest decompression routine presented in the 1847f4caa8cSMaxim Sobolev * FreeBSD kernel, so they should be omitted. 1857f4caa8cSMaxim Sobolev */ 1867f4caa8cSMaxim Sobolev destlen -= 2; 1877f4caa8cSMaxim Sobolev #endif 1887f4caa8cSMaxim Sobolev } else { 1897f4caa8cSMaxim Sobolev destlen = DEV_BSIZE - (offset % DEV_BSIZE); 1907f4caa8cSMaxim Sobolev memset(obuf, 0, destlen); 1917f4caa8cSMaxim Sobolev } 1927f4caa8cSMaxim Sobolev if (write(fdw, obuf, destlen) < 0) { 1937f4caa8cSMaxim Sobolev err(1, "%s", oname); 1947f4caa8cSMaxim Sobolev /* Not reached */ 1957f4caa8cSMaxim Sobolev } 1967f4caa8cSMaxim Sobolev toc[i] = htobe64(offset); 1977f4caa8cSMaxim Sobolev offset += destlen; 1987f4caa8cSMaxim Sobolev } 1997f4caa8cSMaxim Sobolev close(fdr); 2007f4caa8cSMaxim Sobolev 2017f4caa8cSMaxim Sobolev if (verbose != 0) 2027f4caa8cSMaxim Sobolev fprintf(stderr, "compressed data to %llu bytes.\n", offset); 2037f4caa8cSMaxim Sobolev 2047f4caa8cSMaxim Sobolev /* Convert to big endian */ 2057f4caa8cSMaxim Sobolev hdr.blksz = htonl(hdr.blksz); 2067f4caa8cSMaxim Sobolev hdr.nblocks = htonl(hdr.nblocks); 2077f4caa8cSMaxim Sobolev /* Write headers into pre-allocated space */ 2087f4caa8cSMaxim Sobolev lseek(fdw, 0, SEEK_SET); 2097f4caa8cSMaxim Sobolev if (writev(fdw, iov, 2) < 0) { 2107f4caa8cSMaxim Sobolev err(1, "%s", oname); 2117f4caa8cSMaxim Sobolev /* Not reached */ 2127f4caa8cSMaxim Sobolev } 2137f4caa8cSMaxim Sobolev cleanfile = NULL; 2147f4caa8cSMaxim Sobolev close(fdw); 2157f4caa8cSMaxim Sobolev 2167f4caa8cSMaxim Sobolev exit(0); 2177f4caa8cSMaxim Sobolev } 2187f4caa8cSMaxim Sobolev 2197f4caa8cSMaxim Sobolev static char * 2207f4caa8cSMaxim Sobolev readblock(int fd, char *ibuf, u_int32_t clstsize) { 2217f4caa8cSMaxim Sobolev int numread; 2227f4caa8cSMaxim Sobolev 2237f4caa8cSMaxim Sobolev bzero(ibuf, clstsize); 2247f4caa8cSMaxim Sobolev numread = read(fd, ibuf, clstsize); 2257f4caa8cSMaxim Sobolev if (numread < 0) { 2267f4caa8cSMaxim Sobolev err(1, "read() failed"); 2277f4caa8cSMaxim Sobolev /* Not reached */ 2287f4caa8cSMaxim Sobolev } 2297f4caa8cSMaxim Sobolev if (numread == 0) { 2307f4caa8cSMaxim Sobolev return NULL; 2317f4caa8cSMaxim Sobolev } 2327f4caa8cSMaxim Sobolev return ibuf; 2337f4caa8cSMaxim Sobolev } 2347f4caa8cSMaxim Sobolev 2357f4caa8cSMaxim Sobolev static void 2367f4caa8cSMaxim Sobolev usage(void) { 2377f4caa8cSMaxim Sobolev 2387f4caa8cSMaxim Sobolev fprintf(stderr, "usage: mkuzip [-v] [-o outfile] [-s cluster_size] infile\n"); 2397f4caa8cSMaxim Sobolev exit(1); 2407f4caa8cSMaxim Sobolev } 2417f4caa8cSMaxim Sobolev 2427f4caa8cSMaxim Sobolev static void * 2437f4caa8cSMaxim Sobolev safe_malloc(size_t size) { 2447f4caa8cSMaxim Sobolev void *retval; 2457f4caa8cSMaxim Sobolev 2467f4caa8cSMaxim Sobolev retval = malloc(size); 2477f4caa8cSMaxim Sobolev if (retval == NULL) { 2487f4caa8cSMaxim Sobolev err(1, "can't allocate memory"); 2497f4caa8cSMaxim Sobolev /* Not reached */ 2507f4caa8cSMaxim Sobolev } 2517f4caa8cSMaxim Sobolev return retval; 2527f4caa8cSMaxim Sobolev } 2537f4caa8cSMaxim Sobolev 2547f4caa8cSMaxim Sobolev static void 2557f4caa8cSMaxim Sobolev cleanup(void) { 2567f4caa8cSMaxim Sobolev 2577f4caa8cSMaxim Sobolev if (cleanfile != NULL) 2587f4caa8cSMaxim Sobolev unlink(cleanfile); 2597f4caa8cSMaxim Sobolev } 260