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