repo: Add ostree_repo_write_regfile_inline
authorColin Walters <walters@verbum.org>
Wed, 7 Apr 2021 21:03:15 +0000 (21:03 +0000)
committerColin Walters <walters@verbum.org>
Thu, 8 Apr 2021 14:57:33 +0000 (14:57 +0000)
When working on ostree-ext and importing from tar, it's
quite inefficient and awkward for small files to end up creating
a whole `GInputStream` and `GFileInfo` and etc. for small files.

Plus the gtk-rs binding API to map from `impl Read` to Gio
https://docs.rs/gio/0.9.1/gio/struct.ReadInputStream.html
requires that the input stream is `Send` but the Rust `tar` API
isn't.

This is only 1/3 of the problem; we also need similar APIs
to directly create a symlink, and to stream large objects via
a push-based API.

Makefile-libostree.am
apidoc/ostree-sections.txt
src/libostree/libostree-devel.sym
src/libostree/ostree-repo-commit.c
src/libostree/ostree-repo.h
tests/test-core.js
tests/test-repo.c

index ce784aff4381e9e48e0e8542445debcd7c47c5a1..6dbd00f59cb772a09388e07749f6b53c993963d6 100644 (file)
@@ -179,9 +179,9 @@ endif # USE_GPGME
 symbol_files = $(top_srcdir)/src/libostree/libostree-released.sym
 
 # Uncomment this include when adding new development symbols.
-#if BUILDOPT_IS_DEVEL_BUILD
-#symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
-#endif
+if BUILDOPT_IS_DEVEL_BUILD
+symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
+endif
 
 # http://blog.jgc.org/2007/06/escaping-comma-and-space-in-gnu-make.html
 wl_versionscript_arg = -Wl,--version-script=
index e4954c7073145e15dae3e5621632ed251d31840c..6c84199ab6112f51b4aeb28c1485396f6cced1fb 100644 (file)
@@ -349,6 +349,7 @@ ostree_repo_write_metadata
 ostree_repo_write_metadata_async
 ostree_repo_write_metadata_finish
 ostree_repo_write_content
+ostree_repo_write_regfile_inline
 ostree_repo_write_metadata_trusted
 ostree_repo_write_metadata_stream_trusted
 ostree_repo_write_content_trusted
index e2d6efc4d0e5d86a0279f5c7f3ada9a11a8a2c6f..c4a70938dcbafb407374c1f7157dc97c527dfcfa 100644 (file)
    - uncomment the include in Makefile-libostree.am
 */
 
+LIBOSTREE_2021.2 {
+global:
+  ostree_repo_write_regfile_inline;
+} LIBOSTREE_2021.1;
+
 /* Stub section for the stable release *after* this development one; don't
  * edit this other than to update the year.  This is just a copy/paste
  * source.  Replace $LASTSTABLE with the last stable version, and $NEWVERSION
  * with whatever the next version with new symbols will be.
-LIBOSTREE_2020.$NEWVERSION {
+LIBOSTREE_2021.$NEWVERSION {
 global:
   someostree_symbol_deleteme;
-} LIBOSTREE_2020.$LASTSTABLE;
+} LIBOSTREE_2021.$LASTSTABLE;
 */
index 9d5c6d3b28c8a6115e6c6a10f203cf76f7772ab2..409738ad170d394c7426c63142aa71c70a81cbbb 100644 (file)
@@ -2769,6 +2769,51 @@ ostree_repo_write_content (OstreeRepo       *self,
                                cancellable, error);
 }
 
