vendor: update hcsshim to v0.6.11

Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
This commit is contained in:
Jess Frazelle 2018-06-14 10:54:13 -04:00
parent 588e088799
commit b6b47649e0
No known key found for this signature in database
GPG Key ID: 18F3685C0022BFF3
18 changed files with 1546 additions and 927 deletions

4
Godeps/Godeps.json generated
View File

@ -114,8 +114,8 @@
}, },
{ {
"ImportPath": "github.com/Microsoft/hcsshim", "ImportPath": "github.com/Microsoft/hcsshim",
"Comment": "V0.6.3", "Comment": "v0.6.11",
"Rev": "6ea7fe54f719d95721e7d9b26ac0add224c9b923" "Rev": "800683ae704ac360b2f3f47fa88f3a6c8c9091b5"
}, },
{ {
"ImportPath": "github.com/NYTimes/gziphandler", "ImportPath": "github.com/NYTimes/gziphandler",

View File

@ -29,10 +29,13 @@ go_library(
"layerexists.go", "layerexists.go",
"layerutils.go", "layerutils.go",
"legacy.go", "legacy.go",
"legacy18.go",
"legacy19.go",
"nametoguid.go", "nametoguid.go",
"preparelayer.go", "preparelayer.go",
"process.go", "process.go",
"processimage.go", "processimage.go",
"safeopen.go",
"unpreparelayer.go", "unpreparelayer.go",
"utils.go", "utils.go",
"version.go", "version.go",

View File

@ -4,9 +4,30 @@ This package supports launching Windows Server containers from Go. It is
primarily used in the [Docker Engine](https://github.com/docker/docker) project, primarily used in the [Docker Engine](https://github.com/docker/docker) project,
but it can be freely used by other projects as well. but it can be freely used by other projects as well.
This project has adopted the [Microsoft Open Source Code of
Conduct](https://opensource.microsoft.com/codeofconduct/). For more information ## Contributing
see the [Code of Conduct ---------------
FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact This project welcomes contributions and suggestions. Most contributions require you to agree to a
[opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
questions or comments. the rights to use your contribution. For details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Reporting Security Issues
Security issues and bugs should be reported privately, via email, to the Microsoft Security
Response Center (MSRC) at [secure@microsoft.com](mailto:secure@microsoft.com). You should
receive a response within 24 hours. If for some reason you do not, please follow up via
email to ensure we received your original message. Further information, including the
[MSRC PGP](https://technet.microsoft.com/en-us/security/dn606155) key, can be found in
the [Security TechCenter](https://technet.microsoft.com/en-us/security/default).
-------------------------------------------
Copyright (c) 2018 Microsoft Corp. All rights reserved.

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

@ -201,12 +201,18 @@ func createContainerWithJSON(id string, c *ContainerConfig, additionalJSON strin
if createError == nil || IsPending(createError) { if createError == nil || IsPending(createError) {
if err := container.registerCallback(); err != nil { if err := container.registerCallback(); err != nil {
// Terminate the container if it still exists. We're okay to ignore a failure here.
container.Terminate()
return nil, makeContainerError(container, operation, "", err) return nil, makeContainerError(container, operation, "", err)
} }
} }
err = processAsyncHcsResult(createError, resultp, container.callbackNumber, hcsNotificationSystemCreateCompleted, &defaultTimeout) err = processAsyncHcsResult(createError, resultp, container.callbackNumber, hcsNotificationSystemCreateCompleted, &defaultTimeout)
if err != nil { if err != nil {
if err == ErrTimeout {
// Terminate the container if it still exists. We're okay to ignore a failure here.
container.Terminate()
}
return nil, makeContainerError(container, operation, configuration, err) return nil, makeContainerError(container, operation, configuration, err)
} }

View File

@ -72,6 +72,22 @@ var (
ErrPlatformNotSupported = errors.New("unsupported platform request") ErrPlatformNotSupported = errors.New("unsupported platform request")
) )
type EndpointNotFoundError struct {
EndpointName string
}
func (e EndpointNotFoundError) Error() string {
return fmt.Sprintf("Endpoint %s not found", e.EndpointName)
}
type NetworkNotFoundError struct {
NetworkName string
}
func (e NetworkNotFoundError) Error() string {
return fmt.Sprintf("Network %s not found", e.NetworkName)
}
// ProcessError is an error encountered in HCS during an operation on a Process object // ProcessError is an error encountered in HCS during an operation on a Process object
type ProcessError struct { type ProcessError struct {
Process *process Process *process
@ -174,6 +190,12 @@ func makeProcessError(process *process, operation string, extraInfo string, err
// will currently return true when the error is ErrElementNotFound or ErrProcNotFound. // will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
func IsNotExist(err error) bool { func IsNotExist(err error) bool {
err = getInnerError(err) err = getInnerError(err)
if _, ok := err.(EndpointNotFoundError); ok {
return true
}
if _, ok := err.(NetworkNotFoundError); ok {
return true
}
return err == ErrComputeSystemDoesNotExist || return err == ErrComputeSystemDoesNotExist ||
err == ErrElementNotFound || err == ErrElementNotFound ||
err == ErrProcNotFound err == ErrProcNotFound

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,318 +1,323 @@
package hcsshim package hcsshim
import ( import (
"encoding/json" "encoding/json"
"fmt" "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, fmt.Errorf("Endpoint %v not found", 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 Acl Policy on the Endpoint func (endpoint *HNSEndpoint) ApplyACLPolicy(policies ...*ACLPolicy) error {
func (endpoint *HNSEndpoint) ApplyACLPolicy(policy *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 {
jsonString, err := json.Marshal(policy) if policy == nil {
if err != nil { continue
return err }
} jsonString, err := json.Marshal(policy)
endpoint.Policies[0] = jsonString if err != nil {
_, err = endpoint.Update() return err
return err }
} endpoint.Policies = append(endpoint.Policies, jsonString)
}
// ContainerAttach attaches an endpoint to container
func (endpoint *HNSEndpoint) ContainerAttach(containerID string, compartmentID uint16) error { _, err := endpoint.Update()
operation := "ContainerAttach" return err
title := "HCSShim::HNSEndpoint::" + operation }
logrus.Debugf(title+" id=%s", endpoint.Id)
// ContainerAttach attaches an endpoint to container
requestMessage := &EndpointAttachDetachRequest{ func (endpoint *HNSEndpoint) ContainerAttach(containerID string, compartmentID uint16) error {
ContainerID: containerID, operation := "ContainerAttach"
CompartmentID: compartmentID, title := "HCSShim::HNSEndpoint::" + operation
SystemType: ContainerType, logrus.Debugf(title+" id=%s", endpoint.Id)
}
response := &EndpointResquestResponse{} requestMessage := &EndpointAttachDetachRequest{
jsonString, err := json.Marshal(requestMessage) ContainerID: containerID,
if err != nil { CompartmentID: compartmentID,
return err SystemType: ContainerType,
} }
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) response := &EndpointResquestResponse{}
} jsonString, err := json.Marshal(requestMessage)
if err != nil {
// ContainerDetach detaches an endpoint from container return err
func (endpoint *HNSEndpoint) ContainerDetach(containerID string) error { }
operation := "ContainerDetach" return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
title := "HCSShim::HNSEndpoint::" + operation }
logrus.Debugf(title+" id=%s", endpoint.Id)
// ContainerDetach detaches an endpoint from container
requestMessage := &EndpointAttachDetachRequest{ func (endpoint *HNSEndpoint) ContainerDetach(containerID string) error {
ContainerID: containerID, operation := "ContainerDetach"
SystemType: ContainerType, title := "HCSShim::HNSEndpoint::" + operation
} logrus.Debugf(title+" id=%s", endpoint.Id)
response := &EndpointResquestResponse{}
requestMessage := &EndpointAttachDetachRequest{
jsonString, err := json.Marshal(requestMessage) ContainerID: containerID,
if err != nil { SystemType: ContainerType,
return err }
} response := &EndpointResquestResponse{}
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
} jsonString, err := json.Marshal(requestMessage)
if err != nil {
// HostAttach attaches a nic on the host return err
func (endpoint *HNSEndpoint) HostAttach(compartmentID uint16) error { }
operation := "HostAttach" return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
title := "HCSShim::HNSEndpoint::" + operation }
logrus.Debugf(title+" id=%s", endpoint.Id)
requestMessage := &EndpointAttachDetachRequest{ // HostAttach attaches a nic on the host
CompartmentID: compartmentID, func (endpoint *HNSEndpoint) HostAttach(compartmentID uint16) error {
SystemType: HostType, operation := "HostAttach"
} title := "HCSShim::HNSEndpoint::" + operation
response := &EndpointResquestResponse{} logrus.Debugf(title+" id=%s", endpoint.Id)
requestMessage := &EndpointAttachDetachRequest{
jsonString, err := json.Marshal(requestMessage) CompartmentID: compartmentID,
if err != nil { SystemType: HostType,
return err }
} response := &EndpointResquestResponse{}
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
jsonString, err := json.Marshal(requestMessage)
} if err != nil {
return err
// HostDetach detaches a nic on the host }
func (endpoint *HNSEndpoint) HostDetach() error { return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
operation := "HostDetach"
title := "HCSShim::HNSEndpoint::" + operation }
logrus.Debugf(title+" id=%s", endpoint.Id)
requestMessage := &EndpointAttachDetachRequest{ // HostDetach detaches a nic on the host
SystemType: HostType, func (endpoint *HNSEndpoint) HostDetach() error {
} operation := "HostDetach"
response := &EndpointResquestResponse{} title := "HCSShim::HNSEndpoint::" + operation
logrus.Debugf(title+" id=%s", endpoint.Id)
jsonString, err := json.Marshal(requestMessage) requestMessage := &EndpointAttachDetachRequest{
if err != nil { SystemType: HostType,
return err }
} response := &EndpointResquestResponse{}
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
} jsonString, err := json.Marshal(requestMessage)
if err != nil {
// VirtualMachineNICAttach attaches a endpoint to a virtual machine return err
func (endpoint *HNSEndpoint) VirtualMachineNICAttach(virtualMachineNICName string) error { }
operation := "VirtualMachineNicAttach" return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
title := "HCSShim::HNSEndpoint::" + operation }
logrus.Debugf(title+" id=%s", endpoint.Id)
requestMessage := &EndpointAttachDetachRequest{ // VirtualMachineNICAttach attaches a endpoint to a virtual machine
VirtualNICName: virtualMachineNICName, func (endpoint *HNSEndpoint) VirtualMachineNICAttach(virtualMachineNICName string) error {
SystemType: VirtualMachineType, operation := "VirtualMachineNicAttach"
} title := "HCSShim::HNSEndpoint::" + operation
response := &EndpointResquestResponse{} logrus.Debugf(title+" id=%s", endpoint.Id)
requestMessage := &EndpointAttachDetachRequest{
jsonString, err := json.Marshal(requestMessage) VirtualNICName: virtualMachineNICName,
if err != nil { SystemType: VirtualMachineType,
return err }
} response := &EndpointResquestResponse{}
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
} jsonString, err := json.Marshal(requestMessage)
if err != nil {
// VirtualMachineNICDetach detaches a endpoint from a virtual machine return err
func (endpoint *HNSEndpoint) VirtualMachineNICDetach() error { }
operation := "VirtualMachineNicDetach" return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
title := "HCSShim::HNSEndpoint::" + operation }
logrus.Debugf(title+" id=%s", endpoint.Id)
// VirtualMachineNICDetach detaches a endpoint from a virtual machine
requestMessage := &EndpointAttachDetachRequest{ func (endpoint *HNSEndpoint) VirtualMachineNICDetach() error {
SystemType: VirtualMachineType, operation := "VirtualMachineNicDetach"
} title := "HCSShim::HNSEndpoint::" + operation
response := &EndpointResquestResponse{} logrus.Debugf(title+" id=%s", endpoint.Id)
jsonString, err := json.Marshal(requestMessage) requestMessage := &EndpointAttachDetachRequest{
if err != nil { SystemType: VirtualMachineType,
return err }
} response := &EndpointResquestResponse{}
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
} jsonString, err := json.Marshal(requestMessage)
if err != nil {
return err
}
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
}

View File

@ -1,142 +1,141 @@
package hcsshim package hcsshim
import ( import (
"encoding/json" "encoding/json"
"fmt" "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, fmt.Errorf("Network %v not found", 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,95 +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
LocalAddress string LocalAddresses string
RemoteAddress 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

@ -30,11 +30,12 @@ type Layer struct {
} }
type MappedDir struct { type MappedDir struct {
HostPath string HostPath string
ContainerPath string ContainerPath string
ReadOnly bool ReadOnly bool
BandwidthMaximum uint64 BandwidthMaximum uint64
IOPSMaximum uint64 IOPSMaximum uint64
CreateInUtilityVM bool
} }
type MappedPipe struct { type MappedPipe struct {

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 strings.EqualFold(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,39 +532,49 @@ 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
// Directories, reparse points, and files that will be mutated during // Directories, reparse points, and files that will be mutated during
// utility VM import must be copied. All other files can be hard linked. // utility VM import must be copied. All other files can be hard linked.
isReparsePoint := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 isReparsePoint := fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0
if info.IsDir() || isReparsePoint || mutatedFiles[relPath] { // In go1.9, FileInfo.IsDir() returns false if the directory is also a symlink.
fi, err := copyFileWithMetadata(srcFilePath, destFilePath, info.IsDir()) // See: https://github.com/golang/go/commit/1989921aef60c83e6f9127a8448fb5ede10e9acc
// Fixes the problem by checking syscall.FILE_ATTRIBUTE_DIRECTORY directly
isDir := fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0
if isDir || isReparsePoint || mutatedFiles[relPath] {
fi, err := copyFileWithMetadata(srcRoot, destRoot, relPath, isDir)
if err != nil { if err != nil {
return err return err
} }
if info.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
} }
} }
// Don't recurse on reparse points. // Don't recurse on reparse points in go1.8 and older. Filepath.Walk
if info.IsDir() && isReparsePoint { // handles this in go1.9 and newer.
if isDir && isReparsePoint && shouldSkipDirectoryReparse {
return filepath.SkipDir return filepath.SkipDir
} }
@ -501,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
} }
@ -515,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")
@ -522,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
} }
@ -533,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)
} }
}() }()
@ -574,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)
} }
}() }()
@ -617,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.
@ -638,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)) {
@ -647,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
} }
@ -661,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 {
@ -685,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
} }
@ -711,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
} }

7
vendor/github.com/Microsoft/hcsshim/legacy18.go generated vendored Normal file
View File

@ -0,0 +1,7 @@
// +build !go1.9
package hcsshim
// 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
var shouldSkipDirectoryReparse = true

7
vendor/github.com/Microsoft/hcsshim/legacy19.go generated vendored Normal file
View File

@ -0,0 +1,7 @@
// +build go1.9
package hcsshim
// 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
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
}