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

Side by Side Diff: ui/file_manager/zip_archiver/unpacker/cpp/volume_reader_javascript_stream.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_reader_javascript_stream.h"
6
7 #include <algorithm>
8 #include <limits>
9
10 #include "archive.h"
11 #include "ppapi/cpp/logging.h"
12
13 VolumeReaderJavaScriptStream::VolumeReaderJavaScriptStream(
14 int64_t archive_size,
15 JavaScriptRequestorInterface* requestor)
16 : archive_size_(archive_size),
17 requestor_(requestor),
18 available_data_(false),
19 read_error_(false),
20 passphrase_error_(false),
21 offset_(0),
22 last_read_chunk_offset_(-1) /* For first call -1 will force a chunk
23 request from JavaScript as offset
24 parameter is 0. */,
25 read_ahead_array_buffer_ptr_(&first_array_buffer_) {
26 pthread_mutex_init(&shared_state_lock_, NULL);
27 pthread_cond_init(&available_data_cond_, NULL);
28 pthread_cond_init(&available_passphrase_cond_, NULL);
29
30 // Dummy Map the second buffer as first buffer is used for read ahead by
31 // read_ahead_array_buffer_ptr_. This operation is required in order for Unmap
32 // to correctly work in the destructor and VolumeReaderJavaScriptStream::Read.
33 second_array_buffer_.Map();
34 }
35
36 VolumeReaderJavaScriptStream::~VolumeReaderJavaScriptStream() {
37 pthread_mutex_destroy(&shared_state_lock_);
38 pthread_cond_destroy(&available_data_cond_);
39 pthread_cond_destroy(&available_passphrase_cond_);
40
41 // Unmap last mapped buffer. This is the other buffer to
42 // read_ahead_array_buffer_ptr_ as read_ahead_array_buffer_ptr_ must be
43 // available for SetBufferAndSignal to overwrite.
44 if (read_ahead_array_buffer_ptr_ != &first_array_buffer_)
45 first_array_buffer_.Unmap();
46 else
47 second_array_buffer_.Unmap();
48 };
49
50 void VolumeReaderJavaScriptStream::SetBufferAndSignal(
51 const pp::VarArrayBuffer& array_buffer,
52 int64_t read_offset) {
53 PP_DCHECK(read_offset >= 0);
54
55 // Ignore read ahead in case offset was changed using Skip or Seek and in case
56 // we already have available data. This can happen in case of 2+ RequestChunk
57 // calls done in parallel as a result of calling Read, Skip and Seek one after
58 // another really fast. The usage of the buffer is not guarded so in case we
59 // overwrite *read_ahead_array_buffer_ptr_ we will end up with memory
60 // corruption.
61 // In case read_offset and offset_ are different, then the read ahead data is
62 // not valid anymore, but in case they are equal and available_data_ is set to
63 // true then the second read ahead data is the same as the first read ahead
64 // data so we can just ignore it.
65
66 // TODO(mtomasz): We don't need to discard everything. Sometimes part of the
67 // buffer can still be used. In such case we should use it. That can greatly
68 // improve traversing headers for archives with small files!
69
70 pthread_mutex_lock(&shared_state_lock_);
71 if (read_offset == offset_ && !available_data_ && !read_error_) {
72 // Signal VolumeReaderJavaScriptStream::Read to continue execution. Copies
73 // buffer locally so libarchive has the buffer in memory when working with
74 // it. Though we acquire a lock here this call is blocking only for a few
75 // moments as VolumeReaderJavaScriptStream::Read will release the lock with
76 // pthread_cond_wait. So we cannot arrive at a deadlock that will block the
77 // main thread.
78
79 *read_ahead_array_buffer_ptr_ = array_buffer; // Copy operation.
80 available_data_ = true;
81
82 pthread_cond_signal(&available_data_cond_);
83 }
84 pthread_mutex_unlock(&shared_state_lock_);
85 }
86
87 void VolumeReaderJavaScriptStream::ReadErrorSignal() {
88 pthread_mutex_lock(&shared_state_lock_);
89 read_error_ = true; // Read error from JavaScript.
90 pthread_cond_signal(&available_data_cond_);
91 pthread_mutex_unlock(&shared_state_lock_);
92 }
93
94 void VolumeReaderJavaScriptStream::SetPassphraseAndSignal(
95 const std::string& passphrase) {
96 pthread_mutex_lock(&shared_state_lock_);
97 // Signal VolumeReaderJavaScriptStream::Passphrase to continue execution.
98 available_passphrase_ = passphrase;
99 pthread_cond_signal(&available_passphrase_cond_);
100 pthread_mutex_unlock(&shared_state_lock_);
101 }
102
103 void VolumeReaderJavaScriptStream::PassphraseErrorSignal() {
104 pthread_mutex_lock(&shared_state_lock_);
105 passphrase_error_ = true; // Passphrase error from JavaScript.
106 pthread_cond_signal(&available_passphrase_cond_);
107 pthread_mutex_unlock(&shared_state_lock_);
108 }
109
110 int64_t VolumeReaderJavaScriptStream::Read(int64_t bytes_to_read,
111 const void** destination_buffer) {
112 PP_DCHECK(bytes_to_read > 0);
113
114 pthread_mutex_lock(&shared_state_lock_);
115
116 // No more data, so signal end of reading.
117 if (offset_ >= archive_size_) {
118 pthread_mutex_unlock(&shared_state_lock_);
119 return 0;
120 }
121
122 // Call in case of first read or read after Seek and Skip.
123 if (last_read_chunk_offset_ != offset_)
124 RequestChunk(bytes_to_read);
125
126 if (!available_data_) {
127 // Wait for data from JavaScript.
128 while (!available_data_) { // Check again available data as first call
129 // was done outside guarded zone.
130 if (read_error_) {
131 pthread_mutex_unlock(&shared_state_lock_);
132 return ARCHIVE_FATAL;
133 }
134 pthread_cond_wait(&available_data_cond_, &shared_state_lock_);
135 }
136 }
137
138 if (read_error_) { // Read ahead failed.
139 pthread_mutex_unlock(&shared_state_lock_);
140 return ARCHIVE_FATAL;
141 }
142
143 // Make data available for libarchive custom read. No need to lock this part.
144 // The reason is that VolumeReaderJavaScriptStream::RequestChunk is the only
145 // function that can set available_data_ back to false and let
146 // VolumeReaderJavaScriptStream::SetBufferAndSignal overwrite the buffer. But
147 // reading ahead is done only at the end of this function after the buffers
148 // are switched.
149 *destination_buffer = read_ahead_array_buffer_ptr_->Map();
150 int64_t bytes_read =
151 std::min(static_cast<int64_t>(read_ahead_array_buffer_ptr_->ByteLength()),
152 bytes_to_read);
153
154 offset_ += bytes_read;
155 last_read_chunk_offset_ = offset_;
156
157 // Ask for more data from JavaScript in the other buffer. This is the only
158 // time when we switch buffers. The reason is that libarchive must
159 // always work on valid data and that data must be available until next
160 // VolumeReaderJavaScriptStream::Read call, and as the data can be received
161 // at any time from JavaScript, we need a buffer to store it in case of
162 // reading ahead.
163 read_ahead_array_buffer_ptr_ =
164 read_ahead_array_buffer_ptr_ != &first_array_buffer_
165 ? &first_array_buffer_
166 : &second_array_buffer_;
167
168 // Unmap old buffer. Only Read and constructor can Map the buffers so Read and
169 // destructor should be the one to Unmap them. This will work because it is
170 // called before RequestChunk which is the only method that overwrites the
171 // buffer. The constructor should also Map a default pp::VarArrayBuffer and
172 // destructor Unmap the last used array buffer (which is the other buffer than
173 // read_ahead_array_buffer_ptr_). Unfortunately it's not clear from the
174 // API description if this call is done automatically on pp::VarArrayBuffer
175 // destructor.
176 read_ahead_array_buffer_ptr_->Unmap();
177
178 // Read ahead next chunk with a length similar to current read.
179 RequestChunk(bytes_to_read);
180 pthread_mutex_unlock(&shared_state_lock_);
181
182 return bytes_read;
183 }
184
185 int64_t VolumeReaderJavaScriptStream::Seek(int64_t offset, int whence) {
186 pthread_mutex_lock(&shared_state_lock_);
187
188 int64_t new_offset = offset_;
189 switch (whence) {
190 case SEEK_SET:
191 new_offset = offset;
192 break;
193 case SEEK_CUR:
194 new_offset += offset;
195 break;
196 case SEEK_END:
197 new_offset = archive_size_ + offset;
198 break;
199 default:
200 PP_NOTREACHED();
201 pthread_mutex_unlock(&shared_state_lock_);
202 return ARCHIVE_FATAL;
203 }
204
205 if (new_offset < 0 || new_offset > archive_size_) {
206 pthread_mutex_unlock(&shared_state_lock_);
207 return ARCHIVE_FATAL;
208 }
209
210 offset_ = new_offset;
211 pthread_mutex_unlock(&shared_state_lock_);
212
213 return new_offset;
214 }
215
216 int64_t VolumeReaderJavaScriptStream::Skip(int64_t bytes_to_skip) {
217 pthread_mutex_lock(&shared_state_lock_);
218 // Invalid bytes_to_skip. This "if" can be triggered for corrupted archives.
219 // We return 0 instead of ARCHIVE_FATAL in order for libarchive to use normal
220 // Read and return the correct error. In case we return ARCHIVE_FATAL here
221 // then libarchive just stops without telling us why it wasn't able to
222 // process the archive.
223 if (archive_size_ - offset_ < bytes_to_skip || bytes_to_skip < 0) {
224 pthread_mutex_unlock(&shared_state_lock_);
225 return 0;
226 }
227
228 offset_ += bytes_to_skip;
229 pthread_mutex_unlock(&shared_state_lock_);
230
231 return bytes_to_skip;
232 }
233
234 void VolumeReaderJavaScriptStream::SetRequestId(const std::string& request_id) {
235 // No lock necessary, as request_id is used by one thread only.
236 request_id_ = request_id;
237 }
238
239 const char* VolumeReaderJavaScriptStream::Passphrase() {
240 // The error is not recoverable. Once passphrase fails to be provided, it is
241 // never asked again. Note, that still users are able to retry entering the
242 // password, unless they click Cancel.
243 pthread_mutex_lock(&shared_state_lock_);
244 if (passphrase_error_) {
245 pthread_mutex_unlock(&shared_state_lock_);
246 return NULL;
247 }
248 pthread_mutex_unlock(&shared_state_lock_);
249
250 // Request the passphrase outside of the lock.
251 requestor_->RequestPassphrase(request_id_);
252
253 pthread_mutex_lock(&shared_state_lock_);
254 // Wait for the passphrase from JavaScript.
255 pthread_cond_wait(&available_passphrase_cond_, &shared_state_lock_);
256 const char* result = NULL;
257 if (!passphrase_error_)
258 result = strdup(available_passphrase_.c_str());
259 pthread_mutex_unlock(&shared_state_lock_);
260
261 return result;
262 }
263
264 void VolumeReaderJavaScriptStream::RequestChunk(int64_t length) {
265 // Read next chunk only if not at the end of archive.
266 if (archive_size_ <= offset_)
267 return;
268
269 int64_t bytes_to_read =
270 std::min(length, archive_size_ - offset_ /* Positive check above. */);
271 available_data_ = false;
272
273 requestor_->RequestFileChunk(request_id_, offset_, bytes_to_read);
274 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698