From 3b3d203828dd176c75e454b630f472c656797875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harry=20M=C3=ADchal?= Date: Wed, 27 May 2020 15:57:11 +0200 Subject: [PATCH] cmd/initContainer: Follow symlinks when redirecting There has been a long-standing issue[0] when symlinking /etc/resolv.conf to /run/host/etc/resolv.conf (host's resolv.conf). Many solutions were proposed. There are many ways how to go about it. Hardcode other paths, where the file could be, completely rely on tools like flatpak-session-helper that will track such files in a single directory, or update the symlinking function to follow symlinks and update the target path. The first solution is the easiest short-term but not very good long-term. The second solution is possibly the best long-term but there is a problem with using flatpak-session-helper. It cannot be used as root. That leaves the third option, follow symlinks and updating the target path. This commit takes the third approach to solve the issue. Now the target of the symlinking is first tested if it is a symlink and if it is, it's target will be set as the new target. This is repeated if the new target is also a symlink. The function looks for the links in the chain under /run/host where the host's filesystem is available. Based on: https://github.com/containers/toolbox/pull/380 [0] https://github.com/containers/toolbox/issues/187 --- src/cmd/initContainer.go | 59 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/src/cmd/initContainer.go b/src/cmd/initContainer.go index 3706b2c4b..a54e88fac 100644 --- a/src/cmd/initContainer.go +++ b/src/cmd/initContainer.go @@ -397,22 +397,73 @@ func mountBind(containerPath, source, flags string) error { return nil } +// redirectPath serves for creating symlinks for mainly crucial system files to +// their counterparts on the host's filesystem. +// +// containerPath and target must be absolute paths +// +// folder signifies the target is a folder +// +// The files of the host's filesystem are available in a toolbox containers +// under /run/host. +// +// If the target is a symlink, redirectPath will follow the chain of links and +// try to lookup the real target in /run/host +// +// Example: systemd-resolved makes /etc/resolv.conf a link to +// /run/systemd/resolved/resolv.conf - in a container found under +// /run/host/run/systemd/resolved/resolv.conf). func redirectPath(containerPath, target string, folder bool) error { logrus.Debugf("Redirecting %s to %s", containerPath, target) - err := os.Remove(containerPath) + targetInfo, err := os.Lstat(target) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return fmt.Errorf("target %s was not found in the container", target) + } + return fmt.Errorf("failed to lstat %s: %w", target, err) + } + isSymlink := (targetInfo.Mode() & os.ModeSymlink) != 0 + + // If the target is a link, follow the chain. If needed, repeat the process. + for isSymlink { + logrus.Debugf("Path %s is a symlink. Resolving.", target) + newTarget, err := os.Readlink(target) + if err != nil { + return fmt.Errorf("Could not read link %s: %w", target, err) + } + + // Try to find the file inside of /run/host in the container where the + // available host's files are kept. + target = fmt.Sprintf("/run/host%s", newTarget) + + targetInfo, err = os.Lstat(target) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return fmt.Errorf("target %s was not found in the container", target) + } + return fmt.Errorf("failed to lstat %s: %w", target, err) + } + + isSymlink = (targetInfo.Mode() & os.ModeSymlink) != 0 + if !isSymlink { + logrus.Debugf("Resolved and redirecting %s to %s", containerPath, newTarget) + } + } + + err = os.Remove(containerPath) if folder { if err != nil { - return fmt.Errorf("failed to redirect %s to %s", containerPath, target) + return fmt.Errorf("failed to redirect %s to %s: %w", containerPath, target, err) } if err := os.MkdirAll(target, 0755); err != nil { - return fmt.Errorf("failed to redirect %s to %s", containerPath, target) + return fmt.Errorf("failed to redirect %s to %s: %w", containerPath, target, err) } } if err := os.Symlink(target, containerPath); err != nil { - return fmt.Errorf("failed to redirect %s to %s", containerPath, target) + return fmt.Errorf("failed to redirect %s to %s: %w", containerPath, target, err) } return nil