1// Copyright (c) 2006-2010 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/file.h"
6
7#include "base/file_path.h"
8#include "base/lazy_instance.h"
9#include "base/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  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 MessageLoopForIO::IOHandler {
33  virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
34                             DWORD actual_bytes, DWORD error);
35};
36
37static base::LazyInstance<CompletionHandler> g_completion_handler(
38    base::LINKER_INITIALIZED);
39
40void CompletionHandler::OnIOCompleted(MessageLoopForIO::IOContext* context,
41                                      DWORD actual_bytes, DWORD error) {
42  MyOverlapped* data = reinterpret_cast<MyOverlapped*>(context);
43
44  if (error) {
45    DCHECK(!actual_bytes);
46    actual_bytes = static_cast<DWORD>(net::ERR_CACHE_READ_FAILURE);
47    NOTREACHED();
48  }
49
50  if (data->callback_)
51    data->callback_->OnFileIOComplete(static_cast<int>(actual_bytes));
52
53  delete data;
54}
55
56MyOverlapped::MyOverlapped(disk_cache::File* file, size_t offset,
57                           disk_cache::FileIOCallback* callback) {
58  memset(this, 0, sizeof(*this));
59  context_.handler = g_completion_handler.Pointer();
60  context_.overlapped.Offset = static_cast<DWORD>(offset);
61  file_ = file;
62  callback_ = callback;
63}
64
65}  // namespace
66
67namespace disk_cache {
68
69File::File(base::PlatformFile file)
70    : init_(true), mixed_(true), platform_file_(INVALID_HANDLE_VALUE),
71      sync_platform_file_(file) {
72}
73
74bool File::Init(const FilePath& name) {
75  DCHECK(!init_);
76  if (init_)
77    return false;
78
79  DWORD sharing = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
80  DWORD access = GENERIC_READ | GENERIC_WRITE | DELETE;
81  platform_file_ = CreateFile(name.value().c_str(), access, sharing, NULL,
82                              OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
83
84  if (INVALID_HANDLE_VALUE == platform_file_)
85    return false;
86
87  MessageLoopForIO::current()->RegisterIOHandler(
88      platform_file_, g_completion_handler.Pointer());
89
90  init_ = true;
91  sync_platform_file_  = CreateFile(name.value().c_str(), access, sharing, NULL,
92                                    OPEN_EXISTING, 0, NULL);
93
94  if (INVALID_HANDLE_VALUE == sync_platform_file_)
95    return false;
96
97  return true;
98}
99
100File::~File() {
101  if (!init_)
102    return;
103
104  if (INVALID_HANDLE_VALUE != platform_file_)
105    CloseHandle(platform_file_);
106  if (INVALID_HANDLE_VALUE != sync_platform_file_)
107    CloseHandle(sync_platform_file_);
108}
109
110base::PlatformFile File::platform_file() const {
111  DCHECK(init_);
112  return (INVALID_HANDLE_VALUE == platform_file_) ? sync_platform_file_ :
113                                                    platform_file_;
114}
115
116bool File::IsValid() const {
117  if (!init_)
118    return false;
119  return (INVALID_HANDLE_VALUE != platform_file_ ||
120          INVALID_HANDLE_VALUE != sync_platform_file_);
121}
122
123bool File::Read(void* buffer, size_t buffer_len, size_t offset) {
124  DCHECK(init_);
125  if (buffer_len > ULONG_MAX || offset > LONG_MAX)
126    return false;
127
128  DWORD ret = SetFilePointer(sync_platform_file_, static_cast<LONG>(offset),
129                             NULL, FILE_BEGIN);
130  if (INVALID_SET_FILE_POINTER == ret)
131    return false;
132
133  DWORD actual;
134  DWORD size = static_cast<DWORD>(buffer_len);
135  if (!ReadFile(sync_platform_file_, buffer, size, &actual, NULL))
136    return false;
137  return actual == size;
138}
139
140bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
141  DCHECK(init_);
142  if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
143    return false;
144
145  DWORD ret = SetFilePointer(sync_platform_file_, static_cast<LONG>(offset),
146                             NULL, FILE_BEGIN);
147  if (INVALID_SET_FILE_POINTER == ret)
148    return false;
149
150  DWORD actual;
151  DWORD size = static_cast<DWORD>(buffer_len);
152  if (!WriteFile(sync_platform_file_, buffer, size, &actual, NULL))
153    return false;
154  return actual == size;
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).
160bool 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(platform_file_, buffer, size, &actual, data->overlapped())) {
177    *completed = false;
178    if (GetLastError() == ERROR_IO_PENDING)
179      return true;
180    delete data;
181    return false;
182  }
183
184  // The operation completed already. We'll be called back anyway.
185  *completed = (actual == size);
186  DCHECK(actual == size);
187  data->callback_ = NULL;
188  data->file_ = NULL;  // There is no reason to hold on to this anymore.
189  return *completed;
190}
191
192bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
193                 FileIOCallback* callback, bool* completed) {
194  DCHECK(init_);
195  if (!callback) {
196    if (completed)
197      *completed = true;
198    return Write(buffer, buffer_len, offset);
199  }
200
201  return AsyncWrite(buffer, buffer_len, offset, callback, completed);
202}
203
204bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset,
205                      FileIOCallback* callback, bool* completed) {
206  DCHECK(init_);
207  DCHECK(callback);
208  DCHECK(completed);
209  if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
210    return false;
211
212  MyOverlapped* data = new MyOverlapped(this, offset, callback);
213  DWORD size = static_cast<DWORD>(buffer_len);
214
215  DWORD actual;
216  if (!WriteFile(platform_file_, buffer, size, &actual, data->overlapped())) {
217    *completed = false;
218    if (GetLastError() == ERROR_IO_PENDING)
219      return true;
220    delete data;
221    return false;
222  }
223
224  // The operation completed already. We'll be called back anyway.
225  *completed = (actual == size);
226  DCHECK(actual == size);
227  data->callback_ = NULL;
228  data->file_ = NULL;  // There is no reason to hold on to this anymore.
229  return *completed;
230}
231
232bool File::SetLength(size_t length) {
233  DCHECK(init_);
234  if (length > ULONG_MAX)
235    return false;
236
237  DWORD size = static_cast<DWORD>(length);
238  HANDLE file = platform_file();
239  if (INVALID_SET_FILE_POINTER == SetFilePointer(file, size, NULL, FILE_BEGIN))
240    return false;
241
242  return TRUE == SetEndOfFile(file);
243}
244
245size_t File::GetLength() {
246  DCHECK(init_);
247  LARGE_INTEGER size;
248  HANDLE file = platform_file();
249  if (!GetFileSizeEx(file, &size))
250    return 0;
251  if (size.HighPart)
252    return ULONG_MAX;
253
254  return static_cast<size_t>(size.LowPart);
255}
256
257// Static.
258void File::WaitForPendingIO(int* num_pending_io) {
259  while (*num_pending_io) {
260    // Asynchronous IO operations may be in flight and the completion may end
261    // up calling us back so let's wait for them.
262    MessageLoopForIO::IOHandler* handler = g_completion_handler.Pointer();
263    MessageLoopForIO::current()->WaitForIOCompletion(100, handler);
264  }
265}
266
267}  // namespace disk_cache
268