diff --git a/Makefile-libostree.am b/Makefile-libostree.am index 4b412fec..a7e7e123 100644 --- a/Makefile-libostree.am +++ b/Makefile-libostree.am @@ -214,7 +214,7 @@ libostree_1_la_CFLAGS += $(OT_DEP_AVAHI_CFLAGS) libostree_1_la_LIBADD += $(OT_DEP_AVAHI_LIBS) endif -if BUILDOPT_LIBSYSTEMD +if BUILDOPT_SYSTEMD libostree_1_la_CFLAGS += $(LIBSYSTEMD_CFLAGS) libostree_1_la_LIBADD += $(LIBSYSTEMD_LIBS) endif diff --git a/Makefile-switchroot.am b/Makefile-switchroot.am index b81b843f..ad370eb7 100644 --- a/Makefile-switchroot.am +++ b/Makefile-switchroot.am @@ -55,7 +55,8 @@ ostree_remount_SOURCES = \ src/switchroot/ostree-mount-util.h \ src/switchroot/ostree-remount.c \ $(NULL) -ostree_remount_CPPFLAGS = $(AM_CPPFLAGS) -Isrc/switchroot +ostree_remount_CPPFLAGS = $(AM_CPPFLAGS) $(OT_INTERNAL_GIO_UNIX_CFLAGS) -Isrc/switchroot -I$(srcdir)/libglnx +ostree_remount_LDADD = $(AM_LDFLAGS) $(OT_INTERNAL_GIO_UNIX_LIBS) libglnx.la if BUILDOPT_SYSTEMD ostree_prepare_root_CPPFLAGS += -DHAVE_SYSTEMD=1 diff --git a/Makefile-tests.am b/Makefile-tests.am index fc2f2d91..83b0f1a2 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -86,6 +86,7 @@ _installed_or_uninstalled_test_scripts = \ tests/test-pull-resume.sh \ tests/test-pull-basicauth.sh \ tests/test-pull-repeated.sh \ + tests/test-pull-sizes.sh \ tests/test-pull-untrusted.sh \ tests/test-pull-override-url.sh \ tests/test-pull-localcache.sh \ @@ -220,10 +221,17 @@ dist_gpginsttest_DATA = tests/gpghome/secring.gpg \ tests/gpghome/key3.asc gpginsttest_trusteddir = $(installed_testdir)/gpghome/trusted dist_gpginsttest_trusted_DATA = tests/gpghome/trusted/pubring.gpg +gpginsttest_revocdir = $(installed_testdir)/gpghome/revocations +dist_gpginsttest_revoc_DATA = \ + tests/gpghome/revocations/key1.rev \ + tests/gpghome/revocations/key2.rev \ + tests/gpghome/revocations/key3.rev \ + $(NULL) gpgvinsttestdir = $(installed_testdir)/gpg-verify-data dist_gpgvinsttest_DATA = $(addprefix tests/gpg-verify-data/, \ - gpg.conf lgpl2 lgpl2.sig pubring.gpg secring.gpg trustdb.gpg) + gpg.conf lgpl2 lgpl2.sig lgpl2.sig0 lgpl2.sig1 lgpl2.sig2 lgpl2.sig3 \ + lgpl2.sig4 pubring.gpg secring.gpg trustdb.gpg) endif endif @@ -347,7 +355,10 @@ tests_test_varint_LDADD = $(TESTS_LDADD) tests_test_bsdiff_CFLAGS = $(TESTS_CFLAGS) tests_test_bsdiff_LDADD = libbsdiff.la $(TESTS_LDADD) -tests_test_checksum_SOURCES = src/libostree/ostree-core.c tests/test-checksum.c +tests_test_checksum_SOURCES = \ + src/libostree/ostree-core.c \ + src/libostree/ostree-varint.c \ + tests/test-checksum.c tests_test_checksum_CFLAGS = $(TESTS_CFLAGS) $(libglnx_cflags) tests_test_checksum_LDADD = $(TESTS_LDADD) diff --git a/Makefile.in b/Makefile.in index 0855556d..58410f8b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -461,8 +461,8 @@ check_PROGRAMS = $(am__EXEEXT_12) $(am__EXEEXT_13) $(am__EXEEXT_14) @USE_LIBARCHIVE_TRUE@am__append_29 = $(OT_DEP_LIBARCHIVE_LIBS) @USE_AVAHI_TRUE@am__append_30 = $(OT_DEP_AVAHI_CFLAGS) @USE_AVAHI_TRUE@am__append_31 = $(OT_DEP_AVAHI_LIBS) -@BUILDOPT_LIBSYSTEMD_TRUE@am__append_32 = $(LIBSYSTEMD_CFLAGS) -@BUILDOPT_LIBSYSTEMD_TRUE@am__append_33 = $(LIBSYSTEMD_LIBS) +@BUILDOPT_SYSTEMD_TRUE@am__append_32 = $(LIBSYSTEMD_CFLAGS) +@BUILDOPT_SYSTEMD_TRUE@am__append_33 = $(LIBSYSTEMD_LIBS) @USE_CURL_OR_SOUP_TRUE@am__append_34 = \ @USE_CURL_OR_SOUP_TRUE@ src/libostree/ostree-fetcher.h \ @USE_CURL_OR_SOUP_TRUE@ src/libostree/ostree-fetcher-util.h \ @@ -620,6 +620,7 @@ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(dist_completions_DATA) \ $(am__dist_gpginsttest_DATA_DIST) \ + $(am__dist_gpginsttest_revoc_DATA_DIST) \ $(am__dist_gpginsttest_trusted_DATA_DIST) \ $(am__dist_gpgvinsttest_DATA_DIST) \ $(am__dist_systemdtmpfiles_DATA_DIST) \ @@ -644,6 +645,7 @@ am__installdirs = "$(DESTDIR)$(bindir)" \ "$(DESTDIR)$(ostree_bootdir)" "$(DESTDIR)$(pkglibexecdir)" \ "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" \ "$(DESTDIR)$(completionsdir)" "$(DESTDIR)$(gpginsttestdir)" \ + "$(DESTDIR)$(gpginsttest_revocdir)" \ "$(DESTDIR)$(gpginsttest_trusteddir)" \ "$(DESTDIR)$(gpgvinsttestdir)" \ "$(DESTDIR)$(systemdtmpfilesdir)" "$(DESTDIR)$(dracutconfdir)" \ @@ -762,7 +764,7 @@ libglnx_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ @USE_GPGME_TRUE@ $(am__DEPENDENCIES_1) @USE_LIBARCHIVE_TRUE@am__DEPENDENCIES_4 = $(am__DEPENDENCIES_1) @USE_AVAHI_TRUE@am__DEPENDENCIES_5 = $(am__DEPENDENCIES_1) -@BUILDOPT_LIBSYSTEMD_TRUE@am__DEPENDENCIES_6 = $(am__DEPENDENCIES_1) +@BUILDOPT_SYSTEMD_TRUE@am__DEPENDENCIES_6 = $(am__DEPENDENCIES_1) @USE_CURL_TRUE@am__DEPENDENCIES_7 = $(am__DEPENDENCIES_1) @USE_CURL_FALSE@@USE_LIBSOUP_TRUE@am__DEPENDENCIES_8 = \ @USE_CURL_FALSE@@USE_LIBSOUP_TRUE@ $(am__DEPENDENCIES_2) @@ -1166,7 +1168,7 @@ am_ostree_remount_OBJECTS = \ src/switchroot/ostree_remount-ostree-remount.$(OBJEXT) \ $(am__objects_1) ostree_remount_OBJECTS = $(am_ostree_remount_OBJECTS) -ostree_remount_LDADD = $(LDADD) +ostree_remount_DEPENDENCIES = $(am__DEPENDENCIES_2) libglnx.la am__ostree_system_generator_SOURCES_DIST = \ src/switchroot/ostree-mount-util.h \ src/switchroot/ostree-system-generator.c @@ -1294,6 +1296,7 @@ tests_test_bsdiff_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ -o $@ am_tests_test_checksum_OBJECTS = \ src/libostree/tests_test_checksum-ostree-core.$(OBJEXT) \ + src/libostree/tests_test_checksum-ostree-varint.$(OBJEXT) \ tests/test_checksum-test-checksum.$(OBJEXT) tests_test_checksum_OBJECTS = $(am_tests_test_checksum_OBJECTS) tests_test_checksum_DEPENDENCIES = $(am__DEPENDENCIES_13) @@ -1580,6 +1583,7 @@ am__depfiles_remade = bsdiff/$(DEPDIR)/libbsdiff_la-bsdiff.Plo \ src/libostree/$(DEPDIR)/libostree_1_la-ostree-varint.Plo \ src/libostree/$(DEPDIR)/tests_test_bloom-ostree-bloom.Po \ src/libostree/$(DEPDIR)/tests_test_checksum-ostree-core.Po \ + src/libostree/$(DEPDIR)/tests_test_checksum-ostree-varint.Po \ src/libostree/$(DEPDIR)/tests_test_kargs-ostree-kernel-args.Po \ src/libostree/$(DEPDIR)/tests_test_lzma-ostree-lzma-common.Po \ src/libostree/$(DEPDIR)/tests_test_lzma-ostree-lzma-compressor.Po \ @@ -1788,12 +1792,18 @@ am__dist_gpginsttest_DATA_DIST = tests/gpghome/secring.gpg \ tests/gpghome/pubring.gpg tests/gpghome/trustdb.gpg \ tests/gpghome/key1.asc tests/gpghome/key2.asc \ tests/gpghome/key3.asc +am__dist_gpginsttest_revoc_DATA_DIST = \ + tests/gpghome/revocations/key1.rev \ + tests/gpghome/revocations/key2.rev \ + tests/gpghome/revocations/key3.rev am__dist_gpginsttest_trusted_DATA_DIST = \ tests/gpghome/trusted/pubring.gpg am__dist_gpgvinsttest_DATA_DIST = $(addprefix tests/gpg-verify-data/, \ - gpg.conf lgpl2 lgpl2.sig pubring.gpg secring.gpg trustdb.gpg) + gpg.conf lgpl2 lgpl2.sig lgpl2.sig0 lgpl2.sig1 lgpl2.sig2 \ + lgpl2.sig3 lgpl2.sig4 pubring.gpg secring.gpg trustdb.gpg) am__dist_systemdtmpfiles_DATA_DIST = src/boot/ostree-tmpfiles.conf DATA = $(dist_completions_DATA) $(dist_gpginsttest_DATA) \ + $(dist_gpginsttest_revoc_DATA) \ $(dist_gpginsttest_trusted_DATA) $(dist_gpgvinsttest_DATA) \ $(dist_systemdtmpfiles_DATA) $(dracutconf_DATA) $(gir_DATA) \ $(gpgreadme_DATA) $(installed_test_DATA) \ @@ -2009,9 +2019,9 @@ am__EXEEXT_25 = tests/test-basic.sh tests/test-basic-user.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-pull-basicauth.sh tests/test-pull-repeated.sh \ - tests/test-pull-untrusted.sh tests/test-pull-override-url.sh \ - tests/test-pull-localcache.sh tests/test-local-pull.sh \ - tests/test-local-pull-depth.sh \ + tests/test-pull-sizes.sh tests/test-pull-untrusted.sh \ + tests/test-pull-override-url.sh tests/test-pull-localcache.sh \ + tests/test-local-pull.sh tests/test-local-pull-depth.sh \ tests/test-admin-upgrade-unconfigured.sh \ tests/test-admin-upgrade-endoflife.sh \ tests/test-admin-upgrade-systemd-update.sh \ @@ -2799,8 +2809,10 @@ ostree_remount_SOURCES = \ src/switchroot/ostree-remount.c \ $(NULL) -ostree_remount_CPPFLAGS = $(AM_CPPFLAGS) -Isrc/switchroot \ - $(am__append_67) +ostree_remount_CPPFLAGS = $(AM_CPPFLAGS) \ + $(OT_INTERNAL_GIO_UNIX_CFLAGS) -Isrc/switchroot \ + -I$(srcdir)/libglnx $(am__append_67) +ostree_remount_LDADD = $(AM_LDFLAGS) $(OT_INTERNAL_GIO_UNIX_LIBS) libglnx.la @BUILDOPT_SYSTEMD_TRUE@ostree_prepare_root_LDADD = $(AM_LDFLAGS) $(LIBSYSTEMD_LIBS) @BUILDOPT_SYSTEMD_AND_LIBMOUNT_TRUE@ostree_system_generator_SOURCES = src/switchroot/ostree-mount-util.h \ @BUILDOPT_SYSTEMD_AND_LIBMOUNT_TRUE@ src/switchroot/ostree-system-generator.c @@ -2843,9 +2855,9 @@ _installed_or_uninstalled_test_scripts = tests/test-basic.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-pull-basicauth.sh tests/test-pull-repeated.sh \ - tests/test-pull-untrusted.sh tests/test-pull-override-url.sh \ - tests/test-pull-localcache.sh tests/test-local-pull.sh \ - tests/test-local-pull-depth.sh \ + tests/test-pull-sizes.sh tests/test-pull-untrusted.sh \ + tests/test-pull-override-url.sh tests/test-pull-localcache.sh \ + tests/test-local-pull.sh tests/test-local-pull-depth.sh \ tests/test-admin-upgrade-unconfigured.sh \ tests/test-admin-upgrade-endoflife.sh \ tests/test-admin-upgrade-systemd-update.sh \ @@ -2928,9 +2940,17 @@ dist_test_extra_scripts = \ @ENABLE_INSTALLED_TESTS_TRUE@@USE_GPGME_TRUE@gpginsttest_trusteddir = $(installed_testdir)/gpghome/trusted @ENABLE_INSTALLED_TESTS_TRUE@@USE_GPGME_TRUE@dist_gpginsttest_trusted_DATA = tests/gpghome/trusted/pubring.gpg +@ENABLE_INSTALLED_TESTS_TRUE@@USE_GPGME_TRUE@gpginsttest_revocdir = $(installed_testdir)/gpghome/revocations +@ENABLE_INSTALLED_TESTS_TRUE@@USE_GPGME_TRUE@dist_gpginsttest_revoc_DATA = \ +@ENABLE_INSTALLED_TESTS_TRUE@@USE_GPGME_TRUE@ tests/gpghome/revocations/key1.rev \ +@ENABLE_INSTALLED_TESTS_TRUE@@USE_GPGME_TRUE@ tests/gpghome/revocations/key2.rev \ +@ENABLE_INSTALLED_TESTS_TRUE@@USE_GPGME_TRUE@ tests/gpghome/revocations/key3.rev \ +@ENABLE_INSTALLED_TESTS_TRUE@@USE_GPGME_TRUE@ $(NULL) + @ENABLE_INSTALLED_TESTS_TRUE@@USE_GPGME_TRUE@gpgvinsttestdir = $(installed_testdir)/gpg-verify-data @ENABLE_INSTALLED_TESTS_TRUE@@USE_GPGME_TRUE@dist_gpgvinsttest_DATA = $(addprefix tests/gpg-verify-data/, \ -@ENABLE_INSTALLED_TESTS_TRUE@@USE_GPGME_TRUE@ gpg.conf lgpl2 lgpl2.sig pubring.gpg secring.gpg trustdb.gpg) +@ENABLE_INSTALLED_TESTS_TRUE@@USE_GPGME_TRUE@ gpg.conf lgpl2 lgpl2.sig lgpl2.sig0 lgpl2.sig1 lgpl2.sig2 lgpl2.sig3 \ +@ENABLE_INSTALLED_TESTS_TRUE@@USE_GPGME_TRUE@ lgpl2.sig4 pubring.gpg secring.gpg trustdb.gpg) js_installed_tests = \ tests/test-core.js \ @@ -3008,7 +3028,11 @@ tests_test_varint_CFLAGS = $(TESTS_CFLAGS) tests_test_varint_LDADD = $(TESTS_LDADD) tests_test_bsdiff_CFLAGS = $(TESTS_CFLAGS) tests_test_bsdiff_LDADD = libbsdiff.la $(TESTS_LDADD) -tests_test_checksum_SOURCES = src/libostree/ostree-core.c tests/test-checksum.c +tests_test_checksum_SOURCES = \ + src/libostree/ostree-core.c \ + src/libostree/ostree-varint.c \ + tests/test-checksum.c + tests_test_checksum_CFLAGS = $(TESTS_CFLAGS) $(libglnx_cflags) tests_test_checksum_LDADD = $(TESTS_LDADD) tests_test_libarchive_import_SOURCES = tests/test-libarchive-import.c @@ -4268,6 +4292,9 @@ tests/test-bsdiff$(EXEEXT): $(tests_test_bsdiff_OBJECTS) $(tests_test_bsdiff_DEP src/libostree/tests_test_checksum-ostree-core.$(OBJEXT): \ src/libostree/$(am__dirstamp) \ src/libostree/$(DEPDIR)/$(am__dirstamp) +src/libostree/tests_test_checksum-ostree-varint.$(OBJEXT): \ + src/libostree/$(am__dirstamp) \ + src/libostree/$(DEPDIR)/$(am__dirstamp) tests/test_checksum-test-checksum.$(OBJEXT): tests/$(am__dirstamp) \ tests/$(DEPDIR)/$(am__dirstamp) @@ -4731,6 +4758,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/libostree/$(DEPDIR)/libostree_1_la-ostree-varint.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/libostree/$(DEPDIR)/tests_test_bloom-ostree-bloom.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/libostree/$(DEPDIR)/tests_test_checksum-ostree-core.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/libostree/$(DEPDIR)/tests_test_checksum-ostree-varint.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/libostree/$(DEPDIR)/tests_test_kargs-ostree-kernel-args.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/libostree/$(DEPDIR)/tests_test_lzma-ostree-lzma-common.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/libostree/$(DEPDIR)/tests_test_lzma-ostree-lzma-compressor.Po@am__quote@ # am--include-marker @@ -6645,6 +6673,20 @@ src/libostree/tests_test_checksum-ostree-core.obj: src/libostree/ostree-core.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_test_checksum_CFLAGS) $(CFLAGS) -c -o src/libostree/tests_test_checksum-ostree-core.obj `if test -f 'src/libostree/ostree-core.c'; then $(CYGPATH_W) 'src/libostree/ostree-core.c'; else $(CYGPATH_W) '$(srcdir)/src/libostree/ostree-core.c'; fi` +src/libostree/tests_test_checksum-ostree-varint.o: src/libostree/ostree-varint.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_test_checksum_CFLAGS) $(CFLAGS) -MT src/libostree/tests_test_checksum-ostree-varint.o -MD -MP -MF src/libostree/$(DEPDIR)/tests_test_checksum-ostree-varint.Tpo -c -o src/libostree/tests_test_checksum-ostree-varint.o `test -f 'src/libostree/ostree-varint.c' || echo '$(srcdir)/'`src/libostree/ostree-varint.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/libostree/$(DEPDIR)/tests_test_checksum-ostree-varint.Tpo src/libostree/$(DEPDIR)/tests_test_checksum-ostree-varint.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/libostree/ostree-varint.c' object='src/libostree/tests_test_checksum-ostree-varint.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_test_checksum_CFLAGS) $(CFLAGS) -c -o src/libostree/tests_test_checksum-ostree-varint.o `test -f 'src/libostree/ostree-varint.c' || echo '$(srcdir)/'`src/libostree/ostree-varint.c + +src/libostree/tests_test_checksum-ostree-varint.obj: src/libostree/ostree-varint.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_test_checksum_CFLAGS) $(CFLAGS) -MT src/libostree/tests_test_checksum-ostree-varint.obj -MD -MP -MF src/libostree/$(DEPDIR)/tests_test_checksum-ostree-varint.Tpo -c -o src/libostree/tests_test_checksum-ostree-varint.obj `if test -f 'src/libostree/ostree-varint.c'; then $(CYGPATH_W) 'src/libostree/ostree-varint.c'; else $(CYGPATH_W) '$(srcdir)/src/libostree/ostree-varint.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/libostree/$(DEPDIR)/tests_test_checksum-ostree-varint.Tpo src/libostree/$(DEPDIR)/tests_test_checksum-ostree-varint.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/libostree/ostree-varint.c' object='src/libostree/tests_test_checksum-ostree-varint.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_test_checksum_CFLAGS) $(CFLAGS) -c -o src/libostree/tests_test_checksum-ostree-varint.obj `if test -f 'src/libostree/ostree-varint.c'; then $(CYGPATH_W) 'src/libostree/ostree-varint.c'; else $(CYGPATH_W) '$(srcdir)/src/libostree/ostree-varint.c'; fi` + tests/test_checksum-test-checksum.o: tests/test-checksum.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tests_test_checksum_CFLAGS) $(CFLAGS) -MT tests/test_checksum-test-checksum.o -MD -MP -MF tests/$(DEPDIR)/test_checksum-test-checksum.Tpo -c -o tests/test_checksum-test-checksum.o `test -f 'tests/test-checksum.c' || echo '$(srcdir)/'`tests/test-checksum.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) tests/$(DEPDIR)/test_checksum-test-checksum.Tpo tests/$(DEPDIR)/test_checksum-test-checksum.Po @@ -7174,6 +7216,27 @@ uninstall-dist_gpginsttestDATA: @list='$(dist_gpginsttest_DATA)'; test -n "$(gpginsttestdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(gpginsttestdir)'; $(am__uninstall_files_from_dir) +install-dist_gpginsttest_revocDATA: $(dist_gpginsttest_revoc_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_gpginsttest_revoc_DATA)'; test -n "$(gpginsttest_revocdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(gpginsttest_revocdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(gpginsttest_revocdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(gpginsttest_revocdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(gpginsttest_revocdir)" || exit $$?; \ + done + +uninstall-dist_gpginsttest_revocDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_gpginsttest_revoc_DATA)'; test -n "$(gpginsttest_revocdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(gpginsttest_revocdir)'; $(am__uninstall_files_from_dir) install-dist_gpginsttest_trustedDATA: $(dist_gpginsttest_trusted_DATA) @$(NORMAL_INSTALL) @list='$(dist_gpginsttest_trusted_DATA)'; test -n "$(gpginsttest_trusteddir)" || list=; \ @@ -8055,6 +8118,13 @@ tests/test-pull-repeated.sh.log: tests/test-pull-repeated.sh --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) +tests/test-pull-sizes.sh.log: tests/test-pull-sizes.sh + @p='tests/test-pull-sizes.sh'; \ + b='tests/test-pull-sizes.sh'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) tests/test-pull-untrusted.sh.log: tests/test-pull-untrusted.sh @p='tests/test-pull-untrusted.sh'; \ b='tests/test-pull-untrusted.sh'; \ @@ -8753,7 +8823,7 @@ install-binPROGRAMS: install-libLTLIBRARIES installdirs: installdirs-recursive installdirs-am: - for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(installed_testdir)" "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(ostree_bootdir)" "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(systemdsystemgeneratordir)" "$(DESTDIR)$(installed_testdir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(privlibdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(dracutmoddir)" "$(DESTDIR)$(installed_testdir)" "$(DESTDIR)$(mkinitcpioinstalldir)" "$(DESTDIR)$(ostree_bootdir)" "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(completionsdir)" "$(DESTDIR)$(gpginsttestdir)" "$(DESTDIR)$(gpginsttest_trusteddir)" "$(DESTDIR)$(gpgvinsttestdir)" "$(DESTDIR)$(systemdtmpfilesdir)" "$(DESTDIR)$(dracutconfdir)" "$(DESTDIR)$(girdir)" "$(DESTDIR)$(gpgreadmedir)" "$(DESTDIR)$(installed_testdir)" "$(DESTDIR)$(installed_test_metadir)" "$(DESTDIR)$(mkinitcpioconfdir)" "$(DESTDIR)$(installed_testdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(systemdsystemunitdir)" "$(DESTDIR)$(typelibdir)" "$(DESTDIR)$(libostreeincludedir)"; do \ + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(installed_testdir)" "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(ostree_bootdir)" "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(systemdsystemgeneratordir)" "$(DESTDIR)$(installed_testdir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(privlibdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(dracutmoddir)" "$(DESTDIR)$(installed_testdir)" "$(DESTDIR)$(mkinitcpioinstalldir)" "$(DESTDIR)$(ostree_bootdir)" "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(completionsdir)" "$(DESTDIR)$(gpginsttestdir)" "$(DESTDIR)$(gpginsttest_revocdir)" "$(DESTDIR)$(gpginsttest_trusteddir)" "$(DESTDIR)$(gpgvinsttestdir)" "$(DESTDIR)$(systemdtmpfilesdir)" "$(DESTDIR)$(dracutconfdir)" "$(DESTDIR)$(girdir)" "$(DESTDIR)$(gpgreadmedir)" "$(DESTDIR)$(installed_testdir)" "$(DESTDIR)$(installed_test_metadir)" "$(DESTDIR)$(mkinitcpioconfdir)" "$(DESTDIR)$(installed_testdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(systemdsystemunitdir)" "$(DESTDIR)$(typelibdir)" "$(DESTDIR)$(libostreeincludedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) @@ -8912,6 +8982,7 @@ distclean: distclean-recursive -rm -f src/libostree/$(DEPDIR)/libostree_1_la-ostree-varint.Plo -rm -f src/libostree/$(DEPDIR)/tests_test_bloom-ostree-bloom.Po -rm -f src/libostree/$(DEPDIR)/tests_test_checksum-ostree-core.Po + -rm -f src/libostree/$(DEPDIR)/tests_test_checksum-ostree-varint.Po -rm -f src/libostree/$(DEPDIR)/tests_test_kargs-ostree-kernel-args.Po -rm -f src/libostree/$(DEPDIR)/tests_test_lzma-ostree-lzma-common.Po -rm -f src/libostree/$(DEPDIR)/tests_test_lzma-ostree-lzma-compressor.Po @@ -9042,6 +9113,7 @@ info-am: install-data-am: install-dist_completionsDATA \ install-dist_gpginsttestDATA \ + install-dist_gpginsttest_revocDATA \ install-dist_gpginsttest_trustedDATA \ install-dist_gpgvinsttestDATA install-dist_systemdtmpfilesDATA \ install-dracutconfDATA install-dracutmodSCRIPTS \ @@ -9178,6 +9250,7 @@ maintainer-clean: maintainer-clean-recursive -rm -f src/libostree/$(DEPDIR)/libostree_1_la-ostree-varint.Plo -rm -f src/libostree/$(DEPDIR)/tests_test_bloom-ostree-bloom.Po -rm -f src/libostree/$(DEPDIR)/tests_test_checksum-ostree-core.Po + -rm -f src/libostree/$(DEPDIR)/tests_test_checksum-ostree-varint.Po -rm -f src/libostree/$(DEPDIR)/tests_test_kargs-ostree-kernel-args.Po -rm -f src/libostree/$(DEPDIR)/tests_test_lzma-ostree-lzma-common.Po -rm -f src/libostree/$(DEPDIR)/tests_test_lzma-ostree-lzma-compressor.Po @@ -9308,6 +9381,7 @@ ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS \ uninstall-dist_completionsDATA uninstall-dist_gpginsttestDATA \ + uninstall-dist_gpginsttest_revocDATA \ uninstall-dist_gpginsttest_trustedDATA \ uninstall-dist_gpgvinsttestDATA \ uninstall-dist_systemdtmpfilesDATA uninstall-dracutconfDATA \ @@ -9351,6 +9425,7 @@ uninstall-man: uninstall-man1 uninstall-man5 install install-am install-binPROGRAMS install-binSCRIPTS \ install-data install-data-am install-data-hook \ install-dist_completionsDATA install-dist_gpginsttestDATA \ + install-dist_gpginsttest_revocDATA \ install-dist_gpginsttest_trustedDATA \ install-dist_gpgvinsttestDATA install-dist_systemdtmpfilesDATA \ install-dracutconfDATA install-dracutmodSCRIPTS install-dvi \ @@ -9376,6 +9451,7 @@ uninstall-man: uninstall-man1 uninstall-man5 recheck tags tags-am uninstall uninstall-am \ uninstall-binPROGRAMS uninstall-binSCRIPTS \ uninstall-dist_completionsDATA uninstall-dist_gpginsttestDATA \ + uninstall-dist_gpginsttest_revocDATA \ uninstall-dist_gpginsttest_trustedDATA \ uninstall-dist_gpgvinsttestDATA \ uninstall-dist_systemdtmpfilesDATA uninstall-dracutconfDATA \ diff --git a/apidoc/html/index.html b/apidoc/html/index.html index f462ee84..9abed893 100644 --- a/apidoc/html/index.html +++ b/apidoc/html/index.html @@ -14,7 +14,7 @@
-

for OSTree 2019.6

+

for OSTree 2019.7


diff --git a/apidoc/html/ostree-Core-repository-independent-functions.html b/apidoc/html/ostree-Core-repository-independent-functions.html index f5a7ffe6..b3468b8b 100644 --- a/apidoc/html/ostree-Core-repository-independent-functions.html +++ b/apidoc/html/ostree-Core-repository-independent-functions.html @@ -430,6 +430,38 @@ gboolean +ostree_commit_get_object_sizes () + + + + +OstreeCommitSizesEntry * + + +ostree_commit_sizes_entry_new () + + + + +OstreeCommitSizesEntry * + + +ostree_commit_sizes_entry_copy () + + + + +void + + +ostree_commit_sizes_entry_free () + + + + +gboolean + + ostree_check_version () @@ -500,6 +532,10 @@ #define OSTREE_SUMMARY_GVARIANT_FORMAT + +  +OstreeCommitSizesEntry + @@ -2386,6 +2422,151 @@ is not well-formed.


+

ostree_commit_get_object_sizes ()

+
gboolean
+ostree_commit_get_object_sizes (GVariant *commit_variant,
+                                GPtrArray **out_sizes_entries,
+                                GError **error);
+

Reads a commit's "ostree.sizes" metadata and returns an array of +OstreeCommitSizesEntry in out_sizes_entries +. Each element +represents an object in the commit. If the commit does not contain +the "ostree.sizes" metadata, a G_IO_ERROR_NOT_FOUND error will be +returned.

+
+

Parameters

+
+++++ + + + + + + + + + + + + + + + + + +

commit_variant

variant of type OSTREE_OBJECT_TYPE_COMMIT.

[not nullable]

out_sizes_entries

return location for an array of object size entries.

[out][element-type OstreeCommitSizesEntry][transfer container][optional]

error

Error

 
+
+

Since: 2020.1

+
+
+
+

ostree_commit_sizes_entry_new ()

+
OstreeCommitSizesEntry *
+ostree_commit_sizes_entry_new (const gchar *checksum,
+                               OstreeObjectType objtype,
+                               guint64 unpacked,
+                               guint64 archived);
+

Create a new OstreeCommitSizesEntry for representing an object in a +commit's "ostree.sizes" metadata.

+
+

Parameters

+
+++++ + + + + + + + + + + + + + + + + + + + + + + +

checksum

object checksum.

[not nullable]

objtype

object type

 

unpacked

unpacked object size

 

archived

compressed object size

 
+
+
+

Returns

+

a new OstreeCommitSizesEntry.

+

[transfer full][nullable]

+
+

Since: 2020.1

+
+
+
+

ostree_commit_sizes_entry_copy ()

+
OstreeCommitSizesEntry *
+ostree_commit_sizes_entry_copy (const OstreeCommitSizesEntry *entry);
+

Create a copy of the given entry +.

+
+

Parameters

+
+++++ + + + + + +

entry

an OstreeCommitSizesEntry.

[not nullable]
+
+
+

Returns

+

a new copy of entry +.

+

[transfer full][nullable]

+
+

Since: 2020.1

+
+
+
+

ostree_commit_sizes_entry_free ()

+
void
+ostree_commit_sizes_entry_free (OstreeCommitSizesEntry *entry);
+

Free given entry +.

+
+

Parameters

+
+++++ + + + + + +

entry

an OstreeCommitSizesEntry.

[transfer full]
+
+

Since: 2020.1

+
+
+

ostree_check_version ()

gboolean
 ostree_check_version (guint required_year,
@@ -2620,6 +2801,52 @@ stale and should be re-downloaded if possible (similar to the HTTP
 
  • key: ostree.commit.timestamp, value: t, timestamp (seconds since the Unix epoch in UTC, big-endian) when the commit was committed

+
+
+

OstreeCommitSizesEntry

+
typedef struct {
+  gchar *checksum;
+  OstreeObjectType objtype;
+  guint64 unpacked;
+  guint64 archived;
+} OstreeCommitSizesEntry;
+
+

Structure representing an entry in the "ostree.sizes" commit metadata. Each +entry corresponds to an object in the associated commit.

+
+

Members

+
+++++ + + + + + + + + + + + + + + + + + + + + + + +

gchar *checksum;

object checksum.

[not nullable]

OstreeObjectType objtype;

object type

 

guint64 unpacked;

unpacked object size

 

guint64 archived;

compressed object size

 
+
+

Since: 2020.1

+
diff --git a/apidoc/html/ostree-Root-partition-mount-point.html b/apidoc/html/ostree-Root-partition-mount-point.html index 492aef96..cf860858 100644 --- a/apidoc/html/ostree-Root-partition-mount-point.html +++ b/apidoc/html/ostree-Root-partition-mount-point.html @@ -57,6 +57,14 @@ +gboolean + + +ostree_sysroot_initialize () + + + + GFile * @@ -129,6 +137,22 @@ +void + + +ostree_sysroot_set_mount_namespace_in_use () + + + + +gboolean + + +ostree_sysroot_is_booted () + + + + int @@ -438,6 +462,33 @@ ostree_sysroot_new_default (void<
+

ostree_sysroot_initialize ()

+
gboolean
+ostree_sysroot_initialize (OstreeSysroot *self,
+                           GError **error);
+

Subset of ostree_sysroot_load(); performs basic initialization. Notably, one +can invoke ostree_sysroot_get_fd() after calling this function.

+

It is not necessary to call this function if ostree_sysroot_load() is +invoked.

+
+

Parameters

+
+++++ + + + + + +

self

sysroot

 
+
+

Since: 2020.1

+
+
+

ostree_sysroot_get_path ()

GFile *
 ostree_sysroot_get_path (OstreeSysroot *self);
@@ -737,12 +788,57 @@ may not be predictable.


+

ostree_sysroot_set_mount_namespace_in_use ()

+
void
+ostree_sysroot_set_mount_namespace_in_use
+                               (OstreeSysroot *self);
+

If this function is invoked, then libostree will assume that +a private Linux mount namespace has been created by the process. +The primary use case for this is to have e.g. /sysroot mounted +read-only by default.

+

If this function has been called, then when a function which requires +writable access is invoked, libostree will automatically remount as writable +any mount points on which it operates. This currently is just /sysroot and +/boot.

+

If you invoke this function, it must be before ostree_sysroot_load(); it may +be invoked before or after ostree_sysroot_initialize().

+

Since: 2020.1

+
+
+
+

ostree_sysroot_is_booted ()

+
gboolean
+ostree_sysroot_is_booted (OstreeSysroot *self);
+

Can only be invoked after ostree_sysroot_initialize().

+
+

Parameters

+
+++++ + + + + + +

self

Sysroot

 
+
+
+

Returns

+

TRUE iff the sysroot points to a booted deployment

+
+

Since: 2020.1

+
+
+

ostree_sysroot_get_fd ()

int
 ostree_sysroot_get_fd (OstreeSysroot *self);
-

Access a file descriptor that refers to the root directory of this -sysroot. ostree_sysroot_load() must have been invoked prior to -calling this function.

+

Access a file descriptor that refers to the root directory of this sysroot. +ostree_sysroot_initialize() (or ostree_sysroot_load()) must have been invoked +prior to calling this function.

Parameters

@@ -1113,8 +1209,8 @@ flag in options
OstreeRepo *
 ostree_sysroot_repo (OstreeSysroot *self);

This function is a variant of ostree_sysroot_get_repo() that cannot fail, and -returns a cached repository. Can only be called after ostree_sysroot_load() -has been invoked successfully.

+returns a cached repository. Can only be called after ostree_sysroot_initialize() +or ostree_sysroot_load() has been invoked successfully.

Parameters

diff --git a/apidoc/html/ostree.devhelp2 b/apidoc/html/ostree.devhelp2 index d2d82d7a..3bb2f8ff 100644 --- a/apidoc/html/ostree.devhelp2 +++ b/apidoc/html/ostree.devhelp2 @@ -69,6 +69,10 @@ + + + + @@ -84,6 +88,7 @@ + @@ -262,6 +267,7 @@ + @@ -271,6 +277,8 @@ + + @@ -488,6 +496,9 @@ + + + @@ -506,6 +517,10 @@ + + + + diff --git a/apidoc/html/reference.html b/apidoc/html/reference.html index 1a9ba640..72f6613d 100644 --- a/apidoc/html/reference.html +++ b/apidoc/html/reference.html @@ -302,10 +302,18 @@ ostree_collection_ref_new, function in ostree-ref
+OstreeCommitSizesEntry, struct in Core repository-independent functions +
+
+
ostree_commit_get_content_checksum, function in Core repository-independent functions
+ostree_commit_get_object_sizes, function in Core repository-independent functions +
+
+
ostree_commit_get_parent, function in Core repository-independent functions
@@ -322,6 +330,18 @@ ostree_collection_ref_new, function in ostree-ref
+ostree_commit_sizes_entry_copy, function in Core repository-independent functions +
+
+
+ostree_commit_sizes_entry_free, function in Core repository-independent functions +
+
+
+ostree_commit_sizes_entry_new, function in Core repository-independent functions +
+
+
ostree_content_file_parse, function in Core repository-independent functions
@@ -1724,10 +1744,18 @@ ostree_repo_transaction_set_collection_ref, function in ostree-misc-experimental
+ostree_sysroot_initialize, function in Root partition mount point +
+
+
ostree_sysroot_init_osname, function in Root partition mount point
+ostree_sysroot_is_booted, function in Root partition mount point +
+
+
ostree_sysroot_load, function in Root partition mount point
@@ -1772,6 +1800,10 @@ ostree_repo_transaction_set_collection_ref, function in ostree-misc-experimental
+ostree_sysroot_set_mount_namespace_in_use, function in Root partition mount point +
+
+
ostree_sysroot_simple_write_deployment, function in Root partition mount point
diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index f99c4df5..32cf5228 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -151,7 +151,14 @@ ostree_validate_structureof_dirmeta ostree_commit_get_parent ostree_commit_get_timestamp ostree_commit_get_content_checksum +ostree_commit_get_object_sizes +OstreeCommitSizesEntry +ostree_commit_sizes_entry_new +ostree_commit_sizes_entry_copy +ostree_commit_sizes_entry_free ostree_check_version + +ostree_commit_sizes_entry_get_type
@@ -499,6 +506,7 @@ ostree_sepolicy_get_type OstreeSysroot ostree_sysroot_new ostree_sysroot_new_default +ostree_sysroot_initialize ostree_sysroot_get_path ostree_sysroot_load ostree_sysroot_load_if_changed @@ -508,6 +516,8 @@ ostree_sysroot_lock_async ostree_sysroot_lock_finish ostree_sysroot_unlock ostree_sysroot_unload +ostree_sysroot_set_mount_namespace_in_use +ostree_sysroot_is_booted ostree_sysroot_get_fd ostree_sysroot_ensure_initialized ostree_sysroot_get_bootversion diff --git a/apidoc/ostree.types b/apidoc/ostree.types index c2faa1f4..aa2b9f0b 100644 --- a/apidoc/ostree.types +++ b/apidoc/ostree.types @@ -4,6 +4,7 @@ ostree_bootconfig_parser_get_type ostree_chain_input_stream_get_type ostree_checksum_input_stream_get_type ostree_collection_ref_get_type +ostree_commit_sizes_entry_get_type ostree_deployment_get_type ostree_diff_item_get_type ostree_gpg_verify_result_get_type diff --git a/apidoc/version.xml b/apidoc/version.xml index a6415107..36d21d4f 100644 --- a/apidoc/version.xml +++ b/apidoc/version.xml @@ -1 +1 @@ -2019.6 \ No newline at end of file +2019.7 \ No newline at end of file diff --git a/bash/ostree b/bash/ostree index fc429983..4aec588b 100644 --- a/bash/ostree +++ b/bash/ostree @@ -1445,6 +1445,7 @@ _ostree_show() { local boolean_options=" $main_boolean_options --print-related + --print-sizes --raw " diff --git a/config.h.in b/config.h.in index 1eef9ff2..ac9b3713 100644 --- a/config.h.in +++ b/config.h.in @@ -82,6 +82,9 @@ /* Define if we have libsystemd.pc */ #undef HAVE_LIBSYSTEMD +/* Define to 1 if you have the header file. */ +#undef HAVE_LINUX_FSVERITY_H + /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H diff --git a/configure b/configure index 4ae7dd2f..0544942f 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for libostree 2019.6. +# Generated by GNU Autoconf 2.69 for libostree 2020.2. # # Report bugs to . # @@ -590,8 +590,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='libostree' PACKAGE_TARNAME='libostree' -PACKAGE_VERSION='2019.6' -PACKAGE_STRING='libostree 2019.6' +PACKAGE_VERSION='2020.2' +PACKAGE_STRING='libostree 2020.2' PACKAGE_BUGREPORT='walters@verbum.org' PACKAGE_URL='' @@ -654,8 +654,6 @@ BUILDOPT_SYSTEMD_FALSE BUILDOPT_SYSTEMD_TRUE systemdsystemgeneratordir systemdsystemunitdir -BUILDOPT_LIBSYSTEMD_FALSE -BUILDOPT_LIBSYSTEMD_TRUE LIBSYSTEMD_LIBS LIBSYSTEMD_CFLAGS BUILDOPT_MKINITCPIO_FALSE @@ -1554,7 +1552,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures libostree 2019.6 to adapt to many kinds of systems. +\`configure' configures libostree 2020.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1624,7 +1622,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libostree 2019.6:";; + short | recursive ) echo "Configuration of libostree 2020.2:";; esac cat <<\_ACEOF @@ -1883,7 +1881,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libostree configure 2019.6 +libostree configure 2020.2 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2355,7 +2353,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libostree $as_me 2019.6, which was +It was created by libostree $as_me 2020.2, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3223,7 +3221,7 @@ fi # Define the identity of the package. PACKAGE='libostree' - VERSION='2019.6' + VERSION='2020.2' # Some tools Automake needs. @@ -5955,11 +5953,11 @@ done test -n "$YACC" || YACC="yacc" -YEAR_VERSION=2019 +YEAR_VERSION=2020 -RELEASE_VERSION=6 +RELEASE_VERSION=2 -PACKAGE_VERSION=2019.6 +PACKAGE_VERSION=2020.2 if echo "$CFLAGS" | grep -q -E -e '-Werror($| )'; then : @@ -16082,6 +16080,22 @@ LIBARCHIVE_DEPENDENCY="libarchive >= 2.8.0" # What's in RHEL7.2. FUSE_DEPENDENCY="fuse >= 2.9.2" +for ac_header in linux/fsverity.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "linux/fsverity.h" "ac_cv_header_linux_fsverity_h" "$ac_includes_default" +if test "x$ac_cv_header_linux_fsverity_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LINUX_FSVERITY_H 1 +_ACEOF + +fi + +done + +if test x$ac_cv_header_linux_fsverity_h = xyes ; then : + OSTREE_FEATURES="$OSTREE_FEATURES ex-fsverity" +fi + # check for gtk-doc @@ -17865,18 +17879,9 @@ fi else with_libsystemd=no fi - if test $with_libsystemd != no; then - BUILDOPT_LIBSYSTEMD_TRUE= - BUILDOPT_LIBSYSTEMD_FALSE='#' -else - BUILDOPT_LIBSYSTEMD_TRUE='#' - BUILDOPT_LIBSYSTEMD_FALSE= -fi +if test "x$with_libsystemd" = "xyes"; then : -if test "x$have_libsystemd" = "xyes"; then : - - with_systemd=yes # Check whether --with-systemdsystemunitdir was given. if test "${with_systemdsystemunitdir+set}" = set; then : @@ -17907,7 +17912,7 @@ fi fi fi - if test x$with_systemd = xyes; then + if test x$with_libsystemd = xyes; then BUILDOPT_SYSTEMD_TRUE= BUILDOPT_SYSTEMD_FALSE='#' else @@ -17915,7 +17920,7 @@ else BUILDOPT_SYSTEMD_FALSE= fi - if test x$with_systemd = xyes && test x$with_libmount = xyes; then + if test x$with_libsystemd = xyes && test x$with_libmount = xyes; then BUILDOPT_SYSTEMD_AND_LIBMOUNT_TRUE= BUILDOPT_SYSTEMD_AND_LIBMOUNT_FALSE='#' else @@ -17928,6 +17933,7 @@ if test -z "$BUILDOPT_SYSTEMD_AND_LIBMOUNT_TRUE"; then : $as_echo "#define BUILDOPT_LIBSYSTEMD_AND_LIBMOUNT 1" >>confdefs.h fi +if test x$with_libsystemd = xyes; then OSTREE_FEATURES="$OSTREE_FEATURES systemd"; fi # Check whether --with-builtin-grub2-mkconfig was given. @@ -18413,10 +18419,6 @@ if test -z "${BUILDOPT_MKINITCPIO_TRUE}" && test -z "${BUILDOPT_MKINITCPIO_FALSE as_fn_error $? "conditional \"BUILDOPT_MKINITCPIO\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi -if test -z "${BUILDOPT_LIBSYSTEMD_TRUE}" && test -z "${BUILDOPT_LIBSYSTEMD_FALSE}"; then - as_fn_error $? "conditional \"BUILDOPT_LIBSYSTEMD\" was never defined. -Usually this means the macro was only invoked conditionally." "$LINENO" 5 -fi if test -z "${BUILDOPT_SYSTEMD_TRUE}" && test -z "${BUILDOPT_SYSTEMD_FALSE}"; then as_fn_error $? "conditional \"BUILDOPT_SYSTEMD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 @@ -18842,7 +18844,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by libostree $as_me 2019.6, which was +This file was extended by libostree $as_me 2020.2, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -18908,7 +18910,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -libostree config.status 2019.6 +libostree config.status 2020.2 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -20610,8 +20612,9 @@ echo " HTTP backend: $fetcher_backend \"ostree trivial-httpd\": $enable_trivial_httpd_cmdline SELinux: $with_selinux + fs-verity: $ac_cv_header_linux_fsverity_h cryptographic checksums: $with_crypto - systemd: $have_libsystemd + systemd: $with_libsystemd libmount: $with_libmount libarchive (parse tar files directly): $with_libarchive static deltas: yes (always enabled now) diff --git a/configure.ac b/configure.ac index 46a900f5..36995403 100644 --- a/configure.ac +++ b/configure.ac @@ -2,12 +2,12 @@ AC_PREREQ([2.63]) dnl To do a release: follow the instructions to update libostree-released.sym from dnl libostree-devel.sym, update the checksum in test-symbols.sh, set is_release_build=yes dnl below. Then make another post-release commit to bump the version and set -dnl is_release_build=no. +dnl is_release_build=yes dnl Seed the release notes with `git-shortlog-with-prs ..`. Then use dnl `git-evtag` to create the tag and push it. Finally, create a GitHub release and attach dnl the tarball from `make dist`. -m4_define([year_version], [2019]) -m4_define([release_version], [6]) +m4_define([year_version], [2020]) +m4_define([release_version], [2]) m4_define([package_version], [year_version.release_version]) AC_INIT([libostree], [package_version], [walters@verbum.org]) is_release_build=yes @@ -246,6 +246,10 @@ LIBARCHIVE_DEPENDENCY="libarchive >= 2.8.0" # What's in RHEL7.2. FUSE_DEPENDENCY="fuse >= 2.9.2" +AC_CHECK_HEADERS([linux/fsverity.h]) +AS_IF([test x$ac_cv_header_linux_fsverity_h = xyes ], + [OSTREE_FEATURES="$OSTREE_FEATURES ex-fsverity"]) + # check for gtk-doc m4_ifdef([GTK_DOC_CHECK], [ GTK_DOC_CHECK([1.15], [--flavour no-tmpl]) @@ -509,10 +513,8 @@ AS_IF([ test x$with_libsystemd != xno ], [ with_libsystemd=no ]) ], [ with_libsystemd=no ]) -AM_CONDITIONAL(BUILDOPT_LIBSYSTEMD, test $with_libsystemd != no) -AS_IF([test "x$have_libsystemd" = "xyes"], [ - with_systemd=yes +AS_IF([test "x$with_libsystemd" = "xyes"], [ AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]), [], @@ -528,11 +530,12 @@ AS_IF([test "x$have_libsystemd" = "xyes"], [ AC_SUBST([systemdsystemgeneratordir], [$with_systemdsystemgeneratordir]) ]) ]) -AM_CONDITIONAL(BUILDOPT_SYSTEMD, test x$with_systemd = xyes) +AM_CONDITIONAL(BUILDOPT_SYSTEMD, test x$with_libsystemd = xyes) dnl If we have both, we use the "new /var" model with ostree-system-generator -AM_CONDITIONAL(BUILDOPT_SYSTEMD_AND_LIBMOUNT,[test x$with_systemd = xyes && test x$with_libmount = xyes]) +AM_CONDITIONAL(BUILDOPT_SYSTEMD_AND_LIBMOUNT,[test x$with_libsystemd = xyes && test x$with_libmount = xyes]) AM_COND_IF(BUILDOPT_SYSTEMD_AND_LIBMOUNT, AC_DEFINE([BUILDOPT_LIBSYSTEMD_AND_LIBMOUNT], 1, [Define if systemd and libmount])) +if test x$with_libsystemd = xyes; then OSTREE_FEATURES="$OSTREE_FEATURES systemd"; fi AC_ARG_WITH(builtin-grub2-mkconfig, AS_HELP_STRING([--with-builtin-grub2-mkconfig], @@ -619,8 +622,9 @@ echo " HTTP backend: $fetcher_backend \"ostree trivial-httpd\": $enable_trivial_httpd_cmdline SELinux: $with_selinux + fs-verity: $ac_cv_header_linux_fsverity_h cryptographic checksums: $with_crypto - systemd: $have_libsystemd + systemd: $with_libsystemd libmount: $with_libmount libarchive (parse tar files directly): $with_libarchive static deltas: yes (always enabled now) diff --git a/man/ostree-pull.xml b/man/ostree-pull.xml index 84d75be3..0606f690 100644 --- a/man/ostree-pull.xml +++ b/man/ostree-pull.xml @@ -143,6 +143,14 @@ Boston, MA 02111-1307, USA. Description + + Without --mirror, this command will create new refs + under remotes/REMOTE/ directory + for each pulled branch unless they are already created. Such + refs can be then referenced by REMOTE:BRANCH in + ostree subcommands (e.g. ostree log origin:exampleos/x86_64/standard). + + This command can retrieve just a specific commit, or go all the way to performing a full mirror of the remote diff --git a/man/ostree-show.xml b/man/ostree-show.xml index a3d9aa4a..a28f704c 100644 --- a/man/ostree-show.xml +++ b/man/ostree-show.xml @@ -99,6 +99,17 @@ Boston, MA 02111-1307, USA. + + + + + Show the commit size metadata. This in only supported for + commits that contain ostree.sizes + metadata. This can be included when creating commits with + ostree commit --generate-sizes. + + + diff --git a/src/boot/ostree-remount.service b/src/boot/ostree-remount.service index b98110c2..4c3ed94a 100644 --- a/src/boot/ostree-remount.service +++ b/src/boot/ostree-remount.service @@ -22,12 +22,12 @@ DefaultDependencies=no ConditionKernelCommandLine=ostree OnFailure=emergency.target Conflicts=umount.target -After=-.mount +# Run after core mounts +After=-.mount var.mount After=systemd-remount-fs.service +# But we run *before* most other core bootup services that need write access to /etc and /var Before=local-fs.target umount.target -# Other early boot units that need to write to /var Before=systemd-random-seed.service plymouth-read-write.service systemd-journal-flush.service -# tmpfiles.d usually needs write access to a few places Before=systemd-tmpfiles-setup.service [Service] diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index a5d15e93..aa3392cc 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -18,15 +18,17 @@ ***/ /* Add new symbols here. Release commits should copy this section into -released.sym. */ -LIBOSTREE_2019.7 { -} LIBOSTREE_2019.6; +LIBOSTREE_2020.2 { +global: + someostree_symbol_deleteme; +} LIBOSTREE_2020.1; /* Stub section for the stable release *after* this development one; don't * edit this other than to update the year. This is just a copy/paste * source. Replace $LASTSTABLE with the last stable version, and $NEWVERSION * with whatever the next version with new symbols will be. -LIBOSTREE_2019.$NEWVERSION { +LIBOSTREE_2020.$NEWVERSION { global: someostree_symbol_deleteme; -} LIBOSTREE_2019.$LASTSTABLE; +} LIBOSTREE_2020.$LASTSTABLE; */ diff --git a/src/libostree/libostree-released.sym b/src/libostree/libostree-released.sym index e81a6595..33d4d0e3 100644 --- a/src/libostree/libostree-released.sym +++ b/src/libostree/libostree-released.sym @@ -581,6 +581,20 @@ LIBOSTREE_2019.6 { ostree_async_progress_copy_state; } LIBOSTREE_2019.4; +LIBOSTREE_2020.1 { +global: + ostree_commit_get_object_sizes; + ostree_commit_sizes_entry_copy; + ostree_commit_sizes_entry_free; + ostree_commit_sizes_entry_get_type; + ostree_commit_sizes_entry_new; + ostree_sysroot_initialize; + ostree_sysroot_is_booted; + ostree_sysroot_set_mount_namespace_in_use; +} LIBOSTREE_2019.6; + +/* No new symbols in 2020.2 */ + /* NOTE: Only add more content here in release commits! See the * comments at the top of this file. */ diff --git a/src/libostree/ostree-autocleanups.h b/src/libostree/ostree-autocleanups.h index c07f88a8..c9692ebe 100644 --- a/src/libostree/ostree-autocleanups.h +++ b/src/libostree/ostree-autocleanups.h @@ -49,6 +49,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoDevInoCache, ostree_repo_devino_cache_u G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeAsyncProgress, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeBootconfigParser, g_object_unref) +G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeCommitSizesEntry, ostree_commit_sizes_entry_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeDeployment, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeGpgVerifyResult, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeKernelArgs, ostree_kernel_args_free) diff --git a/src/libostree/ostree-core-private.h b/src/libostree/ostree-core-private.h index 43cf22c4..5d9d948a 100644 --- a/src/libostree/ostree-core-private.h +++ b/src/libostree/ostree-core-private.h @@ -30,6 +30,12 @@ G_BEGIN_DECLS /* It's what gzip does, 9 is too slow */ #define OSTREE_ARCHIVE_DEFAULT_COMPRESSION_LEVEL (6) +/* Note the permissive group bits. We want to be liberal here and let individual machines + * narrow permissions as needed via umask. This is important in setups where group ownership + * can matter for repo management (like OpenShift). */ +#define DEFAULT_DIRECTORY_MODE 0775 +#define DEFAULT_REGFILE_MODE 0660 + /* This file contains private implementation data format definitions * read by multiple implementation .c files. */ @@ -102,6 +108,9 @@ _ostree_checksum_inplace_from_bytes_v (GVariant *csum_v, char *buf) */ #define _OSTREE_LOOSE_PATH_MAX (256) +/* GVariant format for ostree.sizes metadata entries. */ +#define _OSTREE_OBJECT_SIZES_ENTRY_SIGNATURE "ay" + char * _ostree_get_relative_object_path (const char *checksum, OstreeObjectType type, diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c index 3d16757e..48815bd3 100644 --- a/src/libostree/ostree-core.c +++ b/src/libostree/ostree-core.c @@ -32,6 +32,7 @@ #include "ostree.h" #include "ostree-core-private.h" #include "ostree-chain-input-stream.h" +#include "ostree-varint.h" #include "otutil.h" /* Generic ABI checks */ @@ -2430,6 +2431,187 @@ ostree_commit_get_content_checksum (GVariant *commit_variant) return g_strdup (hexdigest); } +G_DEFINE_BOXED_TYPE (OstreeCommitSizesEntry, ostree_commit_sizes_entry, + ostree_commit_sizes_entry_copy, ostree_commit_sizes_entry_free) + +/** + * ostree_commit_sizes_entry_new: + * @checksum: (not nullable): object checksum + * @objtype: object type + * @unpacked: unpacked object size + * @archived: compressed object size + * + * Create a new #OstreeCommitSizesEntry for representing an object in a + * commit's "ostree.sizes" metadata. + * + * Returns: (transfer full) (nullable): a new #OstreeCommitSizesEntry + * Since: 2020.1 + */ +OstreeCommitSizesEntry * +ostree_commit_sizes_entry_new (const gchar *checksum, + OstreeObjectType objtype, + guint64 unpacked, + guint64 archived) +{ + g_return_val_if_fail (checksum == NULL || ostree_validate_checksum_string (checksum, NULL), NULL); + + g_autoptr(OstreeCommitSizesEntry) entry = g_new0 (OstreeCommitSizesEntry, 1); + entry->checksum = g_strdup (checksum); + entry->objtype = objtype; + entry->unpacked = unpacked; + entry->archived = archived; + + return g_steal_pointer (&entry); +} + +/** + * ostree_commit_sizes_entry_copy: + * @entry: (not nullable): an #OstreeCommitSizesEntry + * + * Create a copy of the given @entry. + * + * Returns: (transfer full) (nullable): a new copy of @entry + * Since: 2020.1 + */ +OstreeCommitSizesEntry * +ostree_commit_sizes_entry_copy (const OstreeCommitSizesEntry *entry) +{ + g_return_val_if_fail (entry != NULL, NULL); + + return ostree_commit_sizes_entry_new (entry->checksum, + entry->objtype, + entry->unpacked, + entry->archived); +} + +/** + * ostree_commit_sizes_entry_free: + * @entry: (transfer full): an #OstreeCommitSizesEntry + * + * Free given @entry. + * + * Since: 2020.1 + */ +void +ostree_commit_sizes_entry_free (OstreeCommitSizesEntry *entry) +{ + g_return_if_fail (entry != NULL); + + g_free (entry->checksum); + g_free (entry); +} + +static gboolean +read_sizes_entry (GVariant *entry, + OstreeCommitSizesEntry **out_sizes, + GError **error) +{ + gsize entry_size = g_variant_get_size (entry); + g_return_val_if_fail (entry_size >= OSTREE_SHA256_DIGEST_LEN + 2, FALSE); + + const guchar *buffer = g_variant_get_data (entry); + if (buffer == NULL) + return glnx_throw (error, "Could not read ostree.sizes metadata entry"); + + char checksum[OSTREE_SHA256_STRING_LEN + 1]; + ostree_checksum_inplace_from_bytes (buffer, checksum); + buffer += OSTREE_SHA256_DIGEST_LEN; + entry_size -= OSTREE_SHA256_DIGEST_LEN; + + gsize bytes_read = 0; + guint64 archived = 0; + if (!_ostree_read_varuint64 (buffer, entry_size, &archived, &bytes_read)) + return glnx_throw (error, "Unexpected EOF reading ostree.sizes varint"); + buffer += bytes_read; + entry_size -= bytes_read; + + guint64 unpacked = 0; + if (!_ostree_read_varuint64 (buffer, entry_size, &unpacked, &bytes_read)) + return glnx_throw (error, "Unexpected EOF reading ostree.sizes varint"); + buffer += bytes_read; + entry_size -= bytes_read; + + /* On newer commits, an additional byte is used for the object type. */ + OstreeObjectType objtype; + if (entry_size > 0) + { + objtype = *buffer; + if (objtype < OSTREE_OBJECT_TYPE_FILE || objtype > OSTREE_OBJECT_TYPE_LAST) + return glnx_throw (error, "Unexpected ostree.sizes object type %u", + objtype); + buffer++; + entry_size--; + } + else + { + /* Assume the object is a file. */ + objtype = OSTREE_OBJECT_TYPE_FILE; + } + + g_autoptr(OstreeCommitSizesEntry) sizes = ostree_commit_sizes_entry_new (checksum, + objtype, + unpacked, + archived); + + if (out_sizes != NULL) + *out_sizes = g_steal_pointer (&sizes); + + return TRUE; +} + +/** + * ostree_commit_get_object_sizes: + * @commit_variant: (not nullable): variant of type %OSTREE_OBJECT_TYPE_COMMIT + * @out_sizes_entries: (out) (element-type OstreeCommitSizesEntry) (transfer container) (optional): + * return location for an array of object size entries + * @error: Error + * + * Reads a commit's "ostree.sizes" metadata and returns an array of + * #OstreeCommitSizesEntry in @out_sizes_entries. Each element + * represents an object in the commit. If the commit does not contain + * the "ostree.sizes" metadata, a %G_IO_ERROR_NOT_FOUND error will be + * returned. + * + * Since: 2020.1 + */ +gboolean +ostree_commit_get_object_sizes (GVariant *commit_variant, + GPtrArray **out_sizes_entries, + GError **error) +{ + g_return_val_if_fail (commit_variant != NULL, FALSE); + + g_autoptr(GVariant) metadata = g_variant_get_child_value (commit_variant, 0); + g_autoptr(GVariant) sizes_variant = + g_variant_lookup_value (metadata, "ostree.sizes", + G_VARIANT_TYPE ("a" _OSTREE_OBJECT_SIZES_ENTRY_SIGNATURE)); + if (sizes_variant == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "No metadata key ostree.sizes in commit"); + return FALSE; + } + + g_autoptr(GPtrArray) sizes_entries = + g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_commit_sizes_entry_free); + g_autoptr(GVariant) entry = NULL; + GVariantIter entry_iter; + g_variant_iter_init (&entry_iter, sizes_variant); + while ((entry = g_variant_iter_next_value (&entry_iter))) + { + OstreeCommitSizesEntry *sizes_entry = NULL; + if (!read_sizes_entry (entry, &sizes_entry, error)) + return FALSE; + g_clear_pointer (&entry, g_variant_unref); + g_ptr_array_add (sizes_entries, sizes_entry); + } + + if (out_sizes_entries != NULL) + *out_sizes_entries = g_steal_pointer (&sizes_entries); + + return TRUE; +} + /* Used in pull/deploy to validate we're not being downgraded */ gboolean _ostree_compare_timestamps (const char *current_rev, diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h index 69477a75..540f3232 100644 --- a/src/libostree/ostree-core.h +++ b/src/libostree/ostree-core.h @@ -521,6 +521,43 @@ guint64 ostree_commit_get_timestamp (GVariant *commit_variant); _OSTREE_PUBLIC gchar * ostree_commit_get_content_checksum (GVariant *commit_variant); +/** + * OstreeCommitSizesEntry: + * @checksum: (not nullable): object checksum + * @objtype: object type + * @unpacked: unpacked object size + * @archived: compressed object size + * + * Structure representing an entry in the "ostree.sizes" commit metadata. Each + * entry corresponds to an object in the associated commit. + * + * Since: 2020.1 + */ +typedef struct { + gchar *checksum; + OstreeObjectType objtype; + guint64 unpacked; + guint64 archived; +} OstreeCommitSizesEntry; + +_OSTREE_PUBLIC +GType ostree_commit_sizes_entry_get_type (void); + +_OSTREE_PUBLIC +OstreeCommitSizesEntry *ostree_commit_sizes_entry_new (const gchar *checksum, + OstreeObjectType objtype, + guint64 unpacked, + guint64 archived); +_OSTREE_PUBLIC +OstreeCommitSizesEntry *ostree_commit_sizes_entry_copy (const OstreeCommitSizesEntry *entry); +_OSTREE_PUBLIC +void ostree_commit_sizes_entry_free (OstreeCommitSizesEntry *entry); + +_OSTREE_PUBLIC +gboolean ostree_commit_get_object_sizes (GVariant *commit_variant, + GPtrArray **out_sizes_entries, + GError **error); + _OSTREE_PUBLIC gboolean ostree_check_version (guint required_year, guint required_release); diff --git a/src/libostree/ostree-gpg-verify-result.c b/src/libostree/ostree-gpg-verify-result.c index d4d1cef6..67270c82 100644 --- a/src/libostree/ostree-gpg-verify-result.c +++ b/src/libostree/ostree-gpg-verify-result.c @@ -548,14 +548,10 @@ append_expire_info (GString *output_buffer, gint64 exp_timestamp, gboolean expired) { - g_autoptr(GDateTime) date_time_utc = NULL; - g_autoptr(GDateTime) date_time_local = NULL; - g_autofree char *formatted_date_time = NULL; - if (line_prefix != NULL) g_string_append (output_buffer, line_prefix); - date_time_utc = g_date_time_new_from_unix_utc (exp_timestamp); + g_autoptr(GDateTime) date_time_utc = g_date_time_new_from_unix_utc (exp_timestamp); if (date_time_utc == NULL) { g_string_append_printf (output_buffer, @@ -565,8 +561,8 @@ append_expire_info (GString *output_buffer, return; } - date_time_local = g_date_time_to_local (date_time_utc); - formatted_date_time = g_date_time_format (date_time_local, "%c"); + g_autoptr(GDateTime) date_time_local = g_date_time_to_local (date_time_utc); + g_autofree char *formatted_date_time = g_date_time_format (date_time_local, "%c"); if (expired) { @@ -773,8 +769,58 @@ ostree_gpg_verify_result_require_valid_signature (OstreeGpgVerifyResult *result, if (ostree_gpg_verify_result_count_valid (result) == 0) { - g_set_error (error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_MISSING_KEY, - "GPG signatures found, but none are in trusted keyring"); + /* + * Join the description of each failed signature for the error message. + * Only one error code can be returned, so if there was more than one + * signature, use the error of the last one under the assumption that + * it's the most recent and hopefully most likely to be made with a + * valid key. + */ + gint code = OSTREE_GPG_ERROR_NO_SIGNATURE; + g_autoptr(GString) buffer = g_string_sized_new (256); + guint nsigs = ostree_gpg_verify_result_count_all (result); + + if (nsigs == 0) + /* In case an empty result was passed in */ + g_string_append (buffer, "No GPG signatures found"); + else + { + for (int i = nsigs - 1; i >= 0; i--) + { + g_autoptr(GVariant) info = ostree_gpg_verify_result_get_all (result, i); + ostree_gpg_verify_result_describe_variant (info, buffer, "", + OSTREE_GPG_SIGNATURE_FORMAT_DEFAULT); + + if (i == nsigs - 1) + { + gboolean key_missing, key_revoked, key_expired, sig_expired; + g_variant_get_child (info, OSTREE_GPG_SIGNATURE_ATTR_KEY_MISSING, + "b", &key_missing); + g_variant_get_child (info, OSTREE_GPG_SIGNATURE_ATTR_KEY_REVOKED, + "b", &key_revoked); + g_variant_get_child (info, OSTREE_GPG_SIGNATURE_ATTR_KEY_EXPIRED, + "b", &key_expired); + g_variant_get_child (info, OSTREE_GPG_SIGNATURE_ATTR_SIG_EXPIRED, + "b", &sig_expired); + + if (key_missing) + code = OSTREE_GPG_ERROR_MISSING_KEY; + else if (key_revoked) + code = OSTREE_GPG_ERROR_REVOKED_KEY; + else if (key_expired) + code = OSTREE_GPG_ERROR_EXPIRED_KEY; + else if (sig_expired) + code = OSTREE_GPG_ERROR_EXPIRED_SIGNATURE; + else + /* Assume any other issue is a bad signature */ + code = OSTREE_GPG_ERROR_INVALID_SIGNATURE; + } + } + } + + /* Strip any trailing newlines */ + g_strchomp (buffer->str); + g_set_error_literal (error, OSTREE_GPG_ERROR, code, buffer->str); return FALSE; } diff --git a/src/libostree/ostree-gpg-verify-result.h b/src/libostree/ostree-gpg-verify-result.h index 7c71ecdc..6f51ce8a 100644 --- a/src/libostree/ostree-gpg-verify-result.h +++ b/src/libostree/ostree-gpg-verify-result.h @@ -159,6 +159,11 @@ gboolean ostree_gpg_verify_result_require_valid_signature (OstreeGpgVerifyResult * @OSTREE_GPG_ERROR_NO_SIGNATURE: A signature was expected, but not found. * @OSTREE_GPG_ERROR_INVALID_SIGNATURE: A signature was malformed. * @OSTREE_GPG_ERROR_MISSING_KEY: A signature was found, but was created with a key not in the configured keyrings. + * @OSTREE_GPG_ERROR_EXPIRED_SIGNATURE: A signature was expired. Since: 2020.1. + * @OSTREE_GPG_ERROR_EXPIRED_KEY: A signature was found, but the key used to + * sign it has expired. Since: 2020.1. + * @OSTREE_GPG_ERROR_REVOKED_KEY: A signature was found, but the key used to + * sign it has been revoked. Since: 2020.1. * * Errors returned by signature creation and verification operations in OSTree. * These may be returned by any API which creates or verifies signatures. @@ -169,6 +174,9 @@ typedef enum { OSTREE_GPG_ERROR_NO_SIGNATURE = 0, OSTREE_GPG_ERROR_INVALID_SIGNATURE, OSTREE_GPG_ERROR_MISSING_KEY, + OSTREE_GPG_ERROR_EXPIRED_SIGNATURE, + OSTREE_GPG_ERROR_EXPIRED_KEY, + OSTREE_GPG_ERROR_REVOKED_KEY, } OstreeGpgError; /** diff --git a/src/libostree/ostree-impl-system-generator.c b/src/libostree/ostree-impl-system-generator.c index ce40a698..cb9170b3 100644 --- a/src/libostree/ostree-impl-system-generator.c +++ b/src/libostree/ostree-impl-system-generator.c @@ -28,6 +28,7 @@ #ifdef HAVE_LIBMOUNT #include #endif +#include #include #include "otutil.h" @@ -189,8 +190,6 @@ _ostree_impl_system_generator (const char *ostree_cmdline, "[Unit]\n" "Documentation=man:ostree(1)\n" "ConditionKernelCommandLine=!systemd.volatile\n" - /* We need /sysroot mounted writable first */ - "After=ostree-remount.service\n" "Before=local-fs.target\n" "\n" "[Mount]\n" diff --git a/src/libostree/ostree-linuxfsutil.c b/src/libostree/ostree-linuxfsutil.c index d1dde8b4..231ecf76 100644 --- a/src/libostree/ostree-linuxfsutil.c +++ b/src/libostree/ostree-linuxfsutil.c @@ -68,6 +68,10 @@ _ostree_linuxfs_fd_alter_immutable_flag (int fd, } else { + gboolean prev_immutable_state = (flags & EXT2_IMMUTABLE_FL) > 0; + if (prev_immutable_state == new_immutable_state) + return TRUE; /* Nothing to do */ + if (new_immutable_state) flags |= EXT2_IMMUTABLE_FL; else diff --git a/src/libostree/ostree-repo-checkout.c b/src/libostree/ostree-repo-checkout.c index 8dd14640..dc36370f 100644 --- a/src/libostree/ostree-repo-checkout.c +++ b/src/libostree/ostree-repo-checkout.c @@ -92,8 +92,8 @@ checkout_object_for_uncompressed_cache (OstreeRepo *self, if (self->uncompressed_objects_dir_fd == -1) { - if (!glnx_shutil_mkdir_p_at (self->repo_dir_fd, "uncompressed-objects-cache", 0755, - cancellable, error)) + if (!glnx_shutil_mkdir_p_at (self->repo_dir_fd, "uncompressed-objects-cache", + DEFAULT_DIRECTORY_MODE, cancellable, error)) return FALSE; if (!glnx_opendirat (self->repo_dir_fd, "uncompressed-objects-cache", TRUE, &self->uncompressed_objects_dir_fd, diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c index 8c5d9411..dac4573c 100644 --- a/src/libostree/ostree-repo-commit.c +++ b/src/libostree/ostree-repo-commit.c @@ -32,6 +32,10 @@ #include #include #include +#include +#ifdef HAVE_LINUX_FSVERITY_H +#include +#endif #include "otutil.h" #include "ostree.h" @@ -168,6 +172,113 @@ ot_security_smack_reset_fd (int fd) #endif } +/* Wrapper around the fsverity ioctl, compressing the result to + * "success, unsupported or error". This is used for /boot where + * we enable verity if supported. + * */ +gboolean +_ostree_tmpf_fsverity_core (GLnxTmpfile *tmpf, + _OstreeFeatureSupport fsverity_requested, + gboolean *supported, + GError **error) +{ + /* Set this by default to simplify the code below */ + if (supported) + *supported = FALSE; + + if (fsverity_requested == _OSTREE_FEATURE_NO) + return TRUE; + +#ifdef HAVE_LINUX_FSVERITY_H + GLNX_AUTO_PREFIX_ERROR ("fsverity", error); + + /* fs-verity requires a read-only file descriptor */ + if (!glnx_tmpfile_reopen_rdonly (tmpf, error)) + return FALSE; + + struct fsverity_enable_arg arg = { 0, }; + arg.version = 1; + arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; /* TODO configurable? */ + arg.block_size = 4096; /* FIXME query */ + arg.salt_size = 0; /* TODO store salt in ostree repo config */ + arg.salt_ptr = 0; + arg.sig_size = 0; /* We don't currently expect use of in-kernel signature verification */ + arg.sig_ptr = 0; + + if (ioctl (tmpf->fd, FS_IOC_ENABLE_VERITY, &arg) < 0) + { + switch (errno) + { + case ENOTTY: + case EOPNOTSUPP: + return TRUE; + default: + return glnx_throw_errno_prefix (error, "ioctl(FS_IOC_ENABLE_VERITY)"); + } + } + + if (supported) + *supported = TRUE; +#endif + return TRUE; +} + +/* Enable verity on a file, respecting the "wanted" and "supported" states. + * The main idea here is to optimize out pointlessly calling the ioctl() + * over and over in cases where it's not supported for the repo's filesystem, + * as well as to support "opportunistic" use (requested and if filesystem supports). + * */ +gboolean +_ostree_tmpf_fsverity (OstreeRepo *self, + GLnxTmpfile *tmpf, + GError **error) +{ +#ifdef HAVE_LINUX_FSVERITY_H + g_mutex_lock (&self->txn_lock); + _OstreeFeatureSupport fsverity_wanted = self->fs_verity_wanted; + _OstreeFeatureSupport fsverity_supported = self->fs_verity_supported; + g_mutex_unlock (&self->txn_lock); + + switch (fsverity_wanted) + { + case _OSTREE_FEATURE_YES: + { + if (fsverity_supported == _OSTREE_FEATURE_NO) + return glnx_throw (error, "fsverity required but filesystem does not support it"); + } + break; + case _OSTREE_FEATURE_MAYBE: + break; + case _OSTREE_FEATURE_NO: + return TRUE; + } + + gboolean supported = FALSE; + if (!_ostree_tmpf_fsverity_core (tmpf, fsverity_wanted, &supported, error)) + return FALSE; + + if (!supported) + { + if (G_UNLIKELY (fsverity_wanted == _OSTREE_FEATURE_YES)) + return glnx_throw (error, "fsverity required but filesystem does not support it"); + + /* If we got here, we must be trying "opportunistic" use of fs-verity */ + g_assert_cmpint (fsverity_wanted, ==, _OSTREE_FEATURE_MAYBE); + g_mutex_lock (&self->txn_lock); + self->fs_verity_supported = _OSTREE_FEATURE_NO; + g_mutex_unlock (&self->txn_lock); + return TRUE; + } + + g_mutex_lock (&self->txn_lock); + self->fs_verity_supported = _OSTREE_FEATURE_YES; + g_mutex_unlock (&self->txn_lock); +#else + g_assert_cmpint (self->fs_verity_wanted, !=, _OSTREE_FEATURE_YES); +#endif + return TRUE; +} + /* Given an O_TMPFILE regular file, link it into place. */ gboolean _ostree_repo_commit_tmpf_final (OstreeRepo *self, @@ -185,6 +296,9 @@ _ostree_repo_commit_tmpf_final (OstreeRepo *self, cancellable, error)) return FALSE; + if (!_ostree_tmpf_fsverity (self, tmpf, error)) + return FALSE; + if (!glnx_link_tmpfile_at (tmpf, GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST, dest_dfd, tmpbuf, error)) return FALSE; @@ -322,16 +436,19 @@ commit_loose_regfile_object (OstreeRepo *self, /* This is used by OSTREE_REPO_COMMIT_MODIFIER_FLAGS_GENERATE_SIZES */ typedef struct { + OstreeObjectType objtype; goffset unpacked; goffset archived; } OstreeContentSizeCacheEntry; static OstreeContentSizeCacheEntry * -content_size_cache_entry_new (goffset unpacked, - goffset archived) +content_size_cache_entry_new (OstreeObjectType objtype, + goffset unpacked, + goffset archived) { OstreeContentSizeCacheEntry *entry = g_slice_new0 (OstreeContentSizeCacheEntry); + entry->objtype = objtype; entry->unpacked = unpacked; entry->archived = archived; @@ -345,19 +462,61 @@ content_size_cache_entry_free (gpointer entry) g_slice_free (OstreeContentSizeCacheEntry, entry); } +void +_ostree_repo_setup_generate_sizes (OstreeRepo *self, + OstreeRepoCommitModifier *modifier) +{ + if (modifier && modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_GENERATE_SIZES) + { + if (ostree_repo_get_mode (self) == OSTREE_REPO_MODE_ARCHIVE) + { + self->generate_sizes = TRUE; + + /* Clear any stale data in the object sizes hash table */ + if (self->object_sizes != NULL) + g_hash_table_remove_all (self->object_sizes); + } + else + g_debug ("Not generating sizes for non-archive repo"); + } +} + static void -repo_store_size_entry (OstreeRepo *self, - const gchar *checksum, - goffset unpacked, - goffset archived) +repo_ensure_size_entries (OstreeRepo *self) { if (G_UNLIKELY (self->object_sizes == NULL)) self->object_sizes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, content_size_cache_entry_free); +} +static gboolean +repo_has_size_entry (OstreeRepo *self, + OstreeObjectType objtype, + const gchar *checksum) +{ + /* Only file, dirtree and dirmeta objects appropriate for size metadata */ + if (objtype > OSTREE_OBJECT_TYPE_DIR_META) + return TRUE; + + repo_ensure_size_entries (self); + return (g_hash_table_lookup (self->object_sizes, checksum) != NULL); +} + +static void +repo_store_size_entry (OstreeRepo *self, + OstreeObjectType objtype, + const gchar *checksum, + goffset unpacked, + goffset archived) +{ + /* Only file, dirtree and dirmeta objects appropriate for size metadata */ + if (objtype > OSTREE_OBJECT_TYPE_DIR_META) + return; + + repo_ensure_size_entries (self); g_hash_table_replace (self->object_sizes, g_strdup (checksum), - content_size_cache_entry_new (unpacked, archived)); + content_size_cache_entry_new (objtype, unpacked, archived)); } static int @@ -408,6 +567,7 @@ add_size_index_to_metadata (OstreeRepo *self, g_hash_table_lookup (self->object_sizes, e_checksum); _ostree_write_varuint64 (buffer, e_size->archived); _ostree_write_varuint64 (buffer, e_size->unpacked); + g_string_append_c (buffer, (gchar) e_size->objtype); g_variant_builder_add (&index_builder, "@ay", ot_gvariant_new_bytearray ((guint8*)buffer->str, buffer->len)); @@ -415,6 +575,9 @@ add_size_index_to_metadata (OstreeRepo *self, g_variant_builder_add (builder, "{sv}", "ostree.sizes", g_variant_builder_end (&index_builder)); + + /* Clear the object sizes hash table for a subsequent commit. */ + g_hash_table_remove_all (self->object_sizes); } return g_variant_ref_sink (g_variant_builder_end (builder)); @@ -956,7 +1119,6 @@ write_content_object (OstreeRepo *self, g_auto(OtCleanupUnlinkat) tmp_unlinker = { commit_tmp_dfd (self), NULL }; g_auto(GLnxTmpfile) tmpf = { 0, }; goffset unpacked_size = 0; - gboolean indexable = FALSE; /* Is it a symlink physically? */ if (phys_object_is_symlink) { @@ -982,9 +1144,6 @@ write_content_object (OstreeRepo *self, g_assert (repo_mode == OSTREE_REPO_MODE_ARCHIVE); - if (self->generate_sizes) - indexable = TRUE; - if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (self), ".", O_WRONLY|O_CLOEXEC, &tmpf, error)) return FALSE; @@ -1013,6 +1172,11 @@ write_content_object (OstreeRepo *self, unpacked_size = g_file_info_get_size (file_info); } + else + { + /* For a symlink, the size is the length of the target */ + unpacked_size = strlen (g_file_info_get_symlink_target (file_info)); + } if (!g_output_stream_flush (temp_out, cancellable, error)) return FALSE; @@ -1042,6 +1206,19 @@ write_content_object (OstreeRepo *self, g_assert (actual_checksum != NULL); /* Pacify static analysis */ + /* Update size metadata if configured and entry missing */ + if (self->generate_sizes && + !repo_has_size_entry (self, OSTREE_OBJECT_TYPE_FILE, actual_checksum)) + { + struct stat stbuf; + + if (!glnx_fstat (tmpf.fd, &stbuf, error)) + return FALSE; + + repo_store_size_entry (self, OSTREE_OBJECT_TYPE_FILE, actual_checksum, + unpacked_size, stbuf.st_size); + } + /* See whether or not we have the object, now that we know the * checksum. */ @@ -1107,17 +1284,6 @@ write_content_object (OstreeRepo *self, } else { - /* Update size metadata if configured */ - if (indexable && object_file_type == G_FILE_TYPE_REGULAR) - { - struct stat stbuf; - - if (!glnx_fstat (tmpf.fd, &stbuf, error)) - return FALSE; - - repo_store_size_entry (self, actual_checksum, unpacked_size, stbuf.st_size); - } - /* Check if a file with the same payload is present in the repository, and in case try to reflink it */ if (actual_payload_checksum && !_try_clone_from_payload_link (self, self, actual_payload_checksum, file_info, &tmpf, cancellable, error)) @@ -1309,6 +1475,11 @@ write_metadata_object (OstreeRepo *self, */ if (have_obj) { + /* Update size metadata if needed */ + if (self->generate_sizes && + !repo_has_size_entry (self, objtype, actual_checksum)) + repo_store_size_entry (self, objtype, actual_checksum, len, len); + g_mutex_lock (&self->txn_lock); self->txn.stats.metadata_objects_total++; g_mutex_unlock (&self->txn_lock); @@ -1330,6 +1501,11 @@ write_metadata_object (OstreeRepo *self, gsize len; const guint8 *bufp = g_bytes_get_data (buf, &len); + /* Update size metadata if needed */ + if (self->generate_sizes && + !repo_has_size_entry (self, objtype, actual_checksum)) + repo_store_size_entry (self, objtype, actual_checksum, len, len); + /* Write the metadata to a temporary file */ g_auto(GLnxTmpfile) tmpf = { 0, }; if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (self), ".", O_WRONLY|O_CLOEXEC, @@ -2345,6 +2521,16 @@ ostree_repo_write_metadata (OstreeRepo *self, return FALSE; if (have_obj) { + /* Update size metadata if needed */ + if (self->generate_sizes && + !repo_has_size_entry (self, objtype, expected_checksum)) + { + /* Make sure we have a fully serialized object */ + g_autoptr(GVariant) trusted = g_variant_get_normal_form (object); + gsize size = g_variant_get_size (trusted); + repo_store_size_entry (self, objtype, expected_checksum, size, size); + } + if (out_csum) *out_csum = ostree_checksum_to_bytes (expected_checksum); return TRUE; @@ -2620,8 +2806,11 @@ ostree_repo_write_content (OstreeRepo *self, { /* First, if we have an expected checksum, see if we already have this * object. This mirrors the same logic in ostree_repo_write_metadata(). + * + * If size metadata is needed, fall through to write_content_object() + * where the entries are made. */ - if (expected_checksum) + if (expected_checksum && !self->generate_sizes) { gboolean have_obj; if (!_ostree_repo_has_loose_object (self, expected_checksum, @@ -3848,8 +4037,7 @@ ostree_repo_write_directory_to_mtree (OstreeRepo *self, } else { - if (modifier && modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_GENERATE_SIZES) - self->generate_sizes = TRUE; + _ostree_repo_setup_generate_sizes (self, modifier); g_autoptr(GPtrArray) path = g_ptr_array_new (); if (!write_directory_to_mtree_internal (self, dir, mtree, modifier, path, @@ -3883,8 +4071,7 @@ ostree_repo_write_dfd_to_mtree (OstreeRepo *self, GCancellable *cancellable, GError **error) { - if (modifier && modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_GENERATE_SIZES) - self->generate_sizes = TRUE; + _ostree_repo_setup_generate_sizes (self, modifier); g_auto(GLnxDirFdIterator) dfd_iter = { 0, }; if (!glnx_dirfd_iterator_init_at (dfd, path, FALSE, &dfd_iter, error)) diff --git a/src/libostree/ostree-repo-libarchive.c b/src/libostree/ostree-repo-libarchive.c index 1850f99f..d55459f4 100644 --- a/src/libostree/ostree-repo-libarchive.c +++ b/src/libostree/ostree-repo-libarchive.c @@ -844,6 +844,8 @@ ostree_repo_import_archive_to_mtree (OstreeRepo *self, .modifier = modifier }; + _ostree_repo_setup_generate_sizes (self, modifier); + while (TRUE) { int r = archive_read_next_header (a, &aictx.entry); diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h index b57ad799..571cab6b 100644 --- a/src/libostree/ostree-repo-private.h +++ b/src/libostree/ostree-repo-private.h @@ -22,6 +22,7 @@ #pragma once #include +#include "config.h" #include "otutil.h" #include "ostree-ref.h" #include "ostree-repo.h" @@ -31,8 +32,6 @@ G_BEGIN_DECLS #define OSTREE_DELTAPART_VERSION (0) -#define _OSTREE_OBJECT_SIZES_ENTRY_SIGNATURE "ay" - #define _OSTREE_SUMMARY_CACHE_DIR "summaries" #define _OSTREE_CACHE_DIR "cache" @@ -97,6 +96,12 @@ typedef struct { fsblkcnt_t max_blocks; } OstreeRepoTxn; +typedef enum { + _OSTREE_FEATURE_NO, + _OSTREE_FEATURE_MAYBE, + _OSTREE_FEATURE_YES, +} _OstreeFeatureSupport; + /** * OstreeRepo: * @@ -127,6 +132,8 @@ struct OstreeRepo { GMutex txn_lock; OstreeRepoTxn txn; gboolean txn_locked; + _OstreeFeatureSupport fs_verity_wanted; + _OstreeFeatureSupport fs_verity_supported; GMutex cache_lock; guint dirmeta_cache_refcount; @@ -143,6 +150,14 @@ struct OstreeRepo { guint zlib_compression_level; GHashTable *loose_object_devino_hash; GHashTable *updated_uncompressed_dirs; + + /* FIXME: The object sizes hash table is really per-commit state, not repo + * state. Using a single table for the repo means that commits cannot be + * built simultaneously if they're adding size information. This data should + * probably be in OstreeMutableTree, but that's gone by the time the actual + * commit is constructed. At that point the only commit state is in the root + * OstreeRepoFile. + */ GHashTable *object_sizes; /* Cache the repo's device/inode to use for comparisons elsewhere */ @@ -329,6 +344,10 @@ _ostree_repo_commit_modifier_apply (OstreeRepo *self, GFileInfo *file_info, GFileInfo **out_modified_info); +void +_ostree_repo_setup_generate_sizes (OstreeRepo *self, + OstreeRepoCommitModifier *modifier); + gboolean _ostree_repo_remote_name_is_file (const char *remote_name); @@ -471,4 +490,15 @@ OstreeRepoAutoLock * _ostree_repo_auto_lock_push (OstreeRepo *self, void _ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *lock); G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoAutoLock, _ostree_repo_auto_lock_cleanup) +gboolean +_ostree_tmpf_fsverity_core (GLnxTmpfile *tmpf, + _OstreeFeatureSupport fsverity_requested, + gboolean *supported, + GError **error); + +gboolean +_ostree_tmpf_fsverity (OstreeRepo *self, + GLnxTmpfile *tmpf, + GError **error); + G_END_DECLS diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 586b34fe..125c113d 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -2925,7 +2925,7 @@ _ostree_repo_cache_summary (OstreeRepo *self, if (self->cache_dir_fd == -1) return TRUE; - if (!glnx_shutil_mkdir_p_at (self->cache_dir_fd, _OSTREE_SUMMARY_CACHE_DIR, 0775, cancellable, error)) + if (!glnx_shutil_mkdir_p_at (self->cache_dir_fd, _OSTREE_SUMMARY_CACHE_DIR, DEFAULT_DIRECTORY_MODE, cancellable, error)) return FALSE; const char *summary_cache_file = glnx_strjoina (_OSTREE_SUMMARY_CACHE_DIR, "/", remote); diff --git a/src/libostree/ostree-repo-refs.c b/src/libostree/ostree-repo-refs.c index 536a763a..d1d679b2 100644 --- a/src/libostree/ostree-repo-refs.c +++ b/src/libostree/ostree-repo-refs.c @@ -1181,7 +1181,7 @@ _ostree_repo_write_ref (OstreeRepo *self, char *parent = strdupa (ref->ref_name); parent[lastslash - ref->ref_name] = '\0'; - if (!glnx_shutil_mkdir_p_at (dfd, parent, 0755, cancellable, error)) + if (!glnx_shutil_mkdir_p_at (dfd, parent, DEFAULT_DIRECTORY_MODE, cancellable, error)) return FALSE; } diff --git a/src/libostree/ostree-repo-static-delta-compilation.c b/src/libostree/ostree-repo-static-delta-compilation.c index 054ac06f..88e9ddf6 100644 --- a/src/libostree/ostree-repo-static-delta-compilation.c +++ b/src/libostree/ostree-repo-static-delta-compilation.c @@ -1427,7 +1427,7 @@ ostree_repo_static_delta_generate (OstreeRepo *self, g_autofree char *dnbuf = g_strdup (descriptor_relpath); const char *dn = dirname (dnbuf); - if (!glnx_shutil_mkdir_p_at (self->repo_dir_fd, dn, 0755, cancellable, error)) + if (!glnx_shutil_mkdir_p_at (self->repo_dir_fd, dn, DEFAULT_DIRECTORY_MODE, cancellable, error)) goto out; if (!glnx_opendirat (self->repo_dir_fd, dn, TRUE, &descriptor_dfd, error)) goto out; diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index cff70d47..3aeecc5c 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -31,6 +31,7 @@ #include "libglnx.h" #include "otutil.h" #include +#include #include "ostree-core-private.h" #include "ostree-sysroot-private.h" @@ -47,6 +48,7 @@ #include #include #include +#include #define REPO_LOCK_DISABLED (-2) #define REPO_LOCK_BLOCKING (-1) @@ -357,7 +359,7 @@ push_repo_lock (OstreeRepo *self, g_debug ("Opening repo lock file"); lock->fd = TEMP_FAILURE_RETRY (openat (self->repo_dir_fd, ".lock", O_CREAT | O_RDWR | O_CLOEXEC, - 0600)); + DEFAULT_REGFILE_MODE)); if (lock->fd < 0) { free_repo_lock (lock); @@ -2489,7 +2491,7 @@ repo_create_at_internal (int dfd, } } - if (mkdirat (dfd, path, 0755) != 0) + if (mkdirat (dfd, path, DEFAULT_DIRECTORY_MODE) != 0) { if (G_UNLIKELY (errno != EEXIST)) return glnx_throw_errno_prefix (error, "mkdirat"); @@ -2527,7 +2529,7 @@ repo_create_at_internal (int dfd, for (guint i = 0; i < G_N_ELEMENTS (state_dirs); i++) { const char *elt = state_dirs[i]; - if (mkdirat (repo_dfd, elt, 0755) == -1) + if (mkdirat (repo_dfd, elt, DEFAULT_DIRECTORY_MODE) == -1) { if (G_UNLIKELY (errno != EEXIST)) return glnx_throw_errno_prefix (error, "mkdirat"); @@ -3033,6 +3035,34 @@ reload_core_config (OstreeRepo *self, } } + /* Currently experimental */ + static const char fsverity_key[] = "ex-fsverity"; + self->fs_verity_wanted = _OSTREE_FEATURE_NO; +#ifdef HAVE_LINUX_FSVERITY_H + self->fs_verity_supported = _OSTREE_FEATURE_MAYBE; +#else + self->fs_verity_supported = _OSTREE_FEATURE_NO; +#endif + gboolean fsverity_required = FALSE; + if (!ot_keyfile_get_boolean_with_default (self->config, fsverity_key, "required", + FALSE, &fsverity_required, error)) + return FALSE; + if (fsverity_required) + { + self->fs_verity_wanted = _OSTREE_FEATURE_YES; + if (self->fs_verity_supported == _OSTREE_FEATURE_NO) + return glnx_throw (error, "fsverity required, but libostree compiled without support"); + } + else + { + gboolean fsverity_opportunistic = FALSE; + if (!ot_keyfile_get_boolean_with_default (self->config, fsverity_key, "opportunistic", + FALSE, &fsverity_opportunistic, error)) + return FALSE; + if (fsverity_opportunistic) + self->fs_verity_wanted = _OSTREE_FEATURE_MAYBE; + } + { g_clear_pointer (&self->collection_id, g_free); if (!ot_keyfile_get_value_with_default (self->config, "core", "collection-id", @@ -3295,7 +3325,7 @@ ostree_repo_open (OstreeRepo *self, * * https://github.com/ostreedev/ostree/issues/1018 */ - if (mkdirat (self->repo_dir_fd, "tmp", 0755) == -1) + if (mkdirat (self->repo_dir_fd, "tmp", DEFAULT_DIRECTORY_MODE) == -1) { if (G_UNLIKELY (errno != EEXIST)) return glnx_throw_errno_prefix (error, "mkdir(tmp)"); @@ -3307,7 +3337,7 @@ ostree_repo_open (OstreeRepo *self, if (self->writable) { - if (!glnx_shutil_mkdir_p_at (self->tmp_dir_fd, _OSTREE_CACHE_DIR, 0775, cancellable, error)) + if (!glnx_shutil_mkdir_p_at (self->tmp_dir_fd, _OSTREE_CACHE_DIR, DEFAULT_DIRECTORY_MODE, cancellable, error)) return FALSE; if (!glnx_opendirat (self->tmp_dir_fd, _OSTREE_CACHE_DIR, TRUE, &self->cache_dir_fd, error)) @@ -6099,7 +6129,7 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd, { g_auto(GLnxTmpDir) new_tmpdir = { 0, }; /* No existing tmpdir found, create a new */ - if (!glnx_mkdtempat (tmpdir_dfd, tmpdir_name_template, 0755, + if (!glnx_mkdtempat (tmpdir_dfd, tmpdir_name_template, DEFAULT_DIRECTORY_MODE, &new_tmpdir, error)) return FALSE; diff --git a/src/libostree/ostree-sysroot-cleanup.c b/src/libostree/ostree-sysroot-cleanup.c index ef95d13c..71d978e0 100644 --- a/src/libostree/ostree-sysroot-cleanup.c +++ b/src/libostree/ostree-sysroot-cleanup.c @@ -455,6 +455,9 @@ ostree_sysroot_cleanup_prune_repo (OstreeSysroot *sysroot, OstreeRepo *repo = ostree_sysroot_repo (sysroot); const guint depth = 0; /* Historical default */ + if (!_ostree_sysroot_ensure_writable (sysroot, error)) + return FALSE; + /* Hold an exclusive lock by default across gathering refs and doing * the prune. */ @@ -535,7 +538,10 @@ _ostree_sysroot_cleanup_internal (OstreeSysroot *self, GError **error) { g_return_val_if_fail (OSTREE_IS_SYSROOT (self), FALSE); - g_return_val_if_fail (self->loaded, FALSE); + g_return_val_if_fail (self->loadstate == OSTREE_SYSROOT_LOAD_STATE_LOADED, FALSE); + + if (!_ostree_sysroot_ensure_writable (self, error)) + return FALSE; if (!cleanup_other_bootversions (self, cancellable, error)) return glnx_prefix_error (error, "Cleaning bootversions"); diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index a09c354b..ee00c02c 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -41,6 +41,7 @@ #include "otutil.h" #include "ostree.h" +#include "ostree-repo-private.h" #include "ostree-sysroot-private.h" #include "ostree-sepolicy-private.h" #include "ostree-bootloader-zipl.h" @@ -56,6 +57,9 @@ #define OSTREE_DEPLOYMENT_FINALIZING_ID SD_ID128_MAKE(e8,64,6c,d6,3d,ff,46,25,b7,79,09,a8,e7,a4,09,94) #endif +static gboolean +is_ro_mount (const char *path); + /* * Like symlinkat() but overwrites (atomically) an existing * symlink. @@ -101,7 +105,8 @@ sysroot_flags_to_copy_flags (GLnxFileCopyFlags defaults, * hardlink if we're on the same partition. */ static gboolean -install_into_boot (OstreeSePolicy *sepolicy, +install_into_boot (OstreeRepo *repo, + OstreeSePolicy *sepolicy, int src_dfd, const char *src_subpath, int dest_dfd, @@ -110,32 +115,67 @@ install_into_boot (OstreeSePolicy *sepolicy, GCancellable *cancellable, GError **error) { - if (linkat (src_dfd, src_subpath, dest_dfd, dest_subpath, 0) != 0) - { - if (G_IN_SET (errno, EMLINK, EXDEV)) - { - /* Be sure we relabel when copying the kernel, as in current - * e.g. Fedora it might be labeled module_object_t or usr_t, - * but policy may not allow other processes to read from that - * like kdump. - * See also https://github.com/fedora-selinux/selinux-policy/commit/747f4e6775d773ab74efae5aa37f3e5e7f0d4aca - * This means we also drop xattrs but...I doubt anyone uses - * non-SELinux xattrs for the kernel anyways aside from perhaps - * IMA but that's its own story. - */ - g_auto(OstreeSepolicyFsCreatecon) fscreatecon = { 0, }; - const char *boot_path = glnx_strjoina ("/boot/", glnx_basename (dest_subpath)); - if (!_ostree_sepolicy_preparefscreatecon (&fscreatecon, sepolicy, - boot_path, S_IFREG | 0644, - error)) - return FALSE; - return glnx_file_copy_at (src_dfd, src_subpath, NULL, dest_dfd, dest_subpath, - GLNX_FILE_COPY_NOXATTRS | GLNX_FILE_COPY_DATASYNC, - cancellable, error); - } - else - return glnx_throw_errno_prefix (error, "linkat(%s)", dest_subpath); - } + if (linkat (src_dfd, src_subpath, dest_dfd, dest_subpath, 0) == 0) + return TRUE; /* Note early return */ + if (!G_IN_SET (errno, EMLINK, EXDEV)) + return glnx_throw_errno_prefix (error, "linkat(%s)", dest_subpath); + + /* Otherwise, copy */ + struct stat src_stbuf; + if (!glnx_fstatat (src_dfd, src_subpath, &src_stbuf, AT_SYMLINK_NOFOLLOW, error)) + return FALSE; + + glnx_autofd int src_fd = -1; + if (!glnx_openat_rdonly (src_dfd, src_subpath, FALSE, &src_fd, error)) + return FALSE; + + /* Be sure we relabel when copying the kernel, as in current + * e.g. Fedora it might be labeled module_object_t or usr_t, + * but policy may not allow other processes to read from that + * like kdump. + * See also https://github.com/fedora-selinux/selinux-policy/commit/747f4e6775d773ab74efae5aa37f3e5e7f0d4aca + * This means we also drop xattrs but...I doubt anyone uses + * non-SELinux xattrs for the kernel anyways aside from perhaps + * IMA but that's its own story. + */ + g_auto(OstreeSepolicyFsCreatecon) fscreatecon = { 0, }; + const char *boot_path = glnx_strjoina ("/boot/", glnx_basename (dest_subpath)); + if (!_ostree_sepolicy_preparefscreatecon (&fscreatecon, sepolicy, + boot_path, S_IFREG | 0644, + error)) + return FALSE; + + g_auto(GLnxTmpfile) tmp_dest = { 0, }; + if (!glnx_open_tmpfile_linkable_at (dest_dfd, ".", O_WRONLY | O_CLOEXEC, + &tmp_dest, error)) + return FALSE; + + if (glnx_regfile_copy_bytes (src_fd, tmp_dest.fd, (off_t) -1) < 0) + return glnx_throw_errno_prefix (error, "regfile copy"); + + /* Kernel data should always be root-owned */ + if (fchown (tmp_dest.fd, src_stbuf.st_uid, src_stbuf.st_gid) != 0) + return glnx_throw_errno_prefix (error, "fchown"); + + if (fchmod (tmp_dest.fd, src_stbuf.st_mode & 07777) != 0) + return glnx_throw_errno_prefix (error, "fchmod"); + + if (fdatasync (tmp_dest.fd) < 0) + return glnx_throw_errno_prefix (error, "fdatasync"); + + /* Today we don't have a config flag to *require* verity on /boot, + * and at least for Fedora CoreOS we're not likely to do fsverity on + * /boot soon due to wanting to support mounting it from old Linux + * kernels. So change "required" to "maybe". + */ + _OstreeFeatureSupport boot_verity = _OSTREE_FEATURE_NO; + if (repo->fs_verity_wanted != _OSTREE_FEATURE_NO) + boot_verity = _OSTREE_FEATURE_MAYBE; + if (!_ostree_tmpf_fsverity_core (&tmp_dest, boot_verity, NULL, error)) + return FALSE; + + if (!glnx_link_tmpfile_at (&tmp_dest, GLNX_LINK_TMPFILE_NOREPLACE, dest_dfd, dest_subpath, error)) + return FALSE; return TRUE; } @@ -806,6 +846,9 @@ write_origin_file_internal (OstreeSysroot *sysroot, GCancellable *cancellable, GError **error) { + if (!_ostree_sysroot_ensure_writable (sysroot, error)) + return FALSE; + GLNX_AUTO_PREFIX_ERROR ("Writing out origin file", error); GKeyFile *origin = new_origin ? new_origin : ostree_deployment_get_origin (deployment); @@ -1660,7 +1703,7 @@ install_deployment_kernel (OstreeSysroot *sysroot, return FALSE; if (errno == ENOENT) { - if (!install_into_boot (sepolicy, kernel_layout->boot_dfd, kernel_layout->kernel_srcpath, + if (!install_into_boot (repo, sepolicy, kernel_layout->boot_dfd, kernel_layout->kernel_srcpath, bootcsum_dfd, kernel_layout->kernel_namever, sysroot->debug_flags, cancellable, error)) @@ -1677,7 +1720,7 @@ install_deployment_kernel (OstreeSysroot *sysroot, return FALSE; if (errno == ENOENT) { - if (!install_into_boot (sepolicy, kernel_layout->boot_dfd, kernel_layout->initramfs_srcpath, + if (!install_into_boot (repo, sepolicy, kernel_layout->boot_dfd, kernel_layout->initramfs_srcpath, bootcsum_dfd, kernel_layout->initramfs_namever, sysroot->debug_flags, cancellable, error)) @@ -1692,7 +1735,7 @@ install_deployment_kernel (OstreeSysroot *sysroot, return FALSE; if (errno == ENOENT) { - if (!install_into_boot (sepolicy, kernel_layout->boot_dfd, kernel_layout->devicetree_srcpath, + if (!install_into_boot (repo, sepolicy, kernel_layout->boot_dfd, kernel_layout->devicetree_srcpath, bootcsum_dfd, kernel_layout->devicetree_namever, sysroot->debug_flags, cancellable, error)) @@ -1706,7 +1749,7 @@ install_deployment_kernel (OstreeSysroot *sysroot, return FALSE; if (errno == ENOENT) { - if (!install_into_boot (sepolicy, kernel_layout->boot_dfd, kernel_layout->kernel_hmac_srcpath, + if (!install_into_boot (repo, sepolicy, kernel_layout->boot_dfd, kernel_layout->kernel_hmac_srcpath, bootcsum_dfd, kernel_layout->kernel_hmac_namever, sysroot->debug_flags, cancellable, error)) @@ -2217,7 +2260,10 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self, GCancellable *cancellable, GError **error) { - g_assert (self->loaded); + g_assert (self->loadstate == OSTREE_SYSROOT_LOAD_STATE_LOADED); + + if (!_ostree_sysroot_ensure_writable (self, error)) + return FALSE; /* Dealing with the staged deployment is quite tricky here. This function is * primarily concerned with writing out "finalized" deployments which have @@ -2374,7 +2420,6 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self, if (boot_was_ro_mount) { - /* TODO: Use new mount namespace. https://github.com/ostreedev/ostree/issues/1265 */ if (mount ("/boot", "/boot", NULL, MS_REMOUNT | MS_SILENT, NULL) < 0) return glnx_throw_errno_prefix (error, "Remounting /boot read-write"); } @@ -2408,8 +2453,10 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self, /* Note equivalent of try/finally here */ gboolean success = write_deployments_bootswap (self, new_deployments, opts, bootloader, &syncstats, cancellable, error); - /* Below here don't set GError until the if (!success) check */ - if (boot_was_ro_mount) + /* Below here don't set GError until the if (!success) check. + * Note we only bother remounting if a mount namespace isn't in use. + * */ + if (boot_was_ro_mount && !self->mount_namespace_in_use) { if (mount ("/boot", "/boot", NULL, MS_REMOUNT | MS_RDONLY | MS_SILENT, NULL) < 0) { @@ -2716,6 +2763,9 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self, GCancellable *cancellable, GError **error) { + if (!_ostree_sysroot_ensure_writable (self, error)) + return FALSE; + g_autoptr(OstreeDeployment) deployment = NULL; if (!sysroot_initialize_deployment (self, osname, revision, origin, override_kernel_argv, &deployment, cancellable, error)) @@ -2817,6 +2867,9 @@ ostree_sysroot_stage_tree (OstreeSysroot *self, GCancellable *cancellable, GError **error) { + if (!_ostree_sysroot_ensure_writable (self, error)) + return FALSE; + OstreeDeployment *booted_deployment = ostree_sysroot_get_booted_deployment (self); if (booted_deployment == NULL) return glnx_throw (error, "Cannot stage a deployment when not currently booted into an OSTree system"); @@ -3043,6 +3096,9 @@ ostree_sysroot_deployment_set_kargs (OstreeSysroot *self, GCancellable *cancellable, GError **error) { + if (!_ostree_sysroot_ensure_writable (self, error)) + return FALSE; + /* For now; instead of this do a redeployment */ g_assert (!ostree_deployment_is_staged (deployment)); @@ -3090,6 +3146,8 @@ ostree_sysroot_deployment_set_mutable (OstreeSysroot *self, GCancellable *cancellable, GError **error) { + if (!_ostree_sysroot_ensure_writable (self, error)) + return FALSE; if (g_cancellable_set_error_if_cancelled (cancellable, error)) return FALSE; diff --git a/src/libostree/ostree-sysroot-private.h b/src/libostree/ostree-sysroot-private.h index 858673c5..96670b13 100644 --- a/src/libostree/ostree-sysroot-private.h +++ b/src/libostree/ostree-sysroot-private.h @@ -40,6 +40,12 @@ typedef enum { OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH = 1 << 3, } OstreeSysrootDebugFlags; +typedef enum { + OSTREE_SYSROOT_LOAD_STATE_NONE, /* ostree_sysroot_new() was called */ + OSTREE_SYSROOT_LOAD_STATE_INIT, /* We've loaded basic sysroot state and have an fd */ + OSTREE_SYSROOT_LOAD_STATE_LOADED, /* We've loaded all of the deployments */ +} OstreeSysrootLoadState; + /** * OstreeSysroot: * Internal struct @@ -51,7 +57,8 @@ struct OstreeSysroot { int sysroot_fd; GLnxLockFile lock; - gboolean loaded; + OstreeSysrootLoadState loadstate; + gboolean mount_namespace_in_use; /* TRUE if caller has told us they used CLONE_NEWNS */ gboolean root_is_ostree_booted; /* TRUE if sysroot is / and we are booted via ostree */ /* The device/inode for /, used to detect booted deployment */ dev_t root_device; @@ -79,6 +86,10 @@ struct OstreeSysroot { #define _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_DIR "/run/ostree/deployment-state/" #define _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT "unlocked-development" +gboolean +_ostree_sysroot_ensure_writable (OstreeSysroot *self, + GError **error); + void _ostree_sysroot_emit_journal_msg (OstreeSysroot *self, const char *msg); diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index 1c9dbf37..55a61014 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -226,6 +226,33 @@ ostree_sysroot_new_default (void) return ostree_sysroot_new (NULL); } +/** + * ostree_sysroot_set_mount_namespace_in_use: + * + * If this function is invoked, then libostree will assume that + * a private Linux mount namespace has been created by the process. + * The primary use case for this is to have e.g. /sysroot mounted + * read-only by default. + * + * If this function has been called, then when a function which requires + * writable access is invoked, libostree will automatically remount as writable + * any mount points on which it operates. This currently is just `/sysroot` and + * `/boot`. + * + * If you invoke this function, it must be before ostree_sysroot_load(); it may + * be invoked before or after ostree_sysroot_initialize(). + * + * Since: 2020.1 + */ +void +ostree_sysroot_set_mount_namespace_in_use (OstreeSysroot *self) +{ + /* Must be before we're loaded, as otherwise we'd have to close/reopen all our + fds, e.g. the repo */ + g_return_if_fail (self->loadstate < OSTREE_SYSROOT_LOAD_STATE_LOADED); + self->mount_namespace_in_use = TRUE; +} + /** * ostree_sysroot_get_path: * @self: @@ -238,6 +265,7 @@ ostree_sysroot_get_path (OstreeSysroot *self) return self->path; } +/* Open a directory file descriptor for the sysroot if we haven't yet */ static gboolean ensure_sysroot_fd (OstreeSysroot *self, GError **error) @@ -251,13 +279,51 @@ ensure_sysroot_fd (OstreeSysroot *self, return TRUE; } +/* Remount /sysroot read-write if necessary */ +gboolean +_ostree_sysroot_ensure_writable (OstreeSysroot *self, + GError **error) +{ + /* Do nothing if no mount namespace is in use */ + if (!self->mount_namespace_in_use) + return TRUE; + + /* If a mount namespace is in use, ensure we're initialized */ + if (!ostree_sysroot_initialize (self, error)) + return FALSE; + + /* If we aren't operating on a booted system, then we don't + * do anything with mounts. + */ + if (!self->root_is_ostree_booted) + return TRUE; + + /* Check if /sysroot is a read-only mountpoint */ + struct statvfs stvfsbuf; + if (statvfs ("/sysroot", &stvfsbuf) < 0) + return glnx_throw_errno_prefix (error, "fstatvfs(/sysroot)"); + if ((stvfsbuf.f_flag & ST_RDONLY) == 0) + return TRUE; + + /* OK, let's remount writable. */ + if (mount ("/sysroot", "/sysroot", NULL, MS_REMOUNT | MS_RELATIME, "") < 0) + return glnx_throw_errno_prefix (error, "Remounting /sysroot read-write"); + + /* Reopen our fd */ + glnx_close_fd (&self->sysroot_fd); + if (!ensure_sysroot_fd (self, error)) + return FALSE; + + return TRUE; +} + /** * ostree_sysroot_get_fd: * @self: Sysroot * - * Access a file descriptor that refers to the root directory of this - * sysroot. ostree_sysroot_load() must have been invoked prior to - * calling this function. + * Access a file descriptor that refers to the root directory of this sysroot. + * ostree_sysroot_initialize() (or ostree_sysroot_load()) must have been invoked + * prior to calling this function. * * Returns: A file descriptor valid for the lifetime of @self */ @@ -268,6 +334,22 @@ ostree_sysroot_get_fd (OstreeSysroot *self) return self->sysroot_fd; } +/** + * ostree_sysroot_is_booted: + * @self: Sysroot + * + * Can only be invoked after `ostree_sysroot_initialize()`. + * + * Returns: %TRUE iff the sysroot points to a booted deployment + * Since: 2020.1 + */ +gboolean +ostree_sysroot_is_booted (OstreeSysroot *self) +{ + g_return_val_if_fail (self->loadstate >= OSTREE_SYSROOT_LOAD_STATE_INIT, FALSE); + return self->root_is_ostree_booted; +} + gboolean _ostree_sysroot_bump_mtime (OstreeSysroot *self, GError **error) @@ -798,6 +880,57 @@ ensure_repo (OstreeSysroot *self, return TRUE; } +/** + * ostree_sysroot_initialize: + * @self: sysroot + * + * Subset of ostree_sysroot_load(); performs basic initialization. Notably, one + * can invoke `ostree_sysroot_get_fd()` after calling this function. + * + * It is not necessary to call this function if ostree_sysroot_load() is + * invoked. + * + * Since: 2020.1 + */ +gboolean +ostree_sysroot_initialize (OstreeSysroot *self, + GError **error) +{ + if (!ensure_sysroot_fd (self, error)) + return FALSE; + + if (self->loadstate < OSTREE_SYSROOT_LOAD_STATE_INIT) + { + /* Gather some global state; first if we have the global ostree-booted flag; + * we'll use it to sanity check that we found a booted deployment for example. + * Second, we also find out whether sysroot == /. + */ + if (!glnx_fstatat_allow_noent (AT_FDCWD, "/run/ostree-booted", NULL, 0, error)) + return FALSE; + const gboolean ostree_booted = (errno == 0); + + { struct stat root_stbuf; + if (!glnx_fstatat (AT_FDCWD, "/", &root_stbuf, 0, error)) + return FALSE; + self->root_device = root_stbuf.st_dev; + self->root_inode = root_stbuf.st_ino; + } + + struct stat self_stbuf; + if (!glnx_fstatat (AT_FDCWD, gs_file_get_path_cached (self->path), &self_stbuf, 0, error)) + return FALSE; + + const gboolean root_is_sysroot = + (self->root_device == self_stbuf.st_dev && + self->root_inode == self_stbuf.st_ino); + + self->root_is_ostree_booted = (ostree_booted && root_is_sysroot); + self->loadstate = OSTREE_SYSROOT_LOAD_STATE_INIT; + } + + return TRUE; +} + /* Reload the staged deployment from the file in /run */ gboolean _ostree_sysroot_reload_staged (OstreeSysroot *self, @@ -870,7 +1003,7 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self, GCancellable *cancellable, GError **error) { - if (!ensure_sysroot_fd (self, error)) + if (!ostree_sysroot_initialize (self, error)) return FALSE; /* Here we also lazily initialize the repository. We didn't do this @@ -880,34 +1013,6 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self, if (!ensure_repo (self, error)) return FALSE; - /* Gather some global state; first if we have the global ostree-booted flag; - * we'll use it to sanity check that we found a booted deployment for example. - * Second, we also find out whether sysroot == /. - */ - if (!self->loaded) - { - if (!glnx_fstatat_allow_noent (AT_FDCWD, "/run/ostree-booted", NULL, 0, error)) - return FALSE; - const gboolean ostree_booted = (errno == 0); - - { struct stat root_stbuf; - if (!glnx_fstatat (AT_FDCWD, "/", &root_stbuf, 0, error)) - return FALSE; - self->root_device = root_stbuf.st_dev; - self->root_inode = root_stbuf.st_ino; - } - - struct stat self_stbuf; - if (!glnx_fstat (self->sysroot_fd, &self_stbuf, error)) - return FALSE; - - const gboolean root_is_sysroot = - (self->root_device == self_stbuf.st_dev && - self->root_inode == self_stbuf.st_ino); - - self->root_is_ostree_booted = (ostree_booted && root_is_sysroot); - } - int bootversion = 0; if (!read_current_bootversion (self, &bootversion, cancellable, error)) return FALSE; @@ -990,8 +1095,8 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self, ostree_deployment_set_index (deployment, i); } - /* Determine whether we're "physical" or not, the first time we initialize */ - if (!self->loaded) + /* Determine whether we're "physical" or not, the first time we load deployments */ + if (self->loadstate < OSTREE_SYSROOT_LOAD_STATE_LOADED) { /* If we have a booted deployment, the sysroot is / and we're definitely * not physical. @@ -1009,13 +1114,14 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self, self->is_physical = TRUE; } /* Otherwise, the default is FALSE */ + + self->loadstate = OSTREE_SYSROOT_LOAD_STATE_LOADED; } self->bootversion = bootversion; self->subbootversion = subbootversion; self->deployments = deployments; deployments = NULL; /* Transfer ownership */ - self->loaded = TRUE; self->loaded_ts = stbuf.st_mtim; if (out_changed) @@ -1044,7 +1150,7 @@ ostree_sysroot_get_subbootversion (OstreeSysroot *self) OstreeDeployment * ostree_sysroot_get_booted_deployment (OstreeSysroot *self) { - g_return_val_if_fail (self->loaded, NULL); + g_return_val_if_fail (self->loadstate == OSTREE_SYSROOT_LOAD_STATE_LOADED, NULL); return self->booted_deployment; } @@ -1060,7 +1166,7 @@ ostree_sysroot_get_booted_deployment (OstreeSysroot *self) OstreeDeployment * ostree_sysroot_get_staged_deployment (OstreeSysroot *self) { - g_return_val_if_fail (self->loaded, NULL); + g_return_val_if_fail (self->loadstate == OSTREE_SYSROOT_LOAD_STATE_LOADED, NULL); return self->staged_deployment; } @@ -1074,7 +1180,7 @@ ostree_sysroot_get_staged_deployment (OstreeSysroot *self) GPtrArray * ostree_sysroot_get_deployments (OstreeSysroot *self) { - g_return_val_if_fail (self->loaded, NULL); + g_return_val_if_fail (self->loadstate == OSTREE_SYSROOT_LOAD_STATE_LOADED, NULL); GPtrArray *copy = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); for (guint i = 0; i < self->deployments->len; i++) @@ -1163,8 +1269,8 @@ ostree_sysroot_get_repo (OstreeSysroot *self, * @self: Sysroot * * This function is a variant of ostree_sysroot_get_repo() that cannot fail, and - * returns a cached repository. Can only be called after ostree_sysroot_load() - * has been invoked successfully. + * returns a cached repository. Can only be called after ostree_sysroot_initialize() + * or ostree_sysroot_load() has been invoked successfully. * * Returns: (transfer none): The OSTree repository in sysroot @self. * @@ -1173,7 +1279,7 @@ ostree_sysroot_get_repo (OstreeSysroot *self, OstreeRepo * ostree_sysroot_repo (OstreeSysroot *self) { - g_return_val_if_fail (self->loaded, NULL); + g_return_val_if_fail (self->loadstate >= OSTREE_SYSROOT_LOAD_STATE_LOADED, NULL); g_assert (self->repo); return self->repo; } @@ -1368,6 +1474,10 @@ ostree_sysroot_lock (OstreeSysroot *self, { if (!ensure_sysroot_fd (self, error)) return FALSE; + + if (!_ostree_sysroot_ensure_writable (self, error)) + return FALSE; + return glnx_make_lock_file (self->sysroot_fd, OSTREE_SYSROOT_LOCKFILE, LOCK_EX, &self->lock, error); } @@ -1391,12 +1501,14 @@ ostree_sysroot_try_lock (OstreeSysroot *self, gboolean *out_acquired, GError **error) { - g_autoptr(GError) local_error = NULL; - if (!ensure_sysroot_fd (self, error)) return FALSE; + if (!_ostree_sysroot_ensure_writable (self, error)) + return FALSE; + /* Note use of LOCK_NB */ + g_autoptr(GError) local_error = NULL; if (!glnx_make_lock_file (self->sysroot_fd, OSTREE_SYSROOT_LOCKFILE, LOCK_EX | LOCK_NB, &self->lock, &local_error)) { @@ -1509,7 +1621,7 @@ ostree_sysroot_init_osname (OstreeSysroot *self, GCancellable *cancellable, GError **error) { - if (!ensure_sysroot_fd (self, error)) + if (!_ostree_sysroot_ensure_writable (self, error)) return FALSE; const char *deploydir = glnx_strjoina ("ostree/deploy/", osname); diff --git a/src/libostree/ostree-sysroot.h b/src/libostree/ostree-sysroot.h index 502cd750..af8192fc 100644 --- a/src/libostree/ostree-sysroot.h +++ b/src/libostree/ostree-sysroot.h @@ -41,12 +41,22 @@ OstreeSysroot* ostree_sysroot_new (GFile *path); _OSTREE_PUBLIC OstreeSysroot* ostree_sysroot_new_default (void); +_OSTREE_PUBLIC +void ostree_sysroot_set_mount_namespace_in_use (OstreeSysroot *self); + _OSTREE_PUBLIC GFile *ostree_sysroot_get_path (OstreeSysroot *self); +_OSTREE_PUBLIC +gboolean ostree_sysroot_is_booted (OstreeSysroot *self); + _OSTREE_PUBLIC int ostree_sysroot_get_fd (OstreeSysroot *self); +_OSTREE_PUBLIC +gboolean ostree_sysroot_initialize (OstreeSysroot *self, + GError **error); + _OSTREE_PUBLIC gboolean ostree_sysroot_load (OstreeSysroot *self, GCancellable *cancellable, @@ -90,6 +100,10 @@ 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_lock_with_mount_namespace (OstreeSysroot *self, GError **error); + _OSTREE_PUBLIC gboolean ostree_sysroot_try_lock (OstreeSysroot *self, gboolean *out_acquired, diff --git a/src/libostree/ostree-version.h b/src/libostree/ostree-version.h index e828c885..be52a9d9 100644 --- a/src/libostree/ostree-version.h +++ b/src/libostree/ostree-version.h @@ -34,7 +34,7 @@ * * Since: 2017.4 */ -#define OSTREE_YEAR_VERSION (2019) +#define OSTREE_YEAR_VERSION (2020) /** * OSTREE_RELEASE_VERSION: @@ -43,7 +43,7 @@ * * Since: 2017.4 */ -#define OSTREE_RELEASE_VERSION (6) +#define OSTREE_RELEASE_VERSION (2) /** * OSTREE_VERSION @@ -52,7 +52,7 @@ * * Since: 2017.4 */ -#define OSTREE_VERSION (2019.6) +#define OSTREE_VERSION (2020.2) /** * OSTREE_VERSION_S: @@ -62,7 +62,7 @@ * * Since: 2017.4 */ -#define OSTREE_VERSION_S "2019.6" +#define OSTREE_VERSION_S "2020.2" #define OSTREE_ENCODE_VERSION(year,release) \ ((year) << 16 | (release)) @@ -100,4 +100,4 @@ * * Since: 2019.3 */ -#define OSTREE_BUILT_FEATURES "libcurl libsoup gpgme libarchive selinux openssl libmount release p2p" +#define OSTREE_BUILT_FEATURES "libcurl libsoup gpgme ex-fsverity libarchive selinux openssl libmount systemd release p2p" diff --git a/src/ostree/ostree-trivial-httpd.c b/src/ostree/ostree-trivial-httpd.c index 5d3a004e..a38abbea 100644 --- a/src/ostree/ostree-trivial-httpd.c +++ b/src/ostree/ostree-trivial-httpd.c @@ -494,6 +494,7 @@ on_dir_changed (GFileMonitor *mon, if (event == G_FILE_MONITOR_EVENT_DELETED) { + httpd_log (self, "root directory removed, exiting\n"); self->running = FALSE; g_main_context_wakeup (NULL); } @@ -507,6 +508,7 @@ run (int argc, char **argv, GCancellable *cancellable, GError **error) const char *dirpath; OtTrivialHttpd appstruct = { 0, }; OtTrivialHttpd *app = &appstruct; + int pipefd[2] = { -1, -1 }; glnx_unref_object SoupServer *server = NULL; g_autoptr(GFileMonitor) dirmon = NULL; @@ -540,17 +542,86 @@ run (int argc, char **argv, GCancellable *cancellable, GError **error) goto out; } + if (opt_daemonize && (g_strcmp0 (opt_log, "-") == 0)) + { + ot_util_usage_error (context, "Cannot use --log-file=- and --daemonize at the same time", error); + goto out; + } + + /* Fork early before glib sets up its worker context and thread since they'll + * be gone once the parent exits. The parent waits on a pipe with the child to + * handle setup errors. The child writes a 0 when setup is successful and a 1 + * otherwise. + */ + if (opt_daemonize) + { + if (pipe (pipefd) == -1) + { + glnx_set_error_from_errno (error); + goto out; + } + + pid_t pid = fork(); + if (pid == -1) + { + glnx_set_error_from_errno (error); + goto out; + } + else if (pid > 0) + { + /* Parent, read the child status from the pipe */ + glnx_close_fd (&pipefd[1]); + guint8 buf; + int res = TEMP_FAILURE_RETRY (read (pipefd[0], &buf, 1)); + if (res < 0) + { + glnx_set_error_from_errno (error); + goto out; + } + else if (res == 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Child process closed pipe without writing status"); + goto out; + } + + g_debug ("Read %u from child", buf); + if (buf > 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Child process failed during setup"); + goto out; + } + glnx_close_fd (&pipefd[0]); + + ret = TRUE; + goto out; + } + + /* Child, continue */ + glnx_close_fd (&pipefd[0]); + } + else + { + /* Since we're used for testing purposes, let's just do this by + * default. This ensures we exit when our parent does. + */ + if (prctl (PR_SET_PDEATHSIG, SIGTERM) != 0) + { + if (errno != ENOSYS) + { + glnx_set_error_from_errno (error); + goto out; + } + } + } + if (opt_log) { GOutputStream *stream = NULL; if (g_strcmp0 (opt_log, "-") == 0) { - if (opt_daemonize) - { - ot_util_usage_error (context, "Cannot use --log-file=- and --daemonize at the same time", error); - goto out; - } stream = G_OUTPUT_STREAM (g_unix_output_stream_new (STDOUT_FILENO, FALSE)); } else @@ -625,45 +696,39 @@ run (int argc, char **argv, GCancellable *cancellable, GError **error) #if !SOUP_CHECK_VERSION(2, 48, 0) soup_server_run_async (server); #endif - + if (opt_daemonize) { - pid_t pid = fork(); - if (pid == -1) + /* Write back a 0 to the pipe to indicate setup was successful. */ + guint8 buf = 0; + g_debug ("Writing %u to parent", buf); + if (TEMP_FAILURE_RETRY (write (pipefd[1], &buf, 1)) == -1) { - int errsv = errno; - g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errsv), - g_strerror (errsv)); + glnx_set_error_from_errno (error); goto out; } - else if (pid > 0) - { - ret = TRUE; - goto out; - } - /* Child, continue */ + glnx_close_fd (&pipefd[1]); + if (setsid () < 0) - err (1, "setsid"); + { + glnx_set_prefix_error_from_errno (error, "%s", "setsid: "); + goto out; + } /* Daemonising: close stdout/stderr so $() et al work on us */ if (freopen("/dev/null", "r", stdin) == NULL) - err (1, "freopen"); - if (freopen("/dev/null", "w", stdout) == NULL) - err (1, "freopen"); - if (freopen("/dev/null", "w", stderr) == NULL) - err (1, "freopen"); - } - else - { - /* Since we're used for testing purposes, let's just do this by - * default. This ensures we exit when our parent does. - */ - if (prctl (PR_SET_PDEATHSIG, SIGTERM) != 0) { - if (errno != ENOSYS) - { - glnx_set_error_from_errno (error); - goto out; - } + glnx_set_prefix_error_from_errno (error, "%s", "freopen: "); + goto out; + } + if (freopen("/dev/null", "w", stdout) == NULL) + { + glnx_set_prefix_error_from_errno (error, "%s", "freopen: "); + goto out; + } + if (freopen("/dev/null", "w", stderr) == NULL) + { + glnx_set_prefix_error_from_errno (error, "%s", "freopen: "); + goto out; } } @@ -699,6 +764,21 @@ run (int argc, char **argv, GCancellable *cancellable, GError **error) ret = TRUE; out: + if (pipefd[0] >= 0) + { + /* Read end in the parent. This should only be open on errors. */ + g_assert_false (ret); + glnx_close_fd (&pipefd[0]); + } + if (pipefd[1] >= 0) + { + /* Write end in the child. This should only be open on errors. */ + g_assert_false (ret); + guint8 buf = 1; + g_debug ("Writing %u to parent", buf); + (void) TEMP_FAILURE_RETRY (write (pipefd[1], &buf, 1)); + glnx_close_fd (&pipefd[1]); + } if (app->root_dfd != -1) (void) close (app->root_dfd); g_clear_object (&app->log); diff --git a/src/ostree/ot-admin-builtin-finalize-staged.c b/src/ostree/ot-admin-builtin-finalize-staged.c index 6740f82a..3cea1bdb 100644 --- a/src/ostree/ot-admin-builtin-finalize-staged.c +++ b/src/ostree/ot-admin-builtin-finalize-staged.c @@ -34,6 +34,10 @@ #include "ostree-cmdprivate.h" #include "ostree.h" +static GOptionEntry options[] = { + { NULL } +}; + /* Called by ostree-finalize-staged.service, and in turn * invokes a cmdprivate function inside the shared library. */ @@ -46,11 +50,13 @@ ot_admin_builtin_finalize_staged (int argc, char **argv, OstreeCommandInvocation if (fstatat (AT_FDCWD, "/run/ostree-booted", &stbuf, 0) < 0) return TRUE; - g_autoptr(GFile) sysroot_file = g_file_new_for_path ("/"); - g_autoptr(OstreeSysroot) sysroot = ostree_sysroot_new (sysroot_file); - - if (!ostree_sysroot_load (sysroot, cancellable, error)) + g_autoptr(GOptionContext) context = g_option_context_new (""); + g_autoptr(OstreeSysroot) sysroot = NULL; + if (!ostree_admin_option_context_parse (context, options, &argc, &argv, + OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER, + invocation, &sysroot, cancellable, error)) return FALSE; + if (!ostree_cmd__private__()->ostree_finalize_staged (sysroot, cancellable, error)) return FALSE; diff --git a/src/ostree/ot-builtin-show.c b/src/ostree/ot-builtin-show.c index 5091a93c..96e2d4c6 100644 --- a/src/ostree/ot-builtin-show.c +++ b/src/ostree/ot-builtin-show.c @@ -33,6 +33,7 @@ static gboolean opt_print_related; static char* opt_print_variant_type; static char* opt_print_metadata_key; static char* opt_print_detached_metadata_key; +static gboolean opt_print_sizes; static gboolean opt_raw; static gboolean opt_no_byteswap; static char *opt_gpg_homedir; @@ -48,6 +49,7 @@ static GOptionEntry options[] = { { "print-variant-type", 0, 0, G_OPTION_ARG_STRING, &opt_print_variant_type, "Memory map OBJECT (in this case a filename) to the GVariant type string", "TYPE" }, { "print-metadata-key", 0, 0, G_OPTION_ARG_STRING, &opt_print_metadata_key, "Print string value of metadata key", "KEY" }, { "print-detached-metadata-key", 0, 0, G_OPTION_ARG_STRING, &opt_print_detached_metadata_key, "Print string value of detached metadata key", "KEY" }, + { "print-sizes", 0, 0, G_OPTION_ARG_NONE, &opt_print_sizes, "Show the commit size metadata", NULL }, { "raw", 0, 0, G_OPTION_ARG_NONE, &opt_raw, "Show raw variant data" }, { "no-byteswap", 'B', 0, G_OPTION_ARG_NONE, &opt_no_byteswap, "Do not automatically convert variant data from big endian" }, { "gpg-homedir", 0, 0, G_OPTION_ARG_FILENAME, &opt_gpg_homedir, "GPG Homedir to use when looking for keyrings", "HOMEDIR"}, @@ -146,6 +148,65 @@ do_print_metadata_key (OstreeRepo *repo, return TRUE; } +static gboolean +do_print_sizes (OstreeRepo *repo, + const char *rev, + GError **error) +{ + g_autoptr(GVariant) commit = NULL; + if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, rev, + &commit, error)) + { + g_prefix_error (error, "Failed to read commit: "); + return FALSE; + } + + g_autoptr(GPtrArray) sizes = NULL; + if (!ostree_commit_get_object_sizes (commit, &sizes, error)) + return FALSE; + + gint64 new_archived = 0; + gint64 new_unpacked = 0; + gsize new_objects = 0; + gint64 archived = 0; + gint64 unpacked = 0; + gsize objects = 0; + for (guint i = 0; i < sizes->len; i++) + { + OstreeCommitSizesEntry *entry = sizes->pdata[i]; + + archived += entry->archived; + unpacked += entry->unpacked; + objects++; + + gboolean exists; + if (!ostree_repo_has_object (repo, entry->objtype, entry->checksum, + &exists, NULL, error)) + return FALSE; + + if (!exists) + { + /* Object not in local repo */ + new_archived += entry->archived; + new_unpacked += entry->unpacked; + new_objects++; + } + } + + g_autofree char *new_archived_str = g_format_size (new_archived); + g_autofree char *archived_str = g_format_size (archived); + g_autofree char *new_unpacked_str = g_format_size (new_unpacked); + g_autofree char *unpacked_str = g_format_size (unpacked); + g_print ("Compressed size (needed/total): %s/%s\n" + "Unpacked size (needed/total): %s/%s\n" + "Number of objects (needed/total): %" G_GSIZE_FORMAT "/%" G_GSIZE_FORMAT "\n", + new_archived_str, archived_str, + new_unpacked_str, unpacked_str, + new_objects, objects); + + return TRUE; +} + static gboolean print_object (OstreeRepo *repo, OstreeObjectType objtype, @@ -279,6 +340,14 @@ ostree_builtin_show (int argc, char **argv, OstreeCommandInvocation *invocation, if (!do_print_variant_generic (G_VARIANT_TYPE (opt_print_variant_type), rev, error)) return FALSE; } + else if (opt_print_sizes) + { + if (!ostree_repo_resolve_rev (repo, rev, FALSE, &resolved_rev, error)) + return FALSE; + + if (!do_print_sizes (repo, resolved_rev, error)) + return FALSE; + } else { gboolean found = FALSE; diff --git a/src/ostree/ot-main.c b/src/ostree/ot-main.c index 4b72f399..a044cef2 100644 --- a/src/ostree/ot-main.c +++ b/src/ostree/ot-main.c @@ -27,6 +27,7 @@ #include #include +#include #include "ot-main.h" #include "ostree.h" @@ -434,10 +435,46 @@ ostree_admin_option_context_parse (GOptionContext *context, sysroot_path = g_file_new_for_path (opt_sysroot); g_autoptr(OstreeSysroot) sysroot = ostree_sysroot_new (sysroot_path); + if (!ostree_sysroot_initialize (sysroot, error)) + return FALSE; g_signal_connect (sysroot, "journal-msg", G_CALLBACK (on_sysroot_journal_msg), NULL); if ((flags & OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED) == 0) { + /* If we're requested to lock the sysroot, first check if we're operating + * on a booted (not physical) sysroot. Then find out if the /sysroot + * subdir is a read-only mount point, and if so, create a new mount + * namespace and tell the sysroot that we've done so. See the docs for + * ostree_sysroot_set_mount_namespace_in_use(). + * + * This is a conservative approach; we could just always + * unshare() too. + */ + if (ostree_sysroot_is_booted (sysroot)) + { + int sysroot_fd = ostree_sysroot_get_fd (sysroot); + g_assert_cmpint (sysroot_fd, !=, -1); + + glnx_autofd int sysroot_subdir_fd = glnx_opendirat_with_errno (sysroot_fd, "sysroot", TRUE); + if (sysroot_subdir_fd < 0) + { + if (errno != ENOENT) + return glnx_throw_errno_prefix (error, "opendirat"); + } + else if (getuid () == 0) + { + struct statvfs stvfs; + if (fstatvfs (sysroot_subdir_fd, &stvfs) < 0) + return glnx_throw_errno_prefix (error, "fstatvfs"); + if (stvfs.f_flag & ST_RDONLY) + { + if (unshare (CLONE_NEWNS) < 0) + return glnx_throw_errno_prefix (error, "preparing writable sysroot: unshare (CLONE_NEWNS)"); + ostree_sysroot_set_mount_namespace_in_use (sysroot); + } + } + } + /* Released when sysroot is finalized, or on process exit */ if (!ot_admin_sysroot_lock (sysroot, error)) return FALSE; diff --git a/src/switchroot/ostree-prepare-root.c b/src/switchroot/ostree-prepare-root.c index 33c46ff4..c25d3fe9 100644 --- a/src/switchroot/ostree-prepare-root.c +++ b/src/switchroot/ostree-prepare-root.c @@ -29,6 +29,28 @@ * Boston, MA 02111-1307, USA. */ +/* The high level goal of ostree-prepare-root.service is to run inside + * the initial ram disk (if one is in use) and set up the `/` mountpoint + * to be the deployment root, using the ostree= kernel commandline + * argument to find the target deployment root. + * + * It's really the heart of how ostree works - basically multiple + * hardlinked chroot() targets are maintained, this one does the equivalent + * of chroot(). + * + * If using systemd, an excellent reference is `man bootup`. This + * service runs Before=initrd-root-fs.target. At this point it's + * assumed that the block storage and root filesystem are mounted at + * /sysroot - i.e. /sysroot points to the *physical* root before + * this service runs. After, `/` is the deployment root. + * + * There is also a secondary mode for this service when an initrd isn't + * used - instead the binary must be statically linked (and the kernel + * must have mounted the rootfs itself) - then we set things up and + * exec the real init directly. This can be popular in embedded + * systems to increase bootup speed. + */ + #include "config.h" #include diff --git a/src/switchroot/ostree-remount.c b/src/switchroot/ostree-remount.c index 5e6d23d3..326b104f 100644 --- a/src/switchroot/ostree-remount.c +++ b/src/switchroot/ostree-remount.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -37,10 +38,14 @@ #include #include +#include + #include "ostree-mount-util.h" +#include "glnx-backport-autocleanups.h" static void -do_remount (const char *target) +do_remount (const char *target, + bool writable) { struct stat stbuf; if (lstat (target, &stbuf) < 0) @@ -54,20 +59,41 @@ do_remount (const char *target) struct statvfs stvfsbuf; if (statvfs (target, &stvfsbuf) == -1) return; - /* If no read-only flag, skip it */ - if ((stvfsbuf.f_flag & ST_RDONLY) == 0) + + const bool currently_writable = ((stvfsbuf.f_flag & ST_RDONLY) == 0); + if (writable == currently_writable) return; - /* It's a mounted, read-only fs; remount it */ - if (mount (target, target, NULL, MS_REMOUNT | MS_SILENT, NULL) < 0) + + int mnt_flags = MS_REMOUNT | MS_SILENT; + if (!writable) + mnt_flags |= MS_RDONLY; + if (mount (target, target, NULL, mnt_flags, NULL) < 0) { /* Also ignore EINVAL - if the target isn't a mountpoint * already, then assume things are OK. */ - if (errno != EINVAL) - err (EXIT_FAILURE, "failed to remount %s", target); + if (errno != EINVAL) + err (EXIT_FAILURE, "failed to remount(%s) %s", writable ? "rw" : "ro", target); + else + return; } - else - printf ("Remounted: %s\n", target); + + printf ("Remounted %s: %s\n", writable ? "rw" : "ro", target); +} + +static bool +sysroot_is_configured_ro (void) +{ + struct stat stbuf; + static const char config_path[] = "/ostree/repo/config"; + if (stat (config_path, &stbuf) != 0) + return false; + + g_autoptr(GKeyFile) keyfile = g_key_file_new (); + if (!g_key_file_load_from_file (keyfile, config_path, 0, NULL)) + return false; + + return g_key_file_get_boolean (keyfile, "sysroot", "readonly", NULL); } int @@ -95,8 +121,38 @@ main(int argc, char *argv[]) exit (EXIT_SUCCESS); } - do_remount ("/sysroot"); - do_remount ("/var"); + /* Query the repository configuration - this is an operating system builder + * choice. + * */ + const bool sysroot_readonly = sysroot_is_configured_ro (); + + /* Mount the sysroot read-only if we're configured to do so. + * Note we only get here if / is already writable. + */ + do_remount ("/sysroot", !sysroot_readonly); + + if (sysroot_readonly) + { + /* Now, /etc is not normally a bind mount, but remounting the + * sysroot above made it read-only since it's on the same filesystem. + * Make it a self-bind mount, so we can then mount it read-write. + */ + if (mount ("/etc", "/etc", NULL, MS_BIND, NULL) < 0) + err (EXIT_FAILURE, "failed to make /etc a bind mount"); + do_remount ("/etc", true); + } + + /* If /var was created as as an OSTree default bind mount (instead of being a separate filesystem) + * then remounting the root mount read-only also remounted it. + * So just like /etc, we need to make it read-write by default. + * If it was a separate filesystem, we expect it to be writable anyways, + * so it doesn't hurt to remount it if so. + * + * And if we started out with a writable system root, then we need + * to ensure that the /var bind mount created by the systemd generator + * is writable too. + */ + do_remount ("/var", true); exit (EXIT_SUCCESS); } diff --git a/tests/gpg-verify-data/README.md b/tests/gpg-verify-data/README.md index d96fbad5..9ca47581 100644 --- a/tests/gpg-verify-data/README.md +++ b/tests/gpg-verify-data/README.md @@ -1,5 +1,7 @@ This is a GPG config directory for use with the OstreeGpgVerifyResult -test cases. The test data (`lgplv2`) is signed with a variety of valid -and invalid GPG keys in a detached signature file (`lgplv2.sig`). +test cases. The test data (`lgpl2`) is signed with a variety of valid +and invalid GPG keys in a detached signature file (`lgpl2.sig`). In +addition, each detached signature is available in a separate file +(`lgpgl2.sig`). The passphrase for all the keys is `redhat`. diff --git a/tests/gpg-verify-data/lgpl2.sig0 b/tests/gpg-verify-data/lgpl2.sig0 new file mode 100644 index 00000000..375c650a Binary files /dev/null and b/tests/gpg-verify-data/lgpl2.sig0 differ diff --git a/tests/gpg-verify-data/lgpl2.sig1 b/tests/gpg-verify-data/lgpl2.sig1 new file mode 100644 index 00000000..83a6227c Binary files /dev/null and b/tests/gpg-verify-data/lgpl2.sig1 differ diff --git a/tests/gpg-verify-data/lgpl2.sig2 b/tests/gpg-verify-data/lgpl2.sig2 new file mode 100644 index 00000000..ad0f420c Binary files /dev/null and b/tests/gpg-verify-data/lgpl2.sig2 differ diff --git a/tests/gpg-verify-data/lgpl2.sig3 b/tests/gpg-verify-data/lgpl2.sig3 new file mode 100644 index 00000000..5c17ec6f Binary files /dev/null and b/tests/gpg-verify-data/lgpl2.sig3 differ diff --git a/tests/gpg-verify-data/lgpl2.sig4 b/tests/gpg-verify-data/lgpl2.sig4 new file mode 100644 index 00000000..2d3989f6 Binary files /dev/null and b/tests/gpg-verify-data/lgpl2.sig4 differ diff --git a/tests/gpg-verify-data/trustdb.gpg b/tests/gpg-verify-data/trustdb.gpg index 3f046fad..91f87170 100644 Binary files a/tests/gpg-verify-data/trustdb.gpg and b/tests/gpg-verify-data/trustdb.gpg differ diff --git a/tests/gpghome/revocations/key1.rev b/tests/gpghome/revocations/key1.rev new file mode 100644 index 00000000..b918151e --- /dev/null +++ b/tests/gpghome/revocations/key1.rev @@ -0,0 +1,12 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: This is a revocation certificate + +iQE9BCABCgAnFiEEXmXedascUBhi1HY0f8oj2Ecs2voFAl0MvqgJHQBUZXN0aW5n +AAoJEH/KI9hHLNr6/dQH/iPZjfJgFAc/TIR4xE4kB0yL4zdMqxgV1ef/atQDLEN4 +MBiqIltzb8WyG+cpNfNZgFmqXmCRN+1IAla9piixe76ZwOqcQ6S5MU/8nMcyMsD9 +edg+9sg0DH8SEzejVma3ZLfaJ/6ZpU7c6a4vCPNcRBC7PxAvAc0LnAN6KQYGU7GR +gv2k/JsGYgvmUAajhVFy0jc4jGkhRBHMDksGGFdYY94RATFF4gWtlUXyRYMTXBCf +eM3bxEeSMbU7lXCZg9zjoxP6XzJuNW1SLz3zL90GnO19uNh8Pf6pHmkCNTO/L1Ua +Cc6fb3ubtdqgs6an84U/aod1VcK7BNASqZ2gYUsF2KE= +=owvo +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/gpghome/revocations/key2.rev b/tests/gpghome/revocations/key2.rev new file mode 100644 index 00000000..9b8960a2 --- /dev/null +++ b/tests/gpghome/revocations/key2.rev @@ -0,0 +1,12 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: This is a revocation certificate + +iQE9BCABCgAnFiEEezsQINdEeWh/2yJz2CKM/sqVDUEFAl0Mvs4JHQBUZXN0aW5n +AAoJENgijP7KlQ1BzH4IAMUoTrW6XraDYq/d/b0qa3sZ1NTBPUXLp4gFaedZwKk/ +AKSUCK05RWRQO3HrSvmhdSUF6/9tFLGpbu7P56ihjAnq2vpzRyeNTEGQ02IzfCpM +SJup0R6iA7KmjiDutDoEgjhAzxCKbnU71SQ3PmjyaQT1KCBCDJVptcY6HDbo0dRN +vcjTfjtFqkPgqbHyXwGv3rlm4uctSVfACrOS/fKF4Q9Fs4prsUXjQpGLaTHdxhiL +pMRCTfZ4DBEMwAY7s9FpMpIh9rdOwE+zkv5CsE0uJVZq0WW5r0CBDCta6Sopt6uk +pIA+QHL9GPOrG2E3SJxyIBC37Sl40MGAJQ1djmecIGk= +=0KEe +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/gpghome/revocations/key3.rev b/tests/gpghome/revocations/key3.rev new file mode 100644 index 00000000..66e238a1 --- /dev/null +++ b/tests/gpghome/revocations/key3.rev @@ -0,0 +1,12 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: This is a revocation certificate + +iQE9BCABCgAnFiEEfSnPBguCac32O/vdDRX6599ETWcFAl0MvuYJHQBUZXN0aW5n +AAoJEA0V+uffRE1n/p8H/3mmSK2gtbxJ5sfu1z44ra82fLRAUupJzf53dAvvJCEK +4RSJFtHYu+hoPVFd9bmToxo2YQWe67MMZW7cHtq9D/a755SYOrty//KpXsGS22W/ +ZGatBjl36zuE6BoR18Q6VAMgVBwovPSlSuCEW+Ro8JZYyA/LbA95AnMprNod6Jw9 +VSsGC39au5rUlhEOHLL1Iw3dl4blxa6tf/roljbXzaN+Qh2/ez7Cy532oocak2FL +bbblBGrIdfYLAXpLqhnQk2vgEHZ+ZylvStBndpLWwEskXhmaHpW7+WapFhLCUOr+ +arzbc9XQ7ghhF9hSoKiToJqU5PRjaOex85BEDwE5gWY= +=ykAF +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/libtest-core.sh b/tests/libtest-core.sh index 46aafab0..945d2857 100644 --- a/tests/libtest-core.sh +++ b/tests/libtest-core.sh @@ -41,7 +41,7 @@ assert_not_reached () { # If we can't find the locale command assume we have support for C.UTF-8 # (e.g. musl based systems) if type -p locale >/dev/null; then - export LC_ALL=$(locale -a | grep -Ee '\.(UTF-8|utf8)' | grep -iEe '^(C|en_US)' | head -1 || true) + export LC_ALL=$(locale -a | grep -iEe '^(C|en_US)\.(UTF-8|utf8)$' | head -n1 || true) if [ -z "${LC_ALL}" ]; then fatal "Can't find suitable UTF-8 locale"; fi else export LC_ALL=C.UTF-8 diff --git a/tests/libtest.sh b/tests/libtest.sh index 3f5fd931..c82bf487 100755 --- a/tests/libtest.sh +++ b/tests/libtest.sh @@ -78,16 +78,20 @@ unset TAR_OPTIONS # easily clean up. export OSTREE_SYSROOT_DEBUG=mutable-deployments -export TEST_GPG_KEYID_1="472CDAFA" -export TEST_GPG_KEYID_2="CA950D41" -export TEST_GPG_KEYID_3="DF444D67" +export TEST_GPG_KEYID_1="7FCA23D8472CDAFA" +export TEST_GPG_KEYFPR_1="5E65DE75AB1C501862D476347FCA23D8472CDAFA" +export TEST_GPG_KEYID_2="D8228CFECA950D41" +export TEST_GPG_KEYFPR_2="7B3B1020D74479687FDB2273D8228CFECA950D41" +export TEST_GPG_KEYID_3="0D15FAE7DF444D67" +export TEST_GPG_KEYFPR_3="7D29CF060B8269CDF63BFBDD0D15FAE7DF444D67" -# GPG when creating signatures demands a writable +# GPG when creating signatures demands a private writable # homedir in order to create lockfiles. Work around # this by copying locally. echo "Copying gpghome to ${test_tmpdir}" cp -a "${test_srcdir}/gpghome" ${test_tmpdir} chmod -R u+w "${test_tmpdir}" +chmod 700 "${test_tmpdir}/gpghome" export TEST_GPG_KEYHOME=${test_tmpdir}/gpghome export OSTREE_GPG_HOME=${test_tmpdir}/gpghome/trusted @@ -559,6 +563,18 @@ skip_without_user_xattrs () { fi } +_have_systemd_and_libmount='' +have_systemd_and_libmount() { + if test "${_have_systemd_and_libmount}" = ''; then + if [ $(ostree --version | grep -c -e '- systemd' -e '- libmount') -eq 2 ]; then + _have_systemd_and_libmount=yes + else + _have_systemd_and_libmount=no + fi + fi + test ${_have_systemd_and_libmount} = yes +} + # Skip unless SELinux is disabled, or we can relabel. # Default Docker has security.selinux xattrs, but returns # EOPNOTSUPP when trying to set them, even to the existing value. diff --git a/tests/test-gpg-verify-result.c b/tests/test-gpg-verify-result.c index 95de1873..5ae129b9 100644 --- a/tests/test-gpg-verify-result.c +++ b/tests/test-gpg-verify-result.c @@ -36,6 +36,18 @@ } \ } G_STMT_END +#define assert_str_contains(s1, s2) \ + G_STMT_START { \ + const char *__s1 = (s1), *__s2 = (s2); \ + if (strstr (__s1, __s2) == NULL) { \ + g_autoptr(GString) string = g_string_new ("assertion failed (" #s1 " contains " #s2 "): "); \ + g_autofree char *__es1 = g_strescape (__s1, NULL); \ + g_autofree char *__es2 = g_strescape (__s2, NULL); \ + g_string_append_printf (string, "(\"%s\", \"%s\")", __es1, __es2); \ + g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, string->str); \ + } \ + } G_STMT_END + typedef struct { OstreeGpgVerifyResult *result; } TestFixture; @@ -53,12 +65,12 @@ static void test_fixture_setup (TestFixture *fixture, gconstpointer user_data) { + const char * const *sig_files = user_data; gpgme_error_t gpg_error; gpgme_data_t data_buffer; gpgme_data_t signature_buffer; OstreeGpgVerifyResult *result; g_autofree char *homedir = NULL; - g_autofree char *filename = NULL; GError *local_error = NULL; /* Mimic what OstreeGpgVerifier does to create OstreeGpgVerifyResult. @@ -74,15 +86,47 @@ test_fixture_setup (TestFixture *fixture, NULL, &local_error, NULL); g_assert_no_error (local_error); - filename = g_build_filename (homedir, "lgpl2", NULL); - gpg_error = gpgme_data_new_from_file (&data_buffer, filename, 1); - assert_no_gpg_error (gpg_error, filename); + g_autofree char *data_filename = g_build_filename (homedir, "lgpl2", NULL); + gpg_error = gpgme_data_new_from_file (&data_buffer, data_filename, 1); + assert_no_gpg_error (gpg_error, data_filename); - g_clear_pointer (&filename, g_free); + if (sig_files == NULL) + { + /* No signature files specified, use full lgpl2.sig file */ + g_autofree char *filename = g_build_filename (homedir, "lgpl2.sig", NULL); + gpg_error = gpgme_data_new_from_file (&signature_buffer, filename, 1); + assert_no_gpg_error (gpg_error, filename); + } + else + { + /* Read all the specified files into the signature buffer */ + gpg_error = gpgme_data_new (&signature_buffer); + assert_no_gpg_error (gpg_error, NULL); - filename = g_build_filename (homedir, "lgpl2.sig", NULL); - gpg_error = gpgme_data_new_from_file (&signature_buffer, filename, 1); - assert_no_gpg_error (gpg_error, filename); + for (const char * const *name = sig_files; *name != NULL; name++) + { + g_autofree char *path = g_build_filename (homedir, *name, NULL); + g_autoptr(GFile) sig_file = g_file_new_for_path (path); + + g_autofree char *contents = NULL; + gsize len; + g_assert_true (g_file_load_contents (sig_file, NULL, &contents, + &len, NULL, &local_error)); + g_assert_no_error (local_error); + + char *cur = contents; + while (len > 0) + { + ssize_t written = gpgme_data_write (signature_buffer, cur, len); + if (written == -1) + assert_no_gpg_error (gpgme_error_from_syserror (), path); + cur += written; + len -= written; + } + } + + gpgme_data_seek (signature_buffer, 0, SEEK_SET); + } gpg_error = gpgme_op_verify (result->context, signature_buffer, data_buffer, NULL); @@ -115,7 +159,7 @@ test_check_counts (TestFixture *fixture, count_valid = ostree_gpg_verify_result_count_valid (fixture->result); g_assert_cmpint (count_all, ==, 5); - g_assert_cmpint (count_valid, ==, 2); + g_assert_cmpint (count_valid, ==, 1); } static void @@ -123,7 +167,7 @@ test_signature_lookup (TestFixture *fixture, gconstpointer user_data) { /* Checking the signature with the revoked key for this case. */ - guint expected_signature_index = GPOINTER_TO_UINT (user_data); + guint expected_signature_index = 2; /* Lowercase letters to ensure OstreeGpgVerifyResult handles it. */ const char *fingerprint = "68dcc2db4bec5811c2573590bd9d2a44b7f541a6"; @@ -215,7 +259,7 @@ static void test_valid_signature (TestFixture *fixture, gconstpointer user_data) { - guint signature_index = GPOINTER_TO_UINT (user_data); + guint signature_index = 0; g_autoptr(GVariant) tuple = NULL; gboolean valid; gboolean sig_expired; @@ -249,7 +293,7 @@ static void test_expired_key (TestFixture *fixture, gconstpointer user_data) { - guint signature_index = GPOINTER_TO_UINT (user_data); + guint signature_index = 1; g_autoptr(GVariant) tuple = NULL; gboolean valid; gboolean sig_expired; @@ -283,7 +327,7 @@ static void test_revoked_key (TestFixture *fixture, gconstpointer user_data) { - guint signature_index = GPOINTER_TO_UINT (user_data); + guint signature_index = 2; g_autoptr(GVariant) tuple = NULL; gboolean valid; gboolean sig_expired; @@ -317,7 +361,7 @@ static void test_missing_key (TestFixture *fixture, gconstpointer user_data) { - guint signature_index = GPOINTER_TO_UINT (user_data); + guint signature_index = 3; g_autoptr(GVariant) tuple = NULL; gboolean valid; gboolean sig_expired; @@ -351,7 +395,7 @@ static void test_expired_signature (TestFixture *fixture, gconstpointer user_data) { - guint signature_index = GPOINTER_TO_UINT (user_data); + guint signature_index = 4; g_autoptr(GVariant) tuple = NULL; gboolean valid; gboolean sig_expired; @@ -373,7 +417,7 @@ test_expired_signature (TestFixture *fixture, &key_missing, &key_exp_timestamp); - g_assert_true (valid); + g_assert_false (valid); g_assert_true (sig_expired); g_assert_false (key_expired); g_assert_false (key_revoked); @@ -381,6 +425,83 @@ test_expired_signature (TestFixture *fixture, g_assert_cmpint (key_exp_timestamp, ==, 0); } +static void +test_require_valid_signature (TestFixture *fixture, + gconstpointer user_data) +{ + GError *error = NULL; + gboolean res = ostree_gpg_verify_result_require_valid_signature (fixture->result, + &error); + g_assert_true (res); + g_assert_no_error (error); +} + +static void +test_require_valid_signature_expired_key (TestFixture *fixture, + gconstpointer user_data) +{ + GError *error = NULL; + gboolean res = ostree_gpg_verify_result_require_valid_signature (fixture->result, + &error); + g_assert_false (res); + g_assert_error (error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_EXPIRED_KEY); + assert_str_contains (error->message, "Key expired"); +} + +static void +test_require_valid_signature_revoked_key (TestFixture *fixture, + gconstpointer user_data) +{ + GError *error = NULL; + gboolean res = ostree_gpg_verify_result_require_valid_signature (fixture->result, + &error); + g_assert_false (res); + g_assert_error (error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_REVOKED_KEY); + assert_str_contains (error->message, "Key revoked"); +} + +static void +test_require_valid_signature_missing_key (TestFixture *fixture, + gconstpointer user_data) +{ + GError *error = NULL; + gboolean res = ostree_gpg_verify_result_require_valid_signature (fixture->result, + &error); + g_assert_false (res); + g_assert_error (error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_MISSING_KEY); + assert_str_contains (error->message, "public key not found"); +} + +static void +test_require_valid_signature_expired_signature (TestFixture *fixture, + gconstpointer user_data) +{ + GError *error = NULL; + gboolean res = ostree_gpg_verify_result_require_valid_signature (fixture->result, + &error); + g_assert_false (res); + g_assert_error (error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_EXPIRED_SIGNATURE); + assert_str_contains (error->message, "Signature expired"); +} + +static void +test_require_valid_signature_expired_missing_key (TestFixture *fixture, + gconstpointer user_data) +{ + GError *error = NULL; + gboolean res = ostree_gpg_verify_result_require_valid_signature (fixture->result, + &error); + g_assert_false (res); + + /* + * The error will be for the last signature, which is for a missing key, but + * the message should show both issues. + */ + g_assert_error (error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_MISSING_KEY); + assert_str_contains (error->message, "Key expired"); + assert_str_contains (error->message, "public key not found"); +} + int main (int argc, char **argv) { @@ -397,7 +518,7 @@ main (int argc, char **argv) g_test_add ("/gpg-verify-result/signature-lookup", TestFixture, - GINT_TO_POINTER (2), + NULL, test_fixture_setup, test_signature_lookup, test_fixture_teardown); @@ -411,38 +532,85 @@ main (int argc, char **argv) g_test_add ("/gpg-verify-result/valid-signature", TestFixture, - GINT_TO_POINTER (0), /* signature index */ + NULL, test_fixture_setup, test_valid_signature, test_fixture_teardown); g_test_add ("/gpg-verify-result/expired-key", TestFixture, - GINT_TO_POINTER (1), /* signature index */ + NULL, test_fixture_setup, test_expired_key, test_fixture_teardown); g_test_add ("/gpg-verify-result/revoked-key", TestFixture, - GINT_TO_POINTER (2), /* signature index */ + NULL, test_fixture_setup, test_revoked_key, test_fixture_teardown); g_test_add ("/gpg-verify-result/missing-key", TestFixture, - GINT_TO_POINTER (3), /* signature index */ + NULL, test_fixture_setup, test_missing_key, test_fixture_teardown); g_test_add ("/gpg-verify-result/expired-signature", TestFixture, - GINT_TO_POINTER (4), /* signature index */ + NULL, test_fixture_setup, test_expired_signature, test_fixture_teardown); + g_test_add ("/gpg-verify-result/require-valid-signature", + TestFixture, + NULL, + test_fixture_setup, + test_require_valid_signature, + test_fixture_teardown); + + const char *expired_key_files[] = { "lgpl2.sig1", NULL }; + g_test_add ("/gpg-verify-result/require-valid-signature-expired-key", + TestFixture, + expired_key_files, + test_fixture_setup, + test_require_valid_signature_expired_key, + test_fixture_teardown); + + const char *revoked_key_files[] = { "lgpl2.sig2", NULL }; + g_test_add ("/gpg-verify-result/require-valid-signature-revoked-key", + TestFixture, + revoked_key_files, + test_fixture_setup, + test_require_valid_signature_revoked_key, + test_fixture_teardown); + + const char *missing_key_files[] = { "lgpl2.sig3", NULL }; + g_test_add ("/gpg-verify-result/require-valid-signature-missing-key", + TestFixture, + missing_key_files, + test_fixture_setup, + test_require_valid_signature_missing_key, + test_fixture_teardown); + + const char *expired_signature_files[] = { "lgpl2.sig4", NULL }; + g_test_add ("/gpg-verify-result/require-valid-signature-expired-signature", + TestFixture, + expired_signature_files, + test_fixture_setup, + test_require_valid_signature_expired_signature, + test_fixture_teardown); + + const char *expired_missing_key_files[] = { "lgpl2.sig1", "lgpl2.sig3", NULL }; + g_test_add ("/gpg-verify-result/require-valid-signature-expired-missing-key", + TestFixture, + expired_missing_key_files, + test_fixture_setup, + test_require_valid_signature_expired_missing_key, + test_fixture_teardown); + return g_test_run (); } diff --git a/tests/test-libarchive-import.c b/tests/test-libarchive-import.c index 70e221d1..d3429cd6 100644 --- a/tests/test-libarchive-import.c +++ b/tests/test-libarchive-import.c @@ -544,6 +544,12 @@ test_libarchive_selinux (gconstpointer data) if (skip_if_no_xattr (td)) goto out; + if (getenv ("container")) + { + // FIXME dedup this with libtest.sh have_selinux_relabel + g_test_skip ("skip in containers for now"); + goto out; + } if (td->skip_all != NULL) { diff --git a/tests/test-libarchive.sh b/tests/test-libarchive.sh index 24de55b2..174be800 100755 --- a/tests/test-libarchive.sh +++ b/tests/test-libarchive.sh @@ -28,7 +28,7 @@ fi . $(dirname $0)/libtest.sh -echo "1..17" +echo "1..18" setup_test_repository "bare" @@ -234,3 +234,17 @@ for filter in '^usr/bin/,usr/sbin/' '/bin/,/sbin/'; do assert_file_has_content usr/lib/libfoo.so 'a library' echo "ok tar pathname filter modification: ${filter}" done + +# Test sizes metadata. This needs an archive repo, so a separate repo is used. +cd ${test_tmpdir} +rm -rf repo2 +ostree_repo_init repo2 --mode=archive +${CMD_PREFIX} ostree --repo=repo2 commit \ + -s "from tar" -b test-tar \ + --generate-sizes \ + --tree=tar=foo.tar.gz +${CMD_PREFIX} ostree --repo=repo2 show --print-sizes test-tar > sizes.txt +assert_file_has_content sizes.txt 'Compressed size (needed/total): 0[  ]bytes/1.1[  ]kB' +assert_file_has_content sizes.txt 'Unpacked size (needed/total): 0[  ]bytes/900[  ]bytes' +assert_file_has_content sizes.txt 'Number of objects (needed/total): 0/12' +echo "ok tar sizes metadata" diff --git a/tests/test-pull-sizes.sh b/tests/test-pull-sizes.sh new file mode 100755 index 00000000..8ee07cc8 --- /dev/null +++ b/tests/test-pull-sizes.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# +# Copyright (C) 2019 Endless Mobile, Inc. +# +# SPDX-License-Identifier: LGPL-2.0+ +# +# 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 + +. $(dirname $0)/libtest.sh + +setup_fake_remote_repo1 "archive" "--generate-sizes" + +echo '1..3' + +cd ${test_tmpdir} +mkdir repo +ostree_repo_init repo +${CMD_PREFIX} ostree --repo=repo remote add --set=gpg-verify=false origin $(cat httpd-address)/ostree/gnomerepo + +# Pull commit metadata only. All size and objects will be needed. +${CMD_PREFIX} ostree --repo=repo pull --commit-metadata-only origin main +${CMD_PREFIX} ostree --repo=repo show --print-sizes origin:main > show.txt +assert_file_has_content show.txt 'Compressed size (needed/total): 637[  ]bytes/637[  ]bytes' +assert_file_has_content show.txt 'Unpacked size (needed/total): 457[  ]bytes/457[  ]bytes' +assert_file_has_content show.txt 'Number of objects (needed/total): 10/10' +echo "ok sizes commit metadata only" + +# Pull the parent commit so we get most of the objects +parent=$(${CMD_PREFIX} ostree --repo=repo rev-parse origin:main^) +${CMD_PREFIX} ostree --repo=repo pull origin ${parent} +${CMD_PREFIX} ostree --repo=repo show --print-sizes origin:main > show.txt +assert_file_has_content show.txt 'Compressed size (needed/total): 501[  ]bytes/637[  ]bytes' +assert_file_has_content show.txt 'Unpacked size (needed/total): 429[  ]bytes/457[  ]bytes' +assert_file_has_content show.txt 'Number of objects (needed/total): 6/10' +echo "ok sizes commit partial" + +# Finish pulling the commit and check that no objects needed +${CMD_PREFIX} ostree --repo=repo pull origin main +${CMD_PREFIX} ostree --repo=repo show --print-sizes origin:main > show.txt +assert_file_has_content show.txt 'Compressed size (needed/total): 0[  ]bytes/637[  ]bytes' +assert_file_has_content show.txt 'Unpacked size (needed/total): 0[  ]bytes/457[  ]bytes' +assert_file_has_content show.txt 'Number of objects (needed/total): 0/10' +echo "ok sizes commit full" diff --git a/tests/test-pull-summary-sigs.sh b/tests/test-pull-summary-sigs.sh index 821ae953..401e88c9 100755 --- a/tests/test-pull-summary-sigs.sh +++ b/tests/test-pull-summary-sigs.sh @@ -189,7 +189,7 @@ cp ${test_tmpdir}/ostree-srv/gnomerepo/summary.sig{.2,} if ${OSTREE} --repo=repo pull origin main 2>err.txt; then assert_not_reached "Successful pull with old summary" fi -assert_file_has_content err.txt "none are in trusted keyring" +assert_file_has_content err.txt "BAD signature" assert_has_file repo/tmp/cache/summaries/origin assert_has_file repo/tmp/cache/summaries/origin.sig cmp repo/tmp/cache/summaries/origin ${test_tmpdir}/ostree-srv/gnomerepo/summary.1 >&2 diff --git a/tests/test-remote-gpg-import.sh b/tests/test-remote-gpg-import.sh index 4d73fa11..cf13b499 100755 --- a/tests/test-remote-gpg-import.sh +++ b/tests/test-remote-gpg-import.sh @@ -28,7 +28,12 @@ unset OSTREE_GPG_HOME setup_fake_remote_repo1 "archive" -echo "1..4" +# Some tests require an appropriate gpg +num_non_gpg_tests=4 +num_gpg_tests=2 +num_tests=$((num_non_gpg_tests + num_gpg_tests)) + +echo "1..${num_tests}" cd ${test_tmpdir} mkdir repo @@ -163,7 +168,7 @@ ${OSTREE} remote add --set=gpgkeypath=${test_tmpdir}/gpghome/key1.asc,${test_tmp if ${OSTREE} pull R8:main 2>err.txt; then assert_not_reached "Unexpectedly succeeded at pulling with different key" fi -assert_file_has_content err.txt "GPG signatures found, but none are in trusted keyring" +assert_file_has_content err.txt "public key not found" # Test gpgkeypath success with directory containing a valid key ${OSTREE} remote add --set=gpgkeypath=${test_tmpdir}/gpghome/ R9 $(cat httpd-address)/ostree/gnomerepo @@ -243,7 +248,7 @@ ${OSTREE} remote add --set=gpgkeypath=${test_tmpdir}/gpghome/key2.asc R6 $(cat h if ${OSTREE} pull R6:main 2>err.txt; then assert_not_reached "Unexpectedly succeeded at pulling with different key" fi -assert_file_has_content err.txt "GPG signatures found, but none are in trusted keyring" +assert_file_has_content err.txt "public key not found" echo "ok" @@ -269,7 +274,7 @@ newrev=$(${CMD_PREFIX} ostree --repo=${test_tmpdir}/ostree-srv/gnomerepo rev-par if ${OSTREE} pull --require-static-deltas R1:main 2>err.txt; then assert_not_reached "Unexpectedly succeeded at pulling commit signed with untrusted key" fi -assert_file_has_content err.txt "GPG signatures found, but none are in trusted keyring" +assert_file_has_content err.txt "public key not found" echo "ok gpg untrusted signed commit for delta upgrades" @@ -280,3 +285,57 @@ ${CMD_PREFIX} ostree --repo=${test_tmpdir}/ostree-srv/gnomerepo summary -u --gpg ${OSTREE} pull --require-static-deltas R1:main echo "ok gpg trusted signed commit for delta upgrades" + +# Run some more tests if an appropriate gpg is available +GPG=$(which_gpg) +if [ -z "${GPG}" ]; then + # Print a skip message per skipped test + for (( i = 0; i < num_gpg_tests; i++ )); do + echo "ok # SKIP this test requires gpg" + done +else + # Create a commit signed with keyid 1 + echo $(date) > workdir/testfile-for-key-mangling + ${CMD_PREFIX} ostree --repo=${test_tmpdir}/ostree-srv/gnomerepo commit -b main --gpg-sign ${TEST_GPG_KEYID_1} --gpg-homedir ${test_tmpdir}/gpghome workdir + + # Re-add the remote + ${OSTREE} remote delete R1 + ${OSTREE} remote add --gpg-import ${test_tmpdir}/gpghome/key1.asc R1 $(cat httpd-address)/ostree/gnomerepo | grep -o 'Imported [[:digit:]] GPG key' > result + assert_file_has_content result 'Imported 1 GPG key' + + # Expire key 1, wait for it to be expired and import the expired key. Only + # new keys are reported. + ${GPG} --homedir=${test_tmpdir}/gpghome --quick-set-expire ${TEST_GPG_KEYFPR_1} seconds=1 + sleep 2 + ${GPG} --homedir=${test_tmpdir}/gpghome --armor --export ${TEST_GPG_KEYID_1} > ${test_tmpdir}/key1expired.asc + ${OSTREE} remote gpg-import --keyring ${test_tmpdir}/key1expired.asc R1 | grep -o 'Imported [[:digit:]] GPG key' > result + assert_file_has_content result 'Imported 0 GPG key' + + # Pulling should fail since the key is expired + rm repo/refs/remotes/* -rf + ${OSTREE} prune --refs-only + if ${OSTREE} pull R1:main 2>err.txt; then + assert_not_reached "Unexpectedly succeeded at pulling commit signed with expired key" + fi + assert_file_has_content err.txt "Key expired" + + echo "ok imported expired key" + + # Unexpire keyid 1 and revoke it. Revoking is done by importing the + # pre-generated revocation certificate. + ${GPG} --homedir=${test_tmpdir}/gpghome --quick-set-expire ${TEST_GPG_KEYFPR_1} seconds=0 + ${GPG} --homedir=${TEST_GPG_KEYHOME} --import ${TEST_GPG_KEYHOME}/revocations/key1.rev + ${GPG} --homedir=${test_tmpdir}/gpghome --armor --export ${TEST_GPG_KEYID_1} > ${test_tmpdir}/key1revoked.asc + ${OSTREE} remote gpg-import --keyring ${test_tmpdir}/key1revoked.asc R1 | grep -o 'Imported [[:digit:]] GPG key' > result + assert_file_has_content result 'Imported 0 GPG key' + + # Pulling should fail since the key is revoked + rm repo/refs/remotes/* -rf + ${OSTREE} prune --refs-only + if ${OSTREE} pull R1:main 2>err.txt; then + assert_not_reached "Unexpectedly succeeded at pulling commit signed with revoked key" + fi + assert_file_has_content err.txt "Key revoked" + + echo "ok imported revoked key" +fi diff --git a/tests/test-sizes.js b/tests/test-sizes.js index 73b179c5..a2246536 100755 --- a/tests/test-sizes.js +++ b/tests/test-sizes.js @@ -28,11 +28,110 @@ function assertEquals(a, b) { throw new Error("assertion failed " + JSON.stringify(a) + " == " + JSON.stringify(b)); } -print('1..1') +function assertGreater(a, b) { + if (a <= b) + throw new Error("assertion failed " + JSON.stringify(a) + " > " + JSON.stringify(b)); +} + +function assertGreaterEquals(a, b) { + if (a < b) + throw new Error("assertion failed " + JSON.stringify(a) + " >= " + JSON.stringify(b)); +} + +// Adapted from _ostree_read_varuint64() +function readVarint(buffer) { + let result = 0; + let count = 0; + let len = buffer.length; + let cur; + + do { + assertGreater(len, 0); + cur = buffer[count]; + result = result | ((cur & 0x7F) << (7 * count)); + count++; + len--; + } while (cur & 0x80); + + return [result, count]; +} + +// There have been various bugs with byte array unpacking in GJS, so +// just do it manually. +function unpackByteArray(variant) { + let array = []; + let nBytes = variant.n_children(); + for (let i = 0; i < nBytes; i++) { + array.push(variant.get_child_value(i).get_byte()); + } + return array; +} + +function validateSizes(repo, commit, expectedObjects) { + let [,commitVariant] = repo.load_variant(OSTree.ObjectType.COMMIT, commit); + let metadata = commitVariant.get_child_value(0); + let sizes = metadata.lookup_value('ostree.sizes', GLib.VariantType.new('aay')); + let nObjects = sizes.n_children(); + let expectedNObjects = Object.keys(expectedObjects).length + assertEquals(nObjects, expectedNObjects); + + for (let i = 0; i < nObjects; i++) { + let sizeEntry = sizes.get_child_value(i); + assertGreaterEquals(sizeEntry.n_children(), 34); + let entryBytes = unpackByteArray(sizeEntry); + let checksumBytes = entryBytes.slice(0, 32); + let checksumString = OSTree.checksum_from_bytes(checksumBytes); + print("checksum = " + checksumString); + + // Read the sizes from the next 2 varints + let remainingBytes = entryBytes.slice(32); + assertGreaterEquals(remainingBytes.length, 2); + let varintRead; + let compressedSize; + let uncompressedSize; + [compressedSize, varintRead] = readVarint(remainingBytes); + remainingBytes = remainingBytes.slice(varintRead); + assertGreaterEquals(remainingBytes.length, 1); + [uncompressedSize, varintRead] = readVarint(remainingBytes); + remainingBytes = remainingBytes.slice(varintRead); + assertEquals(remainingBytes.length, 1); + let objectType = remainingBytes[0]; + let objectTypeString = OSTree.object_type_to_string(objectType); + print("compressed = " + compressedSize); + print("uncompressed = " + uncompressedSize); + print("objtype = " + objectTypeString + " (" + objectType + ")"); + let objectName = OSTree.object_to_string(checksumString, objectType); + print("object = " + objectName); + + if (!(objectName in expectedObjects)) { + throw new Error("Object " + objectName + " not in " + + JSON.stringify(expectedObjects)); + } + let expectedSizes = expectedObjects[objectName]; + let expectedCompressedSize = expectedSizes[0]; + let expectedUncompressedSize = expectedSizes[1]; + if (compressedSize != expectedCompressedSize) { + throw new Error("Compressed size " + compressedSize + + " for checksum " + checksumString + + " does not match expected " + expectedCompressedSize); + } + if (uncompressedSize != expectedUncompressedSize) { + throw new Error("Uncompressed size " + uncompressedSize + + " for checksum " + checksumString + + " does not match expected " + expectedUncompressedSize); + } + } +} + +print('1..3') let testDataDir = Gio.File.new_for_path('test-data'); testDataDir.make_directory(null); testDataDir.get_child('some-file').replace_contents("hello world!", null, false, 0, null); +testDataDir.get_child('some-file').copy(testDataDir.get_child('duplicate-file'), + Gio.FileCopyFlags.OVERWRITE, + null, null); +testDataDir.get_child('link-file').make_symbolic_link('some-file', null); testDataDir.get_child('another-file').replace_contents("hello world again!", null, false, 0, null); let repoPath = Gio.File.new_for_path('repo'); @@ -41,7 +140,10 @@ repo.create(OSTree.RepoMode.ARCHIVE_Z2, null); repo.open(null); -let commitModifier = OSTree.RepoCommitModifier.new(OSTree.RepoCommitModifierFlags.GENERATE_SIZES, null); +let commitModifierFlags = (OSTree.RepoCommitModifierFlags.GENERATE_SIZES | + OSTree.RepoCommitModifierFlags.SKIP_XATTRS | + OSTree.RepoCommitModifierFlags.CANONICAL_PERMISSIONS); +let commitModifier = OSTree.RepoCommitModifier.new(commitModifierFlags, null); assertEquals(repo.get_mode(), OSTree.RepoMode.ARCHIVE_Z2); @@ -55,32 +157,61 @@ print("commit => " + commit); repo.commit_transaction(null); -// Test the sizes metadata -let [,commitVariant] = repo.load_variant(OSTree.ObjectType.COMMIT, commit); -let metadata = commitVariant.get_child_value(0); -let sizes = metadata.lookup_value('ostree.sizes', GLib.VariantType.new('aay')); -let nSizes = sizes.n_children(); -assertEquals(nSizes, 2); -let expectedUncompressedSizes = [12, 18]; -let foundExpectedUncompressedSizes = 0; -for (let i = 0; i < nSizes; i++) { - let sizeEntry = sizes.get_child_value(i); - assertEquals(sizeEntry.n_children(), 34); - let compressedSize = sizeEntry.get_child_value(32).get_byte(); - let uncompressedSize = sizeEntry.get_child_value(33).get_byte(); - print("compressed = " + compressedSize); - print("uncompressed = " + uncompressedSize); - for (let j = 0; j < expectedUncompressedSizes.length; j++) { - let expected = expectedUncompressedSizes[j]; - if (expected == uncompressedSize) { - print("Matched expected uncompressed size " + expected); - expectedUncompressedSizes.splice(j, 1); - break; - } - } -} -if (expectedUncompressedSizes.length > 0) { - throw new Error("Failed to match expectedUncompressedSizes: " + JSON.stringify(expectedUncompressedSizes)); -} +// Test the sizes metadata. The key is the object and the value is an +// array of compressed size and uncompressed size. +let expectedObjects = { + 'f5ee222a21e2c96edbd6f2543c4bc8a039f827be3823d04777c9ee187778f1ad.file': [ + 54, 18 + ], + 'd35bfc50864fca777dbeead3ba3689115b76674a093210316589b1fe5cc3ff4b.file': [ + 48, 12 + ], + '8322876a078e79d8c960b8b4658fe77e7b2f878f8a6cf89dbb87c6becc8128a0.file': [ + 43, 9 + ], + '1c77033ca06eae77ed99cb26472969964314ffd5b4e4c0fd3ff6ec4265c86e51.dirtree': [ + 185, 185 + ], + '446a0ef11b7cc167f3b603e585c7eeeeb675faa412d5ec73f62988eb0b6c5488.dirmeta': [ + 12, 12 + ], +}; +validateSizes(repo, commit, expectedObjects); print("ok test-sizes"); + +// Remove a file to make sure that metadata is not reused from the +// previous commit. Remove that file from the expected metadata and +// replace the dirtree object. +testDataDir.get_child('another-file').delete(null); +delete expectedObjects['f5ee222a21e2c96edbd6f2543c4bc8a039f827be3823d04777c9ee187778f1ad.file']; +delete expectedObjects['1c77033ca06eae77ed99cb26472969964314ffd5b4e4c0fd3ff6ec4265c86e51.dirtree']; +expectedObjects['a384660cc18ffdb60296961dde9a2d6f78f4fec095165652cb53aa81f6dc7539.dirtree'] = [ + 138, 138 +]; + +repo.prepare_transaction(null); +mtree = OSTree.MutableTree.new(); +repo.write_directory_to_mtree(testDataDir, mtree, commitModifier, null); +[,dirTree] = repo.write_mtree(mtree, null); +[,commit] = repo.write_commit(null, 'Some subject', 'Some body', null, dirTree, null); +print("commit => " + commit); +repo.commit_transaction(null); + +validateSizes(repo, commit, expectedObjects); + +print("ok test-sizes file deleted"); + +// Repeat the commit now that all the objects are cached and ensure the +// metadata is still correct +repo.prepare_transaction(null); +mtree = OSTree.MutableTree.new(); +repo.write_directory_to_mtree(testDataDir, mtree, commitModifier, null); +[,dirTree] = repo.write_mtree(mtree, null); +[,commit] = repo.write_commit(null, 'Another subject', 'Another body', null, dirTree, null); +print("commit => " + commit); +repo.commit_transaction(null); + +validateSizes(repo, commit, expectedObjects); + +print("ok test-sizes repeated"); diff --git a/tests/test-switchroot.sh b/tests/test-switchroot.sh index bc3ec38b..70b2391d 100755 --- a/tests/test-switchroot.sh +++ b/tests/test-switchroot.sh @@ -2,6 +2,16 @@ this_script="${BASH_SOURCE:-$(readlink -f "$0")}" +OSTREE_PREPARE_ROOT=$(dirname "${this_script}")/../ostree-prepare-root +if [ ! -x "${OSTREE_PREPARE_ROOT}" ]; then + # ostree-prepare-root is in $libdir by default, assume we can find it + # based on our test directory, if not we'll have to skip this test. + OSTREE_PREPARE_ROOT=$(dirname "${this_script}")/../../../lib/ostree/ostree-prepare-root + if [ ! -x "${OSTREE_PREPARE_ROOT}" ]; then + OSTREE_PREPARE_ROOT="" + fi +fi + setup_bootfs() { mkdir -p "$1/proc" "$1/bin" @@ -13,7 +23,7 @@ setup_bootfs() { mount --bind "$1/override_cmdline" "$1/proc/cmdline" touch "$1/this_is_bootfs" - cp "$(dirname "$this_script")/../ostree-prepare-root" "$1/bin" + cp "${OSTREE_PREPARE_ROOT}" "$1/bin" } setup_rootfs() { @@ -56,7 +66,7 @@ find_in_env() { "$1" "$tmpdir" enter_fs "$tmpdir" ostree-prepare-root /sysroot - find / + find / \( -path /proc -o -path /sysroot/proc \) -prune -o -print touch /usr/usr_writable 2>/null \ && echo "/usr is writable" \ || echo "/usr is not writable" @@ -82,7 +92,9 @@ test_that_prepare_root_sets_sysroot_up_correctly_with_initrd() { grep -qx "/this_is_bootfs" files grep -qx "/sysroot/this_is_ostree_root" files grep -qx "/sysroot/sysroot/this_is_real_root" files - grep -qx "/sysroot/var/this_is_ostree_var" files + if ! have_systemd_and_libmount; then + grep -qx "/sysroot/var/this_is_ostree_var" files + fi grep -qx "/sysroot/usr/this_is_ostree_usr" files grep -qx "/sysroot/usr is not writable" files @@ -101,7 +113,9 @@ test_that_prepare_root_sets_root_up_correctly_with_no_initrd() { grep -qx "/this_is_ostree_root" files grep -qx "/sysroot/this_is_bootfs" files grep -qx "/sysroot/this_is_real_root" files - grep -qx "/var/this_is_ostree_var" files + if ! have_systemd_and_libmount; then + grep -qx "/var/this_is_ostree_var" files + fi grep -qx "/usr/this_is_ostree_usr" files grep -qx "/usr is not writable" files @@ -130,6 +144,9 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then [ -f /bin/busybox ] || \ skip "this test needs busybox" + [ -n "${OSTREE_PREPARE_ROOT}" ] || \ + skip "this test needs ostree-prepare-root" + echo "1..3" test_that_prepare_root_sets_sysroot_up_correctly_with_initrd test_that_prepare_root_sets_root_up_correctly_with_no_initrd diff --git a/tests/test-symbols.sh b/tests/test-symbols.sh index a041ddb1..f6742d93 100755 --- a/tests/test-symbols.sh +++ b/tests/test-symbols.sh @@ -54,7 +54,7 @@ echo 'ok documented symbols' # ONLY update this checksum in release commits! cat > released-sha256.txt <