diff --git a/Makefile-boot.am b/Makefile-boot.am index 5b512b6c..b4d4a1af 100644 --- a/Makefile-boot.am +++ b/Makefile-boot.am @@ -39,7 +39,10 @@ endif if BUILDOPT_SYSTEMD systemdsystemunit_DATA = src/boot/ostree-prepare-root.service \ - src/boot/ostree-remount.service src/boot/ostree-finalize-staged.service + src/boot/ostree-remount.service \ + src/boot/ostree-finalize-staged.service \ + src/boot/ostree-finalize-staged.path \ + $(NULL) systemdtmpfilesdir = $(prefix)/lib/tmpfiles.d dist_systemdtmpfiles_DATA = src/boot/ostree-tmpfiles.conf @@ -64,6 +67,7 @@ EXTRA_DIST += src/boot/dracut/module-setup.sh \ src/boot/dracut/ostree.conf \ src/boot/mkinitcpio/ostree \ src/boot/ostree-prepare-root.service \ + src/boot/ostree-finalize-staged.path \ src/boot/ostree-remount.service \ src/boot/ostree-finalize-staged.service \ src/boot/grub2/grub2-15_ostree \ diff --git a/Makefile.in b/Makefile.in index de1f8906..4dcedb1f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -2075,6 +2075,7 @@ EXTRA_DIST = $(all_dist_test_scripts) $(all_dist_test_data) autogen.sh \ src/boot/dracut/module-setup.sh src/boot/dracut/ostree.conf \ src/boot/mkinitcpio/ostree \ src/boot/ostree-prepare-root.service \ + src/boot/ostree-finalize-staged.path \ src/boot/ostree-remount.service \ src/boot/ostree-finalize-staged.service \ src/boot/grub2/grub2-15_ostree \ @@ -2764,7 +2765,10 @@ tests_test_gpg_verify_result_LDADD = $(TESTS_LDADD) $(OT_INTERNAL_GPGME_LIBS) @BUILDOPT_MKINITCPIO_TRUE@mkinitcpioconfdir = $(sysconfdir) @BUILDOPT_MKINITCPIO_TRUE@mkinitcpioconf_DATA = src/boot/mkinitcpio/ostree-mkinitcpio.conf @BUILDOPT_SYSTEMD_TRUE@systemdsystemunit_DATA = src/boot/ostree-prepare-root.service \ -@BUILDOPT_SYSTEMD_TRUE@ src/boot/ostree-remount.service src/boot/ostree-finalize-staged.service +@BUILDOPT_SYSTEMD_TRUE@ src/boot/ostree-remount.service \ +@BUILDOPT_SYSTEMD_TRUE@ src/boot/ostree-finalize-staged.service \ +@BUILDOPT_SYSTEMD_TRUE@ src/boot/ostree-finalize-staged.path \ +@BUILDOPT_SYSTEMD_TRUE@ $(NULL) @BUILDOPT_SYSTEMD_TRUE@systemdtmpfilesdir = $(prefix)/lib/tmpfiles.d @BUILDOPT_SYSTEMD_TRUE@dist_systemdtmpfiles_DATA = src/boot/ostree-tmpfiles.conf diff --git a/README.md b/README.md index 36bcfc24..6ea343ee 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,22 @@ The [BuildStream](https://gitlab.com/BuildStream/buildstream) build and integration tool uses libostree as a caching system to store and share built artifacts. +Language bindings +---- + +libostree is accessible via [GObject Introspection](https://gi.readthedocs.io/en/latest/); +any language which has implemented the GI binding model should work. +For example, Both [pygobject](https://pygobject.readthedocs.io/en/latest/) +and [gjs](https://gitlab.gnome.org/GNOME/gjs) are known to work +and further are actually used in libostree's test suite today. + +Some bindings take the approach of using GI as a lower level and +write higher level manual bindings on top; this is more common +for statically compiled languages. Here's a list of such bindings: + + - [ostree-go](https://github.com/ostreedev/ostree-go/) + - [rust-libostree](https://gitlab.com/fkrull/rust-libostree/) + Building -------- diff --git a/apidoc/html/ostree-Core-repository-independent-functions.html b/apidoc/html/ostree-Core-repository-independent-functions.html index 09fdbe44..1ae97715 100644 --- a/apidoc/html/ostree-Core-repository-independent-functions.html +++ b/apidoc/html/ostree-Core-repository-independent-functions.html @@ -2420,17 +2420,17 @@ ostree_check_version (guint

OSTREE_MAX_METADATA_SIZE

#define OSTREE_MAX_METADATA_SIZE (10 * 1024 * 1024)
 
-

Maximum permitted size in bytes of metadata objects. This is an -arbitrary number, but really, no one should be putting humongous -data in metadata.

+

Default limit for maximum permitted size in bytes of metadata objects fetched +over HTTP (including repo/config files, refs, and commit/dirtree/dirmeta +objects). This is an arbitrary number intended to mitigate disk space +exhaustion attacks.


OSTREE_MAX_METADATA_WARN_SIZE

#define OSTREE_MAX_METADATA_WARN_SIZE (7 * 1024 * 1024)
 
-

Objects committed above this size will be allowed, but a warning -will be emitted.

+

This variable is no longer meaningful, it is kept only for compatibility.


diff --git a/apidoc/html/ostree-In-memory-modifiable-filesystem-tree.html b/apidoc/html/ostree-In-memory-modifiable-filesystem-tree.html index 3ceeb8bc..ad0124b6 100644 --- a/apidoc/html/ostree-In-memory-modifiable-filesystem-tree.html +++ b/apidoc/html/ostree-In-memory-modifiable-filesystem-tree.html @@ -106,6 +106,14 @@ gboolean +ostree_mutable_tree_remove () + + + + +gboolean + + ostree_mutable_tree_ensure_dir () @@ -307,6 +315,52 @@ ostree_mutable_tree_replace_file (
+

ostree_mutable_tree_remove ()

+
gboolean
+ostree_mutable_tree_remove (OstreeMutableTree *self,
+                            const char *name,
+                            gboolean allow_noent,
+                            GError **error);
+

Remove the file or subdirectory named name + from the mutable tree self +.

+
+

Parameters

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

self

Tree

 

name

Name of file or subdirectory to remove

 

allow_noent

If FALSE +, an error will be thrown if name +does not exist in the tree

 

error

a GError

 
+
+
+
+

ostree_mutable_tree_ensure_dir ()

gboolean
 ostree_mutable_tree_ensure_dir (OstreeMutableTree *self,
diff --git a/apidoc/html/ostree-OstreeRepo.html b/apidoc/html/ostree-OstreeRepo.html
index eddc4e59..48e4693a 100644
--- a/apidoc/html/ostree-OstreeRepo.html
+++ b/apidoc/html/ostree-OstreeRepo.html
@@ -153,6 +153,14 @@
 
 
 
+gboolean
+
+
+ostree_repo_get_min_free_space_bytes ()
+
+
+
+
 GKeyFile *
 
 
@@ -168,6 +176,13 @@
 
 
 
+const gchar * const *
+
+
+ostree_repo_get_default_repo_finders ()
+
+
+
 
 guint
 
@@ -1209,16 +1224,18 @@
 

Description

The OstreeRepo is like git, a content-addressed object store. Unlike git, it records uid, gid, and extended attributes.

-

There are three possible "modes" for an OstreeRepo; -OSTREE_REPO_MODE_BARE is very simple - content files are -represented exactly as they are, and checkouts are just hardlinks. -OSTREE_REPO_MODE_BARE_USER is similar, except the uid/gids are not -set on the files, and checkouts as hardlinks hardlinks work only for user checkouts. -A OSTREE_REPO_MODE_ARCHIVE_Z2 repository in contrast stores -content files zlib-compressed. It is suitable for non-root-owned +

There are four possible "modes" for an OstreeRepo; OSTREE_REPO_MODE_BARE +is very simple - content files are represented exactly as they are, and +checkouts are just hardlinks. OSTREE_REPO_MODE_BARE_USER is similar, except +the uid/gids are not set on the files, and checkouts as hardlinks work only +for user checkouts. OSTREE_REPO_MODE_BARE_USER_ONLY is the same as +BARE_USER, but all metadata is not stored, so it can only be used for user +checkouts. This mode does not require xattrs. A OSTREE_REPO_MODE_ARCHIVE +(also known as OSTREE_REPO_MODE_ARCHIVE_Z2) repository in contrast stores +content files zlib-compressed. It is suitable for non-root-owned repositories that can be served via a static HTTP server.

Creating an OstreeRepo does not invoke any file I/O, and thus needs -to be initialized, either from an existing contents or with a new +to be initialized, either from existing contents or as a new repository. If you have an existing repo, use ostree_repo_open() to load it from disk and check its validity. To initialize a new repository in the given filepath, use ostree_repo_create() instead.

@@ -1664,6 +1681,14 @@ ostree_repo_get_mode ( +

ostree_repo_get_min_free_space_bytes ()

+
gboolean
+ostree_repo_get_min_free_space_bytes (OstreeRepo *self,
+                                      guint64 *out_reserved_bytes,
+                                      GError **error);
+
+
+

ostree_repo_get_config ()

GKeyFile *
 ostree_repo_get_config (OstreeRepo *self);
@@ -1705,6 +1730,35 @@ repository (to see whether a ref was written).


+

ostree_repo_get_default_repo_finders ()

+
const gchar * const *
+ostree_repo_get_default_repo_finders (OstreeRepo *self);
+

Get the set of default repo finders configured. See the documentation for +the "core.default-repo-finders" config key.

+
+

Parameters

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

self

an OstreeRepo

 
+
+
+

Returns

+

NULL-terminated array of strings.

+

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

+
+

Since: 2018.9

+
+
+

ostree_repo_hash ()

guint
 ostree_repo_hash (OstreeRepo *self);
@@ -7111,6 +7165,7 @@ string to pull the latest commit for that ref

  • require-static-deltas (b): Require static deltas

  • override-commit-ids (as): Array of specific commit IDs to fetch for refs

  • timestamp-check (b): Verify commit timestamps are newer than current (when pulling via ref); Since: 2017.11

  • +
  • metadata-size-restriction (t): Restrict metadata objects to a maximum number of bytes; 0 to disable. Since: 2018.9

  • 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

  • inherit-transaction (b): Don't initiate, finish or abort a transaction, useful to do multiple pulls in one transaction.

  • @@ -8127,7 +8182,8 @@ by + + @@ -246,6 +248,7 @@ + diff --git a/apidoc/html/reference.html b/apidoc/html/reference.html index 85984f13..74389378 100644 --- a/apidoc/html/reference.html +++ b/apidoc/html/reference.html @@ -539,6 +539,10 @@ ostree_collection_ref_new, function in ostree-ref
    +OSTREE_META_KEY_DEPLOY_COLLECTION_ID, macro in ostree-repo-experimental +
    +
    +
    OstreeMutableTree, typedef in In-memory modifiable filesystem tree
    @@ -587,6 +591,10 @@ ostree_collection_ref_new, function in ostree-ref
    +ostree_mutable_tree_remove, function in In-memory modifiable filesystem tree +
    +
    +
    ostree_mutable_tree_replace_file, function in In-memory modifiable filesystem tree
    @@ -1022,6 +1030,10 @@ ostree_repo_get_collection_id, function in ostree-misc-experimental
    +ostree_repo_get_default_repo_finders, function in OstreeRepo +
    +
    +
    ostree_repo_get_dfd, function in OstreeRepo
    @@ -1030,6 +1042,10 @@ ostree_repo_get_collection_id, function in ostree-misc-experimental
    +ostree_repo_get_min_free_space_bytes, function in OstreeRepo +
    +
    +
    ostree_repo_get_mode, function in OstreeRepo
    diff --git a/apidoc/ostree-sections.txt b/apidoc/ostree-sections.txt index 2e174244..5dbafc5f 100644 --- a/apidoc/ostree-sections.txt +++ b/apidoc/ostree-sections.txt @@ -257,6 +257,7 @@ ostree_mutable_tree_get_metadata_checksum ostree_mutable_tree_set_contents_checksum ostree_mutable_tree_get_contents_checksum ostree_mutable_tree_replace_file +ostree_mutable_tree_remove ostree_mutable_tree_ensure_dir ostree_mutable_tree_lookup ostree_mutable_tree_ensure_parent_dirs @@ -294,8 +295,10 @@ ostree_repo_create_at ostree_repo_create ostree_repo_get_path ostree_repo_get_mode +ostree_repo_get_min_free_space_bytes ostree_repo_get_config ostree_repo_get_dfd +ostree_repo_get_default_repo_finders ostree_repo_hash ostree_repo_equal ostree_repo_copy_config @@ -596,6 +599,7 @@ ostree_repo_pull_from_remotes_async ostree_repo_pull_from_remotes_finish ostree_repo_resolve_keyring_for_collection OSTREE_REPO_METADATA_REF +OSTREE_META_KEY_DEPLOY_COLLECTION_ID
    diff --git a/bash/ostree b/bash/ostree index 677aee7c..52b111ec 100644 --- a/bash/ostree +++ b/bash/ostree @@ -24,12 +24,6 @@ # - Structured option arguments (e.g. --foo KEY=VALUE) are not parsed. # # - Static deltas could likely be completed. (e.g. ostree static-delta delete [TAB]) -# -# - The "--repo PATH" option needs to come after the subcommand or it -# won't be picked up for completion of subsequent options. -# i.e. ostree commit --repo PATH ... <-- works -# ostree --repo PATH commit ... <-- does not work -# (Possibly an easy fix.) # Finds the position of the first non-flag word. @@ -183,9 +177,16 @@ __ostree_subcommands() { # This handles "ostree [TAB]" (without a subcommand). _ostree_ostree() { + case "$prev" in + --repo) + __ostree_compreply_dirs_only + return 0 + ;; + esac + case "$cur" in -*) - COMPREPLY=( $( compgen -W "$main_boolean_options" -- "$cur" ) ) + COMPREPLY=( $( compgen -W "$main_options" -- "$cur" ) ) ;; *) COMPREPLY=( $( compgen -W "$commands" -- "$cur" ) ) @@ -1753,6 +1754,14 @@ _ostree() { --verbose -v --version " + local main_options_with_args=" + --repo + " + local main_options_with_args_glob=$( __ostree_to_extglob "$main_options_with_args" ) + local main_options=" + $main_boolean_options + $main_options_with_args + " COMPREPLY=() local cur prev words cword diff --git a/configure b/configure index e551d6b1..4210aa55 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for libostree 2018.8. +# Generated by GNU Autoconf 2.69 for libostree 2018.9. # # Report bugs to . # @@ -590,8 +590,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='libostree' PACKAGE_TARNAME='libostree' -PACKAGE_VERSION='2018.8' -PACKAGE_STRING='libostree 2018.8' +PACKAGE_VERSION='2018.9' +PACKAGE_STRING='libostree 2018.9' PACKAGE_BUGREPORT='walters@verbum.org' PACKAGE_URL='' @@ -1547,7 +1547,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures libostree 2018.8 to adapt to many kinds of systems. +\`configure' configures libostree 2018.9 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1617,7 +1617,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libostree 2018.8:";; + short | recursive ) echo "Configuration of libostree 2018.9:";; esac cat <<\_ACEOF @@ -1864,7 +1864,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libostree configure 2018.8 +libostree configure 2018.9 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2336,7 +2336,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libostree $as_me 2018.8, which was +It was created by libostree $as_me 2018.9, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3204,7 +3204,7 @@ fi # Define the identity of the package. PACKAGE='libostree' - VERSION='2018.8' + VERSION='2018.9' # Some tools Automake needs. @@ -5938,9 +5938,9 @@ test -n "$YACC" || YACC="yacc" YEAR_VERSION=2018 -RELEASE_VERSION=8 +RELEASE_VERSION=9 -PACKAGE_VERSION=2018.8 +PACKAGE_VERSION=2018.9 if echo "$CFLAGS" | grep -q -E -e '-Werror($| )'; then : @@ -18623,7 +18623,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by libostree $as_me 2018.8, which was +This file was extended by libostree $as_me 2018.9, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -18689,7 +18689,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -libostree config.status 2018.8 +libostree config.status 2018.9 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 4ccd79c6..65e0e68c 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ dnl update libostree-released.sym from libostree-devel.sym, and update the check dnl in test-symbols.sh, and also set is_release_build=yes below. Then make dnl another post-release commit to bump the version, and set is_release_build=no. m4_define([year_version], [2018]) -m4_define([release_version], [8]) +m4_define([release_version], [9]) m4_define([package_version], [year_version.release_version]) AC_INIT([libostree], [package_version], [walters@verbum.org]) is_release_build=yes diff --git a/man/ostree-create-usb.xml b/man/ostree-create-usb.xml index d250d915..90b2fc5b 100644 --- a/man/ostree-create-usb.xml +++ b/man/ostree-create-usb.xml @@ -83,11 +83,6 @@ Boston, MA 02111-1307, USA. USB mounts use signed per-repo and per-commit metadata instead of summary signatures. - - This command relies on the summary file in the source repo, so you - may want to run ostree summary -u before running - this command. - @@ -112,6 +107,15 @@ Boston, MA 02111-1307, USA. + + =COMMIT + + + Pull COMMIT instead of whatever REF points to. This can only + be used if a single ref is specified. + + + diff --git a/man/ostree.repo-config.xml b/man/ostree.repo-config.xml index dc126d65..acaa3be5 100644 --- a/man/ostree.repo-config.xml +++ b/man/ostree.repo-config.xml @@ -198,6 +198,38 @@ Boston, MA 02111-1307, USA. + + locking + Boolean value controlling whether or not OSTree does + repository locking internally. This uses file locks and is + hence for multiple process exclusion (e.g. Flatpak and OSTree + writing to the same repository separately). This is enabled by + default since 2018.5. + + + + + lock-timeout-secs + Integer value controlling the number of seconds to + block while attempting to acquire a lock (see above). A value + of -1 means block indefinitely. The default value is 30. + + + + + default-repo-finders + Semicolon separated default list of finders (sources + for refs) to use when pulling. This can be used to disable + pulling from mounted filesystems, peers on the local network, + or the Internet. However note that it only applies when a set + of finders isn't explicitly specified, either by a consumer of + libostree API or on the command line. Possible values: + config, lan, and + mount (or any combination thereof). If unset, this + defaults to config;mount; (since the LAN finder is + costly). + + diff --git a/src/boot/dracut/module-setup.sh b/src/boot/dracut/module-setup.sh index 70364c4b..4d12fae6 100755 --- a/src/boot/dracut/module-setup.sh +++ b/src/boot/dracut/module-setup.sh @@ -36,7 +36,7 @@ depends() { install() { dracut_install /usr/lib/ostree/ostree-prepare-root inst_simple "${systemdsystemunitdir}/ostree-prepare-root.service" - mkdir -p "${initdir}${systemdsystemconfdir}/initrd-switch-root.target.wants" + mkdir -p "${initdir}${systemdsystemconfdir}/initrd-root-fs.target.wants" ln_r "${systemdsystemunitdir}/ostree-prepare-root.service" \ - "${systemdsystemconfdir}/initrd-switch-root.target.wants/ostree-prepare-root.service" + "${systemdsystemconfdir}/initrd-root-fs.target.wants/ostree-prepare-root.service" } diff --git a/src/boot/ostree-finalize-staged.path b/src/boot/ostree-finalize-staged.path new file mode 100644 index 00000000..f0f76151 --- /dev/null +++ b/src/boot/ostree-finalize-staged.path @@ -0,0 +1,28 @@ +# Copyright (C) 2018 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# For some implementation discussion, see: +# https://lists.freedesktop.org/archives/systemd-devel/2018-March/040557.html +[Unit] +Description=OSTree Monitor Staged Deployment +Documentation=man:ostree(1) + +[Path] +PathExists=/run/ostree/staged-deployment + +[Install] +WantedBy=multi-user.target diff --git a/src/boot/ostree-finalize-staged.service b/src/boot/ostree-finalize-staged.service index 570138cd..10e551e4 100644 --- a/src/boot/ostree-finalize-staged.service +++ b/src/boot/ostree-finalize-staged.service @@ -19,6 +19,7 @@ # https://lists.freedesktop.org/archives/systemd-devel/2018-March/040557.html [Unit] Description=OSTree Finalize Staged Deployment +Documentation=man:ostree(1) ConditionPathExists=/run/ostree-booted DefaultDependencies=no @@ -31,6 +32,7 @@ Conflicts=final.target Type=oneshot RemainAfterExit=yes ExecStop=/usr/bin/ostree admin finalize-staged - -[Install] -WantedBy=multi-user.target +# This is a quite long timeout intentionally; the failure mode +# here is that people don't get an upgrade. We need to handle +# cases with slow rotational media, etc. +TimeoutStopSec=5m diff --git a/src/boot/ostree-prepare-root.service b/src/boot/ostree-prepare-root.service index 52cf8863..63357581 100644 --- a/src/boot/ostree-prepare-root.service +++ b/src/boot/ostree-prepare-root.service @@ -17,13 +17,13 @@ [Unit] Description=OSTree Prepare OS/ +Documentation=man:ostree(1) DefaultDependencies=no ConditionKernelCommandLine=ostree ConditionPathExists=/etc/initrd-release OnFailure=emergency.target -After=initrd-switch-root.target -Before=initrd-switch-root.service -Before=plymouth-switch-root.service +After=sysroot.mount +Before=initrd-root-fs.target [Service] Type=oneshot @@ -31,3 +31,4 @@ ExecStart=/usr/lib/ostree/ostree-prepare-root /sysroot StandardInput=null StandardOutput=syslog StandardError=syslog+console +RemainAfterExit=yes diff --git a/src/boot/ostree-remount.service b/src/boot/ostree-remount.service index 47e1387a..b98110c2 100644 --- a/src/boot/ostree-remount.service +++ b/src/boot/ostree-remount.service @@ -16,7 +16,8 @@ # Boston, MA 02111-1307, USA. [Unit] -Description=OSTree Remount OS/ bind mounts +Description=OSTree Remount OS/ Bind Mounts +Documentation=man:ostree(1) DefaultDependencies=no ConditionKernelCommandLine=ostree OnFailure=emergency.target diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index 24db9df2..a4040ba6 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -18,8 +18,6 @@ ***/ /* Add new symbols here. Release commits should copy this section into -released.sym. */ -LIBOSTREE_2018.9 { -} LIBOSTREE_2018.7; /* Stub section for the stable release *after* this development one; don't * edit this other than to update the last number. This is just a copy/paste diff --git a/src/libostree/libostree-released.sym b/src/libostree/libostree-released.sym index ae3bb5ed..90b363ca 100644 --- a/src/libostree/libostree-released.sym +++ b/src/libostree/libostree-released.sym @@ -536,6 +536,12 @@ global: /* No new symbols in 2018.8 */ +LIBOSTREE_2018.9 { + ostree_mutable_tree_remove; + ostree_repo_get_min_free_space_bytes; + ostree_repo_get_default_repo_finders; +} LIBOSTREE_2018.7; + /* NOTE: Only add more content here in release commits! See the * comments at the top of this file. */ diff --git a/src/libostree/ostree-async-progress.c b/src/libostree/ostree-async-progress.c index 9b105886..a8e629ee 100644 --- a/src/libostree/ostree-async-progress.c +++ b/src/libostree/ostree-async-progress.c @@ -228,7 +228,7 @@ idle_invoke_async_progress (gpointer user_data) OstreeAsyncProgress *self = user_data; g_mutex_lock (&self->lock); - self->idle_source = NULL; + g_clear_pointer (&self->idle_source, g_source_unref); g_mutex_unlock (&self->lock); g_signal_emit (self, signals[CHANGED], 0); diff --git a/src/libostree/ostree-bootloader-grub2.c b/src/libostree/ostree-bootloader-grub2.c index f0d34809..6831382c 100644 --- a/src/libostree/ostree-bootloader-grub2.c +++ b/src/libostree/ostree-bootloader-grub2.c @@ -57,7 +57,8 @@ struct _OstreeBootloaderGrub2 GObject parent_instance; OstreeSysroot *sysroot; - GFile *config_path_bios; + GFile *config_path_bios_1; + GFile *config_path_bios_2; GFile *config_path_efi; gboolean is_efi; }; @@ -77,7 +78,8 @@ _ostree_bootloader_grub2_query (OstreeBootloader *bootloader, OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (bootloader); /* Look for the BIOS path first */ - if (g_file_query_exists (self->config_path_bios, NULL)) + if (g_file_query_exists (self->config_path_bios_1, NULL) || + g_file_query_exists (self->config_path_bios_2, NULL)) { /* If we found it, we're done */ *out_is_active = TRUE; @@ -97,7 +99,7 @@ _ostree_bootloader_grub2_query (OstreeBootloader *bootloader, cancellable, error); if (!direnum) return FALSE; - + while (TRUE) { GFileInfo *file_info; @@ -448,7 +450,7 @@ _ostree_bootloader_grub2_write_config (OstreeBootloader *bootloader, } static gboolean -_ostree_bootloader_grub2_is_atomic (OstreeBootloader *bootloader) +_ostree_bootloader_grub2_is_atomic (OstreeBootloader *bootloader) { OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (bootloader); return !self->is_efi; @@ -460,7 +462,8 @@ _ostree_bootloader_grub2_finalize (GObject *object) OstreeBootloaderGrub2 *self = OSTREE_BOOTLOADER_GRUB2 (object); g_clear_object (&self->sysroot); - g_clear_object (&self->config_path_bios); + g_clear_object (&self->config_path_bios_1); + g_clear_object (&self->config_path_bios_2); g_clear_object (&self->config_path_efi); G_OBJECT_CLASS (_ostree_bootloader_grub2_parent_class)->finalize (object); @@ -493,6 +496,9 @@ _ostree_bootloader_grub2_new (OstreeSysroot *sysroot) { OstreeBootloaderGrub2 *self = g_object_new (OSTREE_TYPE_BOOTLOADER_GRUB2, NULL); self->sysroot = g_object_ref (sysroot); - self->config_path_bios = g_file_resolve_relative_path (self->sysroot->path, "boot/grub2/grub.cfg"); + /* Used by (at least) Debian */ + self->config_path_bios_1 = g_file_resolve_relative_path (self->sysroot->path, "boot/grub/grub.cfg"); + /* Used by (at least) Fedora */ + self->config_path_bios_2 = g_file_resolve_relative_path (self->sysroot->path, "boot/grub2/grub.cfg"); return self; } diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h index 08b7d451..69477a75 100644 --- a/src/libostree/ostree-core.h +++ b/src/libostree/ostree-core.h @@ -31,18 +31,18 @@ G_BEGIN_DECLS /** * OSTREE_MAX_METADATA_SIZE: - * - * Maximum permitted size in bytes of metadata objects. This is an - * arbitrary number, but really, no one should be putting humongous - * data in metadata. + * + * Default limit for maximum permitted size in bytes of metadata objects fetched + * over HTTP (including repo/config files, refs, and commit/dirtree/dirmeta + * objects). This is an arbitrary number intended to mitigate disk space + * exhaustion attacks. */ #define OSTREE_MAX_METADATA_SIZE (10 * 1024 * 1024) /** * OSTREE_MAX_METADATA_WARN_SIZE: - * - * Objects committed above this size will be allowed, but a warning - * will be emitted. + * + * This variable is no longer meaningful, it is kept only for compatibility. */ #define OSTREE_MAX_METADATA_WARN_SIZE (7 * 1024 * 1024) diff --git a/src/libostree/ostree-fetcher-curl.c b/src/libostree/ostree-fetcher-curl.c index 2e090cfa..9738f980 100644 --- a/src/libostree/ostree-fetcher-curl.c +++ b/src/libostree/ostree-fetcher-curl.c @@ -172,6 +172,9 @@ _ostree_fetcher_finalize (GObject *object) curl_multi_cleanup (self->multi); g_free (self->remote_name); + g_free (self->tls_ca_db_path); + g_free (self->tls_client_cert_path); + g_free (self->tls_client_key_path); g_free (self->cookie_jar_path); g_free (self->proxy); g_assert_cmpint (g_hash_table_size (self->outstanding_requests), ==, 0); @@ -319,17 +322,15 @@ check_multi_info (OstreeFetcher *fetcher) { /* Handle file not found */ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, - "%s", - curl_easy_strerror (curlres)); + "%s", curl_easy_strerror (curlres)); } else { - g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "[%u] %s", - curlres, + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, + "While fetching %s: [%u] %s", eff_url, curlres, curl_easy_strerror (curlres)); - if (req->fetcher->remote_name) - _ostree_fetcher_journal_failure (req->fetcher->remote_name, - eff_url, curl_easy_strerror (curlres)); + _ostree_fetcher_journal_failure (req->fetcher->remote_name, + eff_url, curl_easy_strerror (curlres)); } } else diff --git a/src/libostree/ostree-fetcher-util.c b/src/libostree/ostree-fetcher-util.c index 6f759c86..35e8c3c3 100644 --- a/src/libostree/ostree-fetcher-util.c +++ b/src/libostree/ostree-fetcher-util.c @@ -172,6 +172,7 @@ _ostree_fetcher_journal_failure (const char *remote_name, "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(OSTREE_HTTP_FAILURE_ID), "OSTREE_REMOTE=%s", remote_name, "OSTREE_URL=%s", url, + "PRIORITY=%i", LOG_ERR, NULL); #endif } diff --git a/src/libostree/ostree-mutable-tree.c b/src/libostree/ostree-mutable-tree.c index cd18927a..f6577210 100644 --- a/src/libostree/ostree-mutable-tree.c +++ b/src/libostree/ostree-mutable-tree.c @@ -305,31 +305,57 @@ ostree_mutable_tree_replace_file (OstreeMutableTree *self, const char *checksum, GError **error) { - gboolean ret = FALSE; - g_return_val_if_fail (name != NULL, FALSE); if (!ot_util_filename_validate (name, error)) - goto out; + return FALSE; if (!_ostree_mutable_tree_make_whole (self, NULL, error)) - goto out; + return FALSE; if (g_hash_table_lookup (self->subdirs, name)) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Can't replace directory with file: %s", name); - goto out; - } + return glnx_throw (error, "Can't replace directory with file: %s", name); invalidate_contents_checksum (self); g_hash_table_replace (self->files, g_strdup (name), g_strdup (checksum)); + return TRUE; +} - ret = TRUE; - out: - return ret; +/** + * ostree_mutable_tree_remove: + * @self: Tree + * @name: Name of file or subdirectory to remove + * @allow_noent: If @FALSE, an error will be thrown if @name does not exist in the tree + * @error: a #GError + * + * Remove the file or subdirectory named @name from the mutable tree @self. + */ +gboolean +ostree_mutable_tree_remove (OstreeMutableTree *self, + const char *name, + gboolean allow_noent, + GError **error) +{ + g_return_val_if_fail (name != NULL, FALSE); + + if (!ot_util_filename_validate (name, error)) + return FALSE; + + if (!_ostree_mutable_tree_make_whole (self, NULL, error)) + return FALSE; + + if (!g_hash_table_remove (self->files, name) && + !g_hash_table_remove (self->subdirs, name)) + { + if (allow_noent) + return TRUE; /* NB: early return */ + return set_error_noent (error, name); + } + + invalidate_contents_checksum (self); + return TRUE; } /** @@ -348,36 +374,29 @@ ostree_mutable_tree_ensure_dir (OstreeMutableTree *self, OstreeMutableTree **out_subdir, GError **error) { - gboolean ret = FALSE; - g_autoptr(OstreeMutableTree) ret_dir = NULL; - g_return_val_if_fail (name != NULL, FALSE); if (!ot_util_filename_validate (name, error)) - goto out; + return FALSE; if (!_ostree_mutable_tree_make_whole (self, NULL, error)) - goto out; + return FALSE; if (g_hash_table_lookup (self->files, name)) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Can't replace file with directory: %s", name); - goto out; - } + return glnx_throw (error, "Can't replace file with directory: %s", name); - ret_dir = ot_gobject_refz (g_hash_table_lookup (self->subdirs, name)); + g_autoptr(OstreeMutableTree) ret_dir = + ot_gobject_refz (g_hash_table_lookup (self->subdirs, name)); if (!ret_dir) { ret_dir = ostree_mutable_tree_new (); invalidate_contents_checksum (self); insert_child_mtree (self, name, g_object_ref (ret_dir)); } - - ret = TRUE; - ot_transfer_out_value (out_subdir, &ret_dir); - out: - return ret; + + if (out_subdir) + *out_subdir = g_steal_pointer (&ret_dir); + return TRUE; } gboolean @@ -387,29 +406,24 @@ ostree_mutable_tree_lookup (OstreeMutableTree *self, OstreeMutableTree **out_subdir, GError **error) { - gboolean ret = FALSE; - g_autoptr(OstreeMutableTree) ret_subdir = NULL; - g_autofree char *ret_file_checksum = NULL; - if (!_ostree_mutable_tree_make_whole (self, NULL, error)) - goto out; + return FALSE; - ret_subdir = ot_gobject_refz (g_hash_table_lookup (self->subdirs, name)); + g_autofree char *ret_file_checksum = NULL; + g_autoptr(OstreeMutableTree) ret_subdir = + ot_gobject_refz (g_hash_table_lookup (self->subdirs, name)); if (!ret_subdir) { ret_file_checksum = g_strdup (g_hash_table_lookup (self->files, name)); if (!ret_file_checksum) - { - set_error_noent (error, name); - goto out; - } + return set_error_noent (error, name); } - ret = TRUE; - ot_transfer_out_value (out_file_checksum, &ret_file_checksum); - ot_transfer_out_value (out_subdir, &ret_subdir); - out: - return ret; + if (out_file_checksum) + *out_file_checksum = g_steal_pointer (&ret_file_checksum); + if (out_subdir) + *out_subdir = g_steal_pointer (&ret_subdir); + return TRUE; } /** @@ -430,49 +444,38 @@ ostree_mutable_tree_ensure_parent_dirs (OstreeMutableTree *self, OstreeMutableTree **out_parent, GError **error) { - gboolean ret = FALSE; - int i; - OstreeMutableTree *subdir = self; /* nofree */ - g_autoptr(OstreeMutableTree) ret_parent = NULL; + g_assert (metadata_checksum != NULL); if (!_ostree_mutable_tree_make_whole (self, NULL, error)) - goto out; - - g_assert (metadata_checksum != NULL); + return FALSE; if (!self->metadata_checksum) ostree_mutable_tree_set_metadata_checksum (self, metadata_checksum); - for (i = 0; i+1 < split_path->len; i++) + OstreeMutableTree *subdir = self; /* nofree */ + for (guint i = 0; i+1 < split_path->len; i++) { OstreeMutableTree *next; const char *name = split_path->pdata[i]; if (g_hash_table_lookup (subdir->files, name)) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Can't replace file with directory: %s", name); - goto out; - } + return glnx_throw (error, "Can't replace file with directory: %s", name); next = g_hash_table_lookup (subdir->subdirs, name); - if (!next) + if (!next) { invalidate_contents_checksum (subdir); next = ostree_mutable_tree_new (); ostree_mutable_tree_set_metadata_checksum (next, metadata_checksum); insert_child_mtree (subdir, g_strdup (name), next); } - + subdir = next; } - ret_parent = g_object_ref (subdir); - - ret = TRUE; - ot_transfer_out_value (out_parent, &ret_parent); - out: - return ret; + if (out_parent) + *out_parent = g_object_ref (subdir); + return TRUE; } const char empty_tree_csum[] = "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"; diff --git a/src/libostree/ostree-mutable-tree.h b/src/libostree/ostree-mutable-tree.h index 4b7f853e..753f96e7 100644 --- a/src/libostree/ostree-mutable-tree.h +++ b/src/libostree/ostree-mutable-tree.h @@ -77,6 +77,12 @@ gboolean ostree_mutable_tree_replace_file (OstreeMutableTree *self, const char *checksum, GError **error); +_OSTREE_PUBLIC +gboolean ostree_mutable_tree_remove (OstreeMutableTree *self, + const char *name, + gboolean allow_noent, + GError **error); + _OSTREE_PUBLIC gboolean ostree_mutable_tree_ensure_dir (OstreeMutableTree *self, const char *name, diff --git a/src/libostree/ostree-repo-checkout.c b/src/libostree/ostree-repo-checkout.c index 6317c9e8..5ae79923 100644 --- a/src/libostree/ostree-repo-checkout.c +++ b/src/libostree/ostree-repo-checkout.c @@ -196,6 +196,7 @@ static gboolean create_file_copy_from_input_at (OstreeRepo *repo, OstreeRepoCheckoutAtOptions *options, CheckoutState *state, + const char *checksum, GFileInfo *file_info, GVariant *xattrs, GInputStream *input, @@ -358,8 +359,35 @@ create_file_copy_from_input_at (OstreeRepo *repo, replace_mode = GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST; break; case OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL: - /* We don't support copying in union identical */ - g_assert_not_reached (); + { + replace_mode = GLNX_LINK_TMPFILE_NOREPLACE; + struct stat dest_stbuf; + if (!glnx_fstatat_allow_noent (destination_dfd, destination_name, &dest_stbuf, + AT_SYMLINK_NOFOLLOW, error)) + return FALSE; + if (errno == 0) + { + /* We do a checksum comparison; see also equivalent code in + * checkout_file_hardlink(). + */ + OstreeChecksumFlags flags = 0; + if (repo->disable_xattrs) + flags |= OSTREE_CHECKSUM_FLAGS_IGNORE_XATTRS; + + g_autofree char *actual_checksum = NULL; + if (!ostree_checksum_file_at (destination_dfd, destination_name, + &dest_stbuf, OSTREE_OBJECT_TYPE_FILE, + flags, &actual_checksum, cancellable, error)) + return FALSE; + + if (g_str_equal (checksum, actual_checksum)) + return TRUE; + + /* Otherwise, fall through and do the link, we should + * get EEXIST. + */ + } + } break; } @@ -586,6 +614,7 @@ checkout_one_file_at (OstreeRepo *repo, const gboolean is_symlink = (g_file_info_get_file_type (source_info) == G_FILE_TYPE_SYMBOLIC_LINK); const gboolean is_whiteout = (!is_symlink && options->process_whiteouts && g_str_has_prefix (destination_name, WHITEOUT_PREFIX)); + const gboolean is_reg_zerosized = (!is_symlink && g_file_info_get_size (source_info) == 0); /* First, see if it's a Docker whiteout, * https://github.com/docker/docker/blob/1a714e76a2cb9008cd19609059e9988ff1660b78/pkg/archive/whiteouts.go @@ -604,6 +633,10 @@ checkout_one_file_at (OstreeRepo *repo, need_copy = FALSE; } + else if (options->force_copy_zerosized && is_reg_zerosized) + { + need_copy = TRUE; + } else if (!options->force_copy) { HardlinkResult hardlink_res = HARDLINK_RESULT_NOT_SUPPORTED; @@ -699,6 +732,7 @@ checkout_one_file_at (OstreeRepo *repo, if (can_cache && !is_whiteout && !is_symlink + && !is_reg_zerosized && need_copy && repo->mode == OSTREE_REPO_MODE_ARCHIVE && options->mode == OSTREE_REPO_CHECKOUT_MODE_USER) @@ -762,12 +796,12 @@ checkout_one_file_at (OstreeRepo *repo, * succeeded at hardlinking above. */ if (options->no_copy_fallback) - g_assert (is_bare_user_symlink); + g_assert (is_bare_user_symlink || is_reg_zerosized); if (!ostree_repo_load_file (repo, checksum, &input, NULL, &xattrs, cancellable, error)) return FALSE; - if (!create_file_copy_from_input_at (repo, options, state, source_info, xattrs, input, + if (!create_file_copy_from_input_at (repo, options, state, checksum, source_info, xattrs, input, destination_dfd, destination_name, cancellable, error)) return glnx_prefix_error (error, "Copy checkout of %s to %s", checksum, destination_name); diff --git a/src/libostree/ostree-repo-commit.c b/src/libostree/ostree-repo-commit.c index d464cd0a..134024b8 100644 --- a/src/libostree/ostree-repo-commit.c +++ b/src/libostree/ostree-repo-commit.c @@ -245,16 +245,7 @@ commit_loose_regfile_object (OstreeRepo *self, GCancellable *cancellable, GError **error) { - /* We may be writing as root to a non-root-owned repository; if so, - * automatically inherit the non-root ownership. - */ - if (self->mode == OSTREE_REPO_MODE_ARCHIVE - && self->target_owner_uid != -1) - { - if (fchown (tmpf->fd, self->target_owner_uid, self->target_owner_gid) < 0) - return glnx_throw_errno_prefix (error, "fchown"); - } - else if (self->mode == OSTREE_REPO_MODE_BARE) + if (self->mode == OSTREE_REPO_MODE_BARE) { if (TEMP_FAILURE_RETRY (fchown (tmpf->fd, uid, gid)) < 0) return glnx_throw_errno_prefix (error, "fchown"); @@ -1336,18 +1327,6 @@ write_metadata_object (OstreeRepo *self, gsize len; const guint8 *bufp = g_bytes_get_data (buf, &len); - /* Do the size warning here, to avoid warning for already extant metadata */ - if (G_UNLIKELY (len > OSTREE_MAX_METADATA_WARN_SIZE)) - { - g_autofree char *metasize = g_format_size (len); - g_autofree char *warnsize = g_format_size (OSTREE_MAX_METADATA_WARN_SIZE); - g_autofree char *maxsize = g_format_size (OSTREE_MAX_METADATA_SIZE); - g_warning ("metadata object %s is %s, which is larger than the warning threshold of %s." \ - " The hard limit on metadata size is %s. Put large content in the tree itself, not in metadata.", - actual_checksum, - metasize, warnsize, maxsize); - } - /* Write the metadata to a temporary file */ g_auto(GLnxTmpfile) tmpf = { 0, }; if (!glnx_open_tmpfile_linkable_at (commit_tmp_dfd (self), ".", O_WRONLY|O_CLOEXEC, @@ -1536,25 +1515,6 @@ devino_cache_lookup (OstreeRepo *self, return dev_ino_val->checksum; } -static guint64 -min_free_space_calculate_reserved_blocks (OstreeRepo *self, struct statvfs *stvfsbuf) -{ - guint64 reserved_blocks = 0; - - if (self->min_free_space_mb > 0) - { - reserved_blocks = (self->min_free_space_mb << 20) / stvfsbuf->f_bsize; - } - else if (self->min_free_space_percent > 0) - { - /* Convert fragment to blocks to compute the total */ - guint64 total_blocks = (stvfsbuf->f_frsize * stvfsbuf->f_blocks) / stvfsbuf->f_bsize; - reserved_blocks = ((double)total_blocks) * (self->min_free_space_percent/100.0); - } - - return reserved_blocks; -} - /** * ostree_repo_scan_hardlinks: * @self: An #OstreeRepo @@ -1626,6 +1586,7 @@ ostree_repo_prepare_transaction (OstreeRepo *self, GError **error) { g_autoptr(_OstreeRepoAutoTransaction) txn = NULL; + guint64 reserved_bytes = 0; g_return_val_if_fail (self->in_transaction == FALSE, FALSE); @@ -1650,11 +1611,17 @@ ostree_repo_prepare_transaction (OstreeRepo *self, g_mutex_lock (&self->txn_lock); self->txn.blocksize = stvfsbuf.f_bsize; - guint64 reserved_blocks = min_free_space_calculate_reserved_blocks (self, &stvfsbuf); + if (!ostree_repo_get_min_free_space_bytes (self, &reserved_bytes, error)) + { + g_mutex_unlock (&self->txn_lock); + return FALSE; + } + self->reserved_blocks = reserved_bytes / self->txn.blocksize; + /* Use the appropriate free block count if we're unprivileged */ guint64 bfree = (getuid () != 0 ? stvfsbuf.f_bavail : stvfsbuf.f_bfree); - if (bfree > reserved_blocks) - self->txn.max_blocks = bfree - reserved_blocks; + if (bfree > self->reserved_blocks) + self->txn.max_blocks = bfree - self->reserved_blocks; else { self->cleanup_stagedir = TRUE; @@ -2290,25 +2257,6 @@ ostree_repo_abort_transaction (OstreeRepo *self, return TRUE; } -/* These limits were introduced since in some cases we may be processing - * malicious metadata, and we want to make disk space exhaustion attacks harder. - */ -static gboolean -metadata_size_valid (OstreeObjectType objtype, - gsize len, - GError **error) -{ - if (G_UNLIKELY (len > OSTREE_MAX_METADATA_SIZE)) - { - g_autofree char *input_bytes = g_format_size (len); - g_autofree char *max_bytes = g_format_size (OSTREE_MAX_METADATA_SIZE); - return glnx_throw (error, "Metadata object of type '%s' is %s; maximum metadata size is %s", - ostree_object_type_to_string (objtype), input_bytes, max_bytes); - } - - return TRUE; -} - /** * ostree_repo_write_metadata: * @self: Repo @@ -2361,9 +2309,6 @@ ostree_repo_write_metadata (OstreeRepo *self, normalized = g_variant_get_normal_form (object); } - if (!metadata_size_valid (objtype, g_variant_get_size (normalized), error)) - return FALSE; - /* For untrusted objects, verify their structure here */ if (expected_checksum) { @@ -2401,9 +2346,6 @@ ostree_repo_write_metadata_stream_trusted (OstreeRepo *self, GCancellable *cancellable, GError **error) { - if (length > 0 && !metadata_size_valid (objtype, length, error)) - return FALSE; - /* This is all pretty ridiculous, but we're keeping this API for backwards * compatibility, it doesn't really need to be fast. */ @@ -4305,11 +4247,12 @@ import_one_object_direct (OstreeRepo *dest_repo, } /* Don't want to copy xattrs for archive repos, nor for - * bare-user-only. + * bare-user-only. We also only do this for content + * objects. */ const gboolean src_is_bare_or_bare_user = G_IN_SET (src_repo->mode, OSTREE_REPO_MODE_BARE, OSTREE_REPO_MODE_BARE_USER); - if (src_is_bare_or_bare_user) + if (src_is_bare_or_bare_user && !OSTREE_OBJECT_TYPE_IS_META(objtype)) { g_autoptr(GVariant) xattrs = NULL; diff --git a/src/libostree/ostree-repo-finder-avahi.c b/src/libostree/ostree-repo-finder-avahi.c index bc383769..f8d1f878 100644 --- a/src/libostree/ostree-repo-finder-avahi.c +++ b/src/libostree/ostree-repo-finder-avahi.c @@ -509,6 +509,16 @@ fill_refs_and_checksums_from_summary (GVariant *summary, return TRUE; } +static gboolean +remove_null_checksum_refs_cb (gpointer key, + gpointer value, + gpointer user_data) +{ + const char *checksum = value; + + return (checksum == NULL); +} + /* Given a summary file (@summary_bytes), extract the refs it lists, and use that * to fill in the checksums in the @supported_ref_to_checksum map. This includes * the main refs list in the summary, and the map of collection IDs to further @@ -518,14 +528,13 @@ fill_refs_and_checksums_from_summary (GVariant *summary, * set and %FALSE will be returned. If the intersection of the summary file refs * and the keys in @supported_ref_to_checksum is empty, an error is set. */ static gboolean -get_refs_and_checksums_from_summary (GBytes *summary_bytes, - GHashTable *supported_ref_to_checksum /* (element-type OstreeCollectionRef utf8) */, - GError **error) +get_refs_and_checksums_from_summary (GBytes *summary_bytes, + GHashTable *supported_ref_to_checksum /* (element-type OstreeCollectionRef utf8) */, + OstreeRemote *remote, + GError **error) { g_autoptr(GVariant) summary = g_variant_ref_sink (g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT, summary_bytes, FALSE)); - GHashTableIter iter; - const OstreeCollectionRef *ref; - const gchar *checksum; + guint removed_refs; if (!g_variant_is_normal_form (summary)) { @@ -544,20 +553,20 @@ get_refs_and_checksums_from_summary (GBytes *summary_bytes, if (!fill_refs_and_checksums_from_summary (summary, supported_ref_to_checksum, error)) return FALSE; - /* Check that at least one of the refs has a non-%NULL checksum set, otherwise - * we can discard this peer. */ - g_hash_table_iter_init (&iter, supported_ref_to_checksum); - while (g_hash_table_iter_next (&iter, - (gpointer *) &ref, - (gpointer *) &checksum)) + removed_refs = g_hash_table_foreach_remove (supported_ref_to_checksum, remove_null_checksum_refs_cb, NULL); + if (removed_refs > 0) + g_debug ("Removed %d refs from the list resolved from ‘%s’ (possibly bloom filter false positives)", + removed_refs, remote->name); + + /* If none of the refs had a non-%NULL checksum set, we can discard this peer. */ + if (g_hash_table_size (supported_ref_to_checksum) == 0) { - if (checksum != NULL) - return TRUE; + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "No matching refs were found in the summary file"); + return FALSE; } - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, - "No matching refs were found in the summary file"); - return FALSE; + return TRUE; } /* Download the summary file from @remote, and return the bytes of the file in @@ -661,7 +670,7 @@ get_checksums (OstreeRepoFinderAvahi *finder, return FALSE; } - return get_refs_and_checksums_from_summary (summary_bytes, supported_ref_to_checksum, error); + return get_refs_and_checksums_from_summary (summary_bytes, supported_ref_to_checksum, remote, error); } /* Build some #OstreeRepoFinderResults out of the given #OstreeAvahiService by diff --git a/src/libostree/ostree-repo-private.h b/src/libostree/ostree-repo-private.h index 99eaf494..40be7263 100644 --- a/src/libostree/ostree-repo-private.h +++ b/src/libostree/ostree-repo-private.h @@ -149,10 +149,9 @@ struct OstreeRepo { dev_t device; ino_t inode; uid_t owner_uid; /* Cache of repo's owner uid */ - uid_t target_owner_uid; /* Ensure files are chowned to this uid/gid */ - gid_t target_owner_gid; guint min_free_space_percent; /* See the min-free-space-percent config option */ guint64 min_free_space_mb; /* See the min-free-space-size config option */ + guint64 reserved_blocks; gboolean cleanup_stagedir; guint test_error_flags; /* OstreeRepoTestErrorFlags */ @@ -169,6 +168,7 @@ struct OstreeRepo { gint lock_timeout_seconds; guint64 payload_link_threshold; gint fs_support_reflink; /* The underlying filesystem has support for ioctl (FICLONE..) */ + gchar **repo_finders; OstreeRepo *parent_repo; }; diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c index c6b70ffb..1efb38b3 100644 --- a/src/libostree/ostree-repo-pull.c +++ b/src/libostree/ostree-repo-pull.c @@ -150,6 +150,7 @@ typedef struct { gboolean timestamp_check; /* Verify commit timestamps */ int maxdepth; + guint64 max_metadata_size; guint64 start_time; gboolean is_mirror; @@ -2193,7 +2194,7 @@ start_fetch (OtPullData *pull_data, if (expected_max_size_p) expected_max_size = *expected_max_size_p; else if (OSTREE_OBJECT_TYPE_IS_META (objtype)) - expected_max_size = OSTREE_MAX_METADATA_SIZE; + expected_max_size = pull_data->max_metadata_size; else expected_max_size = 0; @@ -3488,6 +3489,7 @@ initiate_request (OtPullData *pull_data, * * require-static-deltas (b): Require static deltas * * override-commit-ids (as): Array of specific commit IDs to fetch for refs * * timestamp-check (b): Verify commit timestamps are newer than current (when pulling via ref); Since: 2017.11 + * * metadata-size-restriction (t): Restrict metadata objects to a maximum number of bytes; 0 to disable. Since: 2018.9 * * 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 * * inherit-transaction (b): Don't initiate, finish or abort a transaction, useful to do multiple pulls in one transaction. @@ -3543,6 +3545,9 @@ ostree_repo_pull_with_options (OstreeRepo *self, */ const char *the_ref_to_fetch = NULL; + /* Default */ + pull_data->max_metadata_size = OSTREE_MAX_METADATA_SIZE; + if (options) { int flags_i = OSTREE_REPO_PULL_FLAGS_NONE; @@ -3570,6 +3575,7 @@ ostree_repo_pull_with_options (OstreeRepo *self, (void) g_variant_lookup (options, "update-frequency", "u", &update_frequency); (void) g_variant_lookup (options, "localcache-repos", "^a&s", &opt_localcache_repos); (void) g_variant_lookup (options, "timestamp-check", "b", &pull_data->timestamp_check); + (void) g_variant_lookup (options, "max-metadata-size", "t", &pull_data->max_metadata_size); (void) g_variant_lookup (options, "append-user-agent", "s", &pull_data->append_user_agent); opt_n_network_retries_set = g_variant_lookup (options, "n-network-retries", "u", &pull_data->n_network_retries); @@ -4993,45 +4999,56 @@ ostree_repo_find_remotes_async (OstreeRepo *self, /* Are we using #OstreeRepoFinders provided by the user, or the defaults? */ if (finders == NULL) { + guint finder_index = 0; #ifdef HAVE_AVAHI + guint avahi_index; GMainContext *context = g_main_context_get_thread_default (); g_autoptr(GError) local_error = NULL; #endif /* HAVE_AVAHI */ - finder_config = OSTREE_REPO_FINDER (ostree_repo_finder_config_new ()); - finder_mount = OSTREE_REPO_FINDER (ostree_repo_finder_mount_new (NULL)); + if (g_strv_contains ((const char * const *)self->repo_finders, "config")) + default_finders[finder_index++] = finder_config = OSTREE_REPO_FINDER (ostree_repo_finder_config_new ()); + + if (g_strv_contains ((const char * const *)self->repo_finders, "mount")) + default_finders[finder_index++] = finder_mount = OSTREE_REPO_FINDER (ostree_repo_finder_mount_new (NULL)); + #ifdef HAVE_AVAHI - finder_avahi = OSTREE_REPO_FINDER (ostree_repo_finder_avahi_new (context)); + if (g_strv_contains ((const char * const *)self->repo_finders, "lan")) + { + avahi_index = finder_index; + default_finders[finder_index++] = finder_avahi = OSTREE_REPO_FINDER (ostree_repo_finder_avahi_new (context)); + } #endif /* HAVE_AVAHI */ - default_finders[0] = finder_config; - default_finders[1] = finder_mount; - default_finders[2] = finder_avahi; - + /* self->repo_finders is guaranteed to be non-empty */ + g_assert (default_finders != NULL); finders = default_finders; #ifdef HAVE_AVAHI - ostree_repo_finder_avahi_start (OSTREE_REPO_FINDER_AVAHI (finder_avahi), - &local_error); - - if (local_error != NULL) + if (finder_avahi != NULL) { - /* See ostree-repo-finder-avahi.c:ostree_repo_finder_avahi_start, we - * intentionally throw this so as to distinguish between the Avahi - * finder failing because the Avahi daemon wasn't running and - * the Avahi finder failing because of some actual error. - * - * We need to distinguish between g_debug and g_warning here because - * unit tests that use this code may set G_DEBUG=fatal-warnings which - * would cause client code to abort if a warning were emitted. - */ - if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - g_debug ("Avahi finder failed under normal operation; removing it: %s", local_error->message); - else - g_warning ("Avahi finder failed abnormally; removing it: %s", local_error->message); + ostree_repo_finder_avahi_start (OSTREE_REPO_FINDER_AVAHI (finder_avahi), + &local_error); - default_finders[2] = NULL; - g_clear_object (&finder_avahi); + if (local_error != NULL) + { + /* See ostree-repo-finder-avahi.c:ostree_repo_finder_avahi_start, we + * intentionally throw this so as to distinguish between the Avahi + * finder failing because the Avahi daemon wasn't running and + * the Avahi finder failing because of some actual error. + * + * We need to distinguish between g_debug and g_warning here because + * unit tests that use this code may set G_DEBUG=fatal-warnings which + * would cause client code to abort if a warning were emitted. + */ + if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) + g_debug ("Avahi finder failed under normal operation; removing it: %s", local_error->message); + else + g_warning ("Avahi finder failed abnormally; removing it: %s", local_error->message); + + default_finders[avahi_index] = NULL; + g_clear_object (&finder_avahi); + } } #endif /* HAVE_AVAHI */ } diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 18736c24..fa3c2a94 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -90,17 +90,19 @@ G_STATIC_ASSERT(sizeof(OstreeRepoPruneOptions) == * The #OstreeRepo is like git, a content-addressed object store. * Unlike git, it records uid, gid, and extended attributes. * - * There are three possible "modes" for an #OstreeRepo; - * %OSTREE_REPO_MODE_BARE is very simple - content files are - * represented exactly as they are, and checkouts are just hardlinks. - * %OSTREE_REPO_MODE_BARE_USER is similar, except the uid/gids are not - * set on the files, and checkouts as hardlinks hardlinks work only for user checkouts. - * A %OSTREE_REPO_MODE_ARCHIVE_Z2 repository in contrast stores - * content files zlib-compressed. It is suitable for non-root-owned + * There are four possible "modes" for an #OstreeRepo; %OSTREE_REPO_MODE_BARE + * is very simple - content files are represented exactly as they are, and + * checkouts are just hardlinks. %OSTREE_REPO_MODE_BARE_USER is similar, except + * the uid/gids are not set on the files, and checkouts as hardlinks work only + * for user checkouts. %OSTREE_REPO_MODE_BARE_USER_ONLY is the same as + * BARE_USER, but all metadata is not stored, so it can only be used for user + * checkouts. This mode does not require xattrs. A %OSTREE_REPO_MODE_ARCHIVE + * (also known as %OSTREE_REPO_MODE_ARCHIVE_Z2) repository in contrast stores + * content files zlib-compressed. It is suitable for non-root-owned * repositories that can be served via a static HTTP server. * * Creating an #OstreeRepo does not invoke any file I/O, and thus needs - * to be initialized, either from an existing contents or with a new + * to be initialized, either from existing contents or as a new * repository. If you have an existing repo, use ostree_repo_open() * to load it from disk and check its validity. To initialize a new * repository in the given filepath, use ostree_repo_create() instead. @@ -452,9 +454,9 @@ pop_repo_lock (OstreeRepo *self, * state is not changed and the stack is simply updated. * * ostree_repo_lock_push() waits for the lock depending on the repository's - * lock-timeout configuration. When lock-timeout is -1, a blocking lock is + * lock-timeout-secs configuration. When lock-timeout-secs is -1, a blocking lock is * attempted. Otherwise, the lock is taken non-blocking and - * ostree_repo_lock_push() will sleep synchronously up to lock-timeout seconds + * ostree_repo_lock_push() will sleep synchronously up to lock-timeout-secs seconds * attempting to acquire the lock. If the lock cannot be acquired within the * timeout, a %G_IO_ERROR_WOULD_BLOCK error is returned. * @@ -542,9 +544,9 @@ _ostree_repo_lock_push (OstreeRepo *self, * lock. * * ostree_repo_lock_pop() waits for the lock depending on the repository's - * lock-timeout configuration. When lock-timeout is -1, a blocking lock is + * lock-timeout-secs configuration. When lock-timeout-secs is -1, a blocking lock is * attempted. Otherwise, the lock is removed non-blocking and - * ostree_repo_lock_pop() will sleep synchronously up to lock-timeout seconds + * ostree_repo_lock_pop() will sleep synchronously up to lock-timeout-secs seconds * attempting to remove the lock. If the lock cannot be removed within the * timeout, a %G_IO_ERROR_WOULD_BLOCK error is returned. * @@ -1033,6 +1035,7 @@ ostree_repo_finalize (GObject *object) g_mutex_clear (&self->cache_lock); g_mutex_clear (&self->txn_lock); g_free (self->collection_id); + g_strfreev (self->repo_finders); g_clear_pointer (&self->remotes, g_hash_table_destroy); g_mutex_clear (&self->remotes_lock); @@ -2653,6 +2656,37 @@ get_remotes_d_dir (OstreeRepo *self, return g_file_resolve_relative_path (sysroot, SYSCONF_REMOTES); } +static gboolean +min_free_space_calculate_reserved_bytes (OstreeRepo *self, guint64 *bytes, GError **error) +{ + guint64 reserved_bytes = 0; + + struct statvfs stvfsbuf; + if (TEMP_FAILURE_RETRY (fstatvfs (self->repo_dir_fd, &stvfsbuf)) < 0) + return glnx_throw_errno_prefix (error, "fstatvfs"); + + if (self->min_free_space_mb > 0) + { + if (self->min_free_space_mb > (G_MAXUINT64 >> 20)) + return glnx_throw (error, "min-free-space value is greater than the maximum allowed value of %" G_GUINT64_FORMAT " bytes", + (G_MAXUINT64 >> 20)); + + reserved_bytes = self->min_free_space_mb << 20; + } + else if (self->min_free_space_percent > 0) + { + if (stvfsbuf.f_frsize > (G_MAXUINT64 / stvfsbuf.f_blocks)) + return glnx_throw (error, "Filesystem's size is greater than the maximum allowed value of %" G_GUINT64_FORMAT " bytes", + (G_MAXUINT64 / stvfsbuf.f_blocks)); + + guint64 total_bytes = (stvfsbuf.f_frsize * stvfsbuf.f_blocks); + reserved_bytes = ((double)total_bytes) * (self->min_free_space_percent/100.0); + } + + *bytes = reserved_bytes; + return TRUE; +} + static gboolean min_free_space_size_validate_and_convert (OstreeRepo *self, const char *min_free_space_size_str, @@ -2799,7 +2833,7 @@ reload_core_config (OstreeRepo *self, &lock_timeout_seconds, error)) return FALSE; - self->lock_timeout_seconds = g_ascii_strtoull (lock_timeout_seconds, NULL, 10); + self->lock_timeout_seconds = g_ascii_strtoll (lock_timeout_seconds, NULL, 10); } } @@ -2905,6 +2939,40 @@ reload_core_config (OstreeRepo *self, self->payload_link_threshold = g_ascii_strtoull (payload_threshold, NULL, 10); } + { g_auto(GStrv) configured_finders = NULL; + g_autoptr(GError) local_error = NULL; + + configured_finders = g_key_file_get_string_list (self->config, "core", "default-repo-finders", + NULL, &local_error); + if (g_error_matches (local_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) + g_clear_error (&local_error); + else if (local_error != NULL) + { + g_propagate_error (error, g_steal_pointer (&local_error)); + return FALSE; + } + + if (configured_finders != NULL && *configured_finders == NULL) + return glnx_throw (error, "Invalid empty default-repo-finders configuration"); + + for (char **iter = configured_finders; iter && *iter; iter++) + { + const char *repo_finder = *iter; + + if (strcmp (repo_finder, "config") != 0 && + strcmp (repo_finder, "lan") != 0 && + strcmp (repo_finder, "mount") != 0) + return glnx_throw (error, "Invalid configured repo-finder '%s'", repo_finder); + } + + /* Fall back to a default set of finders */ + if (configured_finders == NULL) + configured_finders = g_strsplit ("config;mount", ";", -1); + + g_clear_pointer (&self->repo_finders, g_strfreev); + self->repo_finders = g_steal_pointer (&configured_finders); + } + return TRUE; } @@ -3047,16 +3115,6 @@ ostree_repo_open (OstreeRepo *self, return FALSE; self->owner_uid = stbuf.st_uid; - if (stbuf.st_uid != getuid () || stbuf.st_gid != getgid ()) - { - self->target_owner_uid = stbuf.st_uid; - self->target_owner_gid = stbuf.st_gid; - } - else - { - self->target_owner_uid = self->target_owner_gid = -1; - } - if (self->writable) { /* Always try to recreate the tmpdir to be nice to people @@ -3292,6 +3350,30 @@ ostree_repo_get_mode (OstreeRepo *self) return self->mode; } +/** + * ostree_repo_get_min_free_space: + * @self: Repo + * @out_reserved_bytes: (out): Location to store the result + * @error: Return location for a #GError + * + * It can be used to query the value (in bytes) of min-free-space-* config option. + * + * Returns: %TRUE on success, %FALSE otherwise. + * Since: 2018.9 + */ +gboolean +ostree_repo_get_min_free_space_bytes (OstreeRepo *self, guint64 *out_reserved_bytes, GError **error) +{ + g_return_val_if_fail (OSTREE_IS_REPO (self), FALSE); + g_return_val_if_fail (out_reserved_bytes != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!min_free_space_calculate_reserved_bytes (self, out_reserved_bytes, error)) + return glnx_prefix_error (error, "Error calculating min-free-space bytes"); + + return TRUE; +} + /** * ostree_repo_get_parent: * @self: Repo @@ -5868,3 +5950,22 @@ ostree_repo_set_collection_id (OstreeRepo *self, return TRUE; } + +/** + * ostree_repo_get_default_repo_finders: + * @self: an #OstreeRepo + * + * Get the set of default repo finders configured. See the documentation for + * the "core.default-repo-finders" config key. + * + * Returns: (array zero-terminated=1) (element-type utf8): + * %NULL-terminated array of strings. + * Since: 2018.9 + */ +const gchar * const * +ostree_repo_get_default_repo_finders (OstreeRepo *self) +{ + g_return_val_if_fail (OSTREE_IS_REPO (self), NULL); + + return (const gchar * const *)self->repo_finders; +} diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index 60fa3d3e..829164ba 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -112,6 +112,9 @@ gboolean ostree_repo_set_collection_id (OstreeRepo *self, const gchar *collection_id, GError **error); +_OSTREE_PUBLIC +const gchar * const * ostree_repo_get_default_repo_finders (OstreeRepo *self); + _OSTREE_PUBLIC GFile * ostree_repo_get_path (OstreeRepo *self); @@ -127,6 +130,10 @@ gboolean ostree_repo_equal (OstreeRepo *a, _OSTREE_PUBLIC OstreeRepoMode ostree_repo_get_mode (OstreeRepo *self); +_OSTREE_PUBLIC +gboolean ostree_repo_get_min_free_space_bytes (OstreeRepo *self, + guint64 *out_reserved_bytes, + GError **error); _OSTREE_PUBLIC GKeyFile * ostree_repo_get_config (OstreeRepo *self); @@ -929,7 +936,8 @@ typedef struct { gboolean no_copy_fallback; gboolean force_copy; /* Since: 2017.6 */ gboolean bareuseronly_dirs; /* Since: 2017.7 */ - gboolean unused_bools[5]; + gboolean force_copy_zerosized; /* Since: 2018.9 */ + gboolean unused_bools[4]; /* 4 byte hole on 64 bit */ const char *subpath; @@ -1395,6 +1403,29 @@ gboolean ostree_repo_regenerate_summary (OstreeRepo *self, */ #define OSTREE_REPO_METADATA_REF "ostree-metadata" +/** + * OSTREE_META_KEY_DEPLOY_COLLECTION_ID: + * + * GVariant type `s`. This key can be used in the repo metadata which is stored + * in OSTREE_REPO_METADATA_REF as well as in the summary. The semantics of this + * are that the remote repository wants clients to update their remote config + * to add this collection ID (clients can't do P2P operations involving a + * remote without a collection ID configured on it, even if one is configured + * on the server side). Clients must never change or remove a collection ID + * already set in their remote config. + * + * Currently, OSTree does not implement changing a remote config based on this + * key, but it may do so in a later release, and until then clients such as + * Flatpak may implement it. + * + * This is a replacement for the similar metadata key implemented by flatpak, + * `xa.collection-id`, which is now deprecated as clients which supported it had + * bugs with their P2P implementations. + * + * Since: 2018.9 + */ +#define OSTREE_META_KEY_DEPLOY_COLLECTION_ID "ostree.deploy-collection-id" + G_END_DECLS diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index 8198e191..b16f65b3 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -49,9 +49,10 @@ #include "libglnx.h" #ifdef HAVE_LIBSYSTEMD -#define OSTREE_VARRELABEL_ID SD_ID128_MAKE(da,67,9b,08,ac,d3,45,04,b7,89,d9,6f,81,8e,a7,81) -#define OSTREE_CONFIGMERGE_ID SD_ID128_MAKE(d3,86,3b,ae,c1,3e,44,49,ab,03,84,68,4a,8a,f3,a7) -#define OSTREE_DEPLOYMENT_COMPLETE_ID SD_ID128_MAKE(dd,44,0e,3e,54,90,83,b6,3d,0e,fc,7d,c1,52,55,f1) +#define OSTREE_VARRELABEL_ID SD_ID128_MAKE(da,67,9b,08,ac,d3,45,04,b7,89,d9,6f,81,8e,a7,81) +#define OSTREE_CONFIGMERGE_ID SD_ID128_MAKE(d3,86,3b,ae,c1,3e,44,49,ab,03,84,68,4a,8a,f3,a7) +#define OSTREE_DEPLOYMENT_COMPLETE_ID SD_ID128_MAKE(dd,44,0e,3e,54,90,83,b6,3d,0e,fc,7d,c1,52,55,f1) +#define OSTREE_DEPLOYMENT_FINALIZING_ID SD_ID128_MAKE(e8,64,6c,d6,3d,ff,46,25,b7,79,09,a8,e7,a4,09,94) #endif /* @@ -2509,6 +2510,47 @@ sysroot_initialize_deployment (OstreeSysroot *self, return TRUE; } +/* Get a directory fd for the /var of @deployment. + * Before we supported having /var be a separate mount point, + * this was easy. However, as https://github.com/ostreedev/ostree/issues/1729 + * raised, in the primary case where we're + * doing a new deployment for the booted stateroot, + * we need to use /var/. This code doesn't correctly + * handle the case of `ostree admin --sysroot upgrade`, + * nor (relatedly) the case of upgrading a separate stateroot. + */ +static gboolean +get_var_dfd (OstreeSysroot *self, + int osdeploy_dfd, + OstreeDeployment *deployment, + int *ret_fd, + GError **error) +{ + const char *booted_stateroot = + self->booted_deployment ? ostree_deployment_get_osname (self->booted_deployment) : NULL; + + int base_dfd; + const char *base_path; + /* The common case is when we're doing a new deployment for the same stateroot (osname). + * If we have a separate mounted /var, then we need to use it - the /var in the + * stateroot will probably just be an empty directory. + * + * If the stateroot doesn't match, just fall back to /var in the target's stateroot. + */ + if (g_strcmp0 (booted_stateroot, ostree_deployment_get_osname (deployment)) == 0) + { + base_dfd = AT_FDCWD; + base_path = "/var"; + } + else + { + base_dfd = osdeploy_dfd; + base_path = "var"; + } + + return glnx_opendirat (base_dfd, base_path, TRUE, ret_fd, error); +} + static gboolean sysroot_finalize_deployment (OstreeSysroot *self, OstreeDeployment *deployment, @@ -2547,6 +2589,9 @@ sysroot_finalize_deployment (OstreeSysroot *self, glnx_autofd int os_deploy_dfd = -1; if (!glnx_opendirat (self->sysroot_fd, osdeploypath, TRUE, &os_deploy_dfd, error)) return FALSE; + glnx_autofd int var_dfd = -1; + if (!get_var_dfd (self, os_deploy_dfd, deployment, &var_dfd, error)) + return FALSE; /* Ensure that the new deployment does not have /etc/.updated or * /var/.updated so that systemd ConditionNeedsUpdate=/etc|/var services run @@ -2554,7 +2599,7 @@ sysroot_finalize_deployment (OstreeSysroot *self, */ if (!ot_ensure_unlinked_at (deployment_dfd, "etc/.updated", error)) return FALSE; - if (!ot_ensure_unlinked_at (os_deploy_dfd, "var/.updated", error)) + if (!ot_ensure_unlinked_at (var_dfd, ".updated", error)) return FALSE; g_autoptr(OstreeSePolicy) sepolicy = ostree_sepolicy_new_at (deployment_dfd, cancellable, error); @@ -2716,6 +2761,10 @@ ostree_sysroot_stage_tree (OstreeSysroot *self, if (booted_deployment == NULL) return glnx_throw (error, "Cannot stage a deployment when not currently booted into an OSTree system"); + /* This is used by the testsuite to exercise the path unit, until it becomes the default + * (which is pending on the preset making it everywhere). */ + if ((self->debug_flags & OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH) == 0) + { /* This is a bit of a hack. When adding a new service we have to end up getting * into the presets for downstream distros; see e.g. https://src.fedoraproject.org/rpms/ostree/pull-request/7 * @@ -2728,6 +2777,11 @@ ostree_sysroot_stage_tree (OstreeSysroot *self, return FALSE; if (!g_spawn_check_exit_status (estatus, error)) return FALSE; + } + else + { + g_print ("test-staged-path: Not running `systemctl start`\n"); + } /* OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH */ g_autoptr(OstreeDeployment) deployment = NULL; if (!sysroot_initialize_deployment (self, osname, revision, origin, override_kernel_argv, @@ -2818,6 +2872,21 @@ _ostree_sysroot_finalize_staged (OstreeSysroot *self, if (!self->staged_deployment) return TRUE; + /* Notice we send this *after* the trivial `return TRUE` above; this msg implies we've + * committed to finalizing the deployment. */ +#ifdef HAVE_LIBSYSTEMD + sd_journal_send ("MESSAGE_ID=" SD_ID128_FORMAT_STR, + SD_ID128_FORMAT_VAL(OSTREE_DEPLOYMENT_FINALIZING_ID), + "MESSAGE=Finalizing staged deployment", + "OSTREE_OSNAME=%s", + ostree_deployment_get_osname (self->staged_deployment), + "OSTREE_CHECKSUM=%s", + ostree_deployment_get_csum (self->staged_deployment), + "OSTREE_DEPLOYSERIAL=%u", + ostree_deployment_get_deployserial (self->staged_deployment), + NULL); +#endif + g_assert (self->staged_deployment_data); g_autoptr(OstreeDeployment) merge_deployment = NULL; diff --git a/src/libostree/ostree-sysroot-private.h b/src/libostree/ostree-sysroot-private.h index 16cca5a1..9da6d4c9 100644 --- a/src/libostree/ostree-sysroot-private.h +++ b/src/libostree/ostree-sysroot-private.h @@ -36,6 +36,9 @@ typedef enum { OSTREE_SYSROOT_DEBUG_NO_XATTRS = 1 << 1, /* https://github.com/ostreedev/ostree/pull/1049 */ OSTREE_SYSROOT_DEBUG_TEST_FIFREEZE = 1 << 2, + /* This is a temporary flag until we fully drop the explicit `systemctl start + * ostree-finalize-staged.service` so that tests can exercise the new path unit. */ + OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH = 1 << 3, } OstreeSysrootDebugFlags; /** diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index 4b2c654c..84c12301 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -189,6 +189,7 @@ ostree_sysroot_init (OstreeSysroot *self) { "mutable-deployments", OSTREE_SYSROOT_DEBUG_MUTABLE_DEPLOYMENTS }, { "test-fifreeze", OSTREE_SYSROOT_DEBUG_TEST_FIFREEZE }, { "no-xattrs", OSTREE_SYSROOT_DEBUG_NO_XATTRS }, + { "test-staged-path", OSTREE_SYSROOT_DEBUG_TEST_STAGED_PATH }, }; self->debug_flags = g_parse_debug_string (g_getenv ("OSTREE_SYSROOT_DEBUG"), @@ -376,12 +377,11 @@ _ostree_sysroot_read_current_subbootversion (OstreeSysroot *self, g_autofree char *ostree_bootdir_name = g_strdup_printf ("ostree/boot.%d", bootversion); struct stat stbuf; - if (fstatat (self->sysroot_fd, ostree_bootdir_name, &stbuf, AT_SYMLINK_NOFOLLOW) != 0) + if (!glnx_fstatat_allow_noent (self->sysroot_fd, ostree_bootdir_name, &stbuf, AT_SYMLINK_NOFOLLOW, error)) + return FALSE; + if (errno == ENOENT) { - if (errno == ENOENT) - *out_subbootversion = 0; - else - return glnx_throw_errno (error); + *out_subbootversion = 0; } else { @@ -499,10 +499,10 @@ read_current_bootversion (OstreeSysroot *self, int ret_bootversion; struct stat stbuf; - if (fstatat (self->sysroot_fd, "boot/loader", &stbuf, AT_SYMLINK_NOFOLLOW) != 0) + if (!glnx_fstatat_allow_noent (self->sysroot_fd, "boot/loader", &stbuf, AT_SYMLINK_NOFOLLOW, error)) + return FALSE; + if (errno == ENOENT) { - if (errno != ENOENT) - return glnx_throw_errno (error); ret_bootversion = 0; } else @@ -613,6 +613,10 @@ parse_deployment (OstreeSysroot *self, error)) return FALSE; + g_autofree char *errprefix = + g_strdup_printf ("Parsing deployment %i in stateroot '%s'", treebootserial, osname); + GLNX_AUTO_PREFIX_ERROR(errprefix, error); + const char *relative_boot_link = boot_link; if (*relative_boot_link == '/') relative_boot_link++; @@ -976,11 +980,12 @@ ostree_sysroot_load_if_changed (OstreeSysroot *self, /* Otherwise - check for /sysroot which should only exist in a deployment, * not in ${sysroot} (a metavariable for the real physical root). */ - else if (fstatat (self->sysroot_fd, "sysroot", &stbuf, 0) < 0) + else { - if (errno != ENOENT) - return glnx_throw_errno_prefix (error, "fstatat"); - self->is_physical = TRUE; + if (!glnx_fstatat_allow_noent (self->sysroot_fd, "sysroot", &stbuf, 0, error)) + return FALSE; + if (errno == ENOENT) + self->is_physical = TRUE; } /* Otherwise, the default is FALSE */ } diff --git a/src/libostree/ostree-version.h b/src/libostree/ostree-version.h index 4a754ead..23c120c4 100644 --- a/src/libostree/ostree-version.h +++ b/src/libostree/ostree-version.h @@ -43,7 +43,7 @@ * * Since: 2017.4 */ -#define OSTREE_RELEASE_VERSION (8) +#define OSTREE_RELEASE_VERSION (9) /** * OSTREE_VERSION @@ -52,7 +52,7 @@ * * Since: 2017.4 */ -#define OSTREE_VERSION (2018.8) +#define OSTREE_VERSION (2018.9) /** * OSTREE_VERSION_S: @@ -62,7 +62,7 @@ * * Since: 2017.4 */ -#define OSTREE_VERSION_S "2018.8" +#define OSTREE_VERSION_S "2018.9" #define OSTREE_ENCODE_VERSION(year,release) \ ((year) << 16 | (release)) diff --git a/src/ostree/ot-builtin-checkout.c b/src/ostree/ot-builtin-checkout.c index e7d6a634..1519e34e 100644 --- a/src/ostree/ot-builtin-checkout.c +++ b/src/ostree/ot-builtin-checkout.c @@ -45,6 +45,7 @@ static char *opt_from_file; static gboolean opt_disable_fsync; static gboolean opt_require_hardlinks; static gboolean opt_force_copy; +static gboolean opt_force_copy_zerosized; static gboolean opt_bareuseronly_dirs; static char *opt_skiplist_file; static char *opt_selinux_policy; @@ -84,6 +85,7 @@ static GOptionEntry options[] = { { "from-file", 0, 0, G_OPTION_ARG_STRING, &opt_from_file, "Process many checkouts from input file", "FILE" }, { "fsync", 0, 0, G_OPTION_ARG_CALLBACK, parse_fsync_cb, "Specify how to invoke fsync()", "POLICY" }, { "require-hardlinks", 'H', 0, G_OPTION_ARG_NONE, &opt_require_hardlinks, "Do not fall back to full copies if hardlinking fails", NULL }, + { "force-copy-zerosized", 'z', 0, G_OPTION_ARG_NONE, &opt_force_copy_zerosized, "Do not hardlink zero-sized files", NULL }, { "force-copy", 'C', 0, G_OPTION_ARG_NONE, &opt_force_copy, "Never hardlink (but may reflink if available)", NULL }, { "bareuseronly-dirs", 'M', 0, G_OPTION_ARG_NONE, &opt_bareuseronly_dirs, "Suppress mode bits outside of 0775 for directories (suid, world writable, etc.)", NULL }, { "skip-list", 0, 0, G_OPTION_ARG_FILENAME, &opt_skiplist_file, "File containing list of files to skip", "PATH" }, @@ -130,7 +132,8 @@ process_one_checkout (OstreeRepo *repo, * convenient infrastructure for testing C APIs with data. */ if (opt_disable_cache || opt_whiteouts || opt_require_hardlinks || - opt_union_add || opt_force_copy || opt_bareuseronly_dirs || opt_union_identical || + opt_union_add || opt_force_copy || opt_force_copy_zerosized || + opt_bareuseronly_dirs || opt_union_identical || opt_skiplist_file || opt_selinux_policy || opt_selinux_prefix) { OstreeRepoCheckoutAtOptions options = { 0, }; @@ -218,6 +221,7 @@ process_one_checkout (OstreeRepo *repo, options.no_copy_fallback = opt_require_hardlinks; options.force_copy = opt_force_copy; + options.force_copy_zerosized = opt_force_copy_zerosized; options.bareuseronly_dirs = opt_bareuseronly_dirs; if (!ostree_repo_checkout_at (repo, &options, diff --git a/src/ostree/ot-builtin-create-usb.c b/src/ostree/ot-builtin-create-usb.c index eee637c8..849684f6 100644 --- a/src/ostree/ot-builtin-create-usb.c +++ b/src/ostree/ot-builtin-create-usb.c @@ -33,11 +33,13 @@ static gboolean opt_disable_fsync = FALSE; static char *opt_destination_repo = NULL; +static char *opt_commit = NULL; static GOptionEntry options[] = { { "disable-fsync", 0, 0, G_OPTION_ARG_NONE, &opt_disable_fsync, "Do not invoke fsync()", NULL }, { "destination-repo", 0, 0, G_OPTION_ARG_FILENAME, &opt_destination_repo, "Use custom repository directory within the mount", "DEST" }, + { "commit", 0, 0, G_OPTION_ARG_STRING, &opt_commit, "Pull a specific commit (only works when a single ref is specified)", "COMMIT" }, { NULL } }; @@ -78,6 +80,12 @@ ostree_builtin_create_usb (int argc, return FALSE; } + if (opt_commit && argc > 4) + { + ot_util_usage_error (context, "The --commit option can only be used when a single COLLECTION-ID REF pair is specified", error); + return FALSE; + } + /* Open the USB stick, which must exist. Allow automounting and following symlinks. */ const char *mount_root_path = argv[1]; struct stat mount_root_stbuf; @@ -102,21 +110,17 @@ ostree_builtin_create_usb (int argc, /* Open the destination repository on the USB stick or create it if it doesn’t exist. * Check it’s below @mount_root_path, and that it’s not the same as the source - * repository. - * - * If the destination file system supports xattrs (for example, ext4), we use - * a BARE_USER repository; if it doesn’t (for example, FAT), we use ARCHIVE. - * In either case, we want a lossless repository. */ + * repository. */ const char *dest_repo_path = (opt_destination_repo != NULL) ? opt_destination_repo : ".ostree/repo"; if (!glnx_shutil_mkdir_p_at (mount_root_dfd, dest_repo_path, 0755, cancellable, error)) return FALSE; - OstreeRepoMode mode = OSTREE_REPO_MODE_BARE_USER; - - if (TEMP_FAILURE_RETRY (fgetxattr (mount_root_dfd, "user.test", NULL, 0)) < 0 && - (errno == ENOTSUP || errno == EOPNOTSUPP)) - mode = OSTREE_REPO_MODE_ARCHIVE; + /* Always use the archive repo mode, which works on FAT file systems that + * don't support xattrs, compresses files to save space, doesn't store + * permission info directly in the file attributes, and is at least sometimes + * more performant than bare-user */ + OstreeRepoMode mode = OSTREE_REPO_MODE_ARCHIVE; g_debug ("%s: Creating repository in mode %u", G_STRFUNC, mode); @@ -158,7 +162,7 @@ ostree_builtin_create_usb (int argc, const OstreeCollectionRef *ref = g_ptr_array_index (refs, i); g_variant_builder_add (&refs_builder, "(sss)", - ref->collection_id, ref->ref_name, ""); + ref->collection_id, ref->ref_name, opt_commit ?: ""); } { diff --git a/src/ostree/ot-builtin-refs.c b/src/ostree/ot-builtin-refs.c index 5c2214cc..f88d08a6 100644 --- a/src/ostree/ot-builtin-refs.c +++ b/src/ostree/ot-builtin-refs.c @@ -146,8 +146,10 @@ static gboolean do_ref (OstreeRepo *repo, const char *refspec_prefix, GCancellab /* If we're doing aliasing, we need the full list of aliases mostly to allow * replacing existing aliases. + * If we are deleting a ref, we want to make sure that it doesn't have + * any corresponding aliases. */ - if (opt_alias) + if (opt_alias || opt_delete) { if (!ostree_repo_list_refs_ext (repo, NULL, &ref_aliases, OSTREE_REPO_LIST_REFS_EXT_ALIASES, @@ -245,7 +247,18 @@ static gboolean do_ref (OstreeRepo *repo, const char *refspec_prefix, GCancellab if (!ostree_parse_refspec (refspec, &remote, &ref, error)) goto out; - + + /* Look for alias if it exists for a ref we want to delete */ + GLNX_HASH_TABLE_FOREACH_KV (ref_aliases, const char *, + ref_alias, const char *, value) + { + if (!strcmp (ref, value)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Ref '%s' has an active alias: '%s'", ref, ref_alias); + goto out; + } + } if (!ostree_repo_set_ref_immediate (repo, remote, ref, NULL, cancellable, error)) goto out; diff --git a/src/rofiles-fuse/main.c b/src/rofiles-fuse/main.c index fd16e29b..4033caa4 100644 --- a/src/rofiles-fuse/main.c +++ b/src/rofiles-fuse/main.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -628,10 +629,7 @@ rofs_parse_opt (void *data, const char *arg, int key, { basefd = openat (AT_FDCWD, arg, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); if (basefd == -1) - { - perror ("openat"); - exit (EXIT_FAILURE); - } + err (1, "opening rootfs %s", arg); return 0; } else diff --git a/src/switchroot/ostree-remount.c b/src/switchroot/ostree-remount.c index 71b7b395..5e6d23d3 100644 --- a/src/switchroot/ostree-remount.c +++ b/src/switchroot/ostree-remount.c @@ -39,13 +39,40 @@ #include "ostree-mount-util.h" +static void +do_remount (const char *target) +{ + struct stat stbuf; + if (lstat (target, &stbuf) < 0) + return; + /* Silently ignore symbolic links; we expect these to point to + * /sysroot, and thus there isn't a bind mount there. + */ + if (S_ISLNK (stbuf.st_mode)) + return; + /* If not a mountpoint, skip it */ + struct statvfs stvfsbuf; + if (statvfs (target, &stvfsbuf) == -1) + return; + /* If no read-only flag, skip it */ + if ((stvfsbuf.f_flag & ST_RDONLY) == 0) + return; + /* It's a mounted, read-only fs; remount it */ + if (mount (target, target, NULL, MS_REMOUNT | MS_SILENT, NULL) < 0) + { + /* Also ignore EINVAL - if the target isn't a mountpoint + * already, then assume things are OK. + */ + if (errno != EINVAL) + err (EXIT_FAILURE, "failed to remount %s", target); + } + else + printf ("Remounted: %s\n", target); +} + int main(int argc, char *argv[]) { - const char *remounts[] = { "/sysroot", "/var", NULL }; - struct stat stbuf; - int i; - /* When systemd is in use this is normally created via the generator, but * we ensure it's created here as well for redundancy. */ @@ -65,39 +92,11 @@ main(int argc, char *argv[]) /* If / isn't writable, don't do any remounts; we don't want * to clear the readonly flag in that case. */ - exit (EXIT_SUCCESS); } - for (i = 0; remounts[i] != NULL; i++) - { - const char *target = remounts[i]; - if (lstat (target, &stbuf) < 0) - continue; - /* Silently ignore symbolic links; we expect these to point to - * /sysroot, and thus there isn't a bind mount there. - */ - if (S_ISLNK (stbuf.st_mode)) - continue; - /* If not a mountpoint, skip it */ - struct statvfs stvfsbuf; - if (statvfs (target, &stvfsbuf) == -1) - continue; - /* If no read-only flag, skip it */ - if ((stvfsbuf.f_flag & ST_RDONLY) == 0) - continue; - /* It's a mounted, read-only fs; remount it */ - if (mount (target, target, NULL, MS_REMOUNT | MS_SILENT, NULL) < 0) - { - /* Also ignore EINVAL - if the target isn't a mountpoint - * already, then assume things are OK. - */ - if (errno != EINVAL) - err (EXIT_FAILURE, "failed to remount %s", target); - } - else - printf ("Remounted: %s\n", target); - } + do_remount ("/sysroot"); + do_remount ("/var"); exit (EXIT_SUCCESS); } diff --git a/tests/basic-test.sh b/tests/basic-test.sh index a0c2f1f7..3c4823d7 100644 --- a/tests/basic-test.sh +++ b/tests/basic-test.sh @@ -21,7 +21,7 @@ set -euo pipefail -echo "1..$((83 + ${extra_basic_tests:-0}))" +echo "1..$((85 + ${extra_basic_tests:-0}))" CHECKOUT_U_ARG="" CHECKOUT_H_ARGS="-H" @@ -694,6 +694,35 @@ for v in bin link; do done echo "ok checkout union identical conflicts" +cd ${test_tmpdir} +rm files -rf && mkdir files +touch files/anemptyfile +touch files/anotheremptyfile +$CMD_PREFIX ostree --repo=repo commit --consume -b tree-with-empty-files --tree=dir=files +$CMD_PREFIX ostree --repo=repo checkout ${CHECKOUT_H_ARGS} -z tree-with-empty-files tree-with-empty-files +if files_are_hardlinked tree-with-empty-files/an{,other}emptyfile; then + fatal "--force-copy-zerosized failed" +fi +rm tree-with-empty-files -rf +$CMD_PREFIX ostree --repo=repo checkout ${CHECKOUT_H_ARGS} tree-with-empty-files tree-with-empty-files +assert_files_hardlinked tree-with-empty-files/an{,other}emptyfile +rm tree-with-empty-files -rf +echo "ok checkout --force-copy-zerosized" + +# These should merge, they're identical +$CMD_PREFIX ostree --repo=repo checkout ${CHECKOUT_H_ARGS} --union-identical -z tree-with-empty-files tree-with-empty-files +$CMD_PREFIX ostree --repo=repo checkout ${CHECKOUT_H_ARGS} --union-identical -z tree-with-empty-files tree-with-empty-files +echo notempty > tree-with-empty-files/anemptyfile.new && mv tree-with-empty-files/anemptyfile{.new,} +$CMD_PREFIX ostree --repo=repo commit --consume -b tree-with-conflicting-empty-files --tree=dir=tree-with-empty-files +# Reset back to base +rm tree-with-empty-files -rf +$CMD_PREFIX ostree --repo=repo checkout ${CHECKOUT_H_ARGS} --union-identical -z tree-with-empty-files tree-with-empty-files +if $CMD_PREFIX ostree --repo=repo checkout ${CHECKOUT_H_ARGS} --union-identical -z tree-with-conflicting-empty-files tree-with-empty-files 2>err.txt; then + fatal "--union-identical --force-copy-zerosized unexpectedly succeeded with non-identical files" +fi +assert_file_has_content err.txt 'error:.*File exists' +echo "ok checkout --union-identical --force-copy-zerosized" + cd ${test_tmpdir} rm files -rf && mkdir files mkdir files/worldwritable-dir diff --git a/tests/test-basic-c.c b/tests/test-basic-c.c index 8515b129..ff6d353c 100644 --- a/tests/test-basic-c.c +++ b/tests/test-basic-c.c @@ -430,6 +430,54 @@ test_devino_cache_xattrs (void) g_assert_cmpint (stats.content_objects_written, ==, 1); } +/* https://github.com/ostreedev/ostree/issues/1721 + * We should be able to commit large metadata objects now. + */ +static void +test_big_metadata (void) +{ + g_autoptr(GError) error = NULL; + gboolean ret = FALSE; + + g_autoptr(GFile) repo_path = g_file_new_for_path ("repo"); + + /* init as bare-user-only so we run everywhere */ + ret = ot_test_run_libtest ("setup_test_repository bare-user-only", &error); + g_assert_no_error (error); + g_assert (ret); + + g_autoptr(OstreeRepo) repo = ostree_repo_new (repo_path); + ret = ostree_repo_open (repo, NULL, &error); + g_assert_no_error (error); + g_assert (ret); + + g_autoptr(GFile) object_to_commit = NULL; + ret = ostree_repo_read_commit (repo, "test2", &object_to_commit, NULL, NULL, &error); + g_assert_no_error (error); + g_assert (ret); + + g_autoptr(OstreeMutableTree) mtree = ostree_mutable_tree_new (); + ret = ostree_repo_write_directory_to_mtree (repo, object_to_commit, mtree, NULL, + NULL, &error); + g_assert_no_error (error); + g_assert (ret); + + const size_t len = 20 * 1024 * 1024; + g_assert_cmpint (len, >, OSTREE_MAX_METADATA_SIZE); + g_autofree char *large_buf = g_malloc (len); + memset (large_buf, 0x42, len); + g_autoptr(GVariantBuilder) builder = + g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + g_autofree char *commit_checksum = NULL; + g_variant_builder_add (builder, "{sv}", "large-value", + g_variant_new_fixed_array ((GVariantType*)"y", + large_buf, len, sizeof (char))); + ret = ostree_repo_write_commit (repo, NULL, NULL, NULL, g_variant_builder_end (builder), + OSTREE_REPO_FILE (object_to_commit), &commit_checksum, NULL, &error); + g_assert_no_error (error); + g_assert (ret); +} + int main (int argc, char **argv) { g_autoptr(GError) error = NULL; @@ -447,6 +495,7 @@ int main (int argc, char **argv) g_test_add_func ("/xattrs-devino-cache", test_devino_cache_xattrs); g_test_add_func ("/break-hardlink", test_break_hardlink); g_test_add_func ("/remotename", test_validate_remotename); + g_test_add_func ("/big-metadata", test_big_metadata); return g_test_run(); out: diff --git a/tests/test-refs.sh b/tests/test-refs.sh index f4fe1833..1730423d 100755 --- a/tests/test-refs.sh +++ b/tests/test-refs.sh @@ -192,6 +192,11 @@ done ${CMD_PREFIX} ostree --repo=repo refs -A > refs.txt assert_file_has_content_literal refs.txt 'exampleos/x86_64/stable/server -> exampleos/x86_64/27/server' +# Test that we don't delete a ref having aliases +if ${CMD_PREFIX} ostree --repo=repo refs --delete exampleos/x86_64/27/server; then + assert_not_reached "refs --delete unexpectedly succeeded in deleting a ref containing alias!" +fi + ${CMD_PREFIX} ostree --repo=repo summary -u echo "ok ref symlink" diff --git a/tests/test-repo.c b/tests/test-repo.c index edccef59..9857228e 100644 --- a/tests/test-repo.c +++ b/tests/test-repo.c @@ -151,6 +151,52 @@ test_repo_equal (Fixture *fixture, g_assert_false (ostree_repo_equal (closed_repo, closed_repo)); } +static void +test_repo_get_min_free_space (Fixture *fixture, + gconstpointer test_data) +{ + g_autoptr (GKeyFile) config = NULL; + g_autoptr(GError) error = NULL; + guint64 bytes = 0; + typedef struct + { + const char *val; + gboolean should_succeed; + } min_free_space_value; + + g_autoptr(OstreeRepo) repo = ostree_repo_create_at (fixture->tmpdir.fd, ".", + OSTREE_REPO_MODE_ARCHIVE, + NULL, + NULL, &error); + g_assert_no_error (error); + + min_free_space_value values_to_test[] = { + {"500MB", TRUE }, + { "0MB", TRUE }, + { "17179869185GB", FALSE }, /* Overflow parameter: bytes > G_MAXUINT64 */ + { NULL, FALSE } + }; + + config = ostree_repo_copy_config (repo); + + for (guint i = 0; values_to_test[i].val != NULL; i++) + { + g_key_file_remove_key (config, "core", "min-free-space-size", NULL); + g_key_file_set_string (config, "core", "min-free-space-size", values_to_test[i].val); + + ostree_repo_write_config (repo, config, &error); + g_assert_no_error (error); + ostree_repo_reload_config (repo, NULL, &error); + g_assert_no_error (error); + + ostree_repo_get_min_free_space_bytes (repo, &bytes, &error); + if (values_to_test[i].should_succeed) + g_assert_no_error (error); + else + continue; + } +} + int main (int argc, char **argv) @@ -164,6 +210,9 @@ main (int argc, test_repo_hash_closed, teardown); g_test_add ("/repo/equal", Fixture, NULL, setup, test_repo_equal, teardown); + g_test_add ("/repo/get_min_free_space", Fixture, NULL, setup, + test_repo_get_min_free_space, teardown); + return g_test_run (); } diff --git a/tests/test-symbols.sh b/tests/test-symbols.sh index df7f8648..47f3ad73 100755 --- a/tests/test-symbols.sh +++ b/tests/test-symbols.sh @@ -54,7 +54,7 @@ echo 'ok documented symbols' # ONLY update this checksum in release commits! cat > released-sha256.txt <