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 // For 64-bit file access (off_t = off64_t, lseek64, etc). 6 #define _FILE_OFFSET_BITS 64 7 8 #include "net/base/file_stream.h" 9 10 #include <sys/types.h> 11 #include <sys/stat.h> 12 #include <fcntl.h> 13 #include <unistd.h> 14 #include <errno.h> 15 16 #include "base/basictypes.h" 17 #include "base/eintr_wrapper.h" 18 #include "base/file_path.h" 19 #include "base/logging.h" 20 #include "base/message_loop.h" 21 #include "base/string_util.h" 22 #include "base/waitable_event.h" 23 #include "base/worker_pool.h" 24 #include "net/base/net_errors.h" 25 26 // We cast back and forth, so make sure it's the size we're expecting. 27 COMPILE_ASSERT(sizeof(int64) == sizeof(off_t), off_t_64_bit); 28 29 // Make sure our Whence mappings match the system headers. 30 COMPILE_ASSERT(net::FROM_BEGIN == SEEK_SET && 31 net::FROM_CURRENT == SEEK_CUR && 32 net::FROM_END == SEEK_END, whence_matches_system); 33 34 namespace net { 35 namespace { 36 37 // Map from errno to net error codes. 38 int64 MapErrorCode(int err) { 39 switch (err) { 40 case ENOENT: 41 return ERR_FILE_NOT_FOUND; 42 case EACCES: 43 return ERR_ACCESS_DENIED; 44 default: 45 LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; 46 return ERR_FAILED; 47 } 48 } 49 50 // ReadFile() is a simple wrapper around read() that handles EINTR signals and 51 // calls MapErrorCode() to map errno to net error codes. 52 int ReadFile(base::PlatformFile file, char* buf, int buf_len) { 53 // read(..., 0) returns 0 to indicate end-of-file. 54 55 // Loop in the case of getting interrupted by a signal. 56 ssize_t res = HANDLE_EINTR(read(file, buf, static_cast<size_t>(buf_len))); 57 if (res == static_cast<ssize_t>(-1)) 58 return MapErrorCode(errno); 59 return static_cast<int>(res); 60 } 61 62 // WriteFile() is a simple wrapper around write() that handles EINTR signals and 63 // calls MapErrorCode() to map errno to net error codes. It tries to write to 64 // completion. 65 int WriteFile(base::PlatformFile file, const char* buf, int buf_len) { 66 ssize_t res = HANDLE_EINTR(write(file, buf, buf_len)); 67 if (res == -1) 68 return MapErrorCode(errno); 69 return res; 70 } 71 72 // BackgroundReadTask is a simple task that reads a file and then runs 73 // |callback|. AsyncContext will post this task to the WorkerPool. 74 class BackgroundReadTask : public Task { 75 public: 76 BackgroundReadTask(base::PlatformFile file, char* buf, int buf_len, 77 CompletionCallback* callback); 78 ~BackgroundReadTask(); 79 80 virtual void Run(); 81 82 private: 83 const base::PlatformFile file_; 84 char* const buf_; 85 const int buf_len_; 86 CompletionCallback* const callback_; 87 88 DISALLOW_COPY_AND_ASSIGN(BackgroundReadTask); 89 }; 90 91 BackgroundReadTask::BackgroundReadTask( 92 base::PlatformFile file, char* buf, int buf_len, 93 CompletionCallback* callback) 94 : file_(file), buf_(buf), buf_len_(buf_len), callback_(callback) {} 95 96 BackgroundReadTask::~BackgroundReadTask() {} 97 98 void BackgroundReadTask::Run() { 99 int result = ReadFile(file_, buf_, buf_len_); 100 callback_->Run(result); 101 } 102 103 // BackgroundWriteTask is a simple task that writes to a file and then runs 104 // |callback|. AsyncContext will post this task to the WorkerPool. 105 class BackgroundWriteTask : public Task { 106 public: 107 BackgroundWriteTask(base::PlatformFile file, const char* buf, int buf_len, 108 CompletionCallback* callback); 109 ~BackgroundWriteTask(); 110 111 virtual void Run(); 112 113 private: 114 const base::PlatformFile file_; 115 const char* const buf_; 116 const int buf_len_; 117 CompletionCallback* const callback_; 118 119 DISALLOW_COPY_AND_ASSIGN(BackgroundWriteTask); 120 }; 121 122 BackgroundWriteTask::BackgroundWriteTask( 123 base::PlatformFile file, const char* buf, int buf_len, 124 CompletionCallback* callback) 125 : file_(file), buf_(buf), buf_len_(buf_len), callback_(callback) {} 126 127 BackgroundWriteTask::~BackgroundWriteTask() {} 128 129 void BackgroundWriteTask::Run() { 130 int result = WriteFile(file_, buf_, buf_len_); 131 callback_->Run(result); 132 } 133 134 } // namespace 135 136 // CancelableCallbackTask takes ownership of the Callback. This task gets 137 // posted to the MessageLoopForIO instance. 138 class CancelableCallbackTask : public CancelableTask { 139 public: 140 explicit CancelableCallbackTask(Callback0::Type* callback) 141 : canceled_(false), callback_(callback) {} 142 143 virtual void Run() { 144 if (!canceled_) 145 callback_->Run(); 146 } 147 148 virtual void Cancel() { 149 canceled_ = true; 150 } 151 152 private: 153 bool canceled_; 154 scoped_ptr<Callback0::Type> callback_; 155 }; 156 157 // FileStream::AsyncContext ---------------------------------------------- 158 159 class FileStream::AsyncContext { 160 public: 161 AsyncContext(); 162 ~AsyncContext(); 163 164 // These methods post synchronous read() and write() calls to a WorkerThread. 165 void InitiateAsyncRead( 166 base::PlatformFile file, char* buf, int buf_len, 167 CompletionCallback* callback); 168 void InitiateAsyncWrite( 169 base::PlatformFile file, const char* buf, int buf_len, 170 CompletionCallback* callback); 171 172 CompletionCallback* callback() const { return callback_; } 173 174 // Called by the WorkerPool thread executing the IO after the IO completes. 175 // This method queues RunAsynchronousCallback() on the MessageLoop and signals 176 // |background_io_completed_callback_|, in case the destructor is waiting. In 177 // that case, the destructor will call RunAsynchronousCallback() instead, and 178 // cancel |message_loop_task_|. 179 // |result| is the result of the Read/Write task. 180 void OnBackgroundIOCompleted(int result); 181 182 private: 183 // Always called on the IO thread, either directly by a task on the 184 // MessageLoop or by ~AsyncContext(). 185 void RunAsynchronousCallback(); 186 187 // The MessageLoopForIO that this AsyncContext is running on. 188 MessageLoopForIO* const message_loop_; 189 CompletionCallback* callback_; // The user provided callback. 190 191 // A callback wrapper around OnBackgroundIOCompleted(). Run by the WorkerPool 192 // thread doing the background IO on our behalf. 193 CompletionCallbackImpl<AsyncContext> background_io_completed_callback_; 194 195 // This is used to synchronize between the AsyncContext destructor (which runs 196 // on the IO thread and OnBackgroundIOCompleted() which runs on the WorkerPool 197 // thread. 198 base::WaitableEvent background_io_completed_; 199 200 // These variables are only valid when background_io_completed is signaled. 201 int result_; 202 CancelableCallbackTask* message_loop_task_; 203 204 bool is_closing_; 205 206 DISALLOW_COPY_AND_ASSIGN(AsyncContext); 207 }; 208 209 FileStream::AsyncContext::AsyncContext() 210 : message_loop_(MessageLoopForIO::current()), 211 callback_(NULL), 212 background_io_completed_callback_( 213 this, &AsyncContext::OnBackgroundIOCompleted), 214 background_io_completed_(true, false), 215 message_loop_task_(NULL), 216 is_closing_(false) {} 217 218 FileStream::AsyncContext::~AsyncContext() { 219 is_closing_ = true; 220 if (callback_) { 221 // If |callback_| is non-NULL, that implies either the worker thread is 222 // still running the IO task, or the completion callback is queued up on the 223 // MessageLoopForIO, but AsyncContext() got deleted before then. 224 const bool need_to_wait = !background_io_completed_.IsSignaled(); 225 base::Time start = base::Time::Now(); 226 RunAsynchronousCallback(); 227 if (need_to_wait) { 228 // We want to see if we block the message loop for too long. 229 UMA_HISTOGRAM_TIMES("AsyncIO.FileStreamClose", base::Time::Now() - start); 230 } 231 } 232 } 233 234 void FileStream::AsyncContext::InitiateAsyncRead( 235 base::PlatformFile file, char* buf, int buf_len, 236 CompletionCallback* callback) { 237 DCHECK(!callback_); 238 callback_ = callback; 239 240 WorkerPool::PostTask(FROM_HERE, 241 new BackgroundReadTask( 242 file, buf, buf_len, 243 &background_io_completed_callback_), 244 true /* task_is_slow */); 245 } 246 247 void FileStream::AsyncContext::InitiateAsyncWrite( 248 base::PlatformFile file, const char* buf, int buf_len, 249 CompletionCallback* callback) { 250 DCHECK(!callback_); 251 callback_ = callback; 252 253 WorkerPool::PostTask(FROM_HERE, 254 new BackgroundWriteTask( 255 file, buf, buf_len, 256 &background_io_completed_callback_), 257 true /* task_is_slow */); 258 } 259 260 void FileStream::AsyncContext::OnBackgroundIOCompleted(int result) { 261 result_ = result; 262 message_loop_task_ = new CancelableCallbackTask( 263 NewCallback(this, &AsyncContext::RunAsynchronousCallback)); 264 message_loop_->PostTask(FROM_HERE, message_loop_task_); 265 background_io_completed_.Signal(); 266 } 267 268 void FileStream::AsyncContext::RunAsynchronousCallback() { 269 // Wait() here ensures that all modifications from the WorkerPool thread are 270 // now visible. 271 background_io_completed_.Wait(); 272 273 // Either we're in the MessageLoop's task, in which case Cancel() doesn't do 274 // anything, or we're in ~AsyncContext(), in which case this prevents the call 275 // from happening again. Must do it here after calling Wait(). 276 message_loop_task_->Cancel(); 277 message_loop_task_ = NULL; 278 279 if (is_closing_) { 280 callback_ = NULL; 281 return; 282 } 283 284 DCHECK(callback_); 285 CompletionCallback* temp = NULL; 286 std::swap(temp, callback_); 287 background_io_completed_.Reset(); 288 temp->Run(result_); 289 } 290 291 // FileStream ------------------------------------------------------------ 292 293 FileStream::FileStream() 294 : file_(base::kInvalidPlatformFileValue), 295 open_flags_(0) { 296 DCHECK(!IsOpen()); 297 } 298 299 FileStream::FileStream(base::PlatformFile file, int flags) 300 : file_(file), 301 open_flags_(flags) { 302 // If the file handle is opened with base::PLATFORM_FILE_ASYNC, we need to 303 // make sure we will perform asynchronous File IO to it. 304 if (flags & base::PLATFORM_FILE_ASYNC) { 305 async_context_.reset(new AsyncContext()); 306 } 307 } 308 309 FileStream::~FileStream() { 310 Close(); 311 } 312 313 void FileStream::Close() { 314 // Abort any existing asynchronous operations. 315 async_context_.reset(); 316 317 if (file_ != base::kInvalidPlatformFileValue) { 318 if (close(file_) != 0) { 319 NOTREACHED(); 320 } 321 file_ = base::kInvalidPlatformFileValue; 322 } 323 } 324 325 int FileStream::Open(const FilePath& path, int open_flags) { 326 if (IsOpen()) { 327 DLOG(FATAL) << "File is already open!"; 328 return ERR_UNEXPECTED; 329 } 330 331 open_flags_ = open_flags; 332 file_ = base::CreatePlatformFile(path, open_flags_, NULL); 333 if (file_ == base::kInvalidPlatformFileValue) { 334 LOG(WARNING) << "Failed to open file: " << errno 335 << " (" << path.ToWStringHack() << ")"; 336 return MapErrorCode(errno); 337 } 338 339 if (open_flags_ & base::PLATFORM_FILE_ASYNC) { 340 async_context_.reset(new AsyncContext()); 341 } 342 343 return OK; 344 } 345 346 bool FileStream::IsOpen() const { 347 return file_ != base::kInvalidPlatformFileValue; 348 } 349 350 int64 FileStream::Seek(Whence whence, int64 offset) { 351 if (!IsOpen()) 352 return ERR_UNEXPECTED; 353 354 // If we're in async, make sure we don't have a request in flight. 355 DCHECK(!async_context_.get() || !async_context_->callback()); 356 357 off_t res = lseek(file_, static_cast<off_t>(offset), 358 static_cast<int>(whence)); 359 if (res == static_cast<off_t>(-1)) 360 return MapErrorCode(errno); 361 362 return res; 363 } 364 365 int64 FileStream::Available() { 366 if (!IsOpen()) 367 return ERR_UNEXPECTED; 368 369 int64 cur_pos = Seek(FROM_CURRENT, 0); 370 if (cur_pos < 0) 371 return cur_pos; 372 373 struct stat info; 374 if (fstat(file_, &info) != 0) 375 return MapErrorCode(errno); 376 377 int64 size = static_cast<int64>(info.st_size); 378 DCHECK_GT(size, cur_pos); 379 380 return size - cur_pos; 381 } 382 383 int FileStream::Read( 384 char* buf, int buf_len, CompletionCallback* callback) { 385 if (!IsOpen()) 386 return ERR_UNEXPECTED; 387 388 // read(..., 0) will return 0, which indicates end-of-file. 389 DCHECK(buf_len > 0); 390 DCHECK(open_flags_ & base::PLATFORM_FILE_READ); 391 392 if (async_context_.get()) { 393 DCHECK(open_flags_ & base::PLATFORM_FILE_ASYNC); 394 // If we're in async, make sure we don't have a request in flight. 395 DCHECK(!async_context_->callback()); 396 async_context_->InitiateAsyncRead(file_, buf, buf_len, callback); 397 return ERR_IO_PENDING; 398 } else { 399 return ReadFile(file_, buf, buf_len); 400 } 401 } 402 403 int FileStream::ReadUntilComplete(char *buf, int buf_len) { 404 int to_read = buf_len; 405 int bytes_total = 0; 406 407 do { 408 int bytes_read = Read(buf, to_read, NULL); 409 if (bytes_read <= 0) { 410 if (bytes_total == 0) 411 return bytes_read; 412 413 return bytes_total; 414 } 415 416 bytes_total += bytes_read; 417 buf += bytes_read; 418 to_read -= bytes_read; 419 } while (bytes_total < buf_len); 420 421 return bytes_total; 422 } 423 424 int FileStream::Write( 425 const char* buf, int buf_len, CompletionCallback* callback) { 426 // write(..., 0) will return 0, which indicates end-of-file. 427 DCHECK(buf_len > 0); 428 429 if (!IsOpen()) 430 return ERR_UNEXPECTED; 431 432 if (async_context_.get()) { 433 DCHECK(open_flags_ & base::PLATFORM_FILE_ASYNC); 434 // If we're in async, make sure we don't have a request in flight. 435 DCHECK(!async_context_->callback()); 436 async_context_->InitiateAsyncWrite(file_, buf, buf_len, callback); 437 return ERR_IO_PENDING; 438 } else { 439 return WriteFile(file_, buf, buf_len); 440 } 441 } 442 443 int64 FileStream::Truncate(int64 bytes) { 444 if (!IsOpen()) 445 return ERR_UNEXPECTED; 446 447 // We better be open for reading. 448 DCHECK(open_flags_ & base::PLATFORM_FILE_WRITE); 449 450 // Seek to the position to truncate from. 451 int64 seek_position = Seek(FROM_BEGIN, bytes); 452 if (seek_position != bytes) 453 return ERR_UNEXPECTED; 454 455 // And truncate the file. 456 int result = ftruncate(file_, bytes); 457 return result == 0 ? seek_position : MapErrorCode(errno); 458 } 459 460 } // namespace net 461