1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * fiocompress - a utility to compress files with a filesystem.
28 * Used to build compressed boot archives to reduce memory
29 * requirements for booting.
30 */
31
32#include <stdio.h>
33#include <errno.h>
34#include <stdlib.h>
35#include <fcntl.h>
36#include <sys/types.h>
37#include <sys/stat.h>
38#include <sys/mman.h>
39#include <unistd.h>
40#include <utility.h>
41#include <zlib.h>
42
43#include <sys/filio.h>
44#include <sys/fs/decomp.h>
45
46#include "message.h"
47
48static void	setup_infile(char *);
49static void	setup_outfile(char *);
50static void	do_comp(size_t);
51static void	do_decomp(void);
52
53static caddr_t	srcaddr;
54static size_t	srclen;
55
56static int	dstfd;
57
58static char	*srcfile;
59static char	*dstfile;
60
61
62int
63main(int argc, char **argv)
64{
65	int compress = 0;
66	int decompress = 0;
67	int doioc = 0;
68	size_t	blksize = 8192;
69	char c;
70
71	while ((c = getopt(argc, argv, "mcdb:")) != -1) {
72		switch (c) {
73		case 'm':
74			doioc++;
75			break;
76		case 'c':
77			if (decompress) {
78				(void) fprintf(stderr, OPT_DC_EXCL);
79				exit(-1);
80			}
81			compress = 1;
82			break;
83		case 'd':
84			if (compress) {
85				(void) fprintf(stderr, OPT_DC_EXCL);
86				exit(-1);
87			}
88			decompress = 1;
89			break;
90		case 'b':
91			blksize = atoi(optarg);
92			if (blksize == 0 || (blksize & (blksize-1))) {
93				(void) fprintf(stderr, INVALID_BLKSZ);
94				exit(-1);
95			}
96			break;
97		case '?':
98			(void) fprintf(stderr, UNKNOWN_OPTION, optopt);
99			exit(-1);
100		}
101	}
102	if (argc - optind != 2) {
103		(void) fprintf(stderr, MISS_FILES);
104		exit(-1);
105	}
106
107	setup_infile(argv[optind]);
108	setup_outfile(argv[optind + 1]);
109
110	if (decompress)
111		do_decomp();
112	else {
113		do_comp(blksize);
114		if (doioc) {
115			if (ioctl(dstfd, _FIO_COMPRESSED, 0) == -1) {
116				(void) fprintf(stderr, FIO_COMP_FAIL,
117				    dstfile, strerror(errno));
118				exit(-1);
119			}
120		}
121	}
122	return (0);
123}
124
125static void
126setup_infile(char *file)
127{
128	int fd;
129	void *addr;
130	struct stat stbuf;
131
132	srcfile = file;
133
134	fd = open(srcfile, O_RDONLY, 0);
135	if (fd == -1) {
136		(void) fprintf(stderr, CANT_OPEN,
137		    srcfile, strerror(errno));
138		exit(-1);
139	}
140
141	if (fstat(fd, &stbuf) == -1) {
142		(void) fprintf(stderr, STAT_FAIL,
143		    srcfile, strerror(errno));
144		exit(-1);
145	}
146	srclen = stbuf.st_size;
147
148	addr = mmap(0, srclen, PROT_READ, MAP_SHARED, fd, 0);
149	if (addr == MAP_FAILED) {
150		(void) fprintf(stderr, MMAP_FAIL, srcfile, strerror(errno));
151		exit(-1);
152	}
153	srcaddr = addr;
154}
155
156static void
157setup_outfile(char *file)
158{
159	int fd;
160
161	dstfile = file;
162
163	fd = open(dstfile, O_WRONLY | O_CREAT | O_TRUNC,
164	    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
165	if (fd == -1) {
166		(void) fprintf(stderr, OPEN_FAIL, dstfile, strerror(errno));
167		exit(-1);
168	}
169	dstfd = fd;
170}
171
172static void
173do_comp(size_t blksize)
174{
175	struct comphdr *hdr;
176	off_t offset;
177	size_t blks, dstlen, hlen;
178	void *dstbuf;
179	int i;
180
181	blks = ((srclen - 1) / blksize) + 1;
182	hlen = offset = sizeof (struct comphdr) + blks * sizeof (uint64_t);
183	hdr = malloc(hlen);
184	if (hdr == NULL) {
185		(void) fprintf(stderr, HDR_ALLOC, hlen);
186		exit(-1);
187	}
188
189	hdr->ch_magic = CH_MAGIC_ZLIB;
190	hdr->ch_version = CH_VERSION;
191	hdr->ch_algorithm = CH_ALG_ZLIB;
192	hdr->ch_fsize = srclen;
193	hdr->ch_blksize = blksize;
194
195	dstlen = ZMAXBUF(blksize);
196	dstbuf = malloc(dstlen);
197	if (dstbuf == NULL) {
198		(void) fprintf(stderr, BUF_ALLOC, dstlen);
199		exit(-1);
200	}
201
202	if (lseek(dstfd, offset, SEEK_SET) == (off_t)-1) {
203		(void) fprintf(stderr, SEEK_ERR,
204		    offset, dstfile, strerror(errno));
205		exit(-1);
206	}
207
208	for (i = 0; i < blks; i++) {
209		ulong_t slen, dlen;
210		int ret;
211
212		hdr->ch_blkmap[i] = offset;
213		slen = MIN(srclen, blksize);
214		dlen = dstlen;
215		ret = compress2(dstbuf, &dlen, (Bytef *)srcaddr, slen, 9);
216		if (ret != Z_OK) {
217			(void) fprintf(stderr, COMP_ERR, srcfile, ret);
218			exit(-1);
219		}
220
221		if (write(dstfd, dstbuf, dlen) != dlen) {
222			(void) fprintf(stderr, WRITE_ERR,
223			    dlen, dstfile, strerror(errno));
224			exit(-1);
225		}
226
227		offset += dlen;
228		srclen -= slen;
229		srcaddr += slen;
230	}
231
232	if (lseek(dstfd, 0, SEEK_SET) == (off_t)-1) {
233		(void) fprintf(stderr, SEEK_ERR,
234		    0, dstfile, strerror(errno));
235		exit(-1);
236	}
237
238	if (write(dstfd, hdr, hlen) != hlen) {
239		(void) fprintf(stderr, WRITE_ERR,
240		    hlen, dstfile, strerror(errno));
241		exit(-1);
242	}
243}
244
245static void
246do_decomp()
247{
248	struct comphdr *hdr;
249	size_t blks, blksize;
250	void *dstbuf;
251	int i;
252	ulong_t slen, dlen;
253	int ret;
254
255	hdr = (struct comphdr *)(void *)srcaddr;
256	if (hdr->ch_magic != CH_MAGIC_ZLIB) {
257		(void) fprintf(stderr, BAD_MAGIC,
258		    srcfile, (uint64_t)hdr->ch_magic, CH_MAGIC_ZLIB);
259		exit(-1);
260	}
261	if (hdr->ch_version != CH_VERSION) {
262		(void) fprintf(stderr, BAD_VERS,
263		    srcfile, (uint64_t)hdr->ch_version, CH_VERSION);
264		exit(-1);
265	}
266	if (hdr->ch_algorithm != CH_ALG_ZLIB) {
267		(void) fprintf(stderr, BAD_ALG,
268		    srcfile, (uint64_t)hdr->ch_algorithm, CH_ALG_ZLIB);
269		exit(-1);
270	}
271
272	blksize = hdr->ch_blksize;
273	dstbuf = malloc(blksize);
274	if (dstbuf == NULL) {
275		(void) fprintf(stderr, HDR_ALLOC, blksize);
276		exit(-1);
277	}
278
279	blks = (hdr->ch_fsize - 1) / blksize;
280	srcaddr += hdr->ch_blkmap[0];
281	for (i = 0; i < blks; i++) {
282		dlen = blksize;
283		slen = hdr->ch_blkmap[i + 1] - hdr->ch_blkmap[i];
284		ret = uncompress(dstbuf, &dlen, (Bytef *)srcaddr, slen);
285		if (ret != Z_OK) {
286			(void) fprintf(stderr, DECOMP_ERR, srcfile, ret);
287			exit(-1);
288		}
289
290		if (dlen != blksize) {
291			(void) fprintf(stderr, CORRUPT, srcfile);
292			exit(-1);
293		}
294		if (write(dstfd, dstbuf, dlen) != dlen) {
295			(void) fprintf(stderr, WRITE_ERR,
296			    dlen, dstfile, strerror(errno));
297			exit(-1);
298		}
299		srcaddr += slen;
300	}
301
302	dlen = blksize;
303	slen = hdr->ch_fsize - hdr->ch_blkmap[i];
304	if ((ret = uncompress(dstbuf, &dlen, (Bytef *)srcaddr, slen)) != Z_OK) {
305		(void) fprintf(stderr, DECOMP_ERR, dstfile, ret);
306		exit(-1);
307	}
308
309	if (write(dstfd, dstbuf, dlen) != dlen) {
310		(void) fprintf(stderr, WRITE_ERR,
311		    dlen, dstfile, strerror(errno));
312		exit(-1);
313	}
314}
315