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