package uvm import ( "fmt" "github.com/Microsoft/hcsshim/internal/guestrequest" "github.com/Microsoft/hcsshim/internal/logfields" "github.com/Microsoft/hcsshim/internal/requesttype" "github.com/Microsoft/hcsshim/internal/schema2" "github.com/sirupsen/logrus" ) // AddPlan9 adds a Plan9 share to a utility VM. Each Plan9 share is ref-counted and // only added if it isn't already. func (uvm *UtilityVM) AddPlan9(hostPath string, uvmPath string, readOnly bool) error { logrus.WithFields(logrus.Fields{ logfields.UVMID: uvm.id, "host-path": hostPath, "uvm-path": uvmPath, "readOnly": readOnly, }).Debug("uvm::AddPlan9") if uvm.operatingSystem != "linux" { return errNotSupported } if uvmPath == "" { return fmt.Errorf("uvmPath must be passed to AddPlan9") } // TODO: JTERRY75 - These are marked private in the schema. For now use them // but when there are public variants we need to switch to them. const ( shareFlagsReadOnly int32 = 0x00000001 shareFlagsLinuxMetadata int32 = 0x00000004 shareFlagsCaseSensitive int32 = 0x00000008 ) flags := shareFlagsLinuxMetadata | shareFlagsCaseSensitive if readOnly { flags |= shareFlagsReadOnly } uvm.m.Lock() defer uvm.m.Unlock() if uvm.plan9Shares == nil { uvm.plan9Shares = make(map[string]*plan9Info) } if _, ok := uvm.plan9Shares[hostPath]; !ok { uvm.plan9Counter++ modification := &hcsschema.ModifySettingRequest{ RequestType: requesttype.Add, Settings: hcsschema.Plan9Share{ Name: fmt.Sprintf("%d", uvm.plan9Counter), Path: hostPath, Port: int32(uvm.plan9Counter), // TODO: Temporary. Will all use a single port (9999) Flags: flags, }, ResourcePath: fmt.Sprintf("VirtualMachine/Devices/Plan9/Shares"), GuestRequest: guestrequest.GuestRequest{ ResourceType: guestrequest.ResourceTypeMappedDirectory, RequestType: requesttype.Add, Settings: guestrequest.LCOWMappedDirectory{ MountPath: uvmPath, Port: int32(uvm.plan9Counter), // TODO: Temporary. Will all use a single port (9999) ReadOnly: readOnly, }, }, } if err := uvm.Modify(modification); err != nil { return err } uvm.plan9Shares[hostPath] = &plan9Info{ refCount: 1, uvmPath: uvmPath, idCounter: uvm.plan9Counter, port: int32(uvm.plan9Counter), // TODO: Temporary. Will all use a single port (9999) } } else { uvm.plan9Shares[hostPath].refCount++ } logrus.Debugf("hcsshim::AddPlan9 Success %s: refcount=%d %+v", hostPath, uvm.plan9Shares[hostPath].refCount, uvm.plan9Shares[hostPath]) return nil } // RemovePlan9 removes a Plan9 share from a utility VM. Each Plan9 share is ref-counted // and only actually removed when the ref-count drops to zero. func (uvm *UtilityVM) RemovePlan9(hostPath string) error { if uvm.operatingSystem != "linux" { return errNotSupported } logrus.Debugf("uvm::RemovePlan9 %s id:%s", hostPath, uvm.id) uvm.m.Lock() defer uvm.m.Unlock() if _, ok := uvm.plan9Shares[hostPath]; !ok { return fmt.Errorf("%s is not present as a Plan9 share in %s, cannot remove", hostPath, uvm.id) } return uvm.removePlan9(hostPath, uvm.plan9Shares[hostPath].uvmPath) } // removePlan9 is the internally callable "unsafe" version of RemovePlan9. The mutex // MUST be held when calling this function. func (uvm *UtilityVM) removePlan9(hostPath, uvmPath string) error { uvm.plan9Shares[hostPath].refCount-- if uvm.plan9Shares[hostPath].refCount > 0 { logrus.Debugf("uvm::RemovePlan9 Success %s id:%s Ref-count now %d. It is still present in the utility VM", hostPath, uvm.id, uvm.plan9Shares[hostPath].refCount) return nil } logrus.Debugf("uvm::RemovePlan9 Zero ref-count, removing. %s id:%s", hostPath, uvm.id) modification := &hcsschema.ModifySettingRequest{ RequestType: requesttype.Remove, Settings: hcsschema.Plan9Share{ Name: fmt.Sprintf("%d", uvm.plan9Shares[hostPath].idCounter), Port: uvm.plan9Shares[hostPath].port, }, ResourcePath: fmt.Sprintf("VirtualMachine/Devices/Plan9/Shares"), GuestRequest: guestrequest.GuestRequest{ ResourceType: guestrequest.ResourceTypeMappedDirectory, RequestType: requesttype.Remove, Settings: guestrequest.LCOWMappedDirectory{ MountPath: uvm.plan9Shares[hostPath].uvmPath, Port: uvm.plan9Shares[hostPath].port, }, }, } if err := uvm.Modify(modification); err != nil { return fmt.Errorf("failed to remove plan9 share %s from %s: %+v: %s", hostPath, uvm.id, modification, err) } delete(uvm.plan9Shares, hostPath) logrus.Debugf("uvm::RemovePlan9 Success %s id:%s successfully removed from utility VM", hostPath, uvm.id) return nil }