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