<commit_before>#include "package_manager/ostreemanager.h"

#include <stdio.h>
#include <unistd.h>
#include <fstream>

#include <gio/gio.h>
#include <json/json.h>
#include <ostree-async-progress.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/filesystem.hpp>
#include <utility>

#include "logging/logging.h"
#include "utilities/utils.h"

static void aktualizr_progress_cb(OstreeAsyncProgress *progress, gpointer data) {
  auto *mt = static_cast<PullMetaStruct *>(data);
  if (mt->check_pause) {
    mt->check_pause();  // If fetcher is paused, sleep until resume
  }

  g_autofree char *status = ostree_async_progress_get_status(progress);
  guint scanning = ostree_async_progress_get_uint(progress, "scanning");
  guint outstanding_fetches = ostree_async_progress_get_uint(progress, "outstanding-fetches");
  guint outstanding_metadata_fetches = ostree_async_progress_get_uint(progress, "outstanding-metadata-fetches");
  guint outstanding_writes = ostree_async_progress_get_uint(progress, "outstanding-writes");
  guint n_scanned_metadata = ostree_async_progress_get_uint(progress, "scanned-metadata");

  if (status != nullptr && *status != '\0') {
    LOG_INFO << "ostree-pull: " << status;
  } else if (outstanding_fetches != 0) {
    guint fetched = ostree_async_progress_get_uint(progress, "fetched");
    guint metadata_fetched = ostree_async_progress_get_uint(progress, "metadata-fetched");
    guint requested = ostree_async_progress_get_uint(progress, "requested");
    if (scanning != 0 || outstanding_metadata_fetches != 0) {
      LOG_INFO << "ostree-pull: Receiving metadata objects: " << metadata_fetched
               << " outstanding: " << outstanding_metadata_fetches;
      if (mt->progress_cb) {
        mt->progress_cb(mt->target, "Receiving metadata objects", 0);
      }
    } else {
      guint calculated = (fetched * 100) / requested;
      if (calculated != mt->percent_complete) {
        mt->percent_complete = calculated;
        LOG_INFO << "ostree-pull: Receiving objects: " << calculated << "% ";
        if (mt->progress_cb) {
          mt->progress_cb(mt->target, "Receiving objects", calculated);
        }
      }
    }
  } else if (outstanding_writes != 0) {
    LOG_INFO << "ostree-pull: Writing objects: " << outstanding_writes;
  } else {
    LOG_INFO << "ostree-pull: Scanning metadata: " << n_scanned_metadata;
    if (mt->progress_cb) {
      mt->progress_cb(mt->target, "Scanning metadata", 0);
    }
  }
}

data::InstallationResult OstreeManager::pull(const boost::filesystem::path &sysroot_path,
                                             const std::string &ostree_server, const KeyManager &keys,
                                             const Uptane::Target &target, const std::function<void()> &pause_cb,
                                             OstreeProgressCb progress_cb) {
  std::string refhash = target.sha256Hash();
  const char *const commit_ids[] = {refhash.c_str()};
  GCancellable *cancellable = nullptr;
  GError *error = nullptr;
  GVariantBuilder builder;
  GVariant *options;
  GObjectUniquePtr<OstreeAsyncProgress> progress = nullptr;

  GObjectUniquePtr<OstreeSysroot> sysroot = OstreeManager::LoadSysroot(sysroot_path);
  GObjectUniquePtr<OstreeRepo> repo = LoadRepo(sysroot.get(), &error);
  if (error != nullptr) {
    LOG_ERROR << "Could not get OSTree repo";
    g_error_free(error);
    return data::InstallationResult(data::ResultCode::Numeric::kInstallFailed, "Could not get OSTree repo");
  }

  GHashTable *ref_list = nullptr;
  if (ostree_repo_list_commit_objects_starting_with(repo.get(), refhash.c_str(), &ref_list, nullptr, &error) != 0) {
    guint length = g_hash_table_size(ref_list);
    g_hash_table_destroy(ref_list);  // OSTree creates the table with destroy notifiers, so no memory leaks expected
    // should never be greater than 1, but use >= for robustness
    if (length >= 1) {
      LOG_DEBUG << "refhash already pulled";
      return data::InstallationResult(true, data::ResultCode::Numeric::kAlreadyProcessed, "Refhash was already pulled");
    }
  }
  if (error != nullptr) {
    g_error_free(error);
    error = nullptr;
  }

  if (!OstreeManager::addRemote(repo.get(), ostree_server, keys)) {
    return data::InstallationResult(data::ResultCode::Numeric::kInstallFailed, "Error adding OSTree remote");
  }

  g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
  g_variant_builder_add(&builder, "{s@v}", "flags", g_variant_new_variant(g_variant_new_int32(0)));

  g_variant_builder_add(&builder, "{s@v}", "refs", g_variant_new_variant(g_variant_new_strv(commit_ids, 1)));

  options = g_variant_builder_end(&builder);

  PullMetaStruct mt(target, pause_cb, std::move(progress_cb));
  progress.reset(ostree_async_progress_new_and_connect(aktualizr_progress_cb, &mt));
  if (ostree_repo_pull_with_options(repo.get(), remote, options, progress.get(), cancellable, &error) == 0) {
    LOG_ERROR << "Error while pulling image: " << error->message;
    data::InstallationResult install_res(data::ResultCode::Numeric::kInstallFailed, error->message);
    g_error_free(error);
    g_variant_unref(options);
    return install_res;
  }
  ostree_async_progress_finish(progress.get());
  g_variant_unref(options);
  return data::InstallationResult(data::ResultCode::Numeric::kOk, "Pulling ostree image was successful");
}

