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

Side by Side Diff: ui/file_manager/zip_archiver/unpacker/cpp/volume.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.h"
6
7 #include <cstring>
8 #include <sstream>
9
10 #include "request.h"
11 #include "volume_archive_libarchive.h"
12 #include "volume_reader_javascript_stream.h"
13
14 namespace {
15
16 #define LOG(x) \
17 do { \
18 std::stringstream fmt; \
19 fmt << x; \
20 message_sender_->CONSOLE_LOG(file_system_id_, request_id, fmt.str()); \
21 } while (0)
22
23 typedef std::map<std::string, VolumeArchive*>::const_iterator
24 volume_archive_iterator;
25
26 const char kPathDelimiter[] = "/";
27
28 // size is int64_t and modification_time is time_t because this is how
29 // libarchive is going to pass them to us.
30 pp::VarDictionary CreateEntry(int64_t index,
31 const std::string& name,
32 bool is_directory,
33 int64_t size,
34 time_t modification_time) {
35 pp::VarDictionary entry_metadata;
36 // index is int64_t, unsupported by pp::Var
37 std::stringstream ss_index;
38 ss_index << index;
39 entry_metadata.Set("index", ss_index.str());
40 entry_metadata.Set("isDirectory", is_directory);
41 entry_metadata.Set("name", name);
42 // size is int64_t, unsupported by pp::Var
43 std::stringstream ss_size;
44 ss_size << size;
45 entry_metadata.Set("size", ss_size.str());
46 // mtime is time_t, unsupported by pp::Var
47 std::stringstream ss_modification_time;
48 ss_modification_time << modification_time;
49 entry_metadata.Set("modificationTime", ss_modification_time.str());
50
51 if (is_directory)
52 entry_metadata.Set("entries", pp::VarDictionary());
53
54 return entry_metadata;
55 }
56
57 void ConstructMetadata(int64_t index,
58 const std::string& entry_path,
59 int64_t size,
60 bool is_directory,
61 time_t modification_time,
62 pp::VarDictionary* parent_metadata) {
63 if (entry_path == "")
64 return;
65
66 pp::VarDictionary parent_entries =
67 pp::VarDictionary(parent_metadata->Get("entries"));
68
69 unsigned int position = entry_path.find(kPathDelimiter);
70 pp::VarDictionary entry_metadata;
71 std::string entry_name;
72
73 if (position == std::string::npos) { // The entry itself.
74 entry_name = entry_path;
75 entry_metadata =
76 CreateEntry(index, entry_name, is_directory, size, modification_time);
77
78 // Update directory information. Required as sometimes the directory itself
79 // is returned after the files inside it.
80 pp::Var old_entry_metadata_var = parent_entries.Get(entry_name);
81 if (!old_entry_metadata_var.is_undefined()) {
82 pp::VarDictionary old_entry_metadata =
83 pp::VarDictionary(old_entry_metadata_var);
84 PP_DCHECK(old_entry_metadata.Get("isDirectory").AsBool());
85 entry_metadata.Set("entries", old_entry_metadata.Get("entries"));
86 }
87 } else { // Get next parent on the way to the entry.
88 entry_name = entry_path.substr(0, position);
89
90 // Get next parent metadata. If none, create a new directory entry for it.
91 // Some archives don't have directory information inside and for some the
92 // information is returned later than the files inside it.
93 pp::Var entry_metadata_var = parent_entries.Get(entry_name);
94 if (entry_metadata_var.is_undefined())
95 entry_metadata = CreateEntry(-1, entry_name, true, 0, modification_time);
96 else
97 entry_metadata = pp::VarDictionary(parent_entries.Get(entry_name));
98
99 // Continue to construct metadata for all directories on the path to the
100 // to the entry and for the entry itself.
101 std::string entry_path_without_next_parent = entry_path.substr(
102 position + sizeof(kPathDelimiter) - 1 /* Last char is '\0'. */);
103
104 ConstructMetadata(index,
105 entry_path_without_next_parent,
106 size,
107 is_directory,
108 modification_time,
109 &entry_metadata);
110 }
111
112 // Recreate parent_metadata. This is necessary because pp::VarDictionary::Get
113 // returns a Var, not a Var& or Var* to directly modify the result.
114 parent_entries.Set(entry_name, entry_metadata);
115 parent_metadata->Set("entries", parent_entries);
116 }
117
118 // An internal implementation of JavaScriptRequestorInterface.
119 class JavaScriptRequestor : public JavaScriptRequestorInterface {
120 public:
121 // JavaScriptRequestor does not own the volume pointer.
122 explicit JavaScriptRequestor(Volume* volume) : volume_(volume) {}
123
124 virtual void RequestFileChunk(const std::string& request_id,
125 int64_t offset,
126 int64_t bytes_to_read) {
127 PP_DCHECK(offset >= 0);
128 PP_DCHECK(bytes_to_read > 0);
129 volume_->message_sender()->SendFileChunkRequest(
130 volume_->file_system_id(), request_id, offset, bytes_to_read);
131 }
132
133 virtual void RequestPassphrase(const std::string& request_id) {
134 volume_->message_sender()->SendPassphraseRequest(
135 volume_->file_system_id(), request_id);
136 }
137
138 private:
139 Volume* volume_;
140 };
141
142 // An internal implementation of VolumeArchiveFactoryInterface for default
143 // Volume constructor.
144 class VolumeArchiveFactory : public VolumeArchiveFactoryInterface {
145 public:
146 virtual VolumeArchive* Create(VolumeReader* reader) {
147 return new VolumeArchiveLibarchive(reader);
148 }
149 };
150
151 // An internal implementation of VolumeReaderFactoryInterface for default Volume
152 // constructor.
153 class VolumeReaderFactory : public VolumeReaderFactoryInterface {
154 public:
155 // VolumeReaderFactory does not own the volume pointer.
156 explicit VolumeReaderFactory(Volume* volume) : volume_(volume) {}
157
158 virtual VolumeReader* Create(int64_t archive_size) {
159 return new VolumeReaderJavaScriptStream(archive_size, volume_->requestor());
160 }
161
162 private:
163 Volume* volume_;
164 };
165
166 } // namespace
167
168 struct Volume::OpenFileArgs {
169 OpenFileArgs(const std::string& request_id,
170 int64_t index,
171 const std::string& encoding,
172 int64_t archive_size) : request_id(request_id),
173 index(index),
174 encoding(encoding),
175 archive_size(archive_size) {}
176 const std::string request_id;
177 const int64_t index;
178 const std::string encoding;
179 const int64_t archive_size;
180 };
181
182 Volume::Volume(const pp::InstanceHandle& instance_handle,
183 const std::string& file_system_id,
184 JavaScriptMessageSenderInterface* message_sender)
185 : volume_archive_(NULL),
186 file_system_id_(file_system_id),
187 message_sender_(message_sender),
188 worker_(instance_handle),
189 callback_factory_(this) {
190 requestor_ = new JavaScriptRequestor(this);
191 volume_archive_factory_ = new VolumeArchiveFactory();
192 volume_reader_factory_ = new VolumeReaderFactory(this);
193 // Delegating constructors only from c++11.
194 }
195
196 Volume::Volume(const pp::InstanceHandle& instance_handle,
197 const std::string& file_system_id,
198 JavaScriptMessageSenderInterface* message_sender,
199 VolumeArchiveFactoryInterface* volume_archive_factory,
200 VolumeReaderFactoryInterface* volume_reader_factory)
201 : volume_archive_(NULL),
202 file_system_id_(file_system_id),
203 message_sender_(message_sender),
204 worker_(instance_handle),
205 callback_factory_(this),
206 volume_archive_factory_(volume_archive_factory),
207 volume_reader_factory_(volume_reader_factory) {
208 requestor_ = new JavaScriptRequestor(this);
209 }
210
211 Volume::~Volume() {
212 worker_.Join();
213
214 if (volume_archive_) {
215 volume_archive_->Cleanup();
216 delete volume_archive_;
217 }
218
219 delete requestor_;
220 delete volume_archive_factory_;
221 delete volume_reader_factory_;
222 }
223
224 bool Volume::Init() {
225 return worker_.Start();
226 }
227
228 void Volume::ReadMetadata(const std::string& request_id,
229 const std::string& encoding,
230 int64_t archive_size) {
231 worker_.message_loop().PostWork(callback_factory_.NewCallback(
232 &Volume::ReadMetadataCallback, request_id, encoding, archive_size));
233 }
234
235 void Volume::OpenFile(const std::string& request_id,
236 int64_t index,
237 const std::string& encoding,
238 int64_t archive_size) {
239 worker_.message_loop().PostWork(callback_factory_.NewCallback(
240 &Volume::OpenFileCallback, OpenFileArgs(request_id, index, encoding,
241 archive_size)));
242 }
243
244 void Volume::CloseFile(const std::string& request_id,
245 const std::string& open_request_id) {
246 // Though close file could be executed on main thread, we send it to worker_
247 // in order to ensure thread safety.
248 worker_.message_loop().PostWork(callback_factory_.NewCallback(
249 &Volume::CloseFileCallback, request_id, open_request_id));
250 }
251
252 void Volume::ReadFile(const std::string& request_id,
253 const pp::VarDictionary& dictionary) {
254 worker_.message_loop().PostWork(callback_factory_.NewCallback(
255 &Volume::ReadFileCallback, request_id, dictionary));
256 }
257
258 void Volume::ReadChunkDone(const std::string& request_id,
259 const pp::VarArrayBuffer& array_buffer,
260 int64_t read_offset) {
261 PP_DCHECK(volume_archive_);
262
263 job_lock_.Acquire();
264 if (request_id == reader_request_id_) {
265 static_cast<VolumeReaderJavaScriptStream*>(volume_archive_->reader())->
266 SetBufferAndSignal(array_buffer, read_offset);
267 }
268 job_lock_.Release();
269 }
270
271 void Volume::ReadChunkError(const std::string& request_id) {
272 PP_DCHECK(volume_archive_);
273 job_lock_.Acquire();
274 if (request_id == reader_request_id_) {
275 static_cast<VolumeReaderJavaScriptStream*>(volume_archive_->reader())->
276 ReadErrorSignal();
277 }
278 job_lock_.Release();
279 }
280
281 void Volume::ReadPassphraseDone(const std::string& request_id,
282 const std::string& passphrase) {
283 PP_DCHECK(volume_archive_);
284
285 job_lock_.Acquire();
286 if (request_id == reader_request_id_) {
287 static_cast<VolumeReaderJavaScriptStream*>(volume_archive_->reader())->
288 SetPassphraseAndSignal(passphrase);
289 }
290 job_lock_.Release();
291 }
292
293 void Volume::ReadPassphraseError(const std::string& request_id) {
294 PP_DCHECK(volume_archive_);
295
296 job_lock_.Acquire();
297 if (request_id == reader_request_id_) {
298 static_cast<VolumeReaderJavaScriptStream*>(volume_archive_->reader())->
299 PassphraseErrorSignal();
300 }
301 job_lock_.Release();
302 }
303
304 void Volume::ReadMetadataCallback(int32_t /*result*/,
305 const std::string& request_id,
306 const std::string& encoding,
307 int64_t archive_size) {
308 if (volume_archive_) {
309 message_sender_->SendFileSystemError(
310 file_system_id_, request_id, "ALREADY_OPENED");
311 }
312
313 job_lock_.Acquire();
314 volume_archive_ = volume_archive_factory_->Create(
315 volume_reader_factory_->Create(archive_size));
316 static_cast<VolumeReaderJavaScriptStream*>(volume_archive_->reader())->
317 SetRequestId(request_id);
318 reader_request_id_ = request_id;
319 job_lock_.Release();
320
321 if (!volume_archive_->Init(encoding)) {
322 message_sender_->SendFileSystemError(
323 file_system_id_, request_id, volume_archive_->error_message());
324 ClearJob();
325 delete volume_archive_;
326 volume_archive_ = NULL;
327 return;
328 }
329
330 // Read and construct metadata.
331 pp::VarDictionary root_metadata = CreateEntry(-1, "" /* name */, true, 0, 0);
332
333 const char* path_name = NULL;
334 int64_t size = 0;
335 bool is_directory = false;
336 time_t modification_time = 0;
337 int64_t index = 0;
338
339 for (;;) {
340 VolumeArchive::Result ret = volume_archive_->GetNextHeader(
341 &path_name, &size, &is_directory, &modification_time);
342 if (ret == VolumeArchive::RESULT_FAIL) {
343 message_sender_->SendFileSystemError(
344 file_system_id_, request_id, volume_archive_->error_message());
345 ClearJob();
346 delete volume_archive_;
347 volume_archive_ = NULL;
348 return;
349 } else if (ret == VolumeArchive::RESULT_EOF)
350 break;
351
352 ConstructMetadata(index, path_name, size, is_directory, modification_time,
353 &root_metadata);
354
355 ++index;
356 }
357
358 ClearJob();
359
360 // Send metadata back to JavaScript.
361 message_sender_->SendReadMetadataDone(
362 file_system_id_, request_id, root_metadata);
363 }
364
365 void Volume::OpenFileCallback(int32_t /*result*/,
366 const OpenFileArgs& args) {
367 if (!volume_archive_) {
368 message_sender_->SendFileSystemError(
369 file_system_id_, args.request_id, "NOT_OPENED");
370 return;
371 }
372
373 job_lock_.Acquire();
374 if (!reader_request_id_.empty()) {
375 // It is illegal to open a file while another operation is in progress or
376 // another file is opened.
377 message_sender_->SendFileSystemError(
378 file_system_id_, args.request_id, "ILLEGAL");
379 job_lock_.Release();
380 return;
381 }
382 static_cast<VolumeReaderJavaScriptStream*>(volume_archive_->reader())->
383 SetRequestId(args.request_id);
384 reader_request_id_ = args.request_id;
385 job_lock_.Release();
386
387 if (!volume_archive_->SeekHeader(args.index)) {
388 message_sender_->SendFileSystemError(
389 file_system_id_, args.request_id, volume_archive_->error_message());
390 ClearJob();
391 return;
392 }
393
394 if (volume_archive_->GetNextHeader() == VolumeArchive::RESULT_FAIL) {
395 message_sender_->SendFileSystemError(
396 file_system_id_, args.request_id, volume_archive_->error_message());
397 ClearJob();
398 return;
399 }
400
401 // Send successful opened file response to NaCl.
402 message_sender_->SendOpenFileDone(file_system_id_, args.request_id);
403 }
404
405 void Volume::CloseFileCallback(int32_t /*result*/,
406 const std::string& request_id,
407 const std::string& open_request_id) {
408 job_lock_.Acquire();
409 reader_request_id_ = "";
410 job_lock_.Release();
411
412 message_sender_->SendCloseFileDone(
413 file_system_id_, request_id, open_request_id);
414 }
415
416 void Volume::ReadFileCallback(int32_t /*result*/,
417 const std::string& request_id,
418 const pp::VarDictionary& dictionary) {
419 if (!volume_archive_) {
420 message_sender_->SendFileSystemError(
421 file_system_id_, request_id, "NOT_OPENED");
422 return;
423 }
424
425 std::string open_request_id(
426 dictionary.Get(request::key::kOpenRequestId).AsString());
427 int64_t offset =
428 request::GetInt64FromString(dictionary, request::key::kOffset);
429 int64_t length =
430 request::GetInt64FromString(dictionary, request::key::kLength);
431 PP_DCHECK(length > 0); // JavaScript must not make requests with length <= 0.
432
433 job_lock_.Acquire();
434 if (open_request_id != reader_request_id_) {
435 // The file is not opened.
436 message_sender_->SendFileSystemError(
437 file_system_id_, request_id, "FILE_NOT_OPENED");
438 job_lock_.Release();
439 return;
440 }
441 job_lock_.Release();
442
443 // Decompress data and send it to JavaScript. Sending data is done in chunks
444 // depending on how many bytes VolumeArchive::ReadData returns.
445 int64_t left_length = length;
446 while (left_length > 0) {
447 const char* destination_buffer = NULL;
448 int64_t read_bytes = volume_archive_->ReadData(
449 offset, left_length, &destination_buffer);
450
451 if (read_bytes < 0) {
452 // Error messages should be sent to the read request (request_id), not
453 // open request (open_request_id), as the last one has finished and this
454 // is a read file.
455 message_sender_->SendFileSystemError(
456 file_system_id_, request_id, volume_archive_->error_message());
457
458 // Should not cleanup VolumeArchive as Volume::CloseFile will be called in
459 // case of failure.
460 return;
461 }
462
463 // Send response back to ReadFile request.
464 pp::VarArrayBuffer array_buffer(read_bytes);
465 if (read_bytes > 0) {
466 char* array_buffer_data = static_cast<char*>(array_buffer.Map());
467 memcpy(array_buffer_data, destination_buffer, read_bytes);
468 array_buffer.Unmap();
469 }
470
471 bool has_more_data = left_length - read_bytes > 0 && read_bytes > 0;
472 message_sender_->SendReadFileDone(
473 file_system_id_, request_id, array_buffer, has_more_data);
474
475 if (read_bytes == 0)
476 break; // No more available data.
477
478 left_length -= read_bytes;
479 offset += read_bytes;
480 }
481 volume_archive_->MaybeDecompressAhead();
482 }
483
484
485 void Volume::ClearJob() {
486 job_lock_.Acquire();
487 reader_request_id_ = "";
488 job_lock_.Release();
489 }
OLDNEW
« no previous file with comments | « ui/file_manager/zip_archiver/unpacker/cpp/volume.h ('k') | ui/file_manager/zip_archiver/unpacker/cpp/volume_archive.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698