Skip to content

Commit

Permalink
core: Add API (and standard concept for) content checksum
Browse files Browse the repository at this point in the history
There are a few cases for knowing whether a commit has identical
content to another commit.  Some people want to do a "promotion workflow",
where the content of a commit on a tesitng branch is then "promoted"
to a production branch with `ostree commit --tree=ref`.

Another use case I just hit in rpm-ostree deals with
[jigdo](coreos/rpm-ostree#1081) where we're
importing RPMs on both the client and server, and will be using the
content checksum, since the client/server cases inject different metadata
into the commit object.

Closes: #131
  • Loading branch information
cgwalters committed Feb 8, 2018
1 parent 2e95e06 commit d883d59
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/libostree/libostree-devel.sym
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

/* Add new symbols here. Release commits should copy this section into -released.sym. */
LIBOSTREE_2018.2 {
ostree_commit_get_content_checksum;
} LIBOSTREE_2018.1;

/* Stub section for the stable release *after* this development one; don't
Expand Down
45 changes: 45 additions & 0 deletions src/libostree/ostree-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2371,6 +2371,51 @@ ostree_commit_get_timestamp (GVariant *commit_variant)
return GUINT64_FROM_BE (ret);
}


/**
* ostree_commit_get_content_checksum:
* @commit_variant: A commit object
*
* There are use cases where one wants a checksum just of the content of a
* commit. OSTree commits by default capture the current timestamp, and may have
* additional metadata, which means that re-committing identical content
* often results in a new checksum.
*
* By comparing checksums of content, it's possible to easily distinguish
* cases where nothing actually changed.
*
* The content checksums is simply defined as `SHA256(root dirtree_checksum || root_dirmeta_checksum)`,
* i.e. the SHA-256 of the root "dirtree" object's checksum concatenated with the
* root "dirmeta" checksum (both in binary form, not hexadecimal).
*
* Returns: (nullable): A SHA-256 hex string, or %NULL if @commit_variant is not well-formed
*/
gchar *
ostree_commit_get_content_checksum (GVariant *commit_variant)
{
g_auto(OtChecksum) checksum = { 0, };
ot_checksum_init (&checksum);

g_autoptr(GVariant) tree_contents_csum = NULL;
g_autoptr(GVariant) tree_meta_csum = NULL;

g_variant_get_child (commit_variant, 6, "@ay", &tree_contents_csum);
g_variant_get_child (commit_variant, 7, "@ay", &tree_meta_csum);

const guchar *bytes;
bytes = ostree_checksum_bytes_peek_validate (tree_contents_csum, NULL);
if (!bytes)
return NULL;
ot_checksum_update (&checksum, bytes, OSTREE_SHA256_DIGEST_LEN);
bytes = ostree_checksum_bytes_peek_validate (tree_meta_csum, NULL);
if (!bytes)
return NULL;
ot_checksum_update (&checksum, bytes, OSTREE_SHA256_DIGEST_LEN);
char hexdigest[OSTREE_SHA256_STRING_LEN+1];
ot_checksum_get_hexdigest (&checksum, hexdigest, sizeof (hexdigest));
return g_strdup (hexdigest);
}

/* Used in pull/deploy to validate we're not being downgraded */
gboolean
_ostree_compare_timestamps (const char *current_rev,
Expand Down
3 changes: 3 additions & 0 deletions src/libostree/ostree-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,9 @@ gchar * ostree_commit_get_parent (GVariant *commit_variant);
_OSTREE_PUBLIC
guint64 ostree_commit_get_timestamp (GVariant *commit_variant);

_OSTREE_PUBLIC
gchar * ostree_commit_get_content_checksum (GVariant *commit_variant);

_OSTREE_PUBLIC
gboolean ostree_check_version (guint required_year, guint required_release);

Expand Down
2 changes: 2 additions & 0 deletions src/ostree/ot-dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ dump_commit (GVariant *variant,
str = format_timestamp (timestamp, &local_error);
if (!str)
errx (1, "Failed to read commit: %s", local_error->message);
g_autofree char *contents = ostree_commit_get_content_checksum (variant) ?: "<invalid commit>";
g_print ("ContentChecksum: %s\n", contents);
g_print ("Date: %s\n", str);

if ((version = ot_admin_checksum_version (variant)))
Expand Down
12 changes: 11 additions & 1 deletion tests/basic-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

set -euo pipefail

echo "1..$((81 + ${extra_basic_tests:-0}))"
echo "1..$((82 + ${extra_basic_tests:-0}))"

CHECKOUT_U_ARG=""
CHECKOUT_H_ARGS="-H"
Expand Down Expand Up @@ -752,6 +752,16 @@ assert_file_has_content show-output "Third commit"
assert_file_has_content show-output "commit $checksum"
echo "ok show full output"

grep -E -e '^ContentChecksum' show-output > previous-content-checksum.txt
cd $test_tmpdir/checkout-test2
checksum=$($OSTREE commit ${COMMIT_ARGS} -b test4 -s "Another commit with different subject")
cd ${test_tmpdir}
$OSTREE show test4 | grep -E -e '^ContentChecksum' > new-content-checksum.txt
if ! diff -u previous-content-checksum.txt new-content-checksum.txt; then
fatal "content checksum differs"
fi
echo "ok content checksum"

cd $test_tmpdir/checkout-test2
checksum1=$($OSTREE commit ${COMMIT_ARGS} -b test5 -s "First commit")
checksum2=$($OSTREE commit ${COMMIT_ARGS} -b test5 -s "Second commit")
Expand Down

0 comments on commit d883d59

Please sign in to comment.