1 #include "ostree_object.h"
11 #include "logging/logging.h"
12 #include "ostree_repo.h"
13 #include "request_pool.h"
14 #include "utilities/utils.h"
18 OSTreeObject::OSTreeObject(
const OSTreeRepo &repo,
const std::string &object_name)
19 : file_path_(repo.root() /
"/objects/" / object_name),
20 object_name_(object_name),
23 is_on_server_(PresenceOnServer::kObjectStateUnknown),
24 curl_handle_(nullptr),
26 if (!boost::filesystem::is_regular_file(file_path_)) {
27 throw std::runtime_error(file_path_.native() +
" is not a valid OSTree repo.");
31 OSTreeObject::~OSTreeObject() {
32 if (curl_handle_ !=
nullptr) {
33 curl_easy_cleanup(curl_handle_);
34 curl_handle_ =
nullptr;
38 void OSTreeObject::AddParent(
OSTreeObject *parent, std::list<OSTreeObject::ptr>::iterator parent_it) {
42 par.second = parent_it;
43 parents_.push_back(par);
46 void OSTreeObject::ChildNotify(std::list<OSTreeObject::ptr>::iterator child_it) {
47 assert((*child_it)->is_on_server() == PresenceOnServer::kObjectPresent);
48 children_.erase(child_it);
51 void OSTreeObject::NotifyParents(
RequestPool &pool) {
52 assert(is_on_server_ == PresenceOnServer::kObjectPresent);
54 for (parentref parent : parents_) {
55 parent.first->ChildNotify(parent.second);
56 if (parent.first->children_ready()) {
57 pool.AddUpload(parent.first);
62 void OSTreeObject::AppendChild(
const OSTreeObject::ptr &child) {
64 if (child->is_on_server() == PresenceOnServer::kObjectPresent) {
68 children_.push_back(child);
69 auto last = children_.end();
71 child->AddParent(
this, last);
75 void OSTreeObject::PopulateChildren() {
76 const boost::filesystem::path ext = file_path_.extension();
77 const GVariantType *content_type;
82 if (ext.compare(
".commit") == 0) {
83 content_type = G_VARIANT_TYPE(
"(a{sv}aya(say)sstayay)");
85 }
else if (ext.compare(
".dirtree") == 0) {
86 content_type = G_VARIANT_TYPE(
"(a(say)a(sayay))");
92 GError *gerror =
nullptr;
93 GMappedFile *mfile = g_mapped_file_new(file_path_.c_str(), FALSE, &gerror);
95 if (mfile ==
nullptr) {
96 throw std::runtime_error(
"Failed to map metadata file " + file_path_.native());
100 g_variant_new_from_data(content_type, g_mapped_file_get_contents(mfile), g_mapped_file_get_length(mfile), TRUE,
101 reinterpret_cast<GDestroyNotify>(g_mapped_file_unref), mfile);
102 g_variant_ref_sink(contents);
106 GVariant *content_csum_variant =
nullptr;
107 g_variant_get_child(contents, 6,
"@ay", &content_csum_variant);
110 const auto *csum = static_cast<const uint8_t *>(g_variant_get_fixed_array(content_csum_variant, &n_elts, 1));
111 assert(n_elts == 32);
112 AppendChild(repo_.GetObject(csum, OstreeObjectType::OSTREE_OBJECT_TYPE_DIR_TREE));
115 GVariant *meta_csum_variant =
nullptr;
116 g_variant_get_child(contents, 7,
"@ay", &meta_csum_variant);
117 csum = static_cast<const uint8_t *>(g_variant_get_fixed_array(meta_csum_variant, &n_elts, 1));
118 assert(n_elts == 32);
119 AppendChild(repo_.GetObject(csum, OstreeObjectType::OSTREE_OBJECT_TYPE_DIR_META));
121 g_variant_unref(meta_csum_variant);
122 g_variant_unref(content_csum_variant);
124 GVariant *files_variant =
nullptr;
125 GVariant *dirs_variant =
nullptr;
127 files_variant = g_variant_get_child_value(contents, 0);
128 dirs_variant = g_variant_get_child_value(contents, 1);
130 gsize nfiles = g_variant_n_children(files_variant);
131 gsize ndirs = g_variant_n_children(dirs_variant);
134 for (gsize i = 0; i < nfiles; i++) {
135 GVariant *csum_variant =
nullptr;
136 const char *fname =
nullptr;
138 g_variant_get_child(files_variant, i,
"(&s@ay)", &fname, &csum_variant);
140 const auto *csum = static_cast<const uint8_t *>(g_variant_get_fixed_array(csum_variant, &n_elts, 1));
141 assert(n_elts == 32);
142 AppendChild(repo_.GetObject(csum, OstreeObjectType::OSTREE_OBJECT_TYPE_FILE));
144 g_variant_unref(csum_variant);
148 for (gsize i = 0; i < ndirs; i++) {
149 GVariant *content_csum_variant =
nullptr;
150 GVariant *meta_csum_variant =
nullptr;
151 const char *fname =
nullptr;
152 g_variant_get_child(dirs_variant, i,
"(&s@ay@ay)", &fname, &content_csum_variant, &meta_csum_variant);
155 const auto *csum = static_cast<const uint8_t *>(g_variant_get_fixed_array(content_csum_variant, &n_elts, 1));
156 assert(n_elts == 32);
157 AppendChild(repo_.GetObject(csum, OstreeObjectType::OSTREE_OBJECT_TYPE_DIR_TREE));
160 csum = static_cast<const uint8_t *>(g_variant_get_fixed_array(meta_csum_variant, &n_elts, 1));
161 assert(n_elts == 32);
162 AppendChild(repo_.GetObject(csum, OstreeObjectType::OSTREE_OBJECT_TYPE_DIR_META));
164 g_variant_unref(meta_csum_variant);
165 g_variant_unref(content_csum_variant);
168 g_variant_unref(dirs_variant);
169 g_variant_unref(files_variant);
171 g_variant_unref(contents);
174 void OSTreeObject::QueryChildren(
RequestPool &pool) {
175 for (
const OSTreeObject::ptr &child : children_) {
176 if (child->is_on_server() == PresenceOnServer::kObjectStateUnknown) {
177 pool.AddQuery(child);
182 string OSTreeObject::Url()
const {
return "objects/" + object_name_; }
184 void OSTreeObject::MakeTestRequest(
const TreehubServer &push_target, CURLM *curl_multi_handle) {
185 assert(!curl_handle_);
186 curl_handle_ = curl_easy_init();
187 if (curl_handle_ ==
nullptr) {
188 throw std::runtime_error(
"Could not initialize curl handle");
190 curlEasySetoptWrapper(curl_handle_, CURLOPT_VERBOSE, get_curlopt_verbose());
191 current_operation_ = CurrentOp::kOstreeObjectPresenceCheck;
193 push_target.InjectIntoCurl(Url(), curl_handle_);
194 curlEasySetoptWrapper(curl_handle_, CURLOPT_NOBODY, 1L);
196 curlEasySetoptWrapper(curl_handle_, CURLOPT_USERAGENT, Utils::getUserAgent());
197 curlEasySetoptWrapper(curl_handle_, CURLOPT_WRITEFUNCTION, &OSTreeObject::curl_handle_write);
198 curlEasySetoptWrapper(curl_handle_, CURLOPT_WRITEDATA,
this);
199 curlEasySetoptWrapper(curl_handle_, CURLOPT_PRIVATE,
this);
200 http_response_.str(
"");
202 const CURLMcode err = curl_multi_add_handle(curl_multi_handle, curl_handle_);
204 LOG_ERROR <<
"err:" << curl_multi_strerror(err);
207 request_start_time_ = std::chrono::steady_clock::now();
210 void OSTreeObject::Upload(
TreehubServer &push_target, CURLM *curl_multi_handle,
const RunMode mode) {
212 LOG_INFO <<
"Uploading " << object_name_;
214 LOG_INFO <<
"Would upload " << object_name_;
215 is_on_server_ = PresenceOnServer::kObjectPresent;
218 assert(!curl_handle_);
220 curl_handle_ = curl_easy_init();
221 if (curl_handle_ ==
nullptr) {
222 throw std::runtime_error(
"Could not initialize curl handle");
224 curlEasySetoptWrapper(curl_handle_, CURLOPT_VERBOSE, get_curlopt_verbose());
225 current_operation_ = CurrentOp::kOstreeObjectUploading;
226 push_target.SetContentType(
"Content-Type: application/octet-stream");
227 push_target.InjectIntoCurl(Url(), curl_handle_);
228 curlEasySetoptWrapper(curl_handle_, CURLOPT_USERAGENT, Utils::getUserAgent());
229 curlEasySetoptWrapper(curl_handle_, CURLOPT_WRITEFUNCTION, &OSTreeObject::curl_handle_write);
230 curlEasySetoptWrapper(curl_handle_, CURLOPT_WRITEDATA,
this);
231 http_response_.str(
"");
233 struct stat file_info {};
234 fd_ = fopen(file_path_.c_str(),
"rb");
235 if (fd_ ==
nullptr) {
236 throw std::runtime_error(
"could not open file to be uploaded");
238 if (stat(file_path_.c_str(), &file_info) < 0) {
239 throw std::runtime_error(
"Could not get file information");
242 curlEasySetoptWrapper(curl_handle_, CURLOPT_READDATA, fd_);
243 curlEasySetoptWrapper(curl_handle_, CURLOPT_POSTFIELDSIZE, file_info.st_size);
244 curlEasySetoptWrapper(curl_handle_, CURLOPT_POST, 1);
246 curlEasySetoptWrapper(curl_handle_, CURLOPT_PRIVATE,
this);
247 const CURLMcode err = curl_multi_add_handle(curl_multi_handle, curl_handle_);
249 LOG_ERROR <<
"curl_multi_add_handle error:" << curl_multi_strerror(err);
253 request_start_time_ = std::chrono::steady_clock::now();
256 void OSTreeObject::CheckChildren(
RequestPool &pool,
const long rescode) {
259 LOG_TRACE <<
"Children of " << object_name_ <<
": " << children_.size();
260 if (children_ready()) {
261 if (rescode != 200) {
262 pool.AddUpload(
this);
268 LOG_ERROR <<
"Source OSTree repo does not contain object " << error.missing_object();
273 void OSTreeObject::PresenceError(
RequestPool &pool,
const int64_t rescode) {
274 is_on_server_ = PresenceOnServer::kObjectStateUnknown;
275 LOG_WARNING <<
"OSTree query reported an error code: " << rescode <<
" retrying...";
276 LOG_DEBUG <<
"Http response code:" << rescode;
277 LOG_DEBUG << http_response_.str();
278 last_operation_result_ = ServerResponse::kTemporaryFailure;
282 void OSTreeObject::UploadError(
RequestPool &pool,
const int64_t rescode) {
283 LOG_WARNING <<
"OSTree upload reported an error code:" << rescode <<
" retrying...";
284 LOG_DEBUG <<
"Http response code:" << rescode;
285 LOG_DEBUG << http_response_.str();
286 is_on_server_ = PresenceOnServer::kObjectMissing;
287 last_operation_result_ = ServerResponse::kTemporaryFailure;
288 pool.AddUpload(
this);
291 void OSTreeObject::CurlDone(CURLM *curl_multi_handle,
RequestPool &pool) {
293 assert(refcount_ > 0);
296 curl_easy_getinfo(curl_handle_, CURLINFO_EFFECTIVE_URL, &url);
298 curl_easy_getinfo(curl_handle_, CURLINFO_RESPONSE_CODE, &rescode);
299 if (current_operation_ == CurrentOp::kOstreeObjectPresenceCheck) {
302 if (url ==
nullptr || strstr(url, object_name_.c_str()) ==
nullptr) {
303 PresenceError(pool, rescode);
304 }
else if (rescode == 200) {
305 LOG_INFO <<
"Already present: " << object_name_;
306 is_on_server_ = PresenceOnServer::kObjectPresent;
307 last_operation_result_ = ServerResponse::kOk;
309 CheckChildren(pool, rescode);
313 }
else if (rescode == 404) {
314 is_on_server_ = PresenceOnServer::kObjectMissing;
315 last_operation_result_ = ServerResponse::kOk;
316 CheckChildren(pool, rescode);
318 PresenceError(pool, rescode);
321 }
else if (current_operation_ == CurrentOp::kOstreeObjectUploading) {
324 if (url ==
nullptr || strstr(url, object_name_.c_str()) ==
nullptr) {
325 UploadError(pool, rescode);
326 }
else if (rescode == 204) {
327 LOG_TRACE <<
"OSTree upload successful";
328 is_on_server_ = PresenceOnServer::kObjectPresent;
329 last_operation_result_ = ServerResponse::kOk;
331 }
else if (rescode == 409) {
332 LOG_DEBUG <<
"OSTree upload reported a 409 Conflict, possibly due to concurrent uploads";
333 is_on_server_ = PresenceOnServer::kObjectPresent;
334 last_operation_result_ = ServerResponse::kOk;
337 UploadError(pool, rescode);
341 LOG_ERROR <<
"Unknown operation: " << static_cast<int>(current_operation_);
344 curl_multi_remove_handle(curl_multi_handle, curl_handle_);
345 curl_easy_cleanup(curl_handle_);
346 curl_handle_ =
nullptr;
349 size_t OSTreeObject::curl_handle_write(
void *buffer,
size_t size,
size_t nmemb,
void *userp) {
350 auto *that = static_cast<OSTreeObject *>(userp);
351 that->http_response_.write(static_cast<const char *>(buffer), static_cast<std::streamsize>(size * nmemb));
355 OSTreeObject::ptr ostree_object_from_curl(CURL *curlhandle) {
357 curl_easy_getinfo(curlhandle, CURLINFO_PRIVATE, &p);
359 auto *h = static_cast<OSTreeObject *>(p);
360 return boost::intrusive_ptr<OSTreeObject>(h);
363 void intrusive_ptr_add_ref(
OSTreeObject *h) { h->refcount_++; }
366 if (--h->refcount_ == 0) {
371 std::ostream &operator<<(std::ostream &stream,
const OSTreeObject &o) {
372 stream << o.object_name_;
376 std::ostream &operator<<(std::ostream &stream,
const OSTreeObject::ptr &o) {