1 #include "ostree_object.h"
10 #include "logging/logging.h"
11 #include "ostree_repo.h"
12 #include "request_pool.h"
13 #include "utilities/utils.h"
17 OSTreeObject::OSTreeObject(
const OSTreeRepo &repo,
const std::string &object_name)
18 : file_path_(repo.root() /
"/objects/" / object_name),
19 object_name_(object_name),
22 is_on_server_(PresenceOnServer::kObjectStateUnknown),
23 curl_handle_(nullptr),
25 if (!boost::filesystem::is_regular_file(file_path_)) {
26 throw std::runtime_error(file_path_.native() +
" is not a valid OSTree object.");
30 OSTreeObject::~OSTreeObject() {
31 if (curl_handle_ !=
nullptr) {
32 curl_easy_cleanup(curl_handle_);
33 curl_handle_ =
nullptr;
37 void OSTreeObject::AddParent(
OSTreeObject *parent, std::list<OSTreeObject::ptr>::iterator parent_it) {
41 par.second = parent_it;
42 parents_.push_back(par);
45 void OSTreeObject::ChildNotify(std::list<OSTreeObject::ptr>::iterator child_it) {
46 assert((*child_it)->is_on_server() == PresenceOnServer::kObjectPresent);
47 children_.erase(child_it);
50 void OSTreeObject::NotifyParents(
RequestPool &pool) {
51 assert(is_on_server_ == PresenceOnServer::kObjectPresent);
53 for (parentref parent : parents_) {
54 parent.first->ChildNotify(parent.second);
55 if (parent.first->children_ready()) {
56 pool.AddUpload(parent.first);
61 void OSTreeObject::AppendChild(
const OSTreeObject::ptr &child) {
63 if (child->is_on_server() == PresenceOnServer::kObjectPresent) {
67 children_.push_back(child);
68 auto last = children_.end();
70 child->AddParent(
this, last);
74 void OSTreeObject::PopulateChildren() {
75 const boost::filesystem::path ext = file_path_.extension();
76 const GVariantType *content_type;
81 if (ext.compare(
".commit") == 0) {
82 content_type = G_VARIANT_TYPE(
"(a{sv}aya(say)sstayay)");
84 }
else if (ext.compare(
".dirtree") == 0) {
85 content_type = G_VARIANT_TYPE(
"(a(say)a(sayay))");
91 GError *gerror =
nullptr;
92 GMappedFile *mfile = g_mapped_file_new(file_path_.c_str(), FALSE, &gerror);
94 if (mfile ==
nullptr) {
95 throw std::runtime_error(
"Failed to map metadata file " + file_path_.native());
99 g_variant_new_from_data(content_type, g_mapped_file_get_contents(mfile), g_mapped_file_get_length(mfile), TRUE,
100 reinterpret_cast<GDestroyNotify
>(g_mapped_file_unref), mfile);
101 g_variant_ref_sink(contents);
105 GVariant *content_csum_variant =
nullptr;
106 g_variant_get_child(contents, 6,
"@ay", &content_csum_variant);
109 const auto *csum =
static_cast<const uint8_t *
>(g_variant_get_fixed_array(content_csum_variant, &n_elts, 1));
110 assert(n_elts == 32);
111 AppendChild(repo_.GetObject(csum, OstreeObjectType::OSTREE_OBJECT_TYPE_DIR_TREE));
114 GVariant *meta_csum_variant =
nullptr;
115 g_variant_get_child(contents, 7,
"@ay", &meta_csum_variant);
116 csum =
static_cast<const uint8_t *
>(g_variant_get_fixed_array(meta_csum_variant, &n_elts, 1));
117 assert(n_elts == 32);
118 AppendChild(repo_.GetObject(csum, OstreeObjectType::OSTREE_OBJECT_TYPE_DIR_META));
120 g_variant_unref(meta_csum_variant);
121 g_variant_unref(content_csum_variant);
123 GVariant *files_variant =
nullptr;
124 GVariant *dirs_variant =
nullptr;
126 files_variant = g_variant_get_child_value(contents, 0);
127 dirs_variant = g_variant_get_child_value(contents, 1);
129 gsize nfiles = g_variant_n_children(files_variant);
130 gsize ndirs = g_variant_n_children(dirs_variant);
133 for (gsize i = 0; i < nfiles; i++) {
134 GVariant *csum_variant =
nullptr;
135 const char *fname =
nullptr;
137 g_variant_get_child(files_variant, i,
"(&s@ay)", &fname, &csum_variant);
139 const auto *csum =
static_cast<const uint8_t *
>(g_variant_get_fixed_array(csum_variant, &n_elts, 1));
140 assert(n_elts == 32);
141 AppendChild(repo_.GetObject(csum, OstreeObjectType::OSTREE_OBJECT_TYPE_FILE));
143 g_variant_unref(csum_variant);
147 for (gsize i = 0; i < ndirs; i++) {
148 GVariant *content_csum_variant =
nullptr;
149 GVariant *meta_csum_variant =
nullptr;
150 const char *fname =
nullptr;
151 g_variant_get_child(dirs_variant, i,
"(&s@ay@ay)", &fname, &content_csum_variant, &meta_csum_variant);
154 const auto *csum =
static_cast<const uint8_t *
>(g_variant_get_fixed_array(content_csum_variant, &n_elts, 1));
155 assert(n_elts == 32);
156 AppendChild(repo_.GetObject(csum, OstreeObjectType::OSTREE_OBJECT_TYPE_DIR_TREE));
159 csum =
static_cast<const uint8_t *
>(g_variant_get_fixed_array(meta_csum_variant, &n_elts, 1));
160 assert(n_elts == 32);
161 AppendChild(repo_.GetObject(csum, OstreeObjectType::OSTREE_OBJECT_TYPE_DIR_META));
163 g_variant_unref(meta_csum_variant);
164 g_variant_unref(content_csum_variant);
167 g_variant_unref(dirs_variant);
168 g_variant_unref(files_variant);
170 g_variant_unref(contents);
173 void OSTreeObject::QueryChildren(
RequestPool &pool) {
174 for (
const OSTreeObject::ptr &child : children_) {
175 if (child->is_on_server() == PresenceOnServer::kObjectStateUnknown) {
176 pool.AddQuery(child);
181 string OSTreeObject::Url()
const {
return "objects/" + object_name_; }
183 void OSTreeObject::MakeTestRequest(
const TreehubServer &push_target, CURLM *curl_multi_handle) {
184 assert(!curl_handle_);
185 curl_handle_ = curl_easy_init();
186 if (curl_handle_ ==
nullptr) {
187 throw std::runtime_error(
"Could not initialize curl handle");
189 curlEasySetoptWrapper(curl_handle_, CURLOPT_VERBOSE, get_curlopt_verbose());
190 current_operation_ = CurrentOp::kOstreeObjectPresenceCheck;
192 push_target.InjectIntoCurl(Url(), curl_handle_);
193 curlEasySetoptWrapper(curl_handle_, CURLOPT_NOBODY, 1L);
195 curlEasySetoptWrapper(curl_handle_, CURLOPT_USERAGENT, Utils::getUserAgent());
196 curlEasySetoptWrapper(curl_handle_, CURLOPT_WRITEFUNCTION, &OSTreeObject::curl_handle_write);
197 curlEasySetoptWrapper(curl_handle_, CURLOPT_WRITEDATA,
this);
198 curlEasySetoptWrapper(curl_handle_, CURLOPT_PRIVATE,
this);
199 http_response_.str(
"");
201 const CURLMcode err = curl_multi_add_handle(curl_multi_handle, curl_handle_);
203 LOG_ERROR <<
"err:" << curl_multi_strerror(err);
206 request_start_time_ = std::chrono::steady_clock::now();
209 void OSTreeObject::Upload(
TreehubServer &push_target, CURLM *curl_multi_handle,
const RunMode mode) {
211 LOG_INFO <<
"Uploading " << object_name_;
213 LOG_INFO <<
"Would upload " << object_name_;
214 is_on_server_ = PresenceOnServer::kObjectPresent;
217 assert(!curl_handle_);
219 curl_handle_ = curl_easy_init();
220 if (curl_handle_ ==
nullptr) {
221 throw std::runtime_error(
"Could not initialize curl handle");
223 curlEasySetoptWrapper(curl_handle_, CURLOPT_VERBOSE, get_curlopt_verbose());
224 current_operation_ = CurrentOp::kOstreeObjectUploading;
225 push_target.SetContentType(
"Content-Type: application/octet-stream");
226 push_target.InjectIntoCurl(Url(), curl_handle_);
227 curlEasySetoptWrapper(curl_handle_, CURLOPT_USERAGENT, Utils::getUserAgent());
228 curlEasySetoptWrapper(curl_handle_, CURLOPT_WRITEFUNCTION, &OSTreeObject::curl_handle_write);
229 curlEasySetoptWrapper(curl_handle_, CURLOPT_WRITEDATA,
this);
230 http_response_.str(
"");
232 struct stat file_info {};
233 fd_ = fopen(file_path_.c_str(),
"rb");
234 if (fd_ ==
nullptr) {
235 throw std::runtime_error(
"could not open file to be uploaded");
237 if (stat(file_path_.c_str(), &file_info) < 0) {
238 throw std::runtime_error(
"Could not get file information");
241 curlEasySetoptWrapper(curl_handle_, CURLOPT_READDATA, fd_);
242 curlEasySetoptWrapper(curl_handle_, CURLOPT_POSTFIELDSIZE, file_info.st_size);
243 curlEasySetoptWrapper(curl_handle_, CURLOPT_POST, 1);
245 curlEasySetoptWrapper(curl_handle_, CURLOPT_PRIVATE,
this);
246 const CURLMcode err = curl_multi_add_handle(curl_multi_handle, curl_handle_);
248 LOG_ERROR <<
"curl_multi_add_handle error:" << curl_multi_strerror(err);
252 request_start_time_ = std::chrono::steady_clock::now();
255 void OSTreeObject::CheckChildren(
RequestPool &pool,
const long rescode) {
258 LOG_TRACE <<
"Children of " << object_name_ <<
": " << children_.size();
259 if (children_ready()) {
260 if (rescode != 200) {
261 pool.AddUpload(
this);
267 LOG_ERROR <<
"Source OSTree repo does not contain object " << error.missing_object();
272 void OSTreeObject::PresenceError(
RequestPool &pool,
const int64_t rescode) {
273 is_on_server_ = PresenceOnServer::kObjectStateUnknown;
274 LOG_WARNING <<
"OSTree query reported an error code: " << rescode <<
" retrying...";
275 LOG_DEBUG <<
"Http response code:" << rescode;
276 LOG_DEBUG << http_response_.str();
277 last_operation_result_ = ServerResponse::kTemporaryFailure;
281 void OSTreeObject::UploadError(
RequestPool &pool,
const int64_t rescode) {
282 LOG_WARNING <<
"OSTree upload reported an error code:" << rescode <<
" retrying...";
283 LOG_DEBUG <<
"Http response code:" << rescode;
284 LOG_DEBUG << http_response_.str();
285 is_on_server_ = PresenceOnServer::kObjectMissing;
286 last_operation_result_ = ServerResponse::kTemporaryFailure;
287 pool.AddUpload(
this);
290 void OSTreeObject::CurlDone(CURLM *curl_multi_handle,
RequestPool &pool) {
292 assert(refcount_ > 0);
295 curl_easy_getinfo(curl_handle_, CURLINFO_EFFECTIVE_URL, &url);
297 curl_easy_getinfo(curl_handle_, CURLINFO_RESPONSE_CODE, &rescode);
298 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) {
325 if (url ==
nullptr || strstr(url, object_name_.c_str()) ==
nullptr) {
326 UploadError(pool, rescode);
327 }
else if (rescode == 204) {
328 LOG_TRACE <<
"OSTree upload successful";
329 is_on_server_ = PresenceOnServer::kObjectPresent;
330 last_operation_result_ = ServerResponse::kOk;
332 }
else if (rescode == 409) {
333 LOG_DEBUG <<
"OSTree upload reported a 409 Conflict, possibly due to concurrent uploads";
334 is_on_server_ = PresenceOnServer::kObjectPresent;
335 last_operation_result_ = ServerResponse::kOk;
338 UploadError(pool, rescode);
342 LOG_ERROR <<
"Unknown operation: " <<
static_cast<int>(current_operation_);
345 curl_multi_remove_handle(curl_multi_handle, curl_handle_);
346 curl_easy_cleanup(curl_handle_);
347 curl_handle_ =
nullptr;
350 size_t OSTreeObject::curl_handle_write(
void *buffer,
size_t size,
size_t nmemb,
void *userp) {
352 that->http_response_.write(
static_cast<const char *
>(buffer),
static_cast<std::streamsize
>(size * nmemb));
356 OSTreeObject::ptr ostree_object_from_curl(CURL *curlhandle) {
358 curl_easy_getinfo(curlhandle, CURLINFO_PRIVATE, &p);
361 return boost::intrusive_ptr<OSTreeObject>(h);
364 void intrusive_ptr_add_ref(
OSTreeObject *h) { h->refcount_++; }
367 if (--h->refcount_ == 0) {
372 std::ostream &operator<<(std::ostream &stream,
const OSTreeObject &o) {
373 stream << o.object_name_;
377 std::ostream &operator<<(std::ostream &stream,
const OSTreeObject::ptr &o) {