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 20 namespace net { 21 22 namespace { 23 24 void CallInt64ToInt(const CompletionCallback& callback, int64 result) { 25 callback.Run(static_cast<int>(result)); 26 } 27 28 } // namespace 29 30 FileStream::Context::IOResult::IOResult() 31 : result(OK), 32 os_error(0) { 33 } 34 35 FileStream::Context::IOResult::IOResult(int64 result, int os_error) 36 : result(result), 37 os_error(os_error) { 38 } 39 40 // static 41 FileStream::Context::IOResult FileStream::Context::IOResult::FromOSError( 42 int64 os_error) { 43 return IOResult(MapSystemError(os_error), os_error); 44 } 45 46 // --------------------------------------------------------------------- 47 48 FileStream::Context::OpenResult::OpenResult() { 49 } 50 51 FileStream::Context::OpenResult::OpenResult(base::File file, 52 IOResult error_code) 53 : file(file.Pass()), 54 error_code(error_code) { 55 } 56 57 FileStream::Context::OpenResult::OpenResult(RValue other) 58 : file(other.object->file.Pass()), 59 error_code(other.object->error_code) { 60 } 61 62 FileStream::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 73 void 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 87 void 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 103 void 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 117 void 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 135 void 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 150 FileStream::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 183 FileStream::Context::IOResult FileStream::Context::CloseFileImpl() { 184 file_.Close(); 185 return IOResult(OK, 0); 186 } 187 188 FileStream::Context::IOResult FileStream::Context::FlushFileImpl() { 189 if (file_.Flush()) 190 return IOResult(OK, 0); 191 192 return IOResult::FromOSError(logging::GetLastSystemErrorCode()); 193 } 194 195 void 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 204 void 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 218 Int64CompletionCallback FileStream::Context::IntToInt64( 219 const CompletionCallback& callback) { 220 return base::Bind(&CallInt64ToInt, callback); 221 } 222 223 void 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