Example #1
0
static void
dump_summary_ref (const char   *ref_name,
                  guint64       commit_size,
                  GVariant     *csum_v,
                  GVariantIter *metadata)
{
  const guchar *csum_bytes;
  GError *csum_error = NULL;
  g_autofree char *size = NULL;
  GVariant *value;
  char *key;

  g_print ("* %s\n", ref_name);

  size = g_format_size (commit_size);
  g_print ("    Latest Commit (%s):\n", size);

  csum_bytes = ostree_checksum_bytes_peek_validate (csum_v, &csum_error);
  if (csum_error == NULL)
    {
      char csum[OSTREE_SHA256_STRING_LEN+1];

      ostree_checksum_inplace_from_bytes (csum_bytes, csum);
      g_print ("      %s\n", csum);
    }
  else
    {
      g_print ("      %s\n", csum_error->message);
      g_clear_error (&csum_error);
    }

  while (g_variant_iter_loop (metadata, "{sv}", &key, &value))
    {
      g_autofree gchar *value_str = NULL;
      const gchar *pretty_key = NULL;

      if (g_strcmp0 (key, OSTREE_COMMIT_TIMESTAMP) == 0)
        {
          pretty_key = "Timestamp";
          value_str = uint64_secs_to_iso8601 (GUINT64_FROM_BE (g_variant_get_uint64 (value)));
        }
      else
        {
          value_str = g_variant_print (value, FALSE);
        }

      /* Print out. */
      if (pretty_key != NULL)
        g_print ("    %s (%s): %s\n", pretty_key, key, value_str);
      else
        g_print ("    %s: %s\n", key, value_str);
    }
}
/**
 * ostree_repo_commit_traverse_iter_init_commit:
 * @iter: An iter
 * @repo: A repo
 * @commit: Variant of type %OSTREE_OBJECT_TYPE_COMMIT
 * @flags: Flags
 * @error: Error
 *
 * Initialize (in place) an iterator over the root of a commit object.
 */
gboolean
ostree_repo_commit_traverse_iter_init_commit (OstreeRepoCommitTraverseIter   *iter,
                                              OstreeRepo                     *repo,
                                              GVariant                       *commit,
                                              OstreeRepoCommitTraverseFlags   flags,
                                              GError                        **error)
{
  struct _OstreeRepoRealCommitTraverseIter *real =
    (struct _OstreeRepoRealCommitTraverseIter*)iter;
  gboolean ret = FALSE;
  const guchar *csum;
  g_autoptr(GVariant) meta_csum_bytes = NULL;
  g_autoptr(GVariant) content_csum_bytes = NULL;

  memset (real, 0, sizeof (*real));
  real->initialized = TRUE;
  real->repo = g_object_ref (repo);
  real->commit = g_variant_ref (commit);
  real->current_dir = NULL;
  real->idx = 0;

  g_variant_get_child (commit, 6, "@ay", &content_csum_bytes);
  csum = ostree_checksum_bytes_peek_validate (content_csum_bytes, error);
  if (!csum)
    goto out;
  ostree_checksum_inplace_from_bytes (csum, real->checksum_content);

  g_variant_get_child (commit, 7, "@ay", &meta_csum_bytes);
  csum = ostree_checksum_bytes_peek_validate (meta_csum_bytes, error);
  if (!csum)
    goto out;
  ostree_checksum_inplace_from_bytes (csum, real->checksum_meta);

  ret = TRUE;
 out:
  return ret;
}
/**
 * ostree_repo_static_delta_execute_offline:
 * @self: Repo
 * @dir_or_file: Path to a directory containing static delta data, or directly to the superblock
 * @skip_validation: If %TRUE, assume data integrity
 * @cancellable: Cancellable
 * @error: Error
 *
 * Given a directory representing an already-downloaded static delta
 * on disk, apply it, generating a new commit.  The directory must be
 * named with the form "FROM-TO", where both are checksums, and it
 * must contain a file named "superblock", along with at least one part.
 */
