Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2012 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 "net/base/file_stream_context.h"
      6 
      7 #include "base/files/file_path.h"
      8 #include "base/location.h"
      9 #include "base/message_loop/message_loop_proxy.h"
     10 #include "base/task_runner.h"
     11 #include "base/task_runner_util.h"
     12 #include "base/threading/thread_restrictions.h"
     13 #include "base/values.h"
     14 #include "net/base/net_errors.h"
     15 
     16 #if defined(OS_ANDROID)
     17 #include "base/android/content_uri_utils.h"
     18 #endif
     19 
     20 namespace net {
     21 
     22 namespace {
     23 
     24 void CallInt64ToInt(const CompletionCallback& callback, int64 result) {
     25   callback.Run(static_cast<int>(result));
     26 }
     27 
     28 }  // namespace
     29 
     30 FileStream::Context::IOResult::IOResult()
     31     : result(OK),
     32       os_error(0) {
     33 }
     34 
     35 FileStream::Context::IOResult::IOResult(int64 result, int os_error)
     36     : result(result),
     37       os_error(os_error) {
     38 }
     39 
     40 // static
     41 FileStream::Context::IOResult FileStream::Context::IOResult::FromOSError(
     42     int64 os_error) {
     43   return IOResult(MapSystemError(os_error), os_error);
     44 }
     45 
     46 // ---------------------------------------------------------------------
     47 
     48 FileStream::Context::OpenResult::OpenResult() {
     49 }
     50 
     51 FileStream::Context::OpenResult::OpenResult(base::File file,
     52                                             IOResult error_code)
     53     : file(file.Pass()),
     54       error_code(error_code) {
     55 }
     56 
     57 FileStream::Context::OpenResult::OpenResult(RValue other)
     58     : file(other.object->file.Pass()),
     59       error_code(other.object->error_code) {
     60 }
     61 
     62 FileStream::Context::OpenResult& FileStream::Context::OpenResult::operator=(
     63     RValue other) {
     64   if (this != other.object) {
     65     file = other.object->file.Pass();
     66     error_code = other.object->error_code;
     67   }
     68   return *this;
     69 }
     70 
     71 // ---------------------------------------------------------------------
     72 
     73 void FileStream::Context::Orphan() {
     74   DCHECK(!orphaned_);
     75 
     76   orphaned_ = true;
     77 
     78   if (!async_in_progress_) {
     79     CloseAndDelete();
     80   } else if (file_.IsValid()) {
     81 #if defined(OS_WIN)
     82     CancelIo(file_.GetPlatformFile());
     83 #endif
     84   }
     85 }
     86 
     87 void FileStream::Context::Open(const base::FilePath& path,
     88                                int open_flags,
     89                                const CompletionCallback& callback) {
     90   DCHECK(!async_in_progress_);
     91 
     92   bool posted = base::PostTaskAndReplyWithResult(
     93       task_runner_.get(),
     94       FROM_HERE,
     95       base::Bind(
     96           &Context::OpenFileImpl, base::Unretained(this), path, open_flags),
     97       base::Bind(&Context::OnOpenCompleted, base::Unretained(this), callback));
     98   DCHECK(posted);
     99 
    100   async_in_progress_ = true;
    101 }
    102 
    103 void FileStream::Context::Close(const CompletionCallback& callback) {
    104   DCHECK(!async_in_progress_);
    105   bool posted = base::PostTaskAndReplyWithResult(
    106       task_runner_.get(),
    107       FROM_HERE,
    108       base::Bind(&Context::CloseFileImpl, base::Unretained(this)),
    109       base::Bind(&Context::OnAsyncCompleted,
    110                  base::Unretained(this),
    111                  IntToInt64(callback)));
    112   DCHECK(posted);
    113 
    114   async_in_progress_ = true;
    115 }
    116 
    117 void FileStream::Context::Seek(base::File::Whence whence,
    118                                int64 offset,
    119                                const Int64CompletionCallback& callback) {
    120   DCHECK(!async_in_progress_);
    121 
    122   bool posted = base::PostTaskAndReplyWithResult(
    123       task_runner_.get(),
    124       FROM_HERE,
    125       base::Bind(
    126           &Context::SeekFileImpl, base::Unretained(this), whence, offset),
    127       base::Bind(&Context::OnAsyncCompleted,
    128                  base::Unretained(this),
    129                  callback));
    130   DCHECK(posted);
    131 
    132   async_in_progress_ = true;
    133 }
    134 
    135 void FileStream::Context::Flush(const CompletionCallback& callback) {
    136   DCHECK(!async_in_progress_);
    137 
    138   bool posted = base::PostTaskAndReplyWithResult(
    139       task_runner_.get(),
    140       FROM_HERE,
    141       base::Bind(&Context::FlushFileImpl, base::Unretained(this)),
    142       base::Bind(&Context::OnAsyncCompleted,
    143                  base::Unretained(this),
    144                  IntToInt64(callback)));
    145   DCHECK(posted);
    146 
    147   async_in_progress_ = true;
    148 }
    149 
    150 FileStream::Context::OpenResult FileStream::Context::OpenFileImpl(
    151     const base::FilePath& path, int open_flags) {
    152 #if defined(OS_POSIX)
    153   // Always use blocking IO.
    154   open_flags &= ~base::File::FLAG_ASYNC;
    155 #endif
    156   base::File file;
    157 #if defined(OS_ANDROID)
    158   if (path.IsContentUri()) {
    159     // Check that only Read flags are set.
    160     DCHECK_EQ(open_flags & ~base::File::FLAG_ASYNC,
    161               base::File::FLAG_OPEN | base::File::FLAG_READ);
    162     file = base::OpenContentUriForRead(path);
    163   } else {
    164 #endif  // defined(OS_ANDROID)
    165     // FileStream::Context actually closes the file asynchronously,
    166     // independently from FileStream's destructor. It can cause problems for
    167     // users wanting to delete the file right after FileStream deletion. Thus
    168     // we are always adding SHARE_DELETE flag to accommodate such use case.
    169     // TODO(rvargas): This sounds like a bug, as deleting the file would
    170     // presumably happen on the wrong thread. There should be an async delete.
    171     open_flags |= base::File::FLAG_SHARE_DELETE;
    172     file.Initialize(path, open_flags);
    173 #if defined(OS_ANDROID)
    174   }
    175 #endif  // defined(OS_ANDROID)
    176   if (!file.IsValid())
    177     return OpenResult(base::File(),
    178                       IOResult::FromOSError(logging::GetLastSystemErrorCode()));
    179 
    180   return OpenResult(file.Pass(), IOResult(OK, 0));
    181 }
    182 
    183 FileStream::Context::IOResult FileStream::Context::CloseFileImpl() {
    184   file_.Close();
    185   return IOResult(OK, 0);
    186 }
    187 
    188 FileStream::Context::IOResult FileStream::Context::FlushFileImpl() {
    189   if (file_.Flush())
    190     return IOResult(OK, 0);
    191 
    192   return IOResult::FromOSError(logging::GetLastSystemErrorCode());
    193 }
    194 
    195 void FileStream::Context::OnOpenCompleted(const CompletionCallback& callback,
    196                                           OpenResult open_result) {
    197   file_ = open_result.file.Pass();
    198   if (file_.IsValid() && !orphaned_)
    199     OnFileOpened();
    200 
    201   OnAsyncCompleted(IntToInt64(callback), open_result.error_code);
    202 }
    203 
    204 void FileStream::Context::CloseAndDelete() {
    205   DCHECK(!async_in_progress_);
    206 
    207   if (file_.IsValid()) {
    208     bool posted = task_runner_.get()->PostTask(
    209         FROM_HERE,
    210         base::Bind(base::IgnoreResult(&Context::CloseFileImpl),
    211                    base::Owned(this)));
    212     DCHECK(posted);
    213   } else {
    214     delete this;
    215   }
    216 }
    217 
    218 Int64CompletionCallback FileStream::Context::IntToInt64(
    219     const CompletionCallback& callback) {
    220   return base::Bind(&CallInt64ToInt, callback);
    221 }
    222 
    223 void FileStream::Context::OnAsyncCompleted(
    224     const Int64CompletionCallback& callback,
    225     const IOResult& result) {
    226   // Reset this before Run() as Run() may issue a new async operation. Also it
    227   // should be reset before Close() because it shouldn't run if any async
    228   // operation is in progress.
    229   async_in_progress_ = false;
    230   if (orphaned_)
    231     CloseAndDelete();
    232   else
    233     callback.Run(result.result);
    234 }
    235 
    236 }  // namespace net
    237