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