From 4b3a9bfd0270cb205b33d22b95e9fd0a664ace7c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 11 Jul 2016 11:41:42 -0400 Subject: [PATCH 1/4] walk: create and insert metadata signature comments create Entry's for metadata signature that describes time, machine, date, and user and append these Entry's to creator.dh.Entries before starting the actual walk. Signed-off-by: Stephen Chung --- cmd/gomtree/main.go | 2 +- tar.go | 4 +++ tar_test.go | 2 +- walk.go | 66 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/cmd/gomtree/main.go b/cmd/gomtree/main.go index a9d60fe..3ee1e43 100644 --- a/cmd/gomtree/main.go +++ b/cmd/gomtree/main.go @@ -75,7 +75,7 @@ func main() { } // -p - var rootPath string = "." + var rootPath = "." if *flPath != "" { rootPath = *flPath } diff --git a/tar.go b/tar.go index 97c6a33..0c83ef1 100644 --- a/tar.go +++ b/tar.go @@ -10,11 +10,15 @@ import ( "strings" ) +// Streamer interface that wraps an io.ReadCloser with a function that will +// return it's Hierarchy type Streamer interface { io.ReadCloser Hierarchy() (*DirectoryHierarchy, error) } +// NewTarStreamer streams a tar archive and creates a file hierarchy based off +// of the tar metadata headers func NewTarStreamer(r io.Reader, keywords []string) Streamer { pR, pW := io.Pipe() ts := &tarStream{ diff --git a/tar_test.go b/tar_test.go index 547e070..f1d011d 100644 --- a/tar_test.go +++ b/tar_test.go @@ -9,7 +9,7 @@ import ( "testing" ) -func ExampleTar() { +func ExampleStreamer() { fh, err := os.Open("./testdata/test.tar") if err != nil { // handle error ... diff --git a/walk.go b/walk.go index a3f3930..804050e 100644 --- a/walk.go +++ b/walk.go @@ -1,10 +1,13 @@ package mtree import ( + "fmt" "io" "os" + "os/user" "path/filepath" "sort" + "time" ) // ExcludeFunc is the type of function called on each path walked to determine @@ -19,7 +22,12 @@ var defaultSetKeywords = []string{"type=file", "nlink=1", "flags=none", "mode=06 // walked paths. The recommended default list is DefaultKeywords. func Walk(root string, exlcudes []ExcludeFunc, keywords []string) (*DirectoryHierarchy, error) { creator := dhCreator{DH: &DirectoryHierarchy{}} - // TODO insert signature and metadata comments first (user, machine, tree, date) + // insert signature and metadata comments first (user, machine, tree, date) + metadataEntries := signatureEntries(root) + for _, e := range metadataEntries { + e.Pos = len(creator.DH.Entries) + creator.DH.Entries = append(creator.DH.Entries, e) + } err := startWalk(&creator, root, func(path string, info os.FileInfo, err error) error { if err != nil { return err @@ -277,3 +285,59 @@ func readOrderedDirNames(dirname string) ([]string, error) { sort.Strings(dirnames) return append(names, dirnames...), nil } + +// signatureEntries is a simple helper function that returns a slice of Entry's +// that describe the metadata signature about the host. Items like date, user, +// machine, and tree (which is specified by argument `root`), are considered. +// These Entry's construct comments in the mtree specification, so if there is +// an error trying to obtain a particular metadata, we simply don't construct +// the Entry. +func signatureEntries(root string) []Entry { + var sigEntries []Entry + user, err := user.Current() + if err == nil { + userEntry := Entry{ + Type: CommentType, + Raw: fmt.Sprintf("#%16s%s", "user: ", user.Username), + } + sigEntries = append(sigEntries, userEntry) + } + + hostname, err := os.Hostname() + if err == nil { + hostEntry := Entry{ + Type: CommentType, + Raw: fmt.Sprintf("#%16s%s", "machine: ", hostname), + } + sigEntries = append(sigEntries, hostEntry) + } + + if tree := filepath.Clean(root); tree == "." || tree == ".." { + root, err := os.Getwd() + if err == nil { + // use parent directory of current directory + if tree == ".." { + root = filepath.Dir(root) + } + treeEntry := Entry{ + Type: CommentType, + Raw: fmt.Sprintf("#%16s%s", "tree: ", filepath.Clean(root)), + } + sigEntries = append(sigEntries, treeEntry) + } + } else { + treeEntry := Entry{ + Type: CommentType, + Raw: fmt.Sprintf("#%16s%s", "tree: ", filepath.Clean(root)), + } + sigEntries = append(sigEntries, treeEntry) + } + + dateEntry := Entry{ + Type: CommentType, + Raw: fmt.Sprintf("#%16s%s", "date: ", time.Now().Format("Mon Jan 2 15:04:05 2006")), + } + sigEntries = append(sigEntries, dateEntry) + + return sigEntries +} From bacbea465f264f8b3adf90ecc76f58360b975cb5 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 11 Jul 2016 17:09:14 -0400 Subject: [PATCH 2/4] Return "link" keyword along with its value Originally only returning `Sys.linkname` when evaluating a tar header's link field. We want it to be in the form of keyword=value instead. Signed-off-by: Stephen Chung --- keywords.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keywords.go b/keywords.go index 8e199ce..f629519 100644 --- a/keywords.go +++ b/keywords.go @@ -206,7 +206,7 @@ var ( } linkKeywordFunc = func(path string, info os.FileInfo, r io.Reader) (string, error) { if sys, ok := info.Sys().(*tar.Header); ok { - return sys.Linkname, nil + return fmt.Sprintf("link=%s", sys.Linkname), nil } if info.Mode()&os.ModeSymlink != 0 { From d906a5994a3e847c8482e458ad6bc3e36f571f36 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 12 Jul 2016 09:40:45 -0400 Subject: [PATCH 3/4] Not all files will have a symlink The linkname field in the header might not always have a value. Need to check empty linkname. Signed-off-by: Stephen Chung --- keywords.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/keywords.go b/keywords.go index f629519..dc008ae 100644 --- a/keywords.go +++ b/keywords.go @@ -206,7 +206,10 @@ var ( } linkKeywordFunc = func(path string, info os.FileInfo, r io.Reader) (string, error) { if sys, ok := info.Sys().(*tar.Header); ok { - return fmt.Sprintf("link=%s", sys.Linkname), nil + if sys.Linkname != "" { + return fmt.Sprintf("link=%s", sys.Linkname), nil + } + return "", nil } if info.Mode()&os.ModeSymlink != 0 { From 72cbe00536eb9184ce4caa246a00aa66668cc448 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 8 Jul 2016 17:00:07 -0400 Subject: [PATCH 4/4] Recognize path when validating Fixes #11. After changing the directory to the root directory specified, we want to call lstat on a relative path, instead of joining the full root path with the file name. Signed-off-by: Stephen Chung --- check.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/check.go b/check.go index 65e1128..876c64c 100644 --- a/check.go +++ b/check.go @@ -50,8 +50,7 @@ func Check(root string, dh *DirectoryHierarchy, keywords []string) (*Result, err creator.curSet = nil } case RelativeType, FullType: - filename := filepath.Join(root, e.Path()) - info, err := os.Lstat(filename) + info, err := os.Lstat(e.Path()) if err != nil { return nil, err }