1 // Copyright (c) 2006-2008 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/message_loop.h"
9 #include "base/singleton.h"
10 #include "net/disk_cache/disk_cache.h"
11
12 namespace {
13
14 // Structure used for asynchronous operations.
15 struct MyOverlapped {
16 MyOverlapped(disk_cache::File* file, size_t offset,
17 disk_cache::FileIOCallback* callback);
18 ~MyOverlapped();
overlapped__anonb49f38d20111::MyOverlapped19 OVERLAPPED* overlapped() {
20 return &context_.overlapped;
21 }
22
23 MessageLoopForIO::IOContext context_;
24 scoped_refptr<disk_cache::File> file_;
25 disk_cache::FileIOCallback* callback_;
26 const void* buffer_;
27 bool delete_buffer_; // Delete the user buffer at completion.
28 };
29
30 COMPILE_ASSERT(!offsetof(MyOverlapped, context_), starts_with_overlapped);
31
32 // Helper class to handle the IO completion notifications from the message loop.
33 class CompletionHandler : public MessageLoopForIO::IOHandler {
34 virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
35 DWORD actual_bytes, DWORD error);
36 };
37
OnIOCompleted(MessageLoopForIO::IOContext * context,DWORD actual_bytes,DWORD error)38 void CompletionHandler::OnIOCompleted(MessageLoopForIO::IOContext* context,
39 DWORD actual_bytes, DWORD error) {
40 MyOverlapped* data = reinterpret_cast<MyOverlapped*>(context);
41
42 if (error) {
43 DCHECK(!actual_bytes);
44 actual_bytes = static_cast<DWORD>(-1);
45 NOTREACHED();
46 }
47
48 if (data->callback_)
49 data->callback_->OnFileIOComplete(static_cast<int>(actual_bytes));
50
51 delete data;
52 }
53
MyOverlapped(disk_cache::File * file,size_t offset,disk_cache::FileIOCallback * callback)54 MyOverlapped::MyOverlapped(disk_cache::File* file, size_t offset,
55 disk_cache::FileIOCallback* callback) {
56 memset(this, 0, sizeof(*this));
57 context_.handler = Singleton<CompletionHandler>::get();
58 context_.overlapped.Offset = static_cast<DWORD>(offset);
59 file_ = file;
60 callback_ = callback;
61 }
62
~MyOverlapped()63 MyOverlapped::~MyOverlapped() {
64 if (delete_buffer_) {
65 DCHECK(!callback_);
66 // This whole thing could be updated to use IOBuffer, but PostWrite is not
67 // used at the moment. TODO(rvargas): remove or update this code.
68 delete[] reinterpret_cast<const char*>(buffer_);
69 }
70 }
71
72 } // namespace
73
74 namespace disk_cache {
75
File(base::PlatformFile file)76 File::File(base::PlatformFile file)
77 : init_(true), mixed_(true), platform_file_(INVALID_HANDLE_VALUE),
78 sync_platform_file_(file) {
79 }
80
Init(const FilePath & name)81 bool File::Init(const FilePath& name) {
82 DCHECK(!init_);
83 if (init_)
84 return false;
85
86 platform_file_ = CreateFile(name.value().c_str(),
87 GENERIC_READ | GENERIC_WRITE,
88 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
89 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
90
91 if (INVALID_HANDLE_VALUE == platform_file_)
92 return false;
93
94 MessageLoopForIO::current()->RegisterIOHandler(
95 platform_file_, Singleton<CompletionHandler>::get());
96
97 init_ = true;
98 sync_platform_file_ = CreateFile(name.value().c_str(),
99 GENERIC_READ | GENERIC_WRITE,
100 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
101 OPEN_EXISTING, 0, NULL);
102
103 if (INVALID_HANDLE_VALUE == sync_platform_file_)
104 return false;
105
106 return true;
107 }
108
~File()109 File::~File() {
110 if (!init_)
111 return;
112
113 if (INVALID_HANDLE_VALUE != platform_file_)
114 CloseHandle(platform_file_);
115 if (INVALID_HANDLE_VALUE != sync_platform_file_)
116 CloseHandle(sync_platform_file_);
117 }
118
platform_file() const119 base::PlatformFile File::platform_file() const {
120 DCHECK(init_);
121 return (INVALID_HANDLE_VALUE == platform_file_) ? sync_platform_file_ :
122 platform_file_;
123 }
124
IsValid() const125 bool File::IsValid() const {
126 if (!init_)
127 return false;
128 return (INVALID_HANDLE_VALUE != platform_file_ ||
129 INVALID_HANDLE_VALUE != sync_platform_file_);
130 }
131
Read(void * buffer,size_t buffer_len,size_t offset)132 bool File::Read(void* buffer, size_t buffer_len, size_t offset) {
133 DCHECK(init_);
134 if (buffer_len > ULONG_MAX || offset > LONG_MAX)
135 return false;
136
137 DWORD ret = SetFilePointer(sync_platform_file_, static_cast<LONG>(offset),
138 NULL, FILE_BEGIN);
139 if (INVALID_SET_FILE_POINTER == ret)
140 return false;
141
142 DWORD actual;
143 DWORD size = static_cast<DWORD>(buffer_len);
144 if (!ReadFile(sync_platform_file_, buffer, size, &actual, NULL))
145 return false;
146 return actual == size;
147 }
148
Write(const void * buffer,size_t buffer_len,size_t offset)149 bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
150 DCHECK(init_);
151 if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
152 return false;
153
154 DWORD ret = SetFilePointer(sync_platform_file_, static_cast<LONG>(offset),
155 NULL, FILE_BEGIN);
156 if (INVALID_SET_FILE_POINTER == ret)
157 return false;
158
159 DWORD actual;
160 DWORD size = static_cast<DWORD>(buffer_len);
161 if (!WriteFile(sync_platform_file_, buffer, size, &actual, NULL))
162 return false;
163 return actual == size;
164 }
165
166 // We have to increase the ref counter of the file before performing the IO to
167 // prevent the completion to happen with an invalid handle (if the file is
168 // closed while the IO is in flight).
Read(void * buffer,size_t buffer_len,size_t offset,FileIOCallback * callback,bool * completed)169 bool File::Read(void* buffer, size_t buffer_len, size_t offset,
170 FileIOCallback* callback, bool* completed) {
171 DCHECK(init_);
172 if (!callback) {
173 if (completed)
174 *completed = true;
175 return Read(buffer, buffer_len, offset);
176 }
177
178 if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
179 return false;
180
181 MyOverlapped* data = new MyOverlapped(this, offset, callback);
182 DWORD size = static_cast<DWORD>(buffer_len);
183
184 DWORD actual;
185 if (!ReadFile(platform_file_, buffer, size, &actual, data->overlapped())) {
186 *completed = false;
187 if (GetLastError() == ERROR_IO_PENDING)
188 return true;
189 delete data;
190 return false;
191 }
192
193 // The operation completed already. We'll be called back anyway.
194 *completed = (actual == size);
195 DCHECK(actual == size);
196 data->callback_ = NULL;
197 data->file_ = NULL; // There is no reason to hold on to this anymore.
198 return *completed;
199 }
200
Write(const void * buffer,size_t buffer_len,size_t offset,FileIOCallback * callback,bool * completed)201 bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
202 FileIOCallback* callback, bool* completed) {
203 DCHECK(init_);
204 if (!callback) {
205 if (completed)
206 *completed = true;
207 return Write(buffer, buffer_len, offset);
208 }
209
210 return AsyncWrite(buffer, buffer_len, offset, true, callback, completed);
211 }
212
PostWrite(const void * buffer,size_t buffer_len,size_t offset)213 bool File::PostWrite(const void* buffer, size_t buffer_len, size_t offset) {
214 DCHECK(init_);
215 return AsyncWrite(buffer, buffer_len, offset, false, NULL, NULL);
216 }
217
AsyncWrite(const void * buffer,size_t buffer_len,size_t offset,bool notify,FileIOCallback * callback,bool * completed)218 bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset,
219 bool notify, FileIOCallback* callback, bool* completed) {
220 DCHECK(init_);
221 if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
222 return false;
223
224 MyOverlapped* data = new MyOverlapped(this, offset, callback);
225 bool dummy_completed;
226 if (!callback) {
227 DCHECK(!notify);
228 data->delete_buffer_ = true;
229 data->buffer_ = buffer;
230 completed = &dummy_completed;
231 }
232
233 DWORD size = static_cast<DWORD>(buffer_len);
234
235 DWORD actual;
236 if (!WriteFile(platform_file_, buffer, size, &actual, data->overlapped())) {
237 *completed = false;
238 if (GetLastError() == ERROR_IO_PENDING)
239 return true;
240 delete data;
241 return false;
242 }
243
244 // The operation completed already. We'll be called back anyway.
245 *completed = (actual == size);
246 DCHECK(actual == size);
247 data->callback_ = NULL;
248 data->file_ = NULL; // There is no reason to hold on to this anymore.
249 return *completed;
250 }
251
SetLength(size_t length)252 bool File::SetLength(size_t length) {
253 DCHECK(init_);
254 if (length > ULONG_MAX)
255 return false;
256
257 DWORD size = static_cast<DWORD>(length);
258 HANDLE file = platform_file();
259 if (INVALID_SET_FILE_POINTER == SetFilePointer(file, size, NULL, FILE_BEGIN))
260 return false;
261
262 return TRUE == SetEndOfFile(file);
263 }
264
GetLength()265 size_t File::GetLength() {
266 DCHECK(init_);
267 LARGE_INTEGER size;
268 HANDLE file = platform_file();
269 if (!GetFileSizeEx(file, &size))
270 return 0;
271 if (size.HighPart)
272 return ULONG_MAX;
273
274 return static_cast<size_t>(size.LowPart);
275 }
276
277 // Static.
WaitForPendingIO(int * num_pending_io)278 void File::WaitForPendingIO(int* num_pending_io) {
279 while (*num_pending_io) {
280 // Asynchronous IO operations may be in flight and the completion may end
281 // up calling us back so let's wait for them.
282 MessageLoopForIO::IOHandler* handler = Singleton<CompletionHandler>::get();
283 MessageLoopForIO::current()->WaitForIOCompletion(100, handler);
284 }
285 }
286
287 } // namespace disk_cache
288