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.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 25 void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) { 26 overlapped->Offset = offset.LowPart; 27 overlapped->OffsetHigh = offset.HighPart; 28 } 29 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 40 FileStream::Context::Context(const scoped_refptr<base::TaskRunner>& task_runner) 41 : io_context_(), 42 async_in_progress_(false), 43 orphaned_(false), 44 task_runner_(task_runner) { 45 io_context_.handler = this; 46 memset(&io_context_.overlapped, 0, sizeof(io_context_.overlapped)); 47 } 48 49 FileStream::Context::Context(base::File file, 50 const scoped_refptr<base::TaskRunner>& task_runner) 51 : io_context_(), 52 file_(file.Pass()), 53 async_in_progress_(false), 54 orphaned_(false), 55 task_runner_(task_runner) { 56 io_context_.handler = this; 57 memset(&io_context_.overlapped, 0, sizeof(io_context_.overlapped)); 58 if (file_.IsValid()) { 59 // TODO(hashimoto): Check that file_ is async. 60 OnAsyncFileOpened(); 61 } 62 } 63 64 FileStream::Context::~Context() { 65 } 66 67 int FileStream::Context::ReadAsync(IOBuffer* buf, 68 int buf_len, 69 const CompletionCallback& callback) { 70 DCHECK(!async_in_progress_); 71 72 DWORD bytes_read; 73 if (!ReadFile(file_.GetPlatformFile(), buf->data(), buf_len, 74 &bytes_read, &io_context_.overlapped)) { 75 IOResult error = IOResult::FromOSError(GetLastError()); 76 if (error.os_error == ERROR_IO_PENDING) { 77 IOCompletionIsPending(callback, buf); 78 } else if (error.os_error == ERROR_HANDLE_EOF) { 79 return 0; // Report EOF by returning 0 bytes read. 80 } else { 81 LOG(WARNING) << "ReadFile failed: " << error.os_error; 82 } 83 return error.result; 84 } 85 86 IOCompletionIsPending(callback, buf); 87 return ERR_IO_PENDING; 88 } 89 90 int FileStream::Context::WriteAsync(IOBuffer* buf, 91 int buf_len, 92 const CompletionCallback& callback) { 93 DWORD bytes_written = 0; 94 if (!WriteFile(file_.GetPlatformFile(), buf->data(), buf_len, 95 &bytes_written, &io_context_.overlapped)) { 96 IOResult error = IOResult::FromOSError(GetLastError()); 97 if (error.os_error == ERROR_IO_PENDING) { 98 IOCompletionIsPending(callback, buf); 99 } else { 100 LOG(WARNING) << "WriteFile failed: " << error.os_error; 101 } 102 return error.result; 103 } 104 105 IOCompletionIsPending(callback, buf); 106 return ERR_IO_PENDING; 107 } 108 109 void FileStream::Context::OnAsyncFileOpened() { 110 base::MessageLoopForIO::current()->RegisterIOHandler(file_.GetPlatformFile(), 111 this); 112 } 113 114 FileStream::Context::IOResult FileStream::Context::SeekFileImpl(Whence whence, 115 int64 offset) { 116 LARGE_INTEGER distance, res; 117 distance.QuadPart = offset; 118 DWORD move_method = static_cast<DWORD>(whence); 119 if (SetFilePointerEx(file_.GetPlatformFile(), distance, &res, move_method)) { 120 SetOffset(&io_context_.overlapped, res); 121 return IOResult(res.QuadPart, 0); 122 } 123 124 return IOResult::FromOSError(GetLastError()); 125 } 126 127 FileStream::Context::IOResult FileStream::Context::FlushFileImpl() { 128 if (FlushFileBuffers(file_.GetPlatformFile())) 129 return IOResult(OK, 0); 130 131 return IOResult::FromOSError(GetLastError()); 132 } 133 134 void FileStream::Context::IOCompletionIsPending( 135 const CompletionCallback& callback, 136 IOBuffer* buf) { 137 DCHECK(callback_.is_null()); 138 callback_ = callback; 139 in_flight_buf_ = buf; // Hold until the async operation ends. 140 async_in_progress_ = true; 141 } 142 143 void FileStream::Context::OnIOCompleted( 144 base::MessageLoopForIO::IOContext* context, 145 DWORD bytes_read, 146 DWORD error) { 147 DCHECK_EQ(&io_context_, context); 148 DCHECK(!callback_.is_null()); 149 DCHECK(async_in_progress_); 150 151 async_in_progress_ = false; 152 if (orphaned_) { 153 callback_.Reset(); 154 in_flight_buf_ = NULL; 155 CloseAndDelete(); 156 return; 157 } 158 159 int result; 160 if (error == ERROR_HANDLE_EOF) { 161 result = 0; 162 } else if (error) { 163 IOResult error_result = IOResult::FromOSError(error); 164 result = error_result.result; 165 } else { 166 result = bytes_read; 167 IncrementOffset(&io_context_.overlapped, bytes_read); 168 } 169 170 CompletionCallback temp_callback = callback_; 171 callback_.Reset(); 172 scoped_refptr<IOBuffer> temp_buf = in_flight_buf_; 173 in_flight_buf_ = NULL; 174 temp_callback.Run(result); 175 } 176 177 } // namespace net 178