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