xref: /freebsd/usr.bin/mkuzip/mkuzip.c (revision 7f4caa8c59a76505cdd82ecd80508572c3f66b45)
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