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