| OLD | NEW |
| 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_reader_javascript_stream.h" | 5 #include "volume_reader_javascript_stream.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <limits> | 8 #include <limits> |
| 9 | 9 |
| 10 #include "archive.h" | 10 #include "third_party/zlib/contrib/minizip/unzip.h" |
| 11 |
| 11 #include "ppapi/cpp/logging.h" | 12 #include "ppapi/cpp/logging.h" |
| 12 | 13 |
| 13 VolumeReaderJavaScriptStream::VolumeReaderJavaScriptStream( | 14 VolumeReaderJavaScriptStream::VolumeReaderJavaScriptStream( |
| 14 int64_t archive_size, | 15 int64_t archive_size, |
| 15 JavaScriptRequestorInterface* requestor) | 16 JavaScriptRequestorInterface* requestor) |
| 16 : archive_size_(archive_size), | 17 : archive_size_(archive_size), |
| 17 requestor_(requestor), | 18 requestor_(requestor), |
| 18 available_data_(false), | 19 available_data_(false), |
| 19 read_error_(false), | 20 read_error_(false), |
| 20 passphrase_error_(false), | 21 passphrase_error_(false), |
| 21 offset_(0), | 22 offset_(0), |
| 22 last_read_chunk_offset_(-1) /* For first call -1 will force a chunk | 23 last_read_chunk_offset_(-1) /* For first call -1 will force a chunk |
| 23 request from JavaScript as offset | 24 request from JavaScript as offset |
| 24 parameter is 0. */, | 25 parameter is 0. */, |
| 25 read_ahead_array_buffer_ptr_(&first_array_buffer_) { | 26 read_ahead_array_buffer_ptr_(&first_array_buffer_) { |
| 26 pthread_mutex_init(&shared_state_lock_, NULL); | 27 pthread_mutex_init(&shared_state_lock_, nullptr); |
| 27 pthread_cond_init(&available_data_cond_, NULL); | 28 pthread_cond_init(&available_data_cond_, nullptr); |
| 28 pthread_cond_init(&available_passphrase_cond_, NULL); | 29 pthread_cond_init(&available_passphrase_cond_, nullptr); |
| 29 | 30 |
| 30 // Dummy Map the second buffer as first buffer is used for read ahead by | 31 // 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 // read_ahead_array_buffer_ptr_. This operation is required in order for Unmap |
| 32 // to correctly work in the destructor and VolumeReaderJavaScriptStream::Read. | 33 // to correctly work in the destructor and VolumeReaderJavaScriptStream::Read. |
| 33 second_array_buffer_.Map(); | 34 second_array_buffer_.Map(); |
| 34 } | 35 } |
| 35 | 36 |
| 36 VolumeReaderJavaScriptStream::~VolumeReaderJavaScriptStream() { | 37 VolumeReaderJavaScriptStream::~VolumeReaderJavaScriptStream() { |
| 37 pthread_mutex_destroy(&shared_state_lock_); | 38 pthread_mutex_destroy(&shared_state_lock_); |
| 38 pthread_cond_destroy(&available_data_cond_); | 39 pthread_cond_destroy(&available_data_cond_); |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 113 | 114 |
| 114 pthread_mutex_lock(&shared_state_lock_); | 115 pthread_mutex_lock(&shared_state_lock_); |
| 115 | 116 |
| 116 // No more data, so signal end of reading. | 117 // No more data, so signal end of reading. |
| 117 if (offset_ >= archive_size_) { | 118 if (offset_ >= archive_size_) { |
| 118 pthread_mutex_unlock(&shared_state_lock_); | 119 pthread_mutex_unlock(&shared_state_lock_); |
| 119 return 0; | 120 return 0; |
| 120 } | 121 } |
| 121 | 122 |
| 122 // Call in case of first read or read after Seek and Skip. | 123 // Call in case of first read or read after Seek and Skip. |
| 123 if (last_read_chunk_offset_ != offset_) | 124 if (last_read_chunk_offset_ != offset_ || !available_data_) |
| 124 RequestChunk(bytes_to_read); | 125 RequestChunk(bytes_to_read); |
| 125 | 126 |
| 126 if (!available_data_) { | 127 if (!available_data_) { |
| 127 // Wait for data from JavaScript. | 128 // Wait for data from JavaScript. |
| 128 while (!available_data_) { // Check again available data as first call | 129 while (!available_data_) { // Check again available data as first call |
| 129 // was done outside guarded zone. | 130 // was done outside guarded zone. |
| 130 if (read_error_) { | 131 if (read_error_) { |
| 131 pthread_mutex_unlock(&shared_state_lock_); | 132 pthread_mutex_unlock(&shared_state_lock_); |
| 132 return ARCHIVE_FATAL; | 133 return -1; |
| 133 } | 134 } |
| 134 pthread_cond_wait(&available_data_cond_, &shared_state_lock_); | 135 pthread_cond_wait(&available_data_cond_, &shared_state_lock_); |
| 135 } | 136 } |
| 136 } | 137 } |
| 137 | 138 |
| 138 if (read_error_) { // Read ahead failed. | 139 if (read_error_) { // Read ahead failed. |
| 139 pthread_mutex_unlock(&shared_state_lock_); | 140 pthread_mutex_unlock(&shared_state_lock_); |
| 140 return ARCHIVE_FATAL; | 141 return -1; |
| 141 } | 142 } |
| 142 | 143 |
| 143 // Make data available for libarchive custom read. No need to lock this part. | 144 // Make data available for libarchive custom read. No need to lock this part. |
| 144 // The reason is that VolumeReaderJavaScriptStream::RequestChunk is the only | 145 // The reason is that VolumeReaderJavaScriptStream::RequestChunk is the only |
| 145 // function that can set available_data_ back to false and let | 146 // function that can set available_data_ back to false and let |
| 146 // VolumeReaderJavaScriptStream::SetBufferAndSignal overwrite the buffer. But | 147 // VolumeReaderJavaScriptStream::SetBufferAndSignal overwrite the buffer. But |
| 147 // reading ahead is done only at the end of this function after the buffers | 148 // reading ahead is done only at the end of this function after the buffers |
| 148 // are switched. | 149 // are switched. |
| 149 *destination_buffer = read_ahead_array_buffer_ptr_->Map(); | 150 *destination_buffer = read_ahead_array_buffer_ptr_->Map(); |
| 150 int64_t bytes_read = | 151 int64_t bytes_read = |
| (...skipping 29 matching lines...) Expand all Loading... |
| 180 pthread_mutex_unlock(&shared_state_lock_); | 181 pthread_mutex_unlock(&shared_state_lock_); |
| 181 | 182 |
| 182 return bytes_read; | 183 return bytes_read; |
| 183 } | 184 } |
| 184 | 185 |
| 185 int64_t VolumeReaderJavaScriptStream::Seek(int64_t offset, int whence) { | 186 int64_t VolumeReaderJavaScriptStream::Seek(int64_t offset, int whence) { |
| 186 pthread_mutex_lock(&shared_state_lock_); | 187 pthread_mutex_lock(&shared_state_lock_); |
| 187 | 188 |
| 188 int64_t new_offset = offset_; | 189 int64_t new_offset = offset_; |
| 189 switch (whence) { | 190 switch (whence) { |
| 190 case SEEK_SET: | 191 case ZLIB_FILEFUNC_SEEK_SET: |
| 191 new_offset = offset; | 192 new_offset = offset; |
| 192 break; | 193 break; |
| 193 case SEEK_CUR: | 194 case ZLIB_FILEFUNC_SEEK_CUR: |
| 194 new_offset += offset; | 195 new_offset += offset; |
| 195 break; | 196 break; |
| 196 case SEEK_END: | 197 case ZLIB_FILEFUNC_SEEK_END: |
| 197 new_offset = archive_size_ + offset; | 198 new_offset = archive_size_ + offset; |
| 198 break; | 199 break; |
| 199 default: | 200 default: |
| 200 PP_NOTREACHED(); | 201 PP_NOTREACHED(); |
| 201 pthread_mutex_unlock(&shared_state_lock_); | 202 pthread_mutex_unlock(&shared_state_lock_); |
| 202 return ARCHIVE_FATAL; | 203 return -1; |
| 203 } | 204 } |
| 204 | 205 |
| 205 if (new_offset < 0 || new_offset > archive_size_) { | 206 if (new_offset < 0) { |
| 206 pthread_mutex_unlock(&shared_state_lock_); | 207 pthread_mutex_unlock(&shared_state_lock_); |
| 207 return ARCHIVE_FATAL; | 208 return -1; |
| 208 } | 209 } |
| 209 | 210 |
| 210 offset_ = new_offset; | 211 offset_ = new_offset; |
| 211 pthread_mutex_unlock(&shared_state_lock_); | 212 pthread_mutex_unlock(&shared_state_lock_); |
| 212 | 213 |
| 213 return new_offset; | 214 return new_offset; |
| 214 } | 215 } |
| 215 | 216 |
| 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) { | 217 void VolumeReaderJavaScriptStream::SetRequestId(const std::string& request_id) { |
| 235 // No lock necessary, as request_id is used by one thread only. | 218 // No lock necessary, as request_id is used by one thread only. |
| 236 request_id_ = request_id; | 219 request_id_ = request_id; |
| 237 } | 220 } |
| 238 | 221 |
| 239 const char* VolumeReaderJavaScriptStream::Passphrase() { | 222 const char* VolumeReaderJavaScriptStream::Passphrase() { |
| 240 // The error is not recoverable. Once passphrase fails to be provided, it is | 223 // 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 | 224 // never asked again. Note, that still users are able to retry entering the |
| 242 // password, unless they click Cancel. | 225 // password, unless they click Cancel. |
| 243 pthread_mutex_lock(&shared_state_lock_); | 226 pthread_mutex_lock(&shared_state_lock_); |
| 244 if (passphrase_error_) { | 227 if (passphrase_error_) { |
| 245 pthread_mutex_unlock(&shared_state_lock_); | 228 pthread_mutex_unlock(&shared_state_lock_); |
| 246 return NULL; | 229 return nullptr; |
| 247 } | 230 } |
| 248 pthread_mutex_unlock(&shared_state_lock_); | 231 pthread_mutex_unlock(&shared_state_lock_); |
| 249 | 232 |
| 250 // Request the passphrase outside of the lock. | 233 // Request the passphrase outside of the lock. |
| 251 requestor_->RequestPassphrase(request_id_); | 234 requestor_->RequestPassphrase(request_id_); |
| 252 | 235 |
| 253 pthread_mutex_lock(&shared_state_lock_); | 236 pthread_mutex_lock(&shared_state_lock_); |
| 254 // Wait for the passphrase from JavaScript. | 237 // Wait for the passphrase from JavaScript. |
| 255 pthread_cond_wait(&available_passphrase_cond_, &shared_state_lock_); | 238 pthread_cond_wait(&available_passphrase_cond_, &shared_state_lock_); |
| 256 const char* result = NULL; | 239 const char* result = nullptr; |
| 257 if (!passphrase_error_) | 240 if (!passphrase_error_) |
| 258 result = strdup(available_passphrase_.c_str()); | 241 result = strdup(available_passphrase_.c_str()); |
| 259 pthread_mutex_unlock(&shared_state_lock_); | 242 pthread_mutex_unlock(&shared_state_lock_); |
| 260 | 243 |
| 261 return result; | 244 return result; |
| 262 } | 245 } |
| 263 | 246 |
| 264 void VolumeReaderJavaScriptStream::RequestChunk(int64_t length) { | 247 void VolumeReaderJavaScriptStream::RequestChunk(int64_t length) { |
| 265 // Read next chunk only if not at the end of archive. | 248 // Read next chunk only if not at the end of archive. |
| 266 if (archive_size_ <= offset_) | 249 if (archive_size_ <= offset_) |
| 267 return; | 250 return; |
| 268 | 251 |
| 269 int64_t bytes_to_read = | 252 int64_t bytes_to_read = |
| 270 std::min(length, archive_size_ - offset_ /* Positive check above. */); | 253 std::min(length, archive_size_ - offset_ /* Positive check above. */); |
| 271 available_data_ = false; | 254 available_data_ = false; |
| 272 | 255 |
| 273 requestor_->RequestFileChunk(request_id_, offset_, bytes_to_read); | 256 requestor_->RequestFileChunk(request_id_, offset_, bytes_to_read); |
| 274 } | 257 } |
| OLD | NEW |