+/**
+ * ostree_repo_write_regfile_inline:
+ * @self: repo
+ * @expected_checksum: (allow-none): The expected checksum
+ * @uid: User id
+ * @gid: Group id
+ * @mode: File mode
+ * @xattrs: (allow-none): Extended attributes, GVariant of type (ayay)
+ * @buf: (array length=len) (element-type guint8): File contents
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Synchronously create a file object from the provided content.  This API
+ * is intended for small files where it is reasonable to buffer the entire
+ * content in memory.
+ *
+ * Unlike `ostree_repo_write_content()`, if @expected_checksum is provided,
+ * this function will not check for the presence of the object beforehand.
+ *
+ * Returns: (transfer full): Checksum (as a hex string) of the committed file
+ * Since: 2021.2
+ */
+_OSTREE_PUBLIC
+char *
+ostree_repo_write_regfile_inline (OstreeRepo       *self,
+                                  const char       *expected_checksum,
+                                  guint32           uid,
+                                  guint32           gid,
+                                  guint32           mode,
+                                  GVariant         *xattrs,
+                                  const guint8*     buf,
+                                  gsize             len,
+                                  GCancellable     *cancellable,
+                                  GError          **error)
+{
+  g_autoptr(GInputStream) memin = g_memory_input_stream_new_from_data (buf, len, NULL);
+  g_autoptr(GFileInfo) finfo = _ostree_mode_uidgid_to_gfileinfo (mode, uid, gid);
+  g_autofree guint8* csum = NULL;
+  if (!write_content_object (self, expected_checksum,
+                             memin, finfo, xattrs, &csum,
+                             cancellable, error))
+    return NULL;
+  return ostree_checksum_from_bytes (csum);
+}
+
 typedef struct {
   OstreeRepo *repo;
   char *expected_checksum;
index e64c3230ce0e5cfd296be9d456268b8b7a96207f..cd50fc4306a2e908371925548c48413918287616 100644 (file)
@@ -423,6 +423,18 @@ gboolean      ostree_repo_write_content (OstreeRepo       *self,
                                          GCancellable     *cancellable,
                                          GError          **error);
 
+_OSTREE_PUBLIC
+char *      ostree_repo_write_regfile_inline (OstreeRepo       *self,
+                                                const char       *expected_checksum,
+                                                guint32           uid,
+                                                guint32           gid,
+                                                guint32           mode,
+                                                GVariant         *xattrs,
+                                                const guint8*     buf,
+                                                gsize             len,
+                                                GCancellable     *cancellable,
+                                                GError          **error);
+
 _OSTREE_PUBLIC
 gboolean      ostree_repo_write_metadata_trusted (OstreeRepo        *self,
                                                   OstreeObjectType   objtype,
index a9ef891922001e1810c4508701726d600ecdee9c..8f460a5fedf75daa44a8b99e4b80a84a9b420aab 100755 (executable)
@@ -49,6 +49,20 @@ let [,dirTree] = repo.write_mtree(mtree, null);
 let [,commit] = repo.write_commit(null, 'Some subject', 'Some body', null, dirTree, null);
 print("commit => " + commit);
 
+// Test direct write APIs
+let inline_content = "default 0.0.0.0\nloopback 127.0.0.0\nlink-local 169.254.0.0\n";
+let regfile_mode = 33188; // 0o100000 | 0o644 (but in decimal so old gjs works)
+let inline_checksum = repo.write_regfile_inline(null, 0, 0, regfile_mode, null, inline_content, null);
+assertEquals(inline_checksum, "8aaa9dc13a0c5839fe4a277756798c609c53fac6fa2290314ecfef9041065873");
+let written = false;
+try {
+    repo.write_regfile_inline("8baa9dc13a0c5839fe4a277756798c609c53fac6fa2290314ecfef9041065873", 0, 0, regfile_mode, null, inline_content, null);
+    written = true;
+} catch (e) {
+}
+if (written)
+  throw new Error("Wrote invalid checksum");
+
 repo.commit_transaction(null, null);
 
 let [,root,checksum] = repo.read_commit(commit, null);
index 9857228e1f4d47010b6ce58388609943fdcc8b71..ad81a7d6146fdc6cc6bca8f8528dbef74f438f32 100644 (file)
@@ -197,6 +197,47 @@ test_repo_get_min_free_space (Fixture *fixture,
     }
 }
 
+static void
+test_write_regfile_api (Fixture *fixture,
+                        gconstpointer test_data)
+{
+  g_autoptr (GKeyFile) config = NULL;
+  g_autoptr(GError) error = NULL;
+
+  g_autoptr(OstreeRepo) repo = ostree_repo_create_at (fixture->tmpdir.fd, ".",
+                                                      OSTREE_REPO_MODE_ARCHIVE,
+                                                      NULL,
+                                                      NULL, &error);
+  g_assert_no_error (error);
+
+  g_auto(GVariantBuilder) xattrs_builder;
+  g_variant_builder_init (&xattrs_builder, (GVariantType*)"a(ayay)");
+  g_variant_builder_add (&xattrs_builder, "(^ay^ay)", "security.selinux", "system_u:object_r:etc_t:s0");
+  g_autoptr(GVariant) xattrs = g_variant_ref_sink (g_variant_builder_end (&xattrs_builder));
+
+  // Current contents of /etc/networks in Fedora
+  static const char contents[] = "default 0.0.0.0\nloopback 127.0.0.0\nlink-local 169.254.0.0\n";
+  // First with no xattrs
+  g_autofree char *checksum = ostree_repo_write_regfile_inline (repo, NULL, 0, 0, S_IFREG | 0644, NULL, (const guint8*)contents, sizeof (contents)-1, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpstr (checksum, ==, "8aaa9dc13a0c5839fe4a277756798c609c53fac6fa2290314ecfef9041065873");
+  g_clear_pointer (&checksum, g_free);
+
+  // Invalid checksum
+  checksum = ostree_repo_write_regfile_inline (repo, "3272139f889f6a7007b3d64adc74be9e2979bf6bbe663d1512e5bd43f4de24a1", 
+              0, 0, S_IFREG | 0644, NULL, (const guint8*)contents, sizeof (contents)-1, NULL, &error);
+  g_assert (checksum == NULL);
+  g_assert (error != NULL);
+  g_clear_error (&error);
+  
+  // Now with xattrs 
+  g_clear_pointer (&checksum, g_free);
+  checksum = ostree_repo_write_regfile_inline (repo, NULL, 0, 0, S_IFREG | 0644, xattrs, (const guint8*)contents, sizeof (contents)-1, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpstr (checksum, ==, "4f600d252338f93279c51c964915cb2c26f0d09082164c54890d1a3c78cdeb1e");
+  g_clear_pointer (&checksum, g_free);
+}
+
 int
 main (int    argc,
       char **argv)
@@ -212,7 +253,8 @@ main (int    argc,
               test_repo_equal, teardown);
   g_test_add ("/repo/get_min_free_space", Fixture, NULL, setup,
               test_repo_get_min_free_space, teardown);
-
+  g_test_add ("/repo/write_regfile_api", Fixture, NULL, setup,
+              test_write_regfile_api, teardown);
 
   return g_test_run ();
 }