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

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

Issue 2804453002: Move files from zip_archiver/unpacker/ to zip_archiver/. (Closed)
Patch Set: Move files from zip_archiver/unpacker/ to zip_archiver/. 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 <limits>
10
11 #include "archive_entry.h"
12 #include "ppapi/cpp/logging.h"
13
14 namespace {
15
16 const int64_t kArchiveReadDataError = -1; // Negative value means error.
17
18 std::string ArchiveError(const std::string& message, archive* archive_object) {
19 return message + archive_error_string(archive_object);
20 }
21
22 // Sets the libarchive internal error to a VolumeReader related error.
23 // archive_error_string function must work on valid strings, but in case of
24 // errors in the custom functions, libarchive API assumes the error is set by
25 // us. If we don't set it, we will get a Segmentation Fault because
26 // archive_error_string will work on invalid memory.
27 void SetLibarchiveErrorToVolumeReaderError(archive* archive_object) {
28 archive_set_error(archive_object,
29 EIO /* I/O error. */,
30 "%s" /* Format string similar to printf. */,
31 volume_archive_constants::kVolumeReaderError);
32 }
33
34 ssize_t CustomArchiveRead(archive* archive_object,
35 void* client_data,
36 const void** buffer) {
37 VolumeArchiveLibarchive* volume_archive =
38 static_cast<VolumeArchiveLibarchive*>(client_data);
39
40 int64_t read_bytes = volume_archive->reader()->Read(
41 volume_archive->reader_data_size(), buffer);
42 if (read_bytes == ARCHIVE_FATAL)
43 SetLibarchiveErrorToVolumeReaderError(archive_object);
44 return read_bytes;
45 }
46
47 int64_t CustomArchiveSkip(archive* archive_object,
48 void* client_data,
49 int64_t request) {
50 VolumeArchiveLibarchive* volume_archive =
51 static_cast<VolumeArchiveLibarchive*>(client_data);
52 // VolumeReader::Skip returns 0 in case of failure and CustomArchiveRead is
53 // used instead, so there is no need to check for VolumeReader error.
54 return volume_archive->reader()->Skip(request);
55 }
56
57 int64_t CustomArchiveSeek(archive* archive_object,
58 void* client_data,
59 int64_t offset,
60 int whence) {
61 VolumeArchiveLibarchive* volume_archive =
62 static_cast<VolumeArchiveLibarchive*>(client_data);
63
64 int64_t new_offset = volume_archive->reader()->Seek(offset, whence);
65 if (new_offset == ARCHIVE_FATAL)
66 SetLibarchiveErrorToVolumeReaderError(archive_object);
67
68 return new_offset;
69 }
70
71 int CustomArchiveClose(archive* archive_object, void* client_data) {
72 return ARCHIVE_OK;
73 }
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
85 VolumeArchiveLibarchive::VolumeArchiveLibarchive(VolumeReader* reader)
86 : VolumeArchive(reader),
87 reader_data_size_(volume_archive_constants::kMinimumDataChunkSize),
88 archive_(NULL),
89 current_archive_entry_(NULL),
90 last_read_data_offset_(0),
91 last_read_data_length_(0),
92 decompressed_data_(NULL),
93 decompressed_data_size_(0),
94 decompressed_error_(false) {
95 }
96
97 VolumeArchiveLibarchive::~VolumeArchiveLibarchive() {
98 Cleanup();
99 }
100
101 bool VolumeArchiveLibarchive::Init(const std::string& encoding) {
102 archive_ = archive_read_new();
103 if (!archive_) {
104 set_error_message(volume_archive_constants::kArchiveReadNewError);
105 return false;
106 }
107
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;
145 }
146
147 VolumeArchive::Result VolumeArchiveLibarchive::GetNextHeader() {
148 // 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
150 // grouped one by one.
151 reader_data_size_ = volume_archive_constants::kMaximumDataChunkSize;
152
153 // Reset to 0 for new VolumeArchive::ReadData operation.
154 last_read_data_offset_ = 0;
155 decompressed_data_size_ = 0;
156
157 // Archive data is skipped automatically by next call to
158 // archive_read_next_header.
159 switch (archive_read_next_header(archive_, &current_archive_entry_)) {
160 case ARCHIVE_EOF:
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 }
169 }
170
171 VolumeArchive::Result VolumeArchiveLibarchive::GetNextHeader(
172 const char** pathname,
173 int64_t* size,
174 bool* is_directory,
175 time_t* modification_time) {
176 Result ret = GetNextHeader();
177
178 if (ret == RESULT_SUCCESS) {
179 *pathname = archive_entry_pathname(current_archive_entry_);
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 }
187
188 bool VolumeArchiveLibarchive::SeekHeader(int64_t index) {
189 // Reset to 0 for new VolumeArchive::ReadData operation.
190 last_read_data_offset_ = 0;
191 decompressed_data_size_ = 0;
192
193 if (archive_read_seek_header(archive_, index) != ARCHIVE_OK) {
194 set_error_message(ArchiveError(
195 volume_archive_constants::kArchiveNextHeaderErrorPrefix, archive_));
196 return false;
197 }
198
199 return true;
200 }
201
202 void VolumeArchiveLibarchive::DecompressData(int64_t offset, int64_t length) {
203 // TODO(cmihail): As an optimization consider using archive_read_data_block
204 // which avoids extra copying in case offset != last_read_data_offset_.
205 // 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.
207
208 // Requests with offset smaller than last read offset are not supported.
209 if (offset < last_read_data_offset_) {
210 set_error_message(
211 std::string(volume_archive_constants::kArchiveReadDataErrorPrefix) +
212 "Reading backwards is not supported.");
213 decompressed_error_ = true;
214 return;
215 }
216
217 // 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
219 // until the requested position must be unpacked.
220 ssize_t size = -1;
221 while (offset > last_read_data_offset_) {
222 // ReadData will call CustomArchiveRead when calling archive_read_data. Read
223 // should not request more bytes than possibly needed, so we request either
224 // offset - last_read_data_offset_, kMaximumDataChunkSize in case the former
225 // is too big or kMinimumDataChunkSize in case its too small and we might
226 // end up with too many IPCs.
227 reader_data_size_ =
228 std::max(std::min(offset - last_read_data_offset_,
229 volume_archive_constants::kMaximumDataChunkSize),
230 volume_archive_constants::kMinimumDataChunkSize);
231
232 // 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
234 // 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.
236 size =
237 archive_read_data(archive_,
238 dummy_buffer_,
239 std::min(offset - last_read_data_offset_,
240 volume_archive_constants::kDummyBufferSize));
241 PP_DCHECK(size != 0); // The actual read is done below. We shouldn't get to
242 // end of file here.
243 if (size < 0) { // Error.
244 set_error_message(ArchiveError(
245 volume_archive_constants::kArchiveReadDataErrorPrefix, archive_));
246 decompressed_error_ = true;
247 return;
248 }
249 last_read_data_offset_ += size;
250 }
251
252 // Do not decompress more bytes than we can store internally. The
253 // kDecompressBufferSize limit is used to avoid huge memory usage.
254 int64_t left_length =
255 std::min(length, volume_archive_constants::kDecompressBufferSize);
256
257 // 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
259 // number of bytes, or kMaximumDataChunkSize / kMinimumDataChunkSize
260 // in case length is too big or too small.
261 reader_data_size_ =
262 std::max(std::min(static_cast<int64_t>(left_length),
263 volume_archive_constants::kMaximumDataChunkSize),
264 volume_archive_constants::kMinimumDataChunkSize);
265
266 // Perform the actual copy.
267 int64_t bytes_read = 0;
268 do {
269 // archive_read_data receives size_t as length parameter, but we limit it to
270 // volume_archive_constants::kMinimumDataChunkSize (see left_length
271 // initialization), which is positive and less than size_t maximum.
272 // So conversion from int64_t to size_t is safe here.
273 size = archive_read_data(
274 archive_, decompressed_data_buffer_ + bytes_read, left_length);
275 if (size < 0) { // Error.
276 set_error_message(ArchiveError(
277 volume_archive_constants::kArchiveReadDataErrorPrefix, archive_));
278 decompressed_error_ = true;
279 return;
280 }
281 bytes_read += size;
282 left_length -= size;
283 } while (left_length > 0 && size != 0); // There is still data to read.
284
285 // VolumeArchiveLibarchive::DecompressData always stores the data from
286 // beginning of the buffer. VolumeArchiveLibarchive::ConsumeData is used
287 // to preserve the bytes that are decompressed but not required by
288 // VolumeArchiveLibarchive::ReadData.
289 decompressed_data_ = decompressed_data_buffer_;
290 decompressed_data_size_ = bytes_read;
291 }
292
293 bool VolumeArchiveLibarchive::Cleanup() {
294 bool returnValue = true;
295 if (archive_ && archive_read_free(archive_) != ARCHIVE_OK) {
296 set_error_message(ArchiveError(
297 volume_archive_constants::kArchiveReadFreeErrorPrefix, archive_));
298 returnValue = false; // Cleanup should release all resources even
299 // in case of failures.
300 }
301 archive_ = NULL;
302
303 CleanupReader();
304
305 return returnValue;
306 }
307
308 int64_t VolumeArchiveLibarchive::ReadData(int64_t offset,
309 int64_t length,
310 const char** buffer) {
311 PP_DCHECK(length > 0); // Length must be at least 1.
312 PP_DCHECK(current_archive_entry_); // Check that GetNextHeader was called at
313 // least once. In case it wasn't, this is
314 // 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
322 // offset is different from the last_read_data_offset_, then force
323 // VolumeArchiveLibarchive::DecompressData as the decompressed data is
324 // invalid.
325 if (!decompressed_data_ || last_read_data_offset_ != offset ||
326 decompressed_data_size_ == 0)
327 DecompressData(offset, length);
328
329 // Decompressed failed.
330 if (decompressed_error_)
331 return kArchiveReadDataError;
332
333 last_read_data_length_ = length; // Used for decompress ahead.
334
335 // Assign the output *buffer parameter to the internal buffer.
336 *buffer = decompressed_data_;
337
338 // Advance internal buffer for next ReadData call.
339 int64_t read_bytes = std::min(decompressed_data_size_, length);
340 decompressed_data_ = decompressed_data_ + read_bytes;
341 decompressed_data_size_ -= read_bytes;
342 last_read_data_offset_ += read_bytes;
343
344 PP_DCHECK(decompressed_data_ + decompressed_data_size_ <=
345 decompressed_data_buffer_ +
346 volume_archive_constants::kDecompressBufferSize);
347
348 return read_bytes;
349 }
350
351 void VolumeArchiveLibarchive::MaybeDecompressAhead() {
352 if (decompressed_data_size_ == 0)
353 DecompressData(last_read_data_offset_, last_read_data_length_);
354 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698