mirror of
https://github.com/vbatts/go-mtree.git
synced 2024-11-22 08:25:38 +00:00
*: entry linking and keyword filter
Setting up sibling and parent relationships for entries, so they can be easier to walk. Also, making "keyword=value" easier to parse. This helps filtering. Both of these ready us for checking/validating a hierarchy. Signed-off-by: Vincent Batts <vbatts@hashbangbash.com>
This commit is contained in:
parent
ef9ba54f10
commit
3b6cb6e117
4 changed files with 91 additions and 20 deletions
|
@ -35,6 +35,9 @@ func (bp byPos) Swap(i, j int) { bp[i], bp[j] = bp[j], bp[i] }
|
||||||
|
|
||||||
// Entry is each component of content in the mtree spec file
|
// Entry is each component of content in the mtree spec file
|
||||||
type Entry struct {
|
type Entry struct {
|
||||||
|
Parent, Child *Entry // up, down
|
||||||
|
Prev, Next *Entry // left, right
|
||||||
|
Set *Entry // current `/set` for additional keywords
|
||||||
Pos int // order in the spec
|
Pos int // order in the spec
|
||||||
Raw string // file or directory name
|
Raw string // file or directory name
|
||||||
Name string // file or directory name
|
Name string // file or directory name
|
||||||
|
|
49
keywords.go
49
keywords.go
|
@ -9,6 +9,7 @@ import (
|
||||||
"hash"
|
"hash"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/crypto/ripemd160"
|
"golang.org/x/crypto/ripemd160"
|
||||||
)
|
)
|
||||||
|
@ -18,6 +19,53 @@ import (
|
||||||
// be included for the file entry. Otherwise, empty string.
|
// be included for the file entry. Otherwise, empty string.
|
||||||
type KeywordFunc func(path string, info os.FileInfo) (string, error)
|
type KeywordFunc func(path string, info os.FileInfo) (string, error)
|
||||||
|
|
||||||
|
// KeyVal is a "keyword=value"
|
||||||
|
type KeyVal string
|
||||||
|
|
||||||
|
// Keyword is the mapping to the available keywords
|
||||||
|
func (kv KeyVal) Keyword() string {
|
||||||
|
if !strings.Contains(string(kv), "=") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
chunks := strings.SplitN(strings.TrimSpace(string(kv)), "=", 2)[0]
|
||||||
|
if !strings.Contains(chunks, ".") {
|
||||||
|
return chunks
|
||||||
|
}
|
||||||
|
return strings.SplitN(chunks, ".", 2)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeywordSuffix is really only used for xattr, as the keyword is a prefix to
|
||||||
|
// the xattr "namespace.key"
|
||||||
|
func (kv KeyVal) KeywordSuffix() string {
|
||||||
|
if !strings.Contains(string(kv), "=") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
chunks := strings.SplitN(strings.TrimSpace(string(kv)), "=", 2)[0]
|
||||||
|
if !strings.Contains(chunks, ".") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.SplitN(chunks, ".", 2)[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value is the data/value portion of "keyword=value"
|
||||||
|
func (kv KeyVal) Value() string {
|
||||||
|
if !strings.Contains(string(kv), "=") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.SplitN(strings.TrimSpace(string(kv)), "=", 2)[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// keywordSelector takes an array of "keyword=value" and filters out that only the set of words
|
||||||
|
func keywordSelector(keyval, words []string) []string {
|
||||||
|
retList := []string{}
|
||||||
|
for _, kv := range keyval {
|
||||||
|
if inSlice(KeyVal(kv).Keyword(), words) {
|
||||||
|
retList = append(retList, kv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retList
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultKeywords has the several default keyword producers (uid, gid,
|
// DefaultKeywords has the several default keyword producers (uid, gid,
|
||||||
// mode, nlink, type, size, mtime)
|
// mode, nlink, type, size, mtime)
|
||||||
|
@ -31,6 +79,7 @@ var (
|
||||||
"nlink",
|
"nlink",
|
||||||
"time",
|
"time",
|
||||||
}
|
}
|
||||||
|
// SetKeywords is the default set of keywords calculated for a `/set` SpecialType
|
||||||
SetKeywords = []string{
|
SetKeywords = []string{
|
||||||
"uid",
|
"uid",
|
||||||
"gid",
|
"gid",
|
||||||
|
|
44
walk.go
44
walk.go
|
@ -13,11 +13,13 @@ type ExcludeFunc func(path string, info os.FileInfo) bool
|
||||||
|
|
||||||
type dhCreator struct {
|
type dhCreator struct {
|
||||||
DH *DirectoryHierarchy
|
DH *DirectoryHierarchy
|
||||||
curSet []string
|
curSet *Entry
|
||||||
depth int
|
curDir *Entry
|
||||||
curDirNum int
|
curEnt *Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var defaultSetKeywords = []string{"type=file", "nlink=1", "flags=none", "mode=0664"}
|
||||||
|
|
||||||
//
|
//
|
||||||
// To be able to do a "walk" that produces an outcome with `/set ...` would
|
// To be able to do a "walk" that produces an outcome with `/set ...` would
|
||||||
// need a more linear walk, which this can not ensure.
|
// need a more linear walk, which this can not ensure.
|
||||||
|
@ -36,13 +38,13 @@ func Walk(root string, exlcudes []ExcludeFunc, keywords []string) (*DirectoryHie
|
||||||
|
|
||||||
// handle the /set SpecialType
|
// handle the /set SpecialType
|
||||||
if info.IsDir() {
|
if info.IsDir() {
|
||||||
if len(creator.curSet) == 0 {
|
if creator.curSet == nil {
|
||||||
// set the initial /set keywords
|
// set the initial /set keywords
|
||||||
e := Entry{
|
e := Entry{
|
||||||
Name: "/set",
|
Name: "/set",
|
||||||
Type: SpecialType,
|
Type: SpecialType,
|
||||||
Pos: len(creator.DH.Entries),
|
Pos: len(creator.DH.Entries),
|
||||||
Keywords: []string{"type=file", "nlink=1", "flags=none", "mode=0664"},
|
Keywords: keywordSelector(defaultSetKeywords, keywords),
|
||||||
}
|
}
|
||||||
for _, keyword := range SetKeywords {
|
for _, keyword := range SetKeywords {
|
||||||
if str, err := KeywordFuncs[keyword](path, info); err == nil && str != "" {
|
if str, err := KeywordFuncs[keyword](path, info); err == nil && str != "" {
|
||||||
|
@ -51,9 +53,9 @@ func Walk(root string, exlcudes []ExcludeFunc, keywords []string) (*DirectoryHie
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
creator.curSet = e.Keywords
|
creator.curSet = &e
|
||||||
creator.DH.Entries = append(creator.DH.Entries, e)
|
creator.DH.Entries = append(creator.DH.Entries, e)
|
||||||
} else if len(creator.curSet) > 0 {
|
} else if creator.curSet != nil {
|
||||||
// check the attributes of the /set keywords and re-set if changed
|
// check the attributes of the /set keywords and re-set if changed
|
||||||
klist := []string{}
|
klist := []string{}
|
||||||
for _, keyword := range SetKeywords {
|
for _, keyword := range SetKeywords {
|
||||||
|
@ -66,19 +68,20 @@ func Walk(root string, exlcudes []ExcludeFunc, keywords []string) (*DirectoryHie
|
||||||
|
|
||||||
needNewSet := false
|
needNewSet := false
|
||||||
for _, k := range klist {
|
for _, k := range klist {
|
||||||
if !inSlice(k, creator.curSet) {
|
if !inSlice(k, creator.curSet.Keywords) {
|
||||||
needNewSet = true
|
needNewSet = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if needNewSet {
|
if needNewSet {
|
||||||
creator.curSet = append([]string{"type=file", "nlink=1", "flags=none", "mode=0664"}, klist...)
|
e := Entry{
|
||||||
creator.DH.Entries = append(creator.DH.Entries, Entry{
|
|
||||||
Name: "/set",
|
Name: "/set",
|
||||||
Type: SpecialType,
|
Type: SpecialType,
|
||||||
Pos: len(creator.DH.Entries),
|
Pos: len(creator.DH.Entries),
|
||||||
Keywords: creator.curSet,
|
Keywords: append(defaultSetKeywords, klist...),
|
||||||
})
|
}
|
||||||
|
creator.curSet = &e
|
||||||
|
creator.DH.Entries = append(creator.DH.Entries, e)
|
||||||
} else {
|
} else {
|
||||||
creator.DH.Entries = append(creator.DH.Entries, Entry{
|
creator.DH.Entries = append(creator.DH.Entries, Entry{
|
||||||
Type: BlankType,
|
Type: BlankType,
|
||||||
|
@ -91,16 +94,30 @@ func Walk(root string, exlcudes []ExcludeFunc, keywords []string) (*DirectoryHie
|
||||||
e := Entry{
|
e := Entry{
|
||||||
Name: filepath.Base(path),
|
Name: filepath.Base(path),
|
||||||
Pos: len(creator.DH.Entries),
|
Pos: len(creator.DH.Entries),
|
||||||
|
Set: creator.curSet,
|
||||||
}
|
}
|
||||||
for _, keyword := range keywords {
|
for _, keyword := range keywords {
|
||||||
if str, err := KeywordFuncs[keyword](path, info); err == nil && str != "" {
|
if str, err := KeywordFuncs[keyword](path, info); err == nil && str != "" {
|
||||||
if !inSlice(str, creator.curSet) {
|
if !inSlice(str, creator.curSet.Keywords) {
|
||||||
e.Keywords = append(e.Keywords, str)
|
e.Keywords = append(e.Keywords, str)
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
if creator.curDir != nil {
|
||||||
|
creator.curDir.Next = &e
|
||||||
|
}
|
||||||
|
e.Prev = creator.curDir
|
||||||
|
creator.curDir = &e
|
||||||
|
} else {
|
||||||
|
if creator.curEnt != nil {
|
||||||
|
creator.curEnt.Next = &e
|
||||||
|
}
|
||||||
|
e.Prev = creator.curEnt
|
||||||
|
creator.curEnt = &e
|
||||||
|
}
|
||||||
creator.DH.Entries = append(creator.DH.Entries, e)
|
creator.DH.Entries = append(creator.DH.Entries, e)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@ -198,5 +215,4 @@ func readOrderedDirNames(dirname string) ([]string, error) {
|
||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
sort.Strings(dirnames)
|
sort.Strings(dirnames)
|
||||||
return append(names, dirnames...), nil
|
return append(names, dirnames...), nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package mtree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,6 +12,8 @@ func TestWalk(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Fatalf("%#v", dh)
|
||||||
|
|
||||||
fh, err := ioutil.TempFile("", "walk.")
|
fh, err := ioutil.TempFile("", "walk.")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
Loading…
Reference in a new issue