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 13namespace { 14 15// Structure used for asynchronous operations. 16struct 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 29COMPILE_ASSERT(!offsetof(MyOverlapped, context_), starts_with_overlapped); 30 31// Helper class to handle the IO completion notifications from the message loop. 32class CompletionHandler : public MessageLoopForIO::IOHandler { 33 virtual void OnIOCompleted(MessageLoopForIO::IOContext* context, 34 DWORD actual_bytes, DWORD error); 35}; 36 37static base::LazyInstance<CompletionHandler> g_completion_handler( 38 base::LINKER_INITIALIZED); 39 40void 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 56MyOverlapped::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 67namespace disk_cache { 68 69File::File(base::PlatformFile file) 70 : init_(true), mixed_(true), platform_file_(INVALID_HANDLE_VALUE), 71 sync_platform_file_(file) { 72} 73 74bool 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 100File::~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 110base::PlatformFile File::platform_file() const { 111 DCHECK(init_); 112 return (INVALID_HANDLE_VALUE == platform_file_) ? sync_platform_file_ : 113 platform_file_; 114} 115 116bool 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 123bool 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 140bool 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). 160bool 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 192bool 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 204bool 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 232bool 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 245size_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. 258void 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