1 /* 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 17 #pragma once 18 19 #include <cstdio> 20 #include <ctime> 21 22 #include <memory> 23 #include <string> 24 #include <vector> 25 26 #include "android-base/macros.h" 27 #include "android-base/off64_t.h" 28 29 struct z_stream_s; 30 typedef struct z_stream_s z_stream; 31 32 /** 33 * Writes a Zip file via a stateful interface. 34 * 35 * Example: 36 * 37 * FILE* file = fopen("path/to/zip.zip", "wb"); 38 * 39 * ZipWriter writer(file); 40 * 41 * writer.StartEntry("test.txt", ZipWriter::kCompress | ZipWriter::kAlign); 42 * writer.WriteBytes(buffer, bufferLen); 43 * writer.WriteBytes(buffer2, bufferLen2); 44 * writer.FinishEntry(); 45 * 46 * writer.StartEntry("empty.txt", 0); 47 * writer.FinishEntry(); 48 * 49 * writer.Finish(); 50 * 51 * fclose(file); 52 */ 53 class ZipWriter { 54 public: 55 enum { 56 /** 57 * Flag to compress the zip entry using deflate. 58 */ 59 kCompress = 0x01, 60 61 /** 62 * Flag to align the zip entry data on a 32bit boundary. Useful for 63 * mmapping the data at runtime. 64 */ 65 kAlign32 = 0x02, 66 }; 67 68 /** 69 * A struct representing a zip file entry. 70 */ 71 struct FileEntry { 72 std::string path; 73 uint16_t compression_method; 74 uint32_t crc32; 75 uint32_t compressed_size; 76 uint32_t uncompressed_size; 77 uint16_t last_mod_time; 78 uint16_t last_mod_date; 79 uint32_t padding_length; 80 off64_t local_file_header_offset; 81 }; 82 83 static const char* ErrorCodeString(int32_t error_code); 84 85 /** 86 * Create a ZipWriter that will write into a FILE stream. The file should be opened with 87 * open mode of "wb" or "w+b". ZipWriter does not take ownership of the file stream. The 88 * caller is responsible for closing the file. 89 */ 90 explicit ZipWriter(FILE* f); 91 92 // Move constructor. 93 ZipWriter(ZipWriter&& zipWriter) noexcept; 94 95 // Move assignment. 96 ZipWriter& operator=(ZipWriter&& zipWriter) noexcept; 97 98 /** 99 * Starts a new zip entry with the given path and flags. 100 * Flags can be a bitwise OR of ZipWriter::kCompress and ZipWriter::kAlign. 101 * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry. 102 * Returns 0 on success, and an error value < 0 on failure. 103 */ 104 int32_t StartEntry(const char* path, size_t flags); 105 106 /** 107 * Starts a new zip entry with the given path and flags, where the 108 * entry will be aligned to the given alignment. 109 * Flags can only be ZipWriter::kCompress. Using the flag ZipWriter::kAlign32 110 * will result in an error. 111 * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry. 112 * Returns 0 on success, and an error value < 0 on failure. 113 */ 114 int32_t StartAlignedEntry(const char* path, size_t flags, uint32_t alignment); 115 116 /** 117 * Same as StartEntry(const char*, size_t), but sets a last modified time for the entry. 118 */ 119 int32_t StartEntryWithTime(const char* path, size_t flags, time_t time); 120 121 /** 122 * Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry. 123 */ 124 int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time, uint32_t alignment); 125 126 /** 127 * Writes bytes to the zip file for the previously started zip entry. 128 * Returns 0 on success, and an error value < 0 on failure. 129 */ 130 int32_t WriteBytes(const void* data, size_t len); 131 132 /** 133 * Finish a zip entry started with StartEntry(const char*, size_t) or 134 * StartEntryWithTime(const char*, size_t, time_t). This must be called before 135 * any new zip entries are started, or before Finish() is called. 136 * Returns 0 on success, and an error value < 0 on failure. 137 */ 138 int32_t FinishEntry(); 139 140 /** 141 * Discards the last-written entry. Can only be called after an entry has been written using 142 * FinishEntry(). 143 * Returns 0 on success, and an error value < 0 on failure. 144 */ 145 int32_t DiscardLastEntry(); 146 147 /** 148 * Sets `out_entry` to the last entry written after a call to FinishEntry(). 149 * Returns 0 on success, and an error value < 0 if no entries have been written. 150 */ 151 int32_t GetLastEntry(FileEntry* out_entry); 152 153 /** 154 * Writes the Central Directory Headers and flushes the zip file stream. 155 * Returns 0 on success, and an error value < 0 on failure. 156 */ 157 int32_t Finish(); 158 159 private: 160 DISALLOW_COPY_AND_ASSIGN(ZipWriter); 161 162 int32_t HandleError(int32_t error_code); 163 int32_t PrepareDeflate(); 164 int32_t StoreBytes(FileEntry* file, const void* data, size_t len); 165 int32_t CompressBytes(FileEntry* file, const void* data, size_t len); 166 int32_t FlushCompressedBytes(FileEntry* file); 167 168 enum class State { 169 kWritingZip, 170 kWritingEntry, 171 kDone, 172 kError, 173 }; 174 175 FILE* file_; 176 bool seekable_; 177 off64_t current_offset_; 178 State state_; 179 std::vector<FileEntry> files_; 180 FileEntry current_file_entry_; 181 182 std::unique_ptr<z_stream, void (*)(z_stream*)> z_stream_; 183 std::vector<uint8_t> buffer_; 184 }; 185