Home | History | Annotate | Download | only in blockfile
      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/disk_cache/blockfile/file.h"
      6 
      7 #include "base/files/file_path.h"
      8 #include "base/lazy_instance.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "net/base/net_errors.h"
     11 #include "net/disk_cache/disk_cache.h"
     12 
     13 namespace {
     14 
     15 // Structure used for asynchronous operations.
     16 struct MyOverlapped {
     17   MyOverlapped(disk_cache::File* file, size_t offset,
     18                disk_cache::FileIOCallback* callback);
     19   ~MyOverlapped() {}
     20   OVERLAPPED* overlapped() {
     21     return &context_.overlapped;
     22   }
     23 
     24   base::MessageLoopForIO::IOContext context_;
     25   scoped_refptr<disk_cache::File> file_;
     26   disk_cache::FileIOCallback* callback_;
     27 };
     28 
     29 COMPILE_ASSERT(!offsetof(MyOverlapped, context_), starts_with_overlapped);
     30 
     31 // Helper class to handle the IO completion notifications from the message loop.
     32 class CompletionHandler : public base::MessageLoopForIO::IOHandler {
     33   virtual void OnIOCompleted(base::MessageLoopForIO::IOContext* context,
     34                              DWORD actual_bytes,
     35                              DWORD error);
     36 };
     37 
     38 static base::LazyInstance<CompletionHandler> g_completion_handler =
     39     LAZY_INSTANCE_INITIALIZER;
     40 
     41 void CompletionHandler::OnIOCompleted(
     42     base::MessageLoopForIO::IOContext* context,
     43     DWORD actual_bytes,
     44     DWORD error) {
     45   MyOverlapped* data = reinterpret_cast<MyOverlapped*>(context);
     46 
     47   if (error) {
     48     DCHECK(!actual_bytes);
     49     actual_bytes = static_cast<DWORD>(net::ERR_CACHE_READ_FAILURE);
     50     NOTREACHED();
     51   }
     52 
     53   if (data->callback_)
     54     data->callback_->OnFileIOComplete(static_cast<int>(actual_bytes));
     55 
     56   delete data;
     57 }
     58 
     59 MyOverlapped::MyOverlapped(disk_cache::File* file, size_t offset,
     60                            disk_cache::FileIOCallback* callback) {
     61   memset(this, 0, sizeof(*this));
     62   context_.handler = g_completion_handler.Pointer();
     63   context_.overlapped.Offset = static_cast<DWORD>(offset);
     64   file_ = file;
     65   callback_ = callback;
     66 }
     67 
     68 }  // namespace
     69 
     70 namespace disk_cache {
     71 
     72 File::File(base::File file)
     73     : init_(true),
     74       mixed_(true),
     75       sync_base_file_(file.Pass()) {
     76 }
     77 
     78 bool File::Init(const base::FilePath& name) {
     79   DCHECK(!init_);
     80   if (init_)
     81     return false;
     82 
     83   DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
     84   DWORD access = GENERIC_READ | GENERIC_WRITE | DELETE;
     85   base_file_ =
     86       base::File(CreateFile(name.value().c_str(), access, sharing, NULL,
     87                             OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL));
     88 
     89   if (!base_file_.IsValid())
     90     return false;
     91 
     92   base::MessageLoopForIO::current()->RegisterIOHandler(
     93       base_file_.GetPlatformFile(), g_completion_handler.Pointer());
     94 
     95   init_ = true;
     96   sync_base_file_  =
     97     base::File(CreateFile(name.value().c_str(), access, sharing, NULL,
     98                           OPEN_EXISTING, 0, NULL));
     99 
    100   if (!sync_base_file_.IsValid())
    101     return false;
    102 
    103   return true;
    104 }
    105 
    106 bool File::IsValid() const {
    107   if (!init_)
    108     return false;
    109   return base_file_.IsValid() || sync_base_file_.IsValid();
    110 }
    111 
    112 bool File::Read(void* buffer, size_t buffer_len, size_t offset) {
    113   DCHECK(init_);
    114   if (buffer_len > ULONG_MAX || offset > LONG_MAX)
    115     return false;
    116 
    117   int ret = sync_base_file_.Read(offset, static_cast<char*>(buffer),
    118                                  buffer_len);
    119   return static_cast<int>(buffer_len) == ret;
    120 }
    121 
    122 bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
    123   DCHECK(init_);
    124   if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
    125     return false;
    126 
    127   int ret = sync_base_file_.Write(offset, static_cast<const char*>(buffer),
    128                                  buffer_len);
    129   return static_cast<int>(buffer_len) == ret;
    130 }
    131 
    132 // We have to increase the ref counter of the file before performing the IO to
    133 // prevent the completion to happen with an invalid handle (if the file is
    134 // closed while the IO is in flight).
    135 bool File::Read(void* buffer, size_t buffer_len, size_t offset,
    136                 FileIOCallback* callback, bool* completed) {
    137   DCHECK(init_);
    138   if (!callback) {
    139     if (completed)
    140       *completed = true;
    141     return Read(buffer, buffer_len, offset);
    142   }
    143 
    144   if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
    145     return false;
    146 
    147   MyOverlapped* data = new MyOverlapped(this, offset, callback);
    148   DWORD size = static_cast<DWORD>(buffer_len);
    149 
    150   DWORD actual;
    151   if (!ReadFile(base_file_.GetPlatformFile(), buffer, size, &actual,
    152                 data->overlapped())) {
    153     *completed = false;
    154     if (GetLastError() == ERROR_IO_PENDING)
    155       return true;
    156     delete data;
    157     return false;
    158   }
    159 
    160   // The operation completed already. We'll be called back anyway.
    161   *completed = (actual == size);
    162   DCHECK_EQ(size, actual);
    163   data->callback_ = NULL;
    164   data->file_ = NULL;  // There is no reason to hold on to this anymore.
    165   return *completed;
    166 }
    167 
    168 bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
    169                  FileIOCallback* callback, bool* completed) {
    170   DCHECK(init_);
    171   if (!callback) {
    172     if (completed)
    173       *completed = true;
    174     return Write(buffer, buffer_len, offset);
    175   }
    176 
    177   return AsyncWrite(buffer, buffer_len, offset, callback, completed);
    178 }
    179 
    180 File::~File() {
    181 }
    182 
    183 base::PlatformFile File::platform_file() const {
    184   DCHECK(init_);
    185   return base_file_.IsValid() ? base_file_.GetPlatformFile() :
    186                                 sync_base_file_.GetPlatformFile();
    187 }
    188 
    189 bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset,
    190                       FileIOCallback* callback, bool* completed) {
    191   DCHECK(init_);
    192   DCHECK(callback);
    193   DCHECK(completed);
    194   if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
    195     return false;
    196 
    197   MyOverlapped* data = new MyOverlapped(this, offset, callback);
    198   DWORD size = static_cast<DWORD>(buffer_len);
    199 
    200   DWORD actual;
    201   if (!WriteFile(base_file_.GetPlatformFile(), buffer, size, &actual,
    202                  data->overlapped())) {
    203     *completed = false;
    204     if (GetLastError() == ERROR_IO_PENDING)
    205       return true;
    206     delete data;
    207     return false;
    208   }
    209 
    210   // The operation completed already. We'll be called back anyway.
    211   *completed = (actual == size);
    212   DCHECK_EQ(size, actual);
    213   data->callback_ = NULL;
    214   data->file_ = NULL;  // There is no reason to hold on to this anymore.
    215   return *completed;
    216 }
    217 
    218 bool File::SetLength(size_t length) {
    219   DCHECK(init_);
    220   if (length > ULONG_MAX)
    221     return false;
    222 
    223   DWORD size = static_cast<DWORD>(length);
    224   HANDLE file = platform_file();
    225   if (INVALID_SET_FILE_POINTER == SetFilePointer(file, size, NULL, FILE_BEGIN))
    226     return false;
    227 
    228   return TRUE == SetEndOfFile(file);
    229 }
    230 
    231 size_t File::GetLength() {
    232   DCHECK(init_);
    233   LARGE_INTEGER size;
    234   HANDLE file = platform_file();
    235   if (!GetFileSizeEx(file, &size))
    236     return 0;
    237   if (size.HighPart)
    238     return ULONG_MAX;
    239 
    240   return static_cast<size_t>(size.LowPart);
    241 }
    242 
    243 // Static.
    244 void File::WaitForPendingIO(int* num_pending_io) {
    245   while (*num_pending_io) {
    246     // Asynchronous IO operations may be in flight and the completion may end
    247     // up calling us back so let's wait for them.
    248     base::MessageLoopForIO::IOHandler* handler = g_completion_handler.Pointer();
    249     base::MessageLoopForIO::current()->WaitForIOCompletion(100, handler);
    250   }
    251 }
    252 
    253 // Static.
    254 void File::DropPendingIO() {
    255 }
    256 
    257 }  // namespace disk_cache
    258