@@ -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,135 @@ 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 m .Disconnect ()
104+
105+ // Ensure fsformatter driver is loaded by querying for the service.
106+ serviceName := "kernelfsformatter"
107+ s , err := m .OpenService (serviceName )
108+ if err != nil {
109+ return syscall .ERROR_FILE_NOT_FOUND
110+ }
111+ defer s .Close ()
112+
113+ _ , err = s .Query ()
114+ if err != nil {
115+ return syscall .ERROR_FILE_NOT_FOUND
116+ }
117+
118+ err = s .Start ()
119+ if err != nil && ! strings .Contains (err .Error (), "An instance of the service is already running" ) {
120+ return fmt .Errorf ("Failed to start service: %v" , err )
121+ }
122+
123+ return nil
124+ }
125+
126+ func TestFormatVHDXToReFS (t * testing.T ) {
127+ // Ensure fsformatter service is loaded and started
128+ err := startFsformatterDriver ()
129+ if err != nil {
130+ // if driver is not loaded already, skip.
131+ if errors .Is (err , syscall .ERROR_FILE_NOT_FOUND ) {
132+ t .Skip ()
133+ }
134+ t .Fatalf ("Failed to start service: %v" , err )
135+ }
136+
137+ ctx := context .Background ()
138+ initialInterfacesList , err := getDeviceInterfaceInstancesByClass (ctx , & devClassDiskGUID , false )
139+ if err != nil {
140+ t .Fatalf ("failed to get initial disk interfaces: %v" , err )
141+ }
142+ // We expect to see only one device initially
143+ if len (initialInterfacesList ) != 1 {
144+ t .Fatalf ("unexpected number of initial disk interfaces: %v" , len (initialInterfacesList ))
145+ }
146+ t .Logf ("initial interface list: %+v\n " , initialInterfacesList )
147+
148+ // Create a fixed VHDX of 31 GB (refs needs size to be > 30GB)
149+ tempDir := t .TempDir ()
150+ vhdxPath := filepath .Join (tempDir , "test.vhdx" )
151+ if err := vhd .CreateVhdx (vhdxPath , diskSizeInGB , defaultVHDxBlockSizeMB ); err != nil {
152+ t .Fatalf ("failed to create VHDX: %v" , err )
153+ }
154+
155+ diskHandle , err := vhd .OpenVirtualDisk (vhdxPath , vhd .VirtualDiskAccessNone , vhd .OpenVirtualDiskFlagNone )
156+ if err != nil {
157+ t .Fatalf ("failed to open VHD handle: %s" , err )
158+ }
159+ t .Cleanup (func () {
160+ if closeErr := windows .CloseHandle (windows .Handle (diskHandle )); closeErr != nil {
161+ t .Logf ("Failed to close VHD handle: %s" , closeErr )
162+ }
163+ })
164+
165+ err = vhd .AttachVirtualDisk (diskHandle , vhd .AttachVirtualDiskFlagNone , & vhd.AttachVirtualDiskParameters {Version : 1 })
166+ if err != nil {
167+ t .Fatalf ("failed to attach VHD: %s" , err )
168+ }
169+ t .Cleanup (func () {
170+ if detachErr := vhd .DetachVirtualDisk (diskHandle ); detachErr != nil {
171+ t .Logf ("failed to detach vhd: %s" , detachErr )
172+ }
173+ })
174+ // Disks might take time to show up. Add a small delay
175+ time .Sleep (1 * time .Second )
176+
177+ interfaceListAfterVHDAttach , err := getDeviceInterfaceInstancesByClass (ctx , & devClassDiskGUID , false )
178+ if err != nil {
179+ t .Fatalf ("failed to get initial disk interfaces: %v" , err )
180+ }
181+ t .Logf ("interface list after attaching VHD: %+v\n " , interfaceListAfterVHDAttach )
182+
183+ if len (initialInterfacesList ) != (len (interfaceListAfterVHDAttach ) - 1 ) {
184+ t .Fatalf ("expected to find exactly 1 new interface in the returned interfaces list" )
185+ }
186+
187+ for _ , iPath := range interfaceListAfterVHDAttach {
188+ // Take only the newly attached vhdx
189+ if iPath == initialInterfacesList [0 ] {
190+ continue
191+ }
192+ utf16Path , err := windows .UTF16PtrFromString (iPath )
193+ if err != nil {
194+ t .Fatalf ("failed to convert interface path [%s] to utf16: %v" , iPath , err )
195+ }
196+
197+ handle , err := windows .CreateFile (utf16Path , windows .GENERIC_READ | windows .GENERIC_WRITE ,
198+ windows .FILE_SHARE_READ | windows .FILE_SHARE_WRITE ,
199+ nil , windows .OPEN_EXISTING , 0 , 0 )
200+ if err != nil {
201+ t .Fatalf ("failed to get handle to interface path [%s]: %v" , iPath , err )
202+ }
203+ defer windows .Close (handle )
204+
205+ deviceNumber , err := getStorageDeviceNumber (ctx , handle )
206+ if err != nil {
207+ t .Fatalf ("failed to get physical device number: %v" , err )
208+ }
209+ diskPath := fmt .Sprintf (fsformatter .VirtualDevObjectPathFormat , deviceNumber .DeviceNumber )
210+ t .Logf ("diskPath %v" , diskPath )
211+
212+ // Invoke refs formatter and ensure it passes.
213+ mountedVolumePath , err := fsformatter .InvokeFsFormatter (ctx , diskPath )
214+ if err != nil {
215+ t .Fatalf ("invoking refsFormatter failed: %v" , err )
216+ }
217+ t .Logf ("mountedVolumePath %v" , mountedVolumePath )
218+ }
219+ }
0 commit comments