1 /* 2 * ---------------------------------------------------------------------------- 3 * "THE BEER-WARE LICENSE" (Revision 42): 4 * <sobomax@FreeBSD.ORG> wrote this file. As long as you retain this notice you 5 * can do whatever you want with this stuff. If we meet some day, and you think 6 * this stuff is worth it, you can buy me a beer in return. Maxim Sobolev 7 * ---------------------------------------------------------------------------- 8 * 9 * $FreeBSD$ 10 * 11 */ 12 13 #include <sys/types.h> 14 #include <sys/endian.h> 15 #include <sys/param.h> 16 #include <sys/stat.h> 17 #include <sys/uio.h> 18 #include <netinet/in.h> 19 #include <zlib.h> 20 #include <err.h> 21 #include <fcntl.h> 22 #include <signal.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #define CLSTSIZE 16384 29 #define DEFAULT_SUFX ".uzip" 30 31 #define CLOOP_MAGIC_LEN 128 32 static char CLOOP_MAGIC_START[] = "#!/bin/sh\n#V2.0 Format\n" 33 "m=geom_uzip\n(kldstat -m $m 2>&-||kldload $m)>&-&&" 34 "mount_cd9660 /dev/`mdconfig -af $0`.uzip $1\nexit $?\n"; 35 36 static char *readblock(int, char *, u_int32_t); 37 static void usage(void); 38 static void *safe_malloc(size_t); 39 static void cleanup(void); 40 41 static char *cleanfile = NULL; 42 43 int main(int argc, char **argv) 44 { 45 char *iname, *oname, *obuf, *ibuf; 46 uint64_t *toc; 47 int fdr, fdw, i, opt, verbose, tmp; 48 struct iovec iov[2]; 49 struct stat sb; 50 uLongf destlen; 51 uint64_t offset; 52 struct cloop_header { 53 char magic[CLOOP_MAGIC_LEN]; /* cloop magic */ 54 uint32_t blksz; /* block size */ 55 uint32_t nblocks; /* number of blocks */ 56 } hdr; 57 58 memset(&hdr, 0, sizeof(hdr)); 59 hdr.blksz = CLSTSIZE; 60 strcpy(hdr.magic, CLOOP_MAGIC_START); 61 oname = NULL; 62 verbose = 0; 63 64 while((opt = getopt(argc, argv, "o:s:v")) != -1) { 65 switch(opt) { 66 case 'o': 67 oname = optarg; 68 break; 69 70 case 's': 71 tmp = atoi(optarg); 72 if (tmp <= 0) { 73 errx(1, "invalid cluster size specified: %s", 74 optarg); 75 /* Not reached */ 76 } 77 if (tmp % DEV_BSIZE != 0) { 78 errx(1, "cluster size should be multiple of %d", 79 DEV_BSIZE); 80 /* Not reached */ 81 } 82 if (compressBound(tmp) > MAXPHYS) { 83 errx(1, "cluster size is too large"); 84 /* Not reached */ 85 } 86 hdr.blksz = tmp; 87 break; 88 89 case 'v': 90 verbose = 1; 91 break; 92 93 default: 94 usage(); 95 /* Not reached */ 96 } 97 } 98 argc -= optind; 99 argv += optind; 100 101 if (argc != 1) { 102 usage(); 103 /* Not reached */ 104 } 105 106 iname = argv[0]; 107 if (oname == NULL) { 108 asprintf(&oname, "%s%s", iname, DEFAULT_SUFX); 109 if (oname == NULL) { 110 err(1, "can't allocate memory"); 111 /* Not reached */ 112 } 113 } 114 115 obuf = safe_malloc(compressBound(hdr.blksz)); 116 ibuf = safe_malloc(hdr.blksz); 117 118 signal(SIGHUP, exit); 119 signal(SIGINT, exit); 120 signal(SIGTERM, exit); 121 signal(SIGXCPU, exit); 122 signal(SIGXFSZ, exit); 123 atexit(cleanup); 124 125 if (stat(iname, &sb) != 0) { 126 err(1, "stat(%s)", iname); 127 /* Not reached */ 128 } 129 hdr.nblocks = sb.st_size / hdr.blksz; 130 if ((sb.st_size % hdr.blksz) != 0) { 131 if (verbose != 0) 132 fprintf(stderr, "file size is not multiple " 133 "of %d, padding data\n", hdr.blksz); 134 hdr.nblocks++; 135 } 136 toc = safe_malloc((hdr.nblocks + 1) * sizeof(*toc)); 137 138 fdr = open(iname, O_RDONLY); 139 if (fdr < 0) { 140 err(1, "open(%s)", iname); 141 /* Not reached */ 142 } 143 fdw = open(oname, O_WRONLY | O_TRUNC | O_CREAT, 144 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 145 if (fdw < 0) { 146 err(1, "open(%s)", oname); 147 /* Not reached */ 148 } 149 cleanfile = oname; 150 151 /* Prepare header that we will write later when we have index ready. */ 152 iov[0].iov_base = (char *)&hdr; 153 iov[0].iov_len = sizeof(hdr); 154 iov[1].iov_base = (char *)toc; 155 iov[1].iov_len = (hdr.nblocks + 1) * sizeof(*toc); 156 offset = iov[0].iov_len + iov[1].iov_len; 157 158 /* Reserve space for header */ 159 lseek(fdw, offset, SEEK_SET); 160 161 if (verbose != 0) 162 fprintf(stderr, "data size %ju bytes, number of clusters " 163 "%u, index length %zu bytes\n", sb.st_size, 164 hdr.nblocks, iov[1].iov_len); 165 166 for(i = 0; i == 0 || ibuf != NULL; i++) { 167 ibuf = readblock(fdr, ibuf, hdr.blksz); 168 if (ibuf != NULL) { 169 destlen = compressBound(hdr.blksz); 170 if (compress2(obuf, &destlen, ibuf, hdr.blksz, 171 Z_BEST_COMPRESSION) != Z_OK) { 172 errx(1, "can't compress data: compress2() " 173 "failed"); 174 /* Not reached */ 175 } 176 if (verbose != 0) 177 fprintf(stderr, "cluster #%d, in %u bytes, " 178 "out %lu bytes\n", i, hdr.blksz, destlen); 179 } else { 180 destlen = DEV_BSIZE - (offset % DEV_BSIZE); 181 memset(obuf, 0, destlen); 182 if (verbose != 0) 183 fprintf(stderr, "padding data with %lu bytes so " 184 "that file size is multiple of %d\n", destlen, 185 DEV_BSIZE); 186 } 187 if (write(fdw, obuf, destlen) < 0) { 188 err(1, "write(%s)", oname); 189 /* Not reached */ 190 } 191 toc[i] = htobe64(offset); 192 offset += destlen; 193 } 194 close(fdr); 195 196 if (verbose != 0) 197 fprintf(stderr, "compressed data to %ju bytes, saved %lld " 198 "bytes, %.2f%% decrease.\n", offset, (long long)(sb.st_size - offset), 199 100.0 * (long long)(sb.st_size - offset) / (float)sb.st_size); 200 201 /* Convert to big endian */ 202 hdr.blksz = htonl(hdr.blksz); 203 hdr.nblocks = htonl(hdr.nblocks); 204 /* Write headers into pre-allocated space */ 205 lseek(fdw, 0, SEEK_SET); 206 if (writev(fdw, iov, 2) < 0) { 207 err(1, "writev(%s)", oname); 208 /* Not reached */ 209 } 210 cleanfile = NULL; 211 close(fdw); 212 213 exit(0); 214 } 215 216 static char * 217 readblock(int fd, char *ibuf, u_int32_t clstsize) 218 { 219 int numread; 220 221 bzero(ibuf, clstsize); 222 numread = read(fd, ibuf, clstsize); 223 if (numread < 0) { 224 err(1, "read() failed"); 225 /* Not reached */ 226 } 227 if (numread == 0) { 228 return NULL; 229 } 230 return ibuf; 231 } 232 233 static void 234 usage(void) 235 { 236 237 fprintf(stderr, "usage: mkuzip [-v] [-o outfile] [-s cluster_size] infile\n"); 238 exit(1); 239 } 240 241 static void * 242 safe_malloc(size_t size) 243 { 244 void *retval; 245 246 retval = malloc(size); 247 if (retval == NULL) { 248 err(1, "can't allocate memory"); 249 /* Not reached */ 250 } 251 return retval; 252 } 253 254 static void 255 cleanup(void) 256 { 257 258 if (cleanfile != NULL) 259 unlink(cleanfile); 260 } 261