Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2017 The Chromium OS Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "compressor_archive_libarchive.h" | 5 #include "compressor_archive_libarchive.h" |
| 6 | 6 |
| 7 #include <cerrno> | 7 #include <cerrno> |
| 8 #include <cstring> | 8 #include <cstring> |
| 9 | 9 |
| 10 #include "archive_entry.h" | 10 #include "base/time/time.h" |
| 11 #include "ppapi/cpp/logging.h" | 11 #include "ppapi/cpp/logging.h" |
| 12 | 12 |
| 13 namespace { | 13 namespace compressor_archive_functions { |
| 14 // Nothing to do here because JavaScript takes care of file open operations. | 14 |
| 15 int CustomArchiveOpen(archive* archive_object, void* client_data) { | 15 // Called when minizip tries to open a zip archive file. We do nothing here |
| 16 return ARCHIVE_OK; | 16 // because JavaScript takes care of file opening operation. |
| 17 } | 17 void* CustomArchiveOpen(void* compressor, |
| 18 | 18 const char* /*filename*/, int /*mode*/) { |
| 19 // Called when any data chunk must be written on the archive. It copies data | 19 return compressor; |
| 20 // from the given buffer processed by libarchive to an array buffer and passes | 20 } |
| 21 // it to compressor_stream. | 21 |
| 22 ssize_t CustomArchiveWrite(archive* archive_object, void* client_data, | 22 // This function is not called because we don't unpack zip files here. |
| 23 const void* buffer, size_t length) { | 23 uLong CustomArchiveRead(void* /*compressor*/, void* /*stream*/, |
| 24 CompressorArchiveLibarchive* compressor_libarchive = | 24 void* /*buffuer*/, uLong /*size*/) { |
|
mtomasz
2017/04/11 06:36:45
typo: buffer
| |
| 25 static_cast<CompressorArchiveLibarchive*>(client_data); | 25 return 0 /* Success */; |
| 26 | 26 } |
| 27 const char* char_buffer = static_cast<const char*>(buffer); | 27 |
| 28 | 28 // Called when data chunk must be written on the archive. It copies data |
| 29 // Copy the data in buffer to array_buffer. | 29 // from the given buffer processed by minizip to an array buffer and passes |
| 30 PP_DCHECK(length > 0); | 30 // it to compressor_stream. |
| 31 pp::VarArrayBuffer array_buffer(length); | 31 uLong CustomArchiveWrite(void* compressor, |
| 32 char* array_buffer_data = static_cast<char*>(array_buffer.Map()); | 32 void* /*stream*/, |
| 33 memcpy(array_buffer_data, char_buffer, length); | 33 const void* zip_buffer, |
| 34 array_buffer.Unmap(); | 34 uLong zip_length) { |
| 35 | 35 CompressorArchiveLibarchive* compressor_libarchive = |
| 36 ssize_t written_bytes = | 36 static_cast<CompressorArchiveLibarchive*>(compressor); |
| 37 compressor_libarchive->compressor_stream()->Write(length, array_buffer); | 37 |
| 38 | 38 int64_t written_bytes = compressor_libarchive->compressor_stream()->Write( |
| 39 // Negative written_bytes represents an error. | 39 compressor_libarchive->offset_, zip_length, |
| 40 if (written_bytes < 0) { | 40 static_cast<const char*>(zip_buffer)); |
| 41 // When writing fails, archive_set_error() should be called and -1 should | 41 |
| 42 // be returned. | 42 if (written_bytes != zip_length) |
| 43 archive_set_error( | 43 return 0 /* Error */; |
| 44 compressor_libarchive->archive(), EIO, "Failed to write a chunk."); | 44 |
| 45 return -1; | 45 // Update offset_ and length_. |
| 46 } | 46 compressor_libarchive->offset_ += written_bytes; |
| 47 return written_bytes; | 47 if (compressor_libarchive->offset_ > compressor_libarchive->length_) |
| 48 } | 48 compressor_libarchive->length_ = compressor_libarchive->offset_; |
| 49 | 49 return static_cast<uLong>(written_bytes); |
| 50 // Nothing to do here because JavaScript takes care of file close operations. | 50 } |
| 51 int CustomArchiveClose(archive* archive_object, void* client_data) { | 51 |
| 52 return ARCHIVE_OK; | 52 // Returns the offset from the beginning of the data. |
| 53 } | 53 long CustomArchiveTell(void* compressor, void* /*stream*/) { |
| 54 } | 54 CompressorArchiveLibarchive* compressor_libarchive = |
| 55 static_cast<CompressorArchiveLibarchive*>(compressor); | |
| 56 return static_cast<long>(compressor_libarchive->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 CompressorArchiveLibarchive* compressor_libarchive = | |
| 65 static_cast<CompressorArchiveLibarchive*>(compressor); | |
| 66 | |
| 67 if (origin == ZLIB_FILEFUNC_SEEK_CUR) { | |
| 68 compressor_libarchive->offset_ = | |
| 69 std::min(compressor_libarchive->offset_ + static_cast<int64_t>(offset), | |
| 70 compressor_libarchive->length_); | |
| 71 return 0 /* Success */; | |
| 72 } | |
| 73 if (origin == ZLIB_FILEFUNC_SEEK_END) { | |
| 74 compressor_libarchive->offset_ = | |
| 75 std::max(compressor_libarchive->length_ - static_cast<int64_t>(offset), | |
| 76 0LL); | |
| 77 return 0 /* Success */; | |
| 78 } | |
| 79 if (origin == ZLIB_FILEFUNC_SEEK_SET) { | |
| 80 compressor_libarchive->offset_ = | |
| 81 std::min(static_cast<int64_t>(offset), compressor_libarchive->length_); | |
| 82 return 0 /* Success */; | |
| 83 } | |
| 84 return -1 /* Error */; | |
|
mtomasz
2017/04/11 06:36:45
nit: We could introduce an enum and avoid these co
| |
| 85 } | |
| 86 | |
| 87 // Releases all used resources. compressor points to compressor_libarchive 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 | |
| 55 | 101 |
| 56 CompressorArchiveLibarchive::CompressorArchiveLibarchive( | 102 CompressorArchiveLibarchive::CompressorArchiveLibarchive( |
| 57 CompressorStream* compressor_stream) | 103 CompressorStream* compressor_stream) |
| 58 : CompressorArchive(compressor_stream), | 104 : CompressorArchive(compressor_stream), |
| 59 compressor_stream_(compressor_stream) { | 105 compressor_stream_(compressor_stream), |
| 106 zip_file_(nullptr), | |
| 107 offset_(0), | |
| 108 length_(0) { | |
| 60 destination_buffer_ = | 109 destination_buffer_ = |
| 61 new char[compressor_archive_constants::kMaximumDataChunkSize]; | 110 new char[compressor_stream_constants::kMaximumDataChunkSize]; |
| 62 } | 111 } |
| 63 | 112 |
| 64 CompressorArchiveLibarchive::~CompressorArchiveLibarchive() { | 113 CompressorArchiveLibarchive::~CompressorArchiveLibarchive() { |
| 65 delete destination_buffer_; | 114 delete destination_buffer_; |
| 66 } | 115 } |
| 67 | 116 |
| 68 void CompressorArchiveLibarchive::CreateArchive() { | 117 bool CompressorArchiveLibarchive::CreateArchive() { |
| 69 archive_ = archive_write_new(); | 118 // Set up archive object. |
| 70 archive_write_set_format_zip(archive_); | 119 zlib_filefunc_def zip_funcs; |
| 71 | 120 zip_funcs.zopen_file = compressor_archive_functions::CustomArchiveOpen; |
| 72 // Passing 1 as the second argument causes the final chunk not to be padded. | 121 zip_funcs.zread_file = compressor_archive_functions::CustomArchiveRead; |
| 73 archive_write_set_bytes_in_last_block(archive_, 1); | 122 zip_funcs.zwrite_file = compressor_archive_functions::CustomArchiveWrite; |
| 74 archive_write_set_bytes_per_block( | 123 zip_funcs.ztell_file = compressor_archive_functions::CustomArchiveTell; |
| 75 archive_, compressor_archive_constants::kMaximumDataChunkSize); | 124 zip_funcs.zseek_file = compressor_archive_functions::CustomArchiveSeek; |
| 76 archive_write_open(archive_, this, CustomArchiveOpen, | 125 zip_funcs.zclose_file = compressor_archive_functions::CustomArchiveClose; |
| 77 CustomArchiveWrite, CustomArchiveClose); | 126 zip_funcs.zerror_file = compressor_archive_functions::CustomArchiveError; |
| 78 } | 127 zip_funcs.opaque = this; |
| 79 | 128 |
| 80 void CompressorArchiveLibarchive::AddToArchive( | 129 zip_file_ = zipOpen2(nullptr /* pathname */, |
| 81 const std::string& filename, | 130 APPEND_STATUS_CREATE, |
| 82 int64_t file_size, | 131 nullptr /* globalcomment */, |
| 83 time_t modification_time, | 132 &zip_funcs); |
| 84 bool is_directory) { | 133 if (!zip_file_) { |
| 85 entry = archive_entry_new(); | 134 set_error_message(compressor_archive_constants::kCreateArchiveError); |
| 86 | 135 return false /* Error */; |
| 87 archive_entry_set_pathname(entry, filename.c_str()); | 136 } |
| 88 archive_entry_set_size(entry, file_size); | 137 return true /* Success */; |
| 89 archive_entry_set_mtime(entry, modification_time, 0 /* millisecond */); | 138 } |
| 90 | 139 |
| 91 if (is_directory) { | 140 bool CompressorArchiveLibarchive::AddToArchive(const std::string& filename, |
| 92 archive_entry_set_filetype(entry, AE_IFDIR); | 141 int64_t file_size, |
| 93 archive_entry_set_perm( | 142 int64_t modification_time, |
| 94 entry, compressor_archive_constants::kDirectoryPermission); | 143 bool is_directory) { |
| 95 } else { | 144 // Minizip takes filenames that end with '/' as directories. |
| 96 archive_entry_set_filetype(entry, AE_IFREG); | 145 std::string normalized_filename = filename; |
| 97 archive_entry_set_perm( | 146 if (is_directory) |
| 98 entry, compressor_archive_constants::kFilePermission); | 147 normalized_filename += "/"; |
| 99 } | 148 |
| 100 archive_write_header(archive_, entry); | 149 // Fill zipfileMetadata with modification_time. |
| 101 // If archive_errno() returns 0, the header was written correctly. | 150 zip_fileinfo zipfileMetadata; |
| 102 if (archive_errno(archive_) != 0) { | 151 // modification_time is millisecond-based, while FromTimeT takes seconds. |
| 103 CloseArchive(true /* hasError */); | 152 base::Time tm = base::Time::FromTimeT((int64_t)modification_time / 1000); |
| 104 return; | 153 base::Time::Exploded exploded_time = {}; |
| 105 } | 154 tm.LocalExplode(&exploded_time); |
| 106 | 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_loc al | |
| 171 0u, // size_extrafield_local | |
| 172 nullptr, // extrafield_global | |
|
mtomasz
2017/04/11 06:36:45
nit: Please fix indentation of comments.
| |
| 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; | |
| 107 if (!is_directory) { | 192 if (!is_directory) { |
| 108 int64_t remaining_size = file_size; | 193 int64_t remaining_size = file_size; |
| 109 while (remaining_size > 0) { | 194 while (remaining_size > 0) { |
| 110 int64_t chunk_size = std::min(remaining_size, | 195 int64_t chunk_size = std::min(remaining_size, |
| 111 compressor_archive_constants::kMaximumDataChunkSize); | 196 compressor_stream_constants::kMaximumDataChunkSize); |
| 112 PP_DCHECK(chunk_size > 0); | 197 PP_DCHECK(chunk_size > 0); |
| 113 | 198 |
| 114 int64_t read_bytes = | 199 int64_t read_bytes = |
| 115 compressor_stream_->Read(chunk_size, destination_buffer_); | 200 compressor_stream_->Read(chunk_size, destination_buffer_); |
| 201 | |
| 116 // Negative read_bytes indicates an error occurred when reading chunks. | 202 // Negative read_bytes indicates an error occurred when reading chunks. |
| 117 if (read_bytes < 0) { | 203 // 0 just means there is no more data available, but here we need positive |
| 118 CloseArchive(true /* hasError */); | 204 // length of bytes, so this is also an error here. |
| 205 if (read_bytes <= 0) { | |
| 206 has_error = true; | |
| 119 break; | 207 break; |
| 120 } | 208 } |
| 121 | 209 |
| 122 int64_t written_bytes = | 210 if (zipWriteInFileInZip(zip_file_, destination_buffer_, read_bytes) != |
| 123 archive_write_data(archive_, destination_buffer_, read_bytes); | 211 ZIP_OK) { |
| 124 // If archive_errno() returns 0, the buffer was written correctly. | 212 has_error = true; |
| 125 if (archive_errno(archive_) != 0) { | |
| 126 CloseArchive(true /* hasError */); | |
| 127 break; | 213 break; |
| 128 } | 214 } |
| 129 PP_DCHECK(written_bytes > 0); | 215 remaining_size -= read_bytes; |
| 130 | |
| 131 remaining_size -= written_bytes; | |
| 132 } | 216 } |
| 133 } | 217 } |
| 134 | 218 |
| 135 archive_entry_free(entry); | 219 if (!has_error && zipCloseFileInZip(zip_file_) != ZIP_OK) |
| 136 } | 220 has_error = true; |
| 137 | 221 |
| 138 void CompressorArchiveLibarchive::CloseArchive(bool has_error) { | 222 if (has_error) { |
| 139 // If has_error is true, mark the archive object as being unusable and | 223 CloseArchive(true /* has_error */); |
| 140 // release resources without writing no more data on the archive. | 224 set_error_message(compressor_archive_constants::kAddToArchiveError); |
| 141 if (has_error) | 225 return false /* Error */; |
| 142 archive_write_fail(archive_); | 226 } |
| 143 if (archive_) { | 227 |
| 144 archive_write_free(archive_); | 228 return true /* Success */; |
| 145 archive_ = NULL; | 229 } |
| 146 } | 230 |
| 147 } | 231 bool CompressorArchiveLibarchive::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 |