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

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

Issue 2807063002: Replace Libarchive with MiniZip. (Closed)
Patch Set: Delete BUILD.gn 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* 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_, &current_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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698