{
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.
*/
--- /dev/null
+#!/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"