Tap plugin
This PR adds a plugin to create tap devices. The plugin adds a tap device to the container. The plugin has a workaround for a golang netlink library which does not allow for tap devices with no owner/group to be created. When no tap owner/group is requested, the plugin will fall back to using the ip tool for creating the tap device. A fix to the golang netlink lib is pending. Signed-off-by: mmirecki <mmirecki@redhat.com>
This commit is contained in:
parent
98e01b7c80
commit
01d0031487
2
go.mod
2
go.mod
@ -17,6 +17,7 @@ require (
|
||||
github.com/networkplumbing/go-nft v0.2.0
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/gomega v1.26.0
|
||||
github.com/opencontainers/selinux v1.8.2
|
||||
github.com/safchain/ethtool v0.2.0
|
||||
github.com/vishvananda/netlink v1.2.1-beta.2
|
||||
golang.org/x/sys v0.5.0
|
||||
@ -24,6 +25,7 @@ require (
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.4.17 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.2.0 // indirect
|
||||
github.com/containerd/cgroups v1.0.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
|
2
go.sum
2
go.sum
@ -82,6 +82,7 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA=
|
||||
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
@ -550,6 +551,7 @@ github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.m
|
||||
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
||||
github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
|
||||
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
|
||||
github.com/opencontainers/selinux v1.8.2 h1:c4ca10UMgRcvZ6h0K4HtS15UaVSBEaE+iln2LVpAuGc=
|
||||
github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
|
454
plugins/main/tap/tap.go
Normal file
454
plugins/main/tap/tap.go
Normal file
@ -0,0 +1,454 @@
|
||||
// Copyright 2022 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/opencontainers/selinux/go-selinux"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
current "github.com/containernetworking/cni/pkg/types/100"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/ip"
|
||||
"github.com/containernetworking/plugins/pkg/ipam"
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
||||
)
|
||||
|
||||
type NetConf struct {
|
||||
types.NetConf
|
||||
MultiQueue bool `json:"multiQueue"`
|
||||
MTU int `json:"mtu"`
|
||||
Mac string `json:"mac,omitempty"`
|
||||
Owner *uint32 `json:"owner,omitempty"`
|
||||
Group *uint32 `json:"group,omitempty"`
|
||||
SelinuxContext string `json:"selinuxContext,omitempty"`
|
||||
Args *struct {
|
||||
} `json:"args,omitempty"`
|
||||
RuntimeConfig struct {
|
||||
Mac string `json:"mac,omitempty"`
|
||||
} `json:"runtimeConfig,omitempty"`
|
||||
}
|
||||
|
||||
// MacEnvArgs represents CNI_ARG
|
||||
type MacEnvArgs struct {
|
||||
types.CommonArgs
|
||||
MAC types.UnmarshallableString `json:"mac,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
// this ensures that main runs only on main thread (thread group leader).
|
||||
// since namespace ops (unshare, setns) are done for a single thread, we
|
||||
// must ensure that the goroutine does not jump from OS thread to thread
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
func loadConf(args *skel.CmdArgs) (*NetConf, string, error) {
|
||||
n := &NetConf{}
|
||||
if err := json.Unmarshal(args.StdinData, n); err != nil {
|
||||
return nil, "", fmt.Errorf("failed to load netconf: %v", err)
|
||||
}
|
||||
if args.Args != "" {
|
||||
e := MacEnvArgs{}
|
||||
err := types.LoadArgs(args.Args, &e)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if e.MAC != "" {
|
||||
n.Mac = string(e.MAC)
|
||||
}
|
||||
}
|
||||
|
||||
if n.RuntimeConfig.Mac != "" {
|
||||
n.Mac = n.RuntimeConfig.Mac
|
||||
}
|
||||
|
||||
return n, n.CNIVersion, nil
|
||||
}
|
||||
|
||||
// We want to share the parent process std{in|out|err} - fds 0 through 2.
|
||||
// Since the FDs are inherited on fork / exec, we close on exec all others.
|
||||
func closeFileDescriptorsOnExec() {
|
||||
minFDToCloseOnExec := 3
|
||||
maxFDToCloseOnExec := 256
|
||||
for fd := minFDToCloseOnExec; fd < maxFDToCloseOnExec; fd++ {
|
||||
syscall.CloseOnExec(fd)
|
||||
}
|
||||
}
|
||||
|
||||
// Due to issues with the vishvananda/netlink library (fix pending) it is not possible to create an ownerless/groupless
|
||||
// tap device. Until the issue is fixed, the workaround for creating a tap device with no owner/group is to use the iptool
|
||||
func createTapWithIptool(tmpName string, mtu int, multiqueue bool, mac string, owner *uint32, group *uint32) error {
|
||||
closeFileDescriptorsOnExec()
|
||||
|
||||
tapDeviceArgs := []string{"tuntap", "add", "mode", "tap", "name", tmpName}
|
||||
if multiqueue {
|
||||
tapDeviceArgs = append(tapDeviceArgs, "multi_queue")
|
||||
}
|
||||
|
||||
if owner != nil {
|
||||
tapDeviceArgs = append(tapDeviceArgs, "user", fmt.Sprintf("%d", *owner))
|
||||
}
|
||||
if group != nil {
|
||||
tapDeviceArgs = append(tapDeviceArgs, "group", fmt.Sprintf("%d", *group))
|
||||
}
|
||||
output, err := exec.Command("ip", tapDeviceArgs...).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to run command %s: %v", output, err)
|
||||
}
|
||||
|
||||
tapDeviceArgs = []string{"link", "set", tmpName}
|
||||
if mtu != 0 {
|
||||
tapDeviceArgs = append(tapDeviceArgs, "mtu", strconv.Itoa(mtu))
|
||||
}
|
||||
if mac != "" {
|
||||
tapDeviceArgs = append(tapDeviceArgs, "address", mac)
|
||||
}
|
||||
output, err = exec.Command("ip", tapDeviceArgs...).CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to run command %s: %v", output, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createLinkWithNetlink(tmpName string, mtu int, nsFd int, multiqueue bool, mac string, owner *uint32, group *uint32) error {
|
||||
linkAttrs := netlink.LinkAttrs{
|
||||
Name: tmpName,
|
||||
Namespace: netlink.NsFd(nsFd),
|
||||
}
|
||||
if mtu != 0 {
|
||||
linkAttrs.MTU = mtu
|
||||
}
|
||||
mv := &netlink.Tuntap{
|
||||
LinkAttrs: linkAttrs,
|
||||
Mode: netlink.TUNTAP_MODE_TAP,
|
||||
}
|
||||
|
||||
if owner != nil {
|
||||
mv.Owner = *owner
|
||||
}
|
||||
if group != nil {
|
||||
mv.Group = *group
|
||||
}
|
||||
|
||||
if mac != "" {
|
||||
addr, err := net.ParseMAC(mac)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid args %v for MAC addr: %v", mac, err)
|
||||
}
|
||||
linkAttrs.HardwareAddr = addr
|
||||
}
|
||||
mv.Flags = netlink.TUNTAP_VNET_HDR | unix.IFF_TAP
|
||||
if multiqueue {
|
||||
mv.Flags = netlink.TUNTAP_MULTI_QUEUE_DEFAULTS | mv.Flags
|
||||
}
|
||||
if err := netlink.LinkAdd(mv); err != nil {
|
||||
return fmt.Errorf("failed to create tap: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createLink(tmpName string, conf *NetConf, netns ns.NetNS) error {
|
||||
if conf.SelinuxContext != "" {
|
||||
if err := selinux.SetExecLabel(conf.SelinuxContext); err != nil {
|
||||
return fmt.Errorf("failed set socket label: %v", err)
|
||||
}
|
||||
return createTapWithIptool(tmpName, conf.MTU, conf.MultiQueue, conf.Mac, conf.Owner, conf.Group)
|
||||
} else if conf.Owner == nil || conf.Group == nil {
|
||||
return createTapWithIptool(tmpName, conf.MTU, conf.MultiQueue, conf.Mac, conf.Owner, conf.Group)
|
||||
} else {
|
||||
return createLinkWithNetlink(tmpName, conf.MTU, int(netns.Fd()), conf.MultiQueue, conf.Mac, conf.Owner, conf.Group)
|
||||
}
|
||||
}
|
||||
|
||||
func createTap(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interface, error) {
|
||||
tap := ¤t.Interface{}
|
||||
// due to kernel bug we have to create with tmpName or it might
|
||||
// collide with the name on the host and error out
|
||||
tmpName, err := ip.RandomVethName()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = netns.Do(func(_ ns.NetNS) error {
|
||||
err := createLink(tmpName, conf, netns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = ip.RenameLink(tmpName, ifName); err != nil {
|
||||
link, err := netlink.LinkByName(tmpName)
|
||||
if err != nil {
|
||||
netlink.LinkDel(link)
|
||||
return fmt.Errorf("failed to rename tap to %q: %v", ifName, err)
|
||||
}
|
||||
}
|
||||
tap.Name = ifName
|
||||
|
||||
// Re-fetch link to get all properties/attributes
|
||||
link, err := netlink.LinkByName(ifName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to refetch tap %q: %v", ifName, err)
|
||||
}
|
||||
|
||||
err = netlink.LinkSetUp(link)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set tap interface up: %v", err)
|
||||
}
|
||||
|
||||
tap.Mac = link.Attrs().HardwareAddr.String()
|
||||
tap.Sandbox = netns.Path()
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tap, nil
|
||||
}
|
||||
|
||||
func cmdAdd(args *skel.CmdArgs) error {
|
||||
n, cniVersion, err := loadConf(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isLayer3 := n.IPAM.Type != ""
|
||||
|
||||
netns, err := ns.GetNS(args.Netns)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open netns %q: %v", netns, err)
|
||||
}
|
||||
defer netns.Close()
|
||||
|
||||
tapInterface, err := createTap(n, args.IfName, netns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete link if err to avoid link leak in this ns
|
||||
defer func() {
|
||||
if err != nil {
|
||||
netns.Do(func(_ ns.NetNS) error {
|
||||
return ip.DelLinkByName(args.IfName)
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
||||
// Assume L2 interface only
|
||||
result := ¤t.Result{
|
||||
CNIVersion: current.ImplementedSpecVersion,
|
||||
Interfaces: []*current.Interface{tapInterface},
|
||||
}
|
||||
|
||||
if isLayer3 {
|
||||
// run the IPAM plugin and get back the config to apply
|
||||
r, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Invoke ipam del if err to avoid ip leak
|
||||
defer func() {
|
||||
if err != nil {
|
||||
ipam.ExecDel(n.IPAM.Type, args.StdinData)
|
||||
}
|
||||
}()
|
||||
|
||||
// Convert whatever the IPAM result was into the current Result type
|
||||
ipamResult, err := current.NewResultFromResult(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(ipamResult.IPs) == 0 {
|
||||
return errors.New("IPAM plugin returned missing IP config")
|
||||
}
|
||||
|
||||
result.IPs = ipamResult.IPs
|
||||
result.Routes = ipamResult.Routes
|
||||
|
||||
for _, ipc := range result.IPs {
|
||||
// All addresses apply to the container tap interface
|
||||
ipc.Interface = current.Int(0)
|
||||
}
|
||||
|
||||
err = netns.Do(func(_ ns.NetNS) error {
|
||||
_, _ = sysctl.Sysctl(fmt.Sprintf("net/ipv4/conf/%s/arp_notify", args.IfName), "1")
|
||||
|
||||
if err := ipam.ConfigureIface(args.IfName, result); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// For L2 just change interface status to up
|
||||
err = netns.Do(func(_ ns.NetNS) error {
|
||||
tapInterfaceLink, err := netlink.LinkByName(args.IfName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find interface name %q: %v", tapInterface.Name, err)
|
||||
}
|
||||
if err := netlink.LinkSetUp(tapInterfaceLink); err != nil {
|
||||
return fmt.Errorf("failed to set %q UP: %v", args.IfName, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
result.DNS = n.DNS
|
||||
return types.PrintResult(result, cniVersion)
|
||||
}
|
||||
|
||||
func cmdDel(args *skel.CmdArgs) error {
|
||||
n, _, err := loadConf(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isLayer3 := n.IPAM.Type != ""
|
||||
|
||||
if isLayer3 {
|
||||
err = ipam.ExecDel(n.IPAM.Type, args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if args.Netns == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// There is a netns so try to clean up. Delete can be called multiple times
|
||||
// so don't return an error if the device is already removed.
|
||||
err = ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
||||
if err := ip.DelLinkByName(args.IfName); err != nil {
|
||||
if err != ip.ErrLinkNotFound {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
|
||||
// so don't return an error if the device is already removed.
|
||||
// https://github.com/kubernetes/kubernetes/issues/43014#issuecomment-287164444
|
||||
_, ok := err.(ns.NSPathNotExistErr)
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func main() {
|
||||
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("tap"))
|
||||
}
|
||||
|
||||
func cmdCheck(args *skel.CmdArgs) error {
|
||||
n, _, err := loadConf(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
isLayer3 := n.IPAM.Type != ""
|
||||
|
||||
netns, err := ns.GetNS(args.Netns)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open netns %q: %v", args.Netns, err)
|
||||
}
|
||||
defer netns.Close()
|
||||
|
||||
if isLayer3 {
|
||||
// run the IPAM plugin and get back the config to apply
|
||||
err = ipam.ExecCheck(n.IPAM.Type, args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Parse previous result.
|
||||
if n.NetConf.RawPrevResult == nil {
|
||||
return fmt.Errorf("Required prevResult missing")
|
||||
}
|
||||
|
||||
if err := version.ParsePrevResult(&n.NetConf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result, err := current.NewResultFromResult(n.PrevResult)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var contMap current.Interface
|
||||
// Find interfaces for names whe know, tap device name inside container
|
||||
for _, intf := range result.Interfaces {
|
||||
if args.IfName == intf.Name {
|
||||
if args.Netns == intf.Sandbox {
|
||||
contMap = *intf
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The namespace must be the same as what was configured
|
||||
if args.Netns != contMap.Sandbox {
|
||||
return fmt.Errorf("Sandbox in prevResult %s doesn't match configured netns: %s",
|
||||
contMap.Sandbox, args.Netns)
|
||||
}
|
||||
|
||||
// Check prevResults for ips, routes and dns against values found in the container
|
||||
if err := netns.Do(func(_ ns.NetNS) error {
|
||||
err = ip.ValidateExpectedInterfaceIPs(args.IfName, result.IPs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ip.ValidateExpectedRoute(result.Routes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
27
plugins/main/tap/tap_suite_test.go
Normal file
27
plugins/main/tap/tap_suite_test.go
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2022 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestTap(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "plugins/meta/tap")
|
||||
}
|
319
plugins/main/tap/tap_test.go
Normal file
319
plugins/main/tap/tap_test.go
Normal file
@ -0,0 +1,319 @@
|
||||
// Copyright 2022 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
types020 "github.com/containernetworking/cni/pkg/types/020"
|
||||
types040 "github.com/containernetworking/cni/pkg/types/040"
|
||||
types100 "github.com/containernetworking/cni/pkg/types/100"
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/containernetworking/plugins/pkg/testutils"
|
||||
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend/allocator"
|
||||
)
|
||||
|
||||
const (
|
||||
TYPETAP = "tuntap"
|
||||
IFNAME = "tapifc"
|
||||
)
|
||||
|
||||
type Net struct {
|
||||
Name string `json:"name"`
|
||||
CNIVersion string `json:"cniVersion"`
|
||||
Type string `json:"type,omitempty"`
|
||||
IPAM *allocator.IPAMConfig `json:"ipam"`
|
||||
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
|
||||
PrevResult types100.Result `json:"-"`
|
||||
}
|
||||
|
||||
func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult types.Result) (*Net, error) {
|
||||
var err error
|
||||
|
||||
inject := map[string]interface{}{
|
||||
"name": netName,
|
||||
"cniVersion": cniVersion,
|
||||
}
|
||||
// Add previous plugin result
|
||||
if prevResult != nil {
|
||||
inject["prevResult"] = prevResult
|
||||
}
|
||||
|
||||
// Ensure every config uses the same name and version
|
||||
config := make(map[string]interface{})
|
||||
|
||||
confBytes, err := json.Marshal(orig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(confBytes, &config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unmarshal existing network bytes: %s", err)
|
||||
}
|
||||
|
||||
for key, value := range inject {
|
||||
config[key] = value
|
||||
}
|
||||
|
||||
newBytes, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf := &Net{}
|
||||
if err := json.Unmarshal(newBytes, &conf); err != nil {
|
||||
return nil, fmt.Errorf("error parsing configuration: %s", err)
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
|
||||
}
|
||||
|
||||
type tester interface {
|
||||
// verifyResult minimally verifies the Result and returns the interface's MAC address
|
||||
verifyResult(result types.Result, name string) string
|
||||
}
|
||||
|
||||
type testerBase struct{}
|
||||
|
||||
type testerV10x testerBase
|
||||
type testerV04x testerBase
|
||||
type testerV03x testerBase
|
||||
type testerV01xOr02x testerBase
|
||||
|
||||
func newTesterByVersion(version string) tester {
|
||||
switch {
|
||||
case strings.HasPrefix(version, "1.0."):
|
||||
return &testerV10x{}
|
||||
case strings.HasPrefix(version, "0.4."):
|
||||
return &testerV04x{}
|
||||
case strings.HasPrefix(version, "0.3."):
|
||||
return &testerV03x{}
|
||||
default:
|
||||
return &testerV01xOr02x{}
|
||||
}
|
||||
}
|
||||
|
||||
// verifyResult minimally verifies the Result and returns the interface's MAC address
|
||||
func (t *testerV10x) verifyResult(result types.Result, name string) string {
|
||||
r, err := types100.GetResult(result)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(len(r.Interfaces)).To(Equal(1))
|
||||
Expect(r.Interfaces[0].Name).To(Equal(name))
|
||||
Expect(len(r.IPs)).To(Equal(1))
|
||||
|
||||
return r.Interfaces[0].Mac
|
||||
}
|
||||
|
||||
func verify0403(result types.Result, name string) string {
|
||||
r, err := types040.GetResult(result)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(len(r.Interfaces)).To(Equal(1))
|
||||
Expect(r.Interfaces[0].Name).To(Equal(name))
|
||||
Expect(len(r.IPs)).To(Equal(1))
|
||||
|
||||
return r.Interfaces[0].Mac
|
||||
}
|
||||
|
||||
// verifyResult minimally verifies the Result and returns the interface's MAC address
|
||||
func (t *testerV04x) verifyResult(result types.Result, name string) string {
|
||||
return verify0403(result, name)
|
||||
}
|
||||
|
||||
// verifyResult minimally verifies the Result and returns the interface's MAC address
|
||||
func (t *testerV03x) verifyResult(result types.Result, name string) string {
|
||||
return verify0403(result, name)
|
||||
}
|
||||
|
||||
// verifyResult minimally verifies the Result and returns the interface's MAC address
|
||||
func (t *testerV01xOr02x) verifyResult(result types.Result, name string) string {
|
||||
r, err := types020.GetResult(result)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(r.IP4.IP.IP).NotTo(BeNil())
|
||||
Expect(r.IP6).To(BeNil())
|
||||
|
||||
// 0.2 and earlier don't return MAC address
|
||||
return ""
|
||||
}
|
||||
|
||||
// Note: the tests might not work with some security modules (like selinux) enabled
|
||||
// To test with security modules enabled please provide an appropriate plugin for creating the tap device
|
||||
var _ = Describe("Add, check, remove tap plugin", func() {
|
||||
var originalNS, targetNS ns.NetNS
|
||||
var dataDir string
|
||||
|
||||
BeforeEach(func() {
|
||||
// Create a new NetNS so we don't modify the host
|
||||
var err error
|
||||
originalNS, err = testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
targetNS, err = testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
dataDir, err = ioutil.TempDir("", "dummy")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(os.RemoveAll(dataDir)).To(Succeed())
|
||||
Expect(originalNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(originalNS)).To(Succeed())
|
||||
Expect(targetNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(targetNS)).To(Succeed())
|
||||
})
|
||||
|
||||
for _, ver := range testutils.AllSpecVersions {
|
||||
ver := ver
|
||||
|
||||
It(fmt.Sprintf("[%s] add, check and remove a tap device run correctly", ver), func() {
|
||||
conf := fmt.Sprintf(`{
|
||||
"cniVersion": "%s",
|
||||
"name": "tapTest",
|
||||
"type": "tap",
|
||||
"owner": 0,
|
||||
"group": 0,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24",
|
||||
"dataDir": "%s"
|
||||
}
|
||||
}`, ver, dataDir)
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: targetNS.Path(),
|
||||
IfName: IFNAME,
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
|
||||
t := newTesterByVersion(ver)
|
||||
|
||||
var result types.Result
|
||||
var macAddress string
|
||||
var err error
|
||||
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
result, _, err = testutils.CmdAddWithArgs(args, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
macAddress = t.verifyResult(result, IFNAME)
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Make sure the tap link exists in the target namespace")
|
||||
err = targetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
link, err := netlink.LinkByName(IFNAME)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(link.Attrs().Name).To(Equal(IFNAME))
|
||||
Expect(link.Type()).To(Equal(TYPETAP))
|
||||
|
||||
if macAddress != "" {
|
||||
hwaddr, err := net.ParseMAC(macAddress)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr))
|
||||
}
|
||||
addrs, err := netlink.AddrList(link, syscall.AF_INET)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(len(addrs)).To(Equal(1))
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Running cmdCheck")
|
||||
n := &Net{}
|
||||
err = json.Unmarshal([]byte(conf), &n)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
n.IPAM, _, err = allocator.LoadIPAMConfig([]byte(conf), "")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
newConf, err := buildOneConfig("tapTest", ver, n, result)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
confString, err := json.Marshal(newConf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
args.StdinData = confString
|
||||
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
return testutils.CmdCheckWithArgs(args, func() error { return cmdCheck(args) })
|
||||
})
|
||||
|
||||
if testutils.SpecVersionHasCHECK(ver) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
} else {
|
||||
Expect(err).To(MatchError("config version does not allow CHECK"))
|
||||
}
|
||||
|
||||
By("Running cmdDel")
|
||||
args.StdinData = []byte(conf)
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
err = testutils.CmdDelWithArgs(args, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = targetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
link, err := netlink.LinkByName(IFNAME)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(link).To(BeNil())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("Running cmdDel more than once without error")
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
err = testutils.CmdDelWithArgs(args, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
}
|
||||
})
|
26
vendor/github.com/bits-and-blooms/bitset/.gitignore
generated
vendored
Normal file
26
vendor/github.com/bits-and-blooms/bitset/.gitignore
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
target
|
37
vendor/github.com/bits-and-blooms/bitset/.travis.yml
generated
vendored
Normal file
37
vendor/github.com/bits-and-blooms/bitset/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
language: go
|
||||
|
||||
sudo: false
|
||||
|
||||
branches:
|
||||
except:
|
||||
- release
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- travis
|
||||
|
||||
go:
|
||||
- "1.11.x"
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
||||
before_install:
|
||||
- if [ -n "$GH_USER" ]; then git config --global github.user ${GH_USER}; fi;
|
||||
- if [ -n "$GH_TOKEN" ]; then git config --global github.token ${GH_TOKEN}; fi;
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
before_script:
|
||||
- make deps
|
||||
|
||||
script:
|
||||
- make qa
|
||||
|
||||
after_failure:
|
||||
- cat ./target/test/report.xml
|
||||
|
||||
after_success:
|
||||
- if [ "$TRAVIS_GO_VERSION" = "1.11.1" ]; then $HOME/gopath/bin/goveralls -covermode=count -coverprofile=target/report/coverage.out -service=travis-ci; fi;
|
27
vendor/github.com/bits-and-blooms/bitset/LICENSE
generated
vendored
Normal file
27
vendor/github.com/bits-and-blooms/bitset/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2014 Will Fitzgerald. 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.
|
93
vendor/github.com/bits-and-blooms/bitset/README.md
generated
vendored
Normal file
93
vendor/github.com/bits-and-blooms/bitset/README.md
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
# bitset
|
||||
|
||||
*Go language library to map between non-negative integers and boolean values*
|
||||
|
||||
[](https://github.com/willf/bitset/actions?query=workflow%3ATest)
|
||||
[](https://goreportcard.com/report/github.com/willf/bitset)
|
||||
[](https://pkg.go.dev/github.com/bits-and-blooms/bitset?tab=doc)
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
Package bitset implements bitsets, a mapping between non-negative integers and boolean values.
|
||||
It should be more efficient than map[uint] bool.
|
||||
|
||||
It provides methods for setting, clearing, flipping, and testing individual integers.
|
||||
|
||||
But it also provides set intersection, union, difference, complement, and symmetric operations, as well as tests to check whether any, all, or no bits are set, and querying a bitset's current length and number of positive bits.
|
||||
|
||||
BitSets are expanded to the size of the largest set bit; the memory allocation is approximately Max bits, where Max is the largest set bit. BitSets are never shrunk. On creation, a hint can be given for the number of bits that will be used.
|
||||
|
||||
Many of the methods, including Set, Clear, and Flip, return a BitSet pointer, which allows for chaining.
|
||||
|
||||
### Example use:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/bits-and-blooms/bitset"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Hello from BitSet!\n")
|
||||
var b bitset.BitSet
|
||||
// play some Go Fish
|
||||
for i := 0; i < 100; i++ {
|
||||
card1 := uint(rand.Intn(52))
|
||||
card2 := uint(rand.Intn(52))
|
||||
b.Set(card1)
|
||||
if b.Test(card2) {
|
||||
fmt.Println("Go Fish!")
|
||||
}
|
||||
b.Clear(card1)
|
||||
}
|
||||
|
||||
// Chaining
|
||||
b.Set(10).Set(11)
|
||||
|
||||
for i, e := b.NextSet(0); e; i, e = b.NextSet(i + 1) {
|
||||
fmt.Println("The following bit is set:", i)
|
||||
}
|
||||
if b.Intersection(bitset.New(100).Set(10)).Count() == 1 {
|
||||
fmt.Println("Intersection works.")
|
||||
} else {
|
||||
fmt.Println("Intersection doesn't work???")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As an alternative to BitSets, one should check out the 'big' package, which provides a (less set-theoretical) view of bitsets.
|
||||
|
||||
Package documentation is at: https://pkg.go.dev/github.com/bits-and-blooms/bitset?tab=doc
|
||||
|
||||
## Memory Usage
|
||||
|
||||
The memory usage of a bitset using N bits is at least N/8 bytes. The number of bits in a bitset is at least as large as one plus the greatest bit index you have accessed. Thus it is possible to run out of memory while using a bitset. If you have lots of bits, you might prefer compressed bitsets, like the [Roaring bitmaps](http://roaringbitmap.org) and its [Go implementation](https://github.com/RoaringBitmap/roaring).
|
||||
|
||||
## Implementation Note
|
||||
|
||||
Go 1.9 introduced a native `math/bits` library. We provide backward compatibility to Go 1.7, which might be removed.
|
||||
|
||||
It is possible that a later version will match the `math/bits` return signature for counts (which is `int`, rather than our library's `unit64`). If so, the version will be bumped.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
go get github.com/bits-and-blooms/bitset
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
If you wish to contribute to this project, please branch and issue a pull request against master ("[GitHub Flow](https://guides.github.com/introduction/flow/)")
|
||||
|
||||
## Running all tests
|
||||
|
||||
Before committing the code, please check if it passes tests, has adequate coverage, etc.
|
||||
```bash
|
||||
go test
|
||||
go test -cover
|
||||
```
|
39
vendor/github.com/bits-and-blooms/bitset/azure-pipelines.yml
generated
vendored
Normal file
39
vendor/github.com/bits-and-blooms/bitset/azure-pipelines.yml
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
# Go
|
||||
# Build your Go project.
|
||||
# Add steps that test, save build artifacts, deploy, and more:
|
||||
# https://docs.microsoft.com/azure/devops/pipelines/languages/go
|
||||
|
||||
trigger:
|
||||
- master
|
||||
|
||||
pool:
|
||||
vmImage: 'Ubuntu-16.04'
|
||||
|
||||
variables:
|
||||
GOBIN: '$(GOPATH)/bin' # Go binaries path
|
||||
GOROOT: '/usr/local/go1.11' # Go installation path
|
||||
GOPATH: '$(system.defaultWorkingDirectory)/gopath' # Go workspace path
|
||||
modulePath: '$(GOPATH)/src/github.com/$(build.repository.name)' # Path to the module's code
|
||||
|
||||
steps:
|
||||
- script: |
|
||||
mkdir -p '$(GOBIN)'
|
||||
mkdir -p '$(GOPATH)/pkg'
|
||||
mkdir -p '$(modulePath)'
|
||||
shopt -s extglob
|
||||
shopt -s dotglob
|
||||
mv !(gopath) '$(modulePath)'
|
||||
echo '##vso[task.prependpath]$(GOBIN)'
|
||||
echo '##vso[task.prependpath]$(GOROOT)/bin'
|
||||
displayName: 'Set up the Go workspace'
|
||||
|
||||
- script: |
|
||||
go version
|
||||
go get -v -t -d ./...
|
||||
if [ -f Gopkg.toml ]; then
|
||||
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
||||
dep ensure
|
||||
fi
|
||||
go build -v .
|
||||
workingDirectory: '$(modulePath)'
|
||||
displayName: 'Get dependencies, then build'
|
952
vendor/github.com/bits-and-blooms/bitset/bitset.go
generated
vendored
Normal file
952
vendor/github.com/bits-and-blooms/bitset/bitset.go
generated
vendored
Normal file
@ -0,0 +1,952 @@
|
||||
/*
|
||||
Package bitset implements bitsets, a mapping
|
||||
between non-negative integers and boolean values. It should be more
|
||||
efficient than map[uint] bool.
|
||||
|
||||
It provides methods for setting, clearing, flipping, and testing
|
||||
individual integers.
|
||||
|
||||
But it also provides set intersection, union, difference,
|
||||
complement, and symmetric operations, as well as tests to
|
||||
check whether any, all, or no bits are set, and querying a
|
||||
bitset's current length and number of positive bits.
|
||||
|
||||
BitSets are expanded to the size of the largest set bit; the
|
||||
memory allocation is approximately Max bits, where Max is
|
||||
the largest set bit. BitSets are never shrunk. On creation,
|
||||
a hint can be given for the number of bits that will be used.
|
||||
|
||||
Many of the methods, including Set,Clear, and Flip, return
|
||||
a BitSet pointer, which allows for chaining.
|
||||
|
||||
Example use:
|
||||
|
||||
import "bitset"
|
||||
var b BitSet
|
||||
b.Set(10).Set(11)
|
||||
if b.Test(1000) {
|
||||
b.Clear(1000)
|
||||
}
|
||||
if B.Intersection(bitset.New(100).Set(10)).Count() > 1 {
|
||||
fmt.Println("Intersection works.")
|
||||
}
|
||||
|
||||
As an alternative to BitSets, one should check out the 'big' package,
|
||||
which provides a (less set-theoretical) view of bitsets.
|
||||
|
||||
*/
|
||||
package bitset
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// the wordSize of a bit set
|
||||
const wordSize = uint(64)
|
||||
|
||||
// log2WordSize is lg(wordSize)
|
||||
const log2WordSize = uint(6)
|
||||
|
||||
// allBits has every bit set
|
||||
const allBits uint64 = 0xffffffffffffffff
|
||||
|
||||
// default binary BigEndian
|
||||
var binaryOrder binary.ByteOrder = binary.BigEndian
|
||||
|
||||
// default json encoding base64.URLEncoding
|
||||
var base64Encoding = base64.URLEncoding
|
||||
|
||||
// Base64StdEncoding Marshal/Unmarshal BitSet with base64.StdEncoding(Default: base64.URLEncoding)
|
||||
func Base64StdEncoding() { base64Encoding = base64.StdEncoding }
|
||||
|
||||
// LittleEndian Marshal/Unmarshal Binary as Little Endian(Default: binary.BigEndian)
|
||||
func LittleEndian() { binaryOrder = binary.LittleEndian }
|
||||
|
||||
// A BitSet is a set of bits. The zero value of a BitSet is an empty set of length 0.
|
||||
type BitSet struct {
|
||||
length uint
|
||||
set []uint64
|
||||
}
|
||||
|
||||
// Error is used to distinguish errors (panics) generated in this package.
|
||||
type Error string
|
||||
|
||||
// safeSet will fixup b.set to be non-nil and return the field value
|
||||
func (b *BitSet) safeSet() []uint64 {
|
||||
if b.set == nil {
|
||||
b.set = make([]uint64, wordsNeeded(0))
|
||||
}
|
||||
return b.set
|
||||
}
|
||||
|
||||
// From is a constructor used to create a BitSet from an array of integers
|
||||
func From(buf []uint64) *BitSet {
|
||||
return &BitSet{uint(len(buf)) * 64, buf}
|
||||
}
|
||||
|
||||
// Bytes returns the bitset as array of integers
|
||||
func (b *BitSet) Bytes() []uint64 {
|
||||
return b.set
|
||||
}
|
||||
|
||||
// wordsNeeded calculates the number of words needed for i bits
|
||||
func wordsNeeded(i uint) int {
|
||||
if i > (Cap() - wordSize + 1) {
|
||||
return int(Cap() >> log2WordSize)
|
||||
}
|
||||
return int((i + (wordSize - 1)) >> log2WordSize)
|
||||
}
|
||||
|
||||
// New creates a new BitSet with a hint that length bits will be required
|
||||
func New(length uint) (bset *BitSet) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
bset = &BitSet{
|
||||
0,
|
||||
make([]uint64, 0),
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
bset = &BitSet{
|
||||
length,
|
||||
make([]uint64, wordsNeeded(length)),
|
||||
}
|
||||
|
||||
return bset
|
||||
}
|
||||
|
||||
// Cap returns the total possible capacity, or number of bits
|
||||
func Cap() uint {
|
||||
return ^uint(0)
|
||||
}
|
||||
|
||||
// Len returns the number of bits in the BitSet.
|
||||
// Note the difference to method Count, see example.
|
||||
func (b *BitSet) Len() uint {
|
||||
return b.length
|
||||
}
|
||||
|
||||
// extendSetMaybe adds additional words to incorporate new bits if needed
|
||||
func (b *BitSet) extendSetMaybe(i uint) {
|
||||
if i >= b.length { // if we need more bits, make 'em
|
||||
if i >= Cap() {
|
||||
panic("You are exceeding the capacity")
|
||||
}
|
||||
nsize := wordsNeeded(i + 1)
|
||||
if b.set == nil {
|
||||
b.set = make([]uint64, nsize)
|
||||
} else if cap(b.set) >= nsize {
|
||||
b.set = b.set[:nsize] // fast resize
|
||||
} else if len(b.set) < nsize {
|
||||
newset := make([]uint64, nsize, 2*nsize) // increase capacity 2x
|
||||
copy(newset, b.set)
|
||||
b.set = newset
|
||||
}
|
||||
b.length = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
// Test whether bit i is set.
|
||||
func (b *BitSet) Test(i uint) bool {
|
||||
if i >= b.length {
|
||||
return false
|
||||
}
|
||||
return b.set[i>>log2WordSize]&(1<<(i&(wordSize-1))) != 0
|
||||
}
|
||||
|
||||
// Set bit i to 1, the capacity of the bitset is automatically
|
||||
// increased accordingly.
|
||||
// If i>= Cap(), this function will panic.
|
||||
// Warning: using a very large value for 'i'
|
||||
// may lead to a memory shortage and a panic: the caller is responsible
|
||||
// for providing sensible parameters in line with their memory capacity.
|
||||
func (b *BitSet) Set(i uint) *BitSet {
|
||||
b.extendSetMaybe(i)
|
||||
b.set[i>>log2WordSize] |= 1 << (i & (wordSize - 1))
|
||||
return b
|
||||
}
|
||||
|
||||
// Clear bit i to 0
|
||||
func (b *BitSet) Clear(i uint) *BitSet {
|
||||
if i >= b.length {
|
||||
return b
|
||||
}
|
||||
b.set[i>>log2WordSize] &^= 1 << (i & (wordSize - 1))
|
||||
return b
|
||||
}
|
||||
|
||||
// SetTo sets bit i to value.
|
||||
// If i>= Cap(), this function will panic.
|
||||
// Warning: using a very large value for 'i'
|
||||
// may lead to a memory shortage and a panic: the caller is responsible
|
||||
// for providing sensible parameters in line with their memory capacity.
|
||||
func (b *BitSet) SetTo(i uint, value bool) *BitSet {
|
||||
if value {
|
||||
return b.Set(i)
|
||||
}
|
||||
return b.Clear(i)
|
||||
}
|
||||
|
||||
// Flip bit at i.
|
||||
// If i>= Cap(), this function will panic.
|
||||
// Warning: using a very large value for 'i'
|
||||
// may lead to a memory shortage and a panic: the caller is responsible
|
||||
// for providing sensible parameters in line with their memory capacity.
|
||||
func (b *BitSet) Flip(i uint) *BitSet {
|
||||
if i >= b.length {
|
||||
return b.Set(i)
|
||||
}
|
||||
b.set[i>>log2WordSize] ^= 1 << (i & (wordSize - 1))
|
||||
return b
|
||||
}
|
||||
|
||||
// FlipRange bit in [start, end).
|
||||
// If end>= Cap(), this function will panic.
|
||||
// Warning: using a very large value for 'end'
|
||||
// may lead to a memory shortage and a panic: the caller is responsible
|
||||
// for providing sensible parameters in line with their memory capacity.
|
||||
func (b *BitSet) FlipRange(start, end uint) *BitSet {
|
||||
if start >= end {
|
||||
return b
|
||||
}
|
||||
|
||||
b.extendSetMaybe(end - 1)
|
||||
var startWord uint = start >> log2WordSize
|
||||
var endWord uint = end >> log2WordSize
|
||||
b.set[startWord] ^= ^(^uint64(0) << (start & (wordSize - 1)))
|
||||
for i := startWord; i < endWord; i++ {
|
||||
b.set[i] = ^b.set[i]
|
||||
}
|
||||
b.set[endWord] ^= ^uint64(0) >> (-end & (wordSize - 1))
|
||||
return b
|
||||
}
|
||||
|
||||
// Shrink shrinks BitSet so that the provided value is the last possible
|
||||
// set value. It clears all bits > the provided index and reduces the size
|
||||
// and length of the set.
|
||||
//
|
||||
// Note that the parameter value is not the new length in bits: it is the
|
||||
// maximal value that can be stored in the bitset after the function call.
|
||||
// The new length in bits is the parameter value + 1. Thus it is not possible
|
||||
// to use this function to set the length to 0, the minimal value of the length
|
||||
// after this function call is 1.
|
||||
//
|
||||
// A new slice is allocated to store the new bits, so you may see an increase in
|
||||
// memory usage until the GC runs. Normally this should not be a problem, but if you
|
||||
// have an extremely large BitSet its important to understand that the old BitSet will
|
||||
// remain in memory until the GC frees it.
|
||||
func (b *BitSet) Shrink(lastbitindex uint) *BitSet {
|
||||
length := lastbitindex + 1
|
||||
idx := wordsNeeded(length)
|
||||
if idx > len(b.set) {
|
||||
return b
|
||||
}
|
||||
shrunk := make([]uint64, idx)
|
||||
copy(shrunk, b.set[:idx])
|
||||
b.set = shrunk
|
||||
b.length = length
|
||||
b.set[idx-1] &= (allBits >> (uint64(64) - uint64(length&(wordSize-1))))
|
||||
return b
|
||||
}
|
||||
|
||||
// Compact shrinks BitSet to so that we preserve all set bits, while minimizing
|
||||
// memory usage. Compact calls Shrink.
|
||||
func (b *BitSet) Compact() *BitSet {
|
||||
idx := len(b.set) - 1
|
||||
for ; idx >= 0 && b.set[idx] == 0; idx-- {
|
||||
}
|
||||
newlength := uint((idx + 1) << log2WordSize)
|
||||
if newlength >= b.length {
|
||||
return b // nothing to do
|
||||
}
|
||||
if newlength > 0 {
|
||||
return b.Shrink(newlength - 1)
|
||||
}
|
||||
// We preserve one word
|
||||
return b.Shrink(63)
|
||||
}
|
||||
|
||||
// InsertAt takes an index which indicates where a bit should be
|
||||
// inserted. Then it shifts all the bits in the set to the left by 1, starting
|
||||
// from the given index position, and sets the index position to 0.
|
||||
//
|
||||
// Depending on the size of your BitSet, and where you are inserting the new entry,
|
||||
// this method could be extremely slow and in some cases might cause the entire BitSet
|
||||
// to be recopied.
|
||||
func (b *BitSet) InsertAt(idx uint) *BitSet {
|
||||
insertAtElement := (idx >> log2WordSize)
|
||||
|
||||
// if length of set is a multiple of wordSize we need to allocate more space first
|
||||
if b.isLenExactMultiple() {
|
||||
b.set = append(b.set, uint64(0))
|
||||
}
|
||||
|
||||
var i uint
|
||||
for i = uint(len(b.set) - 1); i > insertAtElement; i-- {
|
||||
// all elements above the position where we want to insert can simply by shifted
|
||||
b.set[i] <<= 1
|
||||
|
||||
// we take the most significant bit of the previous element and set it as
|
||||
// the least significant bit of the current element
|
||||
b.set[i] |= (b.set[i-1] & 0x8000000000000000) >> 63
|
||||
}
|
||||
|
||||
// generate a mask to extract the data that we need to shift left
|
||||
// within the element where we insert a bit
|
||||
dataMask := ^(uint64(1)<<uint64(idx&(wordSize-1)) - 1)
|
||||
|
||||
// extract that data that we'll shift
|
||||
data := b.set[i] & dataMask
|
||||
|
||||
// set the positions of the data mask to 0 in the element where we insert
|
||||
b.set[i] &= ^dataMask
|
||||
|
||||
// shift data mask to the left and insert its data to the slice element
|
||||
b.set[i] |= data << 1
|
||||
|
||||
// add 1 to length of BitSet
|
||||
b.length++
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// String creates a string representation of the Bitmap
|
||||
func (b *BitSet) String() string {
|
||||
// follows code from https://github.com/RoaringBitmap/roaring
|
||||
var buffer bytes.Buffer
|
||||
start := []byte("{")
|
||||
buffer.Write(start)
|
||||
counter := 0
|
||||
i, e := b.NextSet(0)
|
||||
for e {
|
||||
counter = counter + 1
|
||||
// to avoid exhausting the memory
|
||||
if counter > 0x40000 {
|
||||
buffer.WriteString("...")
|
||||
break
|
||||
}
|
||||
buffer.WriteString(strconv.FormatInt(int64(i), 10))
|
||||
i, e = b.NextSet(i + 1)
|
||||
if e {
|
||||
buffer.WriteString(",")
|
||||
}
|
||||
}
|
||||
buffer.WriteString("}")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// DeleteAt deletes the bit at the given index position from
|
||||
// within the bitset
|
||||
// All the bits residing on the left of the deleted bit get
|
||||
// shifted right by 1
|
||||
// The running time of this operation may potentially be
|
||||
// relatively slow, O(length)
|
||||
func (b *BitSet) DeleteAt(i uint) *BitSet {
|
||||
// the index of the slice element where we'll delete a bit
|
||||
deleteAtElement := i >> log2WordSize
|
||||
|
||||
// generate a mask for the data that needs to be shifted right
|
||||
// within that slice element that gets modified
|
||||
dataMask := ^((uint64(1) << (i & (wordSize - 1))) - 1)
|
||||
|
||||
// extract the data that we'll shift right from the slice element
|
||||
data := b.set[deleteAtElement] & dataMask
|
||||
|
||||
// set the masked area to 0 while leaving the rest as it is
|
||||
b.set[deleteAtElement] &= ^dataMask
|
||||
|
||||
// shift the previously extracted data to the right and then
|
||||
// set it in the previously masked area
|
||||
b.set[deleteAtElement] |= (data >> 1) & dataMask
|
||||
|
||||
// loop over all the consecutive slice elements to copy each
|
||||
// lowest bit into the highest position of the previous element,
|
||||
// then shift the entire content to the right by 1
|
||||
for i := int(deleteAtElement) + 1; i < len(b.set); i++ {
|
||||
b.set[i-1] |= (b.set[i] & 1) << 63
|
||||
b.set[i] >>= 1
|
||||
}
|
||||
|
||||
b.length = b.length - 1
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// NextSet returns the next bit set from the specified index,
|
||||
// including possibly the current index
|
||||
// along with an error code (true = valid, false = no set bit found)
|
||||
// for i,e := v.NextSet(0); e; i,e = v.NextSet(i + 1) {...}
|
||||
//
|
||||
// Users concerned with performance may want to use NextSetMany to
|
||||
// retrieve several values at once.
|
||||
func (b *BitSet) NextSet(i uint) (uint, bool) {
|
||||
x := int(i >> log2WordSize)
|
||||
if x >= len(b.set) {
|
||||
return 0, false
|
||||
}
|
||||
w := b.set[x]
|
||||
w = w >> (i & (wordSize - 1))
|
||||
if w != 0 {
|
||||
return i + trailingZeroes64(w), true
|
||||
}
|
||||
x = x + 1
|
||||
for x < len(b.set) {
|
||||
if b.set[x] != 0 {
|
||||
return uint(x)*wordSize + trailingZeroes64(b.set[x]), true
|
||||
}
|
||||
x = x + 1
|
||||
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// NextSetMany returns many next bit sets from the specified index,
|
||||
// including possibly the current index and up to cap(buffer).
|
||||
// If the returned slice has len zero, then no more set bits were found
|
||||
//
|
||||
// buffer := make([]uint, 256) // this should be reused
|
||||
// j := uint(0)
|
||||
// j, buffer = bitmap.NextSetMany(j, buffer)
|
||||
// for ; len(buffer) > 0; j, buffer = bitmap.NextSetMany(j,buffer) {
|
||||
// for k := range buffer {
|
||||
// do something with buffer[k]
|
||||
// }
|
||||
// j += 1
|
||||
// }
|
||||
//
|
||||
//
|
||||
// It is possible to retrieve all set bits as follow:
|
||||
//
|
||||
// indices := make([]uint, bitmap.Count())
|
||||
// bitmap.NextSetMany(0, indices)
|
||||
//
|
||||
// However if bitmap.Count() is large, it might be preferable to
|
||||
// use several calls to NextSetMany, for performance reasons.
|
||||
func (b *BitSet) NextSetMany(i uint, buffer []uint) (uint, []uint) {
|
||||
myanswer := buffer
|
||||
capacity := cap(buffer)
|
||||
x := int(i >> log2WordSize)
|
||||
if x >= len(b.set) || capacity == 0 {
|
||||
return 0, myanswer[:0]
|
||||
}
|
||||
skip := i & (wordSize - 1)
|
||||
word := b.set[x] >> skip
|
||||
myanswer = myanswer[:capacity]
|
||||
size := int(0)
|
||||
for word != 0 {
|
||||
r := trailingZeroes64(word)
|
||||
t := word & ((^word) + 1)
|
||||
myanswer[size] = r + i
|
||||
size++
|
||||
if size == capacity {
|
||||
goto End
|
||||
}
|
||||
word = word ^ t
|
||||
}
|
||||
x++
|
||||
for idx, word := range b.set[x:] {
|
||||
for word != 0 {
|
||||
r := trailingZeroes64(word)
|
||||
t := word & ((^word) + 1)
|
||||
myanswer[size] = r + (uint(x+idx) << 6)
|
||||
size++
|
||||
if size == capacity {
|
||||
goto End
|
||||
}
|
||||
word = word ^ t
|
||||
}
|
||||
}
|
||||
End:
|
||||
if size > 0 {
|
||||
return myanswer[size-1], myanswer[:size]
|
||||
}
|
||||
return 0, myanswer[:0]
|
||||
}
|
||||
|
||||
// NextClear returns the next clear bit from the specified index,
|
||||
// including possibly the current index
|
||||
// along with an error code (true = valid, false = no bit found i.e. all bits are set)
|
||||
func (b *BitSet) NextClear(i uint) (uint, bool) {
|
||||
x := int(i >> log2WordSize)
|
||||
if x >= len(b.set) {
|
||||
return 0, false
|
||||
}
|
||||
w := b.set[x]
|
||||
w = w >> (i & (wordSize - 1))
|
||||
wA := allBits >> (i & (wordSize - 1))
|
||||
index := i + trailingZeroes64(^w)
|
||||
if w != wA && index < b.length {
|
||||
return index, true
|
||||
}
|
||||
x++
|
||||
for x < len(b.set) {
|
||||
index = uint(x)*wordSize + trailingZeroes64(^b.set[x])
|
||||
if b.set[x] != allBits && index < b.length {
|
||||
return index, true
|
||||
}
|
||||
x++
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// ClearAll clears the entire BitSet
|
||||
func (b *BitSet) ClearAll() *BitSet {
|
||||
if b != nil && b.set != nil {
|
||||
for i := range b.set {
|
||||
b.set[i] = 0
|
||||
}
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// wordCount returns the number of words used in a bit set
|
||||
func (b *BitSet) wordCount() int {
|
||||
return len(b.set)
|
||||
}
|
||||
|
||||
// Clone this BitSet
|
||||
func (b *BitSet) Clone() *BitSet {
|
||||
c := New(b.length)
|
||||
if b.set != nil { // Clone should not modify current object
|
||||
copy(c.set, b.set)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Copy into a destination BitSet
|
||||
// Returning the size of the destination BitSet
|
||||
// like array copy
|
||||
func (b *BitSet) Copy(c *BitSet) (count uint) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
if b.set != nil { // Copy should not modify current object
|
||||
copy(c.set, b.set)
|
||||
}
|
||||
count = c.length
|
||||
if b.length < c.length {
|
||||
count = b.length
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Count (number of set bits).
|
||||
// Also known as "popcount" or "population count".
|
||||
func (b *BitSet) Count() uint {
|
||||
if b != nil && b.set != nil {
|
||||
return uint(popcntSlice(b.set))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Equal tests the equivalence of two BitSets.
|
||||
// False if they are of different sizes, otherwise true
|
||||
// only if all the same bits are set
|
||||
func (b *BitSet) Equal(c *BitSet) bool {
|
||||
if c == nil || b == nil {
|
||||
return c == b
|
||||
}
|
||||
if b.length != c.length {
|
||||
return false
|
||||
}
|
||||
if b.length == 0 { // if they have both length == 0, then could have nil set
|
||||
return true
|
||||
}
|
||||
// testing for equality shoud not transform the bitset (no call to safeSet)
|
||||
|
||||
for p, v := range b.set {
|
||||
if c.set[p] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func panicIfNull(b *BitSet) {
|
||||
if b == nil {
|
||||
panic(Error("BitSet must not be null"))
|
||||
}
|
||||
}
|
||||
|
||||
// Difference of base set and other set
|
||||
// This is the BitSet equivalent of &^ (and not)
|
||||
func (b *BitSet) Difference(compare *BitSet) (result *BitSet) {
|
||||
panicIfNull(b)
|
||||
panicIfNull(compare)
|
||||
result = b.Clone() // clone b (in case b is bigger than compare)
|
||||
l := int(compare.wordCount())
|
||||
if l > int(b.wordCount()) {
|
||||
l = int(b.wordCount())
|
||||
}
|
||||
for i := 0; i < l; i++ {
|
||||
result.set[i] = b.set[i] &^ compare.set[i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DifferenceCardinality computes the cardinality of the differnce
|
||||
func (b *BitSet) DifferenceCardinality(compare *BitSet) uint {
|
||||
panicIfNull(b)
|
||||
panicIfNull(compare)
|
||||
l := int(compare.wordCount())
|
||||
if l > int(b.wordCount()) {
|
||||
l = int(b.wordCount())
|
||||
}
|
||||
cnt := uint64(0)
|
||||
cnt += popcntMaskSlice(b.set[:l], compare.set[:l])
|
||||
cnt += popcntSlice(b.set[l:])
|
||||
return uint(cnt)
|
||||
}
|
||||
|
||||
// InPlaceDifference computes the difference of base set and other set
|
||||
// This is the BitSet equivalent of &^ (and not)
|
||||
func (b *BitSet) InPlaceDifference(compare *BitSet) {
|
||||
panicIfNull(b)
|
||||
panicIfNull(compare)
|
||||
l := int(compare.wordCount())
|
||||
if l > int(b.wordCount()) {
|
||||
l = int(b.wordCount())
|
||||
}
|
||||
for i := 0; i < l; i++ {
|
||||
b.set[i] &^= compare.set[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience function: return two bitsets ordered by
|
||||
// increasing length. Note: neither can be nil
|
||||
func sortByLength(a *BitSet, b *BitSet) (ap *BitSet, bp *BitSet) {
|
||||
if a.length <= b.length {
|
||||
ap, bp = a, b
|
||||
} else {
|
||||
ap, bp = b, a
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Intersection of base set and other set
|
||||
// This is the BitSet equivalent of & (and)
|
||||
func (b *BitSet) Intersection(compare *BitSet) (result *BitSet) {
|
||||
panicIfNull(b)
|
||||
panicIfNull(compare)
|
||||
b, compare = sortByLength(b, compare)
|
||||
result = New(b.length)
|
||||
for i, word := range b.set {
|
||||
result.set[i] = word & compare.set[i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IntersectionCardinality computes the cardinality of the union
|
||||
func (b *BitSet) IntersectionCardinality(compare *BitSet) uint {
|
||||
panicIfNull(b)
|
||||
panicIfNull(compare)
|
||||
b, compare = sortByLength(b, compare)
|
||||
cnt := popcntAndSlice(b.set, compare.set)
|
||||
return uint(cnt)
|
||||
}
|
||||
|
||||
// InPlaceIntersection destructively computes the intersection of
|
||||
// base set and the compare set.
|
||||
// This is the BitSet equivalent of & (and)
|
||||
func (b *BitSet) InPlaceIntersection(compare *BitSet) {
|
||||
panicIfNull(b)
|
||||
panicIfNull(compare)
|
||||
l := int(compare.wordCount())
|
||||
if l > int(b.wordCount()) {
|
||||
l = int(b.wordCount())
|
||||
}
|
||||
for i := 0; i < l; i++ {
|
||||
b.set[i] &= compare.set[i]
|
||||
}
|
||||
for i := l; i < len(b.set); i++ {
|
||||
b.set[i] = 0
|
||||
}
|
||||
if compare.length > 0 {
|
||||
b.extendSetMaybe(compare.length - 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Union of base set and other set
|
||||
// This is the BitSet equivalent of | (or)
|
||||
func (b *BitSet) Union(compare *BitSet) (result *BitSet) {
|
||||
panicIfNull(b)
|
||||
panicIfNull(compare)
|
||||
b, compare = sortByLength(b, compare)
|
||||
result = compare.Clone()
|
||||
for i, word := range b.set {
|
||||
result.set[i] = word | compare.set[i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// UnionCardinality computes the cardinality of the uniton of the base set
|
||||
// and the compare set.
|
||||
func (b *BitSet) UnionCardinality(compare *BitSet) uint {
|
||||
panicIfNull(b)
|
||||
panicIfNull(compare)
|
||||
b, compare = sortByLength(b, compare)
|
||||
cnt := popcntOrSlice(b.set, compare.set)
|
||||
if len(compare.set) > len(b.set) {
|
||||
cnt += popcntSlice(compare.set[len(b.set):])
|
||||
}
|
||||
return uint(cnt)
|
||||
}
|
||||
|
||||
// InPlaceUnion creates the destructive union of base set and compare set.
|
||||
// This is the BitSet equivalent of | (or).
|
||||
func (b *BitSet) InPlaceUnion(compare *BitSet) {
|
||||
panicIfNull(b)
|
||||
panicIfNull(compare)
|
||||
l := int(compare.wordCount())
|
||||
if l > int(b.wordCount()) {
|
||||
l = int(b.wordCount())
|
||||
}
|
||||
if compare.length > 0 {
|
||||
b.extendSetMaybe(compare.length - 1)
|
||||
}
|
||||
for i := 0; i < l; i++ {
|
||||
b.set[i] |= compare.set[i]
|
||||
}
|
||||
if len(compare.set) > l {
|
||||
for i := l; i < len(compare.set); i++ {
|
||||
b.set[i] = compare.set[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SymmetricDifference of base set and other set
|
||||
// This is the BitSet equivalent of ^ (xor)
|
||||
func (b *BitSet) SymmetricDifference(compare *BitSet) (result *BitSet) {
|
||||
panicIfNull(b)
|
||||
panicIfNull(compare)
|
||||
b, compare = sortByLength(b, compare)
|
||||
// compare is bigger, so clone it
|
||||
result = compare.Clone()
|
||||
for i, word := range b.set {
|
||||
result.set[i] = word ^ compare.set[i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SymmetricDifferenceCardinality computes the cardinality of the symmetric difference
|
||||
func (b *BitSet) SymmetricDifferenceCardinality(compare *BitSet) uint {
|
||||
panicIfNull(b)
|
||||
panicIfNull(compare)
|
||||
b, compare = sortByLength(b, compare)
|
||||
cnt := popcntXorSlice(b.set, compare.set)
|
||||
if len(compare.set) > len(b.set) {
|
||||
cnt += popcntSlice(compare.set[len(b.set):])
|
||||
}
|
||||
return uint(cnt)
|
||||
}
|
||||
|
||||
// InPlaceSymmetricDifference creates the destructive SymmetricDifference of base set and other set
|
||||
// This is the BitSet equivalent of ^ (xor)
|
||||
func (b *BitSet) InPlaceSymmetricDifference(compare *BitSet) {
|
||||
panicIfNull(b)
|
||||
panicIfNull(compare)
|
||||
l := int(compare.wordCount())
|
||||
if l > int(b.wordCount()) {
|
||||
l = int(b.wordCount())
|
||||
}
|
||||
if compare.length > 0 {
|
||||
b.extendSetMaybe(compare.length - 1)
|
||||
}
|
||||
for i := 0; i < l; i++ {
|
||||
b.set[i] ^= compare.set[i]
|
||||
}
|
||||
if len(compare.set) > l {
|
||||
for i := l; i < len(compare.set); i++ {
|
||||
b.set[i] = compare.set[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is the length an exact multiple of word sizes?
|
||||
func (b *BitSet) isLenExactMultiple() bool {
|
||||
return b.length%wordSize == 0
|
||||
}
|
||||
|
||||
// Clean last word by setting unused bits to 0
|
||||
func (b *BitSet) cleanLastWord() {
|
||||
if !b.isLenExactMultiple() {
|
||||
b.set[len(b.set)-1] &= allBits >> (wordSize - b.length%wordSize)
|
||||
}
|
||||
}
|
||||
|
||||
// Complement computes the (local) complement of a biset (up to length bits)
|
||||
func (b *BitSet) Complement() (result *BitSet) {
|
||||
panicIfNull(b)
|
||||
result = New(b.length)
|
||||
for i, word := range b.set {
|
||||
result.set[i] = ^word
|
||||
}
|
||||
result.cleanLastWord()
|
||||
return
|
||||
}
|
||||
|
||||
// All returns true if all bits are set, false otherwise. Returns true for
|
||||
// empty sets.
|
||||
func (b *BitSet) All() bool {
|
||||
panicIfNull(b)
|
||||
return b.Count() == b.length
|
||||
}
|
||||
|
||||
// None returns true if no bit is set, false otherwise. Returns true for
|
||||
// empty sets.
|
||||
func (b *BitSet) None() bool {
|
||||
panicIfNull(b)
|
||||
if b != nil && b.set != nil {
|
||||
for _, word := range b.set {
|
||||
if word > 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Any returns true if any bit is set, false otherwise
|
||||
func (b *BitSet) Any() bool {
|
||||
panicIfNull(b)
|
||||
return !b.None()
|
||||
}
|
||||
|
||||
// IsSuperSet returns true if this is a superset of the other set
|
||||
func (b *BitSet) IsSuperSet(other *BitSet) bool {
|
||||
for i, e := other.NextSet(0); e; i, e = other.NextSet(i + 1) {
|
||||
if !b.Test(i) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsStrictSuperSet returns true if this is a strict superset of the other set
|
||||
func (b *BitSet) IsStrictSuperSet(other *BitSet) bool {
|
||||
return b.Count() > other.Count() && b.IsSuperSet(other)
|
||||
}
|
||||
|
||||
// DumpAsBits dumps a bit set as a string of bits
|
||||
func (b *BitSet) DumpAsBits() string {
|
||||
if b.set == nil {
|
||||
return "."
|
||||
}
|
||||
buffer := bytes.NewBufferString("")
|
||||
i := len(b.set) - 1
|
||||
for ; i >= 0; i-- {
|
||||
fmt.Fprintf(buffer, "%064b.", b.set[i])
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// BinaryStorageSize returns the binary storage requirements
|
||||
func (b *BitSet) BinaryStorageSize() int {
|
||||
return binary.Size(uint64(0)) + binary.Size(b.set)
|
||||
}
|
||||
|
||||
// WriteTo writes a BitSet to a stream
|
||||
func (b *BitSet) WriteTo(stream io.Writer) (int64, error) {
|
||||
length := uint64(b.length)
|
||||
|
||||
// Write length
|
||||
err := binary.Write(stream, binaryOrder, length)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Write set
|
||||
err = binary.Write(stream, binaryOrder, b.set)
|
||||
return int64(b.BinaryStorageSize()), err
|
||||
}
|
||||
|
||||
// ReadFrom reads a BitSet from a stream written using WriteTo
|
||||
func (b *BitSet) ReadFrom(stream io.Reader) (int64, error) {
|
||||
var length uint64
|
||||
|
||||
// Read length first
|
||||
err := binary.Read(stream, binaryOrder, &length)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
newset := New(uint(length))
|
||||
|
||||
if uint64(newset.length) != length {
|
||||
return 0, errors.New("unmarshalling error: type mismatch")
|
||||
}
|
||||
|
||||
// Read remaining bytes as set
|
||||
err = binary.Read(stream, binaryOrder, newset.set)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
*b = *newset
|
||||
return int64(b.BinaryStorageSize()), nil
|
||||
}
|
||||
|
||||
// MarshalBinary encodes a BitSet into a binary form and returns the result.
|
||||
func (b *BitSet) MarshalBinary() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
writer := bufio.NewWriter(&buf)
|
||||
|
||||
_, err := b.WriteTo(writer)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
err = writer.Flush()
|
||||
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// UnmarshalBinary decodes the binary form generated by MarshalBinary.
|
||||
func (b *BitSet) UnmarshalBinary(data []byte) error {
|
||||
buf := bytes.NewReader(data)
|
||||
reader := bufio.NewReader(buf)
|
||||
|
||||
_, err := b.ReadFrom(reader)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// MarshalJSON marshals a BitSet as a JSON structure
|
||||
func (b *BitSet) MarshalJSON() ([]byte, error) {
|
||||
buffer := bytes.NewBuffer(make([]byte, 0, b.BinaryStorageSize()))
|
||||
_, err := b.WriteTo(buffer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// URLEncode all bytes
|
||||
return json.Marshal(base64Encoding.EncodeToString(buffer.Bytes()))
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals a BitSet from JSON created using MarshalJSON
|
||||
func (b *BitSet) UnmarshalJSON(data []byte) error {
|
||||
// Unmarshal as string
|
||||
var s string
|
||||
err := json.Unmarshal(data, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// URLDecode string
|
||||
buf, err := base64Encoding.DecodeString(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = b.ReadFrom(bytes.NewReader(buf))
|
||||
return err
|
||||
}
|
53
vendor/github.com/bits-and-blooms/bitset/popcnt.go
generated
vendored
Normal file
53
vendor/github.com/bits-and-blooms/bitset/popcnt.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
package bitset
|
||||
|
||||
// bit population count, take from
|
||||
// https://code.google.com/p/go/issues/detail?id=4988#c11
|
||||
// credit: https://code.google.com/u/arnehormann/
|
||||
func popcount(x uint64) (n uint64) {
|
||||
x -= (x >> 1) & 0x5555555555555555
|
||||
x = (x>>2)&0x3333333333333333 + x&0x3333333333333333
|
||||
x += x >> 4
|
||||
x &= 0x0f0f0f0f0f0f0f0f
|
||||
x *= 0x0101010101010101
|
||||
return x >> 56
|
||||
}
|
||||
|
||||
func popcntSliceGo(s []uint64) uint64 {
|
||||
cnt := uint64(0)
|
||||
for _, x := range s {
|
||||
cnt += popcount(x)
|
||||
}
|
||||
return cnt
|
||||
}
|
||||
|
||||
func popcntMaskSliceGo(s, m []uint64) uint64 {
|
||||
cnt := uint64(0)
|
||||
for i := range s {
|
||||
cnt += popcount(s[i] &^ m[i])
|
||||
}
|
||||
return cnt
|
||||
}
|
||||
|
||||
func popcntAndSliceGo(s, m []uint64) uint64 {
|
||||
cnt := uint64(0)
|
||||
for i := range s {
|
||||
cnt += popcount(s[i] & m[i])
|
||||
}
|
||||
return cnt
|
||||
}
|
||||
|
||||
func popcntOrSliceGo(s, m []uint64) uint64 {
|
||||
cnt := uint64(0)
|
||||
for i := range s {
|
||||
cnt += popcount(s[i] | m[i])
|
||||
}
|
||||
return cnt
|
||||
}
|
||||
|
||||
func popcntXorSliceGo(s, m []uint64) uint64 {
|
||||
cnt := uint64(0)
|
||||
for i := range s {
|
||||
cnt += popcount(s[i] ^ m[i])
|
||||
}
|
||||
return cnt
|
||||
}
|
45
vendor/github.com/bits-and-blooms/bitset/popcnt_19.go
generated
vendored
Normal file
45
vendor/github.com/bits-and-blooms/bitset/popcnt_19.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
// +build go1.9
|
||||
|
||||
package bitset
|
||||
|
||||
import "math/bits"
|
||||
|
||||
func popcntSlice(s []uint64) uint64 {
|
||||
var cnt int
|
||||
for _, x := range s {
|
||||
cnt += bits.OnesCount64(x)
|
||||
}
|
||||
return uint64(cnt)
|
||||
}
|
||||
|
||||
func popcntMaskSlice(s, m []uint64) uint64 {
|
||||
var cnt int
|
||||
for i := range s {
|
||||
cnt += bits.OnesCount64(s[i] &^ m[i])
|
||||
}
|
||||
return uint64(cnt)
|
||||
}
|
||||
|
||||
func popcntAndSlice(s, m []uint64) uint64 {
|
||||
var cnt int
|
||||
for i := range s {
|
||||
cnt += bits.OnesCount64(s[i] & m[i])
|
||||
}
|
||||
return uint64(cnt)
|
||||
}
|
||||
|
||||
func popcntOrSlice(s, m []uint64) uint64 {
|
||||
var cnt int
|
||||
for i := range s {
|
||||
cnt += bits.OnesCount64(s[i] | m[i])
|
||||
}
|
||||
return uint64(cnt)
|
||||
}
|
||||
|
||||
func popcntXorSlice(s, m []uint64) uint64 {
|
||||
var cnt int
|
||||
for i := range s {
|
||||
cnt += bits.OnesCount64(s[i] ^ m[i])
|
||||
}
|
||||
return uint64(cnt)
|
||||
}
|
68
vendor/github.com/bits-and-blooms/bitset/popcnt_amd64.go
generated
vendored
Normal file
68
vendor/github.com/bits-and-blooms/bitset/popcnt_amd64.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
// +build !go1.9
|
||||
// +build amd64,!appengine
|
||||
|
||||
package bitset
|
||||
|
||||
// *** the following functions are defined in popcnt_amd64.s
|
||||
|
||||
//go:noescape
|
||||
|
||||
func hasAsm() bool
|
||||
|
||||
// useAsm is a flag used to select the GO or ASM implementation of the popcnt function
|
||||
var useAsm = hasAsm()
|
||||
|
||||
//go:noescape
|
||||
|
||||
func popcntSliceAsm(s []uint64) uint64
|
||||
|
||||
//go:noescape
|
||||
|
||||
func popcntMaskSliceAsm(s, m []uint64) uint64
|
||||
|
||||
//go:noescape
|
||||
|
||||
func popcntAndSliceAsm(s, m []uint64) uint64
|
||||
|
||||
//go:noescape
|
||||
|
||||
func popcntOrSliceAsm(s, m []uint64) uint64
|
||||
|
||||
//go:noescape
|
||||
|
||||
func popcntXorSliceAsm(s, m []uint64) uint64
|
||||
|
||||
func popcntSlice(s []uint64) uint64 {
|
||||
if useAsm {
|
||||
return popcntSliceAsm(s)
|
||||
}
|
||||
return popcntSliceGo(s)
|
||||
}
|
||||
|
||||
func popcntMaskSlice(s, m []uint64) uint64 {
|
||||
if useAsm {
|
||||
return popcntMaskSliceAsm(s, m)
|
||||
}
|
||||
return popcntMaskSliceGo(s, m)
|
||||
}
|
||||
|
||||
func popcntAndSlice(s, m []uint64) uint64 {
|
||||
if useAsm {
|
||||
return popcntAndSliceAsm(s, m)
|
||||
}
|
||||
return popcntAndSliceGo(s, m)
|
||||
}
|
||||
|
||||
func popcntOrSlice(s, m []uint64) uint64 {
|
||||
if useAsm {
|
||||
return popcntOrSliceAsm(s, m)
|
||||
}
|
||||
return popcntOrSliceGo(s, m)
|
||||
}
|
||||
|
||||
func popcntXorSlice(s, m []uint64) uint64 {
|
||||
if useAsm {
|
||||
return popcntXorSliceAsm(s, m)
|
||||
}
|
||||
return popcntXorSliceGo(s, m)
|
||||
}
|
104
vendor/github.com/bits-and-blooms/bitset/popcnt_amd64.s
generated
vendored
Normal file
104
vendor/github.com/bits-and-blooms/bitset/popcnt_amd64.s
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
// +build !go1.9
|
||||
// +build amd64,!appengine
|
||||
|
||||
TEXT ·hasAsm(SB),4,$0-1
|
||||
MOVQ $1, AX
|
||||
CPUID
|
||||
SHRQ $23, CX
|
||||
ANDQ $1, CX
|
||||
MOVB CX, ret+0(FP)
|
||||
RET
|
||||
|
||||
#define POPCNTQ_DX_DX BYTE $0xf3; BYTE $0x48; BYTE $0x0f; BYTE $0xb8; BYTE $0xd2
|
||||
|
||||
TEXT ·popcntSliceAsm(SB),4,$0-32
|
||||
XORQ AX, AX
|
||||
MOVQ s+0(FP), SI
|
||||
MOVQ s_len+8(FP), CX
|
||||
TESTQ CX, CX
|
||||
JZ popcntSliceEnd
|
||||
popcntSliceLoop:
|
||||
BYTE $0xf3; BYTE $0x48; BYTE $0x0f; BYTE $0xb8; BYTE $0x16 // POPCNTQ (SI), DX
|
||||
ADDQ DX, AX
|
||||
ADDQ $8, SI
|
||||
LOOP popcntSliceLoop
|
||||
popcntSliceEnd:
|
||||
MOVQ AX, ret+24(FP)
|
||||
RET
|
||||
|
||||
TEXT ·popcntMaskSliceAsm(SB),4,$0-56
|
||||
XORQ AX, AX
|
||||
MOVQ s+0(FP), SI
|
||||
MOVQ s_len+8(FP), CX
|
||||
TESTQ CX, CX
|
||||
JZ popcntMaskSliceEnd
|
||||
MOVQ m+24(FP), DI
|
||||
popcntMaskSliceLoop:
|
||||
MOVQ (DI), DX
|
||||
NOTQ DX
|
||||
ANDQ (SI), DX
|
||||
POPCNTQ_DX_DX
|
||||
ADDQ DX, AX
|
||||
ADDQ $8, SI
|
||||
ADDQ $8, DI
|
||||
LOOP popcntMaskSliceLoop
|
||||
popcntMaskSliceEnd:
|
||||
MOVQ AX, ret+48(FP)
|
||||
RET
|
||||
|
||||
TEXT ·popcntAndSliceAsm(SB),4,$0-56
|
||||
XORQ AX, AX
|
||||
MOVQ s+0(FP), SI
|
||||
MOVQ s_len+8(FP), CX
|
||||
TESTQ CX, CX
|
||||
JZ popcntAndSliceEnd
|
||||
MOVQ m+24(FP), DI
|
||||
popcntAndSliceLoop:
|
||||
MOVQ (DI), DX
|
||||
ANDQ (SI), DX
|
||||
POPCNTQ_DX_DX
|
||||
ADDQ DX, AX
|
||||
ADDQ $8, SI
|
||||
ADDQ $8, DI
|
||||
LOOP popcntAndSliceLoop
|
||||
popcntAndSliceEnd:
|
||||
MOVQ AX, ret+48(FP)
|
||||
RET
|
||||
|
||||
TEXT ·popcntOrSliceAsm(SB),4,$0-56
|
||||
XORQ AX, AX
|
||||
MOVQ s+0(FP), SI
|
||||
MOVQ s_len+8(FP), CX
|
||||
TESTQ CX, CX
|
||||
JZ popcntOrSliceEnd
|
||||
MOVQ m+24(FP), DI
|
||||
popcntOrSliceLoop:
|
||||
MOVQ (DI), DX
|
||||
ORQ (SI), DX
|
||||
POPCNTQ_DX_DX
|
||||
ADDQ DX, AX
|
||||
ADDQ $8, SI
|
||||
ADDQ $8, DI
|
||||
LOOP popcntOrSliceLoop
|
||||
popcntOrSliceEnd:
|
||||
MOVQ AX, ret+48(FP)
|
||||
RET
|
||||
|
||||
TEXT ·popcntXorSliceAsm(SB),4,$0-56
|
||||
XORQ AX, AX
|
||||
MOVQ s+0(FP), SI
|
||||
MOVQ s_len+8(FP), CX
|
||||
TESTQ CX, CX
|
||||
JZ popcntXorSliceEnd
|
||||
MOVQ m+24(FP), DI
|
||||
popcntXorSliceLoop:
|
||||
MOVQ (DI), DX
|
||||
XORQ (SI), DX
|
||||
POPCNTQ_DX_DX
|
||||
ADDQ DX, AX
|
||||
ADDQ $8, SI
|
||||
ADDQ $8, DI
|
||||
LOOP popcntXorSliceLoop
|
||||
popcntXorSliceEnd:
|
||||
MOVQ AX, ret+48(FP)
|
||||
RET
|
24
vendor/github.com/bits-and-blooms/bitset/popcnt_generic.go
generated
vendored
Normal file
24
vendor/github.com/bits-and-blooms/bitset/popcnt_generic.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// +build !go1.9
|
||||
// +build !amd64 appengine
|
||||
|
||||
package bitset
|
||||
|
||||
func popcntSlice(s []uint64) uint64 {
|
||||
return popcntSliceGo(s)
|
||||
}
|
||||
|
||||
func popcntMaskSlice(s, m []uint64) uint64 {
|
||||
return popcntMaskSliceGo(s, m)
|
||||
}
|
||||
|
||||
func popcntAndSlice(s, m []uint64) uint64 {
|
||||
return popcntAndSliceGo(s, m)
|
||||
}
|
||||
|
||||
func popcntOrSlice(s, m []uint64) uint64 {
|
||||
return popcntOrSliceGo(s, m)
|
||||
}
|
||||
|
||||
func popcntXorSlice(s, m []uint64) uint64 {
|
||||
return popcntXorSliceGo(s, m)
|
||||
}
|
14
vendor/github.com/bits-and-blooms/bitset/trailing_zeros_18.go
generated
vendored
Normal file
14
vendor/github.com/bits-and-blooms/bitset/trailing_zeros_18.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
// +build !go1.9
|
||||
|
||||
package bitset
|
||||
|
||||
var deBruijn = [...]byte{
|
||||
0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4,
|
||||
62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5,
|
||||
63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11,
|
||||
54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6,
|
||||
}
|
||||
|
||||
func trailingZeroes64(v uint64) uint {
|
||||
return uint(deBruijn[((v&-v)*0x03f79d71b4ca8b09)>>58])
|
||||
}
|
9
vendor/github.com/bits-and-blooms/bitset/trailing_zeros_19.go
generated
vendored
Normal file
9
vendor/github.com/bits-and-blooms/bitset/trailing_zeros_19.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// +build go1.9
|
||||
|
||||
package bitset
|
||||
|
||||
import "math/bits"
|
||||
|
||||
func trailingZeroes64(v uint64) uint {
|
||||
return uint(bits.TrailingZeros64(v))
|
||||
}
|
201
vendor/github.com/opencontainers/selinux/LICENSE
generated
vendored
Normal file
201
vendor/github.com/opencontainers/selinux/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
14
vendor/github.com/opencontainers/selinux/go-selinux/doc.go
generated
vendored
Normal file
14
vendor/github.com/opencontainers/selinux/go-selinux/doc.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
Package selinux provides a high-level interface for interacting with selinux.
|
||||
|
||||
Usage:
|
||||
|
||||
import "github.com/opencontainers/selinux/go-selinux"
|
||||
|
||||
// Ensure that selinux is enforcing mode.
|
||||
if selinux.EnforceMode() != selinux.Enforcing {
|
||||
selinux.SetEnforceMode(selinux.Enforcing)
|
||||
}
|
||||
|
||||
*/
|
||||
package selinux
|
284
vendor/github.com/opencontainers/selinux/go-selinux/selinux.go
generated
vendored
Normal file
284
vendor/github.com/opencontainers/selinux/go-selinux/selinux.go
generated
vendored
Normal file
@ -0,0 +1,284 @@
|
||||
package selinux
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// Enforcing constant indicate SELinux is in enforcing mode
|
||||
Enforcing = 1
|
||||
// Permissive constant to indicate SELinux is in permissive mode
|
||||
Permissive = 0
|
||||
// Disabled constant to indicate SELinux is disabled
|
||||
Disabled = -1
|
||||
// maxCategory is the maximum number of categories used within containers
|
||||
maxCategory = 1024
|
||||
// DefaultCategoryRange is the upper bound on the category range
|
||||
DefaultCategoryRange = uint32(maxCategory)
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrMCSAlreadyExists is returned when trying to allocate a duplicate MCS.
|
||||
ErrMCSAlreadyExists = errors.New("MCS label already exists")
|
||||
// ErrEmptyPath is returned when an empty path has been specified.
|
||||
ErrEmptyPath = errors.New("empty path")
|
||||
|
||||
// InvalidLabel is returned when an invalid label is specified.
|
||||
InvalidLabel = errors.New("Invalid Label")
|
||||
|
||||
// ErrIncomparable is returned two levels are not comparable
|
||||
ErrIncomparable = errors.New("incomparable levels")
|
||||
// ErrLevelSyntax is returned when a sensitivity or category do not have correct syntax in a level
|
||||
ErrLevelSyntax = errors.New("invalid level syntax")
|
||||
|
||||
// ErrContextMissing is returned if a requested context is not found in a file.
|
||||
ErrContextMissing = errors.New("context does not have a match")
|
||||
// ErrVerifierNil is returned when a context verifier function is nil.
|
||||
ErrVerifierNil = errors.New("verifier function is nil")
|
||||
|
||||
// CategoryRange allows the upper bound on the category range to be adjusted
|
||||
CategoryRange = DefaultCategoryRange
|
||||
)
|
||||
|
||||
// Context is a representation of the SELinux label broken into 4 parts
|
||||
type Context map[string]string
|
||||
|
||||
// SetDisabled disables SELinux support for the package
|
||||
func SetDisabled() {
|
||||
setDisabled()
|
||||
}
|
||||
|
||||
// GetEnabled returns whether SELinux is currently enabled.
|
||||
func GetEnabled() bool {
|
||||
return getEnabled()
|
||||
}
|
||||
|
||||
// ClassIndex returns the int index for an object class in the loaded policy,
|
||||
// or -1 and an error
|
||||
func ClassIndex(class string) (int, error) {
|
||||
return classIndex(class)
|
||||
}
|
||||
|
||||
// SetFileLabel sets the SELinux label for this path or returns an error.
|
||||
func SetFileLabel(fpath string, label string) error {
|
||||
return setFileLabel(fpath, label)
|
||||
}
|
||||
|
||||
// FileLabel returns the SELinux label for this path or returns an error.
|
||||
func FileLabel(fpath string) (string, error) {
|
||||
return fileLabel(fpath)
|
||||
}
|
||||
|
||||
// SetFSCreateLabel tells the kernel what label to use for all file system objects
|
||||
// created by this task.
|
||||
// Set the label to an empty string to return to the default label. Calls to SetFSCreateLabel
|
||||
// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until file system
|
||||
// objects created by this task are finished to guarantee another goroutine does not migrate
|
||||
// to the current thread before execution is complete.
|
||||
func SetFSCreateLabel(label string) error {
|
||||
return setFSCreateLabel(label)
|
||||
}
|
||||
|
||||
// FSCreateLabel returns the default label the kernel which the kernel is using
|
||||
// for file system objects created by this task. "" indicates default.
|
||||
func FSCreateLabel() (string, error) {
|
||||
return fsCreateLabel()
|
||||
}
|
||||
|
||||
// CurrentLabel returns the SELinux label of the current process thread, or an error.
|
||||
func CurrentLabel() (string, error) {
|
||||
return currentLabel()
|
||||
}
|
||||
|
||||
// PidLabel returns the SELinux label of the given pid, or an error.
|
||||
func PidLabel(pid int) (string, error) {
|
||||
return pidLabel(pid)
|
||||
}
|
||||
|
||||
// ExecLabel returns the SELinux label that the kernel will use for any programs
|
||||
// that are executed by the current process thread, or an error.
|
||||
func ExecLabel() (string, error) {
|
||||
return execLabel()
|
||||
}
|
||||
|
||||
// CanonicalizeContext takes a context string and writes it to the kernel
|
||||
// the function then returns the context that the kernel will use. Use this
|
||||
// function to check if two contexts are equivalent
|
||||
func CanonicalizeContext(val string) (string, error) {
|
||||
return canonicalizeContext(val)
|
||||
}
|
||||
|
||||
// ComputeCreateContext requests the type transition from source to target for
|
||||
// class from the kernel.
|
||||
func ComputeCreateContext(source string, target string, class string) (string, error) {
|
||||
return computeCreateContext(source, target, class)
|
||||
}
|
||||
|
||||
// CalculateGlbLub computes the glb (greatest lower bound) and lub (least upper bound)
|
||||
// of a source and target range.
|
||||
// The glblub is calculated as the greater of the low sensitivities and
|
||||
// the lower of the high sensitivities and the and of each category bitset.
|
||||
func CalculateGlbLub(sourceRange, targetRange string) (string, error) {
|
||||
return calculateGlbLub(sourceRange, targetRange)
|
||||
}
|
||||
|
||||
// SetExecLabel sets the SELinux label that the kernel will use for any programs
|
||||
// that are executed by the current process thread, or an error. Calls to SetExecLabel
|
||||
// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until execution
|
||||
// of the program is finished to guarantee another goroutine does not migrate to the current
|
||||
// thread before execution is complete.
|
||||
func SetExecLabel(label string) error {
|
||||
return setExecLabel(label)
|
||||
}
|
||||
|
||||
// SetTaskLabel sets the SELinux label for the current thread, or an error.
|
||||
// This requires the dyntransition permission. Calls to SetTaskLabel should
|
||||
// be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() to guarantee
|
||||
// the current thread does not run in a new mislabeled thread.
|
||||
func SetTaskLabel(label string) error {
|
||||
return setTaskLabel(label)
|
||||
}
|
||||
|
||||
// SetSocketLabel takes a process label and tells the kernel to assign the
|
||||
// label to the next socket that gets created. Calls to SetSocketLabel
|
||||
// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until
|
||||
// the the socket is created to guarantee another goroutine does not migrate
|
||||
// to the current thread before execution is complete.
|
||||
func SetSocketLabel(label string) error {
|
||||
return setSocketLabel(label)
|
||||
}
|
||||
|
||||
// SocketLabel retrieves the current socket label setting
|
||||
func SocketLabel() (string, error) {
|
||||
return socketLabel()
|
||||
}
|
||||
|
||||
// PeerLabel retrieves the label of the client on the other side of a socket
|
||||
func PeerLabel(fd uintptr) (string, error) {
|
||||
return peerLabel(fd)
|
||||
}
|
||||
|
||||
// SetKeyLabel takes a process label and tells the kernel to assign the
|
||||
// label to the next kernel keyring that gets created. Calls to SetKeyLabel
|
||||
// should be wrapped in runtime.LockOSThread()/runtime.UnlockOSThread() until
|
||||
// the kernel keyring is created to guarantee another goroutine does not migrate
|
||||
// to the current thread before execution is complete.
|
||||
func SetKeyLabel(label string) error {
|
||||
return setKeyLabel(label)
|
||||
}
|
||||
|
||||
// KeyLabel retrieves the current kernel keyring label setting
|
||||
func KeyLabel() (string, error) {
|
||||
return keyLabel()
|
||||
}
|
||||
|
||||
// Get returns the Context as a string
|
||||
func (c Context) Get() string {
|
||||
return c.get()
|
||||
}
|
||||
|
||||
// NewContext creates a new Context struct from the specified label
|
||||
func NewContext(label string) (Context, error) {
|
||||
return newContext(label)
|
||||
}
|
||||
|
||||
// ClearLabels clears all reserved labels
|
||||
func ClearLabels() {
|
||||
clearLabels()
|
||||
}
|
||||
|
||||
// ReserveLabel reserves the MLS/MCS level component of the specified label
|
||||
func ReserveLabel(label string) {
|
||||
reserveLabel(label)
|
||||
}
|
||||
|
||||
// EnforceMode returns the current SELinux mode Enforcing, Permissive, Disabled
|
||||
func EnforceMode() int {
|
||||
return enforceMode()
|
||||
}
|
||||
|
||||
// SetEnforceMode sets the current SELinux mode Enforcing, Permissive.
|
||||
// Disabled is not valid, since this needs to be set at boot time.
|
||||
func SetEnforceMode(mode int) error {
|
||||
return setEnforceMode(mode)
|
||||
}
|
||||
|
||||
// DefaultEnforceMode returns the systems default SELinux mode Enforcing,
|
||||
// Permissive or Disabled. Note this is is just the default at boot time.
|
||||
// EnforceMode tells you the systems current mode.
|
||||
func DefaultEnforceMode() int {
|
||||
return defaultEnforceMode()
|
||||
}
|
||||
|
||||
// ReleaseLabel un-reserves the MLS/MCS Level field of the specified label,
|
||||
// allowing it to be used by another process.
|
||||
func ReleaseLabel(label string) {
|
||||
releaseLabel(label)
|
||||
}
|
||||
|
||||
// ROFileLabel returns the specified SELinux readonly file label
|
||||
func ROFileLabel() string {
|
||||
return roFileLabel()
|
||||
}
|
||||
|
||||
// KVMContainerLabels returns the default processLabel and mountLabel to be used
|
||||
// for kvm containers by the calling process.
|
||||
func KVMContainerLabels() (string, string) {
|
||||
return kvmContainerLabels()
|
||||
}
|
||||
|
||||
// InitContainerLabels returns the default processLabel and file labels to be
|
||||
// used for containers running an init system like systemd by the calling process.
|
||||
func InitContainerLabels() (string, string) {
|
||||
return initContainerLabels()
|
||||
}
|
||||
|
||||
// ContainerLabels returns an allocated processLabel and fileLabel to be used for
|
||||
// container labeling by the calling process.
|
||||
func ContainerLabels() (processLabel string, fileLabel string) {
|
||||
return containerLabels()
|
||||
}
|
||||
|
||||
// SecurityCheckContext validates that the SELinux label is understood by the kernel
|
||||
func SecurityCheckContext(val string) error {
|
||||
return securityCheckContext(val)
|
||||
}
|
||||
|
||||
// CopyLevel returns a label with the MLS/MCS level from src label replaced on
|
||||
// the dest label.
|
||||
func CopyLevel(src, dest string) (string, error) {
|
||||
return copyLevel(src, dest)
|
||||
}
|
||||
|
||||
// Chcon changes the fpath file object to the SELinux label label.
|
||||
// If fpath is a directory and recurse is true, then Chcon walks the
|
||||
// directory tree setting the label.
|
||||
func Chcon(fpath string, label string, recurse bool) error {
|
||||
return chcon(fpath, label, recurse)
|
||||
}
|
||||
|
||||
// DupSecOpt takes an SELinux process label and returns security options that
|
||||
// can be used to set the SELinux Type and Level for future container processes.
|
||||
func DupSecOpt(src string) ([]string, error) {
|
||||
return dupSecOpt(src)
|
||||
}
|
||||
|
||||
// DisableSecOpt returns a security opt that can be used to disable SELinux
|
||||
// labeling support for future container processes.
|
||||
func DisableSecOpt() []string {
|
||||
return disableSecOpt()
|
||||
}
|
||||
|
||||
// GetDefaultContextWithLevel gets a single context for the specified SELinux user
|
||||
// identity that is reachable from the specified scon context. The context is based
|
||||
// on the per-user /etc/selinux/{SELINUXTYPE}/contexts/users/<username> if it exists,
|
||||
// and falls back to the global /etc/selinux/{SELINUXTYPE}/contexts/default_contexts
|
||||
// file.
|
||||
func GetDefaultContextWithLevel(user, level, scon string) (string, error) {
|
||||
return getDefaultContextWithLevel(user, level, scon)
|
||||
}
|
||||
|
||||
// PrivContainerMountLabel returns mount label for privileged containers
|
||||
func PrivContainerMountLabel() string {
|
||||
return privContainerMountLabel
|
||||
}
|
1212
vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
generated
vendored
Normal file
1212
vendor/github.com/opencontainers/selinux/go-selinux/selinux_linux.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
154
vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go
generated
vendored
Normal file
154
vendor/github.com/opencontainers/selinux/go-selinux/selinux_stub.go
generated
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
// +build !linux
|
||||
|
||||
package selinux
|
||||
|
||||
const privContainerMountLabel = ""
|
||||
|
||||
func setDisabled() {
|
||||
}
|
||||
|
||||
func getEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func classIndex(class string) (int, error) {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func setFileLabel(fpath string, label string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func fileLabel(fpath string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func setFSCreateLabel(label string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func fsCreateLabel() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func currentLabel() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func pidLabel(pid int) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func execLabel() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func canonicalizeContext(val string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func computeCreateContext(source string, target string, class string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func calculateGlbLub(sourceRange, targetRange string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func setExecLabel(label string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setTaskLabel(label string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setSocketLabel(label string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func socketLabel() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func peerLabel(fd uintptr) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func setKeyLabel(label string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func keyLabel() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (c Context) get() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func newContext(label string) (Context, error) {
|
||||
c := make(Context)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func clearLabels() {
|
||||
}
|
||||
|
||||
func reserveLabel(label string) {
|
||||
}
|
||||
|
||||
func enforceMode() int {
|
||||
return Disabled
|
||||
}
|
||||
|
||||
func setEnforceMode(mode int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func defaultEnforceMode() int {
|
||||
return Disabled
|
||||
}
|
||||
|
||||
func releaseLabel(label string) {
|
||||
}
|
||||
|
||||
func roFileLabel() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func kvmContainerLabels() (string, string) {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func initContainerLabels() (string, string) {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func containerLabels() (processLabel string, fileLabel string) {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func securityCheckContext(val string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyLevel(src, dest string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func chcon(fpath string, label string, recurse bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func dupSecOpt(src string) ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func disableSecOpt() []string {
|
||||
return []string{"disable"}
|
||||
}
|
||||
|
||||
func getDefaultContextWithLevel(user, level, scon string) (string, error) {
|
||||
return "", nil
|
||||
}
|
38
vendor/github.com/opencontainers/selinux/go-selinux/xattrs_linux.go
generated
vendored
Normal file
38
vendor/github.com/opencontainers/selinux/go-selinux/xattrs_linux.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
package selinux
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// lgetxattr returns a []byte slice containing the value of
|
||||
// an extended attribute attr set for path.
|
||||
func lgetxattr(path, attr string) ([]byte, error) {
|
||||
// Start with a 128 length byte array
|
||||
dest := make([]byte, 128)
|
||||
sz, errno := doLgetxattr(path, attr, dest)
|
||||
for errno == unix.ERANGE {
|
||||
// Buffer too small, use zero-sized buffer to get the actual size
|
||||
sz, errno = doLgetxattr(path, attr, []byte{})
|
||||
if errno != nil {
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
dest = make([]byte, sz)
|
||||
sz, errno = doLgetxattr(path, attr, dest)
|
||||
}
|
||||
if errno != nil {
|
||||
return nil, errno
|
||||
}
|
||||
|
||||
return dest[:sz], nil
|
||||
}
|
||||
|
||||
// doLgetxattr is a wrapper that retries on EINTR
|
||||
func doLgetxattr(path, attr string, dest []byte) (int, error) {
|
||||
for {
|
||||
sz, err := unix.Lgetxattr(path, attr, dest)
|
||||
if err != unix.EINTR {
|
||||
return sz, err
|
||||
}
|
||||
}
|
||||
}
|
42
vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md
generated
vendored
Normal file
42
vendor/github.com/opencontainers/selinux/pkg/pwalk/README.md
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
## pwalk: parallel implementation of filepath.Walk
|
||||
|
||||
This is a wrapper for [filepath.Walk](https://pkg.go.dev/path/filepath?tab=doc#Walk)
|
||||
which may speed it up by calling multiple callback functions (WalkFunc) in parallel,
|
||||
utilizing goroutines.
|
||||
|
||||
By default, it utilizes 2\*runtime.NumCPU() goroutines for callbacks.
|
||||
This can be changed by using WalkN function which has the additional
|
||||
parameter, specifying the number of goroutines (concurrency).
|
||||
|
||||
### Caveats
|
||||
|
||||
Please note the following limitations of this code:
|
||||
|
||||
* Unlike filepath.Walk, the order of calls is non-deterministic;
|
||||
|
||||
* Only primitive error handling is supported:
|
||||
|
||||
* filepath.SkipDir is not supported;
|
||||
|
||||
* no errors are ever passed to WalkFunc;
|
||||
|
||||
* once any error is returned from any WalkFunc instance, no more new calls
|
||||
to WalkFunc are made, and the error is returned to the caller of Walk;
|
||||
|
||||
* if more than one walkFunc instance will return an error, only one
|
||||
of such errors will be propagated and returned by Walk, others
|
||||
will be silently discarded.
|
||||
|
||||
### Documentation
|
||||
|
||||
For the official documentation, see
|
||||
https://pkg.go.dev/github.com/opencontainers/selinux/pkg/pwalk?tab=doc
|
||||
|
||||
### Benchmarks
|
||||
|
||||
For a WalkFunc that consists solely of the return statement, this
|
||||
implementation is about 10% slower than the standard library's
|
||||
filepath.Walk.
|
||||
|
||||
Otherwise (if a WalkFunc is doing something) this is usually faster,
|
||||
except when the WalkN(..., 1) is used.
|
104
vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go
generated
vendored
Normal file
104
vendor/github.com/opencontainers/selinux/pkg/pwalk/pwalk.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
package pwalk
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type WalkFunc = filepath.WalkFunc
|
||||
|
||||
// Walk is a wrapper for filepath.Walk which can call multiple walkFn
|
||||
// in parallel, allowing to handle each item concurrently. A maximum of
|
||||
// twice the runtime.NumCPU() walkFn will be called at any one time.
|
||||
// If you want to change the maximum, use WalkN instead.
|
||||
//
|
||||
// The order of calls is non-deterministic.
|
||||
//
|
||||
// Note that this implementation only supports primitive error handling:
|
||||
//
|
||||
// - no errors are ever passed to WalkFn;
|
||||
//
|
||||
// - once a walkFn returns any error, all further processing stops
|
||||
// and the error is returned to the caller of Walk;
|
||||
//
|
||||
// - filepath.SkipDir is not supported;
|
||||
//
|
||||
// - if more than one walkFn instance will return an error, only one
|
||||
// of such errors will be propagated and returned by Walk, others
|
||||
// will be silently discarded.
|
||||
func Walk(root string, walkFn WalkFunc) error {
|
||||
return WalkN(root, walkFn, runtime.NumCPU()*2)
|
||||
}
|
||||
|
||||
// WalkN is a wrapper for filepath.Walk which can call multiple walkFn
|
||||
// in parallel, allowing to handle each item concurrently. A maximum of
|
||||
// num walkFn will be called at any one time.
|
||||
//
|
||||
// Please see Walk documentation for caveats of using this function.
|
||||
func WalkN(root string, walkFn WalkFunc, num int) error {
|
||||
// make sure limit is sensible
|
||||
if num < 1 {
|
||||
return errors.Errorf("walk(%q): num must be > 0", root)
|
||||
}
|
||||
|
||||
files := make(chan *walkArgs, 2*num)
|
||||
errCh := make(chan error, 1) // get the first error, ignore others
|
||||
|
||||
// Start walking a tree asap
|
||||
var (
|
||||
err error
|
||||
wg sync.WaitGroup
|
||||
)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
err = filepath.Walk(root, func(p string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
close(files)
|
||||
return err
|
||||
}
|
||||
// add a file to the queue unless a callback sent an error
|
||||
select {
|
||||
case e := <-errCh:
|
||||
close(files)
|
||||
return e
|
||||
default:
|
||||
files <- &walkArgs{path: p, info: &info}
|
||||
return nil
|
||||
}
|
||||
})
|
||||
if err == nil {
|
||||
close(files)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Add(num)
|
||||
for i := 0; i < num; i++ {
|
||||
go func() {
|
||||
for file := range files {
|
||||
if e := walkFn(file.path, *file.info, nil); e != nil {
|
||||
select {
|
||||
case errCh <- e: // sent ok
|
||||
default: // buffer full
|
||||
}
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// walkArgs holds the arguments that were passed to the Walk or WalkLimit
|
||||
// functions.
|
||||
type walkArgs struct {
|
||||
path string
|
||||
info *os.FileInfo
|
||||
}
|
7
vendor/modules.txt
vendored
7
vendor/modules.txt
vendored
@ -35,6 +35,9 @@ github.com/Microsoft/hcsshim/osversion
|
||||
# github.com/alexflint/go-filemutex v1.2.0
|
||||
## explicit; go 1.13
|
||||
github.com/alexflint/go-filemutex
|
||||
# github.com/bits-and-blooms/bitset v1.2.0
|
||||
## explicit; go 1.14
|
||||
github.com/bits-and-blooms/bitset
|
||||
# github.com/buger/jsonparser v1.1.1
|
||||
## explicit; go 1.13
|
||||
github.com/buger/jsonparser
|
||||
@ -145,6 +148,10 @@ github.com/onsi/gomega/matchers/support/goraph/edge
|
||||
github.com/onsi/gomega/matchers/support/goraph/node
|
||||
github.com/onsi/gomega/matchers/support/goraph/util
|
||||
github.com/onsi/gomega/types
|
||||
# github.com/opencontainers/selinux v1.8.2
|
||||
## explicit; go 1.13
|
||||
github.com/opencontainers/selinux/go-selinux
|
||||
github.com/opencontainers/selinux/pkg/pwalk
|
||||
# github.com/pkg/errors v0.9.1
|
||||
## explicit
|
||||
github.com/pkg/errors
|
||||
|
Loading…
x
Reference in New Issue
Block a user