• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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__anon5a67c8490111::MyOverlapped32   ~MyOverlapped() {}
overlapped__anon5a67c8490111::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