k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/volume/local/local.go (about)

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package local
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"path/filepath"
    23  	"runtime"
    24  	"strings"
    25  
    26  	"k8s.io/klog/v2"
    27  
    28  	v1 "k8s.io/api/core/v1"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/types"
    31  	"k8s.io/client-go/tools/record"
    32  	"k8s.io/kubernetes/pkg/kubelet/events"
    33  	"k8s.io/kubernetes/pkg/volume"
    34  	"k8s.io/kubernetes/pkg/volume/util"
    35  	"k8s.io/kubernetes/pkg/volume/util/hostutil"
    36  	"k8s.io/kubernetes/pkg/volume/validation"
    37  	"k8s.io/mount-utils"
    38  	"k8s.io/utils/keymutex"
    39  	utilstrings "k8s.io/utils/strings"
    40  )
    41  
    42  const (
    43  	defaultFSType = "ext4"
    44  )
    45  
    46  // ProbeVolumePlugins is the primary entrypoint for volume plugins.
    47  func ProbeVolumePlugins() []volume.VolumePlugin {
    48  	return []volume.VolumePlugin{&localVolumePlugin{}}
    49  }
    50  
    51  type localVolumePlugin struct {
    52  	host        volume.VolumeHost
    53  	volumeLocks keymutex.KeyMutex
    54  	recorder    record.EventRecorder
    55  }
    56  
    57  var _ volume.VolumePlugin = &localVolumePlugin{}
    58  var _ volume.PersistentVolumePlugin = &localVolumePlugin{}
    59  var _ volume.BlockVolumePlugin = &localVolumePlugin{}
    60  var _ volume.NodeExpandableVolumePlugin = &localVolumePlugin{}
    61  
    62  const (
    63  	localVolumePluginName = "kubernetes.io/local-volume"
    64  )
    65  
    66  func (plugin *localVolumePlugin) Init(host volume.VolumeHost) error {
    67  	plugin.host = host
    68  	plugin.volumeLocks = keymutex.NewHashed(0)
    69  	plugin.recorder = host.GetEventRecorder()
    70  	return nil
    71  }
    72  
    73  func (plugin *localVolumePlugin) GetPluginName() string {
    74  	return localVolumePluginName
    75  }
    76  
    77  func (plugin *localVolumePlugin) GetVolumeName(spec *volume.Spec) (string, error) {
    78  	// This volume is only supported as a PersistentVolumeSource, so the PV name is unique
    79  	return spec.Name(), nil
    80  }
    81  
    82  func (plugin *localVolumePlugin) CanSupport(spec *volume.Spec) bool {
    83  	// This volume is only supported as a PersistentVolumeSource
    84  	return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Local != nil)
    85  }
    86  
    87  func (plugin *localVolumePlugin) RequiresRemount(spec *volume.Spec) bool {
    88  	return false
    89  }
    90  
    91  func (plugin *localVolumePlugin) SupportsMountOption() bool {
    92  	return true
    93  }
    94  
    95  func (plugin *localVolumePlugin) SupportsSELinuxContextMount(spec *volume.Spec) (bool, error) {
    96  	return false, nil
    97  }
    98  
    99  func (plugin *localVolumePlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
   100  	// The current meaning of AccessMode is how many nodes can attach to it, not how many pods can mount it
   101  	return []v1.PersistentVolumeAccessMode{
   102  		v1.ReadWriteOnce,
   103  	}
   104  }
   105  
   106  func getVolumeSource(spec *volume.Spec) (*v1.LocalVolumeSource, bool, error) {
   107  	if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Local != nil {
   108  		return spec.PersistentVolume.Spec.Local, spec.ReadOnly, nil
   109  	}
   110  
   111  	return nil, false, fmt.Errorf("Spec does not reference a Local volume type")
   112  }
   113  
   114  func (plugin *localVolumePlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
   115  	_, readOnly, err := getVolumeSource(spec)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	globalLocalPath, err := plugin.getGlobalLocalPath(spec)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	kvh, ok := plugin.host.(volume.KubeletVolumeHost)
   126  	if !ok {
   127  		return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
   128  	}
   129  
   130  	return &localVolumeMounter{
   131  		localVolume: &localVolume{
   132  			pod:             pod,
   133  			podUID:          pod.UID,
   134  			volName:         spec.Name(),
   135  			mounter:         plugin.host.GetMounter(plugin.GetPluginName()),
   136  			hostUtil:        kvh.GetHostUtil(),
   137  			plugin:          plugin,
   138  			globalPath:      globalLocalPath,
   139  			MetricsProvider: volume.NewMetricsStatFS(plugin.host.GetPodVolumeDir(pod.UID, utilstrings.EscapeQualifiedName(localVolumePluginName), spec.Name())),
   140  		},
   141  		mountOptions: util.MountOptionFromSpec(spec),
   142  		readOnly:     readOnly,
   143  	}, nil
   144  
   145  }
   146  
   147  func (plugin *localVolumePlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
   148  	return &localVolumeUnmounter{
   149  		localVolume: &localVolume{
   150  			podUID:  podUID,
   151  			volName: volName,
   152  			mounter: plugin.host.GetMounter(plugin.GetPluginName()),
   153  			plugin:  plugin,
   154  		},
   155  	}, nil
   156  }
   157  
   158  func (plugin *localVolumePlugin) NewBlockVolumeMapper(spec *volume.Spec, pod *v1.Pod,
   159  	_ volume.VolumeOptions) (volume.BlockVolumeMapper, error) {
   160  	volumeSource, readOnly, err := getVolumeSource(spec)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  
   165  	mapper := &localVolumeMapper{
   166  		localVolume: &localVolume{
   167  			podUID:     pod.UID,
   168  			volName:    spec.Name(),
   169  			globalPath: volumeSource.Path,
   170  			plugin:     plugin,
   171  		},
   172  		readOnly: readOnly,
   173  	}
   174  
   175  	blockPath, err := mapper.GetGlobalMapPath(spec)
   176  	if err != nil {
   177  		return nil, fmt.Errorf("failed to get device path: %v", err)
   178  	}
   179  	mapper.MetricsProvider = volume.NewMetricsBlock(filepath.Join(blockPath, string(pod.UID)))
   180  
   181  	return mapper, nil
   182  }
   183  
   184  func (plugin *localVolumePlugin) NewBlockVolumeUnmapper(volName string,
   185  	podUID types.UID) (volume.BlockVolumeUnmapper, error) {
   186  	return &localVolumeUnmapper{
   187  		localVolume: &localVolume{
   188  			podUID:  podUID,
   189  			volName: volName,
   190  			plugin:  plugin,
   191  		},
   192  	}, nil
   193  }
   194  
   195  // TODO: check if no path and no topology constraints are ok
   196  func (plugin *localVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (volume.ReconstructedVolume, error) {
   197  	fs := v1.PersistentVolumeFilesystem
   198  	// The main purpose of reconstructed volume is to clean unused mount points
   199  	// and directories.
   200  	// For filesystem volume with directory source, no global mount path is
   201  	// needed to clean. Empty path is ok.
   202  	// For filesystem volume with block source, we should resolve to its device
   203  	// path if global mount path exists.
   204  	var path string
   205  	mounter := plugin.host.GetMounter(plugin.GetPluginName())
   206  	refs, err := mounter.GetMountRefs(mountPath)
   207  	if err != nil {
   208  		return volume.ReconstructedVolume{}, err
   209  	}
   210  	baseMountPath := plugin.generateBlockDeviceBaseGlobalPath()
   211  	for _, ref := range refs {
   212  		if mount.PathWithinBase(ref, baseMountPath) {
   213  			// If the global mount for block device exists, the source is block
   214  			// device.
   215  			// The resolved device path may not be the exact same as path in
   216  			// local PV object if symbolic link is used. However, it's the true
   217  			// source and can be used in reconstructed volume.
   218  			path, _, err = mount.GetDeviceNameFromMount(mounter, ref)
   219  			if err != nil {
   220  				return volume.ReconstructedVolume{}, err
   221  			}
   222  			klog.V(4).Infof("local: reconstructing volume %q (pod volume mount: %q) with device %q", volumeName, mountPath, path)
   223  			break
   224  		}
   225  	}
   226  	localVolume := &v1.PersistentVolume{
   227  		ObjectMeta: metav1.ObjectMeta{
   228  			Name: volumeName,
   229  		},
   230  		Spec: v1.PersistentVolumeSpec{
   231  			PersistentVolumeSource: v1.PersistentVolumeSource{
   232  				Local: &v1.LocalVolumeSource{
   233  					Path: path,
   234  				},
   235  			},
   236  			VolumeMode: &fs,
   237  		},
   238  	}
   239  	return volume.ReconstructedVolume{
   240  		Spec: volume.NewSpecFromPersistentVolume(localVolume, false),
   241  	}, nil
   242  }
   243  
   244  func (plugin *localVolumePlugin) ConstructBlockVolumeSpec(podUID types.UID, volumeName,
   245  	mapPath string) (*volume.Spec, error) {
   246  	block := v1.PersistentVolumeBlock
   247  
   248  	localVolume := &v1.PersistentVolume{
   249  		ObjectMeta: metav1.ObjectMeta{
   250  			Name: volumeName,
   251  		},
   252  		Spec: v1.PersistentVolumeSpec{
   253  			PersistentVolumeSource: v1.PersistentVolumeSource{
   254  				Local: &v1.LocalVolumeSource{
   255  					// Not needed because we don't need to detach local device from the host.
   256  					Path: "",
   257  				},
   258  			},
   259  			VolumeMode: &block,
   260  		},
   261  	}
   262  
   263  	return volume.NewSpecFromPersistentVolume(localVolume, false), nil
   264  }
   265  
   266  func (plugin *localVolumePlugin) generateBlockDeviceBaseGlobalPath() string {
   267  	return filepath.Join(plugin.host.GetPluginDir(localVolumePluginName), util.MountsInGlobalPDPath)
   268  }
   269  
   270  func (plugin *localVolumePlugin) getGlobalLocalPath(spec *volume.Spec) (string, error) {
   271  	if spec.PersistentVolume.Spec.Local == nil || len(spec.PersistentVolume.Spec.Local.Path) == 0 {
   272  		return "", fmt.Errorf("local volume source is nil or local path is not set")
   273  	}
   274  
   275  	kvh, ok := plugin.host.(volume.KubeletVolumeHost)
   276  	if !ok {
   277  		return "", fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
   278  	}
   279  
   280  	fileType, err := kvh.GetHostUtil().GetFileType(spec.PersistentVolume.Spec.Local.Path)
   281  	if err != nil {
   282  		return "", err
   283  	}
   284  	switch fileType {
   285  	case hostutil.FileTypeDirectory:
   286  		return spec.PersistentVolume.Spec.Local.Path, nil
   287  	case hostutil.FileTypeBlockDev:
   288  		return filepath.Join(plugin.generateBlockDeviceBaseGlobalPath(), spec.Name()), nil
   289  	default:
   290  		return "", fmt.Errorf("only directory and block device are supported")
   291  	}
   292  }
   293  
   294  var _ volume.DeviceMountableVolumePlugin = &localVolumePlugin{}
   295  
   296  type deviceMounter struct {
   297  	plugin   *localVolumePlugin
   298  	mounter  *mount.SafeFormatAndMount
   299  	hostUtil hostutil.HostUtils
   300  }
   301  
   302  var _ volume.DeviceMounter = &deviceMounter{}
   303  
   304  func (plugin *localVolumePlugin) CanDeviceMount(spec *volume.Spec) (bool, error) {
   305  	return true, nil
   306  }
   307  
   308  func (plugin *localVolumePlugin) NewDeviceMounter() (volume.DeviceMounter, error) {
   309  	kvh, ok := plugin.host.(volume.KubeletVolumeHost)
   310  	if !ok {
   311  		return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
   312  	}
   313  	return &deviceMounter{
   314  		plugin:   plugin,
   315  		mounter:  util.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host),
   316  		hostUtil: kvh.GetHostUtil(),
   317  	}, nil
   318  }
   319  
   320  func (dm *deviceMounter) mountLocalBlockDevice(spec *volume.Spec, devicePath string, deviceMountPath string) error {
   321  	klog.V(4).Infof("local: mounting device %s to %s", devicePath, deviceMountPath)
   322  	notMnt, err := dm.mounter.IsLikelyNotMountPoint(deviceMountPath)
   323  	if err != nil {
   324  		if os.IsNotExist(err) {
   325  			if err := os.MkdirAll(deviceMountPath, 0750); err != nil {
   326  				return err
   327  			}
   328  			notMnt = true
   329  		} else {
   330  			return err
   331  		}
   332  	}
   333  	if !notMnt {
   334  		return nil
   335  	}
   336  	fstype, err := getVolumeSourceFSType(spec)
   337  	if err != nil {
   338  		return err
   339  	}
   340  
   341  	ro, err := getVolumeSourceReadOnly(spec)
   342  	if err != nil {
   343  		return err
   344  	}
   345  	options := []string{}
   346  	if ro {
   347  		options = append(options, "ro")
   348  	}
   349  	mountOptions := util.MountOptionFromSpec(spec, options...)
   350  	err = dm.mounter.FormatAndMount(devicePath, deviceMountPath, fstype, mountOptions)
   351  	if err != nil {
   352  		if rmErr := os.Remove(deviceMountPath); rmErr != nil {
   353  			klog.Warningf("local: failed to remove %s: %v", deviceMountPath, rmErr)
   354  		}
   355  		return fmt.Errorf("local: failed to mount device %s at %s (fstype: %s), error %w", devicePath, deviceMountPath, fstype, err)
   356  	}
   357  	klog.V(3).Infof("local: successfully mount device %s at %s (fstype: %s)", devicePath, deviceMountPath, fstype)
   358  	return nil
   359  }
   360  
   361  func (dm *deviceMounter) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string, _ volume.DeviceMounterArgs) error {
   362  	if spec.PersistentVolume.Spec.Local == nil || len(spec.PersistentVolume.Spec.Local.Path) == 0 {
   363  		return fmt.Errorf("local volume source is nil or local path is not set")
   364  	}
   365  	fileType, err := dm.hostUtil.GetFileType(spec.PersistentVolume.Spec.Local.Path)
   366  	if err != nil {
   367  		return err
   368  	}
   369  
   370  	switch fileType {
   371  	case hostutil.FileTypeBlockDev:
   372  		// local volume plugin does not implement AttachableVolumePlugin interface, so set devicePath to Path in PV spec directly
   373  		return dm.mountLocalBlockDevice(spec, spec.PersistentVolume.Spec.Local.Path, deviceMountPath)
   374  	case hostutil.FileTypeDirectory:
   375  		// if the given local volume path is of already filesystem directory, return directly
   376  		return nil
   377  	default:
   378  		return fmt.Errorf("only directory and block device are supported")
   379  	}
   380  }
   381  
   382  func (plugin *localVolumePlugin) RequiresFSResize() bool {
   383  	return true
   384  }
   385  
   386  func (plugin *localVolumePlugin) NodeExpand(resizeOptions volume.NodeResizeOptions) (bool, error) {
   387  	fsVolume, err := util.CheckVolumeModeFilesystem(resizeOptions.VolumeSpec)
   388  	if err != nil {
   389  		return false, fmt.Errorf("error checking VolumeMode: %v", err)
   390  	}
   391  	if !fsVolume {
   392  		return true, nil
   393  	}
   394  
   395  	localDevicePath := resizeOptions.VolumeSpec.PersistentVolume.Spec.Local.Path
   396  
   397  	kvh, ok := plugin.host.(volume.KubeletVolumeHost)
   398  	if !ok {
   399  		return false, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
   400  	}
   401  
   402  	fileType, err := kvh.GetHostUtil().GetFileType(localDevicePath)
   403  	if err != nil {
   404  		return false, err
   405  	}
   406  
   407  	switch fileType {
   408  	case hostutil.FileTypeBlockDev:
   409  		_, err = util.GenericResizeFS(plugin.host, plugin.GetPluginName(), localDevicePath, resizeOptions.DeviceMountPath)
   410  		if err != nil {
   411  			return false, err
   412  		}
   413  		return true, nil
   414  	case hostutil.FileTypeDirectory:
   415  		// if the given local volume path is of already filesystem directory, return directly because
   416  		// we do not want to prevent mount operation from succeeding.
   417  		klog.InfoS("Expansion of directory based local volumes is NO-OP", "localVolumePath", localDevicePath)
   418  		return true, nil
   419  	default:
   420  		return false, fmt.Errorf("only directory and block device are supported")
   421  	}
   422  }
   423  
   424  func getVolumeSourceFSType(spec *volume.Spec) (string, error) {
   425  	if spec.PersistentVolume != nil &&
   426  		spec.PersistentVolume.Spec.Local != nil {
   427  		if spec.PersistentVolume.Spec.Local.FSType != nil {
   428  			return *spec.PersistentVolume.Spec.Local.FSType, nil
   429  		}
   430  		// if the FSType is not set in local PV spec, setting it to default ("ext4")
   431  		return defaultFSType, nil
   432  	}
   433  
   434  	return "", fmt.Errorf("spec does not reference a Local volume type")
   435  }
   436  
   437  func getVolumeSourceReadOnly(spec *volume.Spec) (bool, error) {
   438  	if spec.PersistentVolume != nil &&
   439  		spec.PersistentVolume.Spec.Local != nil {
   440  		// local volumes used as a PersistentVolume gets the ReadOnly flag indirectly through
   441  		// the persistent-claim volume used to mount the PV
   442  		return spec.ReadOnly, nil
   443  	}
   444  
   445  	return false, fmt.Errorf("spec does not reference a Local volume type")
   446  }
   447  
   448  func (dm *deviceMounter) GetDeviceMountPath(spec *volume.Spec) (string, error) {
   449  	return dm.plugin.getGlobalLocalPath(spec)
   450  }
   451  
   452  func (plugin *localVolumePlugin) NewDeviceUnmounter() (volume.DeviceUnmounter, error) {
   453  	return &deviceMounter{
   454  		plugin:  plugin,
   455  		mounter: util.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host),
   456  	}, nil
   457  }
   458  
   459  func (plugin *localVolumePlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) {
   460  	mounter := plugin.host.GetMounter(plugin.GetPluginName())
   461  	return mounter.GetMountRefs(deviceMountPath)
   462  }
   463  
   464  var _ volume.DeviceUnmounter = &deviceMounter{}
   465  
   466  func (dm *deviceMounter) UnmountDevice(deviceMountPath string) error {
   467  	// If the local PV is a block device,
   468  	// The deviceMountPath is generated to the format like :/var/lib/kubelet/plugins/kubernetes.io/local-volume/mounts/localpv.spec.Name;
   469  	// If it is a filesystem directory, then the deviceMountPath is set directly to pvSpec.Local.Path
   470  	// We only need to unmount block device here, so we need to check if the deviceMountPath passed here
   471  	// has base mount path: /var/lib/kubelet/plugins/kubernetes.io/local-volume/mounts
   472  	basemountPath := dm.plugin.generateBlockDeviceBaseGlobalPath()
   473  	if mount.PathWithinBase(deviceMountPath, basemountPath) {
   474  		return mount.CleanupMountPoint(deviceMountPath, dm.mounter, false)
   475  	}
   476  
   477  	return nil
   478  }
   479  
   480  // Local volumes represent a local directory on a node.
   481  // The directory at the globalPath will be bind-mounted to the pod's directory
   482  type localVolume struct {
   483  	volName string
   484  	pod     *v1.Pod
   485  	podUID  types.UID
   486  	// Global path to the volume
   487  	globalPath string
   488  	// Mounter interface that provides system calls to mount the global path to the pod local path.
   489  	mounter  mount.Interface
   490  	hostUtil hostutil.HostUtils
   491  	plugin   *localVolumePlugin
   492  	volume.MetricsProvider
   493  }
   494  
   495  func (l *localVolume) GetPath() string {
   496  	return l.plugin.host.GetPodVolumeDir(l.podUID, utilstrings.EscapeQualifiedName(localVolumePluginName), l.volName)
   497  }
   498  
   499  type localVolumeMounter struct {
   500  	*localVolume
   501  	readOnly     bool
   502  	mountOptions []string
   503  }
   504  
   505  var _ volume.Mounter = &localVolumeMounter{}
   506  
   507  func (m *localVolumeMounter) GetAttributes() volume.Attributes {
   508  	return volume.Attributes{
   509  		ReadOnly:       m.readOnly,
   510  		Managed:        !m.readOnly,
   511  		SELinuxRelabel: true,
   512  	}
   513  }
   514  
   515  // SetUp bind mounts the directory to the volume path
   516  func (m *localVolumeMounter) SetUp(mounterArgs volume.MounterArgs) error {
   517  	return m.SetUpAt(m.GetPath(), mounterArgs)
   518  }
   519  
   520  // SetUpAt bind mounts the directory to the volume path and sets up volume ownership
   521  func (m *localVolumeMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
   522  	m.plugin.volumeLocks.LockKey(m.globalPath)
   523  	defer m.plugin.volumeLocks.UnlockKey(m.globalPath)
   524  
   525  	if m.globalPath == "" {
   526  		return fmt.Errorf("LocalVolume volume %q path is empty", m.volName)
   527  	}
   528  
   529  	err := validation.ValidatePathNoBacksteps(m.globalPath)
   530  	if err != nil {
   531  		return fmt.Errorf("invalid path: %s %v", m.globalPath, err)
   532  	}
   533  
   534  	notMnt, err := mount.IsNotMountPoint(m.mounter, dir)
   535  	klog.V(4).Infof("LocalVolume mount setup: PodDir(%s) VolDir(%s) Mounted(%t) Error(%v), ReadOnly(%t)", dir, m.globalPath, !notMnt, err, m.readOnly)
   536  	if err != nil && !os.IsNotExist(err) {
   537  		klog.Errorf("cannot validate mount point: %s %v", dir, err)
   538  		return err
   539  	}
   540  
   541  	if !notMnt {
   542  		return nil
   543  	}
   544  	refs, err := m.mounter.GetMountRefs(m.globalPath)
   545  	if mounterArgs.FsGroup != nil {
   546  		if err != nil {
   547  			klog.Errorf("cannot collect mounting information: %s %v", m.globalPath, err)
   548  			return err
   549  		}
   550  
   551  		// Only count mounts from other pods
   552  		refs = m.filterPodMounts(refs)
   553  		if len(refs) > 0 {
   554  			fsGroupNew := int64(*mounterArgs.FsGroup)
   555  			_, fsGroupOld, err := m.hostUtil.GetOwner(m.globalPath)
   556  			if err != nil {
   557  				return fmt.Errorf("failed to check fsGroup for %s (%v)", m.globalPath, err)
   558  			}
   559  			if fsGroupNew != fsGroupOld {
   560  				m.plugin.recorder.Eventf(m.pod, v1.EventTypeWarning, events.WarnAlreadyMountedVolume, "The requested fsGroup is %d, but the volume %s has GID %d. The volume may not be shareable.", fsGroupNew, m.volName, fsGroupOld)
   561  			}
   562  		}
   563  
   564  	}
   565  
   566  	if runtime.GOOS != "windows" {
   567  		// skip below MkdirAll for windows since the "bind mount" logic is implemented differently in mount_wiondows.go
   568  		if err := os.MkdirAll(dir, 0750); err != nil {
   569  			klog.Errorf("mkdir failed on disk %s (%v)", dir, err)
   570  			return err
   571  		}
   572  	}
   573  	// Perform a bind mount to the full path to allow duplicate mounts of the same volume.
   574  	options := []string{"bind"}
   575  	if m.readOnly {
   576  		options = append(options, "ro")
   577  	}
   578  	mountOptions := util.JoinMountOptions(options, m.mountOptions)
   579  
   580  	klog.V(4).Infof("attempting to mount %s", dir)
   581  	globalPath := util.MakeAbsolutePath(runtime.GOOS, m.globalPath)
   582  	err = m.mounter.MountSensitiveWithoutSystemd(globalPath, dir, "", mountOptions, nil)
   583  	if err != nil {
   584  		klog.Errorf("Mount of volume %s failed: %v", dir, err)
   585  		notMnt, mntErr := mount.IsNotMountPoint(m.mounter, dir)
   586  		if mntErr != nil {
   587  			klog.Errorf("IsNotMountPoint check failed: %v", mntErr)
   588  			return err
   589  		}
   590  		if !notMnt {
   591  			if mntErr = m.mounter.Unmount(dir); mntErr != nil {
   592  				klog.Errorf("Failed to unmount: %v", mntErr)
   593  				return err
   594  			}
   595  			notMnt, mntErr = mount.IsNotMountPoint(m.mounter, dir)
   596  			if mntErr != nil {
   597  				klog.Errorf("IsNotMountPoint check failed: %v", mntErr)
   598  				return err
   599  			}
   600  			if !notMnt {
   601  				// This is very odd, we don't expect it.  We'll try again next sync loop.
   602  				klog.Errorf("%s is still mounted, despite call to unmount().  Will try again next sync loop.", dir)
   603  				return err
   604  			}
   605  		}
   606  		if rmErr := os.Remove(dir); rmErr != nil {
   607  			klog.Warningf("failed to remove %s: %v", dir, rmErr)
   608  		}
   609  		return err
   610  	}
   611  	if !m.readOnly {
   612  		// Volume owner will be written only once on the first volume mount
   613  		if len(refs) == 0 {
   614  			return volume.SetVolumeOwnership(m, dir, mounterArgs.FsGroup, mounterArgs.FSGroupChangePolicy, util.FSGroupCompleteHook(m.plugin, nil))
   615  		}
   616  	}
   617  	return nil
   618  }
   619  
   620  // filterPodMounts only returns mount paths inside the kubelet pod directory
   621  func (m *localVolumeMounter) filterPodMounts(refs []string) []string {
   622  	filtered := []string{}
   623  	for _, r := range refs {
   624  		if strings.HasPrefix(r, m.plugin.host.GetPodsDir()+string(os.PathSeparator)) {
   625  			filtered = append(filtered, r)
   626  		}
   627  	}
   628  	return filtered
   629  }
   630  
   631  type localVolumeUnmounter struct {
   632  	*localVolume
   633  }
   634  
   635  var _ volume.Unmounter = &localVolumeUnmounter{}
   636  
   637  // TearDown unmounts the bind mount
   638  func (u *localVolumeUnmounter) TearDown() error {
   639  	return u.TearDownAt(u.GetPath())
   640  }
   641  
   642  // TearDownAt unmounts the bind mount
   643  func (u *localVolumeUnmounter) TearDownAt(dir string) error {
   644  	klog.V(4).Infof("Unmounting volume %q at path %q\n", u.volName, dir)
   645  	return mount.CleanupMountPoint(dir, u.mounter, true) /* extensiveMountPointCheck = true */
   646  }
   647  
   648  // localVolumeMapper implements the BlockVolumeMapper interface for local volumes.
   649  type localVolumeMapper struct {
   650  	*localVolume
   651  	readOnly bool
   652  }
   653  
   654  var _ volume.BlockVolumeMapper = &localVolumeMapper{}
   655  var _ volume.CustomBlockVolumeMapper = &localVolumeMapper{}
   656  
   657  // SetUpDevice prepares the volume to the node by the plugin specific way.
   658  func (m *localVolumeMapper) SetUpDevice() (string, error) {
   659  	return "", nil
   660  }
   661  
   662  // MapPodDevice provides physical device path for the local PV.
   663  func (m *localVolumeMapper) MapPodDevice() (string, error) {
   664  	globalPath := util.MakeAbsolutePath(runtime.GOOS, m.globalPath)
   665  	klog.V(4).Infof("MapPodDevice returning path %s", globalPath)
   666  	return globalPath, nil
   667  }
   668  
   669  // GetStagingPath returns
   670  func (m *localVolumeMapper) GetStagingPath() string {
   671  	return ""
   672  }
   673  
   674  // SupportsMetrics returns true for SupportsMetrics as it initializes the
   675  // MetricsProvider.
   676  func (m *localVolumeMapper) SupportsMetrics() bool {
   677  	return true
   678  }
   679  
   680  // localVolumeUnmapper implements the BlockVolumeUnmapper interface for local volumes.
   681  type localVolumeUnmapper struct {
   682  	*localVolume
   683  	volume.MetricsNil
   684  }
   685  
   686  var _ volume.BlockVolumeUnmapper = &localVolumeUnmapper{}
   687  
   688  // GetGlobalMapPath returns global map path and error.
   689  // path: plugins/kubernetes.io/kubernetes.io/local-volume/volumeDevices/{volumeName}
   690  func (l *localVolume) GetGlobalMapPath(spec *volume.Spec) (string, error) {
   691  	return filepath.Join(l.plugin.host.GetVolumeDevicePluginDir(utilstrings.EscapeQualifiedName(localVolumePluginName)),
   692  		l.volName), nil
   693  }
   694  
   695  // GetPodDeviceMapPath returns pod device map path and volume name.
   696  // path: pods/{podUid}/volumeDevices/kubernetes.io~local-volume
   697  // volName: local-pv-ff0d6d4
   698  func (l *localVolume) GetPodDeviceMapPath() (string, string) {
   699  	return l.plugin.host.GetPodVolumeDeviceDir(l.podUID,
   700  		utilstrings.EscapeQualifiedName(localVolumePluginName)), l.volName
   701  }