Home | History | Annotate | Download | only in fileapi
      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 "webkit/browser/fileapi/sandbox_file_stream_writer.h"
      6 
      7 #include "base/files/file_util_proxy.h"
      8 #include "base/platform_file.h"
      9 #include "base/sequenced_task_runner.h"
     10 #include "net/base/io_buffer.h"
     11 #include "net/base/net_errors.h"
     12 #include "webkit/browser/blob/local_file_stream_reader.h"
     13 #include "webkit/browser/fileapi/file_observers.h"
     14 #include "webkit/browser/fileapi/file_system_context.h"
     15 #include "webkit/browser/fileapi/file_system_operation_runner.h"
     16 #include "webkit/browser/fileapi/local_file_stream_writer.h"
     17 #include "webkit/browser/quota/quota_manager.h"
     18 #include "webkit/common/fileapi/file_system_util.h"
     19 
     20 namespace fileapi {
     21 
     22 namespace {
     23 
     24 // Adjust the |quota| value in overwriting case (i.e. |file_size| > 0 and
     25 // |file_offset| < |file_size|) to make the remaining quota calculation easier.
     26 // Specifically this widens the quota for overlapping range (so that we can
     27 // simply compare written bytes against the adjusted quota).
     28 int64 AdjustQuotaForOverlap(int64 quota,
     29                             int64 file_offset,
     30                             int64 file_size) {
     31   DCHECK_LE(file_offset, file_size);
     32   if (quota < 0)
     33     quota = 0;
     34   int64 overlap = file_size - file_offset;
     35   if (kint64max - overlap > quota)
     36     quota += overlap;
     37   return quota;
     38 }
     39 
     40 }  // namespace
     41 
     42 SandboxFileStreamWriter::SandboxFileStreamWriter(
     43     FileSystemContext* file_system_context,
     44     const FileSystemURL& url,
     45     int64 initial_offset,
     46     const UpdateObserverList& observers)
     47     : file_system_context_(file_system_context),
     48       url_(url),
     49       initial_offset_(initial_offset),
     50       observers_(observers),
     51       file_size_(0),
     52       total_bytes_written_(0),
     53       allowed_bytes_to_write_(0),
     54       has_pending_operation_(false),
     55       default_quota_(kint64max),
     56       weak_factory_(this) {
     57   DCHECK(url_.is_valid());
     58 }
     59 
     60 SandboxFileStreamWriter::~SandboxFileStreamWriter() {}
     61 
     62 int SandboxFileStreamWriter::Write(
     63     net::IOBuffer* buf, int buf_len,
     64     const net::CompletionCallback& callback) {
     65   has_pending_operation_ = true;
     66   if (local_file_writer_)
     67     return WriteInternal(buf, buf_len, callback);
     68 
     69   net::CompletionCallback write_task =
     70       base::Bind(&SandboxFileStreamWriter::DidInitializeForWrite,
     71                  weak_factory_.GetWeakPtr(),
     72                  make_scoped_refptr(buf), buf_len, callback);
     73   file_system_context_->operation_runner()->CreateSnapshotFile(
     74       url_, base::Bind(&SandboxFileStreamWriter::DidCreateSnapshotFile,
     75                        weak_factory_.GetWeakPtr(), write_task));
     76   return net::ERR_IO_PENDING;
     77 }
     78 
     79 int SandboxFileStreamWriter::Cancel(const net::CompletionCallback& callback) {
     80   if (!has_pending_operation_)
     81     return net::ERR_UNEXPECTED;
     82 
     83   DCHECK(!callback.is_null());
     84   cancel_callback_ = callback;
     85   return net::ERR_IO_PENDING;
     86 }
     87 
     88 int SandboxFileStreamWriter::WriteInternal(
     89     net::IOBuffer* buf, int buf_len,
     90     const net::CompletionCallback& callback) {
     91   // allowed_bytes_to_write could be negative if the file size is
     92   // greater than the current (possibly new) quota.
     93   DCHECK(total_bytes_written_ <= allowed_bytes_to_write_ ||
     94          allowed_bytes_to_write_ < 0);
     95   if (total_bytes_written_ >= allowed_bytes_to_write_) {
     96     has_pending_operation_ = false;
     97     return net::ERR_FILE_NO_SPACE;
     98   }
     99 
    100   if (buf_len > allowed_bytes_to_write_ - total_bytes_written_)
    101     buf_len = allowed_bytes_to_write_ - total_bytes_written_;
    102 
    103   DCHECK(local_file_writer_.get());
    104   const int result = local_file_writer_->Write(
    105       buf, buf_len,
    106       base::Bind(&SandboxFileStreamWriter::DidWrite, weak_factory_.GetWeakPtr(),
    107                  callback));
    108   if (result != net::ERR_IO_PENDING)
    109     has_pending_operation_ = false;
    110   return result;
    111 }
    112 
    113 void SandboxFileStreamWriter::DidCreateSnapshotFile(
    114     const net::CompletionCallback& callback,
    115     base::PlatformFileError file_error,
    116     const base::PlatformFileInfo& file_info,
    117     const base::FilePath& platform_path,
    118     const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
    119   DCHECK(!file_ref.get());
    120 
    121   if (CancelIfRequested())
    122     return;
    123   if (file_error != base::PLATFORM_FILE_OK) {
    124     callback.Run(net::PlatformFileErrorToNetError(file_error));
    125     return;
    126   }
    127   if (file_info.is_directory) {
    128     // We should not be writing to a directory.
    129     callback.Run(net::ERR_ACCESS_DENIED);
    130     return;
    131   }
    132   file_size_ = file_info.size;
    133   if (initial_offset_ > file_size_) {
    134     LOG(ERROR) << initial_offset_ << ", " << file_size_;
    135     // This shouldn't happen as long as we check offset in the renderer.
    136     NOTREACHED();
    137     initial_offset_ = file_size_;
    138   }
    139   DCHECK(!local_file_writer_.get());
    140   local_file_writer_.reset(new LocalFileStreamWriter(
    141       file_system_context_->default_file_task_runner(), platform_path,
    142       initial_offset_));
    143 
    144   quota::QuotaManagerProxy* quota_manager_proxy =
    145       file_system_context_->quota_manager_proxy();
    146   if (!quota_manager_proxy) {
    147     // If we don't have the quota manager or the requested filesystem type
    148     // does not support quota, we should be able to let it go.
    149     allowed_bytes_to_write_ = default_quota_;
    150     callback.Run(net::OK);
    151     return;
    152   }
    153 
    154   DCHECK(quota_manager_proxy->quota_manager());
    155   quota_manager_proxy->quota_manager()->GetUsageAndQuota(
    156       url_.origin(),
    157       FileSystemTypeToQuotaStorageType(url_.type()),
    158       base::Bind(&SandboxFileStreamWriter::DidGetUsageAndQuota,
    159                  weak_factory_.GetWeakPtr(), callback));
    160 }
    161 
    162 void SandboxFileStreamWriter::DidGetUsageAndQuota(
    163     const net::CompletionCallback& callback,
    164     quota::QuotaStatusCode status,
    165     int64 usage, int64 quota) {
    166   if (CancelIfRequested())
    167     return;
    168   if (status != quota::kQuotaStatusOk) {
    169     LOG(WARNING) << "Got unexpected quota error : " << status;
    170     callback.Run(net::ERR_FAILED);
    171     return;
    172   }
    173 
    174   allowed_bytes_to_write_ = quota - usage;
    175   callback.Run(net::OK);
    176 }
    177 
    178 void SandboxFileStreamWriter::DidInitializeForWrite(
    179     net::IOBuffer* buf, int buf_len,
    180     const net::CompletionCallback& callback,
    181     int init_status) {
    182   if (CancelIfRequested())
    183     return;
    184   if (init_status != net::OK) {
    185     has_pending_operation_ = false;
    186     callback.Run(init_status);
    187     return;
    188   }
    189   allowed_bytes_to_write_ = AdjustQuotaForOverlap(
    190       allowed_bytes_to_write_, initial_offset_, file_size_);
    191   const int result = WriteInternal(buf, buf_len, callback);
    192   if (result != net::ERR_IO_PENDING)
    193     callback.Run(result);
    194 }
    195 
    196 void SandboxFileStreamWriter::DidWrite(
    197     const net::CompletionCallback& callback,
    198     int write_response) {
    199   DCHECK(has_pending_operation_);
    200   has_pending_operation_ = false;
    201 
    202   if (write_response <= 0) {
    203     if (CancelIfRequested())
    204       return;
    205     callback.Run(write_response);
    206     return;
    207   }
    208 
    209   if (total_bytes_written_ + write_response + initial_offset_ > file_size_) {
    210     int overlapped = file_size_ - total_bytes_written_ - initial_offset_;
    211     if (overlapped < 0)
    212       overlapped = 0;
    213     observers_.Notify(&FileUpdateObserver::OnUpdate,
    214                       MakeTuple(url_, write_response - overlapped));
    215   }
    216   total_bytes_written_ += write_response;
    217 
    218   if (CancelIfRequested())
    219     return;
    220   callback.Run(write_response);
    221 }
    222 
    223 bool SandboxFileStreamWriter::CancelIfRequested() {
    224   if (cancel_callback_.is_null())
    225     return false;
    226 
    227   net::CompletionCallback pending_cancel = cancel_callback_;
    228   has_pending_operation_ = false;
    229   cancel_callback_.Reset();
    230   pending_cancel.Run(net::OK);
    231   return true;
    232 }
    233 
    234 int SandboxFileStreamWriter::Flush(const net::CompletionCallback& callback) {
    235   DCHECK(!has_pending_operation_);
    236   DCHECK(cancel_callback_.is_null());
    237 
    238   // Write() is not called yet, so there's nothing to flush.
    239   if (!local_file_writer_)
    240     return net::OK;
    241 
    242   return local_file_writer_->Flush(callback);
    243 }
    244 
    245 }  // namespace fileapi
    246