diff options
author | Jakob Unterwurzacher | 2017-08-13 21:13:44 +0200 |
---|---|---|
committer | Jakob Unterwurzacher | 2017-08-15 19:03:57 +0200 |
commit | e50a6a57e57bc3cc925ba9a6e7f4dc1da4da3c84 (patch) | |
tree | 79df16efd4d2474502a0e5116a4ee9599a33daee /internal/syscallcompat/getdents_test.go | |
parent | affb1c2f6617d66bdc9fda41b017e0de000c3691 (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.go | 77 |
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) + } + } + } + } +} |