From 656e577ecc026ad0949531037a9b454deaddb062 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 22 Jul 2016 18:01:54 -0400 Subject: [PATCH] *: 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 --- check.go | 11 +++++++++- check_test.go | 52 +++++++++++++++++++++++++++++++++++++++++++++ cmd/gomtree/main.go | 14 ++++++------ keywords.go | 22 ++++++++++++++----- tar.go | 7 ++---- tar_test.go | 7 +++--- walk.go | 18 +++++++++++++--- 7 files changed, 106 insertions(+), 25 deletions(-) diff --git a/check.go b/check.go index 3f2a99a..a565251 100644 --- a/check.go +++ b/check.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "sort" + "strings" ) // Result of a Check @@ -66,7 +67,15 @@ func Check(root string, dh *DirectoryHierarchy, keywords []string) (*Result, err } 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 { return nil, fmt.Errorf("Unknown keyword %q for file %q", kv.Keyword(), e.Path()) } diff --git a/check_test.go b/check_test.go index d1ad6de..fa0368a 100644 --- a/check_test.go +++ b/check_test.go @@ -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) { dir, err := ioutil.TempDir("", "test-comments.") if err != nil { diff --git a/cmd/gomtree/main.go b/cmd/gomtree/main.go index 913428b..c7f90d8 100644 --- a/cmd/gomtree/main.go +++ b/cmd/gomtree/main.go @@ -94,7 +94,11 @@ func main() { currentKeywords = append([]string{"type"}, currentKeywords...) } } else { - currentKeywords = mtree.DefaultKeywords[:] + if *flTar != "" { + currentKeywords = mtree.DefaultTarKeywords[:] + } else { + currentKeywords = mtree.DefaultKeywords[:] + } } // -K if *flAddKeywords != "" { @@ -192,13 +196,9 @@ func main() { log.Println(err) isErr = true 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 { defer os.Exit(1) for _, extra := range res.Extra { diff --git a/keywords.go b/keywords.go index 7bf199c..4119fc0 100644 --- a/keywords.go +++ b/keywords.go @@ -59,6 +59,11 @@ func (kv KeyVal) Value() string { 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 func keywordSelector(keyval, words []string) []string { retList := []string{} @@ -129,6 +134,17 @@ var ( "nlink", "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 = []string{ "uid", @@ -213,11 +229,7 @@ var ( } } tartimeKeywordFunc = func(path string, info os.FileInfo, r io.Reader) (string, error) { - t := info.ModTime().Unix() - if t == 0 { - return "tar_time=0.000000000", nil - } - return fmt.Sprintf("tar_time=%d.000000000", t), nil + return fmt.Sprintf("tar_time=%d.000000000", info.ModTime().Unix()), nil } timeKeywordFunc = func(path string, info os.FileInfo, r io.Reader) (string, error) { t := info.ModTime().UnixNano() diff --git a/tar.go b/tar.go index 36b0f46..b221e24 100644 --- a/tar.go +++ b/tar.go @@ -93,6 +93,8 @@ func (ts *tarStream) readHeaders() { ts.pipeReader.CloseWithError(err) return } + defer tmpFile.Close() + defer os.Remove(tmpFile.Name()) // Alright, it's either file or directory e := Entry{ @@ -119,7 +121,6 @@ func (ts *tarStream) readHeaders() { if err != nil { ts.setErr(err) } - // for good measure, check that we actually get a value for a keyword if 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 // actual sets in `flatten` if hdr.FileInfo().IsDir() { @@ -168,9 +168,6 @@ func (ts *tarStream) readHeaders() { } } populateTree(&root, &e, hdr, ts) - - tmpFile.Close() - os.Remove(tmpFile.Name()) } } diff --git a/tar_test.go b/tar_test.go index 4e940b2..38b8dd5 100644 --- a/tar_test.go +++ b/tar_test.go @@ -3,7 +3,6 @@ package mtree import ( "archive/tar" "bytes" - "fmt" "io" "io/ioutil" "os" @@ -115,17 +114,17 @@ func TestTar(t *testing.T) { switch { case len(res.Failures) > 0: for _, f := range res.Failures { - fmt.Printf("%s\n", f) + t.Errorf("%s\n", f) } errors += "Keyword validation errors\n" case len(res.Missing) > 0: 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" case len(res.Extra) > 0: 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" } diff --git a/walk.go b/walk.go index b4a576e..ca1a706 100644 --- a/walk.go +++ b/walk.go @@ -80,7 +80,11 @@ func Walk(root string, exlcudes []ExcludeFunc, keywords []string) (*DirectoryHie defer fh.Close() 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) } else if err != nil { return err @@ -107,7 +111,11 @@ func Walk(root string, exlcudes []ExcludeFunc, keywords []string) (*DirectoryHie defer fh.Close() 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 { return err } @@ -158,7 +166,11 @@ func Walk(root string, exlcudes []ExcludeFunc, keywords []string) (*DirectoryHie defer fh.Close() 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 { return err }