Skip to content
This repository has been archived by the owner on Mar 29, 2023. It is now read-only.

serialfile: fix handling of hidden paths on windows #21

Merged
merged 4 commits into from
Aug 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions filewriter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 9 additions & 10 deletions is_hidden.go
Original file line number Diff line number Diff line change
@@ -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
}
27 changes: 10 additions & 17 deletions is_hidden_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
2 changes: 1 addition & 1 deletion serialfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
126 changes: 126 additions & 0 deletions serialfile_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
27 changes: 27 additions & 0 deletions walk.go
Original file line number Diff line number Diff line change
@@ -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)
}