Update hcsshim to v0.6.10

Signed-off-by: Darren Stahl <darst@microsoft.com>
This commit is contained in:
Darren Stahl 2018-05-02 16:19:15 -07:00
parent cfba048bec
commit 07d8716004
13 changed files with 1469 additions and 927 deletions

View File

@ -34,7 +34,7 @@ golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
github.com/Microsoft/go-winio v0.4.5 github.com/Microsoft/go-winio v0.4.5
github.com/Microsoft/hcsshim v0.6.7 github.com/Microsoft/hcsshim v0.6.10
github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4

View File

@ -10,7 +10,7 @@ import (
) )
type baseLayerWriter struct { type baseLayerWriter struct {
root string root *os.File
f *os.File f *os.File
bw *winio.BackupFileWriter bw *winio.BackupFileWriter
err error err error
@ -26,10 +26,10 @@ type dirInfo struct {
// reapplyDirectoryTimes reapplies directory modification, creation, etc. times // reapplyDirectoryTimes reapplies directory modification, creation, etc. times
// after processing of the directory tree has completed. The times are expected // after processing of the directory tree has completed. The times are expected
// to be ordered such that parent directories come before child directories. // to be ordered such that parent directories come before child directories.
func reapplyDirectoryTimes(dis []dirInfo) error { func reapplyDirectoryTimes(root *os.File, dis []dirInfo) error {
for i := range dis { for i := range dis {
di := &dis[len(dis)-i-1] // reverse order: process child directories first di := &dis[len(dis)-i-1] // reverse order: process child directories first
f, err := winio.OpenForBackup(di.path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, syscall.OPEN_EXISTING) f, err := openRelative(di.path, root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, _FILE_OPEN, _FILE_DIRECTORY_FILE)
if err != nil { if err != nil {
return err return err
} }
@ -75,12 +75,6 @@ func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err e
w.hasUtilityVM = true w.hasUtilityVM = true
} }
path := filepath.Join(w.root, name)
path, err = makeLongAbsPath(path)
if err != nil {
return err
}
var f *os.File var f *os.File
defer func() { defer func() {
if f != nil { if f != nil {
@ -88,27 +82,23 @@ func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err e
} }
}() }()
createmode := uint32(syscall.CREATE_NEW) extraFlags := uint32(0)
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
err := os.Mkdir(path, 0) extraFlags |= _FILE_DIRECTORY_FILE
if err != nil && !os.IsExist(err) {
return err
}
createmode = syscall.OPEN_EXISTING
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
w.dirInfo = append(w.dirInfo, dirInfo{path, *fileInfo}) w.dirInfo = append(w.dirInfo, dirInfo{name, *fileInfo})
} }
} }
mode := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | winio.WRITE_DAC | winio.WRITE_OWNER | winio.ACCESS_SYSTEM_SECURITY) mode := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | winio.WRITE_DAC | winio.WRITE_OWNER | winio.ACCESS_SYSTEM_SECURITY)
f, err = winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createmode) f, err = openRelative(name, w.root, mode, syscall.FILE_SHARE_READ, _FILE_CREATE, extraFlags)
if err != nil { if err != nil {
return makeError(err, "Failed to OpenForBackup", path) return makeError(err, "Failed to openRelative", name)
} }
err = winio.SetFileBasicInfo(f, fileInfo) err = winio.SetFileBasicInfo(f, fileInfo)
if err != nil { if err != nil {
return makeError(err, "Failed to SetFileBasicInfo", path) return makeError(err, "Failed to SetFileBasicInfo", name)
} }
w.f = f w.f = f
@ -129,17 +119,7 @@ func (w *baseLayerWriter) AddLink(name string, target string) (err error) {
return err return err
} }
linkpath, err := makeLongAbsPath(filepath.Join(w.root, name)) return linkRelative(target, w.root, name, w.root)
if err != nil {
return err
}
linktarget, err := makeLongAbsPath(filepath.Join(w.root, target))
if err != nil {
return err
}
return os.Link(linktarget, linkpath)
} }
func (w *baseLayerWriter) Remove(name string) error { func (w *baseLayerWriter) Remove(name string) error {
@ -155,6 +135,10 @@ func (w *baseLayerWriter) Write(b []byte) (int, error) {
} }
func (w *baseLayerWriter) Close() error { func (w *baseLayerWriter) Close() error {
defer func() {
w.root.Close()
w.root = nil
}()
err := w.closeCurrentFile() err := w.closeCurrentFile()
if err != nil { if err != nil {
return err return err
@ -162,18 +146,22 @@ func (w *baseLayerWriter) Close() error {
if w.err == nil { if w.err == nil {
// Restore the file times of all the directories, since they may have // Restore the file times of all the directories, since they may have
// been modified by creating child directories. // been modified by creating child directories.
err = reapplyDirectoryTimes(w.dirInfo) err = reapplyDirectoryTimes(w.root, w.dirInfo)
if err != nil { if err != nil {
return err return err
} }
err = ProcessBaseLayer(w.root) err = ProcessBaseLayer(w.root.Name())
if err != nil { if err != nil {
return err return err
} }
if w.hasUtilityVM { if w.hasUtilityVM {
err = ProcessUtilityVMImage(filepath.Join(w.root, "UtilityVM")) err := ensureNotReparsePointRelative("UtilityVM", w.root)
if err != nil {
return err
}
err = ProcessUtilityVMImage(filepath.Join(w.root.Name(), "UtilityVM"))
if err != nil { if err != nil {
return err return err
} }

View File

@ -11,7 +11,7 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
//go:generate go run mksyscall_windows.go -output zhcsshim.go hcsshim.go //go:generate go run mksyscall_windows.go -output zhcsshim.go hcsshim.go safeopen.go
//sys coTaskMemFree(buffer unsafe.Pointer) = ole32.CoTaskMemFree //sys coTaskMemFree(buffer unsafe.Pointer) = ole32.CoTaskMemFree
//sys SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) = iphlpapi.SetCurrentThreadCompartmentId //sys SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) = iphlpapi.SetCurrentThreadCompartmentId

View File

@ -1,323 +1,323 @@
package hcsshim package hcsshim
import ( import (
"encoding/json" "encoding/json"
"net" "net"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
// HNSEndpoint represents a network endpoint in HNS // HNSEndpoint represents a network endpoint in HNS
type HNSEndpoint struct { type HNSEndpoint struct {
Id string `json:"ID,omitempty"` Id string `json:"ID,omitempty"`
Name string `json:",omitempty"` Name string `json:",omitempty"`
VirtualNetwork string `json:",omitempty"` VirtualNetwork string `json:",omitempty"`
VirtualNetworkName string `json:",omitempty"` VirtualNetworkName string `json:",omitempty"`
Policies []json.RawMessage `json:",omitempty"` Policies []json.RawMessage `json:",omitempty"`
MacAddress string `json:",omitempty"` MacAddress string `json:",omitempty"`
IPAddress net.IP `json:",omitempty"` IPAddress net.IP `json:",omitempty"`
DNSSuffix string `json:",omitempty"` DNSSuffix string `json:",omitempty"`
DNSServerList string `json:",omitempty"` DNSServerList string `json:",omitempty"`
GatewayAddress string `json:",omitempty"` GatewayAddress string `json:",omitempty"`
EnableInternalDNS bool `json:",omitempty"` EnableInternalDNS bool `json:",omitempty"`
DisableICC bool `json:",omitempty"` DisableICC bool `json:",omitempty"`
PrefixLength uint8 `json:",omitempty"` PrefixLength uint8 `json:",omitempty"`
IsRemoteEndpoint bool `json:",omitempty"` IsRemoteEndpoint bool `json:",omitempty"`
} }
//SystemType represents the type of the system on which actions are done //SystemType represents the type of the system on which actions are done
type SystemType string type SystemType string
// SystemType const // SystemType const
const ( const (
ContainerType SystemType = "Container" ContainerType SystemType = "Container"
VirtualMachineType SystemType = "VirtualMachine" VirtualMachineType SystemType = "VirtualMachine"
HostType SystemType = "Host" HostType SystemType = "Host"
) )
// EndpointAttachDetachRequest is the structure used to send request to the container to modify the system // EndpointAttachDetachRequest is the structure used to send request to the container to modify the system
// Supported resource types are Network and Request Types are Add/Remove // Supported resource types are Network and Request Types are Add/Remove
type EndpointAttachDetachRequest struct { type EndpointAttachDetachRequest struct {
ContainerID string `json:"ContainerId,omitempty"` ContainerID string `json:"ContainerId,omitempty"`
SystemType SystemType `json:"SystemType"` SystemType SystemType `json:"SystemType"`
CompartmentID uint16 `json:"CompartmentId,omitempty"` CompartmentID uint16 `json:"CompartmentId,omitempty"`
VirtualNICName string `json:"VirtualNicName,omitempty"` VirtualNICName string `json:"VirtualNicName,omitempty"`
} }
// EndpointResquestResponse is object to get the endpoint request response // EndpointResquestResponse is object to get the endpoint request response
type EndpointResquestResponse struct { type EndpointResquestResponse struct {
Success bool Success bool
Error string Error string
} }
// HNSEndpointRequest makes a HNS call to modify/query a network endpoint // HNSEndpointRequest makes a HNS call to modify/query a network endpoint
func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) { func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) {
endpoint := &HNSEndpoint{} endpoint := &HNSEndpoint{}
err := hnsCall(method, "/endpoints/"+path, request, &endpoint) err := hnsCall(method, "/endpoints/"+path, request, &endpoint)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return endpoint, nil return endpoint, nil
} }
// HNSListEndpointRequest makes a HNS call to query the list of available endpoints // HNSListEndpointRequest makes a HNS call to query the list of available endpoints
func HNSListEndpointRequest() ([]HNSEndpoint, error) { func HNSListEndpointRequest() ([]HNSEndpoint, error) {
var endpoint []HNSEndpoint var endpoint []HNSEndpoint
err := hnsCall("GET", "/endpoints/", "", &endpoint) err := hnsCall("GET", "/endpoints/", "", &endpoint)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return endpoint, nil return endpoint, nil
} }
// HotAttachEndpoint makes a HCS Call to attach the endpoint to the container // HotAttachEndpoint makes a HCS Call to attach the endpoint to the container
func HotAttachEndpoint(containerID string, endpointID string) error { func HotAttachEndpoint(containerID string, endpointID string) error {
return modifyNetworkEndpoint(containerID, endpointID, Add) return modifyNetworkEndpoint(containerID, endpointID, Add)
} }
// HotDetachEndpoint makes a HCS Call to detach the endpoint from the container // HotDetachEndpoint makes a HCS Call to detach the endpoint from the container
func HotDetachEndpoint(containerID string, endpointID string) error { func HotDetachEndpoint(containerID string, endpointID string) error {
return modifyNetworkEndpoint(containerID, endpointID, Remove) return modifyNetworkEndpoint(containerID, endpointID, Remove)
} }
// ModifyContainer corresponding to the container id, by sending a request // ModifyContainer corresponding to the container id, by sending a request
func modifyContainer(id string, request *ResourceModificationRequestResponse) error { func modifyContainer(id string, request *ResourceModificationRequestResponse) error {
container, err := OpenContainer(id) container, err := OpenContainer(id)
if err != nil { if err != nil {
if IsNotExist(err) { if IsNotExist(err) {
return ErrComputeSystemDoesNotExist return ErrComputeSystemDoesNotExist
} }
return getInnerError(err) return getInnerError(err)
} }
defer container.Close() defer container.Close()
err = container.Modify(request) err = container.Modify(request)
if err != nil { if err != nil {
if IsNotSupported(err) { if IsNotSupported(err) {
return ErrPlatformNotSupported return ErrPlatformNotSupported
} }
return getInnerError(err) return getInnerError(err)
} }
return nil return nil
} }
func modifyNetworkEndpoint(containerID string, endpointID string, request RequestType) error { func modifyNetworkEndpoint(containerID string, endpointID string, request RequestType) error {
requestMessage := &ResourceModificationRequestResponse{ requestMessage := &ResourceModificationRequestResponse{
Resource: Network, Resource: Network,
Request: request, Request: request,
Data: endpointID, Data: endpointID,
} }
err := modifyContainer(containerID, requestMessage) err := modifyContainer(containerID, requestMessage)
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
// GetHNSEndpointByID get the Endpoint by ID // GetHNSEndpointByID get the Endpoint by ID
func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) { func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) {
return HNSEndpointRequest("GET", endpointID, "") return HNSEndpointRequest("GET", endpointID, "")
} }
// GetHNSEndpointByName gets the endpoint filtered by Name // GetHNSEndpointByName gets the endpoint filtered by Name
func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) { func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) {
hnsResponse, err := HNSListEndpointRequest() hnsResponse, err := HNSListEndpointRequest()
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, hnsEndpoint := range hnsResponse { for _, hnsEndpoint := range hnsResponse {
if hnsEndpoint.Name == endpointName { if hnsEndpoint.Name == endpointName {
return &hnsEndpoint, nil return &hnsEndpoint, nil
} }
} }
return nil, EndpointNotFoundError{EndpointName: endpointName} return nil, EndpointNotFoundError{EndpointName: endpointName}
} }
// Create Endpoint by sending EndpointRequest to HNS. TODO: Create a separate HNS interface to place all these methods // Create Endpoint by sending EndpointRequest to HNS. TODO: Create a separate HNS interface to place all these methods
func (endpoint *HNSEndpoint) Create() (*HNSEndpoint, error) { func (endpoint *HNSEndpoint) Create() (*HNSEndpoint, error) {
operation := "Create" operation := "Create"
title := "HCSShim::HNSEndpoint::" + operation title := "HCSShim::HNSEndpoint::" + operation
logrus.Debugf(title+" id=%s", endpoint.Id) logrus.Debugf(title+" id=%s", endpoint.Id)
jsonString, err := json.Marshal(endpoint) jsonString, err := json.Marshal(endpoint)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return HNSEndpointRequest("POST", "", string(jsonString)) return HNSEndpointRequest("POST", "", string(jsonString))
} }
// Delete Endpoint by sending EndpointRequest to HNS // Delete Endpoint by sending EndpointRequest to HNS
func (endpoint *HNSEndpoint) Delete() (*HNSEndpoint, error) { func (endpoint *HNSEndpoint) Delete() (*HNSEndpoint, error) {
operation := "Delete" operation := "Delete"
title := "HCSShim::HNSEndpoint::" + operation title := "HCSShim::HNSEndpoint::" + operation
logrus.Debugf(title+" id=%s", endpoint.Id) logrus.Debugf(title+" id=%s", endpoint.Id)
return HNSEndpointRequest("DELETE", endpoint.Id, "") return HNSEndpointRequest("DELETE", endpoint.Id, "")
} }
// Update Endpoint // Update Endpoint
func (endpoint *HNSEndpoint) Update() (*HNSEndpoint, error) { func (endpoint *HNSEndpoint) Update() (*HNSEndpoint, error) {
operation := "Update" operation := "Update"
title := "HCSShim::HNSEndpoint::" + operation title := "HCSShim::HNSEndpoint::" + operation
logrus.Debugf(title+" id=%s", endpoint.Id) logrus.Debugf(title+" id=%s", endpoint.Id)
jsonString, err := json.Marshal(endpoint) jsonString, err := json.Marshal(endpoint)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = hnsCall("POST", "/endpoints/"+endpoint.Id, string(jsonString), &endpoint) err = hnsCall("POST", "/endpoints/"+endpoint.Id, string(jsonString), &endpoint)
return endpoint, err return endpoint, err
} }
// ContainerHotAttach attaches an endpoint to a running container // ContainerHotAttach attaches an endpoint to a running container
func (endpoint *HNSEndpoint) ContainerHotAttach(containerID string) error { func (endpoint *HNSEndpoint) ContainerHotAttach(containerID string) error {
operation := "ContainerHotAttach" operation := "ContainerHotAttach"
title := "HCSShim::HNSEndpoint::" + operation title := "HCSShim::HNSEndpoint::" + operation
logrus.Debugf(title+" id=%s, containerId=%s", endpoint.Id, containerID) logrus.Debugf(title+" id=%s, containerId=%s", endpoint.Id, containerID)
return modifyNetworkEndpoint(containerID, endpoint.Id, Add) return modifyNetworkEndpoint(containerID, endpoint.Id, Add)
} }
// ContainerHotDetach detaches an endpoint from a running container // ContainerHotDetach detaches an endpoint from a running container
func (endpoint *HNSEndpoint) ContainerHotDetach(containerID string) error { func (endpoint *HNSEndpoint) ContainerHotDetach(containerID string) error {
operation := "ContainerHotDetach" operation := "ContainerHotDetach"
title := "HCSShim::HNSEndpoint::" + operation title := "HCSShim::HNSEndpoint::" + operation
logrus.Debugf(title+" id=%s, containerId=%s", endpoint.Id, containerID) logrus.Debugf(title+" id=%s, containerId=%s", endpoint.Id, containerID)
return modifyNetworkEndpoint(containerID, endpoint.Id, Remove) return modifyNetworkEndpoint(containerID, endpoint.Id, Remove)
} }
// ApplyACLPolicy applies a set of ACL Policies on the Endpoint // ApplyACLPolicy applies a set of ACL Policies on the Endpoint
func (endpoint *HNSEndpoint) ApplyACLPolicy(policies ...*ACLPolicy) error { func (endpoint *HNSEndpoint) ApplyACLPolicy(policies ...*ACLPolicy) error {
operation := "ApplyACLPolicy" operation := "ApplyACLPolicy"
title := "HCSShim::HNSEndpoint::" + operation title := "HCSShim::HNSEndpoint::" + operation
logrus.Debugf(title+" id=%s", endpoint.Id) logrus.Debugf(title+" id=%s", endpoint.Id)
for _, policy := range policies { for _, policy := range policies {
if policy == nil { if policy == nil {
continue continue
} }
jsonString, err := json.Marshal(policy) jsonString, err := json.Marshal(policy)
if err != nil { if err != nil {
return err return err
} }
endpoint.Policies = append(endpoint.Policies, jsonString) endpoint.Policies = append(endpoint.Policies, jsonString)
} }
_, err := endpoint.Update() _, err := endpoint.Update()
return err return err
} }
// ContainerAttach attaches an endpoint to container // ContainerAttach attaches an endpoint to container
func (endpoint *HNSEndpoint) ContainerAttach(containerID string, compartmentID uint16) error { func (endpoint *HNSEndpoint) ContainerAttach(containerID string, compartmentID uint16) error {
operation := "ContainerAttach" operation := "ContainerAttach"
title := "HCSShim::HNSEndpoint::" + operation title := "HCSShim::HNSEndpoint::" + operation
logrus.Debugf(title+" id=%s", endpoint.Id) logrus.Debugf(title+" id=%s", endpoint.Id)
requestMessage := &EndpointAttachDetachRequest{ requestMessage := &EndpointAttachDetachRequest{
ContainerID: containerID, ContainerID: containerID,
CompartmentID: compartmentID, CompartmentID: compartmentID,
SystemType: ContainerType, SystemType: ContainerType,
} }
response := &EndpointResquestResponse{} response := &EndpointResquestResponse{}
jsonString, err := json.Marshal(requestMessage) jsonString, err := json.Marshal(requestMessage)
if err != nil { if err != nil {
return err return err
} }
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
} }
// ContainerDetach detaches an endpoint from container // ContainerDetach detaches an endpoint from container
func (endpoint *HNSEndpoint) ContainerDetach(containerID string) error { func (endpoint *HNSEndpoint) ContainerDetach(containerID string) error {
operation := "ContainerDetach" operation := "ContainerDetach"
title := "HCSShim::HNSEndpoint::" + operation title := "HCSShim::HNSEndpoint::" + operation
logrus.Debugf(title+" id=%s", endpoint.Id) logrus.Debugf(title+" id=%s", endpoint.Id)
requestMessage := &EndpointAttachDetachRequest{ requestMessage := &EndpointAttachDetachRequest{
ContainerID: containerID, ContainerID: containerID,
SystemType: ContainerType, SystemType: ContainerType,
} }
response := &EndpointResquestResponse{} response := &EndpointResquestResponse{}
jsonString, err := json.Marshal(requestMessage) jsonString, err := json.Marshal(requestMessage)
if err != nil { if err != nil {
return err return err
} }
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response) return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
} }
// HostAttach attaches a nic on the host // HostAttach attaches a nic on the host
func (endpoint *HNSEndpoint) HostAttach(compartmentID uint16) error { func (endpoint *HNSEndpoint) HostAttach(compartmentID uint16) error {
operation := "HostAttach" operation := "HostAttach"
title := "HCSShim::HNSEndpoint::" + operation title := "HCSShim::HNSEndpoint::" + operation
logrus.Debugf(title+" id=%s", endpoint.Id) logrus.Debugf(title+" id=%s", endpoint.Id)
requestMessage := &EndpointAttachDetachRequest{ requestMessage := &EndpointAttachDetachRequest{
CompartmentID: compartmentID, CompartmentID: compartmentID,
SystemType: HostType, SystemType: HostType,
} }
response := &EndpointResquestResponse{} response := &EndpointResquestResponse{}
jsonString, err := json.Marshal(requestMessage) jsonString, err := json.Marshal(requestMessage)
if err != nil { if err != nil {
return err return err
} }
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
} }
// HostDetach detaches a nic on the host // HostDetach detaches a nic on the host
func (endpoint *HNSEndpoint) HostDetach() error { func (endpoint *HNSEndpoint) HostDetach() error {
operation := "HostDetach" operation := "HostDetach"
title := "HCSShim::HNSEndpoint::" + operation title := "HCSShim::HNSEndpoint::" + operation
logrus.Debugf(title+" id=%s", endpoint.Id) logrus.Debugf(title+" id=%s", endpoint.Id)
requestMessage := &EndpointAttachDetachRequest{ requestMessage := &EndpointAttachDetachRequest{
SystemType: HostType, SystemType: HostType,
} }
response := &EndpointResquestResponse{} response := &EndpointResquestResponse{}
jsonString, err := json.Marshal(requestMessage) jsonString, err := json.Marshal(requestMessage)
if err != nil { if err != nil {
return err return err
} }
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response) return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
} }
// VirtualMachineNICAttach attaches a endpoint to a virtual machine // VirtualMachineNICAttach attaches a endpoint to a virtual machine
func (endpoint *HNSEndpoint) VirtualMachineNICAttach(virtualMachineNICName string) error { func (endpoint *HNSEndpoint) VirtualMachineNICAttach(virtualMachineNICName string) error {
operation := "VirtualMachineNicAttach" operation := "VirtualMachineNicAttach"
title := "HCSShim::HNSEndpoint::" + operation title := "HCSShim::HNSEndpoint::" + operation
logrus.Debugf(title+" id=%s", endpoint.Id) logrus.Debugf(title+" id=%s", endpoint.Id)
requestMessage := &EndpointAttachDetachRequest{ requestMessage := &EndpointAttachDetachRequest{
VirtualNICName: virtualMachineNICName, VirtualNICName: virtualMachineNICName,
SystemType: VirtualMachineType, SystemType: VirtualMachineType,
} }
response := &EndpointResquestResponse{} response := &EndpointResquestResponse{}
jsonString, err := json.Marshal(requestMessage) jsonString, err := json.Marshal(requestMessage)
if err != nil { if err != nil {
return err return err
} }
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
} }
// VirtualMachineNICDetach detaches a endpoint from a virtual machine // VirtualMachineNICDetach detaches a endpoint from a virtual machine
func (endpoint *HNSEndpoint) VirtualMachineNICDetach() error { func (endpoint *HNSEndpoint) VirtualMachineNICDetach() error {
operation := "VirtualMachineNicDetach" operation := "VirtualMachineNicDetach"
title := "HCSShim::HNSEndpoint::" + operation title := "HCSShim::HNSEndpoint::" + operation
logrus.Debugf(title+" id=%s", endpoint.Id) logrus.Debugf(title+" id=%s", endpoint.Id)
requestMessage := &EndpointAttachDetachRequest{ requestMessage := &EndpointAttachDetachRequest{
SystemType: VirtualMachineType, SystemType: VirtualMachineType,
} }
response := &EndpointResquestResponse{} response := &EndpointResquestResponse{}
jsonString, err := json.Marshal(requestMessage) jsonString, err := json.Marshal(requestMessage)
if err != nil { if err != nil {
return err return err
} }
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response) return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
} }

View File

@ -1,141 +1,141 @@
package hcsshim package hcsshim
import ( import (
"encoding/json" "encoding/json"
"net" "net"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
// Subnet is assoicated with a network and represents a list // Subnet is assoicated with a network and represents a list
// of subnets available to the network // of subnets available to the network
type Subnet struct { type Subnet struct {
AddressPrefix string `json:",omitempty"` AddressPrefix string `json:",omitempty"`
GatewayAddress string `json:",omitempty"` GatewayAddress string `json:",omitempty"`
Policies []json.RawMessage `json:",omitempty"` Policies []json.RawMessage `json:",omitempty"`
} }
// MacPool is assoicated with a network and represents a list // MacPool is assoicated with a network and represents a list
// of macaddresses available to the network // of macaddresses available to the network
type MacPool struct { type MacPool struct {
StartMacAddress string `json:",omitempty"` StartMacAddress string `json:",omitempty"`
EndMacAddress string `json:",omitempty"` EndMacAddress string `json:",omitempty"`
} }
// HNSNetwork represents a network in HNS // HNSNetwork represents a network in HNS
type HNSNetwork struct { type HNSNetwork struct {
Id string `json:"ID,omitempty"` Id string `json:"ID,omitempty"`
Name string `json:",omitempty"` Name string `json:",omitempty"`
Type string `json:",omitempty"` Type string `json:",omitempty"`
NetworkAdapterName string `json:",omitempty"` NetworkAdapterName string `json:",omitempty"`
SourceMac string `json:",omitempty"` SourceMac string `json:",omitempty"`
Policies []json.RawMessage `json:",omitempty"` Policies []json.RawMessage `json:",omitempty"`
MacPools []MacPool `json:",omitempty"` MacPools []MacPool `json:",omitempty"`
Subnets []Subnet `json:",omitempty"` Subnets []Subnet `json:",omitempty"`
DNSSuffix string `json:",omitempty"` DNSSuffix string `json:",omitempty"`
DNSServerList string `json:",omitempty"` DNSServerList string `json:",omitempty"`
DNSServerCompartment uint32 `json:",omitempty"` DNSServerCompartment uint32 `json:",omitempty"`
ManagementIP string `json:",omitempty"` ManagementIP string `json:",omitempty"`
AutomaticDNS bool `json:",omitempty"` AutomaticDNS bool `json:",omitempty"`
} }
type hnsNetworkResponse struct { type hnsNetworkResponse struct {
Success bool Success bool
Error string Error string
Output HNSNetwork Output HNSNetwork
} }
type hnsResponse struct { type hnsResponse struct {
Success bool Success bool
Error string Error string
Output json.RawMessage Output json.RawMessage
} }
// HNSNetworkRequest makes a call into HNS to update/query a single network // HNSNetworkRequest makes a call into HNS to update/query a single network
func HNSNetworkRequest(method, path, request string) (*HNSNetwork, error) { func HNSNetworkRequest(method, path, request string) (*HNSNetwork, error) {
var network HNSNetwork var network HNSNetwork
err := hnsCall(method, "/networks/"+path, request, &network) err := hnsCall(method, "/networks/"+path, request, &network)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &network, nil return &network, nil
} }
// HNSListNetworkRequest makes a HNS call to query the list of available networks // HNSListNetworkRequest makes a HNS call to query the list of available networks
func HNSListNetworkRequest(method, path, request string) ([]HNSNetwork, error) { func HNSListNetworkRequest(method, path, request string) ([]HNSNetwork, error) {
var network []HNSNetwork var network []HNSNetwork
err := hnsCall(method, "/networks/"+path, request, &network) err := hnsCall(method, "/networks/"+path, request, &network)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return network, nil return network, nil
} }
// GetHNSNetworkByID // GetHNSNetworkByID
func GetHNSNetworkByID(networkID string) (*HNSNetwork, error) { func GetHNSNetworkByID(networkID string) (*HNSNetwork, error) {
return HNSNetworkRequest("GET", networkID, "") return HNSNetworkRequest("GET", networkID, "")
} }
// GetHNSNetworkName filtered by Name // GetHNSNetworkName filtered by Name
func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) { func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) {
hsnnetworks, err := HNSListNetworkRequest("GET", "", "") hsnnetworks, err := HNSListNetworkRequest("GET", "", "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, hnsnetwork := range hsnnetworks { for _, hnsnetwork := range hsnnetworks {
if hnsnetwork.Name == networkName { if hnsnetwork.Name == networkName {
return &hnsnetwork, nil return &hnsnetwork, nil
} }
} }
return nil, NetworkNotFoundError{NetworkName: networkName} return nil, NetworkNotFoundError{NetworkName: networkName}
} }
// Create Network by sending NetworkRequest to HNS. // Create Network by sending NetworkRequest to HNS.
func (network *HNSNetwork) Create() (*HNSNetwork, error) { func (network *HNSNetwork) Create() (*HNSNetwork, error) {
operation := "Create" operation := "Create"
title := "HCSShim::HNSNetwork::" + operation title := "HCSShim::HNSNetwork::" + operation
logrus.Debugf(title+" id=%s", network.Id) logrus.Debugf(title+" id=%s", network.Id)
jsonString, err := json.Marshal(network) jsonString, err := json.Marshal(network)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return HNSNetworkRequest("POST", "", string(jsonString)) return HNSNetworkRequest("POST", "", string(jsonString))
} }
// Delete Network by sending NetworkRequest to HNS // Delete Network by sending NetworkRequest to HNS
func (network *HNSNetwork) Delete() (*HNSNetwork, error) { func (network *HNSNetwork) Delete() (*HNSNetwork, error) {
operation := "Delete" operation := "Delete"
title := "HCSShim::HNSNetwork::" + operation title := "HCSShim::HNSNetwork::" + operation
logrus.Debugf(title+" id=%s", network.Id) logrus.Debugf(title+" id=%s", network.Id)
return HNSNetworkRequest("DELETE", network.Id, "") return HNSNetworkRequest("DELETE", network.Id, "")
} }
// Creates an endpoint on the Network. // Creates an endpoint on the Network.
func (network *HNSNetwork) NewEndpoint(ipAddress net.IP, macAddress net.HardwareAddr) *HNSEndpoint { func (network *HNSNetwork) NewEndpoint(ipAddress net.IP, macAddress net.HardwareAddr) *HNSEndpoint {
return &HNSEndpoint{ return &HNSEndpoint{
VirtualNetwork: network.Id, VirtualNetwork: network.Id,
IPAddress: ipAddress, IPAddress: ipAddress,
MacAddress: string(macAddress), MacAddress: string(macAddress),
} }
} }
func (network *HNSNetwork) CreateEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) { func (network *HNSNetwork) CreateEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) {
operation := "CreateEndpoint" operation := "CreateEndpoint"
title := "HCSShim::HNSNetwork::" + operation title := "HCSShim::HNSNetwork::" + operation
logrus.Debugf(title+" id=%s, endpointId=%s", network.Id, endpoint.Id) logrus.Debugf(title+" id=%s, endpointId=%s", network.Id, endpoint.Id)
endpoint.VirtualNetwork = network.Id endpoint.VirtualNetwork = network.Id
return endpoint.Create() return endpoint.Create()
} }
func (network *HNSNetwork) CreateRemoteEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) { func (network *HNSNetwork) CreateRemoteEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) {
operation := "CreateRemoteEndpoint" operation := "CreateRemoteEndpoint"
title := "HCSShim::HNSNetwork::" + operation title := "HCSShim::HNSNetwork::" + operation
logrus.Debugf(title+" id=%s", network.Id) logrus.Debugf(title+" id=%s", network.Id)
endpoint.IsRemoteEndpoint = true endpoint.IsRemoteEndpoint = true
return network.CreateEndpoint(endpoint) return network.CreateEndpoint(endpoint)
} }

