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     CancelIo(file_.GetPlatformFile());
     82   }
     83 }
     84 
     85 void FileStream::Context::OpenAsync(const base::FilePath& path,
     86                                     int open_flags,
     87                                     const CompletionCallback& callback) {
     88   DCHECK(!async_in_progress_);
     89 
     90   bool posted = base::PostTaskAndReplyWithResult(
     91       task_runner_.get(),
     92       FROM_HERE,
     93       base::Bind(
     94           &Context::OpenFileImpl, base::Unretained(this), path, open_flags),
     95       base::Bind(&Context::OnOpenCompleted, base::Unretained(this), callback));
     96   DCHECK(posted);
     97 
     98   async_in_progress_ = true;
     99 }
    100 
    101 void FileStream::Context::CloseAsync(const CompletionCallback& callback) {
    102   DCHECK(!async_in_progress_);
    103   bool posted = base::PostTaskAndReplyWithResult(
    104       task_runner_.get(),
    105       FROM_HERE,
    106       base::Bind(&Context::CloseFileImpl, base::Unretained(this)),
    107       base::Bind(&Context::OnAsyncCompleted,
    108                  base::Unretained(this),
    109                  IntToInt64(callback)));
    110   DCHECK(posted);
    111 
    112   async_in_progress_ = true;
    113 }
    114 
    115 void FileStream::Context::SeekAsync(Whence whence,
    116                                     int64 offset,
    117                                     const Int64CompletionCallback& callback) {
    118   DCHECK(!async_in_progress_);
    119 
    120   bool posted = base::PostTaskAndReplyWithResult(
    121       task_runner_.get(),
    122       FROM_HERE,
    123       base::Bind(
    124           &Context::SeekFileImpl, base::Unretained(this), whence, offset),
    125       base::Bind(&Context::OnAsyncCompleted,
    126                  base::Unretained(this),
    127                  callback));
    128   DCHECK(posted);
    129 
    130   async_in_progress_ = true;
    131 }
    132 
    133 void FileStream::Context::FlushAsync(const CompletionCallback& callback) {
    134   DCHECK(!async_in_progress_);
    135 
    136   bool posted = base::PostTaskAndReplyWithResult(
    137       task_runner_.get(),
    138       FROM_HERE,
    139       base::Bind(&Context::FlushFileImpl, base::Unretained(this)),
    140       base::Bind(&Context::OnAsyncCompleted,
    141                  base::Unretained(this),
    142                  IntToInt64(callback)));
    143   DCHECK(posted);
    144 
    145   async_in_progress_ = true;
    146 }
    147 
    148 FileStream::Context::OpenResult FileStream::Context::OpenFileImpl(
    149     const base::FilePath& path, int open_flags) {
    150 #if defined(OS_POSIX)
    151   // Always use blocking IO.
    152   open_flags &= ~base::File::FLAG_ASYNC;
    153 #endif
    154   base::File file;
    155 #if defined(OS_ANDROID)
    156   if (path.IsContentUri()) {
    157     // Check that only Read flags are set.
    158     DCHECK_EQ(open_flags & ~base::File::FLAG_ASYNC,
    159               base::File::FLAG_OPEN | base::File::FLAG_READ);
    160     file = base::OpenContentUriForRead(path);
    161   } else {
    162 #endif  // defined(OS_ANDROID)
    163     // FileStream::Context actually closes the file asynchronously,
    164     // independently from FileStream's destructor. It can cause problems for
    165     // users wanting to delete the file right after FileStream deletion. Thus
    166     // we are always adding SHARE_DELETE flag to accommodate such use case.
    167     // TODO(rvargas): This sounds like a bug, as deleting the file would
    168     // presumably happen on the wrong thread. There should be an async delete.
    169     open_flags |= base::File::FLAG_SHARE_DELETE;
    170     file.Initialize(path, open_flags);
    171 #if defined(OS_ANDROID)
    172   }
    173 #endif  // defined(OS_ANDROID)
    174   if (!file.IsValid())
    175     return OpenResult(base::File(), IOResult::FromOSError(GetLastErrno()));
    176 
    177   return OpenResult(file.Pass(), IOResult(OK, 0));
    178 }
    179 
    180 FileStream::Context::IOResult FileStream::Context::CloseFileImpl() {
    181   file_.Close();
    182   return IOResult(OK, 0);
    183 }
    184 
    185 void FileStream::Context::OnOpenCompleted(const CompletionCallback& callback,
    186                                           OpenResult open_result) {
    187   file_ = open_result.file.Pass();
    188   if (file_.IsValid() && !orphaned_)
    189     OnAsyncFileOpened();
    190 
    191   OnAsyncCompleted(IntToInt64(callback), open_result.error_code);
    192 }
    193 
    194 void FileStream::Context::CloseAndDelete() {
    195   DCHECK(!async_in_progress_);
    196 
    197   if (file_.IsValid()) {
    198     bool posted = task_runner_.get()->PostTask(
    199         FROM_HERE,
    200         base::Bind(base::IgnoreResult(&Context::CloseFileImpl),
    201                    base::Owned(this)));
    202     DCHECK(posted);
    203   } else {
    204     delete this;
    205   }
    206 }
    207 
    208 Int64CompletionCallback FileStream::Context::IntToInt64(
    209     const CompletionCallback& callback) {
    210   return base::Bind(&CallInt64ToInt, callback);
    211 }
    212 
    213 void FileStream::Context::OnAsyncCompleted(
    214     const Int64CompletionCallback& callback,
    215     const IOResult& result) {
    216   // Reset this before Run() as Run() may issue a new async operation. Also it
    217   // should be reset before CloseAsync() because it shouldn't run if any async
    218   // operation is in progress.
    219   async_in_progress_ = false;
    220   if (orphaned_)
    221     CloseAndDelete();
    222   else
    223     callback.Run(result.result);
    224 }
    225 
    226 }  // namespace net
    227