diff --git a/Makefile-libostree.am b/Makefile-libostree.am index 3bc6b7b6..1a5d6f9f 100644 --- a/Makefile-libostree.am +++ b/Makefile-libostree.am @@ -171,9 +171,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= diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index a9e756eb..3f48ef3a 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -355,6 +355,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 diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index e2d6efc4..c4a70938 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -22,12 +22,17 @@ - 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; */ diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c index 9d5c6d3b..409738ad 100644 --- a/src/libostree/ostree-repo-commit.c +++ b/src/libostree/ostree-repo-commit.c @@ -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; diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index e64c3230..cd50fc43 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -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, diff --git a/tests/test-core.js b/tests/test-core.js index a9ef8919..8f460a5f 100755 --- a/tests/test-core.js +++ b/tests/test-core.js @@ -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); diff --git a/tests/test-repo.c b/tests/test-repo.c index 9857228e..ad81a7d6 100644 --- a/tests/test-repo.c +++ b/tests/test-repo.c @@ -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 (); }