Skip to content

Commit 3c8ed18

Browse files
Add . and .. to list calls in fuse3
1 parent 7b09f6a commit 3c8ed18

File tree

2 files changed

+62
-8
lines changed

2 files changed

+62
-8
lines changed

component/libfuse/libfuse2_handler.go

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,13 @@ package libfuse
2828
import (
2929
"errors"
3030
"fmt"
31+
"hash/fnv"
3132
"io"
3233
"io/fs"
3334
"os"
35+
"path"
3436
"runtime"
37+
"strings"
3538
"syscall"
3639
"time"
3740

@@ -200,6 +203,7 @@ func (lf *Libfuse) fillStat(attr *internal.ObjAttr, stbuf *fuse.Stat_t) {
200203
stbuf.Gid = lf.ownerGID
201204
stbuf.Nlink = 1
202205
stbuf.Size = attr.Size
206+
stbuf.Ino = inodeForAttr(attr)
203207

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

266270
// Return the default configuration for the root
267-
if name == "" {
271+
if name == "" || name == "." || name == ".." {
268272
stat.Mode = fuse.S_IFDIR | 0777
269273
stat.Uid = cf.uid
270274
stat.Gid = cf.gid
@@ -273,6 +277,7 @@ func (cf *CgofuseFS) Getattr(path string, stat *fuse.Stat_t, fh uint64) int {
273277
stat.Mtim = fuse.NewTimespec(time.Now())
274278
stat.Atim = stat.Mtim
275279
stat.Ctim = stat.Mtim
280+
stat.Ino = inodeForPath(name)
276281
return 0
277282
}
278283

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

517522
// add the first two entries in any directory listing ('.' and '..') to the cache
518523
// this replaces any existing cache
519-
func cacheDots(cacheInfo *dirChildCache) {
520-
dotAttrs := []*internal.ObjAttr{
521-
{Flags: fuseFS.lsFlags, Name: "."},
522-
{Flags: fuseFS.lsFlags, Name: ".."},
523-
}
524+
func cacheDots(cacheInfo *dirChildCache, dirPath string) {
525+
currentDir := internal.CreateObjAttrDir(dirPath)
526+
currentDir.Name = "."
527+
parentPath := strings.TrimSuffix(dirPath, "/")
528+
parentPath = path.Dir(parentPath)
529+
parentDir := internal.CreateObjAttrDir(parentPath)
530+
parentDir.Name = ".."
531+
dotAttrs := []*internal.ObjAttr{currentDir, parentDir}
524532
cacheInfo.sIndex = 0
525533
cacheInfo.eIndex = 2
526534
cacheInfo.length = 2
@@ -530,6 +538,22 @@ func cacheDots(cacheInfo *dirChildCache) {
530538
cacheInfo.lastPage = false
531539
}
532540

541+
func inodeForAttr(attr *internal.ObjAttr) uint64 {
542+
if attr == nil {
543+
return 0
544+
}
545+
if attr.Path != "" {
546+
return inodeForPath(attr.Path)
547+
}
548+
return inodeForPath(attr.Name)
549+
}
550+
551+
func inodeForPath(p string) uint64 {
552+
h := fnv.New64a()
553+
_, _ = h.Write([]byte(p))
554+
return h.Sum64()
555+
}
556+
533557
// Fill the directory list cache with data from the next component
534558
func populateDirChildCache(
535559
handle *handlemap.Handle,

component/libfuse/libfuse2_handler_test_wrapper.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ func testReaddirEmptyPageToken(suite *libfuseTestSuite) {
238238
length: 0,
239239
children: make([]*internal.ObjAttr, 0),
240240
}
241-
cacheDots(cacheInfo)
241+
cacheDots(cacheInfo, "dir/")
242242
cacheInfo.token = "next"
243243
cacheInfo.lastPage = false
244244
handle.SetValue("cache", cacheInfo)
@@ -260,6 +260,36 @@ func testReaddirEmptyPageToken(suite *libfuseTestSuite) {
260260
suite.assert.False(fillCalled)
261261
}
262262

263+
func testReaddirNonZeroOffsetCachesDots(suite *libfuseTestSuite) {
264+
defer suite.cleanupTest()
265+
266+
handle := handlemap.NewHandle("dir/")
267+
cacheInfo := &dirChildCache{
268+
sIndex: 0,
269+
eIndex: 0,
270+
token: "",
271+
length: 0,
272+
children: make([]*internal.ObjAttr, 0),
273+
}
274+
handle.SetValue("cache", cacheInfo)
275+
fh := handlemap.Add(handle)
276+
defer handlemap.Delete(handle.ID)
277+
278+
suite.mock.EXPECT().
279+
StreamDir(internal.StreamDirOptions{Name: "dir/", Token: ""}).
280+
Return([]*internal.ObjAttr{}, "", nil)
281+
282+
var names []string
283+
fill := func(name string, stat *fuse.Stat_t, ofst int64) bool {
284+
names = append(names, name)
285+
return true
286+
}
287+
288+
err := cfuseFS.Readdir("/dir", fill, 1, uint64(fh))
289+
suite.assert.Equal(0, err)
290+
suite.assert.Equal([]string{".", ".."}, names)
291+
}
292+
263293
func testRmDir(suite *libfuseTestSuite) {
264294
defer suite.cleanupTest()
265295
name := "path"

0 commit comments

Comments
 (0)