1 // Copyright 2012 The Chromium Authors
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 <limits.h>
8
9 #include <utility>
10
11 #include "base/compiler_specific.h"
12 #include "base/files/file_path.h"
13 #include "base/memory/raw_ptr.h"
14 #include "base/message_loop/message_pump_for_io.h"
15 #include "base/no_destructor.h"
16 #include "base/run_loop.h"
17 #include "base/strings/string_util.h"
18 #include "base/task/current_thread.h"
19 #include "base/task/thread_pool.h"
20 #include "base/task/thread_pool/thread_pool_instance.h"
21 #include "base/threading/platform_thread.h"
22 #include "net/base/net_errors.h"
23 #include "net/disk_cache/disk_cache.h"
24
25 namespace {
26
27 class CompletionHandler;
28 // Structure used for asynchronous operations.
29 struct MyOverlapped {
30 MyOverlapped(disk_cache::File* file, size_t offset,
31 disk_cache::FileIOCallback* callback);
~MyOverlapped__anon6bf80ee50111::MyOverlapped32 ~MyOverlapped() {}
overlapped__anon6bf80ee50111::MyOverlapped33 OVERLAPPED* overlapped() {
34 return &context_.overlapped;
35 }
36
37 base::MessagePumpForIO::IOContext context_;
38 scoped_refptr<disk_cache::File> file_;
39 scoped_refptr<CompletionHandler> completion_handler_;
40 raw_ptr<disk_cache::FileIOCallback> callback_;
41 };
42
43 static_assert(offsetof(MyOverlapped, context_) == 0,
44 "should start with overlapped");
45
46 // Helper class to handle the IO completion notifications from the message loop.
47 class CompletionHandler final : public base::MessagePumpForIO::IOHandler,
48 public base::RefCounted<CompletionHandler> {
49 public:
CompletionHandler()50 CompletionHandler() : base::MessagePumpForIO::IOHandler(FROM_HERE) {}
51 static CompletionHandler* Get();
52
53 CompletionHandler(const CompletionHandler&) = delete;
54 CompletionHandler& operator=(const CompletionHandler&) = delete;
55
56 private:
57 friend class base::RefCounted<CompletionHandler>;
~CompletionHandler()58 ~CompletionHandler() override {}
59
60 // implement base::MessagePumpForIO::IOHandler.
61 void OnIOCompleted(base::MessagePumpForIO::IOContext* context,
62 DWORD actual_bytes,
63 DWORD error) override;
64 };
65
Get()66 CompletionHandler* CompletionHandler::Get() {
67 static base::NoDestructor<scoped_refptr<CompletionHandler>> handler(
68 base::MakeRefCounted<CompletionHandler>());
69 return handler->get();
70 }
71
OnIOCompleted(base::MessagePumpForIO::IOContext * context,DWORD actual_bytes,DWORD error)72 void CompletionHandler::OnIOCompleted(
73 base::MessagePumpForIO::IOContext* context,
74 DWORD actual_bytes,
75 DWORD error) {
76 MyOverlapped* data = reinterpret_cast<MyOverlapped*>(context);
77
78 if (error) {
79 DCHECK(!actual_bytes);
80 actual_bytes = static_cast<DWORD>(net::ERR_CACHE_READ_FAILURE);
81 }
82
83 // `callback_` may self delete while in `OnFileIOComplete`.
84 if (data->callback_)
85 data->callback_.ExtractAsDangling()->OnFileIOComplete(
86 static_cast<int>(actual_bytes));
87
88 delete data;
89 }
90
MyOverlapped(disk_cache::File * file,size_t offset,disk_cache::FileIOCallback * callback)91 MyOverlapped::MyOverlapped(disk_cache::File* file, size_t offset,
92 disk_cache::FileIOCallback* callback) {
93 context_.overlapped.Offset = static_cast<DWORD>(offset);
94 file_ = file;
95 callback_ = callback;
96 completion_handler_ = CompletionHandler::Get();
97 }
98
99 } // namespace
100
101 namespace disk_cache {
102
File(base::File file)103 File::File(base::File file)
104 : init_(true), mixed_(true), sync_base_file_(std::move(file)) {}
105
Init(const base::FilePath & name)106 bool File::Init(const base::FilePath& name) {
107 DCHECK(!init_);
108 if (init_)
109 return false;
110
111 DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
112 DWORD access = GENERIC_READ | GENERIC_WRITE | DELETE;
113 base_file_ =
114 base::File(CreateFile(name.value().c_str(), access, sharing, nullptr,
115 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr));
116
117 if (!base_file_.IsValid())
118 return false;
119
120 if (!base::CurrentIOThread::Get()->RegisterIOHandler(
121 base_file_.GetPlatformFile(), CompletionHandler::Get())) {
122 return false;
123 }
124
125 init_ = true;
126 sync_base_file_ = base::File(CreateFile(name.value().c_str(), access, sharing,
127 nullptr, OPEN_EXISTING, 0, nullptr));
128
129 if (!sync_base_file_.IsValid())
130 return false;
131
132 return true;
133 }
134
IsValid() const135 bool File::IsValid() const {
136 if (!init_)
137 return false;
138 return base_file_.IsValid() || sync_base_file_.IsValid();
139 }
140
Read(void * buffer,size_t buffer_len,size_t offset)141 bool File::Read(void* buffer, size_t buffer_len, size_t offset) {
142 DCHECK(init_);
143 if (buffer_len > ULONG_MAX || offset > LONG_MAX)
144 return false;
145
146 int ret = UNSAFE_TODO(
147 sync_base_file_.Read(offset, static_cast<char*>(buffer), buffer_len));
148 return static_cast<int>(buffer_len) == ret;
149 }
150
Write(const void * buffer,size_t buffer_len,size_t offset)151 bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
152 DCHECK(init_);
153 if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
154 return false;
155
156 int ret = UNSAFE_TODO(sync_base_file_.Write(
157 offset, static_cast<const char*>(buffer), buffer_len));
158 return static_cast<int>(buffer_len) == ret;
159 }
160
161 // We have to increase the ref counter of the file before performing the IO to
162 // prevent the completion to happen with an invalid handle (if the file is
163 // closed while the IO is in flight).
Read(void * buffer,size_t buffer_len,size_t offset,FileIOCallback * callback,bool * completed)164 bool File::Read(void* buffer, size_t buffer_len, size_t offset,
165 FileIOCallback* callback, bool* completed) {
166 DCHECK(init_);
167 if (!callback) {
168 if (completed)
169 *completed = true;
170 return Read(buffer, buffer_len, offset);
171 }
172
173 if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
174 return false;
175
176 MyOverlapped* data = new MyOverlapped(this, offset, callback);
177 DWORD size = static_cast<DWORD>(buffer_len);
178
179 DWORD actual;
180 if (!ReadFile(base_file_.GetPlatformFile(), buffer, size, &actual,
181 data->overlapped())) {
182 *completed = false;
183 if (GetLastError() == ERROR_IO_PENDING)
184 return true;
185 delete data;
186 return false;
187 }
188
189 // The operation completed already. We'll be called back anyway.
190 *completed = (actual == size);
191 DCHECK_EQ(size, actual);
192 data->callback_ = nullptr;
193 data->file_ = nullptr; // There is no reason to hold on to this anymore.
194 return *completed;
195 }
196
Write(const void * buffer,size_t buffer_len,size_t offset,FileIOCallback * callback,bool * completed)197 bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
198 FileIOCallback* callback, bool* completed) {
199 DCHECK(init_);
200 if (!callback) {
201 if (completed)
202 *completed = true;
203 return Write(buffer, buffer_len, offset);
204 }
205
206 return AsyncWrite(buffer, buffer_len, offset, callback, completed);
207 }
208
209 File::~File() = default;
210
platform_file() const211 base::PlatformFile File::platform_file() const {
212 DCHECK(init_);
213 return base_file_.IsValid() ? base_file_.GetPlatformFile() :
214 sync_base_file_.GetPlatformFile();
215 }
216
AsyncWrite(const void * buffer,size_t buffer_len,size_t offset,FileIOCallback * callback,bool * completed)217 bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset,
218 FileIOCallback* callback, bool* completed) {
219 DCHECK(init_);
220 DCHECK(callback);
221 DCHECK(completed);
222 if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
223 return false;
224
225 MyOverlapped* data = new MyOverlapped(this, offset, callback);
226 DWORD size = static_cast<DWORD>(buffer_len);
227
228 DWORD actual;
229 if (!WriteFile(base_file_.GetPlatformFile(), buffer, size, &actual,
230 data->overlapped())) {
231 *completed = false;
232 if (GetLastError() == ERROR_IO_PENDING)
233 return true;
234 delete data;
235 return false;
236 }
237
238 // The operation completed already. We'll be called back anyway.
239 *completed = (actual == size);
240 DCHECK_EQ(size, actual);
241 data->callback_ = nullptr;
242 data->file_ = nullptr; // There is no reason to hold on to this anymore.
243 return *completed;
244 }
245
SetLength(size_t length)246 bool File::SetLength(size_t length) {
247 DCHECK(init_);
248 if (length > ULONG_MAX)
249 return false;
250
251 DWORD size = static_cast<DWORD>(length);
252 HANDLE file = platform_file();
253 if (INVALID_SET_FILE_POINTER ==
254 SetFilePointer(file, size, nullptr, FILE_BEGIN))
255 return false;
256
257 return TRUE == SetEndOfFile(file);
258 }
259
GetLength()260 size_t File::GetLength() {
261 DCHECK(init_);
262 LARGE_INTEGER size;
263 HANDLE file = platform_file();
264 if (!GetFileSizeEx(file, &size))
265 return 0;
266 if (size.HighPart)
267 return ULONG_MAX;
268
269 return static_cast<size_t>(size.LowPart);
270 }
271
272 // Static.
WaitForPendingIOForTesting(int * num_pending_io)273 void File::WaitForPendingIOForTesting(int* num_pending_io) {
274 // Spin on the burn-down count until the file IO completes.
275 constexpr base::TimeDelta kMillisecond = base::Milliseconds(1);
276 for (; *num_pending_io; base::PlatformThread::Sleep(kMillisecond)) {
277 // This waits for callbacks running on worker threads.
278 base::ThreadPoolInstance::Get()->FlushForTesting(); // IN-TEST
279 // This waits for the "Reply" tasks running on the current MessageLoop.
280 base::RunLoop().RunUntilIdle();
281 }
282 }
283
284 // Static.
DropPendingIO()285 void File::DropPendingIO() {
286 }
287
288 } // namespace disk_cache
289