Skip to content

Commit 8a611df

Browse files
committed
vsmb share redirector start and bind to vmbus
Signed-off-by: Pooja Mahadev Soundalgekar <[email protected]>
1 parent 9fb15b4 commit 8a611df

File tree

4 files changed

+351
-0
lines changed

4 files changed

+351
-0
lines changed

cmd/gcs-sidecar/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ func main() {
182182
}
183183
}
184184

185+
logrus.Println("Initializing VSMB redirector..")
186+
sidecar.VsmbMain()
187+
185188
// 1. Start external server to connect with inbox GCS
186189
listener, err := winio.ListenHvsock(&winio.HvsockAddr{
187190
VMID: prot.HvGUIDLoopback,

internal/gcs-sidecar/vsmb.go

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
//go:build windows
2+
3+
package bridge
4+
5+
import (
6+
"time"
7+
"unsafe"
8+
9+
winio "github.com/Microsoft/go-winio"
10+
"github.com/Microsoft/hcsshim/internal/winapi"
11+
"github.com/sirupsen/logrus"
12+
"golang.org/x/sys/windows"
13+
"golang.org/x/sys/windows/svc"
14+
"golang.org/x/sys/windows/svc/mgr"
15+
)
16+
17+
const (
18+
GlobalRdrDeviceName = `\\?\GLOBALROOT\Device\LanmanRedirector`
19+
GlobalVsmbDeviceName = `\\?\GLOBALROOT\Device\vmsmb`
20+
GlobalVsmbInstanceName = `\Device\vmsmb`
21+
GlobalVsmbTransportName = `\Device\VMBus\{4d12e519-17a0-4ae4-8eaa-5270fc6abdb7}-{dcc079ae-60ba-4d07-847c-3493609c0870}-0000`
22+
SeLoadDriverName = "SeLoadDriverPrivilege"
23+
FsctlLmrStartInstance = 0x001403A0
24+
FsctlLmrBindToTransport = 0x001401B0
25+
LmrInstanceFlagRegisterFilesystem = 0x2
26+
LmrInstanceFlagUseCustomTransports = 0x4
27+
LmrInstanceFlagAllowGuestAuth = 0x8
28+
LmrInstanceFlagSupportsDirectmappedIo = 0x10
29+
SmbCeTransportTypeVmbus = 3
30+
)
31+
32+
type IOStatusBlock struct {
33+
Status uintptr
34+
Information uintptr
35+
}
36+
37+
func configureAndStartLanmanWorkstation() error {
38+
m, err := mgr.Connect()
39+
if err != nil {
40+
logrus.Errorf("Failed to connect to Service Manager: %v", err)
41+
return err
42+
}
43+
defer func() {
44+
if m != nil {
45+
if derr := m.Disconnect(); derr != nil {
46+
logrus.WithError(derr).Warn("Failed to disconnect Service Manager")
47+
}
48+
}
49+
}()
50+
51+
s, err := m.OpenService("LanmanWorkstation")
52+
if err != nil {
53+
logrus.Errorf("Failed to open LanmanWorkstation service: %v", err)
54+
return err
55+
}
56+
defer func() {
57+
if s != nil {
58+
if derr := s.Close(); derr != nil {
59+
logrus.WithError(derr).Warn("Failed to close LanmanWorkstation service")
60+
}
61+
}
62+
}()
63+
64+
cfg, err := s.Config()
65+
if err != nil {
66+
logrus.Errorf("retrieve LanmanWorkstation service config: %v", err)
67+
return err
68+
}
69+
cfg.StartType = mgr.StartAutomatic
70+
if err = s.UpdateConfig(cfg); err != nil {
71+
logrus.Errorf("update LanmanWorkstation service confg: %v", err)
72+
return err
73+
}
74+
return s.Start()
75+
}
76+
77+
// Structs
78+
type SMB2InstanceConfiguration struct {
79+
DormantDirectoryTimeout uint32
80+
DormantFileTimeout uint32
81+
DormantFileLimit uint32
82+
FileInfoCacheLifetime uint32
83+
FileNotFoundCacheLifetime uint32
84+
DirectoryCacheLifetime uint32
85+
FileInfoCacheEntriesMax uint32
86+
FileNotFoundCacheEntriesMax uint32
87+
DirectoryCacheEntriesMax uint32
88+
DirectoryCacheSizeMax uint32
89+
ReadAheadGranularity uint32
90+
VolumeFeatureSupportCacheLifetime uint32
91+
VolumeFeatureSupportCacheEntriesMax uint32
92+
FileAbeStatusCacheLifetime uint32
93+
RequireSecuritySignature byte
94+
RequireEncryption byte
95+
Padding [2]byte
96+
}
97+
98+
type LMRConnectionProperties struct {
99+
Flags1 byte
100+
Flags2 byte
101+
Padding [2]byte
102+
SessionTimeoutInterval uint32
103+
CAHandleKeepaliveInterval uint32
104+
NonCAHandleKeepaliveInterval uint32
105+
ActiveIOKeepaliveInterval uint32
106+
DisableRdma uint32
107+
ConnectionCountPerRdmaInterface uint32
108+
AlternateTCPPort uint16
109+
AlternateQuicPort uint16
110+
AlternateRdmaPort uint16
111+
Padding2 [2]byte
112+
}
113+
114+
type LMRStartInstanceRequest struct {
115+
StructureSize uint32
116+
IoTimeout uint32
117+
IoRetryCount uint32
118+
Flags uint16
119+
Padding1 uint16
120+
Reserved1 uint32
121+
InstanceConfig SMB2InstanceConfiguration
122+
DefaultConnectionProperties LMRConnectionProperties
123+
InstanceID byte
124+
Reserved2 byte
125+
DeviceNameLength uint16
126+
}
127+
128+
type LMRBindUnbindTransportRequest struct {
129+
StructureSize uint16
130+
Flags uint16
131+
Type uint32
132+
TransportIDLength uint32
133+
}
134+
135+
func isLanmanWorkstationRunning() (bool, error) {
136+
m, err := mgr.Connect()
137+
if err != nil {
138+
return false, err
139+
}
140+
defer func() {
141+
if m != nil {
142+
if derr := m.Disconnect(); derr != nil {
143+
logrus.WithError(derr).Warn("Failed to disconnect Service Manager")
144+
}
145+
}
146+
}()
147+
148+
s, err := m.OpenService("LanmanWorkstation")
149+
if err != nil {
150+
return false, err
151+
}
152+
defer func() {
153+
if s != nil {
154+
if derr := s.Close(); derr != nil {
155+
logrus.WithError(derr).Warn("Failed to close LanmanWorkstation service")
156+
}
157+
}
158+
}()
159+
160+
status, err := s.Query()
161+
if err != nil {
162+
return false, err
163+
}
164+
165+
// Check if the service state is running
166+
return status.State == svc.Running, nil
167+
}
168+
169+
func VsmbMain() {
170+
logrus.Info("Starting VSMB initialization...")
171+
172+
logrus.Debug("Configuring LanmanWorkstation service...")
173+
if err := configureAndStartLanmanWorkstation(); err != nil {
174+
logrus.Errorf("LanmanWorkstation setup failed: %v", err)
175+
return
176+
}
177+
178+
time.Sleep(3 * time.Second) // TODO: This needs to be better logic.
179+
running, err := isLanmanWorkstationRunning()
180+
if err != nil {
181+
logrus.Errorf("Failed to query LanmanWorkstation status: %v", err)
182+
} else if running {
183+
logrus.Info("LanmanWorkstation service is running.")
184+
} else {
185+
logrus.Warn("LanmanWorkstation service is NOT running.")
186+
}
187+
188+
if err := winio.EnableProcessPrivileges([]string{SeLoadDriverName}); err != nil {
189+
logrus.Errorf("Failed to enable privilege: %v", err)
190+
return
191+
}
192+
// Open LanmanRedirector
193+
namePtr, nerr := windows.UTF16PtrFromString(GlobalRdrDeviceName)
194+
if nerr != nil {
195+
logrus.WithError(nerr).Errorf("invalid device name %q", GlobalRdrDeviceName)
196+
return
197+
}
198+
199+
lmrHandle, err := windows.CreateFile(
200+
namePtr,
201+
windows.SYNCHRONIZE|windows.FILE_LIST_DIRECTORY|windows.FILE_TRAVERSE,
202+
windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE,
203+
nil,
204+
windows.OPEN_EXISTING,
205+
0,
206+
0,
207+
)
208+
if err != nil {
209+
logrus.WithError(err).Error("Failed to open redirector")
210+
return
211+
}
212+
defer func() {
213+
if derr := windows.CloseHandle(lmrHandle); derr != nil {
214+
logrus.WithError(derr).Warn("Failed to close LanmanRedirector handle")
215+
}
216+
}()
217+
218+
logrus.Info("Successfully opened LanmanRedirector device.")
219+
220+
// Build StartInstance buffer
221+
instanceNameUTF16, nerr := windows.UTF16FromString(GlobalVsmbInstanceName)
222+
if nerr != nil {
223+
logrus.WithError(nerr).Errorf("invalid instance name %q", GlobalVsmbInstanceName)
224+
return
225+
}
226+
structSize := int(unsafe.Sizeof(LMRStartInstanceRequest{}))
227+
bufferSize := structSize + (len(instanceNameUTF16)-1)*2
228+
buffer := make([]byte, bufferSize)
229+
230+
startReq := LMRStartInstanceRequest{
231+
StructureSize: uint32(structSize),
232+
IoTimeout: 30,
233+
IoRetryCount: 3,
234+
Flags: LmrInstanceFlagRegisterFilesystem |
235+
LmrInstanceFlagUseCustomTransports |
236+
LmrInstanceFlagAllowGuestAuth |
237+
LmrInstanceFlagSupportsDirectmappedIo,
238+
InstanceID: 1,
239+
DeviceNameLength: uint16((len(instanceNameUTF16) - 1) * 2),
240+
}
241+
242+
startReq.Reserved1 = 0
243+
startReq.InstanceConfig = SMB2InstanceConfiguration{}
244+
startReq.DefaultConnectionProperties = LMRConnectionProperties{}
245+
startReq.DefaultConnectionProperties.Flags1 = 0x1F
246+
startReq.DefaultConnectionProperties.SessionTimeoutInterval = 55
247+
startReq.DefaultConnectionProperties.CAHandleKeepaliveInterval = 10
248+
startReq.DefaultConnectionProperties.NonCAHandleKeepaliveInterval = 30
249+
startReq.DefaultConnectionProperties.ActiveIOKeepaliveInterval = 30
250+
251+
copy(buffer[:structSize], (*[1 << 20]byte)(unsafe.Pointer(&startReq))[:structSize])
252+
copy(buffer[structSize:], (*[1 << 20]byte)(unsafe.Pointer(&instanceNameUTF16[0]))[:(len(instanceNameUTF16)-1)*2])
253+
254+
// lmrHandle is a windows.Handle from windows.CreateFile(...)
255+
var iosb winapi.IOStatusBlock
256+
status := winapi.NtFsControlFile(
257+
lmrHandle, // file
258+
0, // event (none → synchronous)
259+
0, // apcRoutine (none)
260+
0, // apcCtx
261+
&iosb, // IO_STATUS_BLOCK
262+
FsctlLmrStartInstance, // FSCTL
263+
buffer, // input buffer
264+
nil, // output buffer
265+
)
266+
switch status {
267+
case 0:
268+
logrus.Info("VMSMB RDR instance started.")
269+
case 0xC0000035:
270+
logrus.Warn("VMSMB RDR instance already started.")
271+
default:
272+
logrus.Errorf("NtFsControlFile failed: 0x%08X", status)
273+
}
274+
275+
// BindTransport
276+
namePtr, nerr = windows.UTF16PtrFromString(GlobalVsmbDeviceName)
277+
if nerr != nil {
278+
logrus.WithError(nerr).Errorf("invalid device name %q", GlobalVsmbDeviceName)
279+
return
280+
}
281+
vmsmbHandle, err := windows.CreateFile(
282+
namePtr,
283+
windows.SYNCHRONIZE|windows.FILE_LIST_DIRECTORY|windows.FILE_TRAVERSE,
284+
windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE,
285+
nil, windows.OPEN_EXISTING, 0, 0,
286+
)
287+
if err != nil {
288+
logrus.Errorf("Failed to open VMSMB device: %v", err)
289+
return
290+
}
291+
defer func() {
292+
if derr := windows.CloseHandle(vmsmbHandle); derr != nil {
293+
logrus.WithError(derr).Warn("Failed to close VSMB device handle")
294+
}
295+
}()
296+
297+
transportNameUTF16, nerr := windows.UTF16FromString(GlobalVsmbTransportName)
298+
if nerr != nil {
299+
logrus.WithError(nerr).Errorf("invalid instance name %q", GlobalVsmbTransportName)
300+
return
301+
}
302+
303+
bindStructSize := int(unsafe.Sizeof(LMRBindUnbindTransportRequest{}))
304+
bindBufferSize := bindStructSize + (len(transportNameUTF16)-1)*2
305+
bindBuffer := make([]byte, bindBufferSize)
306+
307+
bindReq := LMRBindUnbindTransportRequest{
308+
StructureSize: uint16(bindStructSize) + 4,
309+
Flags: 0,
310+
Type: 2,
311+
TransportIDLength: uint32((len(transportNameUTF16) - 1) * 2),
312+
}
313+
314+
copy(bindBuffer[:bindStructSize], (*[1 << 20]byte)(unsafe.Pointer(&bindReq))[:bindStructSize])
315+
copy(bindBuffer[bindStructSize:], (*[1 << 20]byte)(unsafe.Pointer(&transportNameUTF16[0]))[:(len(transportNameUTF16)-1)*2])
316+
317+
status = winapi.NtFsControlFile(
318+
vmsmbHandle, // windows.Handle from windows.CreateFile
319+
0, // event (0 → synchronous)
320+
0, // apcRoutine
321+
0, // apcCtx
322+
&iosb, // IO_STATUS_BLOCK
323+
FsctlLmrBindToTransport, // FSCTL
324+
bindBuffer, // in
325+
nil, // out
326+
)
327+
if status == 0 {
328+
logrus.Info("VMBUS transport bound to VMSMB RDR instance.")
329+
} else {
330+
logrus.Errorf("NtFsControlFile failed: 0x%08X", status)
331+
}
332+
}

internal/winapi/filesystem.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package winapi
88

99
//sys NtOpenDirectoryObject(handle *uintptr, accessMask uint32, oa *ObjectAttributes) (status uint32) = ntdll.NtOpenDirectoryObject
1010
//sys NtQueryDirectoryObject(handle uintptr, buffer *byte, length uint32, singleEntry bool, restartScan bool, context *uint32, returnLength *uint32)(status uint32) = ntdll.NtQueryDirectoryObject
11+
//sys NtFsControlFile(file windows.Handle, event windows.Handle, apcRoutine uintptr, apcCtx uintptr, iosb *IOStatusBlock, fsControlCode uint32, in []byte, out []byte) (status uint32) = ntdll.NtFsControlFile
1112

1213
const (
1314
FileLinkInformationClass = 11

internal/winapi/zsyscall_windows.go

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)