View File

@ -1,94 +1,94 @@
package hcsshim package hcsshim
// Type of Request Support in ModifySystem // Type of Request Support in ModifySystem
type PolicyType string type PolicyType string
// RequestType const // RequestType const
const ( const (
Nat PolicyType = "NAT" Nat PolicyType = "NAT"
ACL PolicyType = "ACL" ACL PolicyType = "ACL"
PA PolicyType = "PA" PA PolicyType = "PA"
VLAN PolicyType = "VLAN" VLAN PolicyType = "VLAN"
VSID PolicyType = "VSID" VSID PolicyType = "VSID"
VNet PolicyType = "VNET" VNet PolicyType = "VNET"
L2Driver PolicyType = "L2Driver" L2Driver PolicyType = "L2Driver"
Isolation PolicyType = "Isolation" Isolation PolicyType = "Isolation"
QOS PolicyType = "QOS" QOS PolicyType = "QOS"
OutboundNat PolicyType = "OutBoundNAT" OutboundNat PolicyType = "OutBoundNAT"
ExternalLoadBalancer PolicyType = "ELB" ExternalLoadBalancer PolicyType = "ELB"
Route PolicyType = "ROUTE" Route PolicyType = "ROUTE"
) )
type NatPolicy struct { type NatPolicy struct {
Type PolicyType `json:"Type"` Type PolicyType `json:"Type"`
Protocol string Protocol string
InternalPort uint16 InternalPort uint16
ExternalPort uint16 ExternalPort uint16
} }
type QosPolicy struct { type QosPolicy struct {
Type PolicyType `json:"Type"` Type PolicyType `json:"Type"`
MaximumOutgoingBandwidthInBytes uint64 MaximumOutgoingBandwidthInBytes uint64
} }
type IsolationPolicy struct { type IsolationPolicy struct {
Type PolicyType `json:"Type"` Type PolicyType `json:"Type"`
VLAN uint VLAN uint
VSID uint VSID uint
InDefaultIsolation bool InDefaultIsolation bool
} }
type VlanPolicy struct { type VlanPolicy struct {
Type PolicyType `json:"Type"` Type PolicyType `json:"Type"`
VLAN uint VLAN uint
} }
type VsidPolicy struct { type VsidPolicy struct {
Type PolicyType `json:"Type"` Type PolicyType `json:"Type"`
VSID uint VSID uint
} }
type PaPolicy struct { type PaPolicy struct {
Type PolicyType `json:"Type"` Type PolicyType `json:"Type"`
PA string `json:"PA"` PA string `json:"PA"`
} }
type OutboundNatPolicy struct { type OutboundNatPolicy struct {
Policy Policy
VIP string `json:"VIP,omitempty"` VIP string `json:"VIP,omitempty"`
Exceptions []string `json:"ExceptionList,omitempty"` Exceptions []string `json:"ExceptionList,omitempty"`
} }
type ActionType string type ActionType string
type DirectionType string type DirectionType string
type RuleType string type RuleType string
const ( const (
Allow ActionType = "Allow" Allow ActionType = "Allow"
Block ActionType = "Block" Block ActionType = "Block"
In DirectionType = "In" In DirectionType = "In"
Out DirectionType = "Out" Out DirectionType = "Out"
Host RuleType = "Host" Host RuleType = "Host"
Switch RuleType = "Switch" Switch RuleType = "Switch"
) )
type ACLPolicy struct { type ACLPolicy struct {
Type PolicyType `json:"Type"` Type PolicyType `json:"Type"`
Protocol uint16 Protocol uint16
InternalPort uint16 InternalPort uint16
Action ActionType Action ActionType
Direction DirectionType Direction DirectionType
LocalAddresses string LocalAddresses string
RemoteAddresses string RemoteAddresses string
LocalPort uint16 LocalPort uint16
RemotePort uint16 RemotePort uint16
RuleType RuleType `json:"RuleType,omitempty"` RuleType RuleType `json:"RuleType,omitempty"`
Priority uint16 Priority uint16
ServiceName string ServiceName string
} }
type Policy struct { type Policy struct {
Type PolicyType `json:"Type"` Type PolicyType `json:"Type"`
} }

