Merge "Allow setting an arbitrary alignment for an entry." am: ad12df20e9
am: 5633ccf042
* commit '5633ccf042f76a894fa31fe1579846ac516fa60c':
Allow setting an arbitrary alignment for an entry.
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
index 0efade8..0b6ede4 100644
--- a/include/ziparchive/zip_writer.h
+++ b/include/ziparchive/zip_writer.h
@@ -87,11 +87,27 @@
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.
*/
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index f117cc5..1ebed30 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -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 @@
// 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::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 @@
*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 @@
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 @@
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);
}
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index b7d1458..fe0846d 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -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 @@
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 @@
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 @@
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));
+}