@@ -8,7 +8,10 @@ import (
88 "fmt"
99 "io/ioutil"
1010 "os"
11+ "path"
1112 "path/filepath"
13+ "strconv"
14+ "strings"
1215 "time"
1316
1417 "github.com/pkg/errors"
@@ -20,6 +23,7 @@ import (
2023 dm "github.com/Microsoft/hcsshim/internal/guest/storage/devicemapper"
2124 "github.com/Microsoft/hcsshim/internal/log"
2225 "github.com/Microsoft/hcsshim/internal/oc"
26+ "github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
2327 "github.com/Microsoft/hcsshim/internal/protocol/guestresource"
2428 "github.com/Microsoft/hcsshim/pkg/securitypolicy"
2529)
@@ -39,19 +43,53 @@ var (
3943)
4044
4145const (
42- scsiDevicesPath = "/sys/bus/scsi/devices"
43- verityDeviceFmt = "verity-scsi-contr%d-lun%d-%s"
46+ scsiDevicesPath = "/sys/bus/scsi/devices"
47+ vmbusDevicesPath = "/sys/bus/vmbus/devices"
48+ verityDeviceFmt = "verity-scsi-contr%d-lun%d-%s"
4449)
4550
46- // Mount creates a mount from the SCSI device on `controller` index `lun` to
51+ // fetchActualControllerNumber retrieves the actual controller number assigned to a SCSI controller
52+ // with number `passedController`.
53+ // When HCS creates the UVM it adds 4 SCSI controllers to the UVM but the 1st SCSI
54+ // controller according to HCS can actually show up as 2nd, 3rd or 4th controller inside
55+ // the UVM. So the i'th controller from HCS' perspective could actually be j'th controller
56+ // inside the UVM. However, we can refer to the SCSI controllers with their GUIDs (that
57+ // are hardcoded) and then using that GUID find out the SCSI controller number inside the
58+ // guest. This function does exactly that.
59+ func fetchActualControllerNumber (ctx context.Context , passedController uint8 ) (uint8 , error ) {
60+ // find the controller number by looking for a file named host<N> (e.g host1, host3 etc.)
61+ // `N` is the controller number.
62+ // Full file path would be /sys/bus/vmbus/devices/<controller-guid>/host<N>.
63+ controllerDirPath := path .Join (vmbusDevicesPath , guestrequest .ScsiControllerGuids [passedController ])
64+ entries , err := ioutil .ReadDir (controllerDirPath )
65+ if err != nil {
66+ return 0 , err
67+ }
68+
69+ for _ , entry := range entries {
70+ baseName := path .Base (entry .Name ())
71+ if ! strings .HasPrefix (baseName , "host" ) {
72+ continue
73+ }
74+ controllerStr := baseName [len ("host" ):]
75+ controllerNum , err := strconv .ParseUint (controllerStr , 10 , 8 )
76+ if err != nil {
77+ return 0 , fmt .Errorf ("failed to parse controller number from %s: %w" , baseName , err )
78+ }
79+ return uint8 (controllerNum ), nil
80+ }
81+ return 0 , fmt .Errorf ("host<N> directory not found inside %s" , controllerDirPath )
82+ }
83+
84+ // mount creates a mount from the SCSI device on `controller` index `lun` to
4785// `target`
4886//
4987// `target` will be created. On mount failure the created `target` will be
5088// automatically cleaned up.
5189//
5290// If `encrypted` is set to true, the SCSI device will be encrypted using
5391// dm-crypt.
54- func Mount (
92+ func mount (
5593 ctx context.Context ,
5694 controller ,
5795 lun uint8 ,
@@ -159,10 +197,30 @@ func Mount(
159197 return nil
160198}
161199
162- // Unmount unmounts a SCSI device mounted at `target`.
200+ // Mount is just a wrapper over actual mount call. This wrapper finds out the controller
201+ // number from the controller GUID string and calls mount.
202+ func Mount (
203+ ctx context.Context ,
204+ controller ,
205+ lun uint8 ,
206+ target string ,
207+ readonly bool ,
208+ encrypted bool ,
209+ options []string ,
210+ verityInfo * guestresource.DeviceVerityInfo ,
211+ securityPolicy securitypolicy.SecurityPolicyEnforcer ,
212+ ) (err error ) {
213+ cNum , err := fetchActualControllerNumber (ctx , controller )
214+ if err != nil {
215+ return err
216+ }
217+ return mount (ctx , cNum , lun , target , readonly , encrypted , options , verityInfo , securityPolicy )
218+ }
219+
220+ // unmount unmounts a SCSI device mounted at `target`.
163221//
164222// If `encrypted` is true, it removes all its associated dm-crypto state.
165- func Unmount (
223+ func unmount (
166224 ctx context.Context ,
167225 controller ,
168226 lun uint8 ,
@@ -206,6 +264,24 @@ func Unmount(
206264 return nil
207265}
208266
267+ // Unmount is just a wrapper over actual unmount call. This wrapper finds out the controller
268+ // number from the controller GUID string and calls mount.
269+ func Unmount (
270+ ctx context.Context ,
271+ controller ,
272+ lun uint8 ,
273+ target string ,
274+ encrypted bool ,
275+ verityInfo * guestresource.DeviceVerityInfo ,
276+ securityPolicy securitypolicy.SecurityPolicyEnforcer ,
277+ ) (err error ) {
278+ cNum , err := fetchActualControllerNumber (ctx , controller )
279+ if err != nil {
280+ return err
281+ }
282+ return unmount (ctx , cNum , lun , target , encrypted , verityInfo , securityPolicy )
283+ }
284+
209285// ControllerLunToName finds the `/dev/sd*` path to the SCSI device on
210286// `controller` index `lun`.
211287func ControllerLunToName (ctx context.Context , controller , lun uint8 ) (_ string , err error ) {
@@ -217,8 +293,7 @@ func ControllerLunToName(ctx context.Context, controller, lun uint8) (_ string,
217293 trace .Int64Attribute ("controller" , int64 (controller )),
218294 trace .Int64Attribute ("lun" , int64 (lun )))
219295
220- scsiID := fmt .Sprintf ("0:0:%d:%d" , controller , lun )
221-
296+ scsiID := fmt .Sprintf ("%d:0:0:%d" , controller , lun )
222297 // Devices matching the given SCSI code should each have a subdirectory
223298 // under /sys/bus/scsi/devices/<scsiID>/block.
224299 blockPath := filepath .Join (scsiDevicesPath , scsiID , "block" )
@@ -249,11 +324,11 @@ func ControllerLunToName(ctx context.Context, controller, lun uint8) (_ string,
249324 return devicePath , nil
250325}
251326
252- // UnplugDevice finds the SCSI device on `controller` index `lun` and issues a
327+ // unplugDevice finds the SCSI device on `controller` index `lun` and issues a
253328// guest initiated unplug.
254329//
255330// If the device is not attached returns no error.
256- func UnplugDevice (ctx context.Context , controller , lun uint8 ) (err error ) {
331+ func unplugDevice (ctx context.Context , controller , lun uint8 ) (err error ) {
257332 _ , span := trace .StartSpan (ctx , "scsi::UnplugDevice" )
258333 defer span .End ()
259334 defer func () { oc .SetSpanStatus (span , err ) }()
@@ -262,7 +337,7 @@ func UnplugDevice(ctx context.Context, controller, lun uint8) (err error) {
262337 trace .Int64Attribute ("controller" , int64 (controller )),
263338 trace .Int64Attribute ("lun" , int64 (lun )))
264339
265- scsiID := fmt .Sprintf ("0 :0:%d :%d" , controller , lun )
340+ scsiID := fmt .Sprintf ("%d :0:0 :%d" , controller , lun )
266341 f , err := os .OpenFile (filepath .Join (scsiDevicesPath , scsiID , "delete" ), os .O_WRONLY , 0644 )
267342 if err != nil {
268343 if os .IsNotExist (err ) {
@@ -277,3 +352,13 @@ func UnplugDevice(ctx context.Context, controller, lun uint8) (err error) {
277352 }
278353 return nil
279354}
355+
356+ // UnplugDevice is just a wrapper over actual unplugDevice call. This wrapper finds out the controller
357+ // number from the controller GUID string and calls unplugDevice.
358+ func UnplugDevice (ctx context.Context , controller , lun uint8 ) (err error ) {
359+ cNum , err := fetchActualControllerNumber (ctx , controller )
360+ if err != nil {
361+ return err
362+ }
363+ return unplugDevice (ctx , cNum , lun )
364+ }
0 commit comments