aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/tools/singlejar/combiners.cc7
-rw-r--r--src/tools/singlejar/input_jar.cc18
-rw-r--r--src/tools/singlejar/input_jar.h2
-rw-r--r--src/tools/singlejar/output_huge_jar_test.cc77
-rw-r--r--src/tools/singlejar/output_jar.cc185
-rw-r--r--src/tools/singlejar/output_jar.h6
-rw-r--r--src/tools/singlejar/zip_headers.h100
7 files changed, 299 insertions, 96 deletions
diff --git a/src/tools/singlejar/combiners.cc b/src/tools/singlejar/combiners.cc
index b0a712a2a7..ca2e224c7e 100644
--- a/src/tools/singlejar/combiners.cc
+++ b/src/tools/singlejar/combiners.cc
@@ -53,7 +53,7 @@ void *Concatenator::OutputEntry(bool compress) {
// and compressed size values.
uint8_t
zip64_extension_buffer[sizeof(Zip64ExtraField) + 2 * sizeof(uint64_t)];
- bool huge_buffer = (buffer_->data_size() >= 0xFFFFFFFF);
+ bool huge_buffer = ziph::zfield_needs_ext64(buffer_->data_size());
if (huge_buffer) {
deflated_buffer_size += sizeof(zip64_extension_buffer);
}
@@ -97,8 +97,9 @@ void *Concatenator::OutputEntry(bool compress) {
lh->crc32(checksum);
lh->compression_method(method);
if (huge_buffer) {
- lh->compressed_file_size32(compressed_size < 0xFFFFFFFF ? compressed_size
- : 0xFFFFFFFF);
+ lh->compressed_file_size32(ziph::zfield_needs_ext64(compressed_size)
+ ? 0xFFFFFFFF
+ : compressed_size);
// Not sure if this has to be written in the small case, but it shouldn't
// hurt.
const_cast<Zip64ExtraField *>(lh->zip64_extra_field())
diff --git a/src/tools/singlejar/input_jar.cc b/src/tools/singlejar/input_jar.cc
index 348733689d..4a4a92c3b3 100644
--- a/src/tools/singlejar/input_jar.cc
+++ b/src/tools/singlejar/input_jar.cc
@@ -75,7 +75,7 @@ bool InputJar::Open(const std::string &path) {
// First, sanity checks.
uint64_t cen_position = ecd->cen_offset32();
- if (cen_position != 0xFFFFFFFF) {
+ if (!ziph::zfield_has_ext64(cen_position)) {
if (!mapped_file_.mapped(mapped_file_.address(cen_position))) {
diag_warnx("%s:%d: %s is corrupt: Central Directory location 0x%" PRIx64
" is invalid",
@@ -93,7 +93,7 @@ bool InputJar::Open(const std::string &path) {
}
}
uint64_t cen_size = ecd->cen_size32();
- if (cen_size != 0xFFFFFFFF) {
+ if (!ziph::zfield_has_ext64(cen_size)) {
if (cen_size > mapped_file_.offset(ecd)) {
diag_warnx("%s:%d: %s is corrupt: Central Directory size 0x%" PRIx64
" is too large",
@@ -108,10 +108,10 @@ bool InputJar::Open(const std::string &path) {
preamble_size_ = mapped_file_.offset(cdh_) - cen_position;
} else {
auto ecd64loc = reinterpret_cast<const ECD64Locator *>(
- byte_ptr(ecd) - sizeof(ECD64Locator));
+ ziph::byte_ptr(ecd) - sizeof(ECD64Locator));
if (ecd64loc->is()) {
- auto ecd64 =
- reinterpret_cast<const ECD64 *>(byte_ptr(ecd64loc) - sizeof(ECD64));
+ auto ecd64 = reinterpret_cast<const ECD64 *>(ziph::byte_ptr(ecd64loc) -
+ sizeof(ECD64));
if (!ecd64->is()) {
diag_warnx(
"%s:%d: %s is corrupt, expected ECD64 record at offset 0x%" PRIx64
@@ -120,18 +120,20 @@ bool InputJar::Open(const std::string &path) {
mapped_file_.Close();
return false;
}
- cdh_ = reinterpret_cast<const CDH *>(byte_ptr(ecd64) - ecd64->cen_size());
+ cdh_ = reinterpret_cast<const CDH *>(ziph::byte_ptr(ecd64) -
+ ecd64->cen_size());
preamble_size_ = mapped_file_.offset(cdh_) - ecd64->cen_offset();
// Find CEN and preamble size.
} else {
- if (cen_size == 0xFFFFFFFF || cen_position == 0xFFFFFFFF) {
+ if (ziph::zfield_has_ext64(cen_size) ||
+ ziph::zfield_has_ext64(cen_position)) {
diag_warnx(
"%s:%d: %s is corrupt, expected ECD64 locator record at "
"offset 0x%" PRIx64 " is missing",
__FILE__, __LINE__, path.c_str(), mapped_file_.offset(ecd64loc));
return false;
}
- cdh_ = reinterpret_cast<const CDH *>(byte_ptr(ecd) - cen_size);
+ cdh_ = reinterpret_cast<const CDH *>(ziph::byte_ptr(ecd) - cen_size);
preamble_size_ = mapped_file_.offset(cdh_) - cen_position;
}
if (!cdh_->is()) {
diff --git a/src/tools/singlejar/input_jar.h b/src/tools/singlejar/input_jar.h
index 9c30df2856..35545f65e7 100644
--- a/src/tools/singlejar/input_jar.h
+++ b/src/tools/singlejar/input_jar.h
@@ -55,7 +55,7 @@ class InputJar {
return nullptr;
}
const CDH *current_cdh = cdh_;
- const uint8_t *new_cdr = byte_ptr(cdh_) + cdh_->size();
+ const uint8_t *new_cdr = ziph::byte_ptr(cdh_) + cdh_->size();
if (!mapped_file_.mapped(new_cdr)) {
diag_errx(
1,
diff --git a/src/tools/singlejar/output_huge_jar_test.cc b/src/tools/singlejar/output_huge_jar_test.cc
new file mode 100644
index 0000000000..6eefc7f24d
--- /dev/null
+++ b/src/tools/singlejar/output_huge_jar_test.cc
@@ -0,0 +1,77 @@
+// Copyright 2016 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdlib.h>
+
+#include "src/main/cpp/util/file.h"
+#include "src/main/cpp/util/file_platform.h"
+#include "src/main/cpp/util/port.h"
+#include "src/main/cpp/util/strings.h"
+#include "src/tools/singlejar/input_jar.h"
+#include "src/tools/singlejar/options.h"
+#include "src/tools/singlejar/output_jar.h"
+#include "src/tools/singlejar/test_util.h"
+#include "gtest/gtest.h"
+
+namespace {
+
+using singlejar_test_util::AllocateFile;
+using singlejar_test_util::OutputFilePath;
+using singlejar_test_util::VerifyZip;
+
+using std::string;
+
+#if !defined(DATA_DIR_TOP)
+#define DATA_DIR_TOP
+#endif
+
+class OutputHugeJarTest : public ::testing::Test {
+ protected:
+ void CreateOutput(const string &out_path, const std::vector<string> &args) {
+ const char *option_list[100] = {"--output", out_path.c_str()};
+ int nargs = 2;
+ for (auto &arg : args) {
+ if (arg.empty()) {
+ continue;
+ }
+ option_list[nargs++] = arg.c_str();
+ if (arg.find(' ') == string::npos) {
+ fprintf(stderr, " '%s'", arg.c_str());
+ } else {
+ fprintf(stderr, " %s", arg.c_str());
+ }
+ }
+ fprintf(stderr, "\n");
+ options_.ParseCommandLine(nargs, option_list);
+ ASSERT_EQ(0, output_jar_.Doit(&options_));
+ EXPECT_EQ(0, VerifyZip(out_path));
+ }
+
+ OutputJar output_jar_;
+ Options options_;
+};
+
+TEST_F(OutputHugeJarTest, EntryAbove4G) {
+ // Verifies that an entry above 4G is handled correctly.
+
+ // Have huge launcher, then the first jar entry will be above 4G.
+ string launcher_path = OutputFilePath("launcher");
+ ASSERT_TRUE(AllocateFile(launcher_path, 0x100000010));
+
+ string out_path = OutputFilePath("out.jar");
+ CreateOutput(out_path, {"--java_launcher", launcher_path, "--sources",
+ DATA_DIR_TOP "src/tools/singlejar/libtest1.jar"});
+}
+
+} // namespace
diff --git a/src/tools/singlejar/output_jar.cc b/src/tools/singlejar/output_jar.cc
index 47ab373e2d..26f445fb5e 100644
--- a/src/tools/singlejar/output_jar.cc
+++ b/src/tools/singlejar/output_jar.cc
@@ -399,12 +399,13 @@ bool OutputJar::AddJar(int jar_path_index) {
lh->data() + jar_entry->compressed_file_size());
num_bytes +=
jar_entry->compressed_file_size() +
- ddr->size(0xFFFFFFFF == jar_entry->compressed_file_size32(),
- 0xFFFFFFFF == jar_entry->uncompressed_file_size32());
+ ddr->size(
+ ziph::zfield_has_ext64(jar_entry->compressed_file_size32()),
+ ziph::zfield_has_ext64(jar_entry->uncompressed_file_size32()));
} else {
num_bytes += lh->compressed_file_size();
}
- off_t output_position = Position();
+ off_t local_header_offset = Position();
// When normalize_timestamps is set, entry's timestamp is to be set to
// 01/01/1980 00:00:00 (or to 01/01/1980 00:00:02, if an entry is a .class
@@ -431,9 +432,10 @@ bool OutputJar::AddJar(int jar_path_index) {
: reinterpret_cast<LH *>(lh_buffer);
// Remove Unix timestamp field.
if (lh_field_to_remove != nullptr) {
- auto from_end = byte_ptr(lh) + lh->size();
+ auto from_end = ziph::byte_ptr(lh) + lh->size();
size_t removed_size = lh_field_to_remove->size();
- size_t chunk1_size = byte_ptr(lh_field_to_remove) - byte_ptr(lh);
+ size_t chunk1_size =
+ ziph::byte_ptr(lh_field_to_remove) - ziph::byte_ptr(lh);
size_t chunk2_size = lh->size() - (chunk1_size + removed_size);
memcpy(lh_new, lh, chunk1_size);
if (chunk2_size) {
@@ -466,37 +468,8 @@ bool OutputJar::AddJar(int jar_path_index) {
input_jar_path.c_str());
}
- // Append central directory header for this file to the output central
- // directory we are building.
- TODO(output_position < 0xFFFFFFFF, "Handle Zip64");
-
- CDH *out_cdh;
- auto field_to_remove =
- fix_timestamp ? jar_entry->unix_time_extra_field() : nullptr;
- if (field_to_remove != nullptr) {
- // Remove extra fields.
- auto from_start = byte_ptr(jar_entry);
- auto from_end = from_start + jar_entry->size();
- size_t removed_size = field_to_remove->size();
- size_t chunk1_size = byte_ptr(field_to_remove) - from_start;
- size_t chunk2_size = jar_entry->size() - (chunk1_size + removed_size);
- out_cdh =
- reinterpret_cast<CDH *>(ReserveCdr(jar_entry->size() - removed_size));
- memcpy(out_cdh, jar_entry, chunk1_size);
- if (chunk2_size) {
- memcpy(reinterpret_cast<uint8_t *>(out_cdh) + chunk1_size,
- from_end - chunk2_size, chunk2_size);
- }
- out_cdh->extra_fields(out_cdh->extra_fields(),
- jar_entry->extra_fields_length() - removed_size);
- } else {
- out_cdh = AppendToDirectoryBuffer(jar_entry);
- }
- out_cdh->local_header_offset32(output_position);
- if (fix_timestamp) {
- out_cdh->last_mod_file_time(normalized_time);
- out_cdh->last_mod_file_date(33);
- }
+ AppendToDirectoryBuffer(jar_entry, local_header_offset, normalized_time,
+ fix_timestamp);
++entries_;
}
return input_jar.Close();
@@ -509,7 +482,6 @@ off_t OutputJar::Position() {
// You'd think this could be "return ftell(file_);", but that
// generates a needless call to lseek. So instead we cache our
// current position in the output.
- TODO(outpos_ < 0xFFFFFFFF, "Handle Zip64");
return outpos_;
}
@@ -560,8 +532,15 @@ void OutputJar::WriteEntry(void *buffer) {
diag_err(1, "%s:%d: write", __FILE__, __LINE__);
}
// Data written, allocate CDH space and populate CDH.
- CDH *cdh = reinterpret_cast<CDH *>(ReserveCdh(
- sizeof(CDH) + entry->file_name_length() + entry->extra_fields_length()));
+ // Space needed for the CDH varies depending on whether output position field
+ // fits into 32 bits (we do not handle compressed/uncompressed entry sizes
+ // exceeding 32 bits at the moment).
+ uint16_t zip64_size = ziph::zfield_needs_ext64(output_position)
+ ? Zip64ExtraField::space_needed(1)
+ : 0;
+ CDH *cdh = reinterpret_cast<CDH *>(
+ ReserveCdh(sizeof(CDH) + entry->file_name_length() +
+ entry->extra_fields_length() + zip64_size));
cdh->signature();
// Note: do not set the version to Unix 3.0 spec, otherwise
// unzip will think that 'external_attributes' field contains access mode
@@ -578,11 +557,24 @@ void OutputJar::WriteEntry(void *buffer) {
cdh->uncompressed_file_size32(entry->uncompressed_file_size32());
cdh->file_name(entry->file_name(), entry->file_name_length());
cdh->extra_fields(entry->extra_fields(), entry->extra_fields_length());
+ if (zip64_size > 0) {
+ Zip64ExtraField *zip64_ef = reinterpret_cast<Zip64ExtraField *>(
+ cdh->extra_fields() + cdh->extra_fields_length());
+ zip64_ef->signature();
+ zip64_ef->attr_count(1);
+ zip64_ef->attr64(0, output_position);
+ cdh->local_header_offset32(0xFFFFFFFF);
+ // Field address argument points to the already existing field,
+ // so the call just updates the length.
+ cdh->extra_fields(cdh->extra_fields(),
+ cdh->extra_fields_length() + zip64_size);
+ } else {
+ cdh->local_header_offset32(output_position);
+ }
cdh->comment_length(0);
cdh->start_disk_nr(0);
cdh->internal_attributes(0);
cdh->external_attributes(0);
- cdh->local_header_offset32(output_position);
++entries_;
free(reinterpret_cast<void *>(entry));
}
@@ -613,11 +605,112 @@ void OutputJar::WriteMetaInf() {
WriteEntry(lh);
}
-// Appends a Central Directory Entry to the directory buffer.
-CDH *OutputJar::AppendToDirectoryBuffer(const CDH *cdh) {
- size_t cdh_size = cdh->size();
- return reinterpret_cast<CDH *>(
- memcpy(reinterpret_cast<CDH *>(ReserveCdr(cdh_size)), cdh, cdh_size));
+// Create output Central Directory entry for the input jar entry.
+void OutputJar::AppendToDirectoryBuffer(const CDH *cdh, off_t lh_pos,
+ uint16_t normalized_time,
+ bool fix_timestamp) {
+ // While copying from the input CDH pointed to by 'cdh', we may need to drop
+ // Unix timestamp extra field, and we might need to change the number of
+ // attributes of the Zip64 extra field, or create it, or destroy it if entry's
+ // position relative to 4G boundary changes.
+ // The rest of the input CDH is copied.
+
+ // 1. Decide if we need to drop UnixTime.
+ size_t removed_unix_time_field_size = 0;
+ if (fix_timestamp) {
+ auto unix_time_field = cdh->unix_time_extra_field();
+ if (unix_time_field != nullptr) {
+ removed_unix_time_field_size = unix_time_field->size();
+ }
+ }
+
+ // 2. Figure out how many attributes input entry has and how many
+ // the output entry is going to have.
+ const Zip64ExtraField *zip64_ef = cdh->zip64_extra_field();
+ const int zip64_attr_count = zip64_ef == nullptr ? 0 : zip64_ef->attr_count();
+ const bool lh_pos_needs64 = ziph::zfield_needs_ext64(lh_pos);
+ size_t out_zip64_attr_count;
+ if (zip64_attr_count > 0) {
+ out_zip64_attr_count = zip64_attr_count;
+ // The number of attributes may remain the same, or it may increase or
+ // decrease by 1, depending on local_header_offset value.
+ if (ziph::zfield_has_ext64(cdh->local_header_offset32()) !=
+ lh_pos_needs64) {
+ if (lh_pos_needs64) {
+ out_zip64_attr_count += 1;
+ } else {
+ out_zip64_attr_count -= 1;
+ }
+ }
+ } else {
+ out_zip64_attr_count = lh_pos_needs64 ? 1 : 0;
+ }
+ const uint16_t zip64_size = Zip64ExtraField::space_needed(zip64_attr_count);
+ const uint16_t out_zip64_size =
+ Zip64ExtraField::space_needed(out_zip64_attr_count);
+
+ // Allocate output CDH and copy everything but extra fields.
+ const uint16_t ef_size = cdh->extra_fields_length();
+ const uint16_t out_ef_size =
+ (ef_size + out_zip64_size) - (removed_unix_time_field_size + zip64_size);
+
+ const size_t out_cdh_size = cdh->size() + out_ef_size - ef_size;
+ CDH *out_cdh = reinterpret_cast<CDH *>(ReserveCdr(out_cdh_size));
+
+ // Calculate ExtraFields boundaries in the input and output entries.
+ auto ef_begin = reinterpret_cast<const ExtraField *>(cdh->extra_fields());
+ auto ef_end =
+ reinterpret_cast<const ExtraField *>(ziph::byte_ptr(ef_begin) + ef_size);
+ // Copy [cdh..ef_begin) -> [out_cdh..out_ef_begin)
+ memcpy(out_cdh, cdh, ziph::byte_ptr(ef_begin) - ziph::byte_ptr(cdh));
+
+ auto out_ef_begin = reinterpret_cast<ExtraField *>(
+ const_cast<uint8_t *>(out_cdh->extra_fields()));
+ auto out_ef_end = reinterpret_cast<ExtraField *>(
+ reinterpret_cast<uint8_t *>(out_ef_begin) + out_ef_size);
+
+ // Copy [ef_end..cdh_end) -> [out_ef_end..out_cdh_end)
+ memcpy(out_ef_end, ef_end,
+ ziph::byte_ptr(cdh) + cdh->size() - ziph::byte_ptr(ef_end));
+
+ // Copy extra fields, dropping Zip64 and possibly UnixTime fields.
+ ExtraField *out_ef = out_ef_begin;
+ for (const ExtraField *ef = ef_begin; ef < ef_end; ef = ef->next()) {
+ if ((fix_timestamp && ef->is_unix_time()) || ef->is_zip64()) {
+ // Skip this one.
+ } else {
+ memcpy(out_ef, ef, ef->size());
+ out_ef = reinterpret_cast<ExtraField *>(
+ reinterpret_cast<uint8_t *>(out_ef) + ef->size());
+ }
+ }
+
+ // Set up Zip64 extra field if necessary.
+ if (out_zip64_size > 0) {
+ Zip64ExtraField *out_zip64_ef = reinterpret_cast<Zip64ExtraField *>(out_ef);
+ out_zip64_ef->signature();
+ out_zip64_ef->attr_count(out_zip64_attr_count);
+ int copy_count = out_zip64_attr_count < zip64_attr_count
+ ? out_zip64_attr_count
+ : zip64_attr_count;
+ if (copy_count > 0) {
+ out_zip64_ef->attr64(0, zip64_ef->attr64(0));
+ if (copy_count > 1) {
+ out_zip64_ef->attr64(1, zip64_ef->attr64(1));
+ }
+ }
+ // Set 64-bit local_header_offset if necessary. It's always the last
+ // attribute.
+ if (lh_pos_needs64) {
+ out_zip64_ef->attr64(out_zip64_attr_count - 1, lh_pos);
+ }
+ }
+ out_cdh->extra_fields(ziph::byte_ptr(out_ef_begin), out_ef_size);
+ out_cdh->local_header_offset32(lh_pos_needs64 ? 0xFFFFFFFF : lh_pos);
+ if (fix_timestamp) {
+ out_cdh->last_mod_file_time(normalized_time);
+ out_cdh->last_mod_file_date(33);
+ }
}
uint8_t *OutputJar::ReserveCdr(size_t chunk_size) {
@@ -658,8 +751,6 @@ bool OutputJar::Close() {
bool write_zip64_ecd = output_position >= 0xFFFFFFFF || entries_ >= 0xFFFF ||
cen_size_ >= 0xFFFFFFFF;
- TODO(output_position < 0xFFFFFFFF, "Handle Zip64");
-
size_t cen_size = cen_size_; // Save it before ReserveCdh updates it.
if (write_zip64_ecd) {
ECD64 *ecd64 = reinterpret_cast<ECD64 *>(ReserveCdh(sizeof(ECD64)));
diff --git a/src/tools/singlejar/output_jar.h b/src/tools/singlejar/output_jar.h
index be3bef8aad..f5ee2007db 100644
--- a/src/tools/singlejar/output_jar.h
+++ b/src/tools/singlejar/output_jar.h
@@ -73,8 +73,10 @@ class OutputJar {
void WriteEntry(void *local_header_and_payload);
// Write META_INF/ entry (the first entry on output).
void WriteMetaInf();
- // Append given Central Directory Header to CEN (Central Directory) buffer.
- CDH *AppendToDirectoryBuffer(const CDH *cdh);
+ // Create output Central Directory Header for the given input entry and
+ // append it to CEN (Central Directory) buffer.
+ void AppendToDirectoryBuffer(const CDH *cdh, off_t local_header_offset,
+ uint16_t normalized_time, bool fix_timestamp);
// Reserve space in CEN buffer.
uint8_t *ReserveCdr(size_t chunk_size);
// Reserve space for the Central Directory Header in CEN buffer.
diff --git a/src/tools/singlejar/zip_headers.h b/src/tools/singlejar/zip_headers.h
index 8947fc6adb..7d8c3018e7 100644
--- a/src/tools/singlejar/zip_headers.h
+++ b/src/tools/singlejar/zip_headers.h
@@ -41,9 +41,23 @@
#include <string>
#include <type_traits>
-static const uint8_t *byte_ptr(const void *ptr) {
- return reinterpret_cast<const uint8_t *>(ptr);
-}
+class ziph {
+ public:
+ static const uint8_t *byte_ptr(const void *ptr) {
+ return reinterpret_cast<const uint8_t *>(ptr);
+ }
+
+ /* Utility functions to handle Zip64 extensions. Size and position fields in
+ * the Zip headers are 32-bit wide. If field's value does not fit into 32
+ * bits (more precisely, it is >= 0xFFFFFFFF), the field contains 0xFFFFFFFF
+ * and the actual value is saved in the corresponding 64-bit extension field.
+ * The first function returns true if there is an extension for the given
+ * field value, and the second returns true if given field value needs
+ * extension.
+ */
+ static bool zfield_has_ext64(uint32_t v) { return v == 0xFFFFFFFF; }
+ static bool zfield_needs_ext64(uint64_t v) { return v >= 0xFFFFFFFF; }
+};
/* Overall .ZIP file format (section 4.3.6), and the corresponding classes
* [local file header 1] class LH
@@ -78,11 +92,13 @@ class ExtraField {
if (extra_field->is(tag)) {
return extra_field;
}
- start = byte_ptr(start) + extra_field->size();
+ start = ziph::byte_ptr(start) + extra_field->size();
}
return nullptr;
}
bool is(uint16_t tag) const { return htole16(tag_) == tag; }
+ bool is_zip64() const { return is(1); }
+ bool is_unix_time() const { return is(0x5455); }
void signature(uint16_t tag) { tag_ = le16toh(tag); }
uint16_t payload_size() const { return le16toh(payload_size_); }
@@ -90,6 +106,10 @@ class ExtraField {
uint16_t size() const { return sizeof(ExtraField) + payload_size(); }
+ const ExtraField *next() const {
+ return reinterpret_cast<const ExtraField *>(ziph::byte_ptr(this) + size());
+ }
+
protected:
uint16_t tag_;
uint16_t payload_size_;
@@ -115,13 +135,23 @@ class Zip64ExtraField : public ExtraField {
ExtraField::find(1, start, end));
}
- bool is() const { return ExtraField::is(1); }
+ bool is() const { return is_zip64(); }
void signature() { ExtraField::signature(1); }
// The value of i-th attribute
uint64_t attr64(int index) const { return le64toh(attr_[index]); }
void attr64(int index, uint64_t v) { attr_[index] = htole64(v); }
+ // Attribute count
+ int attr_count() const { return payload_size() / sizeof(attr_[0]); }
+ void attr_count(int n) { payload_size(n * sizeof(attr_[0])); }
+
+ // Space needed for this field to accomodate n_attr attributes
+ static uint16_t space_needed(int n_attrs) {
+ return n_attrs > 0 ? sizeof(Zip64ExtraField) + n_attrs * sizeof(uint64_t)
+ : 0;
+ }
+
private:
uint64_t attr_[];
} __attribute__((packed));
@@ -143,7 +173,7 @@ class UnixTimeExtraField : public ExtraField {
return reinterpret_cast<const UnixTimeExtraField *>(
ExtraField::find(0x5455, start, end));
}
- bool is() const { return ExtraField::is(0x5455); }
+ bool is() const { return is_unix_time(); }
void signature() { ExtraField::signature(0x5455); }
void flags(uint8_t v) { flags_ = v; }
@@ -191,11 +221,11 @@ class LH {
size_t compressed_file_size() const {
size_t size32 = compressed_file_size32();
- if (size32 != 0xFFFFFFFF) {
- return size32;
+ if (ziph::zfield_has_ext64(size32)) {
+ const Zip64ExtraField *z64 = zip64_extra_field();
+ return z64 == nullptr ? 0xFFFFFFFF : z64->attr64(1);
}
- const Zip64ExtraField *z64 = zip64_extra_field();
- return z64 == nullptr ? 0xFFFFFFFF : z64->attr64(1);
+ return size32;
}
size_t compressed_file_size32() const {
return le32toh(compressed_file_size32_);
@@ -206,11 +236,11 @@ class LH {
size_t uncompressed_file_size() const {
size_t size32 = uncompressed_file_size32();
- if (size32 != 0xFFFFFFFF) {
- return size32;
+ if (ziph::zfield_has_ext64(size32)) {
+ const Zip64ExtraField *z64 = zip64_extra_field();
+ return z64 == nullptr ? 0xFFFFFFFF : z64->attr64(0);
}
- const Zip64ExtraField *z64 = zip64_extra_field();
- return z64 == nullptr ? 0xFFFFFFFF : z64->attr64(0);
+ return size32;
}
size_t uncompressed_file_size32() const {
return le32toh(uncompressed_file_size32_);
@@ -238,7 +268,7 @@ class LH {
uint16_t extra_fields_length() const { return le16toh(extra_fields_length_); }
const uint8_t *extra_fields() const {
- return byte_ptr(file_name_ + file_name_length());
+ return ziph::byte_ptr(file_name_ + file_name_length());
}
uint8_t *extra_fields() {
return reinterpret_cast<uint8_t *>(file_name_) + file_name_length();
@@ -357,13 +387,13 @@ class CDH {
size_t compressed_file_size() const {
size_t size32 = compressed_file_size32();
- if (size32 != 0xFFFFFFFF) {
- return size32;
+ if (ziph::zfield_has_ext64(size32)) {
+ const Zip64ExtraField *z64 = zip64_extra_field();
+ return z64 == nullptr ? 0xFFFFFFFF
+ : z64->attr64(ziph::zfield_has_ext64(
+ uncompressed_file_size32()));
}
- const Zip64ExtraField *z64 = zip64_extra_field();
- return z64 == nullptr
- ? 0xFFFFFFFF
- : z64->attr64(uncompressed_file_size32() == 0xFFFFFFFF ? 1 : 0);
+ return size32;
}
size_t compressed_file_size32() const {
return le32toh(compressed_file_size32_);
@@ -374,11 +404,11 @@ class CDH {
size_t uncompressed_file_size() const {
uint32_t size32 = uncompressed_file_size32();
- if (size32 != 0xFFFFFFFF) {
- return size32;
+ if (ziph::zfield_has_ext64(size32)) {
+ const Zip64ExtraField *z64 = zip64_extra_field();
+ return z64 == nullptr ? 0xFFFFFFFF : z64->attr64(0);
}
- const Zip64ExtraField *z64 = zip64_extra_field();
- return z64 == nullptr ? 0xFFFFFFFF : z64->attr64(0);
+ return size32;
}
size_t uncompressed_file_size32() const {
return le32toh(uncompressed_file_size32_);
@@ -407,7 +437,7 @@ class CDH {
uint16_t extra_fields_length() const { return le16toh(extra_fields_length_); }
const uint8_t *extra_fields() const {
- return byte_ptr(file_name_ + file_name_length());
+ return ziph::byte_ptr(file_name_ + file_name_length());
}
uint8_t *extra_fields() {
return reinterpret_cast<uint8_t *>(file_name_) + file_name_length();
@@ -433,15 +463,15 @@ class CDH {
uint64_t local_header_offset() const {
uint32_t size32 = local_header_offset32();
- if (size32 != 0xFFFFFFFF) {
- return size32;
- }
- const Zip64ExtraField *z64 = zip64_extra_field();
- int attr_no = uncompressed_file_size32() == 0xFFFFFFFF ? 1 : 0;
- if (compressed_file_size32() == 0xFFFFFFFF) {
- ++attr_no;
+ if (ziph::zfield_has_ext64(size32)) {
+ const Zip64ExtraField *z64 = zip64_extra_field();
+ int attr_no = ziph::zfield_has_ext64(uncompressed_file_size32());
+ if (ziph::zfield_has_ext64(compressed_file_size32())) {
+ ++attr_no;
+ }
+ return z64 == nullptr ? 0xFFFFFFFF : z64->attr64(attr_no);
}
- return z64 == nullptr ? 0xFFFFFFFF : z64->attr64(attr_no);
+ return size32;
}
uint32_t local_header_offset32() const {
@@ -548,7 +578,7 @@ class ECD {
uint64_t ecd64_offset() const {
const ECD64Locator *locator = reinterpret_cast<const ECD64Locator *>(
- byte_ptr(this) - sizeof(ECD64Locator));
+ ziph::byte_ptr(this) - sizeof(ECD64Locator));
return locator->is() ? locator->ecd64_offset() : 0xFFFFFFFFFFFFFFFF;
}