summaryrefslogtreecommitdiff
path: root/vendor/k8s.io/kubernetes/pkg/util/mount
diff options
context:
space:
mode:
authorMatthew Heon <matthew.heon@gmail.com>2017-11-01 11:24:59 -0400
committerMatthew Heon <matthew.heon@gmail.com>2017-11-01 11:24:59 -0400
commita031b83a09a8628435317a03f199cdc18b78262f (patch)
treebc017a96769ce6de33745b8b0b1304ccf38e9df0 /vendor/k8s.io/kubernetes/pkg/util/mount
parent2b74391cd5281f6fdf391ff8ad50fd1490f6bf89 (diff)
downloadpodman-a031b83a09a8628435317a03f199cdc18b78262f.tar.gz
podman-a031b83a09a8628435317a03f199cdc18b78262f.tar.bz2
podman-a031b83a09a8628435317a03f199cdc18b78262f.zip
Initial checkin from CRI-O repo
Signed-off-by: Matthew Heon <matthew.heon@gmail.com>
Diffstat (limited to 'vendor/k8s.io/kubernetes/pkg/util/mount')
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/doc.go18
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/fake.go173
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/mount.go245
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go432
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go67
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go241
-rw-r--r--vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go63
7 files changed, 1239 insertions, 0 deletions
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/doc.go b/vendor/k8s.io/kubernetes/pkg/util/mount/doc.go
new file mode 100644
index 000000000..15179e53f
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/doc.go
@@ -0,0 +1,18 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package mount defines an interface to mounting filesystems.
+package mount // import "k8s.io/kubernetes/pkg/util/mount"
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/fake.go b/vendor/k8s.io/kubernetes/pkg/util/mount/fake.go
new file mode 100644
index 000000000..2b71fa0a7
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/fake.go
@@ -0,0 +1,173 @@
+/*
+Copyright 2015 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package mount
+
+import (
+ "path/filepath"
+ "sync"
+
+ "github.com/golang/glog"
+)
+
+// FakeMounter implements mount.Interface for tests.
+type FakeMounter struct {
+ MountPoints []MountPoint
+ Log []FakeAction
+ // Some tests run things in parallel, make sure the mounter does not produce
+ // any golang's DATA RACE warnings.
+ mutex sync.Mutex
+}
+
+var _ Interface = &FakeMounter{}
+
+// Values for FakeAction.Action
+const FakeActionMount = "mount"
+const FakeActionUnmount = "unmount"
+
+// FakeAction objects are logged every time a fake mount or unmount is called.
+type FakeAction struct {
+ Action string // "mount" or "unmount"
+ Target string // applies to both mount and unmount actions
+ Source string // applies only to "mount" actions
+ FSType string // applies only to "mount" actions
+}
+
+func (f *FakeMounter) ResetLog() {
+ f.mutex.Lock()
+ defer f.mutex.Unlock()
+
+ f.Log = []FakeAction{}
+}
+
+func (f *FakeMounter) Mount(source string, target string, fstype string, options []string) error {
+ f.mutex.Lock()
+ defer f.mutex.Unlock()
+
+ // find 'bind' option
+ for _, option := range options {
+ 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.
+ // E.g. when mounted /dev/sda like this:
+ // $ mount /dev/sda /mnt/test
+ // $ mount -o bind /mnt/test /mnt/bound
+ // then /proc/mount contains:
+ // /dev/sda /mnt/test
+ // /dev/sda /mnt/bound
+ // (and not /mnt/test /mnt/bound)
+ // I.e. we must use /dev/sda as source instead of /mnt/test in the
+ // bind mount.
+ for _, mnt := range f.MountPoints {
+ if source == mnt.Path {
+ source = mnt.Device
+ break
+ }
+ }
+ break
+ }
+ }
+
+ // If target is a symlink, get its absolute path
+ absTarget, err := filepath.EvalSymlinks(target)
+ if err != nil {
+ absTarget = target
+ }
+
+ f.MountPoints = append(f.MountPoints, MountPoint{Device: source, Path: absTarget, Type: fstype})
+ 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
+}
+
+func (f *FakeMounter) Unmount(target string) error {
+ f.mutex.Lock()
+ defer f.mutex.Unlock()
+
+ // If target is a symlink, get its absolute path
+ absTarget, err := filepath.EvalSymlinks(target)
+ if err != nil {
+ absTarget = target
+ }
+
+ newMountpoints := []MountPoint{}
+ for _, mp := range f.MountPoints {
+ if mp.Path == absTarget {
+ glog.V(5).Infof("Fake mounter: unmounted %s from %s", mp.Device, absTarget)
+ // Don't copy it to newMountpoints
+ continue
+ }
+ newMountpoints = append(newMountpoints, MountPoint{Device: mp.Device, Path: mp.Path, Type: mp.Type})
+ }
+ f.MountPoints = newMountpoints
+ f.Log = append(f.Log, FakeAction{Action: FakeActionUnmount, Target: absTarget})
+ return nil
+}
+
+func (f *FakeMounter) List() ([]MountPoint, error) {
+ f.mutex.Lock()
+ defer f.mutex.Unlock()
+
+ return f.MountPoints, nil
+}
+
+func (f *FakeMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
+ return (mp.Path == dir)
+}
+
+func (f *FakeMounter) IsNotMountPoint(dir string) (bool, error) {
+ return IsNotMountPoint(f, dir)
+}
+
+func (f *FakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
+ f.mutex.Lock()
+ defer f.mutex.Unlock()
+
+ // If file is a symlink, get its absolute path
+ absFile, err := filepath.EvalSymlinks(file)
+ if err != nil {
+ absFile = file
+ }
+
+ for _, mp := range f.MountPoints {
+ if mp.Path == absFile {
+ glog.V(5).Infof("isLikelyNotMountPoint for %s: mounted %s, false", file, mp.Path)
+ return false, nil
+ }
+ }
+ glog.V(5).Infof("isLikelyNotMountPoint for %s: true", file)
+ return true, nil
+}
+
+func (f *FakeMounter) DeviceOpened(pathname string) (bool, error) {
+ f.mutex.Lock()
+ defer f.mutex.Unlock()
+
+ for _, mp := range f.MountPoints {
+ if mp.Device == pathname {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+func (f *FakeMounter) PathIsDevice(pathname string) (bool, error) {
+ return true, nil
+}
+
+func (f *FakeMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
+ return getDeviceNameFromMount(f, mountPath, pluginDir)
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/mount.go b/vendor/k8s.io/kubernetes/pkg/util/mount/mount.go
new file mode 100644
index 000000000..0c458d64b
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/mount.go
@@ -0,0 +1,245 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// TODO(thockin): This whole pkg is pretty linux-centric. As soon as we have
+// an alternate platform, we will need to abstract further.
+package mount
+
+import (
+ "fmt"
+ "path"
+ "path/filepath"
+ "strings"
+
+ "github.com/golang/glog"
+ "k8s.io/kubernetes/pkg/util/exec"
+)
+
+const (
+ // Default mount command if mounter path is not specified
+ defaultMountCommand = "mount"
+ MountsInGlobalPDPath = "mounts"
+)
+
+type Interface interface {
+ // Mount mounts source to target as fstype with given options.
+ Mount(source string, target string, fstype string, options []string) error
+ // Unmount unmounts given target.
+ Unmount(target string) error
+ // List returns a list of all mounted filesystems. This can be large.
+ // On some platforms, reading mounts is not guaranteed consistent (i.e.
+ // it could change between chunked reads). This is guaranteed to be
+ // consistent.
+ List() ([]MountPoint, error)
+ // IsMountPointMatch determines if the mountpoint matches the dir
+ IsMountPointMatch(mp MountPoint, dir string) bool
+ // IsNotMountPoint determines if a directory is a mountpoint.
+ // It should return ErrNotExist when the directory does not exist.
+ // IsNotMountPoint is more expensive than IsLikelyNotMountPoint.
+ // IsNotMountPoint detects bind mounts in linux.
+ // IsNotMountPoint enumerates all the mountpoints using List() and
+ // the list of mountpoints may be large, then it uses
+ // IsMountPointMatch to evaluate whether the directory is a mountpoint
+ IsNotMountPoint(file string) (bool, error)
+ // IsLikelyNotMountPoint uses heuristics to determine if a directory
+ // is a mountpoint.
+ // It should return ErrNotExist when the directory does not exist.
+ // IsLikelyNotMountPoint does NOT properly detect all mountpoint types
+ // most notably linux bind mounts.
+ IsLikelyNotMountPoint(file string) (bool, error)
+ // DeviceOpened determines if the device is in use elsewhere
+ // on the system, i.e. still mounted.
+ DeviceOpened(pathname string) (bool, error)
+ // PathIsDevice determines if a path is a device.
+ PathIsDevice(pathname string) (bool, error)
+ // GetDeviceNameFromMount finds the device name by checking the mount path
+ // to get the global mount path which matches its plugin directory
+ GetDeviceNameFromMount(mountPath, pluginDir string) (string, error)
+}
+
+// Compile-time check to ensure all Mounter implementations satisfy
+// the mount interface
+var _ Interface = &Mounter{}
+
+// This represents a single line in /proc/mounts or /etc/fstab.
+type MountPoint struct {
+ Device string
+ Path string
+ Type string
+ Opts []string
+ Freq int
+ Pass int
+}
+
+// SafeFormatAndMount probes a device to see if it is formatted.
+// Namely it checks to see if a file system is present. If so it
+// mounts it otherwise the device is formatted first then mounted.
+type SafeFormatAndMount struct {
+ Interface
+ Runner exec.Interface
+}
+
+// FormatAndMount formats the given disk, if needed, and mounts it.
+// That is if the disk is not formatted and it is not being mounted as
+// read-only it will format it first then mount it. Otherwise, if the
+// disk is already formatted or it is being mounted as read-only, it
+// will be mounted without formatting.
+func (mounter *SafeFormatAndMount) FormatAndMount(source string, target string, fstype string, options []string) error {
+ // Don't attempt to format if mounting as readonly. Go straight to mounting.
+ for _, option := range options {
+ if option == "ro" {
+ return mounter.Interface.Mount(source, target, fstype, options)
+ }
+ }
+ return mounter.formatAndMount(source, target, fstype, options)
+}
+
+// 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.
+func New(mounterPath string) Interface {
+ return &Mounter{
+ mounterPath: mounterPath,
+ }
+}
+
+// GetMountRefs finds all other references to the device referenced
+// by mountPath; returns a list of paths.
+func GetMountRefs(mounter Interface, mountPath string) ([]string, error) {
+ mps, err := mounter.List()
+ if err != nil {
+ return nil, err
+ }
+
+ // Find the device name.
+ deviceName := ""
+ // If mountPath is symlink, need get its target path.
+ slTarget, err := filepath.EvalSymlinks(mountPath)
+ if err != nil {
+ slTarget = mountPath
+ }
+ for i := range mps {
+ if mps[i].Path == slTarget {
+ deviceName = mps[i].Device
+ break
+ }
+ }
+
+ // Find all references to the device.
+ var refs []string
+ if deviceName == "" {
+ glog.Warningf("could not determine device for path: %q", mountPath)
+ } else {
+ for i := range mps {
+ if mps[i].Device == deviceName && mps[i].Path != slTarget {
+ refs = append(refs, mps[i].Path)
+ }
+ }
+ }
+ return refs, nil
+}
+
+// GetDeviceNameFromMount: given a mnt point, find the device from /proc/mounts
+// returns the device name, reference count, and error code
+func GetDeviceNameFromMount(mounter Interface, mountPath string) (string, int, error) {
+ mps, err := mounter.List()
+ if err != nil {
+ return "", 0, err
+ }
+
+ // Find the device name.
+ // FIXME if multiple devices mounted on the same mount path, only the first one is returned
+ device := ""
+ // If mountPath is symlink, need get its target path.
+ slTarget, err := filepath.EvalSymlinks(mountPath)
+ if err != nil {
+ slTarget = mountPath
+ }
+ for i := range mps {
+ if mps[i].Path == slTarget {
+ device = mps[i].Device
+ break
+ }
+ }
+
+ // Find all references to the device.
+ refCount := 0
+ for i := range mps {
+ if mps[i].Device == device {
+ refCount++
+ }
+ }
+ return device, refCount, nil
+}
+
+// getDeviceNameFromMount find the device name from /proc/mounts in which
+// the mount path reference should match the given plugin directory. In case no mount path reference
+// matches, returns the volume name taken from its given mountPath
+func getDeviceNameFromMount(mounter Interface, mountPath, pluginDir string) (string, error) {
+ refs, err := GetMountRefs(mounter, mountPath)
+ if err != nil {
+ glog.V(4).Infof("GetMountRefs failed for mount path %q: %v", mountPath, err)
+ return "", err
+ }
+ if len(refs) == 0 {
+ glog.V(4).Infof("Directory %s is not mounted", mountPath)
+ return "", fmt.Errorf("directory %s is not mounted", mountPath)
+ }
+ basemountPath := path.Join(pluginDir, MountsInGlobalPDPath)
+ for _, ref := range refs {
+ if strings.HasPrefix(ref, basemountPath) {
+ volumeID, err := filepath.Rel(basemountPath, ref)
+ if err != nil {
+ glog.Errorf("Failed to get volume id from mount %s - %v", mountPath, err)
+ return "", err
+ }
+ return volumeID, nil
+ }
+ }
+
+ return path.Base(mountPath), nil
+}
+
+// IsNotMountPoint determines if a directory is a mountpoint.
+// It should return ErrNotExist when the directory does not exist.
+// This method uses the List() of all mountpoints
+// It is more extensive than IsLikelyNotMountPoint
+// and it detects bind mounts in linux
+func IsNotMountPoint(mounter Interface, file string) (bool, error) {
+ // IsLikelyNotMountPoint provides a quick check
+ // to determine whether file IS A mountpoint
+ notMnt, notMntErr := mounter.IsLikelyNotMountPoint(file)
+ if notMntErr != nil {
+ return notMnt, notMntErr
+ }
+ // identified as mountpoint, so return this fact
+ if notMnt == false {
+ return notMnt, nil
+ }
+ // check all mountpoints since IsLikelyNotMountPoint
+ // is not reliable for some mountpoint types
+ mountPoints, mountPointsErr := mounter.List()
+ if mountPointsErr != nil {
+ return notMnt, mountPointsErr
+ }
+ for _, mp := range mountPoints {
+ if mounter.IsMountPointMatch(mp, file) {
+ notMnt = false
+ break
+ }
+ }
+ return notMnt, nil
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go b/vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
new file mode 100644
index 000000000..4c141ad5b
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/mount_linux.go
@@ -0,0 +1,432 @@
+// +build linux
+
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package mount
+
+import (
+ "bufio"
+ "fmt"
+ "hash/fnv"
+ "io"
+ "os"
+ "os/exec"
+ "strconv"
+ "strings"
+ "syscall"
+
+ "github.com/golang/glog"
+ "k8s.io/apimachinery/pkg/util/sets"
+ utilexec "k8s.io/kubernetes/pkg/util/exec"
+)
+
+const (
+ // How many times to retry for a consistent read of /proc/mounts.
+ maxListTries = 3
+ // Number of fields per line in /proc/mounts as per the fstab man page.
+ expectedNumFieldsPerLine = 6
+ // Location of the mount file to use
+ procMountsPath = "/proc/mounts"
+)
+
+const (
+ // 'fsck' found errors and corrected them
+ fsckErrorsCorrected = 1
+ // 'fsck' found errors but exited without correcting them
+ fsckErrorsUncorrected = 4
+)
+
+// Mounter provides the default implementation of mount.Interface
+// for the linux platform. This implementation assumes that the
+// kubelet is running in the host's root mount namespace.
+type Mounter struct {
+ mounterPath string
+}
+
+// Mount mounts source to target as fstype with given options. 'source' and 'fstype' must
+// be an emtpy string in case it's not required, e.g. for remount, or for auto filesystem
+// type, where kernel handles fs type for you. The mount 'options' is a list of options,
+// currently come from mount(8), e.g. "ro", "remount", "bind", etc. If no more option is
+// required, call Mount with an empty string list or nil.
+func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
+ // Path to mounter binary if containerized mounter is needed. Otherwise, it is set to empty.
+ // All Linux distros are expected to be shipped with a mount utility that an support bind mounts.
+ mounterPath := ""
+ bind, bindRemountOpts := isBind(options)
+ if bind {
+ err := doMount(mounterPath, defaultMountCommand, source, target, fstype, []string{"bind"})
+ if err != nil {
+ return err
+ }
+ return doMount(mounterPath, defaultMountCommand, source, target, fstype, bindRemountOpts)
+ }
+ // The list of filesystems that require containerized mounter on GCI image cluster
+ fsTypesNeedMounter := sets.NewString("nfs", "glusterfs", "ceph", "cifs")
+ if fsTypesNeedMounter.Has(fstype) {
+ mounterPath = mounter.mounterPath
+ }
+ return doMount(mounterPath, defaultMountCommand, source, target, fstype, options)
+}
+
+// isBind detects whether a bind mount is being requested and makes the remount options to
+// use in case of bind mount, due to the fact that bind mount doesn't respect mount options.
+// The list equals:
+// options - 'bind' + 'remount' (no duplicate)
+func isBind(options []string) (bool, []string) {
+ bindRemountOpts := []string{"remount"}
+ bind := false
+
+ if len(options) != 0 {
+ for _, option := range options {
+ switch option {
+ case "bind":
+ bind = true
+ break
+ case "remount":
+ break
+ default:
+ bindRemountOpts = append(bindRemountOpts, option)
+ }
+ }
+ }
+
+ return bind, bindRemountOpts
+}
+
+// doMount runs the mount command. mounterPath is the path to mounter binary if containerized mounter is used.
+func doMount(mounterPath string, mountCmd string, source string, target string, fstype string, options []string) error {
+ mountArgs := makeMountArgs(source, target, fstype, options)
+ if len(mounterPath) > 0 {
+ mountArgs = append([]string{mountCmd}, mountArgs...)
+ mountCmd = mounterPath
+ }
+
+ glog.V(4).Infof("Mounting cmd (%s) with arguments (%s)", mountCmd, mountArgs)
+ command := exec.Command(mountCmd, mountArgs...)
+ output, err := command.CombinedOutput()
+ if err != nil {
+ glog.Errorf("Mount failed: %v\nMounting command: %s\nMounting arguments: %s %s %s %v\nOutput: %s\n", err, mountCmd, source, target, fstype, options, string(output))
+ return fmt.Errorf("mount failed: %v\nMounting command: %s\nMounting arguments: %s %s %s %v\nOutput: %s\n",
+ err, mountCmd, source, target, fstype, options, string(output))
+ }
+ return err
+}
+
+// makeMountArgs makes the arguments to the mount(8) command.
+func makeMountArgs(source, target, fstype string, options []string) []string {
+ // Build mount command as follows:
+ // mount [-t $fstype] [-o $options] [$source] $target
+ mountArgs := []string{}
+ if len(fstype) > 0 {
+ mountArgs = append(mountArgs, "-t", fstype)
+ }
+ if len(options) > 0 {
+ mountArgs = append(mountArgs, "-o", strings.Join(options, ","))
+ }
+ if len(source) > 0 {
+ mountArgs = append(mountArgs, source)
+ }
+ mountArgs = append(mountArgs, target)
+
+ return mountArgs
+}
+
+// Unmount unmounts the target.
+func (mounter *Mounter) Unmount(target string) error {
+ glog.V(4).Infof("Unmounting %s", target)
+ command := exec.Command("umount", target)
+ output, err := command.CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("Unmount failed: %v\nUnmounting arguments: %s\nOutput: %s\n", err, target, string(output))
+ }
+ return nil
+}
+
+// List returns a list of all mounted filesystems.
+func (*Mounter) List() ([]MountPoint, error) {
+ return listProcMounts(procMountsPath)
+}
+
+func (mounter *Mounter) IsMountPointMatch(mp MountPoint, dir string) bool {
+ deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
+ return ((mp.Path == dir) || (mp.Path == deletedDir))
+}
+
+func (mounter *Mounter) IsNotMountPoint(dir string) (bool, error) {
+ return IsNotMountPoint(mounter, dir)
+}
+
+// IsLikelyNotMountPoint determines if a directory is not a mountpoint.
+// It is fast but not necessarily ALWAYS correct. If the path is in fact
+// a bind mount from one part of a mount to another it will not be detected.
+// mkdir /tmp/a /tmp/b; mount --bin /tmp/a /tmp/b; IsLikelyNotMountPoint("/tmp/b")
+// will return true. When in fact /tmp/b is a mount point. If this situation
+// if of interest to you, don't use this function...
+func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
+ stat, err := os.Stat(file)
+ if err != nil {
+ return true, err
+ }
+ rootStat, err := os.Lstat(file + "/..")
+ if err != nil {
+ return true, err
+ }
+ // If the directory has a different device as parent, then it is a mountpoint.
+ if stat.Sys().(*syscall.Stat_t).Dev != rootStat.Sys().(*syscall.Stat_t).Dev {
+ return false, nil
+ }
+
+ return true, nil
+}
+
+// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
+// If pathname is not a device, log and return false with nil error.
+// If open returns errno EBUSY, return true with nil error.
+// If open returns nil, return false with nil error.
+// Otherwise, return false with error
+func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
+ return exclusiveOpenFailsOnDevice(pathname)
+}
+
+// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
+// to a device.
+func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
+ return pathIsDevice(pathname)
+}
+
+func exclusiveOpenFailsOnDevice(pathname string) (bool, error) {
+ isDevice, err := pathIsDevice(pathname)
+ if err != nil {
+ return false, fmt.Errorf(
+ "PathIsDevice failed for path %q: %v",
+ pathname,
+ err)
+ }
+ if !isDevice {
+ glog.Errorf("Path %q is not refering to a device.", pathname)
+ return false, nil
+ }
+ fd, errno := syscall.Open(pathname, syscall.O_RDONLY|syscall.O_EXCL, 0)
+ // If the device is in use, open will return an invalid fd.
+ // When this happens, it is expected that Close will fail and throw an error.
+ defer syscall.Close(fd)
+ if errno == nil {
+ // device not in use
+ return false, nil
+ } else if errno == syscall.EBUSY {
+ // device is in use
+ return true, nil
+ }
+ // error during call to Open
+ return false, errno
+}
+
+func pathIsDevice(pathname string) (bool, error) {
+ finfo, err := os.Stat(pathname)
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+ // err in call to os.Stat
+ if err != nil {
+ return false, err
+ }
+ // path refers to a device
+ if finfo.Mode()&os.ModeDevice != 0 {
+ return true, nil
+ }
+ // path does not refer to device
+ return false, nil
+}
+
+//GetDeviceNameFromMount: given a mount point, find the device name from its global mount point
+func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
+ return getDeviceNameFromMount(mounter, mountPath, pluginDir)
+}
+
+func listProcMounts(mountFilePath string) ([]MountPoint, error) {
+ hash1, err := readProcMounts(mountFilePath, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ for i := 0; i < maxListTries; i++ {
+ mps := []MountPoint{}
+ hash2, err := readProcMounts(mountFilePath, &mps)
+ if err != nil {
+ return nil, err
+ }
+ if hash1 == hash2 {
+ // Success
+ return mps, nil
+ }
+ hash1 = hash2
+ }
+ return nil, fmt.Errorf("failed to get a consistent snapshot of %v after %d tries", mountFilePath, maxListTries)
+}
+
+// readProcMounts reads the given mountFilePath (normally /proc/mounts) and produces a hash
+// of the contents. If the out argument is not nil, this fills it with MountPoint structs.
+func readProcMounts(mountFilePath string, out *[]MountPoint) (uint32, error) {
+ file, err := os.Open(mountFilePath)
+ if err != nil {
+ return 0, err
+ }
+ defer file.Close()
+ return readProcMountsFrom(file, out)
+}
+
+func readProcMountsFrom(file io.Reader, out *[]MountPoint) (uint32, error) {
+ hash := fnv.New32a()
+ scanner := bufio.NewReader(file)
+ for {
+ line, err := scanner.ReadString('\n')
+ if err == io.EOF {
+ break
+ }
+ fields := strings.Fields(line)
+ if len(fields) != expectedNumFieldsPerLine {
+ return 0, fmt.Errorf("wrong number of fields (expected %d, got %d): %s", expectedNumFieldsPerLine, len(fields), line)
+ }
+
+ fmt.Fprintf(hash, "%s", line)
+
+ if out != nil {
+ mp := MountPoint{
+ Device: fields[0],
+ Path: fields[1],
+ Type: fields[2],
+ Opts: strings.Split(fields[3], ","),
+ }
+
+ freq, err := strconv.Atoi(fields[4])
+ if err != nil {
+ return 0, err
+ }
+ mp.Freq = freq
+
+ pass, err := strconv.Atoi(fields[5])
+ if err != nil {
+ return 0, err
+ }
+ mp.Pass = pass
+
+ *out = append(*out, mp)
+ }
+ }
+ return hash.Sum32(), nil
+}
+
+// formatAndMount uses unix utils to format and mount the given disk
+func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
+ options = append(options, "defaults")
+
+ // Run fsck on the disk to fix repairable issues
+ glog.V(4).Infof("Checking for issues with fsck on disk: %s", source)
+ args := []string{"-a", source}
+ cmd := mounter.Runner.Command("fsck", args...)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ ee, isExitError := err.(utilexec.ExitError)
+ switch {
+ case err == utilexec.ErrExecutableNotFound:
+ glog.Warningf("'fsck' not found on system; continuing mount without running 'fsck'.")
+ case isExitError && ee.ExitStatus() == fsckErrorsCorrected:
+ glog.Infof("Device %s has errors which were corrected by fsck.", source)
+ case isExitError && ee.ExitStatus() == fsckErrorsUncorrected:
+ return fmt.Errorf("'fsck' found errors on device %s but could not correct them: %s.", source, string(out))
+ case isExitError && ee.ExitStatus() > fsckErrorsUncorrected:
+ glog.Infof("`fsck` error %s", string(out))
+ }
+ }
+
+ // Try to mount the disk
+ glog.V(4).Infof("Attempting to mount disk: %s %s %s", fstype, source, target)
+ mountErr := mounter.Interface.Mount(source, target, fstype, options)
+ if mountErr != nil {
+ // Mount failed. This indicates either that the disk is unformatted or
+ // it contains an unexpected filesystem.
+ existingFormat, err := mounter.getDiskFormat(source)
+ if err != nil {
+ return err
+ }
+ if existingFormat == "" {
+ // Disk is unformatted so format it.
+ args = []string{source}
+ // Use 'ext4' as the default
+ if len(fstype) == 0 {
+ fstype = "ext4"
+ }
+
+ if fstype == "ext4" || fstype == "ext3" {
+ args = []string{"-F", source}
+ }
+ glog.Infof("Disk %q appears to be unformatted, attempting to format as type: %q with options: %v", source, fstype, args)
+ cmd := mounter.Runner.Command("mkfs."+fstype, args...)
+ _, err := cmd.CombinedOutput()
+ if err == nil {
+ // the disk has been formatted successfully try to mount it again.
+ glog.Infof("Disk successfully formatted (mkfs): %s - %s %s", fstype, source, target)
+ return mounter.Interface.Mount(source, target, fstype, options)
+ }
+ glog.Errorf("format of disk %q failed: type:(%q) target:(%q) options:(%q)error:(%v)", source, fstype, target, options, err)
+ return err
+ } else {
+ // Disk is already formatted and failed to mount
+ if len(fstype) == 0 || fstype == existingFormat {
+ // This is mount error
+ return mountErr
+ } else {
+ // Block device is formatted with unexpected filesystem, let the user know
+ return fmt.Errorf("failed to mount the volume as %q, it already contains %s. Mount error: %v", fstype, existingFormat, mountErr)
+ }
+ }
+ }
+ return mountErr
+}
+
+// diskLooksUnformatted uses 'lsblk' to see if the given disk is unformated
+func (mounter *SafeFormatAndMount) getDiskFormat(disk string) (string, error) {
+ args := []string{"-n", "-o", "FSTYPE", disk}
+ cmd := mounter.Runner.Command("lsblk", args...)
+ glog.V(4).Infof("Attempting to determine if disk %q is formatted using lsblk with args: (%v)", disk, args)
+ dataOut, err := cmd.CombinedOutput()
+ output := string(dataOut)
+ glog.V(4).Infof("Output: %q", output)
+
+ if err != nil {
+ glog.Errorf("Could not determine if disk %q is formatted (%v)", disk, err)
+ return "", err
+ }
+
+ // Split lsblk output into lines. Unformatted devices should contain only
+ // "\n". Beware of "\n\n", that's a device with one empty partition.
+ output = strings.TrimSuffix(output, "\n") // Avoid last empty line
+ lines := strings.Split(output, "\n")
+ if lines[0] != "" {
+ // The device is formatted
+ return lines[0], nil
+ }
+
+ if len(lines) == 1 {
+ // The device is unformatted and has no dependent devices
+ return "", nil
+ }
+
+ // The device has dependent devices, most probably partitions (LVM, LUKS
+ // and MD RAID are reported as FSTYPE and caught above).
+ return "unknown data, probably partitions", nil
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go b/vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go
new file mode 100644
index 000000000..632ad0606
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/mount_unsupported.go
@@ -0,0 +1,67 @@
+// +build !linux
+
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package mount
+
+type Mounter struct {
+ mounterPath string
+}
+
+func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
+ return nil
+}
+
+func (mounter *Mounter) Unmount(target string) error {
+ return nil
+}
+
+func (mounter *Mounter) List() ([]MountPoint, error) {
+ return []MountPoint{}, nil
+}
+
+func (mounter *Mounter) IsMountPointMatch(mp MountPoint, dir string) bool {
+ return (mp.Path == dir)
+}
+
+func (mounter *Mounter) IsNotMountPoint(dir string) (bool, error) {
+ return IsNotMountPoint(mounter, dir)
+}
+
+func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
+ return true, nil
+}
+
+func (mounter *Mounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
+ return "", nil
+}
+
+func (mounter *Mounter) DeviceOpened(pathname string) (bool, error) {
+ return false, nil
+}
+
+func (mounter *Mounter) PathIsDevice(pathname string) (bool, error) {
+ return true, nil
+}
+
+func (mounter *SafeFormatAndMount) formatAndMount(source string, target string, fstype string, options []string) error {
+ return nil
+}
+
+func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) {
+ return true, nil
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go b/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go
new file mode 100644
index 000000000..4af8ef0d8
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount.go
@@ -0,0 +1,241 @@
+// +build linux
+
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package mount
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/golang/glog"
+ "k8s.io/kubernetes/pkg/util/exec"
+)
+
+// NsenterMounter is part of experimental support for running the kubelet
+// in a container. Currently, all docker containers receive their own mount
+// namespaces. NsenterMounter works by executing nsenter to run commands in
+// the host's mount namespace.
+//
+// NsenterMounter requires:
+//
+// 1. Docker >= 1.6 due to the dependency on the slave propagation mode
+// of the bind-mount of the kubelet root directory in the container.
+// Docker 1.5 used a private propagation mode for bind-mounts, so mounts
+// performed in the host's mount namespace do not propagate out to the
+// bind-mount in this docker version.
+// 2. The host's root filesystem must be available at /rootfs
+// 3. The nsenter binary must be on the Kubelet process' PATH in the container's
+// filesystem.
+// 4. The Kubelet process must have CAP_SYS_ADMIN (required by nsenter); at
+// the present, this effectively means that the kubelet is running in a
+// privileged container.
+// 5. The volume path used by the Kubelet must be the same inside and outside
+// the container and be writable by the container (to initialize volume)
+// contents. TODO: remove this requirement.
+// 6. The host image must have mount, findmnt, and umount binaries in /bin,
+// /usr/sbin, or /usr/bin
+//
+// For more information about mount propagation modes, see:
+// https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
+type NsenterMounter struct {
+ // a map of commands to their paths on the host filesystem
+ paths map[string]string
+}
+
+func NewNsenterMounter() *NsenterMounter {
+ m := &NsenterMounter{
+ paths: map[string]string{
+ "mount": "",
+ "findmnt": "",
+ "umount": "",
+ },
+ }
+ // search for the mount command in other locations besides /usr/bin
+ for binary := range m.paths {
+ // default to root
+ m.paths[binary] = filepath.Join("/", binary)
+ for _, path := range []string{"/bin", "/usr/sbin", "/usr/bin"} {
+ binPath := filepath.Join(path, binary)
+ if _, err := os.Stat(filepath.Join(hostRootFsPath, binPath)); err != nil {
+ continue
+ }
+ m.paths[binary] = binPath
+ break
+ }
+ // TODO: error, so that the kubelet can stop if the mounts don't exist
+ }
+ return m
+}
+
+// NsenterMounter implements mount.Interface
+var _ = Interface(&NsenterMounter{})
+
+const (
+ hostRootFsPath = "/rootfs"
+ hostProcMountsPath = "/rootfs/proc/1/mounts"
+ nsenterPath = "nsenter"
+)
+
+// Mount runs mount(8) in the host's root mount namespace. Aside from this
+// aspect, Mount has the same semantics as the mounter returned by mount.New()
+func (n *NsenterMounter) Mount(source string, target string, fstype string, options []string) error {
+ bind, bindRemountOpts := isBind(options)
+
+ if bind {
+ err := n.doNsenterMount(source, target, fstype, []string{"bind"})
+ if err != nil {
+ return err
+ }
+ return n.doNsenterMount(source, target, fstype, bindRemountOpts)
+ }
+
+ return n.doNsenterMount(source, target, fstype, options)
+}
+
+// doNsenterMount nsenters the host's mount namespace and performs the
+// requested mount.
+func (n *NsenterMounter) doNsenterMount(source, target, fstype string, options []string) error {
+ glog.V(5).Infof("nsenter Mounting %s %s %s %v", source, target, fstype, options)
+ args := n.makeNsenterArgs(source, target, fstype, options)
+
+ glog.V(5).Infof("Mount command: %v %v", nsenterPath, args)
+ exec := exec.New()
+ outputBytes, err := exec.Command(nsenterPath, args...).CombinedOutput()
+ if len(outputBytes) != 0 {
+ glog.V(5).Infof("Output of mounting %s to %s: %v", source, target, string(outputBytes))
+ }
+
+ return err
+}
+
+// makeNsenterArgs makes a list of argument to nsenter in order to do the
+// requested mount.
+func (n *NsenterMounter) makeNsenterArgs(source, target, fstype string, options []string) []string {
+ nsenterArgs := []string{
+ "--mount=/rootfs/proc/1/ns/mnt",
+ "--",
+ n.absHostPath("mount"),
+ }
+
+ args := makeMountArgs(source, target, fstype, options)
+
+ return append(nsenterArgs, args...)
+}
+
+// Unmount runs umount(8) in the host's mount namespace.
+func (n *NsenterMounter) Unmount(target string) error {
+ args := []string{
+ "--mount=/rootfs/proc/1/ns/mnt",
+ "--",
+ n.absHostPath("umount"),
+ target,
+ }
+
+ glog.V(5).Infof("Unmount command: %v %v", nsenterPath, args)
+ exec := exec.New()
+ outputBytes, err := exec.Command(nsenterPath, args...).CombinedOutput()
+ if len(outputBytes) != 0 {
+ glog.V(5).Infof("Output of unmounting %s: %v", target, string(outputBytes))
+ }
+
+ return err
+}
+
+// List returns a list of all mounted filesystems in the host's mount namespace.
+func (*NsenterMounter) List() ([]MountPoint, error) {
+ return listProcMounts(hostProcMountsPath)
+}
+
+func (m *NsenterMounter) IsNotMountPoint(dir string) (bool, error) {
+ return IsNotMountPoint(m, dir)
+}
+
+func (*NsenterMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
+ deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
+ return ((mp.Path == dir) || (mp.Path == deletedDir))
+}
+
+// IsLikelyNotMountPoint determines whether a path is a mountpoint by calling findmnt
+// in the host's root mount namespace.
+func (n *NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
+ file, err := filepath.Abs(file)
+ if err != nil {
+ return true, err
+ }
+
+ // Check the directory exists
+ if _, err = os.Stat(file); os.IsNotExist(err) {
+ glog.V(5).Infof("findmnt: directory %s does not exist", file)
+ return true, err
+ }
+ // Add --first-only option: since we are testing for the absence of a mountpoint, it is sufficient to get only
+ // the first of multiple possible mountpoints using --first-only.
+ // Also add fstype output to make sure that the output of target file will give the full path
+ // TODO: Need more refactoring for this function. Track the solution with issue #26996
+ args := []string{"--mount=/rootfs/proc/1/ns/mnt", "--", n.absHostPath("findmnt"), "-o", "target,fstype", "--noheadings", "--first-only", "--target", file}
+ glog.V(5).Infof("findmnt command: %v %v", nsenterPath, args)
+
+ exec := exec.New()
+ out, err := exec.Command(nsenterPath, args...).CombinedOutput()
+ if err != nil {
+ glog.V(2).Infof("Failed findmnt command for path %s: %v", file, err)
+ // Different operating systems behave differently for paths which are not mount points.
+ // On older versions (e.g. 2.20.1) we'd get error, on newer ones (e.g. 2.26.2) we'd get "/".
+ // It's safer to assume that it's not a mount point.
+ return true, nil
+ }
+ mountTarget := strings.Split(string(out), " ")[0]
+ mountTarget = strings.TrimSuffix(mountTarget, "\n")
+ glog.V(5).Infof("IsLikelyNotMountPoint findmnt output for path %s: %v:", file, mountTarget)
+
+ if mountTarget == file {
+ glog.V(5).Infof("IsLikelyNotMountPoint: %s is a mount point", file)
+ return false, nil
+ }
+ glog.V(5).Infof("IsLikelyNotMountPoint: %s is not a mount point", file)
+ return true, nil
+}
+
+// DeviceOpened checks if block device in use by calling Open with O_EXCL flag.
+// Returns true if open returns errno EBUSY, and false if errno is nil.
+// Returns an error if errno is any error other than EBUSY.
+// Returns with error if pathname is not a device.
+func (n *NsenterMounter) DeviceOpened(pathname string) (bool, error) {
+ return exclusiveOpenFailsOnDevice(pathname)
+}
+
+// PathIsDevice uses FileInfo returned from os.Stat to check if path refers
+// to a device.
+func (n *NsenterMounter) PathIsDevice(pathname string) (bool, error) {
+ return pathIsDevice(pathname)
+}
+
+//GetDeviceNameFromMount given a mount point, find the volume id from checking /proc/mounts
+func (n *NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
+ return getDeviceNameFromMount(n, mountPath, pluginDir)
+}
+
+func (n *NsenterMounter) absHostPath(command string) string {
+ path, ok := n.paths[command]
+ if !ok {
+ return command
+ }
+ return path
+}
diff --git a/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go b/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go
new file mode 100644
index 000000000..e955e1b78
--- /dev/null
+++ b/vendor/k8s.io/kubernetes/pkg/util/mount/nsenter_mount_unsupported.go
@@ -0,0 +1,63 @@
+// +build !linux
+
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package mount
+
+type NsenterMounter struct{}
+
+func NewNsenterMounter() *NsenterMounter {
+ return &NsenterMounter{}
+}
+
+var _ = Interface(&NsenterMounter{})
+
+func (*NsenterMounter) Mount(source string, target string, fstype string, options []string) error {
+ return nil
+}
+
+func (*NsenterMounter) Unmount(target string) error {
+ return nil
+}
+
+func (*NsenterMounter) List() ([]MountPoint, error) {
+ return []MountPoint{}, nil
+}
+
+func (m *NsenterMounter) IsNotMountPoint(dir string) (bool, error) {
+ return IsNotMountPoint(m, dir)
+}
+
+func (*NsenterMounter) IsMountPointMatch(mp MountPoint, dir string) bool {
+ return (mp.Path == dir)
+}
+
+func (*NsenterMounter) IsLikelyNotMountPoint(file string) (bool, error) {
+ return true, nil
+}
+
+func (*NsenterMounter) DeviceOpened(pathname string) (bool, error) {
+ return false, nil
+}
+
+func (*NsenterMounter) PathIsDevice(pathname string) (bool, error) {
+ return true, nil
+}
+
+func (*NsenterMounter) GetDeviceNameFromMount(mountPath, pluginDir string) (string, error) {
+ return "", nil
+}