From 3bfdecf46734a6fc90d2d185bd334f42e31e809d Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Thu, 10 Nov 2016 12:41:20 +1100 Subject: [PATCH] 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 --- cmd/gomtree/main.go | 87 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/cmd/gomtree/main.go b/cmd/gomtree/main.go index 8e7e941..dfac662 100644 --- a/cmd/gomtree/main.go +++ b/cmd/gomtree/main.go @@ -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) }