gboolean
ostree_repo_static_delta_execute_offline (OstreeRepo                    *self,
                                          GFile                         *dir_or_file,
                                          gboolean                       skip_validation,
                                          GCancellable                  *cancellable,
                                          GError                      **error)
{
  gboolean ret = FALSE;
  guint i, n;
  const char *dir_or_file_path = NULL;
  glnx_fd_close int meta_fd = -1;
  glnx_fd_close int dfd = -1;
  g_autoptr(GVariant) meta = NULL;
  g_autoptr(GVariant) headers = NULL;
  g_autoptr(GVariant) metadata = NULL;
  g_autoptr(GVariant) fallback = NULL;
  g_autofree char *to_checksum = NULL;
  g_autofree char *from_checksum = NULL;
  g_autofree char *basename = NULL;

  dir_or_file_path = gs_file_get_path_cached (dir_or_file);

  /* First, try opening it as a directory */
  dfd = glnx_opendirat_with_errno (AT_FDCWD, dir_or_file_path, TRUE);
  if (dfd < 0)
    {
      if (errno != ENOTDIR)
        {
          glnx_set_error_from_errno (error);
          goto out;
        }
      else
        {
          g_autofree char *dir = dirname (g_strdup (dir_or_file_path));
          basename = g_path_get_basename (dir_or_file_path);

          if (!glnx_opendirat (AT_FDCWD, dir, TRUE, &dfd, error))
            goto out;
        }
    }
  else
    basename = g_strdup ("superblock");

  meta_fd = openat (dfd, basename, O_RDONLY | O_CLOEXEC);
  if (meta_fd < 0)
    {
      glnx_set_error_from_errno (error);
      goto out;
    }
  
  if (!ot_util_variant_map_fd (meta_fd, 0, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT),
                               FALSE, &meta, error))
    goto out;

  /* Parsing OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT */

  metadata = g_variant_get_child_value (meta, 0);

  /* Write the to-commit object */
  {
    g_autoptr(GVariant) to_csum_v = NULL;
    g_autoptr(GVariant) from_csum_v = NULL;
    g_autoptr(GVariant) to_commit = NULL;
    gboolean have_to_commit;
    gboolean have_from_commit;

    to_csum_v = g_variant_get_child_value (meta, 3);
    if (!ostree_validate_structureof_csum_v (to_csum_v, error))
      goto out;
    to_checksum = ostree_checksum_from_bytes_v (to_csum_v);

    from_csum_v = g_variant_get_child_value (meta, 2);
    if (g_variant_n_children (from_csum_v) > 0)
      {
        if (!ostree_validate_structureof_csum_v (from_csum_v, error))
          goto out;
        from_checksum = ostree_checksum_from_bytes_v (from_csum_v);

        if (!ostree_repo_has_object (self, OSTREE_OBJECT_TYPE_COMMIT, from_checksum,
                                     &have_from_commit, cancellable, error))
          goto out;

        if (!have_from_commit)
          {
            g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                         "Commit %s, which is the delta source, is not in repository", from_checksum);
            goto out;
          }
      }

    if (!ostree_repo_has_object (self, OSTREE_OBJECT_TYPE_COMMIT, to_checksum,
                                 &have_to_commit, cancellable, error))
      goto out;
    
    if (!have_to_commit)
      {
        g_autofree char *detached_path = _ostree_get_relative_static_delta_path (from_checksum, to_checksum, "commitmeta");
        g_autoptr(GVariant) detached_data = NULL;

        detached_data = g_variant_lookup_value (metadata, detached_path, G_VARIANT_TYPE("a{sv}"));
        if (detached_data && !ostree_repo_write_commit_detached_metadata (self,
                                                                          to_checksum,
                                                                          detached_data,
                                                                          cancellable,
                                                                          error))
          goto out;

        to_commit = g_variant_get_child_value (meta, 4);
        if (!ostree_repo_write_metadata (self, OSTREE_OBJECT_TYPE_COMMIT,
                                         to_checksum, to_commit, NULL,
                                         cancellable, error))
          goto out;
      }
  }

  fallback = g_variant_get_child_value (meta, 7);
  if (g_variant_n_children (fallback) > 0)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Cannot execute delta offline: contains nonempty http fallback entries");
      goto out;
    }

  headers = g_variant_get_child_value (meta, 6);
  n = g_variant_n_children (headers);
  for (i = 0; i < n; i++)
    {
      guint32 version;
      guint64 size;
      guint64 usize;
      const guchar *csum;
      char checksum[OSTREE_SHA256_STRING_LEN+1];
      gboolean have_all;
      g_autoptr(GInputStream) part_in = NULL;
      g_autoptr(GVariant) inline_part_data = NULL;
      g_autoptr(GVariant) header = NULL;
      g_autoptr(GVariant) csum_v = NULL;
      g_autoptr(GVariant) objects = NULL;
      g_autoptr(GVariant) part = NULL;
      g_autofree char *deltapart_path = NULL;
      OstreeStaticDeltaOpenFlags delta_open_flags = 
        skip_validation ? OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM : 0;

      header = g_variant_get_child_value (headers, i);
      g_variant_get (header, "(u@aytt@ay)", &version, &csum_v, &size, &usize, &objects);

      if (version > OSTREE_DELTAPART_VERSION)
        {
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                       "Delta part has too new version %u", version);
          goto out;
        }

      if (!_ostree_repo_static_delta_part_have_all_objects (self, objects, &have_all,
                                                            cancellable, error))
        goto out;

      /* If we already have these objects, don't bother executing the
       * static delta.
       */
      if (have_all)
        continue;

      csum = ostree_checksum_bytes_peek_validate (csum_v, error);
      if (!csum)
        goto out;
      ostree_checksum_inplace_from_bytes (csum, checksum);

      deltapart_path =
        _ostree_get_relative_static_delta_part_path (from_checksum, to_checksum, i);

      inline_part_data = g_variant_lookup_value (metadata, deltapart_path, G_VARIANT_TYPE("(yay)"));
      if (inline_part_data)
        {
          g_autoptr(GBytes) inline_part_bytes = g_variant_get_data_as_bytes (inline_part_data);
          part_in = g_memory_input_stream_new_from_bytes (inline_part_bytes);

          /* For inline parts, we don't checksum, because it's
           * included with the metadata, so we're not trying to
           * protect against MITM or such.  Non-security related
           * checksums should be done at the underlying storage layer.
           */
          delta_open_flags |= OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM;

          if (!_ostree_static_delta_part_open (part_in, inline_part_bytes, 
                                               delta_open_flags,
                                               NULL,
                                               &part,
                                               cancellable, error))
            goto out;
        }
      else
        {
          g_autofree char *relpath = g_strdup_printf ("%u", i); /* TODO avoid malloc here */
          glnx_fd_close int part_fd = openat (dfd, relpath, O_RDONLY | O_CLOEXEC);
          if (part_fd < 0)
            {
              glnx_set_error_from_errno (error);
              g_prefix_error (error, "Opening deltapart '%s': ", deltapart_path);
              goto out;
            }

          part_in = g_unix_input_stream_new (part_fd, FALSE);

          if (!_ostree_static_delta_part_open (part_in, NULL, 
                                               delta_open_flags,
                                               checksum,
                                               &part,
                                               cancellable, error))
            goto out;
        }

      if (!_ostree_static_delta_part_execute (self, objects, part, skip_validation,
                                              NULL, cancellable, error))
        {
          g_prefix_error (error, "Executing delta part %i: ", i);
          goto out;
        }
    }

  ret = TRUE;
 out:
  return ret;
}
/**
 * ostree_repo_commit_traverse_iter_next:
 * @iter: An iter
 * @cancellable: Cancellable
 * @error: Error
 *
 * Step the interator to the next item.  Files will be returned first,
 * then subdirectories.  Call this in a loop; upon encountering
 * %OSTREE_REPO_COMMIT_ITER_RESULT_END, there will be no more files or
 * directories.  If %OSTREE_REPO_COMMIT_ITER_RESULT_DIR is returned,
 * then call ostree_repo_commit_traverse_iter_get_dir() to retrieve
 * data for that directory.  Similarly, if
 * %OSTREE_REPO_COMMIT_ITER_RESULT_FILE is returned, call
 * ostree_repo_commit_traverse_iter_get_file().
 * 
 * If %OSTREE_REPO_COMMIT_ITER_RESULT_ERROR is returned, it is a
 * program error to call any further API on @iter except for
 * ostree_repo_commit_traverse_iter_clear().
 */
