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
16 namespace net {
17
18 // Ensure that we can just use our Whence values directly.
19 COMPILE_ASSERT(FROM_BEGIN == FILE_BEGIN, bad_whence_begin);
20 COMPILE_ASSERT(FROM_CURRENT == FILE_CURRENT, bad_whence_current);
21 COMPILE_ASSERT(FROM_END == FILE_END, bad_whence_end);
22
23 namespace {
24
SetOffset(OVERLAPPED * overlapped,const LARGE_INTEGER & offset)25 void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) {
26 overlapped->Offset = offset.LowPart;
27 overlapped->OffsetHigh = offset.HighPart;
28 }
29
IncrementOffset(OVERLAPPED * overlapped,DWORD count)30 void 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
Context(const BoundNetLog & bound_net_log,const scoped_refptr<base::TaskRunner> & task_runner)40 FileStream::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
Context(base::PlatformFile file,const BoundNetLog & bound_net_log,int open_flags,const scoped_refptr<base::TaskRunner> & task_runner)54 FileStream::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
~Context()74 FileStream::Context::~Context() {
75 }
76
GetFileSize() const77 int64 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
ReadAsync(IOBuffer * buf,int buf_len,const CompletionCallback & callback)89 int 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
ReadSync(char * buf,int buf_len)114 int 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
WriteAsync(IOBuffer * buf,int buf_len,const CompletionCallback & callback)130 int 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
WriteSync(const char * buf,int buf_len)152 int 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
Truncate(int64 bytes)164 int 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
OnAsyncFileOpened()175 void FileStream::Context::OnAsyncFileOpened() {
176 base::MessageLoopForIO::current()->RegisterIOHandler(file_, this);
177 }
178
SeekFileImpl(Whence whence,int64 offset)179 FileStream::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
FlushFileImpl()192 FileStream::Context::IOResult FileStream::Context::FlushFileImpl() {
193 if (FlushFileBuffers(file_))
194 return IOResult(OK, 0);
195
196 return IOResult::FromOSError(GetLastError());
197 }
198
CloseFileImpl()199 FileStream::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
IOCompletionIsPending(const CompletionCallback & callback,IOBuffer * buf)208 void 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
OnIOCompleted(base::MessageLoopForIO::IOContext * context,DWORD bytes_read,DWORD error)217 void 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