file_win.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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::PlatformFile file)
73    : init_(true), mixed_(true), platform_file_(INVALID_HANDLE_VALUE),
74      sync_platform_file_(file) {
75}
76
77bool File::Init(const base::FilePath& name) {
78  DCHECK(!init_);
79  if (init_)
80    return false;
81
82  DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
83  DWORD access = GENERIC_READ | GENERIC_WRITE | DELETE;
84  platform_file_ = CreateFile(name.value().c_str(), access, sharing, NULL,
85                              OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
86
87  if (INVALID_HANDLE_VALUE == platform_file_)
88    return false;
89
90  base::MessageLoopForIO::current()->RegisterIOHandler(
91      platform_file_, g_completion_handler.Pointer());
92
93  init_ = true;
94  sync_platform_file_  = CreateFile(name.value().c_str(), access, sharing, NULL,
95                                    OPEN_EXISTING, 0, NULL);
96
97  if (INVALID_HANDLE_VALUE == sync_platform_file_)
98    return false;
99
100  return true;
101}
102
103File::~File() {
104  if (!init_)
105    return;
106
107  if (INVALID_HANDLE_VALUE != platform_file_)
108    CloseHandle(platform_file_);
109  if (INVALID_HANDLE_VALUE != sync_platform_file_)
110    CloseHandle(sync_platform_file_);
111}
112
113base::PlatformFile File::platform_file() const {
114  DCHECK(init_);
115  return (INVALID_HANDLE_VALUE == platform_file_) ? sync_platform_file_ :
116                                                    platform_file_;
117}
118
119bool File::IsValid() const {
120  if (!init_)
121    return false;
122  return (INVALID_HANDLE_VALUE != platform_file_ ||
123          INVALID_HANDLE_VALUE != sync_platform_file_);
124}
125
126bool File::Read(void* buffer, size_t buffer_len, size_t offset) {
127  DCHECK(init_);
128  if (buffer_len > ULONG_MAX || offset > LONG_MAX)
129    return false;
130
131  DWORD ret = SetFilePointer(sync_platform_file_, static_cast<LONG>(offset),
132                             NULL, FILE_BEGIN);
133  if (INVALID_SET_FILE_POINTER == ret)
134    return false;
135
136  DWORD actual;
137  DWORD size = static_cast<DWORD>(buffer_len);
138  if (!ReadFile(sync_platform_file_, buffer, size, &actual, NULL))
139    return false;
140  return actual == size;
141}
142
143bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
144  DCHECK(init_);
145  if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
146    return false;
147
148  DWORD ret = SetFilePointer(sync_platform_file_, static_cast<LONG>(offset),
149                             NULL, FILE_BEGIN);
150  if (INVALID_SET_FILE_POINTER == ret)
151    return false;
152
153  DWORD actual;
154  DWORD size = static_cast<DWORD>(buffer_len);
155  if (!WriteFile(sync_platform_file_, buffer, size, &actual, NULL))
156    return false;
157  return actual == size;
158}
159
160// We have to increase the ref counter of the file before performing the IO to
161// prevent the completion to happen with an invalid handle (if the file is
162// closed while the IO is in flight).
163bool File::Read(void* buffer, size_t buffer_len, size_t offset,
164                FileIOCallback* callback, bool* completed) {
165  DCHECK(init_);
166  if (!callback) {
167    if (completed)
168      *completed = true;
169    return Read(buffer, buffer_len, offset);
170  }
171
172  if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
173    return false;
174
175  MyOverlapped* data = new MyOverlapped(this, offset, callback);
176  DWORD size = static_cast<DWORD>(buffer_len);
177
178  DWORD actual;
179  if (!ReadFile(platform_file_, buffer, size, &actual, data->overlapped())) {
180    *completed = false;
181    if (GetLastError() == ERROR_IO_PENDING)
182      return true;
183    delete data;
184    return false;
185  }
186
187  // The operation completed already. We'll be called back anyway.
188  *completed = (actual == size);
189  DCHECK_EQ(size, actual);
190  data->callback_ = NULL;
191  data->file_ = NULL;  // There is no reason to hold on to this anymore.
192  return *completed;
193}
194
195bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
196                 FileIOCallback* callback, bool* completed) {
197  DCHECK(init_);
198  if (!callback) {
199    if (completed)
200      *completed = true;
201    return Write(buffer, buffer_len, offset);
202  }
203
204  return AsyncWrite(buffer, buffer_len, offset, callback, completed);
205}
206
207bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset,
208                      FileIOCallback* callback, bool* completed) {
209  DCHECK(init_);
210  DCHECK(callback);
211  DCHECK(completed);
212  if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
213    return false;
214
215  MyOverlapped* data = new MyOverlapped(this, offset, callback);
216  DWORD size = static_cast<DWORD>(buffer_len);
217
218  DWORD actual;
219  if (!WriteFile(platform_file_, buffer, size, &actual, data->overlapped())) {
220    *completed = false;
221    if (GetLastError() == ERROR_IO_PENDING)
222      return true;
223    delete data;
224    return false;
225  }
226
227  // The operation completed already. We'll be called back anyway.
228  *completed = (actual == size);
229  DCHECK_EQ(size, actual);
230  data->callback_ = NULL;
231  data->file_ = NULL;  // There is no reason to hold on to this anymore.
232  return *completed;
233}
234
235bool File::SetLength(size_t length) {
236  DCHECK(init_);
237  if (length > ULONG_MAX)
238    return false;
239
240  DWORD size = static_cast<DWORD>(length);
241  HANDLE file = platform_file();
242  if (INVALID_SET_FILE_POINTER == SetFilePointer(file, size, NULL, FILE_BEGIN))
243    return false;
244
245  return TRUE == SetEndOfFile(file);
246}
247
248size_t File::GetLength() {
249  DCHECK(init_);
250  LARGE_INTEGER size;
251  HANDLE file = platform_file();
252  if (!GetFileSizeEx(file, &size))
253    return 0;
254  if (size.HighPart)
255    return ULONG_MAX;
256
257  return static_cast<size_t>(size.LowPart);
258}
259
260// Static.
261void File::WaitForPendingIO(int* num_pending_io) {
262  while (*num_pending_io) {
263    // Asynchronous IO operations may be in flight and the completion may end
264    // up calling us back so let's wait for them.
265    base::MessageLoopForIO::IOHandler* handler = g_completion_handler.Pointer();
266    base::MessageLoopForIO::current()->WaitForIOCompletion(100, handler);
267  }
268}
269
270// Static.
271void File::DropPendingIO() {
272}
273
274}  // namespace disk_cache
275