file_stream_reader.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright 2014 The Chromium 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 "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader.h" 6 7#include "base/debug/trace_event.h" 8#include "base/files/file.h" 9#include "base/memory/ref_counted.h" 10#include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h" 11#include "chrome/browser/chromeos/file_system_provider/mount_path_util.h" 12#include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h" 13#include "content/public/browser/browser_thread.h" 14#include "net/base/io_buffer.h" 15#include "net/base/net_errors.h" 16 17using content::BrowserThread; 18 19namespace chromeos { 20namespace file_system_provider { 21namespace { 22 23// Dicards the callback from CloseFile(). 24void EmptyStatusCallback(base::File::Error /* result */) { 25} 26 27// Converts net::CompletionCallback to net::Int64CompletionCallback. 28void Int64ToIntCompletionCallback(net::CompletionCallback callback, 29 int64 result) { 30 callback.Run(static_cast<int>(result)); 31} 32 33} // namespace 34 35class FileStreamReader::OperationRunner 36 : public base::RefCountedThreadSafe<FileStreamReader::OperationRunner> { 37 public: 38 OperationRunner() : file_handle_(-1) {} 39 40 // Opens a file for reading and calls the completion callback. Must be called 41 // on UI thread. 42 void OpenFileOnUIThread( 43 const storage::FileSystemURL& url, 44 const storage::AsyncFileUtil::StatusCallback& callback) { 45 DCHECK_CURRENTLY_ON(BrowserThread::UI); 46 47 util::FileSystemURLParser parser(url); 48 if (!parser.Parse()) { 49 BrowserThread::PostTask( 50 BrowserThread::IO, 51 FROM_HERE, 52 base::Bind(callback, base::File::FILE_ERROR_SECURITY)); 53 return; 54 } 55 56 file_system_ = parser.file_system()->GetWeakPtr(); 57 file_path_ = parser.file_path(); 58 abort_callback_ = parser.file_system()->OpenFile( 59 file_path_, 60 ProvidedFileSystemInterface::OPEN_FILE_MODE_READ, 61 base::Bind( 62 &OperationRunner::OnOpenFileCompletedOnUIThread, this, callback)); 63 } 64 65 // Closes a file. Ignores result, since it is called from a constructor. 66 // Must be called on UI thread. 67 void CloseFileOnUIThread() { 68 DCHECK_CURRENTLY_ON(BrowserThread::UI); 69 if (file_system_.get() && file_handle_ != -1) { 70 // Closing a file must not be aborted, since we could end up on files 71 // which are never closed. 72 file_system_->CloseFile(file_handle_, base::Bind(&EmptyStatusCallback)); 73 } 74 } 75 76 // Requests reading contents of a file. In case of either success or a failure 77 // |callback| is executed. It can be called many times, until |has_more| is 78 // set to false. This function guarantees that it will succeed only if the 79 // file has not been changed while reading. Must be called on UI thread. 80 void ReadFileOnUIThread( 81 scoped_refptr<net::IOBuffer> buffer, 82 int64 offset, 83 int length, 84 const ProvidedFileSystemInterface::ReadChunkReceivedCallback& callback) { 85 DCHECK_CURRENTLY_ON(BrowserThread::UI); 86 87 // If the file system got unmounted, then abort the reading operation. 88 if (!file_system_.get()) { 89 BrowserThread::PostTask( 90 BrowserThread::IO, 91 FROM_HERE, 92 base::Bind( 93 callback, 0, false /* has_more */, base::File::FILE_ERROR_ABORT)); 94 return; 95 } 96 97 abort_callback_ = file_system_->ReadFile( 98 file_handle_, 99 buffer.get(), 100 offset, 101 length, 102 base::Bind( 103 &OperationRunner::OnReadFileCompletedOnUIThread, this, callback)); 104 } 105 106 // Requests metadata of a file. In case of either succes or a failure, 107 // |callback| is executed. Must be called on UI thread. 108 void GetMetadataOnUIThread( 109 const ProvidedFileSystemInterface::GetMetadataCallback& callback) { 110 DCHECK_CURRENTLY_ON(BrowserThread::UI); 111 112 // If the file system got unmounted, then abort the get length operation. 113 if (!file_system_.get()) { 114 BrowserThread::PostTask( 115 BrowserThread::IO, 116 FROM_HERE, 117 base::Bind(callback, 118 base::Passed(make_scoped_ptr<EntryMetadata>(NULL)), 119 base::File::FILE_ERROR_ABORT)); 120 return; 121 } 122 123 abort_callback_ = file_system_->GetMetadata( 124 file_path_, 125 ProvidedFileSystemInterface::METADATA_FIELD_DEFAULT, 126 base::Bind(&OperationRunner::OnGetMetadataCompletedOnUIThread, 127 this, 128 callback)); 129 } 130 131 // Aborts the most recent operation (if exists), and calls the callback. 132 void AbortOnUIThread(const storage::AsyncFileUtil::StatusCallback& callback) { 133 DCHECK_CURRENTLY_ON(BrowserThread::UI); 134 135 if (abort_callback_.is_null()) { 136 // No operation to be cancelled. At most a callback call, which will be 137 // discarded. 138 BrowserThread::PostTask(BrowserThread::IO, 139 FROM_HERE, 140 base::Bind(callback, base::File::FILE_OK)); 141 return; 142 } 143 144 const ProvidedFileSystemInterface::AbortCallback abort_callback = 145 abort_callback_; 146 abort_callback_ = ProvidedFileSystemInterface::AbortCallback(); 147 abort_callback.Run(base::Bind( 148 &OperationRunner::OnAbortCompletedOnUIThread, this, callback)); 149 } 150 151 private: 152 friend class base::RefCountedThreadSafe<OperationRunner>; 153 154 virtual ~OperationRunner() {} 155 156 // Remembers a file handle for further operations and forwards the result to 157 // the IO thread. 158 void OnOpenFileCompletedOnUIThread( 159 const storage::AsyncFileUtil::StatusCallback& callback, 160 int file_handle, 161 base::File::Error result) { 162 DCHECK_CURRENTLY_ON(BrowserThread::UI); 163 164 file_handle_ = file_handle; 165 BrowserThread::PostTask( 166 BrowserThread::IO, FROM_HERE, base::Bind(callback, result)); 167 } 168 169 // Forwards a metadata to the IO thread. 170 void OnGetMetadataCompletedOnUIThread( 171 const ProvidedFileSystemInterface::GetMetadataCallback& callback, 172 scoped_ptr<EntryMetadata> metadata, 173 base::File::Error result) { 174 DCHECK_CURRENTLY_ON(BrowserThread::UI); 175 BrowserThread::PostTask( 176 BrowserThread::IO, 177 FROM_HERE, 178 base::Bind(callback, base::Passed(&metadata), result)); 179 } 180 181 // Forwards a response of reading from a file to the IO thread. 182 void OnReadFileCompletedOnUIThread( 183 const ProvidedFileSystemInterface::ReadChunkReceivedCallback& 184 chunk_received_callback, 185 int chunk_length, 186 bool has_more, 187 base::File::Error result) { 188 DCHECK_CURRENTLY_ON(BrowserThread::UI); 189 BrowserThread::PostTask( 190 BrowserThread::IO, 191 FROM_HERE, 192 base::Bind(chunk_received_callback, chunk_length, has_more, result)); 193 } 194 195 // Forwards a response of aborting an operation to the IO thread. 196 void OnAbortCompletedOnUIThread( 197 const storage::AsyncFileUtil::StatusCallback& callback, 198 base::File::Error result) { 199 DCHECK_CURRENTLY_ON(BrowserThread::UI); 200 BrowserThread::PostTask( 201 BrowserThread::IO, FROM_HERE, base::Bind(callback, result)); 202 } 203 204 ProvidedFileSystemInterface::AbortCallback abort_callback_; 205 base::WeakPtr<ProvidedFileSystemInterface> file_system_; 206 base::FilePath file_path_; 207 int file_handle_; 208 209 DISALLOW_COPY_AND_ASSIGN(OperationRunner); 210}; 211 212FileStreamReader::FileStreamReader(storage::FileSystemContext* context, 213 const storage::FileSystemURL& url, 214 int64 initial_offset, 215 const base::Time& expected_modification_time) 216 : url_(url), 217 current_offset_(initial_offset), 218 current_length_(0), 219 expected_modification_time_(expected_modification_time), 220 runner_(new OperationRunner), 221 state_(NOT_INITIALIZED), 222 weak_ptr_factory_(this) { 223} 224 225FileStreamReader::~FileStreamReader() { 226 // FileStreamReader doesn't have a Cancel() method like in FileStreamWriter. 227 // Therefore, aborting is done from the destructor. 228 BrowserThread::PostTask(BrowserThread::UI, 229 FROM_HERE, 230 base::Bind(&OperationRunner::AbortOnUIThread, 231 runner_, 232 base::Bind(&EmptyStatusCallback))); 233 234 BrowserThread::PostTask( 235 BrowserThread::UI, 236 FROM_HERE, 237 base::Bind(&OperationRunner::CloseFileOnUIThread, runner_)); 238} 239 240void FileStreamReader::Initialize( 241 const base::Closure& pending_closure, 242 const net::Int64CompletionCallback& error_callback) { 243 DCHECK_EQ(NOT_INITIALIZED, state_); 244 state_ = INITIALIZING; 245 246 BrowserThread::PostTask( 247 BrowserThread::UI, 248 FROM_HERE, 249 base::Bind(&OperationRunner::OpenFileOnUIThread, 250 runner_, 251 url_, 252 base::Bind(&FileStreamReader::OnOpenFileCompleted, 253 weak_ptr_factory_.GetWeakPtr(), 254 pending_closure, 255 error_callback))); 256} 257 258void FileStreamReader::OnOpenFileCompleted( 259 const base::Closure& pending_closure, 260 const net::Int64CompletionCallback& error_callback, 261 base::File::Error result) { 262 DCHECK_CURRENTLY_ON(BrowserThread::IO); 263 DCHECK_EQ(INITIALIZING, state_); 264 265 // In case of an error, return immediately using the |error_callback| of the 266 // Read() or GetLength() pending request. 267 if (result != base::File::FILE_OK) { 268 state_ = FAILED; 269 error_callback.Run(net::FileErrorToNetError(result)); 270 return; 271 } 272 273 DCHECK_EQ(base::File::FILE_OK, result); 274 275 // Verify the last modification time. 276 BrowserThread::PostTask( 277 BrowserThread::UI, 278 FROM_HERE, 279 base::Bind(&OperationRunner::GetMetadataOnUIThread, 280 runner_, 281 base::Bind(&FileStreamReader::OnInitializeCompleted, 282 weak_ptr_factory_.GetWeakPtr(), 283 pending_closure, 284 error_callback))); 285} 286 287void FileStreamReader::OnInitializeCompleted( 288 const base::Closure& pending_closure, 289 const net::Int64CompletionCallback& error_callback, 290 scoped_ptr<EntryMetadata> metadata, 291 base::File::Error result) { 292 DCHECK_CURRENTLY_ON(BrowserThread::IO); 293 DCHECK_EQ(INITIALIZING, state_); 294 295 // In case of an error, abort. 296 if (result != base::File::FILE_OK) { 297 state_ = FAILED; 298 error_callback.Run(net::FileErrorToNetError(result)); 299 return; 300 } 301 302 // If the file modification time has changed, then abort. Note, that the file 303 // may be changed without affecting the modification time. 304 DCHECK(metadata.get()); 305 if (!expected_modification_time_.is_null() && 306 metadata->modification_time != expected_modification_time_) { 307 state_ = FAILED; 308 error_callback.Run(net::ERR_UPLOAD_FILE_CHANGED); 309 return; 310 } 311 312 DCHECK_EQ(base::File::FILE_OK, result); 313 state_ = INITIALIZED; 314 315 // Run the task waiting for the initialization to be completed. 316 pending_closure.Run(); 317} 318 319int FileStreamReader::Read(net::IOBuffer* buffer, 320 int buffer_length, 321 const net::CompletionCallback& callback) { 322 DCHECK_CURRENTLY_ON(BrowserThread::IO); 323 TRACE_EVENT_ASYNC_BEGIN1("file_system_provider", 324 "FileStreamReader::Read", 325 this, 326 "buffer_length", 327 buffer_length); 328 329 switch (state_) { 330 case NOT_INITIALIZED: 331 // Lazily initialize with the first call to Read(). 332 Initialize(base::Bind(&FileStreamReader::ReadAfterInitialized, 333 weak_ptr_factory_.GetWeakPtr(), 334 make_scoped_refptr(buffer), 335 buffer_length, 336 base::Bind(&FileStreamReader::OnReadCompleted, 337 weak_ptr_factory_.GetWeakPtr(), 338 callback)), 339 base::Bind(&Int64ToIntCompletionCallback, 340 base::Bind(&FileStreamReader::OnReadCompleted, 341 weak_ptr_factory_.GetWeakPtr(), 342 callback))); 343 break; 344 345 case INITIALIZING: 346 NOTREACHED(); 347 break; 348 349 case INITIALIZED: 350 ReadAfterInitialized(buffer, 351 buffer_length, 352 base::Bind(&FileStreamReader::OnReadCompleted, 353 weak_ptr_factory_.GetWeakPtr(), 354 callback)); 355 break; 356 357 case FAILED: 358 NOTREACHED(); 359 break; 360 } 361 362 return net::ERR_IO_PENDING; 363} 364 365void FileStreamReader::OnReadCompleted(net::CompletionCallback callback, 366 int result) { 367 DCHECK_CURRENTLY_ON(BrowserThread::IO); 368 callback.Run(static_cast<int>(result)); 369 TRACE_EVENT_ASYNC_END0( 370 "file_system_provider", "FileStreamReader::Read", this); 371} 372 373int64 FileStreamReader::GetLength( 374 const net::Int64CompletionCallback& callback) { 375 DCHECK_CURRENTLY_ON(BrowserThread::IO); 376 377 switch (state_) { 378 case NOT_INITIALIZED: 379 // Lazily initialize with the first call to GetLength(). 380 Initialize(base::Bind(&FileStreamReader::GetLengthAfterInitialized, 381 weak_ptr_factory_.GetWeakPtr(), 382 callback), 383 callback); 384 break; 385 386 case INITIALIZING: 387 NOTREACHED(); 388 break; 389 390 case INITIALIZED: 391 GetLengthAfterInitialized(callback); 392 break; 393 394 case FAILED: 395 NOTREACHED(); 396 break; 397 } 398 399 return net::ERR_IO_PENDING; 400} 401 402void FileStreamReader::ReadAfterInitialized( 403 scoped_refptr<net::IOBuffer> buffer, 404 int buffer_length, 405 const net::CompletionCallback& callback) { 406 DCHECK_CURRENTLY_ON(BrowserThread::IO); 407 DCHECK_EQ(INITIALIZED, state_); 408 409 current_length_ = 0; 410 BrowserThread::PostTask( 411 BrowserThread::UI, 412 FROM_HERE, 413 base::Bind(&OperationRunner::ReadFileOnUIThread, 414 runner_, 415 buffer, 416 current_offset_, 417 buffer_length, 418 base::Bind(&FileStreamReader::OnReadChunkReceived, 419 weak_ptr_factory_.GetWeakPtr(), 420 callback))); 421} 422 423void FileStreamReader::GetLengthAfterInitialized( 424 const net::Int64CompletionCallback& callback) { 425 DCHECK_CURRENTLY_ON(BrowserThread::IO); 426 DCHECK_EQ(INITIALIZED, state_); 427 428 BrowserThread::PostTask( 429 BrowserThread::UI, 430 FROM_HERE, 431 base::Bind( 432 &OperationRunner::GetMetadataOnUIThread, 433 runner_, 434 base::Bind(&FileStreamReader::OnGetMetadataForGetLengthReceived, 435 weak_ptr_factory_.GetWeakPtr(), 436 callback))); 437} 438 439void FileStreamReader::OnReadChunkReceived( 440 const net::CompletionCallback& callback, 441 int chunk_length, 442 bool has_more, 443 base::File::Error result) { 444 DCHECK_CURRENTLY_ON(BrowserThread::IO); 445 DCHECK_EQ(INITIALIZED, state_); 446 447 current_length_ += chunk_length; 448 449 // If this is the last chunk with a success, then finalize. 450 if (!has_more && result == base::File::FILE_OK) { 451 current_offset_ += current_length_; 452 callback.Run(current_length_); 453 return; 454 } 455 456 // In case of an error, abort. 457 if (result != base::File::FILE_OK) { 458 DCHECK(!has_more); 459 state_ = FAILED; 460 callback.Run(net::FileErrorToNetError(result)); 461 return; 462 } 463 464 // More data is about to come, so do not call the callback yet. 465 DCHECK(has_more); 466} 467 468void FileStreamReader::OnGetMetadataForGetLengthReceived( 469 const net::Int64CompletionCallback& callback, 470 scoped_ptr<EntryMetadata> metadata, 471 base::File::Error result) { 472 DCHECK_CURRENTLY_ON(BrowserThread::IO); 473 DCHECK_EQ(INITIALIZED, state_); 474 475 // In case of an error, abort. 476 if (result != base::File::FILE_OK) { 477 state_ = FAILED; 478 callback.Run(net::FileErrorToNetError(result)); 479 return; 480 } 481 482 // If the file modification time has changed, then abort. Note, that the file 483 // may be changed without affecting the modification time. 484 DCHECK(metadata.get()); 485 if (!expected_modification_time_.is_null() && 486 metadata->modification_time != expected_modification_time_) { 487 callback.Run(net::ERR_UPLOAD_FILE_CHANGED); 488 return; 489 } 490 491 DCHECK_EQ(base::File::FILE_OK, result); 492 callback.Run(metadata->size); 493} 494 495} // namespace file_system_provider 496} // namespace chromeos 497