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 "content/renderer/pepper/quota_file_io.h" 6 7#include <algorithm> 8 9#include "base/bind.h" 10#include "base/location.h" 11#include "base/memory/weak_ptr.h" 12#include "base/message_loop/message_loop_proxy.h" 13#include "base/stl_util.h" 14#include "base/task_runner_util.h" 15#include "content/renderer/pepper/host_globals.h" 16 17using base::PlatformFile; 18using base::PlatformFileError; 19using quota::StorageType; 20 21namespace content { 22 23namespace { 24StorageType PPFileSystemTypeToQuotaStorageType(PP_FileSystemType type) { 25 switch (type) { 26 case PP_FILESYSTEMTYPE_LOCALPERSISTENT: 27 return quota::kStorageTypePersistent; 28 case PP_FILESYSTEMTYPE_LOCALTEMPORARY: 29 return quota::kStorageTypeTemporary; 30 default: 31 return quota::kStorageTypeUnknown; 32 } 33 NOTREACHED(); 34 return quota::kStorageTypeUnknown; 35} 36 37int WriteAdapter(PlatformFile file, int64 offset, 38 scoped_ptr<char[]> data, int size) { 39 return base::WritePlatformFile(file, offset, data.get(), size); 40} 41 42} // namespace 43 44class QuotaFileIO::PendingOperationBase { 45 public: 46 virtual ~PendingOperationBase() {} 47 48 // Either one of Run() or DidFail() is called (the latter is called when 49 // there was more than one error during quota queries). 50 virtual void Run() = 0; 51 virtual void DidFail(PlatformFileError error) = 0; 52 53 protected: 54 PendingOperationBase(QuotaFileIO* quota_io, bool is_will_operation) 55 : quota_io_(quota_io), is_will_operation_(is_will_operation) { 56 DCHECK(quota_io_); 57 quota_io_->WillUpdate(); 58 } 59 60 QuotaFileIO* quota_io_; 61 const bool is_will_operation_; 62}; 63 64class QuotaFileIO::WriteOperation : public PendingOperationBase { 65 public: 66 WriteOperation(QuotaFileIO* quota_io, 67 bool is_will_operation, 68 int64_t offset, 69 const char* buffer, 70 int32_t bytes_to_write, 71 const WriteCallback& callback) 72 : PendingOperationBase(quota_io, is_will_operation), 73 offset_(offset), 74 bytes_to_write_(bytes_to_write), 75 callback_(callback), 76 finished_(false), 77 status_(base::PLATFORM_FILE_OK), 78 bytes_written_(0), 79 weak_factory_(this) { 80 if (!is_will_operation) { 81 // TODO(kinuko): Check the API convention if we really need to keep a copy 82 // of the buffer during the async write operations. 83 buffer_.reset(new char[bytes_to_write]); 84 memcpy(buffer_.get(), buffer, bytes_to_write); 85 } 86 } 87 virtual ~WriteOperation() {} 88 virtual void Run() OVERRIDE { 89 DCHECK(quota_io_); 90 if (quota_io_->CheckIfExceedsQuota(offset_ + bytes_to_write_)) { 91 DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE); 92 return; 93 } 94 if (is_will_operation_) { 95 // Assuming the write will succeed. 96 DidFinish(base::PLATFORM_FILE_OK, bytes_to_write_); 97 return; 98 } 99 DCHECK(buffer_.get()); 100 101 if (!base::PostTaskAndReplyWithResult( 102 quota_io_->delegate()->GetFileThreadMessageLoopProxy().get(), 103 FROM_HERE, 104 base::Bind(&WriteAdapter, 105 quota_io_->file_, 106 offset_, 107 base::Passed(&buffer_), 108 bytes_to_write_), 109 base::Bind(&WriteOperation::DidWrite, 110 weak_factory_.GetWeakPtr()))) { 111 DidFail(base::PLATFORM_FILE_ERROR_FAILED); 112 return; 113 } 114 } 115 116 virtual void DidFail(PlatformFileError error) OVERRIDE { 117 DidFinish(error, 0); 118 } 119 120 bool finished() const { return finished_; } 121 122 virtual void WillRunCallback() { 123 base::MessageLoopProxy::current()->PostTask( 124 FROM_HERE, 125 base::Bind(&WriteOperation::RunCallback, weak_factory_.GetWeakPtr())); 126 } 127 128 private: 129 void DidWrite(int bytes_written) { 130 base::PlatformFileError error = bytes_written > 0 ? 131 base::PLATFORM_FILE_OK : base::PLATFORM_FILE_ERROR_FAILED; 132 DidFinish(error, bytes_written); 133 } 134 135 void DidFinish(PlatformFileError status, int bytes_written) { 136 finished_ = true; 137 status_ = status; 138 bytes_written_ = bytes_written; 139 int64_t max_offset = 140 (status != base::PLATFORM_FILE_OK) ? 0 : offset_ + bytes_written; 141 // This may delete itself by calling RunCallback. 142 quota_io_->DidWrite(this, max_offset); 143 } 144 145 virtual void RunCallback() { 146 DCHECK_EQ(false, callback_.is_null()); 147 callback_.Run(status_, bytes_written_); 148 delete this; 149 } 150 151 const int64_t offset_; 152 scoped_ptr<char[]> buffer_; 153 const int32_t bytes_to_write_; 154 WriteCallback callback_; 155 bool finished_; 156 PlatformFileError status_; 157 int64_t bytes_written_; 158 base::WeakPtrFactory<WriteOperation> weak_factory_; 159}; 160 161class QuotaFileIO::SetLengthOperation : public PendingOperationBase { 162 public: 163 SetLengthOperation(QuotaFileIO* quota_io, 164 bool is_will_operation, 165 int64_t length, 166 const StatusCallback& callback) 167 : PendingOperationBase(quota_io, is_will_operation), 168 length_(length), 169 callback_(callback), 170 weak_factory_(this) {} 171 172 virtual ~SetLengthOperation() {} 173 174 virtual void Run() OVERRIDE { 175 DCHECK(quota_io_); 176 if (quota_io_->CheckIfExceedsQuota(length_)) { 177 DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE); 178 return; 179 } 180 if (is_will_operation_) { 181 DidFinish(base::PLATFORM_FILE_OK); 182 return; 183 } 184 185 if (!base::FileUtilProxy::Truncate( 186 quota_io_->delegate()->GetFileThreadMessageLoopProxy().get(), 187 quota_io_->file_, 188 length_, 189 base::Bind(&SetLengthOperation::DidFinish, 190 weak_factory_.GetWeakPtr()))) { 191 DidFail(base::PLATFORM_FILE_ERROR_FAILED); 192 return; 193 } 194 } 195 196 virtual void DidFail(PlatformFileError error) OVERRIDE { 197 DidFinish(error); 198 } 199 200 private: 201 void DidFinish(PlatformFileError status) { 202 quota_io_->DidSetLength(status, length_); 203 DCHECK_EQ(false, callback_.is_null()); 204 callback_.Run(status); 205 delete this; 206 } 207 208 int64_t length_; 209 StatusCallback callback_; 210 base::WeakPtrFactory<SetLengthOperation> weak_factory_; 211}; 212 213// QuotaFileIO -------------------------------------------------------------- 214 215QuotaFileIO::QuotaFileIO( 216 Delegate* delegate, 217 PlatformFile file, 218 const GURL& file_url, 219 PP_FileSystemType type) 220 : delegate_(delegate), 221 file_(file), 222 file_url_(file_url), 223 storage_type_(PPFileSystemTypeToQuotaStorageType(type)), 224 cached_file_size_(0), 225 cached_available_space_(0), 226 outstanding_quota_queries_(0), 227 outstanding_errors_(0), 228 max_written_offset_(0), 229 inflight_operations_(0), 230 weak_factory_(this) { 231 DCHECK_NE(base::kInvalidPlatformFileValue, file_); 232 DCHECK_NE(quota::kStorageTypeUnknown, storage_type_); 233} 234 235QuotaFileIO::~QuotaFileIO() { 236 // Note that this doesn't dispatch pending callbacks. 237 STLDeleteContainerPointers(pending_operations_.begin(), 238 pending_operations_.end()); 239 STLDeleteContainerPointers(pending_callbacks_.begin(), 240 pending_callbacks_.end()); 241} 242 243bool QuotaFileIO::Write( 244 int64_t offset, const char* buffer, int32_t bytes_to_write, 245 const WriteCallback& callback) { 246 if (bytes_to_write <= 0) 247 return false; 248 249 WriteOperation* op = new WriteOperation( 250 this, false, offset, buffer, bytes_to_write, callback); 251 return RegisterOperationForQuotaChecks(op); 252} 253 254bool QuotaFileIO::SetLength(int64_t length, const StatusCallback& callback) { 255 DCHECK(pending_operations_.empty()); 256 SetLengthOperation* op = new SetLengthOperation( 257 this, false, length, callback); 258 return RegisterOperationForQuotaChecks(op); 259} 260 261bool QuotaFileIO::WillWrite( 262 int64_t offset, int32_t bytes_to_write, const WriteCallback& callback) { 263 WriteOperation* op = new WriteOperation( 264 this, true, offset, NULL, bytes_to_write, callback); 265 return RegisterOperationForQuotaChecks(op); 266} 267 268bool QuotaFileIO::WillSetLength(int64_t length, 269 const StatusCallback& callback) { 270 DCHECK(pending_operations_.empty()); 271 SetLengthOperation* op = new SetLengthOperation(this, true, length, callback); 272 return RegisterOperationForQuotaChecks(op); 273} 274 275bool QuotaFileIO::RegisterOperationForQuotaChecks( 276 PendingOperationBase* op_ptr) { 277 scoped_ptr<PendingOperationBase> op(op_ptr); 278 if (pending_operations_.empty()) { 279 // This is the first pending quota check. Run querying the file size 280 // and available space. 281 outstanding_quota_queries_ = 0; 282 outstanding_errors_ = 0; 283 284 // Query the file size. 285 ++outstanding_quota_queries_; 286 if (!base::FileUtilProxy::GetFileInfoFromPlatformFile( 287 delegate_->GetFileThreadMessageLoopProxy().get(), 288 file_, 289 base::Bind(&QuotaFileIO::DidQueryInfoForQuota, 290 weak_factory_.GetWeakPtr()))) { 291 // This makes the call fail synchronously; we do not fire the callback 292 // here but just delete the operation and return false. 293 return false; 294 } 295 296 // Query the current available space. 297 ++outstanding_quota_queries_; 298 delegate_->QueryAvailableSpace( 299 file_url_.GetOrigin(), storage_type_, 300 base::Bind(&QuotaFileIO::DidQueryAvailableSpace, 301 weak_factory_.GetWeakPtr())); 302 } 303 pending_operations_.push_back(op.release()); 304 return true; 305} 306 307void QuotaFileIO::DidQueryInfoForQuota( 308 base::PlatformFileError error_code, 309 const base::PlatformFileInfo& file_info) { 310 if (error_code != base::PLATFORM_FILE_OK) 311 ++outstanding_errors_; 312 cached_file_size_ = file_info.size; 313 DCHECK_GT(outstanding_quota_queries_, 0); 314 if (--outstanding_quota_queries_ == 0) 315 DidQueryForQuotaCheck(); 316} 317 318void QuotaFileIO::DidQueryAvailableSpace(int64_t avail_space) { 319 cached_available_space_ = avail_space; 320 DCHECK_GT(outstanding_quota_queries_, 0); 321 if (--outstanding_quota_queries_ == 0) 322 DidQueryForQuotaCheck(); 323} 324 325void QuotaFileIO::DidQueryForQuotaCheck() { 326 DCHECK(!pending_operations_.empty()); 327 DCHECK_GT(inflight_operations_, 0); 328 while (!pending_operations_.empty()) { 329 PendingOperationBase* op = pending_operations_.front(); 330 pending_operations_.pop_front(); 331 pending_callbacks_.push_back(op); 332 if (outstanding_errors_ > 0) { 333 op->DidFail(base::PLATFORM_FILE_ERROR_FAILED); 334 continue; 335 } 336 op->Run(); 337 } 338} 339 340bool QuotaFileIO::CheckIfExceedsQuota(int64_t new_file_size) const { 341 DCHECK_GE(cached_file_size_, 0); 342 DCHECK_GE(cached_available_space_, 0); 343 return new_file_size - cached_file_size_ > cached_available_space_; 344} 345 346void QuotaFileIO::WillUpdate() { 347 if (inflight_operations_++ == 0) { 348 delegate_->WillUpdateFile(file_url_); 349 DCHECK_EQ(0, max_written_offset_); 350 } 351} 352 353void QuotaFileIO::DidWrite(WriteOperation* op, 354 int64_t written_offset_end) { 355 max_written_offset_ = std::max(max_written_offset_, written_offset_end); 356 DCHECK_GT(inflight_operations_, 0); 357 DCHECK(!pending_callbacks_.empty()); 358 // Fire callbacks for finished operations. 359 while (!pending_callbacks_.empty()) { 360 WriteOperation* op = static_cast<WriteOperation*>( 361 pending_callbacks_.front()); 362 if (!op->finished()) 363 break; 364 pending_callbacks_.pop_front(); 365 op->WillRunCallback(); 366 } 367 // If we have no more pending writes, notify the browser that we did 368 // update the file. 369 if (--inflight_operations_ == 0) { 370 DCHECK(pending_operations_.empty()); 371 int64_t growth = max_written_offset_ - cached_file_size_; 372 growth = growth < 0 ? 0 : growth; 373 374 delegate_->DidUpdateFile(file_url_, growth); 375 max_written_offset_ = 0; 376 } 377} 378 379void QuotaFileIO::DidSetLength(PlatformFileError error, int64_t new_file_size) { 380 DCHECK_EQ(1, inflight_operations_); 381 pending_callbacks_.pop_front(); 382 DCHECK(pending_callbacks_.empty()); 383 int64_t delta = (error != base::PLATFORM_FILE_OK) ? 0 : 384 new_file_size - cached_file_size_; 385 386 delegate_->DidUpdateFile(file_url_, delta); 387 inflight_operations_ = 0; 388} 389 390} // namespace content 391