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