1 // Copyright (c) 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/base/file_stream.h"
6
7 #include <windows.h>
8
9 #include "base/file_path.h"
10 #include "base/logging.h"
11 #include "base/message_loop.h"
12 #include "base/metrics/histogram.h"
13 #include "base/threading/thread_restrictions.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
SetOffset(OVERLAPPED * overlapped,const LARGE_INTEGER & offset)23 static void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) {
24 overlapped->Offset = offset.LowPart;
25 overlapped->OffsetHigh = offset.HighPart;
26 }
27
IncrementOffset(OVERLAPPED * overlapped,DWORD count)28 static void IncrementOffset(OVERLAPPED* overlapped, DWORD count) {
29 LARGE_INTEGER offset;
30 offset.LowPart = overlapped->Offset;
31 offset.HighPart = overlapped->OffsetHigh;
32 offset.QuadPart += static_cast<LONGLONG>(count);
33 SetOffset(overlapped, offset);
34 }
35
MapErrorCode(DWORD err)36 static int MapErrorCode(DWORD err) {
37 switch (err) {
38 case ERROR_FILE_NOT_FOUND:
39 case ERROR_PATH_NOT_FOUND:
40 return ERR_FILE_NOT_FOUND;
41 case ERROR_ACCESS_DENIED:
42 return ERR_ACCESS_DENIED;
43 case ERROR_SUCCESS:
44 return OK;
45 default:
46 LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
47 return ERR_FAILED;
48 }
49 }
50
51 // FileStream::AsyncContext ----------------------------------------------
52
53 class FileStream::AsyncContext : public MessageLoopForIO::IOHandler {
54 public:
AsyncContext(FileStream * owner)55 AsyncContext(FileStream* owner)
56 : owner_(owner), context_(), callback_(NULL), is_closing_(false) {
57 context_.handler = this;
58 }
59 ~AsyncContext();
60
61 void IOCompletionIsPending(CompletionCallback* callback);
62
overlapped()63 OVERLAPPED* overlapped() { return &context_.overlapped; }
callback() const64 CompletionCallback* callback() const { return callback_; }
65
66 private:
67 virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
68 DWORD bytes_read, DWORD error);
69
70 FileStream* owner_;
71 MessageLoopForIO::IOContext context_;
72 CompletionCallback* callback_;
73 bool is_closing_;
74 };
75
~AsyncContext()76 FileStream::AsyncContext::~AsyncContext() {
77 is_closing_ = true;
78 bool waited = false;
79 base::TimeTicks start = base::TimeTicks::Now();
80 while (callback_) {
81 waited = true;
82 MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
83 }
84 if (waited) {
85 // We want to see if we block the message loop for too long.
86 UMA_HISTOGRAM_TIMES("AsyncIO.FileStreamClose",
87 base::TimeTicks::Now() - start);
88 }
89 }
90
IOCompletionIsPending(CompletionCallback * callback)91 void FileStream::AsyncContext::IOCompletionIsPending(
92 CompletionCallback* callback) {
93 DCHECK(!callback_);
94 callback_ = callback;
95 }
96
OnIOCompleted(MessageLoopForIO::IOContext * context,DWORD bytes_read,DWORD error)97 void FileStream::AsyncContext::OnIOCompleted(
98 MessageLoopForIO::IOContext* context, DWORD bytes_read, DWORD error) {
99 DCHECK(&context_ == context);
100 DCHECK(callback_);
101
102 if (is_closing_) {
103 callback_ = NULL;
104 return;
105 }
106
107 int result = static_cast<int>(bytes_read);
108 if (error && error != ERROR_HANDLE_EOF)
109 result = MapErrorCode(error);
110
111 if (bytes_read)
112 IncrementOffset(&context->overlapped, bytes_read);
113
114 CompletionCallback* temp = NULL;
115 std::swap(temp, callback_);
116 temp->Run(result);
117 }
118
119 // FileStream ------------------------------------------------------------
120
FileStream()121 FileStream::FileStream()
122 : file_(INVALID_HANDLE_VALUE),
123 open_flags_(0),
124 auto_closed_(true) {
125 }
126
FileStream(base::PlatformFile file,int flags)127 FileStream::FileStream(base::PlatformFile file, int flags)
128 : file_(file),
129 open_flags_(flags),
130 auto_closed_(false) {
131 // If the file handle is opened with base::PLATFORM_FILE_ASYNC, we need to
132 // make sure we will perform asynchronous File IO to it.
133 if (flags & base::PLATFORM_FILE_ASYNC) {
134 async_context_.reset(new AsyncContext(this));
135 MessageLoopForIO::current()->RegisterIOHandler(file_,
136 async_context_.get());
137 }
138 }
139
~FileStream()140 FileStream::~FileStream() {
141 if (auto_closed_)
142 Close();
143 }
144
Close()145 void FileStream::Close() {
146 if (file_ != INVALID_HANDLE_VALUE)
147 CancelIo(file_);
148
149 async_context_.reset();
150 if (file_ != INVALID_HANDLE_VALUE) {
151 CloseHandle(file_);
152 file_ = INVALID_HANDLE_VALUE;
153 }
154 }
155
Open(const FilePath & path,int open_flags)156 int FileStream::Open(const FilePath& path, int open_flags) {
157 if (IsOpen()) {
158 DLOG(FATAL) << "File is already open!";
159 return ERR_UNEXPECTED;
160 }
161
162 open_flags_ = open_flags;
163 file_ = base::CreatePlatformFile(path, open_flags_, NULL, NULL);
164 if (file_ == INVALID_HANDLE_VALUE) {
165 DWORD error = GetLastError();
166 LOG(WARNING) << "Failed to open file: " << error;
167 return MapErrorCode(error);
168 }
169
170 if (open_flags_ & base::PLATFORM_FILE_ASYNC) {
171 async_context_.reset(new AsyncContext(this));
172 MessageLoopForIO::current()->RegisterIOHandler(file_,
173 async_context_.get());
174 }
175
176 return OK;
177 }
178
IsOpen() const179 bool FileStream::IsOpen() const {
180 return file_ != INVALID_HANDLE_VALUE;
181 }
182
Seek(Whence whence,int64 offset)183 int64 FileStream::Seek(Whence whence, int64 offset) {
184 if (!IsOpen())
185 return ERR_UNEXPECTED;
186 DCHECK(!async_context_.get() || !async_context_->callback());
187
188 LARGE_INTEGER distance, result;
189 distance.QuadPart = offset;
190 DWORD move_method = static_cast<DWORD>(whence);
191 if (!SetFilePointerEx(file_, distance, &result, move_method)) {
192 DWORD error = GetLastError();
193 LOG(WARNING) << "SetFilePointerEx failed: " << error;
194 return MapErrorCode(error);
195 }
196 if (async_context_.get())
197 SetOffset(async_context_->overlapped(), result);
198 return result.QuadPart;
199 }
200
Available()201 int64 FileStream::Available() {
202 base::ThreadRestrictions::AssertIOAllowed();
203
204 if (!IsOpen())
205 return ERR_UNEXPECTED;
206
207 int64 cur_pos = Seek(FROM_CURRENT, 0);
208 if (cur_pos < 0)
209 return cur_pos;
210
211 LARGE_INTEGER file_size;
212 if (!GetFileSizeEx(file_, &file_size)) {
213 DWORD error = GetLastError();
214 LOG(WARNING) << "GetFileSizeEx failed: " << error;
215 return MapErrorCode(error);
216 }
217
218 return file_size.QuadPart - cur_pos;
219 }
220
Read(char * buf,int buf_len,CompletionCallback * callback)221 int FileStream::Read(
222 char* buf, int buf_len, CompletionCallback* callback) {
223 if (!IsOpen())
224 return ERR_UNEXPECTED;
225 DCHECK(open_flags_ & base::PLATFORM_FILE_READ);
226
227 OVERLAPPED* overlapped = NULL;
228 if (async_context_.get()) {
229 DCHECK(callback);
230 DCHECK(!async_context_->callback());
231 overlapped = async_context_->overlapped();
232 } else {
233 DCHECK(!callback);
234 base::ThreadRestrictions::AssertIOAllowed();
235 }
236
237 int rv;
238
239 DWORD bytes_read;
240 if (!ReadFile(file_, buf, buf_len, &bytes_read, overlapped)) {
241 DWORD error = GetLastError();
242 if (async_context_.get() && error == ERROR_IO_PENDING) {
243 async_context_->IOCompletionIsPending(callback);
244 rv = ERR_IO_PENDING;
245 } else if (error == ERROR_HANDLE_EOF) {
246 rv = 0; // Report EOF by returning 0 bytes read.
247 } else {
248 LOG(WARNING) << "ReadFile failed: " << error;
249 rv = MapErrorCode(error);
250 }
251 } else if (overlapped) {
252 async_context_->IOCompletionIsPending(callback);
253 rv = ERR_IO_PENDING;
254 } else {
255 rv = static_cast<int>(bytes_read);
256 }
257 return rv;
258 }
259
ReadUntilComplete(char * buf,int buf_len)260 int FileStream::ReadUntilComplete(char *buf, int buf_len) {
261 int to_read = buf_len;
262 int bytes_total = 0;
263
264 do {
265 int bytes_read = Read(buf, to_read, NULL);
266 if (bytes_read <= 0) {
267 if (bytes_total == 0)
268 return bytes_read;
269
270 return bytes_total;
271 }
272
273 bytes_total += bytes_read;
274 buf += bytes_read;
275 to_read -= bytes_read;
276 } while (bytes_total < buf_len);
277
278 return bytes_total;
279 }
280
Write(const char * buf,int buf_len,CompletionCallback * callback)281 int FileStream::Write(
282 const char* buf, int buf_len, CompletionCallback* callback) {
283 if (!IsOpen())
284 return ERR_UNEXPECTED;
285 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
286
287 OVERLAPPED* overlapped = NULL;
288 if (async_context_.get()) {
289 DCHECK(callback);
290 DCHECK(!async_context_->callback());
291 overlapped = async_context_->overlapped();
292 } else {
293 DCHECK(!callback);
294 base::ThreadRestrictions::AssertIOAllowed();
295 }
296
297 int rv;
298 DWORD bytes_written;
299 if (!WriteFile(file_, buf, buf_len, &bytes_written, overlapped)) {
300 DWORD error = GetLastError();
301 if (async_context_.get() && error == ERROR_IO_PENDING) {
302 async_context_->IOCompletionIsPending(callback);
303 rv = ERR_IO_PENDING;
304 } else {
305 LOG(WARNING) << "WriteFile failed: " << error;
306 rv = MapErrorCode(error);
307 }
308 } else if (overlapped) {
309 async_context_->IOCompletionIsPending(callback);
310 rv = ERR_IO_PENDING;
311 } else {
312 rv = static_cast<int>(bytes_written);
313 }
314 return rv;
315 }
316
Flush()317 int FileStream::Flush() {
318 base::ThreadRestrictions::AssertIOAllowed();
319
320 if (!IsOpen())
321 return ERR_UNEXPECTED;
322
323 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
324 if (FlushFileBuffers(file_)) {
325 return OK;
326 }
327
328 int rv;
329 DWORD error = GetLastError();
330 rv = MapErrorCode(error);
331 return rv;
332 }
333
Truncate(int64 bytes)334 int64 FileStream::Truncate(int64 bytes) {
335 base::ThreadRestrictions::AssertIOAllowed();
336
337 if (!IsOpen())
338 return ERR_UNEXPECTED;
339
340 // We better be open for reading.
341 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE);
342
343 // Seek to the position to truncate from.
344 int64 seek_position = Seek(FROM_BEGIN, bytes);
345 if (seek_position != bytes)
346 return ERR_UNEXPECTED;
347
348 // And truncate the file.
349 BOOL result = SetEndOfFile(file_);
350 if (!result) {
351 DWORD error = GetLastError();
352 LOG(WARNING) << "SetEndOfFile failed: " << error;
353 return MapErrorCode(error);
354 }
355
356 // Success.
357 return seek_position;
358 }
359
360 } // namespace net
361