| Alex Deymo | 2c131bb | 2016-05-26 16:43:13 -0700 | [diff] [blame] | 1 | // | 
|  | 2 | // Copyright (C) 2016 The Android Open Source Project | 
|  | 3 | // | 
|  | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 5 | // you may not use this file except in compliance with the License. | 
|  | 6 | // You may obtain a copy of the License at | 
|  | 7 | // | 
|  | 8 | //      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | // | 
|  | 10 | // Unless required by applicable law or agreed to in writing, software | 
|  | 11 | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 13 | // See the License for the specific language governing permissions and | 
|  | 14 | // limitations under the License. | 
|  | 15 | // | 
|  | 16 |  | 
|  | 17 | #include "update_engine/common/file_fetcher.h" | 
|  | 18 |  | 
|  | 19 | #include <algorithm> | 
|  | 20 | #include <string> | 
|  | 21 |  | 
|  | 22 | #include <base/bind.h> | 
|  | 23 | #include <base/format_macros.h> | 
|  | 24 | #include <base/location.h> | 
|  | 25 | #include <base/logging.h> | 
|  | 26 | #include <base/strings/string_util.h> | 
|  | 27 | #include <base/strings/stringprintf.h> | 
|  | 28 | #include <brillo/streams/file_stream.h> | 
|  | 29 |  | 
| Alex Deymo | 2c131bb | 2016-05-26 16:43:13 -0700 | [diff] [blame] | 30 | #include "update_engine/common/hardware_interface.h" | 
|  | 31 | #include "update_engine/common/platform_constants.h" | 
|  | 32 |  | 
|  | 33 | using std::string; | 
|  | 34 |  | 
|  | 35 | namespace { | 
|  | 36 |  | 
|  | 37 | size_t kReadBufferSize = 16 * 1024; | 
|  | 38 |  | 
|  | 39 | }  // namespace | 
|  | 40 |  | 
|  | 41 | namespace chromeos_update_engine { | 
|  | 42 |  | 
|  | 43 | // static | 
|  | 44 | bool FileFetcher::SupportedUrl(const string& url) { | 
|  | 45 | // Note that we require the file path to start with a "/". | 
|  | 46 | return base::StartsWith( | 
|  | 47 | url, "file:///", base::CompareCase::INSENSITIVE_ASCII); | 
|  | 48 | } | 
|  | 49 |  | 
|  | 50 | FileFetcher::~FileFetcher() { | 
|  | 51 | LOG_IF(ERROR, transfer_in_progress_) | 
|  | 52 | << "Destroying the fetcher while a transfer is in progress."; | 
|  | 53 | CleanUp(); | 
|  | 54 | } | 
|  | 55 |  | 
|  | 56 | // Begins the transfer, which must not have already been started. | 
|  | 57 | void FileFetcher::BeginTransfer(const string& url) { | 
|  | 58 | CHECK(!transfer_in_progress_); | 
|  | 59 |  | 
|  | 60 | if (!SupportedUrl(url)) { | 
|  | 61 | LOG(ERROR) << "Unsupported file URL: " << url; | 
|  | 62 | // No HTTP error code when the URL is not supported. | 
|  | 63 | http_response_code_ = 0; | 
|  | 64 | CleanUp(); | 
|  | 65 | if (delegate_) | 
|  | 66 | delegate_->TransferComplete(this, false); | 
|  | 67 | return; | 
|  | 68 | } | 
|  | 69 |  | 
|  | 70 | string file_path = url.substr(strlen("file://")); | 
|  | 71 | stream_ = | 
|  | 72 | brillo::FileStream::Open(base::FilePath(file_path), | 
|  | 73 | brillo::Stream::AccessMode::READ, | 
|  | 74 | brillo::FileStream::Disposition::OPEN_EXISTING, | 
|  | 75 | nullptr); | 
|  | 76 |  | 
|  | 77 | if (!stream_) { | 
|  | 78 | LOG(ERROR) << "Couldn't open " << file_path; | 
|  | 79 | http_response_code_ = kHttpResponseNotFound; | 
|  | 80 | CleanUp(); | 
|  | 81 | if (delegate_) | 
|  | 82 | delegate_->TransferComplete(this, false); | 
|  | 83 | return; | 
|  | 84 | } | 
|  | 85 | http_response_code_ = kHttpResponseOk; | 
|  | 86 |  | 
|  | 87 | if (offset_) | 
|  | 88 | stream_->SetPosition(offset_, nullptr); | 
|  | 89 | bytes_copied_ = 0; | 
|  | 90 | transfer_in_progress_ = true; | 
|  | 91 | ScheduleRead(); | 
|  | 92 | } | 
|  | 93 |  | 
|  | 94 | void FileFetcher::TerminateTransfer() { | 
|  | 95 | CleanUp(); | 
|  | 96 | if (delegate_) { | 
|  | 97 | // Note that after the callback returns this object may be destroyed. | 
|  | 98 | delegate_->TransferTerminated(this); | 
|  | 99 | } | 
|  | 100 | } | 
|  | 101 |  | 
|  | 102 | void FileFetcher::ScheduleRead() { | 
|  | 103 | if (transfer_paused_ || ongoing_read_ || !transfer_in_progress_) | 
|  | 104 | return; | 
|  | 105 |  | 
|  | 106 | buffer_.resize(kReadBufferSize); | 
|  | 107 | size_t bytes_to_read = buffer_.size(); | 
|  | 108 | if (data_length_ >= 0) { | 
|  | 109 | bytes_to_read = std::min(static_cast<uint64_t>(bytes_to_read), | 
|  | 110 | data_length_ - bytes_copied_); | 
|  | 111 | } | 
|  | 112 |  | 
|  | 113 | if (!bytes_to_read) { | 
|  | 114 | OnReadDoneCallback(0); | 
|  | 115 | return; | 
|  | 116 | } | 
|  | 117 |  | 
|  | 118 | ongoing_read_ = stream_->ReadAsync( | 
|  | 119 | buffer_.data(), | 
|  | 120 | bytes_to_read, | 
|  | 121 | base::Bind(&FileFetcher::OnReadDoneCallback, base::Unretained(this)), | 
|  | 122 | base::Bind(&FileFetcher::OnReadErrorCallback, base::Unretained(this)), | 
|  | 123 | nullptr); | 
|  | 124 |  | 
|  | 125 | if (!ongoing_read_) { | 
|  | 126 | LOG(ERROR) << "Unable to schedule an asynchronous read from the stream."; | 
|  | 127 | CleanUp(); | 
|  | 128 | if (delegate_) | 
|  | 129 | delegate_->TransferComplete(this, false); | 
|  | 130 | } | 
|  | 131 | } | 
|  | 132 |  | 
|  | 133 | void FileFetcher::OnReadDoneCallback(size_t bytes_read) { | 
|  | 134 | ongoing_read_ = false; | 
|  | 135 | if (bytes_read == 0) { | 
|  | 136 | CleanUp(); | 
|  | 137 | if (delegate_) | 
|  | 138 | delegate_->TransferComplete(this, true); | 
|  | 139 | } else { | 
|  | 140 | bytes_copied_ += bytes_read; | 
| Amin Hassani | 0cd9d77 | 2018-07-31 23:55:43 -0700 | [diff] [blame] | 141 | if (delegate_ && | 
|  | 142 | !delegate_->ReceivedBytes(this, buffer_.data(), bytes_read)) | 
|  | 143 | return; | 
| Alex Deymo | 2c131bb | 2016-05-26 16:43:13 -0700 | [diff] [blame] | 144 | ScheduleRead(); | 
|  | 145 | } | 
|  | 146 | } | 
|  | 147 |  | 
|  | 148 | void FileFetcher::OnReadErrorCallback(const brillo::Error* error) { | 
|  | 149 | LOG(ERROR) << "Asynchronous read failed: " << error->GetMessage(); | 
|  | 150 | CleanUp(); | 
|  | 151 | if (delegate_) | 
|  | 152 | delegate_->TransferComplete(this, false); | 
|  | 153 | } | 
|  | 154 |  | 
|  | 155 | void FileFetcher::Pause() { | 
|  | 156 | if (transfer_paused_) { | 
|  | 157 | LOG(ERROR) << "Fetcher already paused."; | 
|  | 158 | return; | 
|  | 159 | } | 
|  | 160 | transfer_paused_ = true; | 
|  | 161 | } | 
|  | 162 |  | 
|  | 163 | void FileFetcher::Unpause() { | 
|  | 164 | if (!transfer_paused_) { | 
|  | 165 | LOG(ERROR) << "Resume attempted when fetcher not paused."; | 
|  | 166 | return; | 
|  | 167 | } | 
|  | 168 | transfer_paused_ = false; | 
|  | 169 | ScheduleRead(); | 
|  | 170 | } | 
|  | 171 |  | 
|  | 172 | void FileFetcher::CleanUp() { | 
|  | 173 | if (stream_) { | 
|  | 174 | stream_->CancelPendingAsyncOperations(); | 
|  | 175 | stream_->CloseBlocking(nullptr); | 
|  | 176 | stream_.reset(); | 
|  | 177 | } | 
|  | 178 | // Destroying the |stream_| releases the callback, so we don't have any | 
|  | 179 | // ongoing read at this point. | 
|  | 180 | ongoing_read_ = false; | 
|  | 181 | buffer_ = brillo::Blob(); | 
|  | 182 |  | 
|  | 183 | transfer_in_progress_ = false; | 
|  | 184 | transfer_paused_ = false; | 
|  | 185 | } | 
|  | 186 |  | 
|  | 187 | }  // namespace chromeos_update_engine |