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 "net/base/upload_file_element_reader.h"
6
7 #include "base/bind.h"
8 #include "base/file_util.h"
9 #include "base/location.h"
10 #include "base/task_runner_util.h"
11 #include "net/base/file_stream.h"
12 #include "net/base/io_buffer.h"
13 #include "net/base/net_errors.h"
14
15 namespace net {
16
17 namespace {
18
19 // In tests, this value is used to override the return value of
20 // UploadFileElementReader::GetContentLength() when set to non-zero.
21 uint64 overriding_content_length = 0;
22
23 // This function is used to implement Init().
24 template<typename FileStreamDeleter>
InitInternal(const base::FilePath & path,uint64 range_offset,uint64 range_length,const base::Time & expected_modification_time,scoped_ptr<FileStream,FileStreamDeleter> * out_file_stream,uint64 * out_content_length)25 int InitInternal(const base::FilePath& path,
26 uint64 range_offset,
27 uint64 range_length,
28 const base::Time& expected_modification_time,
29 scoped_ptr<FileStream, FileStreamDeleter>* out_file_stream,
30 uint64* out_content_length) {
31 scoped_ptr<FileStream> file_stream(new FileStream(NULL));
32 int64 rv = file_stream->OpenSync(
33 path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ);
34 if (rv != OK) {
35 // If the file can't be opened, the upload should fail.
36 DLOG(WARNING) << "Failed to open \"" << path.value()
37 << "\" for reading: " << rv;
38 return rv;
39 } else if (range_offset) {
40 rv = file_stream->SeekSync(FROM_BEGIN, range_offset);
41 if (rv < 0) {
42 DLOG(WARNING) << "Failed to seek \"" << path.value()
43 << "\" to offset: " << range_offset << " (" << rv << ")";
44 return rv;
45 }
46 }
47
48 int64 length = 0;
49 if (!base::GetFileSize(path, &length)) {
50 DLOG(WARNING) << "Failed to get file size of \"" << path.value() << "\"";
51 return ERR_FILE_NOT_FOUND;
52 }
53
54 if (range_offset < static_cast<uint64>(length)) {
55 // Compensate for the offset.
56 length = std::min(length - range_offset, range_length);
57 }
58
59 // If the underlying file has been changed and the expected file modification
60 // time is set, treat it as error. Note that the expected modification time
61 // from WebKit is based on time_t precision. So we have to convert both to
62 // time_t to compare. This check is used for sliced files.
63 if (!expected_modification_time.is_null()) {
64 base::PlatformFileInfo info;
65 if (!base::GetFileInfo(path, &info)) {
66 DLOG(WARNING) << "Failed to get file info of \"" << path.value() << "\"";
67 return ERR_FILE_NOT_FOUND;
68 }
69
70 if (expected_modification_time.ToTimeT() != info.last_modified.ToTimeT()) {
71 return ERR_UPLOAD_FILE_CHANGED;
72 }
73 }
74
75 *out_content_length = length;
76 out_file_stream->reset(file_stream.release());
77
78 return OK;
79 }
80
81 // This function is used to implement Read().
ReadInternal(scoped_refptr<IOBuffer> buf,int buf_length,uint64 bytes_remaining,FileStream * file_stream)82 int ReadInternal(scoped_refptr<IOBuffer> buf,
83 int buf_length,
84 uint64 bytes_remaining,
85 FileStream* file_stream) {
86 DCHECK_LT(0, buf_length);
87
88 const uint64 num_bytes_to_read =
89 std::min(bytes_remaining, static_cast<uint64>(buf_length));
90
91 int result = 0;
92 if (num_bytes_to_read > 0) {
93 DCHECK(file_stream); // file_stream is non-null if content_length_ > 0.
94 result = file_stream->ReadSync(buf->data(), num_bytes_to_read);
95 if (result == 0) // Reached end-of-file earlier than expected.
96 result = ERR_UPLOAD_FILE_CHANGED;
97 }
98 return result;
99 }
100
101 } // namespace
102
FileStreamDeleter(base::TaskRunner * task_runner)103 UploadFileElementReader::FileStreamDeleter::FileStreamDeleter(
104 base::TaskRunner* task_runner) : task_runner_(task_runner) {
105 DCHECK(task_runner_.get());
106 }
107
~FileStreamDeleter()108 UploadFileElementReader::FileStreamDeleter::~FileStreamDeleter() {}
109
operator ()(FileStream * file_stream) const110 void UploadFileElementReader::FileStreamDeleter::operator() (
111 FileStream* file_stream) const {
112 if (file_stream) {
113 task_runner_->PostTask(FROM_HERE,
114 base::Bind(&base::DeletePointer<FileStream>,
115 file_stream));
116 }
117 }
118
UploadFileElementReader(base::TaskRunner * task_runner,const base::FilePath & path,uint64 range_offset,uint64 range_length,const base::Time & expected_modification_time)119 UploadFileElementReader::UploadFileElementReader(
120 base::TaskRunner* task_runner,
121 const base::FilePath& path,
122 uint64 range_offset,
123 uint64 range_length,
124 const base::Time& expected_modification_time)
125 : task_runner_(task_runner),
126 path_(path),
127 range_offset_(range_offset),
128 range_length_(range_length),
129 expected_modification_time_(expected_modification_time),
130 file_stream_(NULL, FileStreamDeleter(task_runner_.get())),
131 content_length_(0),
132 bytes_remaining_(0),
133 weak_ptr_factory_(this) {
134 DCHECK(task_runner_.get());
135 }
136
~UploadFileElementReader()137 UploadFileElementReader::~UploadFileElementReader() {
138 }
139
AsFileReader() const140 const UploadFileElementReader* UploadFileElementReader::AsFileReader() const {
141 return this;
142 }
143
Init(const CompletionCallback & callback)144 int UploadFileElementReader::Init(const CompletionCallback& callback) {
145 DCHECK(!callback.is_null());
146 Reset();
147
148 ScopedFileStreamPtr* file_stream =
149 new ScopedFileStreamPtr(NULL, FileStreamDeleter(task_runner_.get()));
150 uint64* content_length = new uint64;
151 const bool posted = base::PostTaskAndReplyWithResult(
152 task_runner_.get(),
153 FROM_HERE,
154 base::Bind(&InitInternal<FileStreamDeleter>,
155 path_,
156 range_offset_,
157 range_length_,
158 expected_modification_time_,
159 file_stream,
160 content_length),
161 base::Bind(&UploadFileElementReader::OnInitCompleted,
162 weak_ptr_factory_.GetWeakPtr(),
163 base::Owned(file_stream),
164 base::Owned(content_length),
165 callback));
166 DCHECK(posted);
167 return ERR_IO_PENDING;
168 }
169
GetContentLength() const170 uint64 UploadFileElementReader::GetContentLength() const {
171 if (overriding_content_length)
172 return overriding_content_length;
173 return content_length_;
174 }
175
BytesRemaining() const176 uint64 UploadFileElementReader::BytesRemaining() const {
177 return bytes_remaining_;
178 }
179
Read(IOBuffer * buf,int buf_length,const CompletionCallback & callback)180 int UploadFileElementReader::Read(IOBuffer* buf,
181 int buf_length,
182 const CompletionCallback& callback) {
183 DCHECK(!callback.is_null());
184
185 if (BytesRemaining() == 0)
186 return 0;
187
188 // Save the value of file_stream_.get() before base::Passed() invalidates it.
189 FileStream* file_stream_ptr = file_stream_.get();
190 // Pass the ownership of file_stream_ to the worker pool to safely perform
191 // operation even when |this| is destructed before the read completes.
192 const bool posted = base::PostTaskAndReplyWithResult(
193 task_runner_.get(),
194 FROM_HERE,
195 base::Bind(&ReadInternal,
196 scoped_refptr<IOBuffer>(buf),
197 buf_length,
198 BytesRemaining(),
199 file_stream_ptr),
200 base::Bind(&UploadFileElementReader::OnReadCompleted,
201 weak_ptr_factory_.GetWeakPtr(),
202 base::Passed(&file_stream_),
203 callback));
204 DCHECK(posted);
205 return ERR_IO_PENDING;
206 }
207
Reset()208 void UploadFileElementReader::Reset() {
209 weak_ptr_factory_.InvalidateWeakPtrs();
210 bytes_remaining_ = 0;
211 content_length_ = 0;
212 file_stream_.reset();
213 }
214
OnInitCompleted(ScopedFileStreamPtr * file_stream,uint64 * content_length,const CompletionCallback & callback,int result)215 void UploadFileElementReader::OnInitCompleted(
216 ScopedFileStreamPtr* file_stream,
217 uint64* content_length,
218 const CompletionCallback& callback,
219 int result) {
220 file_stream_.swap(*file_stream);
221 content_length_ = *content_length;
222 bytes_remaining_ = GetContentLength();
223 if (!callback.is_null())
224 callback.Run(result);
225 }
226
OnReadCompleted(ScopedFileStreamPtr file_stream,const CompletionCallback & callback,int result)227 void UploadFileElementReader::OnReadCompleted(
228 ScopedFileStreamPtr file_stream,
229 const CompletionCallback& callback,
230 int result) {
231 file_stream_.swap(file_stream);
232 if (result > 0) {
233 DCHECK_GE(bytes_remaining_, static_cast<uint64>(result));
234 bytes_remaining_ -= result;
235 }
236 if (!callback.is_null())
237 callback.Run(result);
238 }
239
240 UploadFileElementReader::ScopedOverridingContentLengthForTests::
ScopedOverridingContentLengthForTests(uint64 value)241 ScopedOverridingContentLengthForTests(uint64 value) {
242 overriding_content_length = value;
243 }
244
245 UploadFileElementReader::ScopedOverridingContentLengthForTests::
~ScopedOverridingContentLengthForTests()246 ~ScopedOverridingContentLengthForTests() {
247 overriding_content_length = 0;
248 }
249
UploadFileElementReaderSync(const base::FilePath & path,uint64 range_offset,uint64 range_length,const base::Time & expected_modification_time)250 UploadFileElementReaderSync::UploadFileElementReaderSync(
251 const base::FilePath& path,
252 uint64 range_offset,
253 uint64 range_length,
254 const base::Time& expected_modification_time)
255 : path_(path),
256 range_offset_(range_offset),
257 range_length_(range_length),
258 expected_modification_time_(expected_modification_time),
259 content_length_(0),
260 bytes_remaining_(0) {
261 }
262
~UploadFileElementReaderSync()263 UploadFileElementReaderSync::~UploadFileElementReaderSync() {
264 }
265
Init(const CompletionCallback & callback)266 int UploadFileElementReaderSync::Init(const CompletionCallback& callback) {
267 bytes_remaining_ = 0;
268 content_length_ = 0;
269 file_stream_.reset();
270
271 const int result = InitInternal(path_, range_offset_, range_length_,
272 expected_modification_time_,
273 &file_stream_, &content_length_);
274 bytes_remaining_ = GetContentLength();
275 return result;
276 }
277
GetContentLength() const278 uint64 UploadFileElementReaderSync::GetContentLength() const {
279 return content_length_;
280 }
281
BytesRemaining() const282 uint64 UploadFileElementReaderSync::BytesRemaining() const {
283 return bytes_remaining_;
284 }
285
Read(IOBuffer * buf,int buf_length,const CompletionCallback & callback)286 int UploadFileElementReaderSync::Read(IOBuffer* buf,
287 int buf_length,
288 const CompletionCallback& callback) {
289 const int result = ReadInternal(buf, buf_length, BytesRemaining(),
290 file_stream_.get());
291 if (result > 0) {
292 DCHECK_GE(bytes_remaining_, static_cast<uint64>(result));
293 bytes_remaining_ -= result;
294 }
295 return result;
296 }
297
298 } // namespace net
299