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/location.h"
      8 #include "base/message_loop/message_loop_proxy.h"
      9 #include "base/task_runner_util.h"
     10 #include "base/threading/thread_restrictions.h"
     11 #include "net/base/file_stream_net_log_parameters.h"
     12 #include "net/base/net_errors.h"
     13 
     14 namespace {
     15 
     16 void CallInt64ToInt(const net::CompletionCallback& callback, int64 result) {
     17   callback.Run(static_cast<int>(result));
     18 }
     19 
     20 }
     21 
     22 namespace net {
     23 
     24 FileStream::Context::IOResult::IOResult()
     25     : result(OK),
     26       os_error(0) {
     27 }
     28 
     29 FileStream::Context::IOResult::IOResult(int64 result, int os_error)
     30     : result(result),
     31       os_error(os_error) {
     32 }
     33 
     34 // static
     35 FileStream::Context::IOResult FileStream::Context::IOResult::FromOSError(
     36     int64 os_error) {
     37   return IOResult(MapSystemError(os_error), os_error);
     38 }
     39 
     40 FileStream::Context::OpenResult::OpenResult()
     41     : file(base::kInvalidPlatformFileValue) {
     42 }
     43 
     44 FileStream::Context::OpenResult::OpenResult(base::PlatformFile file,
     45                                             IOResult error_code)
     46     : file(file),
     47       error_code(error_code) {
     48 }
     49 
     50 void FileStream::Context::Orphan() {
     51   DCHECK(!orphaned_);
     52 
     53   orphaned_ = true;
     54   if (file_ != base::kInvalidPlatformFileValue)
     55     bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN);
     56 
     57   if (!async_in_progress_) {
     58     CloseAndDelete();
     59   } else if (file_ != base::kInvalidPlatformFileValue) {
     60     CancelIo(file_);
     61   }
     62 }
     63 
     64 void FileStream::Context::OpenAsync(const base::FilePath& path,
     65                                     int open_flags,
     66                                     const CompletionCallback& callback) {
     67   DCHECK(!async_in_progress_);
     68 
     69   BeginOpenEvent(path);
     70 
     71   const bool posted = base::PostTaskAndReplyWithResult(
     72       task_runner_.get(),
     73       FROM_HERE,
     74       base::Bind(
     75           &Context::OpenFileImpl, base::Unretained(this), path, open_flags),
     76       base::Bind(&Context::OnOpenCompleted, base::Unretained(this), callback));
     77   DCHECK(posted);
     78 
     79   async_in_progress_ = true;
     80 }
     81 
     82 int FileStream::Context::OpenSync(const base::FilePath& path, int open_flags) {
     83   DCHECK(!async_in_progress_);
     84 
     85   BeginOpenEvent(path);
     86   OpenResult result = OpenFileImpl(path, open_flags);
     87   file_ = result.file;
     88   if (file_ == base::kInvalidPlatformFileValue) {
     89     ProcessOpenError(result.error_code);
     90   } else {
     91     // TODO(satorux): Remove this once all async clients are migrated to use
     92     // Open(). crbug.com/114783
     93     if (open_flags & base::PLATFORM_FILE_ASYNC)
     94       OnAsyncFileOpened();
     95   }
     96   return result.error_code.result;
     97 }
     98 
     99 void FileStream::Context::CloseSync() {
    100   DCHECK(!async_in_progress_);
    101   if (file_ != base::kInvalidPlatformFileValue) {
    102     base::ClosePlatformFile(file_);
    103     file_ = base::kInvalidPlatformFileValue;
    104     bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN);
    105   }
    106 }
    107 
    108 void FileStream::Context::SeekAsync(Whence whence,
    109                                     int64 offset,
    110                                     const Int64CompletionCallback& callback) {
    111   DCHECK(!async_in_progress_);
    112 
    113   const bool posted = base::PostTaskAndReplyWithResult(
    114       task_runner_.get(),
    115       FROM_HERE,
    116       base::Bind(
    117           &Context::SeekFileImpl, base::Unretained(this), whence, offset),
    118       base::Bind(&Context::ProcessAsyncResult,
    119                  base::Unretained(this),
    120                  callback,
    121                  FILE_ERROR_SOURCE_SEEK));
    122   DCHECK(posted);
    123 
    124   async_in_progress_ = true;
    125 }
    126 
    127 int64 FileStream::Context::SeekSync(Whence whence, int64 offset) {
    128   IOResult result = SeekFileImpl(whence, offset);
    129   RecordError(result, FILE_ERROR_SOURCE_SEEK);
    130   return result.result;
    131 }
    132 
    133 void FileStream::Context::FlushAsync(const CompletionCallback& callback) {
    134   DCHECK(!async_in_progress_);
    135 
    136   const bool posted = base::PostTaskAndReplyWithResult(
    137       task_runner_.get(),
    138       FROM_HERE,
    139       base::Bind(&Context::FlushFileImpl, base::Unretained(this)),
    140       base::Bind(&Context::ProcessAsyncResult,
    141                  base::Unretained(this),
    142                  IntToInt64(callback),
    143                  FILE_ERROR_SOURCE_FLUSH));
    144   DCHECK(posted);
    145 
    146   async_in_progress_ = true;
    147 }
    148 
    149 int FileStream::Context::FlushSync() {
    150   IOResult result = FlushFileImpl();
    151   RecordError(result, FILE_ERROR_SOURCE_FLUSH);
    152   return result.result;
    153 }
    154 
    155 void FileStream::Context::RecordError(const IOResult& result,
    156                                       FileErrorSource source) const {
    157   if (result.result >= 0) {
    158     // |result| is not an error.
    159     return;
    160   }
    161 
    162   // The following check is against incorrect use or bug. File descriptor
    163   // shouldn't ever be closed outside of FileStream while it still tries to do
    164   // something with it.
    165   DCHECK_NE(result.result, ERR_INVALID_HANDLE);
    166 
    167   if (!orphaned_) {
    168     bound_net_log_.AddEvent(
    169         NetLog::TYPE_FILE_STREAM_ERROR,
    170         base::Bind(&NetLogFileStreamErrorCallback,
    171                    source, result.os_error,
    172                    static_cast<net::Error>(result.result)));
    173   }
    174 
    175   RecordFileError(result.os_error, source, record_uma_);
    176 }
    177 
    178 void FileStream::Context::BeginOpenEvent(const base::FilePath& path) {
    179   std::string file_name = path.AsUTF8Unsafe();
    180   bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_OPEN,
    181                             NetLog::StringCallback("file_name", &file_name));
    182 }
    183 
    184 FileStream::Context::OpenResult FileStream::Context::OpenFileImpl(
    185     const base::FilePath& path, int open_flags) {
    186   // FileStream::Context actually closes the file asynchronously, independently
    187   // from FileStream's destructor. It can cause problems for users wanting to
    188   // delete the file right after FileStream deletion. Thus we are always
    189   // adding SHARE_DELETE flag to accommodate such use case.
    190   open_flags |= base::PLATFORM_FILE_SHARE_DELETE;
    191   base::PlatformFile file =
    192       base::CreatePlatformFile(path, open_flags, NULL, NULL);
    193   if (file == base::kInvalidPlatformFileValue)
    194     return OpenResult(file, IOResult::FromOSError(GetLastErrno()));
    195 
    196   return OpenResult(file, IOResult(OK, 0));
    197 }
    198 
    199 void FileStream::Context::ProcessOpenError(const IOResult& error_code) {
    200   bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN);
    201   RecordError(error_code, FILE_ERROR_SOURCE_OPEN);
    202 }
    203 
    204 void FileStream::Context::OnOpenCompleted(const CompletionCallback& callback,
    205                                           OpenResult open_result) {
    206   file_ = open_result.file;
    207   if (file_ == base::kInvalidPlatformFileValue)
    208     ProcessOpenError(open_result.error_code);
    209   else if (!orphaned_)
    210     OnAsyncFileOpened();
    211   OnAsyncCompleted(IntToInt64(callback), open_result.error_code.result);
    212 }
    213 
    214 void FileStream::Context::CloseAndDelete() {
    215   DCHECK(!async_in_progress_);
    216 
    217   if (file_ == base::kInvalidPlatformFileValue) {
    218     delete this;
    219   } else {
    220     const bool posted = task_runner_->PostTaskAndReply(
    221         FROM_HERE,
    222         base::Bind(base::IgnoreResult(&base::ClosePlatformFile), file_),
    223         base::Bind(&Context::OnCloseCompleted, base::Unretained(this)));
    224     DCHECK(posted);
    225     file_ = base::kInvalidPlatformFileValue;
    226   }
    227 }
    228 
    229 void FileStream::Context::OnCloseCompleted() {
    230   delete this;
    231 }
    232 
    233 Int64CompletionCallback FileStream::Context::IntToInt64(
    234     const CompletionCallback& callback) {
    235   return base::Bind(&CallInt64ToInt, callback);
    236 }
    237 
    238 void FileStream::Context::ProcessAsyncResult(
    239     const Int64CompletionCallback& callback,
    240     FileErrorSource source,
    241     const IOResult& result) {
    242   RecordError(result, source);
    243   OnAsyncCompleted(callback, result.result);
    244 }
    245 
    246 void FileStream::Context::OnAsyncCompleted(
    247     const Int64CompletionCallback& callback,
    248     int64 result) {
    249   // Reset this before Run() as Run() may issue a new async operation. Also it
    250   // should be reset before CloseAsync() because it shouldn't run if any async
    251   // operation is in progress.
    252   async_in_progress_ = false;
    253   if (orphaned_)
    254     CloseAndDelete();
    255   else
    256     callback.Run(result);
    257 }
    258 
    259 }  // namespace net
    260 
    261