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.
5 #include "webkit/browser/fileapi/sandbox_file_stream_writer.h"
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"
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
,
30 DCHECK_LE(file_offset
, file_size
);
33 int64 overlap
= file_size
- file_offset
;
34 if (kint64max
- overlap
> quota
)
41 SandboxFileStreamWriter::SandboxFileStreamWriter(
42 FileSystemContext
* file_system_context
,
43 const FileSystemURL
& url
,
45 const UpdateObserverList
& observers
)
46 : file_system_context_(file_system_context
),
48 initial_offset_(initial_offset
),
49 observers_(observers
),
51 total_bytes_written_(0),
52 allowed_bytes_to_write_(0),
53 has_pending_operation_(false),
54 default_quota_(kint64max
),
56 DCHECK(url_
.is_valid());
59 SandboxFileStreamWriter::~SandboxFileStreamWriter() {}
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
);
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
;
78 int SandboxFileStreamWriter::Cancel(const net::CompletionCallback
& callback
) {
79 if (!has_pending_operation_
)
80 return net::ERR_UNEXPECTED
;
82 DCHECK(!callback
.is_null());
83 cancel_callback_
= callback
;
84 return net::ERR_IO_PENDING
;
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
;
99 if (buf_len
> allowed_bytes_to_write_
- total_bytes_written_
)
100 buf_len
= allowed_bytes_to_write_
- total_bytes_written_
;
102 DCHECK(local_file_writer_
.get());
103 const int result
= local_file_writer_
->Write(
105 base::Bind(&SandboxFileStreamWriter::DidWrite
, weak_factory_
.GetWeakPtr(),
107 if (result
!= net::ERR_IO_PENDING
)
108 has_pending_operation_
= false;
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());
120 if (CancelIfRequested())
122 if (file_error
!= base::File::FILE_OK
) {
123 callback
.Run(net::FileErrorToNetError(file_error
));
126 if (file_info
.is_directory
) {
127 // We should not be writing to a directory.
128 callback
.Run(net::ERR_ACCESS_DENIED
);
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.
136 initial_offset_
= file_size_
;
138 DCHECK(!local_file_writer_
.get());
139 local_file_writer_
.reset(FileStreamWriter::CreateForLocalFile(
140 file_system_context_
->default_file_task_runner(),
143 FileStreamWriter::OPEN_EXISTING_FILE
));
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
);
155 DCHECK(quota_manager_proxy
->quota_manager());
156 quota_manager_proxy
->quota_manager()->GetUsageAndQuota(
158 FileSystemTypeToQuotaStorageType(url_
.type()),
159 base::Bind(&SandboxFileStreamWriter::DidGetUsageAndQuota
,
160 weak_factory_
.GetWeakPtr(), callback
));
163 void SandboxFileStreamWriter::DidGetUsageAndQuota(
164 const net::CompletionCallback
& callback
,
165 quota::QuotaStatusCode status
,
166 int64 usage
, int64 quota
) {
167 if (CancelIfRequested())
169 if (status
!= quota::kQuotaStatusOk
) {
170 LOG(WARNING
) << "Got unexpected quota error : " << status
;
171 callback
.Run(net::ERR_FAILED
);
175 allowed_bytes_to_write_
= quota
- usage
;
176 callback
.Run(net::OK
);
179 void SandboxFileStreamWriter::DidInitializeForWrite(
180 net::IOBuffer
* buf
, int buf_len
,
181 const net::CompletionCallback
& callback
,
183 if (CancelIfRequested())
185 if (init_status
!= net::OK
) {
186 has_pending_operation_
= false;
187 callback
.Run(init_status
);
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
);
197 void SandboxFileStreamWriter::DidWrite(
198 const net::CompletionCallback
& callback
,
199 int write_response
) {
200 DCHECK(has_pending_operation_
);
201 has_pending_operation_
= false;
203 if (write_response
<= 0) {
204 if (CancelIfRequested())
206 callback
.Run(write_response
);
210 if (total_bytes_written_
+ write_response
+ initial_offset_
> file_size_
) {
211 int overlapped
= file_size_
- total_bytes_written_
- initial_offset_
;
214 observers_
.Notify(&FileUpdateObserver::OnUpdate
,
215 MakeTuple(url_
, write_response
- overlapped
));
217 total_bytes_written_
+= write_response
;
219 if (CancelIfRequested())
221 callback
.Run(write_response
);
224 bool SandboxFileStreamWriter::CancelIfRequested() {
225 if (cancel_callback_
.is_null())
228 net::CompletionCallback pending_cancel
= cancel_callback_
;
229 has_pending_operation_
= false;
230 cancel_callback_
.Reset();
231 pending_cancel
.Run(net::OK
);
235 int SandboxFileStreamWriter::Flush(const net::CompletionCallback
& callback
) {
236 DCHECK(!has_pending_operation_
);
237 DCHECK(cancel_callback_
.is_null());
239 // Write() is not called yet, so there's nothing to flush.
240 if (!local_file_writer_
)
243 return local_file_writer_
->Flush(callback
);
246 } // namespace fileapi