diff --git a/Makefile-boot.am b/Makefile-boot.am index d3d2f673..5b512b6c 100644 --- a/Makefile-boot.am +++ b/Makefile-boot.am @@ -39,7 +39,7 @@ endif if BUILDOPT_SYSTEMD systemdsystemunit_DATA = src/boot/ostree-prepare-root.service \ - src/boot/ostree-remount.service + src/boot/ostree-remount.service src/boot/ostree-finalize-staged.service systemdtmpfilesdir = $(prefix)/lib/tmpfiles.d dist_systemdtmpfiles_DATA = src/boot/ostree-tmpfiles.conf @@ -65,6 +65,7 @@ EXTRA_DIST += src/boot/dracut/module-setup.sh \ src/boot/mkinitcpio/ostree \ src/boot/ostree-prepare-root.service \ src/boot/ostree-remount.service \ + src/boot/ostree-finalize-staged.service \ src/boot/grub2/grub2-15_ostree \ src/boot/grub2/ostree-grub-generator \ $(NULL) diff --git a/Makefile-man.am b/Makefile-man.am index 4d99cde1..342af520 100644 --- a/Makefile-man.am +++ b/Makefile-man.am @@ -45,7 +45,7 @@ man1_files += rofiles-fuse.1 endif if ENABLE_EXPERIMENTAL_API -man1_files += ostree-find-remotes.1 +man1_files += ostree-find-remotes.1 ostree-create-usb.1 endif man5_files = ostree.repo.5 ostree.repo-config.5 diff --git a/Makefile-ostree.am b/Makefile-ostree.am index cccbe300..91d8383d 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -52,6 +52,9 @@ ostree_SOURCES = src/ostree/main.c \ src/ostree/ot-editor.c \ src/ostree/ot-editor.h \ src/ostree/parse-datetime.h \ + $(NULL) + +nodist_ostree_SOURCES = \ src/ostree/parse-datetime.c \ $(NULL) @@ -67,6 +70,7 @@ ostree_SOURCES += \ src/ostree/ot-admin-builtin-init-fs.c \ src/ostree/ot-admin-builtin-diff.c \ src/ostree/ot-admin-builtin-deploy.c \ + src/ostree/ot-admin-builtin-finalize-staged.c \ src/ostree/ot-admin-builtin-undeploy.c \ src/ostree/ot-admin-builtin-instutil.c \ src/ostree/ot-admin-builtin-cleanup.c \ diff --git a/Makefile.in b/Makefile.in index db100456..689b0347 100644 --- a/Makefile.in +++ b/Makefile.in @@ -565,7 +565,7 @@ check_PROGRAMS = $(am__EXEEXT_13) $(am__EXEEXT_14) $(am__EXEEXT_15) # We still want to distribute the source, even if we are not building it @BUILDOPT_TRIVIAL_HTTPD_FALSE@@ENABLE_MAN_TRUE@am__append_87 = man/ostree-trivial-httpd.xml @BUILDOPT_FUSE_TRUE@@ENABLE_MAN_TRUE@am__append_88 = rofiles-fuse.1 -@ENABLE_EXPERIMENTAL_API_TRUE@@ENABLE_MAN_TRUE@am__append_89 = ostree-find-remotes.1 +@ENABLE_EXPERIMENTAL_API_TRUE@@ENABLE_MAN_TRUE@am__append_89 = ostree-find-remotes.1 ostree-create-usb.1 @ENABLE_MAN_TRUE@am__append_90 = $(man1_MANS:.1=.xml) $(man5_MANS:.5=.xml) @ENABLE_MAN_TRUE@am__append_91 = \ @ENABLE_MAN_TRUE@ $(man1_MANS) \ @@ -994,12 +994,12 @@ am__ostree_SOURCES_DIST = src/ostree/main.c \ src/ostree/ot-builtin-static-delta.c src/ostree/ot-main.h \ src/ostree/ot-main.c src/ostree/ot-dump.h src/ostree/ot-dump.c \ src/ostree/ot-editor.c src/ostree/ot-editor.h \ - src/ostree/parse-datetime.h src/ostree/parse-datetime.c \ - src/ostree/ot-builtin-create-usb.c \ + src/ostree/parse-datetime.h src/ostree/ot-builtin-create-usb.c \ src/ostree/ot-builtin-find-remotes.c \ src/ostree/ot-admin-builtin-init-fs.c \ src/ostree/ot-admin-builtin-diff.c \ src/ostree/ot-admin-builtin-deploy.c \ + src/ostree/ot-admin-builtin-finalize-staged.c \ src/ostree/ot-admin-builtin-undeploy.c \ src/ostree/ot-admin-builtin-instutil.c \ src/ostree/ot-admin-builtin-cleanup.c \ @@ -1067,12 +1067,12 @@ am_ostree_OBJECTS = src/ostree/ostree-main.$(OBJEXT) \ src/ostree/ostree-ot-builtin-static-delta.$(OBJEXT) \ src/ostree/ostree-ot-main.$(OBJEXT) \ src/ostree/ostree-ot-dump.$(OBJEXT) \ - src/ostree/ostree-ot-editor.$(OBJEXT) \ - src/ostree/ostree-parse-datetime.$(OBJEXT) $(am__objects_1) \ + src/ostree/ostree-ot-editor.$(OBJEXT) $(am__objects_1) \ $(am__objects_11) \ src/ostree/ostree-ot-admin-builtin-init-fs.$(OBJEXT) \ src/ostree/ostree-ot-admin-builtin-diff.$(OBJEXT) \ src/ostree/ostree-ot-admin-builtin-deploy.$(OBJEXT) \ + src/ostree/ostree-ot-admin-builtin-finalize-staged.$(OBJEXT) \ src/ostree/ostree-ot-admin-builtin-undeploy.$(OBJEXT) \ src/ostree/ostree-ot-admin-builtin-instutil.$(OBJEXT) \ src/ostree/ostree-ot-admin-builtin-cleanup.$(OBJEXT) \ @@ -1096,7 +1096,9 @@ am_ostree_OBJECTS = src/ostree/ostree-main.$(OBJEXT) \ src/ostree/ostree-ot-remote-builtin-refs.$(OBJEXT) \ src/ostree/ostree-ot-remote-builtin-summary.$(OBJEXT) \ $(am__objects_1) $(am__objects_12) $(am__objects_13) -ostree_OBJECTS = $(am_ostree_OBJECTS) +nodist_ostree_OBJECTS = src/ostree/ostree-parse-datetime.$(OBJEXT) \ + $(am__objects_1) +ostree_OBJECTS = $(am_ostree_OBJECTS) $(nodist_ostree_OBJECTS) ostree_DEPENDENCIES = $(am__DEPENDENCIES_11) libbsdiff.la \ libostree-kernel-args.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_8) $(am__DEPENDENCIES_4) @@ -1431,8 +1433,8 @@ SOURCES = $(libbsdiff_la_SOURCES) $(libbupsplit_la_SOURCES) \ $(libostree_kernel_args_la_SOURCES) \ $(libostreetest_la_SOURCES) $(libotutil_la_SOURCES) \ $(libreaddir_rand_la_SOURCES) $(ostree_SOURCES) \ - $(ostree_prepare_root_SOURCES) $(ostree_remount_SOURCES) \ - $(ostree_system_generator_SOURCES) \ + $(nodist_ostree_SOURCES) $(ostree_prepare_root_SOURCES) \ + $(ostree_remount_SOURCES) $(ostree_system_generator_SOURCES) \ $(ostree_trivial_httpd_SOURCES) $(rofiles_fuse_SOURCES) \ $(test_libglnx_errors_SOURCES) $(test_libglnx_fdio_SOURCES) \ $(test_libglnx_macros_SOURCES) $(test_libglnx_shutil_SOURCES) \ @@ -2106,7 +2108,9 @@ EXTRA_DIST = $(all_dist_test_scripts) $(all_dist_test_data) autogen.sh \ src/boot/dracut/module-setup.sh src/boot/dracut/ostree.conf \ src/boot/mkinitcpio/ostree \ src/boot/ostree-prepare-root.service \ - src/boot/ostree-remount.service src/boot/grub2/grub2-15_ostree \ + src/boot/ostree-remount.service \ + src/boot/ostree-finalize-staged.service \ + src/boot/grub2/grub2-15_ostree \ src/boot/grub2/ostree-grub-generator $(NULL) $(am__append_87) \ $(am__append_90) bin_SCRIPTS = @@ -2460,10 +2464,11 @@ ostree_SOURCES = src/ostree/main.c src/ostree/ot-builtin-admin.c \ src/ostree/ot-builtin-static-delta.c src/ostree/ot-main.h \ src/ostree/ot-main.c src/ostree/ot-dump.h src/ostree/ot-dump.c \ src/ostree/ot-editor.c src/ostree/ot-editor.h \ - src/ostree/parse-datetime.h src/ostree/parse-datetime.c \ - $(NULL) $(am__append_49) src/ostree/ot-admin-builtin-init-fs.c \ + src/ostree/parse-datetime.h $(NULL) $(am__append_49) \ + src/ostree/ot-admin-builtin-init-fs.c \ src/ostree/ot-admin-builtin-diff.c \ src/ostree/ot-admin-builtin-deploy.c \ + src/ostree/ot-admin-builtin-finalize-staged.c \ src/ostree/ot-admin-builtin-undeploy.c \ src/ostree/ot-admin-builtin-instutil.c \ src/ostree/ot-admin-builtin-cleanup.c \ @@ -2490,6 +2495,10 @@ ostree_SOURCES = src/ostree/main.c src/ostree/ot-builtin-admin.c \ src/ostree/ot-remote-builtin-refs.c \ src/ostree/ot-remote-builtin-summary.c $(NULL) \ $(am__append_50) $(am__append_51) +nodist_ostree_SOURCES = \ + src/ostree/parse-datetime.c \ + $(NULL) + ostree_bin_shared_cflags = $(AM_CFLAGS) -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree \ -I$(builddir)/src/libostree -I$(srcdir)/src/ostree -I$(srcdir)/libglnx $(OT_INTERNAL_GIO_UNIX_CFLAGS) \ -DPKGLIBEXECDIR=\"$(pkglibexecdir)\" @@ -2763,7 +2772,7 @@ tests_test_gpg_verify_result_LDADD = $(TESTS_LDADD) $(OT_INTERNAL_GPGME_LIBS) @BUILDOPT_MKINITCPIO_TRUE@mkinitcpioconfdir = $(sysconfdir) @BUILDOPT_MKINITCPIO_TRUE@mkinitcpioconf_DATA = src/boot/mkinitcpio/ostree-mkinitcpio.conf @BUILDOPT_SYSTEMD_TRUE@systemdsystemunit_DATA = src/boot/ostree-prepare-root.service \ -@BUILDOPT_SYSTEMD_TRUE@ src/boot/ostree-remount.service +@BUILDOPT_SYSTEMD_TRUE@ src/boot/ostree-remount.service src/boot/ostree-finalize-staged.service @BUILDOPT_SYSTEMD_TRUE@systemdtmpfilesdir = $(prefix)/lib/tmpfiles.d @BUILDOPT_SYSTEMD_TRUE@dist_systemdtmpfiles_DATA = src/boot/ostree-tmpfiles.conf @@ -3733,9 +3742,6 @@ src/ostree/ostree-ot-dump.$(OBJEXT): src/ostree/$(am__dirstamp) \ src/ostree/$(DEPDIR)/$(am__dirstamp) src/ostree/ostree-ot-editor.$(OBJEXT): src/ostree/$(am__dirstamp) \ src/ostree/$(DEPDIR)/$(am__dirstamp) -src/ostree/ostree-parse-datetime.$(OBJEXT): \ - src/ostree/$(am__dirstamp) \ - src/ostree/$(DEPDIR)/$(am__dirstamp) src/ostree/ostree-ot-builtin-create-usb.$(OBJEXT): \ src/ostree/$(am__dirstamp) \ src/ostree/$(DEPDIR)/$(am__dirstamp) @@ -3751,6 +3757,9 @@ src/ostree/ostree-ot-admin-builtin-diff.$(OBJEXT): \ src/ostree/ostree-ot-admin-builtin-deploy.$(OBJEXT): \ src/ostree/$(am__dirstamp) \ src/ostree/$(DEPDIR)/$(am__dirstamp) +src/ostree/ostree-ot-admin-builtin-finalize-staged.$(OBJEXT): \ + src/ostree/$(am__dirstamp) \ + src/ostree/$(DEPDIR)/$(am__dirstamp) src/ostree/ostree-ot-admin-builtin-undeploy.$(OBJEXT): \ src/ostree/$(am__dirstamp) \ src/ostree/$(DEPDIR)/$(am__dirstamp) @@ -3832,6 +3841,9 @@ src/ostree/ostree-ot-builtin-pull.$(OBJEXT): \ src/ostree/ostree-ot-builtin-trivial-httpd.$(OBJEXT): \ src/ostree/$(am__dirstamp) \ src/ostree/$(DEPDIR)/$(am__dirstamp) +src/ostree/ostree-parse-datetime.$(OBJEXT): \ + src/ostree/$(am__dirstamp) \ + src/ostree/$(DEPDIR)/$(am__dirstamp) ostree$(EXEEXT): $(ostree_OBJECTS) $(ostree_DEPENDENCIES) $(EXTRA_ostree_DEPENDENCIES) @rm -f ostree$(EXEEXT) @@ -4423,6 +4435,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-cleanup.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-deploy.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-diff.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-finalize-staged.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-init-fs.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-instutil.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-os-init.Po@am__quote@ @@ -5507,20 +5520,6 @@ src/ostree/ostree-ot-editor.obj: src/ostree/ot-editor.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) $(ostree_CFLAGS) $(CFLAGS) -c -o src/ostree/ostree-ot-editor.obj `if test -f 'src/ostree/ot-editor.c'; then $(CYGPATH_W) 'src/ostree/ot-editor.c'; else $(CYGPATH_W) '$(srcdir)/src/ostree/ot-editor.c'; fi` -src/ostree/ostree-parse-datetime.o: src/ostree/parse-datetime.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ostree_CFLAGS) $(CFLAGS) -MT src/ostree/ostree-parse-datetime.o -MD -MP -MF src/ostree/$(DEPDIR)/ostree-parse-datetime.Tpo -c -o src/ostree/ostree-parse-datetime.o `test -f 'src/ostree/parse-datetime.c' || echo '$(srcdir)/'`src/ostree/parse-datetime.c -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/ostree/$(DEPDIR)/ostree-parse-datetime.Tpo src/ostree/$(DEPDIR)/ostree-parse-datetime.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/ostree/parse-datetime.c' object='src/ostree/ostree-parse-datetime.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) $(ostree_CFLAGS) $(CFLAGS) -c -o src/ostree/ostree-parse-datetime.o `test -f 'src/ostree/parse-datetime.c' || echo '$(srcdir)/'`src/ostree/parse-datetime.c - -src/ostree/ostree-parse-datetime.obj: src/ostree/parse-datetime.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ostree_CFLAGS) $(CFLAGS) -MT src/ostree/ostree-parse-datetime.obj -MD -MP -MF src/ostree/$(DEPDIR)/ostree-parse-datetime.Tpo -c -o src/ostree/ostree-parse-datetime.obj `if test -f 'src/ostree/parse-datetime.c'; then $(CYGPATH_W) 'src/ostree/parse-datetime.c'; else $(CYGPATH_W) '$(srcdir)/src/ostree/parse-datetime.c'; fi` -@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/ostree/$(DEPDIR)/ostree-parse-datetime.Tpo src/ostree/$(DEPDIR)/ostree-parse-datetime.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/ostree/parse-datetime.c' object='src/ostree/ostree-parse-datetime.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) $(ostree_CFLAGS) $(CFLAGS) -c -o src/ostree/ostree-parse-datetime.obj `if test -f 'src/ostree/parse-datetime.c'; then $(CYGPATH_W) 'src/ostree/parse-datetime.c'; else $(CYGPATH_W) '$(srcdir)/src/ostree/parse-datetime.c'; fi` - src/ostree/ostree-ot-builtin-create-usb.o: src/ostree/ot-builtin-create-usb.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ostree_CFLAGS) $(CFLAGS) -MT src/ostree/ostree-ot-builtin-create-usb.o -MD -MP -MF src/ostree/$(DEPDIR)/ostree-ot-builtin-create-usb.Tpo -c -o src/ostree/ostree-ot-builtin-create-usb.o `test -f 'src/ostree/ot-builtin-create-usb.c' || echo '$(srcdir)/'`src/ostree/ot-builtin-create-usb.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/ostree/$(DEPDIR)/ostree-ot-builtin-create-usb.Tpo src/ostree/$(DEPDIR)/ostree-ot-builtin-create-usb.Po @@ -5591,6 +5590,20 @@ src/ostree/ostree-ot-admin-builtin-deploy.obj: src/ostree/ot-admin-builtin-deplo @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) $(ostree_CFLAGS) $(CFLAGS) -c -o src/ostree/ostree-ot-admin-builtin-deploy.obj `if test -f 'src/ostree/ot-admin-builtin-deploy.c'; then $(CYGPATH_W) 'src/ostree/ot-admin-builtin-deploy.c'; else $(CYGPATH_W) '$(srcdir)/src/ostree/ot-admin-builtin-deploy.c'; fi` +src/ostree/ostree-ot-admin-builtin-finalize-staged.o: src/ostree/ot-admin-builtin-finalize-staged.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ostree_CFLAGS) $(CFLAGS) -MT src/ostree/ostree-ot-admin-builtin-finalize-staged.o -MD -MP -MF src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-finalize-staged.Tpo -c -o src/ostree/ostree-ot-admin-builtin-finalize-staged.o `test -f 'src/ostree/ot-admin-builtin-finalize-staged.c' || echo '$(srcdir)/'`src/ostree/ot-admin-builtin-finalize-staged.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-finalize-staged.Tpo src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-finalize-staged.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/ostree/ot-admin-builtin-finalize-staged.c' object='src/ostree/ostree-ot-admin-builtin-finalize-staged.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) $(ostree_CFLAGS) $(CFLAGS) -c -o src/ostree/ostree-ot-admin-builtin-finalize-staged.o `test -f 'src/ostree/ot-admin-builtin-finalize-staged.c' || echo '$(srcdir)/'`src/ostree/ot-admin-builtin-finalize-staged.c + +src/ostree/ostree-ot-admin-builtin-finalize-staged.obj: src/ostree/ot-admin-builtin-finalize-staged.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ostree_CFLAGS) $(CFLAGS) -MT src/ostree/ostree-ot-admin-builtin-finalize-staged.obj -MD -MP -MF src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-finalize-staged.Tpo -c -o src/ostree/ostree-ot-admin-builtin-finalize-staged.obj `if test -f 'src/ostree/ot-admin-builtin-finalize-staged.c'; then $(CYGPATH_W) 'src/ostree/ot-admin-builtin-finalize-staged.c'; else $(CYGPATH_W) '$(srcdir)/src/ostree/ot-admin-builtin-finalize-staged.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-finalize-staged.Tpo src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-finalize-staged.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/ostree/ot-admin-builtin-finalize-staged.c' object='src/ostree/ostree-ot-admin-builtin-finalize-staged.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) $(ostree_CFLAGS) $(CFLAGS) -c -o src/ostree/ostree-ot-admin-builtin-finalize-staged.obj `if test -f 'src/ostree/ot-admin-builtin-finalize-staged.c'; then $(CYGPATH_W) 'src/ostree/ot-admin-builtin-finalize-staged.c'; else $(CYGPATH_W) '$(srcdir)/src/ostree/ot-admin-builtin-finalize-staged.c'; fi` + src/ostree/ostree-ot-admin-builtin-undeploy.o: src/ostree/ot-admin-builtin-undeploy.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ostree_CFLAGS) $(CFLAGS) -MT src/ostree/ostree-ot-admin-builtin-undeploy.o -MD -MP -MF src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-undeploy.Tpo -c -o src/ostree/ostree-ot-admin-builtin-undeploy.o `test -f 'src/ostree/ot-admin-builtin-undeploy.c' || echo '$(srcdir)/'`src/ostree/ot-admin-builtin-undeploy.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-undeploy.Tpo src/ostree/$(DEPDIR)/ostree-ot-admin-builtin-undeploy.Po @@ -5969,6 +5982,20 @@ src/ostree/ostree-ot-builtin-trivial-httpd.obj: src/ostree/ot-builtin-trivial-ht @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) $(ostree_CFLAGS) $(CFLAGS) -c -o src/ostree/ostree-ot-builtin-trivial-httpd.obj `if test -f 'src/ostree/ot-builtin-trivial-httpd.c'; then $(CYGPATH_W) 'src/ostree/ot-builtin-trivial-httpd.c'; else $(CYGPATH_W) '$(srcdir)/src/ostree/ot-builtin-trivial-httpd.c'; fi` +src/ostree/ostree-parse-datetime.o: src/ostree/parse-datetime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ostree_CFLAGS) $(CFLAGS) -MT src/ostree/ostree-parse-datetime.o -MD -MP -MF src/ostree/$(DEPDIR)/ostree-parse-datetime.Tpo -c -o src/ostree/ostree-parse-datetime.o `test -f 'src/ostree/parse-datetime.c' || echo '$(srcdir)/'`src/ostree/parse-datetime.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/ostree/$(DEPDIR)/ostree-parse-datetime.Tpo src/ostree/$(DEPDIR)/ostree-parse-datetime.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/ostree/parse-datetime.c' object='src/ostree/ostree-parse-datetime.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) $(ostree_CFLAGS) $(CFLAGS) -c -o src/ostree/ostree-parse-datetime.o `test -f 'src/ostree/parse-datetime.c' || echo '$(srcdir)/'`src/ostree/parse-datetime.c + +src/ostree/ostree-parse-datetime.obj: src/ostree/parse-datetime.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(ostree_CFLAGS) $(CFLAGS) -MT src/ostree/ostree-parse-datetime.obj -MD -MP -MF src/ostree/$(DEPDIR)/ostree-parse-datetime.Tpo -c -o src/ostree/ostree-parse-datetime.obj `if test -f 'src/ostree/parse-datetime.c'; then $(CYGPATH_W) 'src/ostree/parse-datetime.c'; else $(CYGPATH_W) '$(srcdir)/src/ostree/parse-datetime.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/ostree/$(DEPDIR)/ostree-parse-datetime.Tpo src/ostree/$(DEPDIR)/ostree-parse-datetime.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='src/ostree/parse-datetime.c' object='src/ostree/ostree-parse-datetime.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) $(ostree_CFLAGS) $(CFLAGS) -c -o src/ostree/ostree-parse-datetime.obj `if test -f 'src/ostree/parse-datetime.c'; then $(CYGPATH_W) 'src/ostree/parse-datetime.c'; else $(CYGPATH_W) '$(srcdir)/src/ostree/parse-datetime.c'; fi` + src/switchroot/ostree_prepare_root-ostree-prepare-root.o: src/switchroot/ostree-prepare-root.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(ostree_prepare_root_CPPFLAGS) $(CPPFLAGS) $(ostree_prepare_root_CFLAGS) $(CFLAGS) -MT src/switchroot/ostree_prepare_root-ostree-prepare-root.o -MD -MP -MF src/switchroot/$(DEPDIR)/ostree_prepare_root-ostree-prepare-root.Tpo -c -o src/switchroot/ostree_prepare_root-ostree-prepare-root.o `test -f 'src/switchroot/ostree-prepare-root.c' || echo '$(srcdir)/'`src/switchroot/ostree-prepare-root.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) src/switchroot/$(DEPDIR)/ostree_prepare_root-ostree-prepare-root.Tpo src/switchroot/$(DEPDIR)/ostree_prepare_root-ostree-prepare-root.Po diff --git a/README.md b/README.md index bf9088bd..36bcfc24 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,9 @@ bootloader configuration. The core OSTree model is like git in that it checksums individual files and has a content-addressed-object store. It's unlike git in that it "checks out" the -files via hardlinks, and they should thus be immutable. Therefore, another way -to think of OSTree is that it's just a more polished version -of +files via hardlinks, and they thus need to be immutable to prevent corruption. +Therefore, another way to think of OSTree is that it's just a more polished +version of [Linux VServer hardlinks](http://linux-vserver.org/index.php?title=util-vserver:Vhashify&oldid=2285). **Features:** @@ -113,5 +113,12 @@ See [Contributing](CONTRIBUTING.md). Licensing ------- -The license for the *code* of libostree can be found in [COPYING](COPYING). -The license for the *documentation* of libostree is: `SPDX-License-Identifier: (CC-BY-SA-3.0 OR GFDL-1.3-or-later)` +The licensing for the *code* of libostree can be canonically found in the individual files; +and the overall status in the [COPYING](https://github.com/ostreedev/ostree/blob/master/COPYING) +file in the source. Currently, that's LGPLv2+. This also covers the man pages and API docs. + +The license for the manual documentation in the `doc/` directory is: +`SPDX-License-Identifier: (CC-BY-SA-3.0 OR GFDL-1.3-or-later)` +This is intended to allow use by Wikipedia and other projects. + +In general, files should have a `SPDX-License-Identifier` and that is canonical. diff --git a/apidoc/html/ostree-OstreeRepo.html b/apidoc/html/ostree-OstreeRepo.html index 80160bef..acb78c8d 100644 --- a/apidoc/html/ostree-OstreeRepo.html +++ b/apidoc/html/ostree-OstreeRepo.html @@ -881,6 +881,22 @@ +GHashTable * + + +ostree_repo_traverse_new_parents () + + + + +char ** + + +ostree_repo_traverse_parents_get_commits () + + + + gboolean @@ -897,6 +913,14 @@ +gboolean + + +ostree_repo_traverse_commit_union_with_parents () + + + + void @@ -2659,7 +2683,7 @@ checksum.

There is an upfront cost to creating this mapping, as this will scan the entire objects directory. If your commit is composed of mostly hardlinks to existing ostree objects, then this will speed up considerably, so call it -before you call ostree_write_directory_to_mtree() or similar. However, +before you call ostree_repo_write_directory_to_mtree() or similar. However, ostree_repo_devino_cache_new() is better as it avoids scanning all objects.

Multithreading: This function is *not* MT safe.

@@ -2709,10 +2733,9 @@ further writing refs is also not currently atomic.

of OstreeRepo; however, it is safe to have multiple threads writing objects on a single OstreeRepo instance as long as their lifetime is bounded by the transaction.

-

Multithreading: This function is *not* MT safe; only one transaction can be +

Locking: Acquires a shared lock; release via commit or abort +Multithreading: This function is *not* MT safe; only one transaction can be active at a time.

-

This function takes a shared lock on the self - repository.

Parameters

