1 /* 2 * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 #ifdef _ALLBSD_SOURCE 26 #include <stdint.h> 27 #define THRTYPE intptr_t 28 #else 29 #define THRTYPE int 30 #endif 31 32 #include <sys/types.h> 33 34 #include <stdio.h> 35 #include <string.h> 36 #include <stdlib.h> 37 #include <stdarg.h> 38 #include <errno.h> 39 40 #include <limits.h> 41 #include <time.h> 42 43 #if defined(unix) && !defined(PRODUCT) 44 #include "pthread.h" 45 #define THREAD_SELF ((THRTYPE)pthread_self()) 46 #endif 47 48 #include "jni.h" 49 #include "defines.h" 50 #include "bytes.h" 51 #include "utils.h" 52 #include "coding.h" 53 #include "bands.h" 54 55 #include "constants.h" 56 57 #include "zip.h" 58 59 #include "unpack.h" 60 61 62 JNIEXPORT int 63 main(int argc, char **argv) { 64 return unpacker::run(argc, argv); 65 } 66 67 // Single-threaded, implementation, not reentrant. 68 // Includes a weak error check against MT access. 69 #ifndef THREAD_SELF 70 #define THREAD_SELF ((THRTYPE) 0) 71 #endif 72 NOT_PRODUCT(static THRTYPE uThread = -1;) 73 74 unpacker* unpacker::non_mt_current = null; 75 unpacker* unpacker::current() { 76 //assert(uThread == THREAD_SELF); 77 return non_mt_current; 78 } 79 static void set_current_unpacker(unpacker* u) { 80 unpacker::non_mt_current = u; 81 assert(((uThread = (u == null) ? (THRTYPE) -1 : THREAD_SELF), 82 true)); 83 } 84 85 // Callback for fetching data, Unix style. 86 static jlong read_input_via_stdio(unpacker* u, 87 void* buf, jlong minlen, jlong maxlen) { 88 assert(minlen <= maxlen); // don't talk nonsense 89 jlong numread = 0; 90 char* bufptr = (char*) buf; 91 while (numread < minlen) { 92 // read available input, up to buf.length or maxlen 93 int readlen = (1<<16); 94 if (readlen > (maxlen - numread)) 95 readlen = (int)(maxlen - numread); 96 int nr = 0; 97 if (u->infileptr != null) { 98 nr = (int)fread(bufptr, 1, readlen, u->infileptr); 99 } else { 100 #ifndef WIN32 101 // we prefer unbuffered inputs 102 nr = (int)read(u->infileno, bufptr, readlen); 103 #else 104 nr = (int)fread(bufptr, 1, readlen, stdin); 105 #endif 106 } 107 if (nr <= 0) { 108 if (errno != EINTR) 109 break; 110 nr = 0; 111 } 112 numread += nr; 113 bufptr += nr; 114 assert(numread <= maxlen); 115 } 116 //fprintf(u->errstrm, "readInputFn(%d,%d) => %d\n", 117 // (int)minlen, (int)maxlen, (int)numread); 118 return numread; 119 } 120 121 enum { EOF_MAGIC = 0, BAD_MAGIC = -1 }; 122 static int read_magic(unpacker* u, char peek[], int peeklen) { 123 assert(peeklen == 4); // magic numbers are always 4 bytes 124 jlong nr = (u->read_input_fn)(u, peek, peeklen, peeklen); 125 if (nr != peeklen) { 126 return (nr == 0) ? EOF_MAGIC : BAD_MAGIC; 127 } 128 int magic = 0; 129 for (int i = 0; i < peeklen; i++) { 130 magic <<= 8; 131 magic += peek[i] & 0xFF; 132 } 133 return magic; 134 } 135 136 static void setup_gzin(unpacker* u) { 137 gunzip* gzin = NEW(gunzip, 1); 138 gzin->init(u); 139 } 140 141 static const char* nbasename(const char* progname) { 142 const char* slash = strrchr(progname, '/'); 143 if (slash != null) progname = ++slash; 144 return progname; 145 } 146 147 #define USAGE_HEADER "Usage: %s [-opt... | --option=value]... x.pack[.gz] y.jar\n" 148 #define USAGE_OPTIONS \ 149 "\n" \ 150 "Unpacking Options\n" \ 151 " -H{h}, --deflate-hint={h} override transmitted deflate hint:\n" \ 152 " true, false, or keep (default)\n" \ 153 " -r, --remove-pack-file remove input file after unpacking\n" \ 154 " -v, --verbose increase program verbosity\n" \ 155 " -q, --quiet set verbosity to lowest level\n" \ 156 " -l{F}, --log-file={F} output to the given log file,\n" \ 157 " or '-' for standard output (default)\n" \ 158 " -?, -h, --help print this help message\n" \ 159 " -V, --version print program version\n" \ 160 "\n" \ 161 "Exit Status:\n" \ 162 " 0 if successful, >0 if an error occurred\n" 163 164 #define DEPRECATE_WARNING \ 165 "\nWarning: The %s tool is planned to be removed in a future JDK release.\n\n" 166 167 #define SUPPRESS_DEPRECATE_MSG "-XDsuppress-tool-removal-message" 168 169 static bool suppress_warning = false; 170 171 static void usage(unpacker* u, const char* progname, bool full = false) { 172 // WinMain does not set argv[0] to the progrname 173 progname = (progname != null) ? nbasename(progname) : "unpack200"; 174 175 fprintf(u->errstrm, USAGE_HEADER, progname); 176 if (full) { 177 fprintf(u->errstrm, USAGE_OPTIONS); 178 } else { 179 fprintf(u->errstrm, "(For more information, run %s --help .)\n", progname); 180 } 181 } 182 183 // argument parsing 184 static char** init_args(int argc, char** argv, int &envargc) { 185 const char* env = getenv("UNPACK200_FLAGS"); 186 ptrlist envargs; 187 envargs.init(); 188 if (env != null) { 189 char* buf = (char*) strdup(env); 190 const char* delim = "\n\t "; 191 for (char* p = strtok(buf, delim); p != null; p = strtok(null, delim)) { 192 if (!strcmp(p, SUPPRESS_DEPRECATE_MSG)) { 193 suppress_warning = true; 194 } else { 195 envargs.add(p); 196 } 197 } 198 } 199 // allocate extra margin at both head and tail 200 char** argp = NEW(char*, envargs.length()+argc+1); 201 char** argp0 = argp; 202 int i; 203 for (i = 0; i < envargs.length(); i++) { 204 *argp++ = (char*) envargs.get(i); 205 } 206 for (i = 1; i < argc; i++) { 207 // note: skip argv[0] (program name) 208 if (!strcmp(argv[i], SUPPRESS_DEPRECATE_MSG)) { 209 suppress_warning = true; 210 } else { 211 *argp++ = (char*) strdup(argv[i]); // make a scratch copy 212 } 213 } 214 *argp = null; // sentinel 215 envargc = envargs.length(); // report this count to next_arg 216 envargs.free(); 217 return argp0; 218 } 219 220 static int strpcmp(const char* str, const char* pfx) { 221 return strncmp(str, pfx, strlen(pfx)); 222 } 223 224 static const char flag_opts[] = "vqrVh?"; 225 static const char string_opts[] = "HlJ"; 226 227 static int next_arg(char** &argp) { 228 char* arg = *argp; 229 if (arg == null || arg[0] != '-') { // end of option list 230 return 0; 231 } 232 //printf("opt: %s\n", arg); 233 char ach = arg[1]; 234 if (ach == '\0') { 235 // ++argp; // do not pop this arg 236 return 0; // bare "-" is stdin/stdout 237 } else if (arg[1] == '-') { // --foo option 238 static const char* keys[] = { 239 "Hdeflate-hint=", 240 "vverbose", 241 "qquiet", 242 "rremove-pack-file", 243 "llog-file=", 244 "Vversion", 245 "hhelp", 246 null }; 247 if (arg[2] == '\0') { // end of option list 248 ++argp; // pop the "--" 249 return 0; 250 } 251 for (int i = 0; keys[i] != null; i++) { 252 const char* key = keys[i]; 253 char kch = *key++; 254 if (strchr(key, '=') == null) { 255 if (!strcmp(arg+2, key)) { 256 ++argp; // pop option arg 257 return kch; 258 } 259 } else { 260 if (!strpcmp(arg+2, key)) { 261 *argp += 2 + strlen(key); // remove "--"+key from arg 262 return kch; 263 } 264 } 265 } 266 } else if (strchr(flag_opts, ach) != null) { // plain option 267 if (arg[2] == '\0') { 268 ++argp; 269 } else { 270 // in-place edit of "-vxyz" to "-xyz" 271 arg += 1; // skip original '-' 272 arg[0] = '-'; 273 *argp = arg; 274 } 275 //printf(" key => %c\n", ach); 276 return ach; 277 } else if (strchr(string_opts, ach) != null) { // argument-bearing option 278 if (arg[2] == '\0') { 279 if (argp[1] == null) return -1; // no next arg 280 ++argp; // leave the argument in place 281 } else { 282 // in-place edit of "-Hxyz" to "xyz" 283 arg += 2; // skip original '-H' 284 *argp = arg; 285 } 286 //printf(" key => %c\n", ach); 287 return ach; 288 } 289 return -1; // bad argument 290 } 291 292 static const char sccsver[] = "1.30, 07/05/05"; 293 294 // Usage: unpackage input.pack output.jar 295 int unpacker::run(int argc, char **argv) { 296 unpacker u; 297 u.init(read_input_via_stdio); 298 set_current_unpacker(&u); 299 300 jar jarout; 301 jarout.init(&u); 302 303 int envargc = 0; 304 char** argbuf = init_args(argc, argv, envargc); 305 char** arg0 = argbuf+envargc; 306 char** argp = argbuf; 307 308 int verbose = 0; 309 char* logfile = null; 310 311 if (!suppress_warning) { 312 fprintf(u.errstrm, DEPRECATE_WARNING, nbasename(argv[0])); 313 } 314 315 for (;;) { 316 const char* arg = (*argp == null)? "": u.saveStr(*argp); 317 bool isenvarg = (argp < arg0); 318 int ach = next_arg(argp); 319 bool hasoptarg = (ach != 0 && strchr(string_opts, ach) != null); 320 if (ach == 0 && argp >= arg0) break; 321 if (isenvarg && argp == arg0 && hasoptarg) ach = 0; // don't pull from cmdline 322 switch (ach) { 323 case 'H': u.set_option(UNPACK_DEFLATE_HINT,*argp++); break; 324 case 'v': ++verbose; break; 325 case 'q': verbose = 0; break; 326 case 'r': u.set_option(UNPACK_REMOVE_PACKFILE,"1"); break; 327 case 'l': logfile = *argp++; break; 328 case 'J': argp += 1; break; // skip ignored -Jxxx parameter 329 330 case 'V': 331 fprintf(u.errstrm, VERSION_STRING, nbasename(argv[0]), sccsver); 332 exit(0); 333 334 case 'h': 335 case '?': 336 usage(&u, argv[0], true); 337 exit(0); 338 339 default: 340 const char* inenv = isenvarg? " in ${UNPACK200_FLAGS}": ""; 341 if (hasoptarg) 342 fprintf(u.errstrm, "Missing option string%s: %s\n", inenv, arg); 343 else 344 fprintf(u.errstrm, "Unrecognized argument%s: %s\n", inenv, arg); 345 usage(&u, argv[0]); 346 exit(2); 347 } 348 } 349 350 if (verbose != 0) { 351 u.set_option(DEBUG_VERBOSE, u.saveIntStr(verbose)); 352 } 353 if (logfile != null) { 354 u.set_option(UNPACK_LOG_FILE, logfile); 355 } 356 357 u.redirect_stdio(); 358 359 const char* source_file = *argp++; 360 const char* destination_file = *argp++; 361 362 if (source_file == null || destination_file == null || *argp != null) { 363 usage(&u, argv[0]); 364 exit(2); 365 } 366 367 if (verbose != 0) { 368 fprintf(u.errstrm, 369 "Unpacking from %s to %s\n", source_file, destination_file); 370 } 371 bool& remove_source = u.remove_packfile; 372 373 if (strcmp(source_file, "-") == 0) { 374 remove_source = false; 375 u.infileno = fileno(stdin); 376 } else { 377 u.infileptr = fopen(source_file, "rb"); 378 if (u.infileptr == null) { 379 fprintf(u.errstrm, 380 "Error: Could not open input file: %s\n", source_file); 381 exit(3); // Called only from the native standalone unpacker 382 } 383 } 384 385 if (strcmp(destination_file, "-") == 0) { 386 jarout.jarfp = stdout; 387 jarout.jarname = null; 388 if (u.errstrm == stdout) // do not mix output 389 u.set_option(UNPACK_LOG_FILE, LOGFILE_STDERR); 390 } else { 391 jarout.openJarFile(destination_file); 392 assert(jarout.jarfp != null); 393 } 394 395 if (verbose != 0) 396 u.dump_options(); 397 398 char peek[4]; 399 int magic; 400 401 // check for GZIP input 402 magic = read_magic(&u, peek, (int)sizeof(peek)); 403 if ((magic & GZIP_MAGIC_MASK) == GZIP_MAGIC) { 404 // Oops; must slap an input filter on this data. 405 setup_gzin(&u); 406 u.gzin->start(magic); 407 u.gzin->gzcrc = 0; 408 u.gzin->gzlen = 0; 409 if (!u.aborting()) { 410 u.start(); 411 } 412 } else { 413 u.start(peek, sizeof(peek)); 414 } 415 416 // Note: The checks to u.aborting() are necessary to gracefully 417 // terminate processing when the first segment throws an error. 418 419 for (;;) { 420 if (u.aborting()) break; 421 422 // Each trip through this loop unpacks one segment 423 // and then resets the unpacker. 424 for (unpacker::file* filep; (filep = u.get_next_file()) != null; ) { 425 if (u.aborting()) break; 426 u.write_file_to_jar(filep); 427 } 428 if (u.aborting()) break; 429 430 // Peek ahead for more data. 431 magic = read_magic(&u, peek, (int)sizeof(peek)); 432 if (magic != (int)JAVA_PACKAGE_MAGIC) { 433 if (magic != EOF_MAGIC) 434 u.abort("garbage after end of pack archive"); 435 break; // all done 436 } 437 438 // Release all storage from parsing the old segment. 439 u.reset(); 440 441 // Restart, beginning with the peek-ahead. 442 u.start(peek, sizeof(peek)); 443 } 444 445 int status = 0; 446 if (u.aborting()) { 447 fprintf(u.errstrm, "Error: %s\n", u.get_abort_message()); 448 status = 1; 449 } 450 451 if (u.infileptr != null) { 452 fclose(u.infileptr); 453 u.infileptr = null; 454 } 455 456 if (!u.aborting() && remove_source) 457 remove(source_file); 458 459 if (verbose != 0) { 460 fprintf(u.errstrm, "unpacker completed with status=%d\n", status); 461 } 462 463 u.finish(); 464 465 u.free(); // tidy up malloc blocks 466 set_current_unpacker(null); // clean up global pointer 467 468 return status; 469 }