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 "storage/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 "storage/browser/blob/file_stream_reader.h"
12 #include "storage/browser/fileapi/file_observers.h"
13 #include "storage/browser/fileapi/file_stream_writer.h"
14 #include "storage/browser/fileapi/file_system_context.h"
15 #include "storage/browser/fileapi/file_system_operation_runner.h"
16 #include "storage/browser/quota/quota_manager_proxy.h"
17 #include "storage/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
<storage::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 storage::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 storage::QuotaStatusCode status
,
168 if (CancelIfRequested())
170 if (status
!= storage::kQuotaStatusOk
) {
171 LOG(WARNING
) << "Got unexpected quota error : " << status
;
172 callback
.Run(net::ERR_FAILED
);
176 allowed_bytes_to_write_
= quota
- usage
;
177 callback
.Run(net::OK
);
180 void SandboxFileStreamWriter::DidInitializeForWrite(
181 net::IOBuffer
* buf
, int buf_len
,
182 const net::CompletionCallback
& callback
,
184 if (CancelIfRequested())
186 if (init_status
!= net::OK
) {
187 has_pending_operation_
= false;
188 callback
.Run(init_status
);
191 allowed_bytes_to_write_
= AdjustQuotaForOverlap(
192 allowed_bytes_to_write_
, initial_offset_
, file_size_
);
193 const int result
= WriteInternal(buf
, buf_len
, callback
);
194 if (result
!= net::ERR_IO_PENDING
)
195 callback
.Run(result
);
198 void SandboxFileStreamWriter::DidWrite(
199 const net::CompletionCallback
& callback
,
200 int write_response
) {
201 DCHECK(has_pending_operation_
);
202 has_pending_operation_
= false;
204 if (write_response
<= 0) {
205 if (CancelIfRequested())
207 callback
.Run(write_response
);
211 if (total_bytes_written_
+ write_response
+ initial_offset_
> file_size_
) {
212 int overlapped
= file_size_
- total_bytes_written_
- initial_offset_
;
215 observers_
.Notify(&FileUpdateObserver::OnUpdate
,
216 MakeTuple(url_
, write_response
- overlapped
));
218 total_bytes_written_
+= write_response
;
220 if (CancelIfRequested())
222 callback
.Run(write_response
);
225 bool SandboxFileStreamWriter::CancelIfRequested() {
226 if (cancel_callback_
.is_null())
229 net::CompletionCallback pending_cancel
= cancel_callback_
;
230 has_pending_operation_
= false;
231 cancel_callback_
.Reset();
232 pending_cancel
.Run(net::OK
);
236 int SandboxFileStreamWriter::Flush(const net::CompletionCallback
& callback
) {
237 DCHECK(!has_pending_operation_
);
238 DCHECK(cancel_callback_
.is_null());
240 // Write() is not called yet, so there's nothing to flush.
241 if (!local_file_writer_
)
244 return local_file_writer_
->Flush(callback
);
247 } // namespace storage