| Index: third_party/ijar/zip.cc
|
| diff --git a/third_party/ijar/zip.cc b/third_party/ijar/zip.cc
|
| index cb9d1cc9d776e6b311e983fdbaec579faaf7730e..44372f443bb5ae619585c5b8d565dfd21c28afc3 100644
|
| --- a/third_party/ijar/zip.cc
|
| +++ b/third_party/ijar/zip.cc
|
| @@ -1,6 +1,4 @@
|
| -// Copyright 2007 Alan Donovan. All rights reserved.
|
| -//
|
| -// Author: Alan Donovan <adonovan@google.com>
|
| +// Copyright 2015 The Bazel Authors. All rights reserved.
|
| //
|
| // Licensed under the Apache License, Version 2.0 (the "License");
|
| // you may not use this file except in compliance with the License.
|
| @@ -38,13 +36,24 @@
|
| #include <limits>
|
| #include <vector>
|
|
|
| +#include "third_party/ijar/mapped_file.h"
|
| #include "third_party/ijar/zip.h"
|
| #include <zlib.h>
|
|
|
| -#define LOCAL_FILE_HEADER_SIGNATURE 0x04034b50
|
| -#define CENTRAL_FILE_HEADER_SIGNATURE 0x02014b50
|
| -#define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50
|
| -#define DATA_DESCRIPTOR_SIGNATURE 0x08074b50
|
| +#define LOCAL_FILE_HEADER_SIGNATURE 0x04034b50
|
| +#define CENTRAL_FILE_HEADER_SIGNATURE 0x02014b50
|
| +#define DIGITAL_SIGNATURE 0x05054b50
|
| +#define ZIP64_EOCD_SIGNATURE 0x06064b50
|
| +#define ZIP64_EOCD_LOCATOR_SIGNATURE 0x07064b50
|
| +#define EOCD_SIGNATURE 0x06054b50
|
| +#define DATA_DESCRIPTOR_SIGNATURE 0x08074b50
|
| +
|
| +#define U2_MAX 0xffff
|
| +#define U4_MAX 0xffffffffUL
|
| +
|
| +#define ZIP64_EOCD_LOCATOR_SIZE 20
|
| +// zip64 eocd is fixed size in the absence of a zip64 extensible data sector
|
| +#define ZIP64_EOCD_FIXED_SIZE 56
|
|
|
| // version to extract: 1.0 - default value from APPNOTE.TXT.
|
| // Output JAR files contain no extra ZIP features, so this is enough.
|
| @@ -65,22 +74,13 @@ namespace devtools_ijar {
|
| // http://www.info-zip.org/FAQ.html#limits
|
| static const u8 kMaximumOutputSize = std::numeric_limits<uint32_t>::max();
|
|
|
| -static bool ProcessCentralDirEntry(const u1 *&p,
|
| - size_t *compressed_size,
|
| - size_t *uncompressed_size,
|
| - char *filename,
|
| - size_t filename_size,
|
| - u4 *attr,
|
| - u4 *offset);
|
| -
|
| //
|
| // A class representing a ZipFile for reading. Its public API is exposed
|
| // using the ZipExtractor abstract class.
|
| //
|
| class InputZipFile : public ZipExtractor {
|
| public:
|
| - InputZipFile(ZipExtractorProcessor *processor, int fd, off_t in_length,
|
| - off_t in_offset, const u1* zipdata_in, const u1* central_dir);
|
| + InputZipFile(ZipExtractorProcessor *processor, const char* filename);
|
| virtual ~InputZipFile();
|
|
|
| virtual const char* GetError() {
|
| @@ -90,27 +90,32 @@ class InputZipFile : public ZipExtractor {
|
| return errmsg;
|
| }
|
|
|
| + bool Open();
|
| virtual bool ProcessNext();
|
| virtual void Reset();
|
| virtual size_t GetSize() {
|
| - return in_length_;
|
| + return input_file_->Length();
|
| }
|
|
|
| virtual u8 CalculateOutputLength();
|
|
|
| + virtual bool ProcessCentralDirEntry(const u1 *&p, size_t *compressed_size,
|
| + size_t *uncompressed_size, char *filename,
|
| + size_t filename_size, u4 *attr,
|
| + u4 *offset);
|
| +
|
| private:
|
| ZipExtractorProcessor *processor;
|
| -
|
| - int fd_in; // Input file descripor
|
| + const char* filename_;
|
| + MappedInputFile *input_file_;
|
|
|
| // InputZipFile is responsible for maintaining the following
|
| // pointers. They are allocated by the Create() method before
|
| // the object is actually created using mmap.
|
| - const u1 * const zipdata_in_; // start of input file mmap
|
| - const u1 * zipdata_in_mapped_; // start of still mapped region
|
| - const u1 * const central_dir_; // central directory in input file
|
| + const u1 * zipdata_in_; // start of input file mmap
|
| + size_t bytes_unmapped_; // bytes that have already been unmapped
|
| + const u1 * central_dir_; // central directory in input file
|
|
|
| - size_t in_length_; // size of the input file
|
| size_t in_offset_; // offset the input file
|
|
|
| const u1 *p; // input cursor
|
| @@ -122,10 +127,10 @@ class InputZipFile : public ZipExtractor {
|
| // not enough, we bail out. We only decompress class files, so they should
|
| // be smaller than 64K anyway, but we give a little leeway.
|
| // MAX_BUFFER_SIZE must be bigger than the size of the biggest file in the
|
| - // ZIP. It is set to 128M here so we can uncompress the Bazel server with
|
| - // this library.
|
| + // ZIP. It is set to 2GB here because no one has audited the code for 64-bit
|
| + // cleanliness.
|
| static const size_t INITIAL_BUFFER_SIZE = 256 * 1024; // 256K
|
| - static const size_t MAX_BUFFER_SIZE = 128 * 1024 * 1024;
|
| + static const size_t MAX_BUFFER_SIZE = std::numeric_limits<int32_t>::max();
|
| static const size_t MAX_MAPPED_REGION = 32 * 1024 * 1024;
|
|
|
| // These metadata fields are the fields of the ZIP header of the file being
|
| @@ -168,7 +173,7 @@ class InputZipFile : public ZipExtractor {
|
| // we're about to read, for diagnostics.
|
| int EnsureRemaining(size_t n, const char *state) {
|
| size_t in_offset = p - zipdata_in_;
|
| - size_t remaining = in_length_ - in_offset;
|
| + size_t remaining = input_file_->Length() - in_offset;
|
| if (n > remaining) {
|
| return error("Premature end of file (at offset %zd, state=%s); "
|
| "expected %zd more bytes but found %zd.\n",
|
| @@ -197,10 +202,11 @@ class InputZipFile : public ZipExtractor {
|
| //
|
| class OutputZipFile : public ZipBuilder {
|
| public:
|
| - OutputZipFile(int fd, u1 * const zipdata_out) :
|
| - fd_out(fd),
|
| - zipdata_out_(zipdata_out),
|
| - q(zipdata_out) {
|
| + OutputZipFile(const char* filename, u8 estimated_size) :
|
| + output_file_(NULL),
|
| + filename_(filename),
|
| + estimated_size_(estimated_size),
|
| + finished_(false) {
|
| errmsg[0] = 0;
|
| }
|
|
|
| @@ -213,7 +219,8 @@ class OutputZipFile : public ZipBuilder {
|
|
|
| virtual ~OutputZipFile() { Finish(); }
|
| virtual u1* NewFile(const char* filename, const u4 attr);
|
| - virtual int FinishFile(size_t filelength, bool compress = false);
|
| + virtual int FinishFile(size_t filelength, bool compress = false,
|
| + bool compute_crc = false);
|
| virtual int WriteEmptyFile(const char *filename);
|
| virtual size_t GetSize() {
|
| return Offset(q);
|
| @@ -222,6 +229,7 @@ class OutputZipFile : public ZipBuilder {
|
| return entries_.size();
|
| }
|
| virtual int Finish();
|
| + bool Open();
|
|
|
| private:
|
| struct LocalFileEntry {
|
| @@ -235,6 +243,9 @@ class OutputZipFile : public ZipBuilder {
|
| // Compression method
|
| u2 compression_method;
|
|
|
| + // CRC32
|
| + u4 crc32;
|
| +
|
| // external attributes field
|
| u4 external_attr;
|
|
|
| @@ -247,12 +258,15 @@ class OutputZipFile : public ZipBuilder {
|
| u2 extra_field_length;
|
| };
|
|
|
| - int fd_out; // file descriptor for the output file
|
| + MappedOutputFile* output_file_;
|
| + const char* filename_;
|
| + u8 estimated_size_;
|
| + bool finished_;
|
|
|
| // OutputZipFile is responsible for maintaining the following
|
| // pointers. They are allocated by the Create() method before
|
| // the object is actually created using mmap.
|
| - u1 * const zipdata_out_; // start of output file mmap
|
| + u1 *zipdata_out_; // start of output file mmap
|
| u1 *q; // output cursor
|
|
|
| u1 *header_ptr; // Current pointer to "compression method" entry.
|
| @@ -289,8 +303,10 @@ class OutputZipFile : public ZipBuilder {
|
|
|
| // Fill in the "compressed size" and "uncompressed size" fields in a local
|
| // file header previously written by WriteLocalFileHeader().
|
| - size_t WriteFileSizeInLocalFileHeader(u1 *header_ptr, size_t out_length,
|
| - bool compress = false);
|
| + size_t WriteFileSizeInLocalFileHeader(u1 *header_ptr,
|
| + size_t out_length,
|
| + bool compress = false,
|
| + const u4 crc = 0);
|
| };
|
|
|
| //
|
| @@ -413,9 +429,10 @@ int InputZipFile::ProcessLocalFileEntry(
|
| }
|
| }
|
|
|
| - if (p > zipdata_in_mapped_ + MAX_MAPPED_REGION) {
|
| - munmap(const_cast<u1 *>(zipdata_in_mapped_), MAX_MAPPED_REGION);
|
| - zipdata_in_mapped_ += MAX_MAPPED_REGION;
|
| + size_t bytes_processed = p - zipdata_in_;
|
| + if (bytes_processed > bytes_unmapped_ + MAX_MAPPED_REGION) {
|
| + input_file_->Discard(MAX_MAPPED_REGION);
|
| + bytes_unmapped_ += MAX_MAPPED_REGION;
|
| }
|
|
|
| return 0;
|
| @@ -440,7 +457,7 @@ int InputZipFile::SkipFile(const bool compressed) {
|
|
|
| u1* InputZipFile::UncompressFile() {
|
| size_t in_offset = p - zipdata_in_;
|
| - size_t remaining = in_length_ - in_offset;
|
| + size_t remaining = input_file_->Length() - in_offset;
|
| z_stream stream;
|
|
|
| stream.zalloc = Z_NULL;
|
| @@ -547,11 +564,17 @@ int InputZipFile::ProcessFile(const bool compressed) {
|
| // Of course, in the latter case, the size output variables are not changed.
|
| // Note that the central directory is always followed by another data structure
|
| // that has a signature, so parsing it this way is safe.
|
| -static bool ProcessCentralDirEntry(
|
| - const u1 *&p, size_t *compressed_size, size_t *uncompressed_size,
|
| - char *filename, size_t filename_size, u4 *attr, u4 *offset) {
|
| +bool InputZipFile::ProcessCentralDirEntry(const u1 *&p, size_t *compressed_size,
|
| + size_t *uncompressed_size,
|
| + char *filename, size_t filename_size,
|
| + u4 *attr, u4 *offset) {
|
| u4 signature = get_u4le(p);
|
| +
|
| if (signature != CENTRAL_FILE_HEADER_SIGNATURE) {
|
| + if (signature != DIGITAL_SIGNATURE && signature != EOCD_SIGNATURE &&
|
| + signature != ZIP64_EOCD_SIGNATURE) {
|
| + error("invalid central file header signature: 0x%x\n", signature);
|
| + }
|
| return false;
|
| }
|
|
|
| @@ -609,14 +632,132 @@ u8 InputZipFile::CalculateOutputLength() {
|
| // The worst case is when the output is simply the input uncompressed. The
|
| // metadata in the zip file will stay the same, so the file will grow by the
|
| // difference between the compressed and uncompressed sizes.
|
| - return (u8) in_length_ - skipped_compressed_size
|
| + return (u8) input_file_->Length() - skipped_compressed_size
|
| + (uncompressed_size - compressed_size);
|
| }
|
|
|
| +// An end of central directory record, sized for optional zip64 contents.
|
| +struct EndOfCentralDirectoryRecord {
|
| + u4 number_of_this_disk;
|
| + u4 disk_with_central_dir;
|
| + u8 central_dir_entries_on_this_disk;
|
| + u8 central_dir_entries;
|
| + u8 central_dir_size;
|
| + u8 central_dir_offset;
|
| +};
|
| +
|
| +// Checks for a zip64 end of central directory record. If a valid zip64 EOCD is
|
| +// found, updates the original EOCD record and returns true.
|
| +bool MaybeReadZip64CentralDirectory(const u1 *bytes, size_t in_length,
|
| + const u1 *current,
|
| + const u1 **end_of_central_dir,
|
| + EndOfCentralDirectoryRecord *cd) {
|
| + if (current < bytes) {
|
| + return false;
|
| + }
|
| + const u1 *candidate = current;
|
| + u4 zip64_directory_signature = get_u4le(current);
|
| + if (zip64_directory_signature != ZIP64_EOCD_SIGNATURE) {
|
| + return false;
|
| + }
|
| +
|
| + // size of zip64 end of central directory record
|
| + // (fixed size unless there's a zip64 extensible data sector, which
|
| + // we don't need to read)
|
| + get_u8le(current);
|
| + get_u2be(current); // version made by
|
| + get_u2be(current); // version needed to extract
|
| +
|
| + u4 number_of_this_disk = get_u4be(current);
|
| + u4 disk_with_central_dir = get_u4le(current);
|
| + u8 central_dir_entries_on_this_disk = get_u8le(current);
|
| + u8 central_dir_entries = get_u8le(current);
|
| + u8 central_dir_size = get_u8le(current);
|
| + u8 central_dir_offset = get_u8le(current);
|
| +
|
| + // check for a zip64 EOCD that matches the regular EOCD
|
| + if (number_of_this_disk != cd->number_of_this_disk &&
|
| + cd->number_of_this_disk != U2_MAX) {
|
| + return false;
|
| + }
|
| + if (disk_with_central_dir != cd->disk_with_central_dir &&
|
| + cd->disk_with_central_dir != U2_MAX) {
|
| + return false;
|
| + }
|
| + if (central_dir_entries_on_this_disk !=
|
| + cd->central_dir_entries_on_this_disk &&
|
| + cd->central_dir_entries_on_this_disk != U2_MAX) {
|
| + return false;
|
| + }
|
| + if (central_dir_entries != cd->central_dir_entries &&
|
| + cd->central_dir_entries != U2_MAX) {
|
| + return false;
|
| + }
|
| + if (central_dir_size != cd->central_dir_size &&
|
| + cd->central_dir_size != U4_MAX) {
|
| + return false;
|
| + }
|
| + if (central_dir_offset != cd->central_dir_offset &&
|
| + cd->central_dir_offset != U4_MAX) {
|
| + return false;
|
| + }
|
| +
|
| + *end_of_central_dir = candidate;
|
| + cd->number_of_this_disk = number_of_this_disk;
|
| + cd->disk_with_central_dir = disk_with_central_dir;
|
| + cd->central_dir_entries_on_this_disk = central_dir_entries_on_this_disk;
|
| + cd->central_dir_entries = central_dir_entries;
|
| + cd->central_dir_size = central_dir_size;
|
| + cd->central_dir_offset = central_dir_offset;
|
| + return true;
|
| +}
|
| +
|
| +// Starting from the end of central directory record, attempts to locate a zip64
|
| +// end of central directory record. If found, updates the given record and
|
| +// offset with the zip64 data. Returns false on error.
|
| +bool FindZip64CentralDirectory(const u1 *bytes, size_t in_length,
|
| + const u1 **end_of_central_dir,
|
| + EndOfCentralDirectoryRecord *cd) {
|
| + // In the absence of a zip64 extensible data sector, the zip64 EOCD is at a
|
| + // fixed offset from the regular central directory.
|
| + if (MaybeReadZip64CentralDirectory(
|
| + bytes, in_length,
|
| + *end_of_central_dir - ZIP64_EOCD_LOCATOR_SIZE - ZIP64_EOCD_FIXED_SIZE,
|
| + end_of_central_dir, cd)) {
|
| + return true;
|
| + }
|
| +
|
| + // If we couldn't find a zip64 EOCD at a fixed offset, either it doesn't exist
|
| + // or there was a zip64 extensible data sector, so try going through the
|
| + // locator. This approach doesn't work if data was prepended to the archive
|
| + // without updating the offset in the locator.
|
| + const u1 *zip64_locator = *end_of_central_dir - ZIP64_EOCD_LOCATOR_SIZE;
|
| + if (zip64_locator - ZIP64_EOCD_FIXED_SIZE < bytes) {
|
| + return true;
|
| + }
|
| + u4 zip64_locator_signature = get_u4le(zip64_locator);
|
| + if (zip64_locator_signature != ZIP64_EOCD_LOCATOR_SIGNATURE) {
|
| + return true;
|
| + }
|
| + u4 disk_with_zip64_central_directory = get_u4le(zip64_locator);
|
| + u8 zip64_end_of_central_dir_offset = get_u8le(zip64_locator);
|
| + u4 zip64_total_disks = get_u4le(zip64_locator);
|
| + if (MaybeReadZip64CentralDirectory(bytes, in_length,
|
| + bytes + zip64_end_of_central_dir_offset,
|
| + end_of_central_dir, cd)) {
|
| + if (disk_with_zip64_central_directory != 0 || zip64_total_disks != 1) {
|
| + fprintf(stderr, "multi-disk JAR files are not supported\n");
|
| + return false;
|
| + }
|
| + return true;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| // Given the data in the zip file, returns the offset of the central directory
|
| // and the number of files contained in it.
|
| -bool FindZipCentralDirectory(const u1* bytes, size_t in_length,
|
| - u4* offset, const u1** central_dir) {
|
| +bool FindZipCentralDirectory(const u1 *bytes, size_t in_length, u4 *offset,
|
| + const u1 **central_dir) {
|
| static const int MAX_COMMENT_LENGTH = 0xffff;
|
| static const int CENTRAL_DIR_LOCATOR_SIZE = 22;
|
| // Maximum distance of start of central dir locator from end of file
|
| @@ -631,7 +772,7 @@ bool FindZipCentralDirectory(const u1* bytes, size_t in_length,
|
| current >= last_pos_to_check;
|
| current-- ) {
|
| const u1* p = current;
|
| - if (get_u4le(p) != END_OF_CENTRAL_DIR_SIGNATURE) {
|
| + if (get_u4le(p) != EOCD_SIGNATURE) {
|
| continue;
|
| }
|
|
|
| @@ -655,36 +796,40 @@ bool FindZipCentralDirectory(const u1* bytes, size_t in_length,
|
| return false;
|
| }
|
|
|
| + EndOfCentralDirectoryRecord cd;
|
| const u1* end_of_central_dir = current;
|
| get_u4le(current); // central directory locator signature, already checked
|
| - u2 number_of_this_disk = get_u2le(current);
|
| - u2 disk_with_central_dir = get_u2le(current);
|
| - u2 central_dir_entries_on_this_disk = get_u2le(current);
|
| - u2 central_dir_entries = get_u2le(current);
|
| - u4 central_dir_size = get_u4le(current);
|
| - u4 central_dir_offset = get_u4le(current);
|
| + cd.number_of_this_disk = get_u2le(current);
|
| + cd.disk_with_central_dir = get_u2le(current);
|
| + cd.central_dir_entries_on_this_disk = get_u2le(current);
|
| + cd.central_dir_entries = get_u2le(current);
|
| + cd.central_dir_size = get_u4le(current);
|
| + cd.central_dir_offset = get_u4le(current);
|
| u2 file_comment_length = get_u2le(current);
|
| current += file_comment_length; // set current to the end of the central dir
|
|
|
| - if (number_of_this_disk != 0
|
| - || disk_with_central_dir != 0
|
| - || central_dir_entries_on_this_disk != central_dir_entries) {
|
| + if (!FindZip64CentralDirectory(bytes, in_length, &end_of_central_dir, &cd)) {
|
| + return false;
|
| + }
|
| +
|
| + if (cd.number_of_this_disk != 0 || cd.disk_with_central_dir != 0 ||
|
| + cd.central_dir_entries_on_this_disk != cd.central_dir_entries) {
|
| fprintf(stderr, "multi-disk JAR files are not supported\n");
|
| return false;
|
| }
|
|
|
| // Do not change output values before determining that they are OK.
|
| - *offset = central_dir_offset;
|
| + *offset = cd.central_dir_offset;
|
| // Central directory start can then be used to determine the actual
|
| // starts of the zip file (which can be different in case of a non-zip
|
| // header like for auto-extractable binaries).
|
| - *central_dir = end_of_central_dir - central_dir_size;
|
| + *central_dir = end_of_central_dir - cd.central_dir_size;
|
| return true;
|
| }
|
|
|
| void InputZipFile::Reset() {
|
| central_dir_current_ = central_dir_;
|
| - zipdata_in_mapped_ = zipdata_in_;
|
| + bytes_unmapped_ = 0;
|
| p = zipdata_in_ + in_offset_;
|
| }
|
|
|
| @@ -698,55 +843,68 @@ int ZipExtractor::ProcessAll() {
|
|
|
| ZipExtractor* ZipExtractor::Create(const char* filename,
|
| ZipExtractorProcessor *processor) {
|
| - int fd_in = open(filename, O_RDONLY);
|
| - if (fd_in < 0) {
|
| + InputZipFile* result = new InputZipFile(processor, filename);
|
| + if (!result->Open()) {
|
| + fprintf(stderr, "%s\n", result->GetError());
|
| + delete result;
|
| return NULL;
|
| }
|
|
|
| - off_t length = lseek(fd_in, 0, SEEK_END);
|
| - if (length < 0) {
|
| - return NULL;
|
| - }
|
| + return result;
|
| +}
|
|
|
| - void *zipdata_in = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd_in, 0);
|
| - if (zipdata_in == MAP_FAILED) {
|
| - return NULL;
|
| +// zipdata_in_, in_offset_, p, central_dir_current_
|
| +
|
| +InputZipFile::InputZipFile(ZipExtractorProcessor *processor,
|
| + const char* filename)
|
| + : processor(processor), filename_(filename), input_file_(NULL),
|
| + bytes_unmapped_(0) {
|
| + uncompressed_data_allocated_ = INITIAL_BUFFER_SIZE;
|
| + uncompressed_data_ =
|
| + reinterpret_cast<u1*>(malloc(uncompressed_data_allocated_));
|
| + errmsg[0] = 0;
|
| +}
|
| +
|
| +bool InputZipFile::Open() {
|
| + MappedInputFile* input_file = new MappedInputFile(filename_);
|
| + if (!input_file->Opened()) {
|
| + snprintf(errmsg, sizeof(errmsg), "%s", input_file->Error());
|
| + delete input_file;
|
| + return false;
|
| }
|
|
|
| + void *zipdata_in = input_file->Buffer();
|
| u4 central_dir_offset;
|
| const u1 *central_dir = NULL;
|
|
|
| if (!devtools_ijar::FindZipCentralDirectory(
|
| - static_cast<const u1*>(zipdata_in), length,
|
| + static_cast<const u1*>(zipdata_in), input_file->Length(),
|
| ¢ral_dir_offset, ¢ral_dir)) {
|
| errno = EIO; // we don't really have a good error number
|
| - return NULL;
|
| + error("Cannot find central directory");
|
| + delete input_file;
|
| + return false;
|
| }
|
| const u1 *zipdata_start = static_cast<const u1*>(zipdata_in);
|
| - off_t offset = - static_cast<off_t>(zipdata_start
|
| - + central_dir_offset
|
| - - central_dir);
|
| -
|
| - return new InputZipFile(processor, fd_in, length, offset,
|
| - zipdata_start, central_dir);
|
| -}
|
| -
|
| -InputZipFile::InputZipFile(ZipExtractorProcessor *processor, int fd,
|
| - off_t in_length, off_t in_offset,
|
| - const u1* zipdata_in, const u1* central_dir)
|
| - : processor(processor), fd_in(fd),
|
| - zipdata_in_(zipdata_in), zipdata_in_mapped_(zipdata_in),
|
| - central_dir_(central_dir), in_length_(in_length), in_offset_(in_offset),
|
| - p(zipdata_in + in_offset), central_dir_current_(central_dir) {
|
| - uncompressed_data_allocated_ = INITIAL_BUFFER_SIZE;
|
| - uncompressed_data_ =
|
| - reinterpret_cast<u1*>(malloc(uncompressed_data_allocated_));
|
| + in_offset_ = - static_cast<off_t>(zipdata_start
|
| + + central_dir_offset
|
| + - central_dir);
|
| +
|
| + input_file_ = input_file;
|
| + zipdata_in_ = zipdata_start;
|
| + central_dir_ = central_dir;
|
| + central_dir_current_ = central_dir;
|
| + p = zipdata_in_ + in_offset_;
|
| errmsg[0] = 0;
|
| + return true;
|
| }
|
|
|
| InputZipFile::~InputZipFile() {
|
| free(uncompressed_data_);
|
| - close(fd_in);
|
| + if (input_file_ != NULL) {
|
| + input_file_->Close();
|
| + delete input_file_;
|
| + }
|
| }
|
|
|
|
|
| @@ -760,6 +918,7 @@ int OutputZipFile::WriteEmptyFile(const char *filename) {
|
| LocalFileEntry *entry = new LocalFileEntry;
|
| entry->local_header_offset = Offset(q);
|
| entry->external_attr = 0;
|
| + entry->crc32 = 0;
|
|
|
| // Output the ZIP local_file_header:
|
| put_u4le(q, LOCAL_FILE_HEADER_SIGNATURE);
|
| @@ -768,7 +927,7 @@ int OutputZipFile::WriteEmptyFile(const char *filename) {
|
| put_u2le(q, 0); // compression_method
|
| put_u2le(q, 0); // last_mod_file_time
|
| put_u2le(q, 0); // last_mod_file_date
|
| - put_u4le(q, 0); // crc32
|
| + put_u4le(q, entry->crc32); // crc32
|
| put_u4le(q, 0); // compressed_size
|
| put_u4le(q, 0); // uncompressed_size
|
| put_u2le(q, file_name_length);
|
| @@ -800,7 +959,7 @@ void OutputZipFile::WriteCentralDirectory() {
|
| put_u2le(q, entry->compression_method); // compression method:
|
| put_u2le(q, 0); // last_mod_file_time
|
| put_u2le(q, 0); // last_mod_file_date
|
| - put_u4le(q, 0); // crc32 (jar/javac tools don't care)
|
| + put_u4le(q, entry->crc32); // crc32
|
| put_u4le(q, entry->compressed_length); // compressed_size
|
| put_u4le(q, entry->uncompressed_length); // uncompressed_size
|
| put_u2le(q, entry->file_name_length);
|
| @@ -816,17 +975,60 @@ void OutputZipFile::WriteCentralDirectory() {
|
| put_n(q, entry->file_name, entry->file_name_length);
|
| put_n(q, entry->extra_field, entry->extra_field_length);
|
| }
|
| - u4 central_directory_size = q - central_directory_start;
|
| -
|
| - put_u4le(q, END_OF_CENTRAL_DIR_SIGNATURE);
|
| - put_u2le(q, 0); // number of this disk
|
| - put_u2le(q, 0); // number of the disk with the start of the central directory
|
| - put_u2le(q, entries_.size()); // # central dir entries on this disk
|
| - put_u2le(q, entries_.size()); // total # entries in the central directory
|
| - put_u4le(q, central_directory_size); // size of the central directory
|
| - put_u4le(q, Offset(central_directory_start)); // offset of start of central
|
| - // directory wrt starting disk
|
| - put_u2le(q, 0); // .ZIP file comment length
|
| + u8 central_directory_size = q - central_directory_start;
|
| +
|
| + if (entries_.size() > U2_MAX || central_directory_size > U4_MAX ||
|
| + Offset(central_directory_start) > U4_MAX) {
|
| + u1 *zip64_end_of_central_directory_start = q;
|
| +
|
| + put_u4le(q, ZIP64_EOCD_SIGNATURE);
|
| + // signature and size field doesn't count towards size
|
| + put_u8le(q, ZIP64_EOCD_FIXED_SIZE - 12);
|
| + put_u2le(q, 0); // version made by
|
| + put_u2le(q, 0); // version needed to extract
|
| + put_u4le(q, 0); // number of this disk
|
| + put_u4le(q, 0); // # of the disk with the start of the central directory
|
| + put_u8le(q, entries_.size()); // # central dir entries on this disk
|
| + put_u8le(q, entries_.size()); // total # entries in the central directory
|
| + put_u8le(q, central_directory_size); // size of the central directory
|
| + // offset of start of central directory wrt starting disk
|
| + put_u8le(q, Offset(central_directory_start));
|
| +
|
| + put_u4le(q, ZIP64_EOCD_LOCATOR_SIGNATURE);
|
| + // number of the disk with the start of the zip64 end of central directory
|
| + put_u4le(q, 0);
|
| + // relative offset of the zip64 end of central directory record
|
| + put_u8le(q, Offset(zip64_end_of_central_directory_start));
|
| + // total number of disks
|
| + put_u4le(q, 1);
|
| +
|
| + put_u4le(q, EOCD_SIGNATURE);
|
| + put_u2le(q, 0); // number of this disk
|
| + put_u2le(q, 0); // # of disk with the start of the central directory
|
| + // # central dir entries on this disk
|
| + put_u2le(q, entries_.size() > 0xffff ? 0xffff : entries_.size());
|
| + // total # entries in the central directory
|
| + put_u2le(q, entries_.size() > 0xffff ? 0xffff : entries_.size());
|
| + // size of the central directory
|
| + put_u4le(q,
|
| + central_directory_size > U4_MAX ? U4_MAX : central_directory_size);
|
| + // offset of start of central
|
| + put_u4le(q, Offset(central_directory_start) > U4_MAX
|
| + ? U4_MAX
|
| + : Offset(central_directory_start));
|
| + put_u2le(q, 0); // .ZIP file comment length
|
| +
|
| + } else {
|
| + put_u4le(q, EOCD_SIGNATURE);
|
| + put_u2le(q, 0); // number of this disk
|
| + put_u2le(q, 0); // # of the disk with the start of the central directory
|
| + put_u2le(q, entries_.size()); // # central dir entries on this disk
|
| + put_u2le(q, entries_.size()); // total # entries in the central directory
|
| + put_u4le(q, central_directory_size); // size of the central directory
|
| + // offset of start of central directory wrt starting disk
|
| + put_u4le(q, Offset(central_directory_start));
|
| + put_u2le(q, 0); // .ZIP file comment length
|
| + }
|
| }
|
|
|
| u1* OutputZipFile::WriteLocalFileHeader(const char* filename, const u4 attr) {
|
| @@ -848,7 +1050,7 @@ u1* OutputZipFile::WriteLocalFileHeader(const char* filename, const u4 attr) {
|
| put_u2le(q, COMPRESSION_METHOD_STORED); // compression method = placeholder
|
| put_u2le(q, 0); // last_mod_file_time
|
| put_u2le(q, 0); // last_mod_file_date
|
| - put_u4le(q, 0); // crc32 (jar/javac tools don't care)
|
| + put_u4le(q, entry->crc32); // crc32
|
| put_u4le(q, 0); // compressed_size = placeholder
|
| put_u4le(q, 0); // uncompressed_size = placeholder
|
| put_u2le(q, entry->file_name_length);
|
| @@ -880,7 +1082,9 @@ size_t TryDeflate(u1 *buf, size_t length) {
|
| stream.next_in = buf;
|
| stream.next_out = outbuf;
|
|
|
| - if (deflateInit(&stream, Z_DEFAULT_COMPRESSION) != Z_OK) {
|
| + // deflateInit2 negative windows size prevent the zlib wrapper to be used.
|
| + if (deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
| + -MAX_WBITS, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
|
| // Failure to compress => return the buffer uncompressed
|
| free(outbuf);
|
| return length;
|
| @@ -901,7 +1105,8 @@ size_t TryDeflate(u1 *buf, size_t length) {
|
|
|
| size_t OutputZipFile::WriteFileSizeInLocalFileHeader(u1 *header_ptr,
|
| size_t out_length,
|
| - bool compress) {
|
| + bool compress,
|
| + const u4 crc) {
|
| size_t compressed_size = out_length;
|
| if (compress) {
|
| compressed_size = TryDeflate(q, out_length);
|
| @@ -912,23 +1117,25 @@ size_t OutputZipFile::WriteFileSizeInLocalFileHeader(u1 *header_ptr,
|
| } else {
|
| put_u2le(header_ptr, COMPRESSION_METHOD_STORED);
|
| }
|
| - header_ptr += 8;
|
| + header_ptr += 4;
|
| + put_u4le(header_ptr, crc); // crc32
|
| put_u4le(header_ptr, compressed_size); // compressed_size
|
| put_u4le(header_ptr, out_length); // uncompressed_size
|
| return compressed_size;
|
| }
|
|
|
| int OutputZipFile::Finish() {
|
| - if (fd_out > 0) {
|
| - WriteCentralDirectory();
|
| - if (ftruncate(fd_out, GetSize()) < 0) {
|
| - return error("ftruncate(fd_out, GetSize()): %s", strerror(errno));
|
| - }
|
| - if (close(fd_out) < 0) {
|
| - return error("close(fd_out): %s", strerror(errno));
|
| - }
|
| - fd_out = -1;
|
| + if (finished_) {
|
| + return 0;
|
| + }
|
| +
|
| + finished_ = true;
|
| + WriteCentralDirectory();
|
| + if (output_file_->Close(GetSize()) < 0) {
|
| + return error("%s", output_file_->Error());
|
| }
|
| + delete output_file_;
|
| + output_file_ = NULL;
|
| return 0;
|
| }
|
|
|
| @@ -937,9 +1144,15 @@ u1* OutputZipFile::NewFile(const char* filename, const u4 attr) {
|
| return q;
|
| }
|
|
|
| -int OutputZipFile::FinishFile(size_t filelength, bool compress) {
|
| +int OutputZipFile::FinishFile(size_t filelength, bool compress,
|
| + bool compute_crc) {
|
| + u4 crc = 0;
|
| + if (compute_crc) {
|
| + crc = crc32(crc, q, filelength);
|
| + }
|
| size_t compressed_size =
|
| - WriteFileSizeInLocalFileHeader(header_ptr, filelength, compress);
|
| + WriteFileSizeInLocalFileHeader(header_ptr, filelength, compress, crc);
|
| + entries_.back()->crc32 = crc;
|
| entries_.back()->compressed_length = compressed_size;
|
| entries_.back()->uncompressed_length = filelength;
|
| if (compressed_size < filelength) {
|
| @@ -951,39 +1164,39 @@ int OutputZipFile::FinishFile(size_t filelength, bool compress) {
|
| return 0;
|
| }
|
|
|
| -ZipBuilder* ZipBuilder::Create(const char* zip_file, u8 estimated_size) {
|
| - if (estimated_size > kMaximumOutputSize) {
|
| +bool OutputZipFile::Open() {
|
| + if (estimated_size_ > kMaximumOutputSize) {
|
| fprintf(stderr,
|
| "Uncompressed input jar has size %llu, "
|
| "which exceeds the maximum supported output size %llu.\n"
|
| "Assuming that ijar will be smaller and hoping for the best.\n",
|
| - estimated_size, kMaximumOutputSize);
|
| - estimated_size = kMaximumOutputSize;
|
| - }
|
| -
|
| - int fd_out = open(zip_file, O_CREAT|O_RDWR|O_TRUNC, 0644);
|
| - if (fd_out < 0) {
|
| - return NULL;
|
| + estimated_size_, kMaximumOutputSize);
|
| + estimated_size_ = kMaximumOutputSize;
|
| }
|
|
|
| - // Create mmap-able sparse file
|
| - if (ftruncate(fd_out, estimated_size) < 0) {
|
| - return NULL;
|
| + MappedOutputFile* output_file = new MappedOutputFile(
|
| + filename_, estimated_size_);
|
| + if (!output_file->Opened()) {
|
| + snprintf(errmsg, sizeof(errmsg), "%s", output_file->Error());
|
| + delete output_file;
|
| + return false;
|
| }
|
|
|
| - // Ensure that any buffer overflow in JarStripper will result in
|
| - // SIGSEGV or SIGBUS by over-allocating beyond the end of the file.
|
| - size_t mmap_length = std::min(estimated_size + sysconf(_SC_PAGESIZE),
|
| - (u8) std::numeric_limits<size_t>::max());
|
| + output_file_ = output_file;
|
| + q = output_file->Buffer();
|
| + zipdata_out_ = output_file->Buffer();
|
| + return true;
|
| +}
|
|
|
| - void *zipdata_out = mmap(NULL, mmap_length, PROT_WRITE,
|
| - MAP_SHARED, fd_out, 0);
|
| - if (zipdata_out == MAP_FAILED) {
|
| - fprintf(stderr, "output_length=%llu\n", estimated_size);
|
| +ZipBuilder* ZipBuilder::Create(const char* zip_file, u8 estimated_size) {
|
| + OutputZipFile* result = new OutputZipFile(zip_file, estimated_size);
|
| + if (!result->Open()) {
|
| + fprintf(stderr, "%s\n", result->GetError());
|
| + delete result;
|
| return NULL;
|
| }
|
|
|
| - return new OutputZipFile(fd_out, (u1*) zipdata_out);
|
| + return result;
|
| }
|
|
|
| u8 ZipBuilder::EstimateSize(char **files) {
|
|
|