Commit 8cbe92c1 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

fs: fix Lseek whence=SEEK_HOLE default implementation

According to the lseek(2) manpage, seek(SEEK_HOLE) should behave as if
there is an implicit hole at the end of every file.

Add a test.

Fixes #417.

Change-Id: I169a803ea81e3c920b61edf8bb8b774870d042c5
parent e452f946
...@@ -1153,23 +1153,35 @@ func (b *rawBridge) CopyFileRange(cancel <-chan struct{}, in *fuse.CopyFileRange ...@@ -1153,23 +1153,35 @@ func (b *rawBridge) CopyFileRange(cancel <-chan struct{}, in *fuse.CopyFileRange
func (b *rawBridge) Lseek(cancel <-chan struct{}, in *fuse.LseekIn, out *fuse.LseekOut) fuse.Status { func (b *rawBridge) Lseek(cancel <-chan struct{}, in *fuse.LseekIn, out *fuse.LseekOut) fuse.Status {
n, f := b.inode(in.NodeId, in.Fh) n, f := b.inode(in.NodeId, in.Fh)
ctx := &fuse.Context{Caller: in.Caller, Cancel: cancel}
ls, ok := n.ops.(NodeLseeker) ls, ok := n.ops.(NodeLseeker)
if ok { if ok {
off, errno := ls.Lseek(&fuse.Context{Caller: in.Caller, Cancel: cancel}, off, errno := ls.Lseek(ctx,
f.file, in.Offset, in.Whence) f.file, in.Offset, in.Whence)
out.Offset = off out.Offset = off
return errnoToStatus(errno) return errnoToStatus(errno)
} }
if fs, ok := f.file.(FileLseeker); ok { if fs, ok := f.file.(FileLseeker); ok {
off, errno := fs.Lseek(&fuse.Context{Caller: in.Caller, Cancel: cancel}, in.Offset, in.Whence) off, errno := fs.Lseek(ctx, in.Offset, in.Whence)
out.Offset = off out.Offset = off
return errnoToStatus(errno) return errnoToStatus(errno)
} }
if in.Whence == _SEEK_DATA || in.Whence == _SEEK_HOLE { if in.Whence == _SEEK_DATA {
out.Offset = in.Offset out.Offset = in.Offset
return fuse.OK return fuse.OK
} }
if in.Whence == _SEEK_HOLE {
var attr fuse.AttrOut
if s := b.getattr(ctx, n, nil, &attr); s != 0 {
return errnoToStatus(s)
}
out.Offset = attr.Size
return fuse.OK
}
return fuse.ENOTSUP return fuse.ENOTSUP
} }
...@@ -19,6 +19,7 @@ import ( ...@@ -19,6 +19,7 @@ import (
"github.com/hanwen/go-fuse/v2/fuse" "github.com/hanwen/go-fuse/v2/fuse"
"github.com/hanwen/go-fuse/v2/internal/testutil" "github.com/hanwen/go-fuse/v2/internal/testutil"
"github.com/hanwen/go-fuse/v2/posixtest"
) )
func testMount(t *testing.T, root InodeEmbedder, opts *Options) (string, *fuse.Server) { func testMount(t *testing.T, root InodeEmbedder, opts *Options) (string, *fuse.Server) {
...@@ -90,6 +91,28 @@ func TestRootInode(t *testing.T) { ...@@ -90,6 +91,28 @@ func TestRootInode(t *testing.T) {
} }
} }
func TestLseekDefault(t *testing.T) {
data := []byte("hello")
root := &Inode{}
mntDir, _ := testMount(t, root, &Options{
FirstAutomaticIno: 1,
OnAdd: func(ctx context.Context) {
n := root.EmbeddedInode()
ch := n.NewPersistentInode(
ctx,
&MemRegularFile{
Data: data,
Attr: fuse.Attr{
Mode: 0464,
},
}, StableAttr{})
n.AddChild("file.bin", ch, false)
},
})
posixtest.LseekHoleSeeksToEOF(t, mntDir)
}
func TestDataFile(t *testing.T) { func TestDataFile(t *testing.T) {
want := "hello" want := "hello"
root := &Inode{} root := &Inode{}
......
...@@ -33,6 +33,7 @@ var All = map[string]func(*testing.T, string){ ...@@ -33,6 +33,7 @@ var All = map[string]func(*testing.T, string){
"ParallelFileOpen": ParallelFileOpen, "ParallelFileOpen": ParallelFileOpen,
"Link": Link, "Link": Link,
"LinkUnlinkRename": LinkUnlinkRename, "LinkUnlinkRename": LinkUnlinkRename,
"LseekHoleSeeksToEOF": LseekHoleSeeksToEOF,
"RenameOverwriteDestNoExist": RenameOverwriteDestNoExist, "RenameOverwriteDestNoExist": RenameOverwriteDestNoExist,
"RenameOverwriteDestExist": RenameOverwriteDestExist, "RenameOverwriteDestExist": RenameOverwriteDestExist,
"RenameOpenDir": RenameOpenDir, "RenameOpenDir": RenameOpenDir,
...@@ -698,3 +699,24 @@ func FcntlFlockLocksFile(t *testing.T, mnt string) { ...@@ -698,3 +699,24 @@ func FcntlFlockLocksFile(t *testing.T, mnt string) {
t.Errorf("FcntlFlock returned %v, expected EAGAIN", err) t.Errorf("FcntlFlock returned %v, expected EAGAIN", err)
} }
} }
func LseekHoleSeeksToEOF(t *testing.T, mnt string) {
fn := filepath.Join(mnt, "file.bin")
content := bytes.Repeat([]byte("abcxyz\n"), 1024)
if err := ioutil.WriteFile(fn, content, 0644); err != nil {
t.Fatalf("WriteFile: %v", err)
}
fd, err := syscall.Open(fn, syscall.O_RDONLY, 0644)
if err != nil {
t.Fatalf("Open: %v", err)
}
defer syscall.Close(fd)
off, err := unix.Seek(fd, int64(len(content)/2), unix.SEEK_HOLE)
if err != nil {
t.Fatalf("Seek: %v", err)
} else if off != int64(len(content)) {
t.Errorf("got offset %d, want %d", off, len(content))
}
}
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