From 6673b31f4fb63339e169879e1c37f748020c3abe Mon Sep 17 00:00:00 2001 From: Sasha Smundak Date: Fri, 23 Sep 2016 19:12:31 +0000 Subject: Add '0xCAFE' extra field to the first entry of the output jar (which is META-INF/). Unix 'file' utility uses it to distinguish jar file from zip file. -- MOS_MIGRATED_REVID=134102453 --- src/tools/singlejar/output_jar.cc | 23 ++++++++++++++++------- src/tools/singlejar/output_jar.h | 4 ++-- src/tools/singlejar/output_jar_simple_test.cc | 16 ++++++++++++++++ src/tools/singlejar/zip_headers.h | 2 +- 4 files changed, 35 insertions(+), 10 deletions(-) (limited to 'src/tools/singlejar') diff --git a/src/tools/singlejar/output_jar.cc b/src/tools/singlejar/output_jar.cc index 855afe6d2c..d5e2c10969 100644 --- a/src/tools/singlejar/output_jar.cc +++ b/src/tools/singlejar/output_jar.cc @@ -191,7 +191,7 @@ int OutputJar::Doit(Options *options) { bool compress = options_->force_compression || options_->preserve_compression; // First, write a directory entry for the META-INF, followed by the manifest // file, followed by the build properties file. - AddDirectory("META-INF/"); + WriteMetaInf(); manifest_.Append("\r\n"); WriteEntry(manifest_.OutputEntry(compress)); if (!options_->exclude_build_data) { @@ -538,8 +538,8 @@ 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( - ReserveCdh(sizeof(CDH) + entry->file_name_length())); + CDH *cdh = reinterpret_cast(ReserveCdh( + sizeof(CDH) + entry->file_name_length() + entry->extra_fields_length())); cdh->signature(); // Note: do not set the version to Unix 3.0 spec, otherwise // unzip will think that 'external_attributes' field contains access mode @@ -555,7 +555,7 @@ void OutputJar::WriteEntry(void *buffer) { TODO(entry->uncompressed_file_size32() != 0xFFFFFFFF, "Handle Zip64"); cdh->uncompressed_file_size32(entry->uncompressed_file_size32()); cdh->file_name(entry->file_name(), entry->file_name_length()); - cdh->extra_fields(nullptr, 0); + cdh->extra_fields(entry->extra_fields(), entry->extra_fields_length()); cdh->comment_length(0); cdh->start_disk_nr(0); cdh->internal_attributes(0); @@ -565,9 +565,18 @@ void OutputJar::WriteEntry(void *buffer) { free(reinterpret_cast(entry)); } -void OutputJar::AddDirectory(const char *path) { +void OutputJar::WriteMetaInf() { + const char path[] = "META-INF/"; size_t n_path = strlen(path); - size_t lh_size = sizeof(LH) + n_path; + + // META_INF/ is always the first entry, and as such it should have an extra + // field with the tag 0xCAFE and zero bytes of data. This is not the part of + // the jar file spec, but Unix 'file' utility relies on it to distiguish jar + // file from zip file. See https://bugs.openjdk.java.net/browse/JDK-6808540 + const uint8_t extra_fields[] = {0xFE, 0xCA, 0, 0}; + const uint16_t n_extra_fields = + sizeof(extra_fields) / sizeof(extra_fields[0]); + size_t lh_size = sizeof(LH) + n_path + n_extra_fields; LH *lh = reinterpret_cast(malloc(lh_size)); lh->signature(); lh->version(20); // 2.0 @@ -577,7 +586,7 @@ void OutputJar::AddDirectory(const char *path) { lh->compressed_file_size32(0); lh->uncompressed_file_size32(0); lh->file_name(path, n_path); - lh->extra_fields(nullptr, 0); + lh->extra_fields(extra_fields, n_extra_fields); known_members_.emplace(path, EntryInfo{&null_combiner_}); WriteEntry(lh); } diff --git a/src/tools/singlejar/output_jar.h b/src/tools/singlejar/output_jar.h index 082bf7a33b..be3bef8aad 100644 --- a/src/tools/singlejar/output_jar.h +++ b/src/tools/singlejar/output_jar.h @@ -71,8 +71,8 @@ class OutputJar { off_t Position(); // Write Jar entry. void WriteEntry(void *local_header_and_payload); - // Write a directory entry. - void AddDirectory(const char *path); + // 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); // Reserve space in CEN buffer. diff --git a/src/tools/singlejar/output_jar_simple_test.cc b/src/tools/singlejar/output_jar_simple_test.cc index de9751ba15..6bad8e3fc4 100644 --- a/src/tools/singlejar/output_jar_simple_test.cc +++ b/src/tools/singlejar/output_jar_simple_test.cc @@ -115,9 +115,12 @@ TEST_F(OutputJarSimpleTest, Empty) { CreateOutput(out_path, {}); InputJar input_jar; ASSERT_TRUE(input_jar.Open(out_path)); + int entry_count = 0; const LH *lh; const CDH *cdh; + const uint8_t cafe_extra_field[] = {0xFE, 0xCA, 0, 0}; while ((cdh = input_jar.NextEntry(&lh))) { + ++entry_count; ASSERT_TRUE(cdh->is()) << "No expected tag in the Central Directory Entry."; ASSERT_NE(nullptr, lh) << "No local header."; ASSERT_TRUE(lh->is()) << "No expected tag in the Local Header."; @@ -163,6 +166,19 @@ TEST_F(OutputJarSimpleTest, Empty) { EXPECT_GE(now, entry_time) << now_time_str << " vs. " << entry_time_str; EXPECT_LE(now, entry_time + 300) << now_time_str << " vs. " << entry_time_str; + + // The first entry should be for the META-INF/ directory, and it should + // contain a single extra field 0xCAFE. Although + // https://bugs.openjdk.java.net/browse/JDK-6808540 claims that this extra + // field is optional, 'file' utility in Linux relies on to distinguish + // jar from zip. + if (entry_count == 1) { + ASSERT_EQ("META-INF/", lh->file_name_string()); + ASSERT_EQ(4, lh->extra_fields_length()); + ASSERT_EQ(0, memcmp(cafe_extra_field, lh->extra_fields(), 4)); + ASSERT_EQ(4, cdh->extra_fields_length()); + ASSERT_EQ(0, memcmp(cafe_extra_field, cdh->extra_fields(), 4)); + } } input_jar.Close(); string manifest = GetEntryContents(out_path, "META-INF/MANIFEST.MF"); diff --git a/src/tools/singlejar/zip_headers.h b/src/tools/singlejar/zip_headers.h index 02f8167973..226c7cb800 100644 --- a/src/tools/singlejar/zip_headers.h +++ b/src/tools/singlejar/zip_headers.h @@ -243,7 +243,7 @@ class LH { uint8_t *extra_fields() { return reinterpret_cast(file_name_) + file_name_length(); } - void extra_fields(uint8_t *data, uint16_t data_length) { + void extra_fields(const uint8_t *data, uint16_t data_length) { extra_fields_length_ = htole16(data_length); if (data_length) { memcpy(extra_fields(), data, data_length); -- cgit v1.2.3