blob: 4c18f921c50c9d47d95dce8546e39f3f997bc833 [file] [log] [blame]
Avi Drissman64595482022-09-14 20:52:291// Copyright 2012 The Chromium Authors
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit586acc5fe2008-07-26 22:42:524
[email protected]c2c5cfc2014-03-03 16:35:285#include "net/disk_cache/blockfile/file.h"
initial.commit586acc5fe2008-07-26 22:42:526
Peter Kasting47b489402025-01-01 02:08:027#include <windows.h>
8
avi51ba3e692015-12-26 17:30:509#include <limits.h>
Tom Sepeze9b4dd552024-08-14 22:44:2910
dcheng70c49422016-03-02 23:20:3411#include <utility>
avi51ba3e692015-12-26 17:30:5012
Tom Sepeze9b4dd552024-08-14 22:44:2913#include "base/compiler_specific.h"
[email protected]57999812013-02-24 05:40:5214#include "base/files/file_path.h"
Keishi Hattori0e45c022021-11-27 09:25:5215#include "base/memory/raw_ptr.h"
Gabriel Charette19d2ae62018-04-10 14:10:5816#include "base/message_loop/message_pump_for_io.h"
Adam Ricef48f6e32023-03-30 02:03:5717#include "base/no_destructor.h"
Sigurdur Asgeirsson8dbbea82021-03-04 16:50:3218#include "base/run_loop.h"
jdoerrie6312bf62019-02-01 22:03:4219#include "base/strings/string_util.h"
Carlos Caballerob25fe8472020-07-17 10:27:1720#include "base/task/current_thread.h"
Sigurdur Asgeirsson8dbbea82021-03-04 16:50:3221#include "base/task/thread_pool.h"
22#include "base/task/thread_pool/thread_pool_instance.h"
23#include "base/threading/platform_thread.h"
[email protected]e25d44d2011-07-13 18:41:5824#include "net/base/net_errors.h"
initial.commit586acc5fe2008-07-26 22:42:5225#include "net/disk_cache/disk_cache.h"
26
27namespace {
28
Konstantin Ganenkoaba55d22017-12-12 21:30:5529class CompletionHandler;
initial.commit586acc5fe2008-07-26 22:42:5230// Structure used for asynchronous operations.
31struct MyOverlapped {
[email protected]17b89142008-11-07 21:52:1532 MyOverlapped(disk_cache::File* file, size_t offset,
33 disk_cache::FileIOCallback* callback);
Peter Kasting47b489402025-01-01 02:08:0234 ~MyOverlapped() = default;
35 OVERLAPPED* overlapped() { return context_.GetOverlapped(); }
[email protected]17b89142008-11-07 21:52:1536
Gabriel Charette19d2ae62018-04-10 14:10:5837 base::MessagePumpForIO::IOContext context_;
[email protected]17b89142008-11-07 21:52:1538 scoped_refptr<disk_cache::File> file_;
Konstantin Ganenkoaba55d22017-12-12 21:30:5539 scoped_refptr<CompletionHandler> completion_handler_;
Keishi Hattori0e45c022021-11-27 09:25:5240 raw_ptr<disk_cache::FileIOCallback> callback_;
initial.commit586acc5fe2008-07-26 22:42:5241};
42
mostynb91e0da982015-01-20 19:17:2743static_assert(offsetof(MyOverlapped, context_) == 0,
44 "should start with overlapped");
initial.commit586acc5fe2008-07-26 22:42:5245
[email protected]17b89142008-11-07 21:52:1546// Helper class to handle the IO completion notifications from the message loop.
Adam Ricef48f6e32023-03-30 02:03:5747class CompletionHandler final : public base::MessagePumpForIO::IOHandler,
48 public base::RefCounted<CompletionHandler> {
Konstantin Ganenkoaba55d22017-12-12 21:30:5549 public:
Gabriel Charette96d5c642020-04-09 20:31:1050 CompletionHandler() : base::MessagePumpForIO::IOHandler(FROM_HERE) {}
Konstantin Ganenkoaba55d22017-12-12 21:30:5551 static CompletionHandler* Get();
52
Peter Boström407869b2021-10-07 04:42:4853 CompletionHandler(const CompletionHandler&) = delete;
54 CompletionHandler& operator=(const CompletionHandler&) = delete;
55
Konstantin Ganenkoaba55d22017-12-12 21:30:5556 private:
57 friend class base::RefCounted<CompletionHandler>;
58 ~CompletionHandler() override {}
59
Gabriel Charette19d2ae62018-04-10 14:10:5860 // implement base::MessagePumpForIO::IOHandler.
61 void OnIOCompleted(base::MessagePumpForIO::IOContext* context,
dchengaf6908f62015-04-17 23:31:0562 DWORD actual_bytes,
63 DWORD error) override;
[email protected]17b89142008-11-07 21:52:1564};
initial.commit586acc5fe2008-07-26 22:42:5265
Konstantin Ganenkoaba55d22017-12-12 21:30:5566CompletionHandler* CompletionHandler::Get() {
Adam Ricef48f6e32023-03-30 02:03:5767 static base::NoDestructor<scoped_refptr<CompletionHandler>> handler(
68 base::MakeRefCounted<CompletionHandler>());
69 return handler->get();
Konstantin Ganenkoaba55d22017-12-12 21:30:5570}
[email protected]625332e02010-12-14 07:48:4971
[email protected]2da659e2013-05-23 20:51:3472void CompletionHandler::OnIOCompleted(
Gabriel Charette19d2ae62018-04-10 14:10:5873 base::MessagePumpForIO::IOContext* context,
[email protected]2da659e2013-05-23 20:51:3474 DWORD actual_bytes,
75 DWORD error) {
[email protected]17b89142008-11-07 21:52:1576 MyOverlapped* data = reinterpret_cast<MyOverlapped*>(context);
initial.commit586acc5fe2008-07-26 22:42:5277
78 if (error) {
79 DCHECK(!actual_bytes);
[email protected]e25d44d2011-07-13 18:41:5880 actual_bytes = static_cast<DWORD>(net::ERR_CACHE_READ_FAILURE);
initial.commit586acc5fe2008-07-26 22:42:5281 }
82
Paul Semela1647942022-09-15 16:11:0283 // `callback_` may self delete while in `OnFileIOComplete`.
[email protected]17b89142008-11-07 21:52:1584 if (data->callback_)
Paul Semela1647942022-09-15 16:11:0285 data->callback_.ExtractAsDangling()->OnFileIOComplete(
86 static_cast<int>(actual_bytes));
initial.commit586acc5fe2008-07-26 22:42:5287
[email protected]17b89142008-11-07 21:52:1588 delete data;
89}
90
91MyOverlapped::MyOverlapped(disk_cache::File* file, size_t offset,
92 disk_cache::FileIOCallback* callback) {
Peter Kasting47b489402025-01-01 02:08:0293 context_.GetOverlapped()->Offset = static_cast<DWORD>(offset);
[email protected]17b89142008-11-07 21:52:1594 file_ = file;
95 callback_ = callback;
Konstantin Ganenkoaba55d22017-12-12 21:30:5596 completion_handler_ = CompletionHandler::Get();
[email protected]17b89142008-11-07 21:52:1597}
98
[email protected]17b89142008-11-07 21:52:1599} // namespace
100
101namespace disk_cache {
102
[email protected]04ba1822014-04-03 22:44:09103File::File(base::File file)
dcheng70c49422016-03-02 23:20:34104 : init_(true), mixed_(true), sync_base_file_(std::move(file)) {}
[email protected]408d35f52008-08-13 18:30:22105
[email protected]6cdfd7f2013-02-08 20:40:15106bool File::Init(const base::FilePath& name) {
initial.commit586acc5fe2008-07-26 22:42:52107 DCHECK(!init_);
108 if (init_)
109 return false;
110
[email protected]10f2e692010-09-29 21:00:35111 DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
112 DWORD access = GENERIC_READ | GENERIC_WRITE | DELETE;
Jan Wilken Dörrie9720dce2020-07-21 17:14:23113 base_file_ =
114 base::File(CreateFile(name.value().c_str(), access, sharing, nullptr,
115 OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr));
initial.commit586acc5fe2008-07-26 22:42:52116
[email protected]04ba1822014-04-03 22:44:09117 if (!base_file_.IsValid())
initial.commit586acc5fe2008-07-26 22:42:52118 return false;
119
François Doray5b785902024-11-05 14:32:04120 if (!base::CurrentIOThread::Get()->RegisterIOHandler(
121 base_file_.GetPlatformFile(), CompletionHandler::Get())) {
122 return false;
123 }
initial.commit586acc5fe2008-07-26 22:42:52124
[email protected]17b89142008-11-07 21:52:15125 init_ = true;
Jan Wilken Dörrie9720dce2020-07-21 17:14:23126 sync_base_file_ = base::File(CreateFile(name.value().c_str(), access, sharing,
127 nullptr, OPEN_EXISTING, 0, nullptr));
[email protected]17b89142008-11-07 21:52:15128
[email protected]04ba1822014-04-03 22:44:09129 if (!sync_base_file_.IsValid())
[email protected]17b89142008-11-07 21:52:15130 return false;
initial.commit586acc5fe2008-07-26 22:42:52131
132 return true;
133}
134
[email protected]408d35f52008-08-13 18:30:22135bool File::IsValid() const {
136 if (!init_)
137 return false;
[email protected]04ba1822014-04-03 22:44:09138 return base_file_.IsValid() || sync_base_file_.IsValid();
initial.commit586acc5fe2008-07-26 22:42:52139}
140
weidongliuabbe2322025-05-28 02:24:27141bool File::Read(base::span<uint8_t> buffer, size_t offset) {
[email protected]408d35f52008-08-13 18:30:22142 DCHECK(init_);
weidongliuabbe2322025-05-28 02:24:27143 if (buffer.size() > ULONG_MAX || offset > LONG_MAX) {
initial.commit586acc5fe2008-07-26 22:42:52144 return false;
weidongliuabbe2322025-05-28 02:24:27145 }
initial.commit586acc5fe2008-07-26 22:42:52146
weidongliuabbe2322025-05-28 02:24:27147 std::optional<size_t> ret = sync_base_file_.Read(offset, buffer);
148 return ret == buffer.size();
initial.commit586acc5fe2008-07-26 22:42:52149}
150
weidongliuabbe2322025-05-28 02:24:27151bool File::Write(base::span<const uint8_t> buffer, size_t offset) {
[email protected]408d35f52008-08-13 18:30:22152 DCHECK(init_);
weidongliuabbe2322025-05-28 02:24:27153 if (buffer.size() > ULONG_MAX || offset > ULONG_MAX) {
initial.commit586acc5fe2008-07-26 22:42:52154 return false;
weidongliuabbe2322025-05-28 02:24:27155 }
initial.commit586acc5fe2008-07-26 22:42:52156
weidongliuabbe2322025-05-28 02:24:27157 std::optional<size_t> ret = sync_base_file_.Write(offset, buffer);
158 return ret == buffer.size();
initial.commit586acc5fe2008-07-26 22:42:52159}
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).
weidongliuabbe2322025-05-28 02:24:27164bool File::Read(base::span<uint8_t> buffer,
165 size_t offset,
166 FileIOCallback* callback,
167 bool* completed) {
[email protected]408d35f52008-08-13 18:30:22168 DCHECK(init_);
[email protected]f3c56e72009-08-21 18:35:24169 if (!callback) {
170 if (completed)
171 *completed = true;
weidongliuabbe2322025-05-28 02:24:27172 return Read(buffer, offset);
[email protected]f3c56e72009-08-21 18:35:24173 }
[email protected]17b89142008-11-07 21:52:15174
weidongliuabbe2322025-05-28 02:24:27175 if (buffer.size() > ULONG_MAX || offset > ULONG_MAX) {
initial.commit586acc5fe2008-07-26 22:42:52176 return false;
weidongliuabbe2322025-05-28 02:24:27177 }
initial.commit586acc5fe2008-07-26 22:42:52178
[email protected]17b89142008-11-07 21:52:15179 MyOverlapped* data = new MyOverlapped(this, offset, callback);
weidongliuabbe2322025-05-28 02:24:27180 DWORD size = static_cast<DWORD>(buffer.size());
initial.commit586acc5fe2008-07-26 22:42:52181
[email protected]17b89142008-11-07 21:52:15182 DWORD actual;
weidongliuabbe2322025-05-28 02:24:27183 if (!ReadFile(base_file_.GetPlatformFile(), buffer.data(), size, &actual,
[email protected]04ba1822014-04-03 22:44:09184 data->overlapped())) {
[email protected]17b89142008-11-07 21:52:15185 *completed = false;
186 if (GetLastError() == ERROR_IO_PENDING)
187 return true;
initial.commit586acc5fe2008-07-26 22:42:52188 delete data;
189 return false;
190 }
191
[email protected]17b89142008-11-07 21:52:15192 // The operation completed already. We'll be called back anyway.
193 *completed = (actual == size);
[email protected]c34e9a7a2011-05-11 12:06:57194 DCHECK_EQ(size, actual);
Raul Tambre94493c652019-03-11 17:18:35195 data->callback_ = nullptr;
196 data->file_ = nullptr; // There is no reason to hold on to this anymore.
[email protected]17b89142008-11-07 21:52:15197 return *completed;
initial.commit586acc5fe2008-07-26 22:42:52198}
199
weidongliuabbe2322025-05-28 02:24:27200bool File::Write(base::span<const uint8_t> buffer,
201 size_t offset,
202 FileIOCallback* callback,
203 bool* completed) {
[email protected]408d35f52008-08-13 18:30:22204 DCHECK(init_);
[email protected]f3c56e72009-08-21 18:35:24205 if (!callback) {
206 if (completed)
207 *completed = true;
weidongliuabbe2322025-05-28 02:24:27208 return Write(buffer, offset);
[email protected]f3c56e72009-08-21 18:35:24209 }
[email protected]17b89142008-11-07 21:52:15210
weidongliuabbe2322025-05-28 02:24:27211 return AsyncWrite(buffer, offset, callback, completed);
initial.commit586acc5fe2008-07-26 22:42:52212}
213
Tsuyoshi Horo07c3f0e2022-06-16 07:30:47214File::~File() = default;
[email protected]04ba1822014-04-03 22:44:09215
216base::PlatformFile File::platform_file() const {
217 DCHECK(init_);
218 return base_file_.IsValid() ? base_file_.GetPlatformFile() :
219 sync_base_file_.GetPlatformFile();
220}
221
weidongliuabbe2322025-05-28 02:24:27222bool File::AsyncWrite(base::span<const uint8_t> buffer,
223 size_t offset,
224 FileIOCallback* callback,
225 bool* completed) {
[email protected]408d35f52008-08-13 18:30:22226 DCHECK(init_);
[email protected]e1fcf142010-08-23 18:47:25227 DCHECK(callback);
228 DCHECK(completed);
weidongliuabbe2322025-05-28 02:24:27229 if (buffer.size() > ULONG_MAX || offset > ULONG_MAX) {
initial.commit586acc5fe2008-07-26 22:42:52230 return false;
weidongliuabbe2322025-05-28 02:24:27231 }
initial.commit586acc5fe2008-07-26 22:42:52232
[email protected]17b89142008-11-07 21:52:15233 MyOverlapped* data = new MyOverlapped(this, offset, callback);
weidongliuabbe2322025-05-28 02:24:27234 DWORD size = static_cast<DWORD>(buffer.size());
initial.commit586acc5fe2008-07-26 22:42:52235
[email protected]17b89142008-11-07 21:52:15236 DWORD actual;
weidongliuabbe2322025-05-28 02:24:27237 if (!WriteFile(base_file_.GetPlatformFile(), buffer.data(), size, &actual,
[email protected]04ba1822014-04-03 22:44:09238 data->overlapped())) {
[email protected]17b89142008-11-07 21:52:15239 *completed = false;
240 if (GetLastError() == ERROR_IO_PENDING)
241 return true;
initial.commit586acc5fe2008-07-26 22:42:52242 delete data;
243 return false;
244 }
245
[email protected]17b89142008-11-07 21:52:15246 // The operation completed already. We'll be called back anyway.
247 *completed = (actual == size);
[email protected]c34e9a7a2011-05-11 12:06:57248 DCHECK_EQ(size, actual);
Raul Tambre94493c652019-03-11 17:18:35249 data->callback_ = nullptr;
250 data->file_ = nullptr; // There is no reason to hold on to this anymore.
[email protected]17b89142008-11-07 21:52:15251 return *completed;
initial.commit586acc5fe2008-07-26 22:42:52252}
253
254bool File::SetLength(size_t length) {
[email protected]408d35f52008-08-13 18:30:22255 DCHECK(init_);
initial.commit586acc5fe2008-07-26 22:42:52256 if (length > ULONG_MAX)
257 return false;
258
259 DWORD size = static_cast<DWORD>(length);
[email protected]21da6eb2008-11-03 17:18:14260 HANDLE file = platform_file();
Raul Tambre94493c652019-03-11 17:18:35261 if (INVALID_SET_FILE_POINTER ==
262 SetFilePointer(file, size, nullptr, FILE_BEGIN))
initial.commit586acc5fe2008-07-26 22:42:52263 return false;
264
[email protected]408d35f52008-08-13 18:30:22265 return TRUE == SetEndOfFile(file);
initial.commit586acc5fe2008-07-26 22:42:52266}
267
268size_t File::GetLength() {
[email protected]408d35f52008-08-13 18:30:22269 DCHECK(init_);
initial.commit586acc5fe2008-07-26 22:42:52270 LARGE_INTEGER size;
[email protected]21da6eb2008-11-03 17:18:14271 HANDLE file = platform_file();
[email protected]408d35f52008-08-13 18:30:22272 if (!GetFileSizeEx(file, &size))
initial.commit586acc5fe2008-07-26 22:42:52273 return 0;
274 if (size.HighPart)
275 return ULONG_MAX;
276
277 return static_cast<size_t>(size.LowPart);
278}
279
[email protected]f3c56e72009-08-21 18:35:24280// Static.
Sigurdur Asgeirsson8dbbea82021-03-04 16:50:32281void File::WaitForPendingIOForTesting(int* num_pending_io) {
282 // Spin on the burn-down count until the file IO completes.
Peter Kastinge5a38ed2021-10-02 03:06:35283 constexpr base::TimeDelta kMillisecond = base::Milliseconds(1);
Sigurdur Asgeirsson8dbbea82021-03-04 16:50:32284 for (; *num_pending_io; base::PlatformThread::Sleep(kMillisecond)) {
285 // This waits for callbacks running on worker threads.
286 base::ThreadPoolInstance::Get()->FlushForTesting(); // IN-TEST
287 // This waits for the "Reply" tasks running on the current MessageLoop.
288 base::RunLoop().RunUntilIdle();
Takumi Fujimoto4fbd4f12021-03-03 17:38:13289 }
[email protected]f3c56e72009-08-21 18:35:24290}
291
[email protected]48a109f2014-01-08 06:16:22292// Static.
293void File::DropPendingIO() {
294}
295
initial.commit586acc5fe2008-07-26 22:42:52296} // namespace disk_cache