Skip to content

Commit 30a9024

Browse files
authored
Merge pull request #1849 from kubernetes-sigs/remove-smb-mount-win-1.28
[release-1.28] fix: remove SMBGlobalMapping on Windows node
2 parents 83b1640 + 9531af6 commit 30a9024

10 files changed

+160
-19
lines changed

pkg/azurefile/azure_common_darwin.go

+4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ func SMBMount(m *mount.SafeFormatAndMount, source, target, fsType string, option
3131
return nil
3232
}
3333

34+
func SMBUnmount(m *mount.SafeFormatAndMount, target string, _, _ bool) error {
35+
return nil
36+
}
37+
3438
func CleanupMountPoint(m *mount.SafeFormatAndMount, target string, extensiveMountCheck bool) error {
3539
return nil
3640
}

pkg/azurefile/azure_common_linux.go

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ func SMBMount(m *mount.SafeFormatAndMount, source, target, fsType string, option
3333
return m.MountSensitive(source, target, fsType, options, sensitiveMountOptions)
3434
}
3535

36+
func SMBUnmount(m *mount.SafeFormatAndMount, target string, _, _ bool) error {
37+
return mount.CleanupMountPoint(target, m.Interface, true /*extensiveMountPointCheck*/)
38+
}
39+
3640
func CleanupMountPoint(m *mount.SafeFormatAndMount, target string, _ bool) error {
3741
return mount.CleanupMountPoint(target, m.Interface, true /*extensiveMountPointCheck*/)
3842
}

pkg/azurefile/azure_common_windows.go

+10
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ func SMBMount(m *mount.SafeFormatAndMount, source, target, fsType string, mountO
3939
return fmt.Errorf("could not cast to csi proxy class")
4040
}
4141

