aboutsummaryrefslogtreecommitdiff
path: root/internal/syscallcompat/getdents_test.go
diff options
context:
space:
mode:
authorJakob Unterwurzacher2017-08-13 21:13:44 +0200
committerJakob Unterwurzacher2017-08-15 19:03:57 +0200
commite50a6a57e57bc3cc925ba9a6e7f4dc1da4da3c84 (patch)
tree79df16efd4d2474502a0e5116a4ee9599a33daee /internal/syscallcompat/getdents_test.go
parentaffb1c2f6617d66bdc9fda41b017e0de000c3691 (diff)
syscallcompat: implement Getdents()
The Readdir function provided by os is inherently slow because it calls Lstat on all files. Getdents gives us all the information we need, but does not have a proper wrapper in the stdlib. Implement the "Getdents()" wrapper function that calls syscall.Getdents() and parses the returned byte blob to a fuse.DirEntry slice.
Diffstat (limited to 'internal/syscallcompat/getdents_test.go')
-rw-r--r--internal/syscallcompat/getdents_test.go77
1 files changed, 77 insertions, 0 deletions
diff --git a/internal/syscallcompat/getdents_test.go b/internal/syscallcompat/getdents_test.go
new file mode 100644
index 0000000..f0b1e60
--- /dev/null
+++ b/internal/syscallcompat/getdents_test.go
@@ -0,0 +1,77 @@
+// +build linux
+
+package syscallcompat
+
+import (
+ "io/ioutil"
+ "os"
+ "strings"
+ "syscall"
+ "testing"
+
+ "github.com/hanwen/go-fuse/fuse"
+)
+
+func TestGetdents(t *testing.T) {
+ // Fill a directory with filenames of length 1 ... 255
+ testDir, err := ioutil.TempDir("", "TestGetdents")
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i := 1; i <= syscall.NAME_MAX; i++ {
+ n := strings.Repeat("x", i)
+ err = ioutil.WriteFile(testDir+"/"+n, nil, 0600)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ // "/", "/dev" and "/proc" are good test cases because they contain many
+ // different file types (block and char devices, symlinks, mountpoints)
+ dirs := []string{testDir, "/", "/dev", "/proc"}
+ for _, dir := range dirs {
+ // Read directory using stdlib Readdir()
+ fd, err := os.Open(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ readdirEntries, err := fd.Readdir(0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ fd.Close()
+ readdirMap := make(map[string]*syscall.Stat_t)
+ for _, v := range readdirEntries {
+ readdirMap[v.Name()] = fuse.ToStatT(v)
+ }
+ // Read using our Getdents()
+ getdentsEntries, err := Getdents(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ getdentsMap := make(map[string]fuse.DirEntry)
+ for _, v := range getdentsEntries {
+ getdentsMap[v.Name] = v
+ }
+ // Compare results
+ if len(getdentsEntries) != len(readdirEntries) {
+ t.Fatalf("len(getdentsEntries)=%d, len(readdirEntries)=%d",
+ len(getdentsEntries), len(readdirEntries))
+ }
+ for name := range readdirMap {
+ g := getdentsMap[name]
+ r := readdirMap[name]
+ rTyp := r.Mode & syscall.S_IFMT
+ if g.Mode != rTyp {
+ t.Errorf("%q: g.Mode=%#o, r.Mode=%#o", name, g.Mode, rTyp)
+ }
+ if g.Ino != r.Ino {
+ // The inode number of a directory that is reported by stat
+ // and getdents is different when it is a mountpoint. Only
+ // throw an error when we are NOT looking at a directory.
+ if g.Mode != syscall.S_IFDIR {
+ t.Errorf("%s: g.Ino=%d, r.Ino=%d", name, g.Ino, r.Ino)
+ }
+ }
+ }
+ }
+}