The `PathPolicies` implements a generic concept that can be fit on more use cases than just mountpoints. Another one would be a policy for creating custom files and directories in the image. Having the implementation in the `disk` package and using data structures from the `disk` and `blueprint` packages makes it impossible to use it for any additional BP customization due to a circular dependencies that always occurs. Split out the implementation into a separate package `pathpolicy` as the first step. Signed-off-by: Tomáš Hozza <thozza@redhat.com>
123 lines
2.3 KiB
Go
123 lines
2.3 KiB
Go
package pathpolicy
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// splits the path into its individual components. Retruns the
|
|
// empty list if the path is just the absolute root, i.e. "/".
|
|
func pathTrieSplitPath(path string) []string {
|
|
path = strings.Trim(path, "/")
|
|
if path == "" {
|
|
return []string{}
|
|
}
|
|
|
|
return strings.Split(path, "/")
|
|
}
|
|
|
|
type PathTrie struct {
|
|
Name []string
|
|
Paths []*PathTrie
|
|
Payload interface{}
|
|
}
|
|
|
|
// match checks if the given trie is a prefix of path
|
|
func (trie *PathTrie) match(path []string) bool {
|
|
if len(trie.Name) > len(path) {
|
|
return false
|
|
}
|
|
|
|
for i := range trie.Name {
|
|
if path[i] != trie.Name[i] {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (trie *PathTrie) get(path []string) (*PathTrie, []string) {
|
|
if len(path) < 1 {
|
|
panic("programming error: expected root node")
|
|
}
|
|
|
|
var node *PathTrie
|
|
for i := range trie.Paths {
|
|
if trie.Paths[i].match(path) {
|
|
node = trie.Paths[i]
|
|
break
|
|
}
|
|
}
|
|
|
|
// no subpath match, we are the best match
|
|
if node == nil {
|
|
return trie, path
|
|
}
|
|
|
|
// node, or one of its sub-nodes, is a match
|
|
prefix := len(node.Name)
|
|
|
|
// the node is a perfect match, return it
|
|
if len(path) == prefix {
|
|
return node, nil
|
|
}
|
|
|
|
// check if any sub-path's of node match
|
|
return node.get(path[prefix:])
|
|
}
|
|
|
|
func (trie *PathTrie) add(path []string) *PathTrie {
|
|
node := &PathTrie{Name: path}
|
|
|
|
if trie.Paths == nil {
|
|
trie.Paths = make([]*PathTrie, 0, 1)
|
|
}
|
|
|
|
trie.Paths = append(trie.Paths, node)
|
|
|
|
return node
|
|
}
|
|
|
|
// Construct a new trie from a map of paths to their payloads.
|
|
// Returns the root node of the trie.
|
|
func NewPathTrieFromMap(entries map[string]interface{}) *PathTrie {
|
|
root := &PathTrie{Name: []string{}}
|
|
|
|
keys := make([]string, 0, len(entries))
|
|
for k := range entries {
|
|
keys = append(keys, k)
|
|
}
|
|
|
|
sort.Strings(keys)
|
|
|
|
for _, k := range keys {
|
|
node, left := root.Lookup(k)
|
|
|
|
if len(left) > 0 {
|
|
node = node.add(left)
|
|
}
|
|
|
|
node.Payload = entries[k]
|
|
}
|
|
|
|
return root
|
|
}
|
|
|
|
// Lookup returns the node that is the prefix of path and
|
|
// the unmatched path segment. Must be called on the root
|
|
// trie node.
|
|
func (root *PathTrie) Lookup(path string) (*PathTrie, []string) {
|
|
|
|
if len(root.Name) != 0 {
|
|
panic("programming error: lookup on non-root trie node")
|
|
}
|
|
|
|
elements := pathTrieSplitPath(path)
|
|
|
|
if len(elements) == 0 {
|
|
return root, elements
|
|
}
|
|
|
|
return root.get(elements)
|
|
}
|