diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt
index e5ce157c..1ba02cfc 100644
--- a/apidoc/ostree-sections.txt
+++ b/apidoc/ostree-sections.txt
@@ -135,6 +135,7 @@ ostree_raw_file_to_archive_z2_stream_with_options
ostree_raw_file_to_content_stream
ostree_checksum_file_from_input
ostree_checksum_file
+ostree_checksum_file_at
ostree_checksum_file_async
ostree_checksum_file_async_finish
ostree_create_directory_metadata
diff --git a/bash/ostree b/bash/ostree
index 034f101b..c132a43f 100644
--- a/bash/ostree
+++ b/bash/ostree
@@ -745,6 +745,7 @@ _ostree_checkout() {
_ostree_checksum() {
local boolean_options="
$main_boolean_options
+ --ignore-xattrs
"
case "$cur" in
diff --git a/man/ostree-checksum.xml b/man/ostree-checksum.xml
index 53914782..c6e16a8b 100644
--- a/man/ostree-checksum.xml
+++ b/man/ostree-checksum.xml
@@ -61,6 +61,19 @@ Boston, MA 02111-1307, USA.
+
+ Options
+
+
+
+
+
+ Ignore extended attributes when checksumming.
+
+
+
+
+
Example
$ ostree checksum file1
diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym
index 896d2b75..07d99f15 100644
--- a/src/libostree/libostree-devel.sym
+++ b/src/libostree/libostree-devel.sym
@@ -19,6 +19,8 @@
/* Add new symbols here. Release commits should copy this section into -released.sym. */
LIBOSTREE_2017.13 {
+global:
+ ostree_checksum_file_at;
} LIBOSTREE_2017.12;
/* Stub section for the stable release *after* this development one; don't
@@ -27,6 +29,6 @@ LIBOSTREE_2017.13 {
* with whatever the next version with new symbols will be.
LIBOSTREE_2017.$NEWVERSION {
global:
- someostree_symbol_deleteme;
+ someostree_symbol_deleteme;
} LIBOSTREE_2017.$LASTSTABLE;
*/
diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c
index 733901f7..615f5dec 100644
--- a/src/libostree/ostree-core.c
+++ b/src/libostree/ostree-core.c
@@ -832,6 +832,81 @@ ostree_checksum_file (GFile *f,
return TRUE;
}
+/**
+ * ostree_checksum_file_at:
+ * @dfd: Directory file descriptor
+ * @path: Subpath
+ * @stbuf (allow-none): Optional stat buffer
+ * @objtype: Object type
+ * @flags: Flags
+ * @out_checksum (out) (transfer full): Return location for hex checksum
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Compute the OSTree checksum for a given file. This is an fd-relative version
+ * of ostree_checksum_file() which also takes flags and fills in a caller
+ * allocated buffer.
+ *
+ * Since: 2017.13
+ */
+gboolean
+ostree_checksum_file_at (int dfd,
+ const char *path,
+ struct stat *stbuf,
+ OstreeObjectType objtype,
+ OstreeChecksumFlags flags,
+ char **out_checksum,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (out_checksum != NULL, FALSE);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return FALSE;
+
+ struct stat local_stbuf;
+ if (stbuf == NULL)
+ {
+ stbuf = &local_stbuf;
+ if (!glnx_fstatat (dfd, path, stbuf, AT_SYMLINK_NOFOLLOW, error))
+ return FALSE;
+ }
+
+ g_autoptr(GFileInfo) file_info = _ostree_stbuf_to_gfileinfo (stbuf);
+
+ g_autoptr(GInputStream) in = NULL;
+ if (S_ISREG (stbuf->st_mode))
+ {
+ glnx_autofd int fd = -1;
+ if (!glnx_openat_rdonly (dfd, path, FALSE, &fd, error))
+ return FALSE;
+ in = g_unix_input_stream_new (glnx_steal_fd (&fd), TRUE);
+ }
+ else if (S_ISLNK (stbuf->st_mode))
+ {
+ if (!ot_readlinkat_gfile_info (dfd, path, file_info, cancellable, error))
+ return FALSE;
+ }
+
+ const gboolean ignore_xattrs =
+ ((flags & OSTREE_CHECKSUM_FLAGS_IGNORE_XATTRS) > 0);
+
+ g_autoptr(GVariant) xattrs = NULL;
+ if (!ignore_xattrs && objtype == OSTREE_OBJECT_TYPE_FILE)
+ {
+ if (!glnx_dfd_name_get_all_xattrs (dfd, path, &xattrs, cancellable, error))
+ return FALSE;
+ }
+
+ g_autofree guchar *csum_bytes = NULL;
+ if (!ostree_checksum_file_from_input (file_info, xattrs, in, objtype,
+ &csum_bytes, cancellable, error))
+ return FALSE;
+
+ *out_checksum = ostree_checksum_from_bytes (csum_bytes);
+ return TRUE;
+}
+
typedef struct {
GFile *f;
OstreeObjectType objtype;
diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h
index aae86d54..979b35a2 100644
--- a/src/libostree/ostree-core.h
+++ b/src/libostree/ostree-core.h
@@ -21,6 +21,7 @@
#pragma once
+#include
#include
#include
@@ -420,6 +421,26 @@ gboolean ostree_checksum_file (GFile *f,
GCancellable *cancellable,
GError **error);
+/**
+ * OstreeChecksumFlags:
+ *
+ * Since: 2017.13
+ */
+typedef enum {
+ OSTREE_CHECKSUM_FLAGS_NONE = 0,
+ OSTREE_CHECKSUM_FLAGS_IGNORE_XATTRS = (1 << 0),
+} OstreeChecksumFlags;
+
+_OSTREE_PUBLIC
+gboolean ostree_checksum_file_at (int dfd,
+ const char *path,
+ struct stat *stbuf,
+ OstreeObjectType objtype,
+ OstreeChecksumFlags flags,
+ char **out_checksum,
+ GCancellable *cancellable,
+ GError **error);
+
_OSTREE_PUBLIC
void ostree_checksum_file_async (GFile *f,
OstreeObjectType objtype,
diff --git a/src/ostree/ot-builtin-checksum.c b/src/ostree/ot-builtin-checksum.c
index 0bc98d3d..008c4fe7 100644
--- a/src/ostree/ot-builtin-checksum.c
+++ b/src/ostree/ot-builtin-checksum.c
@@ -32,7 +32,10 @@
* man page (man/ostree-checksum.xml) when changing the option list.
*/
+static gboolean opt_ignore_xattrs;
+
static GOptionEntry options[] = {
+ { "ignore-xattrs", 0, 0, G_OPTION_ARG_NONE, &opt_ignore_xattrs, "Don't include xattrs in checksum", NULL },
{ NULL }
};
@@ -49,12 +52,14 @@ on_checksum_received (GObject *obj,
{
AsyncChecksumData *data = user_data;
- g_autofree guchar *csum = NULL;
- data->success = ostree_checksum_file_async_finish ((GFile*)obj, result, &csum, data->error);
+ g_autofree guchar *csum_bytes = NULL;
+ data->success =
+ ostree_checksum_file_async_finish ((GFile*)obj, result, &csum_bytes, data->error);
if (data->success)
{
- g_autofree char *checksum = ostree_checksum_from_bytes (csum);
- g_print ("%s\n", checksum);
+ char csum[OSTREE_SHA256_STRING_LEN+1];
+ ostree_checksum_inplace_from_bytes (csum_bytes, csum);
+ g_print ("%s\n", csum);
}
g_main_loop_quit (data->loop);
@@ -73,15 +78,28 @@ ostree_builtin_checksum (int argc, char **argv, GCancellable *cancellable, GErro
return glnx_throw (error, "A filename must be given");
const char *path = argv[1];
- g_autoptr(GFile) f = g_file_new_for_path (path);
- g_autoptr(GMainLoop) loop = g_main_loop_new (NULL, FALSE);
+ /* for test coverage, use the async API if no flags are needed */
+ if (!opt_ignore_xattrs)
+ {
+ g_autoptr(GFile) f = g_file_new_for_path (path);
+ g_autoptr(GMainLoop) loop = g_main_loop_new (NULL, FALSE);
- AsyncChecksumData data = { 0, };
+ AsyncChecksumData data = { 0, };
- data.loop = loop;
- data.error = error;
- ostree_checksum_file_async (f, OSTREE_OBJECT_TYPE_FILE, G_PRIORITY_DEFAULT, cancellable,
- on_checksum_received, &data);
- g_main_loop_run (data.loop);
- return data.success;
+ data.loop = loop;
+ data.error = error;
+ ostree_checksum_file_async (f, OSTREE_OBJECT_TYPE_FILE, G_PRIORITY_DEFAULT,
+ cancellable, on_checksum_received, &data);
+ g_main_loop_run (data.loop);
+ return data.success;
+ }
+
+ g_autofree char *checksum = NULL;
+ if (!ostree_checksum_file_at (AT_FDCWD, path, NULL, OSTREE_OBJECT_TYPE_FILE,
+ OSTREE_CHECKSUM_FLAGS_IGNORE_XATTRS, &checksum,
+ cancellable, error))
+ return FALSE;
+
+ g_print ("%s\n", checksum);
+ return TRUE;
}
diff --git a/tests/basic-test.sh b/tests/basic-test.sh
index 037f7b45..4aae6b33 100644
--- a/tests/basic-test.sh
+++ b/tests/basic-test.sh
@@ -19,7 +19,7 @@
set -euo pipefail
-echo "1..$((74 + ${extra_basic_tests:-0}))"
+echo "1..$((75 + ${extra_basic_tests:-0}))"
CHECKOUT_U_ARG=""
CHECKOUT_H_ARGS="-H"
@@ -238,6 +238,33 @@ fi
assert_file_has_content err.txt "No such metadata object"
echo "ok commit orphaned"
+cd ${test_tmpdir}
+# in bare-user-only mode, we canonicalize ownership to 0:0, so checksums won't
+# match -- we could add a --ignore-ownership option I suppose?
+if is_bare_user_only_repo repo; then
+ echo "ok # SKIP checksums won't match up in bare-user-only"
+else
+ $OSTREE fsck
+ CHECKSUM_FLAG=
+ if [ -n "${OSTREE_NO_XATTRS:-}" ]; then
+ CHECKSUM_FLAG=--ignore-xattrs
+ fi
+ rm -rf checksum-test
+ $OSTREE checkout test2 checksum-test
+ find checksum-test/ -type f | while read fn; do
+ checksum=$($CMD_PREFIX ostree checksum $CHECKSUM_FLAG $fn)
+ objpath=repo/objects/${checksum::2}/${checksum:2}.file
+ assert_has_file $objpath
+ # running `ostree checksum` on the obj might not necessarily match, let's
+ # just check that they have the same content to confirm that it's
+ # (probably) the originating file
+ object_content_checksum=$(sha256sum $objpath | cut -f1 -d' ')
+ checkout_content_checksum=$(sha256sum $fn | cut -f1 -d' ')
+ assert_streq "$object_content_checksum" "$checkout_content_checksum"
+ done
+ echo "ok checksum CLI"
+fi
+
cd ${test_tmpdir}
$OSTREE diff test2^ test2 > diff-test2
assert_file_has_content diff-test2 'D */a/5'