From d76a8dc422de2d77c6d0e7162f0b602daccd13e5 Mon Sep 17 00:00:00 2001 From: Kathryn Baldauf Date: Thu, 14 Oct 2021 12:11:23 -0700 Subject: [PATCH 1/3] Add tool to install modules in lcow and plumb through Signed-off-by: Kathryn Baldauf --- Makefile | 3 +- cmd/gcstools/generichook.go | 2 + cmd/gcstools/installdrivers.go | 97 ++++++ cmd/gcstools/main.go | 5 +- go.mod | 1 + go.sum | 2 + internal/devices/assigned_devices.go | 32 -- internal/devices/drivers.go | 39 +++ internal/devices/pnp.go | 74 +++++ internal/guest/runtime/hcsv2/spec.go | 55 ++++ internal/guest/runtime/hcsv2/uvm.go | 2 +- .../guest/runtime/hcsv2/workload_container.go | 57 ---- internal/guest/storage/overlay/overlay.go | 43 ++- .../guest/storage/overlay/overlay_test.go | 6 +- internal/hcsoci/create.go | 8 + internal/hcsoci/devices.go | 27 ++ internal/hcsoci/resources_wcow.go | 30 +- test/go.sum | 3 +- .../github.com/Microsoft/hcsshim/Makefile | 3 +- .../github.com/Microsoft/hcsshim/go.mod | 1 + .../github.com/Microsoft/hcsshim/go.sum | 2 + .../internal/devices/assigned_devices.go | 32 -- .../hcsshim/internal/devices/drivers.go | 39 +++ .../Microsoft/hcsshim/internal/devices/pnp.go | 74 +++++ .../hcsshim/internal/hcsoci/create.go | 8 + .../hcsshim/internal/hcsoci/devices.go | 27 ++ .../hcsshim/internal/hcsoci/resources_wcow.go | 30 +- test/vendor/github.com/google/uuid/null.go | 118 +++++++ test/vendor/github.com/google/uuid/uuid.go | 45 ++- .../vendor/github.com/google/uuid/version4.go | 27 +- test/vendor/modules.txt | 2 +- vendor/github.com/google/uuid/.travis.yml | 9 + vendor/github.com/google/uuid/CONTRIBUTING.md | 10 + vendor/github.com/google/uuid/CONTRIBUTORS | 9 + vendor/github.com/google/uuid/LICENSE | 27 ++ vendor/github.com/google/uuid/README.md | 19 ++ vendor/github.com/google/uuid/dce.go | 80 +++++ vendor/github.com/google/uuid/doc.go | 12 + vendor/github.com/google/uuid/go.mod | 1 + vendor/github.com/google/uuid/hash.go | 53 ++++ vendor/github.com/google/uuid/marshal.go | 38 +++ vendor/github.com/google/uuid/node.go | 90 ++++++ vendor/github.com/google/uuid/node_js.go | 12 + vendor/github.com/google/uuid/node_net.go | 33 ++ vendor/github.com/google/uuid/null.go | 118 +++++++ vendor/github.com/google/uuid/sql.go | 59 ++++ vendor/github.com/google/uuid/time.go | 123 ++++++++ vendor/github.com/google/uuid/util.go | 43 +++ vendor/github.com/google/uuid/uuid.go | 294 ++++++++++++++++++ vendor/github.com/google/uuid/version1.go | 44 +++ vendor/github.com/google/uuid/version4.go | 76 +++++ vendor/modules.txt | 2 + 52 files changed, 1861 insertions(+), 185 deletions(-) create mode 100644 cmd/gcstools/installdrivers.go create mode 100644 test/vendor/github.com/google/uuid/null.go create mode 100644 vendor/github.com/google/uuid/.travis.yml create mode 100644 vendor/github.com/google/uuid/CONTRIBUTING.md create mode 100644 vendor/github.com/google/uuid/CONTRIBUTORS create mode 100644 vendor/github.com/google/uuid/LICENSE create mode 100644 vendor/github.com/google/uuid/README.md create mode 100644 vendor/github.com/google/uuid/dce.go create mode 100644 vendor/github.com/google/uuid/doc.go create mode 100644 vendor/github.com/google/uuid/go.mod create mode 100644 vendor/github.com/google/uuid/hash.go create mode 100644 vendor/github.com/google/uuid/marshal.go create mode 100644 vendor/github.com/google/uuid/node.go create mode 100644 vendor/github.com/google/uuid/node_js.go create mode 100644 vendor/github.com/google/uuid/node_net.go create mode 100644 vendor/github.com/google/uuid/null.go create mode 100644 vendor/github.com/google/uuid/sql.go create mode 100644 vendor/github.com/google/uuid/time.go create mode 100644 vendor/github.com/google/uuid/util.go create mode 100644 vendor/github.com/google/uuid/uuid.go create mode 100644 vendor/github.com/google/uuid/version1.go create mode 100644 vendor/github.com/google/uuid/version4.go diff --git a/Makefile b/Makefile index a8f5516cd0..ac00def635 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,8 @@ SRCROOT=$(dir $(abspath $(firstword $(MAKEFILE_LIST)))) # The link aliases for gcstools GCS_TOOLS=\ - generichook + generichook \ + install-drivers .PHONY: all always rootfs test diff --git a/cmd/gcstools/generichook.go b/cmd/gcstools/generichook.go index 0ffa57d6b2..f29145642d 100644 --- a/cmd/gcstools/generichook.go +++ b/cmd/gcstools/generichook.go @@ -1,3 +1,5 @@ +// +build linux + package main import ( diff --git a/cmd/gcstools/installdrivers.go b/cmd/gcstools/installdrivers.go new file mode 100644 index 0000000000..f14953c8ea --- /dev/null +++ b/cmd/gcstools/installdrivers.go @@ -0,0 +1,97 @@ +// +build linux + +package main + +import ( + "context" + "fmt" + "io/fs" + "os" + "os/exec" + "path/filepath" + "strings" + + "github.com/Microsoft/hcsshim/internal/guest/storage/overlay" + "github.com/google/uuid" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const ( + lcowGlobalDriversFormat = "/run/drivers/%s" + + moduleExtension = ".ko" +) + +func install(ctx context.Context) error { + args := []string(os.Args[1:]) + + if len(args) == 0 { + return errors.New("no driver paths provided for install") + } + + for _, driver := range args { + modules := []string{} + + driverGUID, err := uuid.NewRandom() + if err != nil { + return err + } + + // create an overlay mount from the driver's UVM path so we can write to the + // mount path in the UVM despite having mounted in the driver originally as + // readonly + runDriverPath := fmt.Sprintf(lcowGlobalDriversFormat, driverGUID.String()) + upperPath := filepath.Join(runDriverPath, "upper") + workPath := filepath.Join(runDriverPath, "work") + rootPath := filepath.Join(runDriverPath, "content") + if err := overlay.Mount(ctx, []string{driver}, upperPath, workPath, rootPath, false); err != nil { + return err + } + + // find all module files, which end with ".ko" extension, and remove extension + // for use when calling `modprobe` below. + if walkErr := filepath.WalkDir(rootPath, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return errors.Wrap(err, "failed to read directory while walking dir") + } + if !d.IsDir() && filepath.Ext(d.Name()) == moduleExtension { + moduleName := strings.TrimSuffix(d.Name(), moduleExtension) + modules = append(modules, moduleName) + } + return nil + }); walkErr != nil { + return walkErr + } + + // create a new module dependency map database for the driver + depmodArgs := []string{"-b", rootPath} + cmd := exec.Command("depmod", depmodArgs...) + out, err := cmd.CombinedOutput() + if err != nil { + return errors.Wrapf(err, "failed to run depmod with args %v: %s", depmodArgs, out) + } + + // run modprobe for every module name found + modprobeArgs := append([]string{"-d", rootPath, "-a"}, modules...) + cmd = exec.Command( + "modprobe", + modprobeArgs..., + ) + + out, err = cmd.CombinedOutput() + if err != nil { + return errors.Wrapf(err, "failed to run modporbe with args %v: %s", modprobeArgs, out) + } + } + + return nil +} + +func installDriversMain() { + ctx := context.Background() + logrus.SetOutput(os.Stderr) + if err := install(ctx); err != nil { + logrus.Fatalf("error in install drivers: %s", err) + } +} diff --git a/cmd/gcstools/main.go b/cmd/gcstools/main.go index bf88c764f0..5c2963b5d2 100644 --- a/cmd/gcstools/main.go +++ b/cmd/gcstools/main.go @@ -1,3 +1,5 @@ +// +build linux + package main import ( @@ -7,7 +9,8 @@ import ( ) var commands = map[string]func(){ - "generichook": genericHookMain, + "generichook": genericHookMain, + "install-drivers": installDriversMain, } func main() { diff --git a/go.mod b/go.mod index 4d26c7c362..2cacad5219 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.5.6 github.com/google/go-containerregistry v0.5.1 + github.com/google/uuid v1.3.0 github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3 github.com/mattn/go-shellwords v1.0.6 github.com/opencontainers/runc v1.0.2 diff --git a/go.sum b/go.sum index 572931b3bb..c19a00fc9a 100644 --- a/go.sum +++ b/go.sum @@ -363,6 +363,8 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= diff --git a/internal/devices/assigned_devices.go b/internal/devices/assigned_devices.go index d7361ddaa7..3f4f3c27b2 100644 --- a/internal/devices/assigned_devices.go +++ b/internal/devices/assigned_devices.go @@ -5,9 +5,6 @@ package devices import ( "context" "fmt" - "io/ioutil" - "net" - "strings" "github.com/Microsoft/hcsshim/internal/cmd" "github.com/Microsoft/hcsshim/internal/log" @@ -107,32 +104,3 @@ func createDeviceUtilChildrenCommand(deviceUtilPath string, vmBusInstanceID stri args := []string{deviceUtilPath, "children", parentIDsFlag, "--property=location"} return args } - -// readCsPipeOutput is a helper function that connects to a listener and reads -// the connection's comma separated output until done. resulting comma separated -// values are returned in the `result` param. The `errChan` param is used to -// propagate an errors to the calling function. -func readCsPipeOutput(l net.Listener, errChan chan<- error, result *[]string) { - defer close(errChan) - c, err := l.Accept() - if err != nil { - errChan <- errors.Wrapf(err, "failed to accept named pipe") - return - } - bytes, err := ioutil.ReadAll(c) - if err != nil { - errChan <- err - return - } - - elementsAsString := strings.TrimSuffix(string(bytes), "\n") - elements := strings.Split(elementsAsString, ",") - *result = append(*result, elements...) - - if len(*result) == 0 { - errChan <- errors.Wrapf(err, "failed to get any pipe output") - return - } - - errChan <- nil -} diff --git a/internal/devices/drivers.go b/internal/devices/drivers.go index 8b21cbc347..f02056d940 100644 --- a/internal/devices/drivers.go +++ b/internal/devices/drivers.go @@ -38,3 +38,42 @@ func InstallWindowsDriver(ctx context.Context, vm *uvm.UtilityVM, driver string) } return closer, execPnPInstallDriver(ctx, vm, uvmPath) } + +// InstallKernelDriver mounts a specified kernel driver, then installs it in the UVM. +// +// `driver` is a directory path on the host that contains driver files for standard installation. +// For windows this means files for pnp installation (.inf, .cat, .sys, .cert files). +// For linux this means a vhd file that contains the drivers under /lib/modules/`uname -r` for use +// with depmod and modprobe. +// +// Returns a ResourceCloser for the added mount. On failure, the mounted share will be released, +// the returned ResourceCloser will be nil, and an error will be returned. +func InstallKernelDriver(ctx context.Context, vm *uvm.UtilityVM, driver string) (closer resources.ResourceCloser, err error) { + defer func() { + if err != nil && closer != nil { + // best effort clean up allocated resource on failure + if releaseErr := closer.Release(ctx); releaseErr != nil { + log.G(ctx).WithError(releaseErr).Error("failed to release container resource") + } + closer = nil + } + }() + if vm.OS() == "windows" { + options := vm.DefaultVSMBOptions(true) + closer, err = vm.AddVSMB(ctx, driver, options) + if err != nil { + return closer, fmt.Errorf("failed to add VSMB share to utility VM for path %+v: %s", driver, err) + } + uvmPath, err := vm.GetVSMBUvmPath(ctx, driver, true) + if err != nil { + return closer, err + } + return closer, execPnPInstallDriver(ctx, vm, uvmPath) + } + uvmPathForShare := fmt.Sprintf(uvm.LCOWGlobalMountPrefix, vm.UVMMountCounter()) + scsiCloser, err := vm.AddSCSI(ctx, driver, uvmPathForShare, true, false, []string{}, uvm.VMAccessTypeIndividual) + if err != nil { + return closer, fmt.Errorf("failed to add SCSI disk to utility VM for path %+v: %s", driver, err) + } + return scsiCloser, execModprobeInstallDriver(ctx, vm, uvmPathForShare) +} diff --git a/internal/devices/pnp.go b/internal/devices/pnp.go index 1bad6ab685..96108feda5 100644 --- a/internal/devices/pnp.go +++ b/internal/devices/pnp.go @@ -5,6 +5,9 @@ package devices import ( "context" "fmt" + "io/ioutil" + "net" + "strings" "github.com/Microsoft/hcsshim/internal/cmd" "github.com/Microsoft/hcsshim/internal/log" @@ -22,6 +25,8 @@ const ( is an expected race and can be ignored.` ) +var noExecOutputErr = errors.New("failed to get any pipe output") + // createPnPInstallDriverCommand creates a pnputil command to add and install drivers // present in `driverUVMPath` and all subdirectories. func createPnPInstallDriverCommand(driverUVMPath string) []string { @@ -61,3 +66,72 @@ func execPnPInstallDriver(ctx context.Context, vm *uvm.UtilityVM, driverDir stri log.G(ctx).WithField("added drivers", driverDir).Debug("installed drivers") return nil } + +func execModprobeInstallDriver(ctx context.Context, vm *uvm.UtilityVM, driverDir string) error { + p, l, err := cmd.CreateNamedPipeListener() + if err != nil { + return err + } + defer l.Close() + + var pipeResults []string + errChan := make(chan error) + + go readCsPipeOutput(l, errChan, &pipeResults) + + args := []string{ + "/bin/install-drivers", + driverDir, + } + req := &cmd.CmdProcessRequest{ + Args: args, + Stderr: p, + } + + exitCode, err := cmd.ExecInUvm(ctx, vm, req) + if err != nil && err != noExecOutputErr { + return errors.Wrapf(err, "failed to install driver %s in uvm with exit code %d", driverDir, exitCode) + } + + // wait to finish parsing stdout results + select { + case err := <-errChan: + if err != nil { + return err + } + case <-ctx.Done(): + return ctx.Err() + } + + log.G(ctx).WithField("added drivers", driverDir).Debug("installed drivers") + return nil +} + +// readCsPipeOutput is a helper function that connects to a listener and reads +// the connection's comma separated output until done. resulting comma separated +// values are returned in the `result` param. The `errChan` param is used to +// propagate an errors to the calling function. +func readCsPipeOutput(l net.Listener, errChan chan<- error, result *[]string) { + defer close(errChan) + c, err := l.Accept() + if err != nil { + errChan <- errors.Wrapf(err, "failed to accept named pipe") + return + } + bytes, err := ioutil.ReadAll(c) + if err != nil { + errChan <- err + return + } + + elementsAsString := strings.TrimSuffix(string(bytes), "\n") + elements := strings.Split(elementsAsString, ",") + *result = append(*result, elements...) + + if len(*result) == 0 { + errChan <- noExecOutputErr + return + } + + errChan <- nil +} diff --git a/internal/guest/runtime/hcsv2/spec.go b/internal/guest/runtime/hcsv2/spec.go index d53fa576ba..60d6300f6e 100644 --- a/internal/guest/runtime/hcsv2/spec.go +++ b/internal/guest/runtime/hcsv2/spec.go @@ -254,3 +254,58 @@ func applyAnnotationsToSpec(ctx context.Context, spec *oci.Spec) error { return nil } + +// Helper function to create an oci prestart hook to run ldconfig +func addLDConfigHook(ctx context.Context, spec *oci.Spec, args, env []string) error { + if spec.Hooks == nil { + spec.Hooks = &oci.Hooks{} + } + + ldConfigHook := oci.Hook{ + Path: "/sbin/ldconfig", + Args: args, + Env: env, + } + + spec.Hooks.Prestart = append(spec.Hooks.Prestart, ldConfigHook) + return nil +} + +func addLinuxDeviceToSpec(ctx context.Context, hostDevice *devices.Device, spec *oci.Spec, addCgroupDevice bool) { + rd := oci.LinuxDevice{ + Path: hostDevice.Path, + Type: string(hostDevice.Type), + Major: hostDevice.Major, + Minor: hostDevice.Minor, + UID: &hostDevice.Uid, + GID: &hostDevice.Gid, + } + if hostDevice.Major == 0 && hostDevice.Minor == 0 { + // Invalid device, most likely a symbolic link, skip it. + return + } + found := false + for i, dev := range spec.Linux.Devices { + if dev.Path == rd.Path { + found = true + spec.Linux.Devices[i] = rd + break + } + if dev.Type == rd.Type && dev.Major == rd.Major && dev.Minor == rd.Minor { + log.G(ctx).Warnf("The same type '%s', major '%d' and minor '%d', should not be used for multiple devices.", dev.Type, dev.Major, dev.Minor) + } + } + if !found { + spec.Linux.Devices = append(spec.Linux.Devices, rd) + if addCgroupDevice { + deviceCgroup := oci.LinuxDeviceCgroup{ + Allow: true, + Type: string(hostDevice.Type), + Major: &hostDevice.Major, + Minor: &hostDevice.Minor, + Access: string(hostDevice.Permissions), + } + spec.Linux.Resources.Devices = append(spec.Linux.Resources.Devices, deviceCgroup) + } + } +} diff --git a/internal/guest/runtime/hcsv2/uvm.go b/internal/guest/runtime/hcsv2/uvm.go index d58d986057..9b2f2a1924 100644 --- a/internal/guest/runtime/hcsv2/uvm.go +++ b/internal/guest/runtime/hcsv2/uvm.go @@ -498,7 +498,7 @@ func modifyCombinedLayers(ctx context.Context, rt prot.ModifyRequestType, cl *pr workdirPath = filepath.Join(cl.ScratchPath, "work") } - return overlay.Mount(ctx, layerPaths, upperdirPath, workdirPath, cl.ContainerRootPath, readonly, cl.ContainerId, securityPolicy) + return overlay.MountLayer(ctx, layerPaths, upperdirPath, workdirPath, cl.ContainerRootPath, readonly, cl.ContainerId, securityPolicy) case prot.MreqtRemove: return storage.UnmountPath(ctx, cl.ContainerRootPath, true) default: diff --git a/internal/guest/runtime/hcsv2/workload_container.go b/internal/guest/runtime/hcsv2/workload_container.go index 913552fc3d..fcdbb7af17 100644 --- a/internal/guest/runtime/hcsv2/workload_container.go +++ b/internal/guest/runtime/hcsv2/workload_container.go @@ -8,10 +8,8 @@ import ( "path/filepath" "strings" - "github.com/Microsoft/hcsshim/internal/log" "github.com/Microsoft/hcsshim/internal/oc" "github.com/Microsoft/hcsshim/pkg/annotations" - "github.com/opencontainers/runc/libcontainer/devices" oci "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "go.opencensus.io/trace" @@ -198,58 +196,3 @@ func setupWorkloadContainerSpec(ctx context.Context, sbid, id string, spec *oci. return nil } - -// Helper function to create an oci prestart hook to run ldconfig -func addLDConfigHook(ctx context.Context, spec *oci.Spec, args, env []string) error { - if spec.Hooks == nil { - spec.Hooks = &oci.Hooks{} - } - - ldConfigHook := oci.Hook{ - Path: "/sbin/ldconfig", - Args: args, - Env: env, - } - - spec.Hooks.Prestart = append(spec.Hooks.Prestart, ldConfigHook) - return nil -} - -func addLinuxDeviceToSpec(ctx context.Context, hostDevice *devices.Device, spec *oci.Spec, addCgroupDevice bool) { - rd := oci.LinuxDevice{ - Path: hostDevice.Path, - Type: string(hostDevice.Type), - Major: hostDevice.Major, - Minor: hostDevice.Minor, - UID: &hostDevice.Uid, - GID: &hostDevice.Gid, - } - if hostDevice.Major == 0 && hostDevice.Minor == 0 { - // Invalid device, most likely a symbolic link, skip it. - return - } - found := false - for i, dev := range spec.Linux.Devices { - if dev.Path == rd.Path { - found = true - spec.Linux.Devices[i] = rd - break - } - if dev.Type == rd.Type && dev.Major == rd.Major && dev.Minor == rd.Minor { - log.G(ctx).Warnf("The same type '%s', major '%d' and minor '%d', should not be used for multiple devices.", dev.Type, dev.Major, dev.Minor) - } - } - if !found { - spec.Linux.Devices = append(spec.Linux.Devices, rd) - if addCgroupDevice { - deviceCgroup := oci.LinuxDeviceCgroup{ - Allow: true, - Type: string(hostDevice.Type), - Major: &hostDevice.Major, - Minor: &hostDevice.Minor, - Access: string(hostDevice.Permissions), - } - spec.Linux.Resources.Devices = append(spec.Linux.Resources.Devices, deviceCgroup) - } - } -} diff --git a/internal/guest/storage/overlay/overlay.go b/internal/guest/storage/overlay/overlay.go index 696ff09565..8c2de80268 100644 --- a/internal/guest/storage/overlay/overlay.go +++ b/internal/guest/storage/overlay/overlay.go @@ -21,7 +21,20 @@ var ( unixMount = unix.Mount ) -// Mount creates an overlay mount with `layerPaths` at `rootfsPath`. +// MountLayer first enforces the security policy for the container's layer paths +// and then calls Mount to mount the layer paths as an overlayfs +func MountLayer(ctx context.Context, layerPaths []string, upperdirPath, workdirPath, rootfsPath string, readonly bool, containerId string, securityPolicy securitypolicy.SecurityPolicyEnforcer) (err error) { + _, span := trace.StartSpan(ctx, "overlay::MountLayer") + defer span.End() + defer func() { oc.SetSpanStatus(span, err) }() + + if err := securityPolicy.EnforceOverlayMountPolicy(containerId, layerPaths); err != nil { + return err + } + return Mount(ctx, layerPaths, upperdirPath, workdirPath, rootfsPath, readonly) +} + +// Mount creates an overlay mount with `basePaths` at `target`. // // If `upperdirPath != ""` the path will be created. On mount failure the // created `upperdirPath` will be automatically cleaned up. @@ -29,27 +42,23 @@ var ( // If `workdirPath != ""` the path will be created. On mount failure the created // `workdirPath` will be automatically cleaned up. // -// Always creates `rootfsPath`. On mount failure the created `rootfsPath` will +// Always creates `target`. On mount failure the created `target` will // be automatically cleaned up. -func Mount(ctx context.Context, layerPaths []string, upperdirPath, workdirPath, rootfsPath string, readonly bool, containerId string, securityPolicy securitypolicy.SecurityPolicyEnforcer) (err error) { +func Mount(ctx context.Context, basePaths []string, upperdirPath, workdirPath, target string, readonly bool) (err error) { _, span := trace.StartSpan(ctx, "overlay::Mount") defer span.End() defer func() { oc.SetSpanStatus(span, err) }() - if err := securityPolicy.EnforceOverlayMountPolicy(containerId, layerPaths); err != nil { - return err - } - - lowerdir := strings.Join(layerPaths, ":") + lowerdir := strings.Join(basePaths, ":") span.AddAttributes( - trace.StringAttribute("layerPaths", lowerdir), + trace.StringAttribute("lowerdir", lowerdir), trace.StringAttribute("upperdirPath", upperdirPath), trace.StringAttribute("workdirPath", workdirPath), - trace.StringAttribute("rootfsPath", rootfsPath), + trace.StringAttribute("target", target), trace.BoolAttribute("readonly", readonly)) - if rootfsPath == "" { - return errors.New("cannot have empty rootfsPath") + if target == "" { + return errors.New("cannot have empty target") } if readonly && (upperdirPath != "" || workdirPath != "") { @@ -79,20 +88,20 @@ func Mount(ctx context.Context, layerPaths []string, upperdirPath, workdirPath, }() options = append(options, "workdir="+workdirPath) } - if err := osMkdirAll(rootfsPath, 0755); err != nil { - return errors.Wrapf(err, "failed to create directory for container root filesystem %s", rootfsPath) + if err := osMkdirAll(target, 0755); err != nil { + return errors.Wrapf(err, "failed to create directory for container root filesystem %s", target) } defer func() { if err != nil { - osRemoveAll(rootfsPath) + osRemoveAll(target) } }() var flags uintptr if readonly { flags |= unix.MS_RDONLY } - if err := unixMount("overlay", rootfsPath, "overlay", flags, strings.Join(options, ",")); err != nil { - return errors.Wrapf(err, "failed to mount container root filesystem using overlayfs %s", rootfsPath) + if err := unixMount("overlay", target, "overlay", flags, strings.Join(options, ",")); err != nil { + return errors.Wrapf(err, "failed to mount overlayfs at %s", target) } return nil } diff --git a/internal/guest/storage/overlay/overlay_test.go b/internal/guest/storage/overlay/overlay_test.go index 38a6d8c180..828ee2a955 100644 --- a/internal/guest/storage/overlay/overlay_test.go +++ b/internal/guest/storage/overlay/overlay_test.go @@ -83,7 +83,7 @@ func Test_Mount_Success(t *testing.T) { return nil } - err := Mount(context.Background(), []string{"/layer1", "/layer2"}, "/upper", "/work", "/root", false, fakeContainerId, openDoorSecurityPolicyEnforcer()) + err := MountLayer(context.Background(), []string{"/layer1", "/layer2"}, "/upper", "/work", "/root", false, fakeContainerId, openDoorSecurityPolicyEnforcer()) if err != nil { t.Fatalf("expected no error got: %v", err) } @@ -127,7 +127,7 @@ func Test_Mount_Readonly_Success(t *testing.T) { return nil } - err := Mount(context.Background(), []string{"/layer1", "/layer2"}, "", "", "/root", false, fakeContainerId, openDoorSecurityPolicyEnforcer()) + err := MountLayer(context.Background(), []string{"/layer1", "/layer2"}, "", "", "/root", false, fakeContainerId, openDoorSecurityPolicyEnforcer()) if err != nil { t.Fatalf("expected no error got: %v", err) } @@ -178,7 +178,7 @@ func Test_Security_Policy_Enforcement(t *testing.T) { } enforcer := mountMonitoringSecurityPolicyEnforcer() - err := Mount(context.Background(), []string{"/layer1", "/layer2"}, "/upper", "/work", "/root", false, fakeContainerId, enforcer) + err := MountLayer(context.Background(), []string{"/layer1", "/layer2"}, "/upper", "/work", "/root", false, fakeContainerId, enforcer) if err != nil { t.Fatalf("expected no error got: %v", err) } diff --git a/internal/hcsoci/create.go b/internal/hcsoci/create.go index ae1dbdeab2..5f80fd2f26 100644 --- a/internal/hcsoci/create.go +++ b/internal/hcsoci/create.go @@ -284,6 +284,14 @@ func CreateContainer(ctx context.Context, createOptions *CreateOptions) (_ cow.C n := coi.HostingSystem.ContainerCounter() r.SetContainerRootInUVM(fmt.Sprintf(wcowRootInUVM, strconv.FormatUint(n, 16))) } + // install kernel drivers if necessary. + // do this before network setup in case any of the drivers requested are + // network drivers + driverClosers, err := installPodDrivers(ctx, coi.HostingSystem, coi.Spec.Annotations) + if err != nil { + return nil, r, err + } + r.Add(driverClosers...) } ct, _, err := oci.GetSandboxTypeAndID(coi.Spec.Annotations) diff --git a/internal/hcsoci/devices.go b/internal/hcsoci/devices.go index cccac4397b..19562cc32f 100644 --- a/internal/hcsoci/devices.go +++ b/internal/hcsoci/devices.go @@ -144,6 +144,33 @@ func handleAssignedDevicesWindows(ctx context.Context, vm *uvm.UtilityVM, annota return resultDevs, closers, nil } +func installPodDrivers(ctx context.Context, vm *uvm.UtilityVM, annotations map[string]string) (closers []resources.ResourceCloser, err error) { + defer func() { + if err != nil { + // best effort clean up allocated resources on failure + for _, r := range closers { + if releaseErr := r.Release(ctx); releaseErr != nil { + log.G(ctx).WithError(releaseErr).Error("failed to release container resource") + } + } + } + }() + + // get the spec specified kernel drivers and install them on the UVM + drivers, err := getSpecKernelDrivers(annotations) + if err != nil { + return closers, err + } + for _, d := range drivers { + driverCloser, err := devices.InstallKernelDriver(ctx, vm, d) + if err != nil { + return closers, err + } + closers = append(closers, driverCloser) + } + return closers, err +} + func getDeviceInfoFromPath(rawDevicePath string) (string, uint16) { indexString := filepath.Base(rawDevicePath) index, err := strconv.ParseUint(indexString, 10, 16) diff --git a/internal/hcsoci/resources_wcow.go b/internal/hcsoci/resources_wcow.go index 0a2fef6fc8..d7ce96e2a7 100644 --- a/internal/hcsoci/resources_wcow.go +++ b/internal/hcsoci/resources_wcow.go @@ -14,7 +14,6 @@ import ( "github.com/Microsoft/hcsshim/internal/cmd" "github.com/Microsoft/hcsshim/internal/credentials" - "github.com/Microsoft/hcsshim/internal/devices" "github.com/Microsoft/hcsshim/internal/layers" "github.com/Microsoft/hcsshim/internal/log" "github.com/Microsoft/hcsshim/internal/resources" @@ -98,28 +97,23 @@ func allocateWindowsResources(ctx context.Context, coi *createOptionsInternal, r } } - if coi.HostingSystem != nil && coi.hasWindowsAssignedDevices() { - windowsDevices, closers, err := handleAssignedDevicesWindows(ctx, coi.HostingSystem, coi.Spec.Annotations, coi.Spec.Windows.Devices) - if err != nil { - return err - } - r.Add(closers...) - coi.Spec.Windows.Devices = windowsDevices - } - if coi.HostingSystem != nil { - // get the spec specified kernel drivers and install them on the UVM - drivers, err := getSpecKernelDrivers(coi.Spec.Annotations) - if err != nil { - return err - } - for _, d := range drivers { - driverCloser, err := devices.InstallWindowsDriver(ctx, coi.HostingSystem, d) + if coi.hasWindowsAssignedDevices() { + windowsDevices, closers, err := handleAssignedDevicesWindows(ctx, coi.HostingSystem, coi.Spec.Annotations, coi.Spec.Windows.Devices) if err != nil { return err } - r.Add(driverCloser) + r.Add(closers...) + coi.Spec.Windows.Devices = windowsDevices + } + // when driver installation completes, we are guaranteed that the device is ready for use, + // so reinstall drivers to make sure the devices are ready when we proceed. + // TODO katiewasnothere: we should find a way to avoid reinstalling drivers + driverClosers, err := installPodDrivers(ctx, coi.HostingSystem, coi.Spec.Annotations) + if err != nil { + return err } + r.Add(driverClosers...) } return nil diff --git a/test/go.sum b/test/go.sum index cb4394507a..4d136e2dd7 100644 --- a/test/go.sum +++ b/test/go.sum @@ -332,8 +332,9 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= diff --git a/test/vendor/github.com/Microsoft/hcsshim/Makefile b/test/vendor/github.com/Microsoft/hcsshim/Makefile index a8f5516cd0..ac00def635 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/Makefile +++ b/test/vendor/github.com/Microsoft/hcsshim/Makefile @@ -18,7 +18,8 @@ SRCROOT=$(dir $(abspath $(firstword $(MAKEFILE_LIST)))) # The link aliases for gcstools GCS_TOOLS=\ - generichook + generichook \ + install-drivers .PHONY: all always rootfs test diff --git a/test/vendor/github.com/Microsoft/hcsshim/go.mod b/test/vendor/github.com/Microsoft/hcsshim/go.mod index 4d26c7c362..2cacad5219 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/go.mod +++ b/test/vendor/github.com/Microsoft/hcsshim/go.mod @@ -15,6 +15,7 @@ require ( github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.5.6 github.com/google/go-containerregistry v0.5.1 + github.com/google/uuid v1.3.0 github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3 github.com/mattn/go-shellwords v1.0.6 github.com/opencontainers/runc v1.0.2 diff --git a/test/vendor/github.com/Microsoft/hcsshim/go.sum b/test/vendor/github.com/Microsoft/hcsshim/go.sum index 572931b3bb..c19a00fc9a 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/go.sum +++ b/test/vendor/github.com/Microsoft/hcsshim/go.sum @@ -363,6 +363,8 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= diff --git a/test/vendor/github.com/Microsoft/hcsshim/internal/devices/assigned_devices.go b/test/vendor/github.com/Microsoft/hcsshim/internal/devices/assigned_devices.go index d7361ddaa7..3f4f3c27b2 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/internal/devices/assigned_devices.go +++ b/test/vendor/github.com/Microsoft/hcsshim/internal/devices/assigned_devices.go @@ -5,9 +5,6 @@ package devices import ( "context" "fmt" - "io/ioutil" - "net" - "strings" "github.com/Microsoft/hcsshim/internal/cmd" "github.com/Microsoft/hcsshim/internal/log" @@ -107,32 +104,3 @@ func createDeviceUtilChildrenCommand(deviceUtilPath string, vmBusInstanceID stri args := []string{deviceUtilPath, "children", parentIDsFlag, "--property=location"} return args } - -// readCsPipeOutput is a helper function that connects to a listener and reads -// the connection's comma separated output until done. resulting comma separated -// values are returned in the `result` param. The `errChan` param is used to -// propagate an errors to the calling function. -func readCsPipeOutput(l net.Listener, errChan chan<- error, result *[]string) { - defer close(errChan) - c, err := l.Accept() - if err != nil { - errChan <- errors.Wrapf(err, "failed to accept named pipe") - return - } - bytes, err := ioutil.ReadAll(c) - if err != nil { - errChan <- err - return - } - - elementsAsString := strings.TrimSuffix(string(bytes), "\n") - elements := strings.Split(elementsAsString, ",") - *result = append(*result, elements...) - - if len(*result) == 0 { - errChan <- errors.Wrapf(err, "failed to get any pipe output") - return - } - - errChan <- nil -} diff --git a/test/vendor/github.com/Microsoft/hcsshim/internal/devices/drivers.go b/test/vendor/github.com/Microsoft/hcsshim/internal/devices/drivers.go index 8b21cbc347..f02056d940 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/internal/devices/drivers.go +++ b/test/vendor/github.com/Microsoft/hcsshim/internal/devices/drivers.go @@ -38,3 +38,42 @@ func InstallWindowsDriver(ctx context.Context, vm *uvm.UtilityVM, driver string) } return closer, execPnPInstallDriver(ctx, vm, uvmPath) } + +// InstallKernelDriver mounts a specified kernel driver, then installs it in the UVM. +// +// `driver` is a directory path on the host that contains driver files for standard installation. +// For windows this means files for pnp installation (.inf, .cat, .sys, .cert files). +// For linux this means a vhd file that contains the drivers under /lib/modules/`uname -r` for use +// with depmod and modprobe. +// +// Returns a ResourceCloser for the added mount. On failure, the mounted share will be released, +// the returned ResourceCloser will be nil, and an error will be returned. +func InstallKernelDriver(ctx context.Context, vm *uvm.UtilityVM, driver string) (closer resources.ResourceCloser, err error) { + defer func() { + if err != nil && closer != nil { + // best effort clean up allocated resource on failure + if releaseErr := closer.Release(ctx); releaseErr != nil { + log.G(ctx).WithError(releaseErr).Error("failed to release container resource") + } + closer = nil + } + }() + if vm.OS() == "windows" { + options := vm.DefaultVSMBOptions(true) + closer, err = vm.AddVSMB(ctx, driver, options) + if err != nil { + return closer, fmt.Errorf("failed to add VSMB share to utility VM for path %+v: %s", driver, err) + } + uvmPath, err := vm.GetVSMBUvmPath(ctx, driver, true) + if err != nil { + return closer, err + } + return closer, execPnPInstallDriver(ctx, vm, uvmPath) + } + uvmPathForShare := fmt.Sprintf(uvm.LCOWGlobalMountPrefix, vm.UVMMountCounter()) + scsiCloser, err := vm.AddSCSI(ctx, driver, uvmPathForShare, true, false, []string{}, uvm.VMAccessTypeIndividual) + if err != nil { + return closer, fmt.Errorf("failed to add SCSI disk to utility VM for path %+v: %s", driver, err) + } + return scsiCloser, execModprobeInstallDriver(ctx, vm, uvmPathForShare) +} diff --git a/test/vendor/github.com/Microsoft/hcsshim/internal/devices/pnp.go b/test/vendor/github.com/Microsoft/hcsshim/internal/devices/pnp.go index 1bad6ab685..96108feda5 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/internal/devices/pnp.go +++ b/test/vendor/github.com/Microsoft/hcsshim/internal/devices/pnp.go @@ -5,6 +5,9 @@ package devices import ( "context" "fmt" + "io/ioutil" + "net" + "strings" "github.com/Microsoft/hcsshim/internal/cmd" "github.com/Microsoft/hcsshim/internal/log" @@ -22,6 +25,8 @@ const ( is an expected race and can be ignored.` ) +var noExecOutputErr = errors.New("failed to get any pipe output") + // createPnPInstallDriverCommand creates a pnputil command to add and install drivers // present in `driverUVMPath` and all subdirectories. func createPnPInstallDriverCommand(driverUVMPath string) []string { @@ -61,3 +66,72 @@ func execPnPInstallDriver(ctx context.Context, vm *uvm.UtilityVM, driverDir stri log.G(ctx).WithField("added drivers", driverDir).Debug("installed drivers") return nil } + +func execModprobeInstallDriver(ctx context.Context, vm *uvm.UtilityVM, driverDir string) error { + p, l, err := cmd.CreateNamedPipeListener() + if err != nil { + return err + } + defer l.Close() + + var pipeResults []string + errChan := make(chan error) + + go readCsPipeOutput(l, errChan, &pipeResults) + + args := []string{ + "/bin/install-drivers", + driverDir, + } + req := &cmd.CmdProcessRequest{ + Args: args, + Stderr: p, + } + + exitCode, err := cmd.ExecInUvm(ctx, vm, req) + if err != nil && err != noExecOutputErr { + return errors.Wrapf(err, "failed to install driver %s in uvm with exit code %d", driverDir, exitCode) + } + + // wait to finish parsing stdout results + select { + case err := <-errChan: + if err != nil { + return err + } + case <-ctx.Done(): + return ctx.Err() + } + + log.G(ctx).WithField("added drivers", driverDir).Debug("installed drivers") + return nil +} + +// readCsPipeOutput is a helper function that connects to a listener and reads +// the connection's comma separated output until done. resulting comma separated +// values are returned in the `result` param. The `errChan` param is used to +// propagate an errors to the calling function. +func readCsPipeOutput(l net.Listener, errChan chan<- error, result *[]string) { + defer close(errChan) + c, err := l.Accept() + if err != nil { + errChan <- errors.Wrapf(err, "failed to accept named pipe") + return + } + bytes, err := ioutil.ReadAll(c) + if err != nil { + errChan <- err + return + } + + elementsAsString := strings.TrimSuffix(string(bytes), "\n") + elements := strings.Split(elementsAsString, ",") + *result = append(*result, elements...) + + if len(*result) == 0 { + errChan <- noExecOutputErr + return + } + + errChan <- nil +} diff --git a/test/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/create.go b/test/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/create.go index ae1dbdeab2..5f80fd2f26 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/create.go +++ b/test/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/create.go @@ -284,6 +284,14 @@ func CreateContainer(ctx context.Context, createOptions *CreateOptions) (_ cow.C n := coi.HostingSystem.ContainerCounter() r.SetContainerRootInUVM(fmt.Sprintf(wcowRootInUVM, strconv.FormatUint(n, 16))) } + // install kernel drivers if necessary. + // do this before network setup in case any of the drivers requested are + // network drivers + driverClosers, err := installPodDrivers(ctx, coi.HostingSystem, coi.Spec.Annotations) + if err != nil { + return nil, r, err + } + r.Add(driverClosers...) } ct, _, err := oci.GetSandboxTypeAndID(coi.Spec.Annotations) diff --git a/test/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/devices.go b/test/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/devices.go index cccac4397b..19562cc32f 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/devices.go +++ b/test/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/devices.go @@ -144,6 +144,33 @@ func handleAssignedDevicesWindows(ctx context.Context, vm *uvm.UtilityVM, annota return resultDevs, closers, nil } +func installPodDrivers(ctx context.Context, vm *uvm.UtilityVM, annotations map[string]string) (closers []resources.ResourceCloser, err error) { + defer func() { + if err != nil { + // best effort clean up allocated resources on failure + for _, r := range closers { + if releaseErr := r.Release(ctx); releaseErr != nil { + log.G(ctx).WithError(releaseErr).Error("failed to release container resource") + } + } + } + }() + + // get the spec specified kernel drivers and install them on the UVM + drivers, err := getSpecKernelDrivers(annotations) + if err != nil { + return closers, err + } + for _, d := range drivers { + driverCloser, err := devices.InstallKernelDriver(ctx, vm, d) + if err != nil { + return closers, err + } + closers = append(closers, driverCloser) + } + return closers, err +} + func getDeviceInfoFromPath(rawDevicePath string) (string, uint16) { indexString := filepath.Base(rawDevicePath) index, err := strconv.ParseUint(indexString, 10, 16) diff --git a/test/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources_wcow.go b/test/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources_wcow.go index 0a2fef6fc8..d7ce96e2a7 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources_wcow.go +++ b/test/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources_wcow.go @@ -14,7 +14,6 @@ import ( "github.com/Microsoft/hcsshim/internal/cmd" "github.com/Microsoft/hcsshim/internal/credentials" - "github.com/Microsoft/hcsshim/internal/devices" "github.com/Microsoft/hcsshim/internal/layers" "github.com/Microsoft/hcsshim/internal/log" "github.com/Microsoft/hcsshim/internal/resources" @@ -98,28 +97,23 @@ func allocateWindowsResources(ctx context.Context, coi *createOptionsInternal, r } } - if coi.HostingSystem != nil && coi.hasWindowsAssignedDevices() { - windowsDevices, closers, err := handleAssignedDevicesWindows(ctx, coi.HostingSystem, coi.Spec.Annotations, coi.Spec.Windows.Devices) - if err != nil { - return err - } - r.Add(closers...) - coi.Spec.Windows.Devices = windowsDevices - } - if coi.HostingSystem != nil { - // get the spec specified kernel drivers and install them on the UVM - drivers, err := getSpecKernelDrivers(coi.Spec.Annotations) - if err != nil { - return err - } - for _, d := range drivers { - driverCloser, err := devices.InstallWindowsDriver(ctx, coi.HostingSystem, d) + if coi.hasWindowsAssignedDevices() { + windowsDevices, closers, err := handleAssignedDevicesWindows(ctx, coi.HostingSystem, coi.Spec.Annotations, coi.Spec.Windows.Devices) if err != nil { return err } - r.Add(driverCloser) + r.Add(closers...) + coi.Spec.Windows.Devices = windowsDevices + } + // when driver installation completes, we are guaranteed that the device is ready for use, + // so reinstall drivers to make sure the devices are ready when we proceed. + // TODO katiewasnothere: we should find a way to avoid reinstalling drivers + driverClosers, err := installPodDrivers(ctx, coi.HostingSystem, coi.Spec.Annotations) + if err != nil { + return err } + r.Add(driverClosers...) } return nil diff --git a/test/vendor/github.com/google/uuid/null.go b/test/vendor/github.com/google/uuid/null.go new file mode 100644 index 0000000000..d7fcbf2865 --- /dev/null +++ b/test/vendor/github.com/google/uuid/null.go @@ -0,0 +1,118 @@ +// Copyright 2021 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "database/sql/driver" + "encoding/json" + "fmt" +) + +var jsonNull = []byte("null") + +// NullUUID represents a UUID that may be null. +// NullUUID implements the SQL driver.Scanner interface so +// it can be used as a scan destination: +// +// var u uuid.NullUUID +// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u) +// ... +// if u.Valid { +// // use u.UUID +// } else { +// // NULL value +// } +// +type NullUUID struct { + UUID UUID + Valid bool // Valid is true if UUID is not NULL +} + +// Scan implements the SQL driver.Scanner interface. +func (nu *NullUUID) Scan(value interface{}) error { + if value == nil { + nu.UUID, nu.Valid = Nil, false + return nil + } + + err := nu.UUID.Scan(value) + if err != nil { + nu.Valid = false + return err + } + + nu.Valid = true + return nil +} + +// Value implements the driver Valuer interface. +func (nu NullUUID) Value() (driver.Value, error) { + if !nu.Valid { + return nil, nil + } + // Delegate to UUID Value function + return nu.UUID.Value() +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (nu NullUUID) MarshalBinary() ([]byte, error) { + if nu.Valid { + return nu.UUID[:], nil + } + + return []byte(nil), nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (nu *NullUUID) UnmarshalBinary(data []byte) error { + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + copy(nu.UUID[:], data) + nu.Valid = true + return nil +} + +// MarshalText implements encoding.TextMarshaler. +func (nu NullUUID) MarshalText() ([]byte, error) { + if nu.Valid { + return nu.UUID.MarshalText() + } + + return jsonNull, nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (nu *NullUUID) UnmarshalText(data []byte) error { + id, err := ParseBytes(data) + if err != nil { + nu.Valid = false + return err + } + nu.UUID = id + nu.Valid = true + return nil +} + +// MarshalJSON implements json.Marshaler. +func (nu NullUUID) MarshalJSON() ([]byte, error) { + if nu.Valid { + return json.Marshal(nu.UUID) + } + + return jsonNull, nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (nu *NullUUID) UnmarshalJSON(data []byte) error { + if bytes.Equal(data, jsonNull) { + *nu = NullUUID{} + return nil // valid null UUID + } + err := json.Unmarshal(data, &nu.UUID) + nu.Valid = err == nil + return err +} diff --git a/test/vendor/github.com/google/uuid/uuid.go b/test/vendor/github.com/google/uuid/uuid.go index 60d26bb50c..a57207aeb6 100644 --- a/test/vendor/github.com/google/uuid/uuid.go +++ b/test/vendor/github.com/google/uuid/uuid.go @@ -12,6 +12,7 @@ import ( "fmt" "io" "strings" + "sync" ) // A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC @@ -33,7 +34,15 @@ const ( Future // Reserved for future definition. ) -var rander = rand.Reader // random function +const randPoolSize = 16 * 16 + +var ( + rander = rand.Reader // random function + poolEnabled = false + poolMu sync.Mutex + poolPos = randPoolSize // protected with poolMu + pool [randPoolSize]byte // protected with poolMu +) type invalidLengthError struct{ len int } @@ -41,6 +50,12 @@ func (err invalidLengthError) Error() string { return fmt.Sprintf("invalid UUID length: %d", err.len) } +// IsInvalidLengthError is matcher function for custom error invalidLengthError +func IsInvalidLengthError(err error) bool { + _, ok := err.(invalidLengthError) + return ok +} + // Parse decodes s into a UUID or returns an error. Both the standard UUID // forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the @@ -249,3 +264,31 @@ func SetRand(r io.Reader) { } rander = r } + +// EnableRandPool enables internal randomness pool used for Random +// (Version 4) UUID generation. The pool contains random bytes read from +// the random number generator on demand in batches. Enabling the pool +// may improve the UUID generation throughput significantly. +// +// Since the pool is stored on the Go heap, this feature may be a bad fit +// for security sensitive applications. +// +// Both EnableRandPool and DisableRandPool are not thread-safe and should +// only be called when there is no possibility that New or any other +// UUID Version 4 generation function will be called concurrently. +func EnableRandPool() { + poolEnabled = true +} + +// DisableRandPool disables the randomness pool if it was previously +// enabled with EnableRandPool. +// +// Both EnableRandPool and DisableRandPool are not thread-safe and should +// only be called when there is no possibility that New or any other +// UUID Version 4 generation function will be called concurrently. +func DisableRandPool() { + poolEnabled = false + defer poolMu.Unlock() + poolMu.Lock() + poolPos = randPoolSize +} diff --git a/test/vendor/github.com/google/uuid/version4.go b/test/vendor/github.com/google/uuid/version4.go index 86160fbd07..7697802e4d 100644 --- a/test/vendor/github.com/google/uuid/version4.go +++ b/test/vendor/github.com/google/uuid/version4.go @@ -27,6 +27,8 @@ func NewString() string { // The strength of the UUIDs is based on the strength of the crypto/rand // package. // +// Uses the randomness pool if it was enabled with EnableRandPool. +// // A note about uniqueness derived from the UUID Wikipedia entry: // // Randomly generated UUIDs have 122 random bits. One's annual risk of being @@ -35,7 +37,10 @@ func NewString() string { // equivalent to the odds of creating a few tens of trillions of UUIDs in a // year and having one duplicate. func NewRandom() (UUID, error) { - return NewRandomFromReader(rander) + if !poolEnabled { + return NewRandomFromReader(rander) + } + return newRandomFromPool() } // NewRandomFromReader returns a UUID based on bytes read from a given io.Reader. @@ -49,3 +54,23 @@ func NewRandomFromReader(r io.Reader) (UUID, error) { uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 return uuid, nil } + +func newRandomFromPool() (UUID, error) { + var uuid UUID + poolMu.Lock() + if poolPos == randPoolSize { + _, err := io.ReadFull(rander, pool[:]) + if err != nil { + poolMu.Unlock() + return Nil, err + } + poolPos = 0 + } + copy(uuid[:], pool[poolPos:(poolPos+16)]) + poolPos += 16 + poolMu.Unlock() + + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid, nil +} diff --git a/test/vendor/modules.txt b/test/vendor/modules.txt index d297c9cfd9..f305a6e588 100644 --- a/test/vendor/modules.txt +++ b/test/vendor/modules.txt @@ -178,7 +178,7 @@ github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/flags github.com/google/go-cmp/cmp/internal/function github.com/google/go-cmp/cmp/internal/value -# github.com/google/uuid v1.2.0 +# github.com/google/uuid v1.3.0 github.com/google/uuid # github.com/hashicorp/errwrap v1.0.0 github.com/hashicorp/errwrap diff --git a/vendor/github.com/google/uuid/.travis.yml b/vendor/github.com/google/uuid/.travis.yml new file mode 100644 index 0000000000..d8156a60ba --- /dev/null +++ b/vendor/github.com/google/uuid/.travis.yml @@ -0,0 +1,9 @@ +language: go + +go: + - 1.4.3 + - 1.5.3 + - tip + +script: + - go test -v ./... diff --git a/vendor/github.com/google/uuid/CONTRIBUTING.md b/vendor/github.com/google/uuid/CONTRIBUTING.md new file mode 100644 index 0000000000..04fdf09f13 --- /dev/null +++ b/vendor/github.com/google/uuid/CONTRIBUTING.md @@ -0,0 +1,10 @@ +# How to contribute + +We definitely welcome patches and contribution to this project! + +### Legal requirements + +In order to protect both you and ourselves, you will need to sign the +[Contributor License Agreement](https://cla.developers.google.com/clas). + +You may have already signed it for other Google projects. diff --git a/vendor/github.com/google/uuid/CONTRIBUTORS b/vendor/github.com/google/uuid/CONTRIBUTORS new file mode 100644 index 0000000000..b4bb97f6bc --- /dev/null +++ b/vendor/github.com/google/uuid/CONTRIBUTORS @@ -0,0 +1,9 @@ +Paul Borman +bmatsuo +shawnps +theory +jboverfelt +dsymonds +cd1 +wallclockbuilder +dansouza diff --git a/vendor/github.com/google/uuid/LICENSE b/vendor/github.com/google/uuid/LICENSE new file mode 100644 index 0000000000..5dc68268d9 --- /dev/null +++ b/vendor/github.com/google/uuid/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009,2014 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/google/uuid/README.md b/vendor/github.com/google/uuid/README.md new file mode 100644 index 0000000000..f765a46f91 --- /dev/null +++ b/vendor/github.com/google/uuid/README.md @@ -0,0 +1,19 @@ +# uuid ![build status](https://travis-ci.org/google/uuid.svg?branch=master) +The uuid package generates and inspects UUIDs based on +[RFC 4122](http://tools.ietf.org/html/rfc4122) +and DCE 1.1: Authentication and Security Services. + +This package is based on the github.com/pborman/uuid package (previously named +code.google.com/p/go-uuid). It differs from these earlier packages in that +a UUID is a 16 byte array rather than a byte slice. One loss due to this +change is the ability to represent an invalid UUID (vs a NIL UUID). + +###### Install +`go get github.com/google/uuid` + +###### Documentation +[![GoDoc](https://godoc.org/github.com/google/uuid?status.svg)](http://godoc.org/github.com/google/uuid) + +Full `go doc` style documentation for the package can be viewed online without +installing this package by using the GoDoc site here: +http://pkg.go.dev/github.com/google/uuid diff --git a/vendor/github.com/google/uuid/dce.go b/vendor/github.com/google/uuid/dce.go new file mode 100644 index 0000000000..fa820b9d30 --- /dev/null +++ b/vendor/github.com/google/uuid/dce.go @@ -0,0 +1,80 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "fmt" + "os" +) + +// A Domain represents a Version 2 domain +type Domain byte + +// Domain constants for DCE Security (Version 2) UUIDs. +const ( + Person = Domain(0) + Group = Domain(1) + Org = Domain(2) +) + +// NewDCESecurity returns a DCE Security (Version 2) UUID. +// +// The domain should be one of Person, Group or Org. +// On a POSIX system the id should be the users UID for the Person +// domain and the users GID for the Group. The meaning of id for +// the domain Org or on non-POSIX systems is site defined. +// +// For a given domain/id pair the same token may be returned for up to +// 7 minutes and 10 seconds. +func NewDCESecurity(domain Domain, id uint32) (UUID, error) { + uuid, err := NewUUID() + if err == nil { + uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2 + uuid[9] = byte(domain) + binary.BigEndian.PutUint32(uuid[0:], id) + } + return uuid, err +} + +// NewDCEPerson returns a DCE Security (Version 2) UUID in the person +// domain with the id returned by os.Getuid. +// +// NewDCESecurity(Person, uint32(os.Getuid())) +func NewDCEPerson() (UUID, error) { + return NewDCESecurity(Person, uint32(os.Getuid())) +} + +// NewDCEGroup returns a DCE Security (Version 2) UUID in the group +// domain with the id returned by os.Getgid. +// +// NewDCESecurity(Group, uint32(os.Getgid())) +func NewDCEGroup() (UUID, error) { + return NewDCESecurity(Group, uint32(os.Getgid())) +} + +// Domain returns the domain for a Version 2 UUID. Domains are only defined +// for Version 2 UUIDs. +func (uuid UUID) Domain() Domain { + return Domain(uuid[9]) +} + +// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2 +// UUIDs. +func (uuid UUID) ID() uint32 { + return binary.BigEndian.Uint32(uuid[0:4]) +} + +func (d Domain) String() string { + switch d { + case Person: + return "Person" + case Group: + return "Group" + case Org: + return "Org" + } + return fmt.Sprintf("Domain%d", int(d)) +} diff --git a/vendor/github.com/google/uuid/doc.go b/vendor/github.com/google/uuid/doc.go new file mode 100644 index 0000000000..5b8a4b9af8 --- /dev/null +++ b/vendor/github.com/google/uuid/doc.go @@ -0,0 +1,12 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package uuid generates and inspects UUIDs. +// +// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security +// Services. +// +// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to +// maps or compared directly. +package uuid diff --git a/vendor/github.com/google/uuid/go.mod b/vendor/github.com/google/uuid/go.mod new file mode 100644 index 0000000000..fc84cd79d4 --- /dev/null +++ b/vendor/github.com/google/uuid/go.mod @@ -0,0 +1 @@ +module github.com/google/uuid diff --git a/vendor/github.com/google/uuid/hash.go b/vendor/github.com/google/uuid/hash.go new file mode 100644 index 0000000000..b404f4bec2 --- /dev/null +++ b/vendor/github.com/google/uuid/hash.go @@ -0,0 +1,53 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "crypto/md5" + "crypto/sha1" + "hash" +) + +// Well known namespace IDs and UUIDs +var ( + NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) + NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) + Nil UUID // empty UUID, all zeros +) + +// NewHash returns a new UUID derived from the hash of space concatenated with +// data generated by h. The hash should be at least 16 byte in length. The +// first 16 bytes of the hash are used to form the UUID. The version of the +// UUID will be the lower 4 bits of version. NewHash is used to implement +// NewMD5 and NewSHA1. +func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { + h.Reset() + h.Write(space[:]) //nolint:errcheck + h.Write(data) //nolint:errcheck + s := h.Sum(nil) + var uuid UUID + copy(uuid[:], s) + uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) + uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant + return uuid +} + +// NewMD5 returns a new MD5 (Version 3) UUID based on the +// supplied name space and data. It is the same as calling: +// +// NewHash(md5.New(), space, data, 3) +func NewMD5(space UUID, data []byte) UUID { + return NewHash(md5.New(), space, data, 3) +} + +// NewSHA1 returns a new SHA1 (Version 5) UUID based on the +// supplied name space and data. It is the same as calling: +// +// NewHash(sha1.New(), space, data, 5) +func NewSHA1(space UUID, data []byte) UUID { + return NewHash(sha1.New(), space, data, 5) +} diff --git a/vendor/github.com/google/uuid/marshal.go b/vendor/github.com/google/uuid/marshal.go new file mode 100644 index 0000000000..14bd34072b --- /dev/null +++ b/vendor/github.com/google/uuid/marshal.go @@ -0,0 +1,38 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "fmt" + +// MarshalText implements encoding.TextMarshaler. +func (uuid UUID) MarshalText() ([]byte, error) { + var js [36]byte + encodeHex(js[:], uuid) + return js[:], nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (uuid *UUID) UnmarshalText(data []byte) error { + id, err := ParseBytes(data) + if err != nil { + return err + } + *uuid = id + return nil +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (uuid UUID) MarshalBinary() ([]byte, error) { + return uuid[:], nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (uuid *UUID) UnmarshalBinary(data []byte) error { + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + copy(uuid[:], data) + return nil +} diff --git a/vendor/github.com/google/uuid/node.go b/vendor/github.com/google/uuid/node.go new file mode 100644 index 0000000000..d651a2b061 --- /dev/null +++ b/vendor/github.com/google/uuid/node.go @@ -0,0 +1,90 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "sync" +) + +var ( + nodeMu sync.Mutex + ifname string // name of interface being used + nodeID [6]byte // hardware for version 1 UUIDs + zeroID [6]byte // nodeID with only 0's +) + +// NodeInterface returns the name of the interface from which the NodeID was +// derived. The interface "user" is returned if the NodeID was set by +// SetNodeID. +func NodeInterface() string { + defer nodeMu.Unlock() + nodeMu.Lock() + return ifname +} + +// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs. +// If name is "" then the first usable interface found will be used or a random +// Node ID will be generated. If a named interface cannot be found then false +// is returned. +// +// SetNodeInterface never fails when name is "". +func SetNodeInterface(name string) bool { + defer nodeMu.Unlock() + nodeMu.Lock() + return setNodeInterface(name) +} + +func setNodeInterface(name string) bool { + iname, addr := getHardwareInterface(name) // null implementation for js + if iname != "" && addr != nil { + ifname = iname + copy(nodeID[:], addr) + return true + } + + // We found no interfaces with a valid hardware address. If name + // does not specify a specific interface generate a random Node ID + // (section 4.1.6) + if name == "" { + ifname = "random" + randomBits(nodeID[:]) + return true + } + return false +} + +// NodeID returns a slice of a copy of the current Node ID, setting the Node ID +// if not already set. +func NodeID() []byte { + defer nodeMu.Unlock() + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") + } + nid := nodeID + return nid[:] +} + +// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes +// of id are used. If id is less than 6 bytes then false is returned and the +// Node ID is not set. +func SetNodeID(id []byte) bool { + if len(id) < 6 { + return false + } + defer nodeMu.Unlock() + nodeMu.Lock() + copy(nodeID[:], id) + ifname = "user" + return true +} + +// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is +// not valid. The NodeID is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) NodeID() []byte { + var node [6]byte + copy(node[:], uuid[10:]) + return node[:] +} diff --git a/vendor/github.com/google/uuid/node_js.go b/vendor/github.com/google/uuid/node_js.go new file mode 100644 index 0000000000..24b78edc90 --- /dev/null +++ b/vendor/github.com/google/uuid/node_js.go @@ -0,0 +1,12 @@ +// Copyright 2017 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build js + +package uuid + +// getHardwareInterface returns nil values for the JS version of the code. +// This remvoves the "net" dependency, because it is not used in the browser. +// Using the "net" library inflates the size of the transpiled JS code by 673k bytes. +func getHardwareInterface(name string) (string, []byte) { return "", nil } diff --git a/vendor/github.com/google/uuid/node_net.go b/vendor/github.com/google/uuid/node_net.go new file mode 100644 index 0000000000..0cbbcddbd6 --- /dev/null +++ b/vendor/github.com/google/uuid/node_net.go @@ -0,0 +1,33 @@ +// Copyright 2017 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !js + +package uuid + +import "net" + +var interfaces []net.Interface // cached list of interfaces + +// getHardwareInterface returns the name and hardware address of interface name. +// If name is "" then the name and hardware address of one of the system's +// interfaces is returned. If no interfaces are found (name does not exist or +// there are no interfaces) then "", nil is returned. +// +// Only addresses of at least 6 bytes are returned. +func getHardwareInterface(name string) (string, []byte) { + if interfaces == nil { + var err error + interfaces, err = net.Interfaces() + if err != nil { + return "", nil + } + } + for _, ifs := range interfaces { + if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { + return ifs.Name, ifs.HardwareAddr + } + } + return "", nil +} diff --git a/vendor/github.com/google/uuid/null.go b/vendor/github.com/google/uuid/null.go new file mode 100644 index 0000000000..d7fcbf2865 --- /dev/null +++ b/vendor/github.com/google/uuid/null.go @@ -0,0 +1,118 @@ +// Copyright 2021 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "database/sql/driver" + "encoding/json" + "fmt" +) + +var jsonNull = []byte("null") + +// NullUUID represents a UUID that may be null. +// NullUUID implements the SQL driver.Scanner interface so +// it can be used as a scan destination: +// +// var u uuid.NullUUID +// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u) +// ... +// if u.Valid { +// // use u.UUID +// } else { +// // NULL value +// } +// +type NullUUID struct { + UUID UUID + Valid bool // Valid is true if UUID is not NULL +} + +// Scan implements the SQL driver.Scanner interface. +func (nu *NullUUID) Scan(value interface{}) error { + if value == nil { + nu.UUID, nu.Valid = Nil, false + return nil + } + + err := nu.UUID.Scan(value) + if err != nil { + nu.Valid = false + return err + } + + nu.Valid = true + return nil +} + +// Value implements the driver Valuer interface. +func (nu NullUUID) Value() (driver.Value, error) { + if !nu.Valid { + return nil, nil + } + // Delegate to UUID Value function + return nu.UUID.Value() +} + +// MarshalBinary implements encoding.BinaryMarshaler. +func (nu NullUUID) MarshalBinary() ([]byte, error) { + if nu.Valid { + return nu.UUID[:], nil + } + + return []byte(nil), nil +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler. +func (nu *NullUUID) UnmarshalBinary(data []byte) error { + if len(data) != 16 { + return fmt.Errorf("invalid UUID (got %d bytes)", len(data)) + } + copy(nu.UUID[:], data) + nu.Valid = true + return nil +} + +// MarshalText implements encoding.TextMarshaler. +func (nu NullUUID) MarshalText() ([]byte, error) { + if nu.Valid { + return nu.UUID.MarshalText() + } + + return jsonNull, nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (nu *NullUUID) UnmarshalText(data []byte) error { + id, err := ParseBytes(data) + if err != nil { + nu.Valid = false + return err + } + nu.UUID = id + nu.Valid = true + return nil +} + +// MarshalJSON implements json.Marshaler. +func (nu NullUUID) MarshalJSON() ([]byte, error) { + if nu.Valid { + return json.Marshal(nu.UUID) + } + + return jsonNull, nil +} + +// UnmarshalJSON implements json.Unmarshaler. +func (nu *NullUUID) UnmarshalJSON(data []byte) error { + if bytes.Equal(data, jsonNull) { + *nu = NullUUID{} + return nil // valid null UUID + } + err := json.Unmarshal(data, &nu.UUID) + nu.Valid = err == nil + return err +} diff --git a/vendor/github.com/google/uuid/sql.go b/vendor/github.com/google/uuid/sql.go new file mode 100644 index 0000000000..2e02ec06c0 --- /dev/null +++ b/vendor/github.com/google/uuid/sql.go @@ -0,0 +1,59 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "database/sql/driver" + "fmt" +) + +// Scan implements sql.Scanner so UUIDs can be read from databases transparently. +// Currently, database types that map to string and []byte are supported. Please +// consult database-specific driver documentation for matching types. +func (uuid *UUID) Scan(src interface{}) error { + switch src := src.(type) { + case nil: + return nil + + case string: + // if an empty UUID comes from a table, we return a null UUID + if src == "" { + return nil + } + + // see Parse for required string format + u, err := Parse(src) + if err != nil { + return fmt.Errorf("Scan: %v", err) + } + + *uuid = u + + case []byte: + // if an empty UUID comes from a table, we return a null UUID + if len(src) == 0 { + return nil + } + + // assumes a simple slice of bytes if 16 bytes + // otherwise attempts to parse + if len(src) != 16 { + return uuid.Scan(string(src)) + } + copy((*uuid)[:], src) + + default: + return fmt.Errorf("Scan: unable to scan type %T into UUID", src) + } + + return nil +} + +// Value implements sql.Valuer so that UUIDs can be written to databases +// transparently. Currently, UUIDs map to strings. Please consult +// database-specific driver documentation for matching types. +func (uuid UUID) Value() (driver.Value, error) { + return uuid.String(), nil +} diff --git a/vendor/github.com/google/uuid/time.go b/vendor/github.com/google/uuid/time.go new file mode 100644 index 0000000000..e6ef06cdc8 --- /dev/null +++ b/vendor/github.com/google/uuid/time.go @@ -0,0 +1,123 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" + "sync" + "time" +) + +// A Time represents a time as the number of 100's of nanoseconds since 15 Oct +// 1582. +type Time int64 + +const ( + lillian = 2299160 // Julian day of 15 Oct 1582 + unix = 2440587 // Julian day of 1 Jan 1970 + epoch = unix - lillian // Days between epochs + g1582 = epoch * 86400 // seconds between epochs + g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs +) + +var ( + timeMu sync.Mutex + lasttime uint64 // last time we returned + clockSeq uint16 // clock sequence for this run + + timeNow = time.Now // for testing +) + +// UnixTime converts t the number of seconds and nanoseconds using the Unix +// epoch of 1 Jan 1970. +func (t Time) UnixTime() (sec, nsec int64) { + sec = int64(t - g1582ns100) + nsec = (sec % 10000000) * 100 + sec /= 10000000 + return sec, nsec +} + +// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and +// clock sequence as well as adjusting the clock sequence as needed. An error +// is returned if the current time cannot be determined. +func GetTime() (Time, uint16, error) { + defer timeMu.Unlock() + timeMu.Lock() + return getTime() +} + +func getTime() (Time, uint16, error) { + t := timeNow() + + // If we don't have a clock sequence already, set one. + if clockSeq == 0 { + setClockSequence(-1) + } + now := uint64(t.UnixNano()/100) + g1582ns100 + + // If time has gone backwards with this clock sequence then we + // increment the clock sequence + if now <= lasttime { + clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000 + } + lasttime = now + return Time(now), clockSeq, nil +} + +// ClockSequence returns the current clock sequence, generating one if not +// already set. The clock sequence is only used for Version 1 UUIDs. +// +// The uuid package does not use global static storage for the clock sequence or +// the last time a UUID was generated. Unless SetClockSequence is used, a new +// random clock sequence is generated the first time a clock sequence is +// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) +func ClockSequence() int { + defer timeMu.Unlock() + timeMu.Lock() + return clockSequence() +} + +func clockSequence() int { + if clockSeq == 0 { + setClockSequence(-1) + } + return int(clockSeq & 0x3fff) +} + +// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to +// -1 causes a new sequence to be generated. +func SetClockSequence(seq int) { + defer timeMu.Unlock() + timeMu.Lock() + setClockSequence(seq) +} + +func setClockSequence(seq int) { + if seq == -1 { + var b [2]byte + randomBits(b[:]) // clock sequence + seq = int(b[0])<<8 | int(b[1]) + } + oldSeq := clockSeq + clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant + if oldSeq != clockSeq { + lasttime = 0 + } +} + +// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in +// uuid. The time is only defined for version 1 and 2 UUIDs. +func (uuid UUID) Time() Time { + time := int64(binary.BigEndian.Uint32(uuid[0:4])) + time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 + time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 + return Time(time) +} + +// ClockSequence returns the clock sequence encoded in uuid. +// The clock sequence is only well defined for version 1 and 2 UUIDs. +func (uuid UUID) ClockSequence() int { + return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff +} diff --git a/vendor/github.com/google/uuid/util.go b/vendor/github.com/google/uuid/util.go new file mode 100644 index 0000000000..5ea6c73780 --- /dev/null +++ b/vendor/github.com/google/uuid/util.go @@ -0,0 +1,43 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "io" +) + +// randomBits completely fills slice b with random data. +func randomBits(b []byte) { + if _, err := io.ReadFull(rander, b); err != nil { + panic(err.Error()) // rand should never fail + } +} + +// xvalues returns the value of a byte as a hexadecimal digit or 255. +var xvalues = [256]byte{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, +} + +// xtob converts hex characters x1 and x2 into a byte. +func xtob(x1, x2 byte) (byte, bool) { + b1 := xvalues[x1] + b2 := xvalues[x2] + return (b1 << 4) | b2, b1 != 255 && b2 != 255 +} diff --git a/vendor/github.com/google/uuid/uuid.go b/vendor/github.com/google/uuid/uuid.go new file mode 100644 index 0000000000..a57207aeb6 --- /dev/null +++ b/vendor/github.com/google/uuid/uuid.go @@ -0,0 +1,294 @@ +// Copyright 2018 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "errors" + "fmt" + "io" + "strings" + "sync" +) + +// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC +// 4122. +type UUID [16]byte + +// A Version represents a UUID's version. +type Version byte + +// A Variant represents a UUID's variant. +type Variant byte + +// Constants returned by Variant. +const ( + Invalid = Variant(iota) // Invalid UUID + RFC4122 // The variant specified in RFC4122 + Reserved // Reserved, NCS backward compatibility. + Microsoft // Reserved, Microsoft Corporation backward compatibility. + Future // Reserved for future definition. +) + +const randPoolSize = 16 * 16 + +var ( + rander = rand.Reader // random function + poolEnabled = false + poolMu sync.Mutex + poolPos = randPoolSize // protected with poolMu + pool [randPoolSize]byte // protected with poolMu +) + +type invalidLengthError struct{ len int } + +func (err invalidLengthError) Error() string { + return fmt.Sprintf("invalid UUID length: %d", err.len) +} + +// IsInvalidLengthError is matcher function for custom error invalidLengthError +func IsInvalidLengthError(err error) bool { + _, ok := err.(invalidLengthError) + return ok +} + +// Parse decodes s into a UUID or returns an error. Both the standard UUID +// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the +// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex +// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. +func Parse(s string) (UUID, error) { + var uuid UUID + switch len(s) { + // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36: + + // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36 + 9: + if strings.ToLower(s[:9]) != "urn:uuid:" { + return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9]) + } + s = s[9:] + + // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + case 36 + 2: + s = s[1:] + + // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + case 32: + var ok bool + for i := range uuid { + uuid[i], ok = xtob(s[i*2], s[i*2+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + } + return uuid, nil + default: + return uuid, invalidLengthError{len(s)} + } + // s is now at least 36 bytes long + // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { + return uuid, errors.New("invalid UUID format") + } + for i, x := range [16]int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34} { + v, ok := xtob(s[x], s[x+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + uuid[i] = v + } + return uuid, nil +} + +// ParseBytes is like Parse, except it parses a byte slice instead of a string. +func ParseBytes(b []byte) (UUID, error) { + var uuid UUID + switch len(b) { + case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) { + return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9]) + } + b = b[9:] + case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} + b = b[1:] + case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + var ok bool + for i := 0; i < 32; i += 2 { + uuid[i/2], ok = xtob(b[i], b[i+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + } + return uuid, nil + default: + return uuid, invalidLengthError{len(b)} + } + // s is now at least 36 bytes long + // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' { + return uuid, errors.New("invalid UUID format") + } + for i, x := range [16]int{ + 0, 2, 4, 6, + 9, 11, + 14, 16, + 19, 21, + 24, 26, 28, 30, 32, 34} { + v, ok := xtob(b[x], b[x+1]) + if !ok { + return uuid, errors.New("invalid UUID format") + } + uuid[i] = v + } + return uuid, nil +} + +// MustParse is like Parse but panics if the string cannot be parsed. +// It simplifies safe initialization of global variables holding compiled UUIDs. +func MustParse(s string) UUID { + uuid, err := Parse(s) + if err != nil { + panic(`uuid: Parse(` + s + `): ` + err.Error()) + } + return uuid +} + +// FromBytes creates a new UUID from a byte slice. Returns an error if the slice +// does not have a length of 16. The bytes are copied from the slice. +func FromBytes(b []byte) (uuid UUID, err error) { + err = uuid.UnmarshalBinary(b) + return uuid, err +} + +// Must returns uuid if err is nil and panics otherwise. +func Must(uuid UUID, err error) UUID { + if err != nil { + panic(err) + } + return uuid +} + +// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +// , or "" if uuid is invalid. +func (uuid UUID) String() string { + var buf [36]byte + encodeHex(buf[:], uuid) + return string(buf[:]) +} + +// URN returns the RFC 2141 URN form of uuid, +// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. +func (uuid UUID) URN() string { + var buf [36 + 9]byte + copy(buf[:], "urn:uuid:") + encodeHex(buf[9:], uuid) + return string(buf[:]) +} + +func encodeHex(dst []byte, uuid UUID) { + hex.Encode(dst, uuid[:4]) + dst[8] = '-' + hex.Encode(dst[9:13], uuid[4:6]) + dst[13] = '-' + hex.Encode(dst[14:18], uuid[6:8]) + dst[18] = '-' + hex.Encode(dst[19:23], uuid[8:10]) + dst[23] = '-' + hex.Encode(dst[24:], uuid[10:]) +} + +// Variant returns the variant encoded in uuid. +func (uuid UUID) Variant() Variant { + switch { + case (uuid[8] & 0xc0) == 0x80: + return RFC4122 + case (uuid[8] & 0xe0) == 0xc0: + return Microsoft + case (uuid[8] & 0xe0) == 0xe0: + return Future + default: + return Reserved + } +} + +// Version returns the version of uuid. +func (uuid UUID) Version() Version { + return Version(uuid[6] >> 4) +} + +func (v Version) String() string { + if v > 15 { + return fmt.Sprintf("BAD_VERSION_%d", v) + } + return fmt.Sprintf("VERSION_%d", v) +} + +func (v Variant) String() string { + switch v { + case RFC4122: + return "RFC4122" + case Reserved: + return "Reserved" + case Microsoft: + return "Microsoft" + case Future: + return "Future" + case Invalid: + return "Invalid" + } + return fmt.Sprintf("BadVariant%d", int(v)) +} + +// SetRand sets the random number generator to r, which implements io.Reader. +// If r.Read returns an error when the package requests random data then +// a panic will be issued. +// +// Calling SetRand with nil sets the random number generator to the default +// generator. +func SetRand(r io.Reader) { + if r == nil { + rander = rand.Reader + return + } + rander = r +} + +// EnableRandPool enables internal randomness pool used for Random +// (Version 4) UUID generation. The pool contains random bytes read from +// the random number generator on demand in batches. Enabling the pool +// may improve the UUID generation throughput significantly. +// +// Since the pool is stored on the Go heap, this feature may be a bad fit +// for security sensitive applications. +// +// Both EnableRandPool and DisableRandPool are not thread-safe and should +// only be called when there is no possibility that New or any other +// UUID Version 4 generation function will be called concurrently. +func EnableRandPool() { + poolEnabled = true +} + +// DisableRandPool disables the randomness pool if it was previously +// enabled with EnableRandPool. +// +// Both EnableRandPool and DisableRandPool are not thread-safe and should +// only be called when there is no possibility that New or any other +// UUID Version 4 generation function will be called concurrently. +func DisableRandPool() { + poolEnabled = false + defer poolMu.Unlock() + poolMu.Lock() + poolPos = randPoolSize +} diff --git a/vendor/github.com/google/uuid/version1.go b/vendor/github.com/google/uuid/version1.go new file mode 100644 index 0000000000..463109629e --- /dev/null +++ b/vendor/github.com/google/uuid/version1.go @@ -0,0 +1,44 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import ( + "encoding/binary" +) + +// NewUUID returns a Version 1 UUID based on the current NodeID and clock +// sequence, and the current time. If the NodeID has not been set by SetNodeID +// or SetNodeInterface then it will be set automatically. If the NodeID cannot +// be set NewUUID returns nil. If clock sequence has not been set by +// SetClockSequence then it will be set automatically. If GetTime fails to +// return the current NewUUID returns nil and an error. +// +// In most cases, New should be used. +func NewUUID() (UUID, error) { + var uuid UUID + now, seq, err := GetTime() + if err != nil { + return uuid, err + } + + timeLow := uint32(now & 0xffffffff) + timeMid := uint16((now >> 32) & 0xffff) + timeHi := uint16((now >> 48) & 0x0fff) + timeHi |= 0x1000 // Version 1 + + binary.BigEndian.PutUint32(uuid[0:], timeLow) + binary.BigEndian.PutUint16(uuid[4:], timeMid) + binary.BigEndian.PutUint16(uuid[6:], timeHi) + binary.BigEndian.PutUint16(uuid[8:], seq) + + nodeMu.Lock() + if nodeID == zeroID { + setNodeInterface("") + } + copy(uuid[10:], nodeID[:]) + nodeMu.Unlock() + + return uuid, nil +} diff --git a/vendor/github.com/google/uuid/version4.go b/vendor/github.com/google/uuid/version4.go new file mode 100644 index 0000000000..7697802e4d --- /dev/null +++ b/vendor/github.com/google/uuid/version4.go @@ -0,0 +1,76 @@ +// Copyright 2016 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package uuid + +import "io" + +// New creates a new random UUID or panics. New is equivalent to +// the expression +// +// uuid.Must(uuid.NewRandom()) +func New() UUID { + return Must(NewRandom()) +} + +// NewString creates a new random UUID and returns it as a string or panics. +// NewString is equivalent to the expression +// +// uuid.New().String() +func NewString() string { + return Must(NewRandom()).String() +} + +// NewRandom returns a Random (Version 4) UUID. +// +// The strength of the UUIDs is based on the strength of the crypto/rand +// package. +// +// Uses the randomness pool if it was enabled with EnableRandPool. +// +// A note about uniqueness derived from the UUID Wikipedia entry: +// +// Randomly generated UUIDs have 122 random bits. One's annual risk of being +// hit by a meteorite is estimated to be one chance in 17 billion, that +// means the probability is about 0.00000000006 (6 × 10−11), +// equivalent to the odds of creating a few tens of trillions of UUIDs in a +// year and having one duplicate. +func NewRandom() (UUID, error) { + if !poolEnabled { + return NewRandomFromReader(rander) + } + return newRandomFromPool() +} + +// NewRandomFromReader returns a UUID based on bytes read from a given io.Reader. +func NewRandomFromReader(r io.Reader) (UUID, error) { + var uuid UUID + _, err := io.ReadFull(r, uuid[:]) + if err != nil { + return Nil, err + } + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid, nil +} + +func newRandomFromPool() (UUID, error) { + var uuid UUID + poolMu.Lock() + if poolPos == randPoolSize { + _, err := io.ReadFull(rander, pool[:]) + if err != nil { + poolMu.Unlock() + return Nil, err + } + poolPos = 0 + } + copy(uuid[:], pool[poolPos:(poolPos+16)]) + poolPos += 16 + poolMu.Unlock() + + uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 + uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 + return uuid, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 54a5476ece..7feee628be 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -101,6 +101,8 @@ github.com/google/go-containerregistry/pkg/v1/remote github.com/google/go-containerregistry/pkg/v1/remote/transport github.com/google/go-containerregistry/pkg/v1/stream github.com/google/go-containerregistry/pkg/v1/types +# github.com/google/uuid v1.3.0 +github.com/google/uuid # github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3 github.com/linuxkit/virtsock/pkg/vsock # github.com/mattn/go-shellwords v1.0.6 From 9ed0296c1f540d0c647d4e47efe69c385e061751 Mon Sep 17 00:00:00 2001 From: Kathryn Baldauf Date: Thu, 28 Oct 2021 12:34:22 -0700 Subject: [PATCH 2/3] Remove io/fs package in favor of filepath functions Signed-off-by: Kathryn Baldauf --- cmd/gcstools/installdrivers.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cmd/gcstools/installdrivers.go b/cmd/gcstools/installdrivers.go index f14953c8ea..108568f01b 100644 --- a/cmd/gcstools/installdrivers.go +++ b/cmd/gcstools/installdrivers.go @@ -5,7 +5,6 @@ package main import ( "context" "fmt" - "io/fs" "os" "os/exec" "path/filepath" @@ -51,12 +50,12 @@ func install(ctx context.Context) error { // find all module files, which end with ".ko" extension, and remove extension // for use when calling `modprobe` below. - if walkErr := filepath.WalkDir(rootPath, func(path string, d fs.DirEntry, err error) error { + if walkErr := filepath.Walk(rootPath, func(path string, info os.FileInfo, err error) error { if err != nil { return errors.Wrap(err, "failed to read directory while walking dir") } - if !d.IsDir() && filepath.Ext(d.Name()) == moduleExtension { - moduleName := strings.TrimSuffix(d.Name(), moduleExtension) + if !info.IsDir() && filepath.Ext(info.Name()) == moduleExtension { + moduleName := strings.TrimSuffix(info.Name(), moduleExtension) modules = append(modules, moduleName) } return nil From af37ae5930a29145b8200f944b7f8f6f6a0c6ec0 Mon Sep 17 00:00:00 2001 From: Kathryn Baldauf Date: Mon, 1 Nov 2021 12:57:54 -0700 Subject: [PATCH 3/3] Move lcow install out of pnp file, remove unnecessary InstallWindowsDriver Signed-off-by: Kathryn Baldauf --- internal/devices/drivers.go | 70 +++++++++++-------- internal/devices/pnp.go | 40 ----------- .../hcsshim/internal/devices/drivers.go | 70 +++++++++++-------- .../Microsoft/hcsshim/internal/devices/pnp.go | 40 ----------- 4 files changed, 84 insertions(+), 136 deletions(-) diff --git a/internal/devices/drivers.go b/internal/devices/drivers.go index f02056d940..f37911efe6 100644 --- a/internal/devices/drivers.go +++ b/internal/devices/drivers.go @@ -6,39 +6,13 @@ import ( "context" "fmt" + "github.com/Microsoft/hcsshim/internal/cmd" "github.com/Microsoft/hcsshim/internal/log" "github.com/Microsoft/hcsshim/internal/resources" "github.com/Microsoft/hcsshim/internal/uvm" + "github.com/pkg/errors" ) -// InstallWindowsDriver mounts a specified kernel driver using vsmb, then installs it in the UVM. -// -// `driver` is a directory path on the host that contains driver files for standard installation. -// -// Returns a ResourceCloser for the added vsmb share. On failure, the vsmb share will be released, -// the returned ResourceCloser will be nil, and an error will be returned. -func InstallWindowsDriver(ctx context.Context, vm *uvm.UtilityVM, driver string) (closer resources.ResourceCloser, err error) { - defer func() { - if err != nil && closer != nil { - // best effort clean up allocated resource on failure - if releaseErr := closer.Release(ctx); releaseErr != nil { - log.G(ctx).WithError(releaseErr).Error("failed to release container resource") - } - closer = nil - } - }() - options := vm.DefaultVSMBOptions(true) - closer, err = vm.AddVSMB(ctx, driver, options) - if err != nil { - return closer, fmt.Errorf("failed to add VSMB share to utility VM for path %+v: %s", driver, err) - } - uvmPath, err := vm.GetVSMBUvmPath(ctx, driver, true) - if err != nil { - return closer, err - } - return closer, execPnPInstallDriver(ctx, vm, uvmPath) -} - // InstallKernelDriver mounts a specified kernel driver, then installs it in the UVM. // // `driver` is a directory path on the host that contains driver files for standard installation. @@ -77,3 +51,43 @@ func InstallKernelDriver(ctx context.Context, vm *uvm.UtilityVM, driver string) } return scsiCloser, execModprobeInstallDriver(ctx, vm, uvmPathForShare) } + +func execModprobeInstallDriver(ctx context.Context, vm *uvm.UtilityVM, driverDir string) error { + p, l, err := cmd.CreateNamedPipeListener() + if err != nil { + return err + } + defer l.Close() + + var pipeResults []string + errChan := make(chan error) + + go readCsPipeOutput(l, errChan, &pipeResults) + + args := []string{ + "/bin/install-drivers", + driverDir, + } + req := &cmd.CmdProcessRequest{ + Args: args, + Stderr: p, + } + + exitCode, err := cmd.ExecInUvm(ctx, vm, req) + if err != nil && err != noExecOutputErr { + return errors.Wrapf(err, "failed to install driver %s in uvm with exit code %d", driverDir, exitCode) + } + + // wait to finish parsing stdout results + select { + case err := <-errChan: + if err != nil { + return err + } + case <-ctx.Done(): + return ctx.Err() + } + + log.G(ctx).WithField("added drivers", driverDir).Debug("installed drivers") + return nil +} diff --git a/internal/devices/pnp.go b/internal/devices/pnp.go index 96108feda5..4714090839 100644 --- a/internal/devices/pnp.go +++ b/internal/devices/pnp.go @@ -67,46 +67,6 @@ func execPnPInstallDriver(ctx context.Context, vm *uvm.UtilityVM, driverDir stri return nil } -func execModprobeInstallDriver(ctx context.Context, vm *uvm.UtilityVM, driverDir string) error { - p, l, err := cmd.CreateNamedPipeListener() - if err != nil { - return err - } - defer l.Close() - - var pipeResults []string - errChan := make(chan error) - - go readCsPipeOutput(l, errChan, &pipeResults) - - args := []string{ - "/bin/install-drivers", - driverDir, - } - req := &cmd.CmdProcessRequest{ - Args: args, - Stderr: p, - } - - exitCode, err := cmd.ExecInUvm(ctx, vm, req) - if err != nil && err != noExecOutputErr { - return errors.Wrapf(err, "failed to install driver %s in uvm with exit code %d", driverDir, exitCode) - } - - // wait to finish parsing stdout results - select { - case err := <-errChan: - if err != nil { - return err - } - case <-ctx.Done(): - return ctx.Err() - } - - log.G(ctx).WithField("added drivers", driverDir).Debug("installed drivers") - return nil -} - // readCsPipeOutput is a helper function that connects to a listener and reads // the connection's comma separated output until done. resulting comma separated // values are returned in the `result` param. The `errChan` param is used to diff --git a/test/vendor/github.com/Microsoft/hcsshim/internal/devices/drivers.go b/test/vendor/github.com/Microsoft/hcsshim/internal/devices/drivers.go index f02056d940..f37911efe6 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/internal/devices/drivers.go +++ b/test/vendor/github.com/Microsoft/hcsshim/internal/devices/drivers.go @@ -6,39 +6,13 @@ import ( "context" "fmt" + "github.com/Microsoft/hcsshim/internal/cmd" "github.com/Microsoft/hcsshim/internal/log" "github.com/Microsoft/hcsshim/internal/resources" "github.com/Microsoft/hcsshim/internal/uvm" + "github.com/pkg/errors" ) -// InstallWindowsDriver mounts a specified kernel driver using vsmb, then installs it in the UVM. -// -// `driver` is a directory path on the host that contains driver files for standard installation. -// -// Returns a ResourceCloser for the added vsmb share. On failure, the vsmb share will be released, -// the returned ResourceCloser will be nil, and an error will be returned. -func InstallWindowsDriver(ctx context.Context, vm *uvm.UtilityVM, driver string) (closer resources.ResourceCloser, err error) { - defer func() { - if err != nil && closer != nil { - // best effort clean up allocated resource on failure - if releaseErr := closer.Release(ctx); releaseErr != nil { - log.G(ctx).WithError(releaseErr).Error("failed to release container resource") - } - closer = nil - } - }() - options := vm.DefaultVSMBOptions(true) - closer, err = vm.AddVSMB(ctx, driver, options) - if err != nil { - return closer, fmt.Errorf("failed to add VSMB share to utility VM for path %+v: %s", driver, err) - } - uvmPath, err := vm.GetVSMBUvmPath(ctx, driver, true) - if err != nil { - return closer, err - } - return closer, execPnPInstallDriver(ctx, vm, uvmPath) -} - // InstallKernelDriver mounts a specified kernel driver, then installs it in the UVM. // // `driver` is a directory path on the host that contains driver files for standard installation. @@ -77,3 +51,43 @@ func InstallKernelDriver(ctx context.Context, vm *uvm.UtilityVM, driver string) } return scsiCloser, execModprobeInstallDriver(ctx, vm, uvmPathForShare) } + +func execModprobeInstallDriver(ctx context.Context, vm *uvm.UtilityVM, driverDir string) error { + p, l, err := cmd.CreateNamedPipeListener() + if err != nil { + return err + } + defer l.Close() + + var pipeResults []string + errChan := make(chan error) + + go readCsPipeOutput(l, errChan, &pipeResults) + + args := []string{ + "/bin/install-drivers", + driverDir, + } + req := &cmd.CmdProcessRequest{ + Args: args, + Stderr: p, + } + + exitCode, err := cmd.ExecInUvm(ctx, vm, req) + if err != nil && err != noExecOutputErr { + return errors.Wrapf(err, "failed to install driver %s in uvm with exit code %d", driverDir, exitCode) + } + + // wait to finish parsing stdout results + select { + case err := <-errChan: + if err != nil { + return err + } + case <-ctx.Done(): + return ctx.Err() + } + + log.G(ctx).WithField("added drivers", driverDir).Debug("installed drivers") + return nil +} diff --git a/test/vendor/github.com/Microsoft/hcsshim/internal/devices/pnp.go b/test/vendor/github.com/Microsoft/hcsshim/internal/devices/pnp.go index 96108feda5..4714090839 100644 --- a/test/vendor/github.com/Microsoft/hcsshim/internal/devices/pnp.go +++ b/test/vendor/github.com/Microsoft/hcsshim/internal/devices/pnp.go @@ -67,46 +67,6 @@ func execPnPInstallDriver(ctx context.Context, vm *uvm.UtilityVM, driverDir stri return nil } -func execModprobeInstallDriver(ctx context.Context, vm *uvm.UtilityVM, driverDir string) error { - p, l, err := cmd.CreateNamedPipeListener() - if err != nil { - return err - } - defer l.Close() - - var pipeResults []string - errChan := make(chan error) - - go readCsPipeOutput(l, errChan, &pipeResults) - - args := []string{ - "/bin/install-drivers", - driverDir, - } - req := &cmd.CmdProcessRequest{ - Args: args, - Stderr: p, - } - - exitCode, err := cmd.ExecInUvm(ctx, vm, req) - if err != nil && err != noExecOutputErr { - return errors.Wrapf(err, "failed to install driver %s in uvm with exit code %d", driverDir, exitCode) - } - - // wait to finish parsing stdout results - select { - case err := <-errChan: - if err != nil { - return err - } - case <-ctx.Done(): - return ctx.Err() - } - - log.G(ctx).WithField("added drivers", driverDir).Debug("installed drivers") - return nil -} - // readCsPipeOutput is a helper function that connects to a listener and reads // the connection's comma separated output until done. resulting comma separated // values are returned in the `result` param. The `errChan` param is used to