Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(60)

Side by Side Diff: ui/file_manager/zip_archiver/cpp/volume_archive_libarchive.cc

Issue 2807063002: Replace Libarchive with MiniZip. (Closed)
Patch Set: Fix a few nits. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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* archive,
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 archive;
19 return message + archive_error_string(archive_object); 22 }
20 } 23
21 24 int64_t DynamicCache(VolumeArchiveLibarchive* archive, int64_t unzip_size) {
22 // Sets the libarchive internal error to a VolumeReader related error. 25 int64_t offset = archive->reader()->offset();
23 // archive_error_string function must work on valid strings, but in case of 26 if (archive->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 archive->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 = archive->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 {
37 VolumeArchiveLibarchive* volume_archive = 40 int64_t read_bytes = archive->reader()->Read(left_length,
38 static_cast<VolumeArchiveLibarchive*>(client_data); 41 &destination_buffer);
39 42 // End of the zip file.
40 int64_t read_bytes = volume_archive->reader()->Read( 43 if (read_bytes == 0)
41 volume_archive->reader_data_size(), buffer); 44 break;
42 if (read_bytes == ARCHIVE_FATAL) 45 if (read_bytes < 0)
43 SetLibarchiveErrorToVolumeReaderError(archive_object); 46 return -1 /* Error */;
44 return read_bytes; 47 memcpy(buffer_pointer, destination_buffer, read_bytes);
45 } 48 left_length -= read_bytes;
46 49 buffer_pointer += read_bytes;
47 int64_t CustomArchiveSkip(archive* archive_object, 50 } while (left_length > 0);
48 void* client_data, 51
49 int64_t request) { 52 if (archive->reader()->Seek(static_cast<int64_t>(offset),
50 VolumeArchiveLibarchive* volume_archive = 53 ZLIB_FILEFUNC_SEEK_SET) < 0) {
51 static_cast<VolumeArchiveLibarchive*>(client_data); 54 return -1 /* Error */;
52 // VolumeReader::Skip returns 0 in case of failure and CustomArchiveRead is 55 }
53 // used instead, so there is no need to check for VolumeReader error. 56 archive->dynamic_cache_size_ = bytes_to_read - left_length;
54 return volume_archive->reader()->Skip(request); 57 archive->dynamic_cache_offset_ = offset;
55 } 58
56 59 return unzip_size - left_length;
57 int64_t CustomArchiveSeek(archive* archive_object, 60 }
58 void* client_data, 61
59 int64_t offset, 62 uLong CustomArchiveRead(void* archive,
60 int whence) { 63 void* /* stream */,
61 VolumeArchiveLibarchive* volume_archive = 64 void* buffer,
62 static_cast<VolumeArchiveLibarchive*>(client_data); 65 uLong size) {
63 66 VolumeArchiveLibarchive* archive_minizip =
64 int64_t new_offset = volume_archive->reader()->Seek(offset, whence); 67 static_cast<VolumeArchiveLibarchive*>(archive);
65 if (new_offset == ARCHIVE_FATAL) 68 int64_t offset = archive_minizip->reader()->offset();
66 SetLibarchiveErrorToVolumeReaderError(archive_object); 69
67 70 // When minizip requests a chunk in static_cache_.
68 return new_offset; 71 if (offset >= archive_minizip->static_cache_offset_) {
69 } 72 // Relative offset in the central directory.
70 73 int64_t offset_in_cache = offset - archive_minizip->static_cache_offset_;
71 int CustomArchiveClose(archive* archive_object, void* client_data) { 74 memcpy(buffer, archive_minizip->static_cache_ + offset_in_cache, size);
72 return ARCHIVE_OK; 75 if (archive_minizip->reader()->Seek(static_cast<int64_t>(size),
73 } 76 ZLIB_FILEFUNC_SEEK_CUR) < 0) {
74 77 return -1 /* Error */;
75 const char* CustomArchivePassphrase( 78 }
76 archive* archive_object, void* client_data) { 79 return size;
77 VolumeArchiveLibarchive* volume_archive = 80 }
78 static_cast<VolumeArchiveLibarchive*>(client_data); 81
79 82 char* unzip_buffer_pointer = static_cast<char*>(buffer);
80 return volume_archive->reader()->Passphrase(); 83 int64_t left_length = static_cast<int64_t>(size);
81 } 84
82 85 do {
83 } // namespace 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 VolumeArchiveLibarchive* archive_minizip =
125 static_cast<VolumeArchiveLibarchive*>(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 VolumeArchiveLibarchive* archive_minizip =
134 static_cast<VolumeArchiveLibarchive*>(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(VolumeArchiveLibarchive* archive_minizip) {
152 const char* password = archive_minizip->reader()->Passphrase();
153 return password;
154 }
155
156 } // volume_archive_functions
84 157
85 VolumeArchiveLibarchive::VolumeArchiveLibarchive(VolumeReader* reader) 158 VolumeArchiveLibarchive::VolumeArchiveLibarchive(VolumeReader* reader)
86 : VolumeArchive(reader), 159 : VolumeArchive(reader),
87 reader_data_size_(volume_archive_constants::kMinimumDataChunkSize), 160 reader_data_size_(volume_archive_constants::kMinimumDataChunkSize),
88 archive_(NULL), 161 zip_file_(nullptr),
89 current_archive_entry_(NULL), 162 dynamic_cache_offset_(0),
163 dynamic_cache_size_(0),
164 static_cache_offset_(0),
165 static_cache_size_(0),
90 last_read_data_offset_(0), 166 last_read_data_offset_(0),
91 last_read_data_length_(0), 167 last_read_data_length_(0),
92 decompressed_data_(NULL), 168 decompressed_data_(nullptr),
93 decompressed_data_size_(0), 169 decompressed_data_size_(0),
94 decompressed_error_(false) { 170 decompressed_error_(false) {}
95 }
96 171
97 VolumeArchiveLibarchive::~VolumeArchiveLibarchive() { 172 VolumeArchiveLibarchive::~VolumeArchiveLibarchive() {
98 Cleanup(); 173 Cleanup();
99 } 174 }
100 175
101 bool VolumeArchiveLibarchive::Init(const std::string& encoding) { 176 bool VolumeArchiveLibarchive::Init(const std::string& encoding) {
102 archive_ = archive_read_new(); 177 // Set up minizip object.
103 if (!archive_) { 178 zlib_filefunc_def zip_funcs;
104 set_error_message(volume_archive_constants::kArchiveReadNewError); 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);
105 return false; 219 return false;
106 } 220 }
107 221
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; 222 return true;
145 } 223 }
146 224
147 VolumeArchive::Result VolumeArchiveLibarchive::GetNextHeader() { 225 VolumeArchive::Result VolumeArchiveLibarchive::GetCurrentFileInfo(
226 std::string* pathname,
227 int64_t* size,
228 bool* is_directory,
229 time_t* modification_time) {
230
148 // Headers are being read from the central directory (in the ZIP format), so 231 // 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 232 // use a large block size to save on IPC calls. The headers in EOCD are
150 // grouped one by one. 233 // grouped one by one.
151 reader_data_size_ = volume_archive_constants::kMaximumDataChunkSize; 234 reader_data_size_ = volume_archive_constants::kMaximumDataChunkSize;
152 235
153 // Reset to 0 for new VolumeArchive::ReadData operation. 236 // Reset to 0 for new VolumeArchive::ReadData operation.
154 last_read_data_offset_ = 0; 237 last_read_data_offset_ = 0;
155 decompressed_data_size_ = 0; 238 decompressed_data_size_ = 0;
156 239
157 // Archive data is skipped automatically by next call to 240 unz_file_pos position = {};
158 // archive_read_next_header. 241 if (unzGetFilePos(zip_file_, &position) != UNZ_OK) {
159 switch (archive_read_next_header(archive_, &current_archive_entry_)) { 242 set_error_message(volume_archive_constants::kArchiveNextHeaderError);
160 case ARCHIVE_EOF: 243 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 } 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;
169 } 289 }
170 290
171 VolumeArchive::Result VolumeArchiveLibarchive::GetNextHeader( 291 VolumeArchive::Result VolumeArchiveLibarchive::GoToNextFile() {
172 const char** pathname, 292 int return_value = unzGoToNextFile(zip_file_);
173 int64_t* size, 293 if (return_value == UNZ_END_OF_LIST_OF_FILE) {
174 bool* is_directory, 294 return VolumeArchive::RESULT_EOF;
175 time_t* modification_time) { 295 }
176 Result ret = GetNextHeader(); 296 if (return_value == UNZ_OK)
297 return VolumeArchive::RESULT_SUCCESS;
177 298
178 if (ret == RESULT_SUCCESS) { 299 set_error_message(volume_archive_constants::kArchiveNextHeaderError);
179 *pathname = archive_entry_pathname(current_archive_entry_); 300 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 } 301 }
187 302
188 bool VolumeArchiveLibarchive::SeekHeader(int64_t index) { 303 bool VolumeArchiveLibarchive::SeekHeader(const std::string& path_name) {
189 // Reset to 0 for new VolumeArchive::ReadData operation. 304 // Reset to 0 for new VolumeArchive::ReadData operation.
190 last_read_data_offset_ = 0; 305 last_read_data_offset_ = 0;
191 decompressed_data_size_ = 0; 306 decompressed_data_size_ = 0;
192 307
193 if (archive_read_seek_header(archive_, index) != ARCHIVE_OK) { 308 const int kDefaultCaseSensivityOfOS = 0;
194 set_error_message(ArchiveError( 309 if (unzLocateFile(zip_file_, path_name.c_str(), kDefaultCaseSensivityOfOS) !=
195 volume_archive_constants::kArchiveNextHeaderErrorPrefix, archive_)); 310 UNZ_OK) {
311 set_error_message(volume_archive_constants::kArchiveNextHeaderError);
196 return false; 312 return false;
197 } 313 }
198 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
199 return true; 352 return true;
200 } 353 }
201 354
202 void VolumeArchiveLibarchive::DecompressData(int64_t offset, int64_t length) { 355 void VolumeArchiveLibarchive::DecompressData(int64_t offset, int64_t length) {
203 // TODO(cmihail): As an optimization consider using archive_read_data_block 356 // TODO(cmihail): As an optimization consider using archive_read_data_block
204 // which avoids extra copying in case offset != last_read_data_offset_. 357 // which avoids extra copying in case offset != last_read_data_offset_.
205 // The logic will be more complicated because archive_read_data_block offset 358 // 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. 359 // will not be aligned with the offset of the read request from JavaScript.
207 360
208 // Requests with offset smaller than last read offset are not supported. 361 // Requests with offset smaller than last read offset are not supported.
209 if (offset < last_read_data_offset_) { 362 if (offset < last_read_data_offset_) {
210 set_error_message( 363 set_error_message(
211 std::string(volume_archive_constants::kArchiveReadDataErrorPrefix) + 364 std::string(volume_archive_constants::kArchiveReadDataError));
212 "Reading backwards is not supported.");
213 decompressed_error_ = true; 365 decompressed_error_ = true;
214 return; 366 return;
215 } 367 }
216 368
217 // Request with offset greater than last read offset. Skip not needed bytes. 369 // 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 370 // Because files are compressed, seeking is not possible, so all of the bytes
219 // until the requested position must be unpacked. 371 // until the requested position must be unpacked.
220 ssize_t size = -1; 372 ssize_t size = -1;
221 while (offset > last_read_data_offset_) { 373 while (offset > last_read_data_offset_) {
222 // ReadData will call CustomArchiveRead when calling archive_read_data. Read 374 // ReadData will call CustomArchiveRead when calling archive_read_data. Read
223 // should not request more bytes than possibly needed, so we request either 375 // should not request more bytes than possibly needed, so we request either
224 // offset - last_read_data_offset_, kMaximumDataChunkSize in case the former 376 // offset - last_read_data_offset_, kMaximumDataChunkSize in case the former
225 // is too big or kMinimumDataChunkSize in case its too small and we might 377 // is too big or kMinimumDataChunkSize in case its too small and we might
226 // end up with too many IPCs. 378 // end up with too many IPCs.
227 reader_data_size_ = 379 reader_data_size_ =
228 std::max(std::min(offset - last_read_data_offset_, 380 std::max(std::min(offset - last_read_data_offset_,
229 volume_archive_constants::kMaximumDataChunkSize), 381 volume_archive_constants::kMaximumDataChunkSize),
230 volume_archive_constants::kMinimumDataChunkSize); 382 volume_archive_constants::kMinimumDataChunkSize);
231 383
232 // No need for an offset in dummy_buffer as it will be ignored anyway. 384 // 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 385 // archive_read_data receives size_t as length parameter, but we limit it to
234 // volume_archive_constants::kDummyBufferSize which is positive and less 386 // 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. 387 // than size_t maximum. So conversion from int64_t to size_t is safe here.
236 size = 388 size = unzReadCurrentFile(
237 archive_read_data(archive_, 389 zip_file_, dummy_buffer_,
238 dummy_buffer_, 390 std::min(offset - last_read_data_offset_,
239 std::min(offset - last_read_data_offset_, 391 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 392 PP_DCHECK(size != 0); // The actual read is done below. We shouldn't get to
242 // end of file here. 393 // end of file here.
243 if (size < 0) { // Error. 394 if (size < 0) { // Error.
244 set_error_message(ArchiveError( 395 set_error_message(volume_archive_constants::kArchiveReadDataError);
245 volume_archive_constants::kArchiveReadDataErrorPrefix, archive_));
246 decompressed_error_ = true; 396 decompressed_error_ = true;
247 return; 397 return;
248 } 398 }
249 last_read_data_offset_ += size; 399 last_read_data_offset_ += size;
250 } 400 }
251 401
252 // Do not decompress more bytes than we can store internally. The 402 // Do not decompress more bytes than we can store internally. The
253 // kDecompressBufferSize limit is used to avoid huge memory usage. 403 // kDecompressBufferSize limit is used to avoid huge memory usage.
254 int64_t left_length = 404 int64_t left_length =
255 std::min(length, volume_archive_constants::kDecompressBufferSize); 405 std::min(length, volume_archive_constants::kDecompressBufferSize);
256 406
257 // ReadData will call CustomArchiveRead when calling archive_read_data. The 407 // 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 408 // read should be done with a value similar to length, which is the requested
259 // number of bytes, or kMaximumDataChunkSize / kMinimumDataChunkSize 409 // number of bytes, or kMaximumDataChunkSize / kMinimumDataChunkSize
260 // in case length is too big or too small. 410 // in case length is too big or too small.
261 reader_data_size_ = 411 reader_data_size_ =
262 std::max(std::min(static_cast<int64_t>(left_length), 412 std::max(std::min(static_cast<int64_t>(left_length),
263 volume_archive_constants::kMaximumDataChunkSize), 413 volume_archive_constants::kMaximumDataChunkSize),
264 volume_archive_constants::kMinimumDataChunkSize); 414 volume_archive_constants::kMinimumDataChunkSize);
265 415
266 // Perform the actual copy. 416 // Perform the actual copy.
267 int64_t bytes_read = 0; 417 int64_t bytes_read = 0;
268 do { 418 do {
269 // archive_read_data receives size_t as length parameter, but we limit it to 419 // archive_read_data receives size_t as length parameter, but we limit it to
270 // volume_archive_constants::kMinimumDataChunkSize (see left_length 420 // volume_archive_constants::kMinimumDataChunkSize (see left_length
271 // initialization), which is positive and less than size_t maximum. 421 // initialization), which is positive and less than size_t maximum.
272 // So conversion from int64_t to size_t is safe here. 422 // So conversion from int64_t to size_t is safe here.
273 size = archive_read_data( 423 size = unzReadCurrentFile(zip_file_,
274 archive_, decompressed_data_buffer_ + bytes_read, left_length); 424 decompressed_data_buffer_ + bytes_read,
425 left_length);
275 if (size < 0) { // Error. 426 if (size < 0) { // Error.
276 set_error_message(ArchiveError( 427 set_error_message(volume_archive_constants::kArchiveReadDataError);
277 volume_archive_constants::kArchiveReadDataErrorPrefix, archive_));
278 decompressed_error_ = true; 428 decompressed_error_ = true;
279 return; 429 return;
280 } 430 }
281 bytes_read += size; 431 bytes_read += size;
282 left_length -= size; 432 left_length -= size;
283 } while (left_length > 0 && size != 0); // There is still data to read. 433 } while (left_length > 0 && size != 0); // There is still data to read.
284 434
285 // VolumeArchiveLibarchive::DecompressData always stores the data from 435 // VolumeArchiveLibarchive::DecompressData always stores the data from
286 // beginning of the buffer. VolumeArchiveLibarchive::ConsumeData is used 436 // beginning of the buffer. VolumeArchiveLibarchive::ConsumeData is used
287 // to preserve the bytes that are decompressed but not required by 437 // to preserve the bytes that are decompressed but not required by
288 // VolumeArchiveLibarchive::ReadData. 438 // VolumeArchiveLibarchive::ReadData.
289 decompressed_data_ = decompressed_data_buffer_; 439 decompressed_data_ = decompressed_data_buffer_;
290 decompressed_data_size_ = bytes_read; 440 decompressed_data_size_ = bytes_read;
291 } 441 }
292 442
293 bool VolumeArchiveLibarchive::Cleanup() { 443 bool VolumeArchiveLibarchive::Cleanup() {
294 bool returnValue = true; 444 bool returnValue = true;
295 if (archive_ && archive_read_free(archive_) != ARCHIVE_OK) { 445 if (zip_file_) {
296 set_error_message(ArchiveError( 446 if (unzClose(zip_file_) != UNZ_OK) {
297 volume_archive_constants::kArchiveReadFreeErrorPrefix, archive_)); 447 set_error_message(volume_archive_constants::kArchiveReadFreeError);
298 returnValue = false; // Cleanup should release all resources even 448 returnValue = false;
299 // in case of failures. 449 }
300 } 450 }
301 archive_ = NULL; 451 zip_file_ = nullptr;
302 452
303 CleanupReader(); 453 CleanupReader();
304 454
305 return returnValue; 455 return returnValue;
306 } 456 }
307 457
308 int64_t VolumeArchiveLibarchive::ReadData(int64_t offset, 458 int64_t VolumeArchiveLibarchive::ReadData(int64_t offset,
309 int64_t length, 459 int64_t length,
310 const char** buffer) { 460 const char** buffer) {
311 PP_DCHECK(length > 0); // Length must be at least 1. 461 PP_DCHECK(length > 0); // Length must be at least 1.
312 PP_DCHECK(current_archive_entry_); // Check that GetNextHeader was called at 462 PP_DCHECK(current_archive_entry_); // Check that GetNextHeader was called at
313 // least once. In case it wasn't, this is 463 // least once. In case it wasn't, this is
314 // a programmer error. 464 // 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 465 // 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 466 // offset is different from the last_read_data_offset_, then force
323 // VolumeArchiveLibarchive::DecompressData as the decompressed data is 467 // VolumeArchiveLibarchive::DecompressData as the decompressed data is
324 // invalid. 468 // invalid.
325 if (!decompressed_data_ || last_read_data_offset_ != offset || 469 if (!decompressed_data_ || last_read_data_offset_ != offset ||
326 decompressed_data_size_ == 0) 470 decompressed_data_size_ == 0)
327 DecompressData(offset, length); 471 DecompressData(offset, length);
328 472
329 // Decompressed failed. 473 // Decompressed failed.
330 if (decompressed_error_) 474 if (decompressed_error_) {
331 return kArchiveReadDataError; 475 set_error_message(volume_archive_constants::kArchiveReadDataError);
476 return -1 /* Error */;
477 }
332 478
333 last_read_data_length_ = length; // Used for decompress ahead. 479 last_read_data_length_ = length; // Used for decompress ahead.
334 480
335 // Assign the output *buffer parameter to the internal buffer. 481 // Assign the output *buffer parameter to the internal buffer.
336 *buffer = decompressed_data_; 482 *buffer = decompressed_data_;
337 483
338 // Advance internal buffer for next ReadData call. 484 // Advance internal buffer for next ReadData call.
339 int64_t read_bytes = std::min(decompressed_data_size_, length); 485 int64_t read_bytes = std::min(decompressed_data_size_, length);
340 decompressed_data_ = decompressed_data_ + read_bytes; 486 decompressed_data_ = decompressed_data_ + read_bytes;
341 decompressed_data_size_ -= read_bytes; 487 decompressed_data_size_ -= read_bytes;
342 last_read_data_offset_ += read_bytes; 488 last_read_data_offset_ += read_bytes;
343 489
344 PP_DCHECK(decompressed_data_ + decompressed_data_size_ <= 490 PP_DCHECK(decompressed_data_ + decompressed_data_size_ <=
345 decompressed_data_buffer_ + 491 decompressed_data_buffer_ +
346 volume_archive_constants::kDecompressBufferSize); 492 volume_archive_constants::kDecompressBufferSize);
347 493
348 return read_bytes; 494 return read_bytes;
349 } 495 }
350 496
351 void VolumeArchiveLibarchive::MaybeDecompressAhead() { 497 void VolumeArchiveLibarchive::MaybeDecompressAhead() {
352 if (decompressed_data_size_ == 0) 498 if (decompressed_data_size_ == 0)
353 DecompressData(last_read_data_offset_, last_read_data_length_); 499 DecompressData(last_read_data_offset_, last_read_data_length_);
354 } 500 }
OLDNEW
« no previous file with comments | « ui/file_manager/zip_archiver/cpp/volume_archive_libarchive.h ('k') | ui/file_manager/zip_archiver/cpp/volume_reader.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698