• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/file_stream_reader.h"
13 #include "webkit/browser/fileapi/file_observers.h"
14 #include "webkit/browser/fileapi/file_stream_writer.h"
15 #include "webkit/browser/fileapi/file_system_context.h"
16 #include "webkit/browser/fileapi/file_system_operation_runner.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).
AdjustQuotaForOverlap(int64 quota,int64 file_offset,int64 file_size)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 
SandboxFileStreamWriter(FileSystemContext * file_system_context,const FileSystemURL & url,int64 initial_offset,const UpdateObserverList & observers)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 
~SandboxFileStreamWriter()60 SandboxFileStreamWriter::~SandboxFileStreamWriter() {}
61 
Write(net::IOBuffer * buf,int buf_len,const net::CompletionCallback & callback)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 
Cancel(const net::CompletionCallback & callback)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 
WriteInternal(net::IOBuffer * buf,int buf_len,const net::CompletionCallback & callback)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 
DidCreateSnapshotFile(const net::CompletionCallback & callback,base::PlatformFileError file_error,const base::PlatformFileInfo & file_info,const base::FilePath & platform_path,const scoped_refptr<webkit_blob::ShareableFileReference> & file_ref)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(FileStreamWriter::CreateForLocalFile(
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 
DidGetUsageAndQuota(const net::CompletionCallback & callback,quota::QuotaStatusCode status,int64 usage,int64 quota)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 
DidInitializeForWrite(net::IOBuffer * buf,int buf_len,const net::CompletionCallback & callback,int init_status)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 
DidWrite(const net::CompletionCallback & callback,int write_response)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 
CancelIfRequested()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 
Flush(const net::CompletionCallback & callback)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