Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium OS Authors. All rights reserved. | 1 // Copyright 2014 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 "volume_archive_libarchive.h" | 5 #include "volume_archive_libarchive.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <cerrno> | 8 #include <cerrno> |
| 9 #include <cstring> | |
| 9 #include <limits> | 10 #include <limits> |
| 10 | 11 #include <time.h> |
| 11 #include "archive_entry.h" | 12 |
| 13 #include "base/strings/string_util.h" | |
| 14 #include "base/time/time.h" | |
| 12 #include "ppapi/cpp/logging.h" | 15 #include "ppapi/cpp/logging.h" |
| 13 | 16 |
| 14 namespace { | 17 namespace volume_archive_functions { |
| 15 | 18 void* CustomArchiveOpen(void* opaque, |
| 16 const int64_t kArchiveReadDataError = -1; // Negative value means error. | 19 const char* /* filename */, |
| 17 | 20 int /* mode */) { |
| 18 std::string ArchiveError(const std::string& message, archive* archive_object) { | 21 return opaque; |
| 19 return message + archive_error_string(archive_object); | 22 } |
| 20 } | 23 |
| 21 | 24 int64_t DynamicCache(VolumeArchiveLibarchive* va, int64_t unz_size) { |
|
mtomasz
2017/04/10 07:15:09
nit: Can we chose a better name? What does this me
mtomasz
2017/04/10 07:15:09
nit: What is unz in unz_size? Can we find a better
takise
2017/04/11 06:00:52
As mentioned in volume_archive_libarchive.h, this
| |
| 22 // Sets the libarchive internal error to a VolumeReader related error. | 25 int64_t offset = va->reader()->offset(); |
| 23 // archive_error_string function must work on valid strings, but in case of | 26 if (va->reader()->Seek(static_cast<int64_t>(offset), |
| 24 // errors in the custom functions, libarchive API assumes the error is set by | 27 ZLIB_FILEFUNC_SEEK_SET) < 0) { |
| 25 // us. If we don't set it, we will get a Segmentation Fault because | 28 return -1 /* Error */; |
| 26 // archive_error_string will work on invalid memory. | 29 } |
| 27 void SetLibarchiveErrorToVolumeReaderError(archive* archive_object) { | 30 |
| 28 archive_set_error(archive_object, | 31 int64_t bytes_to_read = |
| 29 EIO /* I/O error. */, | 32 std::min(volume_archive_constants::kMaximumDataChunkSize, |
| 30 "%s" /* Format string similar to printf. */, | 33 va->reader()->archive_size() - offset); |
| 31 volume_archive_constants::kVolumeReaderError); | 34 PP_DCHECK(bytes_to_read > 0); |
| 32 } | 35 int64_t left_length = bytes_to_read; |
| 33 | 36 char* buffer_pointer = va->dynamic_cache_; |
| 34 ssize_t CustomArchiveRead(archive* archive_object, | 37 const void* destination_buffer; |
| 35 void* client_data, | 38 |
| 36 const void** buffer) { | 39 do { |
| 40 int64_t read_bytes = va->reader()->Read(left_length, &destination_buffer); | |
| 41 // End of the zip file. | |
| 42 if (read_bytes == 0) | |
| 43 break; | |
| 44 if (read_bytes < 0) | |
| 45 return -1 /* Error */; | |
| 46 memcpy(buffer_pointer, destination_buffer, read_bytes); | |
| 47 left_length -= read_bytes; | |
| 48 buffer_pointer += read_bytes; | |
| 49 } while (left_length > 0); | |
| 50 | |
| 51 if (va->reader()->Seek(static_cast<int64_t>(offset), | |
| 52 ZLIB_FILEFUNC_SEEK_SET) < 0) { | |
| 53 return -1 /* Error */; | |
| 54 } | |
| 55 va->dynamic_cache_size_ = bytes_to_read - left_length; | |
| 56 va->dynamic_cache_offset_ = offset; | |
| 57 | |
| 58 return unz_size - left_length; | |
| 59 } | |
| 60 | |
| 61 uLong CustomArchiveRead(void* opaque, | |
| 62 void* /* stream */, | |
| 63 void* buffer, | |
| 64 uLong size) { | |
| 65 VolumeArchiveLibarchive* va = static_cast<VolumeArchiveLibarchive*>(opaque); | |
|
mtomasz
2017/04/10 07:15:09
nit: Our style guide forbids variable name abbrevi
takise
2017/04/11 06:00:51
Done.
| |
| 66 int64_t offset = va->reader()->offset(); | |
| 67 | |
| 68 // When minizip requests a chunk in static_cache_. | |
| 69 if (offset >= va->static_cache_offset_) { | |
| 70 // Relative offset in the central directory. | |
|
mtomasz
2017/04/10 07:15:09
This may only work for small archives. Please chec
takise
2017/04/11 06:00:51
Yes, this is only work for small archives, but the
| |
| 71 int64_t offset_in_cache = offset - va->static_cache_offset_; | |
| 72 memcpy(buffer, va->static_cache_ + offset_in_cache, size); | |
| 73 if (va->reader()->Seek(static_cast<int64_t>(size), | |
| 74 ZLIB_FILEFUNC_SEEK_CUR) < 0) { | |
| 75 return -1 /* Error */; | |
| 76 } | |
| 77 return size; | |
| 78 } | |
| 79 | |
| 80 char* unz_buffer_pointer = static_cast<char*>(buffer); | |
|
mtomasz
2017/04/10 07:15:09
nit: unz -> ...
While existing method naming eg.
takise
2017/04/11 06:00:51
Done.
| |
| 81 int64_t left_length = static_cast<int64_t>(size); | |
| 82 | |
| 83 do { | |
| 84 offset = va->reader()->offset(); | |
| 85 // If dynamic_cache_ is empty or it cannot be reused, update the cache so | |
| 86 // that it contains the chunk required by minizip. | |
| 87 if (va->dynamic_cache_size_ == 0 || offset < va->dynamic_cache_offset_ || | |
| 88 va->dynamic_cache_offset_ + va->dynamic_cache_size_ < offset + size) { | |
| 89 volume_archive_functions::DynamicCache(va, size); | |
| 90 } | |
| 91 | |
| 92 // Just copy the required data from the cache. | |
| 93 int64_t offset_in_cache = offset - va->dynamic_cache_offset_; | |
| 94 int64_t copy_length = | |
| 95 std::min(left_length, va->dynamic_cache_size_ - offset_in_cache); | |
| 96 memcpy(unz_buffer_pointer, va->dynamic_cache_ + offset_in_cache, | |
| 97 copy_length); | |
| 98 unz_buffer_pointer += copy_length; | |
| 99 left_length -= copy_length; | |
| 100 if (va->reader()->Seek(static_cast<int64_t>(copy_length), | |
| 101 ZLIB_FILEFUNC_SEEK_CUR) < 0) { | |
| 102 return -1 /* Error */; | |
| 103 } | |
| 104 } while (left_length > 0); | |
| 105 | |
| 106 return size; | |
| 107 } | |
| 108 | |
| 109 uLong CustomArchiveWrite(void* opaque, | |
| 110 void* /*stream*/, | |
| 111 const void* buffer, | |
| 112 uLong length) { | |
| 113 return 0 /* Success */; | |
| 114 } | |
| 115 | |
| 116 long CustomArchiveTell(void* opaque, void* /*stream*/) { | |
| 37 VolumeArchiveLibarchive* volume_archive = | 117 VolumeArchiveLibarchive* volume_archive = |
| 38 static_cast<VolumeArchiveLibarchive*>(client_data); | 118 static_cast<VolumeArchiveLibarchive*>(opaque); |
| 39 | 119 return static_cast<long>(volume_archive->reader()->offset()); |
| 40 int64_t read_bytes = volume_archive->reader()->Read( | 120 } |
| 41 volume_archive->reader_data_size(), buffer); | 121 |
| 42 if (read_bytes == ARCHIVE_FATAL) | 122 long CustomArchiveSeek(void* opaque, |
| 43 SetLibarchiveErrorToVolumeReaderError(archive_object); | 123 void* /*stream*/, |
| 44 return read_bytes; | 124 uLong offset, |
| 45 } | 125 int origin) { |
| 46 | |
| 47 int64_t CustomArchiveSkip(archive* archive_object, | |
| 48 void* client_data, | |
| 49 int64_t request) { | |
| 50 VolumeArchiveLibarchive* volume_archive = | 126 VolumeArchiveLibarchive* volume_archive = |
| 51 static_cast<VolumeArchiveLibarchive*>(client_data); | 127 static_cast<VolumeArchiveLibarchive*>(opaque); |
| 52 // VolumeReader::Skip returns 0 in case of failure and CustomArchiveRead is | 128 |
| 53 // used instead, so there is no need to check for VolumeReader error. | 129 long return_value = static_cast<long>(volume_archive->reader()->Seek( |
| 54 return volume_archive->reader()->Skip(request); | 130 static_cast<int64_t>(offset), static_cast<int64_t>(origin))); |
| 55 } | 131 if (return_value >= 0) |
| 56 | 132 return 0 /* Success */; |
| 57 int64_t CustomArchiveSeek(archive* archive_object, | 133 return -1 /* Error */; |
| 58 void* client_data, | 134 } |
| 59 int64_t offset, | 135 |
| 60 int whence) { | 136 int CustomArchiveClose(void* opaque, void* /*stream*/) { |
| 61 VolumeArchiveLibarchive* volume_archive = | 137 return 0; |
| 62 static_cast<VolumeArchiveLibarchive*>(client_data); | 138 } |
| 63 | 139 |
| 64 int64_t new_offset = volume_archive->reader()->Seek(offset, whence); | 140 int CustomArchiveError(void* /*opaque*/, void* /*stream*/) { |
| 65 if (new_offset == ARCHIVE_FATAL) | 141 return 0; |
| 66 SetLibarchiveErrorToVolumeReaderError(archive_object); | 142 } |
| 67 | 143 |
| 68 return new_offset; | 144 const char* GetPassphrase(VolumeArchiveLibarchive* volume_archive) { |
| 69 } | 145 const char* password = volume_archive->reader()->Passphrase(); |
| 70 | 146 return password; |
| 71 int CustomArchiveClose(archive* archive_object, void* client_data) { | 147 } |
| 72 return ARCHIVE_OK; | 148 |
| 73 } | 149 } // volume_archive_functions |
| 74 | |
| 75 const char* CustomArchivePassphrase( | |
| 76 archive* archive_object, void* client_data) { | |
| 77 VolumeArchiveLibarchive* volume_archive = | |
| 78 static_cast<VolumeArchiveLibarchive*>(client_data); | |
| 79 | |
| 80 return volume_archive->reader()->Passphrase(); | |
| 81 } | |
| 82 | |
| 83 } // namespace | |
| 84 | 150 |
| 85 VolumeArchiveLibarchive::VolumeArchiveLibarchive(VolumeReader* reader) | 151 VolumeArchiveLibarchive::VolumeArchiveLibarchive(VolumeReader* reader) |
| 86 : VolumeArchive(reader), | 152 : VolumeArchive(reader), |
| 87 reader_data_size_(volume_archive_constants::kMinimumDataChunkSize), | 153 reader_data_size_(volume_archive_constants::kMinimumDataChunkSize), |
| 88 archive_(NULL), | 154 zip_file_(NULL), |
| 89 current_archive_entry_(NULL), | 155 dynamic_cache_offset_(0), |
| 156 dynamic_cache_size_(0), | |
| 157 static_cache_offset_(0), | |
| 158 static_cache_size_(0), | |
| 90 last_read_data_offset_(0), | 159 last_read_data_offset_(0), |
| 91 last_read_data_length_(0), | 160 last_read_data_length_(0), |
| 92 decompressed_data_(NULL), | 161 decompressed_data_(NULL), |
| 93 decompressed_data_size_(0), | 162 decompressed_data_size_(0), |
| 94 decompressed_error_(false) { | 163 decompressed_error_(false) {} |
| 95 } | |
| 96 | 164 |
| 97 VolumeArchiveLibarchive::~VolumeArchiveLibarchive() { | 165 VolumeArchiveLibarchive::~VolumeArchiveLibarchive() { |
| 98 Cleanup(); | 166 Cleanup(); |
| 99 } | 167 } |
| 100 | 168 |
| 101 bool VolumeArchiveLibarchive::Init(const std::string& encoding) { | 169 bool VolumeArchiveLibarchive::Init(const std::string& encoding) { |
| 102 archive_ = archive_read_new(); | 170 // Set up minizip obejct. |
|
mtomasz
2017/04/10 07:15:09
typo: object
takise
2017/04/11 06:00:51
Done.
| |
| 103 if (!archive_) { | 171 zlib_filefunc_def zip_funcs; |
| 104 set_error_message(volume_archive_constants::kArchiveReadNewError); | 172 zip_funcs.zopen_file = volume_archive_functions::CustomArchiveOpen; |
| 173 zip_funcs.zread_file = volume_archive_functions::CustomArchiveRead; | |
| 174 zip_funcs.zwrite_file = volume_archive_functions::CustomArchiveWrite; | |
| 175 zip_funcs.ztell_file = volume_archive_functions::CustomArchiveTell; | |
| 176 zip_funcs.zseek_file = volume_archive_functions::CustomArchiveSeek; | |
| 177 zip_funcs.zclose_file = volume_archive_functions::CustomArchiveClose; | |
| 178 zip_funcs.zerror_file = volume_archive_functions::CustomArchiveError; | |
| 179 zip_funcs.opaque = static_cast<void*>(this); | |
| 180 | |
| 181 // Load muximum static_cache_size_ bytes from the end of the archive to | |
|
mtomasz
2017/04/10 07:15:09
typo: maximum
takise
2017/04/11 06:00:52
Done.
| |
| 182 // static_cache_. | |
| 183 static_cache_size_ = | |
| 184 std::min(volume_archive_constants::kStaticCacheSize, | |
| 185 reader()->archive_size()); | |
| 186 int64_t previous_offset = reader()->offset(); | |
| 187 char* buffer_pointer = static_cache_; | |
| 188 int64_t left_length = static_cache_size_; | |
| 189 static_cache_offset_ = | |
| 190 std::max(reader()->archive_size() - static_cache_size_, 0); | |
| 191 if (reader()->Seek(static_cache_offset_, ZLIB_FILEFUNC_SEEK_SET) < 0) { | |
| 192 set_error_message(volume_archive_constants::kArchiveOpenError); | |
| 193 return false /* Error */; | |
| 194 } | |
| 195 do { | |
| 196 const void* destination_buffer; | |
| 197 int64_t read_bytes = reader()->Read(left_length, &destination_buffer); | |
| 198 memcpy(buffer_pointer, destination_buffer, read_bytes); | |
| 199 left_length -= read_bytes; | |
| 200 buffer_pointer += read_bytes; | |
| 201 } while (left_length > 0); | |
| 202 | |
| 203 // Set the offset to the original position. | |
| 204 if (reader()->Seek(previous_offset, ZLIB_FILEFUNC_SEEK_SET) < 0) { | |
| 205 set_error_message(volume_archive_constants::kArchiveOpenError); | |
| 206 return false /* Error */; | |
| 207 } | |
| 208 | |
| 209 zip_file_ = unzOpen2(NULL /* filename */, &zip_funcs); | |
| 210 if (!zip_file_) { | |
| 211 set_error_message(volume_archive_constants::kArchiveOpenError); | |
| 105 return false; | 212 return false; |
| 106 } | 213 } |
| 107 | 214 |
| 108 // TODO(cmihail): Once the bug mentioned at | |
| 109 // https://github.com/libarchive/libarchive/issues/373 is resolved | |
| 110 // add RAR file handler to manifest.json. | |
| 111 if (archive_read_support_format_rar(archive_) != ARCHIVE_OK || | |
| 112 archive_read_support_format_zip_seekable(archive_) != ARCHIVE_OK) { | |
| 113 set_error_message(ArchiveError( | |
| 114 volume_archive_constants::kArchiveSupportErrorPrefix, archive_)); | |
| 115 return false; | |
| 116 } | |
| 117 | |
| 118 // Default encoding for file names in headers. Note, that another one may be | |
| 119 // used if specified in the archive. | |
| 120 std::string options = std::string("hdrcharset=") + encoding; | |
| 121 if (!encoding.empty() && | |
| 122 archive_read_set_options(archive_, options.c_str()) != ARCHIVE_OK) { | |
| 123 set_error_message(ArchiveError( | |
| 124 volume_archive_constants::kArchiveSupportErrorPrefix, archive_)); | |
| 125 return false; | |
| 126 } | |
| 127 | |
| 128 // Set callbacks for processing the archive's data and open the archive. | |
| 129 // The callback data is the VolumeArchive itself. | |
| 130 int ok = ARCHIVE_OK; | |
| 131 if (archive_read_set_read_callback(archive_, CustomArchiveRead) != ok || | |
| 132 archive_read_set_skip_callback(archive_, CustomArchiveSkip) != ok || | |
| 133 archive_read_set_seek_callback(archive_, CustomArchiveSeek) != ok || | |
| 134 archive_read_set_close_callback(archive_, CustomArchiveClose) != ok || | |
| 135 archive_read_set_passphrase_callback( | |
| 136 archive_, this, CustomArchivePassphrase) != ok || | |
| 137 archive_read_set_callback_data(archive_, this) != ok || | |
| 138 archive_read_open1(archive_) != ok) { | |
| 139 set_error_message(ArchiveError( | |
| 140 volume_archive_constants::kArchiveOpenErrorPrefix, archive_)); | |
| 141 return false; | |
| 142 } | |
| 143 | |
| 144 return true; | 215 return true; |
| 145 } | 216 } |
| 146 | 217 |
| 147 VolumeArchive::Result VolumeArchiveLibarchive::GetNextHeader() { | 218 VolumeArchive::Result VolumeArchiveLibarchive::GetCurrentFileInfo( |
| 219 std::string* pathname, | |
| 220 int64_t* size, | |
| 221 bool* is_directory, | |
| 222 time_t* modification_time) { | |
| 223 | |
| 148 // Headers are being read from the central directory (in the ZIP format), so | 224 // Headers are being read from the central directory (in the ZIP format), so |
| 149 // use a large block size to save on IPC calls. The headers in EOCD are | 225 // use a large block size to save on IPC calls. The headers in EOCD are |
| 150 // grouped one by one. | 226 // grouped one by one. |
| 151 reader_data_size_ = volume_archive_constants::kMaximumDataChunkSize; | 227 reader_data_size_ = volume_archive_constants::kMaximumDataChunkSize; |
| 152 | 228 |
| 153 // Reset to 0 for new VolumeArchive::ReadData operation. | 229 // Reset to 0 for new VolumeArchive::ReadData operation. |
| 154 last_read_data_offset_ = 0; | 230 last_read_data_offset_ = 0; |
| 155 decompressed_data_size_ = 0; | 231 decompressed_data_size_ = 0; |
| 156 | 232 |
| 157 // Archive data is skipped automatically by next call to | 233 unz_file_pos position = {}; |
| 158 // archive_read_next_header. | 234 if (unzGetFilePos(zip_file_, &position) != UNZ_OK) { |
| 159 switch (archive_read_next_header(archive_, ¤t_archive_entry_)) { | 235 set_error_message(volume_archive_constants::kArchiveNextHeaderError); |
| 160 case ARCHIVE_EOF: | 236 return VolumeArchive::RESULT_FAIL; |
| 161 return RESULT_EOF; | |
| 162 case ARCHIVE_OK: | |
| 163 return RESULT_SUCCESS; | |
| 164 default: | |
| 165 set_error_message(ArchiveError( | |
| 166 volume_archive_constants::kArchiveNextHeaderErrorPrefix, archive_)); | |
| 167 return RESULT_FAIL; | |
| 168 } | 237 } |
| 238 | |
| 239 // OpenCurrentEntryInZip | |
|
mtomasz
2017/04/10 07:15:09
Is the comment correct?
takise
2017/04/11 06:00:52
Done.
| |
| 240 unz_file_info raw_file_info = {}; | |
| 241 char raw_file_name_in_zip[volume_archive_constants::kZipMaxPath] = {}; | |
| 242 const int result = unzGetCurrentFileInfo(zip_file_, | |
| 243 &raw_file_info, | |
| 244 raw_file_name_in_zip, | |
| 245 sizeof(raw_file_name_in_zip) - 1, | |
| 246 NULL, // extraField. | |
| 247 0, // extraFieldBufferSize. | |
| 248 NULL, // szComment. | |
| 249 0); // commentBufferSize. | |
| 250 | |
| 251 if (result != UNZ_OK || raw_file_name_in_zip[0] == '\0') { | |
| 252 set_error_message(volume_archive_constants::kArchiveNextHeaderError); | |
| 253 return VolumeArchive::RESULT_FAIL; | |
| 254 } | |
| 255 | |
| 256 *pathname = std::string(raw_file_name_in_zip); | |
| 257 *size = raw_file_info.uncompressed_size; | |
| 258 // Directory entries in zip files end with "/". | |
| 259 *is_directory = base::EndsWith(raw_file_name_in_zip, "/", | |
| 260 base::CompareCase::INSENSITIVE_ASCII); | |
| 261 | |
| 262 // Construct the last modified time. The timezone info is not present in | |
| 263 // zip files. By default, the time is set as local time in zip format. | |
| 264 base::Time::Exploded exploded_time = {}; // Zero-clear. | |
| 265 exploded_time.year = raw_file_info.tmu_date.tm_year; | |
| 266 // The month in zip file is 0-based, whereas ours is 1-based. | |
| 267 exploded_time.month = raw_file_info.tmu_date.tm_mon + 1; | |
| 268 exploded_time.day_of_month = raw_file_info.tmu_date.tm_mday; | |
| 269 exploded_time.hour = raw_file_info.tmu_date.tm_hour; | |
| 270 exploded_time.minute = raw_file_info.tmu_date.tm_min; | |
| 271 exploded_time.second = raw_file_info.tmu_date.tm_sec; | |
| 272 exploded_time.millisecond = 0; | |
| 273 | |
| 274 base::Time local_time; | |
| 275 // If the modification time is not available, we set the value to the current | |
| 276 // local time. | |
| 277 if (!base::Time::FromLocalExploded(exploded_time, &local_time)) | |
| 278 local_time = base::Time::UnixEpoch(); | |
| 279 *modification_time = local_time.ToTimeT(); | |
| 280 | |
| 281 return VolumeArchive::RESULT_SUCCESS; | |
| 169 } | 282 } |
| 170 | 283 |
| 171 VolumeArchive::Result VolumeArchiveLibarchive::GetNextHeader( | 284 VolumeArchive::Result VolumeArchiveLibarchive::GoToNextFile() { |
| 172 const char** pathname, | 285 int return_value = unzGoToNextFile(zip_file_); |
| 173 int64_t* size, | 286 if (return_value == UNZ_END_OF_LIST_OF_FILE) { |
| 174 bool* is_directory, | 287 return VolumeArchive::RESULT_EOF; |
| 175 time_t* modification_time) { | 288 } |
| 176 Result ret = GetNextHeader(); | 289 if (return_value == UNZ_OK) |
| 290 return VolumeArchive::RESULT_SUCCESS; | |
| 177 | 291 |
| 178 if (ret == RESULT_SUCCESS) { | 292 set_error_message(volume_archive_constants::kArchiveNextHeaderError); |
| 179 *pathname = archive_entry_pathname(current_archive_entry_); | 293 return VolumeArchive::RESULT_FAIL; |
| 180 *size = archive_entry_size(current_archive_entry_); | |
| 181 *modification_time = archive_entry_mtime(current_archive_entry_); | |
| 182 *is_directory = archive_entry_filetype(current_archive_entry_) == AE_IFDIR; | |
| 183 } | |
| 184 | |
| 185 return ret; | |
| 186 } | 294 } |
| 187 | 295 |
| 188 bool VolumeArchiveLibarchive::SeekHeader(int64_t index) { | 296 bool VolumeArchiveLibarchive::SeekHeader(const std::string& path_name) { |
| 189 // Reset to 0 for new VolumeArchive::ReadData operation. | 297 // Reset to 0 for new VolumeArchive::ReadData operation. |
| 190 last_read_data_offset_ = 0; | 298 last_read_data_offset_ = 0; |
| 191 decompressed_data_size_ = 0; | 299 decompressed_data_size_ = 0; |
| 192 | 300 |
| 193 if (archive_read_seek_header(archive_, index) != ARCHIVE_OK) { | 301 const int kDefaultCaseSensivityOfOS = 0; |
| 194 set_error_message(ArchiveError( | 302 if (unzLocateFile(zip_file_, path_name.c_str(), kDefaultCaseSensivityOfOS) != |
| 195 volume_archive_constants::kArchiveNextHeaderErrorPrefix, archive_)); | 303 UNZ_OK) { |
| 304 set_error_message(volume_archive_constants::kArchiveNextHeaderError); | |
| 196 return false; | 305 return false; |
| 197 } | 306 } |
| 198 | 307 |
| 308 unz_file_info raw_file_info = {}; | |
| 309 char raw_file_name_in_zip[volume_archive_constants::kZipMaxPath] = {}; | |
|
mtomasz
2017/04/10 07:15:09
nit: Is the max path size matching ZIP format spec
takise
2017/04/11 06:00:52
Yes, I checked the ZIP specification.
| |
| 310 if (unzGetCurrentFileInfo(zip_file_, | |
| 311 &raw_file_info, | |
| 312 raw_file_name_in_zip, | |
| 313 sizeof(raw_file_name_in_zip) - 1, | |
| 314 NULL, // extraField. | |
| 315 0, // extraFieldBufferSize. | |
| 316 NULL, // szComment. | |
| 317 0) != UNZ_OK) { | |
| 318 set_error_message(volume_archive_constants::kArchiveNextHeaderError); | |
| 319 return false; | |
| 320 } | |
| 321 | |
| 322 // Directory entries in zip files end with "/". | |
| 323 bool is_directory = base::EndsWith(raw_file_name_in_zip, "/", | |
| 324 base::CompareCase::INSENSITIVE_ASCII); | |
| 325 | |
| 326 int open_result = UNZ_OK; | |
| 327 // If the archive is encrypted, the lowest bit of raw_file_info.flag is set. | |
| 328 // Directories cannot be encrypted with the basic zip encrytion algorithm. | |
| 329 if (((raw_file_info.flag & 1) != 0) && !is_directory) { | |
| 330 // Currently minizip in third_party doesn't support decryption, so we just | |
| 331 // take encrypted zip files as unsupported. | |
| 332 set_error_message(volume_archive_constants::kArchiveNextHeaderError); | |
| 333 return false; | |
| 334 // const char* password = volume_archive_functions::GetPassphrase(this); | |
| 335 // open_result = unzOpenCurrentFilePassword(zip_file_, password); | |
| 336 } else { | |
| 337 open_result = unzOpenCurrentFile(zip_file_); | |
| 338 } | |
| 339 | |
| 340 if (open_result != UNZ_OK) { | |
| 341 set_error_message(volume_archive_constants::kArchiveNextHeaderError); | |
| 342 return false; | |
| 343 } | |
| 344 | |
| 199 return true; | 345 return true; |
| 200 } | 346 } |
| 201 | 347 |
| 202 void VolumeArchiveLibarchive::DecompressData(int64_t offset, int64_t length) { | 348 void VolumeArchiveLibarchive::DecompressData(int64_t offset, int64_t length) { |
| 203 // TODO(cmihail): As an optimization consider using archive_read_data_block | 349 // TODO(cmihail): As an optimization consider using archive_read_data_block |
| 204 // which avoids extra copying in case offset != last_read_data_offset_. | 350 // which avoids extra copying in case offset != last_read_data_offset_. |
| 205 // The logic will be more complicated because archive_read_data_block offset | 351 // The logic will be more complicated because archive_read_data_block offset |
| 206 // will not be aligned with the offset of the read request from JavaScript. | 352 // will not be aligned with the offset of the read request from JavaScript. |
| 207 | 353 |
| 208 // Requests with offset smaller than last read offset are not supported. | 354 // Requests with offset smaller than last read offset are not supported. |
| 209 if (offset < last_read_data_offset_) { | 355 if (offset < last_read_data_offset_) { |
| 210 set_error_message( | 356 set_error_message( |
| 211 std::string(volume_archive_constants::kArchiveReadDataErrorPrefix) + | 357 std::string(volume_archive_constants::kArchiveReadDataError)); |
| 212 "Reading backwards is not supported."); | |
| 213 decompressed_error_ = true; | 358 decompressed_error_ = true; |
| 214 return; | 359 return; |
| 215 } | 360 } |
| 216 | 361 |
| 217 // Request with offset greater than last read offset. Skip not needed bytes. | 362 // Request with offset greater than last read offset. Skip not needed bytes. |
| 218 // Because files are compressed, seeking is not possible, so all of the bytes | 363 // Because files are compressed, seeking is not possible, so all of the bytes |
| 219 // until the requested position must be unpacked. | 364 // until the requested position must be unpacked. |
| 220 ssize_t size = -1; | 365 ssize_t size = -1; |
| 221 while (offset > last_read_data_offset_) { | 366 while (offset > last_read_data_offset_) { |
| 222 // ReadData will call CustomArchiveRead when calling archive_read_data. Read | 367 // ReadData will call CustomArchiveRead when calling archive_read_data. Read |
| 223 // should not request more bytes than possibly needed, so we request either | 368 // should not request more bytes than possibly needed, so we request either |
| 224 // offset - last_read_data_offset_, kMaximumDataChunkSize in case the former | 369 // offset - last_read_data_offset_, kMaximumDataChunkSize in case the former |
| 225 // is too big or kMinimumDataChunkSize in case its too small and we might | 370 // is too big or kMinimumDataChunkSize in case its too small and we might |
| 226 // end up with too many IPCs. | 371 // end up with too many IPCs. |
| 227 reader_data_size_ = | 372 reader_data_size_ = |
| 228 std::max(std::min(offset - last_read_data_offset_, | 373 std::max(std::min(offset - last_read_data_offset_, |
| 229 volume_archive_constants::kMaximumDataChunkSize), | 374 volume_archive_constants::kMaximumDataChunkSize), |
| 230 volume_archive_constants::kMinimumDataChunkSize); | 375 volume_archive_constants::kMinimumDataChunkSize); |
| 231 | 376 |
| 232 // No need for an offset in dummy_buffer as it will be ignored anyway. | 377 // No need for an offset in dummy_buffer as it will be ignored anyway. |
| 233 // archive_read_data receives size_t as length parameter, but we limit it to | 378 // archive_read_data receives size_t as length parameter, but we limit it to |
| 234 // volume_archive_constants::kDummyBufferSize which is positive and less | 379 // volume_archive_constants::kDummyBufferSize which is positive and less |
| 235 // than size_t maximum. So conversion from int64_t to size_t is safe here. | 380 // than size_t maximum. So conversion from int64_t to size_t is safe here. |
| 236 size = | 381 size = unzReadCurrentFile( |
| 237 archive_read_data(archive_, | 382 zip_file_, dummy_buffer_, |
| 238 dummy_buffer_, | 383 std::min(offset - last_read_data_offset_, |
| 239 std::min(offset - last_read_data_offset_, | 384 volume_archive_constants::kDummyBufferSize)); |
| 240 volume_archive_constants::kDummyBufferSize)); | |
| 241 PP_DCHECK(size != 0); // The actual read is done below. We shouldn't get to | 385 PP_DCHECK(size != 0); // The actual read is done below. We shouldn't get to |
| 242 // end of file here. | 386 // end of file here. |
| 243 if (size < 0) { // Error. | 387 if (size < 0) { // Error. |
| 244 set_error_message(ArchiveError( | 388 set_error_message(volume_archive_constants::kArchiveReadDataError); |
| 245 volume_archive_constants::kArchiveReadDataErrorPrefix, archive_)); | |
| 246 decompressed_error_ = true; | 389 decompressed_error_ = true; |
| 247 return; | 390 return; |
| 248 } | 391 } |
| 249 last_read_data_offset_ += size; | 392 last_read_data_offset_ += size; |
| 250 } | 393 } |
| 251 | 394 |
| 252 // Do not decompress more bytes than we can store internally. The | 395 // Do not decompress more bytes than we can store internally. The |
| 253 // kDecompressBufferSize limit is used to avoid huge memory usage. | 396 // kDecompressBufferSize limit is used to avoid huge memory usage. |
| 254 int64_t left_length = | 397 int64_t left_length = |
| 255 std::min(length, volume_archive_constants::kDecompressBufferSize); | 398 std::min(length, volume_archive_constants::kDecompressBufferSize); |
| 256 | 399 |
| 257 // ReadData will call CustomArchiveRead when calling archive_read_data. The | 400 // ReadData will call CustomArchiveRead when calling archive_read_data. The |
| 258 // read should be done with a value similar to length, which is the requested | 401 // read should be done with a value similar to length, which is the requested |
| 259 // number of bytes, or kMaximumDataChunkSize / kMinimumDataChunkSize | 402 // number of bytes, or kMaximumDataChunkSize / kMinimumDataChunkSize |
| 260 // in case length is too big or too small. | 403 // in case length is too big or too small. |
| 261 reader_data_size_ = | 404 reader_data_size_ = |
| 262 std::max(std::min(static_cast<int64_t>(left_length), | 405 std::max(std::min(static_cast<int64_t>(left_length), |
| 263 volume_archive_constants::kMaximumDataChunkSize), | 406 volume_archive_constants::kMaximumDataChunkSize), |
| 264 volume_archive_constants::kMinimumDataChunkSize); | 407 volume_archive_constants::kMinimumDataChunkSize); |
| 265 | 408 |
| 266 // Perform the actual copy. | 409 // Perform the actual copy. |
| 267 int64_t bytes_read = 0; | 410 int64_t bytes_read = 0; |
| 268 do { | 411 do { |
| 269 // archive_read_data receives size_t as length parameter, but we limit it to | 412 // archive_read_data receives size_t as length parameter, but we limit it to |
| 270 // volume_archive_constants::kMinimumDataChunkSize (see left_length | 413 // volume_archive_constants::kMinimumDataChunkSize (see left_length |
| 271 // initialization), which is positive and less than size_t maximum. | 414 // initialization), which is positive and less than size_t maximum. |
| 272 // So conversion from int64_t to size_t is safe here. | 415 // So conversion from int64_t to size_t is safe here. |
| 273 size = archive_read_data( | 416 size = unzReadCurrentFile(zip_file_, |
| 274 archive_, decompressed_data_buffer_ + bytes_read, left_length); | 417 decompressed_data_buffer_ + bytes_read, |
| 418 left_length); | |
| 275 if (size < 0) { // Error. | 419 if (size < 0) { // Error. |
| 276 set_error_message(ArchiveError( | 420 set_error_message(volume_archive_constants::kArchiveReadDataError); |
| 277 volume_archive_constants::kArchiveReadDataErrorPrefix, archive_)); | |
| 278 decompressed_error_ = true; | 421 decompressed_error_ = true; |
| 279 return; | 422 return; |
| 280 } | 423 } |
| 281 bytes_read += size; | 424 bytes_read += size; |
| 282 left_length -= size; | 425 left_length -= size; |
| 283 } while (left_length > 0 && size != 0); // There is still data to read. | 426 } while (left_length > 0 && size != 0); // There is still data to read. |
| 284 | 427 |
| 285 // VolumeArchiveLibarchive::DecompressData always stores the data from | 428 // VolumeArchiveLibarchive::DecompressData always stores the data from |
| 286 // beginning of the buffer. VolumeArchiveLibarchive::ConsumeData is used | 429 // beginning of the buffer. VolumeArchiveLibarchive::ConsumeData is used |
| 287 // to preserve the bytes that are decompressed but not required by | 430 // to preserve the bytes that are decompressed but not required by |
| 288 // VolumeArchiveLibarchive::ReadData. | 431 // VolumeArchiveLibarchive::ReadData. |
| 289 decompressed_data_ = decompressed_data_buffer_; | 432 decompressed_data_ = decompressed_data_buffer_; |
| 290 decompressed_data_size_ = bytes_read; | 433 decompressed_data_size_ = bytes_read; |
| 291 } | 434 } |
| 292 | 435 |
| 293 bool VolumeArchiveLibarchive::Cleanup() { | 436 bool VolumeArchiveLibarchive::Cleanup() { |
| 294 bool returnValue = true; | 437 bool returnValue = true; |
| 295 if (archive_ && archive_read_free(archive_) != ARCHIVE_OK) { | 438 if (zip_file_) { |
| 296 set_error_message(ArchiveError( | 439 if (unzClose(zip_file_) != UNZ_OK) { |
| 297 volume_archive_constants::kArchiveReadFreeErrorPrefix, archive_)); | 440 set_error_message(volume_archive_constants::kArchiveReadFreeError); |
| 298 returnValue = false; // Cleanup should release all resources even | 441 returnValue = false; |
| 299 // in case of failures. | 442 } |
| 300 } | 443 } |
| 301 archive_ = NULL; | 444 zip_file_ = NULL; |
| 302 | 445 |
| 303 CleanupReader(); | 446 CleanupReader(); |
| 304 | 447 |
| 305 return returnValue; | 448 return returnValue; |
| 306 } | 449 } |
| 307 | 450 |
| 308 int64_t VolumeArchiveLibarchive::ReadData(int64_t offset, | 451 int64_t VolumeArchiveLibarchive::ReadData(int64_t offset, |
| 309 int64_t length, | 452 int64_t length, |
| 310 const char** buffer) { | 453 const char** buffer) { |
| 311 PP_DCHECK(length > 0); // Length must be at least 1. | 454 PP_DCHECK(length > 0); // Length must be at least 1. |
| 312 PP_DCHECK(current_archive_entry_); // Check that GetNextHeader was called at | 455 PP_DCHECK(current_archive_entry_); // Check that GetNextHeader was called at |
| 313 // least once. In case it wasn't, this is | 456 // least once. In case it wasn't, this is |
| 314 // a programmer error. | 457 // a programmer error. |
| 315 | |
| 316 // End of archive. | |
| 317 if (archive_entry_size_is_set(current_archive_entry_) && | |
| 318 archive_entry_size(current_archive_entry_) <= offset) | |
| 319 return 0; | |
| 320 | |
| 321 // In case of first read or no more available data in the internal buffer or | 458 // In case of first read or no more available data in the internal buffer or |
| 322 // offset is different from the last_read_data_offset_, then force | 459 // offset is different from the last_read_data_offset_, then force |
| 323 // VolumeArchiveLibarchive::DecompressData as the decompressed data is | 460 // VolumeArchiveLibarchive::DecompressData as the decompressed data is |
| 324 // invalid. | 461 // invalid. |
| 325 if (!decompressed_data_ || last_read_data_offset_ != offset || | 462 if (!decompressed_data_ || last_read_data_offset_ != offset || |
| 326 decompressed_data_size_ == 0) | 463 decompressed_data_size_ == 0) |
| 327 DecompressData(offset, length); | 464 DecompressData(offset, length); |
| 328 | 465 |
| 329 // Decompressed failed. | 466 // Decompressed failed. |
| 330 if (decompressed_error_) | 467 if (decompressed_error_) { |
| 331 return kArchiveReadDataError; | 468 set_error_message(volume_archive_constants::kArchiveReadDataError); |
| 469 return -1 /* Error */; | |
| 470 } | |
| 332 | 471 |
| 333 last_read_data_length_ = length; // Used for decompress ahead. | 472 last_read_data_length_ = length; // Used for decompress ahead. |
| 334 | 473 |
| 335 // Assign the output *buffer parameter to the internal buffer. | 474 // Assign the output *buffer parameter to the internal buffer. |
| 336 *buffer = decompressed_data_; | 475 *buffer = decompressed_data_; |
| 337 | 476 |
| 338 // Advance internal buffer for next ReadData call. | 477 // Advance internal buffer for next ReadData call. |
| 339 int64_t read_bytes = std::min(decompressed_data_size_, length); | 478 int64_t read_bytes = std::min(decompressed_data_size_, length); |
| 340 decompressed_data_ = decompressed_data_ + read_bytes; | 479 decompressed_data_ = decompressed_data_ + read_bytes; |
| 341 decompressed_data_size_ -= read_bytes; | 480 decompressed_data_size_ -= read_bytes; |
| 342 last_read_data_offset_ += read_bytes; | 481 last_read_data_offset_ += read_bytes; |
| 343 | 482 |
| 344 PP_DCHECK(decompressed_data_ + decompressed_data_size_ <= | 483 PP_DCHECK(decompressed_data_ + decompressed_data_size_ <= |
| 345 decompressed_data_buffer_ + | 484 decompressed_data_buffer_ + |
| 346 volume_archive_constants::kDecompressBufferSize); | 485 volume_archive_constants::kDecompressBufferSize); |
| 347 | 486 |
| 348 return read_bytes; | 487 return read_bytes; |
| 349 } | 488 } |
| 350 | 489 |
| 351 void VolumeArchiveLibarchive::MaybeDecompressAhead() { | 490 void VolumeArchiveLibarchive::MaybeDecompressAhead() { |
| 352 if (decompressed_data_size_ == 0) | 491 if (decompressed_data_size_ == 0) |
| 353 DecompressData(last_read_data_offset_, last_read_data_length_); | 492 DecompressData(last_read_data_offset_, last_read_data_length_); |
| 354 } | 493 } |
| OLD | NEW |