xref: /freebsd/usr.bin/mkuzip/mkuzip.c (revision ed9302fdd6033cf83eed762010b7d0ec83feacc0)
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 static char *readblock(int, char *, u_int32_t);
377f4caa8cSMaxim Sobolev static void usage(void);
387f4caa8cSMaxim Sobolev static void *safe_malloc(size_t);
397f4caa8cSMaxim Sobolev static void cleanup(void);
407f4caa8cSMaxim Sobolev 
417f4caa8cSMaxim Sobolev static char *cleanfile = NULL;
427f4caa8cSMaxim Sobolev 
437f4caa8cSMaxim Sobolev int main(int argc, char **argv)
447f4caa8cSMaxim Sobolev {
457f4caa8cSMaxim Sobolev 	char *iname, *oname, *obuf, *ibuf;
467f4caa8cSMaxim Sobolev 	uint64_t *toc;
477f4caa8cSMaxim Sobolev 	int fdr, fdw, i, opt, verbose, tmp;
487f4caa8cSMaxim Sobolev 	struct iovec iov[2];
497f4caa8cSMaxim Sobolev 	struct stat sb;
507f4caa8cSMaxim Sobolev 	uLongf destlen;
517f4caa8cSMaxim Sobolev 	uint64_t offset;
527f4caa8cSMaxim Sobolev 	struct cloop_header {
537f4caa8cSMaxim Sobolev 		char magic[CLOOP_MAGIC_LEN];    /* cloop magic */
547f4caa8cSMaxim Sobolev 		uint32_t blksz;                 /* block size */
557f4caa8cSMaxim Sobolev 		uint32_t nblocks;               /* number of blocks */
567f4caa8cSMaxim Sobolev 	} hdr;
577f4caa8cSMaxim Sobolev 
587f4caa8cSMaxim Sobolev 	memset(&hdr, 0, sizeof(hdr));
597f4caa8cSMaxim Sobolev 	hdr.blksz = CLSTSIZE;
607f4caa8cSMaxim Sobolev 	strcpy(hdr.magic, CLOOP_MAGIC_START);
617f4caa8cSMaxim Sobolev 	oname = NULL;
627f4caa8cSMaxim Sobolev 	verbose = 0;
637f4caa8cSMaxim Sobolev 
647f4caa8cSMaxim Sobolev 	while((opt = getopt(argc, argv, "o:s:v")) != -1) {
657f4caa8cSMaxim Sobolev 		switch(opt) {
667f4caa8cSMaxim Sobolev 		case 'o':
677f4caa8cSMaxim Sobolev 			oname = optarg;
687f4caa8cSMaxim Sobolev 			break;
697f4caa8cSMaxim Sobolev 
707f4caa8cSMaxim Sobolev 		case 's':
717f4caa8cSMaxim Sobolev 			tmp = atoi(optarg);
727f4caa8cSMaxim Sobolev 			if (tmp <= 0) {
737f4caa8cSMaxim Sobolev 				errx(1, "invalid cluster size specified: %s",
747f4caa8cSMaxim Sobolev 				    optarg);
757f4caa8cSMaxim Sobolev 				/* Not reached */
767f4caa8cSMaxim Sobolev 			}
777f4caa8cSMaxim Sobolev 			if (tmp % DEV_BSIZE != 0) {
787f4caa8cSMaxim Sobolev 				errx(1, "cluster size should be multiple of %d",
797f4caa8cSMaxim Sobolev 				    DEV_BSIZE);
807f4caa8cSMaxim Sobolev 				/* Not reached */
817f4caa8cSMaxim Sobolev 			}
820b99ac63SMaxim Sobolev 			if (compressBound(tmp) > MAXPHYS) {
830b99ac63SMaxim Sobolev 				errx(1, "cluster size is too large");
847f4caa8cSMaxim Sobolev 				    /* Not reached */
857f4caa8cSMaxim Sobolev 			}
867f4caa8cSMaxim Sobolev 			hdr.blksz = tmp;
877f4caa8cSMaxim Sobolev 			break;
887f4caa8cSMaxim Sobolev 
897f4caa8cSMaxim Sobolev 		case 'v':
907f4caa8cSMaxim Sobolev 			verbose = 1;
917f4caa8cSMaxim Sobolev 			break;
927f4caa8cSMaxim Sobolev 
937f4caa8cSMaxim Sobolev 		default:
947f4caa8cSMaxim Sobolev 			usage();
957f4caa8cSMaxim Sobolev 			/* Not reached */
967f4caa8cSMaxim Sobolev 		}
977f4caa8cSMaxim Sobolev 	}
987f4caa8cSMaxim Sobolev 	argc -= optind;
997f4caa8cSMaxim Sobolev 	argv += optind;
1007f4caa8cSMaxim Sobolev 
1017f4caa8cSMaxim Sobolev 	if (argc != 1) {
1027f4caa8cSMaxim Sobolev 		usage();
1037f4caa8cSMaxim Sobolev 		/* Not reached */
1047f4caa8cSMaxim Sobolev 	}
1057f4caa8cSMaxim Sobolev 
1067f4caa8cSMaxim Sobolev 	iname = argv[0];
1077f4caa8cSMaxim Sobolev 	if (oname == NULL) {
1087f4caa8cSMaxim Sobolev 		asprintf(&oname, "%s%s", iname, DEFAULT_SUFX);
1097f4caa8cSMaxim Sobolev 		if (oname == NULL) {
1107f4caa8cSMaxim Sobolev 			err(1, "can't allocate memory");
1117f4caa8cSMaxim Sobolev 			/* Not reached */
1127f4caa8cSMaxim Sobolev 		}
1137f4caa8cSMaxim Sobolev 	}
1147f4caa8cSMaxim Sobolev 
1157f4caa8cSMaxim Sobolev 	obuf = safe_malloc(compressBound(hdr.blksz));
1167f4caa8cSMaxim Sobolev 	ibuf = safe_malloc(hdr.blksz);
1177f4caa8cSMaxim Sobolev 
1187f4caa8cSMaxim Sobolev 	signal(SIGHUP, exit);
1197f4caa8cSMaxim Sobolev 	signal(SIGINT, exit);
1207f4caa8cSMaxim Sobolev 	signal(SIGTERM, exit);
1217f4caa8cSMaxim Sobolev 	signal(SIGXCPU, exit);
1227f4caa8cSMaxim Sobolev 	signal(SIGXFSZ, exit);
1237f4caa8cSMaxim Sobolev 	atexit(cleanup);
1247f4caa8cSMaxim Sobolev 
1257f4caa8cSMaxim Sobolev 	if (stat(iname, &sb) != 0) {
1267f4caa8cSMaxim Sobolev 		err(1, "%s", iname);
1277f4caa8cSMaxim Sobolev 		/* Not reached */
1287f4caa8cSMaxim Sobolev 	}
1297f4caa8cSMaxim Sobolev 	hdr.nblocks = sb.st_size / hdr.blksz;
1300b99ac63SMaxim Sobolev 	if ((sb.st_size % hdr.blksz) != 0) {
1310b99ac63SMaxim Sobolev 		if (verbose != 0)
1320b99ac63SMaxim Sobolev 			fprintf(stderr, "file size is not multiple "
1330b99ac63SMaxim Sobolev 			"of %d, padding data\n", hdr.blksz);
1340b99ac63SMaxim Sobolev 		hdr.nblocks++;
1350b99ac63SMaxim Sobolev 	}
1367f4caa8cSMaxim Sobolev 	toc = safe_malloc((hdr.nblocks + 1) * sizeof(*toc));
1377f4caa8cSMaxim Sobolev 
1387f4caa8cSMaxim Sobolev 	fdr = open(iname, O_RDONLY);
1397f4caa8cSMaxim Sobolev 	if (fdr < 0) {
1407f4caa8cSMaxim Sobolev 		err(1, "%s", iname);
1417f4caa8cSMaxim Sobolev 		/* Not reached */
1427f4caa8cSMaxim Sobolev 	}
1437f4caa8cSMaxim Sobolev 	fdw = open(oname, O_WRONLY | O_TRUNC | O_CREAT,
1447f4caa8cSMaxim Sobolev 		   S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1457f4caa8cSMaxim Sobolev 	if (fdw < 0) {
1467f4caa8cSMaxim Sobolev 		err(1, "%s", oname);
1477f4caa8cSMaxim Sobolev 		/* Not reached */
1487f4caa8cSMaxim Sobolev 	}
1497f4caa8cSMaxim Sobolev 	cleanfile = oname;
1507f4caa8cSMaxim Sobolev 
1517f4caa8cSMaxim Sobolev 	/* Prepare header that we will write later when we have index ready. */
1527f4caa8cSMaxim Sobolev 	iov[0].iov_base = (char *)&hdr;
1537f4caa8cSMaxim Sobolev 	iov[0].iov_len = sizeof(hdr);
1547f4caa8cSMaxim Sobolev 	iov[1].iov_base = (char *)toc;
1557f4caa8cSMaxim Sobolev 	iov[1].iov_len = (hdr.nblocks + 1) * sizeof(*toc);
1567f4caa8cSMaxim Sobolev 	offset = iov[0].iov_len + iov[1].iov_len;
1577f4caa8cSMaxim Sobolev 
1587f4caa8cSMaxim Sobolev 	/* Reserve space for header */
1597f4caa8cSMaxim Sobolev 	lseek(fdw, offset, SEEK_SET);
1607f4caa8cSMaxim Sobolev 
1617f4caa8cSMaxim Sobolev 	if (verbose != 0)
162ed9302fdSMaxim Sobolev 		fprintf(stderr, "data size %ju bytes, number of clusters "
163ed9302fdSMaxim Sobolev 		    "%u, index lengh %zu bytes\n", sb.st_size,
1640b99ac63SMaxim Sobolev 		    hdr.nblocks, iov[1].iov_len);
1657f4caa8cSMaxim Sobolev 
1667f4caa8cSMaxim Sobolev 	for(i = 0; i == 0 || ibuf != NULL; i++) {
1677f4caa8cSMaxim Sobolev 		ibuf = readblock(fdr, ibuf, hdr.blksz);
1687f4caa8cSMaxim Sobolev 		if (ibuf != NULL) {
1697f4caa8cSMaxim Sobolev 			destlen = compressBound(hdr.blksz);
1700b99ac63SMaxim Sobolev 			if (compress2(obuf, &destlen, ibuf, hdr.blksz,
1710b99ac63SMaxim Sobolev 			    Z_BEST_COMPRESSION) != Z_OK) {
1720b99ac63SMaxim Sobolev 				errx(1, "can't compress data: compress2() "
1730b99ac63SMaxim Sobolev 				    "failed");
1747f4caa8cSMaxim Sobolev 				/* Not reached */
1757f4caa8cSMaxim Sobolev 			}
1760b99ac63SMaxim Sobolev 			if (verbose != 0)
1770b99ac63SMaxim Sobolev 				fprintf(stderr, "cluster #%d, in %u bytes, "
1780b99ac63SMaxim Sobolev 				    "out %lu bytes\n", i, hdr.blksz, destlen);
1797f4caa8cSMaxim Sobolev 		} else {
1807f4caa8cSMaxim Sobolev 			destlen = DEV_BSIZE - (offset % DEV_BSIZE);
1817f4caa8cSMaxim Sobolev 			memset(obuf, 0, destlen);
1820b99ac63SMaxim Sobolev 			if (verbose != 0)
1830b99ac63SMaxim Sobolev 				fprintf(stderr, "padding data with %lu bytes so "
1840b99ac63SMaxim Sobolev 				    "that file size is multiple of %d\n", destlen,
1850b99ac63SMaxim Sobolev 				    DEV_BSIZE);
1867f4caa8cSMaxim Sobolev 		}
1877f4caa8cSMaxim Sobolev 		if (write(fdw, obuf, destlen) < 0) {
1887f4caa8cSMaxim Sobolev 			err(1, "%s", oname);
1897f4caa8cSMaxim Sobolev 			/* Not reached */
1907f4caa8cSMaxim Sobolev 		}
1917f4caa8cSMaxim Sobolev 		toc[i] = htobe64(offset);
1927f4caa8cSMaxim Sobolev 		offset += destlen;
1937f4caa8cSMaxim Sobolev 	}
1947f4caa8cSMaxim Sobolev 	close(fdr);
1957f4caa8cSMaxim Sobolev 
1967f4caa8cSMaxim Sobolev 	if (verbose != 0)
197ed9302fdSMaxim Sobolev 		fprintf(stderr, "compressed data to %ju bytes, saved %lld "
1980b99ac63SMaxim Sobolev 		    "bytes, %.2f%% decrease.\n", offset, (long long)(sb.st_size - offset),
1990b99ac63SMaxim Sobolev 		    100.0 * (long long)(sb.st_size - offset) / (float)sb.st_size);
2007f4caa8cSMaxim Sobolev 
2017f4caa8cSMaxim Sobolev 	/* Convert to big endian */
2027f4caa8cSMaxim Sobolev 	hdr.blksz = htonl(hdr.blksz);
2037f4caa8cSMaxim Sobolev 	hdr.nblocks = htonl(hdr.nblocks);
2047f4caa8cSMaxim Sobolev 	/* Write headers into pre-allocated space */
2057f4caa8cSMaxim Sobolev 	lseek(fdw, 0, SEEK_SET);
2067f4caa8cSMaxim Sobolev 	if (writev(fdw, iov, 2) < 0) {
2077f4caa8cSMaxim Sobolev 		err(1, "%s", oname);
2087f4caa8cSMaxim Sobolev 		/* Not reached */
2097f4caa8cSMaxim Sobolev 	}
2107f4caa8cSMaxim Sobolev 	cleanfile = NULL;
2117f4caa8cSMaxim Sobolev 	close(fdw);
2127f4caa8cSMaxim Sobolev 
2137f4caa8cSMaxim Sobolev 	exit(0);
2147f4caa8cSMaxim Sobolev }
2157f4caa8cSMaxim Sobolev 
2167f4caa8cSMaxim Sobolev static char *
2170b99ac63SMaxim Sobolev readblock(int fd, char *ibuf, u_int32_t clstsize)
2180b99ac63SMaxim Sobolev {
2197f4caa8cSMaxim Sobolev 	int numread;
2207f4caa8cSMaxim Sobolev 
2217f4caa8cSMaxim Sobolev 	bzero(ibuf, clstsize);
2227f4caa8cSMaxim Sobolev 	numread = read(fd, ibuf, clstsize);
2237f4caa8cSMaxim Sobolev 	if (numread < 0) {
2247f4caa8cSMaxim Sobolev 		err(1, "read() failed");
2257f4caa8cSMaxim Sobolev 		/* Not reached */
2267f4caa8cSMaxim Sobolev 	}
2277f4caa8cSMaxim Sobolev 	if (numread == 0) {
2287f4caa8cSMaxim Sobolev 		return NULL;
2297f4caa8cSMaxim Sobolev 	}
2307f4caa8cSMaxim Sobolev 	return ibuf;
2317f4caa8cSMaxim Sobolev }
2327f4caa8cSMaxim Sobolev 
2337f4caa8cSMaxim Sobolev static void
2340b99ac63SMaxim Sobolev usage(void)
2350b99ac63SMaxim Sobolev {
2367f4caa8cSMaxim Sobolev 
2377f4caa8cSMaxim Sobolev 	fprintf(stderr, "usage: mkuzip [-v] [-o outfile] [-s cluster_size] infile\n");
2387f4caa8cSMaxim Sobolev 	exit(1);
2397f4caa8cSMaxim Sobolev }
2407f4caa8cSMaxim Sobolev 
2417f4caa8cSMaxim Sobolev static void *
2420b99ac63SMaxim Sobolev safe_malloc(size_t size)
2430b99ac63SMaxim Sobolev {
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
2550b99ac63SMaxim Sobolev cleanup(void)
2560b99ac63SMaxim Sobolev {
2577f4caa8cSMaxim Sobolev 
2587f4caa8cSMaxim Sobolev 	if (cleanfile != NULL)
2597f4caa8cSMaxim Sobolev 		unlink(cleanfile);
2607f4caa8cSMaxim Sobolev }
261