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.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 scoped_refptr<base::TaskRunner>& task_runner)
     41     : io_context_(),
     42       async_in_progress_(false),
     43       orphaned_(false),
     44       task_runner_(task_runner) {
     45   io_context_.handler = this;
     46   memset(&io_context_.overlapped, 0, sizeof(io_context_.overlapped));
     47 }
     48 
     49 FileStream::Context::Context(base::File file,
     50                              const scoped_refptr<base::TaskRunner>& task_runner)
     51     : io_context_(),
     52       file_(file.Pass()),
     53       async_in_progress_(false),
     54       orphaned_(false),
     55       task_runner_(task_runner) {
     56   io_context_.handler = this;
     57   memset(&io_context_.overlapped, 0, sizeof(io_context_.overlapped));
     58   if (file_.IsValid()) {
     59     // TODO(hashimoto): Check that file_ is async.
     60     OnAsyncFileOpened();
     61   }
     62 }
     63 
     64 FileStream::Context::~Context() {
     65 }
     66 
     67 int FileStream::Context::ReadAsync(IOBuffer* buf,
     68                                    int buf_len,
     69                                    const CompletionCallback& callback) {
     70   DCHECK(!async_in_progress_);
     71 
     72   DWORD bytes_read;
     73   if (!ReadFile(file_.GetPlatformFile(), buf->data(), buf_len,
     74                 &bytes_read, &io_context_.overlapped)) {
     75     IOResult error = IOResult::FromOSError(GetLastError());
     76     if (error.os_error == ERROR_IO_PENDING) {
     77       IOCompletionIsPending(callback, buf);
     78     } else if (error.os_error == ERROR_HANDLE_EOF) {
     79       return 0;  // Report EOF by returning 0 bytes read.
     80     } else {
     81       LOG(WARNING) << "ReadFile failed: " << error.os_error;
     82     }
     83     return error.result;
     84   }
     85 
     86   IOCompletionIsPending(callback, buf);
     87   return ERR_IO_PENDING;
     88 }
     89 
     90 int FileStream::Context::WriteAsync(IOBuffer* buf,
     91                                     int buf_len,
     92                                     const CompletionCallback& callback) {
     93   DWORD bytes_written = 0;
     94   if (!WriteFile(file_.GetPlatformFile(), buf->data(), buf_len,
     95                  &bytes_written, &io_context_.overlapped)) {
     96     IOResult error = IOResult::FromOSError(GetLastError());
     97     if (error.os_error == ERROR_IO_PENDING) {
     98       IOCompletionIsPending(callback, buf);
     99     } else {
    100       LOG(WARNING) << "WriteFile failed: " << error.os_error;
    101     }
    102     return error.result;
    103   }
    104 
    105   IOCompletionIsPending(callback, buf);
    106   return ERR_IO_PENDING;
    107 }
    108 
    109 void FileStream::Context::OnAsyncFileOpened() {
    110   base::MessageLoopForIO::current()->RegisterIOHandler(file_.GetPlatformFile(),
    111                                                        this);
    112 }
    113 
    114 FileStream::Context::IOResult FileStream::Context::SeekFileImpl(Whence whence,
    115                                                                 int64 offset) {
    116   LARGE_INTEGER distance, res;
    117   distance.QuadPart = offset;
    118   DWORD move_method = static_cast<DWORD>(whence);
    119   if (SetFilePointerEx(file_.GetPlatformFile(), distance, &res, move_method)) {
    120     SetOffset(&io_context_.overlapped, res);
    121     return IOResult(res.QuadPart, 0);
    122   }
    123 
    124   return IOResult::FromOSError(GetLastError());
    125 }
    126 
    127 FileStream::Context::IOResult FileStream::Context::FlushFileImpl() {
    128   if (FlushFileBuffers(file_.GetPlatformFile()))
    129     return IOResult(OK, 0);
    130 
    131   return IOResult::FromOSError(GetLastError());
    132 }
    133 
    134 void FileStream::Context::IOCompletionIsPending(
    135     const CompletionCallback& callback,
    136     IOBuffer* buf) {
    137   DCHECK(callback_.is_null());
    138   callback_ = callback;
    139   in_flight_buf_ = buf;  // Hold until the async operation ends.
    140   async_in_progress_ = true;
    141 }
    142 
    143 void FileStream::Context::OnIOCompleted(
    144     base::MessageLoopForIO::IOContext* context,
    145     DWORD bytes_read,
    146     DWORD error) {
    147   DCHECK_EQ(&io_context_, context);
    148   DCHECK(!callback_.is_null());
    149   DCHECK(async_in_progress_);
    150 
    151   async_in_progress_ = false;
    152   if (orphaned_) {
    153     callback_.Reset();
    154     in_flight_buf_ = NULL;
    155     CloseAndDelete();
    156     return;
    157   }
    158 
    159   int result;
    160   if (error == ERROR_HANDLE_EOF) {
    161     result = 0;
    162   } else if (error) {
    163     IOResult error_result = IOResult::FromOSError(error);
    164     result = error_result.result;
    165   } else {
    166     result = bytes_read;
    167     IncrementOffset(&io_context_.overlapped, bytes_read);
    168   }
    169 
    170   CompletionCallback temp_callback = callback_;
    171   callback_.Reset();
    172   scoped_refptr<IOBuffer> temp_buf = in_flight_buf_;
    173   in_flight_buf_ = NULL;
    174   temp_callback.Run(result);
    175 }
    176 
    177 }  // namespace net
    178