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/base/file_stream_context.h"
6
7#include <windows.h>
8
9#include "base/files/file_path.h"
10#include "base/logging.h"
11#include "base/metrics/histogram.h"
12#include "base/task_runner_util.h"
13#include "net/base/io_buffer.h"
14#include "net/base/net_errors.h"
15
16namespace net {
17
18// Ensure that we can just use our Whence values directly.
19COMPILE_ASSERT(FROM_BEGIN == FILE_BEGIN, bad_whence_begin);
20COMPILE_ASSERT(FROM_CURRENT == FILE_CURRENT, bad_whence_current);
21COMPILE_ASSERT(FROM_END == FILE_END, bad_whence_end);
22
23namespace {
24
25void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) {
26  overlapped->Offset = offset.LowPart;
27  overlapped->OffsetHigh = offset.HighPart;
28}
29
30void IncrementOffset(OVERLAPPED* overlapped, DWORD count) {
31  LARGE_INTEGER offset;
32  offset.LowPart = overlapped->Offset;
33  offset.HighPart = overlapped->OffsetHigh;
34  offset.QuadPart += static_cast<LONGLONG>(count);
35  SetOffset(overlapped, offset);
36}
37
38}  // namespace
39
40FileStream::Context::Context(const BoundNetLog& bound_net_log,
41                             const scoped_refptr<base::TaskRunner>& task_runner)
42    : io_context_(),
43      file_(base::kInvalidPlatformFileValue),
44      record_uma_(false),
45      async_in_progress_(false),
46      orphaned_(false),
47      bound_net_log_(bound_net_log),
48      error_source_(FILE_ERROR_SOURCE_COUNT),
49      task_runner_(task_runner) {
50  io_context_.handler = this;
51  memset(&io_context_.overlapped, 0, sizeof(io_context_.overlapped));
52}
53
54FileStream::Context::Context(base::PlatformFile file,
55                             const BoundNetLog& bound_net_log,
56                             int open_flags,
57                             const scoped_refptr<base::TaskRunner>& task_runner)
58    : io_context_(),
59      file_(file),
60      record_uma_(false),
61      async_in_progress_(false),
62      orphaned_(false),
63      bound_net_log_(bound_net_log),
64      error_source_(FILE_ERROR_SOURCE_COUNT),
65      task_runner_(task_runner) {
66  io_context_.handler = this;
67  memset(&io_context_.overlapped, 0, sizeof(io_context_.overlapped));
68  if (file_ != base::kInvalidPlatformFileValue &&
69      (open_flags & base::PLATFORM_FILE_ASYNC)) {
70    OnAsyncFileOpened();
71  }
72}
73
74FileStream::Context::~Context() {
75}
76
77int64 FileStream::Context::GetFileSize() const {
78  LARGE_INTEGER file_size;
79  if (!GetFileSizeEx(file_, &file_size)) {
80    IOResult error = IOResult::FromOSError(GetLastError());
81    LOG(WARNING) << "GetFileSizeEx failed: " << error.os_error;
82    RecordError(error, FILE_ERROR_SOURCE_GET_SIZE);
83    return error.result;
84  }
85
86  return file_size.QuadPart;
87}
88
89int FileStream::Context::ReadAsync(IOBuffer* buf,
90                                   int buf_len,
91                                   const CompletionCallback& callback) {
92  DCHECK(!async_in_progress_);
93  error_source_ = FILE_ERROR_SOURCE_READ;
94
95  DWORD bytes_read;
96  if (!ReadFile(file_, buf->data(), buf_len,
97                &bytes_read, &io_context_.overlapped)) {
98    IOResult error = IOResult::FromOSError(GetLastError());
99    if (error.os_error == ERROR_IO_PENDING) {
100      IOCompletionIsPending(callback, buf);
101    } else if (error.os_error == ERROR_HANDLE_EOF) {
102      return 0;  // Report EOF by returning 0 bytes read.
103    } else {
104      LOG(WARNING) << "ReadFile failed: " << error.os_error;
105      RecordError(error, FILE_ERROR_SOURCE_READ);
106    }
107    return error.result;
108  }
109
110  IOCompletionIsPending(callback, buf);
111  return ERR_IO_PENDING;
112}
113
114int FileStream::Context::ReadSync(char* buf, int buf_len) {
115  DWORD bytes_read;
116  if (!ReadFile(file_, buf, buf_len, &bytes_read, NULL)) {
117    IOResult error = IOResult::FromOSError(GetLastError());
118    if (error.os_error == ERROR_HANDLE_EOF) {
119      return 0;  // Report EOF by returning 0 bytes read.
120    } else {
121      LOG(WARNING) << "ReadFile failed: " << error.os_error;
122      RecordError(error, FILE_ERROR_SOURCE_READ);
123    }
124    return error.result;
125  }
126
127  return bytes_read;
128}
129
130int FileStream::Context::WriteAsync(IOBuffer* buf,
131                                    int buf_len,
132                                    const CompletionCallback& callback) {
133  error_source_ = FILE_ERROR_SOURCE_WRITE;
134
135  DWORD bytes_written = 0;
136  if (!WriteFile(file_, buf->data(), buf_len,
137                 &bytes_written, &io_context_.overlapped)) {
138    IOResult error = IOResult::FromOSError(GetLastError());
139    if (error.os_error == ERROR_IO_PENDING) {
140      IOCompletionIsPending(callback, buf);
141    } else {
142      LOG(WARNING) << "WriteFile failed: " << error.os_error;
143      RecordError(error, FILE_ERROR_SOURCE_WRITE);
144    }
145    return error.result;
146  }
147
148  IOCompletionIsPending(callback, buf);
149  return ERR_IO_PENDING;
150}
151
152int FileStream::Context::WriteSync(const char* buf, int buf_len) {
153  DWORD bytes_written = 0;
154  if (!WriteFile(file_, buf, buf_len, &bytes_written, NULL)) {
155    IOResult error = IOResult::FromOSError(GetLastError());
156    LOG(WARNING) << "WriteFile failed: " << error.os_error;
157    RecordError(error, FILE_ERROR_SOURCE_WRITE);
158    return error.result;
159  }
160
161  return bytes_written;
162}
163
164int FileStream::Context::Truncate(int64 bytes) {
165  if (!SetEndOfFile(file_)) {
166    IOResult error = IOResult::FromOSError(GetLastError());
167    LOG(WARNING) << "SetEndOfFile failed: " << error.os_error;
168    RecordError(error, FILE_ERROR_SOURCE_SET_EOF);
169    return error.result;
170  }
171
172  return bytes;
173}
174
175void FileStream::Context::OnAsyncFileOpened() {
176  base::MessageLoopForIO::current()->RegisterIOHandler(file_, this);
177}
178
179FileStream::Context::IOResult FileStream::Context::SeekFileImpl(Whence whence,
180                                                                int64 offset) {
181  LARGE_INTEGER distance, res;
182  distance.QuadPart = offset;
183  DWORD move_method = static_cast<DWORD>(whence);
184  if (SetFilePointerEx(file_, distance, &res, move_method)) {
185    SetOffset(&io_context_.overlapped, res);
186    return IOResult(res.QuadPart, 0);
187  }
188
189  return IOResult::FromOSError(GetLastError());
190}
191
192FileStream::Context::IOResult FileStream::Context::FlushFileImpl() {
193  if (FlushFileBuffers(file_))
194    return IOResult(OK, 0);
195
196  return IOResult::FromOSError(GetLastError());
197}
198
199FileStream::Context::IOResult FileStream::Context::CloseFileImpl() {
200  bool success = base::ClosePlatformFile(file_);
201  file_ = base::kInvalidPlatformFileValue;
202  if (success)
203    return IOResult(OK, 0);
204
205  return IOResult::FromOSError(GetLastError());
206}
207
208void FileStream::Context::IOCompletionIsPending(
209    const CompletionCallback& callback,
210    IOBuffer* buf) {
211  DCHECK(callback_.is_null());
212  callback_ = callback;
213  in_flight_buf_ = buf;  // Hold until the async operation ends.
214  async_in_progress_ = true;
215}
216
217void FileStream::Context::OnIOCompleted(
218    base::MessageLoopForIO::IOContext* context,
219    DWORD bytes_read,
220    DWORD error) {
221  DCHECK_EQ(&io_context_, context);
222  DCHECK(!callback_.is_null());
223  DCHECK(async_in_progress_);
224
225  async_in_progress_ = false;
226  if (orphaned_) {
227    callback_.Reset();
228    in_flight_buf_ = NULL;
229    CloseAndDelete();
230    return;
231  }
232
233  int result;
234  if (error == ERROR_HANDLE_EOF) {
235    result = 0;
236  } else if (error) {
237    IOResult error_result = IOResult::FromOSError(error);
238    RecordError(error_result, error_source_);
239    result = error_result.result;
240  } else {
241    result = bytes_read;
242    IncrementOffset(&io_context_.overlapped, bytes_read);
243  }
244
245  CompletionCallback temp_callback = callback_;
246  callback_.Reset();
247  scoped_refptr<IOBuffer> temp_buf = in_flight_buf_;
248  in_flight_buf_ = NULL;
249  temp_callback.Run(result);
250}
251
252}  // namespace net
253