OstreeRepoCommitIterResult
ostree_repo_commit_traverse_iter_next (OstreeRepoCommitTraverseIter *iter,
                                       GCancellable                 *cancellable,
                                       GError                      **error)
{
  struct _OstreeRepoRealCommitTraverseIter *real =
    (struct _OstreeRepoRealCommitTraverseIter*)iter;
  OstreeRepoCommitIterResult res = OSTREE_REPO_COMMIT_ITER_RESULT_ERROR;

  if (!real->current_dir)
    {
      if (!ostree_repo_load_variant (real->repo, OSTREE_OBJECT_TYPE_DIR_TREE,
                                     real->checksum_content,
                                     &real->current_dir,
                                     error))
        goto out;
      res = OSTREE_REPO_COMMIT_ITER_RESULT_DIR;
    }
  else
    {
      guint nfiles;
      guint ndirs;
      guint idx;
      const guchar *csum;
      g_autoptr(GVariant) content_csum_v = NULL;
      g_autoptr(GVariant) meta_csum_v = NULL;
      g_autoptr(GVariant) files_variant = NULL;
      g_autoptr(GVariant) dirs_variant = NULL;

      files_variant = g_variant_get_child_value (real->current_dir, 0);
      dirs_variant = g_variant_get_child_value (real->current_dir, 1);

      nfiles = g_variant_n_children (files_variant);
      ndirs = g_variant_n_children (dirs_variant);
      if (real->idx < nfiles)
        {
          idx = real->idx;
          g_variant_get_child (files_variant, idx, "(&s@ay)",
                               &real->name,
                               &content_csum_v);

          csum = ostree_checksum_bytes_peek_validate (content_csum_v, error);
          if (!csum)
            goto out;
          ostree_checksum_inplace_from_bytes (csum, real->checksum_content);

          res = OSTREE_REPO_COMMIT_ITER_RESULT_FILE;

          real->idx++;
        }
      else if (real->idx < nfiles + ndirs)
        {
          idx = real->idx - nfiles;

          g_variant_get_child (dirs_variant, idx, "(&s@ay@ay)",
                               &real->name, &content_csum_v, &meta_csum_v);

          csum = ostree_checksum_bytes_peek_validate (content_csum_v, error);
          if (!csum)
            goto out;
          ostree_checksum_inplace_from_bytes (csum, real->checksum_content);

          csum = ostree_checksum_bytes_peek_validate (meta_csum_v, error);
          if (!csum)
            goto out;
          ostree_checksum_inplace_from_bytes (csum, real->checksum_meta);
          
          res = OSTREE_REPO_COMMIT_ITER_RESULT_DIR;

          real->idx++;
        }
      else
        res = OSTREE_REPO_COMMIT_ITER_RESULT_END;
    }
  
  real->state = res;
 out:
  return res;
}
/**
 * ostree_repo_static_delta_execute_offline:
 * @self: Repo
 * @dir: Path to a directory containing static delta data
 * @skip_validation: If %TRUE, assume data integrity
 * @cancellable: Cancellable
 * @error: Error
 *
 * Given a directory representing an already-downloaded static delta
 * on disk, apply it, generating a new commit.  The directory must be
 * named with the form "FROM-TO", where both are checksums, and it
 * must contain a file named "superblock", along with at least one part.
 */