@@ -2761,7 +2784,8 @@ ostree_repo_commit_transaction (ostree_repo_transaction_set_refspec() will be written out.

Note that if multiple threads are performing writes, all such threads must have terminated before this function is invoked.

-

Multithreading: This function is *not* MT safe; only one transaction can be +

Locking: Releases shared lock acquired by ostree_repo_prepare_transaction() +Multithreading: This function is *not* MT safe; only one transaction can be active at a time.

Parameters

@@ -3282,7 +3306,7 @@ ostree_repo_write_metadata (guchar **out_csum, GCancellable *cancellable, GError **error); -

Store the metadata object variant +

Store the metadata object object . Return the checksum as out_csum .

@@ -6202,6 +6226,38 @@ ostree_repo_traverse_new_reachable ( +

ostree_repo_traverse_new_parents ()

+
GHashTable *
+ostree_repo_traverse_new_parents (void);
+

This hash table is a mapping from GVariant which can be accessed +via ostree_object_name_deserialize() to a GVariant containing either +a similar GVariant or and array of them, listing the parents of the key.

+
+

Returns

+

A new hash table.

+

[transfer container][element-type GVariant GVariant]

+
+

Since: 2018.5

+
+
+
+

ostree_repo_traverse_parents_get_commits ()

+
char **
+ostree_repo_traverse_parents_get_commits
+                               (GHashTable *parents,
+                                GVariant *object);
+

Gets all the commits that a certain object belongs to, as recorded +by a parents table gotten from ostree_repo_traverse_commit_union_with_parents.

+
+

Returns

+

An array of checksums for +the commits the key belongs to.

+

[transfer full][array zero-terminated=1]

+
+

Since: 2018.5

+
+
+

ostree_repo_traverse_commit ()

gboolean
 ostree_repo_traverse_commit (OstreeRepo *repo,
@@ -6319,6 +6375,76 @@ from commit_checksum
 

+

ostree_repo_traverse_commit_union_with_parents ()

+
gboolean
+ostree_repo_traverse_commit_union_with_parents
+                               (OstreeRepo *repo,
+                                const char *commit_checksum,
+                                int maxdepth,
+                                GHashTable *inout_reachable,
+                                GHashTable *inout_parents,
+                                GCancellable *cancellable,
+                                GError **error);
+

Update the set inout_reachable + containing all objects reachable +from commit_checksum +, traversing maxdepth + parent commits.

+

Additionally this constructs a mapping from each object to the parents +of the object, which can be used to track which commits an object +belongs to.

+

[skip]

+
+

Parameters

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

repo

Repo

 

commit_checksum

ASCII SHA256 checksum

 

maxdepth

Traverse this many parent commits, -1 for unlimited

 

inout_reachable

Set of reachable objects

 

inout_parents

Map from object to parent object

 

cancellable

Cancellable

 

error

Error

 
+
+

Since: 2018.5

+
+
+

ostree_repo_commit_traverse_iter_cleanup ()

void
 ostree_repo_commit_traverse_iter_cleanup
@@ -6584,8 +6710,7 @@ history from the repository.

Use the OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE to just determine statistics on objects that would be deleted, without actually deleting them.

-

This function takes an exclusive lock on the self - repository.

+

Locking: exclusive

Parameters

@@ -6650,8 +6775,7 @@ ostree_repo_prune_static_deltas (Prune static deltas, if COMMIT is specified then delete static delta files only targeting that commit; otherwise any static delta of non existing commits are deleted.

-

This function takes an exclusive lock on the self - repository.

+

Locking: exclusive

Parameters

@@ -6706,8 +6830,7 @@ retain all commits from a production branch, but just GC some history from your dev branch.

The OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE flag may be specified to just determine statistics on objects that would be deleted, without actually deleting them.

-

This function takes an exclusive lock on the self - repository.

+

Locking: exclusive

Parameters

@@ -7508,11 +7631,11 @@ will aid clients in working out when to check for updates.

It is regenerated automatically after a commit if core/commit-update-summary is set.

If the core/collection-id key is set in the configuration, it will be -included as OSTREE_SUMMARY_COLLECTION_ID in the summary file. Refs from the -refs/mirrors directory will be included in the generated summary file, -listed under the OSTREE_SUMMARY_COLLECTION_MAP key. Collection IDs and refs -in OSTREE_SUMMARY_COLLECTION_MAP are guaranteed to be in lexicographic -order.

+included as OSTREE_SUMMARY_COLLECTION_ID in the summary file. Refs that +have associated collection IDs will be included in the generated summary +file, listed under the OSTREE_SUMMARY_COLLECTION_MAP key. Collection IDs +and refs in OSTREE_SUMMARY_COLLECTION_MAP are guaranteed to be in +lexicographic order.

Parameters

diff --git a/apidoc/html/ostree-Root-partition-mount-point.html b/apidoc/html/ostree-Root-partition-mount-point.html index d05195f3..d7d4a1c7 100644 --- a/apidoc/html/ostree-Root-partition-mount-point.html +++ b/apidoc/html/ostree-Root-partition-mount-point.html @@ -233,6 +233,14 @@ + + + + + + + + @@ -1063,6 +1079,32 @@ ostree_sysroot_get_repo ( +

ostree_sysroot_get_staged_deployment ()

+
OstreeDeployment *
+ostree_sysroot_get_staged_deployment (OstreeSysroot *self);
+
+

Parameters

+
+OstreeDeployment * + +ostree_sysroot_get_staged_deployment () +
gboolean @@ -300,6 +308,14 @@ gboolean +ostree_sysroot_stage_tree () +
+gboolean + ostree_sysroot_deploy_tree ()
+++++ + + + + + +

self

Sysroot

 
+
+
+

Returns

+

The currently staged deployment, or NULL if none.

+

[transfer none]

+
+
+
+

ostree_sysroot_init_osname ()

gboolean
 ostree_sysroot_init_osname (OstreeSysroot *self,
@@ -1464,6 +1506,79 @@ this function will write the current origin of deplo
 

+

ostree_sysroot_stage_tree ()

+
gboolean
+ostree_sysroot_stage_tree (OstreeSysroot *self,
+                           const char *osname,
+                           const char *revision,
+                           GKeyFile *origin,
+                           OstreeDeployment *merge_deployment,
+                           char **override_kernel_argv,
+                           OstreeDeployment **out_new_deployment,
+                           GCancellable *cancellable,
+                           GError **error);
+

Like ostree_sysroot_deploy_tree(), but "finalization" only occurs at OS +shutdown time.

+
+

Parameters

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

self

Sysroot

 

osname

osname to use for merge deployment.

[allow-none]

revision

Checksum to add

 

origin

Origin to use for upgrades.

[allow-none]

merge_deployment

Use this deployment for merge path.

[allow-none]

override_kernel_argv

Use these as kernel arguments; if NULL, inherit options from provided_merge_deployment.

[allow-none][array zero-terminated=1][element-type utf8]

out_new_deployment

The new deployment path.

[out]

cancellable

Cancellable

 

error

Error

 
+
+
+
+

ostree_sysroot_deploy_tree ()

gboolean
 ostree_sysroot_deploy_tree (OstreeSysroot *self,
@@ -1479,6 +1594,8 @@ ostree_sysroot_deploy_tree (provided_merge_deployment
  for configuration.

+

While this API is not deprecated, you most likely want to use the +ostree_sysroot_stage_tree() API.

Parameters

diff --git a/apidoc/html/ostree-ostree-deployment.html b/apidoc/html/ostree-ostree-deployment.html index bdf259fe..5df93985 100644 --- a/apidoc/html/ostree-ostree-deployment.html +++ b/apidoc/html/ostree-ostree-deployment.html @@ -150,6 +150,14 @@ + + + + + + + + +
+gboolean + +ostree_deployment_is_staged () +
void @@ -425,6 +433,32 @@ ostree_deployment_is_pinned ( +

ostree_deployment_is_staged ()

+
gboolean
+ostree_deployment_is_staged (OstreeDeployment *self);
+
+

Parameters

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

self

Deployment

 
+
+
+

Returns

+

TRUE if deployment should be "finalized" at shutdown time

+
+

Since: 2018.3

+ +
+

ostree_deployment_set_index ()

void
 ostree_deployment_set_index (OstreeDeployment *self,
@@ -537,6 +571,7 @@ ostree_deployment_unlocked_state_to_string
   OstreeBootconfigParser *bootconfig;
   GKeyFile *origin;
   OstreeDeploymentUnlockedState unlocked;
+  gboolean staged;
 } OstreeDeployment;
 
@@ -593,6 +628,11 @@ ostree_deployment_unlocked_state_to_string

The unlocked state

 

gboolean staged;

TRUE iff this deployment is staged

 
diff --git a/apidoc/html/ostree.devhelp2 b/apidoc/html/ostree.devhelp2 index 1c33dbda..17996f71 100644 --- a/apidoc/html/ostree.devhelp2 +++ b/apidoc/html/ostree.devhelp2 @@ -189,8 +189,11 @@ + + + @@ -271,6 +274,7 @@ + @@ -279,6 +283,7 @@ + @@ -364,6 +369,7 @@ + @@ -500,5 +506,6 @@ + diff --git a/apidoc/html/reference.html b/apidoc/html/reference.html index 231b9288..11e1abfa 100644 --- a/apidoc/html/reference.html +++ b/apidoc/html/reference.html @@ -363,6 +363,10 @@ OSTREE_CHECK_VERSION, macro in ostree-version
+ostree_deployment_is_staged, function in ostree-deployment +
+
+
ostree_deployment_new, function in ostree-deployment
@@ -1174,10 +1178,22 @@ OSTREE_RELEASE_VERSION, macro in ostree-version
+ostree_repo_traverse_commit_union_with_parents, function in OstreeRepo +
+
+
+ostree_repo_traverse_new_parents, function in OstreeRepo +
+
+
ostree_repo_traverse_new_reachable, function in OstreeRepo
+ostree_repo_traverse_parents_get_commits, function in OstreeRepo +
+
+
ostree_repo_verify_commit, function in OstreeRepo
@@ -1407,6 +1423,10 @@ OSTREE_RELEASE_VERSION, macro in ostree-version
+ostree_sysroot_get_staged_deployment, function in Root partition mount point +
+
+
ostree_sysroot_get_subbootversion, function in Root partition mount point
@@ -1463,6 +1483,10 @@ OSTREE_RELEASE_VERSION, macro in ostree-version
+ostree_sysroot_stage_tree, function in Root partition mount point +
+
+
ostree_sysroot_try_lock, function in Root partition mount point
diff --git a/apidoc/ostree-experimental-sections.txt b/apidoc/ostree-experimental-sections.txt index 0d168406..60daaca5 100644 --- a/apidoc/ostree-experimental-sections.txt +++ b/apidoc/ostree-experimental-sections.txt @@ -90,12 +90,6 @@ ostree_repo_finder_override_get_type
ostree-misc-experimental -OstreeRepoLockType -ostree_repo_lock_push -ostree_repo_lock_pop -OstreeRepoAutoLock -ostree_repo_auto_lock_push -ostree_repo_auto_lock_cleanup ostree_repo_get_collection_id ostree_repo_set_collection_id ostree_validate_collection_id diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index 55f2e7a9..5162b2f7 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -170,6 +170,7 @@ ostree_deployment_get_origin ostree_deployment_get_origin_relpath ostree_deployment_get_unlocked ostree_deployment_is_pinned +ostree_deployment_is_staged ostree_deployment_set_index ostree_deployment_set_bootserial ostree_deployment_set_bootconfig @@ -395,8 +396,11 @@ OstreeStaticDeltaGenerateOpt ostree_repo_static_delta_generate ostree_repo_static_delta_execute_offline ostree_repo_traverse_new_reachable +ostree_repo_traverse_new_parents +ostree_repo_traverse_parents_get_commits ostree_repo_traverse_commit ostree_repo_traverse_commit_union +ostree_repo_traverse_commit_union_with_parents ostree_repo_commit_traverse_iter_cleanup ostree_repo_commit_traverse_iter_clear ostree_repo_commit_traverse_iter_get_dir @@ -506,6 +510,7 @@ ostree_sysroot_cleanup ostree_sysroot_prepare_cleanup ostree_sysroot_repo ostree_sysroot_get_repo +ostree_sysroot_get_staged_deployment ostree_sysroot_init_osname ostree_sysroot_deployment_set_kargs ostree_sysroot_deployment_set_mutable @@ -514,6 +519,7 @@ ostree_sysroot_deployment_set_pinned ostree_sysroot_write_deployments ostree_sysroot_write_deployments_with_options ostree_sysroot_write_origin_file +ostree_sysroot_stage_tree ostree_sysroot_deploy_tree ostree_sysroot_get_merge_deployment ostree_sysroot_query_deployments_for diff --git a/configure b/configure index 0cb23000..397c0b05 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 2018.4. +# Generated by GNU Autoconf 2.69 for libostree 2018.5. # # Report bugs to . # @@ -590,8 +590,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='libostree' PACKAGE_TARNAME='libostree' -PACKAGE_VERSION='2018.4' -PACKAGE_STRING='libostree 2018.4' +PACKAGE_VERSION='2018.5' +PACKAGE_STRING='libostree 2018.5' PACKAGE_BUGREPORT='walters@verbum.org' PACKAGE_URL='' @@ -1542,7 +1542,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 2018.4 to adapt to many kinds of systems. +\`configure' configures libostree 2018.5 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1612,7 +1612,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libostree 2018.4:";; + short | recursive ) echo "Configuration of libostree 2018.5:";; esac cat <<\_ACEOF @@ -1855,7 +1855,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libostree configure 2018.4 +libostree configure 2018.5 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2327,7 +2327,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 2018.4, which was +It was created by libostree $as_me 2018.5, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3195,7 +3195,7 @@ fi # Define the identity of the package. PACKAGE='libostree' - VERSION='2018.4' + VERSION='2018.5' # Some tools Automake needs. @@ -5929,9 +5929,9 @@ test -n "$YACC" || YACC="yacc" YEAR_VERSION=2018 -RELEASE_VERSION=4 +RELEASE_VERSION=5 -PACKAGE_VERSION=2018.4 +PACKAGE_VERSION=2018.5 if echo "$CFLAGS" | grep -q -E -e '-Werror($| )'; then : @@ -18447,7 +18447,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 2018.4, which was +This file was extended by libostree $as_me 2018.5, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -18513,7 +18513,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 2018.4 +libostree config.status 2018.5 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 35962dfa..afedc030 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ dnl update libostree-released.sym from libostree-devel.sym, and update the check dnl in test-symbols.sh, and also set is_release_build=yes below. Then make dnl another post-release commit to bump the version, and set is_release_build=no. m4_define([year_version], [2018]) -m4_define([release_version], [4]) +m4_define([release_version], [5]) m4_define([package_version], [year_version.release_version]) AC_INIT([libostree], [package_version], [walters@verbum.org]) is_release_build=yes diff --git a/libglnx/glnx-missing-syscall.h b/libglnx/glnx-missing-syscall.h index ebfd7f47..4876ca39 100644 --- a/libglnx/glnx-missing-syscall.h +++ b/libglnx/glnx-missing-syscall.h @@ -140,10 +140,10 @@ static inline int memfd_create(const char *name, unsigned int flags) { # endif # endif -static inline ssize_t copy_file_range(int fd_in, loff_t *off_in, - int fd_out, loff_t *off_out, - size_t len, - unsigned int flags) { +static inline ssize_t missing_copy_file_range(int fd_in, loff_t *off_in, + int fd_out, loff_t *off_out, + size_t len, + unsigned int flags) { # ifdef __NR_copy_file_range return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags); # else @@ -151,4 +151,6 @@ static inline ssize_t copy_file_range(int fd_in, loff_t *off_in, return -1; # endif } + +# define copy_file_range missing_copy_file_range #endif diff --git a/man/ostree-init.xml b/man/ostree-init.xml index b74648db..b2726321 100644 --- a/man/ostree-init.xml +++ b/man/ostree-init.xml @@ -69,13 +69,24 @@ Boston, MA 02111-1307, USA. ="MODE" - Initialize repository in given mode - (bare, bare-user, - archive). The default is - bare. Note that for - archive the repository configuration file - will actually have archive-z2, as that's the - historical name. + + Initialize repository in given mode + (bare, bare-user, + bare-user-only, archive). + The default is bare. Note that for + archive the repository configuration file + will actually have archive-z2, as that's + the historical name. + + See the manual for differences between these modes. + Briefly, bare mode stores files as they + are, so they can be directly hardlinked, + bare-user uses extended attributes to + store ownership and xattr information, allowing non-root + operations, bare-user-only does not store + ownership information, and archive stores + files compressed, to be served over the network. + diff --git a/man/ostree.xml b/man/ostree.xml index 06076c45..f8d3e2fc 100644 --- a/man/ostree.xml +++ b/man/ostree.xml @@ -111,14 +111,14 @@ Boston, MA 02111-1307, USA. - For most commands, - when run as non-root, repository is - required. If - ostree is run as - root, it is assumed operations will be - performed on the - /sysroot/ostree/repo - repository. + For most commands, a repository is + required. If unspecified, the current + directory is used if it appears to be an + OSTree repository. If it isn't, either + the OSTREE_REPO + environment variable is used, or the + system repository located at + /sysroot/ostree/repo. @@ -283,6 +283,15 @@ Boston, MA 02111-1307, USA. + + ostree-diff1 diff --git a/src/boot/ostree-finalize-staged.service b/src/boot/ostree-finalize-staged.service new file mode 100644 index 00000000..570138cd --- /dev/null +++ b/src/boot/ostree-finalize-staged.service @@ -0,0 +1,36 @@ +# Copyright (C) 2018 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# For some implementation discussion, see: +# https://lists.freedesktop.org/archives/systemd-devel/2018-March/040557.html +[Unit] +Description=OSTree Finalize Staged Deployment +ConditionPathExists=/run/ostree-booted +DefaultDependencies=no + +RequiresMountsFor=/sysroot +After=basic.target +Before=multi-user.target final.target +Conflicts=final.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStop=/usr/bin/ostree admin finalize-staged + +[Install] +WantedBy=multi-user.target diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index 3377ae12..46ad280c 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -18,8 +18,6 @@ ***/ /* Add new symbols here. Release commits should copy this section into -released.sym. */ -LIBOSTREE_2018.5 { -} LIBOSTREE_2018.3; /* Stub section for the stable release *after* this development one; don't * edit this other than to update the last number. This is just a copy/paste diff --git a/src/libostree/libostree-experimental.sym b/src/libostree/libostree-experimental.sym index 3f3454f3..b83ad1b0 100644 --- a/src/libostree/libostree-experimental.sym +++ b/src/libostree/libostree-experimental.sym @@ -94,8 +94,4 @@ LIBOSTREE_2017.14_EXPERIMENTAL { global: ostree_remote_get_type; ostree_remote_get_url; - ostree_repo_auto_lock_cleanup; - ostree_repo_auto_lock_push; - ostree_repo_lock_pop; - ostree_repo_lock_push; } LIBOSTREE_2017.13_EXPERIMENTAL; diff --git a/src/libostree/libostree-released.sym b/src/libostree/libostree-released.sym index e9a95cc7..533bc5c2 100644 --- a/src/libostree/libostree-released.sym +++ b/src/libostree/libostree-released.sym @@ -464,6 +464,15 @@ LIBOSTREE_2018.3 { ostree_deployment_is_pinned; } LIBOSTREE_2018.2; +LIBOSTREE_2018.5 { + ostree_sysroot_stage_tree; + ostree_sysroot_get_staged_deployment; + ostree_deployment_is_staged; + ostree_repo_traverse_new_parents; + ostree_repo_traverse_parents_get_commits; + ostree_repo_traverse_commit_union_with_parents; +} LIBOSTREE_2018.3; + /* 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 75b498fc..504954e0 100644 --- a/src/libostree/ostree-autocleanups.h +++ b/src/libostree/ostree-autocleanups.h @@ -61,7 +61,6 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeSysrootUpgrader, g_object_unref) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (OstreeRepoCommitTraverseIter, ostree_repo_commit_traverse_iter_clear) #ifdef OSTREE_ENABLE_EXPERIMENTAL_API -G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoAutoLock, ostree_repo_auto_lock_cleanup) G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeCollectionRef, ostree_collection_ref_free) G_DEFINE_AUTO_CLEANUP_FREE_FUNC (OstreeCollectionRefv, ostree_collection_ref_freev, NULL) G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRemote, ostree_remote_unref) diff --git a/src/libostree/ostree-cmdprivate.c b/src/libostree/ostree-cmdprivate.c index 49d3f5e5..de82521c 100644 --- a/src/libostree/ostree-cmdprivate.c +++ b/src/libostree/ostree-cmdprivate.c @@ -26,7 +26,7 @@ #include "ostree-core-private.h" #include "ostree-repo-pull-private.h" #include "ostree-repo-static-delta-private.h" -#include "ostree-sysroot.h" +#include "ostree-sysroot-private.h" #include "ostree-bootloader-grub2.h" #include "otutil.h" @@ -52,7 +52,8 @@ ostree_cmd__private__ (void) _ostree_repo_static_delta_dump, _ostree_repo_static_delta_query_exists, _ostree_repo_static_delta_delete, - _ostree_repo_verify_bindings + _ostree_repo_verify_bindings, + _ostree_sysroot_finalize_staged, }; return &table; diff --git a/src/libostree/ostree-cmdprivate.h b/src/libostree/ostree-cmdprivate.h index 1ac5a1c8..592157bf 100644 --- a/src/libostree/ostree-cmdprivate.h +++ b/src/libostree/ostree-cmdprivate.h @@ -34,6 +34,7 @@ typedef struct { gboolean (* ostree_static_delta_query_exists) (OstreeRepo *repo, const char *delta_id, gboolean *out_exists, GCancellable *cancellable, GError **error); gboolean (* ostree_static_delta_delete) (OstreeRepo *repo, const char *delta_id, GCancellable *cancellable, GError **error); gboolean (* ostree_repo_verify_bindings) (const char *collection_id, const char *ref_name, GVariant *commit, GError **error); + gboolean (* ostree_finalize_staged) (OstreeSysroot *sysroot, GCancellable *cancellable, GError **error); } OstreeCmdPrivateVTable; /* Note this not really "public", we just export the symbol, but not the header */ diff --git a/src/libostree/ostree-deployment-private.h b/src/libostree/ostree-deployment-private.h index 114e2f63..ad77317d 100644 --- a/src/libostree/ostree-deployment-private.h +++ b/src/libostree/ostree-deployment-private.h @@ -36,6 +36,7 @@ G_BEGIN_DECLS * @bootconfig: Bootloader configuration * @origin: How to construct an upgraded version of this tree * @unlocked: The unlocked state + * @staged: TRUE iff this deployment is staged */ struct _OstreeDeployment { @@ -50,6 +51,7 @@ struct _OstreeDeployment OstreeBootconfigParser *bootconfig; GKeyFile *origin; OstreeDeploymentUnlockedState unlocked; + gboolean staged; }; void _ostree_deployment_set_bootcsum (OstreeDeployment *self, const char *bootcsum); diff --git a/src/libostree/ostree-deployment.c b/src/libostree/ostree-deployment.c index 75a5bd1d..820c2632 100644 --- a/src/libostree/ostree-deployment.c +++ b/src/libostree/ostree-deployment.c @@ -339,3 +339,16 @@ ostree_deployment_is_pinned (OstreeDeployment *self) return FALSE; return g_key_file_get_boolean (self->origin, OSTREE_ORIGIN_TRANSIENT_GROUP, "pinned", NULL); } + +/** + * ostree_deployment_is_staged: + * @self: Deployment + * + * Returns: `TRUE` if deployment should be "finalized" at shutdown time + * Since: 2018.3 + */ +gboolean +ostree_deployment_is_staged (OstreeDeployment *self) +{ + return self->staged; +} diff --git a/src/libostree/ostree-deployment.h b/src/libostree/ostree-deployment.h index 612222a2..756e39d2 100644 --- a/src/libostree/ostree-deployment.h +++ b/src/libostree/ostree-deployment.h @@ -73,7 +73,8 @@ OstreeBootconfigParser *ostree_deployment_get_bootconfig (OstreeDeployment *self _OSTREE_PUBLIC GKeyFile *ostree_deployment_get_origin (OstreeDeployment *self); - +_OSTREE_PUBLIC +gboolean ostree_deployment_is_staged (OstreeDeployment *self); _OSTREE_PUBLIC gboolean ostree_deployment_is_pinned (OstreeDeployment *self); diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c index 16081a95..86ee5e30 100644 --- a/src/libostree/ostree-repo-commit.c +++ b/src/libostree/ostree-repo-commit.c @@ -598,27 +598,26 @@ create_regular_tmpfile_linkable_with_content (OstreeRepo *self, } static gboolean -_check_support_reflink (OstreeRepo *self, gboolean *supported, GError **error) +_check_support_reflink (OstreeRepo *dest, gboolean *supported, GError **error) { - /* We have not checked yet if the file system supports reflinks, do it here */ - if (g_atomic_int_get (&self->fs_support_reflink) == 0) + /* We have not checked yet if the destination file system supports reflinks, do it here */ + if (g_atomic_int_get (&dest->fs_support_reflink) == 0) { - g_auto(GLnxTmpfile) src_tmpf = { 0, }; + glnx_autofd int src_fd = -1; g_auto(GLnxTmpfile) dest_tmpf = { 0, }; - if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (self), ".", O_RDWR|O_CLOEXEC, - &src_tmpf, error)) + if (!glnx_openat_rdonly (dest->repo_dir_fd, "config", TRUE, &src_fd, error)) return FALSE; - if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (self), ".", O_WRONLY|O_CLOEXEC, + if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (dest), ".", O_WRONLY|O_CLOEXEC, &dest_tmpf, error)) return FALSE; - if (ioctl (dest_tmpf.fd, FICLONE, src_tmpf.fd) == 0) - g_atomic_int_set (&self->fs_support_reflink, 1); + if (ioctl (dest_tmpf.fd, FICLONE, src_fd) == 0) + g_atomic_int_set (&dest->fs_support_reflink, 1); else if (errno == EOPNOTSUPP) /* Ignore other kind of errors as they might be temporary failures */ - g_atomic_int_set (&self->fs_support_reflink, -1); + g_atomic_int_set (&dest->fs_support_reflink, -1); } - *supported = g_atomic_int_get (&self->fs_support_reflink) >= 0; + *supported = g_atomic_int_get (&dest->fs_support_reflink) >= 0; return TRUE; } @@ -631,6 +630,7 @@ _create_payload_link (OstreeRepo *self, GError **error) { gboolean reflinks_supported = FALSE; + if (!_check_support_reflink (self, &reflinks_supported, error)) return FALSE; @@ -664,8 +664,8 @@ _create_payload_link (OstreeRepo *self, } static gboolean -_import_payload_link (OstreeRepo *self, - OstreeRepo *source, +_import_payload_link (OstreeRepo *dest_repo, + OstreeRepo *src_repo, const char *checksum, GCancellable *cancellable, GError **error) @@ -676,20 +676,24 @@ _import_payload_link (OstreeRepo *self, glnx_unref_object OtChecksumInstream *checksum_payload = NULL; g_autoptr(GFileInfo) file_info = NULL; - if (!_check_support_reflink (self, &reflinks_supported, error)) + /* The two repositories are on different devices */ + if (src_repo->device != dest_repo->device) + return TRUE; + + if (!_check_support_reflink (dest_repo, &reflinks_supported, error)) return FALSE; if (!reflinks_supported) return TRUE; - if (!G_IN_SET(self->mode, OSTREE_REPO_MODE_BARE, OSTREE_REPO_MODE_BARE_USER, OSTREE_REPO_MODE_BARE_USER_ONLY)) + if (!G_IN_SET(dest_repo->mode, OSTREE_REPO_MODE_BARE, OSTREE_REPO_MODE_BARE_USER, OSTREE_REPO_MODE_BARE_USER_ONLY)) return TRUE; - if (!ostree_repo_load_file (source, checksum, &is, &file_info, NULL, cancellable, error)) + if (!ostree_repo_load_file (src_repo, checksum, &is, &file_info, NULL, cancellable, error)) return FALSE; if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_REGULAR - || g_file_info_get_size (file_info) < self->payload_link_threshold) + || g_file_info_get_size (file_info) < dest_repo->payload_link_threshold) return TRUE; checksum_payload = ot_checksum_instream_new (is, G_CHECKSUM_SHA256); @@ -706,11 +710,12 @@ _import_payload_link (OstreeRepo *self, } payload_checksum = ot_checksum_instream_get_string (checksum_payload); - return _create_payload_link (self, checksum, payload_checksum, file_info, cancellable, error); + return _create_payload_link (dest_repo, checksum, payload_checksum, file_info, cancellable, error); } static gboolean _try_clone_from_payload_link (OstreeRepo *self, + OstreeRepo *dest_repo, const char *payload_checksum, GFileInfo *file_info, GLnxTmpfile *tmpf, @@ -722,7 +727,11 @@ _try_clone_from_payload_link (OstreeRepo *self, if (self->commit_stagedir.initialized) dfd_searches[0] = self->commit_stagedir.fd; - if (!_check_support_reflink (self, &reflinks_supported, error)) + /* The two repositories are on different devices */ + if (self->device != dest_repo->device) + return TRUE; + + if (!_check_support_reflink (dest_repo, &reflinks_supported, error)) return FALSE; if (!reflinks_supported) @@ -778,7 +787,7 @@ _try_clone_from_payload_link (OstreeRepo *self, } } if (self->parent_repo) - return _try_clone_from_payload_link (self->parent_repo, payload_checksum, file_info, tmpf, cancellable, error); + return _try_clone_from_payload_link (self->parent_repo, dest_repo, payload_checksum, file_info, tmpf, cancellable, error); return TRUE; } @@ -1073,7 +1082,7 @@ write_content_object (OstreeRepo *self, /* 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, actual_payload_checksum, file_info, &tmpf, cancellable, error)) + if (actual_payload_checksum && !_try_clone_from_payload_link (self, self, actual_payload_checksum, file_info, &tmpf, cancellable, error)) return FALSE; /* This path is for regular files */ @@ -1246,7 +1255,7 @@ write_metadata_object (OstreeRepo *self, } else { - OtChecksum checksum = { 0, }; + g_auto(OtChecksum) checksum = { 0, }; ot_checksum_init (&checksum); gsize len; const guint8*bufdata = g_bytes_get_data (buf, &len); @@ -1440,7 +1449,8 @@ scan_loose_devino (OstreeRepo *self, return FALSE; } - if (self->mode == OSTREE_REPO_MODE_ARCHIVE) + if (self->mode == OSTREE_REPO_MODE_ARCHIVE && + self->uncompressed_objects_dir_fd != -1) { if (!scan_one_loose_devino (self, self->uncompressed_objects_dir_fd, devino_cache, cancellable, error)) @@ -1500,7 +1510,7 @@ devino_cache_lookup (OstreeRepo *self, * There is an upfront cost to creating this mapping, as this will scan the * entire objects directory. If your commit is composed of mostly hardlinks to * existing ostree objects, then this will speed up considerably, so call it - * before you call ostree_write_directory_to_mtree() or similar. However, + * before you call ostree_repo_write_directory_to_mtree() or similar. However, * ostree_repo_devino_cache_new() is better as it avoids scanning all objects. * * Multithreading: This function is *not* MT safe. @@ -1541,10 +1551,9 @@ ostree_repo_scan_hardlinks (OstreeRepo *self, * on a single `OstreeRepo` instance as long as their lifetime is bounded by the * transaction. * + * Locking: Acquires a `shared` lock; release via commit or abort * Multithreading: This function is *not* MT safe; only one transaction can be * active at a time. - * - * This function takes a shared lock on the @self repository. */ gboolean ostree_repo_prepare_transaction (OstreeRepo *self, @@ -1557,8 +1566,8 @@ ostree_repo_prepare_transaction (OstreeRepo *self, memset (&self->txn.stats, 0, sizeof (OstreeRepoTransactionStats)); - self->txn_locked = ostree_repo_lock_push (self, OSTREE_REPO_LOCK_SHARED, - cancellable, error); + self->txn_locked = _ostree_repo_lock_push (self, OSTREE_REPO_LOCK_SHARED, + cancellable, error); if (!self->txn_locked) return FALSE; @@ -2070,6 +2079,7 @@ ostree_repo_set_collection_ref_immediate (OstreeRepo *self, * Note that if multiple threads are performing writes, all such threads must * have terminated before this function is invoked. * + * Locking: Releases `shared` lock acquired by `ostree_repo_prepare_transaction()` * Multithreading: This function is *not* MT safe; only one transaction can be * active at a time. */ @@ -2126,7 +2136,7 @@ ostree_repo_commit_transaction (OstreeRepo *self, if (self->txn_locked) { - if (!ostree_repo_lock_pop (self, cancellable, error)) + if (!_ostree_repo_lock_pop (self, cancellable, error)) return FALSE; self->txn_locked = FALSE; } @@ -2179,7 +2189,7 @@ ostree_repo_abort_transaction (OstreeRepo *self, if (self->txn_locked) { - if (!ostree_repo_lock_pop (self, cancellable, error)) + if (!_ostree_repo_lock_pop (self, cancellable, error)) return FALSE; self->txn_locked = FALSE; } @@ -2216,7 +2226,7 @@ metadata_size_valid (OstreeObjectType objtype, * @cancellable: Cancellable * @error: Error * - * Store the metadata object @variant. Return the checksum + * Store the metadata object @object. Return the checksum * as @out_csum. * * If @expected_checksum is not %NULL, verify it against the diff --git a/src/libostree/ostree-repo-finder-avahi.c b/src/libostree/ostree-repo-finder-avahi.c index 6687a835..1e77a6e0 100644 --- a/src/libostree/ostree-repo-finder-avahi.c +++ b/src/libostree/ostree-repo-finder-avahi.c @@ -844,7 +844,7 @@ ostree_avahi_service_build_repo_finder_result (OstreeAvahiService } g_ptr_array_add (results, ostree_repo_finder_result_new (remote, OSTREE_REPO_FINDER (finder), - priority, supported_ref_to_checksum, + priority, supported_ref_to_checksum, NULL, GUINT64_FROM_BE (g_variant_get_uint64 (summary_timestamp)))); } } @@ -1437,9 +1437,15 @@ ostree_repo_finder_avahi_start (OstreeRepoFinderAvahi *self, if (client == NULL) { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Failed to create finder client: %s", - avahi_strerror (failure)); + if (failure == AVAHI_ERR_NO_DAEMON) + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "Avahi daemon is not running: %s", + avahi_strerror (failure)); + else + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Failed to create finder client: %s", + avahi_strerror (failure)); + return; } diff --git a/src/libostree/ostree-repo-finder-config.c b/src/libostree/ostree-repo-finder-config.c index 76acb58e..5d1e1595 100644 --- a/src/libostree/ostree-repo-finder-config.c +++ b/src/libostree/ostree-repo-finder-config.c @@ -192,7 +192,7 @@ ostree_repo_finder_config_resolve_async (OstreeRepoFinder *find continue; } - g_ptr_array_add (results, ostree_repo_finder_result_new (remote, finder, priority, supported_ref_to_checksum, 0)); + g_ptr_array_add (results, ostree_repo_finder_result_new (remote, finder, priority, supported_ref_to_checksum, NULL, 0)); } g_ptr_array_sort (results, results_compare_cb); diff --git a/src/libostree/ostree-repo-finder-mount.c b/src/libostree/ostree-repo-finder-mount.c index 41a6bed2..09e85035 100644 --- a/src/libostree/ostree-repo-finder-mount.c +++ b/src/libostree/ostree-repo-finder-mount.c @@ -434,7 +434,8 @@ G_GNUC_END_IGNORE_DEPRECATIONS g_array_sort (repos_refs, repo_and_refs_compare); /* Also check the well-known special-case directories in the mount. - * Add them after sorting, so they’re always last. */ + * Add them after sorting, so they’re always last. + * NOTE: If you change these, update the man page. */ const gchar * const well_known_repos[] = { ".ostree/repo", @@ -545,7 +546,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS * the code in ostree_repo_pull_from_remotes_async() will be able to * check it just as quickly as we can here; so don’t duplicate the * code. */ - g_ptr_array_add (results, ostree_repo_finder_result_new (remote, finder, priority, supported_ref_to_checksum, 0)); + g_ptr_array_add (results, ostree_repo_finder_result_new (remote, finder, priority, supported_ref_to_checksum, NULL, 0)); } } diff --git a/src/libostree/ostree-repo-finder-override.c b/src/libostree/ostree-repo-finder-override.c index 5367708b..5bad9ace 100644 --- a/src/libostree/ostree-repo-finder-override.c +++ b/src/libostree/ostree-repo-finder-override.c @@ -243,7 +243,7 @@ ostree_repo_finder_override_resolve_async (OstreeRepoFinder *fi g_hash_table_iter_init (&iter, repo_remote_to_refs); while (g_hash_table_iter_next (&iter, (gpointer *) &remote, (gpointer *) &supported_ref_to_checksum)) - g_ptr_array_add (results, ostree_repo_finder_result_new (remote, finder, priority, supported_ref_to_checksum, 0)); + g_ptr_array_add (results, ostree_repo_finder_result_new (remote, finder, priority, supported_ref_to_checksum, NULL, 0)); g_ptr_array_sort (results, results_compare_cb); diff --git a/src/libostree/ostree-repo-finder.c b/src/libostree/ostree-repo-finder.c index 829e8c6d..e7943c3e 100644 --- a/src/libostree/ostree-repo-finder.c +++ b/src/libostree/ostree-repo-finder.c @@ -132,8 +132,8 @@ static void resolve_cb (GObject *obj, * which the result provides. If the result provides the latest commit for a ref * across all of the results, the checksum will be set. Otherwise, if the * result provides an outdated commit, or doesn’t provide a given ref at all, - * the ref will not be set. Results which provide none of the requested @refs - * may be listed with an empty refs map. + * the checksum will not be set. Results which provide none of the requested + * @refs may be listed with an empty refs map. * * Pass the results to ostree_repo_pull_from_remotes_async() to pull the given * @refs from those remotes. @@ -436,6 +436,9 @@ G_DEFINE_BOXED_TYPE (OstreeRepoFinderResult, ostree_repo_finder_result, * priority * @ref_to_checksum: (element-type OstreeCollectionRef utf8) (transfer none): * map of collection–ref pairs to checksums provided by this result + * @ref_to_timestamp: (element-type OstreeCollectionRef guint64) (nullable) + * (transfer none): map of collection–ref pairs to timestamps provided by this + * result * @summary_last_modified: Unix timestamp (seconds since the epoch, UTC) when * the summary file for the result was last modified, or `0` if this is unknown * @@ -450,6 +453,7 @@ ostree_repo_finder_result_new (OstreeRemote *remote, OstreeRepoFinder *finder, gint priority, GHashTable *ref_to_checksum, + GHashTable *ref_to_timestamp, guint64 summary_last_modified) { g_autoptr(OstreeRepoFinderResult) result = NULL; @@ -463,6 +467,7 @@ ostree_repo_finder_result_new (OstreeRemote *remote, result->finder = g_object_ref (finder); result->priority = priority; result->ref_to_checksum = g_hash_table_ref (ref_to_checksum); + result->ref_to_timestamp = ref_to_timestamp != NULL ? g_hash_table_ref (ref_to_timestamp) : NULL; result->summary_last_modified = summary_last_modified; return g_steal_pointer (&result); @@ -484,7 +489,7 @@ ostree_repo_finder_result_dup (OstreeRepoFinderResult *result) return ostree_repo_finder_result_new (result->remote, result->finder, result->priority, result->ref_to_checksum, - result->summary_last_modified); + result->ref_to_timestamp, result->summary_last_modified); } /** @@ -554,6 +559,7 @@ ostree_repo_finder_result_free (OstreeRepoFinderResult *result) /* This may be NULL iff the result is freed half-way through find_remotes_cb() * in ostree-repo-pull.c, and at no other time. */ g_clear_pointer (&result->ref_to_checksum, g_hash_table_unref); + g_clear_pointer (&result->ref_to_timestamp, g_hash_table_unref); g_object_unref (result->finder); ostree_remote_unref (result->remote); g_free (result); diff --git a/src/libostree/ostree-repo-finder.h b/src/libostree/ostree-repo-finder.h index bb1a437e..e622c9a6 100644 --- a/src/libostree/ostree-repo-finder.h +++ b/src/libostree/ostree-repo-finder.h @@ -99,6 +99,8 @@ GPtrArray *ostree_repo_finder_resolve_all_finish (GAsyncResult *result, * @ref_to_checksum: (element-type OstreeCollectionRef utf8): map of collection–ref * pairs to checksums provided by this remote; values may be %NULL to * indicate this remote doesn’t provide that ref + * @ref_to_timestamp: (element-type OstreeCollectionRef guint64) (nullable): map of + * collection–ref pairs to timestamps; values may be 0 for various reasons * @summary_last_modified: Unix timestamp (seconds since the epoch, UTC) when * the summary file on the remote was last modified, or `0` if unknown * @@ -122,6 +124,15 @@ GPtrArray *ostree_repo_finder_resolve_all_finish (GAsyncResult *result, * should be available locally, so the details for each checksum can be looked * up using ostree_repo_load_commit(). * + * @ref_to_timestamp provides timestamps for the set of refs in + * @ref_to_checksum. The refs are keys (of type #OstreeCollectionRef) and the + * values are guint64 pointers with the timestamp associated with the checksum + * provided in @ref_to_checksum. @ref_to_timestamp can be %NULL, and when it's + * not, the timestamps are zero when any of the following conditions are met: + * (1) the override-commit-ids option was used on + * ostree_repo_find_remotes_async (2) there was an error in trying to get the + * commit metadata (3) the checksum for this ref is %NULL in @ref_to_checksum. + * * Since: 2017.8 */ typedef struct @@ -131,9 +142,10 @@ typedef struct gint priority; GHashTable *ref_to_checksum; guint64 summary_last_modified; + GHashTable *ref_to_timestamp; /*< private >*/ - gpointer padding[4]; + gpointer padding[3]; } OstreeRepoFinderResult; _OSTREE_PUBLIC @@ -144,6 +156,7 @@ OstreeRepoFinderResult *ostree_repo_finder_result_new (OstreeRemote *remote, OstreeRepoFinder *finder, gint priority, GHashTable *ref_to_checksum, + GHashTable *ref_to_timestamp, guint64 summary_last_modified); _OSTREE_PUBLIC OstreeRepoFinderResult *ostree_repo_finder_result_dup (OstreeRepoFinderResult *result); diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h index 3078a9e2..77203638 100644 --- a/src/libostree/ostree-repo-private.h +++ b/src/libostree/ostree-repo-private.h @@ -435,34 +435,36 @@ _ostree_repo_get_remote_inherited (OstreeRepo *self, const char *name, GError **error); -#ifndef OSTREE_ENABLE_EXPERIMENTAL_API - -/* All the locking APIs below are duplicated in ostree-repo.h. Remove the ones - * here once it's no longer experimental. +/* Locking APIs are currently private. + * See https://github.com/ostreedev/ostree/pull/1555 */ - typedef enum { OSTREE_REPO_LOCK_SHARED, OSTREE_REPO_LOCK_EXCLUSIVE } OstreeRepoLockType; -gboolean ostree_repo_lock_push (OstreeRepo *self, +gboolean _ostree_repo_lock_push (OstreeRepo *self, OstreeRepoLockType lock_type, GCancellable *cancellable, GError **error); -gboolean ostree_repo_lock_pop (OstreeRepo *self, - GCancellable *cancellable, - GError **error); +gboolean _ostree_repo_lock_pop (OstreeRepo *self, + GCancellable *cancellable, + GError **error); typedef OstreeRepo OstreeRepoAutoLock; -OstreeRepoAutoLock * ostree_repo_auto_lock_push (OstreeRepo *self, - OstreeRepoLockType lock_type, - GCancellable *cancellable, - GError **error); -void ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *lock); -G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoAutoLock, ostree_repo_auto_lock_cleanup) +OstreeRepoAutoLock * _ostree_repo_auto_lock_push (OstreeRepo *self, + OstreeRepoLockType lock_type, + GCancellable *cancellable, + GError **error); +void _ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *lock); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoAutoLock, _ostree_repo_auto_lock_cleanup) +#ifndef OSTREE_ENABLE_EXPERIMENTAL_API + +/* These APIs are duplicated in the public headers when doing an + * experimental-API build. + */ const gchar * ostree_repo_get_collection_id (OstreeRepo *self); gboolean ostree_repo_set_collection_id (OstreeRepo *self, const gchar *collection_id, diff --git a/src/libostree/ostree-repo-prune.c b/src/libostree/ostree-repo-prune.c index f0c0a974..4c883542 100644 --- a/src/libostree/ostree-repo-prune.c +++ b/src/libostree/ostree-repo-prune.c @@ -193,7 +193,7 @@ _ostree_repo_prune_tmp (OstreeRepo *self, * targeting that commit; otherwise any static delta of non existing commits are * deleted. * - * This function takes an exclusive lock on the @self repository. + * Locking: exclusive */ gboolean ostree_repo_prune_static_deltas (OstreeRepo *self, const char *commit, @@ -201,8 +201,7 @@ ostree_repo_prune_static_deltas (OstreeRepo *self, const char *commit, GError **error) { g_autoptr(OstreeRepoAutoLock) lock = - ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, - error); + _ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, error); if (!lock) return FALSE; @@ -327,7 +326,7 @@ repo_prune_internal (OstreeRepo *self, * statistics on objects that would be deleted, without actually * deleting them. * - * This function takes an exclusive lock on the @self repository. + * Locking: exclusive */ gboolean ostree_repo_prune (OstreeRepo *self, @@ -340,8 +339,7 @@ ostree_repo_prune (OstreeRepo *self, GError **error) { g_autoptr(OstreeRepoAutoLock) lock = - ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, - error); + _ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, error); if (!lock) return FALSE; @@ -440,7 +438,7 @@ ostree_repo_prune (OstreeRepo *self, * The %OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE flag may be specified to just determine * statistics on objects that would be deleted, without actually deleting them. * - * This function takes an exclusive lock on the @self repository. + * Locking: exclusive */ gboolean ostree_repo_prune_from_reachable (OstreeRepo *self, @@ -452,8 +450,7 @@ ostree_repo_prune_from_reachable (OstreeRepo *self, GError **error) { g_autoptr(OstreeRepoAutoLock) lock = - ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, - error); + _ostree_repo_auto_lock_push (self, OSTREE_REPO_LOCK_EXCLUSIVE, cancellable, error); if (!lock) return FALSE; diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index 89c67c8e..f5745f86 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -2913,6 +2913,7 @@ repo_remote_fetch_summary (OstreeRepo *self, GVariant *options, GBytes **out_summary, GBytes **out_signatures, + gboolean *out_from_cache, GCancellable *cancellable, GError **error) { @@ -3015,32 +3016,13 @@ repo_remote_fetch_summary (OstreeRepo *self, goto out; } - if (!from_cache && *out_summary && *out_signatures) - { - g_autoptr(GError) temp_error = NULL; - - if (!_ostree_repo_cache_summary (self, - name, - *out_summary, - *out_signatures, - cancellable, - &temp_error)) - { - if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) - g_debug ("No permissions to save summary cache"); - else - { - g_propagate_error (error, g_steal_pointer (&temp_error)); - goto out; - } - } - } - ret = TRUE; out: if (mainctx) g_main_context_pop_thread_default (mainctx); + + *out_from_cache = from_cache; return ret; } @@ -4704,7 +4686,20 @@ ostree_repo_find_remotes_async (OstreeRepo *self, if (local_error != NULL) { - g_warning ("Avahi finder failed; removing it: %s", local_error->message); + /* See ostree-repo-finder-avahi.c:ostree_repo_finder_avahi_start, we + * intentionally throw this so as to distinguish between the Avahi + * finder failing because the Avahi daemon wasn't running and + * the Avahi finder failing because of some actual error. + * + * We need to distinguish between g_debug and g_warning here because + * unit tests that use this code may set G_DEBUG=fatal-warnings which + * would cause client code to abort if a warning were emitted. + */ + if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + g_debug ("Avahi finder failed under normal operation; removing it: %s", local_error->message); + else + g_warning ("Avahi finder failed abnormally; removing it: %s", local_error->message); + default_finders[2] = NULL; g_clear_object (&finder_avahi); } @@ -4764,7 +4759,7 @@ find_remotes_process_refs (OstreeRepo *self, for (j = 0, n = g_variant_n_children (summary_refs); j < n; j++) { const guchar *csum_bytes; - g_autoptr(GVariant) ref_v = NULL, csum_v = NULL, commit_metadata_v = NULL, stored_commit_metadata_v = NULL; + g_autoptr(GVariant) ref_v = NULL, csum_v = NULL, commit_metadata_v = NULL, stored_commit_v = NULL; guint64 commit_size, commit_timestamp; gchar tmp_checksum[OSTREE_SHA256_STRING_LEN + 1]; gsize ref_index; @@ -4801,9 +4796,9 @@ find_remotes_process_refs (OstreeRepo *self, if (!collection_refv_contains (refs, summary_collection_id, ref_name, &ref_index)) continue; - /* Load the commit metadata from disk if possible, for verification. */ - if (!ostree_repo_load_commit (self, tmp_checksum, &stored_commit_metadata_v, NULL, NULL)) - stored_commit_metadata_v = NULL; + /* Load the commit from disk if possible, for verification. */ + if (!ostree_repo_load_commit (self, tmp_checksum, &stored_commit_v, NULL, NULL)) + stored_commit_v = NULL; /* Check the additional metadata. */ if (!g_variant_lookup (commit_metadata_v, OSTREE_COMMIT_TIMESTAMP, "t", &commit_timestamp)) @@ -4826,7 +4821,7 @@ find_remotes_process_refs (OstreeRepo *self, if (commit_metadata == NULL) { commit_metadata = commit_metadata_new (tmp_checksum, commit_size, - (stored_commit_metadata_v != NULL) ? ostree_commit_get_timestamp (stored_commit_metadata_v) : 0, + (stored_commit_v != NULL) ? ostree_commit_get_timestamp (stored_commit_v) : 0, NULL); g_hash_table_insert (commit_metadatas, commit_metadata->checksum, commit_metadata /* transfer */); @@ -4880,6 +4875,7 @@ find_remotes_cb (GObject *obj, g_autoptr(GHashTable) commit_metadatas = NULL; /* (element-type commit-checksum CommitMetadata) */ g_autoptr(OstreeFetcher) fetcher = NULL; g_autofree const gchar **ref_to_latest_commit = NULL; /* indexed as @refs; (element-type commit-checksum) */ + g_autofree guint64 *ref_to_latest_timestamp = NULL; /* indexed as @refs; (element-type commit-timestamp) */ gsize n_refs; g_autofree char **override_commit_ids = NULL; g_autoptr(GPtrArray) remotes_to_remove = NULL; /* (element-type OstreeRemote) */ @@ -5035,6 +5031,7 @@ find_remotes_cb (GObject *obj, * it’s been moved to @refs_and_remotes_table and is now potentially out * of date. */ g_clear_pointer (&result->ref_to_checksum, g_hash_table_unref); + g_clear_pointer (&result->ref_to_timestamp, g_hash_table_unref); result->summary_last_modified = summary_last_modified; } @@ -5171,8 +5168,12 @@ find_remotes_cb (GObject *obj, * * @ref_to_latest_commit is indexed by @ref_index, and its values are the * latest checksum for each ref. If override-commit-ids was used, - * @ref_to_latest_commit won't be initialized or used.*/ + * @ref_to_latest_commit won't be initialized or used. + * + * @ref_to_latest_timestamp is also indexed by @ref_index, and its values are + * the latest timestamp for each ref, when available.*/ ref_to_latest_commit = g_new0 (const gchar *, n_refs); + ref_to_latest_timestamp = g_new0 (guint64, n_refs); for (i = 0; i < n_refs; i++) { @@ -5213,6 +5214,11 @@ find_remotes_cb (GObject *obj, * the summary or commit metadata files above. */ ref_to_latest_commit[i] = latest_checksum; + if (latest_checksum != NULL && latest_commit_metadata != NULL) + ref_to_latest_timestamp[i] = latest_commit_metadata->timestamp; + else + ref_to_latest_timestamp[i] = 0; + if (latest_commit_metadata != NULL) { latest_commit_timestamp_str = uint64_secs_to_iso8601 (latest_commit_metadata->timestamp); @@ -5235,7 +5241,8 @@ find_remotes_cb (GObject *obj, for (i = 0; i < results->len; i++) { OstreeRepoFinderResult *result = g_ptr_array_index (results, i); - g_autoptr(GHashTable) validated_ref_to_checksum = NULL; /* (element-type utf8 utf8) */ + g_autoptr(GHashTable) validated_ref_to_checksum = NULL; /* (element-type OstreeCollectionRef utf8) */ + g_autoptr(GHashTable) validated_ref_to_timestamp = NULL; /* (element-type OstreeCollectionRef guint64) */ gsize j, n_latest_refs; /* Previous error processing this result? */ @@ -5249,11 +5256,24 @@ find_remotes_cb (GObject *obj, (GDestroyNotify) ostree_collection_ref_free, g_free); + validated_ref_to_timestamp = g_hash_table_new_full (ostree_collection_ref_hash, + ostree_collection_ref_equal, + (GDestroyNotify) ostree_collection_ref_free, + g_free); if (override_commit_ids) { for (j = 0; refs[j] != NULL; j++) - g_hash_table_insert (validated_ref_to_checksum, ostree_collection_ref_dup (refs[j]), - g_strdup (override_commit_ids[j])); + { + guint64 *timestamp_ptr; + + g_hash_table_insert (validated_ref_to_checksum, ostree_collection_ref_dup (refs[j]), + g_strdup (override_commit_ids[j])); + + timestamp_ptr = g_malloc (sizeof (guint64)); + *timestamp_ptr = 0; + g_hash_table_insert (validated_ref_to_timestamp, ostree_collection_ref_dup (refs[j]), + timestamp_ptr); + } } else { @@ -5262,6 +5282,7 @@ find_remotes_cb (GObject *obj, for (j = 0; refs[j] != NULL; j++) { const gchar *latest_commit_for_ref = ref_to_latest_commit[j]; + guint64 *timestamp_ptr; if (pointer_table_get (refs_and_remotes_table, j, i) != latest_commit_for_ref) latest_commit_for_ref = NULL; @@ -5270,6 +5291,14 @@ find_remotes_cb (GObject *obj, g_hash_table_insert (validated_ref_to_checksum, ostree_collection_ref_dup (refs[j]), g_strdup (latest_commit_for_ref)); + + timestamp_ptr = g_malloc (sizeof (guint64)); + if (latest_commit_for_ref != NULL) + *timestamp_ptr = GUINT64_TO_BE (ref_to_latest_timestamp[j]); + else + *timestamp_ptr = 0; + g_hash_table_insert (validated_ref_to_timestamp, ostree_collection_ref_dup (refs[j]), + timestamp_ptr); } if (n_latest_refs == 0) @@ -5282,6 +5311,7 @@ find_remotes_cb (GObject *obj, } result->ref_to_checksum = g_steal_pointer (&validated_ref_to_checksum); + result->ref_to_timestamp = g_steal_pointer (&validated_ref_to_timestamp); g_ptr_array_add (final_results, g_steal_pointer (&g_ptr_array_index (results, i))); } @@ -5396,6 +5426,13 @@ copy_option (GVariantDict *master_options, * * `flags` (`i`): #OstreeRepoPullFlags to apply to the pull operation * * `inherit-transaction` (`b`): %TRUE to inherit an ongoing transaction on * the #OstreeRepo, rather than encapsulating the pull in a new one + * * `depth` (`i`): How far in the history to traverse; default is 0, -1 means infinite + * * `disable-static-deltas` (`b`): Do not use static deltas + * * `http-headers` (`a(ss)`): Additional headers to add to all HTTP requests + * * `subdirs` (`as`): Pull just these subdirectories + * * `update-frequency` (`u`): Frequency to call the async progress callback in + * milliseconds, if any; only values higher than 0 are valid + * * `append-user-agent` (`s`): Additional string to append to the user agent * * Since: 2017.8 */ @@ -5756,6 +5793,7 @@ ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self, g_autoptr(GBytes) signatures = NULL; gboolean ret = FALSE; gboolean gpg_verify_summary; + gboolean summary_is_from_cache; g_return_val_if_fail (OSTREE_REPO (self), FALSE); g_return_val_if_fail (name != NULL, FALSE); @@ -5770,6 +5808,7 @@ ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self, options, &summary, &signatures, + &summary_is_from_cache, cancellable, error)) goto out; @@ -5777,6 +5816,13 @@ ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self, if (!ostree_repo_remote_get_gpg_verify_summary (self, name, &gpg_verify_summary, error)) goto out; + if (gpg_verify_summary && summary == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "GPG verification enabled, but no summary found (check that the configured URL in remote config is correct)"); + goto out; + } + if (gpg_verify_summary && signatures == NULL) { g_set_error (error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_NO_SIGNATURE, @@ -5799,6 +5845,27 @@ ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self, goto out; } + if (!summary_is_from_cache && summary && signatures) + { + g_autoptr(GError) temp_error = NULL; + + if (!_ostree_repo_cache_summary (self, + name, + summary, + signatures, + cancellable, + &temp_error)) + { + if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) + g_debug ("No permissions to save summary cache"); + else + { + g_propagate_error (error, g_steal_pointer (&temp_error)); + goto out; + } + } + } + if (out_summary != NULL) *out_summary = g_steal_pointer (&summary); diff --git a/src/libostree/ostree-repo-static-delta-core.c b/src/libostree/ostree-repo-static-delta-core.c index 68b06b5c..57c89736 100644 --- a/src/libostree/ostree-repo-static-delta-core.c +++ b/src/libostree/ostree-repo-static-delta-core.c @@ -104,11 +104,6 @@ ostree_repo_list_static_delta_names (OstreeRepo *self, while (TRUE) { struct dirent *sub_dent; - const char *name1; - const char *name2; - g_autofree char *superblock_subpath = NULL; - struct stat stbuf; - if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&sub_dfd_iter, &sub_dent, cancellable, error)) return FALSE; @@ -117,39 +112,33 @@ ostree_repo_list_static_delta_names (OstreeRepo *self, if (dent->d_type != DT_DIR) continue; - name1 = dent->d_name; - name2 = sub_dent->d_name; + const char *name1 = dent->d_name; + const char *name2 = sub_dent->d_name; - superblock_subpath = g_strconcat (name2, "/superblock", NULL); - if (fstatat (sub_dfd_iter.fd, superblock_subpath, &stbuf, 0) < 0) - { - if (errno != ENOENT) - { - glnx_set_error_from_errno (error); - return FALSE; - } - } - else - { - g_autofree char *buf = g_strconcat (name1, name2, NULL); - GString *out = g_string_new (""); - char checksum[OSTREE_SHA256_STRING_LEN+1]; - guchar csum[OSTREE_SHA256_DIGEST_LEN]; - const char *dash = strchr (buf, '-'); + g_autofree char *superblock_subpath = g_strconcat (name2, "/superblock", NULL); + if (!glnx_fstatat_allow_noent (sub_dfd_iter.fd, superblock_subpath, NULL, 0, error)) + return FALSE; + if (errno == ENOENT) + continue; - ostree_checksum_b64_inplace_to_bytes (buf, csum); + g_autofree char *buf = g_strconcat (name1, name2, NULL); + GString *out = g_string_new (""); + char checksum[OSTREE_SHA256_STRING_LEN+1]; + guchar csum[OSTREE_SHA256_DIGEST_LEN]; + const char *dash = strchr (buf, '-'); + + ostree_checksum_b64_inplace_to_bytes (buf, csum); + ostree_checksum_inplace_from_bytes (csum, checksum); + g_string_append (out, checksum); + if (dash) + { + g_string_append_c (out, '-'); + ostree_checksum_b64_inplace_to_bytes (dash+1, csum); ostree_checksum_inplace_from_bytes (csum, checksum); g_string_append (out, checksum); - if (dash) - { - g_string_append_c (out, '-'); - ostree_checksum_b64_inplace_to_bytes (dash+1, csum); - ostree_checksum_inplace_from_bytes (csum, checksum); - g_string_append (out, checksum); - } - - g_ptr_array_add (ret_deltas, g_string_free (out, FALSE)); } + + g_ptr_array_add (ret_deltas, g_string_free (out, FALSE)); } } @@ -320,13 +309,10 @@ ostree_repo_static_delta_execute_offline (OstreeRepo *self, guint32 version; guint64 size; guint64 usize; - const guchar *csum; char checksum[OSTREE_SHA256_STRING_LEN+1]; - gboolean have_all; g_autoptr(GVariant) csum_v = NULL; g_autoptr(GVariant) objects = NULL; g_autoptr(GVariant) part = NULL; - g_autofree char *deltapart_path = NULL; OstreeStaticDeltaOpenFlags delta_open_flags = skip_validation ? OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM : 0; g_autoptr(GVariant) header = g_variant_get_child_value (headers, i); @@ -335,6 +321,7 @@ ostree_repo_static_delta_execute_offline (OstreeRepo *self, if (version > OSTREE_DELTAPART_VERSION) return glnx_throw (error, "Delta part has too new version %u", version); + gboolean have_all; if (!_ostree_repo_static_delta_part_have_all_objects (self, objects, &have_all, cancellable, error)) return FALSE; @@ -345,12 +332,12 @@ ostree_repo_static_delta_execute_offline (OstreeRepo *self, if (have_all) continue; - csum = ostree_checksum_bytes_peek_validate (csum_v, error); + const guchar *csum = ostree_checksum_bytes_peek_validate (csum_v, error); if (!csum) return FALSE; ostree_checksum_inplace_from_bytes (csum, checksum); - deltapart_path = + g_autofree char *deltapart_path = _ostree_get_relative_static_delta_part_path (from_checksum, to_checksum, i); g_autoptr(GInputStream) part_in = NULL; @@ -410,16 +397,14 @@ _ostree_static_delta_part_open (GInputStream *part_in, { const gboolean trusted = (flags & OSTREE_STATIC_DELTA_OPEN_FLAGS_VARIANT_TRUSTED) > 0; const gboolean skip_checksum = (flags & OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM) > 0; - gsize bytes_read; - guint8 comptype; - g_autoptr(GChecksum) checksum = NULL; - g_autoptr(GInputStream) checksum_in = NULL; - GInputStream *source_in; /* We either take a fd or a GBytes reference */ g_return_val_if_fail (G_IS_FILE_DESCRIPTOR_BASED (part_in) || inline_part_bytes != NULL, FALSE); g_return_val_if_fail (skip_checksum || expected_checksum != NULL, FALSE); + g_autoptr(GChecksum) checksum = NULL; + g_autoptr(GInputStream) checksum_in = NULL; + GInputStream *source_in; if (!skip_checksum) { checksum = g_checksum_new (G_CHECKSUM_SHA256); @@ -431,7 +416,9 @@ _ostree_static_delta_part_open (GInputStream *part_in, source_in = part_in; } + guint8 comptype; { guint8 buf[1]; + gsize bytes_read; /* First byte is compression type */ if (!g_input_stream_read_all (source_in, buf, sizeof(buf), &bytes_read, cancellable, error)) @@ -511,7 +498,6 @@ show_one_part (OstreeRepo *self, GCancellable *cancellable, GError **error) { - g_autoptr(GVariant) part = NULL; g_autofree char *part_path = _ostree_get_relative_static_delta_part_path (from, to, i); guint32 version; @@ -530,6 +516,7 @@ show_one_part (OstreeRepo *self, return glnx_throw_errno_prefix (error, "openat(%s)", part_path); g_autoptr(GInputStream) part_in = g_unix_input_stream_new (part_fd, FALSE); + g_autoptr(GVariant) part = NULL; if (!_ostree_static_delta_part_open (part_in, NULL, OSTREE_STATIC_DELTA_OPEN_FLAGS_SKIP_CHECKSUM, NULL, @@ -576,19 +563,13 @@ OstreeDeltaEndianness _ostree_delta_get_endianness (GVariant *superblock, gboolean *out_was_heuristic) { - guint8 endianness_char; - g_autoptr(GVariant) delta_meta = NULL; - g_autoptr(GVariantDict) delta_metadict = NULL; - guint64 total_size = 0; - guint64 total_usize = 0; - guint total_objects = 0; - - delta_meta = g_variant_get_child_value (superblock, 0); - delta_metadict = g_variant_dict_new (delta_meta); + g_autoptr(GVariant) delta_meta = g_variant_get_child_value (superblock, 0); + g_autoptr(GVariantDict) delta_metadict = g_variant_dict_new (delta_meta); if (out_was_heuristic) *out_was_heuristic = FALSE; + guint8 endianness_char; if (g_variant_dict_lookup (delta_metadict, "ostree.endianness", "y", &endianness_char)) { switch (endianness_char) @@ -605,15 +586,16 @@ _ostree_delta_get_endianness (GVariant *superblock, if (out_was_heuristic) *out_was_heuristic = TRUE; + guint64 total_size = 0; + guint64 total_usize = 0; + guint total_objects = 0; { g_autoptr(GVariant) meta_entries = NULL; - guint n_parts; - guint i; gboolean is_byteswapped = FALSE; g_variant_get_child (superblock, 6, "@a" OSTREE_STATIC_DELTA_META_ENTRY_FORMAT, &meta_entries); - n_parts = g_variant_n_children (meta_entries); + const guint n_parts = g_variant_n_children (meta_entries); - for (i = 0; i < n_parts; i++) + for (guint i = 0; i < n_parts; i++) { g_autoptr(GVariant) objects = NULL; guint64 size, usize; @@ -631,7 +613,7 @@ _ostree_delta_get_endianness (GVariant *superblock, double ratio = ((double)size)/((double)usize); /* This should really never happen where compressing things makes it more than 50% bigger. - */ + */ if (ratio > 1.2) { is_byteswapped = TRUE; @@ -724,26 +706,16 @@ _ostree_repo_static_delta_query_exists (OstreeRepo *self, GCancellable *cancellable, GError **error) { - g_autofree char *from = NULL; + g_autofree char *from = NULL; g_autofree char *to = NULL; - struct stat stbuf; - if (!_ostree_parse_delta_name (delta_id, &from, &to, error)) return FALSE; g_autofree char *superblock_path = _ostree_get_relative_static_delta_superblock_path (from, to); + if (!glnx_fstatat_allow_noent (self->repo_dir_fd, superblock_path, NULL, 0, error)) + return FALSE; - if (fstatat (self->repo_dir_fd, superblock_path, &stbuf, 0) < 0) - { - if (errno == ENOENT) - { - *out_exists = FALSE; - return TRUE; - } - else - return glnx_throw_errno_prefix (error, "fstatat(%s)", superblock_path); - } - *out_exists = TRUE; + *out_exists = (errno == 0); return TRUE; } @@ -755,21 +727,15 @@ _ostree_repo_static_delta_dump (OstreeRepo *self, { g_autofree char *from = NULL; g_autofree char *to = NULL; - g_autofree char *superblock_path = NULL; - g_autoptr(GVariant) delta_superblock = NULL; - guint64 total_size = 0, total_usize = 0; - guint64 total_fallback_size = 0, total_fallback_usize = 0; - OstreeDeltaEndianness endianness; - gboolean swap_endian = FALSE; - if (!_ostree_parse_delta_name (delta_id, &from, &to, error)) return FALSE; - superblock_path = _ostree_get_relative_static_delta_superblock_path (from, to); + g_autofree char *superblock_path = _ostree_get_relative_static_delta_superblock_path (from, to); glnx_autofd int superblock_fd = -1; if (!glnx_openat_rdonly (self->repo_dir_fd, superblock_path, TRUE, &superblock_fd, error)) return FALSE; + g_autoptr(GVariant) delta_superblock = NULL; if (!ot_variant_read_fd (superblock_fd, 0, (GVariantType*)OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT, TRUE, &delta_superblock, error)) @@ -777,6 +743,8 @@ _ostree_repo_static_delta_dump (OstreeRepo *self, g_print ("Delta: %s\n", delta_id); + gboolean swap_endian = FALSE; + OstreeDeltaEndianness endianness; { const char *endianness_description; gboolean was_heuristic; @@ -823,6 +791,8 @@ _ostree_repo_static_delta_dump (OstreeRepo *self, g_print ("Number of fallback entries: %u\n", n_fallback); + guint64 total_size = 0, total_usize = 0; + guint64 total_fallback_size = 0, total_fallback_usize = 0; for (guint i = 0; i < n_fallback; i++) { guint64 size, usize; diff --git a/src/libostree/ostree-repo-traverse.c b/src/libostree/ostree-repo-traverse.c index be3ae109..d0edd65d 100644 --- a/src/libostree/ostree-repo-traverse.c +++ b/src/libostree/ostree-repo-traverse.c @@ -294,18 +294,141 @@ ostree_repo_traverse_new_reachable (void) NULL, (GDestroyNotify)g_variant_unref); } +/** + * ostree_repo_traverse_new_parents: + * + * This hash table is a mapping from #GVariant which can be accessed + * via ostree_object_name_deserialize() to a #GVariant containing either + * a similar #GVariant or and array of them, listing the parents of the key. + * + * Returns: (transfer container) (element-type GVariant GVariant): A new hash table + * + * Since: 2018.5 + */ +GHashTable * +ostree_repo_traverse_new_parents (void) +{ + return g_hash_table_new_full (ostree_hash_object_name, g_variant_equal, + (GDestroyNotify)g_variant_unref, (GDestroyNotify)g_variant_unref); +} + +static void +parents_get_commits (GHashTable *parents_ht, GVariant *object, GHashTable *res) +{ + const char *checksum; + OstreeObjectType type; + + if (object == NULL) + return; + + ostree_object_name_deserialize (object, &checksum, &type); + if (type == OSTREE_OBJECT_TYPE_COMMIT) + g_hash_table_add (res, g_strdup (checksum)); + else + { + GVariant *parents = g_hash_table_lookup (parents_ht, object); + + if (parents == NULL) + g_debug ("Unexpected NULL parent"); + else if (g_variant_is_of_type (parents, G_VARIANT_TYPE_ARRAY)) + { + gsize i, len = g_variant_n_children (parents); + + for (i = 0; i < len; i++) + { + g_autoptr(GVariant) parent = g_variant_get_child_value (parents, i); + parents_get_commits (parents_ht, parent, res); + } + } + else + parents_get_commits (parents_ht, parents, res); + } +} + +/** + * ostree_repo_traverse_parents_get_commits: + * + * Gets all the commits that a certain object belongs to, as recorded + * by a parents table gotten from ostree_repo_traverse_commit_union_with_parents. + * + * Returns: (transfer full) (array zero-terminated=1): An array of checksums for + * the commits the key belongs to. + * + * Since: 2018.5 + */ +char ** +ostree_repo_traverse_parents_get_commits (GHashTable *parents, GVariant *object) +{ + g_autoptr(GHashTable) res = g_hash_table_new (g_str_hash, g_str_equal); + + parents_get_commits (parents, object, res); + + return (char **)g_hash_table_get_keys_as_array (res, NULL); +} + static gboolean traverse_dirtree (OstreeRepo *repo, const char *checksum, + GVariant *parent_key, GHashTable *inout_reachable, + GHashTable *inout_parents, gboolean ignore_missing_dirs, GCancellable *cancellable, GError **error); +static void +add_parent_ref (GHashTable *inout_parents, + GVariant *key, + GVariant *parent_key) +{ + GVariant *old_parents; + + if (inout_parents == NULL) + return; + + old_parents = g_hash_table_lookup (inout_parents, key); + if (old_parents == NULL) + { + /* For the common case of a single pointer we skip using an array to save memory. */ + g_hash_table_insert (inout_parents, g_variant_ref (key), g_variant_ref (parent_key)); + } + else + { + g_autofree GVariant **new_parents = NULL; + gsize i, len = 0; + + if (g_variant_is_of_type (old_parents, G_VARIANT_TYPE_ARRAY)) + { + gsize old_parents_len = g_variant_n_children (old_parents); + new_parents = g_new (GVariant *, old_parents_len + 1); + for (i = 0; i < old_parents_len ; i++) + { + g_autoptr(GVariant) old_parent = g_variant_get_child_value (old_parents, i); + if (!g_variant_equal (old_parent, parent_key)) + new_parents[len++] = g_steal_pointer (&old_parent); + } + } + else + { + new_parents = g_new (GVariant *, 2); + if (!g_variant_equal (old_parents, parent_key)) + new_parents[len++] = g_variant_ref (old_parents); + } + new_parents[len++] = g_variant_ref (parent_key); + g_hash_table_insert (inout_parents, g_variant_ref (key), + g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("(su)"), new_parents , len))); + for (i = 0; i < len; i++) + g_variant_unref (new_parents[i]); + } +} + + static gboolean traverse_iter (OstreeRepo *repo, OstreeRepoCommitTraverseIter *iter, + GVariant *parent_key, GHashTable *inout_reachable, + GHashTable *inout_parents, gboolean ignore_missing_dirs, GCancellable *cancellable, GError **error) @@ -343,6 +466,7 @@ traverse_iter (OstreeRepo *repo, g_debug ("Found file object %s", checksum); key = g_variant_ref_sink (ostree_object_name_serialize (checksum, OSTREE_OBJECT_TYPE_FILE)); + add_parent_ref (inout_parents, key, parent_key); g_hash_table_add (inout_reachable, g_steal_pointer (&key)); } else if (iterres == OSTREE_REPO_COMMIT_ITER_RESULT_DIR) @@ -357,16 +481,18 @@ traverse_iter (OstreeRepo *repo, g_debug ("Found dirtree object %s", content_checksum); g_debug ("Found dirmeta object %s", meta_checksum); key = g_variant_ref_sink (ostree_object_name_serialize (meta_checksum, OSTREE_OBJECT_TYPE_DIR_META)); + add_parent_ref (inout_parents, key, parent_key); g_hash_table_add (inout_reachable, g_steal_pointer (&key)); key = g_variant_ref_sink (ostree_object_name_serialize (content_checksum, OSTREE_OBJECT_TYPE_DIR_TREE)); + add_parent_ref (inout_parents, key, parent_key); if (!g_hash_table_lookup (inout_reachable, key)) { - g_hash_table_add (inout_reachable, g_steal_pointer (&key)); - - if (!traverse_dirtree (repo, content_checksum, inout_reachable, + if (!traverse_dirtree (repo, content_checksum, key, inout_reachable, inout_parents, ignore_missing_dirs, cancellable, error)) return FALSE; + + g_hash_table_add (inout_reachable, g_steal_pointer (&key)); } } else @@ -379,7 +505,9 @@ traverse_iter (OstreeRepo *repo, static gboolean traverse_dirtree (OstreeRepo *repo, const char *checksum, + GVariant *parent_key, GHashTable *inout_reachable, + GHashTable *inout_parents, gboolean ignore_missing_dirs, GCancellable *cancellable, GError **error) @@ -409,31 +537,39 @@ traverse_dirtree (OstreeRepo *repo, error)) return FALSE; - if (!traverse_iter (repo, &iter, inout_reachable, ignore_missing_dirs, cancellable, error)) + if (!traverse_iter (repo, &iter, parent_key, inout_reachable, inout_parents, ignore_missing_dirs, cancellable, error)) return FALSE; return TRUE; } /** - * ostree_repo_traverse_commit_union: (skip) + * ostree_repo_traverse_commit_union_with_parents: (skip) * @repo: Repo * @commit_checksum: ASCII SHA256 checksum * @maxdepth: Traverse this many parent commits, -1 for unlimited * @inout_reachable: Set of reachable objects + * @inout_parents: Map from object to parent object * @cancellable: Cancellable * @error: Error * * Update the set @inout_reachable containing all objects reachable * from @commit_checksum, traversing @maxdepth parent commits. + * + * Additionally this constructs a mapping from each object to the parents + * of the object, which can be used to track which commits an object + * belongs to. + * + * Since: 2018.5 */ gboolean -ostree_repo_traverse_commit_union (OstreeRepo *repo, - const char *commit_checksum, - int maxdepth, - GHashTable *inout_reachable, - GCancellable *cancellable, - GError **error) +ostree_repo_traverse_commit_union_with_parents (OstreeRepo *repo, + const char *commit_checksum, + int maxdepth, + GHashTable *inout_reachable, + GHashTable *inout_parents, + GCancellable *cancellable, + GError **error) { g_autofree char *tmp_checksum = NULL; @@ -467,8 +603,7 @@ ostree_repo_traverse_commit_union (OstreeRepo *repo, if ((commitstate & OSTREE_REPO_COMMIT_STATE_PARTIAL) != 0) ignore_missing_dirs = TRUE; - g_hash_table_add (inout_reachable, key); - key = NULL; + g_hash_table_add (inout_reachable, g_variant_ref (key)); g_debug ("Traversing commit %s", commit_checksum); ostree_cleanup_repo_commit_traverse_iter @@ -478,7 +613,7 @@ ostree_repo_traverse_commit_union (OstreeRepo *repo, error)) return FALSE; - if (!traverse_iter (repo, &iter, inout_reachable, ignore_missing_dirs, cancellable, error)) + if (!traverse_iter (repo, &iter, key, inout_reachable, inout_parents, ignore_missing_dirs, cancellable, error)) return FALSE; gboolean recurse = FALSE; @@ -501,6 +636,32 @@ ostree_repo_traverse_commit_union (OstreeRepo *repo, return TRUE; } +/** + * ostree_repo_traverse_commit_union: (skip) + * @repo: Repo + * @commit_checksum: ASCII SHA256 checksum + * @maxdepth: Traverse this many parent commits, -1 for unlimited + * @inout_reachable: Set of reachable objects + * @cancellable: Cancellable + * @error: Error + * + * Update the set @inout_reachable containing all objects reachable + * from @commit_checksum, traversing @maxdepth parent commits. + */ +gboolean +ostree_repo_traverse_commit_union (OstreeRepo *repo, + const char *commit_checksum, + int maxdepth, + GHashTable *inout_reachable, + GCancellable *cancellable, + GError **error) +{ + return + ostree_repo_traverse_commit_union_with_parents (repo, commit_checksum, maxdepth, + inout_reachable, NULL, + cancellable, error); +} + /** * ostree_repo_traverse_commit: * @repo: Repo diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 8ff0d961..3251880f 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -436,7 +436,7 @@ pop_repo_lock (OstreeRepo *self, return TRUE; } -/** +/* * ostree_repo_lock_push: * @self: a #OstreeRepo * @lock_type: the type of lock to acquire @@ -462,13 +462,12 @@ pop_repo_lock (OstreeRepo *self, * %TRUE is returned. * * Returns: %TRUE on success, otherwise %FALSE with @error set - * Since: 2017.14 */ gboolean -ostree_repo_lock_push (OstreeRepo *self, - OstreeRepoLockType lock_type, - GCancellable *cancellable, - GError **error) +_ostree_repo_lock_push (OstreeRepo *self, + OstreeRepoLockType lock_type, + GCancellable *cancellable, + GError **error) { g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE); @@ -531,8 +530,8 @@ ostree_repo_lock_push (OstreeRepo *self, } } -/** - * ostree_repo_lock_pop: +/* + * _ostree_repo_lock_pop: * @self: a #OstreeRepo * @cancellable: a #GCancellable * @error: a #GError @@ -553,12 +552,11 @@ ostree_repo_lock_push (OstreeRepo *self, * %TRUE is returned. * * Returns: %TRUE on success, otherwise %FALSE with @error set - * Since: 2017.14 */ gboolean -ostree_repo_lock_pop (OstreeRepo *self, - GCancellable *cancellable, - GError **error) +_ostree_repo_lock_pop (OstreeRepo *self, + GCancellable *cancellable, + GError **error) { g_return_val_if_fail (self != NULL, FALSE); g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE); @@ -621,8 +619,8 @@ ostree_repo_lock_pop (OstreeRepo *self, } } -/** - * ostree_repo_auto_lock_push: (skip) +/* + * _ostree_repo_auto_lock_push: (skip) * @self: a #OstreeRepo * @lock_type: the type of lock to acquire * @cancellable: a #GCancellable @@ -636,37 +634,34 @@ ostree_repo_lock_pop (OstreeRepo *self, * * |[ * g_autoptr(OstreeRepoAutoLock) lock = NULL; - * lock = ostree_repo_auto_lock_push (repo, lock_type, cancellable, error); + * lock = _ostree_repo_auto_lock_push (repo, lock_type, cancellable, error); * if (!lock) * return FALSE; * ]| * * Returns: @self on success, otherwise %NULL with @error set - * Since: 2017.14 */ OstreeRepoAutoLock * -ostree_repo_auto_lock_push (OstreeRepo *self, - OstreeRepoLockType lock_type, - GCancellable *cancellable, - GError **error) +_ostree_repo_auto_lock_push (OstreeRepo *self, + OstreeRepoLockType lock_type, + GCancellable *cancellable, + GError **error) { - if (!ostree_repo_lock_push (self, lock_type, cancellable, error)) + if (!_ostree_repo_lock_push (self, lock_type, cancellable, error)) return NULL; return (OstreeRepoAutoLock *)self; } -/** - * ostree_repo_auto_lock_cleanup: (skip) +/* + * _ostree_repo_auto_lock_cleanup: (skip) * @lock: a #OstreeRepoAutoLock * * A cleanup handler for use with ostree_repo_auto_lock_push(). If @lock is * not %NULL, ostree_repo_lock_pop() will be called on it. If * ostree_repo_lock_pop() fails, a critical warning will be emitted. - * - * Since: 2017.14 */ void -ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *lock) +_ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *lock) { OstreeRepo *repo = lock; if (repo) @@ -674,7 +669,7 @@ ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *lock) g_autoptr(GError) error = NULL; int errsv = errno; - if (!ostree_repo_lock_pop (repo, NULL, &error)) + if (!_ostree_repo_lock_pop (repo, NULL, &error)) g_critical ("Cleanup repo lock failed: %s", error->message); errno = errsv; @@ -2742,10 +2737,10 @@ reload_core_config (OstreeRepo *self, self->tmp_expiry_seconds = g_ascii_strtoull (tmp_expiry_seconds, NULL, 10); } - /* Disable locking by default for now */ { gboolean locking; + /* Enabled by default in 2018.05 */ if (!ot_keyfile_get_boolean_with_default (self->config, "core", "locking", - FALSE, &locking, error)) + TRUE, &locking, error)) return FALSE; if (!locking) { @@ -3550,6 +3545,9 @@ _ostree_repo_load_file_bare (OstreeRepo *self, return FALSE; } + const char *errprefix = glnx_strjoina ("Opening content object ", checksum); + GLNX_AUTO_PREFIX_ERROR (errprefix, error); + struct stat stbuf; glnx_autofd int fd = -1; g_autofree char *ret_symlink = NULL; @@ -3590,7 +3588,7 @@ _ostree_repo_load_file_bare (OstreeRepo *self, } if (!(S_ISREG (stbuf.st_mode) || S_ISLNK (stbuf.st_mode))) - return glnx_throw (error, "Not a regular file or symlink: %s", loose_path_buf); + return glnx_throw (error, "Not a regular file or symlink"); /* In the non-bare-user case, gather symlink info if requested */ if (self->mode != OSTREE_REPO_MODE_BARE_USER @@ -4642,6 +4640,10 @@ sign_data (OstreeRepo *self, if (gpgme_err_code (err) == GPG_ERR_EOF) return glnx_throw (error, "No gpg key found with ID %s (homedir: %s)", key_id, homedir ? homedir : ""); + else if (gpgme_err_code (err) == GPG_ERR_AMBIGUOUS_NAME) { + return glnx_throw (error, "gpg key id %s ambiguous (homedir: %s). Try the fingerprint instead", key_id, + homedir ? homedir : ""); + } else if (err != GPG_ERR_NO_ERROR) return ot_gpgme_throw (err, error, "Unable to lookup key ID %s", key_id); @@ -5311,11 +5313,11 @@ summary_add_ref_entry (OstreeRepo *self, * `core/commit-update-summary` is set. * * If the `core/collection-id` key is set in the configuration, it will be - * included as %OSTREE_SUMMARY_COLLECTION_ID in the summary file. Refs from the - * `refs/mirrors` directory will be included in the generated summary file, - * listed under the %OSTREE_SUMMARY_COLLECTION_MAP key. Collection IDs and refs - * in %OSTREE_SUMMARY_COLLECTION_MAP are guaranteed to be in lexicographic - * order. + * included as %OSTREE_SUMMARY_COLLECTION_ID in the summary file. Refs that + * have associated collection IDs will be included in the generated summary + * file, listed under the %OSTREE_SUMMARY_COLLECTION_MAP key. Collection IDs + * and refs in %OSTREE_SUMMARY_COLLECTION_MAP are guaranteed to be in + * lexicographic order. */ gboolean ostree_repo_regenerate_summary (OstreeRepo *self, diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index faac5d9a..8d3a7a6f 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -109,48 +109,6 @@ OstreeRepo * ostree_repo_create_at (int dfd, #ifdef OSTREE_ENABLE_EXPERIMENTAL_API -/** - * OstreeRepoLockType: - * @OSTREE_REPO_LOCK_SHARED: A shared lock - * @OSTREE_REPO_LOCK_EXCLUSIVE: An exclusive lock - * - * The type of repository lock to acquire. - * - * Since: 2017.14 - */ -typedef enum { - OSTREE_REPO_LOCK_SHARED, - OSTREE_REPO_LOCK_EXCLUSIVE -} OstreeRepoLockType; - -_OSTREE_PUBLIC -gboolean ostree_repo_lock_push (OstreeRepo *self, - OstreeRepoLockType lock_type, - GCancellable *cancellable, - GError **error); -_OSTREE_PUBLIC -gboolean ostree_repo_lock_pop (OstreeRepo *self, - GCancellable *cancellable, - GError **error); - -/** - * OstreeRepoAutoLock: (skip) - * - * This is simply an alias to #OstreeRepo used for automatic lock cleanup. - * See ostree_repo_auto_lock_push() for its intended usage. - * - * Since: 2017.14 - */ -typedef OstreeRepo OstreeRepoAutoLock; - -_OSTREE_PUBLIC -OstreeRepoAutoLock * ostree_repo_auto_lock_push (OstreeRepo *self, - OstreeRepoLockType lock_type, - GCancellable *cancellable, - GError **error); -_OSTREE_PUBLIC -void ostree_repo_auto_lock_cleanup (OstreeRepoAutoLock *lock); - _OSTREE_PUBLIC const gchar * ostree_repo_get_collection_id (OstreeRepo *self); _OSTREE_PUBLIC @@ -1112,6 +1070,12 @@ gboolean ostree_repo_static_delta_execute_offline (OstreeRepo _OSTREE_PUBLIC GHashTable *ostree_repo_traverse_new_reachable (void); +_OSTREE_PUBLIC +GHashTable *ostree_repo_traverse_new_parents (void); + +_OSTREE_PUBLIC +char ** ostree_repo_traverse_parents_get_commits (GHashTable *parents, GVariant *object); + _OSTREE_PUBLIC gboolean ostree_repo_traverse_commit (OstreeRepo *repo, const char *commit_checksum, @@ -1127,6 +1091,14 @@ gboolean ostree_repo_traverse_commit_union (OstreeRepo *repo, GHashTable *inout_reachable, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC +gboolean ostree_repo_traverse_commit_union_with_parents (OstreeRepo *repo, + const char *commit_checksum, + int maxdepth, + GHashTable *inout_reachable, + GHashTable *inout_parents, + GCancellable *cancellable, + GError **error); struct _OstreeRepoCommitTraverseIter { gboolean initialized; diff --git a/src/libostree/ostree-sysroot-cleanup.c b/src/libostree/ostree-sysroot-cleanup.c index 2e7cd44b..1d46222b 100644 --- a/src/libostree/ostree-sysroot-cleanup.c +++ b/src/libostree/ostree-sysroot-cleanup.c @@ -239,6 +239,44 @@ cleanup_other_bootversions (OstreeSysroot *self, return TRUE; } +/* Delete a deployment directory */ +gboolean +_ostree_sysroot_rmrf_deployment (OstreeSysroot *self, + OstreeDeployment *deployment, + GCancellable *cancellable, + GError **error) +{ + g_autofree char *origin_relpath = ostree_deployment_get_origin_relpath (deployment); + g_autofree char *deployment_path = ostree_sysroot_get_deployment_dirpath (self, deployment); + struct stat stbuf; + glnx_autofd int deployment_fd = -1; + + if (!glnx_opendirat (self->sysroot_fd, deployment_path, TRUE, + &deployment_fd, error)) + return FALSE; + + if (!glnx_fstat (deployment_fd, &stbuf, error)) + return FALSE; + + /* This shouldn't happen, because higher levels should + * disallow having the booted deployment not in the active + * deployment list, but let's be extra safe. */ + if (stbuf.st_dev == self->root_device && + stbuf.st_ino == self->root_inode) + return TRUE; + + /* This deployment wasn't referenced, so delete it */ + if (!_ostree_linuxfs_fd_alter_immutable_flag (deployment_fd, FALSE, + cancellable, error)) + return FALSE; + if (!glnx_shutil_rm_rf_at (self->sysroot_fd, origin_relpath, cancellable, error)) + return FALSE; + if (!glnx_shutil_rm_rf_at (self->sysroot_fd, deployment_path, cancellable, error)) + return FALSE; + + return TRUE; +} + /* As the bootloader configuration changes, we will have leftover deployments * on disk. This function deletes all deployments which aren't actively * referenced. @@ -279,36 +317,12 @@ cleanup_old_deployments (OstreeSysroot *self, { OstreeDeployment *deployment = all_deployment_dirs->pdata[i]; g_autofree char *deployment_path = ostree_sysroot_get_deployment_dirpath (self, deployment); - g_autofree char *origin_relpath = ostree_deployment_get_origin_relpath (deployment); - if (!g_hash_table_lookup (active_deployment_dirs, deployment_path)) - { - struct stat stbuf; - glnx_autofd int deployment_fd = -1; + if (g_hash_table_lookup (active_deployment_dirs, deployment_path)) + continue; - if (!glnx_opendirat (self->sysroot_fd, deployment_path, TRUE, - &deployment_fd, error)) - return FALSE; - - if (!glnx_fstat (deployment_fd, &stbuf, error)) - return FALSE; - - /* This shouldn't happen, because higher levels should - * disallow having the booted deployment not in the active - * deployment list, but let's be extra safe. */ - if (stbuf.st_dev == root_stbuf.st_dev && - stbuf.st_ino == root_stbuf.st_ino) - continue; - - /* This deployment wasn't referenced, so delete it */ - if (!_ostree_linuxfs_fd_alter_immutable_flag (deployment_fd, FALSE, - cancellable, error)) - return FALSE; - if (!glnx_shutil_rm_rf_at (self->sysroot_fd, origin_relpath, cancellable, error)) - return FALSE; - if (!glnx_shutil_rm_rf_at (self->sysroot_fd, deployment_path, cancellable, error)) - return FALSE; - } + if (!_ostree_sysroot_rmrf_deployment (self, deployment, cancellable, error)) + return FALSE; } /* Clean up boot directories */ diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index 93a29ed6..6f0f6efd 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -128,7 +128,7 @@ install_into_boot (OstreeSePolicy *sepolicy, error)) return FALSE; return glnx_file_copy_at (src_dfd, src_subpath, NULL, dest_dfd, dest_subpath, - GLNX_FILE_COPY_NOXATTRS, + GLNX_FILE_COPY_NOXATTRS | GLNX_FILE_COPY_DATASYNC, cancellable, error); } else @@ -390,7 +390,6 @@ copy_modified_config_file (int orig_etc_fd, * merge_configuration_from: * @sysroot: Sysroot * @merge_deployment: Source of configuration differences - * @merge_deployment_dfd: Directory fd, may be -1 * @new_deployment: Target for merge of configuration * @new_deployment_dfd: Directory fd for @new_deployment (may *not* be -1) * @cancellable: Cancellable @@ -407,27 +406,22 @@ copy_modified_config_file (int orig_etc_fd, static gboolean merge_configuration_from (OstreeSysroot *sysroot, OstreeDeployment *merge_deployment, - int merge_deployment_dfd, OstreeDeployment *new_deployment, int new_deployment_dfd, GCancellable *cancellable, GError **error) { - glnx_autofd int owned_merge_deployment_dfd = -1; + GLNX_AUTO_PREFIX_ERROR ("During /etc merge", error); const OstreeSysrootDebugFlags flags = sysroot->debug_flags; g_assert (merge_deployment != NULL && new_deployment != NULL); g_assert (new_deployment_dfd != -1); - /* Allow the caller to pass -1 for the merge, for convenience */ - if (merge_deployment_dfd == -1) - { - g_autofree char *merge_deployment_path = ostree_sysroot_get_deployment_dirpath (sysroot, merge_deployment); - if (!glnx_opendirat (sysroot->sysroot_fd, merge_deployment_path, FALSE, - &owned_merge_deployment_dfd, error)) - return FALSE; - merge_deployment_dfd = owned_merge_deployment_dfd; - } + g_autofree char *merge_deployment_path = ostree_sysroot_get_deployment_dirpath (sysroot, merge_deployment); + glnx_autofd int merge_deployment_dfd = -1; + if (!glnx_opendirat (sysroot->sysroot_fd, merge_deployment_path, FALSE, + &merge_deployment_dfd, error)) + return FALSE; /* TODO: get rid of GFile usage here */ g_autoptr(GFile) orig_etc = ot_fdrel_to_gfile (merge_deployment_dfd, "usr/etc"); @@ -690,10 +684,15 @@ selinux_relabel_dir (OstreeSysroot *sysroot, static gboolean selinux_relabel_var_if_needed (OstreeSysroot *sysroot, OstreeSePolicy *sepolicy, - int os_deploy_dfd, + OstreeDeployment *deployment, GCancellable *cancellable, GError **error) { + const char *osdeploypath = glnx_strjoina ("ostree/deploy/", ostree_deployment_get_osname (deployment)); + glnx_autofd int os_deploy_dfd = -1; + if (!glnx_opendirat (sysroot->sysroot_fd, osdeploypath, TRUE, &os_deploy_dfd, error)) + return FALSE; + /* This is a bit of a hack; we should change the code at some * point in the distant future to only create (and label) /var * when doing a deployment. @@ -741,37 +740,18 @@ selinux_relabel_var_if_needed (OstreeSysroot *sysroot, return TRUE; } -/* OSTree implements a "3 way" merge model for /etc. For a bit more information - * on this, see the manual. This function uses the configuration for - * @previous_deployment, and writes the merged configuration into @deployment's - * /etc. If available, we also load the SELinux policy from the new root. +/* Handle initial creation of /etc in the deployment. See also + * merge_configuration_from(). */ static gboolean -merge_configuration (OstreeSysroot *sysroot, - OstreeRepo *repo, - OstreeDeployment *previous_deployment, - OstreeDeployment *deployment, - int deployment_dfd, - OstreeSePolicy **out_sepolicy, - GCancellable *cancellable, - GError **error) +prepare_deployment_etc (OstreeSysroot *sysroot, + OstreeRepo *repo, + OstreeDeployment *deployment, + int deployment_dfd, + GCancellable *cancellable, + GError **error) { - GLNX_AUTO_PREFIX_ERROR ("During /etc merge", error); - g_autoptr(OstreeSePolicy) sepolicy = NULL; - - if (previous_deployment) - { - OstreeBootconfigParser *previous_bootconfig = ostree_deployment_get_bootconfig (previous_deployment); - if (previous_bootconfig) - { - const char *previous_options = ostree_bootconfig_parser_get (previous_bootconfig, "options"); - /* Completely overwrite the previous options here; we will extend - * them later. - */ - ostree_bootconfig_parser_set (ostree_deployment_get_bootconfig (deployment), "options", - previous_options); - } - } + GLNX_AUTO_PREFIX_ERROR ("Preparing /etc", error); struct stat stbuf; if (!glnx_fstatat_allow_noent (deployment_dfd, "etc", &stbuf, AT_SYMLINK_NOFOLLOW, error)) @@ -804,7 +784,7 @@ merge_configuration (OstreeSysroot *sysroot, /* Here, we initialize SELinux policy from the /usr/etc inside * the root - this is before we've finalized the configuration * merge into /etc. */ - sepolicy = ostree_sepolicy_new_at (deployment_dfd, cancellable, error); + g_autoptr(OstreeSePolicy) sepolicy = ostree_sepolicy_new_at (deployment_dfd, cancellable, error); if (!sepolicy) return FALSE; if (ostree_sepolicy_get_name (sepolicy) != NULL) @@ -819,16 +799,6 @@ merge_configuration (OstreeSysroot *sysroot, } - if (previous_deployment) - { - if (!merge_configuration_from (sysroot, previous_deployment, -1, - deployment, deployment_dfd, - cancellable, error)) - return FALSE; - } - - if (out_sepolicy) - *out_sepolicy = g_steal_pointer (&sepolicy); return TRUE; } @@ -862,7 +832,6 @@ write_origin_file_internal (OstreeSysroot *sysroot, ostree_deployment_get_csum (deployment), ostree_deployment_get_deployserial (deployment)); - gsize len; g_autofree char *contents = g_key_file_to_data (origin, &len, error); if (!contents) @@ -1459,6 +1428,7 @@ full_system_sync (OstreeSysroot *self, GCancellable *cancellable, GError **error) { + GLNX_AUTO_PREFIX_ERROR ("Full sync", error); guint64 start_msec = g_get_monotonic_time () / 1000; if (syncfs (self->sysroot_fd) != 0) return glnx_throw_errno_prefix (error, "syncfs(sysroot)"); @@ -1850,6 +1820,7 @@ prepare_new_bootloader_link (OstreeSysroot *sysroot, GCancellable *cancellable, GError **error) { + GLNX_AUTO_PREFIX_ERROR ("Preparing final bootloader swap", error); g_assert ((current_bootversion == 0 && new_bootversion == 1) || (current_bootversion == 1 && new_bootversion == 0)); @@ -1872,11 +1843,12 @@ swap_bootloader (OstreeSysroot *sysroot, GCancellable *cancellable, GError **error) { - glnx_autofd int boot_dfd = -1; + GLNX_AUTO_PREFIX_ERROR ("Final bootloader swap", error); g_assert ((current_bootversion == 0 && new_bootversion == 1) || (current_bootversion == 1 && new_bootversion == 0)); + glnx_autofd int boot_dfd = -1; if (!glnx_opendirat (sysroot->sysroot_fd, "boot", TRUE, &boot_dfd, error)) return FALSE; @@ -1902,26 +1874,54 @@ swap_bootloader (OstreeSysroot *sysroot, return TRUE; } -static GHashTable * +/* Deployments may share boot checksums; the bootserial indexes them + * per-bootchecksum. It's used by the symbolic links after the bootloader. + */ +static void assign_bootserials (GPtrArray *deployments) { - guint i; - GHashTable *ret = + g_autoptr(GHashTable) serials = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); - for (i = 0; i < deployments->len; i++) + for (guint i = 0; i < deployments->len; i++) { OstreeDeployment *deployment = deployments->pdata[i]; const char *bootcsum = ostree_deployment_get_bootcsum (deployment); - guint count; - - count = GPOINTER_TO_UINT (g_hash_table_lookup (ret, bootcsum)); - g_hash_table_replace (ret, (char*) bootcsum, + /* Note that not-found maps to NULL which converts to zero */ + guint count = GPOINTER_TO_UINT (g_hash_table_lookup (serials, bootcsum)); + g_hash_table_replace (serials, (char*) bootcsum, GUINT_TO_POINTER (count + 1)); ostree_deployment_set_bootserial (deployment, count); } - return ret; +} + +static char* +get_deployment_nonostree_kargs (OstreeDeployment *deployment) +{ + /* pick up kernel arguments but filter out ostree= */ + OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (deployment); + const char *boot_options = ostree_bootconfig_parser_get (bootconfig, "options"); + g_autoptr(OstreeKernelArgs) kargs = _ostree_kernel_args_from_string (boot_options); + _ostree_kernel_args_replace (kargs, "ostree"); + return _ostree_kernel_args_to_string (kargs); +} + +static char* +get_deployment_ostree_version (OstreeRepo *repo, + OstreeDeployment *deployment) +{ + const char *csum = ostree_deployment_get_csum (deployment); + + g_autofree char *version = NULL; + g_autoptr(GVariant) variant = NULL; + if (ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, csum, &variant, NULL)) + { + g_autoptr(GVariant) metadata = g_variant_get_child_value (variant, 0); + g_variant_lookup (metadata, OSTREE_COMMIT_META_KEY_VERSION, "s", &version); + } + + return g_steal_pointer (&version); } /* OSTree implements a special optimization where we want to avoid touching @@ -1934,32 +1934,27 @@ assign_bootserials (GPtrArray *deployments) * bootloader perspective. */ static gboolean -deployment_bootconfigs_equal (OstreeDeployment *a, +deployment_bootconfigs_equal (OstreeRepo *repo, + OstreeDeployment *a, OstreeDeployment *b) { + /* same kernel & initramfs? */ const char *a_bootcsum = ostree_deployment_get_bootcsum (a); const char *b_bootcsum = ostree_deployment_get_bootcsum (b); - if (strcmp (a_bootcsum, b_bootcsum) != 0) return FALSE; - { - /* We checksum the kernel arguments *except* ostree= */ - OstreeBootconfigParser *a_bootconfig = ostree_deployment_get_bootconfig (a); - const char *a_boot_options = ostree_bootconfig_parser_get (a_bootconfig, "options"); - g_autoptr(OstreeKernelArgs) a_kargs = _ostree_kernel_args_from_string (a_boot_options); - _ostree_kernel_args_replace (a_kargs, "ostree"); - g_autofree char *a_boot_options_without_ostree = _ostree_kernel_args_to_string (a_kargs); + /* same kargs? */ + g_autofree char *a_boot_options_without_ostree = get_deployment_nonostree_kargs (a); + g_autofree char *b_boot_options_without_ostree = get_deployment_nonostree_kargs (b); + if (strcmp (a_boot_options_without_ostree, b_boot_options_without_ostree) != 0) + return FALSE; - OstreeBootconfigParser *b_bootconfig = ostree_deployment_get_bootconfig (b); - const char *b_boot_options = ostree_bootconfig_parser_get (b_bootconfig, "options"); - g_autoptr(OstreeKernelArgs) b_kargs = _ostree_kernel_args_from_string (b_boot_options); - _ostree_kernel_args_replace (b_kargs, "ostree"); - g_autofree char *b_boot_options_without_ostree = _ostree_kernel_args_to_string (b_kargs); - - if (strcmp (a_boot_options_without_ostree, b_boot_options_without_ostree) != 0) - return FALSE; - } + /* same ostree version? this is just for the menutitle, we won't have to cp the kernel */ + g_autofree char *a_version = get_deployment_ostree_version (repo, a); + g_autofree char *b_version = get_deployment_ostree_version (repo, b); + if (g_strcmp0 (a_version, b_version) != 0) + return FALSE; return TRUE; } @@ -2063,6 +2058,110 @@ ostree_sysroot_write_deployments (OstreeSysroot *self, cancellable, error); } +/* Handle writing out a new bootloader config. One reason this needs to be a + * helper function is to handle wrapping it with temporarily remounting /boot + * rw. + */ +static gboolean +write_deployments_bootswap (OstreeSysroot *self, + GPtrArray *new_deployments, + OstreeSysrootWriteDeploymentsOpts *opts, + OstreeBootloader *bootloader, + SyncStats *out_syncstats, + GCancellable *cancellable, + GError **error) +{ + const int new_bootversion = self->bootversion ? 0 : 1; + + g_autofree char* new_loader_entries_dir = g_strdup_printf ("boot/loader.%d/entries", new_bootversion); + if (!glnx_shutil_rm_rf_at (self->sysroot_fd, new_loader_entries_dir, cancellable, error)) + return FALSE; + if (!glnx_shutil_mkdir_p_at (self->sysroot_fd, new_loader_entries_dir, 0755, + cancellable, error)) + return FALSE; + + /* Need the repo to try and extract the versions for deployments. + * But this is a "nice-to-have" for the bootloader UI, so failure + * here is not fatal to the whole operation. We just gracefully + * fall back to the deployment index. */ + g_autoptr(OstreeRepo) repo = NULL; + (void) ostree_sysroot_get_repo (self, &repo, cancellable, NULL); + + /* Only show the osname in bootloader titles if there are multiple + * osname's among the new deployments. Check for that here. */ + gboolean show_osname = FALSE; + for (guint i = 1; i < new_deployments->len; i++) + { + const char *osname_0 = ostree_deployment_get_osname (new_deployments->pdata[0]); + const char *osname_i = ostree_deployment_get_osname (new_deployments->pdata[i]); + if (!g_str_equal (osname_0, osname_i)) + { + show_osname = TRUE; + break; + } + } + + for (guint i = 0; i < new_deployments->len; i++) + { + OstreeDeployment *deployment = new_deployments->pdata[i]; + if (!install_deployment_kernel (self, repo, new_bootversion, + deployment, new_deployments->len, + show_osname, cancellable, error)) + return FALSE; + } + + /* Create and swap bootlinks for *new* version */ + if (!create_new_bootlinks (self, new_bootversion, + new_deployments, + cancellable, error)) + return FALSE; + if (!swap_bootlinks (self, new_bootversion, new_deployments, + cancellable, error)) + return FALSE; + + g_debug ("Using bootloader: %s", bootloader ? + g_type_name (G_TYPE_FROM_INSTANCE (bootloader)) : "(none)"); + + if (bootloader) + { + if (!_ostree_bootloader_write_config (bootloader, new_bootversion, + cancellable, error)) + return glnx_prefix_error (error, "Bootloader write config"); + } + + if (!prepare_new_bootloader_link (self, self->bootversion, new_bootversion, + cancellable, error)) + return FALSE; + + if (!full_system_sync (self, out_syncstats, cancellable, error)) + return FALSE; + + if (!swap_bootloader (self, self->bootversion, new_bootversion, + cancellable, error)) + return FALSE; + + return TRUE; +} + +/* Actions taken after writing deployments is complete */ +static gboolean +write_deployments_finish (OstreeSysroot *self, + GCancellable *cancellable, + GError **error) +{ + if (!_ostree_sysroot_bump_mtime (self, error)) + return FALSE; + + /* Now reload from disk */ + if (!ostree_sysroot_load (self, cancellable, error)) + return glnx_prefix_error (error, "Reloading deployments after commit"); + + if (!cleanup_legacy_current_symlinks (self, cancellable, error)) + return FALSE; + + return TRUE; +} + /** * ostree_sysroot_write_deployments_with_options: * @self: Sysroot @@ -2085,15 +2184,45 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self, GCancellable *cancellable, GError **error) { - gboolean ret = FALSE; - gboolean boot_was_ro_mount = FALSE; - g_autoptr(OstreeBootloader) bootloader = NULL; - g_assert (self->loaded); + /* It dramatically simplifies a lot of the logic below if we + * drop the staged deployment from both the source deployment list, + * as well as the target list. We don't want to write it to the bootloader + * now, which is mostly what this function is concerned with. + * In the future we though should probably adapt things to keep it. + */ + gboolean removed_staged = FALSE; + if (self->staged_deployment) + { + if (!glnx_unlinkat (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED, 0, error)) + return FALSE; + + if (!_ostree_sysroot_rmrf_deployment (self, self->staged_deployment, cancellable, error)) + return FALSE; + + g_assert (self->staged_deployment == self->deployments->pdata[0]); + g_ptr_array_remove_index (self->deployments, 0); + removed_staged = TRUE; + } + /* First new deployment; we'll see if it's staged */ + OstreeDeployment *first_new = + (new_deployments->len > 0 ? new_deployments->pdata[0] : NULL); + g_autoptr(GPtrArray) new_deployments_copy = NULL; + if (first_new && ostree_deployment_is_staged (first_new)) + { + g_assert_cmpint (new_deployments->len, >, 0); + new_deployments_copy = g_ptr_array_sized_new (new_deployments->len - 1); + for (guint i = 1; i < new_deployments->len; i++) + g_ptr_array_add (new_deployments_copy, new_deployments->pdata[i]); + } + else + new_deployments_copy = g_ptr_array_ref (new_deployments); + new_deployments = new_deployments_copy; + /* Assign a bootserial to each new deployment. */ - g_hash_table_unref (assign_bootserials (new_deployments)); + assign_bootserials (new_deployments); /* Determine whether or not we need to touch the bootloader * configuration. If we have an equal number of deployments with @@ -2105,14 +2234,41 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self, requires_new_bootversion = TRUE; else { + gboolean is_noop = TRUE; + OstreeRepo *repo = ostree_sysroot_repo (self); for (guint i = 0; i < new_deployments->len; i++) { - if (!deployment_bootconfigs_equal (new_deployments->pdata[i], - self->deployments->pdata[i])) + OstreeDeployment *cur_deploy = self->deployments->pdata[i]; + if (ostree_deployment_is_staged (cur_deploy)) + continue; + OstreeDeployment *new_deploy = new_deployments->pdata[i]; + if (!deployment_bootconfigs_equal (repo, cur_deploy, new_deploy)) { requires_new_bootversion = TRUE; + is_noop = FALSE; break; } + if (cur_deploy != new_deploy) + is_noop = FALSE; + } + + /* If we're passed the same set of deployments, we don't need + * to drop into the rest of this function which deals with + * changing the bootloader config. + */ + if (is_noop) + { + g_assert (!requires_new_bootversion); + /* However, if we dropped the staged deployment, we still + * need to do finalization steps such as regenerating + * the refs and bumping the mtime. + */ + if (removed_staged) + { + if (!write_deployments_finish (self, cancellable, error)) + return FALSE; + } + return TRUE; } } @@ -2120,58 +2276,45 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self, for (guint i = 0; i < new_deployments->len; i++) { OstreeDeployment *deployment = new_deployments->pdata[i]; - g_autoptr(GFile) deployment_root = NULL; + g_assert (!ostree_deployment_is_staged (deployment)); if (deployment == self->booted_deployment) found_booted_deployment = TRUE; - deployment_root = ostree_sysroot_get_deployment_directory (self, deployment); + g_autoptr(GFile) deployment_root = ostree_sysroot_get_deployment_directory (self, deployment); if (!g_file_query_exists (deployment_root, NULL)) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Unable to find expected deployment root: %s", - gs_file_get_path_cached (deployment_root)); - goto out; - } + return glnx_throw (error, "Unable to find expected deployment root: %s", + gs_file_get_path_cached (deployment_root)); ostree_deployment_set_index (deployment, i); } if (self->booted_deployment && !found_booted_deployment) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Attempting to remove booted deployment"); - goto out; - } + return glnx_throw (error, "Attempting to remove booted deployment"); gboolean bootloader_is_atomic = FALSE; SyncStats syncstats = { 0, }; + g_autoptr(OstreeBootloader) bootloader = NULL; if (!requires_new_bootversion) { if (!create_new_bootlinks (self, self->bootversion, new_deployments, cancellable, error)) - goto out; + return FALSE; if (!full_system_sync (self, &syncstats, cancellable, error)) - { - g_prefix_error (error, "Full sync: "); - goto out; - } + return FALSE; if (!swap_bootlinks (self, self->bootversion, new_deployments, cancellable, error)) - goto out; + return FALSE; bootloader_is_atomic = TRUE; } else { - int new_bootversion = self->bootversion ? 0 : 1; - g_autofree char* new_loader_entries_dir = NULL; - g_autoptr(OstreeRepo) repo = NULL; - gboolean show_osname = FALSE; - + gboolean boot_was_ro_mount = FALSE; if (self->booted_deployment) boot_was_ro_mount = is_ro_mount ("/boot"); @@ -2179,99 +2322,37 @@ 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) - { - glnx_set_prefix_error_from_errno (error, "%s", "Remounting /boot read-write"); - goto out; - } + return glnx_throw_errno_prefix (error, "Remounting /boot read-write"); } if (!_ostree_sysroot_query_bootloader (self, &bootloader, cancellable, error)) - goto out; + return FALSE; + bootloader_is_atomic = bootloader != NULL && _ostree_bootloader_is_atomic (bootloader); - new_loader_entries_dir = g_strdup_printf ("boot/loader.%d/entries", new_bootversion); - if (!glnx_shutil_rm_rf_at (self->sysroot_fd, new_loader_entries_dir, cancellable, error)) - goto out; - if (!glnx_shutil_mkdir_p_at (self->sysroot_fd, new_loader_entries_dir, 0755, - cancellable, error)) - goto out; - - /* Need the repo to try and extract the versions for deployments. - * But this is a "nice-to-have" for the bootloader UI, so failure - * here is not fatal to the whole operation. We just gracefully - * fall back to the deployment index. */ - (void) ostree_sysroot_get_repo (self, &repo, cancellable, NULL); - - /* Only show the osname in bootloader titles if there are multiple - * osname's among the new deployments. Check for that here. */ - for (guint i = 1; i < new_deployments->len; i++) + /* 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) { - const char *osname_0 = ostree_deployment_get_osname (new_deployments->pdata[0]); - const char *osname_i = ostree_deployment_get_osname (new_deployments->pdata[i]); - if (!g_str_equal (osname_0, osname_i)) + if (mount ("/boot", "/boot", NULL, MS_REMOUNT | MS_RDONLY | MS_SILENT, NULL) < 0) { - show_osname = TRUE; - break; + /* Only make this a warning because we don't want to + * completely bomb out if some other process happened to + * jump in and open a file there. See above TODO + * around doing this in a new mount namespace. + */ + g_printerr ("warning: Failed to remount /boot read-only: %s\n", strerror (errno)); } } - - for (guint i = 0; i < new_deployments->len; i++) - { - OstreeDeployment *deployment = new_deployments->pdata[i]; - if (!install_deployment_kernel (self, repo, new_bootversion, - deployment, new_deployments->len, - show_osname, cancellable, error)) - goto out; - } - - /* Create and swap bootlinks for *new* version */ - if (!create_new_bootlinks (self, new_bootversion, - new_deployments, - cancellable, error)) - goto out; - if (!swap_bootlinks (self, new_bootversion, new_deployments, - cancellable, error)) - goto out; - - g_debug ("Using bootloader: %s", bootloader ? - g_type_name (G_TYPE_FROM_INSTANCE (bootloader)) : "(none)"); - - if (bootloader) - bootloader_is_atomic = _ostree_bootloader_is_atomic (bootloader); - - if (bootloader) - { - if (!_ostree_bootloader_write_config (bootloader, new_bootversion, - cancellable, error)) - { - g_prefix_error (error, "Bootloader write config: "); - goto out; - } - } - - if (!prepare_new_bootloader_link (self, self->bootversion, new_bootversion, - cancellable, error)) - { - g_prefix_error (error, "Preparing final bootloader swap: "); - goto out; - } - - if (!full_system_sync (self, &syncstats, cancellable, error)) - { - g_prefix_error (error, "Full sync: "); - goto out; - } - - if (!swap_bootloader (self, self->bootversion, new_bootversion, - cancellable, error)) - { - g_prefix_error (error, "Final bootloader swap: "); - goto out; - } + if (!success) + return FALSE; } { g_autofree char *msg = - g_strdup_printf ("%s; bootconfig swap: %s deployment count change: %i", + g_strdup_printf ("%s; bootconfig swap: %s; deployment count change: %i", (bootloader_is_atomic ? "Transaction complete" : "Bootloader updated"), requires_new_bootversion ? "yes" : "no", new_deployments->len - self->deployments->len); @@ -2290,45 +2371,18 @@ ostree_sysroot_write_deployments_with_options (OstreeSysroot *self, _ostree_sysroot_emit_journal_msg (self, msg); } - if (!_ostree_sysroot_bump_mtime (self, error)) - goto out; - - /* Now reload from disk */ - if (!ostree_sysroot_load (self, cancellable, error)) - { - g_prefix_error (error, "Reloading deployments after commit: "); - goto out; - } - - if (!cleanup_legacy_current_symlinks (self, cancellable, error)) - goto out; + if (!write_deployments_finish (self, cancellable, error)) + return FALSE; /* And finally, cleanup of any leftover data. */ if (opts->do_postclean) { if (!ostree_sysroot_cleanup (self, cancellable, error)) - { - g_prefix_error (error, "Performing final cleanup: "); - goto out; - } + return glnx_prefix_error (error, "Performing final cleanup"); } - ret = TRUE; - out: - if (boot_was_ro_mount) - { - if (mount ("/boot", "/boot", NULL, MS_REMOUNT | MS_RDONLY | MS_SILENT, NULL) < 0) - { - /* Only make this a warning because we don't want to - * completely bomb out if some other process happened to - * jump in and open a file there. - */ - int errsv = errno; - g_printerr ("warning: Failed to remount /boot read-only: %s\n", strerror (errsv)); - } - } - return ret; + return TRUE; } static gboolean @@ -2366,46 +2420,47 @@ allocate_deployserial (OstreeSysroot *self, return TRUE; } -/** - * ostree_sysroot_deploy_tree: - * @self: Sysroot - * @osname: (allow-none): osname to use for merge deployment - * @revision: Checksum to add - * @origin: (allow-none): Origin to use for upgrades - * @provided_merge_deployment: (allow-none): Use this deployment for merge path - * @override_kernel_argv: (allow-none) (array zero-terminated=1) (element-type utf8): Use these as kernel arguments; if %NULL, inherit options from provided_merge_deployment - * @out_new_deployment: (out): The new deployment path - * @cancellable: Cancellable - * @error: Error - * - * Check out deployment tree with revision @revision, performing a 3 - * way merge with @provided_merge_deployment for configuration. +void +_ostree_deployment_set_bootconfig_from_kargs (OstreeDeployment *deployment, + char **override_kernel_argv) +{ + /* Create an empty boot configuration; we will merge things into + * it as we go. + */ + g_autoptr(OstreeBootconfigParser) bootconfig = ostree_bootconfig_parser_new (); + ostree_deployment_set_bootconfig (deployment, bootconfig); + + /* After this, install_deployment_kernel() will set the other boot + * options and write it out to disk. + */ + if (override_kernel_argv) + { + g_autoptr(OstreeKernelArgs) kargs = _ostree_kernel_args_new (); + _ostree_kernel_args_append_argv (kargs, override_kernel_argv); + g_autofree char *new_options = _ostree_kernel_args_to_string (kargs); + ostree_bootconfig_parser_set (bootconfig, "options", new_options); + } +} + +/* The first part of writing a deployment. This primarily means doing the + * hardlink farm checkout, but we also compute some initial state. */ -gboolean -ostree_sysroot_deploy_tree (OstreeSysroot *self, - const char *osname, - const char *revision, - GKeyFile *origin, - OstreeDeployment *provided_merge_deployment, - char **override_kernel_argv, - OstreeDeployment **out_new_deployment, - GCancellable *cancellable, - GError **error) +static gboolean +sysroot_initialize_deployment (OstreeSysroot *self, + const char *osname, + const char *revision, + GKeyFile *origin, + char **override_kernel_argv, + OstreeDeployment **out_new_deployment, + GCancellable *cancellable, + GError **error) { g_return_val_if_fail (osname != NULL || self->booted_deployment != NULL, FALSE); if (osname == NULL) osname = ostree_deployment_get_osname (self->booted_deployment); - const char *osdeploypath = glnx_strjoina ("ostree/deploy/", osname); - glnx_autofd int os_deploy_dfd = -1; - if (!glnx_opendirat (self->sysroot_fd, osdeploypath, TRUE, &os_deploy_dfd, error)) - return FALSE; - OstreeRepo *repo = ostree_sysroot_repo (self); - g_autoptr(OstreeDeployment) merge_deployment = NULL; - if (provided_merge_deployment != NULL) - merge_deployment = g_object_ref (provided_merge_deployment); gint new_deployserial; if (!allocate_deployserial (self, osname, revision, &new_deployserial, @@ -2430,54 +2485,374 @@ ostree_sysroot_deploy_tree (OstreeSysroot *self, return FALSE; _ostree_deployment_set_bootcsum (new_deployment, kernel_layout->bootcsum); + _ostree_deployment_set_bootconfig_from_kargs (new_deployment, override_kernel_argv); - /* Create an empty boot configuration; we will merge things into - * it as we go. - */ - g_autoptr(OstreeBootconfigParser) bootconfig = ostree_bootconfig_parser_new (); - ostree_deployment_set_bootconfig (new_deployment, bootconfig); - - g_autoptr(OstreeSePolicy) sepolicy = NULL; - if (!merge_configuration (self, repo, merge_deployment, new_deployment, - deployment_dfd, - &sepolicy, - cancellable, error)) + if (!prepare_deployment_etc (self, repo, new_deployment, deployment_dfd, + cancellable, error)) return FALSE; - if (!selinux_relabel_var_if_needed (self, sepolicy, os_deploy_dfd, - cancellable, error)) + ot_transfer_out_value (out_new_deployment, &new_deployment); + return TRUE; +} + +static gboolean +sysroot_finalize_deployment (OstreeSysroot *self, + OstreeDeployment *deployment, + char **override_kernel_argv, + OstreeDeployment *merge_deployment, + GCancellable *cancellable, + GError **error) +{ + g_autofree char *deployment_path = ostree_sysroot_get_deployment_dirpath (self, deployment); + glnx_autofd int deployment_dfd = -1; + if (!glnx_opendirat (self->sysroot_fd, deployment_path, TRUE, &deployment_dfd, error)) return FALSE; - if (!(self->debug_flags & OSTREE_SYSROOT_DEBUG_MUTABLE_DEPLOYMENTS)) + /* Only use the merge if we didn't get an override */ + if (!override_kernel_argv && merge_deployment) { - if (!ostree_sysroot_deployment_set_mutable (self, new_deployment, FALSE, - cancellable, error)) + /* Override the bootloader arguments */ + OstreeBootconfigParser *merge_bootconfig = ostree_deployment_get_bootconfig (merge_deployment); + if (merge_bootconfig) + { + const char *opts = ostree_bootconfig_parser_get (merge_bootconfig, "options"); + ostree_bootconfig_parser_set (ostree_deployment_get_bootconfig (deployment), "options", opts); + } + + } + + if (merge_deployment) + { + /* And do the /etc merge */ + if (!merge_configuration_from (self, merge_deployment, deployment, deployment_dfd, + cancellable, error)) return FALSE; } - /* Don't fsync here, as we assume that's all done in - * ostree_sysroot_write_deployments(). + g_autoptr(OstreeSePolicy) sepolicy = ostree_sepolicy_new_at (deployment_dfd, cancellable, error); + if (!sepolicy) + return FALSE; + + if (!selinux_relabel_var_if_needed (self, sepolicy, deployment, cancellable, error)) + return FALSE; + + /* Rewrite the origin using the final merged selinux config, just to be + * conservative about getting the right labels. */ - if (!write_origin_file_internal (self, sepolicy, new_deployment, NULL, + if (!write_origin_file_internal (self, sepolicy, deployment, + ostree_deployment_get_origin (deployment), GLNX_FILE_REPLACE_NODATASYNC, cancellable, error)) return FALSE; - /* After this, install_deployment_kernel() will set the other boot - * options and write it out to disk. - */ - if (override_kernel_argv) + /* Seal it */ + if (!(self->debug_flags & OSTREE_SYSROOT_DEBUG_MUTABLE_DEPLOYMENTS)) { - g_autoptr(OstreeKernelArgs) kargs = NULL; - g_autofree char *new_options = NULL; - - kargs = _ostree_kernel_args_new (); - _ostree_kernel_args_append_argv (kargs, override_kernel_argv); - new_options = _ostree_kernel_args_to_string (kargs); - ostree_bootconfig_parser_set (bootconfig, "options", new_options); + if (!ostree_sysroot_deployment_set_mutable (self, deployment, FALSE, + cancellable, error)) + return FALSE; } - ot_transfer_out_value (out_new_deployment, &new_deployment); + return TRUE; +} + +/** + * ostree_sysroot_deploy_tree: + * @self: Sysroot + * @osname: (allow-none): osname to use for merge deployment + * @revision: Checksum to add + * @origin: (allow-none): Origin to use for upgrades + * @provided_merge_deployment: (allow-none): Use this deployment for merge path + * @override_kernel_argv: (allow-none) (array zero-terminated=1) (element-type utf8): Use these as kernel arguments; if %NULL, inherit options from provided_merge_deployment + * @out_new_deployment: (out): The new deployment path + * @cancellable: Cancellable + * @error: Error + * + * Check out deployment tree with revision @revision, performing a 3 + * way merge with @provided_merge_deployment for configuration. + * + * While this API is not deprecated, you most likely want to use the + * ostree_sysroot_stage_tree() API. + */ +gboolean +ostree_sysroot_deploy_tree (OstreeSysroot *self, + const char *osname, + const char *revision, + GKeyFile *origin, + OstreeDeployment *provided_merge_deployment, + char **override_kernel_argv, + OstreeDeployment **out_new_deployment, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(OstreeDeployment) deployment = NULL; + if (!sysroot_initialize_deployment (self, osname, revision, origin, override_kernel_argv, + &deployment, cancellable, error)) + return FALSE; + + if (!sysroot_finalize_deployment (self, deployment, override_kernel_argv, + provided_merge_deployment, + cancellable, error)) + return FALSE; + + *out_new_deployment = g_steal_pointer (&deployment); + return TRUE; +} + +/* Serialize information about a deployment to a variant, used by the staging + * code. + */ +static GVariant * +serialize_deployment_to_variant (OstreeDeployment *deployment) +{ + g_auto(GVariantBuilder) builder = OT_VARIANT_BUILDER_INITIALIZER; + g_variant_builder_init (&builder, (GVariantType*)"a{sv}"); + g_autofree char *name = + g_strdup_printf ("%s.%d", ostree_deployment_get_csum (deployment), + ostree_deployment_get_deployserial (deployment)); + g_variant_builder_add (&builder, "{sv}", "name", + g_variant_new_string (name)); + g_variant_builder_add (&builder, "{sv}", "osname", + g_variant_new_string (ostree_deployment_get_osname (deployment))); + g_variant_builder_add (&builder, "{sv}", "bootcsum", + g_variant_new_string (ostree_deployment_get_bootcsum (deployment))); + + return g_variant_builder_end (&builder); +} + +static gboolean +require_str_key (GVariantDict *dict, + const char *name, + const char **ret, + GError **error) +{ + if (!g_variant_dict_lookup (dict, name, "&s", ret)) + return glnx_throw (error, "Missing key: %s", name); + return TRUE; +} + +/* Reverse of the above; convert a variant to a deployment. Note that the + * deployment may not actually be present; this should be verified by + * higher level code. + */ +OstreeDeployment * +_ostree_sysroot_deserialize_deployment_from_variant (GVariant *v, + GError **error) +{ + g_autoptr(GVariantDict) dict = g_variant_dict_new (v); + const char *name = NULL; + if (!require_str_key (dict, "name", &name, error)) + return FALSE; + const char *bootcsum = NULL; + if (!require_str_key (dict, "bootcsum", &bootcsum, error)) + return FALSE; + const char *osname = NULL; + if (!require_str_key (dict, "osname", &osname, error)) + return FALSE; + g_autofree char *checksum = NULL; + gint deployserial; + if (!_ostree_sysroot_parse_deploy_path_name (name, &checksum, &deployserial, error)) + return NULL; + return ostree_deployment_new (-1, osname, checksum, deployserial, + bootcsum, -1); +} + + +/** + * ostree_sysroot_stage_tree: + * @self: Sysroot + * @osname: (allow-none): osname to use for merge deployment + * @revision: Checksum to add + * @origin: (allow-none): Origin to use for upgrades + * @merge_deployment: (allow-none): Use this deployment for merge path + * @override_kernel_argv: (allow-none) (array zero-terminated=1) (element-type utf8): Use these as kernel arguments; if %NULL, inherit options from provided_merge_deployment + * @out_new_deployment: (out): The new deployment path + * @cancellable: Cancellable + * @error: Error + * + * Like ostree_sysroot_deploy_tree(), but "finalization" only occurs at OS + * shutdown time. + */ +gboolean +ostree_sysroot_stage_tree (OstreeSysroot *self, + const char *osname, + const char *revision, + GKeyFile *origin, + OstreeDeployment *merge_deployment, + char **override_kernel_argv, + OstreeDeployment **out_new_deployment, + GCancellable *cancellable, + GError **error) +{ + 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"); + + /* This is a bit of a hack. When adding a new service we have to end up getting + * into the presets for downstream distros; see e.g. https://src.fedoraproject.org/rpms/ostree/pull-request/7 + * + * Then again, it's perhaps a bit nicer to only start the service on-demand anyways. + */ + const char *const systemctl_argv[] = {"systemctl", "start", "ostree-finalize-staged.service", NULL}; + int estatus; + if (!g_spawn_sync (NULL, (char**)systemctl_argv, NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, NULL, NULL, &estatus, error)) + return FALSE; + if (!g_spawn_check_exit_status (estatus, error)) + return FALSE; + + g_autoptr(OstreeDeployment) deployment = NULL; + if (!sysroot_initialize_deployment (self, osname, revision, origin, override_kernel_argv, + &deployment, cancellable, error)) + return FALSE; + + /* Write out the origin file using the sepolicy from the non-merged root for + * now (i.e. using /usr/etc policy, not /etc); in practice we don't really + * expect people to customize the label for it. + */ + { g_autofree char *deployment_path = ostree_sysroot_get_deployment_dirpath (self, deployment); + glnx_autofd int deployment_dfd = -1; + if (!glnx_opendirat (self->sysroot_fd, deployment_path, FALSE, + &deployment_dfd, error)) + return FALSE; + g_autoptr(OstreeSePolicy) sepolicy = ostree_sepolicy_new_at (deployment_dfd, cancellable, error); + if (!sepolicy) + return FALSE; + if (!write_origin_file_internal (self, sepolicy, deployment, + ostree_deployment_get_origin (deployment), + GLNX_FILE_REPLACE_NODATASYNC, + cancellable, error)) + return FALSE; + } + + /* After here we defer action until shutdown. The remaining arguments (merge + * deployment, kargs) are serialized to a state file in /run. + */ + + /* "target" is the staged deployment */ + g_autoptr(GVariantBuilder) builder = g_variant_builder_new ((GVariantType*)"a{sv}"); + g_variant_builder_add (builder, "{sv}", "target", + serialize_deployment_to_variant (deployment)); + + if (merge_deployment) + g_variant_builder_add (builder, "{sv}", "merge-deployment", + serialize_deployment_to_variant (merge_deployment)); + + if (override_kernel_argv) + g_variant_builder_add (builder, "{sv}", "kargs", + g_variant_new_strv ((const char *const*)override_kernel_argv, -1)); + + const char *parent = dirname (strdupa (_OSTREE_SYSROOT_RUNSTATE_STAGED)); + if (!glnx_shutil_mkdir_p_at (AT_FDCWD, parent, 0755, cancellable, error)) + return FALSE; + + g_autoptr(GVariant) state = g_variant_ref_sink (g_variant_builder_end (builder)); + if (!glnx_file_replace_contents_at (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED, + g_variant_get_data (state), g_variant_get_size (state), + GLNX_FILE_REPLACE_NODATASYNC, + cancellable, error)) + return FALSE; + + /* If we have a previous one, clean it up */ + if (self->staged_deployment) + { + if (!_ostree_sysroot_rmrf_deployment (self, self->staged_deployment, cancellable, error)) + return FALSE; + } + + /* Bump mtime so external processes know something changed, and then reload. */ + if (!_ostree_sysroot_bump_mtime (self, error)) + return FALSE; + if (!ostree_sysroot_load (self, cancellable, error)) + return FALSE; + /* Like deploy, we do a prepare cleanup; among other things, this ensures + * that a ref will be written for the staged tree. See also + * https://github.com/ostreedev/ostree/pull/1566 though which + * adds an ostree_sysroot_cleanup_prune() API. + */ + if (!ostree_sysroot_prepare_cleanup (self, cancellable, error)) + return FALSE; + + ot_transfer_out_value (out_new_deployment, &deployment); + return TRUE; +} + +/* Invoked at shutdown time by ostree-finalize-staged.service */ +gboolean +_ostree_sysroot_finalize_staged (OstreeSysroot *self, + GCancellable *cancellable, + GError **error) +{ + /* It's totally fine if there's no staged deployment; perhaps down the line + * though we could teach the ostree cmdline to tell systemd to activate the + * service when a staged deployment is created. + */ + if (!self->staged_deployment) + return TRUE; + + g_assert (self->staged_deployment_data); + + g_autoptr(OstreeDeployment) merge_deployment = NULL; + g_autoptr(GVariant) merge_deployment_v = NULL; + if (g_variant_lookup (self->staged_deployment_data, "merge-deployment", "@a{sv}", + &merge_deployment_v)) + { + g_autoptr(OstreeDeployment) merge_deployment_stub = + _ostree_sysroot_deserialize_deployment_from_variant (merge_deployment_v, error); + if (!merge_deployment_stub) + return FALSE; + for (guint i = 0; i < self->deployments->len; i++) + { + OstreeDeployment *deployment = self->deployments->pdata[i]; + if (ostree_deployment_equal (deployment, merge_deployment_stub)) + { + merge_deployment = g_object_ref (deployment); + break; + } + } + + if (!merge_deployment) + return glnx_throw (error, "Failed to find merge deployment %s.%d for staged", + ostree_deployment_get_csum (merge_deployment_stub), + ostree_deployment_get_deployserial (merge_deployment_stub)); + } + g_autofree char **kargs = NULL; + g_variant_lookup (self->staged_deployment_data, "kargs", "^a&s", &kargs); + + /* Unlink the staged state now; if we're interrupted in the middle, + * we don't want e.g. deal with the partially written /etc merge. + */ + if (!glnx_unlinkat (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED, 0, error)) + return FALSE; + + if (!sysroot_finalize_deployment (self, self->staged_deployment, NULL, merge_deployment, + cancellable, error)) + return FALSE; + + /* Now, take ownership of the staged state, as normally the API below strips + * it out. + */ + g_autoptr(OstreeDeployment) staged = g_steal_pointer (&self->staged_deployment); + staged->staged = FALSE; + g_ptr_array_remove_index (self->deployments, 0); + + /* TODO: Proxy across flags too? + * + * But note that we always use NO_CLEAN to avoid adding more latency at + * shutdown, and also because e.g. rpm-ostree wants to own the cleanup + * process. + */ + OstreeSysrootSimpleWriteDeploymentFlags flags = + OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NO_CLEAN; + if (!ostree_sysroot_simple_write_deployment (self, ostree_deployment_get_osname (staged), + staged, merge_deployment, flags, + cancellable, error)) + return FALSE; + + /* Do the basic cleanup that may impact /boot, but not the repo pruning */ + if (!ostree_sysroot_prepare_cleanup (self, cancellable, error)) + return FALSE; + return TRUE; } @@ -2499,6 +2874,9 @@ ostree_sysroot_deployment_set_kargs (OstreeSysroot *self, GCancellable *cancellable, GError **error) { + /* For now; instead of this do a redeployment */ + g_assert (!ostree_deployment_is_staged (deployment)); + g_autoptr(OstreeDeployment) new_deployment = ostree_deployment_clone (deployment); OstreeBootconfigParser *new_bootconfig = ostree_deployment_get_bootconfig (new_deployment); diff --git a/src/libostree/ostree-sysroot-private.h b/src/libostree/ostree-sysroot-private.h index b2776e8d..16cca5a1 100644 --- a/src/libostree/ostree-sysroot-private.h +++ b/src/libostree/ostree-sysroot-private.h @@ -50,8 +50,7 @@ struct OstreeSysroot { GLnxLockFile lock; gboolean loaded; - gboolean ostree_booted; - gboolean root_is_sysroot; /* TRUE if sysroot_fd is pointed to rootfs "/" */ + 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; ino_t root_inode; @@ -61,6 +60,8 @@ struct OstreeSysroot { int bootversion; int subbootversion; OstreeDeployment *booted_deployment; + OstreeDeployment *staged_deployment; + GVariant *staged_deployment_data; struct timespec loaded_ts; /* Only access through ostree_sysroot_[_get]repo() */ @@ -71,6 +72,7 @@ struct OstreeSysroot { #define OSTREE_SYSROOT_LOCKFILE "ostree/lock" /* We keep some transient state in /run */ +#define _OSTREE_SYSROOT_RUNSTATE_STAGED "/run/ostree/staged-deployment" #define _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_DIR "/run/ostree/deployment-state/" #define _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT "unlocked-development" @@ -105,6 +107,22 @@ _ostree_sysroot_list_deployment_dirs_for_os (int deploydir_dfd, GCancellable *cancellable, GError **error); +void +_ostree_deployment_set_bootconfig_from_kargs (OstreeDeployment *deployment, + char **override_kernel_argv); + +gboolean +_ostree_sysroot_reload_staged (OstreeSysroot *self, GError **error); + +gboolean +_ostree_sysroot_finalize_staged (OstreeSysroot *self, + GCancellable *cancellable, + GError **error); + +OstreeDeployment * +_ostree_sysroot_deserialize_deployment_from_variant (GVariant *v, + GError **error); + char * _ostree_sysroot_get_origin_relpath (GFile *path, guint32 *out_device, @@ -112,6 +130,14 @@ _ostree_sysroot_get_origin_relpath (GFile *path, GCancellable *cancellable, GError **error); +gboolean +_ostree_sysroot_rmrf_deployment (OstreeSysroot *sysroot, + OstreeDeployment *deployment, + GCancellable *cancellable, + GError **error); + +char * _ostree_sysroot_get_runstate_path (OstreeDeployment *deployment, const char *key); + char *_ostree_sysroot_join_lines (GPtrArray *lines); gboolean _ostree_sysroot_query_bootloader (OstreeSysroot *sysroot, diff --git a/src/libostree/ostree-sysroot-upgrader.c b/src/libostree/ostree-sysroot-upgrader.c index a87548a3..8fb231a3 100644 --- a/src/libostree/ostree-sysroot-upgrader.c +++ b/src/libostree/ostree-sysroot-upgrader.c @@ -644,21 +644,37 @@ ostree_sysroot_upgrader_deploy (OstreeSysrootUpgrader *self, GError **error) { g_autoptr(OstreeDeployment) new_deployment = NULL; - if (!ostree_sysroot_deploy_tree (self->sysroot, self->osname, - self->new_revision, - self->origin, - self->merge_deployment, - NULL, - &new_deployment, - cancellable, error)) - return FALSE; - if (!ostree_sysroot_simple_write_deployment (self->sysroot, self->osname, - new_deployment, - self->merge_deployment, - 0, - cancellable, error)) - return FALSE; + /* Experimental flag to enable staging */ + if (getenv ("OSTREE_EX_STAGE_DEPLOYMENTS")) + { + if (!ostree_sysroot_stage_tree (self->sysroot, self->osname, + self->new_revision, + self->origin, + self->merge_deployment, + NULL, + &new_deployment, + cancellable, error)) + return FALSE; + } + else + { + if (!ostree_sysroot_deploy_tree (self->sysroot, self->osname, + self->new_revision, + self->origin, + self->merge_deployment, + NULL, + &new_deployment, + cancellable, error)) + return FALSE; + + if (!ostree_sysroot_simple_write_deployment (self->sysroot, self->osname, + new_deployment, + self->merge_deployment, + 0, + cancellable, error)) + return FALSE; + } return TRUE; } diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index f77d7703..707b161f 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -82,6 +82,8 @@ ostree_sysroot_finalize (GObject *object) g_clear_object (&self->repo); g_clear_pointer (&self->deployments, g_ptr_array_unref); g_clear_object (&self->booted_deployment); + g_clear_object (&self->staged_deployment); + g_clear_pointer (&self->staged_deployment_data, (GDestroyNotify)g_variant_unref); glnx_release_lock_file (&self->lock); @@ -525,32 +527,30 @@ read_current_bootversion (OstreeSysroot *self, } static gboolean -parse_origin (OstreeSysroot *self, - int deployment_dfd, - const char *deployment_name, - GKeyFile **out_origin, - GCancellable *cancellable, - GError **error) +load_origin (OstreeSysroot *self, + OstreeDeployment *deployment, + GCancellable *cancellable, + GError **error) { - g_autofree char *origin_path = g_strconcat ("../", deployment_name, ".origin", NULL); - g_autoptr(GKeyFile) ret_origin = g_key_file_new (); + g_autofree char *origin_path = ostree_deployment_get_origin_relpath (deployment); - struct stat stbuf; - if (!glnx_fstatat_allow_noent (deployment_dfd, origin_path, &stbuf, 0, error)) + glnx_autofd int fd = -1; + if (!ot_openat_ignore_enoent (self->sysroot_fd, origin_path, &fd, error)) return FALSE; - if (errno == 0) + if (fd >= 0) { g_autofree char *origin_contents = - glnx_file_get_contents_utf8_at (deployment_dfd, origin_path, - NULL, cancellable, error); + glnx_fd_readall_utf8 (fd, NULL, cancellable, error); if (!origin_contents) return FALSE; - if (!g_key_file_load_from_data (ret_origin, origin_contents, -1, 0, error)) + g_autoptr(GKeyFile) origin = g_key_file_new (); + if (!g_key_file_load_from_data (origin, origin_contents, -1, 0, error)) return glnx_prefix_error (error, "Parsing %s", origin_path); + + ostree_deployment_set_origin (deployment, origin); } - ot_transfer_out_value(out_origin, &ret_origin); return TRUE; } @@ -584,14 +584,14 @@ parse_bootlink (const char *bootlink, return TRUE; } -static char * -get_unlocked_development_path (OstreeDeployment *deployment) +char * +_ostree_sysroot_get_runstate_path (OstreeDeployment *deployment, const char *key) { return g_strdup_printf ("%s%s.%d/%s", _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_DIR, ostree_deployment_get_csum (deployment), ostree_deployment_get_deployserial (deployment), - _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT); + key); } static gboolean @@ -637,8 +637,7 @@ parse_deployment (OstreeSysroot *self, /* See if this is the booted deployment */ const gboolean looking_for_booted_deployment = - (self->ostree_booted && self->root_is_sysroot && - !self->booted_deployment); + (self->root_is_ostree_booted && !self->booted_deployment); gboolean is_booted_deployment = FALSE; if (looking_for_booted_deployment) { @@ -653,26 +652,23 @@ parse_deployment (OstreeSysroot *self, stbuf.st_ino == self->root_inode); } - g_autoptr(GKeyFile) origin = NULL; - if (!parse_origin (self, deployment_dfd, deploy_basename, &origin, - cancellable, error)) - return FALSE; - g_autoptr(OstreeDeployment) ret_deployment = ostree_deployment_new (-1, osname, treecsum, deployserial, bootcsum, treebootserial); - if (origin) - ostree_deployment_set_origin (ret_deployment, origin); + if (!load_origin (self, ret_deployment, cancellable, error)) + return FALSE; ret_deployment->unlocked = OSTREE_DEPLOYMENT_UNLOCKED_NONE; - g_autofree char *unlocked_development_path = get_unlocked_development_path (ret_deployment); + g_autofree char *unlocked_development_path = + _ostree_sysroot_get_runstate_path (ret_deployment, _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT); struct stat stbuf; if (lstat (unlocked_development_path, &stbuf) == 0) ret_deployment->unlocked = OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT; else { - g_autofree char *existing_unlocked_state = - g_key_file_get_string (origin, "origin", "unlocked", NULL); + GKeyFile *origin = ostree_deployment_get_origin (ret_deployment); + g_autofree char *existing_unlocked_state = origin ? + g_key_file_get_string (origin, "origin", "unlocked", NULL) : NULL; if (g_strcmp0 (existing_unlocked_state, "hotfix") == 0) { @@ -742,6 +738,15 @@ compare_deployments_by_boot_loader_version_reversed (gconstpointer a_pp, OstreeBootconfigParser *a_bootconfig = ostree_deployment_get_bootconfig (a); OstreeBootconfigParser *b_bootconfig = ostree_deployment_get_bootconfig (b); + /* Staged deployments are always first */ + if (ostree_deployment_is_staged (a)) + { + g_assert (!ostree_deployment_is_staged (b)); + return -1; + } + else if (ostree_deployment_is_staged (b)) + return 1; + return compare_boot_loader_configs (a_bootconfig, b_bootconfig); } @@ -789,6 +794,63 @@ ensure_repo (OstreeSysroot *self, return TRUE; } +/* Reload the staged deployment from the file in /run */ +gboolean +_ostree_sysroot_reload_staged (OstreeSysroot *self, + GError **error) +{ + GLNX_AUTO_PREFIX_ERROR ("Loading staged deployment", error); + if (!self->root_is_ostree_booted) + return TRUE; /* Note early return */ + + g_assert (self->booted_deployment); + + g_clear_object (&self->staged_deployment); + g_clear_pointer (&self->staged_deployment_data, (GDestroyNotify)g_variant_unref); + + /* Read the staged state from disk */ + glnx_autofd int fd = -1; + if (!ot_openat_ignore_enoent (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED, &fd, error)) + return FALSE; + if (fd != -1) + { + g_autoptr(GBytes) contents = ot_fd_readall_or_mmap (fd, 0, error); + if (!contents) + return FALSE; + g_autoptr(GVariant) staged_deployment_data = + g_variant_new_from_bytes ((GVariantType*)"a{sv}", contents, TRUE); + g_autoptr(GVariantDict) staged_deployment_dict = + g_variant_dict_new (staged_deployment_data); + + /* Parse it */ + g_autoptr(GVariant) target = NULL; + g_autofree char **kargs = NULL; + g_variant_dict_lookup (staged_deployment_dict, "target", "@a{sv}", &target); + g_variant_dict_lookup (staged_deployment_dict, "kargs", "^a&s", &kargs); + if (target) + { + g_autoptr(OstreeDeployment) staged = + _ostree_sysroot_deserialize_deployment_from_variant (target, error); + if (!staged) + return FALSE; + + _ostree_deployment_set_bootconfig_from_kargs (staged, kargs); + if (!load_origin (self, staged, NULL, error)) + return FALSE; + + self->staged_deployment = g_steal_pointer (&staged); + self->staged_deployment_data = g_steal_pointer (&staged_deployment_data); + /* We set this flag for ostree_deployment_is_staged() because that API + * doesn't have access to the sysroot, which currently has the + * canonical "staged_deployment" reference. + */ + self->staged_deployment->staged = TRUE; + } + } + + return TRUE; +} + gboolean ostree_sysroot_load_if_changed (OstreeSysroot *self, gboolean *out_changed, @@ -813,7 +875,7 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self, { if (!glnx_fstatat_allow_noent (AT_FDCWD, "/run/ostree-booted", NULL, 0, error)) return FALSE; - self->ostree_booted = (errno == 0); + const gboolean ostree_booted = (errno == 0); { struct stat root_stbuf; if (!glnx_fstatat (AT_FDCWD, "/", &root_stbuf, 0, error)) @@ -826,9 +888,11 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self, if (!glnx_fstat (self->sysroot_fd, &self_stbuf, error)) return FALSE; - self->root_is_sysroot = + 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; @@ -857,6 +921,7 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self, g_clear_pointer (&self->deployments, g_ptr_array_unref); g_clear_object (&self->booted_deployment); + g_clear_object (&self->staged_deployment); self->bootversion = -1; self->subbootversion = -1; @@ -880,11 +945,20 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self, } } - if (self->ostree_booted && self->root_is_sysroot - && !self->booted_deployment) + if (self->root_is_ostree_booted && !self->booted_deployment) return glnx_throw (error, "Unexpected state: /run/ostree-booted found and in / sysroot but not in a booted deployment"); + if (!_ostree_sysroot_reload_staged (self, error)) + return FALSE; + + /* Ensure the entires are sorted */ g_ptr_array_sort (deployments, compare_deployments_by_boot_loader_version_reversed); + + /* Staged shows up first */ + if (self->staged_deployment) + g_ptr_array_insert (deployments, 0, g_object_ref (self->staged_deployment)); + + /* And then set their index variables */ for (guint i = 0; i < deployments->len; i++) { OstreeDeployment *deployment = deployments->pdata[i]; @@ -949,6 +1023,20 @@ ostree_sysroot_get_booted_deployment (OstreeSysroot *self) return self->booted_deployment; } +/** + * ostree_sysroot_get_staged_deployment: + * @self: Sysroot + * + * Returns: (transfer none): The currently staged deployment, or %NULL if none + */ +OstreeDeployment * +ostree_sysroot_get_staged_deployment (OstreeSysroot *self) +{ + g_return_val_if_fail (self->loaded, NULL); + + return self->staged_deployment; +} + /** * ostree_sysroot_get_deployments: * @self: Sysroot @@ -1769,7 +1857,8 @@ ostree_sysroot_deployment_unlock (OstreeSysroot *self, break; case OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT: { - g_autofree char *devpath = get_unlocked_development_path (deployment); + g_autofree char *devpath = + _ostree_sysroot_get_runstate_path (deployment, _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT); g_autofree char *devpath_parent = dirname (g_strdup (devpath)); if (!glnx_shutil_mkdir_p_at (AT_FDCWD, devpath_parent, 0755, cancellable, error)) diff --git a/src/libostree/ostree-sysroot.h b/src/libostree/ostree-sysroot.h index e4763d37..47cbb022 100644 --- a/src/libostree/ostree-sysroot.h +++ b/src/libostree/ostree-sysroot.h @@ -74,6 +74,8 @@ _OSTREE_PUBLIC GPtrArray *ostree_sysroot_get_deployments (OstreeSysroot *self); _OSTREE_PUBLIC OstreeDeployment *ostree_sysroot_get_booted_deployment (OstreeSysroot *self); +_OSTREE_PUBLIC +OstreeDeployment *ostree_sysroot_get_staged_deployment (OstreeSysroot *self); _OSTREE_PUBLIC GFile *ostree_sysroot_get_deployment_directory (OstreeSysroot *self, @@ -174,6 +176,17 @@ gboolean ostree_sysroot_deploy_tree (OstreeSysroot *self, GCancellable *cancellable, GError **error); +_OSTREE_PUBLIC +gboolean ostree_sysroot_stage_tree (OstreeSysroot *self, + const char *osname, + const char *revision, + GKeyFile *origin, + OstreeDeployment *merge_deployment, + char **override_kernel_argv, + OstreeDeployment **out_new_deployment, + GCancellable *cancellable, + GError **error); + _OSTREE_PUBLIC gboolean ostree_sysroot_deployment_set_mutable (OstreeSysroot *self, OstreeDeployment *deployment, diff --git a/src/libostree/ostree-version.h b/src/libostree/ostree-version.h index 1f843105..78828b1e 100644 --- a/src/libostree/ostree-version.h +++ b/src/libostree/ostree-version.h @@ -43,7 +43,7 @@ * * Since: 2017.4 */ -#define OSTREE_RELEASE_VERSION (4) +#define OSTREE_RELEASE_VERSION (5) /** * OSTREE_VERSION @@ -52,7 +52,7 @@ * * Since: 2017.4 */ -#define OSTREE_VERSION (2018.4) +#define OSTREE_VERSION (2018.5) /** * OSTREE_VERSION_S: @@ -62,7 +62,7 @@ * * Since: 2017.4 */ -#define OSTREE_VERSION_S "2018.4" +#define OSTREE_VERSION_S "2018.5" #define OSTREE_ENCODE_VERSION(year,release) \ ((year) << 16 | (release)) diff --git a/src/libotutil/ot-checksum-utils.c b/src/libotutil/ot-checksum-utils.c index 6e0e5641..6eb6fdc0 100644 --- a/src/libotutil/ot-checksum-utils.c +++ b/src/libotutil/ot-checksum-utils.c @@ -54,6 +54,7 @@ ot_bin2hex (char *out_buf, const guint8 *inbuf, gsize len) */ typedef struct { gboolean initialized; + gboolean closed; #if defined(HAVE_OPENSSL) EVP_MD_CTX *checksum; #elif defined(HAVE_GNUTLS) @@ -84,6 +85,7 @@ ot_checksum_init (OtChecksum *checksum) real->digest_len = g_checksum_type_get_length (G_CHECKSUM_SHA256); #endif g_assert_cmpint (real->digest_len, ==, _OSTREE_SHA256_DIGEST_LEN); + real->closed = FALSE; real->initialized = TRUE; } @@ -94,6 +96,7 @@ ot_checksum_update (OtChecksum *checksum, { OtRealChecksum *real = (OtRealChecksum*)checksum; g_return_if_fail (real->initialized); + g_return_if_fail (!real->closed); #if defined(HAVE_OPENSSL) g_assert (EVP_DigestUpdate (real->checksum, buf, len)); #elif defined(HAVE_GNUTLS) @@ -130,7 +133,7 @@ ot_checksum_get_digest (OtChecksum *checksum, { OtRealChecksum *real = (OtRealChecksum*)checksum; ot_checksum_get_digest_internal (real, buf, buflen); - real->initialized = FALSE; + real->closed = TRUE; } void @@ -143,7 +146,6 @@ ot_checksum_get_hexdigest (OtChecksum *checksum, guint8 digest_buf[digest_len]; ot_checksum_get_digest (checksum, digest_buf, digest_len); ot_bin2hex (buf, (guint8*)digest_buf, digest_len); - real->initialized = FALSE; } void diff --git a/src/ostree/ot-admin-builtin-cleanup.c b/src/ostree/ot-admin-builtin-cleanup.c index a4753030..875d6fa3 100644 --- a/src/ostree/ot-admin-builtin-cleanup.c +++ b/src/ostree/ot-admin-builtin-cleanup.c @@ -37,21 +37,16 @@ static GOptionEntry options[] = { gboolean ot_admin_builtin_cleanup (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { - g_autoptr(GOptionContext) context = NULL; + g_autoptr(GOptionContext) context = g_option_context_new (""); + g_autoptr(OstreeSysroot) sysroot = NULL; - gboolean ret = FALSE; - - context = g_option_context_new (""); - if (!ostree_admin_option_context_parse (context, options, &argc, &argv, OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER, invocation, &sysroot, cancellable, error)) - goto out; + return FALSE; if (!ostree_sysroot_cleanup (sysroot, cancellable, error)) - goto out; + return FALSE; - ret = TRUE; - out: - return ret; + return TRUE; } diff --git a/src/ostree/ot-admin-builtin-deploy.c b/src/ostree/ot-admin-builtin-deploy.c index d9905212..38ec923f 100644 --- a/src/ostree/ot-admin-builtin-deploy.c +++ b/src/ostree/ot-admin-builtin-deploy.c @@ -34,6 +34,7 @@ #include static gboolean opt_retain; +static gboolean opt_stage; static gboolean opt_retain_pending; static gboolean opt_retain_rollback; static gboolean opt_not_as_default; @@ -50,6 +51,7 @@ static GOptionEntry options[] = { { "origin-file", 0, 0, G_OPTION_ARG_FILENAME, &opt_origin_path, "Specify origin file", "FILENAME" }, { "no-prune", 0, 0, G_OPTION_ARG_NONE, &opt_no_prune, "Don't prune the repo when done", NULL}, { "retain", 0, 0, G_OPTION_ARG_NONE, &opt_retain, "Do not delete previous deployments", NULL }, + { "stage", 0, 0, G_OPTION_ARG_NONE, &opt_stage, "Complete deployment at OS shutdown", NULL }, { "retain-pending", 0, 0, G_OPTION_ARG_NONE, &opt_retain_pending, "Do not delete pending deployments", NULL }, { "retain-rollback", 0, 0, G_OPTION_ARG_NONE, &opt_retain_rollback, "Do not delete rollback deployments", NULL }, { "not-as-default", 0, 0, G_OPTION_ARG_NONE, &opt_not_as_default, "Append rather than prepend new deployment", NULL }, @@ -63,8 +65,6 @@ static GOptionEntry options[] = { gboolean ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { - g_autoptr(OstreeKernelArgs) kargs = NULL; - g_autoptr(GOptionContext) context = g_option_context_new ("REFSPEC"); @@ -127,61 +127,89 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeCommandInvocation *invocat if (!ostree_sysroot_prepare_cleanup (sysroot, cancellable, error)) return glnx_prefix_error (error, "Performing initial cleanup"); - kargs = _ostree_kernel_args_new (); - - /* If they want the current kernel's args, they very likely don't - * want the ones from the merge. + /* Initial set of kernel arguments; the default is to use the merge + * deployment, unless --karg-none or --karg-proc-cmdline are specified. */ - if (opt_kernel_proc_cmdline) + g_autoptr(OstreeKernelArgs) kargs = NULL; + if (opt_kernel_arg_none) { + kargs = _ostree_kernel_args_new (); + } + else if (opt_kernel_proc_cmdline) + { + kargs = _ostree_kernel_args_new (); if (!_ostree_kernel_args_append_proc_cmdline (kargs, cancellable, error)) return FALSE; } - else if (merge_deployment && !opt_kernel_arg_none) + else if (merge_deployment && (opt_kernel_argv || opt_kernel_argv_append)) { OstreeBootconfigParser *bootconfig = ostree_deployment_get_bootconfig (merge_deployment); g_auto(GStrv) previous_args = g_strsplit (ostree_bootconfig_parser_get (bootconfig, "options"), " ", -1); - + kargs = _ostree_kernel_args_new (); _ostree_kernel_args_append_argv (kargs, previous_args); } + /* Now replace/extend the above set. Note that if no options are specified, + * we should end up passing NULL as override_kernel_argv for + * ostree_sysroot_deploy_tree() so we get the defaults. + */ if (opt_kernel_argv) { + if (!kargs) + kargs = _ostree_kernel_args_new (); _ostree_kernel_args_replace_argv (kargs, opt_kernel_argv); } if (opt_kernel_argv_append) { + if (!kargs) + kargs = _ostree_kernel_args_new (); _ostree_kernel_args_append_argv (kargs, opt_kernel_argv_append); } g_autoptr(OstreeDeployment) new_deployment = NULL; - g_auto(GStrv) kargs_strv = _ostree_kernel_args_to_strv (kargs); - if (!ostree_sysroot_deploy_tree (sysroot, opt_osname, revision, origin, merge_deployment, - kargs_strv, &new_deployment, cancellable, error)) - return FALSE; - - OstreeSysrootSimpleWriteDeploymentFlags flags = OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NO_CLEAN; - if (opt_retain) - flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN; + g_auto(GStrv) kargs_strv = kargs ? _ostree_kernel_args_to_strv (kargs) : NULL; + if (opt_stage) + { + if (opt_retain_pending || opt_retain_rollback) + return glnx_throw (error, "--stage cannot currently be combined with --retain arguments"); + if (opt_not_as_default) + return glnx_throw (error, "--stage cannot currently be combined with --not-as-default"); + if (!ostree_sysroot_stage_tree (sysroot, opt_osname, revision, origin, merge_deployment, + kargs_strv, &new_deployment, cancellable, error)) + return FALSE; + g_assert (new_deployment); + } else { - if (opt_retain_pending) - flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PENDING; - if (opt_retain_rollback) - flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_ROLLBACK; + if (!ostree_sysroot_deploy_tree (sysroot, opt_osname, revision, origin, merge_deployment, + kargs_strv, &new_deployment, cancellable, error)) + return FALSE; + g_assert (new_deployment); + + OstreeSysrootSimpleWriteDeploymentFlags flags = OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NO_CLEAN; + if (opt_retain) + flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN; + else + { + if (opt_retain_pending) + flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_PENDING; + if (opt_retain_rollback) + flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_RETAIN_ROLLBACK; + } + + if (opt_not_as_default) + flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NOT_DEFAULT; + + if (!ostree_sysroot_simple_write_deployment (sysroot, opt_osname, new_deployment, + merge_deployment, flags, cancellable, error)) + return FALSE; } - if (opt_not_as_default) - flags |= OSTREE_SYSROOT_SIMPLE_WRITE_DEPLOYMENT_FLAGS_NOT_DEFAULT; - - if (!ostree_sysroot_simple_write_deployment (sysroot, opt_osname, new_deployment, - merge_deployment, flags, cancellable, error)) - return FALSE; - - /* And finally, cleanup of any leftover data. + /* And finally, cleanup of any leftover data. In stage mode, we + * don't do a full cleanup as we didn't touch the bootloader. */ - if (opt_no_prune) + if (opt_no_prune || opt_stage) { if (!ostree_sysroot_prepare_cleanup (sysroot, cancellable, error)) return FALSE; diff --git a/src/ostree/ot-admin-builtin-finalize-staged.c b/src/ostree/ot-admin-builtin-finalize-staged.c new file mode 100644 index 00000000..6740f82a --- /dev/null +++ b/src/ostree/ot-admin-builtin-finalize-staged.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2018 Red Hat, 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. + */ + +#include "config.h" + +#include "config.h" + +#include + +#include "ot-main.h" +#include "ot-admin-builtins.h" +#include "ot-admin-functions.h" +#include "ostree.h" +#include "otutil.h" + +#include "ostree-cmdprivate.h" +#include "ostree.h" + +/* Called by ostree-finalize-staged.service, and in turn + * invokes a cmdprivate function inside the shared library. + */ +gboolean +ot_admin_builtin_finalize_staged (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) +{ + /* Just a sanity check; we shouldn't be called outside of the service though. + */ + struct stat stbuf; + 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)) + return FALSE; + if (!ostree_cmd__private__()->ostree_finalize_staged (sysroot, cancellable, error)) + return FALSE; + + return TRUE; +} diff --git a/src/ostree/ot-admin-builtin-instutil.c b/src/ostree/ot-admin-builtin-instutil.c index 035bd4f8..fe0d80a4 100644 --- a/src/ostree/ot-admin-builtin-instutil.c +++ b/src/ostree/ot-admin-builtin-instutil.c @@ -55,9 +55,12 @@ ostree_admin_instutil_option_context_new_with_commands (void) while (command->name != NULL) { - g_string_append_printf (summary, "\n %-24s", command->name); - if (command->description != NULL) - g_string_append_printf (summary, "%s", command->description); + if ((command->flags & OSTREE_BUILTIN_FLAG_HIDDEN) == 0) + { + g_string_append_printf (summary, "\n %-24s", command->name); + if (command->description != NULL) + g_string_append_printf (summary, "%s", command->description); + } command++; } diff --git a/src/ostree/ot-admin-builtin-status.c b/src/ostree/ot-admin-builtin-status.c index 096155c6..ca165f45 100644 --- a/src/ostree/ot-admin-builtin-status.c +++ b/src/ostree/ot-admin-builtin-status.c @@ -96,7 +96,9 @@ deployment_print_status (OstreeSysroot *sysroot, GKeyFile *origin = ostree_deployment_get_origin (deployment); const char *deployment_status = ""; - if (is_pending) + if (ostree_deployment_is_staged (deployment)) + deployment_status = " (staged)"; + else if (is_pending) deployment_status = " (pending)"; else if (is_rollback) deployment_status = " (rollback)"; diff --git a/src/ostree/ot-admin-builtins.h b/src/ostree/ot-admin-builtins.h index a81f4d62..d88fc0b9 100644 --- a/src/ostree/ot-admin-builtins.h +++ b/src/ostree/ot-admin-builtins.h @@ -40,6 +40,7 @@ BUILTINPROTO(undeploy); BUILTINPROTO(deploy); BUILTINPROTO(cleanup); BUILTINPROTO(pin); +BUILTINPROTO(finalize_staged); BUILTINPROTO(unlock); BUILTINPROTO(status); BUILTINPROTO(set_origin); diff --git a/src/ostree/ot-builtin-admin.c b/src/ostree/ot-builtin-admin.c index fd6d9a8f..b26eea81 100644 --- a/src/ostree/ot-builtin-admin.c +++ b/src/ostree/ot-builtin-admin.c @@ -45,9 +45,9 @@ static OstreeCommand admin_subcommands[] = { { "init-fs", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_init_fs, "Initialize a root filesystem" }, - { "instutil", OSTREE_BUILTIN_FLAG_NO_REPO, + { "instutil", OSTREE_BUILTIN_FLAG_NO_REPO | OSTREE_BUILTIN_FLAG_HIDDEN, ot_admin_builtin_instutil, - "Provide instutil commands, allow admin to change boot configuration and relabel selinux " }, + "Deprecated commands intended for installer programs" }, { "os-init", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_os_init, "Initialize empty state for given operating system" }, @@ -57,6 +57,9 @@ static OstreeCommand admin_subcommands[] = { { "pin", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_pin, "Change the \"pinning\" state of a deployment" }, + { "finalize-staged", OSTREE_BUILTIN_FLAG_NO_REPO | OSTREE_BUILTIN_FLAG_HIDDEN, + ot_admin_builtin_finalize_staged, + "Internal command to run at shutdown time" }, { "status", OSTREE_BUILTIN_FLAG_NO_REPO, ot_admin_builtin_status, "List deployments" }, @@ -85,9 +88,12 @@ ostree_admin_option_context_new_with_commands (void) while (command->name != NULL) { - g_string_append_printf (summary, "\n %-19s", command->name); - if (command->description != NULL) - g_string_append_printf (summary, "%s", command->description); + if ((command->flags & OSTREE_BUILTIN_FLAG_HIDDEN) == 0) + { + g_string_append_printf (summary, "\n %-19s", command->name); + if (command->description != NULL) + g_string_append_printf (summary, "%s", command->description); + } command++; } diff --git a/src/ostree/ot-builtin-create-usb.c b/src/ostree/ot-builtin-create-usb.c index 02b55b94..57feeaa1 100644 --- a/src/ostree/ot-builtin-create-usb.c +++ b/src/ostree/ot-builtin-create-usb.c @@ -41,7 +41,6 @@ static GOptionEntry options[] = { NULL } }; -/* TODO: Add a man page. */ gboolean ostree_builtin_create_usb (int argc, char **argv, @@ -201,11 +200,7 @@ ostree_builtin_create_usb (int argc, /* FIXME: It should be possible to work without this, but find_remotes_cb() in * ostree-repo-pull.c currently assumes a summary file (signed or unsigned) is * present. */ - struct stat stbuf; - if (!glnx_fstatat_allow_noent (ostree_repo_get_dfd (dest_repo), "summary", &stbuf, 0, error)) - return FALSE; - if (errno == ENOENT && - !ostree_repo_regenerate_summary (dest_repo, NULL, cancellable, error)) + if (!ostree_repo_regenerate_summary (dest_repo, NULL, cancellable, error)) return FALSE; /* Add the symlinks .ostree/repos.d/@symlink_name → @dest_repo_path, unless diff --git a/src/ostree/ot-builtin-fsck.c b/src/ostree/ot-builtin-fsck.c index 3f9da783..81124d3f 100644 --- a/src/ostree/ot-builtin-fsck.c +++ b/src/ostree/ot-builtin-fsck.c @@ -53,6 +53,8 @@ static gboolean fsck_one_object (OstreeRepo *repo, const char *checksum, OstreeObjectType objtype, + GHashTable *object_parents, + GVariant *key, gboolean *out_found_corruption, GCancellable *cancellable, GError **error) @@ -60,12 +62,14 @@ fsck_one_object (OstreeRepo *repo, g_autoptr(GError) temp_error = NULL; if (!ostree_repo_fsck_object (repo, objtype, checksum, cancellable, &temp_error)) { + gboolean object_missing = FALSE; + if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { g_clear_error (&temp_error); g_printerr ("Object missing: %s.%s\n", checksum, ostree_object_type_to_string (objtype)); - *out_found_corruption = TRUE; + object_missing = TRUE; } else { @@ -73,7 +77,7 @@ fsck_one_object (OstreeRepo *repo, { g_printerr ("%s\n", temp_error->message); (void) ostree_repo_delete_object (repo, objtype, checksum, cancellable, NULL); - *out_found_corruption = TRUE; + object_missing = TRUE; } else { @@ -81,6 +85,33 @@ fsck_one_object (OstreeRepo *repo, return FALSE; } } + + if (object_missing) + { + *out_found_corruption = TRUE; + + if (object_parents != NULL && objtype != OSTREE_OBJECT_TYPE_COMMIT) + { + g_auto(GStrv) parent_commits = ostree_repo_traverse_parents_get_commits (object_parents, key); + int i; + + /* The commit was missing or deleted, mark the commit partial */ + for (i = 0; parent_commits[i] != NULL; i++) + { + const char *parent_commit = parent_commits[i]; + OstreeRepoCommitState state; + if (!ostree_repo_load_commit (repo, parent_commit, NULL, + &state, error)) + return FALSE; + if ((state & OSTREE_REPO_COMMIT_STATE_PARTIAL) == 0) + { + g_printerr ("Marking commit as partial: %s\n", parent_commit); + if (!ostree_repo_mark_commit_partial (repo, parent_commit, TRUE, error)) + return FALSE; + } + } + } + } } return TRUE; @@ -94,6 +125,7 @@ fsck_reachable_objects_from_commits (OstreeRepo *repo, GError **error) { g_autoptr(GHashTable) reachable_objects = ostree_repo_traverse_new_reachable (); + g_autoptr(GHashTable) object_parents = ostree_repo_traverse_new_parents (); GHashTableIter hash_iter; gpointer key, value; @@ -108,8 +140,8 @@ fsck_reachable_objects_from_commits (OstreeRepo *repo, g_assert (objtype == OSTREE_OBJECT_TYPE_COMMIT); - if (!ostree_repo_traverse_commit_union (repo, checksum, 0, reachable_objects, - cancellable, error)) + if (!ostree_repo_traverse_commit_union_with_parents (repo, checksum, 0, reachable_objects, object_parents, + cancellable, error)) return FALSE; } @@ -127,8 +159,9 @@ fsck_reachable_objects_from_commits (OstreeRepo *repo, ostree_object_name_deserialize (serialized_key, &checksum, &objtype); - if (!fsck_one_object (repo, checksum, objtype, out_found_corruption, - cancellable, error)) + if (!fsck_one_object (repo, checksum, objtype, + object_parents, serialized_key, + out_found_corruption, cancellable, error)) return FALSE; i++; @@ -150,7 +183,7 @@ fsck_commit_for_ref (OstreeRepo *repo, GError **error) { if (!fsck_one_object (repo, checksum, OSTREE_OBJECT_TYPE_COMMIT, - found_corruption, + NULL, NULL, found_corruption, cancellable, error)) return FALSE; diff --git a/src/ostree/ot-builtin-init.c b/src/ostree/ot-builtin-init.c index ae0d0cc6..a4fb5c51 100644 --- a/src/ostree/ot-builtin-init.c +++ b/src/ostree/ot-builtin-init.c @@ -38,7 +38,7 @@ static char *opt_collection_id = NULL; */ static GOptionEntry options[] = { - { "mode", 0, 0, G_OPTION_ARG_STRING, &opt_mode, "Initialize repository in given mode (bare, archive)", NULL }, + { "mode", 0, 0, G_OPTION_ARG_STRING, &opt_mode, "Initialize repository in given mode (bare, bare-user, bare-user-only, archive)", NULL }, #ifdef OSTREE_ENABLE_EXPERIMENTAL_API { "collection-id", 0, 0, G_OPTION_ARG_STRING, &opt_collection_id, "Globally unique ID for this repository as an collection of refs for redistribution to other repositories", "COLLECTION-ID" }, diff --git a/src/ostree/ot-builtin-remote.c b/src/ostree/ot-builtin-remote.c index 4deee8c6..0712d5bf 100644 --- a/src/ostree/ot-builtin-remote.c +++ b/src/ostree/ot-builtin-remote.c @@ -73,10 +73,12 @@ remote_option_context_new_with_commands (void) while (subcommand->name != NULL) { - g_string_append_printf (summary, "\n %-18s", subcommand->name); - if (subcommand->description != NULL) - g_string_append_printf (summary, "%s", subcommand->description); - + if ((subcommand->flags & OSTREE_BUILTIN_FLAG_HIDDEN) == 0) + { + g_string_append_printf (summary, "\n %-18s", subcommand->name); + if (subcommand->description != NULL) + g_string_append_printf (summary, "%s", subcommand->description); + } subcommand++; } diff --git a/src/ostree/ot-builtin-static-delta.c b/src/ostree/ot-builtin-static-delta.c index d2a343f2..4f9ff2b2 100644 --- a/src/ostree/ot-builtin-static-delta.c +++ b/src/ostree/ot-builtin-static-delta.c @@ -117,7 +117,8 @@ static_delta_usage (char **argv, while (command->name) { - print_func (" %-17s%s\n", command->name, command->description ?: ""); + if ((command->flags & OSTREE_BUILTIN_FLAG_HIDDEN) == 0) + print_func (" %-17s%s\n", command->name, command->description ?: ""); command++; } diff --git a/src/ostree/ot-main.c b/src/ostree/ot-main.c index 654a5f3d..148c8371 100644 --- a/src/ostree/ot-main.c +++ b/src/ostree/ot-main.c @@ -46,7 +46,7 @@ static GOptionEntry global_entries[] = { }; static GOptionEntry repo_entry[] = { - { "repo", 0, 0, G_OPTION_ARG_FILENAME, &opt_repo, "Path to OSTree repository (defaults to /sysroot/ostree/repo)", "PATH" }, + { "repo", 0, 0, G_OPTION_ARG_FILENAME, &opt_repo, "Path to OSTree repository (defaults to current directory or /sysroot/ostree/repo)", "PATH" }, { NULL } }; @@ -66,10 +66,13 @@ ostree_option_context_new_with_commands (OstreeCommand *commands) while (commands->name != NULL) { - g_string_append_printf (summary, "\n %-18s", commands->name); + if ((commands->flags & OSTREE_BUILTIN_FLAG_HIDDEN) == 0) + { + g_string_append_printf (summary, "\n %-18s", commands->name); - if (commands->description != NULL ) - g_string_append_printf (summary, "%s", commands->description); + if (commands->description != NULL ) + g_string_append_printf (summary, "%s", commands->description); + } commands++; } diff --git a/src/ostree/ot-main.h b/src/ostree/ot-main.h index ac75118c..b1b994b6 100644 --- a/src/ostree/ot-main.h +++ b/src/ostree/ot-main.h @@ -29,7 +29,8 @@ typedef enum { OSTREE_BUILTIN_FLAG_NONE = 0, OSTREE_BUILTIN_FLAG_NO_REPO = 1 << 0, - OSTREE_BUILTIN_FLAG_NO_CHECK = 1 << 1 + OSTREE_BUILTIN_FLAG_NO_CHECK = 1 << 1, + OSTREE_BUILTIN_FLAG_HIDDEN = 1 << 2, } OstreeBuiltinFlags; typedef enum { diff --git a/src/ostree/parse-datetime.c b/src/ostree/parse-datetime.c deleted file mode 100644 index 4e482d13..00000000 --- a/src/ostree/parse-datetime.c +++ /dev/null @@ -1,3328 +0,0 @@ -/* A Bison parser, made by GNU Bison 3.0.4. */ - -/* Bison implementation for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -/* As a special exception, you may create a larger work that contains - part or all of the Bison parser skeleton and distribute that work - under terms of your choice, so long as that work isn't itself a - parser generator using the skeleton or a modified version thereof - as a parser skeleton. Alternatively, if you modify or redistribute - the parser skeleton itself, you may (at your option) remove this - special exception, which will cause the skeleton and the resulting - Bison output files to be licensed under the GNU General Public - License without this special exception. - - This special exception was added by the Free Software Foundation in - version 2.2 of Bison. */ - -/* C LALR(1) parser skeleton written by Richard Stallman, by - simplifying the original so-called "semantic" parser. */ - -/* All symbols defined below should begin with yy or YY, to avoid - infringing on user name space. This should be done even for local - variables, as they might otherwise be expanded by user macros. - There are some unavoidable exceptions within include files to - define necessary library symbols; they are noted "INFRINGES ON - USER NAME SPACE" below. */ - -/* Identify Bison output. */ -#define YYBISON 1 - -/* Bison version. */ -#define YYBISON_VERSION "3.0.4" - -/* Skeleton name. */ -#define YYSKELETON_NAME "yacc.c" - -/* Pure parsers. */ -#define YYPURE 1 - -/* Push parsers. */ -#define YYPUSH 0 - -/* Pull parsers. */ -#define YYPULL 1 - - - - -/* Copy the first part of user declarations. */ -#line 1 "src/ostree/parse-datetime.y" /* yacc.c:339 */ - -/* Parse a string into an internal time stamp. - - Copyright (C) 1999-2000, 2002-2015 Free Software Foundation, Inc. - - This program 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 program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -/* Originally written by Steven M. Bellovin while - at the University of North Carolina at Chapel Hill. Later tweaked by - a couple of people on Usenet. Completely overhauled by Rich $alz - and Jim Berets in August, 1990. - - Modified by Paul Eggert in August 1999 to do - the right thing about local DST. Also modified by Paul Eggert - in February 2004 to support - nanosecond-resolution time stamps, and in October 2004 to support - TZ strings in dates. */ - -/* FIXME: Check for arithmetic overflow in all cases, not just - some of them. */ - - -#include "config.h" -#include "parse-datetime.h" -#include -#include -#include -#include -#include -#include - -/* There's no need to extend the stack, so there's no need to involve - alloca. */ -#define YYSTACK_USE_ALLOCA 0 - -static char * -xmemdup (void const *p, size_t s) -{ - char *result = g_malloc (s); - memcpy (result, p, s); - return result; -} - -static void -gettime (struct timespec *ts) - { -#ifdef HAVE_NANOTIME - nanotime (ts); -#else - -# if defined(CLOCK_REALTIME) && defined(HAVE_CLOCK_GETTIME) - if (clock_gettime (CLOCK_REALTIME, ts) == 0) - return; -# endif - - { - struct timeval tv; - gettimeofday (&tv, NULL); - ts->tv_sec = tv.tv_sec; - ts->tv_nsec = tv.tv_usec * 1000; - } - -#endif - } - -/* Tell Bison how much stack space is needed. 20 should be plenty for - this grammar, which is not right recursive. Beware setting it too - high, since that might cause problems on machines whose - implementations have lame stack-overflow checking. */ -#define YYMAXDEPTH 20 -#define YYINITDEPTH YYMAXDEPTH - -/* Since the code of parse-datetime.y is not included in the Emacs executable - itself, there is no need to #define static in this file. Even if - the code were included in the Emacs executable, it probably - wouldn't do any harm to #undef it here; this will only cause - problems if we try to write to a static variable, which I don't - think this code needs to do. */ -#ifdef emacs -# undef static -#endif - -#include -#include -#include -#include - - -/* Bison's skeleton tests _STDLIB_H, while some stdlib.h headers - use _STDLIB_H_ as witness. Map the latter to the one bison uses. */ -/* FIXME: this is temporary. Remove when we have a mechanism to ensure - that the version we're using is fixed, too. */ -#ifdef _STDLIB_H_ -# undef _STDLIB_H -# define _STDLIB_H 1 -#endif - -/* ISDIGIT differs from isdigit, as follows: - - Its arg may be any int or unsigned int; it need not be an unsigned char - or EOF. - - It's typically faster. - POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to - isdigit unless it's important to use the locale's definition - of "digit" even when the host does not conform to POSIX. */ -#define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9) - -/* Shift A right by B bits portably, by dividing A by 2**B and - truncating towards minus infinity. A and B should be free of side - effects, and B should be in the range 0 <= B <= INT_BITS - 2, where - INT_BITS is the number of useful bits in an int. GNU code can - assume that INT_BITS is at least 32. - - ISO C99 says that A >> B is implementation-defined if A < 0. Some - implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift - right in the usual way when A < 0, so SHR falls back on division if - ordinary A >> B doesn't seem to be the usual signed shift. */ -#define SHR(a, b) \ - (-1 >> 1 == -1 \ - ? (a) >> (b) \ - : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0)) - -#define EPOCH_YEAR 1970 -#define TM_YEAR_BASE 1900 - -#define HOUR(x) ((x) * 60) - -/* Convert a possibly-signed character to an unsigned character. This is - a bit safer than casting to unsigned char, since it catches some type - errors that the cast doesn't. */ -static unsigned char to_uchar (char ch) { return ch; } - -/* FIXME: It also assumes that signed integer overflow silently wraps around, - but this is not true any more with recent versions of GCC 4. */ - -/* An integer value, and the number of digits in its textual - representation. */ -typedef struct -{ - bool negative; - long int value; - size_t digits; -} textint; - -/* An entry in the lexical lookup table. */ -typedef struct -{ - char const *name; - int type; - int value; -} table; - -/* Meridian: am, pm, or 24-hour style. */ -enum { MERam, MERpm, MER24 }; - -enum { BILLION = 1000000000, LOG10_BILLION = 9 }; - -/* Relative times. */ -typedef struct -{ - /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */ - long int year; - long int month; - long int day; - long int hour; - long int minutes; - intmax_t seconds; - int ns; -} relative_time; - -#define RELATIVE_TIME_0 ((relative_time) { 0, 0, 0, 0, 0, 0, 0 }) - -/* Information passed to and from the parser. */ -typedef struct -{ - /* The input string remaining to be parsed. */ - const char *input; - - /* N, if this is the Nth Tuesday. */ - long int day_ordinal; - - /* Day of week; Sunday is 0. */ - int day_number; - - /* tm_isdst flag for the local zone. */ - int local_isdst; - - /* Time zone, in minutes east of UTC. */ - long int time_zone; - - /* Style used for time. */ - int meridian; - - /* Gregorian year, month, day, hour, minutes, seconds, and nanoseconds. */ - textint year; - long int month; - long int day; - long int hour; - long int minutes; - struct timespec seconds; /* includes nanoseconds */ - - /* Relative year, month, day, hour, minutes, seconds, and nanoseconds. */ - relative_time rel; - - /* Presence or counts of nonterminals of various flavors parsed so far. */ - bool timespec_seen; - bool rels_seen; - size_t dates_seen; - size_t days_seen; - size_t local_zones_seen; - size_t dsts_seen; - size_t times_seen; - size_t zones_seen; - - /* Table of local time zone abbreviations, terminated by a null entry. */ - table local_time_zone_table[3]; -} parser_control; - -union YYSTYPE; -static int yylex (union YYSTYPE *, parser_control *); -static int yyerror (parser_control const *, char const *); -static long int time_zone_hhmm (parser_control *, textint, long int); - -/* Extract into *PC any date and time info from a string of digits - of the form e.g., YYYYMMDD, YYMMDD, HHMM, HH (and sometimes YYY, - YYYY, ...). */ -static void -digits_to_date_time (parser_control *pc, textint text_int) -{ - if (pc->dates_seen && ! pc->year.digits - && ! pc->rels_seen && (pc->times_seen || 2 < text_int.digits)) - pc->year = text_int; - else - { - if (4 < text_int.digits) - { - pc->dates_seen++; - pc->day = text_int.value % 100; - pc->month = (text_int.value / 100) % 100; - pc->year.value = text_int.value / 10000; - pc->year.digits = text_int.digits - 4; - } - else - { - pc->times_seen++; - if (text_int.digits <= 2) - { - pc->hour = text_int.value; - pc->minutes = 0; - } - else - { - pc->hour = text_int.value / 100; - pc->minutes = text_int.value % 100; - } - pc->seconds.tv_sec = 0; - pc->seconds.tv_nsec = 0; - pc->meridian = MER24; - } - } -} - -/* Increment PC->rel by FACTOR * REL (FACTOR is 1 or -1). */ -static void -apply_relative_time (parser_control *pc, relative_time rel, int factor) -{ - pc->rel.ns += factor * rel.ns; - pc->rel.seconds += factor * rel.seconds; - pc->rel.minutes += factor * rel.minutes; - pc->rel.hour += factor * rel.hour; - pc->rel.day += factor * rel.day; - pc->rel.month += factor * rel.month; - pc->rel.year += factor * rel.year; - pc->rels_seen = true; -} - -/* Set PC-> hour, minutes, seconds and nanoseconds members from arguments. */ -static void -set_hhmmss (parser_control *pc, long int hour, long int minutes, - time_t sec, long int nsec) -{ - pc->hour = hour; - pc->minutes = minutes; - pc->seconds.tv_sec = sec; - pc->seconds.tv_nsec = nsec; -} - - -#line 365 "src/ostree/parse-datetime.c" /* yacc.c:339 */ - -# ifndef YY_NULLPTR -# if defined __cplusplus && 201103L <= __cplusplus -# define YY_NULLPTR nullptr -# else -# define YY_NULLPTR 0 -# endif -# endif - -/* Enabling verbose error messages. */ -#ifdef YYERROR_VERBOSE -# undef YYERROR_VERBOSE -# define YYERROR_VERBOSE 1 -#else -# define YYERROR_VERBOSE 0 -#endif - - -/* Debug traces. */ -#ifndef YYDEBUG -# define YYDEBUG 0 -#endif -#if YYDEBUG -extern int yydebug; -#endif - -/* Token type. */ -#ifndef YYTOKENTYPE -# define YYTOKENTYPE - enum yytokentype - { - tAGO = 258, - tDST = 259, - tYEAR_UNIT = 260, - tMONTH_UNIT = 261, - tHOUR_UNIT = 262, - tMINUTE_UNIT = 263, - tSEC_UNIT = 264, - tDAY_UNIT = 265, - tDAY_SHIFT = 266, - tDAY = 267, - tDAYZONE = 268, - tLOCAL_ZONE = 269, - tMERIDIAN = 270, - tMONTH = 271, - tORDINAL = 272, - tZONE = 273, - tSNUMBER = 274, - tUNUMBER = 275, - tSDECIMAL_NUMBER = 276, - tUDECIMAL_NUMBER = 277 - }; -#endif -/* Tokens. */ -#define tAGO 258 -#define tDST 259 -#define tYEAR_UNIT 260 -#define tMONTH_UNIT 261 -#define tHOUR_UNIT 262 -#define tMINUTE_UNIT 263 -#define tSEC_UNIT 264 -#define tDAY_UNIT 265 -#define tDAY_SHIFT 266 -#define tDAY 267 -#define tDAYZONE 268 -#define tLOCAL_ZONE 269 -#define tMERIDIAN 270 -#define tMONTH 271 -#define tORDINAL 272 -#define tZONE 273 -#define tSNUMBER 274 -#define tUNUMBER 275 -#define tSDECIMAL_NUMBER 276 -#define tUDECIMAL_NUMBER 277 - -/* Value type. */ -#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED - -union YYSTYPE -{ -#line 310 "src/ostree/parse-datetime.y" /* yacc.c:355 */ - - long int intval; - textint textintval; - struct timespec timespec; - relative_time rel; - -#line 453 "src/ostree/parse-datetime.c" /* yacc.c:355 */ -}; - -typedef union YYSTYPE YYSTYPE; -# define YYSTYPE_IS_TRIVIAL 1 -# define YYSTYPE_IS_DECLARED 1 -#endif - - - -int yyparse (parser_control *pc); - - - -/* Copy the second part of user declarations. */ - -#line 469 "src/ostree/parse-datetime.c" /* yacc.c:358 */ - -#ifdef short -# undef short -#endif - -#ifdef YYTYPE_UINT8 -typedef YYTYPE_UINT8 yytype_uint8; -#else -typedef unsigned char yytype_uint8; -#endif - -#ifdef YYTYPE_INT8 -typedef YYTYPE_INT8 yytype_int8; -#else -typedef signed char yytype_int8; -#endif - -#ifdef YYTYPE_UINT16 -typedef YYTYPE_UINT16 yytype_uint16; -#else -typedef unsigned short int yytype_uint16; -#endif - -#ifdef YYTYPE_INT16 -typedef YYTYPE_INT16 yytype_int16; -#else -typedef short int yytype_int16; -#endif - -#ifndef YYSIZE_T -# ifdef __SIZE_TYPE__ -# define YYSIZE_T __SIZE_TYPE__ -# elif defined size_t -# define YYSIZE_T size_t -# elif ! defined YYSIZE_T -# include /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t -# else -# define YYSIZE_T unsigned int -# endif -#endif - -#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) - -#ifndef YY_ -# if defined YYENABLE_NLS && YYENABLE_NLS -# if ENABLE_NLS -# include /* INFRINGES ON USER NAME SPACE */ -# define YY_(Msgid) dgettext ("bison-runtime", Msgid) -# endif -# endif -# ifndef YY_ -# define YY_(Msgid) Msgid -# endif -#endif - -#ifndef YY_ATTRIBUTE -# if (defined __GNUC__ \ - && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ - || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C -# define YY_ATTRIBUTE(Spec) __attribute__(Spec) -# else -# define YY_ATTRIBUTE(Spec) /* empty */ -# endif -#endif - -#ifndef YY_ATTRIBUTE_PURE -# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) -#endif - -#ifndef YY_ATTRIBUTE_UNUSED -# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) -#endif - -#if !defined _Noreturn \ - && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) -# if defined _MSC_VER && 1200 <= _MSC_VER -# define _Noreturn __declspec (noreturn) -# else -# define _Noreturn YY_ATTRIBUTE ((__noreturn__)) -# endif -#endif - -/* Suppress unused-variable warnings by "using" E. */ -#if ! defined lint || defined __GNUC__ -# define YYUSE(E) ((void) (E)) -#else -# define YYUSE(E) /* empty */ -#endif - -#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ -/* Suppress an incorrect diagnostic about yylval being uninitialized. */ -# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ - _Pragma ("GCC diagnostic push") \ - _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ - _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") -# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ - _Pragma ("GCC diagnostic pop") -#else -# define YY_INITIAL_VALUE(Value) Value -#endif -#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN -# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN -# define YY_IGNORE_MAYBE_UNINITIALIZED_END -#endif -#ifndef YY_INITIAL_VALUE -# define YY_INITIAL_VALUE(Value) /* Nothing. */ -#endif - - -#if ! defined yyoverflow || YYERROR_VERBOSE - -/* The parser invokes alloca or malloc; define the necessary symbols. */ - -# ifdef YYSTACK_USE_ALLOCA -# if YYSTACK_USE_ALLOCA -# ifdef __GNUC__ -# define YYSTACK_ALLOC __builtin_alloca -# elif defined __BUILTIN_VA_ARG_INCR -# include /* INFRINGES ON USER NAME SPACE */ -# elif defined _AIX -# define YYSTACK_ALLOC __alloca -# elif defined _MSC_VER -# include /* INFRINGES ON USER NAME SPACE */ -# define alloca _alloca -# else -# define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS -# include /* INFRINGES ON USER NAME SPACE */ - /* Use EXIT_SUCCESS as a witness for stdlib.h. */ -# ifndef EXIT_SUCCESS -# define EXIT_SUCCESS 0 -# endif -# endif -# endif -# endif -# endif - -# ifdef YYSTACK_ALLOC - /* Pacify GCC's 'empty if-body' warning. */ -# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) -# ifndef YYSTACK_ALLOC_MAXIMUM - /* The OS might guarantee only one guard page at the bottom of the stack, - and a page size can be as small as 4096 bytes. So we cannot safely - invoke alloca (N) if N exceeds 4096. Use a slightly smaller number - to allow for a few compiler-allocated temporary stack slots. */ -# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ -# endif -# else -# define YYSTACK_ALLOC YYMALLOC -# define YYSTACK_FREE YYFREE -# ifndef YYSTACK_ALLOC_MAXIMUM -# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM -# endif -# if (defined __cplusplus && ! defined EXIT_SUCCESS \ - && ! ((defined YYMALLOC || defined malloc) \ - && (defined YYFREE || defined free))) -# include /* INFRINGES ON USER NAME SPACE */ -# ifndef EXIT_SUCCESS -# define EXIT_SUCCESS 0 -# endif -# endif -# ifndef YYMALLOC -# define YYMALLOC malloc -# if ! defined malloc && ! defined EXIT_SUCCESS -void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ -# endif -# endif -# ifndef YYFREE -# define YYFREE free -# if ! defined free && ! defined EXIT_SUCCESS -void free (void *); /* INFRINGES ON USER NAME SPACE */ -# endif -# endif -# endif -#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ - - -#if (! defined yyoverflow \ - && (! defined __cplusplus \ - || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) - -/* A type that is properly aligned for any stack member. */ -union yyalloc -{ - yytype_int16 yyss_alloc; - YYSTYPE yyvs_alloc; -}; - -/* The size of the maximum gap between one aligned stack and the next. */ -# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) - -/* The size of an array large to enough to hold all stacks, each with - N elements. */ -# define YYSTACK_BYTES(N) \ - ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ - + YYSTACK_GAP_MAXIMUM) - -# define YYCOPY_NEEDED 1 - -/* Relocate STACK from its old location to the new one. The - local variables YYSIZE and YYSTACKSIZE give the old and new number of - elements in the stack, and YYPTR gives the new location of the - stack. Advance YYPTR to a properly aligned location for the next - stack. */ -# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ - do \ - { \ - YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ - Stack = &yyptr->Stack_alloc; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / sizeof (*yyptr); \ - } \ - while (0) - -#endif - -#if defined YYCOPY_NEEDED && YYCOPY_NEEDED -/* Copy COUNT objects from SRC to DST. The source and destination do - not overlap. */ -# ifndef YYCOPY -# if defined __GNUC__ && 1 < __GNUC__ -# define YYCOPY(Dst, Src, Count) \ - __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) -# else -# define YYCOPY(Dst, Src, Count) \ - do \ - { \ - YYSIZE_T yyi; \ - for (yyi = 0; yyi < (Count); yyi++) \ - (Dst)[yyi] = (Src)[yyi]; \ - } \ - while (0) -# endif -# endif -#endif /* !YYCOPY_NEEDED */ - -/* YYFINAL -- State number of the termination state. */ -#define YYFINAL 12 -/* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 112 - -/* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 28 -/* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 26 -/* YYNRULES -- Number of rules. */ -#define YYNRULES 91 -/* YYNSTATES -- Number of states. */ -#define YYNSTATES 114 - -/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned - by yylex, with out-of-bounds checking. */ -#define YYUNDEFTOK 2 -#define YYMAXUTOK 277 - -#define YYTRANSLATE(YYX) \ - ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) - -/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM - as returned by yylex, without out-of-bounds checking. */ -static const yytype_uint8 yytranslate[] = -{ - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 26, 2, 2, 27, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 25, 2, - 2, 2, 2, 2, 23, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 24, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22 -}; - -#if YYDEBUG - /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ -static const yytype_uint16 yyrline[] = -{ - 0, 337, 337, 338, 342, 349, 351, 355, 357, 359, - 361, 363, 365, 367, 368, 369, 373, 377, 381, 386, - 391, 396, 400, 405, 410, 417, 419, 423, 431, 436, - 446, 448, 450, 453, 456, 458, 460, 465, 470, 475, - 480, 488, 493, 513, 521, 529, 534, 540, 545, 551, - 555, 565, 567, 569, 574, 576, 578, 580, 582, 584, - 586, 588, 590, 592, 594, 596, 598, 600, 602, 604, - 606, 608, 610, 612, 614, 618, 620, 622, 624, 626, - 628, 633, 637, 637, 640, 641, 646, 647, 652, 657, - 668, 669 -}; -#endif - -#if YYDEBUG || YYERROR_VERBOSE || 0 -/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. - First, the terminals, then, starting at YYNTOKENS, nonterminals. */ -static const char *const yytname[] = -{ - "$end", "error", "$undefined", "tAGO", "tDST", "tYEAR_UNIT", - "tMONTH_UNIT", "tHOUR_UNIT", "tMINUTE_UNIT", "tSEC_UNIT", "tDAY_UNIT", - "tDAY_SHIFT", "tDAY", "tDAYZONE", "tLOCAL_ZONE", "tMERIDIAN", "tMONTH", - "tORDINAL", "tZONE", "tSNUMBER", "tUNUMBER", "tSDECIMAL_NUMBER", - "tUDECIMAL_NUMBER", "'@'", "'T'", "':'", "','", "'/'", "$accept", "spec", - "timespec", "items", "item", "datetime", "iso_8601_datetime", "time", - "iso_8601_time", "o_zone_offset", "zone_offset", "local_zone", "zone", - "day", "date", "iso_8601_date", "rel", "relunit", "relunit_snumber", - "dayshift", "seconds", "signed_seconds", "unsigned_seconds", "number", - "hybrid", "o_colon_minutes", YY_NULLPTR -}; -#endif - -# ifdef YYPRINT -/* YYTOKNUM[NUM] -- (External) token number corresponding to the - (internal) symbol number NUM (which must be that of a token). */ -static const yytype_uint16 yytoknum[] = -{ - 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, - 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, - 275, 276, 277, 64, 84, 58, 44, 47 -}; -# endif - -#define YYPACT_NINF -93 - -#define yypact_value_is_default(Yystate) \ - (!!((Yystate) == (-93))) - -#define YYTABLE_NINF -1 - -#define yytable_value_is_error(Yytable_value) \ - 0 - - /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing - STATE-NUM. */ -static const yytype_int8 yypact[] = -{ - 38, 27, 77, -93, 46, -93, -93, -93, -93, -93, - -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, - 62, -93, 82, -3, 66, 3, 74, -4, 83, 84, - 75, -93, -93, -93, -93, -93, -93, -93, -93, -93, - 71, -93, 93, -93, -93, -93, -93, -93, -93, 78, - 72, -93, -93, -93, -93, -93, -93, -93, -93, 25, - -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, - -93, -93, -93, -93, -93, 21, 19, 79, 80, -93, - -93, -93, -93, -93, 81, -93, -93, 85, 86, -93, - -93, -93, -93, -93, -6, 76, 17, -93, -93, -93, - -93, 87, 69, -93, -93, 88, 89, -1, -93, 18, - -93, -93, 69, 91 -}; - - /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. - Performed when YYTABLE does not specify something else to do. Zero - means the default is an error. */ -static const yytype_uint8 yydefact[] = -{ - 5, 0, 0, 2, 3, 85, 87, 84, 86, 4, - 82, 83, 1, 56, 59, 65, 68, 73, 62, 81, - 37, 35, 28, 0, 0, 30, 0, 88, 0, 0, - 31, 6, 7, 16, 8, 21, 9, 10, 12, 11, - 49, 13, 52, 74, 53, 14, 15, 38, 29, 0, - 45, 54, 57, 63, 66, 69, 60, 39, 36, 90, - 32, 75, 76, 78, 79, 80, 77, 55, 58, 64, - 67, 70, 61, 40, 18, 47, 90, 0, 0, 22, - 89, 71, 72, 33, 0, 51, 44, 0, 0, 34, - 43, 48, 50, 27, 25, 41, 0, 17, 46, 91, - 19, 90, 0, 23, 26, 0, 0, 25, 42, 25, - 20, 24, 0, 25 -}; - - /* YYPGOTO[NTERM-NUM]. */ -static const yytype_int8 yypgoto[] = -{ - -93, -93, -93, -93, -93, -93, -93, -93, 20, -68, - -27, -93, -93, -93, -93, -93, -93, -93, 60, -93, - -93, -93, -92, -93, -93, 43 -}; - - /* YYDEFGOTO[NTERM-NUM]. */ -static const yytype_int8 yydefgoto[] = -{ - -1, 2, 3, 4, 31, 32, 33, 34, 35, 103, - 104, 36, 37, 38, 39, 40, 41, 42, 43, 44, - 9, 10, 11, 45, 46, 93 -}; - - /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If - positive, shift that token. If negative, reduce the rule whose - number is the opposite. If YYTABLE_NINF, syntax error. */ -static const yytype_uint8 yytable[] = -{ - 79, 67, 68, 69, 70, 71, 72, 58, 73, 100, - 107, 74, 75, 101, 110, 76, 49, 50, 101, 102, - 113, 77, 59, 78, 61, 62, 63, 64, 65, 66, - 61, 62, 63, 64, 65, 66, 101, 101, 92, 111, - 90, 91, 106, 112, 88, 111, 5, 6, 7, 8, - 88, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 1, 23, 24, 25, 26, 27, 28, 29, 79, - 30, 51, 52, 53, 54, 55, 56, 12, 57, 61, - 62, 63, 64, 65, 66, 60, 48, 80, 47, 6, - 83, 8, 81, 82, 26, 84, 85, 86, 87, 94, - 95, 96, 89, 105, 97, 98, 99, 0, 108, 109, - 101, 0, 88 -}; - -static const yytype_int8 yycheck[] = -{ - 27, 5, 6, 7, 8, 9, 10, 4, 12, 15, - 102, 15, 16, 19, 15, 19, 19, 20, 19, 25, - 112, 25, 19, 27, 5, 6, 7, 8, 9, 10, - 5, 6, 7, 8, 9, 10, 19, 19, 19, 107, - 19, 20, 25, 25, 25, 113, 19, 20, 21, 22, - 25, 5, 6, 7, 8, 9, 10, 11, 12, 13, - 14, 23, 16, 17, 18, 19, 20, 21, 22, 96, - 24, 5, 6, 7, 8, 9, 10, 0, 12, 5, - 6, 7, 8, 9, 10, 25, 4, 27, 26, 20, - 30, 22, 9, 9, 19, 24, 3, 19, 26, 20, - 20, 20, 59, 27, 84, 20, 20, -1, 20, 20, - 19, -1, 25 -}; - - /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing - symbol of state STATE-NUM. */ -static const yytype_uint8 yystos[] = -{ - 0, 23, 29, 30, 31, 19, 20, 21, 22, 48, - 49, 50, 0, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, - 24, 32, 33, 34, 35, 36, 39, 40, 41, 42, - 43, 44, 45, 46, 47, 51, 52, 26, 4, 19, - 20, 5, 6, 7, 8, 9, 10, 12, 4, 19, - 46, 5, 6, 7, 8, 9, 10, 5, 6, 7, - 8, 9, 10, 12, 15, 16, 19, 25, 27, 38, - 46, 9, 9, 46, 24, 3, 19, 26, 25, 53, - 19, 20, 19, 53, 20, 20, 20, 36, 20, 20, - 15, 19, 25, 37, 38, 27, 25, 50, 20, 20, - 15, 37, 25, 50 -}; - - /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ -static const yytype_uint8 yyr1[] = -{ - 0, 28, 29, 29, 30, 31, 31, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 33, 34, 35, 35, - 35, 35, 36, 36, 36, 37, 37, 38, 39, 39, - 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, - 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 43, 44, 44, 44, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, - 46, 47, 48, 48, 49, 49, 50, 50, 51, 52, - 53, 53 -}; - - /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ -static const yytype_uint8 yyr2[] = -{ - 0, 2, 1, 1, 2, 0, 2, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 3, 2, 4, - 6, 1, 2, 4, 6, 0, 1, 2, 1, 2, - 1, 1, 2, 2, 3, 1, 2, 1, 2, 2, - 2, 3, 5, 3, 3, 2, 4, 2, 3, 1, - 3, 2, 1, 1, 2, 2, 1, 2, 2, 1, - 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, - 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, - 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, - 0, 2 -}; - - -#define yyerrok (yyerrstatus = 0) -#define yyclearin (yychar = YYEMPTY) -#define YYEMPTY (-2) -#define YYEOF 0 - -#define YYACCEPT goto yyacceptlab -#define YYABORT goto yyabortlab -#define YYERROR goto yyerrorlab - - -#define YYRECOVERING() (!!yyerrstatus) - -#define YYBACKUP(Token, Value) \ -do \ - if (yychar == YYEMPTY) \ - { \ - yychar = (Token); \ - yylval = (Value); \ - YYPOPSTACK (yylen); \ - yystate = *yyssp; \ - goto yybackup; \ - } \ - else \ - { \ - yyerror (pc, YY_("syntax error: cannot back up")); \ - YYERROR; \ - } \ -while (0) - -/* Error token number */ -#define YYTERROR 1 -#define YYERRCODE 256 - - - -/* Enable debugging if requested. */ -#if YYDEBUG - -# ifndef YYFPRINTF -# include /* INFRINGES ON USER NAME SPACE */ -# define YYFPRINTF fprintf -# endif - -# define YYDPRINTF(Args) \ -do { \ - if (yydebug) \ - YYFPRINTF Args; \ -} while (0) - -/* This macro is provided for backward compatibility. */ -#ifndef YY_LOCATION_PRINT -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) -#endif - - -# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ -do { \ - if (yydebug) \ - { \ - YYFPRINTF (stderr, "%s ", Title); \ - yy_symbol_print (stderr, \ - Type, Value, pc); \ - YYFPRINTF (stderr, "\n"); \ - } \ -} while (0) - - -/*----------------------------------------. -| Print this symbol's value on YYOUTPUT. | -`----------------------------------------*/ - -static void -yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, parser_control *pc) -{ - FILE *yyo = yyoutput; - YYUSE (yyo); - YYUSE (pc); - if (!yyvaluep) - return; -# ifdef YYPRINT - if (yytype < YYNTOKENS) - YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); -# endif - YYUSE (yytype); -} - - -/*--------------------------------. -| Print this symbol on YYOUTPUT. | -`--------------------------------*/ - -static void -yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, parser_control *pc) -{ - YYFPRINTF (yyoutput, "%s %s (", - yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); - - yy_symbol_value_print (yyoutput, yytype, yyvaluep, pc); - YYFPRINTF (yyoutput, ")"); -} - -/*------------------------------------------------------------------. -| yy_stack_print -- Print the state stack from its BOTTOM up to its | -| TOP (included). | -`------------------------------------------------------------------*/ - -static void -yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) -{ - YYFPRINTF (stderr, "Stack now"); - for (; yybottom <= yytop; yybottom++) - { - int yybot = *yybottom; - YYFPRINTF (stderr, " %d", yybot); - } - YYFPRINTF (stderr, "\n"); -} - -# define YY_STACK_PRINT(Bottom, Top) \ -do { \ - if (yydebug) \ - yy_stack_print ((Bottom), (Top)); \ -} while (0) - - -/*------------------------------------------------. -| Report that the YYRULE is going to be reduced. | -`------------------------------------------------*/ - -static void -yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, parser_control *pc) -{ - unsigned long int yylno = yyrline[yyrule]; - int yynrhs = yyr2[yyrule]; - int yyi; - YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", - yyrule - 1, yylno); - /* The symbols being reduced. */ - for (yyi = 0; yyi < yynrhs; yyi++) - { - YYFPRINTF (stderr, " $%d = ", yyi + 1); - yy_symbol_print (stderr, - yystos[yyssp[yyi + 1 - yynrhs]], - &(yyvsp[(yyi + 1) - (yynrhs)]) - , pc); - YYFPRINTF (stderr, "\n"); - } -} - -# define YY_REDUCE_PRINT(Rule) \ -do { \ - if (yydebug) \ - yy_reduce_print (yyssp, yyvsp, Rule, pc); \ -} while (0) - -/* Nonzero means print parse trace. It is left uninitialized so that - multiple parsers can coexist. */ -int yydebug; -#else /* !YYDEBUG */ -# define YYDPRINTF(Args) -# define YY_SYMBOL_PRINT(Title, Type, Value, Location) -# define YY_STACK_PRINT(Bottom, Top) -# define YY_REDUCE_PRINT(Rule) -#endif /* !YYDEBUG */ - - -/* YYINITDEPTH -- initial size of the parser's stacks. */ -#ifndef YYINITDEPTH -# define YYINITDEPTH 200 -#endif - -/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only - if the built-in stack extension method is used). - - Do not make this value too large; the results are undefined if - YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) - evaluated with infinite-precision integer arithmetic. */ - -#ifndef YYMAXDEPTH -# define YYMAXDEPTH 10000 -#endif - - -#if YYERROR_VERBOSE - -# ifndef yystrlen -# if defined __GLIBC__ && defined _STRING_H -# define yystrlen strlen -# else -/* Return the length of YYSTR. */ -static YYSIZE_T -yystrlen (const char *yystr) -{ - YYSIZE_T yylen; - for (yylen = 0; yystr[yylen]; yylen++) - continue; - return yylen; -} -# endif -# endif - -# ifndef yystpcpy -# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE -# define yystpcpy stpcpy -# else -/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in - YYDEST. */ -static char * -yystpcpy (char *yydest, const char *yysrc) -{ - char *yyd = yydest; - const char *yys = yysrc; - - while ((*yyd++ = *yys++) != '\0') - continue; - - return yyd - 1; -} -# endif -# endif - -# ifndef yytnamerr -/* Copy to YYRES the contents of YYSTR after stripping away unnecessary - quotes and backslashes, so that it's suitable for yyerror. The - heuristic is that double-quoting is unnecessary unless the string - contains an apostrophe, a comma, or backslash (other than - backslash-backslash). YYSTR is taken from yytname. If YYRES is - null, do not copy; instead, return the length of what the result - would have been. */ -static YYSIZE_T -yytnamerr (char *yyres, const char *yystr) -{ - if (*yystr == '"') - { - YYSIZE_T yyn = 0; - char const *yyp = yystr; - - for (;;) - switch (*++yyp) - { - case '\'': - case ',': - goto do_not_strip_quotes; - - case '\\': - if (*++yyp != '\\') - goto do_not_strip_quotes; - /* Fall through. */ - default: - if (yyres) - yyres[yyn] = *yyp; - yyn++; - break; - - case '"': - if (yyres) - yyres[yyn] = '\0'; - return yyn; - } - do_not_strip_quotes: ; - } - - if (! yyres) - return yystrlen (yystr); - - return yystpcpy (yyres, yystr) - yyres; -} -# endif - -/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message - about the unexpected token YYTOKEN for the state stack whose top is - YYSSP. - - Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is - not large enough to hold the message. In that case, also set - *YYMSG_ALLOC to the required number of bytes. Return 2 if the - required number of bytes is too large to store. */ -static int -yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, - yytype_int16 *yyssp, int yytoken) -{ - YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); - YYSIZE_T yysize = yysize0; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - /* Internationalized format string. */ - const char *yyformat = YY_NULLPTR; - /* Arguments of yyformat. */ - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - /* Number of reported tokens (one for the "unexpected", one per - "expected"). */ - int yycount = 0; - - /* There are many possibilities here to consider: - - If this state is a consistent state with a default action, then - the only way this function was invoked is if the default action - is an error action. In that case, don't check for expected - tokens because there are none. - - The only way there can be no lookahead present (in yychar) is if - this state is a consistent state with a default action. Thus, - detecting the absence of a lookahead is sufficient to determine - that there is no unexpected or expected token to report. In that - case, just report a simple "syntax error". - - Don't assume there isn't a lookahead just because this state is a - consistent state with a default action. There might have been a - previous inconsistent state, consistent state with a non-default - action, or user semantic action that manipulated yychar. - - Of course, the expected token list depends on states to have - correct lookahead information, and it depends on the parser not - to perform extra reductions after fetching a lookahead from the - scanner and before detecting a syntax error. Thus, state merging - (from LALR or IELR) and default reductions corrupt the expected - token list. However, the list is correct for canonical LR with - one exception: it will still contain any token that will not be - accepted due to an error action in a later state. - */ - if (yytoken != YYEMPTY) - { - int yyn = yypact[*yyssp]; - yyarg[yycount++] = yytname[yytoken]; - if (!yypact_value_is_default (yyn)) - { - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. In other words, skip the first -YYN actions for - this state because they are default actions. */ - int yyxbegin = yyn < 0 ? -yyn : 0; - /* Stay within bounds of both yycheck and yytname. */ - int yychecklim = YYLAST - yyn + 1; - int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yyx; - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR - && !yytable_value_is_error (yytable[yyx + yyn])) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - break; - } - yyarg[yycount++] = yytname[yyx]; - { - YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); - if (! (yysize <= yysize1 - && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) - return 2; - yysize = yysize1; - } - } - } - } - - switch (yycount) - { -# define YYCASE_(N, S) \ - case N: \ - yyformat = S; \ - break - YYCASE_(0, YY_("syntax error")); - YYCASE_(1, YY_("syntax error, unexpected %s")); - YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); - YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); - YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); - YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); -# undef YYCASE_ - } - - { - YYSIZE_T yysize1 = yysize + yystrlen (yyformat); - if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) - return 2; - yysize = yysize1; - } - - if (*yymsg_alloc < yysize) - { - *yymsg_alloc = 2 * yysize; - if (! (yysize <= *yymsg_alloc - && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) - *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; - return 1; - } - - /* Avoid sprintf, as that infringes on the user's name space. - Don't have undefined behavior even if the translation - produced a string with the wrong number of "%s"s. */ - { - char *yyp = *yymsg; - int yyi = 0; - while ((*yyp = *yyformat) != '\0') - if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyformat += 2; - } - else - { - yyp++; - yyformat++; - } - } - return 0; -} -#endif /* YYERROR_VERBOSE */ - -/*-----------------------------------------------. -| Release the memory associated to this symbol. | -`-----------------------------------------------*/ - -static void -yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, parser_control *pc) -{ - YYUSE (yyvaluep); - YYUSE (pc); - if (!yymsg) - yymsg = "Deleting"; - YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); - - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - YYUSE (yytype); - YY_IGNORE_MAYBE_UNINITIALIZED_END -} - - - - -/*----------. -| yyparse. | -`----------*/ - -int -yyparse (parser_control *pc) -{ -/* The lookahead symbol. */ -int yychar; - - -/* The semantic value of the lookahead symbol. */ -/* Default value used for initialization, for pacifying older GCCs - or non-GCC compilers. */ -YY_INITIAL_VALUE (static YYSTYPE yyval_default;) -YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); - - /* Number of syntax errors so far. */ - int yynerrs; - - int yystate; - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; - - /* The stacks and their tools: - 'yyss': related to states. - 'yyvs': related to semantic values. - - Refer to the stacks through separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* The state stack. */ - yytype_int16 yyssa[YYINITDEPTH]; - yytype_int16 *yyss; - yytype_int16 *yyssp; - - /* The semantic value stack. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs; - YYSTYPE *yyvsp; - - YYSIZE_T yystacksize; - - int yyn; - int yyresult; - /* Lookahead token as an internal (translated) token number. */ - int yytoken = 0; - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; - -#if YYERROR_VERBOSE - /* Buffer for error messages, and its allocated size. */ - char yymsgbuf[128]; - char *yymsg = yymsgbuf; - YYSIZE_T yymsg_alloc = sizeof yymsgbuf; -#endif - -#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) - - /* The number of symbols on the RHS of the reduced rule. - Keep to zero when no symbol should be popped. */ - int yylen = 0; - - yyssp = yyss = yyssa; - yyvsp = yyvs = yyvsa; - yystacksize = YYINITDEPTH; - - YYDPRINTF ((stderr, "Starting parse\n")); - - yystate = 0; - yyerrstatus = 0; - yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ - goto yysetstate; - -/*------------------------------------------------------------. -| yynewstate -- Push a new state, which is found in yystate. | -`------------------------------------------------------------*/ - yynewstate: - /* In all cases, when you get here, the value and location stacks - have just been pushed. So pushing a state here evens the stacks. */ - yyssp++; - - yysetstate: - *yyssp = yystate; - - if (yyss + yystacksize - 1 <= yyssp) - { - /* Get the current used size of the three stacks, in elements. */ - YYSIZE_T yysize = yyssp - yyss + 1; - -#ifdef yyoverflow - { - /* Give user a chance to reallocate the stack. Use copies of - these so that the &'s don't force the real ones into - memory. */ - YYSTYPE *yyvs1 = yyvs; - yytype_int16 *yyss1 = yyss; - - /* Each stack pointer address is followed by the size of the - data in use in that stack, in bytes. This used to be a - conditional around just the two extra args, but that might - be undefined if yyoverflow is a macro. */ - yyoverflow (YY_("memory exhausted"), - &yyss1, yysize * sizeof (*yyssp), - &yyvs1, yysize * sizeof (*yyvsp), - &yystacksize); - - yyss = yyss1; - yyvs = yyvs1; - } -#else /* no yyoverflow */ -# ifndef YYSTACK_RELOCATE - goto yyexhaustedlab; -# else - /* Extend the stack our own way. */ - if (YYMAXDEPTH <= yystacksize) - goto yyexhaustedlab; - yystacksize *= 2; - if (YYMAXDEPTH < yystacksize) - yystacksize = YYMAXDEPTH; - - { - yytype_int16 *yyss1 = yyss; - union yyalloc *yyptr = - (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); - if (! yyptr) - goto yyexhaustedlab; - YYSTACK_RELOCATE (yyss_alloc, yyss); - YYSTACK_RELOCATE (yyvs_alloc, yyvs); -# undef YYSTACK_RELOCATE - if (yyss1 != yyssa) - YYSTACK_FREE (yyss1); - } -# endif -#endif /* no yyoverflow */ - - yyssp = yyss + yysize - 1; - yyvsp = yyvs + yysize - 1; - - YYDPRINTF ((stderr, "Stack size increased to %lu\n", - (unsigned long int) yystacksize)); - - if (yyss + yystacksize - 1 <= yyssp) - YYABORT; - } - - YYDPRINTF ((stderr, "Entering state %d\n", yystate)); - - if (yystate == YYFINAL) - YYACCEPT; - - goto yybackup; - -/*-----------. -| yybackup. | -`-----------*/ -yybackup: - - /* Do appropriate processing given the current state. Read a - lookahead token if we need one and don't already have one. */ - - /* First try to decide what to do without reference to lookahead token. */ - yyn = yypact[yystate]; - if (yypact_value_is_default (yyn)) - goto yydefault; - - /* Not known => get a lookahead token if don't already have one. */ - - /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ - if (yychar == YYEMPTY) - { - YYDPRINTF ((stderr, "Reading a token: ")); - yychar = yylex (&yylval, pc); - } - - if (yychar <= YYEOF) - { - yychar = yytoken = YYEOF; - YYDPRINTF ((stderr, "Now at end of input.\n")); - } - else - { - yytoken = YYTRANSLATE (yychar); - YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); - } - - /* If the proper action on seeing token YYTOKEN is to reduce or to - detect an error, take that action. */ - yyn += yytoken; - if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) - goto yydefault; - yyn = yytable[yyn]; - if (yyn <= 0) - { - if (yytable_value_is_error (yyn)) - goto yyerrlab; - yyn = -yyn; - goto yyreduce; - } - - /* Count tokens shifted since error; after three, turn off error - status. */ - if (yyerrstatus) - yyerrstatus--; - - /* Shift the lookahead token. */ - YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); - - /* Discard the shifted token. */ - yychar = YYEMPTY; - - yystate = yyn; - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - *++yyvsp = yylval; - YY_IGNORE_MAYBE_UNINITIALIZED_END - - goto yynewstate; - - -/*-----------------------------------------------------------. -| yydefault -- do the default action for the current state. | -`-----------------------------------------------------------*/ -yydefault: - yyn = yydefact[yystate]; - if (yyn == 0) - goto yyerrlab; - goto yyreduce; - - -/*-----------------------------. -| yyreduce -- Do a reduction. | -`-----------------------------*/ -yyreduce: - /* yyn is the number of a rule to reduce with. */ - yylen = yyr2[yyn]; - - /* If YYLEN is nonzero, implement the default value of the action: - '$$ = $1'. - - Otherwise, the following line sets YYVAL to garbage. - This behavior is undocumented and Bison - users should not rely upon it. Assigning to YYVAL - unconditionally makes the parser a bit smaller, and it avoids a - GCC warning that YYVAL may be used uninitialized. */ - yyval = yyvsp[1-yylen]; - - - YY_REDUCE_PRINT (yyn); - switch (yyn) - { - case 4: -#line 343 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - pc->seconds = (yyvsp[0].timespec); - pc->timespec_seen = true; - } -#line 1641 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 7: -#line 356 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { pc->times_seen++; pc->dates_seen++; } -#line 1647 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 8: -#line 358 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { pc->times_seen++; } -#line 1653 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 9: -#line 360 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { pc->local_zones_seen++; } -#line 1659 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 10: -#line 362 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { pc->zones_seen++; } -#line 1665 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 11: -#line 364 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { pc->dates_seen++; } -#line 1671 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 12: -#line 366 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { pc->days_seen++; } -#line 1677 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 18: -#line 382 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - set_hhmmss (pc, (yyvsp[-1].textintval).value, 0, 0, 0); - pc->meridian = (yyvsp[0].intval); - } -#line 1686 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 19: -#line 387 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - set_hhmmss (pc, (yyvsp[-3].textintval).value, (yyvsp[-1].textintval).value, 0, 0); - pc->meridian = (yyvsp[0].intval); - } -#line 1695 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 20: -#line 392 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - set_hhmmss (pc, (yyvsp[-5].textintval).value, (yyvsp[-3].textintval).value, (yyvsp[-1].timespec).tv_sec, (yyvsp[-1].timespec).tv_nsec); - pc->meridian = (yyvsp[0].intval); - } -#line 1704 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 22: -#line 401 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - set_hhmmss (pc, (yyvsp[-1].textintval).value, 0, 0, 0); - pc->meridian = MER24; - } -#line 1713 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 23: -#line 406 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - set_hhmmss (pc, (yyvsp[-3].textintval).value, (yyvsp[-1].textintval).value, 0, 0); - pc->meridian = MER24; - } -#line 1722 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 24: -#line 411 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - set_hhmmss (pc, (yyvsp[-5].textintval).value, (yyvsp[-3].textintval).value, (yyvsp[-1].timespec).tv_sec, (yyvsp[-1].timespec).tv_nsec); - pc->meridian = MER24; - } -#line 1731 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 27: -#line 424 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - pc->zones_seen++; - pc->time_zone = time_zone_hhmm (pc, (yyvsp[-1].textintval), (yyvsp[0].intval)); - } -#line 1740 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 28: -#line 432 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - pc->local_isdst = (yyvsp[0].intval); - pc->dsts_seen += (0 < (yyvsp[0].intval)); - } -#line 1749 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 29: -#line 437 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - pc->local_isdst = 1; - pc->dsts_seen += (0 < (yyvsp[-1].intval)) + 1; - } -#line 1758 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 30: -#line 447 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { pc->time_zone = (yyvsp[0].intval); } -#line 1764 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 31: -#line 449 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { pc->time_zone = HOUR(7); } -#line 1770 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 32: -#line 451 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { pc->time_zone = (yyvsp[-1].intval); - apply_relative_time (pc, (yyvsp[0].rel), 1); } -#line 1777 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 33: -#line 454 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { pc->time_zone = HOUR(7); - apply_relative_time (pc, (yyvsp[0].rel), 1); } -#line 1784 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 34: -#line 457 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { pc->time_zone = (yyvsp[-2].intval) + time_zone_hhmm (pc, (yyvsp[-1].textintval), (yyvsp[0].intval)); } -#line 1790 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 35: -#line 459 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { pc->time_zone = (yyvsp[0].intval) + 60; } -#line 1796 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 36: -#line 461 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { pc->time_zone = (yyvsp[-1].intval) + 60; } -#line 1802 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 37: -#line 466 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - pc->day_ordinal = 0; - pc->day_number = (yyvsp[0].intval); - } -#line 1811 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 38: -#line 471 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - pc->day_ordinal = 0; - pc->day_number = (yyvsp[-1].intval); - } -#line 1820 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 39: -#line 476 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - pc->day_ordinal = (yyvsp[-1].intval); - pc->day_number = (yyvsp[0].intval); - } -#line 1829 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 40: -#line 481 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - pc->day_ordinal = (yyvsp[-1].textintval).value; - pc->day_number = (yyvsp[0].intval); - } -#line 1838 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 41: -#line 489 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - pc->month = (yyvsp[-2].textintval).value; - pc->day = (yyvsp[0].textintval).value; - } -#line 1847 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 42: -#line 494 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - /* Interpret as YYYY/MM/DD if the first value has 4 or more digits, - otherwise as MM/DD/YY. - The goal in recognizing YYYY/MM/DD is solely to support legacy - machine-generated dates like those in an RCS log listing. If - you want portability, use the ISO 8601 format. */ - if (4 <= (yyvsp[-4].textintval).digits) - { - pc->year = (yyvsp[-4].textintval); - pc->month = (yyvsp[-2].textintval).value; - pc->day = (yyvsp[0].textintval).value; - } - else - { - pc->month = (yyvsp[-4].textintval).value; - pc->day = (yyvsp[-2].textintval).value; - pc->year = (yyvsp[0].textintval); - } - } -#line 1871 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 43: -#line 514 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - /* e.g. 17-JUN-1992. */ - pc->day = (yyvsp[-2].textintval).value; - pc->month = (yyvsp[-1].intval); - pc->year.value = -(yyvsp[0].textintval).value; - pc->year.digits = (yyvsp[0].textintval).digits; - } -#line 1883 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 44: -#line 522 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - /* e.g. JUN-17-1992. */ - pc->month = (yyvsp[-2].intval); - pc->day = -(yyvsp[-1].textintval).value; - pc->year.value = -(yyvsp[0].textintval).value; - pc->year.digits = (yyvsp[0].textintval).digits; - } -#line 1895 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 45: -#line 530 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - pc->month = (yyvsp[-1].intval); - pc->day = (yyvsp[0].textintval).value; - } -#line 1904 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 46: -#line 535 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - pc->month = (yyvsp[-3].intval); - pc->day = (yyvsp[-2].textintval).value; - pc->year = (yyvsp[0].textintval); - } -#line 1914 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 47: -#line 541 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - pc->day = (yyvsp[-1].textintval).value; - pc->month = (yyvsp[0].intval); - } -#line 1923 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 48: -#line 546 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - pc->day = (yyvsp[-2].textintval).value; - pc->month = (yyvsp[-1].intval); - pc->year = (yyvsp[0].textintval); - } -#line 1933 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 50: -#line 556 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - /* ISO 8601 format. YYYY-MM-DD. */ - pc->year = (yyvsp[-2].textintval); - pc->month = -(yyvsp[-1].textintval).value; - pc->day = -(yyvsp[0].textintval).value; - } -#line 1944 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 51: -#line 566 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { apply_relative_time (pc, (yyvsp[-1].rel), (yyvsp[0].intval)); } -#line 1950 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 52: -#line 568 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { apply_relative_time (pc, (yyvsp[0].rel), 1); } -#line 1956 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 53: -#line 570 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { apply_relative_time (pc, (yyvsp[0].rel), 1); } -#line 1962 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 54: -#line 575 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).year = (yyvsp[-1].intval); } -#line 1968 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 55: -#line 577 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).year = (yyvsp[-1].textintval).value; } -#line 1974 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 56: -#line 579 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).year = 1; } -#line 1980 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 57: -#line 581 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).month = (yyvsp[-1].intval); } -#line 1986 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 58: -#line 583 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).month = (yyvsp[-1].textintval).value; } -#line 1992 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 59: -#line 585 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).month = 1; } -#line 1998 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 60: -#line 587 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).day = (yyvsp[-1].intval) * (yyvsp[0].intval); } -#line 2004 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 61: -#line 589 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).day = (yyvsp[-1].textintval).value * (yyvsp[0].intval); } -#line 2010 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 62: -#line 591 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).day = (yyvsp[0].intval); } -#line 2016 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 63: -#line 593 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).hour = (yyvsp[-1].intval); } -#line 2022 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 64: -#line 595 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).hour = (yyvsp[-1].textintval).value; } -#line 2028 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 65: -#line 597 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).hour = 1; } -#line 2034 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 66: -#line 599 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).minutes = (yyvsp[-1].intval); } -#line 2040 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 67: -#line 601 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).minutes = (yyvsp[-1].textintval).value; } -#line 2046 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 68: -#line 603 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).minutes = 1; } -#line 2052 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 69: -#line 605 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).seconds = (yyvsp[-1].intval); } -#line 2058 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 70: -#line 607 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).seconds = (yyvsp[-1].textintval).value; } -#line 2064 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 71: -#line 609 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).seconds = (yyvsp[-1].timespec).tv_sec; (yyval.rel).ns = (yyvsp[-1].timespec).tv_nsec; } -#line 2070 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 72: -#line 611 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).seconds = (yyvsp[-1].timespec).tv_sec; (yyval.rel).ns = (yyvsp[-1].timespec).tv_nsec; } -#line 2076 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 73: -#line 613 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).seconds = 1; } -#line 2082 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 75: -#line 619 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).year = (yyvsp[-1].textintval).value; } -#line 2088 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 76: -#line 621 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).month = (yyvsp[-1].textintval).value; } -#line 2094 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 77: -#line 623 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).day = (yyvsp[-1].textintval).value * (yyvsp[0].intval); } -#line 2100 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 78: -#line 625 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).hour = (yyvsp[-1].textintval).value; } -#line 2106 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 79: -#line 627 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).minutes = (yyvsp[-1].textintval).value; } -#line 2112 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 80: -#line 629 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).seconds = (yyvsp[-1].textintval).value; } -#line 2118 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 81: -#line 634 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.rel) = RELATIVE_TIME_0; (yyval.rel).day = (yyvsp[0].intval); } -#line 2124 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 85: -#line 642 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.timespec).tv_sec = (yyvsp[0].textintval).value; (yyval.timespec).tv_nsec = 0; } -#line 2130 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 87: -#line 648 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.timespec).tv_sec = (yyvsp[0].textintval).value; (yyval.timespec).tv_nsec = 0; } -#line 2136 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 88: -#line 653 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { digits_to_date_time (pc, (yyvsp[0].textintval)); } -#line 2142 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 89: -#line 658 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { - /* Hybrid all-digit and relative offset, so that we accept e.g., - "YYYYMMDD +N days" as well as "YYYYMMDD N days". */ - digits_to_date_time (pc, (yyvsp[-1].textintval)); - apply_relative_time (pc, (yyvsp[0].rel), 1); - } -#line 2153 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 90: -#line 668 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.intval) = -1; } -#line 2159 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - case 91: -#line 670 "src/ostree/parse-datetime.y" /* yacc.c:1646 */ - { (yyval.intval) = (yyvsp[0].textintval).value; } -#line 2165 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - break; - - -#line 2169 "src/ostree/parse-datetime.c" /* yacc.c:1646 */ - default: break; - } - /* User semantic actions sometimes alter yychar, and that requires - that yytoken be updated with the new translation. We take the - approach of translating immediately before every use of yytoken. - One alternative is translating here after every semantic action, - but that translation would be missed if the semantic action invokes - YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or - if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an - incorrect destructor might then be invoked immediately. In the - case of YYERROR or YYBACKUP, subsequent parser actions might lead - to an incorrect destructor call or verbose syntax error message - before the lookahead is translated. */ - YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); - - YYPOPSTACK (yylen); - yylen = 0; - YY_STACK_PRINT (yyss, yyssp); - - *++yyvsp = yyval; - - /* Now 'shift' the result of the reduction. Determine what state - that goes to, based on the state we popped back to and the rule - number reduced by. */ - - yyn = yyr1[yyn]; - - yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; - if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) - yystate = yytable[yystate]; - else - yystate = yydefgoto[yyn - YYNTOKENS]; - - goto yynewstate; - - -/*--------------------------------------. -| yyerrlab -- here on detecting error. | -`--------------------------------------*/ -yyerrlab: - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); - - /* If not already recovering from an error, report this error. */ - if (!yyerrstatus) - { - ++yynerrs; -#if ! YYERROR_VERBOSE - yyerror (pc, YY_("syntax error")); -#else -# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ - yyssp, yytoken) - { - char const *yymsgp = YY_("syntax error"); - int yysyntax_error_status; - yysyntax_error_status = YYSYNTAX_ERROR; - if (yysyntax_error_status == 0) - yymsgp = yymsg; - else if (yysyntax_error_status == 1) - { - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); - if (!yymsg) - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - yysyntax_error_status = 2; - } - else - { - yysyntax_error_status = YYSYNTAX_ERROR; - yymsgp = yymsg; - } - } - yyerror (pc, yymsgp); - if (yysyntax_error_status == 2) - goto yyexhaustedlab; - } -# undef YYSYNTAX_ERROR -#endif - } - - - - if (yyerrstatus == 3) - { - /* If just tried and failed to reuse lookahead token after an - error, discard it. */ - - if (yychar <= YYEOF) - { - /* Return failure if at end of input. */ - if (yychar == YYEOF) - YYABORT; - } - else - { - yydestruct ("Error: discarding", - yytoken, &yylval, pc); - yychar = YYEMPTY; - } - } - - /* Else will try to reuse lookahead token after shifting the error - token. */ - goto yyerrlab1; - - -/*---------------------------------------------------. -| yyerrorlab -- error raised explicitly by YYERROR. | -`---------------------------------------------------*/ -yyerrorlab: - - /* Pacify compilers like GCC when the user code never invokes - YYERROR and the label yyerrorlab therefore never appears in user - code. */ - if (/*CONSTCOND*/ 0) - goto yyerrorlab; - - /* Do not reclaim the symbols of the rule whose action triggered - this YYERROR. */ - YYPOPSTACK (yylen); - yylen = 0; - YY_STACK_PRINT (yyss, yyssp); - yystate = *yyssp; - goto yyerrlab1; - - -/*-------------------------------------------------------------. -| yyerrlab1 -- common code for both syntax error and YYERROR. | -`-------------------------------------------------------------*/ -yyerrlab1: - yyerrstatus = 3; /* Each real token shifted decrements this. */ - - for (;;) - { - yyn = yypact[yystate]; - if (!yypact_value_is_default (yyn)) - { - yyn += YYTERROR; - if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) - { - yyn = yytable[yyn]; - if (0 < yyn) - break; - } - } - - /* Pop the current state because it cannot handle the error token. */ - if (yyssp == yyss) - YYABORT; - - - yydestruct ("Error: popping", - yystos[yystate], yyvsp, pc); - YYPOPSTACK (1); - yystate = *yyssp; - YY_STACK_PRINT (yyss, yyssp); - } - - YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN - *++yyvsp = yylval; - YY_IGNORE_MAYBE_UNINITIALIZED_END - - - /* Shift the error token. */ - YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); - - yystate = yyn; - goto yynewstate; - - -/*-------------------------------------. -| yyacceptlab -- YYACCEPT comes here. | -`-------------------------------------*/ -yyacceptlab: - yyresult = 0; - goto yyreturn; - -/*-----------------------------------. -| yyabortlab -- YYABORT comes here. | -`-----------------------------------*/ -yyabortlab: - yyresult = 1; - goto yyreturn; - -#if !defined yyoverflow || YYERROR_VERBOSE -/*-------------------------------------------------. -| yyexhaustedlab -- memory exhaustion comes here. | -`-------------------------------------------------*/ -yyexhaustedlab: - yyerror (pc, YY_("memory exhausted")); - yyresult = 2; - /* Fall through. */ -#endif - -yyreturn: - if (yychar != YYEMPTY) - { - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = YYTRANSLATE (yychar); - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval, pc); - } - /* Do not reclaim the symbols of the rule whose action triggered - this YYABORT or YYACCEPT. */ - YYPOPSTACK (yylen); - YY_STACK_PRINT (yyss, yyssp); - while (yyssp != yyss) - { - yydestruct ("Cleanup: popping", - yystos[*yyssp], yyvsp, pc); - YYPOPSTACK (1); - } -#ifndef yyoverflow - if (yyss != yyssa) - YYSTACK_FREE (yyss); -#endif -#if YYERROR_VERBOSE - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); -#endif - return yyresult; -} -#line 673 "src/ostree/parse-datetime.y" /* yacc.c:1906 */ - - -static table const meridian_table[] = -{ - { "AM", tMERIDIAN, MERam }, - { "A.M.", tMERIDIAN, MERam }, - { "PM", tMERIDIAN, MERpm }, - { "P.M.", tMERIDIAN, MERpm }, - { NULL, 0, 0 } -}; - -static table const dst_table[] = -{ - { "DST", tDST, 0 } -}; - -static table const month_and_day_table[] = -{ - { "JANUARY", tMONTH, 1 }, - { "FEBRUARY", tMONTH, 2 }, - { "MARCH", tMONTH, 3 }, - { "APRIL", tMONTH, 4 }, - { "MAY", tMONTH, 5 }, - { "JUNE", tMONTH, 6 }, - { "JULY", tMONTH, 7 }, - { "AUGUST", tMONTH, 8 }, - { "SEPTEMBER",tMONTH, 9 }, - { "SEPT", tMONTH, 9 }, - { "OCTOBER", tMONTH, 10 }, - { "NOVEMBER", tMONTH, 11 }, - { "DECEMBER", tMONTH, 12 }, - { "SUNDAY", tDAY, 0 }, - { "MONDAY", tDAY, 1 }, - { "TUESDAY", tDAY, 2 }, - { "TUES", tDAY, 2 }, - { "WEDNESDAY",tDAY, 3 }, - { "WEDNES", tDAY, 3 }, - { "THURSDAY", tDAY, 4 }, - { "THUR", tDAY, 4 }, - { "THURS", tDAY, 4 }, - { "FRIDAY", tDAY, 5 }, - { "SATURDAY", tDAY, 6 }, - { NULL, 0, 0 } -}; - -static table const time_units_table[] = -{ - { "YEAR", tYEAR_UNIT, 1 }, - { "MONTH", tMONTH_UNIT, 1 }, - { "FORTNIGHT",tDAY_UNIT, 14 }, - { "WEEK", tDAY_UNIT, 7 }, - { "DAY", tDAY_UNIT, 1 }, - { "HOUR", tHOUR_UNIT, 1 }, - { "MINUTE", tMINUTE_UNIT, 1 }, - { "MIN", tMINUTE_UNIT, 1 }, - { "SECOND", tSEC_UNIT, 1 }, - { "SEC", tSEC_UNIT, 1 }, - { NULL, 0, 0 } -}; - -/* Assorted relative-time words. */ -static table const relative_time_table[] = -{ - { "TOMORROW", tDAY_SHIFT, 1 }, - { "YESTERDAY",tDAY_SHIFT, -1 }, - { "TODAY", tDAY_SHIFT, 0 }, - { "NOW", tDAY_SHIFT, 0 }, - { "LAST", tORDINAL, -1 }, - { "THIS", tORDINAL, 0 }, - { "NEXT", tORDINAL, 1 }, - { "FIRST", tORDINAL, 1 }, -/*{ "SECOND", tORDINAL, 2 }, */ - { "THIRD", tORDINAL, 3 }, - { "FOURTH", tORDINAL, 4 }, - { "FIFTH", tORDINAL, 5 }, - { "SIXTH", tORDINAL, 6 }, - { "SEVENTH", tORDINAL, 7 }, - { "EIGHTH", tORDINAL, 8 }, - { "NINTH", tORDINAL, 9 }, - { "TENTH", tORDINAL, 10 }, - { "ELEVENTH", tORDINAL, 11 }, - { "TWELFTH", tORDINAL, 12 }, - { "AGO", tAGO, -1 }, - { "HENCE", tAGO, 1 }, - { NULL, 0, 0 } -}; - -/* The universal time zone table. These labels can be used even for - time stamps that would not otherwise be valid, e.g., GMT time - stamps in London during summer. */ -static table const universal_time_zone_table[] = -{ - { "GMT", tZONE, HOUR ( 0) }, /* Greenwich Mean */ - { "UT", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */ - { "UTC", tZONE, HOUR ( 0) }, - { NULL, 0, 0 } -}; - -/* The time zone table. This table is necessarily incomplete, as time - zone abbreviations are ambiguous; e.g. Australians interpret "EST" - as Eastern time in Australia, not as US Eastern Standard Time. - You cannot rely on parse_datetime to handle arbitrary time zone - abbreviations; use numeric abbreviations like "-0500" instead. */ -static table const time_zone_table[] = -{ - { "WET", tZONE, HOUR ( 0) }, /* Western European */ - { "WEST", tDAYZONE, HOUR ( 0) }, /* Western European Summer */ - { "BST", tDAYZONE, HOUR ( 0) }, /* British Summer */ - { "ART", tZONE, -HOUR ( 3) }, /* Argentina */ - { "BRT", tZONE, -HOUR ( 3) }, /* Brazil */ - { "BRST", tDAYZONE, -HOUR ( 3) }, /* Brazil Summer */ - { "NST", tZONE, -(HOUR ( 3) + 30) }, /* Newfoundland Standard */ - { "NDT", tDAYZONE,-(HOUR ( 3) + 30) }, /* Newfoundland Daylight */ - { "AST", tZONE, -HOUR ( 4) }, /* Atlantic Standard */ - { "ADT", tDAYZONE, -HOUR ( 4) }, /* Atlantic Daylight */ - { "CLT", tZONE, -HOUR ( 4) }, /* Chile */ - { "CLST", tDAYZONE, -HOUR ( 4) }, /* Chile Summer */ - { "EST", tZONE, -HOUR ( 5) }, /* Eastern Standard */ - { "EDT", tDAYZONE, -HOUR ( 5) }, /* Eastern Daylight */ - { "CST", tZONE, -HOUR ( 6) }, /* Central Standard */ - { "CDT", tDAYZONE, -HOUR ( 6) }, /* Central Daylight */ - { "MST", tZONE, -HOUR ( 7) }, /* Mountain Standard */ - { "MDT", tDAYZONE, -HOUR ( 7) }, /* Mountain Daylight */ - { "PST", tZONE, -HOUR ( 8) }, /* Pacific Standard */ - { "PDT", tDAYZONE, -HOUR ( 8) }, /* Pacific Daylight */ - { "AKST", tZONE, -HOUR ( 9) }, /* Alaska Standard */ - { "AKDT", tDAYZONE, -HOUR ( 9) }, /* Alaska Daylight */ - { "HST", tZONE, -HOUR (10) }, /* Hawaii Standard */ - { "HAST", tZONE, -HOUR (10) }, /* Hawaii-Aleutian Standard */ - { "HADT", tDAYZONE, -HOUR (10) }, /* Hawaii-Aleutian Daylight */ - { "SST", tZONE, -HOUR (12) }, /* Samoa Standard */ - { "WAT", tZONE, HOUR ( 1) }, /* West Africa */ - { "CET", tZONE, HOUR ( 1) }, /* Central European */ - { "CEST", tDAYZONE, HOUR ( 1) }, /* Central European Summer */ - { "MET", tZONE, HOUR ( 1) }, /* Middle European */ - { "MEZ", tZONE, HOUR ( 1) }, /* Middle European */ - { "MEST", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */ - { "MESZ", tDAYZONE, HOUR ( 1) }, /* Middle European Summer */ - { "EET", tZONE, HOUR ( 2) }, /* Eastern European */ - { "EEST", tDAYZONE, HOUR ( 2) }, /* Eastern European Summer */ - { "CAT", tZONE, HOUR ( 2) }, /* Central Africa */ - { "SAST", tZONE, HOUR ( 2) }, /* South Africa Standard */ - { "EAT", tZONE, HOUR ( 3) }, /* East Africa */ - { "MSK", tZONE, HOUR ( 3) }, /* Moscow */ - { "MSD", tDAYZONE, HOUR ( 3) }, /* Moscow Daylight */ - { "IST", tZONE, (HOUR ( 5) + 30) }, /* India Standard */ - { "SGT", tZONE, HOUR ( 8) }, /* Singapore */ - { "KST", tZONE, HOUR ( 9) }, /* Korea Standard */ - { "JST", tZONE, HOUR ( 9) }, /* Japan Standard */ - { "GST", tZONE, HOUR (10) }, /* Guam Standard */ - { "NZST", tZONE, HOUR (12) }, /* New Zealand Standard */ - { "NZDT", tDAYZONE, HOUR (12) }, /* New Zealand Daylight */ - { NULL, 0, 0 } -}; - -/* Military time zone table. - - Note 'T' is a special case, as it is used as the separator in ISO - 8601 date and time of day representation. */ -static table const military_table[] = -{ - { "A", tZONE, -HOUR ( 1) }, - { "B", tZONE, -HOUR ( 2) }, - { "C", tZONE, -HOUR ( 3) }, - { "D", tZONE, -HOUR ( 4) }, - { "E", tZONE, -HOUR ( 5) }, - { "F", tZONE, -HOUR ( 6) }, - { "G", tZONE, -HOUR ( 7) }, - { "H", tZONE, -HOUR ( 8) }, - { "I", tZONE, -HOUR ( 9) }, - { "K", tZONE, -HOUR (10) }, - { "L", tZONE, -HOUR (11) }, - { "M", tZONE, -HOUR (12) }, - { "N", tZONE, HOUR ( 1) }, - { "O", tZONE, HOUR ( 2) }, - { "P", tZONE, HOUR ( 3) }, - { "Q", tZONE, HOUR ( 4) }, - { "R", tZONE, HOUR ( 5) }, - { "S", tZONE, HOUR ( 6) }, - { "T", 'T', 0 }, - { "U", tZONE, HOUR ( 8) }, - { "V", tZONE, HOUR ( 9) }, - { "W", tZONE, HOUR (10) }, - { "X", tZONE, HOUR (11) }, - { "Y", tZONE, HOUR (12) }, - { "Z", tZONE, HOUR ( 0) }, - { NULL, 0, 0 } -}; - - - -/* Convert a time zone expressed as HH:MM into an integer count of - minutes. If MM is negative, then S is of the form HHMM and needs - to be picked apart; otherwise, S is of the form HH. As specified in - http://www.opengroup.org/susv3xbd/xbd_chap08.html#tag_08_03, allow - only valid TZ range, and consider first two digits as hours, if no - minutes specified. */ - -static long int -time_zone_hhmm (parser_control *pc, textint s, long int mm) -{ - long int n_minutes; - - /* If the length of S is 1 or 2 and no minutes are specified, - interpret it as a number of hours. */ - if (s.digits <= 2 && mm < 0) - s.value *= 100; - - if (mm < 0) - n_minutes = (s.value / 100) * 60 + s.value % 100; - else - n_minutes = s.value * 60 + (s.negative ? -mm : mm); - - /* If the absolute number of minutes is larger than 24 hours, - arrange to reject it by incrementing pc->zones_seen. Thus, - we allow only values in the range UTC-24:00 to UTC+24:00. */ - if (24 * 60 < labs (n_minutes)) - pc->zones_seen++; - - return n_minutes; -} - -static int -to_hour (long int hours, int meridian) -{ - switch (meridian) - { - default: /* Pacify GCC. */ - case MER24: - return 0 <= hours && hours < 24 ? hours : -1; - case MERam: - return 0 < hours && hours < 12 ? hours : hours == 12 ? 0 : -1; - case MERpm: - return 0 < hours && hours < 12 ? hours + 12 : hours == 12 ? 12 : -1; - } -} - -static long int -to_year (textint textyear) -{ - long int year = textyear.value; - - if (year < 0) - year = -year; - - /* XPG4 suggests that years 00-68 map to 2000-2068, and - years 69-99 map to 1969-1999. */ - else if (textyear.digits == 2) - year += year < 69 ? 2000 : 1900; - - return year; -} - -static table const * -lookup_zone (parser_control const *pc, char const *name) -{ - table const *tp; - - for (tp = universal_time_zone_table; tp->name; tp++) - if (strcmp (name, tp->name) == 0) - return tp; - - /* Try local zone abbreviations before those in time_zone_table, as - the local ones are more likely to be right. */ - for (tp = pc->local_time_zone_table; tp->name; tp++) - if (strcmp (name, tp->name) == 0) - return tp; - - for (tp = time_zone_table; tp->name; tp++) - if (strcmp (name, tp->name) == 0) - return tp; - - return NULL; -} - -// #if ! HAVE_TM_GMTOFF -#if 1 // Always true for us -/* Yield the difference between *A and *B, - measured in seconds, ignoring leap seconds. - The body of this function is taken directly from the GNU C Library; - see src/strftime.c. */ -static long int -tm_diff (struct tm const *a, struct tm const *b) -{ - /* Compute intervening leap days correctly even if year is negative. - Take care to avoid int overflow in leap day calculations. */ - int a4 = SHR (a->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (a->tm_year & 3); - int b4 = SHR (b->tm_year, 2) + SHR (TM_YEAR_BASE, 2) - ! (b->tm_year & 3); - int a100 = a4 / 25 - (a4 % 25 < 0); - int b100 = b4 / 25 - (b4 % 25 < 0); - int a400 = SHR (a100, 2); - int b400 = SHR (b100, 2); - int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400); - long int ayear = a->tm_year; - long int years = ayear - b->tm_year; - long int days = (365 * years + intervening_leap_days - + (a->tm_yday - b->tm_yday)); - return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour)) - + (a->tm_min - b->tm_min)) - + (a->tm_sec - b->tm_sec)); -} -#endif /* ! HAVE_TM_GMTOFF */ - -static table const * -lookup_word (parser_control const *pc, char *word) -{ - char *p; - char *q; - size_t wordlen; - table const *tp; - bool period_found; - bool abbrev; - - /* Make it uppercase. */ - for (p = word; *p; p++) - { - unsigned char ch = *p; - *p = toupper (ch); - } - - for (tp = meridian_table; tp->name; tp++) - if (strcmp (word, tp->name) == 0) - return tp; - - /* See if we have an abbreviation for a month. */ - wordlen = strlen (word); - abbrev = wordlen == 3 || (wordlen == 4 && word[3] == '.'); - - for (tp = month_and_day_table; tp->name; tp++) - if ((abbrev ? strncmp (word, tp->name, 3) : strcmp (word, tp->name)) == 0) - return tp; - - if ((tp = lookup_zone (pc, word))) - return tp; - - if (strcmp (word, dst_table[0].name) == 0) - return dst_table; - - for (tp = time_units_table; tp->name; tp++) - if (strcmp (word, tp->name) == 0) - return tp; - - /* Strip off any plural and try the units table again. */ - if (word[wordlen - 1] == 'S') - { - word[wordlen - 1] = '\0'; - for (tp = time_units_table; tp->name; tp++) - if (strcmp (word, tp->name) == 0) - return tp; - word[wordlen - 1] = 'S'; /* For "this" in relative_time_table. */ - } - - for (tp = relative_time_table; tp->name; tp++) - if (strcmp (word, tp->name) == 0) - return tp; - - /* Military time zones. */ - if (wordlen == 1) - for (tp = military_table; tp->name; tp++) - if (word[0] == tp->name[0]) - return tp; - - /* Drop out any periods and try the time zone table again. */ - for (period_found = false, p = q = word; (*p = *q); q++) - if (*q == '.') - period_found = true; - else - p++; - if (period_found && (tp = lookup_zone (pc, word))) - return tp; - - return NULL; -} - -static int -yylex (union YYSTYPE *lvalp, parser_control *pc) -{ - unsigned char c; - size_t count; - - for (;;) - { - while (c = *pc->input, isspace (c)) - pc->input++; - - if (ISDIGIT (c) || c == '-' || c == '+') - { - char const *p; - int sign; - unsigned long int value; - if (c == '-' || c == '+') - { - sign = c == '-' ? -1 : 1; - while (c = *++pc->input, isspace (c)) - continue; - if (! ISDIGIT (c)) - /* skip the '-' sign */ - continue; - } - else - sign = 0; - p = pc->input; - for (value = 0; ; value *= 10) - { - unsigned long int value1 = value + (c - '0'); - if (value1 < value) - return '?'; - value = value1; - c = *++p; - if (! ISDIGIT (c)) - break; - if (ULONG_MAX / 10 < value) - return '?'; - } - if ((c == '.' || c == ',') && ISDIGIT (p[1])) - { - time_t s; - int ns; - int digits; - unsigned long int value1; - - /* Check for overflow when converting value to time_t. */ - if (sign < 0) - { - s = - value; - if (0 < s) - return '?'; - value1 = -s; - } - else - { - s = value; - if (s < 0) - return '?'; - value1 = s; - } - if (value != value1) - return '?'; - - /* Accumulate fraction, to ns precision. */ - p++; - ns = *p++ - '0'; - for (digits = 2; digits <= LOG10_BILLION; digits++) - { - ns *= 10; - if (ISDIGIT (*p)) - ns += *p++ - '0'; - } - - /* Skip excess digits, truncating toward -Infinity. */ - if (sign < 0) - for (; ISDIGIT (*p); p++) - if (*p != '0') - { - ns++; - break; - } - while (ISDIGIT (*p)) - p++; - - /* Adjust to the timespec convention, which is that - tv_nsec is always a positive offset even if tv_sec is - negative. */ - if (sign < 0 && ns) - { - s--; - if (! (s < 0)) - return '?'; - ns = BILLION - ns; - } - - lvalp->timespec.tv_sec = s; - lvalp->timespec.tv_nsec = ns; - pc->input = p; - return sign ? tSDECIMAL_NUMBER : tUDECIMAL_NUMBER; - } - else - { - lvalp->textintval.negative = sign < 0; - if (sign < 0) - { - lvalp->textintval.value = - value; - if (0 < lvalp->textintval.value) - return '?'; - } - else - { - lvalp->textintval.value = value; - if (lvalp->textintval.value < 0) - return '?'; - } - lvalp->textintval.digits = p - pc->input; - pc->input = p; - return sign ? tSNUMBER : tUNUMBER; - } - } - - if (isalpha (c)) - { - char buff[20]; - char *p = buff; - table const *tp; - - do - { - if (p < buff + sizeof buff - 1) - *p++ = c; - c = *++pc->input; - } - while (isalpha (c) || c == '.'); - - *p = '\0'; - tp = lookup_word (pc, buff); - if (! tp) - return '?'; - lvalp->intval = tp->value; - return tp->type; - } - - if (c != '(') - return to_uchar (*pc->input++); - - count = 0; - do - { - c = *pc->input++; - if (c == '\0') - return c; - if (c == '(') - count++; - else if (c == ')') - count--; - } - while (count != 0); - } -} - -/* Do nothing if the parser reports an error. */ -static int -yyerror (parser_control const *pc, - char const *s) -{ - return 0; -} - -/* If *TM0 is the old and *TM1 is the new value of a struct tm after - passing it to mktime, return true if it's OK that mktime returned T. - It's not OK if *TM0 has out-of-range members. */ - -static bool -mktime_ok (struct tm const *tm0, struct tm const *tm1, time_t t) -{ - if (t == (time_t) -1) - { - /* Guard against falsely reporting an error when parsing a time - stamp that happens to equal (time_t) -1, on a host that - supports such a time stamp. */ - tm1 = localtime (&t); - if (!tm1) - return false; - } - - return ! ((tm0->tm_sec ^ tm1->tm_sec) - | (tm0->tm_min ^ tm1->tm_min) - | (tm0->tm_hour ^ tm1->tm_hour) - | (tm0->tm_mday ^ tm1->tm_mday) - | (tm0->tm_mon ^ tm1->tm_mon) - | (tm0->tm_year ^ tm1->tm_year)); -} - -/* A reasonable upper bound for the size of ordinary TZ strings. - Use heap allocation if TZ's length exceeds this. */ -enum { TZBUFSIZE = 100 }; - -/* Return a copy of TZ, stored in TZBUF if it fits, and heap-allocated - otherwise. */ -static char * -get_tz (char tzbuf[TZBUFSIZE]) -{ - char *tz = getenv ("TZ"); - if (tz) - { - size_t tzsize = strlen (tz) + 1; - tz = (tzsize <= TZBUFSIZE - ? memcpy (tzbuf, tz, tzsize) - : xmemdup (tz, tzsize)); - } - return tz; -} - -/* Parse a date/time string, storing the resulting time value into *RESULT. - The string itself is pointed to by P. Return true if successful. - P can be an incomplete or relative time specification; if so, use - *NOW as the basis for the returned time. */ -bool -parse_datetime (struct timespec *result, char const *p, - struct timespec const *now) -{ - time_t Start; - long int Start_ns; - struct tm const *tmp; - struct tm tm = { 0, }; - struct tm tm0 = { 0, }; - parser_control pc; - struct timespec gettime_buffer; - unsigned char c; - bool tz_was_altered = false; - char *tz0 = NULL; - char tz0buf[TZBUFSIZE]; - bool ok = true; - - if (! now) - { - gettime (&gettime_buffer); - now = &gettime_buffer; - } - - Start = now->tv_sec; - Start_ns = now->tv_nsec; - - tmp = localtime (&now->tv_sec); - if (! tmp) - return false; - - while (c = *p, isspace (c)) - p++; - - if (strncmp (p, "TZ=\"", 4) == 0) - { - char const *tzbase = p + 4; - size_t tzsize = 1; - char const *s; - - for (s = tzbase; *s; s++, tzsize++) - if (*s == '\\') - { - s++; - if (! (*s == '\\' || *s == '"')) - break; - } - else if (*s == '"') - { - char *z; - char *tz1; - char tz1buf[TZBUFSIZE]; - bool large_tz = TZBUFSIZE < tzsize; - bool setenv_ok; - tz0 = get_tz (tz0buf); - z = tz1 = large_tz ? g_malloc (tzsize) : tz1buf; - for (s = tzbase; *s != '"'; s++) - *z++ = *(s += *s == '\\'); - *z = '\0'; - setenv_ok = setenv ("TZ", tz1, 1) == 0; - if (large_tz) - free (tz1); - if (!setenv_ok) - goto fail; - tz_was_altered = true; - - p = s + 1; - while (c = *p, isspace (c)) - p++; - - break; - } - } - - /* As documented, be careful to treat the empty string just like - a date string of "0". Without this, an empty string would be - declared invalid when parsed during a DST transition. */ - if (*p == '\0') - p = "0"; - - pc.input = p; - pc.year.value = tmp->tm_year; - pc.year.value += TM_YEAR_BASE; - pc.year.digits = 0; - pc.month = tmp->tm_mon + 1; - pc.day = tmp->tm_mday; - pc.hour = tmp->tm_hour; - pc.minutes = tmp->tm_min; - pc.seconds.tv_sec = tmp->tm_sec; - pc.seconds.tv_nsec = Start_ns; - tm.tm_isdst = tmp->tm_isdst; - - pc.meridian = MER24; - pc.rel = RELATIVE_TIME_0; - pc.timespec_seen = false; - pc.rels_seen = false; - pc.dates_seen = 0; - pc.days_seen = 0; - pc.times_seen = 0; - pc.local_zones_seen = 0; - pc.dsts_seen = 0; - pc.zones_seen = 0; - -#if HAVE_STRUCT_TM_TM_ZONE - pc.local_time_zone_table[0].name = tmp->tm_zone; - pc.local_time_zone_table[0].type = tLOCAL_ZONE; - pc.local_time_zone_table[0].value = tmp->tm_isdst; - pc.local_time_zone_table[1].name = NULL; - - /* Probe the names used in the next three calendar quarters, looking - for a tm_isdst different from the one we already have. */ - { - int quarter; - for (quarter = 1; quarter <= 3; quarter++) - { - time_t probe = Start + quarter * (90 * 24 * 60 * 60); - struct tm const *probe_tm = localtime (&probe); - if (probe_tm && probe_tm->tm_zone - && probe_tm->tm_isdst != pc.local_time_zone_table[0].value) - { - { - pc.local_time_zone_table[1].name = probe_tm->tm_zone; - pc.local_time_zone_table[1].type = tLOCAL_ZONE; - pc.local_time_zone_table[1].value = probe_tm->tm_isdst; - pc.local_time_zone_table[2].name = NULL; - } - break; - } - } - } -#else -#if HAVE_TZNAME - { -# if !HAVE_DECL_TZNAME - extern char *tzname[]; -# endif - int i; - for (i = 0; i < 2; i++) - { - pc.local_time_zone_table[i].name = tzname[i]; - pc.local_time_zone_table[i].type = tLOCAL_ZONE; - pc.local_time_zone_table[i].value = i; - } - pc.local_time_zone_table[i].name = NULL; - } -#else - pc.local_time_zone_table[0].name = NULL; -#endif -#endif - - if (pc.local_time_zone_table[0].name && pc.local_time_zone_table[1].name - && ! strcmp (pc.local_time_zone_table[0].name, - pc.local_time_zone_table[1].name)) - { - /* This locale uses the same abbreviation for standard and - daylight times. So if we see that abbreviation, we don't - know whether it's daylight time. */ - pc.local_time_zone_table[0].value = -1; - pc.local_time_zone_table[1].name = NULL; - } - - if (yyparse (&pc) != 0) - goto fail; - - if (pc.timespec_seen) - *result = pc.seconds; - else - { - if (1 < (pc.times_seen | pc.dates_seen | pc.days_seen | pc.dsts_seen - | (pc.local_zones_seen + pc.zones_seen))) - goto fail; - - tm.tm_year = to_year (pc.year) - TM_YEAR_BASE; - tm.tm_mon = pc.month - 1; - tm.tm_mday = pc.day; - if (pc.times_seen || (pc.rels_seen && ! pc.dates_seen && ! pc.days_seen)) - { - tm.tm_hour = to_hour (pc.hour, pc.meridian); - if (tm.tm_hour < 0) - goto fail; - tm.tm_min = pc.minutes; - tm.tm_sec = pc.seconds.tv_sec; - } - else - { - tm.tm_hour = tm.tm_min = tm.tm_sec = 0; - pc.seconds.tv_nsec = 0; - } - - /* Let mktime deduce tm_isdst if we have an absolute time stamp. */ - if (pc.dates_seen | pc.days_seen | pc.times_seen) - tm.tm_isdst = -1; - - /* But if the input explicitly specifies local time with or without - DST, give mktime that information. */ - if (pc.local_zones_seen) - tm.tm_isdst = pc.local_isdst; - - tm0 = tm; - - Start = mktime (&tm); - - if (! mktime_ok (&tm0, &tm, Start)) - { - if (! pc.zones_seen) - goto fail; - else - { - /* Guard against falsely reporting errors near the time_t - boundaries when parsing times in other time zones. For - example, suppose the input string "1969-12-31 23:00:00 -0100", - the current time zone is 8 hours ahead of UTC, and the min - time_t value is 1970-01-01 00:00:00 UTC. Then the min - localtime value is 1970-01-01 08:00:00, and mktime will - therefore fail on 1969-12-31 23:00:00. To work around the - problem, set the time zone to 1 hour behind UTC temporarily - by setting TZ="XXX1:00" and try mktime again. */ - - long int time_zone = pc.time_zone; - long int abs_time_zone = time_zone < 0 ? - time_zone : time_zone; - long int abs_time_zone_hour = abs_time_zone / 60; - int abs_time_zone_min = abs_time_zone % 60; - char tz1buf[sizeof "XXX+0:00" - + sizeof pc.time_zone * CHAR_BIT / 3]; - if (!tz_was_altered) - tz0 = get_tz (tz0buf); - sprintf (tz1buf, "XXX%s%ld:%02d", &"-"[time_zone < 0], - abs_time_zone_hour, abs_time_zone_min); - if (setenv ("TZ", tz1buf, 1) != 0) - goto fail; - tz_was_altered = true; - tm = tm0; - Start = mktime (&tm); - if (! mktime_ok (&tm0, &tm, Start)) - goto fail; - } - } - - if (pc.days_seen && ! pc.dates_seen) - { - tm.tm_mday += ((pc.day_number - tm.tm_wday + 7) % 7 - + 7 * (pc.day_ordinal - - (0 < pc.day_ordinal - && tm.tm_wday != pc.day_number))); - tm.tm_isdst = -1; - Start = mktime (&tm); - if (Start == (time_t) -1) - goto fail; - } - - /* Add relative date. */ - if (pc.rel.year | pc.rel.month | pc.rel.day) - { - int year = tm.tm_year + pc.rel.year; - int month = tm.tm_mon + pc.rel.month; - int day = tm.tm_mday + pc.rel.day; - if (((year < tm.tm_year) ^ (pc.rel.year < 0)) - | ((month < tm.tm_mon) ^ (pc.rel.month < 0)) - | ((day < tm.tm_mday) ^ (pc.rel.day < 0))) - goto fail; - tm.tm_year = year; - tm.tm_mon = month; - tm.tm_mday = day; - tm.tm_hour = tm0.tm_hour; - tm.tm_min = tm0.tm_min; - tm.tm_sec = tm0.tm_sec; - tm.tm_isdst = tm0.tm_isdst; - Start = mktime (&tm); - if (Start == (time_t) -1) - goto fail; - } - - /* The only "output" of this if-block is an updated Start value, - so this block must follow others that clobber Start. */ - if (pc.zones_seen) - { - long int delta = pc.time_zone * 60; - time_t t1; -#ifdef HAVE_TM_GMTOFF - delta -= tm.tm_gmtoff; -#else - time_t t = Start; - struct tm const *gmt = gmtime (&t); - if (! gmt) - goto fail; - delta -= tm_diff (&tm, gmt); -#endif - t1 = Start - delta; - if ((Start < t1) != (delta < 0)) - goto fail; /* time_t overflow */ - Start = t1; - } - - /* Add relative hours, minutes, and seconds. On hosts that support - leap seconds, ignore the possibility of leap seconds; e.g., - "+ 10 minutes" adds 600 seconds, even if one of them is a - leap second. Typically this is not what the user wants, but it's - too hard to do it the other way, because the time zone indicator - must be applied before relative times, and if mktime is applied - again the time zone will be lost. */ - { - long int sum_ns = pc.seconds.tv_nsec + pc.rel.ns; - long int normalized_ns = (sum_ns % BILLION + BILLION) % BILLION; - time_t t0 = Start; - long int d1 = 60 * 60 * pc.rel.hour; - time_t t1 = t0 + d1; - long int d2 = 60 * pc.rel.minutes; - time_t t2 = t1 + d2; - intmax_t d3 = pc.rel.seconds; - intmax_t t3 = t2 + d3; - long int d4 = (sum_ns - normalized_ns) / BILLION; - intmax_t t4 = t3 + d4; - time_t t5 = t4; - - if ((d1 / (60 * 60) ^ pc.rel.hour) - | (d2 / 60 ^ pc.rel.minutes) - | ((t1 < t0) ^ (d1 < 0)) - | ((t2 < t1) ^ (d2 < 0)) - | ((t3 < t2) ^ (d3 < 0)) - | ((t4 < t3) ^ (d4 < 0)) - | (t5 != t4)) - goto fail; - - result->tv_sec = t5; - result->tv_nsec = normalized_ns; - } - } - - goto done; - - fail: - ok = false; - done: - if (tz_was_altered) - ok &= (tz0 ? setenv ("TZ", tz0, 1) : unsetenv ("TZ")) == 0; - if (tz0 != tz0buf) - free (tz0); - return ok; -} diff --git a/src/switchroot/ostree-prepare-root.c b/src/switchroot/ostree-prepare-root.c index a5c3c785..5ed9b60f 100644 --- a/src/switchroot/ostree-prepare-root.c +++ b/src/switchroot/ostree-prepare-root.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,9 @@ #include "ostree-mount-util.h" +/* Initialized early in main */ +static bool running_as_pid1; + static char* resolve_deploy_path (const char * root_mountpoint) { @@ -60,7 +64,6 @@ resolve_deploy_path (const char * root_mountpoint) errx (EXIT_FAILURE, "No OSTree target; expected ostree=/ostree/boot.N/..."); snprintf (destpath, sizeof(destpath), "%s/%s", root_mountpoint, ostree_target); - printf ("Examining %s\n", destpath); if (lstat (destpath, &stbuf) < 0) err (EXIT_FAILURE, "Couldn't find specified OSTree root '%s'", destpath); if (!S_ISLNK (stbuf.st_mode)) @@ -68,7 +71,9 @@ resolve_deploy_path (const char * root_mountpoint) deploy_path = realpath (destpath, NULL); if (deploy_path == NULL) err (EXIT_FAILURE, "realpath(%s) failed", destpath); - printf ("Resolved OSTree target to: %s\n", deploy_path); + /* Quiet logs if there's no journal */ + if (!running_as_pid1) + printf ("Resolved OSTree target to: %s\n", deploy_path); return deploy_path; } @@ -81,13 +86,18 @@ pivot_root(const char * new_root, const char * put_old) int main(int argc, char *argv[]) { - const char *root_mountpoint = NULL, *root_arg = NULL; - char *deploy_path = NULL; - char srcpath[PATH_MAX]; - struct stat stbuf; - int we_mounted_proc = 0; + /* If we're pid 1, that means there's no initramfs; in this situation + * various defaults change: + * + * - Assume that the target root is / + * - Quiet logging as there's no journal + * etc. + */ + running_as_pid1 = (getpid () == 1); - if (getpid() == 1) + const char *root_arg = NULL; + bool we_mounted_proc = false; + if (running_as_pid1) { root_arg = "/"; } @@ -98,6 +108,7 @@ main(int argc, char *argv[]) root_arg = argv[1]; } + struct stat stbuf; if (stat ("/proc/cmdline", &stbuf) < 0) { if (errno != ENOENT) @@ -109,10 +120,10 @@ main(int argc, char *argv[]) we_mounted_proc = 1; } - root_mountpoint = realpath (root_arg, NULL); + const char *root_mountpoint = realpath (root_arg, NULL); if (root_mountpoint == NULL) err (EXIT_FAILURE, "realpath(\"%s\")", root_arg); - deploy_path = resolve_deploy_path (root_mountpoint); + char *deploy_path = resolve_deploy_path (root_mountpoint); if (we_mounted_proc) { @@ -147,6 +158,7 @@ main(int argc, char *argv[]) err (EXIT_FAILURE, "failed to bind mount ../../var to var"); #endif + char srcpath[PATH_MAX]; /* If /boot is on the same partition, use a bind mount to make it visible * at /boot inside the deployment. */ snprintf (srcpath, sizeof(srcpath), "%s/boot/loader", root_mountpoint); @@ -193,7 +205,7 @@ main(int argc, char *argv[]) * not pid 1. Otherwise it's handled later via ostree-remount.service. * https://mail.gnome.org/archives/ostree-list/2018-March/msg00012.html */ - if (getpid () != 1) + if (!running_as_pid1) touch_run_ostree (); if (strcmp(root_mountpoint, "/") == 0) @@ -246,7 +258,7 @@ main(int argc, char *argv[]) if (mount ("none", "sysroot", NULL, MS_PRIVATE, NULL) < 0) err (EXIT_FAILURE, "remounting 'sysroot' private"); - if (getpid() == 1) + if (running_as_pid1) { execl ("/sbin/init", "/sbin/init", NULL); err (EXIT_FAILURE, "failed to exec init inside ostree"); diff --git a/tests/admin-test.sh b/tests/admin-test.sh index b546e142..7384d8f3 100644 --- a/tests/admin-test.sh +++ b/tests/admin-test.sh @@ -21,7 +21,7 @@ set -euo pipefail -echo "1..$((23 + ${extra_admin_tests:-0}))" +echo "1..$((25 + ${extra_admin_tests:-0}))" function validate_bootloader() { cd ${test_tmpdir}; @@ -78,6 +78,12 @@ assert_ostree_deployment_refs 1/1/0 ${CMD_PREFIX} ostree admin status echo "ok layout" +if ${CMD_PREFIX} ostree admin deploy --stage --os=testos testos:testos/buildmaster/x86_64-runtime 2>err.txt; then + fatal "staged when not booted" +fi +assert_file_has_content_literal err.txt "Cannot stage a deployment when not currently booted into an OSTree system" +echo "ok staging does not work when not booted" + orig_mtime=$(stat -c '%.Y' sysroot/ostree/deploy) ${CMD_PREFIX} ostree admin deploy --os=testos testos:testos/buildmaster/x86_64-runtime new_mtime=$(stat -c '%.Y' sysroot/ostree/deploy) @@ -147,8 +153,13 @@ ln -s /ENOENT sysroot/ostree/deploy/testos/deploy/${rev}.3/etc/a-new-broken-syml ${CMD_PREFIX} ostree admin deploy --retain --os=testos testos:testos/buildmaster/x86_64-runtime assert_not_has_dir sysroot/boot/loader.0 assert_has_dir sysroot/boot/loader.1 -linktarget=$(readlink sysroot/ostree/deploy/testos/deploy/${rev}.4/etc/a-new-broken-symlink) -test "${linktarget}" = /ENOENT +link=sysroot/ostree/deploy/testos/deploy/${rev}.4/etc/a-new-broken-symlink +if ! test -L ${link}; then + ls -al ${link} + fatal "Not a symlink: ${link}" +fi +linktarget=$(readlink ${link}) +assert_streq "${linktarget}" /ENOENT assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.3/etc/os-release 'NAME=TestOS' assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.4/etc/os-release 'NAME=TestOS' assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.4/etc/a-new-config-file 'a new local config file' @@ -204,6 +215,11 @@ validate_bootloader echo "ok upgrade bare" os_repository_new_commit +if env OSTREE_EX_STAGE_DEPLOYMENTS=1 ${CMD_PREFIX} ostree admin upgrade --os=testos 2>err.txt; then + fatal "staged when not booted" +fi +echo "ok upgrade failed when staged" + ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo remote add --set=gpg-verify=false testos file://$(pwd)/testos-repo testos/buildmaster/x86_64-runtime ${CMD_PREFIX} ostree admin upgrade --os=testos origrev=${rev} diff --git a/tests/basic-test.sh b/tests/basic-test.sh index b8e7eb07..e0ed2c32 100644 --- a/tests/basic-test.sh +++ b/tests/basic-test.sh @@ -851,7 +851,7 @@ $OSTREE show --print-metadata-key=ostree.ref-binding test2 > test2-ref-binding assert_file_has_content test2-ref-binding 'test2' $OSTREE commit ${COMMIT_ARGS} -b test2-unbound --no-bindings --tree=dir=${test_tmpdir}/checkout-test2 -if $OSTREE show --print-metadata-key=ostree.ref-binding; then +if $OSTREE show --print-metadata-key=ostree.ref-binding test2-unbound; then fatal "ref bindings found with --no-bindings?" fi echo "ok refbinding" diff --git a/tests/libtest.sh b/tests/libtest.sh index 586bf9ef..f6b6eb2f 100755 --- a/tests/libtest.sh +++ b/tests/libtest.sh @@ -491,7 +491,7 @@ os_repository_new_commit () echo "content iteration ${content_iteration}" > usr/bin/content-iteration - version=$(date "+%Y%m%d.${content_iteration}") + export version=$(date "+%Y%m%d.${content_iteration}") ${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit --add-metadata-string "version=${version}" -b $branch -s "Build" cd ${test_tmpdir} diff --git a/tests/test-admin-deploy-2.sh b/tests/test-admin-deploy-2.sh index eab0a3d3..7e69ec88 100755 --- a/tests/test-admin-deploy-2.sh +++ b/tests/test-admin-deploy-2.sh @@ -26,7 +26,7 @@ set -euo pipefail # Exports OSTREE_SYSROOT so --sysroot not needed. setup_os_repository "archive" "syslinux" -echo "1..6" +echo "1..7" ${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime rev=$(${CMD_PREFIX} ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime) @@ -64,6 +64,19 @@ assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/etc/os-r echo "ok manual cleanup" +# Commit + upgrade twice, so that we'll rotate out the original deployment +os_repository_new_commit "1" +${CMD_PREFIX} ostree admin upgrade --os=testos +oldversion=${version} +# another commit with *same* bootcsum but *new* content +os_repository_new_commit "1" "2" +newversion=${version} +assert_file_has_content sysroot/boot/loader/entries/ostree-testos-0.conf ${oldversion} +${CMD_PREFIX} ostree admin upgrade --os=testos +assert_file_has_content sysroot/boot/loader/entries/ostree-testos-0.conf ${newversion} + +echo "ok new version same bootcsum" + assert_n_pinned() { local n=$1 ${CMD_PREFIX} ostree admin status > status.txt diff --git a/tests/test-concurrency.py b/tests/test-concurrency.py index 3ec3681c..e4ce21e9 100755 --- a/tests/test-concurrency.py +++ b/tests/test-concurrency.py @@ -44,7 +44,6 @@ subprocess.check_call(['ostree', '--repo=repo', 'init', '--mode=bare']) # and we don't need xattr coverage for this with open('repo/config', 'a') as f: f.write('disable-xattrs=true\n') - f.write('locking=true\n') def commit(v): tdir='tree{}'.format(v) diff --git a/tests/test-corruption.sh b/tests/test-corruption.sh index 997d39c7..f26ed2d2 100755 --- a/tests/test-corruption.sh +++ b/tests/test-corruption.sh @@ -21,7 +21,7 @@ set -euo pipefail -echo "1..6" +echo "1..8" . $(dirname $0)/libtest.sh @@ -89,3 +89,43 @@ if ${CMD_PREFIX} ostree --repo=ostree-path-traverse/repo checkout pathtraverse-t fi assert_file_has_content_literal err.txt 'Invalid / in filename ../afile' echo "ok path traverse checkout" + +cd ${test_tmpdir} +rm repo files -rf +setup_test_repository "bare" + +rev=$($OSTREE rev-parse test2) + +filechecksum=$(ostree_file_path_to_checksum repo test2 /firstfile) +rm repo/$(ostree_checksum_to_relative_object_path repo $filechecksum) + +assert_not_has_file repo/state/${rev}.commitpartial + +if $OSTREE fsck -q 2>err.txt; then + assert_not_reached "fsck unexpectedly succeeded" +fi +assert_file_has_content_literal err.txt "Object missing:" +assert_file_has_content_literal err.txt "Marking commit as partial: $rev" +assert_has_file repo/state/${rev}.commitpartial + +echo "ok missing file" + +cd ${test_tmpdir} +rm repo files -rf +setup_test_repository "bare" + +rev=$($OSTREE rev-parse test2) + +filechecksum=$(ostree_file_path_to_checksum repo test2 /firstfile) +echo corrupted >> repo/$(ostree_checksum_to_relative_object_path repo $filechecksum) + +assert_not_has_file repo/state/${rev}.commitpartial + +if $OSTREE fsck -q --delete 2>err.txt; then + assert_not_reached "fsck unexpectedly succeeded" +fi +assert_file_has_content_literal err.txt "Corrupted file object;" +assert_file_has_content_literal err.txt "Marking commit as partial: $rev" +assert_has_file repo/state/${rev}.commitpartial + +echo "ok corrupt file" diff --git a/tests/test-create-usb.sh b/tests/test-create-usb.sh index e057e90f..392352fd 100755 --- a/tests/test-create-usb.sh +++ b/tests/test-create-usb.sh @@ -94,9 +94,12 @@ assert_has_dir "dest-mount3/some-dest" assert_symlink_has_content "dest-mount3/.ostree/repos.d/00-generated" "/some-dest$" ${CMD_PREFIX} ostree --repo=dest-mount3/.ostree/repos.d/00-generated refs --collections > dest-refs assert_file_has_content dest-refs "^(org.example.Collection1, test-1)$" -assert_file_has_content dest-refs "^(org.example.Collection1, test-1)$" +assert_file_has_content dest-refs "^(org.example.Collection1, test-2)$" assert_file_has_content dest-refs "^(org.example.Collection1, test-3)$" -assert_has_file dest-mount3/.ostree/repos.d/00-generated/summary +${CMD_PREFIX} ostree --repo=dest-mount3/.ostree/repos.d/00-generated summary -v > dest-summary +assert_file_has_content dest-summary "(org.example.Collection1, test-1)$" +assert_file_has_content dest-summary "(org.example.Collection1, test-2)$" +assert_file_has_content dest-summary "(org.example.Collection1, test-3)$" echo "ok 4 adding ref to an existing usb" diff --git a/tests/test-repo-finder-config.c b/tests/test-repo-finder-config.c index 61d49b48..a87e3f4b 100644 --- a/tests/test-repo-finder-config.c +++ b/tests/test-repo-finder-config.c @@ -303,6 +303,130 @@ test_repo_finder_config_mixed_configs (Fixture *fixture, g_main_context_pop_thread_default (context); } +/* Test that using ostree_repo_find_remotes_async() works too.*/ +static void +test_repo_finder_config_find_remotes (Fixture *fixture, + gconstpointer test_data) +{ + g_autoptr(OstreeRepoFinder) finder = NULL; + g_autoptr(GMainContext) context = NULL; + g_autoptr(GAsyncResult) result = NULL; + g_auto(OstreeRepoFinderResultv) results = NULL; + g_autoptr(GError) error = NULL; + gsize i; + const OstreeCollectionRef ref0 = { "org.example.Collection0", "exampleos/x86_64/ref0" }; + const OstreeCollectionRef ref1 = { "org.example.Collection0", "exampleos/x86_64/ref1" }; + const OstreeCollectionRef ref2 = { "org.example.Collection1", "exampleos/x86_64/ref1" }; + const OstreeCollectionRef ref3 = { "org.example.Collection1", "exampleos/x86_64/ref2" }; + const OstreeCollectionRef ref4 = { "org.example.Collection2", "exampleos/x86_64/ref3" }; + const OstreeCollectionRef * const refs[] = { &ref0, &ref1, &ref2, &ref3, &ref4, NULL }; + OstreeRepoFinder *finders[2] = {NULL, }; + + context = g_main_context_new (); + g_main_context_push_thread_default (context); + + /* Put together various ref configuration files. */ + g_autofree gchar *collection0_uri = assert_create_remote (fixture, "org.example.Collection0", + "exampleos/x86_64/ref0", + "exampleos/x86_64/ref1", + NULL); + g_autofree gchar *collection1_uri = assert_create_remote (fixture, "org.example.Collection1", + "exampleos/x86_64/ref2", + NULL); + g_autofree gchar *no_collection_uri = assert_create_remote (fixture, NULL, + "exampleos/x86_64/ref3", + NULL); + + assert_create_remote_config (fixture->parent_repo, "remote0", collection0_uri, "org.example.Collection0"); + assert_create_remote_config (fixture->parent_repo, "remote1", collection1_uri, "org.example.Collection1"); + assert_create_remote_config (fixture->parent_repo, "remote0-copy", collection0_uri, "org.example.Collection0"); + assert_create_remote_config (fixture->parent_repo, "remote1-bad-copy", collection1_uri, "org.example.NotCollection1"); + assert_create_remote_config (fixture->parent_repo, "remote2", no_collection_uri, NULL); + + finders[0] = OSTREE_REPO_FINDER (ostree_repo_finder_config_new ()); + + /* Resolve the refs. */ + ostree_repo_find_remotes_async (fixture->parent_repo, refs, + NULL, finders, + NULL, NULL, result_cb, &result); + + while (result == NULL) + g_main_context_iteration (context, TRUE); + + results = ostree_repo_find_remotes_finish (fixture->parent_repo, + result, &error); + g_assert_no_error (error); + g_assert_nonnull (results); + g_assert_cmpuint (g_strv_length ((char **) results), ==, 3); + + /* Check that the results are correct: the invalid refs should have been + * ignored, and the valid results canonicalised and deduplicated. */ + for (i = 0; results[i] != NULL; i++) + { + const char *ref0_checksum, *ref1_checksum, *ref2_checksum, *ref3_checksum; + guint64 *ref0_timestamp, *ref1_timestamp, *ref2_timestamp, *ref3_timestamp; + + if (g_strcmp0 (ostree_remote_get_name (results[i]->remote), "remote0") == 0 || + g_strcmp0 (ostree_remote_get_name (results[i]->remote), "remote0-copy") == 0) + { + g_assert_cmpuint (g_hash_table_size (results[i]->ref_to_checksum), ==, 5); + + ref0_checksum = g_hash_table_lookup (results[i]->ref_to_checksum, &ref0); + g_assert_true (ostree_validate_checksum_string (ref0_checksum, NULL)); + + ref1_checksum = g_hash_table_lookup (results[i]->ref_to_checksum, &ref1); + g_assert_true (ostree_validate_checksum_string (ref1_checksum, NULL)); + + ref2_checksum = g_hash_table_lookup (results[i]->ref_to_checksum, &ref2); + g_assert (ref2_checksum == NULL); + + g_assert_cmpuint (g_hash_table_size (results[i]->ref_to_timestamp), ==, 5); + + ref0_timestamp = g_hash_table_lookup (results[i]->ref_to_timestamp, &ref0); + *ref0_timestamp = GUINT64_FROM_BE (*ref0_timestamp); + g_assert_cmpuint (*ref0_timestamp, >, 0); + + ref1_timestamp = g_hash_table_lookup (results[i]->ref_to_timestamp, &ref1); + *ref1_timestamp = GUINT64_FROM_BE (*ref1_timestamp); + g_assert_cmpuint (*ref1_timestamp, >, 0); + + ref2_timestamp = g_hash_table_lookup (results[i]->ref_to_timestamp, &ref2); + *ref2_timestamp = GUINT64_FROM_BE (*ref2_timestamp); + g_assert_cmpuint (*ref2_timestamp, ==, 0); + + g_assert_cmpstr (ostree_remote_get_url (results[i]->remote), ==, collection0_uri); + } + else if (g_strcmp0 (ostree_remote_get_name (results[i]->remote), "remote1") == 0) + { + g_assert_cmpuint (g_hash_table_size (results[i]->ref_to_checksum), ==, 5); + + ref3_checksum = g_hash_table_lookup (results[i]->ref_to_checksum, &ref3); + g_assert_true (ostree_validate_checksum_string (ref3_checksum, NULL)); + + ref0_checksum = g_hash_table_lookup (results[i]->ref_to_checksum, &ref0); + g_assert (ref0_checksum == NULL); + + g_assert_cmpuint (g_hash_table_size (results[i]->ref_to_timestamp), ==, 5); + + ref3_timestamp = g_hash_table_lookup (results[i]->ref_to_timestamp, &ref3); + *ref3_timestamp = GUINT64_FROM_BE (*ref3_timestamp); + g_assert_cmpuint (*ref3_timestamp, >, 0); + + ref0_timestamp = g_hash_table_lookup (results[i]->ref_to_timestamp, &ref0); + *ref0_timestamp = GUINT64_FROM_BE (*ref0_timestamp); + g_assert_cmpuint (*ref0_timestamp, ==, 0); + + g_assert_cmpstr (ostree_remote_get_url (results[i]->remote), ==, collection1_uri); + } + else + { + g_assert_not_reached (); + } + } + + g_main_context_pop_thread_default (context); +} + int main (int argc, char **argv) { setlocale (LC_ALL, ""); @@ -313,6 +437,8 @@ int main (int argc, char **argv) test_repo_finder_config_no_configs, teardown); g_test_add ("/repo-finder-config/mixed-configs", Fixture, NULL, setup, test_repo_finder_config_mixed_configs, teardown); + g_test_add ("/repo-finder-config/find-remotes", Fixture, NULL, setup, + test_repo_finder_config_find_remotes, teardown); return g_test_run(); } diff --git a/tests/test-symbols.sh b/tests/test-symbols.sh index 13b47afb..501bedeb 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 <