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/location.h" 8 #include "base/message_loop/message_loop_proxy.h" 9 #include "base/task_runner_util.h" 10 #include "base/threading/thread_restrictions.h" 11 #include "net/base/file_stream_net_log_parameters.h" 12 #include "net/base/net_errors.h" 13 14 #if defined(OS_ANDROID) 15 #include "base/android/content_uri_utils.h" 16 #endif 17 18 namespace { 19 20 void CallInt64ToInt(const net::CompletionCallback& callback, int64 result) { 21 callback.Run(static_cast<int>(result)); 22 } 23 24 } 25 26 namespace net { 27 28 FileStream::Context::IOResult::IOResult() 29 : result(OK), 30 os_error(0) { 31 } 32 33 FileStream::Context::IOResult::IOResult(int64 result, int os_error) 34 : result(result), 35 os_error(os_error) { 36 } 37 38 // static 39 FileStream::Context::IOResult FileStream::Context::IOResult::FromOSError( 40 int64 os_error) { 41 return IOResult(MapSystemError(os_error), os_error); 42 } 43 44 FileStream::Context::OpenResult::OpenResult() 45 : file(base::kInvalidPlatformFileValue) { 46 } 47 48 FileStream::Context::OpenResult::OpenResult(base::PlatformFile file, 49 IOResult error_code) 50 : file(file), 51 error_code(error_code) { 52 } 53 54 void FileStream::Context::Orphan() { 55 DCHECK(!orphaned_); 56 57 orphaned_ = true; 58 if (file_ != base::kInvalidPlatformFileValue) 59 bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN); 60 61 if (!async_in_progress_) { 62 CloseAndDelete(); 63 } else if (file_ != base::kInvalidPlatformFileValue) { 64 CancelIo(file_); 65 } 66 } 67 68 void FileStream::Context::OpenAsync(const base::FilePath& path, 69 int open_flags, 70 const CompletionCallback& callback) { 71 DCHECK(!async_in_progress_); 72 73 BeginOpenEvent(path); 74 75 const bool posted = base::PostTaskAndReplyWithResult( 76 task_runner_.get(), 77 FROM_HERE, 78 base::Bind( 79 &Context::OpenFileImpl, base::Unretained(this), path, open_flags), 80 base::Bind(&Context::OnOpenCompleted, base::Unretained(this), callback)); 81 DCHECK(posted); 82 83 async_in_progress_ = true; 84 } 85 86 int FileStream::Context::OpenSync(const base::FilePath& path, int open_flags) { 87 DCHECK(!async_in_progress_); 88 89 BeginOpenEvent(path); 90 OpenResult result = OpenFileImpl(path, open_flags); 91 file_ = result.file; 92 if (file_ == base::kInvalidPlatformFileValue) { 93 ProcessOpenError(result.error_code); 94 } else { 95 // TODO(satorux): Remove this once all async clients are migrated to use 96 // Open(). crbug.com/114783 97 if (open_flags & base::PLATFORM_FILE_ASYNC) 98 OnAsyncFileOpened(); 99 } 100 return result.error_code.result; 101 } 102 103 void FileStream::Context::CloseSync() { 104 DCHECK(!async_in_progress_); 105 if (file_ != base::kInvalidPlatformFileValue) { 106 base::ClosePlatformFile(file_); 107 file_ = base::kInvalidPlatformFileValue; 108 bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN); 109 } 110 } 111 112 void FileStream::Context::CloseAsync(const CompletionCallback& callback) { 113 DCHECK(!async_in_progress_); 114 const bool posted = base::PostTaskAndReplyWithResult( 115 task_runner_.get(), 116 FROM_HERE, 117 base::Bind(&Context::CloseFileImpl, base::Unretained(this)), 118 base::Bind(&Context::ProcessAsyncResult, 119 base::Unretained(this), 120 IntToInt64(callback), 121 FILE_ERROR_SOURCE_CLOSE)); 122 DCHECK(posted); 123 124 async_in_progress_ = true; 125 } 126 127 void FileStream::Context::SeekAsync(Whence whence, 128 int64 offset, 129 const Int64CompletionCallback& callback) { 130 DCHECK(!async_in_progress_); 131 132 const bool posted = base::PostTaskAndReplyWithResult( 133 task_runner_.get(), 134 FROM_HERE, 135 base::Bind( 136 &Context::SeekFileImpl, base::Unretained(this), whence, offset), 137 base::Bind(&Context::ProcessAsyncResult, 138 base::Unretained(this), 139 callback, 140 FILE_ERROR_SOURCE_SEEK)); 141 DCHECK(posted); 142 143 async_in_progress_ = true; 144 } 145 146 int64 FileStream::Context::SeekSync(Whence whence, int64 offset) { 147 IOResult result = SeekFileImpl(whence, offset); 148 RecordError(result, FILE_ERROR_SOURCE_SEEK); 149 return result.result; 150 } 151 152 void FileStream::Context::FlushAsync(const CompletionCallback& callback) { 153 DCHECK(!async_in_progress_); 154 155 const bool posted = base::PostTaskAndReplyWithResult( 156 task_runner_.get(), 157 FROM_HERE, 158 base::Bind(&Context::FlushFileImpl, base::Unretained(this)), 159 base::Bind(&Context::ProcessAsyncResult, 160 base::Unretained(this), 161 IntToInt64(callback), 162 FILE_ERROR_SOURCE_FLUSH)); 163 DCHECK(posted); 164 165 async_in_progress_ = true; 166 } 167 168 int FileStream::Context::FlushSync() { 169 IOResult result = FlushFileImpl(); 170 RecordError(result, FILE_ERROR_SOURCE_FLUSH); 171 return result.result; 172 } 173 174 void FileStream::Context::RecordError(const IOResult& result, 175 FileErrorSource source) const { 176 if (result.result >= 0) { 177 // |result| is not an error. 178 return; 179 } 180 181 if (!orphaned_) { 182 bound_net_log_.AddEvent( 183 NetLog::TYPE_FILE_STREAM_ERROR, 184 base::Bind(&NetLogFileStreamErrorCallback, 185 source, result.os_error, 186 static_cast<net::Error>(result.result))); 187 } 188 189 RecordFileError(result.os_error, source, record_uma_); 190 } 191 192 void FileStream::Context::BeginOpenEvent(const base::FilePath& path) { 193 std::string file_name = path.AsUTF8Unsafe(); 194 bound_net_log_.BeginEvent(NetLog::TYPE_FILE_STREAM_OPEN, 195 NetLog::StringCallback("file_name", &file_name)); 196 } 197 198 FileStream::Context::OpenResult FileStream::Context::OpenFileImpl( 199 const base::FilePath& path, int open_flags) { 200 base::PlatformFile file; 201 #if defined(OS_ANDROID) 202 if (path.IsContentUri()) { 203 // Check that only Read flags are set. 204 DCHECK_EQ(open_flags & ~base::PLATFORM_FILE_ASYNC, 205 base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ); 206 file = base::OpenContentUriForRead(path); 207 } else { 208 #endif // defined(OS_ANDROID) 209 // FileStream::Context actually closes the file asynchronously, 210 // independently from FileStream's destructor. It can cause problems for 211 // users wanting to delete the file right after FileStream deletion. Thus 212 // we are always adding SHARE_DELETE flag to accommodate such use case. 213 open_flags |= base::PLATFORM_FILE_SHARE_DELETE; 214 file = base::CreatePlatformFile(path, open_flags, NULL, NULL); 215 #if defined(OS_ANDROID) 216 } 217 #endif // defined(OS_ANDROID) 218 if (file == base::kInvalidPlatformFileValue) 219 return OpenResult(file, IOResult::FromOSError(GetLastErrno())); 220 221 return OpenResult(file, IOResult(OK, 0)); 222 } 223 224 void FileStream::Context::ProcessOpenError(const IOResult& error_code) { 225 bound_net_log_.EndEvent(NetLog::TYPE_FILE_STREAM_OPEN); 226 RecordError(error_code, FILE_ERROR_SOURCE_OPEN); 227 } 228 229 void FileStream::Context::OnOpenCompleted(const CompletionCallback& callback, 230 OpenResult open_result) { 231 file_ = open_result.file; 232 if (file_ == base::kInvalidPlatformFileValue) 233 ProcessOpenError(open_result.error_code); 234 else if (!orphaned_) 235 OnAsyncFileOpened(); 236 OnAsyncCompleted(IntToInt64(callback), open_result.error_code.result); 237 } 238 239 void FileStream::Context::CloseAndDelete() { 240 DCHECK(!async_in_progress_); 241 242 if (file_ == base::kInvalidPlatformFileValue) { 243 delete this; 244 } else { 245 const bool posted = task_runner_->PostTaskAndReply( 246 FROM_HERE, 247 base::Bind(base::IgnoreResult(&base::ClosePlatformFile), file_), 248 base::Bind(&Context::OnCloseCompleted, base::Unretained(this))); 249 DCHECK(posted); 250 file_ = base::kInvalidPlatformFileValue; 251 } 252 } 253 254 void FileStream::Context::OnCloseCompleted() { 255 delete this; 256 } 257 258 Int64CompletionCallback FileStream::Context::IntToInt64( 259 const CompletionCallback& callback) { 260 return base::Bind(&CallInt64ToInt, callback); 261 } 262 263 void FileStream::Context::ProcessAsyncResult( 264 const Int64CompletionCallback& callback, 265 FileErrorSource source, 266 const IOResult& result) { 267 RecordError(result, source); 268 OnAsyncCompleted(callback, result.result); 269 } 270 271 void FileStream::Context::OnAsyncCompleted( 272 const Int64CompletionCallback& callback, 273 int64 result) { 274 // Reset this before Run() as Run() may issue a new async operation. Also it 275 // should be reset before CloseAsync() because it shouldn't run if any async 276 // operation is in progress. 277 async_in_progress_ = false; 278 if (orphaned_) 279 CloseAndDelete(); 280 else 281 callback.Run(result); 282 } 283 284 } // namespace net 285 286