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 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; 141 if (delegate_) 142 delegate_->ReceivedBytes(this, buffer_.data(), bytes_read); 143 ScheduleRead(); 144 } 145 } 146 147 void FileFetcher::OnReadErrorCallback(const brillo::Error* error) { 148 LOG(ERROR) << "Asynchronous read failed: " << error->GetMessage(); 149 CleanUp(); 150 if (delegate_) 151 delegate_->TransferComplete(this, false); 152 } 153 154 void FileFetcher::Pause() { 155 if (transfer_paused_) { 156 LOG(ERROR) << "Fetcher already paused."; 157 return; 158 } 159 transfer_paused_ = true; 160 } 161 162 void FileFetcher::Unpause() { 163 if (!transfer_paused_) { 164 LOG(ERROR) << "Resume attempted when fetcher not paused."; 165 return; 166 } 167 transfer_paused_ = false; 168 ScheduleRead(); 169 } 170 171 void FileFetcher::CleanUp() { 172 if (stream_) { 173 stream_->CancelPendingAsyncOperations(); 174 stream_->CloseBlocking(nullptr); 175 stream_.reset(); 176 } 177 // Destroying the |stream_| releases the callback, so we don't have any 178 // ongoing read at this point. 179 ongoing_read_ = false; 180 buffer_ = brillo::Blob(); 181 182 transfer_in_progress_ = false; 183 transfer_paused_ = false; 184 } 185 186 } // namespace chromeos_update_engine 187