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
62JNIEXPORT int
63main(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
72NOT_PRODUCT(static THRTYPE uThread = -1;)
73
74unpacker* unpacker::non_mt_current = null;
75unpacker* unpacker::current() {
76 //assert(uThread == THREAD_SELF);
77 return non_mt_current;
78}
79static 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.
86static 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
121enum { EOF_MAGIC = 0, BAD_MAGIC = -1 };
122static 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
136static void setup_gzin(unpacker* u) {
137 gunzip* gzin = NEW(gunzip, 1);
138 gzin->init(u);
139}
140
141static const char* nbasename(const char* progname) {
142 const char* slash = strrchr(progname, PATH_SEPARATOR);
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 deprecated, and is planned for removal in a future JDK release.\n\n"
166
167#define SUPPRESS_DEPRECATE_MSG "-XDsuppress-tool-removal-message"
168
169static bool suppress_warning = false;
170
171static 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
184static 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
220static int strpcmp(const char* str, const char* pfx) {
221 return strncmp(str, pfx, strlen(pfx));
222}
223
224static const char flag_opts[] = "vqrVh?";
225static const char string_opts[] = "HlJ";
226
227static 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
292static const char sccsver[] = "1.30, 07/05/05";
293
294// Usage: unpackage input.pack output.jar
295int 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}
470