diff --git a/Makefile-boot.am b/Makefile-boot.am
index 7ec54fa3..8b62d1ba 100644
--- a/Makefile-boot.am
+++ b/Makefile-boot.am
@@ -21,7 +21,8 @@ if BUILDOPT_DRACUT
# Not using $(libdir) here is intentional, dracut modules go in prefix/lib
dracutmoddir = $(prefix)/lib/dracut/modules.d/98ostree
dracutmod_SCRIPTS = src/boot/dracut/module-setup.sh
-
+endif
+if BUILDOPT_DRACUT_CONF
dracutconfdir = $(sysconfdir)/dracut.conf.d
dracutconf_DATA = src/boot/dracut/ostree.conf
endif
diff --git a/Makefile-decls.am b/Makefile-decls.am
index d8ec5ab4..f7ebd422 100644
--- a/Makefile-decls.am
+++ b/Makefile-decls.am
@@ -18,7 +18,7 @@
# Common variables
AM_CPPFLAGS =
AM_CFLAGS =
-DISTCHECK_CONFIGURE_FLAGS =
+AM_DISTCHECK_CONFIGURE_FLAGS =
SUBDIRS =
NULL =
BUILT_SOURCES =
diff --git a/Makefile-libostree-defines.am b/Makefile-libostree-defines.am
index f623900c..2d478bb6 100644
--- a/Makefile-libostree-defines.am
+++ b/Makefile-libostree-defines.am
@@ -21,7 +21,9 @@
libostree_public_headers = \
src/libostree/ostree.h \
src/libostree/ostree-async-progress.h \
+ src/libostree/ostree-autocleanups.h \
src/libostree/ostree-core.h \
+ src/libostree/ostree-dummy-enumtypes.h \
src/libostree/ostree-mutable-tree.h \
src/libostree/ostree-repo.h \
src/libostree/ostree-types.h \
diff --git a/Makefile-libostree.am b/Makefile-libostree.am
index a50b2b9d..d6b83528 100644
--- a/Makefile-libostree.am
+++ b/Makefile-libostree.am
@@ -33,9 +33,11 @@ lib_LTLIBRARIES += libostree-1.la
libostreeincludedir = $(includedir)/ostree-1
libostreeinclude_HEADERS = $(libostree_public_headers)
-ENUM_TYPES = \
- $(srcdir)/src/libostree/ostree-fetcher.h \
- $(NULL)
+ENUM_TYPES = $(NULL)
+
+if USE_LIBSOUP
+ENUM_TYPES += $(srcdir)/src/libostree/ostree-fetcher.h
+endif
src/libostree/ostree-enumtypes.h: src/libostree/ostree-enumtypes.h.template $(ENUM_TYPES)
$(AM_V_GEN) $(GLIB_MKENUMS) \
@@ -48,12 +50,14 @@ src/libostree/ostree-enumtypes.c: src/libostree/ostree-enumtypes.c.template $(EN
--fhead "#include \"ostree-enumtypes.h\"" \
$(ENUM_TYPES) > $@.tmp && mv $@.tmp $@
+if USE_LIBSOUP
ENUM_GENERATED = \
src/libostree/ostree-enumtypes.h \
src/libostree/ostree-enumtypes.c \
$(NULL)
BUILT_SOURCES += $(ENUM_GENERATED)
+endif
CLEANFILES += $(BUILT_SOURCES)
@@ -69,6 +73,7 @@ libostree_1_la_SOURCES = \
src/libostree/ostree-cmdprivate.c \
src/libostree/ostree-core-private.h \
src/libostree/ostree-core.c \
+ src/libostree/ostree-dummy-enumtypes.c \
src/libostree/ostree-checksum-input-stream.c \
src/libostree/ostree-checksum-input-stream.h \
src/libostree/ostree-chain-input-stream.c \
@@ -90,6 +95,7 @@ libostree_1_la_SOURCES = \
src/libostree/ostree-repo.c \
src/libostree/ostree-repo-checkout.c \
src/libostree/ostree-repo-commit.c \
+ src/libostree/ostree-repo-pull.c \
src/libostree/ostree-repo-libarchive.c \
src/libostree/ostree-repo-prune.c \
src/libostree/ostree-repo-refs.c \
@@ -123,6 +129,7 @@ libostree_1_la_SOURCES = \
src/libostree/ostree-gpg-verifier.h \
src/libostree/ostree-gpg-verify-result.c \
src/libostree/ostree-gpg-verify-result-private.h \
+ src/libostree/ostree-autocleanups.h \
$(NULL)
if USE_LIBARCHIVE
libostree_1_la_SOURCES += src/libostree/ostree-libarchive-input-stream.h \
@@ -142,6 +149,8 @@ libostree_1_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/bsdiff -I$(srcdir)/libglnx -I$(
libostree_1_la_LDFLAGS = -version-number 1:0:0 -Bsymbolic-functions -Wl,--version-script=$(top_srcdir)/src/libostree/libostree.sym
libostree_1_la_LIBADD = libotutil.la libbupsplit.la libglnx.la libbsdiff.la libostree-kernel-args.la $(OT_INTERNAL_GIO_UNIX_LIBS) $(OT_INTERNAL_GPGME_LIBS) $(OT_DEP_LZMA_LIBS) $(OT_DEP_ZLIB_LIBS)
+EXTRA_DIST += src/libostree/libostree.sym
+
if USE_LIBARCHIVE
libostree_1_la_CFLAGS += $(OT_DEP_LIBARCHIVE_CFLAGS)
libostree_1_la_LIBADD += $(OT_DEP_LIBARCHIVE_LIBS)
@@ -153,7 +162,6 @@ libostree_1_la_SOURCES += \
src/libostree/ostree-fetcher.c \
src/libostree/ostree-metalink.h \
src/libostree/ostree-metalink.c \
- src/libostree/ostree-repo-pull.c \
$(NULL)
libostree_1_la_CFLAGS += $(OT_INTERNAL_SOUP_CFLAGS)
libostree_1_la_LIBADD += $(OT_INTERNAL_SOUP_LIBS)
diff --git a/Makefile-tests.am b/Makefile-tests.am
index b3d75142..80903071 100644
--- a/Makefile-tests.am
+++ b/Makefile-tests.am
@@ -19,18 +19,25 @@
include $(top_srcdir)/buildutil/glib-tap.mk
+EXTRA_DIST += \
+ buildutil/tap-driver.sh \
+ buildutil/tap-test \
+ $(NULL)
+
# We should probably consider flipping the default for DEBUG. Also,
# include the builddir in $PATH so we find our just-built ostree
# binary.
TESTS_ENVIRONMENT += OT_TESTS_DEBUG=1 \
GI_TYPELIB_PATH=$$(cd $(top_builddir) && pwd) \
LD_LIBRARY_PATH=$$(cd $(top_builddir)/.libs && pwd) \
- PATH=$$(cd $(top_builddir) && pwd):$${PATH} \
+ PATH=$$(cd $(top_builddir)/tests && pwd):$${PATH} \
$(NULL)
-uninstalled_test_scripts = tests/test-abi.sh
+uninstalled_test_data = tests/ostree-symlink-stamp
-test_scripts = \
+dist_uninstalled_test_scripts = tests/test-symbols.sh
+
+dist_test_scripts = \
tests/test-basic.sh \
tests/test-pull-subpath.sh \
tests/test-archivez.sh \
@@ -51,6 +58,7 @@ test_scripts = \
tests/test-pull-summary-sigs.sh \
tests/test-pull-resume.sh \
tests/test-pull-untrusted.sh \
+ tests/test-pull-override-url.sh \
tests/test-local-pull.sh \
tests/test-local-pull-depth.sh \
tests/test-gpg-signed-commit.sh \
@@ -62,6 +70,7 @@ test_scripts = \
tests/test-admin-deploy-etcmerge-cornercases.sh \
tests/test-admin-deploy-uboot.sh \
tests/test-admin-deploy-grub2.sh \
+ tests/test-admin-deploy-bootid-gc.sh \
tests/test-admin-instutil-set-kargs.sh \
tests/test-admin-upgrade-not-backwards.sh \
tests/test-admin-pull-deploy-commit.sh \
@@ -70,7 +79,6 @@ test_scripts = \
tests/test-repo-checkout-subpath.sh \
tests/test-reset-nonlinear.sh \
tests/test-oldstyle-partial.sh \
- tests/test-setuid.sh \
tests/test-delta.sh \
tests/test-xattrs.sh \
tests/test-auto-summary.sh \
@@ -80,15 +88,15 @@ test_scripts = \
$(NULL)
if BUILDOPT_FUSE
-test_scripts += tests/test-rofiles-fuse.sh
+dist_test_scripts += tests/test-rofiles-fuse.sh
endif
# This one uses corrupt-repo-ref.js
if BUILDOPT_GJS
-test_scripts += tests/test-corruption.sh
+dist_test_scripts += tests/test-corruption.sh
endif
-installed_test_data = tests/archive-test.sh \
+dist_installed_test_data = tests/archive-test.sh \
tests/pull-test.sh \
tests/libtest.sh \
tests/admin-test.sh \
@@ -99,29 +107,29 @@ installed_test_data = tests/archive-test.sh \
tests/pre-endian-deltas-repo-little.tar.xz \
$(NULL)
-test_extra_scripts = tests/bootloader-entries-crosscheck.py \
+dist_test_extra_scripts = tests/bootloader-entries-crosscheck.py \
tests/ostree-grub-generator
# We can't use nobase_ as we need to strip off the tests/, can't
# use plain installed_ as we do need the gpghome/ prefix.
if ENABLE_INSTALLED_TESTS
gpginsttestdir = $(installed_testdir)/gpghome
-gpginsttest_DATA = tests/gpghome/secring.gpg \
+dist_gpginsttest_DATA = tests/gpghome/secring.gpg \
tests/gpghome/pubring.gpg \
tests/gpghome/trustdb.gpg \
tests/gpghome/key1.asc \
tests/gpghome/key2.asc \
tests/gpghome/key3.asc
gpginsttest_trusteddir = $(installed_testdir)/gpghome/trusted
-gpginsttest_trusted_DATA = tests/gpghome/trusted/pubring.gpg
+dist_gpginsttest_trusted_DATA = tests/gpghome/trusted/pubring.gpg
gpgvinsttestdir = $(installed_testdir)/gpg-verify-data
-gpgvinsttest_DATA = $(addprefix tests/gpg-verify-data/, \
+dist_gpgvinsttest_DATA = $(addprefix tests/gpg-verify-data/, \
gpg.conf lgpl2 lgpl2.sig pubring.gpg secring.gpg trustdb.gpg)
endif
if BUILDOPT_GJS
-installed_test_scripts = tests/test-core.js \
+dist_installed_test_scripts = tests/test-core.js \
tests/test-sizes.js \
tests/test-sysroot.js \
$(NULL)
@@ -129,9 +137,12 @@ endif
test_ltlibraries = libreaddir-rand.la
libreaddir_rand_la_SOURCES = tests/readdir-rand.c
-libreaddir_rand_la_CFLAGS = $(OT_INTERNAL_GIO_UNIX_CFLAGS)
-libreaddir_rand_la_LIBADD = $(OT_INTERNAL_GIO_UNIX_LIBS)
-libreaddir_rand_la_LDFLAGS = -avoid-version
+libreaddir_rand_la_CFLAGS = $(AM_CFLAGS) $(OT_INTERNAL_GIO_UNIX_CFLAGS)
+libreaddir_rand_la_LIBADD = \
+ -ldl \
+ $(OT_INTERNAL_GIO_UNIX_LIBS) \
+ $(NULL)
+libreaddir_rand_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
if !ENABLE_INSTALLED_TESTS
libreaddir_rand_la_LDFLAGS += -rpath $(abs_builddir)
endif
@@ -139,7 +150,7 @@ endif
test_programs = tests/test-varint tests/test-ot-unix-utils tests/test-bsdiff tests/test-mutable-tree \
tests/test-keyfile-utils tests/test-ot-opt-utils tests/test-ot-tool-util \
tests/test-gpg-verify-result tests/test-checksum tests/test-lzma tests/test-rollsum \
- tests/test-basic-c tests/test-sysroot-c
+ tests/test-basic-c tests/test-sysroot-c tests/test-pull-c
# An interactive tool
noinst_PROGRAMS += tests/test-rollsum-cli
@@ -176,6 +187,9 @@ tests_test_basic_c_LDADD = $(TESTS_LDADD)
tests_test_sysroot_c_CFLAGS = $(TESTS_CFLAGS)
tests_test_sysroot_c_LDADD = $(TESTS_LDADD)
+tests_test_pull_c_CFLAGS = $(TESTS_CFLAGS)
+tests_test_pull_c_LDADD = $(TESTS_LDADD)
+
tests_test_ot_unix_utils_CFLAGS = $(TESTS_CFLAGS)
tests_test_ot_unix_utils_LDADD = $(TESTS_LDADD)
@@ -216,6 +230,7 @@ tests_test_gpg_verify_result_LDADD = $(TESTS_LDADD) $(OT_INTERNAL_GPGME_LIBS)
EXTRA_DIST += \
tests/libostreetest.h \
+ tests/libtest.sh \
tests/gpg-verify-data/README.md \
tests/gpg-verify-data/lgpl2 \
tests/gpg-verify-data/lgpl2.sig \
@@ -227,7 +242,12 @@ EXTRA_DIST += \
tests/libreaddir-rand.so: Makefile
$(AM_V_GEN) ln -fns ../.libs/libreaddir-rand.so tests
ALL_LOCAL_RULES += tests/libreaddir-rand.so
-CLEANFILES += tests/libreaddir-rand.so
+CLEANFILES += tests/libreaddir-rand.so tests/ostree-symlink-stamp tests/ostree
+
+tests/ostree-symlink-stamp: Makefile
+ @real_bin=`cd $(top_builddir) && libtool --mode=execute echo ostree`; \
+ ln -sf "$${real_bin}" tests/ostree; \
+ touch $@
# Unfortunately the glib test data APIs don't actually handle
# non-recursive Automake, so we change our code to canonically look
diff --git a/Makefile.am b/Makefile.am
index 488d4b6d..1de31544 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -24,11 +24,12 @@ AM_CPPFLAGS += -DDATADIR='"$(datadir)"' -DLIBEXECDIR='"$(libexecdir)"' \
-DLOCALEDIR=\"$(datadir)/locale\" -DSYSCONFDIR=\"$(sysconfdir)\" \
-DSHORTENED_SYSCONFDIR=\"$(shortened_sysconfdir)\" \
-DOSTREE_FEATURES='"$(OSTREE_FEATURES)"' \
+ -DOSTREE_COMPILATION \
-DG_LOG_DOMAIN=\"OSTree\" \
-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_40 -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_40 \
-DSOUP_VERSION_MIN_REQUIRED=SOUP_VERSION_2_40 -DSOUP_VERSION_MAX_ALLOWED=SOUP_VERSION_2_48
-AM_CFLAGS += $(WARN_CFLAGS)
-DISTCHECK_CONFIGURE_FLAGS += --enable-gtk-doc --disable-maintainer-mode
+AM_CFLAGS += -std=gnu99 $(WARN_CFLAGS)
+AM_DISTCHECK_CONFIGURE_FLAGS += --enable-gtk-doc --disable-maintainer-mode
GITIGNOREFILES = aclocal.m4 build-aux/ buildutil/*.m4 config.h.in gtk-doc.make
@@ -71,7 +72,9 @@ include Makefile-otutil.am
include Makefile-libostree.am
include Makefile-ostree.am
include Makefile-switchroot.am
+if BUILDOPT_FUSE
include src/rofiles-fuse/Makefile-inc.am
+endif
include Makefile-tests.am
include Makefile-boot.am
include Makefile-man.am
diff --git a/README.md b/README.md
index 15cba7ab..caa8503d 100644
--- a/README.md
+++ b/README.md
@@ -44,7 +44,7 @@ provide a minimal host for Docker formatted Linux containers.
Replicating a base immutable OS, then using Docker for applications
meshes together two different tools with different tradeoffs.
-[xdg-app](https://github.com/alexlarsson/xdg-app) uses OSTree
+[flatpak](https://github.com/alexlarsson/xdg-app) uses OSTree
for desktop application containers.
[GNOME Continuous](https://wiki.gnome.org/Projects/GnomeContinuous) is
diff --git a/apidoc/.gitignore b/apidoc/.gitignore
index 77cacd4d..80d2c112 100644
--- a/apidoc/.gitignore
+++ b/apidoc/.gitignore
@@ -32,6 +32,7 @@
/ostree.args
/ostree.hierarchy
/ostree.interfaces
+/ostree-overrides.txt
/ostree.pdf
/ostree.prerequisites
/ostree.signals
@@ -48,4 +49,5 @@
/tmpl/*.bak
/tmpl/ostree-unused.sgml
/xml
+/version.xml
_libs
diff --git a/apidoc/ostree-docs.xml b/apidoc/ostree-docs.xml
index c5ea28e6..8721ffa8 100644
--- a/apidoc/ostree-docs.xml
+++ b/apidoc/ostree-docs.xml
@@ -13,25 +13,23 @@
API Reference
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
API Index
-
diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt
index 1eef5da7..6dca9ecc 100644
--- a/apidoc/ostree-sections.txt
+++ b/apidoc/ostree-sections.txt
@@ -1,5 +1,5 @@
-libostree-async-progress
+ostree-async-progress
OstreeAsyncProgress
ostree_async_progress_new
ostree_async_progress_new_and_connect
@@ -22,12 +22,14 @@ ostree_async_progress_get_type
-libostree-bootconfig-parser
+ostree-bootconfig-parser
OstreeBootconfigParser
ostree_bootconfig_parser_new
ostree_bootconfig_parser_clone
ostree_bootconfig_parser_parse
+ostree_bootconfig_parser_parse_at
ostree_bootconfig_parser_write
+ostree_bootconfig_parser_write_at
ostree_bootconfig_parser_set
ostree_bootconfig_parser_get
@@ -38,7 +40,7 @@ ostree_bootconfig_parser_get_type
-libostree-chain-input-stream
+ostree-chain-input-stream
OstreeChainInputStream
ostree_chain_input_stream_new
@@ -54,7 +56,7 @@ ostree_chain_input_stream_get_type
-libostree-checksum-input-stream
+ostree-checksum-input-stream
OstreeChecksumInputStream
ostree_checksum_input_stream_new
@@ -70,7 +72,7 @@ ostree_checksum_input_stream_get_type
-libostree-core
+ostree-core
OSTREE_MAX_METADATA_SIZE
OSTREE_MAX_METADATA_WARN_SIZE
OSTREE_MAX_RECURSION
@@ -97,10 +99,11 @@ ostree_checksum_inplace_from_bytes
ostree_checksum_inplace_to_bytes
ostree_checksum_bytes_peek
ostree_checksum_bytes_peek_validate
+ostree_checksum_b64_inplace_from_bytes
+ostree_checksum_b64_inplace_to_bytes
ostree_cmp_checksum_bytes
ostree_validate_rev
ostree_parse_refspec
-ostree_checksum_update_meta
ostree_object_type_to_string
ostree_object_type_from_string
ostree_hash_object_name
@@ -110,6 +113,8 @@ ostree_object_to_string
ostree_object_from_string
ostree_content_stream_parse
ostree_content_file_parse
+ostree_content_file_parse_at
+ostree_raw_file_to_archive_z2_stream
ostree_raw_file_to_content_stream
ostree_checksum_file_from_input
ostree_checksum_file
@@ -128,7 +133,7 @@ ostree_commit_get_timestamp
-libostree-deployment
+ostree-deployment
OstreeDeployment
ostree_deployment_hash
ostree_deployment_equal
@@ -141,11 +146,14 @@ ostree_deployment_get_bootcsum
ostree_deployment_get_bootserial
ostree_deployment_get_bootconfig
ostree_deployment_get_origin
+ostree_deployment_get_origin_relpath
+ostree_deployment_get_unlocked
ostree_deployment_set_index
ostree_deployment_set_bootserial
ostree_deployment_set_bootconfig
ostree_deployment_set_origin
ostree_deployment_clone
+ostree_deployment_unlocked_state_to_string
OSTREE_DEPLOYMENT
OSTREE_IS_DEPLOYMENT
@@ -154,7 +162,7 @@ ostree_deployment_get_type
-libostree-diff
+ostree-diff
OstreeDiffFlags
OstreeDiffItem
ostree_diff_item_ref
@@ -166,7 +174,7 @@ ostree_diff_item_get_type
-libostree-gpg-verify-result
+ostree-gpg-verify-result
OstreeGpgVerifyResult
OstreeGpgSignatureAttr
ostree_gpg_verify_result_count_all
@@ -177,6 +185,7 @@ ostree_gpg_verify_result_get_all
OstreeGpgSignatureFormatFlags
ostree_gpg_verify_result_describe
ostree_gpg_verify_result_describe_variant
+ostree_gpg_verify_result_require_valid_signature
OSTREE_GPG_VERIFY_RESULT
OSTREE_IS_GPG_VERIFY_RESULT
@@ -184,8 +193,33 @@ OSTREE_TYPE_GPG_VERIFY_RESULT
ostree_gpg_verify_result_get_type
+ostree-lzma-compressor
+
+OSTREE_IS_LZMA_COMPRESSOR
+OSTREE_IS_LZMA_COMPRESSOR_CLASS
+OSTREE_LZMA_COMPRESSOR
+OSTREE_LZMA_COMPRESSOR_CLASS
+OSTREE_LZMA_COMPRESSOR_GET_CLASS
+OSTREE_TYPE_LZMA_COMPRESSOR
+OstreeLzmaCompressor
+OstreeLzmaCompressorClass
+
+
-libostree-mutable-tree
+ostree-lzma-decompressor
+
+OSTREE_IS_LZMA_DECOMPRESSOR
+OSTREE_IS_LZMA_DECOMPRESSOR_CLASS
+OSTREE_LZMA_DECOMPRESSOR
+OSTREE_LZMA_DECOMPRESSOR_CLASS
+OSTREE_LZMA_DECOMPRESSOR_GET_CLASS
+OSTREE_TYPE_LZMA_DECOMPRESSOR
+OstreeLzmaDecompressor
+OstreeLzmaDecompressorClass
+
+
+
+ostree-mutable-tree
OstreeMutableTree
ostree_mutable_tree_new
ostree_mutable_tree_set_metadata_checksum
@@ -211,7 +245,7 @@ ostree_mutable_tree_get_type
-libostree-repo
+ostree-repo
OstreeRepo
OstreeRepoMode
ostree_repo_mode_from_string
@@ -227,6 +261,7 @@ ostree_repo_create
ostree_repo_get_path
ostree_repo_get_mode
ostree_repo_get_config
+ostree_repo_get_dfd
ostree_repo_copy_config
ostree_repo_remote_add
ostree_repo_remote_delete
@@ -238,6 +273,10 @@ ostree_repo_remote_get_gpg_verify
ostree_repo_remote_get_gpg_verify_summary
ostree_repo_remote_gpg_import
ostree_repo_remote_fetch_summary
+ostree_repo_remote_fetch_summary_with_options
+ostree_repo_get_remote_boolean_option
+ostree_repo_get_remote_list_option
+ostree_repo_get_remote_option
ostree_repo_get_parent
ostree_repo_write_config
OstreeRepoTransactionStats
@@ -248,6 +287,8 @@ ostree_repo_abort_transaction
ostree_repo_transaction_set_refspec
ostree_repo_transaction_set_ref
ostree_repo_set_ref_immediate
+ostree_repo_set_cache_dir
+ostree_repo_sign_delta
ostree_repo_has_object
ostree_repo_write_metadata
ostree_repo_write_metadata_async
@@ -270,6 +311,8 @@ ostree_repo_load_object_stream
ostree_repo_query_object_storage_size
ostree_repo_import_object_from
ostree_repo_import_object_from_with_trust
+ostree_repo_import_archive_to_mtree
+ostree_repo_export_tree_to_archive
ostree_repo_delete_object
OstreeRepoCommitFilterResult
OstreeRepoCommitFilter
@@ -285,6 +328,7 @@ ostree_repo_commit_modifier_unref
ostree_repo_devino_cache_new
ostree_repo_devino_cache_ref
ostree_repo_devino_cache_unref
+ostree_repo_devino_cache_get_type
ostree_repo_write_directory_to_mtree
ostree_repo_write_dfd_to_mtree
ostree_repo_write_archive_to_mtree
@@ -310,8 +354,16 @@ ostree_repo_static_delta_execute_offline
ostree_repo_traverse_new_reachable
ostree_repo_traverse_commit
ostree_repo_traverse_commit_union
+ostree_repo_commit_traverse_iter_cleanup
+ostree_repo_commit_traverse_iter_clear
+ostree_repo_commit_traverse_iter_get_dir
+ostree_repo_commit_traverse_iter_get_file
+ostree_repo_commit_traverse_iter_init_commit
+ostree_repo_commit_traverse_iter_init_dirtree
+ostree_repo_commit_traverse_iter_next
OstreeRepoPruneFlags
ostree_repo_prune
+ostree_repo_prune_static_deltas
OstreeRepoPullFlags
ostree_repo_pull
ostree_repo_pull_one_dir
@@ -319,6 +371,8 @@ ostree_repo_pull_with_options
ostree_repo_pull_default_console_progress_changed
ostree_repo_sign_commit
ostree_repo_append_gpg_signature
+ostree_repo_add_gpg_signature_summary
+ostree_repo_gpg_verify_data
ostree_repo_verify_commit
ostree_repo_verify_commit_ext
ostree_repo_verify_summary
@@ -333,13 +387,12 @@ ostree_repo_transaction_stats_get_type
-libostree-repo-file
+ostree-repo-file
OstreeRepoFile
ostree_repo_file_ensure_resolved
ostree_repo_file_get_xattrs
ostree_repo_file_get_repo
ostree_repo_file_get_root
-ostree_repo_file_make_empty_tree
ostree_repo_file_tree_set_metadata
ostree_repo_file_tree_get_contents_checksum
ostree_repo_file_tree_get_metadata_checksum
@@ -360,14 +413,17 @@ ostree_repo_file_get_type
-libostree-sepolicy
+ostree-sepolicy
OstreeSePolicy
ostree_sepolicy_new
ostree_sepolicy_get_path
ostree_sepolicy_get_name
ostree_sepolicy_get_label
+ostree_sepolicy_get_csum
OstreeSePolicyRestoreconFlags
ostree_sepolicy_restorecon
+ostree_sepolicy_setfscreatecon
+ostree_sepolicy_fscreatecon_cleanup
OSTREE_SEPOLICY
OSTREE_IS_SEPOLICY
@@ -376,7 +432,7 @@ ostree_sepolicy_get_type
-libostree-sysroot
+ostree-sysroot
OstreeSysroot
ostree_sysroot_new
ostree_sysroot_new_default
@@ -388,6 +444,7 @@ ostree_sysroot_try_lock
ostree_sysroot_lock_async
ostree_sysroot_lock_finish
ostree_sysroot_unlock
+ostree_sysroot_unload
ostree_sysroot_get_fd
ostree_sysroot_ensure_initialized
ostree_sysroot_get_bootversion
@@ -395,13 +452,17 @@ ostree_sysroot_get_subbootversion
ostree_sysroot_get_deployments
ostree_sysroot_get_booted_deployment
ostree_sysroot_get_deployment_directory
+ostree_sysroot_get_deployment_dirpath
ostree_sysroot_get_deployment_origin_path
ostree_sysroot_cleanup
ostree_sysroot_prepare_cleanup
ostree_sysroot_get_repo
ostree_sysroot_init_osname
ostree_sysroot_deployment_set_kargs
+ostree_sysroot_deployment_set_mutable
+ostree_sysroot_deployment_unlock
ostree_sysroot_write_deployments
+ostree_sysroot_write_origin_file
ostree_sysroot_deploy_tree
ostree_sysroot_get_merge_deployment
ostree_sysroot_origin_new_from_refspec
@@ -415,7 +476,7 @@ ostree_sysroot_get_type
-libostree-sysroot-upgrader
+ostree-sysroot-upgrader
OstreeSysrootUpgrader
ostree_sysroot_upgrader_new
ostree_sysroot_upgrader_new_for_os
diff --git a/autogen.sh b/autogen.sh
index 0eb65550..581f3dee 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -33,8 +33,8 @@ if ! test -f libglnx/README.md || ! test -f bsdiff/README.md; then
git submodule update --init
fi
# Workaround automake bug with subdir-objects and computed paths
-sed -e 's,$(libglnx_srcpath),'${srcdir}/libglnx,g < libglnx/Makefile-libglnx.am >libglnx/Makefile-libglnx.am.inc
-sed -e 's,$(libbsdiff_srcpath),'${srcdir}/bsdiff,g < bsdiff/Makefile-bsdiff.am >bsdiff/Makefile-bsdiff.am.inc
+sed -e 's,$(libglnx_srcpath),libglnx,g' < libglnx/Makefile-libglnx.am >libglnx/Makefile-libglnx.am.inc
+sed -e 's,$(libbsdiff_srcpath),bsdiff,g' < bsdiff/Makefile-bsdiff.am >bsdiff/Makefile-bsdiff.am.inc
autoreconf --force --install --verbose
diff --git a/configure.ac b/configure.ac
index dca9f536..18f9f277 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
AC_PREREQ([2.63])
-AC_INIT([ostree], [2016.5], [walters@verbum.org])
+AC_INIT([ostree], [2016.6], [walters@verbum.org])
AC_CONFIG_HEADER([config.h])
AC_CONFIG_MACRO_DIR([buildutil])
AC_CONFIG_AUX_DIR([build-aux])
@@ -38,13 +38,15 @@ GLIB_TESTS
AC_CHECK_HEADER([sys/xattr.h],,[AC_MSG_ERROR([You must have sys/xattr.h from glibc])])
-AC_CHECK_PROGS(YACC, 'bison -y', :)
-AS_IF([test "$YACC" = :], [AC_MSG_ERROR([bison not found but required])])
+AS_IF([test "$YACC" != "bison -y"], [AC_MSG_ERROR([bison not found but required])])
PKG_PROG_PKG_CONFIG
AM_PATH_GLIB_2_0
+dnl When bumping the gio-unix-2.0 dependency (or glib-2.0 in general),
+dnl remember to bump GLIB_VERSION_MIN_REQUIRED and
+dnl GLIB_VERSION_MAX_ALLOWED in Makefile.am
GIO_DEPENDENCY="gio-unix-2.0 >= 2.40.0 libgsystem >= 2015.1"
PKG_CHECK_MODULES(OT_DEP_GIO_UNIX, $GIO_DEPENDENCY)
@@ -57,6 +59,9 @@ PKG_CHECK_MODULES(OT_DEP_ZLIB, zlib)
dnl We're not actually linking to this, just using the header
PKG_CHECK_MODULES(OT_DEP_E2P, e2p)
+dnl When bumping the libsoup-2.4 dependency, remember to bump
+dnl SOUP_VERSION_MIN_REQUIRED and SOUP_VERSION_MAX_ALLOWED in
+dnl Makefile.am
SOUP_DEPENDENCY="libsoup-2.4 >= 2.39.1"
AC_ARG_WITH(soup,
AS_HELP_STRING([--with-soup], [Use libsoup @<:@default=yes@:>@]),
@@ -222,7 +227,7 @@ AC_ARG_ENABLE(rofiles-fuse,
[AS_HELP_STRING([--enable-rofiles-fuse],
[generate rofiles-fuse helper [default=yes]])],,
enable_rofiles_fuse=yes)
-AS_IF([ test $enable_rofiles_fuse != xno ], [
+AS_IF([ test x$enable_rofiles_fuse != xno ], [
PKG_CHECK_MODULES(BUILDOPT_FUSE, $FUSE_DEPENDENCY)
], [enable_rofiles_fuse=no])
AM_CONDITIONAL(BUILDOPT_FUSE, test x$enable_rofiles_fuse = xyes)
@@ -231,7 +236,14 @@ AC_ARG_WITH(dracut,
AS_HELP_STRING([--with-dracut],
[Install dracut module (default: no)]),,
[with_dracut=no])
-AM_CONDITIONAL(BUILDOPT_DRACUT, test x$with_dracut = xyes)
+case x$with_dracut in
+ xno) ;;
+ xyes) ;;
+ xyesbutnoconf) ;;
+ *) AC_MSG_ERROR([Unknown --with-dracut value $with_dracut])
+esac
+AM_CONDITIONAL(BUILDOPT_DRACUT, test x$with_dracut = xyes || test x$with_dracut = xyesbutnoconf)
+AM_CONDITIONAL(BUILDOPT_DRACUT_CONF, test x$with_dracut = xyes)
AC_ARG_WITH(mkinitcpio,
AS_HELP_STRING([--with-mkinitcpio],
@@ -239,7 +251,7 @@ AC_ARG_WITH(mkinitcpio,
[with_mkinitcpio=no])
AM_CONDITIONAL(BUILDOPT_MKINITCPIO, test x$with_mkinitcpio = xyes)
-AS_IF([test "x$with_dracut" = "xyes" || test "x$with_mkinitcpio" = "xyes"], [
+AS_IF([test "x$with_dracut" = "xyes" || test "x$with_dracut" = "xyesbutnoconf" || test "x$with_mkinitcpio" = "xyes"], [
with_systemd=yes
AC_ARG_WITH([systemdsystemunitdir],
AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
diff --git a/docs/manual/atomic-upgrades.md b/docs/manual/atomic-upgrades.md
index b5f398d6..40515b83 100644
--- a/docs/manual/atomic-upgrades.md
+++ b/docs/manual/atomic-upgrades.md
@@ -45,7 +45,7 @@ operate "live" on the currently booted filesystem. The way they could
work with OSTree is instead to take the list of installed packages in
the currently booted tree, and compute a new filesystem from that. A
later chapter describes in more details how this could work:
-[adapting-existing.md](Adapting Existing Systems).
+[Adapting Existing Systems](adapting-existing.md).
For the purposes of this section, let's assume that we have a
newly generated filesystem tree stored in the repo (which shares
@@ -56,7 +56,7 @@ checking it back out of the repo into a deployment.
Given a commit to deploy, OSTree first allocates a directory for
it. This is of the form `/boot/loader/entries/ostree-$osname-$checksum.$serial.conf`.
-The `$serial` is normally 0, but if a
+The `$serial` is normally `0`, but if a
given commit is deployed more than once, it will be incremented.
This is supported because the previous deployment may have
configuration in `/etc` that we do not want to use or overwrite.
diff --git a/docs/manual/formats.md b/docs/manual/formats.md
index e689f8a8..87d0005f 100644
--- a/docs/manual/formats.md
+++ b/docs/manual/formats.md
@@ -125,8 +125,7 @@ the client executes.
This "updates as code" model allows for multiple content generation
strategies. The design of this was inspired by that of Chromium:
-[http://dev.chromium.org/chromium-os/chromiumos-design-docs/filesystem-autoupdate](ChromiumOS
-autoupdate).
+[ChromiumOS Autoupdate](http://dev.chromium.org/chromium-os/chromiumos-design-docs/filesystem-autoupdate).
### The delta superblock
diff --git a/docs/manual/related-projects.md b/docs/manual/related-projects.md
index 896c7655..d37e2cfe 100644
--- a/docs/manual/related-projects.md
+++ b/docs/manual/related-projects.md
@@ -78,6 +78,10 @@ available.
All of the above also applies if one replaces "BTRFS" with "LVM
snapshots" except for the reflinks.
+OSTree supports using "bare-user" repositories, which do not require
+root to use. Using a filesystem-level layer without root is more
+difficult and would likely require a setuid helper or privileged service.
+
Finally, see the next portion around ChromiumOS for why a hybrid but
integrated package/image system improves on this.
@@ -130,6 +134,11 @@ believes that at the moment, the "CL updater" is not truly atomic in
the sense that because it applies updates live, there is a window
where the OS root may be inconsistent.
+## Mender.io
+
+[Mender.io](https://mender.io/) is another implementation of the dual
+partition approach.
+
## OLPC update
OSTree is basically a generalization of olpc-update, except using
@@ -147,37 +156,54 @@ See
[this comment](http://blog.verbum.org/2013/08/26/ostree-v2013-6-released/#comment-1169)
for a comparison.
-## NixOS
+## NixOS / Nix
-See [NixOS](http://nixos.org/). It was a very influential project for
-OSTree. NixOS and OSTree both support the idea of independent "roots"
-that are bootable.
+See [NixOS](http://nixos.org/). It was a very influential project for OSTree.
+NixOS and OSTree both support the idea of independent "roots" that are bootable.
-In NixOS, the entire system is based on checksums of package inputs
-(build dependencies) - see [Nix store](http://nixos.org/nix/manual/#chap-package-management/). A both
-positive and negative of the Nix model is that a change in the build
-dependencies (e.g. being built with a newer gcc), requires a cascading
-rebuild of everything.
+In NixOS, files in a package are accessed by a path depending on the checksums
+of package inputs (build dependencies) - see
+[Nix store](http://nixos.org/nix/manual/#chap-package-management/).
+However, OSTree uses a commit/deploy model - it isn't tied to any particular
+directory layout, and you can put whatever data you want inside an OSTree, for
+example the standard FHS layout. A both positive and negative of the Nix model
+is that a change in the build dependencies (e.g. being built with a newer gcc),
+requires a cascading rebuild of everything. It's good because it makes it easy
+to do massive system-wide changes such as gcc upgrades, and allows installing
+multiple versions of packages at once. However, a security update to e.g. glibc
+forces a rebuild of everything from scratch, and so Nix is not practical at
+scale. OSTree supports using a build system that just rebuilds individual
+components (packages) as they change, without forcing a rebuild of their
+dependencies.
-In OSTree, the checksums are of object *content* (including extended
-attributes). This means that any data that's identical is
-transparently, automatically shared on disk. It's possible to ask the
-Nix store to deduplicate, (via hard links and immutable bit), but this
-is significantly less efficient than the OSTree approach. The Nix use
-of the ext immutable bit is racy, since it has to be briefly removed
-to make a hard link.
+Nix automatically detects runtime package dependencies by scanning content for
+hashes. OSTree only supports only system-level images, and doesn't do dependency
+management. Nix can store arbitrary files, using nix-store --add, but, more
+commonly, paths are added as the result of running a derivation file generated
+using the Nix language. OSTree is build-system agnostic; filesystem trees are
+committed using a simple C API, and this is the only way to commit files.
-At the lowest level, OSTree is just "git for binaries" - it isn't tied
-strongly to any particular build system. You can put whatever data you
-want inside an OSTree repository, built however you like. So for
-example, while one could make a build system that did the "purely
-functional" approach of Nix, it also works to have a build system that
-just rebuilds individual components (packages) as they change, without
-forcing a rebuild of their dependencies.
+OSTree automatically shares the storage of identical data using hard links into
+a content-addressed store. Nix can deduplicate using hard links as well, using
+the auto-optimise-store option, but this is not on by default, and Nix does not
+guarantee that all of its files are in the content-addressed store. OSTree
+provides a git-like command line interface for browsing the content-addressed
+store, while Nix does not have this functionality.
-The author of OSTree believes that while Nix has some good ideas,
-forcing a rebuild of everything for a security update to e.g. glibc is
-not practical at scale.
+Nix used to use the immutable bit to prevent modifications to /nix/store, but
+now it uses a read-only bind mount. The bind mount can be privately remounted,
+allowing per-process privileged write access. OSTree uses the immutable
+bit on the root of the deployment, and mounts /usr as read-only.
+
+NixOS supports switching OS images on-the-fly, by maintaining both booted-system
+and current-system roots. It is not clear how well this approach works. OSTree
+currently requries a reboot to switch images.
+
+Finally, NixOS supports installing user-specific packages from trusted
+repositories without requiring root, using a trusted daemon.
+[Flatpak](https://lwn.net/Articles/687909/), based on OSTree, similarly has a
+policykit-based system helper that allows you to authenticate via polkit to
+install into the system repository.
## Solaris IPS
@@ -205,3 +231,40 @@ See
[bmap](https://source.tizen.org/documentation/reference/bmaptool/introduction).
A tool for optimized copying of disk images. Intended for offline use,
so not directly comparable.
+
+## Git
+
+Although OSTree has been called "Git for Binaries", and the two share the idea
+of a hashed content store, the implementation details are quite different.
+OSTree supports extended attributes and uses SHA256 instead of Git's SHA1. It
+"checks out" files via hardlinks, rather than copying, and thus requires the
+checkout to be immutable. At the moment, OSTree commits may have at most one
+parent, as opposed to Git which allows an arbitrary number. Git uses a
+smart-delta protocol for updates, while OSTree uses 1 HTTP request per changed
+file, or can generate static deltas.
+
+## Conda
+
+[Conda](http://conda.pydata.org/docs/) is an "OS-agnostic, system-level binary
+package manager and ecosystem"; although most well-known for its accompanying
+Python distribution anaconda, its scope has been expanding quickly. The package
+format is very similar to well-known ones such as RPM. However, unlike typical
+RPMs, the packages are built to be relocatable. Also, the package manager runs
+natively on Windows. Conda's main advantage is its ability to install
+collections of packages into "environments" by unpacking them all to the same
+directory. Conda reduces duplication across environments using hardlinks,
+similar to OSTree's sharing between deployments (although Conda uses package /
+file path instead of file hash). Overall, it is quite similar to rpm-ostree in
+functionality and scope.
+
+## rpm-ostree
+
+This builds on top of ostree to support building RPMs into OSTree images, and
+even composing RPMs on-the-fly using an overlay filesystem. It is being
+developed by Fedora, Red Hat, and CentOS as part of Project Atomic.
+
+## GNOME Continuous
+
+This is a service that incrementally rebuilds and tests GNOME on every commit.
+The need to make and distribute snapshots for this system was the original
+inspiration for ostree.
diff --git a/docs/manual/repo.md b/docs/manual/repo.md
index d3be549c..bce7e0c9 100644
--- a/docs/manual/repo.md
+++ b/docs/manual/repo.md
@@ -47,6 +47,22 @@ payload sections. The header contains uid, gid, mode, and symbolic
link target (for symlinks), as well as extended attributes. After the
header, for regular files, the content follows.
+The OSTree data format intentionally does not contain timestamps. The reasoning
+is that data files may be downloaded at different times, and by different build
+systems, and so will have different timestamps but identical physical content.
+These files may be large, so most users would like them to be shared, both in
+the repository and between the repository and deployments.
+
+This could cause problems with programs that check if files are out-of-date by
+comparing timestamps. For Git, the logical choice is to not mess with
+timestamps, because unnecessary rebuilding is better than a broken tree.
+However, OSTree has to hardlink files to check them out, and commits are assumed
+to be internally consistent with no build steps needed. For this reason, OSTree
+acts as though all timestamps are set to time_t 1, so that comparisons will be
+considered up-to-date. 1 is a better choice than 0 because some programs use 0
+as a special value; for example, GNU Tar warns of an "implausibly old time
+stamp" with 0.
+
# Repository types and locations
Also unlike git, an OSTree repository can be in one of three separate
@@ -98,7 +114,7 @@ that.
A later addition to OSTree is the concept of a "summary" file, created
via the `ostree summary -u` command. This was introduced for a few
reasons. A primary use case is to be a target a
-(Metalink)[https://en.wikipedia.org/wiki/Metalink], which requires a
+[Metalink](https://en.wikipedia.org/wiki/Metalink), which requires a
single file with a known checksum as a target.
The summary file primarily contains two mappings:
diff --git a/docs/manual/repository-management.md b/docs/manual/repository-management.md
index b83f6c15..b6da9629 100644
--- a/docs/manual/repository-management.md
+++ b/docs/manual/repository-management.md
@@ -4,11 +4,12 @@ Once you have a build system going, if you actually want client
systems to retrieve the content, you will quickly feel a need for
"repository management".
-OSTree itself does not currently come with tools to do this. One
-reason is that how content is delivered and managed has concerns very
-specific to the organization. For example, some operating system
-content vendors may want integration with a specific errata
-notification system.
+The command line tool `ostree` does cover some core functionality, but
+doesn't include very high level workflows. One reason is that how
+content is delivered and managed has concerns very specific to the
+organization. For example, some operating system content vendors may
+want integration with a specific errata notification system when
+generating commits.
In this section, we will describe some high level ideas and methods
for managing content in OSTree repositories, mostly independent of any
@@ -21,6 +22,27 @@ repositories today is the [Pulp Project](http://www.pulpproject.org/),
which has a
[Pulp OSTree plugin](https://pulp-ostree.readthedocs.org/en/latest/).
+## Mirroring repositories
+
+It's very common to want to perform a full or partial mirror, in
+particular across organizational boundaries (e.g. an upstream OS
+provider, and a user that wants offline and faster access to the
+content). OSTree supports both full and partial mirroring of the base
+`archive-z2` content, although not yet of static deltas.
+
+To create a mirror, first create an `archive-z2` repository (you don't
+need to run this as root), then add the upstream as a remote, then use
+`pull --mirror`.
+
+```
+ostree --repo=repo init --mode=archive-z2
+ostree --repo=repo remote add exampleos https://exampleos.com/ostree/repo
+ostree --repo=repo pull --mirror exampleos:exampleos/x86_64/standard
+```
+
+You can use the `--depth=-1` option to retrieve all history, or a
+positive integer like `3` to retrieve just the last 3 commits.
+
## Separate development vs release repositories
By default, OSTree accumulates server side history. This is actually
diff --git a/libglnx/glnx-console.c b/libglnx/glnx-console.c
index 39733118..d40702ed 100644
--- a/libglnx/glnx-console.c
+++ b/libglnx/glnx-console.c
@@ -177,22 +177,9 @@ printpad (const char *padbuf,
fwrite (padbuf, 1, r, stdout);
}
-/**
- * glnx_console_progress_text_percent:
- * @text: Show this text before the progress bar
- * @percentage: An integer in the range of 0 to 100
- *
- * On a tty, print to the console @text followed by an ASCII art
- * progress bar whose percentage is @percentage. If stdout is not a
- * tty, a more basic line by line change will be printed.
- *
- * You must have called glnx_console_lock() before invoking this
- * function.
- *
- */
-void
-glnx_console_progress_text_percent (const char *text,
- guint percentage)
+static void
+text_percent_internal (const char *text,
+ int percentage)
{
static const char equals[] = "====================";
const guint n_equals = sizeof (equals) - 1;
@@ -201,10 +188,6 @@ glnx_console_progress_text_percent (const char *text,
const guint ncolumns = glnx_console_columns ();
const guint bar_min = 10;
const guint input_textlen = text ? strlen (text) : 0;
- guint textlen;
- guint barlen;
-
- g_return_if_fail (percentage >= 0 && percentage <= 100);
if (text && !*text)
text = NULL;
@@ -236,36 +219,69 @@ glnx_console_progress_text_percent (const char *text,
(void) fwrite (beginbuf, 1, sizeof (beginbuf), stdout);
}
- textlen = MIN (input_textlen, ncolumns - bar_min);
- barlen = ncolumns - textlen;
-
- if (textlen > 0)
+ if (percentage == -1)
{
- fwrite (text, 1, textlen, stdout);
- fputc (' ', stdout);
+ const guint spacelen = ncolumns - input_textlen;
+ fwrite (text, 1, input_textlen, stdout);
+ printpad (spaces, n_spaces, spacelen);
}
-
- {
- const guint nbraces = 2;
- const guint textpercent_len = 5;
- const guint bar_internal_len = barlen - nbraces - textpercent_len;
- const guint eqlen = bar_internal_len * (percentage / 100.0);
- const guint spacelen = bar_internal_len - eqlen;
+ else
+ {
+ const guint textlen = MIN (input_textlen, ncolumns - bar_min);
+ const guint barlen = ncolumns - (textlen + 1);;
- fputc ('[', stdout);
- printpad (equals, n_equals, eqlen);
- printpad (spaces, n_spaces, spacelen);
- fputc (']', stdout);
- fprintf (stdout, " %3d%%", percentage);
- }
+ if (textlen > 0)
+ {
+ fwrite (text, 1, textlen, stdout);
+ fputc (' ', stdout);
+ }
- { const guint spacelen = ncolumns - textlen - barlen;
- printpad (spaces, n_spaces, spacelen);
- }
+ {
+ const guint nbraces = 2;
+ const guint textpercent_len = 5;
+ const guint bar_internal_len = barlen - nbraces - textpercent_len;
+ const guint eqlen = bar_internal_len * (percentage / 100.0);
+ const guint spacelen = bar_internal_len - eqlen;
+
+ fputc ('[', stdout);
+ printpad (equals, n_equals, eqlen);
+ printpad (spaces, n_spaces, spacelen);
+ fputc (']', stdout);
+ fprintf (stdout, " %3d%%", percentage);
+ }
+ }
fflush (stdout);
}
+/**
+ * glnx_console_progress_text_percent:
+ * @text: Show this text before the progress bar
+ * @percentage: An integer in the range of 0 to 100
+ *
+ * On a tty, print to the console @text followed by an ASCII art
+ * progress bar whose percentage is @percentage. If stdout is not a
+ * tty, a more basic line by line change will be printed.
+ *
+ * You must have called glnx_console_lock() before invoking this
+ * function.
+ *
+ */
+void
+glnx_console_progress_text_percent (const char *text,
+ guint percentage)
+{
+ g_return_if_fail (percentage >= 0 && percentage <= 100);
+
+ text_percent_internal (text, percentage);
+}
+
+void
+glnx_console_text (const char *text)
+{
+ text_percent_internal (text, -1);
+}
+
/**
* glnx_console_unlock:
*
diff --git a/libglnx/glnx-console.h b/libglnx/glnx-console.h
index 8fc38656..8c1d8115 100644
--- a/libglnx/glnx-console.h
+++ b/libglnx/glnx-console.h
@@ -33,6 +33,8 @@ typedef struct GLnxConsoleRef GLnxConsoleRef;
void glnx_console_lock (GLnxConsoleRef *ref);
+void glnx_console_text (const char *text);
+
void glnx_console_progress_text_percent (const char *text,
guint percentage);
diff --git a/libglnx/glnx-dirfd.c b/libglnx/glnx-dirfd.c
index 4861ccfe..95d7fe17 100644
--- a/libglnx/glnx-dirfd.c
+++ b/libglnx/glnx-dirfd.c
@@ -277,20 +277,17 @@ glnx_fdrel_abspath (int dfd,
}
/**
- * glnx_mkdtempat:
- * @dfd: Directory fd
- * @tmpl: (type filename): template directory name
- * @mode: permissions to create the temporary directory with
- * @error: Error
+ * glnx_gen_temp_name:
+ * @tmpl: (type filename): template directory name, the last 6 characters will be replaced
*
- * Similar to g_mkdtemp_full, but using openat.
+ * Replace the last 6 characters of @tmpl with random ASCII. You must
+ * use this in combination with a mechanism to ensure race-free file
+ * creation such as `O_EXCL`.
*/
-gboolean
-glnx_mkdtempat (int dfd,
- gchar *tmpl,
- int mode,
- GError **error)
+void
+glnx_gen_temp_name (gchar *tmpl)
{
+ size_t len;
char *XXXXXX;
int count;
static const char letters[] =
@@ -300,17 +297,11 @@ glnx_mkdtempat (int dfd,
GTimeVal tv;
static int counter = 0;
- g_return_val_if_fail (tmpl != NULL, -1);
+ g_return_if_fail (tmpl != NULL);
+ len = strlen (tmpl);
+ g_return_if_fail (len >= 6);
- /* find the last occurrence of "XXXXXX" */
- XXXXXX = g_strrstr (tmpl, "XXXXXX");
-
- if (!XXXXXX || strncmp (XXXXXX, "XXXXXX", 6))
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
- "Invalid temporary directory template '%s'", tmpl);
- return FALSE;
- }
+ XXXXXX = tmpl + (len - 6);
/* Get some more or less random data. */
g_get_current_time (&tv);
@@ -332,6 +323,31 @@ glnx_mkdtempat (int dfd,
XXXXXX[4] = letters[v % NLETTERS];
v /= NLETTERS;
XXXXXX[5] = letters[v % NLETTERS];
+ }
+}
+
+/**
+ * glnx_mkdtempat:
+ * @dfd: Directory fd
+ * @tmpl: (type filename): template directory name, last 6 characters will be replaced
+ * @mode: permissions to create the temporary directory with
+ * @error: Error
+ *
+ * Similar to g_mkdtemp_full, but using openat.
+ */
+gboolean
+glnx_mkdtempat (int dfd,
+ gchar *tmpl,
+ int mode,
+ GError **error)
+{
+ int count;
+
+ g_return_val_if_fail (tmpl != NULL, -1);
+
+ for (count = 0; count < 100; count++)
+ {
+ glnx_gen_temp_name (tmpl);
if (mkdirat (dfd, tmpl, mode) == -1)
{
diff --git a/libglnx/glnx-dirfd.h b/libglnx/glnx-dirfd.h
index c13e3dc5..3a766952 100644
--- a/libglnx/glnx-dirfd.h
+++ b/libglnx/glnx-dirfd.h
@@ -81,6 +81,8 @@ gboolean glnx_opendirat (int dfd,
char *glnx_fdrel_abspath (int dfd,
const char *path);
+void glnx_gen_temp_name (gchar *tmpl);
+
gboolean glnx_mkdtempat (int dfd,
gchar *tmpl,
int mode,
diff --git a/libglnx/glnx-fdio.c b/libglnx/glnx-fdio.c
index 466cbc4e..cdbb69fe 100644
--- a/libglnx/glnx-fdio.c
+++ b/libglnx/glnx-fdio.c
@@ -746,5 +746,35 @@ glnx_file_replace_contents_with_perms_at (int dfd,
ret = TRUE;
out:
+ if (!ret)
+ (void) unlink (tmppath);
return ret;
}
+
+/**
+ * glnx_stream_fstat:
+ * @stream: A stream containing a Unix file descriptor
+ * @stbuf: Memory location to write stat buffer
+ * @error:
+ *
+ * Some streams created via libgsystem are #GUnixInputStream; these do
+ * not support e.g. g_file_input_stream_query_info(). This function
+ * allows dropping to the raw unix fstat() call for these types of
+ * streams, while still conveniently wrapped with the normal GLib
+ * handling of @error.
+ */
+gboolean
+glnx_stream_fstat (GFileDescriptorBased *stream,
+ struct stat *stbuf,
+ GError **error)
+{
+ int fd = g_file_descriptor_based_get_fd (stream);
+
+ if (fstat (fd, stbuf) == -1)
+ {
+ glnx_set_prefix_error_from_errno (error, "%s", "fstat");
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/libglnx/glnx-fdio.h b/libglnx/glnx-fdio.h
index c0fd4e48..3ca1a660 100644
--- a/libglnx/glnx-fdio.h
+++ b/libglnx/glnx-fdio.h
@@ -21,6 +21,7 @@
#pragma once
#include
+#include
#include
#include
#include
@@ -121,4 +122,9 @@ glnx_file_copy_at (int src_dfd,
GCancellable *cancellable,
GError **error);
+gboolean
+glnx_stream_fstat (GFileDescriptorBased *stream,
+ struct stat *stbuf,
+ GError **error);
+
G_END_DECLS
diff --git a/libglnx/glnx-libcontainer.c b/libglnx/glnx-libcontainer.c
index 8c0f3407..38c1937f 100644
--- a/libglnx/glnx-libcontainer.c
+++ b/libglnx/glnx-libcontainer.c
@@ -274,6 +274,14 @@ glnx_libcontainer_run_chroot_private (const char *dest,
if (chdir ("/") != 0)
_perror_fatal ("chdir: ");
+ /* Environment variables like PATH in the end are distribution
+ * specific. The most correct thing would be to run through PAM,
+ * but that's a huge level of pain. We'd like to drive towards a
+ * standard /usr/bin (i.e. unified sbin too), but for now this is
+ * pretty compatible.
+ */
+ setenv ("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1);
+
if (binary[0] == '/')
{
if (execv (binary, argv) != 0)
@@ -281,9 +289,6 @@ glnx_libcontainer_run_chroot_private (const char *dest,
}
else
{
- /* Set PATH to something sane. */
- setenv ("PATH", "/usr/sbin:/usr/bin", 1);
-
if (execvp (binary, argv) != 0)
_perror_fatal ("execvp: ");
}
diff --git a/libglnx/glnx-local-alloc.h b/libglnx/glnx-local-alloc.h
index af5af4b8..f628b61c 100644
--- a/libglnx/glnx-local-alloc.h
+++ b/libglnx/glnx-local-alloc.h
@@ -21,6 +21,7 @@
#pragma once
#include
+#include
G_BEGIN_DECLS
@@ -195,13 +196,17 @@ GLNX_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, glnx_local_keyfile_unref, g_key_file_un
static inline void
glnx_cleanup_close_fdp (int *fdp)
{
- int fd;
+ int fd, errsv;
g_assert (fdp);
fd = *fdp;
if (fd != -1)
- (void) close (fd);
+ {
+ errsv = errno;
+ (void) close (fd);
+ errno = errsv;
+ }
}
/**
diff --git a/man/ostree-commit.xml b/man/ostree-commit.xml
index 4a86c1d2..8f0037f6 100644
--- a/man/ostree-commit.xml
+++ b/man/ostree-commit.xml
@@ -57,7 +57,7 @@ Boston, MA 02111-1307, USA.
Description
- This allows you to commit changes to a branch. The specification of the branch is required. If no commit message is specified with then a text editor will be opened. The commit will be aborted if the commit subject is left empty. The command will print the checksum of a successful commit.
+ This allows you to commit changes to a branch. The specification of the branch is required. The command will print the checksum of a successful commit.
@@ -68,7 +68,7 @@ Boston, MA 02111-1307, USA.
, ="SUBJECT"
- One line subject.
+ One line subject. (optional)
@@ -76,7 +76,15 @@ Boston, MA 02111-1307, USA.
, ="BODY"
- Full description.
+ Full description. (optional)
+
+
+
+
+ ,
+
+
+ Open a text editor for the commit description. It will use OSTREE_EDITOR, VISUAL, EDITOR, or vi, in descending order of preference. The commit will be aborted if the message is left empty.
@@ -84,7 +92,7 @@ Boston, MA 02111-1307, USA.
, ="BRANCH"
- Branch.
+ Branch. Required, unless --orphan is given.
@@ -163,7 +171,15 @@ Boston, MA 02111-1307, USA.
="PATH"
- File containing list of modifications to make permissions.
+ File containing list of modifications to make permissions (file mode, followed by space, followed by file path).
+
+
+
+
+ ="PATH"
+
+
+ File containing list of file paths to skip (one path per line).
@@ -206,6 +222,22 @@ Boston, MA 02111-1307, USA.
Override the timestamp of the commit to TIMESTAMP.
+
+
+
+
+
+ Create a commit without writing to a ref (branch)
+
+
+
+
+ ="POLICY"
+
+
+ POLICY is a boolean which specifies whether fsync should be used or not. Default to true.
+
+
diff --git a/packaging/ostree.spec.in b/packaging/ostree.spec.in
index 2d6a2660..2f07f0a8 100644
--- a/packaging/ostree.spec.in
+++ b/packaging/ostree.spec.in
@@ -72,7 +72,7 @@ env NOCONFIGURE=1 ./autogen.sh
%configure --disable-silent-rules \
--enable-gtk-doc \
--with-selinux \
- --with-dracut
+ --with-dracut=yesbutnoconf
make %{?_smp_mflags}
%install
@@ -94,7 +94,6 @@ rm -rf $RPM_BUILD_ROOT
%{_bindir}/ostree
%{_sbindir}/ostree-prepare-root
%{_sbindir}/ostree-remount
-%{_sysconfdir}/dracut.conf.d/ostree.conf
%dir %{_prefix}/lib/dracut/modules.d/98ostree
%{_prefix}/lib/systemd/system/ostree*.service
%{_prefix}/lib/dracut/modules.d/98ostree/*
diff --git a/src/libostree/libostree.sym b/src/libostree/libostree.sym
index 89a1457e..e8cca9d8 100644
--- a/src/libostree/libostree.sym
+++ b/src/libostree/libostree.sym
@@ -323,11 +323,6 @@ global:
ostree_deployment_unlocked_state_to_string;
} LIBOSTREE_2016.3;
-/* NOTE NOTE NOTE
- * Versions above here are released. Only add symbols below this line.
- * NOTE NOTE NOTE
- */
-
LIBOSTREE_2016.5 {
global:
ostree_repo_import_object_from_with_trust;
@@ -337,3 +332,24 @@ global:
ostree_repo_get_remote_boolean_option;
ostree_repo_set_cache_dir;
} LIBOSTREE_2016.4;
+
+LIBOSTREE_2016.6 {
+global:
+ ostree_gpg_verify_result_require_valid_signature;
+ ostree_raw_file_to_archive_z2_stream;
+ ostree_repo_gpg_verify_data;
+ ostree_repo_remote_fetch_summary_with_options;
+} LIBOSTREE_2016.5;
+
+/* NOTE NOTE NOTE
+ * Versions above here are released. Only add symbols below this line.
+ * NOTE NOTE NOTE
+ */
+
+/* Uncomment this when adding a new symbol */
+/*
+LIBOSTREE_2016.7 {
+global:
+ ostree_some_new_symbol;
+} LIBOSTREE_2016.6;
+*/
diff --git a/src/libostree/ostree-async-progress.c b/src/libostree/ostree-async-progress.c
index 59656fc9..0851fd86 100644
--- a/src/libostree/ostree-async-progress.c
+++ b/src/libostree/ostree-async-progress.c
@@ -23,7 +23,7 @@
#include "ostree-async-progress.h"
/**
- * SECTION:libostree-async-progress
+ * SECTION:ostree-async-progress
* @title: Progress notification system for asynchronous operations
* @short_description: Values representing progress
*
diff --git a/src/libostree/ostree-autocleanups.h b/src/libostree/ostree-autocleanups.h
new file mode 100644
index 00000000..7301ef1d
--- /dev/null
+++ b/src/libostree/ostree-autocleanups.h
@@ -0,0 +1,68 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2016 Endless Mobile, 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.
+ *
+ * Author: Krzesimir Nowak
+ */
+
+#pragma once
+
+#include
+
+G_BEGIN_DECLS
+
+#ifndef OSTREE_WITH_AUTOCLEANUPS
+#define OSTREE_WITH_AUTOCLEANUPS 0
+#endif
+
+/* ostree can use g_autoptr backports from libglnx when glib is too
+ * old, but still avoid exposing them to users that also have an old
+ * glib */
+#if defined(OSTREE_COMPILATION) || (OSTREE_WITH_AUTOCLEANUPS && GLIB_CHECK_VERSION(2, 44, 0))
+
+/*
+ * The following types have no specific clear/free/unref functions, so
+ * they can be used as the stack-allocated variables or as the
+ * g_autofree heap-allocated variables.
+ *
+ * OstreeRepoTransactionStats
+ * OstreeRepoImportArchiveOptions
+ * OstreeRepoExportArchiveOptions
+ * OstreeRepoCheckoutOptions
+ */
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeDiffItem, ostree_diff_item_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoCommitModifier, ostree_repo_commit_modifier_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoDevInoCache, ostree_repo_devino_cache_unref)
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeAsyncProgress, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeBootconfigParser, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeDeployment, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeGpgVerifyResult, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeMutableTree, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepo, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFile, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeSePolicy, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeSysroot, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeSysrootUpgrader, g_object_unref)
+
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (OstreeRepoCommitTraverseIter, ostree_repo_commit_traverse_iter_clear)
+
+#endif
+
+G_END_DECLS
diff --git a/src/libostree/ostree-bootloader-syslinux.c b/src/libostree/ostree-bootloader-syslinux.c
index 16951629..05cb173e 100644
--- a/src/libostree/ostree-bootloader-syslinux.c
+++ b/src/libostree/ostree-bootloader-syslinux.c
@@ -138,7 +138,8 @@ _ostree_bootloader_syslinux_write_config (OstreeBootloader *bootloader,
bootversion);
/* This should follow the symbolic link to the current bootversion. */
- config_contents = gs_file_load_contents_utf8 (self->config_path, cancellable, error);
+ config_contents = glnx_file_get_contents_utf8_at (AT_FDCWD, gs_file_get_path_cached (self->config_path), NULL,
+ cancellable, error);
if (!config_contents)
goto out;
diff --git a/src/libostree/ostree-bootloader-uboot.c b/src/libostree/ostree-bootloader-uboot.c
index f67e9bdb..f95ea843 100644
--- a/src/libostree/ostree-bootloader-uboot.c
+++ b/src/libostree/ostree-bootloader-uboot.c
@@ -113,7 +113,8 @@ _ostree_bootloader_uboot_write_config (OstreeBootloader *bootloader,
g_autoptr(GPtrArray) new_lines = NULL;
/* This should follow the symbolic link to the current bootversion. */
- config_contents = gs_file_load_contents_utf8 (self->config_path, cancellable, error);
+ config_contents = glnx_file_get_contents_utf8_at (AT_FDCWD, gs_file_get_path_cached (self->config_path), NULL,
+ cancellable, error);
if (!config_contents)
return FALSE;
diff --git a/src/libostree/ostree-core-private.h b/src/libostree/ostree-core-private.h
index a92aa4c7..91d52f1b 100644
--- a/src/libostree/ostree-core-private.h
+++ b/src/libostree/ostree-core-private.h
@@ -83,7 +83,7 @@ _ostree_make_temporary_symlink_at (int tmp_dirfd,
GFileInfo * _ostree_header_gfile_info_new (mode_t mode, uid_t uid, gid_t gid);
-/* XX + / + checksum-2 + . + extension, but let's just use 256 for a
+/* XX/checksum-2.extension, but let's just use 256 for a
* bit of overkill.
*/
#define _OSTREE_LOOSE_PATH_MAX (256)
@@ -112,8 +112,7 @@ _ostree_get_relative_static_delta_part_path (const char *from,
const char *to,
guint i);
-static inline char *
-_ostree_get_commitpartial_path (const char *checksum)
+static inline char * _ostree_get_commitpartial_path (const char *checksum)
{
return g_strconcat ("state/", checksum, ".commitpartial", NULL);
}
diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c
index ded5e976..8e8424fc 100644
--- a/src/libostree/ostree-core.c
+++ b/src/libostree/ostree-core.c
@@ -26,11 +26,11 @@
#include
#include
#include
+#include "libglnx.h"
#include "ostree.h"
#include "ostree-core-private.h"
#include "ostree-chain-input-stream.h"
#include "otutil.h"
-#include "libglnx.h"
#define ALIGN_VALUE(this, boundary) \
(( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
@@ -47,7 +47,7 @@ zlib_file_header_parse (GVariant *metadata,
GError **error);
/**
- * SECTION:libostree-core
+ * SECTION:ostree-core
* @title: Core repository-independent functions
* @short_description: Create, validate, and convert core data types
*
@@ -400,6 +400,98 @@ write_file_header_update_checksum (GOutputStream *out,
return ret;
}
+/*
+ * header_and_input_to_stream:
+ * @file_header: A file header
+ * @input: File raw content stream
+ * @out_input: (out): Serialized object stream
+ * @out_header_size: (out): Length of the header
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Combines @file_header and @input into a single stream.
+ */
+static gboolean
+header_and_input_to_stream (GVariant *file_header,
+ GInputStream *input,
+ GInputStream **out_input,
+ guint64 *out_header_size,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gpointer header_data;
+ gsize header_size;
+ g_autoptr(GInputStream) ret_input = NULL;
+ g_autoptr(GPtrArray) streams = NULL;
+ g_autoptr(GOutputStream) header_out_stream = NULL;
+ g_autoptr(GInputStream) header_in_stream = NULL;
+
+ header_out_stream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
+
+ if (!_ostree_write_variant_with_size (header_out_stream, file_header, 0, NULL, NULL,
+ cancellable, error))
+ return FALSE;
+
+ if (!g_output_stream_close (header_out_stream, cancellable, error))
+ return FALSE;
+
+ header_size = g_memory_output_stream_get_data_size ((GMemoryOutputStream*) header_out_stream);
+ header_data = g_memory_output_stream_steal_data ((GMemoryOutputStream*) header_out_stream);
+ header_in_stream = g_memory_input_stream_new_from_data (header_data, header_size, g_free);
+
+ streams = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
+
+ g_ptr_array_add (streams, g_object_ref (header_in_stream));
+ if (input)
+ g_ptr_array_add (streams, g_object_ref (input));
+
+ ret_input = (GInputStream*)ostree_chain_input_stream_new (streams);
+ ot_transfer_out_value (out_input, &ret_input);
+ if (out_header_size)
+ *out_header_size = header_size;
+
+ return TRUE;
+}
+
+/**
+ * ostree_raw_file_to_archive_z2_stream:
+ * @input: File raw content stream
+ * @file_info: A file info
+ * @xattrs: (allow-none): Optional extended attributes
+ * @out_input: (out): Serialized object stream
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Convert from a "bare" file representation into an
+ * OSTREE_OBJECT_TYPE_FILE stream suitable for ostree pull.
+ */
+gboolean
+ostree_raw_file_to_archive_z2_stream (GInputStream *input,
+ GFileInfo *file_info,
+ GVariant *xattrs,
+ GInputStream **out_input,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GVariant) file_header = NULL;
+ g_autoptr(GInputStream) zlib_input = NULL;
+
+ file_header = _ostree_zlib_file_header_new (file_info, xattrs);
+ if (input != NULL)
+ {
+ g_autoptr(GConverter) zlib_compressor = NULL;
+
+ zlib_compressor = G_CONVERTER (g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_RAW, 9));
+ zlib_input = g_converter_input_stream_new (input, zlib_compressor);
+ }
+ return header_and_input_to_stream (file_header,
+ zlib_input,
+ out_input,
+ NULL,
+ cancellable,
+ error);
+}
+
/**
* ostree_raw_file_to_content_stream:
* @input: File raw content stream
@@ -423,44 +515,20 @@ ostree_raw_file_to_content_stream (GInputStream *input,
GCancellable *cancellable,
GError **error)
{
- gboolean ret = FALSE;
- gpointer header_data;
- gsize header_size;
- g_autoptr(GInputStream) ret_input = NULL;
g_autoptr(GVariant) file_header = NULL;
- g_autoptr(GPtrArray) streams = NULL;
- g_autoptr(GOutputStream) header_out_stream = NULL;
- g_autoptr(GInputStream) header_in_stream = NULL;
+ guint64 header_size;
file_header = file_header_new (file_info, xattrs);
-
- header_out_stream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
-
- if (!_ostree_write_variant_with_size (header_out_stream, file_header, 0, NULL, NULL,
- cancellable, error))
- goto out;
-
- if (!g_output_stream_close (header_out_stream, cancellable, error))
- goto out;
-
- header_size = g_memory_output_stream_get_data_size ((GMemoryOutputStream*) header_out_stream);
- header_data = g_memory_output_stream_steal_data ((GMemoryOutputStream*) header_out_stream);
- header_in_stream = g_memory_input_stream_new_from_data (header_data, header_size, g_free);
-
- streams = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
-
- g_ptr_array_add (streams, g_object_ref (header_in_stream));
- if (input)
- g_ptr_array_add (streams, g_object_ref (input));
-
- ret_input = (GInputStream*)ostree_chain_input_stream_new (streams);
-
- ret = TRUE;
- ot_transfer_out_value (out_input, &ret_input);
+ if (!header_and_input_to_stream (file_header,
+ input,
+ out_input,
+ &header_size,
+ cancellable,
+ error))
+ return FALSE;
if (out_length)
*out_length = header_size + g_file_info_get_size (file_info);
- out:
- return ret;
+ return TRUE;
}
/**
@@ -613,7 +681,7 @@ ostree_content_file_parse_at (gboolean compressed,
cancellable, error))
goto out;
- if (!gs_stream_fstat ((GFileDescriptorBased*)file_input, &stbuf, cancellable, error))
+ if (!glnx_stream_fstat ((GFileDescriptorBased*)file_input, &stbuf, error))
goto out;
if (!ostree_content_stream_parse (compressed, file_input, stbuf.st_size, trusted,
@@ -921,14 +989,13 @@ _ostree_make_temporary_symlink_at (int tmp_dirfd,
GError **error)
{
gboolean ret = FALSE;
- g_autofree char *tmpname = NULL;
+ char *tmpname = g_strdup ("tmplink.XXXXXX");
guint i;
const int max_attempts = 128;
for (i = 0; i < max_attempts; i++)
{
- g_free (tmpname);
- tmpname = gs_fileutil_gen_tmp_name (NULL, NULL);
+ glnx_gen_temp_name (tmpname);
if (symlinkat (target, tmp_dirfd, tmpname) < 0)
{
if (errno == EEXIST)
diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h
index 29ef7b28..4a0e60e9 100644
--- a/src/libostree/ostree-core.h
+++ b/src/libostree/ostree-core.h
@@ -59,7 +59,7 @@ G_BEGIN_DECLS
* @OSTREE_OBJECT_TYPE_DIR_TREE: List of children (trees or files), and metadata
* @OSTREE_OBJECT_TYPE_DIR_META: Directory metadata
* @OSTREE_OBJECT_TYPE_COMMIT: Toplevel object, refers to tree and dirmeta for root
- * @OSTREE_OBJECT_TYPE_COMMIT_TOMBSTONE: Toplevel object, refers to a deleted commit
+ * @OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT: Toplevel object, refers to a deleted commit
*
* Enumeration for core object types; %OSTREE_OBJECT_TYPE_FILE is for
* content, the other types are metadata.
@@ -90,10 +90,10 @@ typedef enum {
/**
* OSTREE_DIRMETA_GVARIANT_FORMAT:
*
- * u - uid
- * u - gid
- * u - mode
- * a(ayay) - xattrs
+ * - u - uid
+ * - u - gid
+ * - u - mode
+ * - a(ayay) - xattrs
*/
#define OSTREE_DIRMETA_GVARIANT_STRING "(uuua(ayay))"
#define OSTREE_DIRMETA_GVARIANT_FORMAT G_VARIANT_TYPE (OSTREE_DIRMETA_GVARIANT_STRING)
@@ -106,10 +106,10 @@ typedef enum {
* can't store in the real filesystem but we can still use a regular .file object
* that we can hardlink to in the case of a user-mode checkout.
*
- * u - uid
- * u - gid
- * u - mode
- * a(ayay) - xattrs
+ * - u - uid
+ * - u - gid
+ * - u - mode
+ * - a(ayay) - xattrs
*/
#define OSTREE_FILEMETA_GVARIANT_STRING "(uuua(ayay))"
#define OSTREE_FILEMETA_GVARIANT_FORMAT G_VARIANT_TYPE (OSTREE_FILEMETA_GVARIANT_STRING)
@@ -117,8 +117,8 @@ typedef enum {
/**
* OSTREE_TREE_GVARIANT_FORMAT:
*
- * a(say) - array of (filename, checksum) for files
- * a(sayay) - array of (dirname, tree_checksum, meta_checksum) for directories
+ * - a(say) - array of (filename, checksum) for files
+ * - a(sayay) - array of (dirname, tree_checksum, meta_checksum) for directories
*/
#define OSTREE_TREE_GVARIANT_STRING "(a(say)a(sayay))"
#define OSTREE_TREE_GVARIANT_FORMAT G_VARIANT_TYPE (OSTREE_TREE_GVARIANT_STRING)
@@ -126,14 +126,14 @@ typedef enum {
/**
* OSTREE_COMMIT_GVARIANT_FORMAT:
*
- * a{sv} - Metadata
- * ay - parent checksum (empty string for initial)
- * a(say) - Related objects
- * s - subject
- * s - body
- * t - Timestamp in seconds since the epoch (UTC)
- * ay - Root tree contents
- * ay - Root tree metadata
+ * - a{sv} - Metadata
+ * - ay - parent checksum (empty string for initial)
+ * - a(say) - Related objects
+ * - s - subject
+ * - s - body
+ * - t - Timestamp in seconds since the epoch (UTC)
+ * - ay - Root tree contents
+ * - ay - Root tree metadata
*/
#define OSTREE_COMMIT_GVARIANT_STRING "(a{sv}aya(say)sstayay)"
#define OSTREE_COMMIT_GVARIANT_FORMAT G_VARIANT_TYPE (OSTREE_COMMIT_GVARIANT_STRING)
@@ -141,8 +141,9 @@ typedef enum {
/**
* OSTREE_SUMMARY_GVARIANT_FORMAT:
*
- * refs: a(s(taya{sv})) - Map of ref name -> (latest commit size, latest commit checksum, additional metadata), sorted by ref name
- * extensions: a{sv} - Additional metadata, none defined at the current time
+ * - a(s(taya{sv})) - Map of ref name -> (latest commit size, latest commit checksum, additional metadata), sorted by ref name
+ * - a{sv} - Additional metadata, at the current time the following are defined:
+ * - key: "ostree.static-deltas", value: a{sv}, static delta name -> 32 bytes of checksum
*/
#define OSTREE_SUMMARY_GVARIANT_STRING "(a(s(taya{sv}))a{sv})"
#define OSTREE_SUMMARY_GVARIANT_FORMAT G_VARIANT_TYPE (OSTREE_SUMMARY_GVARIANT_STRING)
@@ -152,9 +153,9 @@ typedef enum {
/**
* OstreeRepoMode:
- * @OSTREE_REPO_MODE_BARE: Files are stored as themselves; can only be written as root
+ * @OSTREE_REPO_MODE_BARE: Files are stored as themselves; checkouts are hardlinks; can only be written as root
* @OSTREE_REPO_MODE_ARCHIVE_Z2: Files are compressed, should be owned by non-root. Can be served via HTTP
- * @OSTREE_REPO_MODE_BARE_USER: Files are stored as themselves, except ownership; can be written by user
+ * @OSTREE_REPO_MODE_BARE_USER: Files are stored as themselves, except ownership; can be written by user. Hardlinks work only in user checkouts.
*
* See the documentation of #OstreeRepo for more information about the
* possible modes.
@@ -165,8 +166,8 @@ typedef enum {
OSTREE_REPO_MODE_BARE_USER
} OstreeRepoMode;
-const _OSTREE_PUBLIC
-GVariantType *ostree_metadata_variant_type (OstreeObjectType objtype);
+_OSTREE_PUBLIC
+const GVariantType *ostree_metadata_variant_type (OstreeObjectType objtype);
_OSTREE_PUBLIC
gboolean ostree_validate_checksum_string (const char *sha256,
@@ -214,9 +215,6 @@ gboolean ostree_parse_refspec (const char *refspec,
char **out_ref,
GError **error);
-_OSTREE_PUBLIC
-void ostree_checksum_update_meta (GChecksum *checksum, GFileInfo *file_info, GVariant *xattrs);
-
_OSTREE_PUBLIC
const char * ostree_object_type_to_string (OstreeObjectType objtype);
@@ -244,7 +242,8 @@ void ostree_object_from_string (const char *str,
gchar **out_checksum,
OstreeObjectType *out_objtype);
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
ostree_content_stream_parse (gboolean compressed,
GInputStream *input,
guint64 input_length,
@@ -276,6 +275,15 @@ gboolean ostree_content_file_parse_at (gboolean compressed,
GCancellable *cancellable,
GError **error);
+_OSTREE_PUBLIC
+gboolean
+ostree_raw_file_to_archive_z2_stream (GInputStream *input,
+ GFileInfo *file_info,
+ GVariant *xattrs,
+ GInputStream **out_input,
+ GCancellable *cancellable,
+ GError **error);
+
_OSTREE_PUBLIC
gboolean ostree_raw_file_to_content_stream (GInputStream *input,
GFileInfo *file_info,
diff --git a/src/libostree/ostree-deployment-private.h b/src/libostree/ostree-deployment-private.h
index 856a3987..01fdf1bf 100644
--- a/src/libostree/ostree-deployment-private.h
+++ b/src/libostree/ostree-deployment-private.h
@@ -24,19 +24,33 @@
G_BEGIN_DECLS
+
+/**
+ * OstreeDeployment:
+ * @parent_instance:
+ * @index: Global offset
+ * @osname:
+ * @csum: OSTree checksum of tree
+ * @deployserial: How many times this particular csum appears in deployment list
+ * @bootcsum: Checksum of kernel+initramfs
+ * @bootserial: An integer assigned to this tree per its ${bootcsum}
+ * @bootconfig: Bootloader configuration
+ * @origin: How to construct an upgraded version of this tree
+ * @unlocked: The unlocked state
+ */
struct _OstreeDeployment
{
GObject parent_instance;
- int index; /* Global offset */
- char *osname; /* osname */
- char *csum; /* OSTree checksum of tree */
- int deployserial; /* How many times this particular csum appears in deployment list */
- char *bootcsum; /* Checksum of kernel+initramfs */
- int bootserial; /* An integer assigned to this tree per its ${bootcsum} */
- OstreeBootconfigParser *bootconfig; /* Bootloader configuration */
- GKeyFile *origin; /* How to construct an upgraded version of this tree */
- OstreeDeploymentUnlockedState unlocked; /* The unlocked state */
+ int index;
+ char *osname;
+ char *csum;
+ int deployserial;
+ char *bootcsum;
+ int bootserial;
+ OstreeBootconfigParser *bootconfig;
+ GKeyFile *origin;
+ OstreeDeploymentUnlockedState unlocked;
};
void _ostree_deployment_set_bootcsum (OstreeDeployment *self, const char *bootcsum);
diff --git a/src/libostree/ostree-diff.c b/src/libostree/ostree-diff.c
index 4b733461..69a67b76 100644
--- a/src/libostree/ostree-diff.c
+++ b/src/libostree/ostree-diff.c
@@ -22,6 +22,7 @@
#include "config.h"
+#include "libglnx.h"
#include "ostree.h"
#include "otutil.h"
@@ -209,6 +210,8 @@ diff_add_dir_recurse (GFile *d,
* @modified: (element-type OstreeDiffItem): Modified files
* @removed: (element-type Gio.File): Removed files
* @added: (element-type Gio.File): Added files
+ * @cancellable: Cancellable
+ * @error: Error
*
* Compute the difference between directory @a and @b as 3 separate
* sets of #OstreeDiffItem in @modified, @removed, and @added.
diff --git a/src/libostree/ostree-diff.h b/src/libostree/ostree-diff.h
index f4db23ef..781bf768 100644
--- a/src/libostree/ostree-diff.h
+++ b/src/libostree/ostree-diff.h
@@ -27,11 +27,17 @@
G_BEGIN_DECLS
+/**
+ * OstreeDiffFlags:
+ */
typedef enum {
OSTREE_DIFF_FLAGS_NONE = 0,
OSTREE_DIFF_FLAGS_IGNORE_XATTRS = (1 << 0)
} OstreeDiffFlags;
+/**
+ * OstreeDiffItem:
+ */
typedef struct _OstreeDiffItem OstreeDiffItem;
struct _OstreeDiffItem
{
diff --git a/src/libostree/ostree-dummy-enumtypes.c b/src/libostree/ostree-dummy-enumtypes.c
new file mode 100644
index 00000000..259273b5
--- /dev/null
+++ b/src/libostree/ostree-dummy-enumtypes.c
@@ -0,0 +1,31 @@
+/* This file declares a stub function that is only exported
+ * to pacify ABI checkers - no one could really have used it.
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "ostree-dummy-enumtypes.h"
+
+/* Exported for backwards compat - see
+ * https://bugzilla.gnome.org/show_bug.cgi?id=764131
+ */
+GType
+ostree_fetcher_config_flags_get_type (void)
+{
+ return G_TYPE_INVALID;
+}
diff --git a/src/libostree/ostree-dummy-enumtypes.h b/src/libostree/ostree-dummy-enumtypes.h
new file mode 100644
index 00000000..017ed28e
--- /dev/null
+++ b/src/libostree/ostree-dummy-enumtypes.h
@@ -0,0 +1,29 @@
+/* This file declares a stub function that is only exported
+ * to pacify ABI checkers - no one could really have used it.
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include
+
+#ifndef __GI_SCANNER__
+_OSTREE_PUBLIC GType
+ostree_fetcher_config_flags_get_type (void);
+#endif
diff --git a/src/libostree/ostree-enumtypes.c.template b/src/libostree/ostree-enumtypes.c.template
index fe8807ae..ab1b2aec 100644
--- a/src/libostree/ostree-enumtypes.c.template
+++ b/src/libostree/ostree-enumtypes.c.template
@@ -28,7 +28,7 @@
/*** BEGIN value-header ***/
GType
-@enum_name@_get_type (void)
+_@enum_name@_get_type (void)
{
static volatile gsize the_type__volatile = 0;
diff --git a/src/libostree/ostree-enumtypes.h.template b/src/libostree/ostree-enumtypes.h.template
index 40899d7f..ec20fe0d 100644
--- a/src/libostree/ostree-enumtypes.h.template
+++ b/src/libostree/ostree-enumtypes.h.template
@@ -32,9 +32,8 @@ G_BEGIN_DECLS
/*** END file-production ***/
/*** BEGIN enumeration-production ***/
-#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
-_OSTREE_PUBLIC
-GType @enum_name@_get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (_@enum_name@_get_type ())
+GType _@enum_name@_get_type (void) G_GNUC_CONST;
/*** END enumeration-production ***/
diff --git a/src/libostree/ostree-fetcher.c b/src/libostree/ostree-fetcher.c
index d7915ba6..313df6a8 100644
--- a/src/libostree/ostree-fetcher.c
+++ b/src/libostree/ostree-fetcher.c
@@ -25,6 +25,7 @@
#include
#include
+#include "libglnx.h"
#include "ostree-fetcher.h"
#ifdef HAVE_LIBSOUP_CLIENT_CERTS
#include "ostree-tls-cert-interaction.h"
@@ -66,7 +67,12 @@ typedef struct {
guint64 total_downloaded;
} ThreadClosure;
+static void
+session_thread_process_pending_queue (ThreadClosure *thread_closure);
+
typedef struct {
+ volatile int ref_count;
+
ThreadClosure *thread_closure;
SoupURI *uri;
@@ -185,10 +191,22 @@ pending_task_compare (gconstpointer a,
(priority_a < priority_b) ? -1 : 1;
}
-static void
-pending_uri_free (OstreeFetcherPendingURI *pending)
+static OstreeFetcherPendingURI *
+pending_uri_ref (OstreeFetcherPendingURI *pending)
{
- g_hash_table_remove (pending->thread_closure->outstanding, pending);
+ g_return_val_if_fail (pending != NULL, NULL);
+ g_return_val_if_fail (pending->ref_count > 0, NULL);
+
+ g_atomic_int_inc (&pending->ref_count);
+
+ return pending;
+}
+
+static void
+pending_uri_unref (OstreeFetcherPendingURI *pending)
+{
+ if (!g_atomic_int_dec_and_test (&pending->ref_count))
+ return;
g_clear_pointer (&pending->thread_closure, thread_closure_unref);
@@ -273,6 +291,7 @@ session_thread_set_proxy_cb (ThreadClosure *thread_closure,
proxy_uri, NULL);
}
+#ifdef HAVE_LIBSOUP_CLIENT_CERTS
static void
session_thread_set_tls_interaction_cb (ThreadClosure *thread_closure,
gpointer data)
@@ -288,6 +307,7 @@ session_thread_set_tls_interaction_cb (ThreadClosure *thread_closure,
SOUP_SESSION_TLS_INTERACTION,
interaction, NULL);
}
+#endif
static void
session_thread_set_tls_database_cb (ThreadClosure *thread_closure,
@@ -328,8 +348,7 @@ session_thread_process_pending_queue (ThreadClosure *thread_closure)
pending = g_task_get_task_data (task);
cancellable = g_task_get_cancellable (task);
- /* pending_uri_free() removes this. */
- g_hash_table_add (thread_closure->outstanding, pending);
+ g_hash_table_add (thread_closure->outstanding, pending_uri_ref (pending));
soup_request_send_async (pending->request,
cancellable,
@@ -383,7 +402,7 @@ session_thread_request_uri (ThreadClosure *thread_closure,
if (thread_closure->tmpdir_name == NULL)
{
if (!_ostree_repo_allocate_tmpdir (thread_closure->base_tmpdir_dfd,
- "fetcher-",
+ OSTREE_REPO_TMPDIR_FETCHER,
&thread_closure->tmpdir_name,
&thread_closure->tmpdir_dfd,
&thread_closure->tmpdir_lock,
@@ -537,7 +556,7 @@ _ostree_fetcher_constructed (GObject *object)
self->thread_closure->tmpdir_dfd = -1;
self->thread_closure->tmpdir_lock = empty_lockfile;
- self->thread_closure->outstanding = g_hash_table_new (NULL, NULL);
+ self->thread_closure->outstanding = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)pending_uri_unref);
self->thread_closure->output_stream_set = g_hash_table_new_full (NULL, NULL,
(GDestroyNotify) NULL,
(GDestroyNotify) g_object_unref);
@@ -739,6 +758,18 @@ on_stream_read (GObject *object,
GAsyncResult *result,
gpointer user_data);
+static void
+remove_pending_rerun_queue (OstreeFetcherPendingURI *pending)
+{
+ /* Hold a temporary ref to ensure the reference to
+ * pending->thread_closure is valid.
+ */
+ pending_uri_ref (pending);
+ g_hash_table_remove (pending->thread_closure->outstanding, pending);
+ session_thread_process_pending_queue (pending->thread_closure);
+ pending_uri_unref (pending);
+}
+
static void
on_out_splice_complete (GObject *object,
GAsyncResult *result,
@@ -767,7 +798,10 @@ on_out_splice_complete (GObject *object,
out:
if (local_error)
- g_task_return_error (task, local_error);
+ {
+ g_task_return_error (task, local_error);
+ remove_pending_rerun_queue (pending);
+ }
g_object_unref (task);
}
@@ -799,6 +833,7 @@ on_stream_read (GObject *object,
g_task_return_pointer (task,
g_strdup (pending->out_tmpfile),
(GDestroyNotify) g_free);
+ remove_pending_rerun_queue (pending);
}
else
{
@@ -834,7 +869,10 @@ on_stream_read (GObject *object,
out:
if (local_error)
- g_task_return_error (task, local_error);
+ {
+ g_task_return_error (task, local_error);
+ remove_pending_rerun_queue (pending);
+ }
g_object_unref (task);
}
@@ -880,6 +918,7 @@ on_request_sent (GObject *object,
g_strdup (pending->out_tmpfile),
(GDestroyNotify) g_free);
}
+ remove_pending_rerun_queue (pending);
goto out;
}
else if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
@@ -944,6 +983,7 @@ on_request_sent (GObject *object,
g_task_return_pointer (task,
g_object_ref (pending->request_body),
(GDestroyNotify) g_object_unref);
+ remove_pending_rerun_queue (pending);
}
out:
@@ -952,6 +992,7 @@ on_request_sent (GObject *object,
if (pending->request_body)
(void) g_input_stream_close (pending->request_body, NULL, NULL);
g_task_return_error (task, local_error);
+ remove_pending_rerun_queue (pending);
}
g_object_unref (task);
@@ -976,6 +1017,7 @@ ostree_fetcher_request_uri_internal (OstreeFetcher *self,
/* SoupRequest is created in session thread. */
pending = g_new0 (OstreeFetcherPendingURI, 1);
+ pending->ref_count = 1;
pending->thread_closure = thread_closure_ref (self->thread_closure);
pending->uri = soup_uri_copy (uri);
pending->max_size = max_size;
@@ -983,7 +1025,7 @@ ostree_fetcher_request_uri_internal (OstreeFetcher *self,
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, source_tag);
- g_task_set_task_data (task, pending, (GDestroyNotify) pending_uri_free);
+ g_task_set_task_data (task, pending, (GDestroyNotify) pending_uri_unref);
/* We'll use the GTask priority for our own priority queue. */
g_task_set_priority (task, priority);
@@ -1067,7 +1109,7 @@ _ostree_fetcher_bytes_transferred (OstreeFetcher *self)
if (G_IS_FILE_DESCRIPTOR_BASED (stream))
{
- if (gs_stream_fstat ((GFileDescriptorBased*)stream, &stbuf, NULL, NULL))
+ if (glnx_stream_fstat ((GFileDescriptorBased*)stream, &stbuf, NULL))
ret += stbuf.st_size;
}
}
diff --git a/src/libostree/ostree-gpg-verifier.h b/src/libostree/ostree-gpg-verifier.h
index 209f7342..2db39f3b 100644
--- a/src/libostree/ostree-gpg-verifier.h
+++ b/src/libostree/ostree-gpg-verifier.h
@@ -35,6 +35,11 @@ G_BEGIN_DECLS
typedef struct OstreeGpgVerifier OstreeGpgVerifier;
+/* If this type becomes public in future, move this autoptr cleanup
+ * definition to the ostree-autocleanups.h header file. Right now it
+ * relies on glnx's fallback definition of the macro. */
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeGpgVerifier, g_object_unref)
+
GType _ostree_gpg_verifier_get_type (void);
OstreeGpgVerifier *_ostree_gpg_verifier_new (void);
diff --git a/src/libostree/ostree-gpg-verify-result.c b/src/libostree/ostree-gpg-verify-result.c
index 37fbfb5c..fa4614d1 100644
--- a/src/libostree/ostree-gpg-verify-result.c
+++ b/src/libostree/ostree-gpg-verify-result.c
@@ -27,7 +27,7 @@
#include "ostree-gpg-verify-result-private.h"
/**
- * SECTION: libostree-gpg-verify-result
+ * SECTION: ostree-gpg-verify-result
* @title: GPG signature verification results
* @short_description: Inspect detached GPG signatures
*
@@ -622,3 +622,33 @@ ostree_gpg_verify_result_describe_variant (GVariant *variant,
}
}
}
+
+/**
+ * ostree_gpg_verify_result_require_valid_signature:
+ * @result: (nullable): an #OstreeGpgVerifyResult
+ * @error: A #GError
+ *
+ * Checks if the result contains at least one signature from the
+ * trusted keyring. You can call this function immediately after
+ * ostree_repo_verify_summary() or ostree_repo_verify_commit_ext() -
+ * it will handle the %NULL @result and filled @error too.
+ *
+ * Returns: %TRUE if @result was not %NULL and had at least one
+ * signature from trusted keyring, otherwise %FALSE
+ */
+gboolean
+ostree_gpg_verify_result_require_valid_signature (OstreeGpgVerifyResult *result,
+ GError **error)
+{
+ if (result == NULL)
+ return FALSE;
+
+ if (ostree_gpg_verify_result_count_valid (result) == 0)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "GPG signatures found, but none are in trusted keyring");
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/src/libostree/ostree-gpg-verify-result.h b/src/libostree/ostree-gpg-verify-result.h
index 8894afdf..f9512538 100644
--- a/src/libostree/ostree-gpg-verify-result.h
+++ b/src/libostree/ostree-gpg-verify-result.h
@@ -133,4 +133,8 @@ void ostree_gpg_verify_result_describe_variant (GVariant *variant,
const gchar *line_prefix,
OstreeGpgSignatureFormatFlags flags);
+_OSTREE_PUBLIC
+gboolean ostree_gpg_verify_result_require_valid_signature (OstreeGpgVerifyResult *result,
+ GError **error);
+
G_END_DECLS
diff --git a/src/libostree/ostree-linuxfsutil.c b/src/libostree/ostree-linuxfsutil.c
index 4151fa34..b270a94e 100644
--- a/src/libostree/ostree-linuxfsutil.c
+++ b/src/libostree/ostree-linuxfsutil.c
@@ -107,7 +107,7 @@ _ostree_linuxfs_alter_immutable_flag (GFile *path,
GError **error)
{
gboolean ret = FALSE;
- int fd = -1;
+ glnx_fd_close int fd = -1;
if (g_cancellable_set_error_if_cancelled (cancellable, error))
return FALSE;
@@ -129,7 +129,5 @@ _ostree_linuxfs_alter_immutable_flag (GFile *path,
ret = TRUE;
out:
- if (fd != -1)
- (void) close (fd);
return ret;
}
diff --git a/src/libostree/ostree-lzma-compressor.c b/src/libostree/ostree-lzma-compressor.c
index 1ec03c41..8d9dcbc9 100644
--- a/src/libostree/ostree-lzma-compressor.c
+++ b/src/libostree/ostree-lzma-compressor.c
@@ -32,7 +32,7 @@ enum {
/**
* SECTION:ostree-lzma-compressor
- * @short_description: LZMA compressor
+ * @title: LZMA compressor
*
* An implementation of #GConverter that compresses data using
* LZMA.
diff --git a/src/libostree/ostree-lzma-decompressor.c b/src/libostree/ostree-lzma-decompressor.c
index b46e8fb2..6376e389 100644
--- a/src/libostree/ostree-lzma-decompressor.c
+++ b/src/libostree/ostree-lzma-decompressor.c
@@ -29,6 +29,14 @@ enum {
PROP_0,
};
+/**
+ * SECTION:ostree-lzma-decompressor
+ * @title: LZMA decompressor
+ *
+ * An implementation of #GConverter that decompresses data using
+ * LZMA.
+ */
+
static void _ostree_lzma_decompressor_iface_init (GConverterIface *iface);
struct _OstreeLzmaDecompressor
diff --git a/src/libostree/ostree-mutable-tree.c b/src/libostree/ostree-mutable-tree.c
index bc4f4250..5540cc7c 100644
--- a/src/libostree/ostree-mutable-tree.c
+++ b/src/libostree/ostree-mutable-tree.c
@@ -27,7 +27,7 @@
#include "ostree-core.h"
/**
- * SECTION:libostree-mutable-tree
+ * SECTION:ostree-mutable-tree
* @title: In-memory modifiable filesystem tree
* @short_description: Modifiable filesystem tree
*
@@ -159,6 +159,11 @@ ostree_mutable_tree_replace_file (OstreeMutableTree *self,
{
gboolean ret = FALSE;
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (!ot_util_filename_validate (name, error))
+ goto out;
+
if (g_hash_table_lookup (self->subdirs, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
@@ -187,6 +192,9 @@ ostree_mutable_tree_ensure_dir (OstreeMutableTree *self,
g_return_val_if_fail (name != NULL, FALSE);
+ if (!ot_util_filename_validate (name, error))
+ goto out;
+
if (g_hash_table_lookup (self->files, name))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
diff --git a/src/libostree/ostree-mutable-tree.h b/src/libostree/ostree-mutable-tree.h
index 30425d8d..1b642c42 100644
--- a/src/libostree/ostree-mutable-tree.h
+++ b/src/libostree/ostree-mutable-tree.h
@@ -84,7 +84,8 @@ gboolean ostree_mutable_tree_lookup (OstreeMutableTree *self,
OstreeMutableTree **out_subdir,
GError **error);
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
ostree_mutable_tree_ensure_parent_dirs (OstreeMutableTree *self,
GPtrArray *split_path,
const char *metadata_checksum,
diff --git a/src/libostree/ostree-repo-checkout.c b/src/libostree/ostree-repo-checkout.c
index 0a77da02..5f7cf0bd 100644
--- a/src/libostree/ostree-repo-checkout.c
+++ b/src/libostree/ostree-repo-checkout.c
@@ -632,7 +632,7 @@ checkout_tree_at (OstreeRepo *self,
{
gboolean ret = FALSE;
gboolean did_exist = FALSE;
- int destination_dfd = -1;
+ glnx_fd_close int destination_dfd = -1;
int res;
g_autoptr(GVariant) xattrs = NULL;
g_autoptr(GFileEnumerator) dir_enum = NULL;
@@ -752,12 +752,12 @@ checkout_tree_at (OstreeRepo *self,
}
}
- /* Set directory mtime to 0, so that it is constant for all checkouts.
+ /* Set directory mtime to OSTREE_TIMESTAMP, so that it is constant for all checkouts.
* Must be done after setting permissions and creating all children.
*/
if (!did_exist)
{
- const struct timespec times[2] = { { 0, UTIME_OMIT }, { 0, } };
+ const struct timespec times[2] = { { OSTREE_TIMESTAMP, UTIME_OMIT }, { OSTREE_TIMESTAMP, 0} };
do
res = futimens (destination_dfd, times);
while (G_UNLIKELY (res == -1 && errno == EINTR));
@@ -779,8 +779,6 @@ checkout_tree_at (OstreeRepo *self,
ret = TRUE;
out:
- if (destination_dfd != -1)
- (void) close (destination_dfd);
return ret;
}
@@ -957,38 +955,36 @@ ostree_repo_checkout_gc (OstreeRepo *self,
g_hash_table_iter_init (&iter, to_clean_dirs);
while (to_clean_dirs && g_hash_table_iter_next (&iter, &key, &value))
{
- g_autoptr(GFile) objdir = NULL;
- g_autoptr(GFileEnumerator) enumerator = NULL;
- g_autofree char *objdir_name = NULL;
+ g_autofree char *objdir_name = g_strdup_printf ("%02x", GPOINTER_TO_UINT (key));
+ g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
- objdir_name = g_strdup_printf ("%02x", GPOINTER_TO_UINT (key));
- objdir = g_file_get_child (self->uncompressed_objects_dir, objdir_name);
-
- enumerator = g_file_enumerate_children (objdir, "standard::name,standard::type,unix::inode,unix::nlink",
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- cancellable,
- error);
- if (!enumerator)
+ if (!glnx_dirfd_iterator_init_at (self->uncompressed_objects_dir_fd, objdir_name, FALSE,
+ &dfd_iter, error))
goto out;
-
+
while (TRUE)
{
- GFileInfo *file_info;
- guint32 nlinks;
+ struct dirent *dent;
+ struct stat stbuf;
- if (!gs_file_enumerator_iterate (enumerator, &file_info, NULL,
- cancellable, error))
+ if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
goto out;
- if (file_info == NULL)
+ if (dent == NULL)
break;
-
- nlinks = g_file_info_get_attribute_uint32 (file_info, "unix::nlink");
- if (nlinks == 1)
+
+ if (fstatat (dfd_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW) != 0)
{
- g_autoptr(GFile) objpath = NULL;
- objpath = g_file_get_child (objdir, g_file_info_get_name (file_info));
- if (!gs_file_unlink (objpath, cancellable, error))
- goto out;
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ if (stbuf.st_nlink == 1)
+ {
+ if (unlinkat (dfd_iter.fd, dent->d_name, 0) != 0)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
}
}
}
diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c
index 7f03e11d..685eadd0 100644
--- a/src/libostree/ostree-repo-commit.c
+++ b/src/libostree/ostree-repo-commit.c
@@ -36,22 +36,6 @@
#include
#include
-struct OstreeRepoCommitModifier {
- volatile gint refcount;
-
- OstreeRepoCommitModifierFlags flags;
- OstreeRepoCommitFilter filter;
- gpointer user_data;
- GDestroyNotify destroy_notify;
-
- OstreeRepoCommitModifierXattrCallback xattr_callback;
- GDestroyNotify xattr_destroy;
- gpointer xattr_user_data;
-
- OstreeSePolicy *sepolicy;
- GHashTable *devino_cache;
-};
-
gboolean
_ostree_repo_ensure_loose_objdir_at (int dfd,
const char *loose_path,
@@ -226,7 +210,6 @@ commit_loose_object_trusted (OstreeRepo *self,
else
{
int res;
- struct timespec times[2];
if (objtype == OSTREE_OBJECT_TYPE_FILE && self->mode == OSTREE_REPO_MODE_BARE)
{
@@ -282,12 +265,9 @@ commit_loose_object_trusted (OstreeRepo *self,
{
/* To satisfy tools such as guile which compare mtimes
* to determine whether or not source files need to be compiled,
- * set the modification time to 0.
+ * set the modification time to OSTREE_TIMESTAMP.
*/
- times[0].tv_sec = 0; /* atime */
- times[0].tv_nsec = UTIME_OMIT;
- times[1].tv_sec = 0; /* mtime */
- times[1].tv_nsec = 0;
+ const struct timespec times[2] = { { OSTREE_TIMESTAMP, UTIME_OMIT }, { OSTREE_TIMESTAMP, 0} };
do
res = futimens (fd, times);
while (G_UNLIKELY (res == -1 && errno == EINTR));
@@ -573,11 +553,8 @@ _ostree_repo_open_trusted_content_bare (OstreeRepo *self,
g_autofree char *temp_filename = NULL;
g_autoptr(GOutputStream) ret_stream = NULL;
gboolean have_obj;
- char loose_objpath[_OSTREE_LOOSE_PATH_MAX];
- if (!_ostree_repo_has_loose_object (self, checksum, OSTREE_OBJECT_TYPE_FILE,
- &have_obj, loose_objpath,
- NULL,
+ if (!_ostree_repo_has_loose_object (self, checksum, OSTREE_OBJECT_TYPE_FILE, &have_obj,
cancellable, error))
goto out;
@@ -662,7 +639,6 @@ write_object (OstreeRepo *self,
gboolean temp_file_is_regular;
gboolean temp_file_is_symlink;
gboolean object_is_symlink = FALSE;
- char loose_objpath[_OSTREE_LOOSE_PATH_MAX];
gssize unpacked_size = 0;
gboolean indexable = FALSE;
@@ -673,9 +649,8 @@ write_object (OstreeRepo *self,
if (expected_checksum)
{
- if (!_ostree_repo_has_loose_object (self, expected_checksum, objtype,
- &have_obj, loose_objpath,
- NULL, cancellable, error))
+ if (!_ostree_repo_has_loose_object (self, expected_checksum, objtype, &have_obj,
+ cancellable, error))
goto out;
if (have_obj)
{
@@ -852,8 +827,7 @@ write_object (OstreeRepo *self,
repo_store_size_entry (self, actual_checksum, unpacked_size, stbuf.st_size);
}
- if (!_ostree_repo_has_loose_object (self, actual_checksum, objtype,
- &have_obj, loose_objpath, NULL,
+ if (!_ostree_repo_has_loose_object (self, actual_checksum, objtype, &have_obj,
cancellable, error))
goto out;
@@ -1167,7 +1141,6 @@ ostree_repo_prepare_transaction (OstreeRepo *self,
{
gboolean ret = FALSE;
gboolean ret_transaction_resume = FALSE;
- g_autofree char *stagedir_boot_id_prefix = NULL;
g_autofree char *stagedir_name = NULL;
glnx_fd_close int stagedir_fd = -1;
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
@@ -1178,10 +1151,8 @@ ostree_repo_prepare_transaction (OstreeRepo *self,
self->in_transaction = TRUE;
- stagedir_boot_id_prefix = g_strconcat ("staging-", self->boot_id, "-", NULL);
-
if (!_ostree_repo_allocate_tmpdir (self->tmp_dir_fd,
- stagedir_boot_id_prefix,
+ self->stagedir_prefix,
&self->commit_stagedir_name,
&self->commit_stagedir_fd,
&self->commit_stagedir_lock,
@@ -1287,44 +1258,87 @@ cleanup_tmpdir (OstreeRepo *self,
GError **error)
{
gboolean ret = FALSE;
- g_autoptr(GFileEnumerator) enumerator = NULL;
+ g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
guint64 curtime_secs;
- enumerator = g_file_enumerate_children (self->tmp_dir, "standard::name,time::modified",
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- cancellable,
- error);
- if (!enumerator)
- goto out;
-
curtime_secs = g_get_real_time () / 1000000;
+ if (!glnx_dirfd_iterator_init_at (self->tmp_dir_fd, ".", TRUE, &dfd_iter, error))
+ goto out;
+
while (TRUE)
{
- GFileInfo *file_info;
- GFile *path;
- guint64 mtime;
guint64 delta;
+ struct dirent *dent;
+ struct stat stbuf;
+ g_auto(GLnxLockFile) lockfile = GLNX_LOCK_FILE_INIT;
+ gboolean did_lock;
- if (!gs_file_enumerator_iterate (enumerator, &file_info, &path,
- cancellable, error))
+ if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
goto out;
- if (file_info == NULL)
+
+ if (dent == NULL)
break;
- mtime = g_file_info_get_attribute_uint64 (file_info, "time::modified");
- if (mtime > curtime_secs)
- continue;
- /* Only delete files older than a day. To do better, we would
- * need to coordinate between multiple processes in a reliable
- * fashion. See
- * https://bugzilla.gnome.org/show_bug.cgi?id=709115
- */
- delta = curtime_secs - mtime;
- if (delta > 60*60*24)
+ if (TEMP_FAILURE_RETRY (fstatat (dfd_iter.fd, dent->d_name, &stbuf, AT_SYMLINK_NOFOLLOW)) < 0)
{
- if (!glnx_shutil_rm_rf_at (AT_FDCWD, gs_file_get_path_cached (path), cancellable, error))
+ if (errno == ENOENT) /* Did another cleanup win? */
+ continue;
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+
+ /* First, if it's a directory which needs locking, but it's
+ * busy, skip it.
+ */
+ if (_ostree_repo_is_locked_tmpdir (dent->d_name))
+ {
+ if (!_ostree_repo_try_lock_tmpdir (dfd_iter.fd, dent->d_name,
+ &lockfile, &did_lock, error))
goto out;
+ if (!did_lock)
+ continue;
+ }
+
+ /* If however this is the staging directory for the *current*
+ * boot, then don't delete it now - we may end up reusing it, as
+ * is the point.
+ */
+ if (g_str_has_prefix (dent->d_name, self->stagedir_prefix))
+ continue;
+ else if (g_str_has_prefix (dent->d_name, OSTREE_REPO_TMPDIR_STAGING))
+ {
+ /* But, crucially we can now clean up staging directories
+ * from *other* boots
+ */
+ if (!glnx_shutil_rm_rf_at (dfd_iter.fd, dent->d_name, cancellable, error))
+ goto out;
+ }
+ /* FIXME - move OSTREE_REPO_TMPDIR_FETCHER underneath the
+ * staging/boot-id scheme as well, since all of the "did it get
+ * fsync'd" concerns apply to that as well. Then we can skip
+ * this special case.
+ */
+ else if (g_str_has_prefix (dent->d_name, OSTREE_REPO_TMPDIR_FETCHER))
+ continue;
+ else
+ {
+ /* Now we do time-based cleanup. Ignore it if it's somehow
+ * in the future...
+ */
+ if (stbuf.st_mtime > curtime_secs)
+ continue;
+
+ /* Now, we're pruning content based on the expiry, which
+ * defaults to a day. That's what we were doing before we
+ * had locking...but in future we can be smarter here.
+ */
+ delta = curtime_secs - stbuf.st_mtime;
+ if (delta > self->tmp_expiry_seconds)
+ {
+ if (!glnx_shutil_rm_rf_at (dfd_iter.fd, dent->d_name, cancellable, error))
+ goto out;
+ }
}
}
@@ -1448,12 +1462,25 @@ ostree_repo_commit_transaction (OstreeRepo *self,
g_return_val_if_fail (self->in_transaction == TRUE, FALSE);
- if (syncfs (self->tmp_dir_fd) < 0)
+ if ((self->test_error_flags & OSTREE_REPO_TEST_ERROR_PRE_COMMIT) > 0)
{
- glnx_set_error_from_errno (error);
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "OSTREE_REPO_TEST_ERROR_PRE_COMMIT specified");
goto out;
}
+ /* FIXME: Added since valgrind in el7 doesn't know about
+ * `syncfs`...we should delete this later.
+ */
+ if (g_getenv ("OSTREE_SUPPRESS_SYNCFS") == NULL)
+ {
+ if (syncfs (self->tmp_dir_fd) < 0)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ }
+
if (!rename_pending_loose_objects (self, cancellable, error))
goto out;
@@ -1937,7 +1964,7 @@ create_empty_gvariant_dict (void)
* ostree_repo_write_commit:
* @self: Repo
* @parent: (allow-none): ASCII SHA256 checksum for parent, or %NULL for none
- * @subject: Subject
+ * @subject: (allow-none): Subject
* @body: (allow-none): Body
* @metadata: (allow-none): GVariant of type a{sv}, or %NULL for none
* @root: The tree to point the commit to
@@ -1981,10 +2008,11 @@ ostree_repo_write_commit (OstreeRepo *self,
* ostree_repo_write_commit_with_time:
* @self: Repo
* @parent: (allow-none): ASCII SHA256 checksum for parent, or %NULL for none
- * @subject: Subject
+ * @subject: (allow-none): Subject
* @body: (allow-none): Body
* @metadata: (allow-none): GVariant of type a{sv}, or %NULL for none
* @root: The tree to point the commit to
+ * @time: The time to use to stamp the commit
* @out_commit: (out): Resulting ASCII SHA256 checksum for commit
* @cancellable: Cancellable
* @error: Error
@@ -2011,8 +2039,6 @@ ostree_repo_write_commit_with_time (OstreeRepo *self,
g_autofree guchar *commit_csum = NULL;
OstreeRepoFile *repo_root = OSTREE_REPO_FILE (root);
- g_return_val_if_fail (subject != NULL, FALSE);
-
/* Add sizes information to our metadata object */
if (!add_size_index_to_metadata (self, metadata, &new_metadata,
cancellable, error))
@@ -2022,7 +2048,7 @@ ostree_repo_write_commit_with_time (OstreeRepo *self,
new_metadata ? new_metadata : create_empty_gvariant_dict (),
parent ? ostree_checksum_to_bytes_v (parent) : ot_gvariant_new_bytearray (NULL, 0),
g_variant_new_array (G_VARIANT_TYPE ("(say)"), NULL, 0),
- subject, body ? body : "",
+ subject ? subject : "", body ? body : "",
GUINT64_TO_BE (time),
ostree_checksum_to_bytes_v (ostree_repo_file_tree_get_contents_checksum (repo_root)),
ostree_checksum_to_bytes_v (ostree_repo_file_tree_get_metadata_checksum (repo_root)));
@@ -2073,21 +2099,13 @@ ostree_repo_read_commit_detached_metadata (OstreeRepo *self,
g_autoptr(GFile) metadata_path =
_ostree_repo_get_commit_metadata_loose_path (self, checksum);
g_autoptr(GVariant) ret_metadata = NULL;
- GError *temp_error = NULL;
- if (!ot_util_variant_map (metadata_path, G_VARIANT_TYPE ("a{sv}"),
- TRUE, &ret_metadata, &temp_error))
+ if (!ot_util_variant_map_at (AT_FDCWD, gs_file_get_path_cached (metadata_path),
+ G_VARIANT_TYPE ("a{sv}"),
+ OT_VARIANT_MAP_ALLOW_NOENT | OT_VARIANT_MAP_TRUSTED, &ret_metadata, error))
{
- if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
- {
- g_clear_error (&temp_error);
- }
- else
- {
- g_prefix_error (error, "Unable to read existing detached metadata: ");
- g_propagate_error (error, temp_error);
- goto out;
- }
+ g_prefix_error (error, "Unable to read existing detached metadata: ");
+ goto out;
}
ret = TRUE;
@@ -2184,6 +2202,10 @@ create_tree_variant_from_hashes (GHashTable *file_checksums,
while (g_hash_table_iter_next (&hash_iter, &key, &value))
{
const char *name = key;
+
+ /* Should have been validated earlier, but be paranoid */
+ g_assert (ot_util_filename_validate (name, NULL));
+
sorted_filenames = g_slist_prepend (sorted_filenames, (char*)name);
}
@@ -2355,7 +2377,7 @@ get_modified_xattrs (OstreeRepo *self,
if (label)
{
- GVariantBuilder *builder;
+ g_autoptr(GVariantBuilder) builder = NULL;
/* ret_xattrs may be NULL */
builder = ot_util_variant_builder_from_variant (ret_xattrs,
diff --git a/src/libostree/ostree-repo-file.h b/src/libostree/ostree-repo-file.h
index fb651645..6f9b79fe 100644
--- a/src/libostree/ostree-repo-file.h
+++ b/src/libostree/ostree-repo-file.h
@@ -58,9 +58,6 @@ OstreeRepo * ostree_repo_file_get_repo (OstreeRepoFile *self);
_OSTREE_PUBLIC
OstreeRepoFile * ostree_repo_file_get_root (OstreeRepoFile *self);
-_OSTREE_PUBLIC
-void ostree_repo_file_make_empty_tree (OstreeRepoFile *self);
-
_OSTREE_PUBLIC
void ostree_repo_file_tree_set_metadata (OstreeRepoFile *self,
const char *checksum,
diff --git a/src/libostree/ostree-repo-libarchive.c b/src/libostree/ostree-repo-libarchive.c
index 7a30192c..45427ef7 100644
--- a/src/libostree/ostree-repo-libarchive.c
+++ b/src/libostree/ostree-repo-libarchive.c
@@ -37,6 +37,8 @@
#ifdef HAVE_LIBARCHIVE
+#define DEFAULT_DIRMODE (0755 | S_IFDIR)
+
static void
propagate_libarchive_error (GError **error,
struct archive *a)
@@ -45,269 +47,761 @@ propagate_libarchive_error (GError **error,
"%s", archive_error_string (a));
}
+static const char *
+path_relative (const char *src,
+ GError **error)
+{
+ /* One issue here is that some archives almost record the pathname as just a
+ * string and don't need to actually encode parent/child relationships in the
+ * archive. For us however, this will be important. So we do our best to deal
+ * with non-conventional paths. We also validate the path at the end to make
+ * sure there are no illegal components. Also important, we relativize the
+ * path. */
+
+ /* relativize first (and make /../../ --> /) */
+ while (src[0] == '/')
+ {
+ src += 1;
+ if (src[0] == '.' && src[1] == '.' && src[2] == '/')
+ src += 2; /* keep trailing / so we continue */
+ }
+
+ /* now let's skip . and empty components */
+ while (TRUE)
+ {
+ if (src[0] == '.' && src[1] == '/')
+ src += 2;
+ else if (src[0] == '/')
+ src += 1;
+ else
+ break;
+ }
+
+ /* assume a single '.' means the root dir itself, which we handle as the empty
+ * string in our code */
+ if (src[0] == '.' && src[1] == '\0')
+ src += 1;
+
+ /* make sure that the final path is valid (no . or ..) */
+ if (!ot_util_path_split_validate (src, NULL, error))
+ {
+ g_prefix_error (error, "While making relative path \"%s\":", src);
+ return NULL;
+ }
+
+ return src;
+}
+
+static char *
+path_relative_ostree (const char *path,
+ GError **error)
+{
+ path = path_relative (path, error);
+ if (path == NULL)
+ return NULL;
+ if (g_str_has_prefix (path, "etc/"))
+ return g_strconcat ("usr/", path, NULL);
+ else if (strcmp (path, "etc") == 0)
+ return g_strdup ("usr/etc");
+ return g_strdup (path);
+}
+
+static void
+append_path_component (char **path_builder,
+ const char *component)
+{
+ g_autofree char *s = g_steal_pointer (path_builder);
+ *path_builder = g_build_filename (s ?: "/", component, NULL);
+}
+
+/* inplace trailing slash squashing */
+static void
+squash_trailing_slashes (char *path)
+{
+ char *endp = path + strlen (path) - 1;
+ for (; endp > path && *endp == '/'; endp--)
+ *endp = '\0';
+}
+
static GFileInfo *
-file_info_from_archive_entry_and_modifier (OstreeRepo *repo,
- struct archive_entry *entry,
- OstreeRepoCommitModifier *modifier)
+file_info_from_archive_entry (struct archive_entry *entry)
{
g_autoptr(GFileInfo) info = NULL;
- GFileInfo *modified_info = NULL;
- const struct stat *st;
+ const struct stat *st = NULL;
guint32 file_type;
+ mode_t mode;
st = archive_entry_stat (entry);
+ mode = st->st_mode;
- info = _ostree_header_gfile_info_new (st->st_mode, st->st_uid, st->st_gid);
- file_type = ot_gfile_type_for_mode (st->st_mode);
+ /* Some archives only store the permission mode bits in hardlink entries, so
+ * let's just make it into a regular file. Yes, this hack will work even if
+ * it's a hardlink to a symlink. */
+ if (archive_entry_hardlink (entry))
+ mode |= S_IFREG;
+ info = _ostree_header_gfile_info_new (mode, st->st_uid, st->st_gid);
+
+ file_type = ot_gfile_type_for_mode (mode);
if (file_type == G_FILE_TYPE_REGULAR)
{
g_file_info_set_attribute_uint64 (info, "standard::size", st->st_size);
}
else if (file_type == G_FILE_TYPE_SYMBOLIC_LINK)
{
- g_file_info_set_attribute_byte_string (info, "standard::symlink-target", archive_entry_symlink (entry));
+ g_file_info_set_attribute_byte_string (info, "standard::symlink-target",
+ archive_entry_symlink (entry));
}
- _ostree_repo_commit_modifier_apply (repo, modifier,
- archive_entry_pathname (entry),
- info, &modified_info);
-
- return modified_info;
+ return g_steal_pointer (&info);
}
static gboolean
-import_libarchive_entry_file (OstreeRepo *self,
- OstreeRepoImportArchiveOptions *opts,
- struct archive *a,
- struct archive_entry *entry,
- GFileInfo *file_info,
- guchar **out_csum,
- GCancellable *cancellable,
- GError **error)
+builder_add_label (GVariantBuilder *builder,
+ OstreeSePolicy *sepolicy,
+ const char *path,
+ mode_t mode,
+ GCancellable *cancellable,
+ GError **error)
{
- gboolean ret = FALSE;
- g_autoptr(GInputStream) file_object_input = NULL;
- g_autoptr(GInputStream) archive_stream = NULL;
- guint64 length;
-
- if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ g_autofree char *label = NULL;
+
+ if (!sepolicy)
+ return TRUE;
+
+ if (!ostree_sepolicy_get_label (sepolicy, path, mode, &label,
+ cancellable, error))
return FALSE;
- switch (g_file_info_get_file_type (file_info))
- {
- case G_FILE_TYPE_REGULAR:
- archive_stream = _ostree_libarchive_input_stream_new (a);
- break;
- case G_FILE_TYPE_SYMBOLIC_LINK:
- break;
- default:
- if (opts->ignore_unsupported_content)
- {
- ret = TRUE;
- goto out;
- }
- else
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Unable to import non-regular/non-symlink file '%s'",
- archive_entry_pathname (entry));
- goto out;
- }
- }
-
- if (!ostree_raw_file_to_content_stream (archive_stream, file_info, NULL,
- &file_object_input, &length, cancellable, error))
- goto out;
-
- if (!ostree_repo_write_content (self, NULL, file_object_input, length, out_csum,
- cancellable, error))
- goto out;
-
- ret = TRUE;
- out:
- return ret;
+ if (label)
+ g_variant_builder_add (builder, "(@ay@ay)",
+ g_variant_new_bytestring ("security.selinux"),
+ g_variant_new_bytestring (label));
+ return TRUE;
}
+
+/* Like ostree_mutable_tree_ensure_dir(), but also creates and sets dirmeta if
+ * the dir has to be created. */
static gboolean
-write_libarchive_entry_to_mtree (OstreeRepo *self,
- OstreeRepoImportArchiveOptions *opts,
- OstreeMutableTree *root,
- struct archive *a,
- struct archive_entry *entry,
- OstreeRepoCommitModifier *modifier,
- const guchar *tmp_dir_csum,
- GCancellable *cancellable,
- GError **error)
+mtree_ensure_dir_with_meta (OstreeRepo *repo,
+ OstreeMutableTree *parent,
+ const char *name,
+ GFileInfo *file_info,
+ GVariant *xattrs,
+ gboolean error_if_exist, /* XXX: remove if not needed */
+ OstreeMutableTree **out_dir,
+ GCancellable *cancellable,
+ GError **error)
{
- gboolean ret = FALSE;
- const char *pathname;
- const char *hardlink;
- const char *basename;
+ glnx_unref_object OstreeMutableTree *dir = NULL;
+ g_autofree guchar *csum_raw = NULL;
+ g_autofree char *csum = NULL;
+
+ if (name[0] == '\0') /* root? */
+ dir = g_object_ref (parent);
+ else if (ostree_mutable_tree_lookup (parent, name, NULL, &dir, error))
+ {
+ if (error_if_exist)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Directory \"%s\" already exists", name);
+ return FALSE;
+ }
+ }
+
+ if (dir == NULL)
+ {
+ if (!g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ return FALSE;
+
+ g_clear_error (error);
+
+ if (!ostree_mutable_tree_ensure_dir (parent, name, &dir, error))
+ return FALSE;
+ }
+
+ if (!_ostree_repo_write_directory_meta (repo, file_info, xattrs,
+ &csum_raw, cancellable, error))
+ return FALSE;
+
+ csum = ostree_checksum_from_bytes (csum_raw);
+
+ ostree_mutable_tree_set_metadata_checksum (dir, csum);
+
+ if (out_dir)
+ *out_dir = g_steal_pointer (&dir);
+
+ return TRUE;
+}
+
+typedef struct {
+ OstreeRepo *repo;
+ OstreeRepoImportArchiveOptions *opts;
+ OstreeMutableTree *root;
+ struct archive *archive;
+ struct archive_entry *entry;
+ GHashTable *deferred_hardlinks;
+ OstreeRepoCommitModifier *modifier;
+} OstreeRepoArchiveImportContext;
+
+typedef struct {
+ OstreeMutableTree *parent;
+ char *path;
+ guint64 size;
+} DeferredHardlink;
+
+static inline char*
+aic_get_final_path (OstreeRepoArchiveImportContext *ctx,
+ const char *path,
+ GError **error)
+{
+ if (ctx->opts->use_ostree_convention)
+ return path_relative_ostree (path, error);
+ return g_strdup (path_relative (path, error));
+}
+
+static inline char*
+aic_get_final_entry_pathname (OstreeRepoArchiveImportContext *ctx,
+ GError **error)
+{
+ const char *pathname = archive_entry_pathname (ctx->entry);
+ g_autofree char *final = aic_get_final_path (ctx, pathname, error);
+
+ if (final == NULL)
+ return NULL;
+
+ /* get rid of trailing slashes some archives put on dirs */
+ squash_trailing_slashes (final);
+ return g_steal_pointer (&final);
+}
+
+static inline char*
+aic_get_final_entry_hardlink (OstreeRepoArchiveImportContext *ctx)
+{
+ GError *local_error = NULL;
+ const char *hardlink = archive_entry_hardlink (ctx->entry);
+ g_autofree char *final = NULL;
+
+ if (hardlink != NULL)
+ {
+ final = aic_get_final_path (ctx, hardlink, &local_error);
+
+ /* hardlinks always point to a preceding entry, so if there were an error
+ * it would have failed then */
+ g_assert_no_error (local_error);
+ }
+
+ return g_steal_pointer (&final);
+}
+
+static OstreeRepoCommitFilterResult
+aic_apply_modifier_filter (OstreeRepoArchiveImportContext *ctx,
+ const char *relpath,
+ GFileInfo **out_file_info)
+{
+ g_autofree char *hardlink = aic_get_final_entry_hardlink (ctx);
g_autoptr(GFileInfo) file_info = NULL;
- g_autoptr(GPtrArray) split_path = NULL;
- g_autoptr(GPtrArray) hardlink_split_path = NULL;
- glnx_unref_object OstreeMutableTree *subdir = NULL;
- glnx_unref_object OstreeMutableTree *parent = NULL;
- glnx_unref_object OstreeMutableTree *hardlink_source_parent = NULL;
- g_autofree char *hardlink_source_checksum = NULL;
- glnx_unref_object OstreeMutableTree *hardlink_source_subdir = NULL;
- g_autofree guchar *tmp_csum = NULL;
- g_autofree char *tmp_checksum = NULL;
+ g_autofree char *abspath = NULL;
+ const char *cb_path = NULL;
- pathname = archive_entry_pathname (entry);
-
- if (!ot_util_path_split_validate (pathname, &split_path, error))
- goto out;
-
- if (split_path->len == 0)
- {
- parent = NULL;
- basename = NULL;
- }
+ if (ctx->opts->callback_with_entry_pathname)
+ cb_path = archive_entry_pathname (ctx->entry);
else
{
- if (tmp_dir_csum)
- {
- g_free (tmp_checksum);
- tmp_checksum = ostree_checksum_from_bytes (tmp_dir_csum);
- if (!ostree_mutable_tree_ensure_parent_dirs (root, split_path,
- tmp_checksum,
- &parent,
- error))
- goto out;
- }
- else
- {
- if (!ostree_mutable_tree_walk (root, split_path, 0, &parent, error))
- goto out;
- }
- basename = (char*)split_path->pdata[split_path->len-1];
+ /* the user expects an abspath (where the dir to commit represents /) */
+ abspath = g_build_filename ("/", relpath, NULL);
+ cb_path = abspath;
}
- hardlink = archive_entry_hardlink (entry);
- if (hardlink)
- {
- const char *hardlink_basename;
-
- g_assert (parent != NULL);
+ file_info = file_info_from_archive_entry (ctx->entry);
- if (!ot_util_path_split_validate (hardlink, &hardlink_split_path, error))
- goto out;
- if (hardlink_split_path->len == 0)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Invalid hardlink path %s", hardlink);
- goto out;
- }
-
- hardlink_basename = hardlink_split_path->pdata[hardlink_split_path->len - 1];
-
- if (!ostree_mutable_tree_walk (root, hardlink_split_path, 0, &hardlink_source_parent, error))
- goto out;
-
- if (!ostree_mutable_tree_lookup (hardlink_source_parent, hardlink_basename,
- &hardlink_source_checksum,
- &hardlink_source_subdir,
- error))
- {
- g_prefix_error (error, "While resolving hardlink target: ");
- goto out;
- }
-
- if (hardlink_source_subdir)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Hardlink %s refers to directory %s",
- pathname, hardlink);
- goto out;
- }
- g_assert (hardlink_source_checksum);
-
- if (!ostree_mutable_tree_replace_file (parent,
- basename,
- hardlink_source_checksum,
- error))
- goto out;
- }
- else
- {
- file_info = file_info_from_archive_entry_and_modifier (self, entry, modifier);
-
- if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_UNKNOWN)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Unsupported file for import: %s", pathname);
- goto out;
- }
-
- if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY)
- {
-
- if (!_ostree_repo_write_directory_meta (self, file_info, NULL, &tmp_csum, cancellable, error))
- goto out;
-
- if (parent == NULL)
- {
- subdir = g_object_ref (root);
- }
- else
- {
- if (!ostree_mutable_tree_ensure_dir (parent, basename, &subdir, error))
- goto out;
- }
-
- g_free (tmp_checksum);
- tmp_checksum = ostree_checksum_from_bytes (tmp_csum);
- ostree_mutable_tree_set_metadata_checksum (subdir, tmp_checksum);
- }
- else
- {
- if (parent == NULL)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Can't import file as root");
- goto out;
- }
-
- if (!import_libarchive_entry_file (self, opts, a, entry, file_info, &tmp_csum,
- cancellable, error))
- goto out;
-
- if (tmp_csum)
- {
- g_free (tmp_checksum);
- tmp_checksum = ostree_checksum_from_bytes (tmp_csum);
- if (!ostree_mutable_tree_replace_file (parent, basename,
- tmp_checksum,
- error))
- goto out;
- }
- }
- }
-
- ret = TRUE;
- out:
- return ret;
+ return _ostree_repo_commit_modifier_apply (ctx->repo, ctx->modifier, cb_path,
+ file_info, out_file_info);
}
static gboolean
-create_empty_dir_with_uidgid (OstreeRepo *self,
- guint32 uid,
- guint32 gid,
- guint8 **out_csum,
- GCancellable *cancellable,
- GError **error)
+aic_ensure_parent_dir_with_file_info (OstreeRepoArchiveImportContext *ctx,
+ OstreeMutableTree *parent,
+ const char *fullpath,
+ GFileInfo *file_info,
+ OstreeMutableTree **out_dir,
+ GCancellable *cancellable,
+ GError **error)
{
- g_autoptr(GFileInfo) tmp_dir_info = g_file_info_new ();
-
- g_file_info_set_attribute_uint32 (tmp_dir_info, "unix::uid", uid);
- g_file_info_set_attribute_uint32 (tmp_dir_info, "unix::gid", gid);
- g_file_info_set_attribute_uint32 (tmp_dir_info, "unix::mode", 0755 | S_IFDIR);
-
- return _ostree_repo_write_directory_meta (self, tmp_dir_info, NULL, out_csum, cancellable, error);
+ const char *name = glnx_basename (fullpath);
+ g_auto(GVariantBuilder) xattrs_builder;
+ g_autoptr(GVariant) xattrs = NULL;
+
+ /* is this the root directory itself? transform into empty string */
+ if (name[0] == '/' && name[1] == '\0')
+ name++;
+
+ g_variant_builder_init (&xattrs_builder, (GVariantType*)"a(ayay)");
+
+ if (ctx->modifier && ctx->modifier->sepolicy)
+ if (!builder_add_label (&xattrs_builder, ctx->modifier->sepolicy, fullpath,
+ DEFAULT_DIRMODE, cancellable, error))
+ return FALSE;
+
+ xattrs = g_variant_ref_sink (g_variant_builder_end (&xattrs_builder));
+ return mtree_ensure_dir_with_meta (ctx->repo, parent, name, file_info,
+ xattrs,
+ FALSE /* error_if_exist */, out_dir,
+ cancellable, error);
}
-#endif
+
+static gboolean
+aic_ensure_parent_dir (OstreeRepoArchiveImportContext *ctx,
+ OstreeMutableTree *parent,
+ const char *fullpath,
+ OstreeMutableTree **out_dir,
+ GCancellable *cancellable,
+ GError **error)
+{
+ /* Who should own the parent dir? Since it's not in the archive, it's up to
+ * us. Here, we use the heuristic of simply creating it as the same user as
+ * the owner of the archive entry for which we're creating the dir. This is OK
+ * since any nontrivial dir perms should have explicit archive entries. */
+
+ guint32 uid = archive_entry_uid (ctx->entry);
+ guint32 gid = archive_entry_gid (ctx->entry);
+ glnx_unref_object GFileInfo *file_info = g_file_info_new ();
+
+ g_file_info_set_attribute_uint32 (file_info, "unix::uid", uid);
+ g_file_info_set_attribute_uint32 (file_info, "unix::gid", gid);
+ g_file_info_set_attribute_uint32 (file_info, "unix::mode", DEFAULT_DIRMODE);
+
+ return aic_ensure_parent_dir_with_file_info (ctx, parent, fullpath, file_info,
+ out_dir, cancellable, error);
+}
+
+static gboolean
+aic_create_parent_dirs (OstreeRepoArchiveImportContext *ctx,
+ GPtrArray *components,
+ OstreeMutableTree **out_subdir,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autofree char *fullpath = NULL;
+ glnx_unref_object OstreeMutableTree *dir = NULL;
+
+ /* start with the root itself */
+ if (!aic_ensure_parent_dir (ctx, ctx->root, "/", &dir, cancellable, error))
+ return FALSE;
+
+ for (guint i = 0; i < components->len-1; i++)
+ {
+ glnx_unref_object OstreeMutableTree *subdir = NULL;
+ append_path_component (&fullpath, components->pdata[i]);
+
+ if (!aic_ensure_parent_dir (ctx, dir, fullpath, &subdir,
+ cancellable, error))
+ return FALSE;
+
+ g_set_object (&dir, subdir);
+ }
+
+ *out_subdir = g_steal_pointer (&dir);
+ return TRUE;
+}
+
+static gboolean
+aic_get_parent_dir (OstreeRepoArchiveImportContext *ctx,
+ const char *path,
+ OstreeMutableTree **out_dir,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GPtrArray) components = NULL;
+ if (!ot_util_path_split_validate (path, &components, error))
+ return FALSE;
+
+ if (components->len == 0) /* root dir? */
+ {
+ *out_dir = g_object_ref (ctx->root);
+ return TRUE;
+ }
+
+ if (ostree_mutable_tree_walk (ctx->root, components, 0, out_dir, error))
+ return TRUE; /* already exists, nice! */
+
+ if (!g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ return FALSE; /* some other error occurred */
+
+ if (ctx->opts->autocreate_parents)
+ {
+ g_clear_error (error);
+ return aic_create_parent_dirs (ctx, components, out_dir,
+ cancellable, error);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+aic_get_xattrs (OstreeRepoArchiveImportContext *ctx,
+ const char *path,
+ GFileInfo *file_info,
+ GVariant **out_xattrs,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autofree char *abspath = g_build_filename ("/", path, NULL);
+ g_autoptr(GVariant) xattrs = NULL;
+ const char *cb_path = abspath;
+
+ if (ctx->opts->callback_with_entry_pathname)
+ cb_path = archive_entry_pathname (ctx->entry);
+
+ if (ctx->modifier && ctx->modifier->xattr_callback)
+ xattrs = ctx->modifier->xattr_callback (ctx->repo, cb_path, file_info,
+ ctx->modifier->xattr_user_data);
+
+ if (ctx->modifier && ctx->modifier->sepolicy)
+ {
+ mode_t mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
+ g_autoptr(GVariantBuilder) builder =
+ ot_util_variant_builder_from_variant (xattrs, G_VARIANT_TYPE
+ ("a(ayay)"));
+
+ if (!builder_add_label (builder, ctx->modifier->sepolicy, abspath, mode,
+ cancellable, error))
+ return FALSE;
+
+ if (xattrs)
+ g_variant_unref (xattrs);
+
+ xattrs = g_variant_builder_end (builder);
+ g_variant_ref_sink (xattrs);
+ }
+
+ *out_xattrs = g_steal_pointer (&xattrs);
+ return TRUE;
+}
+
+/* XXX: add option in ctx->opts to disallow already existing dirs? see
+ * error_if_exist */
+static gboolean
+aic_handle_dir (OstreeRepoArchiveImportContext *ctx,
+ OstreeMutableTree *parent,
+ const char *path,
+ GFileInfo *fi,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const char *name = glnx_basename (path);
+ g_autoptr(GVariant) xattrs = NULL;
+
+ if (!aic_get_xattrs (ctx, path, fi, &xattrs, cancellable, error))
+ return FALSE;
+
+ return mtree_ensure_dir_with_meta (ctx->repo, parent, name, fi, xattrs,
+ FALSE /* error_if_exist */, NULL,
+ cancellable, error);
+}
+
+static gboolean
+aic_write_file (OstreeRepoArchiveImportContext *ctx,
+ GFileInfo *fi,
+ GVariant *xattrs,
+ char **out_csum,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GInputStream) archive_stream = NULL;
+ g_autoptr(GInputStream) file_object_input = NULL;
+ guint64 length;
+
+ g_autofree guchar *csum_raw = NULL;
+
+ if (g_file_info_get_file_type (fi) == G_FILE_TYPE_REGULAR)
+ archive_stream = _ostree_libarchive_input_stream_new (ctx->archive);
+
+ if (!ostree_raw_file_to_content_stream (archive_stream, fi, xattrs,
+ &file_object_input, &length,
+ cancellable, error))
+ return FALSE;
+
+ if (!ostree_repo_write_content (ctx->repo, NULL, file_object_input, length,
+ &csum_raw, cancellable, error))
+ return FALSE;
+
+ *out_csum = ostree_checksum_from_bytes (csum_raw);
+ return TRUE;
+}
+
+static gboolean
+aic_import_file (OstreeRepoArchiveImportContext *ctx,
+ OstreeMutableTree *parent,
+ const char *path,
+ GFileInfo *fi,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const char *name = glnx_basename (path);
+ g_autoptr(GVariant) xattrs = NULL;
+ g_autofree char *csum = NULL;
+
+ if (!aic_get_xattrs (ctx, path, fi, &xattrs, cancellable, error))
+ return FALSE;
+
+ if (!aic_write_file (ctx, fi, xattrs, &csum, cancellable, error))
+ return FALSE;
+
+ if (!ostree_mutable_tree_replace_file (parent, name, csum, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+aic_add_deferred_hardlink (OstreeRepoArchiveImportContext *ctx,
+ const char *hardlink,
+ DeferredHardlink *dh)
+{
+ gboolean new_slist;
+ GSList *slist;
+
+ slist = g_hash_table_lookup (ctx->deferred_hardlinks, hardlink);
+ new_slist = (slist == NULL);
+
+ slist = g_slist_append (slist, dh);
+
+ if (new_slist)
+ g_hash_table_insert (ctx->deferred_hardlinks, g_strdup (hardlink), slist);
+}
+
+static void
+aic_defer_hardlink (OstreeRepoArchiveImportContext *ctx,
+ OstreeMutableTree *parent,
+ const char *path,
+ guint64 size,
+ const char *hardlink)
+{
+ DeferredHardlink *dh = g_slice_new (DeferredHardlink);
+ dh->parent = g_object_ref (parent);
+ dh->path = g_strdup (path);
+ dh->size = size;
+
+ aic_add_deferred_hardlink (ctx, hardlink, dh);
+}
+
+static gboolean
+aic_handle_file (OstreeRepoArchiveImportContext *ctx,
+ OstreeMutableTree *parent,
+ const char *path,
+ GFileInfo *fi,
+ GCancellable *cancellable,
+ GError **error)
+{
+ /* The wonderful world of hardlinks and archives. We have to be very careful
+ * here. Do not assume that if a file is a hardlink, it will have size 0 (e.g.
+ * cpio). Do not assume that if a file will have hardlinks to it, it will have
+ * size > 0. Also do not assume that its nlink param is present (tar) or even
+ * accurate (cpio). Also do not assume that hardlinks follow each other in
+ * order of entries.
+ *
+ * These archives were made to be extracted onto a filesystem, not directly
+ * hashed into an object store. So to be careful, we defer all hardlink
+ * imports until the very end. Nonzero files have to be imported, hardlink or
+ * not, since we can't easily seek back to this position later on.
+ * */
+
+ g_autofree char *hardlink = aic_get_final_entry_hardlink (ctx);
+ guint64 size = g_file_info_get_attribute_uint64 (fi, "standard::size");
+
+ if (hardlink == NULL || size > 0)
+ if (!aic_import_file (ctx, parent, path, fi, cancellable, error))
+ return FALSE;
+
+ if (hardlink)
+ aic_defer_hardlink (ctx, parent, path, size, hardlink);
+
+ return TRUE;
+}
+
+static gboolean
+aic_handle_entry (OstreeRepoArchiveImportContext *ctx,
+ OstreeMutableTree *parent,
+ const char *path,
+ GFileInfo *fi,
+ GCancellable *cancellable,
+ GError **error)
+{
+ switch (g_file_info_get_file_type (fi))
+ {
+ case G_FILE_TYPE_DIRECTORY:
+ return aic_handle_dir (ctx, parent, path, fi, cancellable, error);
+ case G_FILE_TYPE_REGULAR:
+ case G_FILE_TYPE_SYMBOLIC_LINK:
+ return aic_handle_file (ctx, parent, path, fi, cancellable, error);
+ default:
+ if (ctx->opts->ignore_unsupported_content)
+ return TRUE;
+ else
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Unsupported file type for path \"%s\"", path);
+ return FALSE;
+ }
+ }
+}
+
+static gboolean
+aic_import_entry (OstreeRepoArchiveImportContext *ctx,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GFileInfo) fi = NULL;
+ glnx_unref_object OstreeMutableTree *parent = NULL;
+ g_autofree char *path = aic_get_final_entry_pathname (ctx, error);
+
+ if (path == NULL)
+ return FALSE;
+
+ if (aic_apply_modifier_filter (ctx, path, &fi)
+ == OSTREE_REPO_COMMIT_FILTER_SKIP)
+ return TRUE;
+
+ if (!aic_get_parent_dir (ctx, path, &parent, cancellable, error))
+ return FALSE;
+
+ return aic_handle_entry (ctx, parent, path, fi, cancellable, error);
+}
+
+static gboolean
+aic_import_from_hardlink (OstreeRepoArchiveImportContext *ctx,
+ const char *target,
+ DeferredHardlink *dh,
+ GError **error)
+{
+ g_autofree char *csum = NULL;
+ const char *name = glnx_basename (target);
+ const char *name_dh = glnx_basename (dh->path);
+ g_autoptr(GPtrArray) components = NULL;
+ glnx_unref_object OstreeMutableTree *parent = NULL;
+
+ if (!ostree_mutable_tree_lookup (dh->parent, name_dh, &csum, NULL, error))
+ return FALSE;
+
+ g_assert (csum);
+
+ if (!ot_util_path_split_validate (target, &components, error))
+ return FALSE;
+
+ if (!ostree_mutable_tree_walk (ctx->root, components, 0, &parent, error))
+ return FALSE;
+
+ if (!ostree_mutable_tree_replace_file (parent, name, csum, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+aic_lookup_file_csum (OstreeRepoArchiveImportContext *ctx,
+ const char *target,
+ char **out_csum,
+ GError **error)
+{
+ g_autofree char *csum = NULL;
+ const char *name = glnx_basename (target);
+ glnx_unref_object OstreeMutableTree *parent = NULL;
+ glnx_unref_object OstreeMutableTree *subdir = NULL;
+ g_autoptr(GPtrArray) components = NULL;
+
+ if (!ot_util_path_split_validate (target, &components, error))
+ return FALSE;
+
+ if (!ostree_mutable_tree_walk (ctx->root, components, 0, &parent, error))
+ return FALSE;
+
+ if (!ostree_mutable_tree_lookup (parent, name, &csum, &subdir, error))
+ return FALSE;
+
+ if (subdir != NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Expected hardlink file target at \"%s\" but found a "
+ "directory", target);
+ return FALSE;
+ }
+
+ *out_csum = g_steal_pointer (&csum);
+ return TRUE;
+}
+
+static gboolean
+aic_import_deferred_hardlinks_for (OstreeRepoArchiveImportContext *ctx,
+ const char *target,
+ GSList *hardlinks,
+ GError **error)
+{
+ GSList *payload = hardlinks;
+ g_autofree char *csum = NULL;
+
+ /* find node with the payload, if any (if none, then they're all hardlinks to
+ * a zero sized target, and there's no rewrite required) */
+ while (payload && ((DeferredHardlink*)payload->data)->size == 0)
+ payload = g_slist_next (payload);
+
+ /* rewrite the target so it points to the csum of the payload hardlink */
+ if (payload)
+ if (!aic_import_from_hardlink (ctx, target, payload->data, error))
+ return FALSE;
+
+ if (!aic_lookup_file_csum (ctx, target, &csum, error))
+ return FALSE;
+
+ /* import all the hardlinks */
+ for (GSList *hl = hardlinks; hl != NULL; hl = g_slist_next (hl))
+ {
+ DeferredHardlink *df = hl->data;
+ const char *name = glnx_basename (df->path);
+
+ if (hl == payload)
+ continue; /* small optimization; no need to redo this one */
+
+ if (!ostree_mutable_tree_replace_file (df->parent, name, csum, error))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+aic_import_deferred_hardlinks (OstreeRepoArchiveImportContext *ctx,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, ctx->deferred_hardlinks);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ if (!aic_import_deferred_hardlinks_for (ctx, key, value, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+deferred_hardlink_free (void *data)
+{
+ DeferredHardlink *dh = data;
+ g_object_unref (dh->parent);
+ g_free (dh->path);
+ g_slice_free (DeferredHardlink, dh);
+}
+
+static void
+deferred_hardlinks_list_free (void *data)
+{
+ GSList *slist = data;
+ g_slist_free_full (slist, deferred_hardlink_free);
+}
+#endif /* HAVE_LIBARCHIVE */
/**
* ostree_repo_import_archive_to_mtree:
@@ -334,61 +828,56 @@ ostree_repo_import_archive_to_mtree (OstreeRepo *self,
#ifdef HAVE_LIBARCHIVE
gboolean ret = FALSE;
struct archive *a = archive;
- struct archive_entry *entry;
- g_autofree guchar *tmp_csum = NULL;
- int r;
+ g_autoptr(GHashTable) deferred_hardlinks =
+ g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ deferred_hardlinks_list_free);
+ OstreeRepoArchiveImportContext aictx = {
+ .repo = self,
+ .opts = opts,
+ .root = mtree,
+ .archive = archive,
+ .deferred_hardlinks = deferred_hardlinks,
+ .modifier = modifier
+ };
while (TRUE)
{
- r = archive_read_next_header (a, &entry);
+ int r = archive_read_next_header (a, &aictx.entry);
if (r == ARCHIVE_EOF)
break;
- else if (r != ARCHIVE_OK)
+ if (r != ARCHIVE_OK)
{
propagate_libarchive_error (error, a);
goto out;
}
- /* TODO - refactor this to only create the metadata on demand
- * (i.e. if there is a missing parent dir)
- */
- if (opts->autocreate_parents && !tmp_csum)
- {
- /* Here, we auto-pick the first uid/gid we find in the
- * archive. Realistically this is probably always going to
- * be root, but eh, at least we try to match.
- */
- if (!create_empty_dir_with_uidgid (self, archive_entry_uid (entry),
- archive_entry_gid (entry),
- &tmp_csum, cancellable, error))
- goto out;
- }
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ goto out;
- if (!write_libarchive_entry_to_mtree (self, opts, mtree, a,
- entry, modifier, tmp_csum,
- cancellable, error))
+ if (!aic_import_entry (&aictx, cancellable, error))
goto out;
}
+ if (!aic_import_deferred_hardlinks (&aictx, cancellable, error))
+ goto out;
+
/* If we didn't import anything at all, and autocreation of parents
* is enabled, automatically create a root directory. This is
* useful primarily when importing Docker image layers, which can
* just be metadata.
*/
- if (!ostree_mutable_tree_get_metadata_checksum (mtree) && opts->autocreate_parents)
+ if (opts->autocreate_parents &&
+ ostree_mutable_tree_get_metadata_checksum (mtree) == NULL)
{
- char tmp_checksum[65];
+ glnx_unref_object GFileInfo *fi = g_file_info_new ();
+ g_file_info_set_attribute_uint32 (fi, "unix::uid", 0);
+ g_file_info_set_attribute_uint32 (fi, "unix::gid", 0);
+ g_file_info_set_attribute_uint32 (fi, "unix::mode", DEFAULT_DIRMODE);
- if (!tmp_csum)
- {
- /* We didn't have any archive entries to match, so pick uid 0, gid 0. */
- if (!create_empty_dir_with_uidgid (self, 0, 0, &tmp_csum, cancellable, error))
- goto out;
- }
-
- ostree_checksum_inplace_from_bytes (tmp_csum, tmp_checksum);
- ostree_mutable_tree_set_metadata_checksum (mtree, tmp_checksum);
+ if (!aic_ensure_parent_dir_with_file_info (&aictx, mtree, "/", fi, NULL,
+ cancellable, error))
+ goto out;
}
ret = TRUE;
@@ -400,7 +889,7 @@ ostree_repo_import_archive_to_mtree (OstreeRepo *self,
return FALSE;
#endif
}
-
+
/**
* ostree_repo_write_archive_to_mtree:
* @self: An #OstreeRepo
@@ -420,8 +909,8 @@ ostree_repo_write_archive_to_mtree (OstreeRepo *self,
OstreeMutableTree *mtree,
OstreeRepoCommitModifier *modifier,
gboolean autocreate_parents,
- GCancellable *cancellable,
- GError **error)
+ GCancellable *cancellable,
+ GError **error)
{
#ifdef HAVE_LIBARCHIVE
gboolean ret = FALSE;
@@ -480,16 +969,22 @@ file_to_archive_entry_common (GFile *root,
g_autoptr(GVariant) xattrs = NULL;
time_t ts = (time_t) opts->timestamp_secs;
- if (pathstr && !pathstr[0])
+ if (opts->path_prefix && opts->path_prefix[0])
+ {
+ g_autofree char *old_pathstr = pathstr;
+ pathstr = g_strconcat (opts->path_prefix, old_pathstr, NULL);
+ }
+
+ if (pathstr == NULL || !pathstr[0])
{
g_free (pathstr);
pathstr = g_strdup (".");
}
archive_entry_update_pathname_utf8 (entry, pathstr);
- archive_entry_set_ctime (entry, ts, 0);
- archive_entry_set_mtime (entry, ts, 0);
- archive_entry_set_atime (entry, ts, 0);
+ archive_entry_set_ctime (entry, ts, OSTREE_TIMESTAMP);
+ archive_entry_set_mtime (entry, ts, OSTREE_TIMESTAMP);
+ archive_entry_set_atime (entry, ts, OSTREE_TIMESTAMP);
archive_entry_set_uid (entry, g_file_info_get_attribute_uint32 (file_info, "unix::uid"));
archive_entry_set_gid (entry, g_file_info_get_attribute_uint32 (file_info, "unix::gid"));
archive_entry_set_mode (entry, g_file_info_get_attribute_uint32 (file_info, "unix::mode"));
diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h
index 6a9092e9..f330d169 100644
--- a/src/libostree/ostree-repo-private.h
+++ b/src/libostree/ostree-repo-private.h
@@ -23,10 +23,6 @@
#include "ostree-repo.h"
#include "libglnx.h"
-#ifdef HAVE_LIBSOUP
-#include "ostree-fetcher.h"
-#endif
-
G_BEGIN_DECLS
#define OSTREE_DELTAPART_VERSION (0)
@@ -36,6 +32,28 @@ G_BEGIN_DECLS
#define _OSTREE_SUMMARY_CACHE_DIR "summaries"
#define _OSTREE_CACHE_DIR "cache"
+#define OSTREE_TIMESTAMP (1)
+
+typedef enum {
+ OSTREE_REPO_TEST_ERROR_PRE_COMMIT = (1 << 0)
+} OstreeRepoTestErrorFlags;
+
+struct OstreeRepoCommitModifier {
+ volatile gint refcount;
+
+ OstreeRepoCommitModifierFlags flags;
+ OstreeRepoCommitFilter filter;
+ gpointer user_data;
+ GDestroyNotify destroy_notify;
+
+ OstreeRepoCommitModifierXattrCallback xattr_callback;
+ GDestroyNotify xattr_destroy;
+ gpointer xattr_user_data;
+
+ OstreeSePolicy *sepolicy;
+ GHashTable *devino_cache;
+};
+
/**
* OstreeRepo:
*
@@ -44,7 +62,7 @@ G_BEGIN_DECLS
struct OstreeRepo {
GObject parent;
- char *boot_id;
+ char *stagedir_prefix;
int commit_stagedir_fd;
char *commit_stagedir_name;
GLnxLockFile commit_stagedir_lock;
@@ -86,12 +104,15 @@ struct OstreeRepo {
uid_t target_owner_uid;
gid_t target_owner_gid;
+ guint test_error_flags; /* OstreeRepoTestErrorFlags */
+
GKeyFile *config;
GHashTable *remotes;
GMutex remotes_lock;
OstreeRepoMode mode;
gboolean enable_uncompressed_cache;
gboolean generate_sizes;
+ guint64 tmp_expiry_seconds;
OstreeRepo *parent_repo;
};
@@ -102,6 +123,9 @@ typedef struct {
char checksum[65];
} OstreeDevIno;
+#define OSTREE_REPO_TMPDIR_STAGING "staging-"
+#define OSTREE_REPO_TMPDIR_FETCHER "fetcher-"
+
gboolean
_ostree_repo_allocate_tmpdir (int tmpdir_dfd,
const char *tmpdir_prefix,
@@ -112,6 +136,16 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
GCancellable *cancellable,
GError **error);
+gboolean
+_ostree_repo_is_locked_tmpdir (const char *filename);
+
+gboolean
+_ostree_repo_try_lock_tmpdir (int tmpdir_dfd,
+ const char *tmpdir_name,
+ GLnxLockFile *file_lock_out,
+ gboolean *out_did_lock,
+ GError **error);
+
gboolean
_ostree_repo_ensure_loose_objdir_at (int dfd,
const char *loose_path,
@@ -135,8 +169,6 @@ _ostree_repo_has_loose_object (OstreeRepo *self,
const char *checksum,
OstreeObjectType objtype,
gboolean *out_is_stored,
- char *loose_path_buf,
- GFile **out_stored_path,
GCancellable *cancellable,
GError **error);
@@ -199,13 +231,6 @@ _ostree_repo_commit_modifier_apply (OstreeRepo *self,
gboolean
_ostree_repo_remote_name_is_file (const char *remote_name);
-#ifdef HAVE_LIBSOUP
-OstreeFetcher *
-_ostree_repo_remote_new_fetcher (OstreeRepo *self,
- const char *remote_name,
- GError **error);
-#endif
-
OstreeGpgVerifyResult *
_ostree_repo_gpg_verify_with_metadata (OstreeRepo *self,
GBytes *signed_data,
@@ -293,22 +318,4 @@ gboolean
_ostree_repo_update_mtime (OstreeRepo *self,
GError **error);
-/* Load the summary from the cache if the provided .sig file is the same as the
- cached version. */
-gboolean
-_ostree_repo_load_cache_summary_if_same_sig (OstreeRepo *self,
- const char *remote,
- GBytes *summary_sig,
- GBytes **summary,
- GCancellable *cancellable,
- GError **error);
-
-gboolean
-_ostree_repo_cache_summary (OstreeRepo *self,
- const char *remote,
- GBytes *summary,
- GBytes *summary_sig,
- GCancellable *cancellable,
- GError **error);
-
G_END_DECLS
diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c
index eef5f039..53847340 100644
--- a/src/libostree/ostree-repo-pull.c
+++ b/src/libostree/ostree-repo-pull.c
@@ -23,11 +23,15 @@
#include "config.h"
#include "ostree.h"
+#include "otutil.h"
+
+#ifdef HAVE_LIBSOUP
+
+#include "libglnx.h"
#include "ostree-core-private.h"
#include "ostree-repo-private.h"
#include "ostree-repo-static-delta-private.h"
#include "ostree-metalink.h"
-#include "otutil.h"
#include "ot-fs-utils.h"
#include
@@ -629,6 +633,7 @@ content_fetch_on_write_complete (GObject *object,
const char *expected_checksum;
g_autofree guchar *csum = NULL;
g_autofree char *checksum = NULL;
+ g_autofree char *checksum_obj = NULL;
if (!ostree_repo_write_content_finish ((OstreeRepo*)object, result,
&csum, error))
@@ -639,7 +644,8 @@ content_fetch_on_write_complete (GObject *object,
ostree_object_name_deserialize (fetch_data->object, &expected_checksum, &objtype);
g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
- g_debug ("write of %s complete", ostree_object_to_string (checksum, objtype));
+ checksum_obj = ostree_object_to_string (checksum, objtype);
+ g_debug ("write of %s complete", checksum_obj);
if (strcmp (checksum, expected_checksum) != 0)
{
@@ -675,6 +681,7 @@ content_fetch_on_complete (GObject *object,
g_autoptr(GInputStream) object_input = NULL;
g_autofree char *temp_path = NULL;
const char *checksum;
+ g_autofree char *checksum_obj = NULL;
OstreeObjectType objtype;
temp_path = _ostree_fetcher_request_uri_with_partial_finish (fetcher, result, error);
@@ -684,7 +691,8 @@ content_fetch_on_complete (GObject *object,
ostree_object_name_deserialize (fetch_data->object, &checksum, &objtype);
g_assert (objtype == OSTREE_OBJECT_TYPE_FILE);
- g_debug ("fetch of %s complete", ostree_object_to_string (checksum, objtype));
+ checksum_obj = ostree_object_to_string (checksum, objtype);
+ g_debug ("fetch of %s complete", checksum_obj);
if (pull_data->is_mirror && pull_data->repo->mode == OSTREE_REPO_MODE_ARCHIVE_Z2)
{
@@ -796,13 +804,16 @@ meta_fetch_on_complete (GObject *object,
g_autoptr(GVariant) metadata = NULL;
g_autofree char *temp_path = NULL;
const char *checksum;
+ g_autofree char *checksum_obj = NULL;
OstreeObjectType objtype;
GError *local_error = NULL;
GError **error = &local_error;
glnx_fd_close int fd = -1;
+ gboolean free_fetch_data = FALSE;
ostree_object_name_deserialize (fetch_data->object, &checksum, &objtype);
- g_debug ("fetch of %s%s complete", ostree_object_to_string (checksum, objtype),
+ checksum_obj = ostree_object_to_string (checksum, objtype);
+ g_debug ("fetch of %s%s complete", checksum_obj,
fetch_data->is_detached_meta ? " (detached)" : "");
temp_path = _ostree_fetcher_request_uri_with_partial_finish (fetcher, result, error);
@@ -864,6 +875,8 @@ meta_fetch_on_complete (GObject *object,
if (!fetch_data->object_is_stored)
enqueue_one_object_request (pull_data, checksum, objtype, FALSE, FALSE);
+
+ free_fetch_data = TRUE;
}
else
{
@@ -901,7 +914,7 @@ meta_fetch_on_complete (GObject *object,
pull_data->n_outstanding_metadata_fetches--;
pull_data->n_fetched_metadata++;
check_outstanding_requests_handle_error (pull_data, local_error);
- if (local_error)
+ if (local_error || free_fetch_data)
{
g_variant_unref (fetch_data->object);
g_free (fetch_data);
@@ -1445,6 +1458,7 @@ request_static_delta_superblock_sync (OtPullData *pull_data,
if (out_delta_superblock)
*out_delta_superblock = g_steal_pointer (&ret_delta_superblock);
out:
+ g_clear_pointer (&target_uri, (GDestroyNotify) soup_uri_free);
return ret;
}
@@ -1734,47 +1748,9 @@ validate_variant_is_csum (GVariant *csum,
return ret;
}
-/* documented in ostree-repo.c */
-gboolean
-ostree_repo_pull (OstreeRepo *self,
- const char *remote_name,
- char **refs_to_fetch,
- OstreeRepoPullFlags flags,
- OstreeAsyncProgress *progress,
- GCancellable *cancellable,
- GError **error)
-{
- return ostree_repo_pull_one_dir (self, remote_name, NULL, refs_to_fetch, flags, progress, cancellable, error);
-}
-
-/* Documented in ostree-repo.c */
-gboolean
-ostree_repo_pull_one_dir (OstreeRepo *self,
- const char *remote_name,
- const char *dir_to_pull,
- char **refs_to_fetch,
- OstreeRepoPullFlags flags,
- OstreeAsyncProgress *progress,
- GCancellable *cancellable,
- GError **error)
-{
- GVariantBuilder builder;
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
-
- if (dir_to_pull)
- g_variant_builder_add (&builder, "{s@v}", "subdir",
- g_variant_new_variant (g_variant_new_string (dir_to_pull)));
- g_variant_builder_add (&builder, "{s@v}", "flags",
- g_variant_new_variant (g_variant_new_int32 (flags)));
- if (refs_to_fetch)
- g_variant_builder_add (&builder, "{s@v}", "refs",
- g_variant_new_variant (g_variant_new_strv ((const char *const*) refs_to_fetch, -1)));
-
- return ostree_repo_pull_with_options (self, remote_name, g_variant_builder_end (&builder),
- progress, cancellable, error);
-}
-
-gboolean
+/* Load the summary from the cache if the provided .sig file is the same as the
+ cached version. */
+static gboolean
_ostree_repo_load_cache_summary_if_same_sig (OstreeRepo *self,
const char *remote,
GBytes *summary_sig,
@@ -1836,7 +1812,7 @@ _ostree_repo_load_cache_summary_if_same_sig (OstreeRepo *self,
return ret;
}
-gboolean
+static gboolean
_ostree_repo_cache_summary (OstreeRepo *self,
const char *remote,
GBytes *summary,
@@ -1876,7 +1852,311 @@ _ostree_repo_cache_summary (OstreeRepo *self,
}
-/* Documented in ostree-repo.c */
+static OstreeFetcher *
+_ostree_repo_remote_new_fetcher (OstreeRepo *self,
+ const char *remote_name,
+ GError **error)
+{
+ OstreeFetcher *fetcher = NULL;
+ OstreeFetcherConfigFlags fetcher_flags = 0;
+ gboolean tls_permissive = FALSE;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (OSTREE_IS_REPO (self), NULL);
+ g_return_val_if_fail (remote_name != NULL, NULL);
+
+ if (!ostree_repo_get_remote_boolean_option (self, remote_name,
+ "tls-permissive", FALSE,
+ &tls_permissive, error))
+ goto out;
+
+ if (tls_permissive)
+ fetcher_flags |= OSTREE_FETCHER_FLAGS_TLS_PERMISSIVE;
+
+ fetcher = _ostree_fetcher_new (self->tmp_dir_fd, fetcher_flags);
+
+ {
+ g_autofree char *tls_client_cert_path = NULL;
+ g_autofree char *tls_client_key_path = NULL;
+
+ if (!ostree_repo_get_remote_option (self, remote_name,
+ "tls-client-cert-path", NULL,
+ &tls_client_cert_path, error))
+ goto out;
+ if (!ostree_repo_get_remote_option (self, remote_name,
+ "tls-client-key-path", NULL,
+ &tls_client_key_path, error))
+ goto out;
+
+ if ((tls_client_cert_path != NULL) != (tls_client_key_path != NULL))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Remote \"%s\" must specify both "
+ "\"tls-client-cert-path\" and \"tls-client-key-path\"",
+ remote_name);
+ goto out;
+ }
+ else if (tls_client_cert_path != NULL)
+ {
+ g_autoptr(GTlsCertificate) client_cert = NULL;
+
+ g_assert (tls_client_key_path != NULL);
+
+ client_cert = g_tls_certificate_new_from_files (tls_client_cert_path,
+ tls_client_key_path,
+ error);
+ if (client_cert == NULL)
+ goto out;
+
+ _ostree_fetcher_set_client_cert (fetcher, client_cert);
+ }
+ }
+
+ {
+ g_autofree char *tls_ca_path = NULL;
+
+ if (!ostree_repo_get_remote_option (self, remote_name,
+ "tls-ca-path", NULL,
+ &tls_ca_path, error))
+ goto out;
+
+ if (tls_ca_path != NULL)
+ {
+ g_autoptr(GTlsDatabase) db = NULL;
+
+ db = g_tls_file_database_new (tls_ca_path, error);
+ if (db == NULL)
+ goto out;
+
+ _ostree_fetcher_set_tls_database (fetcher, db);
+ }
+ }
+
+ {
+ g_autofree char *http_proxy = NULL;
+
+ if (!ostree_repo_get_remote_option (self, remote_name,
+ "proxy", NULL,
+ &http_proxy, error))
+ goto out;
+
+ if (http_proxy != NULL)
+ _ostree_fetcher_set_proxy (fetcher, http_proxy);
+ }
+
+ success = TRUE;
+
+out:
+ if (!success)
+ g_clear_object (&fetcher);
+
+ return fetcher;
+}
+
+static gboolean
+_ostree_preload_metadata_file (OstreeRepo *self,
+ OstreeFetcher *fetcher,
+ SoupURI *base_uri,
+ const char *filename,
+ gboolean is_metalink,
+ GBytes **out_bytes,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+
+ if (is_metalink)
+ {
+ glnx_unref_object OstreeMetalink *metalink = NULL;
+ GError *local_error = NULL;
+
+ metalink = _ostree_metalink_new (fetcher, filename,
+ OSTREE_MAX_METADATA_SIZE,
+ base_uri);
+
+ _ostree_metalink_request_sync (metalink, NULL, out_bytes, NULL,
+ cancellable, &local_error);
+
+ if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ {
+ g_clear_error (&local_error);
+ *out_bytes = NULL;
+ }
+ else if (local_error != NULL)
+ {
+ g_propagate_error (error, local_error);
+ goto out;
+ }
+ }
+ else
+ {
+ SoupURI *uri;
+ const char *base_path;
+ g_autofree char *path = NULL;
+
+ base_path = soup_uri_get_path (base_uri);
+ path = g_build_filename (base_path, filename, NULL);
+ uri = soup_uri_new_with_base (base_uri, path);
+
+ ret = _ostree_fetcher_request_uri_to_membuf (fetcher, uri,
+ FALSE, TRUE,
+ out_bytes,
+ OSTREE_MAX_METADATA_SIZE,
+ cancellable, error);
+ soup_uri_free (uri);
+
+ if (!ret)
+ goto out;
+ }
+
+ ret = TRUE;
+out:
+ return ret;
+}
+
+static gboolean
+repo_remote_fetch_summary (OstreeRepo *self,
+ const char *name,
+ const char *metalink_url_string,
+ GVariant *options,
+ GBytes **out_summary,
+ GBytes **out_signatures,
+ GCancellable *cancellable,
+ GError **error)
+{
+ glnx_unref_object OstreeFetcher *fetcher = NULL;
+ g_autoptr(GMainContext) mainctx = NULL;
+ gboolean ret = FALSE;
+ SoupURI *base_uri = NULL;
+ gboolean from_cache = FALSE;
+ g_autofree char *url_override = NULL;
+
+ if (options)
+ (void) g_variant_lookup (options, "override-url", "&s", &url_override);
+
+ mainctx = g_main_context_new ();
+ g_main_context_push_thread_default (mainctx);
+
+ fetcher = _ostree_repo_remote_new_fetcher (self, name, error);
+ if (fetcher == NULL)
+ goto out;
+
+ {
+ g_autofree char *url_string = NULL;
+ if (metalink_url_string)
+ url_string = g_strdup (metalink_url_string);
+ else if (url_override)
+ url_string = g_strdup (url_override);
+ else if (!ostree_repo_remote_get_url (self, name, &url_string, error))
+ goto out;
+
+ base_uri = soup_uri_new (url_string);
+ if (base_uri == NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Invalid URL '%s'", url_string);
+ goto out;
+ }
+ }
+
+ if (!_ostree_preload_metadata_file (self,
+ fetcher,
+ base_uri,
+ "summary.sig",
+ metalink_url_string ? TRUE : FALSE,
+ out_signatures,
+ cancellable,
+ error))
+ goto out;
+
+ if (*out_signatures)
+ {
+ if (!_ostree_repo_load_cache_summary_if_same_sig (self,
+ name,
+ *out_signatures,
+ out_summary,
+ cancellable,
+ error))
+ goto out;
+ }
+
+ if (*out_summary)
+ from_cache = TRUE;
+ else
+ {
+ if (!_ostree_preload_metadata_file (self,
+ fetcher,
+ base_uri,
+ "summary",
+ metalink_url_string ? TRUE : FALSE,
+ out_summary,
+ cancellable,
+ error))
+ goto out;
+ }
+
+ if (!from_cache && *out_summary && *out_signatures)
+ {
+ g_autoptr(GError) temp_error = NULL;
+
+ if (!_ostree_repo_cache_summary (self,
+ name,
+ *out_summary,
+ *out_signatures,
+ cancellable,
+ &temp_error))
+ {
+ if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
+ g_debug ("No permissions to save summary cache");
+ else
+ {
+ g_propagate_error (error, g_steal_pointer (&temp_error));
+ goto out;
+ }
+ }
+ }
+
+ ret = TRUE;
+
+ out:
+ if (mainctx)
+ g_main_context_pop_thread_default (mainctx);
+ if (base_uri != NULL)
+ soup_uri_free (base_uri);
+ return ret;
+}
+
+/* ------------------------------------------------------------------------------------------
+ * Below is the libsoup-invariant API; these should match
+ * the stub functions in the #else clause
+ * ------------------------------------------------------------------------------------------
+ */
+
+/**
+ * ostree_repo_pull_with_options:
+ * @self: Repo
+ * @remote_name: Name of remote
+ * @options: A GVariant a{sv} with an extensible set of flags.
+ * @progress: (allow-none): Progress
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Like ostree_repo_pull(), but supports an extensible set of flags.
+ * The following are currently defined:
+ *
+ * * refs (as): Array of string refs
+ * * flags (i): An instance of #OstreeRepoPullFlags
+ * * subdir (s): Pull just this subdirectory
+ * * override-remote-name (s): If local, add this remote to refspec
+ * * gpg-verify (b): GPG verify commits
+ * * gpg-verify-summary (b): GPG verify summary
+ * * depth (i): How far in the history to traverse; default is 0, -1 means infinite
+ * * disable-static-deltas (b): Do not use static deltas
+ * * require-static-deltas (b): Require static deltas
+ * * override-commit-ids (as): Array of specific commit IDs to fetch for refs
+ * * dry-run (b): Only print information on what will be downloaded (requires static deltas)
+ * * override-url (s): Fetch objects from this URL if remote specifies no metalink in options
+ */
gboolean
ostree_repo_pull_with_options (OstreeRepo *self,
const char *remote_name_or_baseurl,
@@ -1889,8 +2169,6 @@ ostree_repo_pull_with_options (OstreeRepo *self,
GHashTableIter hash_iter;
gpointer key, value;
g_autoptr(GBytes) bytes_summary = NULL;
- g_autofree char *remote_key = NULL;
- g_autofree char *path = NULL;
g_autofree char *metalink_url_str = NULL;
g_autoptr(GHashTable) requested_refs_to_fetch = NULL;
g_autoptr(GHashTable) commits_to_fetch = NULL;
@@ -1904,13 +2182,14 @@ ostree_repo_pull_with_options (OstreeRepo *self,
guint64 end_time;
OstreeRepoPullFlags flags = 0;
const char *dir_to_pull = NULL;
- char **refs_to_fetch = NULL;
+ g_autofree char **refs_to_fetch = NULL;
char **override_commit_ids = NULL;
GSource *update_timeout = NULL;
gboolean disable_static_deltas = FALSE;
gboolean require_static_deltas = FALSE;
gboolean opt_gpg_verify = FALSE;
gboolean opt_gpg_verify_summary = FALSE;
+ const char *url_override = NULL;
if (options)
{
@@ -1928,6 +2207,7 @@ ostree_repo_pull_with_options (OstreeRepo *self,
(void) g_variant_lookup (options, "require-static-deltas", "b", &require_static_deltas);
(void) g_variant_lookup (options, "override-commit-ids", "^a&s", &override_commit_ids);
(void) g_variant_lookup (options, "dry-run", "b", &pull_data->dry_run);
+ (void) g_variant_lookup (options, "override-url", "&s", &url_override);
}
g_return_val_if_fail (pull_data->maxdepth >= -1, FALSE);
@@ -2023,7 +2303,9 @@ ostree_repo_pull_with_options (OstreeRepo *self,
{
g_autofree char *baseurl = NULL;
- if (!ostree_repo_remote_get_url (self, remote_name_or_baseurl, &baseurl, error))
+ if (url_override != NULL)
+ baseurl = g_strdup (url_override);
+ else if (!ostree_repo_remote_get_url (self, remote_name_or_baseurl, &baseurl, error))
goto out;
pull_data->base_uri = soup_uri_new (baseurl);
@@ -2587,3 +2869,121 @@ ostree_repo_pull_with_options (OstreeRepo *self,
g_clear_pointer (&remote_config, (GDestroyNotify) g_key_file_unref);
return ret;
}
+
+/**
+ * ostree_repo_remote_fetch_summary_with_options:
+ * @self: Self
+ * @name: name of a remote
+ * @options: (nullable): A GVariant a{sv} with an extensible set of flags
+ * @out_summary: (nullable): return location for raw summary data, or %NULL
+ * @out_signatures: (nullable): return location for raw summary signature
+ * data, or %NULL
+ * @cancellable: a #GCancellable
+ * @error: a #GError
+ *
+ * Like ostree_repo_remote_fetch_summary(), but supports an extensible set of flags.
+ * The following are currently defined:
+ *
+ * - override-url (s): Fetch summary from this URL if remote specifies no metalink in options
+ *
+ * Returns: %TRUE on success, %FALSE on failure
+ */
+gboolean
+ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self,
+ const char *name,
+ GVariant *options,
+ GBytes **out_summary,
+ GBytes **out_signatures,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autofree char *metalink_url_string = NULL;
+ g_autoptr(GBytes) summary = NULL;
+ g_autoptr(GBytes) signatures = NULL;
+ gboolean ret = FALSE;
+ gboolean gpg_verify_summary;
+
+ g_return_val_if_fail (OSTREE_REPO (self), FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (!ostree_repo_get_remote_option (self, name, "metalink", NULL,
+ &metalink_url_string, error))
+ goto out;
+
+ if (!repo_remote_fetch_summary (self,
+ name,
+ metalink_url_string,
+ options,
+ &summary,
+ &signatures,
+ cancellable,
+ error))
+ goto out;
+
+ if (!ostree_repo_remote_get_gpg_verify_summary (self, name, &gpg_verify_summary, error))
+ goto out;
+
+ if (gpg_verify_summary && signatures == NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "GPG verification enabled, but no summary signatures found (use gpg-verify-summary=false in remote config to disable)");
+ goto out;
+ }
+
+ /* Verify any summary signatures. */
+ if (gpg_verify_summary && summary != NULL && signatures != NULL)
+ {
+ glnx_unref_object OstreeGpgVerifyResult *result = NULL;
+
+ result = ostree_repo_verify_summary (self,
+ name,
+ summary,
+ signatures,
+ cancellable,
+ error);
+ if (!ostree_gpg_verify_result_require_valid_signature (result, error))
+ goto out;
+ }
+
+ if (out_summary != NULL)
+ *out_summary = g_steal_pointer (&summary);
+
+ if (out_signatures != NULL)
+ *out_signatures = g_steal_pointer (&signatures);
+
+ ret = TRUE;
+
+out:
+ return ret;
+}
+
+#else /* HAVE_LIBSOUP */
+
+gboolean
+ostree_repo_pull_with_options (OstreeRepo *self,
+ const char *remote_name,
+ GVariant *options,
+ OstreeAsyncProgress *progress,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "This version of ostree was built without libsoup, and cannot fetch over HTTP");
+ return FALSE;
+}
+
+gboolean
+ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self,
+ const char *name,
+ GVariant *options,
+ GBytes **out_summary,
+ GBytes **out_signatures,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "This version of ostree was built without libsoup, and cannot fetch over HTTP");
+ return FALSE;
+}
+
+#endif /* HAVE_LIBSOUP */
diff --git a/src/libostree/ostree-repo-static-delta-compilation.c b/src/libostree/ostree-repo-static-delta-compilation.c
index b3989234..4e4e7f9c 100644
--- a/src/libostree/ostree-repo-static-delta-compilation.c
+++ b/src/libostree/ostree-repo-static-delta-compilation.c
@@ -1226,7 +1226,7 @@ get_fallback_headers (OstreeRepo *self,
*
* The @params argument should be an a{sv}. The following attributes
* are known:
- * - min-fallback-size: u: Minimume uncompressed size in megabytes to use fallback, 0 to disable fallbacks
+ * - min-fallback-size: u: Minimum uncompressed size in megabytes to use fallback, 0 to disable fallbacks
* - max-chunk-size: u: Maximum size in megabytes of a delta part
* - max-bsdiff-size: u: Maximum size in megabytes to consider bsdiff compression
* for input files
@@ -1253,7 +1253,7 @@ ostree_repo_static_delta_generate (OstreeRepo *self,
guint min_fallback_size;
guint max_bsdiff_size;
guint max_chunk_size;
- GVariantBuilder metadata_builder;
+ g_auto(GVariantBuilder) metadata_builder = {{0,}};
DeltaOpts delta_opts = DELTAOPT_FLAG_NONE;
guint64 total_compressed_size = 0;
guint64 total_uncompressed_size = 0;
@@ -1384,16 +1384,18 @@ ostree_repo_static_delta_generate (OstreeRepo *self,
g_autoptr(GVariant) delta_part_content = NULL;
g_autoptr(GVariant) delta_part = NULL;
g_autoptr(GVariant) delta_part_header = NULL;
- GVariantBuilder *mode_builder = g_variant_builder_new (G_VARIANT_TYPE ("a(uuu)"));
- GVariantBuilder *xattr_builder = g_variant_builder_new (G_VARIANT_TYPE ("aa(ayay)"));
+ g_auto(GVariantBuilder) mode_builder = {{0,}};
+ g_auto(GVariantBuilder) xattr_builder = {{0,}};
guint8 compression_type_char;
+ g_variant_builder_init (&mode_builder, G_VARIANT_TYPE ("a(uuu)"));
+ g_variant_builder_init (&xattr_builder, G_VARIANT_TYPE ("aa(ayay)"));
{ guint j;
for (j = 0; j < part_builder->modes->len; j++)
- g_variant_builder_add_value (mode_builder, part_builder->modes->pdata[j]);
+ g_variant_builder_add_value (&mode_builder, part_builder->modes->pdata[j]);
for (j = 0; j < part_builder->xattrs->len; j++)
- g_variant_builder_add_value (xattr_builder, part_builder->xattrs->pdata[j]);
+ g_variant_builder_add_value (&xattr_builder, part_builder->xattrs->pdata[j]);
}
payload_b = g_string_free_to_bytes (part_builder->payload);
@@ -1403,7 +1405,7 @@ ostree_repo_static_delta_generate (OstreeRepo *self,
part_builder->operations = NULL;
/* FIXME - avoid duplicating memory here */
delta_part_content = g_variant_new ("(a(uuu)aa(ayay)@ay@ay)",
- mode_builder, xattr_builder,
+ &mode_builder, &xattr_builder,
ot_gvariant_new_ay_bytes (payload_b),
ot_gvariant_new_ay_bytes (operations_b));
g_variant_ref_sink (delta_part_content);
@@ -1481,7 +1483,8 @@ ostree_repo_static_delta_generate (OstreeRepo *self,
descriptor_dir = g_file_get_parent (descriptor_path);
- if (!gs_file_ensure_directory (descriptor_dir, TRUE, cancellable, error))
+ if (!glnx_shutil_mkdir_p_at (AT_FDCWD, gs_file_get_path_cached (descriptor_dir), 0755,
+ cancellable, error))
goto out;
for (i = 0; i < part_tempfiles->len; i++)
diff --git a/src/libostree/ostree-repo-static-delta-core.c b/src/libostree/ostree-repo-static-delta-core.c
index 9e3ed09f..de9e6c7a 100644
--- a/src/libostree/ostree-repo-static-delta-core.c
+++ b/src/libostree/ostree-repo-static-delta-core.c
@@ -126,8 +126,8 @@ ostree_repo_list_static_delta_names (OstreeRepo *self,
if (g_file_info_get_file_type (file_info2) != G_FILE_TYPE_DIRECTORY)
continue;
- name1 = gs_file_get_basename_cached (child);
- name2 = gs_file_get_basename_cached (child2);
+ name1 = g_file_info_get_name (file_info);
+ name2 = g_file_info_get_name (file_info2);
{
g_autoptr(GFile) meta_path = g_file_get_child (child2, "superblock");
@@ -159,7 +159,8 @@ ostree_repo_list_static_delta_names (OstreeRepo *self,
}
ret = TRUE;
- gs_transfer_out_value (out_deltas, &ret_deltas);
+ if (out_deltas)
+ *out_deltas = g_steal_pointer (&ret_deltas);
out:
return ret;
}
@@ -512,6 +513,7 @@ _ostree_static_delta_part_open (GInputStream *part_in,
g_bytes_get_size (inline_part_bytes) - 1);
ret_part = g_variant_new_from_bytes (G_VARIANT_TYPE (OSTREE_STATIC_DELTA_PART_PAYLOAD_FORMAT_V0),
content_bytes, trusted);
+ g_variant_ref_sink (ret_part);
}
if (!skip_checksum)
@@ -840,7 +842,7 @@ _ostree_repo_static_delta_dump (OstreeRepo *self,
if (!ot_util_variant_map_at (self->repo_dir_fd, superblock_path,
(GVariantType*)OSTREE_STATIC_DELTA_SUPERBLOCK_FORMAT,
- TRUE, &delta_superblock, error))
+ OT_VARIANT_MAP_TRUSTED, &delta_superblock, error))
goto out;
g_print ("%s\n", g_variant_print (delta_superblock, 1));
diff --git a/src/libostree/ostree-repo-traverse.c b/src/libostree/ostree-repo-traverse.c
index bb437c38..503ab329 100644
--- a/src/libostree/ostree-repo-traverse.c
+++ b/src/libostree/ostree-repo-traverse.c
@@ -22,6 +22,7 @@
#include "config.h"
+#include "libglnx.h"
#include "ostree.h"
#include "otutil.h"
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
index 08e6a48f..e86685bf 100644
--- a/src/libostree/ostree-repo.c
+++ b/src/libostree/ostree-repo.c
@@ -26,7 +26,9 @@
#include
#include
#include
+#include "libglnx.h"
#include "otutil.h"
+#include
#include "ostree-core-private.h"
#include "ostree-repo-private.h"
@@ -35,16 +37,13 @@
#include "ostree-gpg-verifier.h"
#include "ostree-repo-static-delta-private.h"
#include "ot-fs-utils.h"
-
-#ifdef HAVE_LIBSOUP
-#include "ostree-metalink.h"
-#endif
+#include "ostree-autocleanups.h"
#include
#include
/**
- * SECTION:libostree-repo
+ * SECTION:ostree-repo
* @title: Content-addressed object store
* @short_description: A git-like storage system for operating system binaries
*
@@ -131,7 +130,7 @@ static OstreeRemote *
ost_remote_new_from_keyfile (GKeyFile *keyfile,
const gchar *group)
{
- GMatchInfo *match = NULL;
+ g_autoptr(GMatchInfo) match = NULL;
OstreeRemote *remote;
static gsize regex_initialized;
@@ -158,8 +157,6 @@ ost_remote_new_from_keyfile (GKeyFile *keyfile,
ot_keyfile_copy_group (keyfile, remote->options, group);
- g_match_info_unref (match);
-
return remote;
}
@@ -322,12 +319,16 @@ ostree_repo_get_remote_option (OstreeRepo *self,
{
if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
{
- if (self->parent_repo != NULL)
- return ostree_repo_get_remote_option (self->parent_repo,
- remote_name, option_name,
- default_value,
- out_value,
- error);
+ /* Note: We ignore errors on the parent because the parent config may not
+ specify this remote, causing a "remote not found" error, but we found
+ the remote at some point, so we need to instead return the default */
+ if (self->parent_repo != NULL &&
+ ostree_repo_get_remote_option (self->parent_repo,
+ remote_name, option_name,
+ default_value,
+ out_value,
+ NULL))
+ return TRUE;
value = g_strdup (default_value);
ret = TRUE;
@@ -397,11 +398,16 @@ ostree_repo_get_remote_list_option (OstreeRepo *self,
/* Default value if key not found is always NULL. */
if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
{
- if (self->parent_repo != NULL)
- return ostree_repo_get_remote_list_option (self->parent_repo,
- remote_name, option_name,
- out_value,
- error);
+ /* Note: We ignore errors on the parent because the parent config may not
+ specify this remote, causing a "remote not found" error, but we found
+ the remote at some point, so we need to instead return the default */
+ if (self->parent_repo != NULL &&
+ ostree_repo_get_remote_list_option (self->parent_repo,
+ remote_name, option_name,
+ out_value,
+ NULL))
+ return TRUE;
+
ret = TRUE;
}
else if (temp_error)
@@ -426,7 +432,7 @@ ostree_repo_get_remote_list_option (OstreeRepo *self,
* @self: A OstreeRepo
* @remote_name: Name
* @option_name: Option
- * @default_value: (allow-none): Value returned if @option_name is not present
+ * @default_value: Value returned if @option_name is not present
* @out_value: (out) : location to store the result.
* @error: Error
*
@@ -464,12 +470,16 @@ ostree_repo_get_remote_boolean_option (OstreeRepo *self,
{
if (g_error_matches (temp_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND))
{
- if (self->parent_repo != NULL)
- return ostree_repo_get_remote_boolean_option (self->parent_repo,
- remote_name, option_name,
- default_value,
- out_value,
- error);
+ /* Note: We ignore errors on the parent because the parent config may not
+ specify this remote, causing a "remote not found" error, but we found
+ the remote at some point, so we need to instead return the default */
+ if (self->parent_repo != NULL &&
+ ostree_repo_get_remote_boolean_option (self->parent_repo,
+ remote_name, option_name,
+ default_value,
+ out_value,
+ NULL))
+ return TRUE;
value = default_value;
ret = TRUE;
@@ -493,109 +503,6 @@ ostree_repo_get_remote_boolean_option (OstreeRepo *self,
return ret;
}
-#ifdef HAVE_LIBSOUP
-OstreeFetcher *
-_ostree_repo_remote_new_fetcher (OstreeRepo *self,
- const char *remote_name,
- GError **error)
-{
- OstreeFetcher *fetcher = NULL;
- OstreeFetcherConfigFlags fetcher_flags = 0;
- gboolean tls_permissive = FALSE;
- gboolean success = FALSE;
-
- g_return_val_if_fail (OSTREE_IS_REPO (self), NULL);
- g_return_val_if_fail (remote_name != NULL, NULL);
-
- if (!ostree_repo_get_remote_boolean_option (self, remote_name,
- "tls-permissive", FALSE,
- &tls_permissive, error))
- goto out;
-
- if (tls_permissive)
- fetcher_flags |= OSTREE_FETCHER_FLAGS_TLS_PERMISSIVE;
-
- fetcher = _ostree_fetcher_new (self->tmp_dir_fd, fetcher_flags);
-
- {
- g_autofree char *tls_client_cert_path = NULL;
- g_autofree char *tls_client_key_path = NULL;
-
- if (!ostree_repo_get_remote_option (self, remote_name,
- "tls-client-cert-path", NULL,
- &tls_client_cert_path, error))
- goto out;
- if (!ostree_repo_get_remote_option (self, remote_name,
- "tls-client-key-path", NULL,
- &tls_client_key_path, error))
- goto out;
-
- if ((tls_client_cert_path != NULL) != (tls_client_key_path != NULL))
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Remote \"%s\" must specify both "
- "\"tls-client-cert-path\" and \"tls-client-key-path\"",
- remote_name);
- goto out;
- }
- else if (tls_client_cert_path != NULL)
- {
- g_autoptr(GTlsCertificate) client_cert = NULL;
-
- g_assert (tls_client_key_path != NULL);
-
- client_cert = g_tls_certificate_new_from_files (tls_client_cert_path,
- tls_client_key_path,
- error);
- if (client_cert == NULL)
- goto out;
-
- _ostree_fetcher_set_client_cert (fetcher, client_cert);
- }
- }
-
- {
- g_autofree char *tls_ca_path = NULL;
-
- if (!ostree_repo_get_remote_option (self, remote_name,
- "tls-ca-path", NULL,
- &tls_ca_path, error))
- goto out;
-
- if (tls_ca_path != NULL)
- {
- g_autoptr(GTlsDatabase) db = NULL;
-
- db = g_tls_file_database_new (tls_ca_path, error);
- if (db == NULL)
- goto out;
-
- _ostree_fetcher_set_tls_database (fetcher, db);
- }
- }
-
- {
- g_autofree char *http_proxy = NULL;
-
- if (!ostree_repo_get_remote_option (self, remote_name,
- "proxy", NULL,
- &http_proxy, error))
- goto out;
-
- if (http_proxy != NULL)
- _ostree_fetcher_set_proxy (fetcher, http_proxy);
- }
-
- success = TRUE;
-
-out:
- if (!success)
- g_clear_object (&fetcher);
-
- return fetcher;
-}
-#endif
-
static void
ostree_repo_finalize (GObject *object)
{
@@ -603,7 +510,7 @@ ostree_repo_finalize (GObject *object)
g_clear_object (&self->parent_repo);
- g_free (self->boot_id);
+ g_free (self->stagedir_prefix);
g_clear_object (&self->repodir);
if (self->repo_dir_fd != -1)
(void) close (self->repo_dir_fd);
@@ -784,6 +691,9 @@ ostree_repo_init (OstreeRepo *self)
{
static gsize gpgme_initialized;
GLnxLockFile empty_lockfile = GLNX_LOCK_FILE_INIT;
+ const GDebugKey test_error_keys[] = {
+ { "pre-commit", OSTREE_REPO_TEST_ERROR_PRE_COMMIT },
+ };
if (g_once_init_enter (&gpgme_initialized))
{
@@ -792,6 +702,9 @@ ostree_repo_init (OstreeRepo *self)
g_once_init_leave (&gpgme_initialized, 1);
}
+ self->test_error_flags = g_parse_debug_string (g_getenv ("OSTREE_REPO_TEST_ERROR"),
+ test_error_keys, G_N_ELEMENTS (test_error_keys));
+
g_mutex_init (&self->cache_lock);
g_mutex_init (&self->txn_stats_lock);
@@ -920,6 +833,14 @@ ostree_repo_is_writable (OstreeRepo *self,
return self->writable;
}
+/**
+ * _ostree_repo_update_mtime:
+ * @self: Repo
+ * @error: a #GError
+ *
+ * Bump the mtime of the repository so that programs
+ * can detect that the refs have updated.
+ */
gboolean
_ostree_repo_update_mtime (OstreeRepo *self,
GError **error)
@@ -1233,8 +1154,11 @@ impl_repo_remote_delete (OstreeRepo *self,
if (remote->file != NULL)
{
- if (!gs_file_unlink (remote->file, cancellable, error))
- goto out;
+ if (unlink (gs_file_get_path_cached (remote->file)) != 0)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
}
else
{
@@ -1809,185 +1733,12 @@ out:
return ret;
}
-#ifdef HAVE_LIBSOUP
-static gboolean
-_ostree_preload_metadata_file (OstreeRepo *self,
- OstreeFetcher *fetcher,
- SoupURI *base_uri,
- const char *filename,
- gboolean is_metalink,
- GBytes **out_bytes,
- GCancellable *cancellable,
- GError **error)
-{
- gboolean ret = FALSE;
-
- if (is_metalink)
- {
- glnx_unref_object OstreeMetalink *metalink = NULL;
- GError *local_error = NULL;
-
- metalink = _ostree_metalink_new (fetcher, filename,
- OSTREE_MAX_METADATA_SIZE,
- base_uri);
-
- _ostree_metalink_request_sync (metalink, NULL, out_bytes, NULL,
- cancellable, &local_error);
-
- if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
- {
- g_clear_error (&local_error);
- *out_bytes = NULL;
- }
- else if (local_error != NULL)
- {
- g_propagate_error (error, local_error);
- goto out;
- }
- }
- else
- {
- SoupURI *uri;
- const char *base_path;
- g_autofree char *path = NULL;
-
- base_path = soup_uri_get_path (base_uri);
- path = g_build_filename (base_path, filename, NULL);
- uri = soup_uri_new_with_base (base_uri, path);
-
- ret = _ostree_fetcher_request_uri_to_membuf (fetcher, uri,
- FALSE, TRUE,
- out_bytes,
- OSTREE_MAX_METADATA_SIZE,
- cancellable, error);
- soup_uri_free (uri);
-
- if (!ret)
- goto out;
- }
-
- ret = TRUE;
-out:
- return ret;
-}
-
-static gboolean
-repo_remote_fetch_summary (OstreeRepo *self,
- const char *name,
- const char *metalink_url_string,
- GBytes **out_summary,
- GBytes **out_signatures,
- GCancellable *cancellable,
- GError **error)
-{
- glnx_unref_object OstreeFetcher *fetcher = NULL;
- g_autoptr(GMainContext) mainctx = NULL;
- gboolean ret = FALSE;
- SoupURI *base_uri = NULL;
- gboolean from_cache = FALSE;
-
- mainctx = g_main_context_new ();
- g_main_context_push_thread_default (mainctx);
-
- fetcher = _ostree_repo_remote_new_fetcher (self, name, error);
- if (fetcher == NULL)
- goto out;
-
- base_uri = soup_uri_new (metalink_url_string);
-
- {
- g_autofree char *url_string = NULL;
- if (metalink_url_string)
- url_string = g_strdup (metalink_url_string);
- else
- {
- if (!ostree_repo_remote_get_url (self, name, &url_string, error))
- goto out;
- }
-
- base_uri = soup_uri_new (url_string);
- if (base_uri == NULL)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Invalid URL '%s'", url_string);
- goto out;
- }
- }
-
- if (!_ostree_preload_metadata_file (self,
- fetcher,
- base_uri,
- "summary.sig",
- metalink_url_string ? TRUE : FALSE,
- out_signatures,
- cancellable,
- error))
- goto out;
-
- if (*out_signatures)
- {
- if (!_ostree_repo_load_cache_summary_if_same_sig (self,
- name,
- *out_signatures,
- out_summary,
- cancellable,
- error))
- goto out;
- }
-
- if (*out_summary)
- from_cache = TRUE;
- else
- {
- if (!_ostree_preload_metadata_file (self,
- fetcher,
- base_uri,
- "summary",
- metalink_url_string ? TRUE : FALSE,
- out_summary,
- cancellable,
- error))
- goto out;
- }
-
- if (!from_cache && *out_summary && *out_signatures)
- {
- g_autoptr(GError) temp_error = NULL;
-
- if (!_ostree_repo_cache_summary (self,
- name,
- *out_summary,
- *out_signatures,
- cancellable,
- &temp_error))
- {
- if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
- g_debug ("No permissions to save summary cache");
- else
- {
- g_propagate_error (error, g_steal_pointer (&temp_error));
- goto out;
- }
- }
- }
-
- ret = TRUE;
-
- out:
- if (mainctx)
- g_main_context_pop_thread_default (mainctx);
- if (base_uri != NULL)
- soup_uri_free (base_uri);
- return ret;
-}
-#endif
-
/**
* ostree_repo_remote_fetch_summary:
* @self: Self
* @name: name of a remote
- * @out_summary: (allow-none): return location for raw summary data, or %NULL
- * @out_signatures: (allow-none): return location for raw summary signature
+ * @out_summary: (nullable): return location for raw summary data, or %NULL
+ * @out_signatures: (nullable): return location for raw summary signature
* data, or %NULL
* @cancellable: a #GCancellable
* @error: a #GError
@@ -2013,76 +1764,13 @@ ostree_repo_remote_fetch_summary (OstreeRepo *self,
GCancellable *cancellable,
GError **error)
{
-#ifdef HAVE_LIBSOUP
- g_autofree char *metalink_url_string = NULL;
- g_autoptr(GBytes) summary = NULL;
- g_autoptr(GBytes) signatures = NULL;
- gboolean ret = FALSE;
- gboolean gpg_verify_summary;
-
- g_return_val_if_fail (OSTREE_REPO (self), FALSE);
- g_return_val_if_fail (name != NULL, FALSE);
-
- if (!ostree_repo_get_remote_option (self, name, "metalink", NULL,
- &metalink_url_string, error))
- goto out;
-
- if (!repo_remote_fetch_summary (self,
- name,
- metalink_url_string,
- &summary,
- &signatures,
- cancellable,
- error))
- goto out;
-
- if (!ostree_repo_remote_get_gpg_verify_summary (self, name, &gpg_verify_summary, error))
- goto out;
-
- if (gpg_verify_summary && signatures == NULL)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "GPG verification enabled, but no summary signatures found (use gpg-verify-summary=false in remote config to disable)");
- goto out;
- }
-
- /* Verify any summary signatures. */
- if (gpg_verify_summary && summary != NULL && signatures != NULL)
- {
- glnx_unref_object OstreeGpgVerifyResult *result = NULL;
-
- result = ostree_repo_verify_summary (self,
- name,
- summary,
- signatures,
- cancellable,
- error);
- if (result == NULL)
- goto out;
-
- if (ostree_gpg_verify_result_count_valid (result) == 0)
- {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "GPG signatures found, but none are in trusted keyring");
- goto out;
- }
- }
-
- if (out_summary != NULL)
- *out_summary = g_steal_pointer (&summary);
-
- if (out_signatures != NULL)
- *out_signatures = g_steal_pointer (&signatures);
-
- ret = TRUE;
-
-out:
- return ret;
-#else
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- "This version of ostree was built without libsoup, and cannot fetch over HTTP");
- return FALSE;
-#endif
+ return ostree_repo_remote_fetch_summary_with_options (self,
+ name,
+ NULL,
+ out_summary,
+ out_signatures,
+ cancellable,
+ error);
}
static gboolean
@@ -2128,7 +1816,8 @@ ostree_repo_mode_from_string (const char *mode,
ret_mode = OSTREE_REPO_MODE_BARE;
else if (strcmp (mode, "bare-user") == 0)
ret_mode = OSTREE_REPO_MODE_BARE_USER;
- else if (strcmp (mode, "archive-z2") == 0)
+ else if (strcmp (mode, "archive-z2") == 0 ||
+ strcmp (mode, "archive") == 0)
ret_mode = OSTREE_REPO_MODE_ARCHIVE_Z2;
else
{
@@ -2171,8 +1860,14 @@ ostree_repo_create (OstreeRepo *self,
if (!ostree_repo_mode_to_string (mode, &mode_str, error))
goto out;
- if (!gs_file_ensure_directory (self->repodir, FALSE, cancellable, error))
- goto out;
+ if (mkdir (gs_file_get_path_cached (self->repodir), 0755) != 0)
+ {
+ if (errno != EEXIST)
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ }
config_data = g_string_new (DEFAULT_CONFIG_CONTENTS);
g_string_append_printf (config_data, "mode=%s\n", mode_str);
@@ -2190,6 +1885,13 @@ ostree_repo_create (OstreeRepo *self,
if (!g_file_make_directory (self->tmp_dir, cancellable, error))
goto out;
+ {
+ g_autoptr(GFile) extensions_dir =
+ g_file_resolve_relative_path (self->repodir, "extensions");
+ if (!g_file_make_directory (extensions_dir, cancellable, error))
+ goto out;
+ }
+
g_clear_object (&child);
child = g_file_get_child (self->repodir, "refs");
if (!g_file_make_directory (child, cancellable, error))
@@ -2402,22 +2104,33 @@ ostree_repo_open (OstreeRepo *self,
g_autofree char *version = NULL;
g_autofree char *mode = NULL;
g_autofree char *parent_repo_path = NULL;
- g_autoptr(GError) temp_error = NULL;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (self->inited)
return TRUE;
- /* We use a per-boot identifier to keep track of which file contents
- * possibly haven't been sync'd to disk.
+ /* We use a directory of the form `staging-${BOOT_ID}-${RANDOM}`
+ * where if the ${BOOT_ID} doesn't match, we know file contents
+ * possibly haven't been sync'd to disk and need to be discarded.
*/
- if (!g_file_get_contents ("/proc/sys/kernel/random/boot_id",
- &self->boot_id,
- NULL,
- error))
- goto out;
- g_strdelimit (self->boot_id, "\n", '\0');
+ { const char *env_bootid = getenv ("OSTREE_BOOTID");
+ g_autofree char *boot_id = NULL;
+
+ if (env_bootid != NULL)
+ boot_id = g_strdup (env_bootid);
+ else
+ {
+ if (!g_file_get_contents ("/proc/sys/kernel/random/boot_id",
+ &boot_id,
+ NULL,
+ error))
+ goto out;
+ g_strdelimit (boot_id, "\n", '\0');
+ }
+
+ self->stagedir_prefix = g_strconcat (OSTREE_REPO_TMPDIR_STAGING, boot_id, "-", NULL);
+ }
if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (self->repodir), TRUE,
&self->repo_dir_fd, error))
@@ -2530,43 +2243,35 @@ ostree_repo_open (OstreeRepo *self,
ostree_repo_set_disable_fsync (self, TRUE);
}
+ { g_autofree char *tmp_expiry_seconds = NULL;
+
+ /* 86400 secs = one day */
+ if (!ot_keyfile_get_value_with_default (self->config, "core", "tmp-expiry-secs", "86400",
+ &tmp_expiry_seconds, error))
+ goto out;
+
+ self->tmp_expiry_seconds = g_ascii_strtoull (tmp_expiry_seconds, NULL, 10);
+ }
+
if (!append_remotes_d (self, cancellable, error))
goto out;
if (!glnx_opendirat (self->repo_dir_fd, "tmp", TRUE, &self->tmp_dir_fd, error))
goto out;
- if (!glnx_shutil_mkdir_p_at (self->tmp_dir_fd, _OSTREE_CACHE_DIR, 0775, cancellable, &temp_error))
+ if (self->writable)
{
- if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED))
- {
- g_clear_error (&temp_error);
- g_debug ("No permissions to create cache dir");
- }
- else
- {
- g_propagate_error (error, g_steal_pointer (&temp_error));
- goto out;
- }
- }
+ if (!glnx_shutil_mkdir_p_at (self->tmp_dir_fd, _OSTREE_CACHE_DIR, 0775, cancellable, error))
+ goto out;
- if (!glnx_opendirat (self->tmp_dir_fd, _OSTREE_CACHE_DIR, TRUE, &self->cache_dir_fd, &temp_error))
- {
- if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
- {
- g_clear_error (&temp_error);
- g_debug ("No cache dir");
- }
- else
- {
- g_propagate_error (error, g_steal_pointer (&temp_error));
- goto out;
- }
+ if (!glnx_opendirat (self->tmp_dir_fd, _OSTREE_CACHE_DIR, TRUE, &self->cache_dir_fd, error))
+ goto out;
}
if (self->mode == OSTREE_REPO_MODE_ARCHIVE_Z2 && self->enable_uncompressed_cache)
{
- if (!gs_file_ensure_directory (self->uncompressed_objects_dir, TRUE, cancellable, error))
+ if (!glnx_shutil_mkdir_p_at (self->repo_dir_fd, "uncompressed-objects-cache", 0755,
+ cancellable, error))
goto out;
if (!glnx_opendirat (self->repo_dir_fd, "uncompressed-objects-cache", TRUE,
&self->uncompressed_objects_dir_fd,
@@ -2603,6 +2308,8 @@ ostree_repo_set_disable_fsync (OstreeRepo *self,
* @self: An #OstreeRepo
* @dfd: directory fd
* @path: subpath in @dfd
+ * @cancellable: a #GCancellable
+ * @error: a #GError
*
* Set a custom location for the cache directory used for e.g.
* per-remote summary caches. Setting this manually is useful when
@@ -2852,7 +2559,7 @@ load_metadata_internal (OstreeRepo *self,
{
gboolean ret = FALSE;
char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
- int fd = -1;
+ glnx_fd_close int fd = -1;
g_autoptr(GInputStream) ret_stream = NULL;
g_autoptr(GVariant) ret_variant = NULL;
@@ -2880,8 +2587,6 @@ load_metadata_internal (OstreeRepo *self,
mfile = g_mapped_file_new_from_fd (fd, FALSE, error);
if (!mfile)
goto out;
- (void) close (fd); /* Ignore errors, we have it mapped */
- fd = -1;
ret_variant = g_variant_new_from_data (ostree_metadata_variant_type (objtype),
g_mapped_file_get_contents (mfile),
g_mapped_file_get_length (mfile),
@@ -2903,7 +2608,7 @@ load_metadata_internal (OstreeRepo *self,
{
struct stat stbuf;
- if (!gs_stream_fstat ((GFileDescriptorBased*)ret_stream, &stbuf, cancellable, error))
+ if (!glnx_stream_fstat ((GFileDescriptorBased*)ret_stream, &stbuf, error))
goto out;
*out_size = stbuf.st_size;
}
@@ -2926,8 +2631,6 @@ load_metadata_internal (OstreeRepo *self,
ot_transfer_out_value (out_variant, &ret_variant);
ot_transfer_out_value (out_stream, &ret_stream);
out:
- if (fd != -1)
- (void) close (fd);
return ret;
}
@@ -3018,8 +2721,13 @@ _ostree_repo_read_bare_fd (OstreeRepo *self,
_ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_FILE, self->mode);
- return gs_file_openat_noatime (self->objects_dir_fd, loose_path_buf, out_fd,
- cancellable, error);
+ *out_fd = openat (self->objects_dir_fd, loose_path_buf, O_RDONLY | O_CLOEXEC);
+ if (*out_fd < 0)
+ {
+ glnx_set_error_from_errno (error);
+ return FALSE;
+ }
+ return TRUE;
}
/**
@@ -3066,13 +2774,20 @@ ostree_repo_load_file (OstreeRepo *self,
error))
goto out;
+ if (fd < 0 && self->commit_stagedir_fd != -1)
+ {
+ if (!ot_openat_ignore_enoent (self->commit_stagedir_fd, loose_path_buf, &fd,
+ error))
+ goto out;
+ }
+
if (fd != -1)
{
tmp_stream = g_unix_input_stream_new (fd, TRUE);
fd = -1; /* Transfer ownership */
- if (!gs_stream_fstat ((GFileDescriptorBased*) tmp_stream, &stbuf,
- cancellable, error))
+ if (!glnx_stream_fstat ((GFileDescriptorBased*) tmp_stream, &stbuf,
+ error))
goto out;
if (!ostree_content_stream_parse (TRUE, tmp_stream, stbuf.st_size, TRUE,
@@ -3285,15 +3000,13 @@ _ostree_repo_has_loose_object (OstreeRepo *self,
const char *checksum,
OstreeObjectType objtype,
gboolean *out_is_stored,
- char *loose_path_buf,
- GFile **out_stored_path,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
struct stat stbuf;
int res = -1;
- gboolean tmp_file = FALSE;
+ char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
_ostree_loose_path (loose_path_buf, checksum, objtype, self->mode);
@@ -3309,9 +3022,7 @@ _ostree_repo_has_loose_object (OstreeRepo *self,
}
}
- if (res == 0)
- tmp_file = TRUE;
- else
+ if (res < 0)
{
do
res = fstatat (self->objects_dir_fd, loose_path_buf, &stbuf, AT_SYMLINK_NOFOLLOW);
@@ -3325,32 +3036,10 @@ _ostree_repo_has_loose_object (OstreeRepo *self,
ret = TRUE;
*out_is_stored = (res != -1);
-
- if (out_stored_path)
- {
- if (res != -1)
- *out_stored_path = g_file_resolve_relative_path (tmp_file ? self->tmp_dir : self->objects_dir, loose_path_buf);
- else
- *out_stored_path = NULL;
- }
out:
return ret;
}
-gboolean
-_ostree_repo_find_object (OstreeRepo *self,
- OstreeObjectType objtype,
- const char *checksum,
- GFile **out_stored_path,
- GCancellable *cancellable,
- GError **error)
-{
- gboolean has_object;
- char loose_path[_OSTREE_LOOSE_PATH_MAX];
- return _ostree_repo_has_loose_object (self, checksum, objtype, &has_object, loose_path,
- out_stored_path, cancellable, error);
-}
-
/**
* ostree_repo_has_object:
* @self: Repo
@@ -3375,13 +3064,12 @@ ostree_repo_has_object (OstreeRepo *self,
{
gboolean ret = FALSE;
gboolean ret_have_object;
- g_autoptr(GFile) loose_path = NULL;
- if (!_ostree_repo_find_object (self, objtype, checksum, &loose_path,
- cancellable, error))
+ if (!_ostree_repo_has_loose_object (self, checksum, objtype, &ret_have_object,
+ cancellable, error))
goto out;
- ret_have_object = (loose_path != NULL);
+ /* In the future, here is where we would also look up in metadata pack files */
if (!ret_have_object && self->parent_repo)
{
@@ -3462,13 +3150,16 @@ ostree_repo_delete_object (OstreeRepo *self,
if (tombstone_commits)
{
- g_autoptr(GVariantBuilder) builder = NULL;
- builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
- g_variant_builder_add (builder, "{sv}", "commit", g_variant_new_bytestring (sha256));
+ g_auto(GVariantBuilder) builder = {{0,}};
+ g_autoptr(GVariant) variant = NULL;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&builder, "{sv}", "commit", g_variant_new_bytestring (sha256));
+ variant = g_variant_ref_sink (g_variant_builder_end (&builder));
if (!ostree_repo_write_metadata_trusted (self,
OSTREE_OBJECT_TYPE_TOMBSTONE_COMMIT,
sha256,
- g_variant_builder_end (builder),
+ variant,
cancellable,
error))
goto out;
@@ -4003,7 +3694,6 @@ ostree_repo_read_commit (OstreeRepo *self,
return ret;
}
-#ifndef HAVE_LIBSOUP
/**
* ostree_repo_pull:
* @self: Repo
@@ -4040,9 +3730,7 @@ ostree_repo_pull (OstreeRepo *self,
GCancellable *cancellable,
GError **error)
{
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- "This version of ostree was built without libsoup, and cannot fetch over HTTP");
- return FALSE;
+ return ostree_repo_pull_one_dir (self, remote_name, NULL, refs_to_fetch, flags, progress, cancellable, error);
}
/**
@@ -4069,49 +3757,55 @@ ostree_repo_pull_one_dir (OstreeRepo *self,
GCancellable *cancellable,
GError **error)
{
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- "This version of ostree was built without libsoup, and cannot fetch over HTTP");
- return FALSE;
+ GVariantBuilder builder;
+ g_autoptr(GVariant) options = NULL;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+
+ if (dir_to_pull)
+ g_variant_builder_add (&builder, "{s@v}", "subdir",
+ g_variant_new_variant (g_variant_new_string (dir_to_pull)));
+ g_variant_builder_add (&builder, "{s@v}", "flags",
+ g_variant_new_variant (g_variant_new_int32 (flags)));
+ if (refs_to_fetch)
+ g_variant_builder_add (&builder, "{s@v}", "refs",
+ g_variant_new_variant (g_variant_new_strv ((const char *const*) refs_to_fetch, -1)));
+
+ options = g_variant_ref_sink (g_variant_builder_end (&builder));
+ return ostree_repo_pull_with_options (self, remote_name, options,
+ progress, cancellable, error);
}
/**
- * ostree_repo_pull_with_options:
- * @self: Repo
- * @remote_name: Name of remote
- * @options: A GVariant a{sv} with an extensible set of flags.
- * @progress: (allow-none): Progress
- * @cancellable: Cancellable
- * @error: Error
+ * _formatted_time_remaining_from_seconds
+ * @seconds_remaining: Estimated number of seconds remaining.
*
- * Like ostree_repo_pull(), but supports an extensible set of flags.
- * The following are currently defined:
- *
- * * refs (as): Array of string refs
- * * flags (i): An instance of #OstreeRepoPullFlags
- * * subdir (s): Pull just this subdirectory
- * * override-remote-name (s): If local, add this remote to refspec
- * * gpg-verify (b): GPG verify commits
- * * gpg-verify-summary (b): GPG verify summary
- * * depth (i): How far in the history to traverse; default is 0, -1 means infinite
- * * disable-static-deltas (b): Do not use static deltas
- * * require-static-deltas (b): Require static deltas
- * * override-commit-ids (as): Array of specific commit IDs to fetch for refs
- * * dry-run (b): Only print information on what will be downloaded (requires static deltas)
- */
-gboolean
-ostree_repo_pull_with_options (OstreeRepo *self,
- const char *remote_name,
- GVariant *options,
- OstreeAsyncProgress *progress,
- GCancellable *cancellable,
- GError **error)
+ * Returns a strings showing the number of days, hours, minutes
+ * and seconds remaining.
+ **/
+static char *
+_formatted_time_remaining_from_seconds (guint64 seconds_remaining)
{
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- "This version of ostree was built without libsoup, and cannot fetch over HTTP");
- return FALSE;
-}
+ guint64 minutes_remaining = seconds_remaining / 60;
+ guint64 hours_remaining = minutes_remaining / 60;
+ guint64 days_remaining = hours_remaining / 24;
-#endif
+ GString *description = g_string_new (NULL);
+
+ if (days_remaining)
+ g_string_append_printf (description, "%" G_GUINT64_FORMAT " days ", days_remaining);
+
+ if (hours_remaining)
+ g_string_append_printf (description, "%" G_GUINT64_FORMAT " hours ", hours_remaining % 24);
+
+ if (minutes_remaining)
+ g_string_append_printf (description, "%" G_GUINT64_FORMAT " minutes ", minutes_remaining % 60);
+
+ if (seconds_remaining)
+ g_string_append_printf (description, "%" G_GUINT64_FORMAT " seconds ", seconds_remaining % 60);
+
+ return g_string_free (description, FALSE);
+}
/**
* ostree_repo_pull_default_console_progress_changed:
@@ -4126,12 +3820,15 @@ ostree_repo_pull_with_options (OstreeRepo *self,
* custom status message, or else outstanding fetch progress in bytes/sec,
* or else outstanding content or metadata writes to the repository in
* number of objects.
+ *
+ * Compatibility note: this function previously assumed that @user_data
+ * was a pointer to a #GSConsole instance. This is no longer the case,
+ * and @user_data is ignored.
**/
void
ostree_repo_pull_default_console_progress_changed (OstreeAsyncProgress *progress,
gpointer user_data)
{
- GSConsole *console = user_data;
GString *buf;
g_autofree char *status = NULL;
guint outstanding_fetches;
@@ -4141,7 +3838,8 @@ ostree_repo_pull_default_console_progress_changed (OstreeAsyncProgress *progress
guint fetched_delta_parts;
guint total_delta_parts;
- if (!console)
+ /* Historical note; we used to treat this as a GSConsole instance */
+ if (user_data == NULL)
return;
buf = g_string_new ("");
@@ -4164,28 +3862,39 @@ ostree_repo_pull_default_console_progress_changed (OstreeAsyncProgress *progress
guint fetched = ostree_async_progress_get_uint (progress, "fetched");
guint metadata_fetched = ostree_async_progress_get_uint (progress, "metadata-fetched");
guint requested = ostree_async_progress_get_uint (progress, "requested");
- guint64 bytes_sec = (g_get_monotonic_time () - ostree_async_progress_get_uint64 (progress, "start-time")) / G_USEC_PER_SEC;
+ guint64 start_time = ostree_async_progress_get_uint64 (progress, "start-time");
+ guint64 total_delta_part_size = ostree_async_progress_get_uint64 (progress, "total-delta-part-size");
+ guint64 current_time = g_get_monotonic_time ();
g_autofree char *formatted_bytes_transferred =
g_format_size_full (bytes_transferred, 0);
g_autofree char *formatted_bytes_sec = NULL;
+ g_autofree char *formatted_est_time_remaining = NULL;
- if (!bytes_sec) // Ignore first second
- formatted_bytes_sec = g_strdup ("-");
+ /* Ignore the first second, or when we haven't transferred any
+ * data, since those could cause divide by zero below.
+ */
+ if ((current_time - start_time) < G_USEC_PER_SEC || bytes_transferred == 0)
+ {
+ formatted_bytes_sec = g_strdup ("-");
+ formatted_est_time_remaining = g_strdup ("- ");
+ }
else
{
- bytes_sec = bytes_transferred / bytes_sec;
+ guint64 bytes_sec = bytes_transferred / ((current_time - start_time) / G_USEC_PER_SEC);
+ guint64 est_time_remaining = (total_delta_part_size - bytes_transferred) / bytes_sec;
formatted_bytes_sec = g_format_size (bytes_sec);
+ formatted_est_time_remaining = _formatted_time_remaining_from_seconds (est_time_remaining);
}
if (total_delta_parts > 0)
{
- guint64 total_delta_part_size = ostree_async_progress_get_uint64 (progress, "total-delta-part-size");
g_autofree char *formatted_total =
g_format_size (total_delta_part_size);
- g_string_append_printf (buf, "Receiving delta parts: %u/%u %s/s %s/%s",
+ /* No space between %s and remaining, since formatted_est_time_remaining has a trailing space */
+ g_string_append_printf (buf, "Receiving delta parts: %u/%u %s/s %s/%s %sremaining",
fetched_delta_parts, total_delta_parts,
formatted_bytes_sec, formatted_bytes_transferred,
- formatted_total);
+ formatted_total, formatted_est_time_remaining);
}
else if (outstanding_metadata_fetches)
{
@@ -4208,7 +3917,7 @@ ostree_repo_pull_default_console_progress_changed (OstreeAsyncProgress *progress
g_string_append_printf (buf, "Scanning metadata: %u", n_scanned_metadata);
}
- gs_console_begin_status_line (console, buf->str, NULL, NULL);
+ glnx_console_text (buf->str);
g_string_free (buf, TRUE);
}
@@ -4233,7 +3942,6 @@ ostree_repo_append_gpg_signature (OstreeRepo *self,
gboolean ret = FALSE;
g_autoptr(GVariant) metadata = NULL;
g_autoptr(GVariant) new_metadata = NULL;
- g_autoptr(GVariantBuilder) builder = NULL;
if (!ostree_repo_read_commit_detached_metadata (self,
commit_checksum,
@@ -4359,7 +4067,7 @@ sign_data (OstreeRepo *self,
if (!g_output_stream_close (tmp_signature_output, cancellable, error))
goto out;
- signature_file = gs_file_map_noatime (tmp_signature_file, cancellable, error);
+ signature_file = g_mapped_file_new (gs_file_get_path_cached (tmp_signature_file), FALSE, error);
if (!signature_file)
goto out;
ret_signature = g_mapped_file_get_bytes (signature_file);
@@ -4475,9 +4183,7 @@ out:
/**
* ostree_repo_sign_delta:
- * @self: Self
- * @from_commit: SHA256 of starting commit to sign, or %NULL
- * @to_commit: SHA256 of target commit to sign
+ *
* This function is deprecated, sign the summary file instead.
* Add a GPG signature to a static delta.
*/
@@ -4513,33 +4219,19 @@ ostree_repo_add_gpg_signature_summary (OstreeRepo *self,
{
gboolean ret = FALSE;
g_autoptr(GBytes) summary_data = NULL;
- g_autoptr(GFile) summary_file = NULL;
- g_autoptr(GFile) signature_path = NULL;
- GError *temp_error = NULL;
g_autoptr(GVariant) existing_signatures = NULL;
g_autoptr(GVariant) new_metadata = NULL;
g_autoptr(GVariant) normalized = NULL;
guint i;
- signature_path = g_file_resolve_relative_path (self->repodir, "summary.sig");
- summary_file = g_file_resolve_relative_path (self->repodir, "summary");
- summary_data = gs_file_map_readonly (summary_file, cancellable, error);
+ summary_data = ot_file_mapat_bytes (self->repo_dir_fd, "summary", error);
if (!summary_data)
goto out;
- if (!ot_util_variant_map (signature_path, G_VARIANT_TYPE (OSTREE_SUMMARY_SIG_GVARIANT_STRING),
- TRUE, &existing_signatures, &temp_error))
- {
- if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
- {
- g_clear_error (&temp_error);
- }
- else
- {
- g_propagate_error (error, temp_error);
- goto out;
- }
- }
+ if (!ot_util_variant_map_at (self->repo_dir_fd, "summary.sig",
+ G_VARIANT_TYPE (OSTREE_SUMMARY_SIG_GVARIANT_STRING),
+ OT_VARIANT_MAP_ALLOW_NOENT, &existing_signatures, error))
+ goto out;
for (i = 0; key_id[i]; i++)
{
@@ -4599,23 +4291,17 @@ find_keyring (OstreeRepo *self,
return NULL;
}
-OstreeGpgVerifyResult *
-_ostree_repo_gpg_verify_with_metadata (OstreeRepo *self,
- GBytes *signed_data,
- GVariant *metadata,
- const char *remote_name,
- GFile *keyringdir,
- GFile *extra_keyring,
- GCancellable *cancellable,
- GError **error)
+static OstreeGpgVerifyResult *
+_ostree_repo_gpg_verify_data_internal (OstreeRepo *self,
+ const gchar *remote_name,
+ GBytes *data,
+ GBytes *signatures,
+ GFile *keyringdir,
+ GFile *extra_keyring,
+ GCancellable *cancellable,
+ GError **error)
{
- OstreeGpgVerifyResult *result = NULL;
glnx_unref_object OstreeGpgVerifier *verifier = NULL;
- g_autoptr(GVariant) signaturedata = NULL;
- GByteArray *buffer;
- GVariantIter iter;
- GVariant *child;
- g_autoptr (GBytes) signatures = NULL;
gboolean add_global_keyring_dir = TRUE;
verifier = _ostree_gpg_verifier_new ();
@@ -4626,7 +4312,7 @@ _ostree_repo_gpg_verify_with_metadata (OstreeRepo *self,
if (!_ostree_gpg_verifier_add_keyring_dir (verifier, self->repodir,
cancellable, error))
- goto out;
+ return NULL;
}
else if (remote_name != NULL)
{
@@ -4637,7 +4323,7 @@ _ostree_repo_gpg_verify_with_metadata (OstreeRepo *self,
remote = ost_repo_get_remote_inherited (self, remote_name, error);
if (remote == NULL)
- goto out;
+ return NULL;
file = find_keyring (self, remote, cancellable);
@@ -4654,20 +4340,43 @@ _ostree_repo_gpg_verify_with_metadata (OstreeRepo *self,
{
/* Use the deprecated global keyring directory. */
if (!_ostree_gpg_verifier_add_global_keyring_dir (verifier, cancellable, error))
- goto out;
+ return NULL;
}
if (keyringdir)
{
if (!_ostree_gpg_verifier_add_keyring_dir (verifier, keyringdir,
cancellable, error))
- goto out;
+ return NULL;
}
if (extra_keyring != NULL)
{
_ostree_gpg_verifier_add_keyring (verifier, extra_keyring);
}
+ return _ostree_gpg_verifier_check_signature (verifier,
+ data,
+ signatures,
+ cancellable,
+ error);
+}
+
+OstreeGpgVerifyResult *
+_ostree_repo_gpg_verify_with_metadata (OstreeRepo *self,
+ GBytes *signed_data,
+ GVariant *metadata,
+ const char *remote_name,
+ GFile *keyringdir,
+ GFile *extra_keyring,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GVariant) signaturedata = NULL;
+ GByteArray *buffer;
+ GVariantIter iter;
+ GVariant *child;
+ g_autoptr (GBytes) signatures = NULL;
+
if (metadata)
signaturedata = g_variant_lookup_value (metadata,
_OSTREE_METADATA_GPGSIGS_NAME,
@@ -4676,7 +4385,7 @@ _ostree_repo_gpg_verify_with_metadata (OstreeRepo *self,
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"GPG verification enabled, but no signatures found (use gpg-verify=false in remote config to disable)");
- goto out;
+ return NULL;
}
/* OpenPGP data is organized into binary records called packets. RFC 4880
@@ -4698,12 +4407,14 @@ _ostree_repo_gpg_verify_with_metadata (OstreeRepo *self,
}
signatures = g_byte_array_free_to_bytes (buffer);
- result = _ostree_gpg_verifier_check_signature (verifier,
- signed_data, signatures,
- cancellable, error);
-
- out:
- return result;
+ return _ostree_repo_gpg_verify_data_internal (self,
+ remote_name,
+ signed_data,
+ signatures,
+ keyringdir,
+ extra_keyring,
+ cancellable,
+ error);
}
/* Needed an internal version for the remote_name parameter. */
@@ -4773,6 +4484,8 @@ out:
*
* Check for a valid GPG signature on commit named by the ASCII
* checksum @commit_checksum.
+ *
+ * Returns: %TRUE if there was a GPG signature from a trusted keyring, otherwise %FALSE
*/
gboolean
ostree_repo_verify_commit (OstreeRepo *self,
@@ -4783,25 +4496,12 @@ ostree_repo_verify_commit (OstreeRepo *self,
GError **error)
{
glnx_unref_object OstreeGpgVerifyResult *result = NULL;
- gboolean ret = FALSE;
result = ostree_repo_verify_commit_ext (self, commit_checksum,
keyringdir, extra_keyring,
cancellable, error);
- if (result == NULL)
- goto out;
- if (ostree_gpg_verify_result_count_valid (result) == 0)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "GPG signatures found, but none are in trusted keyring");
- goto out;
- }
-
- ret = TRUE;
-
- out:
- return ret;
+ return ostree_gpg_verify_result_require_valid_signature (result, error);
}
/**
@@ -4835,6 +4535,49 @@ ostree_repo_verify_commit_ext (OstreeRepo *self,
error);
}
+/**
+ * ostree_repo_gpg_verify_data:
+ * @self: Repository
+ * @remote_name: (nullable): Name of remote
+ * @data: Data as a #GBytes
+ * @signatures: Signatures as a #GBytes
+ * @keyringdir: (nullable): Path to directory GPG keyrings; overrides built-in default if given
+ * @extra_keyring: (nullable): Path to additional keyring file (not a directory)
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Verify @signatures for @data using GPG keys in the keyring for
+ * @remote_name, and return an #OstreeGpgVerifyResult.
+ *
+ * The @remote_name parameter can be %NULL. In that case it will do
+ * the verifications using GPG keys in the keyrings of all remotes.
+ *
+ * Returns: (transfer full): an #OstreeGpgVerifyResult, or %NULL on error
+ */
+OstreeGpgVerifyResult *
+ostree_repo_gpg_verify_data (OstreeRepo *self,
+ const gchar *remote_name,
+ GBytes *data,
+ GBytes *signatures,
+ GFile *keyringdir,
+ GFile *extra_keyring,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (OSTREE_IS_REPO (self), NULL);
+ g_return_val_if_fail (data != NULL, NULL);
+ g_return_val_if_fail (signatures != NULL, NULL);
+
+ return _ostree_repo_gpg_verify_data_internal (self,
+ (remote_name != NULL) ? remote_name : OSTREE_ALL_REMOTES,
+ data,
+ signatures,
+ keyringdir,
+ extra_keyring,
+ cancellable,
+ error);
+}
+
/**
* ostree_repo_verify_summary:
* @self: Repo
@@ -4901,7 +4644,7 @@ ostree_repo_regenerate_summary (OstreeRepo *self,
g_autoptr(GVariant) summary = NULL;
GList *ordered_keys = NULL;
GList *iter = NULL;
- GVariantDict additional_metadata_builder;
+ g_auto(GVariantDict) additional_metadata_builder = {{0,}};
if (!ostree_repo_list_refs (self, NULL, &refs, cancellable, error))
goto out;
@@ -4934,7 +4677,7 @@ ostree_repo_regenerate_summary (OstreeRepo *self,
{
guint i;
g_autoptr(GPtrArray) delta_names = NULL;
- GVariantDict deltas_builder;
+ g_auto(GVariantDict) deltas_builder = {{0,}};
g_autoptr(GVariant) deltas = NULL;
if (!ostree_repo_list_static_delta_names (self, &delta_names, cancellable, error))
@@ -5010,6 +4753,50 @@ ostree_repo_regenerate_summary (OstreeRepo *self,
return ret;
}
+gboolean
+_ostree_repo_is_locked_tmpdir (const char *filename)
+{
+ return g_str_has_prefix (filename, OSTREE_REPO_TMPDIR_STAGING) ||
+ g_str_has_prefix (filename, OSTREE_REPO_TMPDIR_FETCHER);
+}
+
+gboolean
+_ostree_repo_try_lock_tmpdir (int tmpdir_dfd,
+ const char *tmpdir_name,
+ GLnxLockFile *file_lock_out,
+ gboolean *out_did_lock,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ g_autofree char *lock_name = g_strconcat (tmpdir_name, "-lock", NULL);
+ gboolean did_lock = FALSE;
+ g_autoptr(GError) local_error = NULL;
+
+ /* We put the lock outside the dir, so we can hold the lock
+ * until the directory is fully removed */
+ if (!glnx_make_lock_file (tmpdir_dfd, lock_name, LOCK_EX | LOCK_NB,
+ file_lock_out, &local_error))
+ {
+ if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+ {
+ did_lock = FALSE;
+ }
+ else
+ {
+ g_propagate_error (error, g_steal_pointer (&local_error));
+ goto out;
+ }
+ }
+ else
+ {
+ did_lock = TRUE;
+ }
+
+ ret = TRUE;
+ *out_did_lock = did_lock;
+ out:
+ return ret;
+}
/* This allocates and locks a subdir of the repo tmp dir, using an existing
* one with the same prefix if it is not in use already. */
@@ -5023,25 +4810,28 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
GCancellable *cancellable,
GError **error)
{
+ gboolean ret = FALSE;
gboolean reusing_dir = FALSE;
+ gboolean did_lock;
g_autofree char *tmpdir_name = NULL;
glnx_fd_close int tmpdir_fd = -1;
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
+ g_return_val_if_fail (_ostree_repo_is_locked_tmpdir (tmpdir_prefix), FALSE);
+
/* Look for existing tmpdir (with same prefix) to reuse */
if (!glnx_dirfd_iterator_init_at (tmpdir_dfd, ".", FALSE, &dfd_iter, error))
- return FALSE;
+ goto out;
- while (TRUE)
+ while (tmpdir_name == NULL)
{
gs_dirfd_iterator_cleanup GSDirFdIterator child_dfd_iter = { 0, };
struct dirent *dent;
glnx_fd_close int existing_tmpdir_fd = -1;
g_autoptr(GError) local_error = NULL;
- g_autofree char *lock_name = NULL;
if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
- return FALSE;
+ goto out;
if (dent == NULL)
break;
@@ -5062,25 +4852,18 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
else
{
g_propagate_error (error, g_steal_pointer (&local_error));
- return FALSE;
+ goto out;
}
}
- lock_name = g_strconcat (dent->d_name, "-lock", NULL);
-
/* We put the lock outside the dir, so we can hold the lock
* until the directory is fully removed */
- if (!glnx_make_lock_file (dfd_iter.fd, lock_name, LOCK_EX | LOCK_NB,
- file_lock_out, &local_error))
- {
- if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
- continue;
- else
- {
- g_propagate_error (error, g_steal_pointer (&local_error));
- return FALSE;
- }
- }
+ if (!_ostree_repo_try_lock_tmpdir (dfd_iter.fd, dent->d_name,
+ file_lock_out, &did_lock,
+ error))
+ goto out;
+ if (!did_lock)
+ continue;
/* Touch the reused directory so that we don't accidentally
* remove it due to being old when cleaning up the tmpdir
@@ -5098,32 +4881,24 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
g_autofree char *tmpdir_name_template = g_strconcat (tmpdir_prefix, "XXXXXX", NULL);
glnx_fd_close int new_tmpdir_fd = -1;
g_autoptr(GError) local_error = NULL;
- g_autofree char *lock_name = NULL;
/* No existing tmpdir found, create a new */
if (!glnx_mkdtempat (tmpdir_dfd, tmpdir_name_template, 0777, error))
- return FALSE;
+ goto out;
if (!glnx_opendirat (tmpdir_dfd, tmpdir_name_template, FALSE,
&new_tmpdir_fd, error))
- return FALSE;
-
- lock_name = g_strconcat (tmpdir_name_template, "-lock", NULL);
+ goto out;
/* Note, at this point we can race with another process that picks up this
* new directory. If that happens we need to retry, making a new directory. */
- if (!glnx_make_lock_file (tmpdir_dfd, lock_name, LOCK_EX | LOCK_NB,
- file_lock_out, &local_error))
- {
- if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
- continue;
- else
- {
- g_propagate_error (error, g_steal_pointer (&local_error));
- return FALSE;
- }
- }
+ if (!_ostree_repo_try_lock_tmpdir (tmpdir_dfd, tmpdir_name_template,
+ file_lock_out, &did_lock,
+ error))
+ goto out;
+ if (!did_lock)
+ continue;
tmpdir_name = g_steal_pointer (&tmpdir_name_template);
tmpdir_fd = glnx_steal_fd (&new_tmpdir_fd);
@@ -5138,5 +4913,7 @@ _ostree_repo_allocate_tmpdir (int tmpdir_dfd,
if (reusing_dir_out)
*reusing_dir_out = reusing_dir;
- return TRUE;
+ ret = TRUE;
+ out:
+ return ret;
}
diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h
index 8a04e8e5..ce280ee6 100644
--- a/src/libostree/ostree-repo.h
+++ b/src/libostree/ostree-repo.h
@@ -115,6 +115,10 @@ gboolean ostree_repo_remote_delete (OstreeRepo *self,
GCancellable *cancellable,
GError **error);
+/**
+ * OstreeRepoRemoteChange:
+ * The remote change operation.
+ */
typedef enum {
OSTREE_REPO_REMOTE_CHANGE_ADD,
OSTREE_REPO_REMOTE_CHANGE_ADD_IF_NOT_EXISTS,
@@ -194,6 +198,15 @@ gboolean ostree_repo_remote_fetch_summary (OstreeRepo *self,
GCancellable *cancellable,
GError **error);
+_OSTREE_PUBLIC
+gboolean ostree_repo_remote_fetch_summary_with_options (OstreeRepo *self,
+ const char *name,
+ GVariant *options,
+ GBytes **out_summary,
+ GBytes **out_signatures,
+ GCancellable *cancellable,
+ GError **error);
+
_OSTREE_PUBLIC
OstreeRepo * ostree_repo_get_parent (OstreeRepo *self);
@@ -214,6 +227,10 @@ gboolean ostree_repo_write_config (OstreeRepo *self,
* were written to the repository in this transaction.
* @content_bytes_written: The amount of data added to the repository,
* in bytes, counting only content objects.
+ * @padding1: reserved
+ * @padding2: reserved
+ * @padding3: reserved
+ * @padding4: reserved
*
* A list of statistics for each transaction that may be
* interesting for reporting purposes.
@@ -582,7 +599,9 @@ gboolean ostree_repo_write_archive_to_mtree (OstreeRepo *
typedef struct {
guint ignore_unsupported_content : 1;
guint autocreate_parents : 1;
- guint reserved : 30;
+ guint use_ostree_convention : 1;
+ guint callback_with_entry_pathname : 1;
+ guint reserved : 28;
guint unused_uint[8];
gpointer unused_ptrs[8];
@@ -610,7 +629,10 @@ typedef struct {
guint64 timestamp_secs;
guint unused_uint[8];
- gpointer unused_ptrs[8];
+
+ char *path_prefix;
+
+ gpointer unused_ptrs[7];
} OstreeRepoExportArchiveOptions;
_OSTREE_PUBLIC
@@ -685,7 +707,8 @@ typedef enum {
OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES = 1
} OstreeRepoCheckoutOverwriteMode;
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
ostree_repo_checkout_tree (OstreeRepo *self,
OstreeRepoCheckoutMode mode,
OstreeRepoCheckoutOverwriteMode overwrite_mode,
@@ -852,14 +875,16 @@ typedef enum {
OSTREE_REPO_COMMIT_TRAVERSE_FLAG_NONE = (1 << 0)
} OstreeRepoCommitTraverseFlags;
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
ostree_repo_commit_traverse_iter_init_commit (OstreeRepoCommitTraverseIter *iter,
OstreeRepo *repo,
GVariant *commit,
OstreeRepoCommitTraverseFlags flags,
GError **error);
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
ostree_repo_commit_traverse_iter_init_dirtree (OstreeRepoCommitTraverseIter *iter,
OstreeRepo *repo,
GVariant *dirtree,
@@ -889,8 +914,8 @@ void ostree_repo_commit_traverse_iter_get_dir (OstreeRepoCommitTraverseIter *ite
char **out_content_checksum,
char **out_meta_checksum);
-_OSTREE_PUBLIC void
-ostree_repo_commit_traverse_iter_clear (OstreeRepoCommitTraverseIter *iter);
+_OSTREE_PUBLIC
+void ostree_repo_commit_traverse_iter_clear (OstreeRepoCommitTraverseIter *iter);
_OSTREE_PUBLIC
void ostree_repo_commit_traverse_iter_cleanup (void *p);
@@ -909,7 +934,8 @@ typedef enum {
OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY
} OstreeRepoPruneFlags;
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
ostree_repo_prune_static_deltas (OstreeRepo *self, const char *commit,
GCancellable *cancellable,
GError **error);
@@ -947,7 +973,8 @@ gboolean ostree_repo_pull (OstreeRepo *self,
GCancellable *cancellable,
GError **error);
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
ostree_repo_pull_one_dir (OstreeRepo *self,
const char *remote_name,
const char *dir_to_pull,
@@ -986,7 +1013,8 @@ gboolean ostree_repo_sign_delta (OstreeRepo *self,
GCancellable *cancellable,
GError **error);
-_OSTREE_PUBLIC gboolean
+_OSTREE_PUBLIC
+gboolean
ostree_repo_add_gpg_signature_summary (OstreeRepo *self,
const gchar **key_id,
const gchar *homedir,
@@ -1016,6 +1044,16 @@ OstreeGpgVerifyResult * ostree_repo_verify_commit_ext (OstreeRepo *self,
GCancellable *cancellable,
GError **error);
+_OSTREE_PUBLIC
+OstreeGpgVerifyResult * ostree_repo_gpg_verify_data (OstreeRepo *self,
+ const gchar *remote_name,
+ GBytes *data,
+ GBytes *signatures,
+ GFile *keyringdir,
+ GFile *extra_keyring,
+ GCancellable *cancellable,
+ GError **error);
+
_OSTREE_PUBLIC
OstreeGpgVerifyResult * ostree_repo_verify_summary (OstreeRepo *self,
const char *remote_name,
diff --git a/src/libostree/ostree-sepolicy.c b/src/libostree/ostree-sepolicy.c
index 3b1a391b..8e49428f 100644
--- a/src/libostree/ostree-sepolicy.c
+++ b/src/libostree/ostree-sepolicy.c
@@ -32,7 +32,7 @@
#include "ostree-bootloader-syslinux.h"
/**
- * SECTION:libostree-sepolicy
+ * SECTION:ostree-sepolicy
* @title: SELinux policy management
* @short_description: Read SELinux policy and manage filesystem labels
*
diff --git a/src/libostree/ostree-sysroot-cleanup.c b/src/libostree/ostree-sysroot-cleanup.c
index 5c370507..64c1389e 100644
--- a/src/libostree/ostree-sysroot-cleanup.c
+++ b/src/libostree/ostree-sysroot-cleanup.c
@@ -32,7 +32,7 @@ _ostree_sysroot_list_deployment_dirs_for_os (GFile *osdir,
GError **error)
{
gboolean ret = FALSE;
- const char *osname = gs_file_get_basename_cached (osdir);
+ const char *osname = glnx_basename (gs_file_get_path_cached (osdir));
g_autoptr(GFileEnumerator) dir_enum = NULL;
g_autoptr(GFile) osdeploy_dir = NULL;
GError *temp_error = NULL;
@@ -370,7 +370,7 @@ cleanup_old_deployments (OstreeSysroot *self,
g_autofree char *osname = NULL;
g_autofree char *bootcsum = NULL;
- if (!parse_bootdir_name (gs_file_get_basename_cached (bootdir),
+ if (!parse_bootdir_name (glnx_basename (gs_file_get_path_cached (bootdir)),
&osname, &bootcsum))
g_assert_not_reached ();
diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c
index 77ecb9af..4616bab1 100644
--- a/src/libostree/ostree-sysroot-deploy.c
+++ b/src/libostree/ostree-sysroot-deploy.c
@@ -87,6 +87,39 @@ symlink_at_replace (const char *oldpath,
return ret;
}
+/* Try a hardlink if we can, otherwise fall back to copying. Used
+ * right now for kernels/initramfs in /boot, where we can just
+ * hardlink if we're on the same partition.
+ */
+static gboolean
+hardlink_or_copy_at (int src_dfd,
+ const char *src_subpath,
+ int dest_dfd,
+ const char *dest_subpath,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+
+ if (linkat (src_dfd, src_subpath, dest_dfd, dest_subpath, 0) != 0)
+ {
+ if (errno == EMLINK || errno == EXDEV)
+ {
+ return glnx_file_copy_at (src_dfd, src_subpath, NULL, dest_dfd, dest_subpath, 0,
+ cancellable, error);
+ }
+ else
+ {
+ glnx_set_error_from_errno (error);
+ goto out;
+ }
+ }
+
+ ret = TRUE;
+ out:
+ return ret;
+}
+
static gboolean
dirfd_copy_attributes_and_xattrs (int src_parent_dfd,
const char *src_name,
@@ -139,8 +172,8 @@ copy_dir_recurse (int src_parent_dfd,
GError **error)
{
gboolean ret = FALSE;
- int src_dfd = -1;
- int dest_dfd = -1;
+ glnx_fd_close int src_dfd = -1;
+ glnx_fd_close int dest_dfd = -1;
DIR *srcd = NULL;
struct dirent *dent;
@@ -207,10 +240,6 @@ copy_dir_recurse (int src_parent_dfd,
/* Note the srcd owns src_dfd */
src_dfd = -1;
}
- if (src_dfd != -1)
- (void) close (src_dfd);
- if (dest_dfd != -1)
- (void) close (dest_dfd);
return ret;
}
@@ -224,8 +253,8 @@ ensure_directory_from_template (int orig_etc_fd,
GError **error)
{
gboolean ret = FALSE;
- int src_dfd = -1;
- int target_dfd = -1;
+ glnx_fd_close int src_dfd = -1;
+ glnx_fd_close int target_dfd = -1;
g_assert (path != NULL);
g_assert (*path != '/' && *path != '\0');
@@ -283,10 +312,6 @@ ensure_directory_from_template (int orig_etc_fd,
target_dfd = -1;
}
out:
- if (src_dfd != -1)
- (void) close (src_dfd);
- if (target_dfd != -1)
- (void) close (target_dfd);
return ret;
}
@@ -308,7 +333,7 @@ copy_modified_config_file (int orig_etc_fd,
gboolean ret = FALSE;
struct stat modified_stbuf;
struct stat new_stbuf;
- int dest_parent_dfd = -1;
+ glnx_fd_close int dest_parent_dfd = -1;
if (fstatat (modified_etc_fd, path, &modified_stbuf, AT_SYMLINK_NOFOLLOW) < 0)
{
@@ -398,8 +423,6 @@ copy_modified_config_file (int orig_etc_fd,
ret = TRUE;
out:
- if (dest_parent_dfd != -1)
- (void) close (dest_parent_dfd);
return ret;
}
@@ -426,9 +449,9 @@ merge_etc_changes (GFile *orig_etc,
g_autoptr(GPtrArray) removed = NULL;
g_autoptr(GPtrArray) added = NULL;
guint i;
- int orig_etc_fd = -1;
- int modified_etc_fd = -1;
- int new_etc_fd = -1;
+ glnx_fd_close int orig_etc_fd = -1;
+ glnx_fd_close int modified_etc_fd = -1;
+ glnx_fd_close int new_etc_fd = -1;
modified = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_diff_item_unref);
removed = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
@@ -505,12 +528,6 @@ merge_etc_changes (GFile *orig_etc,
ret = TRUE;
out:
- if (orig_etc_fd != -1)
- (void) close (orig_etc_fd);
- if (modified_etc_fd != -1)
- (void) close (modified_etc_fd);
- if (new_etc_fd != -1)
- (void) close (new_etc_fd);
return ret;
}
@@ -651,7 +668,7 @@ relabel_recursively (OstreeSysroot *sysroot,
if (file_info == NULL)
break;
- g_ptr_array_add (path_parts, (char*)gs_file_get_basename_cached (child));
+ g_ptr_array_add (path_parts, (char*)g_file_info_get_name (file_info));
ftype = g_file_info_get_file_type (file_info);
if (ftype == G_FILE_TYPE_DIRECTORY)
@@ -723,12 +740,12 @@ selinux_relabel_file (OstreeSysroot *sysroot,
goto out;
g_ptr_array_add (path_parts, (char*)prefix);
- g_ptr_array_add (path_parts, (char*)gs_file_get_basename_cached (path));
+ g_ptr_array_add (path_parts, (char*)g_file_info_get_name (file_info));
if (!relabel_one_path (sysroot, sepolicy, path, file_info, path_parts,
cancellable, error))
{
g_prefix_error (error, "Relabeling /%s/%s: ", prefix,
- gs_file_get_basename_cached (path));
+ g_file_info_get_name (file_info));
goto out;
}
@@ -1277,7 +1294,6 @@ install_deployment_kernel (OstreeSysroot *sysroot,
struct stat stbuf;
const char *osname = ostree_deployment_get_osname (deployment);
const char *bootcsum = ostree_deployment_get_bootcsum (deployment);
- g_autoptr(GFile) bootdir = NULL;
g_autofree char *bootcsumdir = NULL;
g_autofree char *bootconfdir = NULL;
g_autofree char *bootconf_name = NULL;
@@ -1293,10 +1309,6 @@ install_deployment_kernel (OstreeSysroot *sysroot,
g_autofree char *contents = NULL;
g_autofree char *deployment_version = NULL;
g_autoptr(GHashTable) osrelease_values = NULL;
- g_autofree char *linux_relpath = NULL;
- g_autofree char *linux_key = NULL;
- g_autofree char *initramfs_relpath = NULL;
- g_autofree char *initrd_key = NULL;
g_autofree char *version_key = NULL;
g_autofree char *ostree_kernel_arg = NULL;
g_autofree char *options_key = NULL;
@@ -1342,9 +1354,9 @@ install_deployment_kernel (OstreeSysroot *sysroot,
glnx_set_prefix_error_from_errno (error, "fstat %s", dest_kernel_name);
goto out;
}
- if (!glnx_file_copy_at (tree_boot_dfd, tree_kernel_name, NULL,
- bootcsum_dfd, dest_kernel_name, 0,
- cancellable, error))
+ if (!hardlink_or_copy_at (tree_boot_dfd, tree_kernel_name,
+ bootcsum_dfd, dest_kernel_name,
+ cancellable, error))
goto out;
}
@@ -1359,9 +1371,9 @@ install_deployment_kernel (OstreeSysroot *sysroot,
glnx_set_prefix_error_from_errno (error, "fstat %s", dest_initramfs_name);
goto out;
}
- if (!glnx_file_copy_at (tree_boot_dfd, tree_initramfs_name, NULL,
- bootcsum_dfd, dest_initramfs_name, 0,
- cancellable, error))
+ if (!hardlink_or_copy_at (tree_boot_dfd, tree_initramfs_name,
+ bootcsum_dfd, dest_initramfs_name,
+ cancellable, error))
goto out;
}
}
diff --git a/src/libostree/ostree-sysroot-private.h b/src/libostree/ostree-sysroot-private.h
index d210a36f..1fa8e83c 100644
--- a/src/libostree/ostree-sysroot-private.h
+++ b/src/libostree/ostree-sysroot-private.h
@@ -34,6 +34,10 @@ typedef enum {
} OstreeSysrootDebugFlags;
+/**
+ * OstreeSysroot:
+ * Internal struct
+ */
struct OstreeSysroot {
GObject parent;
diff --git a/src/libostree/ostree-sysroot-upgrader.c b/src/libostree/ostree-sysroot-upgrader.c
index 5c8f9d1d..92b8dc83 100644
--- a/src/libostree/ostree-sysroot-upgrader.c
+++ b/src/libostree/ostree-sysroot-upgrader.c
@@ -25,7 +25,7 @@
#include "ostree-sysroot-upgrader.h"
/**
- * SECTION:libostree-sysroot-upgrader
+ * SECTION:ostree-sysroot-upgrader
* @title: Simple upgrade class
* @short_description: Upgrade OSTree systems
*
diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c
index b114a901..7cc4c9f5 100644
--- a/src/libostree/ostree-sysroot.c
+++ b/src/libostree/ostree-sysroot.c
@@ -37,7 +37,7 @@ find_booted_deployment (OstreeSysroot *self,
GError **error);
/**
- * SECTION:libostree-sysroot
+ * SECTION:ostree-sysroot
* @title: Root partition mount point
* @short_description: Manage physical root filesystem
*
@@ -70,10 +70,12 @@ ostree_sysroot_finalize (GObject *object)
g_clear_object (&self->path);
g_clear_object (&self->sepolicy);
g_clear_object (&self->repo);
+ g_clear_pointer (&self->deployments, g_ptr_array_unref);
+ g_clear_object (&self->booted_deployment);
glnx_release_lock_file (&self->lock);
- (void) ostree_sysroot_unload (self);
+ ostree_sysroot_unload (self);
G_OBJECT_CLASS (ostree_sysroot_parent_class)->finalize (object);
}
@@ -276,28 +278,34 @@ ostree_sysroot_ensure_initialized (OstreeSysroot *self,
GError **error)
{
gboolean ret = FALSE;
- g_autoptr(GFile) dir = NULL;
- g_autoptr(GFile) ostree_dir = NULL;
- g_autoptr(GFile) repo_dir = NULL;
+ struct stat stbuf;
- ostree_dir = g_file_get_child (self->path, "ostree");
- repo_dir = g_file_get_child (ostree_dir, "repo");
- if (!gs_file_ensure_directory (repo_dir, TRUE, cancellable, error))
+ if (!ensure_sysroot_fd (self, error))
goto out;
- g_clear_object (&dir);
- dir = g_file_get_child (ostree_dir, "deploy");
- if (!gs_file_ensure_directory (dir, TRUE, cancellable, error))
- goto out;
-
- g_clear_object (&dir);
- dir = ot_gfile_get_child_build_path (ostree_dir, "repo", "objects", NULL);
- if (!g_file_query_exists (dir, NULL))
- {
- glnx_unref_object OstreeRepo *repo = ostree_repo_new (repo_dir);
- if (!ostree_repo_create (repo, OSTREE_REPO_MODE_BARE,
+ if (!glnx_shutil_mkdir_p_at (self->sysroot_fd, "ostree/repo", 0755,
cancellable, error))
- goto out;
+ goto out;
+
+ if (!glnx_shutil_mkdir_p_at (self->sysroot_fd, "ostree/deploy", 0755,
+ cancellable, error))
+ goto out;
+
+ if (fstatat (self->sysroot_fd, "ostree/repo/objects", &stbuf, 0) != 0)
+ {
+ if (errno != ENOENT)
+ {
+ glnx_set_prefix_error_from_errno (error, "stat %s", "ostree/repo/objects");
+ goto out;
+ }
+ else
+ {
+ g_autoptr(GFile) repo_dir = g_file_resolve_relative_path (self->path, "ostree/repo");
+ glnx_unref_object OstreeRepo *repo = ostree_repo_new (repo_dir);
+ if (!ostree_repo_create (repo, OSTREE_REPO_MODE_BARE,
+ cancellable, error))
+ goto out;
+ }
}
ret = TRUE;
@@ -873,7 +881,7 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self,
}
g_clear_pointer (&self->deployments, g_ptr_array_unref);
- g_clear_pointer (&self->booted_deployment, g_object_unref);
+ g_clear_object (&self->booted_deployment);
self->bootversion = -1;
self->subbootversion = -1;
diff --git a/src/libostree/ostree.h b/src/libostree/ostree.h
index f6bb6adb..9846a7e3 100644
--- a/src/libostree/ostree.h
+++ b/src/libostree/ostree.h
@@ -31,3 +31,5 @@
#include
#include
#include
+
+#include
diff --git a/src/libotutil/ot-fs-utils.c b/src/libotutil/ot-fs-utils.c
index 4d45cd06..46a0405b 100644
--- a/src/libotutil/ot-fs-utils.c
+++ b/src/libotutil/ot-fs-utils.c
@@ -230,3 +230,24 @@ ot_openat_ignore_enoent (int dfd,
out:
return ret;
}
+
+GBytes *
+ot_file_mapat_bytes (int dfd,
+ const char *path,
+ GError **error)
+{
+ glnx_fd_close int fd = openat (dfd, path, O_RDONLY | O_CLOEXEC);
+ g_autoptr(GMappedFile) mfile = NULL;
+
+ if (fd < 0)
+ {
+ glnx_set_error_from_errno (error);
+ return FALSE;
+ }
+
+ mfile = g_mapped_file_new_from_fd (fd, FALSE, error);
+ if (!mfile)
+ return FALSE;
+
+ return g_mapped_file_get_bytes (mfile);
+}
diff --git a/src/libotutil/ot-fs-utils.h b/src/libotutil/ot-fs-utils.h
index cfeea74d..27f0f38e 100644
--- a/src/libotutil/ot-fs-utils.h
+++ b/src/libotutil/ot-fs-utils.h
@@ -66,4 +66,8 @@ gboolean ot_openat_ignore_enoent (int dfd,
int *out_fd,
GError **error);
+GBytes *ot_file_mapat_bytes (int dfd,
+ const char *path,
+ GError **error);
+
G_END_DECLS
diff --git a/src/libotutil/ot-gio-utils.c b/src/libotutil/ot-gio-utils.c
index 9b8b0dce..eb9b94f5 100644
--- a/src/libotutil/ot-gio-utils.c
+++ b/src/libotutil/ot-gio-utils.c
@@ -254,7 +254,8 @@ ot_gfile_load_contents_utf8_allow_noent (GFile *path,
GError *temp_error = NULL;
g_autofree char *ret_contents = NULL;
- ret_contents = gs_file_load_contents_utf8 (path, cancellable, &temp_error);
+ ret_contents = glnx_file_get_contents_utf8_at (AT_FDCWD, gs_file_get_path_cached (path), NULL,
+ cancellable, &temp_error);
if (!ret_contents)
{
if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
@@ -356,8 +357,8 @@ ot_gfile_replace_contents_fsync (GFile *path,
GError **error)
{
gboolean ret = FALSE;
- int parent_dfd;
- const char *target_basename = gs_file_get_basename_cached (path);
+ glnx_fd_close int parent_dfd = -1;
+ const char *target_basename = glnx_basename (gs_file_get_path_cached (path));
g_autoptr(GFile) parent = NULL;
parent = g_file_get_parent (path);
@@ -373,8 +374,6 @@ ot_gfile_replace_contents_fsync (GFile *path,
ret = TRUE;
out:
- if (parent_dfd != -1)
- (void) close (parent_dfd);
return ret;
}
@@ -389,25 +388,12 @@ ot_gfile_ensure_unlinked (GFile *path,
GCancellable *cancellable,
GError **error)
{
- gboolean ret = FALSE;
- GError *temp_error = NULL;
-
- if (!gs_file_unlink (path, cancellable, &temp_error))
+ if (unlink (gs_file_get_path_cached (path)) != 0)
{
- if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
- {
- g_clear_error (&temp_error);
- }
- else
- {
- g_propagate_error (error, temp_error);
- goto out;
- }
+ if (errno != ENOENT)
+ return FALSE;
}
-
- ret = TRUE;
- out:
- return ret;
+ return TRUE;
}
/**
@@ -424,7 +410,7 @@ ot_util_fsync_directory (GFile *dir,
GError **error)
{
gboolean ret = FALSE;
- int dfd = -1;
+ glnx_fd_close int dfd = -1;
if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (dir), TRUE,
&dfd, error))
@@ -438,8 +424,6 @@ ot_util_fsync_directory (GFile *dir,
ret = TRUE;
out:
- if (dfd != -1)
- (void) close (dfd);
return ret;
}
@@ -458,8 +442,8 @@ ot_util_ensure_directory_and_fsync (GFile *dir,
GError **error)
{
gboolean ret = FALSE;
- int parentfd = -1;
- const char *basename = gs_file_get_basename_cached (dir);
+ glnx_fd_close int parentfd = -1;
+ const char *basename = glnx_basename (gs_file_get_path_cached (dir));
g_autoptr(GFile) parent = g_file_get_parent (dir);
again:
@@ -507,7 +491,5 @@ ot_util_ensure_directory_and_fsync (GFile *dir,
ret = TRUE;
out:
- if (parentfd != -1)
- (void) close (parentfd);
return ret;
}
diff --git a/src/libotutil/ot-variant-utils.c b/src/libotutil/ot-variant-utils.c
index 2dc07582..315bbeb2 100644
--- a/src/libotutil/ot-variant-utils.c
+++ b/src/libotutil/ot-variant-utils.c
@@ -33,7 +33,10 @@
GVariant *
ot_gvariant_new_empty_string_dict (void)
{
- return g_variant_builder_end (g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")));
+ g_auto(GVariantBuilder) builder = {{0,}};
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+ return g_variant_builder_end (&builder);
}
GVariant *
@@ -114,63 +117,31 @@ ot_util_variant_take_ref (GVariant *variant)
return g_variant_take_ref (variant);
}
-/**
- * ot_util_variant_map:
- * @src: a #GFile
- * @type: Use this for variant
- * @trusted: See documentation of g_variant_new_from_data()
- * @out_variant: (out): Return location for new variant
- * @error:
- *
- * Memory-map @src, and store a new #GVariant referring to this memory
- * in @out_variant. Note the returned @out_variant is not floating.
- */
-gboolean
-ot_util_variant_map (GFile *src,
- const GVariantType *type,
- gboolean trusted,
- GVariant **out_variant,
- GError **error)
-{
- gboolean ret = FALSE;
- g_autoptr(GVariant) ret_variant = NULL;
- GMappedFile *mfile = NULL;
-
- mfile = gs_file_map_noatime (src, NULL, error);
- if (!mfile)
- goto out;
-
- ret_variant = g_variant_new_from_data (type,
- g_mapped_file_get_contents (mfile),
- g_mapped_file_get_length (mfile),
- trusted,
- (GDestroyNotify) g_mapped_file_unref,
- mfile);
- g_variant_ref_sink (ret_variant);
-
- ret = TRUE;
- ot_transfer_out_value(out_variant, &ret_variant);
- out:
- return ret;
-}
-
gboolean
ot_util_variant_map_at (int dfd,
const char *path,
const GVariantType *type,
- gboolean trusted,
+ OtVariantMapFlags flags,
GVariant **out_variant,
GError **error)
{
glnx_fd_close int fd = -1;
- g_autoptr(GVariant) ret_variant = NULL;
+ const gboolean trusted = (flags & OT_VARIANT_MAP_TRUSTED) > 0;
fd = openat (dfd, path, O_RDONLY | O_CLOEXEC);
if (fd < 0)
{
- glnx_set_error_from_errno (error);
- g_prefix_error (error, "Opening %s: ", path);
- return FALSE;
+ if (errno == ENOENT && (flags & OT_VARIANT_MAP_ALLOW_NOENT) > 0)
+ {
+ *out_variant = NULL;
+ return TRUE;
+ }
+ else
+ {
+ glnx_set_error_from_errno (error);
+ g_prefix_error (error, "Opening %s: ", path);
+ return FALSE;
+ }
}
return ot_util_variant_map_fd (fd, 0, type, trusted, out_variant, error);
@@ -186,6 +157,7 @@ variant_map_data_destroy (gpointer data)
{
VariantMapData *mdata = data;
(void) munmap (mdata->addr, mdata->len);
+ g_free (mdata);
}
gboolean
@@ -221,8 +193,8 @@ ot_util_variant_map_fd (int fd,
mdata->len = len;
ret = TRUE;
- *out_variant = g_variant_new_from_data (type, map, len, trusted,
- variant_map_data_destroy, mdata);
+ *out_variant = g_variant_ref_sink (g_variant_new_from_data (type, map, len, trusted,
+ variant_map_data_destroy, mdata));
out:
return ret;
}
diff --git a/src/libotutil/ot-variant-utils.h b/src/libotutil/ot-variant-utils.h
index 1a7abe0e..8a33cf60 100644
--- a/src/libotutil/ot-variant-utils.h
+++ b/src/libotutil/ot-variant-utils.h
@@ -42,16 +42,15 @@ gboolean ot_util_variant_save (GFile *dest,
GCancellable *cancellable,
GError **error);
-gboolean ot_util_variant_map (GFile *src,
- const GVariantType *type,
- gboolean trusted,
- GVariant **out_variant,
- GError **error);
+typedef enum {
+ OT_VARIANT_MAP_TRUSTED = (1 << 0),
+ OT_VARIANT_MAP_ALLOW_NOENT = (1 << 1)
+} OtVariantMapFlags;
gboolean ot_util_variant_map_at (int dfd,
const char *path,
const GVariantType *type,
- gboolean trusted,
+ OtVariantMapFlags flags,
GVariant **out_variant,
GError **error);
diff --git a/src/ostree/ot-admin-builtin-init-fs.c b/src/ostree/ot-admin-builtin-init-fs.c
index 0172f01c..1f122553 100644
--- a/src/ostree/ot-admin-builtin-init-fs.c
+++ b/src/ostree/ot-admin-builtin-init-fs.c
@@ -39,8 +39,7 @@ ot_admin_builtin_init_fs (int argc, char **argv, GCancellable *cancellable, GErr
GOptionContext *context;
glnx_unref_object OstreeSysroot *sysroot = NULL;
gboolean ret = FALSE;
- g_autoptr(GFile) dir = NULL;
- g_autoptr(GFile) child = NULL;
+ glnx_fd_close int root_dfd = -1;
glnx_unref_object OstreeSysroot *target_sysroot = NULL;
guint i;
const char *normal_toplevels[] = {"boot", "dev", "home", "proc", "run", "sys"};
@@ -58,36 +57,31 @@ ot_admin_builtin_init_fs (int argc, char **argv, GCancellable *cancellable, GErr
goto out;
}
- dir = g_file_new_for_path (argv[1]);
- target_sysroot = ostree_sysroot_new (dir);
+ if (!glnx_opendirat (AT_FDCWD, argv[1], TRUE, &root_dfd, error))
+ goto out;
+ { g_autoptr(GFile) dir = g_file_new_for_path (argv[1]);
+ target_sysroot = ostree_sysroot_new (dir);
+ }
for (i = 0; i < G_N_ELEMENTS(normal_toplevels); i++)
{
- child = g_file_get_child (dir, normal_toplevels[i]);
- if (!gs_file_ensure_directory_mode (child, 0755, cancellable, error))
+ if (!glnx_shutil_mkdir_p_at (root_dfd, normal_toplevels[i], 0755,
+ cancellable, error))
goto out;
- g_clear_object (&child);
}
-
- child = g_file_get_child (dir, "root");
- if (!gs_file_ensure_directory_mode (child, 0700, cancellable, error))
+
+ if (!glnx_shutil_mkdir_p_at (root_dfd, "root", 0700,
+ cancellable, error))
goto out;
- g_clear_object (&child);
- child = g_file_get_child (dir, "tmp");
- if (!gs_file_ensure_directory_mode (child, 01777, cancellable, error))
+ if (!glnx_shutil_mkdir_p_at (root_dfd, "tmp", 01777,
+ cancellable, error))
goto out;
- /* FIXME - we should be using an API that explicitly ignores umask;
- */
- {
- const char *path = gs_file_get_path_cached (child);
- if (chmod (path, 01777) == -1)
- {
- gs_set_prefix_error_from_errno (error, errno, "chmod");
- goto out;
- }
- }
- g_clear_object (&child);
+ if (fchmodat (root_dfd, "tmp", 01777, 0) == -1)
+ {
+ glnx_set_prefix_error_from_errno (error, "chmod: %s", "tmp");
+ goto out;
+ }
if (!ostree_sysroot_ensure_initialized (target_sysroot, cancellable, error))
goto out;
diff --git a/src/ostree/ot-admin-builtin-instutil.c b/src/ostree/ot-admin-builtin-instutil.c
index 1087b381..88894a6a 100644
--- a/src/ostree/ot-admin-builtin-instutil.c
+++ b/src/ostree/ot-admin-builtin-instutil.c
@@ -20,11 +20,11 @@
#include "config.h"
+#include "ot-main.h"
#include "ot-builtins.h"
#include "ot-admin-instutil-builtins.h"
#include "ot-admin-builtins.h"
#include "ot-admin-functions.h"
-#include "ot-main.h"
#include "ostree.h"
#include
diff --git a/src/ostree/ot-admin-builtin-switch.c b/src/ostree/ot-admin-builtin-switch.c
index 0e0101a9..b313e830 100644
--- a/src/ostree/ot-admin-builtin-switch.c
+++ b/src/ostree/ot-admin-builtin-switch.c
@@ -61,8 +61,6 @@ ot_admin_builtin_switch (int argc, char **argv, GCancellable *cancellable, GErro
glnx_unref_object OstreeSysrootUpgrader *upgrader = NULL;
glnx_unref_object OstreeAsyncProgress *progress = NULL;
gboolean changed;
- GSConsole *console = NULL;
- gboolean in_status_line = FALSE;
GKeyFile *old_origin;
GKeyFile *new_origin = NULL;
@@ -125,28 +123,24 @@ ot_admin_builtin_switch (int argc, char **argv, GCancellable *cancellable, GErro
if (!ostree_sysroot_upgrader_set_origin (upgrader, new_origin, cancellable, error))
goto out;
- console = gs_console_get ();
- if (console)
- {
- gs_console_begin_status_line (console, "", NULL, NULL);
- in_status_line = TRUE;
- progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console);
- }
+ { g_auto(GLnxConsoleRef) console = { 0, };
+ glnx_console_lock (&console);
- /* Always allow older...there's not going to be a chronological
- * relationship necessarily.
- */
- if (!ostree_sysroot_upgrader_pull (upgrader, 0,
- OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER,
- progress, &changed,
- cancellable, error))
- goto out;
+ if (console.is_tty)
+ progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console);
- if (in_status_line)
- {
- gs_console_end_status_line (console, NULL, NULL);
- in_status_line = FALSE;
- }
+ /* Always allow older...there's not going to be a chronological
+ * relationship necessarily.
+ */
+ if (!ostree_sysroot_upgrader_pull (upgrader, 0,
+ OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER,
+ progress, &changed,
+ cancellable, error))
+ goto out;
+
+ if (progress)
+ ostree_async_progress_finish (progress);
+ }
if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, error))
goto out;
@@ -171,8 +165,6 @@ ot_admin_builtin_switch (int argc, char **argv, GCancellable *cancellable, GErro
ret = TRUE;
out:
- if (in_status_line)
- gs_console_end_status_line (console, NULL, NULL);
if (new_origin)
g_key_file_unref (new_origin);
if (context)
diff --git a/src/ostree/ot-admin-builtin-upgrade.c b/src/ostree/ot-admin-builtin-upgrade.c
index b3b531c0..81f9bb6f 100644
--- a/src/ostree/ot-admin-builtin-upgrade.c
+++ b/src/ostree/ot-admin-builtin-upgrade.c
@@ -55,8 +55,6 @@ ot_admin_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GErr
g_autoptr(GFile) deployment_path = NULL;
g_autoptr(GFile) deployment_origin_path = NULL;
g_autoptr(GKeyFile) origin = NULL;
- GSConsole *console = NULL;
- gboolean in_status_line = FALSE;
glnx_unref_object OstreeAsyncProgress *progress = NULL;
gboolean changed;
OstreeSysrootUpgraderPullFlags upgraderpullflags = 0;
@@ -108,27 +106,23 @@ ot_admin_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GErr
}
}
- console = gs_console_get ();
- if (console)
- {
- gs_console_begin_status_line (console, "", NULL, NULL);
- in_status_line = TRUE;
- progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console);
- }
+ { g_auto(GLnxConsoleRef) console = { 0, };
+ glnx_console_lock (&console);
- if (opt_allow_downgrade)
- upgraderpullflags |= OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER;
+ if (console.is_tty)
+ progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console);
- if (!ostree_sysroot_upgrader_pull (upgrader, 0, upgraderpullflags,
- progress, &changed,
- cancellable, error))
- goto out;
+ if (opt_allow_downgrade)
+ upgraderpullflags |= OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER;
+
+ if (!ostree_sysroot_upgrader_pull (upgrader, 0, upgraderpullflags,
+ progress, &changed,
+ cancellable, error))
+ goto out;
- if (in_status_line)
- {
- gs_console_end_status_line (console, NULL, NULL);
- in_status_line = FALSE;
- }
+ if (progress)
+ ostree_async_progress_finish (progress);
+ }
if (!changed)
{
@@ -148,8 +142,6 @@ ot_admin_builtin_upgrade (int argc, char **argv, GCancellable *cancellable, GErr
ret = TRUE;
out:
- if (in_status_line)
- gs_console_end_status_line (console, NULL, NULL);
if (context)
g_option_context_free (context);
return ret;
diff --git a/src/ostree/ot-admin-functions.c b/src/ostree/ot-admin-functions.c
index bc9034e1..ed4dfdfb 100644
--- a/src/ostree/ot-admin-functions.c
+++ b/src/ostree/ot-admin-functions.c
@@ -22,6 +22,7 @@
#include "config.h"
+#include "libglnx.h"
#include "ot-admin-functions.h"
#include "otutil.h"
#include "ostree.h"
diff --git a/src/ostree/ot-admin-instutil-builtin-selinux-ensure-labeled.c b/src/ostree/ot-admin-instutil-builtin-selinux-ensure-labeled.c
index 1f0e91e3..776ab54f 100644
--- a/src/ostree/ot-admin-instutil-builtin-selinux-ensure-labeled.c
+++ b/src/ostree/ot-admin-instutil-builtin-selinux-ensure-labeled.c
@@ -120,7 +120,7 @@ relabel_recursively (OstreeSePolicy *sepolicy,
if (file_info == NULL)
break;
- g_ptr_array_add (path_parts, (char*)gs_file_get_basename_cached (child));
+ g_ptr_array_add (path_parts, (char*)g_file_info_get_name (file_info));
ftype = g_file_info_get_file_type (file_info);
if (ftype == G_FILE_TYPE_DIRECTORY)
diff --git a/src/ostree/ot-builtin-admin.c b/src/ostree/ot-builtin-admin.c
index 8b866170..7e7b04b5 100644
--- a/src/ostree/ot-builtin-admin.c
+++ b/src/ostree/ot-builtin-admin.c
@@ -22,10 +22,10 @@
#include "config.h"
+#include "ot-main.h"
#include "ot-builtins.h"
#include "ot-admin-builtins.h"
#include "ot-admin-functions.h"
-#include "ot-main.h"
#include "ostree.h"
#include "ostree-repo-file.h"
diff --git a/src/ostree/ot-builtin-commit.c b/src/ostree/ot-builtin-commit.c
index 1d96855d..85c4c65d 100644
--- a/src/ostree/ot-builtin-commit.c
+++ b/src/ostree/ot-builtin-commit.c
@@ -32,10 +32,12 @@
static char *opt_subject;
static char *opt_body;
+static gboolean opt_editor;
static char *opt_parent;
static gboolean opt_orphan;
static char *opt_branch;
static char *opt_statoverride_file;
+static char *opt_skiplist_file;
static char **opt_metadata_strings;
static char **opt_detached_metadata_strings;
static gboolean opt_link_checkout_speedup;
@@ -72,6 +74,7 @@ static GOptionEntry options[] = {
{ "parent", 0, 0, G_OPTION_ARG_STRING, &opt_parent, "Parent ref, or \"none\"", "REF" },
{ "subject", 's', 0, G_OPTION_ARG_STRING, &opt_subject, "One line subject", "SUBJECT" },
{ "body", 'm', 0, G_OPTION_ARG_STRING, &opt_body, "Full description", "BODY" },
+ { "editor", 'e', 0, G_OPTION_ARG_NONE, &opt_editor, "Use an editor to write the commit message", NULL },
{ "branch", 'b', 0, G_OPTION_ARG_STRING, &opt_branch, "Branch", "BRANCH" },
{ "orphan", 0, 0, G_OPTION_ARG_NONE, &opt_orphan, "Create a commit without writing a ref", NULL },
{ "tree", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_trees, "Overlay the given argument as a tree", "dir=PATH or tar=TARFILE or ref=COMMIT" },
@@ -84,6 +87,7 @@ static GOptionEntry options[] = {
{ "tar-autocreate-parents", 0, 0, G_OPTION_ARG_NONE, &opt_tar_autocreate_parents, "When loading tar archives, automatically create parent directories as needed", NULL },
{ "skip-if-unchanged", 0, 0, G_OPTION_ARG_NONE, &opt_skip_if_unchanged, "If the contents are unchanged from previous commit, do nothing", NULL },
{ "statoverride", 0, 0, G_OPTION_ARG_FILENAME, &opt_statoverride_file, "File containing list of modifications to make to permissions", "PATH" },
+ { "skip-list", 0, 0, G_OPTION_ARG_FILENAME, &opt_skiplist_file, "File containing list of files to skip", "PATH" },
{ "table-output", 0, 0, G_OPTION_ARG_NONE, &opt_table_output, "Output more information in a KEY: VALUE format", NULL },
{ "gpg-sign", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_key_ids, "GPG Key ID to sign the commit with", "KEY-ID"},
{ "gpg-homedir", 0, 0, G_OPTION_ARG_STRING, &opt_gpg_homedir, "GPG Homedir to use when looking for keyrings", "HOMEDIR"},
@@ -95,65 +99,85 @@ static GOptionEntry options[] = {
};
static gboolean
-parse_statoverride_file (GHashTable **out_mode_add,
- GCancellable *cancellable,
- GError **error)
+parse_file_by_line (const char *path,
+ gboolean (*cb)(const char*, void*, GError**),
+ void *cbdata,
+ GCancellable *cancellable,
+ GError **error)
{
gboolean ret = FALSE;
- gsize len;
- char **iter = NULL; /* nofree */
- g_autoptr(GHashTable) ret_hash = NULL;
- g_autoptr(GFile) path = NULL;
g_autofree char *contents = NULL;
+ g_autoptr(GFile) file = NULL;
char **lines = NULL;
- path = g_file_new_for_path (opt_statoverride_file);
-
- if (!g_file_load_contents (path, cancellable, &contents, &len, NULL,
- error))
+ file = g_file_new_for_path (path);
+ if (!g_file_load_contents (file, cancellable, &contents, NULL, NULL, error))
goto out;
-
- ret_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
lines = g_strsplit (contents, "\n", -1);
-
- for (iter = lines; iter && *iter; iter++)
+ for (char **iter = lines; iter && *iter; iter++)
{
- const char *line = *iter;
+ /* skip empty lines at least */
+ if (**iter == '\0')
+ continue;
- if (*line == '+')
- {
- const char *spc;
- guint mode_add;
-
- spc = strchr (line + 1, ' ');
- if (!spc)
- {
- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "Malformed statoverride file");
- goto out;
- }
-
- mode_add = (guint32)(gint32)g_ascii_strtod (line + 1, NULL);
- g_hash_table_insert (ret_hash,
- g_strdup (spc + 1),
- GUINT_TO_POINTER((gint32)mode_add));
- }
+ if (!cb (*iter, cbdata, error))
+ goto out;
}
ret = TRUE;
- ot_transfer_out_value (out_mode_add, &ret_hash);
- out:
+out:
g_strfreev (lines);
return ret;
}
+static gboolean
+handle_statoverride_line (const char *line,
+ void *data,
+ GError **error)
+{
+ GHashTable *files = data;
+ const char *spc;
+ guint mode_add;
+
+ spc = strchr (line, ' ');
+ if (spc == NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Malformed statoverride file (no space found)");
+ return FALSE;
+ }
+
+ mode_add = (guint32)(gint32)g_ascii_strtod (line, NULL);
+ g_hash_table_insert (files, g_strdup (spc + 1),
+ GUINT_TO_POINTER((gint32)mode_add));
+ return TRUE;
+}
+
+static gboolean
+handle_skiplist_line (const char *line,
+ void *data,
+ GError **error)
+{
+ GHashTable *files = data;
+ g_hash_table_add (files, g_strdup (line));
+ return TRUE;
+}
+
+struct CommitFilterData {
+ GHashTable *mode_adds;
+ GHashTable *skip_list;
+};
+
static OstreeRepoCommitFilterResult
commit_filter (OstreeRepo *self,
const char *path,
GFileInfo *file_info,
gpointer user_data)
{
- GHashTable *mode_adds = user_data;
+ struct CommitFilterData *data = user_data;
+ GHashTable *mode_adds = data->mode_adds;
+ GHashTable *skip_list = data->skip_list;
gpointer value;
if (opt_owner_uid >= 0)
@@ -169,7 +193,13 @@ commit_filter (OstreeRepo *self,
current_mode | mode_add);
g_hash_table_remove (mode_adds, path);
}
-
+
+ if (skip_list && g_hash_table_contains (skip_list, path))
+ {
+ g_hash_table_remove (skip_list, path);
+ return OSTREE_REPO_COMMIT_FILTER_SKIP;
+ }
+
return OSTREE_REPO_COMMIT_FILTER_ALLOW;
}
@@ -188,14 +218,18 @@ commit_editor (OstreeRepo *repo,
char **lines = NULL;
int i;
- *subject = NULL;
- *body = NULL;
-
input = g_strdup_printf ("\n"
"# Please enter the commit message for your changes. The first line will\n"
"# become the subject, and the remainder the body. Lines starting\n"
"# with '#' will be ignored, and an empty message aborts the commit."
- "%s%s\n", branch ? "\n#\n# Branch: " : "", branch ?: "");
+ "%s%s%s%s%s%s\n"
+ , branch ? "\n#\n# Branch: " : "", branch ? branch : ""
+ , *subject ? "\n" : "", *subject ? *subject : ""
+ , *body ? "\n" : "", *body ? *body : ""
+ );
+
+ *subject = NULL;
+ *body = NULL;
output = ot_editor_prompt (repo, input, cancellable, error);
if (output == NULL)
@@ -310,9 +344,11 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
glnx_unref_object OstreeMutableTree *mtree = NULL;
g_autofree char *tree_type = NULL;
g_autoptr(GHashTable) mode_adds = NULL;
+ g_autoptr(GHashTable) skip_list = NULL;
OstreeRepoCommitModifierFlags flags = 0;
OstreeRepoCommitModifier *modifier = NULL;
OstreeRepoTransactionStats stats;
+ struct CommitFilterData filter_data = { 0, };
context = g_option_context_new ("[PATH] - Commit a new revision");
@@ -324,7 +360,17 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
if (opt_statoverride_file)
{
- if (!parse_statoverride_file (&mode_adds, cancellable, error))
+ mode_adds = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ if (!parse_file_by_line (opt_statoverride_file, handle_statoverride_line,
+ mode_adds, cancellable, error))
+ goto out;
+ }
+
+ if (opt_skiplist_file)
+ {
+ skip_list = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ if (!parse_file_by_line (opt_skiplist_file, handle_skiplist_line,
+ skip_list, cancellable, error))
goto out;
}
@@ -359,9 +405,13 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
|| opt_owner_uid >= 0
|| opt_owner_gid >= 0
|| opt_statoverride_file != NULL
+ || opt_skiplist_file != NULL
|| opt_no_xattrs)
{
- modifier = ostree_repo_commit_modifier_new (flags, commit_filter, mode_adds, NULL);
+ filter_data.mode_adds = mode_adds;
+ filter_data.skip_list = skip_list;
+ modifier = ostree_repo_commit_modifier_new (flags, commit_filter,
+ &filter_data, NULL);
}
if (opt_parent)
@@ -381,19 +431,12 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
goto out;
}
- if (!opt_subject && !opt_body)
+ if (opt_editor)
{
if (!commit_editor (repo, opt_branch, &opt_subject, &opt_body, cancellable, error))
goto out;
}
- if (!opt_subject)
- {
- g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- "A subject must be specified with --subject");
- goto out;
- }
-
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
goto out;
@@ -491,6 +534,22 @@ ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError
goto out;
}
+ if (skip_list && g_hash_table_size (skip_list) > 0)
+ {
+ GHashTableIter hash_iter;
+ gpointer key;
+
+ g_hash_table_iter_init (&hash_iter, skip_list);
+
+ while (g_hash_table_iter_next (&hash_iter, &key, NULL))
+ {
+ g_printerr ("Unmatched skip-list path: %s\n", (char*)key);
+ }
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Unmatched skip-list paths");
+ goto out;
+ }
+
if (!ostree_repo_write_mtree (repo, mtree, &root, cancellable, error))
goto out;
diff --git a/src/ostree/ot-builtin-export.c b/src/ostree/ot-builtin-export.c
index cccb50e7..5b84d1ab 100644
--- a/src/ostree/ot-builtin-export.c
+++ b/src/ostree/ot-builtin-export.c
@@ -32,10 +32,14 @@
#endif
static char *opt_output_path;
+static char *opt_subpath;
+static char *opt_prefix;
static gboolean opt_no_xattrs;
static GOptionEntry options[] = {
{ "no-xattrs", 0, 0, G_OPTION_ARG_NONE, &opt_no_xattrs, "Skip output of extended attributes", NULL },
+ { "subpath", 0, 0, G_OPTION_ARG_STRING, &opt_subpath, "Checkout sub-directory PATH", "PATH" },
+ { "prefix", 0, 0, G_OPTION_ARG_STRING, &opt_prefix, "Add PATH as prefix to archive pathnames", "PATH" },
{ "output", 'o', 0, G_OPTION_ARG_STRING, &opt_output_path, "Output to PATH ", "PATH" },
{ NULL }
};
@@ -60,6 +64,7 @@ ostree_builtin_export (int argc, char **argv, GCancellable *cancellable, GError
gboolean ret = FALSE;
const char *rev;
g_autoptr(GFile) root = NULL;
+ g_autoptr(GFile) subtree = NULL;
g_autofree char *commit = NULL;
g_autoptr(GVariant) commit_data = NULL;
struct archive *a;
@@ -124,7 +129,14 @@ ostree_builtin_export (int argc, char **argv, GCancellable *cancellable, GError
opts.timestamp_secs = ostree_commit_get_timestamp (commit_data);
- if (!ostree_repo_export_tree_to_archive (repo, &opts, (OstreeRepoFile*)root, a,
+ if (opt_subpath)
+ subtree = g_file_resolve_relative_path (root, opt_subpath);
+ else
+ subtree = g_object_ref (root);
+
+ opts.path_prefix = opt_prefix;
+
+ if (!ostree_repo_export_tree_to_archive (repo, &opts, (OstreeRepoFile*)subtree, a,
cancellable, error))
goto out;
diff --git a/src/ostree/ot-builtin-init.c b/src/ostree/ot-builtin-init.c
index 9e7c8a2b..a250b793 100644
--- a/src/ostree/ot-builtin-init.c
+++ b/src/ostree/ot-builtin-init.c
@@ -37,7 +37,7 @@ gboolean
ostree_builtin_init (int argc, char **argv, GCancellable *cancellable, GError **error)
{
GOptionContext *context = NULL;
- glnx_unref_object OstreeRepo *repo = NULL;
+ g_autoptr(OstreeRepo) repo = NULL;
gboolean ret = FALSE;
OstreeRepoMode mode;
diff --git a/src/ostree/ot-builtin-pull-local.c b/src/ostree/ot-builtin-pull-local.c
index 36057ec6..7fb6f03a 100644
--- a/src/ostree/ot-builtin-pull-local.c
+++ b/src/ostree/ot-builtin-pull-local.c
@@ -55,7 +55,6 @@ ostree_builtin_pull_local (int argc, char **argv, GCancellable *cancellable, GEr
glnx_unref_object OstreeRepo *repo = NULL;
int i;
const char *src_repo_arg;
- GSConsole *console = NULL;
g_autofree char *src_repo_uri = NULL;
glnx_unref_object OstreeAsyncProgress *progress = NULL;
g_autoptr(GPtrArray) refs_to_fetch = NULL;
@@ -132,14 +131,11 @@ ostree_builtin_pull_local (int argc, char **argv, GCancellable *cancellable, GEr
g_ptr_array_add (refs_to_fetch, NULL);
}
- console = gs_console_get ();
- if (console)
- {
- gs_console_begin_status_line (console, "", NULL, NULL);
- progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console);
- }
-
{ GVariantBuilder builder;
+ g_auto(GLnxConsoleRef) console = { 0, };
+
+ glnx_console_lock (&console);
+
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_add (&builder, "{s@v}", "flags",
@@ -158,17 +154,21 @@ ostree_builtin_pull_local (int argc, char **argv, GCancellable *cancellable, GEr
g_variant_builder_add (&builder, "{s@v}", "depth",
g_variant_new_variant (g_variant_new_int32 (opt_depth)));
+ if (console.is_tty)
+ progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console);
+
if (!ostree_repo_pull_with_options (repo, src_repo_uri,
g_variant_builder_end (&builder),
progress,
cancellable, error))
goto out;
+
+ if (progress)
+ ostree_async_progress_finish (progress);
}
ret = TRUE;
out:
- if (progress)
- ostree_async_progress_finish (progress);
if (context)
g_option_context_free (context);
if (repo)
diff --git a/src/ostree/ot-builtin-pull.c b/src/ostree/ot-builtin-pull.c
index 734f7440..99b25937 100644
--- a/src/ostree/ot-builtin-pull.c
+++ b/src/ostree/ot-builtin-pull.c
@@ -37,7 +37,8 @@ static gboolean opt_untrusted;
static char* opt_subpath;
static char* opt_cache_dir;
static int opt_depth = 0;
-
+static char* opt_url;
+
static GOptionEntry options[] = {
{ "commit-metadata-only", 0, 0, G_OPTION_ARG_NONE, &opt_commit_only, "Fetch only the commit metadata", NULL },
{ "cache-dir", 0, 0, G_OPTION_ARG_STRING, &opt_cache_dir, "Use custom cache dir", NULL },
@@ -49,6 +50,7 @@ static GOptionEntry options[] = {
{ "untrusted", 0, 0, G_OPTION_ARG_NONE, &opt_untrusted, "Do not trust (local) sources", NULL },
{ "dry-run", 0, 0, G_OPTION_ARG_NONE, &opt_dry_run, "Only print information on what will be downloaded (requires static deltas)", NULL },
{ "depth", 0, 0, G_OPTION_ARG_INT, &opt_depth, "Traverse DEPTH parents (-1=infinite) (default: 0)", "DEPTH" },
+ { "url", 0, 0, G_OPTION_ARG_STRING, &opt_url, "Pull objects from this URL instead of the one from the remote config", NULL },
{ NULL }
};
@@ -56,16 +58,17 @@ static void
gpg_verify_result_cb (OstreeRepo *repo,
const char *checksum,
OstreeGpgVerifyResult *result,
- GSConsole *console)
+ GLnxConsoleRef *console)
{
- /* Temporarily place the GSConsole stream (which is just stdout)
- * back in normal mode before printing GPG verification results. */
- gs_console_end_status_line (console, NULL, NULL);
+ /* Temporarily place the tty back in normal mode before printing GPG
+ * verification results.
+ */
+ glnx_console_unlock (console);
g_print ("\n");
ostree_print_gpg_verify_result (result);
- gs_console_begin_status_line (console, "", NULL, NULL);
+ glnx_console_lock (console);
}
static gboolean printed_console_progress;
@@ -109,7 +112,6 @@ ostree_builtin_pull (int argc, char **argv, GCancellable *cancellable, GError **
gboolean ret = FALSE;
g_autofree char *remote = NULL;
OstreeRepoPullFlags pullflags = 0;
- GSConsole *console = NULL;
g_autoptr(GPtrArray) refs_to_fetch = NULL;
g_autoptr(GPtrArray) override_commit_ids = NULL;
glnx_unref_object OstreeAsyncProgress *progress = NULL;
@@ -204,30 +206,16 @@ ostree_builtin_pull (int argc, char **argv, GCancellable *cancellable, GError **
g_ptr_array_add (refs_to_fetch, NULL);
}
- if (!opt_dry_run)
- {
- console = gs_console_get ();
- if (console)
- {
- gs_console_begin_status_line (console, "", NULL, NULL);
- progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console);
- signal_handler_id = g_signal_connect (repo, "gpg-verify-result",
- G_CALLBACK (gpg_verify_result_cb),
- console);
- }
- }
- else
- {
- progress = ostree_async_progress_new_and_connect (dry_run_console_progress_changed, console);
- signal_handler_id = g_signal_connect (repo, "gpg-verify-result",
- G_CALLBACK (gpg_verify_result_cb),
- console);
- }
-
{
GVariantBuilder builder;
+ g_auto(GLnxConsoleRef) console = { 0, };
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+ glnx_console_lock (&console);
+
+ if (opt_url)
+ g_variant_builder_add (&builder, "{s@v}", "override-url",
+ g_variant_new_variant (g_variant_new_string (opt_url)));
if (opt_subpath)
g_variant_builder_add (&builder, "{s@v}", "subdir",
g_variant_new_variant (g_variant_new_string (opt_subpath)));
@@ -252,25 +240,38 @@ ostree_builtin_pull (int argc, char **argv, GCancellable *cancellable, GError **
g_variant_builder_add (&builder, "{s@v}", "override-commit-ids",
g_variant_new_variant (g_variant_new_strv ((const char*const*)override_commit_ids->pdata, override_commit_ids->len)));
+ if (!opt_dry_run)
+ {
+ if (console.is_tty)
+ progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, &console);
+ }
+ else
+ {
+ progress = ostree_async_progress_new_and_connect (dry_run_console_progress_changed, NULL);
+ }
+
+ if (console.is_tty)
+ {
+ signal_handler_id = g_signal_connect (repo, "gpg-verify-result",
+ G_CALLBACK (gpg_verify_result_cb),
+ &console);
+ }
+
if (!ostree_repo_pull_with_options (repo, remote, g_variant_builder_end (&builder),
progress, cancellable, error))
goto out;
+
+ if (progress)
+ ostree_async_progress_finish (progress);
+
+ if (opt_dry_run)
+ g_assert (printed_console_progress);
}
- if (progress)
- ostree_async_progress_finish (progress);
-
- if (opt_dry_run)
- g_assert (printed_console_progress);
-
ret = TRUE;
out:
if (signal_handler_id > 0)
g_signal_handler_disconnect (repo, signal_handler_id);
-
- if (console)
- gs_console_end_status_line (console, NULL, NULL);
-
if (context)
g_option_context_free (context);
return ret;
diff --git a/src/ostree/ot-builtin-refs.c b/src/ostree/ot-builtin-refs.c
index af90c841..10647ec6 100644
--- a/src/ostree/ot-builtin-refs.c
+++ b/src/ostree/ot-builtin-refs.c
@@ -28,10 +28,12 @@
static gboolean opt_delete;
static gboolean opt_list;
+static char *opt_create;
static GOptionEntry options[] = {
{ "delete", 0, 0, G_OPTION_ARG_NONE, &opt_delete, "Delete refs which match PREFIX, rather than listing them", NULL },
{ "list", 0, 0, G_OPTION_ARG_NONE, &opt_list, "Do not remove the prefix from the refs", NULL },
+ { "create", 0, 0, G_OPTION_ARG_STRING, &opt_create, "Create a new ref for an existing commit", "NEWREF" },
{ NULL }
};
@@ -48,10 +50,16 @@ static gboolean do_ref (OstreeRepo *repo, const char *refspec_prefix, GCancellab
cancellable, error))
goto out;
}
+ else if (opt_create)
+ {
+ if (!ostree_repo_list_refs_ext (repo, NULL, &refs, OSTREE_REPO_LIST_REFS_EXT_NONE,
+ cancellable, error))
+ goto out;
+ }
else if (!ostree_repo_list_refs (repo, refspec_prefix, &refs, cancellable, error))
goto out;
- if (!opt_delete)
+ if (!opt_delete && !opt_create)
{
g_hash_table_iter_init (&hashiter, refs);
while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
@@ -60,7 +68,30 @@ static gboolean do_ref (OstreeRepo *repo, const char *refspec_prefix, GCancellab
g_print ("%s\n", ref);
}
}
+ else if (opt_create)
+ {
+ g_autofree char *checksum = NULL;
+ g_autofree char *checksum_existing = NULL;
+
+ if (!ostree_repo_resolve_rev (repo, opt_create, TRUE, &checksum_existing, error))
+ goto out;
+
+ if (checksum_existing != NULL)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "--create specified but ref %s already exists", opt_create);
+ goto out;
+ }
+
+ if (!ostree_repo_resolve_rev (repo, refspec_prefix, FALSE, &checksum, error))
+ goto out;
+
+ if (!ostree_repo_set_ref_immediate (repo, NULL, opt_create, checksum,
+ cancellable, error))
+ goto out;
+ }
else
+ /* delete */
{
g_hash_table_iter_init (&hashiter, refs);
while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
@@ -97,6 +128,12 @@ ostree_builtin_refs (int argc, char **argv, GCancellable *cancellable, GError **
if (argc >= 2)
{
+ if (opt_create && argc > 2)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "You must specify only 1 existing ref when creating a new ref");
+ goto out;
+ }
for (i = 1; i < argc; i++)
if (!do_ref (repo, argv[i], cancellable, error))
goto out;
@@ -110,6 +147,13 @@ ostree_builtin_refs (int argc, char **argv, GCancellable *cancellable, GError **
"At least one PREFIX is required when deleting refs");
goto out;
}
+ else if (opt_create)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "You must specify an existing ref when creating a new ref");
+ goto out;
+ }
+
ret = do_ref (repo, NULL, cancellable, error);
}
diff --git a/src/ostree/ot-builtin-show.c b/src/ostree/ot-builtin-show.c
index a1b4db5f..ef541c2a 100644
--- a/src/ostree/ot-builtin-show.c
+++ b/src/ostree/ot-builtin-show.c
@@ -51,12 +51,9 @@ do_print_variant_generic (const GVariantType *type,
GError **error)
{
gboolean ret = FALSE;
- g_autoptr(GFile) f = NULL;
g_autoptr(GVariant) variant = NULL;
- f = g_file_new_for_path (filename);
-
- if (!ot_util_variant_map (f, type, TRUE, &variant, error))
+ if (!ot_util_variant_map_at (AT_FDCWD, filename, type, TRUE, &variant, error))
goto out;
ot_dump_variant (variant);
diff --git a/src/ostree/ot-builtin-summary.c b/src/ostree/ot-builtin-summary.c
index 4622ceef..2ff1c965 100644
--- a/src/ostree/ot-builtin-summary.c
+++ b/src/ostree/ot-builtin-summary.c
@@ -31,7 +31,7 @@ static char *opt_gpg_homedir;
static GOptionEntry options[] = {
{ "update", 'u', 0, G_OPTION_ARG_NONE, &opt_update, "Update the summary", NULL },
- { "gpg-sign", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_key_ids, "GPG Key ID to sign the commit with", "KEY-ID"},
+ { "gpg-sign", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_key_ids, "GPG Key ID to sign the summary with", "KEY-ID"},
{ "gpg-homedir", 0, 0, G_OPTION_ARG_STRING, &opt_gpg_homedir, "GPG Homedir to use when looking for keyrings", "HOMEDIR"},
{ NULL }
};
diff --git a/src/ostree/ot-builtin-trivial-httpd.c b/src/ostree/ot-builtin-trivial-httpd.c
index 038559bb..811e8924 100644
--- a/src/ostree/ot-builtin-trivial-httpd.c
+++ b/src/ostree/ot-builtin-trivial-httpd.c
@@ -22,6 +22,8 @@
#include
+#include
+
#include "ot-main.h"
#include "ot-builtins.h"
#include "ostree.h"
@@ -32,6 +34,7 @@
#include
static char *opt_port_file = NULL;
+static char *opt_log = NULL;
static gboolean opt_daemonize;
static gboolean opt_autoexit;
static gboolean opt_force_ranges;
@@ -40,6 +43,7 @@ static gint opt_port = 0;
typedef struct {
GFile *root;
gboolean running;
+ GOutputStream *log;
} OtTrivialHttpd;
static GOptionEntry options[] = {
@@ -48,9 +52,31 @@ static GOptionEntry options[] = {
{ "port", 'P', 0, G_OPTION_ARG_INT, &opt_port, "Use the specified TCP port", NULL },
{ "port-file", 'p', 0, G_OPTION_ARG_FILENAME, &opt_port_file, "Write port number to PATH (- for standard output)", "PATH" },
{ "force-range-requests", 0, 0, G_OPTION_ARG_NONE, &opt_force_ranges, "Force range requests by only serving half of files", NULL },
+ { "log-file", 0, 0, G_OPTION_ARG_FILENAME, &opt_log, "Put logs here", "PATH" },
{ NULL }
};
+static void
+httpd_log (OtTrivialHttpd *httpd, const gchar *format, ...) __attribute__ ((format(printf, 2, 3)));
+
+static void
+httpd_log (OtTrivialHttpd *httpd, const gchar *format, ...)
+{
+ g_autoptr(GString) str = NULL;
+ va_list args;
+ gsize written;
+
+ if (!httpd->log)
+ return;
+
+ str = g_string_new (NULL);
+ va_start (args, format);
+ g_string_vprintf (str, format, args);
+ va_end (args);
+
+ g_output_stream_write_all (httpd->log, str->str, str->len, &written, NULL, NULL);
+}
+
static int
compare_strings (gconstpointer a, gconstpointer b)
{
@@ -154,6 +180,7 @@ do_get (OtTrivialHttpd *self,
struct stat stbuf;
g_autofree char *safepath = NULL;
+ httpd_log (self, "serving %s\n", path);
if (strstr (path, "../") != NULL)
{
soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
@@ -296,6 +323,16 @@ do_get (OtTrivialHttpd *self,
soup_message_set_status (msg, SOUP_STATUS_OK);
}
out:
+ {
+ guint status = 0;
+ g_autofree gchar *reason = NULL;
+
+ g_object_get (msg,
+ "status-code", &status,
+ "reason-phrase", &reason,
+ NULL);
+ httpd_log (self, " status: %s (%u)\n", reason, status);
+ }
return;
}
@@ -305,9 +342,6 @@ httpd_callback (SoupServer *server, SoupMessage *msg,
SoupClientContext *context, gpointer data)
{
OtTrivialHttpd *self = data;
- SoupMessageHeadersIter iter;
-
- soup_message_headers_iter_init (&iter, msg->request_headers);
if (msg->method == SOUP_METHOD_GET || msg->method == SOUP_METHOD_HEAD)
do_get (self, server, msg, path, context);
@@ -354,6 +388,37 @@ ostree_builtin_trivial_httpd (int argc, char **argv, GCancellable *cancellable,
app->root = g_file_new_for_path (dirpath);
+ if (opt_log)
+ {
+ GOutputStream *stream = NULL;
+
+ if (g_strcmp0 (opt_log, "-") == 0)
+ {
+ if (opt_daemonize)
+ {
+ ot_util_usage_error (context, "Cannot use --log-file=- and --daemonize at the same time", error);
+ goto out;
+ }
+ stream = G_OUTPUT_STREAM (g_unix_output_stream_new (STDOUT_FILENO, FALSE));
+ }
+ else
+ {
+ g_autoptr(GFile) log_file;
+ GFileOutputStream* log_stream;
+
+ log_file = g_file_new_for_path (opt_log);
+ log_stream = g_file_create (log_file,
+ G_FILE_CREATE_PRIVATE,
+ cancellable,
+ error);
+ if (!log_stream)
+ goto out;
+ stream = G_OUTPUT_STREAM (log_stream);
+ }
+
+ app->log = stream;
+ }
+
#if SOUP_CHECK_VERSION(2, 48, 0)
server = soup_server_new (SOUP_SERVER_SERVER_HEADER, "ostree-httpd ", NULL);
if (!soup_server_listen_all (server, opt_port, 0, error))
@@ -459,13 +524,17 @@ ostree_builtin_trivial_httpd (int argc, char **argv, GCancellable *cancellable,
goto out;
g_signal_connect (dirmon, "changed", G_CALLBACK (on_dir_changed), app);
}
-
+ {
+ g_autofree gchar *path = g_file_get_path (app->root);
+ httpd_log (app, "serving at root %s\n", path);
+ }
while (app->running)
g_main_context_iteration (NULL, TRUE);
-
+
ret = TRUE;
out:
g_clear_object (&app->root);
+ g_clear_object (&app->log);
if (context)
g_option_context_free (context);
return ret;
diff --git a/src/ostree/ot-dump.c b/src/ostree/ot-dump.c
index 670ccd6a..48b087af 100644
--- a/src/ostree/ot-dump.c
+++ b/src/ostree/ot-dump.c
@@ -118,8 +118,15 @@ dump_commit (GVariant *variant,
g_print ("Version: %s\n", version);
}
- g_print ("\n");
- dump_indented_lines (subject);
+ if (subject[0])
+ {
+ g_print ("\n");
+ dump_indented_lines (subject);
+ }
+ else
+ {
+ g_print ("(no subject)\n");
+ }
if (body[0])
{
diff --git a/src/ostree/ot-editor.c b/src/ostree/ot-editor.c
index 4c29c81e..66c6e7be 100644
--- a/src/ostree/ot-editor.c
+++ b/src/ostree/ot-editor.c
@@ -22,9 +22,9 @@
#include "config.h"
+#include "libglnx.h"
#include "ot-editor.h"
#include "libgsystem.h"
-#include "libglnx.h"
#include
#include
@@ -101,7 +101,8 @@ ot_editor_prompt (OstreeRepo *repo,
goto out;
}
- ret = gs_file_load_contents_utf8 (file, cancellable, error);
+ ret = glnx_file_get_contents_utf8_at (AT_FDCWD, gs_file_get_path_cached (file), NULL,
+ cancellable, error);
out:
if (file)
diff --git a/src/ostree/ot-main.c b/src/ostree/ot-main.c
index 93f841dc..18c13239 100644
--- a/src/ostree/ot-main.c
+++ b/src/ostree/ot-main.c
@@ -27,9 +27,9 @@
#include
#include
+#include "ot-main.h"
#include "ostree.h"
#include "ot-admin-functions.h"
-#include "ot-main.h"
#include "otutil.h"
static char *opt_repo;
diff --git a/src/ostree/ot-main.h b/src/ostree/ot-main.h
index 32620c52..3bb75243 100644
--- a/src/ostree/ot-main.h
+++ b/src/ostree/ot-main.h
@@ -22,8 +22,8 @@
#pragma once
-#include "ostree.h"
#include "libglnx.h"
+#include "ostree.h"
typedef enum {
OSTREE_BUILTIN_FLAG_NONE = 0,
diff --git a/tests/basic-test.sh b/tests/basic-test.sh
index 5519764a..003df893 100755
--- a/tests/basic-test.sh
+++ b/tests/basic-test.sh
@@ -19,7 +19,7 @@
set -euo pipefail
-echo "1..53"
+echo "1..57"
$OSTREE checkout test2 checkout-test2
echo "ok checkout"
@@ -103,6 +103,12 @@ $OSTREE commit -b test2-no-parent -s '' --parent=none $test_tmpdir/checkout-test
assert_streq $($OSTREE log test2-no-parent |grep '^commit' | wc -l) "1"
echo "ok commit no parent"
+cd ${test_tmpdir}
+empty_rev=$($OSTREE commit -b test2-no-subject -s '' --timestamp="2005-10-29 12:43:29 +0000" $test_tmpdir/checkout-test2-4)
+omitted_rev=$($OSTREE commit -b test2-no-subject-2 --timestamp="2005-10-29 12:43:29 +0000" $test_tmpdir/checkout-test2-4)
+assert_streq $empty_rev $omitted_rev
+echo "ok commit no subject"
+
cd ${test_tmpdir}
$OSTREE commit -b test2-custom-parent -s '' $test_tmpdir/checkout-test2-4
$OSTREE commit -b test2-custom-parent -s '' $test_tmpdir/checkout-test2-4
@@ -179,13 +185,31 @@ cd ${test_tmpdir}/checkout-test2-4
$OSTREE commit -b test2 -s "no xattrs" --no-xattrs
echo "ok commit with no xattrs"
+# NB: The + is optional, but we need to make sure we support it
cd ${test_tmpdir}
cat > test-statoverride.txt < test-skiplist.txt < cow-mtime
-assert_file_has_content cow-mtime 0
+assert_file_has_content cow-mtime 1
stat '--format=%Y' test2-checkout/baz/deeper > deeper-mtime
-assert_file_has_content deeper-mtime 0
+assert_file_has_content deeper-mtime 1
echo "ok content mtime"
cd ${test_tmpdir}
@@ -381,7 +405,7 @@ echo "ok commit of fifo was rejected"
cd ${test_tmpdir}
rm repo2 -rf
mkdir repo2
-${CMD_PREFIX} ostree --repo=repo2 init --mode=archive-z2
+${CMD_PREFIX} ostree --repo=repo2 init --mode=archive
${CMD_PREFIX} ostree --repo=repo2 pull-local repo
rm -rf test2-checkout
${CMD_PREFIX} ostree --repo=repo2 checkout -U --disable-cache test2 test2-checkout
@@ -397,6 +421,28 @@ assert_file_has_content test2-checkout/baz/cow moo
assert_has_dir repo2/uncompressed-objects-cache
echo "ok disable cache checkout"
+cd ${test_tmpdir}
+rm checkout-test2 -rf
+$OSTREE checkout test2 checkout-test2
+date > checkout-test2/date.txt
+rm repo/tmp/* -rf
+export TEST_BOOTID=3072029c-8b10-60d1-d31b-8422eeff9b42
+if env OSTREE_REPO_TEST_ERROR=pre-commit OSTREE_BOOTID=${TEST_BOOTID} \
+ $OSTREE commit -b test2 -s '' $test_tmpdir/checkout-test2 2>err.txt; then
+ assert_not_reached "Should have hit OSTREE_REPO_TEST_ERROR_PRE_COMMIT"
+fi
+assert_file_has_content err.txt OSTREE_REPO_TEST_ERROR_PRE_COMMIT
+found_staging=0
+for d in $(find repo/tmp/ -maxdepth 1 -type d); do
+ bn=$(basename $d)
+ if test ${bn##staging-} != ${bn}; then
+ assert_str_match "${bn}" "^staging-${TEST_BOOTID}-"
+ found_staging=1
+ fi
+done
+assert_streq "${found_staging}" 1
+echo "ok test error pre commit/bootid"
+
# Whiteouts
cd ${test_tmpdir}
mkdir -p overlay/baz/
@@ -468,5 +514,9 @@ cd ..
if cmp timestamp-{orig,new}.txt; then
assert_not_reached "failed to update mtime on repo"
fi
-
echo "ok mtime updated"
+
+cd ${test_tmpdir}
+$OSTREE init --mode=bare --repo=repo-extensions
+assert_has_dir repo-extensions/extensions
+echo "ok extensions dir"
diff --git a/tests/glib.supp b/tests/glib.supp
new file mode 100644
index 00000000..33449070
--- /dev/null
+++ b/tests/glib.supp
@@ -0,0 +1,535 @@
+# This GLib suppressions file is known to be used at least by:
+#
+# - rpm-software-management/libhif
+#
+# Please use the upstream verison in libhif for changes.
+{
+ gobject_init_1
+ Memcheck:Leak
+ ...
+ fun:gobject_init
+}
+{
+ g_type_register_static_1
+ Memcheck:Leak
+ ...
+ fun:g_type_register_static
+}
+{
+ g_type_register_fundamental
+ Memcheck:Leak
+ ...
+ fun:g_type_register_fundamental
+}
+{
+ g_type_init_with_debug_flags
+ Memcheck:Leak
+ ...
+ fun:g_type_init_with_debug_flags
+}
+{
+ g_type_class_ref_1
+ Memcheck:Leak
+ ...
+ fun:type_iface_vtable_base_init_Wm
+ ...
+ fun:g_type_class_ref
+}
+{
+ g_type_class_ref_2
+ Memcheck:Leak
+ ...
+ fun:type_class_init_Wm
+ ...
+ fun:g_type_class_ref
+}
+{
+ g_type_add_interface_static
+ Memcheck:Leak
+ ...
+ fun:g_type_add_interface_static
+}
+{
+ g_param_spec_internal
+ Memcheck:Leak
+ ...
+ fun:g_type_class_ref
+ fun:g_type_create_instance
+ fun:g_param_spec_internal
+}
+{
+ g_param_spec_enum
+ Memcheck:Leak
+ ...
+ fun:g_type_class_ref
+ fun:g_param_spec_enum
+}
+{
+ g_param_spec_flags
+ Memcheck:Leak
+ ...
+ fun:g_type_class_ref
+ fun:g_param_spec_flags
+}
+{
+ g_quark_from_static_string
+ Memcheck:Leak
+ ...
+ fun:g_quark_from_static_string
+}
+{
+ g_quark_from_string
+ Memcheck:Leak
+ ...
+ fun:g_quark_from_string
+}
+{
+ g_value_register_transform_func
+ Memcheck:Leak
+ ...
+ fun:g_value_register_transform_func
+}
+{
+ test_run_seed
+ Memcheck:Leak
+ ...
+ fun:g_rand_new_with_seed_array
+ fun:test_run_seed
+ ...
+ fun:g_test_run_suite
+}
+{
+ g_test_init
+ Memcheck:Leak
+ ...
+ fun:g_rand_new_with_seed_array
+ ...
+ fun:g_test_init
+}
+{
+ g_intern_static_string
+ Memcheck:Leak
+ ...
+ fun:g_intern_static_string
+}
+{
+ g_main_context_push_thread_default
+ Memcheck:Leak
+ ...
+ fun:g_queue_new
+ fun:g_main_context_push_thread_default
+}
+{
+ g_main_context_push_thread_default_inlined
+ Memcheck:Leak
+ ...
+ fun:g_slice_alloc0
+ fun:g_main_context_push_thread_default
+}
+{
+ g_dbus_error_register_error
+ Memcheck:Leak
+ ...
+ fun:g_dbus_error_register_error
+}
+{
+ g_param_spec_pool_insert
+ Memcheck:Leak
+ ...
+ fun:g_param_spec_pool_insert
+}
+{
+ g_main_context_default
+ Memcheck:Leak
+ ...
+ fun:g_main_context_default
+}
+{
+ g_main_context_check
+ Memcheck:Leak
+ ...
+ fun:g_ptr_array_add
+ fun:g_main_context_check
+}
+{
+ g_test_run_suite
+ Memcheck:Leak
+ ...
+ fun:g_slist_copy
+ fun:g_test_run_suite_internal
+ fun:g_test_run_suite
+}
+{
+ g_dbus_interface_info_cache_build
+ Memcheck:Leak
+ ...
+ fun:g_dbus_interface_info_cache_build
+}
+{
+ g_cancellable_push_current
+ Memcheck:Leak
+ ...
+ fun:thread_memory_from_self
+ ...
+ fun:g_cancellable_push_current
+}
+{
+ _g_io_module_get_default
+ Memcheck:Leak
+ ...
+ fun:g_io_module_new
+ fun:g_io_modules_scan_all_in_directory_with_scope
+ fun:_g_io_modules_ensure_loaded
+ fun:_g_io_module_get_default
+}
+{
+ g_io_scheduler_push_job
+ Memcheck:Leak
+ ...
+ fun:init_scheduler
+ fun:g_once_impl
+ fun:g_io_scheduler_push_job
+}
+{
+ g_io_scheduler_push_job_2
+ Memcheck:Leak
+ ...
+ fun:g_system_thread_new
+ ...
+ fun:g_io_scheduler_push_job
+}
+{
+ g_bus_get_sync__available_connections
+ Memcheck:Leak
+ ...
+ fun:g_hash_table_new
+ fun:initable_init
+ fun:g_initable_init
+ fun:g_bus_get_sync
+}
+{
+ g_socket_connection_factory_register_type
+ Memcheck:Leak
+ ...
+ fun:g_socket_connection_factory_register_type
+}
+{
+ g_test_add_vtable
+ Memcheck:Leak
+ ...
+ fun:g_test_add_vtable
+}
+{
+ g_mutex_lock
+ Memcheck:Leak
+ ...
+ fun:g_mutex_impl_new
+ fun:g_mutex_get_impl
+ fun:g_mutex_lock
+}
+{
+ g_thread_self
+ Memcheck:Leak
+ ...
+ fun:g_thread_self
+}
+{
+ g_rec_mutex_lock
+ Memcheck:Leak
+ ...
+ fun:g_rec_mutex_impl_new
+ fun:g_rec_mutex_get_impl
+ fun:g_rec_mutex_lock
+}
+{
+ test_case_run
+ Memcheck:Leak
+ ...
+ fun:g_malloc0
+ fun:test_case_run
+ ...
+ fun:g_test_run_suite
+}
+{
+ g_get_charset
+ Memcheck:Leak
+ ...
+ fun:g_get_charset
+}
+{
+ g_test_run_suite__timer_new
+ Memcheck:Leak
+ ...
+ fun:g_timer_new
+ fun:test_case_run
+ ...
+ fun:g_test_run_suite
+}
+{
+ g_test_run_suite__timer_new2
+ Memcheck:Leak
+ ...
+ fun:g_timer_new
+ fun:test_case_run_suite_internal
+ ...
+ fun:g_test_run_suite
+}
+{
+ g_test_run_suite__strconcat
+ Memcheck:Leak
+ ...
+ fun:g_strconcat
+ fun:test_case_run
+ ...
+ fun:g_test_run_suite
+ fun:g_test_run
+}
+{
+ g_type_interface_add_prerequisite
+ Memcheck:Leak
+ ...
+ fun:g_type_interface_add_prerequisite
+}
+{
+
+ Memcheck:Leak
+ ...
+ fun:g_slist_copy
+ fun:g_test_run_suite_internal
+ ...
+ fun:g_test_run_suite
+}
+{
+ g_set_prgname
+ Memcheck:Leak
+ ...
+ fun:g_set_prgname
+}
+{
+ g_test_run_suite__strconcat_2
+ Memcheck:Leak
+ ...
+ fun:g_strconcat
+ fun:g_test_run_suite_internal
+}
+{
+ g_test_run_suite__strdup
+ Memcheck:Leak
+ ...
+ fun:g_strdup
+ fun:g_test_run_suite_internal
+}
+{
+ g_private_get
+ Memcheck:Leak
+ ...
+ fun:g_private_get
+}
+{
+ g_private_set
+ Memcheck:Leak
+ ...
+ fun:g_private_set
+}
+{
+ g_static_mutex_get_mutex_impl
+ Memcheck:Leak
+ ...
+ fun:g_static_mutex_get_mutex_impl
+}
+{
+ g_variant_type_info_unref
+ Memcheck:Leak
+ ...
+ fun:g_hash_table_remove
+ fun:g_variant_type_info_unref
+}
+{
+ g_rw_lock_reader_lock
+ Memcheck:Leak
+ ...
+ fun:g_rw_lock_impl_new
+ fun:g_rw_lock_get_impl
+ fun:g_rw_lock_reader_lock
+}
+{
+ g_child_watch_finalize__rt_sigaction
+ Memcheck:Param
+ rt_sigaction(act->sa_flags)
+ fun:__libc_sigaction
+ ...
+ fun:g_child_watch_finalize
+}
+{
+ g_dbus_worker_new
+ Memcheck:Leak
+ fun:calloc
+ ...
+ fun:_g_dbus_worker_new
+}
+{
+ gdbus_shared_thread_func
+ Memcheck:Leak
+ match-leak-kinds: definite
+ ...
+ fun:g_malloc
+ ...
+ fun:gdbus_shared_thread_func
+}
+{
+ g_task_start_task_thread
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ fun:g_slice_alloc
+ fun:g_slice_alloc0
+ ...
+ fun:g_thread_pool_push
+ fun:g_task_start_task_thread
+}
+{
+ g_get_language_names
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:calloc
+ fun:g_malloc0
+ fun:g_get_language_names
+}
+{
+ g_get_filename_charsets
+ Memcheck:Leak
+ match-leak-kinds: definite
+ ...
+ fun:g_get_filename_charsets
+ fun:g_filename_display_name
+}
+{
+ g_main_current_source
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ ...
+ fun:g_main_current_source
+ fun:g_task_return
+ fun:g_task_thread_pool_thread
+}
+{
+ g_once_init_enter
+ Memcheck:Leak
+ match-leak-kinds: definite
+ ...
+ fun:g_once_init_enter
+}
+{
+ g_child_watch_source_new
+ Memcheck:Leak
+ match-leak-kinds: definite
+ ...
+ fun:g_thread_new
+ ...
+ fun:g_child_watch_source_new
+}
+{
+ continue_writing_in_idle_cb
+ Memcheck:Leak
+ match-leak-kinds: definite
+ ...
+ fun:g_task_new
+ ...
+ fun:continue_writing_in_idle_cb
+ fun:g_main_context_dispatch
+}
+{
+ g_main_current_source
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ ...
+ fun:g_main_current_source
+}
+{
+ g_thread_pool_push
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ ...
+ fun:g_thread_pool_push
+}
+{
+ leak_test_dbus_dispose
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ ...
+ fun:g_main_loop_run
+ fun:g_test_dbus_down
+}
+{
+ leak_test_dbus_down
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:calloc
+ fun:g_malloc0
+ fun:g_main_loop_new
+ fun:g_test_dbus_down
+}
+{
+ leak_socket_client_connect
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ fun:g_slice_alloc
+ fun:g_slice_alloc0
+ fun:g_socket_client_connect_async
+ fun:g_socket_client_connect_to_uri_async
+}
+{
+ leak_signal_handlers_disconnect_matched
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:calloc
+ fun:g_malloc0
+ ...
+ fun:g_slice_alloc
+ ...
+ fun:g_signal_handlers_disconnect_matched
+}
+{
+ g_tls_connection_gnutls_init_priorities
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ fun:g_strdup
+ fun:g_tls_connection_gnutls_init_priorities
+}
+{
+ g_tls_connection_gnutls_heisenbug_likely_same_as_above
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ fun:g_strdup
+ ...
+ fun:g_tls_client_connection_new
+}
+{
+ g_unix_signal_add_full
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ ...
+ fun:g_thread_new
+ ...
+ fun:g_unix_signal_add_full
+}
+{
+ glib_worker_1
+ Memcheck:Leak
+ ...
+ fun:glib_worker_main
+}
diff --git a/tests/libostreetest.c b/tests/libostreetest.c
index 58283368..81f743e9 100644
--- a/tests/libostreetest.c
+++ b/tests/libostreetest.c
@@ -28,11 +28,11 @@
/* This function hovers in a quantum superposition of horrifying and
* beautiful. Future generations may interpret it as modern art.
*/
-static gboolean
-run_libtest (const char *cmd, GError **error)
+gboolean
+ot_test_run_libtest (const char *cmd, GError **error)
{
gboolean ret = FALSE;
- const char *builddir = g_getenv ("G_TEST_BUILDDIR");
+ const char *srcdir = g_getenv ("G_TEST_SRCDIR");
int estatus;
g_autoptr(GPtrArray) argv = g_ptr_array_new ();
g_autoptr(GString) cmdstr = g_string_new ("");
@@ -40,8 +40,8 @@ run_libtest (const char *cmd, GError **error)
g_ptr_array_add (argv, "bash");
g_ptr_array_add (argv, "-c");
- g_string_append (cmdstr, ". ");
- g_string_append (cmdstr, builddir);
+ g_string_append (cmdstr, "set -xeuo pipefail; . ");
+ g_string_append (cmdstr, srcdir);
g_string_append (cmdstr, "/tests/libtest.sh; ");
g_string_append (cmdstr, cmd);
@@ -68,7 +68,7 @@ ot_test_setup_repo (GCancellable *cancellable,
g_autoptr(GFile) repo_path = g_file_new_for_path ("repo");
glnx_unref_object OstreeRepo* ret_repo = NULL;
- if (!run_libtest ("setup_test_repository", error))
+ if (!ot_test_run_libtest ("setup_test_repository archive-z2", error))
goto out;
ret_repo = ostree_repo_new (repo_path);
@@ -91,7 +91,7 @@ ot_test_setup_sysroot (GCancellable *cancellable,
g_autoptr(GFile) sysroot_path = g_file_new_for_path ("sysroot");
glnx_unref_object OstreeSysroot *ret_sysroot = NULL;
- if (!run_libtest ("setup_os_repository \"archive-z2\" \"syslinux\"", error))
+ if (!ot_test_run_libtest ("setup_os_repository \"archive-z2\" \"syslinux\"", error))
goto out;
ret_sysroot = ostree_sysroot_new (sysroot_path);
diff --git a/tests/libostreetest.h b/tests/libostreetest.h
index eb9bb0b2..f777545e 100644
--- a/tests/libostreetest.h
+++ b/tests/libostreetest.h
@@ -27,6 +27,7 @@
G_BEGIN_DECLS
+gboolean ot_test_run_libtest (const char *cmd, GError **error);
OstreeRepo *ot_test_setup_repo (GCancellable *cancellable,
GError **error);
diff --git a/tests/libtest.sh b/tests/libtest.sh
index 572f023c..2d064299 100755
--- a/tests/libtest.sh
+++ b/tests/libtest.sh
@@ -69,6 +69,7 @@ export TEST_GPG_KEYID_3="DF444D67"
# this by copying locally.
echo "Copying gpghome to ${test_tmpdir}"
cp -a "${test_srcdir}/gpghome" ${test_tmpdir}
+chmod -R u+w "${test_tmpdir}"
export TEST_GPG_KEYHOME=${test_tmpdir}/gpghome
export OSTREE_GPG_HOME=${test_tmpdir}/gpghome/trusted
@@ -77,15 +78,28 @@ if test -n "${OT_TESTS_DEBUG:-}"; then
fi
if test -n "${OT_TESTS_VALGRIND:-}"; then
- CMD_PREFIX="env G_SLICE=always-malloc valgrind -q --leak-check=full --num-callers=30 --suppressions=${test_srcdir}/ostree-valgrind.supp"
+ CMD_PREFIX="env G_SLICE=always-malloc OSTREE_SUPPRESS_SYNCFS=1 valgrind -q --error-exitcode=1 --leak-check=full --num-callers=30 --suppressions=${test_srcdir}/glib.supp --suppressions=${test_srcdir}/ostree.supp"
else
- CMD_PREFIX="env LD_PRELOAD=${test_builddir}/libreaddir-rand.so"
+ # In some cases the LD_PRELOAD may cause obscure problems,
+ # e.g. right now it breaks for me with -fsanitize=address, so
+ # let's allow users to skip it.
+ if test -z "${OT_SKIP_READDIR_RAND:-}"; then
+ CMD_PREFIX="env LD_PRELOAD=${test_builddir}/libreaddir-rand.so"
+ else
+ CMD_PREFIX=""
+ fi
fi
assert_streq () {
test "$1" = "$2" || (echo 1>&2 "$1 != $2"; exit 1)
}
+assert_str_match () {
+ if ! echo "$1" | grep -E -q "$2"; then
+ (echo 1>&2 "$1 does not match regexp $2"; exit 1)
+ fi
+}
+
assert_not_streq () {
(! test "$1" = "$2") || (echo 1>&2 "$1 == $2"; exit 1)
}
@@ -100,13 +114,17 @@ assert_has_dir () {
assert_not_has_file () {
if test -f "$1"; then
- echo 1>&2 "File '$1' exists"; exit 1
+ sed -e 's/^/# /' < "$1" >&2
+ echo 1>&2 "File '$1' exists"
+ exit 1
fi
}
assert_not_file_has_content () {
if grep -q -e "$2" "$1"; then
- echo 1>&2 "File '$1' incorrectly matches regexp '$2'"; exit 1
+ sed -e 's/^/# /' < "$1" >&2
+ echo 1>&2 "File '$1' incorrectly matches regexp '$2'"
+ exit 1
fi
}
@@ -118,13 +136,38 @@ assert_not_has_dir () {
assert_file_has_content () {
if ! grep -q -e "$2" "$1"; then
- echo 1>&2 "File '$1' doesn't match regexp '$2'"; exit 1
+ sed -e 's/^/# /' < "$1" >&2
+ echo 1>&2 "File '$1' doesn't match regexp '$2'"
+ exit 1
+ fi
+}
+
+assert_symlink_has_content () {
+ if ! test -L "$1"; then
+ echo 1>&2 "File '$1' is not a symbolic link"
+ exit 1
+ fi
+ if ! readlink "$1" | grep -q -e "$2"; then
+ sed -e 's/^/# /' < "$1" >&2
+ echo 1>&2 "Symbolic link '$1' doesn't match regexp '$2'"
+ exit 1
fi
}
assert_file_empty() {
if test -s "$1"; then
- echo 1>&2 "File '$1' is not empty"; exit 1
+ sed -e 's/^/# /' < "$1" >&2
+ echo 1>&2 "File '$1' is not empty"
+ exit 1
+ fi
+}
+
+assert_files_hardlinked() {
+ f1=$(stat -c %i $1)
+ f2=$(stat -c %i $2)
+ if [ "$f1" != "$f2" ]; then
+ echo 1>&2 "Files '$1' and '$2' are not hardlinked"
+ exit 1
fi
}
@@ -370,6 +413,11 @@ skip_without_fuse () {
exit 0
fi
+ if ! capsh --print | grep -q 'Bounding set.*[^a-z]cap_sys_admin'; then
+ echo "1..0 # SKIP No cap_sys_admin in bounding set, can't use FUSE"
+ exit 0
+ fi
+
if ! [ -w /dev/fuse ]; then
echo "1..0 # SKIP no write access to /dev/fuse"
exit 0
diff --git a/tests/ostree-valgrind.supp b/tests/ostree-valgrind.supp
deleted file mode 100644
index 00efd87b..00000000
--- a/tests/ostree-valgrind.supp
+++ /dev/null
@@ -1,199 +0,0 @@
-{
- g_type_init_with_debug_flags calloc
- Memcheck:Leak
- fun:calloc
- ...
- fun:g_type_init_with_debug_flags
- ...
-}
-
-{
- g_type_add_interface_static malloc
- Memcheck:Leak
- fun:malloc
- ...
- fun:g_type_add_interface_static
- ...
-}
-
-{
- g_type_add_interface_dynamic malloc
- Memcheck:Leak
- fun:malloc
- ...
- fun:g_type_add_interface_dynamic
- ...
-}
-
-{
- g_type_class_ref malloc
- Memcheck:Leak
- fun:malloc
- ...
- fun:g_type_class_ref
- ...
-}
-
-{
- g_type_register_dynamic malloc
- Memcheck:Leak
- fun:malloc
- ...
- fun:g_type_register_dynamic
- ...
-}
-
-{
- g_type_init_with_debug_flags malloc
- Memcheck:Leak
- fun:malloc
- ...
- fun:g_type_init_with_debug_flags
- ...
-}
-
-{
- g_type_init_with_debug_flags realloc
- Memcheck:Leak
- fun:realloc
- ...
- fun:g_type_init_with_debug_flags
- ...
-}
-
-{
- g_test_add_vtable malloc
- Memcheck:Leak
- fun:malloc
- ...
- fun:g_test_add_vtable
- ...
-}
-
-{
- g_test_init
- Memcheck:Leak
- fun:malloc
- ...
- fun:g_test_init
- ...
-}
-
-{
- g_type_register_static malloc
- Memcheck:Leak
- fun:malloc
- ...
- fun:g_type_register_static
- ...
-}
-
-{
- g_type_register_static realloc
- Memcheck:Leak
- fun:realloc
- ...
- fun:g_type_register_static
- ...
-}
-
-{
- g_type_register_fundamental never freed
- Memcheck:Leak
- fun:malloc
- ...
- fun:g_type_register_fundamental
- ...
-}
-
-{
- g_type_class_ref never finalized
- Memcheck:Leak
- fun:calloc
- ...
- fun:g_type_class_ref
- ...
-}
-
-{
- DBusGValue qdata
- Memcheck:Leak
- fun:realloc
- fun:g_realloc
- fun:g_type_set_qdata
- fun:_dbus_g_value_types_init
- ...
-}
-
-{
- gettext conditional jump
- Memcheck:Cond
- fun:__GI___strcasecmp_l
- fun:__gconv_open
- fun:_nl_find_msg
- fun:__dcigettext
- ...
-}
-
-{
- gettext uninitialized value
- Memcheck:Value8
- fun:__GI___strcasecmp_l
- fun:__gconv_open
- fun:_nl_find_msg
- fun:__dcigettext
- ...
-}
-
-{
- font config invalid reads
- Memcheck:Addr4
- ...
- fun:FcConfigParseAndLoad
- ...
-}
-
-{
- dynamic loader conditional jump
- Memcheck:Cond
- fun:index
- fun:expand_dynamic_string_token
- fun:_dl_map_object
- fun:map_doit
- fun:_dl_catch_error
- fun:do_preload
- fun:dl_main
- ...
-}
-
-{
- g_vfs_get_local
- Memcheck:Leak
- ...
- fun:g_vfs_get_local
- ...
-}
-
-{
- _g_io_modules_ensure_loaded
- Memcheck:Leak
- ...
- fun:_g_io_modules_ensure_loaded
- ...
-}
-
-{
- _g_io_module_get_default
- Memcheck:Leak
- ...
- fun:_g_io_module_get_default
- ...
-}
-
-{
- _dl_allocate_tls
- Memcheck:Leak
- ...
- fun:_dl_allocate_tls
- ...
-}
diff --git a/tests/ostree.supp b/tests/ostree.supp
new file mode 100644
index 00000000..b81ea51b
--- /dev/null
+++ b/tests/ostree.supp
@@ -0,0 +1 @@
+# Use this to suppress "possibly lost" for global statics
diff --git a/tests/readdir-rand.c b/tests/readdir-rand.c
index 527d4a3a..afef387c 100644
--- a/tests/readdir-rand.c
+++ b/tests/readdir-rand.c
@@ -97,7 +97,6 @@ readdir (DIR *dirp)
while (cache_another)
{
DirEntries *de;
- GSList *l;
errno = 0;
ret = real_readdir (dirp);
@@ -110,12 +109,12 @@ readdir (DIR *dirp)
{
if (g_random_boolean ())
{
+ struct dirent *copy;
if (!de)
{
de = dir_entries_new ();
g_hash_table_insert (direntcache, dirp, de);
}
- struct dirent *copy;
copy = g_memdup (ret, sizeof (struct dirent));
g_ptr_array_add (de->entries, copy);
}
diff --git a/tests/test-admin-deploy-bootid-gc.sh b/tests/test-admin-deploy-bootid-gc.sh
new file mode 100755
index 00000000..ba16f336
--- /dev/null
+++ b/tests/test-admin-deploy-bootid-gc.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 Colin Walters
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+set -euo pipefail
+
+. $(dirname $0)/libtest.sh
+
+# Exports OSTREE_SYSROOT so --sysroot not needed.
+setup_os_repository "archive-z2" "syslinux"
+
+echo "1..1"
+
+${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime
+rev=$(${CMD_PREFIX} ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime)
+export rev
+${CMD_PREFIX} ostree admin deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime
+os_repository_new_commit
+
+rm sysroot/ostree/repo/tmp/* -rf
+export TEST_BOOTID=4072029c-8b10-60d1-d31b-8422eeff9b42
+if env OSTREE_REPO_TEST_ERROR=pre-commit OSTREE_BOOTID=${TEST_BOOTID} \
+ ${CMD_PREFIX} ostree admin deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime 2>err.txt; then
+ assert_not_reached "Should have hit OSTREE_REPO_TEST_ERROR_PRE_COMMIT"
+fi
+stagepath=$(ls -d sysroot/ostree/repo/tmp/staging-${TEST_BOOTID}-*)
+assert_has_dir "${stagepath}"
+
+# We have an older failed stage, now use a new boot id
+
+export NEW_TEST_BOOTID=5072029c-8b10-60d1-d31b-8422eeff9b42
+if env OSTREE_REPO_TEST_ERROR=pre-commit OSTREE_BOOTID=${NEW_TEST_BOOTID} \
+ ${CMD_PREFIX} ostree admin deploy --karg=root=LABEL=FOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime 2>err.txt; then
+ assert_not_reached "Should have hit OSTREE_REPO_TEST_ERROR_PRE_COMMIT"
+fi
+newstagepath=$(ls -d sysroot/ostree/repo/tmp/staging-${NEW_TEST_BOOTID}-*)
+assert_has_dir "${newstagepath}"
+env OSTREE_BOOTID=${NEW_TEST_BOOTID} ${CMD_PREFIX} ostree admin deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime
+newstagepath=$(ls -d sysroot/ostree/repo/tmp/staging-${NEW_TEST_BOOTID}-*)
+assert_not_has_dir "${stagepath}"
+assert_not_has_dir "${newstagepath}"
+
+echo "ok admin bootid GC"
diff --git a/tests/test-basic-c.c b/tests/test-basic-c.c
index d5dcc811..447c46ea 100644
--- a/tests/test-basic-c.c
+++ b/tests/test-basic-c.c
@@ -34,6 +34,143 @@ test_repo_is_not_system (gconstpointer data)
g_assert (!ostree_repo_is_system (repo));
}
+static GBytes *
+input_stream_to_bytes (GInputStream *input)
+{
+ g_autoptr(GOutputStream) mem_out_stream = NULL;
+ g_autoptr(GError) error = NULL;
+
+ if (input == NULL)
+ return g_bytes_new (NULL, 0);
+
+ mem_out_stream = g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
+ g_output_stream_splice (mem_out_stream,
+ input,
+ G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+
+ return g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (mem_out_stream));
+}
+
+static void
+test_raw_file_to_archive_z2_stream (gconstpointer data)
+{
+ OstreeRepo *repo = OSTREE_REPO (data);
+ g_autofree gchar *commit_checksum = NULL;
+ g_autoptr(GHashTable) reachable = NULL;
+ g_autoptr(GError) error = NULL;
+ /* branch name of the test repository, see setup_test_repository in libtest.sh */
+ const gchar *rev = "test2";
+ GHashTableIter iter;
+ GVariant *serialized_object;
+ guint checks = 0;
+
+ ostree_repo_resolve_rev (repo,
+ rev,
+ FALSE,
+ &commit_checksum,
+ &error);
+ g_assert_no_error (error);
+ reachable = ostree_repo_traverse_new_reachable ();
+ ostree_repo_traverse_commit (repo,
+ commit_checksum,
+ -1,
+ &reachable,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_hash_table_iter_init (&iter, reachable);
+ while (g_hash_table_iter_next (&iter, (gpointer*)&serialized_object, NULL))
+ {
+ const gchar *object_checksum;
+ OstreeObjectType object_type;
+ g_autoptr(GInputStream) input = NULL;
+ g_autoptr(GFileInfo) info = NULL;
+ g_autoptr(GVariant) xattrs = NULL;
+ g_autoptr(GBytes) input_bytes = NULL;
+ g_autoptr(GInputStream) mem_input = NULL;
+ g_autoptr(GInputStream) zlib_stream = NULL;
+ g_autoptr(GBytes) zlib_bytes = NULL;
+ g_autoptr(GInputStream) mem_zlib = NULL;
+ g_autoptr(GInputStream) input2 = NULL;
+ g_autoptr(GFileInfo) info2 = NULL;
+ g_autoptr(GVariant) xattrs2 = NULL;
+ g_autoptr(GBytes) input2_bytes = NULL;
+
+ ostree_object_name_deserialize (serialized_object, &object_checksum, &object_type);
+ if (object_type != OSTREE_OBJECT_TYPE_FILE)
+ continue;
+
+ ostree_repo_load_file (repo,
+ object_checksum,
+ &input,
+ &info,
+ &xattrs,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+
+ input_bytes = input_stream_to_bytes (input);
+ /* This is to simulate NULL input received from
+ * ostree_repo_load_file. Instead of creating the mem_input
+ * variable, I could also rewind the input stream and pass it to
+ * the function below, but this would assume that the input
+ * stream implements either the GSeekable or
+ * GFileDescriptorBased interface. */
+ if (input != NULL)
+ mem_input = g_memory_input_stream_new_from_bytes (input_bytes);
+ ostree_raw_file_to_archive_z2_stream (mem_input,
+ info,
+ xattrs,
+ &zlib_stream,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+
+ zlib_bytes = input_stream_to_bytes (zlib_stream);
+ mem_zlib = g_memory_input_stream_new_from_bytes (zlib_bytes);
+ ostree_content_stream_parse (FALSE,
+ mem_zlib,
+ g_bytes_get_size (zlib_bytes),
+ FALSE,
+ &input2,
+ &info2,
+ &xattrs2,
+ NULL,
+ &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
+ g_clear_error (&error);
+
+ g_seekable_seek (G_SEEKABLE (mem_zlib),
+ 0,
+ G_SEEK_SET,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+
+ ostree_content_stream_parse (TRUE,
+ mem_zlib,
+ g_bytes_get_size (zlib_bytes),
+ FALSE,
+ &input2,
+ &info2,
+ &xattrs2,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+
+ input2_bytes = input_stream_to_bytes (input2);
+ g_assert_true (g_bytes_equal (input_bytes, input2_bytes));
+ g_assert_true (g_variant_equal (xattrs, xattrs2));
+ /* TODO: Not sure how to compare fileinfos */
+ ++checks;
+ }
+ /* to make sure we really tested the function */
+ g_assert_cmpint (checks, >, 0);
+}
+
int main (int argc, char **argv)
{
g_autoptr(GError) error = NULL;
@@ -46,6 +183,7 @@ int main (int argc, char **argv)
goto out;
g_test_add_data_func ("/repo-not-system", repo, test_repo_is_not_system);
+ g_test_add_data_func ("/raw-file-to-archive-z2-stream", repo, test_raw_file_to_archive_z2_stream);
return g_test_run();
out:
diff --git a/tests/test-checksum.c b/tests/test-checksum.c
index 25d3c37e..e7fd7be4 100644
--- a/tests/test-checksum.c
+++ b/tests/test-checksum.c
@@ -20,6 +20,7 @@
#include "config.h"
+#include "libglnx.h"
#include "libgsystem.h"
#include
#include
diff --git a/tests/test-export.sh b/tests/test-export.sh
index 1e81a2e9..856c4073 100755
--- a/tests/test-export.sh
+++ b/tests/test-export.sh
@@ -23,7 +23,7 @@ set -euo pipefail
setup_test_repository "archive-z2"
-echo '1..2'
+echo '1..5'
$OSTREE checkout test2 test2-co
$OSTREE commit --no-xattrs -b test2-noxattrs -s "test2 without xattrs" --tree=dir=test2-co
@@ -35,10 +35,39 @@ mkdir t
(cd t && tar xf ../test2.tar)
${CMD_PREFIX} ostree --repo=repo diff --no-xattrs test2-noxattrs ./t > diff.txt
assert_file_empty diff.txt
-rm test2.tar diff.txt t -rf
echo 'ok export gnutar diff (no xattrs)'
+cd ${test_tmpdir}
+${OSTREE} 'export' test2-noxattrs --subpath=baz -o test2-subpath.tar
+mkdir t2
+(cd t2 && tar xf ../test2-subpath.tar)
+${CMD_PREFIX} ostree --repo=repo diff --no-xattrs ./t2 ./t/baz > diff.txt
+assert_file_empty diff.txt
+
+echo 'ok export --subpath gnutar diff (no xattrs)'
+
+cd ${test_tmpdir}
+${OSTREE} 'export' test2-noxattrs --prefix=the-prefix/ -o test2-prefix.tar
+mkdir t3
+(cd t3 && tar xf ../test2-prefix.tar)
+${CMD_PREFIX} ostree --repo=repo diff --no-xattrs test2-noxattrs ./t3/the-prefix > diff.txt
+assert_file_empty diff.txt
+
+echo 'ok export --prefix gnutar diff (no xattrs)'
+
+cd ${test_tmpdir}
+${OSTREE} 'export' test2-noxattrs --subpath=baz --prefix=the-prefix/ -o test2-prefix-subpath.tar
+mkdir t4
+(cd t4 && tar xf ../test2-prefix-subpath.tar)
+${CMD_PREFIX} ostree --repo=repo diff --no-xattrs ./t4/the-prefix ./t/baz > diff.txt
+${CMD_PREFIX} ostree --repo=repo diff --no-xattrs test2-noxattrs ./t3/the-prefix > diff.txt
+assert_file_empty diff.txt
+
+echo 'ok export --prefix --subpath gnutar diff (no xattrs)'
+
+rm test2.tar test2-subpath.tar diff.txt t t2 t3 t4 -rf
+
cd ${test_tmpdir}
${OSTREE} 'export' test2 -o test2.tar
${OSTREE} commit -b test2-from-tar -s 'Import from tar' --tree=tar=test2.tar
diff --git a/tests/test-libarchive-import.c b/tests/test-libarchive-import.c
index 877fa77c..aaa3c37b 100644
--- a/tests/test-libarchive-import.c
+++ b/tests/test-libarchive-import.c
@@ -42,6 +42,8 @@ test_data_init (TestData *td)
GError *error = NULL;
struct archive *a = archive_write_new ();
struct archive_entry *ae;
+ uid_t uid = getuid ();
+ gid_t gid = getgid ();
td->tmpd = g_mkdtemp (g_strdup ("/var/tmp/test-libarchive-import-XXXXXX"));
g_assert_cmpint (0, ==, chdir (td->tmpd));
@@ -59,12 +61,16 @@ test_data_init (TestData *td)
ae = archive_entry_new ();
archive_entry_set_pathname (ae, "/");
archive_entry_set_mode (ae, S_IFDIR | 0755);
+ archive_entry_set_uid (ae, uid);
+ archive_entry_set_gid (ae, gid);
g_assert_cmpint (0, ==, archive_write_header (a, ae));
archive_entry_free (ae);
ae = archive_entry_new ();
archive_entry_set_pathname (ae, "/file");
archive_entry_set_mode (ae, S_IFREG | 0777);
+ archive_entry_set_uid (ae, uid);
+ archive_entry_set_gid (ae, gid);
archive_entry_set_size (ae, 4);
g_assert_cmpint (0, ==, archive_write_header (a, ae));
g_assert_cmpint (4, ==, archive_write_data (a, "foo\n", 4));
@@ -73,6 +79,8 @@ test_data_init (TestData *td)
ae = archive_entry_new ();
archive_entry_set_pathname (ae, "/devnull");
archive_entry_set_mode (ae, S_IFCHR | 0777);
+ archive_entry_set_uid (ae, uid);
+ archive_entry_set_gid (ae, gid);
archive_entry_set_devmajor (ae, 1);
archive_entry_set_devminor (ae, 3);
g_assert_cmpint (0, ==, archive_write_header (a, ae));
@@ -81,6 +89,26 @@ test_data_init (TestData *td)
ae = archive_entry_new ();
archive_entry_set_pathname (ae, "/anotherfile");
archive_entry_set_mode (ae, S_IFREG | 0777);
+ archive_entry_set_uid (ae, uid);
+ archive_entry_set_gid (ae, gid);
+ archive_entry_set_size (ae, 4);
+ g_assert_cmpint (0, ==, archive_write_header (a, ae));
+ g_assert_cmpint (4, ==, archive_write_data (a, "bar\n", 4));
+ archive_entry_free (ae);
+
+ ae = archive_entry_new ();
+ archive_entry_set_pathname (ae, "/etc");
+ archive_entry_set_mode (ae, S_IFDIR | 0755);
+ archive_entry_set_uid (ae, uid);
+ archive_entry_set_gid (ae, gid);
+ g_assert_cmpint (0, ==, archive_write_header (a, ae));
+ archive_entry_free (ae);
+
+ ae = archive_entry_new ();
+ archive_entry_set_pathname (ae, "/etc/file");
+ archive_entry_set_mode (ae, S_IFREG | 0777);
+ archive_entry_set_uid (ae, uid);
+ archive_entry_set_gid (ae, gid);
archive_entry_set_size (ae, 4);
g_assert_cmpint (0, ==, archive_write_header (a, ae));
g_assert_cmpint (4, ==, archive_write_data (a, "bar\n", 4));
@@ -123,6 +151,15 @@ spawn_cmdline (const char *cmd, GError **error)
return TRUE;
}
+static void
+test_archive_setup (int fd, struct archive *a)
+{
+ g_assert_cmpint (0, ==, lseek (fd, 0, SEEK_SET));
+ g_assert_cmpint (0, ==, archive_read_support_format_all (a));
+ g_assert_cmpint (0, ==, archive_read_support_filter_all (a));
+ g_assert_cmpint (0, ==, archive_read_open_fd (a, fd, 8192));
+}
+
static void
test_libarchive_noautocreate_empty (gconstpointer data)
{
@@ -132,10 +169,7 @@ test_libarchive_noautocreate_empty (gconstpointer data)
OstreeRepoImportArchiveOptions opts = { 0, };
glnx_unref_object OstreeMutableTree *mtree = ostree_mutable_tree_new ();
- g_assert_cmpint (0, ==, lseek (td->fd_empty, 0, SEEK_SET));
- g_assert_cmpint (0, ==, archive_read_support_format_all (a));
- g_assert_cmpint (0, ==, archive_read_support_filter_all (a));
- g_assert_cmpint (0, ==, archive_read_open_fd (a, td->fd_empty, 8192));
+ test_archive_setup (td->fd_empty, a);
(void)ostree_repo_import_archive_to_mtree (td->repo, &opts, a, mtree, NULL, NULL, &error);
g_assert_no_error (error);
@@ -153,10 +187,7 @@ test_libarchive_autocreate_empty (gconstpointer data)
opts.autocreate_parents = 1;
- g_assert_cmpint (0, ==, lseek (td->fd_empty, 0, SEEK_SET));
- g_assert_cmpint (0, ==, archive_read_support_format_all (a));
- g_assert_cmpint (0, ==, archive_read_support_filter_all (a));
- g_assert_cmpint (0, ==, archive_read_open_fd (a, td->fd_empty, 8192));
+ test_archive_setup (td->fd_empty, a);
(void)ostree_repo_import_archive_to_mtree (td->repo, &opts, a, mtree, NULL, NULL, &error);
g_assert_no_error (error);
@@ -172,68 +203,95 @@ test_libarchive_error_device_file (gconstpointer data)
OstreeRepoImportArchiveOptions opts = { 0, };
glnx_unref_object OstreeMutableTree *mtree = ostree_mutable_tree_new ();
- g_assert_cmpint (0, ==, lseek (td->fd, 0, SEEK_SET));
- g_assert_cmpint (0, ==, archive_read_support_format_all (a));
- g_assert_cmpint (0, ==, archive_read_support_filter_all (a));
- g_assert_cmpint (0, ==, archive_read_open_fd (a, td->fd, 8192));
+ test_archive_setup (td->fd, a);
(void)ostree_repo_import_archive_to_mtree (td->repo, &opts, a, mtree, NULL, NULL, &error);
g_assert (error != NULL);
}
+static gboolean
+skip_if_no_xattr (TestData *td)
+{
+ /* /var/tmp might actually be a tmpfs */
+ if (setxattr (td->tmpd, "user.test-xattr-support", "yes", 4, 0) != 0)
+ {
+ int saved_errno = errno;
+ g_autofree gchar *message
+ = g_strdup_printf ("unable to setxattr on \"%s\": %s",
+ td->tmpd, g_strerror (saved_errno));
+ g_test_skip (message);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+import_write_and_ref (OstreeRepo *repo,
+ OstreeRepoImportArchiveOptions *opts,
+ struct archive *a,
+ const char *ref,
+ OstreeRepoCommitModifier *modifier,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ glnx_unref_object GFile *root = NULL;
+ g_autofree char *commit_checksum = NULL;
+ glnx_unref_object OstreeMutableTree *mtree = ostree_mutable_tree_new ();
+
+ if (!ostree_repo_prepare_transaction (repo, NULL, NULL, error))
+ goto out;
+
+ if (!ostree_repo_import_archive_to_mtree (repo, opts, a, mtree, modifier,
+ NULL, error))
+ goto out;
+
+ if (!ostree_repo_write_mtree (repo, mtree, &root, NULL, error))
+ goto out;
+
+ if (!ostree_repo_write_commit (repo, NULL, "", "", NULL,
+ OSTREE_REPO_FILE (root),
+ &commit_checksum, NULL, error))
+ goto out;
+
+ ostree_repo_transaction_set_ref (repo, NULL, ref, commit_checksum);
+
+ if (!ostree_repo_commit_transaction (repo, NULL, NULL, error))
+ goto out;
+
+ ret = TRUE;
+out:
+ return ret;
+}
+
static void
test_libarchive_ignore_device_file (gconstpointer data)
{
TestData *td = (void*)data;
GError *error = NULL;
- GCancellable *cancellable = NULL;
struct archive *a = archive_read_new ();
OstreeRepoImportArchiveOptions opts = { 0, };
- glnx_unref_object OstreeMutableTree *mtree = ostree_mutable_tree_new ();
- glnx_unref_object GFile *root = NULL;
- g_autofree char *commit_checksum = NULL;
- if (setxattr (td->tmpd, "user.test-xattr-support", "yes", 4, 0) != 0)
- {
- int saved_errno = errno;
- g_autofree gchar *message = g_strdup_printf ("unable to setxattr on \"%s\": %s", td->tmpd, g_strerror (saved_errno));
+ if (skip_if_no_xattr (td))
+ goto out;
- g_test_skip (message);
- goto out;
- }
-
- g_assert_cmpint (0, ==, lseek (td->fd, 0, SEEK_SET));
- g_assert_cmpint (0, ==, archive_read_support_format_all (a));
- g_assert_cmpint (0, ==, archive_read_support_filter_all (a));
- g_assert_cmpint (0, ==, archive_read_open_fd (a, td->fd, 8192));
+ test_archive_setup (td->fd, a);
opts.ignore_unsupported_content = TRUE;
- if (!ostree_repo_prepare_transaction (td->repo, NULL, cancellable, &error))
- goto out;
-
- if (!ostree_repo_import_archive_to_mtree (td->repo, &opts, a, mtree, NULL, NULL, &error))
- goto out;
-
- if (!ostree_repo_write_mtree (td->repo, mtree, &root, cancellable, &error))
- goto out;
-
- if (!ostree_repo_write_commit (td->repo, NULL, "", "", NULL,
- OSTREE_REPO_FILE (root),
- &commit_checksum, cancellable, &error))
- goto out;
-
- ostree_repo_transaction_set_ref (td->repo, NULL, "foo", commit_checksum);
-
- if (!ostree_repo_commit_transaction (td->repo, NULL, cancellable, &error))
+ if (!import_write_and_ref (td->repo, &opts, a, "foo", NULL, &error))
goto out;
+ /* check contents */
if (!spawn_cmdline ("ostree --repo=repo ls foo file", &error))
goto out;
if (!spawn_cmdline ("ostree --repo=repo ls foo anotherfile", &error))
goto out;
+ if (!spawn_cmdline ("ostree --repo=repo ls foo /etc/file", &error))
+ goto out;
+
if (spawn_cmdline ("ostree --repo=repo ls foo devnull", &error))
g_assert_not_reached ();
g_assert (error != NULL);
@@ -243,6 +301,243 @@ test_libarchive_ignore_device_file (gconstpointer data)
g_assert_no_error (error);
}
+static gboolean
+check_ostree_convention (GError *error)
+{
+ if (!spawn_cmdline ("ostree --repo=repo ls bar file", &error))
+ return FALSE;
+
+ if (!spawn_cmdline ("ostree --repo=repo ls bar anotherfile", &error))
+ return FALSE;
+
+ if (!spawn_cmdline ("ostree --repo=repo ls bar /usr/etc/file", &error))
+ return FALSE;
+
+ if (spawn_cmdline ("ostree --repo=repo ls bar /etc/file", &error))
+ g_assert_not_reached ();
+ g_assert (error != NULL);
+ g_clear_error (&error);
+
+ if (spawn_cmdline ("ostree --repo=repo ls bar devnull", &error))
+ g_assert_not_reached ();
+ g_assert (error != NULL);
+ g_clear_error (&error);
+
+ return TRUE;
+}
+
+static void
+test_libarchive_ostree_convention (gconstpointer data)
+{
+ TestData *td = (void*)data;
+ GError *error = NULL;
+ struct archive *a = archive_read_new ();
+ OstreeRepoImportArchiveOptions opts = { 0, };
+
+ if (skip_if_no_xattr (td))
+ goto out;
+
+ test_archive_setup (td->fd, a);
+
+ opts.autocreate_parents = TRUE;
+ opts.use_ostree_convention = TRUE;
+ opts.ignore_unsupported_content = TRUE;
+
+ if (!import_write_and_ref (td->repo, &opts, a, "bar", NULL, &error))
+ goto out;
+
+ if (!check_ostree_convention (error))
+ goto out;
+
+ out:
+ g_assert_no_error (error);
+}
+
+static GVariant*
+xattr_cb (OstreeRepo *repo,
+ const char *path,
+ GFileInfo *file_info,
+ gpointer user_data)
+{
+ g_auto(GVariantBuilder) builder;
+ g_variant_builder_init (&builder, (GVariantType*)"a(ayay)");
+ if (strcmp (path, "/anotherfile") == 0)
+ g_variant_builder_add (&builder, "(@ay@ay)",
+ g_variant_new_bytestring ("user.data"),
+ g_variant_new_bytestring ("mydata"));
+ return g_variant_ref_sink (g_variant_builder_end (&builder));
+}
+
+static void
+test_libarchive_xattr_callback (gconstpointer data)
+{
+ TestData *td = (void*)data;
+ GError *error = NULL;
+ struct archive *a = archive_read_new ();
+ OstreeRepoImportArchiveOptions opts = { 0 };
+ OstreeRepoCommitModifier *modifier = NULL;
+ char buf[7] = { 0 };
+
+ if (skip_if_no_xattr (td))
+ goto out;
+
+ modifier = ostree_repo_commit_modifier_new (0, NULL, NULL, NULL);
+ ostree_repo_commit_modifier_set_xattr_callback (modifier, xattr_cb,
+ NULL, NULL);
+
+ test_archive_setup (td->fd, a);
+
+ opts.ignore_unsupported_content = TRUE;
+
+ if (!import_write_and_ref (td->repo, &opts, a, "baz", modifier, &error))
+ goto out;
+
+ /* check contents */
+ if (!spawn_cmdline ("ostree --repo=repo checkout baz baz-checkout", &error))
+ goto out;
+
+ g_assert_cmpint (0, >, getxattr ("baz-checkout/file", "user.data", NULL, 0));
+ g_assert_cmpint (ENODATA, ==, errno);
+
+ if (getxattr ("baz-checkout/anotherfile", "user.data", buf, sizeof buf) < 0)
+ {
+ glnx_set_prefix_error_from_errno (&error, "%s", "getxattr");
+ goto out;
+ }
+
+ g_assert_cmpstr (buf, ==, "mydata");
+
+ out:
+ if (modifier)
+ ostree_repo_commit_modifier_unref (modifier);
+ g_assert_no_error (error);
+}
+
+static GVariant*
+path_cb (OstreeRepo *repo,
+ const char *path,
+ GFileInfo *file_info,
+ gpointer user_data)
+{
+ if (strcmp (path, "/etc/file") == 0)
+ *(gboolean*)user_data = TRUE;
+ return NULL;
+}
+
+static void
+entry_pathname_test_helper (gconstpointer data, gboolean on)
+{
+ TestData *td = (void*)data; GError *error = NULL;
+ struct archive *a = archive_read_new ();
+ OstreeRepoImportArchiveOptions opts = { 0, };
+ OstreeRepoCommitModifier *modifier = NULL;
+ gboolean met_etc_file = FALSE;
+
+ modifier = ostree_repo_commit_modifier_new (0, NULL, NULL, NULL);
+ ostree_repo_commit_modifier_set_xattr_callback (modifier, path_cb,
+ NULL, &met_etc_file);
+
+ test_archive_setup (td->fd, a);
+
+ opts.autocreate_parents = TRUE;
+ opts.use_ostree_convention = TRUE;
+ opts.ignore_unsupported_content = TRUE;
+ opts.callback_with_entry_pathname = on;
+
+ if (!import_write_and_ref (td->repo, &opts, a, "bar", modifier, &error))
+ goto out;
+
+ /* the flag shouldn't have any effect on the final tree */
+ if (!check_ostree_convention (error))
+ goto out;
+
+ if (!on && met_etc_file)
+ {
+ g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Received callback with /etc/file");
+ goto out;
+ }
+
+ if (on && !met_etc_file)
+ {
+ g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Did not receive callback with /etc/file");
+ goto out;
+ }
+
+ out:
+ g_assert_no_error (error);
+}
+
+static void
+test_libarchive_no_use_entry_pathname (gconstpointer data)
+{
+ entry_pathname_test_helper (data, FALSE);
+}
+
+static void
+test_libarchive_use_entry_pathname (gconstpointer data)
+{
+ entry_pathname_test_helper (data, TRUE);
+}
+
+static void
+test_libarchive_selinux (gconstpointer data)
+{
+ TestData *td = (void*)data;
+ GError *error = NULL;
+ struct archive *a = archive_read_new ();
+ OstreeRepoImportArchiveOptions opts = { 0 };
+ glnx_unref_object OstreeSePolicy *sepol = NULL;
+ OstreeRepoCommitModifier *modifier = NULL;
+ char buf[64] = { 0 };
+
+ if (skip_if_no_xattr (td))
+ goto out;
+
+ {
+ glnx_unref_object GFile *root = g_file_new_for_path ("/");
+
+ /* creation should always succeed */
+ sepol = ostree_sepolicy_new (root, NULL, &error);
+ g_assert (sepol != NULL);
+ }
+
+ if (ostree_sepolicy_get_name (sepol) == NULL)
+ {
+ g_test_skip ("SELinux disabled");
+ goto out;
+ }
+
+ modifier = ostree_repo_commit_modifier_new (0, NULL, NULL, NULL);
+ ostree_repo_commit_modifier_set_sepolicy (modifier, sepol);
+
+ test_archive_setup (td->fd, a);
+
+ opts.ignore_unsupported_content = TRUE;
+
+ if (!import_write_and_ref (td->repo, &opts, a, "bob", modifier, &error))
+ goto out;
+
+ /* check contents */
+ if (!spawn_cmdline ("ostree --repo=repo checkout bob bob-checkout", &error))
+ goto out;
+
+ if (getxattr ("bob-checkout/etc", "security.selinux", buf, sizeof buf) < 0)
+ {
+ glnx_set_prefix_error_from_errno (&error, "%s", "getxattr");
+ goto out;
+ }
+
+ buf[(sizeof buf) - 1] = '\0';
+ g_assert_cmpstr (buf, ==, "system_u:object_r:etc_t:s0");
+
+ out:
+ if (modifier)
+ ostree_repo_commit_modifier_unref (modifier);
+ g_assert_no_error (error);
+}
+
int main (int argc, char **argv)
{
TestData td = {NULL,};
@@ -256,10 +551,15 @@ int main (int argc, char **argv)
g_test_add_data_func ("/libarchive/autocreate-empty", &td, test_libarchive_autocreate_empty);
g_test_add_data_func ("/libarchive/error-device-file", &td, test_libarchive_error_device_file);
g_test_add_data_func ("/libarchive/ignore-device-file", &td, test_libarchive_ignore_device_file);
+ g_test_add_data_func ("/libarchive/ostree-convention", &td, test_libarchive_ostree_convention);
+ g_test_add_data_func ("/libarchive/xattr-callback", &td, test_libarchive_xattr_callback);
+ g_test_add_data_func ("/libarchive/no-use-entry-pathname", &td, test_libarchive_no_use_entry_pathname);
+ g_test_add_data_func ("/libarchive/use-entry-pathname", &td, test_libarchive_use_entry_pathname);
+ g_test_add_data_func ("/libarchive/selinux", &td, test_libarchive_selinux);
r = g_test_run();
- if (td.tmpd)
+ if (td.tmpd && g_getenv ("TEST_SKIP_CLEANUP") == NULL)
(void) glnx_shutil_rm_rf_at (AT_FDCWD, td.tmpd, NULL, NULL);
return r;
}
diff --git a/tests/test-libarchive.sh b/tests/test-libarchive.sh
index 7309ffd2..0c579459 100755
--- a/tests/test-libarchive.sh
+++ b/tests/test-libarchive.sh
@@ -26,57 +26,95 @@ fi
. $(dirname $0)/libtest.sh
-echo "1..7"
+echo "1..20"
setup_test_repository "bare"
+
cd ${test_tmpdir}
mkdir foo
cd foo
-echo hi > hi
-ln -s hi hello
-mkdir subdir
-echo contents > subdir/more
-mkdir subdir/1
-touch subdir/1/empty
-mkdir subdir/2
-touch subdir/2/empty
-echo not > subdir/2/notempty
+mkdir -p usr/bin
+echo contents > usr/bin/foo
+touch usr/bin/foo0
+ln usr/bin/foo usr/bin/bar
+ln usr/bin/foo0 usr/bin/bar0
+ln -s foo usr/bin/sl
+mkdir -p usr/local/bin
+ln usr/bin/foo usr/local/bin/baz
+ln usr/bin/foo0 usr/local/bin/baz0
+ln usr/bin/sl usr/local/bin/slhl
+touch usr/bin/setuidme
+touch usr/bin/skipme
tar -c -z -f ../foo.tar.gz .
+find . | cpio -o -H newc > ../foo.cpio
+
cd ..
-$OSTREE commit -s "from tar" -b test-tar --tree=tar=foo.tar.gz
+
+cat > statoverride.txt < skiplist.txt < otherfile
-echo foo1 > foo
-ln foo bar
-tar czf ${test_tmpdir}/hardlinktest.tar.gz .
-cd ${test_tmpdir}
-$OSTREE commit -s 'hardlinks' -b test-hardlinks --tree=tar=hardlinktest.tar.gz
-rm -rf hardlinktest
-echo "ok hardlink commit"
+ # basic content check
+ assert_file_has_content usr/bin/foo contents
+ assert_file_has_content usr/bin/bar contents
+ assert_file_has_content usr/local/bin/baz contents
+ assert_file_empty usr/bin/foo0
+ assert_file_empty usr/bin/bar0
+ assert_file_empty usr/local/bin/baz0
+ echo "ok $1 contents"
-cd ${test_tmpdir}
-$OSTREE checkout test-hardlinks test-hardlinks-checkout
-cd test-hardlinks-checkout
-assert_file_has_content foo foo1
-assert_file_has_content bar foo1
-echo "ok hardlink contents"
+ # hardlinks
+ assert_files_hardlinked usr/bin/foo usr/bin/bar
+ assert_files_hardlinked usr/bin/foo usr/local/bin/baz
+ echo "ok $1 hardlink"
+ assert_files_hardlinked usr/bin/foo0 usr/bin/bar0
+ assert_files_hardlinked usr/bin/foo0 usr/local/bin/baz0
+ echo "ok $1 hardlink to empty files"
+
+ # symlinks
+ assert_symlink_has_content usr/bin/sl foo
+ assert_file_has_content usr/bin/sl contents
+ echo "ok $1 symlink"
+ # ostree checkout doesn't care if two symlinks are actually hardlinked
+ # together (which is fine). checking that it's also a symlink is good enough.
+ assert_symlink_has_content usr/local/bin/slhl foo
+ echo "ok $1 hardlink to symlink"
+
+ # stat override
+ test -u usr/bin/setuidme
+ echo "ok $1 setuid"
+
+ # skip list
+ test ! -f usr/bin/skipme
+ echo "ok $1 file skip"
+
+ cd ${test_tmpdir}
+ rm -rf test-$1-checkout
+}
+
+assert_valid_checkout tar
+assert_valid_checkout cpio
cd ${test_tmpdir}
mkdir multicommit-files
@@ -115,3 +153,4 @@ cd ${test_tmpdir}
$OSTREE checkout partial partial-checkout
cd partial-checkout
assert_file_has_content subdir/original "original"
+echo "ok tar partial commit contents"
diff --git a/tests/test-parent.sh b/tests/test-parent.sh
index 05b102a2..d1f5d7c9 100755
--- a/tests/test-parent.sh
+++ b/tests/test-parent.sh
@@ -21,6 +21,8 @@ set -euo pipefail
. $(dirname $0)/libtest.sh
+skip_without_user_xattrs
+
echo '1..2'
setup_test_repository "archive-z2"
diff --git a/tests/test-pull-c.c b/tests/test-pull-c.c
new file mode 100644
index 00000000..d784312f
--- /dev/null
+++ b/tests/test-pull-c.c
@@ -0,0 +1,132 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "libglnx.h"
+#include
+#include
+#include
+#include
+
+#include "libostreetest.h"
+
+typedef struct {
+ OstreeRepo *repo;
+} TestData;
+
+static void
+test_data_init (TestData *td)
+{
+ GError *local_error = NULL;
+ GError **error = &local_error;
+ g_autofree char *http_address = NULL;
+ g_autofree char *repo_url = NULL;
+
+ td->repo = ot_test_setup_repo (NULL, error);
+ if (!td->repo)
+ goto out;
+
+ if (!ot_test_run_libtest ("setup_fake_remote_repo1 archive-z2", error))
+ goto out;
+
+ if (!g_file_get_contents ("httpd-address", &http_address, NULL, error))
+ goto out;
+
+ repo_url = g_strconcat (http_address, "/ostree/gnomerepo", NULL);
+
+ { g_autoptr(GVariantBuilder) builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
+ g_autoptr(GVariant) opts = NULL;
+
+ g_variant_builder_add (builder, "{s@v}", "gpg-verify", g_variant_new_variant (g_variant_new_boolean (FALSE)));
+ opts = g_variant_ref_sink (g_variant_builder_end (builder));
+
+ if (!ostree_repo_remote_change (td->repo, NULL, OSTREE_REPO_REMOTE_CHANGE_ADD,
+ "origin", repo_url, opts, NULL, error))
+ goto out;
+ }
+
+ out:
+ g_assert_no_error (local_error);
+}
+
+static void
+test_pull_multi_nochange (gconstpointer data)
+{
+ GError *local_error = NULL;
+ GError **error = &local_error;
+ TestData *td = (void*)data;
+ char *refs[] = { "main", NULL };
+
+ if (!ostree_repo_pull (td->repo, "origin", (char**)&refs, 0, NULL, NULL, error))
+ goto out;
+ if (!ostree_repo_pull (td->repo, "origin", (char**)&refs, 0, NULL, NULL, error))
+ goto out;
+ if (!ostree_repo_pull (td->repo, "origin", (char**)&refs, 0, NULL, NULL, error))
+ goto out;
+
+ out:
+ g_assert_no_error (local_error);
+}
+
+static void
+test_pull_multi_error_then_ok (gconstpointer data)
+{
+ GError *local_error = NULL;
+ GError **error = &local_error;
+
+ TestData *td = (void*)data;
+ char *ok_refs[] = { "main", NULL };
+ char *bad_refs[] = { "nosuchbranch", NULL };
+
+ for (guint i = 0; i < 3; i++)
+ {
+ g_autoptr(GError) tmp_error = NULL;
+ if (!ostree_repo_pull (td->repo, "origin", (char**)&ok_refs, 0, NULL, NULL, error))
+ goto out;
+ if (ostree_repo_pull (td->repo, "origin", (char**)&bad_refs, 0, NULL, NULL, &tmp_error))
+ g_assert_not_reached ();
+ g_clear_error (&tmp_error);
+ if (ostree_repo_pull (td->repo, "origin", (char**)&bad_refs, 0, NULL, NULL, &tmp_error))
+ g_assert_not_reached ();
+ g_clear_error (&tmp_error);
+ if (!ostree_repo_pull (td->repo, "origin", (char**)&ok_refs, 0, NULL, NULL, error))
+ goto out;
+ }
+
+ out:
+ g_assert_no_error (local_error);
+}
+
+int main (int argc, char **argv)
+{
+ TestData td = {NULL,};
+ int r;
+
+ test_data_init (&td);
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_data_func ("/test-pull-c/multi-nochange", &td, test_pull_multi_nochange);
+ g_test_add_data_func ("/test-pull-c/multi-ok-error-repeat", &td, test_pull_multi_error_then_ok);
+
+ r = g_test_run();
+
+ return r;
+}
diff --git a/tests/test-pull-override-url.sh b/tests/test-pull-override-url.sh
new file mode 100755
index 00000000..d81b3454
--- /dev/null
+++ b/tests/test-pull-override-url.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+#
+# Copyright (C) 2016 Endless Mobile, 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.
+
+set -euo pipefail
+
+. $(dirname $0)/libtest.sh
+
+setup_fake_remote_repo1 "archive-z2"
+
+echo '1..1'
+
+cd ${test_tmpdir}
+# get a list of XX/XXXXXXX...XX.commit
+find ostree-srv/gnomerepo/objects -name '*.commit' | cut -d/ -f4- | sort >${test_tmpdir}/original_commits
+
+
+gnomerepo_url="$(cat httpd-address)/ostree/gnomerepo"
+mkdir mirror-srv
+cd mirror-srv
+mkdir gnomerepo
+${CMD_PREFIX} ostree --repo=gnomerepo init --mode "archive-z2"
+${CMD_PREFIX} ostree --repo=gnomerepo remote add --set=gpg-verify=false origin ${gnomerepo_url}
+${CMD_PREFIX} ostree --repo=gnomerepo pull --mirror --depth=-1 origin main
+
+find gnomerepo/objects -name '*.commit' | cut -d/ -f3- | sort >${test_tmpdir}/mirror_commits
+
+if ! cmp --quiet ${test_tmpdir}/original_commits ${test_tmpdir}/mirror_commits
+then
+ assert_not_reached 'original repository and its mirror should have the same commits'
+fi
+
+cd ${test_tmpdir}
+mkdir mirror-httpd
+cd mirror-httpd
+ln -s ${test_tmpdir}/mirror-srv ostree
+mirror_log="${test_tmpdir}/mirror_log"
+${CMD_PREFIX} ostree trivial-httpd --log-file=${mirror_log} --autoexit --daemonize -p ${test_tmpdir}/mirror-httpd-port
+port=$(cat ${test_tmpdir}/mirror-httpd-port)
+echo "http://127.0.0.1:${port}" > ${test_tmpdir}/mirror-httpd-address
+
+cd ${test_tmpdir}
+mirrorrepo_url="$(cat mirror-httpd-address)/ostree/gnomerepo"
+mkdir repo
+${CMD_PREFIX} ostree --repo=repo init
+${CMD_PREFIX} ostree --repo=repo remote add --set=gpg-verify=false origin ${gnomerepo_url}
+rm -rf ${test_tmpdir}/ostree-srv/gnomerepo
+if ${CMD_PREFIX} ostree --repo=repo pull --depth=-1 origin main
+then
+ assert_not_reached 'pulling from removed remote should have failed'
+fi
+
+if ! ${CMD_PREFIX} ostree --repo=repo pull --depth=-1 --url=${mirrorrepo_url} origin main
+then
+ cat ${mirror_log}
+ assert_not_reached 'fetching from the overridden URL should succeed'
+fi
+find repo/objects -name '*.commit' | cut -d/ -f3- | sort >${test_tmpdir}/repo_commits
+
+if ! cmp --quiet ${test_tmpdir}/original_commits ${test_tmpdir}/repo_commits
+then
+ assert_not_reached 'original repository and its mirror should have the same commits'
+fi
+
+echo "ok pull url override"
diff --git a/tests/test-refs.sh b/tests/test-refs.sh
index bcebae9e..3d229031 100755
--- a/tests/test-refs.sh
+++ b/tests/test-refs.sh
@@ -65,4 +65,35 @@ ${CMD_PREFIX} ostree refs --repo=repo | wc -l > refscount.delete3
assert_file_has_content refscount.delete3 "^3$"
assert_not_file_has_content reflist '^test-1$'
+#Add a few more commits, to test --create
+${CMD_PREFIX} ostree --repo=repo commit --branch=ctest -m ctest -s ctest tree
+${CMD_PREFIX} ostree --repo=repo commit --branch=foo/ctest -m ctest -s ctest tree
+
+${CMD_PREFIX} ostree --repo=repo refs | wc -l > refscount
+assert_file_has_content refscount "^5$"
+
+if ${CMD_PREFIX} ostree --repo=repo refs --create=ctest-new; then
+ assert_not_reached "refs --create unexpectedly succeeded without specifying an existing ref!"
+fi
+if ${CMD_PREFIX} ostree --repo=repo refs ctest --create; then
+ assert_not_reached "refs --create unexpectedly succeeded without specifying the ref to create!"
+fi
+if ${CMD_PREFIX} ostree --repo=repo refs does-not-exist --create=ctest-new; then
+ assert_not_reached "refs --create unexpectedly succeeded for a prefix that doesn't exist!"
+fi
+if ${CMD_PREFIX} ostree --repo=repo refs ctest --create=foo; then
+ assert_not_reached "refs --create unexpectedly succeeded for a prefix that is already in use by a folder!"
+fi
+if ${CMD_PREFIX} ostree --repo=repo refs foo/ctest --create=ctest; then
+ assert_not_reached "refs --create unexpectedly succeeded in overwriting an existing prefix!"
+fi
+
+#Check to see if any uncleaned tmp files were created after failed --create
+${CMD_PREFIX} ostree --repo=repo refs | wc -l > refscount.create1
+assert_file_has_content refscount.create1 "^5$"
+
+${CMD_PREFIX} ostree --repo=repo refs ctest --create ctest-new
+${CMD_PREFIX} ostree --repo=repo refs | wc -l > refscount.create2
+assert_file_has_content refscount.create2 "^6$"
+
echo "ok refs"
diff --git a/tests/test-setuid.sh b/tests/test-setuid.sh
deleted file mode 100755
index edf707df..00000000
--- a/tests/test-setuid.sh
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2013 Colin Walters
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-
-set -euo pipefail
-
-echo "1..1"
-
-. $(dirname $0)/libtest.sh
-
-setup_test_repository "bare"
-
-cd ${test_tmpdir}
-cat > test-statoverride.txt < expected-symbols.txt
eu-readelf -a ${G_TEST_BUILDDIR}/.libs/libostree-1.so | grep 'FUNC.*GLOBAL.*DEFAULT.*@@LIBOSTREE_' | sed -e 's,^.* \(ostree_[A-Za-z0-9_]*\)@@LIBOSTREE_[0-9_.]*,\1,' |sort -u > found-symbols.txt
diff -u expected-symbols.txt found-symbols.txt
+echo "ok exports"
-echo 'ok'
+# cmd__private__ is private. The fetcher symbol should not have been made public.
+grep -E -v '(ostree_cmd__private__)|(ostree_fetcher_config_flags_get_type)' found-symbols.txt > expected-documented.txt
+
+echo "Verifying all public symbols are documented:"
+grep '^ostree_' ${G_TEST_SRCDIR}/apidoc/ostree-sections.txt |sort -u > found-documented.txt
+diff -u expected-documented.txt found-documented.txt
+
+echo 'ok documented symbols'
diff --git a/tests/test-sysroot.js b/tests/test-sysroot.js
index 9468d2fb..7c31659d 100644
--- a/tests/test-sysroot.js
+++ b/tests/test-sysroot.js
@@ -37,7 +37,7 @@ function libtestExec(shellCode) {
let testdatadir = GLib.getenv("G_TEST_SRCDIR");
let libtestPath = GLib.build_filenamev([testdatadir, 'libtest.sh'])
let proc = GSystem.Subprocess.new_simple_argv(['bash', '-c',
- '. ' + GLib.shell_quote(libtestPath) + '; ' + shellCode],
+ 'set -xeuo pipefail; . ' + GLib.shell_quote(libtestPath) + '; ' + shellCode],
GSystem.SubprocessStreamDisposition.INHERIT,
GSystem.SubprocessStreamDisposition.INHERIT,
null);