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

Commit 5e9f3d44 authored by Christopher Ferris's avatar Christopher Ferris
Browse files

Allow setting an arbitrary alignment for an entry.

The current code only allows the creation of an entry that is 32 bit
aligned.

Bug: 25446938

Change-Id: I6c924df12ef2bc067b3de7789257af7e3db7e904
parent 542a511f
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -86,11 +86,27 @@ public:
   */
  int32_t StartEntry(const char* path, size_t flags);

  /**
   * Starts a new zip entry with the given path and flags, where the
   * entry will be aligned to the given alignment.
   * Flags can only be ZipWriter::kCompress. Using the flag ZipWriter::kAlign32
   * will result in an error.
   * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
   * Returns 0 on success, and an error value < 0 on failure.
   */
  int32_t StartAlignedEntry(const char* path, size_t flags, uint32_t alignment);

  /**
   * Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
   */
  int32_t StartEntryWithTime(const char* path, size_t flags, time_t time);

  /**
   * Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry.
   */
  int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time,
                                    uint32_t alignment);

  /**
   * Writes bytes to the zip file for the previously started zip entry.
   * Returns 0 on success, and an error value < 0 on failure.
+50 −5
Original line number Diff line number Diff line
@@ -20,12 +20,19 @@

#include <utils/Log.h>

#include <sys/param.h>

#include <cassert>
#include <cstdio>
#include <memory>
#include <vector>
#include <zlib.h>
#define DEF_MEM_LEVEL 8                // normally in zutil.h?

#if !defined(powerof2)
#define powerof2(x) ((((x)-1)&(x))==0)
#endif

/* Zip compression methods we support */
enum {
  kCompressStored     = 0,        // no compression
@@ -50,6 +57,12 @@ static const int32_t kInvalidEntryName = -3;
// An error occurred in zlib.
static const int32_t kZlibError = -4;

// The start aligned function was called with the aligned flag.
static const int32_t kInvalidAlign32Flag = -5;

// The alignment parameter is not a power of 2.
static const int32_t kInvalidAlignment = -6;

static const char* sErrorCodes[] = {
    "Invalid state",
    "IO error",
@@ -102,7 +115,25 @@ int32_t ZipWriter::HandleError(int32_t error_code) {
}

int32_t ZipWriter::StartEntry(const char* path, size_t flags) {
  return StartEntryWithTime(path, flags, time_t());
  uint32_t alignment = 0;
  if (flags & kAlign32) {
    flags &= ~kAlign32;
    alignment = 4;
  }
  return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
}

int32_t ZipWriter::StartAlignedEntry(const char* path, size_t flags, uint32_t alignment) {
  return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
}

int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t time) {
  uint32_t alignment = 0;
  if (flags & kAlign32) {
    flags &= ~kAlign32;
    alignment = 4;
  }
  return StartAlignedEntryWithTime(path, flags, time, alignment);
}

static void ExtractTimeAndDate(time_t when, uint16_t* out_time, uint16_t* out_date) {
@@ -126,11 +157,20 @@ static void ExtractTimeAndDate(time_t when, uint16_t* out_time, uint16_t* out_da
  *out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
}

int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t time) {
int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags,
                                             time_t time, uint32_t alignment) {
  if (state_ != State::kWritingZip) {
    return kInvalidState;
  }

  if (flags & kAlign32) {
    return kInvalidAlign32Flag;
  }

  if (powerof2(alignment) == 0) {
    return kInvalidAlignment;
  }

  FileInfo fileInfo = {};
  fileInfo.path = std::string(path);
  fileInfo.local_file_header_offset = current_offset_;
@@ -166,11 +206,14 @@ int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t tim
  header.file_name_length = fileInfo.path.size();

  off64_t offset = current_offset_ + sizeof(header) + fileInfo.path.size();
  if ((flags & ZipWriter::kAlign32) && (offset & 0x03)) {
  std::vector<char> zero_padding;
  if (alignment != 0 && (offset & (alignment - 1))) {
    // Pad the extra field so the data will be aligned.
    uint16_t padding = 4 - (offset % 4);
    uint16_t padding = alignment - (offset % alignment);
    header.extra_field_length = padding;
    offset += padding;
    zero_padding.resize(padding);
    memset(zero_padding.data(), 0, zero_padding.size());
  }

  if (fwrite(&header, sizeof(header), 1, file_) != 1) {
@@ -181,7 +224,9 @@ int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t tim
    return HandleError(kIoError);
  }

  if (fwrite("\0\0\0", 1, header.extra_field_length, file_) != header.extra_field_length) {
  if (header.extra_field_length != 0 &&
      fwrite(zero_padding.data(), 1, header.extra_field_length, file_)
      != header.extra_field_length) {
    return HandleError(kIoError);
  }

+106 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@

#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <time.h>
#include <memory>
#include <vector>

@@ -122,7 +123,7 @@ TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) {
  CloseArchive(handle);
}

TEST_F(zipwriter, WriteUncompressedZipWithAlignedFile) {
TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlag) {
  ZipWriter writer(file_);

  ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32));
@@ -142,6 +143,103 @@ TEST_F(zipwriter, WriteUncompressedZipWithAlignedFile) {
  CloseArchive(handle);
}

void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) {
  memset(tm, 0, sizeof(struct tm));
  tm->tm_hour = (zip_time >> 11) & 0x1f;
  tm->tm_min = (zip_time >> 5) & 0x3f;
  tm->tm_sec = (zip_time & 0x1f) << 1;

  tm->tm_year = ((zip_time >> 25) & 0x7f) + 80;
  tm->tm_mon = ((zip_time >> 21) & 0xf) - 1;
  tm->tm_mday = (zip_time >> 16) & 0x1f;
}

TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) {
  ZipWriter writer(file_);

  struct tm tm;
  memset(&tm, 0, sizeof(struct tm));
  ASSERT_TRUE(strptime("18:30:20 1/12/2001", "%H:%M:%S %d/%m/%Y", &tm) != nullptr);
  time_t time = mktime(&tm);
  ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time));
  ASSERT_EQ(0, writer.WriteBytes("he", 2));
  ASSERT_EQ(0, writer.FinishEntry());
  ASSERT_EQ(0, writer.Finish());

  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));

  ZipArchiveHandle handle;
  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));

  ZipEntry data;
  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
  EXPECT_EQ(0, data.offset & 0x03);

  struct tm mod;
  ConvertZipTimeToTm(data.mod_time, &mod);
  EXPECT_EQ(tm.tm_sec, mod.tm_sec);
  EXPECT_EQ(tm.tm_min, mod.tm_min);
  EXPECT_EQ(tm.tm_hour, mod.tm_hour);
  EXPECT_EQ(tm.tm_mday, mod.tm_mday);
  EXPECT_EQ(tm.tm_mon, mod.tm_mon);
  EXPECT_EQ(tm.tm_year, mod.tm_year);

  CloseArchive(handle);
}

TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValue) {
  ZipWriter writer(file_);

  ASSERT_EQ(0, writer.StartAlignedEntry("align.txt", 0, 4096));
  ASSERT_EQ(0, writer.WriteBytes("he", 2));
  ASSERT_EQ(0, writer.FinishEntry());
  ASSERT_EQ(0, writer.Finish());

  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));

  ZipArchiveHandle handle;
  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));

  ZipEntry data;
  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
  EXPECT_EQ(0, data.offset & 0xfff);

  CloseArchive(handle);
}

TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) {
  ZipWriter writer(file_);

  struct tm tm;
  memset(&tm, 0, sizeof(struct tm));
  ASSERT_TRUE(strptime("18:30:20 1/12/2001", "%H:%M:%S %d/%m/%Y", &tm) != nullptr);
  time_t time = mktime(&tm);
  ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096));
  ASSERT_EQ(0, writer.WriteBytes("he", 2));
  ASSERT_EQ(0, writer.FinishEntry());
  ASSERT_EQ(0, writer.Finish());

  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));

  ZipArchiveHandle handle;
  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));

  ZipEntry data;
  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
  EXPECT_EQ(0, data.offset & 0xfff);

  struct tm mod;
  ConvertZipTimeToTm(data.mod_time, &mod);
  EXPECT_EQ(tm.tm_sec, mod.tm_sec);
  EXPECT_EQ(tm.tm_min, mod.tm_min);
  EXPECT_EQ(tm.tm_hour, mod.tm_hour);
  EXPECT_EQ(tm.tm_mday, mod.tm_mday);
  EXPECT_EQ(tm.tm_mon, mod.tm_mon);
  EXPECT_EQ(tm.tm_year, mod.tm_year);

  CloseArchive(handle);
}

TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
  ZipWriter writer(file_);

@@ -206,3 +304,10 @@ TEST_F(zipwriter, WriteCompressedZipFlushFull) {

  CloseArchive(handle);
}

TEST_F(zipwriter, CheckStartEntryErrors) {
  ZipWriter writer(file_);

  ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096));
  ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3));
}