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
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);
}