1package uvm 2 3import ( 4 "context" 5 "errors" 6 "fmt" 7 "strconv" 8 9 "github.com/Microsoft/hcsshim/internal/guestrequest" 10 "github.com/Microsoft/hcsshim/internal/requesttype" 11 hcsschema "github.com/Microsoft/hcsshim/internal/schema2" 12 "github.com/Microsoft/hcsshim/osversion" 13) 14 15// Plan9Share is a struct containing host paths for the UVM 16type Plan9Share struct { 17 // UVM resource belongs to 18 vm *UtilityVM 19 name, uvmPath string 20} 21 22// Release frees the resources of the corresponding Plan9 share 23func (p9 *Plan9Share) Release(ctx context.Context) error { 24 if err := p9.vm.RemovePlan9(ctx, p9); err != nil { 25 return fmt.Errorf("failed to remove plan9 share: %s", err) 26 } 27 return nil 28} 29 30const plan9Port = 564 31 32// AddPlan9 adds a Plan9 share to a utility VM. 33func (uvm *UtilityVM) AddPlan9(ctx context.Context, hostPath string, uvmPath string, readOnly bool, restrict bool, allowedNames []string) (*Plan9Share, error) { 34 if uvm.operatingSystem != "linux" { 35 return nil, errNotSupported 36 } 37 if restrict && osversion.Get().Build < osversion.V19H1 { 38 return nil, errors.New("single-file mappings are not supported on this build of Windows") 39 } 40 if uvmPath == "" { 41 return nil, fmt.Errorf("uvmPath must be passed to AddPlan9") 42 } 43 44 // TODO: JTERRY75 - These are marked private in the schema. For now use them 45 // but when there are public variants we need to switch to them. 46 const ( 47 shareFlagsReadOnly int32 = 0x00000001 48 shareFlagsLinuxMetadata int32 = 0x00000004 49 shareFlagsCaseSensitive int32 = 0x00000008 50 shareFlagsRestrictFileAccess int32 = 0x00000080 51 ) 52 53 // TODO: JTERRY75 - `shareFlagsCaseSensitive` only works if the Windows 54 // `hostPath` supports case sensitivity. We need to detect this case before 55 // forwarding this flag in all cases. 56 flags := shareFlagsLinuxMetadata // | shareFlagsCaseSensitive 57 if readOnly { 58 flags |= shareFlagsReadOnly 59 } 60 if restrict { 61 flags |= shareFlagsRestrictFileAccess 62 } 63 64 uvm.m.Lock() 65 index := uvm.plan9Counter 66 uvm.plan9Counter++ 67 uvm.m.Unlock() 68 name := strconv.FormatUint(index, 10) 69 70 modification := &hcsschema.ModifySettingRequest{ 71 RequestType: requesttype.Add, 72 Settings: hcsschema.Plan9Share{ 73 Name: name, 74 AccessName: name, 75 Path: hostPath, 76 Port: plan9Port, 77 Flags: flags, 78 AllowedFiles: allowedNames, 79 }, 80 ResourcePath: plan9ShareResourcePath, 81 GuestRequest: guestrequest.GuestRequest{ 82 ResourceType: guestrequest.ResourceTypeMappedDirectory, 83 RequestType: requesttype.Add, 84 Settings: guestrequest.LCOWMappedDirectory{ 85 MountPath: uvmPath, 86 ShareName: name, 87 Port: plan9Port, 88 ReadOnly: readOnly, 89 }, 90 }, 91 } 92 93 if err := uvm.modify(ctx, modification); err != nil { 94 return nil, err 95 } 96 97 return &Plan9Share{ 98 vm: uvm, 99 name: name, 100 uvmPath: uvmPath, 101 }, nil 102} 103 104// RemovePlan9 removes a Plan9 share from a utility VM. Each Plan9 share is ref-counted 105// and only actually removed when the ref-count drops to zero. 106func (uvm *UtilityVM) RemovePlan9(ctx context.Context, share *Plan9Share) error { 107 if uvm.operatingSystem != "linux" { 108 return errNotSupported 109 } 110 111 modification := &hcsschema.ModifySettingRequest{ 112 RequestType: requesttype.Remove, 113 Settings: hcsschema.Plan9Share{ 114 Name: share.name, 115 AccessName: share.name, 116 Port: plan9Port, 117 }, 118 ResourcePath: plan9ShareResourcePath, 119 GuestRequest: guestrequest.GuestRequest{ 120 ResourceType: guestrequest.ResourceTypeMappedDirectory, 121 RequestType: requesttype.Remove, 122 Settings: guestrequest.LCOWMappedDirectory{ 123 MountPath: share.uvmPath, 124 ShareName: share.name, 125 Port: plan9Port, 126 }, 127 }, 128 } 129 if err := uvm.modify(ctx, modification); err != nil { 130 return fmt.Errorf("failed to remove plan9 share %s from %s: %+v: %s", share.name, uvm.id, modification, err) 131 } 132 return nil 133} 134