1 #include "packagemanagerinterface.h"
3 #include "http/httpclient.h"
4 #include "logging/logging.h"
8 : hash_type{target_in.hashes()[0].type()},
9 target{std::move(target_in)},
11 progress_cb{std::move(progress_cb_in)} {}
12 uintmax_t downloaded_length{0};
13 unsigned int last_progress{0};
14 std::unique_ptr<StorageTargetWHandle> fhandle;
15 const Hash::Type hash_type;
18 case Hash::Type::kSha256:
20 case Hash::Type::kSha512:
23 throw std::runtime_error(
"Unknown hash algorithm");
28 FetcherProgressCb progress_cb;
35 static size_t DownloadHandler(
char* contents,
size_t size,
size_t nmemb,
void* userp) {
37 auto* ds = static_cast<DownloadMetaStruct*>(userp);
38 uint64_t downloaded = size * nmemb;
39 uint64_t expected = ds->target.length();
40 if ((ds->downloaded_length + downloaded) > expected) {
41 return downloaded + 1;
45 size_t written_size = ds->fhandle->wfeed(reinterpret_cast<uint8_t*>(contents), downloaded);
46 ds->hasher().update(reinterpret_cast<const unsigned char*>(contents), written_size);
48 ds->downloaded_length += downloaded;
52 static int ProgressHandler(
void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
57 auto* ds = static_cast<DownloadMetaStruct*>(clientp);
59 uint64_t expected = ds->target.length();
60 auto progress = static_cast<unsigned int>((ds->downloaded_length * 100) / expected);
61 if (ds->progress_cb && progress > ds->last_progress) {
62 ds->last_progress = progress;
63 ds->progress_cb(ds->target,
"Downloading", progress);
65 if (ds->token !=
nullptr && !ds->token->canContinue(
false)) {
73 static constexpr
size_t buf_len = 1024;
74 std::array<uint8_t, buf_len> buf{};
76 data_len =
data->rread(buf.data(), buf.size());
77 hasher.update(buf.data(), data_len);
78 }
while (data_len != 0);
82 const KeyManager& keys, FetcherProgressCb progress_cb,
87 if (target.hashes().empty()) {
90 TargetStatus exists = PackageManagerInterface::verifyTarget(target);
91 if (exists == TargetStatus::kGood) {
92 LOG_INFO <<
"Image already downloaded; skipping download";
95 std::unique_ptr<DownloadMetaStruct> ds = std_::make_unique<DownloadMetaStruct>(target, progress_cb, token);
96 if (target.length() == 0) {
97 LOG_INFO <<
"Skipping download of target with length 0";
98 ds->fhandle = storage_->allocateTargetFile(target);
101 if (exists == TargetStatus::kIncomplete) {
102 LOG_INFO <<
"Continuing incomplete download of file " << target.filename();
103 auto target_check = storage_->checkTargetFile(target);
104 ds->downloaded_length = target_check->first;
105 auto target_handle = storage_->openTargetFile(target);
106 ::restoreHasherState(ds->hasher(), target_handle.get());
107 target_handle->rclose();
108 ds->fhandle = target_handle->toWriteHandle();
112 LOG_DEBUG <<
"Initiating download of file " << target.filename();
113 ds->fhandle = storage_->allocateTargetFile(target);
116 const uint64_t required_bytes = target.length() - ds->downloaded_length;
117 if (!storage_->checkAvailableDiskSpace(required_bytes)) {
118 throw std::runtime_error(
"Insufficient disk space available to download target");
121 std::string target_url = target.uri();
122 if (target_url.empty()) {
123 target_url = fetcher.getRepoServer() +
"/targets/" + Utils::urlEncode(target.filename());
128 response = http_->download(target_url, DownloadHandler, ProgressHandler, ds.get(),
129 static_cast<curl_off_t>(ds->downloaded_length));
131 if (response.curl_code == CURLE_RANGE_ERROR) {
132 LOG_WARNING <<
"The image server doesn't support byte range requests,"
133 " try to download the image from the beginning: "
135 ds = std_::make_unique<DownloadMetaStruct>(target, progress_cb, token);
136 ds->fhandle = storage_->allocateTargetFile(target);
140 if (!response.wasInterrupted()) {
148 ds->fhandle = storage_->openTargetFile(target)->toWriteHandle();
150 LOG_TRACE <<
"Download status: " << response.getStatusStr() << std::endl;
151 if (!response.isOk()) {
152 if (response.curl_code == CURLE_WRITE_ERROR) {
155 throw Uptane::Exception(
"image",
"Could not download file, error: " + response.error_message);
157 if (!target.MatchHash(
Hash(ds->hash_type, ds->hasher().getHexDigest()))) {
158 ds->fhandle->wabort();
161 ds->fhandle->wcommit();
163 }
catch (
const std::exception& e) {
164 LOG_WARNING <<
"Error while downloading a target: " << e.what();
169 TargetStatus PackageManagerInterface::verifyTarget(
const Uptane::Target& target)
const {
170 auto target_exists = storage_->checkTargetFile(target);
171 if (!target_exists) {
172 LOG_DEBUG <<
"File " << target.filename() <<
" with expected hash not found in the database.";
173 return TargetStatus::kNotFound;
174 }
else if (target_exists->first < target.length()) {
175 LOG_DEBUG <<
"File " << target.filename() <<
" was found in the database, but is incomplete.";
176 return TargetStatus::kIncomplete;
177 }
else if (target_exists->first > target.length()) {
178 LOG_DEBUG <<
"File " << target.filename() <<
" was found in the database, but is oversized.";
179 return TargetStatus::kOversized;
184 ds.downloaded_length = target_exists->first;
185 auto target_handle = storage_->openTargetFile(target);
186 ::restoreHasherState(ds.hasher(), target_handle.get());
187 target_handle->rclose();
188 if (!target.MatchHash(
Hash(ds.hash_type, ds.hasher().getHexDigest()))) {
189 LOG_ERROR <<
"Target exists with expected length, but hash does not match metadata! " << target;
190 return TargetStatus::kHashMismatch;
193 return TargetStatus::kGood;