mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-20 17:49:10 +00:00
Merge pull request #2701 from rn/vpnkit
Add support for publish port on localhost to the hyperkit backend
This commit is contained in:
commit
581cbdd1e8
@ -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 <IP address of VM>
|
||||
```
|
||||
|
||||
### 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:<IP address of VM>: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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 'vpnkit.uuid' 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, "vpnkit.uuid")
|
||||
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
|
||||
}
|
||||
|
@ -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.`")
|
||||
@ -618,85 +618,24 @@ func discoverBackend(config QemuConfig) QemuConfig {
|
||||
return config
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
func (f *multipleFlag) Set(value string) error {
|
||||
*f = append(*f, value)
|
||||
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 <host>:<guest> or <host>:<guest>/<tcp|udp>")
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
@ -705,11 +644,11 @@ 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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
@ -191,3 +203,52 @@ 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 uint16
|
||||
Host uint16
|
||||
Protocol string
|
||||
}
|
||||
|
||||
// NewPublishedPort parses a string of the form <host>:<guest>[/<tcp|udp>] 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 <host>:<guest> or <host>:<guest>/<tcp|udp>")
|
||||
}
|
||||
|
||||
hostPort, err := strconv.ParseUint(slice[0], 10, 16)
|
||||
if err != nil {
|
||||
return p, fmt.Errorf("The provided hostPort can't be converted to uint16")
|
||||
}
|
||||
|
||||
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.ParseUint(right[0], 10, 16)
|
||||
if err != nil {
|
||||
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 = uint16(guestPort)
|
||||
p.Host = uint16(hostPort)
|
||||
p.Protocol = protocol
|
||||
return p, nil
|
||||
}
|
||||
|
@ -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/hyperkit 3e31617ae866c93925e2b3bc5d8006b60985e920
|
||||
github.com/moby/datakit 97b3d230535397a813323902c23751e176481a86
|
||||
github.com/moby/hyperkit a12cd7250bcd8d689078e3e42ae4a7cf6a0cbaf3
|
||||
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
|
||||
|
60
src/cmd/linuxkit/vendor/github.com/docker/docker/hack/README.md
generated
vendored
60
src/cmd/linuxkit/vendor/github.com/docker/docker/hack/README.md
generated
vendored
@ -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)
|
69
src/cmd/linuxkit/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/README.md
generated
vendored
69
src/cmd/linuxkit/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/README.md
generated
vendored
@ -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
|
@ -1,2 +0,0 @@
|
||||
# dependencies specific to worker (i.e. github.com/docker/docker/...) are not vendored here
|
||||
github.com/bfirsh/funker-go eaa0a2e06f30e72c9a0b7f858951e581e26ef773
|
202
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/LICENSE
generated
vendored
Normal file
202
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/LICENSE
generated
vendored
Normal file
@ -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.
|
||||
|
11
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/README.md
generated
vendored
Normal file
11
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/README.md
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# p9p [](https://godoc.org/github.com/docker/go-p9p) [](https://raw.githubusercontent.com/docker/go-p9p/master/LICENSE) [](https://circleci.com/gh/docker/go-p9p) [](https://travis-ci.org/docker/go-p9p) [](https://goreportcard.com/report/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.
|
343
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/channel.go
generated
vendored
Normal file
343
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/channel.go
generated
vendored
Normal file
@ -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
|
||||
}
|
253
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/client.go
generated
vendored
Normal file
253
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/client.go
generated
vendored
Normal file
@ -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
|
||||
}
|
26
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/context.go
generated
vendored
Normal file
26
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/context.go
generated
vendored
Normal file
@ -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
|
||||
}
|
133
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/dispatcher.go
generated
vendored
Normal file
133
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/dispatcher.go
generated
vendored
Normal file
@ -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
|
||||
}
|
||||
})
|
||||
}
|
78
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/doc.go
generated
vendored
Normal file
78
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/doc.go
generated
vendored
Normal file
@ -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
|
564
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/encoding.go
generated
vendored
Normal file
564
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/encoding.go
generated
vendored
Normal file
@ -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
|
||||
}
|
58
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/errors.go
generated
vendored
Normal file
58
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/errors.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
142
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/fcall.go
generated
vendored
Normal file
142
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/fcall.go
generated
vendored
Normal file
@ -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))
|
||||
}
|
216
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/messages.go
generated
vendored
Normal file
216
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/messages.go
generated
vendored
Normal file
@ -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 }
|
49
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/overflow.go
generated
vendored
Normal file
49
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/overflow.go
generated
vendored
Normal file
@ -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
|
||||
}
|
93
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/readdir.go
generated
vendored
Normal file
93
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/readdir.go
generated
vendored
Normal file
@ -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
|
||||
}
|
257
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/server.go
generated
vendored
Normal file
257
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/server.go
generated
vendored
Normal file
@ -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
|
||||
}
|
42
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/session.go
generated
vendored
Normal file
42
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/session.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
250
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/transport.go
generated
vendored
Normal file
250
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/transport.go
generated
vendored
Normal file
@ -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)
|
||||
})
|
||||
}
|
149
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/types.go
generated
vendored
Normal file
149
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/types.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
112
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/version.go
generated
vendored
Normal file
112
src/cmd/linuxkit/vendor/github.com/docker/go-p9p/version.go
generated
vendored
Normal file
@ -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
|
||||
}
|
27
src/cmd/linuxkit/vendor/github.com/google/uuid/LICENSE
generated
vendored
Normal file
27
src/cmd/linuxkit/vendor/github.com/google/uuid/LICENSE
generated
vendored
Normal file
@ -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.
|
23
src/cmd/linuxkit/vendor/github.com/google/uuid/README.md
generated
vendored
Normal file
23
src/cmd/linuxkit/vendor/github.com/google/uuid/README.md
generated
vendored
Normal file
@ -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 
|
||||
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
|
||||
[](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
|
80
src/cmd/linuxkit/vendor/github.com/google/uuid/dce.go
generated
vendored
Normal file
80
src/cmd/linuxkit/vendor/github.com/google/uuid/dce.go
generated
vendored
Normal file
@ -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))
|
||||
}
|
12
src/cmd/linuxkit/vendor/github.com/google/uuid/doc.go
generated
vendored
Normal file
12
src/cmd/linuxkit/vendor/github.com/google/uuid/doc.go
generated
vendored
Normal file
@ -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
|
53
src/cmd/linuxkit/vendor/github.com/google/uuid/hash.go
generated
vendored
Normal file
53
src/cmd/linuxkit/vendor/github.com/google/uuid/hash.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
39
src/cmd/linuxkit/vendor/github.com/google/uuid/marshal.go
generated
vendored
Normal file
39
src/cmd/linuxkit/vendor/github.com/google/uuid/marshal.go
generated
vendored
Normal file
@ -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
|
||||
}
|
103
src/cmd/linuxkit/vendor/github.com/google/uuid/node.go
generated
vendored
Normal file
103
src/cmd/linuxkit/vendor/github.com/google/uuid/node.go
generated
vendored
Normal file
@ -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[:]
|
||||
}
|
59
src/cmd/linuxkit/vendor/github.com/google/uuid/sql.go
generated
vendored
Normal file
59
src/cmd/linuxkit/vendor/github.com/google/uuid/sql.go
generated
vendored
Normal file
@ -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
|
||||
}
|
123
src/cmd/linuxkit/vendor/github.com/google/uuid/time.go
generated
vendored
Normal file
123
src/cmd/linuxkit/vendor/github.com/google/uuid/time.go
generated
vendored
Normal file
@ -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
|
||||
}
|
43
src/cmd/linuxkit/vendor/github.com/google/uuid/util.go
generated
vendored
Normal file
43
src/cmd/linuxkit/vendor/github.com/google/uuid/util.go
generated
vendored
Normal file
@ -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
|
||||
}
|
191
src/cmd/linuxkit/vendor/github.com/google/uuid/uuid.go
generated
vendored
Normal file
191
src/cmd/linuxkit/vendor/github.com/google/uuid/uuid.go
generated
vendored
Normal file
@ -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
|
||||
}
|
44
src/cmd/linuxkit/vendor/github.com/google/uuid/version1.go
generated
vendored
Normal file
44
src/cmd/linuxkit/vendor/github.com/google/uuid/version1.go
generated
vendored
Normal file
@ -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
|
||||
}
|
38
src/cmd/linuxkit/vendor/github.com/google/uuid/version4.go
generated
vendored
Normal file
38
src/cmd/linuxkit/vendor/github.com/google/uuid/version4.go
generated
vendored
Normal file
@ -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
|
||||
}
|
191
src/cmd/linuxkit/vendor/github.com/moby/datakit/LICENSE.md
generated
vendored
Normal file
191
src/cmd/linuxkit/vendor/github.com/moby/datakit/LICENSE.md
generated
vendored
Normal file
@ -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.
|
112
src/cmd/linuxkit/vendor/github.com/moby/datakit/README.md
generated
vendored
Normal file
112
src/cmd/linuxkit/vendor/github.com/moby/datakit/README.md
generated
vendored
Normal file
@ -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.
|
||||
|
||||
---
|
||||
|
||||
[](https://travis-ci.org/moby/datakit)
|
||||
[](https://ci.appveyor.com/project/moby/datakit/branch/master)
|
||||
[](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 <path/to/git/repo>:/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
|
1
src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/README
generated
vendored
Normal file
1
src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/README
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
To run test on windows, launch the a datakit server with --url \\.\pipe\datakit-test
|
352
src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/client.go
generated
vendored
Normal file
352
src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/client.go
generated
vendored
Normal file
@ -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
|
||||
}
|
380
src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/config.go
generated
vendored
Normal file
380
src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/config.go
generated
vendored
Normal file
@ -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}
|
||||
}
|
5
src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/doc.go
generated
vendored
Normal file
5
src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/doc.go
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/*
|
||||
The datakit package contains common patterns over 9P, which avoids the need
|
||||
for applications to use 9P directly.
|
||||
*/
|
||||
package datakit
|
87
src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/snapshot.go
generated
vendored
Normal file
87
src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/snapshot.go
generated
vendored
Normal file
@ -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/<fromBranch>/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)
|
||||
}
|
136
src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/transaction.go
generated
vendored
Normal file
136
src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/transaction.go
generated
vendored
Normal file
@ -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
|
||||
}
|
77
src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/watch.go
generated
vendored
Normal file
77
src/cmd/linuxkit/vendor/github.com/moby/datakit/api/go-datakit/watch.go
generated
vendored
Normal file
@ -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/<fromBranch>/watch/<path.node>/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)
|
||||
}
|
6
src/cmd/linuxkit/vendor/github.com/moby/hyperkit/go/hyperkit.go
generated
vendored
6
src/cmd/linuxkit/vendor/github.com/moby/hyperkit/go/hyperkit.go
generated
vendored
@ -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 {
|
||||
|
@ -4,5 +4,6 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
int bootrom_init(const char *bootrom_path);
|
||||
const char *bootrom(void);
|
||||
uint64_t bootrom_load(void);
|
||||
bool bootrom_contains_gpa(uint64_t gpa);
|
||||
|
54
src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/include/xhyve/fwctl.h
generated
vendored
Normal file
54
src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/include/xhyve/fwctl.h
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
/*-
|
||||
* Copyright (c) 2015 Peter Grehan <grehan@freebsd.org>
|
||||
* 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 <xhyve/support/linker_set.h>
|
||||
|
||||
/*
|
||||
* 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_ */
|
100
src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/block_if.c
generated
vendored
100
src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/block_if.c
generated
vendored
@ -33,6 +33,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/disk.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
@ -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);
|
||||
|
560
src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/fwctl.c
generated
vendored
Normal file
560
src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/fwctl.c
generated
vendored
Normal file
@ -0,0 +1,560 @@
|
||||
/*-
|
||||
* Copyright (c) 2015 Peter Grehan <grehan@freebsd.org>
|
||||
* 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 <sys/cdefs.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
13
src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/pci_ahci.c
generated
vendored
13
src/cmd/linuxkit/vendor/github.com/moby/hyperkit/src/lib/pci_ahci.c
generated
vendored
@ -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;
|
||||
}
|
||||
|
191
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/LICENSE
generated
vendored
Normal file
191
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/LICENSE
generated
vendored
Normal file
@ -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.
|
78
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/README.md
generated
vendored
Normal file
78
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/README.md
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
VPN-friendly networking devices for [HyperKit](https://github.com/moby/hyperkit)
|
||||
===============================
|
||||
|
||||
[](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 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.
|
248
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/9pmount-vsock.c
generated
vendored
Normal file
248
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/9pmount-vsock.c
generated
vendored
Normal file
@ -0,0 +1,248 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <syslog.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <err.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#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 <guid>] <listen | connect> <tag> <path>\n");
|
||||
printf("where\n");
|
||||
printf("\t--serviceid <guid>: use <guid> 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 */
|
||||
}
|
||||
}
|
37
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/hvsock.c
generated
vendored
Normal file
37
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/hvsock.c
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#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);
|
48
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/hvsock.h
generated
vendored
Normal file
48
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-9pmount-vsock/hvsock.h
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
/* AF_HYPERV definitions and utilities */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
/* 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;
|
40
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/hvsock.c
generated
vendored
Normal file
40
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/hvsock.c
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#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);
|
48
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/hvsock.h
generated
vendored
Normal file
48
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/hvsock.h
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
/* AF_HYPERV definitions and utilities */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
/* 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;
|
225
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/protocol.c
generated
vendored
Normal file
225
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/protocol.c
generated
vendored
Normal file
@ -0,0 +1,225 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#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;
|
||||
}
|
77
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/protocol.h
generated
vendored
Normal file
77
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/protocol.h
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
#ifndef _VMNET_PROTOCOL_H_
|
||||
#define _VMNET_PROTOCOL_H_
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* 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_ */
|
269
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/ring.c
generated
vendored
Normal file
269
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/ring.c
generated
vendored
Normal file
@ -0,0 +1,269 @@
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
35
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/ring.h
generated
vendored
Normal file
35
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/ring.h
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
/* 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
|
||||
);
|
732
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/tap-vsockd.c
generated
vendored
Normal file
732
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/c/vpnkit-tap-vsockd/tap-vsockd.c
generated
vendored
Normal file
@ -0,0 +1,732 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <syslog.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <err.h>
|
||||
#include <sys/uio.h>
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <ifaddrs.h>
|
||||
|
||||
#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 <name>] [--serviceid <guid>] [--pid <file>]\n");
|
||||
printf("\t[--message-size <bytes>] [--buffer-size <bytes>]\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 <name>: create a tap device with the given name\n");
|
||||
printf("\t (defaults to eth1)\n");
|
||||
printf("\t--serviceid <guid>: use <guid> as the well-known service GUID\n");
|
||||
printf("\t (defaults to %s)\n", default_sid);
|
||||
printf("\t--pid <file>: write a pid to the given file\n");
|
||||
printf("\t--message-size <bytes>: dictates the maximum transfer size for AF_HVSOCK\n");
|
||||
printf("\t--buffer-size <bytes>: 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;
|
||||
}
|
||||
}
|
||||
}
|
22
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/connection.go
generated
vendored
Normal file
22
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/connection.go
generated
vendored
Normal file
@ -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
|
||||
}
|
9
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/doc.go
generated
vendored
Normal file
9
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/doc.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/*
|
||||
Package vpnkit allows a running VPNKit service to be reconfigured.
|
||||
|
||||
Features
|
||||
|
||||
- expose/unexpose TCP and UDP ports
|
||||
|
||||
*/
|
||||
package vpnkit
|
200
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/port.go
generated
vendored
Normal file
200
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/port.go
generated
vendored
Normal file
@ -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"}
|
633
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/vmnet.go
generated
vendored
Normal file
633
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/pkg/vpnkit/vmnet.go
generated
vendored
Normal file
@ -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
|
||||
}
|
||||
|
||||
}
|
5
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/vendor.conf
generated
vendored
Normal file
5
src/cmd/linuxkit/vendor/github.com/moby/vpnkit/go/vendor.conf
generated
vendored
Normal file
@ -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
|
20
src/cmd/linuxkit/vendor/github.com/satori/go.uuid/LICENSE
generated
vendored
20
src/cmd/linuxkit/vendor/github.com/satori/go.uuid/LICENSE
generated
vendored
@ -1,20 +0,0 @@
|
||||
Copyright (C) 2013-2016 by Maxim Bublis <b@codemonkey.ru>
|
||||
|
||||
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.
|
65
src/cmd/linuxkit/vendor/github.com/satori/go.uuid/README.md
generated
vendored
65
src/cmd/linuxkit/vendor/github.com/satori/go.uuid/README.md
generated
vendored
@ -1,65 +0,0 @@
|
||||
# UUID package for Go language
|
||||
|
||||
[](https://travis-ci.org/satori/go.uuid)
|
||||
[](https://coveralls.io/github/satori/go.uuid)
|
||||
[](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 <b@codemonkey.ru>.
|
||||
|
||||
UUID package released under MIT License.
|
||||
See [LICENSE](https://github.com/satori/go.uuid/blob/master/LICENSE) for details.
|
481
src/cmd/linuxkit/vendor/github.com/satori/go.uuid/uuid.go
generated
vendored
481
src/cmd/linuxkit/vendor/github.com/satori/go.uuid/uuid.go
generated
vendored
@ -1,481 +0,0 @@
|
||||
// Copyright (C) 2013-2015 by Maxim Bublis <b@codemonkey.ru>
|
||||
//
|
||||
// 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user