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 object.");
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) {
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);
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) {
Walk the entire tree and upload any missing objects.
Walk the entire tree (without uploading).
Thrown by GetObject when the object requested is not present in the repository.
RunMode
Execution mode to run garage tools in.
A source repository to read OSTree objects from.