From 834ce75f8815cab304353e6d5284eeb735cf0744 Mon Sep 17 00:00:00 2001 From: Yubao Liu Date: Sun, 10 Jul 2022 19:48:16 +0800 Subject: [PATCH] Make sure auto-mounted subpath mount source is already mounted For example, we have two filesystems, one is embedded into another: /a/test # first filesystem with a directory "/a/test/b2" /a/test/b2 # not auto mounted yet second filesystem, notice "/a/test/b2" is # a new directory on this filesystem after this filesystem is mounted For subpath mount "/a/test/b2", `openat("/a/test", "b2")` gets directory "b2" on the first filesystem, then "mount -c" will use this wrong directory as source directory. `fstatat("/a/test", "b2/")` forces triggering auto mount of second filesystem, so `openat("/a/test", "b2")` gets correct source directory for "mount -c". This fixes issue https://github.com/kubernetes/kubernetes/issues/110818#issuecomment-1175736550 References: 1. https://man7.org/linux/man-pages/man2/openat.2.html If pathname refers to an automount point that has not yet been triggered, so no other filesystem is mounted on it, then the call returns a file descriptor referring to the automount directory without triggering a mount. 2. https://man7.org/linux/man-pages/man2/open_by_handle_at.2.html name_to_handle_at() does not trigger a mount when the final component of the pathname is an automount point. When a filesystem supports both file handles and automount points, a name_to_handle_at() call on an automount point will return with error EOVERFLOW without having increased handle_bytes. This can happen since Linux 4.13 with NFS when accessing a directory which is on a separate filesystem on the server. In this case, the automount can be triggered by adding a "/" to the end of the pathname. --- pkg/volume/util/subpath/subpath_linux.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/volume/util/subpath/subpath_linux.go b/pkg/volume/util/subpath/subpath_linux.go index 4a505be3b3c..583939bdc4a 100644 --- a/pkg/volume/util/subpath/subpath_linux.go +++ b/pkg/volume/util/subpath/subpath_linux.go @@ -566,18 +566,23 @@ func doSafeOpen(pathname string, base string) (int, error) { // Follow the segments one by one using openat() to make // sure the user cannot change already existing directories into symlinks. for _, seg := range segments { + var deviceStat unix.Stat_t + currentPath = filepath.Join(currentPath, seg) if !mount.PathWithinBase(currentPath, base) { return -1, fmt.Errorf("path %s is outside of allowed base %s", currentPath, base) } + // Trigger auto mount if it's an auto-mounted directory, ignore error if not a directory. + // Notice the trailing slash is mandatory, see "automount" in openat(2) and open_by_handle_at(2). + unix.Fstatat(parentFD, seg+"/", &deviceStat, unix.AT_SYMLINK_NOFOLLOW) + klog.V(5).Infof("Opening path %s", currentPath) childFD, err = syscall.Openat(parentFD, seg, openFDFlags|unix.O_CLOEXEC, 0) if err != nil { return -1, fmt.Errorf("cannot open %s: %s", currentPath, err) } - var deviceStat unix.Stat_t err := unix.Fstat(childFD, &deviceStat) if err != nil { return -1, fmt.Errorf("error running fstat on %s with %v", currentPath, err)