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