]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.bin/mkulzma/mkulzma.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.bin / mkulzma / mkulzma.c
1 /*
2  * ----------------------------------------------------------------------------
3  * Derived from mkuzip.c by Aleksandr Rybalko <ray@ddteam.net>
4  * ----------------------------------------------------------------------------
5  * "THE BEER-WARE LICENSE" (Revision 42):
6  * <sobomax@FreeBSD.ORG> wrote this file. As long as you retain this notice you
7  * can do whatever you want with this stuff. If we meet some day, and you think
8  * this stuff is worth it, you can buy me a beer in return.       Maxim Sobolev
9  * ----------------------------------------------------------------------------
10  *
11  * $FreeBSD$
12  *
13  */
14
15 #include <sys/disk.h>
16 #include <sys/endian.h>
17 #include <sys/param.h>
18 #include <sys/stat.h>
19 #include <sys/uio.h>
20 #include <netinet/in.h>
21 #include <err.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include <lzma.h>
29
30 #define CLSTSIZE        16384
31 #define DEFAULT_SUFX    ".ulzma"
32
33 #define USED_BLOCKSIZE DEV_BSIZE
34
35 #define CLOOP_MAGIC_LEN 128
36 /* Format L3.0, since we move to XZ API */
37 static char CLOOP_MAGIC_START[] =
38     "#!/bin/sh\n"
39     "#L3.0\n"
40     "n=uncompress\n"
41     "m=geom_$n\n"
42     "(kldstat -m $m 2>&-||kldload $m)>&-&&"
43         "mount_cd9660 /dev/`mdconfig -af $0`.$n $1\n"
44     "exit $?\n";
45
46 static char *readblock(int, char *, u_int32_t);
47 static void usage(void);
48 static void *safe_malloc(size_t);
49 static void cleanup(void);
50
51 static char *cleanfile = NULL;
52
53 int main(int argc, char **argv)
54 {
55         char *iname, *oname, *obuf, *ibuf;
56         int fdr, fdw, i, opt, verbose, tmp;
57         struct iovec iov[2];
58         struct stat sb;
59         uint32_t destlen;
60         uint64_t offset;
61         uint64_t *toc;
62         lzma_filter filters[2];
63         lzma_options_lzma opt_lzma;
64         lzma_ret ret;
65         lzma_stream strm = LZMA_STREAM_INIT;
66         struct cloop_header {
67                 char magic[CLOOP_MAGIC_LEN];    /* cloop magic */
68                 uint32_t blksz;                 /* block size */
69                 uint32_t nblocks;               /* number of blocks */
70         } hdr;
71
72         memset(&hdr, 0, sizeof(hdr));
73         hdr.blksz = CLSTSIZE;
74         strcpy(hdr.magic, CLOOP_MAGIC_START);
75         oname = NULL;
76         verbose = 0;
77
78         while((opt = getopt(argc, argv, "o:s:v")) != -1) {
79                 switch(opt) {
80                 case 'o':
81                         oname = optarg;
82                         break;
83
84                 case 's':
85                         tmp = atoi(optarg);
86                         if (tmp <= 0) {
87                                 errx(1,
88                                     "invalid cluster size specified: %s",
89                                     optarg);
90                                 /* Not reached */
91                         }
92                         if (tmp % USED_BLOCKSIZE != 0) {
93                                 errx(1,
94                                     "cluster size should be multiple of %d",
95                                     USED_BLOCKSIZE);
96                                 /* Not reached */
97                         }
98                         if ( tmp > MAXPHYS) {
99                                 errx(1, "cluster size is too large");
100                                     /* Not reached */
101                         }
102                         hdr.blksz = tmp;
103                         break;
104
105                 case 'v':
106                         verbose = 1;
107                         break;
108
109                 default:
110                         usage();
111                         /* Not reached */
112                 }
113         }
114         argc -= optind;
115         argv += optind;
116
117         if (argc != 1) {
118                 usage();
119                 /* Not reached */
120         }
121
122         iname = argv[0];
123         if (oname == NULL) {
124                 asprintf(&oname, "%s%s", iname, DEFAULT_SUFX);
125                 if (oname == NULL) {
126                         err(1, "can't allocate memory");
127                         /* Not reached */
128                 }
129         }
130
131         obuf = safe_malloc(hdr.blksz*2);
132         ibuf = safe_malloc(hdr.blksz);
133
134         signal(SIGHUP, exit);
135         signal(SIGINT, exit);
136         signal(SIGTERM, exit);
137         signal(SIGXCPU, exit);
138         signal(SIGXFSZ, exit);
139         atexit(cleanup);
140
141         fdr = open(iname, O_RDONLY);
142         if (fdr < 0) {
143                 err(1, "open(%s)", iname);
144                 /* Not reached */
145         }
146         if (fstat(fdr, &sb) != 0) {
147                 err(1, "fstat(%s)", iname);
148                 /* Not reached */
149         }
150         if (S_ISCHR(sb.st_mode)) {
151                 off_t ms;
152
153                 if (ioctl(fdr, DIOCGMEDIASIZE, &ms) < 0) {
154                         err(1, "ioctl(DIOCGMEDIASIZE)");
155                         /* Not reached */
156                 }
157                 sb.st_size = ms;
158         } else if (!S_ISREG(sb.st_mode)) {
159                 fprintf(stderr,
160                     "%s: not a character device or regular file\n",
161                     iname);
162                 exit(1);
163         }
164         hdr.nblocks = sb.st_size / hdr.blksz;
165         if ((sb.st_size % hdr.blksz) != 0) {
166                 if (verbose != 0)
167                         fprintf(stderr, "file size is not multiple "
168                         "of %d, padding data\n", hdr.blksz);
169                 hdr.nblocks++;
170         }
171         toc = safe_malloc((hdr.nblocks + 1) * sizeof(*toc));
172
173         fdw = open(oname, O_WRONLY | O_TRUNC | O_CREAT,
174                    S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
175         if (fdw < 0) {
176                 err(1, "open(%s)", oname);
177                 /* Not reached */
178         }
179         cleanfile = oname;
180
181         /*
182          * Prepare header that we will write later when we have index ready.
183          */
184         iov[0].iov_base = (char *)&hdr;
185         iov[0].iov_len = sizeof(hdr);
186         iov[1].iov_base = (char *)toc;
187         iov[1].iov_len = (hdr.nblocks + 1) * sizeof(*toc);
188         offset = iov[0].iov_len + iov[1].iov_len;
189
190         /* Reserve space for header */
191         lseek(fdw, offset, SEEK_SET);
192
193         if (verbose != 0)
194                 fprintf(stderr, "data size %ju bytes, number of clusters "
195                     "%u, index length %zu bytes\n", sb.st_size,
196                     hdr.nblocks, iov[1].iov_len);
197
198         /* Init lzma encoder */
199         if (lzma_lzma_preset(&opt_lzma, LZMA_PRESET_DEFAULT))
200                 errx(1, "Error loading LZMA preset");
201
202         filters[0].id = LZMA_FILTER_LZMA2;
203         filters[0].options = &opt_lzma;
204         filters[1].id = LZMA_VLI_UNKNOWN;
205
206         for(i = 0; i == 0 || ibuf != NULL; i++) {
207                 ibuf = readblock(fdr, ibuf, hdr.blksz);
208                 if (ibuf != NULL) {
209                         destlen = hdr.blksz*2;
210
211                         ret = lzma_stream_encoder(&strm, filters,
212                             LZMA_CHECK_CRC32);
213                         if (ret != LZMA_OK) {
214                                 if (ret == LZMA_MEMLIMIT_ERROR)
215                                         errx(1, "can't compress data: "
216                                             "LZMA_MEMLIMIT_ERROR");
217
218                                 errx(1, "can't compress data: "
219                                     "LZMA compressor ERROR");
220                         }
221
222                         strm.next_in = ibuf;
223                         strm.avail_in = hdr.blksz;
224                         strm.next_out = obuf;
225                         strm.avail_out = hdr.blksz*2;
226
227                         ret = lzma_code(&strm, LZMA_FINISH);
228
229                         if (ret != LZMA_STREAM_END) {
230                                 /* Error */
231                                 errx(1, "lzma_code FINISH failed, code=%d, "
232                                     "pos(in=%zd, out=%zd)",
233                                     ret,
234                                     (hdr.blksz - strm.avail_in),
235                                     (hdr.blksz*2 - strm.avail_out));
236                         }
237
238                         destlen -= strm.avail_out;
239
240                         lzma_end(&strm);
241
242                         if (verbose != 0)
243                                 fprintf(stderr, "cluster #%d, in %u bytes, "
244                                     "out %u bytes\n", i, hdr.blksz, destlen);
245                 } else {
246                         destlen = USED_BLOCKSIZE - (offset % USED_BLOCKSIZE);
247                         memset(obuf, 0, destlen);
248                         if (verbose != 0)
249                                 fprintf(stderr, "padding data with %u bytes"
250                                     " so that file size is multiple of %d\n",
251                                     destlen,
252                                     USED_BLOCKSIZE);
253                 }
254                 if (write(fdw, obuf, destlen) < 0) {
255                         err(1, "write(%s)", oname);
256                         /* Not reached */
257                 }
258                 toc[i] = htobe64(offset);
259                 offset += destlen;
260         }
261         close(fdr);
262
263         if (verbose != 0)
264                 fprintf(stderr, "compressed data to %ju bytes, saved %lld "
265                     "bytes, %.2f%% decrease.\n", offset,
266                     (long long)(sb.st_size - offset),
267                     100.0 * (long long)(sb.st_size - offset) /
268                     (float)sb.st_size);
269
270         /* Convert to big endian */
271         hdr.blksz = htonl(hdr.blksz);
272         hdr.nblocks = htonl(hdr.nblocks);
273         /* Write headers into pre-allocated space */
274         lseek(fdw, 0, SEEK_SET);
275         if (writev(fdw, iov, 2) < 0) {
276                 err(1, "writev(%s)", oname);
277                 /* Not reached */
278         }
279         cleanfile = NULL;
280         close(fdw);
281
282         exit(0);
283 }
284
285 static char *
286 readblock(int fd, char *ibuf, u_int32_t clstsize)
287 {
288         int numread;
289
290         bzero(ibuf, clstsize);
291         numread = read(fd, ibuf, clstsize);
292         if (numread < 0) {
293                 err(1, "read() failed");
294                 /* Not reached */
295         }
296         if (numread == 0) {
297                 return NULL;
298         }
299         return ibuf;
300 }
301
302 static void
303 usage(void)
304 {
305
306         fprintf(stderr, "usage: mkulzma [-v] [-o outfile] [-s cluster_size] "
307             "infile\n");
308         exit(1);
309 }
310
311 static void *
312 safe_malloc(size_t size)
313 {
314         void *retval;
315
316         retval = malloc(size);
317         if (retval == NULL) {
318                 err(1, "can't allocate memory");
319                 /* Not reached */
320         }
321         return retval;
322 }
323
324 static void
325 cleanup(void)
326 {
327
328         if (cleanfile != NULL)
329                 unlink(cleanfile);
330 }