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