mirror of
https://github.com/containers/podman.git
synced 2025-05-22 01:27:07 +08:00
Initial addition of 9p code to Podman
This includes two new hidden commands: a 9p server, `podman machine server9p`, and a 9p client, `podman machine client9p` with `server9p` currently only configured to run on Windows and serve 9p via HyperV vsock, and `client9p` only configured to run on Linux. The server is run by `podman machine start` and has the same lifespan as gvproxy (waits for the gvproxy PID to die before shutting down). The client is run inside the VM, also by `podman machine start`, and mounts uses kernel 9p mount code to complete the mount. It's unfortunately not possible to use mount directly without the wrapper; we need to set up the vsock and pass it to mount as an FD. In theory this can be generalized so that the server can run anywhere and over almost any transport, but I haven't done this here as I don't think we have a usecase other than HyperV right now. [NO NEW TESTS NEEDED] This requires changes to Podman in the VM, so we need to wait until a build with this lands in FCOS to test. Signed-off-by: Matthew Heon <matthew.heon@pm.me>
This commit is contained in:
135
cmd/podman/machine/client9p.go
Normal file
135
cmd/podman/machine/client9p.go
Normal file
@ -0,0 +1,135 @@
|
||||
//go:build linux && (amd64 || arm64)
|
||||
// +build linux
|
||||
// +build amd64 arm64
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/containers/common/pkg/completion"
|
||||
"github.com/containers/podman/v4/cmd/podman/registry"
|
||||
"github.com/mdlayher/vsock"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
client9pCommand = &cobra.Command{
|
||||
Args: cobra.ExactArgs(2),
|
||||
Use: "client9p PORT DIR",
|
||||
Hidden: true,
|
||||
Short: "Mount a remote directory using 9p over hvsock",
|
||||
Long: "Connect to the given hvsock port using 9p and mount the served filesystem at the given directory",
|
||||
RunE: remoteDirClient,
|
||||
ValidArgsFunction: completion.AutocompleteNone,
|
||||
Example: `podman system client9p 55000 /mnt`,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Command: client9pCommand,
|
||||
Parent: machineCmd,
|
||||
})
|
||||
}
|
||||
|
||||
func remoteDirClient(cmd *cobra.Command, args []string) error {
|
||||
port, err := strconv.Atoi(args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing port number: %w", err)
|
||||
}
|
||||
|
||||
if err := client9p(uint32(port), args[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is Linux-only as we only intend for this function to be used inside the
|
||||
// `podman machine` VM, which is guaranteed to be Linux.
|
||||
func client9p(portNum uint32, mountPath string) error {
|
||||
cleanPath, err := filepath.Abs(mountPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("absolute path for %s: %w", mountPath, err)
|
||||
}
|
||||
mountPath = cleanPath
|
||||
|
||||
// Mountpath needs to exist and be a directory
|
||||
stat, err := os.Stat(mountPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("stat %s: %w", mountPath, err)
|
||||
}
|
||||
if !stat.IsDir() {
|
||||
return fmt.Errorf("path %s is not a directory", mountPath)
|
||||
}
|
||||
|
||||
logrus.Infof("Going to mount 9p on vsock port %d to directory %s", portNum, mountPath)
|
||||
|
||||
// Host connects to non-hypervisor processes on the host running the VM.
|
||||
conn, err := vsock.Dial(vsock.Host, portNum, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dialing vsock port %d: %w", portNum, err)
|
||||
}
|
||||
defer func() {
|
||||
if err := conn.Close(); err != nil {
|
||||
logrus.Errorf("Error closing vsock: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// vsock doesn't give us direct access to the underlying FD. That's kind
|
||||
// of inconvenient, because we have to pass it off to mount.
|
||||
// However, it does give us the ability to get a syscall.RawConn, which
|
||||
// has a method that allows us to run a function that takes the FD
|
||||
// number as an argument.
|
||||
// Which ought to be good enough? Probably?
|
||||
// Overall, this is gross and I hate it, but I don't see a better way.
|
||||
rawConn, err := conn.SyscallConn()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting vsock raw conn: %w", err)
|
||||
}
|
||||
errChan := make(chan error, 1)
|
||||
runMount := func(fd uintptr) {
|
||||
vsock := os.NewFile(fd, "vsock")
|
||||
if vsock == nil {
|
||||
errChan <- fmt.Errorf("could not convert vsock fd to os.File")
|
||||
return
|
||||
}
|
||||
|
||||
// This is ugly, but it lets us use real kernel mount code,
|
||||
// instead of maintaining our own FUSE 9p implementation.
|
||||
cmd := exec.Command("mount", "-t", "9p", "-o", "trans=fd,rfdno=3,wfdno=3,version=9p2000.L", "9p", mountPath)
|
||||
cmd.ExtraFiles = []*os.File{vsock}
|
||||
|
||||
err := cmd.Run()
|
||||
output, outErr := cmd.CombinedOutput()
|
||||
switch {
|
||||
case outErr != nil:
|
||||
logrus.Errorf("Unable to obtain output of mount command: %v", err)
|
||||
case err == nil:
|
||||
logrus.Debugf("Mount output: %s", string(output))
|
||||
logrus.Infof("Mounted directory %s using 9p", mountPath)
|
||||
default:
|
||||
err = fmt.Errorf("running mount: %w\nOutput: %s", err, string(output))
|
||||
}
|
||||
|
||||
errChan <- err
|
||||
close(errChan)
|
||||
}
|
||||
if err := rawConn.Control(runMount); err != nil {
|
||||
return fmt.Errorf("running mount function for dir %s: %w", mountPath, err)
|
||||
}
|
||||
|
||||
if err := <-errChan; err != nil {
|
||||
return fmt.Errorf("mounting filesystem %s: %w", mountPath, err)
|
||||
}
|
||||
|
||||
logrus.Infof("Mount of filesystem %s successful", mountPath)
|
||||
|
||||
return nil
|
||||
}
|
89
cmd/podman/machine/server9p.go
Normal file
89
cmd/podman/machine/server9p.go
Normal file
@ -0,0 +1,89 @@
|
||||
//go:build windows && (amd64 || arm64)
|
||||
// +build windows
|
||||
// +build amd64 arm64
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/common/pkg/completion"
|
||||
"github.com/containers/podman/v4/cmd/podman/registry"
|
||||
"github.com/containers/podman/v4/pkg/fileserver"
|
||||
"github.com/containers/podman/v4/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
server9pCommand = &cobra.Command{
|
||||
Args: cobra.ExactArgs(1),
|
||||
Use: "server9p [options] PID",
|
||||
Hidden: true,
|
||||
Short: "Serve a directory using 9p over hvsock",
|
||||
Long: "Start a number of 9p servers on given hvsock UUIDs, and run until the given PID exits",
|
||||
RunE: remoteDirServer,
|
||||
ValidArgsFunction: completion.AutocompleteNone,
|
||||
Example: `podman system server9p --serve C:\Users\myuser:00000050-FACB-11E6-BD58-64006A7986D3 /mnt`,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Command: server9pCommand,
|
||||
Parent: machineCmd,
|
||||
})
|
||||
|
||||
flags := server9pCommand.Flags()
|
||||
|
||||
serveFlagName := "serve"
|
||||
flags.StringArrayVar(&serveDirs, serveFlagName, []string{}, "directories to serve and UUID of vsock to serve on, colon-separated")
|
||||
_ = server9pCommand.RegisterFlagCompletionFunc(serveFlagName, completion.AutocompleteNone)
|
||||
}
|
||||
|
||||
var (
|
||||
serveDirs []string
|
||||
)
|
||||
|
||||
func remoteDirServer(cmd *cobra.Command, args []string) error {
|
||||
pid, err := strconv.Atoi(args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing PID: %w", err)
|
||||
}
|
||||
if pid < 0 {
|
||||
return fmt.Errorf("PIDs cannot be negative")
|
||||
}
|
||||
|
||||
if len(serveDirs) == 0 {
|
||||
return fmt.Errorf("must provide at least one directory to serve")
|
||||
}
|
||||
|
||||
// TODO: need to support options here
|
||||
shares := make(map[string]string, len(serveDirs))
|
||||
for _, share := range serveDirs {
|
||||
splitShare := strings.Split(share, ":")
|
||||
if len(splitShare) < 2 {
|
||||
return fmt.Errorf("paths passed to --share must include an hvsock GUID")
|
||||
}
|
||||
|
||||
// Every element but the last one is the real filepath to share
|
||||
path := strings.Join(splitShare[:len(splitShare)-1], ":")
|
||||
|
||||
shares[path] = splitShare[len(splitShare)-1]
|
||||
}
|
||||
|
||||
if err := fileserver.StartShares(shares); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for the given PID to exit
|
||||
if err := util.WaitForPIDExit(uint(pid)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logrus.Infof("Exiting cleanly as PID %d has died", pid)
|
||||
|
||||
return nil
|
||||
}
|
5
go.mod
5
go.mod
@ -39,9 +39,12 @@ require (
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/schema v1.2.0
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hugelgupf/p9 v0.3.1-0.20230822151754-54f5c5530921
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/linuxkit/virtsock v0.0.0-20220523201153-1a23e78aa7a2
|
||||
github.com/mattn/go-shellwords v1.0.12
|
||||
github.com/mattn/go-sqlite3 v1.14.17
|
||||
github.com/mdlayher/vsock v1.2.1
|
||||
github.com/moby/term v0.5.0
|
||||
github.com/nxadm/tail v1.4.11
|
||||
github.com/onsi/ginkgo/v2 v2.13.0
|
||||
@ -149,6 +152,7 @@ require (
|
||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mdlayher/socket v0.4.1 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mistifyio/go-zfs/v3 v3.0.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
@ -180,6 +184,7 @@ require (
|
||||
github.com/theupdateframework/go-tuf v0.5.2 // indirect
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/vbatts/tar-split v0.11.5 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
|
25
go.sum
25
go.sum
@ -72,6 +72,7 @@ github.com/Microsoft/hcsshim v0.12.0-rc.0/go.mod h1:rvOnw3YlfoNnEp45wReUngvsXbwR
|
||||
github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
|
||||
github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
@ -360,6 +361,7 @@ github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZ
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
@ -579,6 +581,7 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/goterm v0.0.0-20200907032337-555d40f16ae2 h1:CVuJwN34x4xM2aT4sIKhmeib40NeBPhRihNjQmpJsA4=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
@ -641,6 +644,10 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
|
||||
github.com/honeycombio/beeline-go v1.10.0 h1:cUDe555oqvw8oD76BQJ8alk7FP0JZ/M/zXpNvOEDLDc=
|
||||
github.com/honeycombio/libhoney-go v1.16.0 h1:kPpqoz6vbOzgp7jC6SR7SkNj7rua7rgxvznI6M3KdHc=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hugelgupf/p9 v0.3.1-0.20230822151754-54f5c5530921 h1:cfYGdNpXGZobTSSDFB+wx2FRfWptM7sCkScJgVx0Tkk=
|
||||
github.com/hugelgupf/p9 v0.3.1-0.20230822151754-54f5c5530921/go.mod h1:nMr69J6AmirlSvzeVLK7gj4DUY1oYtSwcSiSJ7BBb0A=
|
||||
github.com/hugelgupf/socketpair v0.0.0-20230822150718-707395b1939a h1:Nq7wDsqsVBUBfGn8yB1M028ShWTKTtZBcafaTJ35N0s=
|
||||
github.com/hugelgupf/vmtest v0.0.0-20230810222836-f8c8e381617c h1:4A+BVHylCBQPxlW1NrUITDpRAHCeX6QSZHmzzFQqliU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
@ -651,6 +658,7 @@ github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230731140434-0f9eb93a696c h1:P/3mFnHCv1A/ej4m8pF5EB6FUt9qEL2Q9lfrcUNwCYs=
|
||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
|
||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
@ -662,6 +670,8 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@ -711,6 +721,8 @@ github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNa
|
||||
github.com/letsencrypt/boulder v0.0.0-20230213213521-fdfea0d469b6 h1:unJdfS94Y3k85TKy+mvKzjW5R9rIC+Lv4KGbE7uNu0I=
|
||||
github.com/letsencrypt/boulder v0.0.0-20230213213521-fdfea0d469b6/go.mod h1:PUgW5vI9ANEaV6qv9a6EKu8gAySgwf0xrzG9xIB/CK0=
|
||||
github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
|
||||
github.com/linuxkit/virtsock v0.0.0-20220523201153-1a23e78aa7a2 h1:DZMFueDbfz6PNc1GwDRA8+6lBx1TB9UnxDQliCqR73Y=
|
||||
github.com/linuxkit/virtsock v0.0.0-20220523201153-1a23e78aa7a2/go.mod h1:SWzULI85WerrFt3u+nIm5F9l7EvxZTKQvd0InF3nmgM=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
@ -741,6 +753,11 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
|
||||
github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
|
||||
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
||||
github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ=
|
||||
github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE=
|
||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
@ -858,6 +875,8 @@ github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrap
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@ -1013,6 +1032,10 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/u-root/gobusybox/src v0.0.0-20230806212452-e9366a5b9fdc h1:udgfN9Qy573qgHWMEORFgy6YXNDiN/Fd5LlKdlp+/Mo=
|
||||
github.com/u-root/u-root v0.11.1-0.20230807200058-f87ad7ccb594 h1:1AIJqOtdEufYfGb3eRpdaqWONzBOpAwrg1fehbWg+Mg=
|
||||
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 h1:YcojQL98T/OO+rybuzn2+5KrD5dBwXIvYBvQ2cD3Avg=
|
||||
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
@ -1319,6 +1342,7 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -1608,3 +1632,4 @@ sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
src.elv.sh v0.16.0-rc1.0.20220116211855-fda62502ad7f h1:pjVeIo9Ba6K1Wy+rlwX91zT7A+xGEmxiNRBdN04gDTQ=
|
||||
|
94
pkg/fileserver/plan9/serve.go
Normal file
94
pkg/fileserver/plan9/serve.go
Normal file
@ -0,0 +1,94 @@
|
||||
package plan9
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hugelgupf/p9/fsimpl/localfs"
|
||||
"github.com/hugelgupf/p9/p9"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
server *p9.Server
|
||||
// TODO: Once server has a proper Close() we don't need this.
|
||||
// This is basically just a short-circuit to actually close the server
|
||||
// without that ability.
|
||||
listener net.Listener
|
||||
// Errors from the server being started will come out here.
|
||||
errChan chan error
|
||||
}
|
||||
|
||||
// Expose a single directory (and all children) via the given net.Listener.
|
||||
// Directory given must be an absolute path and must exist.
|
||||
func New9pServer(listener net.Listener, exposeDir string) (*Server, error) {
|
||||
// Verify that exposeDir makes sense.
|
||||
if !filepath.IsAbs(exposeDir) {
|
||||
return nil, fmt.Errorf("path to expose to machine must be absolute: %s", exposeDir)
|
||||
}
|
||||
stat, err := os.Stat(exposeDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot stat path to expose to machine: %w", err)
|
||||
}
|
||||
if !stat.IsDir() {
|
||||
return nil, fmt.Errorf("path to expose to machine must be a directory: %s", exposeDir)
|
||||
}
|
||||
|
||||
server := p9.NewServer(localfs.Attacher(exposeDir), []p9.ServerOpt{}...)
|
||||
if server == nil {
|
||||
return nil, fmt.Errorf("p9.NewServer returned nil")
|
||||
}
|
||||
|
||||
errChan := make(chan error)
|
||||
|
||||
// TODO: Use a channel to pass back this if it occurs within a
|
||||
// reasonable timeframe.
|
||||
go func() {
|
||||
errChan <- server.Serve(listener)
|
||||
close(errChan)
|
||||
}()
|
||||
|
||||
toReturn := new(Server)
|
||||
toReturn.listener = listener
|
||||
toReturn.server = server
|
||||
toReturn.errChan = errChan
|
||||
|
||||
// Just before returning, check to see if we got an error off server
|
||||
// startup.
|
||||
select {
|
||||
case err := <-errChan:
|
||||
return nil, fmt.Errorf("starting 9p server: %w", err)
|
||||
default:
|
||||
logrus.Infof("Successfully started 9p server for directory %s", exposeDir)
|
||||
}
|
||||
|
||||
return toReturn, nil
|
||||
}
|
||||
|
||||
// Stop a running server.
|
||||
// Please note that this does *BAD THINGS* to clients if they are still running
|
||||
// when the server stops. Processes get stuck in I/O deep sleep and zombify, and
|
||||
// nothing I do save restarting the VM can remove the zombies.
|
||||
func (s *Server) Stop() error {
|
||||
if s.server != nil {
|
||||
if err := s.listener.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
s.server = nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wait for an error from a running server.
|
||||
func (s *Server) WaitForError() error {
|
||||
if s.server != nil {
|
||||
err := <-s.errChan
|
||||
return err
|
||||
}
|
||||
|
||||
// Server already down, return nil
|
||||
return nil
|
||||
}
|
16
pkg/fileserver/server_unsupported.go
Normal file
16
pkg/fileserver/server_unsupported.go
Normal file
@ -0,0 +1,16 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package fileserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func StartShares(mounts map[string]string) error {
|
||||
if len(mounts) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("this platform does not support sharing directories")
|
||||
}
|
62
pkg/fileserver/server_windows.go
Normal file
62
pkg/fileserver/server_windows.go
Normal file
@ -0,0 +1,62 @@
|
||||
package fileserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containers/podman/v4/pkg/fileserver/plan9"
|
||||
"github.com/linuxkit/virtsock/pkg/hvsock"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Start serving the given shares on Windows HVSocks for use by a Hyper-V VM.
|
||||
// Mounts is formatted as a map of directory to be shared to vsock GUID.
|
||||
// The vsocks used must already be defined before StartShares is called; it's
|
||||
// expected that the vsocks will be created and torn down by the program calling
|
||||
// gvproxy.
|
||||
// TODO: The map here probably doesn't make sense.
|
||||
// In the future, possibly accept a struct instead, so we can accept things
|
||||
// other than a vsock and support non-Windows OSes.
|
||||
func StartShares(mounts map[string]string) (defErr error) {
|
||||
for path, guid := range mounts {
|
||||
service, err := hvsock.GUIDFromString(guid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing vsock guid %s: %w", guid, err)
|
||||
}
|
||||
|
||||
listener, err := hvsock.Listen(hvsock.Addr{
|
||||
VMID: hvsock.GUIDWildcard,
|
||||
ServiceID: service,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("retrieving listener for vsock %s: %w", guid, err)
|
||||
}
|
||||
|
||||
logrus.Debugf("Going to serve directory %s on vsock %s", path, guid)
|
||||
|
||||
server, err := plan9.New9pServer(listener, path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("serving directory %s on vsock %s: %w", path, guid, err)
|
||||
}
|
||||
defer func() {
|
||||
if defErr != nil {
|
||||
if err := server.Stop(); err != nil {
|
||||
logrus.Errorf("Error stopping 9p server: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
serverDir := path
|
||||
|
||||
go func() {
|
||||
if err := server.WaitForError(); err != nil {
|
||||
logrus.Errorf("Error from 9p server for %s: %v", serverDir, err)
|
||||
} else {
|
||||
// We do not expect server exits - this should
|
||||
// run until the program exits.
|
||||
logrus.Warnf("9p server for %s exited without error", serverDir)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -6,10 +6,12 @@ import (
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
loops = 5
|
||||
loops = 10
|
||||
sleepTime = time.Millisecond * 1
|
||||
)
|
||||
|
||||
@ -19,8 +21,10 @@ const (
|
||||
func backoffForProcess(pid int) error {
|
||||
sleepInterval := sleepTime
|
||||
for i := 0; i < loops; i++ {
|
||||
logrus.Debugf("Sleeping for %d milliseconds", sleepInterval.Milliseconds())
|
||||
proxyProc, err := findProcess(pid)
|
||||
if proxyProc == nil && err != nil {
|
||||
logrus.Debugf("Error opening process %d: %v", pid, err)
|
||||
// process is killed, gone
|
||||
return nil //nolint: nilerr
|
||||
}
|
||||
@ -35,6 +39,8 @@ func backoffForProcess(pid int) error {
|
||||
// process to not exist. if the sigterm does not end the process after an interval,
|
||||
// then sigkill is sent. it also waits for the process to exit after the sigkill too.
|
||||
func waitOnProcess(processID int) error {
|
||||
logrus.Infof("Going to stop gvproxy (PID %d)", processID)
|
||||
|
||||
proxyProc, err := findProcess(processID)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -58,10 +64,12 @@ func waitOnProcess(processID int) error {
|
||||
proxyProc, err = findProcess(processID)
|
||||
if proxyProc == nil && err != nil {
|
||||
// process is killed, gone
|
||||
logrus.Debugf("Error opening gvproxy process: %v", err)
|
||||
return nil //nolint: nilerr
|
||||
}
|
||||
if err := proxyProc.Signal(syscall.SIGKILL); err != nil {
|
||||
if err == syscall.ESRCH {
|
||||
logrus.Debugf("Gvproxy already dead, exiting cleanly")
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
|
@ -12,8 +12,10 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
"github.com/containers/common/pkg/config"
|
||||
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
|
||||
"github.com/containers/libhvee/pkg/hypervctl"
|
||||
@ -67,6 +69,9 @@ type HyperVMachine struct {
|
||||
LastUp time.Time
|
||||
// GVProxy will write its PID here
|
||||
GvProxyPid machine.VMFile
|
||||
// MountVsocks contains the currently-active vsocks, mapped to the
|
||||
// directory they should be mounted on.
|
||||
MountVsocks map[string]uint64
|
||||
// Used at runtime for serializing write operations
|
||||
lock *lockfile.LockFile
|
||||
}
|
||||
@ -215,6 +220,31 @@ func (m *HyperVMachine) Init(opts machine.InitOptions) (bool, error) {
|
||||
callbackFuncs.Add(m.ConfigPath.Delete)
|
||||
callbackFuncs.Add(m.unregisterMachine)
|
||||
|
||||
// Parsing here is confusing.
|
||||
// Basically, we have two paths: a source path, on the Windows machine,
|
||||
// with all that entails (drive letter, backslash separator, etc) and a
|
||||
// dest path, in the Linux machine, normal Unix semantics. They are
|
||||
// separated by a : character, with source path first, dest path second.
|
||||
// So we split on :, first two parts are guaranteed to be Windows (the
|
||||
// drive letter and file path), next one is Linux. Options, when we get
|
||||
// around to those, would be another : after that.
|
||||
// TODO: Need to support options here
|
||||
for _, mount := range opts.Volumes {
|
||||
newMount := machine.Mount{}
|
||||
|
||||
splitMount := strings.Split(mount, ":")
|
||||
if len(splitMount) < 3 {
|
||||
return false, fmt.Errorf("volumes must be specified as source:destination and must be absolute")
|
||||
}
|
||||
newMount.Target = splitMount[2]
|
||||
newMount.Source = strings.Join(splitMount[:2], ":")
|
||||
if len(splitMount) > 3 {
|
||||
return false, fmt.Errorf("volume options are not presently supported on Hyper-V")
|
||||
}
|
||||
|
||||
m.Mounts = append(m.Mounts, newMount)
|
||||
}
|
||||
|
||||
if err = m.addNetworkAndReadySocketsToRegistry(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -403,7 +433,7 @@ func (m *HyperVMachine) Remove(_ string, opts machine.RemoveOptions) (string, fu
|
||||
vmm := hypervctl.NewVirtualMachineManager()
|
||||
vm, err := vmm.GetMachine(m.Name)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
return "", nil, fmt.Errorf("getting virtual machine: %w", err)
|
||||
}
|
||||
// In hyperv, they call running 'enabled'
|
||||
if vm.State() == hypervctl.Enabled {
|
||||
@ -412,13 +442,19 @@ func (m *HyperVMachine) Remove(_ string, opts machine.RemoveOptions) (string, fu
|
||||
}
|
||||
// force stop bc we are destroying
|
||||
if err := vm.StopWithForce(); err != nil {
|
||||
return "", nil, err
|
||||
return "", nil, fmt.Errorf("stopping virtual machine: %w", err)
|
||||
}
|
||||
|
||||
// Update state on the VM by pulling its info again
|
||||
vm, err = vmm.GetMachine(m.Name)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("getting VM: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
vm, err = vmm.GetMachine(m.Name)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
// Tear down vsocks
|
||||
if err := m.removeShares(); err != nil {
|
||||
logrus.Errorf("Error removing vsock: %w", err)
|
||||
}
|
||||
|
||||
// Collect all the files that need to be destroyed
|
||||
@ -433,7 +469,10 @@ func (m *HyperVMachine) Remove(_ string, opts machine.RemoveOptions) (string, fu
|
||||
return confirmationMessage, func() error {
|
||||
machine.RemoveFilesAndConnections(files, m.Name, m.Name+"-root")
|
||||
m.removeNetworkAndReadySocketsFromRegistry()
|
||||
return vm.Remove("")
|
||||
if err := vm.Remove(""); err != nil {
|
||||
return fmt.Errorf("removing virtual machine: %w", err)
|
||||
}
|
||||
return nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -450,7 +489,7 @@ func (m *HyperVMachine) Set(name string, opts machine.SetOptions) ([]error, erro
|
||||
// Considering this a hard return if we cannot lookup the machine
|
||||
vm, err := vmm.GetMachine(m.Name)
|
||||
if err != nil {
|
||||
return setErrors, err
|
||||
return setErrors, fmt.Errorf("getting machine: %w", err)
|
||||
}
|
||||
if vm.State() != hypervctl.Disabled {
|
||||
return nil, errors.New("unable to change settings unless vm is stopped")
|
||||
@ -492,7 +531,7 @@ func (m *HyperVMachine) Set(name string, opts machine.SetOptions) ([]error, erro
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
setErrors = append(setErrors, err)
|
||||
setErrors = append(setErrors, fmt.Errorf("setting CPU and Memory for VM: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,6 +564,16 @@ func (m *HyperVMachine) Start(name string, opts machine.StartOptions) error {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
// Start 9p shares
|
||||
shares, err := m.createShares()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.MountVsocks = shares
|
||||
if err := m.writeConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vmm := hypervctl.NewVirtualMachineManager()
|
||||
vm, err := vmm.GetMachine(m.Name)
|
||||
if err != nil {
|
||||
@ -556,6 +605,10 @@ func (m *HyperVMachine) Start(name string, opts machine.StartOptions) error {
|
||||
// set starting back false now that we are running
|
||||
m.Starting = false
|
||||
|
||||
if err := m.startShares(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if m.HostUser.Modified {
|
||||
if machine.UpdatePodmanDockerSockService(m, name, m.UID, m.Rootful) == nil {
|
||||
// Reset modification state if there are no errors, otherwise ignore errors
|
||||
@ -591,7 +644,7 @@ func (m *HyperVMachine) Stop(name string, opts machine.StopOptions) error {
|
||||
vmm := hypervctl.NewVirtualMachineManager()
|
||||
vm, err := vmm.GetMachine(m.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("getting virtual machine: %w", err)
|
||||
}
|
||||
vmState := vm.State()
|
||||
if vm.State() == hypervctl.Disabled {
|
||||
@ -606,7 +659,7 @@ func (m *HyperVMachine) Stop(name string, opts machine.StopOptions) error {
|
||||
}
|
||||
|
||||
if err := vm.Stop(); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("stopping virtual machine: %w", err)
|
||||
}
|
||||
|
||||
// keep track of last up
|
||||
@ -696,22 +749,11 @@ func (m *HyperVMachine) startHostNetworking() (string, machine.APIForwardingStat
|
||||
return "", machine.NoForwarding, err
|
||||
}
|
||||
|
||||
dnr, dnw, err := machine.GetDevNullFiles()
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
return "", machine.NoForwarding, err
|
||||
return "", 0, fmt.Errorf("unable to locate executable: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := dnr.Close(); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
}()
|
||||
defer func() {
|
||||
if err := dnw.Close(); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
gvproxyBinary, err := cfg.FindHelperBinary("gvproxy.exe", false)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
@ -725,13 +767,58 @@ func (m *HyperVMachine) startHostNetworking() (string, machine.APIForwardingStat
|
||||
cmd, forwardSock, state = m.setupAPIForwarding(cmd)
|
||||
if logrus.IsLevelEnabled(logrus.DebugLevel) {
|
||||
cmd.Debug = true
|
||||
logrus.Debug(cmd)
|
||||
}
|
||||
|
||||
c := cmd.Cmd(gvproxyBinary)
|
||||
|
||||
if logrus.IsLevelEnabled(logrus.DebugLevel) {
|
||||
c.Stdout = os.Stdout
|
||||
c.Stderr = os.Stderr
|
||||
}
|
||||
|
||||
logrus.Debugf("Starting gvproxy with command: %s %v", gvproxyBinary, c.Args)
|
||||
|
||||
if err := c.Start(); err != nil {
|
||||
return "", 0, fmt.Errorf("unable to execute: %s: %w", cmd.ToCmdline(), err)
|
||||
}
|
||||
|
||||
logrus.Debugf("Got gvproxy PID as %d", c.Process.Pid)
|
||||
|
||||
if len(m.MountVsocks) == 0 {
|
||||
return forwardSock, state, nil
|
||||
}
|
||||
|
||||
// Start the 9p server in the background
|
||||
args := []string{}
|
||||
if logrus.IsLevelEnabled(logrus.DebugLevel) {
|
||||
args = append(args, "--log-level=debug")
|
||||
}
|
||||
args = append(args, "machine", "server9p")
|
||||
for dir, vsock := range m.MountVsocks {
|
||||
for _, mount := range m.Mounts {
|
||||
if mount.Target == dir {
|
||||
args = append(args, "--serve", fmt.Sprintf("%s:%s", mount.Source, winio.VsockServiceID(uint32(vsock)).String()))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
args = append(args, fmt.Sprintf("%d", c.Process.Pid))
|
||||
|
||||
logrus.Debugf("Going to start 9p server using command: %s %v", executable, args)
|
||||
|
||||
fsCmd := exec.Command(executable, args...)
|
||||
|
||||
if logrus.IsLevelEnabled(logrus.DebugLevel) {
|
||||
fsCmd.Stdout = os.Stdout
|
||||
fsCmd.Stderr = os.Stderr
|
||||
}
|
||||
|
||||
if err := fsCmd.Start(); err != nil {
|
||||
return "", 0, fmt.Errorf("unable to execute: %s %v: %w", executable, args, err)
|
||||
}
|
||||
|
||||
logrus.Infof("Started podman 9p server as PID %d", fsCmd.Process.Pid)
|
||||
|
||||
return forwardSock, state, nil
|
||||
}
|
||||
|
||||
@ -804,3 +891,88 @@ func (m *HyperVMachine) resizeDisk(newSize strongunits.GiB) error {
|
||||
func (m *HyperVMachine) isStarting() bool {
|
||||
return m.Starting
|
||||
}
|
||||
|
||||
func (m *HyperVMachine) createShares() (_ map[string]uint64, defErr error) {
|
||||
toReturn := make(map[string]uint64)
|
||||
|
||||
for _, mount := range m.Mounts {
|
||||
var vsock *HVSockRegistryEntry
|
||||
|
||||
vsockNum, ok := m.MountVsocks[mount.Target]
|
||||
if ok {
|
||||
// Ignore errors here, we'll just try and recreate the
|
||||
// vsock below.
|
||||
testVsock, err := LoadHVSockRegistryEntry(vsockNum)
|
||||
if err == nil {
|
||||
vsock = testVsock
|
||||
}
|
||||
}
|
||||
if vsock == nil {
|
||||
testVsock, err := NewHVSockRegistryEntry(m.Name, Fileserver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if defErr != nil {
|
||||
if err := testVsock.Remove(); err != nil {
|
||||
logrus.Errorf("Removing vsock: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
vsock = testVsock
|
||||
}
|
||||
|
||||
logrus.Debugf("Going to share directory %s via 9p on vsock %d", mount.Source, vsock.Port)
|
||||
|
||||
toReturn[mount.Target] = vsock.Port
|
||||
}
|
||||
|
||||
return toReturn, nil
|
||||
}
|
||||
|
||||
func (m *HyperVMachine) removeShares() error {
|
||||
var removalErr error
|
||||
|
||||
for _, mount := range m.Mounts {
|
||||
vsockNum, ok := m.MountVsocks[mount.Target]
|
||||
if !ok {
|
||||
// Mount doesn't have a valid vsock, no need to tear down
|
||||
continue
|
||||
}
|
||||
|
||||
vsock, err := LoadHVSockRegistryEntry(vsockNum)
|
||||
if err != nil {
|
||||
logrus.Debugf("Vsock %d for mountpoint %s does not have a valid registry entry, skipping removal", vsockNum, mount.Target)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := vsock.Remove(); err != nil {
|
||||
if removalErr != nil {
|
||||
logrus.Errorf("Error removing vsock: %w", removalErr)
|
||||
}
|
||||
removalErr = fmt.Errorf("removing vsock %d for mountpoint %s: %w", vsockNum, mount.Target, err)
|
||||
}
|
||||
}
|
||||
|
||||
return removalErr
|
||||
}
|
||||
|
||||
func (m *HyperVMachine) startShares() error {
|
||||
for mountpoint, sockNum := range m.MountVsocks {
|
||||
args := []string{"-q", "--", "sudo", "podman"}
|
||||
if logrus.IsLevelEnabled(logrus.DebugLevel) {
|
||||
args = append(args, "--log-level=debug")
|
||||
}
|
||||
args = append(args, "machine", "client9p", fmt.Sprintf("%d", sockNum), mountpoint)
|
||||
|
||||
sshOpts := machine.SSHOptions{
|
||||
Args: args,
|
||||
}
|
||||
|
||||
if err := m.SSH(m.Name, sshOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
@ -36,6 +37,8 @@ const (
|
||||
Network HVSockPurpose = iota
|
||||
// Events implies the sock is used for notification (like "Ready")
|
||||
Events
|
||||
// Fileserver implies that the sock is used for serving files from host to VM
|
||||
Fileserver
|
||||
)
|
||||
|
||||
func (hv HVSockPurpose) string() string {
|
||||
@ -44,6 +47,8 @@ func (hv HVSockPurpose) string() string {
|
||||
return "Network"
|
||||
case Events:
|
||||
return "Events"
|
||||
case Fileserver:
|
||||
return "Fileserver"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@ -58,6 +63,8 @@ func toHVSockPurpose(p string) (HVSockPurpose, error) {
|
||||
return Network, nil
|
||||
case "Events":
|
||||
return Events, nil
|
||||
case "Fileserver":
|
||||
return Fileserver, nil
|
||||
}
|
||||
return 0, fmt.Errorf("unknown hvsockpurpose: %s", p)
|
||||
}
|
||||
@ -239,14 +246,24 @@ func LoadHVSockRegistryEntry(port uint64) (*HVSockRegistryEntry, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Listen is used on the windows side to listen for anything to come
|
||||
// over the hvsock as a signal the vm is booted
|
||||
func (hv *HVSockRegistryEntry) Listen() error {
|
||||
// Listener returns a net.Listener for the given HvSock.
|
||||
func (hv *HVSockRegistryEntry) Listener() (net.Listener, error) {
|
||||
n := winio.HvsockAddr{
|
||||
VMID: winio.HvsockGUIDWildcard(), // When listening on the host side, use equiv of 0.0.0.0
|
||||
ServiceID: winio.VsockServiceID(uint32(hv.Port)),
|
||||
}
|
||||
listener, err := winio.ListenHvsock(&n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return listener, nil
|
||||
}
|
||||
|
||||
// Listen is used on the windows side to listen for anything to come
|
||||
// over the hvsock as a signal the vm is booted
|
||||
func (hv *HVSockRegistryEntry) Listen() error {
|
||||
listener, err := hv.Listener()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -7,17 +7,22 @@ import (
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func GetProcessState(pid int) (active bool, exitCode int) {
|
||||
const da = syscall.STANDARD_RIGHTS_READ | syscall.PROCESS_QUERY_INFORMATION | syscall.SYNCHRONIZE
|
||||
handle, err := syscall.OpenProcess(da, false, uint32(pid))
|
||||
if err != nil {
|
||||
logrus.Debugf("Error retrieving process %d: %v", pid, err)
|
||||
return false, int(syscall.ERROR_PROC_NOT_FOUND)
|
||||
}
|
||||
|
||||
var code uint32
|
||||
syscall.GetExitCodeProcess(handle, &code)
|
||||
if err := syscall.GetExitCodeProcess(handle, &code); err != nil {
|
||||
logrus.Errorf("Error retrieving process %d exit code: %v", pid, err)
|
||||
}
|
||||
return code == 259, int(code)
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/containers/podman/v4/libpod/define"
|
||||
"github.com/containers/podman/v4/pkg/rootless"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -118,3 +119,9 @@ func GetRootlessPauseProcessPidPath() (string, error) {
|
||||
// the tmpdir which can be changed via --tmpdir.
|
||||
return filepath.Join(runtimeDir, "libpod", "tmp", "pause.pid"), nil
|
||||
}
|
||||
|
||||
// WaitForPIDExit waits for a PID to exit.
|
||||
// Not implemented for Linux at this time, only for Windows.
|
||||
func WaitForPIDExit(pid uint) error {
|
||||
return define.ErrNotImplemented
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containers/storage/pkg/homedir"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var errNotImplemented = errors.New("not yet implemented")
|
||||
@ -44,3 +45,35 @@ func GetRuntimeDir() (string, error) {
|
||||
func GetRootlessConfigHomeDir() (string, error) {
|
||||
return "", errors.New("this function is not implemented for windows")
|
||||
}
|
||||
|
||||
// Wait until the given PID exits. Returns nil if wait was successful, errors on
|
||||
// unexpected condition (IE, pid was not valid)
|
||||
func WaitForPIDExit(pid uint) error {
|
||||
const PROCESS_ALL_ACCESS = 0x1F0FFF
|
||||
|
||||
// We need to turn the PID into a Windows handle.
|
||||
// To do this we need Windows' OpenProcess func.
|
||||
// To get that, we need to open the kernel32 DLL.
|
||||
kernel32, err := windows.LoadDLL("kernel32.dll")
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading kernel32 dll: %w", err)
|
||||
}
|
||||
|
||||
openProc, err := kernel32.FindProc("OpenProcess")
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading OpenProcess API: %w", err)
|
||||
}
|
||||
|
||||
handle, _, err := openProc.Call(uintptr(PROCESS_ALL_ACCESS), uintptr(1), uintptr(pid))
|
||||
if err != nil {
|
||||
return fmt.Errorf("converting PID to handle: %w", err)
|
||||
}
|
||||
|
||||
// We can now wait for the handle.
|
||||
_, err = windows.WaitForSingleObject(windows.Handle(handle), 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("waiting for handle: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
202
vendor/github.com/hugelgupf/p9/LICENSE
generated
vendored
Normal file
202
vendor/github.com/hugelgupf/p9/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
281
vendor/github.com/hugelgupf/p9/fsimpl/localfs/localfs.go
generated
vendored
Normal file
281
vendor/github.com/hugelgupf/p9/fsimpl/localfs/localfs.go
generated
vendored
Normal file
@ -0,0 +1,281 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package localfs exposes the host's local file system as a p9.File.
|
||||
package localfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/hugelgupf/p9/fsimpl/templatefs"
|
||||
"github.com/hugelgupf/p9/internal"
|
||||
"github.com/hugelgupf/p9/linux"
|
||||
"github.com/hugelgupf/p9/p9"
|
||||
)
|
||||
|
||||
type attacher struct {
|
||||
root string
|
||||
}
|
||||
|
||||
var (
|
||||
_ p9.Attacher = &attacher{}
|
||||
)
|
||||
|
||||
// RootAttacher attaches at the host file system's root.
|
||||
func RootAttacher() p9.Attacher {
|
||||
return &attacher{root: "/"}
|
||||
}
|
||||
|
||||
// Attacher returns an attacher that exposes files under root.
|
||||
func Attacher(root string) p9.Attacher {
|
||||
if len(root) == 0 {
|
||||
root = "/"
|
||||
}
|
||||
return &attacher{root: root}
|
||||
}
|
||||
|
||||
// Attach implements p9.Attacher.Attach.
|
||||
func (a *attacher) Attach() (p9.File, error) {
|
||||
umask(0)
|
||||
return &Local{path: a.root}, nil
|
||||
}
|
||||
|
||||
// Local is a p9.File.
|
||||
type Local struct {
|
||||
p9.DefaultWalkGetAttr
|
||||
templatefs.NoopFile
|
||||
|
||||
path string
|
||||
file *os.File
|
||||
}
|
||||
|
||||
var (
|
||||
_ p9.File = &Local{}
|
||||
)
|
||||
|
||||
// info constructs a QID for this file.
|
||||
func (l *Local) info() (p9.QID, os.FileInfo, error) {
|
||||
var (
|
||||
qid p9.QID
|
||||
fi os.FileInfo
|
||||
err error
|
||||
)
|
||||
|
||||
// Stat the file.
|
||||
if l.file != nil {
|
||||
fi, err = l.file.Stat()
|
||||
} else {
|
||||
fi, err = os.Lstat(l.path)
|
||||
}
|
||||
if err != nil {
|
||||
return qid, nil, err
|
||||
}
|
||||
|
||||
// Construct the QID type.
|
||||
qid.Type = p9.ModeFromOS(fi.Mode()).QIDType()
|
||||
|
||||
// Save the path from the Ino.
|
||||
ninePath, err := localToQid(l.path, fi)
|
||||
if err != nil {
|
||||
return qid, nil, err
|
||||
}
|
||||
|
||||
qid.Path = ninePath
|
||||
|
||||
return qid, fi, nil
|
||||
}
|
||||
|
||||
// Walk implements p9.File.Walk.
|
||||
func (l *Local) Walk(names []string) ([]p9.QID, p9.File, error) {
|
||||
var qids []p9.QID
|
||||
last := &Local{path: l.path}
|
||||
|
||||
// A walk with no names is a copy of self.
|
||||
if len(names) == 0 {
|
||||
return nil, last, nil
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
c := &Local{path: path.Join(last.path, name)}
|
||||
qid, _, err := c.info()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
qids = append(qids, qid)
|
||||
last = c
|
||||
}
|
||||
return qids, last, nil
|
||||
}
|
||||
|
||||
// FSync implements p9.File.FSync.
|
||||
func (l *Local) FSync() error {
|
||||
return l.file.Sync()
|
||||
}
|
||||
|
||||
// GetAttr implements p9.File.GetAttr.
|
||||
//
|
||||
// Not fully implemented.
|
||||
func (l *Local) GetAttr(req p9.AttrMask) (p9.QID, p9.AttrMask, p9.Attr, error) {
|
||||
qid, fi, err := l.info()
|
||||
if err != nil {
|
||||
return qid, p9.AttrMask{}, p9.Attr{}, err
|
||||
}
|
||||
|
||||
stat := internal.InfoToStat(fi)
|
||||
attr := &p9.Attr{
|
||||
Mode: p9.FileMode(stat.Mode),
|
||||
UID: p9.UID(stat.Uid),
|
||||
GID: p9.GID(stat.Gid),
|
||||
NLink: p9.NLink(stat.Nlink),
|
||||
RDev: p9.Dev(stat.Rdev),
|
||||
Size: uint64(stat.Size),
|
||||
BlockSize: uint64(stat.Blksize),
|
||||
Blocks: uint64(stat.Blocks),
|
||||
ATimeSeconds: uint64(stat.Atim.Sec),
|
||||
ATimeNanoSeconds: uint64(stat.Atim.Nsec),
|
||||
MTimeSeconds: uint64(stat.Mtim.Sec),
|
||||
MTimeNanoSeconds: uint64(stat.Mtim.Nsec),
|
||||
CTimeSeconds: uint64(stat.Ctim.Sec),
|
||||
CTimeNanoSeconds: uint64(stat.Ctim.Nsec),
|
||||
}
|
||||
return qid, req, *attr, nil
|
||||
}
|
||||
|
||||
// Close implements p9.File.Close.
|
||||
func (l *Local) Close() error {
|
||||
if l.file != nil {
|
||||
// We don't set l.file = nil, as Close is called by servers
|
||||
// only in Clunk. Clunk should release the last (direct)
|
||||
// reference to this file.
|
||||
return l.file.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Open implements p9.File.Open.
|
||||
func (l *Local) Open(mode p9.OpenFlags) (p9.QID, uint32, error) {
|
||||
qid, _, err := l.info()
|
||||
if err != nil {
|
||||
return qid, 0, err
|
||||
}
|
||||
|
||||
// Do the actual open.
|
||||
f, err := os.OpenFile(l.path, int(mode), 0)
|
||||
if err != nil {
|
||||
return qid, 0, err
|
||||
}
|
||||
l.file = f
|
||||
|
||||
return qid, 0, nil
|
||||
}
|
||||
|
||||
// ReadAt implements p9.File.ReadAt.
|
||||
func (l *Local) ReadAt(p []byte, offset int64) (int, error) {
|
||||
return l.file.ReadAt(p, offset)
|
||||
}
|
||||
|
||||
// Lock implements p9.File.Lock.
|
||||
func (l *Local) Lock(pid int, locktype p9.LockType, flags p9.LockFlags, start, length uint64, client string) (p9.LockStatus, error) {
|
||||
return l.lock(pid, locktype, flags, start, length, client)
|
||||
}
|
||||
|
||||
// WriteAt implements p9.File.WriteAt.
|
||||
func (l *Local) WriteAt(p []byte, offset int64) (int, error) {
|
||||
return l.file.WriteAt(p, offset)
|
||||
}
|
||||
|
||||
// Create implements p9.File.Create.
|
||||
func (l *Local) Create(name string, mode p9.OpenFlags, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.File, p9.QID, uint32, error) {
|
||||
newName := path.Join(l.path, name)
|
||||
f, err := os.OpenFile(newName, int(mode)|os.O_CREATE|os.O_EXCL, os.FileMode(permissions))
|
||||
if err != nil {
|
||||
return nil, p9.QID{}, 0, err
|
||||
}
|
||||
|
||||
l2 := &Local{path: newName, file: f}
|
||||
qid, _, err := l2.info()
|
||||
if err != nil {
|
||||
l2.Close()
|
||||
return nil, p9.QID{}, 0, err
|
||||
}
|
||||
return l2, qid, 0, nil
|
||||
}
|
||||
|
||||
// Mkdir implements p9.File.Mkdir.
|
||||
//
|
||||
// Not properly implemented.
|
||||
func (l *Local) Mkdir(name string, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.QID, error) {
|
||||
if err := os.Mkdir(path.Join(l.path, name), os.FileMode(permissions)); err != nil {
|
||||
return p9.QID{}, err
|
||||
}
|
||||
|
||||
// Blank QID.
|
||||
return p9.QID{}, nil
|
||||
}
|
||||
|
||||
// Symlink implements p9.File.Symlink.
|
||||
//
|
||||
// Not properly implemented.
|
||||
func (l *Local) Symlink(oldname string, newname string, _ p9.UID, _ p9.GID) (p9.QID, error) {
|
||||
if err := os.Symlink(oldname, path.Join(l.path, newname)); err != nil {
|
||||
return p9.QID{}, err
|
||||
}
|
||||
|
||||
// Blank QID.
|
||||
return p9.QID{}, nil
|
||||
}
|
||||
|
||||
// Link implements p9.File.Link.
|
||||
//
|
||||
// Not properly implemented.
|
||||
func (l *Local) Link(target p9.File, newname string) error {
|
||||
return os.Link(target.(*Local).path, path.Join(l.path, newname))
|
||||
}
|
||||
|
||||
// RenameAt implements p9.File.RenameAt.
|
||||
func (l *Local) RenameAt(oldName string, newDir p9.File, newName string) error {
|
||||
oldPath := path.Join(l.path, oldName)
|
||||
newPath := path.Join(newDir.(*Local).path, newName)
|
||||
|
||||
return os.Rename(oldPath, newPath)
|
||||
}
|
||||
|
||||
// Readlink implements p9.File.Readlink.
|
||||
//
|
||||
// Not properly implemented.
|
||||
func (l *Local) Readlink() (string, error) {
|
||||
return os.Readlink(l.path)
|
||||
}
|
||||
|
||||
// Renamed implements p9.File.Renamed.
|
||||
func (l *Local) Renamed(parent p9.File, newName string) {
|
||||
l.path = path.Join(parent.(*Local).path, newName)
|
||||
}
|
||||
|
||||
// SetAttr implements p9.File.SetAttr.
|
||||
func (l *Local) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error {
|
||||
// When truncate(2) is called on Linux, Linux will try to set time & size. Fake it. Sorry.
|
||||
supported := p9.SetAttrMask{Size: true, MTime: true, CTime: true}
|
||||
if !valid.IsSubsetOf(supported) {
|
||||
return linux.ENOSYS
|
||||
}
|
||||
|
||||
if valid.Size {
|
||||
// If more than one thing is ever implemented, we can't just
|
||||
// return an error here.
|
||||
return os.Truncate(l.path, int64(attr.Size))
|
||||
}
|
||||
return nil
|
||||
}
|
50
vendor/github.com/hugelgupf/p9/fsimpl/localfs/readdir.go
generated
vendored
Normal file
50
vendor/github.com/hugelgupf/p9/fsimpl/localfs/readdir.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
package localfs
|
||||
|
||||
import (
|
||||
"io"
|
||||
"path"
|
||||
|
||||
"github.com/hugelgupf/p9/p9"
|
||||
)
|
||||
|
||||
// Readdir implements p9.File.Readdir.
|
||||
func (l *Local) Readdir(offset uint64, count uint32) (p9.Dirents, error) {
|
||||
var (
|
||||
p9Ents = make([]p9.Dirent, 0)
|
||||
cursor = uint64(0)
|
||||
)
|
||||
|
||||
for len(p9Ents) < int(count) {
|
||||
singleEnt, err := l.file.Readdirnames(1)
|
||||
|
||||
if err == io.EOF {
|
||||
return p9Ents, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// we consumed an entry
|
||||
cursor++
|
||||
|
||||
// cursor \in (offset, offset+count)
|
||||
if cursor < offset || cursor > offset+uint64(count) {
|
||||
continue
|
||||
}
|
||||
|
||||
name := singleEnt[0]
|
||||
|
||||
localEnt := Local{path: path.Join(l.path, name)}
|
||||
qid, _, err := localEnt.info()
|
||||
if err != nil {
|
||||
return p9Ents, err
|
||||
}
|
||||
p9Ents = append(p9Ents, p9.Dirent{
|
||||
QID: qid,
|
||||
Type: qid.Type,
|
||||
Name: name,
|
||||
Offset: cursor,
|
||||
})
|
||||
}
|
||||
|
||||
return p9Ents, nil
|
||||
}
|
40
vendor/github.com/hugelgupf/p9/fsimpl/localfs/system_unix.go
generated
vendored
Normal file
40
vendor/github.com/hugelgupf/p9/fsimpl/localfs/system_unix.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package localfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/hugelgupf/p9/p9"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func umask(mask int) int {
|
||||
return syscall.Umask(mask)
|
||||
}
|
||||
|
||||
func localToQid(_ string, fi os.FileInfo) (uint64, error) {
|
||||
return uint64(fi.Sys().(*syscall.Stat_t).Ino), nil
|
||||
}
|
||||
|
||||
// lock implements p9.File.Lock.
|
||||
func (l *Local) lock(pid int, locktype p9.LockType, flags p9.LockFlags, start, length uint64, client string) (p9.LockStatus, error) {
|
||||
switch locktype {
|
||||
case p9.ReadLock, p9.WriteLock:
|
||||
if err := unix.Flock(int(l.file.Fd()), unix.LOCK_EX); err != nil {
|
||||
return p9.LockStatusError, nil
|
||||
}
|
||||
|
||||
case p9.Unlock:
|
||||
if err := unix.Flock(int(l.file.Fd()), unix.LOCK_EX); err != nil {
|
||||
return p9.LockStatusError, nil
|
||||
}
|
||||
|
||||
default:
|
||||
return p9.LockStatusOK, unix.ENOSYS
|
||||
}
|
||||
|
||||
return p9.LockStatusOK, nil
|
||||
}
|
50
vendor/github.com/hugelgupf/p9/fsimpl/localfs/system_windows.go
generated
vendored
Normal file
50
vendor/github.com/hugelgupf/p9/fsimpl/localfs/system_windows.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
package localfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/hugelgupf/p9/linux"
|
||||
"github.com/hugelgupf/p9/p9"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func umask(_ int) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func localToQid(path string, info os.FileInfo) (uint64, error) {
|
||||
pathPtr, err := windows.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var (
|
||||
access uint32 // none; we only need metadata
|
||||
sharemode uint32
|
||||
createmode uint32 = windows.OPEN_EXISTING
|
||||
attribute uint32 = windows.FILE_ATTRIBUTE_NORMAL
|
||||
)
|
||||
if info.IsDir() {
|
||||
attribute = windows.FILE_FLAG_BACKUP_SEMANTICS
|
||||
}
|
||||
fd, err := windows.CreateFile(pathPtr, access, sharemode, nil, createmode, attribute, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
fi := &windows.ByHandleFileInformation{}
|
||||
if err = windows.GetFileInformationByHandle(fd, fi); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
x := uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow)
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// lock implements p9.File.Lock.
|
||||
// As in FreeBSD NFS locking, we just say "sure, we did it" without actually
|
||||
// doing anything; this lock design makes even less sense on Windows than
|
||||
// it does on Linux (pid? really? what were they thinking?)
|
||||
func (l *Local) lock(pid int, locktype p9.LockType, flags p9.LockFlags, start, length uint64, client string) (p9.LockStatus, error) {
|
||||
return p9.LockStatusOK, linux.ENOSYS
|
||||
}
|
25
vendor/github.com/hugelgupf/p9/fsimpl/localfs/system_xattr.go
generated
vendored
Normal file
25
vendor/github.com/hugelgupf/p9/fsimpl/localfs/system_xattr.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
//go:build unix && !solaris && !openbsd
|
||||
|
||||
package localfs
|
||||
|
||||
import (
|
||||
"github.com/hugelgupf/p9/fsimpl/xattr"
|
||||
"github.com/hugelgupf/p9/p9"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (l *Local) SetXattr(attr string, data []byte, flags p9.XattrFlags) error {
|
||||
return unix.Setxattr(l.path, attr, data, int(flags))
|
||||
}
|
||||
|
||||
func (l *Local) ListXattrs() ([]string, error) {
|
||||
return xattr.List(l.path)
|
||||
}
|
||||
|
||||
func (l *Local) GetXattr(attr string) ([]byte, error) {
|
||||
return xattr.Get(l.path, attr)
|
||||
}
|
||||
|
||||
func (l *Local) RemoveXattr(attr string) error {
|
||||
return unix.Removexattr(l.path, attr)
|
||||
}
|
194
vendor/github.com/hugelgupf/p9/fsimpl/templatefs/readonly.go
generated
vendored
Normal file
194
vendor/github.com/hugelgupf/p9/fsimpl/templatefs/readonly.go
generated
vendored
Normal file
@ -0,0 +1,194 @@
|
||||
package templatefs
|
||||
|
||||
import (
|
||||
"github.com/hugelgupf/p9/linux"
|
||||
"github.com/hugelgupf/p9/p9"
|
||||
)
|
||||
|
||||
// NotSymlinkFile denies Readlink with EINVAL.
|
||||
//
|
||||
// EINVAL is returned by readlink(2) when the file is not a symlink.
|
||||
type NotSymlinkFile struct{}
|
||||
|
||||
// Readlink implements p9.File.Readlink.
|
||||
func (NotSymlinkFile) Readlink() (string, error) {
|
||||
return "", linux.EINVAL
|
||||
}
|
||||
|
||||
// NotDirectoryFile denies any directory operations with ENOTDIR.
|
||||
//
|
||||
// Those operations are Create, Mkdir, Symlink, Link, Mknod, RenameAt,
|
||||
// UnlinkAt, and Readdir.
|
||||
type NotDirectoryFile struct{}
|
||||
|
||||
// Create implements p9.File.Create.
|
||||
func (NotDirectoryFile) Create(name string, mode p9.OpenFlags, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.File, p9.QID, uint32, error) {
|
||||
return nil, p9.QID{}, 0, linux.ENOTDIR
|
||||
}
|
||||
|
||||
// Mkdir implements p9.File.Mkdir.
|
||||
func (NotDirectoryFile) Mkdir(name string, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.QID, error) {
|
||||
return p9.QID{}, linux.ENOTDIR
|
||||
}
|
||||
|
||||
// Symlink implements p9.File.Symlink.
|
||||
func (NotDirectoryFile) Symlink(oldname string, newname string, _ p9.UID, _ p9.GID) (p9.QID, error) {
|
||||
return p9.QID{}, linux.ENOTDIR
|
||||
}
|
||||
|
||||
// Link implements p9.File.Link.
|
||||
func (NotDirectoryFile) Link(target p9.File, newname string) error {
|
||||
return linux.ENOTDIR
|
||||
}
|
||||
|
||||
// Mknod implements p9.File.Mknod.
|
||||
func (NotDirectoryFile) Mknod(name string, mode p9.FileMode, major uint32, minor uint32, _ p9.UID, _ p9.GID) (p9.QID, error) {
|
||||
return p9.QID{}, linux.ENOTDIR
|
||||
}
|
||||
|
||||
// RenameAt implements p9.File.RenameAt.
|
||||
func (NotDirectoryFile) RenameAt(oldname string, newdir p9.File, newname string) error {
|
||||
return linux.ENOTDIR
|
||||
}
|
||||
|
||||
// UnlinkAt implements p9.File.UnlinkAt.
|
||||
func (NotDirectoryFile) UnlinkAt(name string, flags uint32) error {
|
||||
return linux.ENOTDIR
|
||||
}
|
||||
|
||||
// Readdir implements p9.File.Readdir.
|
||||
func (NotDirectoryFile) Readdir(offset uint64, count uint32) (p9.Dirents, error) {
|
||||
return nil, linux.ENOTDIR
|
||||
}
|
||||
|
||||
// ReadOnlyFile returns default denials for all methods except Open, ReadAt,
|
||||
// Walk, Close, and GetAttr.
|
||||
//
|
||||
// Returns EROFS for most modifying operations, ENOTDIR for file creation ops
|
||||
// or readdir, EINVAL for readlink, xattr and lock operations return ENOSYS.
|
||||
//
|
||||
// Does nothing for Renamed.
|
||||
type ReadOnlyFile struct {
|
||||
NotSymlinkFile
|
||||
NotDirectoryFile
|
||||
XattrUnimplemented
|
||||
NoopRenamed
|
||||
NotLockable
|
||||
}
|
||||
|
||||
// FSync implements p9.File.FSync.
|
||||
func (ReadOnlyFile) FSync() error {
|
||||
return linux.EROFS
|
||||
}
|
||||
|
||||
// SetAttr implements p9.File.SetAttr.
|
||||
func (ReadOnlyFile) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error {
|
||||
return linux.EROFS
|
||||
}
|
||||
|
||||
// Remove implements p9.File.Remove.
|
||||
func (ReadOnlyFile) Remove() error {
|
||||
return linux.EROFS
|
||||
}
|
||||
|
||||
// Rename implements p9.File.Rename.
|
||||
func (ReadOnlyFile) Rename(directory p9.File, name string) error {
|
||||
return linux.EROFS
|
||||
}
|
||||
|
||||
// WriteAt implements p9.File.WriteAt.
|
||||
func (ReadOnlyFile) WriteAt(p []byte, offset int64) (int, error) {
|
||||
return 0, linux.EROFS
|
||||
}
|
||||
|
||||
// Flush implements p9.File.Flush.
|
||||
func (ReadOnlyFile) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadOnlyDir implements default denials for all methods except Walk, Open,
|
||||
// GetAttr, Readdir, Close.
|
||||
//
|
||||
// Creation operations return EROFS. Read/write operations return EISDIR.
|
||||
// EINVAL for readlink. Renaming does nothing by default, xattr and locking are
|
||||
// unimplemented.
|
||||
type ReadOnlyDir struct {
|
||||
NotSymlinkFile
|
||||
IsDir
|
||||
XattrUnimplemented
|
||||
NoopRenamed
|
||||
NotLockable
|
||||
}
|
||||
|
||||
// Create implements p9.File.Create.
|
||||
func (ReadOnlyDir) Create(name string, mode p9.OpenFlags, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.File, p9.QID, uint32, error) {
|
||||
return nil, p9.QID{}, 0, linux.EROFS
|
||||
}
|
||||
|
||||
// Mkdir implements p9.File.Mkdir.
|
||||
func (ReadOnlyDir) Mkdir(name string, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.QID, error) {
|
||||
return p9.QID{}, linux.EROFS
|
||||
}
|
||||
|
||||
// Symlink implements p9.File.Symlink.
|
||||
func (ReadOnlyDir) Symlink(oldname string, newname string, _ p9.UID, _ p9.GID) (p9.QID, error) {
|
||||
return p9.QID{}, linux.EROFS
|
||||
}
|
||||
|
||||
// Link implements p9.File.Link.
|
||||
func (ReadOnlyDir) Link(target p9.File, newname string) error {
|
||||
return linux.EROFS
|
||||
}
|
||||
|
||||
// Mknod implements p9.File.Mknod.
|
||||
func (ReadOnlyDir) Mknod(name string, mode p9.FileMode, major uint32, minor uint32, _ p9.UID, _ p9.GID) (p9.QID, error) {
|
||||
return p9.QID{}, linux.EROFS
|
||||
}
|
||||
|
||||
// RenameAt implements p9.File.RenameAt.
|
||||
func (ReadOnlyDir) RenameAt(oldname string, newdir p9.File, newname string) error {
|
||||
return linux.EROFS
|
||||
}
|
||||
|
||||
// UnlinkAt implements p9.File.UnlinkAt.
|
||||
func (ReadOnlyDir) UnlinkAt(name string, flags uint32) error {
|
||||
return linux.EROFS
|
||||
}
|
||||
|
||||
// Readdir implements p9.File.Readdir.
|
||||
func (ReadOnlyDir) Readdir(offset uint64, count uint32) (p9.Dirents, error) {
|
||||
return nil, linux.EROFS
|
||||
}
|
||||
|
||||
// FSync implements p9.File.FSync.
|
||||
func (ReadOnlyDir) FSync() error {
|
||||
return linux.EROFS
|
||||
}
|
||||
|
||||
// SetAttr implements p9.File.SetAttr.
|
||||
func (ReadOnlyDir) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error {
|
||||
return linux.EROFS
|
||||
}
|
||||
|
||||
// Remove implements p9.File.Remove.
|
||||
func (ReadOnlyDir) Remove() error {
|
||||
return linux.EROFS
|
||||
}
|
||||
|
||||
// Rename implements p9.File.Rename.
|
||||
func (ReadOnlyDir) Rename(directory p9.File, name string) error {
|
||||
return linux.EROFS
|
||||
}
|
||||
|
||||
// IsDir returns EISDIR for ReadAt and WriteAt.
|
||||
type IsDir struct{}
|
||||
|
||||
// WriteAt implements p9.File.WriteAt.
|
||||
func (IsDir) WriteAt(p []byte, offset int64) (int, error) {
|
||||
return 0, linux.EISDIR
|
||||
}
|
||||
|
||||
// ReadAt implements p9.File.ReadAt.
|
||||
func (IsDir) ReadAt(p []byte, offset int64) (int, error) {
|
||||
return 0, linux.EISDIR
|
||||
}
|
193
vendor/github.com/hugelgupf/p9/fsimpl/templatefs/unimplfs.go
generated
vendored
Normal file
193
vendor/github.com/hugelgupf/p9/fsimpl/templatefs/unimplfs.go
generated
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package templatefs provides template p9.Files.
|
||||
//
|
||||
// NoopFile can be used to leave some methods unimplemented in incomplete
|
||||
// p9.File implementations.
|
||||
//
|
||||
// NilCloser, ReadOnlyFile, NotDirectoryFile, and NotSymlinkFile can be used as
|
||||
// templates for commonly implemented file types. They are careful not to
|
||||
// conflict with each others' methods, so they can be embedded together.
|
||||
package templatefs
|
||||
|
||||
import (
|
||||
"github.com/hugelgupf/p9/linux"
|
||||
"github.com/hugelgupf/p9/p9"
|
||||
)
|
||||
|
||||
// NilCloser returns nil for Close.
|
||||
type NilCloser struct{}
|
||||
|
||||
// Close implements p9.File.Close.
|
||||
func (NilCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NilSyncer returns nil for FSync.
|
||||
type NilSyncer struct{}
|
||||
|
||||
// FSync implements p9.File.FSync.
|
||||
func (NilSyncer) FSync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NoopRenamed does nothing when the file is renamed.
|
||||
type NoopRenamed struct{}
|
||||
|
||||
// Renamed implements p9.File.Renamed.
|
||||
func (NoopRenamed) Renamed(parent p9.File, newName string) {}
|
||||
|
||||
// NotImplementedFile is a p9.File that returns ENOSYS for every listed method.
|
||||
//
|
||||
// Compatible with NoopRenamed, NilCloser, and NilSyncer.
|
||||
type NotImplementedFile struct {
|
||||
p9.DefaultWalkGetAttr
|
||||
NotLockable
|
||||
XattrUnimplemented
|
||||
}
|
||||
|
||||
// NoopFile is a p9.File with every method unimplemented.
|
||||
type NoopFile struct {
|
||||
NotImplementedFile
|
||||
NilCloser
|
||||
NilSyncer
|
||||
NoopRenamed
|
||||
}
|
||||
|
||||
var (
|
||||
_ p9.File = &NoopFile{}
|
||||
)
|
||||
|
||||
// Walk implements p9.File.Walk.
|
||||
func (NotImplementedFile) Walk(names []string) ([]p9.QID, p9.File, error) {
|
||||
return nil, nil, linux.ENOSYS
|
||||
}
|
||||
|
||||
// StatFS implements p9.File.StatFS.
|
||||
//
|
||||
// Not implemented.
|
||||
func (NotImplementedFile) StatFS() (p9.FSStat, error) {
|
||||
return p9.FSStat{}, linux.ENOSYS
|
||||
}
|
||||
|
||||
// Open implements p9.File.Open.
|
||||
func (NotImplementedFile) Open(mode p9.OpenFlags) (p9.QID, uint32, error) {
|
||||
return p9.QID{}, 0, linux.ENOSYS
|
||||
}
|
||||
|
||||
// ReadAt implements p9.File.ReadAt.
|
||||
func (NotImplementedFile) ReadAt(p []byte, offset int64) (int, error) {
|
||||
return 0, linux.ENOSYS
|
||||
}
|
||||
|
||||
// GetAttr implements p9.File.GetAttr.
|
||||
func (NotImplementedFile) GetAttr(req p9.AttrMask) (p9.QID, p9.AttrMask, p9.Attr, error) {
|
||||
return p9.QID{}, p9.AttrMask{}, p9.Attr{}, linux.ENOSYS
|
||||
}
|
||||
|
||||
// SetAttr implements p9.File.SetAttr.
|
||||
func (NotImplementedFile) SetAttr(valid p9.SetAttrMask, attr p9.SetAttr) error {
|
||||
return linux.ENOSYS
|
||||
}
|
||||
|
||||
// Remove implements p9.File.Remove.
|
||||
func (NotImplementedFile) Remove() error {
|
||||
return linux.ENOSYS
|
||||
}
|
||||
|
||||
// Rename implements p9.File.Rename.
|
||||
func (NotImplementedFile) Rename(directory p9.File, name string) error {
|
||||
return linux.ENOSYS
|
||||
}
|
||||
|
||||
// WriteAt implements p9.File.WriteAt.
|
||||
func (NotImplementedFile) WriteAt(p []byte, offset int64) (int, error) {
|
||||
return 0, linux.ENOSYS
|
||||
}
|
||||
|
||||
// Create implements p9.File.Create.
|
||||
func (NotImplementedFile) Create(name string, mode p9.OpenFlags, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.File, p9.QID, uint32, error) {
|
||||
return nil, p9.QID{}, 0, linux.ENOSYS
|
||||
}
|
||||
|
||||
// Mkdir implements p9.File.Mkdir.
|
||||
func (NotImplementedFile) Mkdir(name string, permissions p9.FileMode, _ p9.UID, _ p9.GID) (p9.QID, error) {
|
||||
return p9.QID{}, linux.ENOSYS
|
||||
}
|
||||
|
||||
// Symlink implements p9.File.Symlink.
|
||||
func (NotImplementedFile) Symlink(oldname string, newname string, _ p9.UID, _ p9.GID) (p9.QID, error) {
|
||||
return p9.QID{}, linux.ENOSYS
|
||||
}
|
||||
|
||||
// Link implements p9.File.Link.
|
||||
func (NotImplementedFile) Link(target p9.File, newname string) error {
|
||||
return linux.ENOSYS
|
||||
}
|
||||
|
||||
// Mknod implements p9.File.Mknod.
|
||||
func (NotImplementedFile) Mknod(name string, mode p9.FileMode, major uint32, minor uint32, _ p9.UID, _ p9.GID) (p9.QID, error) {
|
||||
return p9.QID{}, linux.ENOSYS
|
||||
}
|
||||
|
||||
// RenameAt implements p9.File.RenameAt.
|
||||
func (NotImplementedFile) RenameAt(oldname string, newdir p9.File, newname string) error {
|
||||
return linux.ENOSYS
|
||||
}
|
||||
|
||||
// UnlinkAt implements p9.File.UnlinkAt.
|
||||
func (NotImplementedFile) UnlinkAt(name string, flags uint32) error {
|
||||
return linux.ENOSYS
|
||||
}
|
||||
|
||||
// Readdir implements p9.File.Readdir.
|
||||
func (NotImplementedFile) Readdir(offset uint64, count uint32) (p9.Dirents, error) {
|
||||
return nil, linux.ENOSYS
|
||||
}
|
||||
|
||||
// Readlink implements p9.File.Readlink.
|
||||
func (NotImplementedFile) Readlink() (string, error) {
|
||||
return "", linux.ENOSYS
|
||||
}
|
||||
|
||||
// XattrUnimplemented implements Xattr methods returning ENOSYS.
|
||||
type XattrUnimplemented struct{}
|
||||
|
||||
// SetXattr implements p9.File.SetXattr.
|
||||
func (XattrUnimplemented) SetXattr(attr string, data []byte, flags p9.XattrFlags) error {
|
||||
return linux.ENOSYS
|
||||
}
|
||||
|
||||
// GetXattr implements p9.File.GetXattr.
|
||||
func (XattrUnimplemented) GetXattr(attr string) ([]byte, error) {
|
||||
return nil, linux.ENOSYS
|
||||
}
|
||||
|
||||
// ListXattrs implements p9.File.ListXattrs.
|
||||
func (XattrUnimplemented) ListXattrs() ([]string, error) {
|
||||
return nil, linux.ENOSYS
|
||||
}
|
||||
|
||||
// RemoveXattr implements p9.File.RemoveXattr.
|
||||
func (XattrUnimplemented) RemoveXattr(attr string) error {
|
||||
return linux.ENOSYS
|
||||
}
|
||||
|
||||
type NotLockable struct{}
|
||||
|
||||
// Lock implements p9.File.Lock.
|
||||
func (NotLockable) Lock(pid int, locktype p9.LockType, flags p9.LockFlags, start, length uint64, client string) (p9.LockStatus, error) {
|
||||
return p9.LockStatusOK, linux.ENOSYS
|
||||
}
|
39
vendor/github.com/hugelgupf/p9/fsimpl/xattr/xattr.go
generated
vendored
Normal file
39
vendor/github.com/hugelgupf/p9/fsimpl/xattr/xattr.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
//go:build unix && !openbsd && !solaris
|
||||
|
||||
package xattr
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func List(p string) ([]string, error) {
|
||||
sz, err := unix.Listxattr(p, nil)
|
||||
if err != nil {
|
||||
return nil, &fs.PathError{Op: "listxattr-get-size", Path: p, Err: err}
|
||||
}
|
||||
|
||||
b := make([]byte, sz)
|
||||
sz, err = unix.Listxattr(p, b)
|
||||
if err != nil {
|
||||
return nil, &fs.PathError{Op: "listxattr", Path: p, Err: err}
|
||||
}
|
||||
|
||||
return strings.Split(strings.Trim(string(b[:sz]), "\000"), "\000"), nil
|
||||
}
|
||||
|
||||
func Get(p string, attr string) ([]byte, error) {
|
||||
sz, err := unix.Getxattr(p, attr, nil)
|
||||
if err != nil {
|
||||
return nil, &fs.PathError{Op: "getxattr-get-size", Path: p, Err: err}
|
||||
}
|
||||
|
||||
b := make([]byte, sz)
|
||||
sz, err = unix.Getxattr(p, attr, b)
|
||||
if err != nil {
|
||||
return nil, &fs.PathError{Op: "getxattr", Path: p, Err: err}
|
||||
}
|
||||
return b[:sz], nil
|
||||
}
|
11
vendor/github.com/hugelgupf/p9/fsimpl/xattr/xattr_windows.go
generated
vendored
Normal file
11
vendor/github.com/hugelgupf/p9/fsimpl/xattr/xattr_windows.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
package xattr
|
||||
|
||||
import "github.com/hugelgupf/p9/linux"
|
||||
|
||||
func List(p string) ([]string, error) {
|
||||
return nil, linux.ENOSYS
|
||||
}
|
||||
|
||||
func Get(p string, attr string) ([]byte, error) {
|
||||
return nil, linux.ENOSYS
|
||||
}
|
2
vendor/github.com/hugelgupf/p9/internal/doc.go
generated
vendored
Normal file
2
vendor/github.com/hugelgupf/p9/internal/doc.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Package sys abstracts operating system features for p9.
|
||||
package internal
|
31
vendor/github.com/hugelgupf/p9/internal/stat_bsd.go
generated
vendored
Normal file
31
vendor/github.com/hugelgupf/p9/internal/stat_bsd.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
//go:build freebsd || darwin || netbsd
|
||||
// +build freebsd darwin netbsd
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// InfoToStat takes a platform native FileInfo and converts it into a 9P2000.L compatible Stat_t
|
||||
func InfoToStat(fi os.FileInfo) *Stat_t {
|
||||
nativeStat := fi.Sys().(*syscall.Stat_t)
|
||||
return &Stat_t{
|
||||
Dev: nativeStat.Dev,
|
||||
Ino: nativeStat.Ino,
|
||||
Nlink: nativeStat.Nlink,
|
||||
Mode: nativeStat.Mode,
|
||||
Uid: nativeStat.Uid,
|
||||
Gid: nativeStat.Gid,
|
||||
Rdev: nativeStat.Rdev,
|
||||
Size: nativeStat.Size,
|
||||
Blksize: nativeStat.Blksize,
|
||||
Blocks: nativeStat.Blocks,
|
||||
Atim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Atimespec)),
|
||||
Mtim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Mtimespec)),
|
||||
Ctim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Ctimespec)),
|
||||
}
|
||||
}
|
28
vendor/github.com/hugelgupf/p9/internal/stat_openbsd.go
generated
vendored
Normal file
28
vendor/github.com/hugelgupf/p9/internal/stat_openbsd.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// InfoToStat takes a platform native FileInfo and converts it into a 9P2000.L compatible Stat_t
|
||||
func InfoToStat(fi os.FileInfo) *Stat_t {
|
||||
nativeStat := fi.Sys().(*syscall.Stat_t)
|
||||
return &Stat_t{
|
||||
Dev: nativeStat.Dev,
|
||||
Ino: nativeStat.Ino,
|
||||
Nlink: nativeStat.Nlink,
|
||||
Mode: nativeStat.Mode,
|
||||
Uid: nativeStat.Uid,
|
||||
Gid: nativeStat.Gid,
|
||||
Rdev: nativeStat.Rdev,
|
||||
Size: nativeStat.Size,
|
||||
Blksize: int32(nativeStat.Blksize),
|
||||
Blocks: nativeStat.Blocks,
|
||||
Atim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Atim)),
|
||||
Mtim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Mtim)),
|
||||
Ctim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Ctim)),
|
||||
}
|
||||
}
|
31
vendor/github.com/hugelgupf/p9/internal/stat_standard.go
generated
vendored
Normal file
31
vendor/github.com/hugelgupf/p9/internal/stat_standard.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
//go:build linux || dragonfly || solaris
|
||||
// +build linux dragonfly solaris
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// InfoToStat takes a platform native FileInfo and converts it into a 9P2000.L compatible Stat_t
|
||||
func InfoToStat(fi os.FileInfo) *Stat_t {
|
||||
nativeStat := fi.Sys().(*syscall.Stat_t)
|
||||
return &Stat_t{
|
||||
Dev: nativeStat.Dev,
|
||||
Ino: nativeStat.Ino,
|
||||
Nlink: nativeStat.Nlink,
|
||||
Mode: nativeStat.Mode,
|
||||
Uid: nativeStat.Uid,
|
||||
Gid: nativeStat.Gid,
|
||||
Rdev: nativeStat.Rdev,
|
||||
Size: nativeStat.Size,
|
||||
Blksize: nativeStat.Blksize,
|
||||
Blocks: nativeStat.Blocks,
|
||||
Atim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Atim)),
|
||||
Mtim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Mtim)),
|
||||
Ctim: unix.NsecToTimespec(syscall.TimespecToNsec(nativeStat.Ctim)),
|
||||
}
|
||||
}
|
11
vendor/github.com/hugelgupf/p9/internal/stat_unix.go
generated
vendored
Normal file
11
vendor/github.com/hugelgupf/p9/internal/stat_unix.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Stat_t is the Linux Stat_t.
|
||||
type Stat_t = unix.Stat_t
|
74
vendor/github.com/hugelgupf/p9/internal/stat_windows.go
generated
vendored
Normal file
74
vendor/github.com/hugelgupf/p9/internal/stat_windows.go
generated
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// NOTE: taken from amd64 Linux
|
||||
type Timespec struct {
|
||||
Sec int64
|
||||
Nsec int64
|
||||
}
|
||||
|
||||
type Stat_t struct {
|
||||
Dev uint64
|
||||
Ino uint64
|
||||
Nlink uint64
|
||||
Mode uint32
|
||||
Uid uint32
|
||||
Gid uint32
|
||||
Rdev uint64
|
||||
Size int64
|
||||
Blksize int64
|
||||
Blocks int64
|
||||
Atim Timespec
|
||||
Mtim Timespec
|
||||
Ctim Timespec
|
||||
}
|
||||
|
||||
// InfoToStat takes a platform native FileInfo and converts it into a 9P2000.L compatible Stat_t
|
||||
func InfoToStat(fi os.FileInfo) *Stat_t {
|
||||
return &Stat_t{
|
||||
Size: fi.Size(),
|
||||
Mode: uint32(modeFromOS(fi.Mode())),
|
||||
Mtim: Timespec{
|
||||
Sec: fi.ModTime().Unix(),
|
||||
Nsec: fi.ModTime().UnixNano(),
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: copied from pkg p9
|
||||
// we should probably migrate the OS methods from p9 into sys
|
||||
const (
|
||||
FileModeMask uint32 = 0170000
|
||||
ModeSocket = 0140000
|
||||
ModeSymlink = 0120000
|
||||
ModeRegular = 0100000
|
||||
ModeBlockDevice = 060000
|
||||
ModeDirectory = 040000
|
||||
ModeCharacterDevice = 020000
|
||||
ModeNamedPipe = 010000
|
||||
)
|
||||
|
||||
func modeFromOS(mode os.FileMode) uint32 {
|
||||
m := uint32(mode.Perm())
|
||||
switch {
|
||||
case mode.IsDir():
|
||||
m |= ModeDirectory
|
||||
case mode&os.ModeSymlink != 0:
|
||||
m |= ModeSymlink
|
||||
case mode&os.ModeSocket != 0:
|
||||
m |= ModeSocket
|
||||
case mode&os.ModeNamedPipe != 0:
|
||||
m |= ModeNamedPipe
|
||||
case mode&os.ModeCharDevice != 0:
|
||||
m |= ModeCharacterDevice
|
||||
case mode&os.ModeDevice != 0:
|
||||
m |= ModeBlockDevice
|
||||
default:
|
||||
m |= ModeRegular
|
||||
}
|
||||
return m
|
||||
}
|
291
vendor/github.com/hugelgupf/p9/linux/errno.go
generated
vendored
Normal file
291
vendor/github.com/hugelgupf/p9/linux/errno.go
generated
vendored
Normal file
@ -0,0 +1,291 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package linux
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Errno is a Linux error number on every GOOS.
|
||||
type Errno uintptr
|
||||
|
||||
func (e Errno) Error() string {
|
||||
if 0 <= int(e) && int(e) < len(errorTable) {
|
||||
s := errorTable[e]
|
||||
if s != "" {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("errno %d", int(e))
|
||||
}
|
||||
|
||||
// numbers defined on Linux/amd64.
|
||||
const (
|
||||
E2BIG = Errno(0x7)
|
||||
EACCES = Errno(0xd)
|
||||
EADDRINUSE = Errno(0x62)
|
||||
EADDRNOTAVAIL = Errno(0x63)
|
||||
EADV = Errno(0x44)
|
||||
EAFNOSUPPORT = Errno(0x61)
|
||||
EAGAIN = Errno(0xb)
|
||||
EALREADY = Errno(0x72)
|
||||
EBADE = Errno(0x34)
|
||||
EBADF = Errno(0x9)
|
||||
EBADFD = Errno(0x4d)
|
||||
EBADMSG = Errno(0x4a)
|
||||
EBADR = Errno(0x35)
|
||||
EBADRQC = Errno(0x38)
|
||||
EBADSLT = Errno(0x39)
|
||||
EBFONT = Errno(0x3b)
|
||||
EBUSY = Errno(0x10)
|
||||
ECANCELED = Errno(0x7d)
|
||||
ECHILD = Errno(0xa)
|
||||
ECHRNG = Errno(0x2c)
|
||||
ECOMM = Errno(0x46)
|
||||
ECONNABORTED = Errno(0x67)
|
||||
ECONNREFUSED = Errno(0x6f)
|
||||
ECONNRESET = Errno(0x68)
|
||||
EDEADLK = Errno(0x23)
|
||||
EDEADLOCK = Errno(0x23)
|
||||
EDESTADDRREQ = Errno(0x59)
|
||||
EDOM = Errno(0x21)
|
||||
EDOTDOT = Errno(0x49)
|
||||
EDQUOT = Errno(0x7a)
|
||||
EEXIST = Errno(0x11)
|
||||
EFAULT = Errno(0xe)
|
||||
EFBIG = Errno(0x1b)
|
||||
EHOSTDOWN = Errno(0x70)
|
||||
EHOSTUNREACH = Errno(0x71)
|
||||
EHWPOISON = Errno(0x85)
|
||||
EIDRM = Errno(0x2b)
|
||||
EILSEQ = Errno(0x54)
|
||||
EINPROGRESS = Errno(0x73)
|
||||
EINTR = Errno(0x4)
|
||||
EINVAL = Errno(0x16)
|
||||
EIO = Errno(0x5)
|
||||
EISCONN = Errno(0x6a)
|
||||
EISDIR = Errno(0x15)
|
||||
EISNAM = Errno(0x78)
|
||||
EKEYEXPIRED = Errno(0x7f)
|
||||
EKEYREJECTED = Errno(0x81)
|
||||
EKEYREVOKED = Errno(0x80)
|
||||
EL2HLT = Errno(0x33)
|
||||
EL2NSYNC = Errno(0x2d)
|
||||
EL3HLT = Errno(0x2e)
|
||||
EL3RST = Errno(0x2f)
|
||||
ELIBACC = Errno(0x4f)
|
||||
ELIBBAD = Errno(0x50)
|
||||
ELIBEXEC = Errno(0x53)
|
||||
ELIBMAX = Errno(0x52)
|
||||
ELIBSCN = Errno(0x51)
|
||||
ELNRNG = Errno(0x30)
|
||||
ELOOP = Errno(0x28)
|
||||
EMEDIUMTYPE = Errno(0x7c)
|
||||
EMFILE = Errno(0x18)
|
||||
EMLINK = Errno(0x1f)
|
||||
EMSGSIZE = Errno(0x5a)
|
||||
EMULTIHOP = Errno(0x48)
|
||||
ENAMETOOLONG = Errno(0x24)
|
||||
ENAVAIL = Errno(0x77)
|
||||
ENETDOWN = Errno(0x64)
|
||||
ENETRESET = Errno(0x66)
|
||||
ENETUNREACH = Errno(0x65)
|
||||
ENFILE = Errno(0x17)
|
||||
ENOANO = Errno(0x37)
|
||||
ENOBUFS = Errno(0x69)
|
||||
ENOCSI = Errno(0x32)
|
||||
ENODATA = Errno(0x3d)
|
||||
ENODEV = Errno(0x13)
|
||||
ENOENT = Errno(0x2)
|
||||
ENOEXEC = Errno(0x8)
|
||||
ENOKEY = Errno(0x7e)
|
||||
ENOLCK = Errno(0x25)
|
||||
ENOLINK = Errno(0x43)
|
||||
ENOMEDIUM = Errno(0x7b)
|
||||
ENOMEM = Errno(0xc)
|
||||
ENOMSG = Errno(0x2a)
|
||||
ENONET = Errno(0x40)
|
||||
ENOPKG = Errno(0x41)
|
||||
ENOPROTOOPT = Errno(0x5c)
|
||||
ENOSPC = Errno(0x1c)
|
||||
ENOSR = Errno(0x3f)
|
||||
ENOSTR = Errno(0x3c)
|
||||
ENOSYS = Errno(0x26)
|
||||
ENOTBLK = Errno(0xf)
|
||||
ENOTCONN = Errno(0x6b)
|
||||
ENOTDIR = Errno(0x14)
|
||||
ENOTEMPTY = Errno(0x27)
|
||||
ENOTNAM = Errno(0x76)
|
||||
ENOTRECOVERABLE = Errno(0x83)
|
||||
ENOTSOCK = Errno(0x58)
|
||||
ENOTSUP = Errno(0x5f)
|
||||
ENOTTY = Errno(0x19)
|
||||
ENOTUNIQ = Errno(0x4c)
|
||||
ENXIO = Errno(0x6)
|
||||
EOPNOTSUPP = Errno(0x5f)
|
||||
EOVERFLOW = Errno(0x4b)
|
||||
EOWNERDEAD = Errno(0x82)
|
||||
EPERM = Errno(0x1)
|
||||
EPFNOSUPPORT = Errno(0x60)
|
||||
EPIPE = Errno(0x20)
|
||||
EPROTO = Errno(0x47)
|
||||
EPROTONOSUPPORT = Errno(0x5d)
|
||||
EPROTOTYPE = Errno(0x5b)
|
||||
ERANGE = Errno(0x22)
|
||||
EREMCHG = Errno(0x4e)
|
||||
EREMOTE = Errno(0x42)
|
||||
EREMOTEIO = Errno(0x79)
|
||||
ERESTART = Errno(0x55)
|
||||
ERFKILL = Errno(0x84)
|
||||
EROFS = Errno(0x1e)
|
||||
ESHUTDOWN = Errno(0x6c)
|
||||
ESOCKTNOSUPPORT = Errno(0x5e)
|
||||
ESPIPE = Errno(0x1d)
|
||||
ESRCH = Errno(0x3)
|
||||
ESRMNT = Errno(0x45)
|
||||
ESTALE = Errno(0x74)
|
||||
ESTRPIPE = Errno(0x56)
|
||||
ETIME = Errno(0x3e)
|
||||
ETIMEDOUT = Errno(0x6e)
|
||||
ETOOMANYREFS = Errno(0x6d)
|
||||
ETXTBSY = Errno(0x1a)
|
||||
EUCLEAN = Errno(0x75)
|
||||
EUNATCH = Errno(0x31)
|
||||
EUSERS = Errno(0x57)
|
||||
EWOULDBLOCK = Errno(0xb)
|
||||
EXDEV = Errno(0x12)
|
||||
EXFULL = Errno(0x36)
|
||||
)
|
||||
|
||||
var errorTable = [...]string{
|
||||
1: "operation not permitted",
|
||||
2: "no such file or directory",
|
||||
3: "no such process",
|
||||
4: "interrupted system call",
|
||||
5: "input/output error",
|
||||
6: "no such device or address",
|
||||
7: "argument list too long",
|
||||
8: "exec format error",
|
||||
9: "bad file descriptor",
|
||||
10: "no child processes",
|
||||
11: "resource temporarily unavailable",
|
||||
12: "cannot allocate memory",
|
||||
13: "permission denied",
|
||||
14: "bad address",
|
||||
15: "block device required",
|
||||
16: "device or resource busy",
|
||||
17: "file exists",
|
||||
18: "invalid cross-device link",
|
||||
19: "no such device",
|
||||
20: "not a directory",
|
||||
21: "is a directory",
|
||||
22: "invalid argument",
|
||||
23: "too many open files in system",
|
||||
24: "too many open files",
|
||||
25: "inappropriate ioctl for device",
|
||||
26: "text file busy",
|
||||
27: "file too large",
|
||||
28: "no space left on device",
|
||||
29: "illegal seek",
|
||||
30: "read-only file system",
|
||||
31: "too many links",
|
||||
32: "broken pipe",
|
||||
33: "numerical argument out of domain",
|
||||
34: "numerical result out of range",
|
||||
35: "resource deadlock avoided",
|
||||
36: "file name too long",
|
||||
37: "no locks available",
|
||||
38: "function not implemented",
|
||||
39: "directory not empty",
|
||||
40: "too many levels of symbolic links",
|
||||
42: "no message of desired type",
|
||||
43: "identifier removed",
|
||||
44: "channel number out of range",
|
||||
45: "level 2 not synchronized",
|
||||
46: "level 3 halted",
|
||||
47: "level 3 reset",
|
||||
48: "link number out of range",
|
||||
49: "protocol driver not attached",
|
||||
50: "no CSI structure available",
|
||||
51: "level 2 halted",
|
||||
52: "invalid exchange",
|
||||
53: "invalid request descriptor",
|
||||
54: "exchange full",
|
||||
55: "no anode",
|
||||
56: "invalid request code",
|
||||
57: "invalid slot",
|
||||
59: "bad font file format",
|
||||
60: "device not a stream",
|
||||
61: "no data available",
|
||||
62: "timer expired",
|
||||
63: "out of streams resources",
|
||||
64: "machine is not on the network",
|
||||
65: "package not installed",
|
||||
66: "object is remote",
|
||||
67: "link has been severed",
|
||||
68: "advertise error",
|
||||
69: "srmount error",
|
||||
70: "communication error on send",
|
||||
71: "protocol error",
|
||||
72: "multihop attempted",
|
||||
73: "RFS specific error",
|
||||
74: "bad message",
|
||||
75: "value too large for defined data type",
|
||||
76: "name not unique on network",
|
||||
77: "file descriptor in bad state",
|
||||
78: "remote address changed",
|
||||
79: "can not access a needed shared library",
|
||||
80: "accessing a corrupted shared library",
|
||||
81: ".lib section in a.out corrupted",
|
||||
82: "attempting to link in too many shared libraries",
|
||||
83: "cannot exec a shared library directly",
|
||||
84: "invalid or incomplete multibyte or wide character",
|
||||
85: "interrupted system call should be restarted",
|
||||
86: "streams pipe error",
|
||||
87: "too many users",
|
||||
88: "socket operation on non-socket",
|
||||
89: "destination address required",
|
||||
90: "message too long",
|
||||
91: "protocol wrong type for socket",
|
||||
92: "protocol not available",
|
||||
93: "protocol not supported",
|
||||
94: "socket type not supported",
|
||||
95: "operation not supported",
|
||||
96: "protocol family not supported",
|
||||
97: "address family not supported by protocol",
|
||||
98: "address already in use",
|
||||
99: "cannot assign requested address",
|
||||
100: "network is down",
|
||||
101: "network is unreachable",
|
||||
102: "network dropped connection on reset",
|
||||
103: "software caused connection abort",
|
||||
104: "connection reset by peer",
|
||||
105: "no buffer space available",
|
||||
106: "transport endpoint is already connected",
|
||||
107: "transport endpoint is not connected",
|
||||
108: "cannot send after transport endpoint shutdown",
|
||||
109: "too many references: cannot splice",
|
||||
110: "connection timed out",
|
||||
111: "connection refused",
|
||||
112: "host is down",
|
||||
113: "no route to host",
|
||||
114: "operation already in progress",
|
||||
115: "operation now in progress",
|
||||
116: "stale NFS file handle",
|
||||
117: "structure needs cleaning",
|
||||
118: "not a XENIX named type file",
|
||||
119: "no XENIX semaphores available",
|
||||
120: "is a named type file",
|
||||
121: "remote I/O error",
|
||||
122: "disk quota exceeded",
|
||||
123: "no medium found",
|
||||
124: "wrong medium type",
|
||||
125: "operation canceled",
|
||||
126: "required key not available",
|
||||
127: "key has expired",
|
||||
128: "key has been revoked",
|
||||
129: "key was rejected by service",
|
||||
130: "owner died",
|
||||
131: "state not recoverable",
|
||||
132: "operation not possible due to RF-kill",
|
||||
}
|
38
vendor/github.com/hugelgupf/p9/linux/errors.go
generated
vendored
Normal file
38
vendor/github.com/hugelgupf/p9/linux/errors.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
package linux
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ExtractErrno extracts an [Errno] from an error, best effort.
|
||||
//
|
||||
// If the system-specific or Go-specific error cannot be mapped to anything, it
|
||||
// will be logged and EIO will be returned.
|
||||
func ExtractErrno(err error) Errno {
|
||||
for _, pair := range []struct {
|
||||
error
|
||||
Errno
|
||||
}{
|
||||
{os.ErrNotExist, ENOENT},
|
||||
{os.ErrExist, EEXIST},
|
||||
{os.ErrPermission, EACCES},
|
||||
{os.ErrInvalid, EINVAL},
|
||||
} {
|
||||
if errors.Is(err, pair.error) {
|
||||
return pair.Errno
|
||||
}
|
||||
}
|
||||
|
||||
var errno Errno
|
||||
if errors.As(err, &errno) {
|
||||
return errno
|
||||
}
|
||||
|
||||
if e := sysErrno(err); e != 0 {
|
||||
return e
|
||||
}
|
||||
|
||||
// Default case.
|
||||
return EIO
|
||||
}
|
17
vendor/github.com/hugelgupf/p9/linux/errors_linux.go
generated
vendored
Normal file
17
vendor/github.com/hugelgupf/p9/linux/errors_linux.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package linux
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func sysErrno(err error) Errno {
|
||||
var systemErr syscall.Errno
|
||||
if errors.As(err, &systemErr) {
|
||||
return Errno(systemErr)
|
||||
}
|
||||
return 0
|
||||
}
|
21
vendor/github.com/hugelgupf/p9/linux/errors_unix.go
generated
vendored
Normal file
21
vendor/github.com/hugelgupf/p9/linux/errors_unix.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
//go:build !windows && !linux
|
||||
// +build !windows,!linux
|
||||
|
||||
package linux
|
||||
|
||||
import "syscall"
|
||||
|
||||
func sysErrno(err error) Errno {
|
||||
se, ok := err.(syscall.Errno)
|
||||
if ok {
|
||||
// POSIX-defined errors seem to match up to error number 34
|
||||
// according to http://www.ioplex.com/~miallen/errcmpp.html.
|
||||
//
|
||||
// 9P2000.L expects Linux error codes, so after 34 we normalize.
|
||||
if se <= 34 {
|
||||
return Errno(se)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return 0
|
||||
}
|
28
vendor/github.com/hugelgupf/p9/linux/errors_windows.go
generated
vendored
Normal file
28
vendor/github.com/hugelgupf/p9/linux/errors_windows.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package linux
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func sysErrno(err error) Errno {
|
||||
for _, pair := range []struct {
|
||||
error
|
||||
Errno
|
||||
}{
|
||||
{syscall.ERROR_FILE_NOT_FOUND, ENOENT},
|
||||
{syscall.ERROR_PATH_NOT_FOUND, ENOENT},
|
||||
{syscall.ERROR_ACCESS_DENIED, EACCES},
|
||||
{syscall.ERROR_FILE_EXISTS, EEXIST},
|
||||
{syscall.ERROR_INSUFFICIENT_BUFFER, ENOMEM},
|
||||
} {
|
||||
if errors.Is(err, pair.error) {
|
||||
return pair.Errno
|
||||
}
|
||||
}
|
||||
// No clue what to do with others.
|
||||
return 0
|
||||
}
|
253
vendor/github.com/hugelgupf/p9/p9/buffer.go
generated
vendored
Normal file
253
vendor/github.com/hugelgupf/p9/p9/buffer.go
generated
vendored
Normal file
@ -0,0 +1,253 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package p9
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// encoder is used for messages and 9P primitives.
|
||||
type encoder interface {
|
||||
// decode decodes from the given buffer. decode may be called more than once
|
||||
// to reuse the instance. It must clear any previous state.
|
||||
//
|
||||
// This may not fail, exhaustion will be recorded in the buffer.
|
||||
decode(b *buffer)
|
||||
|
||||
// encode encodes to the given buffer.
|
||||
//
|
||||
// This may not fail.
|
||||
encode(b *buffer)
|
||||
}
|
||||
|
||||
// order is the byte order used for encoding.
|
||||
var order = binary.LittleEndian
|
||||
|
||||
// buffer is a slice that is consumed.
|
||||
//
|
||||
// This is passed to the encoder methods.
|
||||
type buffer struct {
|
||||
// data is the underlying data. This may grow during encode.
|
||||
data []byte
|
||||
|
||||
// overflow indicates whether an overflow has occurred.
|
||||
overflow bool
|
||||
}
|
||||
|
||||
// append appends n bytes to the buffer and returns a slice pointing to the
|
||||
// newly appended bytes.
|
||||
func (b *buffer) append(n int) []byte {
|
||||
b.data = append(b.data, make([]byte, n)...)
|
||||
return b.data[len(b.data)-n:]
|
||||
}
|
||||
|
||||
// consume consumes n bytes from the buffer.
|
||||
func (b *buffer) consume(n int) ([]byte, bool) {
|
||||
if !b.has(n) {
|
||||
b.markOverrun()
|
||||
return nil, false
|
||||
}
|
||||
rval := b.data[:n]
|
||||
b.data = b.data[n:]
|
||||
return rval, true
|
||||
}
|
||||
|
||||
// has returns true if n bytes are available.
|
||||
func (b *buffer) has(n int) bool {
|
||||
return len(b.data) >= n
|
||||
}
|
||||
|
||||
// markOverrun immediately marks this buffer as overrun.
|
||||
//
|
||||
// This is used by ReadString, since some invalid data implies the rest of the
|
||||
// buffer is no longer valid either.
|
||||
func (b *buffer) markOverrun() {
|
||||
b.overflow = true
|
||||
}
|
||||
|
||||
// isOverrun returns true if this buffer has run past the end.
|
||||
func (b *buffer) isOverrun() bool {
|
||||
return b.overflow
|
||||
}
|
||||
|
||||
// Read8 reads a byte from the buffer.
|
||||
func (b *buffer) Read8() uint8 {
|
||||
v, ok := b.consume(1)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return uint8(v[0])
|
||||
}
|
||||
|
||||
// Read16 reads a 16-bit value from the buffer.
|
||||
func (b *buffer) Read16() uint16 {
|
||||
v, ok := b.consume(2)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return order.Uint16(v)
|
||||
}
|
||||
|
||||
// Read32 reads a 32-bit value from the buffer.
|
||||
func (b *buffer) Read32() uint32 {
|
||||
v, ok := b.consume(4)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return order.Uint32(v)
|
||||
}
|
||||
|
||||
// Read64 reads a 64-bit value from the buffer.
|
||||
func (b *buffer) Read64() uint64 {
|
||||
v, ok := b.consume(8)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return order.Uint64(v)
|
||||
}
|
||||
|
||||
// ReadQIDType reads a QIDType value.
|
||||
func (b *buffer) ReadQIDType() QIDType {
|
||||
return QIDType(b.Read8())
|
||||
}
|
||||
|
||||
// ReadTag reads a Tag value.
|
||||
func (b *buffer) ReadTag() tag {
|
||||
return tag(b.Read16())
|
||||
}
|
||||
|
||||
// ReadFID reads a FID value.
|
||||
func (b *buffer) ReadFID() fid {
|
||||
return fid(b.Read32())
|
||||
}
|
||||
|
||||
// ReadUID reads a UID value.
|
||||
func (b *buffer) ReadUID() UID {
|
||||
return UID(b.Read32())
|
||||
}
|
||||
|
||||
// ReadGID reads a GID value.
|
||||
func (b *buffer) ReadGID() GID {
|
||||
return GID(b.Read32())
|
||||
}
|
||||
|
||||
// ReadPermissions reads a file mode value and applies the mask for permissions.
|
||||
func (b *buffer) ReadPermissions() FileMode {
|
||||
return b.ReadFileMode() & permissionsMask
|
||||
}
|
||||
|
||||
// ReadFileMode reads a file mode value.
|
||||
func (b *buffer) ReadFileMode() FileMode {
|
||||
return FileMode(b.Read32())
|
||||
}
|
||||
|
||||
// ReadOpenFlags reads an OpenFlags.
|
||||
func (b *buffer) ReadOpenFlags() OpenFlags {
|
||||
return OpenFlags(b.Read32())
|
||||
}
|
||||
|
||||
// ReadMsgType writes a msgType.
|
||||
func (b *buffer) ReadMsgType() msgType {
|
||||
return msgType(b.Read8())
|
||||
}
|
||||
|
||||
// ReadString deserializes a string.
|
||||
func (b *buffer) ReadString() string {
|
||||
l := b.Read16()
|
||||
if !b.has(int(l)) {
|
||||
// Mark the buffer as corrupted.
|
||||
b.markOverrun()
|
||||
return ""
|
||||
}
|
||||
|
||||
bs := make([]byte, l)
|
||||
for i := 0; i < int(l); i++ {
|
||||
bs[i] = byte(b.Read8())
|
||||
}
|
||||
return string(bs)
|
||||
}
|
||||
|
||||
// Write8 writes a byte to the buffer.
|
||||
func (b *buffer) Write8(v uint8) {
|
||||
b.append(1)[0] = byte(v)
|
||||
}
|
||||
|
||||
// Write16 writes a 16-bit value to the buffer.
|
||||
func (b *buffer) Write16(v uint16) {
|
||||
order.PutUint16(b.append(2), v)
|
||||
}
|
||||
|
||||
// Write32 writes a 32-bit value to the buffer.
|
||||
func (b *buffer) Write32(v uint32) {
|
||||
order.PutUint32(b.append(4), v)
|
||||
}
|
||||
|
||||
// Write64 writes a 64-bit value to the buffer.
|
||||
func (b *buffer) Write64(v uint64) {
|
||||
order.PutUint64(b.append(8), v)
|
||||
}
|
||||
|
||||
// WriteQIDType writes a QIDType value.
|
||||
func (b *buffer) WriteQIDType(qidType QIDType) {
|
||||
b.Write8(uint8(qidType))
|
||||
}
|
||||
|
||||
// WriteTag writes a Tag value.
|
||||
func (b *buffer) WriteTag(tag tag) {
|
||||
b.Write16(uint16(tag))
|
||||
}
|
||||
|
||||
// WriteFID writes a FID value.
|
||||
func (b *buffer) WriteFID(fid fid) {
|
||||
b.Write32(uint32(fid))
|
||||
}
|
||||
|
||||
// WriteUID writes a UID value.
|
||||
func (b *buffer) WriteUID(uid UID) {
|
||||
b.Write32(uint32(uid))
|
||||
}
|
||||
|
||||
// WriteGID writes a GID value.
|
||||
func (b *buffer) WriteGID(gid GID) {
|
||||
b.Write32(uint32(gid))
|
||||
}
|
||||
|
||||
// WritePermissions applies a permissions mask and writes the FileMode.
|
||||
func (b *buffer) WritePermissions(perm FileMode) {
|
||||
b.WriteFileMode(perm & permissionsMask)
|
||||
}
|
||||
|
||||
// WriteFileMode writes a FileMode.
|
||||
func (b *buffer) WriteFileMode(mode FileMode) {
|
||||
b.Write32(uint32(mode))
|
||||
}
|
||||
|
||||
// WriteOpenFlags writes an OpenFlags.
|
||||
func (b *buffer) WriteOpenFlags(flags OpenFlags) {
|
||||
b.Write32(uint32(flags))
|
||||
}
|
||||
|
||||
// WriteMsgType writes a MsgType.
|
||||
func (b *buffer) WriteMsgType(t msgType) {
|
||||
b.Write8(uint8(t))
|
||||
}
|
||||
|
||||
// WriteString serializes the given string.
|
||||
func (b *buffer) WriteString(s string) {
|
||||
b.Write16(uint16(len(s)))
|
||||
for i := 0; i < len(s); i++ {
|
||||
b.Write8(byte(s[i]))
|
||||
}
|
||||
}
|
344
vendor/github.com/hugelgupf/p9/p9/client.go
generated
vendored
Normal file
344
vendor/github.com/hugelgupf/p9/p9/client.go
generated
vendored
Normal file
@ -0,0 +1,344 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package p9
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/hugelgupf/p9/linux"
|
||||
"github.com/u-root/uio/ulog"
|
||||
)
|
||||
|
||||
// ErrOutOfTags indicates no tags are available.
|
||||
var ErrOutOfTags = errors.New("out of tags -- messages lost?")
|
||||
|
||||
// ErrOutOfFIDs indicates no more FIDs are available.
|
||||
var ErrOutOfFIDs = errors.New("out of FIDs -- messages lost?")
|
||||
|
||||
// ErrUnexpectedTag indicates a response with an unexpected tag was received.
|
||||
var ErrUnexpectedTag = errors.New("unexpected tag in response")
|
||||
|
||||
// ErrVersionsExhausted indicates that all versions to negotiate have been exhausted.
|
||||
var ErrVersionsExhausted = errors.New("exhausted all versions to negotiate")
|
||||
|
||||
// ErrBadVersionString indicates that the version string is malformed or unsupported.
|
||||
var ErrBadVersionString = errors.New("bad version string")
|
||||
|
||||
// ErrBadResponse indicates the response didn't match the request.
|
||||
type ErrBadResponse struct {
|
||||
Got msgType
|
||||
Want msgType
|
||||
}
|
||||
|
||||
// Error returns a highly descriptive error.
|
||||
func (e *ErrBadResponse) Error() string {
|
||||
return fmt.Sprintf("unexpected message type: got %v, want %v", e.Got, e.Want)
|
||||
}
|
||||
|
||||
// response is the asynchronous return from recv.
|
||||
//
|
||||
// This is used in the pending map below.
|
||||
type response struct {
|
||||
r message
|
||||
done chan error
|
||||
}
|
||||
|
||||
var responsePool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &response{
|
||||
done: make(chan error, 1),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Client is at least a 9P2000.L client.
|
||||
type Client struct {
|
||||
// conn is the connected conn.
|
||||
conn io.ReadWriteCloser
|
||||
|
||||
// tagPool is the collection of available tags.
|
||||
tagPool pool
|
||||
|
||||
// fidPool is the collection of available fids.
|
||||
fidPool pool
|
||||
|
||||
// pending is the set of pending messages.
|
||||
pending map[tag]*response
|
||||
pendingMu sync.Mutex
|
||||
|
||||
// sendMu is the lock for sending a request.
|
||||
sendMu sync.Mutex
|
||||
|
||||
// recvr is essentially a mutex for calling recv.
|
||||
//
|
||||
// Whoever writes to this channel is permitted to call recv. When
|
||||
// finished calling recv, this channel should be emptied.
|
||||
recvr chan bool
|
||||
|
||||
// messageSize is the maximum total size of a message.
|
||||
messageSize uint32
|
||||
|
||||
// payloadSize is the maximum payload size of a read or write
|
||||
// request. For large reads and writes this means that the
|
||||
// read or write is broken up into buffer-size/payloadSize
|
||||
// requests.
|
||||
payloadSize uint32
|
||||
|
||||
// version is the agreed upon version X of 9P2000.L.Google.X.
|
||||
// version 0 implies 9P2000.L.
|
||||
version uint32
|
||||
|
||||
// log is the logger to write to, if specified.
|
||||
log ulog.Logger
|
||||
}
|
||||
|
||||
// ClientOpt enables optional client configuration.
|
||||
type ClientOpt func(*Client) error
|
||||
|
||||
// WithMessageSize overrides the default message size.
|
||||
func WithMessageSize(m uint32) ClientOpt {
|
||||
return func(c *Client) error {
|
||||
// Need at least one byte of payload.
|
||||
if m <= msgDotLRegistry.largestFixedSize {
|
||||
return &ErrMessageTooLarge{
|
||||
size: m,
|
||||
msize: msgDotLRegistry.largestFixedSize,
|
||||
}
|
||||
}
|
||||
c.messageSize = m
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithClientLogger overrides the default logger for the client.
|
||||
func WithClientLogger(l ulog.Logger) ClientOpt {
|
||||
return func(c *Client) error {
|
||||
c.log = l
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func roundDown(p uint32, align uint32) uint32 {
|
||||
if p > align && p%align != 0 {
|
||||
return p - p%align
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// NewClient creates a new client. It performs a Tversion exchange with
|
||||
// the server to assert that messageSize is ok to use.
|
||||
//
|
||||
// You should not use the same conn for multiple clients.
|
||||
func NewClient(conn io.ReadWriteCloser, o ...ClientOpt) (*Client, error) {
|
||||
c := &Client{
|
||||
conn: conn,
|
||||
tagPool: pool{start: 1, limit: uint64(noTag)},
|
||||
fidPool: pool{start: 1, limit: uint64(noFID)},
|
||||
pending: make(map[tag]*response),
|
||||
recvr: make(chan bool, 1),
|
||||
messageSize: DefaultMessageSize,
|
||||
log: ulog.Null,
|
||||
|
||||
// Request a high version by default.
|
||||
version: highestSupportedVersion,
|
||||
}
|
||||
|
||||
for _, opt := range o {
|
||||
if err := opt(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Compute a payload size and round to 512 (normal block size)
|
||||
// if it's larger than a single block.
|
||||
c.payloadSize = roundDown(c.messageSize-msgDotLRegistry.largestFixedSize, 512)
|
||||
|
||||
// Agree upon a version.
|
||||
requested := c.version
|
||||
for {
|
||||
rversion := rversion{}
|
||||
err := c.sendRecv(&tversion{Version: versionString(version9P2000L, requested), MSize: c.messageSize}, &rversion)
|
||||
|
||||
// The server told us to try again with a lower version.
|
||||
if errors.Is(err, linux.EAGAIN) {
|
||||
if requested == lowestSupportedVersion {
|
||||
return nil, ErrVersionsExhausted
|
||||
}
|
||||
requested--
|
||||
continue
|
||||
}
|
||||
|
||||
// We requested an impossible version or our other parameters were bogus.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse the version.
|
||||
baseVersion, version, ok := parseVersion(rversion.Version)
|
||||
if !ok {
|
||||
// The server gave us a bad version. We return a generically worrisome error.
|
||||
c.log.Printf("server returned bad version string %q", rversion.Version)
|
||||
return nil, ErrBadVersionString
|
||||
}
|
||||
if baseVersion != version9P2000L {
|
||||
c.log.Printf("server returned unsupported base version %q (version %q)", baseVersion, rversion.Version)
|
||||
return nil, ErrBadVersionString
|
||||
}
|
||||
c.version = version
|
||||
break
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// handleOne handles a single incoming message.
|
||||
//
|
||||
// This should only be called with the token from recvr. Note that the received
|
||||
// tag will automatically be cleared from pending.
|
||||
func (c *Client) handleOne() {
|
||||
t, r, err := recv(c.log, c.conn, c.messageSize, func(t tag, mt msgType) (message, error) {
|
||||
c.pendingMu.Lock()
|
||||
resp := c.pending[t]
|
||||
c.pendingMu.Unlock()
|
||||
|
||||
// Not expecting this message?
|
||||
if resp == nil {
|
||||
c.log.Printf("client received unexpected tag %v, ignoring", t)
|
||||
return nil, ErrUnexpectedTag
|
||||
}
|
||||
|
||||
// Is it an error? We specifically allow this to
|
||||
// go through, and then we deserialize below.
|
||||
if mt == msgRlerror {
|
||||
return &rlerror{}, nil
|
||||
}
|
||||
|
||||
// Does it match expectations?
|
||||
if mt != resp.r.typ() {
|
||||
return nil, &ErrBadResponse{Got: mt, Want: resp.r.typ()}
|
||||
}
|
||||
|
||||
// Return the response.
|
||||
return resp.r, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
// No tag was extracted (probably a conn error).
|
||||
//
|
||||
// Likely catastrophic. Notify all waiters and clear pending.
|
||||
c.pendingMu.Lock()
|
||||
for _, resp := range c.pending {
|
||||
resp.done <- err
|
||||
}
|
||||
c.pending = make(map[tag]*response)
|
||||
c.pendingMu.Unlock()
|
||||
} else {
|
||||
// Process the tag.
|
||||
//
|
||||
// We know that is is contained in the map because our lookup function
|
||||
// above must have succeeded (found the tag) to return nil err.
|
||||
c.pendingMu.Lock()
|
||||
resp := c.pending[t]
|
||||
delete(c.pending, t)
|
||||
c.pendingMu.Unlock()
|
||||
resp.r = r
|
||||
resp.done <- err
|
||||
}
|
||||
}
|
||||
|
||||
// waitAndRecv co-ordinates with other receivers to handle responses.
|
||||
func (c *Client) waitAndRecv(done chan error) error {
|
||||
for {
|
||||
select {
|
||||
case err := <-done:
|
||||
return err
|
||||
case c.recvr <- true:
|
||||
select {
|
||||
case err := <-done:
|
||||
// It's possible that we got the token, despite
|
||||
// done also being available. Check for that.
|
||||
<-c.recvr
|
||||
return err
|
||||
default:
|
||||
// Handle receiving one tag.
|
||||
c.handleOne()
|
||||
|
||||
// Return the token.
|
||||
<-c.recvr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sendRecv performs a roundtrip message exchange.
|
||||
//
|
||||
// This is called by internal functions.
|
||||
func (c *Client) sendRecv(tm message, rm message) error {
|
||||
t, ok := c.tagPool.Get()
|
||||
if !ok {
|
||||
return ErrOutOfTags
|
||||
}
|
||||
defer c.tagPool.Put(t)
|
||||
|
||||
// Indicate we're expecting a response.
|
||||
//
|
||||
// Note that the tag will be cleared from pending
|
||||
// automatically (see handleOne for details).
|
||||
resp := responsePool.Get().(*response)
|
||||
defer responsePool.Put(resp)
|
||||
resp.r = rm
|
||||
c.pendingMu.Lock()
|
||||
c.pending[tag(t)] = resp
|
||||
c.pendingMu.Unlock()
|
||||
|
||||
// Send the request over the wire.
|
||||
c.sendMu.Lock()
|
||||
err := send(c.log, c.conn, tag(t), tm)
|
||||
c.sendMu.Unlock()
|
||||
if err != nil {
|
||||
return fmt.Errorf("send: %w", err)
|
||||
}
|
||||
|
||||
// Co-ordinate with other receivers.
|
||||
if err := c.waitAndRecv(resp.done); err != nil {
|
||||
return fmt.Errorf("wait: %w", err)
|
||||
}
|
||||
|
||||
// Is it an error message?
|
||||
//
|
||||
// For convenience, we transform these directly
|
||||
// into errors. Handlers need not handle this case.
|
||||
if rlerr, ok := resp.r.(*rlerror); ok {
|
||||
return linux.Errno(rlerr.Error)
|
||||
}
|
||||
|
||||
// At this point, we know it matches.
|
||||
//
|
||||
// Per recv call above, we will only allow a type
|
||||
// match (and give our r) or an instance of Rlerror.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Version returns the negotiated 9P2000.L.Google version number.
|
||||
func (c *Client) Version() uint32 {
|
||||
return c.version
|
||||
}
|
||||
|
||||
// Close closes the underlying connection.
|
||||
func (c *Client) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
568
vendor/github.com/hugelgupf/p9/p9/client_file.go
generated
vendored
Normal file
568
vendor/github.com/hugelgupf/p9/p9/client_file.go
generated
vendored
Normal file
@ -0,0 +1,568 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package p9
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/hugelgupf/p9/linux"
|
||||
)
|
||||
|
||||
// Attach attaches to a server.
|
||||
//
|
||||
// Note that authentication is not currently supported.
|
||||
func (c *Client) Attach(name string) (File, error) {
|
||||
id, ok := c.fidPool.Get()
|
||||
if !ok {
|
||||
return nil, ErrOutOfFIDs
|
||||
}
|
||||
|
||||
rattach := rattach{}
|
||||
if err := c.sendRecv(&tattach{fid: fid(id), Auth: tauth{AttachName: name, Authenticationfid: noFID, UID: NoUID}}, &rattach); err != nil {
|
||||
c.fidPool.Put(id)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.newFile(fid(id)), nil
|
||||
}
|
||||
|
||||
// newFile returns a new client file.
|
||||
func (c *Client) newFile(fid fid) *clientFile {
|
||||
cf := &clientFile{
|
||||
client: c,
|
||||
fid: fid,
|
||||
}
|
||||
|
||||
// Make sure the file is closed.
|
||||
runtime.SetFinalizer(cf, (*clientFile).Close)
|
||||
|
||||
return cf
|
||||
}
|
||||
|
||||
// clientFile is provided to clients.
|
||||
//
|
||||
// This proxies all of the interfaces found in file.go.
|
||||
type clientFile struct {
|
||||
// client is the originating client.
|
||||
client *Client
|
||||
|
||||
// fid is the fid for this file.
|
||||
fid fid
|
||||
|
||||
// closed indicates whether this file has been closed.
|
||||
closed uint32
|
||||
}
|
||||
|
||||
// SetXattr implements p9.File.SetXattr.
|
||||
func (c *clientFile) SetXattr(attr string, data []byte, flags XattrFlags) error {
|
||||
return linux.ENOSYS
|
||||
}
|
||||
|
||||
// RemoveXattr implements p9.File.RemoveXattr.
|
||||
func (c *clientFile) RemoveXattr(attr string) error {
|
||||
return linux.ENOSYS
|
||||
}
|
||||
|
||||
// GetXattr implements p9.File.GetXattr.
|
||||
func (c *clientFile) GetXattr(attr string) ([]byte, error) {
|
||||
return nil, linux.ENOSYS
|
||||
}
|
||||
|
||||
// ListXattrs implements p9.File.ListXattrs.
|
||||
func (c *clientFile) ListXattrs() ([]string, error) {
|
||||
return nil, linux.ENOSYS
|
||||
}
|
||||
|
||||
// Walk implements File.Walk.
|
||||
func (c *clientFile) Walk(names []string) ([]QID, File, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return nil, nil, linux.EBADF
|
||||
}
|
||||
|
||||
id, ok := c.client.fidPool.Get()
|
||||
if !ok {
|
||||
return nil, nil, ErrOutOfFIDs
|
||||
}
|
||||
|
||||
rwalk := rwalk{}
|
||||
if err := c.client.sendRecv(&twalk{fid: c.fid, newFID: fid(id), Names: names}, &rwalk); err != nil {
|
||||
c.client.fidPool.Put(id)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Return a new client file.
|
||||
return rwalk.QIDs, c.client.newFile(fid(id)), nil
|
||||
}
|
||||
|
||||
// WalkGetAttr implements File.WalkGetAttr.
|
||||
func (c *clientFile) WalkGetAttr(components []string) ([]QID, File, AttrMask, Attr, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return nil, nil, AttrMask{}, Attr{}, linux.EBADF
|
||||
}
|
||||
|
||||
if !versionSupportsTwalkgetattr(c.client.version) {
|
||||
qids, file, err := c.Walk(components)
|
||||
if err != nil {
|
||||
return nil, nil, AttrMask{}, Attr{}, err
|
||||
}
|
||||
_, valid, attr, err := file.GetAttr(AttrMaskAll)
|
||||
if err != nil {
|
||||
file.Close()
|
||||
return nil, nil, AttrMask{}, Attr{}, err
|
||||
}
|
||||
return qids, file, valid, attr, nil
|
||||
}
|
||||
|
||||
id, ok := c.client.fidPool.Get()
|
||||
if !ok {
|
||||
return nil, nil, AttrMask{}, Attr{}, ErrOutOfFIDs
|
||||
}
|
||||
|
||||
rwalkgetattr := rwalkgetattr{}
|
||||
if err := c.client.sendRecv(&twalkgetattr{fid: c.fid, newFID: fid(id), Names: components}, &rwalkgetattr); err != nil {
|
||||
c.client.fidPool.Put(id)
|
||||
return nil, nil, AttrMask{}, Attr{}, err
|
||||
}
|
||||
|
||||
// Return a new client file.
|
||||
return rwalkgetattr.QIDs, c.client.newFile(fid(id)), rwalkgetattr.Valid, rwalkgetattr.Attr, nil
|
||||
}
|
||||
|
||||
// StatFS implements File.StatFS.
|
||||
func (c *clientFile) StatFS() (FSStat, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return FSStat{}, linux.EBADF
|
||||
}
|
||||
|
||||
rstatfs := rstatfs{}
|
||||
if err := c.client.sendRecv(&tstatfs{fid: c.fid}, &rstatfs); err != nil {
|
||||
return FSStat{}, err
|
||||
}
|
||||
|
||||
return rstatfs.FSStat, nil
|
||||
}
|
||||
|
||||
// FSync implements File.FSync.
|
||||
func (c *clientFile) FSync() error {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return linux.EBADF
|
||||
}
|
||||
|
||||
return c.client.sendRecv(&tfsync{fid: c.fid}, &rfsync{})
|
||||
}
|
||||
|
||||
// GetAttr implements File.GetAttr.
|
||||
func (c *clientFile) GetAttr(req AttrMask) (QID, AttrMask, Attr, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return QID{}, AttrMask{}, Attr{}, linux.EBADF
|
||||
}
|
||||
|
||||
rgetattr := rgetattr{}
|
||||
if err := c.client.sendRecv(&tgetattr{fid: c.fid, AttrMask: req}, &rgetattr); err != nil {
|
||||
return QID{}, AttrMask{}, Attr{}, err
|
||||
}
|
||||
|
||||
return rgetattr.QID, rgetattr.Valid, rgetattr.Attr, nil
|
||||
}
|
||||
|
||||
// SetAttr implements File.SetAttr.
|
||||
func (c *clientFile) SetAttr(valid SetAttrMask, attr SetAttr) error {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return linux.EBADF
|
||||
}
|
||||
|
||||
return c.client.sendRecv(&tsetattr{fid: c.fid, Valid: valid, SetAttr: attr}, &rsetattr{})
|
||||
}
|
||||
|
||||
// Lock implements File.Lock
|
||||
func (c *clientFile) Lock(pid int, locktype LockType, flags LockFlags, start, length uint64, client string) (LockStatus, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return LockStatusError, linux.EBADF
|
||||
}
|
||||
|
||||
r := rlock{}
|
||||
err := c.client.sendRecv(&tlock{
|
||||
Type: locktype,
|
||||
Flags: flags,
|
||||
Start: start,
|
||||
Length: length,
|
||||
PID: int32(pid),
|
||||
Client: client,
|
||||
}, &r)
|
||||
return r.Status, err
|
||||
}
|
||||
|
||||
// Remove implements File.Remove.
|
||||
//
|
||||
// N.B. This method is no longer part of the file interface and should be
|
||||
// considered deprecated.
|
||||
func (c *clientFile) Remove() error {
|
||||
// Avoid double close.
|
||||
if !atomic.CompareAndSwapUint32(&c.closed, 0, 1) {
|
||||
return linux.EBADF
|
||||
}
|
||||
runtime.SetFinalizer(c, nil)
|
||||
|
||||
// Send the remove message.
|
||||
if err := c.client.sendRecv(&tremove{fid: c.fid}, &rremove{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// "It is correct to consider remove to be a clunk with the side effect
|
||||
// of removing the file if permissions allow."
|
||||
// https://swtch.com/plan9port/man/man9/remove.html
|
||||
|
||||
// Return the fid to the pool.
|
||||
c.client.fidPool.Put(uint64(c.fid))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close implements File.Close.
|
||||
func (c *clientFile) Close() error {
|
||||
// Avoid double close.
|
||||
if !atomic.CompareAndSwapUint32(&c.closed, 0, 1) {
|
||||
return linux.EBADF
|
||||
}
|
||||
runtime.SetFinalizer(c, nil)
|
||||
|
||||
// Send the close message.
|
||||
if err := c.client.sendRecv(&tclunk{fid: c.fid}, &rclunk{}); err != nil {
|
||||
// If an error occurred, we toss away the fid. This isn't ideal,
|
||||
// but I'm not sure what else makes sense in this context.
|
||||
return err
|
||||
}
|
||||
|
||||
// Return the fid to the pool.
|
||||
c.client.fidPool.Put(uint64(c.fid))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Open implements File.Open.
|
||||
func (c *clientFile) Open(flags OpenFlags) (QID, uint32, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return QID{}, 0, linux.EBADF
|
||||
}
|
||||
|
||||
rlopen := rlopen{}
|
||||
if err := c.client.sendRecv(&tlopen{fid: c.fid, Flags: flags}, &rlopen); err != nil {
|
||||
return QID{}, 0, err
|
||||
}
|
||||
|
||||
return rlopen.QID, rlopen.IoUnit, nil
|
||||
}
|
||||
|
||||
// chunk applies fn to p in chunkSize-sized chunks until fn returns a partial result, p is
|
||||
// exhausted, or an error is encountered (which may be io.EOF).
|
||||
func chunk(chunkSize uint32, fn func([]byte, int64) (int, error), p []byte, offset int64) (int, error) {
|
||||
// Some p9.Clients depend on executing fn on zero-byte buffers. Handle this
|
||||
// as a special case (normally it is fine to short-circuit and return (0, nil)).
|
||||
if len(p) == 0 {
|
||||
return fn(p, offset)
|
||||
}
|
||||
|
||||
// total is the cumulative bytes processed.
|
||||
var total int
|
||||
for {
|
||||
var n int
|
||||
var err error
|
||||
|
||||
// We're done, don't bother trying to do anything more.
|
||||
if total == len(p) {
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// Apply fn to a chunkSize-sized (or less) chunk of p.
|
||||
if len(p) < total+int(chunkSize) {
|
||||
n, err = fn(p[total:], offset)
|
||||
} else {
|
||||
n, err = fn(p[total:total+int(chunkSize)], offset)
|
||||
}
|
||||
total += n
|
||||
offset += int64(n)
|
||||
|
||||
// Return whatever we have processed if we encounter an error. This error
|
||||
// could be io.EOF.
|
||||
if err != nil {
|
||||
return total, err
|
||||
}
|
||||
|
||||
// Did we get a partial result? If so, return it immediately.
|
||||
if n < int(chunkSize) {
|
||||
return total, nil
|
||||
}
|
||||
|
||||
// If we received more bytes than we ever requested, this is a problem.
|
||||
if total > len(p) {
|
||||
panic(fmt.Sprintf("bytes completed (%d)) > requested (%d)", total, len(p)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReadAt proxies File.ReadAt.
|
||||
func (c *clientFile) ReadAt(p []byte, offset int64) (int, error) {
|
||||
return chunk(c.client.payloadSize, c.readAt, p, offset)
|
||||
}
|
||||
|
||||
func (c *clientFile) readAt(p []byte, offset int64) (int, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return 0, linux.EBADF
|
||||
}
|
||||
|
||||
rread := rread{Data: p}
|
||||
if err := c.client.sendRecv(&tread{fid: c.fid, Offset: uint64(offset), Count: uint32(len(p))}, &rread); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// The message may have been truncated, or for some reason a new buffer
|
||||
// allocated. This isn't the common path, but we make sure that if the
|
||||
// payload has changed we copy it. See transport.go for more information.
|
||||
if len(p) > 0 && len(rread.Data) > 0 && &rread.Data[0] != &p[0] {
|
||||
copy(p, rread.Data)
|
||||
}
|
||||
|
||||
// io.EOF is not an error that a p9 server can return. Use POSIX semantics to
|
||||
// return io.EOF manually: zero bytes were returned and a non-zero buffer was used.
|
||||
if len(rread.Data) == 0 && len(p) > 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
return len(rread.Data), nil
|
||||
}
|
||||
|
||||
// WriteAt proxies File.WriteAt.
|
||||
func (c *clientFile) WriteAt(p []byte, offset int64) (int, error) {
|
||||
return chunk(c.client.payloadSize, c.writeAt, p, offset)
|
||||
}
|
||||
|
||||
func (c *clientFile) writeAt(p []byte, offset int64) (int, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return 0, linux.EBADF
|
||||
}
|
||||
|
||||
rwrite := rwrite{}
|
||||
if err := c.client.sendRecv(&twrite{fid: c.fid, Offset: uint64(offset), Data: p}, &rwrite); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int(rwrite.Count), nil
|
||||
}
|
||||
|
||||
// Rename implements File.Rename.
|
||||
func (c *clientFile) Rename(dir File, name string) error {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return linux.EBADF
|
||||
}
|
||||
|
||||
clientDir, ok := dir.(*clientFile)
|
||||
if !ok {
|
||||
return linux.EBADF
|
||||
}
|
||||
|
||||
return c.client.sendRecv(&trename{fid: c.fid, Directory: clientDir.fid, Name: name}, &rrename{})
|
||||
}
|
||||
|
||||
// Create implements File.Create.
|
||||
func (c *clientFile) Create(name string, openFlags OpenFlags, permissions FileMode, uid UID, gid GID) (File, QID, uint32, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return nil, QID{}, 0, linux.EBADF
|
||||
}
|
||||
|
||||
msg := tlcreate{
|
||||
fid: c.fid,
|
||||
Name: name,
|
||||
OpenFlags: openFlags,
|
||||
Permissions: permissions,
|
||||
GID: NoGID,
|
||||
}
|
||||
|
||||
if versionSupportsTucreation(c.client.version) {
|
||||
msg.GID = gid
|
||||
rucreate := rucreate{}
|
||||
if err := c.client.sendRecv(&tucreate{tlcreate: msg, UID: uid}, &rucreate); err != nil {
|
||||
return nil, QID{}, 0, err
|
||||
}
|
||||
return c, rucreate.QID, rucreate.IoUnit, nil
|
||||
}
|
||||
|
||||
rlcreate := rlcreate{}
|
||||
if err := c.client.sendRecv(&msg, &rlcreate); err != nil {
|
||||
return nil, QID{}, 0, err
|
||||
}
|
||||
|
||||
return c, rlcreate.QID, rlcreate.IoUnit, nil
|
||||
}
|
||||
|
||||
// Mkdir implements File.Mkdir.
|
||||
func (c *clientFile) Mkdir(name string, permissions FileMode, uid UID, gid GID) (QID, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return QID{}, linux.EBADF
|
||||
}
|
||||
|
||||
msg := tmkdir{
|
||||
Directory: c.fid,
|
||||
Name: name,
|
||||
Permissions: permissions,
|
||||
GID: NoGID,
|
||||
}
|
||||
|
||||
if versionSupportsTucreation(c.client.version) {
|
||||
msg.GID = gid
|
||||
rumkdir := rumkdir{}
|
||||
if err := c.client.sendRecv(&tumkdir{tmkdir: msg, UID: uid}, &rumkdir); err != nil {
|
||||
return QID{}, err
|
||||
}
|
||||
return rumkdir.QID, nil
|
||||
}
|
||||
|
||||
rmkdir := rmkdir{}
|
||||
if err := c.client.sendRecv(&msg, &rmkdir); err != nil {
|
||||
return QID{}, err
|
||||
}
|
||||
|
||||
return rmkdir.QID, nil
|
||||
}
|
||||
|
||||
// Symlink implements File.Symlink.
|
||||
func (c *clientFile) Symlink(oldname string, newname string, uid UID, gid GID) (QID, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return QID{}, linux.EBADF
|
||||
}
|
||||
|
||||
msg := tsymlink{
|
||||
Directory: c.fid,
|
||||
Name: newname,
|
||||
Target: oldname,
|
||||
GID: NoGID,
|
||||
}
|
||||
|
||||
if versionSupportsTucreation(c.client.version) {
|
||||
msg.GID = gid
|
||||
rusymlink := rusymlink{}
|
||||
if err := c.client.sendRecv(&tusymlink{tsymlink: msg, UID: uid}, &rusymlink); err != nil {
|
||||
return QID{}, err
|
||||
}
|
||||
return rusymlink.QID, nil
|
||||
}
|
||||
|
||||
rsymlink := rsymlink{}
|
||||
if err := c.client.sendRecv(&msg, &rsymlink); err != nil {
|
||||
return QID{}, err
|
||||
}
|
||||
|
||||
return rsymlink.QID, nil
|
||||
}
|
||||
|
||||
// Link implements File.Link.
|
||||
func (c *clientFile) Link(target File, newname string) error {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return linux.EBADF
|
||||
}
|
||||
|
||||
targetFile, ok := target.(*clientFile)
|
||||
if !ok {
|
||||
return linux.EBADF
|
||||
}
|
||||
|
||||
return c.client.sendRecv(&tlink{Directory: c.fid, Name: newname, Target: targetFile.fid}, &rlink{})
|
||||
}
|
||||
|
||||
// Mknod implements File.Mknod.
|
||||
func (c *clientFile) Mknod(name string, mode FileMode, major uint32, minor uint32, uid UID, gid GID) (QID, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return QID{}, linux.EBADF
|
||||
}
|
||||
|
||||
msg := tmknod{
|
||||
Directory: c.fid,
|
||||
Name: name,
|
||||
Mode: mode,
|
||||
Major: major,
|
||||
Minor: minor,
|
||||
GID: NoGID,
|
||||
}
|
||||
|
||||
if versionSupportsTucreation(c.client.version) {
|
||||
msg.GID = gid
|
||||
rumknod := rumknod{}
|
||||
if err := c.client.sendRecv(&tumknod{tmknod: msg, UID: uid}, &rumknod); err != nil {
|
||||
return QID{}, err
|
||||
}
|
||||
return rumknod.QID, nil
|
||||
}
|
||||
|
||||
rmknod := rmknod{}
|
||||
if err := c.client.sendRecv(&msg, &rmknod); err != nil {
|
||||
return QID{}, err
|
||||
}
|
||||
|
||||
return rmknod.QID, nil
|
||||
}
|
||||
|
||||
// RenameAt implements File.RenameAt.
|
||||
func (c *clientFile) RenameAt(oldname string, newdir File, newname string) error {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return linux.EBADF
|
||||
}
|
||||
|
||||
clientNewDir, ok := newdir.(*clientFile)
|
||||
if !ok {
|
||||
return linux.EBADF
|
||||
}
|
||||
|
||||
return c.client.sendRecv(&trenameat{OldDirectory: c.fid, OldName: oldname, NewDirectory: clientNewDir.fid, NewName: newname}, &rrenameat{})
|
||||
}
|
||||
|
||||
// UnlinkAt implements File.UnlinkAt.
|
||||
func (c *clientFile) UnlinkAt(name string, flags uint32) error {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return linux.EBADF
|
||||
}
|
||||
|
||||
return c.client.sendRecv(&tunlinkat{Directory: c.fid, Name: name, Flags: flags}, &runlinkat{})
|
||||
}
|
||||
|
||||
// Readdir implements File.Readdir.
|
||||
func (c *clientFile) Readdir(offset uint64, count uint32) (Dirents, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return nil, linux.EBADF
|
||||
}
|
||||
|
||||
rreaddir := rreaddir{}
|
||||
if err := c.client.sendRecv(&treaddir{Directory: c.fid, Offset: offset, Count: count}, &rreaddir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rreaddir.Entries, nil
|
||||
}
|
||||
|
||||
// Readlink implements File.Readlink.
|
||||
func (c *clientFile) Readlink() (string, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return "", linux.EBADF
|
||||
}
|
||||
|
||||
rreadlink := rreadlink{}
|
||||
if err := c.client.sendRecv(&treadlink{fid: c.fid}, &rreadlink); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return rreadlink.Target, nil
|
||||
}
|
||||
|
||||
// Renamed implements File.Renamed.
|
||||
func (c *clientFile) Renamed(newDir File, newName string) {}
|
274
vendor/github.com/hugelgupf/p9/p9/file.go
generated
vendored
Normal file
274
vendor/github.com/hugelgupf/p9/p9/file.go
generated
vendored
Normal file
@ -0,0 +1,274 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package p9
|
||||
|
||||
import (
|
||||
"github.com/hugelgupf/p9/linux"
|
||||
)
|
||||
|
||||
// Attacher is provided by the server.
|
||||
type Attacher interface {
|
||||
// Attach returns a new File.
|
||||
//
|
||||
// The client-side attach will be translate to a series of walks from
|
||||
// the file returned by this Attach call.
|
||||
Attach() (File, error)
|
||||
}
|
||||
|
||||
// File is a set of operations corresponding to a single node.
|
||||
//
|
||||
// Note that on the server side, the server logic places constraints on
|
||||
// concurrent operations to make things easier. This may reduce the need for
|
||||
// complex, error-prone locking and logic in the backend. These are documented
|
||||
// for each method.
|
||||
//
|
||||
// There are three different types of guarantees provided:
|
||||
//
|
||||
// none: There is no concurrency guarantee. The method may be invoked
|
||||
// concurrently with any other method on any other file.
|
||||
//
|
||||
// read: The method is guaranteed to be exclusive of any write or global
|
||||
// operation that is mutating the state of the directory tree starting at this
|
||||
// node. For example, this means creating new files, symlinks, directories or
|
||||
// renaming a directory entry (or renaming in to this target), but the method
|
||||
// may be called concurrently with other read methods.
|
||||
//
|
||||
// write: The method is guaranteed to be exclusive of any read, write or global
|
||||
// operation that is mutating the state of the directory tree starting at this
|
||||
// node, as described in read above. There may however, be other write
|
||||
// operations executing concurrently on other components in the directory tree.
|
||||
//
|
||||
// global: The method is guaranteed to be exclusive of any read, write or
|
||||
// global operation.
|
||||
type File interface {
|
||||
// Walk walks to the path components given in names.
|
||||
//
|
||||
// Walk returns QIDs in the same order that the names were passed in.
|
||||
//
|
||||
// An empty list of arguments should return a copy of the current file.
|
||||
//
|
||||
// On the server, Walk has a read concurrency guarantee.
|
||||
Walk(names []string) ([]QID, File, error)
|
||||
|
||||
// WalkGetAttr walks to the next file and returns its maximal set of
|
||||
// attributes.
|
||||
//
|
||||
// Server-side p9.Files may return linux.ENOSYS to indicate that Walk
|
||||
// and GetAttr should be used separately to satisfy this request.
|
||||
//
|
||||
// On the server, WalkGetAttr has a read concurrency guarantee.
|
||||
WalkGetAttr([]string) ([]QID, File, AttrMask, Attr, error)
|
||||
|
||||
// StatFS returns information about the file system associated with
|
||||
// this file.
|
||||
//
|
||||
// On the server, StatFS has no concurrency guarantee.
|
||||
StatFS() (FSStat, error)
|
||||
|
||||
// GetAttr returns attributes of this node.
|
||||
//
|
||||
// On the server, GetAttr has a read concurrency guarantee.
|
||||
GetAttr(req AttrMask) (QID, AttrMask, Attr, error)
|
||||
|
||||
// SetAttr sets attributes on this node.
|
||||
//
|
||||
// On the server, SetAttr has a write concurrency guarantee.
|
||||
SetAttr(valid SetAttrMask, attr SetAttr) error
|
||||
|
||||
// Close is called when all references are dropped on the server side,
|
||||
// and Close should be called by the client to drop all references.
|
||||
//
|
||||
// For server-side implementations of Close, the error is ignored.
|
||||
//
|
||||
// Close must be called even when Open has not been called.
|
||||
//
|
||||
// On the server, Close has no concurrency guarantee.
|
||||
Close() error
|
||||
|
||||
// Open must be called prior to using ReadAt, WriteAt, or Readdir. Once
|
||||
// Open is called, some operations, such as Walk, will no longer work.
|
||||
//
|
||||
// On the client, Open should be called only once. The fd return is
|
||||
// optional, and may be nil.
|
||||
//
|
||||
// On the server, Open has a read concurrency guarantee. Open is
|
||||
// guaranteed to be called only once.
|
||||
//
|
||||
// N.B. The server must resolve any lazy paths when open is called.
|
||||
// After this point, read and write may be called on files with no
|
||||
// deletion check, so resolving in the data path is not viable.
|
||||
Open(mode OpenFlags) (QID, uint32, error)
|
||||
|
||||
// ReadAt reads from this file. Open must be called first.
|
||||
//
|
||||
// This may return io.EOF in addition to linux.Errno values.
|
||||
//
|
||||
// On the server, ReadAt has a read concurrency guarantee. See Open for
|
||||
// additional requirements regarding lazy path resolution.
|
||||
ReadAt(p []byte, offset int64) (int, error)
|
||||
|
||||
// WriteAt writes to this file. Open must be called first.
|
||||
//
|
||||
// This may return io.EOF in addition to linux.Errno values.
|
||||
//
|
||||
// On the server, WriteAt has a read concurrency guarantee. See Open
|
||||
// for additional requirements regarding lazy path resolution.
|
||||
WriteAt(p []byte, offset int64) (int, error)
|
||||
|
||||
// SetXattr sets the extended attributes attr=data of the file.
|
||||
//
|
||||
// Flags are implementation-specific, but are
|
||||
// generally Linux setxattr(2) flags.
|
||||
SetXattr(attr string, data []byte, flags XattrFlags) error
|
||||
|
||||
// GetXattr fetches the extended attribute attr of the file.
|
||||
GetXattr(attr string) ([]byte, error)
|
||||
|
||||
// ListXattrs lists the extended attribute names of the file.
|
||||
ListXattrs() ([]string, error)
|
||||
|
||||
// RemoveXattr removes the extended attribute attr from the file.
|
||||
RemoveXattr(attr string) error
|
||||
|
||||
// FSync syncs this node. Open must be called first.
|
||||
//
|
||||
// On the server, FSync has a read concurrency guarantee.
|
||||
FSync() error
|
||||
|
||||
// Lock locks the file. The operation as defined in 9P2000.L is fairly
|
||||
// ambitious, being a near-direct mapping to lockf(2)/fcntl(2)-style
|
||||
// locking, but most implementations use flock(2).
|
||||
//
|
||||
// Arguments are defined by the 9P2000.L standard.
|
||||
//
|
||||
// Pid is the PID on the client. Locktype is one of read, write, or
|
||||
// unlock (resp. 0, 1, or 2). Flags are to block (0), meaning wait; or
|
||||
// reclaim (1), which is currently "reserved for future use." Start and
|
||||
// length are the start of the region to use and the size. In many
|
||||
// implementations, they are ignored and flock(2) is used. Client is an
|
||||
// arbitrary string, also frequently unused. The Linux v9fs client
|
||||
// happens to set the client name to the node name.
|
||||
//
|
||||
// The Linux v9fs client implements fcntl(F_SETLK) by calling lock
|
||||
// without any flags set.
|
||||
//
|
||||
// The Linux v9fs client implements the fcntl(F_SETLKW) (blocking)
|
||||
// lock request by calling lock with P9_LOCK_FLAGS_BLOCK set. If the
|
||||
// response is P9_LOCK_BLOCKED, it retries the lock request in an
|
||||
// interruptible loop until status is no longer P9_LOCK_BLOCKED.
|
||||
//
|
||||
// The Linux v9fs client translates BSD advisory locks (flock) to
|
||||
// whole-file POSIX record locks. v9fs does not implement mandatory
|
||||
// locks and will return ENOLCK if use is attempted.
|
||||
//
|
||||
// In the return values, a LockStatus corresponds to an Rlock, while
|
||||
// returning an error corresponds to an Rlerror message. If any non-nil
|
||||
// error is returned, an Rlerror message will be sent.
|
||||
//
|
||||
// The most commonly used return values are success and error (resp. 0
|
||||
// and 2); blocked (1) and grace (3) are also possible.
|
||||
Lock(pid int, locktype LockType, flags LockFlags, start, length uint64, client string) (LockStatus, error)
|
||||
|
||||
// Create creates a new regular file and opens it according to the
|
||||
// flags given. This file is already Open.
|
||||
//
|
||||
// N.B. On the client, the returned file is a reference to the current
|
||||
// file, which now represents the created file. This is not the case on
|
||||
// the server. These semantics are very subtle and can easily lead to
|
||||
// bugs, but are a consequence of the 9P create operation.
|
||||
//
|
||||
// On the server, Create has a write concurrency guarantee.
|
||||
Create(name string, flags OpenFlags, permissions FileMode, uid UID, gid GID) (File, QID, uint32, error)
|
||||
|
||||
// Mkdir creates a subdirectory.
|
||||
//
|
||||
// On the server, Mkdir has a write concurrency guarantee.
|
||||
Mkdir(name string, permissions FileMode, uid UID, gid GID) (QID, error)
|
||||
|
||||
// Symlink makes a new symbolic link.
|
||||
//
|
||||
// On the server, Symlink has a write concurrency guarantee.
|
||||
Symlink(oldName string, newName string, uid UID, gid GID) (QID, error)
|
||||
|
||||
// Link makes a new hard link.
|
||||
//
|
||||
// On the server, Link has a write concurrency guarantee.
|
||||
Link(target File, newName string) error
|
||||
|
||||
// Mknod makes a new device node.
|
||||
//
|
||||
// On the server, Mknod has a write concurrency guarantee.
|
||||
Mknod(name string, mode FileMode, major uint32, minor uint32, uid UID, gid GID) (QID, error)
|
||||
|
||||
// Rename renames the file.
|
||||
//
|
||||
// Rename will never be called on the server, and RenameAt will always
|
||||
// be used instead.
|
||||
Rename(newDir File, newName string) error
|
||||
|
||||
// RenameAt renames a given file to a new name in a potentially new
|
||||
// directory.
|
||||
//
|
||||
// oldName must be a name relative to this file, which must be a
|
||||
// directory. newName is a name relative to newDir.
|
||||
//
|
||||
// On the server, RenameAt has a global concurrency guarantee.
|
||||
RenameAt(oldName string, newDir File, newName string) error
|
||||
|
||||
// UnlinkAt the given named file.
|
||||
//
|
||||
// name must be a file relative to this directory.
|
||||
//
|
||||
// Flags are implementation-specific (e.g. O_DIRECTORY), but are
|
||||
// generally Linux unlinkat(2) flags.
|
||||
//
|
||||
// On the server, UnlinkAt has a write concurrency guarantee.
|
||||
UnlinkAt(name string, flags uint32) error
|
||||
|
||||
// Readdir reads directory entries.
|
||||
//
|
||||
// offset is the entry offset, and count the number of entries to
|
||||
// return.
|
||||
//
|
||||
// This may return io.EOF in addition to linux.Errno values.
|
||||
//
|
||||
// On the server, Readdir has a read concurrency guarantee.
|
||||
Readdir(offset uint64, count uint32) (Dirents, error)
|
||||
|
||||
// Readlink reads the link target.
|
||||
//
|
||||
// On the server, Readlink has a read concurrency guarantee.
|
||||
Readlink() (string, error)
|
||||
|
||||
// Renamed is called when this node is renamed.
|
||||
//
|
||||
// This may not fail. The file will hold a reference to its parent
|
||||
// within the p9 package, and is therefore safe to use for the lifetime
|
||||
// of this File (until Close is called).
|
||||
//
|
||||
// This method should not be called by clients, who should use the
|
||||
// relevant Rename methods. (Although the method will be a no-op.)
|
||||
//
|
||||
// On the server, Renamed has a global concurrency guarantee.
|
||||
Renamed(newDir File, newName string)
|
||||
}
|
||||
|
||||
// DefaultWalkGetAttr implements File.WalkGetAttr to return ENOSYS for server-side Files.
|
||||
type DefaultWalkGetAttr struct{}
|
||||
|
||||
// WalkGetAttr implements File.WalkGetAttr.
|
||||
func (DefaultWalkGetAttr) WalkGetAttr([]string) ([]QID, File, AttrMask, Attr, error) {
|
||||
return nil, nil, AttrMask{}, Attr{}, linux.ENOSYS
|
||||
}
|
27
vendor/github.com/hugelgupf/p9/p9/fuzz.go
generated
vendored
Normal file
27
vendor/github.com/hugelgupf/p9/p9/fuzz.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
//go:build gofuzz
|
||||
// +build gofuzz
|
||||
|
||||
package p9
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/u-root/uio/ulog"
|
||||
)
|
||||
|
||||
func Fuzz(data []byte) int {
|
||||
buf := bytes.NewBuffer(data)
|
||||
tag, msg, err := recv(ulog.Null, buf, DefaultMessageSize, msgDotLRegistry.get)
|
||||
if err != nil {
|
||||
if msg != nil {
|
||||
panic("msg !=nil on error")
|
||||
}
|
||||
return 0
|
||||
}
|
||||
buf.Reset()
|
||||
send(ulog.Null, buf, tag, msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return 1
|
||||
}
|
1390
vendor/github.com/hugelgupf/p9/p9/handlers.go
generated
vendored
Normal file
1390
vendor/github.com/hugelgupf/p9/p9/handlers.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2348
vendor/github.com/hugelgupf/p9/p9/messages.go
generated
vendored
Normal file
2348
vendor/github.com/hugelgupf/p9/p9/messages.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1167
vendor/github.com/hugelgupf/p9/p9/p9.go
generated
vendored
Normal file
1167
vendor/github.com/hugelgupf/p9/p9/p9.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
238
vendor/github.com/hugelgupf/p9/p9/path_tree.go
generated
vendored
Normal file
238
vendor/github.com/hugelgupf/p9/p9/path_tree.go
generated
vendored
Normal file
@ -0,0 +1,238 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package p9
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// pathNode is a single node in a path traversal.
|
||||
//
|
||||
// These are shared by all fidRefs that point to the same path.
|
||||
//
|
||||
// Lock ordering:
|
||||
//
|
||||
// opMu
|
||||
// childMu
|
||||
//
|
||||
// Two different pathNodes may only be locked if Server.renameMu is held for
|
||||
// write, in which case they can be acquired in any order.
|
||||
type pathNode struct {
|
||||
// opMu synchronizes high-level, sematic operations, such as the
|
||||
// simultaneous creation and deletion of a file.
|
||||
opMu sync.RWMutex
|
||||
|
||||
// deleted indicates that the backing file has been deleted. We stop many
|
||||
// operations at the API level if they are incompatible with a file that has
|
||||
// already been unlinked. deleted is protected by opMu. However, it may be
|
||||
// changed without opMu if this node is deleted as part of an entire subtree
|
||||
// on unlink. So deleted must only be accessed/mutated using atomics.
|
||||
deleted uint32
|
||||
|
||||
// childMu protects the fields below.
|
||||
childMu sync.RWMutex
|
||||
|
||||
// childNodes maps child path component names to their pathNode.
|
||||
childNodes map[string]*pathNode
|
||||
|
||||
// childRefs maps child path component names to all of the their
|
||||
// references.
|
||||
childRefs map[string]map[*fidRef]struct{}
|
||||
|
||||
// childRefNames maps child references back to their path component
|
||||
// name.
|
||||
childRefNames map[*fidRef]string
|
||||
}
|
||||
|
||||
func newPathNode() *pathNode {
|
||||
return &pathNode{
|
||||
childNodes: make(map[string]*pathNode),
|
||||
childRefs: make(map[string]map[*fidRef]struct{}),
|
||||
childRefNames: make(map[*fidRef]string),
|
||||
}
|
||||
}
|
||||
|
||||
// forEachChildRef calls fn for each child reference.
|
||||
func (p *pathNode) forEachChildRef(fn func(ref *fidRef, name string)) {
|
||||
p.childMu.RLock()
|
||||
defer p.childMu.RUnlock()
|
||||
|
||||
for name, m := range p.childRefs {
|
||||
for ref := range m {
|
||||
fn(ref, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// forEachChildNode calls fn for each child pathNode.
|
||||
func (p *pathNode) forEachChildNode(fn func(pn *pathNode)) {
|
||||
p.childMu.RLock()
|
||||
defer p.childMu.RUnlock()
|
||||
|
||||
for _, pn := range p.childNodes {
|
||||
fn(pn)
|
||||
}
|
||||
}
|
||||
|
||||
// pathNodeFor returns the path node for the given name, or a new one.
|
||||
func (p *pathNode) pathNodeFor(name string) *pathNode {
|
||||
p.childMu.RLock()
|
||||
// Fast path, node already exists.
|
||||
if pn, ok := p.childNodes[name]; ok {
|
||||
p.childMu.RUnlock()
|
||||
return pn
|
||||
}
|
||||
p.childMu.RUnlock()
|
||||
|
||||
// Slow path, create a new pathNode for shared use.
|
||||
p.childMu.Lock()
|
||||
|
||||
// Re-check after re-lock.
|
||||
if pn, ok := p.childNodes[name]; ok {
|
||||
p.childMu.Unlock()
|
||||
return pn
|
||||
}
|
||||
|
||||
pn := newPathNode()
|
||||
p.childNodes[name] = pn
|
||||
p.childMu.Unlock()
|
||||
return pn
|
||||
}
|
||||
|
||||
// nameFor returns the name for the given fidRef.
|
||||
//
|
||||
// Precondition: addChild is called for ref before nameFor.
|
||||
func (p *pathNode) nameFor(ref *fidRef) string {
|
||||
p.childMu.RLock()
|
||||
n, ok := p.childRefNames[ref]
|
||||
p.childMu.RUnlock()
|
||||
|
||||
if !ok {
|
||||
// This should not happen, don't proceed.
|
||||
panic(fmt.Sprintf("expected name for %+v, none found", ref))
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
// addChildLocked adds a child reference to p.
|
||||
//
|
||||
// Precondition: As addChild, plus childMu is locked for write.
|
||||
func (p *pathNode) addChildLocked(ref *fidRef, name string) {
|
||||
if n, ok := p.childRefNames[ref]; ok {
|
||||
// This should not happen, don't proceed.
|
||||
panic(fmt.Sprintf("unexpected fidRef %+v with path %q, wanted %q", ref, n, name))
|
||||
}
|
||||
|
||||
p.childRefNames[ref] = name
|
||||
|
||||
m, ok := p.childRefs[name]
|
||||
if !ok {
|
||||
m = make(map[*fidRef]struct{})
|
||||
p.childRefs[name] = m
|
||||
}
|
||||
|
||||
m[ref] = struct{}{}
|
||||
}
|
||||
|
||||
// addChild adds a child reference to p.
|
||||
//
|
||||
// Precondition: ref may only be added once at a time.
|
||||
func (p *pathNode) addChild(ref *fidRef, name string) {
|
||||
p.childMu.Lock()
|
||||
p.addChildLocked(ref, name)
|
||||
p.childMu.Unlock()
|
||||
}
|
||||
|
||||
// removeChild removes the given child.
|
||||
//
|
||||
// This applies only to an individual fidRef, which is not required to exist.
|
||||
func (p *pathNode) removeChild(ref *fidRef) {
|
||||
p.childMu.Lock()
|
||||
|
||||
// This ref may not exist anymore. This can occur, e.g., in unlink,
|
||||
// where a removeWithName removes the ref, and then a DecRef on the ref
|
||||
// attempts to remove again.
|
||||
if name, ok := p.childRefNames[ref]; ok {
|
||||
m, ok := p.childRefs[name]
|
||||
if !ok {
|
||||
// This should not happen, don't proceed.
|
||||
p.childMu.Unlock()
|
||||
panic(fmt.Sprintf("name %s missing from childfidRefs", name))
|
||||
}
|
||||
|
||||
delete(m, ref)
|
||||
if len(m) == 0 {
|
||||
delete(p.childRefs, name)
|
||||
}
|
||||
}
|
||||
|
||||
delete(p.childRefNames, ref)
|
||||
|
||||
p.childMu.Unlock()
|
||||
}
|
||||
|
||||
// addPathNodeFor adds an existing pathNode as the node for name.
|
||||
//
|
||||
// Preconditions: newName does not exist.
|
||||
func (p *pathNode) addPathNodeFor(name string, pn *pathNode) {
|
||||
p.childMu.Lock()
|
||||
|
||||
if opn, ok := p.childNodes[name]; ok {
|
||||
p.childMu.Unlock()
|
||||
panic(fmt.Sprintf("unexpected pathNode %+v with path %q", opn, name))
|
||||
}
|
||||
|
||||
p.childNodes[name] = pn
|
||||
p.childMu.Unlock()
|
||||
}
|
||||
|
||||
// removeWithName removes all references with the given name.
|
||||
//
|
||||
// The provided function is executed after reference removal. The only method
|
||||
// it may (transitively) call on this pathNode is addChildLocked.
|
||||
//
|
||||
// If a child pathNode for name exists, it is removed from this pathNode and
|
||||
// returned by this function. Any operations on the removed tree must use this
|
||||
// value.
|
||||
func (p *pathNode) removeWithName(name string, fn func(ref *fidRef)) *pathNode {
|
||||
p.childMu.Lock()
|
||||
defer p.childMu.Unlock()
|
||||
|
||||
if m, ok := p.childRefs[name]; ok {
|
||||
for ref := range m {
|
||||
delete(m, ref)
|
||||
delete(p.childRefNames, ref)
|
||||
if fn == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Attempt to hold a reference while calling fn() to
|
||||
// prevent concurrent destruction of the child, which
|
||||
// can lead to data races. If the child has already
|
||||
// been destroyed, then we can skip the callback.
|
||||
if ref.TryIncRef() {
|
||||
fn(ref)
|
||||
ref.DecRef()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the original path node, if it exists.
|
||||
origPathNode := p.childNodes[name]
|
||||
delete(p.childNodes, name)
|
||||
return origPathNode
|
||||
}
|
65
vendor/github.com/hugelgupf/p9/p9/pool.go
generated
vendored
Normal file
65
vendor/github.com/hugelgupf/p9/p9/pool.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package p9
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// pool is a simple allocator.
|
||||
//
|
||||
// It is used for both tags and FIDs.
|
||||
type pool struct {
|
||||
mu sync.Mutex
|
||||
|
||||
// cache is the set of returned values.
|
||||
cache []uint64
|
||||
|
||||
// start is the starting value (if needed).
|
||||
start uint64
|
||||
|
||||
// limit is the upper limit.
|
||||
limit uint64
|
||||
}
|
||||
|
||||
// Get gets a value from the pool.
|
||||
func (p *pool) Get() (uint64, bool) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Anything cached?
|
||||
if len(p.cache) > 0 {
|
||||
v := p.cache[len(p.cache)-1]
|
||||
p.cache = p.cache[:len(p.cache)-1]
|
||||
return v, true
|
||||
}
|
||||
|
||||
// Over the limit?
|
||||
if p.start == p.limit {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Generate a new value.
|
||||
v := p.start
|
||||
p.start++
|
||||
return v, true
|
||||
}
|
||||
|
||||
// Put returns a value to the pool.
|
||||
func (p *pool) Put(v uint64) {
|
||||
p.mu.Lock()
|
||||
p.cache = append(p.cache, v)
|
||||
p.mu.Unlock()
|
||||
}
|
681
vendor/github.com/hugelgupf/p9/p9/server.go
generated
vendored
Normal file
681
vendor/github.com/hugelgupf/p9/p9/server.go
generated
vendored
Normal file
@ -0,0 +1,681 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package p9
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/hugelgupf/p9/linux"
|
||||
"github.com/u-root/uio/ulog"
|
||||
)
|
||||
|
||||
// Server is a 9p2000.L server.
|
||||
type Server struct {
|
||||
// attacher provides the attach function.
|
||||
attacher Attacher
|
||||
|
||||
// pathTree is the full set of paths opened on this server.
|
||||
//
|
||||
// These may be across different connections, but rename operations
|
||||
// must be serialized globally for safely. There is a single pathTree
|
||||
// for the entire server, and not per connection.
|
||||
pathTree *pathNode
|
||||
|
||||
// renameMu is a global lock protecting rename operations. With this
|
||||
// lock, we can be certain that any given rename operation can safely
|
||||
// acquire two path nodes in any order, as all other concurrent
|
||||
// operations acquire at most a single node.
|
||||
renameMu sync.RWMutex
|
||||
|
||||
// log is a logger to log to, if specified.
|
||||
log ulog.Logger
|
||||
}
|
||||
|
||||
// ServerOpt is an optional config for a new server.
|
||||
type ServerOpt func(s *Server)
|
||||
|
||||
// WithServerLogger overrides the default logger for the server.
|
||||
func WithServerLogger(l ulog.Logger) ServerOpt {
|
||||
return func(s *Server) {
|
||||
s.log = l
|
||||
}
|
||||
}
|
||||
|
||||
// NewServer returns a new server.
|
||||
func NewServer(attacher Attacher, o ...ServerOpt) *Server {
|
||||
s := &Server{
|
||||
attacher: attacher,
|
||||
pathTree: newPathNode(),
|
||||
log: ulog.Null,
|
||||
}
|
||||
for _, opt := range o {
|
||||
opt(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// connState is the state for a single connection.
|
||||
type connState struct {
|
||||
// server is the backing server.
|
||||
server *Server
|
||||
|
||||
// fids is the set of active fids.
|
||||
//
|
||||
// This is used to find fids for files.
|
||||
fidMu sync.Mutex
|
||||
fids map[fid]*fidRef
|
||||
|
||||
// tags is the set of active tags.
|
||||
//
|
||||
// The given channel is closed when the
|
||||
// tag is finished with processing.
|
||||
tagMu sync.Mutex
|
||||
tags map[tag]chan struct{}
|
||||
|
||||
// messageSize is the maximum message size. The server does not
|
||||
// do automatic splitting of messages.
|
||||
messageSize uint32
|
||||
readBufPool sync.Pool
|
||||
pristineZeros []byte
|
||||
|
||||
// baseVersion is the version of 9P protocol.
|
||||
baseVersion baseVersion
|
||||
|
||||
// version is the agreed upon version X of 9P2000.L.Google.X.
|
||||
// version 0 implies 9P2000.L.
|
||||
version uint32
|
||||
|
||||
// pendingWg counts requests that are still being handled.
|
||||
pendingWg sync.WaitGroup
|
||||
|
||||
// recvMu serializes receiving from t.
|
||||
recvMu sync.Mutex
|
||||
|
||||
// recvIdle is the number of goroutines in handleRequests() attempting to
|
||||
// lock recvMu so that they can receive from t. recvIdle is accessed
|
||||
// using atomic memory operations.
|
||||
recvIdle int32
|
||||
|
||||
// If recvShutdown is true, at least one goroutine has observed a
|
||||
// connection error while receiving from t, and all goroutines in
|
||||
// handleRequests() should exit immediately. recvShutdown is protected
|
||||
// by recvMu.
|
||||
recvShutdown bool
|
||||
|
||||
// sendMu serializes sending to r.
|
||||
sendMu sync.Mutex
|
||||
|
||||
// t reads T messages and r write R messages
|
||||
t io.ReadCloser
|
||||
r io.WriteCloser
|
||||
}
|
||||
|
||||
// xattrOp is the xattr related operations, walk or create.
|
||||
type xattrOp int
|
||||
|
||||
const (
|
||||
xattrNone = 0
|
||||
xattrCreate = 1
|
||||
xattrWalk = 2
|
||||
)
|
||||
|
||||
type pendingXattr struct {
|
||||
// the pending xattr-related operation
|
||||
op xattrOp
|
||||
|
||||
// name is the attribute.
|
||||
name string
|
||||
|
||||
// size of the attribute value, represents the
|
||||
// length of the attribute value that is going to write to or read from a file.
|
||||
size uint64
|
||||
|
||||
// flags associated with a txattrcreate message.
|
||||
// generally Linux setxattr(2) flags.
|
||||
flags XattrFlags
|
||||
|
||||
// saved up xattr operation value (for reads, listed / gotten buffer --
|
||||
// ready for chunking; for writes, this is used to accumulate chunked
|
||||
// values until a Tclunk actuates the operation)
|
||||
buf []byte
|
||||
}
|
||||
|
||||
// fidRef wraps a node and tracks references.
|
||||
type fidRef struct {
|
||||
// server is the associated server.
|
||||
server *Server
|
||||
|
||||
// file is the associated File.
|
||||
file File
|
||||
|
||||
// pendingXattr is the xattr-related operations that are going to be done
|
||||
// in a tread or twrite request.
|
||||
pendingXattr pendingXattr
|
||||
|
||||
// refs is an active refence count.
|
||||
//
|
||||
// The node above will be closed only when refs reaches zero.
|
||||
refs int64
|
||||
|
||||
// opened indicates whether this has been opened already.
|
||||
//
|
||||
// This is updated in handlers.go.
|
||||
//
|
||||
// opened is protected by pathNode.opMu or renameMu (for write).
|
||||
opened bool
|
||||
|
||||
// mode is the fidRef's mode from the walk. Only the type bits are
|
||||
// valid, the permissions may change. This is used to sanity check
|
||||
// operations on this element, and prevent walks across
|
||||
// non-directories.
|
||||
mode FileMode
|
||||
|
||||
// openFlags is the mode used in the open.
|
||||
//
|
||||
// This is updated in handlers.go.
|
||||
//
|
||||
// opened is protected by pathNode.opMu or renameMu (for write).
|
||||
openFlags OpenFlags
|
||||
|
||||
// pathNode is the current pathNode for this fid.
|
||||
pathNode *pathNode
|
||||
|
||||
// parent is the parent fidRef. We hold on to a parent reference to
|
||||
// ensure that hooks, such as Renamed, can be executed safely by the
|
||||
// server code.
|
||||
//
|
||||
// Note that parent cannot be changed without holding both the global
|
||||
// rename lock and a writable lock on the associated pathNode for this
|
||||
// fidRef. Holding either of these locks is sufficient to examine
|
||||
// parent safely.
|
||||
//
|
||||
// The parent will be nil for root fidRefs, and non-nil otherwise. The
|
||||
// method maybeParent can be used to return a cyclical reference, and
|
||||
// isRoot should be used to check for root over looking at parent
|
||||
// directly.
|
||||
parent *fidRef
|
||||
}
|
||||
|
||||
// IncRef increases the references on a fid.
|
||||
func (f *fidRef) IncRef() {
|
||||
atomic.AddInt64(&f.refs, 1)
|
||||
}
|
||||
|
||||
// DecRef should be called when you're finished with a fid.
|
||||
func (f *fidRef) DecRef() error {
|
||||
if atomic.AddInt64(&f.refs, -1) == 0 {
|
||||
var (
|
||||
errs []error
|
||||
err = f.file.Close()
|
||||
)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("file: %w", err)
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
// Drop the parent reference.
|
||||
//
|
||||
// Since this fidRef is guaranteed to be non-discoverable when
|
||||
// the references reach zero, we don't need to worry about
|
||||
// clearing the parent.
|
||||
if f.parent != nil {
|
||||
// If we've been previously deleted, removing this
|
||||
// ref is a no-op. That's expected.
|
||||
f.parent.pathNode.removeChild(f)
|
||||
if pErr := f.parent.DecRef(); pErr != nil {
|
||||
pErr = fmt.Errorf("parent: %w", pErr)
|
||||
errs = append(errs, pErr)
|
||||
}
|
||||
}
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TryIncRef returns true if a new reference is taken on the fid, and false if
|
||||
// the fid has been destroyed.
|
||||
func (f *fidRef) TryIncRef() bool {
|
||||
for {
|
||||
r := atomic.LoadInt64(&f.refs)
|
||||
if r <= 0 {
|
||||
return false
|
||||
}
|
||||
if atomic.CompareAndSwapInt64(&f.refs, r, r+1) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// isDeleted returns true if this fidRef has been deleted.
|
||||
//
|
||||
// Precondition: this must be called via safelyRead, safelyWrite or
|
||||
// safelyGlobal.
|
||||
func (f *fidRef) isDeleted() bool {
|
||||
return atomic.LoadUint32(&f.pathNode.deleted) != 0
|
||||
}
|
||||
|
||||
// isRoot indicates whether this is a root fid.
|
||||
func (f *fidRef) isRoot() bool {
|
||||
return f.parent == nil
|
||||
}
|
||||
|
||||
// maybeParent returns a cyclic reference for roots, and the parent otherwise.
|
||||
func (f *fidRef) maybeParent() *fidRef {
|
||||
if f.parent != nil {
|
||||
return f.parent
|
||||
}
|
||||
return f // Root has itself.
|
||||
}
|
||||
|
||||
// notifyDelete marks all fidRefs as deleted.
|
||||
//
|
||||
// Precondition: this must be called via safelyWrite or safelyGlobal.
|
||||
func notifyDelete(pn *pathNode) {
|
||||
atomic.StoreUint32(&pn.deleted, 1)
|
||||
|
||||
// Call on all subtrees.
|
||||
pn.forEachChildNode(func(pn *pathNode) {
|
||||
notifyDelete(pn)
|
||||
})
|
||||
}
|
||||
|
||||
// markChildDeleted marks all children below the given name as deleted.
|
||||
//
|
||||
// Precondition: this must be called via safelyWrite or safelyGlobal.
|
||||
func (f *fidRef) markChildDeleted(name string) {
|
||||
if origPathNode := f.pathNode.removeWithName(name, nil); origPathNode != nil {
|
||||
// Mark all children as deleted.
|
||||
notifyDelete(origPathNode)
|
||||
}
|
||||
}
|
||||
|
||||
// notifyNameChange calls the relevant Renamed method on all nodes in the path,
|
||||
// recursively. Note that this applies only for subtrees, as these
|
||||
// notifications do not apply to the actual file whose name has changed.
|
||||
//
|
||||
// Precondition: this must be called via safelyGlobal.
|
||||
func notifyNameChange(pn *pathNode) {
|
||||
// Call on all local references.
|
||||
pn.forEachChildRef(func(ref *fidRef, name string) {
|
||||
ref.file.Renamed(ref.parent.file, name)
|
||||
})
|
||||
|
||||
// Call on all subtrees.
|
||||
pn.forEachChildNode(func(pn *pathNode) {
|
||||
notifyNameChange(pn)
|
||||
})
|
||||
}
|
||||
|
||||
// renameChildTo renames the given child to the target.
|
||||
//
|
||||
// Precondition: this must be called via safelyGlobal.
|
||||
func (f *fidRef) renameChildTo(oldName string, target *fidRef, newName string) {
|
||||
target.markChildDeleted(newName)
|
||||
origPathNode := f.pathNode.removeWithName(oldName, func(ref *fidRef) {
|
||||
// N.B. DecRef can take f.pathNode's parent's childMu. This is
|
||||
// allowed because renameMu is held for write via safelyGlobal.
|
||||
ref.parent.DecRef() // Drop original reference.
|
||||
ref.parent = target // Change parent.
|
||||
ref.parent.IncRef() // Acquire new one.
|
||||
if f.pathNode == target.pathNode {
|
||||
target.pathNode.addChildLocked(ref, newName)
|
||||
} else {
|
||||
target.pathNode.addChild(ref, newName)
|
||||
}
|
||||
ref.file.Renamed(target.file, newName)
|
||||
})
|
||||
|
||||
if origPathNode != nil {
|
||||
// Replace the previous (now deleted) path node.
|
||||
target.pathNode.addPathNodeFor(newName, origPathNode)
|
||||
// Call Renamed on all children.
|
||||
notifyNameChange(origPathNode)
|
||||
}
|
||||
}
|
||||
|
||||
// safelyRead executes the given operation with the local path node locked.
|
||||
// This implies that paths will not change during the operation.
|
||||
func (f *fidRef) safelyRead(fn func() error) (err error) {
|
||||
f.server.renameMu.RLock()
|
||||
defer f.server.renameMu.RUnlock()
|
||||
f.pathNode.opMu.RLock()
|
||||
defer f.pathNode.opMu.RUnlock()
|
||||
return fn()
|
||||
}
|
||||
|
||||
// safelyWrite executes the given operation with the local path node locked in
|
||||
// a writable fashion. This implies some paths may change.
|
||||
func (f *fidRef) safelyWrite(fn func() error) (err error) {
|
||||
f.server.renameMu.RLock()
|
||||
defer f.server.renameMu.RUnlock()
|
||||
f.pathNode.opMu.Lock()
|
||||
defer f.pathNode.opMu.Unlock()
|
||||
return fn()
|
||||
}
|
||||
|
||||
// safelyGlobal executes the given operation with the global path lock held.
|
||||
func (f *fidRef) safelyGlobal(fn func() error) (err error) {
|
||||
f.server.renameMu.Lock()
|
||||
defer f.server.renameMu.Unlock()
|
||||
return fn()
|
||||
}
|
||||
|
||||
// Lookupfid finds the given fid.
|
||||
//
|
||||
// You should call fid.DecRef when you are finished using the fid.
|
||||
func (cs *connState) LookupFID(fid fid) (*fidRef, bool) {
|
||||
cs.fidMu.Lock()
|
||||
defer cs.fidMu.Unlock()
|
||||
fidRef, ok := cs.fids[fid]
|
||||
if ok {
|
||||
fidRef.IncRef()
|
||||
return fidRef, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Insertfid installs the given fid.
|
||||
//
|
||||
// This fid starts with a reference count of one. If a fid exists in
|
||||
// the slot already it is closed, per the specification.
|
||||
func (cs *connState) InsertFID(fid fid, newRef *fidRef) {
|
||||
cs.fidMu.Lock()
|
||||
defer cs.fidMu.Unlock()
|
||||
origRef, ok := cs.fids[fid]
|
||||
if ok {
|
||||
defer origRef.DecRef()
|
||||
}
|
||||
newRef.IncRef()
|
||||
cs.fids[fid] = newRef
|
||||
}
|
||||
|
||||
// Deletefid removes the given fid.
|
||||
//
|
||||
// This simply removes it from the map and drops a reference.
|
||||
func (cs *connState) DeleteFID(fid fid) error {
|
||||
cs.fidMu.Lock()
|
||||
defer cs.fidMu.Unlock()
|
||||
fidRef, ok := cs.fids[fid]
|
||||
if !ok {
|
||||
return linux.EBADF
|
||||
}
|
||||
delete(cs.fids, fid)
|
||||
return fidRef.DecRef()
|
||||
}
|
||||
|
||||
// StartTag starts handling the tag.
|
||||
//
|
||||
// False is returned if this tag is already active.
|
||||
func (cs *connState) StartTag(t tag) bool {
|
||||
cs.tagMu.Lock()
|
||||
defer cs.tagMu.Unlock()
|
||||
_, ok := cs.tags[t]
|
||||
if ok {
|
||||
return false
|
||||
}
|
||||
cs.tags[t] = make(chan struct{})
|
||||
return true
|
||||
}
|
||||
|
||||
// ClearTag finishes handling a tag.
|
||||
func (cs *connState) ClearTag(t tag) {
|
||||
cs.tagMu.Lock()
|
||||
defer cs.tagMu.Unlock()
|
||||
ch, ok := cs.tags[t]
|
||||
if !ok {
|
||||
// Should never happen.
|
||||
panic("unused tag cleared")
|
||||
}
|
||||
delete(cs.tags, t)
|
||||
|
||||
// Notify.
|
||||
close(ch)
|
||||
}
|
||||
|
||||
// Waittag waits for a tag to finish.
|
||||
func (cs *connState) WaitTag(t tag) {
|
||||
cs.tagMu.Lock()
|
||||
ch, ok := cs.tags[t]
|
||||
cs.tagMu.Unlock()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for close.
|
||||
<-ch
|
||||
}
|
||||
|
||||
// handleRequest handles a single request.
|
||||
//
|
||||
// The recvDone channel is signaled when recv is done (with a error if
|
||||
// necessary). The sendDone channel is signaled with the result of the send.
|
||||
func (cs *connState) handleRequest() bool {
|
||||
cs.pendingWg.Add(1)
|
||||
defer cs.pendingWg.Done()
|
||||
|
||||
// Obtain the right to receive a message from cs.t.
|
||||
atomic.AddInt32(&cs.recvIdle, 1)
|
||||
cs.recvMu.Lock()
|
||||
atomic.AddInt32(&cs.recvIdle, -1)
|
||||
|
||||
if cs.recvShutdown {
|
||||
// Another goroutine already detected a connection problem; exit
|
||||
// immediately.
|
||||
cs.recvMu.Unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
messageSize := atomic.LoadUint32(&cs.messageSize)
|
||||
if messageSize == 0 {
|
||||
// Default or not yet negotiated.
|
||||
messageSize = maximumLength
|
||||
}
|
||||
|
||||
// Receive a message.
|
||||
tag, m, err := recv(cs.server.log, cs.t, messageSize, msgDotLRegistry.get)
|
||||
if errSocket, ok := err.(ConnError); ok {
|
||||
if errSocket.error != io.EOF {
|
||||
// Connection problem; stop serving.
|
||||
cs.server.log.Printf("p9.recv: %v", errSocket.error)
|
||||
}
|
||||
cs.recvShutdown = true
|
||||
cs.recvMu.Unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
// Ensure that another goroutine is available to receive from cs.t.
|
||||
if atomic.LoadInt32(&cs.recvIdle) == 0 {
|
||||
go cs.handleRequests() // S/R-SAFE: Irrelevant.
|
||||
}
|
||||
cs.recvMu.Unlock()
|
||||
|
||||
// Deal with other errors.
|
||||
if err != nil && err != io.EOF {
|
||||
// If it's not a connection error, but some other protocol error,
|
||||
// we can send a response immediately.
|
||||
cs.sendMu.Lock()
|
||||
err := send(cs.server.log, cs.r, tag, newErr(err))
|
||||
cs.sendMu.Unlock()
|
||||
if err != nil {
|
||||
cs.server.log.Printf("p9.send: %v", err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Try to start the tag.
|
||||
if !cs.StartTag(tag) {
|
||||
cs.server.log.Printf("no valid tag [%05d]", tag)
|
||||
// Nothing we can do at this point; client is bogus.
|
||||
return true
|
||||
}
|
||||
|
||||
// Handle the message.
|
||||
r := cs.handle(m)
|
||||
|
||||
// Clear the tag before sending. That's because as soon as this
|
||||
// hits the wire, the client can legally send another message
|
||||
// with the same tag.
|
||||
cs.ClearTag(tag)
|
||||
|
||||
// Send back the result.
|
||||
cs.sendMu.Lock()
|
||||
err = send(cs.server.log, cs.r, tag, r)
|
||||
cs.sendMu.Unlock()
|
||||
if err != nil {
|
||||
cs.server.log.Printf("p9.send: %v", err)
|
||||
}
|
||||
|
||||
msgDotLRegistry.put(m)
|
||||
m = nil // 'm' should not be touched after this point.
|
||||
return true
|
||||
}
|
||||
|
||||
func (cs *connState) handle(m message) (r message) {
|
||||
defer func() {
|
||||
if r == nil {
|
||||
// Don't allow a panic to propagate.
|
||||
err := recover()
|
||||
|
||||
// Include a useful log message.
|
||||
cs.server.log.Printf("panic in handler - %v: %s", err, debug.Stack())
|
||||
|
||||
// Wrap in an EFAULT error; we don't really have a
|
||||
// better way to describe this kind of error. It will
|
||||
// usually manifest as a result of the test framework.
|
||||
r = newErr(linux.EFAULT)
|
||||
}
|
||||
}()
|
||||
|
||||
if handler, ok := m.(handler); ok {
|
||||
// Call the message handler.
|
||||
r = handler.handle(cs)
|
||||
} else {
|
||||
// Produce an ENOSYS error.
|
||||
r = newErr(linux.ENOSYS)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cs *connState) handleRequests() {
|
||||
for {
|
||||
if !cs.handleRequest() {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *connState) stop() {
|
||||
// Wait for completion of all inflight request goroutines.. If a
|
||||
// request is stuck, something has the opportunity to kill us with
|
||||
// SIGABRT to get a stack dump of the offending handler.
|
||||
cs.pendingWg.Wait()
|
||||
|
||||
// Ensure the connection is closed.
|
||||
cs.r.Close()
|
||||
cs.t.Close()
|
||||
|
||||
for _, fidRef := range cs.fids {
|
||||
// Drop final reference in the fid table. Note this should
|
||||
// always close the file, since we've ensured that there are no
|
||||
// handlers running via the wait for Pending => 0 below.
|
||||
fidRef.DecRef()
|
||||
}
|
||||
}
|
||||
|
||||
// Handle handles a single connection.
|
||||
func (s *Server) Handle(t io.ReadCloser, r io.WriteCloser) error {
|
||||
cs := &connState{
|
||||
server: s,
|
||||
t: t,
|
||||
r: r,
|
||||
fids: make(map[fid]*fidRef),
|
||||
tags: make(map[tag]chan struct{}),
|
||||
}
|
||||
defer cs.stop()
|
||||
|
||||
// Serve requests from t in the current goroutine; handleRequests()
|
||||
// will create more goroutines as needed.
|
||||
cs.handleRequests()
|
||||
return nil
|
||||
}
|
||||
|
||||
func isErrClosing(err error) bool {
|
||||
return strings.Contains(err.Error(), "use of closed network connection")
|
||||
}
|
||||
|
||||
// Serve handles requests from the bound socket.
|
||||
//
|
||||
// The passed serverSocket _must_ be created in packet mode.
|
||||
func (s *Server) Serve(serverSocket net.Listener) error {
|
||||
return s.ServeContext(nil, serverSocket)
|
||||
}
|
||||
|
||||
var errAlreadyClosed = errors.New("already closed")
|
||||
|
||||
// ServeContext handles requests from the bound socket.
|
||||
//
|
||||
// The passed serverSocket _must_ be created in packet mode.
|
||||
//
|
||||
// When the context is done, the listener is closed and serve returns once
|
||||
// every request has been handled.
|
||||
func (s *Server) ServeContext(ctx context.Context, serverSocket net.Listener) error {
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
var cancelCause context.CancelCauseFunc
|
||||
if ctx != nil {
|
||||
ctx, cancelCause = context.WithCancelCause(ctx)
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
<-ctx.Done()
|
||||
|
||||
// Only close the server socket if it wasn't already closed.
|
||||
if err := ctx.Err(); errors.Is(err, errAlreadyClosed) {
|
||||
return
|
||||
}
|
||||
serverSocket.Close()
|
||||
}()
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := serverSocket.Accept()
|
||||
if err != nil {
|
||||
if cancelCause != nil {
|
||||
cancelCause(errAlreadyClosed)
|
||||
}
|
||||
if isErrClosing(err) {
|
||||
return nil
|
||||
}
|
||||
// Something went wrong.
|
||||
return err
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func(conn net.Conn) { // S/R-SAFE: Irrelevant.
|
||||
s.Handle(conn, conn)
|
||||
wg.Done()
|
||||
}(conn)
|
||||
}
|
||||
}
|
245
vendor/github.com/hugelgupf/p9/p9/transport.go
generated
vendored
Normal file
245
vendor/github.com/hugelgupf/p9/p9/transport.go
generated
vendored
Normal file
@ -0,0 +1,245 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package p9
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/hugelgupf/p9/vecnet"
|
||||
"github.com/u-root/uio/ulog"
|
||||
)
|
||||
|
||||
// ConnError is returned in cases of a connection issue.
|
||||
//
|
||||
// This may be treated differently than other errors.
|
||||
type ConnError struct {
|
||||
// error is the socket error.
|
||||
error
|
||||
}
|
||||
|
||||
func (e ConnError) Error() string {
|
||||
return fmt.Sprintf("socket error: %v", e.error)
|
||||
}
|
||||
|
||||
// Is reports whether any error in err's chain matches target.
|
||||
func (e ConnError) Is(target error) bool { return target == e.error }
|
||||
|
||||
// ErrMessageTooLarge indicates the size was larger than reasonable.
|
||||
type ErrMessageTooLarge struct {
|
||||
size uint32
|
||||
msize uint32
|
||||
}
|
||||
|
||||
// Error returns a sensible error.
|
||||
func (e *ErrMessageTooLarge) Error() string {
|
||||
return fmt.Sprintf("message too large for fixed buffer: size is %d, limit is %d", e.size, e.msize)
|
||||
}
|
||||
|
||||
// ErrNoValidMessage indicates no valid message could be decoded.
|
||||
var ErrNoValidMessage = errors.New("buffer contained no valid message")
|
||||
|
||||
const (
|
||||
// headerLength is the number of bytes required for a header.
|
||||
headerLength uint32 = 7
|
||||
|
||||
// maximumLength is the largest possible message.
|
||||
maximumLength uint32 = 4 * 1024 * 1024
|
||||
|
||||
// initialBufferLength is the initial data buffer we allocate.
|
||||
initialBufferLength uint32 = 64
|
||||
)
|
||||
|
||||
var dataPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
// These buffers are used for decoding without a payload.
|
||||
// We need to return a pointer to avoid unnecessary allocations
|
||||
// (see https://staticcheck.io/docs/checks#SA6002).
|
||||
b := make([]byte, initialBufferLength)
|
||||
return &b
|
||||
},
|
||||
}
|
||||
|
||||
// send sends the given message over the socket.
|
||||
func send(l ulog.Logger, w io.Writer, tag tag, m message) error {
|
||||
data := dataPool.Get().(*[]byte)
|
||||
dataBuf := buffer{data: (*data)[:0]}
|
||||
|
||||
// Encode the message. The buffer will grow automatically.
|
||||
m.encode(&dataBuf)
|
||||
|
||||
l.Printf("send [w %p] [Tag %06d] %s", w, tag, m)
|
||||
|
||||
// Get our vectors to send.
|
||||
var hdr [headerLength]byte
|
||||
vecs := make(net.Buffers, 0, 3)
|
||||
vecs = append(vecs, hdr[:])
|
||||
if len(dataBuf.data) > 0 {
|
||||
vecs = append(vecs, dataBuf.data)
|
||||
}
|
||||
totalLength := headerLength + uint32(len(dataBuf.data))
|
||||
|
||||
// Is there a payload?
|
||||
if payloader, ok := m.(payloader); ok {
|
||||
p := payloader.Payload()
|
||||
if len(p) > 0 {
|
||||
vecs = append(vecs, p)
|
||||
totalLength += uint32(len(p))
|
||||
}
|
||||
defer payloader.PayloadCleanup()
|
||||
}
|
||||
|
||||
// Construct the header.
|
||||
headerBuf := buffer{data: hdr[:0]}
|
||||
headerBuf.Write32(totalLength)
|
||||
headerBuf.WriteMsgType(m.typ())
|
||||
headerBuf.WriteTag(tag)
|
||||
|
||||
if _, err := vecs.WriteTo(w); err != nil {
|
||||
return ConnError{err}
|
||||
}
|
||||
|
||||
// All set.
|
||||
dataPool.Put(&dataBuf.data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// lookupTagAndType looks up an existing message or creates a new one.
|
||||
//
|
||||
// This is called by recv after decoding the header. Any error returned will be
|
||||
// propagating back to the caller. You may use messageByType directly as a
|
||||
// lookupTagAndType function (by design).
|
||||
type lookupTagAndType func(tag tag, t msgType) (message, error)
|
||||
|
||||
// recv decodes a message from the socket.
|
||||
//
|
||||
// This is done in two parts, and is thus not safe for multiple callers.
|
||||
//
|
||||
// On a socket error, the special error type ErrSocket is returned.
|
||||
//
|
||||
// The tag value NoTag will always be returned if err is non-nil.
|
||||
func recv(l ulog.Logger, r io.Reader, msize uint32, lookup lookupTagAndType) (tag, message, error) {
|
||||
// Read a header.
|
||||
var hdr [headerLength]byte
|
||||
|
||||
if _, err := io.ReadAtLeast(r, hdr[:], int(headerLength)); err != nil {
|
||||
return noTag, nil, ConnError{err}
|
||||
}
|
||||
|
||||
// Decode the header.
|
||||
headerBuf := buffer{data: hdr[:]}
|
||||
size := headerBuf.Read32()
|
||||
t := headerBuf.ReadMsgType()
|
||||
tag := headerBuf.ReadTag()
|
||||
if size < headerLength {
|
||||
// The message is too small.
|
||||
//
|
||||
// See above: it's probably screwed.
|
||||
return noTag, nil, ConnError{ErrNoValidMessage}
|
||||
}
|
||||
if size > maximumLength || size > msize {
|
||||
// The message is too big.
|
||||
return noTag, nil, ConnError{&ErrMessageTooLarge{size, msize}}
|
||||
}
|
||||
remaining := size - headerLength
|
||||
|
||||
// Find our message to decode.
|
||||
m, err := lookup(tag, t)
|
||||
if err != nil {
|
||||
// Throw away the contents of this message.
|
||||
if remaining > 0 {
|
||||
_, _ = io.Copy(ioutil.Discard, io.LimitReader(r, int64(remaining)))
|
||||
}
|
||||
return tag, nil, err
|
||||
}
|
||||
|
||||
// Not yet initialized.
|
||||
var dataBuf buffer
|
||||
var vecs vecnet.Buffers
|
||||
|
||||
appendBuffer := func(size int) *[]byte {
|
||||
// Pull a data buffer from the pool.
|
||||
datap := dataPool.Get().(*[]byte)
|
||||
data := *datap
|
||||
if size > len(data) {
|
||||
// Create a larger data buffer.
|
||||
data = make([]byte, size)
|
||||
datap = &data
|
||||
} else {
|
||||
// Limit the data buffer.
|
||||
data = data[:size]
|
||||
}
|
||||
dataBuf = buffer{data: data}
|
||||
vecs = append(vecs, data)
|
||||
return datap
|
||||
}
|
||||
|
||||
// Read the rest of the payload.
|
||||
//
|
||||
// This requires some special care to ensure that the vectors all line
|
||||
// up the way they should. We do this to minimize copying data around.
|
||||
if payloader, ok := m.(payloader); ok {
|
||||
fixedSize := payloader.FixedSize()
|
||||
|
||||
// Do we need more than there is?
|
||||
if fixedSize > remaining {
|
||||
// This is not a valid message.
|
||||
if remaining > 0 {
|
||||
_, _ = io.Copy(ioutil.Discard, io.LimitReader(r, int64(remaining)))
|
||||
}
|
||||
return noTag, nil, ErrNoValidMessage
|
||||
}
|
||||
|
||||
if fixedSize != 0 {
|
||||
datap := appendBuffer(int(fixedSize))
|
||||
defer dataPool.Put(datap)
|
||||
}
|
||||
|
||||
// Include the payload.
|
||||
p := payloader.Payload()
|
||||
if p == nil || len(p) != int(remaining-fixedSize) {
|
||||
p = make([]byte, remaining-fixedSize)
|
||||
payloader.SetPayload(p)
|
||||
}
|
||||
if len(p) > 0 {
|
||||
vecs = append(vecs, p)
|
||||
}
|
||||
} else if remaining != 0 {
|
||||
datap := appendBuffer(int(remaining))
|
||||
defer dataPool.Put(datap)
|
||||
}
|
||||
|
||||
if len(vecs) > 0 {
|
||||
if _, err := vecs.ReadFrom(r); err != nil {
|
||||
return noTag, nil, ConnError{err}
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the message data.
|
||||
m.decode(&dataBuf)
|
||||
if dataBuf.isOverrun() {
|
||||
// No need to drain the socket.
|
||||
return noTag, nil, ErrNoValidMessage
|
||||
}
|
||||
|
||||
l.Printf("recv [r %p] [Tag %06d] %s", r, tag, m)
|
||||
|
||||
// All set.
|
||||
return tag, m, nil
|
||||
}
|
134
vendor/github.com/hugelgupf/p9/p9/version.go
generated
vendored
Normal file
134
vendor/github.com/hugelgupf/p9/p9/version.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package p9
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// highestSupportedVersion is the highest supported version X in a
|
||||
// version string of the format 9P2000.L.Google.X.
|
||||
//
|
||||
// Clients are expected to start requesting this version number and
|
||||
// to continuously decrement it until a Tversion request succeeds.
|
||||
highestSupportedVersion uint32 = 7
|
||||
|
||||
// lowestSupportedVersion is the lowest supported version X in a
|
||||
// version string of the format 9P2000.L.Google.X.
|
||||
//
|
||||
// Clients are free to send a Tversion request at a version below this
|
||||
// value but are expected to encounter an Rlerror in response.
|
||||
lowestSupportedVersion uint32 = 0
|
||||
)
|
||||
|
||||
type baseVersion string
|
||||
|
||||
const (
|
||||
undetermined baseVersion = ""
|
||||
version9P2000 baseVersion = "9P2000"
|
||||
version9P2000U baseVersion = "9P2000.u"
|
||||
version9P2000L baseVersion = "9P2000.L"
|
||||
)
|
||||
|
||||
// HighestVersionString returns the highest possible version string that a client
|
||||
// may request or a server may support.
|
||||
func HighestVersionString() string {
|
||||
return versionString(version9P2000L, highestSupportedVersion)
|
||||
}
|
||||
|
||||
// parseVersion parses a Tversion version string into a numeric version number
|
||||
// if the version string is supported by p9. Otherwise returns (0, false).
|
||||
//
|
||||
// From Tversion(9P): "Version strings are defined such that, if the client string
|
||||
// contains one or more period characters, the initial substring up to but not
|
||||
// including any single period in the version string defines a version of the protocol."
|
||||
//
|
||||
// p9 intentionally diverges from this and always requires that the version string
|
||||
// start with 9P2000.L to express that it is always compatible with 9P2000.L. The
|
||||
// only supported versions extensions are of the format 9p2000.L.Google.X where X
|
||||
// is an ever increasing version counter.
|
||||
//
|
||||
// Version 9P2000.L.Google.0 implies 9P2000.L.
|
||||
//
|
||||
// New versions must always be a strict superset of 9P2000.L. A version increase must
|
||||
// define a predicate representing the feature extension introduced by that version. The
|
||||
// predicate must be commented and should take the format:
|
||||
//
|
||||
// // VersionSupportsX returns true if version v supports X and must be checked when ...
|
||||
//
|
||||
// func VersionSupportsX(v int32) bool {
|
||||
// ...
|
||||
//
|
||||
// )
|
||||
func parseVersion(str string) (baseVersion, uint32, bool) {
|
||||
switch str {
|
||||
case "9P2000.L":
|
||||
return version9P2000L, 0, true
|
||||
case "9P2000.u":
|
||||
return version9P2000U, 0, true
|
||||
case "9P2000":
|
||||
return version9P2000, 0, true
|
||||
default:
|
||||
substr := strings.Split(str, ".")
|
||||
if len(substr) != 4 {
|
||||
return "", 0, false
|
||||
}
|
||||
if substr[0] != "9P2000" || substr[1] != "L" || substr[2] != "Google" || len(substr[3]) == 0 {
|
||||
return "", 0, false
|
||||
}
|
||||
version, err := strconv.ParseUint(substr[3], 10, 32)
|
||||
if err != nil {
|
||||
return "", 0, false
|
||||
}
|
||||
return version9P2000L, uint32(version), true
|
||||
}
|
||||
}
|
||||
|
||||
// versionString formats a p9 version number into a Tversion version string.
|
||||
func versionString(baseVersion baseVersion, version uint32) string {
|
||||
// Special case the base version so that clients expecting this string
|
||||
// instead of the 9P2000.L.Google.0 equivalent get it. This is important
|
||||
// for backwards compatibility with legacy servers that check for exactly
|
||||
// the baseVersion and allow nothing else.
|
||||
if version == 0 {
|
||||
return string(baseVersion)
|
||||
}
|
||||
return fmt.Sprintf("9P2000.L.Google.%d", version)
|
||||
}
|
||||
|
||||
// versionSupportsTwalkgetattr returns true if version v supports the
|
||||
// Twalkgetattr message. This predicate must be checked by clients before
|
||||
// attempting to make a Twalkgetattr request.
|
||||
func versionSupportsTwalkgetattr(v uint32) bool {
|
||||
return v >= 2
|
||||
}
|
||||
|
||||
// versionSupportsTucreation returns true if version v supports the Tucreation
|
||||
// messages (Tucreate, Tusymlink, Tumkdir, Tumknod). This predicate must be
|
||||
// checked by clients before attempting to make a Tucreation request.
|
||||
// If Tucreation messages are not supported, their non-UID supporting
|
||||
// counterparts (Tlcreate, Tsymlink, Tmkdir, Tmknod) should be used.
|
||||
func versionSupportsTucreation(v uint32) bool {
|
||||
return v >= 3
|
||||
}
|
||||
|
||||
// VersionSupportsMultiUser returns true if version v supports multi-user fake
|
||||
// directory permissions and ID values.
|
||||
func VersionSupportsMultiUser(v uint32) bool {
|
||||
return v >= 6
|
||||
}
|
22
vendor/github.com/hugelgupf/p9/vecnet/iov32_linux.go
generated
vendored
Normal file
22
vendor/github.com/hugelgupf/p9/vecnet/iov32_linux.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build 386 || mips || arm || mipsle
|
||||
// +build 386 mips arm mipsle
|
||||
|
||||
package vecnet
|
||||
|
||||
func iovlen(i int) uint32 {
|
||||
return uint32(i)
|
||||
}
|
22
vendor/github.com/hugelgupf/p9/vecnet/iov_linux.go
generated
vendored
Normal file
22
vendor/github.com/hugelgupf/p9/vecnet/iov_linux.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !386 && !arm && !mips && !mipsle
|
||||
// +build !386,!arm,!mips,!mipsle
|
||||
|
||||
package vecnet
|
||||
|
||||
func iovlen(i int) uint64 {
|
||||
return uint64(i)
|
||||
}
|
54
vendor/github.com/hugelgupf/p9/vecnet/vecnet.go
generated
vendored
Normal file
54
vendor/github.com/hugelgupf/p9/vecnet/vecnet.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package vecnet provides access to recvmsg syscalls on net.Conns.
|
||||
package vecnet
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Buffers points to zero or more buffers to read into.
|
||||
//
|
||||
// On connections that support it, ReadFrom is optimized into the batch read
|
||||
// operation recvmsg.
|
||||
type Buffers net.Buffers
|
||||
|
||||
// ReadFrom reads into the pre-allocated bufs. Returns bytes read.
|
||||
//
|
||||
// ReadFrom keeps reading until all bufs are filled or EOF is received.
|
||||
//
|
||||
// The pre-allocatted space used by ReadFrom is based upon slice lengths.
|
||||
func (bufs Buffers) ReadFrom(r io.Reader) (int64, error) {
|
||||
if conn, ok := r.(syscall.Conn); ok && readFromBuffers != nil {
|
||||
return readFromBuffers(bufs, conn)
|
||||
}
|
||||
|
||||
var total int64
|
||||
for _, buf := range bufs {
|
||||
for filled := 0; filled < len(buf); {
|
||||
n, err := r.Read(buf[filled:])
|
||||
total += int64(n)
|
||||
filled += n
|
||||
if (n == 0 && err == nil) || err == io.EOF {
|
||||
return total, io.EOF
|
||||
} else if err != nil {
|
||||
return total, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return total, nil
|
||||
}
|
112
vendor/github.com/hugelgupf/p9/vecnet/vecnet_linux.go
generated
vendored
Normal file
112
vendor/github.com/hugelgupf/p9/vecnet/vecnet_linux.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !386
|
||||
// +build !386
|
||||
|
||||
package vecnet
|
||||
|
||||
import (
|
||||
"io"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var readFromBuffers = readFromBuffersLinux
|
||||
|
||||
func readFromBuffersLinux(bufs Buffers, conn syscall.Conn) (int64, error) {
|
||||
rc, err := conn.SyscallConn()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
length := int64(0)
|
||||
for _, buf := range bufs {
|
||||
length += int64(len(buf))
|
||||
}
|
||||
|
||||
for n := int64(0); n < length; {
|
||||
cur, err := recvmsg(bufs, rc)
|
||||
if err != nil && (cur == 0 || err != io.EOF) {
|
||||
return n, err
|
||||
}
|
||||
n += int64(cur)
|
||||
|
||||
// Consume iovecs to retry.
|
||||
for consumed := 0; consumed < cur; {
|
||||
if len(bufs[0]) <= cur-consumed {
|
||||
consumed += len(bufs[0])
|
||||
bufs = bufs[1:]
|
||||
} else {
|
||||
bufs[0] = bufs[0][cur-consumed:]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return length, nil
|
||||
}
|
||||
|
||||
// buildIovec builds an iovec slice from the given []byte slice.
|
||||
//
|
||||
// iovecs is used as an initial slice, to avoid excessive allocations.
|
||||
func buildIovec(bufs Buffers, iovecs []syscall.Iovec) ([]syscall.Iovec, int) {
|
||||
var length int
|
||||
for _, buf := range bufs {
|
||||
if l := len(buf); l > 0 {
|
||||
iovecs = append(iovecs, syscall.Iovec{
|
||||
Base: &buf[0],
|
||||
Len: iovlen(l),
|
||||
})
|
||||
length += l
|
||||
}
|
||||
}
|
||||
return iovecs, length
|
||||
}
|
||||
|
||||
func recvmsg(bufs Buffers, rc syscall.RawConn) (int, error) {
|
||||
iovecs, length := buildIovec(bufs, make([]syscall.Iovec, 0, 2))
|
||||
|
||||
var msg syscall.Msghdr
|
||||
if len(iovecs) != 0 {
|
||||
msg.Iov = &iovecs[0]
|
||||
msg.Iovlen = iovlen(len(iovecs))
|
||||
}
|
||||
|
||||
// n is the bytes received.
|
||||
var n uintptr
|
||||
var e syscall.Errno
|
||||
err := rc.Read(func(fd uintptr) bool {
|
||||
n, _, e = syscall.Syscall(syscall.SYS_RECVMSG, fd, uintptr(unsafe.Pointer(&msg)), syscall.MSG_DONTWAIT)
|
||||
// Return false if EINTR, EAGAIN, or EWOULDBLOCK to retry.
|
||||
return !(e == syscall.EINTR || e == syscall.EAGAIN || e == syscall.EWOULDBLOCK)
|
||||
})
|
||||
runtime.KeepAlive(iovecs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if e != 0 {
|
||||
return 0, e
|
||||
}
|
||||
|
||||
// The other end is closed by returning a 0 length read with no error.
|
||||
if n == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if int(n) > length {
|
||||
return length, io.ErrShortBuffer
|
||||
}
|
||||
return int(n), nil
|
||||
}
|
24
vendor/github.com/hugelgupf/p9/vecnet/vecnet_other.go
generated
vendored
Normal file
24
vendor/github.com/hugelgupf/p9/vecnet/vecnet_other.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !linux || (linux && 386)
|
||||
// +build !linux linux,386
|
||||
|
||||
package vecnet
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var readFromBuffers func(bufs Buffers, conn syscall.Conn) (int64, error)
|
11
vendor/github.com/linuxkit/virtsock/AUTHORS
generated
vendored
Normal file
11
vendor/github.com/linuxkit/virtsock/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# This file lists all individuals having contributed content to the repository.
|
||||
# For how it is generated, see `scripts/generate-authors.sh`.
|
||||
|
||||
Ben Weedon <beweedon@microsoft.com>
|
||||
Ian Campbell <ian.campbell@docker.com>
|
||||
John Starks <jostarks@microsoft.com>
|
||||
Justin Cormack <justin.cormack@docker.com>
|
||||
Magnus Skjegstad <magnus.skjegstad@docker.com>
|
||||
Riyaz Faizullabhoy <riyaz.faizullabhoy@docker.com>
|
||||
Rolf Neugebauer <rolf.neugebauer@docker.com>
|
||||
Simon Ferquel <simon.ferquel@docker.com>
|
16
vendor/github.com/linuxkit/virtsock/LICENSE
generated
vendored
Normal file
16
vendor/github.com/linuxkit/virtsock/LICENSE
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
Unless explicitly stated at the top of the file, this code is covered
|
||||
by the following license:
|
||||
|
||||
Copyright 2016-2017 The authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
115
vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock.go
generated
vendored
Normal file
115
vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock.go
generated
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
// Package hvsock provides a Go interface to Hyper-V sockets both on
|
||||
// Windows and on Linux. The Linux bindings require patches for the
|
||||
// 4.9.x kernel. If you are using a Linux kernel 4.14.x or newer you
|
||||
// should use the vsock package instead as the Hyper-V socket support
|
||||
// in these kernels have been merged with the virtio sockets
|
||||
// implementation.
|
||||
package hvsock
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var (
|
||||
// GUIDZero used by listeners to accept connections from all partitions
|
||||
GUIDZero, _ = GUIDFromString("00000000-0000-0000-0000-000000000000")
|
||||
// GUIDWildcard used by listeners to accept connections from all partitions
|
||||
GUIDWildcard, _ = GUIDFromString("00000000-0000-0000-0000-000000000000")
|
||||
// GUIDBroadcast undocumented
|
||||
GUIDBroadcast, _ = GUIDFromString("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF")
|
||||
// GUIDChildren used by listeners to accept connections from children
|
||||
GUIDChildren, _ = GUIDFromString("90db8b89-0d35-4f79-8ce9-49ea0ac8b7cd")
|
||||
// GUIDLoopback use to connect in loopback mode
|
||||
GUIDLoopback, _ = GUIDFromString("e0e16197-dd56-4a10-9195-5ee7a155a838")
|
||||
// GUIDParent use to connect to the parent partition
|
||||
GUIDParent, _ = GUIDFromString("a42e7cda-d03f-480c-9cc2-a4de20abb878")
|
||||
|
||||
// GUIDs for LinuxVMs with the new Hyper-V socket implementation need to match this template
|
||||
guidTemplate, _ = GUIDFromString("00000000-facb-11e6-bd58-64006a7986d3")
|
||||
)
|
||||
|
||||
const (
|
||||
// The Hyper-V socket implementation used in the 4.9.x kernels
|
||||
// seems to fail silently if messages are above 8k. The newer
|
||||
// implementation in the 4.14.x (and newer) kernels seems to
|
||||
// work fine with larger messages. This is constant is used as
|
||||
// a temporary workaround to limit the amount of data sent and
|
||||
// should be removed once support for 4.9.x kernels is
|
||||
// deprecated.
|
||||
maxMsgSize = 8 * 1024
|
||||
)
|
||||
|
||||
// GUID is used by Hypper-V sockets for "addresses" and "ports"
|
||||
type GUID [16]byte
|
||||
|
||||
// Convert a GUID into a string
|
||||
func (g *GUID) String() string {
|
||||
/* XXX This assume little endian */
|
||||
return fmt.Sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
g[3], g[2], g[1], g[0],
|
||||
g[5], g[4],
|
||||
g[7], g[6],
|
||||
g[8], g[9],
|
||||
g[10], g[11], g[12], g[13], g[14], g[15])
|
||||
}
|
||||
|
||||
// Port converts a Service GUID to a "port" usable by the vsock package.
|
||||
// It can be used to convert hvsock code to vsock code. On 4.14.x
|
||||
// kernels Service GUIDs for talking to Linux should have the form of
|
||||
// xxxxxxxx-facb-11e6-bd58-64006a7986d3, where xxxxxxxx is the vsock port.
|
||||
func (g *GUID) Port() (uint32, error) {
|
||||
// Check that the GUID is as expected
|
||||
if !reflect.DeepEqual(g[4:], guidTemplate[4:]) {
|
||||
return 0, fmt.Errorf("%s does not conform with the template", g)
|
||||
}
|
||||
return binary.LittleEndian.Uint32(g[0:4]), nil
|
||||
}
|
||||
|
||||
// GUIDFromString parses a string and returns a GUID
|
||||
func GUIDFromString(s string) (GUID, error) {
|
||||
var g GUID
|
||||
var err error
|
||||
_, err = fmt.Sscanf(s, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
&g[3], &g[2], &g[1], &g[0],
|
||||
&g[5], &g[4],
|
||||
&g[7], &g[6],
|
||||
&g[8], &g[9],
|
||||
&g[10], &g[11], &g[12], &g[13], &g[14], &g[15])
|
||||
return g, err
|
||||
}
|
||||
|
||||
// Addr represents a Hyper-V socket address
|
||||
type Addr struct {
|
||||
VMID GUID
|
||||
ServiceID GUID
|
||||
}
|
||||
|
||||
// Network returns the type of network for Hyper-V sockets
|
||||
func (a Addr) Network() string {
|
||||
return "hvsock"
|
||||
}
|
||||
|
||||
func (a Addr) String() string {
|
||||
vmid := a.VMID.String()
|
||||
svc := a.ServiceID.String()
|
||||
|
||||
return vmid + ":" + svc
|
||||
}
|
||||
|
||||
// Conn is a hvsock connection which supports half-close.
|
||||
type Conn interface {
|
||||
net.Conn
|
||||
CloseRead() error
|
||||
CloseWrite() error
|
||||
}
|
||||
|
||||
// Since there doesn't seem to be a standard min function
|
||||
func min(x, y int) int {
|
||||
if x < y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
22
vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_fallback.go
generated
vendored
Normal file
22
vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_fallback.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// +build !linux,!windows
|
||||
|
||||
package hvsock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// Supported returns if hvsocks are supported on your platform
|
||||
func Supported() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func Dial(raddr Addr) (Conn, error) {
|
||||
return nil, fmt.Errorf("Dial() not implemented on %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
func Listen(addr Addr) (net.Listener, error) {
|
||||
return nil, fmt.Errorf("Listen() not implemented on %s", runtime.GOOS)
|
||||
}
|
277
vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_linux.go
generated
vendored
Normal file
277
vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_linux.go
generated
vendored
Normal file
@ -0,0 +1,277 @@
|
||||
package hvsock
|
||||
|
||||
// On Linux we have to deal with two different implementations. The
|
||||
// "legacy" implementation never made it into the kernel, but several
|
||||
// kernels, including the LinuxKit one carried patches for it for
|
||||
// quite a while. The legacy version defined a new address family
|
||||
// while the new version sits on top of the existing VMware/virtio
|
||||
// socket implementation.
|
||||
//
|
||||
// We try to determine at init if we are on a kernel with the legacy
|
||||
// implementation or the new version and set "legacyMode" accordingly.
|
||||
//
|
||||
// We can't just reuse the vsock implementation as we still need to
|
||||
// emulated CloseRead()/CloseWrite() as not all Windows builds support
|
||||
// it.
|
||||
|
||||
/*
|
||||
#include <sys/socket.h>
|
||||
|
||||
struct sockaddr_hv {
|
||||
unsigned short shv_family;
|
||||
unsigned short reserved;
|
||||
unsigned char shv_vm_id[16];
|
||||
unsigned char shv_service_id[16];
|
||||
};
|
||||
int bind_sockaddr_hv(int fd, const struct sockaddr_hv *sa_hv) {
|
||||
return bind(fd, (const struct sockaddr*)sa_hv, sizeof(*sa_hv));
|
||||
}
|
||||
int connect_sockaddr_hv(int fd, const struct sockaddr_hv *sa_hv) {
|
||||
return connect(fd, (const struct sockaddr*)sa_hv, sizeof(*sa_hv));
|
||||
}
|
||||
int accept_hv(int fd, struct sockaddr_hv *sa_hv, socklen_t *sa_hv_len) {
|
||||
return accept(fd, (struct sockaddr *)sa_hv, sa_hv_len);
|
||||
}
|
||||
int getsockname_hv(int fd, struct sockaddr_hv *sa_hv, socklen_t *sa_hv_len) {
|
||||
return getsockname(fd, (struct sockaddr *)sa_hv, sa_hv_len);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
hvsockAF = 43 //SHV_PROTO_RAW
|
||||
hvsockRaw = 1 // SHV_PROTO_RAW
|
||||
)
|
||||
|
||||
// Supported returns if hvsocks are supported on your platform
|
||||
func Supported() bool {
|
||||
var sa C.struct_sockaddr_hv
|
||||
var sa_len C.socklen_t
|
||||
|
||||
// Try opening a hvsockAF socket. If it works we are on older, i.e. 4.9.x kernels.
|
||||
// 4.11 defines AF_SMC as 43 but it doesn't support protocol 1 so the
|
||||
// socket() call should fail.
|
||||
fd, err := syscall.Socket(hvsockAF, syscall.SOCK_STREAM, hvsockRaw)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// 4.16 defines SMCPROTO_SMC6 as 1 but its socket name size doesn't match
|
||||
// size of sockaddr_hv so corresponding check should fail.
|
||||
sa_len = C.sizeof_struct_sockaddr_hv
|
||||
ret, _ := C.getsockname_hv(C.int(fd), &sa, &sa_len)
|
||||
syscall.Close(fd)
|
||||
if ret < 0 || sa_len != C.sizeof_struct_sockaddr_hv {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Dial a Hyper-V socket address
|
||||
func Dial(raddr Addr) (Conn, error) {
|
||||
fd, err := syscall.Socket(hvsockAF, syscall.SOCK_STREAM, hvsockRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sa := C.struct_sockaddr_hv{}
|
||||
sa.shv_family = hvsockAF
|
||||
sa.reserved = 0
|
||||
|
||||
for i := 0; i < 16; i++ {
|
||||
sa.shv_vm_id[i] = C.uchar(raddr.VMID[i])
|
||||
}
|
||||
for i := 0; i < 16; i++ {
|
||||
sa.shv_service_id[i] = C.uchar(raddr.ServiceID[i])
|
||||
}
|
||||
|
||||
// Retry connect in a loop if EINTR is encountered.
|
||||
for {
|
||||
if ret, errno := C.connect_sockaddr_hv(C.int(fd), &sa); ret != 0 {
|
||||
if errno == syscall.EINTR {
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("connect(%s) failed with %d, errno=%d", raddr, ret, errno)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return newHVsockConn(uintptr(fd), &Addr{VMID: GUIDZero, ServiceID: GUIDZero}, &raddr), nil
|
||||
}
|
||||
|
||||
// Listen returns a net.Listener which can accept connections on the given port
|
||||
func Listen(addr Addr) (net.Listener, error) {
|
||||
fd, err := syscall.Socket(hvsockAF, syscall.SOCK_STREAM, hvsockRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sa := C.struct_sockaddr_hv{}
|
||||
sa.shv_family = hvsockAF
|
||||
sa.reserved = 0
|
||||
|
||||
for i := 0; i < 16; i++ {
|
||||
sa.shv_vm_id[i] = C.uchar(addr.VMID[i])
|
||||
}
|
||||
for i := 0; i < 16; i++ {
|
||||
sa.shv_service_id[i] = C.uchar(addr.ServiceID[i])
|
||||
}
|
||||
|
||||
if ret, errno := C.bind_sockaddr_hv(C.int(fd), &sa); ret != 0 {
|
||||
return nil, fmt.Errorf("listen(%s) failed with %d, errno=%d", addr, ret, errno)
|
||||
}
|
||||
|
||||
err = syscall.Listen(fd, syscall.SOMAXCONN)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "listen(%s) failed", addr)
|
||||
}
|
||||
return &hvsockListener{fd, addr}, nil
|
||||
}
|
||||
|
||||
//
|
||||
// Hyper-v sockets Listener implementation
|
||||
//
|
||||
|
||||
type hvsockListener struct {
|
||||
fd int
|
||||
local Addr
|
||||
}
|
||||
|
||||
// Accept accepts an incoming call and returns the new connection.
|
||||
func (v *hvsockListener) Accept() (net.Conn, error) {
|
||||
var acceptSA C.struct_sockaddr_hv
|
||||
var acceptSALen C.socklen_t
|
||||
|
||||
acceptSALen = C.sizeof_struct_sockaddr_hv
|
||||
fd, err := C.accept_hv(C.int(v.fd), &acceptSA, &acceptSALen)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "accept(%s) failed", v.local)
|
||||
}
|
||||
|
||||
remote := &Addr{VMID: guidFromC(acceptSA.shv_vm_id), ServiceID: guidFromC(acceptSA.shv_service_id)}
|
||||
return newHVsockConn(uintptr(fd), &v.local, remote), nil
|
||||
}
|
||||
|
||||
// Close closes the listening connection
|
||||
func (v *hvsockListener) Close() error {
|
||||
// Note this won't cause the Accept to unblock.
|
||||
return unix.Close(v.fd)
|
||||
}
|
||||
|
||||
// Addr returns the address the Listener is listening on
|
||||
func (v *hvsockListener) Addr() net.Addr {
|
||||
return v.local
|
||||
}
|
||||
|
||||
//
|
||||
// Hyper-V socket connection implementation
|
||||
//
|
||||
|
||||
// hvsockConn represents a connection over a Hyper-V socket
|
||||
type hvsockConn struct {
|
||||
hvsock *os.File
|
||||
fd uintptr
|
||||
local *Addr
|
||||
remote *Addr
|
||||
}
|
||||
|
||||
func newHVsockConn(fd uintptr, local, remote *Addr) *hvsockConn {
|
||||
hvsock := os.NewFile(fd, fmt.Sprintf("hvsock:%d", fd))
|
||||
return &hvsockConn{hvsock: hvsock, fd: fd, local: local, remote: remote}
|
||||
}
|
||||
|
||||
// LocalAddr returns the local address of a connection
|
||||
func (v *hvsockConn) LocalAddr() net.Addr {
|
||||
return v.local
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote address of a connection
|
||||
func (v *hvsockConn) RemoteAddr() net.Addr {
|
||||
return v.remote
|
||||
}
|
||||
|
||||
// Close closes the connection
|
||||
func (v *hvsockConn) Close() error {
|
||||
return v.hvsock.Close()
|
||||
}
|
||||
|
||||
// CloseRead shuts down the reading side of a hvsock connection
|
||||
func (v *hvsockConn) CloseRead() error {
|
||||
return syscall.Shutdown(int(v.fd), syscall.SHUT_RD)
|
||||
}
|
||||
|
||||
// CloseWrite shuts down the writing side of a hvsock connection
|
||||
func (v *hvsockConn) CloseWrite() error {
|
||||
return syscall.Shutdown(int(v.fd), syscall.SHUT_WR)
|
||||
}
|
||||
|
||||
// Read reads data from the connection
|
||||
func (v *hvsockConn) Read(buf []byte) (int, error) {
|
||||
return v.hvsock.Read(buf)
|
||||
}
|
||||
|
||||
// Write writes data over the connection
|
||||
// TODO(rn): replace with a straight call to v.hvsock.Write() once 4.9.x support is deprecated
|
||||
func (v *hvsockConn) Write(buf []byte) (int, error) {
|
||||
written := 0
|
||||
toWrite := len(buf)
|
||||
for toWrite > 0 {
|
||||
thisBatch := min(toWrite, maxMsgSize)
|
||||
n, err := v.hvsock.Write(buf[written : written+thisBatch])
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
if n != thisBatch {
|
||||
return written, fmt.Errorf("short write %d != %d", n, thisBatch)
|
||||
}
|
||||
toWrite -= n
|
||||
written += n
|
||||
}
|
||||
|
||||
return written, nil
|
||||
}
|
||||
|
||||
// SetDeadline sets the read and write deadlines associated with the connection
|
||||
func (v *hvsockConn) SetDeadline(t time.Time) error {
|
||||
return nil // FIXME
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the deadline for future Read calls.
|
||||
func (v *hvsockConn) SetReadDeadline(t time.Time) error {
|
||||
return nil // FIXME
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the deadline for future Write calls
|
||||
func (v *hvsockConn) SetWriteDeadline(t time.Time) error {
|
||||
return nil // FIXME
|
||||
}
|
||||
|
||||
// File duplicates the underlying socket descriptor and returns it.
|
||||
func (v *hvsockConn) File() (*os.File, error) {
|
||||
// This is equivalent to dup(2) but creates the new fd with CLOEXEC already set.
|
||||
r0, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, uintptr(v.hvsock.Fd()), syscall.F_DUPFD_CLOEXEC, 0)
|
||||
if e1 != 0 {
|
||||
return nil, os.NewSyscallError("fcntl", e1)
|
||||
}
|
||||
return os.NewFile(r0, v.hvsock.Name()), nil
|
||||
}
|
||||
|
||||
func guidFromC(cg [16]C.uchar) GUID {
|
||||
var g GUID
|
||||
for i := 0; i < 16; i++ {
|
||||
g[i] = byte(cg[i])
|
||||
}
|
||||
return g
|
||||
}
|
496
vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_windows.go
generated
vendored
Normal file
496
vendor/github.com/linuxkit/virtsock/pkg/hvsock/hvsock_windows.go
generated
vendored
Normal file
@ -0,0 +1,496 @@
|
||||
package hvsock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Make sure Winsock2 is initialised
|
||||
func init() {
|
||||
e := syscall.WSAStartup(uint32(0x202), &wsaData)
|
||||
if e != nil {
|
||||
log.Fatal("WSAStartup", e)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
hvsockAF = 34 // AF_HYPERV
|
||||
hvsockRaw = 1 // SHV_PROTO_RAW
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrTimeout is an error returned on timeout
|
||||
ErrTimeout = &timeoutError{}
|
||||
|
||||
wsaData syscall.WSAData
|
||||
)
|
||||
|
||||
// Supported returns if hvsocks are supported on your platform
|
||||
func Supported() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Dial a Hyper-V socket address
|
||||
func Dial(raddr Addr) (Conn, error) {
|
||||
fd, err := syscall.Socket(hvsockAF, syscall.SOCK_STREAM, hvsockRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sa rawSockaddrHyperv
|
||||
ptr, n, err := raddr.sockaddr(&sa)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := sys_connect(fd, ptr, n); err != nil {
|
||||
return nil, errors.Wrapf(err, "connect(%s) failed", raddr)
|
||||
}
|
||||
|
||||
return newHVsockConn(fd, Addr{VMID: GUIDZero, ServiceID: GUIDZero}, raddr)
|
||||
}
|
||||
|
||||
// Listen returns a net.Listener which can accept connections on the given port
|
||||
func Listen(addr Addr) (net.Listener, error) {
|
||||
fd, err := syscall.Socket(hvsockAF, syscall.SOCK_STREAM, hvsockRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sa rawSockaddrHyperv
|
||||
ptr, n, err := addr.sockaddr(&sa)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := sys_bind(fd, ptr, n); err != nil {
|
||||
return nil, fmt.Errorf("bind(%s) failed with %v", addr, err)
|
||||
}
|
||||
|
||||
err = syscall.Listen(fd, syscall.SOMAXCONN)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "listen(%s) failed", addr)
|
||||
}
|
||||
|
||||
return &hvsockListener{fd, addr}, nil
|
||||
}
|
||||
|
||||
//
|
||||
// Hyper-v sockets Listener implementation
|
||||
//
|
||||
|
||||
type hvsockListener struct {
|
||||
fd syscall.Handle
|
||||
local Addr
|
||||
}
|
||||
|
||||
// Accept accepts an incoming call and returns the new connection
|
||||
func (v *hvsockListener) Accept() (net.Conn, error) {
|
||||
var sa rawSockaddrHyperv
|
||||
var n = int32(unsafe.Sizeof(sa))
|
||||
fd, err := sys_accept(v.fd, &sa, &n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Extract an Addr from sa
|
||||
raddr := Addr{}
|
||||
for i := 0; i < len(raddr.VMID); i++ {
|
||||
raddr.VMID[i] = sa.VMID[i]
|
||||
}
|
||||
for i := 0; i < len(raddr.ServiceID); i++ {
|
||||
raddr.ServiceID[i] = sa.ServiceID[i]
|
||||
}
|
||||
return newHVsockConn(fd, v.local, raddr)
|
||||
}
|
||||
|
||||
// Close closes the listening connection
|
||||
func (v *hvsockListener) Close() error {
|
||||
return syscall.Close(v.fd)
|
||||
}
|
||||
|
||||
// Addr returns the address the Listener is listening on
|
||||
func (v *hvsockListener) Addr() net.Addr {
|
||||
return v.local
|
||||
}
|
||||
|
||||
//
|
||||
// Hyper-V socket connection implementation
|
||||
//
|
||||
|
||||
// hvsockConn represent a Hyper-V connection. Complex mostly due to asynch send()/recv() syscalls.
|
||||
type hvsockConn struct {
|
||||
fd syscall.Handle
|
||||
local Addr
|
||||
remote Addr
|
||||
|
||||
wg sync.WaitGroup
|
||||
wgLock sync.RWMutex
|
||||
closing atomicBool
|
||||
|
||||
readDeadline deadlineHandler
|
||||
writeDeadline deadlineHandler
|
||||
}
|
||||
|
||||
func newHVsockConn(h syscall.Handle, local Addr, remote Addr) (*hvsockConn, error) {
|
||||
ioInitOnce.Do(initIo)
|
||||
v := &hvsockConn{fd: h, local: local, remote: remote}
|
||||
|
||||
_, err := createIoCompletionPort(h, ioCompletionPort, 0, 0xffffffff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = setFileCompletionNotificationModes(h,
|
||||
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS|cFILE_SKIP_SET_EVENT_ON_HANDLE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.readDeadline.channel = make(timeoutChan)
|
||||
v.writeDeadline.channel = make(timeoutChan)
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// LocalAddr returns the local address of a connection
|
||||
func (v *hvsockConn) LocalAddr() net.Addr {
|
||||
return v.local
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote address of a connection
|
||||
func (v *hvsockConn) RemoteAddr() net.Addr {
|
||||
return v.remote
|
||||
}
|
||||
|
||||
// Close closes the connection
|
||||
func (v *hvsockConn) Close() error {
|
||||
v.close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// CloseRead shuts down the reading side of a hvsock connection
|
||||
func (v *hvsockConn) CloseRead() error {
|
||||
return syscall.Shutdown(v.fd, syscall.SHUT_RD)
|
||||
}
|
||||
|
||||
// CloseWrite shuts down the writing side of a hvsock connection
|
||||
func (v *hvsockConn) CloseWrite() error {
|
||||
return syscall.Shutdown(v.fd, syscall.SHUT_WR)
|
||||
}
|
||||
|
||||
// Read reads data from the connection
|
||||
func (v *hvsockConn) Read(buf []byte) (int, error) {
|
||||
var b syscall.WSABuf
|
||||
var f uint32
|
||||
|
||||
b.Len = uint32(len(buf))
|
||||
b.Buf = &buf[0]
|
||||
|
||||
c, err := v.prepareIo()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer v.wg.Done()
|
||||
|
||||
if v.readDeadline.timedout.isSet() {
|
||||
return 0, ErrTimeout
|
||||
}
|
||||
|
||||
var bytes uint32
|
||||
err = syscall.WSARecv(v.fd, &b, 1, &bytes, &f, &c.o, nil)
|
||||
n, err := v.asyncIo(c, &v.readDeadline, bytes, err)
|
||||
runtime.KeepAlive(buf)
|
||||
|
||||
// Handle EOF conditions.
|
||||
if err == nil && n == 0 && len(buf) != 0 {
|
||||
return 0, io.EOF
|
||||
} else if err == syscall.ERROR_BROKEN_PIPE {
|
||||
return 0, io.EOF
|
||||
} else {
|
||||
return n, err
|
||||
}
|
||||
}
|
||||
|
||||
// Write writes data over the connection
|
||||
// TODO(rn): Remove once 4.9.x support is deprecated
|
||||
func (v *hvsockConn) Write(buf []byte) (int, error) {
|
||||
written := 0
|
||||
toWrite := len(buf)
|
||||
for toWrite > 0 {
|
||||
thisBatch := min(toWrite, maxMsgSize)
|
||||
n, err := v.write(buf[written : written+thisBatch])
|
||||
if err != nil {
|
||||
return written, err
|
||||
}
|
||||
if n != thisBatch {
|
||||
return written, fmt.Errorf("short write %d != %d", n, thisBatch)
|
||||
}
|
||||
toWrite -= n
|
||||
written += n
|
||||
}
|
||||
|
||||
return written, nil
|
||||
}
|
||||
|
||||
func (v *hvsockConn) write(buf []byte) (int, error) {
|
||||
var b syscall.WSABuf
|
||||
var f uint32
|
||||
|
||||
if len(buf) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
f = 0
|
||||
b.Len = uint32(len(buf))
|
||||
b.Buf = &buf[0]
|
||||
|
||||
c, err := v.prepareIo()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer v.wg.Done()
|
||||
|
||||
if v.writeDeadline.timedout.isSet() {
|
||||
return 0, ErrTimeout
|
||||
}
|
||||
|
||||
var bytes uint32
|
||||
err = syscall.WSASend(v.fd, &b, 1, &bytes, f, &c.o, nil)
|
||||
n, err := v.asyncIo(c, &v.writeDeadline, bytes, err)
|
||||
runtime.KeepAlive(buf)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// SetReadDeadline implementation for Hyper-V sockets
|
||||
func (v *hvsockConn) SetReadDeadline(deadline time.Time) error {
|
||||
return v.readDeadline.set(deadline)
|
||||
}
|
||||
|
||||
// SetWriteDeadline implementation for Hyper-V sockets
|
||||
func (v *hvsockConn) SetWriteDeadline(deadline time.Time) error {
|
||||
return v.writeDeadline.set(deadline)
|
||||
}
|
||||
|
||||
// SetDeadline implementation for Hyper-V sockets
|
||||
func (v *hvsockConn) SetDeadline(deadline time.Time) error {
|
||||
if err := v.SetReadDeadline(deadline); err != nil {
|
||||
return err
|
||||
}
|
||||
return v.SetWriteDeadline(deadline)
|
||||
}
|
||||
|
||||
// Helper functions for conversion to sockaddr
|
||||
|
||||
// struck sockaddr equivalent
|
||||
type rawSockaddrHyperv struct {
|
||||
Family uint16
|
||||
Reserved uint16
|
||||
VMID GUID
|
||||
ServiceID GUID
|
||||
}
|
||||
|
||||
// Utility function to build a struct sockaddr for syscalls.
|
||||
func (a Addr) sockaddr(sa *rawSockaddrHyperv) (unsafe.Pointer, int32, error) {
|
||||
sa.Family = hvsockAF
|
||||
sa.Reserved = 0
|
||||
for i := 0; i < len(sa.VMID); i++ {
|
||||
sa.VMID[i] = a.VMID[i]
|
||||
}
|
||||
for i := 0; i < len(sa.ServiceID); i++ {
|
||||
sa.ServiceID[i] = a.ServiceID[i]
|
||||
}
|
||||
|
||||
return unsafe.Pointer(sa), int32(unsafe.Sizeof(*sa)), nil
|
||||
}
|
||||
|
||||
// Help for read/write timeouts
|
||||
type deadlineHandler struct {
|
||||
setLock sync.Mutex
|
||||
channel timeoutChan
|
||||
channelLock sync.RWMutex
|
||||
timer *time.Timer
|
||||
timedout atomicBool
|
||||
}
|
||||
|
||||
// The code below here is adjusted from:
|
||||
// https://github.com/Microsoft/go-winio/blob/master/file.go
|
||||
type atomicBool int32
|
||||
|
||||
func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 }
|
||||
func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) }
|
||||
func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) }
|
||||
func (b *atomicBool) swap(new bool) bool {
|
||||
var newInt int32
|
||||
if new {
|
||||
newInt = 1
|
||||
}
|
||||
return atomic.SwapInt32((*int32)(b), newInt) == 1
|
||||
}
|
||||
|
||||
type timeoutError struct{}
|
||||
|
||||
func (e *timeoutError) Error() string { return "i/o timeout" }
|
||||
func (e *timeoutError) Timeout() bool { return true }
|
||||
func (e *timeoutError) Temporary() bool { return true }
|
||||
|
||||
type timeoutChan chan struct{}
|
||||
|
||||
var ioInitOnce sync.Once
|
||||
var ioCompletionPort syscall.Handle
|
||||
|
||||
// ioResult contains the result of an asynchronous IO operation
|
||||
type ioResult struct {
|
||||
bytes uint32
|
||||
err error
|
||||
}
|
||||
|
||||
type ioOperation struct {
|
||||
o syscall.Overlapped
|
||||
ch chan ioResult
|
||||
}
|
||||
|
||||
func initIo() {
|
||||
h, err := createIoCompletionPort(syscall.InvalidHandle, 0, 0, 0xffffffff)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ioCompletionPort = h
|
||||
go ioCompletionProcessor(h)
|
||||
}
|
||||
|
||||
func (v *hvsockConn) close() {
|
||||
v.wgLock.Lock()
|
||||
if !v.closing.swap(true) {
|
||||
v.wgLock.Unlock()
|
||||
// cancel all IO and wait for it to complete
|
||||
cancelIoEx(v.fd, nil)
|
||||
v.wg.Wait()
|
||||
// at this point, no new IO can start
|
||||
syscall.Close(v.fd)
|
||||
v.fd = 0
|
||||
} else {
|
||||
v.wgLock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// prepareIo prepares for a new IO operation
|
||||
func (v *hvsockConn) prepareIo() (*ioOperation, error) {
|
||||
v.wgLock.RLock()
|
||||
if v.closing.isSet() {
|
||||
v.wgLock.RUnlock()
|
||||
return nil, fmt.Errorf("HvSocket has already been closed")
|
||||
}
|
||||
v.wg.Add(1)
|
||||
v.wgLock.RUnlock()
|
||||
c := &ioOperation{}
|
||||
c.ch = make(chan ioResult)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// ioCompletionProcessor processes completed async IOs forever
|
||||
func ioCompletionProcessor(h syscall.Handle) {
|
||||
// Set the timer resolution to 1. This fixes a performance regression in golang 1.6.
|
||||
timeBeginPeriod(1)
|
||||
for {
|
||||
var bytes uint32
|
||||
var key uintptr
|
||||
var op *ioOperation
|
||||
err := getQueuedCompletionStatus(h, &bytes, &key, &op, syscall.INFINITE)
|
||||
if op == nil {
|
||||
panic(err)
|
||||
}
|
||||
op.ch <- ioResult{bytes, err}
|
||||
}
|
||||
}
|
||||
|
||||
// asyncIo processes the return value from Recv or Send, blocking until
|
||||
// the operation has actually completed.
|
||||
func (v *hvsockConn) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) {
|
||||
if err != syscall.ERROR_IO_PENDING {
|
||||
return int(bytes), err
|
||||
}
|
||||
|
||||
if v.closing.isSet() {
|
||||
cancelIoEx(v.fd, &c.o)
|
||||
}
|
||||
|
||||
var timeout timeoutChan
|
||||
if d != nil {
|
||||
d.channelLock.Lock()
|
||||
timeout = d.channel
|
||||
d.channelLock.Unlock()
|
||||
}
|
||||
|
||||
var r ioResult
|
||||
select {
|
||||
case r = <-c.ch:
|
||||
err = r.err
|
||||
if err == syscall.ERROR_OPERATION_ABORTED {
|
||||
if v.closing.isSet() {
|
||||
err = fmt.Errorf("HvSocket has already been closed")
|
||||
}
|
||||
}
|
||||
case <-timeout:
|
||||
cancelIoEx(v.fd, &c.o)
|
||||
r = <-c.ch
|
||||
err = r.err
|
||||
if err == syscall.ERROR_OPERATION_ABORTED {
|
||||
err = ErrTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// runtime.KeepAlive is needed, as c is passed via native
|
||||
// code to ioCompletionProcessor, c must remain alive
|
||||
// until the channel read is complete.
|
||||
runtime.KeepAlive(c)
|
||||
return int(r.bytes), err
|
||||
}
|
||||
|
||||
func (d *deadlineHandler) set(deadline time.Time) error {
|
||||
d.setLock.Lock()
|
||||
defer d.setLock.Unlock()
|
||||
|
||||
if d.timer != nil {
|
||||
if !d.timer.Stop() {
|
||||
<-d.channel
|
||||
}
|
||||
d.timer = nil
|
||||
}
|
||||
d.timedout.setFalse()
|
||||
|
||||
select {
|
||||
case <-d.channel:
|
||||
d.channelLock.Lock()
|
||||
d.channel = make(chan struct{})
|
||||
d.channelLock.Unlock()
|
||||
default:
|
||||
}
|
||||
|
||||
if deadline.IsZero() {
|
||||
return nil
|
||||
}
|
||||
|
||||
timeoutIO := func() {
|
||||
d.timedout.setTrue()
|
||||
close(d.channel)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
duration := deadline.Sub(now)
|
||||
if deadline.After(now) {
|
||||
// Deadline is in the future, set a timer to wait
|
||||
d.timer = time.AfterFunc(duration, timeoutIO)
|
||||
} else {
|
||||
// Deadline is in the past. Cancel all pending IO now.
|
||||
timeoutIO()
|
||||
}
|
||||
return nil
|
||||
}
|
171
vendor/github.com/linuxkit/virtsock/pkg/hvsock/zsyscall_windows.go
generated
vendored
Normal file
171
vendor/github.com/linuxkit/virtsock/pkg/hvsock/zsyscall_windows.go
generated
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
package hvsock
|
||||
|
||||
/*
|
||||
Most of this code was derived from: https://github.com/Microsoft/go-winio
|
||||
which has the following license:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Microsoft
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
modws2_32 = syscall.NewLazyDLL("ws2_32.dll")
|
||||
modwinmm = syscall.NewLazyDLL("winmm.dll")
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
procConnect = modws2_32.NewProc("connect")
|
||||
procBind = modws2_32.NewProc("bind")
|
||||
procAccept = modws2_32.NewProc("accept")
|
||||
|
||||
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
|
||||
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
|
||||
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
|
||||
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
|
||||
proctimeBeginPeriod = modwinmm.NewProc("timeBeginPeriod")
|
||||
)
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
socketError = uintptr(^uint32(0))
|
||||
|
||||
cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
|
||||
cFILE_SKIP_SET_EVENT_ON_HANDLE = 2
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return nil
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
func sys_connect(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procConnect.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
|
||||
if r1 == socketError {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func sys_bind(s syscall.Handle, name unsafe.Pointer, namelen int32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procBind.Addr(), 3, uintptr(s), uintptr(name), uintptr(namelen))
|
||||
if r1 == socketError {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func sys_accept(s syscall.Handle, rsa *rawSockaddrHyperv, addrlen *int32) (handle syscall.Handle, err error) {
|
||||
r1, _, e1 := syscall.Syscall(procAccept.Addr(), 3, uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)))
|
||||
handle = syscall.Handle(r1)
|
||||
if r1 == socketError {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func cancelIoEx(file syscall.Handle, o *syscall.Overlapped) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procCancelIoEx.Addr(), 2, uintptr(file), uintptr(unsafe.Pointer(o)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(procCreateIoCompletionPort.Addr(), 4, uintptr(file), uintptr(port), uintptr(key), uintptr(threadCount), 0, 0)
|
||||
newport = syscall.Handle(r0)
|
||||
if newport == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procGetQueuedCompletionStatus.Addr(), 5, uintptr(port), uintptr(unsafe.Pointer(bytes)), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(o)), uintptr(timeout), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetFileCompletionNotificationModes.Addr(), 2, uintptr(h), uintptr(flags), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func timeBeginPeriod(period uint32) (n int32) {
|
||||
r0, _, _ := syscall.Syscall(proctimeBeginPeriod.Addr(), 1, uintptr(period), 0, 0)
|
||||
n = int32(r0)
|
||||
return
|
||||
}
|
80
vendor/github.com/mdlayher/socket/CHANGELOG.md
generated
vendored
Normal file
80
vendor/github.com/mdlayher/socket/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v0.4.1
|
||||
|
||||
- [Bug Fix] [commit](https://github.com/mdlayher/socket/commit/2a14ceef4da279de1f957c5761fffcc6c87bbd3b):
|
||||
ensure `socket.Conn` can be used with non-socket file descriptors by handling
|
||||
`ENOTSOCK` in the constructor.
|
||||
|
||||
## v0.4.0
|
||||
|
||||
**This is the first release of package socket that only supports Go 1.18+.
|
||||
Users on older versions of Go must use v0.3.0.**
|
||||
|
||||
- [Improvement]: drop support for older versions of Go so we can begin using
|
||||
modern versions of `x/sys` and other dependencies.
|
||||
|
||||
## v0.3.0
|
||||
|
||||
**This is the last release of package socket that supports Go 1.17 and below.**
|
||||
|
||||
- [New API/API change] [PR](https://github.com/mdlayher/socket/pull/8):
|
||||
numerous `socket.Conn` methods now support context cancelation. Future
|
||||
releases will continue adding support as needed.
|
||||
- New `ReadContext` and `WriteContext` methods.
|
||||
- `Connect`, `Recvfrom`, `Recvmsg`, `Sendmsg`, and `Sendto` methods now accept
|
||||
a context.
|
||||
- `Sendto` parameter order was also fixed to match the underlying syscall.
|
||||
|
||||
## v0.2.3
|
||||
|
||||
- [New API] [commit](https://github.com/mdlayher/socket/commit/a425d96e0f772c053164f8ce4c9c825380a98086):
|
||||
`socket.Conn` has new `Pidfd*` methods for wrapping the `pidfd_*(2)` family of
|
||||
system calls.
|
||||
|
||||
## v0.2.2
|
||||
|
||||
- [New API] [commit](https://github.com/mdlayher/socket/commit/a2429f1dfe8ec2586df5a09f50ead865276cd027):
|
||||
`socket.Conn` has new `IoctlKCM*` methods for wrapping `ioctl(2)` for `AF_KCM`
|
||||
operations.
|
||||
|
||||
## v0.2.1
|
||||
|
||||
- [New API] [commit](https://github.com/mdlayher/socket/commit/b18ddbe9caa0e34552b4409a3aa311cb460d2f99):
|
||||
`socket.Conn` has a new `SetsockoptPacketMreq` method for wrapping
|
||||
`setsockopt(2)` for `AF_PACKET` socket options.
|
||||
|
||||
## v0.2.0
|
||||
|
||||
- [New API] [commit](https://github.com/mdlayher/socket/commit/6e912a68523c45e5fd899239f4b46c402dd856da):
|
||||
`socket.FileConn` can be used to create a `socket.Conn` from an existing
|
||||
`os.File`, which may be provided by systemd socket activation or another
|
||||
external mechanism.
|
||||
- [API change] [commit](https://github.com/mdlayher/socket/commit/66d61f565188c23fe02b24099ddc856d538bf1a7):
|
||||
`socket.Conn.Connect` now returns the `unix.Sockaddr` value provided by
|
||||
`getpeername(2)`, since we have to invoke that system call anyway to verify
|
||||
that a connection to a remote peer was successfully established.
|
||||
- [Bug Fix] [commit](https://github.com/mdlayher/socket/commit/b60b2dbe0ac3caff2338446a150083bde8c5c19c):
|
||||
check the correct error from `unix.GetsockoptInt` in the `socket.Conn.Connect`
|
||||
method. Thanks @vcabbage!
|
||||
|
||||
## v0.1.2
|
||||
|
||||
- [Bug Fix]: `socket.Conn.Connect` now properly checks the `SO_ERROR` socket
|
||||
option value after calling `connect(2)` to verify whether or not a connection
|
||||
could successfully be established. This means that `Connect` should now report
|
||||
an error for an `AF_INET` TCP connection refused or `AF_VSOCK` connection
|
||||
reset by peer.
|
||||
- [New API]: add `socket.Conn.Getpeername` for use in `Connect`, but also for
|
||||
use by external callers.
|
||||
|
||||
## v0.1.1
|
||||
|
||||
- [New API]: `socket.Conn` now has `CloseRead`, `CloseWrite`, and `Shutdown`
|
||||
methods.
|
||||
- [Improvement]: internal rework to more robustly handle various errors.
|
||||
|
||||
## v0.1.0
|
||||
|
||||
- Initial unstable release. Most functionality has been developed and ported
|
||||
from package [`netlink`](https://github.com/mdlayher/netlink).
|
9
vendor/github.com/mdlayher/socket/LICENSE.md
generated
vendored
Normal file
9
vendor/github.com/mdlayher/socket/LICENSE.md
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# MIT License
|
||||
|
||||
Copyright (C) 2021 Matt Layher
|
||||
|
||||
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.
|
23
vendor/github.com/mdlayher/socket/README.md
generated
vendored
Normal file
23
vendor/github.com/mdlayher/socket/README.md
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# socket [](https://github.com/mdlayher/socket/actions) [](https://pkg.go.dev/github.com/mdlayher/socket) [](https://goreportcard.com/report/github.com/mdlayher/socket)
|
||||
|
||||
Package `socket` provides a low-level network connection type which integrates
|
||||
with Go's runtime network poller to provide asynchronous I/O and deadline
|
||||
support. MIT Licensed.
|
||||
|
||||
This package focuses on UNIX-like operating systems which make use of BSD
|
||||
sockets system call APIs. It is meant to be used as a foundation for the
|
||||
creation of operating system-specific socket packages, for socket families such
|
||||
as Linux's `AF_NETLINK`, `AF_PACKET`, or `AF_VSOCK`. This package should not be
|
||||
used directly in end user applications.
|
||||
|
||||
Any use of package socket should be guarded by build tags, as one would also
|
||||
use when importing the `syscall` or `golang.org/x/sys` packages.
|
||||
|
||||
## Stability
|
||||
|
||||
See the [CHANGELOG](./CHANGELOG.md) file for a description of changes between
|
||||
releases.
|
||||
|
||||
This package only supports the two most recent major versions of Go, mirroring
|
||||
Go's own release policy. Older versions of Go may lack critical features and bug
|
||||
fixes which are necessary for this package to function correctly.
|
23
vendor/github.com/mdlayher/socket/accept.go
generated
vendored
Normal file
23
vendor/github.com/mdlayher/socket/accept.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
//go:build !dragonfly && !freebsd && !illumos && !linux
|
||||
// +build !dragonfly,!freebsd,!illumos,!linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const sysAccept = "accept"
|
||||
|
||||
// accept wraps accept(2).
|
||||
func accept(fd, flags int) (int, unix.Sockaddr, error) {
|
||||
if flags != 0 {
|
||||
// These operating systems have no support for flags to accept(2).
|
||||
return 0, nil, fmt.Errorf("socket: Conn.Accept flags are ineffective on %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
return unix.Accept(fd)
|
||||
}
|
15
vendor/github.com/mdlayher/socket/accept4.go
generated
vendored
Normal file
15
vendor/github.com/mdlayher/socket/accept4.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
//go:build dragonfly || freebsd || illumos || linux
|
||||
// +build dragonfly freebsd illumos linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const sysAccept = "accept4"
|
||||
|
||||
// accept wraps accept4(2).
|
||||
func accept(fd, flags int) (int, unix.Sockaddr, error) {
|
||||
return unix.Accept4(fd, flags)
|
||||
}
|
880
vendor/github.com/mdlayher/socket/conn.go
generated
vendored
Normal file
880
vendor/github.com/mdlayher/socket/conn.go
generated
vendored
Normal file
@ -0,0 +1,880 @@
|
||||
package socket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Lock in an expected public interface for convenience.
|
||||
var _ interface {
|
||||
io.ReadWriteCloser
|
||||
syscall.Conn
|
||||
SetDeadline(t time.Time) error
|
||||
SetReadDeadline(t time.Time) error
|
||||
SetWriteDeadline(t time.Time) error
|
||||
} = &Conn{}
|
||||
|
||||
// A Conn is a low-level network connection which integrates with Go's runtime
|
||||
// network poller to provide asynchronous I/O and deadline support.
|
||||
//
|
||||
// Many of a Conn's blocking methods support net.Conn deadlines as well as
|
||||
// cancelation via context. Note that passing a context with a deadline set will
|
||||
// override any of the previous deadlines set by calls to the SetDeadline family
|
||||
// of methods.
|
||||
type Conn struct {
|
||||
// Indicates whether or not Conn.Close has been called. Must be accessed
|
||||
// atomically. Atomics definitions must come first in the Conn struct.
|
||||
closed uint32
|
||||
|
||||
// A unique name for the Conn which is also associated with derived file
|
||||
// descriptors such as those created by accept(2).
|
||||
name string
|
||||
|
||||
// facts contains information we have determined about Conn to trigger
|
||||
// alternate behavior in certain functions.
|
||||
facts facts
|
||||
|
||||
// Provides access to the underlying file registered with the runtime
|
||||
// network poller, and arbitrary raw I/O calls.
|
||||
fd *os.File
|
||||
rc syscall.RawConn
|
||||
}
|
||||
|
||||
// facts contains facts about a Conn.
|
||||
type facts struct {
|
||||
// isStream reports whether this is a streaming descriptor, as opposed to a
|
||||
// packet-based descriptor like a UDP socket.
|
||||
isStream bool
|
||||
|
||||
// zeroReadIsEOF reports Whether a zero byte read indicates EOF. This is
|
||||
// false for a message based socket connection.
|
||||
zeroReadIsEOF bool
|
||||
}
|
||||
|
||||
// A Config contains options for a Conn.
|
||||
type Config struct {
|
||||
// NetNS specifies the Linux network namespace the Conn will operate in.
|
||||
// This option is unsupported on other operating systems.
|
||||
//
|
||||
// If set (non-zero), Conn will enter the specified network namespace and an
|
||||
// error will occur in Socket if the operation fails.
|
||||
//
|
||||
// If not set (zero), a best-effort attempt will be made to enter the
|
||||
// network namespace of the calling thread: this means that any changes made
|
||||
// to the calling thread's network namespace will also be reflected in Conn.
|
||||
// If this operation fails (due to lack of permissions or because network
|
||||
// namespaces are disabled by kernel configuration), Socket will not return
|
||||
// an error, and the Conn will operate in the default network namespace of
|
||||
// the process. This enables non-privileged use of Conn in applications
|
||||
// which do not require elevated privileges.
|
||||
//
|
||||
// Entering a network namespace is a privileged operation (root or
|
||||
// CAP_SYS_ADMIN are required), and most applications should leave this set
|
||||
// to 0.
|
||||
NetNS int
|
||||
}
|
||||
|
||||
// High-level methods which provide convenience over raw system calls.
|
||||
|
||||
// Close closes the underlying file descriptor for the Conn, which also causes
|
||||
// all in-flight I/O operations to immediately unblock and return errors. Any
|
||||
// subsequent uses of Conn will result in EBADF.
|
||||
func (c *Conn) Close() error {
|
||||
// The caller has expressed an intent to close the socket, so immediately
|
||||
// increment s.closed to force further calls to result in EBADF before also
|
||||
// closing the file descriptor to unblock any outstanding operations.
|
||||
//
|
||||
// Because other operations simply check for s.closed != 0, we will permit
|
||||
// double Close, which would increment s.closed beyond 1.
|
||||
if atomic.AddUint32(&c.closed, 1) != 1 {
|
||||
// Multiple Close calls.
|
||||
return nil
|
||||
}
|
||||
|
||||
return os.NewSyscallError("close", c.fd.Close())
|
||||
}
|
||||
|
||||
// CloseRead shuts down the reading side of the Conn. Most callers should just
|
||||
// use Close.
|
||||
func (c *Conn) CloseRead() error { return c.Shutdown(unix.SHUT_RD) }
|
||||
|
||||
// CloseWrite shuts down the writing side of the Conn. Most callers should just
|
||||
// use Close.
|
||||
func (c *Conn) CloseWrite() error { return c.Shutdown(unix.SHUT_WR) }
|
||||
|
||||
// Read reads directly from the underlying file descriptor.
|
||||
func (c *Conn) Read(b []byte) (int, error) { return c.fd.Read(b) }
|
||||
|
||||
// ReadContext reads from the underlying file descriptor with added support for
|
||||
// context cancelation.
|
||||
func (c *Conn) ReadContext(ctx context.Context, b []byte) (int, error) {
|
||||
if c.facts.isStream && len(b) > maxRW {
|
||||
b = b[:maxRW]
|
||||
}
|
||||
|
||||
n, err := readT(c, ctx, "read", func(fd int) (int, error) {
|
||||
return unix.Read(fd, b)
|
||||
})
|
||||
if n == 0 && err == nil && c.facts.zeroReadIsEOF {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
return n, os.NewSyscallError("read", err)
|
||||
}
|
||||
|
||||
// Write writes directly to the underlying file descriptor.
|
||||
func (c *Conn) Write(b []byte) (int, error) { return c.fd.Write(b) }
|
||||
|
||||
// WriteContext writes to the underlying file descriptor with added support for
|
||||
// context cancelation.
|
||||
func (c *Conn) WriteContext(ctx context.Context, b []byte) (int, error) {
|
||||
var (
|
||||
n, nn int
|
||||
err error
|
||||
)
|
||||
|
||||
doErr := c.write(ctx, "write", func(fd int) error {
|
||||
max := len(b)
|
||||
if c.facts.isStream && max-nn > maxRW {
|
||||
max = nn + maxRW
|
||||
}
|
||||
|
||||
n, err = unix.Write(fd, b[nn:max])
|
||||
if n > 0 {
|
||||
nn += n
|
||||
}
|
||||
if nn == len(b) {
|
||||
return err
|
||||
}
|
||||
if n == 0 && err == nil {
|
||||
err = io.ErrUnexpectedEOF
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
if doErr != nil {
|
||||
return 0, doErr
|
||||
}
|
||||
|
||||
return nn, os.NewSyscallError("write", err)
|
||||
}
|
||||
|
||||
// SetDeadline sets both the read and write deadlines associated with the Conn.
|
||||
func (c *Conn) SetDeadline(t time.Time) error { return c.fd.SetDeadline(t) }
|
||||
|
||||
// SetReadDeadline sets the read deadline associated with the Conn.
|
||||
func (c *Conn) SetReadDeadline(t time.Time) error { return c.fd.SetReadDeadline(t) }
|
||||
|
||||
// SetWriteDeadline sets the write deadline associated with the Conn.
|
||||
func (c *Conn) SetWriteDeadline(t time.Time) error { return c.fd.SetWriteDeadline(t) }
|
||||
|
||||
// ReadBuffer gets the size of the operating system's receive buffer associated
|
||||
// with the Conn.
|
||||
func (c *Conn) ReadBuffer() (int, error) {
|
||||
return c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUF)
|
||||
}
|
||||
|
||||
// WriteBuffer gets the size of the operating system's transmit buffer
|
||||
// associated with the Conn.
|
||||
func (c *Conn) WriteBuffer() (int, error) {
|
||||
return c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUF)
|
||||
}
|
||||
|
||||
// SetReadBuffer sets the size of the operating system's receive buffer
|
||||
// associated with the Conn.
|
||||
//
|
||||
// When called with elevated privileges on Linux, the SO_RCVBUFFORCE option will
|
||||
// be used to override operating system limits. Otherwise SO_RCVBUF is used
|
||||
// (which obeys operating system limits).
|
||||
func (c *Conn) SetReadBuffer(bytes int) error { return c.setReadBuffer(bytes) }
|
||||
|
||||
// SetWriteBuffer sets the size of the operating system's transmit buffer
|
||||
// associated with the Conn.
|
||||
//
|
||||
// When called with elevated privileges on Linux, the SO_SNDBUFFORCE option will
|
||||
// be used to override operating system limits. Otherwise SO_SNDBUF is used
|
||||
// (which obeys operating system limits).
|
||||
func (c *Conn) SetWriteBuffer(bytes int) error { return c.setWriteBuffer(bytes) }
|
||||
|
||||
// SyscallConn returns a raw network connection. This implements the
|
||||
// syscall.Conn interface.
|
||||
//
|
||||
// SyscallConn is intended for advanced use cases, such as getting and setting
|
||||
// arbitrary socket options using the socket's file descriptor. If possible,
|
||||
// those operations should be performed using methods on Conn instead.
|
||||
//
|
||||
// Once invoked, it is the caller's responsibility to ensure that operations
|
||||
// performed using Conn and the syscall.RawConn do not conflict with each other.
|
||||
func (c *Conn) SyscallConn() (syscall.RawConn, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
return nil, os.NewSyscallError("syscallconn", unix.EBADF)
|
||||
}
|
||||
|
||||
// TODO(mdlayher): mutex or similar to enforce syscall.RawConn contract of
|
||||
// FD remaining valid for duration of calls?
|
||||
return c.rc, nil
|
||||
}
|
||||
|
||||
// Socket wraps the socket(2) system call to produce a Conn. domain, typ, and
|
||||
// proto are passed directly to socket(2), and name should be a unique name for
|
||||
// the socket type such as "netlink" or "vsock".
|
||||
//
|
||||
// The cfg parameter specifies optional configuration for the Conn. If nil, no
|
||||
// additional configuration will be applied.
|
||||
//
|
||||
// If the operating system supports SOCK_CLOEXEC and SOCK_NONBLOCK, they are
|
||||
// automatically applied to typ to mirror the standard library's socket flag
|
||||
// behaviors.
|
||||
func Socket(domain, typ, proto int, name string, cfg *Config) (*Conn, error) {
|
||||
if cfg == nil {
|
||||
cfg = &Config{}
|
||||
}
|
||||
|
||||
if cfg.NetNS == 0 {
|
||||
// Non-Linux or no network namespace.
|
||||
return socket(domain, typ, proto, name)
|
||||
}
|
||||
|
||||
// Linux only: create Conn in the specified network namespace.
|
||||
return withNetNS(cfg.NetNS, func() (*Conn, error) {
|
||||
return socket(domain, typ, proto, name)
|
||||
})
|
||||
}
|
||||
|
||||
// socket is the internal, cross-platform entry point for socket(2).
|
||||
func socket(domain, typ, proto int, name string) (*Conn, error) {
|
||||
var (
|
||||
fd int
|
||||
err error
|
||||
)
|
||||
|
||||
for {
|
||||
fd, err = unix.Socket(domain, typ|socketFlags, proto)
|
||||
switch {
|
||||
case err == nil:
|
||||
// Some OSes already set CLOEXEC with typ.
|
||||
if !flagCLOEXEC {
|
||||
unix.CloseOnExec(fd)
|
||||
}
|
||||
|
||||
// No error, prepare the Conn.
|
||||
return New(fd, name)
|
||||
case !ready(err):
|
||||
// System call interrupted or not ready, try again.
|
||||
continue
|
||||
case err == unix.EINVAL, err == unix.EPROTONOSUPPORT:
|
||||
// On Linux, SOCK_NONBLOCK and SOCK_CLOEXEC were introduced in
|
||||
// 2.6.27. On FreeBSD, both flags were introduced in FreeBSD 10.
|
||||
// EINVAL and EPROTONOSUPPORT check for earlier versions of these
|
||||
// OSes respectively.
|
||||
//
|
||||
// Mirror what the standard library does when creating file
|
||||
// descriptors: avoid racing a fork/exec with the creation of new
|
||||
// file descriptors, so that child processes do not inherit socket
|
||||
// file descriptors unexpectedly.
|
||||
//
|
||||
// For a more thorough explanation, see similar work in the Go tree:
|
||||
// func sysSocket in net/sock_cloexec.go, as well as the detailed
|
||||
// comment in syscall/exec_unix.go.
|
||||
syscall.ForkLock.RLock()
|
||||
fd, err = unix.Socket(domain, typ, proto)
|
||||
if err != nil {
|
||||
syscall.ForkLock.RUnlock()
|
||||
return nil, os.NewSyscallError("socket", err)
|
||||
}
|
||||
unix.CloseOnExec(fd)
|
||||
syscall.ForkLock.RUnlock()
|
||||
|
||||
return New(fd, name)
|
||||
default:
|
||||
// Unhandled error.
|
||||
return nil, os.NewSyscallError("socket", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FileConn returns a copy of the network connection corresponding to the open
|
||||
// file. It is the caller's responsibility to close the file when finished.
|
||||
// Closing the Conn does not affect the File, and closing the File does not
|
||||
// affect the Conn.
|
||||
func FileConn(f *os.File, name string) (*Conn, error) {
|
||||
// First we'll try to do fctnl(2) with F_DUPFD_CLOEXEC because we can dup
|
||||
// the file descriptor and set the flag in one syscall.
|
||||
fd, err := unix.FcntlInt(f.Fd(), unix.F_DUPFD_CLOEXEC, 0)
|
||||
switch err {
|
||||
case nil:
|
||||
// OK, ready to set up non-blocking I/O.
|
||||
return New(fd, name)
|
||||
case unix.EINVAL:
|
||||
// The kernel rejected our fcntl(2), fall back to separate dup(2) and
|
||||
// setting close on exec.
|
||||
//
|
||||
// Mirror what the standard library does when creating file descriptors:
|
||||
// avoid racing a fork/exec with the creation of new file descriptors,
|
||||
// so that child processes do not inherit socket file descriptors
|
||||
// unexpectedly.
|
||||
syscall.ForkLock.RLock()
|
||||
fd, err := unix.Dup(fd)
|
||||
if err != nil {
|
||||
syscall.ForkLock.RUnlock()
|
||||
return nil, os.NewSyscallError("dup", err)
|
||||
}
|
||||
unix.CloseOnExec(fd)
|
||||
syscall.ForkLock.RUnlock()
|
||||
|
||||
return New(fd, name)
|
||||
default:
|
||||
// Any other errors.
|
||||
return nil, os.NewSyscallError("fcntl", err)
|
||||
}
|
||||
}
|
||||
|
||||
// New wraps an existing file descriptor to create a Conn. name should be a
|
||||
// unique name for the socket type such as "netlink" or "vsock".
|
||||
//
|
||||
// Most callers should use Socket or FileConn to construct a Conn. New is
|
||||
// intended for integrating with specific system calls which provide a file
|
||||
// descriptor that supports asynchronous I/O. The file descriptor is immediately
|
||||
// set to nonblocking mode and registered with Go's runtime network poller for
|
||||
// future I/O operations.
|
||||
//
|
||||
// Unlike FileConn, New does not duplicate the existing file descriptor in any
|
||||
// way. The returned Conn takes ownership of the underlying file descriptor.
|
||||
func New(fd int, name string) (*Conn, error) {
|
||||
// All Conn I/O is nonblocking for integration with Go's runtime network
|
||||
// poller. Depending on the OS this might already be set but it can't hurt
|
||||
// to set it again.
|
||||
if err := unix.SetNonblock(fd, true); err != nil {
|
||||
return nil, os.NewSyscallError("setnonblock", err)
|
||||
}
|
||||
|
||||
// os.NewFile registers the non-blocking file descriptor with the runtime
|
||||
// poller, which is then used for most subsequent operations except those
|
||||
// that require raw I/O via SyscallConn.
|
||||
//
|
||||
// See also: https://golang.org/pkg/os/#NewFile
|
||||
f := os.NewFile(uintptr(fd), name)
|
||||
rc, err := f.SyscallConn()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &Conn{
|
||||
name: name,
|
||||
fd: f,
|
||||
rc: rc,
|
||||
}
|
||||
|
||||
// Probe the file descriptor for socket settings.
|
||||
sotype, err := c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_TYPE)
|
||||
switch {
|
||||
case err == nil:
|
||||
// File is a socket, check its properties.
|
||||
c.facts = facts{
|
||||
isStream: sotype == unix.SOCK_STREAM,
|
||||
zeroReadIsEOF: sotype != unix.SOCK_DGRAM && sotype != unix.SOCK_RAW,
|
||||
}
|
||||
case errors.Is(err, unix.ENOTSOCK):
|
||||
// File is not a socket, treat it as a regular file.
|
||||
c.facts = facts{
|
||||
isStream: true,
|
||||
zeroReadIsEOF: true,
|
||||
}
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Low-level methods which provide raw system call access.
|
||||
|
||||
// Accept wraps accept(2) or accept4(2) depending on the operating system, but
|
||||
// returns a Conn for the accepted connection rather than a raw file descriptor.
|
||||
//
|
||||
// If the operating system supports accept4(2) (which allows flags),
|
||||
// SOCK_CLOEXEC and SOCK_NONBLOCK are automatically applied to flags to mirror
|
||||
// the standard library's socket flag behaviors.
|
||||
//
|
||||
// If the operating system only supports accept(2) (which does not allow flags)
|
||||
// and flags is not zero, an error will be returned.
|
||||
//
|
||||
// Accept obeys context cancelation and uses the deadline set on the context to
|
||||
// cancel accepting the next connection. If a deadline is set on ctx, this
|
||||
// deadline will override any previous deadlines set using SetDeadline or
|
||||
// SetReadDeadline. Upon return, the read deadline is cleared.
|
||||
func (c *Conn) Accept(ctx context.Context, flags int) (*Conn, unix.Sockaddr, error) {
|
||||
type ret struct {
|
||||
nfd int
|
||||
sa unix.Sockaddr
|
||||
}
|
||||
|
||||
r, err := readT(c, ctx, sysAccept, func(fd int) (ret, error) {
|
||||
// Either accept(2) or accept4(2) depending on the OS.
|
||||
nfd, sa, err := accept(fd, flags|socketFlags)
|
||||
return ret{nfd, sa}, err
|
||||
})
|
||||
if err != nil {
|
||||
// internal/poll, context error, or user function error.
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Successfully accepted a connection, wrap it in a Conn for use by the
|
||||
// caller.
|
||||
ac, err := New(r.nfd, c.name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return ac, r.sa, nil
|
||||
}
|
||||
|
||||
// Bind wraps bind(2).
|
||||
func (c *Conn) Bind(sa unix.Sockaddr) error {
|
||||
return c.control(context.Background(), "bind", func(fd int) error {
|
||||
return unix.Bind(fd, sa)
|
||||
})
|
||||
}
|
||||
|
||||
// Connect wraps connect(2). In order to verify that the underlying socket is
|
||||
// connected to a remote peer, Connect calls getpeername(2) and returns the
|
||||
// unix.Sockaddr from that call.
|
||||
//
|
||||
// Connect obeys context cancelation and uses the deadline set on the context to
|
||||
// cancel connecting to a remote peer. If a deadline is set on ctx, this
|
||||
// deadline will override any previous deadlines set using SetDeadline or
|
||||
// SetWriteDeadline. Upon return, the write deadline is cleared.
|
||||
func (c *Conn) Connect(ctx context.Context, sa unix.Sockaddr) (unix.Sockaddr, error) {
|
||||
const op = "connect"
|
||||
|
||||
// TODO(mdlayher): it would seem that trying to connect to unbound vsock
|
||||
// listeners by calling Connect multiple times results in ECONNRESET for the
|
||||
// first and nil error for subsequent calls. Do we need to memoize the
|
||||
// error? Check what the stdlib behavior is.
|
||||
|
||||
var (
|
||||
// Track progress between invocations of the write closure. We don't
|
||||
// have an explicit WaitWrite call like internal/poll does, so we have
|
||||
// to wait until the runtime calls the closure again to indicate we can
|
||||
// write.
|
||||
progress uint32
|
||||
|
||||
// Capture closure sockaddr and error.
|
||||
rsa unix.Sockaddr
|
||||
err error
|
||||
)
|
||||
|
||||
doErr := c.write(ctx, op, func(fd int) error {
|
||||
if atomic.AddUint32(&progress, 1) == 1 {
|
||||
// First call: initiate connect.
|
||||
return unix.Connect(fd, sa)
|
||||
}
|
||||
|
||||
// Subsequent calls: the runtime network poller indicates fd is
|
||||
// writable. Check for errno.
|
||||
errno, gerr := c.GetsockoptInt(unix.SOL_SOCKET, unix.SO_ERROR)
|
||||
if gerr != nil {
|
||||
return gerr
|
||||
}
|
||||
if errno != 0 {
|
||||
// Connection is still not ready or failed. If errno indicates
|
||||
// the socket is not ready, we will wait for the next write
|
||||
// event. Otherwise we propagate this errno back to the as a
|
||||
// permanent error.
|
||||
uerr := unix.Errno(errno)
|
||||
err = uerr
|
||||
return uerr
|
||||
}
|
||||
|
||||
// According to internal/poll, it's possible for the runtime network
|
||||
// poller to spuriously wake us and return errno 0 for SO_ERROR.
|
||||
// Make sure we are actually connected to a peer.
|
||||
peer, err := c.Getpeername()
|
||||
if err != nil {
|
||||
// internal/poll unconditionally goes back to WaitWrite.
|
||||
// Synthesize an error that will do the same for us.
|
||||
return unix.EAGAIN
|
||||
}
|
||||
|
||||
// Connection complete.
|
||||
rsa = peer
|
||||
return nil
|
||||
})
|
||||
if doErr != nil {
|
||||
// internal/poll or context error.
|
||||
return nil, doErr
|
||||
}
|
||||
|
||||
if err == unix.EISCONN {
|
||||
// TODO(mdlayher): is this block obsolete with the addition of the
|
||||
// getsockopt SO_ERROR check above?
|
||||
//
|
||||
// EISCONN is reported if the socket is already established and should
|
||||
// not be treated as an error.
|
||||
// - Darwin reports this for at least TCP sockets
|
||||
// - Linux reports this for at least AF_VSOCK sockets
|
||||
return rsa, nil
|
||||
}
|
||||
|
||||
return rsa, os.NewSyscallError(op, err)
|
||||
}
|
||||
|
||||
// Getsockname wraps getsockname(2).
|
||||
func (c *Conn) Getsockname() (unix.Sockaddr, error) {
|
||||
return controlT(c, context.Background(), "getsockname", unix.Getsockname)
|
||||
}
|
||||
|
||||
// Getpeername wraps getpeername(2).
|
||||
func (c *Conn) Getpeername() (unix.Sockaddr, error) {
|
||||
return controlT(c, context.Background(), "getpeername", unix.Getpeername)
|
||||
}
|
||||
|
||||
// GetsockoptInt wraps getsockopt(2) for integer values.
|
||||
func (c *Conn) GetsockoptInt(level, opt int) (int, error) {
|
||||
return controlT(c, context.Background(), "getsockopt", func(fd int) (int, error) {
|
||||
return unix.GetsockoptInt(fd, level, opt)
|
||||
})
|
||||
}
|
||||
|
||||
// Listen wraps listen(2).
|
||||
func (c *Conn) Listen(n int) error {
|
||||
return c.control(context.Background(), "listen", func(fd int) error {
|
||||
return unix.Listen(fd, n)
|
||||
})
|
||||
}
|
||||
|
||||
// Recvmsg wraps recvmsg(2).
|
||||
func (c *Conn) Recvmsg(ctx context.Context, p, oob []byte, flags int) (int, int, int, unix.Sockaddr, error) {
|
||||
type ret struct {
|
||||
n, oobn, recvflags int
|
||||
from unix.Sockaddr
|
||||
}
|
||||
|
||||
r, err := readT(c, ctx, "recvmsg", func(fd int) (ret, error) {
|
||||
n, oobn, recvflags, from, err := unix.Recvmsg(fd, p, oob, flags)
|
||||
return ret{n, oobn, recvflags, from}, err
|
||||
})
|
||||
if r.n == 0 && err == nil && c.facts.zeroReadIsEOF {
|
||||
return 0, 0, 0, nil, io.EOF
|
||||
}
|
||||
|
||||
return r.n, r.oobn, r.recvflags, r.from, err
|
||||
}
|
||||
|
||||
// Recvfrom wraps recvfrom(2).
|
||||
func (c *Conn) Recvfrom(ctx context.Context, p []byte, flags int) (int, unix.Sockaddr, error) {
|
||||
type ret struct {
|
||||
n int
|
||||
addr unix.Sockaddr
|
||||
}
|
||||
|
||||
out, err := readT(c, ctx, "recvfrom", func(fd int) (ret, error) {
|
||||
n, addr, err := unix.Recvfrom(fd, p, flags)
|
||||
return ret{n, addr}, err
|
||||
})
|
||||
if out.n == 0 && err == nil && c.facts.zeroReadIsEOF {
|
||||
return 0, nil, io.EOF
|
||||
}
|
||||
|
||||
return out.n, out.addr, err
|
||||
}
|
||||
|
||||
// Sendmsg wraps sendmsg(2).
|
||||
func (c *Conn) Sendmsg(ctx context.Context, p, oob []byte, to unix.Sockaddr, flags int) (int, error) {
|
||||
return writeT(c, ctx, "sendmsg", func(fd int) (int, error) {
|
||||
return unix.SendmsgN(fd, p, oob, to, flags)
|
||||
})
|
||||
}
|
||||
|
||||
// Sendto wraps sendto(2).
|
||||
func (c *Conn) Sendto(ctx context.Context, p []byte, flags int, to unix.Sockaddr) error {
|
||||
return c.write(ctx, "sendto", func(fd int) error {
|
||||
return unix.Sendto(fd, p, flags, to)
|
||||
})
|
||||
}
|
||||
|
||||
// SetsockoptInt wraps setsockopt(2) for integer values.
|
||||
func (c *Conn) SetsockoptInt(level, opt, value int) error {
|
||||
return c.control(context.Background(), "setsockopt", func(fd int) error {
|
||||
return unix.SetsockoptInt(fd, level, opt, value)
|
||||
})
|
||||
}
|
||||
|
||||
// Shutdown wraps shutdown(2).
|
||||
func (c *Conn) Shutdown(how int) error {
|
||||
return c.control(context.Background(), "shutdown", func(fd int) error {
|
||||
return unix.Shutdown(fd, how)
|
||||
})
|
||||
}
|
||||
|
||||
// Conn low-level read/write/control functions. These functions mirror the
|
||||
// syscall.RawConn APIs but the input closures return errors rather than
|
||||
// booleans.
|
||||
|
||||
// read wraps readT to execute a function and capture its error result. This is
|
||||
// a convenience wrapper for functions which don't return any extra values.
|
||||
func (c *Conn) read(ctx context.Context, op string, f func(fd int) error) error {
|
||||
_, err := readT(c, ctx, op, func(fd int) (struct{}, error) {
|
||||
return struct{}{}, f(fd)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// write executes f, a write function, against the associated file descriptor.
|
||||
// op is used to create an *os.SyscallError if the file descriptor is closed.
|
||||
func (c *Conn) write(ctx context.Context, op string, f func(fd int) error) error {
|
||||
_, err := writeT(c, ctx, op, func(fd int) (struct{}, error) {
|
||||
return struct{}{}, f(fd)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// readT executes c.rc.Read for op using the input function, returning a newly
|
||||
// allocated result T.
|
||||
func readT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) {
|
||||
return rwT(c, rwContext[T]{
|
||||
Context: ctx,
|
||||
Type: read,
|
||||
Op: op,
|
||||
Do: f,
|
||||
})
|
||||
}
|
||||
|
||||
// writeT executes c.rc.Write for op using the input function, returning a newly
|
||||
// allocated result T.
|
||||
func writeT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) {
|
||||
return rwT(c, rwContext[T]{
|
||||
Context: ctx,
|
||||
Type: write,
|
||||
Op: op,
|
||||
Do: f,
|
||||
})
|
||||
}
|
||||
|
||||
// readWrite indicates if an operation intends to read or write.
|
||||
type readWrite bool
|
||||
|
||||
// Possible readWrite values.
|
||||
const (
|
||||
read readWrite = false
|
||||
write readWrite = true
|
||||
)
|
||||
|
||||
// An rwContext provides arguments to rwT.
|
||||
type rwContext[T any] struct {
|
||||
// The caller's context passed for cancelation.
|
||||
Context context.Context
|
||||
|
||||
// The type of an operation: read or write.
|
||||
Type readWrite
|
||||
|
||||
// The name of the operation used in errors.
|
||||
Op string
|
||||
|
||||
// The actual function to perform.
|
||||
Do func(fd int) (T, error)
|
||||
}
|
||||
|
||||
// rwT executes c.rc.Read or c.rc.Write (depending on the value of rw.Type) for
|
||||
// rw.Op using the input function, returning a newly allocated result T.
|
||||
//
|
||||
// It obeys context cancelation and the rw.Context must not be nil.
|
||||
func rwT[T any](c *Conn, rw rwContext[T]) (T, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
// If the file descriptor is already closed, do nothing.
|
||||
return *new(T), os.NewSyscallError(rw.Op, unix.EBADF)
|
||||
}
|
||||
|
||||
if err := rw.Context.Err(); err != nil {
|
||||
// Early exit due to context cancel.
|
||||
return *new(T), os.NewSyscallError(rw.Op, err)
|
||||
}
|
||||
|
||||
var (
|
||||
// The read or write function used to access the runtime network poller.
|
||||
poll func(func(uintptr) bool) error
|
||||
|
||||
// The read or write function used to set the matching deadline.
|
||||
deadline func(time.Time) error
|
||||
)
|
||||
|
||||
if rw.Type == write {
|
||||
poll = c.rc.Write
|
||||
deadline = c.SetWriteDeadline
|
||||
} else {
|
||||
poll = c.rc.Read
|
||||
deadline = c.SetReadDeadline
|
||||
}
|
||||
|
||||
var (
|
||||
// Whether or not the context carried a deadline we are actively using
|
||||
// for cancelation.
|
||||
setDeadline bool
|
||||
|
||||
// Signals for the cancelation watcher goroutine.
|
||||
wg sync.WaitGroup
|
||||
doneC = make(chan struct{})
|
||||
|
||||
// Atomic: reports whether we have to disarm the deadline.
|
||||
//
|
||||
// TODO(mdlayher): switch back to atomic.Bool when we drop support for
|
||||
// Go 1.18.
|
||||
needDisarm int64
|
||||
)
|
||||
|
||||
// On cancel, clean up the watcher.
|
||||
defer func() {
|
||||
close(doneC)
|
||||
wg.Wait()
|
||||
}()
|
||||
|
||||
if d, ok := rw.Context.Deadline(); ok {
|
||||
// The context has an explicit deadline. We will use it for cancelation
|
||||
// but disarm it after poll for the next call.
|
||||
if err := deadline(d); err != nil {
|
||||
return *new(T), err
|
||||
}
|
||||
setDeadline = true
|
||||
atomic.AddInt64(&needDisarm, 1)
|
||||
} else {
|
||||
// The context does not have an explicit deadline. We have to watch for
|
||||
// cancelation so we can propagate that signal to immediately unblock
|
||||
// the runtime network poller.
|
||||
//
|
||||
// TODO(mdlayher): is it possible to detect a background context vs a
|
||||
// context with possible future cancel?
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
select {
|
||||
case <-rw.Context.Done():
|
||||
// Cancel the operation. Make the caller disarm after poll
|
||||
// returns.
|
||||
atomic.AddInt64(&needDisarm, 1)
|
||||
_ = deadline(time.Unix(0, 1))
|
||||
case <-doneC:
|
||||
// Nothing to do.
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var (
|
||||
t T
|
||||
err error
|
||||
)
|
||||
|
||||
pollErr := poll(func(fd uintptr) bool {
|
||||
t, err = rw.Do(int(fd))
|
||||
return ready(err)
|
||||
})
|
||||
|
||||
if atomic.LoadInt64(&needDisarm) > 0 {
|
||||
_ = deadline(time.Time{})
|
||||
}
|
||||
|
||||
if pollErr != nil {
|
||||
if rw.Context.Err() != nil || (setDeadline && errors.Is(pollErr, os.ErrDeadlineExceeded)) {
|
||||
// The caller canceled the operation or we set a deadline internally
|
||||
// and it was reached.
|
||||
//
|
||||
// Unpack a plain context error. We wait for the context to be done
|
||||
// to synchronize state externally. Otherwise we have noticed I/O
|
||||
// timeout wakeups when we set a deadline but the context was not
|
||||
// yet marked done.
|
||||
<-rw.Context.Done()
|
||||
return *new(T), os.NewSyscallError(rw.Op, rw.Context.Err())
|
||||
}
|
||||
|
||||
// Error from syscall.RawConn methods. Conventionally the standard
|
||||
// library does not wrap internal/poll errors in os.NewSyscallError.
|
||||
return *new(T), pollErr
|
||||
}
|
||||
|
||||
// Result from user function.
|
||||
return t, os.NewSyscallError(rw.Op, err)
|
||||
}
|
||||
|
||||
// control executes Conn.control for op using the input function.
|
||||
func (c *Conn) control(ctx context.Context, op string, f func(fd int) error) error {
|
||||
_, err := controlT(c, ctx, op, func(fd int) (struct{}, error) {
|
||||
return struct{}{}, f(fd)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// controlT executes c.rc.Control for op using the input function, returning a
|
||||
// newly allocated result T.
|
||||
func controlT[T any](c *Conn, ctx context.Context, op string, f func(fd int) (T, error)) (T, error) {
|
||||
if atomic.LoadUint32(&c.closed) != 0 {
|
||||
// If the file descriptor is already closed, do nothing.
|
||||
return *new(T), os.NewSyscallError(op, unix.EBADF)
|
||||
}
|
||||
|
||||
var (
|
||||
t T
|
||||
err error
|
||||
)
|
||||
|
||||
doErr := c.rc.Control(func(fd uintptr) {
|
||||
// Repeatedly attempt the syscall(s) invoked by f until completion is
|
||||
// indicated by the return value of ready or the context is canceled.
|
||||
//
|
||||
// The last values for t and err are captured outside of the closure for
|
||||
// use when the loop breaks.
|
||||
for {
|
||||
if err = ctx.Err(); err != nil {
|
||||
// Early exit due to context cancel.
|
||||
return
|
||||
}
|
||||
|
||||
t, err = f(int(fd))
|
||||
if ready(err) {
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
if doErr != nil {
|
||||
// Error from syscall.RawConn methods. Conventionally the standard
|
||||
// library does not wrap internal/poll errors in os.NewSyscallError.
|
||||
return *new(T), doErr
|
||||
}
|
||||
|
||||
// Result from user function.
|
||||
return t, os.NewSyscallError(op, err)
|
||||
}
|
||||
|
||||
// ready indicates readiness based on the value of err.
|
||||
func ready(err error) bool {
|
||||
switch err {
|
||||
case unix.EAGAIN, unix.EINPROGRESS, unix.EINTR:
|
||||
// When a socket is in non-blocking mode, we might see a variety of errors:
|
||||
// - EAGAIN: most common case for a socket read not being ready
|
||||
// - EINPROGRESS: reported by some sockets when first calling connect
|
||||
// - EINTR: system call interrupted, more frequently occurs in Go 1.14+
|
||||
// because goroutines can be asynchronously preempted
|
||||
//
|
||||
// Return false to let the poller wait for readiness. See the source code
|
||||
// for internal/poll.FD.RawRead for more details.
|
||||
return false
|
||||
default:
|
||||
// Ready regardless of whether there was an error or no error.
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Darwin and FreeBSD can't read or write 2GB+ files at a time,
|
||||
// even on 64-bit systems.
|
||||
// The same is true of socket implementations on many systems.
|
||||
// See golang.org/issue/7812 and golang.org/issue/16266.
|
||||
// Use 1GB instead of, say, 2GB-1, to keep subsequent reads aligned.
|
||||
const maxRW = 1 << 30
|
118
vendor/github.com/mdlayher/socket/conn_linux.go
generated
vendored
Normal file
118
vendor/github.com/mdlayher/socket/conn_linux.go
generated
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// IoctlKCMClone wraps ioctl(2) for unix.KCMClone values, but returns a Conn
|
||||
// rather than a raw file descriptor.
|
||||
func (c *Conn) IoctlKCMClone() (*Conn, error) {
|
||||
info, err := controlT(c, context.Background(), "ioctl", unix.IoctlKCMClone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Successful clone, wrap in a Conn for use by the caller.
|
||||
return New(int(info.Fd), c.name)
|
||||
}
|
||||
|
||||
// IoctlKCMAttach wraps ioctl(2) for unix.KCMAttach values.
|
||||
func (c *Conn) IoctlKCMAttach(info unix.KCMAttach) error {
|
||||
return c.control(context.Background(), "ioctl", func(fd int) error {
|
||||
return unix.IoctlKCMAttach(fd, info)
|
||||
})
|
||||
}
|
||||
|
||||
// IoctlKCMUnattach wraps ioctl(2) for unix.KCMUnattach values.
|
||||
func (c *Conn) IoctlKCMUnattach(info unix.KCMUnattach) error {
|
||||
return c.control(context.Background(), "ioctl", func(fd int) error {
|
||||
return unix.IoctlKCMUnattach(fd, info)
|
||||
})
|
||||
}
|
||||
|
||||
// PidfdGetfd wraps pidfd_getfd(2) for a Conn which wraps a pidfd, but returns a
|
||||
// Conn rather than a raw file descriptor.
|
||||
func (c *Conn) PidfdGetfd(targetFD, flags int) (*Conn, error) {
|
||||
outFD, err := controlT(c, context.Background(), "pidfd_getfd", func(fd int) (int, error) {
|
||||
return unix.PidfdGetfd(fd, targetFD, flags)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Successful getfd, wrap in a Conn for use by the caller.
|
||||
return New(outFD, c.name)
|
||||
}
|
||||
|
||||
// PidfdSendSignal wraps pidfd_send_signal(2) for a Conn which wraps a Linux
|
||||
// pidfd.
|
||||
func (c *Conn) PidfdSendSignal(sig unix.Signal, info *unix.Siginfo, flags int) error {
|
||||
return c.control(context.Background(), "pidfd_send_signal", func(fd int) error {
|
||||
return unix.PidfdSendSignal(fd, sig, info, flags)
|
||||
})
|
||||
}
|
||||
|
||||
// SetBPF attaches an assembled BPF program to a Conn.
|
||||
func (c *Conn) SetBPF(filter []bpf.RawInstruction) error {
|
||||
// We can't point to the first instruction in the array if no instructions
|
||||
// are present.
|
||||
if len(filter) == 0 {
|
||||
return os.NewSyscallError("setsockopt", unix.EINVAL)
|
||||
}
|
||||
|
||||
prog := unix.SockFprog{
|
||||
Len: uint16(len(filter)),
|
||||
Filter: (*unix.SockFilter)(unsafe.Pointer(&filter[0])),
|
||||
}
|
||||
|
||||
return c.SetsockoptSockFprog(unix.SOL_SOCKET, unix.SO_ATTACH_FILTER, &prog)
|
||||
}
|
||||
|
||||
// RemoveBPF removes a BPF filter from a Conn.
|
||||
func (c *Conn) RemoveBPF() error {
|
||||
// 0 argument is ignored.
|
||||
return c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_DETACH_FILTER, 0)
|
||||
}
|
||||
|
||||
// SetsockoptPacketMreq wraps setsockopt(2) for unix.PacketMreq values.
|
||||
func (c *Conn) SetsockoptPacketMreq(level, opt int, mreq *unix.PacketMreq) error {
|
||||
return c.control(context.Background(), "setsockopt", func(fd int) error {
|
||||
return unix.SetsockoptPacketMreq(fd, level, opt, mreq)
|
||||
})
|
||||
}
|
||||
|
||||
// SetsockoptSockFprog wraps setsockopt(2) for unix.SockFprog values.
|
||||
func (c *Conn) SetsockoptSockFprog(level, opt int, fprog *unix.SockFprog) error {
|
||||
return c.control(context.Background(), "setsockopt", func(fd int) error {
|
||||
return unix.SetsockoptSockFprog(fd, level, opt, fprog)
|
||||
})
|
||||
}
|
||||
|
||||
// GetsockoptTpacketStats wraps getsockopt(2) for unix.TpacketStats values.
|
||||
func (c *Conn) GetsockoptTpacketStats(level, name int) (*unix.TpacketStats, error) {
|
||||
return controlT(c, context.Background(), "getsockopt", func(fd int) (*unix.TpacketStats, error) {
|
||||
return unix.GetsockoptTpacketStats(fd, level, name)
|
||||
})
|
||||
}
|
||||
|
||||
// GetsockoptTpacketStatsV3 wraps getsockopt(2) for unix.TpacketStatsV3 values.
|
||||
func (c *Conn) GetsockoptTpacketStatsV3(level, name int) (*unix.TpacketStatsV3, error) {
|
||||
return controlT(c, context.Background(), "getsockopt", func(fd int) (*unix.TpacketStatsV3, error) {
|
||||
return unix.GetsockoptTpacketStatsV3(fd, level, name)
|
||||
})
|
||||
}
|
||||
|
||||
// Waitid wraps waitid(2).
|
||||
func (c *Conn) Waitid(idType int, info *unix.Siginfo, options int, rusage *unix.Rusage) error {
|
||||
return c.read(context.Background(), "waitid", func(fd int) error {
|
||||
return unix.Waitid(idType, fd, info, options, rusage)
|
||||
})
|
||||
}
|
13
vendor/github.com/mdlayher/socket/doc.go
generated
vendored
Normal file
13
vendor/github.com/mdlayher/socket/doc.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
// Package socket provides a low-level network connection type which integrates
|
||||
// with Go's runtime network poller to provide asynchronous I/O and deadline
|
||||
// support.
|
||||
//
|
||||
// This package focuses on UNIX-like operating systems which make use of BSD
|
||||
// sockets system call APIs. It is meant to be used as a foundation for the
|
||||
// creation of operating system-specific socket packages, for socket families
|
||||
// such as Linux's AF_NETLINK, AF_PACKET, or AF_VSOCK. This package should not
|
||||
// be used directly in end user applications.
|
||||
//
|
||||
// Any use of package socket should be guarded by build tags, as one would also
|
||||
// use when importing the syscall or golang.org/x/sys packages.
|
||||
package socket
|
150
vendor/github.com/mdlayher/socket/netns_linux.go
generated
vendored
Normal file
150
vendor/github.com/mdlayher/socket/netns_linux.go
generated
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// errNetNSDisabled is returned when network namespaces are unavailable on
|
||||
// a given system.
|
||||
var errNetNSDisabled = errors.New("socket: Linux network namespaces are not enabled on this system")
|
||||
|
||||
// withNetNS invokes fn within the context of the network namespace specified by
|
||||
// fd, while also managing the logic required to safely do so by manipulating
|
||||
// thread-local state.
|
||||
func withNetNS(fd int, fn func() (*Conn, error)) (*Conn, error) {
|
||||
var (
|
||||
eg errgroup.Group
|
||||
conn *Conn
|
||||
)
|
||||
|
||||
eg.Go(func() error {
|
||||
// Retrieve and store the calling OS thread's network namespace so the
|
||||
// thread can be reassigned to it after creating a socket in another network
|
||||
// namespace.
|
||||
runtime.LockOSThread()
|
||||
|
||||
ns, err := threadNetNS()
|
||||
if err != nil {
|
||||
// No thread-local manipulation, unlock.
|
||||
runtime.UnlockOSThread()
|
||||
return err
|
||||
}
|
||||
defer ns.Close()
|
||||
|
||||
// Beyond this point, the thread's network namespace is poisoned. Do not
|
||||
// unlock the OS thread until all network namespace manipulation completes
|
||||
// to avoid returning to the caller with altered thread-local state.
|
||||
|
||||
// Assign the current OS thread the goroutine is locked to to the given
|
||||
// network namespace.
|
||||
if err := ns.Set(fd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Attempt Conn creation and unconditionally restore the original namespace.
|
||||
c, err := fn()
|
||||
if nerr := ns.Restore(); nerr != nil {
|
||||
// Failed to restore original namespace. Return an error and allow the
|
||||
// runtime to terminate the thread.
|
||||
if err == nil {
|
||||
_ = c.Close()
|
||||
}
|
||||
|
||||
return nerr
|
||||
}
|
||||
|
||||
// No more thread-local state manipulation; return the new Conn.
|
||||
runtime.UnlockOSThread()
|
||||
conn = c
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// A netNS is a handle that can manipulate network namespaces.
|
||||
//
|
||||
// Operations performed on a netNS must use runtime.LockOSThread before
|
||||
// manipulating any network namespaces.
|
||||
type netNS struct {
|
||||
// The handle to a network namespace.
|
||||
f *os.File
|
||||
|
||||
// Indicates if network namespaces are disabled on this system, and thus
|
||||
// operations should become a no-op or return errors.
|
||||
disabled bool
|
||||
}
|
||||
|
||||
// threadNetNS constructs a netNS using the network namespace of the calling
|
||||
// thread. If the namespace is not the default namespace, runtime.LockOSThread
|
||||
// should be invoked first.
|
||||
func threadNetNS() (*netNS, error) {
|
||||
return fileNetNS(fmt.Sprintf("/proc/self/task/%d/ns/net", unix.Gettid()))
|
||||
}
|
||||
|
||||
// fileNetNS opens file and creates a netNS. fileNetNS should only be called
|
||||
// directly in tests.
|
||||
func fileNetNS(file string) (*netNS, error) {
|
||||
f, err := os.Open(file)
|
||||
switch {
|
||||
case err == nil:
|
||||
return &netNS{f: f}, nil
|
||||
case os.IsNotExist(err):
|
||||
// Network namespaces are not enabled on this system. Use this signal
|
||||
// to return errors elsewhere if the caller explicitly asks for a
|
||||
// network namespace to be set.
|
||||
return &netNS{disabled: true}, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Close releases the handle to a network namespace.
|
||||
func (n *netNS) Close() error {
|
||||
return n.do(func() error { return n.f.Close() })
|
||||
}
|
||||
|
||||
// FD returns a file descriptor which represents the network namespace.
|
||||
func (n *netNS) FD() int {
|
||||
if n.disabled {
|
||||
// No reasonable file descriptor value in this case, so specify a
|
||||
// non-existent one.
|
||||
return -1
|
||||
}
|
||||
|
||||
return int(n.f.Fd())
|
||||
}
|
||||
|
||||
// Restore restores the original network namespace for the calling thread.
|
||||
func (n *netNS) Restore() error {
|
||||
return n.do(func() error { return n.Set(n.FD()) })
|
||||
}
|
||||
|
||||
// Set sets a new network namespace for the current thread using fd.
|
||||
func (n *netNS) Set(fd int) error {
|
||||
return n.do(func() error {
|
||||
return os.NewSyscallError("setns", unix.Setns(fd, unix.CLONE_NEWNET))
|
||||
})
|
||||
}
|
||||
|
||||
// do runs fn if network namespaces are enabled on this system.
|
||||
func (n *netNS) do(fn func() error) error {
|
||||
if n.disabled {
|
||||
return errNetNSDisabled
|
||||
}
|
||||
|
||||
return fn()
|
||||
}
|
14
vendor/github.com/mdlayher/socket/netns_others.go
generated
vendored
Normal file
14
vendor/github.com/mdlayher/socket/netns_others.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package socket
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// withNetNS returns an error on non-Linux systems.
|
||||
func withNetNS(_ int, _ func() (*Conn, error)) (*Conn, error) {
|
||||
return nil, fmt.Errorf("socket: Linux network namespace support is not available on %s", runtime.GOOS)
|
||||
}
|
24
vendor/github.com/mdlayher/socket/setbuffer_linux.go
generated
vendored
Normal file
24
vendor/github.com/mdlayher/socket/setbuffer_linux.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package socket
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// setReadBuffer wraps the SO_RCVBUF{,FORCE} setsockopt(2) options.
|
||||
func (c *Conn) setReadBuffer(bytes int) error {
|
||||
err := c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUFFORCE, bytes)
|
||||
if err != nil {
|
||||
err = c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUF, bytes)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// setWriteBuffer wraps the SO_SNDBUF{,FORCE} setsockopt(2) options.
|
||||
func (c *Conn) setWriteBuffer(bytes int) error {
|
||||
err := c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUFFORCE, bytes)
|
||||
if err != nil {
|
||||
err = c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUF, bytes)
|
||||
}
|
||||
return err
|
||||
}
|
16
vendor/github.com/mdlayher/socket/setbuffer_others.go
generated
vendored
Normal file
16
vendor/github.com/mdlayher/socket/setbuffer_others.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package socket
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// setReadBuffer wraps the SO_RCVBUF setsockopt(2) option.
|
||||
func (c *Conn) setReadBuffer(bytes int) error {
|
||||
return c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_RCVBUF, bytes)
|
||||
}
|
||||
|
||||
// setWriteBuffer wraps the SO_SNDBUF setsockopt(2) option.
|
||||
func (c *Conn) setWriteBuffer(bytes int) error {
|
||||
return c.SetsockoptInt(unix.SOL_SOCKET, unix.SO_SNDBUF, bytes)
|
||||
}
|
12
vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go
generated
vendored
Normal file
12
vendor/github.com/mdlayher/socket/typ_cloexec_nonblock.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
//go:build !darwin
|
||||
// +build !darwin
|
||||
|
||||
package socket
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const (
|
||||
// These operating systems support CLOEXEC and NONBLOCK socket options.
|
||||
flagCLOEXEC = true
|
||||
socketFlags = unix.SOCK_CLOEXEC | unix.SOCK_NONBLOCK
|
||||
)
|
11
vendor/github.com/mdlayher/socket/typ_none.go
generated
vendored
Normal file
11
vendor/github.com/mdlayher/socket/typ_none.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package socket
|
||||
|
||||
const (
|
||||
// These operating systems do not support CLOEXEC and NONBLOCK socket
|
||||
// options.
|
||||
flagCLOEXEC = false
|
||||
socketFlags = 0
|
||||
)
|
4
vendor/github.com/mdlayher/vsock/.gitignore
generated
vendored
Normal file
4
vendor/github.com/mdlayher/vsock/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
cover.out
|
||||
vsock.test
|
||||
cmd/vscp/vscp
|
||||
cmd/vsockhttp/vsockhttp
|
53
vendor/github.com/mdlayher/vsock/CHANGELOG.md
generated
vendored
Normal file
53
vendor/github.com/mdlayher/vsock/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
# CHANGELOG
|
||||
|
||||
# v1.2.1
|
||||
|
||||
- [Improvement]: updated dependencies, test with Go 1.20.
|
||||
|
||||
# v1.2.0
|
||||
|
||||
**This is the first release of package vsock that only supports Go 1.18+. Users
|
||||
on older versions of Go must use v1.1.1.**
|
||||
|
||||
- [Improvement]: drop support for older versions of Go so we can begin using
|
||||
modern versions of `x/sys` and other dependencies.
|
||||
|
||||
## v1.1.1
|
||||
|
||||
**This is the last release of package vsock that supports Go 1.17 and below.**
|
||||
|
||||
- [Bug Fix] [commit](https://github.com/mdlayher/vsock/commit/ead86435c244d5d6baad549a6df0557ada3f4401):
|
||||
fix build on non-UNIX platforms such as Windows. This is a no-op change on
|
||||
Linux but provides a friendlier experience for non-Linux users.
|
||||
|
||||
## v1.1.0
|
||||
|
||||
- [New API] [commit](https://github.com/mdlayher/vsock/commit/44cd82dc5f7de644436f22236b111ab97fa9a14f):
|
||||
`vsock.FileListener` can be used to create a `vsock.Listener` from an existing
|
||||
`os.File`, which may be provided by systemd socket activation or another
|
||||
external mechanism.
|
||||
|
||||
## v1.0.1
|
||||
|
||||
- [Bug Fix] [commit](https://github.com/mdlayher/vsock/commit/99a6dccdebad21d1fa5f757d228d677ccb1412dc):
|
||||
upgrade `github.com/mdlayher/socket` to handle non-blocking `connect(2)`
|
||||
errors (called in `vsock.Dial`) properly by checking the `SO_ERROR` socket
|
||||
option. Lock in this behavior with a new test.
|
||||
- [Improvement] [commit](https://github.com/mdlayher/vsock/commit/375f3bbcc363500daf367ec511638a4655471719):
|
||||
downgrade the version of `golang.org/x/net` in use to support Go 1.12. We
|
||||
don't need the latest version for this package.
|
||||
|
||||
## v1.0.0
|
||||
|
||||
**This is the first release of package vsock that only supports Go 1.12+.
|
||||
Users on older versions of Go must use an unstable release.**
|
||||
|
||||
- Initial stable commit!
|
||||
- [API change]: the `vsock.Dial` and `vsock.Listen` constructors now accept an
|
||||
optional `*vsock.Config` parameter to enable future expansion in v1.x.x
|
||||
without prompting further breaking API changes. Because `vsock.Config` has no
|
||||
options as of this release, `nil` may be passed in all call sites to fix
|
||||
existing code upon upgrading to v1.0.0.
|
||||
- [New API]: the `vsock.ListenContextID` function can be used to create a
|
||||
`*vsock.Listener` which is bound to an explicit context ID address, rather
|
||||
than inferring one automatically as `vsock.Listen` does.
|
9
vendor/github.com/mdlayher/vsock/LICENSE.md
generated
vendored
Normal file
9
vendor/github.com/mdlayher/vsock/LICENSE.md
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# MIT License
|
||||
|
||||
Copyright (C) 2017-2022 Matt Layher
|
||||
|
||||
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.
|
21
vendor/github.com/mdlayher/vsock/README.md
generated
vendored
Normal file
21
vendor/github.com/mdlayher/vsock/README.md
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# vsock [](https://github.com/mdlayher/vsock/actions) [](https://pkg.go.dev/github.com/mdlayher/vsock) [](https://goreportcard.com/report/github.com/mdlayher/vsock)
|
||||
|
||||
Package `vsock` provides access to Linux VM sockets (`AF_VSOCK`) for
|
||||
communication between a hypervisor and its virtual machines. MIT Licensed.
|
||||
|
||||
For more information about VM sockets, see my blog about
|
||||
[Linux VM sockets in Go](https://mdlayher.com/blog/linux-vm-sockets-in-go/) or
|
||||
the [QEMU wiki page on virtio-vsock](http://wiki.qemu-project.org/Features/VirtioVsock).
|
||||
|
||||
## Stability
|
||||
|
||||
See the [CHANGELOG](./CHANGELOG.md) file for a description of changes between
|
||||
releases.
|
||||
|
||||
This package has a stable v1 API and any future breaking changes will prompt
|
||||
the release of a new major version. Features and bug fixes will continue to
|
||||
occur in the v1.x.x series.
|
||||
|
||||
This package only supports the two most recent major versions of Go, mirroring
|
||||
Go's own release policy. Older versions of Go may lack critical features and bug
|
||||
fixes which are necessary for this package to function correctly.
|
62
vendor/github.com/mdlayher/vsock/conn_linux.go
generated
vendored
Normal file
62
vendor/github.com/mdlayher/vsock/conn_linux.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package vsock
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/mdlayher/socket"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// A conn is the net.Conn implementation for connection-oriented VM sockets.
|
||||
// We can use socket.Conn directly on Linux to implement all of the necessary
|
||||
// methods.
|
||||
type conn = socket.Conn
|
||||
|
||||
// dial is the entry point for Dial on Linux.
|
||||
func dial(cid, port uint32, _ *Config) (*Conn, error) {
|
||||
// TODO(mdlayher): Config default nil check and initialize. Pass options to
|
||||
// socket.Config where necessary.
|
||||
|
||||
c, err := socket.Socket(unix.AF_VSOCK, unix.SOCK_STREAM, 0, "vsock", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sa := &unix.SockaddrVM{CID: cid, Port: port}
|
||||
rsa, err := c.Connect(context.Background(), sa)
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(mdlayher): getpeername(2) appears to return nil in the GitHub CI
|
||||
// environment, so in the event of a nil sockaddr, fall back to the previous
|
||||
// method of synthesizing the remote address.
|
||||
if rsa == nil {
|
||||
rsa = sa
|
||||
}
|
||||
|
||||
lsa, err := c.Getsockname()
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lsavm := lsa.(*unix.SockaddrVM)
|
||||
rsavm := rsa.(*unix.SockaddrVM)
|
||||
|
||||
return &Conn{
|
||||
c: c,
|
||||
local: &Addr{
|
||||
ContextID: lsavm.CID,
|
||||
Port: lsavm.Port,
|
||||
},
|
||||
remote: &Addr{
|
||||
ContextID: rsavm.CID,
|
||||
Port: rsavm.Port,
|
||||
},
|
||||
}, nil
|
||||
}
|
10
vendor/github.com/mdlayher/vsock/doc.go
generated
vendored
Normal file
10
vendor/github.com/mdlayher/vsock/doc.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
// Package vsock provides access to Linux VM sockets (AF_VSOCK) for
|
||||
// communication between a hypervisor and its virtual machines.
|
||||
//
|
||||
// The types in this package implement interfaces provided by package net and
|
||||
// may be used in applications that expect a net.Listener or net.Conn.
|
||||
//
|
||||
// - *Addr implements net.Addr
|
||||
// - *Conn implements net.Conn
|
||||
// - *Listener implements net.Listener
|
||||
package vsock
|
36
vendor/github.com/mdlayher/vsock/fd_linux.go
generated
vendored
Normal file
36
vendor/github.com/mdlayher/vsock/fd_linux.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
package vsock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// contextID retrieves the local context ID for this system.
|
||||
func contextID() (uint32, error) {
|
||||
f, err := os.Open(devVsock)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return unix.IoctlGetUint32(int(f.Fd()), unix.IOCTL_VM_SOCKETS_GET_LOCAL_CID)
|
||||
}
|
||||
|
||||
// isErrno determines if an error a matches UNIX error number.
|
||||
func isErrno(err error, errno int) bool {
|
||||
switch errno {
|
||||
case ebadf:
|
||||
return err == unix.EBADF
|
||||
case enotconn:
|
||||
return err == unix.ENOTCONN
|
||||
default:
|
||||
panicf("vsock: isErrno called with unhandled error number parameter: %d", errno)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func panicf(format string, a ...interface{}) {
|
||||
panic(fmt.Sprintf(format, a...))
|
||||
}
|
133
vendor/github.com/mdlayher/vsock/listener_linux.go
generated
vendored
Normal file
133
vendor/github.com/mdlayher/vsock/listener_linux.go
generated
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package vsock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/mdlayher/socket"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var _ net.Listener = &listener{}
|
||||
|
||||
// A listener is the net.Listener implementation for connection-oriented
|
||||
// VM sockets.
|
||||
type listener struct {
|
||||
c *socket.Conn
|
||||
addr *Addr
|
||||
}
|
||||
|
||||
// Addr and Close implement the net.Listener interface for listener.
|
||||
func (l *listener) Addr() net.Addr { return l.addr }
|
||||
func (l *listener) Close() error { return l.c.Close() }
|
||||
func (l *listener) SetDeadline(t time.Time) error { return l.c.SetDeadline(t) }
|
||||
|
||||
// Accept accepts a single connection from the listener, and sets up
|
||||
// a net.Conn backed by conn.
|
||||
func (l *listener) Accept() (net.Conn, error) {
|
||||
c, rsa, err := l.c.Accept(context.Background(), 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
savm := rsa.(*unix.SockaddrVM)
|
||||
remote := &Addr{
|
||||
ContextID: savm.CID,
|
||||
Port: savm.Port,
|
||||
}
|
||||
|
||||
return &Conn{
|
||||
c: c,
|
||||
local: l.addr,
|
||||
remote: remote,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// name is the socket name passed to package socket.
|
||||
const name = "vsock"
|
||||
|
||||
// listen is the entry point for Listen on Linux.
|
||||
func listen(cid, port uint32, _ *Config) (*Listener, error) {
|
||||
// TODO(mdlayher): Config default nil check and initialize. Pass options to
|
||||
// socket.Config where necessary.
|
||||
|
||||
c, err := socket.Socket(unix.AF_VSOCK, unix.SOCK_STREAM, 0, name, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Be sure to close the Conn if any of the system calls fail before we
|
||||
// return the Conn to the caller.
|
||||
|
||||
if port == 0 {
|
||||
port = unix.VMADDR_PORT_ANY
|
||||
}
|
||||
|
||||
if err := c.Bind(&unix.SockaddrVM{CID: cid, Port: port}); err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := c.Listen(unix.SOMAXCONN); err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l, err := newListener(c)
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// fileListener is the entry point for FileListener on Linux.
|
||||
func fileListener(f *os.File) (*Listener, error) {
|
||||
c, err := socket.FileConn(f, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l, err := newListener(c)
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// newListener creates a Listener from a raw socket.Conn.
|
||||
func newListener(c *socket.Conn) (*Listener, error) {
|
||||
lsa, err := c.Getsockname()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Now that the library can also accept arbitrary os.Files, we have to
|
||||
// verify the address family so we don't accidentally create a
|
||||
// *vsock.Listener backed by TCP or some other socket type.
|
||||
lsavm, ok := lsa.(*unix.SockaddrVM)
|
||||
if !ok {
|
||||
// All errors should wrapped with os.SyscallError.
|
||||
return nil, os.NewSyscallError("listen", unix.EINVAL)
|
||||
}
|
||||
|
||||
addr := &Addr{
|
||||
ContextID: lsavm.CID,
|
||||
Port: lsavm.Port,
|
||||
}
|
||||
|
||||
return &Listener{
|
||||
l: &listener{
|
||||
c: c,
|
||||
addr: addr,
|
||||
},
|
||||
}, nil
|
||||
}
|
435
vendor/github.com/mdlayher/vsock/vsock.go
generated
vendored
Normal file
435
vendor/github.com/mdlayher/vsock/vsock.go
generated
vendored
Normal file
@ -0,0 +1,435 @@
|
||||
package vsock
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// Hypervisor specifies that a socket should communicate with the hypervisor
|
||||
// process. Note that this is _not_ the same as a socket owned by a process
|
||||
// running on the hypervisor. Most users should probably use Host instead.
|
||||
Hypervisor = 0x0
|
||||
|
||||
// Local specifies that a socket should communicate with a matching socket
|
||||
// on the same machine. This provides an alternative to UNIX sockets or
|
||||
// similar and may be useful in testing VM sockets applications.
|
||||
Local = 0x1
|
||||
|
||||
// Host specifies that a socket should communicate with processes other than
|
||||
// the hypervisor on the host machine. This is the correct choice to
|
||||
// communicate with a process running on a hypervisor using a socket dialed
|
||||
// from a guest.
|
||||
Host = 0x2
|
||||
|
||||
// Error numbers we recognize, copied here to avoid importing x/sys/unix in
|
||||
// cross-platform code.
|
||||
ebadf = 9
|
||||
enotconn = 107
|
||||
|
||||
// devVsock is the location of /dev/vsock. It is exposed on both the
|
||||
// hypervisor and on virtual machines.
|
||||
devVsock = "/dev/vsock"
|
||||
|
||||
// network is the vsock network reported in net.OpError.
|
||||
network = "vsock"
|
||||
|
||||
// Operation names which may be returned in net.OpError.
|
||||
opAccept = "accept"
|
||||
opClose = "close"
|
||||
opDial = "dial"
|
||||
opListen = "listen"
|
||||
opRawControl = "raw-control"
|
||||
opRawRead = "raw-read"
|
||||
opRawWrite = "raw-write"
|
||||
opRead = "read"
|
||||
opSet = "set"
|
||||
opSyscallConn = "syscall-conn"
|
||||
opWrite = "write"
|
||||
)
|
||||
|
||||
// TODO(mdlayher): plumb through socket.Config.NetNS if it makes sense.
|
||||
|
||||
// Config contains options for a Conn or Listener.
|
||||
type Config struct{}
|
||||
|
||||
// Listen opens a connection-oriented net.Listener for incoming VM sockets
|
||||
// connections. The port parameter specifies the port for the Listener. Config
|
||||
// specifies optional configuration for the Listener. If config is nil, a
|
||||
// default configuration will be used.
|
||||
//
|
||||
// To allow the server to assign a port automatically, specify 0 for port. The
|
||||
// address of the server can be retrieved using the Addr method.
|
||||
//
|
||||
// Listen automatically infers the appropriate context ID for this machine by
|
||||
// calling ContextID and passing that value to ListenContextID. Callers with
|
||||
// advanced use cases (such as using the Local context ID) may wish to use
|
||||
// ListenContextID directly.
|
||||
//
|
||||
// When the Listener is no longer needed, Close must be called to free
|
||||
// resources.
|
||||
func Listen(port uint32, cfg *Config) (*Listener, error) {
|
||||
cid, err := ContextID()
|
||||
if err != nil {
|
||||
// No addresses available.
|
||||
return nil, opError(opListen, err, nil, nil)
|
||||
}
|
||||
|
||||
return ListenContextID(cid, port, cfg)
|
||||
}
|
||||
|
||||
// ListenContextID is the same as Listen, but also accepts an explicit context
|
||||
// ID parameter. This function is intended for advanced use cases and most
|
||||
// callers should use Listen instead.
|
||||
//
|
||||
// See the documentation of Listen for more details.
|
||||
func ListenContextID(contextID, port uint32, cfg *Config) (*Listener, error) {
|
||||
l, err := listen(contextID, port, cfg)
|
||||
if err != nil {
|
||||
// No remote address available.
|
||||
return nil, opError(opListen, err, &Addr{
|
||||
ContextID: contextID,
|
||||
Port: port,
|
||||
}, nil)
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// FileListener returns a copy of the network listener corresponding to an open
|
||||
// os.File. It is the caller's responsibility to close the Listener when
|
||||
// finished. Closing the Listener does not affect the os.File, and closing the
|
||||
// os.File does not affect the Listener.
|
||||
//
|
||||
// This function is intended for advanced use cases and most callers should use
|
||||
// Listen instead.
|
||||
func FileListener(f *os.File) (*Listener, error) {
|
||||
l, err := fileListener(f)
|
||||
if err != nil {
|
||||
// No addresses available.
|
||||
return nil, opError(opListen, err, nil, nil)
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
var _ net.Listener = &Listener{}
|
||||
|
||||
// A Listener is a VM sockets implementation of a net.Listener.
|
||||
type Listener struct {
|
||||
l *listener
|
||||
}
|
||||
|
||||
// Accept implements the Accept method in the net.Listener interface; it waits
|
||||
// for the next call and returns a generic net.Conn. The returned net.Conn will
|
||||
// always be of type *Conn.
|
||||
func (l *Listener) Accept() (net.Conn, error) {
|
||||
c, err := l.l.Accept()
|
||||
if err != nil {
|
||||
return nil, l.opError(opAccept, err)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address, a *Addr. The Addr returned is
|
||||
// shared by all invocations of Addr, so do not modify it.
|
||||
func (l *Listener) Addr() net.Addr { return l.l.Addr() }
|
||||
|
||||
// Close stops listening on the VM sockets address. Already Accepted connections
|
||||
// are not closed.
|
||||
func (l *Listener) Close() error {
|
||||
return l.opError(opClose, l.l.Close())
|
||||
}
|
||||
|
||||
// SetDeadline sets the deadline associated with the listener. A zero time value
|
||||
// disables the deadline.
|
||||
func (l *Listener) SetDeadline(t time.Time) error {
|
||||
return l.opError(opSet, l.l.SetDeadline(t))
|
||||
}
|
||||
|
||||
// opError is a convenience for the function opError that also passes the local
|
||||
// address of the Listener.
|
||||
func (l *Listener) opError(op string, err error) error {
|
||||
// No remote address for a Listener.
|
||||
return opError(op, err, l.Addr(), nil)
|
||||
}
|
||||
|
||||
// Dial dials a connection-oriented net.Conn to a VM sockets listener. The
|
||||
// context ID and port parameters specify the address of the listener. Config
|
||||
// specifies optional configuration for the Conn. If config is nil, a default
|
||||
// configuration will be used.
|
||||
//
|
||||
// If dialing a connection from the hypervisor to a virtual machine, the VM's
|
||||
// context ID should be specified.
|
||||
//
|
||||
// If dialing from a VM to the hypervisor, Hypervisor should be used to
|
||||
// communicate with the hypervisor process, or Host should be used to
|
||||
// communicate with other processes on the host machine.
|
||||
//
|
||||
// When the connection is no longer needed, Close must be called to free
|
||||
// resources.
|
||||
func Dial(contextID, port uint32, cfg *Config) (*Conn, error) {
|
||||
c, err := dial(contextID, port, cfg)
|
||||
if err != nil {
|
||||
// No local address, but we have a remote address we can return.
|
||||
return nil, opError(opDial, err, nil, &Addr{
|
||||
ContextID: contextID,
|
||||
Port: port,
|
||||
})
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
var (
|
||||
_ net.Conn = &Conn{}
|
||||
_ syscall.Conn = &Conn{}
|
||||
)
|
||||
|
||||
// A Conn is a VM sockets implementation of a net.Conn.
|
||||
type Conn struct {
|
||||
c *conn
|
||||
local *Addr
|
||||
remote *Addr
|
||||
}
|
||||
|
||||
// Close closes the connection.
|
||||
func (c *Conn) Close() error {
|
||||
return c.opError(opClose, c.c.Close())
|
||||
}
|
||||
|
||||
// CloseRead shuts down the reading side of the VM sockets connection. Most
|
||||
// callers should just use Close.
|
||||
func (c *Conn) CloseRead() error {
|
||||
return c.opError(opClose, c.c.CloseRead())
|
||||
}
|
||||
|
||||
// CloseWrite shuts down the writing side of the VM sockets connection. Most
|
||||
// callers should just use Close.
|
||||
func (c *Conn) CloseWrite() error {
|
||||
return c.opError(opClose, c.c.CloseWrite())
|
||||
}
|
||||
|
||||
// LocalAddr returns the local network address. The Addr returned is shared by
|
||||
// all invocations of LocalAddr, so do not modify it.
|
||||
func (c *Conn) LocalAddr() net.Addr { return c.local }
|
||||
|
||||
// RemoteAddr returns the remote network address. The Addr returned is shared by
|
||||
// all invocations of RemoteAddr, so do not modify it.
|
||||
func (c *Conn) RemoteAddr() net.Addr { return c.remote }
|
||||
|
||||
// Read implements the net.Conn Read method.
|
||||
func (c *Conn) Read(b []byte) (int, error) {
|
||||
n, err := c.c.Read(b)
|
||||
if err != nil {
|
||||
return n, c.opError(opRead, err)
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Write implements the net.Conn Write method.
|
||||
func (c *Conn) Write(b []byte) (int, error) {
|
||||
n, err := c.c.Write(b)
|
||||
if err != nil {
|
||||
return n, c.opError(opWrite, err)
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// SetDeadline implements the net.Conn SetDeadline method.
|
||||
func (c *Conn) SetDeadline(t time.Time) error {
|
||||
return c.opError(opSet, c.c.SetDeadline(t))
|
||||
}
|
||||
|
||||
// SetReadDeadline implements the net.Conn SetReadDeadline method.
|
||||
func (c *Conn) SetReadDeadline(t time.Time) error {
|
||||
return c.opError(opSet, c.c.SetReadDeadline(t))
|
||||
}
|
||||
|
||||
// SetWriteDeadline implements the net.Conn SetWriteDeadline method.
|
||||
func (c *Conn) SetWriteDeadline(t time.Time) error {
|
||||
return c.opError(opSet, c.c.SetWriteDeadline(t))
|
||||
}
|
||||
|
||||
// SyscallConn returns a raw network connection. This implements the
|
||||
// syscall.Conn interface.
|
||||
func (c *Conn) SyscallConn() (syscall.RawConn, error) {
|
||||
rc, err := c.c.SyscallConn()
|
||||
if err != nil {
|
||||
return nil, c.opError(opSyscallConn, err)
|
||||
}
|
||||
|
||||
return &rawConn{
|
||||
rc: rc,
|
||||
local: c.local,
|
||||
remote: c.remote,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// opError is a convenience for the function opError that also passes the local
|
||||
// and remote addresses of the Conn.
|
||||
func (c *Conn) opError(op string, err error) error {
|
||||
return opError(op, err, c.local, c.remote)
|
||||
}
|
||||
|
||||
// TODO(mdlayher): see if we can port smarter net.OpError with local/remote
|
||||
// address error logic into socket.Conn's SyscallConn type to avoid the need for
|
||||
// this wrapper.
|
||||
|
||||
var _ syscall.RawConn = &rawConn{}
|
||||
|
||||
// A rawConn is a syscall.RawConn that wraps an internal syscall.RawConn in order
|
||||
// to produce net.OpError error values.
|
||||
type rawConn struct {
|
||||
rc syscall.RawConn
|
||||
local, remote *Addr
|
||||
}
|
||||
|
||||
// Control implements the syscall.RawConn Control method.
|
||||
func (rc *rawConn) Control(fn func(fd uintptr)) error {
|
||||
return rc.opError(opRawControl, rc.rc.Control(fn))
|
||||
}
|
||||
|
||||
// Control implements the syscall.RawConn Read method.
|
||||
func (rc *rawConn) Read(fn func(fd uintptr) (done bool)) error {
|
||||
return rc.opError(opRawRead, rc.rc.Read(fn))
|
||||
}
|
||||
|
||||
// Control implements the syscall.RawConn Write method.
|
||||
func (rc *rawConn) Write(fn func(fd uintptr) (done bool)) error {
|
||||
return rc.opError(opRawWrite, rc.rc.Write(fn))
|
||||
}
|
||||
|
||||
// opError is a convenience for the function opError that also passes the local
|
||||
// and remote addresses of the rawConn.
|
||||
func (rc *rawConn) opError(op string, err error) error {
|
||||
return opError(op, err, rc.local, rc.remote)
|
||||
}
|
||||
|
||||
var _ net.Addr = &Addr{}
|
||||
|
||||
// An Addr is the address of a VM sockets endpoint.
|
||||
type Addr struct {
|
||||
ContextID, Port uint32
|
||||
}
|
||||
|
||||
// Network returns the address's network name, "vsock".
|
||||
func (a *Addr) Network() string { return network }
|
||||
|
||||
// String returns a human-readable representation of Addr, and indicates if
|
||||
// ContextID is meant to be used for a hypervisor, host, VM, etc.
|
||||
func (a *Addr) String() string {
|
||||
var host string
|
||||
|
||||
switch a.ContextID {
|
||||
case Hypervisor:
|
||||
host = fmt.Sprintf("hypervisor(%d)", a.ContextID)
|
||||
case Local:
|
||||
host = fmt.Sprintf("local(%d)", a.ContextID)
|
||||
case Host:
|
||||
host = fmt.Sprintf("host(%d)", a.ContextID)
|
||||
default:
|
||||
host = fmt.Sprintf("vm(%d)", a.ContextID)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%d", host, a.Port)
|
||||
}
|
||||
|
||||
// fileName returns a file name for use with os.NewFile for Addr.
|
||||
func (a *Addr) fileName() string {
|
||||
return fmt.Sprintf("%s:%s", a.Network(), a.String())
|
||||
}
|
||||
|
||||
// ContextID retrieves the local VM sockets context ID for this system.
|
||||
// ContextID can be used to directly determine if a system is capable of using
|
||||
// VM sockets.
|
||||
//
|
||||
// If the kernel module is unavailable, access to the kernel module is denied,
|
||||
// or VM sockets are unsupported on this system, it returns an error.
|
||||
func ContextID() (uint32, error) {
|
||||
return contextID()
|
||||
}
|
||||
|
||||
// opError unpacks err if possible, producing a net.OpError with the input
|
||||
// parameters in order to implement net.Conn. As a convenience, opError returns
|
||||
// nil if the input error is nil.
|
||||
func opError(op string, err error, local, remote net.Addr) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO(mdlayher): this entire function is suspect and should probably be
|
||||
// looked at carefully, especially with Go 1.13+ error wrapping.
|
||||
//
|
||||
// Eventually this *net.OpError logic should probably be ported into
|
||||
// mdlayher/socket because similar checks are necessary to comply with
|
||||
// nettest.TestConn.
|
||||
|
||||
// Unwrap inner errors from error types.
|
||||
//
|
||||
// TODO(mdlayher): errors.Cause or similar in Go 1.13.
|
||||
switch xerr := err.(type) {
|
||||
// os.PathError produced by os.File method calls.
|
||||
case *os.PathError:
|
||||
// Although we could make use of xerr.Op here, we're passing it manually
|
||||
// for consistency, since some of the Conn calls we are making don't
|
||||
// wrap an os.File, which would return an Op for us.
|
||||
//
|
||||
// As a special case, if the error is related to access to the /dev/vsock
|
||||
// device, we don't unwrap it, so the caller has more context as to why
|
||||
// their operation actually failed than "permission denied" or similar.
|
||||
if xerr.Path != devVsock {
|
||||
err = xerr.Err
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case err == io.EOF, isErrno(err, enotconn):
|
||||
// We may see a literal io.EOF as happens with x/net/nettest, but
|
||||
// "transport not connected" also means io.EOF in Go.
|
||||
return io.EOF
|
||||
case err == os.ErrClosed, isErrno(err, ebadf), strings.Contains(err.Error(), "use of closed"):
|
||||
// Different operations may return different errors that all effectively
|
||||
// indicate a closed file.
|
||||
//
|
||||
// To rectify the differences, net.TCPConn uses an error with this text
|
||||
// from internal/poll for the backing file already being closed.
|
||||
err = errors.New("use of closed network connection")
|
||||
default:
|
||||
// Nothing to do, return this directly.
|
||||
}
|
||||
|
||||
// Determine source and addr using the rules defined by net.OpError's
|
||||
// documentation: https://golang.org/pkg/net/#OpError.
|
||||
var source, addr net.Addr
|
||||
switch op {
|
||||
case opClose, opDial, opRawRead, opRawWrite, opRead, opWrite:
|
||||
if local != nil {
|
||||
source = local
|
||||
}
|
||||
if remote != nil {
|
||||
addr = remote
|
||||
}
|
||||
case opAccept, opListen, opRawControl, opSet, opSyscallConn:
|
||||
if local != nil {
|
||||
addr = local
|
||||
}
|
||||
}
|
||||
|
||||
return &net.OpError{
|
||||
Op: op,
|
||||
Net: network,
|
||||
Source: source,
|
||||
Addr: addr,
|
||||
Err: err,
|
||||
}
|
||||
}
|
45
vendor/github.com/mdlayher/vsock/vsock_others.go
generated
vendored
Normal file
45
vendor/github.com/mdlayher/vsock/vsock_others.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package vsock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// errUnimplemented is returned by all functions on platforms that
|
||||
// cannot make use of VM sockets.
|
||||
var errUnimplemented = fmt.Errorf("vsock: not implemented on %s", runtime.GOOS)
|
||||
|
||||
func fileListener(_ *os.File) (*Listener, error) { return nil, errUnimplemented }
|
||||
func listen(_, _ uint32, _ *Config) (*Listener, error) { return nil, errUnimplemented }
|
||||
|
||||
type listener struct{}
|
||||
|
||||
func (*listener) Accept() (net.Conn, error) { return nil, errUnimplemented }
|
||||
func (*listener) Addr() net.Addr { return nil }
|
||||
func (*listener) Close() error { return errUnimplemented }
|
||||
func (*listener) SetDeadline(_ time.Time) error { return errUnimplemented }
|
||||
|
||||
func dial(_, _ uint32, _ *Config) (*Conn, error) { return nil, errUnimplemented }
|
||||
|
||||
type conn struct{}
|
||||
|
||||
func (*conn) Close() error { return errUnimplemented }
|
||||
func (*conn) CloseRead() error { return errUnimplemented }
|
||||
func (*conn) CloseWrite() error { return errUnimplemented }
|
||||
func (*conn) Read(_ []byte) (int, error) { return 0, errUnimplemented }
|
||||
func (*conn) Write(_ []byte) (int, error) { return 0, errUnimplemented }
|
||||
func (*conn) SetDeadline(_ time.Time) error { return errUnimplemented }
|
||||
func (*conn) SetReadDeadline(_ time.Time) error { return errUnimplemented }
|
||||
func (*conn) SetWriteDeadline(_ time.Time) error { return errUnimplemented }
|
||||
func (*conn) SyscallConn() (syscall.RawConn, error) { return nil, errUnimplemented }
|
||||
|
||||
func contextID() (uint32, error) { return 0, errUnimplemented }
|
||||
|
||||
func isErrno(_ error, _ int) bool { return false }
|
29
vendor/github.com/u-root/uio/LICENSE
generated
vendored
Normal file
29
vendor/github.com/u-root/uio/LICENSE
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2012-2021, u-root Authors
|
||||
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 the copyright holder 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 HOLDER 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.
|
31
vendor/github.com/u-root/uio/ulog/log.go
generated
vendored
Normal file
31
vendor/github.com/u-root/uio/ulog/log.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2019 the u-root Authors. All rights reserved
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ulog exposes logging via a Go interface.
|
||||
//
|
||||
// ulog has three implementations of the Logger interface: a Go standard
|
||||
// library "log" package Logger and a test Logger that logs via a test's
|
||||
// testing.TB.Logf. To use the test logger import "ulog/ulogtest".
|
||||
package ulog
|
||||
|
||||
import "log"
|
||||
|
||||
// Logger is a log receptacle.
|
||||
//
|
||||
// It puts your information somewhere for safekeeping.
|
||||
type Logger interface {
|
||||
Printf(format string, v ...interface{})
|
||||
Print(v ...interface{})
|
||||
}
|
||||
|
||||
// Log is a Logger that prints to the log package's default logger.
|
||||
var Log Logger = log.Default()
|
||||
|
||||
type emptyLogger struct{}
|
||||
|
||||
func (emptyLogger) Printf(format string, v ...interface{}) {}
|
||||
func (emptyLogger) Print(v ...interface{}) {}
|
||||
|
||||
// Null is a logger that prints nothing.
|
||||
var Null Logger = emptyLogger{}
|
41
vendor/golang.org/x/net/bpf/asm.go
generated
vendored
Normal file
41
vendor/golang.org/x/net/bpf/asm.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Assemble converts insts into raw instructions suitable for loading
|
||||
// into a BPF virtual machine.
|
||||
//
|
||||
// Currently, no optimization is attempted, the assembled program flow
|
||||
// is exactly as provided.
|
||||
func Assemble(insts []Instruction) ([]RawInstruction, error) {
|
||||
ret := make([]RawInstruction, len(insts))
|
||||
var err error
|
||||
for i, inst := range insts {
|
||||
ret[i], err = inst.Assemble()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("assembling instruction %d: %s", i+1, err)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Disassemble attempts to parse raw back into
|
||||
// Instructions. Unrecognized RawInstructions are assumed to be an
|
||||
// extension not implemented by this package, and are passed through
|
||||
// unchanged to the output. The allDecoded value reports whether insts
|
||||
// contains no RawInstructions.
|
||||
func Disassemble(raw []RawInstruction) (insts []Instruction, allDecoded bool) {
|
||||
insts = make([]Instruction, len(raw))
|
||||
allDecoded = true
|
||||
for i, r := range raw {
|
||||
insts[i] = r.Disassemble()
|
||||
if _, ok := insts[i].(RawInstruction); ok {
|
||||
allDecoded = false
|
||||
}
|
||||
}
|
||||
return insts, allDecoded
|
||||
}
|
222
vendor/golang.org/x/net/bpf/constants.go
generated
vendored
Normal file
222
vendor/golang.org/x/net/bpf/constants.go
generated
vendored
Normal file
@ -0,0 +1,222 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf
|
||||
|
||||
// A Register is a register of the BPF virtual machine.
|
||||
type Register uint16
|
||||
|
||||
const (
|
||||
// RegA is the accumulator register. RegA is always the
|
||||
// destination register of ALU operations.
|
||||
RegA Register = iota
|
||||
// RegX is the indirection register, used by LoadIndirect
|
||||
// operations.
|
||||
RegX
|
||||
)
|
||||
|
||||
// An ALUOp is an arithmetic or logic operation.
|
||||
type ALUOp uint16
|
||||
|
||||
// ALU binary operation types.
|
||||
const (
|
||||
ALUOpAdd ALUOp = iota << 4
|
||||
ALUOpSub
|
||||
ALUOpMul
|
||||
ALUOpDiv
|
||||
ALUOpOr
|
||||
ALUOpAnd
|
||||
ALUOpShiftLeft
|
||||
ALUOpShiftRight
|
||||
aluOpNeg // Not exported because it's the only unary ALU operation, and gets its own instruction type.
|
||||
ALUOpMod
|
||||
ALUOpXor
|
||||
)
|
||||
|
||||
// A JumpTest is a comparison operator used in conditional jumps.
|
||||
type JumpTest uint16
|
||||
|
||||
// Supported operators for conditional jumps.
|
||||
// K can be RegX for JumpIfX
|
||||
const (
|
||||
// K == A
|
||||
JumpEqual JumpTest = iota
|
||||
// K != A
|
||||
JumpNotEqual
|
||||
// K > A
|
||||
JumpGreaterThan
|
||||
// K < A
|
||||
JumpLessThan
|
||||
// K >= A
|
||||
JumpGreaterOrEqual
|
||||
// K <= A
|
||||
JumpLessOrEqual
|
||||
// K & A != 0
|
||||
JumpBitsSet
|
||||
// K & A == 0
|
||||
JumpBitsNotSet
|
||||
)
|
||||
|
||||
// An Extension is a function call provided by the kernel that
|
||||
// performs advanced operations that are expensive or impossible
|
||||
// within the BPF virtual machine.
|
||||
//
|
||||
// Extensions are only implemented by the Linux kernel.
|
||||
//
|
||||
// TODO: should we prune this list? Some of these extensions seem
|
||||
// either broken or near-impossible to use correctly, whereas other
|
||||
// (len, random, ifindex) are quite useful.
|
||||
type Extension int
|
||||
|
||||
// Extension functions available in the Linux kernel.
|
||||
const (
|
||||
// extOffset is the negative maximum number of instructions used
|
||||
// to load instructions by overloading the K argument.
|
||||
extOffset = -0x1000
|
||||
// ExtLen returns the length of the packet.
|
||||
ExtLen Extension = 1
|
||||
// ExtProto returns the packet's L3 protocol type.
|
||||
ExtProto Extension = 0
|
||||
// ExtType returns the packet's type (skb->pkt_type in the kernel)
|
||||
//
|
||||
// TODO: better documentation. How nice an API do we want to
|
||||
// provide for these esoteric extensions?
|
||||
ExtType Extension = 4
|
||||
// ExtPayloadOffset returns the offset of the packet payload, or
|
||||
// the first protocol header that the kernel does not know how to
|
||||
// parse.
|
||||
ExtPayloadOffset Extension = 52
|
||||
// ExtInterfaceIndex returns the index of the interface on which
|
||||
// the packet was received.
|
||||
ExtInterfaceIndex Extension = 8
|
||||
// ExtNetlinkAttr returns the netlink attribute of type X at
|
||||
// offset A.
|
||||
ExtNetlinkAttr Extension = 12
|
||||
// ExtNetlinkAttrNested returns the nested netlink attribute of
|
||||
// type X at offset A.
|
||||
ExtNetlinkAttrNested Extension = 16
|
||||
// ExtMark returns the packet's mark value.
|
||||
ExtMark Extension = 20
|
||||
// ExtQueue returns the packet's assigned hardware queue.
|
||||
ExtQueue Extension = 24
|
||||
// ExtLinkLayerType returns the packet's hardware address type
|
||||
// (e.g. Ethernet, Infiniband).
|
||||
ExtLinkLayerType Extension = 28
|
||||
// ExtRXHash returns the packets receive hash.
|
||||
//
|
||||
// TODO: figure out what this rxhash actually is.
|
||||
ExtRXHash Extension = 32
|
||||
// ExtCPUID returns the ID of the CPU processing the current
|
||||
// packet.
|
||||
ExtCPUID Extension = 36
|
||||
// ExtVLANTag returns the packet's VLAN tag.
|
||||
ExtVLANTag Extension = 44
|
||||
// ExtVLANTagPresent returns non-zero if the packet has a VLAN
|
||||
// tag.
|
||||
//
|
||||
// TODO: I think this might be a lie: it reads bit 0x1000 of the
|
||||
// VLAN header, which changed meaning in recent revisions of the
|
||||
// spec - this extension may now return meaningless information.
|
||||
ExtVLANTagPresent Extension = 48
|
||||
// ExtVLANProto returns 0x8100 if the frame has a VLAN header,
|
||||
// 0x88a8 if the frame has a "Q-in-Q" double VLAN header, or some
|
||||
// other value if no VLAN information is present.
|
||||
ExtVLANProto Extension = 60
|
||||
// ExtRand returns a uniformly random uint32.
|
||||
ExtRand Extension = 56
|
||||
)
|
||||
|
||||
// The following gives names to various bit patterns used in opcode construction.
|
||||
|
||||
const (
|
||||
opMaskCls uint16 = 0x7
|
||||
// opClsLoad masks
|
||||
opMaskLoadDest = 0x01
|
||||
opMaskLoadWidth = 0x18
|
||||
opMaskLoadMode = 0xe0
|
||||
// opClsALU & opClsJump
|
||||
opMaskOperand = 0x08
|
||||
opMaskOperator = 0xf0
|
||||
)
|
||||
|
||||
const (
|
||||
// +---------------+-----------------+---+---+---+
|
||||
// | AddrMode (3b) | LoadWidth (2b) | 0 | 0 | 0 |
|
||||
// +---------------+-----------------+---+---+---+
|
||||
opClsLoadA uint16 = iota
|
||||
// +---------------+-----------------+---+---+---+
|
||||
// | AddrMode (3b) | LoadWidth (2b) | 0 | 0 | 1 |
|
||||
// +---------------+-----------------+---+---+---+
|
||||
opClsLoadX
|
||||
// +---+---+---+---+---+---+---+---+
|
||||
// | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
|
||||
// +---+---+---+---+---+---+---+---+
|
||||
opClsStoreA
|
||||
// +---+---+---+---+---+---+---+---+
|
||||
// | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
|
||||
// +---+---+---+---+---+---+---+---+
|
||||
opClsStoreX
|
||||
// +---------------+-----------------+---+---+---+
|
||||
// | Operator (4b) | OperandSrc (1b) | 1 | 0 | 0 |
|
||||
// +---------------+-----------------+---+---+---+
|
||||
opClsALU
|
||||
// +-----------------------------+---+---+---+---+
|
||||
// | TestOperator (4b) | 0 | 1 | 0 | 1 |
|
||||
// +-----------------------------+---+---+---+---+
|
||||
opClsJump
|
||||
// +---+-------------------------+---+---+---+---+
|
||||
// | 0 | 0 | 0 | RetSrc (1b) | 0 | 1 | 1 | 0 |
|
||||
// +---+-------------------------+---+---+---+---+
|
||||
opClsReturn
|
||||
// +---+-------------------------+---+---+---+---+
|
||||
// | 0 | 0 | 0 | TXAorTAX (1b) | 0 | 1 | 1 | 1 |
|
||||
// +---+-------------------------+---+---+---+---+
|
||||
opClsMisc
|
||||
)
|
||||
|
||||
const (
|
||||
opAddrModeImmediate uint16 = iota << 5
|
||||
opAddrModeAbsolute
|
||||
opAddrModeIndirect
|
||||
opAddrModeScratch
|
||||
opAddrModePacketLen // actually an extension, not an addressing mode.
|
||||
opAddrModeMemShift
|
||||
)
|
||||
|
||||
const (
|
||||
opLoadWidth4 uint16 = iota << 3
|
||||
opLoadWidth2
|
||||
opLoadWidth1
|
||||
)
|
||||
|
||||
// Operand for ALU and Jump instructions
|
||||
type opOperand uint16
|
||||
|
||||
// Supported operand sources.
|
||||
const (
|
||||
opOperandConstant opOperand = iota << 3
|
||||
opOperandX
|
||||
)
|
||||
|
||||
// An jumpOp is a conditional jump condition.
|
||||
type jumpOp uint16
|
||||
|
||||
// Supported jump conditions.
|
||||
const (
|
||||
opJumpAlways jumpOp = iota << 4
|
||||
opJumpEqual
|
||||
opJumpGT
|
||||
opJumpGE
|
||||
opJumpSet
|
||||
)
|
||||
|
||||
const (
|
||||
opRetSrcConstant uint16 = iota << 4
|
||||
opRetSrcA
|
||||
)
|
||||
|
||||
const (
|
||||
opMiscTAX = 0x00
|
||||
opMiscTXA = 0x80
|
||||
)
|
80
vendor/golang.org/x/net/bpf/doc.go
generated
vendored
Normal file
80
vendor/golang.org/x/net/bpf/doc.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package bpf implements marshaling and unmarshaling of programs for the
|
||||
Berkeley Packet Filter virtual machine, and provides a Go implementation
|
||||
of the virtual machine.
|
||||
|
||||
BPF's main use is to specify a packet filter for network taps, so that
|
||||
the kernel doesn't have to expensively copy every packet it sees to
|
||||
userspace. However, it's been repurposed to other areas where running
|
||||
user code in-kernel is needed. For example, Linux's seccomp uses BPF
|
||||
to apply security policies to system calls. For simplicity, this
|
||||
documentation refers only to packets, but other uses of BPF have their
|
||||
own data payloads.
|
||||
|
||||
BPF programs run in a restricted virtual machine. It has almost no
|
||||
access to kernel functions, and while conditional branches are
|
||||
allowed, they can only jump forwards, to guarantee that there are no
|
||||
infinite loops.
|
||||
|
||||
# The virtual machine
|
||||
|
||||
The BPF VM is an accumulator machine. Its main register, called
|
||||
register A, is an implicit source and destination in all arithmetic
|
||||
and logic operations. The machine also has 16 scratch registers for
|
||||
temporary storage, and an indirection register (register X) for
|
||||
indirect memory access. All registers are 32 bits wide.
|
||||
|
||||
Each run of a BPF program is given one packet, which is placed in the
|
||||
VM's read-only "main memory". LoadAbsolute and LoadIndirect
|
||||
instructions can fetch up to 32 bits at a time into register A for
|
||||
examination.
|
||||
|
||||
The goal of a BPF program is to produce and return a verdict (uint32),
|
||||
which tells the kernel what to do with the packet. In the context of
|
||||
packet filtering, the returned value is the number of bytes of the
|
||||
packet to forward to userspace, or 0 to ignore the packet. Other
|
||||
contexts like seccomp define their own return values.
|
||||
|
||||
In order to simplify programs, attempts to read past the end of the
|
||||
packet terminate the program execution with a verdict of 0 (ignore
|
||||
packet). This means that the vast majority of BPF programs don't need
|
||||
to do any explicit bounds checking.
|
||||
|
||||
In addition to the bytes of the packet, some BPF programs have access
|
||||
to extensions, which are essentially calls to kernel utility
|
||||
functions. Currently, the only extensions supported by this package
|
||||
are the Linux packet filter extensions.
|
||||
|
||||
# Examples
|
||||
|
||||
This packet filter selects all ARP packets.
|
||||
|
||||
bpf.Assemble([]bpf.Instruction{
|
||||
// Load "EtherType" field from the ethernet header.
|
||||
bpf.LoadAbsolute{Off: 12, Size: 2},
|
||||
// Skip over the next instruction if EtherType is not ARP.
|
||||
bpf.JumpIf{Cond: bpf.JumpNotEqual, Val: 0x0806, SkipTrue: 1},
|
||||
// Verdict is "send up to 4k of the packet to userspace."
|
||||
bpf.RetConstant{Val: 4096},
|
||||
// Verdict is "ignore packet."
|
||||
bpf.RetConstant{Val: 0},
|
||||
})
|
||||
|
||||
This packet filter captures a random 1% sample of traffic.
|
||||
|
||||
bpf.Assemble([]bpf.Instruction{
|
||||
// Get a 32-bit random number from the Linux kernel.
|
||||
bpf.LoadExtension{Num: bpf.ExtRand},
|
||||
// 1% dice roll?
|
||||
bpf.JumpIf{Cond: bpf.JumpLessThan, Val: 2^32/100, SkipFalse: 1},
|
||||
// Capture.
|
||||
bpf.RetConstant{Val: 4096},
|
||||
// Ignore.
|
||||
bpf.RetConstant{Val: 0},
|
||||
})
|
||||
*/
|
||||
package bpf // import "golang.org/x/net/bpf"
|
726
vendor/golang.org/x/net/bpf/instructions.go
generated
vendored
Normal file
726
vendor/golang.org/x/net/bpf/instructions.go
generated
vendored
Normal file
@ -0,0 +1,726 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf
|
||||
|
||||
import "fmt"
|
||||
|
||||
// An Instruction is one instruction executed by the BPF virtual
|
||||
// machine.
|
||||
type Instruction interface {
|
||||
// Assemble assembles the Instruction into a RawInstruction.
|
||||
Assemble() (RawInstruction, error)
|
||||
}
|
||||
|
||||
// A RawInstruction is a raw BPF virtual machine instruction.
|
||||
type RawInstruction struct {
|
||||
// Operation to execute.
|
||||
Op uint16
|
||||
// For conditional jump instructions, the number of instructions
|
||||
// to skip if the condition is true/false.
|
||||
Jt uint8
|
||||
Jf uint8
|
||||
// Constant parameter. The meaning depends on the Op.
|
||||
K uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (ri RawInstruction) Assemble() (RawInstruction, error) { return ri, nil }
|
||||
|
||||
// Disassemble parses ri into an Instruction and returns it. If ri is
|
||||
// not recognized by this package, ri itself is returned.
|
||||
func (ri RawInstruction) Disassemble() Instruction {
|
||||
switch ri.Op & opMaskCls {
|
||||
case opClsLoadA, opClsLoadX:
|
||||
reg := Register(ri.Op & opMaskLoadDest)
|
||||
sz := 0
|
||||
switch ri.Op & opMaskLoadWidth {
|
||||
case opLoadWidth4:
|
||||
sz = 4
|
||||
case opLoadWidth2:
|
||||
sz = 2
|
||||
case opLoadWidth1:
|
||||
sz = 1
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
switch ri.Op & opMaskLoadMode {
|
||||
case opAddrModeImmediate:
|
||||
if sz != 4 {
|
||||
return ri
|
||||
}
|
||||
return LoadConstant{Dst: reg, Val: ri.K}
|
||||
case opAddrModeScratch:
|
||||
if sz != 4 || ri.K > 15 {
|
||||
return ri
|
||||
}
|
||||
return LoadScratch{Dst: reg, N: int(ri.K)}
|
||||
case opAddrModeAbsolute:
|
||||
if ri.K > extOffset+0xffffffff {
|
||||
return LoadExtension{Num: Extension(-extOffset + ri.K)}
|
||||
}
|
||||
return LoadAbsolute{Size: sz, Off: ri.K}
|
||||
case opAddrModeIndirect:
|
||||
return LoadIndirect{Size: sz, Off: ri.K}
|
||||
case opAddrModePacketLen:
|
||||
if sz != 4 {
|
||||
return ri
|
||||
}
|
||||
return LoadExtension{Num: ExtLen}
|
||||
case opAddrModeMemShift:
|
||||
return LoadMemShift{Off: ri.K}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
|
||||
case opClsStoreA:
|
||||
if ri.Op != opClsStoreA || ri.K > 15 {
|
||||
return ri
|
||||
}
|
||||
return StoreScratch{Src: RegA, N: int(ri.K)}
|
||||
|
||||
case opClsStoreX:
|
||||
if ri.Op != opClsStoreX || ri.K > 15 {
|
||||
return ri
|
||||
}
|
||||
return StoreScratch{Src: RegX, N: int(ri.K)}
|
||||
|
||||
case opClsALU:
|
||||
switch op := ALUOp(ri.Op & opMaskOperator); op {
|
||||
case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor:
|
||||
switch operand := opOperand(ri.Op & opMaskOperand); operand {
|
||||
case opOperandX:
|
||||
return ALUOpX{Op: op}
|
||||
case opOperandConstant:
|
||||
return ALUOpConstant{Op: op, Val: ri.K}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
case aluOpNeg:
|
||||
return NegateA{}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
|
||||
case opClsJump:
|
||||
switch op := jumpOp(ri.Op & opMaskOperator); op {
|
||||
case opJumpAlways:
|
||||
return Jump{Skip: ri.K}
|
||||
case opJumpEqual, opJumpGT, opJumpGE, opJumpSet:
|
||||
cond, skipTrue, skipFalse := jumpOpToTest(op, ri.Jt, ri.Jf)
|
||||
switch operand := opOperand(ri.Op & opMaskOperand); operand {
|
||||
case opOperandX:
|
||||
return JumpIfX{Cond: cond, SkipTrue: skipTrue, SkipFalse: skipFalse}
|
||||
case opOperandConstant:
|
||||
return JumpIf{Cond: cond, Val: ri.K, SkipTrue: skipTrue, SkipFalse: skipFalse}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
|
||||
case opClsReturn:
|
||||
switch ri.Op {
|
||||
case opClsReturn | opRetSrcA:
|
||||
return RetA{}
|
||||
case opClsReturn | opRetSrcConstant:
|
||||
return RetConstant{Val: ri.K}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
|
||||
case opClsMisc:
|
||||
switch ri.Op {
|
||||
case opClsMisc | opMiscTAX:
|
||||
return TAX{}
|
||||
case opClsMisc | opMiscTXA:
|
||||
return TXA{}
|
||||
default:
|
||||
return ri
|
||||
}
|
||||
|
||||
default:
|
||||
panic("unreachable") // switch is exhaustive on the bit pattern
|
||||
}
|
||||
}
|
||||
|
||||
func jumpOpToTest(op jumpOp, skipTrue uint8, skipFalse uint8) (JumpTest, uint8, uint8) {
|
||||
var test JumpTest
|
||||
|
||||
// Decode "fake" jump conditions that don't appear in machine code
|
||||
// Ensures the Assemble -> Disassemble stage recreates the same instructions
|
||||
// See https://github.com/golang/go/issues/18470
|
||||
if skipTrue == 0 {
|
||||
switch op {
|
||||
case opJumpEqual:
|
||||
test = JumpNotEqual
|
||||
case opJumpGT:
|
||||
test = JumpLessOrEqual
|
||||
case opJumpGE:
|
||||
test = JumpLessThan
|
||||
case opJumpSet:
|
||||
test = JumpBitsNotSet
|
||||
}
|
||||
|
||||
return test, skipFalse, 0
|
||||
}
|
||||
|
||||
switch op {
|
||||
case opJumpEqual:
|
||||
test = JumpEqual
|
||||
case opJumpGT:
|
||||
test = JumpGreaterThan
|
||||
case opJumpGE:
|
||||
test = JumpGreaterOrEqual
|
||||
case opJumpSet:
|
||||
test = JumpBitsSet
|
||||
}
|
||||
|
||||
return test, skipTrue, skipFalse
|
||||
}
|
||||
|
||||
// LoadConstant loads Val into register Dst.
|
||||
type LoadConstant struct {
|
||||
Dst Register
|
||||
Val uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadConstant) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(a.Dst, 4, opAddrModeImmediate, a.Val)
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadConstant) String() string {
|
||||
switch a.Dst {
|
||||
case RegA:
|
||||
return fmt.Sprintf("ld #%d", a.Val)
|
||||
case RegX:
|
||||
return fmt.Sprintf("ldx #%d", a.Val)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadScratch loads scratch[N] into register Dst.
|
||||
type LoadScratch struct {
|
||||
Dst Register
|
||||
N int // 0-15
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadScratch) Assemble() (RawInstruction, error) {
|
||||
if a.N < 0 || a.N > 15 {
|
||||
return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N)
|
||||
}
|
||||
return assembleLoad(a.Dst, 4, opAddrModeScratch, uint32(a.N))
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadScratch) String() string {
|
||||
switch a.Dst {
|
||||
case RegA:
|
||||
return fmt.Sprintf("ld M[%d]", a.N)
|
||||
case RegX:
|
||||
return fmt.Sprintf("ldx M[%d]", a.N)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadAbsolute loads packet[Off:Off+Size] as an integer value into
|
||||
// register A.
|
||||
type LoadAbsolute struct {
|
||||
Off uint32
|
||||
Size int // 1, 2 or 4
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadAbsolute) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(RegA, a.Size, opAddrModeAbsolute, a.Off)
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadAbsolute) String() string {
|
||||
switch a.Size {
|
||||
case 1: // byte
|
||||
return fmt.Sprintf("ldb [%d]", a.Off)
|
||||
case 2: // half word
|
||||
return fmt.Sprintf("ldh [%d]", a.Off)
|
||||
case 4: // word
|
||||
if a.Off > extOffset+0xffffffff {
|
||||
return LoadExtension{Num: Extension(a.Off + 0x1000)}.String()
|
||||
}
|
||||
return fmt.Sprintf("ld [%d]", a.Off)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadIndirect loads packet[X+Off:X+Off+Size] as an integer value
|
||||
// into register A.
|
||||
type LoadIndirect struct {
|
||||
Off uint32
|
||||
Size int // 1, 2 or 4
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadIndirect) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(RegA, a.Size, opAddrModeIndirect, a.Off)
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadIndirect) String() string {
|
||||
switch a.Size {
|
||||
case 1: // byte
|
||||
return fmt.Sprintf("ldb [x + %d]", a.Off)
|
||||
case 2: // half word
|
||||
return fmt.Sprintf("ldh [x + %d]", a.Off)
|
||||
case 4: // word
|
||||
return fmt.Sprintf("ld [x + %d]", a.Off)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadMemShift multiplies the first 4 bits of the byte at packet[Off]
|
||||
// by 4 and stores the result in register X.
|
||||
//
|
||||
// This instruction is mainly useful to load into X the length of an
|
||||
// IPv4 packet header in a single instruction, rather than have to do
|
||||
// the arithmetic on the header's first byte by hand.
|
||||
type LoadMemShift struct {
|
||||
Off uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadMemShift) Assemble() (RawInstruction, error) {
|
||||
return assembleLoad(RegX, 1, opAddrModeMemShift, a.Off)
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadMemShift) String() string {
|
||||
return fmt.Sprintf("ldx 4*([%d]&0xf)", a.Off)
|
||||
}
|
||||
|
||||
// LoadExtension invokes a linux-specific extension and stores the
|
||||
// result in register A.
|
||||
type LoadExtension struct {
|
||||
Num Extension
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a LoadExtension) Assemble() (RawInstruction, error) {
|
||||
if a.Num == ExtLen {
|
||||
return assembleLoad(RegA, 4, opAddrModePacketLen, 0)
|
||||
}
|
||||
return assembleLoad(RegA, 4, opAddrModeAbsolute, uint32(extOffset+a.Num))
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a LoadExtension) String() string {
|
||||
switch a.Num {
|
||||
case ExtLen:
|
||||
return "ld #len"
|
||||
case ExtProto:
|
||||
return "ld #proto"
|
||||
case ExtType:
|
||||
return "ld #type"
|
||||
case ExtPayloadOffset:
|
||||
return "ld #poff"
|
||||
case ExtInterfaceIndex:
|
||||
return "ld #ifidx"
|
||||
case ExtNetlinkAttr:
|
||||
return "ld #nla"
|
||||
case ExtNetlinkAttrNested:
|
||||
return "ld #nlan"
|
||||
case ExtMark:
|
||||
return "ld #mark"
|
||||
case ExtQueue:
|
||||
return "ld #queue"
|
||||
case ExtLinkLayerType:
|
||||
return "ld #hatype"
|
||||
case ExtRXHash:
|
||||
return "ld #rxhash"
|
||||
case ExtCPUID:
|
||||
return "ld #cpu"
|
||||
case ExtVLANTag:
|
||||
return "ld #vlan_tci"
|
||||
case ExtVLANTagPresent:
|
||||
return "ld #vlan_avail"
|
||||
case ExtVLANProto:
|
||||
return "ld #vlan_tpid"
|
||||
case ExtRand:
|
||||
return "ld #rand"
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// StoreScratch stores register Src into scratch[N].
|
||||
type StoreScratch struct {
|
||||
Src Register
|
||||
N int // 0-15
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a StoreScratch) Assemble() (RawInstruction, error) {
|
||||
if a.N < 0 || a.N > 15 {
|
||||
return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N)
|
||||
}
|
||||
var op uint16
|
||||
switch a.Src {
|
||||
case RegA:
|
||||
op = opClsStoreA
|
||||
case RegX:
|
||||
op = opClsStoreX
|
||||
default:
|
||||
return RawInstruction{}, fmt.Errorf("invalid source register %v", a.Src)
|
||||
}
|
||||
|
||||
return RawInstruction{
|
||||
Op: op,
|
||||
K: uint32(a.N),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a StoreScratch) String() string {
|
||||
switch a.Src {
|
||||
case RegA:
|
||||
return fmt.Sprintf("st M[%d]", a.N)
|
||||
case RegX:
|
||||
return fmt.Sprintf("stx M[%d]", a.N)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// ALUOpConstant executes A = A <Op> Val.
|
||||
type ALUOpConstant struct {
|
||||
Op ALUOp
|
||||
Val uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a ALUOpConstant) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsALU | uint16(opOperandConstant) | uint16(a.Op),
|
||||
K: a.Val,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a ALUOpConstant) String() string {
|
||||
switch a.Op {
|
||||
case ALUOpAdd:
|
||||
return fmt.Sprintf("add #%d", a.Val)
|
||||
case ALUOpSub:
|
||||
return fmt.Sprintf("sub #%d", a.Val)
|
||||
case ALUOpMul:
|
||||
return fmt.Sprintf("mul #%d", a.Val)
|
||||
case ALUOpDiv:
|
||||
return fmt.Sprintf("div #%d", a.Val)
|
||||
case ALUOpMod:
|
||||
return fmt.Sprintf("mod #%d", a.Val)
|
||||
case ALUOpAnd:
|
||||
return fmt.Sprintf("and #%d", a.Val)
|
||||
case ALUOpOr:
|
||||
return fmt.Sprintf("or #%d", a.Val)
|
||||
case ALUOpXor:
|
||||
return fmt.Sprintf("xor #%d", a.Val)
|
||||
case ALUOpShiftLeft:
|
||||
return fmt.Sprintf("lsh #%d", a.Val)
|
||||
case ALUOpShiftRight:
|
||||
return fmt.Sprintf("rsh #%d", a.Val)
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// ALUOpX executes A = A <Op> X
|
||||
type ALUOpX struct {
|
||||
Op ALUOp
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a ALUOpX) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsALU | uint16(opOperandX) | uint16(a.Op),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a ALUOpX) String() string {
|
||||
switch a.Op {
|
||||
case ALUOpAdd:
|
||||
return "add x"
|
||||
case ALUOpSub:
|
||||
return "sub x"
|
||||
case ALUOpMul:
|
||||
return "mul x"
|
||||
case ALUOpDiv:
|
||||
return "div x"
|
||||
case ALUOpMod:
|
||||
return "mod x"
|
||||
case ALUOpAnd:
|
||||
return "and x"
|
||||
case ALUOpOr:
|
||||
return "or x"
|
||||
case ALUOpXor:
|
||||
return "xor x"
|
||||
case ALUOpShiftLeft:
|
||||
return "lsh x"
|
||||
case ALUOpShiftRight:
|
||||
return "rsh x"
|
||||
default:
|
||||
return fmt.Sprintf("unknown instruction: %#v", a)
|
||||
}
|
||||
}
|
||||
|
||||
// NegateA executes A = -A.
|
||||
type NegateA struct{}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a NegateA) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsALU | uint16(aluOpNeg),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a NegateA) String() string {
|
||||
return fmt.Sprintf("neg")
|
||||
}
|
||||
|
||||
// Jump skips the following Skip instructions in the program.
|
||||
type Jump struct {
|
||||
Skip uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a Jump) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsJump | uint16(opJumpAlways),
|
||||
K: a.Skip,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a Jump) String() string {
|
||||
return fmt.Sprintf("ja %d", a.Skip)
|
||||
}
|
||||
|
||||
// JumpIf skips the following Skip instructions in the program if A
|
||||
// <Cond> Val is true.
|
||||
type JumpIf struct {
|
||||
Cond JumpTest
|
||||
Val uint32
|
||||
SkipTrue uint8
|
||||
SkipFalse uint8
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a JumpIf) Assemble() (RawInstruction, error) {
|
||||
return jumpToRaw(a.Cond, opOperandConstant, a.Val, a.SkipTrue, a.SkipFalse)
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a JumpIf) String() string {
|
||||
return jumpToString(a.Cond, fmt.Sprintf("#%d", a.Val), a.SkipTrue, a.SkipFalse)
|
||||
}
|
||||
|
||||
// JumpIfX skips the following Skip instructions in the program if A
|
||||
// <Cond> X is true.
|
||||
type JumpIfX struct {
|
||||
Cond JumpTest
|
||||
SkipTrue uint8
|
||||
SkipFalse uint8
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a JumpIfX) Assemble() (RawInstruction, error) {
|
||||
return jumpToRaw(a.Cond, opOperandX, 0, a.SkipTrue, a.SkipFalse)
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a JumpIfX) String() string {
|
||||
return jumpToString(a.Cond, "x", a.SkipTrue, a.SkipFalse)
|
||||
}
|
||||
|
||||
// jumpToRaw assembles a jump instruction into a RawInstruction
|
||||
func jumpToRaw(test JumpTest, operand opOperand, k uint32, skipTrue, skipFalse uint8) (RawInstruction, error) {
|
||||
var (
|
||||
cond jumpOp
|
||||
flip bool
|
||||
)
|
||||
switch test {
|
||||
case JumpEqual:
|
||||
cond = opJumpEqual
|
||||
case JumpNotEqual:
|
||||
cond, flip = opJumpEqual, true
|
||||
case JumpGreaterThan:
|
||||
cond = opJumpGT
|
||||
case JumpLessThan:
|
||||
cond, flip = opJumpGE, true
|
||||
case JumpGreaterOrEqual:
|
||||
cond = opJumpGE
|
||||
case JumpLessOrEqual:
|
||||
cond, flip = opJumpGT, true
|
||||
case JumpBitsSet:
|
||||
cond = opJumpSet
|
||||
case JumpBitsNotSet:
|
||||
cond, flip = opJumpSet, true
|
||||
default:
|
||||
return RawInstruction{}, fmt.Errorf("unknown JumpTest %v", test)
|
||||
}
|
||||
jt, jf := skipTrue, skipFalse
|
||||
if flip {
|
||||
jt, jf = jf, jt
|
||||
}
|
||||
return RawInstruction{
|
||||
Op: opClsJump | uint16(cond) | uint16(operand),
|
||||
Jt: jt,
|
||||
Jf: jf,
|
||||
K: k,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// jumpToString converts a jump instruction to assembler notation
|
||||
func jumpToString(cond JumpTest, operand string, skipTrue, skipFalse uint8) string {
|
||||
switch cond {
|
||||
// K == A
|
||||
case JumpEqual:
|
||||
return conditionalJump(operand, skipTrue, skipFalse, "jeq", "jneq")
|
||||
// K != A
|
||||
case JumpNotEqual:
|
||||
return fmt.Sprintf("jneq %s,%d", operand, skipTrue)
|
||||
// K > A
|
||||
case JumpGreaterThan:
|
||||
return conditionalJump(operand, skipTrue, skipFalse, "jgt", "jle")
|
||||
// K < A
|
||||
case JumpLessThan:
|
||||
return fmt.Sprintf("jlt %s,%d", operand, skipTrue)
|
||||
// K >= A
|
||||
case JumpGreaterOrEqual:
|
||||
return conditionalJump(operand, skipTrue, skipFalse, "jge", "jlt")
|
||||
// K <= A
|
||||
case JumpLessOrEqual:
|
||||
return fmt.Sprintf("jle %s,%d", operand, skipTrue)
|
||||
// K & A != 0
|
||||
case JumpBitsSet:
|
||||
if skipFalse > 0 {
|
||||
return fmt.Sprintf("jset %s,%d,%d", operand, skipTrue, skipFalse)
|
||||
}
|
||||
return fmt.Sprintf("jset %s,%d", operand, skipTrue)
|
||||
// K & A == 0, there is no assembler instruction for JumpBitNotSet, use JumpBitSet and invert skips
|
||||
case JumpBitsNotSet:
|
||||
return jumpToString(JumpBitsSet, operand, skipFalse, skipTrue)
|
||||
default:
|
||||
return fmt.Sprintf("unknown JumpTest %#v", cond)
|
||||
}
|
||||
}
|
||||
|
||||
func conditionalJump(operand string, skipTrue, skipFalse uint8, positiveJump, negativeJump string) string {
|
||||
if skipTrue > 0 {
|
||||
if skipFalse > 0 {
|
||||
return fmt.Sprintf("%s %s,%d,%d", positiveJump, operand, skipTrue, skipFalse)
|
||||
}
|
||||
return fmt.Sprintf("%s %s,%d", positiveJump, operand, skipTrue)
|
||||
}
|
||||
return fmt.Sprintf("%s %s,%d", negativeJump, operand, skipFalse)
|
||||
}
|
||||
|
||||
// RetA exits the BPF program, returning the value of register A.
|
||||
type RetA struct{}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a RetA) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsReturn | opRetSrcA,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a RetA) String() string {
|
||||
return fmt.Sprintf("ret a")
|
||||
}
|
||||
|
||||
// RetConstant exits the BPF program, returning a constant value.
|
||||
type RetConstant struct {
|
||||
Val uint32
|
||||
}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a RetConstant) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsReturn | opRetSrcConstant,
|
||||
K: a.Val,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a RetConstant) String() string {
|
||||
return fmt.Sprintf("ret #%d", a.Val)
|
||||
}
|
||||
|
||||
// TXA copies the value of register X to register A.
|
||||
type TXA struct{}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a TXA) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsMisc | opMiscTXA,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a TXA) String() string {
|
||||
return fmt.Sprintf("txa")
|
||||
}
|
||||
|
||||
// TAX copies the value of register A to register X.
|
||||
type TAX struct{}
|
||||
|
||||
// Assemble implements the Instruction Assemble method.
|
||||
func (a TAX) Assemble() (RawInstruction, error) {
|
||||
return RawInstruction{
|
||||
Op: opClsMisc | opMiscTAX,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// String returns the instruction in assembler notation.
|
||||
func (a TAX) String() string {
|
||||
return fmt.Sprintf("tax")
|
||||
}
|
||||
|
||||
func assembleLoad(dst Register, loadSize int, mode uint16, k uint32) (RawInstruction, error) {
|
||||
var (
|
||||
cls uint16
|
||||
sz uint16
|
||||
)
|
||||
switch dst {
|
||||
case RegA:
|
||||
cls = opClsLoadA
|
||||
case RegX:
|
||||
cls = opClsLoadX
|
||||
default:
|
||||
return RawInstruction{}, fmt.Errorf("invalid target register %v", dst)
|
||||
}
|
||||
switch loadSize {
|
||||
case 1:
|
||||
sz = opLoadWidth1
|
||||
case 2:
|
||||
sz = opLoadWidth2
|
||||
case 4:
|
||||
sz = opLoadWidth4
|
||||
default:
|
||||
return RawInstruction{}, fmt.Errorf("invalid load byte length %d", sz)
|
||||
}
|
||||
return RawInstruction{
|
||||
Op: cls | sz | mode,
|
||||
K: k,
|
||||
}, nil
|
||||
}
|
10
vendor/golang.org/x/net/bpf/setter.go
generated
vendored
Normal file
10
vendor/golang.org/x/net/bpf/setter.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf
|
||||
|
||||
// A Setter is a type which can attach a compiled BPF filter to itself.
|
||||
type Setter interface {
|
||||
SetBPF(filter []RawInstruction) error
|
||||
}
|
150
vendor/golang.org/x/net/bpf/vm.go
generated
vendored
Normal file
150
vendor/golang.org/x/net/bpf/vm.go
generated
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// A VM is an emulated BPF virtual machine.
|
||||
type VM struct {
|
||||
filter []Instruction
|
||||
}
|
||||
|
||||
// NewVM returns a new VM using the input BPF program.
|
||||
func NewVM(filter []Instruction) (*VM, error) {
|
||||
if len(filter) == 0 {
|
||||
return nil, errors.New("one or more Instructions must be specified")
|
||||
}
|
||||
|
||||
for i, ins := range filter {
|
||||
check := len(filter) - (i + 1)
|
||||
switch ins := ins.(type) {
|
||||
// Check for out-of-bounds jumps in instructions
|
||||
case Jump:
|
||||
if check <= int(ins.Skip) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions; jumping past program bounds", ins.Skip)
|
||||
}
|
||||
case JumpIf:
|
||||
if check <= int(ins.SkipTrue) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
|
||||
}
|
||||
if check <= int(ins.SkipFalse) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
|
||||
}
|
||||
case JumpIfX:
|
||||
if check <= int(ins.SkipTrue) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
|
||||
}
|
||||
if check <= int(ins.SkipFalse) {
|
||||
return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
|
||||
}
|
||||
// Check for division or modulus by zero
|
||||
case ALUOpConstant:
|
||||
if ins.Val != 0 {
|
||||
break
|
||||
}
|
||||
|
||||
switch ins.Op {
|
||||
case ALUOpDiv, ALUOpMod:
|
||||
return nil, errors.New("cannot divide by zero using ALUOpConstant")
|
||||
}
|
||||
// Check for unknown extensions
|
||||
case LoadExtension:
|
||||
switch ins.Num {
|
||||
case ExtLen:
|
||||
default:
|
||||
return nil, fmt.Errorf("extension %d not implemented", ins.Num)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure last instruction is a return instruction
|
||||
switch filter[len(filter)-1].(type) {
|
||||
case RetA, RetConstant:
|
||||
default:
|
||||
return nil, errors.New("BPF program must end with RetA or RetConstant")
|
||||
}
|
||||
|
||||
// Though our VM works using disassembled instructions, we
|
||||
// attempt to assemble the input filter anyway to ensure it is compatible
|
||||
// with an operating system VM.
|
||||
_, err := Assemble(filter)
|
||||
|
||||
return &VM{
|
||||
filter: filter,
|
||||
}, err
|
||||
}
|
||||
|
||||
// Run runs the VM's BPF program against the input bytes.
|
||||
// Run returns the number of bytes accepted by the BPF program, and any errors
|
||||
// which occurred while processing the program.
|
||||
func (v *VM) Run(in []byte) (int, error) {
|
||||
var (
|
||||
// Registers of the virtual machine
|
||||
regA uint32
|
||||
regX uint32
|
||||
regScratch [16]uint32
|
||||
|
||||
// OK is true if the program should continue processing the next
|
||||
// instruction, or false if not, causing the loop to break
|
||||
ok = true
|
||||
)
|
||||
|
||||
// TODO(mdlayher): implement:
|
||||
// - NegateA:
|
||||
// - would require a change from uint32 registers to int32
|
||||
// registers
|
||||
|
||||
// TODO(mdlayher): add interop tests that check signedness of ALU
|
||||
// operations against kernel implementation, and make sure Go
|
||||
// implementation matches behavior
|
||||
|
||||
for i := 0; i < len(v.filter) && ok; i++ {
|
||||
ins := v.filter[i]
|
||||
|
||||
switch ins := ins.(type) {
|
||||
case ALUOpConstant:
|
||||
regA = aluOpConstant(ins, regA)
|
||||
case ALUOpX:
|
||||
regA, ok = aluOpX(ins, regA, regX)
|
||||
case Jump:
|
||||
i += int(ins.Skip)
|
||||
case JumpIf:
|
||||
jump := jumpIf(ins, regA)
|
||||
i += jump
|
||||
case JumpIfX:
|
||||
jump := jumpIfX(ins, regA, regX)
|
||||
i += jump
|
||||
case LoadAbsolute:
|
||||
regA, ok = loadAbsolute(ins, in)
|
||||
case LoadConstant:
|
||||
regA, regX = loadConstant(ins, regA, regX)
|
||||
case LoadExtension:
|
||||
regA = loadExtension(ins, in)
|
||||
case LoadIndirect:
|
||||
regA, ok = loadIndirect(ins, in, regX)
|
||||
case LoadMemShift:
|
||||
regX, ok = loadMemShift(ins, in)
|
||||
case LoadScratch:
|
||||
regA, regX = loadScratch(ins, regScratch, regA, regX)
|
||||
case RetA:
|
||||
return int(regA), nil
|
||||
case RetConstant:
|
||||
return int(ins.Val), nil
|
||||
case StoreScratch:
|
||||
regScratch = storeScratch(ins, regScratch, regA, regX)
|
||||
case TAX:
|
||||
regX = regA
|
||||
case TXA:
|
||||
regA = regX
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown Instruction at index %d: %T", i, ins)
|
||||
}
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
182
vendor/golang.org/x/net/bpf/vm_instructions.go
generated
vendored
Normal file
182
vendor/golang.org/x/net/bpf/vm_instructions.go
generated
vendored
Normal file
@ -0,0 +1,182 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package bpf
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func aluOpConstant(ins ALUOpConstant, regA uint32) uint32 {
|
||||
return aluOpCommon(ins.Op, regA, ins.Val)
|
||||
}
|
||||
|
||||
func aluOpX(ins ALUOpX, regA uint32, regX uint32) (uint32, bool) {
|
||||
// Guard against division or modulus by zero by terminating
|
||||
// the program, as the OS BPF VM does
|
||||
if regX == 0 {
|
||||
switch ins.Op {
|
||||
case ALUOpDiv, ALUOpMod:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
return aluOpCommon(ins.Op, regA, regX), true
|
||||
}
|
||||
|
||||
func aluOpCommon(op ALUOp, regA uint32, value uint32) uint32 {
|
||||
switch op {
|
||||
case ALUOpAdd:
|
||||
return regA + value
|
||||
case ALUOpSub:
|
||||
return regA - value
|
||||
case ALUOpMul:
|
||||
return regA * value
|
||||
case ALUOpDiv:
|
||||
// Division by zero not permitted by NewVM and aluOpX checks
|
||||
return regA / value
|
||||
case ALUOpOr:
|
||||
return regA | value
|
||||
case ALUOpAnd:
|
||||
return regA & value
|
||||
case ALUOpShiftLeft:
|
||||
return regA << value
|
||||
case ALUOpShiftRight:
|
||||
return regA >> value
|
||||
case ALUOpMod:
|
||||
// Modulus by zero not permitted by NewVM and aluOpX checks
|
||||
return regA % value
|
||||
case ALUOpXor:
|
||||
return regA ^ value
|
||||
default:
|
||||
return regA
|
||||
}
|
||||
}
|
||||
|
||||
func jumpIf(ins JumpIf, regA uint32) int {
|
||||
return jumpIfCommon(ins.Cond, ins.SkipTrue, ins.SkipFalse, regA, ins.Val)
|
||||
}
|
||||
|
||||
func jumpIfX(ins JumpIfX, regA uint32, regX uint32) int {
|
||||
return jumpIfCommon(ins.Cond, ins.SkipTrue, ins.SkipFalse, regA, regX)
|
||||
}
|
||||
|
||||
func jumpIfCommon(cond JumpTest, skipTrue, skipFalse uint8, regA uint32, value uint32) int {
|
||||
var ok bool
|
||||
|
||||
switch cond {
|
||||
case JumpEqual:
|
||||
ok = regA == value
|
||||
case JumpNotEqual:
|
||||
ok = regA != value
|
||||
case JumpGreaterThan:
|
||||
ok = regA > value
|
||||
case JumpLessThan:
|
||||
ok = regA < value
|
||||
case JumpGreaterOrEqual:
|
||||
ok = regA >= value
|
||||
case JumpLessOrEqual:
|
||||
ok = regA <= value
|
||||
case JumpBitsSet:
|
||||
ok = (regA & value) != 0
|
||||
case JumpBitsNotSet:
|
||||
ok = (regA & value) == 0
|
||||
}
|
||||
|
||||
if ok {
|
||||
return int(skipTrue)
|
||||
}
|
||||
|
||||
return int(skipFalse)
|
||||
}
|
||||
|
||||
func loadAbsolute(ins LoadAbsolute, in []byte) (uint32, bool) {
|
||||
offset := int(ins.Off)
|
||||
size := ins.Size
|
||||
|
||||
return loadCommon(in, offset, size)
|
||||
}
|
||||
|
||||
func loadConstant(ins LoadConstant, regA uint32, regX uint32) (uint32, uint32) {
|
||||
switch ins.Dst {
|
||||
case RegA:
|
||||
regA = ins.Val
|
||||
case RegX:
|
||||
regX = ins.Val
|
||||
}
|
||||
|
||||
return regA, regX
|
||||
}
|
||||
|
||||
func loadExtension(ins LoadExtension, in []byte) uint32 {
|
||||
switch ins.Num {
|
||||
case ExtLen:
|
||||
return uint32(len(in))
|
||||
default:
|
||||
panic(fmt.Sprintf("unimplemented extension: %d", ins.Num))
|
||||
}
|
||||
}
|
||||
|
||||
func loadIndirect(ins LoadIndirect, in []byte, regX uint32) (uint32, bool) {
|
||||
offset := int(ins.Off) + int(regX)
|
||||
size := ins.Size
|
||||
|
||||
return loadCommon(in, offset, size)
|
||||
}
|
||||
|
||||
func loadMemShift(ins LoadMemShift, in []byte) (uint32, bool) {
|
||||
offset := int(ins.Off)
|
||||
|
||||
// Size of LoadMemShift is always 1 byte
|
||||
if !inBounds(len(in), offset, 1) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Mask off high 4 bits and multiply low 4 bits by 4
|
||||
return uint32(in[offset]&0x0f) * 4, true
|
||||
}
|
||||
|
||||
func inBounds(inLen int, offset int, size int) bool {
|
||||
return offset+size <= inLen
|
||||
}
|
||||
|
||||
func loadCommon(in []byte, offset int, size int) (uint32, bool) {
|
||||
if !inBounds(len(in), offset, size) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
switch size {
|
||||
case 1:
|
||||
return uint32(in[offset]), true
|
||||
case 2:
|
||||
return uint32(binary.BigEndian.Uint16(in[offset : offset+size])), true
|
||||
case 4:
|
||||
return uint32(binary.BigEndian.Uint32(in[offset : offset+size])), true
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid load size: %d", size))
|
||||
}
|
||||
}
|
||||
|
||||
func loadScratch(ins LoadScratch, regScratch [16]uint32, regA uint32, regX uint32) (uint32, uint32) {
|
||||
switch ins.Dst {
|
||||
case RegA:
|
||||
regA = regScratch[ins.N]
|
||||
case RegX:
|
||||
regX = regScratch[ins.N]
|
||||
}
|
||||
|
||||
return regA, regX
|
||||
}
|
||||
|
||||
func storeScratch(ins StoreScratch, regScratch [16]uint32, regA uint32, regX uint32) [16]uint32 {
|
||||
switch ins.Src {
|
||||
case RegA:
|
||||
regScratch[ins.N] = regA
|
||||
case RegX:
|
||||
regScratch[ins.N] = regX
|
||||
}
|
||||
|
||||
return regScratch
|
||||
}
|
22
vendor/modules.txt
vendored
22
vendor/modules.txt
vendored
@ -678,6 +678,15 @@ github.com/hashicorp/go-multierror
|
||||
# github.com/hashicorp/go-retryablehttp v0.7.4
|
||||
## explicit; go 1.13
|
||||
github.com/hashicorp/go-retryablehttp
|
||||
# github.com/hugelgupf/p9 v0.3.1-0.20230822151754-54f5c5530921
|
||||
## explicit; go 1.20
|
||||
github.com/hugelgupf/p9/fsimpl/localfs
|
||||
github.com/hugelgupf/p9/fsimpl/templatefs
|
||||
github.com/hugelgupf/p9/fsimpl/xattr
|
||||
github.com/hugelgupf/p9/internal
|
||||
github.com/hugelgupf/p9/linux
|
||||
github.com/hugelgupf/p9/p9
|
||||
github.com/hugelgupf/p9/vecnet
|
||||
# github.com/inconshreveable/mousetrap v1.1.0
|
||||
## explicit; go 1.18
|
||||
github.com/inconshreveable/mousetrap
|
||||
@ -720,6 +729,9 @@ github.com/letsencrypt/boulder/goodkey
|
||||
github.com/letsencrypt/boulder/identifier
|
||||
github.com/letsencrypt/boulder/probs
|
||||
github.com/letsencrypt/boulder/revocation
|
||||
# github.com/linuxkit/virtsock v0.0.0-20220523201153-1a23e78aa7a2
|
||||
## explicit; go 1.17
|
||||
github.com/linuxkit/virtsock/pkg/hvsock
|
||||
# github.com/mailru/easyjson v0.7.7
|
||||
## explicit; go 1.12
|
||||
github.com/mailru/easyjson/buffer
|
||||
@ -742,6 +754,12 @@ github.com/mattn/go-shellwords
|
||||
# github.com/mattn/go-sqlite3 v1.14.17
|
||||
## explicit; go 1.16
|
||||
github.com/mattn/go-sqlite3
|
||||
# github.com/mdlayher/socket v0.4.1
|
||||
## explicit; go 1.20
|
||||
github.com/mdlayher/socket
|
||||
# github.com/mdlayher/vsock v1.2.1
|
||||
## explicit; go 1.20
|
||||
github.com/mdlayher/vsock
|
||||
# github.com/miekg/pkcs11 v1.1.1
|
||||
## explicit; go 1.12
|
||||
github.com/miekg/pkcs11
|
||||
@ -993,6 +1011,9 @@ github.com/twitchyliquid64/golang-asm/objabi
|
||||
github.com/twitchyliquid64/golang-asm/src
|
||||
github.com/twitchyliquid64/golang-asm/sys
|
||||
github.com/twitchyliquid64/golang-asm/unsafeheader
|
||||
# github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63
|
||||
## explicit; go 1.16
|
||||
github.com/u-root/uio/ulog
|
||||
# github.com/ugorji/go/codec v1.2.11
|
||||
## explicit; go 1.11
|
||||
github.com/ugorji/go/codec
|
||||
@ -1107,6 +1128,7 @@ golang.org/x/mod/semver
|
||||
golang.org/x/mod/sumdb/note
|
||||
# golang.org/x/net v0.17.0
|
||||
## explicit; go 1.17
|
||||
golang.org/x/net/bpf
|
||||
golang.org/x/net/context
|
||||
golang.org/x/net/html
|
||||
golang.org/x/net/html/atom
|
||||
|
Reference in New Issue
Block a user