1229538Sray/*
2229538Sray * ----------------------------------------------------------------------------
3229538Sray * Derived from mkuzip.c by Aleksandr Rybalko <ray@ddteam.net>
4229538Sray * ----------------------------------------------------------------------------
5229538Sray * "THE BEER-WARE LICENSE" (Revision 42):
6229538Sray * <sobomax@FreeBSD.ORG> wrote this file. As long as you retain this notice you
7229538Sray * can do whatever you want with this stuff. If we meet some day, and you think
8229538Sray * this stuff is worth it, you can buy me a beer in return.       Maxim Sobolev
9229538Sray * ----------------------------------------------------------------------------
10229538Sray *
11229538Sray * $FreeBSD$
12229538Sray *
13229538Sray */
14229538Sray
15229538Sray#include <sys/disk.h>
16229538Sray#include <sys/endian.h>
17229538Sray#include <sys/param.h>
18229538Sray#include <sys/stat.h>
19229538Sray#include <sys/uio.h>
20229538Sray#include <netinet/in.h>
21229538Sray#include <err.h>
22229538Sray#include <fcntl.h>
23229538Sray#include <stdio.h>
24229538Sray#include <stdlib.h>
25229538Sray#include <string.h>
26229538Sray#include <unistd.h>
27229538Sray
28229538Sray#include <lzma.h>
29229538Sray
30229538Sray#define CLSTSIZE	16384
31229538Sray#define DEFAULT_SUFX	".ulzma"
32229538Sray
33229538Sray#define USED_BLOCKSIZE DEV_BSIZE
34229538Sray
35229538Sray#define CLOOP_MAGIC_LEN 128
36229538Sray/* Format L3.0, since we move to XZ API */
37229538Sraystatic char CLOOP_MAGIC_START[] =
38229538Sray    "#!/bin/sh\n"
39229538Sray    "#L3.0\n"
40229538Sray    "n=uncompress\n"
41229538Sray    "m=geom_$n\n"
42229538Sray    "(kldstat -m $m 2>&-||kldload $m)>&-&&"
43229538Sray	"mount_cd9660 /dev/`mdconfig -af $0`.$n $1\n"
44229538Sray    "exit $?\n";
45229538Sray
46229538Sraystatic char *readblock(int, char *, u_int32_t);
47229538Sraystatic void usage(void);
48229538Sraystatic void *safe_malloc(size_t);
49229538Sraystatic void cleanup(void);
50229538Sray
51229538Sraystatic char *cleanfile = NULL;
52229538Sray
53229538Srayint main(int argc, char **argv)
54229538Sray{
55229538Sray	char *iname, *oname, *obuf, *ibuf;
56229538Sray	int fdr, fdw, i, opt, verbose, tmp;
57229538Sray	struct iovec iov[2];
58229538Sray	struct stat sb;
59229538Sray	uint32_t destlen;
60229538Sray	uint64_t offset;
61229538Sray	uint64_t *toc;
62229538Sray	lzma_filter filters[2];
63229538Sray	lzma_options_lzma opt_lzma;
64229538Sray	lzma_ret ret;
65229538Sray	lzma_stream strm = LZMA_STREAM_INIT;
66229538Sray	struct cloop_header {
67229538Sray		char magic[CLOOP_MAGIC_LEN];    /* cloop magic */
68229538Sray		uint32_t blksz;                 /* block size */
69229538Sray		uint32_t nblocks;               /* number of blocks */
70229538Sray	} hdr;
71229538Sray
72229538Sray	memset(&hdr, 0, sizeof(hdr));
73229538Sray	hdr.blksz = CLSTSIZE;
74229538Sray	strcpy(hdr.magic, CLOOP_MAGIC_START);
75229538Sray	oname = NULL;
76229538Sray	verbose = 0;
77229538Sray
78229538Sray	while((opt = getopt(argc, argv, "o:s:v")) != -1) {
79229538Sray		switch(opt) {
80229538Sray		case 'o':
81229538Sray			oname = optarg;
82229538Sray			break;
83229538Sray
84229538Sray		case 's':
85229538Sray			tmp = atoi(optarg);
86229538Sray			if (tmp <= 0) {
87229538Sray				errx(1,
88229538Sray				    "invalid cluster size specified: %s",
89229538Sray				    optarg);
90229538Sray				/* Not reached */
91229538Sray			}
92229538Sray			if (tmp % USED_BLOCKSIZE != 0) {
93229538Sray				errx(1,
94229538Sray				    "cluster size should be multiple of %d",
95229538Sray				    USED_BLOCKSIZE);
96229538Sray				/* Not reached */
97229538Sray			}
98229538Sray			if ( tmp > MAXPHYS) {
99229538Sray				errx(1, "cluster size is too large");
100229538Sray				    /* Not reached */
101229538Sray			}
102229538Sray			hdr.blksz = tmp;
103229538Sray			break;
104229538Sray
105229538Sray		case 'v':
106229538Sray			verbose = 1;
107229538Sray			break;
108229538Sray
109229538Sray		default:
110229538Sray			usage();
111229538Sray			/* Not reached */
112229538Sray		}
113229538Sray	}
114229538Sray	argc -= optind;
115229538Sray	argv += optind;
116229538Sray
117229538Sray	if (argc != 1) {
118229538Sray		usage();
119229538Sray		/* Not reached */
120229538Sray	}
121229538Sray
122229538Sray	iname = argv[0];
123229538Sray	if (oname == NULL) {
124229538Sray		asprintf(&oname, "%s%s", iname, DEFAULT_SUFX);
125229538Sray		if (oname == NULL) {
126229538Sray			err(1, "can't allocate memory");
127229538Sray			/* Not reached */
128229538Sray		}
129229538Sray	}
130229538Sray
131229538Sray	obuf = safe_malloc(hdr.blksz*2);
132229538Sray	ibuf = safe_malloc(hdr.blksz);
133229538Sray
134229538Sray	signal(SIGHUP, exit);
135229538Sray	signal(SIGINT, exit);
136229538Sray	signal(SIGTERM, exit);
137229538Sray	signal(SIGXCPU, exit);
138229538Sray	signal(SIGXFSZ, exit);
139229538Sray	atexit(cleanup);
140229538Sray
141229538Sray	fdr = open(iname, O_RDONLY);
142229538Sray	if (fdr < 0) {
143229538Sray		err(1, "open(%s)", iname);
144229538Sray		/* Not reached */
145229538Sray	}
146229538Sray	if (fstat(fdr, &sb) != 0) {
147229538Sray		err(1, "fstat(%s)", iname);
148229538Sray		/* Not reached */
149229538Sray	}
150229538Sray	if (S_ISCHR(sb.st_mode)) {
151229538Sray		off_t ms;
152229538Sray
153229538Sray		if (ioctl(fdr, DIOCGMEDIASIZE, &ms) < 0) {
154229538Sray			err(1, "ioctl(DIOCGMEDIASIZE)");
155229538Sray			/* Not reached */
156229538Sray		}
157229538Sray		sb.st_size = ms;
158229538Sray	} else if (!S_ISREG(sb.st_mode)) {
159229538Sray		fprintf(stderr,
160229538Sray		    "%s: not a character device or regular file\n",
161229538Sray		    iname);
162229538Sray		exit(1);
163229538Sray	}
164229538Sray	hdr.nblocks = sb.st_size / hdr.blksz;
165229538Sray	if ((sb.st_size % hdr.blksz) != 0) {
166229538Sray		if (verbose != 0)
167229538Sray			fprintf(stderr, "file size is not multiple "
168229538Sray			"of %d, padding data\n", hdr.blksz);
169229538Sray		hdr.nblocks++;
170229538Sray	}
171229538Sray	toc = safe_malloc((hdr.nblocks + 1) * sizeof(*toc));
172229538Sray
173229538Sray	fdw = open(oname, O_WRONLY | O_TRUNC | O_CREAT,
174229538Sray		   S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
175229538Sray	if (fdw < 0) {
176229538Sray		err(1, "open(%s)", oname);
177229538Sray		/* Not reached */
178229538Sray	}
179229538Sray	cleanfile = oname;
180229538Sray
181229538Sray	/*
182229538Sray	 * Prepare header that we will write later when we have index ready.
183229538Sray	 */
184229538Sray	iov[0].iov_base = (char *)&hdr;
185229538Sray	iov[0].iov_len = sizeof(hdr);
186229538Sray	iov[1].iov_base = (char *)toc;
187229538Sray	iov[1].iov_len = (hdr.nblocks + 1) * sizeof(*toc);
188229538Sray	offset = iov[0].iov_len + iov[1].iov_len;
189229538Sray
190229538Sray	/* Reserve space for header */
191229538Sray	lseek(fdw, offset, SEEK_SET);
192229538Sray
193229538Sray	if (verbose != 0)
194229538Sray		fprintf(stderr, "data size %ju bytes, number of clusters "
195229538Sray		    "%u, index length %zu bytes\n", sb.st_size,
196229538Sray		    hdr.nblocks, iov[1].iov_len);
197229538Sray
198229538Sray	/* Init lzma encoder */
199229538Sray	if (lzma_lzma_preset(&opt_lzma, LZMA_PRESET_DEFAULT))
200229538Sray		errx(1, "Error loading LZMA preset");
201229538Sray
202229538Sray	filters[0].id = LZMA_FILTER_LZMA2;
203229538Sray	filters[0].options = &opt_lzma;
204229538Sray	filters[1].id = LZMA_VLI_UNKNOWN;
205229538Sray
206229538Sray	for(i = 0; i == 0 || ibuf != NULL; i++) {
207229538Sray		ibuf = readblock(fdr, ibuf, hdr.blksz);
208229538Sray		if (ibuf != NULL) {
209229538Sray			destlen = hdr.blksz*2;
210229538Sray
211229538Sray			ret = lzma_stream_encoder(&strm, filters,
212229538Sray			    LZMA_CHECK_CRC32);
213229538Sray			if (ret != LZMA_OK) {
214229538Sray				if (ret == LZMA_MEMLIMIT_ERROR)
215229538Sray					errx(1, "can't compress data: "
216229538Sray					    "LZMA_MEMLIMIT_ERROR");
217229538Sray
218229538Sray				errx(1, "can't compress data: "
219229538Sray				    "LZMA compressor ERROR");
220229538Sray			}
221229538Sray
222229538Sray			strm.next_in = ibuf;
223229538Sray			strm.avail_in = hdr.blksz;
224229538Sray			strm.next_out = obuf;
225229538Sray			strm.avail_out = hdr.blksz*2;
226229538Sray
227229538Sray			ret = lzma_code(&strm, LZMA_FINISH);
228229538Sray
229229538Sray			if (ret != LZMA_STREAM_END) {
230229538Sray				/* Error */
231229538Sray				errx(1, "lzma_code FINISH failed, code=%d, "
232229538Sray				    "pos(in=%zd, out=%zd)",
233229538Sray				    ret,
234229538Sray				    (hdr.blksz - strm.avail_in),
235229538Sray				    (hdr.blksz*2 - strm.avail_out));
236229538Sray			}
237229538Sray
238229538Sray			destlen -= strm.avail_out;
239229538Sray
240229538Sray			lzma_end(&strm);
241229538Sray
242229538Sray			if (verbose != 0)
243229538Sray				fprintf(stderr, "cluster #%d, in %u bytes, "
244229538Sray				    "out %u bytes\n", i, hdr.blksz, destlen);
245229538Sray		} else {
246229538Sray			destlen = USED_BLOCKSIZE - (offset % USED_BLOCKSIZE);
247229538Sray			memset(obuf, 0, destlen);
248229538Sray			if (verbose != 0)
249229538Sray				fprintf(stderr, "padding data with %u bytes"
250229538Sray				    " so that file size is multiple of %d\n",
251229538Sray				    destlen,
252229538Sray				    USED_BLOCKSIZE);
253229538Sray		}
254229538Sray		if (write(fdw, obuf, destlen) < 0) {
255229538Sray			err(1, "write(%s)", oname);
256229538Sray			/* Not reached */
257229538Sray		}
258229538Sray		toc[i] = htobe64(offset);
259229538Sray		offset += destlen;
260229538Sray	}
261229538Sray	close(fdr);
262229538Sray
263229538Sray	if (verbose != 0)
264229538Sray		fprintf(stderr, "compressed data to %ju bytes, saved %lld "
265229538Sray		    "bytes, %.2f%% decrease.\n", offset,
266229538Sray		    (long long)(sb.st_size - offset),
267229538Sray		    100.0 * (long long)(sb.st_size - offset) /
268229538Sray		    (float)sb.st_size);
269229538Sray
270229538Sray	/* Convert to big endian */
271229538Sray	hdr.blksz = htonl(hdr.blksz);
272229538Sray	hdr.nblocks = htonl(hdr.nblocks);
273229538Sray	/* Write headers into pre-allocated space */
274229538Sray	lseek(fdw, 0, SEEK_SET);
275229538Sray	if (writev(fdw, iov, 2) < 0) {
276229538Sray		err(1, "writev(%s)", oname);
277229538Sray		/* Not reached */
278229538Sray	}
279229538Sray	cleanfile = NULL;
280229538Sray	close(fdw);
281229538Sray
282229538Sray	exit(0);
283229538Sray}
284229538Sray
285229538Sraystatic char *
286229538Srayreadblock(int fd, char *ibuf, u_int32_t clstsize)
287229538Sray{
288229538Sray	int numread;
289229538Sray
290229538Sray	bzero(ibuf, clstsize);
291229538Sray	numread = read(fd, ibuf, clstsize);
292229538Sray	if (numread < 0) {
293229538Sray		err(1, "read() failed");
294229538Sray		/* Not reached */
295229538Sray	}
296229538Sray	if (numread == 0) {
297229538Sray		return NULL;
298229538Sray	}
299229538Sray	return ibuf;
300229538Sray}
301229538Sray
302229538Sraystatic void
303229538Srayusage(void)
304229538Sray{
305229538Sray
306229538Sray	fprintf(stderr, "usage: mkulzma [-v] [-o outfile] [-s cluster_size] "
307229538Sray	    "infile\n");
308229538Sray	exit(1);
309229538Sray}
310229538Sray
311229538Sraystatic void *
312229538Sraysafe_malloc(size_t size)
313229538Sray{
314229538Sray	void *retval;
315229538Sray
316229538Sray	retval = malloc(size);
317229538Sray	if (retval == NULL) {
318229538Sray		err(1, "can't allocate memory");
319229538Sray		/* Not reached */
320229538Sray	}
321229538Sray	return retval;
322229538Sray}
323229538Sray
324229538Sraystatic void
325229538Sraycleanup(void)
326229538Sray{
327229538Sray
328229538Sray	if (cleanfile != NULL)
329229538Sray		unlink(cleanfile);
330229538Sray}
331