xref: /freebsd/usr.bin/mkuzip/mkuzip.c (revision 27d0a1a493460aa1e4f3e11c566fe2368f091ba4)
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>
1427d0a1a4SMax Khon #include <sys/disk.h>
157f4caa8cSMaxim Sobolev #include <sys/endian.h>
167f4caa8cSMaxim Sobolev #include <sys/param.h>
177f4caa8cSMaxim Sobolev #include <sys/stat.h>
187f4caa8cSMaxim Sobolev #include <sys/uio.h>
197f4caa8cSMaxim Sobolev #include <netinet/in.h>
207f4caa8cSMaxim Sobolev #include <zlib.h>
217f4caa8cSMaxim Sobolev #include <err.h>
227f4caa8cSMaxim Sobolev #include <fcntl.h>
237f4caa8cSMaxim Sobolev #include <signal.h>
247f4caa8cSMaxim Sobolev #include <stdio.h>
257f4caa8cSMaxim Sobolev #include <stdlib.h>
267f4caa8cSMaxim Sobolev #include <string.h>
277f4caa8cSMaxim Sobolev #include <unistd.h>
287f4caa8cSMaxim Sobolev 
297f4caa8cSMaxim Sobolev #define CLSTSIZE	16384
307f4caa8cSMaxim Sobolev #define DEFAULT_SUFX	".uzip"
317f4caa8cSMaxim Sobolev 
327f4caa8cSMaxim Sobolev #define CLOOP_MAGIC_LEN 128
337f4caa8cSMaxim Sobolev static char CLOOP_MAGIC_START[] = "#!/bin/sh\n#V2.0 Format\n"
345cf3bf70SMax Khon     "m=geom_uzip\n(kldstat -m $m 2>&-||kldload $m)>&-&&"
357f4caa8cSMaxim Sobolev     "mount_cd9660 /dev/`mdconfig -af $0`.uzip $1\nexit $?\n";
367f4caa8cSMaxim Sobolev 
377f4caa8cSMaxim Sobolev static char *readblock(int, char *, u_int32_t);
387f4caa8cSMaxim Sobolev static void usage(void);
397f4caa8cSMaxim Sobolev static void *safe_malloc(size_t);
407f4caa8cSMaxim Sobolev static void cleanup(void);
417f4caa8cSMaxim Sobolev 
427f4caa8cSMaxim Sobolev static char *cleanfile = NULL;
437f4caa8cSMaxim Sobolev 
447f4caa8cSMaxim Sobolev int main(int argc, char **argv)
457f4caa8cSMaxim Sobolev {
467f4caa8cSMaxim Sobolev 	char *iname, *oname, *obuf, *ibuf;
477f4caa8cSMaxim Sobolev 	uint64_t *toc;
487f4caa8cSMaxim Sobolev 	int fdr, fdw, i, opt, verbose, tmp;
497f4caa8cSMaxim Sobolev 	struct iovec iov[2];
507f4caa8cSMaxim Sobolev 	struct stat sb;
517f4caa8cSMaxim Sobolev 	uLongf destlen;
527f4caa8cSMaxim Sobolev 	uint64_t offset;
537f4caa8cSMaxim Sobolev 	struct cloop_header {
547f4caa8cSMaxim Sobolev 		char magic[CLOOP_MAGIC_LEN];    /* cloop magic */
557f4caa8cSMaxim Sobolev 		uint32_t blksz;                 /* block size */
567f4caa8cSMaxim Sobolev 		uint32_t nblocks;               /* number of blocks */
577f4caa8cSMaxim Sobolev 	} hdr;
587f4caa8cSMaxim Sobolev 
597f4caa8cSMaxim Sobolev 	memset(&hdr, 0, sizeof(hdr));
607f4caa8cSMaxim Sobolev 	hdr.blksz = CLSTSIZE;
617f4caa8cSMaxim Sobolev 	strcpy(hdr.magic, CLOOP_MAGIC_START);
627f4caa8cSMaxim Sobolev 	oname = NULL;
637f4caa8cSMaxim Sobolev 	verbose = 0;
647f4caa8cSMaxim Sobolev 
657f4caa8cSMaxim Sobolev 	while((opt = getopt(argc, argv, "o:s:v")) != -1) {
667f4caa8cSMaxim Sobolev 		switch(opt) {
677f4caa8cSMaxim Sobolev 		case 'o':
687f4caa8cSMaxim Sobolev 			oname = optarg;
697f4caa8cSMaxim Sobolev 			break;
707f4caa8cSMaxim Sobolev 
717f4caa8cSMaxim Sobolev 		case 's':
727f4caa8cSMaxim Sobolev 			tmp = atoi(optarg);
737f4caa8cSMaxim Sobolev 			if (tmp <= 0) {
747f4caa8cSMaxim Sobolev 				errx(1, "invalid cluster size specified: %s",
757f4caa8cSMaxim Sobolev 				    optarg);
767f4caa8cSMaxim Sobolev 				/* Not reached */
777f4caa8cSMaxim Sobolev 			}
787f4caa8cSMaxim Sobolev 			if (tmp % DEV_BSIZE != 0) {
797f4caa8cSMaxim Sobolev 				errx(1, "cluster size should be multiple of %d",
807f4caa8cSMaxim Sobolev 				    DEV_BSIZE);
817f4caa8cSMaxim Sobolev 				/* Not reached */
827f4caa8cSMaxim Sobolev 			}
830b99ac63SMaxim Sobolev 			if (compressBound(tmp) > MAXPHYS) {
840b99ac63SMaxim Sobolev 				errx(1, "cluster size is too large");
857f4caa8cSMaxim Sobolev 				    /* Not reached */
867f4caa8cSMaxim Sobolev 			}
877f4caa8cSMaxim Sobolev 			hdr.blksz = tmp;
887f4caa8cSMaxim Sobolev 			break;
897f4caa8cSMaxim Sobolev 
907f4caa8cSMaxim Sobolev 		case 'v':
917f4caa8cSMaxim Sobolev 			verbose = 1;
927f4caa8cSMaxim Sobolev 			break;
937f4caa8cSMaxim Sobolev 
947f4caa8cSMaxim Sobolev 		default:
957f4caa8cSMaxim Sobolev 			usage();
967f4caa8cSMaxim Sobolev 			/* Not reached */
977f4caa8cSMaxim Sobolev 		}
987f4caa8cSMaxim Sobolev 	}
997f4caa8cSMaxim Sobolev 	argc -= optind;
1007f4caa8cSMaxim Sobolev 	argv += optind;
1017f4caa8cSMaxim Sobolev 
1027f4caa8cSMaxim Sobolev 	if (argc != 1) {
1037f4caa8cSMaxim Sobolev 		usage();
1047f4caa8cSMaxim Sobolev 		/* Not reached */
1057f4caa8cSMaxim Sobolev 	}
1067f4caa8cSMaxim Sobolev 
1077f4caa8cSMaxim Sobolev 	iname = argv[0];
1087f4caa8cSMaxim Sobolev 	if (oname == NULL) {
1097f4caa8cSMaxim Sobolev 		asprintf(&oname, "%s%s", iname, DEFAULT_SUFX);
1107f4caa8cSMaxim Sobolev 		if (oname == NULL) {
1117f4caa8cSMaxim Sobolev 			err(1, "can't allocate memory");
1127f4caa8cSMaxim Sobolev 			/* Not reached */
1137f4caa8cSMaxim Sobolev 		}
1147f4caa8cSMaxim Sobolev 	}
1157f4caa8cSMaxim Sobolev 
1167f4caa8cSMaxim Sobolev 	obuf = safe_malloc(compressBound(hdr.blksz));
1177f4caa8cSMaxim Sobolev 	ibuf = safe_malloc(hdr.blksz);
1187f4caa8cSMaxim Sobolev 
1197f4caa8cSMaxim Sobolev 	signal(SIGHUP, exit);
1207f4caa8cSMaxim Sobolev 	signal(SIGINT, exit);
1217f4caa8cSMaxim Sobolev 	signal(SIGTERM, exit);
1227f4caa8cSMaxim Sobolev 	signal(SIGXCPU, exit);
1237f4caa8cSMaxim Sobolev 	signal(SIGXFSZ, exit);
1247f4caa8cSMaxim Sobolev 	atexit(cleanup);
1257f4caa8cSMaxim Sobolev 
12627d0a1a4SMax Khon 	fdr = open(iname, O_RDONLY);
12727d0a1a4SMax Khon 	if (fdr < 0) {
12827d0a1a4SMax Khon 		err(1, "open(%s)", iname);
1297f4caa8cSMaxim Sobolev 		/* Not reached */
1307f4caa8cSMaxim Sobolev 	}
13127d0a1a4SMax Khon 	if (fstat(fdr, &sb) != 0) {
13227d0a1a4SMax Khon 		err(1, "fstat(%s)", iname);
13327d0a1a4SMax Khon 		/* Not reached */
13427d0a1a4SMax Khon 	}
13527d0a1a4SMax Khon 	if (S_ISCHR(sb.st_mode)) {
13627d0a1a4SMax Khon 		off_t ms;
13727d0a1a4SMax Khon 
13827d0a1a4SMax Khon 		if (ioctl(fdr, DIOCGMEDIASIZE, &ms) < 0) {
13927d0a1a4SMax Khon 			err(1, "ioctl(DIOCGMEDIASIZE)");
14027d0a1a4SMax Khon 			/* Not reached */
14127d0a1a4SMax Khon 		}
14227d0a1a4SMax Khon 		sb.st_size = ms;
14327d0a1a4SMax Khon 	} else if (!S_ISREG(sb.st_mode)) {
14427d0a1a4SMax Khon 		fprintf(stderr, "%s: not a character device or regular file\n",
14527d0a1a4SMax Khon 			iname);
14627d0a1a4SMax Khon 		exit(1);
14727d0a1a4SMax Khon 	}
1487f4caa8cSMaxim Sobolev 	hdr.nblocks = sb.st_size / hdr.blksz;
1490b99ac63SMaxim Sobolev 	if ((sb.st_size % hdr.blksz) != 0) {
1500b99ac63SMaxim Sobolev 		if (verbose != 0)
1510b99ac63SMaxim Sobolev 			fprintf(stderr, "file size is not multiple "
1520b99ac63SMaxim Sobolev 			"of %d, padding data\n", hdr.blksz);
1530b99ac63SMaxim Sobolev 		hdr.nblocks++;
1540b99ac63SMaxim Sobolev 	}
1557f4caa8cSMaxim Sobolev 	toc = safe_malloc((hdr.nblocks + 1) * sizeof(*toc));
1567f4caa8cSMaxim Sobolev 
1577f4caa8cSMaxim Sobolev 	fdw = open(oname, O_WRONLY | O_TRUNC | O_CREAT,
1585cf3bf70SMax Khon 		   S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
1597f4caa8cSMaxim Sobolev 	if (fdw < 0) {
160d72d8f53SPawel Jakub Dawidek 		err(1, "open(%s)", oname);
1617f4caa8cSMaxim Sobolev 		/* Not reached */
1627f4caa8cSMaxim Sobolev 	}
1637f4caa8cSMaxim Sobolev 	cleanfile = oname;
1647f4caa8cSMaxim Sobolev 
1657f4caa8cSMaxim Sobolev 	/* Prepare header that we will write later when we have index ready. */
1667f4caa8cSMaxim Sobolev 	iov[0].iov_base = (char *)&hdr;
1677f4caa8cSMaxim Sobolev 	iov[0].iov_len = sizeof(hdr);
1687f4caa8cSMaxim Sobolev 	iov[1].iov_base = (char *)toc;
1697f4caa8cSMaxim Sobolev 	iov[1].iov_len = (hdr.nblocks + 1) * sizeof(*toc);
1707f4caa8cSMaxim Sobolev 	offset = iov[0].iov_len + iov[1].iov_len;
1717f4caa8cSMaxim Sobolev 
1727f4caa8cSMaxim Sobolev 	/* Reserve space for header */
1737f4caa8cSMaxim Sobolev 	lseek(fdw, offset, SEEK_SET);
1747f4caa8cSMaxim Sobolev 
1757f4caa8cSMaxim Sobolev 	if (verbose != 0)
176ed9302fdSMaxim Sobolev 		fprintf(stderr, "data size %ju bytes, number of clusters "
1775cf3bf70SMax Khon 		    "%u, index length %zu bytes\n", sb.st_size,
1780b99ac63SMaxim Sobolev 		    hdr.nblocks, iov[1].iov_len);
1797f4caa8cSMaxim Sobolev 
1807f4caa8cSMaxim Sobolev 	for(i = 0; i == 0 || ibuf != NULL; i++) {
1817f4caa8cSMaxim Sobolev 		ibuf = readblock(fdr, ibuf, hdr.blksz);
1827f4caa8cSMaxim Sobolev 		if (ibuf != NULL) {
1837f4caa8cSMaxim Sobolev 			destlen = compressBound(hdr.blksz);
1840b99ac63SMaxim Sobolev 			if (compress2(obuf, &destlen, ibuf, hdr.blksz,
1850b99ac63SMaxim Sobolev 			    Z_BEST_COMPRESSION) != Z_OK) {
1860b99ac63SMaxim Sobolev 				errx(1, "can't compress data: compress2() "
1870b99ac63SMaxim Sobolev 				    "failed");
1887f4caa8cSMaxim Sobolev 				/* Not reached */
1897f4caa8cSMaxim Sobolev 			}
1900b99ac63SMaxim Sobolev 			if (verbose != 0)
1910b99ac63SMaxim Sobolev 				fprintf(stderr, "cluster #%d, in %u bytes, "
1920b99ac63SMaxim Sobolev 				    "out %lu bytes\n", i, hdr.blksz, destlen);
1937f4caa8cSMaxim Sobolev 		} else {
1947f4caa8cSMaxim Sobolev 			destlen = DEV_BSIZE - (offset % DEV_BSIZE);
1957f4caa8cSMaxim Sobolev 			memset(obuf, 0, destlen);
1960b99ac63SMaxim Sobolev 			if (verbose != 0)
1970b99ac63SMaxim Sobolev 				fprintf(stderr, "padding data with %lu bytes so "
1980b99ac63SMaxim Sobolev 				    "that file size is multiple of %d\n", destlen,
1990b99ac63SMaxim Sobolev 				    DEV_BSIZE);
2007f4caa8cSMaxim Sobolev 		}
2017f4caa8cSMaxim Sobolev 		if (write(fdw, obuf, destlen) < 0) {
202d72d8f53SPawel Jakub Dawidek 			err(1, "write(%s)", oname);
2037f4caa8cSMaxim Sobolev 			/* Not reached */
2047f4caa8cSMaxim Sobolev 		}
2057f4caa8cSMaxim Sobolev 		toc[i] = htobe64(offset);
2067f4caa8cSMaxim Sobolev 		offset += destlen;
2077f4caa8cSMaxim Sobolev 	}
2087f4caa8cSMaxim Sobolev 	close(fdr);
2097f4caa8cSMaxim Sobolev 
2107f4caa8cSMaxim Sobolev 	if (verbose != 0)
211ed9302fdSMaxim Sobolev 		fprintf(stderr, "compressed data to %ju bytes, saved %lld "
2120b99ac63SMaxim Sobolev 		    "bytes, %.2f%% decrease.\n", offset, (long long)(sb.st_size - offset),
2130b99ac63SMaxim Sobolev 		    100.0 * (long long)(sb.st_size - offset) / (float)sb.st_size);
2147f4caa8cSMaxim Sobolev 
2157f4caa8cSMaxim Sobolev 	/* Convert to big endian */
2167f4caa8cSMaxim Sobolev 	hdr.blksz = htonl(hdr.blksz);
2177f4caa8cSMaxim Sobolev 	hdr.nblocks = htonl(hdr.nblocks);
2187f4caa8cSMaxim Sobolev 	/* Write headers into pre-allocated space */
2197f4caa8cSMaxim Sobolev 	lseek(fdw, 0, SEEK_SET);
2207f4caa8cSMaxim Sobolev 	if (writev(fdw, iov, 2) < 0) {
221d72d8f53SPawel Jakub Dawidek 		err(1, "writev(%s)", oname);
2227f4caa8cSMaxim Sobolev 		/* Not reached */
2237f4caa8cSMaxim Sobolev 	}
2247f4caa8cSMaxim Sobolev 	cleanfile = NULL;
2257f4caa8cSMaxim Sobolev 	close(fdw);
2267f4caa8cSMaxim Sobolev 
2277f4caa8cSMaxim Sobolev 	exit(0);
2287f4caa8cSMaxim Sobolev }
2297f4caa8cSMaxim Sobolev 
2307f4caa8cSMaxim Sobolev static char *
2310b99ac63SMaxim Sobolev readblock(int fd, char *ibuf, u_int32_t clstsize)
2320b99ac63SMaxim Sobolev {
2337f4caa8cSMaxim Sobolev 	int numread;
2347f4caa8cSMaxim Sobolev 
2357f4caa8cSMaxim Sobolev 	bzero(ibuf, clstsize);
2367f4caa8cSMaxim Sobolev 	numread = read(fd, ibuf, clstsize);
2377f4caa8cSMaxim Sobolev 	if (numread < 0) {
2387f4caa8cSMaxim Sobolev 		err(1, "read() failed");
2397f4caa8cSMaxim Sobolev 		/* Not reached */
2407f4caa8cSMaxim Sobolev 	}
2417f4caa8cSMaxim Sobolev 	if (numread == 0) {
2427f4caa8cSMaxim Sobolev 		return NULL;
2437f4caa8cSMaxim Sobolev 	}
2447f4caa8cSMaxim Sobolev 	return ibuf;
2457f4caa8cSMaxim Sobolev }
2467f4caa8cSMaxim Sobolev 
2477f4caa8cSMaxim Sobolev static void
2480b99ac63SMaxim Sobolev usage(void)
2490b99ac63SMaxim Sobolev {
2507f4caa8cSMaxim Sobolev 
2517f4caa8cSMaxim Sobolev 	fprintf(stderr, "usage: mkuzip [-v] [-o outfile] [-s cluster_size] infile\n");
2527f4caa8cSMaxim Sobolev 	exit(1);
2537f4caa8cSMaxim Sobolev }
2547f4caa8cSMaxim Sobolev 
2557f4caa8cSMaxim Sobolev static void *
2560b99ac63SMaxim Sobolev safe_malloc(size_t size)
2570b99ac63SMaxim Sobolev {
2587f4caa8cSMaxim Sobolev 	void *retval;
2597f4caa8cSMaxim Sobolev 
2607f4caa8cSMaxim Sobolev 	retval = malloc(size);
2617f4caa8cSMaxim Sobolev 	if (retval == NULL) {
2627f4caa8cSMaxim Sobolev 		err(1, "can't allocate memory");
2637f4caa8cSMaxim Sobolev 		/* Not reached */
2647f4caa8cSMaxim Sobolev 	}
2657f4caa8cSMaxim Sobolev 	return retval;
2667f4caa8cSMaxim Sobolev }
2677f4caa8cSMaxim Sobolev 
2687f4caa8cSMaxim Sobolev static void
2690b99ac63SMaxim Sobolev cleanup(void)
2700b99ac63SMaxim Sobolev {
2717f4caa8cSMaxim Sobolev 
2727f4caa8cSMaxim Sobolev 	if (cleanfile != NULL)
2737f4caa8cSMaxim Sobolev 		unlink(cleanfile);
2747f4caa8cSMaxim Sobolev }
275