diff --git a/Makefile-libostree.am b/Makefile-libostree.am index 23fa9494..a50b2b9d 100644 --- a/Makefile-libostree.am +++ b/Makefile-libostree.am @@ -137,8 +137,9 @@ libostree_1_la_SOURCES += \ endif libostree_1_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/bsdiff -I$(srcdir)/libglnx -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree -I$(builddir)/src/libostree \ - $(OT_INTERNAL_GIO_UNIX_CFLAGS) $(OT_INTERNAL_GPGME_CFLAGS) $(OT_DEP_LZMA_CFLAGS) $(OT_DEP_ZLIB_CFLAGS) -libostree_1_la_LDFLAGS = -version-number 1:0:0 -Bsymbolic-functions -export-symbols-regex '^ostree_' + $(OT_INTERNAL_GIO_UNIX_CFLAGS) $(OT_INTERNAL_GPGME_CFLAGS) $(OT_DEP_LZMA_CFLAGS) $(OT_DEP_ZLIB_CFLAGS) \ + -fvisibility=hidden '-D_OSTREE_PUBLIC=__attribute__((visibility("default"))) extern' +libostree_1_la_LDFLAGS = -version-number 1:0:0 -Bsymbolic-functions -Wl,--version-script=$(top_srcdir)/src/libostree/libostree.sym libostree_1_la_LIBADD = libotutil.la libbupsplit.la libglnx.la libbsdiff.la libostree-kernel-args.la $(OT_INTERNAL_GIO_UNIX_LIBS) $(OT_INTERNAL_GPGME_LIBS) $(OT_DEP_LZMA_LIBS) $(OT_DEP_ZLIB_LIBS) if USE_LIBARCHIVE @@ -158,6 +159,11 @@ libostree_1_la_CFLAGS += $(OT_INTERNAL_SOUP_CFLAGS) libostree_1_la_LIBADD += $(OT_INTERNAL_SOUP_LIBS) endif +if USE_LIBMOUNT +libostree_1_la_CFLAGS += $(OT_DEP_LIBMOUNT_CFLAGS) +libostree_1_la_LIBADD += $(OT_DEP_LIBMOUNT_LIBS) +endif + if USE_SELINUX libostree_1_la_CFLAGS += $(OT_DEP_SELINUX_CFLAGS) libostree_1_la_LIBADD += $(OT_DEP_SELINUX_LIBS) diff --git a/Makefile-man.am b/Makefile-man.am index 615bf0f0..ce7e93cd 100644 --- a/Makefile-man.am +++ b/Makefile-man.am @@ -19,7 +19,17 @@ if ENABLE_MAN -man1_files = ostree.1 ostree-admin-cleanup.1 ostree-admin-config-diff.1 ostree-admin-deploy.1 ostree-admin-init-fs.1 ostree-admin-instutil.1 ostree-admin-os-init.1 ostree-admin-status.1 ostree-admin-set-origin.1 ostree-admin-switch.1 ostree-admin-undeploy.1 ostree-admin-upgrade.1 ostree-admin.1 ostree-cat.1 ostree-checkout.1 ostree-checksum.1 ostree-commit.1 ostree-export.1 ostree-gpg-sign.1 ostree-config.1 ostree-diff.1 ostree-fsck.1 ostree-init.1 ostree-log.1 ostree-ls.1 ostree-prune.1 ostree-pull-local.1 ostree-pull.1 ostree-refs.1 ostree-remote.1 ostree-reset.1 ostree-rev-parse.1 ostree-show.1 ostree-summary.1 ostree-static-delta.1 ostree-trivial-httpd.1 +man1_files = ostree.1 ostree-admin-cleanup.1 \ +ostree-admin-config-diff.1 ostree-admin-deploy.1 \ +ostree-admin-init-fs.1 ostree-admin-instutil.1 ostree-admin-os-init.1 \ +ostree-admin-status.1 ostree-admin-set-origin.1 ostree-admin-switch.1 \ +ostree-admin-undeploy.1 ostree-admin-upgrade.1 ostree-admin-unlock.1 \ +ostree-admin.1 ostree-cat.1 ostree-checkout.1 ostree-checksum.1 \ +ostree-commit.1 ostree-export.1 ostree-gpg-sign.1 ostree-config.1 \ +ostree-diff.1 ostree-fsck.1 ostree-init.1 ostree-log.1 ostree-ls.1 \ +ostree-prune.1 ostree-pull-local.1 ostree-pull.1 ostree-refs.1 \ +ostree-remote.1 ostree-reset.1 ostree-rev-parse.1 ostree-show.1 \ +ostree-summary.1 ostree-static-delta.1 ostree-trivial-httpd.1 if BUILDOPT_FUSE man1_files += rofiles-fuse.1 diff --git a/Makefile-ostree.am b/Makefile-ostree.am index ff7e372b..0ef5c4ee 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -66,6 +66,7 @@ ostree_SOURCES += \ src/ostree/ot-admin-builtin-status.c \ src/ostree/ot-admin-builtin-switch.c \ src/ostree/ot-admin-builtin-upgrade.c \ + src/ostree/ot-admin-builtin-unlock.c \ src/ostree/ot-admin-builtins.h \ src/ostree/ot-admin-instutil-builtin-selinux-ensure-labeled.c \ src/ostree/ot-admin-instutil-builtin-set-kargs.c \ diff --git a/Makefile-tests.am b/Makefile-tests.am index 6ec58357..9f359ad8 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -17,67 +17,69 @@ # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. +include $(top_srcdir)/buildutil/glib-tap.mk -if BUILDOPT_INSTALL_TESTS +# We should probably consider flipping the default for DEBUG. Also, +# include the builddir in $PATH so we find our just-built ostree +# binary. +TESTS_ENVIRONMENT += OT_TESTS_DEBUG=1 \ + PATH=$$(cd $(top_builddir) && pwd):$${PATH} -insttestdir=$(pkglibexecdir)/installed-tests -testfiles = test-basic \ - test-pull-subpath \ - test-archivez \ - test-remote-add \ - test-remote-gpg-import \ - test-commit-sign \ - test-export \ - test-help \ - test-libarchive \ - test-pull-archive-z \ - test-pull-commit-only \ - test-pull-corruption \ - test-pull-depth \ - test-pull-mirror-summary \ - test-pull-large-metadata \ - test-pull-metalink \ - test-pull-summary-sigs \ - test-pull-resume \ - test-local-pull-depth \ - test-gpg-signed-commit \ - test-admin-upgrade-unconfigured \ - test-admin-deploy-syslinux \ - test-admin-deploy-2 \ - test-admin-deploy-karg \ - test-admin-deploy-switch \ - test-admin-deploy-etcmerge-cornercases \ - test-admin-deploy-uboot \ - test-admin-instutil-set-kargs \ - test-admin-upgrade-not-backwards \ - test-admin-pull-deploy-commit \ - test-admin-locking \ - test-admin-deploy-clean \ - test-repo-checkout-subpath \ - test-reset-nonlinear \ - test-oldstyle-partial \ - test-setuid \ - test-delta \ - test-xattrs \ - test-auto-summary \ - test-prune \ +test_scripts = \ + tests/test-basic.sh \ + tests/test-pull-subpath.sh \ + tests/test-archivez.sh \ + tests/test-remote-add.sh \ + tests/test-remote-gpg-import.sh \ + tests/test-commit-sign.sh \ + tests/test-export.sh \ + tests/test-help.sh \ + tests/test-libarchive.sh \ + tests/test-pull-archive-z.sh \ + tests/test-pull-commit-only.sh \ + tests/test-pull-corruption.sh \ + tests/test-pull-depth.sh \ + tests/test-pull-mirror-summary.sh \ + tests/test-pull-large-metadata.sh \ + tests/test-pull-metalink.sh \ + tests/test-pull-summary-sigs.sh \ + tests/test-pull-resume.sh \ + tests/test-local-pull-depth.sh \ + tests/test-gpg-signed-commit.sh \ + tests/test-admin-upgrade-unconfigured.sh \ + tests/test-admin-deploy-syslinux.sh \ + tests/test-admin-deploy-2.sh \ + tests/test-admin-deploy-karg.sh \ + tests/test-admin-deploy-switch.sh \ + tests/test-admin-deploy-etcmerge-cornercases.sh \ + tests/test-admin-deploy-uboot.sh \ + tests/test-admin-instutil-set-kargs.sh \ + tests/test-admin-upgrade-not-backwards.sh \ + tests/test-admin-pull-deploy-commit.sh \ + tests/test-admin-locking.sh \ + tests/test-admin-deploy-clean.sh \ + tests/test-repo-checkout-subpath.sh \ + tests/test-reset-nonlinear.sh \ + tests/test-oldstyle-partial.sh \ + tests/test-setuid.sh \ + tests/test-delta.sh \ + tests/test-xattrs.sh \ + tests/test-auto-summary.sh \ + tests/test-prune.sh \ + tests/test-refs.sh \ + tests/test-demo-buildsystem.sh \ $(NULL) if BUILDOPT_FUSE -testfiles += test-rofiles-fuse +test_scripts += tests/test-rofiles-fuse.sh endif -insttest_SCRIPTS = $(addprefix tests/,$(testfiles:=.sh)) - # This one uses corrupt-repo-ref.js if BUILDOPT_GJS -testfiles += test-corruption +test_scripts += tests/test-corruption.sh endif -testmetadir = $(datadir)/installed-tests/$(PACKAGE) -testmeta_DATA = $(testfiles:=.test) - -insttest_DATA = tests/archive-test.sh \ +installed_test_data = tests/archive-test.sh \ tests/pull-test.sh \ tests/libtest.sh \ tests/admin-test.sh \ @@ -89,69 +91,61 @@ insttest_DATA = tests/archive-test.sh \ tests/pre-endian-deltas-repo-little.tar.xz \ $(NULL) -insttest_SCRIPTS += \ - tests/syslinux-entries-crosscheck.py \ - $(NULL) +test_extra_scripts = tests/syslinux-entries-crosscheck.py -gpginsttestdir = $(pkglibexecdir)/installed-tests/gpghome +# We can't use nobase_ as we need to strip off the tests/, can't +# use plain installed_ as we do need the gpghome/ prefix. +if ENABLE_INSTALLED_TESTS +gpginsttestdir = $(installed_testdir)/gpghome gpginsttest_DATA = tests/gpghome/secring.gpg \ + tests/gpghome/pubring.gpg \ tests/gpghome/trustdb.gpg \ tests/gpghome/key1.asc \ tests/gpghome/key2.asc \ tests/gpghome/key3.asc -gpginsttest_trusteddir = $(pkglibexecdir)/installed-tests/gpghome/trusted +gpginsttest_trusteddir = $(installed_testdir)/gpghome/trusted gpginsttest_trusted_DATA = tests/gpghome/trusted/pubring.gpg -install-gpg-data-hook: - ln -sf trusted/pubring.gpg $(DESTDIR)$(gpginsttestdir)/pubring.gpg -INSTALL_DATA_HOOKS += install-gpg-data-hook - -%.test: tests/%.sh Makefile - $(AM_V_GEN) (echo '[Test]' > $@.tmp; \ - echo 'Exec=$(pkglibexecdir)/installed-tests/$(notdir $<)' >> $@.tmp; \ - echo 'Type=session' >> $@.tmp; \ - echo 'Output=TAP' >> $@.tmp; \ - mv $@.tmp $@) - -%.test: tests/%.js Makefile - $(AM_V_GEN) (echo '[Test]' > $@.tmp; \ - echo 'Exec=env TESTDATADIR=$(pkglibexecdir)/installed-tests $(pkglibexecdir)/installed-tests/$(notdir $<)' >> $@.tmp; \ - echo 'Type=session' >> $@.tmp; \ - mv $@.tmp $@) +gpgvinsttestdir = $(installed_testdir)/gpg-verify-data +gpgvinsttest_DATA = $(addprefix tests/gpg-verify-data/, \ + gpg.conf lgpl2 lgpl2.sig pubring.gpg secring.gpg trustdb.gpg) +endif if BUILDOPT_GJS -insttest_SCRIPTS += tests/test-core.js \ +installed_test_scripts = tests/test-core.js \ tests/test-sizes.js \ tests/test-sysroot.js \ $(NULL) -testmeta_DATA += test-core.test test-sizes.test test-sysroot.test endif -insttest_LTLIBRARIES = libreaddir-rand.la +test_ltlibraries = libreaddir-rand.la libreaddir_rand_la_SOURCES = tests/readdir-rand.c libreaddir_rand_la_CFLAGS = $(OT_INTERNAL_GIO_UNIX_CFLAGS) libreaddir_rand_la_LIBADD = $(OT_INTERNAL_GIO_UNIX_LIBS) libreaddir_rand_la_LDFLAGS = -avoid-version -endif +test_programs = tests/test-varint tests/test-ot-unix-utils tests/test-bsdiff tests/test-mutable-tree \ + tests/test-keyfile-utils tests/test-ot-opt-utils tests/test-ot-tool-util \ + tests/test-gpg-verify-result tests/test-checksum tests/test-lzma tests/test-rollsum \ + tests/test-basic-c tests/test-sysroot-c -# "make check" do not depend from --enable-installed-tests +# An interactive tool noinst_PROGRAMS += tests/test-rollsum-cli -TESTS = tests/test-varint tests/test-ot-unix-utils tests/test-bsdiff tests/test-mutable-tree \ - tests/test-keyfile-utils tests/test-ot-opt-utils tests/test-ot-tool-util \ - tests/test-gpg-verify-result tests/test-checksum tests/test-lzma tests/test-rollsum - if USE_LIBARCHIVE -TESTS += tests/test-libarchive-import +test_programs += tests/test-libarchive-import endif -check_PROGRAMS = $(TESTS) -TESTS_ENVIRONMENT = \ - G_TEST_SRCDIR=$(abs_srcdir)/tests \ - G_TEST_BUILDDIR=$(abs_builddir)/tests -TESTS_CFLAGS = $(ostree_bin_shared_cflags) $(OT_INTERNAL_GIO_UNIX_CFLAGS) -I$(srcdir)/libglnx -TESTS_LDADD = $(ostree_bin_shared_ldadd) $(OT_INTERNAL_GIO_UNIX_LIBS) +common_tests_cflags = $(ostree_bin_shared_cflags) $(OT_INTERNAL_GIO_UNIX_CFLAGS) -I$(srcdir)/libglnx +common_tests_ldadd = $(ostree_bin_shared_ldadd) $(OT_INTERNAL_GIO_UNIX_LIBS) + +noinst_LTLIBRARIES += libostreetest.la +libostreetest_la_SOURCES = tests/libostreetest.c +libostreetest_la_CFLAGS = $(common_tests_cflags) -I $(srcdir)/tests +libostreetest_la_LIBADD = $(common_tests_ldadd) + +TESTS_CFLAGS = $(common_tests_cflags) +TESTS_LDADD = $(common_tests_ldadd) libostreetest.la tests_test_rollsum_cli_SOURCES = src/libostree/ostree-rollsum.c tests/test-rollsum-cli.c tests_test_rollsum_cli_CFLAGS = $(TESTS_CFLAGS) $(OT_DEP_ZLIB_CFLAGS) @@ -164,6 +158,12 @@ tests_test_rollsum_LDADD = libbupsplit.la $(TESTS_LDADD) $(OT_DEP_ZLIB_LIBS) tests_test_mutable_tree_CFLAGS = $(TESTS_CFLAGS) tests_test_mutable_tree_LDADD = $(TESTS_LDADD) +tests_test_basic_c_CFLAGS = $(TESTS_CFLAGS) +tests_test_basic_c_LDADD = $(TESTS_LDADD) + +tests_test_sysroot_c_CFLAGS = $(TESTS_CFLAGS) +tests_test_sysroot_c_LDADD = $(TESTS_LDADD) + tests_test_ot_unix_utils_CFLAGS = $(TESTS_CFLAGS) tests_test_ot_unix_utils_LDADD = $(TESTS_LDADD) @@ -179,8 +179,8 @@ tests_test_checksum_CFLAGS = $(TESTS_CFLAGS) $(libglnx_cflags) tests_test_checksum_LDADD = $(TESTS_LDADD) tests_test_libarchive_import_SOURCES = tests/test-libarchive-import.c -tests_test_libarchive_import_CFLAGS = $(TESTS_CFLAGS) $(libglnx_cflags) -tests_test_libarchive_import_LDADD = $(TESTS_LDADD) +tests_test_libarchive_import_CFLAGS = $(TESTS_CFLAGS) $(libglnx_cflags) $(OT_DEP_LIBARCHIVE_CFLAGS) +tests_test_libarchive_import_LDADD = $(TESTS_LDADD) $(OT_DEP_LIBARCHIVE_LIBS) tests_test_keyfile_utils_CFLAGS = $(TESTS_CFLAGS) tests_test_keyfile_utils_LDADD = $(TESTS_LDADD) @@ -210,3 +210,15 @@ EXTRA_DIST += \ tests/gpg-verify-data/secring.gpg \ tests/gpg-verify-data/trustdb.gpg \ tests/gpg-verify-data/gpg.conf + +# Unfortunately the glib test data APIs don't actually handle +# non-recursive Automake, so we change our code to canonically look +# for tests/ which is just a symlink when installed. +if ENABLE_INSTALLED_TESTS +install-test-data-file-path-hack: + if test -L $(DESTDIR)$(installed_testdir)/tests; then \ + rm $(DESTDIR)$(installed_testdir)/tests; \ + fi + ln -s . $(DESTDIR)$(installed_testdir)/tests +INSTALL_DATA_HOOKS += install-test-data-file-path-hack +endif diff --git a/Makefile.am b/Makefile.am index 1625419b..a485cc75 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,7 +19,7 @@ include Makefile-decls.am shortened_sysconfdir = $$(echo "$(sysconfdir)" | sed -e 's|^$(prefix)||' -e 's|^/||') -ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} +ACLOCAL_AMFLAGS = -I buildutil ${ACLOCAL_FLAGS} AM_CPPFLAGS += -DDATADIR='"$(datadir)"' -DLIBEXECDIR='"$(libexecdir)"' \ -DLOCALEDIR=\"$(datadir)/locale\" -DSYSCONFDIR=\"$(sysconfdir)\" \ -DSHORTENED_SYSCONFDIR=\"$(shortened_sysconfdir)\" \ @@ -100,12 +100,3 @@ release-tarball-embedded: $(embed_dependency) embedded-dependencies/libsoup; \ mv ostree-embeddeps-$${GITVERSION}.tar{.tmp,}; \ gzip -f ostree-embeddeps-$${GITVERSION}.tar - -check-local: - @echo " *** NOTE ***" - @echo " *** NOTE ***" - @echo " \"make check\" only runs a subset of OSTree's tests." - @echo " The other tests use: use https://live.gnome.org/GnomeGoals/InstalledTests" - @echo " To run them, ostree must be configured with --enable-installed-tests and installed" - @echo " *** NOTE ***" - @echo " *** NOTE ***" diff --git a/README.md b/README.md index 10f7c32f..15cba7ab 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ provide a minimal host for Docker formatted Linux containers. Replicating a base immutable OS, then using Docker for applications meshes together two different tools with different tradeoffs. -[xdg-app](https://github.com/alexlarsson/xdg-app) uses OSTree +[xdg-app](https://github.com/alexlarsson/xdg-app) uses OSTree for desktop application containers. [GNOME Continuous](https://wiki.gnome.org/Projects/GnomeContinuous) is @@ -83,7 +83,7 @@ More documentation New! See the docs online at [Read The Docs (OSTree)](https://ostree.readthedocs.org/en/latest/ ) Some more information is available on the old wiki page: -https://wiki.gnome.org/Projects/OSTree + Contributing ------------ diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index 4b22e25b..87c138d8 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -260,6 +260,7 @@ ostree_repo_write_content_async ostree_repo_write_content_finish ostree_repo_resolve_rev ostree_repo_list_refs +ostree_repo_list_refs_ext ostree_repo_remote_list_refs ostree_repo_load_variant ostree_repo_load_commit @@ -380,6 +381,7 @@ ostree_sysroot_new ostree_sysroot_new_default ostree_sysroot_get_path ostree_sysroot_load +ostree_sysroot_load_if_changed ostree_sysroot_lock ostree_sysroot_try_lock ostree_sysroot_lock_async @@ -396,6 +398,7 @@ ostree_sysroot_get_deployment_origin_path ostree_sysroot_cleanup ostree_sysroot_prepare_cleanup ostree_sysroot_get_repo +ostree_sysroot_init_osname ostree_sysroot_deployment_set_kargs ostree_sysroot_write_deployments ostree_sysroot_deploy_tree diff --git a/buildutil/glib-tap.mk b/buildutil/glib-tap.mk new file mode 100644 index 00000000..ac4329bf --- /dev/null +++ b/buildutil/glib-tap.mk @@ -0,0 +1,125 @@ +# GLIB - Library of useful C routines + +TESTS_ENVIRONMENT= \ + G_TEST_SRCDIR="$(abs_srcdir)" \ + G_TEST_BUILDDIR="$(abs_builddir)" \ + UNINSTALLEDTESTS=1 \ + G_DEBUG=gc-friendly \ + MALLOC_CHECK_=2 \ + MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) +LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/buildutil/tap-driver.sh +LOG_COMPILER = $(top_srcdir)/buildutil/tap-test + +TESTS = + +installed_test_LTLIBRARIES = +installed_test_PROGRAMS = +installed_test_SCRIPTS = +installed_test_DATA = +nobase_installed_test_DATA = + +noinst_SCRIPTS = +noinst_DATA = + +check_LTLIBRARIES = +check_PROGRAMS = +check_SCRIPTS = +check_DATA = + +# We support a fairly large range of possible variables. It is expected that all types of files in a test suite +# will belong in exactly one of the following variables. +# +# First, we support the usual automake suffixes, but in lowercase, with the customary meaning: +# +# test_programs, test_scripts, test_data, test_ltlibraries +# +# The above are used to list files that are involved in both uninstalled and installed testing. The +# test_programs and test_scripts are taken to be actual testcases and will be run as part of the test suite. +# Note that _data is always used with the nobase_ automake variable name to ensure that installed test data is +# installed in the same way as it appears in the package layout. +# +# In order to mark a particular file as being only for one type of testing, use 'installed' or 'uninstalled', +# like so: +# +# installed_test_programs, uninstalled_test_programs +# installed_test_scripts, uninstalled_test_scripts +# installed_test_data, uninstalled_test_data +# installed_test_ltlibraries, uninstalled_test_ltlibraries +# +# Additionally, we support 'extra' infixes for programs and scripts. This is used for support programs/scripts +# that should not themselves be run as testcases (but exist to be used from other testcases): +# +# test_extra_programs, installed_test_extra_programs, uninstalled_test_extra_programs +# test_extra_scripts, installed_test_extra_scripts, uninstalled_test_extra_scripts +# +# Additionally, for _scripts and _data, we support the customary dist_ prefix so that the named script or data +# file automatically end up in the tarball. +# +# dist_test_scripts, dist_test_data, dist_test_extra_scripts +# dist_installed_test_scripts, dist_installed_test_data, dist_installed_test_extra_scripts +# dist_uninstalled_test_scripts, dist_uninstalled_test_data, dist_uninstalled_test_extra_scripts +# +# Note that no file is automatically disted unless it appears in one of the dist_ variables. This follows the +# standard automake convention of not disting programs scripts or data by default. +# +# test_programs, test_scripts, uninstalled_test_programs and uninstalled_test_scripts (as well as their disted +# variants) will be run as part of the in-tree 'make check'. These are all assumed to be runnable under +# gtester. That's a bit strange for scripts, but it's possible. + +TESTS += $(test_programs) $(test_scripts) $(uninstalled_test_programs) $(uninstalled_test_scripts) \ + $(dist_test_scripts) $(dist_uninstalled_test_scripts) + +# Note: build even the installed-only targets during 'make check' to ensure that they still work. +# We need to do a bit of trickery here and manage disting via EXTRA_DIST instead of using dist_ prefixes to +# prevent automake from mistreating gmake functions like $(wildcard ...) and $(addprefix ...) as if they were +# filenames, including removing duplicate instances of the opening part before the space, eg. '$(addprefix'. +all_test_programs = $(test_programs) $(uninstalled_test_programs) $(installed_test_programs) \ + $(test_extra_programs) $(uninstalled_test_extra_programs) $(installed_test_extra_programs) +all_test_scripts = $(test_scripts) $(uninstalled_test_scripts) $(installed_test_scripts) \ + $(test_extra_scripts) $(uninstalled_test_extra_scripts) $(installed_test_extra_scripts) +all_dist_test_scripts = $(dist_test_scripts) $(dist_uninstalled_test_scripts) $(dist_installed_test_scripts) \ + $(dist_test_extra_scripts) $(dist_uninstalled_test_extra_scripts) $(dist_installed_test_extra_scripts) +all_test_scripts += $(all_dist_test_scripts) +EXTRA_DIST += $(all_dist_test_scripts) +all_test_data = $(test_data) $(uninstalled_test_data) $(installed_test_data) +all_dist_test_data = $(dist_test_data) $(dist_uninstalled_test_data) $(dist_installed_test_data) +all_test_data += $(all_dist_test_data) +EXTRA_DIST += $(all_dist_test_data) +all_test_ltlibs = $(test_ltlibraries) $(uninstalled_test_ltlibraries) $(installed_test_ltlibraries) + +if ENABLE_ALWAYS_BUILD_TESTS +noinst_LTLIBRARIES += $(all_test_ltlibs) +noinst_PROGRAMS += $(all_test_programs) +noinst_SCRIPTS += $(all_test_scripts) +noinst_DATA += $(all_test_data) +else +check_LTLIBRARIES += $(all_test_ltlibs) +check_PROGRAMS += $(all_test_programs) +check_SCRIPTS += $(all_test_scripts) +check_DATA += $(all_test_data) +endif + +if ENABLE_INSTALLED_TESTS +installed_test_PROGRAMS += $(test_programs) $(installed_test_programs) \ + $(test_extra_programs) $(installed_test_extra_programs) +installed_test_SCRIPTS += $(test_scripts) $(installed_test_scripts) \ + $(test_extra_scripts) $(test_installed_extra_scripts) +installed_test_SCRIPTS += $(dist_test_scripts) $(dist_test_extra_scripts) \ + $(dist_installed_test_scripts) $(dist_installed_test_extra_scripts) +installed_test_DATA += $(test_data) $(installed_test_data) +installed_test_DATA += $(dist_test_data) $(dist_installed_test_data) +installed_test_LTLIBRARIES += $(test_ltlibraries) $(installed_test_ltlibraries) +installed_testcases = $(test_programs) $(installed_test_programs) \ + $(test_scripts) $(installed_test_scripts) \ + $(dist_test_scripts) $(dist_installed_test_scripts) + +installed_test_meta_DATA = $(installed_testcases:=.test) + +%.test: %$(EXEEXT) Makefile + $(AM_V_GEN) (echo '[Test]' > $@.tmp; \ + echo 'Type=session' >> $@.tmp; \ + echo 'Exec=env G_TEST_SRCDIR=$(installed_testdir) G_TEST_BUILDDIR=$(installed_testdir) $(installed_testdir)/$(notdir $<)' >> $@.tmp; \ + mv $@.tmp $@) + +CLEANFILES += $(installed_test_meta_DATA) +endif diff --git a/buildutil/glibtests.m4 b/buildutil/glibtests.m4 new file mode 100644 index 00000000..27e90246 --- /dev/null +++ b/buildutil/glibtests.m4 @@ -0,0 +1,28 @@ +dnl GLIB_TESTS +dnl + +AC_DEFUN([GLIB_TESTS], +[ + AC_ARG_ENABLE(installed-tests, + AS_HELP_STRING([--enable-installed-tests], + [Enable installation of some test cases]), + [case ${enableval} in + yes) ENABLE_INSTALLED_TESTS="1" ;; + no) ENABLE_INSTALLED_TESTS="" ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-installed-tests]) ;; + esac]) + AM_CONDITIONAL([ENABLE_INSTALLED_TESTS], test "$ENABLE_INSTALLED_TESTS" = "1") + AC_ARG_ENABLE(always-build-tests, + AS_HELP_STRING([--enable-always-build-tests], + [Enable always building tests during 'make all']), + [case ${enableval} in + yes) ENABLE_ALWAYS_BUILD_TESTS="1" ;; + no) ENABLE_ALWAYS_BUILD_TESTS="" ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-always-build-tests]) ;; + esac]) + AM_CONDITIONAL([ENABLE_ALWAYS_BUILD_TESTS], test "$ENABLE_ALWAYS_BUILD_TESTS" = "1") + if test "$ENABLE_INSTALLED_TESTS" == "1"; then + AC_SUBST(installed_test_metadir, [${datadir}/installed-tests/]AC_PACKAGE_NAME) + AC_SUBST(installed_testdir, [${libexecdir}/installed-tests/]AC_PACKAGE_NAME) + fi +]) diff --git a/buildutil/tap-driver.sh b/buildutil/tap-driver.sh new file mode 100755 index 00000000..19aa531d --- /dev/null +++ b/buildutil/tap-driver.sh @@ -0,0 +1,652 @@ +#! /bin/sh +# Copyright (C) 2011-2013 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +scriptversion=2011-12-27.17; # UTC + +# Make unconditional expansion of undefined variables an error. This +# helps a lot in preventing typo-related bugs. +set -u + +me=tap-driver.sh + +fatal () +{ + echo "$me: fatal: $*" >&2 + exit 1 +} + +usage_error () +{ + echo "$me: $*" >&2 + print_usage >&2 + exit 2 +} + +print_usage () +{ + cat < + # + trap : 1 3 2 13 15 + if test $merge -gt 0; then + exec 2>&1 + else + exec 2>&3 + fi + "$@" + echo $? + ) | LC_ALL=C ${AM_TAP_AWK-awk} \ + -v me="$me" \ + -v test_script_name="$test_name" \ + -v log_file="$log_file" \ + -v trs_file="$trs_file" \ + -v expect_failure="$expect_failure" \ + -v merge="$merge" \ + -v ignore_exit="$ignore_exit" \ + -v comments="$comments" \ + -v diag_string="$diag_string" \ +' +# FIXME: the usages of "cat >&3" below could be optimized when using +# FIXME: GNU awk, and/on on systems that supports /dev/fd/. + +# Implementation note: in what follows, `result_obj` will be an +# associative array that (partly) simulates a TAP result object +# from the `TAP::Parser` perl module. + +## ----------- ## +## FUNCTIONS ## +## ----------- ## + +function fatal(msg) +{ + print me ": " msg | "cat >&2" + exit 1 +} + +function abort(where) +{ + fatal("internal error " where) +} + +# Convert a boolean to a "yes"/"no" string. +function yn(bool) +{ + return bool ? "yes" : "no"; +} + +function add_test_result(result) +{ + if (!test_results_index) + test_results_index = 0 + test_results_list[test_results_index] = result + test_results_index += 1 + test_results_seen[result] = 1; +} + +# Whether the test script should be re-run by "make recheck". +function must_recheck() +{ + for (k in test_results_seen) + if (k != "XFAIL" && k != "PASS" && k != "SKIP") + return 1 + return 0 +} + +# Whether the content of the log file associated to this test should +# be copied into the "global" test-suite.log. +function copy_in_global_log() +{ + for (k in test_results_seen) + if (k != "PASS") + return 1 + return 0 +} + +# FIXME: this can certainly be improved ... +function get_global_test_result() +{ + if ("ERROR" in test_results_seen) + return "ERROR" + if ("FAIL" in test_results_seen || "XPASS" in test_results_seen) + return "FAIL" + all_skipped = 1 + for (k in test_results_seen) + if (k != "SKIP") + all_skipped = 0 + if (all_skipped) + return "SKIP" + return "PASS"; +} + +function stringify_result_obj(result_obj) +{ + if (result_obj["is_unplanned"] || result_obj["number"] != testno) + return "ERROR" + + if (plan_seen == LATE_PLAN) + return "ERROR" + + if (result_obj["directive"] == "TODO") + return result_obj["is_ok"] ? "XPASS" : "XFAIL" + + if (result_obj["directive"] == "SKIP") + return result_obj["is_ok"] ? "SKIP" : COOKED_FAIL; + + if (length(result_obj["directive"])) + abort("in function stringify_result_obj()") + + return result_obj["is_ok"] ? COOKED_PASS : COOKED_FAIL +} + +function decorate_result(result) +{ + color_name = color_for_result[result] + if (color_name) + return color_map[color_name] "" result "" color_map["std"] + # If we are not using colorized output, or if we do not know how + # to colorize the given result, we should return it unchanged. + return result +} + +function report(result, details) +{ + if (result ~ /^(X?(PASS|FAIL)|SKIP|ERROR)/) + { + msg = ": " test_script_name + add_test_result(result) + } + else if (result == "#") + { + msg = " " test_script_name ":" + } + else + { + abort("in function report()") + } + if (length(details)) + msg = msg " " details + # Output on console might be colorized. + print decorate_result(result) msg + # Log the result in the log file too, to help debugging (this is + # especially true when said result is a TAP error or "Bail out!"). + print result msg | "cat >&3"; +} + +function testsuite_error(error_message) +{ + report("ERROR", "- " error_message) +} + +function handle_tap_result() +{ + details = result_obj["number"]; + if (length(result_obj["description"])) + details = details " " result_obj["description"] + + if (plan_seen == LATE_PLAN) + { + details = details " # AFTER LATE PLAN"; + } + else if (result_obj["is_unplanned"]) + { + details = details " # UNPLANNED"; + } + else if (result_obj["number"] != testno) + { + details = sprintf("%s # OUT-OF-ORDER (expecting %d)", + details, testno); + } + else if (result_obj["directive"]) + { + details = details " # " result_obj["directive"]; + if (length(result_obj["explanation"])) + details = details " " result_obj["explanation"] + } + + report(stringify_result_obj(result_obj), details) +} + +# `skip_reason` should be empty whenever planned > 0. +function handle_tap_plan(planned, skip_reason) +{ + planned += 0 # Avoid getting confused if, say, `planned` is "00" + if (length(skip_reason) && planned > 0) + abort("in function handle_tap_plan()") + if (plan_seen) + { + # Error, only one plan per stream is acceptable. + testsuite_error("multiple test plans") + return; + } + planned_tests = planned + # The TAP plan can come before or after *all* the TAP results; we speak + # respectively of an "early" or a "late" plan. If we see the plan line + # after at least one TAP result has been seen, assume we have a late + # plan; in this case, any further test result seen after the plan will + # be flagged as an error. + plan_seen = (testno >= 1 ? LATE_PLAN : EARLY_PLAN) + # If testno > 0, we have an error ("too many tests run") that will be + # automatically dealt with later, so do not worry about it here. If + # $plan_seen is true, we have an error due to a repeated plan, and that + # has already been dealt with above. Otherwise, we have a valid "plan + # with SKIP" specification, and should report it as a particular kind + # of SKIP result. + if (planned == 0 && testno == 0) + { + if (length(skip_reason)) + skip_reason = "- " skip_reason; + report("SKIP", skip_reason); + } +} + +function extract_tap_comment(line) +{ + if (index(line, diag_string) == 1) + { + # Strip leading `diag_string` from `line`. + line = substr(line, length(diag_string) + 1) + # And strip any leading and trailing whitespace left. + sub("^[ \t]*", "", line) + sub("[ \t]*$", "", line) + # Return what is left (if any). + return line; + } + return ""; +} + +# When this function is called, we know that line is a TAP result line, +# so that it matches the (perl) RE "^(not )?ok\b". +function setup_result_obj(line) +{ + # Get the result, and remove it from the line. + result_obj["is_ok"] = (substr(line, 1, 2) == "ok" ? 1 : 0) + sub("^(not )?ok[ \t]*", "", line) + + # If the result has an explicit number, get it and strip it; otherwise, + # automatically assing the next progresive number to it. + if (line ~ /^[0-9]+$/ || line ~ /^[0-9]+[^a-zA-Z0-9_]/) + { + match(line, "^[0-9]+") + # The final `+ 0` is to normalize numbers with leading zeros. + result_obj["number"] = substr(line, 1, RLENGTH) + 0 + line = substr(line, RLENGTH + 1) + } + else + { + result_obj["number"] = testno + } + + if (plan_seen == LATE_PLAN) + # No further test results are acceptable after a "late" TAP plan + # has been seen. + result_obj["is_unplanned"] = 1 + else if (plan_seen && testno > planned_tests) + result_obj["is_unplanned"] = 1 + else + result_obj["is_unplanned"] = 0 + + # Strip trailing and leading whitespace. + sub("^[ \t]*", "", line) + sub("[ \t]*$", "", line) + + # This will have to be corrected if we have a "TODO"/"SKIP" directive. + result_obj["description"] = line + result_obj["directive"] = "" + result_obj["explanation"] = "" + + if (index(line, "#") == 0) + return # No possible directive, nothing more to do. + + # Directives are case-insensitive. + rx = "[ \t]*#[ \t]*([tT][oO][dD][oO]|[sS][kK][iI][pP])[ \t]*" + + # See whether we have the directive, and if yes, where. + pos = match(line, rx "$") + if (!pos) + pos = match(line, rx "[^a-zA-Z0-9_]") + + # If there was no TAP directive, we have nothing more to do. + if (!pos) + return + + # Let`s now see if the TAP directive has been escaped. For example: + # escaped: ok \# SKIP + # not escaped: ok \\# SKIP + # escaped: ok \\\\\# SKIP + # not escaped: ok \ # SKIP + if (substr(line, pos, 1) == "#") + { + bslash_count = 0 + for (i = pos; i > 1 && substr(line, i - 1, 1) == "\\"; i--) + bslash_count += 1 + if (bslash_count % 2) + return # Directive was escaped. + } + + # Strip the directive and its explanation (if any) from the test + # description. + result_obj["description"] = substr(line, 1, pos - 1) + # Now remove the test description from the line, that has been dealt + # with already. + line = substr(line, pos) + # Strip the directive, and save its value (normalized to upper case). + sub("^[ \t]*#[ \t]*", "", line) + result_obj["directive"] = toupper(substr(line, 1, 4)) + line = substr(line, 5) + # Now get the explanation for the directive (if any), with leading + # and trailing whitespace removed. + sub("^[ \t]*", "", line) + sub("[ \t]*$", "", line) + result_obj["explanation"] = line +} + +function get_test_exit_message(status) +{ + if (status == 0) + return "" + if (status !~ /^[1-9][0-9]*$/) + abort("getting exit status") + if (status < 127) + exit_details = "" + else if (status == 127) + exit_details = " (command not found?)" + else if (status >= 128 && status <= 255) + exit_details = sprintf(" (terminated by signal %d?)", status - 128) + else if (status > 256 && status <= 384) + # We used to report an "abnormal termination" here, but some Korn + # shells, when a child process die due to signal number n, can leave + # in $? an exit status of 256+n instead of the more standard 128+n. + # Apparently, both behaviours are allowed by POSIX (2008), so be + # prepared to handle them both. See also Austing Group report ID + # 0000051 + exit_details = sprintf(" (terminated by signal %d?)", status - 256) + else + # Never seen in practice. + exit_details = " (abnormal termination)" + return sprintf("exited with status %d%s", status, exit_details) +} + +function write_test_results() +{ + print ":global-test-result: " get_global_test_result() > trs_file + print ":recheck: " yn(must_recheck()) > trs_file + print ":copy-in-global-log: " yn(copy_in_global_log()) > trs_file + for (i = 0; i < test_results_index; i += 1) + print ":test-result: " test_results_list[i] > trs_file + close(trs_file); +} + +BEGIN { + +## ------- ## +## SETUP ## +## ------- ## + +'"$init_colors"' + +# Properly initialized once the TAP plan is seen. +planned_tests = 0 + +COOKED_PASS = expect_failure ? "XPASS": "PASS"; +COOKED_FAIL = expect_failure ? "XFAIL": "FAIL"; + +# Enumeration-like constants to remember which kind of plan (if any) +# has been seen. It is important that NO_PLAN evaluates "false" as +# a boolean. +NO_PLAN = 0 +EARLY_PLAN = 1 +LATE_PLAN = 2 + +testno = 0 # Number of test results seen so far. +bailed_out = 0 # Whether a "Bail out!" directive has been seen. + +# Whether the TAP plan has been seen or not, and if yes, which kind +# it is ("early" is seen before any test result, "late" otherwise). +plan_seen = NO_PLAN + +## --------- ## +## PARSING ## +## --------- ## + +is_first_read = 1 + +while (1) + { + # Involutions required so that we are able to read the exit status + # from the last input line. + st = getline + if (st < 0) # I/O error. + fatal("I/O error while reading from input stream") + else if (st == 0) # End-of-input + { + if (is_first_read) + abort("in input loop: only one input line") + break + } + if (is_first_read) + { + is_first_read = 0 + nextline = $0 + continue + } + else + { + curline = nextline + nextline = $0 + $0 = curline + } + # Copy any input line verbatim into the log file. + print | "cat >&3" + # Parsing of TAP input should stop after a "Bail out!" directive. + if (bailed_out) + continue + + # TAP test result. + if ($0 ~ /^(not )?ok$/ || $0 ~ /^(not )?ok[^a-zA-Z0-9_]/) + { + testno += 1 + setup_result_obj($0) + handle_tap_result() + } + # TAP plan (normal or "SKIP" without explanation). + else if ($0 ~ /^1\.\.[0-9]+[ \t]*$/) + { + # The next two lines will put the number of planned tests in $0. + sub("^1\\.\\.", "") + sub("[^0-9]*$", "") + handle_tap_plan($0, "") + continue + } + # TAP "SKIP" plan, with an explanation. + else if ($0 ~ /^1\.\.0+[ \t]*#/) + { + # The next lines will put the skip explanation in $0, stripping + # any leading and trailing whitespace. This is a little more + # tricky in truth, since we want to also strip a potential leading + # "SKIP" string from the message. + sub("^[^#]*#[ \t]*(SKIP[: \t][ \t]*)?", "") + sub("[ \t]*$", ""); + handle_tap_plan(0, $0) + } + # "Bail out!" magic. + # Older versions of prove and TAP::Harness (e.g., 3.17) did not + # recognize a "Bail out!" directive when preceded by leading + # whitespace, but more modern versions (e.g., 3.23) do. So we + # emulate the latter, "more modern" behaviour. + else if ($0 ~ /^[ \t]*Bail out!/) + { + bailed_out = 1 + # Get the bailout message (if any), with leading and trailing + # whitespace stripped. The message remains stored in `$0`. + sub("^[ \t]*Bail out![ \t]*", ""); + sub("[ \t]*$", ""); + # Format the error message for the + bailout_message = "Bail out!" + if (length($0)) + bailout_message = bailout_message " " $0 + testsuite_error(bailout_message) + } + # Maybe we have too look for dianogtic comments too. + else if (comments != 0) + { + comment = extract_tap_comment($0); + if (length(comment)) + report("#", comment); + } + } + +## -------- ## +## FINISH ## +## -------- ## + +# A "Bail out!" directive should cause us to ignore any following TAP +# error, as well as a non-zero exit status from the TAP producer. +if (!bailed_out) + { + if (!plan_seen) + { + testsuite_error("missing test plan") + } + else if (planned_tests != testno) + { + bad_amount = testno > planned_tests ? "many" : "few" + testsuite_error(sprintf("too %s tests run (expected %d, got %d)", + bad_amount, planned_tests, testno)) + } + if (!ignore_exit) + { + # Fetch exit status from the last line. + exit_message = get_test_exit_message(nextline) + if (exit_message) + testsuite_error(exit_message) + } + } + +write_test_results() + +exit 0 + +} # End of "BEGIN" block. +' + +# TODO: document that we consume the file descriptor 3 :-( +} 3>"$log_file" + +test $? -eq 0 || fatal "I/O or internal error" + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/buildutil/tap-test b/buildutil/tap-test new file mode 100755 index 00000000..e7914541 --- /dev/null +++ b/buildutil/tap-test @@ -0,0 +1,23 @@ +#! /bin/bash +# +# Run a test in tap mode, ensuring we have a temporary directory. We +# always use /var/tmp becuase we might want to use user xattrs, which +# aren't available on tmpfs. +# +# The test binary is passed as $1 + +srcd=$(cd $(dirname $1) && pwd) +bn=$(basename $1) +tempdir=$(mktemp -d /var/tmp/tap-test.XXXXXX) +touch ${tempdir}/.testtmp +function cleanup () { + if test -n "${TEST_SKIP_CLEANUP:-}"; then + echo "Skipping cleanup of ${tempdir}" + else if test -f ${tempdir}/.test; then + rm "${tempdir}" -rf + fi + fi +} +trap cleanup EXIT +cd ${tempdir} +${srcd}/${bn} -k --tap diff --git a/cfg.mk b/cfg.mk index bdbeb9ea..5c3f0b40 100644 --- a/cfg.mk +++ b/cfg.mk @@ -1,4 +1,4 @@ -export VC_LIST_EXCEPT_DEFAULT=^(lib/.*|m4/.*|md5/.*|build-aux/.*|src/gettext\.h|.*ChangeLog)$$ +export VC_LIST_EXCEPT_DEFAULT=^(lib/.*|m4/.*|md5/.*|build-aux/.*|src/gettext\.h|.*ChangeLog|buildutil/.*)$$ local-checks-to-skip = \ sc_const_long_option \ @@ -27,4 +27,4 @@ local-checks-to-skip = \ show-vc-list-except: @$(VC_LIST_EXCEPT) -VC_LIST_ALWAYS_EXCLUDE_REGEX = ^ABOUT-NLS|maint.mk|*.gpg|*.sig$$ +VC_LIST_ALWAYS_EXCLUDE_REGEX = ^ABOUT-NLS|maint.mk|*.gpg|*.sig|.xz$$ diff --git a/configure.ac b/configure.ac index 0687ecdb..86df2a41 100644 --- a/configure.ac +++ b/configure.ac @@ -1,11 +1,11 @@ AC_PREREQ([2.63]) -AC_INIT([ostree], [2016.3], [walters@verbum.org]) +AC_INIT([ostree], [2016.4], [walters@verbum.org]) AC_CONFIG_HEADER([config.h]) -AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_MACRO_DIR([buildutil]) AC_CONFIG_AUX_DIR([build-aux]) AM_INIT_AUTOMAKE([1.13 -Wno-portability foreign no-define tar-ustar no-dist-gzip dist-xz - color-tests parallel-tests subdir-objects]) + color-tests subdir-objects]) AM_MAINTAINER_MODE([enable]) AM_SILENT_RULES([yes]) AC_USE_SYSTEM_EXTENSIONS @@ -34,11 +34,7 @@ LT_INIT([disable-static]) OSTREE_FEATURES="" AC_SUBST([OSTREE_FEATURES]) -AC_ARG_ENABLE(installed_tests, - AS_HELP_STRING([--enable-installed-tests], - [Install test programs (default: no)]),, - [enable_installed_tests=no]) -AM_CONDITIONAL(BUILDOPT_INSTALL_TESTS, test x$enable_installed_tests = xyes) +GLIB_TESTS AC_CHECK_HEADER([sys/xattr.h],,[AC_MSG_ERROR([You must have sys/xattr.h from glibc])]) @@ -196,6 +192,31 @@ AS_IF([ test x$with_selinux != xno ], [ if test x$with_selinux != xno; then OSTREE_FEATURES="$OSTREE_FEATURES +selinux"; fi AM_CONDITIONAL(USE_SELINUX, test $with_selinux != no) +dnl This is what is in RHEL7.2 right now, picking it arbitrarily +LIBMOUNT_DEPENDENCY="mount >= 2.23.0" + +AC_ARG_WITH(libmount, + AS_HELP_STRING([--without-libmount], [Do not use libmount]), + :, with_libmount=maybe) + +AS_IF([ test x$with_libmount != xno ], [ + AC_MSG_CHECKING([for $LIBMOUNT_DEPENDENCY]) + PKG_CHECK_EXISTS($LIBMOUNT_DEPENDENCY, have_libmount=yes, have_libmount=no) + AC_MSG_RESULT([$have_libmount]) + AS_IF([ test x$have_libmount = xno && test x$with_libmount != xmaybe ], [ + AC_MSG_ERROR([libmount is enabled but could not be found]) + ]) + AS_IF([ test x$have_libmount = xyes], [ + AC_DEFINE([HAVE_LIBMOUNT], 1, [Define if we have libmount.pc]) + PKG_CHECK_MODULES(OT_DEP_LIBMOUNT, $LIBMOUNT_DEPENDENCY) + with_libmount=yes + ], [ + with_libmount=no + ]) +], [ with_libmount=no ]) +if test x$with_libmount != xno; then OSTREE_FEATURES="$OSTREE_FEATURES +libmount"; fi +AM_CONDITIONAL(USE_LIBMOUNT, test $with_libmount != no) + # Enabled by default because I think people should use it. AC_ARG_ENABLE(rofiles-fuse, [AS_HELP_STRING([--enable-rofiles-fuse], @@ -264,6 +285,7 @@ echo " libsoup (retrieve remote HTTP repositories): $with_soup libsoup TLS client certs: $have_libsoup_client_certs SELinux: $with_selinux + libmount: $with_libmount libarchive (parse tar files directly): $with_libarchive static deltas: yes (always enabled now) man pages (xsltproc): $enable_man diff --git a/contrib/golang/COPYING b/contrib/golang/COPYING new file mode 100644 index 00000000..aa93b4da --- /dev/null +++ b/contrib/golang/COPYING @@ -0,0 +1,17 @@ +Portions of this code are derived from: + +https://github.com/dradtke/gotk3 + +Copyright (c) 2013 Conformal Systems LLC. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/contrib/golang/README.md b/contrib/golang/README.md new file mode 100644 index 00000000..60a4856d --- /dev/null +++ b/contrib/golang/README.md @@ -0,0 +1,2 @@ +This file contains demonstration FFI bindings for using `-lostree-1` +and `-larchive` from golang. diff --git a/contrib/golang/glibobject.go b/contrib/golang/glibobject.go new file mode 100644 index 00000000..585ccd70 --- /dev/null +++ b/contrib/golang/glibobject.go @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2013 Conformal Systems + * + * This file originated from: http://opensource.conformal.com/ + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package ostree + +// #cgo pkg-config: glib-2.0 gobject-2.0 +// #include +// #include +// #include +// #include "glibobject.go.h" +// #include +import "C" +import ( + "unsafe" + "runtime" + "fmt" + "errors" +) + +func GBool(b bool) C.gboolean { + if b { + return C.gboolean(1) + } + return C.gboolean(0) +} + +func GoBool(b C.gboolean) bool { + if b != 0 { + return true + } + return false +} + +type GError struct { + ptr unsafe.Pointer +} + +func NewGError() GError { + return GError{nil} +} + +func (e *GError) Native() *C.GError { + if e == nil { + return nil + } + return (*C.GError)(e.ptr) +} + +func ConvertGError(e *C.GError) error { + defer C.g_error_free(e) + return errors.New(C.GoString((*C.char)(C._g_error_get_message(e)))) +} + +type GType uint + +func (t GType) Name() string { + return C.GoString((*C.char)(C.g_type_name(C.GType(t)))) +} + +type GVariant struct { + ptr unsafe.Pointer +} + +func GVariantNew(p unsafe.Pointer) *GVariant { + o := &GVariant{p} + runtime.SetFinalizer(o, (*GVariant).Unref) + return o; +} + +func GVariantNewSink(p unsafe.Pointer) *GVariant { + o := &GVariant{p} + runtime.SetFinalizer(o, (*GVariant).Unref) + o.RefSink() + return o; +} + +func (v *GVariant) native() *C.GVariant { + return (*C.GVariant)(v.ptr); +} + +func (v *GVariant) Ref() { + C.g_variant_ref(v.native()) +} + +func (v *GVariant) Unref() { + C.g_variant_unref(v.native()) +} + +func (v *GVariant) RefSink() { + C.g_variant_ref_sink(v.native()) +} + +func (v *GVariant) TypeString() string { + cs := (*C.char)(C.g_variant_get_type_string(v.native())) + return C.GoString(cs) +} + +func (v *GVariant) GetChildValue(i int) *GVariant { + cchild := C.g_variant_get_child_value(v.native(), C.gsize(i)) + return GVariantNew(unsafe.Pointer(cchild)); +} + +func (v *GVariant) LookupString(key string) (string, error) { + ckey := C.CString(key) + defer C.free(unsafe.Pointer(ckey)) + // TODO: Find a way to have constant C strings in golang + cstr := C._g_variant_lookup_string(v.native(), ckey) + if cstr == nil { + return "", fmt.Errorf("No such key: %s", key) + } + return C.GoString(cstr), nil +} + +/* + * GObject + */ + +// IObject is an interface type implemented by Object and all types which embed +// an Object. It is meant to be used as a type for function arguments which +// require GObjects or any subclasses thereof. +type IObject interface { + toGObject() *C.GObject + ToObject() *GObject +} + +// Object is a representation of GLib's GObject. +type GObject struct { + ptr unsafe.Pointer +} + +func GObjectNew(p unsafe.Pointer) *GObject { + o := &GObject{p} + runtime.SetFinalizer(o, (*GObject).Unref) + return o; +} + +func (v *GObject) Ptr() unsafe.Pointer { + return v.ptr +} + +func (v *GObject) Native() *C.GObject { + if v == nil || v.ptr == nil { + return nil + } + return (*C.GObject)(v.ptr) +} + +func (v *GObject) toGObject() *C.GObject { + if v == nil { + return nil + } + return v.Native() +} + +func (v *GObject) Ref() { + C.g_object_ref(C.gpointer(v.ptr)) +} + +func (v *GObject) Unref() { + C.g_object_unref(C.gpointer(v.ptr)) +} + +func (v *GObject) RefSink() { + C.g_object_ref_sink(C.gpointer(v.ptr)) +} + +func (v *GObject) IsFloating() bool { + c := C.g_object_is_floating(C.gpointer(v.ptr)) + return GoBool(c) +} + +func (v *GObject) ForceFloating() { + C.g_object_force_floating((*C.GObject)(v.ptr)) +} + +// GIO types + +type GCancellable struct { + *GObject +} + +func (self *GCancellable) native() *C.GCancellable { + return (*C.GCancellable)(self.ptr) +} + +// At the moment, no cancellable API, just pass nil diff --git a/contrib/golang/glibobject.go.h b/contrib/golang/glibobject.go.h new file mode 100644 index 00000000..a55bd242 --- /dev/null +++ b/contrib/golang/glibobject.go.h @@ -0,0 +1,17 @@ +#include + +static char * +_g_error_get_message (GError *error) +{ + g_assert (error != NULL); + return error->message; +} + +static const char * +_g_variant_lookup_string (GVariant *v, const char *key) +{ + const char *r; + if (g_variant_lookup (v, key, "&s", &r)) + return r; + return NULL; +} diff --git a/contrib/golang/ostree.go b/contrib/golang/ostree.go new file mode 100644 index 00000000..0a60ef68 --- /dev/null +++ b/contrib/golang/ostree.go @@ -0,0 +1,94 @@ +// +build linux + +// Public API specification for libostree Go bindings + +package ostree + +import ( + "unsafe" +) + +// #cgo pkg-config: ostree-1 +// #include +// #include +// #include +// #include "ostree.go.h" +import "C" + +type Repo struct { + *GObject +} + +func RepoGetType() GType { + return GType(C.ostree_repo_get_type()) +} + +func (r *Repo) native() *C.OstreeRepo { + return (*C.OstreeRepo)(r.ptr) +} + +func repoFromNative(p *C.OstreeRepo) *Repo { + if p == nil { + return nil + } + o := GObjectNew(unsafe.Pointer(p)) + r := &Repo{o} + return r +} + +func RepoNewOpen(path string) (*Repo, error) { + var cerr *C.GError = nil + cpath := C.CString(path) + pathc := C.g_file_new_for_path(cpath); + defer C.g_object_unref(C.gpointer(pathc)) + crepo := C.ostree_repo_new(pathc) + repo := repoFromNative(crepo); + r := GoBool(C.ostree_repo_open(repo.native(), nil, &cerr)) + if !r { + return nil, ConvertGError(cerr) + } + return repo, nil +} + +func (r *Repo) GetParent() *Repo { + return repoFromNative(C.ostree_repo_get_parent(r.native())) +} + +type ObjectType int + +const ( + OBJECT_TYPE_FILE ObjectType = C.OSTREE_OBJECT_TYPE_FILE + OBJECT_TYPE_DIR_TREE = C.OSTREE_OBJECT_TYPE_DIR_TREE + OBJECT_TYPE_DIR_META = C.OSTREE_OBJECT_TYPE_DIR_META + OBJECT_TYPE_COMMIT = C.OSTREE_OBJECT_TYPE_COMMIT + OBJECT_TYPE_TOMBSTONE_COMMIT = C.OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT +) + +func (repo *Repo) LoadVariant(t ObjectType, checksum string) (*GVariant, error) { + var cerr *C.GError = nil + var cvariant *C.GVariant = nil + + r := GoBool(C.ostree_repo_load_variant(repo.native(), C.OstreeObjectType(t), C.CString(checksum), &cvariant, &cerr)) + if !r { + return nil, ConvertGError(cerr) + } + variant := GVariantNew(unsafe.Pointer(cvariant)) + return variant, nil +} + +func (repo *Repo) ResolveRev(ref string) (string, error) { + var cerr *C.GError = nil + var crev *C.char = nil + + r := GoBool(C.ostree_repo_resolve_rev(repo.native(), C.CString(ref), GBool(true), &crev, &cerr)) + if !r { + return "", ConvertGError(cerr) + } + defer C.free(unsafe.Pointer(crev)) + return C.GoString(crev), nil +} + +func (commit *GVariant) CommitGetMetadataKeyString(key string) (string, error) { + cmeta := GVariantNew(unsafe.Pointer(C.g_variant_get_child_value(commit.native(), 0))) + return cmeta.LookupString(key) +} diff --git a/contrib/golang/ostree.go.h b/contrib/golang/ostree.go.h new file mode 100644 index 00000000..b1a15517 --- /dev/null +++ b/contrib/golang/ostree.go.h @@ -0,0 +1,21 @@ +#include +#include + +static void +_ostree_repo_checkout_options_init_docker_union (OstreeRepoCheckoutOptions *opts) +{ + memset (opts, 0, sizeof (*opts)); + opts->mode = OSTREE_REPO_CHECKOUT_MODE_USER; + opts->overwrite_mode = OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES; + opts->disable_fsync = 1; + opts->process_whiteouts = 1; +} + +static const char * +_g_variant_lookup_string (GVariant *v, const char *key) +{ + const char *r; + if (g_variant_lookup (v, key, "&s", &r)) + return r; + return NULL; +} diff --git a/contrib/golang/ostree_test.go b/contrib/golang/ostree_test.go new file mode 100644 index 00000000..6b687d8b --- /dev/null +++ b/contrib/golang/ostree_test.go @@ -0,0 +1,55 @@ +// +build linux + +// Public API specification for libostree Go bindings + +package ostree + +import ( + "testing" +) + +func TestTypeName(t *testing.T) { + name := RepoGetType().Name(); + if name != "OstreeRepo" { + t.Errorf("%s != OstreeRepo"); + } +} + +func TestRepoNew(t *testing.T) { + r, err := RepoNewOpen("/ostree/repo") + if err != nil { + t.Errorf("%s", err); + return + } + parent := r.GetParent() + if parent != nil { + t.Errorf("Expected no parent") + return + } +} + +func TestRepoGetMetadataVersion(t *testing.T) { + r, err := RepoNewOpen("/ostree/repo") + if err != nil { + t.Errorf("%s", err); + return + } + commit,err := r.ResolveRev("rhel-atomic-host/7/x86_64/standard") + if err != nil { + t.Errorf("%s", err) + return + } + commitv,err := r.LoadVariant(OBJECT_TYPE_COMMIT, commit) + if err != nil { + t.Errorf("%s", err) + return + } + ver, err := commitv.CommitGetMetadataKeyString("version") + if err != nil { + t.Errorf("%s", err) + return + } + if ver != "7.1.3" { + t.Errorf("expected 7.1.3") + } +} diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 4458256e..397ffeb0 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -3,17 +3,18 @@ Submitting patches You can: - 1. Send mail to ostree-list@gnome.org, with the patch attached - 1. Submit a pull request against https://github.com/GNOME/ostree - 1. Attach them to https://bugzilla.gnome.org/ + 1. Send mail to , with the patch attached + 1. Submit a pull request against + 1. Attach them to -Please look at "git log" and match the commit log style. +Please look at `git log` and match the commit log style. Running the test suite ---------------------- -Currently, ostree uses https://wiki.gnome.org/GnomeGoals/InstalledTests -To run just ostree's tests: +Currently, OSTree uses + +To run just OSTree's tests: ./configure ... --enable-installed-tests gnome-desktop-testing-runner -p 0 ostree/ @@ -47,16 +48,16 @@ This is an example of an "early exit": myfunc (...) { gboolean ret = FALSE; - + /* some code */ - + /* some more code */ - + if (condition) return FALSE; - + /* some more code */ - + ret = TRUE; out: return ret; @@ -85,14 +86,14 @@ functions, particularly inside loops. For example, rather than: { /* deeply nested code */ } - + /* more nested code */ } } } Instead do this: - + static gboolean helperfunc (..., GError **error) { @@ -100,22 +101,21 @@ Instead do this: { /* deeply nested code */ } - + /* more nested code */ - + return ret; } - + while (condition) { /* some code */ if (!condition) continue; - + for (i = 0; i < somevalue; i++) { if (!helperfunc (..., i, error)) goto out; } } - diff --git a/docs/manual/adapting-existing.md b/docs/manual/adapting-existing.md index 858c82d3..8509d48e 100644 --- a/docs/manual/adapting-existing.md +++ b/docs/manual/adapting-existing.md @@ -140,7 +140,7 @@ Now, because we are merely installing new packages and not removing anything, we can make the major optimization of reusing our existing filesystem tree, and merely *layering* the composed filesystem tree of -these new packages on top. A command like this: +these new packages on top. A command like this: ``` ostree commit -b osname/releasename/description diff --git a/docs/manual/atomic-upgrades.md b/docs/manual/atomic-upgrades.md index 9ce2c8ae..fa576734 100644 --- a/docs/manual/atomic-upgrades.md +++ b/docs/manual/atomic-upgrades.md @@ -15,7 +15,7 @@ exactly one ref, which is stored in the `.origin` file for the deployment. The command `ostree admin upgrade` implements this. -o begin a simple upgrade, OSTree fetches the contents of the ref from +To begin a simple upgrade, OSTree fetches the contents of the ref from the remote server. Suppose we're tracking a ref named `exampleos/buildmaster/x86_64-runtime`. OSTree fetches the URL `http://$example.com/repo/refs/exampleos/buildmaster/x86_64-runtime`, @@ -56,11 +56,10 @@ checking it back out of the repo into a deployment. Given a commit to deploy, OSTree first allocates a directory for it. This is of the form `/boot/loader/entries/ostree-$osname-$checksum.$serial.conf`. -he $serial is normally 0, but if a +The `$serial` is normally 0, but if a given commit is deployed more than once, it will be incremented. his is supported because the previous deployment may have -configuration in `/etc` -hat we do not want to use or overwrite. +configuration in `/etc` that we do not want to use or overwrite. Now that we have a deployment directory, a 3-way merge is performed between the (by default) currently booted deployment's @@ -74,7 +73,7 @@ hardlink farm; the running system is untouched, and the bootloader configuration is untouched. We want to add this deployment o the "deployment list". -To support a more general case, OSTree supports atomic ransitioning +To support a more general case, OSTree supports atomic transitioning between arbitrary sets of deployments, with the restriction that the currently booted deployment must always be in the new set. In the normal case, we have exactly one deployment, which is the booted one, @@ -101,7 +100,10 @@ deployment lists. This happens when doing an upgrade that does not include the kernel; think of a simple translation update. OSTree optimizes for this case because on some systems `/boot` may be on a separate medium such as flash storage not optimized for significant -amounts of write traffic. +amounts of write traffic. Related to this, modern OSTree has support +for having `/boot` be a read-only mount by default - it will +automatically remount read-write just for the portion of time +necessary to update the bootloader configuration. To implement this, OSTree also maintains the directory `/ostree/boot.bootversion`, which is a set diff --git a/docs/manual/buildsystem-and-repos.md b/docs/manual/buildsystem-and-repos.md new file mode 100644 index 00000000..d418cb0e --- /dev/null +++ b/docs/manual/buildsystem-and-repos.md @@ -0,0 +1,180 @@ +# Writing a buildsystem and managing repositories + +OSTree is not a package system. It does not directly support building +source code. Rather, it is a tool for transporting and managing +content, along with package-system independent aspects like bootloader +management for updates. + +We'll assume here that we're planning to generate commits on a build +server, then have client systems replicate it. Doing client-side +assembly is also possible of course, but this discussion will focus +primarily on server-side concerns. + +## Build vs buy + +Therefore, you need to either pick an existing tool for writing +content into an OSTree repository, or to write your own. An example +tool is [rpm-ostree](https://github.com/projectatomic/rpm-ostree) - it +takes as input RPMs, and commits them (currently oriented for a server +side, but aiming to do client side too). + +## Initializing + +For this initial discussion, we're assuming you have a single +`archive-z2` repository: + +``` +mkdir repo +ostree --repo=repo init --mode=archive-z2 +``` + +You can export this via a static webserver, and configure clients to +pull from it. + +## Writing your own OSTree buildsystem + +There exist many, many systems that basically follow this pattern: + +``` +$pkg --installroot=/path/to/tmpdir install foo bar baz +$imagesystem commit --root=/path/to/tmpdir +``` + +For various values of `$pkg` such as `yum`, `apt-get`, etc., and +values of `$imagesystem` could be simple tarballs, Amazon Machine +Images, ISOs, etc. + +Now obviously in this document, we're going to talk about the +situation where `$imagesystem` is OSTree. The general idea with +OSTree is that wherever you might store a series of tarballs for +applications or OS images, OSTree is likely going to be better. For +example, it supports GPG signatures, binary deltas, writing bootloader +configuration, etc. + +OSTree does not include a package/component build system simply +because there already exist plenty of good ones - rather, it is +intended to provide an infrastructure layer. + +The above mentioned `rpm-ostree compose tree` chooses RPM as the value +of `$pkg` - so binaries are built as RPMs, then committed as a whole +into an OSTree commit. + +But let's discuss building our own. If you're just experimenting, +it's quite easy to start with the command line. We'll assume for this +purpose that you have a build process that outputs a directory tree - +we'll call this tool `$pkginstallroot` (which could be `yum +--installroot` or `dbootstrap`, etc.). + +Your initial prototype is going to look like: + +``` +$pkginstallroot /path/to/tmpdir +ostree --repo=repo commit -s 'build' -b exampleos/x86_64/standard --tree=dir=/path/to/tmpdir +``` + +Alternatively, if your build system can generate a tarball, you can +commit that tarball into OSTree. For example, +[OpenEmbedded](http://www.openembedded.org/) can output a tarball, and +one can commit it via: + +``` +ostree commit -s 'build' -b exampleos/x86_64/standard --tree=tar=myos.tar +``` + +## Constructing trees from unions + +The above is a very simplistic model, and you will quickly notice that +it's slow. This is because OSTree has to re-checksum and recompress +the content each time it's committed. (Most of the CPU time is spent +in compression which gets thrown away if the content turns out to be +already stored). + +A more advanced approach is to store components in OSTree itself, then +union them, and recommit them. At this point, we recommend taking a +look at the OSTree API, and choose a programming language supported by +[GObject Introspection](https://wiki.gnome.org/Projects/GObjectIntrospection) +to write your buildsystem scripts. Python may be a good choice, or +you could choose custom C code, etc. + +For the purposes of this tutorial we will use shell script, but it's +strongly recommended to choose a real programming language for your +build system. + +Let's say that your build system produces separate artifacts (whether +those are RPMs, zip files, or whatever). These artifacts should be +the result of `make install DESTDIR=` or similar. Basically +equivalent to RPMs/debs. + +Further, in order to make things fast, we will need a separate +`bare-user` repository in order to perform checkouts quickly via +hardlinks. We'll then export content into the `archive-z2` repository +for use by client systems. + +``` +mkdir build-repo +ostree --repo=build-repo init --mode=bare-user +``` + +You can begin committing those as individual branches: + +``` +ostree --repo=build-repo commit -b exampleos/x86_64/bash --tree=tar=bash-4.2-bin.tar.gz +ostree --repo=build-repo commit -b exampleos/x86_64/systemd --tree=tar=systemd-224-bin.tar.gz +``` + +Set things up so that whenever a package changes, you redo the +`commit` with the new package version - conceptually, the branch +tracks the individual package versions over time, and defaults to +"latest". This isn't required - one could also include the version in +the branch name, and have metadata outside to determine "latest" (or +the desired version). + +Now, to construct our final tree: + +``` +rm exampleos-build -rf +for package in bash systemd; do + ostree --repo=build-repo checkout -U --union exampleos/x86_64/${package} exampleos-build +done +# Set up a "rofiles-fuse" mount point; this ensures that any processes +# we run for post-processing of the tree don't corrupt the hardlinks. +mkdir -p mnt +rofiles-fuse exampleos-build mnt +# Now run global "triggers", generate cache files: +ldconfig -r mnt + (Insert other programs here) +fusermount -u mnt +ostree --repo=build-repo commit -b exampleos/x86_64/standard --link-checkout-speedup exampleos-build +``` + +There are a number of interesting things going on here. The major +architectural change is that we're using `--link-checkout-speedup`. +This is a way to tell OSTree that our checkout is made via hardlinks, +and to scan the repository in order to build up a reverse `(device, +inode) -> checksum` mapping. + +In order for this mapping to be accurate, we needed the `rofiles-fuse` +to ensure that any changed files had new inodes (and hence a new +checksum). + +## Migrating content between repositories + +Now that we have content in our `build-repo` repository (in +`bare-user` mode), we need to move the `exampleos/x86_64/standard` +branch content into the repository just named `repo` (in `archive-z2` +mode) for export, which will involve zlib compression of new objects. +We likely want to generate static deltas after that as well. + +Let's copy the content: + +``` +ostree --repo=repo pull-local build-repo exampleos/x86_64/standard +``` + +Clients can now incrementally download new objects - however, this +would also be a good time to generate a delta from the previous +commit. + +``` +ostree --repo=repo static-delta generate exampleos/x86_64/standard +``` diff --git a/docs/manual/deployment.md b/docs/manual/deployment.md index 53b0b662..f1172959 100644 --- a/docs/manual/deployment.md +++ b/docs/manual/deployment.md @@ -13,7 +13,7 @@ OSTree is designed to boot directly into exactly one deployment at a time; each deployment is intended to be a target for `chroot()` or equivalent. -### "osname": Group of deployments that share /var +### "osname": Group of deployments that share /var Each deployment is grouped in exactly one "osname". From above, you can see that an osname is physically represented in the diff --git a/docs/manual/formats.md b/docs/manual/formats.md index bf7fd0ae..e689f8a8 100644 --- a/docs/manual/formats.md +++ b/docs/manual/formats.md @@ -67,7 +67,7 @@ or an unprivileged container. ## Static deltas -OSTree itself was originally focused on a continous delivery model, where +OSTree itself was originally focused on a continuous delivery model, where client systems are expected to update regularly. However, many OS vendors would like to supply content that's updated e.g. once a month or less often. @@ -82,7 +82,7 @@ object. Static deltas also support `from NULL`, where the client can more efficiently download a commit object from scratch. Effectively, we're spending server-side storage (and one-time compute -cost), and gaining efficiency in client network bandwith. +cost), and gaining efficiency in client network bandwidth. ## Static delta repository layout @@ -99,7 +99,7 @@ management easier for filesystem tools A delta is named `$(mbase64 $from)-$(mbase64 $to)`, for example `GpTyZaVut2jXFPWnO4LJiKEdRTvOw_mFUCtIKW1NIX0-L8f+VVDkEBKNc1Ncd+mDUrSVR4EyybQGCkuKtkDnTwk`, -which in sha256 format is +which in SHA256 format is `1a94f265a56eb768d714f5a73b82c988a11d453bcec3f985502b48296d4d217d-2fc7fe5550e410128d73535c77e98352b495478132c9b4060a4b8ab640e74f09`. Finally, the actual content can be found in @@ -136,7 +136,7 @@ The superblock contains: - delta generation timestamp - the new commit object - An array of recursive deltas to apply - - An array of per-part metadata, including total object sizes (compressed and uncompressed), + - An array of per-part metadata, including total object sizes (compressed and uncompressed), - An array of fallback objects Let's define a delta part, then return to discuss details: @@ -160,14 +160,14 @@ a per-file delta algorithm called [bsdiff](https://github.com/mendsley/bsdiff) that most notably works well on executable code. -The current delta compiler scans for files with maching basenamesin +The current delta compiler scans for files with matching basenames in each commit that have a similar size, and attempts a bsdiff between them. (It would make sense later to have a build system provide a hint for this - for example, files within a same package). A generated bsdiff is included in the payload blob, and applying it is an instruction. - + ## Fallback objects It's possible for there to be large-ish files which might be resistant diff --git a/docs/manual/introduction.md b/docs/manual/introduction.md index b0d47390..c88d6c14 100644 --- a/docs/manual/introduction.md +++ b/docs/manual/introduction.md @@ -1,6 +1,6 @@ # OSTree Overview -## Introduction +## Introduction OSTree an upgrade system for Linux-based operating systems that performs atomic upgrades of complete filesystem trees. It is @@ -15,7 +15,7 @@ content-addressed object store, and layered on top of that is bootloader configuration, management of `/etc`, and other functions to perform an upgrade beyond just replicating files. - + You can use OSTree standalone in the pure replication model, but another approach is to add a package manager on top, thus creating a hybrid tree/package system. @@ -46,7 +46,7 @@ HTTP, and where the OS includes (if desired) an entirely separate mechanism to install applications, stored in `/var` if they're system global, or `/home` for per-user application installation. An example application mechanism is -http://docker.io/ + However, it is entirely possible to use OSTree underneath a package system, where the contents of `/usr` are computed on the client. @@ -79,6 +79,13 @@ filesystem that supports hard links. Note: OSTree will transparently take advantage of some BTRFS features if deployed on it. +OSTree is orthogonal to virtualization mechanisms like AMIs and qcow2 +images, though it's most useful though if you plan to update stateful +VMs in-place, rather than generating new images. + +In practice, users of "bare metal" configurations will find the OSTree +model most useful. + ## Atomic transitions between parallel-installable read-only filesystem trees Another deeply fundamental difference between both package diff --git a/docs/manual/related-projects.md b/docs/manual/related-projects.md new file mode 100644 index 00000000..48bc3f1c --- /dev/null +++ b/docs/manual/related-projects.md @@ -0,0 +1,207 @@ +# Related Projects + +OSTree is in many ways very evolutionary. It builds on concepts and +ideas introduced from many different projects such as +[Systemd Stateless](http://0pointer.net/blog/projects/stateless.html), +[Systemd Bootloader Spec](https://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/), +[Chromium Autoupdate](http://dev.chromium.org/chromium-os/chromiumos-design-docs/filesystem-autoupdate), +the much older +[Fedora/Red Hat Stateless Project](https://fedoraproject.org/wiki/StatelessLinux), +[Linux VServer](http://linux-vserver.org/index.php?title=util-vserver:Vhashify&oldid=2285) +and many more. + +As mentioned elsewhere, OSTree is strongly influenced by package +manager designs as well. This page is not intended to be an +exhaustive list of such projects, but we will try to keep it up to +date, and relatively agnostic. + +Broadly speaking, projects in this area fall into two camps; either +a tool to snapshot systems on the client side (dpkg/rpm + BTRFS/LVM), +or a tool to compose on a server and replicate (ChromiumOS, Clear +Linux). OSTree is flexible enough to do both. + +## Combining dpkg/rpm + (BTRFS/LVM) + +In this approach, one uses a block/filesystem snapshot tool underneath +the system package manager. + +The +[oVirt Node imgbased](https://gerrit.ovirt.org/gitweb?p=imgbased.git) +tool is an example of this approach, as are a few others below. + +Regarding [BTRFS](https://btrfs.wiki.kernel.org/index.php/Main_Page) +in particular - the OSTree author believes that Linux storage is a +wide world, and while BTRFS is quite good, it is not everywhere now, +nor will it be in the near future. There are other recently developed +filesystems like [f2fs](https://en.wikipedia.org/wiki/F2FS), and Red +Hat Enterprise Linux still defaults to +[XFS](https://en.wikipedia.org/wiki/XFS). + +Using a snapshot tool underneath a package manager does help +significantly. In the rest of this text, we will use "BTRFS" as a +mostly generic tool for filesystem snapshots. + +The obvious thing to do is layer BTRFS under dpkg/rpm, and have a +separate subvolume for `/home` so rollbacks don't lose your data. See +e.g. [Fedora BTRFS Rollback Feature](http://fedoraproject.org/wiki/Features/SystemRollbackWithBtrfs). + +More generally, if you want to use BTRFS to roll back changes made by +dpkg/rpm, you have to carefully set up the partition layout so that +the files laid out by dpkg/rpm are installed in a subvolume to +snapshot. + +This problem in many ways is addressed by the changes OSTree forces, +such as putting all local state in `/var` (e.g. `/usr/local` -> +`/var/usrlocal`). Then one can BTRFS snapshot `/usr`. This gets pretty +far, except handling `/etc` is messy. This is something OSTree does +well. + +In general, if one really tries to flesh out the BTRFS approach, a +nontrivial middle layer of code between dpkg/rpm and BTRFS (or deep +awareness of BTRFS in dpkg/rpm itself) will be required. + +The OSTree author believes that having total freedom at the block +storage layer is better for general purpose operating systems. For +example, with OSTree, one is free to use BTRFS in any way you like - +you can use a subvolume for `/home`, or you can not. + +Furthermore, in its most basic incarnation, the rpm/dpkg + BTRFS +doesn't solve the race conditions that happen when unpacking packages +into the live system, such as deleting the files underneath Firefox +while it's running. One could unpack packages into a separate root, +and switch to that, which gets closer to the OSTree architecture. + +Note though OSTree does take advantage of BTRFS if installed on top of +it! In particular, it will use reflink for the copies of `/etc` if +available. + +All of the above also applies if one replaces "BTRFS" with "LVM +snapshots" except for the reflinks. + +Finally, see the next portion around ChromiumOS for why a hybrid but +integrated package/image system improves on this. + +## ChromiumOS updater + +Many people who look at OSTree are most interested in using +it as an updater for embedded or fixed-purpose systems, similar to use cases +from the [ChromiumOS updater](http://dev.chromium.org/chromium-os/chromiumos-design-docs/filesystem-autoupdate). + +The ChromiumOS approach uses two partitions that are swapped via the +bootloader. It has a very network-efficient update protocol, using a +custom binary delta scheme between filesystem snapshots. + +This model even allows for switching filesystem types in an update. + +A major downside of this approach is that the OS size is doubled on +disk always. In contrast, OSTree uses plain Unix hardlinks, which +means it essentially only requires disk space proportional to the +changed files, plus some small fixed overhead. + +This means with OSTree, one can easily have more than two trees +(deployments). Another example is that the system OSTree repository +could *also* be used for application containers. + +Finally, the author of OSTree believes that what one really wants for +many cases is image replication *with* the ability to layer on some +additional components (e.g. packages) - a hybrid model. This is what +[rpm-ostree](https://github.com/projectatomic/rpm-ostree/) is aiming +to support. + +## Ubuntu Image Based Updates + +See . Very architecturally +similar to ChromeOS, although more interesting is discussion for +supporting package installation on top, similar to +[rpm-ostree package layering](https://github.com/projectatomic/rpm-ostree/pull/107). + +## Clear Linux Software update + +The +[Clear Linux Software update](https://clearlinux.org/features/software-update) +system is not very well documented. +[This mailing list post](https://lists.clearlinux.org/pipermail/dev/2016-January/000159.html) +has some reverse-engineered design documentation. + +Like OSTree static deltas, it also uses bsdiff for network efficiency. + +More information will be filled in here over time. The OSTree author +believes that at the moment, the "CL updater" is not truly atomic in +the sense that because it applies updates live, there is a window +where the OS root may be inconsistent. + +## OLPC update + +OSTree is basically a generalization of olpc-update, except using +plain HTTP instead of rsync. OSTree has the notion of separate trees +that one can track independently or parallel install, while still +sharing storage via the hardlinked repository, whereas olpc-update +uses version numbers for a single OS. + +OSTree has built-in plain old HTTP replication which can be served +from a static webserver, whereas olpc-update uses `rsync` (more server +load, but more efficient on the network side). The OSTree solution to +improving network bandwidth consumption is via static deltas. + +See +[this comment](http://blog.verbum.org/2013/08/26/ostree-v2013-6-released/#comment-1169) +for a comparison. + +## NixOS + +See [NixOS](http://nixos.org/). It was a very influential project for +OSTree. NixOS and OSTree both support the idea of independent "roots" +that are bootable. + +In NixOS, the entire system is based on checksums of package inputs +(build dependencies) - see [Nix store](http://nixos.org/nix/manual/#chap-package-management/). A both +positive and negative of the Nix model is that a change in the build +dependencies (e.g. being built with a newer gcc), requires a cascading +rebuild of everything. + +In OSTree, the checksums are of object *content* (including extended +attributes). This means that any data that's identical is +transparently, automatically shared on disk. It's possible to ask the +Nix store to deduplicate, (via hard links and immutable bit), but this +is significantly less efficient than the OSTree approach. The Nix use +of the ext immutable bit is racy, since it has to be briefly removed +to make a hard link. + +At the lowest level, OSTree is just "git for binaries" - it isn't tied +strongly to any particular build system. You can put whatever data you +want inside an OSTree repository, built however you like. So for +example, while one could make a build system that did the "purely +functional" approach of Nix, it also works to have a build system that +just rebuilds individual components (packages) as they change, without +forcing a rebuild of their dependencies. + +The author of OSTree believes that while Nix has some good ideas, +forcing a rebuild of everything for a security update to e.g. glibc is +not practical at scale. + +## Solaris IPS + +See +[Solaris IPS](http://hub.opensolaris.org/bin/view/Project+pkg/). Broadly, +this is a similar design as to a combination of BTRFS+RPM/deb. There +is a bootloader management system which combines with the snapshots. +It's relatively well thought through - however, it is a client-side +system assembly. If one wants to image servers and replicate +reliably, that'd be a different system. + + +## Conary + +See +[Conary Updates and Rollbacks](http://wiki.rpath.com/wiki/Conary:Updates_and_Rollbacks). If +rpm/dpkg are like CVS, Conary is closer to Subversion. It's not bad, +but e.g. its rollback model is rather ad-hoc and not atomic. It also +is a fully client side system and doesn't have an image-like +replication with deltas. + +## bmap + +See +[bmap](https://source.tizen.org/documentation/reference/bmaptool/introduction). +A tool for optimized copying of disk images. Intended for offline use, +so not directly comparable. diff --git a/docs/manual/repo.md b/docs/manual/repo.md index 3b7a737f..e8d94b4b 100644 --- a/docs/manual/repo.md +++ b/docs/manual/repo.md @@ -54,12 +54,12 @@ modes: `bare`, `bare-user`, and `archive-z2`. A bare repository is one where content files are just stored as regular files; it's designed to be the source of a "hardlink farm", where each operating system checkout is merely links into it. If you want to store files -owned by e.g. root in this mode, you must run OSTree as root. +owned by e.g. root in this mode, you must run OSTree as root. The `bare-user` is a later addition that is like `bare` in that files are unpacked, but it can (and should generally) be created as non-root. In this mode, extended metadata such as owner uid, gid, and -extended attributes are stored but not actually applied. +extended attributes are stored but not actually applied. The `bare-user` mode is useful for build systems that run as non-root but want to generate root-owned content, as well as non-root container systems. @@ -74,9 +74,13 @@ command, it will operate on the system repository. ## Refs -Like git, OSTree uses "refs" to which are text files that point to -particular commits (i.e. filesystem trees). For example, the -gnome-ostree operating system creates trees named like +Like git, OSTree uses the terminology "references" (abbreviated +"refs") which are text files that name (refer to) to particular +commits. See the +[Git Documentation](https://git-scm.com/book/en/v2/Git-Internals-Git-References) +for information on how git uses them. Unlike git though, it doesn't +usually make sense to have a "master" branch. There is a convention +for references in OSTree that looks like this: `exampleos/buildmaster/x86_64-runtime` and `exampleos/buildmaster/x86_64-devel-debug`. These two refs point to two different generated filesystem trees. In this example, the @@ -88,3 +92,30 @@ the parent of a given commit. For example, `exampleos/buildmaster/x86_64-runtime^` refers to the previous build, and `exampleos/buildmaster/x86_64-runtime^^` refers to the one before that. + +## The summary file + +A later addition to OSTree is the concept of a "summary" file, created +via the `ostree summary -u` command. This was introduced for a few +reasons. A primary use case is to be a target a +(Metalink)[https://en.wikipedia.org/wiki/Metalink], which requires a +single file with a known checksum as a target. + +The summary file primarily contains two mappings: + + - A mapping of the refs and their checksums, equivalent to fetching + the ref file individually + - A list of all static deltas, along with their metadata checksums + +This currently means that it grows linearly with both items. On the +other hand, using the summary file, a client can enumerate branches. + +Further, the summary file is fetched over e.g. pinned TLS, this +creates a strong end-to-end verification of the commit or static delta. + +The summary file can also be GPG signed (detached), and currently this +is the only way provide GPG signatures (transitively) on deltas. + +If a repository administrator creates a summary file, they must +thereafter run `ostree summary -u` to update it whenever a commit is +made or a static delta is generated. diff --git a/man/ostree-admin-unlock.xml b/man/ostree-admin-unlock.xml new file mode 100644 index 00000000..ca02bbde --- /dev/null +++ b/man/ostree-admin-unlock.xml @@ -0,0 +1,88 @@ + + + + + + + + + ostree admin unlock + OSTree + + + + Developer + Colin + Walters + walters@verbum.org + + + + + + ostree admin unlock + 1 + + + + ostree-admin-unlock + Prepare the current deployment for hotfix or development + + + + + ostree admin unlock OPTIONS + + + + + Description + + + Remove the read-only bind mount on /usr + and replace it with a writable overlay filesystem. This + default invocation of "unlock" is intended for + development/testing purposes. All changes in the overlay + are lost on reboot. However, this command also supports + "hotfixes", see below. + + + + + Options + + + + + + If this option is provided, the + current deployment will be cloned as a rollback + target. This option is intended for things like + emergency security updates to userspace components + such as sshd. The semantics here + differ from the default "development" unlock mode + in that reboots will retain any changes (which is what + you likely want for security hotfixes). + + + + + diff --git a/man/ostree-refs.xml b/man/ostree-refs.xml index e97cb1c5..43e934ff 100644 --- a/man/ostree-refs.xml +++ b/man/ostree-refs.xml @@ -57,7 +57,7 @@ Boston, MA 02111-1307, USA. Description - Lists all refs available on the host. If pecified, PREFIX assigns the refspec prefix; default prefix is null, which lists all refs. + Lists all refs available on the host. If specified, PREFIX assigns the refspec prefix; default prefix is null, which lists all refs. @@ -65,6 +65,16 @@ Boston, MA 02111-1307, USA. Options + + + + For historical reasons, refs + without this option will strip the specified prefix + from the output. Normally, one wants to see the full + ref, so providing this option ensures the refs are + printed in full, rather than + truncated. + diff --git a/manual-tests/static-delta-generate-crosscheck.sh b/manual-tests/static-delta-generate-crosscheck.sh index 4dbaab98..fbcbe677 100755 --- a/manual-tests/static-delta-generate-crosscheck.sh +++ b/manual-tests/static-delta-generate-crosscheck.sh @@ -59,4 +59,3 @@ set -x validate_delta_options validate_delta_options --inline validate_delta_options --disable-bsdiff - diff --git a/mkdocs.yml b/mkdocs.yml index 89211c79..e512ea62 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,4 +1,4 @@ -site_name: My Docs +site_name: OSTree pages: - Home: 'index.md' - Contributing: 'CONTRIBUTING.md' @@ -9,3 +9,5 @@ pages: - Atomic Upgrades: 'manual/atomic-upgrades.md' - Adapting Existing Systems: 'manual/adapting-existing.md' - Formats: 'manual/formats.md' + - Build Systems and Repos: 'manual/buildsystem-and-repos.md' + - Related Projects: 'manual/related-projects.md' diff --git a/src/libostree/libostree.sym b/src/libostree/libostree.sym new file mode 100644 index 00000000..7f10b75d --- /dev/null +++ b/src/libostree/libostree.sym @@ -0,0 +1,335 @@ +/*** + Copyright (C) 2016 Colin Walters + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +***/ + + +/* + Retroactively make all of these symbols 2016.3, which is + the first release where we started using versioned symbols. See + also https://www.berrange.com/posts/2011/01/13/versioning-in-the-libvirt-library/ + */ +LIBOSTREE_2016.3 { +global: + ostree_async_progress_finish; + ostree_async_progress_get_status; + ostree_async_progress_get_type; + ostree_async_progress_get_uint; + ostree_async_progress_get_uint64; + ostree_async_progress_new; + ostree_async_progress_new_and_connect; + ostree_async_progress_set_status; + ostree_async_progress_set_uint; + ostree_async_progress_set_uint64; + ostree_bootconfig_parser_clone; + ostree_bootconfig_parser_get; + ostree_bootconfig_parser_get_type; + ostree_bootconfig_parser_new; + ostree_bootconfig_parser_parse; + ostree_bootconfig_parser_parse_at; + ostree_bootconfig_parser_set; + ostree_bootconfig_parser_write; + ostree_bootconfig_parser_write_at; + ostree_chain_input_stream_get_type; + ostree_chain_input_stream_new; + ostree_checksum_b64_inplace_from_bytes; + ostree_checksum_b64_inplace_to_bytes; + ostree_checksum_bytes_peek; + ostree_checksum_bytes_peek_validate; + ostree_checksum_file; + ostree_checksum_file_async; + ostree_checksum_file_async_finish; + ostree_checksum_file_from_input; + ostree_checksum_from_bytes; + ostree_checksum_from_bytes_v; + ostree_checksum_inplace_from_bytes; + ostree_checksum_inplace_to_bytes; + ostree_checksum_input_stream_get_type; + ostree_checksum_input_stream_new; + ostree_checksum_to_bytes; + ostree_checksum_to_bytes_v; + ostree_cmd__private__; + ostree_cmp_checksum_bytes; + ostree_commit_get_parent; + ostree_commit_get_timestamp; + ostree_content_file_parse; + ostree_content_file_parse_at; + ostree_content_stream_parse; + ostree_create_directory_metadata; + ostree_deployment_clone; + ostree_deployment_equal; + ostree_deployment_get_bootconfig; + ostree_deployment_get_bootcsum; + ostree_deployment_get_bootserial; + ostree_deployment_get_csum; + ostree_deployment_get_deployserial; + ostree_deployment_get_index; + ostree_deployment_get_origin; + ostree_deployment_get_origin_relpath; + ostree_deployment_get_osname; + ostree_deployment_get_type; + ostree_deployment_hash; + ostree_deployment_new; + ostree_deployment_set_bootconfig; + ostree_deployment_set_bootserial; + ostree_deployment_set_index; + ostree_deployment_set_origin; + ostree_diff_dirs; + ostree_diff_item_get_type; + ostree_diff_item_ref; + ostree_diff_item_unref; + ostree_diff_print; + ostree_fetcher_config_flags_get_type; + ostree_gpg_verify_result_count_all; + ostree_gpg_verify_result_count_valid; + ostree_gpg_verify_result_describe; + ostree_gpg_verify_result_describe_variant; + ostree_gpg_verify_result_get; + ostree_gpg_verify_result_get_all; + ostree_gpg_verify_result_get_type; + ostree_gpg_verify_result_lookup; + ostree_hash_object_name; + ostree_metadata_variant_type; + ostree_mutable_tree_ensure_dir; + ostree_mutable_tree_ensure_parent_dirs; + ostree_mutable_tree_get_contents_checksum; + ostree_mutable_tree_get_files; + ostree_mutable_tree_get_metadata_checksum; + ostree_mutable_tree_get_subdirs; + ostree_mutable_tree_get_type; + ostree_mutable_tree_lookup; + ostree_mutable_tree_new; + ostree_mutable_tree_replace_file; + ostree_mutable_tree_set_contents_checksum; + ostree_mutable_tree_set_metadata_checksum; + ostree_mutable_tree_walk; + ostree_object_from_string; + ostree_object_name_deserialize; + ostree_object_name_serialize; + ostree_object_to_string; + ostree_object_type_from_string; + ostree_object_type_to_string; + ostree_parse_refspec; + ostree_raw_file_to_content_stream; + ostree_repo_abort_transaction; + ostree_repo_add_gpg_signature_summary; + ostree_repo_append_gpg_signature; + ostree_repo_checkout_gc; + ostree_repo_checkout_tree; + ostree_repo_checkout_tree_at; + ostree_repo_commit_modifier_get_type; + ostree_repo_commit_modifier_new; + ostree_repo_commit_modifier_ref; + ostree_repo_commit_modifier_set_devino_cache; + ostree_repo_commit_modifier_set_sepolicy; + ostree_repo_commit_modifier_set_xattr_callback; + ostree_repo_commit_modifier_unref; + ostree_repo_commit_transaction; + ostree_repo_commit_traverse_iter_cleanup; + ostree_repo_commit_traverse_iter_clear; + ostree_repo_commit_traverse_iter_get_dir; + ostree_repo_commit_traverse_iter_get_file; + ostree_repo_commit_traverse_iter_init_commit; + ostree_repo_commit_traverse_iter_init_dirtree; + ostree_repo_commit_traverse_iter_next; + ostree_repo_copy_config; + ostree_repo_create; + ostree_repo_delete_object; + ostree_repo_devino_cache_get_type; + ostree_repo_devino_cache_new; + ostree_repo_devino_cache_ref; + ostree_repo_devino_cache_unref; + ostree_repo_export_tree_to_archive; + ostree_repo_file_ensure_resolved; + ostree_repo_file_get_checksum; + ostree_repo_file_get_repo; + ostree_repo_file_get_root; + ostree_repo_file_get_type; + ostree_repo_file_get_xattrs; + ostree_repo_file_tree_find_child; + ostree_repo_file_tree_get_contents; + ostree_repo_file_tree_get_contents_checksum; + ostree_repo_file_tree_get_metadata; + ostree_repo_file_tree_get_metadata_checksum; + ostree_repo_file_tree_query_child; + ostree_repo_file_tree_set_metadata; + ostree_repo_get_config; + ostree_repo_get_disable_fsync; + ostree_repo_get_mode; + ostree_repo_get_parent; + ostree_repo_get_path; + ostree_repo_get_type; + ostree_repo_has_object; + ostree_repo_import_archive_to_mtree; + ostree_repo_import_object_from; + ostree_repo_is_system; + ostree_repo_is_writable; + ostree_repo_list_commit_objects_starting_with; + ostree_repo_list_objects; + ostree_repo_list_refs; + ostree_repo_list_static_delta_names; + ostree_repo_load_commit; + ostree_repo_load_file; + ostree_repo_load_object_stream; + ostree_repo_load_variant; + ostree_repo_load_variant_if_exists; + ostree_repo_mode_from_string; + ostree_repo_new; + ostree_repo_new_default; + ostree_repo_new_for_sysroot_path; + ostree_repo_open; + ostree_repo_prepare_transaction; + ostree_repo_prune; + ostree_repo_prune_static_deltas; + ostree_repo_pull; + ostree_repo_pull_default_console_progress_changed; + ostree_repo_pull_one_dir; + ostree_repo_pull_with_options; + ostree_repo_query_object_storage_size; + ostree_repo_read_commit; + ostree_repo_read_commit_detached_metadata; + ostree_repo_regenerate_summary; + ostree_repo_remote_add; + ostree_repo_remote_change; + ostree_repo_remote_delete; + ostree_repo_remote_fetch_summary; + ostree_repo_remote_get_gpg_verify; + ostree_repo_remote_get_gpg_verify_summary; + ostree_repo_remote_get_url; + ostree_repo_remote_gpg_import; + ostree_repo_remote_list; + ostree_repo_remote_list_refs; + ostree_repo_resolve_rev; + ostree_repo_scan_hardlinks; + ostree_repo_set_disable_fsync; + ostree_repo_set_ref_immediate; + ostree_repo_sign_commit; + ostree_repo_sign_delta; + ostree_repo_static_delta_execute_offline; + ostree_repo_static_delta_generate; + ostree_repo_transaction_set_ref; + ostree_repo_transaction_set_refspec; + ostree_repo_transaction_stats_get_type; + ostree_repo_traverse_commit; + ostree_repo_traverse_commit_union; + ostree_repo_traverse_new_reachable; + ostree_repo_verify_commit; + ostree_repo_verify_commit_ext; + ostree_repo_verify_summary; + ostree_repo_write_archive_to_mtree; + ostree_repo_write_commit; + ostree_repo_write_commit_detached_metadata; + ostree_repo_write_commit_with_time; + ostree_repo_write_config; + ostree_repo_write_content; + ostree_repo_write_content_async; + ostree_repo_write_content_finish; + ostree_repo_write_content_trusted; + ostree_repo_write_dfd_to_mtree; + ostree_repo_write_directory_to_mtree; + ostree_repo_write_metadata; + ostree_repo_write_metadata_async; + ostree_repo_write_metadata_finish; + ostree_repo_write_metadata_stream_trusted; + ostree_repo_write_metadata_trusted; + ostree_repo_write_mtree; + ostree_sepolicy_fscreatecon_cleanup; + ostree_sepolicy_get_label; + ostree_sepolicy_get_name; + ostree_sepolicy_get_path; + ostree_sepolicy_get_type; + ostree_sepolicy_new; + ostree_sepolicy_restorecon; + ostree_sepolicy_setfscreatecon; + ostree_sysroot_cleanup; + ostree_sysroot_deployment_set_kargs; + ostree_sysroot_deployment_set_mutable; + ostree_sysroot_deploy_tree; + ostree_sysroot_ensure_initialized; + ostree_sysroot_get_booted_deployment; + ostree_sysroot_get_bootversion; + ostree_sysroot_get_deployment_directory; + ostree_sysroot_get_deployment_dirpath; + ostree_sysroot_get_deployment_origin_path; + ostree_sysroot_get_deployments; + ostree_sysroot_get_fd; + ostree_sysroot_get_merge_deployment; + ostree_sysroot_get_path; + ostree_sysroot_get_repo; + ostree_sysroot_get_subbootversion; + ostree_sysroot_get_type; + ostree_sysroot_load; + ostree_sysroot_lock; + ostree_sysroot_lock_async; + ostree_sysroot_lock_finish; + ostree_sysroot_new; + ostree_sysroot_new_default; + ostree_sysroot_origin_new_from_refspec; + ostree_sysroot_prepare_cleanup; + ostree_sysroot_simple_write_deployment; + ostree_sysroot_try_lock; + ostree_sysroot_unload; + ostree_sysroot_unlock; + ostree_sysroot_upgrader_check_timestamps; + ostree_sysroot_upgrader_deploy; + ostree_sysroot_upgrader_dup_origin; + ostree_sysroot_upgrader_flags_get_type; + ostree_sysroot_upgrader_get_origin; + ostree_sysroot_upgrader_get_origin_description; + ostree_sysroot_upgrader_get_type; + ostree_sysroot_upgrader_new; + ostree_sysroot_upgrader_new_for_os; + ostree_sysroot_upgrader_new_for_os_with_flags; + ostree_sysroot_upgrader_pull; + ostree_sysroot_upgrader_pull_one_dir; + ostree_sysroot_upgrader_set_origin; + ostree_sysroot_write_deployments; + ostree_sysroot_write_origin_file; + ostree_validate_checksum_string; + ostree_validate_rev; + ostree_validate_structureof_checksum_string; + ostree_validate_structureof_commit; + ostree_validate_structureof_csum_v; + ostree_validate_structureof_dirmeta; + ostree_validate_structureof_dirtree; + ostree_validate_structureof_file_mode; + ostree_validate_structureof_objtype; +local: + *; +}; + +LIBOSTREE_2016.4 { +global: + ostree_repo_get_dfd; + ostree_repo_list_refs_ext; + ostree_sysroot_init_osname; + ostree_sysroot_load_if_changed; + ostree_sysroot_deployment_unlock; + ostree_deployment_get_unlocked; + ostree_deployment_unlocked_state_to_string; +} LIBOSTREE_2016.3; + +/* NOTE NOTE NOTE + * Versions above here are released. Only add symbols below this line. + * NOTE NOTE NOTE + */ + +/* UNCOMMENT WITH NEW SYMBOLS HERE: +LIBOSTREE_2016.5 { +global: +} LIBOSTREE_2016.4; +*/ diff --git a/src/libostree/ostree-async-progress.h b/src/libostree/ostree-async-progress.h index 81ed5ef6..ae0e5faa 100644 --- a/src/libostree/ostree-async-progress.h +++ b/src/libostree/ostree-async-progress.h @@ -41,29 +41,39 @@ struct OstreeAsyncProgressClass void (*changed) (OstreeAsyncProgress *self, gpointer user_data); }; +_OSTREE_PUBLIC GType ostree_async_progress_get_type (void) G_GNUC_CONST; +_OSTREE_PUBLIC OstreeAsyncProgress *ostree_async_progress_new (void); +_OSTREE_PUBLIC OstreeAsyncProgress *ostree_async_progress_new_and_connect (void (*changed) (OstreeAsyncProgress *self, gpointer user_data), gpointer user_data); +_OSTREE_PUBLIC char *ostree_async_progress_get_status (OstreeAsyncProgress *self); +_OSTREE_PUBLIC guint ostree_async_progress_get_uint (OstreeAsyncProgress *self, const char *key); +_OSTREE_PUBLIC guint64 ostree_async_progress_get_uint64 (OstreeAsyncProgress *self, const char *key); +_OSTREE_PUBLIC void ostree_async_progress_set_status (OstreeAsyncProgress *self, const char *status); +_OSTREE_PUBLIC void ostree_async_progress_set_uint (OstreeAsyncProgress *self, const char *key, guint value); +_OSTREE_PUBLIC void ostree_async_progress_set_uint64 (OstreeAsyncProgress *self, const char *key, guint64 value); +_OSTREE_PUBLIC void ostree_async_progress_finish (OstreeAsyncProgress *self); G_END_DECLS diff --git a/src/libostree/ostree-bootconfig-parser.h b/src/libostree/ostree-bootconfig-parser.h index 47dac9c9..cd524601 100644 --- a/src/libostree/ostree-bootconfig-parser.h +++ b/src/libostree/ostree-bootconfig-parser.h @@ -30,38 +30,47 @@ G_BEGIN_DECLS typedef struct _OstreeBootconfigParser OstreeBootconfigParser; +_OSTREE_PUBLIC GType ostree_bootconfig_parser_get_type (void) G_GNUC_CONST; +_OSTREE_PUBLIC OstreeBootconfigParser * ostree_bootconfig_parser_new (void); +_OSTREE_PUBLIC OstreeBootconfigParser * ostree_bootconfig_parser_clone (OstreeBootconfigParser *self); +_OSTREE_PUBLIC gboolean ostree_bootconfig_parser_parse (OstreeBootconfigParser *self, GFile *path, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_bootconfig_parser_parse_at (OstreeBootconfigParser *self, int dfd, const char *path, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_bootconfig_parser_write (OstreeBootconfigParser *self, GFile *output, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_bootconfig_parser_write_at (OstreeBootconfigParser *self, int dfd, const char *path, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC void ostree_bootconfig_parser_set (OstreeBootconfigParser *self, const char *key, const char *value); +_OSTREE_PUBLIC const char *ostree_bootconfig_parser_get (OstreeBootconfigParser *self, const char *key); diff --git a/src/libostree/ostree-bootloader-grub2.c b/src/libostree/ostree-bootloader-grub2.c index 42ed5acc..11316e98 100644 --- a/src/libostree/ostree-bootloader-grub2.c +++ b/src/libostree/ostree-bootloader-grub2.c @@ -293,9 +293,9 @@ _ostree_bootloader_grub2_write_config (OstreeBootloader *bootloader, g_autoptr(GFile) efi_new_config_temp = NULL; g_autoptr(GFile) efi_orig_config = NULL; g_autoptr(GFile) new_config_path = NULL; - glnx_unref_object GSSubprocessContext *procctx = NULL; - glnx_unref_object GSSubprocess *proc = NULL; - g_auto(GStrv) child_env = g_get_environ (); + GSubprocessFlags subp_flags = 0; + glnx_unref_object GSubprocessLauncher *launcher = NULL; + glnx_unref_object GSubprocess *proc = NULL; g_autofree char *bootversion_str = g_strdup_printf ("%u", (guint)bootversion); g_autoptr(GFile) config_path_efi_dir = NULL; g_autofree char *grub2_mkconfig_chroot = NULL; @@ -337,41 +337,35 @@ _ostree_bootloader_grub2_write_config (OstreeBootloader *bootloader, bootversion); } - procctx = gs_subprocess_context_newv ("grub2-mkconfig", "-o", - gs_file_get_path_cached (new_config_path), - NULL); - child_env = g_environ_setenv (child_env, "_OSTREE_GRUB2_BOOTVERSION", bootversion_str, TRUE); + if (!g_getenv ("OSTREE_DEBUG_GRUB2")) + subp_flags |= (G_SUBPROCESS_FLAGS_STDOUT_SILENCE | G_SUBPROCESS_FLAGS_STDERR_SILENCE); + + launcher = g_subprocess_launcher_new (subp_flags); + g_subprocess_launcher_setenv (launcher, "_OSTREE_GRUB2_BOOTVERSION", bootversion_str, TRUE); /* We have to pass our state to the child */ if (self->is_efi) - child_env = g_environ_setenv (child_env, "_OSTREE_GRUB2_IS_EFI", "1", TRUE); - gs_subprocess_context_set_environment (procctx, child_env); - gs_subprocess_context_set_stdout_disposition (procctx, GS_SUBPROCESS_STREAM_DISPOSITION_NULL); - if (g_getenv ("OSTREE_DEBUG_GRUB2")) - gs_subprocess_context_set_stderr_disposition (procctx, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT); - else - gs_subprocess_context_set_stderr_disposition (procctx, GS_SUBPROCESS_STREAM_DISPOSITION_NULL); - + g_subprocess_launcher_setenv (launcher, "_OSTREE_GRUB2_IS_EFI", "1", TRUE); + /* We need to chroot() if we're not in /. This assumes our caller has * set up the bind mounts outside. */ if (grub2_mkconfig_chroot != NULL) - { - gs_subprocess_context_set_child_setup (procctx, grub2_child_setup, grub2_mkconfig_chroot); - } + g_subprocess_launcher_set_child_setup (launcher, grub2_child_setup, grub2_mkconfig_chroot, NULL); /* In the current Fedora grub2 package, this script doesn't even try to be atomic; it just does: -cat ${grub_cfg}.new > ${grub_cfg} -rm -f ${grub_cfg}.new + cat ${grub_cfg}.new > ${grub_cfg} + rm -f ${grub_cfg}.new Upstream is fixed though. */ - proc = gs_subprocess_new (procctx, cancellable, error); - if (!proc) - goto out; + proc = g_subprocess_launcher_spawn (launcher, error, + "grub2-mkconfig", "-o", + gs_file_get_path_cached (new_config_path), + NULL); - if (!gs_subprocess_wait_sync_check (proc, cancellable, error)) + if (!g_subprocess_wait_check (proc, cancellable, error)) goto out; /* Now let's fdatasync() for the new file */ diff --git a/src/libostree/ostree-chain-input-stream.h b/src/libostree/ostree-chain-input-stream.h index 7ad074ae..dee38952 100644 --- a/src/libostree/ostree-chain-input-stream.h +++ b/src/libostree/ostree-chain-input-stream.h @@ -59,8 +59,10 @@ struct _OstreeChainInputStreamClass void (*_g_reserved5) (void); }; +_OSTREE_PUBLIC GType ostree_chain_input_stream_get_type (void) G_GNUC_CONST; +_OSTREE_PUBLIC OstreeChainInputStream * ostree_chain_input_stream_new (GPtrArray *streams); G_END_DECLS diff --git a/src/libostree/ostree-checksum-input-stream.h b/src/libostree/ostree-checksum-input-stream.h index 2ec8e0c7..cb5b240c 100644 --- a/src/libostree/ostree-checksum-input-stream.h +++ b/src/libostree/ostree-checksum-input-stream.h @@ -57,8 +57,10 @@ struct _OstreeChecksumInputStreamClass void (*_g_reserved5) (void); }; +_OSTREE_PUBLIC GType ostree_checksum_input_stream_get_type (void) G_GNUC_CONST; +_OSTREE_PUBLIC OstreeChecksumInputStream * ostree_checksum_input_stream_new (GInputStream *stream, GChecksum *checksum); diff --git a/src/libostree/ostree-cmdprivate.h b/src/libostree/ostree-cmdprivate.h index 7746406b..81061568 100644 --- a/src/libostree/ostree-cmdprivate.h +++ b/src/libostree/ostree-cmdprivate.h @@ -29,7 +29,8 @@ typedef struct { gboolean (* ostree_static_delta_dump) (OstreeRepo *repo, const char *delta_id, GCancellable *cancellable, GError **error); } OstreeCmdPrivateVTable; -const OstreeCmdPrivateVTable * +/* Note this not really "public", we just export the symbol, but not the header */ +_OSTREE_PUBLIC const OstreeCmdPrivateVTable * ostree_cmd__private__ (void); G_END_DECLS diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c index 11c61f95..ded5e976 100644 --- a/src/libostree/ostree-core.c +++ b/src/libostree/ostree-core.c @@ -155,7 +155,8 @@ ostree_parse_refspec (const char *refspec, ret = TRUE; - gs_transfer_out_value (out_remote, &remote); + if (out_remote) + *out_remote = g_steal_pointer (&remote); if (out_ref != NULL) *out_ref = g_match_info_fetch (match, 2); out: @@ -766,7 +767,8 @@ ostree_checksum_file (GFile *f, if (objtype == OSTREE_OBJECT_TYPE_FILE) { - if (!gs_file_get_all_xattrs (f, &xattrs, cancellable, error)) + if (!glnx_dfd_name_get_all_xattrs (AT_FDCWD, gs_file_get_path_cached (f), + &xattrs, cancellable, error)) goto out; } @@ -950,7 +952,8 @@ _ostree_make_temporary_symlink_at (int tmp_dirfd, } ret = TRUE; - gs_transfer_out_value (out_name, &tmpname); + if (out_name) + *out_name = g_steal_pointer (&tmpname); out: return ret; } diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h index c5b42a75..29ef7b28 100644 --- a/src/libostree/ostree-core.h +++ b/src/libostree/ostree-core.h @@ -23,6 +23,7 @@ #pragma once #include +#include G_BEGIN_DECLS @@ -164,63 +165,86 @@ typedef enum { OSTREE_REPO_MODE_BARE_USER } OstreeRepoMode; -const GVariantType *ostree_metadata_variant_type (OstreeObjectType objtype); +const _OSTREE_PUBLIC +GVariantType *ostree_metadata_variant_type (OstreeObjectType objtype); +_OSTREE_PUBLIC gboolean ostree_validate_checksum_string (const char *sha256, GError **error); +_OSTREE_PUBLIC guchar *ostree_checksum_to_bytes (const char *checksum); +_OSTREE_PUBLIC GVariant *ostree_checksum_to_bytes_v (const char *checksum); +_OSTREE_PUBLIC void ostree_checksum_b64_inplace_to_bytes (const char *checksum, guint8 *buf); +_OSTREE_PUBLIC char * ostree_checksum_from_bytes (const guchar *csum); +_OSTREE_PUBLIC char * ostree_checksum_from_bytes_v (GVariant *csum_v); +_OSTREE_PUBLIC void ostree_checksum_inplace_from_bytes (const guchar *csum, char *buf); +_OSTREE_PUBLIC void ostree_checksum_b64_inplace_from_bytes (const guchar *csum, char *buf); +_OSTREE_PUBLIC void ostree_checksum_inplace_to_bytes (const char *checksum, guchar *buf); +_OSTREE_PUBLIC const guchar *ostree_checksum_bytes_peek (GVariant *bytes); +_OSTREE_PUBLIC const guchar *ostree_checksum_bytes_peek_validate (GVariant *bytes, GError **error); +_OSTREE_PUBLIC int ostree_cmp_checksum_bytes (const guchar *a, const guchar *b); +_OSTREE_PUBLIC gboolean ostree_validate_rev (const char *rev, GError **error); +_OSTREE_PUBLIC gboolean ostree_parse_refspec (const char *refspec, char **out_remote, char **out_ref, GError **error); +_OSTREE_PUBLIC void ostree_checksum_update_meta (GChecksum *checksum, GFileInfo *file_info, GVariant *xattrs); +_OSTREE_PUBLIC const char * ostree_object_type_to_string (OstreeObjectType objtype); +_OSTREE_PUBLIC OstreeObjectType ostree_object_type_from_string (const char *str); +_OSTREE_PUBLIC guint ostree_hash_object_name (gconstpointer a); +_OSTREE_PUBLIC GVariant *ostree_object_name_serialize (const char *checksum, OstreeObjectType objtype); +_OSTREE_PUBLIC void ostree_object_name_deserialize (GVariant *variant, const char **out_checksum, OstreeObjectType *out_objtype); +_OSTREE_PUBLIC char * ostree_object_to_string (const char *checksum, OstreeObjectType objtype); +_OSTREE_PUBLIC void ostree_object_from_string (const char *str, gchar **out_checksum, OstreeObjectType *out_objtype); -gboolean +_OSTREE_PUBLIC gboolean ostree_content_stream_parse (gboolean compressed, GInputStream *input, guint64 input_length, @@ -231,6 +255,7 @@ ostree_content_stream_parse (gboolean compressed, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_content_file_parse (gboolean compressed, GFile *content_path, gboolean trusted, @@ -240,6 +265,7 @@ gboolean ostree_content_file_parse (gboolean compressed, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_content_file_parse_at (gboolean compressed, int parent_dfd, const char *path, @@ -250,6 +276,7 @@ gboolean ostree_content_file_parse_at (gboolean compressed, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_raw_file_to_content_stream (GInputStream *input, GFileInfo *file_info, GVariant *xattrs, @@ -258,6 +285,7 @@ gboolean ostree_raw_file_to_content_stream (GInputStream *input, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_checksum_file_from_input (GFileInfo *file_info, GVariant *xattrs, GInputStream *in, @@ -266,12 +294,14 @@ gboolean ostree_checksum_file_from_input (GFileInfo *file_info, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_checksum_file (GFile *f, OstreeObjectType objtype, guchar **out_csum, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC void ostree_checksum_file_async (GFile *f, OstreeObjectType objtype, int io_priority, @@ -279,38 +309,49 @@ void ostree_checksum_file_async (GFile *f, GAsyncReadyCallback callback, gpointer user_data); +_OSTREE_PUBLIC gboolean ostree_checksum_file_async_finish (GFile *f, GAsyncResult *result, guchar **out_csum, GError **error); +_OSTREE_PUBLIC GVariant *ostree_create_directory_metadata (GFileInfo *dir_info, GVariant *xattrs); /* VALIDATION */ +_OSTREE_PUBLIC gboolean ostree_validate_structureof_objtype (guchar objtype, GError **error); +_OSTREE_PUBLIC gboolean ostree_validate_structureof_csum_v (GVariant *checksum, GError **error); +_OSTREE_PUBLIC gboolean ostree_validate_structureof_checksum_string (const char *checksum, GError **error); +_OSTREE_PUBLIC gboolean ostree_validate_structureof_file_mode (guint32 mode, GError **error); +_OSTREE_PUBLIC gboolean ostree_validate_structureof_commit (GVariant *commit, GError **error); +_OSTREE_PUBLIC gboolean ostree_validate_structureof_dirtree (GVariant *dirtree, GError **error); +_OSTREE_PUBLIC gboolean ostree_validate_structureof_dirmeta (GVariant *dirmeta, GError **error); +_OSTREE_PUBLIC gchar * ostree_commit_get_parent (GVariant *commit_variant); +_OSTREE_PUBLIC guint64 ostree_commit_get_timestamp (GVariant *commit_variant); G_END_DECLS diff --git a/src/libostree/ostree-deployment-private.h b/src/libostree/ostree-deployment-private.h index b5ebb957..856a3987 100644 --- a/src/libostree/ostree-deployment-private.h +++ b/src/libostree/ostree-deployment-private.h @@ -24,6 +24,21 @@ G_BEGIN_DECLS +struct _OstreeDeployment +{ + GObject parent_instance; + + int index; /* Global offset */ + char *osname; /* osname */ + char *csum; /* OSTree checksum of tree */ + int deployserial; /* How many times this particular csum appears in deployment list */ + char *bootcsum; /* Checksum of kernel+initramfs */ + int bootserial; /* An integer assigned to this tree per its ${bootcsum} */ + OstreeBootconfigParser *bootconfig; /* Bootloader configuration */ + GKeyFile *origin; /* How to construct an upgraded version of this tree */ + OstreeDeploymentUnlockedState unlocked; /* The unlocked state */ +}; + void _ostree_deployment_set_bootcsum (OstreeDeployment *self, const char *bootcsum); G_END_DECLS diff --git a/src/libostree/ostree-deployment.c b/src/libostree/ostree-deployment.c index 3a80474e..7b93e6cc 100644 --- a/src/libostree/ostree-deployment.c +++ b/src/libostree/ostree-deployment.c @@ -23,20 +23,6 @@ #include "ostree-deployment-private.h" #include "libglnx.h" -struct _OstreeDeployment -{ - GObject parent_instance; - - int index; /* Global offset */ - char *osname; /* osname */ - char *csum; /* OSTree checksum of tree */ - int deployserial; /* How many times this particular csum appears in deployment list */ - char *bootcsum; /* Checksum of kernel+initramfs */ - int bootserial; /* An integer assigned to this tree per its ${bootcsum} */ - OstreeBootconfigParser *bootconfig; /* Bootloader configuration */ - GKeyFile *origin; /* How to construct an upgraded version of this tree */ -}; - typedef GObjectClass OstreeDeploymentClass; G_DEFINE_TYPE (OstreeDeployment, ostree_deployment, G_TYPE_OBJECT) @@ -258,6 +244,7 @@ ostree_deployment_new (int index, self->deployserial = deployserial; self->bootcsum = g_strdup (bootcsum); self->bootserial = bootserial; + self->unlocked = OSTREE_DEPLOYMENT_UNLOCKED_NONE; return self; } @@ -279,3 +266,24 @@ ostree_deployment_get_origin_relpath (OstreeDeployment *self) ostree_deployment_get_csum (self), ostree_deployment_get_deployserial (self)); } + +const char * +ostree_deployment_unlocked_state_to_string (OstreeDeploymentUnlockedState state) +{ + switch (state) + { + case OSTREE_DEPLOYMENT_UNLOCKED_NONE: + return "none"; + case OSTREE_DEPLOYMENT_UNLOCKED_HOTFIX: + return "hotfix"; + case OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT: + return "development"; + } + g_assert_not_reached (); +} + +OstreeDeploymentUnlockedState +ostree_deployment_get_unlocked (OstreeDeployment *self) +{ + return self->unlocked; +} diff --git a/src/libostree/ostree-deployment.h b/src/libostree/ostree-deployment.h index dc28d63a..bde0cf37 100644 --- a/src/libostree/ostree-deployment.h +++ b/src/libostree/ostree-deployment.h @@ -30,11 +30,15 @@ G_BEGIN_DECLS typedef struct _OstreeDeployment OstreeDeployment; +_OSTREE_PUBLIC GType ostree_deployment_get_type (void) G_GNUC_CONST; +_OSTREE_PUBLIC guint ostree_deployment_hash (gconstpointer v); +_OSTREE_PUBLIC gboolean ostree_deployment_equal (gconstpointer ap, gconstpointer bp); +_OSTREE_PUBLIC OstreeDeployment * ostree_deployment_new (int index, const char *osname, const char *csum, @@ -42,22 +46,48 @@ OstreeDeployment * ostree_deployment_new (int index, const char *bootcsum, int bootserial); +_OSTREE_PUBLIC int ostree_deployment_get_index (OstreeDeployment *self); +_OSTREE_PUBLIC const char *ostree_deployment_get_osname (OstreeDeployment *self); +_OSTREE_PUBLIC int ostree_deployment_get_deployserial (OstreeDeployment *self); +_OSTREE_PUBLIC const char *ostree_deployment_get_csum (OstreeDeployment *self); +_OSTREE_PUBLIC const char *ostree_deployment_get_bootcsum (OstreeDeployment *self); +_OSTREE_PUBLIC int ostree_deployment_get_bootserial (OstreeDeployment *self); +_OSTREE_PUBLIC OstreeBootconfigParser *ostree_deployment_get_bootconfig (OstreeDeployment *self); +_OSTREE_PUBLIC GKeyFile *ostree_deployment_get_origin (OstreeDeployment *self); +_OSTREE_PUBLIC void ostree_deployment_set_index (OstreeDeployment *self, int index); +_OSTREE_PUBLIC void ostree_deployment_set_bootserial (OstreeDeployment *self, int index); +_OSTREE_PUBLIC void ostree_deployment_set_bootconfig (OstreeDeployment *self, OstreeBootconfigParser *bootconfig); +_OSTREE_PUBLIC void ostree_deployment_set_origin (OstreeDeployment *self, GKeyFile *origin); +_OSTREE_PUBLIC OstreeDeployment *ostree_deployment_clone (OstreeDeployment *self); +_OSTREE_PUBLIC char *ostree_deployment_get_origin_relpath (OstreeDeployment *self); +typedef enum { + OSTREE_DEPLOYMENT_UNLOCKED_NONE, + OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT, + OSTREE_DEPLOYMENT_UNLOCKED_HOTFIX +} OstreeDeploymentUnlockedState; + +_OSTREE_PUBLIC +const char *ostree_deployment_unlocked_state_to_string (OstreeDeploymentUnlockedState state); + +_OSTREE_PUBLIC +OstreeDeploymentUnlockedState ostree_deployment_get_unlocked (OstreeDeployment *self); + G_END_DECLS diff --git a/src/libostree/ostree-diff.c b/src/libostree/ostree-diff.c index c47e98c5..4b733461 100644 --- a/src/libostree/ostree-diff.c +++ b/src/libostree/ostree-diff.c @@ -48,7 +48,8 @@ get_file_checksum (OstreeDiffFlags flags, if (!(flags & OSTREE_DIFF_FLAGS_IGNORE_XATTRS)) { - if (!gs_file_get_all_xattrs (f, &xattrs, cancellable, error)) + if (!glnx_dfd_name_get_all_xattrs (AT_FDCWD, gs_file_get_path_cached (f), + &xattrs, cancellable, error)) goto out; } diff --git a/src/libostree/ostree-diff.h b/src/libostree/ostree-diff.h index ea6db03d..f4db23ef 100644 --- a/src/libostree/ostree-diff.h +++ b/src/libostree/ostree-diff.h @@ -47,11 +47,15 @@ struct _OstreeDiffItem char *target_checksum; }; +_OSTREE_PUBLIC OstreeDiffItem *ostree_diff_item_ref (OstreeDiffItem *diffitem); +_OSTREE_PUBLIC void ostree_diff_item_unref (OstreeDiffItem *diffitem); +_OSTREE_PUBLIC GType ostree_diff_item_get_type (void); +_OSTREE_PUBLIC gboolean ostree_diff_dirs (OstreeDiffFlags flags, GFile *a, GFile *b, @@ -61,6 +65,7 @@ gboolean ostree_diff_dirs (OstreeDiffFlags flags, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC void ostree_diff_print (GFile *a, GFile *b, GPtrArray *modified, diff --git a/src/libostree/ostree-enumtypes.h.template b/src/libostree/ostree-enumtypes.h.template index 85ec41e5..40899d7f 100644 --- a/src/libostree/ostree-enumtypes.h.template +++ b/src/libostree/ostree-enumtypes.h.template @@ -33,6 +33,7 @@ G_BEGIN_DECLS /*** BEGIN enumeration-production ***/ #define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ()) +_OSTREE_PUBLIC GType @enum_name@_get_type (void) G_GNUC_CONST; /*** END enumeration-production ***/ diff --git a/src/libostree/ostree-fetcher.c b/src/libostree/ostree-fetcher.c index b9223217..26e72c45 100644 --- a/src/libostree/ostree-fetcher.c +++ b/src/libostree/ostree-fetcher.c @@ -406,7 +406,7 @@ session_thread_request_uri (ThreadClosure *thread_closure, exists = FALSE; else { - gs_set_error_from_errno (&local_error, errno); + glnx_set_error_from_errno (&local_error); g_task_return_error (task, local_error); return; } @@ -706,7 +706,7 @@ finish_stream (OstreeFetcherPendingURI *pending, pending->out_tmpfile, &stbuf, AT_SYMLINK_NOFOLLOW) != 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -922,7 +922,7 @@ on_request_sent (GObject *object, pending->out_tmpfile, oflags, 0666); if (fd == -1) { - gs_set_error_from_errno (&local_error, errno); + glnx_set_error_from_errno (&local_error); goto out; } pending->out_stream = g_unix_output_stream_new (fd, TRUE); diff --git a/src/libostree/ostree-gpg-verify-result.h b/src/libostree/ostree-gpg-verify-result.h index ce207181..8894afdf 100644 --- a/src/libostree/ostree-gpg-verify-result.h +++ b/src/libostree/ostree-gpg-verify-result.h @@ -21,6 +21,7 @@ #pragma once #include +#include G_BEGIN_DECLS @@ -82,21 +83,27 @@ typedef enum { OSTREE_GPG_SIGNATURE_ATTR_USER_EMAIL } OstreeGpgSignatureAttr; +_OSTREE_PUBLIC GType ostree_gpg_verify_result_get_type (void); +_OSTREE_PUBLIC guint ostree_gpg_verify_result_count_all (OstreeGpgVerifyResult *result); +_OSTREE_PUBLIC guint ostree_gpg_verify_result_count_valid (OstreeGpgVerifyResult *result); +_OSTREE_PUBLIC gboolean ostree_gpg_verify_result_lookup (OstreeGpgVerifyResult *result, const gchar *key_id, guint *out_signature_index); +_OSTREE_PUBLIC GVariant * ostree_gpg_verify_result_get (OstreeGpgVerifyResult *result, guint signature_index, OstreeGpgSignatureAttr *attrs, guint n_attrs); +_OSTREE_PUBLIC GVariant * ostree_gpg_verify_result_get_all (OstreeGpgVerifyResult *result, guint signature_index); @@ -113,12 +120,14 @@ typedef enum { OSTREE_GPG_SIGNATURE_FORMAT_DEFAULT = 0 } OstreeGpgSignatureFormatFlags; +_OSTREE_PUBLIC void ostree_gpg_verify_result_describe (OstreeGpgVerifyResult *result, guint signature_index, GString *output_buffer, const gchar *line_prefix, OstreeGpgSignatureFormatFlags flags); +_OSTREE_PUBLIC void ostree_gpg_verify_result_describe_variant (GVariant *variant, GString *output_buffer, const gchar *line_prefix, diff --git a/src/libostree/ostree-kernel-args.c b/src/libostree/ostree-kernel-args.c index e6b918a1..4c9ff147 100644 --- a/src/libostree/ostree-kernel-args.c +++ b/src/libostree/ostree-kernel-args.c @@ -65,7 +65,7 @@ _ostree_kernel_args_new (void) } void -_ostree_kernel_args_free (OstreeKernelArgs *kargs) +_ostree_kernel_arg_autofree (OstreeKernelArgs *kargs) { if (!kargs) return; @@ -77,7 +77,7 @@ _ostree_kernel_args_free (OstreeKernelArgs *kargs) void _ostree_kernel_args_cleanup (void *loc) { - _ostree_kernel_args_free (*((OstreeKernelArgs**)loc)); + _ostree_kernel_arg_autofree (*((OstreeKernelArgs**)loc)); } void diff --git a/src/libostree/ostree-mutable-tree.h b/src/libostree/ostree-mutable-tree.h index bc8539cd..30425d8d 100644 --- a/src/libostree/ostree-mutable-tree.h +++ b/src/libostree/ostree-mutable-tree.h @@ -45,50 +45,62 @@ struct OstreeMutableTreeClass GObjectClass parent_class; }; +_OSTREE_PUBLIC GType ostree_mutable_tree_get_type (void) G_GNUC_CONST; +_OSTREE_PUBLIC OstreeMutableTree *ostree_mutable_tree_new (void); +_OSTREE_PUBLIC void ostree_mutable_tree_set_metadata_checksum (OstreeMutableTree *self, const char *checksum); +_OSTREE_PUBLIC const char *ostree_mutable_tree_get_metadata_checksum (OstreeMutableTree *self); +_OSTREE_PUBLIC void ostree_mutable_tree_set_contents_checksum (OstreeMutableTree *self, const char *checksum); +_OSTREE_PUBLIC const char *ostree_mutable_tree_get_contents_checksum (OstreeMutableTree *self); +_OSTREE_PUBLIC gboolean ostree_mutable_tree_replace_file (OstreeMutableTree *self, const char *name, const char *checksum, GError **error); +_OSTREE_PUBLIC gboolean ostree_mutable_tree_ensure_dir (OstreeMutableTree *self, const char *name, OstreeMutableTree **out_subdir, GError **error); +_OSTREE_PUBLIC gboolean ostree_mutable_tree_lookup (OstreeMutableTree *self, const char *name, char **out_file_checksum, OstreeMutableTree **out_subdir, GError **error); -gboolean +_OSTREE_PUBLIC gboolean ostree_mutable_tree_ensure_parent_dirs (OstreeMutableTree *self, GPtrArray *split_path, const char *metadata_checksum, OstreeMutableTree **out_parent, GError **error); +_OSTREE_PUBLIC gboolean ostree_mutable_tree_walk (OstreeMutableTree *self, GPtrArray *split_path, guint start, OstreeMutableTree **out_subdir, GError **error); +_OSTREE_PUBLIC GHashTable * ostree_mutable_tree_get_subdirs (OstreeMutableTree *self); +_OSTREE_PUBLIC GHashTable * ostree_mutable_tree_get_files (OstreeMutableTree *self); G_END_DECLS diff --git a/src/libostree/ostree-repo-checkout.c b/src/libostree/ostree-repo-checkout.c index 0879f44b..0a77da02 100644 --- a/src/libostree/ostree-repo-checkout.c +++ b/src/libostree/ostree-repo-checkout.c @@ -73,7 +73,7 @@ checkout_object_for_uncompressed_cache (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (G_UNLIKELY (res == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -91,7 +91,7 @@ checkout_object_for_uncompressed_cache (OstreeRepo *self, { if (errno != EEXIST) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); g_prefix_error (error, "Storing file '%s': ", temp_filename); goto out; } @@ -144,7 +144,7 @@ write_regular_file_content (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (G_UNLIKELY (res == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -153,13 +153,13 @@ write_regular_file_content (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (G_UNLIKELY (res == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } if (xattrs) { - if (!gs_fd_set_all_xattrs (fd, xattrs, cancellable, error)) + if (!glnx_fd_set_all_xattrs (fd, xattrs, cancellable, error)) goto out; } } @@ -168,7 +168,7 @@ write_regular_file_content (OstreeRepo *self, { if (fsync (fd) == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -203,7 +203,7 @@ checkout_file_from_input_at (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (res == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -214,13 +214,13 @@ checkout_file_from_input_at (OstreeRepo *self, g_file_info_get_attribute_uint32 (file_info, "unix::gid"), AT_SYMLINK_NOFOLLOW) == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } if (xattrs) { - if (!gs_dfd_and_name_set_all_xattrs (destination_dfd, destination_name, + if (!glnx_dfd_name_set_all_xattrs (destination_dfd, destination_name, xattrs, cancellable, error)) goto out; } @@ -242,7 +242,7 @@ checkout_file_from_input_at (OstreeRepo *self, while (G_UNLIKELY (fd == -1 && errno == EINTR)); if (fd == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } temp_out = g_unix_output_stream_new (fd, TRUE); @@ -288,7 +288,7 @@ checkout_file_unioning_from_input_at (OstreeRepo *repo, if (xattrs) { - if (!gs_dfd_and_name_set_all_xattrs (destination_dfd, temp_filename, + if (!glnx_dfd_name_set_all_xattrs (destination_dfd, temp_filename, xattrs, cancellable, error)) goto out; } @@ -318,7 +318,7 @@ checkout_file_unioning_from_input_at (OstreeRepo *repo, if (G_UNLIKELY (renameat (destination_dfd, temp_filename, destination_dfd, destination_name) == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -373,7 +373,7 @@ checkout_file_hardlink (OstreeRepo *self, else { g_prefix_error (error, "Hardlinking %s to %s: ", loose_path, destination_name); - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -650,14 +650,13 @@ checkout_tree_at (OstreeRepo *self, did_exist = TRUE; else { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } - if (!gs_file_open_dir_fd_at (destination_parent_fd, destination_name, - &destination_dfd, - cancellable, error)) + if (!glnx_opendirat (destination_parent_fd, destination_name, TRUE, + &destination_dfd, error)) goto out; /* Set the xattrs now, so any derived labeling works */ @@ -668,7 +667,7 @@ checkout_tree_at (OstreeRepo *self, if (xattrs) { - if (!gs_fd_set_all_xattrs (destination_dfd, xattrs, cancellable, error)) + if (!glnx_fd_set_all_xattrs (destination_dfd, xattrs, cancellable, error)) goto out; } } @@ -734,7 +733,7 @@ checkout_tree_at (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (G_UNLIKELY (res == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -748,7 +747,7 @@ checkout_tree_at (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (G_UNLIKELY (res == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -764,7 +763,7 @@ checkout_tree_at (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (G_UNLIKELY (res == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -773,7 +772,7 @@ checkout_tree_at (OstreeRepo *self, { if (fsync (destination_dfd) == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c index a7c16192..0fb3b0fe 100644 --- a/src/libostree/ostree-repo-commit.c +++ b/src/libostree/ostree-repo-commit.c @@ -65,10 +65,9 @@ _ostree_repo_ensure_loose_objdir_at (int dfd, loose_prefix[2] = '\0'; if (mkdirat (dfd, loose_prefix, 0777) == -1) { - int errsv = errno; - if (G_UNLIKELY (errsv != EEXIST)) + if (G_UNLIKELY (errno != EEXIST)) { - gs_set_error_from_errno (error, errsv); + glnx_set_error_from_errno (error); return FALSE; } } @@ -118,7 +117,7 @@ write_file_metadata_to_xattr (int fd, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (G_UNLIKELY (res == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); g_prefix_error (error, "Unable to set xattr: "); return FALSE; } @@ -155,7 +154,7 @@ _ostree_repo_commit_loose_final (OstreeRepo *self, { if (errno != EEXIST) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); g_prefix_error (error, "Storing file '%s': ", temp_filename); goto out; } @@ -195,7 +194,7 @@ commit_loose_object_trusted (OstreeRepo *self, self->target_owner_gid, AT_SYMLINK_NOFOLLOW) == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -213,13 +212,13 @@ commit_loose_object_trusted (OstreeRepo *self, uid, gid, AT_SYMLINK_NOFOLLOW) == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } if (xattrs != NULL) { - if (!gs_dfd_and_name_set_all_xattrs (self->tmp_dir_fd, temp_filename, + if (!glnx_dfd_name_set_all_xattrs (self->tmp_dir_fd, temp_filename, xattrs, cancellable, error)) goto out; } @@ -236,7 +235,7 @@ commit_loose_object_trusted (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (G_UNLIKELY (res == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -245,13 +244,13 @@ commit_loose_object_trusted (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (G_UNLIKELY (res == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } if (xattrs) { - if (!gs_fd_set_all_xattrs (fd, xattrs, cancellable, error)) + if (!glnx_fd_set_all_xattrs (fd, xattrs, cancellable, error)) goto out; } } @@ -272,7 +271,7 @@ commit_loose_object_trusted (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (G_UNLIKELY (res == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -294,7 +293,7 @@ commit_loose_object_trusted (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (G_UNLIKELY (res == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -306,7 +305,7 @@ commit_loose_object_trusted (OstreeRepo *self, { if (fsync (fd) == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -453,7 +452,9 @@ fallocate_stream (GFileDescriptorBased *stream, int r = posix_fallocate (fd, 0, size); if (r != 0) { - gs_set_error_from_errno (error, r); + /* posix_fallocate is a weird deviation from errno standards */ + errno = r; + glnx_set_error_from_errno (error); goto out; } } @@ -511,7 +512,7 @@ _ostree_repo_commit_untrusted_content_bare (OstreeRepo *self, fd = openat (self->tmp_dir_fd, state->temp_filename, O_RDONLY); if (fd == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -597,7 +598,8 @@ _ostree_repo_open_trusted_content_bare (OstreeRepo *self, out_state->temp_filename = temp_filename; temp_filename = NULL; out_state->fd = g_file_descriptor_based_get_fd ((GFileDescriptorBased*)ret_stream); - gs_transfer_out_value (out_stream, &ret_stream); + if (out_stream) + *out_stream = g_steal_pointer (&ret_stream); } *out_have_object = have_obj; out: @@ -843,7 +845,7 @@ write_object (OstreeRepo *self, if (fstatat (self->tmp_dir_fd, temp_filename, &stbuf, AT_SYMLINK_NOFOLLOW) == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -1224,7 +1226,7 @@ rename_pending_loose_objects (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (res == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -1264,14 +1266,14 @@ rename_pending_loose_objects (OstreeRepo *self, if (G_UNLIKELY (renameat (child_dfd_iter.fd, loose_objpath + 3, self->objects_dir_fd, loose_objpath) < 0)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } } - if (!gs_shutil_rm_rf_at (self->tmp_dir_fd, self->commit_stagedir_name, - cancellable, error)) + if (!glnx_shutil_rm_rf_at (self->tmp_dir_fd, self->commit_stagedir_name, + cancellable, error)) goto out; ret = TRUE; @@ -1321,7 +1323,7 @@ cleanup_tmpdir (OstreeRepo *self, delta = curtime_secs - mtime; if (delta > 60*60*24) { - if (!gs_shutil_rm_rf (path, cancellable, error)) + if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (path), cancellable, error)) goto out; } } @@ -1448,7 +1450,7 @@ ostree_repo_commit_transaction (OstreeRepo *self, if (syncfs (self->tmp_dir_fd) < 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -2322,20 +2324,21 @@ get_modified_xattrs (OstreeRepo *self, { if (path) { - if (!gs_file_get_all_xattrs (path, &ret_xattrs, cancellable, error)) + if (!glnx_dfd_name_get_all_xattrs (AT_FDCWD, gs_file_get_path_cached (path), + &ret_xattrs, cancellable, error)) goto out; } else if (dfd_subpath == NULL) { g_assert (dfd != -1); - if (!gs_fd_get_all_xattrs (dfd, &ret_xattrs, + if (!glnx_fd_get_all_xattrs (dfd, &ret_xattrs, cancellable, error)) goto out; } else { g_assert (dfd != -1); - if (!gs_dfd_and_name_get_all_xattrs (dfd, dfd_subpath, &ret_xattrs, + if (!glnx_dfd_name_get_all_xattrs (dfd, dfd_subpath, &ret_xattrs, cancellable, error)) goto out; } @@ -2371,7 +2374,8 @@ get_modified_xattrs (OstreeRepo *self, } ret = TRUE; - gs_transfer_out_value (out_xattrs, &ret_xattrs); + if (out_xattrs) + *out_xattrs = g_steal_pointer (&ret_xattrs); out: return ret; } @@ -2686,7 +2690,7 @@ write_dfd_iter_to_mtree_internal (OstreeRepo *self, if (fstat (src_dfd_iter->fd, &dir_stbuf) != 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -2742,7 +2746,7 @@ write_dfd_iter_to_mtree_internal (OstreeRepo *self, if (fstatat (src_dfd_iter->fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -2966,7 +2970,8 @@ ostree_repo_write_mtree (OstreeRepo *self, } ret = TRUE; - ot_transfer_out_value (out_file, &ret_file); + if (out_file) + *out_file = g_steal_pointer (&ret_file); out: return ret; } diff --git a/src/libostree/ostree-repo-file.h b/src/libostree/ostree-repo-file.h index 925a502c..fb651645 100644 --- a/src/libostree/ostree-repo-file.h +++ b/src/libostree/ostree-repo-file.h @@ -40,38 +40,52 @@ struct _OstreeRepoFileClass GObjectClass parent_class; }; +_OSTREE_PUBLIC GType ostree_repo_file_get_type (void) G_GNUC_CONST; +_OSTREE_PUBLIC gboolean ostree_repo_file_ensure_resolved (OstreeRepoFile *self, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_file_get_xattrs (OstreeRepoFile *self, GVariant **out_xattrs, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC OstreeRepo * ostree_repo_file_get_repo (OstreeRepoFile *self); +_OSTREE_PUBLIC OstreeRepoFile * ostree_repo_file_get_root (OstreeRepoFile *self); +_OSTREE_PUBLIC void ostree_repo_file_make_empty_tree (OstreeRepoFile *self); +_OSTREE_PUBLIC void ostree_repo_file_tree_set_metadata (OstreeRepoFile *self, const char *checksum, GVariant *metadata); +_OSTREE_PUBLIC const char *ostree_repo_file_tree_get_contents_checksum (OstreeRepoFile *self); +_OSTREE_PUBLIC const char *ostree_repo_file_tree_get_metadata_checksum (OstreeRepoFile *self); +_OSTREE_PUBLIC GVariant *ostree_repo_file_tree_get_contents (OstreeRepoFile *self); +_OSTREE_PUBLIC GVariant *ostree_repo_file_tree_get_metadata (OstreeRepoFile *self); +_OSTREE_PUBLIC const char * ostree_repo_file_get_checksum (OstreeRepoFile *self); +_OSTREE_PUBLIC int ostree_repo_file_tree_find_child (OstreeRepoFile *self, const char *name, gboolean *is_dir, GVariant **out_container); +_OSTREE_PUBLIC gboolean ostree_repo_file_tree_query_child (OstreeRepoFile *self, int n, const char *attributes, diff --git a/src/libostree/ostree-repo-libarchive.c b/src/libostree/ostree-repo-libarchive.c index 1c051592..7a30192c 100644 --- a/src/libostree/ostree-repo-libarchive.c +++ b/src/libostree/ostree-repo-libarchive.c @@ -290,7 +290,6 @@ write_libarchive_entry_to_mtree (OstreeRepo *self, out: return ret; } -#endif static gboolean create_empty_dir_with_uidgid (OstreeRepo *self, @@ -308,6 +307,7 @@ create_empty_dir_with_uidgid (OstreeRepo *self, return _ostree_repo_write_directory_meta (self, tmp_dir_info, NULL, out_csum, cancellable, error); } +#endif /** * ostree_repo_import_archive_to_mtree: @@ -331,6 +331,7 @@ ostree_repo_import_archive_to_mtree (OstreeRepo *self, GCancellable *cancellable, GError **error) { +#ifdef HAVE_LIBARCHIVE gboolean ret = FALSE; struct archive *a = archive; struct archive_entry *entry; @@ -393,6 +394,11 @@ ostree_repo_import_archive_to_mtree (OstreeRepo *self, ret = TRUE; out: return ret; +#else + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "This version of ostree is not compiled with libarchive support"); + return FALSE; +#endif } /** diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h index 463b3dd7..484a6ec8 100644 --- a/src/libostree/ostree-repo-private.h +++ b/src/libostree/ostree-repo-private.h @@ -33,6 +33,8 @@ G_BEGIN_DECLS #define _OSTREE_OBJECT_SIZES_ENTRY_SIGNATURE "ay" +#define _OSTREE_SUMMARY_CACHE_PATH "tmp/cache/summaries" + /** * OstreeRepo: * @@ -316,5 +318,23 @@ _ostree_repo_read_bare_fd (OstreeRepo *self, gboolean _ostree_repo_update_mtime (OstreeRepo *self, GError **error); - + +/* Load the summary from the cache if the provided .sig file is the same as the + cached version. */ +gboolean +_ostree_repo_load_cache_summary_if_same_sig (OstreeRepo *self, + const char *remote, + GBytes *summary_sig, + GBytes **summary, + GCancellable *cancellable, + GError **error); + +gboolean +_ostree_repo_cache_summary (OstreeRepo *self, + const char *remote, + GBytes *summary, + GBytes *summary_sig, + GCancellable *cancellable, + GError **error); + G_END_DECLS diff --git a/src/libostree/ostree-repo-prune.c b/src/libostree/ostree-repo-prune.c index 166e67e0..eca2cff6 100644 --- a/src/libostree/ostree-repo-prune.c +++ b/src/libostree/ostree-repo-prune.c @@ -112,6 +112,68 @@ maybe_prune_loose_object (OtPruneData *data, return ret; } +static gboolean +_ostree_repo_prune_tmp (OstreeRepo *self, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; + glnx_fd_close int fd = -1; + + fd = glnx_opendirat_with_errno (self->repo_dir_fd, _OSTREE_SUMMARY_CACHE_PATH, FALSE); + if (fd < 0) + { + if (errno == ENOENT) + ret = TRUE; + else + glnx_set_error_from_errno (error); + goto out; + } + + if (!glnx_dirfd_iterator_init_take_fd (dup (fd), &dfd_iter, error)) + goto out; + + while (TRUE) + { + size_t len; + gboolean has_sig_suffix = FALSE; + struct dirent *dent; + + if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error)) + goto out; + + if (dent == NULL) + break; + + len = strlen (dent->d_name); + if (len > 4 && g_strcmp0 (dent->d_name + len - 4, ".sig") == 0) + { + has_sig_suffix = TRUE; + dent->d_name[len - 4] = '\0'; + } + + if (!g_hash_table_contains (self->remotes, dent->d_name)) + { + /* Restore the previous value to get the file name. */ + if (has_sig_suffix) + dent->d_name[len - 4] = '.'; + + if (unlinkat (fd, dent->d_name, 0) < 0) + { + glnx_set_error_from_errno (error); + goto out; + } + } + } + + ret = TRUE; + + out: + return ret; +} + + /** * ostree_repo_prune_static_deltas: * @self: Repo @@ -174,7 +236,7 @@ ostree_repo_prune_static_deltas (OstreeRepo *self, const char *commit, deltadir = _ostree_get_relative_static_delta_path (from, to, NULL); - if (!gs_shutil_rm_rf_at (self->repo_dir_fd, deltadir, + if (!glnx_shutil_rm_rf_at (self->repo_dir_fd, deltadir, cancellable, error)) goto out; } @@ -241,10 +303,28 @@ ostree_repo_prune (OstreeRepo *self, while (g_hash_table_iter_next (&hash_iter, &key, &value)) { const char *checksum = value; - - if (!ostree_repo_traverse_commit_union (self, checksum, depth, data.reachable, - cancellable, error)) + OstreeRepoCommitState commitstate; + GError *local_error = NULL; + + if (!ostree_repo_load_commit (self, checksum, NULL, &commitstate, + error)) goto out; + + if (!ostree_repo_traverse_commit_union (self, checksum, depth, data.reachable, + cancellable, &local_error)) + { + /* Don't fail traversing a partial commit */ + if ((commitstate & OSTREE_REPO_COMMIT_STATE_PARTIAL) > 0 && + g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_clear_error (&local_error); + } + else + { + g_propagate_error (error, local_error); + goto out; + } + } } } @@ -260,15 +340,33 @@ ostree_repo_prune (OstreeRepo *self, GVariant *serialized_key = key; const char *checksum; OstreeObjectType objtype; + OstreeRepoCommitState commitstate; + GError *local_error = NULL; ostree_object_name_deserialize (serialized_key, &checksum, &objtype); if (objtype != OSTREE_OBJECT_TYPE_COMMIT) continue; - - if (!ostree_repo_traverse_commit_union (self, checksum, depth, data.reachable, - cancellable, error)) + + if (!ostree_repo_load_commit (self, checksum, NULL, &commitstate, + error)) goto out; + + if (!ostree_repo_traverse_commit_union (self, checksum, depth, data.reachable, + cancellable, &local_error)) + { + /* Don't fail traversing a partial commit */ + if ((commitstate & OSTREE_REPO_COMMIT_STATE_PARTIAL) > 0 && + g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + { + g_clear_error (&local_error); + } + else + { + g_propagate_error (error, local_error); + goto out; + } + } } } @@ -295,6 +393,9 @@ ostree_repo_prune (OstreeRepo *self, if (!ostree_repo_prune_static_deltas (self, NULL, cancellable, error)) goto out; + if (!_ostree_repo_prune_tmp (self, cancellable, error)) + goto out; + ret = TRUE; *out_objects_total = (data.n_reachable_meta + data.n_unreachable_meta + data.n_reachable_content + data.n_unreachable_content); diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 0be1b380..ecbd7386 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -28,6 +28,7 @@ #include "ostree-repo-static-delta-private.h" #include "ostree-metalink.h" #include "otutil.h" +#include "ot-fs-utils.h" #include @@ -794,7 +795,7 @@ meta_fetch_on_complete (GObject *object, OstreeObjectType objtype; GError *local_error = NULL; GError **error = &local_error; - gs_fd_close int fd = -1; + glnx_fd_close int fd = -1; ostree_object_name_deserialize (fetch_data->object, &checksum, &objtype); g_debug ("fetch of %s%s complete", ostree_object_to_string (checksum, objtype), @@ -840,7 +841,7 @@ meta_fetch_on_complete (GObject *object, fd = openat (_ostree_fetcher_get_dfd (fetcher), temp_path, O_RDONLY | O_CLOEXEC); if (fd == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -949,7 +950,7 @@ static_deltapart_fetch_on_complete (GObject *object, g_autoptr(GVariant) part = NULL; GError *local_error = NULL; GError **error = &local_error; - gs_fd_close int fd = -1; + glnx_fd_close int fd = -1; g_debug ("fetch static delta part %s complete", fetch_data->expected_checksum); @@ -1187,7 +1188,8 @@ scan_one_metadata_object_c (OtPullData *pull_data, if (pull_data->remote_repo_local) { - if (!ostree_repo_import_object_from (pull_data->repo, pull_data->remote_repo_local, + if (!is_stored && + !ostree_repo_import_object_from (pull_data->repo, pull_data->remote_repo_local, objtype, tmp_checksum, cancellable, error)) goto out; @@ -1398,8 +1400,8 @@ request_static_delta_superblock_sync (OtPullData *pull_data, if (delta_superblock_data) { { - gs_free gchar *delta = NULL; - gs_free guchar *ret_csum = NULL; + g_autofree gchar *delta = NULL; + g_autofree guchar *ret_csum = NULL; guchar *summary_csum; g_autoptr (GInputStream) summary_is = NULL; @@ -1436,7 +1438,8 @@ request_static_delta_superblock_sync (OtPullData *pull_data, } ret = TRUE; - gs_transfer_out_value (out_delta_superblock, &ret_delta_superblock); + if (out_delta_superblock) + *out_delta_superblock = g_steal_pointer (&ret_delta_superblock); out: return ret; } @@ -1767,6 +1770,102 @@ ostree_repo_pull_one_dir (OstreeRepo *self, progress, cancellable, error); } +gboolean +_ostree_repo_load_cache_summary_if_same_sig (OstreeRepo *self, + const char *remote, + GBytes *summary_sig, + GBytes **summary, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + const char *summary_cache_sig_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_PATH, "/", remote, ".sig"); + + glnx_fd_close int prev_fd = -1; + g_autoptr(GBytes) old_sig_contents = NULL; + + if (!ot_openat_ignore_enoent (self->repo_dir_fd, summary_cache_sig_file, &prev_fd, error)) + goto out; + + if (prev_fd < 0) + { + ret = TRUE; + goto out; + } + + old_sig_contents = glnx_fd_readall_bytes (prev_fd, cancellable, error); + if (!old_sig_contents) + goto out; + + if (g_bytes_compare (old_sig_contents, summary_sig) == 0) + { + const char *summary_cache_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_PATH, "/", remote); + glnx_fd_close int summary_fd = -1; + GBytes *summary_data; + + + summary_fd = openat (self->repo_dir_fd, summary_cache_file, O_CLOEXEC | O_RDONLY); + if (summary_fd < 0) + { + if (errno == ENOENT) + { + (void) unlinkat (self->repo_dir_fd, summary_cache_sig_file, 0); + ret = TRUE; + goto out; + } + + glnx_set_error_from_errno (error); + goto out; + } + + summary_data = glnx_fd_readall_bytes (summary_fd, cancellable, error); + if (!summary_data) + goto out; + *summary = summary_data; + } + ret = TRUE; + + out: + return ret; +} + +gboolean +_ostree_repo_cache_summary (OstreeRepo *self, + const char *remote, + GBytes *summary, + GBytes *summary_sig, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + const char *summary_cache_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_PATH, "/", remote); + const char *summary_cache_sig_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_PATH, "/", remote, ".sig"); + + if (!glnx_shutil_mkdir_p_at (self->repo_dir_fd, _OSTREE_SUMMARY_CACHE_PATH, 0775, cancellable, error)) + goto out; + + if (!glnx_file_replace_contents_at (self->repo_dir_fd, + summary_cache_file, + g_bytes_get_data (summary, NULL), + g_bytes_get_size (summary), + self->disable_fsync ? GLNX_FILE_REPLACE_NODATASYNC : GLNX_FILE_REPLACE_DATASYNC_NEW, + cancellable, error)) + goto out; + + if (!glnx_file_replace_contents_at (self->repo_dir_fd, + summary_cache_sig_file, + g_bytes_get_data (summary_sig, NULL), + g_bytes_get_size (summary_sig), + self->disable_fsync ? GLNX_FILE_REPLACE_NODATASYNC : GLNX_FILE_REPLACE_DATASYNC_NEW, + cancellable, error)) + goto out; + + ret = TRUE; + out: + return ret; + +} + /* Documented in ostree-repo.c */ gboolean ostree_repo_pull_with_options (OstreeRepo *self, @@ -1995,15 +2094,36 @@ ostree_repo_pull_with_options (OstreeRepo *self, g_autoptr(GVariant) refs = NULL; g_autoptr(GVariant) deltas = NULL; g_autoptr(GVariant) additional_metadata = NULL; - - if (!pull_data->summary) + gboolean summary_from_cache = FALSE; + + if (!pull_data->summary_data_sig) + { + uri = suburi_new (pull_data->base_uri, "summary.sig", NULL); + if (!fetch_uri_contents_membuf_sync (pull_data, uri, FALSE, TRUE, + &bytes_sig, cancellable, error)) + goto out; + soup_uri_free (uri); + } + + if (bytes_sig && !_ostree_repo_load_cache_summary_if_same_sig (self, + remote_name_or_baseurl, + bytes_sig, + &bytes_summary, + cancellable, + error)) + goto out; + + if (bytes_summary) + summary_from_cache = TRUE; + + if (!pull_data->summary && !bytes_summary) { uri = suburi_new (pull_data->base_uri, "summary", NULL); if (!fetch_uri_contents_membuf_sync (pull_data, uri, FALSE, TRUE, &bytes_summary, cancellable, error)) goto out; soup_uri_free (uri); - } + } if (!bytes_summary && pull_data->gpg_verify_summary) { @@ -2019,15 +2139,6 @@ ostree_repo_pull_with_options (OstreeRepo *self, goto out; } - if (bytes_summary) - { - uri = suburi_new (pull_data->base_uri, "summary.sig", NULL); - if (!fetch_uri_contents_membuf_sync (pull_data, uri, FALSE, TRUE, - &bytes_sig, cancellable, error)) - goto out; - soup_uri_free (uri); - } - if (!bytes_sig && pull_data->gpg_verify_summary) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, @@ -2044,6 +2155,18 @@ ostree_repo_pull_with_options (OstreeRepo *self, pull_data->summary_data_sig = g_bytes_ref (bytes_sig); } + + if (!summary_from_cache && bytes_summary && bytes_sig) + { + if (!_ostree_repo_cache_summary (self, + remote_name_or_baseurl, + bytes_summary, + bytes_sig, + cancellable, + error)) + goto out; + } + if (pull_data->gpg_verify_summary && bytes_summary && bytes_sig) { g_autoptr(GVariant) sig_variant = NULL; diff --git a/src/libostree/ostree-repo-refs.c b/src/libostree/ostree-repo-refs.c index 68e34f20..0c03ad1d 100644 --- a/src/libostree/ostree-repo-refs.c +++ b/src/libostree/ostree-repo-refs.c @@ -22,6 +22,7 @@ #include "ostree-repo-private.h" #include "otutil.h" +#include "ot-fs-utils.h" static gboolean add_ref_to_set (const char *remote, @@ -114,31 +115,6 @@ write_checksum_file_at (OstreeRepo *self, return ret; } -static gboolean -openat_ignore_enoent (int dfd, - const char *path, - int *out_fd, - GError **error) -{ - gboolean ret = FALSE; - int target_fd = -1; - - target_fd = openat (dfd, path, O_CLOEXEC | O_RDONLY); - if (target_fd < 0) - { - if (errno != ENOENT) - { - glnx_set_error_from_errno (error); - goto out; - } - } - - ret = TRUE; - *out_fd = target_fd; - out: - return ret; -} - static gboolean find_ref_in_remotes (OstreeRepo *self, const char *rev, @@ -168,7 +144,7 @@ find_ref_in_remotes (OstreeRepo *self, if (!glnx_opendirat (dfd_iter.fd, dent->d_name, TRUE, &remote_dfd, error)) goto out; - if (!openat_ignore_enoent (remote_dfd, rev, &ret_fd, error)) + if (!ot_openat_ignore_enoent (remote_dfd, rev, &ret_fd, error)) goto out; if (ret_fd != -1) @@ -247,21 +223,21 @@ resolve_refspec (OstreeRepo *self, { const char *remote_ref = glnx_strjoina ("refs/remotes/", remote, "/", ref); - if (!openat_ignore_enoent (self->repo_dir_fd, remote_ref, &target_fd, error)) + if (!ot_openat_ignore_enoent (self->repo_dir_fd, remote_ref, &target_fd, error)) goto out; } else { const char *local_ref = glnx_strjoina ("refs/heads/", ref); - if (!openat_ignore_enoent (self->repo_dir_fd, local_ref, &target_fd, error)) + if (!ot_openat_ignore_enoent (self->repo_dir_fd, local_ref, &target_fd, error)) goto out; if (target_fd == -1) { local_ref = glnx_strjoina ("refs/remotes/", ref); - if (!openat_ignore_enoent (self->repo_dir_fd, local_ref, &target_fd, error)) + if (!ot_openat_ignore_enoent (self->repo_dir_fd, local_ref, &target_fd, error)) goto out; if (target_fd == -1) @@ -508,24 +484,13 @@ enumerate_refs_recurse (OstreeRepo *repo, return ret; } -/** - * ostree_repo_list_refs: - * @self: Repo - * @refspec_prefix: (allow-none): Only list refs which match this prefix - * @out_all_refs: (out) (element-type utf8 utf8): Mapping from ref to checksum - * @cancellable: Cancellable - * @error: Error - * - * If @refspec_prefix is %NULL, list all local and remote refspecs, - * with their current values in @out_all_refs. Otherwise, only list - * refspecs which have @refspec_prefix as a prefix. - */ -gboolean -ostree_repo_list_refs (OstreeRepo *self, - const char *refspec_prefix, - GHashTable **out_all_refs, - GCancellable *cancellable, - GError **error) +static gboolean +_ostree_repo_list_refs_internal (OstreeRepo *self, + gboolean cut_prefix, + const char *refspec_prefix, + GHashTable **out_all_refs, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; g_autoptr(GHashTable) ret_all_refs = NULL; @@ -568,12 +533,14 @@ ostree_repo_list_refs (OstreeRepo *self, { glnx_fd_close int base_fd = -1; g_autoptr(GString) base_path = g_string_new (""); + if (!cut_prefix) + g_string_printf (base_path, "%s/", ref_prefix); - if (!glnx_opendirat (self->repo_dir_fd, path, TRUE, &base_fd, error)) + if (!glnx_opendirat (self->repo_dir_fd, cut_prefix ? path : prefix_path, TRUE, &base_fd, error)) goto out; if (!enumerate_refs_recurse (self, remote, base_fd, base_path, - base_fd, ".", + base_fd, cut_prefix ? "." : ref_prefix, ret_all_refs, cancellable, error)) goto out; } @@ -639,6 +606,54 @@ ostree_repo_list_refs (OstreeRepo *self, return ret; } +/** + * ostree_repo_list_refs: + * @self: Repo + * @refspec_prefix: (allow-none): Only list refs which match this prefix + * @out_all_refs: (out) (element-type utf8 utf8): Mapping from ref to checksum + * @cancellable: Cancellable + * @error: Error + * + * If @refspec_prefix is %NULL, list all local and remote refspecs, + * with their current values in @out_all_refs. Otherwise, only list + * refspecs which have @refspec_prefix as a prefix. + */ +gboolean +ostree_repo_list_refs (OstreeRepo *self, + const char *refspec_prefix, + GHashTable **out_all_refs, + GCancellable *cancellable, + GError **error) +{ + return _ostree_repo_list_refs_internal (self, TRUE, refspec_prefix, out_all_refs, cancellable, error); +} + +/** + * ostree_repo_list_refs_ext: + * @self: Repo + * @refspec_prefix: (allow-none): Only list refs which match this prefix + * @out_all_refs: (out) (element-type utf8 utf8): Mapping from ref to checksum + * @flags: Options controlling listing behavior + * @cancellable: Cancellable + * @error: Error + * + * If @refspec_prefix is %NULL, list all local and remote refspecs, + * with their current values in @out_all_refs. Otherwise, only list + * refspecs which have @refspec_prefix as a prefix. Differently from + * ostree_repo_list_refs(), the prefix will not be removed from the ref + * name. + */ +gboolean +ostree_repo_list_refs_ext (OstreeRepo *self, + const char *refspec_prefix, + GHashTable **out_all_refs, + OstreeRepoListRefsExtFlags flags, + GCancellable *cancellable, + GError **error) +{ + return _ostree_repo_list_refs_internal (self, FALSE, refspec_prefix, out_all_refs, cancellable, error); +} + /** * ostree_repo_remote_list_refs: * @self: Repo diff --git a/src/libostree/ostree-repo-static-delta-compilation-analysis.c b/src/libostree/ostree-repo-static-delta-compilation-analysis.c index 876c041f..ae5b8dec 100644 --- a/src/libostree/ostree-repo-static-delta-compilation-analysis.c +++ b/src/libostree/ostree-repo-static-delta-compilation-analysis.c @@ -181,7 +181,8 @@ build_content_sizenames_filtered (OstreeRepo *repo, g_ptr_array_sort (ret_sizenames, compare_sizenames); ret = TRUE; - gs_transfer_out_value (out_sizenames, &ret_sizenames); + if (out_sizenames) + *out_sizenames = g_steal_pointer (&ret_sizenames); out: return ret; } @@ -296,7 +297,8 @@ _ostree_delta_compute_similar_objects (OstreeRepo *repo, } ret = TRUE; - gs_transfer_out_value (out_modified_regfile_content, &ret_modified_regfile_content); + if (out_modified_regfile_content) + *out_modified_regfile_content = g_steal_pointer (&ret_modified_regfile_content); out: return ret; } diff --git a/src/libostree/ostree-repo-static-delta-compilation.c b/src/libostree/ostree-repo-static-delta-compilation.c index 2d02e6a6..df5dc19e 100644 --- a/src/libostree/ostree-repo-static-delta-compilation.c +++ b/src/libostree/ostree-repo-static-delta-compilation.c @@ -456,7 +456,7 @@ get_unpacked_unlinked_content (OstreeRepo *repo, { gboolean ret = FALSE; g_autofree char *tmpname = g_strdup ("tmpostree-deltaobj-XXXXXX"); - gs_fd_close int fd = -1; + glnx_fd_close int fd = -1; g_autoptr(GBytes) ret_content = NULL; g_autoptr(GInputStream) istream = NULL; g_autoptr(GFileInfo) ret_finfo = NULL; @@ -465,7 +465,7 @@ get_unpacked_unlinked_content (OstreeRepo *repo, fd = g_mkstemp (tmpname); if (fd == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } /* Doesn't need a name */ @@ -488,7 +488,8 @@ get_unpacked_unlinked_content (OstreeRepo *repo, } ret = TRUE; - gs_transfer_out_value (out_content, &ret_content); + if (out_content) + *out_content = g_steal_pointer (&ret_content); out: return ret; } @@ -532,7 +533,8 @@ try_content_bsdiff (OstreeRepo *repo, ret_bsdiff->tmp_to = tmp_to; tmp_to = NULL; ret = TRUE; - gs_transfer_out_value (out_bsdiff, &ret_bsdiff); + if (out_bsdiff) + *out_bsdiff = g_steal_pointer (&ret_bsdiff); out: return ret; } @@ -597,7 +599,8 @@ try_content_rollsum (OstreeRepo *repo, ret_rollsum->tmp_to = tmp_to; tmp_to = NULL; ret = TRUE; - gs_transfer_out_value (out_rollsum, &ret_rollsum); + if (out_rollsum) + *out_rollsum = g_steal_pointer (&ret_rollsum); out: if (matches) _ostree_rollsum_matches_free (matches); @@ -1199,7 +1202,8 @@ get_fallback_headers (OstreeRepo *self, ret_headers = g_variant_ref_sink (g_variant_builder_end (fallback_builder)); ret = TRUE; - gs_transfer_out_value (out_headers, &ret_headers); + if (out_headers) + *out_headers = g_steal_pointer (&ret_headers); out: return ret; } diff --git a/src/libostree/ostree-repo-static-delta-processing.c b/src/libostree/ostree-repo-static-delta-processing.c index b44314b6..6c5dd463 100644 --- a/src/libostree/ostree-repo-static-delta-processing.c +++ b/src/libostree/ostree-repo-static-delta-processing.c @@ -754,7 +754,7 @@ dispatch_write (OstreeRepo *repo, { if (lseek (state->read_source_fd, content_offset, SEEK_SET) == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } while (content_size > 0) @@ -767,7 +767,7 @@ dispatch_write (OstreeRepo *repo, while (G_UNLIKELY (bytes_read == -1 && errno == EINTR)); if (bytes_read == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } if (G_UNLIKELY (bytes_read == 0)) diff --git a/src/libostree/ostree-repo-traverse.c b/src/libostree/ostree-repo-traverse.c index f442343b..97bd1023 100644 --- a/src/libostree/ostree-repo-traverse.c +++ b/src/libostree/ostree-repo-traverse.c @@ -377,9 +377,8 @@ traverse_dirtree (OstreeRepo *repo, ostree_cleanup_repo_commit_traverse_iter OstreeRepoCommitTraverseIter iter = { 0, }; - if (!ostree_repo_load_variant_if_exists (repo, OSTREE_OBJECT_TYPE_DIR_TREE, - checksum, &dirtree, - error)) + if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_DIR_TREE, checksum, + &dirtree, error)) goto out; if (!ostree_repo_commit_traverse_iter_init_dirtree (&iter, repo, dirtree, @@ -503,7 +502,8 @@ ostree_repo_traverse_commit (OstreeRepo *repo, goto out; ret = TRUE; - gs_transfer_out_value (out_reachable, &ret_reachable); + if (out_reachable) + *out_reachable = g_steal_pointer (&ret_reachable); out: return ret; } diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 3b08d445..09791c6a 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -34,6 +34,7 @@ #include "ostree-repo-file-enumerator.h" #include "ostree-gpg-verifier.h" #include "ostree-repo-static-delta-private.h" +#include "ot-fs-utils.h" #ifdef HAVE_LIBSOUP #include "ostree-metalink.h" @@ -1749,9 +1750,7 @@ repo_remote_fetch_summary (OstreeRepo *self, g_autoptr(GMainContext) mainctx = NULL; gboolean ret = FALSE; SoupURI *base_uri = NULL; - uint i; - const char *filenames[] = {"summary", "summary.sig"}; - GBytes **outputs[] = {out_summary, out_signatures}; + gboolean from_cache = FALSE; mainctx = g_main_context_new (); g_main_context_push_thread_default (mainctx); @@ -1781,19 +1780,63 @@ repo_remote_fetch_summary (OstreeRepo *self, } } - for (i = 0; i < G_N_ELEMENTS (filenames); i++) + if (!_ostree_preload_metadata_file (self, + fetcher, + base_uri, + "summary.sig", + metalink_url_string ? TRUE : FALSE, + out_signatures, + cancellable, + error)) + goto out; + + if (*out_signatures) + { + if (!_ostree_repo_load_cache_summary_if_same_sig (self, + name, + *out_signatures, + out_summary, + cancellable, + error)) + goto out; + } + + if (*out_summary) + from_cache = TRUE; + else { if (!_ostree_preload_metadata_file (self, fetcher, base_uri, - filenames[i], + "summary", metalink_url_string ? TRUE : FALSE, - outputs[i], + out_summary, cancellable, error)) goto out; } + if (!from_cache && *out_summary && *out_signatures) + { + g_autoptr(GError) temp_error = NULL; + + if (!_ostree_repo_cache_summary (self, + name, + *out_summary, + *out_signatures, + cancellable, + &temp_error)) + { + if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) + g_debug ("No permissions to save summary cache"); + else + { + g_propagate_error (error, g_steal_pointer (&temp_error)); + goto out; + } + } + } + ret = TRUE; out: @@ -2072,7 +2115,8 @@ enumerate_directory_allow_noent (GFile *dirpath, } ret = TRUE; - gs_transfer_out_value (out_direnum, &ret_direnum); + if (out_direnum) + *out_direnum = g_steal_pointer (&ret_direnum); out: return ret; } @@ -2227,14 +2271,15 @@ ostree_repo_open (OstreeRepo *self, goto out; g_strdelimit (self->boot_id, "\n", '\0'); - if (!gs_file_open_dir_fd (self->repodir, &self->repo_dir_fd, cancellable, error)) + if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (self->repodir), TRUE, + &self->repo_dir_fd, error)) { g_prefix_error (error, "%s: ", gs_file_get_path_cached (self->repodir)); goto out; } - if (!gs_file_open_dir_fd_at (self->repo_dir_fd, "objects", - &self->objects_dir_fd, cancellable, error)) + if (!glnx_opendirat (self->repo_dir_fd, "objects", TRUE, + &self->objects_dir_fd, error)) { g_prefix_error (error, "Opening objects/ directory: "); goto out; @@ -2244,12 +2289,12 @@ ostree_repo_open (OstreeRepo *self, if (!self->writable) { /* This is returned through ostree_repo_is_writable(). */ - gs_set_error_from_errno (&self->writable_error, errno); + glnx_set_error_from_errno (&self->writable_error); } if (fstat (self->objects_dir_fd, &stbuf) != 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -2343,16 +2388,16 @@ ostree_repo_open (OstreeRepo *self, goto out; } - if (!gs_file_open_dir_fd (self->tmp_dir, &self->tmp_dir_fd, cancellable, error)) + if (!glnx_opendirat (self->repo_dir_fd, "tmp", TRUE, &self->tmp_dir_fd, error)) goto out; if (self->mode == OSTREE_REPO_MODE_ARCHIVE_Z2 && self->enable_uncompressed_cache) { if (!gs_file_ensure_directory (self->uncompressed_objects_dir, TRUE, cancellable, error)) goto out; - if (!gs_file_open_dir_fd (self->uncompressed_objects_dir, - &self->uncompressed_objects_dir_fd, - cancellable, error)) + if (!glnx_opendirat (self->repo_dir_fd, "uncompressed-objects-cache", TRUE, + &self->uncompressed_objects_dir_fd, + error)) goto out; } @@ -2423,6 +2468,24 @@ ostree_repo_get_path (OstreeRepo *self) return self->repodir; } +/** + * ostree_repo_get_dfd: + * @self: Repo + * + * In some cases it's useful for applications to access the repository + * directly; for example, writing content into `repo/tmp` ensures it's + * on the same filesystem. Another case is detecting the mtime on the + * repository (to see whether a ref was written). + * + * Returns: File descriptor for repository root - owned by @self + */ +int +ostree_repo_get_dfd (OstreeRepo *self) +{ + g_return_val_if_fail (self->repo_dir_fd != -1, -1); + return self->repo_dir_fd; +} + OstreeRepoMode ostree_repo_get_mode (OstreeRepo *self) { @@ -2463,7 +2526,7 @@ list_loose_objects_at (OstreeRepo *self, d = fdopendir (dfd); if (!d) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -2557,7 +2620,7 @@ list_loose_objects (OstreeRepo *self, continue; else { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -2573,32 +2636,6 @@ list_loose_objects (OstreeRepo *self, return ret; } -static gboolean -openat_allow_noent (int dfd, - const char *path, - int *fd, - GCancellable *cancellable, - GError **error) -{ - GError *temp_error = NULL; - - if (!gs_file_openat_noatime (dfd, path, fd, - cancellable, &temp_error)) - { - if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - { - *fd = -1; - g_clear_error (&temp_error); - } - else - { - g_propagate_error (error, temp_error); - return FALSE; - } - } - return TRUE; -} - static gboolean load_metadata_internal (OstreeRepo *self, OstreeObjectType objtype, @@ -2620,14 +2657,14 @@ load_metadata_internal (OstreeRepo *self, _ostree_loose_path (loose_path_buf, sha256, objtype, self->mode); - if (!openat_allow_noent (self->objects_dir_fd, loose_path_buf, &fd, - cancellable, error)) + if (!ot_openat_ignore_enoent (self->objects_dir_fd, loose_path_buf, &fd, + error)) goto out; if (fd < 0 && self->commit_stagedir_fd != -1) { - if (!openat_allow_noent (self->commit_stagedir_fd, loose_path_buf, &fd, - cancellable, error)) + if (!ot_openat_ignore_enoent (self->commit_stagedir_fd, loose_path_buf, &fd, + error)) goto out; } @@ -2714,7 +2751,7 @@ query_info_for_bare_content_object (OstreeRepo *self, ret = TRUE; goto out; } - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -2738,7 +2775,8 @@ query_info_for_bare_content_object (OstreeRepo *self, } ret = TRUE; - gs_transfer_out_value (out_info, &ret_info); + if (out_info) + *out_info = g_steal_pointer (&ret_info); out: return ret; } @@ -2821,8 +2859,8 @@ ostree_repo_load_file (OstreeRepo *self, struct stat stbuf; g_autoptr(GInputStream) tmp_stream = NULL; - if (!openat_allow_noent (self->objects_dir_fd, loose_path_buf, &fd, - cancellable, error)) + if (!ot_openat_ignore_enoent (self->objects_dir_fd, loose_path_buf, &fd, + error)) goto out; if (fd != -1) @@ -2859,7 +2897,7 @@ ostree_repo_load_file (OstreeRepo *self, guint32 mode; g_autoptr(GVariant) metadata = NULL; g_autoptr(GBytes) bytes = NULL; - gs_fd_close int fd = -1; + glnx_fd_close int fd = -1; bytes = ot_lgetxattrat (self->objects_dir_fd, loose_path_buf, "user.ostreemeta", error); @@ -2918,7 +2956,7 @@ ostree_repo_load_file (OstreeRepo *self, if (g_file_info_get_file_type (ret_file_info) == G_FILE_TYPE_REGULAR && (out_input || out_xattrs)) { - gs_fd_close int fd = -1; + glnx_fd_close int fd = -1; if (!gs_file_openat_noatime (self->objects_dir_fd, loose_path_buf, &fd, cancellable, error)) @@ -2926,7 +2964,7 @@ ostree_repo_load_file (OstreeRepo *self, if (out_xattrs) { - if (!gs_fd_get_all_xattrs (fd, &ret_xattrs, + if (!glnx_fd_get_all_xattrs (fd, &ret_xattrs, cancellable, error)) goto out; } @@ -2940,7 +2978,7 @@ ostree_repo_load_file (OstreeRepo *self, else if (g_file_info_get_file_type (ret_file_info) == G_FILE_TYPE_SYMBOLIC_LINK && out_xattrs) { - if (!gs_dfd_and_name_get_all_xattrs (self->objects_dir_fd, loose_path_buf, + if (!glnx_dfd_name_get_all_xattrs (self->objects_dir_fd, loose_path_buf, &ret_xattrs, cancellable, error)) goto out; @@ -3063,7 +3101,7 @@ _ostree_repo_has_loose_object (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (res == -1 && errno != ENOENT) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -3077,7 +3115,7 @@ _ostree_repo_has_loose_object (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (res == -1 && errno != ENOENT) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -3195,7 +3233,7 @@ ostree_repo_delete_object (OstreeRepo *self, { if (G_UNLIKELY (errno != ENOENT)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -3206,7 +3244,7 @@ ostree_repo_delete_object (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (G_UNLIKELY (res == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -3342,7 +3380,7 @@ import_one_object_link (OstreeRepo *self, ret = TRUE; } else - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -3446,7 +3484,7 @@ ostree_repo_query_object_storage_size (OstreeRepo *self, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (G_UNLIKELY (res == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -4053,7 +4091,8 @@ sign_data (OstreeRepo *self, ret_signature = g_mapped_file_get_bytes (signature_file); ret = TRUE; - gs_transfer_out_value (out_signature, &ret_signature); + if (out_signature) + *out_signature = g_steal_pointer (&ret_signature); out: if (commit_buffer) gpgme_data_release (commit_buffer); @@ -4601,11 +4640,11 @@ ostree_repo_regenerate_summary (OstreeRepo *self, g_variant_dict_init (&deltas_builder, NULL); for (i = 0; i < delta_names->len; i++) { - gs_free char *from = NULL; - gs_free char *to = NULL; - gs_free guchar *csum = NULL; - gs_free char *superblock = NULL; - gs_fd_close int superblock_file_fd = -1; + g_autofree char *from = NULL; + g_autofree char *to = NULL; + g_autofree guchar *csum = NULL; + g_autofree char *superblock = NULL; + glnx_fd_close int superblock_file_fd = -1; g_autoptr(GInputStream) in_stream = NULL; _ostree_parse_delta_name (delta_names->pdata[i], &from, &to); @@ -4613,7 +4652,7 @@ ostree_repo_regenerate_summary (OstreeRepo *self, superblock_file_fd = openat (self->repo_dir_fd, superblock, O_RDONLY | O_CLOEXEC); if (superblock_file_fd == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -4656,7 +4695,7 @@ ostree_repo_regenerate_summary (OstreeRepo *self, { if (errno != ENOENT) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index 4629ea55..07e76aa8 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -36,46 +36,65 @@ G_BEGIN_DECLS #define OSTREE_IS_REPO(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_REPO)) +_OSTREE_PUBLIC gboolean ostree_repo_mode_from_string (const char *mode, OstreeRepoMode *out_mode, GError **error); +_OSTREE_PUBLIC GType ostree_repo_get_type (void); +_OSTREE_PUBLIC OstreeRepo* ostree_repo_new (GFile *path); +_OSTREE_PUBLIC OstreeRepo* ostree_repo_new_for_sysroot_path (GFile *repo_path, GFile *sysroot_path); +_OSTREE_PUBLIC OstreeRepo* ostree_repo_new_default (void); +_OSTREE_PUBLIC gboolean ostree_repo_open (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC void ostree_repo_set_disable_fsync (OstreeRepo *self, gboolean disable_fsync); +_OSTREE_PUBLIC gboolean ostree_repo_get_disable_fsync (OstreeRepo *self); +_OSTREE_PUBLIC gboolean ostree_repo_is_system (OstreeRepo *repo); +_OSTREE_PUBLIC gboolean ostree_repo_is_writable (OstreeRepo *self, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_create (OstreeRepo *self, OstreeRepoMode mode, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC GFile * ostree_repo_get_path (OstreeRepo *self); +_OSTREE_PUBLIC +int ostree_repo_get_dfd (OstreeRepo *self); + +_OSTREE_PUBLIC OstreeRepoMode ostree_repo_get_mode (OstreeRepo *self); +_OSTREE_PUBLIC GKeyFile * ostree_repo_get_config (OstreeRepo *self); +_OSTREE_PUBLIC GKeyFile * ostree_repo_copy_config (OstreeRepo *self); +_OSTREE_PUBLIC gboolean ostree_repo_remote_add (OstreeRepo *self, const char *name, const char *url, @@ -83,6 +102,7 @@ gboolean ostree_repo_remote_add (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_remote_delete (OstreeRepo *self, const char *name, GCancellable *cancellable, @@ -95,6 +115,7 @@ typedef enum { OSTREE_REPO_REMOTE_CHANGE_DELETE_IF_EXISTS } OstreeRepoRemoteChange; +_OSTREE_PUBLIC gboolean ostree_repo_remote_change (OstreeRepo *self, GFile *sysroot, OstreeRepoRemoteChange changeop, @@ -104,24 +125,29 @@ gboolean ostree_repo_remote_change (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC char ** ostree_repo_remote_list (OstreeRepo *self, guint *out_n_remotes); +_OSTREE_PUBLIC gboolean ostree_repo_remote_get_url (OstreeRepo *self, const char *name, char **out_url, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_remote_get_gpg_verify (OstreeRepo *self, const char *name, gboolean *out_gpg_verify, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_remote_get_gpg_verify_summary (OstreeRepo *self, const char *name, gboolean *out_gpg_verify_summary, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_remote_gpg_import (OstreeRepo *self, const char *name, GInputStream *source_stream, @@ -130,6 +156,7 @@ gboolean ostree_repo_remote_gpg_import (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_remote_fetch_summary (OstreeRepo *self, const char *name, GBytes **out_summary, @@ -137,8 +164,10 @@ gboolean ostree_repo_remote_fetch_summary (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC OstreeRepo * ostree_repo_get_parent (OstreeRepo *self); +_OSTREE_PUBLIC gboolean ostree_repo_write_config (OstreeRepo *self, GKeyFile *new_config, GError **error); @@ -174,35 +203,43 @@ struct _OstreeRepoTransactionStats { guint64 padding4; }; +_OSTREE_PUBLIC GType ostree_repo_transaction_stats_get_type (void); +_OSTREE_PUBLIC gboolean ostree_repo_scan_hardlinks (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_prepare_transaction (OstreeRepo *self, gboolean *out_transaction_resume, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_commit_transaction (OstreeRepo *self, OstreeRepoTransactionStats *out_stats, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_abort_transaction (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC void ostree_repo_transaction_set_refspec (OstreeRepo *self, const char *refspec, const char *checksum); +_OSTREE_PUBLIC void ostree_repo_transaction_set_ref (OstreeRepo *self, const char *remote, const char *ref, const char *checksum); +_OSTREE_PUBLIC gboolean ostree_repo_set_ref_immediate (OstreeRepo *self, const char *remote, const char *ref, @@ -210,6 +247,7 @@ gboolean ostree_repo_set_ref_immediate (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_has_object (OstreeRepo *self, OstreeObjectType objtype, const char *checksum, @@ -217,6 +255,7 @@ gboolean ostree_repo_has_object (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_write_metadata (OstreeRepo *self, OstreeObjectType objtype, const char *expected_checksum, @@ -225,6 +264,7 @@ gboolean ostree_repo_write_metadata (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC void ostree_repo_write_metadata_async (OstreeRepo *self, OstreeObjectType objtype, const char *expected_checksum, @@ -233,11 +273,13 @@ void ostree_repo_write_metadata_async (OstreeRepo *self, GAsyncReadyCallback callback, gpointer user_data); +_OSTREE_PUBLIC gboolean ostree_repo_write_metadata_finish (OstreeRepo *self, GAsyncResult *result, guchar **out_csum, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_write_content (OstreeRepo *self, const char *expected_checksum, GInputStream *object_input, @@ -246,6 +288,7 @@ gboolean ostree_repo_write_content (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_write_metadata_trusted (OstreeRepo *self, OstreeObjectType objtype, const char *checksum, @@ -253,6 +296,7 @@ gboolean ostree_repo_write_metadata_trusted (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_write_metadata_stream_trusted (OstreeRepo *self, OstreeObjectType objtype, const char *checksum, @@ -261,6 +305,7 @@ gboolean ostree_repo_write_metadata_stream_trusted (OstreeRepo *self GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_write_content_trusted (OstreeRepo *self, const char *checksum, GInputStream *object_input, @@ -268,6 +313,7 @@ gboolean ostree_repo_write_content_trusted (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC void ostree_repo_write_content_async (OstreeRepo *self, const char *expected_checksum, GInputStream *object, @@ -276,35 +322,57 @@ void ostree_repo_write_content_async (OstreeRepo *self, GAsyncReadyCallback callback, gpointer user_data); +_OSTREE_PUBLIC gboolean ostree_repo_write_content_finish (OstreeRepo *self, GAsyncResult *result, guchar **out_csum, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_resolve_rev (OstreeRepo *self, const char *refspec, gboolean allow_noent, char **out_rev, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_list_refs (OstreeRepo *self, const char *refspec_prefix, GHashTable **out_all_refs, GCancellable *cancellable, GError **error); +/** + * OstreeRepoListRefsExtFlags: + * @OSTREE_REPO_LIST_REFS_EXT_NONE: No flags. + */ +typedef enum { + OSTREE_REPO_LIST_REFS_EXT_NONE = 0, +} OstreeRepoListRefsExtFlags; + +_OSTREE_PUBLIC +gboolean ostree_repo_list_refs_ext (OstreeRepo *self, + const char *refspec_prefix, + GHashTable **out_all_refs, + OstreeRepoListRefsExtFlags flags, + GCancellable *cancellable, + GError **error); + +_OSTREE_PUBLIC gboolean ostree_repo_remote_list_refs (OstreeRepo *self, const char *remote_name, GHashTable **out_all_refs, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_load_variant (OstreeRepo *self, OstreeObjectType objtype, const char *sha256, GVariant **out_variant, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_load_variant_if_exists (OstreeRepo *self, OstreeObjectType objtype, const char *sha256, @@ -315,12 +383,14 @@ typedef enum { OSTREE_REPO_COMMIT_STATE_PARTIAL = (1 << 0), } OstreeRepoCommitState; +_OSTREE_PUBLIC gboolean ostree_repo_load_commit (OstreeRepo *self, const char *checksum, GVariant **out_commit, OstreeRepoCommitState *out_state, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_load_file (OstreeRepo *self, const char *checksum, GInputStream **out_input, @@ -329,6 +399,7 @@ gboolean ostree_repo_load_file (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_load_object_stream (OstreeRepo *self, OstreeObjectType objtype, const char *checksum, @@ -337,6 +408,7 @@ gboolean ostree_repo_load_object_stream (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_query_object_storage_size (OstreeRepo *self, OstreeObjectType objtype, const char *sha256, @@ -344,6 +416,7 @@ gboolean ostree_repo_query_object_storage_size (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_import_object_from (OstreeRepo *self, OstreeRepo *source, OstreeObjectType objtype, @@ -351,6 +424,7 @@ gboolean ostree_repo_import_object_from (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_delete_object (OstreeRepo *self, OstreeObjectType objtype, const char *sha256, @@ -400,11 +474,13 @@ typedef enum { */ typedef struct OstreeRepoCommitModifier OstreeRepoCommitModifier; +_OSTREE_PUBLIC OstreeRepoCommitModifier *ostree_repo_commit_modifier_new (OstreeRepoCommitModifierFlags flags, OstreeRepoCommitFilter commit_filter, gpointer user_data, GDestroyNotify destroy_notify); +_OSTREE_PUBLIC GType ostree_repo_commit_modifier_get_type (void); typedef GVariant *(*OstreeRepoCommitModifierXattrCallback) (OstreeRepo *repo, @@ -412,20 +488,26 @@ typedef GVariant *(*OstreeRepoCommitModifierXattrCallback) (OstreeRepo *repo GFileInfo *file_info, gpointer user_data); +_OSTREE_PUBLIC void ostree_repo_commit_modifier_set_xattr_callback (OstreeRepoCommitModifier *modifier, OstreeRepoCommitModifierXattrCallback callback, GDestroyNotify destroy, gpointer user_data); +_OSTREE_PUBLIC void ostree_repo_commit_modifier_set_sepolicy (OstreeRepoCommitModifier *modifier, OstreeSePolicy *sepolicy); +_OSTREE_PUBLIC void ostree_repo_commit_modifier_set_devino_cache (OstreeRepoCommitModifier *modifier, OstreeRepoDevInoCache *cache); +_OSTREE_PUBLIC OstreeRepoCommitModifier *ostree_repo_commit_modifier_ref (OstreeRepoCommitModifier *modifier); +_OSTREE_PUBLIC void ostree_repo_commit_modifier_unref (OstreeRepoCommitModifier *modifier); +_OSTREE_PUBLIC gboolean ostree_repo_write_directory_to_mtree (OstreeRepo *self, GFile *dir, OstreeMutableTree *mtree, @@ -433,6 +515,7 @@ gboolean ostree_repo_write_directory_to_mtree (OstreeRepo * GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_write_dfd_to_mtree (OstreeRepo *self, int dfd, const char *path, @@ -442,6 +525,7 @@ gboolean ostree_repo_write_dfd_to_mtree (OstreeRepo *self, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_write_archive_to_mtree (OstreeRepo *self, GFile *archive, OstreeMutableTree *mtree, @@ -466,6 +550,7 @@ typedef struct { gpointer unused_ptrs[8]; } OstreeRepoImportArchiveOptions; +_OSTREE_PUBLIC gboolean ostree_repo_import_archive_to_mtree (OstreeRepo *self, OstreeRepoImportArchiveOptions *opts, void *archive, /* Really struct archive * */ @@ -490,6 +575,7 @@ typedef struct { gpointer unused_ptrs[8]; } OstreeRepoExportArchiveOptions; +_OSTREE_PUBLIC gboolean ostree_repo_export_tree_to_archive (OstreeRepo *self, OstreeRepoExportArchiveOptions *opts, OstreeRepoFile *root, @@ -497,12 +583,14 @@ gboolean ostree_repo_export_tree_to_archive (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_write_mtree (OstreeRepo *self, OstreeMutableTree *mtree, GFile **out_file, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_write_commit (OstreeRepo *self, const char *parent, const char *subject, @@ -513,6 +601,7 @@ gboolean ostree_repo_write_commit (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_write_commit_with_time (OstreeRepo *self, const char *parent, const char *subject, @@ -524,12 +613,14 @@ gboolean ostree_repo_write_commit_with_time (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_read_commit_detached_metadata (OstreeRepo *self, const char *checksum, GVariant **out_metadata, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_write_commit_detached_metadata (OstreeRepo *self, const char *checksum, GVariant *metadata, @@ -556,7 +647,7 @@ typedef enum { OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES = 1 } OstreeRepoCheckoutOverwriteMode; -gboolean +_OSTREE_PUBLIC gboolean ostree_repo_checkout_tree (OstreeRepo *self, OstreeRepoCheckoutMode mode, OstreeRepoCheckoutOverwriteMode overwrite_mode, @@ -592,11 +683,16 @@ typedef struct { gpointer unused_ptrs[7]; } OstreeRepoCheckoutOptions; +_OSTREE_PUBLIC GType ostree_repo_devino_cache_get_type (void); +_OSTREE_PUBLIC OstreeRepoDevInoCache *ostree_repo_devino_cache_new (void); +_OSTREE_PUBLIC OstreeRepoDevInoCache * ostree_repo_devino_cache_ref (OstreeRepoDevInoCache *cache); +_OSTREE_PUBLIC void ostree_repo_devino_cache_unref (OstreeRepoDevInoCache *cache); +_OSTREE_PUBLIC gboolean ostree_repo_checkout_tree_at (OstreeRepo *self, OstreeRepoCheckoutOptions *options, int destination_dfd, @@ -605,10 +701,12 @@ gboolean ostree_repo_checkout_tree_at (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_checkout_gc (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_read_commit (OstreeRepo *self, const char *ref, GFile **out_root, @@ -636,18 +734,21 @@ typedef enum { */ #define OSTREE_REPO_LIST_OBJECTS_VARIANT_TYPE (G_VARIANT_TYPE ("(bas)") +_OSTREE_PUBLIC gboolean ostree_repo_list_objects (OstreeRepo *self, OstreeRepoListObjectsFlags flags, GHashTable **out_objects, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_list_commit_objects_starting_with ( OstreeRepo *self, const char *start, GHashTable **out_commits, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_list_static_delta_names (OstreeRepo *self, GPtrArray **out_deltas, GCancellable *cancellable, @@ -665,6 +766,7 @@ typedef enum { OSTREE_STATIC_DELTA_GENERATE_OPT_MAJOR } OstreeStaticDeltaGenerateOpt; +_OSTREE_PUBLIC gboolean ostree_repo_static_delta_generate (OstreeRepo *self, OstreeStaticDeltaGenerateOpt opt, const char *from, @@ -674,14 +776,17 @@ gboolean ostree_repo_static_delta_generate (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_static_delta_execute_offline (OstreeRepo *self, GFile *dir_or_file, gboolean skip_validation, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC GHashTable *ostree_repo_traverse_new_reachable (void); +_OSTREE_PUBLIC gboolean ostree_repo_traverse_commit (OstreeRepo *repo, const char *commit_checksum, int maxdepth, @@ -689,6 +794,7 @@ gboolean ostree_repo_traverse_commit (OstreeRepo *repo, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_traverse_commit_union (OstreeRepo *repo, const char *commit_checksum, int maxdepth, @@ -708,14 +814,14 @@ typedef enum { OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE = (1 << 0) } OstreeRepoCommitTraverseFlags; -gboolean +_OSTREE_PUBLIC gboolean ostree_repo_commit_traverse_iter_init_commit (OstreeRepoCommitTraverseIter *iter, OstreeRepo *repo, GVariant *commit, OstreeRepoCommitTraverseFlags flags, GError **error); -gboolean +_OSTREE_PUBLIC gboolean ostree_repo_commit_traverse_iter_init_dirtree (OstreeRepoCommitTraverseIter *iter, OstreeRepo *repo, GVariant *dirtree, @@ -729,22 +835,26 @@ typedef enum { OSTREE_REPO_COMMIT_ITER_RESULT_DIR } OstreeRepoCommitIterResult; +_OSTREE_PUBLIC OstreeRepoCommitIterResult ostree_repo_commit_traverse_iter_next (OstreeRepoCommitTraverseIter *iter, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC void ostree_repo_commit_traverse_iter_get_file (OstreeRepoCommitTraverseIter *iter, char **out_name, char **out_checksum); +_OSTREE_PUBLIC void ostree_repo_commit_traverse_iter_get_dir (OstreeRepoCommitTraverseIter *iter, char **out_name, char **out_content_checksum, char **out_meta_checksum); -void +_OSTREE_PUBLIC void ostree_repo_commit_traverse_iter_clear (OstreeRepoCommitTraverseIter *iter); +_OSTREE_PUBLIC void ostree_repo_commit_traverse_iter_cleanup (void *p); #define ostree_cleanup_repo_commit_traverse_iter __attribute__ ((cleanup(ostree_repo_commit_traverse_iter_cleanup))) @@ -761,11 +871,12 @@ typedef enum { OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY } OstreeRepoPruneFlags; -gboolean +_OSTREE_PUBLIC gboolean ostree_repo_prune_static_deltas (OstreeRepo *self, const char *commit, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_prune (OstreeRepo *self, OstreeRepoPruneFlags flags, gint depth, @@ -787,6 +898,7 @@ typedef enum { OSTREE_REPO_PULL_FLAGS_COMMIT_ONLY = (1 << 1) } OstreeRepoPullFlags; +_OSTREE_PUBLIC gboolean ostree_repo_pull (OstreeRepo *self, const char *remote_name, char **refs_to_fetch, @@ -795,7 +907,7 @@ gboolean ostree_repo_pull (OstreeRepo *self, GCancellable *cancellable, GError **error); -gboolean +_OSTREE_PUBLIC gboolean ostree_repo_pull_one_dir (OstreeRepo *self, const char *remote_name, const char *dir_to_pull, @@ -805,6 +917,7 @@ ostree_repo_pull_one_dir (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_pull_with_options (OstreeRepo *self, const char *remote_name, GVariant *options, @@ -812,9 +925,11 @@ gboolean ostree_repo_pull_with_options (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC void ostree_repo_pull_default_console_progress_changed (OstreeAsyncProgress *progress, gpointer user_data); +_OSTREE_PUBLIC gboolean ostree_repo_sign_commit (OstreeRepo *self, const gchar *commit_checksum, const gchar *key_id, @@ -822,6 +937,7 @@ gboolean ostree_repo_sign_commit (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_sign_delta (OstreeRepo *self, const gchar *from_commit, const gchar *to_commit, @@ -830,19 +946,21 @@ gboolean ostree_repo_sign_delta (OstreeRepo *self, GCancellable *cancellable, GError **error); -gboolean +_OSTREE_PUBLIC gboolean ostree_repo_add_gpg_signature_summary (OstreeRepo *self, const gchar **key_id, const gchar *homedir, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_append_gpg_signature (OstreeRepo *self, const gchar *commit_checksum, GBytes *signature_bytes, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_verify_commit (OstreeRepo *self, const gchar *commit_checksum, GFile *keyringdir, @@ -850,6 +968,7 @@ gboolean ostree_repo_verify_commit (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC OstreeGpgVerifyResult * ostree_repo_verify_commit_ext (OstreeRepo *self, const gchar *commit_checksum, GFile *keyringdir, @@ -857,6 +976,7 @@ OstreeGpgVerifyResult * ostree_repo_verify_commit_ext (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC OstreeGpgVerifyResult * ostree_repo_verify_summary (OstreeRepo *self, const char *remote_name, GBytes *summary, @@ -864,6 +984,7 @@ OstreeGpgVerifyResult * ostree_repo_verify_summary (OstreeRepo *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_repo_regenerate_summary (OstreeRepo *self, GVariant *additional_metadata, GCancellable *cancellable, diff --git a/src/libostree/ostree-sepolicy.c b/src/libostree/ostree-sepolicy.c index 68a184b0..b8e3572d 100644 --- a/src/libostree/ostree-sepolicy.c +++ b/src/libostree/ostree-sepolicy.c @@ -347,10 +347,9 @@ ostree_sepolicy_get_label (OstreeSePolicy *self, res = selabel_lookup_raw (self->selinux_hnd, &con, relpath, unix_mode); if (res != 0) { - int errsv = errno; - if (errsv != ENOENT) + if (errno != ENOENT) { - gs_set_error_from_errno (error, errsv); + glnx_set_error_from_errno (error); goto out; } } @@ -443,14 +442,15 @@ ostree_sepolicy_restorecon (OstreeSePolicy *self, int res = lsetfilecon (gs_file_get_path_cached (target), label); if (res != 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } } ret = TRUE; - gs_transfer_out_value (out_new_label, &label); + if (out_new_label) + *out_new_label = g_steal_pointer (&label); out: return ret; #else @@ -490,7 +490,7 @@ ostree_sepolicy_setfscreatecon (OstreeSePolicy *self, if (setfscreatecon_raw (label) != 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); return FALSE; } diff --git a/src/libostree/ostree-sepolicy.h b/src/libostree/ostree-sepolicy.h index ac8a0e9c..83e3b379 100644 --- a/src/libostree/ostree-sepolicy.h +++ b/src/libostree/ostree-sepolicy.h @@ -30,16 +30,21 @@ G_BEGIN_DECLS #define OSTREE_IS_SEPOLICY(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_SEPOLICY)) +_OSTREE_PUBLIC GType ostree_sepolicy_get_type (void); +_OSTREE_PUBLIC OstreeSePolicy* ostree_sepolicy_new (GFile *path, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC GFile * ostree_sepolicy_get_path (OstreeSePolicy *self); +_OSTREE_PUBLIC const char *ostree_sepolicy_get_name (OstreeSePolicy *self); +_OSTREE_PUBLIC gboolean ostree_sepolicy_get_label (OstreeSePolicy *self, const char *relpath, guint32 unix_mode, @@ -53,6 +58,7 @@ typedef enum { OSTREE_SEPOLICY_RESTORECON_FLAGS_KEEP_EXISTING = (1 << 1) } OstreeSePolicyRestoreconFlags; +_OSTREE_PUBLIC gboolean ostree_sepolicy_restorecon (OstreeSePolicy *self, const char *path, GFileInfo *info, @@ -62,11 +68,13 @@ gboolean ostree_sepolicy_restorecon (OstreeSePolicy *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_sepolicy_setfscreatecon (OstreeSePolicy *self, const char *path, guint32 mode, GError **error); +_OSTREE_PUBLIC void ostree_sepolicy_fscreatecon_cleanup (void **unused); #define ostree_cleanup_sepolicy_fscreatecon __attribute__ ((cleanup(ostree_sepolicy_fscreatecon_cleanup))) diff --git a/src/libostree/ostree-sysroot-cleanup.c b/src/libostree/ostree-sysroot-cleanup.c index 718c16e8..5c370507 100644 --- a/src/libostree/ostree-sysroot-cleanup.c +++ b/src/libostree/ostree-sysroot-cleanup.c @@ -255,28 +255,28 @@ cleanup_other_bootversions (OstreeSysroot *self, cleanup_subbootversion = self->subbootversion == 0 ? 1 : 0; cleanup_boot_dir = ot_gfile_resolve_path_printf (self->path, "boot/loader.%d", cleanup_bootversion); - if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error)) + if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (cleanup_boot_dir), cancellable, error)) goto out; g_clear_object (&cleanup_boot_dir); cleanup_boot_dir = ot_gfile_resolve_path_printf (self->path, "ostree/boot.%d", cleanup_bootversion); - if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error)) + if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (cleanup_boot_dir), cancellable, error)) goto out; g_clear_object (&cleanup_boot_dir); cleanup_boot_dir = ot_gfile_resolve_path_printf (self->path, "ostree/boot.%d.0", cleanup_bootversion); - if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error)) + if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (cleanup_boot_dir), cancellable, error)) goto out; g_clear_object (&cleanup_boot_dir); cleanup_boot_dir = ot_gfile_resolve_path_printf (self->path, "ostree/boot.%d.1", cleanup_bootversion); - if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error)) + if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (cleanup_boot_dir), cancellable, error)) goto out; g_clear_object (&cleanup_boot_dir); cleanup_boot_dir = ot_gfile_resolve_path_printf (self->path, "ostree/boot.%d.%d", self->bootversion, cleanup_subbootversion); - if (!gs_shutil_rm_rf (cleanup_boot_dir, cancellable, error)) + if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (cleanup_boot_dir), cancellable, error)) goto out; g_clear_object (&cleanup_boot_dir); @@ -377,7 +377,7 @@ cleanup_old_deployments (OstreeSysroot *self, if (g_hash_table_lookup (active_boot_checksums, bootcsum)) continue; - if (!gs_shutil_rm_rf (bootdir, cancellable, error)) + if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (bootdir), cancellable, error)) goto out; } @@ -401,7 +401,7 @@ cleanup_ref_prefix (OstreeRepo *repo, prefix = g_strdup_printf ("ostree/%d/%d", bootversion, subbootversion); - if (!ostree_repo_list_refs (repo, prefix, &refs, cancellable, error)) + if (!ostree_repo_list_refs_ext (repo, prefix, &refs, OSTREE_REPO_LIST_REFS_EXT_NONE, cancellable, error)) goto out; if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) @@ -410,8 +410,7 @@ cleanup_ref_prefix (OstreeRepo *repo, g_hash_table_iter_init (&hashiter, refs); while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue)) { - const char *suffix = hashkey; - g_autofree char *ref = g_strconcat (prefix, "/", suffix, NULL); + const char *ref = hashkey; ostree_repo_transaction_set_refspec (repo, ref, NULL); } diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index aa034951..77ecb9af 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -22,6 +22,12 @@ #include #include +#include +#include + +#ifdef HAVE_LIBMOUNT +#include +#endif #include "ostree-sysroot-private.h" #include "ostree-deployment-private.h" @@ -97,26 +103,26 @@ dirfd_copy_attributes_and_xattrs (int src_parent_dfd, * right. This will allow other users access if they have ACLs, but * oh well. */ - if (!gs_dfd_and_name_get_all_xattrs (src_parent_dfd, src_name, + if (!glnx_dfd_name_get_all_xattrs (src_parent_dfd, src_name, &xattrs, cancellable, error)) goto out; - if (!gs_fd_set_all_xattrs (dest_dfd, xattrs, + if (!glnx_fd_set_all_xattrs (dest_dfd, xattrs, cancellable, error)) goto out; if (fstat (src_dfd, &src_stbuf) != 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } if (fchown (dest_dfd, src_stbuf.st_uid, src_stbuf.st_gid) != 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } if (fchmod (dest_dfd, src_stbuf.st_mode) != 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -144,7 +150,7 @@ copy_dir_recurse (int src_parent_dfd, /* Create with mode 0700, we'll fchmod/fchown later */ if (mkdirat (dest_parent_dfd, name, 0700) != 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -158,7 +164,7 @@ copy_dir_recurse (int src_parent_dfd, srcd = fdopendir (src_dfd); if (!srcd) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -174,7 +180,7 @@ copy_dir_recurse (int src_parent_dfd, if (fstatat (src_dfd, name, &child_stbuf, AT_SYMLINK_NOFOLLOW) != 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -256,7 +262,7 @@ ensure_directory_from_template (int orig_etc_fd, } else { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); g_prefix_error (error, "mkdirat: "); goto out; } @@ -306,7 +312,7 @@ copy_modified_config_file (int orig_etc_fd, if (fstatat (modified_etc_fd, path, &modified_stbuf, AT_SYMLINK_NOFOLLOW) < 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); g_prefix_error (error, "Failed to read modified config file '%s': ", path); goto out; } @@ -324,7 +330,7 @@ copy_modified_config_file (int orig_etc_fd, dest_parent_dfd = dup (new_etc_fd); if (dest_parent_dfd == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -337,7 +343,7 @@ copy_modified_config_file (int orig_etc_fd, ; else { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -363,7 +369,7 @@ copy_modified_config_file (int orig_etc_fd, { if (unlinkat (new_etc_fd, path, 0) < 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } } @@ -450,11 +456,14 @@ merge_etc_changes (GFile *orig_etc, removed->len, added->len); - if (!gs_file_open_dir_fd (orig_etc, &orig_etc_fd, cancellable, error)) + if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (orig_etc), TRUE, + &orig_etc_fd, error)) goto out; - if (!gs_file_open_dir_fd (modified_etc, &modified_etc_fd, cancellable, error)) + if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (modified_etc), TRUE, + &modified_etc_fd, error)) goto out; - if (!gs_file_open_dir_fd (new_etc, &new_etc_fd, cancellable, error)) + if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (new_etc), TRUE, + &new_etc_fd, error)) goto out; for (i = 0; i < removed->len; i++) @@ -467,7 +476,7 @@ merge_etc_changes (GFile *orig_etc, g_assert (path); target_file = g_file_resolve_relative_path (new_etc, path); - if (!gs_shutil_rm_rf (target_file, cancellable, error)) + if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (target_file), cancellable, error)) goto out; } @@ -873,7 +882,8 @@ merge_configuration (OstreeSysroot *sysroot, } ret = TRUE; - gs_transfer_out_value (out_sepolicy, &sepolicy); + if (out_sepolicy) + *out_sepolicy = g_steal_pointer (&sepolicy); out: return ret; } @@ -941,8 +951,8 @@ get_kernel_from_tree (int deployment_dfd, g_auto(GLnxDirFdIterator) dfditer = { 0, }; g_autofree char *ret_kernel_name = NULL; g_autofree char *ret_initramfs_name = NULL; - gs_free char *kernel_checksum = NULL; - gs_free char *initramfs_checksum = NULL; + g_autofree char *kernel_checksum = NULL; + g_autofree char *initramfs_checksum = NULL; ret_boot_dfd = glnx_opendirat_with_errno (deployment_dfd, "usr/lib/ostree-boot", TRUE); if (ret_boot_dfd == -1) @@ -1360,7 +1370,7 @@ install_deployment_kernel (OstreeSysroot *sysroot, { if (errno != ENOENT) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } else @@ -1645,6 +1655,47 @@ cleanup_legacy_current_symlinks (OstreeSysroot *self, return ret; } +static gboolean +is_ro_mount (const char *path) +{ +#ifdef HAVE_LIBMOUNT + /* Dragging in all of this crud is apparently necessary just to determine + * whether something is a mount point. + * + * Systemd has a totally different implementation in + * src/basic/mount-util.c. + */ + struct libmnt_table *tb = mnt_new_table_from_file ("/proc/self/mountinfo"); + struct libmnt_fs *fs; + struct libmnt_cache *cache; + gboolean is_mount = FALSE; + struct statvfs stvfsbuf; + + if (!tb) + return FALSE; + + /* to canonicalize all necessary paths */ + cache = mnt_new_cache (); + mnt_table_set_cache (tb, cache); + + fs = mnt_table_find_target(tb, path, MNT_ITER_BACKWARD); + is_mount = fs && mnt_fs_get_target (fs); + mnt_free_cache (cache); + mnt_free_table (tb); + + if (!is_mount) + return FALSE; + + /* We *could* parse the options, but it seems more reliable to + * introspect the actual mount at runtime. + */ + if (statvfs (path, &stvfsbuf) == 0) + return (stvfsbuf.f_flag & ST_RDONLY) != 0; + +#endif + return FALSE; +} + /** * ostree_sysroot_write_deployments: * @self: Sysroot @@ -1666,6 +1717,7 @@ ostree_sysroot_write_deployments (OstreeSysroot *self, gboolean requires_new_bootversion = FALSE; gboolean found_booted_deployment = FALSE; gboolean bootloader_is_atomic = FALSE; + gboolean boot_was_ro_mount = FALSE; g_assert (self->loaded); @@ -1753,12 +1805,26 @@ ostree_sysroot_write_deployments (OstreeSysroot *self, glnx_unref_object OstreeRepo *repo = NULL; gboolean show_osname = FALSE; + if (self->booted_deployment) + boot_was_ro_mount = is_ro_mount ("/boot"); + + g_debug ("boot is ro: %s", boot_was_ro_mount ? "yes" : "no"); + + if (boot_was_ro_mount) + { + if (mount ("/boot", "/boot", NULL, MS_REMOUNT | MS_SILENT, NULL) < 0) + { + glnx_set_prefix_error_from_errno (error, "%s", "Remounting /boot read-write"); + goto out; + } + } + if (!_ostree_sysroot_query_bootloader (self, &bootloader, cancellable, error)) goto out; new_loader_entries_dir = ot_gfile_resolve_path_printf (self->path, "boot/loader.%d/entries", new_bootversion); - if (!gs_shutil_rm_rf (new_loader_entries_dir, cancellable, error)) + if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (new_loader_entries_dir), cancellable, error)) goto out; if (!ot_util_ensure_directory_and_fsync (new_loader_entries_dir, cancellable, error)) goto out; @@ -1855,6 +1921,9 @@ ostree_sysroot_write_deployments (OstreeSysroot *self, requires_new_bootversion ? "yes" : "no", new_deployments->len - self->deployments->len); + if (!_ostree_sysroot_bump_mtime (self, error)) + goto out; + /* Now reload from disk */ if (!ostree_sysroot_load (self, cancellable, error)) { @@ -1875,6 +1944,18 @@ ostree_sysroot_write_deployments (OstreeSysroot *self, ret = TRUE; out: + if (boot_was_ro_mount) + { + if (mount ("/boot", "/boot", NULL, MS_REMOUNT | MS_RDONLY | MS_SILENT, NULL) < 0) + { + /* Only make this a warning because we don't want to + * completely bomb out if some other process happened to + * jump in and open a file there. + */ + int errsv = errno; + g_printerr ("warning: Failed to remount /boot read-only: %s\n", strerror (errsv)); + } + } return ret; } diff --git a/src/libostree/ostree-sysroot-private.h b/src/libostree/ostree-sysroot-private.h index e0dc24fd..d210a36f 100644 --- a/src/libostree/ostree-sysroot-private.h +++ b/src/libostree/ostree-sysroot-private.h @@ -49,6 +49,7 @@ struct OstreeSysroot { int bootversion; int subbootversion; OstreeDeployment *booted_deployment; + struct timespec loaded_ts; /* Only access through ostree_sysroot_get_repo() */ OstreeRepo *repo; @@ -57,6 +58,9 @@ struct OstreeSysroot { }; #define OSTREE_SYSROOT_LOCKFILE "ostree/lock" +/* We keep some transient state in /run */ +#define _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_DIR "/run/ostree/deployment-state/" +#define _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT "unlocked-development" gboolean _ostree_sysroot_read_boot_loader_configs (OstreeSysroot *self, @@ -98,6 +102,9 @@ gboolean _ostree_sysroot_query_bootloader (OstreeSysroot *sysroot, GCancellable *cancellable, GError **error); +gboolean _ostree_sysroot_bump_mtime (OstreeSysroot *sysroot, + GError **error); + typedef enum { OSTREE_SYSROOT_CLEANUP_BOOTVERSIONS = 1 << 0, OSTREE_SYSROOT_CLEANUP_DEPLOYMENTS = 1 << 1, diff --git a/src/libostree/ostree-sysroot-upgrader.h b/src/libostree/ostree-sysroot-upgrader.h index 394e31a3..77bc8a1d 100644 --- a/src/libostree/ostree-sysroot-upgrader.h +++ b/src/libostree/ostree-sysroot-upgrader.h @@ -42,32 +42,42 @@ typedef enum { OSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED = (1 << 1), } OstreeSysrootUpgraderFlags; +_OSTREE_PUBLIC GType ostree_sysroot_upgrader_get_type (void); +_OSTREE_PUBLIC GType ostree_sysroot_upgrader_flags_get_type (void); +_OSTREE_PUBLIC OstreeSysrootUpgrader *ostree_sysroot_upgrader_new (OstreeSysroot *sysroot, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC OstreeSysrootUpgrader *ostree_sysroot_upgrader_new_for_os (OstreeSysroot *sysroot, const char *osname, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC OstreeSysrootUpgrader *ostree_sysroot_upgrader_new_for_os_with_flags (OstreeSysroot *sysroot, const char *osname, OstreeSysrootUpgraderFlags flags, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC GKeyFile *ostree_sysroot_upgrader_get_origin (OstreeSysrootUpgrader *self); +_OSTREE_PUBLIC GKeyFile *ostree_sysroot_upgrader_dup_origin (OstreeSysrootUpgrader *self); +_OSTREE_PUBLIC gboolean ostree_sysroot_upgrader_set_origin (OstreeSysrootUpgrader *self, GKeyFile *origin, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC char *ostree_sysroot_upgrader_get_origin_description (OstreeSysrootUpgrader *self); +_OSTREE_PUBLIC gboolean ostree_sysroot_upgrader_check_timestamps (OstreeRepo *repo, const char *from_rev, const char *to_rev, @@ -78,6 +88,7 @@ typedef enum { OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER = (1 << 0) } OstreeSysrootUpgraderPullFlags; +_OSTREE_PUBLIC gboolean ostree_sysroot_upgrader_pull (OstreeSysrootUpgrader *self, OstreeRepoPullFlags flags, OstreeSysrootUpgraderPullFlags upgrader_flags, @@ -86,6 +97,7 @@ gboolean ostree_sysroot_upgrader_pull (OstreeSysrootUpgrader *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_sysroot_upgrader_pull_one_dir (OstreeSysrootUpgrader *self, const char *dir_to_pull, OstreeRepoPullFlags flags, @@ -95,6 +107,7 @@ gboolean ostree_sysroot_upgrader_pull_one_dir (OstreeSysrootUpgrader *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_sysroot_upgrader_deploy (OstreeSysrootUpgrader *self, GCancellable *cancellable, GError **error); diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index 5ad2713a..6ee3dff9 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -24,6 +24,7 @@ #include "ostree-core-private.h" #include "ostree-sysroot-private.h" +#include "ostree-deployment-private.h" #include "ostree-bootloader-uboot.h" #include "ostree-bootloader-syslinux.h" #include "ostree-bootloader-grub2.h" @@ -229,6 +230,19 @@ ostree_sysroot_get_fd (OstreeSysroot *self) return self->sysroot_fd; } +gboolean +_ostree_sysroot_bump_mtime (OstreeSysroot *self, + GError **error) +{ + /* Allow other systems to monitor for changes */ + if (utimensat (self->sysroot_fd, "ostree/deploy", NULL, 0) < 0) + { + glnx_set_prefix_error_from_errno (error, "%s", "futimens"); + return FALSE; + } + return TRUE; +} + /** * ostree_sysroot_unload: * @self: Sysroot @@ -485,7 +499,8 @@ _ostree_sysroot_read_boot_loader_configs (OstreeSysroot *self, g_ptr_array_sort (ret_loader_configs, compare_loader_configs_for_sorting); done: - gs_transfer_out_value (out_loader_configs, &ret_loader_configs); + if (out_loader_configs) + *out_loader_configs = g_steal_pointer (&ret_loader_configs); ret = TRUE; out: return ret; @@ -580,7 +595,8 @@ parse_origin (OstreeSysroot *self, } ret = TRUE; - gs_transfer_out_value (out_origin, &ret_origin); + if (out_origin) + *out_origin = g_steal_pointer (&ret_origin); out: if (error) g_prefix_error (error, "Parsing %s: ", origin_path); @@ -631,6 +647,16 @@ parse_bootlink (const char *bootlink, return ret; } +static char * +get_unlocked_development_path (OstreeDeployment *deployment) +{ + return g_strdup_printf ("%s%s.%d/%s", + _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_DIR, + ostree_deployment_get_csum (deployment), + ostree_deployment_get_deployserial (deployment), + _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT); +} + static gboolean parse_deployment (OstreeSysroot *self, const char *boot_link, @@ -652,6 +678,8 @@ parse_deployment (OstreeSysroot *self, g_autofree char *treebootserial_target = NULL; g_autofree char *deploy_dir = NULL; GKeyFile *origin = NULL; + g_autofree char *unlocked_development_path = NULL; + struct stat stbuf; if (!ensure_sysroot_fd (self, error)) goto out; @@ -689,8 +717,27 @@ parse_deployment (OstreeSysroot *self, if (origin) ostree_deployment_set_origin (ret_deployment, origin); + ret_deployment->unlocked = OSTREE_DEPLOYMENT_UNLOCKED_NONE; + unlocked_development_path = get_unlocked_development_path (ret_deployment); + if (lstat (unlocked_development_path, &stbuf) == 0) + ret_deployment->unlocked = OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT; + else + { + g_autofree char *existing_unlocked_state = + g_key_file_get_string (origin, "origin", "unlocked", NULL); + + if (g_strcmp0 (existing_unlocked_state, "hotfix") == 0) + { + ret_deployment->unlocked = OSTREE_DEPLOYMENT_UNLOCKED_HOTFIX; + } + /* TODO: warn on unknown unlock types? */ + } + + g_debug ("Deployment %s.%d unlocked=%d", treecsum, deployserial, ret_deployment->unlocked); + ret = TRUE; - gs_transfer_out_value (out_deployment, &ret_deployment); + if (out_deployment) + *out_deployment = g_steal_pointer (&ret_deployment); out: if (origin) g_key_file_unref (origin); @@ -780,19 +827,24 @@ gboolean ostree_sysroot_load (OstreeSysroot *self, GCancellable *cancellable, GError **error) +{ + return ostree_sysroot_load_if_changed (self, NULL, cancellable, error); +} + +gboolean +ostree_sysroot_load_if_changed (OstreeSysroot *self, + gboolean *out_changed, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; guint i; int bootversion = 0; int subbootversion = 0; + struct stat stbuf; g_autoptr(GPtrArray) boot_loader_configs = NULL; g_autoptr(GPtrArray) deployments = NULL; - g_clear_pointer (&self->deployments, g_ptr_array_unref); - g_clear_pointer (&self->booted_deployment, g_object_unref); - self->bootversion = -1; - self->subbootversion = -1; - if (!ensure_sysroot_fd (self, error)) goto out; @@ -803,6 +855,28 @@ ostree_sysroot_load (OstreeSysroot *self, cancellable, error)) goto out; + if (fstatat (self->sysroot_fd, "ostree/deploy", &stbuf, 0) < 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + if (out_changed) + { + if (self->loaded_ts.tv_sec == stbuf.st_mtim.tv_sec && + self->loaded_ts.tv_nsec == stbuf.st_mtim.tv_nsec) + { + *out_changed = FALSE; + ret = TRUE; + goto out; + } + } + + g_clear_pointer (&self->deployments, g_ptr_array_unref); + g_clear_pointer (&self->booted_deployment, g_object_unref); + self->bootversion = -1; + self->subbootversion = -1; + if (!_ostree_sysroot_read_boot_loader_configs (self, bootversion, &boot_loader_configs, cancellable, error)) goto out; @@ -834,8 +908,11 @@ ostree_sysroot_load (OstreeSysroot *self, self->deployments = deployments; deployments = NULL; /* Transfer ownership */ self->loaded = TRUE; + self->loaded_ts = stbuf.st_mtim; ret = TRUE; + if (out_changed) + *out_changed = TRUE; out: return ret; } @@ -1005,7 +1082,8 @@ _ostree_sysroot_query_bootloader (OstreeSysroot *sysroot, g_clear_object (&ret_loader); ret = TRUE; - gs_transfer_out_value (out_bootloader, &ret_loader); + if (out_bootloader) + *out_bootloader = g_steal_pointer (&ret_loader); out: return ret; } @@ -1337,6 +1415,86 @@ ostree_sysroot_lock_finish (OstreeSysroot *self, return g_task_propagate_boolean ((GTask*)result, error); } +/** + * ostree_sysroot_init_osname: + * @self: Sysroot + * @osname: Name group of operating system checkouts + * @cancellable: Cancellable + * @error: Error + * + * Initialize the directory structure for an "osname", which is a + * group of operating system deployments, with a shared `/var`. One + * is required for generating a deployment. + */ +gboolean +ostree_sysroot_init_osname (OstreeSysroot *self, + const char *osname, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + const char *deploydir = glnx_strjoina ("ostree/deploy/", osname); + glnx_fd_close int dfd = -1; + + if (!ensure_sysroot_fd (self, error)) + goto out; + + if (mkdirat (self->sysroot_fd, deploydir, 0777) < 0) + { + glnx_set_prefix_error_from_errno (error, "Creating %s", deploydir); + goto out; + } + + if (!glnx_opendirat (self->sysroot_fd, deploydir, TRUE, &dfd, error)) + goto out; + + if (mkdirat (dfd, "var", 0777) < 0) + { + glnx_set_prefix_error_from_errno (error, "Creating %s", "var"); + goto out; + } + + /* This is a bit of a legacy hack...but we have to keep it around + * now. We're ensuring core subdirectories of /var exist. + */ + if (mkdirat (dfd, "var/tmp", 0777) < 0) + { + glnx_set_prefix_error_from_errno (error, "Creating %s", "var/tmp"); + goto out; + } + + if (fchmodat (dfd, "var/tmp", 01777, 0) < 0) + { + glnx_set_prefix_error_from_errno (error, "Fchmod %s", "var/tmp"); + goto out; + } + + if (mkdirat (dfd, "var/lib", 0777) < 0) + { + glnx_set_prefix_error_from_errno (error, "Creating %s", "var/tmp"); + goto out; + } + + if (symlinkat ("../run", dfd, "var/run") < 0) + { + glnx_set_prefix_error_from_errno (error, "Symlinking %s", "var/run"); + goto out; + } + + if (symlinkat ("../run/lock", dfd, "var/lock") < 0) + { + glnx_set_prefix_error_from_errno (error, "Symlinking %s", "var/lock"); + goto out; + } + + if (!_ostree_sysroot_bump_mtime (self, error)) + goto out; + + ret = TRUE; + out: + return ret; +} + /** * ostree_sysroot_simple_write_deployment: * @sysroot: Sysroot @@ -1354,6 +1512,10 @@ ostree_sysroot_lock_finish (OstreeSysroot *self, * * If %OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN is * specified, then all current deployments will be kept. + * + * If %OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NOT_DEFAULT is + * specified, then instead of prepending, the new deployment will be + * added right after the booted or merge deployment, instead of first. */ gboolean ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot, @@ -1370,6 +1532,8 @@ ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot, g_autoptr(GPtrArray) deployments = NULL; g_autoptr(GPtrArray) new_deployments = g_ptr_array_new_with_free_func (g_object_unref); gboolean retain = (flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN) > 0; + const gboolean make_default = !((flags & OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NOT_DEFAULT) > 0); + gboolean added_new = FALSE; deployments = ostree_sysroot_get_deployments (sysroot); booted_deployment = ostree_sysroot_get_booted_deployment (sysroot); @@ -1377,23 +1541,44 @@ ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot, if (osname == NULL && booted_deployment) osname = ostree_deployment_get_osname (booted_deployment); - g_ptr_array_add (new_deployments, g_object_ref (new_deployment)); + if (make_default) + { + g_ptr_array_add (new_deployments, g_object_ref (new_deployment)); + added_new = TRUE; + } for (i = 0; i < deployments->len; i++) { OstreeDeployment *deployment = deployments->pdata[i]; + const gboolean is_merge_or_booted = + ostree_deployment_equal (deployment, booted_deployment) || + ostree_deployment_equal (deployment, merge_deployment); /* Keep deployments with different osnames, as well as the * booted and merge deployments */ if (retain || - (osname != NULL && - strcmp (ostree_deployment_get_osname (deployment), osname) != 0) || - ostree_deployment_equal (deployment, booted_deployment) || - ostree_deployment_equal (deployment, merge_deployment)) + (osname != NULL && strcmp (ostree_deployment_get_osname (deployment), osname) != 0) || + is_merge_or_booted) { g_ptr_array_add (new_deployments, g_object_ref (deployment)); } + + if (!added_new) + { + g_ptr_array_add (new_deployments, g_object_ref (new_deployment)); + added_new = TRUE; + } + } + + /* In this non-default case , an improvement in the future would be + * to put the new deployment right after the current default in the + * order. + */ + if (!added_new) + { + g_ptr_array_add (new_deployments, g_object_ref (new_deployment)); + added_new = TRUE; } if (!ostree_sysroot_write_deployments (sysroot, new_deployments, cancellable, error)) @@ -1406,3 +1591,263 @@ ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot, out: return ret; } + +static gboolean +clone_deployment (OstreeSysroot *sysroot, + OstreeDeployment *target_deployment, + OstreeDeployment *merge_deployment, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + __attribute__((cleanup(_ostree_kernel_args_cleanup))) OstreeKernelArgs *kargs = NULL; + glnx_unref_object OstreeDeployment *new_deployment = NULL; + + /* Ensure we have a clean slate */ + if (!ostree_sysroot_prepare_cleanup (sysroot, cancellable, error)) + { + g_prefix_error (error, "Performing initial cleanup: "); + goto out; + } + + kargs = _ostree_kernel_args_new (); + + { OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (merge_deployment); + g_auto(GStrv) previous_args = g_strsplit (ostree_bootconfig_parser_get (bootconfig, "options"), " ", -1); + + _ostree_kernel_args_append_argv (kargs, previous_args); + } + + { + g_auto(GStrv) kargs_strv = _ostree_kernel_args_to_strv (kargs); + + if (!ostree_sysroot_deploy_tree (sysroot, + ostree_deployment_get_osname (target_deployment), + ostree_deployment_get_csum (target_deployment), + ostree_deployment_get_origin (target_deployment), + merge_deployment, + kargs_strv, + &new_deployment, + cancellable, error)) + goto out; + } + + /* Hotfixes push the deployment as rollback target, so it shouldn't + * be the default. + */ + if (!ostree_sysroot_simple_write_deployment (sysroot, ostree_deployment_get_osname (target_deployment), + new_deployment, merge_deployment, + OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NOT_DEFAULT, + cancellable, error)) + goto out; + + ret = TRUE; + out: + return ret; +} + +/** + * ostree_sysroot_deployment_unlock: + * @self: Sysroot + * @deployment: Deployment + * @unlocked_state: Transition to this unlocked state + * @cancellable: Cancellable + * @error: Error + * + * Configure the target deployment @deployment such that it + * is writable. There are multiple modes, essentially differing + * in whether or not any changes persist across reboot. + * + * The `OSTREE_DEPLOYMENT_UNLOCKED_HOTFIX` state is persistent + * across reboots. + */ +gboolean +ostree_sysroot_deployment_unlock (OstreeSysroot *self, + OstreeDeployment *deployment, + OstreeDeploymentUnlockedState unlocked_state, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + OstreeDeploymentUnlockedState current_unlocked = + ostree_deployment_get_unlocked (deployment); + glnx_unref_object OstreeDeployment *deployment_clone = + ostree_deployment_clone (deployment); + glnx_unref_object OstreeDeployment *merge_deployment = NULL; + GKeyFile *origin_clone = ostree_deployment_get_origin (deployment_clone); + const char hotfix_ovl_options[] = "lowerdir=usr,upperdir=.usr-ovl-upper,workdir=.usr-ovl-work"; + const char *ovl_options = NULL; + g_autofree char *deployment_path = NULL; + glnx_fd_close int deployment_dfd = -1; + pid_t mount_child; + + /* This function cannot re-lock */ + g_return_val_if_fail (unlocked_state != OSTREE_DEPLOYMENT_UNLOCKED_NONE, FALSE); + + if (current_unlocked != OSTREE_DEPLOYMENT_UNLOCKED_NONE) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Deployment is already in unlocked state: %s", + ostree_deployment_unlocked_state_to_string (current_unlocked)); + goto out; + } + + merge_deployment = ostree_sysroot_get_merge_deployment (self, ostree_deployment_get_osname (deployment)); + if (!merge_deployment) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No previous deployment to duplicate"); + goto out; + } + + /* For hotfixes, we push a rollback target */ + if (unlocked_state == OSTREE_DEPLOYMENT_UNLOCKED_HOTFIX) + { + if (!clone_deployment (self, deployment, merge_deployment, cancellable, error)) + goto out; + } + + /* Crack it open */ + if (!ostree_sysroot_deployment_set_mutable (self, deployment, TRUE, + cancellable, error)) + goto out; + + deployment_path = ostree_sysroot_get_deployment_dirpath (self, deployment); + + if (!glnx_opendirat (self->sysroot_fd, deployment_path, TRUE, &deployment_dfd, error)) + goto out; + + switch (unlocked_state) + { + case OSTREE_DEPLOYMENT_UNLOCKED_NONE: + g_assert_not_reached (); + break; + case OSTREE_DEPLOYMENT_UNLOCKED_HOTFIX: + { + /* Create the overlayfs directories in the deployment root + * directly for hotfixes. The ostree-prepare-root.c helper + * is also set up to detect and mount these. + */ + if (!glnx_shutil_mkdir_p_at (deployment_dfd, ".usr-ovl-upper", 0755, cancellable, error)) + goto out; + if (!glnx_shutil_mkdir_p_at (deployment_dfd, ".usr-ovl-work", 0755, cancellable, error)) + goto out; + ovl_options = hotfix_ovl_options; + } + break; + case OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT: + { + /* We're just doing transient development/hacking? Okay, + * stick the overlayfs bits in /var/tmp. + */ + char *development_ovldir = strdupa ("/var/tmp/ostree-unlock-ovl.XXXXXX"); + const char *development_ovl_upper; + const char *development_ovl_work; + + if (!glnx_mkdtempat (AT_FDCWD, development_ovldir, 0700, error)) + goto out; + + development_ovl_upper = glnx_strjoina (development_ovldir, "/upper"); + if (!glnx_shutil_mkdir_p_at (AT_FDCWD, development_ovl_upper, 0755, cancellable, error)) + goto out; + development_ovl_work = glnx_strjoina (development_ovldir, "/work"); + if (!glnx_shutil_mkdir_p_at (AT_FDCWD, development_ovl_work, 0755, cancellable, error)) + goto out; + ovl_options = glnx_strjoina ("lowerdir=usr,upperdir=", development_ovl_upper, + ",workdir=", development_ovl_work); + } + } + + g_assert (ovl_options != NULL); + + /* Here we run `mount()` in a fork()ed child because we need to use + * `chdir()` in order to have the mount path options to overlayfs not + * look ugly. + * + * We can't `chdir()` inside a shared library since there may be + * threads, etc. + */ + { + /* Make a copy of the fd that's *not* FD_CLOEXEC so that we pass + * it to the child. + */ + glnx_fd_close int child_deployment_dfd = dup (deployment_dfd); + + if (child_deployment_dfd < 0) + { + glnx_set_error_from_errno (error); + goto out; + } + + mount_child = fork (); + if (mount_child < 0) + { + glnx_set_prefix_error_from_errno (error, "%s", "fork"); + goto out; + } + else if (mount_child == 0) + { + /* Child process. Do NOT use any GLib API here. */ + if (fchdir (child_deployment_dfd) < 0) + exit (EXIT_FAILURE); + (void) close (child_deployment_dfd); + if (mount ("overlay", "/usr", "overlay", 0, ovl_options) < 0) + exit (EXIT_FAILURE); + exit (EXIT_SUCCESS); + } + else + { + /* Parent */ + int estatus; + + if (TEMP_FAILURE_RETRY (waitpid (mount_child, &estatus, 0)) < 0) + { + glnx_set_prefix_error_from_errno (error, "%s", "waitpid() on mount helper"); + goto out; + } + if (!g_spawn_check_exit_status (estatus, error)) + { + g_prefix_error (error, "overlayfs mount helper: "); + goto out; + } + } + } + + /* Now, write out the flag saying what we did */ + switch (unlocked_state) + { + case OSTREE_DEPLOYMENT_UNLOCKED_NONE: + g_assert_not_reached (); + break; + case OSTREE_DEPLOYMENT_UNLOCKED_HOTFIX: + g_key_file_set_string (origin_clone, "origin", "unlocked", + ostree_deployment_unlocked_state_to_string (unlocked_state)); + if (!ostree_sysroot_write_origin_file (self, deployment, origin_clone, + cancellable, error)) + goto out; + break; + case OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT: + { + g_autofree char *devpath = get_unlocked_development_path (deployment); + g_autofree char *devpath_parent = dirname (g_strdup (devpath)); + + if (!glnx_shutil_mkdir_p_at (AT_FDCWD, devpath_parent, 0755, cancellable, error)) + goto out; + + if (!g_file_set_contents (devpath, "", 0, error)) + goto out; + } + } + + /* For hotfixes we already pushed a rollback which will bump the + * mtime, but we need to bump it again so that clients get the state + * change for this deployment. For development we need to do this + * regardless. + */ + if (!_ostree_sysroot_bump_mtime (self, error)) + goto out; + + ret = TRUE; + out: + return ret; +} + diff --git a/src/libostree/ostree-sysroot.h b/src/libostree/ostree-sysroot.h index bd3ecea1..1e60ddbe 100644 --- a/src/libostree/ostree-sysroot.h +++ b/src/libostree/ostree-sysroot.h @@ -31,82 +31,121 @@ G_BEGIN_DECLS #define OSTREE_IS_SYSROOT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_SYSROOT)) +_OSTREE_PUBLIC GType ostree_sysroot_get_type (void); +_OSTREE_PUBLIC OstreeSysroot* ostree_sysroot_new (GFile *path); +_OSTREE_PUBLIC OstreeSysroot* ostree_sysroot_new_default (void); +_OSTREE_PUBLIC GFile *ostree_sysroot_get_path (OstreeSysroot *self); +_OSTREE_PUBLIC int ostree_sysroot_get_fd (OstreeSysroot *self); +_OSTREE_PUBLIC gboolean ostree_sysroot_load (OstreeSysroot *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC +gboolean ostree_sysroot_load_if_changed (OstreeSysroot *self, + gboolean *out_changed, + GCancellable *cancellable, + GError **error); + +_OSTREE_PUBLIC void ostree_sysroot_unload (OstreeSysroot *self); +_OSTREE_PUBLIC gboolean ostree_sysroot_ensure_initialized (OstreeSysroot *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC int ostree_sysroot_get_bootversion (OstreeSysroot *self); +_OSTREE_PUBLIC int ostree_sysroot_get_subbootversion (OstreeSysroot *self); +_OSTREE_PUBLIC GPtrArray *ostree_sysroot_get_deployments (OstreeSysroot *self); +_OSTREE_PUBLIC OstreeDeployment *ostree_sysroot_get_booted_deployment (OstreeSysroot *self); +_OSTREE_PUBLIC GFile *ostree_sysroot_get_deployment_directory (OstreeSysroot *self, OstreeDeployment *deployment); +_OSTREE_PUBLIC char *ostree_sysroot_get_deployment_dirpath (OstreeSysroot *self, OstreeDeployment *deployment); +_OSTREE_PUBLIC GFile * ostree_sysroot_get_deployment_origin_path (GFile *deployment_path); +_OSTREE_PUBLIC gboolean ostree_sysroot_lock (OstreeSysroot *self, GError **error); +_OSTREE_PUBLIC gboolean ostree_sysroot_try_lock (OstreeSysroot *self, gboolean *out_acquired, GError **error); +_OSTREE_PUBLIC void ostree_sysroot_lock_async (OstreeSysroot *self, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); +_OSTREE_PUBLIC gboolean ostree_sysroot_lock_finish (OstreeSysroot *self, GAsyncResult *result, GError **error); +_OSTREE_PUBLIC void ostree_sysroot_unlock (OstreeSysroot *self); +_OSTREE_PUBLIC +gboolean ostree_sysroot_init_osname (OstreeSysroot *self, + const char *osname, + GCancellable *cancellable, + GError **error); + +_OSTREE_PUBLIC gboolean ostree_sysroot_cleanup (OstreeSysroot *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_sysroot_prepare_cleanup (OstreeSysroot *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_sysroot_write_origin_file (OstreeSysroot *sysroot, OstreeDeployment *deployment, GKeyFile *new_origin, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_sysroot_get_repo (OstreeSysroot *self, OstreeRepo **out_repo, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_sysroot_deployment_set_kargs (OstreeSysroot *self, OstreeDeployment *deployment, char **new_kargs, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_sysroot_write_deployments (OstreeSysroot *self, GPtrArray *new_deployments, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_sysroot_deploy_tree (OstreeSysroot *self, const char *osname, const char *revision, @@ -117,24 +156,36 @@ gboolean ostree_sysroot_deploy_tree (OstreeSysroot *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC gboolean ostree_sysroot_deployment_set_mutable (OstreeSysroot *self, OstreeDeployment *deployment, gboolean is_mutable, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC +gboolean ostree_sysroot_deployment_unlock (OstreeSysroot *self, + OstreeDeployment *deployment, + OstreeDeploymentUnlockedState unlocked_state, + GCancellable *cancellable, + GError **error); + +_OSTREE_PUBLIC OstreeDeployment *ostree_sysroot_get_merge_deployment (OstreeSysroot *self, const char *osname); +_OSTREE_PUBLIC GKeyFile *ostree_sysroot_origin_new_from_refspec (OstreeSysroot *self, const char *refspec); typedef enum { OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NONE = 0, - OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN = (1 << 0) + OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN = (1 << 0), + OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NOT_DEFAULT = (1 << 1) } OstreeSysrootSimpleWriteDeploymentFlags; +_OSTREE_PUBLIC gboolean ostree_sysroot_simple_write_deployment (OstreeSysroot *sysroot, const char *osname, OstreeDeployment *new_deployment, diff --git a/src/libostree/ostree-types.h b/src/libostree/ostree-types.h index 691f1289..f6aea0f0 100644 --- a/src/libostree/ostree-types.h +++ b/src/libostree/ostree-types.h @@ -24,6 +24,10 @@ #include +#ifndef _OSTREE_PUBLIC +#define _OSTREE_PUBLIC extern +#endif + G_BEGIN_DECLS typedef struct OstreeRepo OstreeRepo; diff --git a/src/libotutil/ot-fs-utils.c b/src/libotutil/ot-fs-utils.c index b709befe..4d45cd06 100644 --- a/src/libotutil/ot-fs-utils.c +++ b/src/libotutil/ot-fs-utils.c @@ -45,7 +45,7 @@ ot_gopendirat (int dfd, int ret = ot_opendirat (dfd, path, follow); if (ret == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); return FALSE; } *out_fd = ret; @@ -71,7 +71,7 @@ ot_lgetxattrat (int dfd, while (G_UNLIKELY (bytes_read < 0 && errno == EINTR)); if (G_UNLIKELY (bytes_read < 0)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -81,7 +81,7 @@ ot_lgetxattrat (int dfd, while (G_UNLIKELY (real_size < 0 && errno == EINTR)); if (G_UNLIKELY (real_size < 0)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); g_free (buf); goto out; } @@ -111,7 +111,7 @@ ot_lsetxattrat (int dfd, while (G_UNLIKELY (res == -1 && errno == EINTR)); if (G_UNLIKELY (res == -1)) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); return FALSE; } @@ -134,7 +134,7 @@ ot_readlinkat_gfile_info (int dfd, while (G_UNLIKELY (len == -1 && errno == EINTR)); if (len == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } targetbuf[len] = '\0'; @@ -180,7 +180,7 @@ ot_openat_read_stream (int dfd, while (G_UNLIKELY (fd == -1 && errno == EINTR)); if (fd == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -205,3 +205,28 @@ ot_ensure_unlinked_at (int dfd, } return TRUE; } + +gboolean +ot_openat_ignore_enoent (int dfd, + const char *path, + int *out_fd, + GError **error) +{ + gboolean ret = FALSE; + int target_fd = -1; + + target_fd = openat (dfd, path, O_CLOEXEC | O_RDONLY); + if (target_fd < 0) + { + if (errno != ENOENT) + { + glnx_set_error_from_errno (error); + goto out; + } + } + + ret = TRUE; + *out_fd = target_fd; + out: + return ret; +} diff --git a/src/libotutil/ot-fs-utils.h b/src/libotutil/ot-fs-utils.h index 10686be6..cfeea74d 100644 --- a/src/libotutil/ot-fs-utils.h +++ b/src/libotutil/ot-fs-utils.h @@ -61,4 +61,9 @@ gboolean ot_ensure_unlinked_at (int dfd, const char *path, GError **error); +gboolean ot_openat_ignore_enoent (int dfd, + const char *path, + int *out_fd, + GError **error); + G_END_DECLS diff --git a/src/libotutil/ot-gio-utils.c b/src/libotutil/ot-gio-utils.c index b10c04c5..9b8b0dce 100644 --- a/src/libotutil/ot-gio-utils.c +++ b/src/libotutil/ot-gio-utils.c @@ -309,7 +309,9 @@ ot_file_replace_contents_at (int dfd, int r = posix_fallocate (fd, 0, g_bytes_get_size (contents)); if (r != 0) { - gs_set_error_from_errno (error, r); + /* posix_fallocate is a weird deviation from errno standards */ + errno = r; + glnx_set_error_from_errno (error); goto out; } } @@ -320,7 +322,7 @@ ot_file_replace_contents_at (int dfd, if (datasync && fdatasync (fd) != 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -329,7 +331,7 @@ ot_file_replace_contents_at (int dfd, if (renameat (dfd, tmpname, dfd, path) == -1) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -360,8 +362,8 @@ ot_gfile_replace_contents_fsync (GFile *path, parent = g_file_get_parent (path); - if (!gs_file_open_dir_fd (parent, &parent_dfd, - cancellable, error)) + if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (parent), TRUE, + &parent_dfd, error)) goto out; if (!ot_file_replace_contents_at (parent_dfd, target_basename, @@ -424,12 +426,13 @@ ot_util_fsync_directory (GFile *dir, gboolean ret = FALSE; int dfd = -1; - if (!gs_file_open_dir_fd (dir, &dfd, cancellable, error)) + if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (dir), TRUE, + &dfd, error)) goto out; if (fsync (dfd) != 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } diff --git a/src/libotutil/ot-variant-utils.c b/src/libotutil/ot-variant-utils.c index ed650268..0305c34d 100644 --- a/src/libotutil/ot-variant-utils.c +++ b/src/libotutil/ot-variant-utils.c @@ -204,7 +204,7 @@ ot_util_variant_map_fd (int fd, if (fstat (fd, &stbuf) != 0) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } @@ -212,7 +212,7 @@ ot_util_variant_map_fd (int fd, map = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, start); if (!map) { - gs_set_error_from_errno (error, errno); + glnx_set_error_from_errno (error); goto out; } diff --git a/src/ostree/ot-admin-builtin-deploy.c b/src/ostree/ot-admin-builtin-deploy.c index 51de4ee3..c66c9b3f 100644 --- a/src/ostree/ot-admin-builtin-deploy.c +++ b/src/ostree/ot-admin-builtin-deploy.c @@ -113,8 +113,8 @@ ot_admin_builtin_deploy (int argc, char **argv, GCancellable *cancellable, GErro merge_deployment = ostree_sysroot_get_merge_deployment (sysroot, opt_osname); /* Here we perform cleanup of any leftover data from previous - * partial failures. This avoids having to call gs_shutil_rm_rf() - * at random points throughout the process. + * partial failures. This avoids having to call + * glnx_shutil_rm_rf_at() at random points throughout the process. * * TODO: Add /ostree/transaction file, and only do this cleanup if * we find it. diff --git a/src/ostree/ot-admin-builtin-os-init.c b/src/ostree/ot-admin-builtin-os-init.c index f5b5f64b..fbb04949 100644 --- a/src/ostree/ot-admin-builtin-os-init.c +++ b/src/ostree/ot-admin-builtin-os-init.c @@ -40,8 +40,6 @@ ot_admin_builtin_os_init (int argc, char **argv, GCancellable *cancellable, GErr glnx_unref_object OstreeSysroot *sysroot = NULL; gboolean ret = FALSE; const char *osname = NULL; - g_autoptr(GFile) deploy_dir = NULL; - g_autoptr(GFile) dir = NULL; context = g_option_context_new ("OSNAME - Initialize empty state for given operating system"); @@ -61,48 +59,10 @@ ot_admin_builtin_os_init (int argc, char **argv, GCancellable *cancellable, GErr osname = argv[1]; - deploy_dir = ot_gfile_get_child_build_path (ostree_sysroot_get_path (sysroot), "ostree", "deploy", osname, NULL); - - /* Ensure core subdirectories of /var exist, since we need them for - * dracut generation, and the host will want them too. - */ - g_clear_object (&dir); - dir = ot_gfile_get_child_build_path (deploy_dir, "var", "tmp", NULL); - if (!gs_file_ensure_directory (dir, TRUE, cancellable, error)) - goto out; - if (chmod (gs_file_get_path_cached (dir), 01777) < 0) - { - gs_set_error_from_errno (error, errno); - goto out; - } - - g_clear_object (&dir); - dir = ot_gfile_get_child_build_path (deploy_dir, "var", "lib", NULL); - if (!gs_file_ensure_directory (dir, TRUE, cancellable, error)) + if (!ostree_sysroot_init_osname (sysroot, osname, cancellable, error)) goto out; - g_clear_object (&dir); - dir = ot_gfile_get_child_build_path (deploy_dir, "var", "run", NULL); - if (!g_file_test (gs_file_get_path_cached (dir), G_FILE_TEST_IS_SYMLINK)) - { - if (symlink ("../run", gs_file_get_path_cached (dir)) < 0) - { - gs_set_error_from_errno (error, errno); - goto out; - } - } - - dir = ot_gfile_get_child_build_path (deploy_dir, "var", "lock", NULL); - if (!g_file_test (gs_file_get_path_cached (dir), G_FILE_TEST_IS_SYMLINK)) - { - if (symlink ("../run/lock", gs_file_get_path_cached (dir)) < 0) - { - gs_set_error_from_errno (error, errno); - goto out; - } - } - - g_print ("%s initialized as OSTree root\n", gs_file_get_path_cached (deploy_dir)); + g_print ("ostree/deploy/%s initialized as OSTree root\n", osname); ret = TRUE; out: diff --git a/src/ostree/ot-admin-builtin-status.c b/src/ostree/ot-admin-builtin-status.c index cd275cc6..df4d0745 100644 --- a/src/ostree/ot-admin-builtin-status.c +++ b/src/ostree/ot-admin-builtin-status.c @@ -89,6 +89,9 @@ ot_admin_builtin_status (int argc, char **argv, GCancellable *cancellable, GErro glnx_unref_object OstreeRepo *repo = NULL; OstreeDeployment *booted_deployment = NULL; g_autoptr(GPtrArray) deployments = NULL; + const int is_tty = isatty (1); + const char *red_bold_prefix = is_tty ? "\x1b[31m\x1b[1m" : ""; + const char *red_bold_suffix = is_tty ? "\x1b[22m\x1b[0m" : ""; guint i; context = g_option_context_new ("List deployments"); @@ -118,12 +121,15 @@ ot_admin_builtin_status (int argc, char **argv, GCancellable *cancellable, GErro OstreeDeployment *deployment = deployments->pdata[i]; GKeyFile *origin; const char *ref = ostree_deployment_get_csum (deployment); + OstreeDeploymentUnlockedState unlocked = ostree_deployment_get_unlocked (deployment); g_autofree char *version = version_of_commit (repo, ref); glnx_unref_object OstreeGpgVerifyResult *result = NULL; GString *output_buffer; guint jj, n_signatures; GError *local_error = NULL; + origin = ostree_deployment_get_origin (deployment); + g_print ("%c %s %s.%d\n", deployment == booted_deployment ? '*' : ' ', ostree_deployment_get_osname (deployment), @@ -131,7 +137,15 @@ ot_admin_builtin_status (int argc, char **argv, GCancellable *cancellable, GErro ostree_deployment_get_deployserial (deployment)); if (version) g_print (" Version: %s\n", version); - origin = ostree_deployment_get_origin (deployment); + switch (unlocked) + { + case OSTREE_DEPLOYMENT_UNLOCKED_NONE: + break; + default: + g_print (" %sUnlocked: %s%s\n", red_bold_prefix, + ostree_deployment_unlocked_state_to_string (unlocked), + red_bold_suffix); + } if (!origin) g_print (" origin: none\n"); else diff --git a/src/ostree/ot-admin-builtin-switch.c b/src/ostree/ot-admin-builtin-switch.c index 5339de9d..0e0101a9 100644 --- a/src/ostree/ot-admin-builtin-switch.c +++ b/src/ostree/ot-admin-builtin-switch.c @@ -34,6 +34,7 @@ static gboolean opt_reboot; static char *opt_osname; static GOptionEntry options[] = { + { "reboot", 'r', 0, G_OPTION_ARG_NONE, &opt_reboot, "Reboot after switching trees", NULL }, { "os", 0, 0, G_OPTION_ARG_STRING, &opt_osname, "Use a different operating system root than the current one", "OSNAME" }, { NULL } }; @@ -162,16 +163,11 @@ ot_admin_builtin_switch (int argc, char **argv, GCancellable *cancellable, GErro if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error)) goto out; - { - g_autoptr(GFile) real_sysroot = g_file_new_for_path ("/"); - - if (opt_reboot && g_file_equal (ostree_sysroot_get_path (sysroot), real_sysroot)) - { - gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT, - cancellable, error, - "systemctl", "reboot", NULL); - } - } + if (opt_reboot) + { + if (!ot_admin_execve_reboot (sysroot, error)) + goto out; + } ret = TRUE; out: diff --git a/src/ostree/ot-admin-builtin-unlock.c b/src/ostree/ot-admin-builtin-unlock.c new file mode 100644 index 00000000..9d265325 --- /dev/null +++ b/src/ostree/ot-admin-builtin-unlock.c @@ -0,0 +1,104 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2016 Colin Walters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "ot-main.h" +#include "ot-admin-builtins.h" +#include "ot-admin-functions.h" +#include "ostree.h" +#include "otutil.h" + +#include "../libostree/ostree-kernel-args.h" + +#include +#include + +static gboolean opt_hotfix; + +static GOptionEntry options[] = { + { "hotfix", 0, 0, G_OPTION_ARG_NONE, &opt_hotfix, "Keep the current deployment as default", NULL }, + { NULL } +}; + +gboolean +ot_admin_builtin_unlock (int argc, char **argv, GCancellable *cancellable, GError **error) +{ + gboolean ret = FALSE; + GOptionContext *context; + glnx_unref_object OstreeSysroot *sysroot = NULL; + glnx_unref_object OstreeRepo *repo = NULL; + g_autoptr(GPtrArray) new_deployments = NULL; + glnx_unref_object OstreeDeployment *merge_deployment = NULL; + OstreeDeployment *booted_deployment = NULL; + OstreeDeploymentUnlockedState target_state; + + context = g_option_context_new ("Make the current deployment mutable (as a hotfix or development)"); + + if (!ostree_admin_option_context_parse (context, options, &argc, &argv, + OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER, + &sysroot, cancellable, error)) + goto out; + + if (argc > 1) + { + ot_util_usage_error (context, "This command takes no extra arguments", error); + goto out; + } + + if (!ostree_sysroot_load (sysroot, cancellable, error)) + goto out; + + booted_deployment = ostree_sysroot_get_booted_deployment (sysroot); + if (!booted_deployment) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Not currently booted into an OSTree system"); + goto out; + } + + target_state = opt_hotfix ? OSTREE_DEPLOYMENT_UNLOCKED_HOTFIX : OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT; + + if (!ostree_sysroot_deployment_unlock (sysroot, booted_deployment, + target_state, cancellable, error)) + goto out; + + switch (target_state) + { + case OSTREE_DEPLOYMENT_UNLOCKED_NONE: + g_assert_not_reached (); + break; + case OSTREE_DEPLOYMENT_UNLOCKED_HOTFIX: + g_print ("Hotfix mode enabled. A writable overlayfs is now mounted on /usr\n" + "for this booted deployment. A non-hotfixed clone has been created\n" + "as the non-default rollback target.\n"); + break; + case OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT: + g_print ("Development mode enabled. A writable overlayfs is now mounted on /usr.\n" + "All changes there will be discarded on reboot.\n"); + break; + } + + ret = TRUE; + out: + if (context) + g_option_context_free (context); + return ret; +} diff --git a/src/ostree/ot-admin-builtin-upgrade.c b/src/ostree/ot-admin-builtin-upgrade.c index 90d0ce98..b3b531c0 100644 --- a/src/ostree/ot-admin-builtin-upgrade.c +++ b/src/ostree/ot-admin-builtin-upgrade.c @@ -97,6 +97,9 @@ ot_admin_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GErr "override-commit", NULL); } + /* Should we consider requiring --discard-hotfix here? */ + origin_changed |= g_key_file_remove_key (origin, "origin", "unlocked", NULL); + if (origin_changed) { /* XXX GCancellable parameter is not used. */ @@ -133,16 +136,13 @@ ot_admin_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GErr } else { - g_autoptr(GFile) real_sysroot = g_file_new_for_path ("/"); - if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, error)) goto out; - if (opt_reboot && g_file_equal (ostree_sysroot_get_path (sysroot), real_sysroot)) + if (opt_reboot) { - gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT, - cancellable, error, - "systemctl", "reboot", NULL); + if (!ot_admin_execve_reboot (sysroot, error)) + goto out; } } diff --git a/src/ostree/ot-admin-builtins.h b/src/ostree/ot-admin-builtins.h index 1a3c1264..f9425119 100644 --- a/src/ostree/ot-admin-builtins.h +++ b/src/ostree/ot-admin-builtins.h @@ -34,6 +34,7 @@ gboolean ot_admin_builtin_init_fs (int argc, char **argv, GCancellable *cancella gboolean ot_admin_builtin_undeploy (int argc, char **argv, GCancellable *cancellable, GError **error); gboolean ot_admin_builtin_deploy (int argc, char **argv, GCancellable *cancellable, GError **error); gboolean ot_admin_builtin_cleanup (int argc, char **argv, GCancellable *cancellable, GError **error); +gboolean ot_admin_builtin_unlock (int argc, char **argv, GCancellable *cancellable, GError **error); gboolean ot_admin_builtin_status (int argc, char **argv, GCancellable *cancellable, GError **error); gboolean ot_admin_builtin_set_origin (int argc, char **argv, GCancellable *cancellable, GError **error); gboolean ot_admin_builtin_diff (int argc, char **argv, GCancellable *cancellable, GError **error); diff --git a/src/ostree/ot-admin-functions.c b/src/ostree/ot-admin-functions.c index c818a00c..bc9034e1 100644 --- a/src/ostree/ot-admin-functions.c +++ b/src/ostree/ot-admin-functions.c @@ -155,3 +155,20 @@ ot_admin_sysroot_lock (OstreeSysroot *sysroot, g_main_context_unref (state.mainctx); return ret; } + +gboolean +ot_admin_execve_reboot (OstreeSysroot *sysroot, GError **error) +{ + g_autoptr(GFile) real_sysroot = g_file_new_for_path ("/"); + + if (g_file_equal (ostree_sysroot_get_path (sysroot), real_sysroot)) + { + if (execl ("systemctl", "systemctl", "reboot", NULL) < 0) + { + glnx_set_error_from_errno (error); + return FALSE; + } + } + + return TRUE; +} diff --git a/src/ostree/ot-admin-functions.h b/src/ostree/ot-admin-functions.h index 67164ca5..8c0cf8ee 100644 --- a/src/ostree/ot-admin-functions.h +++ b/src/ostree/ot-admin-functions.h @@ -45,5 +45,8 @@ gboolean ot_admin_sysroot_lock (OstreeSysroot *sysroot, GError **error); +gboolean +ot_admin_execve_reboot (OstreeSysroot *sysroot, + GError **error); G_END_DECLS diff --git a/src/ostree/ot-builtin-admin.c b/src/ostree/ot-builtin-admin.c index a4cb0dde..8b866170 100644 --- a/src/ostree/ot-builtin-admin.c +++ b/src/ostree/ot-builtin-admin.c @@ -47,6 +47,7 @@ static OstreeAdminCommand admin_subcommands[] = { { "status", ot_admin_builtin_status }, { "switch", ot_admin_builtin_switch }, { "undeploy", ot_admin_builtin_undeploy }, + { "unlock", ot_admin_builtin_unlock }, { "upgrade", ot_admin_builtin_upgrade }, { NULL, NULL } }; diff --git a/src/ostree/ot-builtin-refs.c b/src/ostree/ot-builtin-refs.c index 575f6646..af90c841 100644 --- a/src/ostree/ot-builtin-refs.c +++ b/src/ostree/ot-builtin-refs.c @@ -27,41 +27,28 @@ #include "ostree.h" static gboolean opt_delete; +static gboolean opt_list; static GOptionEntry options[] = { { "delete", 0, 0, G_OPTION_ARG_NONE, &opt_delete, "Delete refs which match PREFIX, rather than listing them", NULL }, + { "list", 0, 0, G_OPTION_ARG_NONE, &opt_list, "Do not remove the prefix from the refs", NULL }, { NULL } }; -gboolean -ostree_builtin_refs (int argc, char **argv, GCancellable *cancellable, GError **error) +static gboolean do_ref (OstreeRepo *repo, const char *refspec_prefix, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; - GOptionContext *context; - glnx_unref_object OstreeRepo *repo = NULL; - const char *refspec_prefix = NULL; g_autoptr(GHashTable) refs = NULL; GHashTableIter hashiter; gpointer hashkey, hashvalue; + gboolean ret = FALSE; - context = g_option_context_new ("[PREFIX] - List refs"); - - if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) - goto out; - - if (argc >= 2) - refspec_prefix = argv[1]; - - /* Require a prefix when deleting to help avoid accidents. */ - if (opt_delete && refspec_prefix == NULL) + if (opt_delete || opt_list) { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "PREFIX is required when deleting refs"); - goto out; + if (!ostree_repo_list_refs_ext (repo, refspec_prefix, &refs, OSTREE_REPO_LIST_REFS_EXT_NONE, + cancellable, error)) + goto out; } - - if (!ostree_repo_list_refs (repo, refspec_prefix, &refs, - cancellable, error)) + else if (!ostree_repo_list_refs (repo, refspec_prefix, &refs, cancellable, error)) goto out; if (!opt_delete) @@ -90,6 +77,41 @@ ostree_builtin_refs (int argc, char **argv, GCancellable *cancellable, GError ** goto out; } } + ret = TRUE; + out: + return ret; +} + +gboolean +ostree_builtin_refs (int argc, char **argv, GCancellable *cancellable, GError **error) +{ + gboolean ret = FALSE; + GOptionContext *context; + glnx_unref_object OstreeRepo *repo = NULL; + int i; + + context = g_option_context_new ("[PREFIX] - List refs"); + + if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) + goto out; + + if (argc >= 2) + { + for (i = 1; i < argc; i++) + if (!do_ref (repo, argv[i], cancellable, error)) + goto out; + } + else + { + /* Require a prefix when deleting to help avoid accidents. */ + if (opt_delete) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "At least one PREFIX is required when deleting refs"); + goto out; + } + ret = do_ref (repo, NULL, cancellable, error); + } ret = TRUE; out: diff --git a/src/ostree/ot-builtin-trivial-httpd.c b/src/ostree/ot-builtin-trivial-httpd.c index b5fb5d0f..038559bb 100644 --- a/src/ostree/ot-builtin-trivial-httpd.c +++ b/src/ostree/ot-builtin-trivial-httpd.c @@ -427,10 +427,9 @@ ostree_builtin_trivial_httpd (int argc, char **argv, GCancellable *cancellable, */ if (prctl (PR_SET_PDEATHSIG, SIGTERM) != 0) { - int errsv = errno; - if (errsv != ENOSYS) + if (errno != ENOSYS) { - gs_set_error_from_errno (error, errsv); + glnx_set_error_from_errno (error); goto out; } } diff --git a/src/ostree/ot-editor.c b/src/ostree/ot-editor.c index 2b2101f6..4c29c81e 100644 --- a/src/ostree/ot-editor.c +++ b/src/ostree/ot-editor.c @@ -62,8 +62,7 @@ ot_editor_prompt (OstreeRepo *repo, GCancellable *cancellable, GError **error) { - glnx_unref_object GSSubprocessContext *ctx = NULL; - glnx_unref_object GSSubprocess *proc = NULL; + glnx_unref_object GSubprocess *proc = NULL; g_autoptr(GFile) file = NULL; g_autoptr(GFileIOStream) io = NULL; GOutputStream *output; @@ -92,16 +91,11 @@ ot_editor_prompt (OstreeRepo *repo, g_autofree char *quoted_file = g_shell_quote (gs_file_get_path_cached (file)); args = g_strconcat (editor, " ", quoted_file, NULL); } - ctx = gs_subprocess_context_newv ("/bin/sh", "-c", args, NULL); - gs_subprocess_context_set_stdin_disposition (ctx, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT); - gs_subprocess_context_set_stdout_disposition (ctx, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT); - gs_subprocess_context_set_stderr_disposition (ctx, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT); - proc = gs_subprocess_new (ctx, cancellable, error); - if (proc == NULL) - goto out; + proc = g_subprocess_new (G_SUBPROCESS_FLAGS_STDIN_INHERIT, error, + "/bin/sh", "-c", args, NULL); - if (!gs_subprocess_wait_sync_check (proc, cancellable, error)) + if (!g_subprocess_wait_check (proc, cancellable, error)) { g_prefix_error (error, "There was a problem with the editor '%s'", editor); goto out; diff --git a/src/ostree/ot-main.c b/src/ostree/ot-main.c index 5b54af4c..51282f92 100644 --- a/src/ostree/ot-main.c +++ b/src/ostree/ot-main.c @@ -294,7 +294,8 @@ ostree_option_context_parse (GOptionContext *context, } } - gs_transfer_out_value (out_repo, &repo); + if (out_repo) + *out_repo = g_steal_pointer (&repo); success = TRUE; @@ -375,7 +376,8 @@ ostree_admin_option_context_parse (GOptionContext *context, goto out; } - gs_transfer_out_value (out_sysroot, &sysroot); + if (out_sysroot) + *out_sysroot = g_steal_pointer (&sysroot); success = TRUE; diff --git a/src/rofiles-fuse/main.c b/src/rofiles-fuse/main.c index 3c910f1f..bdf7ffb8 100644 --- a/src/rofiles-fuse/main.c +++ b/src/rofiles-fuse/main.c @@ -24,10 +24,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -227,7 +225,7 @@ callback_symlink (const char *from, const char *to) { fprintf (stderr, "Failed to find newly created symlink '%s': %s\n", to, g_strerror (errno)); - exit (1); + exit (EXIT_FAILURE); } return 0; } @@ -263,7 +261,7 @@ can_write (const char *path) else return -errno; } - if (devino_set_contains (stbuf.st_dev, stbuf.st_ino)) + if (!devino_set_contains (stbuf.st_dev, stbuf.st_ino)) return -EROFS; return 0; } @@ -545,7 +543,7 @@ rofs_parse_opt (void *data, const char *arg, int key, if (basefd == -1) { perror ("openat"); - exit (1); + exit (EXIT_FAILURE); } return 0; } @@ -557,10 +555,10 @@ rofs_parse_opt (void *data, const char *arg, int key, return 1; case KEY_HELP: usage (outargs->argv[0]); - exit (0); + exit (EXIT_SUCCESS); default: fprintf (stderr, "see `%s -h' for usage\n", outargs->argv[0]); - exit (1); + exit (EXIT_FAILURE); } return 1; } @@ -584,13 +582,13 @@ main (int argc, char *argv[]) { fprintf (stderr, "Invalid arguments\n"); fprintf (stderr, "see `%s -h' for usage\n", argv[0]); - exit (1); + exit (EXIT_FAILURE); } if (basefd == -1) { fprintf (stderr, "Missing basepath\n"); fprintf (stderr, "see `%s -h' for usage\n", argv[0]); - exit (1); + exit (EXIT_FAILURE); } created_devino_hash = g_hash_table_new_full (devino_hash, devino_equal, g_free, NULL); diff --git a/src/switchroot/ostree-mount-util.c b/src/switchroot/ostree-mount-util.c index c6df559c..daec66c5 100644 --- a/src/switchroot/ostree-mount-util.c +++ b/src/switchroot/ostree-mount-util.c @@ -26,6 +26,10 @@ #include #include #include +#include +#include +#include +#include #include "ostree-mount-util.h" @@ -48,3 +52,18 @@ perrorv (const char *format, ...) return 0; } + +int +path_is_on_readonly_fs (char *path) +{ + struct statvfs stvfsbuf; + + if (statvfs (path, &stvfsbuf) == -1) + { + perrorv ("statvfs(%s): ", path); + exit (EXIT_FAILURE); + } + + return (stvfsbuf.f_flag & ST_RDONLY) != 0; +} + diff --git a/src/switchroot/ostree-mount-util.h b/src/switchroot/ostree-mount-util.h index 63e90c67..475b2cab 100644 --- a/src/switchroot/ostree-mount-util.h +++ b/src/switchroot/ostree-mount-util.h @@ -22,3 +22,5 @@ #pragma once int perrorv (const char *format, ...) __attribute__ ((format (printf, 1, 2))); + +int path_is_on_readonly_fs (char *path); diff --git a/src/switchroot/ostree-prepare-root.c b/src/switchroot/ostree-prepare-root.c index 3de137bb..375867b1 100644 --- a/src/switchroot/ostree-prepare-root.c +++ b/src/switchroot/ostree-prepare-root.c @@ -111,7 +111,6 @@ touch_run_ostree (void) int main(int argc, char *argv[]) { - const char *readonly_bind_mounts[] = { "/usr", NULL }; const char *root_mountpoint = NULL; char *ostree_target = NULL; char *deploy_path = NULL; @@ -119,7 +118,7 @@ main(int argc, char *argv[]) char destpath[PATH_MAX]; char newroot[PATH_MAX]; struct stat stbuf; - int i; + int orig_cwd_dfd; if (argc < 2) { @@ -211,21 +210,70 @@ main(int argc, char *argv[]) } } - /* Set up any read-only bind mounts (notably /usr) */ - for (i = 0; readonly_bind_mounts[i] != NULL; i++) + /* Here we do a dance to chdir to the newroot so that we can have + * the potential overlayfs mount points not look ugly. However...I + * think we could do this a lot earlier and make all of the mounts + * here just be relative. + */ + orig_cwd_dfd = openat (AT_FDCWD, ".", O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); + if (orig_cwd_dfd < 0) { - snprintf (destpath, sizeof(destpath), "%s%s", newroot, readonly_bind_mounts[i]); - if (mount (destpath, destpath, NULL, MS_BIND, NULL) < 0) + perrorv ("failed to open ."); + exit (EXIT_FAILURE); + } + + if (chdir (newroot) < 0) + { + perrorv ("failed to chdir to newroot"); + exit (EXIT_FAILURE); + } + + /* Do we have a persistent overlayfs for /usr? If so, mount it now. */ + if (lstat (".usr-ovl-work", &stbuf) == 0) + { + const char usr_ovl_options[] = "lowerdir=usr,upperdir=.usr-ovl-upper,workdir=.usr-ovl-work"; + + /* Except overlayfs barfs if we try to mount it on a read-only + * filesystem. For this use case I think admins are going to be + * okay if we remount the rootfs here, rather than waiting until + * later boot and `systemd-remount-fs.service`. + */ + if (path_is_on_readonly_fs (".")) { - perrorv ("failed to bind mount (class:readonly) %s", destpath); - exit (EXIT_FAILURE); + if (mount (".", ".", NULL, MS_REMOUNT | MS_SILENT, NULL) < 0) + { + perrorv ("Failed to remount rootfs writable (for overlayfs)"); + exit (EXIT_FAILURE); + } } - if (mount (destpath, destpath, NULL, MS_BIND | MS_REMOUNT | MS_RDONLY, NULL) < 0) + + if (mount ("overlay", "usr", "overlay", 0, usr_ovl_options) < 0) { - perrorv ("failed to bind mount (class:readonly) %s", destpath); + perrorv ("failed to mount /usr overlayfs"); exit (EXIT_FAILURE); } } + else + { + /* Otherwise, a read-only bind mount for /usr */ + if (mount ("usr", "usr", NULL, MS_BIND, NULL) < 0) + { + perrorv ("failed to bind mount (class:readonly) /usr"); + exit (EXIT_FAILURE); + } + if (mount ("usr", "usr", NULL, MS_BIND | MS_REMOUNT | MS_RDONLY, NULL) < 0) + { + perrorv ("failed to bind mount (class:readonly) /usr"); + exit (EXIT_FAILURE); + } + } + + if (fchdir (orig_cwd_dfd) < 0) + { + perrorv ("failed to chdir to orig root"); + exit (EXIT_FAILURE); + } + (void) close (orig_cwd_dfd); touch_run_ostree (); diff --git a/src/switchroot/ostree-remount.c b/src/switchroot/ostree-remount.c index b8d3a963..aecaf9a8 100644 --- a/src/switchroot/ostree-remount.c +++ b/src/switchroot/ostree-remount.c @@ -37,20 +37,6 @@ #include "ostree-mount-util.h" -static int -path_is_on_readonly_fs (char *path) -{ - struct statvfs stvfsbuf; - - if (statvfs (path, &stvfsbuf) == -1) - { - perrorv ("statvfs(%s): ", path); - exit (EXIT_FAILURE); - } - - return (stvfsbuf.f_flag & ST_RDONLY) != 0; -} - /* Having a writeable /var is necessary for full system functioning. * If /var isn't writeable, we mount tmpfs over it. While this is * somewhat outside of ostree's scope, having all /var twiddling diff --git a/tests/admin-test.sh b/tests/admin-test.sh index c4644d3a..3a04a69c 100755 --- a/tests/admin-test.sh +++ b/tests/admin-test.sh @@ -1,4 +1,4 @@ -# +#!/bin/sh # Copyright (C) 2011,2014 Colin Walters # # This library is free software; you can redistribute it and/or @@ -18,7 +18,7 @@ set -euo pipefail -echo "1..10" +echo "1..16" function validate_bootloader() { (cd ${test_tmpdir}; @@ -27,11 +27,14 @@ function validate_bootloader() { fi) } +orig_mtime=$(stat -c '%.Y' sysroot/ostree/deploy) ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime rev=$(${CMD_PREFIX} ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime) export rev # This initial deployment gets kicked off with some kernel arguments ${CMD_PREFIX} ostree admin deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime +new_mtime=$(stat -c '%.Y' sysroot/ostree/deploy) +assert_not_streq "${orig_mtime}" "${new_mtime}" ${CMD_PREFIX} ostree admin status | tee status.txt validate_bootloader @@ -56,7 +59,10 @@ ${CMD_PREFIX} ostree admin status echo "ok layout" +orig_mtime=$(stat -c '%.Y' sysroot/ostree/deploy) ${CMD_PREFIX} ostree admin deploy --os=testos testos:testos/buildmaster/x86_64-runtime +new_mtime=$(stat -c '%.Y' sysroot/ostree/deploy) +assert_not_streq "${orig_mtime}" "${new_mtime}" # Need a new bootversion, sine we now have two deployments assert_has_dir sysroot/boot/loader.0 assert_not_has_dir sysroot/boot/loader.1 diff --git a/tests/basic-test.sh b/tests/basic-test.sh index c0487d64..8acec1c2 100755 --- a/tests/basic-test.sh +++ b/tests/basic-test.sh @@ -19,7 +19,7 @@ set -euo pipefail -echo "1..48" +echo "1..50" $OSTREE checkout test2 checkout-test2 echo "ok checkout" @@ -41,14 +41,6 @@ echo "ok shortened checksum" (cd repo && ${CMD_PREFIX} ostree rev-parse test2) echo "ok repo-in-cwd" -$OSTREE refs > reflist -assert_file_has_content reflist '^test2$' -rm reflist - -$OSTREE refs --delete 2>/dev/null && (echo 1>&2 "refs --delete (without prefix) unexpectedly succeeded!"; exit 1) - -echo "ok refs" - cd checkout-test2 assert_has_file firstfile assert_has_file baz/cow @@ -246,15 +238,6 @@ if test -s file-objects; then fi echo "ok prune in archive-z2 deleted everything" -cd ${test_tmpdir} -$OSTREE commit -b test3 -s "Another commit" --tree=ref=test2 -${CMD_PREFIX} ostree --repo=repo refs > reflist -assert_file_has_content reflist '^test3$' -${CMD_PREFIX} ostree --repo=repo refs --delete test3 -${CMD_PREFIX} ostree --repo=repo refs > reflist -assert_not_file_has_content reflist '^test3$' -echo "ok reflist --delete" - cd ${test_tmpdir} rm -rf test2-checkout $OSTREE checkout test2 test2-checkout @@ -442,6 +425,8 @@ if test "$(id -u)" != "0"; then assert_has_file expected-fail assert_file_has_content error-message "Permission denied" echo "ok unwritable repo was caught" +else + echo "ok # SKIP not run when root" fi cd ${test_tmpdir} @@ -449,10 +434,12 @@ rm -rf test2-checkout mkdir -p test2-checkout cd test2-checkout touch blah -stat --printf="%Z\n" ${test_tmpdir}/repo > ${test_tmpdir}/timestamp-orig.txt +stat --printf="%.Y\n" ${test_tmpdir}/repo > ${test_tmpdir}/timestamp-orig.txt $OSTREE commit -b test2 -s "Should bump the mtime" -stat --printf="%Z\n" ${test_tmpdir}/repo > ${test_tmpdir}/timestamp-new.txt +stat --printf="%.Y\n" ${test_tmpdir}/repo > ${test_tmpdir}/timestamp-new.txt cd .. -if ! cmp timestamp-{orig,new}.txt; then +if cmp timestamp-{orig,new}.txt; then assert_not_reached "failed to update mtime on repo" fi + +echo "ok mtime updated" diff --git a/tests/gpghome/pubring.gpg b/tests/gpghome/pubring.gpg new file mode 120000 index 00000000..3ada1d42 --- /dev/null +++ b/tests/gpghome/pubring.gpg @@ -0,0 +1 @@ +trusted/pubring.gpg \ No newline at end of file diff --git a/tests/libostreetest.c b/tests/libostreetest.c new file mode 100644 index 00000000..58283368 --- /dev/null +++ b/tests/libostreetest.c @@ -0,0 +1,104 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" +#include +#include + +#include "libglnx.h" +#include "libostreetest.h" + +/* This function hovers in a quantum superposition of horrifying and + * beautiful. Future generations may interpret it as modern art. + */ +static gboolean +run_libtest (const char *cmd, GError **error) +{ + gboolean ret = FALSE; + const char *builddir = g_getenv ("G_TEST_BUILDDIR"); + int estatus; + g_autoptr(GPtrArray) argv = g_ptr_array_new (); + g_autoptr(GString) cmdstr = g_string_new (""); + + g_ptr_array_add (argv, "bash"); + g_ptr_array_add (argv, "-c"); + + g_string_append (cmdstr, ". "); + g_string_append (cmdstr, builddir); + g_string_append (cmdstr, "/tests/libtest.sh; "); + g_string_append (cmdstr, cmd); + + g_ptr_array_add (argv, cmdstr->str); + g_ptr_array_add (argv, NULL); + + if (!g_spawn_sync (NULL, (char**)argv->pdata, NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, NULL, NULL, &estatus, error)) + goto out; + + if (!g_spawn_check_exit_status (estatus, error)) + goto out; + + ret = TRUE; + out: + return ret; +} + +OstreeRepo * +ot_test_setup_repo (GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + g_autoptr(GFile) repo_path = g_file_new_for_path ("repo"); + glnx_unref_object OstreeRepo* ret_repo = NULL; + + if (!run_libtest ("setup_test_repository", error)) + goto out; + + ret_repo = ostree_repo_new (repo_path); + + if (!ostree_repo_open (ret_repo, cancellable, error)) + goto out; + + ret = TRUE; + out: + if (ret) + return g_steal_pointer (&ret_repo); + return NULL; +} + +OstreeSysroot * +ot_test_setup_sysroot (GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + g_autoptr(GFile) sysroot_path = g_file_new_for_path ("sysroot"); + glnx_unref_object OstreeSysroot *ret_sysroot = NULL; + + if (!run_libtest ("setup_os_repository \"archive-z2\" \"syslinux\"", error)) + goto out; + + ret_sysroot = ostree_sysroot_new (sysroot_path); + + ret = TRUE; + out: + if (ret) + return g_steal_pointer (&ret_sysroot); + return NULL; +} diff --git a/tests/libostreetest.h b/tests/libostreetest.h new file mode 100644 index 00000000..eb9bb0b2 --- /dev/null +++ b/tests/libostreetest.h @@ -0,0 +1,37 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2016 Colin Walters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Colin Walters + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + + +OstreeRepo *ot_test_setup_repo (GCancellable *cancellable, + GError **error); + +OstreeSysroot *ot_test_setup_sysroot (GCancellable *cancellable, + GError **error); + +G_END_DECLS diff --git a/tests/libtest.sh b/tests/libtest.sh old mode 100644 new mode 100755 index 8cc4345f..06982d21 --- a/tests/libtest.sh +++ b/tests/libtest.sh @@ -18,10 +18,30 @@ # Boston, MA 02111-1307, USA. SRCDIR=$(dirname $0) + +assert_not_reached () { + echo $@ 1>&2; exit 1 +} + test_tmpdir=$(pwd) +# Sanity check that we're in a tmpdir that has +# just .testtmp (created by tap-driver for `make check`, +# or nothing at all (as ginstest-runner does) +if ! test -f .testtmp; then + files=$(ls) + if test -n "${files}"; then + assert_not_reached "test tmpdir=${test_tmpdir} is not empty; run this test via \`make check TESTS=\`, not directly" + fi +fi + export G_DEBUG=fatal-warnings +# Also, unbreak `tar` inside `make check`...Automake will inject +# TAR_OPTIONS: --owner=0 --group=0 --numeric-owner presumably so that +# tarballs are predictable, except we don't want this in our tests. +unset TAR_OPTIONS + # Don't flag deployments as immutable so that test harnesses can # easily clean up. export OSTREE_SYSROOT_DEBUG=mutable-deployments @@ -33,6 +53,7 @@ export TEST_GPG_KEYID_3="DF444D67" # GPG when creating signatures demands a writable # homedir in order to create lockfiles. Work around # this by copying locally. +echo "Copying gpghome to ${test_tmpdir}" cp -a ${SRCDIR}/gpghome ${test_tmpdir} export TEST_GPG_KEYHOME=${test_tmpdir}/gpghome export OSTREE_GPG_HOME=${test_tmpdir}/gpghome/trusted @@ -47,10 +68,6 @@ else CMD_PREFIX="env LD_PRELOAD=${SRCDIR}/libreaddir-rand.so" fi -assert_not_reached () { - echo $@ 1>&2; exit 1 -} - assert_streq () { test "$1" = "$2" || (echo 1>&2 "$1 != $2"; exit 1) } diff --git a/tests/pull-test.sh b/tests/pull-test.sh index 9c8b41fa..8e8a6934 100755 --- a/tests/pull-test.sh +++ b/tests/pull-test.sh @@ -35,6 +35,8 @@ function verify_initial_contents() { assert_file_has_content baz/cow '^moo$' } +echo "1..11" + # Try both syntaxes repo_init ${CMD_PREFIX} ostree --repo=repo pull origin main @@ -108,7 +110,7 @@ rm main-files -rf ${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo static-delta generate main prev_rev=$(ostree --repo=ostree-srv/gnomerepo rev-parse main^) new_rev=$(ostree --repo=ostree-srv/gnomerepo rev-parse main) -ostree --repo=ostree-srv/gnomerepo summary -u +${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo summary -u cd ${test_tmpdir} repo_init @@ -159,7 +161,7 @@ echo "ok pull byteswapped delta" cd ${test_tmpdir} rm ostree-srv/gnomerepo/deltas -rf -ostree --repo=ostree-srv/gnomerepo summary -u +${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo summary -u repo_init if ${CMD_PREFIX} ostree --repo=repo pull --require-static-deltas origin main 2>err.txt; then assert_not_reached "--require-static-deltas unexpectedly succeeded" @@ -179,7 +181,7 @@ cd .. rm main-files -rf # Generate new delta that we'll use ${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo static-delta generate --inline main -ostree --repo=ostree-srv/gnomerepo summary -u +${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo summary -u cd ${test_tmpdir} ${CMD_PREFIX} ostree --repo=repo pull origin main @@ -203,7 +205,7 @@ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/ostree-srv/gnomerepo commit -b main - cd .. rm main-files -rf ${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo static-delta generate main -ostree --repo=ostree-srv/gnomerepo summary -u +${CMD_PREFIX} ostree --repo=ostree-srv/gnomerepo summary -u cd ${test_tmpdir} ${CMD_PREFIX} ostree --repo=repo pull origin main diff --git a/tests/test-admin-deploy-2.sh b/tests/test-admin-deploy-2.sh index ef6b5953..79e253bb 100755 --- a/tests/test-admin-deploy-2.sh +++ b/tests/test-admin-deploy-2.sh @@ -21,14 +21,10 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..1" - # Exports OSTREE_SYSROOT so --sysroot not needed. setup_os_repository "archive-z2" "syslinux" -echo "ok setup" - -echo "1..2" +echo "1..3" ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime rev=$(${CMD_PREFIX} ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime) diff --git a/tests/test-admin-deploy-clean.sh b/tests/test-admin-deploy-clean.sh old mode 100644 new mode 100755 diff --git a/tests/test-admin-deploy-etcmerge-cornercases.sh b/tests/test-admin-deploy-etcmerge-cornercases.sh old mode 100644 new mode 100755 index 4b0d781b..10e7a438 --- a/tests/test-admin-deploy-etcmerge-cornercases.sh +++ b/tests/test-admin-deploy-etcmerge-cornercases.sh @@ -21,13 +21,9 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..1" - # Exports OSTREE_SYSROOT so --sysroot not needed. setup_os_repository "archive-z2" "syslinux" -echo "ok setup" - echo "1..2" ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime diff --git a/tests/test-admin-deploy-karg.sh b/tests/test-admin-deploy-karg.sh old mode 100644 new mode 100755 index a8c1e594..2ce88627 --- a/tests/test-admin-deploy-karg.sh +++ b/tests/test-admin-deploy-karg.sh @@ -21,14 +21,10 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..1" - # Exports OSTREE_SYSROOT so --sysroot not needed. setup_os_repository "archive-z2" "syslinux" -echo "ok setup" - -echo "1..1" +echo "1..3" ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime rev=$(${CMD_PREFIX} ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime) diff --git a/tests/test-admin-deploy-switch.sh b/tests/test-admin-deploy-switch.sh index 4a52000c..0157e27b 100755 --- a/tests/test-admin-deploy-switch.sh +++ b/tests/test-admin-deploy-switch.sh @@ -21,14 +21,10 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..1" - # Exports OSTREE_SYSROOT so --sysroot not needed. setup_os_repository "archive-z2" "syslinux" -echo "ok setup" - -echo "1..3" +echo "1..4" ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo remote add --set=gpg-verify=false testos file://$(pwd)/testos-repo ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull testos testos/buildmaster/x86_64-runtime diff --git a/tests/test-admin-deploy-syslinux.sh b/tests/test-admin-deploy-syslinux.sh index 5883f76d..419df2ba 100755 --- a/tests/test-admin-deploy-syslinux.sh +++ b/tests/test-admin-deploy-syslinux.sh @@ -21,11 +21,7 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..1" - # Exports OSTREE_SYSROOT so --sysroot not needed. setup_os_repository "archive-z2" "syslinux" -echo "ok setup" - . $(dirname $0)/admin-test.sh diff --git a/tests/test-admin-deploy-uboot.sh b/tests/test-admin-deploy-uboot.sh index c22af6f3..b998e082 100755 --- a/tests/test-admin-deploy-uboot.sh +++ b/tests/test-admin-deploy-uboot.sh @@ -22,11 +22,7 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..1" - # Exports OSTREE_SYSROOT so --sysroot not needed. setup_os_repository "archive-z2" "uboot" -echo "ok setup" - . $(dirname $0)/admin-test.sh diff --git a/tests/test-admin-instutil-set-kargs.sh b/tests/test-admin-instutil-set-kargs.sh old mode 100644 new mode 100755 index 33b2b74e..0af940ff --- a/tests/test-admin-instutil-set-kargs.sh +++ b/tests/test-admin-instutil-set-kargs.sh @@ -22,13 +22,9 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..1" - # Exports OSTREE_SYSROOT so --sysroot not needed. setup_os_repository "archive-z2" "syslinux" -echo "ok setup" - echo "1..5" ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime diff --git a/tests/test-admin-locking.sh b/tests/test-admin-locking.sh old mode 100644 new mode 100755 index 5f00f571..564295d2 --- a/tests/test-admin-locking.sh +++ b/tests/test-admin-locking.sh @@ -21,12 +21,14 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..1" - # Exports OSTREE_SYSROOT so --sysroot not needed. setup_os_repository "archive-z2" "syslinux" -echo "ok setup" +# If parallel is not installed, skip the test +if ! parallel --help >/dev/null 2>&1; then + echo "1..0 # SKIP no /usr/bin/parallel" + exit 0 +fi echo "1..1" @@ -40,9 +42,6 @@ echo "rev=${rev}" ${CMD_PREFIX} ostree admin deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime assert_has_dir sysroot/boot/ostree/testos-${bootcsum} -# If parallel is not installed, skip the test -parallel --help >/dev/null 2>&1 || exit 77 - parallel_cmd=parallel if parallel --help | grep -q -e --no-notice; then parallel_cmd="${parallel_cmd} --no-notice" diff --git a/tests/test-admin-pull-deploy-commit.sh b/tests/test-admin-pull-deploy-commit.sh old mode 100644 new mode 100755 diff --git a/tests/test-admin-upgrade-not-backwards.sh b/tests/test-admin-upgrade-not-backwards.sh old mode 100644 new mode 100755 index 1b99e25d..fd1e1108 --- a/tests/test-admin-upgrade-not-backwards.sh +++ b/tests/test-admin-upgrade-not-backwards.sh @@ -21,13 +21,9 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..1" - # Exports OSTREE_SYSROOT so --sysroot not needed. setup_os_repository "archive-z2" "syslinux" -echo "ok setup" - echo "1..2" cd ${test_tmpdir} diff --git a/tests/test-admin-upgrade-unconfigured.sh b/tests/test-admin-upgrade-unconfigured.sh old mode 100644 new mode 100755 index 38df710d..c952356e --- a/tests/test-admin-upgrade-unconfigured.sh +++ b/tests/test-admin-upgrade-unconfigured.sh @@ -21,13 +21,9 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo "1..1" - # Exports OSTREE_SYSROOT so --sysroot not needed. setup_os_repository "archive-z2" "syslinux" -echo "ok setup" - echo "1..2" ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime diff --git a/tests/test-archivez.sh b/tests/test-archivez.sh index 5db973ff..999157de 100755 --- a/tests/test-archivez.sh +++ b/tests/test-archivez.sh @@ -24,7 +24,6 @@ set -euo pipefail echo '1..11' setup_test_repository "archive-z2" -echo "ok setup" . ${SRCDIR}/archive-test.sh diff --git a/tests/test-auto-summary.sh b/tests/test-auto-summary.sh index 6cb52e8c..6039f526 100755 --- a/tests/test-auto-summary.sh +++ b/tests/test-auto-summary.sh @@ -20,7 +20,7 @@ set -euo pipefail -echo "1..1" +echo "1..4" . $(dirname $0)/libtest.sh diff --git a/tests/test-basic-c.c b/tests/test-basic-c.c new file mode 100644 index 00000000..d5dcc811 --- /dev/null +++ b/tests/test-basic-c.c @@ -0,0 +1,55 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include + +#include "libglnx.h" +#include "libostreetest.h" + +static void +test_repo_is_not_system (gconstpointer data) +{ + OstreeRepo *repo = (void*)data; + g_assert (!ostree_repo_is_system (repo)); +} + +int main (int argc, char **argv) +{ + g_autoptr(GError) error = NULL; + glnx_unref_object OstreeRepo *repo = NULL; + + g_test_init (&argc, &argv, NULL); + + repo = ot_test_setup_repo (NULL, &error); + if (!repo) + goto out; + + g_test_add_data_func ("/repo-not-system", repo, test_repo_is_not_system); + + return g_test_run(); + out: + if (error) + g_error ("%s", error->message); + return 1; +} diff --git a/tests/test-basic.sh b/tests/test-basic.sh index ae55aab2..d1afe75f 100755 --- a/tests/test-basic.sh +++ b/tests/test-basic.sh @@ -19,11 +19,8 @@ set -euo pipefail -echo "1..1" - . $(dirname $0)/libtest.sh setup_test_repository "bare" -echo "ok setup" . $(dirname $0)/basic-test.sh diff --git a/tests/test-checksum.c b/tests/test-checksum.c index 80b13431..25d3c37e 100644 --- a/tests/test-checksum.c +++ b/tests/test-checksum.c @@ -31,24 +31,24 @@ static void test_ostree_parse_delta_name (void) { { - gs_free char *from; - gs_free char *to; + g_autofree char *from; + g_autofree char *to; _ostree_parse_delta_name ("30d13b73cfe1e6988ffc345eac905f82a18def8ef1f0666fc392019e9eac388d", &from, &to); g_assert_cmpstr (to, ==, "30d13b73cfe1e6988ffc345eac905f82a18def8ef1f0666fc392019e9eac388d"); g_assert_null (from); } { - gs_free char *from; - gs_free char *to; + g_autofree char *from; + g_autofree char *to; _ostree_parse_delta_name ("30d13b73cfe1e6988ffc345eac905f82a18def8ef1f0666fc392019e9eac388d-5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03", &from, &to); g_assert_cmpstr (from, ==, "30d13b73cfe1e6988ffc345eac905f82a18def8ef1f0666fc392019e9eac388d"); g_assert_cmpstr (to, ==, "5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03"); } { - gs_free char *from; - gs_free char *to; + g_autofree char *from; + g_autofree char *to; _ostree_parse_delta_name ("", &from, &to); g_assert_null (from); g_assert_null (to); diff --git a/tests/test-commit-sign.sh b/tests/test-commit-sign.sh index 2db671ee..8d52bcaf 100755 --- a/tests/test-commit-sign.sh +++ b/tests/test-commit-sign.sh @@ -20,11 +20,14 @@ set -euo pipefail if ! ostree --version | grep -q -e '\+gpgme'; then - exit 77 + echo "1..0 #SKIP no gpg support compiled in" + exit 0 fi . $(dirname $0)/libtest.sh +echo "1..1" + keyid="472CDAFA" oldpwd=`pwd` mkdir ostree-srv @@ -122,10 +125,12 @@ assert_file_has_content show 'Found 1 signature' # Delete the signature from the commit so the detached metadata is empty, # then pull and verify the signature is also deleted on the client side. -${CMD_PREFIX} ostree --repo=${test_tmpdir}/ostree-srv/gnomerepo gpg-sign --gpg-homedir=${SRCDIR}/gpghome --delete main $keyid +${CMD_PREFIX} ostree --repo=${test_tmpdir}/ostree-srv/gnomerepo gpg-sign --gpg-homedir=${test_tmpdir}/gpghome --delete main $keyid ${CMD_PREFIX} ostree --repo=repo pull origin main if ${CMD_PREFIX} ostree --repo=repo show main | grep -o 'Found [[:digit:]] signature'; then assert_not_reached fi rm -rf repo gnomerepo-files + +echo "ok" diff --git a/tests/test-delta.sh b/tests/test-delta.sh index ebe35571..12f54c7d 100755 --- a/tests/test-delta.sh +++ b/tests/test-delta.sh @@ -24,7 +24,7 @@ set -euo pipefail bindatafiles="bash true ostree" morebindatafiles="false ls" -echo '1..3' +echo '1..7' mkdir repo ${CMD_PREFIX} ostree --repo=repo init --mode=archive-z2 diff --git a/tests/test-demo-buildsystem.sh b/tests/test-demo-buildsystem.sh new file mode 100755 index 00000000..500eac68 --- /dev/null +++ b/tests/test-demo-buildsystem.sh @@ -0,0 +1,104 @@ +#!/bin/bash +# +# Copyright (C) 2016 Colin Walters +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +set -euo pipefail + +if ! fusermount --version >/dev/null 2>&1; then + echo "1..0 # SKIP no fusermount" + exit 0 +fi + +. $(dirname $0)/libtest.sh + +echo "1..1" + +# Run "triggers" like ldconfig, gtk-update-icon-cache, etc. +demo_triggers() { + root=$1 + shift + mkdir -p ${root}/usr/lib + echo updated ldconfig at $(date) > ${root}/usr/lib/ld.so.cache.new + mv ${root}/usr/lib/ld.so.cache{.new,} +} + +# Make a binary in /usr/bin/$pkg which contains $version +exampleos_build_commit_package() { + pkg=$1 + version=$2 + mkdir -p ${pkg}-package/usr/bin/ + echo "${pkg}-content ${version}" > ${pkg}-package/usr/bin/${pkg} + # Use a dummy subject for this. + ostree --repo=build-repo commit -b exampleos/x86_64/${pkg} -s '' --tree=dir=${pkg}-package + rm ${pkg}-package -rf +} + +exampleos_recompose() { + rm exampleos-build -rf + for pkg in ${packages}; do + ostree --repo=build-repo checkout -U --union exampleos/x86_64/${pkg} exampleos-build + done + + # Now that we have our rootfs, run triggers + rofiles-fuse exampleos-build mnt + demo_triggers mnt/ + fusermount -u mnt + + # Then we commit it, using --link-checkout-speedup to effectively + # only re-checksum the ldconfig file. We also have dummy commit + # message here. + ostree --repo=build-repo commit -b exampleos/x86_64/standard -s 'exampleos build' --link-checkout-speedup exampleos-build +} + +packages="bash systemd" + +mkdir build-repo +ostree --repo=build-repo init --mode=bare-user +mkdir repo +ostree --repo=repo init --mode=archive-z2 +# Our FUSE mount point +mkdir mnt + +# "Build" some packages which are really just files with +# the version number inside. +exampleos_build_commit_package bash 0.4.7 +exampleos_build_commit_package systemd 224 + +# Now union the packages and commit +exampleos_recompose + +# This is our first commit - let's publish it. +ostree --repo=repo pull-local build-repo exampleos/x86_64/standard + +# Now, update the bash package - this is a new commit on the branch +# exampleos/x86_64/bash. +exampleos_build_commit_package bash 0.5.0 + +# We now have two commits +exampleos_recompose + +# Publish again: +ostree --repo=repo pull-local build-repo exampleos/x86_64/standard +# Optional: Generate a static delta vs the previous build +ostree --repo=repo static-delta generate exampleos/x86_64/standard +# Optional: Regenerate the summary file +ostree --repo=repo summary -u + +# Try: ostree --repo=demo-repo ls -R exampleos/x86_64/standard + +echo "ok demo buildsystem" diff --git a/tests/test-export.sh b/tests/test-export.sh index 18f2f7ae..1e81a2e9 100755 --- a/tests/test-export.sh +++ b/tests/test-export.sh @@ -33,7 +33,7 @@ cd ${test_tmpdir} ${OSTREE} 'export' test2-noxattrs -o test2.tar mkdir t (cd t && tar xf ../test2.tar) -ostree --repo=repo diff --no-xattrs test2-noxattrs ./t > diff.txt +${CMD_PREFIX} ostree --repo=repo diff --no-xattrs test2-noxattrs ./t > diff.txt assert_file_empty diff.txt rm test2.tar diff.txt t -rf @@ -42,9 +42,8 @@ echo 'ok export gnutar diff (no xattrs)' cd ${test_tmpdir} ${OSTREE} 'export' test2 -o test2.tar ${OSTREE} commit -b test2-from-tar -s 'Import from tar' --tree=tar=test2.tar -ostree --repo=repo diff test2 test2-from-tar +${CMD_PREFIX} ostree --repo=repo diff test2 test2-from-tar assert_file_empty diff.txt rm test2.tar diff.txt t -rf echo 'ok export import' - diff --git a/tests/test-gpg-signed-commit.sh b/tests/test-gpg-signed-commit.sh old mode 100644 new mode 100755 index b713da2d..a0bf9832 --- a/tests/test-gpg-signed-commit.sh +++ b/tests/test-gpg-signed-commit.sh @@ -21,11 +21,14 @@ set -euo pipefail if ! ostree --version | grep -q -e '\+gpgme'; then - exit 77 + echo "1..0 #SKIP no gpgme support compiled in" + exit 0 fi . $(dirname $0)/libtest.sh +echo "1..1" + setup_test_repository "archive-z2" export OSTREE_GPG_SIGN="${OSTREE} gpg-sign --gpg-homedir=${TEST_GPG_KEYHOME}" @@ -74,3 +77,5 @@ assert_file_has_content test2-delete 'Signatures deleted: 2' if ${OSTREE} show test2 | grep -o 'Found [[:digit:]] signature'; then assert_not_reached fi + +echo "ok" diff --git a/tests/test-gpg-verify-result.c b/tests/test-gpg-verify-result.c index fe22dc30..e2cb48a7 100644 --- a/tests/test-gpg-verify-result.c +++ b/tests/test-gpg-verify-result.c @@ -66,7 +66,7 @@ test_fixture_setup (TestFixture *fixture, * taken into account, which contains additional data like revocation * certificates for certain test cases. */ - homedir = g_test_build_filename (G_TEST_DIST, "gpg-verify-data", NULL); + homedir = g_test_build_filename (G_TEST_DIST, "tests/gpg-verify-data", NULL); g_setenv ("GNUPGHOME", homedir, TRUE); result = g_initable_new (OSTREE_TYPE_GPG_VERIFY_RESULT, diff --git a/tests/test-libarchive-import.c b/tests/test-libarchive-import.c index 928b1491..877fa77c 100644 --- a/tests/test-libarchive-import.c +++ b/tests/test-libarchive-import.c @@ -193,6 +193,15 @@ test_libarchive_ignore_device_file (gconstpointer data) glnx_unref_object GFile *root = NULL; g_autofree char *commit_checksum = NULL; + if (setxattr (td->tmpd, "user.test-xattr-support", "yes", 4, 0) != 0) + { + int saved_errno = errno; + g_autofree gchar *message = g_strdup_printf ("unable to setxattr on \"%s\": %s", td->tmpd, g_strerror (saved_errno)); + + g_test_skip (message); + goto out; + } + g_assert_cmpint (0, ==, lseek (td->fd, 0, SEEK_SET)); g_assert_cmpint (0, ==, archive_read_support_format_all (a)); g_assert_cmpint (0, ==, archive_read_support_filter_all (a)); diff --git a/tests/test-libarchive.sh b/tests/test-libarchive.sh index 92e24083..7309ffd2 100755 --- a/tests/test-libarchive.sh +++ b/tests/test-libarchive.sh @@ -20,13 +20,14 @@ set -euo pipefail if ! ostree --version | grep -q -e '\+libarchive'; then - exit 77 + echo "1..0 #SKIP no libarchive support compiled in" + exit 0 fi -echo "1..7" - . $(dirname $0)/libtest.sh +echo "1..7" + setup_test_repository "bare" cd ${test_tmpdir} mkdir foo diff --git a/tests/test-oldstyle-partial.sh b/tests/test-oldstyle-partial.sh old mode 100644 new mode 100755 index 220b0831..a578e7d2 --- a/tests/test-oldstyle-partial.sh +++ b/tests/test-oldstyle-partial.sh @@ -35,3 +35,5 @@ ${CMD_PREFIX} ostree --repo=repo pull origin main --subpath /baz ${CMD_PREFIX} ostree fsck --repo=repo >fsck.out assert_file_has_content fsck.out 'Verifying content integrity of 0 commit objects' assert_file_has_content fsck.out '1 partial commits not verified' + +echo "ok" diff --git a/tests/test-prune.sh b/tests/test-prune.sh old mode 100644 new mode 100755 index a1322d90..7184ea9c --- a/tests/test-prune.sh +++ b/tests/test-prune.sh @@ -23,7 +23,7 @@ set -euo pipefail setup_fake_remote_repo1 "archive-z2" -echo '1..1' +echo '1..2' cd ${test_tmpdir} mkdir repo @@ -126,3 +126,11 @@ ${CMD_PREFIX} ostree --repo=repo static-delta list | wc -l > deltascount assert_file_has_content deltascount "^1$" echo "ok prune" + +rm repo -rf +ostree --repo=repo init --mode=bare-user +${CMD_PREFIX} ostree --repo=repo remote add --set=gpg-verify=false origin $(cat httpd-address)/ostree/gnomerepo +${CMD_PREFIX} ostree --repo=repo pull --depth=-1 --commit-metadata-only origin test +ostree --repo=repo prune + +echo "ok prune with partial repo" diff --git a/tests/test-pull-archive-z.sh b/tests/test-pull-archive-z.sh index 6482f6f9..2ea23871 100755 --- a/tests/test-pull-archive-z.sh +++ b/tests/test-pull-archive-z.sh @@ -23,6 +23,4 @@ set -euo pipefail setup_fake_remote_repo1 "archive-z2" -echo '1..2' - . ${SRCDIR}/pull-test.sh diff --git a/tests/test-pull-corruption.sh b/tests/test-pull-corruption.sh index 1df31943..646113a1 100755 --- a/tests/test-pull-corruption.sh +++ b/tests/test-pull-corruption.sh @@ -19,6 +19,12 @@ set -euo pipefail +# If gjs is not installed, skip the test +if ! gjs --help >/dev/null 2>&1; then + echo "1..0 # SKIP no gjs" + exit 0 +fi + . $(dirname $0)/libtest.sh setup_fake_remote_repo1 "archive-z2" @@ -47,9 +53,6 @@ do_corrupt_pull_test() { fi } -# If gjs is not installed, skip the test -gjs --help >/dev/null 2>&1 || exit 77 - # FIXME - ignore errors here since gjs in RHEL7 has the final # unrooting bug gjs $(dirname $0)/corrupt-repo-ref.js ${repopath} main || true diff --git a/tests/test-pull-depth.sh b/tests/test-pull-depth.sh old mode 100644 new mode 100755 diff --git a/tests/test-pull-large-metadata.sh b/tests/test-pull-large-metadata.sh old mode 100644 new mode 100755 index c50d7943..0fe02031 --- a/tests/test-pull-large-metadata.sh +++ b/tests/test-pull-large-metadata.sh @@ -39,3 +39,5 @@ if ${CMD_PREFIX} ostree --repo=repo pull origin main 2>pulllog.txt 1>&2; then assert_not_reached "pull unexpectedly succeeded!" fi assert_file_has_content pulllog.txt "exceeded maximum" + +echo "ok" diff --git a/tests/test-pull-metalink.sh b/tests/test-pull-metalink.sh index 52d2d503..2a1a73e7 100755 --- a/tests/test-pull-metalink.sh +++ b/tests/test-pull-metalink.sh @@ -23,6 +23,8 @@ set -euo pipefail setup_fake_remote_repo1 "archive-z2" +echo '1..9' + # And another web server acting as the metalink server cd ${test_tmpdir} mkdir metalink-data @@ -37,7 +39,6 @@ summary_path=${test_tmpdir}/ostree-srv/gnomerepo/summary echo -n broken > ${summary_path}.bad -echo '1..1' cd ${test_tmpdir} cat > metalink-valid-summary.xml <> tree/root/a + ${CMD_PREFIX} ostree --repo=repo commit --branch=test-$i -m test -s test tree + ${CMD_PREFIX} ostree --repo=repo commit --branch=foo/test-$i -m test -s test tree +done + +${CMD_PREFIX} ostree --repo=repo refs | wc -l > refscount +assert_file_has_content refscount "^10$" + +${CMD_PREFIX} ostree --repo=repo refs foo > refs +assert_not_file_has_content refs foo + +${CMD_PREFIX} ostree --repo=repo refs --list foo > refs +assert_file_has_content refs foo + +${CMD_PREFIX} ostree --repo=repo refs foo | wc -l > refscount.foo +assert_file_has_content refscount.foo "^5$" + +${CMD_PREFIX} ostree --repo=repo refs --delete 2>/dev/null || true +${CMD_PREFIX} ostree --repo=repo refs | wc -l > refscount.delete1 +assert_file_has_content refscount.delete1 "^10$" + +${CMD_PREFIX} ostree refs --delete 2>/dev/null && (echo 1>&2 "refs --delete (without prefix) unexpectedly succeeded!"; exit 1) +${CMD_PREFIX} ostree --repo=repo refs --delete test-1 test-2 +${CMD_PREFIX} ostree --repo=repo refs | wc -l > refscount.delete2 +assert_file_has_content refscount.delete2 "^8$" + +${CMD_PREFIX} ostree refs --repo=repo --delete foo +${CMD_PREFIX} ostree refs --repo=repo | wc -l > refscount.delete3 +assert_file_has_content refscount.delete3 "^3$" +assert_not_file_has_content reflist '^test-1$' + +echo "ok refs" diff --git a/tests/test-remote-add.sh b/tests/test-remote-add.sh index 2294a06d..badf1495 100755 --- a/tests/test-remote-add.sh +++ b/tests/test-remote-add.sh @@ -21,7 +21,7 @@ set -euo pipefail . $(dirname $0)/libtest.sh -echo '1..3' +echo '1..13' setup_test_repository "bare" $OSTREE remote add origin http://example.com/ostree/gnome diff --git a/tests/test-remote-gpg-import.sh b/tests/test-remote-gpg-import.sh index fc833493..bb0c4029 100755 --- a/tests/test-remote-gpg-import.sh +++ b/tests/test-remote-gpg-import.sh @@ -26,6 +26,8 @@ unset OSTREE_GPG_HOME setup_fake_remote_repo1 "archive-z2" +echo "1..1" + cd ${test_tmpdir} mkdir repo ${OSTREE} init @@ -140,3 +142,5 @@ if ${OSTREE} pull R2:main >/dev/null 2>&1; then assert_not_reached "(key3/R2) GPG verification unexpectedly succeeded" fi ${OSTREE} pull R3:main >/dev/null + +echo "ok" diff --git a/tests/test-repo-checkout-subpath.sh b/tests/test-repo-checkout-subpath.sh index bf792184..2da5adc2 100755 --- a/tests/test-repo-checkout-subpath.sh +++ b/tests/test-repo-checkout-subpath.sh @@ -23,12 +23,13 @@ set -euo pipefail . $(dirname $0)/libtest.sh setup_test_repository "bare" -echo "ok setup" -echo '1..2' +echo '1..1' repopath=${test_tmpdir}/ostree-srv/gnomerepo ${CMD_PREFIX} ostree --repo=repo checkout -U --subpath=/ test2 checkedout ${CMD_PREFIX} ostree --repo=repo checkout -U --subpath=/firstfile test2 checkedout2 + +echo "ok" diff --git a/tests/test-rofiles-fuse.sh b/tests/test-rofiles-fuse.sh index 24ee2648..444fbce2 100755 --- a/tests/test-rofiles-fuse.sh +++ b/tests/test-rofiles-fuse.sh @@ -19,11 +19,16 @@ set -euo pipefail -echo "1..5" +if ! fusermount --version >/dev/null 2>&1; then + echo "1..0 # SKIP no fusermount" + exit 0 +fi . $(dirname $0)/libtest.sh setup_test_repository "bare-user" +echo "1..5" + mkdir mnt $OSTREE checkout test2 checkout-test2 @@ -64,11 +69,6 @@ assert_not_has_dir checkout-test2/baz/another echo "ok deletion" -ostree --repo=repo commit -b test2 -s fromfuse --link-checkout-speedup --tree=dir=checkout-test2 +${CMD_PREFIX} ostree --repo=repo commit -b test2 -s fromfuse --link-checkout-speedup --tree=dir=checkout-test2 echo "ok commit" - - - - - diff --git a/tests/test-setuid.sh b/tests/test-setuid.sh index 5354d1f3..edf707df 100755 --- a/tests/test-setuid.sh +++ b/tests/test-setuid.sh @@ -36,3 +36,5 @@ chmod a+x test2-checkout/abinary rm -rf test2-checkout $OSTREE checkout test2 test2-checkout test -u test2-checkout/abinary + +echo "ok" diff --git a/tests/test-sysroot-c.c b/tests/test-sysroot-c.c new file mode 100644 index 00000000..9c07faf4 --- /dev/null +++ b/tests/test-sysroot-c.c @@ -0,0 +1,92 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include +#include + +#include "libglnx.h" +#include "libostreetest.h" + +static gboolean +run_sync (const char *cmdline, GError **error) +{ + int estatus; + if (!g_spawn_command_line_sync (cmdline, NULL, NULL, &estatus, error)) + return FALSE; + if (!g_spawn_check_exit_status (estatus, error)) + return FALSE; + return TRUE; +} + +static void +test_sysroot_reload (gconstpointer data) +{ + OstreeSysroot *sysroot = (void*)data; + g_autoptr(GError) error = NULL; + gboolean changed; + + if (!ostree_sysroot_load (sysroot, NULL, &error)) + goto out; + + if (!ostree_sysroot_load_if_changed (sysroot, &changed, NULL, &error)) + goto out; + g_assert (!changed); + + if (!run_sync ("ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime", &error)) + goto out; + + if (!run_sync ("ostree admin --sysroot=sysroot deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime", &error)) + goto out; + + if (!ostree_sysroot_load_if_changed (sysroot, &changed, NULL, &error)) + goto out; + g_assert (changed); + + if (!ostree_sysroot_load_if_changed (sysroot, &changed, NULL, &error)) + goto out; + g_assert (!changed); + + out: + if (error) + g_error ("%s", error->message); +} + +int main (int argc, char **argv) +{ + g_autoptr(GError) error = NULL; + glnx_unref_object OstreeSysroot *sysroot = NULL; + + g_test_init (&argc, &argv, NULL); + + sysroot = ot_test_setup_sysroot (NULL, &error); + if (!sysroot) + goto out; + + g_test_add_data_func ("/sysroot-reload", sysroot, test_sysroot_reload); + + return g_test_run(); + out: + if (error) + g_error ("%s", error->message); + return 1; +} diff --git a/tests/test-sysroot.js b/tests/test-sysroot.js index fc8e91fd..9468d2fb 100644 --- a/tests/test-sysroot.js +++ b/tests/test-sysroot.js @@ -34,7 +34,7 @@ function assertNotEquals(a, b) { } function libtestExec(shellCode) { - let testdatadir = GLib.getenv("TESTDATADIR"); + let testdatadir = GLib.getenv("G_TEST_SRCDIR"); let libtestPath = GLib.build_filenamev([testdatadir, 'libtest.sh']) let proc = GSystem.Subprocess.new_simple_argv(['bash', '-c', '. ' + GLib.shell_quote(libtestPath) + '; ' + shellCode],