gboolean
ostree_repo_static_delta_execute_offline (OstreeRepo                    *self,
                                          GFile                         *dir,
                                          gboolean                       skip_validation,
                                          GCancellable                  *cancellable,
                                          GError                      **error)
{
  gboolean ret = FALSE;
  guint i, n;
  gs_unref_object GFile *meta_file = g_file_get_child (dir, "superblock");
  gs_unref_variant GVariant *meta = NULL;
  gs_unref_variant GVariant *headers = NULL;
  gs_unref_variant GVariant *fallback = NULL;

  if (!ot_util_variant_map (meta_file, G_VARIANT_TYPE (OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT),
                            FALSE, &meta, error))
    goto out;

  /* Parsing OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT */

  /* Write the to-commit object */
  {
    gs_unref_variant GVariant *to_csum_v = NULL;
    gs_free char *to_checksum = NULL;
    gs_unref_variant GVariant *to_commit = NULL;
    gboolean have_to_commit;

    to_csum_v = g_variant_get_child_value (meta, 3);
    if (!ostree_validate_structureof_csum_v (to_csum_v, error))
      goto out;
    to_checksum = ostree_checksum_from_bytes_v (to_csum_v);

    if (!ostree_repo_has_object (self, OSTREE_OBJECT_TYPE_COMMIT, to_checksum,
                                 &have_to_commit, cancellable, error))
      goto out;
    
    if (!have_to_commit)
      {
        to_commit = g_variant_get_child_value (meta, 4);
        if (!ostree_repo_write_metadata (self, OSTREE_OBJECT_TYPE_COMMIT,
                                         to_checksum, to_commit, NULL,
                                         cancellable, error))
          goto out;
      }
  }

  fallback = g_variant_get_child_value (meta, 7);
  if (g_variant_n_children (fallback) > 0)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                   "Cannot execute delta offline: contains nonempty http fallback entries");
      goto out;
    }

  headers = g_variant_get_child_value (meta, 6);
  n = g_variant_n_children (headers);
  for (i = 0; i < n; i++)
    {
      guint64 size;
      guint64 usize;
      const guchar *csum;
      gboolean have_all;
      gs_unref_variant GVariant *header = NULL;
      gs_unref_variant GVariant *csum_v = NULL;
      gs_unref_variant GVariant *objects = NULL;
      gs_unref_object GFile *part_path = NULL;
      gs_unref_object GInputStream *raw_in = NULL;
      gs_unref_object GInputStream *in = NULL;

      header = g_variant_get_child_value (headers, i);
      g_variant_get (header, "(@aytt@ay)", &csum_v, &size, &usize, &objects);

      if (!_ostree_repo_static_delta_part_have_all_objects (self, objects, &have_all,
                                                            cancellable, error))
        goto out;

      /* If we already have these objects, don't bother executing the
       * static delta.
       */
      if (have_all)
        continue;

      csum = ostree_checksum_bytes_peek_validate (csum_v, error);
      if (!csum)
        goto out;

      part_path = ot_gfile_resolve_path_printf (dir, "%u", i);

      in = (GInputStream*)g_file_read (part_path, cancellable, error);
      if (!in)
        goto out;

      if (!skip_validation)
        {
          gs_free char *expected_checksum = ostree_checksum_from_bytes (csum);
          if (!_ostree_static_delta_part_validate (self, part_path, i,
                                                   expected_checksum,
                                                   cancellable, error))
            goto out;
        }

      {
        GMappedFile *mfile = gs_file_map_noatime (part_path, cancellable, error);
        gs_unref_bytes GBytes *bytes = NULL;

        if (!mfile)
          goto out;

        bytes = g_mapped_file_get_bytes (mfile);
        g_mapped_file_unref (mfile);
        
        if (!_ostree_static_delta_part_execute (self, objects, bytes,
                                                cancellable, error))
          {
            g_prefix_error (error, "executing delta part %i: ", i);
            goto out;
          }
      }
    }

  ret = TRUE;
 out:
  return ret;
}
Example #6
0
/**
 * ostree_repo_remote_list_refs:
 * @self: Repo
 * @remote_name: Name of the remote.
 * @out_all_refs: (out) (element-type utf8 utf8): Mapping from ref to checksum
 * @cancellable: Cancellable
 * @error: Error
 *
 */
