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