• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
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 <memory>
8 
9 #include "base/files/file_util.h"
10 #include "base/functional/bind.h"
11 #include "base/location.h"
12 #include "base/task/task_runner.h"
13 #include "net/base/file_stream.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/net_errors.h"
16 
17 namespace net {
18 
19 namespace {
20 
21 // In tests, this value is used to override the return value of
22 // UploadFileElementReader::GetContentLength() when set to non-zero.
23 uint64_t overriding_content_length = 0;
24 
25 }  // namespace
26 
UploadFileElementReader(base::TaskRunner * task_runner,base::File file,const base::FilePath & path,uint64_t range_offset,uint64_t range_length,const base::Time & expected_modification_time)27 UploadFileElementReader::UploadFileElementReader(
28     base::TaskRunner* task_runner,
29     base::File file,
30     const base::FilePath& path,
31     uint64_t range_offset,
32     uint64_t range_length,
33     const base::Time& expected_modification_time)
34     : task_runner_(task_runner),
35       path_(path),
36       range_offset_(range_offset),
37       range_length_(range_length),
38       expected_modification_time_(expected_modification_time) {
39   DCHECK(file.IsValid());
40   DCHECK(task_runner_.get());
41   file_stream_ = std::make_unique<FileStream>(std::move(file), task_runner);
42 }
43 
UploadFileElementReader(base::TaskRunner * task_runner,const base::FilePath & path,uint64_t range_offset,uint64_t range_length,const base::Time & expected_modification_time)44 UploadFileElementReader::UploadFileElementReader(
45     base::TaskRunner* task_runner,
46     const base::FilePath& path,
47     uint64_t range_offset,
48     uint64_t range_length,
49     const base::Time& expected_modification_time)
50     : task_runner_(task_runner),
51       path_(path),
52       range_offset_(range_offset),
53       range_length_(range_length),
54       expected_modification_time_(expected_modification_time) {
55   DCHECK(task_runner_.get());
56 }
57 
58 UploadFileElementReader::~UploadFileElementReader() = default;
59 
AsFileReader() const60 const UploadFileElementReader* UploadFileElementReader::AsFileReader() const {
61   return this;
62 }
63 
Init(CompletionOnceCallback callback)64 int UploadFileElementReader::Init(CompletionOnceCallback callback) {
65   DCHECK(!callback.is_null());
66 
67   bytes_remaining_ = 0;
68   content_length_ = 0;
69   pending_callback_.Reset();
70 
71   // If the file is being opened, just update the callback, and continue
72   // waiting.
73   if (next_state_ == State::OPEN_COMPLETE) {
74     DCHECK(file_stream_);
75     pending_callback_ = std::move(callback);
76     return ERR_IO_PENDING;
77   }
78 
79   // If there's already a pending operation, wait for it to complete before
80   // restarting the request.
81   if (next_state_ != State::IDLE) {
82     init_called_while_operation_pending_ = true;
83     pending_callback_ = std::move(callback);
84     return ERR_IO_PENDING;
85   }
86 
87   DCHECK(!init_called_while_operation_pending_);
88 
89   if (file_stream_) {
90     // If the file is already open, just re-use it.
91     // TODO(mmenke): Consider reusing file info, too.
92     next_state_ = State::SEEK;
93   } else {
94     next_state_ = State::OPEN;
95   }
96   int result = DoLoop(OK);
97   if (result == ERR_IO_PENDING)
98     pending_callback_ = std::move(callback);
99   return result;
100 }
101 
GetContentLength() const102 uint64_t UploadFileElementReader::GetContentLength() const {
103   if (overriding_content_length)
104     return overriding_content_length;
105   return content_length_;
106 }
107 
BytesRemaining() const108 uint64_t UploadFileElementReader::BytesRemaining() const {
109   return bytes_remaining_;
110 }
111 
Read(IOBuffer * buf,int buf_length,CompletionOnceCallback callback)112 int UploadFileElementReader::Read(IOBuffer* buf,
113                                   int buf_length,
114                                   CompletionOnceCallback callback) {
115   DCHECK(!callback.is_null());
116   DCHECK_EQ(next_state_, State::IDLE);
117   DCHECK(file_stream_);
118 
119   int num_bytes_to_read = static_cast<int>(
120       std::min(BytesRemaining(), static_cast<uint64_t>(buf_length)));
121   if (num_bytes_to_read == 0)
122     return 0;
123 
124   next_state_ = State::READ_COMPLETE;
125   int result = file_stream_->Read(
126       buf, num_bytes_to_read,
127       base::BindOnce(base::IgnoreResult(&UploadFileElementReader::OnIOComplete),
128                      weak_ptr_factory_.GetWeakPtr()));
129 
130   if (result != ERR_IO_PENDING)
131     result = DoLoop(result);
132 
133   if (result == ERR_IO_PENDING)
134     pending_callback_ = std::move(callback);
135 
136   return result;
137 }
138 
DoLoop(int result)139 int UploadFileElementReader::DoLoop(int result) {
140   DCHECK_NE(result, ERR_IO_PENDING);
141 
142   if (init_called_while_operation_pending_) {
143     // File should already have been opened successfully.
144     DCHECK_NE(next_state_, State::OPEN_COMPLETE);
145 
146     next_state_ = State::SEEK;
147     init_called_while_operation_pending_ = false;
148     result = net::OK;
149   }
150 
151   while (next_state_ != State::IDLE && result != ERR_IO_PENDING) {
152     State state = next_state_;
153     next_state_ = State::IDLE;
154     switch (state) {
155       case State::IDLE:
156         NOTREACHED();
157       case State::OPEN:
158         // Ignore previous result here. It's typically OK, but if Init()
159         // interrupted the previous operation, it may be an error.
160         result = DoOpen();
161         break;
162       case State::OPEN_COMPLETE:
163         result = DoOpenComplete(result);
164         break;
165       case State::SEEK:
166         DCHECK_EQ(OK, result);
167         result = DoSeek();
168         break;
169       case State::GET_FILE_INFO:
170         result = DoGetFileInfo(result);
171         break;
172       case State::GET_FILE_INFO_COMPLETE:
173         result = DoGetFileInfoComplete(result);
174         break;
175 
176       case State::READ_COMPLETE:
177         result = DoReadComplete(result);
178         break;
179     }
180   }
181 
182   return result;
183 }
184 
DoOpen()185 int UploadFileElementReader::DoOpen() {
186   DCHECK(!file_stream_);
187 
188   next_state_ = State::OPEN_COMPLETE;
189 
190   file_stream_ = std::make_unique<FileStream>(task_runner_.get());
191   int result = file_stream_->Open(
192       path_,
193       base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_ASYNC,
194       base::BindOnce(&UploadFileElementReader::OnIOComplete,
195                      weak_ptr_factory_.GetWeakPtr()));
196   DCHECK_GT(0, result);
197   return result;
198 }
199 
DoOpenComplete(int result)200 int UploadFileElementReader::DoOpenComplete(int result) {
201   if (result < 0) {
202     DLOG(WARNING) << "Failed to open \"" << path_.value()
203                   << "\" for reading: " << result;
204     file_stream_.reset();
205     return result;
206   }
207 
208   if (range_offset_) {
209     next_state_ = State::SEEK;
210   } else {
211     next_state_ = State::GET_FILE_INFO;
212   }
213   return net::OK;
214 }
215 
DoSeek()216 int UploadFileElementReader::DoSeek() {
217   next_state_ = State::GET_FILE_INFO;
218   return file_stream_->Seek(
219       range_offset_,
220       base::BindOnce(
221           [](base::WeakPtr<UploadFileElementReader> weak_this, int64_t result) {
222             if (!weak_this)
223               return;
224             weak_this->OnIOComplete(result >= 0 ? OK
225                                                 : static_cast<int>(result));
226           },
227           weak_ptr_factory_.GetWeakPtr()));
228 }
229 
DoGetFileInfo(int result)230 int UploadFileElementReader::DoGetFileInfo(int result) {
231   if (result < 0) {
232     DLOG(WARNING) << "Failed to seek \"" << path_.value()
233                   << "\" to offset: " << range_offset_ << " (" << result << ")";
234     return result;
235   }
236 
237   next_state_ = State::GET_FILE_INFO_COMPLETE;
238 
239   auto file_info = std::make_unique<base::File::Info>();
240   auto* file_info_ptr = file_info.get();
241   result = file_stream_->GetFileInfo(
242       file_info_ptr,
243       base::BindOnce(
244           [](base::WeakPtr<UploadFileElementReader> weak_this,
245              std::unique_ptr<base::File::Info> file_info, int result) {
246             if (!weak_this)
247               return;
248             weak_this->file_info_ = *file_info;
249             weak_this->OnIOComplete(result);
250           },
251           weak_ptr_factory_.GetWeakPtr(), std::move(file_info)));
252   // GetFileInfo() can't succeed synchronously.
253   DCHECK_NE(result, OK);
254   return result;
255 }
256 
DoGetFileInfoComplete(int result)257 int UploadFileElementReader::DoGetFileInfoComplete(int result) {
258   if (result != OK) {
259     DLOG(WARNING) << "Failed to get file info of \"" << path_.value() << "\"";
260     return result;
261   }
262 
263   int64_t length = file_info_.size;
264   if (range_offset_ < static_cast<uint64_t>(length)) {
265     // Compensate for the offset.
266     length = std::min(length - range_offset_, range_length_);
267   }
268 
269   // If the underlying file has been changed and the expected file modification
270   // time is set, treat it as error. Note that |expected_modification_time_| may
271   // have gone through multiple conversion steps involving loss of precision
272   // (including conversion to time_t). Therefore the check below only verifies
273   // that the timestamps are within one second of each other. This check is used
274   // for sliced files.
275   if (!expected_modification_time_.is_null() &&
276       (expected_modification_time_ - file_info_.last_modified)
277               .magnitude()
278               .InSeconds() != 0) {
279     return ERR_UPLOAD_FILE_CHANGED;
280   }
281 
282   content_length_ = length;
283   bytes_remaining_ = GetContentLength();
284   return result;
285 }
286 
DoReadComplete(int result)287 int UploadFileElementReader::DoReadComplete(int result) {
288   if (result == 0)  // Reached end-of-file earlier than expected.
289     return ERR_UPLOAD_FILE_CHANGED;
290 
291   if (result > 0) {
292     DCHECK_GE(bytes_remaining_, static_cast<uint64_t>(result));
293     bytes_remaining_ -= result;
294   }
295 
296   return result;
297 }
298 
OnIOComplete(int result)299 void UploadFileElementReader::OnIOComplete(int result) {
300   DCHECK(pending_callback_);
301 
302   result = DoLoop(result);
303 
304   if (result != ERR_IO_PENDING)
305     std::move(pending_callback_).Run(result);
306 }
307 
308 UploadFileElementReader::ScopedOverridingContentLengthForTests::
ScopedOverridingContentLengthForTests(uint64_t value)309     ScopedOverridingContentLengthForTests(uint64_t value) {
310   overriding_content_length = value;
311 }
312 
313 UploadFileElementReader::ScopedOverridingContentLengthForTests::
~ScopedOverridingContentLengthForTests()314 ~ScopedOverridingContentLengthForTests() {
315   overriding_content_length = 0;
316 }
317 
318 }  // namespace net
319