mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 18:43:34 +00:00
vendor update for CSI 0.3.0
This commit is contained in:
55
vendor/k8s.io/kubernetes/pkg/util/mount/BUILD
generated
vendored
55
vendor/k8s.io/kubernetes/pkg/util/mount/BUILD
generated
vendored
@ -1,10 +1,4 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
@ -72,16 +66,49 @@ go_library(
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
importpath = "k8s.io/kubernetes/pkg/util/mount",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:android": [
|
||||
"//pkg/util/nsenter:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:darwin": [
|
||||
"//pkg/util/nsenter:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:dragonfly": [
|
||||
"//pkg/util/nsenter:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:freebsd": [
|
||||
"//pkg/util/nsenter:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//pkg/util/file:go_default_library",
|
||||
"//pkg/util/io:go_default_library",
|
||||
"//pkg/util/nsenter:go_default_library",
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:nacl": [
|
||||
"//pkg/util/nsenter:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:netbsd": [
|
||||
"//pkg/util/nsenter:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:openbsd": [
|
||||
"//pkg/util/nsenter:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:plan9": [
|
||||
"//pkg/util/nsenter:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:solaris": [
|
||||
"//pkg/util/nsenter:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:windows": [
|
||||
"//pkg/util/file:go_default_library",
|
||||
"//pkg/util/nsenter:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
@ -104,7 +131,18 @@ go_test(
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//vendor/k8s.io/utils/exec/testing:go_default_library",
|
||||
],
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:linux": [
|
||||
"//pkg/util/nsenter:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/golang.org/x/sys/unix:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
"@io_bazel_rules_go//go/platform:windows": [
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
@ -118,4 +156,5 @@ filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
1
vendor/k8s.io/kubernetes/pkg/util/mount/OWNERS
generated
vendored
1
vendor/k8s.io/kubernetes/pkg/util/mount/OWNERS
generated
vendored
@ -3,6 +3,7 @@ reviewers:
|
||||
- saad-ali
|
||||
- jsafrane
|
||||
- msau42
|
||||
- andyzhangx
|
||||
approvers:
|
||||
- jingxu97
|
||||
- saad-ali
|
||||
|
31
vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount.go
generated
vendored
31
vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount.go
generated
vendored
@ -20,6 +20,7 @@ package mount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
@ -135,6 +136,34 @@ func (m *execMounter) MakeDir(pathname string) error {
|
||||
return m.wrappedMounter.MakeDir(pathname)
|
||||
}
|
||||
|
||||
func (m *execMounter) ExistsPath(pathname string) bool {
|
||||
func (m *execMounter) ExistsPath(pathname string) (bool, error) {
|
||||
return m.wrappedMounter.ExistsPath(pathname)
|
||||
}
|
||||
|
||||
func (m *execMounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
|
||||
return m.wrappedMounter.PrepareSafeSubpath(subPath)
|
||||
}
|
||||
|
||||
func (m *execMounter) CleanSubPaths(podDir string, volumeName string) error {
|
||||
return m.wrappedMounter.CleanSubPaths(podDir, volumeName)
|
||||
}
|
||||
|
||||
func (m *execMounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error {
|
||||
return m.wrappedMounter.SafeMakeDir(pathname, base, perm)
|
||||
}
|
||||
|
||||
func (m *execMounter) GetMountRefs(pathname string) ([]string, error) {
|
||||
return m.wrappedMounter.GetMountRefs(pathname)
|
||||
}
|
||||
|
||||
func (m *execMounter) GetFSGroup(pathname string) (int64, error) {
|
||||
return m.wrappedMounter.GetFSGroup(pathname)
|
||||
}
|
||||
|
||||
func (m *execMounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||
return m.wrappedMounter.GetSELinuxSupport(pathname)
|
||||
}
|
||||
|
||||
func (m *execMounter) GetMode(pathname string) (os.FileMode, error) {
|
||||
return m.wrappedMounter.GetMode(pathname)
|
||||
}
|
||||
|
35
vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount_test.go
generated
vendored
35
vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount_test.go
generated
vendored
@ -19,7 +19,9 @@ limitations under the License.
|
||||
package mount
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -65,7 +67,7 @@ func TestBindMount(t *testing.T) {
|
||||
expectedArgs = []string{"-t", fsType, "-o", "bind", sourcePath, destinationPath}
|
||||
case 2:
|
||||
// mount -t fstype -o "remount,opts" source target
|
||||
expectedArgs = []string{"-t", fsType, "-o", "remount," + strings.Join(mountOptions, ","), sourcePath, destinationPath}
|
||||
expectedArgs = []string{"-t", fsType, "-o", "bind,remount," + strings.Join(mountOptions, ","), sourcePath, destinationPath}
|
||||
}
|
||||
if !reflect.DeepEqual(expectedArgs, args) {
|
||||
t.Errorf("expected arguments %q, got %q", strings.Join(expectedArgs, " "), strings.Join(args, " "))
|
||||
@ -145,9 +147,36 @@ func (fm *fakeMounter) MakeFile(pathname string) error {
|
||||
func (fm *fakeMounter) MakeDir(pathname string) error {
|
||||
return nil
|
||||
}
|
||||
func (fm *fakeMounter) ExistsPath(pathname string) bool {
|
||||
return false
|
||||
func (fm *fakeMounter) ExistsPath(pathname string) (bool, error) {
|
||||
return false, errors.New("not implemented")
|
||||
}
|
||||
func (fm *fakeMounter) GetFileType(pathname string) (FileType, error) {
|
||||
return FileTypeFile, nil
|
||||
}
|
||||
func (fm *fakeMounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
|
||||
return subPath.Path, nil, nil
|
||||
}
|
||||
|
||||
func (fm *fakeMounter) CleanSubPaths(podDir string, volumeName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fm *fakeMounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fm *fakeMounter) GetMountRefs(pathname string) ([]string, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (fm *fakeMounter) GetFSGroup(pathname string) (int64, error) {
|
||||
return -1, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (fm *fakeMounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||
return false, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (fm *fakeMounter) GetMode(pathname string) (os.FileMode, error) {
|
||||
return 0, errors.New("not implemented")
|
||||
}
|
||||
|
33
vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount_unsupported.go
generated
vendored
33
vendor/k8s.io/kubernetes/pkg/util/mount/exec_mount_unsupported.go
generated
vendored
@ -20,6 +20,7 @@ package mount
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
type execMounter struct{}
|
||||
@ -82,6 +83,34 @@ func (mounter *execMounter) MakeFile(pathname string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mounter *execMounter) ExistsPath(pathname string) bool {
|
||||
return true
|
||||
func (mounter *execMounter) ExistsPath(pathname string) (bool, error) {
|
||||
return true, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (mounter *execMounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
|
||||
return subPath.Path, nil, nil
|
||||
}
|
||||
|
||||
func (mounter *execMounter) CleanSubPaths(podDir string, volumeName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mounter *execMounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mounter *execMounter) GetMountRefs(pathname string) ([]string, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (mounter *execMounter) GetFSGroup(pathname string) (int64, error) {
|
||||
return -1, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (mounter *execMounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||
return false, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (mounter *execMounter) GetMode(pathname string) (os.FileMode, error) {
|
||||
return 0, errors.New("not implemented")
|
||||
}
|
||||
|
49
vendor/k8s.io/kubernetes/pkg/util/mount/fake.go
generated
vendored
49
vendor/k8s.io/kubernetes/pkg/util/mount/fake.go
generated
vendored
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package mount
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
@ -58,8 +59,10 @@ func (f *FakeMounter) Mount(source string, target string, fstype string, options
|
||||
f.mutex.Lock()
|
||||
defer f.mutex.Unlock()
|
||||
|
||||
// find 'bind' option
|
||||
opts := []string{}
|
||||
|
||||
for _, option := range options {
|
||||
// find 'bind' option
|
||||
if option == "bind" {
|
||||
// This is a bind-mount. In order to mimic linux behaviour, we must
|
||||
// use the original device of the bind-mount as the real source.
|
||||
@ -78,7 +81,11 @@ func (f *FakeMounter) Mount(source string, target string, fstype string, options
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
// find 'ro' option
|
||||
if option == "ro" {
|
||||
// reuse MountPoint.Opts field to mark mount as readonly
|
||||
opts = append(opts, "ro")
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,7 +95,7 @@ func (f *FakeMounter) Mount(source string, target string, fstype string, options
|
||||
absTarget = target
|
||||
}
|
||||
|
||||
f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: absTarget, Type: fstype})
|
||||
f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: absTarget, Type: fstype, Opts: opts})
|
||||
glog.V(5).Infof("Fake mounter: mounted %s to %s", source, absTarget)
|
||||
f.Log = append(f.Log, FakeAction{Action: FakeActionMount, Target: absTarget, Source: source, FSType: fstype})
|
||||
return nil
|
||||
@ -194,6 +201,38 @@ func (f *FakeMounter) MakeFile(pathname string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeMounter) ExistsPath(pathname string) bool {
|
||||
return false
|
||||
func (f *FakeMounter) ExistsPath(pathname string) (bool, error) {
|
||||
return false, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (f *FakeMounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
|
||||
return subPath.Path, nil, nil
|
||||
}
|
||||
|
||||
func (f *FakeMounter) CleanSubPaths(podDir string, volumeName string) error {
|
||||
return nil
|
||||
}
|
||||
func (mounter *FakeMounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeMounter) GetMountRefs(pathname string) ([]string, error) {
|
||||
realpath, err := filepath.EvalSymlinks(pathname)
|
||||
if err != nil {
|
||||
// Ignore error in FakeMounter, because we actually didn't create files.
|
||||
realpath = pathname
|
||||
}
|
||||
return getMountRefsByDev(f, realpath)
|
||||
}
|
||||
|
||||
func (f *FakeMounter) GetFSGroup(pathname string) (int64, error) {
|
||||
return -1, errors.New("GetFSGroup not implemented")
|
||||
}
|
||||
|
||||
func (f *FakeMounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||
return false, errors.New("GetSELinuxSupport not implemented")
|
||||
}
|
||||
|
||||
func (f *FakeMounter) GetMode(pathname string) (os.FileMode, error) {
|
||||
return 0, errors.New("not implemented")
|
||||
}
|
||||
|
132
vendor/k8s.io/kubernetes/pkg/util/mount/mount.go
generated
vendored
132
vendor/k8s.io/kubernetes/pkg/util/mount/mount.go
generated
vendored
@ -19,6 +19,7 @@ limitations under the License.
|
||||
package mount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -83,9 +84,58 @@ type Interface interface {
|
||||
// MakeDir creates a new directory.
|
||||
// Will operate in the host mount namespace if kubelet is running in a container
|
||||
MakeDir(pathname string) error
|
||||
// ExistsPath checks whether the path exists.
|
||||
// Will operate in the host mount namespace if kubelet is running in a container
|
||||
ExistsPath(pathname string) bool
|
||||
// SafeMakeDir creates subdir within given base. It makes sure that the
|
||||
// created directory does not escape given base directory mis-using
|
||||
// symlinks. Note that the function makes sure that it creates the directory
|
||||
// somewhere under the base, nothing else. E.g. if the directory already
|
||||
// exists, it may exist outside of the base due to symlinks.
|
||||
// This method should be used if the directory to create is inside volume
|
||||
// that's under user control. User must not be able to use symlinks to
|
||||
// escape the volume to create directories somewhere else.
|
||||
SafeMakeDir(subdir string, base string, perm os.FileMode) error
|
||||
// Will operate in the host mount namespace if kubelet is running in a container.
|
||||
// Error is returned on any other error than "file not found".
|
||||
ExistsPath(pathname string) (bool, error)
|
||||
// CleanSubPaths removes any bind-mounts created by PrepareSafeSubpath in given
|
||||
// pod volume directory.
|
||||
CleanSubPaths(podDir string, volumeName string) error
|
||||
// PrepareSafeSubpath does everything that's necessary to prepare a subPath
|
||||
// that's 1) inside given volumePath and 2) immutable after this call.
|
||||
//
|
||||
// newHostPath - location of prepared subPath. It should be used instead of
|
||||
// hostName when running the container.
|
||||
// cleanupAction - action to run when the container is running or it failed to start.
|
||||
//
|
||||
// CleanupAction must be called immediately after the container with given
|
||||
// subpath starts. On the other hand, Interface.CleanSubPaths must be called
|
||||
// when the pod finishes.
|
||||
PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error)
|
||||
// GetMountRefs finds all mount references to the path, returns a
|
||||
// list of paths. Path could be a mountpoint path, device or a normal
|
||||
// directory (for bind mount).
|
||||
GetMountRefs(pathname string) ([]string, error)
|
||||
// GetFSGroup returns FSGroup of the path.
|
||||
GetFSGroup(pathname string) (int64, error)
|
||||
// GetSELinuxSupport returns true if given path is on a mount that supports
|
||||
// SELinux.
|
||||
GetSELinuxSupport(pathname string) (bool, error)
|
||||
// GetMode returns permissions of the path.
|
||||
GetMode(pathname string) (os.FileMode, error)
|
||||
}
|
||||
|
||||
type Subpath struct {
|
||||
// index of the VolumeMount for this container
|
||||
VolumeMountIndex int
|
||||
// Full path to the subpath directory on the host
|
||||
Path string
|
||||
// name of the volume that is a valid directory name.
|
||||
VolumeName string
|
||||
// Full path to the volume path
|
||||
VolumePath string
|
||||
// Path to the pod's directory, including pod UID
|
||||
PodDir string
|
||||
// Name of the container
|
||||
ContainerName string
|
||||
}
|
||||
|
||||
// Exec executes command where mount utilities are. This can be either the host,
|
||||
@ -129,22 +179,19 @@ func (mounter *SafeFormatAndMount) FormatAndMount(source string, target string,
|
||||
return mounter.formatAndMount(source, target, fstype, options)
|
||||
}
|
||||
|
||||
// GetMountRefsByDev finds all references to the device provided
|
||||
// getMountRefsByDev finds all references to the device provided
|
||||
// by mountPath; returns a list of paths.
|
||||
func GetMountRefsByDev(mounter Interface, mountPath string) ([]string, error) {
|
||||
// Note that mountPath should be path after the evaluation of any symblolic links.
|
||||
func getMountRefsByDev(mounter Interface, mountPath string) ([]string, error) {
|
||||
mps, err := mounter.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
slTarget, err := filepath.EvalSymlinks(mountPath)
|
||||
if err != nil {
|
||||
slTarget = mountPath
|
||||
}
|
||||
|
||||
// Finding the device mounted to mountPath
|
||||
diskDev := ""
|
||||
for i := range mps {
|
||||
if slTarget == mps[i].Path {
|
||||
if mountPath == mps[i].Path {
|
||||
diskDev = mps[i].Device
|
||||
break
|
||||
}
|
||||
@ -153,8 +200,8 @@ func GetMountRefsByDev(mounter Interface, mountPath string) ([]string, error) {
|
||||
// Find all references to the device.
|
||||
var refs []string
|
||||
for i := range mps {
|
||||
if mps[i].Device == diskDev || mps[i].Device == slTarget {
|
||||
if mps[i].Path != slTarget {
|
||||
if mps[i].Device == diskDev || mps[i].Device == mountPath {
|
||||
if mps[i].Path != mountPath {
|
||||
refs = append(refs, mps[i].Path)
|
||||
}
|
||||
}
|
||||
@ -237,7 +284,13 @@ func IsNotMountPoint(mounter Interface, file string) (bool, error) {
|
||||
// The list equals:
|
||||
// options - 'bind' + 'remount' (no duplicate)
|
||||
func isBind(options []string) (bool, []string) {
|
||||
bindRemountOpts := []string{"remount"}
|
||||
// Because we have an FD opened on the subpath bind mount, the "bind" option
|
||||
// needs to be included, otherwise the mount target will error as busy if you
|
||||
// remount as readonly.
|
||||
//
|
||||
// As a consequence, all read only bind mounts will no longer change the underlying
|
||||
// volume mount to be read only.
|
||||
bindRemountOpts := []string{"bind", "remount"}
|
||||
bind := false
|
||||
|
||||
if len(options) != 0 {
|
||||
@ -274,3 +327,56 @@ func HasMountRefs(mountPath string, mountRefs []string) bool {
|
||||
}
|
||||
return count > 0
|
||||
}
|
||||
|
||||
// pathWithinBase checks if give path is within given base directory.
|
||||
func pathWithinBase(fullPath, basePath string) bool {
|
||||
rel, err := filepath.Rel(basePath, fullPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if startsWithBackstep(rel) {
|
||||
// Needed to escape the base path
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// startsWithBackstep checks if the given path starts with a backstep segment
|
||||
func startsWithBackstep(rel string) bool {
|
||||
// normalize to / and check for ../
|
||||
return rel == ".." || strings.HasPrefix(filepath.ToSlash(rel), "../")
|
||||
}
|
||||
|
||||
// getFileType checks for file/directory/socket and block/character devices
|
||||
func getFileType(pathname string) (FileType, error) {
|
||||
var pathType FileType
|
||||
info, err := os.Stat(pathname)
|
||||
if os.IsNotExist(err) {
|
||||
return pathType, fmt.Errorf("path %q does not exist", pathname)
|
||||
}
|
||||
// err in call to os.Stat
|
||||
if err != nil {
|
||||
return pathType, err
|
||||
}
|
||||
|
||||
// checks whether the mode is the target mode
|
||||
isSpecificMode := func(mode, targetMode os.FileMode) bool {
|
||||
return mode&targetMode == targetMode
|
||||
}
|
||||
|
||||
mode := info.Mode()
|
||||
if mode.IsDir() {
|
||||
return FileTypeDirectory, nil
|
||||
} else if mode.IsRegular() {
|
||||
return FileTypeFile, nil
|
||||
} else if isSpecificMode(mode, os.ModeSocket) {
|
||||
return FileTypeSocket, nil
|
||||
} else if isSpecificMode(mode, os.ModeDevice) {
|
||||
if isSpecificMode(mode, os.ModeCharDevice) {
|
||||
return FileTypeCharDev, nil
|
||||
}
|
||||
return FileTypeBlockDev, nil
|
||||
}
|
||||
|
||||
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
|
||||
}
|
||||
|
769
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
generated
vendored
769
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
generated
vendored
@ -21,6 +21,7 @@ package mount
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@ -32,6 +33,7 @@ import (
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/sys/unix"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilfile "k8s.io/kubernetes/pkg/util/file"
|
||||
utilio "k8s.io/kubernetes/pkg/util/io"
|
||||
utilexec "k8s.io/utils/exec"
|
||||
)
|
||||
@ -41,6 +43,8 @@ const (
|
||||
maxListTries = 3
|
||||
// Number of fields per line in /proc/mounts as per the fstab man page.
|
||||
expectedNumFieldsPerLine = 6
|
||||
// At least number of fields per line in /proc/<pid>/mountinfo.
|
||||
expectedAtLeastNumFieldsPerMountInfo = 10
|
||||
// Location of the mount file to use
|
||||
procMountsPath = "/proc/mounts"
|
||||
// Location of the mountinfo file
|
||||
@ -49,6 +53,13 @@ const (
|
||||
fsckErrorsCorrected = 1
|
||||
// 'fsck' found errors but exited without correcting them
|
||||
fsckErrorsUncorrected = 4
|
||||
|
||||
// place for subpath mounts
|
||||
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
|
||||
@ -415,31 +426,7 @@ func (mounter *Mounter) MakeRShared(path string) error {
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
|
||||
var pathType FileType
|
||||
finfo, err := os.Stat(pathname)
|
||||
if os.IsNotExist(err) {
|
||||
return pathType, fmt.Errorf("path %q does not exist", pathname)
|
||||
}
|
||||
// err in call to os.Stat
|
||||
if err != nil {
|
||||
return pathType, err
|
||||
}
|
||||
|
||||
mode := finfo.Sys().(*syscall.Stat_t).Mode
|
||||
switch mode & syscall.S_IFMT {
|
||||
case syscall.S_IFSOCK:
|
||||
return FileTypeSocket, nil
|
||||
case syscall.S_IFBLK:
|
||||
return FileTypeBlockDev, nil
|
||||
case syscall.S_IFCHR:
|
||||
return FileTypeCharDev, nil
|
||||
case syscall.S_IFDIR:
|
||||
return FileTypeDirectory, nil
|
||||
case syscall.S_IFREG:
|
||||
return FileTypeFile, nil
|
||||
}
|
||||
|
||||
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
|
||||
return getFileType(pathname)
|
||||
}
|
||||
|
||||
func (mounter *Mounter) MakeDir(pathname string) error {
|
||||
@ -463,12 +450,8 @@ func (mounter *Mounter) MakeFile(pathname string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) ExistsPath(pathname string) bool {
|
||||
_, err := os.Stat(pathname)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
func (mounter *Mounter) ExistsPath(pathname string) (bool, error) {
|
||||
return utilfile.FileExists(pathname)
|
||||
}
|
||||
|
||||
// formatAndMount uses unix utils to format and mount the given disk
|
||||
@ -527,7 +510,11 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string,
|
||||
}
|
||||
|
||||
if fstype == "ext4" || fstype == "ext3" {
|
||||
args = []string{"-F", source}
|
||||
args = []string{
|
||||
"-F", // Force flag
|
||||
"-m0", // Zero blocks reserved for super-user
|
||||
source,
|
||||
}
|
||||
}
|
||||
glog.Infof("Disk %q appears to be unformatted, attempting to format as type: %q with options: %v", source, fstype, args)
|
||||
_, err := mounter.Exec.Run("mkfs."+fstype, args...)
|
||||
@ -607,27 +594,14 @@ func (mounter *SafeFormatAndMount) GetDiskFormat(disk string) (string, error) {
|
||||
|
||||
// isShared returns true, if given path is on a mount point that has shared
|
||||
// mount propagation.
|
||||
func isShared(path string, filename string) (bool, error) {
|
||||
infos, err := parseMountInfo(filename)
|
||||
func isShared(mount string, mountInfoPath string) (bool, error) {
|
||||
info, err := findMountInfo(mount, mountInfoPath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// process /proc/xxx/mountinfo in backward order and find the first mount
|
||||
// point that is prefix of 'path' - that's the mount where path resides
|
||||
var info *mountInfo
|
||||
for i := len(infos) - 1; i >= 0; i-- {
|
||||
if strings.HasPrefix(path, infos[i].mountPoint) {
|
||||
info = &infos[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if info == nil {
|
||||
return false, fmt.Errorf("cannot find mount point for %q", path)
|
||||
}
|
||||
|
||||
// parse optional parameters
|
||||
for _, opt := range info.optional {
|
||||
for _, opt := range info.optionalFields {
|
||||
if strings.HasPrefix(opt, "shared:") {
|
||||
return true, nil
|
||||
}
|
||||
@ -635,10 +609,28 @@ func isShared(path string, filename string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// This represents a single line in /proc/<pid>/mountinfo.
|
||||
type mountInfo struct {
|
||||
// Unique ID for the mount (maybe reused after umount).
|
||||
id int
|
||||
// The ID of the parent mount (or of self for the root of this mount namespace's mount tree).
|
||||
parentID int
|
||||
// The value of `st_dev` for files on this filesystem.
|
||||
majorMinor string
|
||||
// The pathname of the directory in the filesystem which forms the root of this mount.
|
||||
root string
|
||||
// Mount source, filesystem-specific information. e.g. device, tmpfs name.
|
||||
source string
|
||||
// Mount point, the pathname of the mount point.
|
||||
mountPoint string
|
||||
// list of "optional parameters", mount propagation is one of them
|
||||
optional []string
|
||||
// Optional fieds, zero or more fields of the form "tag[:value]".
|
||||
optionalFields []string
|
||||
// The filesystem type in the form "type[.subtype]".
|
||||
fsType string
|
||||
// Per-mount options.
|
||||
mountOptions []string
|
||||
// Per-superblock options.
|
||||
superOptions []string
|
||||
}
|
||||
|
||||
// parseMountInfo parses /proc/xxx/mountinfo.
|
||||
@ -655,22 +647,66 @@ func parseMountInfo(filename string) ([]mountInfo, error) {
|
||||
// the last split() item is empty string following the last \n
|
||||
continue
|
||||
}
|
||||
// See `man proc` for authoritative description of format of the file.
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 7 {
|
||||
return nil, fmt.Errorf("wrong number of fields in (expected %d, got %d): %s", 8, len(fields), line)
|
||||
if len(fields) < expectedAtLeastNumFieldsPerMountInfo {
|
||||
return nil, fmt.Errorf("wrong number of fields in (expected at least %d, got %d): %s", expectedAtLeastNumFieldsPerMountInfo, len(fields), line)
|
||||
}
|
||||
id, err := strconv.Atoi(fields[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parentID, err := strconv.Atoi(fields[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info := mountInfo{
|
||||
mountPoint: fields[4],
|
||||
optional: []string{},
|
||||
id: id,
|
||||
parentID: parentID,
|
||||
majorMinor: fields[2],
|
||||
root: fields[3],
|
||||
mountPoint: fields[4],
|
||||
mountOptions: strings.Split(fields[5], ","),
|
||||
}
|
||||
for i := 6; i < len(fields) && fields[i] != "-"; i++ {
|
||||
info.optional = append(info.optional, fields[i])
|
||||
// All fields until "-" are "optional fields".
|
||||
i := 6
|
||||
for ; i < len(fields) && fields[i] != "-"; i++ {
|
||||
info.optionalFields = append(info.optionalFields, fields[i])
|
||||
}
|
||||
// Parse the rest 3 fields.
|
||||
i += 1
|
||||
if len(fields)-i < 3 {
|
||||
return nil, fmt.Errorf("expect 3 fields in %s, got %d", line, len(fields)-i)
|
||||
}
|
||||
info.fsType = fields[i]
|
||||
info.source = fields[i+1]
|
||||
info.superOptions = strings.Split(fields[i+2], ",")
|
||||
infos = append(infos, info)
|
||||
}
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
func findMountInfo(path, mountInfoPath string) (mountInfo, error) {
|
||||
infos, err := parseMountInfo(mountInfoPath)
|
||||
if err != nil {
|
||||
return mountInfo{}, err
|
||||
}
|
||||
|
||||
// process /proc/xxx/mountinfo in backward order and find the first mount
|
||||
// point that is prefix of 'path' - that's the mount where path resides
|
||||
var info *mountInfo
|
||||
for i := len(infos) - 1; i >= 0; i-- {
|
||||
if pathWithinBase(path, infos[i].mountPoint) {
|
||||
info = &infos[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if info == nil {
|
||||
return mountInfo{}, fmt.Errorf("cannot find mount point for %q", path)
|
||||
}
|
||||
return *info, nil
|
||||
}
|
||||
|
||||
// doMakeRShared is common implementation of MakeRShared on Linux. It checks if
|
||||
// path is shared and bind-mounts it as rshared if needed. mountCmd and
|
||||
// mountArgs are expected to contain mount-like command, doMakeRShared will add
|
||||
@ -698,3 +734,624 @@ func doMakeRShared(path string, mountInfoFilename string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSELinuxSupport is common implementation of GetSELinuxSupport on Linux.
|
||||
func getSELinuxSupport(path string, mountInfoFilename string) (bool, error) {
|
||||
info, err := findMountInfo(path, mountInfoFilename)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// "seclabel" can be both in mount options and super options.
|
||||
for _, opt := range info.superOptions {
|
||||
if opt == "seclabel" {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
for _, opt := range info.mountOptions {
|
||||
if opt == "seclabel" {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
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
|
||||
glog.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)
|
||||
}
|
||||
glog.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 {
|
||||
glog.V(4).Infof("doBindSubPath() failed for %q, cleaning up subpath", bindPathTarget)
|
||||
if cleanErr := cleanSubPath(mounter, subpath); cleanErr != nil {
|
||||
glog.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"}
|
||||
glog.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
|
||||
|
||||
glog.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)
|
||||
glog.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() {
|
||||
glog.V(4).Infof("Container file is not a directory: %s", containerDir.Name())
|
||||
continue
|
||||
}
|
||||
glog.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())
|
||||
subPaths, err := ioutil.ReadDir(fullContainerDirPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading %s: %s", fullContainerDirPath, err)
|
||||
}
|
||||
for _, subPath := range subPaths {
|
||||
if err = doCleanSubPath(mounter, fullContainerDirPath, subPath.Name()); err != nil {
|
||||
return 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)
|
||||
}
|
||||
glog.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)
|
||||
}
|
||||
glog.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)
|
||||
}
|
||||
glog.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>
|
||||
glog.V(4).Infof("Cleaning up subpath mounts for subpath %v", subPathIndex)
|
||||
fullSubPath := filepath.Join(fullContainerDirPath, subPathIndex)
|
||||
notMnt, err := IsNotMountPoint(mounter, fullSubPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error checking %s for mount: %s", fullSubPath, err)
|
||||
}
|
||||
// Unmount it
|
||||
if !notMnt {
|
||||
if err = mounter.Unmount(fullSubPath); err != nil {
|
||||
return fmt.Errorf("error unmounting %s: %s", fullSubPath, err)
|
||||
}
|
||||
glog.V(5).Infof("Unmounted %s", fullSubPath)
|
||||
}
|
||||
// Remove it *non*-recursively, just in case there were some hiccups.
|
||||
if err = os.Remove(fullSubPath); err != nil {
|
||||
return fmt.Errorf("error deleting %s: %s", fullSubPath, err)
|
||||
}
|
||||
glog.V(5).Infof("Removed %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) {
|
||||
glog.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) {
|
||||
glog.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)
|
||||
}
|
||||
glog.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) {
|
||||
realpath, err := filepath.EvalSymlinks(pathname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return searchMountPoints(realpath, procMountInfoPath)
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||
return getSELinuxSupport(pathname, procMountInfoPath)
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
|
||||
realpath, err := filepath.EvalSymlinks(pathname)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return getFSGroup(realpath)
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetMode(pathname string) (os.FileMode, error) {
|
||||
return getMode(pathname)
|
||||
}
|
||||
|
||||
// This implementation is shared between Linux and NsEnterMounter
|
||||
func getFSGroup(pathname string) (int64, error) {
|
||||
info, err := os.Stat(pathname)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int64(info.Sys().(*syscall.Stat_t).Gid), nil
|
||||
}
|
||||
|
||||
// This implementation is shared between Linux and NsEnterMounter
|
||||
func getMode(pathname string) (os.FileMode, error) {
|
||||
info, err := os.Stat(pathname)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
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 {
|
||||
glog.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.
|
||||
glog.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)
|
||||
}
|
||||
|
||||
glog.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 {
|
||||
glog.V(4).Infof("Closing FD %v failed for safemkdir(%v): %v", parentFD, pathname, err)
|
||||
}
|
||||
}
|
||||
if childFD != -1 {
|
||||
if err = syscall.Close(childFD); err != nil {
|
||||
glog.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)
|
||||
glog.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 {
|
||||
glog.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 {
|
||||
glog.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 {
|
||||
glog.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 {
|
||||
glog.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 {
|
||||
glog.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)
|
||||
}
|
||||
|
||||
glog.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.
|
||||
// Some filesystems may share a source name, e.g. tmpfs. And for bind mounting,
|
||||
// it's possible to mount a non-root path of a filesystem, so we need to use
|
||||
// root path and major:minor to represent mount source uniquely.
|
||||
// This implementation is shared between Linux and NsEnterMounter
|
||||
func searchMountPoints(hostSource, mountInfoPath string) ([]string, error) {
|
||||
mis, err := parseMountInfo(mountInfoPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mountID := 0
|
||||
rootPath := ""
|
||||
majorMinor := ""
|
||||
|
||||
// Finding the underlying root path and major:minor if possible.
|
||||
// We need search in backward order because it's possible for later mounts
|
||||
// to overlap earlier mounts.
|
||||
for i := len(mis) - 1; i >= 0; i-- {
|
||||
if hostSource == mis[i].mountPoint || pathWithinBase(hostSource, mis[i].mountPoint) {
|
||||
// If it's a mount point or path under a mount point.
|
||||
mountID = mis[i].id
|
||||
rootPath = filepath.Join(mis[i].root, strings.TrimPrefix(hostSource, mis[i].mountPoint))
|
||||
majorMinor = mis[i].majorMinor
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if rootPath == "" || majorMinor == "" {
|
||||
return nil, fmt.Errorf("failed to get root path and major:minor for %s", hostSource)
|
||||
}
|
||||
|
||||
var refs []string
|
||||
for i := range mis {
|
||||
if mis[i].id == mountID {
|
||||
// Ignore mount entry for mount source itself.
|
||||
continue
|
||||
}
|
||||
if mis[i].root == rootPath && mis[i].majorMinor == majorMinor {
|
||||
refs = append(refs, mis[i].mountPoint)
|
||||
}
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
|
1759
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux_test.go
generated
vendored
1759
vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
63
vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go
generated
vendored
63
vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go
generated
vendored
@ -20,12 +20,15 @@ package mount
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Mounter struct {
|
||||
mounterPath string
|
||||
}
|
||||
|
||||
var unsupportedErr = errors.New("util/mount on this platform is not supported")
|
||||
|
||||
// New returns a mount.Interface for the current system.
|
||||
// It provides options to override the default mounter behavior.
|
||||
// mounterPath allows using an alternative to `/bin/mount` for mounting.
|
||||
@ -36,21 +39,21 @@ func New(mounterPath string) Interface {
|
||||
}
|
||||
|
||||
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
|
||||
return nil
|
||||
return unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) Unmount(target string) error {
|
||||
return nil
|
||||
return unsupportedErr
|
||||
}
|
||||
|
||||
// GetMountRefs finds all other references to the device referenced
|
||||
// by mountPath; returns a list of paths.
|
||||
func GetMountRefs(mounter Interface, mountPath string) ([]string, error) {
|
||||
return []string{}, nil
|
||||
return []string{}, unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) List() ([]MountPoint, error) {
|
||||
return []MountPoint{}, nil
|
||||
return []MountPoint{}, unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) IsMountPointMatch(mp MountPoint, dir string) bool {
|
||||
@ -62,27 +65,27 @@ func (mounter *Mounter) IsNotMountPoint(dir string) (bool, error) {
|
||||
}
|
||||
|
||||
func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||
return true, nil
|
||||
return true, unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
|
||||
return "", nil
|
||||
return "", unsupportedErr
|
||||
}
|
||||
|
||||
func getDeviceNameFromMount(mounter Interface, mountPath, pluginDir string) (string, error) {
|
||||
return "", nil
|
||||
return "", unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
|
||||
return false, nil
|
||||
return false, unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
|
||||
return true, nil
|
||||
return true, unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) MakeRShared(path string) error {
|
||||
return nil
|
||||
return unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
|
||||
@ -90,21 +93,49 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string,
|
||||
}
|
||||
|
||||
func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) {
|
||||
return true, nil
|
||||
return true, unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
|
||||
return FileType("fake"), errors.New("not implemented")
|
||||
return FileType("fake"), unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) MakeDir(pathname string) error {
|
||||
return nil
|
||||
return unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) MakeFile(pathname string) error {
|
||||
return nil
|
||||
return unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) ExistsPath(pathname string) bool {
|
||||
return true
|
||||
func (mounter *Mounter) ExistsPath(pathname string) (bool, error) {
|
||||
return true, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (mounter *Mounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
|
||||
return subPath.Path, nil, unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) CleanSubPaths(podDir string, volumeName string) error {
|
||||
return unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error {
|
||||
return unsupportedErr
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
|
||||
return -1, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||
return false, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetMode(pathname string) (os.FileMode, error) {
|
||||
return 0, errors.New("not implemented")
|
||||
}
|
||||
|
326
vendor/k8s.io/kubernetes/pkg/util/mount/mount_windows.go
generated
vendored
326
vendor/k8s.io/kubernetes/pkg/util/mount/mount_windows.go
generated
vendored
@ -29,6 +29,8 @@ import (
|
||||
"syscall"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
utilfile "k8s.io/kubernetes/pkg/util/file"
|
||||
)
|
||||
|
||||
// Mounter provides the default implementation of mount.Interface
|
||||
@ -145,7 +147,15 @@ func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
||||
}
|
||||
// If current file is a symlink, then it is a mountpoint.
|
||||
if stat.Mode()&os.ModeSymlink != 0 {
|
||||
return false, nil
|
||||
target, err := os.Readlink(file)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("readlink error: %v", err)
|
||||
}
|
||||
exists, err := mounter.ExistsPath(target)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
return !exists, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
@ -201,31 +211,7 @@ func (mounter *Mounter) MakeRShared(path string) error {
|
||||
|
||||
// GetFileType checks for sockets/block/character devices
|
||||
func (mounter *Mounter) GetFileType(pathname string) (FileType, error) {
|
||||
var pathType FileType
|
||||
info, err := os.Stat(pathname)
|
||||
if os.IsNotExist(err) {
|
||||
return pathType, fmt.Errorf("path %q does not exist", pathname)
|
||||
}
|
||||
// err in call to os.Stat
|
||||
if err != nil {
|
||||
return pathType, err
|
||||
}
|
||||
|
||||
mode := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes
|
||||
switch mode & syscall.S_IFMT {
|
||||
case syscall.S_IFSOCK:
|
||||
return FileTypeSocket, nil
|
||||
case syscall.S_IFBLK:
|
||||
return FileTypeBlockDev, nil
|
||||
case syscall.S_IFCHR:
|
||||
return FileTypeCharDev, nil
|
||||
case syscall.S_IFDIR:
|
||||
return FileTypeDirectory, nil
|
||||
case syscall.S_IFREG:
|
||||
return FileTypeFile, nil
|
||||
}
|
||||
|
||||
return pathType, fmt.Errorf("only recognise file, directory, socket, block device and character device")
|
||||
return getFileType(pathname)
|
||||
}
|
||||
|
||||
// MakeFile creates a new directory
|
||||
@ -252,12 +238,125 @@ func (mounter *Mounter) MakeFile(pathname string) error {
|
||||
}
|
||||
|
||||
// ExistsPath checks whether the path exists
|
||||
func (mounter *Mounter) ExistsPath(pathname string) bool {
|
||||
_, err := os.Stat(pathname)
|
||||
if err != nil {
|
||||
return false
|
||||
func (mounter *Mounter) ExistsPath(pathname string) (bool, error) {
|
||||
return utilfile.FileExists(pathname)
|
||||
}
|
||||
|
||||
// check whether hostPath is within volume path
|
||||
// this func will lock all intermediate subpath directories, need to close handle outside of this func after container started
|
||||
func lockAndCheckSubPath(volumePath, hostPath string) ([]uintptr, error) {
|
||||
if len(volumePath) == 0 || len(hostPath) == 0 {
|
||||
return []uintptr{}, nil
|
||||
}
|
||||
return true
|
||||
|
||||
finalSubPath, err := filepath.EvalSymlinks(hostPath)
|
||||
if err != nil {
|
||||
return []uintptr{}, fmt.Errorf("cannot read link %s: %s", hostPath, err)
|
||||
}
|
||||
finalVolumePath, err := filepath.EvalSymlinks(volumePath)
|
||||
if err != nil {
|
||||
return []uintptr{}, fmt.Errorf("cannot read link %s: %s", volumePath, err)
|
||||
}
|
||||
|
||||
return lockAndCheckSubPathWithoutSymlink(finalVolumePath, finalSubPath)
|
||||
}
|
||||
|
||||
// lock all intermediate subPath directories and check they are all within volumePath
|
||||
// volumePath & subPath should not contain any symlink, otherwise it will return error
|
||||
func lockAndCheckSubPathWithoutSymlink(volumePath, subPath string) ([]uintptr, error) {
|
||||
if len(volumePath) == 0 || len(subPath) == 0 {
|
||||
return []uintptr{}, nil
|
||||
}
|
||||
|
||||
// get relative path to volumePath
|
||||
relSubPath, err := filepath.Rel(volumePath, subPath)
|
||||
if err != nil {
|
||||
return []uintptr{}, fmt.Errorf("Rel(%s, %s) error: %v", volumePath, subPath, err)
|
||||
}
|
||||
if startsWithBackstep(relSubPath) {
|
||||
return []uintptr{}, fmt.Errorf("SubPath %q not within volume path %q", subPath, volumePath)
|
||||
}
|
||||
|
||||
if relSubPath == "." {
|
||||
// volumePath and subPath are equal
|
||||
return []uintptr{}, nil
|
||||
}
|
||||
|
||||
fileHandles := []uintptr{}
|
||||
var errorResult error
|
||||
|
||||
currentFullPath := volumePath
|
||||
dirs := strings.Split(relSubPath, string(os.PathSeparator))
|
||||
for _, dir := range dirs {
|
||||
// lock intermediate subPath directory first
|
||||
currentFullPath = filepath.Join(currentFullPath, dir)
|
||||
handle, err := lockPath(currentFullPath)
|
||||
if err != nil {
|
||||
errorResult = fmt.Errorf("cannot lock path %s: %s", currentFullPath, err)
|
||||
break
|
||||
}
|
||||
fileHandles = append(fileHandles, handle)
|
||||
|
||||
// make sure intermediate subPath directory does not contain symlink any more
|
||||
stat, err := os.Lstat(currentFullPath)
|
||||
if err != nil {
|
||||
errorResult = fmt.Errorf("Lstat(%q) error: %v", currentFullPath, err)
|
||||
break
|
||||
}
|
||||
if stat.Mode()&os.ModeSymlink != 0 {
|
||||
errorResult = fmt.Errorf("subpath %q is an unexpected symlink after EvalSymlinks", currentFullPath)
|
||||
break
|
||||
}
|
||||
|
||||
if !pathWithinBase(currentFullPath, volumePath) {
|
||||
errorResult = fmt.Errorf("SubPath %q not within volume path %q", currentFullPath, volumePath)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return fileHandles, errorResult
|
||||
}
|
||||
|
||||
// unlockPath unlock directories
|
||||
func unlockPath(fileHandles []uintptr) {
|
||||
if fileHandles != nil {
|
||||
for _, handle := range fileHandles {
|
||||
syscall.CloseHandle(syscall.Handle(handle))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lockPath locks a directory or symlink, return handle, exec "syscall.CloseHandle(handle)" to unlock the path
|
||||
func lockPath(path string) (uintptr, error) {
|
||||
if len(path) == 0 {
|
||||
return uintptr(syscall.InvalidHandle), syscall.ERROR_FILE_NOT_FOUND
|
||||
}
|
||||
pathp, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return uintptr(syscall.InvalidHandle), err
|
||||
}
|
||||
access := uint32(syscall.GENERIC_READ)
|
||||
sharemode := uint32(syscall.FILE_SHARE_READ)
|
||||
createmode := uint32(syscall.OPEN_EXISTING)
|
||||
flags := uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS | syscall.FILE_FLAG_OPEN_REPARSE_POINT)
|
||||
fd, err := syscall.CreateFile(pathp, access, sharemode, nil, createmode, flags, 0)
|
||||
return uintptr(fd), err
|
||||
}
|
||||
|
||||
// Lock all directories in subPath and check they're not symlinks.
|
||||
func (mounter *Mounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
|
||||
handles, err := lockAndCheckSubPath(subPath.VolumePath, subPath.Path)
|
||||
|
||||
// Unlock the directories when the container starts
|
||||
cleanupAction = func() {
|
||||
unlockPath(handles)
|
||||
}
|
||||
return subPath.Path, cleanupAction, err
|
||||
}
|
||||
|
||||
// No bind-mounts for subpaths are necessary on Windows
|
||||
func (mounter *Mounter) CleanSubPaths(podDir string, volumeName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
|
||||
@ -265,10 +364,23 @@ func (mounter *SafeFormatAndMount) formatAndMount(source string, target string,
|
||||
glog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target)
|
||||
|
||||
if err := ValidateDiskNumber(source); err != nil {
|
||||
glog.Errorf("azureMount: formatAndMount failed, err: %v\n", err)
|
||||
glog.Errorf("diskMount: formatAndMount failed, err: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(fstype) == 0 {
|
||||
// Use 'NTFS' as the default
|
||||
fstype = "NTFS"
|
||||
}
|
||||
|
||||
// format disk if it is unformatted(raw)
|
||||
cmd := fmt.Sprintf("Get-Disk -Number %s | Where partitionstyle -eq 'raw' | Initialize-Disk -PartitionStyle MBR -PassThru"+
|
||||
" | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -FileSystem %s -Confirm:$false", source, fstype)
|
||||
if output, err := mounter.Exec.Run("powershell", "/c", cmd); err != nil {
|
||||
return fmt.Errorf("diskMount: format disk failed, error: %v, output: %q", err, string(output))
|
||||
}
|
||||
glog.V(4).Infof("diskMount: Disk successfully formatted, disk: %q, fstype: %q", source, fstype)
|
||||
|
||||
driveLetter, err := getDriveLetterByDiskNumber(source, mounter.Exec)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -344,3 +456,153 @@ func getAllParentLinks(path string) ([]string, error) {
|
||||
|
||||
return links, nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
|
||||
realpath, err := filepath.EvalSymlinks(pathname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return getMountRefsByDev(mounter, realpath)
|
||||
}
|
||||
|
||||
// Note that on windows, it always returns 0. We actually don't set FSGroup on
|
||||
// windows platform, see SetVolumeOwnership implementation.
|
||||
func (mounter *Mounter) GetFSGroup(pathname string) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||
// Windows does not support SELinux.
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (mounter *Mounter) GetMode(pathname string) (os.FileMode, error) {
|
||||
info, err := os.Stat(pathname)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return info.Mode(), nil
|
||||
}
|
||||
|
||||
// SafeMakeDir makes sure that the created directory does not escape given base directory mis-using symlinks.
|
||||
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 doSafeMakeDir(pathname string, base string, perm os.FileMode) error {
|
||||
glog.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.
|
||||
glog.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)
|
||||
}
|
||||
if len(toCreate) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensure the existing directory is inside allowed base
|
||||
fullExistingPath, err := filepath.EvalSymlinks(existingPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening existing directory %s: %s", existingPath, err)
|
||||
}
|
||||
fullBasePath, err := filepath.EvalSymlinks(base)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot read link %s: %s", base, err)
|
||||
}
|
||||
if !pathWithinBase(fullExistingPath, fullBasePath) {
|
||||
return fmt.Errorf("path %s is outside of allowed base %s", fullExistingPath, err)
|
||||
}
|
||||
|
||||
// lock all intermediate directories from fullBasePath to fullExistingPath (top to bottom)
|
||||
fileHandles, err := lockAndCheckSubPathWithoutSymlink(fullBasePath, fullExistingPath)
|
||||
defer unlockPath(fileHandles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
glog.V(4).Infof("%q already exists, %q to create", fullExistingPath, filepath.Join(toCreate...))
|
||||
currentPath := fullExistingPath
|
||||
// create the directories one by one, making sure nobody can change
|
||||
// created directory into symlink by lock that directory immediately
|
||||
for _, dir := range toCreate {
|
||||
currentPath = filepath.Join(currentPath, dir)
|
||||
glog.V(4).Infof("Creating %s", dir)
|
||||
if err := os.Mkdir(currentPath, perm); err != nil {
|
||||
return fmt.Errorf("cannot create directory %s: %s", currentPath, err)
|
||||
}
|
||||
handle, err := lockPath(currentPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot lock path %s: %s", currentPath, err)
|
||||
}
|
||||
defer syscall.CloseHandle(syscall.Handle(handle))
|
||||
// make sure newly created directory does not contain symlink after lock
|
||||
stat, err := os.Lstat(currentPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Lstat(%q) error: %v", currentPath, err)
|
||||
}
|
||||
if stat.Mode()&os.ModeSymlink != 0 {
|
||||
return fmt.Errorf("subpath %q is an unexpected symlink after Mkdir", currentPath)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if startsWithBackstep(rel) {
|
||||
return base, nil, fmt.Errorf("pathname(%s) is not within base(%s)", pathname, base)
|
||||
}
|
||||
|
||||
if rel == "." {
|
||||
// base and pathname are equal
|
||||
return pathname, []string{}, nil
|
||||
}
|
||||
|
||||
dirs := strings.Split(rel, string(filepath.Separator))
|
||||
|
||||
parent := base
|
||||
currentPath := base
|
||||
for i, dir := range dirs {
|
||||
parent = currentPath
|
||||
currentPath = filepath.Join(parent, dir)
|
||||
if _, err := os.Lstat(currentPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return parent, dirs[i:], nil
|
||||
}
|
||||
return base, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return pathname, []string{}, nil
|
||||
}
|
||||
|
661
vendor/k8s.io/kubernetes/pkg/util/mount/mount_windows_test.go
generated
vendored
661
vendor/k8s.io/kubernetes/pkg/util/mount/mount_windows_test.go
generated
vendored
@ -20,8 +20,14 @@ package mount
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNormalizeWindowsPath(t *testing.T) {
|
||||
@ -132,3 +138,658 @@ func TestGetMountRefs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoSafeMakeDir(t *testing.T) {
|
||||
base, err := ioutil.TempDir("", "TestDoSafeMakeDir")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
defer os.RemoveAll(base)
|
||||
|
||||
testingVolumePath := filepath.Join(base, "testingVolumePath")
|
||||
os.MkdirAll(testingVolumePath, 0755)
|
||||
defer os.RemoveAll(testingVolumePath)
|
||||
|
||||
tests := []struct {
|
||||
volumePath string
|
||||
subPath string
|
||||
expectError bool
|
||||
symlinkTarget string
|
||||
}{
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: ``,
|
||||
expectError: true,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `x`),
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\c\d`),
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `symlink`),
|
||||
expectError: false,
|
||||
symlinkTarget: base,
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `symlink\c\d`),
|
||||
expectError: true,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `symlink\y926`),
|
||||
expectError: true,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\symlink`),
|
||||
expectError: false,
|
||||
symlinkTarget: base,
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\x\symlink`),
|
||||
expectError: false,
|
||||
symlinkTarget: filepath.Join(testingVolumePath, `a`),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if len(test.volumePath) > 0 && len(test.subPath) > 0 && len(test.symlinkTarget) > 0 {
|
||||
// make all parent sub directories
|
||||
if parent := filepath.Dir(test.subPath); parent != "." {
|
||||
os.MkdirAll(parent, 0755)
|
||||
}
|
||||
|
||||
// make last element as symlink
|
||||
linkPath := test.subPath
|
||||
if _, err := os.Stat(linkPath); err != nil && os.IsNotExist(err) {
|
||||
if err := makeLink(linkPath, test.symlinkTarget); err != nil {
|
||||
t.Fatalf("unexpected error: %v", fmt.Errorf("mklink link(%q) target(%q) error: %q", linkPath, test.symlinkTarget, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := doSafeMakeDir(test.subPath, test.volumePath, os.FileMode(0755))
|
||||
if test.expectError {
|
||||
assert.NotNil(t, err, "Expect error during doSafeMakeDir(%s, %s)", test.subPath, test.volumePath)
|
||||
continue
|
||||
}
|
||||
assert.Nil(t, err, "Expect no error during doSafeMakeDir(%s, %s)", test.subPath, test.volumePath)
|
||||
if _, err := os.Stat(test.subPath); os.IsNotExist(err) {
|
||||
t.Errorf("subPath should exists after doSafeMakeDir(%s, %s)", test.subPath, test.volumePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLockAndCheckSubPath(t *testing.T) {
|
||||
base, err := ioutil.TempDir("", "TestLockAndCheckSubPath")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
defer os.RemoveAll(base)
|
||||
|
||||
testingVolumePath := filepath.Join(base, "testingVolumePath")
|
||||
|
||||
tests := []struct {
|
||||
volumePath string
|
||||
subPath string
|
||||
expectedHandleCount int
|
||||
expectError bool
|
||||
symlinkTarget string
|
||||
}{
|
||||
{
|
||||
volumePath: `c:\`,
|
||||
subPath: ``,
|
||||
expectedHandleCount: 0,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: ``,
|
||||
subPath: `a`,
|
||||
expectedHandleCount: 0,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a`),
|
||||
expectedHandleCount: 1,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\c\d`),
|
||||
expectedHandleCount: 4,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `symlink`),
|
||||
expectedHandleCount: 0,
|
||||
expectError: true,
|
||||
symlinkTarget: base,
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\c\symlink`),
|
||||
expectedHandleCount: 0,
|
||||
expectError: true,
|
||||
symlinkTarget: base,
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\c\d\symlink`),
|
||||
expectedHandleCount: 2,
|
||||
expectError: false,
|
||||
symlinkTarget: filepath.Join(testingVolumePath, `a\b`),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if len(test.volumePath) > 0 && len(test.subPath) > 0 {
|
||||
os.MkdirAll(test.volumePath, 0755)
|
||||
if len(test.symlinkTarget) == 0 {
|
||||
// make all intermediate sub directories
|
||||
os.MkdirAll(test.subPath, 0755)
|
||||
} else {
|
||||
// make all parent sub directories
|
||||
if parent := filepath.Dir(test.subPath); parent != "." {
|
||||
os.MkdirAll(parent, 0755)
|
||||
}
|
||||
|
||||
// make last element as symlink
|
||||
linkPath := test.subPath
|
||||
if _, err := os.Stat(linkPath); err != nil && os.IsNotExist(err) {
|
||||
if err := makeLink(linkPath, test.symlinkTarget); err != nil {
|
||||
t.Fatalf("unexpected error: %v", fmt.Errorf("mklink link(%q) target(%q) error: %q", linkPath, test.symlinkTarget, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileHandles, err := lockAndCheckSubPath(test.volumePath, test.subPath)
|
||||
unlockPath(fileHandles)
|
||||
assert.Equal(t, test.expectedHandleCount, len(fileHandles))
|
||||
if test.expectError {
|
||||
assert.NotNil(t, err, "Expect error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
|
||||
continue
|
||||
}
|
||||
assert.Nil(t, err, "Expect no error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
|
||||
}
|
||||
|
||||
// remove dir will happen after closing all file handles
|
||||
assert.Nil(t, os.RemoveAll(testingVolumePath), "Expect no error during remove dir %s", testingVolumePath)
|
||||
}
|
||||
|
||||
func TestLockAndCheckSubPathWithoutSymlink(t *testing.T) {
|
||||
base, err := ioutil.TempDir("", "TestLockAndCheckSubPathWithoutSymlink")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
defer os.RemoveAll(base)
|
||||
|
||||
testingVolumePath := filepath.Join(base, "testingVolumePath")
|
||||
|
||||
tests := []struct {
|
||||
volumePath string
|
||||
subPath string
|
||||
expectedHandleCount int
|
||||
expectError bool
|
||||
symlinkTarget string
|
||||
}{
|
||||
{
|
||||
volumePath: `c:\`,
|
||||
subPath: ``,
|
||||
expectedHandleCount: 0,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: ``,
|
||||
subPath: `a`,
|
||||
expectedHandleCount: 0,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a`),
|
||||
expectedHandleCount: 1,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\c\d`),
|
||||
expectedHandleCount: 4,
|
||||
expectError: false,
|
||||
symlinkTarget: "",
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `symlink`),
|
||||
expectedHandleCount: 1,
|
||||
expectError: true,
|
||||
symlinkTarget: base,
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\c\symlink`),
|
||||
expectedHandleCount: 4,
|
||||
expectError: true,
|
||||
symlinkTarget: base,
|
||||
},
|
||||
{
|
||||
volumePath: testingVolumePath,
|
||||
subPath: filepath.Join(testingVolumePath, `a\b\c\d\symlink`),
|
||||
expectedHandleCount: 5,
|
||||
expectError: true,
|
||||
symlinkTarget: filepath.Join(testingVolumePath, `a\b`),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if len(test.volumePath) > 0 && len(test.subPath) > 0 {
|
||||
os.MkdirAll(test.volumePath, 0755)
|
||||
if len(test.symlinkTarget) == 0 {
|
||||
// make all intermediate sub directories
|
||||
os.MkdirAll(test.subPath, 0755)
|
||||
} else {
|
||||
// make all parent sub directories
|
||||
if parent := filepath.Dir(test.subPath); parent != "." {
|
||||
os.MkdirAll(parent, 0755)
|
||||
}
|
||||
|
||||
// make last element as symlink
|
||||
linkPath := test.subPath
|
||||
if _, err := os.Stat(linkPath); err != nil && os.IsNotExist(err) {
|
||||
if err := makeLink(linkPath, test.symlinkTarget); err != nil {
|
||||
t.Fatalf("unexpected error: %v", fmt.Errorf("mklink link(%q) target(%q) error: %q", linkPath, test.symlinkTarget, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileHandles, err := lockAndCheckSubPathWithoutSymlink(test.volumePath, test.subPath)
|
||||
unlockPath(fileHandles)
|
||||
assert.Equal(t, test.expectedHandleCount, len(fileHandles))
|
||||
if test.expectError {
|
||||
assert.NotNil(t, err, "Expect error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
|
||||
continue
|
||||
}
|
||||
assert.Nil(t, err, "Expect no error during LockAndCheckSubPath(%s, %s)", test.volumePath, test.subPath)
|
||||
}
|
||||
|
||||
// remove dir will happen after closing all file handles
|
||||
assert.Nil(t, os.RemoveAll(testingVolumePath), "Expect no error during remove dir %s", testingVolumePath)
|
||||
}
|
||||
|
||||
func TestFindExistingPrefix(t *testing.T) {
|
||||
base, err := ioutil.TempDir("", "TestFindExistingPrefix")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
defer os.RemoveAll(base)
|
||||
|
||||
testingVolumePath := filepath.Join(base, "testingVolumePath")
|
||||
|
||||
tests := []struct {
|
||||
base string
|
||||
pathname string
|
||||
expectError bool
|
||||
expectedExistingPath string
|
||||
expectedToCreateDirs []string
|
||||
createSubPathBeforeTest bool
|
||||
}{
|
||||
{
|
||||
base: `c:\tmp\a`,
|
||||
pathname: `c:\tmp\b`,
|
||||
expectError: true,
|
||||
expectedExistingPath: "",
|
||||
expectedToCreateDirs: []string{},
|
||||
createSubPathBeforeTest: false,
|
||||
},
|
||||
{
|
||||
base: ``,
|
||||
pathname: `c:\tmp\b`,
|
||||
expectError: true,
|
||||
expectedExistingPath: "",
|
||||
expectedToCreateDirs: []string{},
|
||||
createSubPathBeforeTest: false,
|
||||
},
|
||||
{
|
||||
base: `c:\tmp\a`,
|
||||
pathname: `d:\tmp\b`,
|
||||
expectError: true,
|
||||
expectedExistingPath: "",
|
||||
expectedToCreateDirs: []string{},
|
||||
createSubPathBeforeTest: false,
|
||||
},
|
||||
{
|
||||
base: testingVolumePath,
|
||||
pathname: testingVolumePath,
|
||||
expectError: false,
|
||||
expectedExistingPath: testingVolumePath,
|
||||
expectedToCreateDirs: []string{},
|
||||
createSubPathBeforeTest: false,
|
||||
},
|
||||
{
|
||||
base: testingVolumePath,
|
||||
pathname: filepath.Join(testingVolumePath, `a\b`),
|
||||
expectError: false,
|
||||
expectedExistingPath: filepath.Join(testingVolumePath, `a\b`),
|
||||
expectedToCreateDirs: []string{},
|
||||
createSubPathBeforeTest: true,
|
||||
},
|
||||
{
|
||||
base: testingVolumePath,
|
||||
pathname: filepath.Join(testingVolumePath, `a\b\c\`),
|
||||
expectError: false,
|
||||
expectedExistingPath: filepath.Join(testingVolumePath, `a\b`),
|
||||
expectedToCreateDirs: []string{`c`},
|
||||
createSubPathBeforeTest: false,
|
||||
},
|
||||
{
|
||||
base: testingVolumePath,
|
||||
pathname: filepath.Join(testingVolumePath, `a\b\c\d`),
|
||||
expectError: false,
|
||||
expectedExistingPath: filepath.Join(testingVolumePath, `a\b`),
|
||||
expectedToCreateDirs: []string{`c`, `d`},
|
||||
createSubPathBeforeTest: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if test.createSubPathBeforeTest {
|
||||
os.MkdirAll(test.pathname, 0755)
|
||||
}
|
||||
|
||||
existingPath, toCreate, err := findExistingPrefix(test.base, test.pathname)
|
||||
if test.expectError {
|
||||
assert.NotNil(t, err, "Expect error during findExistingPrefix(%s, %s)", test.base, test.pathname)
|
||||
continue
|
||||
}
|
||||
assert.Nil(t, err, "Expect no error during findExistingPrefix(%s, %s)", test.base, test.pathname)
|
||||
|
||||
assert.Equal(t, test.expectedExistingPath, existingPath, "Expect result not equal with findExistingPrefix(%s, %s) return: %q, expected: %q",
|
||||
test.base, test.pathname, existingPath, test.expectedExistingPath)
|
||||
|
||||
assert.Equal(t, test.expectedToCreateDirs, toCreate, "Expect result not equal with findExistingPrefix(%s, %s) return: %q, expected: %q",
|
||||
test.base, test.pathname, toCreate, test.expectedToCreateDirs)
|
||||
|
||||
}
|
||||
// remove dir will happen after closing all file handles
|
||||
assert.Nil(t, os.RemoveAll(testingVolumePath), "Expect no error during remove dir %s", testingVolumePath)
|
||||
}
|
||||
|
||||
func TestPathWithinBase(t *testing.T) {
|
||||
tests := []struct {
|
||||
fullPath string
|
||||
basePath string
|
||||
expectedResult bool
|
||||
}{
|
||||
{
|
||||
fullPath: `c:\tmp\a\b\c`,
|
||||
basePath: `c:\tmp`,
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
fullPath: `c:\tmp1`,
|
||||
basePath: `c:\tmp2`,
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
fullPath: `c:\tmp`,
|
||||
basePath: `c:\tmp`,
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
fullPath: `c:\tmp`,
|
||||
basePath: `c:\tmp\a\b\c`,
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
fullPath: `c:\kubelet\pods\uuid\volumes\kubernetes.io~configmap\config\..timestamp\file.txt`,
|
||||
basePath: `c:\kubelet\pods\uuid\volumes\kubernetes.io~configmap\config`,
|
||||
expectedResult: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
result := pathWithinBase(test.fullPath, test.basePath)
|
||||
assert.Equal(t, result, test.expectedResult, "Expect result not equal with pathWithinBase(%s, %s) return: %q, expected: %q",
|
||||
test.fullPath, test.basePath, result, test.expectedResult)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFileType(t *testing.T) {
|
||||
mounter := New("fake/path")
|
||||
|
||||
testCase := []struct {
|
||||
name string
|
||||
expectedType FileType
|
||||
setUp func() (string, string, error)
|
||||
}{
|
||||
{
|
||||
"Directory Test",
|
||||
FileTypeDirectory,
|
||||
func() (string, string, error) {
|
||||
tempDir, err := ioutil.TempDir("", "test-get-filetype-")
|
||||
return tempDir, tempDir, err
|
||||
},
|
||||
},
|
||||
{
|
||||
"File Test",
|
||||
FileTypeFile,
|
||||
func() (string, string, error) {
|
||||
tempFile, err := ioutil.TempFile("", "test-get-filetype")
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
tempFile.Close()
|
||||
return tempFile.Name(), tempFile.Name(), nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for idx, tc := range testCase {
|
||||
path, cleanUpPath, err := tc.setUp()
|
||||
if err != nil {
|
||||
t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err)
|
||||
}
|
||||
if len(cleanUpPath) > 0 {
|
||||
defer os.RemoveAll(cleanUpPath)
|
||||
}
|
||||
|
||||
fileType, err := mounter.GetFileType(path)
|
||||
if err != nil {
|
||||
t.Fatalf("[%d-%s] unexpected error : %v", idx, tc.name, err)
|
||||
}
|
||||
if fileType != tc.expectedType {
|
||||
t.Fatalf("[%d-%s] expected %s, but got %s", idx, tc.name, tc.expectedType, fileType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsLikelyNotMountPoint(t *testing.T) {
|
||||
mounter := Mounter{"fake/path"}
|
||||
|
||||
tests := []struct {
|
||||
fileName string
|
||||
targetLinkName string
|
||||
setUp func(base, fileName, targetLinkName string) error
|
||||
expectedResult bool
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
"Dir",
|
||||
"",
|
||||
func(base, fileName, targetLinkName string) error {
|
||||
return os.Mkdir(filepath.Join(base, fileName), 0750)
|
||||
},
|
||||
true,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"InvalidDir",
|
||||
"",
|
||||
func(base, fileName, targetLinkName string) error {
|
||||
return nil
|
||||
},
|
||||
true,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"ValidSymLink",
|
||||
"targetSymLink",
|
||||
func(base, fileName, targetLinkName string) error {
|
||||
targeLinkPath := filepath.Join(base, targetLinkName)
|
||||
if err := os.Mkdir(targeLinkPath, 0750); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filePath := filepath.Join(base, fileName)
|
||||
if err := makeLink(filePath, targeLinkPath); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"InvalidSymLink",
|
||||
"targetSymLink2",
|
||||
func(base, fileName, targetLinkName string) error {
|
||||
targeLinkPath := filepath.Join(base, targetLinkName)
|
||||
if err := os.Mkdir(targeLinkPath, 0750); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filePath := filepath.Join(base, fileName)
|
||||
if err := makeLink(filePath, targeLinkPath); err != nil {
|
||||
return err
|
||||
}
|
||||
return removeLink(targeLinkPath)
|
||||
},
|
||||
true,
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
base, err := ioutil.TempDir("", test.fileName)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
defer os.RemoveAll(base)
|
||||
|
||||
if err := test.setUp(base, test.fileName, test.targetLinkName); err != nil {
|
||||
t.Fatalf("unexpected error in setUp(%s, %s): %v", test.fileName, test.targetLinkName, err)
|
||||
}
|
||||
|
||||
filePath := filepath.Join(base, test.fileName)
|
||||
result, err := mounter.IsLikelyNotMountPoint(filePath)
|
||||
assert.Equal(t, result, test.expectedResult, "Expect result not equal with IsLikelyNotMountPoint(%s) return: %q, expected: %q",
|
||||
filePath, result, test.expectedResult)
|
||||
|
||||
if test.expectError {
|
||||
assert.NotNil(t, err, "Expect error during IsLikelyNotMountPoint(%s)", filePath)
|
||||
} else {
|
||||
assert.Nil(t, err, "Expect error is nil during IsLikelyNotMountPoint(%s)", filePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatAndMount(t *testing.T) {
|
||||
fakeMounter := ErrorMounter{&FakeMounter{}, 0, nil}
|
||||
execCallback := func(cmd string, args ...string) ([]byte, error) {
|
||||
for j := range args {
|
||||
if strings.Contains(args[j], "Get-Disk -Number") {
|
||||
return []byte("0"), nil
|
||||
}
|
||||
|
||||
if strings.Contains(args[j], "Get-Partition -DiskNumber") {
|
||||
return []byte("0"), nil
|
||||
}
|
||||
|
||||
if strings.Contains(args[j], "mklink") {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Unexpected cmd %s, args %v", cmd, args)
|
||||
}
|
||||
fakeExec := NewFakeExec(execCallback)
|
||||
|
||||
mounter := SafeFormatAndMount{
|
||||
Interface: &fakeMounter,
|
||||
Exec: fakeExec,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
device string
|
||||
target string
|
||||
fstype string
|
||||
mountOptions []string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
"0",
|
||||
"disk",
|
||||
"NTFS",
|
||||
[]string{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"0",
|
||||
"disk",
|
||||
"",
|
||||
[]string{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"invalidDevice",
|
||||
"disk",
|
||||
"NTFS",
|
||||
[]string{},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
base, err := ioutil.TempDir("", test.device)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
defer os.RemoveAll(base)
|
||||
|
||||
target := filepath.Join(base, test.target)
|
||||
err = mounter.FormatAndMount(test.device, target, test.fstype, test.mountOptions)
|
||||
if test.expectError {
|
||||
assert.NotNil(t, err, "Expect error during FormatAndMount(%s, %s, %s, %v)", test.device, test.target, test.fstype, test.mountOptions)
|
||||
} else {
|
||||
assert.Nil(t, err, "Expect error is nil during FormatAndMount(%s, %s, %s, %v)", test.device, test.target, test.fstype, test.mountOptions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
198
vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go
generated
vendored
198
vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go
generated
vendored
@ -23,8 +23,11 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"golang.org/x/sys/unix"
|
||||
utilfile "k8s.io/kubernetes/pkg/util/file"
|
||||
"k8s.io/kubernetes/pkg/util/nsenter"
|
||||
)
|
||||
|
||||
@ -40,10 +43,16 @@ const (
|
||||
// the host's mount namespace.
|
||||
type NsenterMounter struct {
|
||||
ne *nsenter.Nsenter
|
||||
// rootDir is location of /var/lib/kubelet directory.
|
||||
rootDir string
|
||||
}
|
||||
|
||||
func NewNsenterMounter() *NsenterMounter {
|
||||
return &NsenterMounter{ne: nsenter.NewNsenter()}
|
||||
// NewNsenterMounter creates a new mounter for kubelet that runs as a container.
|
||||
func NewNsenterMounter(rootDir string, ne *nsenter.Nsenter) *NsenterMounter {
|
||||
return &NsenterMounter{
|
||||
rootDir: rootDir,
|
||||
ne: ne,
|
||||
}
|
||||
}
|
||||
|
||||
// NsenterMounter implements mount.Interface
|
||||
@ -225,8 +234,13 @@ func (n *NsenterMounter) MakeRShared(path string) error {
|
||||
|
||||
func (mounter *NsenterMounter) GetFileType(pathname string) (FileType, error) {
|
||||
var pathType FileType
|
||||
outputBytes, err := mounter.ne.Exec("stat", []string{"-L", `--printf "%F"`, pathname}).CombinedOutput()
|
||||
outputBytes, err := mounter.ne.Exec("stat", []string{"-L", "--printf=%F", pathname}).CombinedOutput()
|
||||
if err != nil {
|
||||
if strings.Contains(string(outputBytes), "No such file") {
|
||||
err = fmt.Errorf("%s does not exist", pathname)
|
||||
} else {
|
||||
err = fmt.Errorf("stat %s error: %v", pathname, string(outputBytes))
|
||||
}
|
||||
return pathType, err
|
||||
}
|
||||
|
||||
@ -262,11 +276,177 @@ func (mounter *NsenterMounter) MakeFile(pathname string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mounter *NsenterMounter) ExistsPath(pathname string) bool {
|
||||
args := []string{pathname}
|
||||
_, err := mounter.ne.Exec("ls", args).CombinedOutput()
|
||||
if err == nil {
|
||||
return true
|
||||
func (mounter *NsenterMounter) ExistsPath(pathname string) (bool, error) {
|
||||
// Resolve the symlinks but allow the target not to exist. EvalSymlinks
|
||||
// would return an generic error when the target does not exist.
|
||||
hostPath, err := mounter.ne.EvalSymlinks(pathname, false /* mustExist */)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return false
|
||||
kubeletpath := mounter.ne.KubeletPath(hostPath)
|
||||
return utilfile.FileExists(kubeletpath)
|
||||
}
|
||||
|
||||
func (mounter *NsenterMounter) CleanSubPaths(podDir string, volumeName string) error {
|
||||
return doCleanSubPaths(mounter, podDir, volumeName)
|
||||
}
|
||||
|
||||
func (mounter *NsenterMounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
|
||||
// Bind-mount the subpath to avoid using symlinks in subpaths.
|
||||
newHostPath, err = doNsEnterBindSubPath(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
|
||||
}
|
||||
|
||||
func (mounter *NsenterMounter) SafeMakeDir(subdir string, base string, perm os.FileMode) error {
|
||||
fullSubdirPath := filepath.Join(base, subdir)
|
||||
evaluatedSubdirPath, err := mounter.ne.EvalSymlinks(fullSubdirPath, false /* mustExist */)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error resolving symlinks in %s: %s", fullSubdirPath, err)
|
||||
}
|
||||
evaluatedSubdirPath = filepath.Clean(evaluatedSubdirPath)
|
||||
|
||||
evaluatedBase, err := mounter.ne.EvalSymlinks(base, true /* mustExist */)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error resolving symlinks in %s: %s", base, err)
|
||||
}
|
||||
evaluatedBase = filepath.Clean(evaluatedBase)
|
||||
|
||||
rootDir := filepath.Clean(mounter.rootDir)
|
||||
if pathWithinBase(evaluatedBase, rootDir) {
|
||||
// Base is in /var/lib/kubelet. This directory is shared between the
|
||||
// container with kubelet and the host. We don't need to add '/rootfs'.
|
||||
// This is useful when /rootfs is mounted as read-only - we can still
|
||||
// create subpaths for paths in /var/lib/kubelet.
|
||||
return doSafeMakeDir(evaluatedSubdirPath, evaluatedBase, perm)
|
||||
}
|
||||
|
||||
// Base is somewhere on the host's filesystem. Add /rootfs and try to make
|
||||
// the directory there.
|
||||
// This requires /rootfs to be writable.
|
||||
kubeletSubdirPath := mounter.ne.KubeletPath(evaluatedSubdirPath)
|
||||
kubeletBase := mounter.ne.KubeletPath(evaluatedBase)
|
||||
return doSafeMakeDir(kubeletSubdirPath, kubeletBase, perm)
|
||||
}
|
||||
|
||||
func (mounter *NsenterMounter) GetMountRefs(pathname string) ([]string, error) {
|
||||
hostpath, err := mounter.ne.EvalSymlinks(pathname, true /* mustExist */)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return searchMountPoints(hostpath, hostProcMountinfoPath)
|
||||
}
|
||||
|
||||
func doNsEnterBindSubPath(mounter *NsenterMounter, subpath Subpath) (hostPath string, err error) {
|
||||
// Linux, kubelet runs in a container:
|
||||
// - safely open the subpath
|
||||
// - bind-mount the subpath to target (this can be unsafe)
|
||||
// - check that we mounted the right thing by comparing device ID and inode
|
||||
// of the subpath (via safely opened fd) and the target (that's under our
|
||||
// control)
|
||||
|
||||
// Evaluate all symlinks here once for all subsequent functions.
|
||||
evaluatedHostVolumePath, err := mounter.ne.EvalSymlinks(subpath.VolumePath, true /*mustExist*/)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error resolving symlinks in %q: %v", subpath.VolumePath, err)
|
||||
}
|
||||
evaluatedHostSubpath, err := mounter.ne.EvalSymlinks(subpath.Path, true /*mustExist*/)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error resolving symlinks in %q: %v", subpath.Path, err)
|
||||
}
|
||||
glog.V(5).Infof("doBindSubPath %q (%q) for volumepath %q", subpath.Path, evaluatedHostSubpath, subpath.VolumePath)
|
||||
subpath.VolumePath = mounter.ne.KubeletPath(evaluatedHostVolumePath)
|
||||
subpath.Path = mounter.ne.KubeletPath(evaluatedHostSubpath)
|
||||
|
||||
// Check the subpath is correct and open it
|
||||
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 {
|
||||
glog.V(4).Infof("doNsEnterBindSubPath() failed for %q, cleaning up subpath", bindPathTarget)
|
||||
if cleanErr := cleanSubPath(mounter, subpath); cleanErr != nil {
|
||||
glog.Errorf("Failed to clean subpath %q: %v", bindPathTarget, cleanErr)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Leap of faith: optimistically expect that nobody has modified previously
|
||||
// expanded evalSubPath with evil symlinks and bind-mount it.
|
||||
// Mount is done on the host! don't use kubelet path!
|
||||
glog.V(5).Infof("bind mounting %q at %q", evaluatedHostSubpath, bindPathTarget)
|
||||
if err = mounter.Mount(evaluatedHostSubpath, bindPathTarget, "" /*fstype*/, []string{"bind"}); err != nil {
|
||||
return "", fmt.Errorf("error mounting %s: %s", evaluatedHostSubpath, err)
|
||||
}
|
||||
|
||||
// Check that the bind-mount target is the same inode and device as the
|
||||
// source that we keept open, i.e. we mounted the right thing.
|
||||
err = checkDeviceInode(fd, bindPathTarget)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error checking bind mount for subpath %s: %s", subpath.VolumePath, err)
|
||||
}
|
||||
|
||||
success = true
|
||||
glog.V(3).Infof("Bound SubPath %s into %s", subpath.Path, bindPathTarget)
|
||||
return bindPathTarget, nil
|
||||
}
|
||||
|
||||
// checkDeviceInode checks that opened file and path represent the same file.
|
||||
func checkDeviceInode(fd int, path string) error {
|
||||
var srcStat, dstStat unix.Stat_t
|
||||
err := unix.Fstat(fd, &srcStat)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running fstat on subpath FD: %v", err)
|
||||
}
|
||||
|
||||
err = unix.Stat(path, &dstStat)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running fstat on %s: %v", path, err)
|
||||
}
|
||||
|
||||
if srcStat.Dev != dstStat.Dev {
|
||||
return fmt.Errorf("different device number")
|
||||
}
|
||||
if srcStat.Ino != dstStat.Ino {
|
||||
return fmt.Errorf("different inode")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mounter *NsenterMounter) GetFSGroup(pathname string) (int64, error) {
|
||||
hostPath, err := mounter.ne.EvalSymlinks(pathname, true /* mustExist */)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
kubeletpath := mounter.ne.KubeletPath(hostPath)
|
||||
return getFSGroup(kubeletpath)
|
||||
}
|
||||
|
||||
func (mounter *NsenterMounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||
return getSELinuxSupport(pathname, hostProcMountsPath)
|
||||
}
|
||||
|
||||
func (mounter *NsenterMounter) GetMode(pathname string) (os.FileMode, error) {
|
||||
hostPath, err := mounter.ne.EvalSymlinks(pathname, true /* mustExist */)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
kubeletpath := mounter.ne.KubeletPath(hostPath)
|
||||
return getMode(kubeletpath)
|
||||
}
|
||||
|
644
vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_test.go
generated
vendored
644
vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_test.go
generated
vendored
@ -18,7 +18,16 @@ limitations under the License.
|
||||
|
||||
package mount
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"k8s.io/kubernetes/pkg/util/nsenter"
|
||||
)
|
||||
|
||||
func TestParseFindMnt(t *testing.T) {
|
||||
tests := []struct {
|
||||
@ -65,3 +74,636 @@ func TestParseFindMnt(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckDeviceInode(t *testing.T) {
|
||||
testDir, err := ioutil.TempDir("", "nsenter-mounter-device-")
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot create temporary directory: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
srcPath string
|
||||
dstPath string
|
||||
expectError string
|
||||
}{
|
||||
{
|
||||
name: "the same file",
|
||||
srcPath: filepath.Join(testDir, "1"),
|
||||
dstPath: filepath.Join(testDir, "1"),
|
||||
expectError: "",
|
||||
},
|
||||
{
|
||||
name: "different file on the same FS",
|
||||
srcPath: filepath.Join(testDir, "2.1"),
|
||||
dstPath: filepath.Join(testDir, "2.2"),
|
||||
expectError: "different inode",
|
||||
},
|
||||
{
|
||||
name: "different file on different device",
|
||||
srcPath: filepath.Join(testDir, "3"),
|
||||
// /proc is always on a different "device" than /tmp (or $TEMP)
|
||||
dstPath: "/proc/self/status",
|
||||
expectError: "different device",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if err := ioutil.WriteFile(test.srcPath, []byte{}, 0644); err != nil {
|
||||
t.Errorf("Test %q: cannot create srcPath %s: %s", test.name, test.srcPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Don't create dst if it exists
|
||||
if _, err := os.Stat(test.dstPath); os.IsNotExist(err) {
|
||||
if err := ioutil.WriteFile(test.dstPath, []byte{}, 0644); err != nil {
|
||||
t.Errorf("Test %q: cannot create dstPath %s: %s", test.name, test.dstPath, err)
|
||||
continue
|
||||
}
|
||||
} else if err != nil {
|
||||
t.Errorf("Test %q: cannot check existence of dstPath %s: %s", test.name, test.dstPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
fd, err := unix.Open(test.srcPath, unix.O_CREAT, 0644)
|
||||
if err != nil {
|
||||
t.Errorf("Test %q: cannot open srcPath %s: %s", test.name, test.srcPath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
err = checkDeviceInode(fd, test.dstPath)
|
||||
|
||||
if test.expectError == "" && err != nil {
|
||||
t.Errorf("Test %q: expected no error, got %s", test.name, err)
|
||||
}
|
||||
if test.expectError != "" {
|
||||
if err == nil {
|
||||
t.Errorf("Test %q: expected error, got none", test.name)
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), test.expectError) {
|
||||
t.Errorf("Test %q: expected error %q, got %q", test.name, test.expectError, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newFakeNsenterMounter(tmpdir string, t *testing.T) (mounter *NsenterMounter, rootfsPath string, varlibPath string, err error) {
|
||||
rootfsPath = filepath.Join(tmpdir, "rootfs")
|
||||
if err := os.Mkdir(rootfsPath, 0755); err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
ne, err := nsenter.NewFakeNsenter(rootfsPath)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
|
||||
varlibPath = filepath.Join(tmpdir, "/var/lib/kubelet")
|
||||
if err := os.MkdirAll(varlibPath, 0755); err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
|
||||
return NewNsenterMounter(varlibPath, ne), rootfsPath, varlibPath, nil
|
||||
}
|
||||
|
||||
func TestNsenterExistsFile(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
prepare func(base, rootfs string) (string, error)
|
||||
expectedOutput bool
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "simple existing file",
|
||||
prepare: func(base, rootfs string) (string, error) {
|
||||
// On the host: /base/file
|
||||
path := filepath.Join(base, "file")
|
||||
if err := ioutil.WriteFile(path, []byte{}, 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// In kubelet: /rootfs/base/file
|
||||
if _, err := writeRootfsFile(rootfs, path, 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path, nil
|
||||
},
|
||||
expectedOutput: true,
|
||||
},
|
||||
{
|
||||
name: "simple non-existing file",
|
||||
prepare: func(base, rootfs string) (string, error) {
|
||||
path := filepath.Join(base, "file")
|
||||
return path, nil
|
||||
},
|
||||
expectedOutput: false,
|
||||
},
|
||||
{
|
||||
name: "simple non-accessible file",
|
||||
prepare: func(base, rootfs string) (string, error) {
|
||||
// On the host:
|
||||
// create /base/dir/file, then make the dir inaccessible
|
||||
dir := filepath.Join(base, "dir")
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
path := filepath.Join(dir, "file")
|
||||
if err := ioutil.WriteFile(path, []byte{}, 0); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.Chmod(dir, 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// In kubelet: do the same with /rootfs/base/dir/file
|
||||
rootfsPath, err := writeRootfsFile(rootfs, path, 0777)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
rootfsDir := filepath.Dir(rootfsPath)
|
||||
if err := os.Chmod(rootfsDir, 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return path, nil
|
||||
},
|
||||
expectedOutput: false,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "relative symlink to existing file",
|
||||
prepare: func(base, rootfs string) (string, error) {
|
||||
// On the host: /base/link -> file
|
||||
file := filepath.Join(base, "file")
|
||||
if err := ioutil.WriteFile(file, []byte{}, 0); err != nil {
|
||||
return "", err
|
||||
}
|
||||
path := filepath.Join(base, "link")
|
||||
if err := os.Symlink("file", path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// In kubelet: /rootfs/base/file
|
||||
if _, err := writeRootfsFile(rootfs, file, 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path, nil
|
||||
},
|
||||
expectedOutput: true,
|
||||
},
|
||||
{
|
||||
name: "absolute symlink to existing file",
|
||||
prepare: func(base, rootfs string) (string, error) {
|
||||
// On the host: /base/link -> /base/file
|
||||
file := filepath.Join(base, "file")
|
||||
if err := ioutil.WriteFile(file, []byte{}, 0); err != nil {
|
||||
return "", err
|
||||
}
|
||||
path := filepath.Join(base, "link")
|
||||
if err := os.Symlink(file, path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// In kubelet: /rootfs/base/file
|
||||
if _, err := writeRootfsFile(rootfs, file, 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return path, nil
|
||||
},
|
||||
expectedOutput: true,
|
||||
},
|
||||
{
|
||||
name: "relative symlink to non-existing file",
|
||||
prepare: func(base, rootfs string) (string, error) {
|
||||
path := filepath.Join(base, "link")
|
||||
if err := os.Symlink("file", path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path, nil
|
||||
},
|
||||
expectedOutput: false,
|
||||
},
|
||||
{
|
||||
name: "absolute symlink to non-existing file",
|
||||
prepare: func(base, rootfs string) (string, error) {
|
||||
file := filepath.Join(base, "file")
|
||||
path := filepath.Join(base, "link")
|
||||
if err := os.Symlink(file, path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path, nil
|
||||
},
|
||||
expectedOutput: false,
|
||||
},
|
||||
{
|
||||
name: "symlink loop",
|
||||
prepare: func(base, rootfs string) (string, error) {
|
||||
path := filepath.Join(base, "link")
|
||||
if err := os.Symlink(path, path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path, nil
|
||||
},
|
||||
expectedOutput: false,
|
||||
// TODO: realpath -m is not able to detect symlink loop. Should we care?
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
tmpdir, err := ioutil.TempDir("", "nsenter-exists-file")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
testBase := filepath.Join(tmpdir, "base")
|
||||
if err := os.Mkdir(testBase, 0755); err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
mounter, rootfs, _, err := newFakeNsenterMounter(tmpdir, t)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
path, err := test.prepare(testBase, rootfs)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
out, err := mounter.ExistsPath(path)
|
||||
if err != nil && !test.expectError {
|
||||
t.Errorf("Test %q: unexpected error: %s", test.name, err)
|
||||
}
|
||||
if err == nil && test.expectError {
|
||||
t.Errorf("Test %q: expected error, got none", test.name)
|
||||
}
|
||||
|
||||
if out != test.expectedOutput {
|
||||
t.Errorf("Test %q: expected return value %v, got %v", test.name, test.expectedOutput, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNsenterGetMode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
prepare func(base, rootfs string) (string, error)
|
||||
expectedMode os.FileMode
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "simple file",
|
||||
prepare: func(base, rootfs string) (string, error) {
|
||||
// On the host: /base/file
|
||||
path := filepath.Join(base, "file")
|
||||
if err := ioutil.WriteFile(path, []byte{}, 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Prepare a different file as /rootfs/base/file (="the host
|
||||
// visible from container") to check that NsEnterMounter calls
|
||||
// stat on this file and not on /base/file.
|
||||
// Visible from kubelet: /rootfs/base/file
|
||||
if _, err := writeRootfsFile(rootfs, path, 0777); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return path, nil
|
||||
},
|
||||
expectedMode: 0777,
|
||||
},
|
||||
{
|
||||
name: "non-existing file",
|
||||
prepare: func(base, rootfs string) (string, error) {
|
||||
path := filepath.Join(base, "file")
|
||||
return path, nil
|
||||
},
|
||||
expectedMode: 0,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "absolute symlink to existing file",
|
||||
prepare: func(base, rootfs string) (string, error) {
|
||||
// On the host: /base/link -> /base/file
|
||||
file := filepath.Join(base, "file")
|
||||
if err := ioutil.WriteFile(file, []byte{}, 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
path := filepath.Join(base, "link")
|
||||
if err := os.Symlink(file, path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Visible from kubelet:
|
||||
// /rootfs/base/file
|
||||
if _, err := writeRootfsFile(rootfs, file, 0747); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return path, nil
|
||||
},
|
||||
expectedMode: 0747,
|
||||
},
|
||||
{
|
||||
name: "relative symlink to existing file",
|
||||
prepare: func(base, rootfs string) (string, error) {
|
||||
// On the host: /base/link -> file
|
||||
file := filepath.Join(base, "file")
|
||||
if err := ioutil.WriteFile(file, []byte{}, 0741); err != nil {
|
||||
return "", err
|
||||
}
|
||||
path := filepath.Join(base, "link")
|
||||
if err := os.Symlink("file", path); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Visible from kubelet:
|
||||
// /rootfs/base/file
|
||||
if _, err := writeRootfsFile(rootfs, file, 0647); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return path, nil
|
||||
},
|
||||
expectedMode: 0647,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
tmpdir, err := ioutil.TempDir("", "nsenter-get-mode-")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
testBase := filepath.Join(tmpdir, "base")
|
||||
if err := os.Mkdir(testBase, 0755); err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
mounter, rootfs, _, err := newFakeNsenterMounter(tmpdir, t)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
path, err := test.prepare(testBase, rootfs)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
mode, err := mounter.GetMode(path)
|
||||
if err != nil && !test.expectError {
|
||||
t.Errorf("Test %q: unexpected error: %s", test.name, err)
|
||||
}
|
||||
if err == nil && test.expectError {
|
||||
t.Errorf("Test %q: expected error, got none", test.name)
|
||||
}
|
||||
|
||||
if mode != test.expectedMode {
|
||||
t.Errorf("Test %q: expected return value %v, got %v", test.name, test.expectedMode, mode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeRootfsFile(rootfs, path string, mode os.FileMode) (string, error) {
|
||||
fullPath := filepath.Join(rootfs, path)
|
||||
dir := filepath.Dir(fullPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := ioutil.WriteFile(fullPath, []byte{}, mode); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Use chmod, io.WriteFile is affected by umask
|
||||
if err := os.Chmod(fullPath, mode); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fullPath, nil
|
||||
}
|
||||
|
||||
func TestNsenterSafeMakeDir(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
prepare func(base, rootfs, varlib string) (expectedDir string, err error)
|
||||
subdir string
|
||||
expectError bool
|
||||
// If true, "base" directory for SafeMakeDir will be /var/lib/kubelet
|
||||
baseIsVarLib bool
|
||||
}{
|
||||
{
|
||||
name: "simple directory",
|
||||
// evaluated in base
|
||||
subdir: "some/subdirectory/structure",
|
||||
prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
|
||||
// expected to be created in /roots/
|
||||
expectedDir = filepath.Join(rootfs, base, "some/subdirectory/structure")
|
||||
return expectedDir, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "simple existing directory",
|
||||
// evaluated in base
|
||||
subdir: "some/subdirectory/structure",
|
||||
prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
|
||||
// On the host: directory exists
|
||||
hostPath := filepath.Join(base, "some/subdirectory/structure")
|
||||
if err := os.MkdirAll(hostPath, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// In rootfs: directory exists
|
||||
kubeletPath := filepath.Join(rootfs, hostPath)
|
||||
if err := os.MkdirAll(kubeletPath, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// expected to be created in /roots/
|
||||
expectedDir = kubeletPath
|
||||
return expectedDir, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "absolute symlink into safe place",
|
||||
// evaluated in base
|
||||
subdir: "some/subdirectory/structure",
|
||||
prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
|
||||
// On the host: /base/other/subdirectory exists, /base/some is link to /base/other
|
||||
hostPath := filepath.Join(base, "other/subdirectory")
|
||||
if err := os.MkdirAll(hostPath, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
somePath := filepath.Join(base, "some")
|
||||
otherPath := filepath.Join(base, "other")
|
||||
if err := os.Symlink(otherPath, somePath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// In rootfs: /base/other/subdirectory exists
|
||||
kubeletPath := filepath.Join(rootfs, hostPath)
|
||||
if err := os.MkdirAll(kubeletPath, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// expected 'structure' to be created
|
||||
expectedDir = filepath.Join(rootfs, hostPath, "structure")
|
||||
return expectedDir, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "relative symlink into safe place",
|
||||
// evaluated in base
|
||||
subdir: "some/subdirectory/structure",
|
||||
prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
|
||||
// On the host: /base/other/subdirectory exists, /base/some is link to other
|
||||
hostPath := filepath.Join(base, "other/subdirectory")
|
||||
if err := os.MkdirAll(hostPath, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
somePath := filepath.Join(base, "some")
|
||||
if err := os.Symlink("other", somePath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// In rootfs: /base/other/subdirectory exists
|
||||
kubeletPath := filepath.Join(rootfs, hostPath)
|
||||
if err := os.MkdirAll(kubeletPath, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// expected 'structure' to be created
|
||||
expectedDir = filepath.Join(rootfs, hostPath, "structure")
|
||||
return expectedDir, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "symlink into unsafe place",
|
||||
// evaluated in base
|
||||
subdir: "some/subdirectory/structure",
|
||||
prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
|
||||
// On the host: /base/some is link to /bin/other
|
||||
somePath := filepath.Join(base, "some")
|
||||
if err := os.Symlink("/bin", somePath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", nil
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "simple directory in /var/lib/kubelet",
|
||||
// evaluated in varlib
|
||||
subdir: "some/subdirectory/structure",
|
||||
baseIsVarLib: true,
|
||||
prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
|
||||
// expected to be created in /base/var/lib/kubelet, not in /rootfs!
|
||||
expectedDir = filepath.Join(varlib, "some/subdirectory/structure")
|
||||
return expectedDir, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "safe symlink in /var/lib/kubelet",
|
||||
// evaluated in varlib
|
||||
subdir: "some/subdirectory/structure",
|
||||
baseIsVarLib: true,
|
||||
prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
|
||||
// On the host: /varlib/kubelet/other/subdirectory exists, /varlib/some is link to other
|
||||
hostPath := filepath.Join(varlib, "other/subdirectory")
|
||||
if err := os.MkdirAll(hostPath, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
somePath := filepath.Join(varlib, "some")
|
||||
if err := os.Symlink("other", somePath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// expected to be created in /base/var/lib/kubelet, not in /rootfs!
|
||||
expectedDir = filepath.Join(varlib, "other/subdirectory/structure")
|
||||
return expectedDir, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unsafe symlink in /var/lib/kubelet",
|
||||
// evaluated in varlib
|
||||
subdir: "some/subdirectory/structure",
|
||||
baseIsVarLib: true,
|
||||
prepare: func(base, rootfs, varlib string) (expectedDir string, err error) {
|
||||
// On the host: /varlib/some is link to /bin
|
||||
somePath := filepath.Join(varlib, "some")
|
||||
if err := os.Symlink("/bin", somePath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return "", nil
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
tmpdir, err := ioutil.TempDir("", "nsenter-get-mode-")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
mounter, rootfs, varlib, err := newFakeNsenterMounter(tmpdir, t)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
// Prepare base directory for the test
|
||||
testBase := filepath.Join(tmpdir, "base")
|
||||
if err := os.Mkdir(testBase, 0755); err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
// Prepare base directory also in /rootfs
|
||||
rootfsBase := filepath.Join(rootfs, testBase)
|
||||
if err := os.MkdirAll(rootfsBase, 0755); err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
expectedDir := ""
|
||||
if test.prepare != nil {
|
||||
expectedDir, err = test.prepare(testBase, rootfs, varlib)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if test.baseIsVarLib {
|
||||
// use /var/lib/kubelet as the test base so we can test creating
|
||||
// subdirs there directly in /var/lib/kubenet and not in
|
||||
// /rootfs/var/lib/kubelet
|
||||
testBase = varlib
|
||||
}
|
||||
|
||||
err = mounter.SafeMakeDir(test.subdir, testBase, 0755)
|
||||
if err != nil && !test.expectError {
|
||||
t.Errorf("Test %q: unexpected error: %s", test.name, err)
|
||||
}
|
||||
if test.expectError {
|
||||
if err == nil {
|
||||
t.Errorf("Test %q: expected error, got none", test.name)
|
||||
} else {
|
||||
if !strings.Contains(err.Error(), "is outside of allowed base") {
|
||||
t.Errorf("Test %q: expected error to contain \"is outside of allowed base\", got this one instead: %s", test.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if expectedDir != "" {
|
||||
_, err := os.Stat(expectedDir)
|
||||
if err != nil {
|
||||
t.Errorf("Test %q: expected %q to exist, got error: %s", test.name, expectedDir, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
37
vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go
generated
vendored
37
vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go
generated
vendored
@ -20,11 +20,14 @@ package mount
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/nsenter"
|
||||
)
|
||||
|
||||
type NsenterMounter struct{}
|
||||
|
||||
func NewNsenterMounter() *NsenterMounter {
|
||||
func NewNsenterMounter(rootDir string, ne *nsenter.Nsenter) *NsenterMounter {
|
||||
return &NsenterMounter{}
|
||||
}
|
||||
|
||||
@ -82,6 +85,34 @@ func (*NsenterMounter) MakeFile(pathname string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*NsenterMounter) ExistsPath(pathname string) bool {
|
||||
return true
|
||||
func (*NsenterMounter) ExistsPath(pathname string) (bool, error) {
|
||||
return true, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (*NsenterMounter) SafeMakeDir(pathname string, base string, perm os.FileMode) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*NsenterMounter) PrepareSafeSubpath(subPath Subpath) (newHostPath string, cleanupAction func(), err error) {
|
||||
return subPath.Path, nil, nil
|
||||
}
|
||||
|
||||
func (*NsenterMounter) CleanSubPaths(podDir string, volumeName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*NsenterMounter) GetMountRefs(pathname string) ([]string, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (*NsenterMounter) GetFSGroup(pathname string) (int64, error) {
|
||||
return -1, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (*NsenterMounter) GetSELinuxSupport(pathname string) (bool, error) {
|
||||
return false, errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (*NsenterMounter) GetMode(pathname string) (os.FileMode, error) {
|
||||
return 0, errors.New("not implemented")
|
||||
}
|
||||
|
8
vendor/k8s.io/kubernetes/pkg/util/mount/safe_format_and_mount_test.go
generated
vendored
8
vendor/k8s.io/kubernetes/pkg/util/mount/safe_format_and_mount_test.go
generated
vendored
@ -116,7 +116,7 @@ func TestSafeFormatAndMount(t *testing.T) {
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
|
||||
{"mkfs.ext4", []string{"-F", "/dev/foo"}, "", fmt.Errorf("formatting failed")},
|
||||
{"mkfs.ext4", []string{"-F", "-m0", "/dev/foo"}, "", fmt.Errorf("formatting failed")},
|
||||
},
|
||||
expectedError: fmt.Errorf("formatting failed"),
|
||||
},
|
||||
@ -127,7 +127,7 @@ func TestSafeFormatAndMount(t *testing.T) {
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
|
||||
{"mkfs.ext4", []string{"-F", "/dev/foo"}, "", nil},
|
||||
{"mkfs.ext4", []string{"-F", "-m0", "/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: fmt.Errorf("Still cannot mount"),
|
||||
},
|
||||
@ -138,7 +138,7 @@ func TestSafeFormatAndMount(t *testing.T) {
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
|
||||
{"mkfs.ext4", []string{"-F", "/dev/foo"}, "", nil},
|
||||
{"mkfs.ext4", []string{"-F", "-m0", "/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
@ -149,7 +149,7 @@ func TestSafeFormatAndMount(t *testing.T) {
|
||||
execScripts: []ExecArgs{
|
||||
{"fsck", []string{"-a", "/dev/foo"}, "", nil},
|
||||
{"blkid", []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", "/dev/foo"}, "", &fakeexec.FakeExitError{Status: 2}},
|
||||
{"mkfs.ext3", []string{"-F", "/dev/foo"}, "", nil},
|
||||
{"mkfs.ext3", []string{"-F", "-m0", "/dev/foo"}, "", nil},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
|
Reference in New Issue
Block a user