[ostree] core: Add detached metadata, readd metadata to commits



commit ac2d61dd515e547df6e1fc9e9d438783a201848d
Author: Colin Walters <walters verbum org>
Date:   Mon Sep 9 17:01:32 2013 -0400

    core: Add detached metadata, readd metadata to commits
    
    Previously I thought we'd have to ditch the current commit
    format to avoid a{sv} due to
    
    See https://bugzilla.gnome.org/show_bug.cgi?id=673012
    
    But I realized that we don't really have to care about
    unpacking/repacking commit objects, so let's just re-expose the
    existing metadata a{sv} in commits in the API.
    
    Also, add support for "detached" metadata that can be updated at any
    time post-commit.  This is specifically designed for GPG signatures.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=707379

 src/libostree/ostree-repo-commit.c  |   92 ++++++++++++++++++++++++++++++++++-
 src/libostree/ostree-repo-private.h |    4 ++
 src/libostree/ostree-repo-prune.c   |    7 +++
 src/libostree/ostree-repo.h         |   13 +++++
 src/ostree/ot-builtin-commit.c      |   68 +++++++++++++++++++++++++-
 src/ostree/ot-builtin-show.c        |   38 +++++++++-----
 tests/test-basic.sh                 |   11 ++++-
 7 files changed, 217 insertions(+), 16 deletions(-)
---
diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c
index 0cfb216..3a80cde 100644
--- a/src/libostree/ostree-repo-commit.c
+++ b/src/libostree/ostree-repo-commit.c
@@ -1171,6 +1171,7 @@ create_empty_gvariant_dict (void)
  * @parent: (allow-none): ASCII SHA256 checksum for parent, or %NULL for none
  * @subject: Subject
  * @body: (allow-none): Body
+ * @metadata: (allow-none): GVariant of type a{sv}, or %NULL for none
  * @root_contents_checksum: ASCII SHA256 checksum for %OSTREE_OBJECT_TYPE_DIR_TREE
  * @root_metadata_checksum: ASCII SHA256 checksum for %OSTREE_OBJECT_TYPE_DIR_META
  * @out_commit: (out): Resulting ASCII SHA256 checksum for commit
