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 
48 static void	setup_infile(char *);
49 static void	setup_outfile(char *);
50 static void	do_comp(size_t);
51 static void	do_decomp(void);
52 
53 static caddr_t	srcaddr;
54 static size_t	srclen;
55 
56 static int	dstfd;
57 
58 static char	*srcfile;
59 static char	*dstfile;
60 
61 
62 int
63 main(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 
125 static void
126 setup_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 
156 static void
157 setup_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 
172 static void
173 do_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 
245 static void
246 do_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