aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/syscallcompat/getdents_linux.go23
-rw-r--r--internal/syscallcompat/getdents_other.go50
-rw-r--r--internal/syscallcompat/getdents_test.go17
-rw-r--r--internal/syscallcompat/sys_darwin.go5
-rw-r--r--internal/syscallcompat/sys_linux.go9
5 files changed, 74 insertions, 30 deletions
diff --git a/internal/syscallcompat/getdents_linux.go b/internal/syscallcompat/getdents_linux.go
index e3a2c4a..852b3cd 100644
--- a/internal/syscallcompat/getdents_linux.go
+++ b/internal/syscallcompat/getdents_linux.go
@@ -27,7 +27,7 @@ const sizeofDirent = int(unsafe.Sizeof(unix.Dirent{}))
const maxReclen = 280
// getdents wraps unix.Getdents and converts the result to []fuse.DirEntry.
-func getdents(fd int) ([]fuse.DirEntry, error) {
+func getdents(fd int) (entries []fuse.DirEntry, entriesSpecial []fuse.DirEntry, err error) {
// Collect syscall result in smartBuf.
// "bytes.Buffer" is smart about expanding the capacity and avoids the
// exponential runtime of simple append().
@@ -44,9 +44,9 @@ func getdents(fd int) ([]fuse.DirEntry, error) {
} else if err != nil {
if smartBuf.Len() > 0 {
tlog.Warn.Printf("warning: unix.Getdents returned errno %d in the middle of data ( https://github.com/rfjakob/gocryptfs/issues/483 )", err.(syscall.Errno))
- return nil, syscall.EIO
+ return nil, nil, syscall.EIO
}
- return nil, err
+ return nil, nil, err
}
if n == 0 {
break
@@ -66,12 +66,12 @@ func getdents(fd int) ([]fuse.DirEntry, error) {
tlog.Warn.Printf("Getdents: corrupt entry #%d: Reclen=0 at offset=%d. Returning EBADR",
numEntries, offset)
// EBADR = Invalid request descriptor
- return nil, syscall.EBADR
+ return nil, nil, syscall.EBADR
}
if int(s.Reclen) > maxReclen {
tlog.Warn.Printf("Getdents: corrupt entry #%d: Reclen=%d > %d. Returning EBADR",
numEntries, s.Reclen, maxReclen)
- return nil, syscall.EBADR
+ return nil, nil, syscall.EBADR
}
offset += int(s.Reclen)
numEntries++
@@ -80,17 +80,22 @@ func getdents(fd int) ([]fuse.DirEntry, error) {
// Note: syscall.ParseDirent() only returns the names,
// we want all the data, so we have to implement
// it on our own.
- entries := make([]fuse.DirEntry, 0, numEntries)
+ entries = make([]fuse.DirEntry, 0, numEntries)
offset = 0
for offset < len(buf) {
s := *(*unix.Dirent)(unsafe.Pointer(&buf[offset]))
name, err := getdentsName(s)
if err != nil {
- return nil, err
+ return nil, nil, err
}
offset += int(s.Reclen)
if name == "." || name == ".." {
- // os.File.Readdir() drops "." and "..". Let's be compatible.
+ // These are always directories, no need to call convertDType.
+ entriesSpecial = append(entriesSpecial, fuse.DirEntry{
+ Ino: s.Ino,
+ Mode: syscall.S_IFDIR,
+ Name: name,
+ })
continue
}
mode, err := convertDType(fd, name, s.Type)
@@ -105,7 +110,7 @@ func getdents(fd int) ([]fuse.DirEntry, error) {
Name: name,
})
}
- return entries, nil
+ return entries, entriesSpecial, nil
}
// getdentsName extracts the filename from a Dirent struct and returns it as
diff --git a/internal/syscallcompat/getdents_other.go b/internal/syscallcompat/getdents_other.go
index 82932db..1ed5ecf 100644
--- a/internal/syscallcompat/getdents_other.go
+++ b/internal/syscallcompat/getdents_other.go
@@ -9,27 +9,11 @@ import (
"github.com/hanwen/go-fuse/v2/fuse"
)
-// emulateGetdents reads all directory entries from the open directory "fd"
-// and returns them in a fuse.DirEntry slice.
-func emulateGetdents(fd int) (out []fuse.DirEntry, err error) {
- // os.File closes the fd in its finalizer. Duplicate the fd to not affect
- // the original fd.
- newFd, err := syscall.Dup(fd)
- if err != nil {
- return nil, err
- }
- f := os.NewFile(uintptr(newFd), "")
- defer f.Close()
- // Get all file names in the directory
- names, err := f.Readdirnames(0)
- if err != nil {
- return nil, err
- }
- // Stat all of them and convert to fuse.DirEntry
- out = make([]fuse.DirEntry, 0, len(names))
+func fillDirEntries(fd int, names []string) ([]fuse.DirEntry, error) {
+ out := make([]fuse.DirEntry, 0, len(names))
for _, name := range names {
var st unix.Stat_t
- err = Fstatat(fd, name, &st, unix.AT_SYMLINK_NOFOLLOW)
+ err := Fstatat(fd, name, &st, unix.AT_SYMLINK_NOFOLLOW)
if err == syscall.ENOENT {
// File disappeared between readdir and stat. Pretend we did not
// see it.
@@ -47,3 +31,31 @@ func emulateGetdents(fd int) (out []fuse.DirEntry, err error) {
}
return out, nil
}
+
+// emulateGetdents reads all directory entries from the open directory "fd"
+// and returns normal entries and "." / ".." split into two slices.
+func emulateGetdents(fd int) (out []fuse.DirEntry, outSpecial []fuse.DirEntry, err error) {
+ // os.File closes the fd in its finalizer. Duplicate the fd to not affect
+ // the original fd.
+ newFd, err := syscall.Dup(fd)
+ if err != nil {
+ return nil, nil, err
+ }
+ f := os.NewFile(uintptr(newFd), "")
+ defer f.Close()
+ // Get all file names in the directory
+ names, err := f.Readdirnames(0)
+ if err != nil {
+ return nil, nil, err
+ }
+ // Stat all the names and convert to fuse.DirEntry
+ out, err = fillDirEntries(fd, names)
+ if err != nil {
+ return nil, nil, err
+ }
+ outSpecial, err = fillDirEntries(fd, []string{".", ".."})
+ if err != nil {
+ return nil, nil, err
+ }
+ return out, outSpecial, nil
+}
diff --git a/internal/syscallcompat/getdents_test.go b/internal/syscallcompat/getdents_test.go
index cb95b7c..a6f41ca 100644
--- a/internal/syscallcompat/getdents_test.go
+++ b/internal/syscallcompat/getdents_test.go
@@ -83,7 +83,7 @@ func testGetdents(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- getdentsEntries, err := getdentsUnderTest(int(fd.Fd()))
+ getdentsEntries, special, err := getdentsUnderTest(int(fd.Fd()))
if err != nil {
t.Log(err)
skipOnGccGo(t)
@@ -114,5 +114,20 @@ func testGetdents(t *testing.T) {
}
}
}
+ if len(special) != 2 {
+ t.Error(special)
+ }
+ if !(special[0].Name == "." && special[1].Name == ".." ||
+ special[1].Name == "." && special[0].Name == "..") {
+ t.Error(special)
+ }
+ for _, v := range special {
+ if v.Ino == 0 {
+ t.Error(v)
+ }
+ if v.Mode != syscall.S_IFDIR {
+ t.Error(v)
+ }
+ }
}
}
diff --git a/internal/syscallcompat/sys_darwin.go b/internal/syscallcompat/sys_darwin.go
index a14a4ee..b5f7d49 100644
--- a/internal/syscallcompat/sys_darwin.go
+++ b/internal/syscallcompat/sys_darwin.go
@@ -216,6 +216,11 @@ func UtimesNanoAtNofollow(dirfd int, path string, a *time.Time, m *time.Time) (e
}
func Getdents(fd int) ([]fuse.DirEntry, error) {
+ entries, _, err := emulateGetdents(fd)
+ return entries, err
+}
+
+func GetdentsSpecial(fd int) (entries []fuse.DirEntry, entriesSpecial []fuse.DirEntry, err error) {
return emulateGetdents(fd)
}
diff --git a/internal/syscallcompat/sys_linux.go b/internal/syscallcompat/sys_linux.go
index 46d039c..36ce7d5 100644
--- a/internal/syscallcompat/sys_linux.go
+++ b/internal/syscallcompat/sys_linux.go
@@ -244,8 +244,15 @@ func UtimesNanoAtNofollow(dirfd int, path string, a *time.Time, m *time.Time) (e
return err
}
-// Getdents syscall.
+// Getdents syscall with "." and ".." filtered out.
func Getdents(fd int) ([]fuse.DirEntry, error) {
+ entries, _, err := getdents(fd)
+ return entries, err
+}
+
+// GetdentsSpecial calls the Getdents syscall,
+// with normal entries and "." / ".." split into two slices.
+func GetdentsSpecial(fd int) (entries []fuse.DirEntry, entriesSpecial []fuse.DirEntry, err error) {
return getdents(fd)
}