From d4517bc26f3c0f917c3070f173d334cb0bdfa5aa Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Wed, 8 Nov 2017 10:58:42 +0000 Subject: [PATCH 01/11] tool: Move the code for parsing published ports to utils.go This code will soon be shared by other backends. While at it, also rename the type to PublishPort (from publishPorts) as it is just one Port and the function from splitPublish() to NewPublishPort() as this seems more go like. Signed-off-by: Rolf Neugebauer --- src/cmd/linuxkit/run_qemu.go | 54 ++---------------------------------- src/cmd/linuxkit/util.go | 52 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 52 deletions(-) diff --git a/src/cmd/linuxkit/run_qemu.go b/src/cmd/linuxkit/run_qemu.go index 8dec12943..e1a6cd4ab 100644 --- a/src/cmd/linuxkit/run_qemu.go +++ b/src/cmd/linuxkit/run_qemu.go @@ -620,12 +620,6 @@ func discoverBackend(config QemuConfig) QemuConfig { type multipleFlag []string -type publishedPorts struct { - guest int - host int - protocol string -} - func (f *multipleFlag) String() string { return "A multiple flag is a type of flag that can be repeated any number of times" } @@ -635,57 +629,13 @@ func (f *multipleFlag) Set(value string) error { return nil } -func splitPublish(publish string) (publishedPorts, error) { - p := publishedPorts{} - slice := strings.Split(publish, ":") - - if len(slice) < 2 { - return p, fmt.Errorf("Unable to parse the ports to be published, should be in format : or :/") - } - - hostPort, err := strconv.Atoi(slice[0]) - - if err != nil { - return p, fmt.Errorf("The provided hostPort can't be converted to int") - } - - right := strings.Split(slice[1], "/") - - protocol := "tcp" - if len(right) == 2 { - protocol = strings.TrimSpace(strings.ToLower(right[1])) - } - - if protocol != "tcp" && protocol != "udp" { - return p, fmt.Errorf("Provided protocol is not valid, valid options are: udp and tcp") - } - guestPort, err := strconv.Atoi(right[0]) - - if err != nil { - return p, fmt.Errorf("The provided guestPort can't be converted to int") - } - - if hostPort < 1 || hostPort > 65535 { - return p, fmt.Errorf("Invalid hostPort: %d", hostPort) - } - - if guestPort < 1 || guestPort > 65535 { - return p, fmt.Errorf("Invalid guestPort: %d", guestPort) - } - - p.guest = guestPort - p.host = hostPort - p.protocol = protocol - return p, nil -} - func buildQemuForwardings(publishFlags multipleFlag, containerized bool) (string, error) { if len(publishFlags) == 0 { return "", nil } var forwardings string for _, publish := range publishFlags { - p, err := splitPublish(publish) + p, err := NewPublishedPort(publish) if err != nil { return "", err } @@ -705,7 +655,7 @@ func buildQemuForwardings(publishFlags multipleFlag, containerized bool) (string func buildDockerForwardings(publishedPorts []string) ([]string, error) { pmap := []string{} for _, port := range publishedPorts { - s, err := splitPublish(port) + s, err := NewPublishedPort(port) if err != nil { return nil, err } diff --git a/src/cmd/linuxkit/util.go b/src/cmd/linuxkit/util.go index 39d794cb1..1f02fa622 100644 --- a/src/cmd/linuxkit/util.go +++ b/src/cmd/linuxkit/util.go @@ -191,3 +191,55 @@ func (l *Disks) Set(value string) error { *l = append(*l, d) return nil } + +// PublishedPort is used by some backends to expose a VMs port on the host +type PublishedPort struct { + guest int + host int + protocol string +} + +// NewPublishedPort parses a string of the form :[/] and returns a PublishedPort structure +func NewPublishedPort(publish string) (PublishedPort, error) { + p := PublishedPort{} + slice := strings.Split(publish, ":") + + if len(slice) < 2 { + return p, fmt.Errorf("Unable to parse the ports to be published, should be in format : or :/") + } + + hostPort, err := strconv.Atoi(slice[0]) + + if err != nil { + return p, fmt.Errorf("The provided hostPort can't be converted to int") + } + + right := strings.Split(slice[1], "/") + + protocol := "tcp" + if len(right) == 2 { + protocol = strings.TrimSpace(strings.ToLower(right[1])) + } + + if protocol != "tcp" && protocol != "udp" { + return p, fmt.Errorf("Provided protocol is not valid, valid options are: udp and tcp") + } + guestPort, err := strconv.Atoi(right[0]) + + if err != nil { + return p, fmt.Errorf("The provided guestPort can't be converted to int") + } + + if hostPort < 1 || hostPort > 65535 { + return p, fmt.Errorf("Invalid hostPort: %d", hostPort) + } + + if guestPort < 1 || guestPort > 65535 { + return p, fmt.Errorf("Invalid guestPort: %d", guestPort) + } + + p.guest = guestPort + p.host = hostPort + p.protocol = protocol + return p, nil +} From a272eba74042b2a48b48d05275dcefeb07e0ee37 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Wed, 8 Nov 2017 15:18:20 +0000 Subject: [PATCH 02/11] tool: Make ports uint16 The type of the guest and host ports should be uint16 not int. Also make them public member of the PublishPort structure. Signed-off-by: Rolf Neugebauer --- src/cmd/linuxkit/run_qemu.go | 8 ++++---- src/cmd/linuxkit/util.go | 23 ++++++++++------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/cmd/linuxkit/run_qemu.go b/src/cmd/linuxkit/run_qemu.go index e1a6cd4ab..86b6ff119 100644 --- a/src/cmd/linuxkit/run_qemu.go +++ b/src/cmd/linuxkit/run_qemu.go @@ -640,13 +640,13 @@ func buildQemuForwardings(publishFlags multipleFlag, containerized bool) (string return "", err } - hostPort := p.host - guestPort := p.guest + hostPort := p.Host + guestPort := p.Guest if containerized { hostPort = guestPort } - forwardings = fmt.Sprintf("%s,hostfwd=%s::%d-:%d", forwardings, p.protocol, hostPort, guestPort) + forwardings = fmt.Sprintf("%s,hostfwd=%s::%d-:%d", forwardings, p.Protocol, hostPort, guestPort) } return forwardings, nil @@ -659,7 +659,7 @@ func buildDockerForwardings(publishedPorts []string) ([]string, error) { if err != nil { return nil, err } - pmap = append(pmap, "-p", fmt.Sprintf("%d:%d/%s", s.host, s.guest, s.protocol)) + pmap = append(pmap, "-p", fmt.Sprintf("%d:%d/%s", s.Host, s.Guest, s.Protocol)) } return pmap, nil } diff --git a/src/cmd/linuxkit/util.go b/src/cmd/linuxkit/util.go index 1f02fa622..293bc95e2 100644 --- a/src/cmd/linuxkit/util.go +++ b/src/cmd/linuxkit/util.go @@ -194,9 +194,9 @@ func (l *Disks) Set(value string) error { // PublishedPort is used by some backends to expose a VMs port on the host type PublishedPort struct { - guest int - host int - protocol string + Guest uint16 + Host uint16 + Protocol string } // NewPublishedPort parses a string of the form :[/] and returns a PublishedPort structure @@ -208,10 +208,9 @@ func NewPublishedPort(publish string) (PublishedPort, error) { return p, fmt.Errorf("Unable to parse the ports to be published, should be in format : or :/") } - hostPort, err := strconv.Atoi(slice[0]) - + hostPort, err := strconv.ParseUint(slice[0], 10, 16) if err != nil { - return p, fmt.Errorf("The provided hostPort can't be converted to int") + return p, fmt.Errorf("The provided hostPort can't be converted to uint16") } right := strings.Split(slice[1], "/") @@ -220,26 +219,24 @@ func NewPublishedPort(publish string) (PublishedPort, error) { if len(right) == 2 { protocol = strings.TrimSpace(strings.ToLower(right[1])) } - if protocol != "tcp" && protocol != "udp" { return p, fmt.Errorf("Provided protocol is not valid, valid options are: udp and tcp") } - guestPort, err := strconv.Atoi(right[0]) + guestPort, err := strconv.ParseUint(right[0], 10, 16) if err != nil { - return p, fmt.Errorf("The provided guestPort can't be converted to int") + return p, fmt.Errorf("The provided guestPort can't be converted to uint16") } if hostPort < 1 || hostPort > 65535 { return p, fmt.Errorf("Invalid hostPort: %d", hostPort) } - if guestPort < 1 || guestPort > 65535 { return p, fmt.Errorf("Invalid guestPort: %d", guestPort) } - p.guest = guestPort - p.host = hostPort - p.protocol = protocol + p.Guest = uint16(guestPort) + p.Host = uint16(hostPort) + p.Protocol = protocol return p, nil } From fe953f6bc2aee98d2ae603aaae06e813a861b841 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Wed, 8 Nov 2017 13:30:30 +0000 Subject: [PATCH 03/11] tool: Move multipleFlag handling to utils.go It's a generic thing and may be shared by other code. Signed-off-by: Rolf Neugebauer --- src/cmd/linuxkit/run_qemu.go | 11 ----------- src/cmd/linuxkit/util.go | 12 ++++++++++++ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/cmd/linuxkit/run_qemu.go b/src/cmd/linuxkit/run_qemu.go index 86b6ff119..859715f53 100644 --- a/src/cmd/linuxkit/run_qemu.go +++ b/src/cmd/linuxkit/run_qemu.go @@ -618,17 +618,6 @@ func discoverBackend(config QemuConfig) QemuConfig { return config } -type multipleFlag []string - -func (f *multipleFlag) String() string { - return "A multiple flag is a type of flag that can be repeated any number of times" -} - -func (f *multipleFlag) Set(value string) error { - *f = append(*f, value) - return nil -} - func buildQemuForwardings(publishFlags multipleFlag, containerized bool) (string, error) { if len(publishFlags) == 0 { return "", nil diff --git a/src/cmd/linuxkit/util.go b/src/cmd/linuxkit/util.go index 293bc95e2..33c665486 100644 --- a/src/cmd/linuxkit/util.go +++ b/src/cmd/linuxkit/util.go @@ -8,6 +8,18 @@ import ( "strings" ) +// Handle flags with multiple occurrences +type multipleFlag []string + +func (f *multipleFlag) String() string { + return "A multiple flag is a type of flag that can be repeated any number of times" +} + +func (f *multipleFlag) Set(value string) error { + *f = append(*f, value) + return nil +} + func getStringValue(envKey string, flagVal string, defaultVal string) string { var res string From 6a159585e92bd4b109fcbd6be99e5f9bfcee9c86 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Wed, 8 Nov 2017 14:05:56 +0000 Subject: [PATCH 04/11] Vendor vpnkit go bindings Signed-off-by: Rolf Neugebauer --- src/cmd/linuxkit/vendor.conf | 4 + .../github.com/docker/docker/hack/README.md | 60 -- .../hack/integration-cli-on-swarm/README.md | 69 -- .../agent/vendor.conf | 2 - .../vendor/github.com/docker/go-p9p/LICENSE | 202 +++++ .../vendor/github.com/docker/go-p9p/README.md | 11 + .../github.com/docker/go-p9p/channel.go | 343 ++++++++ .../vendor/github.com/docker/go-p9p/client.go | 253 ++++++ .../github.com/docker/go-p9p/context.go | 26 + .../github.com/docker/go-p9p/dispatcher.go | 133 ++++ .../vendor/github.com/docker/go-p9p/doc.go | 78 ++ .../github.com/docker/go-p9p/encoding.go | 564 ++++++++++++++ .../vendor/github.com/docker/go-p9p/errors.go | 58 ++ .../vendor/github.com/docker/go-p9p/fcall.go | 142 ++++ .../github.com/docker/go-p9p/messages.go | 216 ++++++ .../github.com/docker/go-p9p/overflow.go | 49 ++ .../github.com/docker/go-p9p/readdir.go | 93 +++ .../vendor/github.com/docker/go-p9p/server.go | 257 ++++++ .../github.com/docker/go-p9p/session.go | 42 + .../github.com/docker/go-p9p/transport.go | 250 ++++++ .../vendor/github.com/docker/go-p9p/types.go | 149 ++++ .../github.com/docker/go-p9p/version.go | 112 +++ .../vendor/github.com/google/uuid/LICENSE | 27 + .../vendor/github.com/google/uuid/README.md | 23 + .../vendor/github.com/google/uuid/dce.go | 80 ++ .../vendor/github.com/google/uuid/doc.go | 12 + .../vendor/github.com/google/uuid/hash.go | 53 ++ .../vendor/github.com/google/uuid/marshal.go | 39 + .../vendor/github.com/google/uuid/node.go | 103 +++ .../vendor/github.com/google/uuid/sql.go | 59 ++ .../vendor/github.com/google/uuid/time.go | 123 +++ .../vendor/github.com/google/uuid/util.go | 43 + .../vendor/github.com/google/uuid/uuid.go | 191 +++++ .../vendor/github.com/google/uuid/version1.go | 44 ++ .../vendor/github.com/google/uuid/version4.go | 38 + .../vendor/github.com/moby/datakit/LICENSE.md | 191 +++++ .../vendor/github.com/moby/datakit/README.md | 112 +++ .../moby/datakit/api/go-datakit/README | 1 + .../moby/datakit/api/go-datakit/client.go | 352 +++++++++ .../moby/datakit/api/go-datakit/config.go | 380 +++++++++ .../moby/datakit/api/go-datakit/doc.go | 5 + .../moby/datakit/api/go-datakit/snapshot.go | 87 +++ .../datakit/api/go-datakit/transaction.go | 136 ++++ .../moby/datakit/api/go-datakit/watch.go | 77 ++ .../vendor/github.com/moby/vpnkit/LICENSE | 191 +++++ .../vendor/github.com/moby/vpnkit/README.md | 78 ++ .../c/vpnkit-9pmount-vsock/9pmount-vsock.c | 248 ++++++ .../vpnkit/c/vpnkit-9pmount-vsock/hvsock.c | 37 + .../vpnkit/c/vpnkit-9pmount-vsock/hvsock.h | 48 ++ .../moby/vpnkit/c/vpnkit-tap-vsockd/hvsock.c | 40 + .../moby/vpnkit/c/vpnkit-tap-vsockd/hvsock.h | 48 ++ .../vpnkit/c/vpnkit-tap-vsockd/protocol.c | 225 ++++++ .../vpnkit/c/vpnkit-tap-vsockd/protocol.h | 77 ++ .../moby/vpnkit/c/vpnkit-tap-vsockd/ring.c | 269 +++++++ .../moby/vpnkit/c/vpnkit-tap-vsockd/ring.h | 35 + .../vpnkit/c/vpnkit-tap-vsockd/tap-vsockd.c | 732 ++++++++++++++++++ .../moby/vpnkit/go/pkg/vpnkit/connection.go | 22 + .../moby/vpnkit/go/pkg/vpnkit/doc.go | 9 + .../moby/vpnkit/go/pkg/vpnkit/port.go | 200 +++++ .../moby/vpnkit/go/pkg/vpnkit/vmnet.go | 633 +++++++++++++++ .../github.com/moby/vpnkit/go/vendor.conf | 5 + 61 files changed, 8055 insertions(+), 131 deletions(-) delete mode 100644 src/cmd/linuxkit/vendor/github.com/docker/docker/hack/README.md delete mode 100644 src/cmd/linuxkit/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/README.md delete mode 100644 src/cmd/linuxkit/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/agent/vendor.conf create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/LICENSE create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/README.md create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/channel.go create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/client.go create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/context.go create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/dispatcher.go create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/doc.go create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/encoding.go create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/errors.go create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/fcall.go create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/messages.go create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/overflow.go create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/readdir.go create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/server.go create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/session.go create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/transport.go create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/types.go create mode 100644 src/cmd/linuxkit/vendor/github.com/docker/go-p9p/version.go create mode 100644 src/cmd/linuxkit/vendor/github.com/google/uuid/LICENSE create mode 100644 src/cmd/linuxkit/vendor/github.com/google/uuid/README.md create mode 100644 src/cmd/linuxkit/vendor/github.com/google/uuid/dce.go create mode 100644 src/cmd/linuxkit/vendor/github.com/google/uuid/doc.go create mode 100644 src/cmd/linuxkit/vendor/github.com/google/uuid/hash.go create mode 100644 src/cmd/linuxkit/vendor/github.com/google/uuid/marshal.go create mode 100644 src/cmd/linuxkit/vendor/github.com/google/uuid/node.go create mode 100644 src/cmd/linuxkit/vendor/github.com/google/uuid/sql.go create mode 100644 src/cmd/linuxkit/vendor/github.com/google/uuid/time.go create mode 100644 src/cmd/linuxkit/vendor/github.com/google/uuid/util.go create mode 100644 src/cmd/linuxkit/vendor/github.com/google/uuid/uuid.go create mode 100644 src/cmd/linuxkit/vendor/github.com/google/uuid/version1.go create mode 100644 src/cmd/linuxkit/vendor/github.com/google/uuid/version4.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/datakit/LICENSE.md create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/datakit/README.md create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/README create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/client.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/config.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/doc.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/snapshot.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/transaction.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/watch.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/LICENSE create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/README.md create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/9pmount-vsock.c create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/hvsock.c create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/hvsock.h create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/hvsock.c create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/hvsock.h create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/protocol.c create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/protocol.h create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/ring.c create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/ring.h create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/tap-vsockd.c create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/connection.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/doc.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/port.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/vmnet.go create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/vendor.conf diff --git a/src/cmd/linuxkit/vendor.conf b/src/cmd/linuxkit/vendor.conf index f730f4f7d..50791623b 100644 --- a/src/cmd/linuxkit/vendor.conf +++ b/src/cmd/linuxkit/vendor.conf @@ -6,13 +6,17 @@ github.com/aws/aws-sdk-go fa107560b5f3528a859a1a1511086646731bb1a8 github.com/davecgh/go-spew v1.1.0 github.com/dgrijalva/jwt-go 6c8dedd55f8a2e41f605de6d5d66e51ed1f299fc github.com/docker/docker 316b4ba9c2891b9ab4437f1c6a52df2d3d0ca47b +github.com/docker/go-p9p 87ae8514a3a2d9684994a6c319f96ba9e18a062e github.com/go-ini/ini afbc45e87f3ba324c532d12c71918ef52e0fb194 github.com/golang/protobuf c9c7427a2a70d2eb3bafa0ab2dc163e45f143317 +github.com/google/uuid 7e072fc3a7be179aee6d3359e46015aa8c995314 github.com/googleapis/gax-go 8c5154c0fe5bf18cf649634d4c6df50897a32751 github.com/gophercloud/gophercloud 2804b72cf099b41d2e25c8afcca786f9f962ddee github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d github.com/mitchellh/go-ps 4fdf99ab29366514c69ccccddab5dc58b8d84062 +github.com/moby/datakit 97b3d230535397a813323902c23751e176481a86 github.com/moby/hyperkit 3e31617ae866c93925e2b3bc5d8006b60985e920 +github.com/moby/vpnkit 0e4293bb1058598c4b0a406ed171f52573ef414c github.com/packethost/packngo 131798f2804a1b3e895ca98047d56f0d7e094e2a github.com/pmezard/go-difflib v1.0.0 github.com/radu-matei/azure-sdk-for-go 3b12823551999669c9a325a32472508e0af7978e diff --git a/src/cmd/linuxkit/vendor/github.com/docker/docker/hack/README.md b/src/cmd/linuxkit/vendor/github.com/docker/docker/hack/README.md deleted file mode 100644 index 802395d53..000000000 --- a/src/cmd/linuxkit/vendor/github.com/docker/docker/hack/README.md +++ /dev/null @@ -1,60 +0,0 @@ -## About - -This directory contains a collection of scripts used to build and manage this -repository. If there are any issues regarding the intention of a particular -script (or even part of a certain script), please reach out to us. -It may help us either refine our current scripts, or add on new ones -that are appropriate for a given use case. - -## DinD (dind.sh) - -DinD is a wrapper script which allows Docker to be run inside a Docker -container. DinD requires the container to -be run with privileged mode enabled. - -## Generate Authors (generate-authors.sh) - -Generates AUTHORS; a file with all the names and corresponding emails of -individual contributors. AUTHORS can be found in the home directory of -this repository. - -## Make - -There are two make files, each with different extensions. Neither are supposed -to be called directly; only invoke `make`. Both scripts run inside a Docker -container. - -### make.ps1 - -- The Windows native build script that uses PowerShell semantics; it is limited -unlike `hack\make.sh` since it does not provide support for the full set of -operations provided by the Linux counterpart, `make.sh`. However, `make.ps1` -does provide support for local Windows development and Windows to Windows CI. -More information is found within `make.ps1` by the author, @jhowardmsft - -### make.sh - -- Referenced via `make test` when running tests on a local machine, -or directly referenced when running tests inside a Docker development container. -- When running on a local machine, `make test` to run all tests found in -`test`, `test-unit`, `test-integration-cli`, and `test-docker-py` on -your local machine. The default timeout is set in `make.sh` to 60 minutes -(`${TIMEOUT:=60m}`), since it currently takes up to an hour to run -all of the tests. -- When running inside a Docker development container, `hack/make.sh` does -not have a single target that runs all the tests. You need to provide a -single command line with multiple targets that performs the same thing. -An example referenced from [Run targets inside a development container](https://docs.docker.com/opensource/project/test-and-docs/#run-targets-inside-a-development-container): `root@5f8630b873fe:/go/src/github.com/moby/moby# hack/make.sh dynbinary binary cross test-unit test-integration-cli test-docker-py` -- For more information related to testing outside the scope of this README, -refer to -[Run tests and test documentation](https://docs.docker.com/opensource/project/test-and-docs/) - -## Release (release.sh) - -Releases any bundles built by `make` on a public AWS S3 bucket. -For information regarding configuration, please view `release.sh`. - -## Vendor (vendor.sh) - -A shell script that is a wrapper around Vndr. For information on how to use -this, please refer to [vndr's README](https://github.com/LK4D4/vndr/blob/master/README.md) diff --git a/src/cmd/linuxkit/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/README.md b/src/cmd/linuxkit/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/README.md deleted file mode 100644 index 1cea52526..000000000 --- a/src/cmd/linuxkit/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# Integration Testing on Swarm - -IT on Swarm allows you to execute integration test in parallel across a Docker Swarm cluster - -## Architecture - -### Master service - - - Works as a funker caller - - Calls a worker funker (`-worker-service`) with a chunk of `-check.f` filter strings (passed as a file via `-input` flag, typically `/mnt/input`) - -### Worker service - - - Works as a funker callee - - Executes an equivalent of `TESTFLAGS=-check.f TestFoo|TestBar|TestBaz ... make test-integration-cli` using the bind-mounted API socket (`docker.sock`) - -### Client - - - Controls master and workers via `docker stack` - - No need to have a local daemon - -Typically, the master and workers are supposed to be running on a cloud environment, -while the client is supposed to be running on a laptop, e.g. Docker for Mac/Windows. - -## Requirement - - - Docker daemon 1.13 or later - - Private registry for distributed execution with multiple nodes - -## Usage - -### Step 1: Prepare images - - $ make build-integration-cli-on-swarm - -Following environment variables are known to work in this step: - - - `BUILDFLAGS` - - `DOCKER_INCREMENTAL_BINARY` - -Note: during the transition into Moby Project, you might need to create a symbolic link `$GOPATH/src/github.com/docker/docker` to `$GOPATH/src/github.com/moby/moby`. - -### Step 2: Execute tests - - $ ./hack/integration-cli-on-swarm/integration-cli-on-swarm -replicas 40 -push-worker-image YOUR_REGISTRY.EXAMPLE.COM/integration-cli-worker:latest - -Following environment variables are known to work in this step: - - - `DOCKER_GRAPHDRIVER` - - `DOCKER_EXPERIMENTAL` - -#### Flags - -Basic flags: - - - `-replicas N`: the number of worker service replicas. i.e. degree of parallelism. - - `-chunks N`: the number of chunks. By default, `chunks` == `replicas`. - - `-push-worker-image REGISTRY/IMAGE:TAG`: push the worker image to the registry. Note that if you have only single node and hence you do not need a private registry, you do not need to specify `-push-worker-image`. - -Experimental flags for mitigating makespan nonuniformity: - - - `-shuffle`: Shuffle the test filter strings - -Flags for debugging IT on Swarm itself: - - - `-rand-seed N`: the random seed. This flag is useful for deterministic replaying. By default(0), the timestamp is used. - - `-filters-file FILE`: the file contains `-check.f` strings. By default, the file is automatically generated. - - `-dry-run`: skip the actual workload - - `keep-executor`: do not auto-remove executor containers, which is used for running privileged programs on Swarm diff --git a/src/cmd/linuxkit/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/agent/vendor.conf b/src/cmd/linuxkit/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/agent/vendor.conf deleted file mode 100644 index efd6d6d04..000000000 --- a/src/cmd/linuxkit/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/agent/vendor.conf +++ /dev/null @@ -1,2 +0,0 @@ -# dependencies specific to worker (i.e. github.com/docker/docker/...) are not vendored here -github.com/bfirsh/funker-go eaa0a2e06f30e72c9a0b7f858951e581e26ef773 diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/LICENSE b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/LICENSE new file mode 100644 index 000000000..34fc18b87 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015 Docker, Inc. + + 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. + diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/README.md b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/README.md new file mode 100644 index 000000000..6c493cc72 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/README.md @@ -0,0 +1,11 @@ +# p9p [![GoDoc](https://godoc.org/github.com/docker/go-p9p?status.svg)](https://godoc.org/github.com/docker/go-p9p) [![Apache licensed](https://img.shields.io/badge/license-Apache-blue.svg)](https://raw.githubusercontent.com/docker/go-p9p/master/LICENSE) [![CircleCI](https://circleci.com/gh/docker/go-p9p.svg?style=shield)](https://circleci.com/gh/docker/go-p9p) [![TravisCI](https://travis-ci.org/docker/go-p9p.svg?branch=master)](https://travis-ci.org/docker/go-p9p) [![Go Report Card](https://goreportcard.com/badge/github.com/docker/go-p9p)](https://goreportcard.com/report/github.com/docker/go-p9p) [![Badge Badge](http://doyouevenbadge.com/github.com/docker/go-p9p)](http://doyouevenbadge.com/report/github.com/docker/go-p9p) + + +A modern, performant 9P library for Go. + +For information on usage, please see the [GoDoc](https://godoc.org/github.com/docker/go-p9p). + +## Copyright and license + +Copyright © 2015 Docker, Inc. go-p9p is licensed under the Apache License, +Version 2.0. See [LICENSE](LICENSE) for the full license text. diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/channel.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/channel.go new file mode 100644 index 000000000..984958022 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/channel.go @@ -0,0 +1,343 @@ +package p9p + +import ( + "bufio" + "context" + "encoding/binary" + "io" + "io/ioutil" + "log" + "net" + "time" +) + +const ( + // channelMessageHeaderSize is the overhead for sending the size of a + // message on the wire. + channelMessageHeaderSize = 4 +) + +// Channel defines the operations necessary to implement a 9p message channel +// interface. Typically, message channels do no protocol processing except to +// send and receive message frames. +type Channel interface { + // ReadFcall reads one fcall frame into the provided fcall structure. The + // Fcall may be cleared whether there is an error or not. If the operation + // is successful, the contents of the fcall will be populated in the + // argument. ReadFcall cannot be called concurrently with other calls to + // ReadFcall. This both to preserve message ordering and to allow lockless + // buffer reusage. + ReadFcall(ctx context.Context, fcall *Fcall) error + + // WriteFcall writes the provided fcall to the channel. WriteFcall cannot + // be called concurrently with other calls to WriteFcall. + WriteFcall(ctx context.Context, fcall *Fcall) error + + // MSize returns the current msize for the channel. + MSize() int + + // SetMSize sets the maximum message size for the channel. This must never + // be called currently with ReadFcall or WriteFcall. + SetMSize(msize int) +} + +// NewChannel returns a new channel to read and write Fcalls with the provided +// connection and message size. +func NewChannel(conn net.Conn, msize int) Channel { + return newChannel(conn, codec9p{}, msize) +} + +const ( + defaultRWTimeout = 30 * time.Second // default read/write timeout if not set in context +) + +// channel provides bidirectional protocol framing for 9p over net.Conn. +// Operations are not thread-safe but reads and writes may be carried out +// concurrently, supporting separate read and write loops. +// +// Lifecyle +// +// A connection, or message channel abstraction, has a lifecycle delineated by +// Tversion/Rversion request response cycles. For now, this is part of the +// channel itself but doesn't necessarily influence the channels state, except +// the msize. Visually, it might look something like this: +// +// [Established] -> [Version] -> [Session] -> [Version]---+ +// ^ | +// |_________________________________| +// +// The connection is established, then we negotiate a version, run a session, +// then negotiate a version and so on. For most purposes, we are likely going +// to terminate the connection after the session but we may want to support +// connection pooling. Pooling may result in possible security leaks if the +// connections are shared among contexts, since the version is negotiated at +// the start of the session. To avoid this, we can actually use a "tombstone" +// version message which clears the server's session state without starting a +// new session. The next version message would then prepare the session +// without leaking any Fid's. +type channel struct { + conn net.Conn + codec Codec + brd *bufio.Reader + bwr *bufio.Writer + closed chan struct{} + msize int + rdbuf []byte +} + +func newChannel(conn net.Conn, codec Codec, msize int) *channel { + return &channel{ + conn: conn, + codec: codec, + brd: bufio.NewReaderSize(conn, msize), // msize may not be optimal buffer size + bwr: bufio.NewWriterSize(conn, msize), + closed: make(chan struct{}), + msize: msize, + rdbuf: make([]byte, msize), + } +} + +func (ch *channel) MSize() int { + return ch.msize +} + +// setmsize resizes the buffers for use with a separate msize. This call must +// be protected by a mutex or made before passing to other goroutines. +func (ch *channel) SetMSize(msize int) { + // NOTE(stevvooe): We cannot safely resize the buffered reader and writer. + // Proceed assuming that original size is sufficient. + + ch.msize = msize + if msize < len(ch.rdbuf) { + // just change the cap + ch.rdbuf = ch.rdbuf[:msize] + return + } + + ch.rdbuf = make([]byte, msize) +} + +// ReadFcall reads the next message from the channel into fcall. +// +// If the incoming message overflows the msize, Overflow(err) will return +// nonzero with the number of bytes overflowed. +func (ch *channel) ReadFcall(ctx context.Context, fcall *Fcall) error { + select { + case <-ctx.Done(): + return ctx.Err() + case <-ch.closed: + return ErrClosed + default: + } + + deadline, ok := ctx.Deadline() + if !ok { + deadline = time.Now().Add(defaultRWTimeout) + } + + if err := ch.conn.SetReadDeadline(deadline); err != nil { + log.Printf("transport: error setting read deadline on %v: %v", ch.conn.RemoteAddr(), err) + } + + n, err := readmsg(ch.brd, ch.rdbuf) + if err != nil { + // TODO(stevvooe): There may be more we can do here to detect partial + // reads. For now, we just propagate the error untouched. + return err + } + + if n > len(ch.rdbuf) { + return overflowErr{size: n - len(ch.rdbuf)} + } + + // clear out the fcall + *fcall = Fcall{} + if err := ch.codec.Unmarshal(ch.rdbuf[:n], fcall); err != nil { + return err + } + + if err := ch.maybeTruncate(fcall); err != nil { + return err + } + + return nil +} + +// WriteFcall writes the message to the connection. +// +// If a message destined for the wire will overflow MSize, an Overflow error +// may be returned. For Twrite calls, the buffer will simply be truncated to +// the optimal msize, with the caller detecting this condition with +// Rwrite.Count. +func (ch *channel) WriteFcall(ctx context.Context, fcall *Fcall) error { + select { + case <-ctx.Done(): + return ctx.Err() + case <-ch.closed: + return ErrClosed + default: + } + + deadline, ok := ctx.Deadline() + if !ok { + deadline = time.Now().Add(defaultRWTimeout) + } + + if err := ch.conn.SetWriteDeadline(deadline); err != nil { + log.Printf("transport: error setting read deadline on %v: %v", ch.conn.RemoteAddr(), err) + } + + if err := ch.maybeTruncate(fcall); err != nil { + return err + } + + p, err := ch.codec.Marshal(fcall) + if err != nil { + return err + } + + if err := sendmsg(ch.bwr, p); err != nil { + return err + } + + return ch.bwr.Flush() +} + +// maybeTruncate will truncate the message to fit into msize on the wire, if +// possible, or modify the message to ensure the response won't overflow. +// +// If the message cannot be truncated, an error will be returned and the +// message should not be sent. +// +// A nil return value means the message can be sent without +func (ch *channel) maybeTruncate(fcall *Fcall) error { + + // for certain message types, just remove the extra bytes from the data portion. + switch msg := fcall.Message.(type) { + // TODO(stevvooe): There is one more problematic message type: + // + // Rread: while we can employ the same truncation fix as Twrite, we + // need to make it observable to upstream handlers. + + case MessageTread: + // We can rewrite msg.Count so that a return message will be under + // msize. This is more defensive than anything but will ensure that + // calls don't fail on sloppy servers. + + // first, craft the shape of the response message + resp := newFcall(fcall.Tag, MessageRread{}) + overflow := uint32(ch.msgmsize(resp)) + msg.Count - uint32(ch.msize) + + if msg.Count < overflow { + // Let the bad thing happen; msize too small to even support valid + // rewrite. This will result in a Terror from the server-side or + // just work. + return nil + } + + msg.Count -= overflow + fcall.Message = msg + + return nil + case MessageTwrite: + // If we are going to overflow the msize, we need to truncate the write to + // appropriate size or throw an error in all other conditions. + size := ch.msgmsize(fcall) + if size <= ch.msize { + return nil + } + + // overflow the msize, including the channel message size fields. + overflow := size - ch.msize + + if len(msg.Data) < overflow { + // paranoid: if msg.Data is not big enough to handle the + // overflow, we should get an overflow error. MSize would have + // to be way too small to be realistic. + return overflowErr{size: overflow} + } + + // The truncation is reflected in the return message (Rwrite) by + // the server, so we don't need a return value or error condition + // to communicate it. + msg.Data = msg.Data[:len(msg.Data)-overflow] + fcall.Message = msg // since we have a local copy + + return nil + default: + size := ch.msgmsize(fcall) + if size > ch.msize { + // overflow the msize, including the channel message size fields. + return overflowErr{size: size - ch.msize} + } + + return nil + } + +} + +// msgmsize returns the on-wire msize of the Fcall, including the size header. +// Typically, this can be used to detect whether or not the message overflows +// the msize buffer. +func (ch *channel) msgmsize(fcall *Fcall) int { + return channelMessageHeaderSize + ch.codec.Size(fcall) +} + +// readmsg reads a 9p message into p from rd, ensuring that all bytes are +// consumed from the size header. If the size header indicates the message is +// larger than p, the entire message will be discarded, leaving a truncated +// portion in p. Any error should be treated as a framing error unless n is +// zero. The caller must check that n is less than or equal to len(p) to +// ensure that a valid message has been read. +func readmsg(rd io.Reader, p []byte) (n int, err error) { + var msize uint32 + + if err := binary.Read(rd, binary.LittleEndian, &msize); err != nil { + return 0, err + } + + n += binary.Size(msize) + mbody := int(msize) - 4 + + if mbody < len(p) { + p = p[:mbody] + } + + np, err := io.ReadFull(rd, p) + if err != nil { + return np + n, err + } + n += np + + if mbody > len(p) { + // message has been read up to len(p) but we must consume the entire + // message. This is an error condition but is non-fatal if we can + // consume msize bytes. + nn, err := io.CopyN(ioutil.Discard, rd, int64(mbody-len(p))) + n += int(nn) + if err != nil { + return n, err + } + } + + return n, nil +} + +// sendmsg writes a message of len(p) to wr with a 9p size header. All errors +// should be considered terminal. +func sendmsg(wr io.Writer, p []byte) error { + size := uint32(len(p) + 4) // message size plus 4-bytes for size. + if err := binary.Write(wr, binary.LittleEndian, size); err != nil { + return err + } + + // This assume partial writes to wr aren't possible. Not sure if this + // valid. Matters during timeout retries. + if n, err := wr.Write(p); err != nil { + return err + } else if n < len(p) { + return io.ErrShortWrite + } + + return nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/client.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/client.go new file mode 100644 index 000000000..5884b06bf --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/client.go @@ -0,0 +1,253 @@ +package p9p + +import ( + "io" + "net" + + "context" +) + +type client struct { + version string + msize int + ctx context.Context + transport roundTripper +} + +// NewSession returns a session using the connection. The Context ctx provides +// a context for out of bad messages, such as flushes, that may be sent by the +// session. The session can effectively shutdown with this context. +func NewSession(ctx context.Context, conn net.Conn) (Session, error) { + ch := newChannel(conn, codec9p{}, DefaultMSize) // sets msize, effectively. + + // negotiate the protocol version + version, err := clientnegotiate(ctx, ch, DefaultVersion) + if err != nil { + return nil, err + } + + return &client{ + version: version, + msize: ch.MSize(), + ctx: ctx, + transport: newTransport(ctx, ch), + }, nil +} + +var _ Session = &client{} + +func (c *client) Version() (int, string) { + return c.msize, c.version +} + +func (c *client) Auth(ctx context.Context, afid Fid, uname, aname string) (Qid, error) { + m := MessageTauth{ + Afid: afid, + Uname: uname, + Aname: aname, + } + + resp, err := c.transport.send(ctx, m) + if err != nil { + return Qid{}, err + } + + rauth, ok := resp.(MessageRauth) + if !ok { + return Qid{}, ErrUnexpectedMsg + } + + return rauth.Qid, nil +} + +func (c *client) Attach(ctx context.Context, fid, afid Fid, uname, aname string) (Qid, error) { + m := MessageTattach{ + Fid: fid, + Afid: afid, + Uname: uname, + Aname: aname, + } + + resp, err := c.transport.send(ctx, m) + if err != nil { + return Qid{}, err + } + + rattach, ok := resp.(MessageRattach) + if !ok { + return Qid{}, ErrUnexpectedMsg + } + + return rattach.Qid, nil +} + +func (c *client) Clunk(ctx context.Context, fid Fid) error { + resp, err := c.transport.send(ctx, MessageTclunk{ + Fid: fid, + }) + if err != nil { + return err + } + + _, ok := resp.(MessageRclunk) + if !ok { + return ErrUnexpectedMsg + } + + return nil +} + +func (c *client) Remove(ctx context.Context, fid Fid) error { + resp, err := c.transport.send(ctx, MessageTremove{ + Fid: fid, + }) + if err != nil { + return err + } + + _, ok := resp.(MessageRremove) + if !ok { + return ErrUnexpectedMsg + } + + return nil +} + +func (c *client) Walk(ctx context.Context, fid Fid, newfid Fid, names ...string) ([]Qid, error) { + if len(names) > 16 { + return nil, ErrWalkLimit + } + + resp, err := c.transport.send(ctx, MessageTwalk{ + Fid: fid, + Newfid: newfid, + Wnames: names, + }) + if err != nil { + return nil, err + } + + rwalk, ok := resp.(MessageRwalk) + if !ok { + return nil, ErrUnexpectedMsg + } + + return rwalk.Qids, nil +} + +func (c *client) Read(ctx context.Context, fid Fid, p []byte, offset int64) (n int, err error) { + resp, err := c.transport.send(ctx, MessageTread{ + Fid: fid, + Offset: uint64(offset), + Count: uint32(len(p)), + }) + if err != nil { + return 0, err + } + + rread, ok := resp.(MessageRread) + if !ok { + return 0, ErrUnexpectedMsg + } + + n = copy(p, rread.Data) + switch { + case len(rread.Data) == 0: + err = io.EOF + case n < len(p): + // TODO(stevvooe): Technically, we should treat this as an io.EOF. + // However, we cannot tell if the short read was due to EOF or due to + // truncation. + } + + return n, err +} + +func (c *client) Write(ctx context.Context, fid Fid, p []byte, offset int64) (n int, err error) { + resp, err := c.transport.send(ctx, MessageTwrite{ + Fid: fid, + Offset: uint64(offset), + Data: p, + }) + if err != nil { + return 0, err + } + + rwrite, ok := resp.(MessageRwrite) + if !ok { + return 0, ErrUnexpectedMsg + } + + if int(rwrite.Count) < len(p) { + err = io.ErrShortWrite + } + + return int(rwrite.Count), err +} + +func (c *client) Open(ctx context.Context, fid Fid, mode Flag) (Qid, uint32, error) { + resp, err := c.transport.send(ctx, MessageTopen{ + Fid: fid, + Mode: mode, + }) + if err != nil { + return Qid{}, 0, err + } + + ropen, ok := resp.(MessageRopen) + if !ok { + return Qid{}, 0, ErrUnexpectedMsg + } + + return ropen.Qid, ropen.IOUnit, nil +} + +func (c *client) Create(ctx context.Context, parent Fid, name string, perm uint32, mode Flag) (Qid, uint32, error) { + resp, err := c.transport.send(ctx, MessageTcreate{ + Fid: parent, + Name: name, + Perm: perm, + Mode: mode, + }) + if err != nil { + return Qid{}, 0, err + } + + rcreate, ok := resp.(MessageRcreate) + if !ok { + return Qid{}, 0, ErrUnexpectedMsg + } + + return rcreate.Qid, rcreate.IOUnit, nil +} + +func (c *client) Stat(ctx context.Context, fid Fid) (Dir, error) { + resp, err := c.transport.send(ctx, MessageTstat{Fid: fid}) + if err != nil { + return Dir{}, err + } + + rstat, ok := resp.(MessageRstat) + if !ok { + return Dir{}, ErrUnexpectedMsg + } + + return rstat.Stat, nil +} + +func (c *client) WStat(ctx context.Context, fid Fid, dir Dir) error { + resp, err := c.transport.send(ctx, MessageTwstat{ + Fid: fid, + Stat: dir, + }) + if err != nil { + return err + } + + _, ok := resp.(MessageRwstat) + if !ok { + return ErrUnexpectedMsg + } + + return nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/context.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/context.go new file mode 100644 index 000000000..dfa24ce4e --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/context.go @@ -0,0 +1,26 @@ +package p9p + +import ( + "context" +) + +type contextKey string + +const ( + versionKey contextKey = "9p.version" +) + +func withVersion(ctx context.Context, version string) context.Context { + return context.WithValue(ctx, versionKey, version) +} + +// GetVersion returns the protocol version from the context. If the version is +// not known, an empty string is returned. This is typically set on the +// context passed into function calls in a server implementation. +func GetVersion(ctx context.Context) string { + v, ok := ctx.Value(versionKey).(string) + if !ok { + return "" + } + return v +} diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/dispatcher.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/dispatcher.go new file mode 100644 index 000000000..bbb58773a --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/dispatcher.go @@ -0,0 +1,133 @@ +package p9p + +import "context" + +// Handler defines an interface for 9p message handlers. A handler +// implementation could be used to intercept calls of all types before sending +// them to the next handler. +type Handler interface { + Handle(ctx context.Context, msg Message) (Message, error) + + // TODO(stevvooe): Right now, this interface is functianally identical to + // roundtripper. If we find that this is sufficient on the server-side, we + // may unify the types. For now, we leave them separated to differentiate + // between them. +} + +// HandlerFunc is a convenience type for defining inline handlers. +type HandlerFunc func(ctx context.Context, msg Message) (Message, error) + +// Handle implements the requirements for the Handler interface. +func (fn HandlerFunc) Handle(ctx context.Context, msg Message) (Message, error) { + return fn(ctx, msg) +} + +// Dispatch returns a handler that dispatches messages to the target session. +// No concurrency is managed by the returned handler. It simply turns messages +// into function calls on the session. +func Dispatch(session Session) Handler { + return HandlerFunc(func(ctx context.Context, msg Message) (Message, error) { + switch msg := msg.(type) { + case MessageTauth: + qid, err := session.Auth(ctx, msg.Afid, msg.Uname, msg.Aname) + if err != nil { + return nil, err + } + + return MessageRauth{Qid: qid}, nil + case MessageTattach: + qid, err := session.Attach(ctx, msg.Fid, msg.Afid, msg.Uname, msg.Aname) + if err != nil { + return nil, err + } + + return MessageRattach{ + Qid: qid, + }, nil + case MessageTwalk: + // TODO(stevvooe): This is one of the places where we need to manage + // fid allocation lifecycle. We need to reserve the fid, then, if this + // call succeeds, we should alloc the fid for future uses. Also need + // to interact correctly with concurrent clunk and the flush of this + // walk message. + qids, err := session.Walk(ctx, msg.Fid, msg.Newfid, msg.Wnames...) + if err != nil { + return nil, err + } + + return MessageRwalk{ + Qids: qids, + }, nil + case MessageTopen: + qid, iounit, err := session.Open(ctx, msg.Fid, msg.Mode) + if err != nil { + return nil, err + } + + return MessageRopen{ + Qid: qid, + IOUnit: iounit, + }, nil + case MessageTcreate: + qid, iounit, err := session.Create(ctx, msg.Fid, msg.Name, msg.Perm, msg.Mode) + if err != nil { + return nil, err + } + + return MessageRcreate{ + Qid: qid, + IOUnit: iounit, + }, nil + case MessageTread: + p := make([]byte, int(msg.Count)) + n, err := session.Read(ctx, msg.Fid, p, int64(msg.Offset)) + if err != nil { + return nil, err + } + + return MessageRread{ + Data: p[:n], + }, nil + case MessageTwrite: + n, err := session.Write(ctx, msg.Fid, msg.Data, int64(msg.Offset)) + if err != nil { + return nil, err + } + + return MessageRwrite{ + Count: uint32(n), + }, nil + case MessageTclunk: + // TODO(stevvooe): Manage the clunking of file descriptors based on + // walk and attach call progression. + if err := session.Clunk(ctx, msg.Fid); err != nil { + return nil, err + } + + return MessageRclunk{}, nil + case MessageTremove: + if err := session.Remove(ctx, msg.Fid); err != nil { + return nil, err + } + + return MessageRremove{}, nil + case MessageTstat: + dir, err := session.Stat(ctx, msg.Fid) + if err != nil { + return nil, err + } + + return MessageRstat{ + Stat: dir, + }, nil + case MessageTwstat: + if err := session.WStat(ctx, msg.Fid, msg.Stat); err != nil { + return nil, err + } + + return MessageRwstat{}, nil + default: + return nil, ErrUnknownMsg + } + }) +} diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/doc.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/doc.go new file mode 100644 index 000000000..58438cf7c --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/doc.go @@ -0,0 +1,78 @@ +/* +Package p9p implements a compliant 9P2000 client and server library for use +in modern, production Go services. This package differentiates itself in that +is has departed from the plan 9 implementation primitives and better follows +idiomatic Go style. + +The package revolves around the session type, which is an enumeration of raw +9p message calls. A few calls, such as flush and version, have been elided, +defering their usage to the server implementation. Sessions can be trivially +proxied through clients and servers. + +Getting Started + +The best place to get started is with Serve. Serve can be provided a +connection and a handler. A typical implementation will call Serve as part of +a listen/accept loop. As each network connection is created, Serve can be +called with a handler for the specific connection. The handler can be +implemented with a Session via the Dispatch function or can generate sessions +for dispatch in response to client messages. (See cmd/9ps for an example) + +On the client side, NewSession provides a 9p session from a connection. After +a version negotiation, methods can be called on the session, in parallel, and +calls will be sent over the connection. Call timeouts can be controlled via +the context provided to each method call. + +Framework + +This package has the beginning of a nice client-server framework for working +with 9p. Some of the abstractions aren't entirely fleshed out, but most of +this can center around the Handler. + +Missing from this are a number of tools for implementing 9p servers. The most +glaring are directory read and walk helpers. Other, more complex additions +might be a system to manage in memory filesystem trees that expose multi-user +sessions. + +Differences + +The largest difference between this package and other 9p packages is +simplification of the types needed to implement a server. To avoid confusing +bugs and odd behavior, the components are separated by each level of the +protocol. One example is that requests and responses are separated and they no +longer hold mutable state. This means that framing, transport management, +encoding, and dispatching are componentized. Little work will be required to +swap out encodings, transports or connection implementations. + +Context Integration + +This package has been wired from top to bottom to support context-based +resource management. Everything from startup to shutdown can have timeouts +using contexts. Not all close methods are fully in place, but we are very +close to having controlled, predictable cleanup for both servers and clients. +Timeouts can be very granular or very course, depending on the context of the +timeout. For example, it is very easy to set a short timeout for a stat call +but a long timeout for reading data. + +Multiversion Support + +Currently, there is not multiversion support. The hooks and functionality are +in place to add multi-version support. Generally, the correct space to do this +is in the codec. Types, such as Dir, simply need to be extended to support the +possibility of extra fields. + +The real question to ask here is what is the role of the version number in the +9p protocol. It really comes down to the level of support required. Do we just +need it at the protocol level, or do handlers and sessions need to be have +differently based on negotiated versions? + +Caveats + +This package has a number of TODOs to make it easier to use. Most of the +existing code provides a solid base to work from. Don't be discouraged by the +sawdust. + +In addition, the testing is embarassingly lacking. With time, we can get full +testing going and ensure we have confidence in the implementation. +*/ +package p9p diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/encoding.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/encoding.go new file mode 100644 index 000000000..b431cfca9 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/encoding.go @@ -0,0 +1,564 @@ +package p9p + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "log" + "reflect" + "strings" + "time" +) + +// Codec defines the interface for encoding and decoding of 9p types. +// Unsupported types will throw an error. +type Codec interface { + // Unmarshal from data into the value pointed to by v. + Unmarshal(data []byte, v interface{}) error + + // Marshal the value v into a byte slice. + Marshal(v interface{}) ([]byte, error) + + // Size returns the encoded size for the target of v. + Size(v interface{}) int +} + +// NewCodec returns a new, standard 9P2000 codec, ready for use. +func NewCodec() Codec { + return codec9p{} +} + +type codec9p struct{} + +func (c codec9p) Unmarshal(data []byte, v interface{}) error { + dec := &decoder{bytes.NewReader(data)} + return dec.decode(v) +} + +func (c codec9p) Marshal(v interface{}) ([]byte, error) { + var b bytes.Buffer + enc := &encoder{&b} + + if err := enc.encode(v); err != nil { + return nil, err + } + + return b.Bytes(), nil +} + +func (c codec9p) Size(v interface{}) int { + return int(size9p(v)) +} + +// DecodeDir decodes a directory entry from rd using the provided codec. +func DecodeDir(codec Codec, rd io.Reader, d *Dir) error { + var ll uint16 + + // pull the size off the wire + if err := binary.Read(rd, binary.LittleEndian, &ll); err != nil { + return err + } + + p := make([]byte, ll+2) + binary.LittleEndian.PutUint16(p, ll) // must have size at start + + // read out the rest of the record + if _, err := io.ReadFull(rd, p[2:]); err != nil { + return err + } + + return codec.Unmarshal(p, d) +} + +// EncodeDir writes the directory to wr. +func EncodeDir(codec Codec, wr io.Writer, d *Dir) error { + p, err := codec.Marshal(d) + if err != nil { + return err + } + + _, err = wr.Write(p) + return err +} + +type encoder struct { + wr io.Writer +} + +func (e *encoder) encode(vs ...interface{}) error { + for _, v := range vs { + switch v := v.(type) { + case uint8, uint16, uint32, uint64, FcallType, Tag, QType, Fid, Flag, + *uint8, *uint16, *uint32, *uint64, *FcallType, *Tag, *QType, *Fid, *Flag: + if err := binary.Write(e.wr, binary.LittleEndian, v); err != nil { + return err + } + case []byte: + if err := e.encode(uint32(len(v))); err != nil { + return err + } + + if err := binary.Write(e.wr, binary.LittleEndian, v); err != nil { + return err + } + + case *[]byte: + if err := e.encode(*v); err != nil { + return err + } + case string: + if err := binary.Write(e.wr, binary.LittleEndian, uint16(len(v))); err != nil { + return err + } + + _, err := io.WriteString(e.wr, v) + if err != nil { + return err + } + case *string: + if err := e.encode(*v); err != nil { + return err + } + + case []string: + if err := e.encode(uint16(len(v))); err != nil { + return err + } + + for _, m := range v { + if err := e.encode(m); err != nil { + return err + } + } + case *[]string: + if err := e.encode(*v); err != nil { + return err + } + case time.Time: + if err := e.encode(uint32(v.Unix())); err != nil { + return err + } + case *time.Time: + if err := e.encode(*v); err != nil { + return err + } + case Qid: + if err := e.encode(v.Type, v.Version, v.Path); err != nil { + return err + } + case *Qid: + if err := e.encode(*v); err != nil { + return err + } + case []Qid: + if err := e.encode(uint16(len(v))); err != nil { + return err + } + + elements := make([]interface{}, len(v)) + for i := range v { + elements[i] = &v[i] + } + + if err := e.encode(elements...); err != nil { + return err + } + case *[]Qid: + if err := e.encode(*v); err != nil { + return err + } + case Dir: + elements, err := fields9p(v) + if err != nil { + return err + } + + if err := e.encode(uint16(size9p(elements...))); err != nil { + return err + } + + if err := e.encode(elements...); err != nil { + return err + } + case *Dir: + if err := e.encode(*v); err != nil { + return err + } + case []Dir: + elements := make([]interface{}, len(v)) + for i := range v { + elements[i] = &v[i] + } + + if err := e.encode(elements...); err != nil { + return err + } + case *[]Dir: + if err := e.encode(*v); err != nil { + return err + } + case Fcall: + if err := e.encode(v.Type, v.Tag, v.Message); err != nil { + return err + } + case *Fcall: + if err := e.encode(*v); err != nil { + return err + } + case Message: + elements, err := fields9p(v) + if err != nil { + return err + } + + switch v.(type) { + case MessageRstat, *MessageRstat: + // NOTE(stevvooe): Prepend size preceeding Dir. See bugs in + // http://man.cat-v.org/plan_9/5/stat to make sense of this. + // The field has been included here but we need to make sure + // to double emit it for Rstat. + if err := e.encode(uint16(size9p(elements...))); err != nil { + return err + } + } + + if err := e.encode(elements...); err != nil { + return err + } + } + } + + return nil +} + +type decoder struct { + rd io.Reader +} + +// read9p extracts values from rd and unmarshals them to the targets of vs. +func (d *decoder) decode(vs ...interface{}) error { + for _, v := range vs { + switch v := v.(type) { + case *uint8, *uint16, *uint32, *uint64, *FcallType, *Tag, *QType, *Fid, *Flag: + if err := binary.Read(d.rd, binary.LittleEndian, v); err != nil { + return err + } + case *[]byte: + var ll uint32 + + if err := d.decode(&ll); err != nil { + return err + } + + if ll > 0 { + *v = make([]byte, int(ll)) + } + + if err := binary.Read(d.rd, binary.LittleEndian, v); err != nil { + return err + } + case *string: + var ll uint16 + + // implement string[s] encoding + if err := d.decode(&ll); err != nil { + return err + } + + b := make([]byte, ll) + + n, err := io.ReadFull(d.rd, b) + if err != nil { + return err + } + + if n != int(ll) { + return fmt.Errorf("unexpected string length") + } + + *v = string(b) + case *[]string: + var ll uint16 + + if err := d.decode(&ll); err != nil { + return err + } + + elements := make([]interface{}, int(ll)) + *v = make([]string, int(ll)) + for i := range elements { + elements[i] = &(*v)[i] + } + + if err := d.decode(elements...); err != nil { + return err + } + case *time.Time: + var epoch uint32 + if err := d.decode(&epoch); err != nil { + return err + } + + *v = time.Unix(int64(epoch), 0).UTC() + case *Qid: + if err := d.decode(&v.Type, &v.Version, &v.Path); err != nil { + return err + } + case *[]Qid: + var ll uint16 + + if err := d.decode(&ll); err != nil { + return err + } + + elements := make([]interface{}, int(ll)) + *v = make([]Qid, int(ll)) + for i := range elements { + elements[i] = &(*v)[i] + } + + if err := d.decode(elements...); err != nil { + return err + } + case *Dir: + var ll uint16 + + if err := d.decode(&ll); err != nil { + return err + } + + b := make([]byte, ll) + // must consume entire dir entry. + n, err := io.ReadFull(d.rd, b) + if err != nil { + log.Println("dir readfull failed:", err, ll, n) + return err + } + + elements, err := fields9p(v) + if err != nil { + return err + } + + dec := &decoder{bytes.NewReader(b)} + + if err := dec.decode(elements...); err != nil { + return err + } + case *[]Dir: + *v = make([]Dir, 0) + for { + element := Dir{} + if err := d.decode(&element); err != nil { + if err == io.EOF { + return nil + } + return err + } + *v = append(*v, element) + } + case *Fcall: + if err := d.decode(&v.Type, &v.Tag); err != nil { + return err + } + + message, err := newMessage(v.Type) + if err != nil { + return err + } + + // NOTE(stevvooe): We do a little pointer dance to allocate the + // new type, write to it, then assign it back to the interface as + // a concrete type, avoiding a pointer (the interface) to a + // pointer. + rv := reflect.New(reflect.TypeOf(message)) + if err := d.decode(rv.Interface()); err != nil { + return err + } + + v.Message = rv.Elem().Interface().(Message) + case Message: + elements, err := fields9p(v) + if err != nil { + return err + } + + switch v.(type) { + case *MessageRstat, MessageRstat: + // NOTE(stevvooe): Consume extra size preceeding Dir. See bugs + // in http://man.cat-v.org/plan_9/5/stat to make sense of + // this. The field has been included here but we need to make + // sure to double emit it for Rstat. decode extra size header + // for stat structure. + var ll uint16 + if err := d.decode(&ll); err != nil { + return err + } + } + + if err := d.decode(elements...); err != nil { + return err + } + } + } + + return nil +} + +// size9p calculates the projected size of the values in vs when encoded into +// 9p binary protocol. If an element or elements are not valid for 9p encoded, +// the value 0 will be used for the size. The error will be detected when +// encoding. +func size9p(vs ...interface{}) uint32 { + var s uint32 + for _, v := range vs { + if v == nil { + continue + } + + switch v := v.(type) { + case uint8, uint16, uint32, uint64, FcallType, Tag, QType, Fid, Flag, + *uint8, *uint16, *uint32, *uint64, *FcallType, *Tag, *QType, *Fid, *Flag: + s += uint32(binary.Size(v)) + case []byte: + s += uint32(binary.Size(uint32(0)) + len(v)) + case *[]byte: + s += size9p(uint32(0), *v) + case string: + s += uint32(binary.Size(uint16(0)) + len(v)) + case *string: + s += size9p(*v) + case []string: + s += size9p(uint16(0)) + + for _, sv := range v { + s += size9p(sv) + } + case *[]string: + s += size9p(*v) + case time.Time, *time.Time: + // BUG(stevvooe): Y2038 is coming. + s += size9p(uint32(0)) + case Qid: + s += size9p(v.Type, v.Version, v.Path) + case *Qid: + s += size9p(*v) + case []Qid: + s += size9p(uint16(0)) + elements := make([]interface{}, len(v)) + for i := range elements { + elements[i] = &v[i] + } + s += size9p(elements...) + case *[]Qid: + s += size9p(*v) + + case Dir: + // walk the fields of the message to get the total size. we just + // use the field order from the message struct. We may add tag + // ignoring if needed. + elements, err := fields9p(v) + if err != nil { + // BUG(stevvooe): The options here are to return 0, panic or + // make this return an error. Ideally, we make it safe to + // return 0 and have the rest of the package do the right + // thing. For now, we do this, but may want to panic until + // things are stable. + panic(err) + } + + s += size9p(elements...) + size9p(uint16(0)) + case *Dir: + s += size9p(*v) + case []Dir: + elements := make([]interface{}, len(v)) + for i := range elements { + elements[i] = &v[i] + } + s += size9p(elements...) + case *[]Dir: + s += size9p(*v) + case Fcall: + s += size9p(v.Type, v.Tag, v.Message) + case *Fcall: + s += size9p(*v) + case Message: + // special case twstat and rstat for size fields. See bugs in + // http://man.cat-v.org/plan_9/5/stat to make sense of this. + switch v.(type) { + case *MessageRstat, MessageRstat: + s += size9p(uint16(0)) // for extra size field before dir + } + + // walk the fields of the message to get the total size. we just + // use the field order from the message struct. We may add tag + // ignoring if needed. + elements, err := fields9p(v) + if err != nil { + // BUG(stevvooe): The options here are to return 0, panic or + // make this return an error. Ideally, we make it safe to + // return 0 and have the rest of the package do the right + // thing. For now, we do this, but may want to panic until + // things are stable. + panic(err) + } + + s += size9p(elements...) + } + } + + return s +} + +// fields9p lists the settable fields from a struct type for reading and +// writing. We are using a lot of reflection here for fairly static +// serialization but we can replace this in the future with generated code if +// performance is an issue. +func fields9p(v interface{}) ([]interface{}, error) { + rv := reflect.Indirect(reflect.ValueOf(v)) + + if rv.Kind() != reflect.Struct { + return nil, fmt.Errorf("cannot extract fields from non-struct: %v", rv) + } + + var elements []interface{} + for i := 0; i < rv.NumField(); i++ { + f := rv.Field(i) + + if !f.CanInterface() { + // unexported field, skip it. + continue + } + + if f.CanAddr() { + f = f.Addr() + } + + elements = append(elements, f.Interface()) + } + + return elements, nil +} + +func string9p(v interface{}) string { + if v == nil { + return "nil" + } + + rv := reflect.Indirect(reflect.ValueOf(v)) + + if rv.Kind() != reflect.Struct { + panic("not a struct") + } + + var s string + + for i := 0; i < rv.NumField(); i++ { + f := rv.Field(i) + + s += fmt.Sprintf(" %v=%v", strings.ToLower(rv.Type().Field(i).Name), f.Interface()) + } + + return s +} diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/errors.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/errors.go new file mode 100644 index 000000000..1a2dcdf34 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/errors.go @@ -0,0 +1,58 @@ +package p9p + +import ( + "errors" + "fmt" +) + +// MessageRerror provides both a Go error type and message type. +type MessageRerror struct { + Ename string +} + +// 9p wire errors returned by Session interface methods +var ( + ErrBadattach = new9pError("unknown specifier in attach") + ErrBadoffset = new9pError("bad offset") + ErrBadcount = new9pError("bad count") + ErrBotch = new9pError("9P protocol botch") + ErrCreatenondir = new9pError("create in non-directory") + ErrDupfid = new9pError("duplicate fid") + ErrDuptag = new9pError("duplicate tag") + ErrIsdir = new9pError("is a directory") + ErrNocreate = new9pError("create prohibited") + ErrNomem = new9pError("out of memory") + ErrNoremove = new9pError("remove prohibited") + ErrNostat = new9pError("stat prohibited") + ErrNotfound = new9pError("file not found") + ErrNowrite = new9pError("write prohibited") + ErrNowstat = new9pError("wstat prohibited") + ErrPerm = new9pError("permission denied") + ErrUnknownfid = new9pError("unknown fid") + ErrBaddir = new9pError("bad directory in wstat") + ErrWalknodir = new9pError("walk in non-directory") + + // extra errors not part of the normal protocol + + ErrTimeout = new9pError("fcall timeout") // returned when timing out on the fcall + ErrUnknownTag = new9pError("unknown tag") + ErrUnknownMsg = new9pError("unknown message") // returned when encountering unknown message type + ErrUnexpectedMsg = new9pError("unexpected message") // returned when an unexpected message is encountered + ErrWalkLimit = new9pError("too many wnames in walk") + ErrClosed = errors.New("closed") +) + +// new9pError returns a new 9p error ready for the wire. +func new9pError(s string) error { + return MessageRerror{Ename: s} +} + +// Type ensures that 9p errors can be transparently used as a 9p message in an +// Fcall. +func (MessageRerror) Type() FcallType { + return Rerror +} + +func (e MessageRerror) Error() string { + return fmt.Sprintf("9p: %v", e.Ename) +} diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/fcall.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/fcall.go new file mode 100644 index 000000000..e7b76521e --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/fcall.go @@ -0,0 +1,142 @@ +package p9p + +import "fmt" + +// FcallType encodes the message type for the target Fcall. +type FcallType uint8 + +// Definitions for Fcall's used in 9P2000. +const ( + Tversion FcallType = iota + 100 + Rversion + Tauth + Rauth + Tattach + Rattach + Terror + Rerror + Tflush + Rflush + Twalk + Rwalk + Topen + Ropen + Tcreate + Rcreate + Tread + Rread + Twrite + Rwrite + Tclunk + Rclunk + Tremove + Rremove + Tstat + Rstat + Twstat + Rwstat + Tmax +) + +func (fct FcallType) String() string { + switch fct { + case Tversion: + return "Tversion" + case Rversion: + return "Rversion" + case Tauth: + return "Tauth" + case Rauth: + return "Rauth" + case Tattach: + return "Tattach" + case Rattach: + return "Rattach" + case Terror: + // invalid. + return "Terror" + case Rerror: + return "Rerror" + case Tflush: + return "Tflush" + case Rflush: + return "Rflush" + case Twalk: + return "Twalk" + case Rwalk: + return "Rwalk" + case Topen: + return "Topen" + case Ropen: + return "Ropen" + case Tcreate: + return "Tcreate" + case Rcreate: + return "Rcreate" + case Tread: + return "Tread" + case Rread: + return "Rread" + case Twrite: + return "Twrite" + case Rwrite: + return "Rwrite" + case Tclunk: + return "Tclunk" + case Rclunk: + return "Rclunk" + case Tremove: + return "Tremove" + case Rremove: + return "Rremove" + case Tstat: + return "Tstat" + case Rstat: + return "Rstat" + case Twstat: + return "Twstat" + case Rwstat: + return "Rwstat" + default: + return "Tunknown" + } +} + +// Fcall defines the fields for sending a 9p formatted message. The type will +// be introspected from the Message implementation. +type Fcall struct { + Type FcallType + Tag Tag + Message Message +} + +func newFcall(tag Tag, msg Message) *Fcall { + return &Fcall{ + Type: msg.Type(), + Tag: tag, + Message: msg, + } +} + +func newErrorFcall(tag Tag, err error) *Fcall { + var msg Message + + switch v := err.(type) { + case MessageRerror: + msg = v + case *MessageRerror: + msg = *v + default: + msg = MessageRerror{Ename: v.Error()} + } + + return &Fcall{ + Type: Rerror, + Tag: tag, + Message: msg, + } +} + +func (fc *Fcall) String() string { + return fmt.Sprintf("%v(%v) %v", fc.Type, fc.Tag, string9p(fc.Message)) +} diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/messages.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/messages.go new file mode 100644 index 000000000..3c2c4711b --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/messages.go @@ -0,0 +1,216 @@ +package p9p + +import "fmt" + +// Message represents the target of an fcall. +type Message interface { + // Type returns the type of call for the target message. + Type() FcallType +} + +// newMessage returns a new instance of the message based on the Fcall type. +func newMessage(typ FcallType) (Message, error) { + switch typ { + case Tversion: + return MessageTversion{}, nil + case Rversion: + return MessageRversion{}, nil + case Tauth: + return MessageTauth{}, nil + case Rauth: + return MessageRauth{}, nil + case Tattach: + return MessageTattach{}, nil + case Rattach: + return MessageRattach{}, nil + case Rerror: + return MessageRerror{}, nil + case Tflush: + return MessageTflush{}, nil + case Rflush: + return MessageRflush{}, nil // No message body for this response. + case Twalk: + return MessageTwalk{}, nil + case Rwalk: + return MessageRwalk{}, nil + case Topen: + return MessageTopen{}, nil + case Ropen: + return MessageRopen{}, nil + case Tcreate: + return MessageTcreate{}, nil + case Rcreate: + return MessageRcreate{}, nil + case Tread: + return MessageTread{}, nil + case Rread: + return MessageRread{}, nil + case Twrite: + return MessageTwrite{}, nil + case Rwrite: + return MessageRwrite{}, nil + case Tclunk: + return MessageTclunk{}, nil + case Rclunk: + return MessageRclunk{}, nil // no response body + case Tremove: + return MessageTremove{}, nil + case Rremove: + return MessageRremove{}, nil + case Tstat: + return MessageTstat{}, nil + case Rstat: + return MessageRstat{}, nil + case Twstat: + return MessageTwstat{}, nil + case Rwstat: + return MessageRwstat{}, nil + } + + return nil, fmt.Errorf("unknown message type") +} + +// MessageVersion encodes the message body for Tversion and Rversion RPC +// calls. The body is identical in both directions. +type MessageTversion struct { + MSize uint32 + Version string +} + +type MessageRversion struct { + MSize uint32 + Version string +} + +type MessageTauth struct { + Afid Fid + Uname string + Aname string +} + +type MessageRauth struct { + Qid Qid +} + +type MessageTflush struct { + Oldtag Tag +} + +type MessageRflush struct{} + +type MessageTattach struct { + Fid Fid + Afid Fid + Uname string + Aname string +} + +type MessageRattach struct { + Qid Qid +} + +type MessageTwalk struct { + Fid Fid + Newfid Fid + Wnames []string +} + +type MessageRwalk struct { + Qids []Qid +} + +type MessageTopen struct { + Fid Fid + Mode Flag +} + +type MessageRopen struct { + Qid Qid + IOUnit uint32 +} + +type MessageTcreate struct { + Fid Fid + Name string + Perm uint32 + Mode Flag +} + +type MessageRcreate struct { + Qid Qid + IOUnit uint32 +} + +type MessageTread struct { + Fid Fid + Offset uint64 + Count uint32 +} + +type MessageRread struct { + Data []byte +} + +type MessageTwrite struct { + Fid Fid + Offset uint64 + Data []byte +} + +type MessageRwrite struct { + Count uint32 +} + +type MessageTclunk struct { + Fid Fid +} + +type MessageRclunk struct{} + +type MessageTremove struct { + Fid Fid +} + +type MessageRremove struct{} + +type MessageTstat struct { + Fid Fid +} + +type MessageRstat struct { + Stat Dir +} + +type MessageTwstat struct { + Fid Fid + Stat Dir +} + +type MessageRwstat struct{} + +func (MessageTversion) Type() FcallType { return Tversion } +func (MessageRversion) Type() FcallType { return Rversion } +func (MessageTauth) Type() FcallType { return Tauth } +func (MessageRauth) Type() FcallType { return Rauth } +func (MessageTflush) Type() FcallType { return Tflush } +func (MessageRflush) Type() FcallType { return Rflush } +func (MessageTattach) Type() FcallType { return Tattach } +func (MessageRattach) Type() FcallType { return Rattach } +func (MessageTwalk) Type() FcallType { return Twalk } +func (MessageRwalk) Type() FcallType { return Rwalk } +func (MessageTopen) Type() FcallType { return Topen } +func (MessageRopen) Type() FcallType { return Ropen } +func (MessageTcreate) Type() FcallType { return Tcreate } +func (MessageRcreate) Type() FcallType { return Rcreate } +func (MessageTread) Type() FcallType { return Tread } +func (MessageRread) Type() FcallType { return Rread } +func (MessageTwrite) Type() FcallType { return Twrite } +func (MessageRwrite) Type() FcallType { return Rwrite } +func (MessageTclunk) Type() FcallType { return Tclunk } +func (MessageRclunk) Type() FcallType { return Rclunk } +func (MessageTremove) Type() FcallType { return Tremove } +func (MessageRremove) Type() FcallType { return Rremove } +func (MessageTstat) Type() FcallType { return Tstat } +func (MessageRstat) Type() FcallType { return Rstat } +func (MessageTwstat) Type() FcallType { return Twstat } +func (MessageRwstat) Type() FcallType { return Rwstat } diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/overflow.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/overflow.go new file mode 100644 index 000000000..ab4b7755f --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/overflow.go @@ -0,0 +1,49 @@ +package p9p + +import "fmt" + +// Overflow will return a positive number, indicating there was an overflow for +// the error. +func Overflow(err error) int { + if of, ok := err.(overflow); ok { + return of.Size() + } + + // traverse cause, if above fails. + if causal, ok := err.(interface { + Cause() error + }); ok { + return Overflow(causal.Cause()) + } + + return 0 +} + +// overflow is a resolvable error type that can help callers negotiate +// session msize. If this error is encountered, no message was sent. +// +// The return value of `Size()` represents the number of bytes that would have +// been truncated if the message were sent. This IS NOT the optimal buffer size +// for operations like read and write. +// +// In the case of `Twrite`, the caller can Size() from the local size to get an +// optimally size buffer or the write can simply be truncated to `len(buf) - +// err.Size()`. +// +// For the most part, no users of this package should see this error in +// practice. If this escapes the Session interface, it is a bug. +type overflow interface { + Size() int // number of bytes overflowed. +} + +type overflowErr struct { + size int // number of bytes overflowed +} + +func (o overflowErr) Error() string { + return fmt.Sprintf("message overflowed %d bytes", o.size) +} + +func (o overflowErr) Size() int { + return o.size +} diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/readdir.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/readdir.go new file mode 100644 index 000000000..1f0419c57 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/readdir.go @@ -0,0 +1,93 @@ +package p9p + +import ( + "io" + + "context" +) + +// ReaddirAll reads all the directory entries for the resource fid. +func ReaddirAll(session Session, fid Fid) ([]Dir, error) { + panic("not implemented") +} + +// Readdir helps one to implement the server-side of Session.Read on +// directories. +type Readdir struct { + nextfn func() (Dir, error) + buf *Dir // one-item buffer + codec Codec + offset int64 +} + +// NewReaddir returns a new Readdir to assist implementing server-side Readdir. +// The codec will be used to decode messages with Dir entries. The provided +// function next will be called until io.EOF is returned. +func NewReaddir(codec Codec, next func() (Dir, error)) *Readdir { + return &Readdir{ + nextfn: next, + codec: codec, + } +} + +// NewFixedReaddir returns a Readdir that will returned a fixed set of +// directory entries. +func NewFixedReaddir(codec Codec, dir []Dir) *Readdir { + dirs := make([]Dir, len(dir)) + copy(dirs, dir) // make our own copy! + + return NewReaddir(codec, + func() (Dir, error) { + if len(dirs) == 0 { + return Dir{}, io.EOF + } + + d := dirs[0] + dirs = dirs[1:] + return d, nil + }) +} + +func (rd *Readdir) Read(ctx context.Context, p []byte, offset int64) (n int, err error) { + if rd.offset != offset { + return 0, ErrBadoffset + } + + p = p[:0:len(p)] + for len(p) < cap(p) { + var d Dir + if rd.buf != nil { + d = *rd.buf + rd.buf = nil + } else { + d, err = rd.nextfn() + if err != nil { + goto done + } + } + + var dp []byte + dp, err = rd.codec.Marshal(d) + if err != nil { + goto done + } + + if len(p)+len(dp) > cap(p) { + // will over fill buffer. save item and exit. + rd.buf = &d + goto done + } + + p = append(p, dp...) + } + +done: + if err == io.EOF && len(p) > 0 { + // Don't let io.EOF escape if we've written to p. 9p doesn't handle + // this like Go. + err = nil + } + + rd.offset += int64(len(p)) + return len(p), err +} diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/server.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/server.go new file mode 100644 index 000000000..776e851f7 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/server.go @@ -0,0 +1,257 @@ +package p9p + +import ( + "fmt" + "log" + "net" + "sync" + "time" + + "context" +) + +// TODO(stevvooe): Add net/http.Server-like type here to manage connections. +// Coupled with Handler mux, we can get a very http-like experience for 9p +// servers. + +// ServeConn the 9p handler over the provided network connection. +func ServeConn(ctx context.Context, cn net.Conn, handler Handler) error { + + // TODO(stevvooe): It would be nice if the handler could declare the + // supported version. Before we had handler, we used the session to get + // the version (msize, version := session.Version()). We must decided if + // we want to proxy version and message size decisions all the back to the + // origin server or make those decisions at each link of a proxy chain. + + ch := newChannel(cn, codec9p{}, DefaultMSize) + negctx, cancel := context.WithTimeout(ctx, 1*time.Second) + defer cancel() + + // TODO(stevvooe): For now, we negotiate here. It probably makes sense to + // do this outside of this function and then pass in a ready made channel. + // We are not really ready to export the channel type yet. + + if err := servernegotiate(negctx, ch, DefaultVersion); err != nil { + // TODO(stevvooe): Need better error handling and retry support here. + return fmt.Errorf("error negotiating version: %s", err) + } + + ctx = withVersion(ctx, DefaultVersion) + + c := &conn{ + ctx: ctx, + ch: ch, + handler: handler, + closed: make(chan struct{}), + } + + return c.serve() +} + +// conn plays role of session dispatch for handler in a server. +type conn struct { + ctx context.Context + session Session + ch Channel + handler Handler + + once sync.Once + closed chan struct{} + err error // terminal error for the conn +} + +// activeRequest includes information about the active request. +type activeRequest struct { + ctx context.Context + request *Fcall + cancel context.CancelFunc +} + +// serve messages on the connection until an error is encountered. +func (c *conn) serve() error { + tags := map[Tag]*activeRequest{} // active requests + + requests := make(chan *Fcall) // sync, read-limited + responses := make(chan *Fcall) // sync, goroutine consumed + completed := make(chan *Fcall) // sync, send in goroutine per request + + // read loop + go c.read(requests) + go c.write(responses) + + log.Println("server.run()") + for { + select { + case req := <-requests: + if _, ok := tags[req.Tag]; ok { + select { + case responses <- newErrorFcall(req.Tag, ErrDuptag): + // Send to responses, bypass tag management. + case <-c.ctx.Done(): + return c.ctx.Err() + case <-c.closed: + return c.err + } + continue + } + + switch msg := req.Message.(type) { + case MessageTflush: + log.Println("server: flushing message", msg.Oldtag) + + var resp *Fcall + // check if we have actually know about the requested flush + active, ok := tags[msg.Oldtag] + if ok { + active.cancel() // propagate cancellation to callees + delete(tags, msg.Oldtag) + resp = newFcall(req.Tag, MessageRflush{}) + } else { + resp = newErrorFcall(req.Tag, ErrUnknownTag) + } + + select { + case responses <- resp: + // bypass tag management in completed. + case <-c.ctx.Done(): + return c.ctx.Err() + case <-c.closed: + return c.err + } + default: + // Allows us to session handlers to cancel processing of the fcall + // through context. + ctx, cancel := context.WithCancel(c.ctx) + + // The contents of these instances are only writable in the main + // server loop. The value of tag will not change. + tags[req.Tag] = &activeRequest{ + ctx: ctx, + request: req, + cancel: cancel, + } + + go func(ctx context.Context, req *Fcall) { + // TODO(stevvooe): Re-write incoming Treads so that handler + // can always respond with a message of the correct msize. + + var resp *Fcall + msg, err := c.handler.Handle(ctx, req.Message) + if err != nil { + // all handler errors are forwarded as protocol errors. + resp = newErrorFcall(req.Tag, err) + } else { + resp = newFcall(req.Tag, msg) + } + + select { + case completed <- resp: + case <-ctx.Done(): + return + case <-c.closed: + return + } + }(ctx, req) + } + case resp := <-completed: + // only responses that flip the tag state traverse this section. + active, ok := tags[resp.Tag] + if !ok { + // The tag is no longer active. Likely a flushed message. + continue + } + + select { + case responses <- resp: + case <-active.ctx.Done(): + // the context was canceled for some reason, perhaps timeout or + // due to a flush call. We treat this as a condition where a + // response should not be sent. + log.Println("canceled", resp, active.ctx.Err()) + } + delete(tags, resp.Tag) + case <-c.ctx.Done(): + return c.ctx.Err() + case <-c.closed: + return c.err + } + } +} + +// read takes requests off the channel and sends them on requests. +func (c *conn) read(requests chan *Fcall) { + for { + req := new(Fcall) + if err := c.ch.ReadFcall(c.ctx, req); err != nil { + if err, ok := err.(net.Error); ok { + if err.Timeout() || err.Temporary() { + // TODO(stevvooe): A full idle timeout on the connection + // should be enforced here. No logging because it is quite + // chatty. + continue + } + } + + c.CloseWithError(fmt.Errorf("error reading fcall: %v", err)) + return + } + + select { + case requests <- req: + case <-c.ctx.Done(): + c.CloseWithError(c.ctx.Err()) + return + case <-c.closed: + return + } + } +} + +func (c *conn) write(responses chan *Fcall) { + for { + select { + case resp := <-responses: + // TODO(stevvooe): Correctly protect againt overflowing msize from + // handler. This can be done above, in the main message handler + // loop, by adjusting incoming Tread calls to have a Count that + // won't overflow the msize. + + if err := c.ch.WriteFcall(c.ctx, resp); err != nil { + if err, ok := err.(net.Error); ok { + if err.Timeout() || err.Temporary() { + // TODO(stevvooe): A full idle timeout on the + // connection should be enforced here. We log here, + // since this is less common. + log.Printf("9p server: temporary error writing fcall: %v", err) + continue + } + } + + c.CloseWithError(fmt.Errorf("error writing fcall: %v", err)) + return + } + case <-c.ctx.Done(): + c.CloseWithError(c.ctx.Err()) + return + case <-c.closed: + return + } + } +} + +func (c *conn) Close() error { + return c.CloseWithError(nil) +} + +func (c *conn) CloseWithError(err error) error { + c.once.Do(func() { + if err == nil { + err = ErrClosed + } + + c.err = err + close(c.closed) + }) + + return c.err +} diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/session.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/session.go new file mode 100644 index 000000000..eaca8d701 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/session.go @@ -0,0 +1,42 @@ +package p9p + +import "context" + +// Session provides the central abstraction for a 9p connection. Clients +// implement sessions and servers serve sessions. Sessions can be proxied by +// serving up a client session. +// +// The interface is also wired up with full context support to manage timeouts +// and resource clean up. +// +// Session represents the operations covered in section 5 of the plan 9 manual +// (http://man.cat-v.org/plan_9/5/). Requests are managed internally, so the +// Flush method is handled by the internal implementation. Consider preceeding +// these all with context to control request timeout. +type Session interface { + Auth(ctx context.Context, afid Fid, uname, aname string) (Qid, error) + Attach(ctx context.Context, fid, afid Fid, uname, aname string) (Qid, error) + Clunk(ctx context.Context, fid Fid) error + Remove(ctx context.Context, fid Fid) error + Walk(ctx context.Context, fid Fid, newfid Fid, names ...string) ([]Qid, error) + + // Read follows the semantics of io.ReaderAt.ReadAtt method except it takes + // a contxt and Fid. + Read(ctx context.Context, fid Fid, p []byte, offset int64) (n int, err error) + + // Write follows the semantics of io.WriterAt.WriteAt except takes a context and an Fid. + // + // If n == len(p), no error is returned. + // If n < len(p), io.ErrShortWrite will be returned. + Write(ctx context.Context, fid Fid, p []byte, offset int64) (n int, err error) + + Open(ctx context.Context, fid Fid, mode Flag) (Qid, uint32, error) + Create(ctx context.Context, parent Fid, name string, perm uint32, mode Flag) (Qid, uint32, error) + Stat(ctx context.Context, fid Fid) (Dir, error) + WStat(ctx context.Context, fid Fid, dir Dir) error + + // Version returns the supported version and msize of the session. This + // can be affected by negotiating or the level of support provided by the + // session implementation. + Version() (msize int, version string) +} diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/transport.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/transport.go new file mode 100644 index 000000000..83d3ce193 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/transport.go @@ -0,0 +1,250 @@ +package p9p + +import ( + "errors" + "fmt" + "log" + "net" + "sync" + + "context" +) + +// roundTripper manages the request and response from the client-side. A +// roundTripper must abide by similar rules to the http.RoundTripper. +// Typically, the roundTripper will manage tag assignment and message +// serialization. +type roundTripper interface { + send(ctx context.Context, msg Message) (Message, error) +} + +// transport plays the role of being a client channel manager. It multiplexes +// function calls onto the wire and dispatches responses to blocking calls to +// send. On the whole, transport is thread-safe for calling send +type transport struct { + ctx context.Context + ch Channel + requests chan *fcallRequest + + shutdown chan struct{} + once sync.Once // protect closure of shutdown + closed chan struct{} + + tags uint16 +} + +var _ roundTripper = &transport{} + +func newTransport(ctx context.Context, ch Channel) roundTripper { + t := &transport{ + ctx: ctx, + ch: ch, + requests: make(chan *fcallRequest), + shutdown: make(chan struct{}), + closed: make(chan struct{}), + } + + go t.handle() + + return t +} + +// fcallRequest encompasses the request to send a message via fcall. +type fcallRequest struct { + ctx context.Context + message Message + response chan *Fcall + err chan error +} + +func newFcallRequest(ctx context.Context, msg Message) *fcallRequest { + return &fcallRequest{ + ctx: ctx, + message: msg, + response: make(chan *Fcall, 1), + err: make(chan error, 1), + } +} + +func (t *transport) send(ctx context.Context, msg Message) (Message, error) { + req := newFcallRequest(ctx, msg) + + // dispatch the request. + select { + case <-t.closed: + return nil, ErrClosed + case <-ctx.Done(): + return nil, ctx.Err() + case t.requests <- req: + } + + // wait for the response. + select { + case <-t.closed: + return nil, ErrClosed + case <-ctx.Done(): + return nil, ctx.Err() + case err := <-req.err: + return nil, err + case resp := <-req.response: + if resp.Type == Rerror { + // pack the error into something useful + respmesg, ok := resp.Message.(MessageRerror) + if !ok { + return nil, fmt.Errorf("invalid error response: %v", resp) + } + + return nil, respmesg + } + + return resp.Message, nil + } +} + +// allocateTag returns a valid tag given a tag pool map. It receives a hint as +// to where to start the tag search. It returns an error if the allocation is +// not possible. The provided map must not contain NOTAG as a key. +func allocateTag(r *fcallRequest, m map[Tag]*fcallRequest, hint Tag) (Tag, error) { + // The tag pool is depleted if 65535 (0xFFFF) tags are taken. + if len(m) >= 0xFFFF { + return 0, errors.New("tag pool depleted") + } + + // Look for the first tag that doesn't exist in the map and return it. + for i := 0; i < 0xFFFF; i++ { + hint++ + if hint == NOTAG { + hint = 0 + } + + if _, exists := m[hint]; !exists { + return hint, nil + } + } + + return 0, errors.New("allocateTag: unexpected error") +} + +// handle takes messages off the wire and wakes up the waiting tag call. +func (t *transport) handle() { + defer func() { + log.Println("exited handle loop") + close(t.closed) + }() + + // the following variable block are protected components owned by this thread. + var ( + responses = make(chan *Fcall) + // outstanding provides a map of tags to outstanding requests. + outstanding = map[Tag]*fcallRequest{} + selected Tag + ) + + // loop to read messages off of the connection + go func() { + defer func() { + log.Println("exited read loop") + t.close() // single main loop + }() + loop: + for { + fcall := new(Fcall) + if err := t.ch.ReadFcall(t.ctx, fcall); err != nil { + switch err := err.(type) { + case net.Error: + if err.Timeout() || err.Temporary() { + // BUG(stevvooe): There may be partial reads under + // timeout errors where this is actually fatal. + + // can only retry if we haven't offset the frame. + continue loop + } + } + + log.Println("fatal error reading msg:", err) + return + } + + select { + case <-t.ctx.Done(): + return + case <-t.closed: + log.Println("transport closed") + return + case responses <- fcall: + } + } + }() + + for { + select { + case req := <-t.requests: + var err error + + selected, err = allocateTag(req, outstanding, selected) + if err != nil { + req.err <- err + continue + } + + outstanding[selected] = req + fcall := newFcall(selected, req.message) + + // TODO(stevvooe): Consider the case of requests that never + // receive a response. We need to remove the fcall context from + // the tag map and dealloc the tag. We may also want to send a + // flush for the tag. + if err := t.ch.WriteFcall(req.ctx, fcall); err != nil { + delete(outstanding, fcall.Tag) + req.err <- err + } + case b := <-responses: + req, ok := outstanding[b.Tag] + if !ok { + // BUG(stevvooe): The exact handling of an unknown tag is + // unclear at this point. These may not necessarily fatal to + // the session, since they could be messages that the client no + // longer cares for. When we figure this out, replace this + // panic with something more sensible. + panic(fmt.Sprintf("unknown tag received: %v", b)) + } + + // BUG(stevvooe): Must detect duplicate tag and ensure that we are + // waking up the right caller. If a duplicate is received, the + // entry should not be deleted. + delete(outstanding, b.Tag) + + req.response <- b + + // TODO(stevvooe): Reclaim tag id. + case <-t.shutdown: + return + case <-t.ctx.Done(): + return + } + } +} + +func (t *transport) flush(ctx context.Context, tag Tag) error { + // TODO(stevvooe): We need to fire and forget flush messages when a call + // context gets cancelled. + panic("not implemented") +} + +func (t *transport) Close() error { + t.close() + + select { + case <-t.closed: + return nil + case <-t.ctx.Done(): + return t.ctx.Err() + } +} + +// close starts the shutdown process. +func (t *transport) close() { + t.once.Do(func() { + close(t.shutdown) + }) +} diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/types.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/types.go new file mode 100644 index 000000000..e68fd8da1 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/types.go @@ -0,0 +1,149 @@ +package p9p + +import ( + "fmt" + "time" +) + +const ( + // DefaultMSize messages size used to establish a session. + DefaultMSize = 64 << 10 + + // DefaultVersion for this package. Currently, the only supported version. + DefaultVersion = "9P2000" +) + +// Mode constants for use Dir.Mode. +const ( + DMDIR = 0x80000000 // mode bit for directories + DMAPPEND = 0x40000000 // mode bit for append only files + DMEXCL = 0x20000000 // mode bit for exclusive use files + DMMOUNT = 0x10000000 // mode bit for mounted channel + DMAUTH = 0x08000000 // mode bit for authentication file + DMTMP = 0x04000000 // mode bit for non-backed-up files + + // 9p2000.u extensions + + DMSYMLINK = 0x02000000 + DMDEVICE = 0x00800000 + DMNAMEDPIPE = 0x00200000 + DMSOCKET = 0x00100000 + DMSETUID = 0x00080000 + DMSETGID = 0x00040000 + + DMREAD = 0x4 // mode bit for read permission + DMWRITE = 0x2 // mode bit for write permission + DMEXEC = 0x1 // mode bit for execute permission +) + +// Flag defines the flag type for use with open and create +type Flag uint8 + +// Constants to use when opening files. +const ( + OREAD Flag = 0x00 // open for read + OWRITE Flag = 0x01 // write + ORDWR Flag = 0x02 // read and write + OEXEC Flag = 0x03 // execute, == read but check execute permission + + // PROPOSAL(stevvooe): Possible protocal extension to allow the create of + // symlinks. Initially, the link is created with no value. Read and write + // to read and set the link value. + + OSYMLINK Flag = 0x04 + + OTRUNC Flag = 0x10 // or'ed in (except for exec), truncate file first + OCEXEC Flag = 0x20 // or'ed in, close on exec + ORCLOSE Flag = 0x40 // or'ed in, remove on close +) + +// QType indicates the type of a resource within the Qid. +type QType uint8 + +// Constants for use in Qid to indicate resource type. +const ( + QTDIR QType = 0x80 // type bit for directories + QTAPPEND QType = 0x40 // type bit for append only files + QTEXCL QType = 0x20 // type bit for exclusive use files + QTMOUNT QType = 0x10 // type bit for mounted channel + QTAUTH QType = 0x08 // type bit for authentication file + QTTMP QType = 0x04 // type bit for not-backed-up file + QTFILE QType = 0x00 // plain file +) + +func (qt QType) String() string { + switch qt { + case QTDIR: + return "dir" + case QTAPPEND: + return "append" + case QTEXCL: + return "excl" + case QTMOUNT: + return "mount" + case QTAUTH: + return "auth" + case QTTMP: + return "tmp" + case QTFILE: + return "file" + } + + return "unknown" +} + +// Tag uniquely identifies an outstanding fcall in a 9p session. +type Tag uint16 + +// NOTAG is a reserved values for messages sent before establishing a session, +// such as Tversion. +const NOTAG Tag = ^Tag(0) + +// Fid defines a type to hold Fid values. +type Fid uint32 + +// NOFID indicates the lack of an Fid. +const NOFID Fid = ^Fid(0) + +// Qid indicates the type, path and version of the resource returned by a +// server. It is only valid for a session. +// +// Typically, a client maintains a mapping of Fid-Qid as Qids are returned by +// the server. +type Qid struct { + Type QType `9p:"type,1"` + Version uint32 + Path uint64 +} + +func (qid Qid) String() string { + return fmt.Sprintf("qid(%v, v=%x, p=%x)", + qid.Type, qid.Version, qid.Path) +} + +// Dir defines the structure used for expressing resources in stat/wstat and +// when reading directories. +type Dir struct { + Type uint16 + Dev uint32 + Qid Qid + Mode uint32 + + // BUG(stevvooe): The Year 2038 is coming soon. 9p wire protocol has these + // as 4 byte epoch times. Some possibilities include time dilation fields + // or atemporal files. We can also just not use them and set them to zero. + + AccessTime time.Time + ModTime time.Time + + Length uint64 + Name string + UID string + GID string + MUID string +} + +func (d Dir) String() string { + return fmt.Sprintf("dir(%v mode=%v atime=%v mtime=%v length=%v name=%v uid=%v gid=%v muid=%v)", + d.Qid, d.Mode, d.AccessTime, d.ModTime, d.Length, d.Name, d.UID, d.GID, d.MUID) +} diff --git a/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/version.go b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/version.go new file mode 100644 index 000000000..d58caf899 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/docker/go-p9p/version.go @@ -0,0 +1,112 @@ +package p9p + +import ( + "fmt" + + "context" +) + +// NOTE(stevvooe): This file contains functions for negotiating version on the +// client and server. There are some nasty details to get right for +// downgrading the connection on the server-side that are not present yet. +// Really, these should be refactored into some sort of channel type that can +// support resets through version messages during the protocol exchange. + +// clientnegotiate negiotiates the protocol version using channel, blocking +// until a response is received. The received value will be the version +// implemented by the server. +func clientnegotiate(ctx context.Context, ch Channel, version string) (string, error) { + req := newFcall(NOTAG, MessageTversion{ + MSize: uint32(ch.MSize()), + Version: version, + }) + + if err := ch.WriteFcall(ctx, req); err != nil { + return "", err + } + + resp := new(Fcall) + if err := ch.ReadFcall(ctx, resp); err != nil { + return "", err + } + + switch v := resp.Message.(type) { + case MessageRversion: + + if v.Version != version { + // TODO(stevvooe): A stubborn client indeed! + return "", fmt.Errorf("unsupported server version: %v", version) + } + + if int(v.MSize) < ch.MSize() { + // upgrade msize if server differs. + ch.SetMSize(int(v.MSize)) + } + + return v.Version, nil + case error: + return "", v + default: + return "", ErrUnexpectedMsg + } +} + +// servernegotiate blocks until a version message is received or a timeout +// occurs. The msize for the tranport will be set from the negotiation. If +// negotiate returns nil, a server may proceed with the connection. +// +// In the future, it might be better to handle the version messages in a +// separate object that manages the session. Each set of version requests +// effectively "reset" a connection, meaning all fids get clunked and all +// outstanding IO is aborted. This is probably slightly racy, in practice with +// a misbehaved client. The main issue is that we cannot tell which session +// messages belong to. +func servernegotiate(ctx context.Context, ch Channel, version string) error { + // wait for the version message over the transport. + req := new(Fcall) + if err := ch.ReadFcall(ctx, req); err != nil { + return err + } + + mv, ok := req.Message.(MessageTversion) + if !ok { + return fmt.Errorf("expected version message: %v", mv) + } + + respmsg := MessageRversion{ + Version: version, + } + + if mv.Version != version { + // TODO(stevvooe): Not the best place to do version handling. We need + // to have a way to pass supported versions into this method then have + // it return the actual version. For now, respond with 9P2000 for + // anything that doesn't match the provided version string. + // + // version(9) says "The server may respond with the client’s + // version string, or a version string identifying an earlier + // defined protocol version. Currently, the only defined + // version is the 6 characters 9P2000." Therefore, it is always + // OK to respond with this. + respmsg.Version = "9P2000" + } + + if int(mv.MSize) < ch.MSize() { + // if the server msize is too large, use the client's suggested msize. + ch.SetMSize(int(mv.MSize)) + respmsg.MSize = mv.MSize + } else { + respmsg.MSize = uint32(ch.MSize()) + } + + resp := newFcall(NOTAG, respmsg) + if err := ch.WriteFcall(ctx, resp); err != nil { + return err + } + + if respmsg.Version == "unknown" { + return fmt.Errorf("bad version negotiation") + } + + return nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/google/uuid/LICENSE b/src/cmd/linuxkit/vendor/github.com/google/uuid/LICENSE new file mode 100644 index 000000000..5dc68268d --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/google/uuid/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009,2014 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/cmd/linuxkit/vendor/github.com/google/uuid/README.md b/src/cmd/linuxkit/vendor/github.com/google/uuid/README.md new file mode 100644 index 000000000..21205eaeb --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/google/uuid/README.md @@ -0,0 +1,23 @@ +**This package is currently in development and the API may not be stable.** + +The API will become stable with v1. + +# uuid ![build status](https://travis-ci.org/google/uuid.svg?branch=master) +The uuid package generates and inspects UUIDs based on +[RFC 4122](http://tools.ietf.org/html/rfc4122) +and DCE 1.1: Authentication and Security Services. + +This package is based on the github.com/pborman/uuid package (previously named +code.google.com/p/go-uuid). It differs from these earlier packages in that +a UUID is a 16 byte array rather than a byte slice. One loss due to this +change is the ability to represent an invalid UUID (vs a NIL UUID). + +###### Install +`go get github.com/google/uuid` + +###### Documentation +[![GoDoc](https://godoc.org/github.com/google/uuid?status.svg)](http://godoc.org/github.com/google/uuid) + +Full `go doc` style documentation for the package can be viewed online without +installing this package by using the GoDoc site here: +http://godoc.org/github.com/google/uuid diff --git a/src/cmd/linuxkit/vendor/github.com/google/uuid/dce.go b/src/cmd/linuxkit/vendor/github.com/google/uuid/dce.go new file mode 100644 index 000000000..fa820b9d3 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/google/uuid/dce.go @@ -0,0 +1,80 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "fmt" + "os" +) + +// A Domain represents a Version 2 domain +type Domain byte + +// Domain constants for DCE Security (Version 2) UUIDs. +const ( + Person = Domain(0) + Group = Domain(1) + Org = Domain(2) +) + +// NewDCESecurity returns a DCE Security (Version 2) UUID. +// +// The domain should be one of Person, Group or Org. +// On a POSIX system the id should be the users UID for the Person +// domain and the users GID for the Group. The meaning of id for +// the domain Org or on non-POSIX systems is site defined. +// +// For a given domain/id pair the same token may be returned for up to +// 7 minutes and 10 seconds. +func NewDCESecurity(domain Domain, id uint32) (UUID, error) { + uuid, err := NewUUID() + if err == nil { + uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2 + uuid[9] = byte(domain) + binary.BigEndian.PutUint32(uuid[0:], id) + } + return uuid, err +} + +// NewDCEPerson returns a DCE Security (Version 2) UUID in the person +// domain with the id returned by os.Getuid. +// +// NewDCESecurity(Person, uint32(os.Getuid())) +func NewDCEPerson() (UUID, error) { + return NewDCESecurity(Person, uint32(os.Getuid())) +} + +// NewDCEGroup returns a DCE Security (Version 2) UUID in the group +// domain with the id returned by os.Getgid. +// +// NewDCESecurity(Group, uint32(os.Getgid())) +func NewDCEGroup() (UUID, error) { + return NewDCESecurity(Group, uint32(os.Getgid())) +} + +// Domain returns the domain for a Version 2 UUID. Domains are only defined +// for Version 2 UUIDs. +func (uuid UUID) Domain() Domain { + return Domain(uuid[9]) +} + +// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2 +// UUIDs. +func (uuid UUID) ID() uint32 { + return binary.BigEndian.Uint32(uuid[0:4]) +} + +func (d Domain) String() string { + switch d { + case Person: + return "Person" + case Group: + return "Group" + case Org: + return "Org" + } + return fmt.Sprintf("Domain%d", int(d)) +} diff --git a/src/cmd/linuxkit/vendor/github.com/google/uuid/doc.go b/src/cmd/linuxkit/vendor/github.com/google/uuid/doc.go new file mode 100644 index 000000000..5b8a4b9af --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/google/uuid/doc.go @@ -0,0 +1,12 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package uuid generates and inspects UUIDs. +// +// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security +// Services. +// +// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to +// maps or compared directly. +package uuid diff --git a/src/cmd/linuxkit/vendor/github.com/google/uuid/hash.go b/src/cmd/linuxkit/vendor/github.com/google/uuid/hash.go new file mode 100644 index 000000000..4fc5a77df --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/google/uuid/hash.go @@ -0,0 +1,53 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "crypto/md5" + "crypto/sha1" + "hash" +) + +// Well known namespace IDs and UUIDs +var ( + NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) + Nil UUID // empty UUID, all zeros +) + +// NewHash returns a new UUID derived from the hash of space concatenated with +// data generated by h. The hash should be at least 16 byte in length. The +// first 16 bytes of the hash are used to form the UUID. The version of the +// UUID will be the lower 4 bits of version. NewHash is used to implement +// NewMD5 and NewSHA1. +func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { + h.Reset() + h.Write(space[:]) + h.Write([]byte(data)) + s := h.Sum(nil) + var uuid UUID + copy(uuid[:], s) + uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) + uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant + return uuid +} + +// NewMD5 returns a new MD5 (Version 3) UUID based on the +// supplied name space and data. It is the same as calling: +// +// NewHash(md5.New(), space, data, 3) +func NewMD5(space UUID, data []byte) UUID { + return NewHash(md5.New(), space, data, 3) +} + +// NewSHA1 returns a new SHA1 (Version 5) UUID based on the +// supplied name space and data. It is the same as calling: +// +// NewHash(sha1.New(), space, data, 5) +func NewSHA1(space UUID, data []byte) UUID { + return NewHash(sha1.New(), space, data, 5) +} diff --git a/src/cmd/linuxkit/vendor/github.com/google/uuid/marshal.go b/src/cmd/linuxkit/vendor/github.com/google/uuid/marshal.go new file mode 100644 index 000000000..84bbc5880 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/google/uuid/marshal.go @@ -0,0 +1,39 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "fmt" + +// MarshalText implements encoding.TextMarshaler. +func (uuid UUID) MarshalText() ([]byte, error) { + var js [36]byte + encodeHex(js[:], uuid) + return js[:], nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (uuid *UUID) UnmarshalText(data []byte) error { + // See comment in ParseBytes why we do this. + // id, err := ParseBytes(data) + id, err := ParseBytes(data) + if err == nil { + *uuid = id + } + return err +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (uuid UUID) MarshalBinary() ([]byte, error) { + return uuid[:], nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (uuid *UUID) UnmarshalBinary(data []byte) error { + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + copy(uuid[:], data) + return nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/google/uuid/node.go b/src/cmd/linuxkit/vendor/github.com/google/uuid/node.go new file mode 100644 index 000000000..5f0156a2e --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/google/uuid/node.go @@ -0,0 +1,103 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "net" + "sync" +) + +var ( + nodeMu sync.Mutex + interfaces []net.Interface // cached list of interfaces + ifname string // name of interface being used + nodeID [6]byte // hardware for version 1 UUIDs + zeroID [6]byte // nodeID with only 0's +) + +// NodeInterface returns the name of the interface from which the NodeID was +// derived. The interface "user" is returned if the NodeID was set by +// SetNodeID. +func NodeInterface() string { + defer nodeMu.Unlock() + nodeMu.Lock() + return ifname +} + +// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs. +// If name is "" then the first usable interface found will be used or a random +// Node ID will be generated. If a named interface cannot be found then false +// is returned. +// +// SetNodeInterface never fails when name is "". +func SetNodeInterface(name string) bool { + defer nodeMu.Unlock() + nodeMu.Lock() + return setNodeInterface(name) +} + +func setNodeInterface(name string) bool { + if interfaces == nil { + var err error + interfaces, err = net.Interfaces() + if err != nil && name != "" { + return false + } + } + + for _, ifs := range interfaces { + if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { + copy(nodeID[:], ifs.HardwareAddr) + ifname = ifs.Name + return true + } + } + + // We found no interfaces with a valid hardware address. If name + // does not specify a specific interface generate a random Node ID + // (section 4.1.6) + if name == "" { + randomBits(nodeID[:]) + return true + } + return false +} + +// NodeID returns a slice of a copy of the current Node ID, setting the Node ID +// if not already set. +func NodeID() []byte { + defer nodeMu.Unlock() + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") + } + nid := nodeID + return nid[:] +} + +// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes +// of id are used. If id is less than 6 bytes then false is returned and the +// Node ID is not set. +func SetNodeID(id []byte) bool { + if len(id) < 6 { + return false + } + defer nodeMu.Unlock() + nodeMu.Lock() + copy(nodeID[:], id) + ifname = "user" + return true +} + +// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is +// not valid. The NodeID is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) NodeID() []byte { + if len(uuid) != 16 { + return nil + } + var node [6]byte + copy(node[:], uuid[10:]) + return node[:] +} diff --git a/src/cmd/linuxkit/vendor/github.com/google/uuid/sql.go b/src/cmd/linuxkit/vendor/github.com/google/uuid/sql.go new file mode 100644 index 000000000..f326b54db --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/google/uuid/sql.go @@ -0,0 +1,59 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "database/sql/driver" + "fmt" +) + +// Scan implements sql.Scanner so UUIDs can be read from databases transparently +// Currently, database types that map to string and []byte are supported. Please +// consult database-specific driver documentation for matching types. +func (uuid *UUID) Scan(src interface{}) error { + switch src := src.(type) { + case nil: + return nil + + case string: + // if an empty UUID comes from a table, we return a null UUID + if src == "" { + return nil + } + + // see Parse for required string format + u, err := Parse(src) + if err != nil { + return fmt.Errorf("Scan: %v", err) + } + + *uuid = u + + case []byte: + // if an empty UUID comes from a table, we return a null UUID + if len(src) == 0 { + return nil + } + + // assumes a simple slice of bytes if 16 bytes + // otherwise attempts to parse + if len(src) != 16 { + return uuid.Scan(string(src)) + } + copy((*uuid)[:], src) + + default: + return fmt.Errorf("Scan: unable to scan type %T into UUID", src) + } + + return nil +} + +// Value implements sql.Valuer so that UUIDs can be written to databases +// transparently. Currently, UUIDs map to strings. Please consult +// database-specific driver documentation for matching types. +func (uuid UUID) Value() (driver.Value, error) { + return uuid.String(), nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/google/uuid/time.go b/src/cmd/linuxkit/vendor/github.com/google/uuid/time.go new file mode 100644 index 000000000..fd7fe0ac4 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/google/uuid/time.go @@ -0,0 +1,123 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "sync" + "time" +) + +// A Time represents a time as the number of 100's of nanoseconds since 15 Oct +// 1582. +type Time int64 + +const ( + lillian = 2299160 // Julian day of 15 Oct 1582 + unix = 2440587 // Julian day of 1 Jan 1970 + epoch = unix - lillian // Days between epochs + g1582 = epoch * 86400 // seconds between epochs + g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs +) + +var ( + timeMu sync.Mutex + lasttime uint64 // last time we returned + clockSeq uint16 // clock sequence for this run + + timeNow = time.Now // for testing +) + +// UnixTime converts t the number of seconds and nanoseconds using the Unix +// epoch of 1 Jan 1970. +func (t Time) UnixTime() (sec, nsec int64) { + sec = int64(t - g1582ns100) + nsec = (sec % 10000000) * 100 + sec /= 10000000 + return sec, nsec +} + +// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and +// clock sequence as well as adjusting the clock sequence as needed. An error +// is returned if the current time cannot be determined. +func GetTime() (Time, uint16, error) { + defer timeMu.Unlock() + timeMu.Lock() + return getTime() +} + +func getTime() (Time, uint16, error) { + t := timeNow() + + // If we don't have a clock sequence already, set one. + if clockSeq == 0 { + setClockSequence(-1) + } + now := uint64(t.UnixNano()/100) + g1582ns100 + + // If time has gone backwards with this clock sequence then we + // increment the clock sequence + if now <= lasttime { + clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000 + } + lasttime = now + return Time(now), clockSeq, nil +} + +// ClockSequence returns the current clock sequence, generating one if not +// already set. The clock sequence is only used for Version 1 UUIDs. +// +// The uuid package does not use global static storage for the clock sequence or +// the last time a UUID was generated. Unless SetClockSequence is used, a new +// random clock sequence is generated the first time a clock sequence is +// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) +func ClockSequence() int { + defer timeMu.Unlock() + timeMu.Lock() + return clockSequence() +} + +func clockSequence() int { + if clockSeq == 0 { + setClockSequence(-1) + } + return int(clockSeq & 0x3fff) +} + +// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to +// -1 causes a new sequence to be generated. +func SetClockSequence(seq int) { + defer timeMu.Unlock() + timeMu.Lock() + setClockSequence(seq) +} + +func setClockSequence(seq int) { + if seq == -1 { + var b [2]byte + randomBits(b[:]) // clock sequence + seq = int(b[0])<<8 | int(b[1]) + } + old_seq := clockSeq + clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant + if old_seq != clockSeq { + lasttime = 0 + } +} + +// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in +// uuid. The time is only defined for version 1 and 2 UUIDs. +func (uuid UUID) Time() Time { + time := int64(binary.BigEndian.Uint32(uuid[0:4])) + time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 + time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 + return Time(time) +} + +// ClockSequence returns the clock sequence encoded in uuid. +// The clock sequence is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) ClockSequence() int { + return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff +} diff --git a/src/cmd/linuxkit/vendor/github.com/google/uuid/util.go b/src/cmd/linuxkit/vendor/github.com/google/uuid/util.go new file mode 100644 index 000000000..5ea6c7378 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/google/uuid/util.go @@ -0,0 +1,43 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "io" +) + +// randomBits completely fills slice b with random data. +func randomBits(b []byte) { + if _, err := io.ReadFull(rander, b); err != nil { + panic(err.Error()) // rand should never fail + } +} + +// xvalues returns the value of a byte as a hexadecimal digit or 255. +var xvalues = [256]byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +} + +// xtob converts hex characters x1 and x2 into a byte. +func xtob(x1, x2 byte) (byte, bool) { + b1 := xvalues[x1] + b2 := xvalues[x2] + return (b1 << 4) | b2, b1 != 255 && b2 != 255 +} diff --git a/src/cmd/linuxkit/vendor/github.com/google/uuid/uuid.go b/src/cmd/linuxkit/vendor/github.com/google/uuid/uuid.go new file mode 100644 index 000000000..23161a86c --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/google/uuid/uuid.go @@ -0,0 +1,191 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "errors" + "fmt" + "io" + "strings" +) + +// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC +// 4122. +type UUID [16]byte + +// A Version represents a UUID's version. +type Version byte + +// A Variant represents a UUID's variant. +type Variant byte + +// Constants returned by Variant. +const ( + Invalid = Variant(iota) // Invalid UUID + RFC4122 // The variant specified in RFC4122 + Reserved // Reserved, NCS backward compatibility. + Microsoft // Reserved, Microsoft Corporation backward compatibility. + Future // Reserved for future definition. +) + +var rander = rand.Reader // random function + +// Parse decodes s into a UUID or returns an error. Both the UUID form of +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded. +func Parse(s string) (UUID, error) { + var uuid UUID + if len(s) != 36 { + if len(s) != 36+9 { + return uuid, fmt.Errorf("invalid UUID length: %d", len(s)) + } + if strings.ToLower(s[:9]) != "urn:uuid:" { + return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) + } + s = s[9:] + } + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return uuid, errors.New("invalid UUID format") + } + for i, x := range [16]int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34} { + if v, ok := xtob(s[x], s[x+1]); !ok { + return uuid, errors.New("invalid UUID format") + } else { + uuid[i] = v + } + } + return uuid, nil +} + +// ParseBytes is like Parse, except it parses a byte slice instead of a string. +func ParseBytes(b []byte) (UUID, error) { + var uuid UUID + if len(b) != 36 { + if len(b) != 36+9 { + return uuid, fmt.Errorf("invalid UUID length: %d", len(b)) + } + if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) { + return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) + } + b = b[9:] + } + if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' { + return uuid, errors.New("invalid UUID format") + } + for i, x := range [16]int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34} { + if v, ok := xtob(b[x], b[x+1]); !ok { + return uuid, errors.New("invalid UUID format") + } else { + uuid[i] = v + } + } + return uuid, nil +} + +// Must returns uuid if err is nil and panics otherwise. +func Must(uuid UUID, err error) UUID { + if err != nil { + panic(err) + } + return uuid +} + +// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// , or "" if uuid is invalid. +func (uuid UUID) String() string { + var buf [36]byte + encodeHex(buf[:], uuid) + return string(buf[:]) +} + +// URN returns the RFC 2141 URN form of uuid, +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. +func (uuid UUID) URN() string { + var buf [36 + 9]byte + copy(buf[:], "urn:uuid:") + encodeHex(buf[9:], uuid) + return string(buf[:]) +} + +func encodeHex(dst []byte, uuid UUID) { + hex.Encode(dst[:], uuid[:4]) + dst[8] = '-' + hex.Encode(dst[9:13], uuid[4:6]) + dst[13] = '-' + hex.Encode(dst[14:18], uuid[6:8]) + dst[18] = '-' + hex.Encode(dst[19:23], uuid[8:10]) + dst[23] = '-' + hex.Encode(dst[24:], uuid[10:]) +} + +// Variant returns the variant encoded in uuid. +func (uuid UUID) Variant() Variant { + switch { + case (uuid[8] & 0xc0) == 0x80: + return RFC4122 + case (uuid[8] & 0xe0) == 0xc0: + return Microsoft + case (uuid[8] & 0xe0) == 0xe0: + return Future + default: + return Reserved + } +} + +// Version returns the version of uuid. +func (uuid UUID) Version() Version { + return Version(uuid[6] >> 4) +} + +func (v Version) String() string { + if v > 15 { + return fmt.Sprintf("BAD_VERSION_%d", v) + } + return fmt.Sprintf("VERSION_%d", v) +} + +func (v Variant) String() string { + switch v { + case RFC4122: + return "RFC4122" + case Reserved: + return "Reserved" + case Microsoft: + return "Microsoft" + case Future: + return "Future" + case Invalid: + return "Invalid" + } + return fmt.Sprintf("BadVariant%d", int(v)) +} + +// SetRand sets the random number generator to r, which implements io.Reader. +// If r.Read returns an error when the package requests random data then +// a panic will be issued. +// +// Calling SetRand with nil sets the random number generator to the default +// generator. +func SetRand(r io.Reader) { + if r == nil { + rander = rand.Reader + return + } + rander = r +} diff --git a/src/cmd/linuxkit/vendor/github.com/google/uuid/version1.go b/src/cmd/linuxkit/vendor/github.com/google/uuid/version1.go new file mode 100644 index 000000000..199a1ac65 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/google/uuid/version1.go @@ -0,0 +1,44 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" +) + +// NewUUID returns a Version 1 UUID based on the current NodeID and clock +// sequence, and the current time. If the NodeID has not been set by SetNodeID +// or SetNodeInterface then it will be set automatically. If the NodeID cannot +// be set NewUUID returns nil. If clock sequence has not been set by +// SetClockSequence then it will be set automatically. If GetTime fails to +// return the current NewUUID returns nil and an error. +// +// In most cases, New should be used. +func NewUUID() (UUID, error) { + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") + } + nodeMu.Unlock() + + var uuid UUID + now, seq, err := GetTime() + if err != nil { + return uuid, err + } + + timeLow := uint32(now & 0xffffffff) + timeMid := uint16((now >> 32) & 0xffff) + timeHi := uint16((now >> 48) & 0x0fff) + timeHi |= 0x1000 // Version 1 + + binary.BigEndian.PutUint32(uuid[0:], timeLow) + binary.BigEndian.PutUint16(uuid[4:], timeMid) + binary.BigEndian.PutUint16(uuid[6:], timeHi) + binary.BigEndian.PutUint16(uuid[8:], seq) + copy(uuid[10:], nodeID[:]) + + return uuid, nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/google/uuid/version4.go b/src/cmd/linuxkit/vendor/github.com/google/uuid/version4.go new file mode 100644 index 000000000..74c4e6c9f --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/google/uuid/version4.go @@ -0,0 +1,38 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "io" + +// New creates a new random UUID or panics. New is equivalent to +// the expression +// +// uuid.Must(uuid.NewRandom()) +func New() UUID { + return Must(NewRandom()) +} + +// NewRandom returns a Random (Version 4) UUID or panics. +// +// The strength of the UUIDs is based on the strength of the crypto/rand +// package. +// +// A note about uniqueness derived from the UUID Wikipedia entry: +// +// Randomly generated UUIDs have 122 random bits. One's annual risk of being +// hit by a meteorite is estimated to be one chance in 17 billion, that +// means the probability is about 0.00000000006 (6 × 10−11), +// equivalent to the odds of creating a few tens of trillions of UUIDs in a +// year and having one duplicate. +func NewRandom() (UUID, error) { + var uuid UUID + _, err := io.ReadFull(rander, uuid[:]) + if err != nil { + return Nil, err + } + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid, nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/moby/datakit/LICENSE.md b/src/cmd/linuxkit/vendor/github.com/moby/datakit/LICENSE.md new file mode 100644 index 000000000..8f3fee627 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/datakit/LICENSE.md @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2013-2016 Docker, Inc. + + 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 + + https://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. diff --git a/src/cmd/linuxkit/vendor/github.com/moby/datakit/README.md b/src/cmd/linuxkit/vendor/github.com/moby/datakit/README.md new file mode 100644 index 000000000..b1fc42e37 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/datakit/README.md @@ -0,0 +1,112 @@ +## DataKit -- Orchestrate applications using a Git-like dataflow + +*DataKit* is a tool to orchestrate applications using a Git-like dataflow. It +revisits the UNIX pipeline concept, with a modern twist: streams of +tree-structured data instead of raw text. DataKit allows you to define +complex build pipelines over version-controlled data. + +DataKit is currently used as the coordination +layer for [HyperKit](http://github.com/docker/hyperkit), the +hypervisor component of +[Docker for Mac and Windows](https://blog.docker.com/2016/03/docker-for-mac-windows-beta/), and +for the [DataKitCI][] continuous integration system. + +--- + +[![Build Status (OSX, Linux)](https://travis-ci.org/moby/datakit.svg)](https://travis-ci.org/moby/datakit) +[![Build status (Windows)](https://ci.appveyor.com/api/projects/status/6qrdgiqbhi4sehmy/branch/master?svg=true)](https://ci.appveyor.com/project/moby/datakit/branch/master) +[![docs](https://img.shields.io/badge/doc-online-blue.svg)](https://docker.github.io/datakit/) + +There are several components in this repository: + +- `src` contains the main DataKit service. This is a Git-like database to which other services can connect. +- `ci` contains [DataKitCI][], a continuous integration system that uses DataKit to monitor repositories and store build results. +- `ci/self-ci` is the CI configuration for DataKitCI that tests DataKit itself. +- `bridge/github` is a service that monitors repositories on GitHub and syncs their metadata with a DataKit database. + e.g. when a pull request is opened or updated, it will commit that information to DataKit. If you commit a status message to DataKit, the bridge will push it to GitHub. +- `bridge/local` is a drop-in replacement for `bridge/github` that just monitors a local Git repository. This is useful for local testing. + +### Quick Start + +The easiest way to use DataKit is to start both the server and the client in containers. + +To expose a Git repository as a 9p endpoint on port 5640 on a private network, run: + +```shell +$ docker network create datakit-net # create a private network +$ docker run -it --net datakit-net --name datakit -v :/data datakit/db +``` + +*Note*: The `--name datakit` option is mandatory. It will allow the client +to connect to a known name on the private network. + +You can then start a DataKit client, which will mount the 9p endpoint and +expose the database as a filesystem API: + +```shell +# In an other terminal +$ docker run -it --privileged --net datakit-net datakit/client +$ ls /db +branch remotes snapshots trees +``` + +*Note*: the `--privileged` option is needed because the container will have +to mount the 9p endpoint into its local filesystem. + +Now you can explore, edit and script `/db`. See the +[Filesystem API][] +for more details. + +### Building + +The easiest way to build the DataKit project is to use [docker](https://docker.com), +(which is what the +[start-datakit.sh](https://github.com/moby/datakit/blob/master/scripts/start-datakit.sh) script +does under the hood): + +```shell +docker build -t datakit/db -f Dockerfile . +docker run -p 5640:5640 -it --rm datakit/db --listen-9p=tcp://0.0.0.0:5640 +``` +These commands will expose the database's 9p endpoint on port 5640. + +If you want to build the project from source without Docker, you will need to install +[ocaml](http://ocaml.org/) and [opam](http://opam.ocaml.org/). Then write: + +```shell +$ make depends +$ make && make test +``` + +For information about command-line options: + +```shell +$ datakit --help +``` + +## Prometheus metric reporting + +Run with `--listen-prometheus 9090` to expose metrics at `http://*:9090/metrics`. + +Note: there is no encryption and no access control. You are expected to run the +database in a container and to not export this port to the outside world. You +can either collect the metrics by running a Prometheus service in a container +on the same Docker network, or front the service with nginx or similar if you +want to collect metrics remotely. + +## Language bindings + +* **Go** bindings are in the `api/go` directory. +* **OCaml** bindings are in the `api/ocaml` directory. See `examples/ocaml-client` for an example. + +## Licensing + +DataKit is licensed under the Apache License, Version 2.0. See +[LICENSE](https://github.com/moby/datakit/blob/master/LICENSE.md) for the full +license text. + +Contributions are welcome under the terms of this license. You may wish to browse +the [weekly reports](reports) to read about overall activity in the repository. + +[DataKitCI]: https://github.com/moby/datakit/tree/master/ci +[Filesystem API]: https://github.com/moby/datakit/tree/master/9p.md diff --git a/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/README b/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/README new file mode 100644 index 000000000..ccc0448e6 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/README @@ -0,0 +1 @@ +To run test on windows, launch the a datakit server with --url \\.\pipe\datakit-test \ No newline at end of file diff --git a/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/client.go b/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/client.go new file mode 100644 index 000000000..042956c0f --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/client.go @@ -0,0 +1,352 @@ +package datakit + +import ( + "bytes" + "io" + "log" + "net" + "sync" + + p9p "github.com/docker/go-p9p" + "context" +) + +type Client struct { + conn net.Conn + session p9p.Session + m *sync.Mutex + c *sync.Cond + usedFids map[p9p.Fid]bool + freeFids []p9p.Fid + root p9p.Fid +} + +var badFid = p9p.Fid(0) + +var rwx = p9p.DMREAD | p9p.DMWRITE | p9p.DMEXEC +var rx = p9p.DMREAD | p9p.DMEXEC +var rw = p9p.DMREAD | p9p.DMWRITE +var r = p9p.DMREAD +var dirperm = uint32(rwx<<6 | rx<<3 | rx | p9p.DMDIR) +var fileperm = uint32(rw<<6 | r<<3 | r) + +// Dial opens a connection to a 9P server +func Dial(ctx context.Context, network, address string) (*Client, error) { + log.Println("Dialling", network, address) + conn, err := net.Dial(network, address) + if err != nil { + return nil, err + } + return NewClient(ctx, conn) +} + +// NewClient creates opens a connection with the p9p server +func NewClient(ctx context.Context, conn net.Conn) (*Client, error) { + session, err := p9p.NewSession(ctx, conn) + if err != nil { + log.Println("Failed to establish 9P session to", err) + return nil, err + } + root := p9p.Fid(1) + if _, err := session.Attach(ctx, root, p9p.NOFID, "anyone", "/"); err != nil { + log.Println("Failed to Attach to filesystem", err) + return nil, err + } + usedFids := make(map[p9p.Fid]bool, 0) + freeFids := make([]p9p.Fid, 0) + for i := 0; i < 128; i++ { + fid := p9p.Fid(i) + if fid == root { + usedFids[fid] = true + } else { + freeFids = append(freeFids, fid) + usedFids[fid] = false + } + } + var m sync.Mutex + c := sync.NewCond(&m) + return &Client{conn, session, &m, c, usedFids, freeFids, root}, nil +} + +func (c *Client) Close(ctx context.Context) { + if err := c.session.Clunk(ctx, c.root); err != nil { + log.Println("Failed to Clunk root fid", err) + } else { + c.usedFids[c.root] = false + } + c.m.Lock() + defer c.m.Unlock() + for fid, inuse := range c.usedFids { + if inuse { + log.Println("I don't know how to flush: leaking", fid) + } + } + c.conn.Close() +} + +// allocFid returns a fresh fid, bound to a clone of from +func (c *Client) allocFid(ctx context.Context, from p9p.Fid) (p9p.Fid, error) { + c.m.Lock() + defer c.m.Unlock() + for len(c.freeFids) == 0 { + c.c.Wait() + } + fid := c.freeFids[len(c.freeFids)-1] + c.freeFids = c.freeFids[0 : len(c.freeFids)-1] + c.usedFids[fid] = true + _, err := c.session.Walk(ctx, from, fid) + if err != nil { + log.Println("Failed to clone root fid", err) + return badFid, err + } + return fid, nil +} + +// freeFid removes resources associated with the given fid +func (c *Client) freeFid(ctx context.Context, fid p9p.Fid) { + c.m.Lock() + defer c.m.Unlock() + c.freeFids = append(c.freeFids, fid) + c.usedFids[fid] = false + if err := c.session.Clunk(ctx, fid); err != nil { + log.Println("Failed to clunk fid", fid) + } + c.c.Signal() +} + +// Mkdir acts like 'mkdir -p' +func (c *Client) Mkdir(ctx context.Context, path ...string) error { + fid, err := c.allocFid(ctx, c.root) + if err != nil { + return nil + } + defer c.freeFid(ctx, fid) + // mkdir -p + for _, dir := range path { + dirfid, err := c.allocFid(ctx, fid) + if err != nil { + return err + } + // dir may or may not exist + _, _, _ = c.session.Create(ctx, dirfid, dir, dirperm, p9p.OREAD) + c.freeFid(ctx, dirfid) + // dir should definitely exist + if _, err := c.session.Walk(ctx, fid, fid, dir); err != nil { + log.Println("Failed to Walk to", dir, err) + return err + } + } + return nil +} + +var enoent = p9p.MessageRerror{Ename: "No such file or directory"} +var enotdir = p9p.MessageRerror{Ename: "Can't walk from a file"} + +// Remove acts like 'rm -f' +func (c *Client) Remove(ctx context.Context, path ...string) error { + fid, err := c.allocFid(ctx, c.root) + if err != nil { + return err + } + if _, err := c.session.Walk(ctx, fid, fid, path...); err != nil { + if err == enoent || err == enotdir { + c.freeFid(ctx, fid) + return nil + } + log.Println("Failed to walk to", path, err) + c.freeFid(ctx, fid) + return err + } + // Remove will cluck the fid, even if it fails + if err := c.session.Remove(ctx, fid); err != nil { + if err == enoent { + return nil + } + log.Println("Failed to Remove", path, err) + return err + } + return nil +} + +type File struct { + fid p9p.Fid + c *Client + m *sync.Mutex + open bool +} + +// Create creates a file +func (c *Client) Create(ctx context.Context, path ...string) (*File, error) { + fid, err := c.allocFid(ctx, c.root) + if err != nil { + return nil, err + } + dir := path[0 : len(path)-1] + _, err = c.session.Walk(ctx, fid, fid, dir...) + if err != nil { + if err != enoent { + // This is a common error + log.Println("Failed to Walk to", path, err) + } + c.freeFid(ctx, fid) + return nil, err + } + _, _, err = c.session.Create(ctx, fid, path[len(path)-1], fileperm, p9p.ORDWR) + if err != nil { + log.Println("Failed to Create", path, err) + return nil, err + } + var m sync.Mutex + return &File{fid: fid, c: c, m: &m, open: true}, nil +} + +// Open opens a file +func (c *Client) Open(ctx context.Context, mode p9p.Flag, path ...string) (*File, error) { + fid, err := c.allocFid(ctx, c.root) + if err != nil { + return nil, err + } + _, err = c.session.Walk(ctx, fid, fid, path...) + if err != nil { + if err != enoent { + // This is a common error + log.Println("Failed to Walk to", path, err) + } + c.freeFid(ctx, fid) + return nil, err + } + _, _, err = c.session.Open(ctx, fid, mode) + if err != nil { + log.Println("Failed to Open", path, err) + c.freeFid(ctx, fid) + return nil, err + } + var m sync.Mutex + return &File{fid: fid, c: c, m: &m, open: true}, nil +} + +// List a directory +func (c *Client) List(ctx context.Context, path []string) ([]string, error) { + file, err := c.Open(ctx, p9p.OREAD, path...) + if err != nil { + return nil, err + } + defer file.Close(ctx) + + msize, _ := c.session.Version() + iounit := uint32(msize - 24) // size of message max minus fcall io header (Rread) + + p := make([]byte, iounit) + + n, err := c.session.Read(ctx, file.fid, p, 0) + if err != nil { + return nil, err + } + + files := []string{} + + rd := bytes.NewReader(p[:n]) + codec := p9p.NewCodec() // TODO(stevvooe): Need way to resolve codec based on session. + for { + var d p9p.Dir + if err := p9p.DecodeDir(codec, rd, &d); err != nil { + if err == io.EOF { + break + } + return files, err + } + files = append(files, d.Name) + } + return files, nil +} + +// Close closes a file +func (f *File) Close(ctx context.Context) { + f.m.Lock() + defer f.m.Unlock() + if f.open { + f.c.freeFid(ctx, f.fid) + } + f.open = false +} + +// Read reads a value +func (f *File) Read(ctx context.Context, p []byte, offset int64) (int, error) { + f.m.Lock() + defer f.m.Unlock() + if !f.open { + return 0, io.EOF + } + return f.c.session.Read(ctx, f.fid, p, offset) +} + +// Write writes a value +func (f *File) Write(ctx context.Context, p []byte, offset int64) (int, error) { + f.m.Lock() + defer f.m.Unlock() + if !f.open { + return 0, io.EOF + } + return f.c.session.Write(ctx, f.fid, p, offset) +} + +type FileReader struct { + file *File + offset int64 + ctx context.Context +} + +func (f *File) NewFileReader(ctx context.Context) *FileReader { + offset := int64(0) + return &FileReader{file: f, offset: offset, ctx: ctx} +} + +func (f *FileReader) Read(p []byte) (int, error) { + n, err := f.file.Read(f.ctx, p, f.offset) + f.offset = f.offset + int64(n) + if n == 0 { + return 0, io.EOF + } + return n, err +} + +type ioFileReaderWriter struct { + f *File + ctx context.Context + offset int64 +} + +// NewIOReader creates a standard io.Reader at a given position in the file +func (f *File) NewIOReader(ctx context.Context, offset int64) io.Reader { + return &ioFileReaderWriter{f, ctx, offset} +} + +// NewIOWriter creates a standard io.Writer at a given position in the file +func (f *File) NewIOWriter(ctx context.Context, offset int64) io.Writer { + return &ioFileReaderWriter{f, ctx, offset} +} + +func (r *ioFileReaderWriter) Read(p []byte) (n int, err error) { + + r.f.m.Lock() + defer r.f.m.Unlock() + n, err = r.f.c.session.Read(r.ctx, r.f.fid, p, r.offset) + + r.offset += int64(n) + return n, err +} +func (w *ioFileReaderWriter) Write(p []byte) (n int, err error) { + w.f.m.Lock() + defer w.f.m.Unlock() + for err == nil || err == io.ErrShortWrite { + var written int + written, err = w.f.c.session.Write(w.ctx, w.f.fid, p, w.offset) + p = p[written:] + w.offset += int64(written) + n += written + if len(p) == 0 { + break + } + } + return +} diff --git a/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/config.go b/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/config.go new file mode 100644 index 000000000..7d209fef4 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/config.go @@ -0,0 +1,380 @@ +package datakit + +import ( + "fmt" + "log" + "strconv" + "strings" + + "context" +) + +type Version int + +var InitialVersion = Version(0) + +// Record is a typed view on top of a database branch +type Record struct { + client *Client + path []string // directory inside the store + version Version + schemaF *IntField + fields []*StringRefField // registered fields, for schema upgrades + lookupB []string // priority ordered list of branches to look up values in + defaultsB string // name of the branch containing built-in defaults + stateB string // name of the branch containing run-time state + event chan (interface{}) + onUpdate [](func([]*Snapshot, Version)) +} + +func NewRecord(ctx context.Context, client *Client, lookupB []string, defaultsB string, stateB string, path []string) (*Record, error) { + event := make(chan (interface{}), 0) + for _, b := range append(lookupB, stateB) { + // Create the branch if it doesn't exist + t, err := NewTransaction(ctx, client, b) + if err != nil { + log.Fatalf("Failed to open a new transaction: %#v", err) + } + if err = t.Write(ctx, []string{"branch-created"}, ""); err != nil { + log.Fatalf("Failed to write branch-created: %#v", err) + } + if err = t.Commit(ctx, "Creating branch"); err != nil { + log.Fatalf("Failed to commit transaction: %#v", err) + } + } + for _, b := range lookupB { + if err := client.Mkdir(ctx, "branch", b); err != nil { + return nil, err + } + w, err := NewWatch(ctx, client, b, path) + if err != nil { + return nil, err + } + go func() { + for { + _, err := w.Next(ctx) + if err != nil { + return + } + log.Printf("Snapshot has changed\n") + event <- 0 + } + }() + } + onUpdate := make([](func([]*Snapshot, Version)), 0) + fields := make([]*StringRefField, 0) + r := &Record{ + client: client, + path: path, + version: InitialVersion, + fields: fields, + lookupB: lookupB, + defaultsB: defaultsB, + stateB: stateB, + event: event, + onUpdate: onUpdate, + } + r.schemaF = r.IntField("schema-version", 1) + return r, nil +} + +func (r *Record) updateAll(ctx context.Context) error { + snapshots := make([]*Snapshot, 0) + for _, b := range r.lookupB { + head, err := Head(ctx, r.client, b) + if err != nil { + return err + } + snap := NewSnapshot(ctx, r.client, COMMIT, head) + snapshots = append(snapshots, snap) + } + for _, fn := range r.onUpdate { + fn(snapshots, r.version) + } + return nil +} + +func (r *Record) Seal(ctx context.Context) error { + return r.updateAll(ctx) +} + +func (r *Record) Wait(ctx context.Context) error { + <-r.event + r.version = r.version + 1 + return r.updateAll(ctx) +} + +func (r *Record) Upgrade(ctx context.Context, schemaVersion int) error { + currentVersion, _ := r.schemaF.Get() + if schemaVersion <= currentVersion { + log.Printf("No schema upgrade necessary because new version (%d) <= current version (%d)\n", schemaVersion, currentVersion) + return nil + } + r.schemaF.defaultInt = schemaVersion + defaultString := fmt.Sprintf("%d", schemaVersion) + r.schemaF.raw.defaultValue = &defaultString + // Create defaults branch + log.Printf("Performing schema upgrade to version %d\n", schemaVersion) + t, err := NewTransaction(ctx, r.client, r.defaultsB) + if err != nil { + return err + } + // For each known field, write default value to branch + for _, f := range r.fields { + p := append(r.path, f.path...) + if f.defaultValue == nil { + err = t.Remove(ctx, p) + } else { + err = t.Write(ctx, p, *f.defaultValue) + } + if err != nil { + return err + } + } + + // Merge to the defaults branch + err = t.Commit(ctx, fmt.Sprintf("Upgrade to schema version %d", schemaVersion)) + if err != nil { + return err + } + return r.Wait(ctx) +} + +// fillInDefault updates the default branch to contain the new value. +func (r *Record) fillInDefault(path []string, valueref *string) error { + ctx := context.Background() + t, err := NewTransaction(ctx, r.client, r.defaultsB) + if err != nil { + return err + } + p := append(r.path, path...) + if valueref == nil { + log.Printf("Removing default value at %s", strings.Join(p, "/")) + + if err = t.Remove(ctx, p); err != nil { + log.Printf("Failed to remove key at %s", strings.Join(p, "/")) + return err + } + } else { + log.Printf("Updating default value at %s to %s", strings.Join(p, "/"), *valueref) + if err = t.Write(ctx, p, *valueref); err != nil { + log.Printf("Failed to write key %s = %s", strings.Join(p, "/"), *valueref) + return err + } + } + return t.Commit(ctx, fmt.Sprintf("fill-in default for %s", p)) +} + +func (r *Record) SetMultiple(description string, fields []*StringField, values []string) error { + if len(fields) != len(values) { + return fmt.Errorf("Length of fields and values is not equal") + } + ctx := context.Background() + t, err := NewTransaction(ctx, r.client, r.lookupB[0]) + if err != nil { + return err + } + for i, k := range fields { + p := append(r.path, k.raw.path...) + v := values[i] + log.Printf("Setting value in store: %#v=%s\n", p, v) + err = t.Write(ctx, p, v) + if err != nil { + return err + } + } + return t.Commit(ctx, description) +} + +type StringRefField struct { + path []string + value *string + defaultValue *string + version Version // version of last change + record *Record +} + +// Set unconditionally sets the value of the key +func (f *StringRefField) Set(description string, value *string) error { + // TODO: maybe this should return Version, too? + ctx := context.Background() + p := append(f.record.path, f.path...) + log.Printf("Setting value in store: %#v=%#v\n", p, value) + t, err := NewTransaction(ctx, f.record.client, f.record.lookupB[0]) + if err != nil { + return err + } + if value == nil { + err = t.Remove(ctx, p) + } else { + err = t.Write(ctx, p, *value) + } + if err != nil { + return err + } + return t.Commit(ctx, fmt.Sprintf("Unconditionally set %s: %s", f.path, description)) +} + +// Get retrieves the current value of the key +func (f *StringRefField) Get() (*string, Version) { + if f.value == nil { + return nil, f.version + } + raw := strings.TrimSpace(*f.value) + return &raw, f.version +} + +// HasChanged returns true if the key has changed since the given version +func (f *StringRefField) HasChanged(version Version) bool { + return version < f.version +} + +// StringRefField defines a string option which can be nil with a specified +// key and default value +func (f *Record) StringRefField(key string, value *string) *StringRefField { + path := strings.Split(key, "/") + field := &StringRefField{path: path, value: value, defaultValue: value, version: InitialVersion, record: f} + // If the value is not in the database, write the default Value. + err := f.fillInDefault(path, value) + if err != nil { + log.Println("Failed to write default value", key, "=", value) + } + fn := func(snaps []*Snapshot, version Version) { + ctx := context.Background() + var newValue *string + for _, snap := range snaps { + v, err := snap.Read(ctx, append(f.path, path...)) + if err != nil { + if err != enoent { + log.Println("Failed to read key", key, "from directory snapshot", snap) + return + } + // if enoent then newValue == nil + } else { + newValue = &v + break + } + } + if (field.value == nil && newValue != nil) || (field.value != nil && newValue == nil) || (field.value != nil && newValue != nil && *field.value != *newValue) { + field.value = newValue + field.version = version + } + + // Update the value in memory and in the state branch + t, err := NewTransaction(ctx, f.client, f.stateB) + if err != nil { + log.Fatalf("Failed to create transaction for updating state branch: %#v", err) + } + if newValue != nil { + if err = t.Write(ctx, append(f.path, path...), *newValue); err != nil { + log.Fatalf("Failed to write state %#v = %s: %#v", path, *newValue, err) + } + } else { + if err = t.Remove(ctx, append(f.path, path...)); err != nil { + log.Fatalf("Failed to remove state %#v: %#v", path, err) + } + } + if err = t.Commit(ctx, "Updating state branch"); err != nil { + log.Fatalf("Failed to commit transaction: %#v", err) + } + + } + f.onUpdate = append(f.onUpdate, fn) + //fn(f.version) + f.fields = append(f.fields, field) + return field +} + +type StringField struct { + raw *StringRefField + defaultString string +} + +// Get retrieves the current value of the key +func (f *StringField) Get() (string, Version) { + if f.raw.value == nil { + log.Printf("Failed to find string in database at %s, defaulting to %s", strings.Join(f.raw.path, "/"), f.defaultString) + return f.defaultString, f.raw.version + } + return *f.raw.value, f.raw.version +} + +// Set unconditionally sets the value of the key +func (f *StringField) Set(description string, value string) error { + return f.raw.Set(description, &value) +} + +// HasChanged returns true if the key has changed since the given version +func (f *StringField) HasChanged(version Version) bool { + return version < f.raw.version +} + +// StringField defines a string +func (f *Record) StringField(key string, value string) *StringField { + raw := f.StringRefField(key, &value) + return &StringField{raw: raw, defaultString: value} +} + +type IntField struct { + raw *StringRefField + defaultInt int +} + +// Get retrieves the current value of the key +func (f *IntField) Get() (int, Version) { + if f.raw.value == nil { + log.Printf("Key %s missing in database, defaulting value to %t", strings.Join(f.raw.path, "/"), f.defaultInt) + return f.defaultInt, f.raw.version + } + value64, err := strconv.ParseInt(strings.TrimSpace(*f.raw.value), 10, 0) + if err != nil { + // revert to default if we can't parse the result + log.Printf("Failed to parse int in database: '%s', defaulting to %d", f.raw.value, f.defaultInt) + return f.defaultInt, f.raw.version + } + return int(value64), f.raw.version +} + +// HasChanged returns true if the key has changed since the given version +func (f *IntField) HasChanged(version Version) bool { + return version < f.raw.version +} + +// IntField defines an boolean option with a specified key and default value +func (f *Record) IntField(key string, value int) *IntField { + stringValue := fmt.Sprintf("%d", value) + raw := f.StringRefField(key, &stringValue) + return &IntField{raw: raw, defaultInt: value} +} + +type BoolField struct { + raw *StringRefField + defaultBool bool +} + +// Get retrieves the current value of the key +func (f *BoolField) Get() (bool, Version) { + if f.raw.value == nil { + log.Printf("Key %s missing in database, defaulting value to %t", strings.Join(f.raw.path, "/"), f.defaultBool) + return f.defaultBool, f.raw.version + } + value, err := strconv.ParseBool(strings.TrimSpace(*f.raw.value)) + if err != nil { + // revert to default if we can't parse the result + log.Printf("Failed to parse boolean in database: '%s', defaulting to %t", f.raw.value, f.defaultBool) + return f.defaultBool, f.raw.version + } + return value, f.raw.version +} + +// HasChanged returns true if the key has changed since the given version +func (f *BoolField) HasChanged(version Version) bool { + return version < f.raw.version +} + +// BoolField defines an boolean option with a specified key and default value +func (f *Record) BoolField(key string, value bool) *BoolField { + stringValue := fmt.Sprintf("%t", value) + raw := f.StringRefField(key, &stringValue) + return &BoolField{raw: raw, defaultBool: value} +} diff --git a/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/doc.go b/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/doc.go new file mode 100644 index 000000000..4acd418c7 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/doc.go @@ -0,0 +1,5 @@ +/* +The datakit package contains common patterns over 9P, which avoids the need +for applications to use 9P directly. +*/ +package datakit diff --git a/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/snapshot.go b/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/snapshot.go new file mode 100644 index 000000000..0ce1dd116 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/snapshot.go @@ -0,0 +1,87 @@ +package datakit + +import ( + "bytes" + "io" + "log" + "strings" + + p9p "github.com/docker/go-p9p" + "context" +) + +type SnapshotKind uint8 + +const ( + COMMIT SnapshotKind = 0x01 // from a commit hash + OBJECT SnapshotKind = 0x02 // from an object hash +) + +type snapshot struct { + client *Client + kind SnapshotKind + thing string +} + +type Snapshot struct { + snapshot +} + +// NewSnapshot opens a new snapshot referencing the given object. +func NewSnapshot(ctx context.Context, client *Client, kind SnapshotKind, thing string) *Snapshot { + return &Snapshot{snapshot{client: client, kind: kind, thing: thing}} +} + +// Head retrieves the commit sha of the given branch +func Head(ctx context.Context, client *Client, fromBranch string) (string, error) { + // SHA=$(cat branch//head) + file, err := client.Open(ctx, p9p.ORDWR, "branch", fromBranch, "head") + if err != nil { + log.Println("Failed to open branch/", fromBranch, "/head") + return "", err + } + defer file.Close(ctx) + buf := make([]byte, 512) + n, err := file.Read(ctx, buf, 0) + if err != nil { + log.Println("Failed to Read branch", fromBranch, "head", err) + return "", err + } + return strings.TrimSpace(string(buf[0:n])), nil +} + +func (s *Snapshot) getFullPath(path []string) []string { + var p []string + + switch s.kind { + case COMMIT: + p = []string{"snapshots", s.thing, "ro"} + case OBJECT: + p = []string{"trees", s.thing} + } + + for _, element := range path { + p = append(p, element) + } + return p +} + +// Read reads a value from the snapshot +func (s *Snapshot) Read(ctx context.Context, path []string) (string, error) { + p := s.getFullPath(path) + file, err := s.client.Open(ctx, p9p.OREAD, p...) + if err != nil { + return "", err + } + defer file.Close(ctx) + reader := file.NewIOReader(ctx, 0) + buf := bytes.NewBuffer(nil) + io.Copy(buf, reader) + return string(buf.Bytes()), nil +} + +// List returns filenames list in directory +func (s *Snapshot) List(ctx context.Context, path []string) ([]string, error) { + p := s.getFullPath(path) + return s.client.List(ctx, p) +} diff --git a/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/transaction.go b/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/transaction.go new file mode 100644 index 000000000..b55160306 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/transaction.go @@ -0,0 +1,136 @@ +package datakit + +import ( + "bytes" + "io" + "log" + "strconv" + "sync/atomic" + + p9p "github.com/docker/go-p9p" + "context" +) + +type transaction struct { + client *Client + fromBranch string + newBranch string +} + +var nextTransaction = int64(0) + +// NewTransaction opens a new transaction originating from fromBranch, named +// newBranch. +func NewTransaction(ctx context.Context, client *Client, fromBranch string) (*transaction, error) { + + id := atomic.AddInt64(&nextTransaction, 1) + newBranch := strconv.FormatInt(id, 10) + err := client.Mkdir(ctx, "branch", fromBranch) + if err != nil { + log.Println("Failed to Create branch/", fromBranch, err) + return nil, err + } + err = client.Mkdir(ctx, "branch", fromBranch, "transactions", newBranch) + if err != nil { + log.Println("Failed to Create branch/", fromBranch, "/transactions/", newBranch, err) + return nil, err + } + + return &transaction{client: client, fromBranch: fromBranch, newBranch: newBranch}, nil +} + +func (t *transaction) close(ctx context.Context) { + // TODO: do we need to clear up unmerged branches? +} + +// Abort ensures the update will not be committed. +func (t *transaction) Abort(ctx context.Context) { + t.close(ctx) +} + +// Commit merges the newBranch back into the fromBranch, or fails +func (t *transaction) Commit(ctx context.Context, msg string) error { + // msg + msgPath := []string{"branch", t.fromBranch, "transactions", t.newBranch, "msg"} + msgFile, err := t.client.Open(ctx, p9p.ORDWR, msgPath...) + if err != nil { + log.Println("Failed to Open msg", err) + return err + } + defer msgFile.Close(ctx) + _, err = msgFile.Write(ctx, []byte(msg), 0) + if err != nil { + log.Println("Failed to Write msg", err) + } + + // ctl + ctlPath := []string{"branch", t.fromBranch, "transactions", t.newBranch, "ctl"} + ctlFile, err := t.client.Open(ctx, p9p.ORDWR, ctlPath...) + if err != nil { + log.Println("Failed to Open ctl", err) + return err + } + defer ctlFile.Close(ctx) + _, err = ctlFile.Write(ctx, []byte("commit"), 0) + if err != nil { + log.Println("Failed to Write ctl", err) + return err + } + return nil +} + +// Write updates a key=value pair within the transaction. +func (t *transaction) Write(ctx context.Context, path []string, value string) error { + p := []string{"branch", t.fromBranch, "transactions", t.newBranch, "rw"} + for _, dir := range path[0 : len(path)-1] { + p = append(p, dir) + } + err := t.client.Mkdir(ctx, p...) + if err != nil { + log.Println("Failed to Mkdir", p) + } + p = append(p, path[len(path)-1]) + file, err := t.client.Create(ctx, p...) + if err != nil { + log.Println("Failed to Create", p) + return err + } + defer file.Close(ctx) + writer := file.NewIOWriter(ctx, 0) + _, err = writer.Write([]byte(value)) + if err != nil { + log.Println("Failed to Write", path, "=", value, ":", err) + return err + } + return nil +} + +// Read reads a key within the transaction. +func (t *transaction) Read(ctx context.Context, path []string) (string, error) { + p := []string{"branch", t.fromBranch, "transactions", t.newBranch, "rw"} + for _, dir := range path[0 : len(path)-1] { + p = append(p, dir) + } + file, err := t.client.Open(ctx, p9p.OREAD, p...) + if err != nil { + return "", err + } + defer file.Close(ctx) + reader := file.NewIOReader(ctx, 0) + buf := bytes.NewBuffer(nil) + io.Copy(buf, reader) + return string(buf.Bytes()), nil +} + +// Remove deletes a key within the transaction. +func (t *transaction) Remove(ctx context.Context, path []string) error { + p := []string{"branch", t.fromBranch, "transactions", t.newBranch, "rw"} + for _, dir := range path { + p = append(p, dir) + } + err := t.client.Remove(ctx, p...) + if err != nil { + log.Println("Failed to Remove ", p) + } + return nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/watch.go b/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/watch.go new file mode 100644 index 000000000..2557f1e86 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/watch.go @@ -0,0 +1,77 @@ +package datakit + +import ( + "io" + "log" + "strings" + + p9p "github.com/docker/go-p9p" + "context" +) + +type watch struct { + client *Client + file *File + offset int64 // current offset within head.live file +} + +type Watch struct { + watch +} + +// NewWatch starts watching a path within a branch +func NewWatch(ctx context.Context, client *Client, fromBranch string, path []string) (*Watch, error) { + // SHA=$(cat branch//watch//tree.live) + p := []string{"branch", fromBranch, "watch"} + for _, dir := range path { + p = append(p, dir+".node") + } + p = append(p, "tree.live") + file, err := client.Open(ctx, p9p.OREAD, p...) + if err != nil { + log.Println("Failed to open", p, err) + return nil, err + } + offset := int64(0) + return &Watch{watch{client: client, file: file, offset: offset}}, nil +} + +func (w *Watch) Next(ctx context.Context) (*Snapshot, error) { + buf := make([]byte, 512) + sawFlush := false + for { + // NOTE: irmin9p-direct will never return a fragment; + // we can rely on the buffer containing a whold number + // of lines. + n, err := w.file.Read(ctx, buf, w.offset) + if n == 0 { + // Two reads of "" in a row means end-of-file + if sawFlush { + return nil, io.EOF + } else { + sawFlush = true + continue + } + } else { + sawFlush = false + } + w.offset = w.offset + int64(n) + if err != nil { + log.Println("Failed to Read head.live", err) + return nil, io.EOF + } + lines := strings.Split(string(buf[0:n]), "\n") + // Use the last non-empty line + thing := "" + for _, line := range lines { + if line != "" { + thing = line + } + } + return NewSnapshot(ctx, w.client, OBJECT, thing), nil + } +} + +func (w *Watch) Close(ctx context.Context) { + w.file.Close(ctx) +} diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/LICENSE b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/LICENSE new file mode 100644 index 000000000..8f3fee627 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/LICENSE @@ -0,0 +1,191 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2013-2016 Docker, Inc. + + 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 + + https://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. diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/README.md b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/README.md new file mode 100644 index 000000000..adb9948f4 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/README.md @@ -0,0 +1,78 @@ +VPN-friendly networking devices for [HyperKit](https://github.com/moby/hyperkit) +=============================== + +[![Build Status (OSX)](https://circleci.com/gh/moby/vpnkit.png)](https://circleci.com/gh/moby/vpnkit) + +Binary artefacts are built by CI: + +- [MacOS](https://circleci.com/gh/moby/vpnkit) +- [Windows](https://ci.appveyor.com/project/moby/vpnkit/history) + +![VPNKit diagram](http://moby.github.io/vpnkit/vpnkit.png) + +VPNKit is a set of tools and services for helping [HyperKit](https://github.com/moby/hyperkit) +VMs interoperate with host VPN configurations. + + +Building on Unix +---------------- + +First install `wget`, `opam` using your package manager of choice. + +Build all the dependencies and the program itself with: + +``` +cd [path to vpnkit source] +opam remote add vpnkit ./repo/darwin +opam install --deps-only vpnkit +make +``` + +When the build succeeds the `vpnkit` binary should be available in the current path. + +Running with hyperkit +--------------------- + +First ask `vpnkit` to listen for ethernet connections on a local Unix domain socket: +``` +vpnkit --ethernet /tmp/ethernet --debug +``` +Next ask [com.docker.hyperkit](https://github.com/moby/hyperkit) to connect a NIC to this +socket by adding a command-line option like `-s 2:0,virtio-vpnkit,path=/tmp/ethernet`. Note: +you may need to change the slot `2:0` to a free slot in your VM configuration. + +Why is this needed? +------------------- + +Running a VM usually involves modifying the network configuration on the host, for example +by activating Ethernet bridges, new routing table entries, DNS and firewall/NAT configurations. +Activating a VPN involves modifying the same routing tables, DNS and firewall/NAT configurations +and therefore there can be a clash -- this often results in the network connection to the VM +being disconnected. + +VPNKit, part of [HyperKit](https://github.com/moby/hyperkit) +attempts to work nicely with VPN software by intercepting the VM traffic at the Ethernet level, +parsing and understanding protocols like NTP, DNS, UDP, TCP and doing the "right thing" with +respect to the host's VPN configuration. + +VPNKit operates by reconstructing Ethernet traffic from the VM and translating it into the +relevant socket API calls on OSX or Windows. This allows the host application to generate +traffic without requiring low-level Ethernet bridging support. + +Design +------ + +- [Using vpnkit as a default gateway](docs/ethernet.md): describes the flow of ethernet traffic to/from the VM +- [Port forwarding](docs/ports.md): describes how ports are forwarded from the host into the VM +- [Experimental transparent HTTP proxy](docs/transparent-http-proxy.md): describes the + experimental support for transparent HTTP(S) proxying + +Licensing +--------- + +VPNKit is licensed under the Apache License, Version 2.0. See +[LICENSE](https://github.com/moby/vpnkit/blob/master/LICENSE.md) for the full +license text. + +Contributions are welcome under the terms of this license. You may wish to browse +the [weekly reports](reports) to read about overall activity in the repository. diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/9pmount-vsock.c b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/9pmount-vsock.c new file mode 100644 index 000000000..4ecebb24a --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/9pmount-vsock.c @@ -0,0 +1,248 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hvsock.h" + +#define NONE 0 +#define LISTEN 1 +#define CONNECT 2 + +int mode = NONE; + +char *default_sid = "C378280D-DA14-42C8-A24E-0DE92A1028E2"; +char *mount = "/bin/mount"; + +void fatal(const char *msg) +{ + syslog(LOG_CRIT, "%s Error: %d. %s", msg, errno, strerror(errno)); + exit(1); +} + +static int handle(int fd, char *tag, char *path) +{ + char *options = NULL; + int status; + pid_t pid; + int res; + + res = asprintf(&options, + "trans=fd,dfltuid=1001,dfltgid=50,version=9p2000,msize=4096,rfdno=%d,wfdno=%d", + fd, fd); + if (res < 0) + fatal("asprintf()"); + + char *argv[] = { + mount, + "-t", "9p", "-o", options, + tag, path, + NULL + }; + + pid = fork(); + if (pid == 0) { + execv(mount, argv); + fatal("execv()"); + } + + res = waitpid(pid, &status, 0); + if (res == -1) { + syslog(LOG_CRIT, + "waitpid failed: %d. %s", errno, strerror(errno)); + exit(1); + } + return WEXITSTATUS(status); +} + +static int create_listening_socket(GUID serviceid) +{ + SOCKADDR_HV sa; + int lsock; + int res; + + lsock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW); + if (lsock == -1) + fatal("socket()"); + + sa.Family = AF_HYPERV; + sa.Reserved = 0; + sa.VmId = HV_GUID_WILDCARD; + sa.ServiceId = serviceid; + + res = bind(lsock, (const struct sockaddr *)&sa, sizeof(sa)); + if (res == -1) + fatal("bind()"); + + res = listen(lsock, 1); + if (res == -1) + fatal("listen()"); + + return lsock; +} + +static int connect_socket(GUID serviceid) +{ + SOCKADDR_HV sa; + int sock; + int res; + + sock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW); + if (sock == -1) + fatal("socket()"); + + sa.Family = AF_HYPERV; + sa.Reserved = 0; + sa.VmId = HV_GUID_PARENT; + sa.ServiceId = serviceid; + + res = connect(sock, (const struct sockaddr *)&sa, sizeof(sa)); + if (res == -1) + fatal("connect()"); + + return sock; +} + +static int accept_socket(int lsock) +{ + SOCKADDR_HV sac; + socklen_t socklen = sizeof(sac); + int csock; + + csock = accept(lsock, (struct sockaddr *)&sac, &socklen); + if (csock == -1) + fatal("accept()"); + + syslog(LOG_INFO, "Connect from: " GUID_FMT ":" GUID_FMT "\n", + GUID_ARGS(sac.VmId), GUID_ARGS(sac.ServiceId)); + + return csock; +} + +void usage(char *name) +{ + printf("%s: mount a 9P filesystem from an hvsock connection\n", name); + printf("usage:\n"); + printf("\t[--serviceid ] \n"); + printf("where\n"); + printf("\t--serviceid : use as the well-known service GUID\n"); + printf("\t (defaults to %s)\n", default_sid); + printf("\t--listen: listen forever for incoming AF_HVSOCK connections\n"); + printf("\t--connect: connect to the parent partition\n"); +} + +int main(int argc, char **argv) +{ + int res = 0; + GUID sid; + int c; + /* Defaults to a testing GUID */ + char *serviceid = default_sid; + char *tag = NULL; + char *path = NULL; + + opterr = 0; + while (1) { + static struct option long_options[] = { + /* These options set a flag. */ + {"serviceid", required_argument, NULL, 's'}, + {0, 0, 0, 0} + }; + int option_index = 0; + + c = getopt_long(argc, argv, "s:", long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 's': + serviceid = optarg; + break; + case 0: + break; + default: + usage(argv[0]); + exit(1); + } + } + + if (optind < argc) { + if (strcmp(argv[optind], "listen") == 0) + mode = LISTEN; + else if (strcmp(argv[optind], "connect") == 0) + mode = CONNECT; + optind++; + } + if (mode == NONE) { + fprintf(stderr, "Please supply either listen or connect\n"); + usage(argv[0]); + exit(1); + } + + if (optind < argc) + tag = argv[optind++]; + + if (optind < argc) + path = argv[optind++]; + + if (!tag) { + fprintf(stderr, "Please supply a tag name\n"); + usage(argv[0]); + exit(1); + } + + if (!path) { + fprintf(stderr, "Please supply a path\n"); + usage(argv[0]); + exit(1); + } + + res = parseguid(serviceid, &sid); + if (res) { + fprintf(stderr, + "Failed to parse serviceid as GUID: %s\n", serviceid); + usage(argv[0]); + exit(1); + } + + openlog(argv[0], LOG_CONS | LOG_NDELAY | LOG_PERROR, LOG_DAEMON); + for (;;) { + int lsocket; + int sock; + int r; + + if (mode == LISTEN) { + syslog(LOG_INFO, "starting in listening mode with serviceid=%s, tag=%s, path=%s", serviceid, tag, path); + lsocket = create_listening_socket(sid); + sock = accept_socket(lsocket); + close(lsocket); + } else { + syslog(LOG_INFO, "starting in connect mode with serviceid=%s, tag=%s, path=%s", serviceid, tag, path); + sock = connect_socket(sid); + } + + r = handle(sock, tag, path); + close(sock); + + if (r == 0) { + syslog(LOG_INFO, "mount successful for serviceid=%s tag=%s path=%s", serviceid, tag, path); + exit(0); + } + + /* + * This can happen if the client times out the connection + * after we accept it + */ + syslog(LOG_CRIT, "mount failed with %d for serviceid=%s tag=%s path=%s", r, serviceid, tag, path); + sleep(1); /* retry */ + } +} diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/hvsock.c b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/hvsock.c new file mode 100644 index 000000000..94e23a292 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/hvsock.c @@ -0,0 +1,37 @@ +#include + +#include "hvsock.h" + +int parseguid(const char *s, GUID *g) +{ + int res; + int p0, p1, p2, p3, p4, p5, p6, p7; + + res = sscanf(s, GUID_FMT, + &g->Data1, &g->Data2, &g->Data3, + &p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7); + if (res != 11) + return 1; + g->Data4[0] = p0; + g->Data4[1] = p1; + g->Data4[2] = p2; + g->Data4[3] = p3; + g->Data4[4] = p4; + g->Data4[5] = p5; + g->Data4[6] = p6; + g->Data4[7] = p7; + return 0; +} + +DEFINE_GUID(HV_GUID_ZERO, + 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); +DEFINE_GUID(HV_GUID_BROADCAST, + 0xFFFFFFFF, 0xFFFF, 0xFFFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); +DEFINE_GUID(HV_GUID_WILDCARD, + 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); +DEFINE_GUID(HV_GUID_CHILDREN, + 0x90db8b89, 0x0d35, 0x4f79, 0x8c, 0xe9, 0x49, 0xea, 0x0a, 0xc8, 0xb7, 0xcd); +DEFINE_GUID(HV_GUID_LOOPBACK, + 0xe0e16197, 0xdd56, 0x4a10, 0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38); +DEFINE_GUID(HV_GUID_PARENT, + 0xa42e7cda, 0xd03f, 0x480c, 0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78); diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/hvsock.h b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/hvsock.h new file mode 100644 index 000000000..1f532c2eb --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/hvsock.h @@ -0,0 +1,48 @@ +/* AF_HYPERV definitions and utilities */ + +#include +#include +#include +#include + +/* GUID handling */ +typedef struct _GUID { + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; +} GUID; + +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + const GUID name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} + +/* Helper macros for parsing/printing GUIDs */ +#define GUID_FMT "%08x-%04hx-%04hx-%02x%02x-%02x%02x%02x%02x%02x%02x" +#define GUID_ARGS(_g) \ + (_g).Data1, (_g).Data2, (_g).Data3, \ + (_g).Data4[0], (_g).Data4[1], (_g).Data4[2], (_g).Data4[3], \ + (_g).Data4[4], (_g).Data4[5], (_g).Data4[6], (_g).Data4[7] +#define GUID_SARGS(_g) \ + &(_g).Data1, &(_g).Data2, &(_g).Data3, \ + &(_g).Data4[0], &(_g).Data4[1], &(_g).Data4[2], &(_g).Data4[3], \ + &(_g).Data4[4], &(_g).Data4[5], &(_g).Data4[6], &(_g).Data4[7] + +extern int parseguid(const char *s, GUID *g); + +/* HV Socket definitions */ +#define AF_HYPERV 43 +#define HV_PROTOCOL_RAW 1 + +typedef struct _SOCKADDR_HV { + unsigned short Family; + unsigned short Reserved; + GUID VmId; + GUID ServiceId; +} SOCKADDR_HV; + +extern const GUID HV_GUID_ZERO; +extern const GUID HV_GUID_BROADCAST; +extern const GUID HV_GUID_WILDCARD; +extern const GUID HV_GUID_CHILDREN; +extern const GUID HV_GUID_LOOPBACK; +extern const GUID HV_GUID_PARENT; diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/hvsock.c b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/hvsock.c new file mode 100644 index 000000000..59251bab2 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/hvsock.c @@ -0,0 +1,40 @@ +#include + +#include "hvsock.h" + +int parseguid(const char *s, GUID *g) +{ + int res; + int p0, p1, p2, p3, p4, p5, p6, p7; + + res = sscanf(s, GUID_FMT, + &g->Data1, &g->Data2, &g->Data3, + &p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7); + if (res != 11) + return 1; + + g->Data4[0] = p0; + g->Data4[1] = p1; + g->Data4[2] = p2; + g->Data4[3] = p3; + g->Data4[4] = p4; + g->Data4[5] = p5; + g->Data4[6] = p6; + g->Data4[7] = p7; + + return 0; +} + +DEFINE_GUID(HV_GUID_ZERO, + 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); +DEFINE_GUID(HV_GUID_BROADCAST, + 0xFFFFFFFF, 0xFFFF, 0xFFFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); +DEFINE_GUID(HV_GUID_WILDCARD, +0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + +DEFINE_GUID(HV_GUID_CHILDREN, + 0x90db8b89, 0x0d35, 0x4f79, 0x8c, 0xe9, 0x49, 0xea, 0x0a, 0xc8, 0xb7, 0xcd); +DEFINE_GUID(HV_GUID_LOOPBACK, + 0xe0e16197, 0xdd56, 0x4a10, 0x91, 0x95, 0x5e, 0xe7, 0xa1, 0x55, 0xa8, 0x38); +DEFINE_GUID(HV_GUID_PARENT, + 0xa42e7cda, 0xd03f, 0x480c, 0x9c, 0xc2, 0xa4, 0xde, 0x20, 0xab, 0xb8, 0x78); diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/hvsock.h b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/hvsock.h new file mode 100644 index 000000000..bf680a7a2 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/hvsock.h @@ -0,0 +1,48 @@ +/* AF_HYPERV definitions and utilities */ + +#include +#include +#include +#include + +/* GUID handling */ +typedef struct _GUID { + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; +} GUID; + +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + const GUID name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} + +/* Helper macros for parsing/printing GUIDs */ +#define GUID_FMT "%08x-%04hx-%04hx-%02x%02x-%02x%02x%02x%02x%02x%02x" +#define GUID_ARGS(_g) \ + (_g).Data1, (_g).Data2, (_g).Data3, \ + (_g).Data4[0], (_g).Data4[1], (_g).Data4[2], (_g).Data4[3], \ + (_g).Data4[4], (_g).Data4[5], (_g).Data4[6], (_g).Data4[7] +#define GUID_SARGS(_g) \ + &(_g).Data1, &(_g).Data2, &(_g).Data3, \ + &(_g).Data4[0], &(_g).Data4[1], &(_g).Data4[2], &(_g).Data4[3], \ + &(_g).Data4[4], &(_g).Data4[5], &(_g).Data4[6], &(_g).Data4[7] + +extern int parseguid(const char *s, GUID *g); + +/* HV Socket definitions */ +#define AF_HYPERV 43 +#define HV_PROTOCOL_RAW 1 + +typedef struct _SOCKADDR_HV { + unsigned short Family; + unsigned short Reserved; + GUID VmId; + GUID ServiceId; +} SOCKADDR_HV; + +extern const GUID HV_GUID_ZERO; +extern const GUID HV_GUID_BROADCAST; +extern const GUID HV_GUID_WILDCARD; +extern const GUID HV_GUID_CHILDREN; +extern const GUID HV_GUID_LOOPBACK; +extern const GUID HV_GUID_PARENT; diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/protocol.c b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/protocol.c new file mode 100644 index 000000000..559a4d522 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/protocol.c @@ -0,0 +1,225 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "protocol.h" + +/* Version 0 of the protocol used this */ +char expected_hello_old[5] = {'V', 'M', 'N', 'E', 'T'}; + +/* Version 1 and later of the protocol used this */ +char expected_hello[5] = {'V', 'M', 'N', '3', 'T'}; + +int really_read(int fd, uint8_t *buffer, size_t total) +{ + size_t remaining = total; + ssize_t n; + + while (remaining > 0) { + n = read(fd, buffer, remaining); + if (n == 0) { + syslog(LOG_CRIT, "EOF reading from socket: closing\n"); + goto err; + } + if (n < 0) { + syslog(LOG_CRIT, + "Failure reading from socket: closing: %s", + strerror(errno)); + goto err; + } + remaining -= (size_t) n; + buffer = buffer + n; + } + return 0; +err: + /* + * On error: stop reading from the socket and trigger a clean + * shutdown + */ + shutdown(fd, SHUT_RD); + return -1; +} + +int really_write(int fd, uint8_t *buffer, size_t total) +{ + size_t remaining = total; + ssize_t n; + + while (remaining > 0) { + n = write(fd, buffer, remaining); + if (n == 0) { + syslog(LOG_CRIT, "EOF writing to socket: closing"); + goto err; + } + if (n < 0) { + syslog(LOG_CRIT, + "Failure writing to socket: closing: %s", + strerror(errno)); + goto err; + } + remaining -= (size_t) n; + buffer = buffer + n; + } + return 0; +err: + /* On error: stop listening to the socket */ + shutdown(fd, SHUT_WR); + return -1; +} + +struct init_message *create_init_message() +{ + struct init_message *m; + + m = malloc(sizeof(struct init_message)); + if (!m) + return NULL; + + bzero(m, sizeof(struct init_message)); + memcpy(&m->hello[0], &expected_hello[0], sizeof(m->hello)); + m->version = CURRENT_VERSION; + memset(&m->commit[0], 0, sizeof(m->commit)); + + return m; +} + +char *print_init_message(struct init_message *m) +{ + char tmp[41]; + + memcpy(&tmp[0], &m->commit[0], 40); + tmp[40] = '\000'; + char *buffer; + int n; + + buffer = malloc(80); + if (!buffer) + return NULL; + + n = snprintf(buffer, 80, "version %d, commit %s", m->version, tmp); + if (n < 0) { + perror("Failed to format init_message"); + exit(1); + } + return buffer; +} + +int read_init_message(int fd, struct init_message *ci) +{ + int res; + + bzero(ci, sizeof(struct init_message)); + + res = really_read(fd, (uint8_t *)&ci->hello[0], sizeof(ci->hello)); + if (res == -1) { + syslog(LOG_CRIT, "Failed to read hello from client"); + return -1; + } + + res = memcmp(&ci->hello[0], + &expected_hello_old[0], sizeof(expected_hello_old)); + if (res == 0) { + ci->version = 0; + return 0; + } + + res = memcmp(&ci->hello[0], + &expected_hello[0], sizeof(expected_hello)); + if (res != 0) { + syslog(LOG_CRIT, "Failed to read header magic from client"); + return -1; + } + + res = really_read(fd, (uint8_t *)&ci->version, sizeof(ci->version)); + if (res == -1) { + syslog(LOG_CRIT, "Failed to read header version from client"); + return -1; + } + + res = really_read(fd, (uint8_t *)&ci->commit[0], sizeof(ci->commit)); + if (res == -1) { + syslog(LOG_CRIT, "Failed to read header hash from client"); + return -1; + } + + return 0; +} + +int write_init_message(int fd, struct init_message *ci) +{ + int res; + + res = really_write(fd, (uint8_t *)&ci->hello[0], sizeof(ci->hello)); + if (res == -1) { + syslog(LOG_CRIT, "Failed to write hello to client"); + return -1; + } + if (ci->version > 0) { + res = really_write(fd, (uint8_t *)&ci->version, + sizeof(ci->version)); + if (res == -1) { + syslog(LOG_CRIT, "Failed to write version to client"); + return -1; + } + res = really_write(fd, (uint8_t *)&ci->commit[0], + sizeof(ci->commit)); + if (res == -1) { + syslog(LOG_CRIT, + "Failed to write header hash to client"); + return -1; + } + } + return 0; +} + +int read_vif_response(int fd, struct vif_info *vif) +{ + struct msg_response msg; + + if (really_read(fd, (uint8_t*)&msg, sizeof(msg)) == -1) { + syslog(LOG_CRIT, "Client failed to read server response"); + return -1; + } + + switch (msg.response_type) { + case rt_vif: + memcpy((uint8_t*)vif, (uint8_t*)&msg.vif, sizeof(*vif)); + return 0; + case rt_disconnect: + syslog(LOG_CRIT, "Server disconnected: %*s", msg.disconnect.len, msg.disconnect.msg); + return -1; + default: + syslog(LOG_CRIT, "Unknown response type from server"); + return -1; + } + +} + +int write_command(int fd, enum command *c) +{ + uint8_t command = *c; + + if (really_write(fd, (uint8_t *)&command, sizeof(command)) == -1) { + syslog(LOG_CRIT, "Failed to write command to client"); + return -1; + } + return 0; +} + +int write_ethernet_args(int fd, struct ethernet_args *args) +{ + uint8_t buffer[40]; + memset(&buffer[0], 0, sizeof(buffer)); + memcpy(&buffer[0], (uint8_t *)&args->uuid_string[0], 36); + + if (really_write(fd, (uint8_t *)&buffer, sizeof(buffer)) == -1) { + syslog(LOG_CRIT, "Failed to write ethernet args to client"); + return -1; + } + return 0; +} diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/protocol.h b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/protocol.h new file mode 100644 index 000000000..d0eb304ba --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/protocol.h @@ -0,0 +1,77 @@ +#ifndef _VMNET_PROTOCOL_H_ +#define _VMNET_PROTOCOL_H_ + +#include +#include + +/* Client -> Server init_message */ +/* Server -> Client init_message */ +struct init_message { + char hello[5]; + uint8_t _padding[3]; + uint32_t version; + char commit[40]; /* git sha of the compiled commit */ +}; + +/* + * This should be bumped whenever we add something (like a feature or a + * bugfix) and we wish the UI to be able to detect when to trigger a + * reinstall. + */ +#define CURRENT_VERSION 22 + +extern struct init_message *create_init_message(void); +extern int read_init_message(int fd, struct init_message *ci); +extern int write_init_message(int fd, struct init_message *ci); +extern char *print_init_message(struct init_message *m); + +/* Client -> Server command */ +enum command { + ethernet = 1, +}; + +/* Server -> Client response */ +enum response_type { + rt_vif = 1, + rt_disconnect = 2, +}; + +extern int write_command(int fd, enum command *c); + +/* Client -> Server command arguments */ +struct ethernet_args { + char uuid_string[36]; +}; + +extern int write_ethernet_args(int fd, struct ethernet_args *args); + +/* Server -> Client: details of a vif */ +struct vif_info { + uint16_t mtu; + uint16_t max_packet_size; + uint8_t mac[6]; +} __attribute__((packed)); + +/* Server -> Client: disconnect w/reason */ +struct disconnect_reason { + uint8_t len; + char msg[256]; +} __attribute__((packed)); + +struct msg_response { + uint8_t response_type; + union { + struct vif_info vif; + struct disconnect_reason disconnect; + }; +} __attribute__((packed)); + +extern int read_vif_response(int fd, struct vif_info *vif); + +extern char expected_hello[5]; +extern char expected_hello_old[5]; + +extern int really_read(int fd, uint8_t *buffer, size_t total); +extern int really_write(int fd, uint8_t *buffer, size_t total); + +#endif /* _VMNET_PROTOCOL_H_ */ diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/ring.c b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/ring.c new file mode 100644 index 000000000..fe7791d58 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/ring.c @@ -0,0 +1,269 @@ +#include +#include +#include +#include +#include +#include +#include +#include "ring.h" + +extern void fatal(const char *msg); + + +/* A fixed-size circular buffer. + + The producer and consumer are positive integers from 0 to 2 * size-1. + Adds are modulo 2 * size. This effectively uses one bit to distinguish + the case where the buffer is empty (consumer == producer) from the case + where the buffer is full (consumer + size == producer). */ +struct ring { + int producer; /* Next sequence number to be written */ + int consumer; /* Next sequence number to be read */ + int last; /* Sequence number of end of stream or -1 */ + int size; /* Maximum number of buffered bytes */ + pthread_cond_t c; + pthread_mutex_t m; + char *data; +}; + +struct ring *ring_allocate(int size) +{ + struct ring *ring = (struct ring*)malloc(sizeof(struct ring)); + if (!ring) { + fatal("Failed to allocate ring buffer metadata"); + } + ring->data = (char*)malloc(size); + if (!ring->data) { + fatal("Failed to allocate ring buffer data"); + } + int err = 0; + if ((err = pthread_cond_init(&ring->c, NULL)) != 0) { + errno = err; + fatal("Failed to create condition variable"); + } + if ((err = pthread_mutex_init(&ring->m, NULL)) != 0) { + errno = err; + fatal("Failed to create mutex"); + } + ring->size = size; + ring->producer = ring->consumer = 0; + ring->last = -1; + return ring; +} + +#define RING_DATA_AVAILABLE(r) \ + ((r->producer >= r->consumer) ? \ + (r->producer - r->consumer) : \ + (2 * r->size + r->producer - r->consumer)) +#define RING_FREE_REQUESTS(r) (r->size - RING_DATA_AVAILABLE(r)) + +#define RING_GET(r, seq) (&(r->data[seq % r->size])) + +/* Signal that new data is been produced */ +void ring_producer_advance(struct ring *ring, int n) +{ + int err = 0; + assert(n >= 0); + if ((err = pthread_mutex_lock(&ring->m)) != 0) { + errno = err; + fatal("Failed to lock mutex"); + } + ring->producer = (ring->producer + n) % (2 * ring->size); + if ((err = pthread_cond_broadcast(&ring->c)) != 0) { + errno = err; + fatal("Failed to signal condition variable"); + } + if ((err = pthread_mutex_unlock(&ring->m)) != 0) { + errno = err; + fatal("Failed to unlock mutex"); + } + return; +} + +/* Signal that data has been consumed */ +void ring_consumer_advance(struct ring *ring, int n) +{ + int err = 0; + assert(n >= 0); + if ((err = pthread_mutex_lock(&ring->m)) != 0) { + errno = err; + fatal("Failed to lock mutex"); + } + ring->consumer = (ring->consumer + n) % (2 * ring->size); + + if ((err = pthread_cond_broadcast(&ring->c)) != 0) { + errno = err; + fatal("Failed to signal condition variable"); + } + if ((err = pthread_mutex_unlock(&ring->m)) != 0) { + errno = err; + fatal("Failed to unlock mutex"); + } + return; +} + +/* The producer sends Eof */ +void ring_producer_eof(struct ring *ring) +{ + int err = 0; + if ((err = pthread_mutex_lock(&ring->m)) != 0) { + errno = err; + fatal("Failed to lock mutex"); + } + ring->last = ring->producer - 1; + if ((err = pthread_cond_broadcast(&ring->c)) != 0) { + errno = err; + fatal("Failed to signal condition variable"); + } + if ((err = pthread_mutex_unlock(&ring->m)) != 0) { + errno = err; + fatal("Failed to unlock mutex"); + } + return; +} + +/* Wait for n bytes to become available. If the ring has shutdown, return + non-zero. If data is available then return zero and fill in the first + iovec_len entries of the iovec. */ +int ring_producer_wait_available( + struct ring *ring, size_t n, struct iovec *iovec, int *iovec_len +) { + int ret = 1; + int err = 0; + if ((err = pthread_mutex_lock(&ring->m)) != 0) { + errno = err; + fatal("Failed to lock mutex"); + } + while ((RING_FREE_REQUESTS(ring) < n) && (ring->last == -1)) { + if ((err = pthread_cond_wait(&ring->c, &ring->m)) != 0) { + errno = err; + fatal("Failed to wait on condition variable"); + } + } + if (ring->last != -1) { + goto out; + } + char *producer = RING_GET(ring, ring->producer); + char *consumer = RING_GET(ring, ring->consumer); + assert (producer >= RING_GET(ring, 0)); + assert (producer <= RING_GET(ring, ring->size-1)); + assert (consumer >= RING_GET(ring, 0)); + assert (consumer <= RING_GET(ring, ring->size-1)); + if (*iovec_len <= 0) { + ret = 0; + fprintf(stderr, "no iovecs\n"); + goto out; + } + if (consumer > producer) { + /* producer has not wrapped around the buffer yet */ + iovec[0].iov_base = producer; + iovec[0].iov_len = consumer - producer; + assert(iovec[0].iov_len > 0); + *iovec_len = 1; + ret = 0; + goto out; + } + /* consumer has wrapped around, so the first chunk is from the producer to + the end of the buffer */ + iovec[0].iov_base = producer; + iovec[0].iov_len = ring->size - (int) (producer - RING_GET(ring, 0)); + assert(iovec[0].iov_len > 0); + if (*iovec_len == 1) { + ret = 0; + goto out; + } + *iovec_len = 1; + /* also include the chunk from the beginning of the buffer to the consumer */ + iovec[1].iov_base = RING_GET(ring, 0); + iovec[1].iov_len = consumer - RING_GET(ring, 0); + if (iovec[1].iov_len > 0) { + /* ... but don't bother if it's zero */ + *iovec_len = 2; + } + ret = 0; +out: + if ((err = pthread_mutex_unlock(&ring->m)) != 0) { + errno = err; + fatal("Failed to unlock mutex"); + } + if (ret == 0) { + for (int i = 0; i < *iovec_len; i++) { + assert(iovec[i].iov_base >= (void*)RING_GET(ring, 0)); + assert(iovec[i].iov_base + iovec[i].iov_len - 1 <= (void*)RING_GET(ring, ring->size - 1)); + } + } + return ret; +} + +/* Wait for n bytes to become available. If the ring has shutdown, return + non-zero. If data is available then return zero and fill in the first + iovec_len entries of the iovec. */ +int ring_consumer_wait_available( + struct ring *ring, size_t n, struct iovec *iovec, int *iovec_len +) { + + int ret = 1; + int err = 0; + if ((err = pthread_mutex_lock(&ring->m)) != 0) { + errno = err; + fatal("Failed to lock mutex"); + } + while ((RING_DATA_AVAILABLE(ring) < n) && (ring->last == -1)) { + if ((err = pthread_cond_wait(&ring->c, &ring->m)) != 0) { + errno = err; + fatal("Failed to wait on condition variable"); + } + } + if (ring->last != -1) { + goto out; + } + char *producer = RING_GET(ring, ring->producer); + char *consumer = RING_GET(ring, ring->consumer); + assert (producer >= RING_GET(ring, 0)); + assert (producer <= RING_GET(ring, ring->size-1)); + assert (consumer >= RING_GET(ring, 0)); + assert (consumer <= RING_GET(ring, ring->size-1)); + if (*iovec_len <= 0) { + ret = 0; + goto out; + } + if (producer > consumer) { + /* producer has not wrapped around the buffer yet */ + iovec[0].iov_base = consumer; + iovec[0].iov_len = producer - consumer; + assert(iovec[0].iov_len > 0); + *iovec_len = 1; + ret = 0; + goto out; + } + /* producer has wrapped around, so the first chunk is from the consumer to + the end of the buffer */ + iovec[0].iov_base = consumer; + iovec[0].iov_len = ring->size - (int) (consumer - RING_GET(ring, 0)); + assert(iovec[0].iov_len > 0); + if (*iovec_len == 1) { + ret = 0; + goto out; + } + *iovec_len = 1; + /* also include the chunk from the beginning of the buffer to the producer */ + iovec[1].iov_base = RING_GET(ring, 0); + iovec[1].iov_len = producer - RING_GET(ring, 0); + if (iovec[1].iov_len > 0) { + /* ... but don't bother if its zero */ + *iovec_len = 2; + } + ret = 0; +out: + if ((err = pthread_mutex_unlock(&ring->m)) != 0) { + errno = err; + fatal("Failed to unlock mutex"); + } + if (ret == 0) { + for (int i = 0; i < *iovec_len; i++) { + assert(iovec[i].iov_base >= (void*)RING_GET(ring, 0)); + assert(iovec[i].iov_base + iovec[i].iov_len - 1 <= (void*)RING_GET(ring, ring->size - 1)); + } + } + return ret; +} diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/ring.h b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/ring.h new file mode 100644 index 000000000..9d126793b --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/ring.h @@ -0,0 +1,35 @@ +#include +#include + +/* A fixed-size circular buffer */ +struct ring; + +/* Allocate a circular buffer with the given payload size. + Size must be < INT_MAX / 2. */ +extern struct ring *ring_allocate(int size); + +/* Signal that new data is been produced */ +extern void ring_producer_advance(struct ring *ring, int n); + +/* Signal that data has been consumed */ +extern void ring_consumer_advance(struct ring *ring, int n); + +/* The producer sends Eof. This will cause ring_consumer_wait_available + and ring_producer_wait_available to return an error. */ +extern void ring_producer_eof(struct ring *ring); + +/* Wait for n bytes of space for new data to become available. If + ring_producer_eof has been called, return non-zero. If space is available + then fill the first *iovec_len entries of the iovec and set *iovec_len to + the number of iovecs used. */ +extern int ring_producer_wait_available( + struct ring *ring, size_t n, struct iovec *iovec, int *iovec_len +); + +/* Wait for n bytes to become available for reading. If ring_producer_eof has + been called, return non-zero. If data is available then fill the first + *iovec_len entries of the iovec and set *iovec_len to the number of iovecs + used. */ +extern int ring_consumer_wait_available( + struct ring *ring, size_t n, struct iovec *iovec, int *iovec_len +); diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/tap-vsockd.c b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/tap-vsockd.c new file mode 100644 index 000000000..d2fe310b6 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/tap-vsockd.c @@ -0,0 +1,732 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "hvsock.h" +#include "protocol.h" +#include "ring.h" + +int daemon_flag; +int nofork_flag; +int listen_flag; +int connect_flag; + +char *default_sid = "30D48B34-7D27-4B0B-AAAF-BBBED334DD59"; + +/* Support big frames if the server requests it */ +const int max_packet_size = 16384; + +static int verbose; +#define INFO(...) \ + do { \ + if (verbose) { \ + printf(__VA_ARGS__); \ + fflush(stdout); \ + } \ + } while (0) +#define DBG(...) \ + do { \ + if (verbose > 1) { \ + printf(__VA_ARGS__); \ + fflush(stdout); \ + } \ + } while (0) +#define TRC(...) \ + do { \ + if (verbose > 2) { \ + printf(__VA_ARGS__); \ + fflush(stdout); \ + } \ + } while (0) + +void fatal(const char *msg) +{ + syslog(LOG_CRIT, "%s Error: %d. %s", msg, errno, strerror(errno)); + exit(1); +} + +int alloc_tap(const char *dev) +{ + const char *clonedev = "/dev/net/tun"; + struct ifreq ifr; + int persist = 1; + int fd; + + fd = open(clonedev, O_RDWR); + if (fd == -1) + fatal("Failed to open /dev/net/tun"); + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) + fatal("TUNSETIFF failed"); + + if (ioctl(fd, TUNSETPERSIST, persist) < 0) + fatal("TUNSETPERSIST failed"); + + syslog(LOG_INFO, "successfully created TAP device %s", dev); + return fd; +} + +void set_macaddr(const char *dev, uint8_t *mac) +{ + struct ifreq ifq; + int fd; + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd == -1) + fatal("Could not get socket to set MAC address"); + strcpy(ifq.ifr_name, dev); + memcpy(&ifq.ifr_hwaddr.sa_data[0], mac, 6); + ifq.ifr_hwaddr.sa_family = ARPHRD_ETHER; + + if (ioctl(fd, SIOCSIFHWADDR, &ifq) == -1) + fatal("SIOCSIFHWADDR failed"); + + close(fd); +} + +void set_mtu(const char *dev, int mtu) +{ + struct ifreq ifq; + int fd; + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd == -1) + fatal("Could not get socket to set MTU"); + strcpy(ifq.ifr_name, dev); + ifq.ifr_mtu = mtu; + + if (ioctl(fd, SIOCSIFMTU, &ifq) == -1) + fatal("SIOCSIFMTU failed"); + + close(fd); +} + +/* Negotiate a vmnet connection, returns 0 on success and 1 on error. */ +int negotiate(int fd, struct vif_info *vif) +{ + enum command command = ethernet; + struct init_message *me; + struct ethernet_args args; + struct init_message you; + char *txt; + + me = create_init_message(); + if (!me) + goto err; + + if (write_init_message(fd, me) == -1) + goto err; + + if (read_init_message(fd, &you) == -1) + goto err; + + if (me->version != you.version) { + syslog(LOG_CRIT, "Server did not accept our protocol version (client: %d, server: %d)", me->version, you.version); + goto err; + } + + txt = print_init_message(&you); + if (!txt) + goto err; + + syslog(LOG_INFO, "Server reports %s", txt); + free(txt); + + if (write_command(fd, &command) == -1) + goto err; + + /* We don't need a uuid */ + memset(&args.uuid_string[0], 0, sizeof(args.uuid_string)); + if (write_ethernet_args(fd, &args) == -1) + goto err; + + if (read_vif_response(fd, vif) == -1) + goto err; + + return 0; +err: + syslog(LOG_CRIT, "Failed to negotiate vmnet connection"); + return 1; +} + +/* Argument passed to proxy threads */ +struct connection { + int fd; /* Hyper-V socket with vmnet protocol */ + int tapfd; /* TAP device with ethernet frames */ + struct vif_info vif; /* Contains MAC, MTU etc, received from server */ + struct ring* from_vmnet_ring; + struct ring* to_vmnet_ring; + int message_size; /* Maximum size of a Hyper-V read or write */ +}; + +/* Trim the iovec so that it contains at most len bytes. */ +void trim_iovec(struct iovec *iovec, int *iovec_len, size_t len) +{ + for (int i = 0; i < *iovec_len; i++) { + if (iovec[i].iov_len > len) { + iovec[i].iov_len = len; + *iovec_len = i + 1; + return; + } + len -= iovec[i].iov_len; + } +} + +size_t len_iovec(struct iovec *iovec, int iovec_len) +{ + size_t len = 0; + for (int i = 0; i < iovec_len; i++) { + len += iovec[i].iov_len; + } + return len; +} + +/* Read bytes from vmnet into the from_vmnet_ring */ +static void* vmnet_to_ring(void *arg) +{ + struct connection *c = (struct connection *)arg; + struct ring *ring = c->from_vmnet_ring; + struct iovec iovec[2]; /* We won't need more than 2 for the ring */ + int iovec_len; + while (1) { + iovec_len = sizeof(iovec) / sizeof(struct iovec); + TRC("vmnet_to_ring: ring_producer_wait_available n=%d iovec_len=%d\n", 1, iovec_len); + if (ring_producer_wait_available(ring, 1, &iovec[0], &iovec_len) != 0) { + fatal("Failed to read a data from vmnet"); + } + trim_iovec(iovec, &iovec_len, c->message_size); + { + int length = 0; + for (int i = 0; i < iovec_len; i ++) { + length += iovec[i].iov_len; + } + TRC("vmnet_to_ring readv len %d\n", length); + } + ssize_t n = readv(c->fd, &iovec[0], iovec_len); + TRC("vmnet_to_ring: read %zd\n", n); + if (n == 0) { + syslog(LOG_CRIT, "EOF reading from socket: closing\n"); + ring_producer_eof(ring); + goto err; + } + if (n < 0) { + syslog(LOG_CRIT, + "Failure reading from socket: closing: %s (%d)", + strerror(errno), errno); + ring_producer_eof(ring); + goto err; + } + TRC("vmnet_to_ring: advance producer %zd\n", n); + ring_producer_advance(ring, (size_t) n); + } +err: + /* + * On error: stop reading from the socket and trigger a clean + * shutdown + */ + TRC("vmnet_to_ring: shutdown\n"); + shutdown(c->fd, SHUT_RD); + return NULL; +} + +/* Decode packets on the from_vmnet_ring and write to the tap device */ +static void* ring_to_tap(void *arg) +{ + struct connection *c = (struct connection *)arg; + struct iovec iovec[2]; /* We won't need more than 2 for the ring */ + int iovec_len; + int length; + struct ring *ring = c->from_vmnet_ring; + while (1) { + /* Read the packet length: this requires 2 bytes */ + iovec_len = sizeof(iovec) / sizeof(struct iovec); + TRC("ring_to_tap: ring_consumer_wait_available n=%d iovec_len=%d\n", 2, iovec_len); + if (ring_consumer_wait_available(ring, 2, &iovec[0], &iovec_len) != 0) { + fatal("Failed to read a packet header from host"); + } + length = *((uint8_t*)iovec[0].iov_base) & 0xff; + /* The second byte might be in the second iovec array */ + if (iovec[0].iov_len >= 2) { + length |= (*((uint8_t*)iovec[0].iov_base + 1) & 0xff) << 8; + } else { + length |= (*((uint8_t*)iovec[1].iov_base) & 0xff) << 8; + } + assert(length > 0); + TRC("ring_to_tap: packet of length %d\n", length); + if (length > max_packet_size) { + syslog(LOG_CRIT, + "Received an over-large packet: %d > %ld", + length, max_packet_size); + exit(1); + } + ring_consumer_advance(ring, 2); + + /* Read the variable length packet */ + iovec_len = sizeof(iovec) / sizeof(struct iovec); + TRC("ring_to_tap: ring_consumer_wait_available n=%d iovec_len=%d\n", length, iovec_len); + if (ring_consumer_wait_available(ring, length, &iovec[0], &iovec_len) != 0) { + fatal("Failed to read a packet body from host"); + } + assert(len_iovec(&iovec[0], iovec_len) >= length); + trim_iovec(iovec, &iovec_len, length); + ssize_t n = writev(c->tapfd, &iovec[0], iovec_len); + if (n != length) { + syslog(LOG_CRIT, + "Failed to write %d bytes to tap device (wrote %d)", length, n); + //exit(1); + } + TRC("ring_to_tap: ring_consumer_advance n=%zd\n", n); + ring_consumer_advance(ring, (size_t) length); + } + return NULL; +} + +/* Write packets with header from the tap device onto the to_vmnet_ring */ +static void *tap_to_ring(void *arg) +{ + struct connection *connection = (struct connection *)arg; + struct ring *ring = connection->to_vmnet_ring; + struct iovec iovec[2]; /* We won't need more than 2 for the ring */ + int iovec_len; + struct iovec payload[2]; /* The packet body after the 2 byte header */ + int payload_len; + size_t length; + while (1) { + /* Wait for space for a 2 byte header + max_packet_size */ + length = 2 + connection->vif.max_packet_size; + iovec_len = sizeof(iovec) / sizeof(struct iovec); + TRC("tap_to_ring: ring_producer_wait_available n=%zd iovec_len=%d\n", length, iovec_len); + if (ring_producer_wait_available(ring, length, &iovec[0], &iovec_len) != 0) { + fatal("Failed to find enough free space for a packet"); + } + assert(iovec_len > 0); + assert(iovec[0].iov_len > 0); + memcpy(&payload[0], &iovec[0], sizeof(struct iovec) * iovec_len); + payload_len = iovec_len; + + /* take the first 2 bytes of the free space which will contain the header */ + char *header1 = payload[0].iov_base; + payload[0].iov_base++; + payload[0].iov_len--; + if (payload[0].iov_len == 0) { + assert(payload_len == 2); /* because `length` > 1 */ + payload[0].iov_base = payload[1].iov_base; + payload[0].iov_len = payload[1].iov_len; + payload_len --; + } + char *header2 = payload[0].iov_base; + payload[0].iov_base++; + payload[0].iov_len--; + /* payload is now where the packet should go */ + + /* limit the message size */ + trim_iovec(payload, &payload_len, connection->message_size); + + length = readv(connection->tapfd, payload, payload_len); + + if (length == -1) { + if (errno == ENXIO) + fatal("tap device has gone down"); + + syslog(LOG_WARNING, "ignoring error %d", errno); + /* + * This is what mirage-net-unix does. Is it a good + * idea really? + */ + exit(1); + } + *header1 = (length >> 0) & 0xff; + *header2 = (length >> 8) & 0xff; + TRC("tap_to_ring: ring_producer_advance n=%zd\n", length + 2); + + ring_producer_advance(ring, (size_t) (length + 2)); + } + return NULL; +} + +/* Write bytes from the to_vmnet_ring to the vmnet fd */ +static void *ring_to_vmnet(void *arg) +{ + struct connection *c = (struct connection *)arg; + struct iovec iovec[2]; /* We won't need more than 2 for the ring */ + int iovec_len; + int length; + struct ring *ring = c->to_vmnet_ring; + while (1) { + /* Read the packet length: this requires 2 bytes */ + iovec_len = sizeof(iovec) / sizeof(struct iovec); + TRC("ring_to_vmnet: ring_producer_wait_available n=%d iovec_len=%d\n", 1, iovec_len); + if (ring_consumer_wait_available(ring, 1, &iovec[0], &iovec_len) != 0) { + fatal("Failed to read data from ring"); + } + trim_iovec(iovec, &iovec_len, c->message_size); + length = 0; + for (int i = 0; i < iovec_len; i++ ) { + length += iovec[i].iov_len; + } + TRC("ring_to_vmnet: read %d bytes\n", length); + ssize_t n = writev(c->fd, &iovec[0], iovec_len); + + TRC("ring_to_vmnet: advance consumer %zd\n", n); + ring_consumer_advance(ring, (size_t) n); + } + return NULL; +} + +/* + * Handle a connection by exchanging ethernet frames forever. + */ +static void handle(struct connection *connection) +{ + pthread_t v2r, r2t, t2r, r2v; + + if (pthread_create(&t2r, NULL, tap_to_ring, connection) != 0) + fatal("Failed to create the tap_to_ring thread"); + + if (pthread_create(&v2r, NULL, vmnet_to_ring, connection) != 0) + fatal("Failed to create the vmnet_to_tap thread"); + + if (pthread_create(&r2t, NULL, ring_to_tap, connection) != 0) + fatal("Failed to create the ring_to_tap thread"); + + if (pthread_create(&r2v, NULL, ring_to_vmnet, connection) != 0) + fatal("Failed to create the ring_to_vmnet thread"); + + if (pthread_join(t2r, NULL) != 0) + fatal("Failed to join the tap_to_ring thread"); + + if (pthread_join(v2r, NULL) != 0) + fatal("Failed to join the vmnet_to_ring thread"); + + if (pthread_join(t2r, NULL) != 0) + fatal("Failed to join the tap_to_ring thread"); + + if (pthread_join(r2v, NULL) != 0) + fatal("Failed to join the ring_to_vmnet thread"); +} + +static int create_listening_socket(GUID serviceid) +{ + SOCKADDR_HV sa; + int lsock; + int res; + + lsock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW); + if (lsock == -1) + fatal("socket()"); + + sa.Family = AF_HYPERV; + sa.Reserved = 0; + sa.VmId = HV_GUID_WILDCARD; + sa.ServiceId = serviceid; + + res = bind(lsock, (const struct sockaddr *)&sa, sizeof(sa)); + if (res == -1) + fatal("bind()"); + + res = listen(lsock, SOMAXCONN); + if (res == -1) + fatal("listen()"); + + return lsock; +} + +static int connect_socket(GUID serviceid) +{ + SOCKADDR_HV sa; + int sock; + int res; + + sock = socket(AF_HYPERV, SOCK_STREAM, HV_PROTOCOL_RAW); + if (sock == -1) + fatal("socket()"); + + sa.Family = AF_HYPERV; + sa.Reserved = 0; + sa.VmId = HV_GUID_PARENT; + sa.ServiceId = serviceid; + + res = connect(sock, (const struct sockaddr *)&sa, sizeof(sa)); + if (res == -1) + fatal("connect()"); + + return sock; +} + +static int accept_socket(int lsock) +{ + SOCKADDR_HV sac; + socklen_t socklen = sizeof(sac); + int csock; + + csock = accept(lsock, (struct sockaddr *)&sac, &socklen); + if (csock == -1) + fatal("accept()"); + + syslog(LOG_INFO, "Connect from: " GUID_FMT ":" GUID_FMT "\n", + GUID_ARGS(sac.VmId), GUID_ARGS(sac.ServiceId)); + + return csock; +} + +void write_pidfile(const char *pidfile) +{ + pid_t pid = getpid(); + char *pid_s; + FILE *file; + int len; + + if (asprintf(&pid_s, "%lld", (long long)pid) == -1) + fatal("Failed to allocate pidfile string"); + + len = strlen(pid_s); + file = fopen(pidfile, "w"); + if (file == NULL) { + syslog(LOG_CRIT, "Failed to open pidfile %s", pidfile); + exit(1); + } + + if (fwrite(pid_s, 1, len, file) != len) + fatal("Failed to write pid to pidfile"); + + fclose(file); + free(pid_s); +} + +void daemonize(const char *pidfile) +{ + pid_t pid; + int null; + + pid = fork(); + if (pid == -1) + fatal("Failed to fork()"); + else if (pid != 0) + exit(0); + + if (setsid() == -1) + fatal("Failed to setsid()"); + + if (chdir("/") == -1) + fatal("Failed to chdir()"); + + null = open("/dev/null", O_RDWR); + if (null == -1) + fatal("Failed to open /dev/null"); + dup2(null, STDIN_FILENO); + dup2(null, STDOUT_FILENO); + dup2(null, STDERR_FILENO); + close(null); + + if (pidfile) + write_pidfile(pidfile); +} + +void usage(char *name) +{ + printf("%s usage:\n", name); + printf("\t[--daemon] [--tap ] [--serviceid ] [--pid ]\n"); + printf("\t[--message-size ] [--buffer-size ]\n"); + printf("\t[--listen | --connect]\n\n"); + printf("where\n"); + printf("\t--daemonize: run as a background daemon\n"); + printf("\t--nofork: don't run handlers in subprocesses\n"); + printf("\t--tap : create a tap device with the given name\n"); + printf("\t (defaults to eth1)\n"); + printf("\t--serviceid : use as the well-known service GUID\n"); + printf("\t (defaults to %s)\n", default_sid); + printf("\t--pid : write a pid to the given file\n"); + printf("\t--message-size : dictates the maximum transfer size for AF_HVSOCK\n"); + printf("\t--buffer-size : dictates the buffer size for AF_HVSOCK\n"); + printf("\t--listen: listen forever for incoming AF_HVSOCK connections\n"); + printf("\t--connect: connect to the parent partition\n"); +} + +int main(int argc, char **argv) +{ + char *serviceid = default_sid; + struct connection connection; + char *tap = "eth1"; + char *pidfile = NULL; + int lsocket = -1; + int sock = -1; + int res = 0; + int status; + pid_t child; + int tapfd; + int ring_size = 1048576; + int message_size = 8192; /* Well known to work across Hyper-V versions */ + GUID sid; + int c; + + int option_index; + int log_flags = LOG_CONS | LOG_NDELAY; + static struct option long_options[] = { + /* These options set a flag. */ + {"daemon", no_argument, &daemon_flag, 1}, + {"nofork", no_argument, &nofork_flag, 1}, + {"serviceid", required_argument, NULL, 's'}, + {"tap", required_argument, NULL, 't'}, + {"pidfile", required_argument, NULL, 'p'}, + {"listen", no_argument, &listen_flag, 1}, + {"connect", no_argument, &connect_flag, 1}, + {"buffer-size", required_argument, NULL, 'b'}, + {"message-size", required_argument, NULL, 'm'}, + {0, 0, 0, 0} + }; + + opterr = 0; + while (1) { + option_index = 0; + + c = getopt_long(argc, argv, "ds:t:p:r:m:v", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'd': + daemon_flag = 1; + break; + case 'n': + nofork_flag = 1; + break; + case 's': + serviceid = optarg; + break; + case 't': + tap = optarg; + break; + case 'p': + pidfile = optarg; + break; + case 'b': + ring_size = atoi(optarg); + break; + case 'm': + message_size = atoi(optarg); + break; + case 'v': + verbose ++; + break; + case 0: + break; + default: + usage(argv[0]); + exit(1); + } + } + + if ((listen_flag && connect_flag) || !(listen_flag || connect_flag)) { + fprintf(stderr, "Please supply either the --listen or --connect flag, but not both.\n"); + exit(1); + } + + if (daemon_flag && !pidfile) { + fprintf(stderr, "For daemon mode, please supply a --pidfile argument.\n"); + exit(1); + } + + res = parseguid(serviceid, &sid); + if (res) { + fprintf(stderr, "Failed to parse serviceid as GUID: %s\n", serviceid); + usage(argv[0]); + exit(1); + } + + if (!daemon_flag) + log_flags |= LOG_PERROR; + + openlog(argv[0], log_flags, LOG_DAEMON); + + tapfd = alloc_tap(tap); + connection.tapfd = tapfd; + connection.to_vmnet_ring = ring_allocate(ring_size); + connection.from_vmnet_ring = ring_allocate(ring_size); + connection.message_size = message_size; + if (listen_flag) { + syslog(LOG_INFO, "starting in listening mode with serviceid=%s and tap=%s", serviceid, tap); + lsocket = create_listening_socket(sid); + } else { + syslog(LOG_INFO, "starting in connect mode with serviceid=%s and tap=%s", serviceid, tap); + } + + for (;;) { + if (sock != -1) { + close(sock); + sock = -1; + } + + if (listen_flag) + sock = accept_socket(lsocket); + else + sock = connect_socket(sid); + + connection.fd = sock; + if (negotiate(sock, &connection.vif) != 0) { + sleep(1); + continue; + } + + syslog(LOG_INFO, "VMNET VIF has MAC %02x:%02x:%02x:%02x:%02x:%02x", + connection.vif.mac[0], connection.vif.mac[1], connection.vif.mac[2], + connection.vif.mac[3], connection.vif.mac[4], connection.vif.mac[5] + ); + set_macaddr(tap, &connection.vif.mac[0]); + set_mtu(tap, connection.vif.mtu); + + /* Daemonize after we've made our first reliable connection */ + if (daemon_flag) { + daemon_flag = 0; + daemonize(pidfile); + } + if (nofork_flag) { + handle(&connection); + exit(1); + } + /* + * Run the multithreaded part in a subprocess. On error the + * process will exit() which tears down all the threads + */ + child = fork(); + if (child == 0) { + handle(&connection); + /* + * should never happen but just in case of a logic + * bug in handle + */ + exit(1); + } + + for (;;) { + if (waitpid(child, &status, 0) != -1) + break; + } + } +} diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/connection.go b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/connection.go new file mode 100644 index 000000000..a41f27f0a --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/connection.go @@ -0,0 +1,22 @@ +package vpnkit + +import ( + "context" + + datakit "github.com/moby/datakit/api/go-datakit" +) + +// Connection represents an open control connection to vpnkit +type Connection struct { + client *datakit.Client +} + +// NewConnection connects to a vpnkit Unix domain socket on the given path +// and returns the connection +func NewConnection(ctx context.Context, path string) (*Connection, error) { + client, err := datakit.Dial(ctx, "unix", path) + if err != nil { + return nil, err + } + return &Connection{client}, nil +} diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/doc.go b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/doc.go new file mode 100644 index 000000000..e1ddd2401 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/doc.go @@ -0,0 +1,9 @@ +/* +Package vpnkit allows a running VPNKit service to be reconfigured. + +Features + +- expose/unexpose TCP and UDP ports + +*/ +package vpnkit diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/port.go b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/port.go new file mode 100644 index 000000000..4eb41fac0 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/port.go @@ -0,0 +1,200 @@ +package vpnkit + +import ( + "context" + "errors" + "fmt" + "log" + "net" + "strconv" + "strings" + + p9p "github.com/docker/go-p9p" + datakit "github.com/moby/datakit/api/go-datakit" +) + +// Port describes a UDP or TCP port forward +type Port struct { + client *datakit.Client + proto string + outIP net.IP + outPort uint16 + inIP net.IP + inPort uint16 + handle *datakit.File +} + +// NewPort constructs an instance of Port +func NewPort(connection *Connection, proto string, outIP net.IP, outPort uint16, inIP net.IP, inPort uint16) *Port { + return &Port{connection.client, proto, outIP, outPort, inIP, inPort, nil} +} + +// ListExposed returns a list of currently exposed ports +func ListExposed(connection *Connection) ([]*Port, error) { + ctx := context.TODO() + dirs, err := connection.client.List(ctx, []string{}) + if err != nil { + return nil, err + } + results := make([]*Port, 0) + + for _, name := range dirs { + port, err := parse(name) + if err != nil { + // there are some special files like "." and "README" to ignore + continue + } + port.client = connection.client + results = append(results, port) + } + + return results, nil +} + +// String returns a human-readable string +func (p *Port) String() string { + return fmt.Sprintf("%s forward from %s:%d to %s:%d", p.proto, p.outIP.String(), p.outPort, p.inIP.String(), p.inPort) +} + +// spec returns a string of the form proto:outIP:outPort:proto:inIP:inPort as +// understood by vpnkit +func (p *Port) spec() string { + return fmt.Sprintf("%s:%s:%d:%s:%s:%d", p.proto, p.outIP.String(), p.outPort, p.proto, p.inIP.String(), p.inPort) +} + +func parse(name string) (*Port, error) { + bits := strings.Split(name, ":") + if len(bits) != 6 { + return nil, errors.New("Failed to parse port spec: " + name) + } + outProto := bits[0] + outIP := net.ParseIP(bits[1]) + outPort, err := strconv.ParseUint(bits[2], 10, 16) + if err != nil { + return nil, err + } + inProto := bits[3] + inIP := net.ParseIP(bits[4]) + inPort, err := strconv.ParseUint(bits[5], 10, 16) + if err != nil { + return nil, err + } + if outProto != inProto { + return nil, errors.New("Failed to parse port: external proto is " + outProto + " but internal proto is " + inProto) + } + return &Port{nil, outProto, outIP, uint16(outPort), inIP, uint16(inPort), nil}, nil +} + +// Expose asks vpnkit to expose the port +func (p *Port) Expose(ctx context.Context) error { + if p.handle != nil { + return errors.New("Port is already exposed") + } + spec := p.spec() + client := p.client + // use the spec also as a name + name := spec + + log.Printf("Expose %s\n", spec) + _ = client.Remove(ctx, name) + + err := client.Mkdir(ctx, name) + if err != nil { + log.Printf("Expose failed to create %s: %#v\n", name, err) + return err + } + ctl, err := client.Open(ctx, p9p.OREAD, name, "ctl") + if err != nil { + log.Printf("Expose failed to open %s/ctl: %#v\n", name, err) + return err + } + // NB we deliberately leak the fid because we use the clunk as a signal to + // shutdown the forward. + + // Read any error from a previous session + bytes := make([]byte, 100) + n, err := ctl.Read(ctx, bytes, 0) + if err != nil { + log.Printf("Expose %s: failed to read response from ctl: %#v\n", spec, err) + return err + } + _, _ = ctl.Read(ctx, bytes, int64(n)) + + response := string(bytes) + if !strings.HasPrefix(response, "ERROR no request received") { + log.Printf("Expose %s: read error from previous operation: %s\n", spec, response[0:n]) + } + + request := []byte(spec) + _, err = ctl.Write(ctx, request, 0) + if err != nil { + log.Printf("Expose %s: failed to write to ctl: %#v\n", spec, err) + return err + } + + n, err = ctl.Read(ctx, bytes, 0) + if err != nil { + log.Printf("Expose %s: failed to read response from ctl: %#v\n", spec, err) + return err + } + + _, _ = ctl.Read(ctx, bytes, int64(n)) + response = string(bytes) + if strings.HasPrefix(response, "OK ") { + response = strings.Trim(response[3:n], " \t\r\n") + log.Printf("Expose %s: succeeded with %s\n", spec, response) + p.handle = ctl + return nil + } + + log.Printf("Expose %s: failed: %s\n", spec, response[0:n]) + if strings.HasPrefix(response, "ERROR ") { + response = strings.Trim(response[6:n], " \t\r\n") + ctl.Close(ctx) + } + + return errors.New(response) +} + +// Unexpose asks vpnkit to hide the port again +func (p *Port) Unexpose(ctx context.Context) error { + if p.handle == nil { + ctl, err := p.client.Open(ctx, p9p.OREAD, p.spec(), "ctl") + if err != nil { + return errors.New("Port is not exposed") + } + p.handle = ctl + } + ctl := p.handle + p.handle = nil + // Any clunk frees the port + ctl.Close(ctx) + return nil +} + +// Proto returns the protocol: either "tcp" or "udp" +func (p *Port) Proto() string { + return p.proto +} + +// OutIP returns the public IP +func (p *Port) OutIP() net.IP { + return p.outIP +} + +// OutPort returns the public port number +func (p *Port) OutPort() uint16 { + return p.outPort +} + +// InIP returns the private IP +func (p *Port) InIP() net.IP { + return p.inIP +} + +// InPort returns the private port number +func (p *Port) InPort() uint16 { + return p.inPort +} + +var enoent = p9p.MessageRerror{Ename: "file not found"} diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/vmnet.go b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/vmnet.go new file mode 100644 index 000000000..b57594283 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/vmnet.go @@ -0,0 +1,633 @@ +package vpnkit + +import ( + "bytes" + "context" + "encoding/binary" + "errors" + "fmt" + "io" + "net" + "os" + "time" + + "github.com/google/uuid" +) + +// Vmnet describes a "vmnet protocol" connection which allows ethernet frames to be +// sent to and received by vpnkit. +type Vmnet struct { + conn net.Conn + remoteVersion *InitMessage +} + +// NewVmnet constructs an instance of Vmnet. +func NewVmnet(ctx context.Context, path string) (*Vmnet, error) { + d := &net.Dialer{} + conn, err := d.DialContext(ctx, "unix", path) + if err != nil { + return nil, err + } + var remoteVersion *InitMessage + vmnet := &Vmnet{conn, remoteVersion} + err = vmnet.negotiate() + if err != nil { + return nil, err + } + return vmnet, err +} + +// Close closes the connection. +func (v *Vmnet) Close() error { + return v.conn.Close() +} + +// InitMessage is used for the initial version exchange +type InitMessage struct { + magic [5]byte + version uint32 + commit [40]byte +} + +// String returns a human-readable string. +func (m *InitMessage) String() string { + return fmt.Sprintf("magic=%v version=%d commit=%v", m.magic, m.version, m.commit) +} + +// defaultInitMessage is the init message we will send to vpnkit +func defaultInitMessage() *InitMessage { + magic := [5]byte{'V', 'M', 'N', '3', 'T'} + version := uint32(22) + var commit [40]byte + copy(commit[:], []byte("0123456789012345678901234567890123456789")) + return &InitMessage{magic, version, commit} +} + +// Write marshals an init message to a connection +func (m *InitMessage) Write(c net.Conn) error { + if err := binary.Write(c, binary.LittleEndian, m.magic); err != nil { + return err + } + if err := binary.Write(c, binary.LittleEndian, m.version); err != nil { + return err + } + if err := binary.Write(c, binary.LittleEndian, m.commit); err != nil { + return err + } + return nil +} + +// readInitMessage unmarshals an init message from a connection +func (v *Vmnet) readInitMessage() (*InitMessage, error) { + m := defaultInitMessage() + if err := binary.Read(v.conn, binary.LittleEndian, &m.magic); err != nil { + return nil, err + } + if err := binary.Read(v.conn, binary.LittleEndian, &m.version); err != nil { + return nil, err + } + if err := binary.Read(v.conn, binary.LittleEndian, &m.commit); err != nil { + return nil, err + } + return m, nil +} + +func (v *Vmnet) negotiate() error { + m := defaultInitMessage() + if err := m.Write(v.conn); err != nil { + return err + } + remoteVersion, err := v.readInitMessage() + if err != nil { + return err + } + v.remoteVersion = remoteVersion + return nil +} + +// Ethernet requests the creation of a network connection with a given +// uuid and optional IP +type Ethernet struct { + uuid uuid.UUID + ip net.IP +} + +// NewEthernet creates an Ethernet frame +func NewEthernet(uuid uuid.UUID, ip net.IP) *Ethernet { + return &Ethernet{uuid, ip} +} + +// Write marshals an Ethernet message +func (m *Ethernet) Write(c net.Conn) error { + ty := uint8(1) + if m.ip != nil { + ty = uint8(8) + } + if err := binary.Write(c, binary.LittleEndian, ty); err != nil { + return err + } + u, err := m.uuid.MarshalText() + if err != nil { + return err + } + if err := binary.Write(c, binary.LittleEndian, u); err != nil { + return err + } + ip := uint32(0) + if m.ip != nil { + ip = binary.BigEndian.Uint32(m.ip.To4()) + } + // The protocol uses little endian, not network endian + if err := binary.Write(c, binary.LittleEndian, ip); err != nil { + return err + } + return nil +} + +// Vif represents an Ethernet device +type Vif struct { + MTU uint16 + MaxPacketSize uint16 + ClientMAC net.HardwareAddr + IP net.IP + conn net.Conn +} + +func (v *Vmnet) readVif() (*Vif, error) { + var MTU, MaxPacketSize uint16 + + if err := binary.Read(v.conn, binary.LittleEndian, &MTU); err != nil { + return nil, err + } + if err := binary.Read(v.conn, binary.LittleEndian, &MaxPacketSize); err != nil { + return nil, err + } + var mac [6]byte + if err := binary.Read(v.conn, binary.LittleEndian, &mac); err != nil { + return nil, err + } + padding := make([]byte, 1+256-6-2-2) + if err := binary.Read(v.conn, binary.LittleEndian, &padding); err != nil { + return nil, err + } + ClientMAC := mac[:] + conn := v.conn + var IP net.IP + return &Vif{MTU, MaxPacketSize, ClientMAC, IP, conn}, nil +} + +// ConnectVif returns a connected network interface with the given uuid. +func (v *Vmnet) ConnectVif(uuid uuid.UUID) (*Vif, error) { + e := NewEthernet(uuid, nil) + if err := e.Write(v.conn); err != nil { + return nil, err + } + var responseType uint8 + if err := binary.Read(v.conn, binary.LittleEndian, &responseType); err != nil { + return nil, err + } + switch responseType { + case 1: + vif, err := v.readVif() + if err != nil { + return nil, err + } + IP, err := vif.dhcp() + if err != nil { + return nil, err + } + vif.IP = IP + return vif, err + default: + var len uint8 + if err := binary.Read(v.conn, binary.LittleEndian, &len); err != nil { + return nil, err + } + message := make([]byte, len) + if err := binary.Read(v.conn, binary.LittleEndian, &message); err != nil { + return nil, err + } + return nil, errors.New(string(message)) + } +} + +// ConnectVifIP returns a connected network interface with the given uuid +// and IP. If the IP is already in use then return an error. +func (v *Vmnet) ConnectVifIP(uuid uuid.UUID, IP net.IP) (*Vif, error) { + e := NewEthernet(uuid, IP) + if err := e.Write(v.conn); err != nil { + return nil, err + } + var responseType uint8 + if err := binary.Read(v.conn, binary.LittleEndian, &responseType); err != nil { + return nil, err + } + switch responseType { + case 1: + vif, err := v.readVif() + if err != nil { + return nil, err + } + vif.IP = IP + return vif, err + default: + var len uint8 + if err := binary.Read(v.conn, binary.LittleEndian, &len); err != nil { + return nil, err + } + message := make([]byte, len) + if err := binary.Read(v.conn, binary.LittleEndian, &message); err != nil { + return nil, err + } + return nil, errors.New(string(message)) + } +} + +// Write writes a packet to a Vif +func (v *Vif) Write(packet []byte) error { + len := uint16(len(packet)) + if err := binary.Write(v.conn, binary.LittleEndian, len); err != nil { + return err + } + if err := binary.Write(v.conn, binary.LittleEndian, packet); err != nil { + return err + } + return nil +} + +// Read reads the next packet from a Vif +func (v *Vif) Read() ([]byte, error) { + var len uint16 + if err := binary.Read(v.conn, binary.LittleEndian, &len); err != nil { + return nil, err + } + packet := make([]byte, len) + if err := binary.Read(v.conn, binary.LittleEndian, &packet); err != nil { + return nil, err + } + return packet, nil +} + +// PcapWriter writes pcap-formatted packet streams +type PcapWriter struct { + w io.Writer + snaplen uint32 +} + +// NewPcapWriter creates a PcapWriter and writes the initial header +func NewPcapWriter(w io.Writer) (*PcapWriter, error) { + magic := uint32(0xa1b2c3d4) + major := uint16(2) + minor := uint16(4) + thiszone := uint32(0) // GMT to local correction + sigfigs := uint32(0) // accuracy of local timestamps + snaplen := uint32(1500) // max length of captured packets, in octets + network := uint32(1) // ethernet + if err := binary.Write(w, binary.LittleEndian, magic); err != nil { + return nil, err + } + if err := binary.Write(w, binary.LittleEndian, major); err != nil { + return nil, err + } + if err := binary.Write(w, binary.LittleEndian, minor); err != nil { + return nil, err + } + if err := binary.Write(w, binary.LittleEndian, thiszone); err != nil { + return nil, err + } + if err := binary.Write(w, binary.LittleEndian, sigfigs); err != nil { + return nil, err + } + if err := binary.Write(w, binary.LittleEndian, snaplen); err != nil { + return nil, err + } + if err := binary.Write(w, binary.LittleEndian, network); err != nil { + return nil, err + } + return &PcapWriter{w, snaplen}, nil +} + +// Write appends a packet with a pcap-format header +func (p *PcapWriter) Write(packet []byte) error { + stamp := time.Now() + s := uint32(stamp.Second()) + us := uint32(stamp.Nanosecond() / 1000) + actualLen := uint32(len(packet)) + if err := binary.Write(p.w, binary.LittleEndian, s); err != nil { + return err + } + if err := binary.Write(p.w, binary.LittleEndian, us); err != nil { + return err + } + toWrite := packet[:] + if actualLen > p.snaplen { + toWrite = toWrite[0:p.snaplen] + } + caplen := uint32(len(toWrite)) + if err := binary.Write(p.w, binary.LittleEndian, caplen); err != nil { + return err + } + if err := binary.Write(p.w, binary.LittleEndian, actualLen); err != nil { + return err + } + + if err := binary.Write(p.w, binary.LittleEndian, toWrite); err != nil { + return err + } + return nil +} + +// EthernetFrame is an ethernet frame +type EthernetFrame struct { + Dst net.HardwareAddr + Src net.HardwareAddr + Type uint16 + Data []byte +} + +// NewEthernetFrame constructs an Ethernet frame +func NewEthernetFrame(Dst, Src net.HardwareAddr, Type uint16) *EthernetFrame { + Data := make([]byte, 0) + return &EthernetFrame{Dst, Src, Type, Data} +} + +func (e *EthernetFrame) setData(data []byte) { + e.Data = data +} + +// Write marshals an Ethernet frame +func (e *EthernetFrame) Write(w io.Writer) error { + if err := binary.Write(w, binary.BigEndian, e.Dst); err != nil { + return err + } + if err := binary.Write(w, binary.BigEndian, e.Src); err != nil { + return err + } + if err := binary.Write(w, binary.BigEndian, e.Type); err != nil { + return err + } + if err := binary.Write(w, binary.BigEndian, e.Data); err != nil { + return err + } + return nil +} + +// ParseEthernetFrame parses the ethernet frame +func ParseEthernetFrame(frame []byte) (*EthernetFrame, error) { + if len(frame) < (6 + 6 + 2) { + return nil, errors.New("Ethernet frame is too small") + } + Dst := frame[0:6] + Src := frame[6:12] + Type := uint16(frame[12])<<8 + uint16(frame[13]) + Data := frame[14:] + return &EthernetFrame{Dst, Src, Type, Data}, nil +} + +// Bytes returns the marshalled ethernet frame +func (e *EthernetFrame) Bytes() []byte { + buf := bytes.NewBufferString("") + if err := e.Write(buf); err != nil { + panic(err) + } + return buf.Bytes() +} + +// Ipv4 is an IPv4 frame +type Ipv4 struct { + Dst net.IP + Src net.IP + Data []byte + Checksum uint16 +} + +// NewIpv4 constructs a new empty IPv4 packet +func NewIpv4(Dst, Src net.IP) *Ipv4 { + Checksum := uint16(0) + Data := make([]byte, 0) + return &Ipv4{Dst, Src, Data, Checksum} +} + +// ParseIpv4 parses an IP packet +func ParseIpv4(packet []byte) (*Ipv4, error) { + if len(packet) < 20 { + return nil, errors.New("IPv4 packet too small") + } + ihl := int((packet[0] & 0xf) * 4) // in octets + if len(packet) < ihl { + return nil, errors.New("IPv4 packet too small") + } + Dst := packet[12:16] + Src := packet[16:20] + Data := packet[ihl:] + Checksum := uint16(0) // assume offload + return &Ipv4{Dst, Src, Data, Checksum}, nil +} + +func (i *Ipv4) setData(data []byte) { + i.Data = data + i.Checksum = uint16(0) // as if we were using offload +} + +// HeaderBytes returns the marshalled form of the IPv4 header +func (i *Ipv4) HeaderBytes() []byte { + len := len(i.Data) + 20 + length := [2]byte{byte(len >> 8), byte(len & 0xff)} + checksum := [2]byte{byte(i.Checksum >> 8), byte(i.Checksum & 0xff)} + return []byte{ + 0x45, // version + IHL + 0x00, // DSCP + ECN + length[0], length[1], // total length + 0x7f, 0x61, // Identification + 0x00, 0x00, // Flags + Fragment offset + 0x40, // TTL + 0x11, // Protocol + checksum[0], checksum[1], + 0x00, 0x00, 0x00, 0x00, // source + 0xff, 0xff, 0xff, 0xff, // destination + } +} + +// Bytes returns the marshalled IPv4 packet +func (i *Ipv4) Bytes() []byte { + header := i.HeaderBytes() + return append(header, i.Data...) +} + +// Udpv4 is a Udpv4 frame +type Udpv4 struct { + Src uint16 + Dst uint16 + Data []byte + Checksum uint16 +} + +// NewUdpv4 constructs a Udpv4 frame +func NewUdpv4(ipv4 *Ipv4, Dst, Src uint16, Data []byte) *Udpv4 { + Checksum := uint16(0) + return &Udpv4{Dst, Src, Data, Checksum} +} + +// ParseUdpv4 parses a Udpv4 packet +func ParseUdpv4(packet []byte) (*Udpv4, error) { + if len(packet) < 8 { + return nil, errors.New("UDPv4 is too short") + } + Src := uint16(packet[0])<<8 + uint16(packet[1]) + Dst := uint16(packet[2])<<8 + uint16(packet[3]) + Checksum := uint16(packet[6])<<8 + uint16(packet[7]) + Data := packet[8:] + return &Udpv4{Src, Dst, Data, Checksum}, nil +} + +// Write marshalls a Udpv4 frame +func (u *Udpv4) Write(w io.Writer) error { + if err := binary.Write(w, binary.BigEndian, u.Src); err != nil { + return err + } + if err := binary.Write(w, binary.BigEndian, u.Dst); err != nil { + return err + } + length := uint16(8 + len(u.Data)) + if err := binary.Write(w, binary.BigEndian, length); err != nil { + return err + } + if err := binary.Write(w, binary.BigEndian, u.Checksum); err != nil { + return err + } + if err := binary.Write(w, binary.BigEndian, u.Data); err != nil { + return err + } + return nil +} + +// Bytes returns the marshalled Udpv4 frame +func (u *Udpv4) Bytes() []byte { + buf := bytes.NewBufferString("") + if err := u.Write(buf); err != nil { + panic(err) + } + return buf.Bytes() +} + +// DhcpRequest is a simple DHCP request +type DhcpRequest struct { + MAC net.HardwareAddr +} + +// NewDhcpRequest constructs a DHCP request +func NewDhcpRequest(MAC net.HardwareAddr) *DhcpRequest { + if len(MAC) != 6 { + panic("MAC address must be 6 bytes") + } + return &DhcpRequest{MAC} +} + +// Bytes returns the marshalled DHCP request +func (d *DhcpRequest) Bytes() []byte { + bs := []byte{ + 0x01, // OP + 0x01, // HTYPE + 0x06, // HLEN + 0x00, // HOPS + 0x01, 0x00, 0x00, 0x00, // XID + 0x00, 0x00, // SECS + 0x80, 0x00, // FLAGS + 0x00, 0x00, 0x00, 0x00, // CIADDR + 0x00, 0x00, 0x00, 0x00, // YIADDR + 0x00, 0x00, 0x00, 0x00, // SIADDR + 0x00, 0x00, 0x00, 0x00, // GIADDR + d.MAC[0], d.MAC[1], d.MAC[2], d.MAC[3], d.MAC[4], d.MAC[5], + } + bs = append(bs, make([]byte, 202)...) + bs = append(bs, []byte{ + 0x63, 0x82, 0x53, 0x63, // Magic cookie + 0x35, 0x01, 0x01, // DHCP discover + 0xff, // Endmark + }...) + return bs +} + +// dhcp queries the IP by DHCP +func (v *Vif) dhcp() (net.IP, error) { + broadcastMAC := []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + broadcastIP := []byte{0xff, 0xff, 0xff, 0xff} + unknownIP := []byte{0, 0, 0, 0} + + dhcpRequest := NewDhcpRequest(v.ClientMAC).Bytes() + ipv4 := NewIpv4(broadcastIP, unknownIP) + + udpv4 := NewUdpv4(ipv4, 68, 67, dhcpRequest) + ipv4.setData(udpv4.Bytes()) + + ethernet := NewEthernetFrame(broadcastMAC, v.ClientMAC, 0x800) + ethernet.setData(ipv4.Bytes()) + + file, err := os.Create("/tmp/go.pcap") + if err != nil { + panic(err) + } + pcap, err := NewPcapWriter(file) + if err != nil { + panic(err) + } + finished := false + go func() { + for !finished { + if err := v.Write(ethernet.Bytes()); err != nil { + panic(err) + } + if err := pcap.Write(ethernet.Bytes()); err != nil { + panic(err) + } + time.Sleep(time.Second) + } + }() + + for { + response, err := v.Read() + if err != nil { + return nil, err + } + if err := pcap.Write(response); err != nil { + panic(err) + } + ethernet, err = ParseEthernetFrame(response) + if err != nil { + continue + } + for i, x := range ethernet.Dst { + if i > len(v.ClientMAC) || v.ClientMAC[i] != x { + // intended for someone else + continue + } + } + ipv4, err = ParseIpv4(ethernet.Data) + if err != nil { + // probably not an IPv4 packet + continue + } + udpv4, err = ParseUdpv4(ipv4.Data) + if err != nil { + // probably not a UDPv4 packet + continue + } + if udpv4.Src != 67 || udpv4.Dst != 68 { + // not a DHCP response + continue + } + if len(udpv4.Data) < 243 { + // truncated + continue + } + if udpv4.Data[240] != 53 || udpv4.Data[241] != 1 || udpv4.Data[242] != 2 { + // not a DHCP offer + continue + } + var ip net.IP + ip = udpv4.Data[16:20] + finished = true // will terminate sending goroutine + return ip, nil + } + +} diff --git a/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/vendor.conf b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/vendor.conf new file mode 100644 index 000000000..8fe197022 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/vendor.conf @@ -0,0 +1,5 @@ +golang.org/x/sys 9c9d83fe39ed3fd2d9249fcf6b755891fff54b03 +github.com/linuxkit/virtsock a381dcc5bcddf1d7f449495c373dbf70f8e501c0 +github.com/docker/go-p9p 87ae8514a3a2d9684994a6c319f96ba9e18a062e +github.com/moby/datakit 97b3d230535397a813323902c23751e176481a86 +github.com/google/uuid 7e072fc3a7be179aee6d3359e46015aa8c995314 \ No newline at end of file From 69501a8b269d9079ad2bd6f11e4eb4c390ac3109 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Wed, 8 Nov 2017 14:53:45 +0000 Subject: [PATCH 05/11] tool/hyperkit: Add ability to expose port on localhost When hyperkit is used with VPNKit we can "publish" VM ports on localhost by using an API to the VPNKit process. This commit adds a "-publish" flag to the hyperkit backend exposing this functionality. "-publish" can be used multiple times and is used the same way as for the qemu backend. Other changes: - Use 'github.com/google/uuid' as the 'uuid' package since VPKNkit uses it - use 'vpnkit' prefix instead of 'vpnKit' - Improve logging Signed-off-by: Rolf Neugebauer --- src/cmd/linuxkit/run_hyperkit.go | 154 ++++++++++++++++++++++++++----- 1 file changed, 130 insertions(+), 24 deletions(-) diff --git a/src/cmd/linuxkit/run_hyperkit.go b/src/cmd/linuxkit/run_hyperkit.go index fa610c6bb..1eddd79c4 100644 --- a/src/cmd/linuxkit/run_hyperkit.go +++ b/src/cmd/linuxkit/run_hyperkit.go @@ -1,6 +1,7 @@ package main import ( + "context" "flag" "fmt" "io/ioutil" @@ -11,8 +12,9 @@ import ( "strconv" "strings" + "github.com/google/uuid" "github.com/moby/hyperkit/go" - "github.com/satori/go.uuid" + "github.com/moby/vpnkit/go/pkg/vpnkit" log "github.com/sirupsen/logrus" ) @@ -44,16 +46,18 @@ func runHyperKit(args []string) { ipStr := flags.String("ip", "", "Preferred IPv4 address for the VM.") state := flags.String("state", "", "Path to directory to keep VM state in") vsockports := flags.String("vsock-ports", "", "List of vsock ports to forward from the guest on startup (comma separated). A unix domain socket for each port will be created in the state directory") - networking := flags.String("networking", hyperkitNetworkingDefault, "Networking mode. Valid options are 'default', 'docker-for-mac', 'vpnkit[,socket-path]', 'vmnet' and 'none'. 'docker-for-mac' connects to the network used by Docker for Mac. 'vpnkit' connects to the VPNKit socket specified. If socket-path is omitted a new VPNKit instance will be started and 'vpnkit_eth.sock' will be created in the state directory. 'vmnet' uses the Apple vmnet framework, requires root/sudo. 'none' disables networking.`") + networking := flags.String("networking", hyperkitNetworkingDefault, "Networking mode. Valid options are 'default', 'docker-for-mac', 'vpnkit[,eth-socket-path[,port-socket-path]]', 'vmnet' and 'none'. 'docker-for-mac' connects to the network used by Docker for Mac. 'vpnkit' connects to the VPNKit socket(s) specified. If no socket path is provided a new VPNKit instance will be started and 'vpnkit_eth.sock' and 'vpnkit_port.sock' will be created in the state directory. 'port-socket-path' is only needed if you want to publish ports on localhost using an existing VPNKit instance. 'vmnet' uses the Apple vmnet framework, requires root/sudo. 'none' disables networking.`") - vpnKitUUID := flags.String("vpnkit-uuid", "", "Optional UUID used to identify the VPNKit connection. Overrides 'uuid.vpnkit' in the state directory.") + vpnkitUUID := flags.String("vpnkit-uuid", "", "Optional UUID used to identify the VPNKit connection. Overrides 'uuid.vpnkit' in the state directory.") + publishFlags := multipleFlag{} + flags.Var(&publishFlags, "publish", "Publish a vm's port(s) to the host (default [])") // Boot type; we try to determine automatically uefiBoot := flags.Bool("uefi", false, "Use UEFI boot") isoBoot := flags.Bool("iso", false, "Boot image is an ISO") kernelBoot := flags.Bool("kernel", false, "Boot image is kernel+initrd+cmdline 'path'-kernel/-initrd/-cmdline") - // Paths and settings for UEFI firware + // Paths and settings for UEFI firmware // Note, the default uses the firmware shipped with Docker for Mac fw := flags.String("fw", "/Applications/Docker.app/Contents/Resources/uefi/UEFI.fd", "Path to OVMF firmware for UEFI boot") @@ -154,24 +158,29 @@ func runHyperKit(args []string) { // Create UUID for VPNKit or reuse an existing one from state dir. IP addresses are // assigned to the UUID, so to get the same IP we have to store the initial UUID. If // has specified a VPNKit UUID the file is ignored. - if *vpnKitUUID == "" { - vpnKitUUIDFile := filepath.Join(*state, "uuid.vpnkit") - if _, err := os.Stat(vpnKitUUIDFile); os.IsNotExist(err) { - *vpnKitUUID = uuid.NewV4().String() - if err := ioutil.WriteFile(vpnKitUUIDFile, []byte(*vpnKitUUID), 0600); err != nil { - log.Fatalf("Unable to write to %s: %v", vpnKitUUIDFile, err) + if *vpnkitUUID == "" { + vpnkitUUIDFile := filepath.Join(*state, "uuid.vpnkit") + if _, err := os.Stat(vpnkitUUIDFile); os.IsNotExist(err) { + *vpnkitUUID = uuid.New().String() + if err := ioutil.WriteFile(vpnkitUUIDFile, []byte(*vpnkitUUID), 0600); err != nil { + log.Fatalf("Unable to write to %s: %v", vpnkitUUIDFile, err) } } else { - uuid, err := ioutil.ReadFile(vpnKitUUIDFile) + uuidBytes, err := ioutil.ReadFile(vpnkitUUIDFile) if err != nil { - log.Fatalf("Unable to read VPNKit UUID from %s: %v", vpnKitUUIDFile, err) + log.Fatalf("Unable to read VPNKit UUID from %s: %v", vpnkitUUIDFile, err) } - *vpnKitUUID = string(uuid) + if tmp, err := uuid.ParseBytes(uuidBytes); err != nil { + log.Fatalf("Unable to parse VPNKit UUID from %s: %v", vpnkitUUIDFile, err) + } else { + *vpnkitUUID = tmp.String() + } + } } // Generate new UUID, otherwise /sys/class/dmi/id/product_uuid is identical on all VMs - vmUUID := uuid.NewV4().String() + vmUUID := uuid.New().String() // Run var cmdline []byte @@ -208,39 +217,43 @@ func runHyperKit(args []string) { } // Select network mode - var vpnKitProcess *os.Process + var vpnkitProcess *os.Process + var vpnkitPortSocket string if *networking == "" || *networking == "default" { dflt := hyperkitNetworkingDefault networking = &dflt } netMode := strings.SplitN(*networking, ",", 2) - switch netMode[0] { case hyperkitNetworkingDockerForMac: h.VPNKitSock = filepath.Join(os.Getenv("HOME"), "Library/Containers/com.docker.docker/Data/s50") + vpnkitPortSocket = filepath.Join(os.Getenv("HOME"), "Library/Containers/com.docker.docker/Data/s51") case hyperkitNetworkingVPNKit: if len(netMode) > 1 { // Socket path specified, try to use existing VPNKit instance h.VPNKitSock = netMode[1] + if len(netMode) > 2 { + vpnkitPortSocket = netMode[2] + } } else { // Start new VPNKit instance h.VPNKitSock = filepath.Join(*state, "vpnkit_eth.sock") - vpnKitPortSocket := filepath.Join(*state, "vpnkit_port.sock") + vpnkitPortSocket = filepath.Join(*state, "vpnkit_port.sock") vsockSocket := filepath.Join(*state, "connect") - vpnKitProcess, err = launchVPNKit(h.VPNKitSock, vsockSocket, vpnKitPortSocket) + vpnkitProcess, err = launchVPNKit(h.VPNKitSock, vsockSocket, vpnkitPortSocket) if err != nil { log.Fatalln("Unable to start vpnkit: ", err) } defer func() { - if vpnKitProcess != nil { - err := vpnKitProcess.Kill() + if vpnkitProcess != nil { + err := vpnkitProcess.Kill() if err != nil { log.Println(err) } } }() // The guest will use this 9P mount to configure which ports to forward - h.Sockets9P = []hyperkit.Socket9P{{Path: vpnKitPortSocket, Tag: "port"}} + h.Sockets9P = []hyperkit.Socket9P{{Path: vpnkitPortSocket, Tag: "port"}} // VSOCK port 62373 is used to pass traffic from host->guest h.VSockPorts = append(h.VSockPorts, 62373) } @@ -265,7 +278,7 @@ func runHyperKit(args []string) { h.CPUs = *cpus h.Memory = *mem - h.VPNKitUUID = *vpnKitUUID + h.VPNKitUUID = *vpnkitUUID if *ipStr != "" { if ip := net.ParseIP(*ipStr); len(ip) > 0 && ip.To4() != nil { h.VPNKitPreferredIPv4 = ip.String() @@ -274,6 +287,23 @@ func runHyperKit(args []string) { } } + // Publish ports if requested and VPNKit is used + if len(publishFlags) != 0 { + switch netMode[0] { + case hyperkitNetworkingDockerForMac, hyperkitNetworkingVPNKit: + if vpnkitPortSocket == "" { + log.Fatalf("The VPNKit Port socket path is required to publish ports") + } + f, err := vpnkitPublishPorts(h, publishFlags, vpnkitPortSocket) + if err != nil { + log.Fatalf("Publish ports failed with: %v", err) + } + defer f() + default: + log.Fatalf("Port publishing requires %q or %q networking mode", hyperkitNetworkingDockerForMac, hyperkitNetworkingVPNKit) + } + } + err = h.Run(string(cmdline)) if err != nil { log.Fatalf("Cannot run hyperkit: %v", err) @@ -300,7 +330,7 @@ func createListenSocket(path string) (*os.File, error) { func launchVPNKit(etherSock string, vsockSock string, portSock string) (*os.Process, error) { var err error - vpnKitPath, err := exec.LookPath("vpnkit") + vpnkitPath, err := exec.LookPath("vpnkit") if err != nil { return nil, fmt.Errorf("Unable to find vpnkit binary") } @@ -315,7 +345,7 @@ func launchVPNKit(etherSock string, vsockSock string, portSock string) (*os.Proc return nil, err } - cmd := exec.Command(vpnKitPath, + cmd := exec.Command(vpnkitPath, "--ethernet", "fd:3", "--vsock-path", vsockSock, "--port", "fd:4") @@ -329,6 +359,7 @@ func launchVPNKit(etherSock string, vsockSock string, portSock string) (*os.Proc cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr + log.Debugf("Starting vpnkit: %v", cmd.Args) if err := cmd.Start(); err != nil { return nil, err } @@ -337,3 +368,78 @@ func launchVPNKit(etherSock string, vsockSock string, portSock string) (*os.Proc return cmd.Process, nil } + +// vpnkitPublishPorts instructs VPNKit to expose ports from the VM on localhost +// Pre-register the VM with VPNKit using the UUID. This gives the IP +// address (if not specified) allowing us to publish ports. It returns +// a function which should be called to clean up once the VM stops. +func vpnkitPublishPorts(h *hyperkit.HyperKit, publishFlags multipleFlag, portSocket string) (func(), error) { + ctx := context.Background() + + vpnkitUUID, err := uuid.Parse(h.VPNKitUUID) + if err != nil { + return nil, fmt.Errorf("Failed to parse VPNKit UUID %s: %v", h.VPNKitUUID, err) + } + + localhost := net.ParseIP("127.0.0.1") + if localhost == nil { + return nil, fmt.Errorf("Failed to parse 127.0.0.1") + } + + log.Debugf("Creating new VPNKit VMNet on %s", h.VPNKitSock) + vmnet, err := vpnkit.NewVmnet(ctx, h.VPNKitSock) + if err != nil { + return nil, fmt.Errorf("NewVmnet failed: %v", err) + } + defer vmnet.Close() + + // Register with VPNKit + var vif *vpnkit.Vif + if h.VPNKitPreferredIPv4 == "" { + log.Debugf("Creating VPNKit VIF for %v", vpnkitUUID) + vif, err = vmnet.ConnectVif(vpnkitUUID) + if err != nil { + return nil, fmt.Errorf("Connection to Vif failed: %v", err) + } + } else { + ip := net.ParseIP(h.VPNKitPreferredIPv4) + if ip == nil { + return nil, fmt.Errorf("Failed to parse IP: %s", h.VPNKitPreferredIPv4) + } + log.Debugf("Creating VPNKit VIF for %v ip=%v", vpnkitUUID, ip) + vif, err = vmnet.ConnectVifIP(vpnkitUUID, ip) + if err != nil { + return nil, fmt.Errorf("Connection to Vif with IP failed: %v", err) + } + } + log.Debugf("VPNKit UUID:%s IP: %v", vpnkitUUID, vif.IP) + + log.Debugf("Connecting to VPNKit on %s", portSocket) + c, err := vpnkit.NewConnection(context.Background(), portSocket) + if err != nil { + return nil, fmt.Errorf("Connection to VPNKit failed: %v", err) + } + + // Publish ports + var ports []*vpnkit.Port + for _, publish := range publishFlags { + p, err := NewPublishedPort(publish) + if err != nil { + return nil, fmt.Errorf("Failed to parse port publish %s: %v", publish, err) + } + + log.Debugf("Publishing %s", publish) + vp := vpnkit.NewPort(c, p.Protocol, localhost, p.Host, vif.IP, p.Guest) + if err = vp.Expose(context.Background()); err != nil { + return nil, fmt.Errorf("Failed to expose port %s: %v", publish, err) + } + ports = append(ports, vp) + } + + // Return cleanup function + return func() { + for _, vp := range ports { + vp.Unexpose(context.Background()) + } + }, nil +} From c45179dce858a3497bd7a5635fd7284daf37eb5b Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Thu, 9 Nov 2017 14:39:30 +0000 Subject: [PATCH 06/11] tool/hyperkit: Rename uuid.vpnkit to vpnkit.uuid Other vpnkit related files start with vpnkit too. Signed-off-by: Rolf Neugebauer --- src/cmd/linuxkit/run_hyperkit.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmd/linuxkit/run_hyperkit.go b/src/cmd/linuxkit/run_hyperkit.go index 1eddd79c4..35a0d7deb 100644 --- a/src/cmd/linuxkit/run_hyperkit.go +++ b/src/cmd/linuxkit/run_hyperkit.go @@ -48,7 +48,7 @@ func runHyperKit(args []string) { vsockports := flags.String("vsock-ports", "", "List of vsock ports to forward from the guest on startup (comma separated). A unix domain socket for each port will be created in the state directory") networking := flags.String("networking", hyperkitNetworkingDefault, "Networking mode. Valid options are 'default', 'docker-for-mac', 'vpnkit[,eth-socket-path[,port-socket-path]]', 'vmnet' and 'none'. 'docker-for-mac' connects to the network used by Docker for Mac. 'vpnkit' connects to the VPNKit socket(s) specified. If no socket path is provided a new VPNKit instance will be started and 'vpnkit_eth.sock' and 'vpnkit_port.sock' will be created in the state directory. 'port-socket-path' is only needed if you want to publish ports on localhost using an existing VPNKit instance. 'vmnet' uses the Apple vmnet framework, requires root/sudo. 'none' disables networking.`") - vpnkitUUID := flags.String("vpnkit-uuid", "", "Optional UUID used to identify the VPNKit connection. Overrides 'uuid.vpnkit' in the state directory.") + vpnkitUUID := flags.String("vpnkit-uuid", "", "Optional UUID used to identify the VPNKit connection. Overrides 'vpnkit.uuid' in the state directory.") publishFlags := multipleFlag{} flags.Var(&publishFlags, "publish", "Publish a vm's port(s) to the host (default [])") @@ -159,7 +159,7 @@ func runHyperKit(args []string) { // assigned to the UUID, so to get the same IP we have to store the initial UUID. If // has specified a VPNKit UUID the file is ignored. if *vpnkitUUID == "" { - vpnkitUUIDFile := filepath.Join(*state, "uuid.vpnkit") + vpnkitUUIDFile := filepath.Join(*state, "vpnkit.uuid") if _, err := os.Stat(vpnkitUUIDFile); os.IsNotExist(err) { *vpnkitUUID = uuid.New().String() if err := ioutil.WriteFile(vpnkitUUIDFile, []byte(*vpnkitUUID), 0600); err != nil { From 638f15e1f81c62059363b97fef1b4d7845229618 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Thu, 9 Nov 2017 14:44:15 +0000 Subject: [PATCH 07/11] tool/qemu: Switch to use github.com/google/uuid With the hyperkit backend switched to this UUID package switch qemu as well. Signed-off-by: Rolf Neugebauer --- src/cmd/linuxkit/run_qemu.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmd/linuxkit/run_qemu.go b/src/cmd/linuxkit/run_qemu.go index 859715f53..5510c3042 100644 --- a/src/cmd/linuxkit/run_qemu.go +++ b/src/cmd/linuxkit/run_qemu.go @@ -13,7 +13,7 @@ import ( "strconv" "strings" - "github.com/satori/go.uuid" + "github.com/google/uuid" log "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh/terminal" ) @@ -164,7 +164,7 @@ func runQemu(args []string) { qemuContainerized := flags.Bool("containerized", false, "Run qemu in a container") // Generate UUID, so that /sys/class/dmi/id/product_uuid is populated - vmUUID := uuid.NewV4() + vmUUID := uuid.New() // Networking networking := flags.String("networking", qemuNetworkingDefault, "Networking mode. Valid options are 'default', 'user', 'bridge[,name]', tap[,name] and 'none'. 'user' uses QEMUs userspace networking. 'bridge' connects to a preexisting bridge. 'tap' uses a prexisting tap device. 'none' disables networking.`") From bc84fab42fb230a3dc6c1ea03c3a904cfdaff338 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Thu, 9 Nov 2017 14:47:30 +0000 Subject: [PATCH 08/11] vendor: remove github.com/satori/go.uuid With the switch to github.com/satori/go.uuid this is no longer needed. Strangely, it was not in vendor.conf... Signed-off-by: Rolf Neugebauer --- .../vendor/github.com/satori/go.uuid/LICENSE | 20 - .../github.com/satori/go.uuid/README.md | 65 --- .../vendor/github.com/satori/go.uuid/uuid.go | 481 ------------------ 3 files changed, 566 deletions(-) delete mode 100644 src/cmd/linuxkit/vendor/github.com/satori/go.uuid/LICENSE delete mode 100644 src/cmd/linuxkit/vendor/github.com/satori/go.uuid/README.md delete mode 100644 src/cmd/linuxkit/vendor/github.com/satori/go.uuid/uuid.go diff --git a/src/cmd/linuxkit/vendor/github.com/satori/go.uuid/LICENSE b/src/cmd/linuxkit/vendor/github.com/satori/go.uuid/LICENSE deleted file mode 100644 index 488357b8a..000000000 --- a/src/cmd/linuxkit/vendor/github.com/satori/go.uuid/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (C) 2013-2016 by Maxim Bublis - -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. diff --git a/src/cmd/linuxkit/vendor/github.com/satori/go.uuid/README.md b/src/cmd/linuxkit/vendor/github.com/satori/go.uuid/README.md deleted file mode 100644 index b6aad1c81..000000000 --- a/src/cmd/linuxkit/vendor/github.com/satori/go.uuid/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# UUID package for Go language - -[![Build Status](https://travis-ci.org/satori/go.uuid.png?branch=master)](https://travis-ci.org/satori/go.uuid) -[![Coverage Status](https://coveralls.io/repos/github/satori/go.uuid/badge.svg?branch=master)](https://coveralls.io/github/satori/go.uuid) -[![GoDoc](http://godoc.org/github.com/satori/go.uuid?status.png)](http://godoc.org/github.com/satori/go.uuid) - -This package provides pure Go implementation of Universally Unique Identifier (UUID). Supported both creation and parsing of UUIDs. - -With 100% test coverage and benchmarks out of box. - -Supported versions: -* Version 1, based on timestamp and MAC address (RFC 4122) -* Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1) -* Version 3, based on MD5 hashing (RFC 4122) -* Version 4, based on random numbers (RFC 4122) -* Version 5, based on SHA-1 hashing (RFC 4122) - -## Installation - -Use the `go` command: - - $ go get github.com/satori/go.uuid - -## Requirements - -UUID package requires Go >= 1.2. - -## Example - -```go -package main - -import ( - "fmt" - "github.com/satori/go.uuid" -) - -func main() { - // Creating UUID Version 4 - u1 := uuid.NewV4() - fmt.Printf("UUIDv4: %s\n", u1) - - // Parsing UUID from string input - u2, err := uuid.FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") - if err != nil { - fmt.Printf("Something gone wrong: %s", err) - } - fmt.Printf("Successfully parsed: %s", u2) -} -``` - -## Documentation - -[Documentation](http://godoc.org/github.com/satori/go.uuid) is hosted at GoDoc project. - -## Links -* [RFC 4122](http://tools.ietf.org/html/rfc4122) -* [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01) - -## Copyright - -Copyright (C) 2013-2016 by Maxim Bublis . - -UUID package released under MIT License. -See [LICENSE](https://github.com/satori/go.uuid/blob/master/LICENSE) for details. diff --git a/src/cmd/linuxkit/vendor/github.com/satori/go.uuid/uuid.go b/src/cmd/linuxkit/vendor/github.com/satori/go.uuid/uuid.go deleted file mode 100644 index 295f3fc2c..000000000 --- a/src/cmd/linuxkit/vendor/github.com/satori/go.uuid/uuid.go +++ /dev/null @@ -1,481 +0,0 @@ -// Copyright (C) 2013-2015 by Maxim Bublis -// -// 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. - -// Package uuid provides implementation of Universally Unique Identifier (UUID). -// Supported versions are 1, 3, 4 and 5 (as specified in RFC 4122) and -// version 2 (as specified in DCE 1.1). -package uuid - -import ( - "bytes" - "crypto/md5" - "crypto/rand" - "crypto/sha1" - "database/sql/driver" - "encoding/binary" - "encoding/hex" - "fmt" - "hash" - "net" - "os" - "sync" - "time" -) - -// UUID layout variants. -const ( - VariantNCS = iota - VariantRFC4122 - VariantMicrosoft - VariantFuture -) - -// UUID DCE domains. -const ( - DomainPerson = iota - DomainGroup - DomainOrg -) - -// Difference in 100-nanosecond intervals between -// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). -const epochStart = 122192928000000000 - -// Used in string method conversion -const dash byte = '-' - -// UUID v1/v2 storage. -var ( - storageMutex sync.Mutex - storageOnce sync.Once - epochFunc = unixTimeFunc - clockSequence uint16 - lastTime uint64 - hardwareAddr [6]byte - posixUID = uint32(os.Getuid()) - posixGID = uint32(os.Getgid()) -) - -// String parse helpers. -var ( - urnPrefix = []byte("urn:uuid:") - byteGroups = []int{8, 4, 4, 4, 12} -) - -func initClockSequence() { - buf := make([]byte, 2) - safeRandom(buf) - clockSequence = binary.BigEndian.Uint16(buf) -} - -func initHardwareAddr() { - interfaces, err := net.Interfaces() - if err == nil { - for _, iface := range interfaces { - if len(iface.HardwareAddr) >= 6 { - copy(hardwareAddr[:], iface.HardwareAddr) - return - } - } - } - - // Initialize hardwareAddr randomly in case - // of real network interfaces absence - safeRandom(hardwareAddr[:]) - - // Set multicast bit as recommended in RFC 4122 - hardwareAddr[0] |= 0x01 -} - -func initStorage() { - initClockSequence() - initHardwareAddr() -} - -func safeRandom(dest []byte) { - if _, err := rand.Read(dest); err != nil { - panic(err) - } -} - -// Returns difference in 100-nanosecond intervals between -// UUID epoch (October 15, 1582) and current time. -// This is default epoch calculation function. -func unixTimeFunc() uint64 { - return epochStart + uint64(time.Now().UnixNano()/100) -} - -// UUID representation compliant with specification -// described in RFC 4122. -type UUID [16]byte - -// NullUUID can be used with the standard sql package to represent a -// UUID value that can be NULL in the database -type NullUUID struct { - UUID UUID - Valid bool -} - -// The nil UUID is special form of UUID that is specified to have all -// 128 bits set to zero. -var Nil = UUID{} - -// Predefined namespace UUIDs. -var ( - NamespaceDNS, _ = FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") - NamespaceURL, _ = FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8") - NamespaceOID, _ = FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8") - NamespaceX500, _ = FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8") -) - -// And returns result of binary AND of two UUIDs. -func And(u1 UUID, u2 UUID) UUID { - u := UUID{} - for i := 0; i < 16; i++ { - u[i] = u1[i] & u2[i] - } - return u -} - -// Or returns result of binary OR of two UUIDs. -func Or(u1 UUID, u2 UUID) UUID { - u := UUID{} - for i := 0; i < 16; i++ { - u[i] = u1[i] | u2[i] - } - return u -} - -// Equal returns true if u1 and u2 equals, otherwise returns false. -func Equal(u1 UUID, u2 UUID) bool { - return bytes.Equal(u1[:], u2[:]) -} - -// Version returns algorithm version used to generate UUID. -func (u UUID) Version() uint { - return uint(u[6] >> 4) -} - -// Variant returns UUID layout variant. -func (u UUID) Variant() uint { - switch { - case (u[8] & 0x80) == 0x00: - return VariantNCS - case (u[8]&0xc0)|0x80 == 0x80: - return VariantRFC4122 - case (u[8]&0xe0)|0xc0 == 0xc0: - return VariantMicrosoft - } - return VariantFuture -} - -// Bytes returns bytes slice representation of UUID. -func (u UUID) Bytes() []byte { - return u[:] -} - -// Returns canonical string representation of UUID: -// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. -func (u UUID) String() string { - buf := make([]byte, 36) - - hex.Encode(buf[0:8], u[0:4]) - buf[8] = dash - hex.Encode(buf[9:13], u[4:6]) - buf[13] = dash - hex.Encode(buf[14:18], u[6:8]) - buf[18] = dash - hex.Encode(buf[19:23], u[8:10]) - buf[23] = dash - hex.Encode(buf[24:], u[10:]) - - return string(buf) -} - -// SetVersion sets version bits. -func (u *UUID) SetVersion(v byte) { - u[6] = (u[6] & 0x0f) | (v << 4) -} - -// SetVariant sets variant bits as described in RFC 4122. -func (u *UUID) SetVariant() { - u[8] = (u[8] & 0xbf) | 0x80 -} - -// MarshalText implements the encoding.TextMarshaler interface. -// The encoding is the same as returned by String. -func (u UUID) MarshalText() (text []byte, err error) { - text = []byte(u.String()) - return -} - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -// Following formats are supported: -// "6ba7b810-9dad-11d1-80b4-00c04fd430c8", -// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", -// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" -func (u *UUID) UnmarshalText(text []byte) (err error) { - if len(text) < 32 { - err = fmt.Errorf("uuid: UUID string too short: %s", text) - return - } - - t := text[:] - braced := false - - if bytes.Equal(t[:9], urnPrefix) { - t = t[9:] - } else if t[0] == '{' { - braced = true - t = t[1:] - } - - b := u[:] - - for i, byteGroup := range byteGroups { - if i > 0 { - if t[0] != '-' { - err = fmt.Errorf("uuid: invalid string format") - return - } - t = t[1:] - } - - if len(t) < byteGroup { - err = fmt.Errorf("uuid: UUID string too short: %s", text) - return - } - - if i == 4 && len(t) > byteGroup && - ((braced && t[byteGroup] != '}') || len(t[byteGroup:]) > 1 || !braced) { - err = fmt.Errorf("uuid: UUID string too long: %s", text) - return - } - - _, err = hex.Decode(b[:byteGroup/2], t[:byteGroup]) - if err != nil { - return - } - - t = t[byteGroup:] - b = b[byteGroup/2:] - } - - return -} - -// MarshalBinary implements the encoding.BinaryMarshaler interface. -func (u UUID) MarshalBinary() (data []byte, err error) { - data = u.Bytes() - return -} - -// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. -// It will return error if the slice isn't 16 bytes long. -func (u *UUID) UnmarshalBinary(data []byte) (err error) { - if len(data) != 16 { - err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data)) - return - } - copy(u[:], data) - - return -} - -// Value implements the driver.Valuer interface. -func (u UUID) Value() (driver.Value, error) { - return u.String(), nil -} - -// Scan implements the sql.Scanner interface. -// A 16-byte slice is handled by UnmarshalBinary, while -// a longer byte slice or a string is handled by UnmarshalText. -func (u *UUID) Scan(src interface{}) error { - switch src := src.(type) { - case []byte: - if len(src) == 16 { - return u.UnmarshalBinary(src) - } - return u.UnmarshalText(src) - - case string: - return u.UnmarshalText([]byte(src)) - } - - return fmt.Errorf("uuid: cannot convert %T to UUID", src) -} - -// Value implements the driver.Valuer interface. -func (u NullUUID) Value() (driver.Value, error) { - if !u.Valid { - return nil, nil - } - // Delegate to UUID Value function - return u.UUID.Value() -} - -// Scan implements the sql.Scanner interface. -func (u *NullUUID) Scan(src interface{}) error { - if src == nil { - u.UUID, u.Valid = Nil, false - return nil - } - - // Delegate to UUID Scan function - u.Valid = true - return u.UUID.Scan(src) -} - -// FromBytes returns UUID converted from raw byte slice input. -// It will return error if the slice isn't 16 bytes long. -func FromBytes(input []byte) (u UUID, err error) { - err = u.UnmarshalBinary(input) - return -} - -// FromBytesOrNil returns UUID converted from raw byte slice input. -// Same behavior as FromBytes, but returns a Nil UUID on error. -func FromBytesOrNil(input []byte) UUID { - uuid, err := FromBytes(input) - if err != nil { - return Nil - } - return uuid -} - -// FromString returns UUID parsed from string input. -// Input is expected in a form accepted by UnmarshalText. -func FromString(input string) (u UUID, err error) { - err = u.UnmarshalText([]byte(input)) - return -} - -// FromStringOrNil returns UUID parsed from string input. -// Same behavior as FromString, but returns a Nil UUID on error. -func FromStringOrNil(input string) UUID { - uuid, err := FromString(input) - if err != nil { - return Nil - } - return uuid -} - -// Returns UUID v1/v2 storage state. -// Returns epoch timestamp, clock sequence, and hardware address. -func getStorage() (uint64, uint16, []byte) { - storageOnce.Do(initStorage) - - storageMutex.Lock() - defer storageMutex.Unlock() - - timeNow := epochFunc() - // Clock changed backwards since last UUID generation. - // Should increase clock sequence. - if timeNow <= lastTime { - clockSequence++ - } - lastTime = timeNow - - return timeNow, clockSequence, hardwareAddr[:] -} - -// NewV1 returns UUID based on current timestamp and MAC address. -func NewV1() UUID { - u := UUID{} - - timeNow, clockSeq, hardwareAddr := getStorage() - - binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) - binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) - binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) - binary.BigEndian.PutUint16(u[8:], clockSeq) - - copy(u[10:], hardwareAddr) - - u.SetVersion(1) - u.SetVariant() - - return u -} - -// NewV2 returns DCE Security UUID based on POSIX UID/GID. -func NewV2(domain byte) UUID { - u := UUID{} - - timeNow, clockSeq, hardwareAddr := getStorage() - - switch domain { - case DomainPerson: - binary.BigEndian.PutUint32(u[0:], posixUID) - case DomainGroup: - binary.BigEndian.PutUint32(u[0:], posixGID) - } - - binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) - binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) - binary.BigEndian.PutUint16(u[8:], clockSeq) - u[9] = domain - - copy(u[10:], hardwareAddr) - - u.SetVersion(2) - u.SetVariant() - - return u -} - -// NewV3 returns UUID based on MD5 hash of namespace UUID and name. -func NewV3(ns UUID, name string) UUID { - u := newFromHash(md5.New(), ns, name) - u.SetVersion(3) - u.SetVariant() - - return u -} - -// NewV4 returns random generated UUID. -func NewV4() UUID { - u := UUID{} - safeRandom(u[:]) - u.SetVersion(4) - u.SetVariant() - - return u -} - -// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. -func NewV5(ns UUID, name string) UUID { - u := newFromHash(sha1.New(), ns, name) - u.SetVersion(5) - u.SetVariant() - - return u -} - -// Returns UUID based on hashing of namespace UUID and name. -func newFromHash(h hash.Hash, ns UUID, name string) UUID { - u := UUID{} - h.Write(ns[:]) - h.Write([]byte(name)) - copy(u[:], h.Sum(nil)) - - return u -} From d29cc52be46862f36c85530e0b54e914b2c36958 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Thu, 9 Nov 2017 15:49:00 +0000 Subject: [PATCH 09/11] examples: Fix the vpnkit-forwarder example The 'vpnkit-forwarder' packages lacked the vpnkit-expose-port which was removed in f48a2bfe12eb ("pkg: update vpnkit-forwarder"). It is not longer needed as the vpnkit-expose-port functionality was integrated into 'linuskit run' Signed-off-by: Rolf Neugebauer --- examples/vpnkit-forwarder.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/examples/vpnkit-forwarder.yml b/examples/vpnkit-forwarder.yml index 15c78e7dd..f2664e9f5 100644 --- a/examples/vpnkit-forwarder.yml +++ b/examples/vpnkit-forwarder.yml @@ -26,14 +26,6 @@ services: - /var/vpnkit:/port net: host command: ["/vpnkit-forwarder"] - - name: vpnkit-expose-port - image: linuxkit/vpnkit-forwarder:c7e61d9250de0b21455dc5c8bb885bd8faa31621 - net: none - binds: - - /var/vpnkit:/port - command: ["/vpnkit-expose-port","-i", - "-host-ip","127.0.0.1","-host-port","22", - "-container-ip","127.0.0.1","-container-port","22","-no-local-ip"] files: - path: root/.ssh/authorized_keys From a9e6f37958f48b1ff20c0cce76ef84841d398926 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Thu, 9 Nov 2017 16:02:06 +0000 Subject: [PATCH 10/11] doc: Update networking section of platform-hyperkit.md Signed-off-by: Rolf Neugebauer --- docs/platform-hyperkit.md | 95 +++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 28 deletions(-) diff --git a/docs/platform-hyperkit.md b/docs/platform-hyperkit.md index 32a5c9446..3e8535441 100644 --- a/docs/platform-hyperkit.md +++ b/docs/platform-hyperkit.md @@ -62,29 +62,40 @@ this option palatable, and provide alternative options to access the VMs over the network below. -### Accessing services +### Accessing network services -The simplest way to access networking services exposed by a LinuxKit VM is to use a Docker for Mac container. +`hyperkit` offers a number of ways for accessing network services +running inside the LinuxKit VM from the host. These depend on the +networking mode selected via `-networking`. The default mode is +`docker-for-mac` where the same VPNkit instance is shared between +LinuxKit VMs and the VM running as part of Docker for Mac. + + +#### Access from the Docker for Mac VM (`-networking docker-for-mac`) + +The simplest way to access networking services exposed by a LinuxKit +VM is to use a Docker for Mac container. For example, to access an ssh +server in a LinuxKit VM, create a ssh client container from: -For example, to access an ssh server in a LinuxKit VM, create a ssh client container from: ``` FROM alpine:edge RUN apk add --no-cache openssh-client ``` + and then run + ``` docker build -t ssh . docker run --rm -ti -v ~/.ssh:/root/.ssh ssh ssh ``` -### Forwarding ports to the host +#### Forwarding ports with `socat` (`-networking docker-for-mac`) -Ports can be forwarded to the host using a container with `socat` or with VPNKit which comes with Docker for Mac. +A `socat` container on Docker for Mac can be used to proxy between the +LinuxKit VM's ports and localhost. For example, to expose the redis +port from the [RedisOS example](../examples/redis-os.yml), use this +Dockerfile: -#### Port forwarding with `socat` -A `socat` container can be used to proxy between the LinuxKit VM's ports and -localhost. For example, to expose the redis port from the [RedisOS -example](../examples/redis-os.yml), use this Dockerfile: ``` FROM alpine:edge RUN apk add --no-cache socat @@ -96,29 +107,57 @@ docker build -t socat . docker run --rm -t -d -p 6379:6379 socat tcp-listen:6379,reuseaddr,fork tcp::6379 ``` -#### Port forwarding with VPNKit +#### Port forwarding with VPNKit (`-networking docker-for-mac`) -VPNKit has the general tooling to expose any guest VM port on the host (just -like it does with containers in Docker for Mac). To enable forwarding, a -`vpnkit-forwarder` container must be running in the VM. The VM also has to be -booted with `linuxkit run hyperkit -networking=vpnkit`. +There is **experimental** support for exposing selected ports of the +guest on `localhost` using the `-publish` command line option. For +example, using `-publish 2222:22/tcp` exposes the guest TCP port 22 on +localhost on port 2222. Multiple `-publish` options can be +specified. For example, the image build from the [`sshd +example`](../examples/sshd.yml) can be started with: -VPNKit uses a 9P mount in `/port` for coordination between the components. -Port forwarding can be manually set up by creating new directories in `/port` -or by using the `vpnkit-expose-port` tool. More details about the forwarding -mechanism is available in the [VPNKit +``` +linuxkit run -publish 2222:22/tcp sshd +``` + +and then you can log into the LinuxKit VM with `ssh -p 2222 +root@localhost`. + +Note, this mode is **experimental** and may cause the VPNKit instance +shared with Docker for Mac being confused about which ports are +currently in use, in particular if the LinuxKit VM does not exit +gracefully. This can typically be fixed by restarting Docker for Mac. + + +#### Port forwarding with VPNKit (`-networking vpnkit`) + +An alternative to the previous method is to start your own copy of +`vpnkit` (or connect to an already running instance). This can be done +using the `-networking vpnkit` command line option. + +VPNKit uses a 9P mount in `/port` for coordination between +components. The first VM on a VPNKit instance currently needs mount +the 9P filesystem and also needs to run the `vpnkit-forwarder` service +to enable port forwarding to localhost. A full example with `vpnkit` +forwarding of `sshd` is available in +[examples/vpnkit-forwarder.yml](/examples/vpnkit-forwarder.yml). + +To run this example with its own instance of VPNKit, use: + +``` +linuxkit run -networking vpnkit -publish 2222:22/tcp vpnkit-forwarder +``` + +You can then access it via: + +``` +ssh -p 2222 root@localhost +``` + +More details about the VPNKit forwarding mechanism is available in the +[VPNKit documentation](https://github.com/moby/vpnkit/blob/master/docs/ports.md#signalling-from-the-vm-to-the-host). -To get started, the easiest solution at the moment is to use the -`vpnkit-expose-port` command to tell the forwarder and `vpnkit` which ports to -forward. This process requires fewer privileges than `vpnkit-forwarder` and can -be run in a container without networking. - -A full example with `vpnkit` forwarding of `sshd` is available in [examples/vpnkit-forwarder.yml](/examples/vpnkit-forwarder.yml). - -After building and running the example you should be able to connect to ssh on port 22 on -localhost. The port can also be exposed externally by changing the host IP in -the example to 0.0.0.0. ## Integration services and Metadata From 4b9bf071e53a284a15512b6621ce336ae2fc6ae6 Mon Sep 17 00:00:00 2001 From: Rolf Neugebauer Date: Thu, 9 Nov 2017 17:53:28 +0000 Subject: [PATCH 11/11] Update hyperkit vendoring This includes using ftruncate for speedier disk creation Signed-off-by: Rolf Neugebauer --- src/cmd/linuxkit/vendor.conf | 2 +- .../github.com/moby/hyperkit/go/hyperkit.go | 6 +- .../src/include/xhyve/firmware/bootrom.h | 1 + .../moby/hyperkit/src/include/xhyve/fwctl.h | 54 ++ .../moby/hyperkit/src/lib/block_if.c | 100 +++- .../github.com/moby/hyperkit/src/lib/fwctl.c | 560 ++++++++++++++++++ .../moby/hyperkit/src/lib/pci_ahci.c | 13 +- 7 files changed, 715 insertions(+), 21 deletions(-) create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/include/xhyve/fwctl.h create mode 100644 src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/fwctl.c diff --git a/src/cmd/linuxkit/vendor.conf b/src/cmd/linuxkit/vendor.conf index 50791623b..6e3480bfc 100644 --- a/src/cmd/linuxkit/vendor.conf +++ b/src/cmd/linuxkit/vendor.conf @@ -15,7 +15,7 @@ github.com/gophercloud/gophercloud 2804b72cf099b41d2e25c8afcca786f9f962ddee github.com/jmespath/go-jmespath bd40a432e4c76585ef6b72d3fd96fb9b6dc7b68d github.com/mitchellh/go-ps 4fdf99ab29366514c69ccccddab5dc58b8d84062 github.com/moby/datakit 97b3d230535397a813323902c23751e176481a86 -github.com/moby/hyperkit 3e31617ae866c93925e2b3bc5d8006b60985e920 +github.com/moby/hyperkit a12cd7250bcd8d689078e3e42ae4a7cf6a0cbaf3 github.com/moby/vpnkit 0e4293bb1058598c4b0a406ed171f52573ef414c github.com/packethost/packngo 131798f2804a1b3e895ca98047d56f0d7e094e2a github.com/pmezard/go-difflib v1.0.0 diff --git a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/go/hyperkit.go b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/go/hyperkit.go index db0a9b5aa..9cb38b1a2 100644 --- a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/go/hyperkit.go +++ b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/go/hyperkit.go @@ -395,11 +395,7 @@ func CreateDiskImage(location string, sizeMB int) error { } defer f.Close() - buf := make([]byte, 1048676) - for i := 0; i < sizeMB; i++ { - f.Write(buf) - } - return nil + return f.Truncate(int64(sizeMB) * int64(1024) * int64(1024)) } func intArrayToString(i []int, sep string) string { diff --git a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/include/xhyve/firmware/bootrom.h b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/include/xhyve/firmware/bootrom.h index 7c4c82261..170c78d06 100644 --- a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/include/xhyve/firmware/bootrom.h +++ b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/include/xhyve/firmware/bootrom.h @@ -4,5 +4,6 @@ #include int bootrom_init(const char *bootrom_path); +const char *bootrom(void); uint64_t bootrom_load(void); bool bootrom_contains_gpa(uint64_t gpa); diff --git a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/include/xhyve/fwctl.h b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/include/xhyve/fwctl.h new file mode 100644 index 000000000..b45a4dbe4 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/include/xhyve/fwctl.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2015 Peter Grehan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _FWCTL_H_ +#define _FWCTL_H_ + +#include + +/* + * Linker set api for export of information to guest firmware via + * a sysctl-like OID interface + */ +struct ctl { + const char *c_oid; + const void *c_data; + const int c_len; +}; + +#define CTL_NODE(oid, data, len) \ + static struct ctl __CONCAT(__ctl, __LINE__) = { \ + oid, \ + (data), \ + (len), \ + }; \ + DATA_SET(ctl_set, __CONCAT(__ctl, __LINE__)) + +void fwctl_init(void); + +#endif /* _FWCTL_H_ */ diff --git a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/block_if.c b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/block_if.c index 07a3e6efc..cce6ef185 100644 --- a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/block_if.c +++ b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/block_if.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -53,6 +54,14 @@ #define BLOCKIF_SIG 0xb109b109 +#ifndef ALIGNUP +#define ALIGNUP(x, a) (((x - 1) & ~(a - 1)) + a) +#endif + +#ifndef ALIGNDOWN +#define ALIGNDOWN(x, a) (-(a) & (x)) +#endif + /* xhyve: FIXME * * // #define BLOCKIF_NUMTHR 8 @@ -101,6 +110,8 @@ struct blockif_ctxt { off_t bc_size; int bc_sectsz; int bc_psectsz; + size_t bc_delete_alignment; /* f_bsize always a power of 2 */ + void* bc_delete_zero_buf; int bc_psectoff; int bc_closing; pthread_t bc_btid[BLOCKIF_NUMTHR]; @@ -211,6 +222,7 @@ static int block_delete(struct blockif_ctxt *bc, off_t offset, off_t len) { int ret = -1; + #ifdef __FreeBSD__ off_t arg[2] = { offset, len }; #endif @@ -221,22 +233,52 @@ block_delete(struct blockif_ctxt *bc, off_t offset, off_t len) errno = EOPNOTSUPP; else if (bc->bc_rdonly) errno = EROFS; - if (bc->bc_fd >= 0) { - if (bc->bc_ischr) { -#ifdef __FreeBSD__ - ret = ioctl(bc->bc_fd, DIOCGDELETE, arg); -#else - errno = EOPNOTSUPP; -#endif - } else - errno = EOPNOTSUPP; #ifdef HAVE_OCAML_QCOW - } else if (bc->bc_mbh >= 0) { + if (bc->bc_mbh >= 0) { ret = mirage_block_delete(bc->bc_mbh, offset, len); + goto out; + } #endif - } else - abort(); +#ifdef __FreeBSD__ + if ((bc->bc_fd >= 0) && (bc->bc_ischr)) { + ret = ioctl(bc->bc_fd, DIOCGDELETE, arg); + errno = EOPNOTSUPP; + goto out; + } +#elif __APPLE__ + if (bc->bc_fd >= 0) { + /* PUNCHHOLE lengths and offsets have to be aligned so explicitly write zeroes + into the unaligned parts at the beginning and the end. This wouldn't be necessary + if the host and guest had the same sector size */ + assert (offset >= 0); + assert (len >= 0); + size_t fp_offset = (size_t) offset; + size_t fp_length = (size_t) len; + size_t aligned_offset = ALIGNUP(fp_offset, bc->bc_delete_alignment); + if (aligned_offset != fp_offset) { + size_t len_to_zero = aligned_offset - fp_offset; + assert(len_to_zero <= bc->bc_delete_alignment); + ssize_t written = pwrite(bc->bc_fd, bc->bc_delete_zero_buf, len_to_zero, (off_t) fp_offset); + if (written == -1) goto out; + fp_offset += len_to_zero; + fp_length -= len_to_zero; + } + size_t aligned_length = ALIGNDOWN(fp_length, bc->bc_delete_alignment); + if (aligned_length != fp_length) { + size_t len_to_zero = fp_length - aligned_length; + assert(len_to_zero <= bc->bc_delete_alignment); + fp_length -= len_to_zero; + ssize_t written = pwrite(bc->bc_fd, bc->bc_delete_zero_buf, len_to_zero, (off_t) (fp_offset + fp_length)); + if (written == -1) goto out; + } + struct fpunchhole arg = { .fp_flags = 0, .reserved = 0, .fp_offset = (off_t) fp_offset, .fp_length = (off_t) fp_length }; + ret = fcntl(bc->bc_fd, F_PUNCHHOLE, &arg); + goto out; + } +#endif + errno = EOPNOTSUPP; +out: HYPERKIT_BLOCK_DELETE_DONE(offset, ret); return ret; } @@ -554,9 +596,12 @@ blockif_open(const char *optstr, const char *ident) char *nopt, *xopts, *cp; struct blockif_ctxt *bc; struct stat sbuf; + struct statfs fsbuf; // struct diocgattr_arg arg; off_t size, psectsz, psectoff, blocks; int extra, fd, i, sectsz; + size_t delete_alignment; + void *delete_zero_buf; int nocache, sync, ro, candelete, geom, ssopt, pssopt; mirage_block_handle mbh; int use_mirage = 0; @@ -621,6 +666,8 @@ blockif_open(const char *optstr, const char *ident) extra |= O_SYNC; candelete = 0; + sectsz = DEV_BSIZE; + delete_alignment = DEV_BSIZE; if (use_mirage) { #ifdef HAVE_OCAML_QCOW @@ -655,7 +702,33 @@ blockif_open(const char *optstr, const char *ident) perror("Could not stat backing file"); goto err; } + if (fstatfs(fd, &fsbuf) < 0) { + perror("Could not stat backfile file filesystem"); + goto err; + } + delete_alignment = (size_t)fsbuf.f_bsize; +#ifdef __APPLE__ + { + /* Check to see whether we can use F_PUNCHHOLE on this file */ + struct fpunchhole arg = { .fp_flags = 0, .reserved = 0, .fp_offset = 0, .fp_length = 0 }; + if (fcntl(fd, F_PUNCHHOLE, &arg) == 0) { + /* Sparse files are supported: enable TRIM */ + candelete = 1; + } else { + perror("fcntl(F_PUNCHHOLE) failed: host filesystem does not support sparse files"); + candelete = 0; + } + } +#endif } + /* delete_alignment is a power of 2 allowing us to use ALIGNDOWN for rounding */ + assert((delete_alignment & (delete_alignment - 1)) == 0); + + if ((delete_zero_buf = malloc(delete_alignment)) == NULL){ + perror("Failed to allocate zeroed buffer for unaligned deletes"); + goto err; + } + bzero(delete_zero_buf, delete_alignment); /* One and only one handle */ assert(mbh >= 0 || fd >= 0); @@ -664,7 +737,6 @@ blockif_open(const char *optstr, const char *ident) * Deal with raw devices */ size = sbuf.st_size; - sectsz = DEV_BSIZE; psectsz = psectoff = 0; geom = 0; if (S_ISCHR(sbuf.st_mode)) { @@ -749,6 +821,8 @@ blockif_open(const char *optstr, const char *ident) bc->bc_sectsz = sectsz; bc->bc_psectsz = (int)psectsz; bc->bc_psectoff = (int)psectoff; + bc->bc_delete_alignment = delete_alignment; + bc->bc_delete_zero_buf = delete_zero_buf; pthread_mutex_init(&bc->bc_mtx, NULL); pthread_cond_init(&bc->bc_cond, NULL); TAILQ_INIT(&bc->bc_freeq); diff --git a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/fwctl.c b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/fwctl.c new file mode 100644 index 000000000..0dfd77154 --- /dev/null +++ b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/fwctl.c @@ -0,0 +1,560 @@ +/*- + * Copyright (c) 2015 Peter Grehan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Guest firmware interface. Uses i/o ports x510/x511 as Qemu does, + * but with a request/response messaging protocol. + */ +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "xhyve/inout.h" +#include "xhyve/xhyve.h" +#include "xhyve/support/misc.h" +#include "xhyve/fwctl.h" + +/* + * Messaging protocol base operations + */ +#define OP_NULL 1 +#define OP_ECHO 2 +#define OP_GET 3 +#define OP_GET_LEN 4 +#define OP_SET 5 +#define OP_MAX OP_SET + +/* I/O ports */ +#define FWCTL_OUT 0x510 +#define FWCTL_IN 0x511 + +/* + * Back-end state-machine + */ +static enum state { + DORMANT, + IDENT_WAIT, + IDENT_SEND, + REQ, + RESP +} be_state = DORMANT; + +static uint8_t sig[] = { 'B', 'H', 'Y', 'V' }; +static u_int ident_idx; + +struct op_info { + int op; + int (*op_start)(size_t len); + void (*op_data)(uint32_t data, size_t len); + int (*op_result)(struct iovec **data); + void (*op_done)(struct iovec *data); +}; +static struct op_info *ops[OP_MAX+1]; + +/* Return 0-padded uint32_t */ +static uint32_t +fwctl_send_rest(uint8_t *data, size_t len) +{ + union { + uint8_t c[4]; + uint32_t w; + } u; + size_t i; + + u.w = 0; + + for (i = 0, u.w = 0; i < len; i++) + u.c[i] = *data++; + + return (u.w); +} + +/* + * error op dummy proto - drop all data sent and return an error +*/ +static int errop_code; + +static void +errop_set(int err) +{ + + errop_code = err; +} + +static int +errop_start(UNUSED size_t len) +{ + errop_code = ENOENT; + + /* accept any length */ + return (errop_code); +} + +static void +errop_data(UNUSED uint32_t data, UNUSED size_t len) +{ + + /* ignore */ +} + +static int +errop_result(struct iovec **data) +{ + + /* no data to send back; always successful */ + *data = NULL; + return (errop_code); +} + +static void +errop_done(UNUSED struct iovec *data) +{ + + /* assert data is NULL */ +} + +static struct op_info errop_info = { + .op_start = errop_start, + .op_data = errop_data, + .op_result = errop_result, + .op_done = errop_done +}; + +/* OID search */ +SET_DECLARE(ctl_set, struct ctl); + +CTL_NODE("hw.ncpu", &guest_ncpus, sizeof(guest_ncpus)); + +static struct ctl * +ctl_locate(const char *str, size_t maxlen) +{ + struct ctl *cp, **cpp; + + SET_FOREACH(cpp, ctl_set) { + cp = *cpp; + if (!strncmp(str, cp->c_oid, maxlen)) + return (cp); + } + + return (NULL); +} + +/* uefi-sysctl get-len */ +#define FGET_STRSZ 80 +static struct iovec fget_biov[2]; +static char fget_str[FGET_STRSZ]; +static struct { + size_t f_sz; + uint32_t f_data[1024]; +} fget_buf; +static size_t fget_cnt; +static size_t fget_size; + +static int +fget_start(size_t len) +{ + + if (len > FGET_STRSZ) + return(E2BIG); + + fget_cnt = 0; + + return (0); +} + +static void +fget_data(uint32_t data, UNUSED size_t len) +{ + memcpy(&fget_str[fget_cnt], &data, sizeof(data)); + fget_cnt += sizeof(uint32_t); +} + +static int +fget_result(struct iovec **data, int val) +{ + struct ctl *cp; + int err; + + err = 0; + + /* Locate the OID */ + cp = ctl_locate(fget_str, fget_cnt); + if (cp == NULL) { + *data = NULL; + err = ENOENT; + } else { + if (val) { + /* For now, copy the len/data into a buffer */ + memset(&fget_buf, 0, sizeof(fget_buf)); + fget_buf.f_sz = (size_t)cp->c_len; + memcpy(fget_buf.f_data, cp->c_data, cp->c_len); + fget_biov[0].iov_base = (char *)&fget_buf; + fget_biov[0].iov_len = sizeof(fget_buf.f_sz) + + (size_t)cp->c_len; + } else { + fget_size = (size_t)cp->c_len; + fget_biov[0].iov_base = (char *)&fget_size; + fget_biov[0].iov_len = sizeof(fget_size); + } + + fget_biov[1].iov_base = NULL; + fget_biov[1].iov_len = 0; + *data = fget_biov; + } + + return (err); +} + +static void +fget_done(UNUSED struct iovec *data) +{ + + /* nothing needs to be freed */ +} + +static int +fget_len_result(struct iovec **data) +{ + return (fget_result(data, 0)); +} + +static int +fget_val_result(struct iovec **data) +{ + return (fget_result(data, 1)); +} + +static struct op_info fgetlen_info = { + .op_start = fget_start, + .op_data = fget_data, + .op_result = fget_len_result, + .op_done = fget_done +}; + +static struct op_info fgetval_info = { + .op_start = fget_start, + .op_data = fget_data, + .op_result = fget_val_result, + .op_done = fget_done +}; + +static struct req_info { + int req_error; + u_int req_count; + uint32_t req_size; + uint32_t req_type; + uint32_t req_txid; + struct op_info *req_op; + int resp_error; + int resp_count; + int resp_size; + int resp_off; + struct iovec *resp_biov; +} rinfo; + +static void +fwctl_response_done(void) +{ + + (*rinfo.req_op->op_done)(rinfo.resp_biov); + + /* reinit the req data struct */ + memset(&rinfo, 0, sizeof(rinfo)); +} + +static void +fwctl_request_done(void) +{ + + rinfo.resp_error = (*rinfo.req_op->op_result)(&rinfo.resp_biov); + + /* XXX only a single vector supported at the moment */ + rinfo.resp_off = 0; + if (rinfo.resp_biov == NULL) { + rinfo.resp_size = 0; + } else { + assert(rinfo.resp_biov[0].iov_len < INT_MAX); + rinfo.resp_size = (int)rinfo.resp_biov[0].iov_len; + } +} + +static int +fwctl_request_start(void) +{ + int err; + + /* Data size doesn't include header */ + rinfo.req_size -= 12; + + rinfo.req_op = &errop_info; + if (rinfo.req_type <= OP_MAX && ops[rinfo.req_type] != NULL) + rinfo.req_op = ops[rinfo.req_type]; + + err = (*rinfo.req_op->op_start)(rinfo.req_size); + + if (err) { + errop_set(err); + rinfo.req_op = &errop_info; + } + + /* Catch case of zero-length message here */ + if (rinfo.req_size == 0) { + fwctl_request_done(); + return (1); + } + + return (0); +} + +static int +fwctl_request_data(uint32_t value) +{ + size_t remlen; + + /* Make sure remaining size is >= 0 */ + rinfo.req_size -= sizeof(uint32_t); + remlen = MAX(rinfo.req_size, 0); + + (*rinfo.req_op->op_data)(value, remlen); + + if (rinfo.req_size < sizeof(uint32_t)) { + fwctl_request_done(); + return (1); + } + + return (0); +} + +static int +fwctl_request(uint32_t value) +{ + + int ret; + + ret = 0; + + switch (rinfo.req_count) { + case 0: + /* Verify size */ + if (value < 12) { + printf("msg size error"); + exit(1); + } + rinfo.req_size = value; + rinfo.req_count = 1; + break; + case 1: + rinfo.req_type = value; + rinfo.req_count++; + break; + case 2: + rinfo.req_txid = value; + rinfo.req_count++; + ret = fwctl_request_start(); + break; + default: + ret = fwctl_request_data(value); + break; + } + + return (ret); +} + +static int +fwctl_response(uint32_t *retval) +{ + uint8_t *dp; + size_t remlen; + + switch(rinfo.resp_count) { + case 0: + /* 4 x u32 header len + data */ + *retval = 4*sizeof(uint32_t) + + roundup((uint32_t)rinfo.resp_size, sizeof(uint32_t)); + rinfo.resp_count++; + break; + case 1: + *retval = rinfo.req_type; + rinfo.resp_count++; + break; + case 2: + *retval = rinfo.req_txid; + rinfo.resp_count++; + break; + case 3: + *retval = (uint32_t)rinfo.resp_error; + rinfo.resp_count++; + break; + default: + remlen = (size_t)rinfo.resp_size - (size_t)rinfo.resp_off; + dp = ((uint8_t *)rinfo.resp_biov->iov_base + rinfo.resp_off); + if (remlen >= sizeof(uint32_t)) { + memcpy(retval, dp, sizeof(*retval)); + } else if (remlen > 0) { + *retval = fwctl_send_rest(dp, remlen); + } + rinfo.resp_off += sizeof(uint32_t); + break; + } + + if (rinfo.resp_count > 3 && + rinfo.resp_size - rinfo.resp_off <= 0) { + fwctl_response_done(); + return (1); + } + + return (0); +} + + +/* + * i/o port handling. + */ +static uint8_t +fwctl_inb(void) +{ + uint8_t retval; + + retval = 0xff; + + switch (be_state) { + case DORMANT: + case IDENT_WAIT: + break; + case IDENT_SEND: + retval = sig[ident_idx++]; + if (ident_idx >= sizeof(sig)) + be_state = REQ; + break; + case REQ: + case RESP: + break; + } + + return (retval); +} + +static void +fwctl_outw(uint16_t val) +{ + switch (be_state) { + case DORMANT: + break; + case IDENT_WAIT: + if (val == 0) { + be_state = IDENT_SEND; + ident_idx = 0; + } + break; + case IDENT_SEND: + case REQ: + case RESP: + break; + } +} + +static uint32_t +fwctl_inl(void) +{ + uint32_t retval; + + switch (be_state) { + case DORMANT: + case IDENT_WAIT: + case IDENT_SEND: + case REQ: + retval = 0xffffffff; + break; + case RESP: + if (fwctl_response(&retval)) + be_state = REQ; + break; + } + + return (retval); +} + +static void +fwctl_outl(uint32_t val) +{ + + switch (be_state) { + case DORMANT: + case IDENT_WAIT: + case IDENT_SEND: + break; + case REQ: + if (fwctl_request(val)) + be_state = RESP; + case RESP: + break; + } + +} + +static int +fwctl_handler(UNUSED int vcpu, int in, UNUSED int port, int bytes, + uint32_t *eax, UNUSED void *arg) +{ + + if (in) { + if (bytes == 1) + *eax = fwctl_inb(); + else if (bytes == 4) + *eax = fwctl_inl(); + else + *eax = 0xffff; + } else { + if (bytes == 2) + fwctl_outw((uint16_t)*eax); + else if (bytes == 4) + fwctl_outl(*eax); + } + + return (0); +} +INOUT_PORT(fwctl_wreg, FWCTL_OUT, IOPORT_F_INOUT, fwctl_handler); +INOUT_PORT(fwctl_rreg, FWCTL_IN, IOPORT_F_IN, fwctl_handler); + +void +fwctl_init(void) +{ + + ops[OP_GET_LEN] = &fgetlen_info; + ops[OP_GET] = &fgetval_info; + + be_state = IDENT_WAIT; +} diff --git a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/pci_ahci.c b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/pci_ahci.c index 32b4c8d8e..064c44931 100644 --- a/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/pci_ahci.c +++ b/src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/pci_ahci.c @@ -797,7 +797,15 @@ next: done += 8; if (elen == 0) { if (done >= len) { - ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC); + if (ncq) { + if (first) + ahci_write_fis_d2h_ncq(p, slot); + ahci_write_fis_sdb(p, slot, cfis, + ATA_S_READY | ATA_S_DSC); + } else { + ahci_write_fis_d2h(p, slot, cfis, + ATA_S_READY | ATA_S_DSC); + } p->pending &= ~(1 << slot); ahci_check_stopped(p); if (!first) @@ -977,6 +985,7 @@ handle_identify(struct ahci_port *p, int slot, uint8_t *cfis) buf[88] = 0x7f; if (p->xfermode & ATA_UDMA0) buf[88] |= (1 << ((p->xfermode & 7) + 8)); + buf[93] = (1 | 1 <<14); buf[100] = (uint16_t) sectors; buf[101] = (uint16_t) (sectors >> 16); buf[102] = (uint16_t) (sectors >> 32); @@ -1725,7 +1734,7 @@ ahci_handle_cmd(struct ahci_port *p, int slot, uint8_t *cfis) case ATA_SEND_FPDMA_QUEUED: if ((cfis[13] & 0x1f) == ATA_SFPDMA_DSM && cfis[17] == 0 && cfis[16] == ATA_DSM_TRIM && - cfis[11] == 0 && cfis[13] == 1) { + cfis[11] == 0 && cfis[3] == 1) { ahci_handle_dsm_trim(p, slot, cfis, 0); break; }