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