Ejemplo n.º 1
0
gboolean
_ostree_sysroot_read_current_subbootversion (OstreeSysroot *self,
                                             int            bootversion,
                                             int           *out_subbootversion,
                                             GCancellable  *cancellable,
                                             GError       **error)
{
  gboolean ret = FALSE;
  struct stat stbuf;
  g_autofree char *ostree_bootdir_name = g_strdup_printf ("ostree/boot.%d", bootversion);

  if (!ensure_sysroot_fd (self, error))
    goto out;

  if (fstatat (self->sysroot_fd, ostree_bootdir_name, &stbuf, AT_SYMLINK_NOFOLLOW) != 0)
    {
      if (errno == ENOENT)
        *out_subbootversion = 0;
      else
        {
          glnx_set_error_from_errno (error);
          goto out;
        }
    }
  else
    {
      g_autofree char *current_subbootdir_name = NULL;

      current_subbootdir_name = glnx_readlinkat_malloc (self->sysroot_fd, ostree_bootdir_name,
                                                        cancellable, error);
      if (!current_subbootdir_name)
        goto out;
                                                
      if (g_str_has_suffix (current_subbootdir_name, ".0"))
        *out_subbootversion = 0;
      else if (g_str_has_suffix (current_subbootdir_name, ".1"))
        *out_subbootversion = 1;
      else
        {
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                       "Invalid target '%s' in %s",
                       current_subbootdir_name,
                       ostree_bootdir_name);
          goto out;
        }
    }

  ret = TRUE;
 out:
  return ret;
}
Ejemplo n.º 2
0
static gboolean
read_current_bootversion (OstreeSysroot *self,
                          int           *out_bootversion,
                          GCancellable  *cancellable,
                          GError       **error)
{
  gboolean ret = FALSE;
  int ret_bootversion;
  struct stat stbuf;

  if (fstatat (self->sysroot_fd, "boot/loader", &stbuf, AT_SYMLINK_NOFOLLOW) != 0)
    {
      if (errno != ENOENT)
        {
          glnx_set_error_from_errno (error);
          goto out;
        }
      ret_bootversion = 0;
    }
  else
    {
      g_autofree char *target = NULL;

      if (!S_ISLNK (stbuf.st_mode))
        {
          g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                               "Not a symbolic link: boot/loader");
          goto out;
        }

      target = glnx_readlinkat_malloc (self->sysroot_fd, "boot/loader", cancellable, error);
      if (!target)
        goto out;
      if (g_strcmp0 (target, "loader.0") == 0)
        ret_bootversion = 0;
      else if (g_strcmp0 (target, "loader.1") == 0)
        ret_bootversion = 1;
      else
        {
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                       "Invalid target '%s' in boot/loader", target);
          goto out;
        }
    }

  ret = TRUE;
  *out_bootversion = ret_bootversion;
 out:
  return ret;
}
Ejemplo n.º 3
0
static gboolean
convert_var_to_tmpfiles_d_recurse (GOutputStream *tmpfiles_out,
                                   int            dfd,
                                   GString       *prefix,
                                   GCancellable  *cancellable,
                                   GError       **error)
{
    gboolean ret = FALSE;
    g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
    gsize bytes_written;

    if (!glnx_dirfd_iterator_init_at (dfd, prefix->str + 1, TRUE, &dfd_iter, error))
        goto out;

    while (TRUE)
    {
        struct dirent *dent = NULL;
        GString *tmpfiles_d_buf;
        gs_free char *tmpfiles_d_line = NULL;
        char filetype_c;
        gs_free char *relpath = NULL;

        if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, cancellable, error))
            goto out;

        if (!dent)
            break;

        switch (dent->d_type)
        {
        case DT_DIR:
            filetype_c = 'd';
            break;
        case DT_LNK:
            filetype_c = 'L';
            break;
        default:
            g_print ("Ignoring non-directory/non-symlink '%s'\n",
                     dent->d_name);
            continue;
        }

        tmpfiles_d_buf = g_string_new ("");
        g_string_append_c (tmpfiles_d_buf, filetype_c);
        g_string_append_c (tmpfiles_d_buf, ' ');
        g_string_append (tmpfiles_d_buf, prefix->str);
        g_string_append_c (tmpfiles_d_buf, '/');
        g_string_append (tmpfiles_d_buf, dent->d_name);

        if (filetype_c == 'd')
        {
            struct stat stbuf;

            if (TEMP_FAILURE_RETRY (fstatat (dfd_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW)) != 0)
            {
                glnx_set_error_from_errno (error);
                goto out;
            }

            g_string_append_printf (tmpfiles_d_buf, " 0%02o", stbuf.st_mode & ~S_IFMT);
            g_string_append_printf (tmpfiles_d_buf, " %d %d - -", stbuf.st_uid, stbuf.st_gid);

            /* Push prefix */
            g_string_append_c (prefix, '/');
            g_string_append (prefix, dent->d_name);

            if (!convert_var_to_tmpfiles_d_recurse (tmpfiles_out, dfd, prefix,
                                                    cancellable, error))
                goto out;

            /* Pop prefix */
            {
                char *r = memrchr (prefix->str, '/', prefix->len);
                g_assert (r != NULL);
                g_string_truncate (prefix, r - prefix->str);
            }
        }
        else
        {
            g_autofree char *link = glnx_readlinkat_malloc (dfd_iter.fd, dent->d_name, cancellable, error);
            if (!link)
                goto out;
            g_string_append (tmpfiles_d_buf, " - - - - ");
            g_string_append (tmpfiles_d_buf, link);
        }

        g_string_append_c (tmpfiles_d_buf, '\n');

        tmpfiles_d_line = g_string_free (tmpfiles_d_buf, FALSE);

        if (!g_output_stream_write_all (tmpfiles_out, tmpfiles_d_line,
                                        strlen (tmpfiles_d_line), &bytes_written,
                                        cancellable, 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;
}
Ejemplo n.º 5
0
static gboolean
parse_deployment (OstreeSysroot       *self,
                  const char          *boot_link,
                  OstreeDeployment   **out_deployment,
                  GCancellable        *cancellable,
                  GError             **error)
{
  gboolean ret = FALSE;
  const char *relative_boot_link;
  glnx_unref_object OstreeDeployment *ret_deployment = NULL;
  int entry_boot_version;
  int treebootserial = -1;
  int deployserial = -1;
  g_autofree char *osname = NULL;
  g_autofree char *bootcsum = NULL;
  g_autofree char *treecsum = NULL;
  glnx_fd_close int deployment_dfd = -1;
  const char *deploy_basename;
  g_autofree char *treebootserial_target = NULL;
  g_autofree char *deploy_dir = NULL;
  GKeyFile *origin = NULL;

  if (!ensure_sysroot_fd (self, error))
    goto out;
      
  if (!parse_bootlink (boot_link, &entry_boot_version,
                       &osname, &bootcsum, &treebootserial,
                       error))
    goto out;

  relative_boot_link = boot_link;
  if (*relative_boot_link == '/')
    relative_boot_link++;

  treebootserial_target = glnx_readlinkat_malloc (self->sysroot_fd, relative_boot_link,
                                                  cancellable, error);
  if (!treebootserial_target)
    goto out;

  deploy_basename = glnx_basename (treebootserial_target);

  if (!_ostree_sysroot_parse_deploy_path_name (deploy_basename,
                                               &treecsum, &deployserial, error))
    goto out;

  if (!glnx_opendirat (self->sysroot_fd, relative_boot_link, TRUE,
                       &deployment_dfd, error))
    goto out;

  if (!parse_origin (self, deployment_dfd, deploy_basename, &origin,
                     cancellable, error))
    goto out;

  ret_deployment = ostree_deployment_new (-1, osname, treecsum, deployserial,
                                          bootcsum, treebootserial);
  if (origin)
    ostree_deployment_set_origin (ret_deployment, origin);

  ret = TRUE;
  gs_transfer_out_value (out_deployment, &ret_deployment);
 out:
  if (origin)
    g_key_file_unref (origin);
  return ret;
}