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