@@ -4,12 +4,19 @@ package windevice
44
55import (
66 "context"
7+ "errors"
8+ "fmt"
9+ "log"
710 "path/filepath"
11+ "strings"
12+ "syscall"
813 "testing"
914 "time"
1015
1116 "github.com/Microsoft/go-winio/vhd"
17+ "github.com/Microsoft/hcsshim/internal/fsformatter"
1218 "golang.org/x/sys/windows"
19+ "golang.org/x/sys/windows/svc/mgr"
1320)
1421
1522func TestGetDeviceInterfaceInstances (t * testing.T ) {
@@ -78,3 +85,137 @@ func TestGetDeviceInterfaceInstances(t *testing.T) {
7885 t .Fatalf ("expected interface lists to have same length" )
7986 }
8087}
88+
89+ const (
90+ diskSizeInGB = 35
91+ defaultVHDxBlockSizeMB = 1
92+ )
93+
94+ // startFsformatterDriver checks if fsformatter driver
95+ // has already been loaded and starts the service.
96+ // Returns syscall.ERROR_FILE_NOT_FOUND if driver
97+ // is not loaded.
98+ func startFsformatterDriver () error {
99+ m , err := mgr .Connect ()
100+ if err != nil {
101+ log .Fatalf ("Failed to connect to service manager: %v" , err )
102+ }
103+ defer func () {
104+ _ = m .Disconnect ()
105+ }()
106+
107+ // Ensure fsformatter driver is loaded by querying for the service.
108+ serviceName := "kernelfsformatter"
109+ s , err := m .OpenService (serviceName )
110+ if err != nil {
111+ return syscall .ERROR_FILE_NOT_FOUND
112+ }
113+ defer s .Close ()
114+
115+ _ , err = s .Query ()
116+ if err != nil {
117+ return syscall .ERROR_FILE_NOT_FOUND
118+ }
119+
120+ err = s .Start ()
121+ if err != nil && ! strings .Contains (err .Error (), "An instance of the service is already running" ) {
122+ return fmt .Errorf ("Failed to start service: %w" , err )
123+ }
124+
125+ return nil
126+ }
127+
128+ func TestFormatVHDXToReFS (t * testing.T ) {
129+ // Ensure fsformatter service is loaded and started
130+ err := startFsformatterDriver ()
131+ if err != nil {
132+ // if driver is not loaded already, skip.
133+ if errors .Is (err , syscall .ERROR_FILE_NOT_FOUND ) {
134+ t .Skip ()
135+ }
136+ t .Fatalf ("Failed to start service: %v" , err )
137+ }
138+
139+ ctx := context .Background ()
140+ initialInterfacesList , err := getDeviceInterfaceInstancesByClass (ctx , & devClassDiskGUID , false )
141+ if err != nil {
142+ t .Fatalf ("failed to get initial disk interfaces: %v" , err )
143+ }
144+ // We expect to see only one device initially
145+ if len (initialInterfacesList ) != 1 {
146+ t .Fatalf ("unexpected number of initial disk interfaces: %v" , len (initialInterfacesList ))
147+ }
148+ t .Logf ("initial interface list: %+v\n " , initialInterfacesList )
149+
150+ // Create a fixed VHDX of 31 GB (refs needs size to be > 30GB)
151+ tempDir := t .TempDir ()
152+ vhdxPath := filepath .Join (tempDir , "test.vhdx" )
153+ if err := vhd .CreateVhdx (vhdxPath , diskSizeInGB , defaultVHDxBlockSizeMB ); err != nil {
154+ t .Fatalf ("failed to create VHDX: %v" , err )
155+ }
156+
157+ diskHandle , err := vhd .OpenVirtualDisk (vhdxPath , vhd .VirtualDiskAccessNone , vhd .OpenVirtualDiskFlagNone )
158+ if err != nil {
159+ t .Fatalf ("failed to open VHD handle: %s" , err )
160+ }
161+ t .Cleanup (func () {
162+ if closeErr := windows .CloseHandle (windows .Handle (diskHandle )); closeErr != nil {
163+ t .Logf ("Failed to close VHD handle: %s" , closeErr )
164+ }
165+ })
166+
167+ err = vhd .AttachVirtualDisk (diskHandle , vhd .AttachVirtualDiskFlagNone , & vhd.AttachVirtualDiskParameters {Version : 1 })
168+ if err != nil {
169+ t .Fatalf ("failed to attach VHD: %s" , err )
170+ }
171+ t .Cleanup (func () {
172+ if detachErr := vhd .DetachVirtualDisk (diskHandle ); detachErr != nil {
173+ t .Logf ("failed to detach vhd: %s" , detachErr )
174+ }
175+ })
176+ // Disks might take time to show up. Add a small delay
177+ time .Sleep (1 * time .Second )
178+
179+ interfaceListAfterVHDAttach , err := getDeviceInterfaceInstancesByClass (ctx , & devClassDiskGUID , false )
180+ if err != nil {
181+ t .Fatalf ("failed to get initial disk interfaces: %v" , err )
182+ }
183+ t .Logf ("interface list after attaching VHD: %+v\n " , interfaceListAfterVHDAttach )
184+
185+ if len (initialInterfacesList ) != (len (interfaceListAfterVHDAttach ) - 1 ) {
186+ t .Fatalf ("expected to find exactly 1 new interface in the returned interfaces list" )
187+ }
188+
189+ for _ , iPath := range interfaceListAfterVHDAttach {
190+ // Take only the newly attached vhdx
191+ if iPath == initialInterfacesList [0 ] {
192+ continue
193+ }
194+ utf16Path , err := windows .UTF16PtrFromString (iPath )
195+ if err != nil {
196+ t .Fatalf ("failed to convert interface path [%s] to utf16: %v" , iPath , err )
197+ }
198+
199+ handle , err := windows .CreateFile (utf16Path , windows .GENERIC_READ | windows .GENERIC_WRITE ,
200+ windows .FILE_SHARE_READ | windows .FILE_SHARE_WRITE ,
201+ nil , windows .OPEN_EXISTING , 0 , 0 )
202+ if err != nil {
203+ t .Fatalf ("failed to get handle to interface path [%s]: %v" , iPath , err )
204+ }
205+ defer windows .Close (handle )
206+
207+ deviceNumber , err := getStorageDeviceNumber (ctx , handle )
208+ if err != nil {
209+ t .Fatalf ("failed to get physical device number: %v" , err )
210+ }
211+ diskPath := fmt .Sprintf (fsformatter .VirtualDevObjectPathFormat , deviceNumber .DeviceNumber )
212+ t .Logf ("diskPath %v" , diskPath )
213+
214+ // Invoke refs formatter and ensure it passes.
215+ mountedVolumePath , err := fsformatter .InvokeFsFormatter (ctx , diskPath )
216+ if err != nil {
217+ t .Fatalf ("invoking refsFormatter failed: %v" , err )
218+ }
219+ t .Logf ("mountedVolumePath %v" , mountedVolumePath )
220+ }
221+ }
0 commit comments