Exemplo n.º 1
0
static gboolean
vardict_sort_and_insert_pkgs (GVariantDict *dict,
                              const char   *key_prefix,
                              GUnixFDList  *fd_list,
                              const char *const* pkgs,
                              GError      **error)
{
  g_autoptr(GVariant) fd_idxs = NULL;
  g_autoptr(GPtrArray) repo_pkgs = NULL;

  if (!rpmostree_sort_pkgs_strv (pkgs, fd_list, &repo_pkgs, &fd_idxs, error))
    return FALSE;

  /* for grep: here we insert install-packages/override-replace-packages */
  if (repo_pkgs != NULL && repo_pkgs->len > 0)
    g_variant_dict_insert_value (dict, glnx_strjoina (key_prefix, "-packages"),
      g_variant_new_strv ((const char *const*)repo_pkgs->pdata,
                                              repo_pkgs->len));

  /* for grep: here we insert install-local-packages/override-replace-local-packages */
  if (fd_idxs != NULL)
    g_variant_dict_insert_value (dict, glnx_strjoina (key_prefix, "-local-packages"),
                                 fd_idxs);
  return TRUE;
}
Exemplo n.º 2
0
gboolean
rpmostree_bwrap_run (RpmOstreeBwrap *bwrap,
                     GError **error)
{
  int estatus;
  const char *current_lang = getenv ("LANG");

  g_assert (!bwrap->executed);
  bwrap->executed = TRUE;

  if (!current_lang)
    current_lang = "C";

  {
    const char *lang_var = glnx_strjoina ("LANG=", current_lang);
    /* This is similar to what systemd does, except:
     *  - We drop /usr/local, since scripts shouldn't see it.
     *  - We pull in the current process' LANG, since that's what people
     *    have historically expected from RPM scripts.
     */
    const char *bwrap_env[] = {"PATH=/usr/sbin:/usr/bin",
                               lang_var,
                               NULL};

    /* Add the final NULL */
    g_ptr_array_add (bwrap->argv, NULL);

    const char *errmsg = glnx_strjoina ("Executing bwrap(", bwrap->child_argv0, ")");
    GLNX_AUTO_PREFIX_ERROR (errmsg, error);

    if (!g_spawn_sync (NULL, (char**)bwrap->argv->pdata, (char**) bwrap_env, G_SPAWN_SEARCH_PATH,
                       bwrap_child_setup, bwrap,
                       NULL, NULL, &estatus, error))
      return FALSE;
    if (!g_spawn_check_exit_status (estatus, error))
      return FALSE;
  }

  return TRUE;
}
Exemplo n.º 3
0
static RpmOstreeScriptAction
lookup_script_action (DnfPackage *package,
                      GHashTable *ignored_scripts,
                      const char *scriptdesc)
{
  const char *pkg_script = glnx_strjoina (dnf_package_get_name (package), ".", scriptdesc+1);
  const struct RpmOstreePackageScriptHandler *handler = rpmostree_script_gperf_lookup (pkg_script, strlen (pkg_script));
  if (ignored_scripts && g_hash_table_contains (ignored_scripts, pkg_script))
    return RPMOSTREE_SCRIPT_ACTION_IGNORE;
  if (!handler)
    return RPMOSTREE_SCRIPT_ACTION_DEFAULT;
  return handler->action;
}
Exemplo n.º 4
0
/**
 * ot_log_structured_print_id_v:
 * @message_id: A unique MESSAGE_ID
 * @format: A format string
 *
 * The provided @message_id is a unique MESSAGE_ID (see <ulink url="http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html"> for more information).
 *
 * This function otherwise acts as ot_log_structured_print(), taking
 * @format as a format string.
 */
