*: have gomtree always evaluate tar_time if it is present

if the keyword "tar_time" is present when evaluating
an Entry, gomtree should use the tar_time when evaluating
the "time" keyword as well. This commit also adds a test that
makes sure "tar_time" wins against "time" if both are present.
Some minor clean-ups as well, such as checking if KeywordFunc[keyword]
actually retrieves a function.

Signed-off-by: Stephen Chung <schung@redhat.com>
This commit is contained in:
Stephen Chung 2016-07-22 18:01:54 -04:00
parent bc6f3bf902
commit 656e577ecc
7 changed files with 106 additions and 25 deletions

View file

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"sort" "sort"
"strings"
) )
// Result of a Check // Result of a Check
@ -66,7 +67,15 @@ func Check(root string, dh *DirectoryHierarchy, keywords []string) (*Result, err
} }
for _, kv := range kvs { for _, kv := range kvs {
keywordFunc, ok := KeywordFuncs[kv.Keyword()] kw := kv.Keyword()
// 'tar_time' keyword evaluation wins against 'time' keyword evaluation
if kv.Keyword() == "time" && inSlice("tar_time", keywords) {
kw = "tar_time"
tartime := fmt.Sprintf("%s.%s", (strings.Split(kv.Value(), ".")[0]), "000000000")
kv = KeyVal(KeyVal(kw).ChangeValue(tartime))
}
keywordFunc, ok := KeywordFuncs[kw]
if !ok { if !ok {
return nil, fmt.Errorf("Unknown keyword %q for file %q", kv.Keyword(), e.Path()) return nil, fmt.Errorf("Unknown keyword %q for file %q", kv.Keyword(), e.Path())
} }

View file

@ -143,6 +143,58 @@ func TestTimeComparison(t *testing.T) {
} }
} }
func TestTarTime(t *testing.T) {
dir, err := ioutil.TempDir("", "test-tar-time.")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
// This is the format of time from FreeBSD
spec := `
/set type=file time=5.454353132
. type=dir time=5.123456789
file time=5.911134111
..
`
fh, err := os.Create(filepath.Join(dir, "file"))
if err != nil {
t.Fatal(err)
}
// This is what mode we're checking for. Round integer of epoch seconds
epoch := time.Unix(5, 0)
if err := os.Chtimes(fh.Name(), epoch, epoch); err != nil {
t.Fatal(err)
}
if err := os.Chtimes(dir, epoch, epoch); err != nil {
t.Fatal(err)
}
if err := fh.Close(); err != nil {
t.Error(err)
}
dh, err := ParseSpec(bytes.NewBufferString(spec))
if err != nil {
t.Fatal(err)
}
// make sure "time" keyword works
_, err = Check(dir, dh, DefaultKeywords)
if err != nil {
t.Error(err)
}
// make sure tar_time wins
res, err := Check(dir, dh, append(DefaultKeywords, "tar_time"))
if err != nil {
t.Error(err)
}
if len(res.Failures) > 0 {
t.Fatal(res.Failures)
}
}
func TestIgnoreComments(t *testing.T) { func TestIgnoreComments(t *testing.T) {
dir, err := ioutil.TempDir("", "test-comments.") dir, err := ioutil.TempDir("", "test-comments.")
if err != nil { if err != nil {

View file

@ -94,7 +94,11 @@ func main() {
currentKeywords = append([]string{"type"}, currentKeywords...) currentKeywords = append([]string{"type"}, currentKeywords...)
} }
} else { } else {
currentKeywords = mtree.DefaultKeywords[:] if *flTar != "" {
currentKeywords = mtree.DefaultTarKeywords[:]
} else {
currentKeywords = mtree.DefaultKeywords[:]
}
} }
// -K <keywords> // -K <keywords>
if *flAddKeywords != "" { if *flAddKeywords != "" {
@ -192,13 +196,9 @@ func main() {
log.Println(err) log.Println(err)
isErr = true isErr = true
return return
if res != nil {
if len(res.Failures) > 0 {
defer os.Exit(1)
for _, failure := range res.Failures {
fmt.Println(failure)
}
} }
}
if res != nil {
if len(res.Extra) > 0 { if len(res.Extra) > 0 {
defer os.Exit(1) defer os.Exit(1)
for _, extra := range res.Extra { for _, extra := range res.Extra {

View file

@ -59,6 +59,11 @@ func (kv KeyVal) Value() string {
return strings.SplitN(strings.TrimSpace(string(kv)), "=", 2)[1] return strings.SplitN(strings.TrimSpace(string(kv)), "=", 2)[1]
} }
// ChangeValue changes the value of a KeyVal
func (kv KeyVal) ChangeValue(newval string) string {
return fmt.Sprintf("%s=%s", kv.Keyword(), newval)
}
// keywordSelector takes an array of "keyword=value" and filters out that only the set of words // keywordSelector takes an array of "keyword=value" and filters out that only the set of words
func keywordSelector(keyval, words []string) []string { func keywordSelector(keyval, words []string) []string {
retList := []string{} retList := []string{}
@ -129,6 +134,17 @@ var (
"nlink", "nlink",
"time", "time",
} }
// DefaultTarKeywords has keywords that should be used when creating a manifest from
// an archive. Currently, evaluating the # of hardlinks has not been implemented yet
DefaultTarKeywords = []string{
"size",
"type",
"uid",
"gid",
"mode",
"link",
"tar_time",
}
// SetKeywords is the default set of keywords calculated for a `/set` SpecialType // SetKeywords is the default set of keywords calculated for a `/set` SpecialType
SetKeywords = []string{ SetKeywords = []string{
"uid", "uid",
@ -213,11 +229,7 @@ var (
} }
} }
tartimeKeywordFunc = func(path string, info os.FileInfo, r io.Reader) (string, error) { tartimeKeywordFunc = func(path string, info os.FileInfo, r io.Reader) (string, error) {
t := info.ModTime().Unix() return fmt.Sprintf("tar_time=%d.000000000", info.ModTime().Unix()), nil
if t == 0 {
return "tar_time=0.000000000", nil
}
return fmt.Sprintf("tar_time=%d.000000000", t), nil
} }
timeKeywordFunc = func(path string, info os.FileInfo, r io.Reader) (string, error) { timeKeywordFunc = func(path string, info os.FileInfo, r io.Reader) (string, error) {
t := info.ModTime().UnixNano() t := info.ModTime().UnixNano()

7
tar.go
View file

@ -93,6 +93,8 @@ func (ts *tarStream) readHeaders() {
ts.pipeReader.CloseWithError(err) ts.pipeReader.CloseWithError(err)
return return
} }
defer tmpFile.Close()
defer os.Remove(tmpFile.Name())
// Alright, it's either file or directory // Alright, it's either file or directory
e := Entry{ e := Entry{
@ -119,7 +121,6 @@ func (ts *tarStream) readHeaders() {
if err != nil { if err != nil {
ts.setErr(err) ts.setErr(err)
} }
// for good measure, check that we actually get a value for a keyword // for good measure, check that we actually get a value for a keyword
if val != "" { if val != "" {
e.Keywords = append(e.Keywords, val) e.Keywords = append(e.Keywords, val)
@ -134,7 +135,6 @@ func (ts *tarStream) readHeaders() {
} }
} }
} }
// collect meta-set keywords for a directory so that we can build the // collect meta-set keywords for a directory so that we can build the
// actual sets in `flatten` // actual sets in `flatten`
if hdr.FileInfo().IsDir() { if hdr.FileInfo().IsDir() {
@ -168,9 +168,6 @@ func (ts *tarStream) readHeaders() {
} }
} }
populateTree(&root, &e, hdr, ts) populateTree(&root, &e, hdr, ts)
tmpFile.Close()
os.Remove(tmpFile.Name())
} }
} }

View file

@ -3,7 +3,6 @@ package mtree
import ( import (
"archive/tar" "archive/tar"
"bytes" "bytes"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
@ -115,17 +114,17 @@ func TestTar(t *testing.T) {
switch { switch {
case len(res.Failures) > 0: case len(res.Failures) > 0:
for _, f := range res.Failures { for _, f := range res.Failures {
fmt.Printf("%s\n", f) t.Errorf("%s\n", f)
} }
errors += "Keyword validation errors\n" errors += "Keyword validation errors\n"
case len(res.Missing) > 0: case len(res.Missing) > 0:
for _, m := range res.Missing { for _, m := range res.Missing {
fmt.Printf("Missing file: %s\n", m.Path()) t.Errorf("Missing file: %s\n", m.Path())
} }
errors += "Missing files not expected for this test\n" errors += "Missing files not expected for this test\n"
case len(res.Extra) > 0: case len(res.Extra) > 0:
for _, e := range res.Extra { for _, e := range res.Extra {
fmt.Printf("Extra file: %s\n", e.Path()) t.Errorf("Extra file: %s\n", e.Path())
} }
errors += "Extra files not expected for this test\n" errors += "Extra files not expected for this test\n"
} }

18
walk.go
View file

@ -80,7 +80,11 @@ func Walk(root string, exlcudes []ExcludeFunc, keywords []string) (*DirectoryHie
defer fh.Close() defer fh.Close()
r = fh r = fh
} }
if str, err := KeywordFuncs[keyword](path, info, r); err == nil && str != "" { keywordFunc, ok := KeywordFuncs[keyword]
if !ok {
return fmt.Errorf("Unknown keyword %q for file %q", keyword, path)
}
if str, err := keywordFunc(path, info, r); err == nil && str != "" {
e.Keywords = append(e.Keywords, str) e.Keywords = append(e.Keywords, str)
} else if err != nil { } else if err != nil {
return err return err
@ -107,7 +111,11 @@ func Walk(root string, exlcudes []ExcludeFunc, keywords []string) (*DirectoryHie
defer fh.Close() defer fh.Close()
r = fh r = fh
} }
str, err := KeywordFuncs[keyword](path, info, r) keywordFunc, ok := KeywordFuncs[keyword]
if !ok {
return fmt.Errorf("Unknown keyword %q for file %q", keyword, path)
}
str, err := keywordFunc(path, info, r)
if err != nil { if err != nil {
return err return err
} }
@ -158,7 +166,11 @@ func Walk(root string, exlcudes []ExcludeFunc, keywords []string) (*DirectoryHie
defer fh.Close() defer fh.Close()
r = fh r = fh
} }
str, err := KeywordFuncs[keyword](path, info, r) keywordFunc, ok := KeywordFuncs[keyword]
if !ok {
return fmt.Errorf("Unknown keyword %q for file %q", keyword, path)
}
str, err := keywordFunc(path, info, r)
if err != nil { if err != nil {
return err return err
} }