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