xref: /freebsd/usr.bin/mkuzip/mkuzip.c (revision d056fa046c6a91b90cd98165face0e42a33a5173)
1 /*
2  * ----------------------------------------------------------------------------
3  * "THE BEER-WARE LICENSE" (Revision 42):
4  * <sobomax@FreeBSD.ORG> wrote this file. As long as you retain this notice you
5  * can do whatever you want with this stuff. If we meet some day, and you think
6  * this stuff is worth it, you can buy me a beer in return.       Maxim Sobolev
7  * ----------------------------------------------------------------------------
8  *
9  * $FreeBSD$
10  *
11  */
12 
13 #include <sys/types.h>
14 #include <sys/endian.h>
15 #include <sys/param.h>
16 #include <sys/stat.h>
17 #include <sys/uio.h>
18 #include <netinet/in.h>
19 #include <zlib.h>
20 #include <err.h>
21 #include <fcntl.h>
22 #include <signal.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 
28 #define CLSTSIZE	16384
29 #define DEFAULT_SUFX	".uzip"
30 
31 #define CLOOP_MAGIC_LEN 128
32 static char CLOOP_MAGIC_START[] = "#!/bin/sh\n#V2.0 Format\n"
33     "m=geom_uzip\n(kldstat -m $m 2>&-||kldload $m)>&-&&"
34     "mount_cd9660 /dev/`mdconfig -af $0`.uzip $1\nexit $?\n";
35 
36 static char *readblock(int, char *, u_int32_t);
37 static void usage(void);
38 static void *safe_malloc(size_t);
39 static void cleanup(void);
40 
41 static char *cleanfile = NULL;
42 
43 int main(int argc, char **argv)
44 {
45 	char *iname, *oname, *obuf, *ibuf;
46 	uint64_t *toc;
47 	int fdr, fdw, i, opt, verbose, tmp;
48 	struct iovec iov[2];
49 	struct stat sb;
50 	uLongf destlen;
51 	uint64_t offset;
52 	struct cloop_header {
53 		char magic[CLOOP_MAGIC_LEN];    /* cloop magic */
54 		uint32_t blksz;                 /* block size */
55 		uint32_t nblocks;               /* number of blocks */
56 	} hdr;
57 
58 	memset(&hdr, 0, sizeof(hdr));
59 	hdr.blksz = CLSTSIZE;
60 	strcpy(hdr.magic, CLOOP_MAGIC_START);
61 	oname = NULL;
62 	verbose = 0;
63 
64 	while((opt = getopt(argc, argv, "o:s:v")) != -1) {
65 		switch(opt) {
66 		case 'o':
67 			oname = optarg;
68 			break;
69 
70 		case 's':
71 			tmp = atoi(optarg);
72 			if (tmp <= 0) {
73 				errx(1, "invalid cluster size specified: %s",
74 				    optarg);
75 				/* Not reached */
76 			}
77 			if (tmp % DEV_BSIZE != 0) {
78 				errx(1, "cluster size should be multiple of %d",
79 				    DEV_BSIZE);
80 				/* Not reached */
81 			}
82 			if (compressBound(tmp) > MAXPHYS) {
83 				errx(1, "cluster size is too large");
84 				    /* Not reached */
85 			}
86 			hdr.blksz = tmp;
87 			break;
88 
89 		case 'v':
90 			verbose = 1;
91 			break;
92 
93 		default:
94 			usage();
95 			/* Not reached */
96 		}
97 	}
98 	argc -= optind;
99 	argv += optind;
100 
101 	if (argc != 1) {
102 		usage();
103 		/* Not reached */
104 	}
105 
106 	iname = argv[0];
107 	if (oname == NULL) {
108 		asprintf(&oname, "%s%s", iname, DEFAULT_SUFX);
109 		if (oname == NULL) {
110 			err(1, "can't allocate memory");
111 			/* Not reached */
112 		}
113 	}
114 
115 	obuf = safe_malloc(compressBound(hdr.blksz));
116 	ibuf = safe_malloc(hdr.blksz);
117 
118 	signal(SIGHUP, exit);
119 	signal(SIGINT, exit);
120 	signal(SIGTERM, exit);
121 	signal(SIGXCPU, exit);
122 	signal(SIGXFSZ, exit);
123 	atexit(cleanup);
124 
125 	if (stat(iname, &sb) != 0) {
126 		err(1, "stat(%s)", iname);
127 		/* Not reached */
128 	}
129 	hdr.nblocks = sb.st_size / hdr.blksz;
130 	if ((sb.st_size % hdr.blksz) != 0) {
131 		if (verbose != 0)
132 			fprintf(stderr, "file size is not multiple "
133 			"of %d, padding data\n", hdr.blksz);
134 		hdr.nblocks++;
135 	}
136 	toc = safe_malloc((hdr.nblocks + 1) * sizeof(*toc));
137 
138 	fdr = open(iname, O_RDONLY);
139 	if (fdr < 0) {
140 		err(1, "open(%s)", iname);
141 		/* Not reached */
142 	}
143 	fdw = open(oname, O_WRONLY | O_TRUNC | O_CREAT,
144 		   S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
145 	if (fdw < 0) {
146 		err(1, "open(%s)", oname);
147 		/* Not reached */
148 	}
149 	cleanfile = oname;
150 
151 	/* Prepare header that we will write later when we have index ready. */
152 	iov[0].iov_base = (char *)&hdr;
153 	iov[0].iov_len = sizeof(hdr);
154 	iov[1].iov_base = (char *)toc;
155 	iov[1].iov_len = (hdr.nblocks + 1) * sizeof(*toc);
156 	offset = iov[0].iov_len + iov[1].iov_len;
157 
158 	/* Reserve space for header */
159 	lseek(fdw, offset, SEEK_SET);
160 
161 	if (verbose != 0)
162 		fprintf(stderr, "data size %ju bytes, number of clusters "
163 		    "%u, index length %zu bytes\n", sb.st_size,
164 		    hdr.nblocks, iov[1].iov_len);
165 
166 	for(i = 0; i == 0 || ibuf != NULL; i++) {
167 		ibuf = readblock(fdr, ibuf, hdr.blksz);
168 		if (ibuf != NULL) {
169 			destlen = compressBound(hdr.blksz);
170 			if (compress2(obuf, &destlen, ibuf, hdr.blksz,
171 			    Z_BEST_COMPRESSION) != Z_OK) {
172 				errx(1, "can't compress data: compress2() "
173 				    "failed");
174 				/* Not reached */
175 			}
176 			if (verbose != 0)
177 				fprintf(stderr, "cluster #%d, in %u bytes, "
178 				    "out %lu bytes\n", i, hdr.blksz, destlen);
179 		} else {
180 			destlen = DEV_BSIZE - (offset % DEV_BSIZE);
181 			memset(obuf, 0, destlen);
182 			if (verbose != 0)
183 				fprintf(stderr, "padding data with %lu bytes so "
184 				    "that file size is multiple of %d\n", destlen,
185 				    DEV_BSIZE);
186 		}
187 		if (write(fdw, obuf, destlen) < 0) {
188 			err(1, "write(%s)", oname);
189 			/* Not reached */
190 		}
191 		toc[i] = htobe64(offset);
192 		offset += destlen;
193 	}
194 	close(fdr);
195 
196 	if (verbose != 0)
197 		fprintf(stderr, "compressed data to %ju bytes, saved %lld "
198 		    "bytes, %.2f%% decrease.\n", offset, (long long)(sb.st_size - offset),
199 		    100.0 * (long long)(sb.st_size - offset) / (float)sb.st_size);
200 
201 	/* Convert to big endian */
202 	hdr.blksz = htonl(hdr.blksz);
203 	hdr.nblocks = htonl(hdr.nblocks);
204 	/* Write headers into pre-allocated space */
205 	lseek(fdw, 0, SEEK_SET);
206 	if (writev(fdw, iov, 2) < 0) {
207 		err(1, "writev(%s)", oname);
208 		/* Not reached */
209 	}
210 	cleanfile = NULL;
211 	close(fdw);
212 
213 	exit(0);
214 }
215 
216 static char *
217 readblock(int fd, char *ibuf, u_int32_t clstsize)
218 {
219 	int numread;
220 
221 	bzero(ibuf, clstsize);
222 	numread = read(fd, ibuf, clstsize);
223 	if (numread < 0) {
224 		err(1, "read() failed");
225 		/* Not reached */
226 	}
227 	if (numread == 0) {
228 		return NULL;
229 	}
230 	return ibuf;
231 }
232 
233 static void
234 usage(void)
235 {
236 
237 	fprintf(stderr, "usage: mkuzip [-v] [-o outfile] [-s cluster_size] infile\n");
238 	exit(1);
239 }
240 
241 static void *
242 safe_malloc(size_t size)
243 {
244 	void *retval;
245 
246 	retval = malloc(size);
247 	if (retval == NULL) {
248 		err(1, "can't allocate memory");
249 		/* Not reached */
250 	}
251 	return retval;
252 }
253 
254 static void
255 cleanup(void)
256 {
257 
258 	if (cleanfile != NULL)
259 		unlink(cleanfile);
260 }
261