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