View File

@ -1,200 +1,200 @@
package hcsshim package hcsshim
import ( import (
"encoding/json" "encoding/json"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
// RoutePolicy is a structure defining schema for Route based Policy // RoutePolicy is a structure defining schema for Route based Policy
type RoutePolicy struct { type RoutePolicy struct {
Policy Policy
DestinationPrefix string `json:"DestinationPrefix,omitempty"` DestinationPrefix string `json:"DestinationPrefix,omitempty"`
NextHop string `json:"NextHop,omitempty"` NextHop string `json:"NextHop,omitempty"`
EncapEnabled bool `json:"NeedEncap,omitempty"` EncapEnabled bool `json:"NeedEncap,omitempty"`
} }
// ELBPolicy is a structure defining schema for ELB LoadBalancing based Policy // ELBPolicy is a structure defining schema for ELB LoadBalancing based Policy
type ELBPolicy struct { type ELBPolicy struct {
LBPolicy LBPolicy
SourceVIP string `json:"SourceVIP,omitempty"` SourceVIP string `json:"SourceVIP,omitempty"`
VIPs []string `json:"VIPs,omitempty"` VIPs []string `json:"VIPs,omitempty"`
ILB bool `json:"ILB,omitempty"` ILB bool `json:"ILB,omitempty"`
} }
// LBPolicy is a structure defining schema for LoadBalancing based Policy // LBPolicy is a structure defining schema for LoadBalancing based Policy
type LBPolicy struct { type LBPolicy struct {
Policy Policy
Protocol uint16 `json:"Protocol,omitempty"` Protocol uint16 `json:"Protocol,omitempty"`
InternalPort uint16 InternalPort uint16
ExternalPort uint16 ExternalPort uint16
} }
// PolicyList is a structure defining schema for Policy list request // PolicyList is a structure defining schema for Policy list request
type PolicyList struct { type PolicyList struct {
ID string `json:"ID,omitempty"` ID string `json:"ID,omitempty"`
EndpointReferences []string `json:"References,omitempty"` EndpointReferences []string `json:"References,omitempty"`
Policies []json.RawMessage `json:"Policies,omitempty"` Policies []json.RawMessage `json:"Policies,omitempty"`
} }
// HNSPolicyListRequest makes a call into HNS to update/query a single network // HNSPolicyListRequest makes a call into HNS to update/query a single network
func HNSPolicyListRequest(method, path, request string) (*PolicyList, error) { func HNSPolicyListRequest(method, path, request string) (*PolicyList, error) {
var policy PolicyList var policy PolicyList
err := hnsCall(method, "/policylists/"+path, request, &policy) err := hnsCall(method, "/policylists/"+path, request, &policy)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &policy, nil return &policy, nil
} }
// HNSListPolicyListRequest gets all the policy list // HNSListPolicyListRequest gets all the policy list
func HNSListPolicyListRequest() ([]PolicyList, error) { func HNSListPolicyListRequest() ([]PolicyList, error) {
var plist []PolicyList var plist []PolicyList
err := hnsCall("GET", "/policylists/", "", &plist) err := hnsCall("GET", "/policylists/", "", &plist)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return plist, nil return plist, nil
} }
// PolicyListRequest makes a HNS call to modify/query a network policy list // PolicyListRequest makes a HNS call to modify/query a network policy list
func PolicyListRequest(method, path, request string) (*PolicyList, error) { func PolicyListRequest(method, path, request string) (*PolicyList, error) {
policylist := &PolicyList{} policylist := &PolicyList{}
err := hnsCall(method, "/policylists/"+path, request, &policylist) err := hnsCall(method, "/policylists/"+path, request, &policylist)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return policylist, nil return policylist, nil
} }
// GetPolicyListByID get the policy list by ID // GetPolicyListByID get the policy list by ID
func GetPolicyListByID(policyListID string) (*PolicyList, error) { func GetPolicyListByID(policyListID string) (*PolicyList, error) {
return PolicyListRequest("GET", policyListID, "") return PolicyListRequest("GET", policyListID, "")
} }
// Create PolicyList by sending PolicyListRequest to HNS. // Create PolicyList by sending PolicyListRequest to HNS.
func (policylist *PolicyList) Create() (*PolicyList, error) { func (policylist *PolicyList) Create() (*PolicyList, error) {
operation := "Create" operation := "Create"
title := "HCSShim::PolicyList::" + operation title := "HCSShim::PolicyList::" + operation
logrus.Debugf(title+" id=%s", policylist.ID) logrus.Debugf(title+" id=%s", policylist.ID)
jsonString, err := json.Marshal(policylist) jsonString, err := json.Marshal(policylist)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return PolicyListRequest("POST", "", string(jsonString)) return PolicyListRequest("POST", "", string(jsonString))
} }
// Delete deletes PolicyList // Delete deletes PolicyList
func (policylist *PolicyList) Delete() (*PolicyList, error) { func (policylist *PolicyList) Delete() (*PolicyList, error) {
operation := "Delete" operation := "Delete"
title := "HCSShim::PolicyList::" + operation title := "HCSShim::PolicyList::" + operation
logrus.Debugf(title+" id=%s", policylist.ID) logrus.Debugf(title+" id=%s", policylist.ID)
return PolicyListRequest("DELETE", policylist.ID, "") return PolicyListRequest("DELETE", policylist.ID, "")
} }
// AddEndpoint add an endpoint to a Policy List // AddEndpoint add an endpoint to a Policy List
func (policylist *PolicyList) AddEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) { func (policylist *PolicyList) AddEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) {
operation := "AddEndpoint" operation := "AddEndpoint"
title := "HCSShim::PolicyList::" + operation title := "HCSShim::PolicyList::" + operation
logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id) logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id)
_, err := policylist.Delete() _, err := policylist.Delete()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Add Endpoint to the Existing List // Add Endpoint to the Existing List
policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id) policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id)
return policylist.Create() return policylist.Create()
} }
// RemoveEndpoint removes an endpoint from the Policy List // RemoveEndpoint removes an endpoint from the Policy List
func (policylist *PolicyList) RemoveEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) { func (policylist *PolicyList) RemoveEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) {
operation := "RemoveEndpoint" operation := "RemoveEndpoint"
title := "HCSShim::PolicyList::" + operation title := "HCSShim::PolicyList::" + operation
logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id) logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id)
_, err := policylist.Delete() _, err := policylist.Delete()
if err != nil { if err != nil {
return nil, err return nil, err
} }
elementToRemove := "/endpoints/" + endpoint.Id elementToRemove := "/endpoints/" + endpoint.Id
var references []string var references []string
for _, endpointReference := range policylist.EndpointReferences { for _, endpointReference := range policylist.EndpointReferences {
if endpointReference == elementToRemove { if endpointReference == elementToRemove {
continue continue
} }
references = append(references, endpointReference) references = append(references, endpointReference)
} }
policylist.EndpointReferences = references policylist.EndpointReferences = references
return policylist.Create() return policylist.Create()
} }
// AddLoadBalancer policy list for the specified endpoints // AddLoadBalancer policy list for the specified endpoints
func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) { func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) {
operation := "AddLoadBalancer" operation := "AddLoadBalancer"
title := "HCSShim::PolicyList::" + operation title := "HCSShim::PolicyList::" + operation
logrus.Debugf(title+" endpointId=%v, isILB=%v, sourceVIP=%s, vip=%s, protocol=%v, internalPort=%v, externalPort=%v", endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort) logrus.Debugf(title+" endpointId=%v, isILB=%v, sourceVIP=%s, vip=%s, protocol=%v, internalPort=%v, externalPort=%v", endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort)
policylist := &PolicyList{} policylist := &PolicyList{}
elbPolicy := &ELBPolicy{ elbPolicy := &ELBPolicy{
SourceVIP: sourceVIP, SourceVIP: sourceVIP,
ILB: isILB, ILB: isILB,
} }
if len(vip) > 0 { if len(vip) > 0 {
elbPolicy.VIPs = []string{vip} elbPolicy.VIPs = []string{vip}
} }
elbPolicy.Type = ExternalLoadBalancer elbPolicy.Type = ExternalLoadBalancer
elbPolicy.Protocol = protocol elbPolicy.Protocol = protocol
elbPolicy.InternalPort = internalPort elbPolicy.InternalPort = internalPort
elbPolicy.ExternalPort = externalPort elbPolicy.ExternalPort = externalPort
for _, endpoint := range endpoints { for _, endpoint := range endpoints {
policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id) policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id)
} }
jsonString, err := json.Marshal(elbPolicy) jsonString, err := json.Marshal(elbPolicy)
if err != nil { if err != nil {
return nil, err return nil, err
} }
policylist.Policies = append(policylist.Policies, jsonString) policylist.Policies = append(policylist.Policies, jsonString)
return policylist.Create() return policylist.Create()
} }
// AddRoute adds route policy list for the specified endpoints // AddRoute adds route policy list for the specified endpoints
func AddRoute(endpoints []HNSEndpoint, destinationPrefix string, nextHop string, encapEnabled bool) (*PolicyList, error) { func AddRoute(endpoints []HNSEndpoint, destinationPrefix string, nextHop string, encapEnabled bool) (*PolicyList, error) {
operation := "AddRoute" operation := "AddRoute"
title := "HCSShim::PolicyList::" + operation title := "HCSShim::PolicyList::" + operation
logrus.Debugf(title+" destinationPrefix:%s", destinationPrefix) logrus.Debugf(title+" destinationPrefix:%s", destinationPrefix)
policylist := &PolicyList{} policylist := &PolicyList{}
rPolicy := &RoutePolicy{ rPolicy := &RoutePolicy{
DestinationPrefix: destinationPrefix, DestinationPrefix: destinationPrefix,
NextHop: nextHop, NextHop: nextHop,
EncapEnabled: encapEnabled, EncapEnabled: encapEnabled,
} }
rPolicy.Type = Route rPolicy.Type = Route
for _, endpoint := range endpoints { for _, endpoint := range endpoints {
policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id) policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id)
} }
jsonString, err := json.Marshal(rPolicy) jsonString, err := json.Marshal(rPolicy)
if err != nil { if err != nil {
return nil, err return nil, err
} }
policylist.Policies = append(policylist.Policies, jsonString) policylist.Policies = append(policylist.Policies, jsonString)
return policylist.Create() return policylist.Create()
} }

