Normalize timestamps to DOS epoch+epsilon to avoid local timezone leaking into ZIPs.
References:
* http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8246129
* https://github.com/bazelbuild/bazel/pull/10976
PiperOrigin-RevId: 313848837
diff --git a/src/test/java/com/google/devtools/build/android/ziputils/SplitZipTest.java b/src/test/java/com/google/devtools/build/android/ziputils/SplitZipTest.java
index 360d900..fb27cb1 100644
--- a/src/test/java/com/google/devtools/build/android/ziputils/SplitZipTest.java
+++ b/src/test/java/com/google/devtools/build/android/ziputils/SplitZipTest.java
@@ -129,7 +129,7 @@
SplitZip result = instance.useDefaultEntryDate();
assertThat(result).isSameInstanceAs(instance);
Date date = instance.getEntryDate();
- assertThat(date).isEqualTo(DosTime.DOS_EPOCH);
+ assertThat(date).isEqualTo(DosTime.DOS_EPOCHISH);
}
@Test
@@ -192,15 +192,15 @@
.create("input.zip");
new ZipFileBuilder()
- .add(new ZipFileBuilder.FileInfo("pkg/test.txt", DosTime.EPOCH.time, "hello world"))
+ .add(new ZipFileBuilder.FileInfo("pkg/test.txt", DosTime.EPOCHISH.time, "hello world"))
.create("expect.zip");
byte[] expectBytes = fileSystem.toByteArray("expect.zip");
new SplitZip()
- .addOutput(new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false),
- "out/shard1.jar"))
+ .addOutput(
+ new ZipOut(fileSystem.getOutputChannel("out/shard1.jar", false), "out/shard1.jar"))
.setVerbose(true)
- .setEntryDate(DosTime.DOS_EPOCH)
+ .setEntryDate(DosTime.DOS_EPOCHISH)
.addInput(new ZipIn(fileSystem.getInputChannel("input.zip"), "input.zip"))
.run()
.close();
diff --git a/src/test/java/com/google/devtools/build/android/ziputils/ZipFileBuilder.java b/src/test/java/com/google/devtools/build/android/ziputils/ZipFileBuilder.java
index 441eb71..db704cd 100644
--- a/src/test/java/com/google/devtools/build/android/ziputils/ZipFileBuilder.java
+++ b/src/test/java/com/google/devtools/build/android/ziputils/ZipFileBuilder.java
@@ -100,16 +100,22 @@
static final short DEFLATED = 8;
public FileInfo(String filename, String content) {
- this(filename, DosTime.EPOCH.time, STORED, 0,
- (content == null ? EMPTY : content.getBytes(CHARSET)), null, null);
+ this(
+ filename,
+ DosTime.EPOCHISH.time,
+ STORED,
+ 0,
+ (content == null ? EMPTY : content.getBytes(CHARSET)),
+ null,
+ null);
}
public FileInfo(String filename, byte[] data) {
- this(filename, DosTime.EPOCH.time, STORED, 0, data, null, null);
+ this(filename, DosTime.EPOCHISH.time, STORED, 0, data, null, null);
}
public FileInfo(String filename, byte[] data, int uncompressed) {
- this(filename, DosTime.EPOCH.time, DEFLATED, uncompressed, data, null, null);
+ this(filename, DosTime.EPOCHISH.time, DEFLATED, uncompressed, data, null, null);
}
public FileInfo(String filename, int dosTime, String content) {
diff --git a/src/test/java/com/google/devtools/build/android/ziputils/ZipOutTest.java b/src/test/java/com/google/devtools/build/android/ziputils/ZipOutTest.java
index 33caeb3..c4e8b9b 100644
--- a/src/test/java/com/google/devtools/build/android/ziputils/ZipOutTest.java
+++ b/src/test/java/com/google/devtools/build/android/ziputils/ZipOutTest.java
@@ -15,13 +15,12 @@
import static com.google.devtools.build.android.ziputils.DirectoryEntry.CENTIM;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
/**
* Unit tests for {@link ZipOut}.
@@ -37,11 +36,13 @@
String filename = "out.zip";
ZipOut instance = new ZipOut(fileSystem.getOutputChannel(filename, false), filename);
- instance.nextEntry(DirectoryEntry.allocate("pgk/a.class", null, null))
- .set(CENTIM, DosTime.EPOCH.time);
+ instance
+ .nextEntry(DirectoryEntry.allocate("pgk/a.class", null, null))
+ .set(CENTIM, DosTime.EPOCHISH.time);
- instance.nextEntry(DirectoryEntry.allocate("pgk/b.class", null, null))
- .set(CENTIM, DosTime.EPOCH.time);
+ instance
+ .nextEntry(DirectoryEntry.allocate("pgk/b.class", null, null))
+ .set(CENTIM, DosTime.EPOCHISH.time);
instance.close();
} catch (IOException ex) {
diff --git a/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceLinker.java b/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceLinker.java
index ea80ee4..91209a7 100644
--- a/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceLinker.java
+++ b/src/tools/android/java/com/google/devtools/build/android/aapt2/ResourceLinker.java
@@ -459,8 +459,8 @@
String comment = dirEntry.getComment();
byte[] extra = dirEntry.getExtraData();
zipOut.nextEntry(
- dirEntry.clone(filename, extra, comment).set(CENTIM, DosTime.EPOCH.time));
- zipOut.write(header.clone(filename, extra).set(LOCTIM, DosTime.EPOCH.time));
+ dirEntry.clone(filename, extra, comment).set(CENTIM, DosTime.EPOCHISH.time));
+ zipOut.write(header.clone(filename, extra).set(LOCTIM, DosTime.EPOCHISH.time));
zipOut.write(data);
if ((header.get(LOCFLG) & LocalFileHeader.SIZE_MASKED_FLAG) != 0) {
DataDescriptor desc =
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/io/BUILD b/src/tools/android/java/com/google/devtools/build/android/desugar/io/BUILD
index db24451..e97e1a5 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/io/BUILD
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/BUILD
@@ -12,6 +12,7 @@
],
deps = [
"//src/tools/android/java/com/google/devtools/build/android/desugar/langmodel",
+ "//src/tools/android/java/com/google/devtools/build/android/ziputils:ziputils_lib",
"//third_party:asm",
"//third_party:asm-commons",
"//third_party:asm-tree",
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/io/ZipOutputFileProvider.java b/src/tools/android/java/com/google/devtools/build/android/desugar/io/ZipOutputFileProvider.java
index 45a7749..2d3b1b3 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/io/ZipOutputFileProvider.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/ZipOutputFileProvider.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.android.desugar.io;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.devtools.build.android.ziputils.DosTime.EPOCHISH;
import com.google.common.io.ByteStreams;
import java.io.BufferedOutputStream;
@@ -83,7 +84,7 @@
checksum.update(content);
ZipEntry result = new ZipEntry(filename);
- result.setTime(0L); // Use stable timestamp Jan 1 1980
+ result.setTime(EPOCHISH.time); // Use stable timestamp Jan 1 1980
result.setCrc(checksum.getValue());
result.setSize(content.length);
result.setCompressedSize(content.length);
diff --git a/src/tools/android/java/com/google/devtools/build/android/ziputils/BUILD b/src/tools/android/java/com/google/devtools/build/android/ziputils/BUILD
index f22792e..d47af74 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ziputils/BUILD
+++ b/src/tools/android/java/com/google/devtools/build/android/ziputils/BUILD
@@ -5,7 +5,7 @@
package(
default_visibility = [
"//src/test/java/com/google/devtools/build/android/ziputils:__pkg__",
- "//src/tools/android/java/com/google/devtools/build/android:__pkg__",
+ "//src/tools/android/java/com/google/devtools/build/android:__subpackages__",
],
)
diff --git a/src/tools/android/java/com/google/devtools/build/android/ziputils/DexReducer.java b/src/tools/android/java/com/google/devtools/build/android/ziputils/DexReducer.java
index 91f1a56..e4feead 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ziputils/DexReducer.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ziputils/DexReducer.java
@@ -100,8 +100,8 @@
String filename = BASENAME + (count == 1 ? "" : Integer.toString(count)) + SUFFIX;
String comment = dirEntry.getComment();
byte[] extra = dirEntry.getExtraData();
- out.nextEntry(dirEntry.clone(filename, extra, comment).set(CENTIM, DosTime.EPOCH.time));
- out.write(header.clone(filename, extra).set(LOCTIM, DosTime.EPOCH.time));
+ out.nextEntry(dirEntry.clone(filename, extra, comment).set(CENTIM, DosTime.EPOCHISH.time));
+ out.write(header.clone(filename, extra).set(LOCTIM, DosTime.EPOCHISH.time));
out.write(data);
if ((header.get(LOCFLG) & LocalFileHeader.SIZE_MASKED_FLAG) != 0) {
DataDescriptor desc = DataDescriptor.allocate()
diff --git a/src/tools/android/java/com/google/devtools/build/android/ziputils/DosTime.java b/src/tools/android/java/com/google/devtools/build/android/ziputils/DosTime.java
index e2e9dc6..c10e7a5 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ziputils/DosTime.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ziputils/DosTime.java
@@ -23,10 +23,17 @@
*/
public final class DosTime {
- /** DOS representation of DOS epoch (midnight, jan 1, 1980) */
- public static final DosTime EPOCH;
+ /**
+ * DOS representation of DOS epoch (1980-01-01) + epsilon.
+ *
+ * <p>NB: we avoid exact midnight since
+ * http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8246129 causes the local timezone to be
+ * leaked into the ZIP file.
+ */
+ public static final DosTime EPOCHISH;
/** {@code java.util.Date} for DOS epoch */
- public static final Date DOS_EPOCH;
+ public static final Date DOS_EPOCHISH;
+
private static final Calendar calendar;
/**
@@ -44,9 +51,9 @@
}
static {
- calendar = new GregorianCalendar(1980, 0, 1, 0, 0, 0);
- DOS_EPOCH = calendar.getTime();
- EPOCH = new DosTime(DOS_EPOCH);
+ calendar = new GregorianCalendar(1980, 0, 1, /*hrs=*/ 0, /*min=*/ 1);
+ DOS_EPOCHISH = calendar.getTime();
+ EPOCHISH = new DosTime(DOS_EPOCHISH);
}
private static synchronized int dateToDosTime(Date date) {
diff --git a/src/tools/android/java/com/google/devtools/build/android/ziputils/SplitZip.java b/src/tools/android/java/com/google/devtools/build/android/ziputils/SplitZip.java
index a19cb58..0528175 100644
--- a/src/tools/android/java/com/google/devtools/build/android/ziputils/SplitZip.java
+++ b/src/tools/android/java/com/google/devtools/build/android/ziputils/SplitZip.java
@@ -174,12 +174,13 @@
}
/**
- * Sets date to {@link DosTime#DOS_EPOCH}.
+ * Sets date to {@link DosTime#DOS_EPOCHISH}.
+ *
* @return this object.
*/
public SplitZip useDefaultEntryDate() {
- this.date = DosTime.DOS_EPOCH;
- this.dosTime = DosTime.EPOCH;
+ this.date = DosTime.DOS_EPOCHISH;
+ this.dosTime = DosTime.EPOCHISH;
return this;
}