• 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 "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