View File

@ -129,37 +129,39 @@ type legacyLayerWriterWrapper struct {
} }
func (r *legacyLayerWriterWrapper) Close() error { func (r *legacyLayerWriterWrapper) Close() error {
defer os.RemoveAll(r.root) defer os.RemoveAll(r.root.Name())
defer r.legacyLayerWriter.CloseRoots()
err := r.legacyLayerWriter.Close() err := r.legacyLayerWriter.Close()
if err != nil { if err != nil {
return err return err
} }
// Use the original path here because ImportLayer does not support long paths for the source in TP5.
// But do use a long path for the destination to work around another bug with directories
// with MAX_PATH - 12 < length < MAX_PATH.
info := r.info info := r.info
fullPath, err := makeLongAbsPath(filepath.Join(info.HomeDir, r.layerID)) info.HomeDir = ""
if err != nil { if err = ImportLayer(info, r.destRoot.Name(), r.path, r.parentLayerPaths); err != nil {
return err return err
} }
for _, name := range r.Tombstones {
info.HomeDir = "" if err = removeRelative(name, r.destRoot); err != nil && !os.IsNotExist(err) {
if err = ImportLayer(info, fullPath, r.path, r.parentLayerPaths); err != nil { return err
return err }
} }
// Add any hard links that were collected. // Add any hard links that were collected.
for _, lnk := range r.PendingLinks { for _, lnk := range r.PendingLinks {
if err = os.Remove(lnk.Path); err != nil && !os.IsNotExist(err) { if err = removeRelative(lnk.Path, r.destRoot); err != nil && !os.IsNotExist(err) {
return err return err
} }
if err = os.Link(lnk.Target, lnk.Path); err != nil { if err = linkRelative(lnk.Target, lnk.TargetRoot, lnk.Path, r.destRoot); err != nil {
return err return err
} }
} }
// Prepare the utility VM for use if one is present in the layer. // Prepare the utility VM for use if one is present in the layer.
if r.HasUtilityVM { if r.HasUtilityVM {
err = ProcessUtilityVMImage(filepath.Join(fullPath, "UtilityVM")) err := ensureNotReparsePointRelative("UtilityVM", r.destRoot)
if err != nil {
return err
}
err = ProcessUtilityVMImage(filepath.Join(r.destRoot.Name(), "UtilityVM"))
if err != nil { if err != nil {
return err return err
} }
@ -173,8 +175,12 @@ func (r *legacyLayerWriterWrapper) Close() error {
func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string) (LayerWriter, error) { func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string) (LayerWriter, error) {
if len(parentLayerPaths) == 0 { if len(parentLayerPaths) == 0 {
// This is a base layer. It gets imported differently. // This is a base layer. It gets imported differently.
f, err := openRoot(filepath.Join(info.HomeDir, layerID))
if err != nil {
return nil, err
}
return &baseLayerWriter{ return &baseLayerWriter{
root: filepath.Join(info.HomeDir, layerID), root: f,
}, nil }, nil
} }
@ -185,8 +191,12 @@ func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string)
if err != nil { if err != nil {
return nil, err return nil, err
} }
w, err := newLegacyLayerWriter(path, parentLayerPaths, filepath.Join(info.HomeDir, layerID))
if err != nil {
return nil, err
}
return &legacyLayerWriterWrapper{ return &legacyLayerWriterWrapper{
legacyLayerWriter: newLegacyLayerWriter(path, parentLayerPaths, filepath.Join(info.HomeDir, layerID)), legacyLayerWriter: w,
info: info, info: info,
layerID: layerID, layerID: layerID,
path: path, path: path,

View File

@ -121,6 +121,16 @@ func (r *legacyLayerReader) walkUntilCancelled() error {
if err != nil { if err != nil {
return err return err
} }
// Indirect fix for https://github.com/moby/moby/issues/32838#issuecomment-343610048.
// Handle failure from what may be a golang bug in the conversion of
// UTF16 to UTF8 in files which are left in the recycle bin. Os.Lstat
// which is called by filepath.Walk will fail when a filename contains
// unicode characters. Skip the recycle bin regardless which is goodness.
if path == filepath.Join(r.root, `Files\$Recycle.Bin`) && info.IsDir() {
return filepath.SkipDir
}
if path == r.root || path == filepath.Join(r.root, "tombstones.txt") || strings.HasSuffix(path, ".$wcidirs$") { if path == r.root || path == filepath.Join(r.root, "tombstones.txt") || strings.HasSuffix(path, ".$wcidirs$") {
return nil return nil
} }
@ -326,59 +336,79 @@ func (r *legacyLayerReader) Close() error {
type pendingLink struct { type pendingLink struct {
Path, Target string Path, Target string
TargetRoot *os.File
}
type pendingDir struct {
Path string
Root *os.File
} }
type legacyLayerWriter struct { type legacyLayerWriter struct {
root string root *os.File
parentRoots []string destRoot *os.File
destRoot string parentRoots []*os.File
currentFile *os.File currentFile *os.File
backupWriter *winio.BackupFileWriter currentFileName string
tombstones []string currentFileRoot *os.File
pathFixed bool backupWriter *winio.BackupFileWriter
HasUtilityVM bool Tombstones []string
uvmDi []dirInfo HasUtilityVM bool
addedFiles map[string]bool uvmDi []dirInfo
PendingLinks []pendingLink addedFiles map[string]bool
PendingLinks []pendingLink
pendingDirs []pendingDir
currentIsDir bool
} }
// newLegacyLayerWriter returns a LayerWriter that can write the contaler layer // newLegacyLayerWriter returns a LayerWriter that can write the contaler layer
// transport format to disk. // transport format to disk.
func newLegacyLayerWriter(root string, parentRoots []string, destRoot string) *legacyLayerWriter { func newLegacyLayerWriter(root string, parentRoots []string, destRoot string) (w *legacyLayerWriter, err error) {
return &legacyLayerWriter{ w = &legacyLayerWriter{
root: root, addedFiles: make(map[string]bool),
parentRoots: parentRoots,
destRoot: destRoot,
addedFiles: make(map[string]bool),
} }
defer func() {
if err != nil {
w.CloseRoots()
w = nil
}
}()
w.root, err = openRoot(root)
if err != nil {
return
}
w.destRoot, err = openRoot(destRoot)
if err != nil {
return
}
for _, r := range parentRoots {
f, err := openRoot(r)
if err != nil {
return w, err
}
w.parentRoots = append(w.parentRoots, f)
}
return
} }
func (w *legacyLayerWriter) init() error { func (w *legacyLayerWriter) CloseRoots() {
if !w.pathFixed { if w.root != nil {
path, err := makeLongAbsPath(w.root) w.root.Close()
if err != nil { w.root = nil
return err
}
for i, p := range w.parentRoots {
w.parentRoots[i], err = makeLongAbsPath(p)
if err != nil {
return err
}
}
destPath, err := makeLongAbsPath(w.destRoot)
if err != nil {
return err
}
w.root = path
w.destRoot = destPath
w.pathFixed = true
} }
return nil if w.destRoot != nil {
w.destRoot.Close()
w.destRoot = nil
}
for i := range w.parentRoots {
w.parentRoots[i].Close()
}
w.parentRoots = nil
} }
func (w *legacyLayerWriter) initUtilityVM() error { func (w *legacyLayerWriter) initUtilityVM() error {
if !w.HasUtilityVM { if !w.HasUtilityVM {
err := os.Mkdir(filepath.Join(w.destRoot, utilityVMPath), 0) err := mkdirRelative(utilityVMPath, w.destRoot)
if err != nil { if err != nil {
return err return err
} }
@ -386,7 +416,7 @@ func (w *legacyLayerWriter) initUtilityVM() error {
// clone the utility VM from the parent layer into this layer. Use hard // clone the utility VM from the parent layer into this layer. Use hard
// links to avoid unnecessary copying, since most of the files are // links to avoid unnecessary copying, since most of the files are
// immutable. // immutable.
err = cloneTree(filepath.Join(w.parentRoots[0], utilityVMFilesPath), filepath.Join(w.destRoot, utilityVMFilesPath), mutatedUtilityVMFiles) err = cloneTree(w.parentRoots[0], w.destRoot, utilityVMFilesPath, mutatedUtilityVMFiles)
if err != nil { if err != nil {
return fmt.Errorf("cloning the parent utility VM image failed: %s", err) return fmt.Errorf("cloning the parent utility VM image failed: %s", err)
} }
@ -395,7 +425,40 @@ func (w *legacyLayerWriter) initUtilityVM() error {
return nil return nil
} }
func (w *legacyLayerWriter) reset() { func (w *legacyLayerWriter) reset() error {
if w.currentIsDir {
r := w.currentFile
br := winio.NewBackupStreamReader(r)
// Seek to the beginning of the backup stream, skipping the fileattrs
if _, err := r.Seek(4, io.SeekStart); err != nil {
return err
}
for {
bhdr, err := br.Next()
if err == io.EOF {
// end of backupstream data
break
}
if err != nil {
return err
}
switch bhdr.Id {
case winio.BackupReparseData:
// The current file is a `.$wcidirs$` metadata file that
// describes a directory reparse point. Delete the placeholder
// directory to prevent future files being added into the
// destination of the reparse point during the ImportLayer call
if err := removeRelative(w.currentFileName, w.currentFileRoot); err != nil {
return err
}
w.pendingDirs = append(w.pendingDirs, pendingDir{Path: w.currentFileName, Root: w.currentFileRoot})
default:
// ignore all other stream types, as we only care about directory reparse points
}
}
w.currentIsDir = false
}
if w.backupWriter != nil { if w.backupWriter != nil {
w.backupWriter.Close() w.backupWriter.Close()
w.backupWriter = nil w.backupWriter = nil
@ -403,21 +466,21 @@ func (w *legacyLayerWriter) reset() {
if w.currentFile != nil { if w.currentFile != nil {
w.currentFile.Close() w.currentFile.Close()
w.currentFile = nil w.currentFile = nil
w.currentFileName = ""
w.currentFileRoot = nil
} }
return nil
} }
// copyFileWithMetadata copies a file using the backup/restore APIs in order to preserve metadata // copyFileWithMetadata copies a file using the backup/restore APIs in order to preserve metadata
func copyFileWithMetadata(srcPath, destPath string, isDir bool) (fileInfo *winio.FileBasicInfo, err error) { func copyFileWithMetadata(srcRoot, destRoot *os.File, subPath string, isDir bool) (fileInfo *winio.FileBasicInfo, err error) {
createDisposition := uint32(syscall.CREATE_NEW) src, err := openRelative(
if isDir { subPath,
err = os.Mkdir(destPath, 0) srcRoot,
if err != nil { syscall.GENERIC_READ|winio.ACCESS_SYSTEM_SECURITY,
return nil, err syscall.FILE_SHARE_READ,
} _FILE_OPEN,
createDisposition = syscall.OPEN_EXISTING _FILE_OPEN_REPARSE_POINT)
}
src, err := openFileOrDir(srcPath, syscall.GENERIC_READ|winio.ACCESS_SYSTEM_SECURITY, syscall.OPEN_EXISTING)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -430,7 +493,17 @@ func copyFileWithMetadata(srcPath, destPath string, isDir bool) (fileInfo *winio
return nil, err return nil, err
} }
dest, err := openFileOrDir(destPath, syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, createDisposition) extraFlags := uint32(0)
if isDir {
extraFlags |= _FILE_DIRECTORY_FILE
}
dest, err := openRelative(
subPath,
destRoot,
syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY,
syscall.FILE_SHARE_READ,
_FILE_CREATE,
extraFlags)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -459,18 +532,21 @@ func copyFileWithMetadata(srcPath, destPath string, isDir bool) (fileInfo *winio
// cloneTree clones a directory tree using hard links. It skips hard links for // cloneTree clones a directory tree using hard links. It skips hard links for
// the file names in the provided map and just copies those files. // the file names in the provided map and just copies those files.
func cloneTree(srcPath, destPath string, mutatedFiles map[string]bool) error { func cloneTree(srcRoot *os.File, destRoot *os.File, subPath string, mutatedFiles map[string]bool) error {
var di []dirInfo var di []dirInfo
err := filepath.Walk(srcPath, func(srcFilePath string, info os.FileInfo, err error) error { err := ensureNotReparsePointRelative(subPath, srcRoot)
if err != nil {
return err
}
err = filepath.Walk(filepath.Join(srcRoot.Name(), subPath), func(srcFilePath string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
} }
relPath, err := filepath.Rel(srcPath, srcFilePath) relPath, err := filepath.Rel(srcRoot.Name(), srcFilePath)
if err != nil { if err != nil {
return err return err
} }
destFilePath := filepath.Join(destPath, relPath)
fileAttributes := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes fileAttributes := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes
// Directories, reparse points, and files that will be mutated during // Directories, reparse points, and files that will be mutated during
@ -482,15 +558,15 @@ func cloneTree(srcPath, destPath string, mutatedFiles map[string]bool) error {
isDir := fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 isDir := fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0
if isDir || isReparsePoint || mutatedFiles[relPath] { if isDir || isReparsePoint || mutatedFiles[relPath] {
fi, err := copyFileWithMetadata(srcFilePath, destFilePath, isDir) fi, err := copyFileWithMetadata(srcRoot, destRoot, relPath, isDir)
if err != nil { if err != nil {
return err return err
} }
if isDir && !isReparsePoint { if isDir && !isReparsePoint {
di = append(di, dirInfo{path: destFilePath, fileInfo: *fi}) di = append(di, dirInfo{path: relPath, fileInfo: *fi})
} }
} else { } else {
err = os.Link(srcFilePath, destFilePath) err = linkRelative(relPath, srcRoot, relPath, destRoot)
if err != nil { if err != nil {
return err return err
} }
@ -508,13 +584,11 @@ func cloneTree(srcPath, destPath string, mutatedFiles map[string]bool) error {
return err return err
} }
return reapplyDirectoryTimes(di) return reapplyDirectoryTimes(destRoot, di)
} }
func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error { func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error {
w.reset() if err := w.reset(); err != nil {
err := w.init()
if err != nil {
return err return err
} }
@ -522,6 +596,7 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
return w.initUtilityVM() return w.initUtilityVM()
} }
name = filepath.Clean(name)
if hasPathPrefix(name, utilityVMPath) { if hasPathPrefix(name, utilityVMPath) {
if !w.HasUtilityVM { if !w.HasUtilityVM {
return errors.New("missing UtilityVM directory") return errors.New("missing UtilityVM directory")
@ -529,10 +604,9 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
if !hasPathPrefix(name, utilityVMFilesPath) && name != utilityVMFilesPath { if !hasPathPrefix(name, utilityVMFilesPath) && name != utilityVMFilesPath {
return errors.New("invalid UtilityVM layer") return errors.New("invalid UtilityVM layer")
} }
path := filepath.Join(w.destRoot, name) createDisposition := uint32(_FILE_OPEN)
createDisposition := uint32(syscall.OPEN_EXISTING)
if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 { if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
st, err := os.Lstat(path) st, err := lstatRelative(name, w.destRoot)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return err return err
} }
@ -540,37 +614,44 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
// Delete the existing file/directory if it is not the same type as this directory. // Delete the existing file/directory if it is not the same type as this directory.
existingAttr := st.Sys().(*syscall.Win32FileAttributeData).FileAttributes existingAttr := st.Sys().(*syscall.Win32FileAttributeData).FileAttributes
if (uint32(fileInfo.FileAttributes)^existingAttr)&(syscall.FILE_ATTRIBUTE_DIRECTORY|syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 { if (uint32(fileInfo.FileAttributes)^existingAttr)&(syscall.FILE_ATTRIBUTE_DIRECTORY|syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 {
if err = os.RemoveAll(path); err != nil { if err = removeAllRelative(name, w.destRoot); err != nil {
return err return err
} }
st = nil st = nil
} }
} }
if st == nil { if st == nil {
if err = os.Mkdir(path, 0); err != nil { if err = mkdirRelative(name, w.destRoot); err != nil {
return err return err
} }
} }
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
w.uvmDi = append(w.uvmDi, dirInfo{path: path, fileInfo: *fileInfo}) w.uvmDi = append(w.uvmDi, dirInfo{path: name, fileInfo: *fileInfo})
} }
} else { } else {
// Overwrite any existing hard link. // Overwrite any existing hard link.
err = os.Remove(path) err := removeRelative(name, w.destRoot)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return err return err
} }
createDisposition = syscall.CREATE_NEW createDisposition = _FILE_CREATE
} }
f, err := openFileOrDir(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, createDisposition) f, err := openRelative(
name,
w.destRoot,
syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY,
syscall.FILE_SHARE_READ,
createDisposition,
_FILE_OPEN_REPARSE_POINT,
)
if err != nil { if err != nil {
return err return err
} }
defer func() { defer func() {
if f != nil { if f != nil {
f.Close() f.Close()
os.Remove(path) removeRelative(name, w.destRoot)
} }
}() }()
@ -581,28 +662,31 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
w.backupWriter = winio.NewBackupFileWriter(f, true) w.backupWriter = winio.NewBackupFileWriter(f, true)
w.currentFile = f w.currentFile = f
w.currentFileName = name
w.currentFileRoot = w.destRoot
w.addedFiles[name] = true w.addedFiles[name] = true
f = nil f = nil
return nil return nil
} }
path := filepath.Join(w.root, name) fname := name
if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 { if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
err := os.Mkdir(path, 0) err := mkdirRelative(name, w.root)
if err != nil { if err != nil {
return err return err
} }
path += ".$wcidirs$" fname += ".$wcidirs$"
w.currentIsDir = true
} }
f, err := openFileOrDir(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.CREATE_NEW) f, err := openRelative(fname, w.root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, _FILE_CREATE, 0)
if err != nil { if err != nil {
return err return err
} }
defer func() { defer func() {
if f != nil { if f != nil {
f.Close() f.Close()
os.Remove(path) removeRelative(fname, w.root)
} }
}() }()
@ -624,19 +708,20 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
} }
w.currentFile = f w.currentFile = f
w.currentFileName = name
w.currentFileRoot = w.root
w.addedFiles[name] = true w.addedFiles[name] = true
f = nil f = nil
return nil return nil
} }
func (w *legacyLayerWriter) AddLink(name string, target string) error { func (w *legacyLayerWriter) AddLink(name string, target string) error {
w.reset() if err := w.reset(); err != nil {
err := w.init()
if err != nil {
return err return err
} }
var roots []string target = filepath.Clean(target)
var roots []*os.File
if hasPathPrefix(target, filesPath) { if hasPathPrefix(target, filesPath) {
// Look for cross-layer hard link targets in the parent layers, since // Look for cross-layer hard link targets in the parent layers, since
// nothing is in the destination path yet. // nothing is in the destination path yet.
@ -645,7 +730,7 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
// Since the utility VM is fully cloned into the destination path // Since the utility VM is fully cloned into the destination path
// already, look for cross-layer hard link targets directly in the // already, look for cross-layer hard link targets directly in the
// destination path. // destination path.
roots = []string{w.destRoot} roots = []*os.File{w.destRoot}
} }
if roots == nil || (!hasPathPrefix(name, filesPath) && !hasPathPrefix(name, utilityVMFilesPath)) { if roots == nil || (!hasPathPrefix(name, filesPath) && !hasPathPrefix(name, utilityVMFilesPath)) {
@ -654,12 +739,12 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
// Find to try the target of the link in a previously added file. If that // Find to try the target of the link in a previously added file. If that
// fails, search in parent layers. // fails, search in parent layers.
var selectedRoot string var selectedRoot *os.File
if _, ok := w.addedFiles[target]; ok { if _, ok := w.addedFiles[target]; ok {
selectedRoot = w.destRoot selectedRoot = w.destRoot
} else { } else {
for _, r := range roots { for _, r := range roots {
if _, err = os.Lstat(filepath.Join(r, target)); err != nil { if _, err := lstatRelative(target, r); err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
return err return err
} }
@ -668,22 +753,25 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
break break
} }
} }
if selectedRoot == "" { if selectedRoot == nil {
return fmt.Errorf("failed to find link target for '%s' -> '%s'", name, target) return fmt.Errorf("failed to find link target for '%s' -> '%s'", name, target)
} }
} }
// The link can't be written until after the ImportLayer call. // The link can't be written until after the ImportLayer call.
w.PendingLinks = append(w.PendingLinks, pendingLink{ w.PendingLinks = append(w.PendingLinks, pendingLink{
Path: filepath.Join(w.destRoot, name), Path: name,
Target: filepath.Join(selectedRoot, target), Target: target,
TargetRoot: selectedRoot,
}) })
w.addedFiles[name] = true w.addedFiles[name] = true
return nil return nil
} }
func (w *legacyLayerWriter) Remove(name string) error { func (w *legacyLayerWriter) Remove(name string) error {
name = filepath.Clean(name)
if hasPathPrefix(name, filesPath) { if hasPathPrefix(name, filesPath) {
w.tombstones = append(w.tombstones, name[len(filesPath)+1:]) w.Tombstones = append(w.Tombstones, name)
} else if hasPathPrefix(name, utilityVMFilesPath) { } else if hasPathPrefix(name, utilityVMFilesPath) {
err := w.initUtilityVM() err := w.initUtilityVM()
if err != nil { if err != nil {
@ -692,11 +780,10 @@ func (w *legacyLayerWriter) Remove(name string) error {
// Make sure the path exists; os.RemoveAll will not fail if the file is // Make sure the path exists; os.RemoveAll will not fail if the file is
// already gone, and this needs to be a fatal error for diagnostics // already gone, and this needs to be a fatal error for diagnostics
// purposes. // purposes.
path := filepath.Join(w.destRoot, name) if _, err := lstatRelative(name, w.destRoot); err != nil {
if _, err := os.Lstat(path); err != nil {
return err return err
} }
err = os.RemoveAll(path) err = removeAllRelative(name, w.destRoot)
if err != nil { if err != nil {
return err return err
} }
@ -718,28 +805,20 @@ func (w *legacyLayerWriter) Write(b []byte) (int, error) {
} }
func (w *legacyLayerWriter) Close() error { func (w *legacyLayerWriter) Close() error {
w.reset() if err := w.reset(); err != nil {
err := w.init()
if err != nil {
return err return err
} }
tf, err := os.Create(filepath.Join(w.root, "tombstones.txt")) if err := removeRelative("tombstones.txt", w.root); err != nil && !os.IsNotExist(err) {
if err != nil {
return err return err
} }
defer tf.Close() for _, pd := range w.pendingDirs {
_, err = tf.Write([]byte("\xef\xbb\xbfVersion 1.0\n")) err := mkdirRelative(pd.Path, pd.Root)
if err != nil {
return err
}
for _, t := range w.tombstones {
_, err = tf.Write([]byte(filepath.Join(`\`, t) + "\n"))
if err != nil { if err != nil {
return err return err
} }
} }
if w.HasUtilityVM { if w.HasUtilityVM {
err = reapplyDirectoryTimes(w.uvmDi) err := reapplyDirectoryTimes(w.destRoot, w.uvmDi)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,7 +1,7 @@
// +build !go1.9 // +build !go1.9
package hcsshim package hcsshim
// Due to a bug in go1.8 and before, directory reparse points need to be skipped // Due to a bug in go1.8 and before, directory reparse points need to be skipped
// during filepath.Walk. This is fixed in go1.9 // during filepath.Walk. This is fixed in go1.9
var shouldSkipDirectoryReparse = true var shouldSkipDirectoryReparse = true

View File

@ -1,7 +1,7 @@
// +build go1.9 // +build go1.9
package hcsshim package hcsshim
// Due to a bug in go1.8 and before, directory reparse points need to be skipped // Due to a bug in go1.8 and before, directory reparse points need to be skipped
// during filepath.Walk. This is fixed in go1.9 // during filepath.Walk. This is fixed in go1.9
var shouldSkipDirectoryReparse = false var shouldSkipDirectoryReparse = false

427
vendor/github.com/Microsoft/hcsshim/safeopen.go generated vendored Normal file
View File

@ -0,0 +1,427 @@
package hcsshim
import (
"errors"
"io"
"os"
"path/filepath"
"strings"
"syscall"
"unicode/utf16"
"unsafe"
winio "github.com/Microsoft/go-winio"
)
//sys ntCreateFile(handle *uintptr, accessMask uint32, oa *objectAttributes, iosb *ioStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) = ntdll.NtCreateFile
//sys ntSetInformationFile(handle uintptr, iosb *ioStatusBlock, information uintptr, length uint32, class uint32) (status uint32) = ntdll.NtSetInformationFile
//sys rtlNtStatusToDosError(status uint32) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb
//sys localAlloc(flags uint32, size int) (ptr uintptr) = kernel32.LocalAlloc
//sys localFree(ptr uintptr) = kernel32.LocalFree
type ioStatusBlock struct {
Status, Information uintptr
}
type objectAttributes struct {
Length uintptr
RootDirectory uintptr
ObjectName uintptr
Attributes uintptr
SecurityDescriptor uintptr
SecurityQoS uintptr
}
type unicodeString struct {
Length uint16
MaximumLength uint16
Buffer uintptr
}
type fileLinkInformation struct {
ReplaceIfExists bool
RootDirectory uintptr
FileNameLength uint32
FileName [1]uint16
}
type fileDispositionInformationEx struct {
Flags uintptr
}
const (
_FileLinkInformation = 11
_FileDispositionInformationEx = 64
_FILE_READ_ATTRIBUTES = 0x0080
_FILE_WRITE_ATTRIBUTES = 0x0100
_DELETE = 0x10000
_FILE_OPEN = 1
_FILE_CREATE = 2
_FILE_DIRECTORY_FILE = 0x00000001
_FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020
_FILE_DELETE_ON_CLOSE = 0x00001000
_FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000
_FILE_OPEN_REPARSE_POINT = 0x00200000
_FILE_DISPOSITION_DELETE = 0x00000001
_OBJ_DONT_REPARSE = 0x1000
_STATUS_REPARSE_POINT_ENCOUNTERED = 0xC000050B
)
func openRoot(path string) (*os.File, error) {
longpath, err := makeLongAbsPath(path)
if err != nil {
return nil, err
}
return winio.OpenForBackup(longpath, syscall.GENERIC_READ, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, syscall.OPEN_EXISTING)
}
func ntRelativePath(path string) ([]uint16, error) {
path = filepath.Clean(path)
if strings.Contains(":", path) {
// Since alternate data streams must follow the file they
// are attached to, finding one here (out of order) is invalid.
return nil, errors.New("path contains invalid character `:`")
}
fspath := filepath.FromSlash(path)
if len(fspath) > 0 && fspath[0] == '\\' {
return nil, errors.New("expected relative path")
}
path16 := utf16.Encode(([]rune)(fspath))
if len(path16) > 32767 {
return nil, syscall.ENAMETOOLONG
}
return path16, nil
}
// openRelativeInternal opens a relative path from the given root, failing if
// any of the intermediate path components are reparse points.
func openRelativeInternal(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) {
var (
h uintptr
iosb ioStatusBlock
oa objectAttributes
)
path16, err := ntRelativePath(path)
if err != nil {
return nil, err
}
if root == nil || root.Fd() == 0 {
return nil, errors.New("missing root directory")
}
upathBuffer := localAlloc(0, int(unsafe.Sizeof(unicodeString{}))+len(path16)*2)
defer localFree(upathBuffer)
upath := (*unicodeString)(unsafe.Pointer(upathBuffer))
upath.Length = uint16(len(path16) * 2)
upath.MaximumLength = upath.Length
upath.Buffer = upathBuffer + unsafe.Sizeof(*upath)
copy((*[32768]uint16)(unsafe.Pointer(upath.Buffer))[:], path16)
oa.Length = unsafe.Sizeof(oa)
oa.ObjectName = upathBuffer
oa.RootDirectory = uintptr(root.Fd())
oa.Attributes = _OBJ_DONT_REPARSE
status := ntCreateFile(
&h,
accessMask|syscall.SYNCHRONIZE,
&oa,
&iosb,
nil,
0,
shareFlags,
createDisposition,
_FILE_OPEN_FOR_BACKUP_INTENT|_FILE_SYNCHRONOUS_IO_NONALERT|flags,
nil,
0,
)
if status != 0 {
return nil, rtlNtStatusToDosError(status)
}
fullPath, err := makeLongAbsPath(filepath.Join(root.Name(), path))
if err != nil {
syscall.Close(syscall.Handle(h))
return nil, err
}
return os.NewFile(h, fullPath), nil
}
// openRelative opens a relative path from the given root, failing if
// any of the intermediate path components are reparse points.
func openRelative(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) {
f, err := openRelativeInternal(path, root, accessMask, shareFlags, createDisposition, flags)
if err != nil {
err = &os.PathError{Op: "open", Path: filepath.Join(root.Name(), path), Err: err}
}
return f, err
}
// linkRelative creates a hard link from oldname to newname (relative to oldroot
// and newroot), failing if any of the intermediate path components are reparse
// points.
func linkRelative(oldname string, oldroot *os.File, newname string, newroot *os.File) error {
// Open the old file.
oldf, err := openRelativeInternal(
oldname,
oldroot,
syscall.FILE_WRITE_ATTRIBUTES,
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
_FILE_OPEN,
0,
)
if err != nil {
return &os.LinkError{Op: "link", Old: filepath.Join(oldroot.Name(), oldname), New: filepath.Join(newroot.Name(), newname), Err: err}
}
defer oldf.Close()
// Open the parent of the new file.
var parent *os.File
parentPath := filepath.Dir(newname)
if parentPath != "." {
parent, err = openRelativeInternal(
parentPath,
newroot,
syscall.GENERIC_READ,
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
_FILE_OPEN,
_FILE_DIRECTORY_FILE)
if err != nil {
return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: err}
}
defer parent.Close()
fi, err := winio.GetFileBasicInfo(parent)
if err != nil {
return err
}
if (fi.FileAttributes & syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 {
return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: rtlNtStatusToDosError(_STATUS_REPARSE_POINT_ENCOUNTERED)}
}
} else {
parent = newroot
}
// Issue an NT call to create the link. This will be safe because NT will
// not open any more directories to create the link, so it cannot walk any
// more reparse points.
newbase := filepath.Base(newname)
newbase16, err := ntRelativePath(newbase)
if err != nil {
return err
}
size := int(unsafe.Offsetof(fileLinkInformation{}.FileName)) + len(newbase16)*2
linkinfoBuffer := localAlloc(0, size)
defer localFree(linkinfoBuffer)
linkinfo := (*fileLinkInformation)(unsafe.Pointer(linkinfoBuffer))
linkinfo.RootDirectory = parent.Fd()
linkinfo.FileNameLength = uint32(len(newbase16) * 2)
copy((*[32768]uint16)(unsafe.Pointer(&linkinfo.FileName[0]))[:], newbase16)
var iosb ioStatusBlock
status := ntSetInformationFile(
oldf.Fd(),
&iosb,
linkinfoBuffer,
uint32(size),
_FileLinkInformation,
)
if status != 0 {
return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(parent.Name(), newbase), Err: rtlNtStatusToDosError(status)}
}
return nil
}
// deleteOnClose marks a file to be deleted when the handle is closed.
func deleteOnClose(f *os.File) error {
disposition := fileDispositionInformationEx{Flags: _FILE_DISPOSITION_DELETE}
var iosb ioStatusBlock
status := ntSetInformationFile(
f.Fd(),
&iosb,
uintptr(unsafe.Pointer(&disposition)),
uint32(unsafe.Sizeof(disposition)),
_FileDispositionInformationEx,
)
if status != 0 {
return rtlNtStatusToDosError(status)
}
return nil
}
// clearReadOnly clears the readonly attribute on a file.
func clearReadOnly(f *os.File) error {
bi, err := winio.GetFileBasicInfo(f)
if err != nil {
return err
}
if bi.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY == 0 {
return nil
}
sbi := winio.FileBasicInfo{
FileAttributes: bi.FileAttributes &^ syscall.FILE_ATTRIBUTE_READONLY,
}
if sbi.FileAttributes == 0 {
sbi.FileAttributes = syscall.FILE_ATTRIBUTE_NORMAL
}
return winio.SetFileBasicInfo(f, &sbi)
}
// removeRelative removes a file or directory relative to a root, failing if any
// intermediate path components are reparse points.
func removeRelative(path string, root *os.File) error {
f, err := openRelativeInternal(
path,
root,
_FILE_READ_ATTRIBUTES|_FILE_WRITE_ATTRIBUTES|_DELETE,
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
_FILE_OPEN,
_FILE_OPEN_REPARSE_POINT)
if err == nil {
defer f.Close()
err = deleteOnClose(f)
if err == syscall.ERROR_ACCESS_DENIED {
// Maybe the file is marked readonly. Clear the bit and retry.
clearReadOnly(f)
err = deleteOnClose(f)
}
}
if err != nil {
return &os.PathError{Op: "remove", Path: filepath.Join(root.Name(), path), Err: err}
}
return nil
}
// removeAllRelative removes a directory tree relative to a root, failing if any
// intermediate path components are reparse points.
func removeAllRelative(path string, root *os.File) error {
fi, err := lstatRelative(path, root)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
fileAttributes := fi.Sys().(*syscall.Win32FileAttributeData).FileAttributes
if fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 || fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 {
// If this is a reparse point, it can't have children. Simple remove will do.
err := removeRelative(path, root)
if err == nil || os.IsNotExist(err) {
return nil
}
return err
}
// It is necessary to use os.Open as Readdirnames does not work with
// openRelative. This is safe because the above lstatrelative fails
// if the target is outside the root, and we know this is not a
// symlink from the above FILE_ATTRIBUTE_REPARSE_POINT check.
fd, err := os.Open(filepath.Join(root.Name(), path))
if err != nil {
if os.IsNotExist(err) {
// Race. It was deleted between the Lstat and Open.
// Return nil per RemoveAll's docs.
return nil
}
return err
}
// Remove contents & return first error.
for {
names, err1 := fd.Readdirnames(100)
for _, name := range names {
err1 := removeAllRelative(path+string(os.PathSeparator)+name, root)
if err == nil {
err = err1
}
}
if err1 == io.EOF {
break
}
// If Readdirnames returned an error, use it.
if err == nil {
err = err1
}
if len(names) == 0 {
break
}
}
fd.Close()
// Remove directory.
err1 := removeRelative(path, root)
if err1 == nil || os.IsNotExist(err1) {
return nil
}
if err == nil {
err = err1
}
return err
}
// mkdirRelative creates a directory relative to a root, failing if any
// intermediate path components are reparse points.
func mkdirRelative(path string, root *os.File) error {
f, err := openRelativeInternal(
path,
root,
0,
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
_FILE_CREATE,
_FILE_DIRECTORY_FILE)
if err == nil {
f.Close()
} else {
err = &os.PathError{Op: "mkdir", Path: filepath.Join(root.Name(), path), Err: err}
}
return err
}
// lstatRelative performs a stat operation on a file relative to a root, failing
// if any intermediate path components are reparse points.
func lstatRelative(path string, root *os.File) (os.FileInfo, error) {
f, err := openRelativeInternal(
path,
root,
_FILE_READ_ATTRIBUTES,
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
_FILE_OPEN,
_FILE_OPEN_REPARSE_POINT)
if err != nil {
return nil, &os.PathError{Op: "stat", Path: filepath.Join(root.Name(), path), Err: err}
}
defer f.Close()
return f.Stat()
}
// ensureNotReparsePointRelative validates that a given file (relative to a
// root) and all intermediate path components are not a reparse points.
func ensureNotReparsePointRelative(path string, root *os.File) error {
// Perform an open with OBJ_DONT_REPARSE but without specifying FILE_OPEN_REPARSE_POINT.
f, err := openRelative(
path,
root,
0,
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
_FILE_OPEN,
0)
if err != nil {
return err
}
f.Close()
return nil
}

View File

@ -41,6 +41,8 @@ var (
modole32 = windows.NewLazySystemDLL("ole32.dll") modole32 = windows.NewLazySystemDLL("ole32.dll")
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") modvmcompute = windows.NewLazySystemDLL("vmcompute.dll")
modntdll = windows.NewLazySystemDLL("ntdll.dll")
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
procCoTaskMemFree = modole32.NewProc("CoTaskMemFree") procCoTaskMemFree = modole32.NewProc("CoTaskMemFree")
procSetCurrentThreadCompartmentId = modiphlpapi.NewProc("SetCurrentThreadCompartmentId") procSetCurrentThreadCompartmentId = modiphlpapi.NewProc("SetCurrentThreadCompartmentId")
@ -94,6 +96,11 @@ var (
procHcsUnregisterProcessCallback = modvmcompute.NewProc("HcsUnregisterProcessCallback") procHcsUnregisterProcessCallback = modvmcompute.NewProc("HcsUnregisterProcessCallback")
procHcsModifyServiceSettings = modvmcompute.NewProc("HcsModifyServiceSettings") procHcsModifyServiceSettings = modvmcompute.NewProc("HcsModifyServiceSettings")
procHNSCall = modvmcompute.NewProc("HNSCall") procHNSCall = modvmcompute.NewProc("HNSCall")
procNtCreateFile = modntdll.NewProc("NtCreateFile")
procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile")
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
procLocalFree = modkernel32.NewProc("LocalFree")
) )
func coTaskMemFree(buffer unsafe.Pointer) { func coTaskMemFree(buffer unsafe.Pointer) {
@ -1040,3 +1047,34 @@ func __hnsCall(method *uint16, path *uint16, object *uint16, response **uint16)
} }
return return
} }
func ntCreateFile(handle *uintptr, accessMask uint32, oa *objectAttributes, iosb *ioStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) {
r0, _, _ := syscall.Syscall12(procNtCreateFile.Addr(), 11, uintptr(unsafe.Pointer(handle)), uintptr(accessMask), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(unsafe.Pointer(allocationSize)), uintptr(fileAttributes), uintptr(shareAccess), uintptr(createDisposition), uintptr(createOptions), uintptr(unsafe.Pointer(eaBuffer)), uintptr(eaLength), 0)
status = uint32(r0)
return
}
func ntSetInformationFile(handle uintptr, iosb *ioStatusBlock, information uintptr, length uint32, class uint32) (status uint32) {
r0, _, _ := syscall.Syscall6(procNtSetInformationFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(iosb)), uintptr(information), uintptr(length), uintptr(class), 0)
status = uint32(r0)
return
}
func rtlNtStatusToDosError(status uint32) (winerr error) {
r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0)
if r0 != 0 {
winerr = syscall.Errno(r0)
}
return
}
func localAlloc(flags uint32, size int) (ptr uintptr) {
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(flags), uintptr(size), 0)
ptr = uintptr(r0)
return
}
func localFree(ptr uintptr) {
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(ptr), 0, 0)
return
}