@@ -3,9 +3,15 @@ package smb
33import  (
44	"fmt" 
55	"strings" 
6+ 	"syscall" 
67
78	"github.com/kubernetes-csi/csi-proxy/pkg/cim" 
8- 	"github.com/kubernetes-csi/csi-proxy/pkg/utils" 
9+ 	"golang.org/x/sys/windows" 
10+ )
11+ 
12+ const  (
13+ 	credentialDelimiter  =  ":" 
14+ 	longPathPrefix       =  `\\?\` 
915)
1016
1117type  API  interface  {
@@ -31,6 +37,30 @@ func remotePathForQuery(remotePath string) string {
3137	return  strings .ReplaceAll (remotePath , "\\ " , "\\ \\ " )
3238}
3339
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 , _  :=  syscall .UTF16PtrFromString (link )
49+ 	targetPtr , _  :=  syscall .UTF16PtrFromString (target )
50+ 
51+ 	var  flags  uint32 
52+ 	if  isDir  {
53+ 		flags  =  windows .SYMBOLIC_LINK_FLAG_DIRECTORY 
54+ 	}
55+ 
56+ 	err  :=  windows .CreateSymbolicLink (
57+ 		linkPtr ,
58+ 		targetPtr ,
59+ 		flags ,
60+ 	)
61+ 	return  err 
62+ }
63+ 
3464func  (* SmbAPI ) IsSmbMapped (remotePath  string ) (bool , error ) {
3565	inst , err  :=  cim .QuerySmbGlobalMappingByRemotePath (remotePathForQuery (remotePath ))
3666	if  err  !=  nil  {
@@ -58,43 +88,40 @@ func (*SmbAPI) NewSmbLink(remotePath, localPath string) error {
5888		// so add one if needed. 
5989		remotePath  =  remotePath  +  "\\ " 
6090	}
91+ 	longRemotePath  :=  remotePath 
92+ 	if  ! strings .HasPrefix (longRemotePath , longPathPrefix ) {
93+ 		longRemotePath  =  longPathPrefix  +  longRemotePath 
94+ 	}
95+ 	longLocalPath  :=  localPath 
96+ 	if  ! strings .HasPrefix (longLocalPath , longPathPrefix ) {
97+ 		longLocalPath  =  longPathPrefix  +  longLocalPath 
98+ 	}
6199
62- 	cmdLine  :=  `New-Item -ItemType SymbolicLink $Env:smblocalPath -Target $Env:smbremotepath` 
63- 	output , err  :=  utils .RunPowershellCmd (cmdLine , fmt .Sprintf ("smbremotepath=%s" , remotePath ), fmt .Sprintf ("smblocalpath=%s" , localPath ))
100+ 	err  :=  createSymlink (longLocalPath , longRemotePath , true )
64101	if  err  !=  nil  {
65- 		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 )
66103	}
67104
68105	return  nil 
69106}
70107
71108func  (api  * SmbAPI ) NewSmbGlobalMapping (remotePath , username , password  string ) error  {
72- 	// use PowerShell Environment Variables to store user input string to prevent command line injection 
73- 	// https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-5.1 
74- 	cmdLine  :=  fmt .Sprintf (`$PWord = ConvertTo-SecureString -String $Env:smbpassword -AsPlainText -Force` + 
75- 		`;$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Env:smbuser, $PWord` + 
76- 		`;New-SmbGlobalMapping -RemotePath $Env:smbremotepath -Credential $Credential -RequirePrivacy $%t` , api .RequirePrivacy )
77- 
78- 	if  output , err  :=  utils .RunPowershellCmd (cmdLine ,
79- 		fmt .Sprintf ("smbuser=%s" , username ),
80- 		fmt .Sprintf ("smbpassword=%s" , password ),
81- 		fmt .Sprintf ("smbremotepath=%s" , remotePath )); err  !=  nil  {
82- 		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 
83117	}
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+ 
84124	return  nil 
85- 	//TODO: move to use WMI when the credentials could be correctly handled 
86- 	//params := map[string]interface{}{ 
87- 	//	"RemotePath":     remotePath, 
88- 	//	"RequirePrivacy": api.RequirePrivacy, 
89- 	//} 
90- 	//if username != "" { 
91- 	//	params["Credential"] = fmt.Sprintf("%s:%s", username, password) 
92- 	//} 
93- 	//result, _, err := cim.InvokeCimMethod(cim.WMINamespaceSmb, "MSFT_SmbGlobalMapping", "Create", params) 
94- 	//if err != nil { 
95- 	//	return fmt.Errorf("NewSmbGlobalMapping failed. result: %d, err: %v", result, err) 
96- 	//} 
97- 	//return nil 
98125}
99126
100127func  (* SmbAPI ) RemoveSmbGlobalMapping (remotePath  string ) error  {
0 commit comments