gboolean
ostree_repo_remote_list_refs (OstreeRepo       *self,
                              const char       *remote_name,
                              GHashTable      **out_all_refs,
                              GCancellable     *cancellable,
                              GError          **error)
{
  g_autoptr(GBytes) summary_bytes = NULL;
  g_autoptr(GHashTable) ret_all_refs = NULL;

  if (!ostree_repo_remote_fetch_summary (self, remote_name,
                                         &summary_bytes, NULL,
                                         cancellable, error))
    return FALSE;

  if (summary_bytes == NULL)
    {
      return glnx_throw (error, "Remote refs not available; server has no summary file");
    }
  else
    {
      g_autoptr(GVariant) summary = NULL;
      g_autoptr(GVariant) ref_map = NULL;
      GVariantIter iter;
      GVariant *child;
      ret_all_refs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);

      summary = g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT,
                                          summary_bytes, FALSE);

      ref_map = g_variant_get_child_value (summary, 0);

      g_variant_iter_init (&iter, ref_map);
      while ((child = g_variant_iter_next_value (&iter)) != NULL)
        {
          const char *ref_name = NULL;
          g_autoptr(GVariant) csum_v = NULL;
          char tmp_checksum[OSTREE_SHA256_STRING_LEN+1];

          g_variant_get_child (child, 0, "&s", &ref_name);

          if (ref_name != NULL)
            {
              g_variant_get_child (child, 1, "(t@aya{sv})", NULL, &csum_v, NULL);

              const guchar *csum_bytes = ostree_checksum_bytes_peek_validate (csum_v, error);
              if (csum_bytes == NULL)
                return FALSE;

              ostree_checksum_inplace_from_bytes (csum_bytes, tmp_checksum);

              g_hash_table_insert (ret_all_refs,
                                   g_strdup (ref_name),
                                   g_strdup (tmp_checksum));
            }

          g_variant_unref (child);
        }
    }

  ot_transfer_out_value (out_all_refs, &ret_all_refs);
  return TRUE;
}