@@ -1185,6 +1186,7 @@ ostree_repo_write_commit (OstreeRepo    *self,
                           const char    *parent,
                           const char    *subject,
                           const char    *body,
+                          GVariant      *metadata,
                           const char    *root_contents_checksum,
                           const char    *root_metadata_checksum,
                           char         **out_commit,
@@ -1203,7 +1205,7 @@ ostree_repo_write_commit (OstreeRepo    *self,
 
   now = g_date_time_new_now_utc ();
   commit = g_variant_new ("(@a{sv} ay@a(say)sst ay@ay)",
-                          create_empty_gvariant_dict (),
+                          metadata ? metadata : create_empty_gvariant_dict (),
                           parent ? ostree_checksum_to_bytes_v (parent) : ot_gvariant_new_bytearray (NULL, 0),
                           g_variant_new_array (G_VARIANT_TYPE ("(say)"), NULL, 0),
                           subject, body ? body : "",
@@ -1226,6 +1228,94 @@ ostree_repo_write_commit (OstreeRepo    *self,
   return ret;
 }
 
+GFile *
+_ostree_repo_get_commit_metadata_loose_path (OstreeRepo        *self,
+                                             const char        *checksum)
+{
+  gs_free char *commit_path = ostree_get_relative_object_path (checksum, OSTREE_OBJECT_TYPE_COMMIT, FALSE);
+  return ot_gfile_resolve_path_printf (self->repodir, "%smeta", commit_path);
+}
+
+/**
+ * ostree_repo_read_commit_detached_metadata:
+ * @self: Repo
+ * @checksum: ASCII SHA256 commit checksum
+ * @out_metadata: (out) (transfer full): Metadata associated with commit in with format "a{sv}", or %NULL if 
none exists
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * OSTree commits can have arbitrary metadata associated; this
+ * function retrieves them.  If none exists, @out_metadata will be set
+ * to %NULL.
+ */
+gboolean
+ostree_repo_read_commit_detached_metadata (OstreeRepo      *self,
+                                           const char      *checksum,
+                                           GVariant       **out_metadata,
+                                           GCancellable    *cancellable,
+                                           GError         **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_object GFile *metadata_path =
+    _ostree_repo_get_commit_metadata_loose_path (self, checksum);
+  gs_unref_variant GVariant *ret_metadata = NULL;
+  GError *temp_error = NULL;
+  
+  if (!ot_util_variant_map (metadata_path, G_VARIANT_TYPE ("a{sv}"),
+                            TRUE, &ret_metadata, &temp_error))
+    {
+      if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+        {
+          g_clear_error (&temp_error);
+        }
+      else
+        {
+          g_propagate_error (error, temp_error);
+          goto out;
+        }
+    }
+
+  ret = TRUE;
+  ot_transfer_out_value (out_metadata, &ret_metadata);
+ out:
+  return ret;
+}
+
+/**
+ * ostree_repo_write_commit_detached_metadata:
+ * @self: Repo
+ * @checksum: ASCII SHA256 commit checksum
+ * @metadata: (allow-none): Metadata to associate with commit in with format "a{sv}", or %NULL to delete
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Replace any existing metadata associated with commit referred to by
+ * @checksum with @metadata.  If @metadata is %NULL, then existing
+ * data will be deleted.
+ */
+gboolean
+ostree_repo_write_commit_detached_metadata (OstreeRepo      *self,
+                                            const char      *checksum,
+                                            GVariant        *metadata,
+                                            GCancellable    *cancellable,
+                                            GError         **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_object GFile *metadata_path =
+    _ostree_repo_get_commit_metadata_loose_path (self, checksum);
+
+  if (!g_file_replace_contents (metadata_path,
+                                g_variant_get_data (metadata),
+                                g_variant_get_size (metadata),
+                                NULL, FALSE, 0, NULL,
+                                cancellable, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
 static GVariant *
 create_tree_variant_from_hashes (GHashTable            *file_checksums,
                                  GHashTable            *dir_contents_checksums,
diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h
index 6df7d0c..a3e8723 100644
--- a/src/libostree/ostree-repo-private.h
+++ b/src/libostree/ostree-repo-private.h
@@ -74,6 +74,10 @@ _ostree_repo_find_object (OstreeRepo           *self,
                           GCancellable         *cancellable,
                           GError             **error);
 
+GFile *
+_ostree_repo_get_commit_metadata_loose_path (OstreeRepo        *self,
+                                             const char        *checksum);
+
 gboolean
 _ostree_repo_has_loose_object (OstreeRepo           *self,
                                const char           *checksum,
diff --git a/src/libostree/ostree-repo-prune.c b/src/libostree/ostree-repo-prune.c
index caef29e..4f1db66 100644
--- a/src/libostree/ostree-repo-prune.c
+++ b/src/libostree/ostree-repo-prune.c
@@ -64,6 +64,13 @@ maybe_prune_loose_object (OtPruneData        *data,
 
           if (info)
             {
+              if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
+                {
+                  gs_unref_object GFile *detached_metadata =
+                    _ostree_repo_get_commit_metadata_loose_path (data->repo, checksum);
+                  if (!ot_gfile_ensure_unlinked (detached_metadata, cancellable, error))
+                    goto out;
+                }
               if (!gs_file_unlink (objf, cancellable, error))
                 goto out;
               data->freed_bytes += g_file_info_get_size (info);
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
index ab4c4c7..ca24a76 100644
--- a/src/libostree/ostree-repo.h
+++ b/src/libostree/ostree-repo.h
@@ -318,12 +318,25 @@ gboolean      ostree_repo_write_commit (OstreeRepo   *self,
                                         const char   *parent,
                                         const char   *subject,
                                         const char   *body,
+                                        GVariant     *metadata,
                                         const char   *root_contents_checksum,
                                         const char   *root_metadata_checksum,
                                         char        **out_commit,
                                         GCancellable *cancellable,
                                         GError      **error);
 
+gboolean      ostree_repo_read_commit_detached_metadata (OstreeRepo      *self,
+                                                         const char      *checksum,
+                                                         GVariant       **out_metadata,
+                                                         GCancellable    *cancellable,
+                                                         GError         **error);
+
+gboolean      ostree_repo_write_commit_detached_metadata (OstreeRepo      *self,
+                                                          const char      *checksum,
+                                                          GVariant        *metadata,
+                                                          GCancellable    *cancellable,
+                                                          GError         **error);
+
 /**
  * OstreeRepoCheckoutMode:
  * @OSTREE_REPO_CHECKOUT_MODE_NONE: No special options
diff --git a/src/ostree/ot-builtin-commit.c b/src/ostree/ot-builtin-commit.c
index fd52a3e..e240204 100644
--- a/src/ostree/ot-builtin-commit.c
+++ b/src/ostree/ot-builtin-commit.c
@@ -31,6 +31,8 @@ static char *opt_subject;
 static char *opt_body;
 static char *opt_branch;
 static char *opt_statoverride_file;
+static char **opt_metadata_strings;
+static char **opt_detached_metadata_strings;
 static gboolean opt_link_checkout_speedup;
 static gboolean opt_skip_if_unchanged;
 static gboolean opt_tar_autocreate_parents;
@@ -45,6 +47,8 @@ static GOptionEntry options[] = {
   { "body", 'm', 0, G_OPTION_ARG_STRING, &opt_body, "Full description", "body" },
   { "branch", 'b', 0, G_OPTION_ARG_STRING, &opt_branch, "Branch", "branch" },
   { "tree", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_trees, "Overlay the given argument as a tree", "NAME" },
+  { "add-metadata-string", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_metadata_strings, "Append given key and 
value (in string format) to metadata", "KEY=VALUE" },
+  { "add-detached-metadata-string", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_detached_metadata_strings, "Append 
given key and value (in string format) to detached metadata", "KEY=VALUE" },
   { "owner-uid", 0, 0, G_OPTION_ARG_INT, &opt_owner_uid, "Set file ownership user id", "UID" },
   { "owner-gid", 0, 0, G_OPTION_ARG_INT, &opt_owner_gid, "Set file ownership group id", "GID" },
   { "no-xattrs", 0, 0, G_OPTION_ARG_NONE, &opt_no_xattrs, "Do not import extended attributes", NULL },
@@ -218,6 +222,45 @@ out:
   return ret;
 }
 
+static gboolean
+parse_keyvalue_strings (char             **strings,
+                        GVariant         **out_metadata,
+                        GError           **error)
+{
+  gboolean ret = FALSE;
+  char **iter;
+  gs_unref_variant_builder GVariantBuilder *builder = NULL;
+
+  builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
+
+  for (iter = strings; *iter; iter++)
+    {
+      const char *s;
+      const char *eq;
+      gs_free char *key = NULL;
+
+      s = *iter;
+
+      eq = strchr (s, '=');
+      if (!eq)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Missing '=' in KEY=VALUE metadata '%s'", s);
+          goto out;
+        }
+          
+      key = g_strndup (s, eq - s);
+      g_variant_builder_add (builder, "{sv}", key,
+                             g_variant_new_string (eq + 1));
+    }
+
+  ret = TRUE;
+  *out_metadata = g_variant_builder_end (builder);
+  g_variant_ref_sink (*out_metadata);
+ out:
+  return ret;
+}
+
 gboolean
 ostree_builtin_commit (int argc, char **argv, OstreeRepo *repo, GCancellable *cancellable, GError **error)
 {
@@ -228,6 +271,8 @@ ostree_builtin_commit (int argc, char **argv, OstreeRepo *repo, GCancellable *ca
   gs_free char *parent = NULL;
   gs_free char *commit_checksum = NULL;
   gs_free char *contents_checksum = NULL;
+  gs_unref_variant GVariant *metadata = NULL;
+  gs_unref_variant GVariant *detached_metadata = NULL;
   gs_unref_object OstreeMutableTree *mtree = NULL;
   gs_free char *tree_type = NULL;
   gs_unref_hashtable GHashTable *mode_adds = NULL;
@@ -246,6 +291,19 @@ ostree_builtin_commit (int argc, char **argv, OstreeRepo *repo, GCancellable *ca
         goto out;
     }
 
+  if (opt_metadata_strings)
+    {
+      if (!parse_keyvalue_strings (opt_metadata_strings,
+                                   &metadata, error))
+        goto out;
+    }
+  if (opt_detached_metadata_strings)
+    {
+      if (!parse_keyvalue_strings (opt_detached_metadata_strings,
+                                   &detached_metadata, error))
+        goto out;
+    }
+      
   if (!opt_branch)
     {
       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
@@ -405,10 +463,18 @@ ostree_builtin_commit (int argc, char **argv, OstreeRepo *repo, GCancellable *ca
         }
 
       if (!ostree_repo_write_commit (repo, parent, opt_subject, opt_body,
-                                     contents_checksum, root_metadata,
+                                     metadata, contents_checksum, root_metadata,
                                      &commit_checksum, cancellable, error))
         goto out;
 
+      if (detached_metadata)
+        {
+          if (!ostree_repo_write_commit_detached_metadata (repo, commit_checksum,
+                                                           detached_metadata,
+                                                           cancellable, error))
+            goto out;
+        }
+
       ostree_repo_transaction_set_ref (repo, NULL, opt_branch, commit_checksum);
 
       if (!ostree_repo_commit_transaction (repo, &stats, cancellable, error))
diff --git a/src/ostree/ot-builtin-show.c b/src/ostree/ot-builtin-show.c
index c98000b..09aaead 100644
--- a/src/ostree/ot-builtin-show.c
+++ b/src/ostree/ot-builtin-show.c
@@ -30,12 +30,14 @@
 static gboolean opt_print_related;
 static char* opt_print_variant_type;
 static char* opt_print_metadata_key;
+static char* opt_print_detached_metadata_key;
 static gboolean opt_raw;
 
 static GOptionEntry options[] = {
   { "print-related", 0, 0, G_OPTION_ARG_NONE, &opt_print_related, "If given, show the \"related\" commits", 
NULL },
   { "print-variant-type", 0, 0, G_OPTION_ARG_STRING, &opt_print_variant_type, "If given, argument should be 
a filename and it will be interpreted as this type", NULL },
   { "print-metadata-key", 0, 0, G_OPTION_ARG_STRING, &opt_print_metadata_key, "Print string value of 
metadata key KEY for given commit", "KEY" },
+  { "print-detached-metadata-key", 0, 0, G_OPTION_ARG_STRING, &opt_print_detached_metadata_key, "Print 
string value of detached metadata key KEY for given commit", "KEY" },
   { "raw", 0, 0, G_OPTION_ARG_NONE, &opt_raw, "Show raw variant data" },
   { NULL }
 };
@@ -98,22 +100,31 @@ do_print_related (OstreeRepo  *repo,
 }
 
 static gboolean
-do_print_metadata_key (OstreeRepo  *repo,
-                       const char *resolved_rev,
-                       const char *key,
-                       GError **error)
+do_print_metadata_key (OstreeRepo     *repo,
+                       const char     *resolved_rev,
+                       gboolean        detached,
+                       const char     *key,
+                       GError        **error)
 {
   gboolean ret = FALSE;
   const char *value;
   gs_unref_variant GVariant *commit = NULL;
   gs_unref_variant GVariant *metadata = NULL;
 
-  if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
-                                 resolved_rev, &commit, error))
-    goto out;
-
-  /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
-  metadata = g_variant_get_child_value (commit, 1);
+  if (!detached)
+    {
+      if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
+                                     resolved_rev, &commit, error))
+        goto out;
+      /* PARSE OSTREE_SERIALIZED_COMMIT_VARIANT */
+      metadata = g_variant_get_child_value (commit, 0);
+    }
+  else
+    {
+      if (!ostree_repo_read_commit_detached_metadata (repo, resolved_rev, &metadata,
+                                                      NULL, error))
+        goto out;
+    }
   
   if (!g_variant_lookup (metadata, key, "&s", &value))
     goto out;
@@ -125,7 +136,6 @@ do_print_metadata_key (OstreeRepo  *repo,
   return ret;
 }
 
-
 static gboolean
 print_object (OstreeRepo          *repo,
               OstreeObjectType     objtype,
@@ -198,12 +208,14 @@ ostree_builtin_show (int argc, char **argv, OstreeRepo *repo, GCancellable *canc
     }
   rev = argv[1];
 
-  if (opt_print_metadata_key)
+  if (opt_print_metadata_key || opt_print_detached_metadata_key)
     {
+      gboolean detached = opt_print_detached_metadata_key != NULL;
+      const char *key = detached ? opt_print_detached_metadata_key : opt_print_metadata_key;
       if (!ostree_repo_resolve_rev (repo, rev, FALSE, &resolved_rev, error))
         goto out;
 
-      if (!do_print_metadata_key (repo, resolved_rev, opt_print_metadata_key, error))
+      if (!do_print_metadata_key (repo, resolved_rev, detached, key, error))
         goto out;
     }
   else if (opt_print_related)
diff --git a/tests/test-basic.sh b/tests/test-basic.sh
index 0c17fc5..d5b9e5a 100755
--- a/tests/test-basic.sh
+++ b/tests/test-basic.sh
@@ -19,7 +19,7 @@
 
 set -e
 
-echo "1..40"
+echo "1..41"
 
 . $(dirname $0)/libtest.sh
 
@@ -282,3 +282,12 @@ $OSTREE checkout test2 checkout-test2
 touch checkout-test2/sometestfile
 $OSTREE commit -s sometest -b test2 checkout-test2
 echo "ok commit with directory filename"
+
+$OSTREE commit -b test2 -s "Metadata string" --add-metadata-string=FOO=BAR 
--add-metadata-string=KITTENS=CUTE --add-detached-metadata-string=SIGNATURE=HANCOCK --tree=ref=test2
+$OSTREE show --print-metadata-key=FOO test2 > test2-meta
+assert_file_has_content test2-meta "BAR"
+$OSTREE show --print-metadata-key=KITTENS test2 > test2-meta
+assert_file_has_content test2-meta "CUTE"
+$OSTREE show --print-detached-metadata-key=SIGNATURE test2 > test2-meta
+assert_file_has_content test2-meta "HANCOCK"
+echo "ok metadata commit with strings"


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]