1// Copyright (c) 2012 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_context.h" 6 7#include "base/files/file_path.h" 8#include "base/location.h" 9#include "base/message_loop/message_loop_proxy.h" 10#include "base/task_runner.h" 11#include "base/task_runner_util.h" 12#include "base/threading/thread_restrictions.h" 13#include "base/values.h" 14#include "net/base/net_errors.h" 15 16#if defined(OS_ANDROID) 17#include "base/android/content_uri_utils.h" 18#endif 19 20namespace net { 21 22namespace { 23 24void CallInt64ToInt(const CompletionCallback& callback, int64 result) { 25 callback.Run(static_cast<int>(result)); 26} 27 28} // namespace 29 30FileStream::Context::IOResult::IOResult() 31 : result(OK), 32 os_error(0) { 33} 34 35FileStream::Context::IOResult::IOResult(int64 result, int os_error) 36 : result(result), 37 os_error(os_error) { 38} 39 40// static 41FileStream::Context::IOResult FileStream::Context::IOResult::FromOSError( 42 int64 os_error) { 43 return IOResult(MapSystemError(os_error), os_error); 44} 45 46// --------------------------------------------------------------------- 47 48FileStream::Context::OpenResult::OpenResult() { 49} 50 51FileStream::Context::OpenResult::OpenResult(base::File file, 52 IOResult error_code) 53 : file(file.Pass()), 54 error_code(error_code) { 55} 56 57FileStream::Context::OpenResult::OpenResult(RValue other) 58 : file(other.object->file.Pass()), 59 error_code(other.object->error_code) { 60} 61 62FileStream::Context::OpenResult& FileStream::Context::OpenResult::operator=( 63 RValue other) { 64 if (this != other.object) { 65 file = other.object->file.Pass(); 66 error_code = other.object->error_code; 67 } 68 return *this; 69} 70 71// --------------------------------------------------------------------- 72 73void FileStream::Context::Orphan() { 74 DCHECK(!orphaned_); 75 76 orphaned_ = true; 77 78 if (!async_in_progress_) { 79 CloseAndDelete(); 80 } else if (file_.IsValid()) { 81#if defined(OS_WIN) 82 CancelIo(file_.GetPlatformFile()); 83#endif 84 } 85} 86 87void FileStream::Context::Open(const base::FilePath& path, 88 int open_flags, 89 const CompletionCallback& callback) { 90 DCHECK(!async_in_progress_); 91 92 bool posted = base::PostTaskAndReplyWithResult( 93 task_runner_.get(), 94 FROM_HERE, 95 base::Bind( 96 &Context::OpenFileImpl, base::Unretained(this), path, open_flags), 97 base::Bind(&Context::OnOpenCompleted, base::Unretained(this), callback)); 98 DCHECK(posted); 99 100 async_in_progress_ = true; 101} 102 103void FileStream::Context::Close(const CompletionCallback& callback) { 104 DCHECK(!async_in_progress_); 105 bool posted = base::PostTaskAndReplyWithResult( 106 task_runner_.get(), 107 FROM_HERE, 108 base::Bind(&Context::CloseFileImpl, base::Unretained(this)), 109 base::Bind(&Context::OnAsyncCompleted, 110 base::Unretained(this), 111 IntToInt64(callback))); 112 DCHECK(posted); 113 114 async_in_progress_ = true; 115} 116 117void FileStream::Context::Seek(base::File::Whence whence, 118 int64 offset, 119 const Int64CompletionCallback& callback) { 120 DCHECK(!async_in_progress_); 121 122 bool posted = base::PostTaskAndReplyWithResult( 123 task_runner_.get(), 124 FROM_HERE, 125 base::Bind( 126 &Context::SeekFileImpl, base::Unretained(this), whence, offset), 127 base::Bind(&Context::OnAsyncCompleted, 128 base::Unretained(this), 129 callback)); 130 DCHECK(posted); 131 132 async_in_progress_ = true; 133} 134 135void FileStream::Context::Flush(const CompletionCallback& callback) { 136 DCHECK(!async_in_progress_); 137 138 bool posted = base::PostTaskAndReplyWithResult( 139 task_runner_.get(), 140 FROM_HERE, 141 base::Bind(&Context::FlushFileImpl, base::Unretained(this)), 142 base::Bind(&Context::OnAsyncCompleted, 143 base::Unretained(this), 144 IntToInt64(callback))); 145 DCHECK(posted); 146 147 async_in_progress_ = true; 148} 149 150FileStream::Context::OpenResult FileStream::Context::OpenFileImpl( 151 const base::FilePath& path, int open_flags) { 152#if defined(OS_POSIX) 153 // Always use blocking IO. 154 open_flags &= ~base::File::FLAG_ASYNC; 155#endif 156 base::File file; 157#if defined(OS_ANDROID) 158 if (path.IsContentUri()) { 159 // Check that only Read flags are set. 160 DCHECK_EQ(open_flags & ~base::File::FLAG_ASYNC, 161 base::File::FLAG_OPEN | base::File::FLAG_READ); 162 file = base::OpenContentUriForRead(path); 163 } else { 164#endif // defined(OS_ANDROID) 165 // FileStream::Context actually closes the file asynchronously, 166 // independently from FileStream's destructor. It can cause problems for 167 // users wanting to delete the file right after FileStream deletion. Thus 168 // we are always adding SHARE_DELETE flag to accommodate such use case. 169 // TODO(rvargas): This sounds like a bug, as deleting the file would 170 // presumably happen on the wrong thread. There should be an async delete. 171 open_flags |= base::File::FLAG_SHARE_DELETE; 172 file.Initialize(path, open_flags); 173#if defined(OS_ANDROID) 174 } 175#endif // defined(OS_ANDROID) 176 if (!file.IsValid()) 177 return OpenResult(base::File(), 178 IOResult::FromOSError(logging::GetLastSystemErrorCode())); 179 180 return OpenResult(file.Pass(), IOResult(OK, 0)); 181} 182 183FileStream::Context::IOResult FileStream::Context::CloseFileImpl() { 184 file_.Close(); 185 return IOResult(OK, 0); 186} 187 188FileStream::Context::IOResult FileStream::Context::FlushFileImpl() { 189 if (file_.Flush()) 190 return IOResult(OK, 0); 191 192 return IOResult::FromOSError(logging::GetLastSystemErrorCode()); 193} 194 195void FileStream::Context::OnOpenCompleted(const CompletionCallback& callback, 196 OpenResult open_result) { 197 file_ = open_result.file.Pass(); 198 if (file_.IsValid() && !orphaned_) 199 OnFileOpened(); 200 201 OnAsyncCompleted(IntToInt64(callback), open_result.error_code); 202} 203 204void FileStream::Context::CloseAndDelete() { 205 DCHECK(!async_in_progress_); 206 207 if (file_.IsValid()) { 208 bool posted = task_runner_.get()->PostTask( 209 FROM_HERE, 210 base::Bind(base::IgnoreResult(&Context::CloseFileImpl), 211 base::Owned(this))); 212 DCHECK(posted); 213 } else { 214 delete this; 215 } 216} 217 218Int64CompletionCallback FileStream::Context::IntToInt64( 219 const CompletionCallback& callback) { 220 return base::Bind(&CallInt64ToInt, callback); 221} 222 223void FileStream::Context::OnAsyncCompleted( 224 const Int64CompletionCallback& callback, 225 const IOResult& result) { 226 // Reset this before Run() as Run() may issue a new async operation. Also it 227 // should be reset before Close() because it shouldn't run if any async 228 // operation is in progress. 229 async_in_progress_ = false; 230 if (orphaned_) 231 CloseAndDelete(); 232 else 233 callback.Run(result.result); 234} 235 236} // namespace net 237