@@ -3,8 +3,15 @@ package smb
33import (
44 "fmt"
55 "strings"
6+ "syscall"
67
8+ "github.com/kubernetes-csi/csi-proxy/pkg/cim"
79 "github.com/kubernetes-csi/csi-proxy/pkg/utils"
10+ "golang.org/x/sys/windows"
11+ )
12+
13+ const (
14+ credentialDelimiter = ":"
815)
916
1017type API interface {
@@ -26,18 +33,52 @@ func New(requirePrivacy bool) *SmbAPI {
2633 }
2734}
2835
36+ func remotePathForQuery (remotePath string ) string {
37+ return strings .ReplaceAll (remotePath , "\\ " , "\\ \\ " )
38+ }
39+
40+ func escapeUserName (userName string ) string {
41+ // refer to https://github.com/PowerShell/PowerShell/blob/9303de597da55963a6e26a8fe164d0b256ca3d4d/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimConverter.cs#L169-L170
42+ escaped := strings .ReplaceAll (userName , "\\ " , "\\ \\ " )
43+ escaped = strings .ReplaceAll (escaped , credentialDelimiter , "\\ " + credentialDelimiter )
44+ return escaped
45+ }
46+
47+ func createSymlink (link , target string , isDir bool ) error {
48+ linkPtr , err := syscall .UTF16PtrFromString (link )
49+ if err != nil {
50+ return err
51+ }
52+ targetPtr , err := syscall .UTF16PtrFromString (target )
53+ if err != nil {
54+ return err
55+ }
56+
57+ var flags uint32
58+ if isDir {
59+ flags = windows .SYMBOLIC_LINK_FLAG_DIRECTORY
60+ }
61+
62+ err = windows .CreateSymbolicLink (
63+ linkPtr ,
64+ targetPtr ,
65+ flags ,
66+ )
67+ return err
68+ }
69+
2970func (* SmbAPI ) IsSmbMapped (remotePath string ) (bool , error ) {
30- cmdLine := `$(Get-SmbGlobalMapping -RemotePath $Env:smbremotepath -ErrorAction Stop).Status `
31- cmdEnv := fmt .Sprintf ("smbremotepath=%s" , remotePath )
32- out , err := utils .RunPowershellCmd (cmdLine , cmdEnv )
71+ inst , err := cim .QuerySmbGlobalMappingByRemotePath (remotePathForQuery (remotePath ))
3372 if err != nil {
34- return false , fmt . Errorf ( "error checking smb mapping. cmd %s, output: %s, err: %v" , remotePath , string ( out ), err )
73+ return false , cim . IgnoreNotFound ( err )
3574 }
3675
37- if len (out ) == 0 || ! strings .EqualFold (strings .TrimSpace (string (out )), "OK" ) {
38- return false , nil
76+ status , err := inst .GetProperty ("Status" )
77+ if err != nil {
78+ return false , err
3979 }
40- return true , nil
80+
81+ return status .(int32 ) == cim .SmbMappingStatusOK , nil
4182}
4283
4384// NewSmbLink - creates a directory symbolic link to the remote share.
@@ -48,42 +89,46 @@ func (*SmbAPI) IsSmbMapped(remotePath string) (bool, error) {
4889// alpha to merge the paths.
4990// TODO (for beta release): Merge the link paths - os.Symlink and Powershell link path.
5091func (* SmbAPI ) NewSmbLink (remotePath , localPath string ) error {
51-
5292 if ! strings .HasSuffix (remotePath , "\\ " ) {
5393 // Golang has issues resolving paths mapped to file shares if they do not end in a trailing \
5494 // so add one if needed.
5595 remotePath = remotePath + "\\ "
5696 }
97+ longRemotePath := utils .EnsureLongPath (remotePath )
98+ longLocalPath := utils .EnsureLongPath (localPath )
5799
58- cmdLine := `New-Item -ItemType SymbolicLink $Env:smblocalPath -Target $Env:smbremotepath`
59- output , err := utils .RunPowershellCmd (cmdLine , fmt .Sprintf ("smbremotepath=%s" , remotePath ), fmt .Sprintf ("smblocalpath=%s" , localPath ))
100+ err := createSymlink (longLocalPath , longRemotePath , true )
60101 if err != nil {
61- return fmt .Errorf ("error linking %s to %s. output: %s, err: %v" , remotePath , localPath , string ( output ) , err )
102+ return fmt .Errorf ("error linking %s to %s. err: %v" , remotePath , localPath , err )
62103 }
63104
64105 return nil
65106}
66107
67108func (api * SmbAPI ) NewSmbGlobalMapping (remotePath , username , password string ) error {
68- // use PowerShell Environment Variables to store user input string to prevent command line injection
69- // https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-5.1
70- cmdLine := fmt .Sprintf (`$PWord = ConvertTo-SecureString -String $Env:smbpassword -AsPlainText -Force` +
71- `;$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Env:smbuser, $PWord` +
72- `;New-SmbGlobalMapping -RemotePath $Env:smbremotepath -Credential $Credential -RequirePrivacy $%t` , api .RequirePrivacy )
73-
74- if output , err := utils .RunPowershellCmd (cmdLine ,
75- fmt .Sprintf ("smbuser=%s" , username ),
76- fmt .Sprintf ("smbpassword=%s" , password ),
77- fmt .Sprintf ("smbremotepath=%s" , remotePath )); err != nil {
78- return fmt .Errorf ("NewSmbGlobalMapping failed. output: %q, err: %v" , string (output ), err )
109+ params := map [string ]interface {}{
110+ "RemotePath" : remotePath ,
111+ "RequirePrivacy" : api .RequirePrivacy ,
112+ }
113+ if username != "" {
114+ // refer to https://github.com/PowerShell/PowerShell/blob/9303de597da55963a6e26a8fe164d0b256ca3d4d/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimConverter.cs#L166-L178
115+ // on how SMB credential is handled in PowerShell
116+ params ["Credential" ] = escapeUserName (username ) + credentialDelimiter + password
79117 }
118+
119+ result , _ , err := cim .InvokeCimMethod (cim .WMINamespaceSmb , "MSFT_SmbGlobalMapping" , "Create" , params )
120+ if err != nil {
121+ return fmt .Errorf ("NewSmbGlobalMapping failed. result: %d, err: %v" , result , err )
122+ }
123+
80124 return nil
81125}
82126
83127func (* SmbAPI ) RemoveSmbGlobalMapping (remotePath string ) error {
84- cmd := `Remove-SmbGlobalMapping -RemotePath $Env:smbremotepath -Force`
85- if output , err := utils . RunPowershellCmd ( cmd , fmt . Sprintf ( "smbremotepath=%s" , remotePath )); err != nil {
86- return fmt .Errorf ("UnmountSmbShare failed. output: %q, err: %v" , string ( output ) , err )
128+ err := cim . RemoveSmbGlobalMappingByRemotePath ( remotePathForQuery ( remotePath ))
129+ if err != nil {
130+ return fmt .Errorf ("error remove smb mapping '%s'. err: %v" , remotePath , err )
87131 }
132+
88133 return nil
89134}
0 commit comments