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 23 static void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) { 24 overlapped->Offset = offset.LowPart; 25 overlapped->OffsetHigh = offset.HighPart; 26 } 27 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 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: 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 63 OVERLAPPED* overlapped() { return &context_.overlapped; } 64 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 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 91 void FileStream::AsyncContext::IOCompletionIsPending( 92 CompletionCallback* callback) { 93 DCHECK(!callback_); 94 callback_ = callback; 95 } 96 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 121 FileStream::FileStream() 122 : file_(INVALID_HANDLE_VALUE), 123 open_flags_(0), 124 auto_closed_(true) { 125 } 126 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 140 FileStream::~FileStream() { 141 if (auto_closed_) 142 Close(); 143 } 144 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 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 179 bool FileStream::IsOpen() const { 180 return file_ != INVALID_HANDLE_VALUE; 181 } 182 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 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 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 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 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 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 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