blob: 76005288681589b8a4d54eb82a30febcc5491884 [file] [log] [blame]
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070017#include "ziparchive/zip_writer.h"
18
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070019#include <cstdio>
Adam Lesinski537713b2017-03-16 13:23:51 -070020#include <sys/param.h>
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070021#include <zlib.h>
Adam Lesinski591fd392015-10-06 15:23:46 -070022#define DEF_MEM_LEVEL 8 // normally in zutil.h?
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070023
Adam Lesinski537713b2017-03-16 13:23:51 -070024#include <memory>
25#include <vector>
26
27#include "android-base/logging.h"
Adam Lesinskib02d6902017-03-23 11:57:05 -070028#include "utils/Compat.h"
Adam Lesinski537713b2017-03-16 13:23:51 -070029#include "utils/Log.h"
30
31#include "entry_name_utils-inl.h"
32#include "zip_archive_common.h"
33
Christopher Ferris5e9f3d42016-01-19 10:33:03 -080034#if !defined(powerof2)
35#define powerof2(x) ((((x)-1)&(x))==0)
36#endif
37
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070038/* Zip compression methods we support */
39enum {
40 kCompressStored = 0, // no compression
41 kCompressDeflated = 8, // standard deflate
42};
43
Adam Lesinski591fd392015-10-06 15:23:46 -070044// Size of the output buffer used for compression.
45static const size_t kBufSize = 32768u;
46
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070047// No error, operation completed successfully.
48static const int32_t kNoError = 0;
49
50// The ZipWriter is in a bad state.
51static const int32_t kInvalidState = -1;
52
53// There was an IO error while writing to disk.
54static const int32_t kIoError = -2;
55
56// The zip entry name was invalid.
57static const int32_t kInvalidEntryName = -3;
58
Adam Lesinski591fd392015-10-06 15:23:46 -070059// An error occurred in zlib.
60static const int32_t kZlibError = -4;
61
Christopher Ferris5e9f3d42016-01-19 10:33:03 -080062// The start aligned function was called with the aligned flag.
63static const int32_t kInvalidAlign32Flag = -5;
64
65// The alignment parameter is not a power of 2.
66static const int32_t kInvalidAlignment = -6;
67
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070068static const char* sErrorCodes[] = {
69 "Invalid state",
70 "IO error",
71 "Invalid entry name",
Adam Lesinski591fd392015-10-06 15:23:46 -070072 "Zlib error",
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070073};
74
75const char* ZipWriter::ErrorCodeString(int32_t error_code) {
76 if (error_code < 0 && (-error_code) < static_cast<int32_t>(arraysize(sErrorCodes))) {
77 return sErrorCodes[-error_code];
78 }
79 return nullptr;
80}
81
Adam Lesinski591fd392015-10-06 15:23:46 -070082static void DeleteZStream(z_stream* stream) {
83 deflateEnd(stream);
84 delete stream;
85}
86
87ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip),
88 z_stream_(nullptr, DeleteZStream), buffer_(kBufSize) {
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070089}
90
91ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
92 current_offset_(writer.current_offset_),
93 state_(writer.state_),
Adam Lesinski591fd392015-10-06 15:23:46 -070094 files_(std::move(writer.files_)),
95 z_stream_(std::move(writer.z_stream_)),
96 buffer_(std::move(writer.buffer_)){
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -070097 writer.file_ = nullptr;
98 writer.state_ = State::kError;
99}
100
101ZipWriter& ZipWriter::operator=(ZipWriter&& writer) {
102 file_ = writer.file_;
103 current_offset_ = writer.current_offset_;
104 state_ = writer.state_;
105 files_ = std::move(writer.files_);
Adam Lesinski591fd392015-10-06 15:23:46 -0700106 z_stream_ = std::move(writer.z_stream_);
107 buffer_ = std::move(writer.buffer_);
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700108 writer.file_ = nullptr;
109 writer.state_ = State::kError;
110 return *this;
111}
112
113int32_t ZipWriter::HandleError(int32_t error_code) {
114 state_ = State::kError;
Adam Lesinski591fd392015-10-06 15:23:46 -0700115 z_stream_.reset();
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700116 return error_code;
117}
118
119int32_t ZipWriter::StartEntry(const char* path, size_t flags) {
Christopher Ferris5e9f3d42016-01-19 10:33:03 -0800120 uint32_t alignment = 0;
121 if (flags & kAlign32) {
122 flags &= ~kAlign32;
123 alignment = 4;
124 }
125 return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
126}
127
128int32_t ZipWriter::StartAlignedEntry(const char* path, size_t flags, uint32_t alignment) {
129 return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
130}
131
132int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t time) {
133 uint32_t alignment = 0;
134 if (flags & kAlign32) {
135 flags &= ~kAlign32;
136 alignment = 4;
137 }
138 return StartAlignedEntryWithTime(path, flags, time, alignment);
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700139}
140
141static void ExtractTimeAndDate(time_t when, uint16_t* out_time, uint16_t* out_date) {
142 /* round up to an even number of seconds */
143 when = static_cast<time_t>((static_cast<unsigned long>(when) + 1) & (~1));
144
145 struct tm* ptm;
146#if !defined(_WIN32)
147 struct tm tm_result;
148 ptm = localtime_r(&when, &tm_result);
149#else
150 ptm = localtime(&when);
151#endif
152
153 int year = ptm->tm_year;
154 if (year < 80) {
155 year = 80;
156 }
157
158 *out_date = (year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday;
159 *out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
160}
161
Christopher Ferris5e9f3d42016-01-19 10:33:03 -0800162int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags,
163 time_t time, uint32_t alignment) {
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700164 if (state_ != State::kWritingZip) {
165 return kInvalidState;
166 }
167
Christopher Ferris5e9f3d42016-01-19 10:33:03 -0800168 if (flags & kAlign32) {
169 return kInvalidAlign32Flag;
170 }
171
172 if (powerof2(alignment) == 0) {
173 return kInvalidAlignment;
174 }
175
Adam Lesinski537713b2017-03-16 13:23:51 -0700176 current_file_entry_ = {};
177 current_file_entry_.path = path;
178 current_file_entry_.local_file_header_offset = current_offset_;
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700179
Adam Lesinski537713b2017-03-16 13:23:51 -0700180 if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(current_file_entry_.path.data()),
181 current_file_entry_.path.size())) {
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700182 return kInvalidEntryName;
183 }
184
185 LocalFileHeader header = {};
186 header.lfh_signature = LocalFileHeader::kSignature;
187
188 // Set this flag to denote that a DataDescriptor struct will appear after the data,
189 // containing the crc and size fields.
190 header.gpb_flags |= kGPBDDFlagMask;
191
Adam Lesinski591fd392015-10-06 15:23:46 -0700192 if (flags & ZipWriter::kCompress) {
Adam Lesinski537713b2017-03-16 13:23:51 -0700193 current_file_entry_.compression_method = kCompressDeflated;
Adam Lesinski591fd392015-10-06 15:23:46 -0700194
195 int32_t result = PrepareDeflate();
196 if (result != kNoError) {
197 return result;
198 }
199 } else {
Adam Lesinski537713b2017-03-16 13:23:51 -0700200 current_file_entry_.compression_method = kCompressStored;
Adam Lesinski591fd392015-10-06 15:23:46 -0700201 }
Adam Lesinski537713b2017-03-16 13:23:51 -0700202 header.compression_method = current_file_entry_.compression_method;
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700203
Adam Lesinski537713b2017-03-16 13:23:51 -0700204 ExtractTimeAndDate(time, &current_file_entry_.last_mod_time, &current_file_entry_.last_mod_date);
205 header.last_mod_time = current_file_entry_.last_mod_time;
206 header.last_mod_date = current_file_entry_.last_mod_date;
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700207
Adam Lesinski537713b2017-03-16 13:23:51 -0700208 header.file_name_length = current_file_entry_.path.size();
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700209
Adam Lesinski537713b2017-03-16 13:23:51 -0700210 off64_t offset = current_offset_ + sizeof(header) + current_file_entry_.path.size();
Christopher Ferris5e9f3d42016-01-19 10:33:03 -0800211 std::vector<char> zero_padding;
212 if (alignment != 0 && (offset & (alignment - 1))) {
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700213 // Pad the extra field so the data will be aligned.
Christopher Ferris5e9f3d42016-01-19 10:33:03 -0800214 uint16_t padding = alignment - (offset % alignment);
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700215 header.extra_field_length = padding;
216 offset += padding;
Christopher Ferris5e9f3d42016-01-19 10:33:03 -0800217 zero_padding.resize(padding);
218 memset(zero_padding.data(), 0, zero_padding.size());
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700219 }
220
221 if (fwrite(&header, sizeof(header), 1, file_) != 1) {
222 return HandleError(kIoError);
223 }
224
Adam Lesinski537713b2017-03-16 13:23:51 -0700225 if (fwrite(path, sizeof(*path), current_file_entry_.path.size(), file_)
226 != current_file_entry_.path.size()) {
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700227 return HandleError(kIoError);
228 }
229
Christopher Ferris5e9f3d42016-01-19 10:33:03 -0800230 if (header.extra_field_length != 0 &&
231 fwrite(zero_padding.data(), 1, header.extra_field_length, file_)
232 != header.extra_field_length) {
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700233 return HandleError(kIoError);
234 }
235
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700236 current_offset_ = offset;
237 state_ = State::kWritingEntry;
238 return kNoError;
239}
240
Adam Lesinski537713b2017-03-16 13:23:51 -0700241int32_t ZipWriter::DiscardLastEntry() {
242 if (state_ != State::kWritingZip || files_.empty()) {
243 return kInvalidState;
244 }
245
246 FileEntry& last_entry = files_.back();
247 current_offset_ = last_entry.local_file_header_offset;
248 if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
249 return HandleError(kIoError);
250 }
251 files_.pop_back();
252 return kNoError;
253}
254
255int32_t ZipWriter::GetLastEntry(FileEntry* out_entry) {
256 CHECK(out_entry != nullptr);
257
258 if (files_.empty()) {
259 return kInvalidState;
260 }
261 *out_entry = files_.back();
262 return kNoError;
263}
264
Adam Lesinski591fd392015-10-06 15:23:46 -0700265int32_t ZipWriter::PrepareDeflate() {
Adam Lesinski537713b2017-03-16 13:23:51 -0700266 CHECK(state_ == State::kWritingZip);
Adam Lesinski591fd392015-10-06 15:23:46 -0700267
268 // Initialize the z_stream for compression.
269 z_stream_ = std::unique_ptr<z_stream, void(*)(z_stream*)>(new z_stream(), DeleteZStream);
270
Colin Cross7c6c7f02016-09-16 10:15:51 -0700271#pragma GCC diagnostic push
272#pragma GCC diagnostic ignored "-Wold-style-cast"
Adam Lesinski591fd392015-10-06 15:23:46 -0700273 int zerr = deflateInit2(z_stream_.get(), Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS,
274 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
Colin Cross7c6c7f02016-09-16 10:15:51 -0700275#pragma GCC diagnostic pop
276
Adam Lesinski591fd392015-10-06 15:23:46 -0700277 if (zerr != Z_OK) {
278 if (zerr == Z_VERSION_ERROR) {
279 ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
280 return HandleError(kZlibError);
281 } else {
282 ALOGE("deflateInit2 failed (zerr=%d)", zerr);
283 return HandleError(kZlibError);
284 }
285 }
286
287 z_stream_->next_out = buffer_.data();
288 z_stream_->avail_out = buffer_.size();
289 return kNoError;
290}
291
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700292int32_t ZipWriter::WriteBytes(const void* data, size_t len) {
293 if (state_ != State::kWritingEntry) {
294 return HandleError(kInvalidState);
295 }
296
Adam Lesinski591fd392015-10-06 15:23:46 -0700297 int32_t result = kNoError;
Adam Lesinski537713b2017-03-16 13:23:51 -0700298 if (current_file_entry_.compression_method & kCompressDeflated) {
299 result = CompressBytes(&current_file_entry_, data, len);
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700300 } else {
Adam Lesinski537713b2017-03-16 13:23:51 -0700301 result = StoreBytes(&current_file_entry_, data, len);
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700302 }
303
Adam Lesinski591fd392015-10-06 15:23:46 -0700304 if (result != kNoError) {
305 return result;
306 }
307
Adam Lesinski537713b2017-03-16 13:23:51 -0700308 current_file_entry_.crc32 = crc32(current_file_entry_.crc32,
309 reinterpret_cast<const Bytef*>(data), len);
310 current_file_entry_.uncompressed_size += len;
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700311 return kNoError;
312}
313
Adam Lesinski537713b2017-03-16 13:23:51 -0700314int32_t ZipWriter::StoreBytes(FileEntry* file, const void* data, size_t len) {
315 CHECK(state_ == State::kWritingEntry);
Adam Lesinski591fd392015-10-06 15:23:46 -0700316
317 if (fwrite(data, 1, len, file_) != len) {
318 return HandleError(kIoError);
319 }
320 file->compressed_size += len;
321 current_offset_ += len;
322 return kNoError;
323}
324
Adam Lesinski537713b2017-03-16 13:23:51 -0700325int32_t ZipWriter::CompressBytes(FileEntry* file, const void* data, size_t len) {
326 CHECK(state_ == State::kWritingEntry);
327 CHECK(z_stream_);
328 CHECK(z_stream_->next_out != nullptr);
329 CHECK(z_stream_->avail_out != 0);
Adam Lesinski591fd392015-10-06 15:23:46 -0700330
331 // Prepare the input.
332 z_stream_->next_in = reinterpret_cast<const uint8_t*>(data);
333 z_stream_->avail_in = len;
334
335 while (z_stream_->avail_in > 0) {
336 // We have more data to compress.
337 int zerr = deflate(z_stream_.get(), Z_NO_FLUSH);
338 if (zerr != Z_OK) {
339 return HandleError(kZlibError);
340 }
341
342 if (z_stream_->avail_out == 0) {
343 // The output is full, let's write it to disk.
Christopher Ferrisa2a32b02015-11-04 17:54:32 -0800344 size_t write_bytes = z_stream_->next_out - buffer_.data();
345 if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
Adam Lesinski591fd392015-10-06 15:23:46 -0700346 return HandleError(kIoError);
347 }
Christopher Ferrisa2a32b02015-11-04 17:54:32 -0800348 file->compressed_size += write_bytes;
349 current_offset_ += write_bytes;
Adam Lesinski591fd392015-10-06 15:23:46 -0700350
351 // Reset the output buffer for the next input.
352 z_stream_->next_out = buffer_.data();
353 z_stream_->avail_out = buffer_.size();
354 }
355 }
356 return kNoError;
357}
358
Adam Lesinski537713b2017-03-16 13:23:51 -0700359int32_t ZipWriter::FlushCompressedBytes(FileEntry* file) {
360 CHECK(state_ == State::kWritingEntry);
361 CHECK(z_stream_);
362 CHECK(z_stream_->next_out != nullptr);
363 CHECK(z_stream_->avail_out != 0);
Adam Lesinski591fd392015-10-06 15:23:46 -0700364
Christopher Ferrisa2a32b02015-11-04 17:54:32 -0800365 // Keep deflating while there isn't enough space in the buffer to
366 // to complete the compress.
367 int zerr;
368 while ((zerr = deflate(z_stream_.get(), Z_FINISH)) == Z_OK) {
Adam Lesinski537713b2017-03-16 13:23:51 -0700369 CHECK(z_stream_->avail_out == 0);
Christopher Ferrisa2a32b02015-11-04 17:54:32 -0800370 size_t write_bytes = z_stream_->next_out - buffer_.data();
371 if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
372 return HandleError(kIoError);
373 }
374 file->compressed_size += write_bytes;
375 current_offset_ += write_bytes;
376
377 z_stream_->next_out = buffer_.data();
378 z_stream_->avail_out = buffer_.size();
379 }
Adam Lesinski591fd392015-10-06 15:23:46 -0700380 if (zerr != Z_STREAM_END) {
381 return HandleError(kZlibError);
382 }
383
Christopher Ferrisa2a32b02015-11-04 17:54:32 -0800384 size_t write_bytes = z_stream_->next_out - buffer_.data();
385 if (write_bytes != 0) {
386 if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
Adam Lesinski591fd392015-10-06 15:23:46 -0700387 return HandleError(kIoError);
388 }
Christopher Ferrisa2a32b02015-11-04 17:54:32 -0800389 file->compressed_size += write_bytes;
390 current_offset_ += write_bytes;
Adam Lesinski591fd392015-10-06 15:23:46 -0700391 }
392 z_stream_.reset();
393 return kNoError;
394}
395
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700396int32_t ZipWriter::FinishEntry() {
397 if (state_ != State::kWritingEntry) {
398 return kInvalidState;
399 }
400
Adam Lesinski537713b2017-03-16 13:23:51 -0700401 if (current_file_entry_.compression_method & kCompressDeflated) {
402 int32_t result = FlushCompressedBytes(&current_file_entry_);
Adam Lesinski591fd392015-10-06 15:23:46 -0700403 if (result != kNoError) {
404 return result;
405 }
406 }
407
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700408 const uint32_t sig = DataDescriptor::kOptSignature;
409 if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
410 state_ = State::kError;
411 return kIoError;
412 }
413
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700414 DataDescriptor dd = {};
Adam Lesinski537713b2017-03-16 13:23:51 -0700415 dd.crc32 = current_file_entry_.crc32;
416 dd.compressed_size = current_file_entry_.compressed_size;
417 dd.uncompressed_size = current_file_entry_.uncompressed_size;
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700418 if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
419 return HandleError(kIoError);
420 }
421
Adam Lesinski537713b2017-03-16 13:23:51 -0700422 files_.emplace_back(std::move(current_file_entry_));
423
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700424 current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
425 state_ = State::kWritingZip;
426 return kNoError;
427}
428
429int32_t ZipWriter::Finish() {
430 if (state_ != State::kWritingZip) {
431 return kInvalidState;
432 }
433
434 off64_t startOfCdr = current_offset_;
Adam Lesinski537713b2017-03-16 13:23:51 -0700435 for (FileEntry& file : files_) {
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700436 CentralDirectoryRecord cdr = {};
437 cdr.record_signature = CentralDirectoryRecord::kSignature;
438 cdr.gpb_flags |= kGPBDDFlagMask;
439 cdr.compression_method = file.compression_method;
440 cdr.last_mod_time = file.last_mod_time;
441 cdr.last_mod_date = file.last_mod_date;
442 cdr.crc32 = file.crc32;
443 cdr.compressed_size = file.compressed_size;
444 cdr.uncompressed_size = file.uncompressed_size;
445 cdr.file_name_length = file.path.size();
446 cdr.local_file_header_offset = file.local_file_header_offset;
447 if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
448 return HandleError(kIoError);
449 }
450
451 if (fwrite(file.path.data(), 1, file.path.size(), file_) != file.path.size()) {
452 return HandleError(kIoError);
453 }
454
455 current_offset_ += sizeof(cdr) + file.path.size();
456 }
457
458 EocdRecord er = {};
459 er.eocd_signature = EocdRecord::kSignature;
Adam Lesinski044c7902015-10-20 12:41:49 -0700460 er.disk_num = 0;
461 er.cd_start_disk = 0;
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700462 er.num_records_on_disk = files_.size();
463 er.num_records = files_.size();
464 er.cd_size = current_offset_ - startOfCdr;
465 er.cd_start_offset = startOfCdr;
466
467 if (fwrite(&er, sizeof(er), 1, file_) != 1) {
468 return HandleError(kIoError);
469 }
470
Adam Lesinski537713b2017-03-16 13:23:51 -0700471 current_offset_ += sizeof(er);
472
473 // Since we can BackUp() and potentially finish writing at an offset less than one we had
474 // already written at, we must truncate the file.
475
476 if (ftruncate64(fileno(file_), current_offset_) != 0) {
477 return HandleError(kIoError);
478 }
479
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700480 if (fflush(file_) != 0) {
481 return HandleError(kIoError);
482 }
483
Adam Lesinskiad4ad8c2015-10-05 18:16:18 -0700484 state_ = State::kDone;
485 return kNoError;
486}