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