mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-22 22:30:23 +00:00
cleanup: remove k8s.io/utils from vendor
as part of running `go mod tidy` the unused k8s.io/utils is removed from the vendor directory. Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
This commit is contained in:
parent
9595c89855
commit
b1a3b02741
15
vendor/k8s.io/utils/mount/OWNERS
generated
vendored
15
vendor/k8s.io/utils/mount/OWNERS
generated
vendored
@ -1,15 +0,0 @@
|
|||||||
# See the OWNERS docs at https://go.k8s.io/owners
|
|
||||||
|
|
||||||
reviewers:
|
|
||||||
- jingxu97
|
|
||||||
- saad-ali
|
|
||||||
- jsafrane
|
|
||||||
- msau42
|
|
||||||
- andyzhangx
|
|
||||||
- gnufied
|
|
||||||
approvers:
|
|
||||||
- andyzhangx
|
|
||||||
- jingxu97
|
|
||||||
- saad-ali
|
|
||||||
- jsafrane
|
|
||||||
|
|
11
vendor/k8s.io/utils/mount/README.md
generated
vendored
11
vendor/k8s.io/utils/mount/README.md
generated
vendored
@ -1,11 +0,0 @@
|
|||||||
# WARNING ! Please read before using mount functionality
|
|
||||||
# THIS REPOSITORY is moved : Please use https://github.com/kubernetes/mount-utils for all your work
|
|
||||||
|
|
||||||
This package has been moved to new location. Please use the new repo for bug fixes and enhancements.
|
|
||||||
All existing dependencies on this repo are being removed. Eventually this repo will be deprecated.
|
|
||||||
If you are using this repo or planning to use, you must use the new repo mentioned here for this functionality.
|
|
||||||
|
|
||||||
New repo : https://github.com/kubernetes/mount-utils
|
|
||||||
New go module: k8s.io/mount-utils
|
|
||||||
For Kubernetes/Kubernetes project the code is available under staging directory.
|
|
||||||
|
|
18
vendor/k8s.io/utils/mount/doc.go
generated
vendored
18
vendor/k8s.io/utils/mount/doc.go
generated
vendored
@ -1,18 +0,0 @@
|
|||||||
/*
|
|
||||||
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/utils/mount"
|
|
216
vendor/k8s.io/utils/mount/fake_mounter.go
generated
vendored
216
vendor/k8s.io/utils/mount/fake_mounter.go
generated
vendored
@ -1,216 +0,0 @@
|
|||||||
/*
|
|
||||||
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 (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"k8s.io/klog/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FakeMounter implements mount.Interface for tests.
|
|
||||||
type FakeMounter struct {
|
|
||||||
MountPoints []MountPoint
|
|
||||||
log []FakeAction
|
|
||||||
// Error to return for a path when calling IsLikelyNotMountPoint
|
|
||||||
MountCheckErrors map[string]error
|
|
||||||
// Some tests run things in parallel, make sure the mounter does not produce
|
|
||||||
// any golang's DATA RACE warnings.
|
|
||||||
mutex sync.Mutex
|
|
||||||
UnmountFunc UnmountFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmountFunc is a function callback to be executed during the Unmount() call.
|
|
||||||
type UnmountFunc func(path string) error
|
|
||||||
|
|
||||||
var _ Interface = &FakeMounter{}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// FakeActionMount is the string for specifying mount as FakeAction.Action
|
|
||||||
FakeActionMount = "mount"
|
|
||||||
// FakeActionUnmount is the string for specifying unmount as FakeAction.Action
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFakeMounter returns a FakeMounter struct that implements Interface and is
|
|
||||||
// suitable for testing purposes.
|
|
||||||
func NewFakeMounter(mps []MountPoint) *FakeMounter {
|
|
||||||
return &FakeMounter{
|
|
||||||
MountPoints: mps,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResetLog clears all the log entries in FakeMounter
|
|
||||||
func (f *FakeMounter) ResetLog() {
|
|
||||||
f.mutex.Lock()
|
|
||||||
defer f.mutex.Unlock()
|
|
||||||
|
|
||||||
f.log = []FakeAction{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLog returns the slice of FakeActions taken by the mounter
|
|
||||||
func (f *FakeMounter) GetLog() []FakeAction {
|
|
||||||
f.mutex.Lock()
|
|
||||||
defer f.mutex.Unlock()
|
|
||||||
|
|
||||||
return f.log
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mount records the mount event and updates the in-memory mount points for FakeMounter
|
|
||||||
func (f *FakeMounter) Mount(source string, target string, fstype string, options []string) error {
|
|
||||||
return f.MountSensitive(source, target, fstype, options, nil /* sensitiveOptions */)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mount records the mount event and updates the in-memory mount points for FakeMounter
|
|
||||||
// sensitiveOptions to be passed in a separate parameter from the normal
|
|
||||||
// mount options and ensures the sensitiveOptions are never logged. This
|
|
||||||
// method should be used by callers that pass sensitive material (like
|
|
||||||
// passwords) as mount options.
|
|
||||||
func (f *FakeMounter) MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
|
||||||
f.mutex.Lock()
|
|
||||||
defer f.mutex.Unlock()
|
|
||||||
|
|
||||||
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.
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// reuse MountPoint.Opts field to mark mount as readonly
|
|
||||||
opts = append(opts, option)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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, Opts: append(opts, sensitiveOptions...)})
|
|
||||||
klog.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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmount records the unmount event and updates the in-memory mount points for FakeMounter
|
|
||||||
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 {
|
|
||||||
if f.UnmountFunc != nil {
|
|
||||||
err := f.UnmountFunc(absTarget)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
klog.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})
|
|
||||||
delete(f.MountCheckErrors, target)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns all the in-memory mountpoints for FakeMounter
|
|
||||||
func (f *FakeMounter) List() ([]MountPoint, error) {
|
|
||||||
f.mutex.Lock()
|
|
||||||
defer f.mutex.Unlock()
|
|
||||||
|
|
||||||
return f.MountPoints, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLikelyNotMountPoint determines whether a path is a mountpoint by checking
|
|
||||||
// if the absolute path to file is in the in-memory mountpoints
|
|
||||||
func (f *FakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
|
||||||
f.mutex.Lock()
|
|
||||||
defer f.mutex.Unlock()
|
|
||||||
|
|
||||||
err := f.MountCheckErrors[file]
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = os.Stat(file)
|
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
klog.V(5).Infof("isLikelyNotMountPoint for %s: mounted %s, false", file, mp.Path)
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
klog.V(5).Infof("isLikelyNotMountPoint for %s: true", file)
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMountRefs finds all mount references to the path, returns a
|
|
||||||
// list of paths.
|
|
||||||
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)
|
|
||||||
}
|
|
370
vendor/k8s.io/utils/mount/mount.go
generated
vendored
370
vendor/k8s.io/utils/mount/mount.go
generated
vendored
@ -1,370 +0,0 @@
|
|||||||
/*
|
|
||||||
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"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
utilexec "k8s.io/utils/exec"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Default mount command if mounter path is not specified.
|
|
||||||
defaultMountCommand = "mount"
|
|
||||||
// Log message where sensitive mount options were removed
|
|
||||||
sensitiveOptionsRemoved = "<masked>"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Interface defines the set of methods to allow for mount operations on a system.
|
|
||||||
type Interface interface {
|
|
||||||
// Mount mounts source to target as fstype with given options.
|
|
||||||
// options MUST not contain sensitive material (like passwords).
|
|
||||||
Mount(source string, target string, fstype string, options []string) error
|
|
||||||
// MountSensitive is the same as Mount() but this method allows
|
|
||||||
// sensitiveOptions to be passed in a separate parameter from the normal
|
|
||||||
// mount options and ensures the sensitiveOptions are never logged. This
|
|
||||||
// method should be used by callers that pass sensitive material (like
|
|
||||||
// passwords) as mount options.
|
|
||||||
MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []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 directly from the OS is not guaranteed
|
|
||||||
// consistent (i.e. it could change between chunked reads). This is guaranteed
|
|
||||||
// to be consistent.
|
|
||||||
List() ([]MountPoint, error)
|
|
||||||
// IsLikelyNotMountPoint uses heuristics to determine if a directory
|
|
||||||
// is not 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 and symbolic link. For callers that do not
|
|
||||||
// care about such situations, this is a faster alternative to calling List()
|
|
||||||
// and scanning that output.
|
|
||||||
IsLikelyNotMountPoint(file string) (bool, error)
|
|
||||||
// GetMountRefs finds all mount references to pathname, returning a slice of
|
|
||||||
// paths. Pathname can be a mountpoint path or a normal directory
|
|
||||||
// (for bind mount). On Linux, pathname is excluded from the slice.
|
|
||||||
// For example, if /dev/sdc was mounted at /path/a and /path/b,
|
|
||||||
// GetMountRefs("/path/a") would return ["/path/b"]
|
|
||||||
// GetMountRefs("/path/b") would return ["/path/a"]
|
|
||||||
// On Windows there is no way to query all mount points; as long as pathname is
|
|
||||||
// a valid mount, it will be returned.
|
|
||||||
GetMountRefs(pathname string) ([]string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile-time check to ensure all Mounter implementations satisfy
|
|
||||||
// the mount interface.
|
|
||||||
var _ Interface = &Mounter{}
|
|
||||||
|
|
||||||
// MountPoint represents a single line in /proc/mounts or /etc/fstab.
|
|
||||||
type MountPoint struct { // nolint: golint
|
|
||||||
Device string
|
|
||||||
Path string
|
|
||||||
Type string
|
|
||||||
Opts []string // Opts may contain sensitive mount options (like passwords) and MUST be treated as such (e.g. not logged).
|
|
||||||
Freq int
|
|
||||||
Pass int
|
|
||||||
}
|
|
||||||
|
|
||||||
type MountErrorType string // nolint: golint
|
|
||||||
|
|
||||||
const (
|
|
||||||
FilesystemMismatch MountErrorType = "FilesystemMismatch"
|
|
||||||
HasFilesystemErrors MountErrorType = "HasFilesystemErrors"
|
|
||||||
UnformattedReadOnly MountErrorType = "UnformattedReadOnly"
|
|
||||||
FormatFailed MountErrorType = "FormatFailed"
|
|
||||||
GetDiskFormatFailed MountErrorType = "GetDiskFormatFailed"
|
|
||||||
UnknownMountError MountErrorType = "UnknownMountError"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MountError struct { // nolint: golint
|
|
||||||
Type MountErrorType
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mountError MountError) String() string {
|
|
||||||
return mountError.Message
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mountError MountError) Error() string {
|
|
||||||
return mountError.Message
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMountError(mountErrorValue MountErrorType, format string, args ...interface{}) error {
|
|
||||||
mountError := MountError{
|
|
||||||
Type: mountErrorValue,
|
|
||||||
Message: fmt.Sprintf(format, args...),
|
|
||||||
}
|
|
||||||
return mountError
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
Exec utilexec.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.
|
|
||||||
// options MUST not contain sensitive material (like passwords).
|
|
||||||
func (mounter *SafeFormatAndMount) FormatAndMount(source string, target string, fstype string, options []string) error {
|
|
||||||
return mounter.FormatAndMountSensitive(source, target, fstype, options, nil /* sensitiveOptions */)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FormatAndMountSensitive is the same as FormatAndMount but this method allows
|
|
||||||
// sensitiveOptions to be passed in a separate parameter from the normal mount
|
|
||||||
// options and ensures the sensitiveOptions are never logged. This method should
|
|
||||||
// be used by callers that pass sensitive material (like passwords) as mount
|
|
||||||
// options.
|
|
||||||
func (mounter *SafeFormatAndMount) FormatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
|
||||||
return mounter.formatAndMountSensitive(source, target, fstype, options, sensitiveOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// getMountRefsByDev finds all references to the device provided
|
|
||||||
// by mountPath; returns a list of paths.
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finding the device mounted to mountPath.
|
|
||||||
diskDev := ""
|
|
||||||
for i := range mps {
|
|
||||||
if mountPath == mps[i].Path {
|
|
||||||
diskDev = mps[i].Device
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find all references to the device.
|
|
||||||
var refs []string
|
|
||||||
for i := range mps {
|
|
||||||
if mps[i].Device == diskDev || mps[i].Device == mountPath {
|
|
||||||
if mps[i].Path != mountPath {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
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 && os.IsPermission(notMntErr) {
|
|
||||||
// We were not allowed to do the simple stat() check, e.g. on NFS with
|
|
||||||
// root_squash. Fall back to /proc/mounts check below.
|
|
||||||
notMnt = true
|
|
||||||
notMntErr = nil
|
|
||||||
}
|
|
||||||
if notMntErr != nil {
|
|
||||||
return notMnt, notMntErr
|
|
||||||
}
|
|
||||||
// identified as mountpoint, so return this fact.
|
|
||||||
if notMnt == false {
|
|
||||||
return notMnt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve any symlinks in file, kernel would do the same and use the resolved path in /proc/mounts.
|
|
||||||
resolvedFile, err := filepath.EvalSymlinks(file)
|
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 isMountPointMatch(mp, resolvedFile) {
|
|
||||||
notMnt = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return notMnt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeBindOpts 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 MakeBindOpts(options []string) (bool, []string, []string) {
|
|
||||||
bind, bindOpts, bindRemountOpts, _ := MakeBindOptsSensitive(options, nil /* sensitiveOptions */)
|
|
||||||
return bind, bindOpts, bindRemountOpts
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeBindOptsSensitive is the same as MakeBindOpts but this method allows
|
|
||||||
// sensitiveOptions to be passed in a separate parameter from the normal mount
|
|
||||||
// options and ensures the sensitiveOptions are never logged. This method should
|
|
||||||
// be used by callers that pass sensitive material (like passwords) as mount
|
|
||||||
// options.
|
|
||||||
func MakeBindOptsSensitive(options []string, sensitiveOptions []string) (bool, []string, []string, []string) {
|
|
||||||
// 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"}
|
|
||||||
bindRemountSensitiveOpts := []string{}
|
|
||||||
bind := false
|
|
||||||
bindOpts := []string{"bind"}
|
|
||||||
|
|
||||||
// _netdev is a userspace mount option and does not automatically get added when
|
|
||||||
// bind mount is created and hence we must carry it over.
|
|
||||||
if checkForNetDev(options, sensitiveOptions) {
|
|
||||||
bindOpts = append(bindOpts, "_netdev")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, option := range options {
|
|
||||||
switch option {
|
|
||||||
case "bind":
|
|
||||||
bind = true
|
|
||||||
break
|
|
||||||
case "remount":
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
bindRemountOpts = append(bindRemountOpts, option)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, sensitiveOption := range sensitiveOptions {
|
|
||||||
switch sensitiveOption {
|
|
||||||
case "bind":
|
|
||||||
bind = true
|
|
||||||
break
|
|
||||||
case "remount":
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
bindRemountSensitiveOpts = append(bindRemountSensitiveOpts, sensitiveOption)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bind, bindOpts, bindRemountOpts, bindRemountSensitiveOpts
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkForNetDev(options []string, sensitiveOptions []string) bool {
|
|
||||||
for _, option := range options {
|
|
||||||
if option == "_netdev" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, sensitiveOption := range sensitiveOptions {
|
|
||||||
if sensitiveOption == "_netdev" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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), "../")
|
|
||||||
}
|
|
||||||
|
|
||||||
// sanitizedOptionsForLogging will return a comma separated string containing
|
|
||||||
// options and sensitiveOptions. Each entry in sensitiveOptions will be
|
|
||||||
// replaced with the string sensitiveOptionsRemoved
|
|
||||||
// e.g. o1,o2,<masked>,<masked>
|
|
||||||
func sanitizedOptionsForLogging(options []string, sensitiveOptions []string) string {
|
|
||||||
separator := ""
|
|
||||||
if len(options) > 0 && len(sensitiveOptions) > 0 {
|
|
||||||
separator = ","
|
|
||||||
}
|
|
||||||
|
|
||||||
sensitiveOptionsStart := ""
|
|
||||||
sensitiveOptionsEnd := ""
|
|
||||||
if len(sensitiveOptions) > 0 {
|
|
||||||
sensitiveOptionsStart = strings.Repeat(sensitiveOptionsRemoved+",", len(sensitiveOptions)-1)
|
|
||||||
sensitiveOptionsEnd = sensitiveOptionsRemoved
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(options, ",") +
|
|
||||||
separator +
|
|
||||||
sensitiveOptionsStart +
|
|
||||||
sensitiveOptionsEnd
|
|
||||||
}
|
|
103
vendor/k8s.io/utils/mount/mount_helper_common.go
generated
vendored
103
vendor/k8s.io/utils/mount/mount_helper_common.go
generated
vendored
@ -1,103 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 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"
|
|
||||||
|
|
||||||
"k8s.io/klog/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CleanupMountPoint unmounts the given path and deletes the remaining directory
|
|
||||||
// if successful. If extensiveMountPointCheck is true IsNotMountPoint will be
|
|
||||||
// called instead of IsLikelyNotMountPoint. IsNotMountPoint is more expensive
|
|
||||||
// but properly handles bind mounts within the same fs.
|
|
||||||
func CleanupMountPoint(mountPath string, mounter Interface, extensiveMountPointCheck bool) error {
|
|
||||||
pathExists, pathErr := PathExists(mountPath)
|
|
||||||
if !pathExists {
|
|
||||||
klog.Warningf("Warning: Unmount skipped because path does not exist: %v", mountPath)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
corruptedMnt := IsCorruptedMnt(pathErr)
|
|
||||||
if pathErr != nil && !corruptedMnt {
|
|
||||||
return fmt.Errorf("Error checking path: %v", pathErr)
|
|
||||||
}
|
|
||||||
return doCleanupMountPoint(mountPath, mounter, extensiveMountPointCheck, corruptedMnt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// doCleanupMountPoint unmounts the given path and
|
|
||||||
// deletes the remaining directory if successful.
|
|
||||||
// if extensiveMountPointCheck is true
|
|
||||||
// IsNotMountPoint will be called instead of IsLikelyNotMountPoint.
|
|
||||||
// IsNotMountPoint is more expensive but properly handles bind mounts within the same fs.
|
|
||||||
// if corruptedMnt is true, it means that the mountPath is a corrupted mountpoint, and the mount point check
|
|
||||||
// will be skipped
|
|
||||||
func doCleanupMountPoint(mountPath string, mounter Interface, extensiveMountPointCheck bool, corruptedMnt bool) error {
|
|
||||||
var notMnt bool
|
|
||||||
var err error
|
|
||||||
if !corruptedMnt {
|
|
||||||
if extensiveMountPointCheck {
|
|
||||||
notMnt, err = IsNotMountPoint(mounter, mountPath)
|
|
||||||
} else {
|
|
||||||
notMnt, err = mounter.IsLikelyNotMountPoint(mountPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if notMnt {
|
|
||||||
klog.Warningf("Warning: %q is not a mountpoint, deleting", mountPath)
|
|
||||||
return os.Remove(mountPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmount the mount path
|
|
||||||
klog.V(4).Infof("%q is a mountpoint, unmounting", mountPath)
|
|
||||||
if err := mounter.Unmount(mountPath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if extensiveMountPointCheck {
|
|
||||||
notMnt, err = IsNotMountPoint(mounter, mountPath)
|
|
||||||
} else {
|
|
||||||
notMnt, err = mounter.IsLikelyNotMountPoint(mountPath)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if notMnt {
|
|
||||||
klog.V(4).Infof("%q is unmounted, deleting the directory", mountPath)
|
|
||||||
return os.Remove(mountPath)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("Failed to unmount path %v", mountPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PathExists returns true if the specified path exists.
|
|
||||||
// TODO: clean this up to use pkg/util/file/FileExists
|
|
||||||
func PathExists(path string) (bool, error) {
|
|
||||||
_, err := os.Stat(path)
|
|
||||||
if err == nil {
|
|
||||||
return true, nil
|
|
||||||
} else if os.IsNotExist(err) {
|
|
||||||
return false, nil
|
|
||||||
} else if IsCorruptedMnt(err) {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
return false, err
|
|
||||||
}
|
|
158
vendor/k8s.io/utils/mount/mount_helper_unix.go
generated
vendored
158
vendor/k8s.io/utils/mount/mount_helper_unix.go
generated
vendored
@ -1,158 +0,0 @@
|
|||||||
// +build !windows
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2019 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"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
utilio "k8s.io/utils/io"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// At least number of fields per line in /proc/<pid>/mountinfo.
|
|
||||||
expectedAtLeastNumFieldsPerMountInfo = 10
|
|
||||||
// How many times to retry for a consistent read of /proc/mounts.
|
|
||||||
maxListTries = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsCorruptedMnt return true if err is about corrupted mount point
|
|
||||||
func IsCorruptedMnt(err error) bool {
|
|
||||||
if err == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
var underlyingError error
|
|
||||||
switch pe := err.(type) {
|
|
||||||
case nil:
|
|
||||||
return false
|
|
||||||
case *os.PathError:
|
|
||||||
underlyingError = pe.Err
|
|
||||||
case *os.LinkError:
|
|
||||||
underlyingError = pe.Err
|
|
||||||
case *os.SyscallError:
|
|
||||||
underlyingError = pe.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
return underlyingError == syscall.ENOTCONN || underlyingError == syscall.ESTALE || underlyingError == syscall.EIO || underlyingError == syscall.EACCES
|
|
||||||
}
|
|
||||||
|
|
||||||
// MountInfo represents a single line in /proc/<pid>/mountinfo.
|
|
||||||
type MountInfo struct { // nolint: golint
|
|
||||||
// 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
|
|
||||||
// Major indicates one half of the device ID which identifies the device class
|
|
||||||
// (parsed from `st_dev` for files on this filesystem).
|
|
||||||
Major int
|
|
||||||
// Minor indicates one half of the device ID which identifies a specific
|
|
||||||
// instance of device (parsed from `st_dev` for files on this filesystem).
|
|
||||||
Minor int
|
|
||||||
// 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
|
|
||||||
// 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.
|
|
||||||
func ParseMountInfo(filename string) ([]MountInfo, error) {
|
|
||||||
content, err := utilio.ConsistentRead(filename, maxListTries)
|
|
||||||
if err != nil {
|
|
||||||
return []MountInfo{}, err
|
|
||||||
}
|
|
||||||
contentStr := string(content)
|
|
||||||
infos := []MountInfo{}
|
|
||||||
|
|
||||||
for _, line := range strings.Split(contentStr, "\n") {
|
|
||||||
if line == "" {
|
|
||||||
// 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) < 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
|
|
||||||
}
|
|
||||||
mm := strings.Split(fields[2], ":")
|
|
||||||
if len(mm) != 2 {
|
|
||||||
return nil, fmt.Errorf("parsing '%s' failed: unexpected minor:major pair %s", line, mm)
|
|
||||||
}
|
|
||||||
major, err := strconv.Atoi(mm[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("parsing '%s' failed: unable to parse major device id, err:%v", mm[0], err)
|
|
||||||
}
|
|
||||||
minor, err := strconv.Atoi(mm[1])
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("parsing '%s' failed: unable to parse minor device id, err:%v", mm[1], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
info := MountInfo{
|
|
||||||
ID: id,
|
|
||||||
ParentID: parentID,
|
|
||||||
Major: major,
|
|
||||||
Minor: minor,
|
|
||||||
Root: fields[3],
|
|
||||||
MountPoint: fields[4],
|
|
||||||
MountOptions: strings.Split(fields[5], ","),
|
|
||||||
}
|
|
||||||
// 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++
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// isMountPointMatch returns true if the path in mp is the same as dir.
|
|
||||||
// Handles case where mountpoint dir has been renamed due to stale NFS mount.
|
|
||||||
func isMountPointMatch(mp MountPoint, dir string) bool {
|
|
||||||
deletedDir := fmt.Sprintf("%s\\040(deleted)", dir)
|
|
||||||
return ((mp.Path == dir) || (mp.Path == deletedDir))
|
|
||||||
}
|
|
101
vendor/k8s.io/utils/mount/mount_helper_windows.go
generated
vendored
101
vendor/k8s.io/utils/mount/mount_helper_windows.go
generated
vendored
@ -1,101 +0,0 @@
|
|||||||
// +build windows
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2019 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"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"k8s.io/klog/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// following failure codes are from https://docs.microsoft.com/en-us/windows/desktop/debug/system-error-codes--1300-1699-
|
|
||||||
// ERROR_BAD_NETPATH = 53
|
|
||||||
// ERROR_NETWORK_BUSY = 54
|
|
||||||
// ERROR_UNEXP_NET_ERR = 59
|
|
||||||
// ERROR_NETNAME_DELETED = 64
|
|
||||||
// ERROR_NETWORK_ACCESS_DENIED = 65
|
|
||||||
// ERROR_BAD_DEV_TYPE = 66
|
|
||||||
// ERROR_BAD_NET_NAME = 67
|
|
||||||
// ERROR_SESSION_CREDENTIAL_CONFLICT = 1219
|
|
||||||
// ERROR_LOGON_FAILURE = 1326
|
|
||||||
var errorNoList = [...]int{53, 54, 59, 64, 65, 66, 67, 1219, 1326}
|
|
||||||
|
|
||||||
// IsCorruptedMnt return true if err is about corrupted mount point
|
|
||||||
func IsCorruptedMnt(err error) bool {
|
|
||||||
if err == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var underlyingError error
|
|
||||||
switch pe := err.(type) {
|
|
||||||
case nil:
|
|
||||||
return false
|
|
||||||
case *os.PathError:
|
|
||||||
underlyingError = pe.Err
|
|
||||||
case *os.LinkError:
|
|
||||||
underlyingError = pe.Err
|
|
||||||
case *os.SyscallError:
|
|
||||||
underlyingError = pe.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ee, ok := underlyingError.(syscall.Errno); ok {
|
|
||||||
for _, errno := range errorNoList {
|
|
||||||
if int(ee) == errno {
|
|
||||||
klog.Warningf("IsCorruptedMnt failed with error: %v, error code: %v", err, errno)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// NormalizeWindowsPath makes sure the given path is a valid path on Windows
|
|
||||||
// systems by making sure all instances of `/` are replaced with `\\`, and the
|
|
||||||
// path beings with `c:`
|
|
||||||
func NormalizeWindowsPath(path string) string {
|
|
||||||
normalizedPath := strings.Replace(path, "/", "\\", -1)
|
|
||||||
if strings.HasPrefix(normalizedPath, "\\") {
|
|
||||||
normalizedPath = "c:" + normalizedPath
|
|
||||||
}
|
|
||||||
return normalizedPath
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidateDiskNumber : disk number should be a number in [0, 99]
|
|
||||||
func ValidateDiskNumber(disk string) error {
|
|
||||||
diskNum, err := strconv.Atoi(disk)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("wrong disk number format: %q, err:%v", disk, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if diskNum < 0 || diskNum > 99 {
|
|
||||||
return fmt.Errorf("disk number out of range: %q", disk)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// isMountPointMatch determines if the mountpoint matches the dir
|
|
||||||
func isMountPointMatch(mp MountPoint, dir string) bool {
|
|
||||||
return mp.Path == dir
|
|
||||||
}
|
|
551
vendor/k8s.io/utils/mount/mount_linux.go
generated
vendored
551
vendor/k8s.io/utils/mount/mount_linux.go
generated
vendored
@ -1,551 +0,0 @@
|
|||||||
// +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"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"k8s.io/klog/v2"
|
|
||||||
utilexec "k8s.io/utils/exec"
|
|
||||||
utilio "k8s.io/utils/io"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// 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"
|
|
||||||
// Location of the mountinfo file
|
|
||||||
procMountInfoPath = "/proc/self/mountinfo"
|
|
||||||
// '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
|
|
||||||
withSystemd bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
withSystemd: detectSystemd(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mount mounts source to target as fstype with given options. 'source' and 'fstype' must
|
|
||||||
// be an empty string in case it's not required, e.g. for remount, or for auto filesystem
|
|
||||||
// type, where kernel handles fstype 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 {
|
|
||||||
return mounter.MountSensitive(source, target, fstype, options, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MountSensitive is the same as Mount() but this method allows
|
|
||||||
// sensitiveOptions to be passed in a separate parameter from the normal
|
|
||||||
// mount options and ensures the sensitiveOptions are never logged. This
|
|
||||||
// method should be used by callers that pass sensitive material (like
|
|
||||||
// passwords) as mount options.
|
|
||||||
func (mounter *Mounter) MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []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 a support bind mounts.
|
|
||||||
mounterPath := ""
|
|
||||||
bind, bindOpts, bindRemountOpts, bindRemountOptsSensitive := MakeBindOptsSensitive(options, sensitiveOptions)
|
|
||||||
if bind {
|
|
||||||
err := mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindOpts, bindRemountOptsSensitive)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, bindRemountOpts, bindRemountOptsSensitive)
|
|
||||||
}
|
|
||||||
// The list of filesystems that require containerized mounter on GCI image cluster
|
|
||||||
fsTypesNeedMounter := map[string]struct{}{
|
|
||||||
"nfs": {},
|
|
||||||
"glusterfs": {},
|
|
||||||
"ceph": {},
|
|
||||||
"cifs": {},
|
|
||||||
}
|
|
||||||
if _, ok := fsTypesNeedMounter[fstype]; ok {
|
|
||||||
mounterPath = mounter.mounterPath
|
|
||||||
}
|
|
||||||
return mounter.doMount(mounterPath, defaultMountCommand, source, target, fstype, options, sensitiveOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// doMount runs the mount command. mounterPath is the path to mounter binary if containerized mounter is used.
|
|
||||||
// sensitiveOptions is an extension of options except they will not be logged (because they may contain sensitive material)
|
|
||||||
func (mounter *Mounter) doMount(mounterPath string, mountCmd string, source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
|
||||||
mountArgs, mountArgsLogStr := MakeMountArgsSensitive(source, target, fstype, options, sensitiveOptions)
|
|
||||||
if len(mounterPath) > 0 {
|
|
||||||
mountArgs = append([]string{mountCmd}, mountArgs...)
|
|
||||||
mountArgsLogStr = mountCmd + " " + mountArgsLogStr
|
|
||||||
mountCmd = mounterPath
|
|
||||||
}
|
|
||||||
|
|
||||||
if mounter.withSystemd {
|
|
||||||
// Try to run mount via systemd-run --scope. This will escape the
|
|
||||||
// service where kubelet runs and any fuse daemons will be started in a
|
|
||||||
// specific scope. kubelet service than can be restarted without killing
|
|
||||||
// these fuse daemons.
|
|
||||||
//
|
|
||||||
// Complete command line (when mounterPath is not used):
|
|
||||||
// systemd-run --description=... --scope -- mount -t <type> <what> <where>
|
|
||||||
//
|
|
||||||
// Expected flow:
|
|
||||||
// * systemd-run creates a transient scope (=~ cgroup) and executes its
|
|
||||||
// argument (/bin/mount) there.
|
|
||||||
// * mount does its job, forks a fuse daemon if necessary and finishes.
|
|
||||||
// (systemd-run --scope finishes at this point, returning mount's exit
|
|
||||||
// code and stdout/stderr - thats one of --scope benefits).
|
|
||||||
// * systemd keeps the fuse daemon running in the scope (i.e. in its own
|
|
||||||
// cgroup) until the fuse daemon dies (another --scope benefit).
|
|
||||||
// Kubelet service can be restarted and the fuse daemon survives.
|
|
||||||
// * When the fuse daemon dies (e.g. during unmount) systemd removes the
|
|
||||||
// scope automatically.
|
|
||||||
//
|
|
||||||
// systemd-mount is not used because it's too new for older distros
|
|
||||||
// (CentOS 7, Debian Jessie).
|
|
||||||
mountCmd, mountArgs, mountArgsLogStr = AddSystemdScopeSensitive("systemd-run", target, mountCmd, mountArgs, mountArgsLogStr)
|
|
||||||
} else {
|
|
||||||
// No systemd-run on the host (or we failed to check it), assume kubelet
|
|
||||||
// does not run as a systemd service.
|
|
||||||
// No code here, mountCmd and mountArgs are already populated.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logging with sensitive mount options removed.
|
|
||||||
klog.V(4).Infof("Mounting cmd (%s) with arguments (%s)", mountCmd, mountArgsLogStr)
|
|
||||||
command := exec.Command(mountCmd, mountArgs...)
|
|
||||||
output, err := command.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
klog.Errorf("Mount failed: %v\nMounting command: %s\nMounting arguments: %s\nOutput: %s\n", err, mountCmd, mountArgsLogStr, string(output))
|
|
||||||
return fmt.Errorf("mount failed: %v\nMounting command: %s\nMounting arguments: %s\nOutput: %s",
|
|
||||||
err, mountCmd, mountArgsLogStr, string(output))
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// detectSystemd returns true if OS runs with systemd as init. When not sure
|
|
||||||
// (permission errors, ...), it returns false.
|
|
||||||
// There may be different ways how to detect systemd, this one makes sure that
|
|
||||||
// systemd-runs (needed by Mount()) works.
|
|
||||||
func detectSystemd() bool {
|
|
||||||
if _, err := exec.LookPath("systemd-run"); err != nil {
|
|
||||||
klog.V(2).Infof("Detected OS without systemd")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// Try to run systemd-run --scope /bin/true, that should be enough
|
|
||||||
// to make sure that systemd is really running and not just installed,
|
|
||||||
// which happens when running in a container with a systemd-based image
|
|
||||||
// but with different pid 1.
|
|
||||||
cmd := exec.Command("systemd-run", "--description=Kubernetes systemd probe", "--scope", "true")
|
|
||||||
output, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
klog.V(2).Infof("Cannot run systemd-run, assuming non-systemd OS")
|
|
||||||
klog.V(4).Infof("systemd-run failed with: %v", err)
|
|
||||||
klog.V(4).Infof("systemd-run output: %s", string(output))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
klog.V(2).Infof("Detected OS with systemd")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeMountArgs makes the arguments to the mount(8) command.
|
|
||||||
// options MUST not contain sensitive material (like passwords).
|
|
||||||
func MakeMountArgs(source, target, fstype string, options []string) (mountArgs []string) {
|
|
||||||
mountArgs, _ = MakeMountArgsSensitive(source, target, fstype, options, nil /* sensitiveOptions */)
|
|
||||||
return mountArgs
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeMountArgsSensitive makes the arguments to the mount(8) command.
|
|
||||||
// sensitiveOptions is an extension of options except they will not be logged (because they may contain sensitive material)
|
|
||||||
func MakeMountArgsSensitive(source, target, fstype string, options []string, sensitiveOptions []string) (mountArgs []string, mountArgsLogStr string) {
|
|
||||||
// Build mount command as follows:
|
|
||||||
// mount [-t $fstype] [-o $options] [$source] $target
|
|
||||||
mountArgs = []string{}
|
|
||||||
mountArgsLogStr = ""
|
|
||||||
if len(fstype) > 0 {
|
|
||||||
mountArgs = append(mountArgs, "-t", fstype)
|
|
||||||
mountArgsLogStr += strings.Join(mountArgs, " ")
|
|
||||||
}
|
|
||||||
if len(options) > 0 || len(sensitiveOptions) > 0 {
|
|
||||||
combinedOptions := []string{}
|
|
||||||
combinedOptions = append(combinedOptions, options...)
|
|
||||||
combinedOptions = append(combinedOptions, sensitiveOptions...)
|
|
||||||
mountArgs = append(mountArgs, "-o", strings.Join(combinedOptions, ","))
|
|
||||||
// exclude sensitiveOptions from log string
|
|
||||||
mountArgsLogStr += " -o " + sanitizedOptionsForLogging(options, sensitiveOptions)
|
|
||||||
}
|
|
||||||
if len(source) > 0 {
|
|
||||||
mountArgs = append(mountArgs, source)
|
|
||||||
mountArgsLogStr += " " + source
|
|
||||||
}
|
|
||||||
mountArgs = append(mountArgs, target)
|
|
||||||
mountArgsLogStr += " " + target
|
|
||||||
|
|
||||||
return mountArgs, mountArgsLogStr
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddSystemdScope adds "system-run --scope" to given command line
|
|
||||||
// If args contains sensitive material, use AddSystemdScopeSensitive to construct
|
|
||||||
// a safe to log string.
|
|
||||||
func AddSystemdScope(systemdRunPath, mountName, command string, args []string) (string, []string) {
|
|
||||||
descriptionArg := fmt.Sprintf("--description=Kubernetes transient mount for %s", mountName)
|
|
||||||
systemdRunArgs := []string{descriptionArg, "--scope", "--", command}
|
|
||||||
return systemdRunPath, append(systemdRunArgs, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddSystemdScopeSensitive adds "system-run --scope" to given command line
|
|
||||||
// It also accepts takes a sanitized string containing mount arguments, mountArgsLogStr,
|
|
||||||
// and returns the string appended to the systemd command for logging.
|
|
||||||
func AddSystemdScopeSensitive(systemdRunPath, mountName, command string, args []string, mountArgsLogStr string) (string, []string, string) {
|
|
||||||
descriptionArg := fmt.Sprintf("--description=Kubernetes transient mount for %s", mountName)
|
|
||||||
systemdRunArgs := []string{descriptionArg, "--scope", "--", command}
|
|
||||||
return systemdRunPath, append(systemdRunArgs, args...), strings.Join(systemdRunArgs, " ") + " " + mountArgsLogStr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmount unmounts the target.
|
|
||||||
func (mounter *Mounter) Unmount(target string) error {
|
|
||||||
klog.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", err, target, string(output))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns a list of all mounted filesystems.
|
|
||||||
func (*Mounter) List() ([]MountPoint, error) {
|
|
||||||
return ListProcMounts(procMountsPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
// It also can not distinguish between mountpoints and symbolic links.
|
|
||||||
// mkdir /tmp/a /tmp/b; mount --bind /tmp/a /tmp/b; IsLikelyNotMountPoint("/tmp/b")
|
|
||||||
// will return true. When in fact /tmp/b is a mount point. If this situation
|
|
||||||
// is 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.Stat(filepath.Dir(strings.TrimSuffix(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
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMountRefs finds all mount references to pathname, returns a
|
|
||||||
// list of paths. Path could be a mountpoint or a normal
|
|
||||||
// directory (for bind mount).
|
|
||||||
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
|
|
||||||
pathExists, pathErr := PathExists(pathname)
|
|
||||||
if !pathExists {
|
|
||||||
return []string{}, nil
|
|
||||||
} else if IsCorruptedMnt(pathErr) {
|
|
||||||
klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", pathname)
|
|
||||||
return []string{}, nil
|
|
||||||
} else if pathErr != nil {
|
|
||||||
return nil, fmt.Errorf("error checking path %s: %v", pathname, pathErr)
|
|
||||||
}
|
|
||||||
realpath, err := filepath.EvalSymlinks(pathname)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return SearchMountPoints(realpath, procMountInfoPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkAndRepairFileSystem checks and repairs filesystems using command fsck.
|
|
||||||
func (mounter *SafeFormatAndMount) checkAndRepairFilesystem(source string) error {
|
|
||||||
klog.V(4).Infof("Checking for issues with fsck on disk: %s", source)
|
|
||||||
args := []string{"-a", source}
|
|
||||||
out, err := mounter.Exec.Command("fsck", args...).CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
ee, isExitError := err.(utilexec.ExitError)
|
|
||||||
switch {
|
|
||||||
case err == utilexec.ErrExecutableNotFound:
|
|
||||||
klog.Warningf("'fsck' not found on system; continuing mount without running 'fsck'.")
|
|
||||||
case isExitError && ee.ExitStatus() == fsckErrorsCorrected:
|
|
||||||
klog.Infof("Device %s has errors which were corrected by fsck.", source)
|
|
||||||
case isExitError && ee.ExitStatus() == fsckErrorsUncorrected:
|
|
||||||
return NewMountError(HasFilesystemErrors, "'fsck' found errors on device %s but could not correct them: %s", source, string(out))
|
|
||||||
case isExitError && ee.ExitStatus() > fsckErrorsUncorrected:
|
|
||||||
klog.Infof("`fsck` error %s", string(out))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// formatAndMount uses unix utils to format and mount the given disk
|
|
||||||
func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
|
||||||
readOnly := false
|
|
||||||
for _, option := range options {
|
|
||||||
if option == "ro" {
|
|
||||||
readOnly = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !readOnly {
|
|
||||||
// Check sensitiveOptions for ro
|
|
||||||
for _, option := range sensitiveOptions {
|
|
||||||
if option == "ro" {
|
|
||||||
readOnly = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
options = append(options, "defaults")
|
|
||||||
mountErrorValue := UnknownMountError
|
|
||||||
|
|
||||||
// Check if the disk is already formatted
|
|
||||||
existingFormat, err := mounter.GetDiskFormat(source)
|
|
||||||
if err != nil {
|
|
||||||
return NewMountError(GetDiskFormatFailed, "failed to get disk format of disk %s: %v", source, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use 'ext4' as the default
|
|
||||||
if len(fstype) == 0 {
|
|
||||||
fstype = "ext4"
|
|
||||||
}
|
|
||||||
|
|
||||||
if existingFormat == "" {
|
|
||||||
// Do not attempt to format the disk if mounting as readonly, return an error to reflect this.
|
|
||||||
if readOnly {
|
|
||||||
return NewMountError(UnformattedReadOnly, "cannot mount unformatted disk %s as we are manipulating it in read-only mode", source)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disk is unformatted so format it.
|
|
||||||
args := []string{source}
|
|
||||||
if fstype == "ext4" || fstype == "ext3" {
|
|
||||||
args = []string{
|
|
||||||
"-F", // Force flag
|
|
||||||
"-m0", // Zero blocks reserved for super-user
|
|
||||||
source,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
klog.Infof("Disk %q appears to be unformatted, attempting to format as type: %q with options: %v", source, fstype, args)
|
|
||||||
output, err := mounter.Exec.Command("mkfs."+fstype, args...).CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
// Do not log sensitiveOptions only options
|
|
||||||
sensitiveOptionsLog := sanitizedOptionsForLogging(options, sensitiveOptions)
|
|
||||||
detailedErr := fmt.Sprintf("format of disk %q failed: type:(%q) target:(%q) options:(%q) errcode:(%v) output:(%v) ", source, fstype, target, sensitiveOptionsLog, err, string(output))
|
|
||||||
klog.Error(detailedErr)
|
|
||||||
return NewMountError(FormatFailed, detailedErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
klog.Infof("Disk successfully formatted (mkfs): %s - %s %s", fstype, source, target)
|
|
||||||
} else {
|
|
||||||
if fstype != existingFormat {
|
|
||||||
// Verify that the disk is formatted with filesystem type we are expecting
|
|
||||||
mountErrorValue = FilesystemMismatch
|
|
||||||
klog.Warningf("Configured to mount disk %s as %s but current format is %s, things might break", source, existingFormat, fstype)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !readOnly {
|
|
||||||
// Run check tools on the disk to fix repairable issues, only do this for formatted volumes requested as rw.
|
|
||||||
err := mounter.checkAndRepairFilesystem(source)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mount the disk
|
|
||||||
klog.V(4).Infof("Attempting to mount disk %s in %s format at %s", source, fstype, target)
|
|
||||||
if err := mounter.MountSensitive(source, target, fstype, options, sensitiveOptions); err != nil {
|
|
||||||
return NewMountError(mountErrorValue, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDiskFormat uses 'blkid' to see if the given disk is unformatted
|
|
||||||
func (mounter *SafeFormatAndMount) GetDiskFormat(disk string) (string, error) {
|
|
||||||
args := []string{"-p", "-s", "TYPE", "-s", "PTTYPE", "-o", "export", disk}
|
|
||||||
klog.V(4).Infof("Attempting to determine if disk %q is formatted using blkid with args: (%v)", disk, args)
|
|
||||||
dataOut, err := mounter.Exec.Command("blkid", args...).CombinedOutput()
|
|
||||||
output := string(dataOut)
|
|
||||||
klog.V(4).Infof("Output: %q, err: %v", output, err)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
if exit, ok := err.(utilexec.ExitError); ok {
|
|
||||||
if exit.ExitStatus() == 2 {
|
|
||||||
// Disk device is unformatted.
|
|
||||||
// For `blkid`, if the specified token (TYPE/PTTYPE, etc) was
|
|
||||||
// not found, or no (specified) devices could be identified, an
|
|
||||||
// exit code of 2 is returned.
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
klog.Errorf("Could not determine if disk %q is formatted (%v)", disk, err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var fstype, pttype string
|
|
||||||
|
|
||||||
lines := strings.Split(output, "\n")
|
|
||||||
for _, l := range lines {
|
|
||||||
if len(l) <= 0 {
|
|
||||||
// Ignore empty line.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cs := strings.Split(l, "=")
|
|
||||||
if len(cs) != 2 {
|
|
||||||
return "", fmt.Errorf("blkid returns invalid output: %s", output)
|
|
||||||
}
|
|
||||||
// TYPE is filesystem type, and PTTYPE is partition table type, according
|
|
||||||
// to https://www.kernel.org/pub/linux/utils/util-linux/v2.21/libblkid-docs/.
|
|
||||||
if cs[0] == "TYPE" {
|
|
||||||
fstype = cs[1]
|
|
||||||
} else if cs[0] == "PTTYPE" {
|
|
||||||
pttype = cs[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(pttype) > 0 {
|
|
||||||
klog.V(4).Infof("Disk %s detected partition table type: %s", disk, pttype)
|
|
||||||
// Returns a special non-empty string as filesystem type, then kubelet
|
|
||||||
// will not format it.
|
|
||||||
return "unknown data, probably partitions", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return fstype, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListProcMounts is shared with NsEnterMounter
|
|
||||||
func ListProcMounts(mountFilePath string) ([]MountPoint, error) {
|
|
||||||
content, err := utilio.ConsistentRead(mountFilePath, maxListTries)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return parseProcMounts(content)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseProcMounts(content []byte) ([]MountPoint, error) {
|
|
||||||
out := []MountPoint{}
|
|
||||||
lines := strings.Split(string(content), "\n")
|
|
||||||
for _, line := range lines {
|
|
||||||
if line == "" {
|
|
||||||
// the last split() item is empty string following the last \n
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fields := strings.Fields(line)
|
|
||||||
if len(fields) != expectedNumFieldsPerLine {
|
|
||||||
// Do not log line in case it contains sensitive Mount options
|
|
||||||
return nil, fmt.Errorf("wrong number of fields (expected %d, got %d)", expectedNumFieldsPerLine, len(fields))
|
|
||||||
}
|
|
||||||
|
|
||||||
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 nil, err
|
|
||||||
}
|
|
||||||
mp.Freq = freq
|
|
||||||
|
|
||||||
pass, err := strconv.Atoi(fields[5])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
mp.Pass = pass
|
|
||||||
|
|
||||||
out = append(out, mp)
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchMountPoints finds all mount references to the source, returns a list of
|
|
||||||
// mountpoints.
|
|
||||||
// The source can be a mount point or a normal directory (bind mount). We
|
|
||||||
// didn't support device because there is no use case by now.
|
|
||||||
// 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 := ""
|
|
||||||
major := -1
|
|
||||||
minor := -1
|
|
||||||
|
|
||||||
// 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))
|
|
||||||
major = mis[i].Major
|
|
||||||
minor = mis[i].Minor
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rootPath == "" || major == -1 || minor == -1 {
|
|
||||||
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].Major == major && mis[i].Minor == minor {
|
|
||||||
refs = append(refs, mis[i].MountPoint)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return refs, nil
|
|
||||||
}
|
|
77
vendor/k8s.io/utils/mount/mount_unsupported.go
generated
vendored
77
vendor/k8s.io/utils/mount/mount_unsupported.go
generated
vendored
@ -1,77 +0,0 @@
|
|||||||
// +build !linux,!windows
|
|
||||||
|
|
||||||
/*
|
|
||||||
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 (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Mounter implements mount.Interface for unsupported platforms
|
|
||||||
type Mounter struct {
|
|
||||||
mounterPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
var errUnsupported = 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.
|
|
||||||
func New(mounterPath string) Interface {
|
|
||||||
return &Mounter{
|
|
||||||
mounterPath: mounterPath,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mount always returns an error on unsupported platforms
|
|
||||||
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
|
|
||||||
return errUnsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mount always returns an error on unsupported platforms
|
|
||||||
func (mounter *Mounter) MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
|
||||||
return errUnsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmount always returns an error on unsupported platforms
|
|
||||||
func (mounter *Mounter) Unmount(target string) error {
|
|
||||||
return errUnsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// List always returns an error on unsupported platforms
|
|
||||||
func (mounter *Mounter) List() ([]MountPoint, error) {
|
|
||||||
return []MountPoint{}, errUnsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLikelyNotMountPoint always returns an error on unsupported platforms
|
|
||||||
func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
|
||||||
return true, errUnsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMountRefs always returns an error on unsupported platforms
|
|
||||||
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
|
|
||||||
return nil, errUnsupported
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
|
||||||
return mounter.Interface.Mount(source, target, fstype, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mounter *SafeFormatAndMount) diskLooksUnformatted(disk string) (bool, error) {
|
|
||||||
return true, errUnsupported
|
|
||||||
}
|
|
313
vendor/k8s.io/utils/mount/mount_windows.go
generated
vendored
313
vendor/k8s.io/utils/mount/mount_windows.go
generated
vendored
@ -1,313 +0,0 @@
|
|||||||
// +build windows
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2017 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"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"k8s.io/klog/v2"
|
|
||||||
"k8s.io/utils/keymutex"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
accessDenied string = "access is denied"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Mounter provides the default implementation of mount.Interface
|
|
||||||
// for the windows platform. This implementation assumes that the
|
|
||||||
// kubelet is running in the host's root mount namespace.
|
|
||||||
type Mounter struct {
|
|
||||||
mounterPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// acquire lock for smb mount
|
|
||||||
var getSMBMountMutex = keymutex.NewHashed(0)
|
|
||||||
|
|
||||||
// Mount : mounts source to target with given options.
|
|
||||||
// currently only supports cifs(smb), bind mount(for disk)
|
|
||||||
func (mounter *Mounter) Mount(source string, target string, fstype string, options []string) error {
|
|
||||||
return mounter.MountSensitive(source, target, fstype, options, nil /* sensitiveOptions */)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MountSensitive is the same as Mount() but this method allows
|
|
||||||
// sensitiveOptions to be passed in a separate parameter from the normal
|
|
||||||
// mount options and ensures the sensitiveOptions are never logged. This
|
|
||||||
// method should be used by callers that pass sensitive material (like
|
|
||||||
// passwords) as mount options.
|
|
||||||
func (mounter *Mounter) MountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
|
||||||
target = NormalizeWindowsPath(target)
|
|
||||||
sanitizedOptionsForLogging := sanitizedOptionsForLogging(options, sensitiveOptions)
|
|
||||||
|
|
||||||
if source == "tmpfs" {
|
|
||||||
klog.V(3).Infof("mounting source (%q), target (%q), with options (%q)", source, target, sanitizedOptionsForLogging)
|
|
||||||
return os.MkdirAll(target, 0755)
|
|
||||||
}
|
|
||||||
|
|
||||||
parentDir := filepath.Dir(target)
|
|
||||||
if err := os.MkdirAll(parentDir, 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
klog.V(4).Infof("mount options(%q) source:%q, target:%q, fstype:%q, begin to mount",
|
|
||||||
sanitizedOptionsForLogging, source, target, fstype)
|
|
||||||
bindSource := source
|
|
||||||
|
|
||||||
if bind, _, _, _ := MakeBindOptsSensitive(options, sensitiveOptions); bind {
|
|
||||||
bindSource = NormalizeWindowsPath(source)
|
|
||||||
} else {
|
|
||||||
allOptions := []string{}
|
|
||||||
allOptions = append(allOptions, options...)
|
|
||||||
allOptions = append(allOptions, sensitiveOptions...)
|
|
||||||
if len(allOptions) < 2 {
|
|
||||||
return fmt.Errorf("mount options(%q) should have at least 2 options, current number:%d, source:%q, target:%q",
|
|
||||||
sanitizedOptionsForLogging, len(allOptions), source, target)
|
|
||||||
}
|
|
||||||
|
|
||||||
// currently only cifs mount is supported
|
|
||||||
if strings.ToLower(fstype) != "cifs" {
|
|
||||||
return fmt.Errorf("only cifs mount is supported now, fstype: %q, mounting source (%q), target (%q), with options (%q)", fstype, source, target, sanitizedOptionsForLogging)
|
|
||||||
}
|
|
||||||
|
|
||||||
// lock smb mount for the same source
|
|
||||||
getSMBMountMutex.LockKey(source)
|
|
||||||
defer getSMBMountMutex.UnlockKey(source)
|
|
||||||
|
|
||||||
username := allOptions[0]
|
|
||||||
password := allOptions[1]
|
|
||||||
if output, err := newSMBMapping(username, password, source); err != nil {
|
|
||||||
klog.Warningf("SMB Mapping(%s) returned with error(%v), output(%s)", source, err, string(output))
|
|
||||||
if isSMBMappingExist(source) {
|
|
||||||
valid, err := isValidPath(source)
|
|
||||||
if !valid {
|
|
||||||
if err == nil || isAccessDeniedError(err) {
|
|
||||||
klog.V(2).Infof("SMB Mapping(%s) already exists while it's not valid, return error: %v, now begin to remove and remount", source, err)
|
|
||||||
if output, err = removeSMBMapping(source); err != nil {
|
|
||||||
return fmt.Errorf("Remove-SmbGlobalMapping failed: %v, output: %q", err, output)
|
|
||||||
}
|
|
||||||
if output, err := newSMBMapping(username, password, source); err != nil {
|
|
||||||
return fmt.Errorf("New-SmbGlobalMapping(%s) failed: %v, output: %q", source, err, output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
klog.V(2).Infof("SMB Mapping(%s) already exists and is still valid, skip error(%v)", source, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("New-SmbGlobalMapping(%s) failed: %v, output: %q", source, err, output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output, err := exec.Command("cmd", "/c", "mklink", "/D", target, bindSource).CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
klog.Errorf("mklink failed: %v, source(%q) target(%q) output: %q", err, bindSource, target, string(output))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
klog.V(2).Infof("mklink source(%q) on target(%q) successfully, output: %q", bindSource, target, string(output))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// do the SMB mount with username, password, remotepath
|
|
||||||
// return (output, error)
|
|
||||||
func newSMBMapping(username, password, remotepath string) (string, error) {
|
|
||||||
if username == "" || password == "" || remotepath == "" {
|
|
||||||
return "", fmt.Errorf("invalid parameter(username: %s, password: %s, remoteapth: %s)", username, sensitiveOptionsRemoved, remotepath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// use PowerShell Environment Variables to store user input string to prevent command line injection
|
|
||||||
// https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-5.1
|
|
||||||
cmdLine := `$PWord = ConvertTo-SecureString -String $Env:smbpassword -AsPlainText -Force` +
|
|
||||||
`;$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Env:smbuser, $PWord` +
|
|
||||||
`;New-SmbGlobalMapping -RemotePath $Env:smbremotepath -Credential $Credential`
|
|
||||||
cmd := exec.Command("powershell", "/c", cmdLine)
|
|
||||||
cmd.Env = append(os.Environ(),
|
|
||||||
fmt.Sprintf("smbuser=%s", username),
|
|
||||||
fmt.Sprintf("smbpassword=%s", password),
|
|
||||||
fmt.Sprintf("smbremotepath=%s", remotepath))
|
|
||||||
|
|
||||||
output, err := cmd.CombinedOutput()
|
|
||||||
return string(output), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// check whether remotepath is already mounted
|
|
||||||
func isSMBMappingExist(remotepath string) bool {
|
|
||||||
cmd := exec.Command("powershell", "/c", `Get-SmbGlobalMapping -RemotePath $Env:smbremotepath`)
|
|
||||||
cmd.Env = append(os.Environ(), fmt.Sprintf("smbremotepath=%s", remotepath))
|
|
||||||
_, err := cmd.CombinedOutput()
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// check whether remotepath is valid
|
|
||||||
// return (true, nil) if remotepath is valid
|
|
||||||
func isValidPath(remotepath string) (bool, error) {
|
|
||||||
cmd := exec.Command("powershell", "/c", `Test-Path $Env:remoteapth`)
|
|
||||||
cmd.Env = append(os.Environ(), fmt.Sprintf("remoteapth=%s", remotepath))
|
|
||||||
output, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return false, fmt.Errorf("returned output: %s, error: %v", string(output), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.HasPrefix(strings.ToLower(string(output)), "true"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isAccessDeniedError(err error) bool {
|
|
||||||
return err != nil && strings.Contains(strings.ToLower(err.Error()), accessDenied)
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove SMB mapping
|
|
||||||
func removeSMBMapping(remotepath string) (string, error) {
|
|
||||||
cmd := exec.Command("powershell", "/c", `Remove-SmbGlobalMapping -RemotePath $Env:smbremotepath -Force`)
|
|
||||||
cmd.Env = append(os.Environ(), fmt.Sprintf("smbremotepath=%s", remotepath))
|
|
||||||
output, err := cmd.CombinedOutput()
|
|
||||||
return string(output), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmount unmounts the target.
|
|
||||||
func (mounter *Mounter) Unmount(target string) error {
|
|
||||||
klog.V(4).Infof("azureMount: Unmount target (%q)", target)
|
|
||||||
target = NormalizeWindowsPath(target)
|
|
||||||
if output, err := exec.Command("cmd", "/c", "rmdir", target).CombinedOutput(); err != nil {
|
|
||||||
klog.Errorf("rmdir failed: %v, output: %q", err, string(output))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns a list of all mounted filesystems. todo
|
|
||||||
func (mounter *Mounter) List() ([]MountPoint, error) {
|
|
||||||
return []MountPoint{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLikelyNotMountPoint determines if a directory is not a mountpoint.
|
|
||||||
func (mounter *Mounter) IsLikelyNotMountPoint(file string) (bool, error) {
|
|
||||||
stat, err := os.Lstat(file)
|
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if stat.Mode()&os.ModeSymlink != 0 {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMountRefs : empty implementation here since there is no place to query all mount points on Windows
|
|
||||||
func (mounter *Mounter) GetMountRefs(pathname string) ([]string, error) {
|
|
||||||
windowsPath := NormalizeWindowsPath(pathname)
|
|
||||||
pathExists, pathErr := PathExists(windowsPath)
|
|
||||||
if !pathExists {
|
|
||||||
return []string{}, nil
|
|
||||||
} else if IsCorruptedMnt(pathErr) {
|
|
||||||
klog.Warningf("GetMountRefs found corrupted mount at %s, treating as unmounted path", windowsPath)
|
|
||||||
return []string{}, nil
|
|
||||||
} else if pathErr != nil {
|
|
||||||
return nil, fmt.Errorf("error checking path %s: %v", windowsPath, pathErr)
|
|
||||||
}
|
|
||||||
return []string{pathname}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mounter *SafeFormatAndMount) formatAndMountSensitive(source string, target string, fstype string, options []string, sensitiveOptions []string) error {
|
|
||||||
// Try to mount the disk
|
|
||||||
klog.V(4).Infof("Attempting to formatAndMount disk: %s %s %s", fstype, source, target)
|
|
||||||
|
|
||||||
if err := ValidateDiskNumber(source); err != nil {
|
|
||||||
klog.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 -UseMaximumSize | Format-Volume -FileSystem %s -Confirm:$false", source, fstype)
|
|
||||||
if output, err := mounter.Exec.Command("powershell", "/c", cmd).CombinedOutput(); err != nil {
|
|
||||||
return fmt.Errorf("diskMount: format disk failed, error: %v, output: %q", err, string(output))
|
|
||||||
}
|
|
||||||
klog.V(4).Infof("diskMount: Disk successfully formatted, disk: %q, fstype: %q", source, fstype)
|
|
||||||
|
|
||||||
volumeIds, err := listVolumesOnDisk(source)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
driverPath := volumeIds[0]
|
|
||||||
target = NormalizeWindowsPath(target)
|
|
||||||
output, err := mounter.Exec.Command("cmd", "/c", "mklink", "/D", target, driverPath).CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
klog.Errorf("mklink(%s, %s) failed: %v, output: %q", target, driverPath, err, string(output))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
klog.V(2).Infof("formatAndMount disk(%s) fstype(%s) on(%s) with output(%s) successfully", driverPath, fstype, target, string(output))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListVolumesOnDisk - returns back list of volumes(volumeIDs) in the disk (requested in diskID).
|
|
||||||
func listVolumesOnDisk(diskID string) (volumeIDs []string, err error) {
|
|
||||||
cmd := fmt.Sprintf("(Get-Disk -DeviceId %s | Get-Partition | Get-Volume).UniqueId", diskID)
|
|
||||||
output, err := exec.Command("powershell", "/c", cmd).CombinedOutput()
|
|
||||||
klog.V(4).Infof("listVolumesOnDisk id from %s: %s", diskID, string(output))
|
|
||||||
if err != nil {
|
|
||||||
return []string{}, fmt.Errorf("error list volumes on disk. cmd: %s, output: %s, error: %v", cmd, string(output), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
volumeIds := strings.Split(strings.TrimSpace(string(output)), "\r\n")
|
|
||||||
return volumeIds, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getAllParentLinks walks all symbolic links and return all the parent targets recursively
|
|
||||||
func getAllParentLinks(path string) ([]string, error) {
|
|
||||||
const maxIter = 255
|
|
||||||
links := []string{}
|
|
||||||
for {
|
|
||||||
links = append(links, path)
|
|
||||||
if len(links) > maxIter {
|
|
||||||
return links, fmt.Errorf("unexpected length of parent links: %v", links)
|
|
||||||
}
|
|
||||||
|
|
||||||
fi, err := os.Lstat(path)
|
|
||||||
if err != nil {
|
|
||||||
return links, fmt.Errorf("Lstat: %v", err)
|
|
||||||
}
|
|
||||||
if fi.Mode()&os.ModeSymlink == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
path, err = os.Readlink(path)
|
|
||||||
if err != nil {
|
|
||||||
return links, fmt.Errorf("Readlink error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return links, nil
|
|
||||||
}
|
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@ -1191,7 +1191,6 @@ k8s.io/utils/internal/third_party/forked/golang/net
|
|||||||
k8s.io/utils/io
|
k8s.io/utils/io
|
||||||
k8s.io/utils/keymutex
|
k8s.io/utils/keymutex
|
||||||
k8s.io/utils/lru
|
k8s.io/utils/lru
|
||||||
k8s.io/utils/mount
|
|
||||||
k8s.io/utils/net
|
k8s.io/utils/net
|
||||||
k8s.io/utils/nsenter
|
k8s.io/utils/nsenter
|
||||||
k8s.io/utils/path
|
k8s.io/utils/path
|
||||||
|
Loading…
Reference in New Issue
Block a user