1 // Copyright 2014 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 "chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer.h" 6 7 #include "base/debug/trace_event.h" 8 #include "base/memory/ref_counted.h" 9 #include "chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util.h" 10 #include "chrome/browser/chromeos/file_system_provider/mount_path_util.h" 11 #include "chrome/browser/chromeos/file_system_provider/provided_file_system_interface.h" 12 #include "content/public/browser/browser_thread.h" 13 #include "net/base/io_buffer.h" 14 #include "net/base/net_errors.h" 15 16 using content::BrowserThread; 17 18 namespace chromeos { 19 namespace file_system_provider { 20 namespace { 21 22 // Dicards the callback from CloseFile(). 23 void EmptyStatusCallback(base::File::Error /* result */) { 24 } 25 26 } // namespace 27 28 class FileStreamWriter::OperationRunner 29 : public base::RefCountedThreadSafe<FileStreamWriter::OperationRunner> { 30 public: 31 OperationRunner() : file_handle_(-1) {} 32 33 // Opens a file for writing and calls the completion callback. Must be called 34 // on UI thread. 35 void OpenFileOnUIThread( 36 const storage::FileSystemURL& url, 37 const storage::AsyncFileUtil::StatusCallback& callback) { 38 DCHECK_CURRENTLY_ON(BrowserThread::UI); 39 40 util::FileSystemURLParser parser(url); 41 if (!parser.Parse()) { 42 BrowserThread::PostTask( 43 BrowserThread::IO, 44 FROM_HERE, 45 base::Bind(callback, base::File::FILE_ERROR_SECURITY)); 46 return; 47 } 48 49 file_system_ = parser.file_system()->GetWeakPtr(); 50 abort_callback_ = parser.file_system()->OpenFile( 51 parser.file_path(), 52 ProvidedFileSystemInterface::OPEN_FILE_MODE_WRITE, 53 base::Bind( 54 &OperationRunner::OnOpenFileCompletedOnUIThread, this, callback)); 55 } 56 57 // Closes a file. Ignores result, since outlives the caller. Must be called on 58 // UI thread. 59 void CloseFileOnUIThread() { 60 DCHECK_CURRENTLY_ON(BrowserThread::UI); 61 if (file_system_.get() && file_handle_ != -1) { 62 // Closing a file must not be aborted, since we could end up on files 63 // which are never closed. 64 file_system_->CloseFile(file_handle_, base::Bind(&EmptyStatusCallback)); 65 } 66 } 67 68 // Requests writing bytes to the file. In case of either success or a failure 69 // |callback| is executed. Must be called on UI thread. 70 void WriteFileOnUIThread( 71 scoped_refptr<net::IOBuffer> buffer, 72 int64 offset, 73 int length, 74 const storage::AsyncFileUtil::StatusCallback& callback) { 75 DCHECK_CURRENTLY_ON(BrowserThread::UI); 76 77 // If the file system got unmounted, then abort the writing operation. 78 if (!file_system_.get()) { 79 BrowserThread::PostTask( 80 BrowserThread::IO, 81 FROM_HERE, 82 base::Bind(callback, base::File::FILE_ERROR_ABORT)); 83 return; 84 } 85 86 abort_callback_ = file_system_->WriteFile( 87 file_handle_, 88 buffer.get(), 89 offset, 90 length, 91 base::Bind( 92 &OperationRunner::OnWriteFileCompletedOnUIThread, this, callback)); 93 } 94 95 // Aborts the most recent operation (if exists), and calls the callback. 96 void AbortOnUIThread(const storage::AsyncFileUtil::StatusCallback& callback) { 97 DCHECK_CURRENTLY_ON(BrowserThread::UI); 98 99 if (abort_callback_.is_null()) { 100 // No operation to be cancelled. At most a callback call, which will be 101 // discarded. 102 BrowserThread::PostTask(BrowserThread::IO, 103 FROM_HERE, 104 base::Bind(callback, base::File::FILE_OK)); 105 return; 106 } 107 108 const ProvidedFileSystemInterface::AbortCallback abort_callback = 109 abort_callback_; 110 abort_callback_ = ProvidedFileSystemInterface::AbortCallback(); 111 abort_callback.Run(base::Bind( 112 &OperationRunner::OnAbortCompletedOnUIThread, this, callback)); 113 } 114 115 private: 116 friend class base::RefCountedThreadSafe<OperationRunner>; 117 118 virtual ~OperationRunner() {} 119 120 // Remembers a file handle for further operations and forwards the result to 121 // the IO thread. 122 void OnOpenFileCompletedOnUIThread( 123 const storage::AsyncFileUtil::StatusCallback& callback, 124 int file_handle, 125 base::File::Error result) { 126 DCHECK_CURRENTLY_ON(BrowserThread::UI); 127 128 file_handle_ = file_handle; 129 BrowserThread::PostTask( 130 BrowserThread::IO, FROM_HERE, base::Bind(callback, result)); 131 } 132 133 // Forwards a response of writing to a file to the IO thread. 134 void OnWriteFileCompletedOnUIThread( 135 const storage::AsyncFileUtil::StatusCallback& callback, 136 base::File::Error result) { 137 DCHECK_CURRENTLY_ON(BrowserThread::UI); 138 BrowserThread::PostTask( 139 BrowserThread::IO, FROM_HERE, base::Bind(callback, result)); 140 } 141 142 // Forwards a response of aborting an operation to the IO thread. 143 void OnAbortCompletedOnUIThread( 144 const storage::AsyncFileUtil::StatusCallback& callback, 145 base::File::Error result) { 146 DCHECK_CURRENTLY_ON(BrowserThread::UI); 147 BrowserThread::PostTask( 148 BrowserThread::IO, FROM_HERE, base::Bind(callback, result)); 149 } 150 151 ProvidedFileSystemInterface::AbortCallback abort_callback_; 152 base::WeakPtr<ProvidedFileSystemInterface> file_system_; 153 int file_handle_; 154 155 DISALLOW_COPY_AND_ASSIGN(OperationRunner); 156 }; 157 158 FileStreamWriter::FileStreamWriter(const storage::FileSystemURL& url, 159 int64 initial_offset) 160 : url_(url), 161 current_offset_(initial_offset), 162 runner_(new OperationRunner), 163 state_(NOT_INITIALIZED), 164 weak_ptr_factory_(this) { 165 } 166 167 FileStreamWriter::~FileStreamWriter() { 168 BrowserThread::PostTask( 169 BrowserThread::UI, 170 FROM_HERE, 171 base::Bind(&OperationRunner::CloseFileOnUIThread, runner_)); 172 } 173 174 void FileStreamWriter::Initialize( 175 const base::Closure& pending_closure, 176 const net::CompletionCallback& error_callback) { 177 DCHECK_CURRENTLY_ON(BrowserThread::IO); 178 DCHECK_EQ(NOT_INITIALIZED, state_); 179 state_ = INITIALIZING; 180 181 BrowserThread::PostTask( 182 BrowserThread::UI, 183 FROM_HERE, 184 base::Bind(&OperationRunner::OpenFileOnUIThread, 185 runner_, 186 url_, 187 base::Bind(&FileStreamWriter::OnOpenFileCompleted, 188 weak_ptr_factory_.GetWeakPtr(), 189 pending_closure, 190 error_callback))); 191 } 192 193 void FileStreamWriter::OnOpenFileCompleted( 194 const base::Closure& pending_closure, 195 const net::CompletionCallback& error_callback, 196 base::File::Error result) { 197 DCHECK_CURRENTLY_ON(BrowserThread::IO); 198 DCHECK_EQ(INITIALIZING, state_); 199 200 // In case of an error, return immediately using the |error_callback| of the 201 // Write() pending request. 202 if (result != base::File::FILE_OK) { 203 state_ = FAILED; 204 error_callback.Run(net::FileErrorToNetError(result)); 205 return; 206 } 207 208 DCHECK_EQ(base::File::FILE_OK, result); 209 state_ = INITIALIZED; 210 211 // Run the task waiting for the initialization to be completed. 212 pending_closure.Run(); 213 } 214 215 int FileStreamWriter::Write(net::IOBuffer* buffer, 216 int buffer_length, 217 const net::CompletionCallback& callback) { 218 DCHECK_CURRENTLY_ON(BrowserThread::IO); 219 TRACE_EVENT_ASYNC_BEGIN1("file_system_provider", 220 "FileStreamWriter::Write", 221 this, 222 "buffer_length", 223 buffer_length); 224 225 switch (state_) { 226 case NOT_INITIALIZED: 227 // Lazily initialize with the first call to Write(). 228 Initialize(base::Bind(&FileStreamWriter::WriteAfterInitialized, 229 weak_ptr_factory_.GetWeakPtr(), 230 make_scoped_refptr(buffer), 231 buffer_length, 232 base::Bind(&FileStreamWriter::OnWriteCompleted, 233 weak_ptr_factory_.GetWeakPtr(), 234 callback)), 235 base::Bind(&FileStreamWriter::OnWriteCompleted, 236 weak_ptr_factory_.GetWeakPtr(), 237 callback)); 238 break; 239 240 case INITIALIZING: 241 NOTREACHED(); 242 break; 243 244 case INITIALIZED: 245 WriteAfterInitialized(buffer, 246 buffer_length, 247 base::Bind(&FileStreamWriter::OnWriteCompleted, 248 weak_ptr_factory_.GetWeakPtr(), 249 callback)); 250 break; 251 252 case FAILED: 253 NOTREACHED(); 254 break; 255 } 256 257 return net::ERR_IO_PENDING; 258 } 259 260 int FileStreamWriter::Cancel(const net::CompletionCallback& callback) { 261 DCHECK_CURRENTLY_ON(BrowserThread::IO); 262 263 BrowserThread::PostTask( 264 BrowserThread::UI, 265 FROM_HERE, 266 base::Bind(&OperationRunner::AbortOnUIThread, 267 runner_, 268 base::Bind(&FileStreamWriter::OnAbortCompleted, 269 weak_ptr_factory_.GetWeakPtr(), 270 callback))); 271 return net::ERR_IO_PENDING; 272 } 273 274 int FileStreamWriter::Flush(const net::CompletionCallback& callback) { 275 if (state_ != INITIALIZED) 276 return net::ERR_FAILED; 277 278 return net::OK; 279 } 280 281 void FileStreamWriter::OnWriteFileCompleted( 282 int buffer_length, 283 const net::CompletionCallback& callback, 284 base::File::Error result) { 285 DCHECK_CURRENTLY_ON(BrowserThread::IO); 286 DCHECK_EQ(INITIALIZED, state_); 287 288 if (result != base::File::FILE_OK) { 289 state_ = FAILED; 290 callback.Run(net::FileErrorToNetError(result)); 291 return; 292 } 293 294 current_offset_ += buffer_length; 295 callback.Run(buffer_length); 296 } 297 298 void FileStreamWriter::OnWriteCompleted(net::CompletionCallback callback, 299 int result) { 300 DCHECK_CURRENTLY_ON(BrowserThread::IO); 301 callback.Run(result); 302 TRACE_EVENT_ASYNC_END0( 303 "file_system_provider", "FileStreamWriter::Write", this); 304 } 305 306 void FileStreamWriter::OnAbortCompleted(const net::CompletionCallback& callback, 307 base::File::Error result) { 308 DCHECK_CURRENTLY_ON(BrowserThread::IO); 309 310 if (result != base::File::FILE_OK) 311 state_ = FAILED; 312 313 callback.Run(net::FileErrorToNetError(result)); 314 } 315 316 void FileStreamWriter::WriteAfterInitialized( 317 scoped_refptr<net::IOBuffer> buffer, 318 int buffer_length, 319 const net::CompletionCallback& callback) { 320 DCHECK_CURRENTLY_ON(BrowserThread::IO); 321 DCHECK_EQ(INITIALIZED, state_); 322 323 BrowserThread::PostTask( 324 BrowserThread::UI, 325 FROM_HERE, 326 base::Bind(&OperationRunner::WriteFileOnUIThread, 327 runner_, 328 buffer, 329 current_offset_, 330 buffer_length, 331 base::Bind(&FileStreamWriter::OnWriteFileCompleted, 332 weak_ptr_factory_.GetWeakPtr(), 333 buffer_length, 334 callback))); 335 } 336 337 } // namespace file_system_provider 338 } // namespace chromeos 339