diff options
| author | Jakob Unterwurzacher | 2021-05-23 12:00:09 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2021-05-26 13:17:56 +0200 | 
| commit | 1b3c3b1347ef711ec38bb4c5cb14661035faf2ce (patch) | |
| tree | 663aa26d1acef54045d526c2c4905b23045fe37a /internal | |
| parent | 7d72baca69825a83487728e000596241f4ac88fe (diff) | |
syscallcompat: add GetdentsSpecial()
GetdentsSpecial calls then Getdents syscall,
with normal entries and "." / ".." split into two slices.
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/syscallcompat/getdents_linux.go | 23 | ||||
| -rw-r--r-- | internal/syscallcompat/getdents_other.go | 50 | ||||
| -rw-r--r-- | internal/syscallcompat/getdents_test.go | 17 | ||||
| -rw-r--r-- | internal/syscallcompat/sys_darwin.go | 5 | ||||
| -rw-r--r-- | internal/syscallcompat/sys_linux.go | 9 | 
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)  } | 