42+
func SMBUnmount(m *mount.SafeFormatAndMount, target string, extensiveMountCheck, removeSMBMountOnWindows bool) error {
43+
if proxy, ok := m.Interface.(mounter.CSIProxyMounter); ok {
44+
if removeSMBMountOnWindows {
45+
return proxy.Unmount(target)
46+
}
47+
return proxy.Rmdir(target)
48+
}
49+
return fmt.Errorf("could not cast to csi proxy class")
50+
}
51+
4252
func CleanupMountPoint(m *mount.SafeFormatAndMount, target string, extensiveMountCheck bool) error {
4353
if proxy, ok := m.Interface.(mounter.CSIProxyMounter); ok {
4454
return proxy.Rmdir(target)

pkg/azurefile/azurefile.go

+3
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ type DriverOptions struct {
213213
KubeAPIQPS float64
214214
KubeAPIBurst int
215215
EnableWindowsHostProcess bool
216+
RemoveSMBMountOnWindows bool
216217
AppendClosetimeoOption bool
217218
AppendNoShareSockOption bool
218219
AppendNoResvPortOption bool
@@ -242,6 +243,7 @@ type Driver struct {
242243
kubeAPIQPS float64
243244
kubeAPIBurst int
244245
enableWindowsHostProcess bool
246+
removeSMBMountOnWindows bool
245247
appendClosetimeoOption bool
246248
appendNoShareSockOption bool
247249
appendNoResvPortOption bool
@@ -300,6 +302,7 @@ func NewDriver(options *DriverOptions) *Driver {
300302
driver.kubeAPIQPS = options.KubeAPIQPS
301303
driver.kubeAPIBurst = options.KubeAPIBurst
302304
driver.enableWindowsHostProcess = options.EnableWindowsHostProcess
305+
driver.removeSMBMountOnWindows = options.RemoveSMBMountOnWindows
303306
driver.appendClosetimeoOption = options.AppendClosetimeoOption
304307
driver.appendNoShareSockOption = options.AppendNoShareSockOption
305308
driver.appendNoResvPortOption = options.AppendNoResvPortOption

pkg/azurefile/nodeserver.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -402,15 +402,17 @@ func (d *Driver) NodeUnstageVolume(_ context.Context, req *csi.NodeUnstageVolume
402402
mc.ObserveOperationWithResult(isOperationSucceeded, VolumeID, volumeID)
403403
}()
404404

405-
klog.V(2).Infof("NodeUnstageVolume: CleanupMountPoint volume %s on %s", volumeID, stagingTargetPath)
406-
if err := CleanupMountPoint(d.mounter, stagingTargetPath, true /*extensiveMountPointCheck*/); err != nil {
405+
klog.V(2).Infof("NodeUnstageVolume: unmount volume %s on %s", volumeID, stagingTargetPath)
406+
if err := SMBUnmount(d.mounter, stagingTargetPath, true /*extensiveMountPointCheck*/, d.removeSMBMountOnWindows); err != nil {
407407
return nil, status.Errorf(codes.Internal, "failed to unmount staging target %s: %v", stagingTargetPath, err)
408408
}
409409

410-
targetPath := filepath.Join(filepath.Dir(stagingTargetPath), proxyMount)
411-
klog.V(2).Infof("NodeUnstageVolume: CleanupMountPoint volume %s on %s", volumeID, targetPath)
412-
if err := CleanupMountPoint(d.mounter, targetPath, false); err != nil {
413-
return nil, status.Errorf(codes.Internal, "failed to unmount staging target %s: %v", targetPath, err)
410+
if runtime.GOOS != "windows" {
411+
targetPath := filepath.Join(filepath.Dir(stagingTargetPath), proxyMount)
412+
klog.V(2).Infof("NodeUnstageVolume: CleanupMountPoint volume %s on %s", volumeID, targetPath)
413+
if err := CleanupMountPoint(d.mounter, targetPath, false); err != nil {
414+
return nil, status.Errorf(codes.Internal, "failed to unmount staging target %s: %v", targetPath, err)
415+
}
414416
}
415417
klog.V(2).Infof("NodeUnstageVolume: unmount volume %s on %s successfully", volumeID, stagingTargetPath)
416418

pkg/azurefileplugin/main.go

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ var (
5757
kubeAPIBurst = flag.Int("kube-api-burst", 50, "Burst to use while communicating with the kubernetes apiserver.")
5858
appendMountErrorHelpLink = flag.Bool("append-mount-error-help-link", true, "Whether to include a link for help with mount errors when a mount error occurs.")
5959
enableWindowsHostProcess = flag.Bool("enable-windows-host-process", false, "enable windows host process")
60+
removeSMBMountOnWindows = flag.Bool("remove-smb-mount-on-windows", true, "remove smb global mapping on windows during unmount")
6061
appendClosetimeoOption = flag.Bool("append-closetimeo-option", false, "Whether appending closetimeo=0 option to smb mount command")
6162
appendNoShareSockOption = flag.Bool("append-nosharesock-option", true, "Whether appending nosharesock option to smb mount command")
6263
appendNoResvPortOption = flag.Bool("append-noresvport-option", true, "Whether appending noresvport option to nfs mount command")
@@ -107,6 +108,7 @@ func handle() {
107108
KubeAPIQPS: *kubeAPIQPS,
108109
KubeAPIBurst: *kubeAPIBurst,
109110
EnableWindowsHostProcess: *enableWindowsHostProcess,
111+
RemoveSMBMountOnWindows: *removeSMBMountOnWindows,
110112
AppendClosetimeoOption: *appendClosetimeoOption,
111113
AppendNoShareSockOption: *appendNoShareSockOption,
112114
AppendNoResvPortOption: *appendNoResvPortOption,

pkg/mounter/safe_mounter_host_process_windows.go

+24-6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import (
3333
"sigs.k8s.io/azurefile-csi-driver/pkg/os/smb"
3434
)
3535

36+
var driverGlobalMountPath = "C:\\var\\lib\\kubelet\\plugins\\kubernetes.io\\csi\\file.csi.azure.com"
37+
3638
var _ CSIProxyMounter = &winMounter{}
3739

3840
type winMounter struct{}
@@ -125,11 +127,6 @@ func (mounter *winMounter) SMBMount(source, target, fsType string, mountOptions,
125127
return nil
126128
}
127129

128-
func (mounter *winMounter) SMBUnmount(target string) error {
129-
klog.V(4).Infof("SMBUnmount: local path: %s", target)
130-
return mounter.Rmdir(target)
131-
}
132-
133130
// Mount just creates a soft link at target pointing to source.
134131
func (mounter *winMounter) Mount(source, target, fstype string, options []string) error {
135132
return filesystem.LinkPath(normalizeWindowsPath(source), normalizeWindowsPath(target))
@@ -142,7 +139,28 @@ func (mounter *winMounter) Rmdir(path string) error {
142139

143140
// Unmount - Removes the directory - equivalent to unmount on Linux.
144141
func (mounter *winMounter) Unmount(target string) error {
145-
klog.V(4).Infof("Unmount: %s", target)
142+
target = normalizeWindowsPath(target)
143+
remoteServer, err := smb.GetRemoteServerFromTarget(target)
144+
if err == nil {
145+
klog.V(2).Infof("remote server path: %s, local path: %s", remoteServer, target)
146+
hasDupSMBMount, err := smb.CheckForDuplicateSMBMounts(driverGlobalMountPath, target, remoteServer)
147+
if err == nil {
148+
if !hasDupSMBMount {
149+
remoteServer = strings.Replace(remoteServer, "UNC\\", "\\\\", 1)
150+
if err := smb.RemoveSmbGlobalMapping(remoteServer); err != nil {
151+
klog.Errorf("RemoveSmbGlobalMapping(%s) failed with %v", target, err)
152+
}
153+
} else {
154+
klog.V(2).Infof("skip unmount as there are other SMB mounts on the same remote server %s", remoteServer)
155+
}
156+
} else {
157+
klog.Errorf("CheckForDuplicateSMBMounts(%s, %s) failed with %v", target, remoteServer, err)
158+
}
159+
} else {
160+
klog.Errorf("GetRemoteServerFromTarget(%s) failed with %v", target, err)
161+
}
162+
163+
klog.V(2).Infof("Unmount: remote path: %s local path: %s", remoteServer, target)
146164
return mounter.Rmdir(target)
147165
}
148166

pkg/mounter/safe_mounter_v1beta_windows.go

-7
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,6 @@ func (mounter *csiProxyMounterV1Beta) SMBMount(source, target, fsType string, mo
9191
return nil
9292
}
9393

94-
func (mounter *csiProxyMounterV1Beta) SMBUnmount(target string) error {
95-
klog.V(4).Infof("SMBUnmount: local path: %s", target)
96-
// TODO: We need to remove the SMB mapping. The change to remove the
97-
// directory brings the CSI code in parity with the in-tree.
98-
return mounter.Rmdir(target)
99-
}
100-
10194
// Mount just creates a soft link at target pointing to source.
10295
func (mounter *csiProxyMounterV1Beta) Mount(source string, target string, fstype string, options []string) error {
10396
klog.V(4).Infof("Mount: old name: %s. new name: %s", source, target)

pkg/os/smb/smb.go

+46
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package smb
1818

1919
import (
2020
"fmt"
21+
"os"
22+
"path/filepath"
2123
"strings"
2224

2325
"k8s.io/klog/v2"
@@ -79,10 +81,54 @@ func NewSmbGlobalMapping(remotePath, username, password string) error {
7981
}
8082

8183
func RemoveSmbGlobalMapping(remotePath string) error {
84+
remotePath = strings.TrimSuffix(remotePath, `\`)
8285
cmd := `Remove-SmbGlobalMapping -RemotePath $Env:smbremotepath -Force`
8386
klog.V(2).Infof("begin to run RemoveSmbGlobalMapping with %s", remotePath)
8487
if output, err := util.RunPowershellCmd(cmd, fmt.Sprintf("smbremotepath=%s", remotePath)); err != nil {
8588
return fmt.Errorf("UnmountSmbShare failed. output: %q, err: %v", string(output), err)
8689
}
8790
return nil
8891
}
92+
93+
// GetRemoteServerFromTarget- gets the remote server path given a mount point, the function is recursive until it find the remote server or errors out
94+
func GetRemoteServerFromTarget(mount string) (string, error) {
95+
cmd := "(Get-Item -Path $Env:mount).Target"
96+
out, err := util.RunPowershellCmd(cmd, fmt.Sprintf("mount=%s", mount))
97+
if err != nil || len(out) == 0 {
98+
return "", fmt.Errorf("error getting volume from mount. cmd: %s, output: %s, error: %v", cmd, string(out), err)
99+
}
100+
return strings.TrimSpace(string(out)), nil
101+
}
102+
103+
// CheckForDuplicateSMBMounts checks if there is any other SMB mount exists on the same remote server
104+
func CheckForDuplicateSMBMounts(dir, mount, remoteServer string) (bool, error) {
105+
files, err := os.ReadDir(dir)
106+
if err != nil {
107+
return false, err
108+
}
109+
110+
for _, file := range files {
111+
klog.V(6).Infof("checking file %s", file.Name())
112+
if file.IsDir() {
113+
globalMountPath := filepath.Join(dir, file.Name(), "globalmount")
114+
if strings.EqualFold(filepath.Clean(globalMountPath), filepath.Clean(mount)) {
115+
klog.V(2).Infof("skip current mount path %s", mount)
116+
} else {
117+
fileInfo, err := os.Lstat(globalMountPath)
118+
// check if the file is a symlink, if yes, check if it is pointing to the same remote server
119+
if err == nil && fileInfo.Mode()&os.ModeSymlink != 0 {
120+
remoteServerPath, err := GetRemoteServerFromTarget(globalMountPath)
121+
klog.V(2).Infof("checking remote server path %s on local path %s", remoteServerPath, globalMountPath)
122+
if err == nil {
123+
if remoteServerPath == remoteServer {
124+
return true, nil
125+
}
126+
} else {
127+
klog.Errorf("GetRemoteServerFromTarget(%s) failed with %v", globalMountPath, err)
128+
}
129+
}
130+
}
131+
}
132+
}
133+
return false, err
134+
}

pkg/os/smb/smb_test.go

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//go:build windows
2+
// +build windows
3+
4+
/*
5+
Copyright 2024 The Kubernetes Authors.
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
*/
19+
20+
package smb
21+
22+
import (
23+
"fmt"
24+
"testing"
25+
)
26+
27+
func TestCheckForDuplicateSMBMounts(t *testing.T) {
28+
tests := []struct {
29+
name string
30+
dir string
31+
mount string
32+
remoteServer string
33+
expectedResult bool
34+
expectedError error
35+
}{
36+
{
37+
name: "directory does not exist",
38+
dir: "non-existing-mount",
39+
expectedResult: false,
40+
expectedError: fmt.Errorf("open non-existing-mount: The system cannot find the file specified."),
41+
},
42+
}
43+
44+
for _, test := range tests {
45+
result, err := CheckForDuplicateSMBMounts(test.dir, test.mount, test.remoteServer)
46+
if result != test.expectedResult {
47+
t.Errorf("Expected %v, got %v", test.expectedResult, result)
48+
}
49+
if err == nil && test.expectedError != nil {
50+
t.Errorf("Expected error %v, got nil", test.expectedError)
51+
}
52+
if err != nil && test.expectedError == nil {
53+
t.Errorf("Expected nil, got %v", err)
54+
}
55+
if err != nil && test.expectedError != nil && err.Error() != test.expectedError.Error() {
56+
t.Errorf("Expected error %v, got %v", test.expectedError, err)
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)