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/fileapi/file_observers.h"
12 #include "storage/browser/fileapi/file_stream_reader.h"
13 #include "storage/browser/fileapi/file_system_context.h"
14 #include "storage/browser/fileapi/file_system_operation_runner.h"
15 #include "storage/browser/quota/quota_manager_proxy.h"
16 #include "storage/common/fileapi/file_system_util.h"
22 // Adjust the |quota| value in overwriting case (i.e. |file_size| > 0 and
23 // |file_offset| < |file_size|) to make the remaining quota calculation easier.
24 // Specifically this widens the quota for overlapping range (so that we can
25 // simply compare written bytes against the adjusted quota).
26 int64
AdjustQuotaForOverlap(int64 quota
,
29 DCHECK_LE(file_offset
, file_size
);
32 int64 overlap
= file_size
- file_offset
;
33 if (kint64max
- overlap
> quota
)
40 SandboxFileStreamWriter::SandboxFileStreamWriter(
41 FileSystemContext
* file_system_context
,
42 const FileSystemURL
& url
,
44 const UpdateObserverList
& observers
)
45 : file_system_context_(file_system_context
),
47 initial_offset_(initial_offset
),
48 observers_(observers
),
50 total_bytes_written_(0),
51 allowed_bytes_to_write_(0),
52 has_pending_operation_(false),
53 default_quota_(kint64max
),
55 DCHECK(url_
.is_valid());
58 SandboxFileStreamWriter::~SandboxFileStreamWriter() {}
60 int SandboxFileStreamWriter::Write(
61 net::IOBuffer
* buf
, int buf_len
,
62 const net::CompletionCallback
& callback
) {
63 has_pending_operation_
= true;
64 if (local_file_writer_
)
65 return WriteInternal(buf
, buf_len
, callback
);
67 net::CompletionCallback write_task
=
68 base::Bind(&SandboxFileStreamWriter::DidInitializeForWrite
,
69 weak_factory_
.GetWeakPtr(),
70 make_scoped_refptr(buf
), buf_len
, callback
);
71 file_system_context_
->operation_runner()->CreateSnapshotFile(
72 url_
, base::Bind(&SandboxFileStreamWriter::DidCreateSnapshotFile
,
73 weak_factory_
.GetWeakPtr(), write_task
));
74 return net::ERR_IO_PENDING
;
77 int SandboxFileStreamWriter::Cancel(const net::CompletionCallback
& callback
) {
78 if (!has_pending_operation_
)
79 return net::ERR_UNEXPECTED
;
81 DCHECK(!callback
.is_null());
82 cancel_callback_
= callback
;
83 return net::ERR_IO_PENDING
;
86 int SandboxFileStreamWriter::WriteInternal(
87 net::IOBuffer
* buf
, int buf_len
,
88 const net::CompletionCallback
& callback
) {
89 // allowed_bytes_to_write could be negative if the file size is
90 // greater than the current (possibly new) quota.
91 DCHECK(total_bytes_written_
<= allowed_bytes_to_write_
||
92 allowed_bytes_to_write_
< 0);
93 if (total_bytes_written_
>= allowed_bytes_to_write_
) {
94 has_pending_operation_
= false;
95 return net::ERR_FILE_NO_SPACE
;
98 if (buf_len
> allowed_bytes_to_write_
- total_bytes_written_
)
99 buf_len
= allowed_bytes_to_write_
- total_bytes_written_
;
101 DCHECK(local_file_writer_
.get());
102 const int result
= local_file_writer_
->Write(
104 base::Bind(&SandboxFileStreamWriter::DidWrite
, weak_factory_
.GetWeakPtr(),
106 if (result
!= net::ERR_IO_PENDING
)
107 has_pending_operation_
= false;
111 void SandboxFileStreamWriter::DidCreateSnapshotFile(
112 const net::CompletionCallback
& callback
,
113 base::File::Error file_error
,
114 const base::File::Info
& file_info
,
115 const base::FilePath
& platform_path
,
116 const scoped_refptr
<storage::ShareableFileReference
>& file_ref
) {
117 DCHECK(!file_ref
.get());
119 if (CancelIfRequested())
121 if (file_error
!= base::File::FILE_OK
) {
122 callback
.Run(net::FileErrorToNetError(file_error
));
125 if (file_info
.is_directory
) {
126 // We should not be writing to a directory.
127 callback
.Run(net::ERR_ACCESS_DENIED
);
130 file_size_
= file_info
.size
;
131 if (initial_offset_
> file_size_
) {
132 LOG(ERROR
) << initial_offset_
<< ", " << file_size_
;
133 // This shouldn't happen as long as we check offset in the renderer.
135 initial_offset_
= file_size_
;
137 DCHECK(!local_file_writer_
.get());
138 local_file_writer_
.reset(FileStreamWriter::CreateForLocalFile(
139 file_system_context_
->default_file_task_runner(),
142 FileStreamWriter::OPEN_EXISTING_FILE
));
144 storage::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
);
154 DCHECK(quota_manager_proxy
->quota_manager());
155 quota_manager_proxy
->quota_manager()->GetUsageAndQuota(
157 FileSystemTypeToQuotaStorageType(url_
.type()),
158 base::Bind(&SandboxFileStreamWriter::DidGetUsageAndQuota
,
159 weak_factory_
.GetWeakPtr(), callback
));
162 void SandboxFileStreamWriter::DidGetUsageAndQuota(
163 const net::CompletionCallback
& callback
,
164 storage::QuotaStatusCode status
,
167 if (CancelIfRequested())
169 if (status
!= storage::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 base::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 storage