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