| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "net/base/file_stream_context.h" | 
 |  | 
 | #include <utility> | 
 |  | 
 | #include "base/files/file_path.h" | 
 | #include "base/location.h" | 
 | #include "base/task_runner.h" | 
 | #include "base/task_runner_util.h" | 
 | #include "base/threading/thread_restrictions.h" | 
 | #include "base/values.h" | 
 | #include "net/base/net_errors.h" | 
 |  | 
 | #ifdef STARBOARD | 
 | #include "base/message_loop/message_loop.h" | 
 | #include "base/single_thread_task_runner.h" | 
 | #include "nb/polymorphic_downcast.h" | 
 | #endif | 
 |  | 
 | #if defined(OS_ANDROID) | 
 | #include "base/android/content_uri_utils.h" | 
 | #endif | 
 |  | 
 | namespace net { | 
 |  | 
 | namespace { | 
 |  | 
 | void CallInt64ToInt(CompletionOnceCallback callback, int64_t result) { | 
 |   std::move(callback).Run(static_cast<int>(result)); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | FileStream::Context::IOResult::IOResult() | 
 |     : result(OK), | 
 |       os_error(0) { | 
 | } | 
 |  | 
 | FileStream::Context::IOResult::IOResult(int64_t result, | 
 |                                         logging::SystemErrorCode os_error) | 
 |     : result(result), os_error(os_error) { | 
 | } | 
 |  | 
 | // static | 
 | FileStream::Context::IOResult FileStream::Context::IOResult::FromOSError( | 
 |     logging::SystemErrorCode os_error) { | 
 |   return IOResult(MapSystemError(os_error), os_error); | 
 | } | 
 |  | 
 | #if defined(STARBOARD) | 
 | //static | 
 | FileStream::Context::IOResult FileStream::Context::IOResult::FromFileError( | 
 |     base::File::Error file_error, logging::SystemErrorCode os_error) { | 
 |   if (file_error == base::File::FILE_ERROR_NOT_FOUND) { | 
 |     return IOResult(ERR_FILE_NOT_FOUND, os_error); | 
 |   } else { | 
 |     return IOResult(ERR_FAILED, os_error); | 
 |   } | 
 | } | 
 | #endif | 
 |  | 
 | // --------------------------------------------------------------------- | 
 |  | 
 | FileStream::Context::OpenResult::OpenResult() = default; | 
 |  | 
 | FileStream::Context::OpenResult::OpenResult(base::File file, | 
 |                                             IOResult error_code) | 
 |     : file(std::move(file)), error_code(error_code) {} | 
 |  | 
 | FileStream::Context::OpenResult::OpenResult(OpenResult&& other) | 
 |     : file(std::move(other.file)), error_code(other.error_code) {} | 
 |  | 
 | FileStream::Context::OpenResult& FileStream::Context::OpenResult::operator=( | 
 |     OpenResult&& other) { | 
 |   file = std::move(other.file); | 
 |   error_code = other.error_code; | 
 |   return *this; | 
 | } | 
 |  | 
 | // --------------------------------------------------------------------- | 
 |  | 
 | void FileStream::Context::Orphan() { | 
 |   DCHECK(!orphaned_); | 
 |  | 
 |   orphaned_ = true; | 
 |  | 
 |   if (!async_in_progress_) { | 
 |     CloseAndDelete(); | 
 |   } else if (file_.IsValid()) { | 
 | #if defined(OS_WIN) | 
 |     CancelIo(file_.GetPlatformFile()); | 
 | #endif | 
 |   } | 
 | } | 
 |  | 
 | void FileStream::Context::Open(const base::FilePath& path, | 
 |                                int open_flags, | 
 |                                CompletionOnceCallback callback) { | 
 |   DCHECK(!async_in_progress_); | 
 |  | 
 |   bool posted = base::PostTaskAndReplyWithResult( | 
 |       task_runner_.get(), FROM_HERE, | 
 |       base::BindOnce(&Context::OpenFileImpl, base::Unretained(this), path, | 
 |                      open_flags), | 
 |       base::BindOnce(&Context::OnOpenCompleted, base::Unretained(this), | 
 |                      std::move(callback))); | 
 |   DCHECK(posted); | 
 |  | 
 |   async_in_progress_ = true; | 
 | } | 
 |  | 
 | void FileStream::Context::Close(CompletionOnceCallback callback) { | 
 |   DCHECK(!async_in_progress_); | 
 |  | 
 |   bool posted = base::PostTaskAndReplyWithResult( | 
 |       task_runner_.get(), FROM_HERE, | 
 |       base::BindOnce(&Context::CloseFileImpl, base::Unretained(this)), | 
 |       base::BindOnce(&Context::OnAsyncCompleted, base::Unretained(this), | 
 |                      IntToInt64(std::move(callback)))); | 
 |   DCHECK(posted); | 
 |  | 
 |   async_in_progress_ = true; | 
 | } | 
 |  | 
 | void FileStream::Context::Seek(int64_t offset, | 
 |                                Int64CompletionOnceCallback callback) { | 
 |   DCHECK(!async_in_progress_); | 
 |  | 
 |   bool posted = base::PostTaskAndReplyWithResult( | 
 |       task_runner_.get(), FROM_HERE, | 
 |       base::BindOnce(&Context::SeekFileImpl, base::Unretained(this), offset), | 
 |       base::BindOnce(&Context::OnAsyncCompleted, base::Unretained(this), | 
 |                      std::move(callback))); | 
 |   DCHECK(posted); | 
 |  | 
 |   async_in_progress_ = true; | 
 | } | 
 |  | 
 | void FileStream::Context::GetFileInfo(base::File::Info* file_info, | 
 |                                       CompletionOnceCallback callback) { | 
 |   base::PostTaskAndReplyWithResult( | 
 |       task_runner_.get(), FROM_HERE, | 
 |       base::BindOnce(&Context::GetFileInfoImpl, base::Unretained(this), | 
 |                      base::Unretained(file_info)), | 
 |       base::BindOnce(&Context::OnAsyncCompleted, base::Unretained(this), | 
 |                      IntToInt64(std::move(callback)))); | 
 |  | 
 |   async_in_progress_ = true; | 
 | } | 
 |  | 
 | void FileStream::Context::Flush(CompletionOnceCallback callback) { | 
 |   DCHECK(!async_in_progress_); | 
 |  | 
 |   bool posted = base::PostTaskAndReplyWithResult( | 
 |       task_runner_.get(), FROM_HERE, | 
 |       base::BindOnce(&Context::FlushFileImpl, base::Unretained(this)), | 
 |       base::BindOnce(&Context::OnAsyncCompleted, base::Unretained(this), | 
 |                      IntToInt64(std::move(callback)))); | 
 |   DCHECK(posted); | 
 |  | 
 |   async_in_progress_ = true; | 
 | } | 
 |  | 
 | bool FileStream::Context::IsOpen() const { | 
 |   return file_.IsValid(); | 
 | } | 
 |  | 
 | FileStream::Context::OpenResult FileStream::Context::OpenFileImpl( | 
 |     const base::FilePath& path, int open_flags) { | 
 | #if defined(OS_POSIX) || defined(STARBOARD) | 
 |   // Always use blocking IO. | 
 |   open_flags &= ~base::File::FLAG_ASYNC; | 
 | #endif | 
 |   base::File file; | 
 | #if defined(OS_ANDROID) | 
 |   if (path.IsContentUri()) { | 
 |     // Check that only Read flags are set. | 
 |     DCHECK_EQ(open_flags & ~base::File::FLAG_ASYNC, | 
 |               base::File::FLAG_OPEN | base::File::FLAG_READ); | 
 |     file = base::OpenContentUriForRead(path); | 
 |   } else { | 
 | #endif  // defined(OS_ANDROID) | 
 |     // FileStream::Context actually closes the file asynchronously, | 
 |     // independently from FileStream's destructor. It can cause problems for | 
 |     // users wanting to delete the file right after FileStream deletion. Thus | 
 |     // we are always adding SHARE_DELETE flag to accommodate such use case. | 
 |     // TODO(rvargas): This sounds like a bug, as deleting the file would | 
 |     // presumably happen on the wrong thread. There should be an async delete. | 
 |     open_flags |= base::File::FLAG_SHARE_DELETE; | 
 |     file.Initialize(path, open_flags); | 
 | #if defined(OS_ANDROID) | 
 |   } | 
 | #endif  // defined(OS_ANDROID) | 
 |   if (!file.IsValid()) { | 
 | #if defined(STARBOARD) | 
 |     return OpenResult( | 
 |         base::File(), IOResult::FromFileError( | 
 |             file.error_details(), logging::GetLastSystemErrorCode())); | 
 | #else | 
 |     return OpenResult(base::File(), | 
 |                       IOResult::FromOSError(logging::GetLastSystemErrorCode())); | 
 | #endif | 
 |   } | 
 |  | 
 |   return OpenResult(std::move(file), IOResult(OK, 0)); | 
 | } | 
 |  | 
 | FileStream::Context::IOResult FileStream::Context::GetFileInfoImpl( | 
 |     base::File::Info* file_info) { | 
 |   bool result = file_.GetInfo(file_info); | 
 |   if (!result) | 
 |     return IOResult::FromOSError(logging::GetLastSystemErrorCode()); | 
 |   return IOResult(OK, 0); | 
 | } | 
 |  | 
 | FileStream::Context::IOResult FileStream::Context::CloseFileImpl() { | 
 |   file_.Close(); | 
 |   return IOResult(OK, 0); | 
 | } | 
 |  | 
 | FileStream::Context::IOResult FileStream::Context::FlushFileImpl() { | 
 |   if (file_.Flush()) | 
 |     return IOResult(OK, 0); | 
 |  | 
 |   return IOResult::FromOSError(logging::GetLastSystemErrorCode()); | 
 | } | 
 |  | 
 | void FileStream::Context::OnOpenCompleted(CompletionOnceCallback callback, | 
 |                                           OpenResult open_result) { | 
 |   file_ = std::move(open_result.file); | 
 |   if (file_.IsValid() && !orphaned_) | 
 |     OnFileOpened(); | 
 |  | 
 |   OnAsyncCompleted(IntToInt64(std::move(callback)), open_result.error_code); | 
 | } | 
 |  | 
 | void FileStream::Context::CloseAndDelete() { | 
 |   DCHECK(!async_in_progress_); | 
 |  | 
 |   if (file_.IsValid()) { | 
 | #ifdef STARBOARD | 
 |     // On Windows, holding file_ will prevent re-creation immediately after | 
 |     // CloseAndDelete is called, failing some tests. | 
 |     if (base::MessageLoop::current()->task_runner() == task_runner_.get()) { | 
 |       file_.Close(); | 
 |       delete this; | 
 |       return; | 
 |     } | 
 | #endif | 
 |     bool posted = task_runner_.get()->PostTask( | 
 |         FROM_HERE, base::BindOnce(base::IgnoreResult(&Context::CloseFileImpl), | 
 |                                   base::Owned(this))); | 
 |     DCHECK(posted); | 
 |   } else { | 
 |     delete this; | 
 |   } | 
 | } | 
 |  | 
 | Int64CompletionOnceCallback FileStream::Context::IntToInt64( | 
 |     CompletionOnceCallback callback) { | 
 |   return base::BindOnce(&CallInt64ToInt, std::move(callback)); | 
 | } | 
 |  | 
 | void FileStream::Context::OnAsyncCompleted(Int64CompletionOnceCallback callback, | 
 |                                            const IOResult& result) { | 
 |   // Reset this before Run() as Run() may issue a new async operation. Also it | 
 |   // should be reset before Close() because it shouldn't run if any async | 
 |   // operation is in progress. | 
 |   async_in_progress_ = false; | 
 |   if (orphaned_) { | 
 |     CloseAndDelete(); | 
 |   } else { | 
 |     std::move(callback).Run(result.result); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace net |