Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions component/libfuse/libfuse2_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ package libfuse
import (
"errors"
"fmt"
"hash/fnv"
"io"
"io/fs"
"os"
"path"
"runtime"
"strings"
"syscall"
"time"

Expand Down Expand Up @@ -200,6 +203,7 @@ func (lf *Libfuse) fillStat(attr *internal.ObjAttr, stbuf *fuse.Stat_t) {
stbuf.Gid = lf.ownerGID
stbuf.Nlink = 1
stbuf.Size = attr.Size
stbuf.Ino = inodeForAttr(attr)

// Populate mode
// Backing storage implementation has support for mode.
Expand Down Expand Up @@ -264,7 +268,7 @@ func (cf *CgofuseFS) Getattr(path string, stat *fuse.Stat_t, fh uint64) int {
// log.Trace("Libfuse::Getattr : %s", name)

// Return the default configuration for the root
if name == "" {
if name == "" || name == "." || name == ".." {
stat.Mode = fuse.S_IFDIR | 0777
stat.Uid = cf.uid
stat.Gid = cf.gid
Expand All @@ -273,6 +277,7 @@ func (cf *CgofuseFS) Getattr(path string, stat *fuse.Stat_t, fh uint64) int {
stat.Mtim = fuse.NewTimespec(time.Now())
stat.Atim = stat.Mtim
stat.Ctim = stat.Mtim
stat.Ino = inodeForPath(name)
return 0
}

Expand Down Expand Up @@ -462,7 +467,7 @@ func (cf *CgofuseFS) Readdir(
newRequest := offset == 0
if newRequest {
// cache the first two entries ('.' & '..')
cacheDots(cacheInfo)
cacheDots(cacheInfo, handle.Path)
}
startOffset := offset
// fetch and serve directory contents back to the OS in a loop until their buffer is full
Expand Down Expand Up @@ -516,11 +521,14 @@ type fillFunc = func(name string, stat *fuse.Stat_t, ofst int64) bool

// add the first two entries in any directory listing ('.' and '..') to the cache
// this replaces any existing cache
func cacheDots(cacheInfo *dirChildCache) {
dotAttrs := []*internal.ObjAttr{
{Flags: fuseFS.lsFlags, Name: "."},
{Flags: fuseFS.lsFlags, Name: ".."},
}
func cacheDots(cacheInfo *dirChildCache, dirPath string) {
currentDir := internal.CreateObjAttrDir(dirPath)
currentDir.Name = "."
parentPath := strings.TrimSuffix(dirPath, "/")
parentPath = path.Dir(parentPath)
parentDir := internal.CreateObjAttrDir(parentPath)
parentDir.Name = ".."
dotAttrs := []*internal.ObjAttr{currentDir, parentDir}
cacheInfo.sIndex = 0
cacheInfo.eIndex = 2
cacheInfo.length = 2
Expand All @@ -530,6 +538,22 @@ func cacheDots(cacheInfo *dirChildCache) {
cacheInfo.lastPage = false
}

func inodeForAttr(attr *internal.ObjAttr) uint64 {
if attr == nil {
return 0
}
if attr.Path != "" {
return inodeForPath(attr.Path)
}
return inodeForPath(attr.Name)
}

func inodeForPath(p string) uint64 {
h := fnv.New64a()
_, _ = h.Write([]byte(p))
return h.Sum64()
}

// Fill the directory list cache with data from the next component
func populateDirChildCache(
handle *handlemap.Handle,
Expand Down
2 changes: 1 addition & 1 deletion component/libfuse/libfuse2_handler_test_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ func testReaddirEmptyPageToken(suite *libfuseTestSuite) {
length: 0,
children: make([]*internal.ObjAttr, 0),
}
cacheDots(cacheInfo)
cacheDots(cacheInfo, "dir/")
cacheInfo.token = "next"
cacheInfo.lastPage = false
handle.SetValue("cache", cacheInfo)
Expand Down
29 changes: 29 additions & 0 deletions test/e2e_tests/dir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,35 @@ func (suite *dirTestSuite) TestDirList() {
suite.dirTestCleanup([]string{testDir})
}

// # List directory with dot entries
func (suite *dirTestSuite) TestDirListShowsDots() {
if runtime.GOOS == "windows" {
fmt.Println("Skipping TestDirListShowsDots on Windows")
return
}
cmd := exec.Command("ls", "-al", suite.testPath)
cliOut, err := cmd.Output()
suite.NoError(err)
lines := strings.Split(string(cliOut), "\n")
foundDot := false
foundDotDot := false
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) == 0 {
continue
}
name := fields[len(fields)-1]
switch name {
case ".":
foundDot = true
case "..":
foundDotDot = true
}
}
suite.True(foundDot, "expected '.' to be listed in ls -al output")
suite.True(foundDotDot, "expected '..' to be listed in ls -al output")
}

// // # List directory recursively
// func (suite *dirTestSuite) TestDirListRecursive() {
// testDir := filepath.Join(suite.testPath, "bigTestDir")
Expand Down
Loading