void
ot_log_structured_print_id_v (const char *message_id,
                              const char *format,
                              ...)
{
  const char *key0 = glnx_strjoina ("MESSAGE_ID=", message_id);
  const char *keys[] = { key0, NULL };
  g_autofree char *msg = NULL;
  va_list args;

  va_start (args, format);
  msg = g_strdup_vprintf (format, args);
  va_end (args);

  ot_log_structured_print (msg, (const char *const *)keys);
}
/*
 * Like symlinkat() but overwrites (atomically) an existing
 * symlink.
 */
static gboolean
symlink_at_replace (const char    *oldpath,
                    int            parent_dfd,
                    const char    *newpath,
                    GCancellable  *cancellable,
                    GError       **error)
{
  gboolean ret = FALSE;
  int res;
  /* Possibly in the future generate a temporary random name here,
   * would need to move "generate a temporary name" code into
   * libglnx or glib?
   */
  const char *temppath = glnx_strjoina (newpath, ".tmp");

  /* Clean up any stale temporary links */ 
  (void) unlinkat (parent_dfd, temppath, 0);

  /* Create the temp link */ 
  do
    res = symlinkat (oldpath, parent_dfd, temppath);
  while (G_UNLIKELY (res == -1 && errno == EINTR));
  if (res == -1)
    {
      glnx_set_error_from_errno (error);
      goto out;
    }

  /* Rename it into place */ 
  do
    res = renameat (parent_dfd, temppath, parent_dfd, newpath);
  while (G_UNLIKELY (res == -1 && errno == EINTR));
  if (res == -1)
    {
      glnx_set_error_from_errno (error);
      goto out;
    }

  ret = TRUE;
 out:
  return ret;
}
gboolean
rpmostree_container_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  int exit_status = EXIT_FAILURE;
  GOptionContext *context = g_option_context_new ("NAME");
  g_auto(ROContainerContext) rocctx_data = RO_CONTAINER_CONTEXT_INIT;
  ROContainerContext *rocctx = &rocctx_data;
  g_autoptr(RpmOstreeInstall) install = NULL;
  const char *name;
  g_autofree char *commit_checksum = NULL;
  g_autofree char *new_commit_checksum = NULL;
  g_autoptr(GVariant) commit = NULL;
  g_autoptr(GVariant) metadata = NULL;
  g_autoptr(GVariant) input_packages_v = NULL;
  g_autoptr(RpmOstreeTreespec) treespec = NULL;
  guint current_version;
  guint new_version;
  g_autofree char *previous_state_sha512 = NULL;
  const char *target_current_root;
  const char *target_new_root;
  
  if (!rpmostree_option_context_parse (context,
                                       assemble_option_entries,
                                       &argc, &argv,
                                       RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD,
                                       cancellable,
                                       NULL,
                                       error))
    goto out;

  if (argc < 1)
    {
      rpmostree_usage_error (context, "NAME must be specified", error);
      goto out;
    }

  name = argv[1];

  if (!roc_context_init (rocctx, error))
    goto out;

  target_current_root = glnx_readlinkat_malloc (rocctx->roots_dfd, name, cancellable, error);
  if (!target_current_root)
    {
      g_prefix_error (error, "Reading app link %s: ", name);
      goto out;
    }

  if (!parse_app_version (target_current_root, &current_version, error))
    goto out;

  { g_autoptr(GVariantDict) metadata_dict = NULL;
    g_autoptr(GVariant) spec_v = NULL;
    g_autoptr(GVariant) previous_sha512_v = NULL;

    if (!ostree_repo_resolve_rev (rocctx->repo, name, FALSE, &commit_checksum, error))
      goto out;

    if (!ostree_repo_load_variant (rocctx->repo, OSTREE_OBJECT_TYPE_COMMIT, commit_checksum,
                                   &commit, error))
      goto out;

    metadata = g_variant_get_child_value (commit, 0);
    metadata_dict = g_variant_dict_new (metadata);

    spec_v = _rpmostree_vardict_lookup_value_required (metadata_dict, "rpmostree.spec",
                                                                 (GVariantType*)"a{sv}", error);
    if (!spec_v)
      goto out;

    treespec = rpmostree_treespec_new (spec_v);

    previous_sha512_v = _rpmostree_vardict_lookup_value_required (metadata_dict,
                                                                  "rpmostree.state-sha512",
                                                                  (GVariantType*)"s", error);
    if (!previous_sha512_v)
      goto out;

    previous_state_sha512 = g_variant_dup_string (previous_sha512_v, NULL);
  }

  new_version = current_version == 0 ? 1 : 0;
  if (new_version == 0)
    target_new_root = glnx_strjoina (name, ".0");
  else
    target_new_root = glnx_strjoina (name, ".1");

  if (!roc_context_prepare_for_root (rocctx, name, treespec, cancellable, error))
    goto out;

  /* --- Downloading metadata --- */
  if (!rpmostree_context_download_metadata (rocctx->ctx, cancellable, error))
    goto out;

  /* --- Resolving dependencies --- */
  if (!rpmostree_context_prepare_install (rocctx->ctx, &install,
                                          cancellable, error))
    goto out;

  { g_autofree char *new_state_sha512 = rpmostree_context_get_state_sha512 (rocctx->ctx);

    if (strcmp (new_state_sha512, previous_state_sha512) == 0)
      {
        g_print ("No changes in inputs to %s (%s)\n", name, commit_checksum);
        exit_status = EXIT_SUCCESS;
        goto out;
      }
  }

  /* --- Download and import as necessary --- */
  if (!rpmostree_context_download_import (rocctx->ctx, install,
                                          cancellable, error))
    goto out;

  { glnx_fd_close int tmpdir_dfd = -1;

    if (!glnx_opendirat (rocctx->userroot_dfd, "tmp", TRUE, &tmpdir_dfd, error))
      goto out;
    
    if (!rpmostree_context_assemble_commit (rocctx->ctx, tmpdir_dfd,
                                            name,
                                            install,
                                            &new_commit_checksum,
                                            cancellable, error))
      goto out;
  }

  g_print ("Checking out %s @ %s...\n", name, new_commit_checksum);

  { OstreeRepoCheckoutOptions opts = { OSTREE_REPO_CHECKOUT_MODE_USER,
                                       OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES, };

    /* For now... to be crash safe we'd need to duplicate some of the
     * boot-uuid/fsync gating at a higher level.
     */
    opts.disable_fsync = TRUE;

    if (!ostree_repo_checkout_tree_at (rocctx->repo, &opts, rocctx->roots_dfd, target_new_root,
                                       new_commit_checksum, cancellable, error))
      goto out;
  }

  g_print ("Checking out %s @ %s...done\n", name, new_commit_checksum);

  if (!symlink_at_replace (target_new_root, rocctx->roots_dfd, name,
                           cancellable, error))
    goto out;

  g_print ("Creating current symlink...done\n");

  exit_status = EXIT_SUCCESS;
 out:
  return exit_status;
}
int
rpmostree_container_builtin_assemble (int             argc,
                                      char          **argv,
                                      GCancellable   *cancellable,
                                      GError        **error)
{
  int exit_status = EXIT_FAILURE;
  GOptionContext *context = g_option_context_new ("NAME [PKGNAME PKGNAME...]");
  g_auto(ROContainerContext) rocctx_data = RO_CONTAINER_CONTEXT_INIT;
  ROContainerContext *rocctx = &rocctx_data;
  g_autoptr(RpmOstreeInstall) install = {0,};
  const char *specpath;
  struct stat stbuf;
  const char *name;
  g_autofree char *commit = NULL;
  const char *target_rootdir;
  g_autoptr(RpmOstreeTreespec) treespec = NULL;
  
  if (!rpmostree_option_context_parse (context,
                                       assemble_option_entries,
                                       &argc, &argv,
                                       RPM_OSTREE_BUILTIN_FLAG_LOCAL_CMD,
                                       cancellable,
                                       NULL,
                                       error))
    goto out;

  if (argc < 1)
    {
      rpmostree_usage_error (context, "SPEC must be specified", error);
      goto out;
    }

  specpath = argv[1];
  treespec = rpmostree_treespec_new_from_path (specpath, error);
  if (!treespec)
    goto out;

  name = rpmostree_treespec_get_ref (treespec);

  if (!roc_context_init (rocctx, error))
    goto out;

  target_rootdir = glnx_strjoina (name, ".0");

  if (fstatat (rocctx->roots_dfd, target_rootdir, &stbuf, AT_SYMLINK_NOFOLLOW) < 0)
    {
      if (errno != ENOENT)
        {
          glnx_set_error_from_errno (error);
          goto out;
        }
    }
  else
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Tree %s already exists", target_rootdir);
      goto out;
    }

  if (!roc_context_prepare_for_root (rocctx, target_rootdir, treespec, cancellable, error))
    goto out;

  /* --- Downloading metadata --- */
  if (!rpmostree_context_download_metadata (rocctx->ctx, cancellable, error))
    goto out;

  /* --- Resolving dependencies --- */
  if (!rpmostree_context_prepare_install (rocctx->ctx, &install, cancellable, error))
    goto out;

  /* --- Download and import as necessary --- */
  if (!rpmostree_context_download_import (rocctx->ctx, install,
                                          cancellable, error))
    goto out;

  { glnx_fd_close int tmpdir_dfd = -1;

    if (!glnx_opendirat (rocctx->userroot_dfd, "tmp", TRUE, &tmpdir_dfd, error))
      goto out;
    
    if (!rpmostree_context_assemble_commit (rocctx->ctx, tmpdir_dfd,
                                            name,
                                            install,
                                            &commit,
                                            cancellable, error))
      goto out;
  }

  g_print ("Checking out %s @ %s...\n", name, commit);

  { OstreeRepoCheckoutOptions opts = { OSTREE_REPO_CHECKOUT_MODE_USER,
                                       OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES, };

    /* For now... to be crash safe we'd need to duplicate some of the
     * boot-uuid/fsync gating at a higher level.
     */
    opts.disable_fsync = TRUE;

    /* Also, what we really want here is some sort of sane lifecycle
     * management with whatever is running in the root.
     */
    if (!glnx_shutil_rm_rf_at (rocctx->roots_dfd, target_rootdir, cancellable, error))
      goto out;

    if (!ostree_repo_checkout_tree_at (rocctx->repo, &opts, rocctx->roots_dfd, target_rootdir,
                                       commit, cancellable, error))
      goto out;
  }

  g_print ("Checking out %s @ %s...done\n", name, commit);

  if (!symlink_at_replace (target_rootdir, rocctx->roots_dfd, name,
                           cancellable, error))
    goto out;

  g_print ("Creating current symlink...done\n");

  exit_status = EXIT_SUCCESS;
 out:
  return exit_status;
}
Exemplo n.º 8
0
static gboolean
run_script_in_bwrap_container (int rootfs_fd,
                               const char *name,
                               const char *scriptdesc,
                               const char *script,
                               GCancellable  *cancellable,
                               GError       **error)
{
  gboolean ret = FALSE;
  int i;
  const char *usr_links[] = {"lib", "lib32", "lib64", "bin", "sbin"};
  int estatus;
  char *rofiles_mnt = strdupa ("/tmp/rofiles-fuse.XXXXXX");
  const char *rofiles_argv[] = { "rofiles-fuse", "./usr", rofiles_mnt, NULL};
  const char *pkg_script = glnx_strjoina (name, ".", scriptdesc+1);
  const char *postscript_name = glnx_strjoina ("/", pkg_script);
  const char *postscript_path_container = glnx_strjoina ("/usr/", postscript_name);
  const char *postscript_path_host;
  gboolean mntpoint_created = FALSE;
  gboolean fuse_mounted = FALSE;
  g_autoptr(GPtrArray) bwrap_argv = g_ptr_array_new ();
  g_autoptr(GPtrArray) bwrap_argv_mallocd = g_ptr_array_new_with_free_func (g_free);
  GSpawnFlags bwrap_spawnflags = G_SPAWN_SEARCH_PATH;
  gboolean created_var_tmp = FALSE;

  if (!glnx_mkdtempat (AT_FDCWD, rofiles_mnt, 0700, error))
    goto out;

  mntpoint_created = TRUE;

  if (!g_spawn_sync (NULL, (char**)rofiles_argv, NULL, G_SPAWN_SEARCH_PATH,
                     child_setup_fchdir, GINT_TO_POINTER (rootfs_fd),
                     NULL, NULL, &estatus, error))
    goto out;
  if (!g_spawn_check_exit_status (estatus, error))
    {
      g_prefix_error (error, "Executing rofiles-fuse: ");
      goto out;
    }

  fuse_mounted = TRUE;

  postscript_path_host = glnx_strjoina (rofiles_mnt, "/", postscript_name);

  /* TODO - Create a pipe and send this to bwrap so it's inside the
   * tmpfs
   */
  if (!g_file_set_contents (postscript_path_host, script, -1, error))
    {
      g_prefix_error (error, "Writing script to %s: ", postscript_path_host);
      goto out;
    }
  if (chmod (postscript_path_host, 0755) != 0)
    {
      g_prefix_error (error, "chmod %s: ", postscript_path_host);
      goto out;
    }

  /* We need to make the mount point in the case where we're doing
   * package layering, since the host `/var` tree is empty.  We
   * *could* point at the real `/var`...but that seems
   * unnecessary/dangerous to me.  Daemons that need to perform data
   * migrations should do them as part of their systemd units and not
   * in %post.
   *
   * Another alternative would be to make a tmpfs with the compat
   * symlinks.
   */
  if (mkdirat (rootfs_fd, "var/tmp", 0755) < 0)
    {
      if (errno == EEXIST)
        ;
      else
        {
          glnx_set_error_from_errno (error);
          goto out;
        }
    }
  else
    created_var_tmp = TRUE;

  add_const_args (bwrap_argv,
                  WITH_BUBBLEWRAP_PATH,
                  "--bind", rofiles_mnt, "/usr",
                  "--dev", "/dev",
                  "--proc", "/proc",
                  "--dir", "/tmp",
                  "--chdir", "/",
                  /* Scripts can see a /var with compat links like alternatives */
                  "--ro-bind", "./var", "/var",
                  /* But no need to access persistent /tmp, so make it /tmp */
                  "--bind", "/tmp", "/var/tmp",
                  /* Allow RPM scripts to change the /etc defaults */
                  "--symlink", "usr/etc", "/etc",
                  "--ro-bind", "/sys/block", "/sys/block",
                  "--ro-bind", "/sys/bus", "/sys/bus",
                  "--ro-bind", "/sys/class", "/sys/class",
                  "--ro-bind", "/sys/dev", "/sys/dev",
                  "--ro-bind", "/sys/devices", "/sys/devices",
                  NULL);

  for (i = 0; i < G_N_ELEMENTS (usr_links); i++)
    {
      const char *subdir = usr_links[i];
      struct stat stbuf;
      char *path;

      if (!(fstatat (rootfs_fd, subdir, &stbuf, AT_SYMLINK_NOFOLLOW) == 0 && S_ISLNK (stbuf.st_mode)))
        continue;

      g_ptr_array_add (bwrap_argv, "--symlink");

      path = g_strconcat ("usr/", subdir, NULL);
      g_ptr_array_add (bwrap_argv_mallocd, path);
      g_ptr_array_add (bwrap_argv, path);

      path = g_strconcat ("/", subdir, NULL);
      g_ptr_array_add (bwrap_argv_mallocd, path);
      g_ptr_array_add (bwrap_argv, path);
    }

  { const char *debugscript = getenv ("RPMOSTREE_DEBUG_SCRIPT");
    if (g_strcmp0 (debugscript, pkg_script) == 0)
      {
        g_ptr_array_add (bwrap_argv, (char*)"/bin/bash");
        bwrap_spawnflags |= G_SPAWN_CHILD_INHERITS_STDIN;
      }
    else
      g_ptr_array_add (bwrap_argv, (char*)postscript_path_container);
  }
  g_ptr_array_add (bwrap_argv, NULL);

  if (!g_spawn_sync (NULL, (char**)bwrap_argv->pdata, NULL, bwrap_spawnflags,
                     child_setup_fchdir, GINT_TO_POINTER (rootfs_fd),
                     NULL, NULL, &estatus, error))
    {
      g_prefix_error (error, "Executing bwrap: ");
      goto out;
    }
  if (!g_spawn_check_exit_status (estatus, error))
    {
      g_prefix_error (error, "Executing bwrap: ");
      goto out;
    }

  ret = TRUE;
 out:
  if (fuse_mounted)
    {
      (void) unlink (postscript_path_host);
      fusermount_cleanup (rofiles_mnt);
    }
  if (mntpoint_created)
    (void) unlinkat (AT_FDCWD, rofiles_mnt, AT_REMOVEDIR);
  if (created_var_tmp)
    (void) unlinkat (rootfs_fd, "var/tmp", AT_REMOVEDIR);
  return ret;
}
Exemplo n.º 9
0
static gboolean
run_script_in_bwrap_container (int rootfs_fd,
                               const char *name,
                               const char *scriptdesc,
                               const char *script,
                               GCancellable  *cancellable,
                               GError       **error)
{
  gboolean ret = FALSE;
  char *rofiles_mnt = strdupa ("/tmp/rofiles-fuse.XXXXXX");
  const char *rofiles_argv[] = { "rofiles-fuse", "./usr", rofiles_mnt, NULL};
  const char *pkg_script = glnx_strjoina (name, ".", scriptdesc+1);
  const char *postscript_name = glnx_strjoina ("/", pkg_script);
  const char *postscript_path_container = glnx_strjoina ("/usr/", postscript_name);
  const char *postscript_path_host;
  gboolean mntpoint_created = FALSE;
  gboolean fuse_mounted = FALSE;
  g_autoptr(GPtrArray) bwrap_argv = g_ptr_array_new ();
  gboolean created_var_tmp = FALSE;

  if (!glnx_mkdtempat (AT_FDCWD, rofiles_mnt, 0700, error))
    goto out;

  mntpoint_created = TRUE;

  if (!rpmostree_run_sync_fchdir_setup ((char**)rofiles_argv, G_SPAWN_SEARCH_PATH,
                                        rootfs_fd, error))
    {
      g_prefix_error (error, "Executing rofiles-fuse: ");
      goto out;
    }

  fuse_mounted = TRUE;

  postscript_path_host = glnx_strjoina (rofiles_mnt, "/", postscript_name);

  /* TODO - Create a pipe and send this to bwrap so it's inside the
   * tmpfs
   */
  if (!g_file_set_contents (postscript_path_host, script, -1, error))
    {
      g_prefix_error (error, "Writing script to %s: ", postscript_path_host);
      goto out;
    }
  if (chmod (postscript_path_host, 0755) != 0)
    {
      g_prefix_error (error, "chmod %s: ", postscript_path_host);
      goto out;
    }

  /* We need to make the mount point in the case where we're doing
   * package layering, since the host `/var` tree is empty.  We
   * *could* point at the real `/var`...but that seems
   * unnecessary/dangerous to me.  Daemons that need to perform data
   * migrations should do them as part of their systemd units and not
   * in %post.
   *
   * Another alternative would be to make a tmpfs with the compat
   * symlinks.
   */
  if (mkdirat (rootfs_fd, "var/tmp", 0755) < 0)
    {
      if (errno == EEXIST)
        ;
      else
        {
          glnx_set_error_from_errno (error);
          goto out;
        }
    }
  else
    created_var_tmp = TRUE;

  bwrap_argv = rpmostree_bwrap_base_argv_new_for_rootfs (rootfs_fd, error);
  if (!bwrap_argv)
    goto out;

  rpmostree_ptrarray_append_strdup (bwrap_argv,
                  "--bind", rofiles_mnt, "/usr",
                  /* Scripts can see a /var with compat links like alternatives */
                  "--ro-bind", "./var", "/var",
                  /* But no need to access persistent /tmp, so make it /tmp */
                  "--bind", "/tmp", "/var/tmp",
                  /* Allow RPM scripts to change the /etc defaults */
                  "--symlink", "usr/etc", "/etc",
                  NULL);

  g_ptr_array_add (bwrap_argv, g_strdup (postscript_path_container));
  /* http://www.rpm.org/max-rpm/s1-rpm-inside-scripts.html#S3-RPM-INSIDE-PRE-SCRIPT */
  g_ptr_array_add (bwrap_argv, g_strdup ("1"));
  g_ptr_array_add (bwrap_argv, NULL);

  if (!rpmostree_run_bwrap_sync ((char**)bwrap_argv->pdata, rootfs_fd, error))
    {
      g_prefix_error (error, "Executing bwrap: ");
      goto out;
    }

  ret = TRUE;
 out:
  if (fuse_mounted)
    {
      (void) unlink (postscript_path_host);
      fusermount_cleanup (rofiles_mnt);
    }
  if (mntpoint_created)
    (void) unlinkat (AT_FDCWD, rofiles_mnt, AT_REMOVEDIR);
  if (created_var_tmp)
    (void) unlinkat (rootfs_fd, "var/tmp", AT_REMOVEDIR);
  return ret;
}
Exemplo n.º 10
0
static gboolean
_ostree_repo_list_refs_internal (OstreeRepo       *self,
                                 gboolean         cut_prefix,
                                 const char       *refspec_prefix,
                                 GHashTable      **out_all_refs,
                                 GCancellable     *cancellable,
                                 GError          **error)
{
  g_autoptr(GHashTable) ret_all_refs = NULL;
  g_autofree char *remote = NULL;
  g_autofree char *ref_prefix = NULL;

  ret_all_refs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);

  if (refspec_prefix)
    {
      struct stat stbuf;
      const char *prefix_path;
      const char *path;

      if (!ostree_parse_refspec (refspec_prefix, &remote, &ref_prefix, error))
        return FALSE;

      if (remote)
        {
          prefix_path = glnx_strjoina ("refs/remotes/", remote, "/");
          path = glnx_strjoina (prefix_path, ref_prefix);
        }
      else
        {
          prefix_path = "refs/heads/";
          path = glnx_strjoina (prefix_path, ref_prefix);
        }

      if (fstatat (self->repo_dir_fd, path, &stbuf, 0) < 0)
        {
          if (errno != ENOENT)
            return glnx_throw_errno (error);
        }
      else
        {
          if (S_ISDIR (stbuf.st_mode))
            {
              glnx_fd_close int base_fd = -1;
              g_autoptr(GString) base_path = g_string_new ("");
              if (!cut_prefix)
                g_string_printf (base_path, "%s/", ref_prefix);

              if (!glnx_opendirat (self->repo_dir_fd, cut_prefix ? path : prefix_path, TRUE, &base_fd, error))
                return FALSE;

              if (!enumerate_refs_recurse (self, remote, base_fd, base_path,
                                           base_fd, cut_prefix ? "." : ref_prefix,
                                           ret_all_refs, cancellable, error))
                return FALSE;
            }
          else
            {
              glnx_fd_close int prefix_dfd = -1;

              if (!glnx_opendirat (self->repo_dir_fd, prefix_path, TRUE, &prefix_dfd, error))
                return FALSE;

              if (!add_ref_to_set (remote, prefix_dfd, ref_prefix, ret_all_refs,
                                   cancellable, error))
                return FALSE;
            }
        }
    }
  else
    {
      g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
      g_autoptr(GString) base_path = g_string_new ("");
      glnx_fd_close int refs_heads_dfd = -1;

      if (!glnx_opendirat (self->repo_dir_fd, "refs/heads", TRUE, &refs_heads_dfd, error))
        return FALSE;

      if (!enumerate_refs_recurse (self, NULL, refs_heads_dfd, base_path,
                                   refs_heads_dfd, ".",
                                   ret_all_refs, cancellable, error))
        return FALSE;

      g_string_truncate (base_path, 0);

      if (!glnx_dirfd_iterator_init_at (self->repo_dir_fd, "refs/remotes", TRUE, &dfd_iter, error))
        return FALSE;

      while (TRUE)
        {
          struct dirent *dent;
          glnx_fd_close int remote_dfd = -1;

          if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error))
            return FALSE;
          if (!dent)
            break;

          if (dent->d_type != DT_DIR)
            continue;

          if (!glnx_opendirat (dfd_iter.fd, dent->d_name, TRUE, &remote_dfd, error))
            return FALSE;

          if (!enumerate_refs_recurse (self, dent->d_name, remote_dfd, base_path,
                                       remote_dfd, ".",
                                       ret_all_refs,
                                       cancellable, error))
            return FALSE;
        }
    }

  ot_transfer_out_value (out_all_refs, &ret_all_refs);
  return TRUE;
}
Exemplo n.º 11
0
static gboolean
resolve_refspec (OstreeRepo     *self,
                 const char     *remote,
                 const char     *ref,
                 gboolean        allow_noent,
                 gboolean        fallback_remote,
                 char          **out_rev,
                 GError        **error)
{
  __attribute__((unused)) GCancellable *cancellable = NULL;
  g_autofree char *ret_rev = NULL;
  glnx_fd_close int target_fd = -1;

  g_return_val_if_fail (ref != NULL, FALSE);

  /* We intentionally don't allow a ref that looks like a checksum */
  if (ostree_validate_checksum_string (ref, NULL))
    {
      ret_rev = g_strdup (ref);
    }
  else if (remote != NULL)
    {
      const char *remote_ref = glnx_strjoina ("refs/remotes/", remote, "/", ref);

      if (!ot_openat_ignore_enoent (self->repo_dir_fd, remote_ref, &target_fd, error))
        return FALSE;
    }
  else
    {
      const char *local_ref = glnx_strjoina ("refs/heads/", ref);

      if (!ot_openat_ignore_enoent (self->repo_dir_fd, local_ref, &target_fd, error))
        return FALSE;

      if (target_fd == -1 && fallback_remote)
        {
          local_ref = glnx_strjoina ("refs/remotes/", ref);

          if (!ot_openat_ignore_enoent (self->repo_dir_fd, local_ref, &target_fd, error))
            return FALSE;

          if (target_fd == -1)
            {
              if (!find_ref_in_remotes (self, ref, &target_fd, error))
                return FALSE;
            }
        }
    }

  if (target_fd != -1)
    {
      ret_rev = glnx_fd_readall_utf8 (target_fd, NULL, NULL, error);
      if (!ret_rev)
        {
          g_prefix_error (error, "Couldn't open ref '%s': ", ref);
          return FALSE;
        }

      g_strchomp (ret_rev);
      if (!ostree_validate_checksum_string (ret_rev, error))
        return FALSE;
    }
  else
    {
      if (!resolve_refspec_fallback (self, remote, ref, allow_noent, fallback_remote,
                                     &ret_rev, cancellable, error))
        return FALSE;
    }

  ot_transfer_out_value (out_rev, &ret_rev);
  return TRUE;
}