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 <windows.h>
      8 
      9 #include "base/files/file_path.h"
     10 #include "base/logging.h"
     11 #include "base/metrics/histogram.h"
     12 #include "base/task_runner_util.h"
     13 #include "net/base/io_buffer.h"
     14 #include "net/base/net_errors.h"
     15 
     16 namespace net {
     17 
     18 // Ensure that we can just use our Whence values directly.
     19 COMPILE_ASSERT(FROM_BEGIN == FILE_BEGIN, bad_whence_begin);
     20 COMPILE_ASSERT(FROM_CURRENT == FILE_CURRENT, bad_whence_current);
     21 COMPILE_ASSERT(FROM_END == FILE_END, bad_whence_end);
     22 
     23 namespace {
     24 
     25 void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) {
     26   overlapped->Offset = offset.LowPart;
     27   overlapped->OffsetHigh = offset.HighPart;
     28 }
     29 
     30 void IncrementOffset(OVERLAPPED* overlapped, DWORD count) {
     31   LARGE_INTEGER offset;
     32   offset.LowPart = overlapped->Offset;
     33   offset.HighPart = overlapped->OffsetHigh;
     34   offset.QuadPart += static_cast<LONGLONG>(count);
     35   SetOffset(overlapped, offset);
     36 }
     37 
     38 }  // namespace
     39 
     40 FileStream::Context::Context(const BoundNetLog& bound_net_log,
     41                              const scoped_refptr<base::TaskRunner>& task_runner)
     42     : io_context_(),
     43       file_(base::kInvalidPlatformFileValue),
     44       record_uma_(false),
     45       async_in_progress_(false),
     46       orphaned_(false),
     47       bound_net_log_(bound_net_log),
     48       error_source_(FILE_ERROR_SOURCE_COUNT),
     49       task_runner_(task_runner) {
     50   io_context_.handler = this;
     51   memset(&io_context_.overlapped, 0, sizeof(io_context_.overlapped));
     52 }
     53 
     54 FileStream::Context::Context(base::PlatformFile file,
     55                              const BoundNetLog& bound_net_log,
     56                              int open_flags,
     57                              const scoped_refptr<base::TaskRunner>& task_runner)
     58     : io_context_(),
     59       file_(file),
     60       record_uma_(false),
     61       async_in_progress_(false),
     62       orphaned_(false),
     63       bound_net_log_(bound_net_log),
     64       error_source_(FILE_ERROR_SOURCE_COUNT),
     65       task_runner_(task_runner) {
     66   io_context_.handler = this;
     67   memset(&io_context_.overlapped, 0, sizeof(io_context_.overlapped));
     68   if (file_ != base::kInvalidPlatformFileValue &&
     69       (open_flags & base::PLATFORM_FILE_ASYNC)) {
     70     OnAsyncFileOpened();
     71   }
     72 }
     73 
     74 FileStream::Context::~Context() {
     75 }
     76 
     77 int64 FileStream::Context::GetFileSize() const {
     78   LARGE_INTEGER file_size;
     79   if (!GetFileSizeEx(file_, &file_size)) {
     80     IOResult error = IOResult::FromOSError(GetLastError());
     81     LOG(WARNING) << "GetFileSizeEx failed: " << error.os_error;
     82     RecordError(error, FILE_ERROR_SOURCE_GET_SIZE);
     83     return error.result;
     84   }
     85 
     86   return file_size.QuadPart;
     87 }
     88 
     89 int FileStream::Context::ReadAsync(IOBuffer* buf,
     90                                    int buf_len,
     91                                    const CompletionCallback& callback) {
     92   DCHECK(!async_in_progress_);
     93   error_source_ = FILE_ERROR_SOURCE_READ;
     94 
     95   DWORD bytes_read;
     96   if (!ReadFile(file_, buf->data(), buf_len,
     97                 &bytes_read, &io_context_.overlapped)) {
     98     IOResult error = IOResult::FromOSError(GetLastError());
     99     if (error.os_error == ERROR_IO_PENDING) {
    100       IOCompletionIsPending(callback, buf);
    101     } else if (error.os_error == ERROR_HANDLE_EOF) {
    102       return 0;  // Report EOF by returning 0 bytes read.
    103     } else {
    104       LOG(WARNING) << "ReadFile failed: " << error.os_error;
    105       RecordError(error, FILE_ERROR_SOURCE_READ);
    106     }
    107     return error.result;
    108   }
    109 
    110   IOCompletionIsPending(callback, buf);
    111   return ERR_IO_PENDING;
    112 }
    113 
    114 int FileStream::Context::ReadSync(char* buf, int buf_len) {
    115   DWORD bytes_read;
    116   if (!ReadFile(file_, buf, buf_len, &bytes_read, NULL)) {
    117     IOResult error = IOResult::FromOSError(GetLastError());
    118     if (error.os_error == ERROR_HANDLE_EOF) {
    119       return 0;  // Report EOF by returning 0 bytes read.
    120     } else {
    121       LOG(WARNING) << "ReadFile failed: " << error.os_error;
    122       RecordError(error, FILE_ERROR_SOURCE_READ);
    123     }
    124     return error.result;
    125   }
    126 
    127   return bytes_read;
    128 }
    129 
    130 int FileStream::Context::WriteAsync(IOBuffer* buf,
    131                                     int buf_len,
    132                                     const CompletionCallback& callback) {
    133   error_source_ = FILE_ERROR_SOURCE_WRITE;
    134 
    135   DWORD bytes_written = 0;
    136   if (!WriteFile(file_, buf->data(), buf_len,
    137                  &bytes_written, &io_context_.overlapped)) {
    138     IOResult error = IOResult::FromOSError(GetLastError());
    139     if (error.os_error == ERROR_IO_PENDING) {
    140       IOCompletionIsPending(callback, buf);
    141     } else {
    142       LOG(WARNING) << "WriteFile failed: " << error.os_error;
    143       RecordError(error, FILE_ERROR_SOURCE_WRITE);
    144     }
    145     return error.result;
    146   }
    147 
    148   IOCompletionIsPending(callback, buf);
    149   return ERR_IO_PENDING;
    150 }
    151 
    152 int FileStream::Context::WriteSync(const char* buf, int buf_len) {
    153   DWORD bytes_written = 0;
    154   if (!WriteFile(file_, buf, buf_len, &bytes_written, NULL)) {
    155     IOResult error = IOResult::FromOSError(GetLastError());
    156     LOG(WARNING) << "WriteFile failed: " << error.os_error;
    157     RecordError(error, FILE_ERROR_SOURCE_WRITE);
    158     return error.result;
    159   }
    160 
    161   return bytes_written;
    162 }
    163 
    164 int FileStream::Context::Truncate(int64 bytes) {
    165   if (!SetEndOfFile(file_)) {
    166     IOResult error = IOResult::FromOSError(GetLastError());
    167     LOG(WARNING) << "SetEndOfFile failed: " << error.os_error;
    168     RecordError(error, FILE_ERROR_SOURCE_SET_EOF);
    169     return error.result;
    170   }
    171 
    172   return bytes;
    173 }
    174 
    175 void FileStream::Context::OnAsyncFileOpened() {
    176   base::MessageLoopForIO::current()->RegisterIOHandler(file_, this);
    177 }
    178 
    179 FileStream::Context::IOResult FileStream::Context::SeekFileImpl(Whence whence,
    180                                                                 int64 offset) {
    181   LARGE_INTEGER distance, res;
    182   distance.QuadPart = offset;
    183   DWORD move_method = static_cast<DWORD>(whence);
    184   if (SetFilePointerEx(file_, distance, &res, move_method)) {
    185     SetOffset(&io_context_.overlapped, res);
    186     return IOResult(res.QuadPart, 0);
    187   }
    188 
    189   return IOResult::FromOSError(GetLastError());
    190 }
    191 
    192 FileStream::Context::IOResult FileStream::Context::FlushFileImpl() {
    193   if (FlushFileBuffers(file_))
    194     return IOResult(OK, 0);
    195 
    196   return IOResult::FromOSError(GetLastError());
    197 }
    198 
    199 FileStream::Context::IOResult FileStream::Context::CloseFileImpl() {
    200   bool success = base::ClosePlatformFile(file_);
    201   file_ = base::kInvalidPlatformFileValue;
    202   if (success)
    203     return IOResult(OK, 0);
    204 
    205   return IOResult::FromOSError(GetLastError());
    206 }
    207 
    208 void FileStream::Context::IOCompletionIsPending(
    209     const CompletionCallback& callback,
    210     IOBuffer* buf) {
    211   DCHECK(callback_.is_null());
    212   callback_ = callback;
    213   in_flight_buf_ = buf;  // Hold until the async operation ends.
    214   async_in_progress_ = true;
    215 }
    216 
    217 void FileStream::Context::OnIOCompleted(
    218     base::MessageLoopForIO::IOContext* context,
    219     DWORD bytes_read,
    220     DWORD error) {
    221   DCHECK_EQ(&io_context_, context);
    222   DCHECK(!callback_.is_null());
    223   DCHECK(async_in_progress_);
    224 
    225   async_in_progress_ = false;
    226   if (orphaned_) {
    227     callback_.Reset();
    228     in_flight_buf_ = NULL;
    229     CloseAndDelete();
    230     return;
    231   }
    232 
    233   int result;
    234   if (error == ERROR_HANDLE_EOF) {
    235     result = 0;
    236   } else if (error) {
    237     IOResult error_result = IOResult::FromOSError(error);
    238     RecordError(error_result, error_source_);
    239     result = error_result.result;
    240   } else {
    241     result = bytes_read;
    242     IncrementOffset(&io_context_.overlapped, bytes_read);
    243   }
    244 
    245   CompletionCallback temp_callback = callback_;
    246   callback_.Reset();
    247   scoped_refptr<IOBuffer> temp_buf = in_flight_buf_;
    248   in_flight_buf_ = NULL;
    249   temp_callback.Run(result);
    250 }
    251 
    252 }  // namespace net
    253