Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2008 The Chromium Authors. All rights reserved.  Use of this
      2 // source code is governed by a BSD-style license that can be found in the
      3 // LICENSE file.
      4 
      5 #include "net/base/file_stream.h"
      6 
      7 #include <windows.h>
      8 
      9 #include "base/file_path.h"
     10 #include "base/logging.h"
     11 #include "base/message_loop.h"
     12 #include "net/base/net_errors.h"
     13 
     14 namespace net {
     15 
     16 // Ensure that we can just use our Whence values directly.
     17 COMPILE_ASSERT(FROM_BEGIN == FILE_BEGIN, bad_whence_begin);
     18 COMPILE_ASSERT(FROM_CURRENT == FILE_CURRENT, bad_whence_current);
     19 COMPILE_ASSERT(FROM_END == FILE_END, bad_whence_end);
     20 
     21 static void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) {
     22   overlapped->Offset = offset.LowPart;
     23   overlapped->OffsetHigh = offset.HighPart;
     24 }
     25 
     26 static void IncrementOffset(OVERLAPPED* overlapped, DWORD count) {
     27   LARGE_INTEGER offset;
     28   offset.LowPart = overlapped->Offset;
     29   offset.HighPart = overlapped->OffsetHigh;
     30   offset.QuadPart += static_cast<LONGLONG>(count);
     31   SetOffset(overlapped, offset);
     32 }
     33 
     34 static int MapErrorCode(DWORD err) {
     35   switch (err) {
     36     case ERROR_FILE_NOT_FOUND:
     37     case ERROR_PATH_NOT_FOUND:
     38       return ERR_FILE_NOT_FOUND;
     39     case ERROR_ACCESS_DENIED:
     40       return ERR_ACCESS_DENIED;
     41     case ERROR_SUCCESS:
     42       return OK;
     43     default:
     44       LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
     45       return ERR_FAILED;
     46   }
     47 }
     48 
     49 // FileStream::AsyncContext ----------------------------------------------
     50 
     51 class FileStream::AsyncContext : public MessageLoopForIO::IOHandler {
     52  public:
     53   AsyncContext(FileStream* owner)
     54       : owner_(owner), context_(), callback_(NULL), is_closing_(false) {
     55     context_.handler = this;
     56   }
     57   ~AsyncContext();
     58 
     59   void IOCompletionIsPending(CompletionCallback* callback);
     60 
     61   OVERLAPPED* overlapped() { return &context_.overlapped; }
     62   CompletionCallback* callback() const { return callback_; }
     63 
     64  private:
     65   virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
     66                              DWORD bytes_read, DWORD error);
     67 
     68   FileStream* owner_;
     69   MessageLoopForIO::IOContext context_;
     70   CompletionCallback* callback_;
     71   bool is_closing_;
     72 };
     73 
     74 FileStream::AsyncContext::~AsyncContext() {
     75   is_closing_ = true;
     76   bool waited = false;
     77   base::Time start = base::Time::Now();
     78   while (callback_) {
     79     waited = true;
     80     MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
     81   }
     82   if (waited) {
     83     // We want to see if we block the message loop for too long.
     84     UMA_HISTOGRAM_TIMES("AsyncIO.FileStreamClose", base::Time::Now() - start);
     85   }
     86 }
     87 
     88 void FileStream::AsyncContext::IOCompletionIsPending(
     89     CompletionCallback* callback) {
     90   DCHECK(!callback_);
     91   callback_ = callback;
     92 }
     93 
     94 void FileStream::AsyncContext::OnIOCompleted(
     95     MessageLoopForIO::IOContext* context, DWORD bytes_read, DWORD error) {
     96   DCHECK(&context_ == context);
     97   DCHECK(callback_);
     98 
     99   if (is_closing_) {
    100     callback_ = NULL;
    101     return;
    102   }
    103 
    104   int result = static_cast<int>(bytes_read);
    105   if (error && error != ERROR_HANDLE_EOF)
    106     result = MapErrorCode(error);
    107 
    108   if (bytes_read)
    109     IncrementOffset(&context->overlapped, bytes_read);
    110 
    111   CompletionCallback* temp = NULL;
    112   std::swap(temp, callback_);
    113   temp->Run(result);
    114 }
    115 
    116 // FileStream ------------------------------------------------------------
    117 
    118 FileStream::FileStream()
    119     : file_(INVALID_HANDLE_VALUE),
    120       open_flags_(0) {
    121 }
    122 
    123 FileStream::FileStream(base::PlatformFile file, int flags)
    124     : file_(file),
    125       open_flags_(flags) {
    126   // If the file handle is opened with base::PLATFORM_FILE_ASYNC, we need to
    127   // make sure we will perform asynchronous File IO to it.
    128   if (flags & base::PLATFORM_FILE_ASYNC) {
    129     async_context_.reset(new AsyncContext(this));
    130     MessageLoopForIO::current()->RegisterIOHandler(file_,
    131                                                    async_context_.get());
    132   }
    133 }
    134 
    135 FileStream::~FileStream() {
    136   Close();
    137 }
    138 
    139 void FileStream::Close() {
    140   if (file_ != INVALID_HANDLE_VALUE)
    141     CancelIo(file_);
    142 
    143   async_context_.reset();
    144   if (file_ != INVALID_HANDLE_VALUE) {
    145     CloseHandle(file_);
    146     file_ = INVALID_HANDLE_VALUE;
    147   }
    148 }
    149 
    150 int FileStream::Open(const FilePath& path, int open_flags) {
    151   if (IsOpen()) {
    152     DLOG(FATAL) << "File is already open!";
    153     return ERR_UNEXPECTED;
    154   }
    155 
    156   open_flags_ = open_flags;
    157   file_ = base::CreatePlatformFile(path.value(), open_flags_, NULL);
    158   if (file_ == INVALID_HANDLE_VALUE) {
    159     DWORD error = GetLastError();
    160     LOG(WARNING) << "Failed to open file: " << error;
    161     return MapErrorCode(error);
    162   }
    163 
    164   if (open_flags_ & base::PLATFORM_FILE_ASYNC) {
    165     async_context_.reset(new AsyncContext(this));
    166     MessageLoopForIO::current()->RegisterIOHandler(file_,
    167                                                    async_context_.get());
    168   }
    169 
    170   return OK;
    171 }
    172 
    173 bool FileStream::IsOpen() const {
    174   return file_ != INVALID_HANDLE_VALUE;
    175 }
    176 
    177 int64 FileStream::Seek(Whence whence, int64 offset) {
    178   if (!IsOpen())
    179     return ERR_UNEXPECTED;
    180   DCHECK(!async_context_.get() || !async_context_->callback());
    181 
    182   LARGE_INTEGER distance, result;
    183   distance.QuadPart = offset;
    184   DWORD move_method = static_cast<DWORD>(whence);
    185   if (!SetFilePointerEx(file_, distance, &result, move_method)) {
    186     DWORD error = GetLastError();
    187     LOG(WARNING) << "SetFilePointerEx failed: " << error;
    188     return MapErrorCode(error);
    189   }
    190   if (async_context_.get())
    191     SetOffset(async_context_->overlapped(), result);
    192   return result.QuadPart;
    193 }
    194 
    195 int64 FileStream::Available() {
    196   if (!IsOpen())
    197     return ERR_UNEXPECTED;
    198 
    199   int64 cur_pos = Seek(FROM_CURRENT, 0);
    200   if (cur_pos < 0)
    201     return cur_pos;
    202 
    203   LARGE_INTEGER file_size;
    204   if (!GetFileSizeEx(file_, &file_size)) {
    205     DWORD error = GetLastError();
    206     LOG(WARNING) << "GetFileSizeEx failed: " << error;
    207     return MapErrorCode(error);
    208   }
    209 
    210   return file_size.QuadPart - cur_pos;
    211 }
    212 
    213 int FileStream::Read(
    214     char* buf, int buf_len, CompletionCallback* callback) {
    215   if (!IsOpen())
    216     return ERR_UNEXPECTED;
    217   DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
    218 
    219   OVERLAPPED* overlapped = NULL;
    220   if (async_context_.get()) {
    221     DCHECK(!async_context_->callback());
    222     overlapped = async_context_->overlapped();
    223   }
    224 
    225   int rv;
    226 
    227   DWORD bytes_read;
    228   if (!ReadFile(file_, buf, buf_len, &bytes_read, overlapped)) {
    229     DWORD error = GetLastError();
    230     if (async_context_.get() && error == ERROR_IO_PENDING) {
    231       async_context_->IOCompletionIsPending(callback);
    232       rv = ERR_IO_PENDING;
    233     } else if (error == ERROR_HANDLE_EOF) {
    234       rv = 0;  // Report EOF by returning 0 bytes read.
    235     } else {
    236       LOG(WARNING) << "ReadFile failed: " << error;
    237       rv = MapErrorCode(error);
    238     }
    239   } else if (overlapped) {
    240     async_context_->IOCompletionIsPending(callback);
    241     rv = ERR_IO_PENDING;
    242   } else {
    243     rv = static_cast<int>(bytes_read);
    244   }
    245   return rv;
    246 }
    247 
    248 int FileStream::ReadUntilComplete(char *buf, int buf_len) {
    249   int to_read = buf_len;
    250   int bytes_total = 0;
    251 
    252   do {
    253     int bytes_read = Read(buf, to_read, NULL);
    254     if (bytes_read <= 0) {
    255       if (bytes_total == 0)
    256         return bytes_read;
    257 
    258       return bytes_total;
    259     }
    260 
    261     bytes_total += bytes_read;
    262     buf += bytes_read;
    263     to_read -= bytes_read;
    264   } while (bytes_total < buf_len);
    265 
    266   return bytes_total;
    267 }
    268 
    269 int FileStream::Write(
    270     const char* buf, int buf_len, CompletionCallback* callback) {
    271   if (!IsOpen())
    272     return ERR_UNEXPECTED;
    273   DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
    274 
    275   OVERLAPPED* overlapped = NULL;
    276   if (async_context_.get()) {
    277     DCHECK(!async_context_->callback());
    278     overlapped = async_context_->overlapped();
    279   }
    280 
    281   int rv;
    282   DWORD bytes_written;
    283   if (!WriteFile(file_, buf, buf_len, &bytes_written, overlapped)) {
    284     DWORD error = GetLastError();
    285     if (async_context_.get() && error == ERROR_IO_PENDING) {
    286       async_context_->IOCompletionIsPending(callback);
    287       rv = ERR_IO_PENDING;
    288     } else {
    289       LOG(WARNING) << "WriteFile failed: " << error;
    290       rv = MapErrorCode(error);
    291     }
    292   } else if (overlapped) {
    293     async_context_->IOCompletionIsPending(callback);
    294     rv = ERR_IO_PENDING;
    295   } else {
    296     rv = static_cast<int>(bytes_written);
    297   }
    298   return rv;
    299 }
    300 
    301 int64 FileStream::Truncate(int64 bytes) {
    302   if (!IsOpen())
    303     return ERR_UNEXPECTED;
    304 
    305   // We better be open for reading.
    306   DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
    307 
    308   // Seek to the position to truncate from.
    309   int64 seek_position = Seek(FROM_BEGIN, bytes);
    310   if (seek_position != bytes)
    311     return ERR_UNEXPECTED;
    312 
    313   // And truncate the file.
    314   BOOL result = SetEndOfFile(file_);
    315   if (!result) {
    316     DWORD error = GetLastError();
    317     LOG(WARNING) << "SetEndOfFile failed: " << error;
    318     return MapErrorCode(error);
    319   }
    320 
    321   // Success.
    322   return seek_position;
    323 }
    324 
    325 }  // namespace net
    326