0N/A/*
2362N/A * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A/**
0N/A * Note: Lifted from uncrunch.c from jdk sources
0N/A */
0N/A#include <stdio.h>
0N/A#include <string.h>
0N/A#include <errno.h>
0N/A#include <time.h>
0N/A
0N/A#include <stdlib.h>
0N/A
0N/A#ifndef _MSC_VER
0N/A#include <strings.h>
0N/A#endif
0N/A
0N/A#include "defines.h"
0N/A#include "bytes.h"
0N/A#include "utils.h"
0N/A
0N/A#include "constants.h"
0N/A#include "unpack.h"
0N/A
0N/A#include "zip.h"
0N/A
0N/A#ifdef NO_ZLIB
0N/A
0N/Ainline bool jar::deflate_bytes(bytes& head, bytes& tail) {
0N/A return false;
0N/A}
0N/Ainline uint jar::get_crc32(uint c, uchar *ptr, uint len) { return 0; }
0N/A#define Z_NULL NULL
0N/A
0N/A#else // Have ZLIB
0N/A
0N/A#include <zlib.h>
0N/A
0N/Ainline uint jar::get_crc32(uint c, uchar *ptr, uint len) { return crc32(c, ptr, len); }
0N/A
0N/A#endif // End of ZLIB
0N/A
0N/A#ifdef sparc
0N/A#define SWAP_BYTES(a) \
0N/A ((((a) << 8) & 0xff00) | 0x00ff) & (((a) >> 8) | 0xff00)
0N/A#else
0N/A#define SWAP_BYTES(a) (a)
0N/A#endif
0N/A
0N/A#define GET_INT_LO(a) \
0N/A SWAP_BYTES(a & 0xFFFF)
0N/A
0N/A#define GET_INT_HI(a) \
0N/A SWAP_BYTES((a >> 16) & 0xFFFF);
0N/A
0N/A
0N/Avoid jar::init(unpacker* u_) {
0N/A BYTES_OF(*this).clear();
0N/A u = u_;
0N/A u->jarout = this;
0N/A}
0N/A
0N/A// Write data to the ZIP output stream.
0N/Avoid jar::write_data(void* buff, int len) {
0N/A while (len > 0) {
494N/A int rc = (int)fwrite(buff, 1, len, jarfp);
0N/A if (rc <= 0) {
0N/A fprintf(u->errstrm, "Error: write on output file failed err=%d\n",errno);
0N/A exit(1); // Called only from the native standalone unpacker
0N/A }
0N/A output_file_offset += rc;
0N/A buff = ((char *)buff) + rc;
0N/A len -= rc;
0N/A }
0N/A}
0N/A
0N/Avoid jar::add_to_jar_directory(const char* fname, bool store, int modtime,
0N/A int len, int clen, uLong crc) {
494N/A uint fname_length = (uint)strlen(fname);
0N/A ushort header[23];
0N/A if (modtime == 0) modtime = default_modtime;
0N/A uLong dostime = get_dostime(modtime);
0N/A
494N/A header[0] = (ushort)SWAP_BYTES(0x4B50);
494N/A header[1] = (ushort)SWAP_BYTES(0x0201);
494N/A header[2] = (ushort)SWAP_BYTES(0xA);
0N/A
0N/A // required version
494N/A header[3] = (ushort)SWAP_BYTES(0xA);
0N/A
0N/A // flags 02 = maximum sub-compression flag
0N/A header[4] = ( store ) ? 0x0 : SWAP_BYTES(0x2);
0N/A
0N/A // Compression method 8=deflate.
0N/A header[5] = ( store ) ? 0x0 : SWAP_BYTES(0x08);
0N/A
0N/A // Last modified date and time.
494N/A header[6] = (ushort)GET_INT_LO(dostime);
494N/A header[7] = (ushort)GET_INT_HI(dostime);
0N/A
0N/A // CRC
494N/A header[8] = (ushort)GET_INT_LO(crc);
494N/A header[9] = (ushort)GET_INT_HI(crc);
0N/A
0N/A // Compressed length:
494N/A header[10] = (ushort)GET_INT_LO(clen);
494N/A header[11] = (ushort)GET_INT_HI(clen);
0N/A
0N/A // Uncompressed length.
494N/A header[12] = (ushort)GET_INT_LO(len);
494N/A header[13] = (ushort)GET_INT_HI(len);
0N/A
0N/A // Filename length
494N/A header[14] = (ushort)SWAP_BYTES(fname_length);
0N/A // So called "extra field" length.
0N/A header[15] = 0;
0N/A // So called "comment" length.
0N/A header[16] = 0;
0N/A // Disk number start
0N/A header[17] = 0;
0N/A // File flags => binary
0N/A header[18] = 0;
0N/A // More file flags
0N/A header[19] = 0;
0N/A header[20] = 0;
0N/A // Offset within ZIP file.
494N/A header[21] = (ushort)GET_INT_LO(output_file_offset);
494N/A header[22] = (ushort)GET_INT_HI(output_file_offset);
0N/A
0N/A // Copy the whole thing into the central directory.
0N/A central_directory.append(header, sizeof(header));
0N/A
0N/A // Copy the fname to the header.
0N/A central_directory.append(fname, fname_length);
0N/A
0N/A central_directory_count++;
0N/A}
0N/A
0N/Avoid jar::write_jar_header(const char* fname, bool store, int modtime,
0N/A int len, int clen, uint crc) {
494N/A uint fname_length = (uint)strlen(fname);
0N/A ushort header[15];
0N/A if (modtime == 0) modtime = default_modtime;
0N/A uLong dostime = get_dostime(modtime);
0N/A
0N/A // ZIP LOC magic.
494N/A header[0] = (ushort)SWAP_BYTES(0x4B50);
494N/A header[1] = (ushort)SWAP_BYTES(0x0403);
0N/A
0N/A // Version
494N/A header[2] = (ushort)SWAP_BYTES(0xA);
0N/A
0N/A // flags 02 = maximum sub-compression flag
0N/A header[3] = ( store ) ? 0x0 : SWAP_BYTES(0x2);
0N/A
0N/A // Compression method = deflate
0N/A header[4] = ( store ) ? 0x0 : SWAP_BYTES(0x08);
0N/A
0N/A // Last modified date and time.
494N/A header[5] = (ushort)GET_INT_LO(dostime);
494N/A header[6] = (ushort)GET_INT_HI(dostime);
0N/A
0N/A // CRC
494N/A header[7] = (ushort)GET_INT_LO(crc);
494N/A header[8] = (ushort)GET_INT_HI(crc);
0N/A
0N/A // Compressed length:
494N/A header[9] = (ushort)GET_INT_LO(clen);
494N/A header[10] = (ushort)GET_INT_HI(clen);
0N/A
0N/A // Uncompressed length.
494N/A header[11] = (ushort)GET_INT_LO(len);
494N/A header[12] = (ushort)GET_INT_HI(len);
0N/A
0N/A // Filename length
494N/A header[13] = (ushort)SWAP_BYTES(fname_length);
0N/A // So called "extra field" length.
0N/A header[14] = 0;
0N/A
0N/A // Write the LOC header to the output file.
494N/A write_data(header, (int)sizeof(header));
0N/A
0N/A // Copy the fname to the header.
494N/A write_data((char*)fname, (int)fname_length);
0N/A}
0N/A
0N/Astatic const char marker_comment[] = ZIP_ARCHIVE_MARKER_COMMENT;
0N/A
0N/Avoid jar::write_central_directory() {
0N/A bytes mc; mc.set(marker_comment);
0N/A
0N/A ushort header[11];
0N/A
0N/A // Create the End of Central Directory structure.
494N/A header[0] = (ushort)SWAP_BYTES(0x4B50);
494N/A header[1] = (ushort)SWAP_BYTES(0x0605);
0N/A // disk numbers
0N/A header[2] = 0;
0N/A header[3] = 0;
0N/A // Number of entries in central directory.
494N/A header[4] = (ushort)SWAP_BYTES(central_directory_count);
494N/A header[5] = (ushort)SWAP_BYTES(central_directory_count);
0N/A // Size of the central directory}
494N/A header[6] = (ushort)GET_INT_LO((int)central_directory.size());
494N/A header[7] = (ushort)GET_INT_HI((int)central_directory.size());
0N/A // Offset of central directory within disk.
494N/A header[8] = (ushort)GET_INT_LO(output_file_offset);
494N/A header[9] = (ushort)GET_INT_HI(output_file_offset);
0N/A // zipfile comment length;
494N/A header [10] = (ushort)SWAP_BYTES((int)mc.len);
0N/A
0N/A // Write the central directory.
494N/A PRINTCR((2, "Central directory at %d\n", output_file_offset));
0N/A write_data(central_directory.b);
0N/A
0N/A // Write the End of Central Directory structure.
494N/A PRINTCR((2, "end-of-directory at %d\n", output_file_offset));
494N/A write_data(header, (int)sizeof(header));
0N/A
494N/A PRINTCR((2, "writing zip comment\n"));
0N/A // Write the comment.
0N/A write_data(mc);
0N/A}
0N/A
0N/A// Public API
0N/A
0N/A// Open a Jar file and initialize.
0N/Avoid jar::openJarFile(const char* fname) {
0N/A if (!jarfp) {
494N/A PRINTCR((1, "jar::openJarFile: opening %s\n",fname));
0N/A jarfp = fopen(fname, "wb");
0N/A if (!jarfp) {
0N/A fprintf(u->errstrm, "Error: Could not open jar file: %s\n",fname);
0N/A exit(3); // Called only from the native standalone unpacker
0N/A }
0N/A }
0N/A}
0N/A
0N/A// Add a ZIP entry and copy the file data
0N/Avoid jar::addJarEntry(const char* fname,
0N/A bool deflate_hint, int modtime,
0N/A bytes& head, bytes& tail) {
494N/A int len = (int)(head.len + tail.len);
0N/A int clen = 0;
0N/A
494N/A uint crc = get_crc32(0,Z_NULL,0);
0N/A if (head.len != 0)
494N/A crc = get_crc32(crc, (uchar *)head.ptr, (uint)head.len);
0N/A if (tail.len != 0)
494N/A crc = get_crc32(crc, (uchar *)tail.ptr, (uint)tail.len);
0N/A
0N/A bool deflate = (deflate_hint && len > 0);
0N/A
0N/A if (deflate) {
0N/A if (deflate_bytes(head, tail) == false) {
494N/A PRINTCR((2, "Reverting to store fn=%s\t%d -> %d\n",
494N/A fname, len, deflated.size()));
0N/A deflate = false;
0N/A }
0N/A }
494N/A clen = (int)((deflate) ? deflated.size() : len);
0N/A add_to_jar_directory(fname, !deflate, modtime, len, clen, crc);
0N/A write_jar_header( fname, !deflate, modtime, len, clen, crc);
0N/A
0N/A if (deflate) {
0N/A write_data(deflated.b);
0N/A } else {
0N/A write_data(head);
0N/A write_data(tail);
0N/A }
0N/A}
0N/A
0N/A// Add a ZIP entry for a directory name no data
0N/Avoid jar::addDirectoryToJarFile(const char* dir_name) {
0N/A bool store = true;
0N/A add_to_jar_directory((const char*)dir_name, store, default_modtime, 0, 0, 0);
0N/A write_jar_header( (const char*)dir_name, store, default_modtime, 0, 0, 0);
0N/A}
0N/A
0N/A// Write out the central directory and close the jar file.
0N/Avoid jar::closeJarFile(bool central) {
0N/A if (jarfp) {
0N/A fflush(jarfp);
0N/A if (central) write_central_directory();
0N/A fflush(jarfp);
0N/A fclose(jarfp);
494N/A PRINTCR((2, "jar::closeJarFile:closed jar-file\n"));
0N/A }
0N/A reset();
0N/A}
0N/A
0N/A/* Convert the date y/n/d and time h:m:s to a four byte DOS date and
0N/A * time (date in high two bytes, time in low two bytes allowing magnitude
0N/A * comparison).
0N/A */
0N/Ainline
0N/AuLong jar::dostime(int y, int n, int d, int h, int m, int s) {
0N/A return y < 1980 ? dostime(1980, 1, 1, 0, 0, 0) :
0N/A (((uLong)y - 1980) << 25) | ((uLong)n << 21) | ((uLong)d << 16) |
0N/A ((uLong)h << 11) | ((uLong)m << 5) | ((uLong)s >> 1);
0N/A}
0N/A
0N/A#ifdef _REENTRANT // solaris
0N/Aextern "C" struct tm *gmtime_r(const time_t *, struct tm *);
0N/A#else
0N/A#define gmtime_r(t, s) gmtime(t)
0N/A#endif
0N/A/*
0N/A * Return the Unix time in DOS format
0N/A */
0N/AuLong jar::get_dostime(int modtime) {
0N/A // see defines.h
0N/A if (modtime != 0 && modtime == modtime_cache)
0N/A return dostime_cache;
0N/A if (modtime != 0 && default_modtime == 0)
0N/A default_modtime = modtime; // catch a reasonable default
0N/A time_t t = modtime;
0N/A struct tm sbuf;
494N/A (void)memset((void*)&sbuf,0, sizeof(sbuf));
0N/A struct tm* s = gmtime_r(&t, &sbuf);
0N/A modtime_cache = modtime;
0N/A dostime_cache = dostime(s->tm_year + 1900, s->tm_mon + 1, s->tm_mday,
0N/A s->tm_hour, s->tm_min, s->tm_sec);
0N/A //printf("modtime %d => %d\n", modtime_cache, dostime_cache);
0N/A return dostime_cache;
0N/A}
0N/A
0N/A
0N/A
0N/A#ifndef NO_ZLIB
0N/A
0N/A/* Returns true on success, and will set the clen to the compressed
0N/A length, the caller should verify if true and clen less than the
0N/A input data
0N/A*/
0N/Abool jar::deflate_bytes(bytes& head, bytes& tail) {
494N/A int len = (int)(head.len + tail.len);
0N/A
0N/A z_stream zs;
0N/A BYTES_OF(zs).clear();
0N/A
0N/A // NOTE: the window size should always be -MAX_WBITS normally -15.
0N/A // unzip/zipup.c and java/Deflater.c
0N/A
0N/A int error = deflateInit2(&zs, Z_BEST_COMPRESSION, Z_DEFLATED,
0N/A -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
0N/A if (error != Z_OK) {
0N/A switch (error) {
0N/A case Z_MEM_ERROR:
494N/A PRINTCR((2, "Error: deflate error : Out of memory \n"));
0N/A break;
0N/A case Z_STREAM_ERROR:
494N/A PRINTCR((2,"Error: deflate error : Invalid compression level \n"));
0N/A break;
0N/A case Z_VERSION_ERROR:
494N/A PRINTCR((2,"Error: deflate error : Invalid version\n"));
0N/A break;
0N/A default:
494N/A PRINTCR((2,"Error: Internal deflate error error = %d\n", error));
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A deflated.empty();
0N/A zs.next_out = (uchar*) deflated.grow(len + (len/2));
494N/A zs.avail_out = (int)deflated.size();
0N/A
0N/A zs.next_in = (uchar*)head.ptr;
494N/A zs.avail_in = (int)head.len;
0N/A
0N/A bytes* first = &head;
0N/A bytes* last = &tail;
0N/A if (last->len == 0) {
0N/A first = null;
0N/A last = &head;
0N/A } else if (first->len == 0) {
0N/A first = null;
0N/A }
0N/A
0N/A if (first != null && error == Z_OK) {
0N/A zs.next_in = (uchar*) first->ptr;
494N/A zs.avail_in = (int)first->len;
0N/A error = deflate(&zs, Z_NO_FLUSH);
0N/A }
0N/A if (error == Z_OK) {
0N/A zs.next_in = (uchar*) last->ptr;
494N/A zs.avail_in = (int)last->len;
0N/A error = deflate(&zs, Z_FINISH);
0N/A }
0N/A if (error == Z_STREAM_END) {
494N/A if (len > (int)zs.total_out ) {
494N/A PRINTCR((2, "deflate compressed data %d -> %d\n", len, zs.total_out));
0N/A deflated.b.len = zs.total_out;
0N/A deflateEnd(&zs);
0N/A return true;
0N/A }
494N/A PRINTCR((2, "deflate expanded data %d -> %d\n", len, zs.total_out));
0N/A deflateEnd(&zs);
0N/A return false;
0N/A }
0N/A
0N/A deflateEnd(&zs);
494N/A PRINTCR((2, "Error: deflate error deflate did not finish error=%d\n",error));
0N/A return false;
0N/A}
0N/A
0N/A// Callback for fetching data from a GZIP input stream
0N/Astatic jlong read_input_via_gzip(unpacker* u,
0N/A void* buf, jlong minlen, jlong maxlen) {
0N/A assert(minlen <= maxlen); // don't talk nonsense
0N/A jlong numread = 0;
0N/A char* bufptr = (char*) buf;
0N/A char* inbuf = u->gzin->inbuf;
0N/A size_t inbuflen = sizeof(u->gzin->inbuf);
0N/A unpacker::read_input_fn_t read_gzin_fn =
0N/A (unpacker::read_input_fn_t) u->gzin->read_input_fn;
0N/A z_stream& zs = *(z_stream*) u->gzin->zstream;
0N/A while (numread < minlen) {
0N/A int readlen = (1 << 16); // pretty arbitrary
0N/A if (readlen > (maxlen - numread))
0N/A readlen = (int)(maxlen - numread);
0N/A zs.next_out = (uchar*) bufptr;
0N/A zs.avail_out = readlen;
0N/A if (zs.avail_in == 0) {
0N/A zs.avail_in = (int) read_gzin_fn(u, inbuf, 1, inbuflen);
0N/A zs.next_in = (uchar*) inbuf;
0N/A }
0N/A int error = inflate(&zs, Z_NO_FLUSH);
0N/A if (error != Z_OK && error != Z_STREAM_END) {
0N/A u->abort("error inflating input");
0N/A break;
0N/A }
0N/A int nr = readlen - zs.avail_out;
0N/A numread += nr;
0N/A bufptr += nr;
0N/A assert(numread <= maxlen);
0N/A if (error == Z_STREAM_END) {
0N/A enum { TRAILER_LEN = 8 };
0N/A // skip 8-byte trailer
0N/A if (zs.avail_in >= TRAILER_LEN) {
0N/A zs.avail_in -= TRAILER_LEN;
0N/A } else {
0N/A // Bug: 5023768,we read past the TRAILER_LEN to see if there is
0N/A // any extraneous data, as we dont support concatenated .gz
0N/A // files just yet.
0N/A int extra = (int) read_gzin_fn(u, inbuf, 1, inbuflen);
0N/A zs.avail_in += extra - TRAILER_LEN;
0N/A }
0N/A // %%% should check final CRC and length here
0N/A // %%% should check for concatenated *.gz files here
0N/A if (zs.avail_in > 0)
0N/A u->abort("garbage after end of deflated input stream");
0N/A // pop this filter off:
0N/A u->gzin->free();
0N/A break;
0N/A }
0N/A }
0N/A
0N/A //fprintf(u->errstrm, "readInputFn(%d,%d) => %d (gunzip)\n",
0N/A // (int)minlen, (int)maxlen, (int)numread);
0N/A return numread;
0N/A}
0N/A
0N/Avoid gunzip::init(unpacker* u_) {
0N/A BYTES_OF(*this).clear();
0N/A u = u_;
0N/A assert(u->gzin == null); // once only, please
494N/A read_input_fn = (void*)u->read_input_fn;
0N/A zstream = NEW(z_stream, 1);
0N/A u->gzin = this;
0N/A u->read_input_fn = read_input_via_gzip;
0N/A}
0N/A
0N/Avoid gunzip::start(int magic) {
0N/A assert((magic & GZIP_MAGIC_MASK) == GZIP_MAGIC);
0N/A int gz_flg = (magic & 0xFF); // keep "flg", discard other 3 bytes
0N/A enum {
0N/A FHCRC = (1<<1),
0N/A FEXTRA = (1<<2),
0N/A FNAME = (1<<3),
0N/A FCOMMENT = (1<<4)
0N/A };
0N/A char gz_mtime[4];
0N/A char gz_xfl[1];
0N/A char gz_os[1];
0N/A char gz_extra_len[2];
0N/A char gz_hcrc[2];
0N/A char gz_ignore;
0N/A // do not save extra, name, comment
0N/A read_fixed_field(gz_mtime, sizeof(gz_mtime));
0N/A read_fixed_field(gz_xfl, sizeof(gz_xfl));
0N/A read_fixed_field(gz_os, sizeof(gz_os));
0N/A if (gz_flg & FEXTRA) {
0N/A read_fixed_field(gz_extra_len, sizeof(gz_extra_len));
0N/A int extra_len = gz_extra_len[0] & 0xFF;
0N/A extra_len += (gz_extra_len[1] & 0xFF) << 8;
0N/A for (; extra_len > 0; extra_len--) {
0N/A read_fixed_field(&gz_ignore, 1);
0N/A }
0N/A }
0N/A int null_terms = 0;
0N/A if (gz_flg & FNAME) null_terms++;
0N/A if (gz_flg & FCOMMENT) null_terms++;
0N/A for (; null_terms; null_terms--) {
0N/A for (;;) {
0N/A gz_ignore = 0;
0N/A read_fixed_field(&gz_ignore, 1);
0N/A if (gz_ignore == 0) break;
0N/A }
0N/A }
0N/A if (gz_flg & FHCRC)
0N/A read_fixed_field(gz_hcrc, sizeof(gz_hcrc));
0N/A
0N/A if (aborting()) return;
0N/A
0N/A // now the input stream is ready to read into the inflater
0N/A int error = inflateInit2((z_stream*) zstream, -MAX_WBITS);
0N/A if (error != Z_OK) { abort("cannot create input"); return; }
0N/A}
0N/A
0N/Avoid gunzip::free() {
0N/A assert(u->gzin == this);
0N/A u->gzin = null;
0N/A u->read_input_fn = (unpacker::read_input_fn_t) this->read_input_fn;
0N/A inflateEnd((z_stream*) zstream);
0N/A mtrace('f', zstream, 0);
0N/A ::free(zstream);
0N/A zstream = null;
0N/A mtrace('f', this, 0);
0N/A ::free(this);
0N/A}
0N/A
0N/Avoid gunzip::read_fixed_field(char* buf, size_t buflen) {
0N/A if (aborting()) return;
0N/A jlong nr = ((unpacker::read_input_fn_t)read_input_fn)
0N/A (u, buf, buflen, buflen);
494N/A if ((size_t)nr != buflen)
0N/A u->abort("short stream header");
0N/A}
0N/A
0N/A#else // NO_ZLIB
0N/A
0N/Avoid gunzip::free() {
0N/A}
0N/A
0N/A#endif // NO_ZLIB