/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include "android-base/macros.h" #include "android-base/off64_t.h" struct z_stream_s; typedef struct z_stream_s z_stream; /** * Writes a Zip file via a stateful interface. * * Example: * * FILE* file = fopen("path/to/zip.zip", "wb"); * * ZipWriter writer(file); * * writer.StartEntry("test.txt", ZipWriter::kCompress | ZipWriter::kAlign); * writer.WriteBytes(buffer, bufferLen); * writer.WriteBytes(buffer2, bufferLen2); * writer.FinishEntry(); * * writer.StartEntry("empty.txt", 0); * writer.FinishEntry(); * * writer.Finish(); * * fclose(file); */ class ZipWriter { public: enum { /** * Flag to compress the zip entry using deflate. */ kCompress = 0x01, /** * Flag to align the zip entry data on a 32bit boundary. Useful for * mmapping the data at runtime. */ kAlign32 = 0x02, }; /** * A struct representing a zip file entry. */ struct FileEntry { std::string path; uint16_t compression_method; uint32_t crc32; uint32_t compressed_size; uint32_t uncompressed_size; uint16_t last_mod_time; uint16_t last_mod_date; uint32_t padding_length; off64_t local_file_header_offset; }; static const char* ErrorCodeString(int32_t error_code); /** * Create a ZipWriter that will write into a FILE stream. The file should be opened with * open mode of "wb" or "w+b". ZipWriter does not take ownership of the file stream. The * caller is responsible for closing the file. */ explicit ZipWriter(FILE* f); // Move constructor. ZipWriter(ZipWriter&& zipWriter) noexcept; // Move assignment. ZipWriter& operator=(ZipWriter&& zipWriter) noexcept; /** * Starts a new zip entry with the given path and flags. * Flags can be a bitwise OR of ZipWriter::kCompress and ZipWriter::kAlign. * 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 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. */ int32_t WriteBytes(const void* data, size_t len); /** * Finish a zip entry started with StartEntry(const char*, size_t) or * StartEntryWithTime(const char*, size_t, time_t). This must be called before * any new zip entries are started, or before Finish() is called. * Returns 0 on success, and an error value < 0 on failure. */ int32_t FinishEntry(); /** * Discards the last-written entry. Can only be called after an entry has been written using * FinishEntry(). * Returns 0 on success, and an error value < 0 on failure. */ int32_t DiscardLastEntry(); /** * Sets `out_entry` to the last entry written after a call to FinishEntry(). * Returns 0 on success, and an error value < 0 if no entries have been written. */ int32_t GetLastEntry(FileEntry* out_entry); /** * Writes the Central Directory Headers and flushes the zip file stream. * Returns 0 on success, and an error value < 0 on failure. */ int32_t Finish(); private: DISALLOW_COPY_AND_ASSIGN(ZipWriter); int32_t HandleError(int32_t error_code); int32_t PrepareDeflate(); int32_t StoreBytes(FileEntry* file, const void* data, size_t len); int32_t CompressBytes(FileEntry* file, const void* data, size_t len); int32_t FlushCompressedBytes(FileEntry* file); enum class State { kWritingZip, kWritingEntry, kDone, kError, }; FILE* file_; bool seekable_; off64_t current_offset_; State state_; std::vector files_; FileEntry current_file_entry_; std::unique_ptr z_stream_; std::vector buffer_; };