Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e5671597 authored by Mark Punzalan's avatar Mark Punzalan
Browse files

[aapt2] Ensure all zip entry times are the same

We want them to be all the same, so that APKs are identical if its
contents are the same, regardless of the time zone of the machine that
produced them. The accompanying change in libziparchive
(Iefc7945bff0610f8a025d4887cd5644cfa3cfb3b) ensures this.

Bug: 277978832
Test: atest aapt2_tests
Change-Id: Iab000b7c74dcb06efad41a6495b73216fd2528d6
parent f6ddadd0
Loading
Loading
Loading
Loading
+110 −0
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@
 * limitations under the License.
 */

#include <stdlib.h>

#include "test/Test.h"

namespace aapt {
@@ -34,6 +36,29 @@ class TestData : public io::MallocData {
  std::string error_;
};

class TzSetter {
 public:
  explicit TzSetter(const std::string& new_tz) {
    old_tz_ = getenv("TZ");
    new_tz_ = "TZ=" + new_tz;
    putenv(const_cast<char*>(new_tz_.c_str()));
    tzset();
  }

  ~TzSetter() {
    if (old_tz_) {
      putenv(old_tz_);
    } else {
      putenv(const_cast<char*>("TZ"));
    }
    tzset();
  }

 private:
  char* old_tz_;
  std::string new_tz_;
};

std::unique_ptr<uint8_t[]> MakeTestArray() {
  auto array = std::make_unique<uint8_t[]>(kTestDataLength);
  for (int index = 0; index < kTestDataLength; ++index) {
@@ -86,6 +111,22 @@ void VerifyZipFile(const std::string& output_path, const std::string& file, cons
  }
}

void VerifyZipFileTimestamps(const std::string& output_path) {
  std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(output_path, nullptr);
  auto it = zip->Iterator();
  while (it->HasNext()) {
    auto file = it->Next();
    struct tm modification_time;
    ASSERT_TRUE(file->GetModificationTime(&modification_time));
    EXPECT_EQ(modification_time.tm_year, 80);
    EXPECT_EQ(modification_time.tm_mon, 0);
    EXPECT_EQ(modification_time.tm_mday, 1);
    EXPECT_EQ(modification_time.tm_hour, 0);
    EXPECT_EQ(modification_time.tm_min, 0);
    EXPECT_EQ(modification_time.tm_sec, 0);
  }
}

TEST_F(ArchiveTest, DirectoryWriteEntrySuccess) {
  std::string output_path = GetTestPath("output");
  std::unique_ptr<IArchiveWriter> writer = MakeDirectoryWriter(output_path);
@@ -206,4 +247,73 @@ TEST_F(ArchiveTest, ZipFileWriteFileError) {
  ASSERT_EQ("ZipFileWriteFileError", writer->GetError());
}

TEST_F(ArchiveTest, ZipFileTimeZoneUTC) {
  TzSetter tz("UTC0");
  std::string output_path = GetTestPath("output.apk");
  std::unique_ptr<IArchiveWriter> writer = MakeZipFileWriter(output_path);
  std::unique_ptr<uint8_t[]> data1 = MakeTestArray();
  std::unique_ptr<uint8_t[]> data2 = MakeTestArray();

  ASSERT_TRUE(writer->StartEntry("test1", 0));
  ASSERT_TRUE(writer->Write(static_cast<const void*>(data1.get()), kTestDataLength));
  ASSERT_TRUE(writer->FinishEntry());
  ASSERT_FALSE(writer->HadError());

  ASSERT_TRUE(writer->StartEntry("test2", 0));
  ASSERT_TRUE(writer->Write(static_cast<const void*>(data2.get()), kTestDataLength));
  ASSERT_TRUE(writer->FinishEntry());
  ASSERT_FALSE(writer->HadError());

  writer.reset();

  // All zip file entries must have the same timestamp, regardless of time zone. See: b/277978832
  VerifyZipFileTimestamps(output_path);
}

TEST_F(ArchiveTest, ZipFileTimeZoneWestOfUTC) {
  TzSetter tz("PST8");
  std::string output_path = GetTestPath("output.apk");
  std::unique_ptr<IArchiveWriter> writer = MakeZipFileWriter(output_path);
  std::unique_ptr<uint8_t[]> data1 = MakeTestArray();
  std::unique_ptr<uint8_t[]> data2 = MakeTestArray();

  ASSERT_TRUE(writer->StartEntry("test1", 0));
  ASSERT_TRUE(writer->Write(static_cast<const void*>(data1.get()), kTestDataLength));
  ASSERT_TRUE(writer->FinishEntry());
  ASSERT_FALSE(writer->HadError());

  ASSERT_TRUE(writer->StartEntry("test2", 0));
  ASSERT_TRUE(writer->Write(static_cast<const void*>(data2.get()), kTestDataLength));
  ASSERT_TRUE(writer->FinishEntry());
  ASSERT_FALSE(writer->HadError());

  writer.reset();

  // All zip file entries must have the same timestamp, regardless of time zone. See: b/277978832
  VerifyZipFileTimestamps(output_path);
}

TEST_F(ArchiveTest, ZipFileTimeZoneEastOfUTC) {
  TzSetter tz("EET-2");
  std::string output_path = GetTestPath("output.apk");
  std::unique_ptr<IArchiveWriter> writer = MakeZipFileWriter(output_path);
  std::unique_ptr<uint8_t[]> data1 = MakeTestArray();
  std::unique_ptr<uint8_t[]> data2 = MakeTestArray();

  ASSERT_TRUE(writer->StartEntry("test1", 0));
  ASSERT_TRUE(writer->Write(static_cast<const void*>(data1.get()), kTestDataLength));
  ASSERT_TRUE(writer->FinishEntry());
  ASSERT_FALSE(writer->HadError());

  ASSERT_TRUE(writer->StartEntry("test2", 0));
  ASSERT_TRUE(writer->Write(static_cast<const void*>(data2.get()), kTestDataLength));
  ASSERT_TRUE(writer->FinishEntry());
  ASSERT_FALSE(writer->HadError());

  writer.reset();

  // All zip file entries must have the same timestamp, regardless of time zone. See: b/277978832
  VerifyZipFileTimestamps(output_path);
}

}  // namespace aapt
+9 −0
Original line number Diff line number Diff line
@@ -57,6 +57,11 @@ class IFile {
    return false;
  }

  // Fills in buf with the last modification time of the file. Returns true if successful,
  // otherwise false (i.e., the operation is not supported or the file system is unable to provide
  // a last modification time).
  virtual bool GetModificationTime(struct tm* buf) const = 0;

 private:
  // Any segments created from this IFile need to be owned by this IFile, so
  // keep them
@@ -79,6 +84,10 @@ class FileSegment : public IFile {
    return file_->GetSource();
  }

  bool GetModificationTime(struct tm* buf) const override {
    return file_->GetModificationTime(buf);
  };

 private:
  DISALLOW_COPY_AND_ASSIGN(FileSegment);

+20 −0
Original line number Diff line number Diff line
@@ -14,9 +14,12 @@
 * limitations under the License.
 */

#define _POSIX_THREAD_SAFE_FUNCTIONS  // For mingw localtime_r().

#include "io/FileSystem.h"

#include <dirent.h>
#include <sys/stat.h>

#include "android-base/errors.h"
#include "androidfw/Source.h"
@@ -54,6 +57,23 @@ const android::Source& RegularFile::GetSource() const {
  return source_;
}

bool RegularFile::GetModificationTime(struct tm* buf) const {
  if (buf == nullptr) {
    return false;
  }
  struct stat stat_buf;
  if (stat(source_.path.c_str(), &stat_buf) != 0) {
    return false;
  }

  struct tm* ptm;
  struct tm tm_result;
  ptm = localtime_r(&stat_buf.st_mtime, &tm_result);

  *buf = *ptm;
  return true;
}

FileCollectionIterator::FileCollectionIterator(FileCollection* collection)
    : current_(collection->files_.begin()), end_(collection->files_.end()) {}

+1 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ class RegularFile : public IFile {
  std::unique_ptr<IData> OpenAsData() override;
  std::unique_ptr<io::InputStream> OpenInputStream() override;
  const android::Source& GetSource() const override;
  bool GetModificationTime(struct tm* buf) const override;

 private:
  DISALLOW_COPY_AND_ASSIGN(RegularFile);
+8 −0
Original line number Diff line number Diff line
@@ -75,6 +75,14 @@ bool ZipFile::WasCompressed() {
  return zip_entry_.method != kCompressStored;
}

bool ZipFile::GetModificationTime(struct tm* buf) const {
  if (buf == nullptr) {
    return false;
  }
  *buf = zip_entry_.GetModificationTime();
  return true;
}

ZipFileCollectionIterator::ZipFileCollectionIterator(
    ZipFileCollection* collection)
    : current_(collection->files_.begin()), end_(collection->files_.end()) {}
Loading