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);
~MyOverlapped__anon6c3639600111::MyOverlapped19 ~MyOverlapped() {}
overlapped__anon6c3639600111::MyOverlapped20 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
OnIOCompleted(base::MessageLoopForIO::IOContext * context,DWORD actual_bytes,DWORD error)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
MyOverlapped(disk_cache::File * file,size_t offset,disk_cache::FileIOCallback * callback)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
File(base::File file)72 File::File(base::File file)
73 : init_(true),
74 mixed_(true),
75 sync_base_file_(file.Pass()) {
76 }
77
Init(const base::FilePath & name)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
IsValid() const106 bool File::IsValid() const {
107 if (!init_)
108 return false;
109 return base_file_.IsValid() || sync_base_file_.IsValid();
110 }
111
Read(void * buffer,size_t buffer_len,size_t offset)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
Write(const void * buffer,size_t buffer_len,size_t offset)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).
Read(void * buffer,size_t buffer_len,size_t offset,FileIOCallback * callback,bool * completed)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
Write(const void * buffer,size_t buffer_len,size_t offset,FileIOCallback * callback,bool * completed)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
~File()180 File::~File() {
181 }
182
platform_file() const183 base::PlatformFile File::platform_file() const {
184 DCHECK(init_);
185 return base_file_.IsValid() ? base_file_.GetPlatformFile() :
186 sync_base_file_.GetPlatformFile();
187 }
188
AsyncWrite(const void * buffer,size_t buffer_len,size_t offset,FileIOCallback * callback,bool * completed)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
SetLength(size_t length)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
GetLength()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.
WaitForPendingIO(int * num_pending_io)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.
DropPendingIO()254 void File::DropPendingIO() {
255 }
256
257 } // namespace disk_cache
258