syscallcompat: add GetdentsSpecial()
GetdentsSpecial calls then Getdents syscall, with normal entries and "." / ".." split into two slices.
This commit is contained in:
parent
7d72baca69
commit
1b3c3b1347
@ -27,7 +27,7 @@ const sizeofDirent = int(unsafe.Sizeof(unix.Dirent{}))
|
|||||||
const maxReclen = 280
|
const maxReclen = 280
|
||||||
|
|
||||||
// getdents wraps unix.Getdents and converts the result to []fuse.DirEntry.
|
// 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.
|
// Collect syscall result in smartBuf.
|
||||||
// "bytes.Buffer" is smart about expanding the capacity and avoids the
|
// "bytes.Buffer" is smart about expanding the capacity and avoids the
|
||||||
// exponential runtime of simple append().
|
// exponential runtime of simple append().
|
||||||
@ -44,9 +44,9 @@ func getdents(fd int) ([]fuse.DirEntry, error) {
|
|||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
if smartBuf.Len() > 0 {
|
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))
|
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 {
|
if n == 0 {
|
||||||
break
|
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",
|
tlog.Warn.Printf("Getdents: corrupt entry #%d: Reclen=0 at offset=%d. Returning EBADR",
|
||||||
numEntries, offset)
|
numEntries, offset)
|
||||||
// EBADR = Invalid request descriptor
|
// EBADR = Invalid request descriptor
|
||||||
return nil, syscall.EBADR
|
return nil, nil, syscall.EBADR
|
||||||
}
|
}
|
||||||
if int(s.Reclen) > maxReclen {
|
if int(s.Reclen) > maxReclen {
|
||||||
tlog.Warn.Printf("Getdents: corrupt entry #%d: Reclen=%d > %d. Returning EBADR",
|
tlog.Warn.Printf("Getdents: corrupt entry #%d: Reclen=%d > %d. Returning EBADR",
|
||||||
numEntries, s.Reclen, maxReclen)
|
numEntries, s.Reclen, maxReclen)
|
||||||
return nil, syscall.EBADR
|
return nil, nil, syscall.EBADR
|
||||||
}
|
}
|
||||||
offset += int(s.Reclen)
|
offset += int(s.Reclen)
|
||||||
numEntries++
|
numEntries++
|
||||||
@ -80,17 +80,22 @@ func getdents(fd int) ([]fuse.DirEntry, error) {
|
|||||||
// Note: syscall.ParseDirent() only returns the names,
|
// Note: syscall.ParseDirent() only returns the names,
|
||||||
// we want all the data, so we have to implement
|
// we want all the data, so we have to implement
|
||||||
// it on our own.
|
// it on our own.
|
||||||
entries := make([]fuse.DirEntry, 0, numEntries)
|
entries = make([]fuse.DirEntry, 0, numEntries)
|
||||||
offset = 0
|
offset = 0
|
||||||
for offset < len(buf) {
|
for offset < len(buf) {
|
||||||
s := *(*unix.Dirent)(unsafe.Pointer(&buf[offset]))
|
s := *(*unix.Dirent)(unsafe.Pointer(&buf[offset]))
|
||||||
name, err := getdentsName(s)
|
name, err := getdentsName(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
offset += int(s.Reclen)
|
offset += int(s.Reclen)
|
||||||
if name == "." || name == ".." {
|
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
|
continue
|
||||||
}
|
}
|
||||||
mode, err := convertDType(fd, name, s.Type)
|
mode, err := convertDType(fd, name, s.Type)
|
||||||
@ -105,7 +110,7 @@ func getdents(fd int) ([]fuse.DirEntry, error) {
|
|||||||
Name: name,
|
Name: name,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return entries, nil
|
return entries, entriesSpecial, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getdentsName extracts the filename from a Dirent struct and returns it as
|
// getdentsName extracts the filename from a Dirent struct and returns it as
|
||||||
|
@ -9,27 +9,11 @@ import (
|
|||||||
"github.com/hanwen/go-fuse/v2/fuse"
|
"github.com/hanwen/go-fuse/v2/fuse"
|
||||||
)
|
)
|
||||||
|
|
||||||
// emulateGetdents reads all directory entries from the open directory "fd"
|
func fillDirEntries(fd int, names []string) ([]fuse.DirEntry, error) {
|
||||||
// and returns them in a fuse.DirEntry slice.
|
out := make([]fuse.DirEntry, 0, len(names))
|
||||||
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))
|
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
var st unix.Stat_t
|
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 {
|
if err == syscall.ENOENT {
|
||||||
// File disappeared between readdir and stat. Pretend we did not
|
// File disappeared between readdir and stat. Pretend we did not
|
||||||
// see it.
|
// see it.
|
||||||
@ -47,3 +31,31 @@ func emulateGetdents(fd int) (out []fuse.DirEntry, err error) {
|
|||||||
}
|
}
|
||||||
return out, nil
|
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
|
||||||
|
}
|
||||||
|
@ -83,7 +83,7 @@ func testGetdents(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
getdentsEntries, err := getdentsUnderTest(int(fd.Fd()))
|
getdentsEntries, special, err := getdentsUnderTest(int(fd.Fd()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
skipOnGccGo(t)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,6 +216,11 @@ func UtimesNanoAtNofollow(dirfd int, path string, a *time.Time, m *time.Time) (e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Getdents(fd int) ([]fuse.DirEntry, error) {
|
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)
|
return emulateGetdents(fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,8 +244,15 @@ func UtimesNanoAtNofollow(dirfd int, path string, a *time.Time, m *time.Time) (e
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getdents syscall.
|
// Getdents syscall with "." and ".." filtered out.
|
||||||
func Getdents(fd int) ([]fuse.DirEntry, error) {
|
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)
|
return getdents(fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user