diff --git a/cmd/oci-storage/container.go b/cmd/oci-storage/container.go index 6e624e2dca..17420d1799 100644 --- a/cmd/oci-storage/container.go +++ b/cmd/oci-storage/container.go @@ -20,25 +20,10 @@ func container(flags *mflag.FlagSet, action string, m storage.Store, args []stri fmt.Fprintf(os.Stderr, "%v\n", err) return 1 } - containers, err := m.Containers() - if err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) - return 1 - } - matches := []storage.Container{} - for _, container := range containers { - nextContainer: - for _, arg := range args { - if container.ID == arg { - matches = append(matches, container) - break nextContainer - } - for _, name := range container.Names { - if name == arg { - matches = append(matches, container) - break nextContainer - } - } + matches := []*storage.Container{} + for _, arg := range args { + if container, err := m.GetContainer(arg); err == nil { + matches = append(matches, container) } } if jsonOutput { diff --git a/cmd/oci-storage/image.go b/cmd/oci-storage/image.go index d364f1a84e..ba17191ed9 100644 --- a/cmd/oci-storage/image.go +++ b/cmd/oci-storage/image.go @@ -15,25 +15,10 @@ var ( ) func image(flags *mflag.FlagSet, action string, m storage.Store, args []string) int { - images, err := m.Images() - if err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) - return 1 - } - matched := []storage.Image{} - for _, image := range images { - nextImage: - for _, arg := range args { - if image.ID == arg { - matched = append(matched, image) - break nextImage - } - for _, name := range image.Names { - if name == arg { - matched = append(matched, image) - break nextImage - } - } + matched := []*storage.Image{} + for _, arg := range args { + if image, err := m.GetImage(arg); err == nil { + matched = append(matched, image) } } if jsonOutput { diff --git a/pkg/truncindex/truncindex.go b/pkg/truncindex/truncindex.go new file mode 100644 index 0000000000..02610b8b7e --- /dev/null +++ b/pkg/truncindex/truncindex.go @@ -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 + }) +} diff --git a/pkg/truncindex/truncindex_test.go b/pkg/truncindex/truncindex_test.go new file mode 100644 index 0000000000..bac5e624ee --- /dev/null +++ b/pkg/truncindex/truncindex_test.go @@ -0,0 +1,429 @@ +package truncindex + +import ( + "math/rand" + "testing" + + "github.com/containers/storage/pkg/stringid" +) + +// Test the behavior of TruncIndex, an index for querying IDs from a non-conflicting prefix. +func TestTruncIndex(t *testing.T) { + ids := []string{} + index := NewTruncIndex(ids) + // Get on an empty index + if _, err := index.Get("foobar"); err == nil { + t.Fatal("Get on an empty index should return an error") + } + + // Spaces should be illegal in an id + if err := index.Add("I have a space"); err == nil { + t.Fatalf("Adding an id with ' ' should return an error") + } + + id := "99b36c2c326ccc11e726eee6ee78a0baf166ef96" + // Add an id + if err := index.Add(id); err != nil { + t.Fatal(err) + } + + // Add an empty id (should fail) + if err := index.Add(""); err == nil { + t.Fatalf("Adding an empty id should return an error") + } + + // Get a non-existing id + assertIndexGet(t, index, "abracadabra", "", true) + // Get an empty id + assertIndexGet(t, index, "", "", true) + // Get the exact id + assertIndexGet(t, index, id, id, false) + // The first letter should match + assertIndexGet(t, index, id[:1], id, false) + // The first half should match + assertIndexGet(t, index, id[:len(id)/2], id, false) + // The second half should NOT match + assertIndexGet(t, index, id[len(id)/2:], "", true) + + id2 := id[:6] + "blabla" + // Add an id + if err := index.Add(id2); err != nil { + t.Fatal(err) + } + // Both exact IDs should work + assertIndexGet(t, index, id, id, false) + assertIndexGet(t, index, id2, id2, false) + + // 6 characters or less should conflict + assertIndexGet(t, index, id[:6], "", true) + assertIndexGet(t, index, id[:4], "", true) + assertIndexGet(t, index, id[:1], "", true) + + // An ambiguous id prefix should return an error + if _, err := index.Get(id[:4]); err == nil { + t.Fatal("An ambiguous id prefix should return an error") + } + + // 7 characters should NOT conflict + assertIndexGet(t, index, id[:7], id, false) + assertIndexGet(t, index, id2[:7], id2, false) + + // Deleting a non-existing id should return an error + if err := index.Delete("non-existing"); err == nil { + t.Fatalf("Deleting a non-existing id should return an error") + } + + // Deleting an empty id should return an error + if err := index.Delete(""); err == nil { + t.Fatal("Deleting an empty id should return an error") + } + + // Deleting id2 should remove conflicts + if err := index.Delete(id2); err != nil { + t.Fatal(err) + } + // id2 should no longer work + assertIndexGet(t, index, id2, "", true) + assertIndexGet(t, index, id2[:7], "", true) + assertIndexGet(t, index, id2[:11], "", true) + + // conflicts between id and id2 should be gone + assertIndexGet(t, index, id[:6], id, false) + assertIndexGet(t, index, id[:4], id, false) + assertIndexGet(t, index, id[:1], id, false) + + // non-conflicting substrings should still not conflict + assertIndexGet(t, index, id[:7], id, false) + assertIndexGet(t, index, id[:15], id, false) + assertIndexGet(t, index, id, id, false) + + assertIndexIterate(t) +} + +func assertIndexIterate(t *testing.T) { + ids := []string{ + "19b36c2c326ccc11e726eee6ee78a0baf166ef96", + "28b36c2c326ccc11e726eee6ee78a0baf166ef96", + "37b36c2c326ccc11e726eee6ee78a0baf166ef96", + "46b36c2c326ccc11e726eee6ee78a0baf166ef96", + } + + index := NewTruncIndex(ids) + + index.Iterate(func(targetId string) { + for _, id := range ids { + if targetId == id { + return + } + } + + t.Fatalf("An unknown ID '%s'", targetId) + }) +} + +func assertIndexGet(t *testing.T, index *TruncIndex, input, expectedResult string, expectError bool) { + if result, err := index.Get(input); err != nil && !expectError { + t.Fatalf("Unexpected error getting '%s': %s", input, err) + } else if err == nil && expectError { + t.Fatalf("Getting '%s' should return an error, not '%s'", input, result) + } else if result != expectedResult { + t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult) + } +} + +func BenchmarkTruncIndexAdd100(b *testing.B) { + var testSet []string + for i := 0; i < 100; i++ { + testSet = append(testSet, stringid.GenerateNonCryptoID()) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + index := NewTruncIndex([]string{}) + for _, id := range testSet { + if err := index.Add(id); err != nil { + b.Fatal(err) + } + } + } +} + +func BenchmarkTruncIndexAdd250(b *testing.B) { + var testSet []string + for i := 0; i < 250; i++ { + testSet = append(testSet, stringid.GenerateNonCryptoID()) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + index := NewTruncIndex([]string{}) + for _, id := range testSet { + if err := index.Add(id); err != nil { + b.Fatal(err) + } + } + } +} + +func BenchmarkTruncIndexAdd500(b *testing.B) { + var testSet []string + for i := 0; i < 500; i++ { + testSet = append(testSet, stringid.GenerateNonCryptoID()) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + index := NewTruncIndex([]string{}) + for _, id := range testSet { + if err := index.Add(id); err != nil { + b.Fatal(err) + } + } + } +} + +func BenchmarkTruncIndexGet100(b *testing.B) { + var testSet []string + var testKeys []string + for i := 0; i < 100; i++ { + testSet = append(testSet, stringid.GenerateNonCryptoID()) + } + index := NewTruncIndex([]string{}) + for _, id := range testSet { + if err := index.Add(id); err != nil { + b.Fatal(err) + } + l := rand.Intn(12) + 12 + testKeys = append(testKeys, id[:l]) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, id := range testKeys { + if res, err := index.Get(id); err != nil { + b.Fatal(res, err) + } + } + } +} + +func BenchmarkTruncIndexGet250(b *testing.B) { + var testSet []string + var testKeys []string + for i := 0; i < 250; i++ { + testSet = append(testSet, stringid.GenerateNonCryptoID()) + } + index := NewTruncIndex([]string{}) + for _, id := range testSet { + if err := index.Add(id); err != nil { + b.Fatal(err) + } + l := rand.Intn(12) + 12 + testKeys = append(testKeys, id[:l]) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, id := range testKeys { + if res, err := index.Get(id); err != nil { + b.Fatal(res, err) + } + } + } +} + +func BenchmarkTruncIndexGet500(b *testing.B) { + var testSet []string + var testKeys []string + for i := 0; i < 500; i++ { + testSet = append(testSet, stringid.GenerateNonCryptoID()) + } + index := NewTruncIndex([]string{}) + for _, id := range testSet { + if err := index.Add(id); err != nil { + b.Fatal(err) + } + l := rand.Intn(12) + 12 + testKeys = append(testKeys, id[:l]) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, id := range testKeys { + if res, err := index.Get(id); err != nil { + b.Fatal(res, err) + } + } + } +} + +func BenchmarkTruncIndexDelete100(b *testing.B) { + var testSet []string + for i := 0; i < 100; i++ { + testSet = append(testSet, stringid.GenerateNonCryptoID()) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + index := NewTruncIndex([]string{}) + for _, id := range testSet { + if err := index.Add(id); err != nil { + b.Fatal(err) + } + } + b.StartTimer() + for _, id := range testSet { + if err := index.Delete(id); err != nil { + b.Fatal(err) + } + } + } +} + +func BenchmarkTruncIndexDelete250(b *testing.B) { + var testSet []string + for i := 0; i < 250; i++ { + testSet = append(testSet, stringid.GenerateNonCryptoID()) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + index := NewTruncIndex([]string{}) + for _, id := range testSet { + if err := index.Add(id); err != nil { + b.Fatal(err) + } + } + b.StartTimer() + for _, id := range testSet { + if err := index.Delete(id); err != nil { + b.Fatal(err) + } + } + } +} + +func BenchmarkTruncIndexDelete500(b *testing.B) { + var testSet []string + for i := 0; i < 500; i++ { + testSet = append(testSet, stringid.GenerateNonCryptoID()) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + index := NewTruncIndex([]string{}) + for _, id := range testSet { + if err := index.Add(id); err != nil { + b.Fatal(err) + } + } + b.StartTimer() + for _, id := range testSet { + if err := index.Delete(id); err != nil { + b.Fatal(err) + } + } + } +} + +func BenchmarkTruncIndexNew100(b *testing.B) { + var testSet []string + for i := 0; i < 100; i++ { + testSet = append(testSet, stringid.GenerateNonCryptoID()) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + NewTruncIndex(testSet) + } +} + +func BenchmarkTruncIndexNew250(b *testing.B) { + var testSet []string + for i := 0; i < 250; i++ { + testSet = append(testSet, stringid.GenerateNonCryptoID()) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + NewTruncIndex(testSet) + } +} + +func BenchmarkTruncIndexNew500(b *testing.B) { + var testSet []string + for i := 0; i < 500; i++ { + testSet = append(testSet, stringid.GenerateNonCryptoID()) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + NewTruncIndex(testSet) + } +} + +func BenchmarkTruncIndexAddGet100(b *testing.B) { + var testSet []string + var testKeys []string + for i := 0; i < 500; i++ { + id := stringid.GenerateNonCryptoID() + testSet = append(testSet, id) + l := rand.Intn(12) + 12 + testKeys = append(testKeys, id[:l]) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + index := NewTruncIndex([]string{}) + for _, id := range testSet { + if err := index.Add(id); err != nil { + b.Fatal(err) + } + } + for _, id := range testKeys { + if res, err := index.Get(id); err != nil { + b.Fatal(res, err) + } + } + } +} + +func BenchmarkTruncIndexAddGet250(b *testing.B) { + var testSet []string + var testKeys []string + for i := 0; i < 500; i++ { + id := stringid.GenerateNonCryptoID() + testSet = append(testSet, id) + l := rand.Intn(12) + 12 + testKeys = append(testKeys, id[:l]) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + index := NewTruncIndex([]string{}) + for _, id := range testSet { + if err := index.Add(id); err != nil { + b.Fatal(err) + } + } + for _, id := range testKeys { + if res, err := index.Get(id); err != nil { + b.Fatal(res, err) + } + } + } +} + +func BenchmarkTruncIndexAddGet500(b *testing.B) { + var testSet []string + var testKeys []string + for i := 0; i < 500; i++ { + id := stringid.GenerateNonCryptoID() + testSet = append(testSet, id) + l := rand.Intn(12) + 12 + testKeys = append(testKeys, id[:l]) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + index := NewTruncIndex([]string{}) + for _, id := range testSet { + if err := index.Add(id); err != nil { + b.Fatal(err) + } + } + for _, id := range testKeys { + if res, err := index.Get(id); err != nil { + b.Fatal(res, err) + } + } + } +} diff --git a/storage/containers.go b/storage/containers.go index 5398f7a7a3..045ab823e5 100644 --- a/storage/containers.go +++ b/storage/containers.go @@ -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 @@ -182,30 +187,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() } @@ -241,6 +251,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 @@ -251,24 +262,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() } @@ -276,22 +277,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) } @@ -308,133 +298,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(id); 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 { diff --git a/storage/images.go b/storage/images.go index 6798c5737e..617d642e13 100644 --- a/storage/images.go +++ b/storage/images.go @@ -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 { @@ -172,26 +177,32 @@ func newImageStore(dir string) (ImageStore, error) { return &istore, nil } +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 + } + return nil, false +} + func (r *imageStore) ClearFlag(id string, flag 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 } - 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() } @@ -225,6 +236,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 @@ -235,20 +247,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() } @@ -256,20 +262,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) } @@ -286,125 +283,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(id); 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 { diff --git a/storage/layers.go b/storage/layers.go index 83a5a8b52a..9ef824b945 100644 --- a/storage/layers.go +++ b/storage/layers.go @@ -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 @@ -306,26 +311,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() } @@ -342,8 +353,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() @@ -376,6 +389,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 @@ -430,48 +444,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 - } - if layer, ok := r.byname[id]; ok { - id = layer.ID - } - if _, ok := r.byid[id]; !ok { - return ErrLayerUnknown + layer, ok := r.lookup(id) + if !ok { + layerByMount, ok := r.bymount[filepath.Clean(id)] + if !ok { + return ErrLayerUnknown + } + layer = layerByMount } - layer := r.byid[id] if layer.MountCount > 1 { layer.MountCount-- return r.Save() @@ -489,20 +494,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) } @@ -519,20 +515,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() } @@ -544,13 +534,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 } @@ -558,66 +547,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) - } - } - 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) + pslice := r.byparent[layer.Parent] + newPslice := []*Layer{} + for _, candidate := range pslice { + if candidate.ID != id { + newPslice = append(newPslice, candidate) } - 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 } @@ -636,24 +614,18 @@ func (r *layerStore) Wipe() error { } 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 - } - if from == "" { - if layer, ok := r.byid[to]; ok { - from = layer.Parent - } - } - if to == "" { + toLayer, ok := r.lookup(to) + if !ok { return nil, ErrLayerUnknown } - if _, ok := r.byid[to]; !ok { - return nil, ErrLayerUnknown + fromLayer, ok := r.lookup(from) + if !ok { + fromLayer, ok = r.lookup(toLayer.Parent) + if !ok { + return nil, ErrParentUnknown + } } - return r.driver.Changes(to, from) + return r.driver.Changes(toLayer.ID, fromLayer.ID) } type simpleGetCloser struct { @@ -688,33 +660,27 @@ 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 + toLayer, ok := r.lookup(to) + if !ok { + return nil, ErrLayerUnknown } - if from == "" { - if layer, ok := r.byid[to]; ok { - from = layer.Parent + fromLayer, ok := r.lookup(from) + if !ok { + fromLayer, ok = r.lookup(toLayer.Parent) + if !ok { + return nil, ErrParentUnknown } } - if to == "" { - return nil, ErrParentUnknown - } - if _, ok := r.byid[to]; !ok { - 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 { - diff, err := r.driver.Diff(to, from) + if fromLayer.ID != toLayer.Parent { + diff, err := r.driver.Diff(toLayer.ID, fromLayer.ID) if err == nil && (compression != archive.Uncompressed) { preader, pwriter := io.Pipe() compressor, err := archive.CompressStream(pwriter, compression) @@ -734,10 +700,10 @@ func (r *layerStore) Diff(from, to string) (io.ReadCloser, error) { return diff, err } - tsfile, err := os.Open(r.tspath(to)) + tsfile, err := os.Open(r.tspath(toLayer.ID)) if err != nil { if os.IsNotExist(err) { - return r.driver.Diff(to, from) + return r.driver.Diff(toLayer.ID, fromLayer.ID) } return nil, err } @@ -791,31 +757,22 @@ 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 + toLayer, ok := r.lookup(to) + if !ok { + return -1, ErrLayerUnknown } - if from == "" { - if layer, ok := r.byid[to]; ok { - from = layer.Parent + fromLayer, ok := r.lookup(from) + if !ok { + fromLayer, ok = r.lookup(toLayer.Parent) + if !ok { + return -1, ErrParentUnknown } } - if to == "" { - return -1, ErrParentUnknown - } - if _, ok := r.byid[to]; !ok { - return -1, ErrLayerUnknown - } - return r.driver.DiffSize(to, from) + return r.driver.DiffSize(toLayer.ID, fromLayer.ID) } 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 } diff --git a/storage/store.go b/storage/store.go index bb6a8426e6..9c42ffbc3a 100644 --- a/storage/store.go +++ b/storage/store.go @@ -2170,6 +2170,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" DefaultStoreOptions.GraphRoot = "/var/lib/containers" diff --git a/vendor.conf b/vendor.conf index 524efeb4e0..507a5cf0bd 100644 --- a/vendor.conf +++ b/vendor.conf @@ -9,6 +9,7 @@ github.com/mattn/go-shellwords 753a2322a99f87c0eff284980e77f53041555bc6 github.com/mistifyio/go-zfs c0224de804d438efd11ea6e52ada8014537d6062 github.com/opencontainers/runc 6c22e77604689db8725fa866f0f2ec0b3e8c3a07 github.com/pborman/uuid 1b00554d822231195d1babd97ff4a781231955c9 +github.com/tchap/go-patricia v2.2.6 github.com/vbatts/tar-split bd4c5d64c3e9297f410025a3b1bd0c58f659e721 github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3 golang.org/x/net f2499483f923065a842d38eb4c7f1927e6fc6e6d diff --git a/vendor/github.com/tchap/go-patricia/LICENSE b/vendor/github.com/tchap/go-patricia/LICENSE new file mode 100644 index 0000000000..e50d398e98 --- /dev/null +++ b/vendor/github.com/tchap/go-patricia/LICENSE @@ -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. diff --git a/vendor/github.com/tchap/go-patricia/patricia/children.go b/vendor/github.com/tchap/go-patricia/patricia/children.go new file mode 100644 index 0000000000..a5677c3352 --- /dev/null +++ b/vendor/github.com/tchap/go-patricia/patricia/children.go @@ -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 +} diff --git a/vendor/github.com/tchap/go-patricia/patricia/patricia.go b/vendor/github.com/tchap/go-patricia/patricia/patricia.go new file mode 100644 index 0000000000..a1fc53d5db --- /dev/null +++ b/vendor/github.com/tchap/go-patricia/patricia/patricia.go @@ -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") +)