Home | History | Annotate | Download | only in disk_cache
      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/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::PlatformFile file)
     73     : init_(true), mixed_(true), platform_file_(INVALID_HANDLE_VALUE),
     74       sync_platform_file_(file) {
     75 }
     76 
     77 bool File::Init(const base::FilePath& name) {
     78   DCHECK(!init_);
     79   if (init_)
     80     return false;
     81 
     82   DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
     83   DWORD access = GENERIC_READ | GENERIC_WRITE | DELETE;
     84   platform_file_ = CreateFile(name.value().c_str(), access, sharing, NULL,
     85                               OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
     86 
     87   if (INVALID_HANDLE_VALUE == platform_file_)
     88     return false;
     89 
     90   base::MessageLoopForIO::current()->RegisterIOHandler(
     91       platform_file_, g_completion_handler.Pointer());
     92 
     93   init_ = true;
     94   sync_platform_file_  = CreateFile(name.value().c_str(), access, sharing, NULL,
     95                                     OPEN_EXISTING, 0, NULL);
     96 
     97   if (INVALID_HANDLE_VALUE == sync_platform_file_)
     98     return false;
     99 
    100   return true;
    101 }
    102 
    103 File::~File() {
    104   if (!init_)
    105     return;
    106 
    107   if (INVALID_HANDLE_VALUE != platform_file_)
    108     CloseHandle(platform_file_);
    109   if (INVALID_HANDLE_VALUE != sync_platform_file_)
    110     CloseHandle(sync_platform_file_);
    111 }
    112 
    113 base::PlatformFile File::platform_file() const {
    114   DCHECK(init_);
    115   return (INVALID_HANDLE_VALUE == platform_file_) ? sync_platform_file_ :
    116                                                     platform_file_;
    117 }
    118 
    119 bool File::IsValid() const {
    120   if (!init_)
    121     return false;
    122   return (INVALID_HANDLE_VALUE != platform_file_ ||
    123           INVALID_HANDLE_VALUE != sync_platform_file_);
    124 }
    125 
    126 bool File::Read(void* buffer, size_t buffer_len, size_t offset) {
    127   DCHECK(init_);
    128   if (buffer_len > ULONG_MAX || offset > LONG_MAX)
    129     return false;
    130 
    131   DWORD ret = SetFilePointer(sync_platform_file_, static_cast<LONG>(offset),
    132                              NULL, FILE_BEGIN);
    133   if (INVALID_SET_FILE_POINTER == ret)
    134     return false;
    135 
    136   DWORD actual;
    137   DWORD size = static_cast<DWORD>(buffer_len);
    138   if (!ReadFile(sync_platform_file_, buffer, size, &actual, NULL))
    139     return false;
    140   return actual == size;
    141 }
    142 
    143 bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
    144   DCHECK(init_);
    145   if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
    146     return false;
    147 
    148   DWORD ret = SetFilePointer(sync_platform_file_, static_cast<LONG>(offset),
    149                              NULL, FILE_BEGIN);
    150   if (INVALID_SET_FILE_POINTER == ret)
    151     return false;
    152 
    153   DWORD actual;
    154   DWORD size = static_cast<DWORD>(buffer_len);
    155   if (!WriteFile(sync_platform_file_, buffer, size, &actual, NULL))
    156     return false;
    157   return actual == size;
    158 }
    159 
    160 // We have to increase the ref counter of the file before performing the IO to
    161 // prevent the completion to happen with an invalid handle (if the file is
    162 // closed while the IO is in flight).
    163 bool File::Read(void* buffer, size_t buffer_len, size_t offset,
    164                 FileIOCallback* callback, bool* completed) {
    165   DCHECK(init_);
    166   if (!callback) {
    167     if (completed)
    168       *completed = true;
    169     return Read(buffer, buffer_len, offset);
    170   }
    171 
    172   if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
    173     return false;
    174 
    175   MyOverlapped* data = new MyOverlapped(this, offset, callback);
    176   DWORD size = static_cast<DWORD>(buffer_len);
    177 
    178   DWORD actual;
    179   if (!ReadFile(platform_file_, buffer, size, &actual, data->overlapped())) {
    180     *completed = false;
    181     if (GetLastError() == ERROR_IO_PENDING)
    182       return true;
    183     delete data;
    184     return false;
    185   }
    186 
    187   // The operation completed already. We'll be called back anyway.
    188   *completed = (actual == size);
    189   DCHECK_EQ(size, actual);
    190   data->callback_ = NULL;
    191   data->file_ = NULL;  // There is no reason to hold on to this anymore.
    192   return *completed;
    193 }
    194 
    195 bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
    196                  FileIOCallback* callback, bool* completed) {
    197   DCHECK(init_);
    198   if (!callback) {
    199     if (completed)
    200       *completed = true;
    201     return Write(buffer, buffer_len, offset);
    202   }
    203 
    204   return AsyncWrite(buffer, buffer_len, offset, callback, completed);
    205 }
    206 
    207 bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset,
    208                       FileIOCallback* callback, bool* completed) {
    209   DCHECK(init_);
    210   DCHECK(callback);
    211   DCHECK(completed);
    212   if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
    213     return false;
    214 
    215   MyOverlapped* data = new MyOverlapped(this, offset, callback);
    216   DWORD size = static_cast<DWORD>(buffer_len);
    217 
    218   DWORD actual;
    219   if (!WriteFile(platform_file_, buffer, size, &actual, data->overlapped())) {
    220     *completed = false;
    221     if (GetLastError() == ERROR_IO_PENDING)
    222       return true;
    223     delete data;
    224     return false;
    225   }
    226 
    227   // The operation completed already. We'll be called back anyway.
    228   *completed = (actual == size);
    229   DCHECK_EQ(size, actual);
    230   data->callback_ = NULL;
    231   data->file_ = NULL;  // There is no reason to hold on to this anymore.
    232   return *completed;
    233 }
    234 
    235 bool File::SetLength(size_t length) {
    236   DCHECK(init_);
    237   if (length > ULONG_MAX)
    238     return false;
    239 
    240   DWORD size = static_cast<DWORD>(length);
    241   HANDLE file = platform_file();
    242   if (INVALID_SET_FILE_POINTER == SetFilePointer(file, size, NULL, FILE_BEGIN))
    243     return false;
    244 
    245   return TRUE == SetEndOfFile(file);
    246 }
    247 
    248 size_t File::GetLength() {
    249   DCHECK(init_);
    250   LARGE_INTEGER size;
    251   HANDLE file = platform_file();
    252   if (!GetFileSizeEx(file, &size))
    253     return 0;
    254   if (size.HighPart)
    255     return ULONG_MAX;
    256 
    257   return static_cast<size_t>(size.LowPart);
    258 }
    259 
    260 // Static.
    261 void File::WaitForPendingIO(int* num_pending_io) {
    262   while (*num_pending_io) {
    263     // Asynchronous IO operations may be in flight and the completion may end
    264     // up calling us back so let's wait for them.
    265     base::MessageLoopForIO::IOHandler* handler = g_completion_handler.Pointer();
    266     base::MessageLoopForIO::current()->WaitForIOCompletion(100, handler);
    267   }
    268 }
    269 
    270 // Static.
    271 void File::DropPendingIO() {
    272 }
    273 
    274 }  // namespace disk_cache
    275