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