diff --git a/src/libpriv/rpmostree-core.cxx b/src/libpriv/rpmostree-core.cxx index 1e6af8edaa..9cc872b27b 100644 --- a/src/libpriv/rpmostree-core.cxx +++ b/src/libpriv/rpmostree-core.cxx @@ -4062,6 +4062,55 @@ rpmostree_context_assemble (RpmOstreeContext *self, GCancellable *cancellable, G if (!build_rootfs_usrlinks (self, error)) return FALSE; + /* This is purely for making it easier for people to test out the + * state-overlay stuff until it's stabilized and part of base composes. */ + if (g_getenv ("RPMOSTREE_EXPERIMENTAL_FORCE_OPT_USRLOCAL_OVERLAY")) + { + rpmostree_output_message ( + "Enabling experimental state overlay support for /opt and /usr/local"); + + struct stat stbuf; + + if (!glnx_fstatat_allow_noent (tmprootfs_dfd, "opt", &stbuf, AT_SYMLINK_NOFOLLOW, error)) + return FALSE; + if (errno == ENOENT || (errno == 0 && S_ISLNK (stbuf.st_mode))) + { + if (errno == 0 && !glnx_unlinkat (tmprootfs_dfd, "opt", 0, error)) + return FALSE; + if (symlinkat ("usr/lib/opt", tmprootfs_dfd, "opt") < 0) + return glnx_throw_errno_prefix (error, "symlinkat(/opt)"); + } + + if (!glnx_fstatat_allow_noent (tmprootfs_dfd, "usr/local", &stbuf, AT_SYMLINK_NOFOLLOW, + error)) + return FALSE; + if (errno == ENOENT || (errno == 0 && S_ISLNK (stbuf.st_mode))) + { + if (errno == 0 && !glnx_unlinkat (tmprootfs_dfd, "usr/local", 0, error)) + return FALSE; + if (mkdirat (tmprootfs_dfd, "usr/local", 0755) < 0) + return glnx_throw_errno_prefix (error, "mkdirat(/usr/local)"); + } + + if (!glnx_shutil_mkdir_p_at (tmprootfs_dfd, "usr/lib/systemd/system/local-fs.target.wants", + 0755, cancellable, error)) + return FALSE; + + if (symlinkat ("ostree-state-overlay@usr-lib-opt.service", tmprootfs_dfd, + "usr/lib/systemd/system/local-fs.target.wants/" + "ostree-state-overlay@usr-lib-opt.service") + < 0 + && errno != EEXIST) + return glnx_throw_errno_prefix (error, "enabling ostree-state-overlay for /usr/lib/opt"); + + if (symlinkat ( + "ostree-state-overlay@usr-local.service", tmprootfs_dfd, + "usr/lib/systemd/system/local-fs.target.wants/ostree-state-overlay@usr-local.service") + < 0 + && errno != EEXIST) + return glnx_throw_errno_prefix (error, "enabling ostree-state-overlay for /usr/local"); + } + /* We need up to date labels; the set of things needing relabeling * will have been calculated in sort_packages() */ diff --git a/tests/kolainst/destructive/state-overlays b/tests/kolainst/destructive/state-overlays new file mode 100755 index 0000000000..d7f7c418b1 --- /dev/null +++ b/tests/kolainst/destructive/state-overlays @@ -0,0 +1,98 @@ +#!/bin/bash +## kola: +## tags: "needs-internet" + +set -euo pipefail + +. ${KOLA_EXT_DATA}/libtest.sh + +rm -rf /etc/yum.repos.d/* +cat > /etc/yum.repos.d/vmcheck.repo << EOF +[test-repo] +name=test-repo +baseurl=file:///${KOLA_EXT_DATA}/rpm-repos/0 +gpgcheck=0 +enabled=1 +EOF + + +case "${AUTOPKGTEST_REBOOT_MARK:-}" in + "") + # XXX: until ostree v2024.1 hits FCOS + ostree_ver=$(rpm -q ostree --qf '%{version}') + if [ "${ostree_ver}" != "2024.1" ] && \ + [ "$(echo -e "${ostree_ver}\n2024.1" | sort -V | tail -n1)" = "2024.1" ]; then + rpm-ostree override replace https://bodhi.fedoraproject.org/updates/FEDORA-2024-6c7480dd2f + fi + + # FCOS doesn't enable opt-usrlocal-overlays so use the hack instead + mkdir -p /etc/systemd/system/rpm-ostreed.service.d/ + cat > /etc/systemd/system/rpm-ostreed.service.d/state-overlay.conf < /etc/systemd/system/move-usr-local.service < /tmp/out.txt + assert_file_has_content /tmp/out.txt 'test-opt' + assert_file_has_content /opt/megacorp/lib/mylib 'lib1' + + # add some state files + echo 'foobar' > /opt/megacorp/state/mystate + + # change some base files + echo 'badlib' > /opt/megacorp/lib/mylib + /tmp/autopkgtest-reboot 2 + ;; + 2) + # check our state is still there + assert_file_has_content /opt/megacorp/state/mystate 'foobar' + assert_file_has_content /opt/megacorp/lib/mylib 'badlib' + + # upgrade to -2 + sed -i -e 's,rpm-repos/0,rpm-repos/1,' /etc/yum.repos.d/vmcheck.repo + rpm-ostree upgrade + /tmp/autopkgtest-reboot 3 + ;; + 3) + # check our state is still there + assert_file_has_content /opt/megacorp/state/mystate 'foobar' + + # but base content has been reset + assert_file_has_content /opt/megacorp/lib/mylib 'lib2' + ;; + *) echo "unexpected mark: ${AUTOPKGTEST_REBOOT_MARK}"; exit 1;; +esac diff --git a/tests/kolainst/kolainst-build.sh b/tests/kolainst/kolainst-build.sh index 544e3f8d49..8ed020f163 100755 --- a/tests/kolainst/kolainst-build.sh +++ b/tests/kolainst/kolainst-build.sh @@ -96,6 +96,13 @@ build_module_defaults foomodular \ build_rpm zincati version 99.99 release 2 build_rpm zincati version 99.99 release 3 +# An RPM that installs in /opt +build_rpm test-opt \ + install "mkdir -p %{buildroot}/opt/megacorp/{bin,lib,state} + install %{name} %{buildroot}/opt/megacorp/bin + echo lib1 > %{buildroot}/opt/megacorp/lib/mylib" \ + files "/opt/megacorp" + mv ${test_tmpdir}/yumrepo/* ${test_tmpdir}/rpm-repos/${repover} # To test remote override replace update @@ -111,6 +118,14 @@ build_rpm pkgsystemd \ install "mkdir -p %{buildroot}/usr/lib/systemd/system install %{name}.service %{buildroot}/usr/lib/systemd/system" \ files "/usr/lib/systemd/system/%{name}.service" + +# to test updates to RPMs that install in /opt +build_rpm test-opt release 2 \ + install "mkdir -p %{buildroot}/opt/megacorp/{bin,lib,state} + install %{name} %{buildroot}/opt/megacorp/bin + echo lib2 > %{buildroot}/opt/megacorp/lib/mylib" \ + files "/opt/megacorp" + mv ${test_tmpdir}/yumrepo/* ${test_tmpdir}/rpm-repos/${repover} # Create an empty repo when we want to test inability to find a package