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 // Called when minizip tries to open a zip archive file. We do nothing here |
|
mtomasz
2017/04/10 07:15:08
nit: Let's add \n after the namespace line for bet
takise
2017/04/11 06:00:51
Done.
| |
| 15 int CustomArchiveOpen(archive* archive_object, void* client_data) { | 15 // because JavaScript takes care of file opening operation. |
| 16 return ARCHIVE_OK; | 16 void* CustomArchiveOpen(void* opaque, const char* filename, int mode) { |
| 17 } | 17 return opaque; |
| 18 | 18 } |
| 19 // Called when any data chunk must be written on the archive. It copies data | 19 |
| 20 // from the given buffer processed by libarchive to an array buffer and passes | 20 // This function is not called because we don't unpack zip files here. |
| 21 // it to compressor_stream. | 21 uLong CustomArchiveRead(void* opaque, void* /*stream*/, void* buf, uLong size) { |
|
mtomasz
2017/04/10 07:15:08
nit: We usually comment out unused arguments. So l
takise
2017/04/11 06:00:51
Done.
| |
| 22 ssize_t CustomArchiveWrite(archive* archive_object, void* client_data, | 22 return 0 /* Success */; |
| 23 const void* buffer, size_t length) { | 23 } |
| 24 CompressorArchiveLibarchive* compressor_libarchive = | 24 |
| 25 static_cast<CompressorArchiveLibarchive*>(client_data); | 25 // Called when data chunk must be written on the archive. It copies data |
| 26 | 26 // from the given buffer processed by minizip to an array buffer and passes |
| 27 const char* char_buffer = static_cast<const char*>(buffer); | 27 // it to compressor_stream. |
| 28 | 28 uLong CustomArchiveWrite(void* opaque, |
|
mtomasz
2017/04/10 07:15:08
Why is this argument called opaque? Can we rename
takise
2017/04/11 06:00:51
Done.
| |
| 29 // Copy the data in buffer to array_buffer. | 29 void* /*stream*/, |
| 30 PP_DCHECK(length > 0); | 30 const void* zip_buffer, |
| 31 pp::VarArrayBuffer array_buffer(length); | 31 uLong zip_length) { |
| 32 char* array_buffer_data = static_cast<char*>(array_buffer.Map()); | 32 CompressorArchiveLibarchive* compressor_libarchive = |
| 33 memcpy(array_buffer_data, char_buffer, length); | 33 static_cast<CompressorArchiveLibarchive*>(opaque); |
| 34 array_buffer.Unmap(); | 34 |
| 35 | 35 int64_t written_bytes = compressor_libarchive->compressor_stream()->Write( |
| 36 ssize_t written_bytes = | 36 compressor_libarchive->offset_, zip_length, zip_buffer); |
| 37 compressor_libarchive->compressor_stream()->Write(length, array_buffer); | 37 |
| 38 | 38 if (written_bytes != zip_length) |
| 39 // Negative written_bytes represents an error. | 39 return 0 /* Error */; |
| 40 if (written_bytes < 0) { | 40 |
| 41 // When writing fails, archive_set_error() should be called and -1 should | 41 // Update offset_ and length_. |
| 42 // be returned. | 42 compressor_libarchive->offset_ += written_bytes; |
| 43 archive_set_error( | 43 if (compressor_libarchive->offset_ > compressor_libarchive->length_) |
| 44 compressor_libarchive->archive(), EIO, "Failed to write a chunk."); | 44 compressor_libarchive->length_ = compressor_libarchive->offset_; |
| 45 return -1; | 45 return static_cast<uLong>(written_bytes); |
| 46 } | 46 } |
| 47 return written_bytes; | 47 |
| 48 } | 48 // Returns the offset from the beginning of the data. |
| 49 | 49 long CustomArchiveTell(void* opaque, void* /*stream*/) { |
| 50 // Nothing to do here because JavaScript takes care of file close operations. | 50 CompressorArchiveLibarchive* compressor_libarchive = |
|
mtomasz
2017/04/10 07:15:08
Can we rename libarchive from variable/class/metho
| |
| 51 int CustomArchiveClose(archive* archive_object, void* client_data) { | 51 static_cast<CompressorArchiveLibarchive*>(opaque); |
| 52 return ARCHIVE_OK; | 52 return static_cast<long>(compressor_libarchive->offset_); |
| 53 } | 53 } |
| 54 } | 54 |
| 55 // Moves the current offset to the specified position. | |
| 56 long CustomArchiveSeek(void* opaque, | |
| 57 void* /*stream*/, | |
| 58 uLong offset, | |
| 59 int origin) { | |
| 60 CompressorArchiveLibarchive* compressor_libarchive = | |
| 61 static_cast<CompressorArchiveLibarchive*>(opaque); | |
| 62 | |
| 63 if (origin == ZLIB_FILEFUNC_SEEK_CUR) { | |
| 64 compressor_libarchive->offset_ = | |
| 65 std::min(compressor_libarchive->offset_ + static_cast<int64_t>(offset), | |
| 66 compressor_libarchive->length_); | |
| 67 return 0 /* Success */; | |
| 68 } | |
| 69 if (origin == ZLIB_FILEFUNC_SEEK_END) { | |
| 70 compressor_libarchive->offset_ = | |
| 71 std::max(compressor_libarchive->length_ - static_cast<int64_t>(offset), | |
| 72 0); | |
| 73 return 0 /* Success */; | |
| 74 } | |
| 75 if (origin == ZLIB_FILEFUNC_SEEK_SET) { | |
| 76 compressor_libarchive->offset_ = | |
| 77 std::min(static_cast<int64_t>(offset), compressor_libarchive->length_); | |
| 78 return 0 /* Success */; | |
| 79 } | |
| 80 return -1 /* Error */; | |
| 81 } | |
| 82 | |
| 83 // Releases all used resources. opaque points to compressor_libarchive and | |
| 84 // it is deleted elsewhere, so we don't need to delete it here. | |
|
mtomasz
2017/04/10 07:15:08
nit: elsewhere -> ...
Can we write a method name i
takise
2017/04/11 06:00:51
Done.
| |
| 85 int CustomArchiveClose(void* opaque, void* /*stream*/) { | |
| 86 return 0 /* Success */; | |
| 87 } | |
| 88 | |
| 89 // Returns the last error that happened when writing data. This function always | |
| 90 // returns zero, which means there are no errors. | |
| 91 int CustomArchiveError(void* /*opaque*/, void* /*stream*/) { | |
| 92 return 0 /* Success */; | |
| 93 } | |
| 94 | |
| 95 } // compressor_archive_functions | |
| 55 | 96 |
| 56 CompressorArchiveLibarchive::CompressorArchiveLibarchive( | 97 CompressorArchiveLibarchive::CompressorArchiveLibarchive( |
| 57 CompressorStream* compressor_stream) | 98 CompressorStream* compressor_stream) |
| 58 : CompressorArchive(compressor_stream), | 99 : CompressorArchive(compressor_stream), |
| 59 compressor_stream_(compressor_stream) { | 100 compressor_stream_(compressor_stream), |
| 101 zip_file_(NULL), | |
| 102 offset_(0), | |
| 103 length_(0) { | |
| 60 destination_buffer_ = | 104 destination_buffer_ = |
| 61 new char[compressor_archive_constants::kMaximumDataChunkSize]; | 105 new char[compressor_stream_constants::kMaximumDataChunkSize]; |
| 62 } | 106 } |
| 63 | 107 |
| 64 CompressorArchiveLibarchive::~CompressorArchiveLibarchive() { | 108 CompressorArchiveLibarchive::~CompressorArchiveLibarchive() { |
| 65 delete destination_buffer_; | 109 delete destination_buffer_; |
| 66 } | 110 } |
| 67 | 111 |
| 68 void CompressorArchiveLibarchive::CreateArchive() { | 112 bool CompressorArchiveLibarchive::CreateArchive() { |
| 69 archive_ = archive_write_new(); | 113 // Set up archive object. |
| 70 archive_write_set_format_zip(archive_); | 114 zlib_filefunc_def zip_funcs; |
| 71 | 115 zip_funcs.zopen_file = compressor_archive_functions::CustomArchiveOpen; |
| 72 // Passing 1 as the second argument causes the final chunk not to be padded. | 116 zip_funcs.zread_file = compressor_archive_functions::CustomArchiveRead; |
| 73 archive_write_set_bytes_in_last_block(archive_, 1); | 117 zip_funcs.zwrite_file = compressor_archive_functions::CustomArchiveWrite; |
| 74 archive_write_set_bytes_per_block( | 118 zip_funcs.ztell_file = compressor_archive_functions::CustomArchiveTell; |
| 75 archive_, compressor_archive_constants::kMaximumDataChunkSize); | 119 zip_funcs.zseek_file = compressor_archive_functions::CustomArchiveSeek; |
| 76 archive_write_open(archive_, this, CustomArchiveOpen, | 120 zip_funcs.zclose_file = compressor_archive_functions::CustomArchiveClose; |
| 77 CustomArchiveWrite, CustomArchiveClose); | 121 zip_funcs.zerror_file = compressor_archive_functions::CustomArchiveError; |
| 78 } | 122 zip_funcs.opaque = this; |
| 79 | 123 |
| 80 void CompressorArchiveLibarchive::AddToArchive( | 124 zip_file_ = zipOpen2(NULL /* pathname */, |
|
mtomasz
2017/04/10 07:15:08
NULL -> nullptr here and in other places
takise
2017/04/11 06:00:51
Done.
| |
| 81 const std::string& filename, | 125 APPEND_STATUS_CREATE, |
| 82 int64_t file_size, | 126 NULL /* globalcomment */, |
| 83 time_t modification_time, | 127 &zip_funcs); |
| 84 bool is_directory) { | 128 if (!zip_file_) { |
| 85 entry = archive_entry_new(); | 129 set_error_message(compressor_archive_constants::kCreateArchiveError); |
| 86 | 130 return false /* Error */; |
| 87 archive_entry_set_pathname(entry, filename.c_str()); | 131 } |
| 88 archive_entry_set_size(entry, file_size); | 132 return true /* Success */; |
| 89 archive_entry_set_mtime(entry, modification_time, 0 /* millisecond */); | 133 } |
| 90 | 134 |
| 91 if (is_directory) { | 135 bool CompressorArchiveLibarchive::AddToArchive(const std::string& _filename, |
|
mtomasz
2017/04/10 07:15:08
nit: We never prefix with _. Can we keep filename
takise
2017/04/11 06:00:51
Done.
| |
| 92 archive_entry_set_filetype(entry, AE_IFDIR); | 136 int64_t file_size, |
| 93 archive_entry_set_perm( | 137 int64_t modification_time, |
| 94 entry, compressor_archive_constants::kDirectoryPermission); | 138 bool is_directory) { |
| 95 } else { | 139 // Minizip takes filenames that end with '/' as directories. |
| 96 archive_entry_set_filetype(entry, AE_IFREG); | 140 std::string filename = _filename; |
| 97 archive_entry_set_perm( | 141 if (is_directory) |
| 98 entry, compressor_archive_constants::kFilePermission); | 142 filename += "/"; |
|
mtomasz
2017/04/10 07:15:08
Does it work with Windows paths? If not, did it wo
| |
| 99 } | 143 |
| 100 archive_write_header(archive_, entry); | 144 // Fill zipfileMetadata with modification_time. |
| 101 // If archive_errno() returns 0, the header was written correctly. | 145 zip_fileinfo zipfileMetadata; |
| 102 if (archive_errno(archive_) != 0) { | 146 // modification_time is millisecond-based, while FromTimeT takes seconds. |
| 103 CloseArchive(true /* hasError */); | 147 base::Time tm = base::Time::FromTimeT((int64_t)modification_time / 1000); |
| 104 return; | 148 base::Time::Exploded exploded_time = {}; |
| 105 } | 149 tm.LocalExplode(&exploded_time); |
| 106 | 150 zipfileMetadata.tmz_date.tm_sec = exploded_time.second; |
| 151 zipfileMetadata.tmz_date.tm_min = exploded_time.minute; | |
| 152 zipfileMetadata.tmz_date.tm_hour = exploded_time.hour; | |
| 153 zipfileMetadata.tmz_date.tm_year = exploded_time.year; | |
| 154 zipfileMetadata.tmz_date.tm_mday = exploded_time.day_of_month; | |
| 155 // Convert from 1-based to 0-based. | |
| 156 zipfileMetadata.tmz_date.tm_mon = exploded_time.month - 1; | |
| 157 | |
| 158 // Section 4.4.4 http://www.pkware.com/documents/casestudies/APPNOTE.TXT | |
| 159 // Setting the Language encoding flag so the file is told to be in utf-8. | |
| 160 const uLong LANGUAGE_ENCODING_FLAG = 0x1 << 11; | |
| 161 | |
| 162 int open_result = zipOpenNewFileInZip4(zip_file_, // file | |
| 163 filename.c_str(), // filename | |
| 164 &zipfileMetadata, // zipfi | |
| 165 NULL, // extrafield_local | |
| 166 0u, // size_extrafield_local | |
| 167 NULL, // extrafield_global | |
| 168 0u, // size_extrafield_global | |
| 169 NULL, // comment | |
| 170 Z_DEFLATED, // method | |
| 171 Z_DEFAULT_COMPRESSION, // level | |
| 172 0, // raw | |
| 173 -MAX_WBITS, // windowBits | |
| 174 DEF_MEM_LEVEL, // memLevel | |
| 175 Z_DEFAULT_STRATEGY, // strategy | |
| 176 NULL, // password | |
| 177 0, // crcForCrypting | |
| 178 0, // versionMadeBy | |
| 179 LANGUAGE_ENCODING_FLAG); // flagBase | |
| 180 if (open_result != ZIP_OK) { | |
| 181 CloseArchive(true /* has_error */); | |
| 182 set_error_message(compressor_archive_constants::kAddtoArchiveError); | |
| 183 return false /* Error */; | |
| 184 } | |
| 185 | |
| 186 bool has_error = false; | |
| 107 if (!is_directory) { | 187 if (!is_directory) { |
| 108 int64_t remaining_size = file_size; | 188 int64_t remaining_size = file_size; |
| 109 while (remaining_size > 0) { | 189 while (remaining_size > 0) { |
| 110 int64_t chunk_size = std::min(remaining_size, | 190 int64_t chunk_size = std::min(remaining_size, |
| 111 compressor_archive_constants::kMaximumDataChunkSize); | 191 compressor_stream_constants::kMaximumDataChunkSize); |
| 112 PP_DCHECK(chunk_size > 0); | 192 PP_DCHECK(chunk_size > 0); |
| 113 | 193 |
| 114 int64_t read_bytes = | 194 int64_t read_bytes = |
| 115 compressor_stream_->Read(chunk_size, destination_buffer_); | 195 compressor_stream_->Read(chunk_size, destination_buffer_); |
| 196 | |
| 116 // Negative read_bytes indicates an error occurred when reading chunks. | 197 // Negative read_bytes indicates an error occurred when reading chunks. |
| 117 if (read_bytes < 0) { | 198 // 0 just means there is no more data available, but here we need positive |
| 118 CloseArchive(true /* hasError */); | 199 // length of bytes, so this is also an error here. |
| 200 if (read_bytes <= 0) { | |
| 201 has_error = true; | |
| 119 break; | 202 break; |
| 120 } | 203 } |
| 121 | 204 |
| 122 int64_t written_bytes = | 205 if (zipWriteInFileInZip(zip_file_, destination_buffer_, read_bytes) != |
| 123 archive_write_data(archive_, destination_buffer_, read_bytes); | 206 ZIP_OK) { |
| 124 // If archive_errno() returns 0, the buffer was written correctly. | 207 has_error = true; |
| 125 if (archive_errno(archive_) != 0) { | |
| 126 CloseArchive(true /* hasError */); | |
| 127 break; | 208 break; |
| 128 } | 209 } |
| 129 PP_DCHECK(written_bytes > 0); | 210 remaining_size -= read_bytes; |
| 130 | |
| 131 remaining_size -= written_bytes; | |
| 132 } | 211 } |
| 133 } | 212 } |
| 134 | 213 |
| 135 archive_entry_free(entry); | 214 if (!has_error && zipCloseFileInZip(zip_file_) != ZIP_OK) |
| 136 } | 215 has_error = true; |
| 137 | 216 |
| 138 void CompressorArchiveLibarchive::CloseArchive(bool has_error) { | 217 if (has_error) { |
| 139 // If has_error is true, mark the archive object as being unusable and | 218 CloseArchive(true /* has_error */); |
| 140 // release resources without writing no more data on the archive. | 219 set_error_message(compressor_archive_constants::kAddtoArchiveError); |
| 141 if (has_error) | 220 return false /* Error */; |
| 142 archive_write_fail(archive_); | 221 } |
| 143 if (archive_) { | 222 |
| 144 archive_write_free(archive_); | 223 return true /* Success */; |
| 145 archive_ = NULL; | 224 } |
| 146 } | 225 |
| 147 } | 226 bool CompressorArchiveLibarchive::CloseArchive(bool has_error) { |
| 227 if (zipClose(zip_file_, NULL /* global_comment */) != ZIP_OK) { | |
| 228 set_error_message(compressor_archive_constants::kCloseArchiveError); | |
| 229 return false /* Error */; | |
| 230 } | |
| 231 if (!has_error) { | |
| 232 if (compressor_stream()->Flush() < 0) { | |
| 233 set_error_message(compressor_archive_constants::kCloseArchiveError); | |
| 234 return false /* Error */; | |
| 235 } | |
| 236 } | |
| 237 return true /* Success */; | |
| 238 } | |
| OLD | NEW |