gomtree: add special cases for tar generation checking

Due to several unsolveable problems in tar generation, such as the
size=... keyword, we have to special case quite a few things in the
checking code. We might want to move this to mtree properly (but I'm
hesitant about ignoring errors that only happen for tar DHes).

Signed-off-by: Aleksa Sarai <asarai@suse.de>
This commit is contained in:
Aleksa Sarai 2016-11-10 12:41:20 +11:00
parent d214ab47e8
commit 3bfdecf467
No known key found for this signature in database
GPG key ID: 9E18AA267DDB8DB4

View file

@ -62,6 +62,90 @@ var formats = map[string]func([]mtree.InodeDelta) string{
},
}
// isDirEntry returns wheter an mtree.Entry describes a directory.
func isDirEntry(e mtree.Entry) bool {
for _, kw := range e.Keywords {
kv := mtree.KeyVal(kw)
if kv.Keyword() == "type" {
return kv.Value() == "dir"
}
}
// Shouldn't be reached.
return false
}
// filterMissingKeywords is a fairly annoying hack to get around the fact that
// tar archive manifest generation has certain unsolveable problems regarding
// certain keywords. For example, the size=... keyword cannot be implemented
// for directories in a tar archive (which causes Missing errors for that
// keyword).
//
// This function just removes all instances of Missing errors for keywords.
// This makes certain assumptions about the type of issues tar archives have.
// Only call this on tar archive manifest comparisons.
func filterMissingKeywords(diffs []mtree.InodeDelta) []mtree.InodeDelta {
newDiffs := []mtree.InodeDelta{}
loop:
for _, diff := range diffs {
if diff.Type() == mtree.Modified {
// We only apply this filtering to directories.
// NOTE: This will probably break if someone drops the size keyword.
if isDirEntry(*diff.Old()) || isDirEntry(*diff.New()) {
// If this applies to '.' then we just filter everything
// (meaning we remove this entry). This is because note all tar
// archives include a '.' entry. Which makes checking this not
// practical.
if diff.Path() == "." {
continue
}
// Only filter out the size keyword.
// NOTE: This currently takes advantage of the fact the
// diff.Diff() returns the actual slice to diff.keys.
keys := diff.Diff()
for idx, k := range keys {
// Delete the key if it's "size". Unfortunately in Go you
// can't delete from a slice without reassigning it. So we
// just overwrite it with the last value.
if k.Name() == "size" {
if len(keys) < 2 {
continue loop
}
keys[idx] = keys[len(keys)-1]
}
}
}
}
// If we got here, append to the new set.
newDiffs = append(newDiffs, diff)
}
return newDiffs
}
// isTarSpec returns whether the spec provided came from the tar generator.
// This takes advantage of an unsolveable problem in tar generation.
func isTarSpec(spec *mtree.DirectoryHierarchy) bool {
// Find a directory and check whether it's missing size=...
// NOTE: This will definitely break if someone drops the size=... keyword.
for _, e := range spec.Entries {
if !isDirEntry(e) {
continue
}
for _, kw := range e.Keywords {
kv := mtree.KeyVal(kw)
if kv.Keyword() == "size" {
return false
}
}
return true
}
// Should never be reached.
return false
}
func main() {
flag.Parse()
@ -327,6 +411,9 @@ func main() {
return
}
if res != nil {
if isTarSpec(specDh) || *flTar != "" {
res = filterMissingKeywords(res)
}
if len(res) > 0 {
defer os.Exit(1)
}