mount: make files opened for read seekable - fixes #707

This commit is contained in:
Nick Craig-Wood 2016-09-10 22:25:26 +01:00
parent aef2ac5c04
commit 265f5b77a7
4 changed files with 65 additions and 10 deletions

View File

@ -112,13 +112,11 @@ func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenR
fs.Debug(o, "File.Open")
// Files aren't seekable
resp.Flags |= fuse.OpenNonSeekable
switch {
case req.Flags.IsReadOnly():
return newReadFileHandle(o)
case req.Flags.IsWriteOnly():
resp.Flags |= fuse.OpenNonSeekable
src := newCreateInfo(f.d.f, o.Remote())
fh, err := newWriteFileHandle(f.d, f, src)
if err != nil {

View File

@ -82,10 +82,9 @@ Or with OS X
### Limitations ###
This can only read files seqentially, or write files sequentially. It
can't read and write or seek in files.
This can only write files seqentially, it can only seek when reading.
rclonefs inherits rclone's directory handling. In rclone's world
Rclone mount inherits rclone's directory handling. In rclone's world
directories don't really exist. This means that empty directories
will have a tendency to disappear once they fall out of the directory
cache.

View File

@ -19,6 +19,7 @@ type ReadFileHandle struct {
r io.ReadCloser
o fs.Object
readCalled bool // set if read has been called
offset int64
}
func newReadFileHandle(o fs.Object) (*ReadFileHandle, error) {
@ -38,14 +39,38 @@ var _ fusefs.Handle = (*ReadFileHandle)(nil)
// Check interface satisfied
var _ fusefs.HandleReader = (*ReadFileHandle)(nil)
// seek to a new offset
func (fh *ReadFileHandle) seek(offset int64) error {
fs.Debug(fh.o, "ReadFileHandle.seek from %d to %d", fh.offset, offset)
r, err := fh.o.Open(&fs.SeekOption{Offset: offset})
if err != nil {
fs.Debug(fh.o, "ReadFileHandle.Read seek failed: %v", err)
return err
}
err = fh.r.Close()
if err != nil {
fs.Debug(fh.o, "ReadFileHandle.Read seek close old failed: %v", err)
}
fh.r = r
return nil
}
// Read from the file handle
func (fh *ReadFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
fs.Debug(fh.o, "ReadFileHandle.Open")
fs.Debug(fh.o, "ReadFileHandle.Read size %d offset %d", req.Size, req.Offset)
if fh.closed {
fs.ErrorLog(fh.o, "ReadFileHandle.Read error: %v", errClosedFileHandle)
return errClosedFileHandle
}
fh.readCalled = true
if req.Offset != fh.offset {
err := fh.seek(req.Offset)
if err != nil {
return err
}
}
if req.Size > 0 {
fh.readCalled = true
}
// We don't actually enforce Offset to match where previous read
// ended. Maybe we should, but that would mean'd we need to track
// it. The kernel *should* do it for us, based on the
@ -60,10 +85,11 @@ func (fh *ReadFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp
err = nil
}
resp.Data = buf[:n]
fh.offset += int64(n)
if err != nil {
fs.ErrorLog(fh.o, "ReadFileHandle.Open error: %v", err)
fs.ErrorLog(fh.o, "ReadFileHandle.Read error: %v", err)
} else {
fs.Debug(fh.o, "ReadFileHandle.Open OK")
fs.Debug(fh.o, "ReadFileHandle.Read OK")
}
return err
}

View File

@ -4,6 +4,7 @@ package mount
import (
"io"
"io/ioutil"
"os"
"syscall"
"testing"
@ -77,3 +78,34 @@ func TestReadFileDoubleClose(t *testing.T) {
run.rm(t, "testdoubleclose")
}
// Test seeking
func TestReadSeek(t *testing.T) {
run.skipIfNoFUSE(t)
var data = []byte("helloHELLO")
run.createFile(t, "testfile", string(data))
run.checkDir(t, "testfile 10")
fd, err := os.Open(run.path("testfile"))
assert.NoError(t, err)
_, err = fd.Seek(5, 0)
assert.NoError(t, err)
buf, err := ioutil.ReadAll(fd)
assert.NoError(t, err)
assert.Equal(t, buf, []byte("HELLO"))
_, err = fd.Seek(0, 0)
assert.NoError(t, err)
buf, err = ioutil.ReadAll(fd)
assert.NoError(t, err)
assert.Equal(t, buf, []byte("helloHELLO"))
err = fd.Close()
assert.NoError(t, err)
run.rm(t, "testfile")
}