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

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

Issue 2818673004: Rename filenames that end with libarchive with minizip. (Closed)
Patch Set: 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
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698