@@ -6,15 +6,16 @@ environment:
|
|||||||
install:
|
install:
|
||||||
- echo %PATH%
|
- echo %PATH%
|
||||||
- echo %GOPATH%
|
- echo %GOPATH%
|
||||||
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
|
||||||
- go version
|
- go version
|
||||||
- go env
|
- go env
|
||||||
|
- ps: $webClient = New-Object System.Net.WebClient; $InstallPath="c:" ; $webClient.DownloadFile("https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/gcc.zip", "$InstallPath\gcc.zip"); Expand-Archive $InstallPath\gcc.zip -DestinationPath $InstallPath\gcc -Force; $webClient.DownloadFile("https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/runtime.zip", "$InstallPath\runtime.zip"); Expand-Archive $InstallPath\runtime.zip -DestinationPath $InstallPath\gcc -Force; $webClient.DownloadFile("https://raw.githubusercontent.com/jhowardmsft/docker-tdmgcc/master/binutils.zip","$InstallPath\binutils.zip"); Expand-Archive $InstallPath\binutils.zip -DestinationPath $InstallPath\gcc -Force;
|
||||||
|
- set PATH=%GOPATH%\bin;c:\go\bin;c:\gcc\bin;%PATH%
|
||||||
|
|
||||||
build: off
|
build: off
|
||||||
|
|
||||||
test_script:
|
test_script:
|
||||||
- ps: |
|
- ps: |
|
||||||
go list ./... | Select-String -Pattern (Get-Content "./plugins/linux_only.txt") -NotMatch > "to_test.txt"
|
go list ./... | Select-String -Pattern (Get-Content "./plugins/windows_only.txt") > "to_test.txt"
|
||||||
echo "Will test:"
|
echo "Will test:"
|
||||||
Get-Content "to_test.txt"
|
Get-Content "to_test.txt"
|
||||||
foreach ($pkg in Get-Content "to_test.txt") {
|
foreach ($pkg in Get-Content "to_test.txt") {
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -26,5 +26,5 @@ _testmain.go
|
|||||||
bin/
|
bin/
|
||||||
gopath/
|
gopath/
|
||||||
.vagrant
|
.vagrant
|
||||||
|
.idea
|
||||||
/release-*
|
/release-*
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ matrix:
|
|||||||
fast_finish: true
|
fast_finish: true
|
||||||
|
|
||||||
install:
|
install:
|
||||||
|
- sudo apt-get install gcc-multilib gcc-mingw-w64 -y
|
||||||
- go get github.com/onsi/ginkgo/ginkgo
|
- go get github.com/onsi/ginkgo/ginkgo
|
||||||
- go get github.com/containernetworking/cni/cnitool
|
- go get github.com/containernetworking/cni/cnitool
|
||||||
|
|
||||||
|
|||||||
96
Godeps/Godeps.json
generated
96
Godeps/Godeps.json
generated
@@ -6,10 +6,79 @@
|
|||||||
"./..."
|
"./..."
|
||||||
],
|
],
|
||||||
"Deps": [
|
"Deps": [
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/Microsoft/go-winio",
|
||||||
|
"Comment": "v0.4.11",
|
||||||
|
"Rev": "97e4973ce50b2ff5f09635a57e2b88a037aae829"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/Microsoft/hcsshim",
|
||||||
|
"Comment": "v0.7.4",
|
||||||
|
"Rev": "e44e499d29527b244d6858772f1b9090eeaddc4e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/Microsoft/hcsshim/internal/guid",
|
||||||
|
"Comment": "v0.7.4",
|
||||||
|
"Rev": "e44e499d29527b244d6858772f1b9090eeaddc4e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/Microsoft/hcsshim/internal/hcs",
|
||||||
|
"Comment": "v0.7.4",
|
||||||
|
"Rev": "e44e499d29527b244d6858772f1b9090eeaddc4e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/Microsoft/hcsshim/internal/hcserror",
|
||||||
|
"Comment": "v0.7.4",
|
||||||
|
"Rev": "e44e499d29527b244d6858772f1b9090eeaddc4e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/Microsoft/hcsshim/internal/hns",
|
||||||
|
"Comment": "v0.7.4",
|
||||||
|
"Rev": "e44e499d29527b244d6858772f1b9090eeaddc4e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/Microsoft/hcsshim/internal/interop",
|
||||||
|
"Comment": "v0.7.4",
|
||||||
|
"Rev": "e44e499d29527b244d6858772f1b9090eeaddc4e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/Microsoft/hcsshim/internal/longpath",
|
||||||
|
"Comment": "v0.7.4",
|
||||||
|
"Rev": "e44e499d29527b244d6858772f1b9090eeaddc4e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/Microsoft/hcsshim/internal/mergemaps",
|
||||||
|
"Comment": "v0.7.4",
|
||||||
|
"Rev": "e44e499d29527b244d6858772f1b9090eeaddc4e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/Microsoft/hcsshim/internal/safefile",
|
||||||
|
"Comment": "v0.7.4",
|
||||||
|
"Rev": "e44e499d29527b244d6858772f1b9090eeaddc4e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/Microsoft/hcsshim/internal/schema1",
|
||||||
|
"Comment": "v0.7.4",
|
||||||
|
"Rev": "e44e499d29527b244d6858772f1b9090eeaddc4e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/Microsoft/hcsshim/internal/timeout",
|
||||||
|
"Comment": "v0.7.4",
|
||||||
|
"Rev": "e44e499d29527b244d6858772f1b9090eeaddc4e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/Microsoft/hcsshim/internal/wclayer",
|
||||||
|
"Comment": "v0.7.4",
|
||||||
|
"Rev": "e44e499d29527b244d6858772f1b9090eeaddc4e"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/alexflint/go-filemutex",
|
"ImportPath": "github.com/alexflint/go-filemutex",
|
||||||
"Rev": "72bdc8eae2aef913234599b837f5dda445ca9bd9"
|
"Rev": "72bdc8eae2aef913234599b837f5dda445ca9bd9"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/buger/jsonparser",
|
||||||
|
"Rev": "f4dd9f5a6b441265aefc1d44872a6f8c10f42b37"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/containernetworking/cni/libcni",
|
"ImportPath": "github.com/containernetworking/cni/libcni",
|
||||||
"Comment": "v0.7.0-alpha1",
|
"Comment": "v0.7.0-alpha1",
|
||||||
@@ -67,10 +136,22 @@
|
|||||||
"ImportPath": "github.com/d2g/dhcp4server",
|
"ImportPath": "github.com/d2g/dhcp4server",
|
||||||
"Rev": "1b74244053681c90de5cf1af3d6b5c93b74e3abb"
|
"Rev": "1b74244053681c90de5cf1af3d6b5c93b74e3abb"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/d2g/dhcp4server/leasepool",
|
||||||
|
"Rev": "1b74244053681c90de5cf1af3d6b5c93b74e3abb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/d2g/dhcp4server/leasepool/memorypool",
|
||||||
|
"Rev": "1b74244053681c90de5cf1af3d6b5c93b74e3abb"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/j-keck/arping",
|
"ImportPath": "github.com/j-keck/arping",
|
||||||
"Rev": "2cf9dc699c5640a7e2c81403a44127bf28033600"
|
"Rev": "2cf9dc699c5640a7e2c81403a44127bf28033600"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/juju/errors",
|
||||||
|
"Rev": "22422dad46e14561a0854ad42497a75af9b61909"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/mattn/go-shellwords",
|
"ImportPath": "github.com/mattn/go-shellwords",
|
||||||
"Comment": "v1.0.3",
|
"Comment": "v1.0.3",
|
||||||
@@ -230,6 +311,11 @@
|
|||||||
"ImportPath": "github.com/safchain/ethtool",
|
"ImportPath": "github.com/safchain/ethtool",
|
||||||
"Rev": "7ff1ba29eca231991280817541cb3903f6be15d1"
|
"Rev": "7ff1ba29eca231991280817541cb3903f6be15d1"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/sirupsen/logrus",
|
||||||
|
"Comment": "v1.0.6",
|
||||||
|
"Rev": "3e01752db0189b9157070a0e1668a620f9a85da2"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/vishvananda/netlink",
|
"ImportPath": "github.com/vishvananda/netlink",
|
||||||
"Rev": "6e453822d85ef5721799774b654d4d02fed62afb"
|
"Rev": "6e453822d85ef5721799774b654d4d02fed62afb"
|
||||||
@@ -242,6 +328,10 @@
|
|||||||
"ImportPath": "github.com/vishvananda/netns",
|
"ImportPath": "github.com/vishvananda/netns",
|
||||||
"Rev": "54f0e4339ce73702a0607f49922aaa1e749b418d"
|
"Rev": "54f0e4339ce73702a0607f49922aaa1e749b418d"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/crypto/ssh/terminal",
|
||||||
|
"Rev": "94eea52f7b742c7cbe0b03b22f0c4c8631ece122"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/bpf",
|
"ImportPath": "golang.org/x/net/bpf",
|
||||||
"Rev": "e90d6d0afc4c315a0d87a568ae68577cc15149a0"
|
"Rev": "e90d6d0afc4c315a0d87a568ae68577cc15149a0"
|
||||||
@@ -256,7 +346,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/sys/unix",
|
"ImportPath": "golang.org/x/sys/unix",
|
||||||
"Rev": "076b546753157f758b316e59bcb51e6807c04057"
|
"Rev": "d5840adf789d732bc8b00f37b26ca956a7cc8e79"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/sys/windows",
|
||||||
|
"Rev": "d5840adf789d732bc8b00f37b26ca956a7cc8e79"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ Read [CONTRIBUTING](CONTRIBUTING.md) for build and test instructions.
|
|||||||
* `macvlan`: Creates a new MAC address, forwards all traffic to that to the container.
|
* `macvlan`: Creates a new MAC address, forwards all traffic to that to the container.
|
||||||
* `ptp`: Creates a veth pair.
|
* `ptp`: Creates a veth pair.
|
||||||
* `vlan`: Allocates a vlan device.
|
* `vlan`: Allocates a vlan device.
|
||||||
|
#### Windows: windows specific
|
||||||
|
* `win-bridge`: Creates a bridge, adds the host and the container to it.
|
||||||
|
* `win-overlay`: Creates an overlay interface to the container.
|
||||||
### IPAM: IP address allocation
|
### IPAM: IP address allocation
|
||||||
* `dhcp`: Runs a daemon on the host to make DHCP requests on behalf of the container
|
* `dhcp`: Runs a daemon on the host to make DHCP requests on behalf of the container
|
||||||
* `host-local`: maintains a local database of allocated IPs
|
* `host-local`: maintains a local database of allocated IPs
|
||||||
|
|||||||
5
Vagrantfile
vendored
5
Vagrantfile
vendored
@@ -8,12 +8,9 @@ Vagrant.configure(2) do |config|
|
|||||||
|
|
||||||
config.vm.provision "shell", inline: <<-SHELL
|
config.vm.provision "shell", inline: <<-SHELL
|
||||||
set -e -x -u
|
set -e -x -u
|
||||||
|
|
||||||
apt-get update -y || (sleep 40 && apt-get update -y)
|
apt-get update -y || (sleep 40 && apt-get update -y)
|
||||||
apt-get install -y git
|
apt-get install -y git gcc-multilib gcc-mingw-w64
|
||||||
|
|
||||||
wget -qO- https://storage.googleapis.com/golang/go1.10.linux-amd64.tar.gz | tar -C /usr/local -xz
|
wget -qO- https://storage.googleapis.com/golang/go1.10.linux-amd64.tar.gz | tar -C /usr/local -xz
|
||||||
|
|
||||||
echo 'export GOPATH=/go' >> /root/.bashrc
|
echo 'export GOPATH=/go' >> /root/.bashrc
|
||||||
echo 'export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin' >> /root/.bashrc
|
echo 'export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin' >> /root/.bashrc
|
||||||
cd /go/src/github.com/containernetworking/plugins
|
cd /go/src/github.com/containernetworking/plugins
|
||||||
|
|||||||
10
build.sh
10
build.sh
@@ -19,12 +19,20 @@ export GO="${GO:-go}"
|
|||||||
|
|
||||||
mkdir -p "${PWD}/bin"
|
mkdir -p "${PWD}/bin"
|
||||||
|
|
||||||
echo "Building plugins"
|
echo "Building plugins ${GOOS}"
|
||||||
PLUGINS="plugins/meta/* plugins/main/* plugins/ipam/* plugins/sample"
|
PLUGINS="plugins/meta/* plugins/main/* plugins/ipam/* plugins/sample"
|
||||||
for d in $PLUGINS; do
|
for d in $PLUGINS; do
|
||||||
if [ -d "$d" ]; then
|
if [ -d "$d" ]; then
|
||||||
plugin="$(basename "$d")"
|
plugin="$(basename "$d")"
|
||||||
|
if [ $plugin == "windows" ]
|
||||||
|
then
|
||||||
|
if [ "$GOARCH" == "amd64" ]
|
||||||
|
then
|
||||||
|
GOOS=windows . $d/build.sh
|
||||||
|
fi
|
||||||
|
else
|
||||||
echo " $plugin"
|
echo " $plugin"
|
||||||
$GO build -o "${PWD}/bin/$plugin" "$@" "$REPO_PATH"/$d
|
$GO build -o "${PWD}/bin/$plugin" "$@" "$REPO_PATH"/$d
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|||||||
152
pkg/hns/endpoint_windows.go
Normal file
152
pkg/hns/endpoint_windows.go
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
// Copyright 2017 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package hns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim"
|
||||||
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
"github.com/juju/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
pauseContainerNetNS = "none"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetSandboxContainerID returns the sandbox ID of this pod
|
||||||
|
func GetSandboxContainerID(containerID string, netNs string) string {
|
||||||
|
if len(netNs) != 0 && netNs != pauseContainerNetNS {
|
||||||
|
splits := strings.SplitN(netNs, ":", 2)
|
||||||
|
if len(splits) == 2 {
|
||||||
|
containerID = splits[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return containerID
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConstructEndpointName constructs enpointId which is used to identify an endpoint from HNS
|
||||||
|
// There is a special consideration for netNs name here, which is required for Windows Server 1709
|
||||||
|
// containerID is the Id of the container on which the endpoint is worked on
|
||||||
|
func ConstructEndpointName(containerID string, netNs string, networkName string) string {
|
||||||
|
return GetSandboxContainerID(containerID, netNs) + "_" + networkName
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeprovisionEndpoint removes an endpoint from the container by sending a Detach request to HNS
|
||||||
|
// For shared endpoint, ContainerDetach is used
|
||||||
|
// for removing the endpoint completely, HotDetachEndpoint is used
|
||||||
|
func DeprovisionEndpoint(epName string, netns string, containerID string) error {
|
||||||
|
if len(netns) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hnsEndpoint, err := hcsshim.GetHNSEndpointByName(epName)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Annotatef(err, "failed to find HNSEndpoint %s", epName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if netns != pauseContainerNetNS {
|
||||||
|
// Shared endpoint removal. Do not remove the endpoint.
|
||||||
|
hnsEndpoint.ContainerDetach(containerID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not consider this as failure, else this would leak endpoints
|
||||||
|
hcsshim.HotDetachEndpoint(containerID, hnsEndpoint.Id)
|
||||||
|
|
||||||
|
// Do not return error
|
||||||
|
hnsEndpoint.Delete()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type EndpointMakerFunc func() (*hcsshim.HNSEndpoint, error)
|
||||||
|
|
||||||
|
// ProvisionEndpoint provisions an endpoint to a container specified by containerID.
|
||||||
|
// If an endpoint already exists, the endpoint is reused.
|
||||||
|
// This call is idempotent
|
||||||
|
func ProvisionEndpoint(epName string, expectedNetworkId string, containerID string, makeEndpoint EndpointMakerFunc) (*hcsshim.HNSEndpoint, error) {
|
||||||
|
// check if endpoint already exists
|
||||||
|
createEndpoint := true
|
||||||
|
hnsEndpoint, err := hcsshim.GetHNSEndpointByName(epName)
|
||||||
|
if hnsEndpoint != nil && hnsEndpoint.VirtualNetwork == expectedNetworkId {
|
||||||
|
createEndpoint = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if createEndpoint {
|
||||||
|
if hnsEndpoint != nil {
|
||||||
|
if _, err = hnsEndpoint.Delete(); err != nil {
|
||||||
|
return nil, errors.Annotate(err, "failed to delete the stale HNSEndpoint")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hnsEndpoint, err = makeEndpoint(); err != nil {
|
||||||
|
return nil, errors.Annotate(err, "failed to make a new HNSEndpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
if hnsEndpoint, err = hnsEndpoint.Create(); err != nil {
|
||||||
|
return nil, errors.Annotate(err, "failed to create the new HNSEndpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// hot attach
|
||||||
|
if err := hcsshim.HotAttachEndpoint(containerID, hnsEndpoint.Id); err != nil {
|
||||||
|
if hcsshim.ErrComputeSystemDoesNotExist == err {
|
||||||
|
return hnsEndpoint, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return hnsEndpoint, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConstructResult constructs the CNI result for the endpoint
|
||||||
|
func ConstructResult(hnsNetwork *hcsshim.HNSNetwork, hnsEndpoint *hcsshim.HNSEndpoint) (*current.Result, error) {
|
||||||
|
resultInterface := ¤t.Interface{
|
||||||
|
Name: hnsEndpoint.Name,
|
||||||
|
Mac: hnsEndpoint.MacAddress,
|
||||||
|
}
|
||||||
|
_, ipSubnet, err := net.ParseCIDR(hnsNetwork.Subnets[0].AddressPrefix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Annotatef(err, "failed to parse CIDR from %s", hnsNetwork.Subnets[0].AddressPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ipVersion string
|
||||||
|
if ipv4 := hnsEndpoint.IPAddress.To4(); ipv4 != nil {
|
||||||
|
ipVersion = "4"
|
||||||
|
} else if ipv6 := hnsEndpoint.IPAddress.To16(); ipv6 != nil {
|
||||||
|
ipVersion = "6"
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("IPAddress of HNSEndpoint %s isn't a valid ipv4 or ipv6 Address", hnsEndpoint.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
resultIPConfig := ¤t.IPConfig{
|
||||||
|
Version: ipVersion,
|
||||||
|
Address: net.IPNet{
|
||||||
|
IP: hnsEndpoint.IPAddress,
|
||||||
|
Mask: ipSubnet.Mask},
|
||||||
|
Gateway: net.ParseIP(hnsEndpoint.GatewayAddress),
|
||||||
|
}
|
||||||
|
result := ¤t.Result{}
|
||||||
|
result.Interfaces = []*current.Interface{resultInterface}
|
||||||
|
result.IPs = []*current.IPConfig{resultIPConfig}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
152
pkg/hns/netconf.go
Normal file
152
pkg/hns/netconf.go
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
// Copyright 2017 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package hns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"bytes"
|
||||||
|
"github.com/buger/jsonparser"
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetConf is the CNI spec
|
||||||
|
type NetConf struct {
|
||||||
|
types.NetConf
|
||||||
|
|
||||||
|
Policies []policy `json:"policies,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type policy struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value json.RawMessage `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalPolicies converts the Endpoint policies in Policies
|
||||||
|
// to HNS specific policies as Json raw bytes
|
||||||
|
func (n *NetConf) MarshalPolicies() []json.RawMessage {
|
||||||
|
if n.Policies == nil {
|
||||||
|
n.Policies = make([]policy, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]json.RawMessage, 0, len(n.Policies))
|
||||||
|
for _, p := range n.Policies {
|
||||||
|
if !strings.EqualFold(p.Name, "EndpointPolicy") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, p.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyOutboundNatPolicy applies NAT Policy in VFP using HNS
|
||||||
|
// Simultaneously an exception is added for the network that has to be Nat'd
|
||||||
|
func (n *NetConf) ApplyOutboundNatPolicy(nwToNat string) {
|
||||||
|
if n.Policies == nil {
|
||||||
|
n.Policies = make([]policy, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
nwToNatBytes := []byte(nwToNat)
|
||||||
|
|
||||||
|
for i, p := range n.Policies {
|
||||||
|
if !strings.EqualFold(p.Name, "EndpointPolicy") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
typeValue, err := jsonparser.GetUnsafeString(p.Value, "Type")
|
||||||
|
if err != nil || len(typeValue) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.EqualFold(typeValue, "OutBoundNAT") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
exceptionListValue, dt, _, _ := jsonparser.Get(p.Value, "ExceptionList")
|
||||||
|
// OutBoundNAT must with ExceptionList, so don't need to judge jsonparser.NotExist
|
||||||
|
if dt == jsonparser.Array {
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
buf.WriteString(`{"Type": "OutBoundNAT", "ExceptionList": [`)
|
||||||
|
|
||||||
|
jsonparser.ArrayEach(exceptionListValue, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
|
||||||
|
if dataType == jsonparser.String && len(value) != 0 {
|
||||||
|
if bytes.Compare(value, nwToNatBytes) != 0 {
|
||||||
|
buf.WriteByte('"')
|
||||||
|
buf.Write(value)
|
||||||
|
buf.WriteByte('"')
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
buf.WriteString(`"` + nwToNat + `"]}`)
|
||||||
|
|
||||||
|
n.Policies[i] = policy{
|
||||||
|
Name: "EndpointPolicy",
|
||||||
|
Value: buf.Bytes(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n.Policies[i] = policy{
|
||||||
|
Name: "EndpointPolicy",
|
||||||
|
Value: []byte(`{"Type": "OutBoundNAT", "ExceptionList": ["` + nwToNat + `"]}`),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// didn't find the policyArg, add it
|
||||||
|
n.Policies = append(n.Policies, policy{
|
||||||
|
Name: "EndpointPolicy",
|
||||||
|
Value: []byte(`{"Type": "OutBoundNAT", "ExceptionList": ["` + nwToNat + `"]}`),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyDefaultPAPolicy is used to configure a endpoint PA policy in HNS
|
||||||
|
func (n *NetConf) ApplyDefaultPAPolicy(paAddress string) {
|
||||||
|
if n.Policies == nil {
|
||||||
|
n.Policies = make([]policy, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if its already present, leave untouched
|
||||||
|
for i, p := range n.Policies {
|
||||||
|
if !strings.EqualFold(p.Name, "EndpointPolicy") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
paValue, dt, _, _ := jsonparser.Get(p.Value, "PA")
|
||||||
|
if dt == jsonparser.NotExist {
|
||||||
|
continue
|
||||||
|
} else if dt == jsonparser.String && len(paValue) != 0 {
|
||||||
|
// found it, don't override
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
n.Policies[i] = policy{
|
||||||
|
Name: "EndpointPolicy",
|
||||||
|
Value: []byte(`{"Type": "PA", "PA": "` + paAddress + `"}`),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// didn't find the policyArg, add it
|
||||||
|
n.Policies = append(n.Policies, policy{
|
||||||
|
Name: "EndpointPolicy",
|
||||||
|
Value: []byte(`{"Type": "PA", "PA": "` + paAddress + `"}`),
|
||||||
|
})
|
||||||
|
}
|
||||||
26
pkg/hns/netconf_suite_test.go
Normal file
26
pkg/hns/netconf_suite_test.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2017 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
package hns
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHns(t *testing.T) {
|
||||||
|
RegisterFailHandler(Fail)
|
||||||
|
RunSpecs(t, "HNS NetConf Suite")
|
||||||
|
}
|
||||||
189
pkg/hns/netconf_test.go
Normal file
189
pkg/hns/netconf_test.go
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
// Copyright 2017 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
package hns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("HNS NetConf", func() {
|
||||||
|
Describe("ApplyOutBoundNATPolicy", func() {
|
||||||
|
Context("when not set by user", func() {
|
||||||
|
It("sets it by adding a policy", func() {
|
||||||
|
|
||||||
|
// apply it
|
||||||
|
n := NetConf{}
|
||||||
|
n.ApplyOutboundNatPolicy("192.168.0.0/16")
|
||||||
|
|
||||||
|
addlArgs := n.Policies
|
||||||
|
Expect(addlArgs).Should(HaveLen(1))
|
||||||
|
|
||||||
|
policy := addlArgs[0]
|
||||||
|
Expect(policy.Name).Should(Equal("EndpointPolicy"))
|
||||||
|
|
||||||
|
value := make(map[string]interface{})
|
||||||
|
json.Unmarshal(policy.Value, &value)
|
||||||
|
|
||||||
|
Expect(value).Should(HaveKey("Type"))
|
||||||
|
Expect(value).Should(HaveKey("ExceptionList"))
|
||||||
|
Expect(value["Type"]).Should(Equal("OutBoundNAT"))
|
||||||
|
|
||||||
|
exceptionList := value["ExceptionList"].([]interface{})
|
||||||
|
Expect(exceptionList).Should(HaveLen(1))
|
||||||
|
Expect(exceptionList[0].(string)).Should(Equal("192.168.0.0/16"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("when set by user", func() {
|
||||||
|
It("appends exceptions to the existing policy", func() {
|
||||||
|
// first set it
|
||||||
|
n := NetConf{}
|
||||||
|
n.ApplyOutboundNatPolicy("192.168.0.0/16")
|
||||||
|
|
||||||
|
// then attempt to update it
|
||||||
|
n.ApplyOutboundNatPolicy("10.244.0.0/16")
|
||||||
|
|
||||||
|
// it should be unchanged!
|
||||||
|
addlArgs := n.Policies
|
||||||
|
Expect(addlArgs).Should(HaveLen(1))
|
||||||
|
|
||||||
|
policy := addlArgs[0]
|
||||||
|
Expect(policy.Name).Should(Equal("EndpointPolicy"))
|
||||||
|
|
||||||
|
var value map[string]interface{}
|
||||||
|
json.Unmarshal(policy.Value, &value)
|
||||||
|
|
||||||
|
Expect(value).Should(HaveKey("Type"))
|
||||||
|
Expect(value).Should(HaveKey("ExceptionList"))
|
||||||
|
Expect(value["Type"]).Should(Equal("OutBoundNAT"))
|
||||||
|
|
||||||
|
exceptionList := value["ExceptionList"].([]interface{})
|
||||||
|
Expect(exceptionList).Should(HaveLen(2))
|
||||||
|
Expect(exceptionList[0].(string)).Should(Equal("192.168.0.0/16"))
|
||||||
|
Expect(exceptionList[1].(string)).Should(Equal("10.244.0.0/16"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Describe("ApplyDefaultPAPolicy", func() {
|
||||||
|
Context("when not set by user", func() {
|
||||||
|
It("sets it by adding a policy", func() {
|
||||||
|
|
||||||
|
n := NetConf{}
|
||||||
|
n.ApplyDefaultPAPolicy("192.168.0.1")
|
||||||
|
|
||||||
|
addlArgs := n.Policies
|
||||||
|
Expect(addlArgs).Should(HaveLen(1))
|
||||||
|
|
||||||
|
policy := addlArgs[0]
|
||||||
|
Expect(policy.Name).Should(Equal("EndpointPolicy"))
|
||||||
|
|
||||||
|
value := make(map[string]interface{})
|
||||||
|
json.Unmarshal(policy.Value, &value)
|
||||||
|
|
||||||
|
Expect(value).Should(HaveKey("Type"))
|
||||||
|
Expect(value["Type"]).Should(Equal("PA"))
|
||||||
|
|
||||||
|
paAddress := value["PA"].(string)
|
||||||
|
Expect(paAddress).Should(Equal("192.168.0.1"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("when set by user", func() {
|
||||||
|
It("does not override", func() {
|
||||||
|
n := NetConf{}
|
||||||
|
n.ApplyDefaultPAPolicy("192.168.0.1")
|
||||||
|
n.ApplyDefaultPAPolicy("192.168.0.2")
|
||||||
|
|
||||||
|
addlArgs := n.Policies
|
||||||
|
Expect(addlArgs).Should(HaveLen(1))
|
||||||
|
|
||||||
|
policy := addlArgs[0]
|
||||||
|
Expect(policy.Name).Should(Equal("EndpointPolicy"))
|
||||||
|
|
||||||
|
value := make(map[string]interface{})
|
||||||
|
json.Unmarshal(policy.Value, &value)
|
||||||
|
|
||||||
|
Expect(value).Should(HaveKey("Type"))
|
||||||
|
Expect(value["Type"]).Should(Equal("PA"))
|
||||||
|
|
||||||
|
paAddress := value["PA"].(string)
|
||||||
|
Expect(paAddress).Should(Equal("192.168.0.1"))
|
||||||
|
Expect(paAddress).ShouldNot(Equal("192.168.0.2"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Describe("MarshalPolicies", func() {
|
||||||
|
Context("when not set by user", func() {
|
||||||
|
It("sets it by adding a policy", func() {
|
||||||
|
|
||||||
|
n := NetConf{
|
||||||
|
Policies: []policy{
|
||||||
|
{
|
||||||
|
Name: "EndpointPolicy",
|
||||||
|
Value: []byte(`{"someKey": "someValue"}`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "someOtherType",
|
||||||
|
Value: []byte(`{"someOtherKey": "someOtherValue"}`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
result := n.MarshalPolicies()
|
||||||
|
Expect(len(result)).To(Equal(1))
|
||||||
|
|
||||||
|
policy := make(map[string]interface{})
|
||||||
|
err := json.Unmarshal(result[0], &policy)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(policy).Should(HaveKey("someKey"))
|
||||||
|
Expect(policy["someKey"]).To(Equal("someValue"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("when set by user", func() {
|
||||||
|
It("appends exceptions to the existing policy", func() {
|
||||||
|
// first set it
|
||||||
|
n := NetConf{}
|
||||||
|
n.ApplyOutboundNatPolicy("192.168.0.0/16")
|
||||||
|
|
||||||
|
// then attempt to update it
|
||||||
|
n.ApplyOutboundNatPolicy("10.244.0.0/16")
|
||||||
|
|
||||||
|
// it should be unchanged!
|
||||||
|
addlArgs := n.Policies
|
||||||
|
Expect(addlArgs).Should(HaveLen(1))
|
||||||
|
|
||||||
|
policy := addlArgs[0]
|
||||||
|
Expect(policy.Name).Should(Equal("EndpointPolicy"))
|
||||||
|
|
||||||
|
var value map[string]interface{}
|
||||||
|
json.Unmarshal(policy.Value, &value)
|
||||||
|
|
||||||
|
Expect(value).Should(HaveKey("Type"))
|
||||||
|
Expect(value).Should(HaveKey("ExceptionList"))
|
||||||
|
Expect(value["Type"]).Should(Equal("OutBoundNAT"))
|
||||||
|
|
||||||
|
exceptionList := value["ExceptionList"].([]interface{})
|
||||||
|
Expect(exceptionList).Should(HaveLen(2))
|
||||||
|
Expect(exceptionList[0].(string)).Should(Equal("192.168.0.0/16"))
|
||||||
|
Expect(exceptionList[1].(string)).Should(Equal("10.244.0.0/16"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
plugins/ipam/dhcp
|
|
||||||
plugins/main/bridge
|
|
||||||
plugins/main/host-device
|
|
||||||
plugins/main/ipvlan
|
|
||||||
plugins/main/loopback
|
|
||||||
plugins/main/macvlan
|
|
||||||
plugins/main/ptp
|
|
||||||
plugins/main/vlan
|
|
||||||
plugins/meta/portmap
|
|
||||||
plugins/meta/tuning
|
|
||||||
plugins/meta/bandwidth
|
|
||||||
13
plugins/main/windows/build.sh
Executable file
13
plugins/main/windows/build.sh
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
PLUGINS=$(cat plugins/windows_only.txt)
|
||||||
|
for d in $PLUGINS; do
|
||||||
|
if [ -d "$d" ]; then
|
||||||
|
plugin="$(basename "$d").exe"
|
||||||
|
|
||||||
|
echo " $plugin"
|
||||||
|
CXX=x86_64-w64-mingw32-g++ CC=x86_64-w64-mingw32-gcc CGO_ENABLED=1 \
|
||||||
|
$GO build -o "${PWD}/bin/$plugin" "$@" "$REPO_PATH"/$d
|
||||||
|
fi
|
||||||
|
done
|
||||||
25
plugins/main/windows/win-bridge/README.md
Normal file
25
plugins/main/windows/win-bridge/README.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# win-bridge plugin
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
With win-bridge plugin, all containers (on the same host) are plugged into an L2Bridge network that has one endpoint in the host namespace.
|
||||||
|
|
||||||
|
## Example configuration
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"name": "mynet",
|
||||||
|
"type": "win-bridge",
|
||||||
|
"ipMasqNetwork": "10.244.0.0/16",
|
||||||
|
"ipam": {
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": "10.10.0.0/16"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Network configuration reference
|
||||||
|
|
||||||
|
* `name` (string, required): the name of the network.
|
||||||
|
* `type` (string, required): "win-bridge".
|
||||||
|
* `ipMasqNetwork` (string, optional): setup NAT if not empty.
|
||||||
|
* `ipam` (dictionary, required): IPAM configuration to be used for this network.
|
||||||
44
plugins/main/windows/win-bridge/sample.conf
Executable file
44
plugins/main/windows/win-bridge/sample.conf
Executable file
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"name":"cbr0",
|
||||||
|
"type":"flannel",
|
||||||
|
"delegate":{
|
||||||
|
"type":"win-bridge",
|
||||||
|
"dns":{
|
||||||
|
"nameservers":[
|
||||||
|
"11.0.0.10"
|
||||||
|
],
|
||||||
|
"search":[
|
||||||
|
"svc.cluster.local"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"policies":[
|
||||||
|
{
|
||||||
|
"name":"EndpointPolicy",
|
||||||
|
"value":{
|
||||||
|
"Type":"OutBoundNAT",
|
||||||
|
"ExceptionList":[
|
||||||
|
"192.168.0.0/16",
|
||||||
|
"11.0.0.0/8",
|
||||||
|
"10.137.196.0/23"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"EndpointPolicy",
|
||||||
|
"value":{
|
||||||
|
"Type":"ROUTE",
|
||||||
|
"DestinationPrefix":"11.0.0.0/8",
|
||||||
|
"NeedEncap":true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"EndpointPolicy",
|
||||||
|
"value":{
|
||||||
|
"Type":"ROUTE",
|
||||||
|
"DestinationPrefix":"10.137.198.27/32",
|
||||||
|
"NeedEncap":true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
151
plugins/main/windows/win-bridge/win-bridge_windows.go
Normal file
151
plugins/main/windows/win-bridge/win-bridge_windows.go
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
// Copyright 2017 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/juju/errors"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim"
|
||||||
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
"github.com/containernetworking/plugins/pkg/hns"
|
||||||
|
"github.com/containernetworking/plugins/pkg/ipam"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NetConf struct {
|
||||||
|
hns.NetConf
|
||||||
|
|
||||||
|
IPMasqNetwork string `json:"ipMasqNetwork,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// this ensures that main runs only on main thread (thread group leader).
|
||||||
|
// since namespace ops (unshare, setns) are done for a single thread, we
|
||||||
|
// must ensure that the goroutine does not jump from OS thread to thread
|
||||||
|
runtime.LockOSThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadNetConf(bytes []byte) (*NetConf, string, error) {
|
||||||
|
n := &NetConf{}
|
||||||
|
if err := json.Unmarshal(bytes, n); err != nil {
|
||||||
|
return nil, "", fmt.Errorf("failed to load netconf: %v", err)
|
||||||
|
}
|
||||||
|
return n, n.CNIVersion, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdAdd(args *skel.CmdArgs) error {
|
||||||
|
n, cniVersion, err := loadNetConf(args.StdinData)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Annotate(err, "error while loadNetConf")
|
||||||
|
}
|
||||||
|
|
||||||
|
networkName := n.Name
|
||||||
|
hnsNetwork, err := hcsshim.GetHNSNetworkByName(networkName)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Annotatef(err, "error while GETHNSNewtorkByName(%s)", networkName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hnsNetwork == nil {
|
||||||
|
return fmt.Errorf("network %v not found", networkName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.EqualFold(hnsNetwork.Type, "L2Bridge") {
|
||||||
|
return fmt.Errorf("network %v is of an unexpected type: %v", networkName, hnsNetwork.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
epName := hns.ConstructEndpointName(args.ContainerID, args.Netns, n.Name)
|
||||||
|
|
||||||
|
hnsEndpoint, err := hns.ProvisionEndpoint(epName, hnsNetwork.Id, args.ContainerID, func() (*hcsshim.HNSEndpoint, error) {
|
||||||
|
// run the IPAM plugin and get back the config to apply
|
||||||
|
r, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Annotatef(err, "error while ipam.ExecAdd")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert whatever the IPAM result was into the current Result type
|
||||||
|
result, err := current.NewResultFromResult(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Annotatef(err, "error while NewResultFromResult")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result.IPs) == 0 {
|
||||||
|
return nil, errors.New("IPAM plugin return is missing IP config")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate gateway for bridge network (needs to be x.2)
|
||||||
|
gw := result.IPs[0].Address.IP.Mask(result.IPs[0].Address.Mask)
|
||||||
|
gw[len(gw)-1] += 2
|
||||||
|
|
||||||
|
// NAT based on the the configured cluster network
|
||||||
|
if len(n.IPMasqNetwork) != 0 {
|
||||||
|
n.ApplyOutboundNatPolicy(n.IPMasqNetwork)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.DNS = n.DNS
|
||||||
|
|
||||||
|
hnsEndpoint := &hcsshim.HNSEndpoint{
|
||||||
|
Name: epName,
|
||||||
|
VirtualNetwork: hnsNetwork.Id,
|
||||||
|
DNSServerList: strings.Join(result.DNS.Nameservers, ","),
|
||||||
|
DNSSuffix: strings.Join(result.DNS.Search, ","),
|
||||||
|
GatewayAddress: gw.String(),
|
||||||
|
IPAddress: result.IPs[0].Address.IP,
|
||||||
|
Policies: n.MarshalPolicies(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return hnsEndpoint, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return errors.Annotatef(err, "error while ProvisionEndpoint(%v,%v,%v)", epName, hnsNetwork.Id, args.ContainerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := hns.ConstructResult(hnsNetwork, hnsEndpoint)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Annotatef(err, "error while constructResult")
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.PrintResult(result, cniVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdDel(args *skel.CmdArgs) error {
|
||||||
|
n, _, err := loadNetConf(args.StdinData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipam.ExecDel(n.IPAM.Type, args.StdinData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
epName := hns.ConstructEndpointName(args.ContainerID, args.Netns, n.Name)
|
||||||
|
|
||||||
|
return hns.DeprovisionEndpoint(epName, args.Netns, args.ContainerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdGet(_ *skel.CmdArgs) error {
|
||||||
|
// TODO: implement
|
||||||
|
return fmt.Errorf("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
skel.PluginMain(cmdAdd, cmdGet, cmdDel, version.All, "TODO")
|
||||||
|
}
|
||||||
27
plugins/main/windows/win-overlay/README.md
Normal file
27
plugins/main/windows/win-overlay/README.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# win-overlay plugin
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
With win-overlay plugin, all containers (on the same host) are plugged into an Overlay network based on VXLAN encapsulation.
|
||||||
|
|
||||||
|
## Example configuration
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"name": "mynet",
|
||||||
|
"type": "win-overlay",
|
||||||
|
"ipMasq": true,
|
||||||
|
"endpointMacPrefix": "0E-2A",
|
||||||
|
"ipam": {
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": "10.10.0.0/16"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Network configuration reference
|
||||||
|
|
||||||
|
* `name` (string, required): the name of the network.
|
||||||
|
* `type` (string, required): "win-overlay".
|
||||||
|
* `ipMasq` (bool, optional): the inverse of `$FLANNEL_IPMASQ`, setup NAT for the hnsNetwork subnet.
|
||||||
|
* `endpointMacPrefix` (string, optional): set to the MAC prefix configured for Flannel
|
||||||
|
* `ipam` (dictionary, required): IPAM configuration to be used for this network.
|
||||||
36
plugins/main/windows/win-overlay/sample.conf
Executable file
36
plugins/main/windows/win-overlay/sample.conf
Executable file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"cniVersion":"0.2.0",
|
||||||
|
"name":"vxlan0",
|
||||||
|
"type":"flannel",
|
||||||
|
"delegate":{
|
||||||
|
"type":"win-overlay",
|
||||||
|
"dns":{
|
||||||
|
"nameservers":[
|
||||||
|
"11.0.0.10"
|
||||||
|
],
|
||||||
|
"search":[
|
||||||
|
"svc.cluster.local"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"policies":[
|
||||||
|
{
|
||||||
|
"name":"EndpointPolicy",
|
||||||
|
"value":{
|
||||||
|
"Type":"OutBoundNAT",
|
||||||
|
"ExceptionList":[
|
||||||
|
"192.168.0.0/16",
|
||||||
|
"11.0.0.0/8"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"EndpointPolicy",
|
||||||
|
"value":{
|
||||||
|
"Type":"ROUTE",
|
||||||
|
"DestinationPrefix":"11.0.0.0/8",
|
||||||
|
"NeedEncap":true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
166
plugins/main/windows/win-overlay/win-overlay_windows.go
Normal file
166
plugins/main/windows/win-overlay/win-overlay_windows.go
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
// Copyright 2017 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/juju/errors"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim"
|
||||||
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
"github.com/containernetworking/plugins/pkg/hns"
|
||||||
|
"github.com/containernetworking/plugins/pkg/ipam"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NetConf struct {
|
||||||
|
hns.NetConf
|
||||||
|
|
||||||
|
IPMasq bool `json:"ipMasq"`
|
||||||
|
EndpointMacPrefix string `json:"endpointMacPrefix,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// this ensures that main runs only on main thread (thread group leader).
|
||||||
|
// since namespace ops (unshare, setns) are done for a single thread, we
|
||||||
|
// must ensure that the goroutine does not jump from OS thread to thread
|
||||||
|
runtime.LockOSThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadNetConf(bytes []byte) (*NetConf, string, error) {
|
||||||
|
n := &NetConf{}
|
||||||
|
if err := json.Unmarshal(bytes, n); err != nil {
|
||||||
|
return nil, "", fmt.Errorf("failed to load netconf: %v", err)
|
||||||
|
}
|
||||||
|
return n, n.CNIVersion, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdAdd(args *skel.CmdArgs) error {
|
||||||
|
n, cniVersion, err := loadNetConf(args.StdinData)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Annotate(err, "error while loadNetConf")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(n.EndpointMacPrefix) != 0 {
|
||||||
|
if len(n.EndpointMacPrefix) != 5 || n.EndpointMacPrefix[2] != '-' {
|
||||||
|
return fmt.Errorf("endpointMacPrefix [%v] is invalid, value must be of the format xx-xx", n.EndpointMacPrefix)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n.EndpointMacPrefix = "0E-2A"
|
||||||
|
}
|
||||||
|
|
||||||
|
networkName := n.Name
|
||||||
|
hnsNetwork, err := hcsshim.GetHNSNetworkByName(networkName)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Annotatef(err, "error while GETHNSNewtorkByName(%s)", networkName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hnsNetwork == nil {
|
||||||
|
return fmt.Errorf("network %v not found", networkName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.EqualFold(hnsNetwork.Type, "Overlay") {
|
||||||
|
return fmt.Errorf("network %v is of an unexpected type: %v", networkName, hnsNetwork.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
epName := hns.ConstructEndpointName(args.ContainerID, args.Netns, n.Name)
|
||||||
|
|
||||||
|
hnsEndpoint, err := hns.ProvisionEndpoint(epName, hnsNetwork.Id, args.ContainerID, func() (*hcsshim.HNSEndpoint, error) {
|
||||||
|
// run the IPAM plugin and get back the config to apply
|
||||||
|
r, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Annotatef(err, "error while ipam.ExecAdd")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert whatever the IPAM result was into the current Result type
|
||||||
|
result, err := current.NewResultFromResult(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Annotatef(err, "error while NewResultFromResult")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result.IPs) == 0 {
|
||||||
|
return nil, errors.New("IPAM plugin return is missing IP config")
|
||||||
|
}
|
||||||
|
|
||||||
|
ipAddr := result.IPs[0].Address.IP.To4()
|
||||||
|
if ipAddr == nil {
|
||||||
|
return nil, errors.New("win-overlay doesn't support IPv6 now")
|
||||||
|
}
|
||||||
|
|
||||||
|
// conjure a MAC based on the IP for Overlay
|
||||||
|
macAddr := fmt.Sprintf("%v-%02x-%02x-%02x-%02x", n.EndpointMacPrefix, ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3])
|
||||||
|
// use the HNS network gateway
|
||||||
|
gw := hnsNetwork.Subnets[0].GatewayAddress
|
||||||
|
n.ApplyDefaultPAPolicy(hnsNetwork.ManagementIP)
|
||||||
|
if n.IPMasq {
|
||||||
|
n.ApplyOutboundNatPolicy(hnsNetwork.Subnets[0].AddressPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.DNS = n.DNS
|
||||||
|
|
||||||
|
hnsEndpoint := &hcsshim.HNSEndpoint{
|
||||||
|
Name: epName,
|
||||||
|
VirtualNetwork: hnsNetwork.Id,
|
||||||
|
DNSServerList: strings.Join(result.DNS.Nameservers, ","),
|
||||||
|
DNSSuffix: strings.Join(result.DNS.Search, ","),
|
||||||
|
GatewayAddress: gw,
|
||||||
|
IPAddress: ipAddr,
|
||||||
|
MacAddress: macAddr,
|
||||||
|
Policies: n.MarshalPolicies(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return hnsEndpoint, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return errors.Annotatef(err, "error while ProvisionEndpoint(%v,%v,%v)", epName, hnsNetwork.Id, args.ContainerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := hns.ConstructResult(hnsNetwork, hnsEndpoint)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Annotatef(err, "error while constructResult")
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.PrintResult(result, cniVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdDel(args *skel.CmdArgs) error {
|
||||||
|
n, _, err := loadNetConf(args.StdinData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ipam.ExecDel(n.IPAM.Type, args.StdinData); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
epName := hns.ConstructEndpointName(args.ContainerID, args.Netns, n.Name)
|
||||||
|
|
||||||
|
return hns.DeprovisionEndpoint(epName, args.Netns, args.ContainerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdGet(_ *skel.CmdArgs) error {
|
||||||
|
// TODO: implement
|
||||||
|
return fmt.Errorf("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
skel.PluginMain(cmdAdd, cmdGet, cmdDel, version.All, "TODO")
|
||||||
|
}
|
||||||
@@ -86,3 +86,50 @@ flannel plugin will set the following fields in the delegated plugin configurati
|
|||||||
* `mtu`: `$FLANNEL_MTU`
|
* `mtu`: `$FLANNEL_MTU`
|
||||||
|
|
||||||
Additionally, for the bridge plugin, `isGateway` will be set to `true`, if not present.
|
Additionally, for the bridge plugin, `isGateway` will be set to `true`, if not present.
|
||||||
|
|
||||||
|
## Windows Support (Experimental)
|
||||||
|
This plugin supports delegating to the windows CNI plugins (overlay.exe, l2bridge.exe) to work in conjunction with [Flannel on Windows](https://github.com/coreos/flannel/issues/833).
|
||||||
|
Flannel sets up an [HNS Network](https://docs.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/container-networking) in L2Bridge mode for host-gw and in Overlay mode for vxlan.
|
||||||
|
|
||||||
|
The following fields must be set in the delegated plugin configuration:
|
||||||
|
* `name` (string, required): the name of the network (must match the name in Flannel config / name of the HNS network)
|
||||||
|
* `type` (string, optional): set to `win-l2bridge` by default. Can be set to `win-overlay` or other custom windows CNI
|
||||||
|
* `ipMasq`: the inverse of `$FLANNEL_IPMASQ`
|
||||||
|
* `endpointMacPrefix` (string, optional): required for `win-overlay` mode, set to the MAC prefix configured for Flannel
|
||||||
|
* `clusterNetworkPrefix` (string, optional): required for `win-l2bridge` mode, setup NAT if `ipMasq` is set to true
|
||||||
|
|
||||||
|
For `win-l2bridge`, the Flannel CNI plugin will set:
|
||||||
|
* `ipam`: "host-local" type will be used with "subnet" set to `$FLANNEL_SUBNET` and gateway as the .2 address in `$FLANNEL_NETWORK`
|
||||||
|
|
||||||
|
For `win-overlay`, the Flannel CNI plugin will set:
|
||||||
|
* `ipam`: "host-local" type will be used with "subnet" set to `$FLANNEL_SUBNET` and gateway as the .1 address in `$FLANNEL_NETWORK`
|
||||||
|
|
||||||
|
If IPMASQ is true, the Flannel CNI plugin will setup an OutBoundNAT policy and add FLANNEL_SUBNET to any existing exclusions.
|
||||||
|
|
||||||
|
All other delegate config e.g. other HNS endpoint policies in AdditionalArgs will be passed to WINCNI as-is.
|
||||||
|
|
||||||
|
Example VXLAN Flannel CNI config
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"name": "mynet",
|
||||||
|
"type": "flannel",
|
||||||
|
"delegate": {
|
||||||
|
"type": "win-overlay",
|
||||||
|
"endpointMacPrefix": "0E-2A"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For this example, Flannel CNI would generate the following config to delegate to the windows CNI when FLANNEL_NETWORK=10.244.0.0/16, FLANNEL_SUBNET=10.244.1.0/24 and IPMASQ=true
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"name": "mynet",
|
||||||
|
"type": "win-overlay",
|
||||||
|
"endpointMacPrefix": "0E-2A",
|
||||||
|
"ipMasq": true,
|
||||||
|
"ipam": {
|
||||||
|
"subnet": "10.244.1.0/24",
|
||||||
|
"type": "host-local"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -42,6 +42,7 @@ const (
|
|||||||
|
|
||||||
type NetConf struct {
|
type NetConf struct {
|
||||||
types.NetConf
|
types.NetConf
|
||||||
|
|
||||||
SubnetFile string `json:"subnetFile"`
|
SubnetFile string `json:"subnetFile"`
|
||||||
DataDir string `json:"dataDir"`
|
DataDir string `json:"dataDir"`
|
||||||
Delegate map[string]interface{} `json:"delegate"`
|
Delegate map[string]interface{} `json:"delegate"`
|
||||||
@@ -202,43 +203,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
n.Delegate["name"] = n.Name
|
return doCmdAdd(args, n, fenv)
|
||||||
|
|
||||||
if !hasKey(n.Delegate, "type") {
|
|
||||||
n.Delegate["type"] = "bridge"
|
|
||||||
}
|
|
||||||
|
|
||||||
if !hasKey(n.Delegate, "ipMasq") {
|
|
||||||
// if flannel is not doing ipmasq, we should
|
|
||||||
ipmasq := !*fenv.ipmasq
|
|
||||||
n.Delegate["ipMasq"] = ipmasq
|
|
||||||
}
|
|
||||||
|
|
||||||
if !hasKey(n.Delegate, "mtu") {
|
|
||||||
mtu := fenv.mtu
|
|
||||||
n.Delegate["mtu"] = mtu
|
|
||||||
}
|
|
||||||
|
|
||||||
if n.Delegate["type"].(string) == "bridge" {
|
|
||||||
if !hasKey(n.Delegate, "isGateway") {
|
|
||||||
n.Delegate["isGateway"] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if n.CNIVersion != "" {
|
|
||||||
n.Delegate["cniVersion"] = n.CNIVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
n.Delegate["ipam"] = map[string]interface{}{
|
|
||||||
"type": "host-local",
|
|
||||||
"subnet": fenv.sn.String(),
|
|
||||||
"routes": []types.Route{
|
|
||||||
types.Route{
|
|
||||||
Dst: *fenv.nw,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return delegateAdd(args.ContainerID, n.DataDir, n.Delegate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdDel(args *skel.CmdArgs) error {
|
func cmdDel(args *skel.CmdArgs) error {
|
||||||
@@ -247,25 +212,10 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
netconfBytes, err := consumeScratchNetConf(args.ContainerID, nc.DataDir)
|
return doCmdDel(args, nc)
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
// Per spec should ignore error if resources are missing / already removed
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
n := &types.NetConf{}
|
|
||||||
if err = json.Unmarshal(netconfBytes, n); err != nil {
|
|
||||||
return fmt.Errorf("failed to parse netconf: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return invoke.DelegateDel(n.Type, netconfBytes, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// TODO: implement plugin version
|
|
||||||
skel.PluginMain(cmdAdd, cmdGet, cmdDel, version.All, "TODO")
|
skel.PluginMain(cmdAdd, cmdGet, cmdDel, version.All, "TODO")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
86
plugins/meta/flannel/flannel_linux.go
Normal file
86
plugins/meta/flannel/flannel_linux.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
// Copyright 2018 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// This is a "meta-plugin". It reads in its own netconf, combines it with
|
||||||
|
// the data from flannel generated subnet file and then invokes a plugin
|
||||||
|
// like bridge or ipvlan to do the real work.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/containernetworking/cni/pkg/invoke"
|
||||||
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func doCmdAdd(args *skel.CmdArgs, n *NetConf, fenv *subnetEnv) error {
|
||||||
|
n.Delegate["name"] = n.Name
|
||||||
|
|
||||||
|
if !hasKey(n.Delegate, "type") {
|
||||||
|
n.Delegate["type"] = "bridge"
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasKey(n.Delegate, "ipMasq") {
|
||||||
|
// if flannel is not doing ipmasq, we should
|
||||||
|
ipmasq := !*fenv.ipmasq
|
||||||
|
n.Delegate["ipMasq"] = ipmasq
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasKey(n.Delegate, "mtu") {
|
||||||
|
mtu := fenv.mtu
|
||||||
|
n.Delegate["mtu"] = mtu
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.Delegate["type"].(string) == "bridge" {
|
||||||
|
if !hasKey(n.Delegate, "isGateway") {
|
||||||
|
n.Delegate["isGateway"] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n.CNIVersion != "" {
|
||||||
|
n.Delegate["cniVersion"] = n.CNIVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
n.Delegate["ipam"] = map[string]interface{}{
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": fenv.sn.String(),
|
||||||
|
"routes": []types.Route{
|
||||||
|
{
|
||||||
|
Dst: *fenv.nw,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return delegateAdd(args.ContainerID, n.DataDir, n.Delegate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doCmdDel(args *skel.CmdArgs, n *NetConf) error {
|
||||||
|
netconfBytes, err := consumeScratchNetConf(args.ContainerID, n.DataDir)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// Per spec should ignore error if resources are missing / already removed
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nc := &types.NetConf{}
|
||||||
|
if err = json.Unmarshal(netconfBytes, nc); err != nil {
|
||||||
|
return fmt.Errorf("failed to parse netconf: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoke.DelegateDel(nc.Type, netconfBytes, nil)
|
||||||
|
}
|
||||||
73
plugins/meta/flannel/flannel_windows.go
Normal file
73
plugins/meta/flannel/flannel_windows.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// Copyright 2018 CNI authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// This is a "meta-plugin". It reads in its own netconf, combines it with
|
||||||
|
// the data from flannel generated subnet file and then invokes a plugin
|
||||||
|
// like bridge or ipvlan to do the real work.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/containernetworking/cni/pkg/invoke"
|
||||||
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
"github.com/containernetworking/cni/pkg/types/020"
|
||||||
|
"github.com/containernetworking/plugins/pkg/hns"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func doCmdAdd(args *skel.CmdArgs, n *NetConf, fenv *subnetEnv) error {
|
||||||
|
n.Delegate["name"] = n.Name
|
||||||
|
|
||||||
|
if !hasKey(n.Delegate, "type") {
|
||||||
|
n.Delegate["type"] = "win-bridge"
|
||||||
|
}
|
||||||
|
|
||||||
|
// if flannel needs ipmasq - get the plugin to configure it
|
||||||
|
// (this is the opposite of how linux works - on linux the flannel daemon configure ipmasq)
|
||||||
|
n.Delegate["ipMasq"] = *fenv.ipmasq
|
||||||
|
n.Delegate["ipMasqNetwork"] = fenv.nw.String()
|
||||||
|
|
||||||
|
n.Delegate["cniVersion"] = types020.ImplementedSpecVersion
|
||||||
|
if len(n.CNIVersion) != 0 {
|
||||||
|
n.Delegate["cniVersion"] = n.CNIVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
n.Delegate["ipam"] = map[string]interface{}{
|
||||||
|
"type": "host-local",
|
||||||
|
"subnet": fenv.sn.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return delegateAdd(hns.GetSandboxContainerID(args.ContainerID, args.Netns), n.DataDir, n.Delegate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doCmdDel(args *skel.CmdArgs, n *NetConf) error {
|
||||||
|
netconfBytes, err := consumeScratchNetConf(hns.GetSandboxContainerID(args.ContainerID, args.Netns), n.DataDir)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// Per spec should ignore error if resources are missing / already removed
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nc := &types.NetConf{}
|
||||||
|
if err = json.Unmarshal(netconfBytes, nc); err != nil {
|
||||||
|
return fmt.Errorf("failed to parse netconf: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoke.DelegateDel(nc.Type, netconfBytes, nil)
|
||||||
|
}
|
||||||
4
plugins/windows_only.txt
Normal file
4
plugins/windows_only.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
plugins/ipam/host-local
|
||||||
|
plugins/main/windows/win-bridge
|
||||||
|
plugins/main/windows/win-overlay
|
||||||
|
plugins/meta/flannel
|
||||||
1
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
Normal file
1
vendor/github.com/Microsoft/go-winio/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.exe
|
||||||
22
vendor/github.com/Microsoft/go-winio/LICENSE
generated
vendored
Normal file
22
vendor/github.com/Microsoft/go-winio/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Microsoft
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
22
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
Normal file
22
vendor/github.com/Microsoft/go-winio/README.md
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# go-winio
|
||||||
|
|
||||||
|
This repository contains utilities for efficiently performing Win32 IO operations in
|
||||||
|
Go. Currently, this is focused on accessing named pipes and other file handles, and
|
||||||
|
for using named pipes as a net transport.
|
||||||
|
|
||||||
|
This code relies on IO completion ports to avoid blocking IO on system threads, allowing Go
|
||||||
|
to reuse the thread to schedule another goroutine. This limits support to Windows Vista and
|
||||||
|
newer operating systems. This is similar to the implementation of network sockets in Go's net
|
||||||
|
package.
|
||||||
|
|
||||||
|
Please see the LICENSE file for licensing information.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Thanks to natefinch for the inspiration for this library. See https://github.com/natefinch/npipe
|
||||||
|
for another named pipe implementation.
|
||||||
280
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
Normal file
280
vendor/github.com/Microsoft/go-winio/backup.go
generated
vendored
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupRead
|
||||||
|
//sys backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) = BackupWrite
|
||||||
|
|
||||||
|
const (
|
||||||
|
BackupData = uint32(iota + 1)
|
||||||
|
BackupEaData
|
||||||
|
BackupSecurity
|
||||||
|
BackupAlternateData
|
||||||
|
BackupLink
|
||||||
|
BackupPropertyData
|
||||||
|
BackupObjectId
|
||||||
|
BackupReparseData
|
||||||
|
BackupSparseBlock
|
||||||
|
BackupTxfsData
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
StreamSparseAttributes = uint32(8)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
WRITE_DAC = 0x40000
|
||||||
|
WRITE_OWNER = 0x80000
|
||||||
|
ACCESS_SYSTEM_SECURITY = 0x1000000
|
||||||
|
)
|
||||||
|
|
||||||
|
// BackupHeader represents a backup stream of a file.
|
||||||
|
type BackupHeader struct {
|
||||||
|
Id uint32 // The backup stream ID
|
||||||
|
Attributes uint32 // Stream attributes
|
||||||
|
Size int64 // The size of the stream in bytes
|
||||||
|
Name string // The name of the stream (for BackupAlternateData only).
|
||||||
|
Offset int64 // The offset of the stream in the file (for BackupSparseBlock only).
|
||||||
|
}
|
||||||
|
|
||||||
|
type win32StreamId struct {
|
||||||
|
StreamId uint32
|
||||||
|
Attributes uint32
|
||||||
|
Size uint64
|
||||||
|
NameSize uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupStreamReader reads from a stream produced by the BackupRead Win32 API and produces a series
|
||||||
|
// of BackupHeader values.
|
||||||
|
type BackupStreamReader struct {
|
||||||
|
r io.Reader
|
||||||
|
bytesLeft int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupStreamReader produces a BackupStreamReader from any io.Reader.
|
||||||
|
func NewBackupStreamReader(r io.Reader) *BackupStreamReader {
|
||||||
|
return &BackupStreamReader{r, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next returns the next backup stream and prepares for calls to Read(). It skips the remainder of the current stream if
|
||||||
|
// it was not completely read.
|
||||||
|
func (r *BackupStreamReader) Next() (*BackupHeader, error) {
|
||||||
|
if r.bytesLeft > 0 {
|
||||||
|
if s, ok := r.r.(io.Seeker); ok {
|
||||||
|
// Make sure Seek on io.SeekCurrent sometimes succeeds
|
||||||
|
// before trying the actual seek.
|
||||||
|
if _, err := s.Seek(0, io.SeekCurrent); err == nil {
|
||||||
|
if _, err = s.Seek(r.bytesLeft, io.SeekCurrent); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r.bytesLeft = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(ioutil.Discard, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var wsi win32StreamId
|
||||||
|
if err := binary.Read(r.r, binary.LittleEndian, &wsi); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hdr := &BackupHeader{
|
||||||
|
Id: wsi.StreamId,
|
||||||
|
Attributes: wsi.Attributes,
|
||||||
|
Size: int64(wsi.Size),
|
||||||
|
}
|
||||||
|
if wsi.NameSize != 0 {
|
||||||
|
name := make([]uint16, int(wsi.NameSize/2))
|
||||||
|
if err := binary.Read(r.r, binary.LittleEndian, name); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hdr.Name = syscall.UTF16ToString(name)
|
||||||
|
}
|
||||||
|
if wsi.StreamId == BackupSparseBlock {
|
||||||
|
if err := binary.Read(r.r, binary.LittleEndian, &hdr.Offset); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hdr.Size -= 8
|
||||||
|
}
|
||||||
|
r.bytesLeft = hdr.Size
|
||||||
|
return hdr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from the current backup stream.
|
||||||
|
func (r *BackupStreamReader) Read(b []byte) (int, error) {
|
||||||
|
if r.bytesLeft == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
if int64(len(b)) > r.bytesLeft {
|
||||||
|
b = b[:r.bytesLeft]
|
||||||
|
}
|
||||||
|
n, err := r.r.Read(b)
|
||||||
|
r.bytesLeft -= int64(n)
|
||||||
|
if err == io.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
} else if r.bytesLeft == 0 && err == nil {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupStreamWriter writes a stream compatible with the BackupWrite Win32 API.
|
||||||
|
type BackupStreamWriter struct {
|
||||||
|
w io.Writer
|
||||||
|
bytesLeft int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupStreamWriter produces a BackupStreamWriter on top of an io.Writer.
|
||||||
|
func NewBackupStreamWriter(w io.Writer) *BackupStreamWriter {
|
||||||
|
return &BackupStreamWriter{w, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteHeader writes the next backup stream header and prepares for calls to Write().
|
||||||
|
func (w *BackupStreamWriter) WriteHeader(hdr *BackupHeader) error {
|
||||||
|
if w.bytesLeft != 0 {
|
||||||
|
return fmt.Errorf("missing %d bytes", w.bytesLeft)
|
||||||
|
}
|
||||||
|
name := utf16.Encode([]rune(hdr.Name))
|
||||||
|
wsi := win32StreamId{
|
||||||
|
StreamId: hdr.Id,
|
||||||
|
Attributes: hdr.Attributes,
|
||||||
|
Size: uint64(hdr.Size),
|
||||||
|
NameSize: uint32(len(name) * 2),
|
||||||
|
}
|
||||||
|
if hdr.Id == BackupSparseBlock {
|
||||||
|
// Include space for the int64 block offset
|
||||||
|
wsi.Size += 8
|
||||||
|
}
|
||||||
|
if err := binary.Write(w.w, binary.LittleEndian, &wsi); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(name) != 0 {
|
||||||
|
if err := binary.Write(w.w, binary.LittleEndian, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hdr.Id == BackupSparseBlock {
|
||||||
|
if err := binary.Write(w.w, binary.LittleEndian, hdr.Offset); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.bytesLeft = hdr.Size
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes to the current backup stream.
|
||||||
|
func (w *BackupStreamWriter) Write(b []byte) (int, error) {
|
||||||
|
if w.bytesLeft < int64(len(b)) {
|
||||||
|
return 0, fmt.Errorf("too many bytes by %d", int64(len(b))-w.bytesLeft)
|
||||||
|
}
|
||||||
|
n, err := w.w.Write(b)
|
||||||
|
w.bytesLeft -= int64(n)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupFileReader provides an io.ReadCloser interface on top of the BackupRead Win32 API.
|
||||||
|
type BackupFileReader struct {
|
||||||
|
f *os.File
|
||||||
|
includeSecurity bool
|
||||||
|
ctx uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupFileReader returns a new BackupFileReader from a file handle. If includeSecurity is true,
|
||||||
|
// Read will attempt to read the security descriptor of the file.
|
||||||
|
func NewBackupFileReader(f *os.File, includeSecurity bool) *BackupFileReader {
|
||||||
|
r := &BackupFileReader{f, includeSecurity, 0}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads a backup stream from the file by calling the Win32 API BackupRead().
|
||||||
|
func (r *BackupFileReader) Read(b []byte) (int, error) {
|
||||||
|
var bytesRead uint32
|
||||||
|
err := backupRead(syscall.Handle(r.f.Fd()), b, &bytesRead, false, r.includeSecurity, &r.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, &os.PathError{"BackupRead", r.f.Name(), err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(r.f)
|
||||||
|
if bytesRead == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
return int(bytesRead), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close frees Win32 resources associated with the BackupFileReader. It does not close
|
||||||
|
// the underlying file.
|
||||||
|
func (r *BackupFileReader) Close() error {
|
||||||
|
if r.ctx != 0 {
|
||||||
|
backupRead(syscall.Handle(r.f.Fd()), nil, nil, true, false, &r.ctx)
|
||||||
|
runtime.KeepAlive(r.f)
|
||||||
|
r.ctx = 0
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BackupFileWriter provides an io.WriteCloser interface on top of the BackupWrite Win32 API.
|
||||||
|
type BackupFileWriter struct {
|
||||||
|
f *os.File
|
||||||
|
includeSecurity bool
|
||||||
|
ctx uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBackupFileWriter returns a new BackupFileWriter from a file handle. If includeSecurity is true,
|
||||||
|
// Write() will attempt to restore the security descriptor from the stream.
|
||||||
|
func NewBackupFileWriter(f *os.File, includeSecurity bool) *BackupFileWriter {
|
||||||
|
w := &BackupFileWriter{f, includeSecurity, 0}
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write restores a portion of the file using the provided backup stream.
|
||||||
|
func (w *BackupFileWriter) Write(b []byte) (int, error) {
|
||||||
|
var bytesWritten uint32
|
||||||
|
err := backupWrite(syscall.Handle(w.f.Fd()), b, &bytesWritten, false, w.includeSecurity, &w.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, &os.PathError{"BackupWrite", w.f.Name(), err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(w.f)
|
||||||
|
if int(bytesWritten) != len(b) {
|
||||||
|
return int(bytesWritten), errors.New("not all bytes could be written")
|
||||||
|
}
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close frees Win32 resources associated with the BackupFileWriter. It does not
|
||||||
|
// close the underlying file.
|
||||||
|
func (w *BackupFileWriter) Close() error {
|
||||||
|
if w.ctx != 0 {
|
||||||
|
backupWrite(syscall.Handle(w.f.Fd()), nil, nil, true, false, &w.ctx)
|
||||||
|
runtime.KeepAlive(w.f)
|
||||||
|
w.ctx = 0
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenForBackup opens a file or directory, potentially skipping access checks if the backup
|
||||||
|
// or restore privileges have been acquired.
|
||||||
|
//
|
||||||
|
// If the file opened was a directory, it cannot be used with Readdir().
|
||||||
|
func OpenForBackup(path string, access uint32, share uint32, createmode uint32) (*os.File, error) {
|
||||||
|
winPath, err := syscall.UTF16FromString(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
h, err := syscall.CreateFile(&winPath[0], access, share, nil, createmode, syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OPEN_REPARSE_POINT, 0)
|
||||||
|
if err != nil {
|
||||||
|
err = &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return os.NewFile(uintptr(h), path), nil
|
||||||
|
}
|
||||||
137
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
Normal file
137
vendor/github.com/Microsoft/go-winio/ea.go
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fileFullEaInformation struct {
|
||||||
|
NextEntryOffset uint32
|
||||||
|
Flags uint8
|
||||||
|
NameLength uint8
|
||||||
|
ValueLength uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
fileFullEaInformationSize = binary.Size(&fileFullEaInformation{})
|
||||||
|
|
||||||
|
errInvalidEaBuffer = errors.New("invalid extended attribute buffer")
|
||||||
|
errEaNameTooLarge = errors.New("extended attribute name too large")
|
||||||
|
errEaValueTooLarge = errors.New("extended attribute value too large")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExtendedAttribute represents a single Windows EA.
|
||||||
|
type ExtendedAttribute struct {
|
||||||
|
Name string
|
||||||
|
Value []byte
|
||||||
|
Flags uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseEa(b []byte) (ea ExtendedAttribute, nb []byte, err error) {
|
||||||
|
var info fileFullEaInformation
|
||||||
|
err = binary.Read(bytes.NewReader(b), binary.LittleEndian, &info)
|
||||||
|
if err != nil {
|
||||||
|
err = errInvalidEaBuffer
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nameOffset := fileFullEaInformationSize
|
||||||
|
nameLen := int(info.NameLength)
|
||||||
|
valueOffset := nameOffset + int(info.NameLength) + 1
|
||||||
|
valueLen := int(info.ValueLength)
|
||||||
|
nextOffset := int(info.NextEntryOffset)
|
||||||
|
if valueLen+valueOffset > len(b) || nextOffset < 0 || nextOffset > len(b) {
|
||||||
|
err = errInvalidEaBuffer
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ea.Name = string(b[nameOffset : nameOffset+nameLen])
|
||||||
|
ea.Value = b[valueOffset : valueOffset+valueLen]
|
||||||
|
ea.Flags = info.Flags
|
||||||
|
if info.NextEntryOffset != 0 {
|
||||||
|
nb = b[info.NextEntryOffset:]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeExtendedAttributes decodes a list of EAs from a FILE_FULL_EA_INFORMATION
|
||||||
|
// buffer retrieved from BackupRead, ZwQueryEaFile, etc.
|
||||||
|
func DecodeExtendedAttributes(b []byte) (eas []ExtendedAttribute, err error) {
|
||||||
|
for len(b) != 0 {
|
||||||
|
ea, nb, err := parseEa(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
eas = append(eas, ea)
|
||||||
|
b = nb
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeEa(buf *bytes.Buffer, ea *ExtendedAttribute, last bool) error {
|
||||||
|
if int(uint8(len(ea.Name))) != len(ea.Name) {
|
||||||
|
return errEaNameTooLarge
|
||||||
|
}
|
||||||
|
if int(uint16(len(ea.Value))) != len(ea.Value) {
|
||||||
|
return errEaValueTooLarge
|
||||||
|
}
|
||||||
|
entrySize := uint32(fileFullEaInformationSize + len(ea.Name) + 1 + len(ea.Value))
|
||||||
|
withPadding := (entrySize + 3) &^ 3
|
||||||
|
nextOffset := uint32(0)
|
||||||
|
if !last {
|
||||||
|
nextOffset = withPadding
|
||||||
|
}
|
||||||
|
info := fileFullEaInformation{
|
||||||
|
NextEntryOffset: nextOffset,
|
||||||
|
Flags: ea.Flags,
|
||||||
|
NameLength: uint8(len(ea.Name)),
|
||||||
|
ValueLength: uint16(len(ea.Value)),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := binary.Write(buf, binary.LittleEndian, &info)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = buf.Write([]byte(ea.Name))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = buf.WriteByte(0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = buf.Write(ea.Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = buf.Write([]byte{0, 0, 0}[0 : withPadding-entrySize])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeExtendedAttributes encodes a list of EAs into a FILE_FULL_EA_INFORMATION
|
||||||
|
// buffer for use with BackupWrite, ZwSetEaFile, etc.
|
||||||
|
func EncodeExtendedAttributes(eas []ExtendedAttribute) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for i := range eas {
|
||||||
|
last := false
|
||||||
|
if i == len(eas)-1 {
|
||||||
|
last = true
|
||||||
|
}
|
||||||
|
|
||||||
|
err := writeEa(&buf, &eas[i], last)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
307
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
Normal file
307
vendor/github.com/Microsoft/go-winio/file.go
generated
vendored
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) = CancelIoEx
|
||||||
|
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
|
||||||
|
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
|
||||||
|
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
|
||||||
|
|
||||||
|
type atomicBool int32
|
||||||
|
|
||||||
|
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
||||||
|
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
|
||||||
|
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
|
||||||
|
func (b *atomicBool) swap(new bool) bool {
|
||||||
|
var newInt int32
|
||||||
|
if new {
|
||||||
|
newInt = 1
|
||||||
|
}
|
||||||
|
return atomic.SwapInt32((*int32)(b), newInt) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
|
||||||
|
cFILE_SKIP_SET_EVENT_ON_HANDLE = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrFileClosed = errors.New("file has already been closed")
|
||||||
|
ErrTimeout = &timeoutError{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type timeoutError struct{}
|
||||||
|
|
||||||
|
func (e *timeoutError) Error() string { return "i/o timeout" }
|
||||||
|
func (e *timeoutError) Timeout() bool { return true }
|
||||||
|
func (e *timeoutError) Temporary() bool { return true }
|
||||||
|
|
||||||
|
type timeoutChan chan struct{}
|
||||||
|
|
||||||
|
var ioInitOnce sync.Once
|
||||||
|
var ioCompletionPort syscall.Handle
|
||||||
|
|
||||||
|
// ioResult contains the result of an asynchronous IO operation
|
||||||
|
type ioResult struct {
|
||||||
|
bytes uint32
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ioOperation represents an outstanding asynchronous Win32 IO
|
||||||
|
type ioOperation struct {
|
||||||
|
o syscall.Overlapped
|
||||||
|
ch chan ioResult
|
||||||
|
}
|
||||||
|
|
||||||
|
func initIo() {
|
||||||
|
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ioCompletionPort = h
|
||||||
|
go ioCompletionProcessor(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// win32File implements Reader, Writer, and Closer on a Win32 handle without blocking in a syscall.
|
||||||
|
// It takes ownership of this handle and will close it if it is garbage collected.
|
||||||
|
type win32File struct {
|
||||||
|
handle syscall.Handle
|
||||||
|
wg sync.WaitGroup
|
||||||
|
wgLock sync.RWMutex
|
||||||
|
closing atomicBool
|
||||||
|
readDeadline deadlineHandler
|
||||||
|
writeDeadline deadlineHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
type deadlineHandler struct {
|
||||||
|
setLock sync.Mutex
|
||||||
|
channel timeoutChan
|
||||||
|
channelLock sync.RWMutex
|
||||||
|
timer *time.Timer
|
||||||
|
timedout atomicBool
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeWin32File makes a new win32File from an existing file handle
|
||||||
|
func makeWin32File(h syscall.Handle) (*win32File, error) {
|
||||||
|
f := &win32File{handle: h}
|
||||||
|
ioInitOnce.Do(initIo)
|
||||||
|
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = setFileCompletionNotificationModes(h, cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f.readDeadline.channel = make(timeoutChan)
|
||||||
|
f.writeDeadline.channel = make(timeoutChan)
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeOpenFile(h syscall.Handle) (io.ReadWriteCloser, error) {
|
||||||
|
return makeWin32File(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// closeHandle closes the resources associated with a Win32 handle
|
||||||
|
func (f *win32File) closeHandle() {
|
||||||
|
f.wgLock.Lock()
|
||||||
|
// Atomically set that we are closing, releasing the resources only once.
|
||||||
|
if !f.closing.swap(true) {
|
||||||
|
f.wgLock.Unlock()
|
||||||
|
// cancel all IO and wait for it to complete
|
||||||
|
cancelIoEx(f.handle, nil)
|
||||||
|
f.wg.Wait()
|
||||||
|
// at this point, no new IO can start
|
||||||
|
syscall.Close(f.handle)
|
||||||
|
f.handle = 0
|
||||||
|
} else {
|
||||||
|
f.wgLock.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes a win32File.
|
||||||
|
func (f *win32File) Close() error {
|
||||||
|
f.closeHandle()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareIo prepares for a new IO operation.
|
||||||
|
// The caller must call f.wg.Done() when the IO is finished, prior to Close() returning.
|
||||||
|
func (f *win32File) prepareIo() (*ioOperation, error) {
|
||||||
|
f.wgLock.RLock()
|
||||||
|
if f.closing.isSet() {
|
||||||
|
f.wgLock.RUnlock()
|
||||||
|
return nil, ErrFileClosed
|
||||||
|
}
|
||||||
|
f.wg.Add(1)
|
||||||
|
f.wgLock.RUnlock()
|
||||||
|
c := &ioOperation{}
|
||||||
|
c.ch = make(chan ioResult)
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ioCompletionProcessor processes completed async IOs forever
|
||||||
|
func ioCompletionProcessor(h syscall.Handle) {
|
||||||
|
for {
|
||||||
|
var bytes uint32
|
||||||
|
var key uintptr
|
||||||
|
var op *ioOperation
|
||||||
|
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
|
||||||
|
if op == nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
op.ch <- ioResult{bytes, err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// asyncIo processes the return value from ReadFile or WriteFile, blocking until
|
||||||
|
// the operation has actually completed.
|
||||||
|
func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
|
||||||
|
if err != syscall.ERROR_IO_PENDING {
|
||||||
|
return int(bytes), err
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.closing.isSet() {
|
||||||
|
cancelIoEx(f.handle, &c.o)
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeout timeoutChan
|
||||||
|
if d != nil {
|
||||||
|
d.channelLock.Lock()
|
||||||
|
timeout = d.channel
|
||||||
|
d.channelLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
var r ioResult
|
||||||
|
select {
|
||||||
|
case r = <-c.ch:
|
||||||
|
err = r.err
|
||||||
|
if err == syscall.ERROR_OPERATION_ABORTED {
|
||||||
|
if f.closing.isSet() {
|
||||||
|
err = ErrFileClosed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case <-timeout:
|
||||||
|
cancelIoEx(f.handle, &c.o)
|
||||||
|
r = <-c.ch
|
||||||
|
err = r.err
|
||||||
|
if err == syscall.ERROR_OPERATION_ABORTED {
|
||||||
|
err = ErrTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// runtime.KeepAlive is needed, as c is passed via native
|
||||||
|
// code to ioCompletionProcessor, c must remain alive
|
||||||
|
// until the channel read is complete.
|
||||||
|
runtime.KeepAlive(c)
|
||||||
|
return int(r.bytes), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from a file handle.
|
||||||
|
func (f *win32File) Read(b []byte) (int, error) {
|
||||||
|
c, err := f.prepareIo()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer f.wg.Done()
|
||||||
|
|
||||||
|
if f.readDeadline.timedout.isSet() {
|
||||||
|
return 0, ErrTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes uint32
|
||||||
|
err = syscall.ReadFile(f.handle, b, &bytes, &c.o)
|
||||||
|
n, err := f.asyncIo(c, &f.readDeadline, bytes, err)
|
||||||
|
runtime.KeepAlive(b)
|
||||||
|
|
||||||
|
// Handle EOF conditions.
|
||||||
|
if err == nil && n == 0 && len(b) != 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
} else if err == syscall.ERROR_BROKEN_PIPE {
|
||||||
|
return 0, io.EOF
|
||||||
|
} else {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes to a file handle.
|
||||||
|
func (f *win32File) Write(b []byte) (int, error) {
|
||||||
|
c, err := f.prepareIo()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer f.wg.Done()
|
||||||
|
|
||||||
|
if f.writeDeadline.timedout.isSet() {
|
||||||
|
return 0, ErrTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes uint32
|
||||||
|
err = syscall.WriteFile(f.handle, b, &bytes, &c.o)
|
||||||
|
n, err := f.asyncIo(c, &f.writeDeadline, bytes, err)
|
||||||
|
runtime.KeepAlive(b)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32File) SetReadDeadline(deadline time.Time) error {
|
||||||
|
return f.readDeadline.set(deadline)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32File) SetWriteDeadline(deadline time.Time) error {
|
||||||
|
return f.writeDeadline.set(deadline)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32File) Flush() error {
|
||||||
|
return syscall.FlushFileBuffers(f.handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *deadlineHandler) set(deadline time.Time) error {
|
||||||
|
d.setLock.Lock()
|
||||||
|
defer d.setLock.Unlock()
|
||||||
|
|
||||||
|
if d.timer != nil {
|
||||||
|
if !d.timer.Stop() {
|
||||||
|
<-d.channel
|
||||||
|
}
|
||||||
|
d.timer = nil
|
||||||
|
}
|
||||||
|
d.timedout.setFalse()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-d.channel:
|
||||||
|
d.channelLock.Lock()
|
||||||
|
d.channel = make(chan struct{})
|
||||||
|
d.channelLock.Unlock()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
if deadline.IsZero() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutIO := func() {
|
||||||
|
d.timedout.setTrue()
|
||||||
|
close(d.channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
duration := deadline.Sub(now)
|
||||||
|
if deadline.After(now) {
|
||||||
|
// Deadline is in the future, set a timer to wait
|
||||||
|
d.timer = time.AfterFunc(duration, timeoutIO)
|
||||||
|
} else {
|
||||||
|
// Deadline is in the past. Cancel all pending IO now.
|
||||||
|
timeoutIO()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
61
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
Normal file
61
vendor/github.com/Microsoft/go-winio/fileinfo.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = GetFileInformationByHandleEx
|
||||||
|
//sys setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) = SetFileInformationByHandle
|
||||||
|
|
||||||
|
const (
|
||||||
|
fileBasicInfo = 0
|
||||||
|
fileIDInfo = 0x12
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileBasicInfo contains file access time and file attributes information.
|
||||||
|
type FileBasicInfo struct {
|
||||||
|
CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime
|
||||||
|
FileAttributes uint32
|
||||||
|
pad uint32 // padding
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileBasicInfo retrieves times and attributes for a file.
|
||||||
|
func GetFileBasicInfo(f *os.File) (*FileBasicInfo, error) {
|
||||||
|
bi := &FileBasicInfo{}
|
||||||
|
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
||||||
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(f)
|
||||||
|
return bi, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFileBasicInfo sets times and attributes for a file.
|
||||||
|
func SetFileBasicInfo(f *os.File, bi *FileBasicInfo) error {
|
||||||
|
if err := setFileInformationByHandle(syscall.Handle(f.Fd()), fileBasicInfo, (*byte)(unsafe.Pointer(bi)), uint32(unsafe.Sizeof(*bi))); err != nil {
|
||||||
|
return &os.PathError{Op: "SetFileInformationByHandle", Path: f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(f)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileIDInfo contains the volume serial number and file ID for a file. This pair should be
|
||||||
|
// unique on a system.
|
||||||
|
type FileIDInfo struct {
|
||||||
|
VolumeSerialNumber uint64
|
||||||
|
FileID [16]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFileID retrieves the unique (volume, file ID) pair for a file.
|
||||||
|
func GetFileID(f *os.File) (*FileIDInfo, error) {
|
||||||
|
fileID := &FileIDInfo{}
|
||||||
|
if err := getFileInformationByHandleEx(syscall.Handle(f.Fd()), fileIDInfo, (*byte)(unsafe.Pointer(fileID)), uint32(unsafe.Sizeof(*fileID))); err != nil {
|
||||||
|
return nil, &os.PathError{Op: "GetFileInformationByHandleEx", Path: f.Name(), Err: err}
|
||||||
|
}
|
||||||
|
runtime.KeepAlive(f)
|
||||||
|
return fileID, nil
|
||||||
|
}
|
||||||
421
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
Normal file
421
vendor/github.com/Microsoft/go-winio/pipe.go
generated
vendored
Normal file
@@ -0,0 +1,421 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
|
||||||
|
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
|
||||||
|
//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
|
||||||
|
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
|
||||||
|
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
|
||||||
|
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
|
||||||
|
|
||||||
|
const (
|
||||||
|
cERROR_PIPE_BUSY = syscall.Errno(231)
|
||||||
|
cERROR_NO_DATA = syscall.Errno(232)
|
||||||
|
cERROR_PIPE_CONNECTED = syscall.Errno(535)
|
||||||
|
cERROR_SEM_TIMEOUT = syscall.Errno(121)
|
||||||
|
|
||||||
|
cPIPE_ACCESS_DUPLEX = 0x3
|
||||||
|
cFILE_FLAG_FIRST_PIPE_INSTANCE = 0x80000
|
||||||
|
cSECURITY_SQOS_PRESENT = 0x100000
|
||||||
|
cSECURITY_ANONYMOUS = 0
|
||||||
|
|
||||||
|
cPIPE_REJECT_REMOTE_CLIENTS = 0x8
|
||||||
|
|
||||||
|
cPIPE_UNLIMITED_INSTANCES = 255
|
||||||
|
|
||||||
|
cNMPWAIT_USE_DEFAULT_WAIT = 0
|
||||||
|
cNMPWAIT_NOWAIT = 1
|
||||||
|
|
||||||
|
cPIPE_TYPE_MESSAGE = 4
|
||||||
|
|
||||||
|
cPIPE_READMODE_MESSAGE = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrPipeListenerClosed is returned for pipe operations on listeners that have been closed.
|
||||||
|
// This error should match net.errClosing since docker takes a dependency on its text.
|
||||||
|
ErrPipeListenerClosed = errors.New("use of closed network connection")
|
||||||
|
|
||||||
|
errPipeWriteClosed = errors.New("pipe has been closed for write")
|
||||||
|
)
|
||||||
|
|
||||||
|
type win32Pipe struct {
|
||||||
|
*win32File
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
type win32MessageBytePipe struct {
|
||||||
|
win32Pipe
|
||||||
|
writeClosed bool
|
||||||
|
readEOF bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type pipeAddress string
|
||||||
|
|
||||||
|
func (f *win32Pipe) LocalAddr() net.Addr {
|
||||||
|
return pipeAddress(f.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32Pipe) RemoteAddr() net.Addr {
|
||||||
|
return pipeAddress(f.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *win32Pipe) SetDeadline(t time.Time) error {
|
||||||
|
f.SetReadDeadline(t)
|
||||||
|
f.SetWriteDeadline(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseWrite closes the write side of a message pipe in byte mode.
|
||||||
|
func (f *win32MessageBytePipe) CloseWrite() error {
|
||||||
|
if f.writeClosed {
|
||||||
|
return errPipeWriteClosed
|
||||||
|
}
|
||||||
|
err := f.win32File.Flush()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = f.win32File.Write(nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.writeClosed = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes bytes to a message pipe in byte mode. Zero-byte writes are ignored, since
|
||||||
|
// they are used to implement CloseWrite().
|
||||||
|
func (f *win32MessageBytePipe) Write(b []byte) (int, error) {
|
||||||
|
if f.writeClosed {
|
||||||
|
return 0, errPipeWriteClosed
|
||||||
|
}
|
||||||
|
if len(b) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return f.win32File.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads bytes from a message pipe in byte mode. A read of a zero-byte message on a message
|
||||||
|
// mode pipe will return io.EOF, as will all subsequent reads.
|
||||||
|
func (f *win32MessageBytePipe) Read(b []byte) (int, error) {
|
||||||
|
if f.readEOF {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
n, err := f.win32File.Read(b)
|
||||||
|
if err == io.EOF {
|
||||||
|
// If this was the result of a zero-byte read, then
|
||||||
|
// it is possible that the read was due to a zero-size
|
||||||
|
// message. Since we are simulating CloseWrite with a
|
||||||
|
// zero-byte message, ensure that all future Read() calls
|
||||||
|
// also return EOF.
|
||||||
|
f.readEOF = true
|
||||||
|
} else if err == syscall.ERROR_MORE_DATA {
|
||||||
|
// ERROR_MORE_DATA indicates that the pipe's read mode is message mode
|
||||||
|
// and the message still has more bytes. Treat this as a success, since
|
||||||
|
// this package presents all named pipes as byte streams.
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s pipeAddress) Network() string {
|
||||||
|
return "pipe"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s pipeAddress) String() string {
|
||||||
|
return string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialPipe connects to a named pipe by path, timing out if the connection
|
||||||
|
// takes longer than the specified duration. If timeout is nil, then we use
|
||||||
|
// a default timeout of 5 seconds. (We do not use WaitNamedPipe.)
|
||||||
|
func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
|
||||||
|
var absTimeout time.Time
|
||||||
|
if timeout != nil {
|
||||||
|
absTimeout = time.Now().Add(*timeout)
|
||||||
|
} else {
|
||||||
|
absTimeout = time.Now().Add(time.Second * 2)
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
var h syscall.Handle
|
||||||
|
for {
|
||||||
|
h, err = createFile(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
|
||||||
|
if err != cERROR_PIPE_BUSY {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if time.Now().After(absTimeout) {
|
||||||
|
return nil, ErrTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait 10 msec and try again. This is a rather simplistic
|
||||||
|
// view, as we always try each 10 milliseconds.
|
||||||
|
time.Sleep(time.Millisecond * 10)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
var flags uint32
|
||||||
|
err = getNamedPipeInfo(h, &flags, nil, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := makeWin32File(h)
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(h)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the pipe is in message mode, return a message byte pipe, which
|
||||||
|
// supports CloseWrite().
|
||||||
|
if flags&cPIPE_TYPE_MESSAGE != 0 {
|
||||||
|
return &win32MessageBytePipe{
|
||||||
|
win32Pipe: win32Pipe{win32File: f, path: path},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return &win32Pipe{win32File: f, path: path}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type acceptResponse struct {
|
||||||
|
f *win32File
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type win32PipeListener struct {
|
||||||
|
firstHandle syscall.Handle
|
||||||
|
path string
|
||||||
|
securityDescriptor []byte
|
||||||
|
config PipeConfig
|
||||||
|
acceptCh chan (chan acceptResponse)
|
||||||
|
closeCh chan int
|
||||||
|
doneCh chan int
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeServerPipeHandle(path string, securityDescriptor []byte, c *PipeConfig, first bool) (syscall.Handle, error) {
|
||||||
|
var flags uint32 = cPIPE_ACCESS_DUPLEX | syscall.FILE_FLAG_OVERLAPPED
|
||||||
|
if first {
|
||||||
|
flags |= cFILE_FLAG_FIRST_PIPE_INSTANCE
|
||||||
|
}
|
||||||
|
|
||||||
|
var mode uint32 = cPIPE_REJECT_REMOTE_CLIENTS
|
||||||
|
if c.MessageMode {
|
||||||
|
mode |= cPIPE_TYPE_MESSAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
sa := &syscall.SecurityAttributes{}
|
||||||
|
sa.Length = uint32(unsafe.Sizeof(*sa))
|
||||||
|
if securityDescriptor != nil {
|
||||||
|
len := uint32(len(securityDescriptor))
|
||||||
|
sa.SecurityDescriptor = localAlloc(0, len)
|
||||||
|
defer localFree(sa.SecurityDescriptor)
|
||||||
|
copy((*[0xffff]byte)(unsafe.Pointer(sa.SecurityDescriptor))[:], securityDescriptor)
|
||||||
|
}
|
||||||
|
h, err := createNamedPipe(path, flags, mode, cPIPE_UNLIMITED_INSTANCES, uint32(c.OutputBufferSize), uint32(c.InputBufferSize), 0, sa)
|
||||||
|
if err != nil {
|
||||||
|
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
||||||
|
}
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
|
||||||
|
h, err := makeServerPipeHandle(l.path, l.securityDescriptor, &l.config, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f, err := makeWin32File(h)
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(h)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) {
|
||||||
|
p, err := l.makeServerPipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the client to connect.
|
||||||
|
ch := make(chan error)
|
||||||
|
go func(p *win32File) {
|
||||||
|
ch <- connectPipe(p)
|
||||||
|
}(p)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err = <-ch:
|
||||||
|
if err != nil {
|
||||||
|
p.Close()
|
||||||
|
p = nil
|
||||||
|
}
|
||||||
|
case <-l.closeCh:
|
||||||
|
// Abort the connect request by closing the handle.
|
||||||
|
p.Close()
|
||||||
|
p = nil
|
||||||
|
err = <-ch
|
||||||
|
if err == nil || err == ErrFileClosed {
|
||||||
|
err = ErrPipeListenerClosed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) listenerRoutine() {
|
||||||
|
closed := false
|
||||||
|
for !closed {
|
||||||
|
select {
|
||||||
|
case <-l.closeCh:
|
||||||
|
closed = true
|
||||||
|
case responseCh := <-l.acceptCh:
|
||||||
|
var (
|
||||||
|
p *win32File
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
p, err = l.makeConnectedServerPipe()
|
||||||
|
// If the connection was immediately closed by the client, try
|
||||||
|
// again.
|
||||||
|
if err != cERROR_NO_DATA {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
responseCh <- acceptResponse{p, err}
|
||||||
|
closed = err == ErrPipeListenerClosed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syscall.Close(l.firstHandle)
|
||||||
|
l.firstHandle = 0
|
||||||
|
// Notify Close() and Accept() callers that the handle has been closed.
|
||||||
|
close(l.doneCh)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PipeConfig contain configuration for the pipe listener.
|
||||||
|
type PipeConfig struct {
|
||||||
|
// SecurityDescriptor contains a Windows security descriptor in SDDL format.
|
||||||
|
SecurityDescriptor string
|
||||||
|
|
||||||
|
// MessageMode determines whether the pipe is in byte or message mode. In either
|
||||||
|
// case the pipe is read in byte mode by default. The only practical difference in
|
||||||
|
// this implementation is that CloseWrite() is only supported for message mode pipes;
|
||||||
|
// CloseWrite() is implemented as a zero-byte write, but zero-byte writes are only
|
||||||
|
// transferred to the reader (and returned as io.EOF in this implementation)
|
||||||
|
// when the pipe is in message mode.
|
||||||
|
MessageMode bool
|
||||||
|
|
||||||
|
// InputBufferSize specifies the size the input buffer, in bytes.
|
||||||
|
InputBufferSize int32
|
||||||
|
|
||||||
|
// OutputBufferSize specifies the size the input buffer, in bytes.
|
||||||
|
OutputBufferSize int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenPipe creates a listener on a Windows named pipe path, e.g. \\.\pipe\mypipe.
|
||||||
|
// The pipe must not already exist.
|
||||||
|
func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {
|
||||||
|
var (
|
||||||
|
sd []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if c == nil {
|
||||||
|
c = &PipeConfig{}
|
||||||
|
}
|
||||||
|
if c.SecurityDescriptor != "" {
|
||||||
|
sd, err = SddlToSecurityDescriptor(c.SecurityDescriptor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h, err := makeServerPipeHandle(path, sd, c, true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Create a client handle and connect it. This results in the pipe
|
||||||
|
// instance always existing, so that clients see ERROR_PIPE_BUSY
|
||||||
|
// rather than ERROR_FILE_NOT_FOUND. This ties the first instance
|
||||||
|
// up so that no other instances can be used. This would have been
|
||||||
|
// cleaner if the Win32 API matched CreateFile with ConnectNamedPipe
|
||||||
|
// instead of CreateNamedPipe. (Apparently created named pipes are
|
||||||
|
// considered to be in listening state regardless of whether any
|
||||||
|
// active calls to ConnectNamedPipe are outstanding.)
|
||||||
|
h2, err := createFile(path, 0, 0, nil, syscall.OPEN_EXISTING, cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(h)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Close the client handle. The server side of the instance will
|
||||||
|
// still be busy, leading to ERROR_PIPE_BUSY instead of
|
||||||
|
// ERROR_NOT_FOUND, as long as we don't close the server handle,
|
||||||
|
// or disconnect the client with DisconnectNamedPipe.
|
||||||
|
syscall.Close(h2)
|
||||||
|
l := &win32PipeListener{
|
||||||
|
firstHandle: h,
|
||||||
|
path: path,
|
||||||
|
securityDescriptor: sd,
|
||||||
|
config: *c,
|
||||||
|
acceptCh: make(chan (chan acceptResponse)),
|
||||||
|
closeCh: make(chan int),
|
||||||
|
doneCh: make(chan int),
|
||||||
|
}
|
||||||
|
go l.listenerRoutine()
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectPipe(p *win32File) error {
|
||||||
|
c, err := p.prepareIo()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer p.wg.Done()
|
||||||
|
|
||||||
|
err = connectNamedPipe(p.handle, &c.o)
|
||||||
|
_, err = p.asyncIo(c, nil, 0, err)
|
||||||
|
if err != nil && err != cERROR_PIPE_CONNECTED {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) Accept() (net.Conn, error) {
|
||||||
|
ch := make(chan acceptResponse)
|
||||||
|
select {
|
||||||
|
case l.acceptCh <- ch:
|
||||||
|
response := <-ch
|
||||||
|
err := response.err
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if l.config.MessageMode {
|
||||||
|
return &win32MessageBytePipe{
|
||||||
|
win32Pipe: win32Pipe{win32File: response.f, path: l.path},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return &win32Pipe{win32File: response.f, path: l.path}, nil
|
||||||
|
case <-l.doneCh:
|
||||||
|
return nil, ErrPipeListenerClosed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) Close() error {
|
||||||
|
select {
|
||||||
|
case l.closeCh <- 1:
|
||||||
|
<-l.doneCh
|
||||||
|
case <-l.doneCh:
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) Addr() net.Addr {
|
||||||
|
return pipeAddress(l.path)
|
||||||
|
}
|
||||||
202
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
Normal file
202
vendor/github.com/Microsoft/go-winio/privilege.go
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) [true] = advapi32.AdjustTokenPrivileges
|
||||||
|
//sys impersonateSelf(level uint32) (err error) = advapi32.ImpersonateSelf
|
||||||
|
//sys revertToSelf() (err error) = advapi32.RevertToSelf
|
||||||
|
//sys openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) = advapi32.OpenThreadToken
|
||||||
|
//sys getCurrentThread() (h syscall.Handle) = GetCurrentThread
|
||||||
|
//sys lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) = advapi32.LookupPrivilegeValueW
|
||||||
|
//sys lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) = advapi32.LookupPrivilegeNameW
|
||||||
|
//sys lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) = advapi32.LookupPrivilegeDisplayNameW
|
||||||
|
|
||||||
|
const (
|
||||||
|
SE_PRIVILEGE_ENABLED = 2
|
||||||
|
|
||||||
|
ERROR_NOT_ALL_ASSIGNED syscall.Errno = 1300
|
||||||
|
|
||||||
|
SeBackupPrivilege = "SeBackupPrivilege"
|
||||||
|
SeRestorePrivilege = "SeRestorePrivilege"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
securityAnonymous = iota
|
||||||
|
securityIdentification
|
||||||
|
securityImpersonation
|
||||||
|
securityDelegation
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
privNames = make(map[string]uint64)
|
||||||
|
privNameMutex sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrivilegeError represents an error enabling privileges.
|
||||||
|
type PrivilegeError struct {
|
||||||
|
privileges []uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *PrivilegeError) Error() string {
|
||||||
|
s := ""
|
||||||
|
if len(e.privileges) > 1 {
|
||||||
|
s = "Could not enable privileges "
|
||||||
|
} else {
|
||||||
|
s = "Could not enable privilege "
|
||||||
|
}
|
||||||
|
for i, p := range e.privileges {
|
||||||
|
if i != 0 {
|
||||||
|
s += ", "
|
||||||
|
}
|
||||||
|
s += `"`
|
||||||
|
s += getPrivilegeName(p)
|
||||||
|
s += `"`
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunWithPrivilege enables a single privilege for a function call.
|
||||||
|
func RunWithPrivilege(name string, fn func() error) error {
|
||||||
|
return RunWithPrivileges([]string{name}, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunWithPrivileges enables privileges for a function call.
|
||||||
|
func RunWithPrivileges(names []string, fn func() error) error {
|
||||||
|
privileges, err := mapPrivileges(names)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
token, err := newThreadToken()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer releaseThreadToken(token)
|
||||||
|
err = adjustPrivileges(token, privileges, SE_PRIVILEGE_ENABLED)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapPrivileges(names []string) ([]uint64, error) {
|
||||||
|
var privileges []uint64
|
||||||
|
privNameMutex.Lock()
|
||||||
|
defer privNameMutex.Unlock()
|
||||||
|
for _, name := range names {
|
||||||
|
p, ok := privNames[name]
|
||||||
|
if !ok {
|
||||||
|
err := lookupPrivilegeValue("", name, &p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
privNames[name] = p
|
||||||
|
}
|
||||||
|
privileges = append(privileges, p)
|
||||||
|
}
|
||||||
|
return privileges, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableProcessPrivileges enables privileges globally for the process.
|
||||||
|
func EnableProcessPrivileges(names []string) error {
|
||||||
|
return enableDisableProcessPrivilege(names, SE_PRIVILEGE_ENABLED)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableProcessPrivileges disables privileges globally for the process.
|
||||||
|
func DisableProcessPrivileges(names []string) error {
|
||||||
|
return enableDisableProcessPrivilege(names, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func enableDisableProcessPrivilege(names []string, action uint32) error {
|
||||||
|
privileges, err := mapPrivileges(names)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p, _ := windows.GetCurrentProcess()
|
||||||
|
var token windows.Token
|
||||||
|
err = windows.OpenProcessToken(p, windows.TOKEN_ADJUST_PRIVILEGES|windows.TOKEN_QUERY, &token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer token.Close()
|
||||||
|
return adjustPrivileges(token, privileges, action)
|
||||||
|
}
|
||||||
|
|
||||||
|
func adjustPrivileges(token windows.Token, privileges []uint64, action uint32) error {
|
||||||
|
var b bytes.Buffer
|
||||||
|
binary.Write(&b, binary.LittleEndian, uint32(len(privileges)))
|
||||||
|
for _, p := range privileges {
|
||||||
|
binary.Write(&b, binary.LittleEndian, p)
|
||||||
|
binary.Write(&b, binary.LittleEndian, action)
|
||||||
|
}
|
||||||
|
prevState := make([]byte, b.Len())
|
||||||
|
reqSize := uint32(0)
|
||||||
|
success, err := adjustTokenPrivileges(token, false, &b.Bytes()[0], uint32(len(prevState)), &prevState[0], &reqSize)
|
||||||
|
if !success {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err == ERROR_NOT_ALL_ASSIGNED {
|
||||||
|
return &PrivilegeError{privileges}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPrivilegeName(luid uint64) string {
|
||||||
|
var nameBuffer [256]uint16
|
||||||
|
bufSize := uint32(len(nameBuffer))
|
||||||
|
err := lookupPrivilegeName("", &luid, &nameBuffer[0], &bufSize)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("<unknown privilege %d>", luid)
|
||||||
|
}
|
||||||
|
|
||||||
|
var displayNameBuffer [256]uint16
|
||||||
|
displayBufSize := uint32(len(displayNameBuffer))
|
||||||
|
var langID uint32
|
||||||
|
err = lookupPrivilegeDisplayName("", &nameBuffer[0], &displayNameBuffer[0], &displayBufSize, &langID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("<unknown privilege %s>", string(utf16.Decode(nameBuffer[:bufSize])))
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(utf16.Decode(displayNameBuffer[:displayBufSize]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func newThreadToken() (windows.Token, error) {
|
||||||
|
err := impersonateSelf(securityImpersonation)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var token windows.Token
|
||||||
|
err = openThreadToken(getCurrentThread(), syscall.TOKEN_ADJUST_PRIVILEGES|syscall.TOKEN_QUERY, false, &token)
|
||||||
|
if err != nil {
|
||||||
|
rerr := revertToSelf()
|
||||||
|
if rerr != nil {
|
||||||
|
panic(rerr)
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func releaseThreadToken(h windows.Token) {
|
||||||
|
err := revertToSelf()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
h.Close()
|
||||||
|
}
|
||||||
128
vendor/github.com/Microsoft/go-winio/reparse.go
generated
vendored
Normal file
128
vendor/github.com/Microsoft/go-winio/reparse.go
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
reparseTagMountPoint = 0xA0000003
|
||||||
|
reparseTagSymlink = 0xA000000C
|
||||||
|
)
|
||||||
|
|
||||||
|
type reparseDataBuffer struct {
|
||||||
|
ReparseTag uint32
|
||||||
|
ReparseDataLength uint16
|
||||||
|
Reserved uint16
|
||||||
|
SubstituteNameOffset uint16
|
||||||
|
SubstituteNameLength uint16
|
||||||
|
PrintNameOffset uint16
|
||||||
|
PrintNameLength uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReparsePoint describes a Win32 symlink or mount point.
|
||||||
|
type ReparsePoint struct {
|
||||||
|
Target string
|
||||||
|
IsMountPoint bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsupportedReparsePointError is returned when trying to decode a non-symlink or
|
||||||
|
// mount point reparse point.
|
||||||
|
type UnsupportedReparsePointError struct {
|
||||||
|
Tag uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnsupportedReparsePointError) Error() string {
|
||||||
|
return fmt.Sprintf("unsupported reparse point %x", e.Tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeReparsePoint decodes a Win32 REPARSE_DATA_BUFFER structure containing either a symlink
|
||||||
|
// or a mount point.
|
||||||
|
func DecodeReparsePoint(b []byte) (*ReparsePoint, error) {
|
||||||
|
tag := binary.LittleEndian.Uint32(b[0:4])
|
||||||
|
return DecodeReparsePointData(tag, b[8:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecodeReparsePointData(tag uint32, b []byte) (*ReparsePoint, error) {
|
||||||
|
isMountPoint := false
|
||||||
|
switch tag {
|
||||||
|
case reparseTagMountPoint:
|
||||||
|
isMountPoint = true
|
||||||
|
case reparseTagSymlink:
|
||||||
|
default:
|
||||||
|
return nil, &UnsupportedReparsePointError{tag}
|
||||||
|
}
|
||||||
|
nameOffset := 8 + binary.LittleEndian.Uint16(b[4:6])
|
||||||
|
if !isMountPoint {
|
||||||
|
nameOffset += 4
|
||||||
|
}
|
||||||
|
nameLength := binary.LittleEndian.Uint16(b[6:8])
|
||||||
|
name := make([]uint16, nameLength/2)
|
||||||
|
err := binary.Read(bytes.NewReader(b[nameOffset:nameOffset+nameLength]), binary.LittleEndian, &name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ReparsePoint{string(utf16.Decode(name)), isMountPoint}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDriveLetter(c byte) bool {
|
||||||
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeReparsePoint encodes a Win32 REPARSE_DATA_BUFFER structure describing a symlink or
|
||||||
|
// mount point.
|
||||||
|
func EncodeReparsePoint(rp *ReparsePoint) []byte {
|
||||||
|
// Generate an NT path and determine if this is a relative path.
|
||||||
|
var ntTarget string
|
||||||
|
relative := false
|
||||||
|
if strings.HasPrefix(rp.Target, `\\?\`) {
|
||||||
|
ntTarget = `\??\` + rp.Target[4:]
|
||||||
|
} else if strings.HasPrefix(rp.Target, `\\`) {
|
||||||
|
ntTarget = `\??\UNC\` + rp.Target[2:]
|
||||||
|
} else if len(rp.Target) >= 2 && isDriveLetter(rp.Target[0]) && rp.Target[1] == ':' {
|
||||||
|
ntTarget = `\??\` + rp.Target
|
||||||
|
} else {
|
||||||
|
ntTarget = rp.Target
|
||||||
|
relative = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// The paths must be NUL-terminated even though they are counted strings.
|
||||||
|
target16 := utf16.Encode([]rune(rp.Target + "\x00"))
|
||||||
|
ntTarget16 := utf16.Encode([]rune(ntTarget + "\x00"))
|
||||||
|
|
||||||
|
size := int(unsafe.Sizeof(reparseDataBuffer{})) - 8
|
||||||
|
size += len(ntTarget16)*2 + len(target16)*2
|
||||||
|
|
||||||
|
tag := uint32(reparseTagMountPoint)
|
||||||
|
if !rp.IsMountPoint {
|
||||||
|
tag = reparseTagSymlink
|
||||||
|
size += 4 // Add room for symlink flags
|
||||||
|
}
|
||||||
|
|
||||||
|
data := reparseDataBuffer{
|
||||||
|
ReparseTag: tag,
|
||||||
|
ReparseDataLength: uint16(size),
|
||||||
|
SubstituteNameOffset: 0,
|
||||||
|
SubstituteNameLength: uint16((len(ntTarget16) - 1) * 2),
|
||||||
|
PrintNameOffset: uint16(len(ntTarget16) * 2),
|
||||||
|
PrintNameLength: uint16((len(target16) - 1) * 2),
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
binary.Write(&b, binary.LittleEndian, &data)
|
||||||
|
if !rp.IsMountPoint {
|
||||||
|
flags := uint32(0)
|
||||||
|
if relative {
|
||||||
|
flags |= 1
|
||||||
|
}
|
||||||
|
binary.Write(&b, binary.LittleEndian, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
binary.Write(&b, binary.LittleEndian, ntTarget16)
|
||||||
|
binary.Write(&b, binary.LittleEndian, target16)
|
||||||
|
return b.Bytes()
|
||||||
|
}
|
||||||
98
vendor/github.com/Microsoft/go-winio/sd.go
generated
vendored
Normal file
98
vendor/github.com/Microsoft/go-winio/sd.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//sys lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) = advapi32.LookupAccountNameW
|
||||||
|
//sys convertSidToStringSid(sid *byte, str **uint16) (err error) = advapi32.ConvertSidToStringSidW
|
||||||
|
//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
|
||||||
|
//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
|
||||||
|
//sys localFree(mem uintptr) = LocalFree
|
||||||
|
//sys getSecurityDescriptorLength(sd uintptr) (len uint32) = advapi32.GetSecurityDescriptorLength
|
||||||
|
|
||||||
|
const (
|
||||||
|
cERROR_NONE_MAPPED = syscall.Errno(1332)
|
||||||
|
)
|
||||||
|
|
||||||
|
type AccountLookupError struct {
|
||||||
|
Name string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *AccountLookupError) Error() string {
|
||||||
|
if e.Name == "" {
|
||||||
|
return "lookup account: empty account name specified"
|
||||||
|
}
|
||||||
|
var s string
|
||||||
|
switch e.Err {
|
||||||
|
case cERROR_NONE_MAPPED:
|
||||||
|
s = "not found"
|
||||||
|
default:
|
||||||
|
s = e.Err.Error()
|
||||||
|
}
|
||||||
|
return "lookup account " + e.Name + ": " + s
|
||||||
|
}
|
||||||
|
|
||||||
|
type SddlConversionError struct {
|
||||||
|
Sddl string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SddlConversionError) Error() string {
|
||||||
|
return "convert " + e.Sddl + ": " + e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupSidByName looks up the SID of an account by name
|
||||||
|
func LookupSidByName(name string) (sid string, err error) {
|
||||||
|
if name == "" {
|
||||||
|
return "", &AccountLookupError{name, cERROR_NONE_MAPPED}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sidSize, sidNameUse, refDomainSize uint32
|
||||||
|
err = lookupAccountName(nil, name, nil, &sidSize, nil, &refDomainSize, &sidNameUse)
|
||||||
|
if err != nil && err != syscall.ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return "", &AccountLookupError{name, err}
|
||||||
|
}
|
||||||
|
sidBuffer := make([]byte, sidSize)
|
||||||
|
refDomainBuffer := make([]uint16, refDomainSize)
|
||||||
|
err = lookupAccountName(nil, name, &sidBuffer[0], &sidSize, &refDomainBuffer[0], &refDomainSize, &sidNameUse)
|
||||||
|
if err != nil {
|
||||||
|
return "", &AccountLookupError{name, err}
|
||||||
|
}
|
||||||
|
var strBuffer *uint16
|
||||||
|
err = convertSidToStringSid(&sidBuffer[0], &strBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return "", &AccountLookupError{name, err}
|
||||||
|
}
|
||||||
|
sid = syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(strBuffer))[:])
|
||||||
|
localFree(uintptr(unsafe.Pointer(strBuffer)))
|
||||||
|
return sid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SddlToSecurityDescriptor(sddl string) ([]byte, error) {
|
||||||
|
var sdBuffer uintptr
|
||||||
|
err := convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &sdBuffer, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &SddlConversionError{sddl, err}
|
||||||
|
}
|
||||||
|
defer localFree(sdBuffer)
|
||||||
|
sd := make([]byte, getSecurityDescriptorLength(sdBuffer))
|
||||||
|
copy(sd, (*[0xffff]byte)(unsafe.Pointer(sdBuffer))[:len(sd)])
|
||||||
|
return sd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SecurityDescriptorToSddl(sd []byte) (string, error) {
|
||||||
|
var sddl *uint16
|
||||||
|
// The returned string length seems to including an aribtrary number of terminating NULs.
|
||||||
|
// Don't use it.
|
||||||
|
err := convertSecurityDescriptorToStringSecurityDescriptor(&sd[0], 1, 0xff, &sddl, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer localFree(uintptr(unsafe.Pointer(sddl)))
|
||||||
|
return syscall.UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(sddl))[:]), nil
|
||||||
|
}
|
||||||
3
vendor/github.com/Microsoft/go-winio/syscall.go
generated
vendored
Normal file
3
vendor/github.com/Microsoft/go-winio/syscall.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package winio
|
||||||
|
|
||||||
|
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go file.go pipe.go sd.go fileinfo.go privilege.go backup.go
|
||||||
520
vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
generated
vendored
Normal file
520
vendor/github.com/Microsoft/go-winio/zsyscall_windows.go
generated
vendored
Normal file
@@ -0,0 +1,520 @@
|
|||||||
|
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||||
|
|
||||||
|
package winio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
// TODO: add more here, after collecting data on the common
|
||||||
|
// error values see on Windows. (perhaps when running
|
||||||
|
// all.bat?)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||||
|
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||||
|
|
||||||
|
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||||
|
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
||||||
|
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
||||||
|
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
||||||
|
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
|
||||||
|
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
|
||||||
|
procCreateFileW = modkernel32.NewProc("CreateFileW")
|
||||||
|
procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW")
|
||||||
|
procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo")
|
||||||
|
procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW")
|
||||||
|
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
|
||||||
|
procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW")
|
||||||
|
procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW")
|
||||||
|
procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW")
|
||||||
|
procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW")
|
||||||
|
procLocalFree = modkernel32.NewProc("LocalFree")
|
||||||
|
procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength")
|
||||||
|
procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx")
|
||||||
|
procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle")
|
||||||
|
procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
|
||||||
|
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
|
||||||
|
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
|
||||||
|
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
|
||||||
|
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
|
||||||
|
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
|
||||||
|
procLookupPrivilegeNameW = modadvapi32.NewProc("LookupPrivilegeNameW")
|
||||||
|
procLookupPrivilegeDisplayNameW = modadvapi32.NewProc("LookupPrivilegeDisplayNameW")
|
||||||
|
procBackupRead = modkernel32.NewProc("BackupRead")
|
||||||
|
procBackupWrite = modkernel32.NewProc("BackupWrite")
|
||||||
|
)
|
||||||
|
|
||||||
|
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
|
||||||
|
newport = syscall.Handle(r0)
|
||||||
|
if newport == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _createNamedPipe(_p0, flags, pipeMode, maxInstances, outSize, inSize, defaultTimeout, sa)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _createNamedPipe(name *uint16, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall9(procCreateNamedPipeW.Addr(), 8, uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(pipeMode), uintptr(maxInstances), uintptr(outSize), uintptr(inSize), uintptr(defaultTimeout), uintptr(unsafe.Pointer(sa)), 0)
|
||||||
|
handle = syscall.Handle(r0)
|
||||||
|
if handle == syscall.InvalidHandle {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _createFile(_p0, access, mode, sa, createmode, attrs, templatefile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _createFile(name *uint16, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) {
|
||||||
|
r0, _, e1 := syscall.Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
|
||||||
|
handle = syscall.Handle(r0)
|
||||||
|
if handle == syscall.InvalidHandle {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitNamedPipe(name string, timeout uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _waitNamedPipe(_p0, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _waitNamedPipe(name *uint16, timeout uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procWaitNamedPipeW.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(timeout), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procGetNamedPipeInfo.Addr(), 5, uintptr(pipe), uintptr(unsafe.Pointer(flags)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(inSize)), uintptr(unsafe.Pointer(maxInstances)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall9(procGetNamedPipeHandleStateW.Addr(), 7, uintptr(pipe), uintptr(unsafe.Pointer(state)), uintptr(unsafe.Pointer(curInstances)), uintptr(unsafe.Pointer(maxCollectionCount)), uintptr(unsafe.Pointer(collectDataTimeout)), uintptr(unsafe.Pointer(userName)), uintptr(maxUserNameSize), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func localAlloc(uFlags uint32, length uint32) (ptr uintptr) {
|
||||||
|
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0)
|
||||||
|
ptr = uintptr(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(accountName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _lookupAccountName(systemName, _p0, sid, sidSize, refDomain, refDomainSize, sidNameUse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _lookupAccountName(systemName *uint16, accountName *uint16, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall9(procLookupAccountNameW.Addr(), 7, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(accountName)), uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(sidSize)), uintptr(unsafe.Pointer(refDomain)), uintptr(unsafe.Pointer(refDomainSize)), uintptr(unsafe.Pointer(sidNameUse)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertSidToStringSid(sid *byte, str **uint16) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procConvertSidToStringSidW.Addr(), 2, uintptr(unsafe.Pointer(sid)), uintptr(unsafe.Pointer(str)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd *uintptr, size *uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(str)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd *uintptr, size *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertSecurityDescriptorToStringSecurityDescriptor(sd *byte, revision uint32, secInfo uint32, sddl **uint16, sddlSize *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(secInfo), uintptr(unsafe.Pointer(sddl)), uintptr(unsafe.Pointer(sddlSize)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func localFree(mem uintptr) {
|
||||||
|
syscall.Syscall(procLocalFree.Addr(), 1, uintptr(mem), 0, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSecurityDescriptorLength(sd uintptr) (len uint32) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(sd), 0, 0)
|
||||||
|
len = uint32(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFileInformationByHandleEx(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setFileInformationByHandle(h syscall.Handle, class uint32, buffer *byte, size uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(h), uintptr(class), uintptr(unsafe.Pointer(buffer)), uintptr(size), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func adjustTokenPrivileges(token windows.Token, releaseAll bool, input *byte, outputSize uint32, output *byte, requiredSize *uint32) (success bool, err error) {
|
||||||
|
var _p0 uint32
|
||||||
|
if releaseAll {
|
||||||
|
_p0 = 1
|
||||||
|
} else {
|
||||||
|
_p0 = 0
|
||||||
|
}
|
||||||
|
r0, _, e1 := syscall.Syscall6(procAdjustTokenPrivileges.Addr(), 6, uintptr(token), uintptr(_p0), uintptr(unsafe.Pointer(input)), uintptr(outputSize), uintptr(unsafe.Pointer(output)), uintptr(unsafe.Pointer(requiredSize)))
|
||||||
|
success = r0 != 0
|
||||||
|
if true {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func impersonateSelf(level uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(level), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func revertToSelf() (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procRevertToSelf.Addr(), 0, 0, 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func openThreadToken(thread syscall.Handle, accessMask uint32, openAsSelf bool, token *windows.Token) (err error) {
|
||||||
|
var _p0 uint32
|
||||||
|
if openAsSelf {
|
||||||
|
_p0 = 1
|
||||||
|
} else {
|
||||||
|
_p0 = 0
|
||||||
|
}
|
||||||
|
r1, _, e1 := syscall.Syscall6(procOpenThreadToken.Addr(), 4, uintptr(thread), uintptr(accessMask), uintptr(_p0), uintptr(unsafe.Pointer(token)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCurrentThread() (h syscall.Handle) {
|
||||||
|
r0, _, _ := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0)
|
||||||
|
h = syscall.Handle(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupPrivilegeValue(systemName string, name string, luid *uint64) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *uint16
|
||||||
|
_p1, err = syscall.UTF16PtrFromString(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _lookupPrivilegeValue(_p0, _p1, luid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _lookupPrivilegeValue(systemName *uint16, name *uint16, luid *uint64) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall(procLookupPrivilegeValueW.Addr(), 3, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(luid)))
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupPrivilegeName(systemName string, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _lookupPrivilegeName(_p0, luid, buffer, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _lookupPrivilegeName(systemName *uint16, luid *uint64, buffer *uint16, size *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeNameW.Addr(), 4, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(luid)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupPrivilegeDisplayName(systemName string, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, err = syscall.UTF16PtrFromString(systemName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _lookupPrivilegeDisplayName(_p0, name, buffer, size, languageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _lookupPrivilegeDisplayName(systemName *uint16, name *uint16, buffer *uint16, size *uint32, languageId *uint32) (err error) {
|
||||||
|
r1, _, e1 := syscall.Syscall6(procLookupPrivilegeDisplayNameW.Addr(), 5, uintptr(unsafe.Pointer(systemName)), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buffer)), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(languageId)), 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func backupRead(h syscall.Handle, b []byte, bytesRead *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
if len(b) > 0 {
|
||||||
|
_p0 = &b[0]
|
||||||
|
}
|
||||||
|
var _p1 uint32
|
||||||
|
if abort {
|
||||||
|
_p1 = 1
|
||||||
|
} else {
|
||||||
|
_p1 = 0
|
||||||
|
}
|
||||||
|
var _p2 uint32
|
||||||
|
if processSecurity {
|
||||||
|
_p2 = 1
|
||||||
|
} else {
|
||||||
|
_p2 = 0
|
||||||
|
}
|
||||||
|
r1, _, e1 := syscall.Syscall9(procBackupRead.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesRead)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func backupWrite(h syscall.Handle, b []byte, bytesWritten *uint32, abort bool, processSecurity bool, context *uintptr) (err error) {
|
||||||
|
var _p0 *byte
|
||||||
|
if len(b) > 0 {
|
||||||
|
_p0 = &b[0]
|
||||||
|
}
|
||||||
|
var _p1 uint32
|
||||||
|
if abort {
|
||||||
|
_p1 = 1
|
||||||
|
} else {
|
||||||
|
_p1 = 0
|
||||||
|
}
|
||||||
|
var _p2 uint32
|
||||||
|
if processSecurity {
|
||||||
|
_p2 = 1
|
||||||
|
} else {
|
||||||
|
_p2 = 0
|
||||||
|
}
|
||||||
|
r1, _, e1 := syscall.Syscall9(procBackupWrite.Addr(), 7, uintptr(h), uintptr(unsafe.Pointer(_p0)), uintptr(len(b)), uintptr(unsafe.Pointer(bytesWritten)), uintptr(_p1), uintptr(_p2), uintptr(unsafe.Pointer(context)), 0, 0)
|
||||||
|
if r1 == 0 {
|
||||||
|
if e1 != 0 {
|
||||||
|
err = errnoErr(e1)
|
||||||
|
} else {
|
||||||
|
err = syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
1
vendor/github.com/Microsoft/hcsshim/.gitignore
generated
vendored
Normal file
1
vendor/github.com/Microsoft/hcsshim/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.exe
|
||||||
21
vendor/github.com/Microsoft/hcsshim/LICENSE
generated
vendored
Normal file
21
vendor/github.com/Microsoft/hcsshim/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Microsoft
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
41
vendor/github.com/Microsoft/hcsshim/README.md
generated
vendored
Normal file
41
vendor/github.com/Microsoft/hcsshim/README.md
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# hcsshim
|
||||||
|
|
||||||
|
[](https://ci.appveyor.com/project/WindowsVirtualization/hcsshim/branch/master)
|
||||||
|
|
||||||
|
This package contains the Golang interface for using the Windows [Host Compute Service](https://blogs.technet.microsoft.com/virtualization/2017/01/27/introducing-the-host-compute-service-hcs/) (HCS) to launch and manage [Windows Containers](https://docs.microsoft.com/en-us/virtualization/windowscontainers/about/). It also contains other helpers and functions for managing Windows Containers such as the Golang interface for the Host Network Service (HNS).
|
||||||
|
|
||||||
|
It is primarily used in the [Moby Project](https://github.com/moby/moby), but it can be freely used by other projects as well.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||||
|
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
This project requires Golang 1.9 or newer to build.
|
||||||
|
|
||||||
|
For system requirements to run this project, see the Microsoft docs on [Windows Container requirements](https://docs.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/system-requirements).
|
||||||
|
|
||||||
|
## 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).
|
||||||
|
|
||||||
|
For additional details, see [Report a Computer Security Vulnerability](https://technet.microsoft.com/en-us/security/ff852094.aspx) on Technet
|
||||||
|
|
||||||
|
---------------
|
||||||
|
Copyright (c) 2018 Microsoft Corp. All rights reserved.
|
||||||
19
vendor/github.com/Microsoft/hcsshim/appveyor.yml
generated
vendored
Normal file
19
vendor/github.com/Microsoft/hcsshim/appveyor.yml
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
version: 0.1.{build}
|
||||||
|
|
||||||
|
image: Visual Studio 2017
|
||||||
|
|
||||||
|
clone_folder: c:\gopath\src\github.com\Microsoft\hcsshim
|
||||||
|
|
||||||
|
environment:
|
||||||
|
GOPATH: c:\gopath
|
||||||
|
PATH: C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin;%PATH%
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- go get -v -t ./...
|
||||||
|
- go build ./cmd/wclayer
|
||||||
|
- go build ./cmd/runhcs
|
||||||
|
- go test -v ./... -tags admin
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
- path: 'wclayer.exe'
|
||||||
|
- path: 'runhcs.exe'
|
||||||
192
vendor/github.com/Microsoft/hcsshim/container.go
generated
vendored
Normal file
192
vendor/github.com/Microsoft/hcsshim/container.go
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcs"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/mergemaps"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/schema1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContainerProperties holds the properties for a container and the processes running in that container
|
||||||
|
type ContainerProperties = schema1.ContainerProperties
|
||||||
|
|
||||||
|
// MemoryStats holds the memory statistics for a container
|
||||||
|
type MemoryStats = schema1.MemoryStats
|
||||||
|
|
||||||
|
// ProcessorStats holds the processor statistics for a container
|
||||||
|
type ProcessorStats = schema1.ProcessorStats
|
||||||
|
|
||||||
|
// StorageStats holds the storage statistics for a container
|
||||||
|
type StorageStats = schema1.StorageStats
|
||||||
|
|
||||||
|
// NetworkStats holds the network statistics for a container
|
||||||
|
type NetworkStats = schema1.NetworkStats
|
||||||
|
|
||||||
|
// Statistics is the structure returned by a statistics call on a container
|
||||||
|
type Statistics = schema1.Statistics
|
||||||
|
|
||||||
|
// ProcessList is the structure of an item returned by a ProcessList call on a container
|
||||||
|
type ProcessListItem = schema1.ProcessListItem
|
||||||
|
|
||||||
|
// MappedVirtualDiskController is the structure of an item returned by a MappedVirtualDiskList call on a container
|
||||||
|
type MappedVirtualDiskController = schema1.MappedVirtualDiskController
|
||||||
|
|
||||||
|
// Type of Request Support in ModifySystem
|
||||||
|
type RequestType = schema1.RequestType
|
||||||
|
|
||||||
|
// Type of Resource Support in ModifySystem
|
||||||
|
type ResourceType = schema1.ResourceType
|
||||||
|
|
||||||
|
// RequestType const
|
||||||
|
const (
|
||||||
|
Add = schema1.Add
|
||||||
|
Remove = schema1.Remove
|
||||||
|
Network = schema1.Network
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResourceModificationRequestResponse 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
|
||||||
|
type ResourceModificationRequestResponse = schema1.ResourceModificationRequestResponse
|
||||||
|
|
||||||
|
type container struct {
|
||||||
|
system *hcs.System
|
||||||
|
}
|
||||||
|
|
||||||
|
// createComputeSystemAdditionalJSON is read from the environment at initialisation
|
||||||
|
// time. It allows an environment variable to define additional JSON which
|
||||||
|
// is merged in the CreateComputeSystem call to HCS.
|
||||||
|
var createContainerAdditionalJSON []byte
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
createContainerAdditionalJSON = ([]byte)(os.Getenv("HCSSHIM_CREATECONTAINER_ADDITIONALJSON"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateContainer creates a new container with the given configuration but does not start it.
|
||||||
|
func CreateContainer(id string, c *ContainerConfig) (Container, error) {
|
||||||
|
fullConfig, err := mergemaps.MergeJSON(c, createContainerAdditionalJSON)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to merge additional JSON '%s': %s", createContainerAdditionalJSON, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
system, err := hcs.CreateComputeSystem(id, fullConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &container{system}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenContainer opens an existing container by ID.
|
||||||
|
func OpenContainer(id string) (Container, error) {
|
||||||
|
system, err := hcs.OpenComputeSystem(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &container{system}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContainers gets a list of the containers on the system that match the query
|
||||||
|
func GetContainers(q ComputeSystemQuery) ([]ContainerProperties, error) {
|
||||||
|
return hcs.GetComputeSystems(q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start synchronously starts the container.
|
||||||
|
func (container *container) Start() error {
|
||||||
|
return convertSystemError(container.system.Start(), container)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds.
|
||||||
|
func (container *container) Shutdown() error {
|
||||||
|
return convertSystemError(container.system.Shutdown(), container)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds.
|
||||||
|
func (container *container) Terminate() error {
|
||||||
|
return convertSystemError(container.system.Terminate(), container)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Waits synchronously waits for the container to shutdown or terminate.
|
||||||
|
func (container *container) Wait() error {
|
||||||
|
return convertSystemError(container.system.Wait(), container)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It
|
||||||
|
// returns false if timeout occurs.
|
||||||
|
func (container *container) WaitTimeout(t time.Duration) error {
|
||||||
|
return convertSystemError(container.system.WaitTimeout(t), container)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause pauses the execution of a container.
|
||||||
|
func (container *container) Pause() error {
|
||||||
|
return convertSystemError(container.system.Pause(), container)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume resumes the execution of a container.
|
||||||
|
func (container *container) Resume() error {
|
||||||
|
return convertSystemError(container.system.Resume(), container)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPendingUpdates returns true if the container has updates pending to install
|
||||||
|
func (container *container) HasPendingUpdates() (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statistics returns statistics for the container. This is a legacy v1 call
|
||||||
|
func (container *container) Statistics() (Statistics, error) {
|
||||||
|
properties, err := container.system.Properties(schema1.PropertyTypeStatistics)
|
||||||
|
if err != nil {
|
||||||
|
return Statistics{}, convertSystemError(err, container)
|
||||||
|
}
|
||||||
|
|
||||||
|
return properties.Statistics, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessList returns an array of ProcessListItems for the container. This is a legacy v1 call
|
||||||
|
func (container *container) ProcessList() ([]ProcessListItem, error) {
|
||||||
|
properties, err := container.system.Properties(schema1.PropertyTypeProcessList)
|
||||||
|
if err != nil {
|
||||||
|
return nil, convertSystemError(err, container)
|
||||||
|
}
|
||||||
|
|
||||||
|
return properties.ProcessList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a legacy v1 call
|
||||||
|
func (container *container) MappedVirtualDisks() (map[int]MappedVirtualDiskController, error) {
|
||||||
|
properties, err := container.system.Properties(schema1.PropertyTypeMappedVirtualDisk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, convertSystemError(err, container)
|
||||||
|
}
|
||||||
|
|
||||||
|
return properties.MappedVirtualDiskControllers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateProcess launches a new process within the container.
|
||||||
|
func (container *container) CreateProcess(c *ProcessConfig) (Process, error) {
|
||||||
|
p, err := container.system.CreateProcess(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, convertSystemError(err, container)
|
||||||
|
}
|
||||||
|
return &process{p}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenProcess gets an interface to an existing process within the container.
|
||||||
|
func (container *container) OpenProcess(pid int) (Process, error) {
|
||||||
|
p, err := container.system.OpenProcess(pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, convertSystemError(err, container)
|
||||||
|
}
|
||||||
|
return &process{p}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close cleans up any state associated with the container but does not terminate or wait for it.
|
||||||
|
func (container *container) Close() error {
|
||||||
|
return convertSystemError(container.system.Close(), container)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify the System
|
||||||
|
func (container *container) Modify(config *ResourceModificationRequestResponse) error {
|
||||||
|
return convertSystemError(container.system.Modify(config), container)
|
||||||
|
}
|
||||||
257
vendor/github.com/Microsoft/hcsshim/errors.go
generated
vendored
Normal file
257
vendor/github.com/Microsoft/hcsshim/errors.go
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hns"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcs"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists = hcs.exist
|
||||||
|
ErrComputeSystemDoesNotExist = hcs.ErrComputeSystemDoesNotExist
|
||||||
|
|
||||||
|
// ErrElementNotFound is an error encountered when the object being referenced does not exist
|
||||||
|
ErrElementNotFound = hcs.ErrElementNotFound
|
||||||
|
|
||||||
|
// ErrElementNotFound is an error encountered when the object being referenced does not exist
|
||||||
|
ErrNotSupported = hcs.ErrNotSupported
|
||||||
|
|
||||||
|
// ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported
|
||||||
|
// decimal -2147024883 / hex 0x8007000d
|
||||||
|
ErrInvalidData = hcs.ErrInvalidData
|
||||||
|
|
||||||
|
// ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed
|
||||||
|
ErrHandleClose = hcs.ErrHandleClose
|
||||||
|
|
||||||
|
// ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method
|
||||||
|
ErrAlreadyClosed = hcs.ErrAlreadyClosed
|
||||||
|
|
||||||
|
// ErrInvalidNotificationType is an error encountered when an invalid notification type is used
|
||||||
|
ErrInvalidNotificationType = hcs.ErrInvalidNotificationType
|
||||||
|
|
||||||
|
// ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation
|
||||||
|
ErrInvalidProcessState = hcs.ErrInvalidProcessState
|
||||||
|
|
||||||
|
// ErrTimeout is an error encountered when waiting on a notification times out
|
||||||
|
ErrTimeout = hcs.ErrTimeout
|
||||||
|
|
||||||
|
// ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for
|
||||||
|
// a different expected notification
|
||||||
|
ErrUnexpectedContainerExit = hcs.ErrUnexpectedContainerExit
|
||||||
|
|
||||||
|
// ErrUnexpectedProcessAbort is the error encountered when communication with the compute service
|
||||||
|
// is lost while waiting for a notification
|
||||||
|
ErrUnexpectedProcessAbort = hcs.ErrUnexpectedProcessAbort
|
||||||
|
|
||||||
|
// ErrUnexpectedValue is an error encountered when hcs returns an invalid value
|
||||||
|
ErrUnexpectedValue = hcs.ErrUnexpectedValue
|
||||||
|
|
||||||
|
// ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container
|
||||||
|
ErrVmcomputeAlreadyStopped = hcs.ErrVmcomputeAlreadyStopped
|
||||||
|
|
||||||
|
// ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously
|
||||||
|
ErrVmcomputeOperationPending = hcs.ErrVmcomputeOperationPending
|
||||||
|
|
||||||
|
// ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation
|
||||||
|
ErrVmcomputeOperationInvalidState = hcs.ErrVmcomputeOperationInvalidState
|
||||||
|
|
||||||
|
// ErrProcNotFound is an error encountered when the the process cannot be found
|
||||||
|
ErrProcNotFound = hcs.ErrProcNotFound
|
||||||
|
|
||||||
|
// ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2
|
||||||
|
// builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3.
|
||||||
|
ErrVmcomputeOperationAccessIsDenied = hcs.ErrVmcomputeOperationAccessIsDenied
|
||||||
|
|
||||||
|
// ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management
|
||||||
|
ErrVmcomputeInvalidJSON = hcs.ErrVmcomputeInvalidJSON
|
||||||
|
|
||||||
|
// ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message
|
||||||
|
ErrVmcomputeUnknownMessage = hcs.ErrVmcomputeUnknownMessage
|
||||||
|
|
||||||
|
// ErrNotSupported is an error encountered when hcs doesn't support the request
|
||||||
|
ErrPlatformNotSupported = hcs.ErrPlatformNotSupported
|
||||||
|
)
|
||||||
|
|
||||||
|
type EndpointNotFoundError = hns.EndpointNotFoundError
|
||||||
|
type NetworkNotFoundError = hns.NetworkNotFoundError
|
||||||
|
|
||||||
|
// ProcessError is an error encountered in HCS during an operation on a Process object
|
||||||
|
type ProcessError struct {
|
||||||
|
Process *process
|
||||||
|
Operation string
|
||||||
|
ExtraInfo string
|
||||||
|
Err error
|
||||||
|
Events []hcs.ErrorEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerError is an error encountered in HCS during an operation on a Container object
|
||||||
|
type ContainerError struct {
|
||||||
|
Container *container
|
||||||
|
Operation string
|
||||||
|
ExtraInfo string
|
||||||
|
Err error
|
||||||
|
Events []hcs.ErrorEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ContainerError) Error() string {
|
||||||
|
if e == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Container == nil {
|
||||||
|
return "unexpected nil container for error: " + e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
s := "container " + e.Container.system.ID()
|
||||||
|
|
||||||
|
if e.Operation != "" {
|
||||||
|
s += " encountered an error during " + e.Operation
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e.Err.(type) {
|
||||||
|
case nil:
|
||||||
|
break
|
||||||
|
case syscall.Errno:
|
||||||
|
s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, hcserror.Win32FromError(e.Err))
|
||||||
|
default:
|
||||||
|
s += fmt.Sprintf(": %s", e.Err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ev := range e.Events {
|
||||||
|
s += "\n" + ev.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.ExtraInfo != "" {
|
||||||
|
s += " extra info: " + e.ExtraInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeContainerError(container *container, operation string, extraInfo string, err error) error {
|
||||||
|
// Don't double wrap errors
|
||||||
|
if _, ok := err.(*ContainerError); ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
containerError := &ContainerError{Container: container, Operation: operation, ExtraInfo: extraInfo, Err: err}
|
||||||
|
return containerError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ProcessError) Error() string {
|
||||||
|
if e == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Process == nil {
|
||||||
|
return "Unexpected nil process for error: " + e.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
s := fmt.Sprintf("process %d in container %s", e.Process.p.Pid(), e.Process.p.SystemID())
|
||||||
|
if e.Operation != "" {
|
||||||
|
s += " encountered an error during " + e.Operation
|
||||||
|
}
|
||||||
|
|
||||||
|
switch e.Err.(type) {
|
||||||
|
case nil:
|
||||||
|
break
|
||||||
|
case syscall.Errno:
|
||||||
|
s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, hcserror.Win32FromError(e.Err))
|
||||||
|
default:
|
||||||
|
s += fmt.Sprintf(": %s", e.Err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ev := range e.Events {
|
||||||
|
s += "\n" + ev.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeProcessError(process *process, operation string, extraInfo string, err error) error {
|
||||||
|
// Don't double wrap errors
|
||||||
|
if _, ok := err.(*ProcessError); ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
processError := &ProcessError{Process: process, Operation: operation, ExtraInfo: extraInfo, Err: err}
|
||||||
|
return processError
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotExist checks if an error is caused by the Container or Process not existing.
|
||||||
|
// Note: Currently, ErrElementNotFound can mean that a Process has either
|
||||||
|
// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
|
||||||
|
// will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
|
||||||
|
func IsNotExist(err error) bool {
|
||||||
|
if _, ok := err.(EndpointNotFoundError); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := err.(NetworkNotFoundError); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return hcs.IsNotExist(getInnerError(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAlreadyClosed checks if an error is caused by the Container or Process having been
|
||||||
|
// already closed by a call to the Close() method.
|
||||||
|
func IsAlreadyClosed(err error) bool {
|
||||||
|
return hcs.IsAlreadyClosed(getInnerError(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPending returns a boolean indicating whether the error is that
|
||||||
|
// the requested operation is being completed in the background.
|
||||||
|
func IsPending(err error) bool {
|
||||||
|
return hcs.IsPending(getInnerError(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTimeout returns a boolean indicating whether the error is caused by
|
||||||
|
// a timeout waiting for the operation to complete.
|
||||||
|
func IsTimeout(err error) bool {
|
||||||
|
return hcs.IsTimeout(getInnerError(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAlreadyStopped returns a boolean indicating whether the error is caused by
|
||||||
|
// a Container or Process being already stopped.
|
||||||
|
// Note: Currently, ErrElementNotFound can mean that a Process has either
|
||||||
|
// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
|
||||||
|
// will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
|
||||||
|
func IsAlreadyStopped(err error) bool {
|
||||||
|
return hcs.IsAlreadyStopped(getInnerError(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotSupported returns a boolean indicating whether the error is caused by
|
||||||
|
// unsupported platform requests
|
||||||
|
// Note: Currently Unsupported platform requests can be mean either
|
||||||
|
// ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported or ErrVmcomputeUnknownMessage
|
||||||
|
// is thrown from the Platform
|
||||||
|
func IsNotSupported(err error) bool {
|
||||||
|
return hcs.IsNotSupported(getInnerError(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getInnerError(err error) error {
|
||||||
|
switch pe := err.(type) {
|
||||||
|
case nil:
|
||||||
|
return nil
|
||||||
|
case *ContainerError:
|
||||||
|
err = pe.Err
|
||||||
|
case *ProcessError:
|
||||||
|
err = pe.Err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertSystemError(err error, c *container) error {
|
||||||
|
if serr, ok := err.(*hcs.SystemError); ok {
|
||||||
|
return &ContainerError{Container: c, Operation: serr.Op, ExtraInfo: serr.Extra, Err: serr.Err, Events: serr.Events}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertProcessError(err error, p *process) error {
|
||||||
|
if perr, ok := err.(*hcs.ProcessError); ok {
|
||||||
|
return &ProcessError{Process: p, Operation: perr.Op, Err: perr.Err, Events: perr.Events}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
12
vendor/github.com/Microsoft/hcsshim/functional_tests.ps1
generated
vendored
Normal file
12
vendor/github.com/Microsoft/hcsshim/functional_tests.ps1
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Requirements so far:
|
||||||
|
# dockerd running
|
||||||
|
# - image microsoft/nanoserver (matching host base image) docker load -i c:\baseimages\nanoserver.tar
|
||||||
|
# - image alpine (linux) docker pull --platform=linux alpine
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Add this a parameter for debugging. ie "functional-tests -debug=$true"
|
||||||
|
#$env:HCSSHIM_FUNCTIONAL_TESTS_DEBUG="yes please"
|
||||||
|
|
||||||
|
#pushd uvm
|
||||||
|
go test -v -tags "functional uvmcreate uvmscratch uvmscsi uvmvpmem uvmvsmb uvmp9" ./...
|
||||||
|
#popd
|
||||||
28
vendor/github.com/Microsoft/hcsshim/hcsshim.go
generated
vendored
Normal file
28
vendor/github.com/Microsoft/hcsshim/hcsshim.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// Shim for the Host Compute Service (HCS) to manage Windows Server
|
||||||
|
// containers and Hyper-V containers.
|
||||||
|
|
||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run mksyscall_windows.go -output zsyscall_windows.go hcsshim.go
|
||||||
|
|
||||||
|
//sys SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) = iphlpapi.SetCurrentThreadCompartmentId
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Specific user-visible exit codes
|
||||||
|
WaitErrExecFailed = 32767
|
||||||
|
|
||||||
|
ERROR_GEN_FAILURE = hcserror.ERROR_GEN_FAILURE
|
||||||
|
ERROR_SHUTDOWN_IN_PROGRESS = syscall.Errno(1115)
|
||||||
|
WSAEINVAL = syscall.Errno(10022)
|
||||||
|
|
||||||
|
// Timeout on wait calls
|
||||||
|
TimeoutInfinite = 0xFFFFFFFF
|
||||||
|
)
|
||||||
|
|
||||||
|
type HcsError = hcserror.HcsError
|
||||||
91
vendor/github.com/Microsoft/hcsshim/hnsendpoint.go
generated
vendored
Normal file
91
vendor/github.com/Microsoft/hcsshim/hnsendpoint.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HNSEndpoint represents a network endpoint in HNS
|
||||||
|
type HNSEndpoint = hns.HNSEndpoint
|
||||||
|
|
||||||
|
//SystemType represents the type of the system on which actions are done
|
||||||
|
type SystemType string
|
||||||
|
|
||||||
|
// SystemType const
|
||||||
|
const (
|
||||||
|
ContainerType SystemType = "Container"
|
||||||
|
VirtualMachineType SystemType = "VirtualMachine"
|
||||||
|
HostType SystemType = "Host"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
type EndpointAttachDetachRequest = hns.EndpointAttachDetachRequest
|
||||||
|
|
||||||
|
// EndpointResquestResponse is object to get the endpoint request response
|
||||||
|
type EndpointResquestResponse = hns.EndpointResquestResponse
|
||||||
|
|
||||||
|
// HNSEndpointRequest makes a HNS call to modify/query a network endpoint
|
||||||
|
func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) {
|
||||||
|
return hns.HNSEndpointRequest(method, path, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSListEndpointRequest makes a HNS call to query the list of available endpoints
|
||||||
|
func HNSListEndpointRequest() ([]HNSEndpoint, error) {
|
||||||
|
return hns.HNSListEndpointRequest()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HotAttachEndpoint makes a HCS Call to attach the endpoint to the container
|
||||||
|
func HotAttachEndpoint(containerID string, endpointID string) error {
|
||||||
|
return modifyNetworkEndpoint(containerID, endpointID, Add)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HotDetachEndpoint makes a HCS Call to detach the endpoint from the container
|
||||||
|
func HotDetachEndpoint(containerID string, endpointID string) error {
|
||||||
|
return modifyNetworkEndpoint(containerID, endpointID, Remove)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyContainer corresponding to the container id, by sending a request
|
||||||
|
func modifyContainer(id string, request *ResourceModificationRequestResponse) error {
|
||||||
|
container, err := OpenContainer(id)
|
||||||
|
if err != nil {
|
||||||
|
if IsNotExist(err) {
|
||||||
|
return ErrComputeSystemDoesNotExist
|
||||||
|
}
|
||||||
|
return getInnerError(err)
|
||||||
|
}
|
||||||
|
defer container.Close()
|
||||||
|
err = container.Modify(request)
|
||||||
|
if err != nil {
|
||||||
|
if IsNotSupported(err) {
|
||||||
|
return ErrPlatformNotSupported
|
||||||
|
}
|
||||||
|
return getInnerError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func modifyNetworkEndpoint(containerID string, endpointID string, request RequestType) error {
|
||||||
|
requestMessage := &ResourceModificationRequestResponse{
|
||||||
|
Resource: Network,
|
||||||
|
Request: request,
|
||||||
|
Data: endpointID,
|
||||||
|
}
|
||||||
|
err := modifyContainer(containerID, requestMessage)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHNSEndpointByID get the Endpoint by ID
|
||||||
|
func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) {
|
||||||
|
return hns.GetHNSEndpointByID(endpointID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHNSEndpointByName gets the endpoint filtered by Name
|
||||||
|
func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) {
|
||||||
|
return hns.GetHNSEndpointByName(endpointName)
|
||||||
|
}
|
||||||
16
vendor/github.com/Microsoft/hcsshim/hnsglobals.go
generated
vendored
Normal file
16
vendor/github.com/Microsoft/hcsshim/hnsglobals.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hns"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HNSGlobals = hns.HNSGlobals
|
||||||
|
type HNSVersion = hns.HNSVersion
|
||||||
|
|
||||||
|
var (
|
||||||
|
HNSVersion1803 = hns.HNSVersion1803
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetHNSGlobals() (*HNSGlobals, error) {
|
||||||
|
return hns.GetHNSGlobals()
|
||||||
|
}
|
||||||
36
vendor/github.com/Microsoft/hcsshim/hnsnetwork.go
generated
vendored
Normal file
36
vendor/github.com/Microsoft/hcsshim/hnsnetwork.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Subnet is assoicated with a network and represents a list
|
||||||
|
// of subnets available to the network
|
||||||
|
type Subnet = hns.Subnet
|
||||||
|
|
||||||
|
// MacPool is assoicated with a network and represents a list
|
||||||
|
// of macaddresses available to the network
|
||||||
|
type MacPool = hns.MacPool
|
||||||
|
|
||||||
|
// HNSNetwork represents a network in HNS
|
||||||
|
type HNSNetwork = hns.HNSNetwork
|
||||||
|
|
||||||
|
// HNSNetworkRequest makes a call into HNS to update/query a single network
|
||||||
|
func HNSNetworkRequest(method, path, request string) (*HNSNetwork, error) {
|
||||||
|
return hns.HNSNetworkRequest(method, path, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSListNetworkRequest makes a HNS call to query the list of available networks
|
||||||
|
func HNSListNetworkRequest(method, path, request string) ([]HNSNetwork, error) {
|
||||||
|
return hns.HNSListNetworkRequest(method, path, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHNSNetworkByID
|
||||||
|
func GetHNSNetworkByID(networkID string) (*HNSNetwork, error) {
|
||||||
|
return hns.GetHNSNetworkByID(networkID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHNSNetworkName filtered by Name
|
||||||
|
func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) {
|
||||||
|
return hns.GetHNSNetworkByName(networkName)
|
||||||
|
}
|
||||||
57
vendor/github.com/Microsoft/hcsshim/hnspolicy.go
generated
vendored
Normal file
57
vendor/github.com/Microsoft/hcsshim/hnspolicy.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Type of Request Support in ModifySystem
|
||||||
|
type PolicyType = hns.PolicyType
|
||||||
|
|
||||||
|
// RequestType const
|
||||||
|
const (
|
||||||
|
Nat = hns.Nat
|
||||||
|
ACL = hns.ACL
|
||||||
|
PA = hns.PA
|
||||||
|
VLAN = hns.VLAN
|
||||||
|
VSID = hns.VSID
|
||||||
|
VNet = hns.VNet
|
||||||
|
L2Driver = hns.L2Driver
|
||||||
|
Isolation = hns.Isolation
|
||||||
|
QOS = hns.QOS
|
||||||
|
OutboundNat = hns.OutboundNat
|
||||||
|
ExternalLoadBalancer = hns.ExternalLoadBalancer
|
||||||
|
Route = hns.Route
|
||||||
|
)
|
||||||
|
|
||||||
|
type NatPolicy = hns.NatPolicy
|
||||||
|
|
||||||
|
type QosPolicy = hns.QosPolicy
|
||||||
|
|
||||||
|
type IsolationPolicy = hns.IsolationPolicy
|
||||||
|
|
||||||
|
type VlanPolicy = hns.VlanPolicy
|
||||||
|
|
||||||
|
type VsidPolicy = hns.VsidPolicy
|
||||||
|
|
||||||
|
type PaPolicy = hns.PaPolicy
|
||||||
|
|
||||||
|
type OutboundNatPolicy = hns.OutboundNatPolicy
|
||||||
|
|
||||||
|
type ActionType = hns.ActionType
|
||||||
|
type DirectionType = hns.DirectionType
|
||||||
|
type RuleType = hns.RuleType
|
||||||
|
|
||||||
|
const (
|
||||||
|
Allow = hns.Allow
|
||||||
|
Block = hns.Block
|
||||||
|
|
||||||
|
In = hns.In
|
||||||
|
Out = hns.Out
|
||||||
|
|
||||||
|
Host = hns.Host
|
||||||
|
Switch = hns.Switch
|
||||||
|
)
|
||||||
|
|
||||||
|
type ACLPolicy = hns.ACLPolicy
|
||||||
|
|
||||||
|
type Policy = hns.Policy
|
||||||
47
vendor/github.com/Microsoft/hcsshim/hnspolicylist.go
generated
vendored
Normal file
47
vendor/github.com/Microsoft/hcsshim/hnspolicylist.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RoutePolicy is a structure defining schema for Route based Policy
|
||||||
|
type RoutePolicy = hns.RoutePolicy
|
||||||
|
|
||||||
|
// ELBPolicy is a structure defining schema for ELB LoadBalancing based Policy
|
||||||
|
type ELBPolicy = hns.ELBPolicy
|
||||||
|
|
||||||
|
// LBPolicy is a structure defining schema for LoadBalancing based Policy
|
||||||
|
type LBPolicy = hns.LBPolicy
|
||||||
|
|
||||||
|
// PolicyList is a structure defining schema for Policy list request
|
||||||
|
type PolicyList = hns.PolicyList
|
||||||
|
|
||||||
|
// HNSPolicyListRequest makes a call into HNS to update/query a single network
|
||||||
|
func HNSPolicyListRequest(method, path, request string) (*PolicyList, error) {
|
||||||
|
return hns.HNSPolicyListRequest(method, path, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSListPolicyListRequest gets all the policy list
|
||||||
|
func HNSListPolicyListRequest() ([]PolicyList, error) {
|
||||||
|
return hns.HNSListPolicyListRequest()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyListRequest makes a HNS call to modify/query a network policy list
|
||||||
|
func PolicyListRequest(method, path, request string) (*PolicyList, error) {
|
||||||
|
return hns.PolicyListRequest(method, path, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPolicyListByID get the policy list by ID
|
||||||
|
func GetPolicyListByID(policyListID string) (*PolicyList, error) {
|
||||||
|
return hns.GetPolicyListByID(policyListID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddLoadBalancer policy list for the specified endpoints
|
||||||
|
func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) {
|
||||||
|
return hns.AddLoadBalancer(endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRoute adds route policy list for the specified endpoints
|
||||||
|
func AddRoute(endpoints []HNSEndpoint, destinationPrefix string, nextHop string, encapEnabled bool) (*PolicyList, error) {
|
||||||
|
return hns.AddRoute(endpoints, destinationPrefix, nextHop, encapEnabled)
|
||||||
|
}
|
||||||
13
vendor/github.com/Microsoft/hcsshim/hnssupport.go
generated
vendored
Normal file
13
vendor/github.com/Microsoft/hcsshim/hnssupport.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hns"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HNSSupportedFeatures = hns.HNSSupportedFeatures
|
||||||
|
|
||||||
|
type HNSAclFeatures = hns.HNSAclFeatures
|
||||||
|
|
||||||
|
func GetHNSSupportedFeatures() HNSSupportedFeatures {
|
||||||
|
return hns.GetHNSSupportedFeatures()
|
||||||
|
}
|
||||||
109
vendor/github.com/Microsoft/hcsshim/interface.go
generated
vendored
Normal file
109
vendor/github.com/Microsoft/hcsshim/interface.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package hcsshim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/schema1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessConfig is used as both the input of Container.CreateProcess
|
||||||
|
// and to convert the parameters to JSON for passing onto the HCS
|
||||||
|
type ProcessConfig = schema1.ProcessConfig
|
||||||
|
|
||||||
|
type Layer = schema1.Layer
|
||||||
|
type MappedDir = schema1.MappedDir
|
||||||
|
type MappedPipe = schema1.MappedPipe
|
||||||
|
type HvRuntime = schema1.HvRuntime
|
||||||
|
type MappedVirtualDisk = schema1.MappedVirtualDisk
|
||||||
|
|
||||||
|
// ContainerConfig is used as both the input of CreateContainer
|
||||||
|
// and to convert the parameters to JSON for passing onto the HCS
|
||||||
|
type ContainerConfig = schema1.ContainerConfig
|
||||||
|
|
||||||
|
type ComputeSystemQuery = schema1.ComputeSystemQuery
|
||||||
|
|
||||||
|
// Container represents a created (but not necessarily running) container.
|
||||||
|
type Container interface {
|
||||||
|
// Start synchronously starts the container.
|
||||||
|
Start() error
|
||||||
|
|
||||||
|
// Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds.
|
||||||
|
Shutdown() error
|
||||||
|
|
||||||
|
// Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds.
|
||||||
|
Terminate() error
|
||||||
|
|
||||||
|
// Waits synchronously waits for the container to shutdown or terminate.
|
||||||
|
Wait() error
|
||||||
|
|
||||||
|
// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It
|
||||||
|
// returns false if timeout occurs.
|
||||||
|
WaitTimeout(time.Duration) error
|
||||||
|
|
||||||
|
// Pause pauses the execution of a container.
|
||||||
|
Pause() error
|
||||||
|
|
||||||
|
// Resume resumes the execution of a container.
|
||||||
|
Resume() error
|
||||||
|
|
||||||
|
// HasPendingUpdates returns true if the container has updates pending to install.
|
||||||
|
HasPendingUpdates() (bool, error)
|
||||||
|
|
||||||
|
// Statistics returns statistics for a container.
|
||||||
|
Statistics() (Statistics, error)
|
||||||
|
|
||||||
|
// ProcessList returns details for the processes in a container.
|
||||||
|
ProcessList() ([]ProcessListItem, error)
|
||||||
|
|
||||||
|
// MappedVirtualDisks returns virtual disks mapped to a utility VM, indexed by controller
|
||||||
|
MappedVirtualDisks() (map[int]MappedVirtualDiskController, error)
|
||||||
|
|
||||||
|
// CreateProcess launches a new process within the container.
|
||||||
|
CreateProcess(c *ProcessConfig) (Process, error)
|
||||||
|
|
||||||
|
// OpenProcess gets an interface to an existing process within the container.
|
||||||
|
OpenProcess(pid int) (Process, error)
|
||||||
|
|
||||||
|
// Close cleans up any state associated with the container but does not terminate or wait for it.
|
||||||
|
Close() error
|
||||||
|
|
||||||
|
// Modify the System
|
||||||
|
Modify(config *ResourceModificationRequestResponse) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process represents a running or exited process.
|
||||||
|
type Process interface {
|
||||||
|
// Pid returns the process ID of the process within the container.
|
||||||
|
Pid() int
|
||||||
|
|
||||||
|
// Kill signals the process to terminate but does not wait for it to finish terminating.
|
||||||
|
Kill() error
|
||||||
|
|
||||||
|
// Wait waits for the process to exit.
|
||||||
|
Wait() error
|
||||||
|
|
||||||
|
// WaitTimeout waits for the process to exit or the duration to elapse. It returns
|
||||||
|
// false if timeout occurs.
|
||||||
|
WaitTimeout(time.Duration) error
|
||||||
|
|
||||||
|
// ExitCode returns the exit code of the process. The process must have
|
||||||
|
// already terminated.
|
||||||
|
ExitCode() (int, error)
|
||||||
|
|
||||||
|
// ResizeConsole resizes the console of the process.
|
||||||
|
ResizeConsole(width, height uint16) error
|
||||||
|
|
||||||
|
// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing
|
||||||
|
// these pipes does not close the underlying pipes; it should be possible to
|
||||||
|
// call this multiple times to get multiple interfaces.
|
||||||
|
Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error)
|
||||||
|
|
||||||
|
// CloseStdin closes the write side of the stdin pipe so that the process is
|
||||||
|
// notified on the read side that there is no more data in stdin.
|
||||||
|
CloseStdin() error
|
||||||
|
|
||||||
|
// Close cleans up any state associated with the process but does not kill
|
||||||
|
// or wait on it.
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
69
vendor/github.com/Microsoft/hcsshim/internal/guid/guid.go
generated
vendored
Normal file
69
vendor/github.com/Microsoft/hcsshim/internal/guid/guid.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package guid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = (json.Marshaler)(&GUID{})
|
||||||
|
var _ = (json.Unmarshaler)(&GUID{})
|
||||||
|
|
||||||
|
type GUID [16]byte
|
||||||
|
|
||||||
|
func New() GUID {
|
||||||
|
g := GUID{}
|
||||||
|
_, err := io.ReadFull(rand.Reader, g[:])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GUID) String() string {
|
||||||
|
return fmt.Sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x-%02x", g[3], g[2], g[1], g[0], g[5], g[4], g[7], g[6], g[8:10], g[10:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromString(s string) GUID {
|
||||||
|
if len(s) != 36 {
|
||||||
|
panic(fmt.Sprintf("invalid GUID length: %d", len(s)))
|
||||||
|
}
|
||||||
|
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
||||||
|
panic("invalid GUID format")
|
||||||
|
}
|
||||||
|
indexOrder := [16]int{
|
||||||
|
0, 2, 4, 6,
|
||||||
|
9, 11,
|
||||||
|
14, 16,
|
||||||
|
19, 21,
|
||||||
|
24, 26, 28, 30, 32, 34,
|
||||||
|
}
|
||||||
|
byteOrder := [16]int{
|
||||||
|
3, 2, 1, 0,
|
||||||
|
5, 4,
|
||||||
|
7, 6,
|
||||||
|
8, 9,
|
||||||
|
10, 11, 12, 13, 14, 15,
|
||||||
|
}
|
||||||
|
var g GUID
|
||||||
|
for i, x := range indexOrder {
|
||||||
|
b, err := strconv.ParseInt(s[x:x+2], 16, 16)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
g[byteOrder[i]] = byte(b)
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GUID) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(g.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GUID) UnmarshalJSON(data []byte) error {
|
||||||
|
*g = FromString(strings.Trim(string(data), "\""))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
81
vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go
generated
vendored
Normal file
81
vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package hcs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/interop"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
nextCallback uintptr
|
||||||
|
callbackMap = map[uintptr]*notifcationWatcherContext{}
|
||||||
|
callbackMapLock = sync.RWMutex{}
|
||||||
|
|
||||||
|
notificationWatcherCallback = syscall.NewCallback(notificationWatcher)
|
||||||
|
|
||||||
|
// Notifications for HCS_SYSTEM handles
|
||||||
|
hcsNotificationSystemExited hcsNotification = 0x00000001
|
||||||
|
hcsNotificationSystemCreateCompleted hcsNotification = 0x00000002
|
||||||
|
hcsNotificationSystemStartCompleted hcsNotification = 0x00000003
|
||||||
|
hcsNotificationSystemPauseCompleted hcsNotification = 0x00000004
|
||||||
|
hcsNotificationSystemResumeCompleted hcsNotification = 0x00000005
|
||||||
|
|
||||||
|
// Notifications for HCS_PROCESS handles
|
||||||
|
hcsNotificationProcessExited hcsNotification = 0x00010000
|
||||||
|
|
||||||
|
// Common notifications
|
||||||
|
hcsNotificationInvalid hcsNotification = 0x00000000
|
||||||
|
hcsNotificationServiceDisconnect hcsNotification = 0x01000000
|
||||||
|
)
|
||||||
|
|
||||||
|
type hcsNotification uint32
|
||||||
|
type notificationChannel chan error
|
||||||
|
|
||||||
|
type notifcationWatcherContext struct {
|
||||||
|
channels notificationChannels
|
||||||
|
handle hcsCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
type notificationChannels map[hcsNotification]notificationChannel
|
||||||
|
|
||||||
|
func newChannels() notificationChannels {
|
||||||
|
channels := make(notificationChannels)
|
||||||
|
|
||||||
|
channels[hcsNotificationSystemExited] = make(notificationChannel, 1)
|
||||||
|
channels[hcsNotificationSystemCreateCompleted] = make(notificationChannel, 1)
|
||||||
|
channels[hcsNotificationSystemStartCompleted] = make(notificationChannel, 1)
|
||||||
|
channels[hcsNotificationSystemPauseCompleted] = make(notificationChannel, 1)
|
||||||
|
channels[hcsNotificationSystemResumeCompleted] = make(notificationChannel, 1)
|
||||||
|
channels[hcsNotificationProcessExited] = make(notificationChannel, 1)
|
||||||
|
channels[hcsNotificationServiceDisconnect] = make(notificationChannel, 1)
|
||||||
|
return channels
|
||||||
|
}
|
||||||
|
func closeChannels(channels notificationChannels) {
|
||||||
|
close(channels[hcsNotificationSystemExited])
|
||||||
|
close(channels[hcsNotificationSystemCreateCompleted])
|
||||||
|
close(channels[hcsNotificationSystemStartCompleted])
|
||||||
|
close(channels[hcsNotificationSystemPauseCompleted])
|
||||||
|
close(channels[hcsNotificationSystemResumeCompleted])
|
||||||
|
close(channels[hcsNotificationProcessExited])
|
||||||
|
close(channels[hcsNotificationServiceDisconnect])
|
||||||
|
}
|
||||||
|
|
||||||
|
func notificationWatcher(notificationType hcsNotification, callbackNumber uintptr, notificationStatus uintptr, notificationData *uint16) uintptr {
|
||||||
|
var result error
|
||||||
|
if int32(notificationStatus) < 0 {
|
||||||
|
result = interop.Win32FromHresult(notificationStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackMapLock.RLock()
|
||||||
|
context := callbackMap[callbackNumber]
|
||||||
|
callbackMapLock.RUnlock()
|
||||||
|
|
||||||
|
if context == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
context.channels[notificationType] <- result
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
7
vendor/github.com/Microsoft/hcsshim/internal/hcs/cgo.go
generated
vendored
Normal file
7
vendor/github.com/Microsoft/hcsshim/internal/hcs/cgo.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package hcs
|
||||||
|
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
// This import is needed to make the library compile as CGO because HCSSHIM
|
||||||
|
// only works with CGO due to callbacks from HCS comming back from a C thread
|
||||||
|
// which is not supported without CGO. See https://github.com/golang/go/issues/10973
|
||||||
279
vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go
generated
vendored
Normal file
279
vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go
generated
vendored
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
package hcs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/interop"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists
|
||||||
|
ErrComputeSystemDoesNotExist = syscall.Errno(0xc037010e)
|
||||||
|
|
||||||
|
// ErrElementNotFound is an error encountered when the object being referenced does not exist
|
||||||
|
ErrElementNotFound = syscall.Errno(0x490)
|
||||||
|
|
||||||
|
// ErrElementNotFound is an error encountered when the object being referenced does not exist
|
||||||
|
ErrNotSupported = syscall.Errno(0x32)
|
||||||
|
|
||||||
|
// ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported
|
||||||
|
// decimal -2147024883 / hex 0x8007000d
|
||||||
|
ErrInvalidData = syscall.Errno(0xd)
|
||||||
|
|
||||||
|
// ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed
|
||||||
|
ErrHandleClose = errors.New("hcsshim: the handle generating this notification has been closed")
|
||||||
|
|
||||||
|
// ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method
|
||||||
|
ErrAlreadyClosed = errors.New("hcsshim: the handle has already been closed")
|
||||||
|
|
||||||
|
// ErrInvalidNotificationType is an error encountered when an invalid notification type is used
|
||||||
|
ErrInvalidNotificationType = errors.New("hcsshim: invalid notification type")
|
||||||
|
|
||||||
|
// ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation
|
||||||
|
ErrInvalidProcessState = errors.New("the process is in an invalid state for the attempted operation")
|
||||||
|
|
||||||
|
// ErrTimeout is an error encountered when waiting on a notification times out
|
||||||
|
ErrTimeout = errors.New("hcsshim: timeout waiting for notification")
|
||||||
|
|
||||||
|
// ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for
|
||||||
|
// a different expected notification
|
||||||
|
ErrUnexpectedContainerExit = errors.New("unexpected container exit")
|
||||||
|
|
||||||
|
// ErrUnexpectedProcessAbort is the error encountered when communication with the compute service
|
||||||
|
// is lost while waiting for a notification
|
||||||
|
ErrUnexpectedProcessAbort = errors.New("lost communication with compute service")
|
||||||
|
|
||||||
|
// ErrUnexpectedValue is an error encountered when hcs returns an invalid value
|
||||||
|
ErrUnexpectedValue = errors.New("unexpected value returned from hcs")
|
||||||
|
|
||||||
|
// ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container
|
||||||
|
ErrVmcomputeAlreadyStopped = syscall.Errno(0xc0370110)
|
||||||
|
|
||||||
|
// ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously
|
||||||
|
ErrVmcomputeOperationPending = syscall.Errno(0xC0370103)
|
||||||
|
|
||||||
|
// ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation
|
||||||
|
ErrVmcomputeOperationInvalidState = syscall.Errno(0xc0370105)
|
||||||
|
|
||||||
|
// ErrProcNotFound is an error encountered when the the process cannot be found
|
||||||
|
ErrProcNotFound = syscall.Errno(0x7f)
|
||||||
|
|
||||||
|
// ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2
|
||||||
|
// builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3.
|
||||||
|
ErrVmcomputeOperationAccessIsDenied = syscall.Errno(0x5)
|
||||||
|
|
||||||
|
// ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management
|
||||||
|
ErrVmcomputeInvalidJSON = syscall.Errno(0xc037010d)
|
||||||
|
|
||||||
|
// ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message
|
||||||
|
ErrVmcomputeUnknownMessage = syscall.Errno(0xc037010b)
|
||||||
|
|
||||||
|
// ErrNotSupported is an error encountered when hcs doesn't support the request
|
||||||
|
ErrPlatformNotSupported = errors.New("unsupported platform request")
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrorEvent struct {
|
||||||
|
Message string `json:"Message,omitempty"` // Fully formated error message
|
||||||
|
StackTrace string `json:"StackTrace,omitempty"` // Stack trace in string form
|
||||||
|
Provider string `json:"Provider,omitempty"`
|
||||||
|
EventID uint16 `json:"EventId,omitempty"`
|
||||||
|
Flags uint32 `json:"Flags,omitempty"`
|
||||||
|
Source string `json:"Source,omitempty"`
|
||||||
|
//Data []EventData `json:"Data,omitempty"` // Omit this as HCS doesn't encode this well. It's more confusing to include. It is however logged in debug mode (see processHcsResult function)
|
||||||
|
}
|
||||||
|
|
||||||
|
type hcsResult struct {
|
||||||
|
Error int32
|
||||||
|
ErrorMessage string
|
||||||
|
ErrorEvents []ErrorEvent `json:"ErrorEvents,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ev *ErrorEvent) String() string {
|
||||||
|
evs := "[Event Detail: " + ev.Message
|
||||||
|
if ev.StackTrace != "" {
|
||||||
|
evs += " Stack Trace: " + ev.StackTrace
|
||||||
|
}
|
||||||
|
if ev.Provider != "" {
|
||||||
|
evs += " Provider: " + ev.Provider
|
||||||
|
}
|
||||||
|
if ev.EventID != 0 {
|
||||||
|
evs = fmt.Sprintf("%s EventID: %d", evs, ev.EventID)
|
||||||
|
}
|
||||||
|
if ev.Flags != 0 {
|
||||||
|
evs = fmt.Sprintf("%s flags: %d", evs, ev.Flags)
|
||||||
|
}
|
||||||
|
if ev.Source != "" {
|
||||||
|
evs += " Source: " + ev.Source
|
||||||
|
}
|
||||||
|
evs += "]"
|
||||||
|
return evs
|
||||||
|
}
|
||||||
|
|
||||||
|
func processHcsResult(resultp *uint16) []ErrorEvent {
|
||||||
|
if resultp != nil {
|
||||||
|
resultj := interop.ConvertAndFreeCoTaskMemString(resultp)
|
||||||
|
logrus.Debugf("Result: %s", resultj)
|
||||||
|
result := &hcsResult{}
|
||||||
|
if err := json.Unmarshal([]byte(resultj), result); err != nil {
|
||||||
|
logrus.Warnf("Could not unmarshal HCS result %s: %s", resultj, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return result.ErrorEvents
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type HcsError struct {
|
||||||
|
Op string
|
||||||
|
Err error
|
||||||
|
Events []ErrorEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HcsError) Error() string {
|
||||||
|
s := e.Op + ": " + e.Err.Error()
|
||||||
|
for _, ev := range e.Events {
|
||||||
|
s += "\n" + ev.String()
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessError is an error encountered in HCS during an operation on a Process object
|
||||||
|
type ProcessError struct {
|
||||||
|
SystemID string
|
||||||
|
Pid int
|
||||||
|
Op string
|
||||||
|
Err error
|
||||||
|
Events []ErrorEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemError is an error encountered in HCS during an operation on a Container object
|
||||||
|
type SystemError struct {
|
||||||
|
ID string
|
||||||
|
Op string
|
||||||
|
Err error
|
||||||
|
Extra string
|
||||||
|
Events []ErrorEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SystemError) Error() string {
|
||||||
|
s := e.Op + " " + e.ID + ": " + e.Err.Error()
|
||||||
|
for _, ev := range e.Events {
|
||||||
|
s += "\n" + ev.String()
|
||||||
|
}
|
||||||
|
if e.Extra != "" {
|
||||||
|
s += "\n(extra info: " + e.Extra + ")"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeSystemError(system *System, op string, extra string, err error, events []ErrorEvent) error {
|
||||||
|
// Don't double wrap errors
|
||||||
|
if _, ok := err.(*SystemError); ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return &SystemError{
|
||||||
|
ID: system.ID(),
|
||||||
|
Op: op,
|
||||||
|
Extra: extra,
|
||||||
|
Err: err,
|
||||||
|
Events: events,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ProcessError) Error() string {
|
||||||
|
s := fmt.Sprintf("%s %s:%d: %s", e.Op, e.SystemID, e.Pid, e.Err.Error())
|
||||||
|
for _, ev := range e.Events {
|
||||||
|
s += "\n" + ev.String()
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeProcessError(process *Process, op string, err error, events []ErrorEvent) error {
|
||||||
|
// Don't double wrap errors
|
||||||
|
if _, ok := err.(*ProcessError); ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return &ProcessError{
|
||||||
|
Pid: process.Pid(),
|
||||||
|
SystemID: process.SystemID(),
|
||||||
|
Op: op,
|
||||||
|
Err: err,
|
||||||
|
Events: events,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotExist checks if an error is caused by the Container or Process not existing.
|
||||||
|
// Note: Currently, ErrElementNotFound can mean that a Process has either
|
||||||
|
// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
|
||||||
|
// will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
|
||||||
|
func IsNotExist(err error) bool {
|
||||||
|
err = getInnerError(err)
|
||||||
|
return err == ErrComputeSystemDoesNotExist ||
|
||||||
|
err == ErrElementNotFound ||
|
||||||
|
err == ErrProcNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAlreadyClosed checks if an error is caused by the Container or Process having been
|
||||||
|
// already closed by a call to the Close() method.
|
||||||
|
func IsAlreadyClosed(err error) bool {
|
||||||
|
err = getInnerError(err)
|
||||||
|
return err == ErrAlreadyClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPending returns a boolean indicating whether the error is that
|
||||||
|
// the requested operation is being completed in the background.
|
||||||
|
func IsPending(err error) bool {
|
||||||
|
err = getInnerError(err)
|
||||||
|
return err == ErrVmcomputeOperationPending
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTimeout returns a boolean indicating whether the error is caused by
|
||||||
|
// a timeout waiting for the operation to complete.
|
||||||
|
func IsTimeout(err error) bool {
|
||||||
|
err = getInnerError(err)
|
||||||
|
return err == ErrTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAlreadyStopped returns a boolean indicating whether the error is caused by
|
||||||
|
// a Container or Process being already stopped.
|
||||||
|
// Note: Currently, ErrElementNotFound can mean that a Process has either
|
||||||
|
// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist
|
||||||
|
// will currently return true when the error is ErrElementNotFound or ErrProcNotFound.
|
||||||
|
func IsAlreadyStopped(err error) bool {
|
||||||
|
err = getInnerError(err)
|
||||||
|
return err == ErrVmcomputeAlreadyStopped ||
|
||||||
|
err == ErrElementNotFound ||
|
||||||
|
err == ErrProcNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotSupported returns a boolean indicating whether the error is caused by
|
||||||
|
// unsupported platform requests
|
||||||
|
// Note: Currently Unsupported platform requests can be mean either
|
||||||
|
// ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported or ErrVmcomputeUnknownMessage
|
||||||
|
// is thrown from the Platform
|
||||||
|
func IsNotSupported(err error) bool {
|
||||||
|
err = getInnerError(err)
|
||||||
|
// If Platform doesn't recognize or support the request sent, below errors are seen
|
||||||
|
return err == ErrVmcomputeInvalidJSON ||
|
||||||
|
err == ErrInvalidData ||
|
||||||
|
err == ErrNotSupported ||
|
||||||
|
err == ErrVmcomputeUnknownMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
func getInnerError(err error) error {
|
||||||
|
switch pe := err.(type) {
|
||||||
|
case nil:
|
||||||
|
return nil
|
||||||
|
case *HcsError:
|
||||||
|
err = pe.Err
|
||||||
|
case *SystemError:
|
||||||
|
err = pe.Err
|
||||||
|
case *ProcessError:
|
||||||
|
err = pe.Err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
47
vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go
generated
vendored
Normal file
47
vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Shim for the Host Compute Service (HCS) to manage Windows Server
|
||||||
|
// containers and Hyper-V containers.
|
||||||
|
|
||||||
|
package hcs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go hcs.go
|
||||||
|
|
||||||
|
//sys hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) = vmcompute.HcsEnumerateComputeSystems?
|
||||||
|
//sys hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) = vmcompute.HcsCreateComputeSystem?
|
||||||
|
//sys hcsOpenComputeSystem(id string, computeSystem *hcsSystem, result **uint16) (hr error) = vmcompute.HcsOpenComputeSystem?
|
||||||
|
//sys hcsCloseComputeSystem(computeSystem hcsSystem) (hr error) = vmcompute.HcsCloseComputeSystem?
|
||||||
|
//sys hcsStartComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsStartComputeSystem?
|
||||||
|
//sys hcsShutdownComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsShutdownComputeSystem?
|
||||||
|
//sys hcsTerminateComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsTerminateComputeSystem?
|
||||||
|
//sys hcsPauseComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsPauseComputeSystem?
|
||||||
|
//sys hcsResumeComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsResumeComputeSystem?
|
||||||
|
//sys hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetComputeSystemProperties?
|
||||||
|
//sys hcsModifyComputeSystem(computeSystem hcsSystem, configuration string, result **uint16) (hr error) = vmcompute.HcsModifyComputeSystem?
|
||||||
|
//sys hcsRegisterComputeSystemCallback(computeSystem hcsSystem, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) = vmcompute.HcsRegisterComputeSystemCallback?
|
||||||
|
//sys hcsUnregisterComputeSystemCallback(callbackHandle hcsCallback) (hr error) = vmcompute.HcsUnregisterComputeSystemCallback?
|
||||||
|
|
||||||
|
//sys hcsCreateProcess(computeSystem hcsSystem, processParameters string, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) = vmcompute.HcsCreateProcess?
|
||||||
|
//sys hcsOpenProcess(computeSystem hcsSystem, pid uint32, process *hcsProcess, result **uint16) (hr error) = vmcompute.HcsOpenProcess?
|
||||||
|
//sys hcsCloseProcess(process hcsProcess) (hr error) = vmcompute.HcsCloseProcess?
|
||||||
|
//sys hcsTerminateProcess(process hcsProcess, result **uint16) (hr error) = vmcompute.HcsTerminateProcess?
|
||||||
|
//sys hcsGetProcessInfo(process hcsProcess, processInformation *hcsProcessInformation, result **uint16) (hr error) = vmcompute.HcsGetProcessInfo?
|
||||||
|
//sys hcsGetProcessProperties(process hcsProcess, processProperties **uint16, result **uint16) (hr error) = vmcompute.HcsGetProcessProperties?
|
||||||
|
//sys hcsModifyProcess(process hcsProcess, settings string, result **uint16) (hr error) = vmcompute.HcsModifyProcess?
|
||||||
|
//sys hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetServiceProperties?
|
||||||
|
//sys hcsRegisterProcessCallback(process hcsProcess, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) = vmcompute.HcsRegisterProcessCallback?
|
||||||
|
//sys hcsUnregisterProcessCallback(callbackHandle hcsCallback) (hr error) = vmcompute.HcsUnregisterProcessCallback?
|
||||||
|
|
||||||
|
type hcsSystem syscall.Handle
|
||||||
|
type hcsProcess syscall.Handle
|
||||||
|
type hcsCallback syscall.Handle
|
||||||
|
|
||||||
|
type hcsProcessInformation struct {
|
||||||
|
ProcessId uint32
|
||||||
|
Reserved uint32
|
||||||
|
StdInput syscall.Handle
|
||||||
|
StdOutput syscall.Handle
|
||||||
|
StdError syscall.Handle
|
||||||
|
}
|
||||||
393
vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go
generated
vendored
Normal file
393
vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go
generated
vendored
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
package hcs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/interop"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContainerError is an error encountered in HCS
|
||||||
|
type Process struct {
|
||||||
|
handleLock sync.RWMutex
|
||||||
|
handle hcsProcess
|
||||||
|
processID int
|
||||||
|
system *System
|
||||||
|
cachedPipes *cachedPipes
|
||||||
|
callbackNumber uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type cachedPipes struct {
|
||||||
|
stdIn syscall.Handle
|
||||||
|
stdOut syscall.Handle
|
||||||
|
stdErr syscall.Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
type processModifyRequest struct {
|
||||||
|
Operation string
|
||||||
|
ConsoleSize *consoleSize `json:",omitempty"`
|
||||||
|
CloseHandle *closeHandle `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type consoleSize struct {
|
||||||
|
Height uint16
|
||||||
|
Width uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type closeHandle struct {
|
||||||
|
Handle string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProcessStatus struct {
|
||||||
|
ProcessID uint32
|
||||||
|
Exited bool
|
||||||
|
ExitCode uint32
|
||||||
|
LastWaitResult int32
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
stdIn string = "StdIn"
|
||||||
|
stdOut string = "StdOut"
|
||||||
|
stdErr string = "StdErr"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
modifyConsoleSize string = "ConsoleSize"
|
||||||
|
modifyCloseHandle string = "CloseHandle"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Pid returns the process ID of the process within the container.
|
||||||
|
func (process *Process) Pid() int {
|
||||||
|
return process.processID
|
||||||
|
}
|
||||||
|
|
||||||
|
// SystemID returns the ID of the process's compute system.
|
||||||
|
func (process *Process) SystemID() string {
|
||||||
|
return process.system.ID()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill signals the process to terminate but does not wait for it to finish terminating.
|
||||||
|
func (process *Process) Kill() error {
|
||||||
|
process.handleLock.RLock()
|
||||||
|
defer process.handleLock.RUnlock()
|
||||||
|
operation := "Kill"
|
||||||
|
title := "hcsshim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
if process.handle == 0 {
|
||||||
|
return makeProcessError(process, operation, ErrAlreadyClosed, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
completed := false
|
||||||
|
go syscallWatcher(fmt.Sprintf("TerminateProcess %s: %d", process.SystemID(), process.Pid()), &completed)
|
||||||
|
err := hcsTerminateProcess(process.handle, &resultp)
|
||||||
|
completed = true
|
||||||
|
events := processHcsResult(resultp)
|
||||||
|
if err != nil {
|
||||||
|
return makeProcessError(process, operation, err, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d", process.processID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait waits for the process to exit.
|
||||||
|
func (process *Process) Wait() error {
|
||||||
|
operation := "Wait"
|
||||||
|
title := "hcsshim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
err := waitForNotification(process.callbackNumber, hcsNotificationProcessExited, nil)
|
||||||
|
if err != nil {
|
||||||
|
return makeProcessError(process, operation, err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d", process.processID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitTimeout waits for the process to exit or the duration to elapse. It returns
|
||||||
|
// false if timeout occurs.
|
||||||
|
func (process *Process) WaitTimeout(timeout time.Duration) error {
|
||||||
|
operation := "WaitTimeout"
|
||||||
|
title := "hcsshim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
err := waitForNotification(process.callbackNumber, hcsNotificationProcessExited, &timeout)
|
||||||
|
if err != nil {
|
||||||
|
return makeProcessError(process, operation, err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d", process.processID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizeConsole resizes the console of the process.
|
||||||
|
func (process *Process) ResizeConsole(width, height uint16) error {
|
||||||
|
process.handleLock.RLock()
|
||||||
|
defer process.handleLock.RUnlock()
|
||||||
|
operation := "ResizeConsole"
|
||||||
|
title := "hcsshim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
if process.handle == 0 {
|
||||||
|
return makeProcessError(process, operation, ErrAlreadyClosed, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyRequest := processModifyRequest{
|
||||||
|
Operation: modifyConsoleSize,
|
||||||
|
ConsoleSize: &consoleSize{
|
||||||
|
Height: height,
|
||||||
|
Width: width,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyRequestb, err := json.Marshal(modifyRequest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyRequestStr := string(modifyRequestb)
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp)
|
||||||
|
events := processHcsResult(resultp)
|
||||||
|
if err != nil {
|
||||||
|
return makeProcessError(process, operation, err, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d", process.processID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (process *Process) Properties() (*ProcessStatus, error) {
|
||||||
|
process.handleLock.RLock()
|
||||||
|
defer process.handleLock.RUnlock()
|
||||||
|
operation := "Properties"
|
||||||
|
title := "hcsshim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
if process.handle == 0 {
|
||||||
|
return nil, makeProcessError(process, operation, ErrAlreadyClosed, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
resultp *uint16
|
||||||
|
propertiesp *uint16
|
||||||
|
)
|
||||||
|
completed := false
|
||||||
|
go syscallWatcher(fmt.Sprintf("GetProcessProperties %s: %d", process.SystemID(), process.Pid()), &completed)
|
||||||
|
err := hcsGetProcessProperties(process.handle, &propertiesp, &resultp)
|
||||||
|
completed = true
|
||||||
|
events := processHcsResult(resultp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, makeProcessError(process, operation, err, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
if propertiesp == nil {
|
||||||
|
return nil, ErrUnexpectedValue
|
||||||
|
}
|
||||||
|
propertiesRaw := interop.ConvertAndFreeCoTaskMemBytes(propertiesp)
|
||||||
|
|
||||||
|
properties := &ProcessStatus{}
|
||||||
|
if err := json.Unmarshal(propertiesRaw, properties); err != nil {
|
||||||
|
return nil, makeProcessError(process, operation, err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d, properties=%s", process.processID, propertiesRaw)
|
||||||
|
return properties, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExitCode returns the exit code of the process. The process must have
|
||||||
|
// already terminated.
|
||||||
|
func (process *Process) ExitCode() (int, error) {
|
||||||
|
operation := "ExitCode"
|
||||||
|
properties, err := process.Properties()
|
||||||
|
if err != nil {
|
||||||
|
return 0, makeProcessError(process, operation, err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if properties.Exited == false {
|
||||||
|
return 0, makeProcessError(process, operation, ErrInvalidProcessState, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if properties.LastWaitResult != 0 {
|
||||||
|
return 0, makeProcessError(process, operation, syscall.Errno(properties.LastWaitResult), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(properties.ExitCode), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing
|
||||||
|
// these pipes does not close the underlying pipes; it should be possible to
|
||||||
|
// call this multiple times to get multiple interfaces.
|
||||||
|
func (process *Process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) {
|
||||||
|
process.handleLock.RLock()
|
||||||
|
defer process.handleLock.RUnlock()
|
||||||
|
operation := "Stdio"
|
||||||
|
title := "hcsshim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
if process.handle == 0 {
|
||||||
|
return nil, nil, nil, makeProcessError(process, operation, ErrAlreadyClosed, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdIn, stdOut, stdErr syscall.Handle
|
||||||
|
|
||||||
|
if process.cachedPipes == nil {
|
||||||
|
var (
|
||||||
|
processInfo hcsProcessInformation
|
||||||
|
resultp *uint16
|
||||||
|
)
|
||||||
|
err := hcsGetProcessInfo(process.handle, &processInfo, &resultp)
|
||||||
|
events := processHcsResult(resultp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, makeProcessError(process, operation, err, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
stdIn, stdOut, stdErr = processInfo.StdInput, processInfo.StdOutput, processInfo.StdError
|
||||||
|
} else {
|
||||||
|
// Use cached pipes
|
||||||
|
stdIn, stdOut, stdErr = process.cachedPipes.stdIn, process.cachedPipes.stdOut, process.cachedPipes.stdErr
|
||||||
|
|
||||||
|
// Invalidate the cache
|
||||||
|
process.cachedPipes = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pipes, err := makeOpenFiles([]syscall.Handle{stdIn, stdOut, stdErr})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, makeProcessError(process, operation, err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d", process.processID)
|
||||||
|
return pipes[0], pipes[1], pipes[2], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseStdin closes the write side of the stdin pipe so that the process is
|
||||||
|
// notified on the read side that there is no more data in stdin.
|
||||||
|
func (process *Process) CloseStdin() error {
|
||||||
|
process.handleLock.RLock()
|
||||||
|
defer process.handleLock.RUnlock()
|
||||||
|
operation := "CloseStdin"
|
||||||
|
title := "hcsshim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
if process.handle == 0 {
|
||||||
|
return makeProcessError(process, operation, ErrAlreadyClosed, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyRequest := processModifyRequest{
|
||||||
|
Operation: modifyCloseHandle,
|
||||||
|
CloseHandle: &closeHandle{
|
||||||
|
Handle: stdIn,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyRequestb, err := json.Marshal(modifyRequest)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyRequestStr := string(modifyRequestb)
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp)
|
||||||
|
events := processHcsResult(resultp)
|
||||||
|
if err != nil {
|
||||||
|
return makeProcessError(process, operation, err, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d", process.processID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close cleans up any state associated with the process but does not kill
|
||||||
|
// or wait on it.
|
||||||
|
func (process *Process) Close() error {
|
||||||
|
process.handleLock.Lock()
|
||||||
|
defer process.handleLock.Unlock()
|
||||||
|
operation := "Close"
|
||||||
|
title := "hcsshim::Process::" + operation
|
||||||
|
logrus.Debugf(title+" processid=%d", process.processID)
|
||||||
|
|
||||||
|
// Don't double free this
|
||||||
|
if process.handle == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := process.unregisterCallback(); err != nil {
|
||||||
|
return makeProcessError(process, operation, err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := hcsCloseProcess(process.handle); err != nil {
|
||||||
|
return makeProcessError(process, operation, err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
process.handle = 0
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d", process.processID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (process *Process) registerCallback() error {
|
||||||
|
context := ¬ifcationWatcherContext{
|
||||||
|
channels: newChannels(),
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackMapLock.Lock()
|
||||||
|
callbackNumber := nextCallback
|
||||||
|
nextCallback++
|
||||||
|
callbackMap[callbackNumber] = context
|
||||||
|
callbackMapLock.Unlock()
|
||||||
|
|
||||||
|
var callbackHandle hcsCallback
|
||||||
|
err := hcsRegisterProcessCallback(process.handle, notificationWatcherCallback, callbackNumber, &callbackHandle)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
context.handle = callbackHandle
|
||||||
|
process.callbackNumber = callbackNumber
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (process *Process) unregisterCallback() error {
|
||||||
|
callbackNumber := process.callbackNumber
|
||||||
|
|
||||||
|
callbackMapLock.RLock()
|
||||||
|
context := callbackMap[callbackNumber]
|
||||||
|
callbackMapLock.RUnlock()
|
||||||
|
|
||||||
|
if context == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
handle := context.handle
|
||||||
|
|
||||||
|
if handle == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hcsUnregisterProcessCallback has its own syncronization
|
||||||
|
// to wait for all callbacks to complete. We must NOT hold the callbackMapLock.
|
||||||
|
err := hcsUnregisterProcessCallback(handle)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
closeChannels(context.channels)
|
||||||
|
|
||||||
|
callbackMapLock.Lock()
|
||||||
|
callbackMap[callbackNumber] = nil
|
||||||
|
callbackMapLock.Unlock()
|
||||||
|
|
||||||
|
handle = 0
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
585
vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go
generated
vendored
Normal file
585
vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go
generated
vendored
Normal file
@@ -0,0 +1,585 @@
|
|||||||
|
package hcs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/interop"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/schema1"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/timeout"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// currentContainerStarts is used to limit the number of concurrent container
|
||||||
|
// starts.
|
||||||
|
var currentContainerStarts containerStarts
|
||||||
|
|
||||||
|
type containerStarts struct {
|
||||||
|
maxParallel int
|
||||||
|
inProgress int
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
mpsS := os.Getenv("HCSSHIM_MAX_PARALLEL_START")
|
||||||
|
if len(mpsS) > 0 {
|
||||||
|
mpsI, err := strconv.Atoi(mpsS)
|
||||||
|
if err != nil || mpsI < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentContainerStarts.maxParallel = mpsI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type System struct {
|
||||||
|
handleLock sync.RWMutex
|
||||||
|
handle hcsSystem
|
||||||
|
id string
|
||||||
|
callbackNumber uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateComputeSystem creates a new compute system with the given configuration but does not start it.
|
||||||
|
func CreateComputeSystem(id string, hcsDocumentInterface interface{}) (*System, error) {
|
||||||
|
operation := "CreateComputeSystem"
|
||||||
|
title := "hcsshim::" + operation
|
||||||
|
|
||||||
|
computeSystem := &System{
|
||||||
|
id: id,
|
||||||
|
}
|
||||||
|
|
||||||
|
hcsDocumentB, err := json.Marshal(hcsDocumentInterface)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hcsDocument := string(hcsDocumentB)
|
||||||
|
logrus.Debugf(title+" ID=%s config=%s", id, hcsDocument)
|
||||||
|
|
||||||
|
var (
|
||||||
|
resultp *uint16
|
||||||
|
identity syscall.Handle
|
||||||
|
)
|
||||||
|
completed := false
|
||||||
|
go syscallWatcher(fmt.Sprintf("CreateCompleteSystem %s: %s", id, hcsDocument), &completed)
|
||||||
|
createError := hcsCreateComputeSystem(id, hcsDocument, identity, &computeSystem.handle, &resultp)
|
||||||
|
completed = true
|
||||||
|
|
||||||
|
if createError == nil || IsPending(createError) {
|
||||||
|
if err := computeSystem.registerCallback(); err != nil {
|
||||||
|
// Terminate the compute system if it still exists. We're okay to
|
||||||
|
// ignore a failure here.
|
||||||
|
computeSystem.Terminate()
|
||||||
|
return nil, makeSystemError(computeSystem, operation, "", err, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
events, err := processAsyncHcsResult(createError, resultp, computeSystem.callbackNumber, hcsNotificationSystemCreateCompleted, &timeout.SystemCreate)
|
||||||
|
if err != nil {
|
||||||
|
if err == ErrTimeout {
|
||||||
|
// Terminate the compute system if it still exists. We're okay to
|
||||||
|
// ignore a failure here.
|
||||||
|
computeSystem.Terminate()
|
||||||
|
}
|
||||||
|
return nil, makeSystemError(computeSystem, operation, hcsDocument, err, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s handle=%d", id, computeSystem.handle)
|
||||||
|
return computeSystem, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenComputeSystem opens an existing compute system by ID.
|
||||||
|
func OpenComputeSystem(id string) (*System, error) {
|
||||||
|
operation := "OpenComputeSystem"
|
||||||
|
title := "hcsshim::" + operation
|
||||||
|
logrus.Debugf(title+" ID=%s", id)
|
||||||
|
|
||||||
|
computeSystem := &System{
|
||||||
|
id: id,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
handle hcsSystem
|
||||||
|
resultp *uint16
|
||||||
|
)
|
||||||
|
err := hcsOpenComputeSystem(id, &handle, &resultp)
|
||||||
|
events := processHcsResult(resultp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, makeSystemError(computeSystem, operation, "", err, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
computeSystem.handle = handle
|
||||||
|
|
||||||
|
if err := computeSystem.registerCallback(); err != nil {
|
||||||
|
return nil, makeSystemError(computeSystem, operation, "", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded id=%s handle=%d", id, handle)
|
||||||
|
return computeSystem, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetComputeSystems gets a list of the compute systems on the system that match the query
|
||||||
|
func GetComputeSystems(q schema1.ComputeSystemQuery) ([]schema1.ContainerProperties, error) {
|
||||||
|
operation := "GetComputeSystems"
|
||||||
|
title := "hcsshim::" + operation
|
||||||
|
|
||||||
|
queryb, err := json.Marshal(q)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
query := string(queryb)
|
||||||
|
logrus.Debugf(title+" query=%s", query)
|
||||||
|
|
||||||
|
var (
|
||||||
|
resultp *uint16
|
||||||
|
computeSystemsp *uint16
|
||||||
|
)
|
||||||
|
completed := false
|
||||||
|
go syscallWatcher(fmt.Sprintf("GetComputeSystems %s:", query), &completed)
|
||||||
|
err = hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp)
|
||||||
|
completed = true
|
||||||
|
events := processHcsResult(resultp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, &HcsError{Op: operation, Err: err, Events: events}
|
||||||
|
}
|
||||||
|
|
||||||
|
if computeSystemsp == nil {
|
||||||
|
return nil, ErrUnexpectedValue
|
||||||
|
}
|
||||||
|
computeSystemsRaw := interop.ConvertAndFreeCoTaskMemBytes(computeSystemsp)
|
||||||
|
computeSystems := []schema1.ContainerProperties{}
|
||||||
|
if err := json.Unmarshal(computeSystemsRaw, &computeSystems); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title + " succeeded")
|
||||||
|
return computeSystems, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start synchronously starts the computeSystem.
|
||||||
|
func (computeSystem *System) Start() error {
|
||||||
|
computeSystem.handleLock.RLock()
|
||||||
|
defer computeSystem.handleLock.RUnlock()
|
||||||
|
title := "hcsshim::ComputeSystem::Start ID=" + computeSystem.ID()
|
||||||
|
logrus.Debugf(title)
|
||||||
|
|
||||||
|
if computeSystem.handle == 0 {
|
||||||
|
return makeSystemError(computeSystem, "Start", "", ErrAlreadyClosed, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a very simple backoff-retry loop to limit the number
|
||||||
|
// of parallel container starts if environment variable
|
||||||
|
// HCSSHIM_MAX_PARALLEL_START is set to a positive integer.
|
||||||
|
// It should generally only be used as a workaround to various
|
||||||
|
// platform issues that exist between RS1 and RS4 as of Aug 2018
|
||||||
|
if currentContainerStarts.maxParallel > 0 {
|
||||||
|
for {
|
||||||
|
currentContainerStarts.Lock()
|
||||||
|
if currentContainerStarts.inProgress < currentContainerStarts.maxParallel {
|
||||||
|
currentContainerStarts.inProgress++
|
||||||
|
currentContainerStarts.Unlock()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if currentContainerStarts.inProgress == currentContainerStarts.maxParallel {
|
||||||
|
currentContainerStarts.Unlock()
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Make sure we decrement the count when we are done.
|
||||||
|
defer func() {
|
||||||
|
currentContainerStarts.Lock()
|
||||||
|
currentContainerStarts.inProgress--
|
||||||
|
currentContainerStarts.Unlock()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
completed := false
|
||||||
|
go syscallWatcher(fmt.Sprintf("StartComputeSystem %s:", computeSystem.ID()), &completed)
|
||||||
|
err := hcsStartComputeSystem(computeSystem.handle, "", &resultp)
|
||||||
|
completed = true
|
||||||
|
events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemStartCompleted, &timeout.SystemStart)
|
||||||
|
if err != nil {
|
||||||
|
return makeSystemError(computeSystem, "Start", "", err, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title + " succeeded")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID returns the compute system's identifier.
|
||||||
|
func (computeSystem *System) ID() string {
|
||||||
|
return computeSystem.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown requests a compute system shutdown, if IsPending() on the error returned is true,
|
||||||
|
// it may not actually be shut down until Wait() succeeds.
|
||||||
|
func (computeSystem *System) Shutdown() error {
|
||||||
|
computeSystem.handleLock.RLock()
|
||||||
|
defer computeSystem.handleLock.RUnlock()
|
||||||
|
title := "hcsshim::ComputeSystem::Shutdown"
|
||||||
|
logrus.Debugf(title)
|
||||||
|
if computeSystem.handle == 0 {
|
||||||
|
return makeSystemError(computeSystem, "Shutdown", "", ErrAlreadyClosed, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
completed := false
|
||||||
|
go syscallWatcher(fmt.Sprintf("ShutdownComputeSystem %s:", computeSystem.ID()), &completed)
|
||||||
|
err := hcsShutdownComputeSystem(computeSystem.handle, "", &resultp)
|
||||||
|
completed = true
|
||||||
|
events := processHcsResult(resultp)
|
||||||
|
if err != nil {
|
||||||
|
return makeSystemError(computeSystem, "Shutdown", "", err, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title + " succeeded")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terminate requests a compute system terminate, if IsPending() on the error returned is true,
|
||||||
|
// it may not actually be shut down until Wait() succeeds.
|
||||||
|
func (computeSystem *System) Terminate() error {
|
||||||
|
computeSystem.handleLock.RLock()
|
||||||
|
defer computeSystem.handleLock.RUnlock()
|
||||||
|
title := "hcsshim::ComputeSystem::Terminate ID=" + computeSystem.ID()
|
||||||
|
logrus.Debugf(title)
|
||||||
|
|
||||||
|
if computeSystem.handle == 0 {
|
||||||
|
return makeSystemError(computeSystem, "Terminate", "", ErrAlreadyClosed, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
completed := false
|
||||||
|
go syscallWatcher(fmt.Sprintf("TerminateComputeSystem %s:", computeSystem.ID()), &completed)
|
||||||
|
err := hcsTerminateComputeSystem(computeSystem.handle, "", &resultp)
|
||||||
|
completed = true
|
||||||
|
events := processHcsResult(resultp)
|
||||||
|
if err != nil {
|
||||||
|
return makeSystemError(computeSystem, "Terminate", "", err, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title + " succeeded")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait synchronously waits for the compute system to shutdown or terminate.
|
||||||
|
func (computeSystem *System) Wait() error {
|
||||||
|
title := "hcsshim::ComputeSystem::Wait ID=" + computeSystem.ID()
|
||||||
|
logrus.Debugf(title)
|
||||||
|
|
||||||
|
err := waitForNotification(computeSystem.callbackNumber, hcsNotificationSystemExited, nil)
|
||||||
|
if err != nil {
|
||||||
|
return makeSystemError(computeSystem, "Wait", "", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title + " succeeded")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitTimeout synchronously waits for the compute system to terminate or the duration to elapse.
|
||||||
|
// If the timeout expires, IsTimeout(err) == true
|
||||||
|
func (computeSystem *System) WaitTimeout(timeout time.Duration) error {
|
||||||
|
title := "hcsshim::ComputeSystem::WaitTimeout ID=" + computeSystem.ID()
|
||||||
|
logrus.Debugf(title)
|
||||||
|
|
||||||
|
err := waitForNotification(computeSystem.callbackNumber, hcsNotificationSystemExited, &timeout)
|
||||||
|
if err != nil {
|
||||||
|
return makeSystemError(computeSystem, "WaitTimeout", "", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title + " succeeded")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (computeSystem *System) Properties(types ...schema1.PropertyType) (*schema1.ContainerProperties, error) {
|
||||||
|
computeSystem.handleLock.RLock()
|
||||||
|
defer computeSystem.handleLock.RUnlock()
|
||||||
|
|
||||||
|
queryj, err := json.Marshal(schema1.PropertyQuery{types})
|
||||||
|
if err != nil {
|
||||||
|
return nil, makeSystemError(computeSystem, "Properties", "", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultp, propertiesp *uint16
|
||||||
|
completed := false
|
||||||
|
go syscallWatcher(fmt.Sprintf("GetComputeSystemProperties %s:", computeSystem.ID()), &completed)
|
||||||
|
err = hcsGetComputeSystemProperties(computeSystem.handle, string(queryj), &propertiesp, &resultp)
|
||||||
|
completed = true
|
||||||
|
events := processHcsResult(resultp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, makeSystemError(computeSystem, "Properties", "", err, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
if propertiesp == nil {
|
||||||
|
return nil, ErrUnexpectedValue
|
||||||
|
}
|
||||||
|
propertiesRaw := interop.ConvertAndFreeCoTaskMemBytes(propertiesp)
|
||||||
|
properties := &schema1.ContainerProperties{}
|
||||||
|
if err := json.Unmarshal(propertiesRaw, properties); err != nil {
|
||||||
|
return nil, makeSystemError(computeSystem, "Properties", "", err, nil)
|
||||||
|
}
|
||||||
|
return properties, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause pauses the execution of the computeSystem. This feature is not enabled in TP5.
|
||||||
|
func (computeSystem *System) Pause() error {
|
||||||
|
computeSystem.handleLock.RLock()
|
||||||
|
defer computeSystem.handleLock.RUnlock()
|
||||||
|
title := "hcsshim::ComputeSystem::Pause ID=" + computeSystem.ID()
|
||||||
|
logrus.Debugf(title)
|
||||||
|
|
||||||
|
if computeSystem.handle == 0 {
|
||||||
|
return makeSystemError(computeSystem, "Pause", "", ErrAlreadyClosed, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
completed := false
|
||||||
|
go syscallWatcher(fmt.Sprintf("PauseComputeSystem %s:", computeSystem.ID()), &completed)
|
||||||
|
err := hcsPauseComputeSystem(computeSystem.handle, "", &resultp)
|
||||||
|
completed = true
|
||||||
|
events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemPauseCompleted, &timeout.SystemPause)
|
||||||
|
if err != nil {
|
||||||
|
return makeSystemError(computeSystem, "Pause", "", err, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title + " succeeded")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume resumes the execution of the computeSystem. This feature is not enabled in TP5.
|
||||||
|
func (computeSystem *System) Resume() error {
|
||||||
|
computeSystem.handleLock.RLock()
|
||||||
|
defer computeSystem.handleLock.RUnlock()
|
||||||
|
title := "hcsshim::ComputeSystem::Resume ID=" + computeSystem.ID()
|
||||||
|
logrus.Debugf(title)
|
||||||
|
|
||||||
|
if computeSystem.handle == 0 {
|
||||||
|
return makeSystemError(computeSystem, "Resume", "", ErrAlreadyClosed, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
completed := false
|
||||||
|
go syscallWatcher(fmt.Sprintf("ResumeComputeSystem %s:", computeSystem.ID()), &completed)
|
||||||
|
err := hcsResumeComputeSystem(computeSystem.handle, "", &resultp)
|
||||||
|
completed = true
|
||||||
|
events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemResumeCompleted, &timeout.SystemResume)
|
||||||
|
if err != nil {
|
||||||
|
return makeSystemError(computeSystem, "Resume", "", err, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title + " succeeded")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateProcess launches a new process within the computeSystem.
|
||||||
|
func (computeSystem *System) CreateProcess(c interface{}) (*Process, error) {
|
||||||
|
computeSystem.handleLock.RLock()
|
||||||
|
defer computeSystem.handleLock.RUnlock()
|
||||||
|
title := "hcsshim::ComputeSystem::CreateProcess ID=" + computeSystem.ID()
|
||||||
|
var (
|
||||||
|
processInfo hcsProcessInformation
|
||||||
|
processHandle hcsProcess
|
||||||
|
resultp *uint16
|
||||||
|
)
|
||||||
|
|
||||||
|
if computeSystem.handle == 0 {
|
||||||
|
return nil, makeSystemError(computeSystem, "CreateProcess", "", ErrAlreadyClosed, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
configurationb, err := json.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, makeSystemError(computeSystem, "CreateProcess", "", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
configuration := string(configurationb)
|
||||||
|
logrus.Debugf(title+" config=%s", configuration)
|
||||||
|
|
||||||
|
completed := false
|
||||||
|
go syscallWatcher(fmt.Sprintf("CreateProcess %s: %s", computeSystem.ID(), configuration), &completed)
|
||||||
|
err = hcsCreateProcess(computeSystem.handle, configuration, &processInfo, &processHandle, &resultp)
|
||||||
|
completed = true
|
||||||
|
events := processHcsResult(resultp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, makeSystemError(computeSystem, "CreateProcess", configuration, err, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
process := &Process{
|
||||||
|
handle: processHandle,
|
||||||
|
processID: int(processInfo.ProcessId),
|
||||||
|
system: computeSystem,
|
||||||
|
cachedPipes: &cachedPipes{
|
||||||
|
stdIn: processInfo.StdInput,
|
||||||
|
stdOut: processInfo.StdOutput,
|
||||||
|
stdErr: processInfo.StdError,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := process.registerCallback(); err != nil {
|
||||||
|
return nil, makeSystemError(computeSystem, "CreateProcess", "", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%d", process.processID)
|
||||||
|
return process, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenProcess gets an interface to an existing process within the computeSystem.
|
||||||
|
func (computeSystem *System) OpenProcess(pid int) (*Process, error) {
|
||||||
|
computeSystem.handleLock.RLock()
|
||||||
|
defer computeSystem.handleLock.RUnlock()
|
||||||
|
title := "hcsshim::ComputeSystem::OpenProcess ID=" + computeSystem.ID()
|
||||||
|
logrus.Debugf(title+" processid=%d", pid)
|
||||||
|
var (
|
||||||
|
processHandle hcsProcess
|
||||||
|
resultp *uint16
|
||||||
|
)
|
||||||
|
|
||||||
|
if computeSystem.handle == 0 {
|
||||||
|
return nil, makeSystemError(computeSystem, "OpenProcess", "", ErrAlreadyClosed, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
completed := false
|
||||||
|
go syscallWatcher(fmt.Sprintf("OpenProcess %s: %d", computeSystem.ID(), pid), &completed)
|
||||||
|
err := hcsOpenProcess(computeSystem.handle, uint32(pid), &processHandle, &resultp)
|
||||||
|
completed = true
|
||||||
|
events := processHcsResult(resultp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, makeSystemError(computeSystem, "OpenProcess", "", err, events)
|
||||||
|
}
|
||||||
|
|
||||||
|
process := &Process{
|
||||||
|
handle: processHandle,
|
||||||
|
processID: pid,
|
||||||
|
system: computeSystem,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := process.registerCallback(); err != nil {
|
||||||
|
return nil, makeSystemError(computeSystem, "OpenProcess", "", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" succeeded processid=%s", process.processID)
|
||||||
|
return process, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close cleans up any state associated with the compute system but does not terminate or wait for it.
|
||||||
|
func (computeSystem *System) Close() error {
|
||||||
|
computeSystem.handleLock.Lock()
|
||||||
|
defer computeSystem.handleLock.Unlock()
|
||||||
|
title := "hcsshim::ComputeSystem::Close ID=" + computeSystem.ID()
|
||||||
|
logrus.Debugf(title)
|
||||||
|
|
||||||
|
// Don't double free this
|
||||||
|
if computeSystem.handle == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := computeSystem.unregisterCallback(); err != nil {
|
||||||
|
return makeSystemError(computeSystem, "Close", "", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
completed := false
|
||||||
|
go syscallWatcher(fmt.Sprintf("CloseComputeSystem %s:", computeSystem.ID()), &completed)
|
||||||
|
err := hcsCloseComputeSystem(computeSystem.handle)
|
||||||
|
completed = true
|
||||||
|
if err != nil {
|
||||||
|
return makeSystemError(computeSystem, "Close", "", err, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
computeSystem.handle = 0
|
||||||
|
|
||||||
|
logrus.Debugf(title + " succeeded")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (computeSystem *System) registerCallback() error {
|
||||||
|
context := ¬ifcationWatcherContext{
|
||||||
|
channels: newChannels(),
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackMapLock.Lock()
|
||||||
|
callbackNumber := nextCallback
|
||||||
|
nextCallback++
|
||||||
|
callbackMap[callbackNumber] = context
|
||||||
|
callbackMapLock.Unlock()
|
||||||
|
|
||||||
|
var callbackHandle hcsCallback
|
||||||
|
err := hcsRegisterComputeSystemCallback(computeSystem.handle, notificationWatcherCallback, callbackNumber, &callbackHandle)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
context.handle = callbackHandle
|
||||||
|
computeSystem.callbackNumber = callbackNumber
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (computeSystem *System) unregisterCallback() error {
|
||||||
|
callbackNumber := computeSystem.callbackNumber
|
||||||
|
|
||||||
|
callbackMapLock.RLock()
|
||||||
|
context := callbackMap[callbackNumber]
|
||||||
|
callbackMapLock.RUnlock()
|
||||||
|
|
||||||
|
if context == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
handle := context.handle
|
||||||
|
|
||||||
|
if handle == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hcsUnregisterComputeSystemCallback has its own syncronization
|
||||||
|
// to wait for all callbacks to complete. We must NOT hold the callbackMapLock.
|
||||||
|
err := hcsUnregisterComputeSystemCallback(handle)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
closeChannels(context.channels)
|
||||||
|
|
||||||
|
callbackMapLock.Lock()
|
||||||
|
callbackMap[callbackNumber] = nil
|
||||||
|
callbackMapLock.Unlock()
|
||||||
|
|
||||||
|
handle = 0
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modifies the System by sending a request to HCS
|
||||||
|
func (computeSystem *System) Modify(config interface{}) error {
|
||||||
|
computeSystem.handleLock.RLock()
|
||||||
|
defer computeSystem.handleLock.RUnlock()
|
||||||
|
title := "hcsshim::Modify ID=" + computeSystem.id
|
||||||
|
|
||||||
|
if computeSystem.handle == 0 {
|
||||||
|
return makeSystemError(computeSystem, "Modify", "", ErrAlreadyClosed, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
requestJSON, err := json.Marshal(config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
requestString := string(requestJSON)
|
||||||
|
logrus.Debugf(title + " " + requestString)
|
||||||
|
|
||||||
|
var resultp *uint16
|
||||||
|
completed := false
|
||||||
|
go syscallWatcher(fmt.Sprintf("ModifyComputeSystem %s: %s", computeSystem.ID(), requestString), &completed)
|
||||||
|
err = hcsModifyComputeSystem(computeSystem.handle, requestString, &resultp)
|
||||||
|
completed = true
|
||||||
|
events := processHcsResult(resultp)
|
||||||
|
if err != nil {
|
||||||
|
return makeSystemError(computeSystem, "Modify", requestString, err, events)
|
||||||
|
}
|
||||||
|
logrus.Debugf(title + " succeeded ")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
33
vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go
generated
vendored
Normal file
33
vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package hcs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
|
)
|
||||||
|
|
||||||
|
// makeOpenFiles calls winio.MakeOpenFile for each handle in a slice but closes all the handles
|
||||||
|
// if there is an error.
|
||||||
|
func makeOpenFiles(hs []syscall.Handle) (_ []io.ReadWriteCloser, err error) {
|
||||||
|
fs := make([]io.ReadWriteCloser, len(hs))
|
||||||
|
for i, h := range hs {
|
||||||
|
if h != syscall.Handle(0) {
|
||||||
|
if err == nil {
|
||||||
|
fs[i], err = winio.MakeOpenFile(h)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
syscall.Close(h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
for _, f := range fs {
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return fs, nil
|
||||||
|
}
|
||||||
63
vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go
generated
vendored
Normal file
63
vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package hcs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func processAsyncHcsResult(err error, resultp *uint16, callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) ([]ErrorEvent, error) {
|
||||||
|
events := processHcsResult(resultp)
|
||||||
|
if IsPending(err) {
|
||||||
|
return nil, waitForNotification(callbackNumber, expectedNotification, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
return events, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForNotification(callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) error {
|
||||||
|
callbackMapLock.RLock()
|
||||||
|
channels := callbackMap[callbackNumber].channels
|
||||||
|
callbackMapLock.RUnlock()
|
||||||
|
|
||||||
|
expectedChannel := channels[expectedNotification]
|
||||||
|
if expectedChannel == nil {
|
||||||
|
logrus.Errorf("unknown notification type in waitForNotification %x", expectedNotification)
|
||||||
|
return ErrInvalidNotificationType
|
||||||
|
}
|
||||||
|
|
||||||
|
var c <-chan time.Time
|
||||||
|
if timeout != nil {
|
||||||
|
timer := time.NewTimer(*timeout)
|
||||||
|
c = timer.C
|
||||||
|
defer timer.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err, ok := <-expectedChannel:
|
||||||
|
if !ok {
|
||||||
|
return ErrHandleClose
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
case err, ok := <-channels[hcsNotificationSystemExited]:
|
||||||
|
if !ok {
|
||||||
|
return ErrHandleClose
|
||||||
|
}
|
||||||
|
// If the expected notification is hcsNotificationSystemExited which of the two selects
|
||||||
|
// chosen is random. Return the raw error if hcsNotificationSystemExited is expected
|
||||||
|
if channels[hcsNotificationSystemExited] == expectedChannel {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ErrUnexpectedContainerExit
|
||||||
|
case _, ok := <-channels[hcsNotificationServiceDisconnect]:
|
||||||
|
if !ok {
|
||||||
|
return ErrHandleClose
|
||||||
|
}
|
||||||
|
// hcsNotificationServiceDisconnect should never be an expected notification
|
||||||
|
// it does not need the same handling as hcsNotificationSystemExited
|
||||||
|
return ErrUnexpectedProcessAbort
|
||||||
|
case <-c:
|
||||||
|
return ErrTimeout
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
30
vendor/github.com/Microsoft/hcsshim/internal/hcs/watcher.go
generated
vendored
Normal file
30
vendor/github.com/Microsoft/hcsshim/internal/hcs/watcher.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package hcs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/timeout"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// syscallWatcher is used as a very simple goroutine around calls into
|
||||||
|
// the platform. In some cases, we have seen HCS APIs not returning due to
|
||||||
|
// various bugs, and the goroutine making the syscall ends up not returning,
|
||||||
|
// prior to its async callback. By spinning up a syscallWatcher, it allows
|
||||||
|
// us to at least log a warning if a syscall doesn't complete in a reasonable
|
||||||
|
// amount of time.
|
||||||
|
//
|
||||||
|
// Usage is:
|
||||||
|
//
|
||||||
|
// completed := false
|
||||||
|
// go syscallWatcher("some description", &completed)
|
||||||
|
// <syscall>
|
||||||
|
// completed = true
|
||||||
|
//
|
||||||
|
func syscallWatcher(description string, syscallCompleted *bool) {
|
||||||
|
time.Sleep(timeout.SyscallWatcher)
|
||||||
|
if *syscallCompleted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logrus.Warnf("%s: Did not complete within %s. This may indicate a platform issue. If it appears to be making no forward progress, obtain the stacks and see is there is a syscall stuck in the platform API for a significant length of time.", description, timeout.SyscallWatcher)
|
||||||
|
}
|
||||||
441
vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go
generated
vendored
Normal file
441
vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go
generated
vendored
Normal file
@@ -0,0 +1,441 @@
|
|||||||
|
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||||
|
|
||||||
|
package hcs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/interop"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
// TODO: add more here, after collecting data on the common
|
||||||
|
// error values see on Windows. (perhaps when running
|
||||||
|
// all.bat?)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modvmcompute = windows.NewLazySystemDLL("vmcompute.dll")
|
||||||
|
|
||||||
|
procHcsEnumerateComputeSystems = modvmcompute.NewProc("HcsEnumerateComputeSystems")
|
||||||
|
procHcsCreateComputeSystem = modvmcompute.NewProc("HcsCreateComputeSystem")
|
||||||
|
procHcsOpenComputeSystem = modvmcompute.NewProc("HcsOpenComputeSystem")
|
||||||
|
procHcsCloseComputeSystem = modvmcompute.NewProc("HcsCloseComputeSystem")
|
||||||
|
procHcsStartComputeSystem = modvmcompute.NewProc("HcsStartComputeSystem")
|
||||||
|
procHcsShutdownComputeSystem = modvmcompute.NewProc("HcsShutdownComputeSystem")
|
||||||
|
procHcsTerminateComputeSystem = modvmcompute.NewProc("HcsTerminateComputeSystem")
|
||||||
|
procHcsPauseComputeSystem = modvmcompute.NewProc("HcsPauseComputeSystem")
|
||||||
|
procHcsResumeComputeSystem = modvmcompute.NewProc("HcsResumeComputeSystem")
|
||||||
|
procHcsGetComputeSystemProperties = modvmcompute.NewProc("HcsGetComputeSystemProperties")
|
||||||
|
procHcsModifyComputeSystem = modvmcompute.NewProc("HcsModifyComputeSystem")
|
||||||
|
procHcsRegisterComputeSystemCallback = modvmcompute.NewProc("HcsRegisterComputeSystemCallback")
|
||||||
|
procHcsUnregisterComputeSystemCallback = modvmcompute.NewProc("HcsUnregisterComputeSystemCallback")
|
||||||
|
procHcsCreateProcess = modvmcompute.NewProc("HcsCreateProcess")
|
||||||
|
procHcsOpenProcess = modvmcompute.NewProc("HcsOpenProcess")
|
||||||
|
procHcsCloseProcess = modvmcompute.NewProc("HcsCloseProcess")
|
||||||
|
procHcsTerminateProcess = modvmcompute.NewProc("HcsTerminateProcess")
|
||||||
|
procHcsGetProcessInfo = modvmcompute.NewProc("HcsGetProcessInfo")
|
||||||
|
procHcsGetProcessProperties = modvmcompute.NewProc("HcsGetProcessProperties")
|
||||||
|
procHcsModifyProcess = modvmcompute.NewProc("HcsModifyProcess")
|
||||||
|
procHcsGetServiceProperties = modvmcompute.NewProc("HcsGetServiceProperties")
|
||||||
|
procHcsRegisterProcessCallback = modvmcompute.NewProc("HcsRegisterProcessCallback")
|
||||||
|
procHcsUnregisterProcessCallback = modvmcompute.NewProc("HcsUnregisterProcessCallback")
|
||||||
|
)
|
||||||
|
|
||||||
|
func hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, hr = syscall.UTF16PtrFromString(query)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _hcsEnumerateComputeSystems(_p0, computeSystems, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _hcsEnumerateComputeSystems(query *uint16, computeSystems **uint16, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsEnumerateComputeSystems.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsEnumerateComputeSystems.Addr(), 3, uintptr(unsafe.Pointer(query)), uintptr(unsafe.Pointer(computeSystems)), uintptr(unsafe.Pointer(result)))
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, hr = syscall.UTF16PtrFromString(id)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *uint16
|
||||||
|
_p1, hr = syscall.UTF16PtrFromString(configuration)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _hcsCreateComputeSystem(_p0, _p1, identity, computeSystem, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _hcsCreateComputeSystem(id *uint16, configuration *uint16, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsCreateComputeSystem.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall6(procHcsCreateComputeSystem.Addr(), 5, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(configuration)), uintptr(identity), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result)), 0)
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsOpenComputeSystem(id string, computeSystem *hcsSystem, result **uint16) (hr error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, hr = syscall.UTF16PtrFromString(id)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _hcsOpenComputeSystem(_p0, computeSystem, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _hcsOpenComputeSystem(id *uint16, computeSystem *hcsSystem, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsOpenComputeSystem.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsOpenComputeSystem.Addr(), 3, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result)))
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsCloseComputeSystem(computeSystem hcsSystem) (hr error) {
|
||||||
|
if hr = procHcsCloseComputeSystem.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsCloseComputeSystem.Addr(), 1, uintptr(computeSystem), 0, 0)
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsStartComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, hr = syscall.UTF16PtrFromString(options)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _hcsStartComputeSystem(computeSystem, _p0, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _hcsStartComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsStartComputeSystem.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsStartComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result)))
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsShutdownComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, hr = syscall.UTF16PtrFromString(options)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _hcsShutdownComputeSystem(computeSystem, _p0, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _hcsShutdownComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsShutdownComputeSystem.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsShutdownComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result)))
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsTerminateComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, hr = syscall.UTF16PtrFromString(options)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _hcsTerminateComputeSystem(computeSystem, _p0, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _hcsTerminateComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsTerminateComputeSystem.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsTerminateComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result)))
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsPauseComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, hr = syscall.UTF16PtrFromString(options)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _hcsPauseComputeSystem(computeSystem, _p0, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _hcsPauseComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsPauseComputeSystem.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsPauseComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result)))
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsResumeComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, hr = syscall.UTF16PtrFromString(options)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _hcsResumeComputeSystem(computeSystem, _p0, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _hcsResumeComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsResumeComputeSystem.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsResumeComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result)))
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, hr = syscall.UTF16PtrFromString(propertyQuery)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _hcsGetComputeSystemProperties(computeSystem, _p0, properties, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery *uint16, properties **uint16, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsGetComputeSystemProperties.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall6(procHcsGetComputeSystemProperties.Addr(), 4, uintptr(computeSystem), uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result)), 0, 0)
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsModifyComputeSystem(computeSystem hcsSystem, configuration string, result **uint16) (hr error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, hr = syscall.UTF16PtrFromString(configuration)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _hcsModifyComputeSystem(computeSystem, _p0, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _hcsModifyComputeSystem(computeSystem hcsSystem, configuration *uint16, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsModifyComputeSystem.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsModifyComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(configuration)), uintptr(unsafe.Pointer(result)))
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsRegisterComputeSystemCallback(computeSystem hcsSystem, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) {
|
||||||
|
if hr = procHcsRegisterComputeSystemCallback.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall6(procHcsRegisterComputeSystemCallback.Addr(), 4, uintptr(computeSystem), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0)
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsUnregisterComputeSystemCallback(callbackHandle hcsCallback) (hr error) {
|
||||||
|
if hr = procHcsUnregisterComputeSystemCallback.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsUnregisterComputeSystemCallback.Addr(), 1, uintptr(callbackHandle), 0, 0)
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsCreateProcess(computeSystem hcsSystem, processParameters string, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, hr = syscall.UTF16PtrFromString(processParameters)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _hcsCreateProcess(computeSystem, _p0, processInformation, process, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _hcsCreateProcess(computeSystem hcsSystem, processParameters *uint16, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsCreateProcess.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall6(procHcsCreateProcess.Addr(), 5, uintptr(computeSystem), uintptr(unsafe.Pointer(processParameters)), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result)), 0)
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsOpenProcess(computeSystem hcsSystem, pid uint32, process *hcsProcess, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsOpenProcess.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall6(procHcsOpenProcess.Addr(), 4, uintptr(computeSystem), uintptr(pid), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result)), 0, 0)
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsCloseProcess(process hcsProcess) (hr error) {
|
||||||
|
if hr = procHcsCloseProcess.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsCloseProcess.Addr(), 1, uintptr(process), 0, 0)
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsTerminateProcess(process hcsProcess, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsTerminateProcess.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsTerminateProcess.Addr(), 2, uintptr(process), uintptr(unsafe.Pointer(result)), 0)
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsGetProcessInfo(process hcsProcess, processInformation *hcsProcessInformation, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsGetProcessInfo.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsGetProcessInfo.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(result)))
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsGetProcessProperties(process hcsProcess, processProperties **uint16, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsGetProcessProperties.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsGetProcessProperties.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processProperties)), uintptr(unsafe.Pointer(result)))
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsModifyProcess(process hcsProcess, settings string, result **uint16) (hr error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, hr = syscall.UTF16PtrFromString(settings)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _hcsModifyProcess(process, _p0, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _hcsModifyProcess(process hcsProcess, settings *uint16, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsModifyProcess.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsModifyProcess.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result)))
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, hr = syscall.UTF16PtrFromString(propertyQuery)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return _hcsGetServiceProperties(_p0, properties, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _hcsGetServiceProperties(propertyQuery *uint16, properties **uint16, result **uint16) (hr error) {
|
||||||
|
if hr = procHcsGetServiceProperties.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsGetServiceProperties.Addr(), 3, uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result)))
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsRegisterProcessCallback(process hcsProcess, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) {
|
||||||
|
if hr = procHcsRegisterProcessCallback.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall6(procHcsRegisterProcessCallback.Addr(), 4, uintptr(process), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0)
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func hcsUnregisterProcessCallback(callbackHandle hcsCallback) (hr error) {
|
||||||
|
if hr = procHcsUnregisterProcessCallback.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall(procHcsUnregisterProcessCallback.Addr(), 1, uintptr(callbackHandle), 0, 0)
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
51
vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go
generated
vendored
Normal file
51
vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package hcserror
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ERROR_GEN_FAILURE = syscall.Errno(31)
|
||||||
|
|
||||||
|
type HcsError struct {
|
||||||
|
title string
|
||||||
|
rest string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HcsError) Error() string {
|
||||||
|
s := e.title
|
||||||
|
if len(s) > 0 && s[len(s)-1] != ' ' {
|
||||||
|
s += " "
|
||||||
|
}
|
||||||
|
s += fmt.Sprintf("failed in Win32: %s (0x%x)", e.Err, Win32FromError(e.Err))
|
||||||
|
if e.rest != "" {
|
||||||
|
if e.rest[0] != ' ' {
|
||||||
|
s += " "
|
||||||
|
}
|
||||||
|
s += e.rest
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(err error, title, rest string) error {
|
||||||
|
// Pass through DLL errors directly since they do not originate from HCS.
|
||||||
|
if _, ok := err.(*syscall.DLLError); ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return &HcsError{title, rest, err}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Errorf(err error, title, format string, a ...interface{}) error {
|
||||||
|
return New(err, title, fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Win32FromError(err error) uint32 {
|
||||||
|
if herr, ok := err.(*HcsError); ok {
|
||||||
|
return Win32FromError(herr.Err)
|
||||||
|
}
|
||||||
|
if code, ok := err.(syscall.Errno); ok {
|
||||||
|
return uint32(code)
|
||||||
|
}
|
||||||
|
return uint32(ERROR_GEN_FAILURE)
|
||||||
|
}
|
||||||
23
vendor/github.com/Microsoft/hcsshim/internal/hns/hns.go
generated
vendored
Normal file
23
vendor/github.com/Microsoft/hcsshim/internal/hns/hns.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package hns
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go hns.go
|
||||||
|
|
||||||
|
//sys _hnsCall(method string, path string, object string, response **uint16) (hr error) = vmcompute.HNSCall?
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
260
vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go
generated
vendored
Normal file
260
vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go
generated
vendored
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
package hns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HNSEndpoint represents a network endpoint in HNS
|
||||||
|
type HNSEndpoint struct {
|
||||||
|
Id string `json:"ID,omitempty"`
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
VirtualNetwork string `json:",omitempty"`
|
||||||
|
VirtualNetworkName string `json:",omitempty"`
|
||||||
|
Policies []json.RawMessage `json:",omitempty"`
|
||||||
|
MacAddress string `json:",omitempty"`
|
||||||
|
IPAddress net.IP `json:",omitempty"`
|
||||||
|
DNSSuffix string `json:",omitempty"`
|
||||||
|
DNSServerList string `json:",omitempty"`
|
||||||
|
GatewayAddress string `json:",omitempty"`
|
||||||
|
EnableInternalDNS bool `json:",omitempty"`
|
||||||
|
DisableICC bool `json:",omitempty"`
|
||||||
|
PrefixLength uint8 `json:",omitempty"`
|
||||||
|
IsRemoteEndpoint bool `json:",omitempty"`
|
||||||
|
Namespace *Namespace `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//SystemType represents the type of the system on which actions are done
|
||||||
|
type SystemType string
|
||||||
|
|
||||||
|
// SystemType const
|
||||||
|
const (
|
||||||
|
ContainerType SystemType = "Container"
|
||||||
|
VirtualMachineType SystemType = "VirtualMachine"
|
||||||
|
HostType SystemType = "Host"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
type EndpointAttachDetachRequest struct {
|
||||||
|
ContainerID string `json:"ContainerId,omitempty"`
|
||||||
|
SystemType SystemType `json:"SystemType"`
|
||||||
|
CompartmentID uint16 `json:"CompartmentId,omitempty"`
|
||||||
|
VirtualNICName string `json:"VirtualNicName,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EndpointResquestResponse is object to get the endpoint request response
|
||||||
|
type EndpointResquestResponse struct {
|
||||||
|
Success bool
|
||||||
|
Error string
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSEndpointRequest makes a HNS call to modify/query a network endpoint
|
||||||
|
func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) {
|
||||||
|
endpoint := &HNSEndpoint{}
|
||||||
|
err := hnsCall(method, "/endpoints/"+path, request, &endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpoint, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSListEndpointRequest makes a HNS call to query the list of available endpoints
|
||||||
|
func HNSListEndpointRequest() ([]HNSEndpoint, error) {
|
||||||
|
var endpoint []HNSEndpoint
|
||||||
|
err := hnsCall("GET", "/endpoints/", "", &endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpoint, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHNSEndpointByID get the Endpoint by ID
|
||||||
|
func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) {
|
||||||
|
return HNSEndpointRequest("GET", endpointID, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHNSEndpointByName gets the endpoint filtered by Name
|
||||||
|
func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) {
|
||||||
|
hnsResponse, err := HNSListEndpointRequest()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, hnsEndpoint := range hnsResponse {
|
||||||
|
if hnsEndpoint.Name == endpointName {
|
||||||
|
return &hnsEndpoint, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, EndpointNotFoundError{EndpointName: endpointName}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Endpoint by sending EndpointRequest to HNS. TODO: Create a separate HNS interface to place all these methods
|
||||||
|
func (endpoint *HNSEndpoint) Create() (*HNSEndpoint, error) {
|
||||||
|
operation := "Create"
|
||||||
|
title := "hcsshim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return HNSEndpointRequest("POST", "", string(jsonString))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete Endpoint by sending EndpointRequest to HNS
|
||||||
|
func (endpoint *HNSEndpoint) Delete() (*HNSEndpoint, error) {
|
||||||
|
operation := "Delete"
|
||||||
|
title := "hcsshim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
|
||||||
|
return HNSEndpointRequest("DELETE", endpoint.Id, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Endpoint
|
||||||
|
func (endpoint *HNSEndpoint) Update() (*HNSEndpoint, error) {
|
||||||
|
operation := "Update"
|
||||||
|
title := "hcsshim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
jsonString, err := json.Marshal(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = hnsCall("POST", "/endpoints/"+endpoint.Id, string(jsonString), &endpoint)
|
||||||
|
|
||||||
|
return endpoint, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyACLPolicy applies a set of ACL Policies on the Endpoint
|
||||||
|
func (endpoint *HNSEndpoint) ApplyACLPolicy(policies ...*ACLPolicy) error {
|
||||||
|
operation := "ApplyACLPolicy"
|
||||||
|
title := "hcsshim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
|
||||||
|
for _, policy := range policies {
|
||||||
|
if policy == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
jsonString, err := json.Marshal(policy)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
endpoint.Policies = append(endpoint.Policies, jsonString)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := endpoint.Update()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerAttach attaches an endpoint to container
|
||||||
|
func (endpoint *HNSEndpoint) ContainerAttach(containerID string, compartmentID uint16) error {
|
||||||
|
operation := "ContainerAttach"
|
||||||
|
title := "hcsshim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
|
||||||
|
requestMessage := &EndpointAttachDetachRequest{
|
||||||
|
ContainerID: containerID,
|
||||||
|
CompartmentID: compartmentID,
|
||||||
|
SystemType: ContainerType,
|
||||||
|
}
|
||||||
|
response := &EndpointResquestResponse{}
|
||||||
|
jsonString, err := json.Marshal(requestMessage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerDetach detaches an endpoint from container
|
||||||
|
func (endpoint *HNSEndpoint) ContainerDetach(containerID string) error {
|
||||||
|
operation := "ContainerDetach"
|
||||||
|
title := "hcsshim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
|
||||||
|
requestMessage := &EndpointAttachDetachRequest{
|
||||||
|
ContainerID: containerID,
|
||||||
|
SystemType: ContainerType,
|
||||||
|
}
|
||||||
|
response := &EndpointResquestResponse{}
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(requestMessage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostAttach attaches a nic on the host
|
||||||
|
func (endpoint *HNSEndpoint) HostAttach(compartmentID uint16) error {
|
||||||
|
operation := "HostAttach"
|
||||||
|
title := "hcsshim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
requestMessage := &EndpointAttachDetachRequest{
|
||||||
|
CompartmentID: compartmentID,
|
||||||
|
SystemType: HostType,
|
||||||
|
}
|
||||||
|
response := &EndpointResquestResponse{}
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(requestMessage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostDetach detaches a nic on the host
|
||||||
|
func (endpoint *HNSEndpoint) HostDetach() error {
|
||||||
|
operation := "HostDetach"
|
||||||
|
title := "hcsshim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
requestMessage := &EndpointAttachDetachRequest{
|
||||||
|
SystemType: HostType,
|
||||||
|
}
|
||||||
|
response := &EndpointResquestResponse{}
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(requestMessage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VirtualMachineNICAttach attaches a endpoint to a virtual machine
|
||||||
|
func (endpoint *HNSEndpoint) VirtualMachineNICAttach(virtualMachineNICName string) error {
|
||||||
|
operation := "VirtualMachineNicAttach"
|
||||||
|
title := "hcsshim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
requestMessage := &EndpointAttachDetachRequest{
|
||||||
|
VirtualNICName: virtualMachineNICName,
|
||||||
|
SystemType: VirtualMachineType,
|
||||||
|
}
|
||||||
|
response := &EndpointResquestResponse{}
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(requestMessage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VirtualMachineNICDetach detaches a endpoint from a virtual machine
|
||||||
|
func (endpoint *HNSEndpoint) VirtualMachineNICDetach() error {
|
||||||
|
operation := "VirtualMachineNicDetach"
|
||||||
|
title := "hcsshim::HNSEndpoint::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", endpoint.Id)
|
||||||
|
|
||||||
|
requestMessage := &EndpointAttachDetachRequest{
|
||||||
|
SystemType: VirtualMachineType,
|
||||||
|
}
|
||||||
|
response := &EndpointResquestResponse{}
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(requestMessage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response)
|
||||||
|
}
|
||||||
42
vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go
generated
vendored
Normal file
42
vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package hns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/interop"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func hnsCall(method, path, request string, returnResponse interface{}) error {
|
||||||
|
var responseBuffer *uint16
|
||||||
|
logrus.Debugf("[%s]=>[%s] Request : %s", method, path, request)
|
||||||
|
|
||||||
|
err := _hnsCall(method, path, request, &responseBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return hcserror.New(err, "hnsCall ", "")
|
||||||
|
}
|
||||||
|
response := interop.ConvertAndFreeCoTaskMemString(responseBuffer)
|
||||||
|
|
||||||
|
hnsresponse := &hnsResponse{}
|
||||||
|
if err = json.Unmarshal([]byte(response), &hnsresponse); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hnsresponse.Success {
|
||||||
|
return fmt.Errorf("HNS failed with error : %s", hnsresponse.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(hnsresponse.Output) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("Network Response : %s", hnsresponse.Output)
|
||||||
|
err = json.Unmarshal(hnsresponse.Output, returnResponse)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
28
vendor/github.com/Microsoft/hcsshim/internal/hns/hnsglobals.go
generated
vendored
Normal file
28
vendor/github.com/Microsoft/hcsshim/internal/hns/hnsglobals.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package hns
|
||||||
|
|
||||||
|
type HNSGlobals struct {
|
||||||
|
Version HNSVersion `json:"Version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HNSVersion struct {
|
||||||
|
Major int `json:"Major"`
|
||||||
|
Minor int `json:"Minor"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
HNSVersion1803 = HNSVersion{Major: 7, Minor: 2}
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetHNSGlobals() (*HNSGlobals, error) {
|
||||||
|
var version HNSVersion
|
||||||
|
err := hnsCall("GET", "/globals/version", "", &version)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
globals := &HNSGlobals{
|
||||||
|
Version: version,
|
||||||
|
}
|
||||||
|
|
||||||
|
return globals, nil
|
||||||
|
}
|
||||||
141
vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go
generated
vendored
Normal file
141
vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
package hns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Subnet is assoicated with a network and represents a list
|
||||||
|
// of subnets available to the network
|
||||||
|
type Subnet struct {
|
||||||
|
AddressPrefix string `json:",omitempty"`
|
||||||
|
GatewayAddress string `json:",omitempty"`
|
||||||
|
Policies []json.RawMessage `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MacPool is assoicated with a network and represents a list
|
||||||
|
// of macaddresses available to the network
|
||||||
|
type MacPool struct {
|
||||||
|
StartMacAddress string `json:",omitempty"`
|
||||||
|
EndMacAddress string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSNetwork represents a network in HNS
|
||||||
|
type HNSNetwork struct {
|
||||||
|
Id string `json:"ID,omitempty"`
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
Type string `json:",omitempty"`
|
||||||
|
NetworkAdapterName string `json:",omitempty"`
|
||||||
|
SourceMac string `json:",omitempty"`
|
||||||
|
Policies []json.RawMessage `json:",omitempty"`
|
||||||
|
MacPools []MacPool `json:",omitempty"`
|
||||||
|
Subnets []Subnet `json:",omitempty"`
|
||||||
|
DNSSuffix string `json:",omitempty"`
|
||||||
|
DNSServerList string `json:",omitempty"`
|
||||||
|
DNSServerCompartment uint32 `json:",omitempty"`
|
||||||
|
ManagementIP string `json:",omitempty"`
|
||||||
|
AutomaticDNS bool `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type hnsNetworkResponse struct {
|
||||||
|
Success bool
|
||||||
|
Error string
|
||||||
|
Output HNSNetwork
|
||||||
|
}
|
||||||
|
|
||||||
|
type hnsResponse struct {
|
||||||
|
Success bool
|
||||||
|
Error string
|
||||||
|
Output json.RawMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSNetworkRequest makes a call into HNS to update/query a single network
|
||||||
|
func HNSNetworkRequest(method, path, request string) (*HNSNetwork, error) {
|
||||||
|
var network HNSNetwork
|
||||||
|
err := hnsCall(method, "/networks/"+path, request, &network)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &network, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSListNetworkRequest makes a HNS call to query the list of available networks
|
||||||
|
func HNSListNetworkRequest(method, path, request string) ([]HNSNetwork, error) {
|
||||||
|
var network []HNSNetwork
|
||||||
|
err := hnsCall(method, "/networks/"+path, request, &network)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return network, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHNSNetworkByID
|
||||||
|
func GetHNSNetworkByID(networkID string) (*HNSNetwork, error) {
|
||||||
|
return HNSNetworkRequest("GET", networkID, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHNSNetworkName filtered by Name
|
||||||
|
func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) {
|
||||||
|
hsnnetworks, err := HNSListNetworkRequest("GET", "", "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, hnsnetwork := range hsnnetworks {
|
||||||
|
if hnsnetwork.Name == networkName {
|
||||||
|
return &hnsnetwork, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, NetworkNotFoundError{NetworkName: networkName}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Network by sending NetworkRequest to HNS.
|
||||||
|
func (network *HNSNetwork) Create() (*HNSNetwork, error) {
|
||||||
|
operation := "Create"
|
||||||
|
title := "hcsshim::HNSNetwork::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", network.Id)
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(network)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return HNSNetworkRequest("POST", "", string(jsonString))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete Network by sending NetworkRequest to HNS
|
||||||
|
func (network *HNSNetwork) Delete() (*HNSNetwork, error) {
|
||||||
|
operation := "Delete"
|
||||||
|
title := "hcsshim::HNSNetwork::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", network.Id)
|
||||||
|
|
||||||
|
return HNSNetworkRequest("DELETE", network.Id, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates an endpoint on the Network.
|
||||||
|
func (network *HNSNetwork) NewEndpoint(ipAddress net.IP, macAddress net.HardwareAddr) *HNSEndpoint {
|
||||||
|
return &HNSEndpoint{
|
||||||
|
VirtualNetwork: network.Id,
|
||||||
|
IPAddress: ipAddress,
|
||||||
|
MacAddress: string(macAddress),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (network *HNSNetwork) CreateEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) {
|
||||||
|
operation := "CreateEndpoint"
|
||||||
|
title := "hcsshim::HNSNetwork::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s, endpointId=%s", network.Id, endpoint.Id)
|
||||||
|
|
||||||
|
endpoint.VirtualNetwork = network.Id
|
||||||
|
return endpoint.Create()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (network *HNSNetwork) CreateRemoteEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) {
|
||||||
|
operation := "CreateRemoteEndpoint"
|
||||||
|
title := "hcsshim::HNSNetwork::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", network.Id)
|
||||||
|
endpoint.IsRemoteEndpoint = true
|
||||||
|
return network.CreateEndpoint(endpoint)
|
||||||
|
}
|
||||||
98
vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go
generated
vendored
Normal file
98
vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package hns
|
||||||
|
|
||||||
|
// Type of Request Support in ModifySystem
|
||||||
|
type PolicyType string
|
||||||
|
|
||||||
|
// RequestType const
|
||||||
|
const (
|
||||||
|
Nat PolicyType = "NAT"
|
||||||
|
ACL PolicyType = "ACL"
|
||||||
|
PA PolicyType = "PA"
|
||||||
|
VLAN PolicyType = "VLAN"
|
||||||
|
VSID PolicyType = "VSID"
|
||||||
|
VNet PolicyType = "VNET"
|
||||||
|
L2Driver PolicyType = "L2Driver"
|
||||||
|
Isolation PolicyType = "Isolation"
|
||||||
|
QOS PolicyType = "QOS"
|
||||||
|
OutboundNat PolicyType = "OutBoundNAT"
|
||||||
|
ExternalLoadBalancer PolicyType = "ELB"
|
||||||
|
Route PolicyType = "ROUTE"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NatPolicy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
Protocol string
|
||||||
|
InternalPort uint16
|
||||||
|
ExternalPort uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type QosPolicy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
MaximumOutgoingBandwidthInBytes uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type IsolationPolicy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
VLAN uint
|
||||||
|
VSID uint
|
||||||
|
InDefaultIsolation bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type VlanPolicy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
VLAN uint
|
||||||
|
}
|
||||||
|
|
||||||
|
type VsidPolicy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
VSID uint
|
||||||
|
}
|
||||||
|
|
||||||
|
type PaPolicy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
PA string `json:"PA"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OutboundNatPolicy struct {
|
||||||
|
Policy
|
||||||
|
VIP string `json:"VIP,omitempty"`
|
||||||
|
Exceptions []string `json:"ExceptionList,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActionType string
|
||||||
|
type DirectionType string
|
||||||
|
type RuleType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Allow ActionType = "Allow"
|
||||||
|
Block ActionType = "Block"
|
||||||
|
|
||||||
|
In DirectionType = "In"
|
||||||
|
Out DirectionType = "Out"
|
||||||
|
|
||||||
|
Host RuleType = "Host"
|
||||||
|
Switch RuleType = "Switch"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ACLPolicy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
Id string `json:"Id,omitempty"`
|
||||||
|
Protocol uint16
|
||||||
|
Protocols string `json:"Protocols,omitempty"`
|
||||||
|
InternalPort uint16
|
||||||
|
Action ActionType
|
||||||
|
Direction DirectionType
|
||||||
|
LocalAddresses string
|
||||||
|
RemoteAddresses string
|
||||||
|
LocalPorts string `json:"LocalPorts,omitempty"`
|
||||||
|
LocalPort uint16
|
||||||
|
RemotePorts string `json:"RemotePorts,omitempty"`
|
||||||
|
RemotePort uint16
|
||||||
|
RuleType RuleType `json:"RuleType,omitempty"`
|
||||||
|
Priority uint16
|
||||||
|
ServiceName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Policy struct {
|
||||||
|
Type PolicyType `json:"Type"`
|
||||||
|
}
|
||||||
200
vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go
generated
vendored
Normal file
200
vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
package hns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RoutePolicy is a structure defining schema for Route based Policy
|
||||||
|
type RoutePolicy struct {
|
||||||
|
Policy
|
||||||
|
DestinationPrefix string `json:"DestinationPrefix,omitempty"`
|
||||||
|
NextHop string `json:"NextHop,omitempty"`
|
||||||
|
EncapEnabled bool `json:"NeedEncap,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ELBPolicy is a structure defining schema for ELB LoadBalancing based Policy
|
||||||
|
type ELBPolicy struct {
|
||||||
|
LBPolicy
|
||||||
|
SourceVIP string `json:"SourceVIP,omitempty"`
|
||||||
|
VIPs []string `json:"VIPs,omitempty"`
|
||||||
|
ILB bool `json:"ILB,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LBPolicy is a structure defining schema for LoadBalancing based Policy
|
||||||
|
type LBPolicy struct {
|
||||||
|
Policy
|
||||||
|
Protocol uint16 `json:"Protocol,omitempty"`
|
||||||
|
InternalPort uint16
|
||||||
|
ExternalPort uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyList is a structure defining schema for Policy list request
|
||||||
|
type PolicyList struct {
|
||||||
|
ID string `json:"ID,omitempty"`
|
||||||
|
EndpointReferences []string `json:"References,omitempty"`
|
||||||
|
Policies []json.RawMessage `json:"Policies,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSPolicyListRequest makes a call into HNS to update/query a single network
|
||||||
|
func HNSPolicyListRequest(method, path, request string) (*PolicyList, error) {
|
||||||
|
var policy PolicyList
|
||||||
|
err := hnsCall(method, "/policylists/"+path, request, &policy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &policy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HNSListPolicyListRequest gets all the policy list
|
||||||
|
func HNSListPolicyListRequest() ([]PolicyList, error) {
|
||||||
|
var plist []PolicyList
|
||||||
|
err := hnsCall("GET", "/policylists/", "", &plist)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return plist, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyListRequest makes a HNS call to modify/query a network policy list
|
||||||
|
func PolicyListRequest(method, path, request string) (*PolicyList, error) {
|
||||||
|
policylist := &PolicyList{}
|
||||||
|
err := hnsCall(method, "/policylists/"+path, request, &policylist)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return policylist, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPolicyListByID get the policy list by ID
|
||||||
|
func GetPolicyListByID(policyListID string) (*PolicyList, error) {
|
||||||
|
return PolicyListRequest("GET", policyListID, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create PolicyList by sending PolicyListRequest to HNS.
|
||||||
|
func (policylist *PolicyList) Create() (*PolicyList, error) {
|
||||||
|
operation := "Create"
|
||||||
|
title := "hcsshim::PolicyList::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", policylist.ID)
|
||||||
|
jsonString, err := json.Marshal(policylist)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return PolicyListRequest("POST", "", string(jsonString))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes PolicyList
|
||||||
|
func (policylist *PolicyList) Delete() (*PolicyList, error) {
|
||||||
|
operation := "Delete"
|
||||||
|
title := "hcsshim::PolicyList::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s", policylist.ID)
|
||||||
|
|
||||||
|
return PolicyListRequest("DELETE", policylist.ID, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEndpoint add an endpoint to a Policy List
|
||||||
|
func (policylist *PolicyList) AddEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) {
|
||||||
|
operation := "AddEndpoint"
|
||||||
|
title := "hcsshim::PolicyList::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id)
|
||||||
|
|
||||||
|
_, err := policylist.Delete()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Endpoint to the Existing List
|
||||||
|
policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id)
|
||||||
|
|
||||||
|
return policylist.Create()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveEndpoint removes an endpoint from the Policy List
|
||||||
|
func (policylist *PolicyList) RemoveEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) {
|
||||||
|
operation := "RemoveEndpoint"
|
||||||
|
title := "hcsshim::PolicyList::" + operation
|
||||||
|
logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id)
|
||||||
|
|
||||||
|
_, err := policylist.Delete()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
elementToRemove := "/endpoints/" + endpoint.Id
|
||||||
|
|
||||||
|
var references []string
|
||||||
|
|
||||||
|
for _, endpointReference := range policylist.EndpointReferences {
|
||||||
|
if endpointReference == elementToRemove {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
references = append(references, endpointReference)
|
||||||
|
}
|
||||||
|
policylist.EndpointReferences = references
|
||||||
|
return policylist.Create()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddLoadBalancer policy list for the specified endpoints
|
||||||
|
func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) {
|
||||||
|
operation := "AddLoadBalancer"
|
||||||
|
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)
|
||||||
|
|
||||||
|
policylist := &PolicyList{}
|
||||||
|
|
||||||
|
elbPolicy := &ELBPolicy{
|
||||||
|
SourceVIP: sourceVIP,
|
||||||
|
ILB: isILB,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(vip) > 0 {
|
||||||
|
elbPolicy.VIPs = []string{vip}
|
||||||
|
}
|
||||||
|
elbPolicy.Type = ExternalLoadBalancer
|
||||||
|
elbPolicy.Protocol = protocol
|
||||||
|
elbPolicy.InternalPort = internalPort
|
||||||
|
elbPolicy.ExternalPort = externalPort
|
||||||
|
|
||||||
|
for _, endpoint := range endpoints {
|
||||||
|
policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(elbPolicy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
policylist.Policies = append(policylist.Policies, jsonString)
|
||||||
|
return policylist.Create()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRoute adds route policy list for the specified endpoints
|
||||||
|
func AddRoute(endpoints []HNSEndpoint, destinationPrefix string, nextHop string, encapEnabled bool) (*PolicyList, error) {
|
||||||
|
operation := "AddRoute"
|
||||||
|
title := "hcsshim::PolicyList::" + operation
|
||||||
|
logrus.Debugf(title+" destinationPrefix:%s", destinationPrefix)
|
||||||
|
|
||||||
|
policylist := &PolicyList{}
|
||||||
|
|
||||||
|
rPolicy := &RoutePolicy{
|
||||||
|
DestinationPrefix: destinationPrefix,
|
||||||
|
NextHop: nextHop,
|
||||||
|
EncapEnabled: encapEnabled,
|
||||||
|
}
|
||||||
|
rPolicy.Type = Route
|
||||||
|
|
||||||
|
for _, endpoint := range endpoints {
|
||||||
|
policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonString, err := json.Marshal(rPolicy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
policylist.Policies = append(policylist.Policies, jsonString)
|
||||||
|
return policylist.Create()
|
||||||
|
}
|
||||||
49
vendor/github.com/Microsoft/hcsshim/internal/hns/hnssupport.go
generated
vendored
Normal file
49
vendor/github.com/Microsoft/hcsshim/internal/hns/hnssupport.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package hns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HNSSupportedFeatures struct {
|
||||||
|
Acl HNSAclFeatures `json:"ACL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HNSAclFeatures struct {
|
||||||
|
AclAddressLists bool `json:"AclAddressLists"`
|
||||||
|
AclNoHostRulePriority bool `json:"AclHostRulePriority"`
|
||||||
|
AclPortRanges bool `json:"AclPortRanges"`
|
||||||
|
AclRuleId bool `json:"AclRuleId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHNSSupportedFeatures() HNSSupportedFeatures {
|
||||||
|
var hnsFeatures HNSSupportedFeatures
|
||||||
|
|
||||||
|
globals, err := GetHNSGlobals()
|
||||||
|
if err != nil {
|
||||||
|
// Expected on pre-1803 builds, all features will be false/unsupported
|
||||||
|
logrus.Debugf("Unable to obtain HNS globals: %s", err)
|
||||||
|
return hnsFeatures
|
||||||
|
}
|
||||||
|
|
||||||
|
hnsFeatures.Acl = HNSAclFeatures{
|
||||||
|
AclAddressLists: isHNSFeatureSupported(globals.Version, HNSVersion1803),
|
||||||
|
AclNoHostRulePriority: isHNSFeatureSupported(globals.Version, HNSVersion1803),
|
||||||
|
AclPortRanges: isHNSFeatureSupported(globals.Version, HNSVersion1803),
|
||||||
|
AclRuleId: isHNSFeatureSupported(globals.Version, HNSVersion1803),
|
||||||
|
}
|
||||||
|
|
||||||
|
return hnsFeatures
|
||||||
|
}
|
||||||
|
|
||||||
|
func isHNSFeatureSupported(currentVersion HNSVersion, minVersionSupported HNSVersion) bool {
|
||||||
|
if currentVersion.Major < minVersionSupported.Major {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if currentVersion.Major > minVersionSupported.Major {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if currentVersion.Minor < minVersionSupported.Minor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
110
vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go
generated
vendored
Normal file
110
vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go
generated
vendored
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
package hns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type namespaceRequest struct {
|
||||||
|
IsDefault bool `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type namespaceEndpointRequest struct {
|
||||||
|
ID string `json:"Id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NamespaceResource struct {
|
||||||
|
Type string
|
||||||
|
Data json.RawMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
type namespaceResourceRequest struct {
|
||||||
|
Type string
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Namespace struct {
|
||||||
|
ID string
|
||||||
|
IsDefault bool `json:",omitempty"`
|
||||||
|
ResourceList []NamespaceResource `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func issueNamespaceRequest(id *string, method, subpath string, request interface{}) (*Namespace, error) {
|
||||||
|
var err error
|
||||||
|
hnspath := "/namespaces/"
|
||||||
|
if id != nil {
|
||||||
|
hnspath = path.Join(hnspath, *id)
|
||||||
|
}
|
||||||
|
if subpath != "" {
|
||||||
|
hnspath = path.Join(hnspath, subpath)
|
||||||
|
}
|
||||||
|
var reqJSON []byte
|
||||||
|
if request != nil {
|
||||||
|
if reqJSON, err = json.Marshal(request); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var ns Namespace
|
||||||
|
err = hnsCall(method, hnspath, string(reqJSON), &ns)
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "Element not found.") {
|
||||||
|
return nil, os.ErrNotExist
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s %s: %s", method, hnspath, err)
|
||||||
|
}
|
||||||
|
return &ns, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateNamespace() (string, error) {
|
||||||
|
req := namespaceRequest{}
|
||||||
|
ns, err := issueNamespaceRequest(nil, "POST", "", &req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return ns.ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveNamespace(id string) error {
|
||||||
|
_, err := issueNamespaceRequest(&id, "DELETE", "", nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetNamespaceEndpoints(id string) ([]string, error) {
|
||||||
|
ns, err := issueNamespaceRequest(&id, "GET", "", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var endpoints []string
|
||||||
|
for _, rsrc := range ns.ResourceList {
|
||||||
|
if rsrc.Type == "Endpoint" {
|
||||||
|
var endpoint namespaceEndpointRequest
|
||||||
|
err = json.Unmarshal(rsrc.Data, &endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unmarshal endpoint: %s", err)
|
||||||
|
}
|
||||||
|
endpoints = append(endpoints, endpoint.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return endpoints, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddNamespaceEndpoint(id string, endpointID string) error {
|
||||||
|
resource := namespaceResourceRequest{
|
||||||
|
Type: "Endpoint",
|
||||||
|
Data: namespaceEndpointRequest{endpointID},
|
||||||
|
}
|
||||||
|
_, err := issueNamespaceRequest(&id, "POST", "addresource", &resource)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveNamespaceEndpoint(id string, endpointID string) error {
|
||||||
|
resource := namespaceResourceRequest{
|
||||||
|
Type: "Endpoint",
|
||||||
|
Data: namespaceEndpointRequest{endpointID},
|
||||||
|
}
|
||||||
|
_, err := issueNamespaceRequest(&id, "POST", "removeresource", &resource)
|
||||||
|
return err
|
||||||
|
}
|
||||||
74
vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go
generated
vendored
Normal file
74
vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||||
|
|
||||||
|
package hns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/interop"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
// TODO: add more here, after collecting data on the common
|
||||||
|
// error values see on Windows. (perhaps when running
|
||||||
|
// all.bat?)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modvmcompute = windows.NewLazySystemDLL("vmcompute.dll")
|
||||||
|
|
||||||
|
procHNSCall = modvmcompute.NewProc("HNSCall")
|
||||||
|
)
|
||||||
|
|
||||||
|
func _hnsCall(method string, path string, object string, response **uint16) (hr error) {
|
||||||
|
var _p0 *uint16
|
||||||
|
_p0, hr = syscall.UTF16PtrFromString(method)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p1 *uint16
|
||||||
|
_p1, hr = syscall.UTF16PtrFromString(path)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var _p2 *uint16
|
||||||
|
_p2, hr = syscall.UTF16PtrFromString(object)
|
||||||
|
if hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return __hnsCall(_p0, _p1, _p2, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func __hnsCall(method *uint16, path *uint16, object *uint16, response **uint16) (hr error) {
|
||||||
|
if hr = procHNSCall.Find(); hr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r0, _, _ := syscall.Syscall6(procHNSCall.Addr(), 4, uintptr(unsafe.Pointer(method)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(object)), uintptr(unsafe.Pointer(response)), 0, 0)
|
||||||
|
if int32(r0) < 0 {
|
||||||
|
hr = interop.Win32FromHresult(r0)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
27
vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go
generated
vendored
Normal file
27
vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package interop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go interop.go
|
||||||
|
|
||||||
|
//sys coTaskMemFree(buffer unsafe.Pointer) = ole32.CoTaskMemFree
|
||||||
|
|
||||||
|
func ConvertAndFreeCoTaskMemString(buffer *uint16) string {
|
||||||
|
str := syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(buffer))[:])
|
||||||
|
coTaskMemFree(unsafe.Pointer(buffer))
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertAndFreeCoTaskMemBytes(buffer *uint16) []byte {
|
||||||
|
return []byte(ConvertAndFreeCoTaskMemString(buffer))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Win32FromHresult(hr uintptr) syscall.Errno {
|
||||||
|
if hr&0x1fff0000 == 0x00070000 {
|
||||||
|
return syscall.Errno(hr & 0xffff)
|
||||||
|
}
|
||||||
|
return syscall.Errno(hr)
|
||||||
|
}
|
||||||
48
vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go
generated
vendored
Normal file
48
vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Code generated by 'go generate'; DO NOT EDIT.
|
||||||
|
|
||||||
|
package interop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
// TODO: add more here, after collecting data on the common
|
||||||
|
// error values see on Windows. (perhaps when running
|
||||||
|
// all.bat?)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modole32 = windows.NewLazySystemDLL("ole32.dll")
|
||||||
|
|
||||||
|
procCoTaskMemFree = modole32.NewProc("CoTaskMemFree")
|
||||||
|
)
|
||||||
|
|
||||||
|
func coTaskMemFree(buffer unsafe.Pointer) {
|
||||||
|
syscall.Syscall(procCoTaskMemFree.Addr(), 1, uintptr(buffer), 0, 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
24
vendor/github.com/Microsoft/hcsshim/internal/longpath/longpath.go
generated
vendored
Normal file
24
vendor/github.com/Microsoft/hcsshim/internal/longpath/longpath.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package longpath
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LongAbs makes a path absolute and returns it in NT long path form.
|
||||||
|
func LongAbs(path string) (string, error) {
|
||||||
|
if strings.HasPrefix(path, `\\?\`) || strings.HasPrefix(path, `\\.\`) {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
if !filepath.IsAbs(path) {
|
||||||
|
absPath, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
path = absPath
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(path, `\\`) {
|
||||||
|
return `\\?\UNC\` + path[2:], nil
|
||||||
|
}
|
||||||
|
return `\\?\` + path, nil
|
||||||
|
}
|
||||||
52
vendor/github.com/Microsoft/hcsshim/internal/mergemaps/merge.go
generated
vendored
Normal file
52
vendor/github.com/Microsoft/hcsshim/internal/mergemaps/merge.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package mergemaps
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
// Merge recursively merges map `fromMap` into map `ToMap`. Any pre-existing values
|
||||||
|
// in ToMap are overwritten. Values in fromMap are added to ToMap.
|
||||||
|
// From http://stackoverflow.com/questions/40491438/merging-two-json-strings-in-golang
|
||||||
|
func Merge(fromMap, ToMap interface{}) interface{} {
|
||||||
|
switch fromMap := fromMap.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
ToMap, ok := ToMap.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return fromMap
|
||||||
|
}
|
||||||
|
for keyToMap, valueToMap := range ToMap {
|
||||||
|
if valueFromMap, ok := fromMap[keyToMap]; ok {
|
||||||
|
fromMap[keyToMap] = Merge(valueFromMap, valueToMap)
|
||||||
|
} else {
|
||||||
|
fromMap[keyToMap] = valueToMap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case nil:
|
||||||
|
// merge(nil, map[string]interface{...}) -> map[string]interface{...}
|
||||||
|
ToMap, ok := ToMap.(map[string]interface{})
|
||||||
|
if ok {
|
||||||
|
return ToMap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fromMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeJSON merges the contents of a JSON string into an object representation,
|
||||||
|
// returning a new object suitable for translating to JSON.
|
||||||
|
func MergeJSON(object interface{}, additionalJSON []byte) (interface{}, error) {
|
||||||
|
if len(additionalJSON) == 0 {
|
||||||
|
return object, nil
|
||||||
|
}
|
||||||
|
objectJSON, err := json.Marshal(object)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var objectMap, newMap map[string]interface{}
|
||||||
|
err = json.Unmarshal(objectJSON, &objectMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(additionalJSON, &newMap)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return Merge(newMap, objectMap), nil
|
||||||
|
}
|
||||||
431
vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go
generated
vendored
Normal file
431
vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go
generated
vendored
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
package safefile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"unicode/utf16"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/longpath"
|
||||||
|
|
||||||
|
winio "github.com/Microsoft/go-winio"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run $GOROOT\src\syscall\mksyscall_windows.go -output zsyscall_windows.go safeopen.go
|
||||||
|
|
||||||
|
//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 := longpath.LongAbs(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 := longpath.LongAbs(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
|
||||||
|
}
|
||||||
79
vendor/github.com/Microsoft/hcsshim/internal/safefile/zsyscall_windows.go
generated
vendored
Normal file
79
vendor/github.com/Microsoft/hcsshim/internal/safefile/zsyscall_windows.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// Code generated by 'go generate'; DO NOT EDIT.
|
||||||
|
|
||||||
|
package safefile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ unsafe.Pointer
|
||||||
|
|
||||||
|
// Do the interface allocations only once for common
|
||||||
|
// Errno values.
|
||||||
|
const (
|
||||||
|
errnoERROR_IO_PENDING = 997
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||||
|
)
|
||||||
|
|
||||||
|
// errnoErr returns common boxed Errno values, to prevent
|
||||||
|
// allocations at runtime.
|
||||||
|
func errnoErr(e syscall.Errno) error {
|
||||||
|
switch e {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case errnoERROR_IO_PENDING:
|
||||||
|
return errERROR_IO_PENDING
|
||||||
|
}
|
||||||
|
// TODO: add more here, after collecting data on the common
|
||||||
|
// error values see on Windows. (perhaps when running
|
||||||
|
// all.bat?)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
modntdll = windows.NewLazySystemDLL("ntdll.dll")
|
||||||
|
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||||
|
|
||||||
|
procNtCreateFile = modntdll.NewProc("NtCreateFile")
|
||||||
|
procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile")
|
||||||
|
procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb")
|
||||||
|
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
|
||||||
|
procLocalFree = modkernel32.NewProc("LocalFree")
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
228
vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go
generated
vendored
Normal file
228
vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go
generated
vendored
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
package schema1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessConfig is used as both the input of Container.CreateProcess
|
||||||
|
// and to convert the parameters to JSON for passing onto the HCS
|
||||||
|
type ProcessConfig struct {
|
||||||
|
ApplicationName string `json:",omitempty"`
|
||||||
|
CommandLine string `json:",omitempty"`
|
||||||
|
CommandArgs []string `json:",omitempty"` // Used by Linux Containers on Windows
|
||||||
|
User string `json:",omitempty"`
|
||||||
|
WorkingDirectory string `json:",omitempty"`
|
||||||
|
Environment map[string]string `json:",omitempty"`
|
||||||
|
EmulateConsole bool `json:",omitempty"`
|
||||||
|
CreateStdInPipe bool `json:",omitempty"`
|
||||||
|
CreateStdOutPipe bool `json:",omitempty"`
|
||||||
|
CreateStdErrPipe bool `json:",omitempty"`
|
||||||
|
ConsoleSize [2]uint `json:",omitempty"`
|
||||||
|
CreateInUtilityVm bool `json:",omitempty"` // Used by Linux Containers on Windows
|
||||||
|
OCISpecification *json.RawMessage `json:",omitempty"` // Used by Linux Containers on Windows
|
||||||
|
}
|
||||||
|
|
||||||
|
type Layer struct {
|
||||||
|
ID string
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
type MappedDir struct {
|
||||||
|
HostPath string
|
||||||
|
ContainerPath string
|
||||||
|
ReadOnly bool
|
||||||
|
BandwidthMaximum uint64
|
||||||
|
IOPSMaximum uint64
|
||||||
|
CreateInUtilityVM bool
|
||||||
|
// LinuxMetadata - Support added in 1803/RS4+.
|
||||||
|
LinuxMetadata bool `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MappedPipe struct {
|
||||||
|
HostPath string
|
||||||
|
ContainerPipeName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type HvRuntime struct {
|
||||||
|
ImagePath string `json:",omitempty"`
|
||||||
|
SkipTemplate bool `json:",omitempty"`
|
||||||
|
LinuxInitrdFile string `json:",omitempty"` // File under ImagePath on host containing an initrd image for starting a Linux utility VM
|
||||||
|
LinuxKernelFile string `json:",omitempty"` // File under ImagePath on host containing a kernel for starting a Linux utility VM
|
||||||
|
LinuxBootParameters string `json:",omitempty"` // Additional boot parameters for starting a Linux Utility VM in initrd mode
|
||||||
|
BootSource string `json:",omitempty"` // "Vhd" for Linux Utility VM booting from VHD
|
||||||
|
WritableBootSource bool `json:",omitempty"` // Linux Utility VM booting from VHD
|
||||||
|
}
|
||||||
|
|
||||||
|
type MappedVirtualDisk struct {
|
||||||
|
HostPath string `json:",omitempty"` // Path to VHD on the host
|
||||||
|
ContainerPath string // Platform-specific mount point path in the container
|
||||||
|
CreateInUtilityVM bool `json:",omitempty"`
|
||||||
|
ReadOnly bool `json:",omitempty"`
|
||||||
|
Cache string `json:",omitempty"` // "" (Unspecified); "Disabled"; "Enabled"; "Private"; "PrivateAllowSharing"
|
||||||
|
AttachOnly bool `json:",omitempty:`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssignedDevice represents a device that has been directly assigned to a container
|
||||||
|
//
|
||||||
|
// NOTE: Support added in RS5
|
||||||
|
type AssignedDevice struct {
|
||||||
|
// InterfaceClassGUID of the device to assign to container.
|
||||||
|
InterfaceClassGUID string `json:"InterfaceClassGuid,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerConfig is used as both the input of CreateContainer
|
||||||
|
// and to convert the parameters to JSON for passing onto the HCS
|
||||||
|
type ContainerConfig struct {
|
||||||
|
SystemType string // HCS requires this to be hard-coded to "Container"
|
||||||
|
Name string // Name of the container. We use the docker ID.
|
||||||
|
Owner string `json:",omitempty"` // The management platform that created this container
|
||||||
|
VolumePath string `json:",omitempty"` // Windows volume path for scratch space. Used by Windows Server Containers only. Format \\?\\Volume{GUID}
|
||||||
|
IgnoreFlushesDuringBoot bool `json:",omitempty"` // Optimization hint for container startup in Windows
|
||||||
|
LayerFolderPath string `json:",omitempty"` // Where the layer folders are located. Used by Windows Server Containers only. Format %root%\windowsfilter\containerID
|
||||||
|
Layers []Layer // List of storage layers. Required for Windows Server and Hyper-V Containers. Format ID=GUID;Path=%root%\windowsfilter\layerID
|
||||||
|
Credentials string `json:",omitempty"` // Credentials information
|
||||||
|
ProcessorCount uint32 `json:",omitempty"` // Number of processors to assign to the container.
|
||||||
|
ProcessorWeight uint64 `json:",omitempty"` // CPU shares (relative weight to other containers with cpu shares). Range is from 1 to 10000. A value of 0 results in default shares.
|
||||||
|
ProcessorMaximum int64 `json:",omitempty"` // Specifies the portion of processor cycles that this container can use as a percentage times 100. Range is from 1 to 10000. A value of 0 results in no limit.
|
||||||
|
StorageIOPSMaximum uint64 `json:",omitempty"` // Maximum Storage IOPS
|
||||||
|
StorageBandwidthMaximum uint64 `json:",omitempty"` // Maximum Storage Bandwidth in bytes per second
|
||||||
|
StorageSandboxSize uint64 `json:",omitempty"` // Size in bytes that the container system drive should be expanded to if smaller
|
||||||
|
MemoryMaximumInMB int64 `json:",omitempty"` // Maximum memory available to the container in Megabytes
|
||||||
|
HostName string `json:",omitempty"` // Hostname
|
||||||
|
MappedDirectories []MappedDir `json:",omitempty"` // List of mapped directories (volumes/mounts)
|
||||||
|
MappedPipes []MappedPipe `json:",omitempty"` // List of mapped Windows named pipes
|
||||||
|
HvPartition bool // True if it a Hyper-V Container
|
||||||
|
NetworkSharedContainerName string `json:",omitempty"` // Name (ID) of the container that we will share the network stack with.
|
||||||
|
EndpointList []string `json:",omitempty"` // List of networking endpoints to be attached to container
|
||||||
|
HvRuntime *HvRuntime `json:",omitempty"` // Hyper-V container settings. Used by Hyper-V containers only. Format ImagePath=%root%\BaseLayerID\UtilityVM
|
||||||
|
Servicing bool `json:",omitempty"` // True if this container is for servicing
|
||||||
|
AllowUnqualifiedDNSQuery bool `json:",omitempty"` // True to allow unqualified DNS name resolution
|
||||||
|
DNSSearchList string `json:",omitempty"` // Comma seperated list of DNS suffixes to use for name resolution
|
||||||
|
ContainerType string `json:",omitempty"` // "Linux" for Linux containers on Windows. Omitted otherwise.
|
||||||
|
TerminateOnLastHandleClosed bool `json:",omitempty"` // Should HCS terminate the container once all handles have been closed
|
||||||
|
MappedVirtualDisks []MappedVirtualDisk `json:",omitempty"` // Array of virtual disks to mount at start
|
||||||
|
AssignedDevices []AssignedDevice `json:",omitempty"` // Array of devices to assign. NOTE: Support added in RS5
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComputeSystemQuery struct {
|
||||||
|
IDs []string `json:"Ids,omitempty"`
|
||||||
|
Types []string `json:",omitempty"`
|
||||||
|
Names []string `json:",omitempty"`
|
||||||
|
Owners []string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PropertyType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
PropertyTypeStatistics PropertyType = "Statistics"
|
||||||
|
PropertyTypeProcessList = "ProcessList"
|
||||||
|
PropertyTypeMappedVirtualDisk = "MappedVirtualDisk"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PropertyQuery struct {
|
||||||
|
PropertyTypes []PropertyType `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerProperties holds the properties for a container and the processes running in that container
|
||||||
|
type ContainerProperties struct {
|
||||||
|
ID string `json:"Id"`
|
||||||
|
State string
|
||||||
|
Name string
|
||||||
|
SystemType string
|
||||||
|
Owner string
|
||||||
|
SiloGUID string `json:"SiloGuid,omitempty"`
|
||||||
|
RuntimeID string `json:"RuntimeId,omitempty"`
|
||||||
|
IsRuntimeTemplate bool `json:",omitempty"`
|
||||||
|
RuntimeImagePath string `json:",omitempty"`
|
||||||
|
Stopped bool `json:",omitempty"`
|
||||||
|
ExitType string `json:",omitempty"`
|
||||||
|
AreUpdatesPending bool `json:",omitempty"`
|
||||||
|
ObRoot string `json:",omitempty"`
|
||||||
|
Statistics Statistics `json:",omitempty"`
|
||||||
|
ProcessList []ProcessListItem `json:",omitempty"`
|
||||||
|
MappedVirtualDiskControllers map[int]MappedVirtualDiskController `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemoryStats holds the memory statistics for a container
|
||||||
|
type MemoryStats struct {
|
||||||
|
UsageCommitBytes uint64 `json:"MemoryUsageCommitBytes,omitempty"`
|
||||||
|
UsageCommitPeakBytes uint64 `json:"MemoryUsageCommitPeakBytes,omitempty"`
|
||||||
|
UsagePrivateWorkingSetBytes uint64 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessorStats holds the processor statistics for a container
|
||||||
|
type ProcessorStats struct {
|
||||||
|
TotalRuntime100ns uint64 `json:",omitempty"`
|
||||||
|
RuntimeUser100ns uint64 `json:",omitempty"`
|
||||||
|
RuntimeKernel100ns uint64 `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorageStats holds the storage statistics for a container
|
||||||
|
type StorageStats struct {
|
||||||
|
ReadCountNormalized uint64 `json:",omitempty"`
|
||||||
|
ReadSizeBytes uint64 `json:",omitempty"`
|
||||||
|
WriteCountNormalized uint64 `json:",omitempty"`
|
||||||
|
WriteSizeBytes uint64 `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkStats holds the network statistics for a container
|
||||||
|
type NetworkStats struct {
|
||||||
|
BytesReceived uint64 `json:",omitempty"`
|
||||||
|
BytesSent uint64 `json:",omitempty"`
|
||||||
|
PacketsReceived uint64 `json:",omitempty"`
|
||||||
|
PacketsSent uint64 `json:",omitempty"`
|
||||||
|
DroppedPacketsIncoming uint64 `json:",omitempty"`
|
||||||
|
DroppedPacketsOutgoing uint64 `json:",omitempty"`
|
||||||
|
EndpointId string `json:",omitempty"`
|
||||||
|
InstanceId string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statistics is the structure returned by a statistics call on a container
|
||||||
|
type Statistics struct {
|
||||||
|
Timestamp time.Time `json:",omitempty"`
|
||||||
|
ContainerStartTime time.Time `json:",omitempty"`
|
||||||
|
Uptime100ns uint64 `json:",omitempty"`
|
||||||
|
Memory MemoryStats `json:",omitempty"`
|
||||||
|
Processor ProcessorStats `json:",omitempty"`
|
||||||
|
Storage StorageStats `json:",omitempty"`
|
||||||
|
Network []NetworkStats `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessList is the structure of an item returned by a ProcessList call on a container
|
||||||
|
type ProcessListItem struct {
|
||||||
|
CreateTimestamp time.Time `json:",omitempty"`
|
||||||
|
ImageName string `json:",omitempty"`
|
||||||
|
KernelTime100ns uint64 `json:",omitempty"`
|
||||||
|
MemoryCommitBytes uint64 `json:",omitempty"`
|
||||||
|
MemoryWorkingSetPrivateBytes uint64 `json:",omitempty"`
|
||||||
|
MemoryWorkingSetSharedBytes uint64 `json:",omitempty"`
|
||||||
|
ProcessId uint32 `json:",omitempty"`
|
||||||
|
UserTime100ns uint64 `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MappedVirtualDiskController is the structure of an item returned by a MappedVirtualDiskList call on a container
|
||||||
|
type MappedVirtualDiskController struct {
|
||||||
|
MappedVirtualDisks map[int]MappedVirtualDisk `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type of Request Support in ModifySystem
|
||||||
|
type RequestType string
|
||||||
|
|
||||||
|
// Type of Resource Support in ModifySystem
|
||||||
|
type ResourceType string
|
||||||
|
|
||||||
|
// RequestType const
|
||||||
|
const (
|
||||||
|
Add RequestType = "Add"
|
||||||
|
Remove RequestType = "Remove"
|
||||||
|
Network ResourceType = "Network"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResourceModificationRequestResponse 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
|
||||||
|
type ResourceModificationRequestResponse struct {
|
||||||
|
Resource ResourceType `json:"ResourceType"`
|
||||||
|
Data interface{} `json:"Settings"`
|
||||||
|
Request RequestType `json:"RequestType,omitempty"`
|
||||||
|
}
|
||||||
70
vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go
generated
vendored
Normal file
70
vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package timeout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// defaultTimeout is the timeout for most operations that is not overridden.
|
||||||
|
defaultTimeout = 4 * time.Minute
|
||||||
|
|
||||||
|
// defaultTimeoutTestdRetry is the retry loop timeout for testd to respond
|
||||||
|
// for a disk to come online in LCOW.
|
||||||
|
defaultTimeoutTestdRetry = 5 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
// External variables for HCSShim consumers to use.
|
||||||
|
var (
|
||||||
|
// SystemCreate is the timeout for creating a compute system
|
||||||
|
SystemCreate time.Duration = defaultTimeout
|
||||||
|
|
||||||
|
// SystemStart is the timeout for starting a compute system
|
||||||
|
SystemStart time.Duration = defaultTimeout
|
||||||
|
|
||||||
|
// SystemPause is the timeout for pausing a compute system
|
||||||
|
SystemPause time.Duration = defaultTimeout
|
||||||
|
|
||||||
|
// SystemResume is the timeout for resuming a compute system
|
||||||
|
SystemResume time.Duration = defaultTimeout
|
||||||
|
|
||||||
|
// SyscallWatcher is the timeout before warning of a potential stuck platform syscall.
|
||||||
|
SyscallWatcher time.Duration = defaultTimeout
|
||||||
|
|
||||||
|
// Tar2VHD is the timeout for the tar2vhd operation to complete
|
||||||
|
Tar2VHD time.Duration = defaultTimeout
|
||||||
|
|
||||||
|
// ExternalCommandToStart is the timeout for external commands to start
|
||||||
|
ExternalCommandToStart = defaultTimeout
|
||||||
|
|
||||||
|
// ExternalCommandToComplete is the timeout for external commands to complete.
|
||||||
|
// Generally this means copying data from their stdio pipes.
|
||||||
|
ExternalCommandToComplete = defaultTimeout
|
||||||
|
|
||||||
|
// TestDRetryLoop is the timeout for testd retry loop when onlining a SCSI disk in LCOW
|
||||||
|
TestDRetryLoop = defaultTimeoutTestdRetry
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SystemCreate = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMCREATE", SystemCreate)
|
||||||
|
SystemStart = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMSTART", SystemStart)
|
||||||
|
SystemPause = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMPAUSE", SystemPause)
|
||||||
|
SystemResume = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMRESUME", SystemResume)
|
||||||
|
SyscallWatcher = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSCALLWATCHER", SyscallWatcher)
|
||||||
|
Tar2VHD = durationFromEnvironment("HCSSHIM_TIMEOUT_TAR2VHD", Tar2VHD)
|
||||||
|
ExternalCommandToStart = durationFromEnvironment("HCSSHIM_TIMEOUT_EXTERNALCOMMANDSTART", ExternalCommandToStart)
|
||||||
|
ExternalCommandToComplete = durationFromEnvironment("HCSSHIM_TIMEOUT_EXTERNALCOMMANDCOMPLETE", ExternalCommandToComplete)
|
||||||
|
TestDRetryLoop = durationFromEnvironment("HCSSHIM_TIMEOUT_TESTDRETRYLOOP", TestDRetryLoop)
|
||||||
|
}
|
||||||
|
|
||||||
|
func durationFromEnvironment(env string, defaultValue time.Duration) time.Duration {
|
||||||
|
envTimeout := os.Getenv(env)
|
||||||
|
if len(envTimeout) > 0 {
|
||||||
|
e, err := strconv.Atoi(envTimeout)
|
||||||
|
if err == nil && e > 0 {
|
||||||
|
return time.Second * time.Duration(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
25
vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go
generated
vendored
Normal file
25
vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ActivateLayer will find the layer with the given id and mount it's filesystem.
|
||||||
|
// For a read/write layer, the mounted filesystem will appear as a volume on the
|
||||||
|
// host, while a read-only layer is generally expected to be a no-op.
|
||||||
|
// An activated layer must later be deactivated via DeactivateLayer.
|
||||||
|
func ActivateLayer(path string) error {
|
||||||
|
title := "hcsshim::ActivateLayer "
|
||||||
|
logrus.Debugf(title+"path %s", path)
|
||||||
|
|
||||||
|
err := activateLayer(&stdDriverInfo, path)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.Errorf(err, title, "path=%s", path)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+" - succeeded path=%s", path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
173
vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go
generated
vendored
Normal file
173
vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/safefile"
|
||||||
|
)
|
||||||
|
|
||||||
|
type baseLayerWriter struct {
|
||||||
|
root *os.File
|
||||||
|
f *os.File
|
||||||
|
bw *winio.BackupFileWriter
|
||||||
|
err error
|
||||||
|
hasUtilityVM bool
|
||||||
|
dirInfo []dirInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type dirInfo struct {
|
||||||
|
path string
|
||||||
|
fileInfo winio.FileBasicInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// reapplyDirectoryTimes reapplies directory modification, creation, etc. times
|
||||||
|
// after processing of the directory tree has completed. The times are expected
|
||||||
|
// to be ordered such that parent directories come before child directories.
|
||||||
|
func reapplyDirectoryTimes(root *os.File, dis []dirInfo) error {
|
||||||
|
for i := range dis {
|
||||||
|
di := &dis[len(dis)-i-1] // reverse order: process child directories first
|
||||||
|
f, err := safefile.OpenRelative(di.path, root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, safefile.FILE_OPEN, safefile.FILE_DIRECTORY_FILE)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = winio.SetFileBasicInfo(f, &di.fileInfo)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *baseLayerWriter) closeCurrentFile() error {
|
||||||
|
if w.f != nil {
|
||||||
|
err := w.bw.Close()
|
||||||
|
err2 := w.f.Close()
|
||||||
|
w.f = nil
|
||||||
|
w.bw = nil
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
w.err = err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = w.closeCurrentFile()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.ToSlash(name) == `UtilityVM/Files` {
|
||||||
|
w.hasUtilityVM = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var f *os.File
|
||||||
|
defer func() {
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
extraFlags := uint32(0)
|
||||||
|
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||||
|
extraFlags |= safefile.FILE_DIRECTORY_FILE
|
||||||
|
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||||
|
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)
|
||||||
|
f, err = safefile.OpenRelative(name, w.root, mode, syscall.FILE_SHARE_READ, safefile.FILE_CREATE, extraFlags)
|
||||||
|
if err != nil {
|
||||||
|
return hcserror.New(err, "Failed to safefile.OpenRelative", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = winio.SetFileBasicInfo(f, fileInfo)
|
||||||
|
if err != nil {
|
||||||
|
return hcserror.New(err, "Failed to SetFileBasicInfo", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.f = f
|
||||||
|
w.bw = winio.NewBackupFileWriter(f, true)
|
||||||
|
f = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *baseLayerWriter) AddLink(name string, target string) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
w.err = err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = w.closeCurrentFile()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return safefile.LinkRelative(target, w.root, name, w.root)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *baseLayerWriter) Remove(name string) error {
|
||||||
|
return errors.New("base layer cannot have tombstones")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *baseLayerWriter) Write(b []byte) (int, error) {
|
||||||
|
n, err := w.bw.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
w.err = err
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *baseLayerWriter) Close() error {
|
||||||
|
defer func() {
|
||||||
|
w.root.Close()
|
||||||
|
w.root = nil
|
||||||
|
}()
|
||||||
|
err := w.closeCurrentFile()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if w.err == nil {
|
||||||
|
// Restore the file times of all the directories, since they may have
|
||||||
|
// been modified by creating child directories.
|
||||||
|
err = reapplyDirectoryTimes(w.root, w.dirInfo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ProcessBaseLayer(w.root.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.hasUtilityVM {
|
||||||
|
err := safefile.EnsureNotReparsePointRelative("UtilityVM", w.root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ProcessUtilityVMImage(filepath.Join(w.root.Name(), "UtilityVM"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return w.err
|
||||||
|
}
|
||||||
23
vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go
generated
vendored
Normal file
23
vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateLayer creates a new, empty, read-only layer on the filesystem based on
|
||||||
|
// the parent layer provided.
|
||||||
|
func CreateLayer(path, parent string) error {
|
||||||
|
title := "hcsshim::CreateLayer "
|
||||||
|
logrus.Debugf(title+"ID %s parent %s", path, parent)
|
||||||
|
|
||||||
|
err := createLayer(&stdDriverInfo, path, parent)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.Errorf(err, title, "path=%s parent=%s", path, parent)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"- succeeded path=%s parent=%s", path, parent)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
31
vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go
generated
vendored
Normal file
31
vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateScratchLayer creates and populates new read-write layer for use by a container.
|
||||||
|
// This requires both the id of the direct parent layer, as well as the full list
|
||||||
|
// of paths to all parent layers up to the base (and including the direct parent
|
||||||
|
// whose id was provided).
|
||||||
|
func CreateScratchLayer(path string, parentLayerPaths []string) error {
|
||||||
|
title := "hcsshim::CreateScratchLayer "
|
||||||
|
logrus.Debugf(title+"path %s", path)
|
||||||
|
|
||||||
|
// Generate layer descriptors
|
||||||
|
layers, err := layerPathsToDescriptors(parentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = createSandboxLayer(&stdDriverInfo, path, 0, layers)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.Errorf(err, title, "path=%s", path)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"- succeeded path=%s", path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
22
vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go
generated
vendored
Normal file
22
vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeactivateLayer will dismount a layer that was mounted via ActivateLayer.
|
||||||
|
func DeactivateLayer(path string) error {
|
||||||
|
title := "hcsshim::DeactivateLayer "
|
||||||
|
logrus.Debugf(title+"path %s", path)
|
||||||
|
|
||||||
|
err := deactivateLayer(&stdDriverInfo, path)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.Errorf(err, title, "path=%s", path)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"succeeded path=%s", path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
23
vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go
generated
vendored
Normal file
23
vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DestroyLayer will remove the on-disk files representing the layer with the given
|
||||||
|
// path, including that layer's containing folder, if any.
|
||||||
|
func DestroyLayer(path string) error {
|
||||||
|
title := "hcsshim::DestroyLayer "
|
||||||
|
logrus.Debugf(title+"path %s", path)
|
||||||
|
|
||||||
|
err := destroyLayer(&stdDriverInfo, path)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.Errorf(err, title, "path=%s", path)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"succeeded path=%s", path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
22
vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go
generated
vendored
Normal file
22
vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExpandScratchSize expands the size of a layer to at least size bytes.
|
||||||
|
func ExpandScratchSize(path string, size uint64) error {
|
||||||
|
title := "hcsshim::ExpandScratchSize "
|
||||||
|
logrus.Debugf(title+"path=%s size=%d", path, size)
|
||||||
|
|
||||||
|
err := expandSandboxSize(&stdDriverInfo, path, size)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.Errorf(err, title, "path=%s size=%d", path, size)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"- succeeded path=%s size=%d", path, size)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
147
vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go
generated
vendored
Normal file
147
vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/interop"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExportLayer will create a folder at exportFolderPath and fill that folder with
|
||||||
|
// the transport format version of the layer identified by layerId. This transport
|
||||||
|
// format includes any metadata required for later importing the layer (using
|
||||||
|
// ImportLayer), and requires the full list of parent layer paths in order to
|
||||||
|
// perform the export.
|
||||||
|
func ExportLayer(path string, exportFolderPath string, parentLayerPaths []string) error {
|
||||||
|
title := "hcsshim::ExportLayer "
|
||||||
|
logrus.Debugf(title+"path %s folder %s", path, exportFolderPath)
|
||||||
|
|
||||||
|
// Generate layer descriptors
|
||||||
|
layers, err := layerPathsToDescriptors(parentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = exportLayer(&stdDriverInfo, path, exportFolderPath, layers)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.Errorf(err, title, "path=%s folder=%s", path, exportFolderPath)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"succeeded path=%s folder=%s", path, exportFolderPath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type LayerReader interface {
|
||||||
|
Next() (string, int64, *winio.FileBasicInfo, error)
|
||||||
|
Read(b []byte) (int, error)
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterLayerReader provides an interface for extracting the contents of an on-disk layer.
|
||||||
|
type FilterLayerReader struct {
|
||||||
|
context uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next reads the next available file from a layer, ensuring that parent directories are always read
|
||||||
|
// before child files and directories.
|
||||||
|
//
|
||||||
|
// Next returns the file's relative path, size, and basic file metadata. Read() should be used to
|
||||||
|
// extract a Win32 backup stream with the remainder of the metadata and the data.
|
||||||
|
func (r *FilterLayerReader) Next() (string, int64, *winio.FileBasicInfo, error) {
|
||||||
|
var fileNamep *uint16
|
||||||
|
fileInfo := &winio.FileBasicInfo{}
|
||||||
|
var deleted uint32
|
||||||
|
var fileSize int64
|
||||||
|
err := exportLayerNext(r.context, &fileNamep, fileInfo, &fileSize, &deleted)
|
||||||
|
if err != nil {
|
||||||
|
if err == syscall.ERROR_NO_MORE_FILES {
|
||||||
|
err = io.EOF
|
||||||
|
} else {
|
||||||
|
err = hcserror.New(err, "ExportLayerNext", "")
|
||||||
|
}
|
||||||
|
return "", 0, nil, err
|
||||||
|
}
|
||||||
|
fileName := interop.ConvertAndFreeCoTaskMemString(fileNamep)
|
||||||
|
if deleted != 0 {
|
||||||
|
fileInfo = nil
|
||||||
|
}
|
||||||
|
if fileName[0] == '\\' {
|
||||||
|
fileName = fileName[1:]
|
||||||
|
}
|
||||||
|
return fileName, fileSize, fileInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from the current file's Win32 backup stream.
|
||||||
|
func (r *FilterLayerReader) Read(b []byte) (int, error) {
|
||||||
|
var bytesRead uint32
|
||||||
|
err := exportLayerRead(r.context, b, &bytesRead)
|
||||||
|
if err != nil {
|
||||||
|
return 0, hcserror.New(err, "ExportLayerRead", "")
|
||||||
|
}
|
||||||
|
if bytesRead == 0 {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
return int(bytesRead), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close frees resources associated with the layer reader. It will return an
|
||||||
|
// error if there was an error while reading the layer or of the layer was not
|
||||||
|
// completely read.
|
||||||
|
func (r *FilterLayerReader) Close() (err error) {
|
||||||
|
if r.context != 0 {
|
||||||
|
err = exportLayerEnd(r.context)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.New(err, "ExportLayerEnd", "")
|
||||||
|
}
|
||||||
|
r.context = 0
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLayerReader returns a new layer reader for reading the contents of an on-disk layer.
|
||||||
|
// The caller must have taken the SeBackupPrivilege privilege
|
||||||
|
// to call this and any methods on the resulting LayerReader.
|
||||||
|
func NewLayerReader(path string, parentLayerPaths []string) (LayerReader, error) {
|
||||||
|
if procExportLayerBegin.Find() != nil {
|
||||||
|
// The new layer reader is not available on this Windows build. Fall back to the
|
||||||
|
// legacy export code path.
|
||||||
|
exportPath, err := ioutil.TempDir("", "hcs")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = ExportLayer(path, exportPath, parentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
os.RemoveAll(exportPath)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &legacyLayerReaderWrapper{newLegacyLayerReader(exportPath)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
layers, err := layerPathsToDescriptors(parentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r := &FilterLayerReader{}
|
||||||
|
err = exportLayerBegin(&stdDriverInfo, path, layers, &r.context)
|
||||||
|
if err != nil {
|
||||||
|
return nil, hcserror.New(err, "ExportLayerBegin", "")
|
||||||
|
}
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type legacyLayerReaderWrapper struct {
|
||||||
|
*legacyLayerReader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReaderWrapper) Close() error {
|
||||||
|
err := r.legacyLayerReader.Close()
|
||||||
|
os.RemoveAll(r.root)
|
||||||
|
return err
|
||||||
|
}
|
||||||
49
vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go
generated
vendored
Normal file
49
vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetLayerMountPath will look for a mounted layer with the given path and return
|
||||||
|
// the path at which that layer can be accessed. This path may be a volume path
|
||||||
|
// if the layer is a mounted read-write layer, otherwise it is expected to be the
|
||||||
|
// folder path at which the layer is stored.
|
||||||
|
func GetLayerMountPath(path string) (string, error) {
|
||||||
|
title := "hcsshim::GetLayerMountPath "
|
||||||
|
logrus.Debugf(title+"path %s", path)
|
||||||
|
|
||||||
|
var mountPathLength uintptr
|
||||||
|
mountPathLength = 0
|
||||||
|
|
||||||
|
// Call the procedure itself.
|
||||||
|
logrus.Debugf("Calling proc (1)")
|
||||||
|
err := getLayerMountPath(&stdDriverInfo, path, &mountPathLength, nil)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.Errorf(err, title, "(first call) path=%s", path)
|
||||||
|
logrus.Error(err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a mount path of the returned length.
|
||||||
|
if mountPathLength == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
mountPathp := make([]uint16, mountPathLength)
|
||||||
|
mountPathp[0] = 0
|
||||||
|
|
||||||
|
// Call the procedure again
|
||||||
|
logrus.Debugf("Calling proc (2)")
|
||||||
|
err = getLayerMountPath(&stdDriverInfo, path, &mountPathLength, &mountPathp[0])
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.Errorf(err, title, "(second call) path=%s", path)
|
||||||
|
logrus.Error(err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
mountPath := syscall.UTF16ToString(mountPathp[0:])
|
||||||
|
logrus.Debugf(title+"succeeded path=%s mountPath=%s", path, mountPath)
|
||||||
|
return mountPath, nil
|
||||||
|
}
|
||||||
26
vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go
generated
vendored
Normal file
26
vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/interop"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetSharedBaseImages will enumerate the images stored in the common central
|
||||||
|
// image store and return descriptive info about those images for the purpose
|
||||||
|
// of registering them with the graphdriver, graph, and tagstore.
|
||||||
|
func GetSharedBaseImages() (imageData string, err error) {
|
||||||
|
title := "hcsshim::GetSharedBaseImages "
|
||||||
|
|
||||||
|
logrus.Debugf("Calling proc")
|
||||||
|
var buffer *uint16
|
||||||
|
err = getBaseImages(&buffer)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.New(err, title, "")
|
||||||
|
logrus.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
imageData = interop.ConvertAndFreeCoTaskMemString(buffer)
|
||||||
|
logrus.Debugf(title+" - succeeded output=%s", imageData)
|
||||||
|
return
|
||||||
|
}
|
||||||
24
vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go
generated
vendored
Normal file
24
vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GrantVmAccess adds access to a file for a given VM
|
||||||
|
func GrantVmAccess(vmid string, filepath string) error {
|
||||||
|
title := fmt.Sprintf("hcsshim::GrantVmAccess id:%s path:%s ", vmid, filepath)
|
||||||
|
logrus.Debugf(title)
|
||||||
|
|
||||||
|
err := grantVmAccess(vmid, filepath)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.Errorf(err, title, "path=%s", filepath)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title + " - succeeded")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
206
vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go
generated
vendored
Normal file
206
vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go
generated
vendored
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/safefile"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ImportLayer will take the contents of the folder at importFolderPath and import
|
||||||
|
// that into a layer with the id layerId. Note that in order to correctly populate
|
||||||
|
// the layer and interperet the transport format, all parent layers must already
|
||||||
|
// be present on the system at the paths provided in parentLayerPaths.
|
||||||
|
func ImportLayer(path string, importFolderPath string, parentLayerPaths []string) error {
|
||||||
|
title := "hcsshim::ImportLayer "
|
||||||
|
logrus.Debugf(title+"path %s folder %s", path, importFolderPath)
|
||||||
|
|
||||||
|
// Generate layer descriptors
|
||||||
|
layers, err := layerPathsToDescriptors(parentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = importLayer(&stdDriverInfo, path, importFolderPath, layers)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.Errorf(err, title, "path=%s folder=%s", path, importFolderPath)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"succeeded path=%s folder=%s", path, importFolderPath)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerWriter is an interface that supports writing a new container image layer.
|
||||||
|
type LayerWriter interface {
|
||||||
|
// Add adds a file to the layer with given metadata.
|
||||||
|
Add(name string, fileInfo *winio.FileBasicInfo) error
|
||||||
|
// AddLink adds a hard link to the layer. The target must already have been added.
|
||||||
|
AddLink(name string, target string) error
|
||||||
|
// Remove removes a file that was present in a parent layer from the layer.
|
||||||
|
Remove(name string) error
|
||||||
|
// Write writes data to the current file. The data must be in the format of a Win32
|
||||||
|
// backup stream.
|
||||||
|
Write(b []byte) (int, error)
|
||||||
|
// Close finishes the layer writing process and releases any resources.
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterLayerWriter provides an interface to write the contents of a layer to the file system.
|
||||||
|
type FilterLayerWriter struct {
|
||||||
|
context uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a file or directory to the layer. The file's parent directory must have already been added.
|
||||||
|
//
|
||||||
|
// name contains the file's relative path. fileInfo contains file times and file attributes; the rest
|
||||||
|
// of the file metadata and the file data must be written as a Win32 backup stream to the Write() method.
|
||||||
|
// winio.BackupStreamWriter can be used to facilitate this.
|
||||||
|
func (w *FilterLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error {
|
||||||
|
if name[0] != '\\' {
|
||||||
|
name = `\` + name
|
||||||
|
}
|
||||||
|
err := importLayerNext(w.context, name, fileInfo)
|
||||||
|
if err != nil {
|
||||||
|
return hcserror.New(err, "ImportLayerNext", "")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddLink adds a hard link to the layer. The target of the link must have already been added.
|
||||||
|
func (w *FilterLayerWriter) AddLink(name string, target string) error {
|
||||||
|
return errors.New("hard links not yet supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes a file from the layer. The file must have been present in the parent layer.
|
||||||
|
//
|
||||||
|
// name contains the file's relative path.
|
||||||
|
func (w *FilterLayerWriter) Remove(name string) error {
|
||||||
|
if name[0] != '\\' {
|
||||||
|
name = `\` + name
|
||||||
|
}
|
||||||
|
err := importLayerNext(w.context, name, nil)
|
||||||
|
if err != nil {
|
||||||
|
return hcserror.New(err, "ImportLayerNext", "")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes more backup stream data to the current file.
|
||||||
|
func (w *FilterLayerWriter) Write(b []byte) (int, error) {
|
||||||
|
err := importLayerWrite(w.context, b)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.New(err, "ImportLayerWrite", "")
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(b), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close completes the layer write operation. The error must be checked to ensure that the
|
||||||
|
// operation was successful.
|
||||||
|
func (w *FilterLayerWriter) Close() (err error) {
|
||||||
|
if w.context != 0 {
|
||||||
|
err = importLayerEnd(w.context)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.New(err, "ImportLayerEnd", "")
|
||||||
|
}
|
||||||
|
w.context = 0
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type legacyLayerWriterWrapper struct {
|
||||||
|
*legacyLayerWriter
|
||||||
|
path string
|
||||||
|
parentLayerPaths []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerWriterWrapper) Close() error {
|
||||||
|
defer os.RemoveAll(r.root.Name())
|
||||||
|
defer r.legacyLayerWriter.CloseRoots()
|
||||||
|
err := r.legacyLayerWriter.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = ImportLayer(r.destRoot.Name(), r.path, r.parentLayerPaths); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, name := range r.Tombstones {
|
||||||
|
if err = safefile.RemoveRelative(name, r.destRoot); err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add any hard links that were collected.
|
||||||
|
for _, lnk := range r.PendingLinks {
|
||||||
|
if err = safefile.RemoveRelative(lnk.Path, r.destRoot); err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = safefile.LinkRelative(lnk.Target, lnk.TargetRoot, lnk.Path, r.destRoot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Prepare the utility VM for use if one is present in the layer.
|
||||||
|
if r.HasUtilityVM {
|
||||||
|
err := safefile.EnsureNotReparsePointRelative("UtilityVM", r.destRoot)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ProcessUtilityVMImage(filepath.Join(r.destRoot.Name(), "UtilityVM"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLayerWriter returns a new layer writer for creating a layer on disk.
|
||||||
|
// The caller must have taken the SeBackupPrivilege and SeRestorePrivilege privileges
|
||||||
|
// to call this and any methods on the resulting LayerWriter.
|
||||||
|
func NewLayerWriter(path string, parentLayerPaths []string) (LayerWriter, error) {
|
||||||
|
if len(parentLayerPaths) == 0 {
|
||||||
|
// This is a base layer. It gets imported differently.
|
||||||
|
f, err := safefile.OpenRoot(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &baseLayerWriter{
|
||||||
|
root: f,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if procImportLayerBegin.Find() != nil {
|
||||||
|
// The new layer reader is not available on this Windows build. Fall back to the
|
||||||
|
// legacy export code path.
|
||||||
|
importPath, err := ioutil.TempDir("", "hcs")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
w, err := newLegacyLayerWriter(importPath, parentLayerPaths, path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &legacyLayerWriterWrapper{
|
||||||
|
legacyLayerWriter: w,
|
||||||
|
path: importPath,
|
||||||
|
parentLayerPaths: parentLayerPaths,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
layers, err := layerPathsToDescriptors(parentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
w := &FilterLayerWriter{}
|
||||||
|
err = importLayerBegin(&stdDriverInfo, path, layers, &w.context)
|
||||||
|
if err != nil {
|
||||||
|
return nil, hcserror.New(err, "ImportLayerStart", "")
|
||||||
|
}
|
||||||
|
return w, nil
|
||||||
|
}
|
||||||
25
vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go
generated
vendored
Normal file
25
vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LayerExists will return true if a layer with the given id exists and is known
|
||||||
|
// to the system.
|
||||||
|
func LayerExists(path string) (bool, error) {
|
||||||
|
title := "hcsshim::LayerExists "
|
||||||
|
logrus.Debugf(title+"path %s", path)
|
||||||
|
|
||||||
|
// Call the procedure itself.
|
||||||
|
var exists uint32
|
||||||
|
err := layerExists(&stdDriverInfo, path, &exists)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.Errorf(err, title, "path=%s", path)
|
||||||
|
logrus.Error(err)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"succeeded path=%s exists=%d", path, exists)
|
||||||
|
return exists != 0, nil
|
||||||
|
}
|
||||||
13
vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go
generated
vendored
Normal file
13
vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/guid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LayerID returns the layer ID of a layer on disk.
|
||||||
|
func LayerID(path string) (guid.GUID, error) {
|
||||||
|
_, file := filepath.Split(path)
|
||||||
|
return NameToGuid(file)
|
||||||
|
}
|
||||||
96
vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go
generated
vendored
Normal file
96
vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
// This file contains utility functions to support storage (graph) related
|
||||||
|
// functionality.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/guid"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
/* To pass into syscall, we need a struct matching the following:
|
||||||
|
enum GraphDriverType
|
||||||
|
{
|
||||||
|
DiffDriver,
|
||||||
|
FilterDriver
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DriverInfo {
|
||||||
|
GraphDriverType Flavour;
|
||||||
|
LPCWSTR HomeDir;
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
type driverInfo struct {
|
||||||
|
Flavour int
|
||||||
|
HomeDirp *uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
utf16EmptyString uint16
|
||||||
|
stdDriverInfo = driverInfo{1, &utf16EmptyString}
|
||||||
|
)
|
||||||
|
|
||||||
|
/* To pass into syscall, we need a struct matching the following:
|
||||||
|
typedef struct _WC_LAYER_DESCRIPTOR {
|
||||||
|
|
||||||
|
//
|
||||||
|
// The ID of the layer
|
||||||
|
//
|
||||||
|
|
||||||
|
GUID LayerId;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Additional flags
|
||||||
|
//
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
ULONG Reserved : 31;
|
||||||
|
ULONG Dirty : 1; // Created from sandbox as a result of snapshot
|
||||||
|
};
|
||||||
|
ULONG Value;
|
||||||
|
} Flags;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Path to the layer root directory, null-terminated
|
||||||
|
//
|
||||||
|
|
||||||
|
PCWSTR Path;
|
||||||
|
|
||||||
|
} WC_LAYER_DESCRIPTOR, *PWC_LAYER_DESCRIPTOR;
|
||||||
|
*/
|
||||||
|
type WC_LAYER_DESCRIPTOR struct {
|
||||||
|
LayerId guid.GUID
|
||||||
|
Flags uint32
|
||||||
|
Pathp *uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func layerPathsToDescriptors(parentLayerPaths []string) ([]WC_LAYER_DESCRIPTOR, error) {
|
||||||
|
// Array of descriptors that gets constructed.
|
||||||
|
var layers []WC_LAYER_DESCRIPTOR
|
||||||
|
|
||||||
|
for i := 0; i < len(parentLayerPaths); i++ {
|
||||||
|
g, err := LayerID(parentLayerPaths[i])
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("Failed to convert name to guid %s", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := syscall.UTF16PtrFromString(parentLayerPaths[i])
|
||||||
|
if err != nil {
|
||||||
|
logrus.Debugf("Failed conversion of parentLayerPath to pointer %s", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
layers = append(layers, WC_LAYER_DESCRIPTOR{
|
||||||
|
LayerId: g,
|
||||||
|
Flags: 0,
|
||||||
|
Pathp: p,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return layers, nil
|
||||||
|
}
|
||||||
815
vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go
generated
vendored
Normal file
815
vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go
generated
vendored
Normal file
@@ -0,0 +1,815 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/Microsoft/go-winio"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/longpath"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/safefile"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errorIterationCanceled = errors.New("")
|
||||||
|
|
||||||
|
var mutatedUtilityVMFiles = map[string]bool{
|
||||||
|
`EFI\Microsoft\Boot\BCD`: true,
|
||||||
|
`EFI\Microsoft\Boot\BCD.LOG`: true,
|
||||||
|
`EFI\Microsoft\Boot\BCD.LOG1`: true,
|
||||||
|
`EFI\Microsoft\Boot\BCD.LOG2`: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
filesPath = `Files`
|
||||||
|
hivesPath = `Hives`
|
||||||
|
utilityVMPath = `UtilityVM`
|
||||||
|
utilityVMFilesPath = `UtilityVM\Files`
|
||||||
|
)
|
||||||
|
|
||||||
|
func openFileOrDir(path string, mode uint32, createDisposition uint32) (file *os.File, err error) {
|
||||||
|
return winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createDisposition)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasPathPrefix(p, prefix string) bool {
|
||||||
|
return strings.HasPrefix(p, prefix) && len(p) > len(prefix) && p[len(prefix)] == '\\'
|
||||||
|
}
|
||||||
|
|
||||||
|
type fileEntry struct {
|
||||||
|
path string
|
||||||
|
fi os.FileInfo
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
type legacyLayerReader struct {
|
||||||
|
root string
|
||||||
|
result chan *fileEntry
|
||||||
|
proceed chan bool
|
||||||
|
currentFile *os.File
|
||||||
|
backupReader *winio.BackupFileReader
|
||||||
|
}
|
||||||
|
|
||||||
|
// newLegacyLayerReader returns a new LayerReader that can read the Windows
|
||||||
|
// container layer transport format from disk.
|
||||||
|
func newLegacyLayerReader(root string) *legacyLayerReader {
|
||||||
|
r := &legacyLayerReader{
|
||||||
|
root: root,
|
||||||
|
result: make(chan *fileEntry),
|
||||||
|
proceed: make(chan bool),
|
||||||
|
}
|
||||||
|
go r.walk()
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTombstones(path string) (map[string]([]string), error) {
|
||||||
|
tf, err := os.Open(filepath.Join(path, "tombstones.txt"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer tf.Close()
|
||||||
|
s := bufio.NewScanner(tf)
|
||||||
|
if !s.Scan() || s.Text() != "\xef\xbb\xbfVersion 1.0" {
|
||||||
|
return nil, errors.New("Invalid tombstones file")
|
||||||
|
}
|
||||||
|
|
||||||
|
ts := make(map[string]([]string))
|
||||||
|
for s.Scan() {
|
||||||
|
t := filepath.Join(filesPath, s.Text()[1:]) // skip leading `\`
|
||||||
|
dir := filepath.Dir(t)
|
||||||
|
ts[dir] = append(ts[dir], t)
|
||||||
|
}
|
||||||
|
if err = s.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReader) walkUntilCancelled() error {
|
||||||
|
root, err := longpath.LongAbs(r.root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r.root = root
|
||||||
|
ts, err := readTombstones(r.root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = filepath.Walk(r.root, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
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$") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r.result <- &fileEntry{path, info, nil}
|
||||||
|
if !<-r.proceed {
|
||||||
|
return errorIterationCanceled
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all the tombstones.
|
||||||
|
if info.IsDir() {
|
||||||
|
relPath, err := filepath.Rel(r.root, path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dts, ok := ts[relPath]; ok {
|
||||||
|
for _, t := range dts {
|
||||||
|
r.result <- &fileEntry{filepath.Join(r.root, t), nil, nil}
|
||||||
|
if !<-r.proceed {
|
||||||
|
return errorIterationCanceled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err == errorIterationCanceled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReader) walk() {
|
||||||
|
defer close(r.result)
|
||||||
|
if !<-r.proceed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.walkUntilCancelled()
|
||||||
|
if err != nil {
|
||||||
|
for {
|
||||||
|
r.result <- &fileEntry{err: err}
|
||||||
|
if !<-r.proceed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReader) reset() {
|
||||||
|
if r.backupReader != nil {
|
||||||
|
r.backupReader.Close()
|
||||||
|
r.backupReader = nil
|
||||||
|
}
|
||||||
|
if r.currentFile != nil {
|
||||||
|
r.currentFile.Close()
|
||||||
|
r.currentFile = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findBackupStreamSize(r io.Reader) (int64, error) {
|
||||||
|
br := winio.NewBackupStreamReader(r)
|
||||||
|
for {
|
||||||
|
hdr, err := br.Next()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if hdr.Id == winio.BackupData {
|
||||||
|
return hdr.Size, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReader) Next() (path string, size int64, fileInfo *winio.FileBasicInfo, err error) {
|
||||||
|
r.reset()
|
||||||
|
r.proceed <- true
|
||||||
|
fe := <-r.result
|
||||||
|
if fe == nil {
|
||||||
|
err = errors.New("LegacyLayerReader closed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if fe.err != nil {
|
||||||
|
err = fe.err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
path, err = filepath.Rel(r.root, fe.path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if fe.fi == nil {
|
||||||
|
// This is a tombstone. Return a nil fileInfo.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if fe.fi.IsDir() && hasPathPrefix(path, filesPath) {
|
||||||
|
fe.path += ".$wcidirs$"
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := openFileOrDir(fe.path, syscall.GENERIC_READ, syscall.OPEN_EXISTING)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
fileInfo, err = winio.GetFileBasicInfo(f)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasPathPrefix(path, filesPath) {
|
||||||
|
size = fe.fi.Size()
|
||||||
|
r.backupReader = winio.NewBackupFileReader(f, false)
|
||||||
|
if path == hivesPath || path == filesPath {
|
||||||
|
// The Hives directory has a non-deterministic file time because of the
|
||||||
|
// nature of the import process. Use the times from System_Delta.
|
||||||
|
var g *os.File
|
||||||
|
g, err = os.Open(filepath.Join(r.root, hivesPath, `System_Delta`))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
attr := fileInfo.FileAttributes
|
||||||
|
fileInfo, err = winio.GetFileBasicInfo(g)
|
||||||
|
g.Close()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileInfo.FileAttributes = attr
|
||||||
|
}
|
||||||
|
|
||||||
|
// The creation time and access time get reset for files outside of the Files path.
|
||||||
|
fileInfo.CreationTime = fileInfo.LastWriteTime
|
||||||
|
fileInfo.LastAccessTime = fileInfo.LastWriteTime
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// The file attributes are written before the backup stream.
|
||||||
|
var attr uint32
|
||||||
|
err = binary.Read(f, binary.LittleEndian, &attr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fileInfo.FileAttributes = attr
|
||||||
|
beginning := int64(4)
|
||||||
|
|
||||||
|
// Find the accurate file size.
|
||||||
|
if !fe.fi.IsDir() {
|
||||||
|
size, err = findBackupStreamSize(f)
|
||||||
|
if err != nil {
|
||||||
|
err = &os.PathError{Op: "findBackupStreamSize", Path: fe.path, Err: err}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return back to the beginning of the backup stream.
|
||||||
|
_, err = f.Seek(beginning, 0)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.currentFile = f
|
||||||
|
f = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReader) Read(b []byte) (int, error) {
|
||||||
|
if r.backupReader == nil {
|
||||||
|
if r.currentFile == nil {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
return r.currentFile.Read(b)
|
||||||
|
}
|
||||||
|
return r.backupReader.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReader) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
if r.backupReader == nil {
|
||||||
|
if r.currentFile == nil {
|
||||||
|
return 0, errors.New("no current file")
|
||||||
|
}
|
||||||
|
return r.currentFile.Seek(offset, whence)
|
||||||
|
}
|
||||||
|
return 0, errors.New("seek not supported on this stream")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *legacyLayerReader) Close() error {
|
||||||
|
r.proceed <- false
|
||||||
|
<-r.result
|
||||||
|
r.reset()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type pendingLink struct {
|
||||||
|
Path, Target string
|
||||||
|
TargetRoot *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
type pendingDir struct {
|
||||||
|
Path string
|
||||||
|
Root *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
type legacyLayerWriter struct {
|
||||||
|
root *os.File
|
||||||
|
destRoot *os.File
|
||||||
|
parentRoots []*os.File
|
||||||
|
currentFile *os.File
|
||||||
|
bufWriter *bufio.Writer
|
||||||
|
currentFileName string
|
||||||
|
currentFileRoot *os.File
|
||||||
|
backupWriter *winio.BackupFileWriter
|
||||||
|
Tombstones []string
|
||||||
|
HasUtilityVM bool
|
||||||
|
uvmDi []dirInfo
|
||||||
|
addedFiles map[string]bool
|
||||||
|
PendingLinks []pendingLink
|
||||||
|
pendingDirs []pendingDir
|
||||||
|
currentIsDir bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// newLegacyLayerWriter returns a LayerWriter that can write the contaler layer
|
||||||
|
// transport format to disk.
|
||||||
|
func newLegacyLayerWriter(root string, parentRoots []string, destRoot string) (w *legacyLayerWriter, err error) {
|
||||||
|
w = &legacyLayerWriter{
|
||||||
|
addedFiles: make(map[string]bool),
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
w.CloseRoots()
|
||||||
|
w = nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
w.root, err = safefile.OpenRoot(root)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.destRoot, err = safefile.OpenRoot(destRoot)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, r := range parentRoots {
|
||||||
|
f, err := safefile.OpenRoot(r)
|
||||||
|
if err != nil {
|
||||||
|
return w, err
|
||||||
|
}
|
||||||
|
w.parentRoots = append(w.parentRoots, f)
|
||||||
|
}
|
||||||
|
w.bufWriter = bufio.NewWriterSize(ioutil.Discard, 65536)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *legacyLayerWriter) CloseRoots() {
|
||||||
|
if w.root != nil {
|
||||||
|
w.root.Close()
|
||||||
|
w.root = 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 {
|
||||||
|
if !w.HasUtilityVM {
|
||||||
|
err := safefile.MkdirRelative(utilityVMPath, w.destRoot)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Server 2016 does not support multiple layers for the utility VM, so
|
||||||
|
// clone the utility VM from the parent layer into this layer. Use hard
|
||||||
|
// links to avoid unnecessary copying, since most of the files are
|
||||||
|
// immutable.
|
||||||
|
err = cloneTree(w.parentRoots[0], w.destRoot, utilityVMFilesPath, mutatedUtilityVMFiles)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cloning the parent utility VM image failed: %s", err)
|
||||||
|
}
|
||||||
|
w.HasUtilityVM = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *legacyLayerWriter) reset() error {
|
||||||
|
err := w.bufWriter.Flush()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.bufWriter.Reset(ioutil.Discard)
|
||||||
|
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 := safefile.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 {
|
||||||
|
w.backupWriter.Close()
|
||||||
|
w.backupWriter = nil
|
||||||
|
}
|
||||||
|
if w.currentFile != nil {
|
||||||
|
w.currentFile.Close()
|
||||||
|
w.currentFile = nil
|
||||||
|
w.currentFileName = ""
|
||||||
|
w.currentFileRoot = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyFileWithMetadata copies a file using the backup/restore APIs in order to preserve metadata
|
||||||
|
func copyFileWithMetadata(srcRoot, destRoot *os.File, subPath string, isDir bool) (fileInfo *winio.FileBasicInfo, err error) {
|
||||||
|
src, err := safefile.OpenRelative(
|
||||||
|
subPath,
|
||||||
|
srcRoot,
|
||||||
|
syscall.GENERIC_READ|winio.ACCESS_SYSTEM_SECURITY,
|
||||||
|
syscall.FILE_SHARE_READ,
|
||||||
|
safefile.FILE_OPEN,
|
||||||
|
safefile.FILE_OPEN_REPARSE_POINT)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer src.Close()
|
||||||
|
srcr := winio.NewBackupFileReader(src, true)
|
||||||
|
defer srcr.Close()
|
||||||
|
|
||||||
|
fileInfo, err = winio.GetFileBasicInfo(src)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
extraFlags := uint32(0)
|
||||||
|
if isDir {
|
||||||
|
extraFlags |= safefile.FILE_DIRECTORY_FILE
|
||||||
|
}
|
||||||
|
dest, err := safefile.OpenRelative(
|
||||||
|
subPath,
|
||||||
|
destRoot,
|
||||||
|
syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY,
|
||||||
|
syscall.FILE_SHARE_READ,
|
||||||
|
safefile.FILE_CREATE,
|
||||||
|
extraFlags)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer dest.Close()
|
||||||
|
|
||||||
|
err = winio.SetFileBasicInfo(dest, fileInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
destw := winio.NewBackupFileWriter(dest, true)
|
||||||
|
defer func() {
|
||||||
|
cerr := destw.Close()
|
||||||
|
if err == nil {
|
||||||
|
err = cerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, err = io.Copy(destw, srcr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
func cloneTree(srcRoot *os.File, destRoot *os.File, subPath string, mutatedFiles map[string]bool) error {
|
||||||
|
var di []dirInfo
|
||||||
|
err := safefile.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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
relPath, err := filepath.Rel(srcRoot.Name(), srcFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fileAttributes := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes
|
||||||
|
// Directories, reparse points, and files that will be mutated during
|
||||||
|
// utility VM import must be copied. All other files can be hard linked.
|
||||||
|
isReparsePoint := fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0
|
||||||
|
// In go1.9, FileInfo.IsDir() returns false if the directory is also a symlink.
|
||||||
|
// 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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if isDir && !isReparsePoint {
|
||||||
|
di = append(di, dirInfo{path: relPath, fileInfo: *fi})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = safefile.LinkRelative(relPath, srcRoot, relPath, destRoot)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return reapplyDirectoryTimes(destRoot, di)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error {
|
||||||
|
if err := w.reset(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if name == utilityVMPath {
|
||||||
|
return w.initUtilityVM()
|
||||||
|
}
|
||||||
|
|
||||||
|
name = filepath.Clean(name)
|
||||||
|
if hasPathPrefix(name, utilityVMPath) {
|
||||||
|
if !w.HasUtilityVM {
|
||||||
|
return errors.New("missing UtilityVM directory")
|
||||||
|
}
|
||||||
|
if !hasPathPrefix(name, utilityVMFilesPath) && name != utilityVMFilesPath {
|
||||||
|
return errors.New("invalid UtilityVM layer")
|
||||||
|
}
|
||||||
|
createDisposition := uint32(safefile.FILE_OPEN)
|
||||||
|
if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
|
||||||
|
st, err := safefile.LstatRelative(name, w.destRoot)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if st != nil {
|
||||||
|
// Delete the existing file/directory if it is not the same type as this directory.
|
||||||
|
existingAttr := st.Sys().(*syscall.Win32FileAttributeData).FileAttributes
|
||||||
|
if (uint32(fileInfo.FileAttributes)^existingAttr)&(syscall.FILE_ATTRIBUTE_DIRECTORY|syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 {
|
||||||
|
if err = safefile.RemoveAllRelative(name, w.destRoot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
st = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if st == nil {
|
||||||
|
if err = safefile.MkdirRelative(name, w.destRoot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||||
|
w.uvmDi = append(w.uvmDi, dirInfo{path: name, fileInfo: *fileInfo})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Overwrite any existing hard link.
|
||||||
|
err := safefile.RemoveRelative(name, w.destRoot)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
createDisposition = safefile.FILE_CREATE
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := safefile.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,
|
||||||
|
safefile.FILE_OPEN_REPARSE_POINT,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
safefile.RemoveRelative(name, w.destRoot)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = winio.SetFileBasicInfo(f, fileInfo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.backupWriter = winio.NewBackupFileWriter(f, true)
|
||||||
|
w.bufWriter.Reset(w.backupWriter)
|
||||||
|
w.currentFile = f
|
||||||
|
w.currentFileName = name
|
||||||
|
w.currentFileRoot = w.destRoot
|
||||||
|
w.addedFiles[name] = true
|
||||||
|
f = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fname := name
|
||||||
|
if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
|
||||||
|
err := safefile.MkdirRelative(name, w.root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fname += ".$wcidirs$"
|
||||||
|
w.currentIsDir = true
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := safefile.OpenRelative(fname, w.root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, safefile.FILE_CREATE, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
safefile.RemoveRelative(fname, w.root)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
strippedFi := *fileInfo
|
||||||
|
strippedFi.FileAttributes = 0
|
||||||
|
err = winio.SetFileBasicInfo(f, &strippedFi)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasPathPrefix(name, hivesPath) {
|
||||||
|
w.backupWriter = winio.NewBackupFileWriter(f, false)
|
||||||
|
w.bufWriter.Reset(w.backupWriter)
|
||||||
|
} else {
|
||||||
|
w.bufWriter.Reset(f)
|
||||||
|
// The file attributes are written before the stream.
|
||||||
|
err = binary.Write(w.bufWriter, binary.LittleEndian, uint32(fileInfo.FileAttributes))
|
||||||
|
if err != nil {
|
||||||
|
w.bufWriter.Reset(ioutil.Discard)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.currentFile = f
|
||||||
|
w.currentFileName = name
|
||||||
|
w.currentFileRoot = w.root
|
||||||
|
w.addedFiles[name] = true
|
||||||
|
f = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *legacyLayerWriter) AddLink(name string, target string) error {
|
||||||
|
if err := w.reset(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
target = filepath.Clean(target)
|
||||||
|
var roots []*os.File
|
||||||
|
if hasPathPrefix(target, filesPath) {
|
||||||
|
// Look for cross-layer hard link targets in the parent layers, since
|
||||||
|
// nothing is in the destination path yet.
|
||||||
|
roots = w.parentRoots
|
||||||
|
} else if hasPathPrefix(target, utilityVMFilesPath) {
|
||||||
|
// Since the utility VM is fully cloned into the destination path
|
||||||
|
// already, look for cross-layer hard link targets directly in the
|
||||||
|
// destination path.
|
||||||
|
roots = []*os.File{w.destRoot}
|
||||||
|
}
|
||||||
|
|
||||||
|
if roots == nil || (!hasPathPrefix(name, filesPath) && !hasPathPrefix(name, utilityVMFilesPath)) {
|
||||||
|
return errors.New("invalid hard link in layer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find to try the target of the link in a previously added file. If that
|
||||||
|
// fails, search in parent layers.
|
||||||
|
var selectedRoot *os.File
|
||||||
|
if _, ok := w.addedFiles[target]; ok {
|
||||||
|
selectedRoot = w.destRoot
|
||||||
|
} else {
|
||||||
|
for _, r := range roots {
|
||||||
|
if _, err := safefile.LstatRelative(target, r); err != nil {
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectedRoot = r
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if selectedRoot == nil {
|
||||||
|
return fmt.Errorf("failed to find link target for '%s' -> '%s'", name, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The link can't be written until after the ImportLayer call.
|
||||||
|
w.PendingLinks = append(w.PendingLinks, pendingLink{
|
||||||
|
Path: name,
|
||||||
|
Target: target,
|
||||||
|
TargetRoot: selectedRoot,
|
||||||
|
})
|
||||||
|
w.addedFiles[name] = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *legacyLayerWriter) Remove(name string) error {
|
||||||
|
name = filepath.Clean(name)
|
||||||
|
if hasPathPrefix(name, filesPath) {
|
||||||
|
w.Tombstones = append(w.Tombstones, name)
|
||||||
|
} else if hasPathPrefix(name, utilityVMFilesPath) {
|
||||||
|
err := w.initUtilityVM()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
// purposes.
|
||||||
|
if _, err := safefile.LstatRelative(name, w.destRoot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = safefile.RemoveAllRelative(name, w.destRoot)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("invalid tombstone %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *legacyLayerWriter) Write(b []byte) (int, error) {
|
||||||
|
if w.backupWriter == nil && w.currentFile == nil {
|
||||||
|
return 0, errors.New("closed")
|
||||||
|
}
|
||||||
|
return w.bufWriter.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *legacyLayerWriter) Close() error {
|
||||||
|
if err := w.reset(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := safefile.RemoveRelative("tombstones.txt", w.root); err != nil && !os.IsNotExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, pd := range w.pendingDirs {
|
||||||
|
err := safefile.MkdirRelative(pd.Path, pd.Root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if w.HasUtilityVM {
|
||||||
|
err := reapplyDirectoryTimes(w.destRoot, w.uvmDi)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
24
vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go
generated
vendored
Normal file
24
vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Microsoft/hcsshim/internal/guid"
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NameToGuid converts the given string into a GUID using the algorithm in the
|
||||||
|
// Host Compute Service, ensuring GUIDs generated with the same string are common
|
||||||
|
// across all clients.
|
||||||
|
func NameToGuid(name string) (id guid.GUID, err error) {
|
||||||
|
title := "hcsshim::NameToGuid "
|
||||||
|
|
||||||
|
err = nameToGuid(name, &id)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.Errorf(err, title, "name=%s", name)
|
||||||
|
logrus.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"name:%s guid:%s", name, id.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
40
vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go
generated
vendored
Normal file
40
vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package wclayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/internal/hcserror"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var prepareLayerLock sync.Mutex
|
||||||
|
|
||||||
|
// PrepareLayer finds a mounted read-write layer matching path and enables the
|
||||||
|
// the filesystem filter for use on that layer. This requires the paths to all
|
||||||
|
// parent layers, and is necessary in order to view or interact with the layer
|
||||||
|
// as an actual filesystem (reading and writing files, creating directories, etc).
|
||||||
|
// Disabling the filter must be done via UnprepareLayer.
|
||||||
|
func PrepareLayer(path string, parentLayerPaths []string) error {
|
||||||
|
title := "hcsshim::PrepareLayer "
|
||||||
|
logrus.Debugf(title+"path %s", path)
|
||||||
|
|
||||||
|
// Generate layer descriptors
|
||||||
|
layers, err := layerPathsToDescriptors(parentLayerPaths)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// This lock is a temporary workaround for a Windows bug. Only allowing one
|
||||||
|
// call to prepareLayer at a time vastly reduces the chance of a timeout.
|
||||||
|
prepareLayerLock.Lock()
|
||||||
|
defer prepareLayerLock.Unlock()
|
||||||
|
err = prepareLayer(&stdDriverInfo, path, layers)
|
||||||
|
if err != nil {
|
||||||
|
err = hcserror.Errorf(err, title, "path=%s", path)
|
||||||
|
logrus.Error(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf(title+"succeeded path=%s", path)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user