| OLD | NEW |
| (Empty) |
| 1 // Copyright 2017 The Chromium OS Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "compressor_archive_libarchive.h" | |
| 6 | |
| 7 #include <cerrno> | |
| 8 #include <cstring> | |
| 9 | |
| 10 #include "base/time/time.h" | |
| 11 #include "ppapi/cpp/logging.h" | |
| 12 | |
| 13 namespace compressor_archive_functions { | |
| 14 | |
| 15 // Called when minizip tries to open a zip archive file. We do nothing here | |
| 16 // because JavaScript takes care of file opening operation. | |
| 17 void* CustomArchiveOpen(void* compressor, | |
| 18 const char* /*filename*/, int /*mode*/) { | |
| 19 return compressor; | |
| 20 } | |
| 21 | |
| 22 // This function is not called because we don't unpack zip files here. | |
| 23 uLong CustomArchiveRead(void* /*compressor*/, void* /*stream*/, | |
| 24 void* /*buffur*/, uLong /*size*/) { | |
| 25 return 0 /* Success */; | |
| 26 } | |
| 27 | |
| 28 // Called when data chunk must be written on the archive. It copies data | |
| 29 // from the given buffer processed by minizip to an array buffer and passes | |
| 30 // it to compressor_stream. | |
| 31 uLong CustomArchiveWrite(void* compressor, | |
| 32 void* /*stream*/, | |
| 33 const void* zip_buffer, | |
| 34 uLong zip_length) { | |
| 35 CompressorArchiveMinizip* compressor_minizip = | |
| 36 static_cast<CompressorArchiveMinizip*>(compressor); | |
| 37 | |
| 38 int64_t written_bytes = compressor_minizip->compressor_stream()->Write( | |
| 39 compressor_minizip->offset_, zip_length, | |
| 40 static_cast<const char*>(zip_buffer)); | |
| 41 | |
| 42 if (written_bytes != zip_length) | |
| 43 return 0 /* Error */; | |
| 44 | |
| 45 // Update offset_ and length_. | |
| 46 compressor_minizip->offset_ += written_bytes; | |
| 47 if (compressor_minizip->offset_ > compressor_minizip->length_) | |
| 48 compressor_minizip->length_ = compressor_minizip->offset_; | |
| 49 return static_cast<uLong>(written_bytes); | |
| 50 } | |
| 51 | |
| 52 // Returns the offset from the beginning of the data. | |
| 53 long CustomArchiveTell(void* compressor, void* /*stream*/) { | |
| 54 CompressorArchiveMinizip* compressor_minizip = | |
| 55 static_cast<CompressorArchiveMinizip*>(compressor); | |
| 56 return static_cast<long>(compressor_minizip->offset_); | |
| 57 } | |
| 58 | |
| 59 // Moves the current offset to the specified position. | |
| 60 long CustomArchiveSeek(void* compressor, | |
| 61 void* /*stream*/, | |
| 62 uLong offset, | |
| 63 int origin) { | |
| 64 CompressorArchiveMinizip* compressor_minizip = | |
| 65 static_cast<CompressorArchiveMinizip*>(compressor); | |
| 66 | |
| 67 if (origin == ZLIB_FILEFUNC_SEEK_CUR) { | |
| 68 compressor_minizip->offset_ = | |
| 69 std::min(compressor_minizip->offset_ + static_cast<int64_t>(offset), | |
| 70 compressor_minizip->length_); | |
| 71 return 0 /* Success */; | |
| 72 } | |
| 73 if (origin == ZLIB_FILEFUNC_SEEK_END) { | |
| 74 compressor_minizip->offset_ = | |
| 75 std::max(compressor_minizip->length_ - static_cast<int64_t>(offset), | |
| 76 0LL); | |
| 77 return 0 /* Success */; | |
| 78 } | |
| 79 if (origin == ZLIB_FILEFUNC_SEEK_SET) { | |
| 80 compressor_minizip->offset_ = | |
| 81 std::min(static_cast<int64_t>(offset), compressor_minizip->length_); | |
| 82 return 0 /* Success */; | |
| 83 } | |
| 84 return -1 /* Error */; | |
| 85 } | |
| 86 | |
| 87 // Releases all used resources. compressor points to compressor_minizip and | |
| 88 // it is deleted in the destructor of Compressor, so we don't need to delete | |
| 89 // it here. | |
| 90 int CustomArchiveClose(void* /*compressor*/, void* /*stream*/) { | |
| 91 return 0 /* Success */; | |
| 92 } | |
| 93 | |
| 94 // Returns the last error that happened when writing data. This function always | |
| 95 // returns zero, which means there are no errors. | |
| 96 int CustomArchiveError(void* /*compressor*/, void* /*stream*/) { | |
| 97 return 0 /* Success */; | |
| 98 } | |
| 99 | |
| 100 } // compressor_archive_functions | |
| 101 | |
| 102 CompressorArchiveMinizip::CompressorArchiveMinizip( | |
| 103 CompressorStream* compressor_stream) | |
| 104 : CompressorArchive(compressor_stream), | |
| 105 compressor_stream_(compressor_stream), | |
| 106 zip_file_(nullptr), | |
| 107 offset_(0), | |
| 108 length_(0) { | |
| 109 destination_buffer_ = | |
| 110 new char[compressor_stream_constants::kMaximumDataChunkSize]; | |
| 111 } | |
| 112 | |
| 113 CompressorArchiveMinizip::~CompressorArchiveMinizip() { | |
| 114 delete destination_buffer_; | |
| 115 } | |
| 116 | |
| 117 bool CompressorArchiveMinizip::CreateArchive() { | |
| 118 // Set up archive object. | |
| 119 zlib_filefunc_def zip_funcs; | |
| 120 zip_funcs.zopen_file = compressor_archive_functions::CustomArchiveOpen; | |
| 121 zip_funcs.zread_file = compressor_archive_functions::CustomArchiveRead; | |
| 122 zip_funcs.zwrite_file = compressor_archive_functions::CustomArchiveWrite; | |
| 123 zip_funcs.ztell_file = compressor_archive_functions::CustomArchiveTell; | |
| 124 zip_funcs.zseek_file = compressor_archive_functions::CustomArchiveSeek; | |
| 125 zip_funcs.zclose_file = compressor_archive_functions::CustomArchiveClose; | |
| 126 zip_funcs.zerror_file = compressor_archive_functions::CustomArchiveError; | |
| 127 zip_funcs.opaque = this; | |
| 128 | |
| 129 zip_file_ = zipOpen2(nullptr /* pathname */, | |
| 130 APPEND_STATUS_CREATE, | |
| 131 nullptr /* globalcomment */, | |
| 132 &zip_funcs); | |
| 133 if (!zip_file_) { | |
| 134 set_error_message(compressor_archive_constants::kCreateArchiveError); | |
| 135 return false /* Error */; | |
| 136 } | |
| 137 return true /* Success */; | |
| 138 } | |
| 139 | |
| 140 bool CompressorArchiveMinizip::AddToArchive(const std::string& filename, | |
| 141 int64_t file_size, | |
| 142 int64_t modification_time, | |
| 143 bool is_directory) { | |
| 144 // Minizip takes filenames that end with '/' as directories. | |
| 145 std::string normalized_filename = filename; | |
| 146 if (is_directory) | |
| 147 normalized_filename += "/"; | |
| 148 | |
| 149 // Fill zipfileMetadata with modification_time. | |
| 150 zip_fileinfo zipfileMetadata; | |
| 151 // modification_time is millisecond-based, while FromTimeT takes seconds. | |
| 152 base::Time tm = base::Time::FromTimeT((int64_t)modification_time / 1000); | |
| 153 base::Time::Exploded exploded_time = {}; | |
| 154 tm.LocalExplode(&exploded_time); | |
| 155 zipfileMetadata.tmz_date.tm_sec = exploded_time.second; | |
| 156 zipfileMetadata.tmz_date.tm_min = exploded_time.minute; | |
| 157 zipfileMetadata.tmz_date.tm_hour = exploded_time.hour; | |
| 158 zipfileMetadata.tmz_date.tm_year = exploded_time.year; | |
| 159 zipfileMetadata.tmz_date.tm_mday = exploded_time.day_of_month; | |
| 160 // Convert from 1-based to 0-based. | |
| 161 zipfileMetadata.tmz_date.tm_mon = exploded_time.month - 1; | |
| 162 | |
| 163 // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT | |
| 164 // Setting the Language encoding flag so the file is told to be in utf-8. | |
| 165 const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11; | |
| 166 | |
| 167 int open_result = zipOpenNewFileInZip4(zip_file_, // file | |
| 168 normalized_filename.c_str(),// filename | |
| 169 &zipfileMetadata, // zipfi | |
| 170 nullptr, // extrafield_local | |
| 171 0u, // size_extrafield_local | |
| 172 nullptr, // extrafield_global | |
| 173 0u, // size_extrafield_global | |
| 174 nullptr, // comment | |
| 175 Z_DEFLATED, // method | |
| 176 Z_DEFAULT_COMPRESSION, // level | |
| 177 0, // raw | |
| 178 -MAX_WBITS, // windowBits | |
| 179 DEF_MEM_LEVEL, // memLevel | |
| 180 Z_DEFAULT_STRATEGY, // strategy | |
| 181 nullptr, // password | |
| 182 0, // crcForCrypting | |
| 183 0, // versionMadeBy | |
| 184 LANGUAGE_ENCODING_FLAG); // flagBase | |
| 185 if (open_result != ZIP_OK) { | |
| 186 CloseArchive(true /* has_error */); | |
| 187 set_error_message(compressor_archive_constants::kAddToArchiveError); | |
| 188 return false /* Error */; | |
| 189 } | |
| 190 | |
| 191 bool has_error = false; | |
| 192 if (!is_directory) { | |
| 193 int64_t remaining_size = file_size; | |
| 194 while (remaining_size > 0) { | |
| 195 int64_t chunk_size = std::min(remaining_size, | |
| 196 compressor_stream_constants::kMaximumDataChunkSize); | |
| 197 PP_DCHECK(chunk_size > 0); | |
| 198 | |
| 199 int64_t read_bytes = | |
| 200 compressor_stream_->Read(chunk_size, destination_buffer_); | |
| 201 | |
| 202 // Negative read_bytes indicates an error occurred when reading chunks. | |
| 203 // 0 just means there is no more data available, but here we need positive | |
| 204 // length of bytes, so this is also an error here. | |
| 205 if (read_bytes <= 0) { | |
| 206 has_error = true; | |
| 207 break; | |
| 208 } | |
| 209 | |
| 210 if (zipWriteInFileInZip(zip_file_, destination_buffer_, read_bytes) != | |
| 211 ZIP_OK) { | |
| 212 has_error = true; | |
| 213 break; | |
| 214 } | |
| 215 remaining_size -= read_bytes; | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 if (!has_error && zipCloseFileInZip(zip_file_) != ZIP_OK) | |
| 220 has_error = true; | |
| 221 | |
| 222 if (has_error) { | |
| 223 CloseArchive(true /* has_error */); | |
| 224 set_error_message(compressor_archive_constants::kAddToArchiveError); | |
| 225 return false /* Error */; | |
| 226 } | |
| 227 | |
| 228 return true /* Success */; | |
| 229 } | |
| 230 | |
| 231 bool CompressorArchiveMinizip::CloseArchive(bool has_error) { | |
| 232 if (zipClose(zip_file_, nullptr /* global_comment */) != ZIP_OK) { | |
| 233 set_error_message(compressor_archive_constants::kCloseArchiveError); | |
| 234 return false /* Error */; | |
| 235 } | |
| 236 if (!has_error) { | |
| 237 if (compressor_stream()->Flush() < 0) { | |
| 238 set_error_message(compressor_archive_constants::kCloseArchiveError); | |
| 239 return false /* Error */; | |
| 240 } | |
| 241 } | |
| 242 return true /* Success */; | |
| 243 } | |
| OLD | NEW |