lib/core: add ostree_checksum_file_at API
authorJonathan Lebon <jlebon@redhat.com>
Wed, 11 Oct 2017 14:52:02 +0000 (14:52 +0000)
committerAtomic Bot <atomic-devel@projectatomic.io>
Thu, 12 Oct 2017 12:53:01 +0000 (12:53 +0000)
This is like `ostree_checksum_file` but fd-relative. This will be used
by https://github.com/ostreedev/ostree/pull/1258.

AFAICT, we actually didn't have any tests that check the `checksum` CLI.
Add a basic one here to test the old code as well as the new code.

Closes: #1263
Approved by: cgwalters

apidoc/ostree-sections.txt
bash/ostree
man/ostree-checksum.xml
src/libostree/libostree-devel.sym
src/libostree/ostree-core.c
src/libostree/ostree-core.h
src/ostree/ot-builtin-checksum.c
tests/basic-test.sh

index e5ce157cc7b527bba6ff8548235543453b221eac..1ba02cfc0b04aa3b08247e40557276bc5fb8dc3a 100644 (file)
@@ -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
index 034f101b243d6c4ee98f11bf5a8938617a2d4c57..c132a43fd7d92794b09a06d917255d8c61ddef5e 100644 (file)
@@ -745,6 +745,7 @@ _ostree_checkout() {
 _ostree_checksum() {
     local boolean_options="
         $main_boolean_options
+        --ignore-xattrs
     "
 
     case "$cur" in
index 53914782e6a716c5bb2adc3c925d529f154a976e..c6e16a8b2d857404eb9565c6cadff42df989ca79 100644 (file)
@@ -61,6 +61,19 @@ Boston, MA 02111-1307, USA.
         </para>
     </refsect1>
 
+    <refsect1>
+        <title>Options</title>
+        <variablelist>
+            <varlistentry>
+                <term><option>--ignore-xattrs</option></term>
+
+                <listitem><para>
+                    Ignore extended attributes when checksumming.
+                </para></listitem>
+            </varlistentry>
+        </variablelist>
+    </refsect1>
+
     <refsect1>
         <title>Example</title>
         <para><command>$ ostree checksum file1</command></para>
index 896d2b759fb9172de5abba2d6542e12a31fbdfb5..07d99f1564ea4ca68f141581c36c149693fdc695 100644 (file)
@@ -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;
 */
index 733901f7f0bc2ca18d8729de1a90c9e498ae7494..615f5dec71a84feb2d2566e937e4bc4ecf6311e9 100644 (file)
@@ -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;
index aae86d548193ea67c45edf003f6fda79062ee85a..979b35a2cbd414986ccbee2d6a2c29e692cbaee8 100644 (file)
@@ -21,6 +21,7 @@
 
 #pragma once
 
+#include <sys/stat.h>
 #include <gio/gio.h>
 #include <ostree-types.h>
 
@@ -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,
index 0bc98d3d583d53cd0cd974fff3d3f446389bddf7..008c4fe773a8047b121c76210d14ad38064b4b9f 100644 (file)
  * 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;
+    }
+
+  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;
 
-  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_print ("%s\n", checksum);
+  return TRUE;
 }
index 037f7b45f519645e6d37d243196120b33bfab6d0..4aae6b33a4d143f32053b1fce0b96d23ae4e0e13 100644 (file)
@@ -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'