Include manifest size when presizing ijar output PiperOrigin-RevId: 190010996
diff --git a/third_party/ijar/ijar.cc b/third_party/ijar/ijar.cc index d7742ee..2ed559d 100644 --- a/third_party/ijar/ijar.cc +++ b/third_party/ijar/ijar.cc
@@ -36,13 +36,20 @@ const char* CLASS_EXTENSION = ".class"; const size_t CLASS_EXTENSION_LENGTH = strlen(CLASS_EXTENSION); +const char *MANIFEST_DIR_PATH = "META-INF/"; +const size_t MANIFEST_DIR_PATH_LENGTH = strlen(MANIFEST_DIR_PATH); +const char *MANIFEST_PATH = "META-INF/MANIFEST.MF"; +const size_t MANIFEST_PATH_LENGTH = strlen(MANIFEST_PATH); const char *MANIFEST_HEADER = - "Manifest-Version: 1.0\n" - "Created-By: bazel\n"; + "Manifest-Version: 1.0\r\n" + "Created-By: bazel\r\n"; +const size_t MANIFEST_HEADER_LENGTH = strlen(MANIFEST_HEADER); // These attributes are used by JavaBuilder, Turbine, and ijar. // They must all be kept in sync. const char *TARGET_LABEL_KEY = "Target-Label: "; +const size_t TARGET_LABEL_KEY_LENGTH = strlen(TARGET_LABEL_KEY); const char *INJECTING_RULE_KIND_KEY = "Injecting-Rule-Kind: "; +const size_t INJECTING_RULE_KIND_KEY_LENGTH = strlen(INJECTING_RULE_KIND_KEY); // ZipExtractorProcessor that select only .class file and use // StripClass to generate an interface class, storing as a new file @@ -120,21 +127,48 @@ return len; } +// Computes the size of zip file content for the manifest created by +// WriteManifest, including zip file format overhead. +static size_t EstimateManifestOutputSize(const char *target_label, + const char *injecting_rule_kind) { + if (target_label == NULL) { + return 0; + } + // local headers + size_t length = 30 * 2 + MANIFEST_DIR_PATH_LENGTH + MANIFEST_PATH_LENGTH; + // central directory + length += 46 * 2 + MANIFEST_DIR_PATH_LENGTH + MANIFEST_PATH_LENGTH; + // zip64 EOCD entries + length += 56 * 2; + + // manifest content + length += MANIFEST_HEADER_LENGTH; + // target label manifest entry, including newline + length += TARGET_LABEL_KEY_LENGTH + strlen(target_label) + 2; + if (injecting_rule_kind) { + // injecting rule kind manifest entry, including newline + length += INJECTING_RULE_KIND_KEY_LENGTH + strlen(injecting_rule_kind) + 2; + } + return length; +} + static void WriteManifest(ZipBuilder *out, const char *target_label, const char *injecting_rule_kind) { if (target_label == NULL) { return; } - out->WriteEmptyFile("META-INF/"); - u1 *start = out->NewFile("META-INF/MANIFEST.MF", 0); + out->WriteEmptyFile(MANIFEST_DIR_PATH); + u1 *start = out->NewFile(MANIFEST_PATH, 0); u1 *buf = start; buf += WriteStr(buf, MANIFEST_HEADER); buf += WriteStr(buf, TARGET_LABEL_KEY); buf += WriteStr(buf, target_label); + *buf++ = '\r'; *buf++ = '\n'; if (injecting_rule_kind) { buf += WriteStr(buf, INJECTING_RULE_KIND_KEY); buf += WriteStr(buf, injecting_rule_kind); + *buf++ = '\r'; *buf++ = '\n'; } size_t total_len = buf - start; @@ -153,7 +187,9 @@ strerror(errno)); abort(); } - u8 output_length = in->CalculateOutputLength(); + u8 output_length = + in->CalculateOutputLength() + + EstimateManifestOutputSize(target_label, injecting_rule_kind); std::unique_ptr<ZipBuilder> out(ZipBuilder::Create(file_out, output_length)); if (out.get() == NULL) { fprintf(stderr, "Unable to open output file %s: %s\n", file_out,
diff --git a/third_party/ijar/mapped_file.h b/third_party/ijar/mapped_file.h index 6db0673..7653638 100644 --- a/third_party/ijar/mapped_file.h +++ b/third_party/ijar/mapped_file.h
@@ -62,6 +62,7 @@ const char* errmsg_; bool opened_; u1* buffer_; + u8 estimated_size_; public: MappedOutputFile(const char* name, u8 estimated_size);
diff --git a/third_party/ijar/mapped_file_unix.cc b/third_party/ijar/mapped_file_unix.cc index d5a0b57..ccb8787 100644 --- a/third_party/ijar/mapped_file_unix.cc +++ b/third_party/ijar/mapped_file_unix.cc
@@ -90,7 +90,8 @@ int mmap_length_; }; -MappedOutputFile::MappedOutputFile(const char* name, u8 estimated_size) { +MappedOutputFile::MappedOutputFile(const char* name, u8 estimated_size) + : estimated_size_(estimated_size) { impl_ = NULL; opened_ = false; int fd = open(name, O_CREAT|O_RDWR|O_TRUNC, 0644); @@ -131,6 +132,12 @@ } int MappedOutputFile::Close(int size) { + if (size > estimated_size_) { + snprintf(errmsg, MAX_ERROR, "size %d > estimated size %lld", size, + estimated_size_); + errmsg_ = errmsg; + return -1; + } munmap(buffer_, impl_->mmap_length_); if (ftruncate(impl_->fd_, size) < 0) { snprintf(errmsg, MAX_ERROR, "ftruncate(): %s", strerror(errno));
diff --git a/third_party/ijar/test/BUILD b/third_party/ijar/test/BUILD index d9ab5f7..62b6edd 100644 --- a/third_party/ijar/test/BUILD +++ b/third_party/ijar/test/BUILD
@@ -109,6 +109,14 @@ tools = ["//third_party/ijar"], ) +genrule( + name = "empty_with_target_label", + srcs = [":empty_zip.jar"], + outs = ["empty_with_target_label.jar"], + cmd = "$(location //third_party/ijar) $< $@ --target_label //empty", + tools = ["//third_party/ijar"], +) + java_library( name = "typeannotations2", srcs = glob(["typeannotations2/**/*.java"]), @@ -187,6 +195,7 @@ "UseDeprecatedParts.java", "UseRestrictedAnnotation.java", "package-info.java", + ":empty_with_target_label", ":interface_ijar_testlib", ":interface_ijar_testlib_with_target_label", ":liblocal_and_anonymous_lib.jar", @@ -234,6 +243,7 @@ ("largest_regular", 65535), ("smallest_zip64", 65536), ("definitely_zip64", 70000), + ("empty_zip", 1), ] ]
diff --git a/third_party/ijar/test/IjarTests.java b/third_party/ijar/test/IjarTests.java index a2e10db..dc6c020 100644 --- a/third_party/ijar/test/IjarTests.java +++ b/third_party/ijar/test/IjarTests.java
@@ -292,4 +292,16 @@ LocalDateTime.of(2010, 1, 1, 0, 0, 0).atZone(ZoneOffset.systemDefault()).toInstant()); } } + + @Test + public void testEmptyWithTargetLabel() throws Exception { + try (JarFile jf = new JarFile("third_party/ijar/test/empty_with_target_label.jar")) { + Manifest manifest = jf.getManifest(); + Attributes attributes = manifest.getMainAttributes(); + assertThat(attributes.getValue("Target-Label")).isEqualTo("//empty"); + assertThat(jf.getEntry(JarFile.MANIFEST_NAME).getLastModifiedTime().toInstant()) + .isEqualTo( + LocalDateTime.of(2010, 1, 1, 0, 0, 0).atZone(ZoneOffset.systemDefault()).toInstant()); + } + } }