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) { |