mirror of
https://github.com/containers/skopeo.git
synced 2025-09-21 18:09:08 +00:00
Re-vendor, primarily for https://github.com/containers/storage/pull/11
containers/storage got new dependencies, so we will need to re-vendor eventually anyway, and having this separate from other major work is cleaner. But the primary goal of this commit is to see whether it makes skopeo buildable on OS X.
This commit is contained in:
2
vendor/github.com/containers/image/signature/signature.go
generated
vendored
2
vendor/github.com/containers/image/signature/signature.go
generated
vendored
@@ -1,5 +1,7 @@
|
||||
// Note: Consider the API unstable until the code supports at least three different image formats or transports.
|
||||
|
||||
// NOTE: Keep this in sync with docs/atomic-signature.md and docs/atomic-signature-embedded.json!
|
||||
|
||||
package signature
|
||||
|
||||
import (
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// +build linux
|
||||
|
||||
package overlay2
|
||||
package overlay
|
||||
|
||||
import (
|
||||
"bytes"
|
@@ -1,6 +1,6 @@
|
||||
// +build linux
|
||||
|
||||
package overlay2
|
||||
package overlay
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -61,10 +61,9 @@ var (
|
||||
// that mounts do not fail due to length.
|
||||
|
||||
const (
|
||||
driverName = "overlay2"
|
||||
linkDir = "l"
|
||||
lowerFile = "lower"
|
||||
maxDepth = 128
|
||||
linkDir = "l"
|
||||
lowerFile = "lower"
|
||||
maxDepth = 128
|
||||
|
||||
// idLength represents the number of random characters
|
||||
// which can be used to create the unique link identifer
|
||||
@@ -78,6 +77,7 @@ const (
|
||||
|
||||
// Driver contains information about the home directory and the list of active mounts that are created using this driver.
|
||||
type Driver struct {
|
||||
name string
|
||||
home string
|
||||
uidMaps []idtools.IDMap
|
||||
gidMaps []idtools.IDMap
|
||||
@@ -87,13 +87,13 @@ type Driver struct {
|
||||
var backingFs = "<unknown>"
|
||||
|
||||
func init() {
|
||||
graphdriver.Register(driverName, Init)
|
||||
graphdriver.Register("overlay", InitAsOverlay)
|
||||
graphdriver.Register("overlay2", InitAsOverlay2)
|
||||
}
|
||||
|
||||
// Init returns the a native diff driver for overlay filesystem.
|
||||
// If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error.
|
||||
// If a overlay filesystem is not supported over a existing filesystem then error graphdriver.ErrIncompatibleFS is returned.
|
||||
func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
||||
// InitWithName returns the a naive diff driver for the overlay filesystem,
|
||||
// which returns the passed-in name when asked which driver it is.
|
||||
func InitWithName(name, home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
||||
opts, err := parseOptions(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -112,7 +112,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||
if !opts.overrideKernelCheck {
|
||||
return nil, graphdriver.ErrNotSupported
|
||||
}
|
||||
logrus.Warnf("Using pre-4.0.0 kernel for overlay2, mount failures may require kernel update")
|
||||
logrus.Warnf("Using pre-4.0.0 kernel for overlay, mount failures may require kernel update")
|
||||
}
|
||||
|
||||
fsMagic, err := graphdriver.GetFSMagic(home)
|
||||
@@ -126,7 +126,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||
// check if they are running over btrfs, aufs, zfs, overlay, or ecryptfs
|
||||
switch fsMagic {
|
||||
case graphdriver.FsMagicBtrfs, graphdriver.FsMagicAufs, graphdriver.FsMagicZfs, graphdriver.FsMagicOverlay, graphdriver.FsMagicEcryptfs:
|
||||
logrus.Errorf("'overlay2' is not supported over %s", backingFs)
|
||||
logrus.Errorf("'overlay' is not supported over %s", backingFs)
|
||||
return nil, graphdriver.ErrIncompatibleFS
|
||||
}
|
||||
|
||||
@@ -144,6 +144,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||
}
|
||||
|
||||
d := &Driver{
|
||||
name: name,
|
||||
home: home,
|
||||
uidMaps: uidMaps,
|
||||
gidMaps: gidMaps,
|
||||
@@ -153,6 +154,20 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// InitAsOverlay returns the a naive diff driver for overlay filesystem.
|
||||
// If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error.
|
||||
// If a overlay filesystem is not supported over a existing filesystem then error graphdriver.ErrIncompatibleFS is returned.
|
||||
func InitAsOverlay(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
||||
return InitWithName("overlay", home, options, uidMaps, gidMaps)
|
||||
}
|
||||
|
||||
// InitAsOverlay2 returns the a naive diff driver for overlay filesystem.
|
||||
// If overlay filesystem is not supported on the host, graphdriver.ErrNotSupported is returned as error.
|
||||
// If a overlay filesystem is not supported over a existing filesystem then error graphdriver.ErrIncompatibleFS is returned.
|
||||
func InitAsOverlay2(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
||||
return InitWithName("overlay2", home, options, uidMaps, gidMaps)
|
||||
}
|
||||
|
||||
type overlayOptions struct {
|
||||
overrideKernelCheck bool
|
||||
}
|
||||
@@ -166,13 +181,13 @@ func parseOptions(options []string) (*overlayOptions, error) {
|
||||
}
|
||||
key = strings.ToLower(key)
|
||||
switch key {
|
||||
case "overlay2.override_kernel_check":
|
||||
case "overlay.override_kernel_check", "overlay2.override_kernel_check":
|
||||
o.overrideKernelCheck, err = strconv.ParseBool(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("overlay2: Unknown option %s", key)
|
||||
return nil, fmt.Errorf("overlay: Unknown option %s", key)
|
||||
}
|
||||
}
|
||||
return o, nil
|
||||
@@ -200,7 +215,7 @@ func supportsOverlay() error {
|
||||
}
|
||||
|
||||
func (d *Driver) String() string {
|
||||
return driverName
|
||||
return d.name
|
||||
}
|
||||
|
||||
// Status returns current driver information in a two dimensional string array.
|
@@ -1,3 +1,3 @@
|
||||
// +build !linux
|
||||
|
||||
package overlay2
|
||||
package overlay
|
@@ -1,6 +1,6 @@
|
||||
// +build linux
|
||||
|
||||
package overlay2
|
||||
package overlay
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
4
vendor/github.com/containers/storage/drivers/register/register_overlay.go
generated
vendored
4
vendor/github.com/containers/storage/drivers/register/register_overlay.go
generated
vendored
@@ -3,6 +3,6 @@
|
||||
package register
|
||||
|
||||
import (
|
||||
// register the overlay2 graphdriver
|
||||
_ "github.com/containers/storage/drivers/overlay2"
|
||||
// register the overlay graphdriver
|
||||
_ "github.com/containers/storage/drivers/overlay"
|
||||
)
|
||||
|
137
vendor/github.com/containers/storage/pkg/truncindex/truncindex.go
generated
vendored
Normal file
137
vendor/github.com/containers/storage/pkg/truncindex/truncindex.go
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
// Package truncindex provides a general 'index tree', used by Docker
|
||||
// in order to be able to reference containers by only a few unambiguous
|
||||
// characters of their id.
|
||||
package truncindex
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/tchap/go-patricia/patricia"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrEmptyPrefix is an error returned if the prefix was empty.
|
||||
ErrEmptyPrefix = errors.New("Prefix can't be empty")
|
||||
|
||||
// ErrIllegalChar is returned when a space is in the ID
|
||||
ErrIllegalChar = errors.New("illegal character: ' '")
|
||||
|
||||
// ErrNotExist is returned when ID or its prefix not found in index.
|
||||
ErrNotExist = errors.New("ID does not exist")
|
||||
)
|
||||
|
||||
// ErrAmbiguousPrefix is returned if the prefix was ambiguous
|
||||
// (multiple ids for the prefix).
|
||||
type ErrAmbiguousPrefix struct {
|
||||
prefix string
|
||||
}
|
||||
|
||||
func (e ErrAmbiguousPrefix) Error() string {
|
||||
return fmt.Sprintf("Multiple IDs found with provided prefix: %s", e.prefix)
|
||||
}
|
||||
|
||||
// TruncIndex allows the retrieval of string identifiers by any of their unique prefixes.
|
||||
// This is used to retrieve image and container IDs by more convenient shorthand prefixes.
|
||||
type TruncIndex struct {
|
||||
sync.RWMutex
|
||||
trie *patricia.Trie
|
||||
ids map[string]struct{}
|
||||
}
|
||||
|
||||
// NewTruncIndex creates a new TruncIndex and initializes with a list of IDs.
|
||||
func NewTruncIndex(ids []string) (idx *TruncIndex) {
|
||||
idx = &TruncIndex{
|
||||
ids: make(map[string]struct{}),
|
||||
|
||||
// Change patricia max prefix per node length,
|
||||
// because our len(ID) always 64
|
||||
trie: patricia.NewTrie(patricia.MaxPrefixPerNode(64)),
|
||||
}
|
||||
for _, id := range ids {
|
||||
idx.addID(id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (idx *TruncIndex) addID(id string) error {
|
||||
if strings.Contains(id, " ") {
|
||||
return ErrIllegalChar
|
||||
}
|
||||
if id == "" {
|
||||
return ErrEmptyPrefix
|
||||
}
|
||||
if _, exists := idx.ids[id]; exists {
|
||||
return fmt.Errorf("id already exists: '%s'", id)
|
||||
}
|
||||
idx.ids[id] = struct{}{}
|
||||
if inserted := idx.trie.Insert(patricia.Prefix(id), struct{}{}); !inserted {
|
||||
return fmt.Errorf("failed to insert id: %s", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add adds a new ID to the TruncIndex.
|
||||
func (idx *TruncIndex) Add(id string) error {
|
||||
idx.Lock()
|
||||
defer idx.Unlock()
|
||||
if err := idx.addID(id); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete removes an ID from the TruncIndex. If there are multiple IDs
|
||||
// with the given prefix, an error is thrown.
|
||||
func (idx *TruncIndex) Delete(id string) error {
|
||||
idx.Lock()
|
||||
defer idx.Unlock()
|
||||
if _, exists := idx.ids[id]; !exists || id == "" {
|
||||
return fmt.Errorf("no such id: '%s'", id)
|
||||
}
|
||||
delete(idx.ids, id)
|
||||
if deleted := idx.trie.Delete(patricia.Prefix(id)); !deleted {
|
||||
return fmt.Errorf("no such id: '%s'", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get retrieves an ID from the TruncIndex. If there are multiple IDs
|
||||
// with the given prefix, an error is thrown.
|
||||
func (idx *TruncIndex) Get(s string) (string, error) {
|
||||
if s == "" {
|
||||
return "", ErrEmptyPrefix
|
||||
}
|
||||
var (
|
||||
id string
|
||||
)
|
||||
subTreeVisitFunc := func(prefix patricia.Prefix, item patricia.Item) error {
|
||||
if id != "" {
|
||||
// we haven't found the ID if there are two or more IDs
|
||||
id = ""
|
||||
return ErrAmbiguousPrefix{prefix: string(prefix)}
|
||||
}
|
||||
id = string(prefix)
|
||||
return nil
|
||||
}
|
||||
|
||||
idx.RLock()
|
||||
defer idx.RUnlock()
|
||||
if err := idx.trie.VisitSubtree(patricia.Prefix(s), subTreeVisitFunc); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if id != "" {
|
||||
return id, nil
|
||||
}
|
||||
return "", ErrNotExist
|
||||
}
|
||||
|
||||
// Iterate iterates over all stored IDs, and passes each of them to the given handler.
|
||||
func (idx *TruncIndex) Iterate(handler func(id string)) {
|
||||
idx.trie.Visit(func(prefix patricia.Prefix, item patricia.Item) error {
|
||||
handler(string(prefix))
|
||||
return nil
|
||||
})
|
||||
}
|
185
vendor/github.com/containers/storage/storage/containers.go
generated
vendored
185
vendor/github.com/containers/storage/storage/containers.go
generated
vendored
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/containers/storage/pkg/ioutils"
|
||||
"github.com/containers/storage/pkg/stringid"
|
||||
"github.com/containers/storage/pkg/truncindex"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -93,6 +94,7 @@ type containerStore struct {
|
||||
lockfile Locker
|
||||
dir string
|
||||
containers []Container
|
||||
idindex *truncindex.TruncIndex
|
||||
byid map[string]*Container
|
||||
bylayer map[string]*Container
|
||||
byname map[string]*Container
|
||||
@@ -123,10 +125,12 @@ func (r *containerStore) Load() error {
|
||||
}
|
||||
containers := []Container{}
|
||||
layers := make(map[string]*Container)
|
||||
idlist := []string{}
|
||||
ids := make(map[string]*Container)
|
||||
names := make(map[string]*Container)
|
||||
if err = json.Unmarshal(data, &containers); len(data) == 0 || err == nil {
|
||||
for n, container := range containers {
|
||||
idlist = append(idlist, container.ID)
|
||||
ids[container.ID] = &containers[n]
|
||||
layers[container.LayerID] = &containers[n]
|
||||
for _, name := range container.Names {
|
||||
@@ -139,6 +143,7 @@ func (r *containerStore) Load() error {
|
||||
}
|
||||
}
|
||||
r.containers = containers
|
||||
r.idindex = truncindex.NewTruncIndex(idlist)
|
||||
r.byid = ids
|
||||
r.bylayer = layers
|
||||
r.byname = names
|
||||
@@ -185,30 +190,35 @@ func newContainerStore(dir string) (ContainerStore, error) {
|
||||
return &cstore, nil
|
||||
}
|
||||
|
||||
func (r *containerStore) ClearFlag(id string, flag string) error {
|
||||
if container, ok := r.byname[id]; ok {
|
||||
id = container.ID
|
||||
func (r *containerStore) lookup(id string) (*Container, bool) {
|
||||
if container, ok := r.byid[id]; ok {
|
||||
return container, ok
|
||||
} else if container, ok := r.byname[id]; ok {
|
||||
return container, ok
|
||||
} else if container, ok := r.bylayer[id]; ok {
|
||||
id = container.ID
|
||||
return container, ok
|
||||
} else if longid, err := r.idindex.Get(id); err == nil {
|
||||
if container, ok := r.byid[longid]; ok {
|
||||
return container, ok
|
||||
}
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (r *containerStore) ClearFlag(id string, flag string) error {
|
||||
container, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrContainerUnknown
|
||||
}
|
||||
container := r.byid[id]
|
||||
delete(container.Flags, flag)
|
||||
return r.Save()
|
||||
}
|
||||
|
||||
func (r *containerStore) SetFlag(id string, flag string, value interface{}) error {
|
||||
if container, ok := r.byname[id]; ok {
|
||||
id = container.ID
|
||||
} else if container, ok := r.bylayer[id]; ok {
|
||||
id = container.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
container, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrContainerUnknown
|
||||
}
|
||||
container := r.byid[id]
|
||||
container.Flags[flag] = value
|
||||
return r.Save()
|
||||
}
|
||||
@@ -244,6 +254,7 @@ func (r *containerStore) Create(id string, names []string, image, layer, metadat
|
||||
r.containers = append(r.containers, newContainer)
|
||||
container = &r.containers[len(r.containers)-1]
|
||||
r.byid[id] = container
|
||||
r.idindex.Add(id)
|
||||
r.bylayer[layer] = container
|
||||
for _, name := range names {
|
||||
r.byname[name] = container
|
||||
@@ -254,24 +265,14 @@ func (r *containerStore) Create(id string, names []string, image, layer, metadat
|
||||
}
|
||||
|
||||
func (r *containerStore) GetMetadata(id string) (string, error) {
|
||||
if container, ok := r.byname[id]; ok {
|
||||
id = container.ID
|
||||
} else if container, ok := r.bylayer[id]; ok {
|
||||
id = container.ID
|
||||
}
|
||||
if container, ok := r.byid[id]; ok {
|
||||
if container, ok := r.lookup(id); ok {
|
||||
return container.Metadata, nil
|
||||
}
|
||||
return "", ErrContainerUnknown
|
||||
}
|
||||
|
||||
func (r *containerStore) SetMetadata(id, metadata string) error {
|
||||
if container, ok := r.byname[id]; ok {
|
||||
id = container.ID
|
||||
} else if container, ok := r.bylayer[id]; ok {
|
||||
id = container.ID
|
||||
}
|
||||
if container, ok := r.byid[id]; ok {
|
||||
if container, ok := r.lookup(id); ok {
|
||||
container.Metadata = metadata
|
||||
return r.Save()
|
||||
}
|
||||
@@ -279,22 +280,11 @@ func (r *containerStore) SetMetadata(id, metadata string) error {
|
||||
}
|
||||
|
||||
func (r *containerStore) removeName(container *Container, name string) {
|
||||
newNames := []string{}
|
||||
for _, oldName := range container.Names {
|
||||
if oldName != name {
|
||||
newNames = append(newNames, oldName)
|
||||
}
|
||||
}
|
||||
container.Names = newNames
|
||||
container.Names = stringSliceWithoutValue(container.Names, name)
|
||||
}
|
||||
|
||||
func (r *containerStore) SetNames(id string, names []string) error {
|
||||
if container, ok := r.byname[id]; ok {
|
||||
id = container.ID
|
||||
} else if container, ok := r.bylayer[id]; ok {
|
||||
id = container.ID
|
||||
}
|
||||
if container, ok := r.byid[id]; ok {
|
||||
if container, ok := r.lookup(id); ok {
|
||||
for _, name := range container.Names {
|
||||
delete(r.byname, name)
|
||||
}
|
||||
@@ -311,133 +301,104 @@ func (r *containerStore) SetNames(id string, names []string) error {
|
||||
}
|
||||
|
||||
func (r *containerStore) Delete(id string) error {
|
||||
if container, ok := r.byname[id]; ok {
|
||||
id = container.ID
|
||||
} else if container, ok := r.bylayer[id]; ok {
|
||||
id = container.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
container, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrContainerUnknown
|
||||
}
|
||||
if container, ok := r.byid[id]; ok {
|
||||
newContainers := []Container{}
|
||||
for _, candidate := range r.containers {
|
||||
if candidate.ID != id {
|
||||
newContainers = append(newContainers, candidate)
|
||||
}
|
||||
}
|
||||
delete(r.byid, container.ID)
|
||||
delete(r.bylayer, container.LayerID)
|
||||
for _, name := range container.Names {
|
||||
delete(r.byname, name)
|
||||
}
|
||||
r.containers = newContainers
|
||||
if err := r.Save(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(r.datadir(id)); err != nil {
|
||||
return err
|
||||
id = container.ID
|
||||
newContainers := []Container{}
|
||||
for _, candidate := range r.containers {
|
||||
if candidate.ID != id {
|
||||
newContainers = append(newContainers, candidate)
|
||||
}
|
||||
}
|
||||
delete(r.byid, id)
|
||||
r.idindex.Delete(id)
|
||||
delete(r.bylayer, container.LayerID)
|
||||
for _, name := range container.Names {
|
||||
delete(r.byname, name)
|
||||
}
|
||||
r.containers = newContainers
|
||||
if err := r.Save(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(r.datadir(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *containerStore) Get(id string) (*Container, error) {
|
||||
if c, ok := r.byname[id]; ok {
|
||||
return c, nil
|
||||
} else if c, ok := r.bylayer[id]; ok {
|
||||
return c, nil
|
||||
}
|
||||
if c, ok := r.byid[id]; ok {
|
||||
return c, nil
|
||||
if container, ok := r.lookup(id); ok {
|
||||
return container, nil
|
||||
}
|
||||
return nil, ErrContainerUnknown
|
||||
}
|
||||
|
||||
func (r *containerStore) Lookup(name string) (id string, err error) {
|
||||
container, ok := r.byname[name]
|
||||
if !ok {
|
||||
container, ok = r.byid[name]
|
||||
if !ok {
|
||||
return "", ErrContainerUnknown
|
||||
}
|
||||
if container, ok := r.lookup(name); ok {
|
||||
return container.ID, nil
|
||||
}
|
||||
return container.ID, nil
|
||||
return "", ErrContainerUnknown
|
||||
}
|
||||
|
||||
func (r *containerStore) Exists(id string) bool {
|
||||
if _, ok := r.byname[id]; ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := r.bylayer[id]; ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := r.byid[id]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
_, ok := r.lookup(id)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *containerStore) GetBigData(id, key string) ([]byte, error) {
|
||||
if img, ok := r.byname[id]; ok {
|
||||
id = img.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
c, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return nil, ErrContainerUnknown
|
||||
}
|
||||
return ioutil.ReadFile(r.datapath(id, key))
|
||||
return ioutil.ReadFile(r.datapath(c.ID, key))
|
||||
}
|
||||
|
||||
func (r *containerStore) GetBigDataSize(id, key string) (int64, error) {
|
||||
if img, ok := r.byname[id]; ok {
|
||||
id = img.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
c, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return -1, ErrContainerUnknown
|
||||
}
|
||||
if size, ok := r.byid[id].BigDataSizes[key]; ok {
|
||||
if size, ok := c.BigDataSizes[key]; ok {
|
||||
return size, nil
|
||||
}
|
||||
return -1, ErrSizeUnknown
|
||||
}
|
||||
|
||||
func (r *containerStore) GetBigDataNames(id string) ([]string, error) {
|
||||
if img, ok := r.byname[id]; ok {
|
||||
id = img.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
c, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return nil, ErrContainerUnknown
|
||||
}
|
||||
return r.byid[id].BigDataNames, nil
|
||||
return c.BigDataNames, nil
|
||||
}
|
||||
|
||||
func (r *containerStore) SetBigData(id, key string, data []byte) error {
|
||||
if img, ok := r.byname[id]; ok {
|
||||
id = img.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
c, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrContainerUnknown
|
||||
}
|
||||
if err := os.MkdirAll(r.datadir(id), 0700); err != nil {
|
||||
if err := os.MkdirAll(r.datadir(c.ID), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
err := ioutils.AtomicWriteFile(r.datapath(id, key), data, 0600)
|
||||
err := ioutils.AtomicWriteFile(r.datapath(c.ID, key), data, 0600)
|
||||
if err == nil {
|
||||
save := false
|
||||
oldSize, ok := r.byid[id].BigDataSizes[key]
|
||||
r.byid[id].BigDataSizes[key] = int64(len(data))
|
||||
if !ok || oldSize != r.byid[id].BigDataSizes[key] {
|
||||
oldSize, ok := c.BigDataSizes[key]
|
||||
c.BigDataSizes[key] = int64(len(data))
|
||||
if !ok || oldSize != c.BigDataSizes[key] {
|
||||
save = true
|
||||
}
|
||||
add := true
|
||||
for _, name := range r.byid[id].BigDataNames {
|
||||
for _, name := range c.BigDataNames {
|
||||
if name == key {
|
||||
add = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if add {
|
||||
r.byid[id].BigDataNames = append(r.byid[id].BigDataNames, key)
|
||||
c.BigDataNames = append(c.BigDataNames, key)
|
||||
save = true
|
||||
}
|
||||
if save {
|
||||
|
163
vendor/github.com/containers/storage/storage/images.go
generated
vendored
163
vendor/github.com/containers/storage/storage/images.go
generated
vendored
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/containers/storage/pkg/ioutils"
|
||||
"github.com/containers/storage/pkg/stringid"
|
||||
"github.com/containers/storage/pkg/truncindex"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -88,6 +89,7 @@ type imageStore struct {
|
||||
lockfile Locker
|
||||
dir string
|
||||
images []Image
|
||||
idindex *truncindex.TruncIndex
|
||||
byid map[string]*Image
|
||||
byname map[string]*Image
|
||||
}
|
||||
@@ -116,11 +118,13 @@ func (r *imageStore) Load() error {
|
||||
return err
|
||||
}
|
||||
images := []Image{}
|
||||
idlist := []string{}
|
||||
ids := make(map[string]*Image)
|
||||
names := make(map[string]*Image)
|
||||
if err = json.Unmarshal(data, &images); len(data) == 0 || err == nil {
|
||||
for n, image := range images {
|
||||
ids[image.ID] = &images[n]
|
||||
idlist = append(idlist, image.ID)
|
||||
for _, name := range image.Names {
|
||||
if conflict, ok := names[name]; ok {
|
||||
r.removeName(conflict, name)
|
||||
@@ -131,6 +135,7 @@ func (r *imageStore) Load() error {
|
||||
}
|
||||
}
|
||||
r.images = images
|
||||
r.idindex = truncindex.NewTruncIndex(idlist)
|
||||
r.byid = ids
|
||||
r.byname = names
|
||||
if needSave {
|
||||
@@ -175,26 +180,32 @@ func newImageStore(dir string) (ImageStore, error) {
|
||||
return &istore, nil
|
||||
}
|
||||
|
||||
func (r *imageStore) ClearFlag(id string, flag string) error {
|
||||
if image, ok := r.byname[id]; ok {
|
||||
id = image.ID
|
||||
func (r *imageStore) lookup(id string) (*Image, bool) {
|
||||
if image, ok := r.byid[id]; ok {
|
||||
return image, ok
|
||||
} else if image, ok := r.byname[id]; ok {
|
||||
return image, ok
|
||||
} else if longid, err := r.idindex.Get(id); err == nil {
|
||||
image, ok := r.byid[longid]
|
||||
return image, ok
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (r *imageStore) ClearFlag(id string, flag string) error {
|
||||
image, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrImageUnknown
|
||||
}
|
||||
image := r.byid[id]
|
||||
delete(image.Flags, flag)
|
||||
return r.Save()
|
||||
}
|
||||
|
||||
func (r *imageStore) SetFlag(id string, flag string, value interface{}) error {
|
||||
if image, ok := r.byname[id]; ok {
|
||||
id = image.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
image, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrImageUnknown
|
||||
}
|
||||
image := r.byid[id]
|
||||
image.Flags[flag] = value
|
||||
return r.Save()
|
||||
}
|
||||
@@ -228,6 +239,7 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string) (
|
||||
}
|
||||
r.images = append(r.images, newImage)
|
||||
image = &r.images[len(r.images)-1]
|
||||
r.idindex.Add(id)
|
||||
r.byid[id] = image
|
||||
for _, name := range names {
|
||||
r.byname[name] = image
|
||||
@@ -238,20 +250,14 @@ func (r *imageStore) Create(id string, names []string, layer, metadata string) (
|
||||
}
|
||||
|
||||
func (r *imageStore) GetMetadata(id string) (string, error) {
|
||||
if image, ok := r.byname[id]; ok {
|
||||
id = image.ID
|
||||
}
|
||||
if image, ok := r.byid[id]; ok {
|
||||
if image, ok := r.lookup(id); ok {
|
||||
return image.Metadata, nil
|
||||
}
|
||||
return "", ErrImageUnknown
|
||||
}
|
||||
|
||||
func (r *imageStore) SetMetadata(id, metadata string) error {
|
||||
if image, ok := r.byname[id]; ok {
|
||||
id = image.ID
|
||||
}
|
||||
if image, ok := r.byid[id]; ok {
|
||||
if image, ok := r.lookup(id); ok {
|
||||
image.Metadata = metadata
|
||||
return r.Save()
|
||||
}
|
||||
@@ -259,20 +265,11 @@ func (r *imageStore) SetMetadata(id, metadata string) error {
|
||||
}
|
||||
|
||||
func (r *imageStore) removeName(image *Image, name string) {
|
||||
newNames := []string{}
|
||||
for _, oldName := range image.Names {
|
||||
if oldName != name {
|
||||
newNames = append(newNames, oldName)
|
||||
}
|
||||
}
|
||||
image.Names = newNames
|
||||
image.Names = stringSliceWithoutValue(image.Names, name)
|
||||
}
|
||||
|
||||
func (r *imageStore) SetNames(id string, names []string) error {
|
||||
if image, ok := r.byname[id]; ok {
|
||||
id = image.ID
|
||||
}
|
||||
if image, ok := r.byid[id]; ok {
|
||||
if image, ok := r.lookup(id); ok {
|
||||
for _, name := range image.Names {
|
||||
delete(r.byname, name)
|
||||
}
|
||||
@@ -289,125 +286,103 @@ func (r *imageStore) SetNames(id string, names []string) error {
|
||||
}
|
||||
|
||||
func (r *imageStore) Delete(id string) error {
|
||||
if image, ok := r.byname[id]; ok {
|
||||
id = image.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
image, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrImageUnknown
|
||||
}
|
||||
if image, ok := r.byid[id]; ok {
|
||||
newImages := []Image{}
|
||||
for _, candidate := range r.images {
|
||||
if candidate.ID != id {
|
||||
newImages = append(newImages, candidate)
|
||||
}
|
||||
}
|
||||
delete(r.byid, image.ID)
|
||||
for _, name := range image.Names {
|
||||
delete(r.byname, name)
|
||||
}
|
||||
r.images = newImages
|
||||
if err := r.Save(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(r.datadir(id)); err != nil {
|
||||
return err
|
||||
id = image.ID
|
||||
newImages := []Image{}
|
||||
for _, candidate := range r.images {
|
||||
if candidate.ID != id {
|
||||
newImages = append(newImages, candidate)
|
||||
}
|
||||
}
|
||||
delete(r.byid, id)
|
||||
r.idindex.Delete(id)
|
||||
for _, name := range image.Names {
|
||||
delete(r.byname, name)
|
||||
}
|
||||
r.images = newImages
|
||||
if err := r.Save(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.RemoveAll(r.datadir(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *imageStore) Get(id string) (*Image, error) {
|
||||
if image, ok := r.byname[id]; ok {
|
||||
return image, nil
|
||||
}
|
||||
if image, ok := r.byid[id]; ok {
|
||||
if image, ok := r.lookup(id); ok {
|
||||
return image, nil
|
||||
}
|
||||
return nil, ErrImageUnknown
|
||||
}
|
||||
|
||||
func (r *imageStore) Lookup(name string) (id string, err error) {
|
||||
image, ok := r.byname[name]
|
||||
if !ok {
|
||||
image, ok = r.byid[name]
|
||||
if !ok {
|
||||
return "", ErrImageUnknown
|
||||
}
|
||||
if image, ok := r.lookup(name); ok {
|
||||
return image.ID, nil
|
||||
}
|
||||
return image.ID, nil
|
||||
return "", ErrImageUnknown
|
||||
}
|
||||
|
||||
func (r *imageStore) Exists(id string) bool {
|
||||
if _, ok := r.byname[id]; ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := r.byid[id]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
_, ok := r.lookup(id)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *imageStore) GetBigData(id, key string) ([]byte, error) {
|
||||
if img, ok := r.byname[id]; ok {
|
||||
id = img.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
image, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return nil, ErrImageUnknown
|
||||
}
|
||||
return ioutil.ReadFile(r.datapath(id, key))
|
||||
return ioutil.ReadFile(r.datapath(image.ID, key))
|
||||
}
|
||||
|
||||
func (r *imageStore) GetBigDataSize(id, key string) (int64, error) {
|
||||
if img, ok := r.byname[id]; ok {
|
||||
id = img.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
image, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return -1, ErrImageUnknown
|
||||
}
|
||||
if size, ok := r.byid[id].BigDataSizes[key]; ok {
|
||||
if size, ok := image.BigDataSizes[key]; ok {
|
||||
return size, nil
|
||||
}
|
||||
return -1, ErrSizeUnknown
|
||||
}
|
||||
|
||||
func (r *imageStore) GetBigDataNames(id string) ([]string, error) {
|
||||
if img, ok := r.byname[id]; ok {
|
||||
id = img.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
image, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return nil, ErrImageUnknown
|
||||
}
|
||||
return r.byid[id].BigDataNames, nil
|
||||
return image.BigDataNames, nil
|
||||
}
|
||||
|
||||
func (r *imageStore) SetBigData(id, key string, data []byte) error {
|
||||
if img, ok := r.byname[id]; ok {
|
||||
id = img.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
image, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrImageUnknown
|
||||
}
|
||||
if err := os.MkdirAll(r.datadir(id), 0700); err != nil {
|
||||
if err := os.MkdirAll(r.datadir(image.ID), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
err := ioutils.AtomicWriteFile(r.datapath(id, key), data, 0600)
|
||||
err := ioutils.AtomicWriteFile(r.datapath(image.ID, key), data, 0600)
|
||||
if err == nil {
|
||||
add := true
|
||||
save := false
|
||||
oldSize, ok := r.byid[id].BigDataSizes[key]
|
||||
r.byid[id].BigDataSizes[key] = int64(len(data))
|
||||
if !ok || oldSize != r.byid[id].BigDataSizes[key] {
|
||||
oldSize, ok := image.BigDataSizes[key]
|
||||
image.BigDataSizes[key] = int64(len(data))
|
||||
if !ok || oldSize != image.BigDataSizes[key] {
|
||||
save = true
|
||||
}
|
||||
for _, name := range r.byid[id].BigDataNames {
|
||||
for _, name := range image.BigDataNames {
|
||||
if name == key {
|
||||
add = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if add {
|
||||
r.byid[id].BigDataNames = append(r.byid[id].BigDataNames, key)
|
||||
image.BigDataNames = append(image.BigDataNames, key)
|
||||
save = true
|
||||
}
|
||||
if save {
|
||||
|
267
vendor/github.com/containers/storage/storage/layers.go
generated
vendored
267
vendor/github.com/containers/storage/storage/layers.go
generated
vendored
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
"github.com/containers/storage/pkg/ioutils"
|
||||
"github.com/containers/storage/pkg/stringid"
|
||||
"github.com/containers/storage/pkg/truncindex"
|
||||
"github.com/vbatts/tar-split/tar/asm"
|
||||
"github.com/vbatts/tar-split/tar/storage"
|
||||
)
|
||||
@@ -159,6 +160,7 @@ type layerStore struct {
|
||||
driver drivers.Driver
|
||||
layerdir string
|
||||
layers []Layer
|
||||
idindex *truncindex.TruncIndex
|
||||
byid map[string]*Layer
|
||||
byname map[string]*Layer
|
||||
byparent map[string][]*Layer
|
||||
@@ -185,6 +187,7 @@ func (r *layerStore) Load() error {
|
||||
return err
|
||||
}
|
||||
layers := []Layer{}
|
||||
idlist := []string{}
|
||||
ids := make(map[string]*Layer)
|
||||
names := make(map[string]*Layer)
|
||||
mounts := make(map[string]*Layer)
|
||||
@@ -192,6 +195,7 @@ func (r *layerStore) Load() error {
|
||||
if err = json.Unmarshal(data, &layers); len(data) == 0 || err == nil {
|
||||
for n, layer := range layers {
|
||||
ids[layer.ID] = &layers[n]
|
||||
idlist = append(idlist, layer.ID)
|
||||
for _, name := range layer.Names {
|
||||
if conflict, ok := names[name]; ok {
|
||||
r.removeName(conflict, name)
|
||||
@@ -224,6 +228,7 @@ func (r *layerStore) Load() error {
|
||||
}
|
||||
}
|
||||
r.layers = layers
|
||||
r.idindex = truncindex.NewTruncIndex(idlist)
|
||||
r.byid = ids
|
||||
r.byname = names
|
||||
r.byparent = parents
|
||||
@@ -312,26 +317,32 @@ func newLayerStore(rundir string, layerdir string, driver drivers.Driver) (Layer
|
||||
return &rlstore, nil
|
||||
}
|
||||
|
||||
func (r *layerStore) ClearFlag(id string, flag string) error {
|
||||
if layer, ok := r.byname[id]; ok {
|
||||
id = layer.ID
|
||||
func (r *layerStore) lookup(id string) (*Layer, bool) {
|
||||
if layer, ok := r.byid[id]; ok {
|
||||
return layer, ok
|
||||
} else if layer, ok := r.byname[id]; ok {
|
||||
return layer, ok
|
||||
} else if longid, err := r.idindex.Get(id); err == nil {
|
||||
layer, ok := r.byid[longid]
|
||||
return layer, ok
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (r *layerStore) ClearFlag(id string, flag string) error {
|
||||
layer, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrLayerUnknown
|
||||
}
|
||||
layer := r.byid[id]
|
||||
delete(layer.Flags, flag)
|
||||
return r.Save()
|
||||
}
|
||||
|
||||
func (r *layerStore) SetFlag(id string, flag string, value interface{}) error {
|
||||
if layer, ok := r.byname[id]; ok {
|
||||
id = layer.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
layer, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrLayerUnknown
|
||||
}
|
||||
layer := r.byid[id]
|
||||
layer.Flags[flag] = value
|
||||
return r.Save()
|
||||
}
|
||||
@@ -348,8 +359,10 @@ func (r *layerStore) Put(id, parent string, names []string, mountLabel string, o
|
||||
if err := os.MkdirAll(r.layerdir, 0700); err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
if parentLayer, ok := r.byname[parent]; ok {
|
||||
parent = parentLayer.ID
|
||||
if parent != "" {
|
||||
if parentLayer, ok := r.lookup(parent); ok {
|
||||
parent = parentLayer.ID
|
||||
}
|
||||
}
|
||||
if id == "" {
|
||||
id = stringid.GenerateRandomID()
|
||||
@@ -382,6 +395,7 @@ func (r *layerStore) Put(id, parent string, names []string, mountLabel string, o
|
||||
}
|
||||
r.layers = append(r.layers, newLayer)
|
||||
layer = &r.layers[len(r.layers)-1]
|
||||
r.idindex.Add(id)
|
||||
r.byid[id] = layer
|
||||
for _, name := range names {
|
||||
r.byname[name] = layer
|
||||
@@ -436,48 +450,39 @@ func (r *layerStore) Create(id, parent string, names []string, mountLabel string
|
||||
}
|
||||
|
||||
func (r *layerStore) Mount(id, mountLabel string) (string, error) {
|
||||
if layer, ok := r.byname[id]; ok {
|
||||
id = layer.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
layer, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return "", ErrLayerUnknown
|
||||
}
|
||||
layer := r.byid[id]
|
||||
if layer.MountCount > 0 {
|
||||
layer.MountCount++
|
||||
return layer.MountPoint, r.Save()
|
||||
}
|
||||
if mountLabel == "" {
|
||||
if layer, ok := r.byid[id]; ok {
|
||||
mountLabel = layer.MountLabel
|
||||
}
|
||||
mountLabel = layer.MountLabel
|
||||
}
|
||||
mountpoint, err := r.driver.Get(id, mountLabel)
|
||||
if mountpoint != "" && err == nil {
|
||||
if layer, ok := r.byid[id]; ok {
|
||||
if layer.MountPoint != "" {
|
||||
delete(r.bymount, layer.MountPoint)
|
||||
}
|
||||
layer.MountPoint = filepath.Clean(mountpoint)
|
||||
layer.MountCount++
|
||||
r.bymount[layer.MountPoint] = layer
|
||||
err = r.Save()
|
||||
if layer.MountPoint != "" {
|
||||
delete(r.bymount, layer.MountPoint)
|
||||
}
|
||||
layer.MountPoint = filepath.Clean(mountpoint)
|
||||
layer.MountCount++
|
||||
r.bymount[layer.MountPoint] = layer
|
||||
err = r.Save()
|
||||
}
|
||||
return mountpoint, err
|
||||
}
|
||||
|
||||
func (r *layerStore) Unmount(id string) error {
|
||||
if layer, ok := r.bymount[filepath.Clean(id)]; ok {
|
||||
id = layer.ID
|
||||
layer, ok := r.lookup(id)
|
||||
if !ok {
|
||||
layerByMount, ok := r.bymount[filepath.Clean(id)]
|
||||
if !ok {
|
||||
return ErrLayerUnknown
|
||||
}
|
||||
layer = layerByMount
|
||||
}
|
||||
if layer, ok := r.byname[id]; ok {
|
||||
id = layer.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
return ErrLayerUnknown
|
||||
}
|
||||
layer := r.byid[id]
|
||||
if layer.MountCount > 1 {
|
||||
layer.MountCount--
|
||||
return r.Save()
|
||||
@@ -495,20 +500,11 @@ func (r *layerStore) Unmount(id string) error {
|
||||
}
|
||||
|
||||
func (r *layerStore) removeName(layer *Layer, name string) {
|
||||
newNames := []string{}
|
||||
for _, oldName := range layer.Names {
|
||||
if oldName != name {
|
||||
newNames = append(newNames, oldName)
|
||||
}
|
||||
}
|
||||
layer.Names = newNames
|
||||
layer.Names = stringSliceWithoutValue(layer.Names, name)
|
||||
}
|
||||
|
||||
func (r *layerStore) SetNames(id string, names []string) error {
|
||||
if layer, ok := r.byname[id]; ok {
|
||||
id = layer.ID
|
||||
}
|
||||
if layer, ok := r.byid[id]; ok {
|
||||
if layer, ok := r.lookup(id); ok {
|
||||
for _, name := range layer.Names {
|
||||
delete(r.byname, name)
|
||||
}
|
||||
@@ -525,20 +521,14 @@ func (r *layerStore) SetNames(id string, names []string) error {
|
||||
}
|
||||
|
||||
func (r *layerStore) GetMetadata(id string) (string, error) {
|
||||
if layer, ok := r.byname[id]; ok {
|
||||
id = layer.ID
|
||||
}
|
||||
if layer, ok := r.byid[id]; ok {
|
||||
if layer, ok := r.lookup(id); ok {
|
||||
return layer.Metadata, nil
|
||||
}
|
||||
return "", ErrLayerUnknown
|
||||
}
|
||||
|
||||
func (r *layerStore) SetMetadata(id, metadata string) error {
|
||||
if layer, ok := r.byname[id]; ok {
|
||||
id = layer.ID
|
||||
}
|
||||
if layer, ok := r.byid[id]; ok {
|
||||
if layer, ok := r.lookup(id); ok {
|
||||
layer.Metadata = metadata
|
||||
return r.Save()
|
||||
}
|
||||
@@ -550,13 +540,12 @@ func (r *layerStore) tspath(id string) string {
|
||||
}
|
||||
|
||||
func (r *layerStore) Delete(id string) error {
|
||||
if layer, ok := r.byname[id]; ok {
|
||||
id = layer.ID
|
||||
}
|
||||
if _, ok := r.byid[id]; !ok {
|
||||
layer, ok := r.lookup(id)
|
||||
if !ok {
|
||||
return ErrLayerUnknown
|
||||
}
|
||||
for r.byid[id].MountCount > 0 {
|
||||
id = layer.ID
|
||||
for layer.MountCount > 0 {
|
||||
if err := r.Unmount(id); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -564,66 +553,55 @@ func (r *layerStore) Delete(id string) error {
|
||||
err := r.driver.Remove(id)
|
||||
if err == nil {
|
||||
os.Remove(r.tspath(id))
|
||||
if layer, ok := r.byid[id]; ok {
|
||||
pslice := r.byparent[layer.Parent]
|
||||
newPslice := []*Layer{}
|
||||
for _, candidate := range pslice {
|
||||
if candidate.ID != id {
|
||||
newPslice = append(newPslice, candidate)
|
||||
}
|
||||
pslice := r.byparent[layer.Parent]
|
||||
newPslice := []*Layer{}
|
||||
for _, candidate := range pslice {
|
||||
if candidate.ID != id {
|
||||
newPslice = append(newPslice, candidate)
|
||||
}
|
||||
delete(r.byid, layer.ID)
|
||||
if len(newPslice) > 0 {
|
||||
r.byparent[layer.Parent] = newPslice
|
||||
} else {
|
||||
delete(r.byparent, layer.Parent)
|
||||
}
|
||||
for _, name := range layer.Names {
|
||||
delete(r.byname, name)
|
||||
}
|
||||
if layer.MountPoint != "" {
|
||||
delete(r.bymount, layer.MountPoint)
|
||||
}
|
||||
newLayers := []Layer{}
|
||||
for _, candidate := range r.layers {
|
||||
if candidate.ID != id {
|
||||
newLayers = append(newLayers, candidate)
|
||||
}
|
||||
}
|
||||
r.layers = newLayers
|
||||
if err = r.Save(); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(r.byid, id)
|
||||
r.idindex.Delete(id)
|
||||
if len(newPslice) > 0 {
|
||||
r.byparent[layer.Parent] = newPslice
|
||||
} else {
|
||||
delete(r.byparent, layer.Parent)
|
||||
}
|
||||
for _, name := range layer.Names {
|
||||
delete(r.byname, name)
|
||||
}
|
||||
if layer.MountPoint != "" {
|
||||
delete(r.bymount, layer.MountPoint)
|
||||
}
|
||||
newLayers := []Layer{}
|
||||
for _, candidate := range r.layers {
|
||||
if candidate.ID != id {
|
||||
newLayers = append(newLayers, candidate)
|
||||
}
|
||||
}
|
||||
r.layers = newLayers
|
||||
if err = r.Save(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *layerStore) Lookup(name string) (id string, err error) {
|
||||
layer, ok := r.byname[name]
|
||||
if !ok {
|
||||
layer, ok = r.byid[name]
|
||||
if !ok {
|
||||
return "", ErrLayerUnknown
|
||||
}
|
||||
if layer, ok := r.lookup(name); ok {
|
||||
return layer.ID, nil
|
||||
}
|
||||
return layer.ID, nil
|
||||
return "", ErrLayerUnknown
|
||||
}
|
||||
|
||||
func (r *layerStore) Exists(id string) bool {
|
||||
if layer, ok := r.byname[id]; ok {
|
||||
id = layer.ID
|
||||
}
|
||||
l, exists := r.byid[id]
|
||||
return l != nil && exists
|
||||
_, ok := r.lookup(id)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *layerStore) Get(id string) (*Layer, error) {
|
||||
if l, ok := r.byname[id]; ok {
|
||||
return l, nil
|
||||
}
|
||||
if l, ok := r.byid[id]; ok {
|
||||
return l, nil
|
||||
if layer, ok := r.lookup(id); ok {
|
||||
return layer, nil
|
||||
}
|
||||
return nil, ErrLayerUnknown
|
||||
}
|
||||
@@ -641,22 +619,32 @@ func (r *layerStore) Wipe() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *layerStore) Changes(from, to string) ([]archive.Change, error) {
|
||||
if layer, ok := r.byname[from]; ok {
|
||||
from = layer.ID
|
||||
}
|
||||
if layer, ok := r.byname[to]; ok {
|
||||
to = layer.ID
|
||||
func (r *layerStore) findParentAndLayer(from, to string) (fromID string, toID string, fromLayer *Layer, toLayer *Layer, err error) {
|
||||
var ok bool
|
||||
toLayer, ok = r.lookup(to)
|
||||
if !ok {
|
||||
return "", "", nil, nil, ErrLayerUnknown
|
||||
}
|
||||
to = toLayer.ID
|
||||
if from == "" {
|
||||
if layer, ok := r.byid[to]; ok {
|
||||
from = layer.Parent
|
||||
from = toLayer.Parent
|
||||
}
|
||||
if from != "" {
|
||||
fromLayer, ok = r.lookup(from)
|
||||
if !ok {
|
||||
fromLayer, ok = r.lookup(toLayer.Parent)
|
||||
if !ok {
|
||||
return "", "", nil, nil, ErrParentUnknown
|
||||
}
|
||||
}
|
||||
from = fromLayer.ID
|
||||
}
|
||||
if to == "" {
|
||||
return nil, ErrLayerUnknown
|
||||
}
|
||||
if _, ok := r.byid[to]; !ok {
|
||||
return from, to, fromLayer, toLayer, nil
|
||||
}
|
||||
|
||||
func (r *layerStore) Changes(from, to string) ([]archive.Change, error) {
|
||||
from, to, _, _, err := r.findParentAndLayer(from, to)
|
||||
if err != nil {
|
||||
return nil, ErrLayerUnknown
|
||||
}
|
||||
return r.driver.Changes(to, from)
|
||||
@@ -694,32 +682,19 @@ func (r *layerStore) newFileGetter(id string) (drivers.FileGetCloser, error) {
|
||||
func (r *layerStore) Diff(from, to string) (io.ReadCloser, error) {
|
||||
var metadata storage.Unpacker
|
||||
|
||||
if layer, ok := r.byname[from]; ok {
|
||||
from = layer.ID
|
||||
}
|
||||
if layer, ok := r.byname[to]; ok {
|
||||
to = layer.ID
|
||||
}
|
||||
if from == "" {
|
||||
if layer, ok := r.byid[to]; ok {
|
||||
from = layer.Parent
|
||||
}
|
||||
}
|
||||
if to == "" {
|
||||
return nil, ErrParentUnknown
|
||||
}
|
||||
if _, ok := r.byid[to]; !ok {
|
||||
from, to, _, toLayer, err := r.findParentAndLayer(from, to)
|
||||
if err != nil {
|
||||
return nil, ErrLayerUnknown
|
||||
}
|
||||
compression := archive.Uncompressed
|
||||
if cflag, ok := r.byid[to].Flags[compressionFlag]; ok {
|
||||
if cflag, ok := toLayer.Flags[compressionFlag]; ok {
|
||||
if ctype, ok := cflag.(float64); ok {
|
||||
compression = archive.Compression(ctype)
|
||||
} else if ctype, ok := cflag.(archive.Compression); ok {
|
||||
compression = archive.Compression(ctype)
|
||||
}
|
||||
}
|
||||
if from != r.byid[to].Parent {
|
||||
if from != toLayer.Parent {
|
||||
diff, err := r.driver.Diff(to, from)
|
||||
if err == nil && (compression != archive.Uncompressed) {
|
||||
preader, pwriter := io.Pipe()
|
||||
@@ -797,31 +772,15 @@ func (r *layerStore) Diff(from, to string) (io.ReadCloser, error) {
|
||||
}
|
||||
|
||||
func (r *layerStore) DiffSize(from, to string) (size int64, err error) {
|
||||
if layer, ok := r.byname[from]; ok {
|
||||
from = layer.ID
|
||||
}
|
||||
if layer, ok := r.byname[to]; ok {
|
||||
to = layer.ID
|
||||
}
|
||||
if from == "" {
|
||||
if layer, ok := r.byid[to]; ok {
|
||||
from = layer.Parent
|
||||
}
|
||||
}
|
||||
if to == "" {
|
||||
return -1, ErrParentUnknown
|
||||
}
|
||||
if _, ok := r.byid[to]; !ok {
|
||||
from, to, _, _, err = r.findParentAndLayer(from, to)
|
||||
if err != nil {
|
||||
return -1, ErrLayerUnknown
|
||||
}
|
||||
return r.driver.DiffSize(to, from)
|
||||
}
|
||||
|
||||
func (r *layerStore) ApplyDiff(to string, diff archive.Reader) (size int64, err error) {
|
||||
if layer, ok := r.byname[to]; ok {
|
||||
to = layer.ID
|
||||
}
|
||||
layer, ok := r.byid[to]
|
||||
layer, ok := r.lookup(to)
|
||||
if !ok {
|
||||
return -1, ErrLayerUnknown
|
||||
}
|
||||
|
37
vendor/github.com/containers/storage/storage/lockfile.go
generated
vendored
37
vendor/github.com/containers/storage/storage/lockfile.go
generated
vendored
@@ -4,9 +4,10 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/containers/storage/pkg/stringid"
|
||||
)
|
||||
|
||||
@@ -51,7 +52,7 @@ func GetLockfile(path string) (Locker, error) {
|
||||
if locker, ok := lockfiles[filepath.Clean(path)]; ok {
|
||||
return locker, nil
|
||||
}
|
||||
fd, err := syscall.Open(filepath.Clean(path), os.O_RDWR|os.O_CREATE, syscall.S_IRUSR|syscall.S_IWUSR)
|
||||
fd, err := unix.Open(filepath.Clean(path), os.O_RDWR|os.O_CREATE, unix.S_IRUSR|unix.S_IWUSR)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -61,28 +62,28 @@ func GetLockfile(path string) (Locker, error) {
|
||||
}
|
||||
|
||||
func (l *lockfile) Lock() {
|
||||
lk := syscall.Flock_t{
|
||||
Type: syscall.F_WRLCK,
|
||||
lk := unix.Flock_t{
|
||||
Type: unix.F_WRLCK,
|
||||
Whence: int16(os.SEEK_SET),
|
||||
Start: 0,
|
||||
Len: 0,
|
||||
Pid: int32(os.Getpid()),
|
||||
}
|
||||
l.mu.Lock()
|
||||
for syscall.FcntlFlock(l.fd, syscall.F_SETLKW, &lk) != nil {
|
||||
for unix.FcntlFlock(l.fd, unix.F_SETLKW, &lk) != nil {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lockfile) Unlock() {
|
||||
lk := syscall.Flock_t{
|
||||
Type: syscall.F_UNLCK,
|
||||
lk := unix.Flock_t{
|
||||
Type: unix.F_UNLCK,
|
||||
Whence: int16(os.SEEK_SET),
|
||||
Start: 0,
|
||||
Len: 0,
|
||||
Pid: int32(os.Getpid()),
|
||||
}
|
||||
for syscall.FcntlFlock(l.fd, syscall.F_SETLKW, &lk) != nil {
|
||||
for unix.FcntlFlock(l.fd, unix.F_SETLKW, &lk) != nil {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
@@ -91,18 +92,18 @@ func (l *lockfile) Unlock() {
|
||||
func (l *lockfile) Touch() error {
|
||||
l.lw = stringid.GenerateRandomID()
|
||||
id := []byte(l.lw)
|
||||
_, err := syscall.Seek(int(l.fd), 0, os.SEEK_SET)
|
||||
_, err := unix.Seek(int(l.fd), 0, os.SEEK_SET)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := syscall.Write(int(l.fd), id)
|
||||
n, err := unix.Write(int(l.fd), id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != len(id) {
|
||||
return syscall.ENOSPC
|
||||
return unix.ENOSPC
|
||||
}
|
||||
err = syscall.Fsync(int(l.fd))
|
||||
err = unix.Fsync(int(l.fd))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -111,16 +112,16 @@ func (l *lockfile) Touch() error {
|
||||
|
||||
func (l *lockfile) Modified() (bool, error) {
|
||||
id := []byte(l.lw)
|
||||
_, err := syscall.Seek(int(l.fd), 0, os.SEEK_SET)
|
||||
_, err := unix.Seek(int(l.fd), 0, os.SEEK_SET)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
n, err := syscall.Read(int(l.fd), id)
|
||||
n, err := unix.Read(int(l.fd), id)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
if n != len(id) {
|
||||
return true, syscall.ENOSPC
|
||||
return true, unix.ENOSPC
|
||||
}
|
||||
lw := l.lw
|
||||
l.lw = string(id)
|
||||
@@ -128,11 +129,11 @@ func (l *lockfile) Modified() (bool, error) {
|
||||
}
|
||||
|
||||
func (l *lockfile) TouchedSince(when time.Time) bool {
|
||||
st := syscall.Stat_t{}
|
||||
err := syscall.Fstat(int(l.fd), &st)
|
||||
st := unix.Stat_t{}
|
||||
err := unix.Fstat(int(l.fd), &st)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
touched := time.Unix(st.Mtim.Unix())
|
||||
touched := time.Unix(statTMtimeUnix(st))
|
||||
return when.Before(touched)
|
||||
}
|
||||
|
11
vendor/github.com/containers/storage/storage/stat_mtim.go
generated
vendored
Normal file
11
vendor/github.com/containers/storage/storage/stat_mtim.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// +build linux solaris
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func statTMtimeUnix(st unix.Stat_t) (int64, int64) {
|
||||
return st.Mtim.Unix()
|
||||
}
|
11
vendor/github.com/containers/storage/storage/stat_mtimespec.go
generated
vendored
Normal file
11
vendor/github.com/containers/storage/storage/stat_mtimespec.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// +build !linux,!solaris
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func statTMtimeUnix(st unix.Stat_t) (int64, int64) {
|
||||
return st.Mtimespec.Unix()
|
||||
}
|
11
vendor/github.com/containers/storage/storage/store.go
generated
vendored
11
vendor/github.com/containers/storage/storage/store.go
generated
vendored
@@ -2176,6 +2176,17 @@ func makeBigDataBaseName(key string) string {
|
||||
return key
|
||||
}
|
||||
|
||||
func stringSliceWithoutValue(slice []string, value string) []string {
|
||||
modified := []string{}
|
||||
for _, v := range slice {
|
||||
if v == value {
|
||||
continue
|
||||
}
|
||||
modified = append(modified, v)
|
||||
}
|
||||
return modified
|
||||
}
|
||||
|
||||
func init() {
|
||||
DefaultStoreOptions.RunRoot = "/var/run/containers/storage"
|
||||
DefaultStoreOptions.GraphRoot = "/var/lib/containers/storage"
|
||||
|
3
vendor/github.com/docker/distribution/README.md
generated
vendored
3
vendor/github.com/docker/distribution/README.md
generated
vendored
@@ -76,8 +76,7 @@ may be the better choice.
|
||||
For those who have previously deployed their own registry based on the Registry
|
||||
1.0 implementation and wish to deploy a Registry 2.0 while retaining images,
|
||||
data migration is required. A tool to assist with migration efforts has been
|
||||
created. For more information see [docker/migrator]
|
||||
(https://github.com/docker/migrator).
|
||||
created. For more information see [docker/migrator](https://github.com/docker/migrator).
|
||||
|
||||
## Contribute
|
||||
|
||||
|
2
vendor/github.com/docker/distribution/reference/reference.go
generated
vendored
2
vendor/github.com/docker/distribution/reference/reference.go
generated
vendored
@@ -15,7 +15,7 @@
|
||||
// tag := /[\w][\w.-]{0,127}/
|
||||
//
|
||||
// digest := digest-algorithm ":" digest-hex
|
||||
// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]
|
||||
// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]*
|
||||
// digest-algorithm-separator := /[+.-_]/
|
||||
// digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/
|
||||
// digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
|
||||
|
20
vendor/github.com/opencontainers/runc/README.md
generated
vendored
20
vendor/github.com/opencontainers/runc/README.md
generated
vendored
@@ -117,8 +117,8 @@ Assuming you have an OCI bundle from the previous step you can execute the conta
|
||||
The first way is to use the convenience command `run` that will handle creating, starting, and deleting the container after it exits.
|
||||
|
||||
```bash
|
||||
# run as root
|
||||
cd /mycontainer
|
||||
|
||||
runc run mycontainerid
|
||||
```
|
||||
|
||||
@@ -165,8 +165,8 @@ Now we can go though the lifecycle operations in your shell.
|
||||
|
||||
|
||||
```bash
|
||||
# run as root
|
||||
cd /mycontainer
|
||||
|
||||
runc create mycontainerid
|
||||
|
||||
# view the container is created and in the "created" state
|
||||
@@ -185,6 +185,22 @@ runc delete mycontainerid
|
||||
This adds more complexity but allows higher level systems to manage runc and provides points in the containers creation to setup various settings after the container has created and/or before it is deleted.
|
||||
This is commonly used to setup the container's network stack after `create` but before `start` where the user's defined process will be running.
|
||||
|
||||
#### Rootless containers
|
||||
`runc` has the ability to run containers without root privileges. This is called `rootless`. You need to pass some parameters to `runc` in order to run rootless containers. See below and compare with the previous version. Run the following commands as an ordinary user:
|
||||
```bash
|
||||
# Same as the first example
|
||||
mkdir ~/mycontainer
|
||||
cd ~/mycontainer
|
||||
mkdir rootfs
|
||||
docker export $(docker create busybox) | tar -C rootfs -xvf -
|
||||
|
||||
# The --rootless parameter instructs runc spec to generate a configuration for a rootless container, which will allow you to run the container as a non-root user.
|
||||
runc spec --rootless
|
||||
|
||||
# The --root parameter tells runc where to store the container state. It must be writable by the user.
|
||||
runc --root /tmp/runc run mycontainerid
|
||||
```
|
||||
|
||||
#### Supervisors
|
||||
|
||||
`runc` can be used with process supervisors and init systems to ensure that containers are restarted when they exit.
|
||||
|
148
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.c
generated
vendored
148
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.c
generated
vendored
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 SUSE LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cmsg.h"
|
||||
|
||||
#define error(fmt, ...) \
|
||||
({ \
|
||||
fprintf(stderr, "nsenter: " fmt ": %m\n", ##__VA_ARGS__); \
|
||||
errno = ECOMM; \
|
||||
goto err; /* return value */ \
|
||||
})
|
||||
|
||||
/*
|
||||
* Sends a file descriptor along the sockfd provided. Returns the return
|
||||
* value of sendmsg(2). Any synchronisation and preparation of state
|
||||
* should be done external to this (we expect the other side to be in
|
||||
* recvfd() in the code).
|
||||
*/
|
||||
ssize_t sendfd(int sockfd, struct file_t file)
|
||||
{
|
||||
struct msghdr msg = {0};
|
||||
struct iovec iov[1] = {0};
|
||||
struct cmsghdr *cmsg;
|
||||
int *fdptr;
|
||||
int ret;
|
||||
|
||||
union {
|
||||
char buf[CMSG_SPACE(sizeof(file.fd))];
|
||||
struct cmsghdr align;
|
||||
} u;
|
||||
|
||||
/*
|
||||
* We need to send some other data along with the ancillary data,
|
||||
* otherwise the other side won't recieve any data. This is very
|
||||
* well-hidden in the documentation (and only applies to
|
||||
* SOCK_STREAM). See the bottom part of unix(7).
|
||||
*/
|
||||
iov[0].iov_base = file.name;
|
||||
iov[0].iov_len = strlen(file.name) + 1;
|
||||
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = u.buf;
|
||||
msg.msg_controllen = sizeof(u.buf);
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
|
||||
fdptr = (int *) CMSG_DATA(cmsg);
|
||||
memcpy(fdptr, &file.fd, sizeof(int));
|
||||
|
||||
return sendmsg(sockfd, &msg, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Receives a file descriptor from the sockfd provided. Returns the file
|
||||
* descriptor as sent from sendfd(). It will return the file descriptor
|
||||
* or die (literally) trying. Any synchronisation and preparation of
|
||||
* state should be done external to this (we expect the other side to be
|
||||
* in sendfd() in the code).
|
||||
*/
|
||||
struct file_t recvfd(int sockfd)
|
||||
{
|
||||
struct msghdr msg = {0};
|
||||
struct iovec iov[1] = {0};
|
||||
struct cmsghdr *cmsg;
|
||||
struct file_t file = {0};
|
||||
int *fdptr;
|
||||
int olderrno;
|
||||
|
||||
union {
|
||||
char buf[CMSG_SPACE(sizeof(file.fd))];
|
||||
struct cmsghdr align;
|
||||
} u;
|
||||
|
||||
/* Allocate a buffer. */
|
||||
/* TODO: Make this dynamic with MSG_PEEK. */
|
||||
file.name = malloc(TAG_BUFFER);
|
||||
if (!file.name)
|
||||
error("recvfd: failed to allocate file.tag buffer\n");
|
||||
|
||||
/*
|
||||
* We need to "recieve" the non-ancillary data even though we don't
|
||||
* plan to use it at all. Otherwise, things won't work as expected.
|
||||
* See unix(7) and other well-hidden documentation.
|
||||
*/
|
||||
iov[0].iov_base = file.name;
|
||||
iov[0].iov_len = TAG_BUFFER;
|
||||
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = u.buf;
|
||||
msg.msg_controllen = sizeof(u.buf);
|
||||
|
||||
ssize_t ret = recvmsg(sockfd, &msg, 0);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (!cmsg)
|
||||
error("recvfd: got NULL from CMSG_FIRSTHDR");
|
||||
if (cmsg->cmsg_level != SOL_SOCKET)
|
||||
error("recvfd: expected SOL_SOCKET in cmsg: %d", cmsg->cmsg_level);
|
||||
if (cmsg->cmsg_type != SCM_RIGHTS)
|
||||
error("recvfd: expected SCM_RIGHTS in cmsg: %d", cmsg->cmsg_type);
|
||||
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
|
||||
error("recvfd: expected correct CMSG_LEN in cmsg: %lu", (unsigned long)cmsg->cmsg_len);
|
||||
|
||||
fdptr = (int *) CMSG_DATA(cmsg);
|
||||
if (!fdptr || *fdptr < 0)
|
||||
error("recvfd: recieved invalid pointer");
|
||||
|
||||
file.fd = *fdptr;
|
||||
return file;
|
||||
|
||||
err:
|
||||
olderrno = errno;
|
||||
free(file.name);
|
||||
errno = olderrno;
|
||||
return (struct file_t){0};
|
||||
}
|
57
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go
generated
vendored
57
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.go
generated
vendored
@@ -1,57 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
package utils
|
||||
|
||||
/*
|
||||
* Copyright 2016 SUSE LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include "cmsg.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// RecvFd waits for a file descriptor to be sent over the given AF_UNIX
|
||||
// socket. The file name of the remote file descriptor will be recreated
|
||||
// locally (it is sent as non-auxiliary data in the same payload).
|
||||
func RecvFd(socket *os.File) (*os.File, error) {
|
||||
file, err := C.recvfd(C.int(socket.Fd()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer C.free(unsafe.Pointer(file.name))
|
||||
return os.NewFile(uintptr(file.fd), C.GoString(file.name)), nil
|
||||
}
|
||||
|
||||
// SendFd sends a file descriptor over the given AF_UNIX socket. In
|
||||
// addition, the file.Name() of the given file will also be sent as
|
||||
// non-auxiliary data in the same payload (allowing to send contextual
|
||||
// information for a file descriptor).
|
||||
func SendFd(socket, file *os.File) error {
|
||||
var cfile C.struct_file_t
|
||||
cfile.fd = C.int(file.Fd())
|
||||
cfile.name = C.CString(file.Name())
|
||||
defer C.free(unsafe.Pointer(cfile.name))
|
||||
|
||||
_, err := C.sendfd(C.int(socket.Fd()), cfile)
|
||||
return err
|
||||
}
|
36
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.h
generated
vendored
36
vendor/github.com/opencontainers/runc/libcontainer/utils/cmsg.h
generated
vendored
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 SUSE LLC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(CMSG_H)
|
||||
#define CMSG_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
/* TODO: Implement this properly with MSG_PEEK. */
|
||||
#define TAG_BUFFER 4096
|
||||
|
||||
/* This mirrors Go's (*os.File). */
|
||||
struct file_t {
|
||||
char *name;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct file_t recvfd(int sockfd);
|
||||
ssize_t sendfd(int sockfd, struct file_t file);
|
||||
|
||||
#endif /* !defined(CMSG_H) */
|
126
vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go
generated
vendored
126
vendor/github.com/opencontainers/runc/libcontainer/utils/utils.go
generated
vendored
@@ -1,126 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
exitSignalOffset = 128
|
||||
)
|
||||
|
||||
// GenerateRandomName returns a new name joined with a prefix. This size
|
||||
// specified is used to truncate the randomly generated value
|
||||
func GenerateRandomName(prefix string, size int) (string, error) {
|
||||
id := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand.Reader, id); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if size > 64 {
|
||||
size = 64
|
||||
}
|
||||
return prefix + hex.EncodeToString(id)[:size], nil
|
||||
}
|
||||
|
||||
// ResolveRootfs ensures that the current working directory is
|
||||
// not a symlink and returns the absolute path to the rootfs
|
||||
func ResolveRootfs(uncleanRootfs string) (string, error) {
|
||||
rootfs, err := filepath.Abs(uncleanRootfs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.EvalSymlinks(rootfs)
|
||||
}
|
||||
|
||||
// ExitStatus returns the correct exit status for a process based on if it
|
||||
// was signaled or exited cleanly
|
||||
func ExitStatus(status syscall.WaitStatus) int {
|
||||
if status.Signaled() {
|
||||
return exitSignalOffset + int(status.Signal())
|
||||
}
|
||||
return status.ExitStatus()
|
||||
}
|
||||
|
||||
// WriteJSON writes the provided struct v to w using standard json marshaling
|
||||
func WriteJSON(w io.Writer, v interface{}) error {
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
// CleanPath makes a path safe for use with filepath.Join. This is done by not
|
||||
// only cleaning the path, but also (if the path is relative) adding a leading
|
||||
// '/' and cleaning it (then removing the leading '/'). This ensures that a
|
||||
// path resulting from prepending another path will always resolve to lexically
|
||||
// be a subdirectory of the prefixed path. This is all done lexically, so paths
|
||||
// that include symlinks won't be safe as a result of using CleanPath.
|
||||
func CleanPath(path string) string {
|
||||
// Deal with empty strings nicely.
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Ensure that all paths are cleaned (especially problematic ones like
|
||||
// "/../../../../../" which can cause lots of issues).
|
||||
path = filepath.Clean(path)
|
||||
|
||||
// If the path isn't absolute, we need to do more processing to fix paths
|
||||
// such as "../../../../<etc>/some/path". We also shouldn't convert absolute
|
||||
// paths to relative ones.
|
||||
if !filepath.IsAbs(path) {
|
||||
path = filepath.Clean(string(os.PathSeparator) + path)
|
||||
// This can't fail, as (by definition) all paths are relative to root.
|
||||
path, _ = filepath.Rel(string(os.PathSeparator), path)
|
||||
}
|
||||
|
||||
// Clean the path again for good measure.
|
||||
return filepath.Clean(path)
|
||||
}
|
||||
|
||||
// SearchLabels searches a list of key-value pairs for the provided key and
|
||||
// returns the corresponding value. The pairs must be separated with '='.
|
||||
func SearchLabels(labels []string, query string) string {
|
||||
for _, l := range labels {
|
||||
parts := strings.SplitN(l, "=", 2)
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
if parts[0] == query {
|
||||
return parts[1]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Annotations returns the bundle path and user defined annotations from the
|
||||
// libcontainer state. We need to remove the bundle because that is a label
|
||||
// added by libcontainer.
|
||||
func Annotations(labels []string) (bundle string, userAnnotations map[string]string) {
|
||||
userAnnotations = make(map[string]string)
|
||||
for _, l := range labels {
|
||||
parts := strings.SplitN(l, "=", 2)
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
if parts[0] == "bundle" {
|
||||
bundle = parts[1]
|
||||
} else {
|
||||
userAnnotations[parts[0]] = parts[1]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetIntSize() int {
|
||||
return int(unsafe.Sizeof(1))
|
||||
}
|
43
vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go
generated
vendored
43
vendor/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go
generated
vendored
@@ -1,43 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func CloseExecFrom(minFd int) error {
|
||||
fdList, err := ioutil.ReadDir("/proc/self/fd")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, fi := range fdList {
|
||||
fd, err := strconv.Atoi(fi.Name())
|
||||
if err != nil {
|
||||
// ignore non-numeric file names
|
||||
continue
|
||||
}
|
||||
|
||||
if fd < minFd {
|
||||
// ignore descriptors lower than our specified minimum
|
||||
continue
|
||||
}
|
||||
|
||||
// intentionally ignore errors from syscall.CloseOnExec
|
||||
syscall.CloseOnExec(fd)
|
||||
// the cases where this might fail are basically file descriptors that have already been closed (including and especially the one that was created when ioutil.ReadDir did the "opendir" syscall)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewSockPair returns a new unix socket pair
|
||||
func NewSockPair(name string) (parent *os.File, child *os.File, err error) {
|
||||
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM|syscall.SOCK_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return os.NewFile(uintptr(fds[1]), name+"-p"), os.NewFile(uintptr(fds[0]), name+"-c"), nil
|
||||
}
|
4
vendor/github.com/opencontainers/selinux/go-selinux/selinux.go
generated
vendored
4
vendor/github.com/opencontainers/selinux/go-selinux/selinux.go
generated
vendored
@@ -213,7 +213,7 @@ func SetFileLabel(path string, label string) error {
|
||||
return lsetxattr(path, xattrNameSelinux, []byte(label), 0)
|
||||
}
|
||||
|
||||
// Filecon returns the SELinux label for this path or returns an error.
|
||||
// FileLabel returns the SELinux label for this path or returns an error.
|
||||
func FileLabel(path string) (string, error) {
|
||||
label, err := lgetxattr(path, xattrNameSelinux)
|
||||
if err != nil {
|
||||
@@ -331,7 +331,7 @@ func EnforceMode() int {
|
||||
}
|
||||
|
||||
/*
|
||||
SetEnforce sets the current SELinux mode Enforcing, Permissive.
|
||||
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 {
|
||||
|
8
vendor/github.com/pkg/errors/stack.go
generated
vendored
8
vendor/github.com/pkg/errors/stack.go
generated
vendored
@@ -79,6 +79,14 @@ func (f Frame) Format(s fmt.State, verb rune) {
|
||||
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
|
||||
type StackTrace []Frame
|
||||
|
||||
// Format formats the stack of Frames according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s lists source files for each Frame in the stack
|
||||
// %v lists the source file and line number for each Frame in the stack
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+v Prints filename, function, and line number for each Frame in the stack.
|
||||
func (st StackTrace) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
|
20
vendor/github.com/tchap/go-patricia/LICENSE
generated
vendored
Normal file
20
vendor/github.com/tchap/go-patricia/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 The AUTHORS
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
123
vendor/github.com/tchap/go-patricia/README.md
generated
vendored
Normal file
123
vendor/github.com/tchap/go-patricia/README.md
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
# go-patricia #
|
||||
|
||||
**Documentation**: [GoDoc](http://godoc.org/github.com/tchap/go-patricia/patricia)<br />
|
||||
**Build Status**: [](https://drone.io/github.com/tchap/go-patricia/latest)<br />
|
||||
**Test Coverage**: [](https://coveralls.io/r/tchap/go-patricia)
|
||||
|
||||
## About ##
|
||||
|
||||
A generic patricia trie (also called radix tree) implemented in Go (Golang).
|
||||
|
||||
The patricia trie as implemented in this library enables fast visiting of items
|
||||
in some particular ways:
|
||||
|
||||
1. visit all items saved in the tree,
|
||||
2. visit all items matching particular prefix (visit subtree), or
|
||||
3. given a string, visit all items matching some prefix of that string.
|
||||
|
||||
`[]byte` type is used for keys, `interface{}` for values.
|
||||
|
||||
`Trie` is not thread safe. Synchronize the access yourself.
|
||||
|
||||
### State of the Project ###
|
||||
|
||||
Apparently some people are using this, so the API should not change often.
|
||||
Any ideas on how to make the library better are still welcome.
|
||||
|
||||
More (unit) testing would be cool as well...
|
||||
|
||||
## Usage ##
|
||||
|
||||
Import the package from GitHub first.
|
||||
|
||||
```go
|
||||
import "github.com/tchap/go-patricia/patricia"
|
||||
```
|
||||
|
||||
You can as well use gopkg.in thingie:
|
||||
|
||||
```go
|
||||
import "gopkg.in/tchap/go-patricia.v2/patricia"
|
||||
```
|
||||
|
||||
Then you can start having fun.
|
||||
|
||||
```go
|
||||
printItem := func(prefix patricia.Prefix, item patricia.Item) error {
|
||||
fmt.Printf("%q: %v\n", prefix, item)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a new default trie (using the default parameter values).
|
||||
trie := NewTrie()
|
||||
|
||||
// Create a new custom trie.
|
||||
trie := NewTrie(MaxPrefixPerNode(16), MaxChildrenPerSparseNode(10))
|
||||
|
||||
// Insert some items.
|
||||
trie.Insert(Prefix("Pepa Novak"), 1)
|
||||
trie.Insert(Prefix("Pepa Sindelar"), 2)
|
||||
trie.Insert(Prefix("Karel Macha"), 3)
|
||||
trie.Insert(Prefix("Karel Hynek Macha"), 4)
|
||||
|
||||
// Just check if some things are present in the tree.
|
||||
key := Prefix("Pepa Novak")
|
||||
fmt.Printf("%q present? %v\n", key, trie.Match(key))
|
||||
// "Pepa Novak" present? true
|
||||
key = Prefix("Karel")
|
||||
fmt.Printf("Anybody called %q here? %v\n", key, trie.MatchSubtree(key))
|
||||
// Anybody called "Karel" here? true
|
||||
|
||||
// Walk the tree in alphabetical order.
|
||||
trie.Visit(printItem)
|
||||
// "Karel Hynek Macha": 4
|
||||
// "Karel Macha": 3
|
||||
// "Pepa Novak": 1
|
||||
// "Pepa Sindelar": 2
|
||||
|
||||
// Walk a subtree.
|
||||
trie.VisitSubtree(Prefix("Pepa"), printItem)
|
||||
// "Pepa Novak": 1
|
||||
// "Pepa Sindelar": 2
|
||||
|
||||
// Modify an item, then fetch it from the tree.
|
||||
trie.Set(Prefix("Karel Hynek Macha"), 10)
|
||||
key = Prefix("Karel Hynek Macha")
|
||||
fmt.Printf("%q: %v\n", key, trie.Get(key))
|
||||
// "Karel Hynek Macha": 10
|
||||
|
||||
// Walk prefixes.
|
||||
prefix := Prefix("Karel Hynek Macha je kouzelnik")
|
||||
trie.VisitPrefixes(prefix, printItem)
|
||||
// "Karel Hynek Macha": 10
|
||||
|
||||
// Delete some items.
|
||||
trie.Delete(Prefix("Pepa Novak"))
|
||||
trie.Delete(Prefix("Karel Macha"))
|
||||
|
||||
// Walk again.
|
||||
trie.Visit(printItem)
|
||||
// "Karel Hynek Macha": 10
|
||||
// "Pepa Sindelar": 2
|
||||
|
||||
// Delete a subtree.
|
||||
trie.DeleteSubtree(Prefix("Pepa"))
|
||||
|
||||
// Print what is left.
|
||||
trie.Visit(printItem)
|
||||
// "Karel Hynek Macha": 10
|
||||
```
|
||||
|
||||
## License ##
|
||||
|
||||
MIT, check the `LICENSE` file.
|
||||
|
||||
[](https://www.gittip.com/tchap/
|
||||
"Gittip Badge")
|
||||
|
||||
[](https://bitdeli.com/free
|
||||
"Bitdeli Badge")
|
325
vendor/github.com/tchap/go-patricia/patricia/children.go
generated
vendored
Normal file
325
vendor/github.com/tchap/go-patricia/patricia/children.go
generated
vendored
Normal file
@@ -0,0 +1,325 @@
|
||||
// Copyright (c) 2014 The go-patricia AUTHORS
|
||||
//
|
||||
// Use of this source code is governed by The MIT License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package patricia
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type childList interface {
|
||||
length() int
|
||||
head() *Trie
|
||||
add(child *Trie) childList
|
||||
remove(b byte)
|
||||
replace(b byte, child *Trie)
|
||||
next(b byte) *Trie
|
||||
walk(prefix *Prefix, visitor VisitorFunc) error
|
||||
print(w io.Writer, indent int)
|
||||
total() int
|
||||
}
|
||||
|
||||
type tries []*Trie
|
||||
|
||||
func (t tries) Len() int {
|
||||
return len(t)
|
||||
}
|
||||
|
||||
func (t tries) Less(i, j int) bool {
|
||||
strings := sort.StringSlice{string(t[i].prefix), string(t[j].prefix)}
|
||||
return strings.Less(0, 1)
|
||||
}
|
||||
|
||||
func (t tries) Swap(i, j int) {
|
||||
t[i], t[j] = t[j], t[i]
|
||||
}
|
||||
|
||||
type sparseChildList struct {
|
||||
children tries
|
||||
}
|
||||
|
||||
func newSparseChildList(maxChildrenPerSparseNode int) childList {
|
||||
return &sparseChildList{
|
||||
children: make(tries, 0, maxChildrenPerSparseNode),
|
||||
}
|
||||
}
|
||||
|
||||
func (list *sparseChildList) length() int {
|
||||
return len(list.children)
|
||||
}
|
||||
|
||||
func (list *sparseChildList) head() *Trie {
|
||||
return list.children[0]
|
||||
}
|
||||
|
||||
func (list *sparseChildList) add(child *Trie) childList {
|
||||
// Search for an empty spot and insert the child if possible.
|
||||
if len(list.children) != cap(list.children) {
|
||||
list.children = append(list.children, child)
|
||||
return list
|
||||
}
|
||||
|
||||
// Otherwise we have to transform to the dense list type.
|
||||
return newDenseChildList(list, child)
|
||||
}
|
||||
|
||||
func (list *sparseChildList) remove(b byte) {
|
||||
for i, node := range list.children {
|
||||
if node.prefix[0] == b {
|
||||
list.children[i] = list.children[len(list.children)-1]
|
||||
list.children[len(list.children)-1] = nil
|
||||
list.children = list.children[:len(list.children)-1]
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// This is not supposed to be reached.
|
||||
panic("removing non-existent child")
|
||||
}
|
||||
|
||||
func (list *sparseChildList) replace(b byte, child *Trie) {
|
||||
// Make a consistency check.
|
||||
if p0 := child.prefix[0]; p0 != b {
|
||||
panic(fmt.Errorf("child prefix mismatch: %v != %v", p0, b))
|
||||
}
|
||||
|
||||
// Seek the child and replace it.
|
||||
for i, node := range list.children {
|
||||
if node.prefix[0] == b {
|
||||
list.children[i] = child
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (list *sparseChildList) next(b byte) *Trie {
|
||||
for _, child := range list.children {
|
||||
if child.prefix[0] == b {
|
||||
return child
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (list *sparseChildList) walk(prefix *Prefix, visitor VisitorFunc) error {
|
||||
|
||||
sort.Sort(list.children)
|
||||
|
||||
for _, child := range list.children {
|
||||
*prefix = append(*prefix, child.prefix...)
|
||||
if child.item != nil {
|
||||
err := visitor(*prefix, child.item)
|
||||
if err != nil {
|
||||
if err == SkipSubtree {
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
|
||||
continue
|
||||
}
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := child.children.walk(prefix, visitor)
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (list *sparseChildList) total() int {
|
||||
tot := 0
|
||||
for _, child := range list.children {
|
||||
if child != nil {
|
||||
tot = tot + child.total()
|
||||
}
|
||||
}
|
||||
return tot
|
||||
}
|
||||
|
||||
func (list *sparseChildList) print(w io.Writer, indent int) {
|
||||
for _, child := range list.children {
|
||||
if child != nil {
|
||||
child.print(w, indent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type denseChildList struct {
|
||||
min int
|
||||
max int
|
||||
numChildren int
|
||||
headIndex int
|
||||
children []*Trie
|
||||
}
|
||||
|
||||
func newDenseChildList(list *sparseChildList, child *Trie) childList {
|
||||
var (
|
||||
min int = 255
|
||||
max int = 0
|
||||
)
|
||||
for _, child := range list.children {
|
||||
b := int(child.prefix[0])
|
||||
if b < min {
|
||||
min = b
|
||||
}
|
||||
if b > max {
|
||||
max = b
|
||||
}
|
||||
}
|
||||
|
||||
b := int(child.prefix[0])
|
||||
if b < min {
|
||||
min = b
|
||||
}
|
||||
if b > max {
|
||||
max = b
|
||||
}
|
||||
|
||||
children := make([]*Trie, max-min+1)
|
||||
for _, child := range list.children {
|
||||
children[int(child.prefix[0])-min] = child
|
||||
}
|
||||
children[int(child.prefix[0])-min] = child
|
||||
|
||||
return &denseChildList{
|
||||
min: min,
|
||||
max: max,
|
||||
numChildren: list.length() + 1,
|
||||
headIndex: 0,
|
||||
children: children,
|
||||
}
|
||||
}
|
||||
|
||||
func (list *denseChildList) length() int {
|
||||
return list.numChildren
|
||||
}
|
||||
|
||||
func (list *denseChildList) head() *Trie {
|
||||
return list.children[list.headIndex]
|
||||
}
|
||||
|
||||
func (list *denseChildList) add(child *Trie) childList {
|
||||
b := int(child.prefix[0])
|
||||
var i int
|
||||
|
||||
switch {
|
||||
case list.min <= b && b <= list.max:
|
||||
if list.children[b-list.min] != nil {
|
||||
panic("dense child list collision detected")
|
||||
}
|
||||
i = b - list.min
|
||||
list.children[i] = child
|
||||
|
||||
case b < list.min:
|
||||
children := make([]*Trie, list.max-b+1)
|
||||
i = 0
|
||||
children[i] = child
|
||||
copy(children[list.min-b:], list.children)
|
||||
list.children = children
|
||||
list.min = b
|
||||
|
||||
default: // b > list.max
|
||||
children := make([]*Trie, b-list.min+1)
|
||||
i = b - list.min
|
||||
children[i] = child
|
||||
copy(children, list.children)
|
||||
list.children = children
|
||||
list.max = b
|
||||
}
|
||||
|
||||
list.numChildren++
|
||||
if i < list.headIndex {
|
||||
list.headIndex = i
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (list *denseChildList) remove(b byte) {
|
||||
i := int(b) - list.min
|
||||
if list.children[i] == nil {
|
||||
// This is not supposed to be reached.
|
||||
panic("removing non-existent child")
|
||||
}
|
||||
list.numChildren--
|
||||
list.children[i] = nil
|
||||
|
||||
// Update head index.
|
||||
if i == list.headIndex {
|
||||
for ; i < len(list.children); i++ {
|
||||
if list.children[i] != nil {
|
||||
list.headIndex = i
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (list *denseChildList) replace(b byte, child *Trie) {
|
||||
// Make a consistency check.
|
||||
if p0 := child.prefix[0]; p0 != b {
|
||||
panic(fmt.Errorf("child prefix mismatch: %v != %v", p0, b))
|
||||
}
|
||||
|
||||
// Replace the child.
|
||||
list.children[int(b)-list.min] = child
|
||||
}
|
||||
|
||||
func (list *denseChildList) next(b byte) *Trie {
|
||||
i := int(b)
|
||||
if i < list.min || list.max < i {
|
||||
return nil
|
||||
}
|
||||
return list.children[i-list.min]
|
||||
}
|
||||
|
||||
func (list *denseChildList) walk(prefix *Prefix, visitor VisitorFunc) error {
|
||||
for _, child := range list.children {
|
||||
if child == nil {
|
||||
continue
|
||||
}
|
||||
*prefix = append(*prefix, child.prefix...)
|
||||
if child.item != nil {
|
||||
if err := visitor(*prefix, child.item); err != nil {
|
||||
if err == SkipSubtree {
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
|
||||
continue
|
||||
}
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := child.children.walk(prefix, visitor)
|
||||
*prefix = (*prefix)[:len(*prefix)-len(child.prefix)]
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (list *denseChildList) print(w io.Writer, indent int) {
|
||||
for _, child := range list.children {
|
||||
if child != nil {
|
||||
child.print(w, indent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (list *denseChildList) total() int {
|
||||
tot := 0
|
||||
for _, child := range list.children {
|
||||
if child != nil {
|
||||
tot = tot + child.total()
|
||||
}
|
||||
}
|
||||
return tot
|
||||
}
|
594
vendor/github.com/tchap/go-patricia/patricia/patricia.go
generated
vendored
Normal file
594
vendor/github.com/tchap/go-patricia/patricia/patricia.go
generated
vendored
Normal file
@@ -0,0 +1,594 @@
|
||||
// Copyright (c) 2014 The go-patricia AUTHORS
|
||||
//
|
||||
// Use of this source code is governed by The MIT License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package patricia
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Trie
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const (
|
||||
DefaultMaxPrefixPerNode = 10
|
||||
DefaultMaxChildrenPerSparseNode = 8
|
||||
)
|
||||
|
||||
type (
|
||||
Prefix []byte
|
||||
Item interface{}
|
||||
VisitorFunc func(prefix Prefix, item Item) error
|
||||
)
|
||||
|
||||
// Trie is a generic patricia trie that allows fast retrieval of items by prefix.
|
||||
// and other funky stuff.
|
||||
//
|
||||
// Trie is not thread-safe.
|
||||
type Trie struct {
|
||||
prefix Prefix
|
||||
item Item
|
||||
|
||||
maxPrefixPerNode int
|
||||
maxChildrenPerSparseNode int
|
||||
|
||||
children childList
|
||||
}
|
||||
|
||||
// Public API ------------------------------------------------------------------
|
||||
|
||||
type Option func(*Trie)
|
||||
|
||||
// Trie constructor.
|
||||
func NewTrie(options ...Option) *Trie {
|
||||
trie := &Trie{}
|
||||
|
||||
for _, opt := range options {
|
||||
opt(trie)
|
||||
}
|
||||
|
||||
if trie.maxPrefixPerNode <= 0 {
|
||||
trie.maxPrefixPerNode = DefaultMaxPrefixPerNode
|
||||
}
|
||||
if trie.maxChildrenPerSparseNode <= 0 {
|
||||
trie.maxChildrenPerSparseNode = DefaultMaxChildrenPerSparseNode
|
||||
}
|
||||
|
||||
trie.children = newSparseChildList(trie.maxChildrenPerSparseNode)
|
||||
return trie
|
||||
}
|
||||
|
||||
func MaxPrefixPerNode(value int) Option {
|
||||
return func(trie *Trie) {
|
||||
trie.maxPrefixPerNode = value
|
||||
}
|
||||
}
|
||||
|
||||
func MaxChildrenPerSparseNode(value int) Option {
|
||||
return func(trie *Trie) {
|
||||
trie.maxChildrenPerSparseNode = value
|
||||
}
|
||||
}
|
||||
|
||||
// Item returns the item stored in the root of this trie.
|
||||
func (trie *Trie) Item() Item {
|
||||
return trie.item
|
||||
}
|
||||
|
||||
// Insert inserts a new item into the trie using the given prefix. Insert does
|
||||
// not replace existing items. It returns false if an item was already in place.
|
||||
func (trie *Trie) Insert(key Prefix, item Item) (inserted bool) {
|
||||
return trie.put(key, item, false)
|
||||
}
|
||||
|
||||
// Set works much like Insert, but it always sets the item, possibly replacing
|
||||
// the item previously inserted.
|
||||
func (trie *Trie) Set(key Prefix, item Item) {
|
||||
trie.put(key, item, true)
|
||||
}
|
||||
|
||||
// Get returns the item located at key.
|
||||
//
|
||||
// This method is a bit dangerous, because Get can as well end up in an internal
|
||||
// node that is not really representing any user-defined value. So when nil is
|
||||
// a valid value being used, it is not possible to tell if the value was inserted
|
||||
// into the tree by the user or not. A possible workaround for this is not to use
|
||||
// nil interface as a valid value, even using zero value of any type is enough
|
||||
// to prevent this bad behaviour.
|
||||
func (trie *Trie) Get(key Prefix) (item Item) {
|
||||
_, node, found, leftover := trie.findSubtree(key)
|
||||
if !found || len(leftover) != 0 {
|
||||
return nil
|
||||
}
|
||||
return node.item
|
||||
}
|
||||
|
||||
// Match returns what Get(prefix) != nil would return. The same warning as for
|
||||
// Get applies here as well.
|
||||
func (trie *Trie) Match(prefix Prefix) (matchedExactly bool) {
|
||||
return trie.Get(prefix) != nil
|
||||
}
|
||||
|
||||
// MatchSubtree returns true when there is a subtree representing extensions
|
||||
// to key, that is if there are any keys in the tree which have key as prefix.
|
||||
func (trie *Trie) MatchSubtree(key Prefix) (matched bool) {
|
||||
_, _, matched, _ = trie.findSubtree(key)
|
||||
return
|
||||
}
|
||||
|
||||
// Visit calls visitor on every node containing a non-nil item
|
||||
// in alphabetical order.
|
||||
//
|
||||
// If an error is returned from visitor, the function stops visiting the tree
|
||||
// and returns that error, unless it is a special error - SkipSubtree. In that
|
||||
// case Visit skips the subtree represented by the current node and continues
|
||||
// elsewhere.
|
||||
func (trie *Trie) Visit(visitor VisitorFunc) error {
|
||||
return trie.walk(nil, visitor)
|
||||
}
|
||||
|
||||
func (trie *Trie) size() int {
|
||||
n := 0
|
||||
|
||||
trie.walk(nil, func(prefix Prefix, item Item) error {
|
||||
n++
|
||||
return nil
|
||||
})
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (trie *Trie) total() int {
|
||||
return 1 + trie.children.total()
|
||||
}
|
||||
|
||||
// VisitSubtree works much like Visit, but it only visits nodes matching prefix.
|
||||
func (trie *Trie) VisitSubtree(prefix Prefix, visitor VisitorFunc) error {
|
||||
// Nil prefix not allowed.
|
||||
if prefix == nil {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
// Empty trie must be handled explicitly.
|
||||
if trie.prefix == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Locate the relevant subtree.
|
||||
_, root, found, leftover := trie.findSubtree(prefix)
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
prefix = append(prefix, leftover...)
|
||||
|
||||
// Visit it.
|
||||
return root.walk(prefix, visitor)
|
||||
}
|
||||
|
||||
// VisitPrefixes visits only nodes that represent prefixes of key.
|
||||
// To say the obvious, returning SkipSubtree from visitor makes no sense here.
|
||||
func (trie *Trie) VisitPrefixes(key Prefix, visitor VisitorFunc) error {
|
||||
// Nil key not allowed.
|
||||
if key == nil {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
// Empty trie must be handled explicitly.
|
||||
if trie.prefix == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Walk the path matching key prefixes.
|
||||
node := trie
|
||||
prefix := key
|
||||
offset := 0
|
||||
for {
|
||||
// Compute what part of prefix matches.
|
||||
common := node.longestCommonPrefixLength(key)
|
||||
key = key[common:]
|
||||
offset += common
|
||||
|
||||
// Partial match means that there is no subtree matching prefix.
|
||||
if common < len(node.prefix) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Call the visitor.
|
||||
if item := node.item; item != nil {
|
||||
if err := visitor(prefix[:offset], item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(key) == 0 {
|
||||
// This node represents key, we are finished.
|
||||
return nil
|
||||
}
|
||||
|
||||
// There is some key suffix left, move to the children.
|
||||
child := node.children.next(key[0])
|
||||
if child == nil {
|
||||
// There is nowhere to continue, return.
|
||||
return nil
|
||||
}
|
||||
|
||||
node = child
|
||||
}
|
||||
}
|
||||
|
||||
// Delete deletes the item represented by the given prefix.
|
||||
//
|
||||
// True is returned if the matching node was found and deleted.
|
||||
func (trie *Trie) Delete(key Prefix) (deleted bool) {
|
||||
// Nil prefix not allowed.
|
||||
if key == nil {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
// Empty trie must be handled explicitly.
|
||||
if trie.prefix == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Find the relevant node.
|
||||
path, found, _ := trie.findSubtreePath(key)
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
|
||||
node := path[len(path)-1]
|
||||
var parent *Trie
|
||||
if len(path) != 1 {
|
||||
parent = path[len(path)-2]
|
||||
}
|
||||
|
||||
// If the item is already set to nil, there is nothing to do.
|
||||
if node.item == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Delete the item.
|
||||
node.item = nil
|
||||
|
||||
// Initialise i before goto.
|
||||
// Will be used later in a loop.
|
||||
i := len(path) - 1
|
||||
|
||||
// In case there are some child nodes, we cannot drop the whole subtree.
|
||||
// We can try to compact nodes, though.
|
||||
if node.children.length() != 0 {
|
||||
goto Compact
|
||||
}
|
||||
|
||||
// In case we are at the root, just reset it and we are done.
|
||||
if parent == nil {
|
||||
node.reset()
|
||||
return true
|
||||
}
|
||||
|
||||
// We can drop a subtree.
|
||||
// Find the first ancestor that has its value set or it has 2 or more child nodes.
|
||||
// That will be the node where to drop the subtree at.
|
||||
for ; i >= 0; i-- {
|
||||
if current := path[i]; current.item != nil || current.children.length() >= 2 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the case when there is no such node.
|
||||
// In other words, we can reset the whole tree.
|
||||
if i == -1 {
|
||||
path[0].reset()
|
||||
return true
|
||||
}
|
||||
|
||||
// We can just remove the subtree here.
|
||||
node = path[i]
|
||||
if i == 0 {
|
||||
parent = nil
|
||||
} else {
|
||||
parent = path[i-1]
|
||||
}
|
||||
// i+1 is always a valid index since i is never pointing to the last node.
|
||||
// The loop above skips at least the last node since we are sure that the item
|
||||
// is set to nil and it has no children, othewise we would be compacting instead.
|
||||
node.children.remove(path[i+1].prefix[0])
|
||||
|
||||
Compact:
|
||||
// The node is set to the first non-empty ancestor,
|
||||
// so try to compact since that might be possible now.
|
||||
if compacted := node.compact(); compacted != node {
|
||||
if parent == nil {
|
||||
*node = *compacted
|
||||
} else {
|
||||
parent.children.replace(node.prefix[0], compacted)
|
||||
*parent = *parent.compact()
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// DeleteSubtree finds the subtree exactly matching prefix and deletes it.
|
||||
//
|
||||
// True is returned if the subtree was found and deleted.
|
||||
func (trie *Trie) DeleteSubtree(prefix Prefix) (deleted bool) {
|
||||
// Nil prefix not allowed.
|
||||
if prefix == nil {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
// Empty trie must be handled explicitly.
|
||||
if trie.prefix == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Locate the relevant subtree.
|
||||
parent, root, found, _ := trie.findSubtree(prefix)
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
|
||||
// If we are in the root of the trie, reset the trie.
|
||||
if parent == nil {
|
||||
root.reset()
|
||||
return true
|
||||
}
|
||||
|
||||
// Otherwise remove the root node from its parent.
|
||||
parent.children.remove(root.prefix[0])
|
||||
return true
|
||||
}
|
||||
|
||||
// Internal helper methods -----------------------------------------------------
|
||||
|
||||
func (trie *Trie) empty() bool {
|
||||
return trie.item == nil && trie.children.length() == 0
|
||||
}
|
||||
|
||||
func (trie *Trie) reset() {
|
||||
trie.prefix = nil
|
||||
trie.children = newSparseChildList(trie.maxPrefixPerNode)
|
||||
}
|
||||
|
||||
func (trie *Trie) put(key Prefix, item Item, replace bool) (inserted bool) {
|
||||
// Nil prefix not allowed.
|
||||
if key == nil {
|
||||
panic(ErrNilPrefix)
|
||||
}
|
||||
|
||||
var (
|
||||
common int
|
||||
node *Trie = trie
|
||||
child *Trie
|
||||
)
|
||||
|
||||
if node.prefix == nil {
|
||||
if len(key) <= trie.maxPrefixPerNode {
|
||||
node.prefix = key
|
||||
goto InsertItem
|
||||
}
|
||||
node.prefix = key[:trie.maxPrefixPerNode]
|
||||
key = key[trie.maxPrefixPerNode:]
|
||||
goto AppendChild
|
||||
}
|
||||
|
||||
for {
|
||||
// Compute the longest common prefix length.
|
||||
common = node.longestCommonPrefixLength(key)
|
||||
key = key[common:]
|
||||
|
||||
// Only a part matches, split.
|
||||
if common < len(node.prefix) {
|
||||
goto SplitPrefix
|
||||
}
|
||||
|
||||
// common == len(node.prefix) since never (common > len(node.prefix))
|
||||
// common == len(former key) <-> 0 == len(key)
|
||||
// -> former key == node.prefix
|
||||
if len(key) == 0 {
|
||||
goto InsertItem
|
||||
}
|
||||
|
||||
// Check children for matching prefix.
|
||||
child = node.children.next(key[0])
|
||||
if child == nil {
|
||||
goto AppendChild
|
||||
}
|
||||
node = child
|
||||
}
|
||||
|
||||
SplitPrefix:
|
||||
// Split the prefix if necessary.
|
||||
child = new(Trie)
|
||||
*child = *node
|
||||
*node = *NewTrie()
|
||||
node.prefix = child.prefix[:common]
|
||||
child.prefix = child.prefix[common:]
|
||||
child = child.compact()
|
||||
node.children = node.children.add(child)
|
||||
|
||||
AppendChild:
|
||||
// Keep appending children until whole prefix is inserted.
|
||||
// This loop starts with empty node.prefix that needs to be filled.
|
||||
for len(key) != 0 {
|
||||
child := NewTrie()
|
||||
if len(key) <= trie.maxPrefixPerNode {
|
||||
child.prefix = key
|
||||
node.children = node.children.add(child)
|
||||
node = child
|
||||
goto InsertItem
|
||||
} else {
|
||||
child.prefix = key[:trie.maxPrefixPerNode]
|
||||
key = key[trie.maxPrefixPerNode:]
|
||||
node.children = node.children.add(child)
|
||||
node = child
|
||||
}
|
||||
}
|
||||
|
||||
InsertItem:
|
||||
// Try to insert the item if possible.
|
||||
if replace || node.item == nil {
|
||||
node.item = item
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (trie *Trie) compact() *Trie {
|
||||
// Only a node with a single child can be compacted.
|
||||
if trie.children.length() != 1 {
|
||||
return trie
|
||||
}
|
||||
|
||||
child := trie.children.head()
|
||||
|
||||
// If any item is set, we cannot compact since we want to retain
|
||||
// the ability to do searching by key. This makes compaction less usable,
|
||||
// but that simply cannot be avoided.
|
||||
if trie.item != nil || child.item != nil {
|
||||
return trie
|
||||
}
|
||||
|
||||
// Make sure the combined prefixes fit into a single node.
|
||||
if len(trie.prefix)+len(child.prefix) > trie.maxPrefixPerNode {
|
||||
return trie
|
||||
}
|
||||
|
||||
// Concatenate the prefixes, move the items.
|
||||
child.prefix = append(trie.prefix, child.prefix...)
|
||||
if trie.item != nil {
|
||||
child.item = trie.item
|
||||
}
|
||||
|
||||
return child
|
||||
}
|
||||
|
||||
func (trie *Trie) findSubtree(prefix Prefix) (parent *Trie, root *Trie, found bool, leftover Prefix) {
|
||||
// Find the subtree matching prefix.
|
||||
root = trie
|
||||
for {
|
||||
// Compute what part of prefix matches.
|
||||
common := root.longestCommonPrefixLength(prefix)
|
||||
prefix = prefix[common:]
|
||||
|
||||
// We used up the whole prefix, subtree found.
|
||||
if len(prefix) == 0 {
|
||||
found = true
|
||||
leftover = root.prefix[common:]
|
||||
return
|
||||
}
|
||||
|
||||
// Partial match means that there is no subtree matching prefix.
|
||||
if common < len(root.prefix) {
|
||||
leftover = root.prefix[common:]
|
||||
return
|
||||
}
|
||||
|
||||
// There is some prefix left, move to the children.
|
||||
child := root.children.next(prefix[0])
|
||||
if child == nil {
|
||||
// There is nowhere to continue, there is no subtree matching prefix.
|
||||
return
|
||||
}
|
||||
|
||||
parent = root
|
||||
root = child
|
||||
}
|
||||
}
|
||||
|
||||
func (trie *Trie) findSubtreePath(prefix Prefix) (path []*Trie, found bool, leftover Prefix) {
|
||||
// Find the subtree matching prefix.
|
||||
root := trie
|
||||
var subtreePath []*Trie
|
||||
for {
|
||||
// Append the current root to the path.
|
||||
subtreePath = append(subtreePath, root)
|
||||
|
||||
// Compute what part of prefix matches.
|
||||
common := root.longestCommonPrefixLength(prefix)
|
||||
prefix = prefix[common:]
|
||||
|
||||
// We used up the whole prefix, subtree found.
|
||||
if len(prefix) == 0 {
|
||||
path = subtreePath
|
||||
found = true
|
||||
leftover = root.prefix[common:]
|
||||
return
|
||||
}
|
||||
|
||||
// Partial match means that there is no subtree matching prefix.
|
||||
if common < len(root.prefix) {
|
||||
leftover = root.prefix[common:]
|
||||
return
|
||||
}
|
||||
|
||||
// There is some prefix left, move to the children.
|
||||
child := root.children.next(prefix[0])
|
||||
if child == nil {
|
||||
// There is nowhere to continue, there is no subtree matching prefix.
|
||||
return
|
||||
}
|
||||
|
||||
root = child
|
||||
}
|
||||
}
|
||||
|
||||
func (trie *Trie) walk(actualRootPrefix Prefix, visitor VisitorFunc) error {
|
||||
var prefix Prefix
|
||||
// Allocate a bit more space for prefix at the beginning.
|
||||
if actualRootPrefix == nil {
|
||||
prefix = make(Prefix, 32+len(trie.prefix))
|
||||
copy(prefix, trie.prefix)
|
||||
prefix = prefix[:len(trie.prefix)]
|
||||
} else {
|
||||
prefix = make(Prefix, 32+len(actualRootPrefix))
|
||||
copy(prefix, actualRootPrefix)
|
||||
prefix = prefix[:len(actualRootPrefix)]
|
||||
}
|
||||
|
||||
// Visit the root first. Not that this works for empty trie as well since
|
||||
// in that case item == nil && len(children) == 0.
|
||||
if trie.item != nil {
|
||||
if err := visitor(prefix, trie.item); err != nil {
|
||||
if err == SkipSubtree {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Then continue to the children.
|
||||
return trie.children.walk(&prefix, visitor)
|
||||
}
|
||||
|
||||
func (trie *Trie) longestCommonPrefixLength(prefix Prefix) (i int) {
|
||||
for ; i < len(prefix) && i < len(trie.prefix) && prefix[i] == trie.prefix[i]; i++ {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (trie *Trie) dump() string {
|
||||
writer := &bytes.Buffer{}
|
||||
trie.print(writer, 0)
|
||||
return writer.String()
|
||||
}
|
||||
|
||||
func (trie *Trie) print(writer io.Writer, indent int) {
|
||||
fmt.Fprintf(writer, "%s%s %v\n", strings.Repeat(" ", indent), string(trie.prefix), trie.item)
|
||||
trie.children.print(writer, indent+2)
|
||||
}
|
||||
|
||||
// Errors ----------------------------------------------------------------------
|
||||
|
||||
var (
|
||||
SkipSubtree = errors.New("Skip this subtree")
|
||||
ErrNilPrefix = errors.New("Nil prefix passed into a method call")
|
||||
)
|
Reference in New Issue
Block a user