data::InstallationResult OstreeManager::install(const Uptane::Target &target) const {
  const char *opt_osname = nullptr;
  GCancellable *cancellable = nullptr;
  GError *error = nullptr;
  char *revision;

  if (config.os.size() != 0u) {
    opt_osname = config.os.c_str();
  }

  GObjectUniquePtr<OstreeSysroot> sysroot = OstreeManager::LoadSysroot(config.sysroot);
  GObjectUniquePtr<OstreeRepo> repo = LoadRepo(sysroot.get(), &error);

  if (error != nullptr) {
    LOG_ERROR << "could not get repo";
    g_error_free(error);
    return data::InstallationResult(data::ResultCode::Numeric::kInstallFailed, "could not get repo");
  }

  auto origin = StructGuard<GKeyFile>(
      ostree_sysroot_origin_new_from_refspec(sysroot.get(), target.sha256Hash().c_str()), g_key_file_free);
  if (ostree_repo_resolve_rev(repo.get(), target.sha256Hash().c_str(), FALSE, &revision, &error) == 0) {
    LOG_ERROR << error->message;
    data::InstallationResult install_res(data::ResultCode::Numeric::kInstallFailed, error->message);
    g_error_free(error);
    return install_res;
  }

  OstreeDeployment *merge_deployment = ostree_sysroot_get_merge_deployment(sysroot.get(), opt_osname);<commit_msg>Fix a leak in OstreeManager::install<commit_after>#include "package_manager/ostreemanager.h"

#include <stdio.h>
#include <unistd.h>
#include <fstream>

#include <gio/gio.h>
#include <json/json.h>
#include <ostree-async-progress.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/filesystem.hpp>
#include <utility>

#include "logging/logging.h"
#include "utilities/utils.h"

static void aktualizr_progress_cb(OstreeAsyncProgress *progress, gpointer data) {
  auto *mt = static_cast<PullMetaStruct *>(data);
  if (mt->check_pause) {
    mt->check_pause();  // If fetcher is paused, sleep until resume
  }

  g_autofree char *status = ostree_async_progress_get_status(progress);
  guint scanning = ostree_async_progress_get_uint(progress, "scanning");
  guint outstanding_fetches = ostree_async_progress_get_uint(progress, "outstanding-fetches");
  guint outstanding_metadata_fetches = ostree_async_progress_get_uint(progress, "outstanding-metadata-fetches");
  guint outstanding_writes = ostree_async_progress_get_uint(progress, "outstanding-writes");
  guint n_scanned_metadata = ostree_async_progress_get_uint(progress, "scanned-metadata");

  if (status != nullptr && *status != '\0') {
    LOG_INFO << "ostree-pull: " << status;
  } else if (outstanding_fetches != 0) {
    guint fetched = ostree_async_progress_get_uint(progress, "fetched");
    guint metadata_fetched = ostree_async_progress_get_uint(progress, "metadata-fetched");
    guint requested = ostree_async_progress_get_uint(progress, "requested");
    if (scanning != 0 || outstanding_metadata_fetches != 0) {
      LOG_INFO << "ostree-pull: Receiving metadata objects: " << metadata_fetched
               << " outstanding: " << outstanding_metadata_fetches;
      if (mt->progress_cb) {
        mt->progress_cb(mt->target, "Receiving metadata objects", 0);
      }
    } else {
      guint calculated = (fetched * 100) / requested;
      if (calculated != mt->percent_complete) {
        mt->percent_complete = calculated;
        LOG_INFO << "ostree-pull: Receiving objects: " << calculated << "% ";
        if (mt->progress_cb) {
          mt->progress_cb(mt->target, "Receiving objects", calculated);
        }
      }
    }
  } else if (outstanding_writes != 0) {
    LOG_INFO << "ostree-pull: Writing objects: " << outstanding_writes;
  } else {
    LOG_INFO << "ostree-pull: Scanning metadata: " << n_scanned_metadata;
    if (mt->progress_cb) {
      mt->progress_cb(mt->target, "Scanning metadata", 0);
    }
  }
}

data::InstallationResult OstreeManager::pull(const boost::filesystem::path &sysroot_path,
                                             const std::string &ostree_server, const KeyManager &keys,
                                             const Uptane::Target &target, const std::function<void()> &pause_cb,
                                             OstreeProgressCb progress_cb) {
  std::string refhash = target.sha256Hash();
  const char *const commit_ids[] = {refhash.c_str()};
  GCancellable *cancellable = nullptr;
  GError *error = nullptr;
  GVariantBuilder builder;
  GVariant *options;
  GObjectUniquePtr<OstreeAsyncProgress> progress = nullptr;

  GObjectUniquePtr<OstreeSysroot> sysroot = OstreeManager::LoadSysroot(sysroot_path);
  GObjectUniquePtr<OstreeRepo> repo = LoadRepo(sysroot.get(), &error);
  if (error != nullptr) {
    LOG_ERROR << "Could not get OSTree repo";
    g_error_free(error);
    return data::InstallationResult(data::ResultCode::Numeric::kInstallFailed, "Could not get OSTree repo");
  }

  GHashTable *ref_list = nullptr;
  if (ostree_repo_list_commit_objects_starting_with(repo.get(), refhash.c_str(), &ref_list, nullptr, &error) != 0) {
    guint length = g_hash_table_size(ref_list);
    g_hash_table_destroy(ref_list);  // OSTree creates the table with destroy notifiers, so no memory leaks expected
    // should never be greater than 1, but use >= for robustness
    if (length >= 1) {
      LOG_DEBUG << "refhash already pulled";
      return data::InstallationResult(true, data::ResultCode::Numeric::kAlreadyProcessed, "Refhash was already pulled");
    }
  }
  if (error != nullptr) {
    g_error_free(error);
    error = nullptr;
  }

  if (!OstreeManager::addRemote(repo.get(), ostree_server, keys)) {
    return data::InstallationResult(data::ResultCode::Numeric::kInstallFailed, "Error adding OSTree remote");
  }

  g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
  g_variant_builder_add(&builder, "{s@v}", "flags", g_variant_new_variant(g_variant_new_int32(0)));

  g_variant_builder_add(&builder, "{s@v}", "refs", g_variant_new_variant(g_variant_new_strv(commit_ids, 1)));

  options = g_variant_builder_end(&builder);

  PullMetaStruct mt(target, pause_cb, std::move(progress_cb));
  progress.reset(ostree_async_progress_new_and_connect(aktualizr_progress_cb, &mt));
  if (ostree_repo_pull_with_options(repo.get(), remote, options, progress.get(), cancellable, &error) == 0) {
    LOG_ERROR << "Error while pulling image: " << error->message;
    data::InstallationResult install_res(data::ResultCode::Numeric::kInstallFailed, error->message);
    g_error_free(error);
    g_variant_unref(options);
    return install_res;
  }
  ostree_async_progress_finish(progress.get());
  g_variant_unref(options);
  return data::InstallationResult(data::ResultCode::Numeric::kOk, "Pulling ostree image was successful");
}

data::InstallationResult OstreeManager::install(const Uptane::Target &target) const {
  const char *opt_osname = nullptr;
  GCancellable *cancellable = nullptr;
  GError *error = nullptr;
  g_autofree char *revision = nullptr;

  if (config.os.size() != 0u) {
    opt_osname = config.os.c_str();
  }

  GObjectUniquePtr<OstreeSysroot> sysroot = OstreeManager::LoadSysroot(config.sysroot);
  GObjectUniquePtr<OstreeRepo> repo = LoadRepo(sysroot.get(), &error);

  if (error != nullptr) {
    LOG_ERROR << "could not get repo";
    g_error_free(error);
    return data::InstallationResult(data::ResultCode::Numeric::kInstallFailed, "could not get repo");
  }

  auto origin = StructGuard<GKeyFile>(
      ostree_sysroot_origin_new_from_refspec(sysroot.get(), target.sha256Hash().c_str()), g_key_file_free);
  if (ostree_repo_resolve_rev(repo.get(), target.sha256Hash().c_str(), FALSE, &revision, &error) == 0) {
    LOG_ERROR << error->message;
    data::InstallationResult install_res(data::ResultCode::Numeric::kInstallFailed, error->message);
    g_error_free(error);
    return install_res;
  }

  OstreeDeployment *merge_deployment = ostree_sysroot_get_merge_deployment(sysroot.get(), opt_osname);
{'fraction_non_alphanumeric': 0.1284769207637949, 'fraction_numerical': 0.0043602465794617355, 'mean_word_length': 4.7143470790378, 'pattern_counts': {'":': 0, '<': 117, '<?xml version=': 0, '>': 79, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1556064', 'n_tokens_mistral': 4372, 'n_tokens_neox': 4253, 'n_words': 817}