Update go dependencies
This commit is contained in:
parent
14a9e9f3fa
commit
14f4a7b8e8
1349 changed files with 128369 additions and 32627 deletions
534
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
generated
vendored
534
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
generated
vendored
|
|
@ -21,7 +21,6 @@ package mount
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
|
|
@ -33,9 +32,9 @@ import (
|
|||
"golang.org/x/sys/unix"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/klog"
|
||||
utilfile "k8s.io/kubernetes/pkg/util/file"
|
||||
utilio "k8s.io/kubernetes/pkg/util/io"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
utilio "k8s.io/utils/io"
|
||||
utilpath "k8s.io/utils/path"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -53,14 +52,6 @@ const (
|
|||
fsckErrorsCorrected = 1
|
||||
// 'fsck' found errors but exited without correcting them
|
||||
fsckErrorsUncorrected = 4
|
||||
|
||||
// place for subpath mounts
|
||||
// TODO: pass in directory using kubelet_getters instead
|
||||
containerSubPathDirectoryName = "volume-subpaths"
|
||||
// syscall.Openat flags used to traverse directories not following symlinks
|
||||
nofollowFlags = unix.O_RDONLY | unix.O_NOFOLLOW
|
||||
// flags for getting file descriptor without following the symlink
|
||||
openFDFlags = unix.O_NOFOLLOW | unix.O_PATH
|
||||
)
|
||||
|
||||
// Mounter provides the default implementation of mount.Interface
|
||||
|
|
@ -229,7 +220,7 @@ func (mounter *Mounter) IsMountPointMatch(mp MountPoint, dir string) bool {
|
|||
}
|
||||
|
||||
func (mounter *Mounter) IsNotMountPoint(dir string) (bool, error) {
|
||||
return IsNotMountPoint(mounter, dir)
|
||||
return isNotMountPoint(mounter, dir)
|
||||
}
|
||||
|
||||
// IsLikelyNotMountPoint determines if a directory is not a mountpoint.
|
||||
|
|
@ -417,7 +408,7 @@ func (mounter *Mounter) MakeFile(pathname string) error {
|
|||
}
|
||||
|
||||
func (mounter *Mounter) ExistsPath(pathname string) (bool, error) {
|
||||
return utilfile.FileExists(pathname)
|
||||
return utilpath.Exists(utilpath.CheckFollowSymlink, pathname)
|
||||
}
|
||||
|
||||
func (mounter *Mounter) EvalHostSymlinks(pathname string) (string, error) {
|
||||
|
|
@ -726,287 +717,15 @@ func getSELinuxSupport(path string, mountInfoFilename string) (bool, error) {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
|
||||
newHostPath, err = doBindSubPath(mounter, subPath)
|
||||
|
||||
// There is no action when the container starts. Bind-mount will be cleaned
|
||||
// when container stops by CleanSubPaths.
|
||||
cleanupAction = nil
|
||||
return newHostPath, cleanupAction, err
|
||||
}
|
||||
|
||||
// This implementation is shared between Linux and NsEnterMounter
|
||||
func safeOpenSubPath(mounter Interface, subpath Subpath) (int, error) {
|
||||
if !PathWithinBase(subpath.Path, subpath.VolumePath) {
|
||||
return -1, fmt.Errorf("subpath %q not within volume path %q", subpath.Path, subpath.VolumePath)
|
||||
}
|
||||
fd, err := doSafeOpen(subpath.Path, subpath.VolumePath)
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("error opening subpath %v: %v", subpath.Path, err)
|
||||
}
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
// prepareSubpathTarget creates target for bind-mount of subpath. It returns
|
||||
// "true" when the target already exists and something is mounted there.
|
||||
// Given Subpath must have all paths with already resolved symlinks and with
|
||||
// paths relevant to kubelet (when it runs in a container).
|
||||
// This function is called also by NsEnterMounter. It works because
|
||||
// /var/lib/kubelet is mounted from the host into the container with Kubelet as
|
||||
// /var/lib/kubelet too.
|
||||
func prepareSubpathTarget(mounter Interface, subpath Subpath) (bool, string, error) {
|
||||
// Early check for already bind-mounted subpath.
|
||||
bindPathTarget := getSubpathBindTarget(subpath)
|
||||
notMount, err := IsNotMountPoint(mounter, bindPathTarget)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return false, "", fmt.Errorf("error checking path %s for mount: %s", bindPathTarget, err)
|
||||
}
|
||||
// Ignore ErrorNotExist: the file/directory will be created below if it does not exist yet.
|
||||
notMount = true
|
||||
}
|
||||
if !notMount {
|
||||
// It's already mounted
|
||||
klog.V(5).Infof("Skipping bind-mounting subpath %s: already mounted", bindPathTarget)
|
||||
return true, bindPathTarget, nil
|
||||
}
|
||||
|
||||
// bindPathTarget is in /var/lib/kubelet and thus reachable without any
|
||||
// translation even to containerized kubelet.
|
||||
bindParent := filepath.Dir(bindPathTarget)
|
||||
err = os.MkdirAll(bindParent, 0750)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
return false, "", fmt.Errorf("error creating directory %s: %s", bindParent, err)
|
||||
}
|
||||
|
||||
t, err := os.Lstat(subpath.Path)
|
||||
if err != nil {
|
||||
return false, "", fmt.Errorf("lstat %s failed: %s", subpath.Path, err)
|
||||
}
|
||||
|
||||
if t.Mode()&os.ModeDir > 0 {
|
||||
if err = os.Mkdir(bindPathTarget, 0750); err != nil && !os.IsExist(err) {
|
||||
return false, "", fmt.Errorf("error creating directory %s: %s", bindPathTarget, err)
|
||||
}
|
||||
} else {
|
||||
// "/bin/touch <bindPathTarget>".
|
||||
// A file is enough for all possible targets (symlink, device, pipe,
|
||||
// socket, ...), bind-mounting them into a file correctly changes type
|
||||
// of the target file.
|
||||
if err = ioutil.WriteFile(bindPathTarget, []byte{}, 0640); err != nil {
|
||||
return false, "", fmt.Errorf("error creating file %s: %s", bindPathTarget, err)
|
||||
}
|
||||
}
|
||||
return false, bindPathTarget, nil
|
||||
}
|
||||
|
||||
func getSubpathBindTarget(subpath Subpath) string {
|
||||
// containerName is DNS label, i.e. safe as a directory name.
|
||||
return filepath.Join(subpath.PodDir, containerSubPathDirectoryName, subpath.VolumeName, subpath.ContainerName, strconv.Itoa(subpath.VolumeMountIndex))
|
||||
}
|
||||
|
||||
func doBindSubPath(mounter Interface, subpath Subpath) (hostPath string, err error) {
|
||||
// Linux, kubelet runs on the host:
|
||||
// - safely open the subpath
|
||||
// - bind-mount /proc/<pid of kubelet>/fd/<fd> to subpath target
|
||||
// User can't change /proc/<pid of kubelet>/fd/<fd> to point to a bad place.
|
||||
|
||||
// Evaluate all symlinks here once for all subsequent functions.
|
||||
newVolumePath, err := filepath.EvalSymlinks(subpath.VolumePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error resolving symlinks in %q: %v", subpath.VolumePath, err)
|
||||
}
|
||||
newPath, err := filepath.EvalSymlinks(subpath.Path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error resolving symlinks in %q: %v", subpath.Path, err)
|
||||
}
|
||||
klog.V(5).Infof("doBindSubPath %q (%q) for volumepath %q", subpath.Path, newPath, subpath.VolumePath)
|
||||
subpath.VolumePath = newVolumePath
|
||||
subpath.Path = newPath
|
||||
|
||||
fd, err := safeOpenSubPath(mounter, subpath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
|
||||
alreadyMounted, bindPathTarget, err := prepareSubpathTarget(mounter, subpath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if alreadyMounted {
|
||||
return bindPathTarget, nil
|
||||
}
|
||||
|
||||
success := false
|
||||
defer func() {
|
||||
// Cleanup subpath on error
|
||||
if !success {
|
||||
klog.V(4).Infof("doBindSubPath() failed for %q, cleaning up subpath", bindPathTarget)
|
||||
if cleanErr := cleanSubPath(mounter, subpath); cleanErr != nil {
|
||||
klog.Errorf("Failed to clean subpath %q: %v", bindPathTarget, cleanErr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
kubeletPid := os.Getpid()
|
||||
mountSource := fmt.Sprintf("/proc/%d/fd/%v", kubeletPid, fd)
|
||||
|
||||
// Do the bind mount
|
||||
options := []string{"bind"}
|
||||
klog.V(5).Infof("bind mounting %q at %q", mountSource, bindPathTarget)
|
||||
if err = mounter.Mount(mountSource, bindPathTarget, "" /*fstype*/, options); err != nil {
|
||||
return "", fmt.Errorf("error mounting %s: %s", subpath.Path, err)
|
||||
}
|
||||
success = true
|
||||
|
||||
klog.V(3).Infof("Bound SubPath %s into %s", subpath.Path, bindPathTarget)
|
||||
return bindPathTarget, nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) CleanSubPaths(podDir string, volumeName string) error {
|
||||
return doCleanSubPaths(mounter, podDir, volumeName)
|
||||
}
|
||||
|
||||
// This implementation is shared between Linux and NsEnterMounter
|
||||
func doCleanSubPaths(mounter Interface, podDir string, volumeName string) error {
|
||||
// scan /var/lib/kubelet/pods/<uid>/volume-subpaths/<volume>/*
|
||||
subPathDir := filepath.Join(podDir, containerSubPathDirectoryName, volumeName)
|
||||
klog.V(4).Infof("Cleaning up subpath mounts for %s", subPathDir)
|
||||
|
||||
containerDirs, err := ioutil.ReadDir(subPathDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("error reading %s: %s", subPathDir, err)
|
||||
}
|
||||
|
||||
for _, containerDir := range containerDirs {
|
||||
if !containerDir.IsDir() {
|
||||
klog.V(4).Infof("Container file is not a directory: %s", containerDir.Name())
|
||||
continue
|
||||
}
|
||||
klog.V(4).Infof("Cleaning up subpath mounts for container %s", containerDir.Name())
|
||||
|
||||
// scan /var/lib/kubelet/pods/<uid>/volume-subpaths/<volume>/<container name>/*
|
||||
fullContainerDirPath := filepath.Join(subPathDir, containerDir.Name())
|
||||
err = filepath.Walk(fullContainerDirPath, func(path string, info os.FileInfo, err error) error {
|
||||
if path == fullContainerDirPath {
|
||||
// Skip top level directory
|
||||
return nil
|
||||
}
|
||||
|
||||
// pass through errors and let doCleanSubPath handle them
|
||||
if err = doCleanSubPath(mounter, fullContainerDirPath, filepath.Base(path)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error processing %s: %s", fullContainerDirPath, err)
|
||||
}
|
||||
|
||||
// Whole container has been processed, remove its directory.
|
||||
if err := os.Remove(fullContainerDirPath); err != nil {
|
||||
return fmt.Errorf("error deleting %s: %s", fullContainerDirPath, err)
|
||||
}
|
||||
klog.V(5).Infof("Removed %s", fullContainerDirPath)
|
||||
}
|
||||
// Whole pod volume subpaths have been cleaned up, remove its subpath directory.
|
||||
if err := os.Remove(subPathDir); err != nil {
|
||||
return fmt.Errorf("error deleting %s: %s", subPathDir, err)
|
||||
}
|
||||
klog.V(5).Infof("Removed %s", subPathDir)
|
||||
|
||||
// Remove entire subpath directory if it's the last one
|
||||
podSubPathDir := filepath.Join(podDir, containerSubPathDirectoryName)
|
||||
if err := os.Remove(podSubPathDir); err != nil && !os.IsExist(err) {
|
||||
return fmt.Errorf("error deleting %s: %s", podSubPathDir, err)
|
||||
}
|
||||
klog.V(5).Infof("Removed %s", podSubPathDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
// doCleanSubPath tears down the single subpath bind mount
|
||||
func doCleanSubPath(mounter Interface, fullContainerDirPath, subPathIndex string) error {
|
||||
// process /var/lib/kubelet/pods/<uid>/volume-subpaths/<volume>/<container name>/<subPathName>
|
||||
klog.V(4).Infof("Cleaning up subpath mounts for subpath %v", subPathIndex)
|
||||
fullSubPath := filepath.Join(fullContainerDirPath, subPathIndex)
|
||||
|
||||
if err := CleanupMountPoint(fullSubPath, mounter, true); err != nil {
|
||||
return fmt.Errorf("error cleaning subpath mount %s: %s", fullSubPath, err)
|
||||
}
|
||||
|
||||
klog.V(4).Infof("Successfully cleaned subpath directory %s", fullSubPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanSubPath will teardown the subpath bind mount and any remove any directories if empty
|
||||
func cleanSubPath(mounter Interface, subpath Subpath) error {
|
||||
containerDir := filepath.Join(subpath.PodDir, containerSubPathDirectoryName, subpath.VolumeName, subpath.ContainerName)
|
||||
|
||||
// Clean subdir bindmount
|
||||
if err := doCleanSubPath(mounter, containerDir, strconv.Itoa(subpath.VolumeMountIndex)); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
// Recusively remove directories if empty
|
||||
if err := removeEmptyDirs(subpath.PodDir, containerDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeEmptyDirs works backwards from endDir to baseDir and removes each directory
|
||||
// if it is empty. It stops once it encounters a directory that has content
|
||||
func removeEmptyDirs(baseDir, endDir string) error {
|
||||
if !PathWithinBase(endDir, baseDir) {
|
||||
return fmt.Errorf("endDir %q is not within baseDir %q", endDir, baseDir)
|
||||
}
|
||||
|
||||
for curDir := endDir; curDir != baseDir; curDir = filepath.Dir(curDir) {
|
||||
s, err := os.Stat(curDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
klog.V(5).Infof("curDir %q doesn't exist, skipping", curDir)
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("error stat %q: %v", curDir, err)
|
||||
}
|
||||
if !s.IsDir() {
|
||||
return fmt.Errorf("path %q not a directory", curDir)
|
||||
}
|
||||
|
||||
err = os.Remove(curDir)
|
||||
if os.IsExist(err) {
|
||||
klog.V(5).Infof("Directory %q not empty, not removing", curDir)
|
||||
break
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("error removing directory %q: %v", curDir, err)
|
||||
}
|
||||
klog.V(5).Infof("Removed directory %q", curDir)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) SafeMakeDir(subdir string, base string, perm os.FileMode) error {
|
||||
realBase, err := filepath.EvalSymlinks(base)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error resolving symlinks in %s: %s", base, err)
|
||||
}
|
||||
|
||||
realFullPath := filepath.Join(realBase, subdir)
|
||||
|
||||
return doSafeMakeDir(realFullPath, realBase, perm)
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
|
||||
if _, err := os.Stat(pathname); os.IsNotExist(err) {
|
||||
pathExists, pathErr := PathExists(pathname)
|
||||
if !pathExists {
|
||||
return []string{}, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
} else if IsCorruptedMnt(pathErr) {
|
||||
klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", pathname)
|
||||
return []string{}, nil
|
||||
} else if pathErr != nil {
|
||||
return nil, fmt.Errorf("error checking path %s: %v", pathname, pathErr)
|
||||
}
|
||||
realpath, err := filepath.EvalSymlinks(pathname)
|
||||
if err != nil {
|
||||
|
|
@ -1049,237 +768,6 @@ func getMode(pathname string) (os.FileMode, error) {
|
|||
return info.Mode(), nil
|
||||
}
|
||||
|
||||
// This implementation is shared between Linux and NsEnterMounter. Both pathname
|
||||
// and base must be either already resolved symlinks or thet will be resolved in
|
||||
// kubelet's mount namespace (in case it runs containerized).
|
||||
func doSafeMakeDir(pathname string, base string, perm os.FileMode) error {
|
||||
klog.V(4).Infof("Creating directory %q within base %q", pathname, base)
|
||||
|
||||
if !PathWithinBase(pathname, base) {
|
||||
return fmt.Errorf("path %s is outside of allowed base %s", pathname, base)
|
||||
}
|
||||
|
||||
// Quick check if the directory already exists
|
||||
s, err := os.Stat(pathname)
|
||||
if err == nil {
|
||||
// Path exists
|
||||
if s.IsDir() {
|
||||
// The directory already exists. It can be outside of the parent,
|
||||
// but there is no race-proof check.
|
||||
klog.V(4).Infof("Directory %s already exists", pathname)
|
||||
return nil
|
||||
}
|
||||
return &os.PathError{Op: "mkdir", Path: pathname, Err: syscall.ENOTDIR}
|
||||
}
|
||||
|
||||
// Find all existing directories
|
||||
existingPath, toCreate, err := findExistingPrefix(base, pathname)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening directory %s: %s", pathname, err)
|
||||
}
|
||||
// Ensure the existing directory is inside allowed base
|
||||
fullExistingPath, err := filepath.EvalSymlinks(existingPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening directory %s: %s", existingPath, err)
|
||||
}
|
||||
if !PathWithinBase(fullExistingPath, base) {
|
||||
return fmt.Errorf("path %s is outside of allowed base %s", fullExistingPath, err)
|
||||
}
|
||||
|
||||
klog.V(4).Infof("%q already exists, %q to create", fullExistingPath, filepath.Join(toCreate...))
|
||||
parentFD, err := doSafeOpen(fullExistingPath, base)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot open directory %s: %s", existingPath, err)
|
||||
}
|
||||
childFD := -1
|
||||
defer func() {
|
||||
if parentFD != -1 {
|
||||
if err = syscall.Close(parentFD); err != nil {
|
||||
klog.V(4).Infof("Closing FD %v failed for safemkdir(%v): %v", parentFD, pathname, err)
|
||||
}
|
||||
}
|
||||
if childFD != -1 {
|
||||
if err = syscall.Close(childFD); err != nil {
|
||||
klog.V(4).Infof("Closing FD %v failed for safemkdir(%v): %v", childFD, pathname, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
currentPath := fullExistingPath
|
||||
// create the directories one by one, making sure nobody can change
|
||||
// created directory into symlink.
|
||||
for _, dir := range toCreate {
|
||||
currentPath = filepath.Join(currentPath, dir)
|
||||
klog.V(4).Infof("Creating %s", dir)
|
||||
err = syscall.Mkdirat(parentFD, currentPath, uint32(perm))
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create directory %s: %s", currentPath, err)
|
||||
}
|
||||
// Dive into the created directory
|
||||
childFD, err := syscall.Openat(parentFD, dir, nofollowFlags, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot open %s: %s", currentPath, err)
|
||||
}
|
||||
// We can be sure that childFD is safe to use. It could be changed
|
||||
// by user after Mkdirat() and before Openat(), however:
|
||||
// - it could not be changed to symlink - we use nofollowFlags
|
||||
// - it could be changed to a file (or device, pipe, socket, ...)
|
||||
// but either subsequent Mkdirat() fails or we mount this file
|
||||
// to user's container. Security is no violated in both cases
|
||||
// and user either gets error or the file that it can already access.
|
||||
|
||||
if err = syscall.Close(parentFD); err != nil {
|
||||
klog.V(4).Infof("Closing FD %v failed for safemkdir(%v): %v", parentFD, pathname, err)
|
||||
}
|
||||
parentFD = childFD
|
||||
childFD = -1
|
||||
}
|
||||
|
||||
// Everything was created. mkdirat(..., perm) above was affected by current
|
||||
// umask and we must apply the right permissions to the last directory
|
||||
// (that's the one that will be available to the container as subpath)
|
||||
// so user can read/write it. This is the behavior of previous code.
|
||||
// TODO: chmod all created directories, not just the last one.
|
||||
// parentFD is the last created directory.
|
||||
|
||||
// Translate perm (os.FileMode) to uint32 that fchmod() expects
|
||||
kernelPerm := uint32(perm & os.ModePerm)
|
||||
if perm&os.ModeSetgid > 0 {
|
||||
kernelPerm |= syscall.S_ISGID
|
||||
}
|
||||
if perm&os.ModeSetuid > 0 {
|
||||
kernelPerm |= syscall.S_ISUID
|
||||
}
|
||||
if perm&os.ModeSticky > 0 {
|
||||
kernelPerm |= syscall.S_ISVTX
|
||||
}
|
||||
if err = syscall.Fchmod(parentFD, kernelPerm); err != nil {
|
||||
return fmt.Errorf("chmod %q failed: %s", currentPath, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// findExistingPrefix finds prefix of pathname that exists. In addition, it
|
||||
// returns list of remaining directories that don't exist yet.
|
||||
func findExistingPrefix(base, pathname string) (string, []string, error) {
|
||||
rel, err := filepath.Rel(base, pathname)
|
||||
if err != nil {
|
||||
return base, nil, err
|
||||
}
|
||||
dirs := strings.Split(rel, string(filepath.Separator))
|
||||
|
||||
// Do OpenAt in a loop to find the first non-existing dir. Resolve symlinks.
|
||||
// This should be faster than looping through all dirs and calling os.Stat()
|
||||
// on each of them, as the symlinks are resolved only once with OpenAt().
|
||||
currentPath := base
|
||||
fd, err := syscall.Open(currentPath, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return pathname, nil, fmt.Errorf("error opening %s: %s", currentPath, err)
|
||||
}
|
||||
defer func() {
|
||||
if err = syscall.Close(fd); err != nil {
|
||||
klog.V(4).Infof("Closing FD %v failed for findExistingPrefix(%v): %v", fd, pathname, err)
|
||||
}
|
||||
}()
|
||||
for i, dir := range dirs {
|
||||
// Using O_PATH here will prevent hangs in case user replaces directory with
|
||||
// fifo
|
||||
childFD, err := syscall.Openat(fd, dir, unix.O_PATH, 0)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return currentPath, dirs[i:], nil
|
||||
}
|
||||
return base, nil, err
|
||||
}
|
||||
if err = syscall.Close(fd); err != nil {
|
||||
klog.V(4).Infof("Closing FD %v failed for findExistingPrefix(%v): %v", fd, pathname, err)
|
||||
}
|
||||
fd = childFD
|
||||
currentPath = filepath.Join(currentPath, dir)
|
||||
}
|
||||
return pathname, []string{}, nil
|
||||
}
|
||||
|
||||
// This implementation is shared between Linux and NsEnterMounter
|
||||
// Open path and return its fd.
|
||||
// Symlinks are disallowed (pathname must already resolve symlinks),
|
||||
// and the path must be within the base directory.
|
||||
func doSafeOpen(pathname string, base string) (int, error) {
|
||||
pathname = filepath.Clean(pathname)
|
||||
base = filepath.Clean(base)
|
||||
|
||||
// Calculate segments to follow
|
||||
subpath, err := filepath.Rel(base, pathname)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
segments := strings.Split(subpath, string(filepath.Separator))
|
||||
|
||||
// Assumption: base is the only directory that we have under control.
|
||||
// Base dir is not allowed to be a symlink.
|
||||
parentFD, err := syscall.Open(base, nofollowFlags, 0)
|
||||
if err != nil {
|
||||
return -1, fmt.Errorf("cannot open directory %s: %s", base, err)
|
||||
}
|
||||
defer func() {
|
||||
if parentFD != -1 {
|
||||
if err = syscall.Close(parentFD); err != nil {
|
||||
klog.V(4).Infof("Closing FD %v failed for safeopen(%v): %v", parentFD, pathname, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
childFD := -1
|
||||
defer func() {
|
||||
if childFD != -1 {
|
||||
if err = syscall.Close(childFD); err != nil {
|
||||
klog.V(4).Infof("Closing FD %v failed for safeopen(%v): %v", childFD, pathname, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
currentPath := base
|
||||
|
||||
// Follow the segments one by one using openat() to make
|
||||
// sure the user cannot change already existing directories into symlinks.
|
||||
for _, seg := range segments {
|
||||
currentPath = filepath.Join(currentPath, seg)
|
||||
if !PathWithinBase(currentPath, base) {
|
||||
return -1, fmt.Errorf("path %s is outside of allowed base %s", currentPath, base)
|
||||
}
|
||||
|
||||
klog.V(5).Infof("Opening path %s", currentPath)
|
||||
childFD, err = syscall.Openat(parentFD, seg, openFDFlags, 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)
|
||||
}
|
||||
fileFmt := deviceStat.Mode & syscall.S_IFMT
|
||||
if fileFmt == syscall.S_IFLNK {
|
||||
return -1, fmt.Errorf("Unexpected symlink found %s", currentPath)
|
||||
}
|
||||
|
||||
// Close parentFD
|
||||
if err = syscall.Close(parentFD); err != nil {
|
||||
return -1, fmt.Errorf("closing fd for %q failed: %v", filepath.Dir(currentPath), err)
|
||||
}
|
||||
// Set child to new parent
|
||||
parentFD = childFD
|
||||
childFD = -1
|
||||
}
|
||||
|
||||
// We made it to the end, return this fd, don't close it
|
||||
finalFD := parentFD
|
||||
parentFD = -1
|
||||
|
||||
return finalFD, nil
|
||||
}
|
||||
|
||||
// searchMountPoints finds all mount references to the source, returns a list of
|
||||
// mountpoints.
|
||||
// This function assumes source cannot be device.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue