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 }