Skip to content

Commit

Permalink
deploy: Support an empty /etc and populated /usr/etc
Browse files Browse the repository at this point in the history
In preparation for support for a transient `/etc`:
ostreedev#2868
particularly in combination with composefs.

Basically it's just much more elegant if we can directly mount
an overlayfs on the *empty* `etc` directory, using `usr/etc` as
the lower.

In the composefs case, we'd have to mount the composefs overlayfs
itself writable (and call `mkdir`) *just* so we can make that
empty `etc` directory which is ugly.
  • Loading branch information
cgwalters committed Jul 29, 2023
1 parent a2663e8 commit 0406fd3
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 10 deletions.
1 change: 1 addition & 0 deletions Makefile-tests.am
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ _installed_or_uninstalled_test_scripts = \
tests/test-admin-deploy-none.sh \
tests/test-admin-deploy-bootid-gc.sh \
tests/test-admin-deploy-whiteouts.sh \
tests/test-admin-deploy-emptyetc.sh \
tests/test-osupdate-dtb.sh \
tests/test-admin-instutil-set-kargs.sh \
tests/test-admin-upgrade-not-backwards.sh \
Expand Down
74 changes: 64 additions & 10 deletions src/libostree/ostree-sysroot-deploy.c
Original file line number Diff line number Diff line change
Expand Up @@ -872,26 +872,80 @@ prepare_deployment_etc (OstreeSysroot *sysroot, OstreeRepo *repo, OstreeDeployme
{
GLNX_AUTO_PREFIX_ERROR ("Preparing /etc", error);

enum DirectoryState
{
DIRSTATE_NONEXISTENT,
DIRSTATE_EMPTY,
DIRSTATE_POPULATED,
};

enum DirectoryState etc_state;
{
gboolean exists = FALSE;
g_auto (GLnxDirFdIterator) dfd_iter = {
0,
};
if (!ot_dfd_iter_init_allow_noent (deployment_dfd, "etc", &dfd_iter, &exists, error))
return glnx_prefix_error (error, "Failed to stat etc in deployment");
if (!exists)
{
etc_state = DIRSTATE_NONEXISTENT;
}
else
{
struct dirent *dent;
if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, NULL, error))
return FALSE;
if (dent)
etc_state = DIRSTATE_POPULATED;
else
etc_state = DIRSTATE_EMPTY;
}
}
struct stat stbuf;
if (!glnx_fstatat_allow_noent (deployment_dfd, "etc", &stbuf, AT_SYMLINK_NOFOLLOW, error))
return FALSE;
gboolean etc_exists = (errno == 0);
if (!glnx_fstatat_allow_noent (deployment_dfd, "usr/etc", &stbuf, AT_SYMLINK_NOFOLLOW, error))
return FALSE;
gboolean usretc_exists = (errno == 0);

if (etc_exists)
switch (etc_state)
{
if (usretc_exists)
return glnx_throw (error, "Tree contains both /etc and /usr/etc");
/* Compatibility hack */
if (!glnx_renameat (deployment_dfd, "etc", deployment_dfd, "usr/etc", error))
return FALSE;
usretc_exists = TRUE;
case DIRSTATE_NONEXISTENT:
break;
case DIRSTATE_EMPTY:
{
if (usretc_exists)
{
/* For now it's actually simpler to just remove the empty directory
* and have a symmetrical code path.
*/
if (unlinkat (deployment_dfd, "etc", AT_REMOVEDIR) < 0)
return glnx_throw_errno_prefix (error, "Failed to remove empty etc");
etc_state = DIRSTATE_NONEXISTENT;
}
/* Otherwise, there's no /etc or /usr/etc, we'll assume they know what they're doing... */
}
break;
case DIRSTATE_POPULATED:
{
if (usretc_exists)
{
return glnx_throw (error, "Tree contains both /etc and /usr/etc");
}
else
{
/* Compatibility hack */
if (!glnx_renameat (deployment_dfd, "etc", deployment_dfd, "usr/etc", error))
return FALSE;
etc_state = DIRSTATE_NONEXISTENT;
usretc_exists = TRUE;
}
}
break;
}

if (usretc_exists)
{
g_assert (etc_state == DIRSTATE_NONEXISTENT);
/* We need copies of /etc from /usr/etc (so admins can use vi), and if
* SELinux is enabled, we need to relabel.
*/
Expand Down
33 changes: 33 additions & 0 deletions tests/test-admin-deploy-emptyetc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/bin/bash
#
# SPDX-License-Identifier: LGPL-2.0+
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <https://www.gnu.org/licenses/>.

set -euo pipefail

. $(dirname $0)/libtest.sh

setup_os_repository "archive" "syslinux"

echo "1..1"
cd ${test_tmpdir}/osdata
mkdir etc
${CMD_PREFIX} ostree --repo=${test_tmpdir}/testos-repo commit --add-metadata-string "version=42.etc" -b testos/buildmain/x86_64-runtime
cd -
${CMD_PREFIX} ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmain/x86_64-runtime
${CMD_PREFIX} ostree admin deploy --os=testos testos:testos/buildmain/x86_64-runtime
origdeployment=$(${CMD_PREFIX} ostree admin --sysroot=sysroot --print-current-dir)
assert_file_has_content ${origdeployment}/etc/NetworkManager/nm.conf "a default daemon file"
echo "ok empty etc"

0 comments on commit 0406fd3

Please sign in to comment.