From a7bc21a37b777217e30b332761593c7dfbab0216 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 15 Aug 2019 17:42:13 -0700 Subject: [PATCH 1/4] serialfile: fix handling of hidden paths on windows fixes #11 --- is_hidden.go | 19 +++++++++---------- is_hidden_windows.go | 27 ++++++++++----------------- serialfile.go | 2 +- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/is_hidden.go b/is_hidden.go index 4ebca60..27960ac 100644 --- a/is_hidden.go +++ b/is_hidden.go @@ -1,18 +1,17 @@ -// +build !windows +//+build !windows package files import ( - "path/filepath" - "strings" + "os" ) -func IsHidden(name string, f Node) bool { - fName := filepath.Base(name) - - if strings.HasPrefix(fName, ".") && len(fName) > 1 { - return true +func isHidden(fi os.FileInfo) bool { + fName := fi.Name() + switch fName { + case "", ".", "..": + return false + default: + return fName[0] == '.' } - - return false } diff --git a/is_hidden_windows.go b/is_hidden_windows.go index 7419f93..6f8bd78 100644 --- a/is_hidden_windows.go +++ b/is_hidden_windows.go @@ -3,33 +3,26 @@ package files import ( - "path/filepath" - "strings" + "os" windows "golang.org/x/sys/windows" ) -func IsHidden(name string, f Node) bool { - - fName := filepath.Base(name) +func isHidden(fi os.FileInfo) bool { + fName := fi.Name() + switch fName { + case "", ".", "..": + return false + } - if strings.HasPrefix(fName, ".") && len(fName) > 1 { + if fName[0] == '.' { return true } - fi, ok := f.(FileInfo) + wi, ok := fi.Sys().(*windows.Win32FileAttributeData) if !ok { return false } - p, e := windows.UTF16PtrFromString(fi.AbsPath()) - if e != nil { - return false - } - - attrs, e := windows.GetFileAttributes(p) - if e != nil { - return false - } - return attrs&windows.FILE_ATTRIBUTE_HIDDEN != 0 + return wi.FileAttributes&windows.FILE_ATTRIBUTE_HIDDEN != 0 } diff --git a/serialfile.go b/serialfile.go index e29752d..75a73b5 100644 --- a/serialfile.go +++ b/serialfile.go @@ -75,7 +75,7 @@ func (it *serialIterator) Next() bool { stat := it.files[0] it.files = it.files[1:] - for !it.handleHiddenFiles && strings.HasPrefix(stat.Name(), ".") { + for !it.handleHiddenFiles && isHidden(stat) { if len(it.files) == 0 { return false } From f3569282d35970223610ad3895b81e552df0d94c Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 15 Aug 2019 19:19:21 -0700 Subject: [PATCH 2/4] test: fix an edge case --- filewriter_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/filewriter_test.go b/filewriter_test.go index d80ac91..5809aba 100644 --- a/filewriter_test.go +++ b/filewriter_test.go @@ -40,6 +40,9 @@ func TestWriteTo(t *testing.T) { "5/a": "foobar", } err = filepath.Walk(path, func(cpath string, info os.FileInfo, err error) error { + if err != nil { + return err + } rpath, err := filepath.Rel(path, cpath) if err != nil { return err From e1ba4f66e7ebec9f8744703808e4adc43afe0faf Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 15 Aug 2019 19:19:33 -0700 Subject: [PATCH 3/4] walk: add a walk function --- walk.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 walk.go diff --git a/walk.go b/walk.go new file mode 100644 index 0000000..f23e7e4 --- /dev/null +++ b/walk.go @@ -0,0 +1,27 @@ +package files + +import ( + "path/filepath" +) + +// Walk walks a file tree, like `os.Walk`. +func Walk(nd Node, cb func(fpath string, nd Node) error) error { + var helper func(string, Node) error + helper = func(path string, nd Node) error { + if err := cb(path, nd); err != nil { + return err + } + dir, ok := nd.(Directory) + if !ok { + return nil + } + iter := dir.Entries() + for iter.Next() { + if err := helper(filepath.Join(path, iter.Name()), iter.Node()); err != nil { + return err + } + } + return iter.Err() + } + return helper("", nd) +} From f0180f2755b56d79131e9062fcec7caccb886e5a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 15 Aug 2019 19:19:47 -0700 Subject: [PATCH 4/4] test: test walk, serialfile, and hidden --- serialfile_test.go | 126 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 serialfile_test.go diff --git a/serialfile_test.go b/serialfile_test.go new file mode 100644 index 0000000..748ba16 --- /dev/null +++ b/serialfile_test.go @@ -0,0 +1,126 @@ +package files + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" +) + +func isPathHidden(p string) bool { + return strings.HasPrefix(p, ".") || strings.Contains(p, "/.") +} + +func TestSerialFile(t *testing.T) { + t.Run("Hidden", func(t *testing.T) { testSerialFile(t, true) }) + t.Run("NotHidden", func(t *testing.T) { testSerialFile(t, false) }) +} + +func testSerialFile(t *testing.T, hidden bool) { + tmppath, err := ioutil.TempDir("", "files-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmppath) + + expected := map[string]string{ + "1": "Some text!\n", + "2": "beep", + "3": "", + "4": "boop", + "5": "", + "5/a": "foobar", + ".6": "thing", + "7": "", + "7/.foo": "bla", + ".8": "", + ".8/foo": "bla", + } + + for p, c := range expected { + path := filepath.Join(tmppath, p) + if c != "" { + continue + } + if err := os.MkdirAll(path, 0777); err != nil { + t.Fatal(err) + } + } + + for p, c := range expected { + path := filepath.Join(tmppath, p) + if c == "" { + continue + } + if err := ioutil.WriteFile(path, []byte(c), 0666); err != nil { + t.Fatal(err) + } + } + + stat, err := os.Stat(tmppath) + if err != nil { + t.Fatal(err) + } + + sf, err := NewSerialFile(tmppath, hidden, stat) + if err != nil { + t.Fatal(err) + } + defer sf.Close() + + rootFound := false + err = Walk(sf, func(path string, nd Node) error { + defer nd.Close() + + // root node. + if path == "" { + if rootFound { + return fmt.Errorf("found root twice") + } + if sf != nd { + return fmt.Errorf("wrong root") + } + rootFound = true + return nil + } + + if !hidden && isPathHidden(path) { + return fmt.Errorf("found a hidden file") + } + + data, ok := expected[path] + if !ok { + return fmt.Errorf("expected something at %q", path) + } + delete(expected, path) + + switch nd := nd.(type) { + case *Symlink: + return fmt.Errorf("didn't expect a symlink") + case Directory: + if data != "" { + return fmt.Errorf("expected a directory at %q", path) + } + case File: + actual, err := ioutil.ReadAll(nd) + if err != nil { + return err + } + if string(actual) != data { + return fmt.Errorf("expected %q, got %q", data, string(actual)) + } + } + return nil + }) + if !rootFound { + t.Fatal("didn't find the root") + } + for p := range expected { + if !hidden && isPathHidden(p) { + continue + } + t.Errorf("missed %q", p) + } +}