Commit 111c2926 authored by Jakob Unterwurzacher's avatar Jakob Unterwurzacher

fs: bridge: implement simple directory seeking

"seeking" works by skipping forward to the requested offset,
first rewinding to zero if seeking back.

The performance is obviously bad, however, seeking
inside a directory does not seem to be too common.

I think the solution is good enough until we see a
use case where this causes performance issues.

Fixes https://github.com/hanwen/go-fuse/issues/344

Change-Id: Ia7f7fbffaf932c16919eafede6b70f4aff245c25
parent 73071346
......@@ -33,6 +33,12 @@ type fileEntry struct {
dirStream DirStream
hasOverflow bool
overflow fuse.DirEntry
// dirOffset is the current location in the directory (see `telldir(3)`).
// The value is equivalent to `d_off` (see `getdents(2)`) of the last
// directory entry sent to the kernel so far.
// If `dirOffset` and `fuse.DirEntryList.offset` disagree, then a
// directory seek has taken place.
dirOffset uint64
wg sync.WaitGroup
}
......@@ -842,23 +848,47 @@ func (b *rawBridge) OpenDir(cancel <-chan struct{}, input *fuse.OpenIn, out *fus
return fuse.OK
}
// setStream sets the directory part of f. Must hold f.mu
func (b *rawBridge) setStream(cancel <-chan struct{}, input *fuse.ReadIn, inode *Inode, f *fileEntry) syscall.Errno {
if f.dirStream == nil || input.Offset == 0 {
// setStream makes sure `f.dirStream` and associated state variables are set and
// seeks to offset requested in `input`. Caller must hold `f.mu`.
// The `eof` return value shows if `f.dirStream` ended before the requested
// offset was reached.
func (b *rawBridge) setStream(cancel <-chan struct{}, input *fuse.ReadIn, inode *Inode, f *fileEntry) (errno syscall.Errno, eof bool) {
// Get a new directory stream in the following cases:
// 1) f.dirStream == nil ............ First READDIR[PLUS] on this file handle.
// 2) input.Offset == 0 ............. Start reading the directory again from
// the beginning (user called rewinddir(3) or lseek(2)).
// 3) input.Offset < f.nextOffset ... Seek back (user called seekdir(3) or lseek(2)).
if f.dirStream == nil || input.Offset == 0 || input.Offset < f.dirOffset {
if f.dirStream != nil {
f.dirStream.Close()
f.dirStream = nil
}
str, errno := b.getStream(&fuse.Context{Caller: input.Caller, Cancel: cancel}, inode)
if errno != 0 {
return errno
return errno, false
}
f.dirOffset = 0
f.hasOverflow = false
f.dirStream = str
}
return 0
// Seek forward?
for f.dirOffset < input.Offset {
f.hasOverflow = false
if !f.dirStream.HasNext() {
// Seek past end of directory. This is not an error, but the
// user will get an empty directory listing.
return 0, true
}
_, errno := f.dirStream.Next()
if errno != 0 {
return errno, true
}
f.dirOffset++
}
return 0, false
}
func (b *rawBridge) getStream(ctx context.Context, inode *Inode) (DirStream, syscall.Errno) {
......@@ -880,14 +910,19 @@ func (b *rawBridge) ReadDir(cancel <-chan struct{}, input *fuse.ReadIn, out *fus
f.mu.Lock()
defer f.mu.Unlock()
if errno := b.setStream(cancel, input, n, f); errno != 0 {
errno, eof := b.setStream(cancel, input, n, f)
if errno != 0 {
return errnoToStatus(errno)
} else if eof {
return fuse.OK
}
if f.hasOverflow {
// always succeeds.
out.AddDirEntry(f.overflow)
f.hasOverflow = false
f.dirOffset++
}
for f.dirStream.HasNext() {
......@@ -901,6 +936,7 @@ func (b *rawBridge) ReadDir(cancel <-chan struct{}, input *fuse.ReadIn, out *fus
f.hasOverflow = true
return errnoToStatus(errno)
}
f.dirOffset++
}
return fuse.OK
......@@ -911,8 +947,12 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out
f.mu.Lock()
defer f.mu.Unlock()
if errno := b.setStream(cancel, input, n, f); errno != 0 {
errno, eof := b.setStream(cancel, input, n, f)
if errno != 0 {
return errnoToStatus(errno)
} else if eof {
return fuse.OK
}
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
......@@ -937,6 +977,7 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out
f.hasOverflow = true
return fuse.OK
}
f.dirOffset++
// Virtual entries "." and ".." should be part of the
// directory listing, but not part of the filesystem tree.
......
......@@ -36,10 +36,17 @@ func (d DirEntry) String() string {
// DirEntryList holds the return value for READDIR and READDIRPLUS
// opcodes.
type DirEntryList struct {
buf []byte
size int // capacity of the underlying buffer
offset uint64 // entry count (NOT a byte offset)
lastDirent *_Dirent // pointer to the last serialized _Dirent. Used by FixMode().
buf []byte
// capacity of the underlying buffer
size int
// offset is the requested location in the directory. go-fuse
// currently counts in number of directory entries, but this is an
// implementation detail and may change in the future.
// If `offset` and `fs.fileEntry.dirOffset` disagree, then a
// directory seek has taken place.
offset uint64
// pointer to the last serialized _Dirent. Used by FixMode().
lastDirent *_Dirent
}
// NewDirEntryList creates a DirEntryList with the given data buffer
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment