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") fs.Debug(o, "File.Open")
// Files aren't seekable
resp.Flags |= fuse.OpenNonSeekable
switch { switch {
case req.Flags.IsReadOnly(): case req.Flags.IsReadOnly():
return newReadFileHandle(o) return newReadFileHandle(o)
case req.Flags.IsWriteOnly(): case req.Flags.IsWriteOnly():
resp.Flags |= fuse.OpenNonSeekable
src := newCreateInfo(f.d.f, o.Remote()) src := newCreateInfo(f.d.f, o.Remote())
fh, err := newWriteFileHandle(f.d, f, src) fh, err := newWriteFileHandle(f.d, f, src)
if err != nil { if err != nil {

View File

@ -82,10 +82,9 @@ Or with OS X
### Limitations ### ### Limitations ###
This can only read files seqentially, or write files sequentially. It This can only write files seqentially, it can only seek when reading.
can't read and write or seek in files.
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 directories don't really exist. This means that empty directories
will have a tendency to disappear once they fall out of the directory will have a tendency to disappear once they fall out of the directory
cache. cache.

View File

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

View File

@ -4,6 +4,7 @@ package mount
import ( import (
"io" "io"
"io/ioutil"
"os" "os"
"syscall" "syscall"
"testing" "testing"
@ -77,3 +78,34 @@ func TestReadFileDoubleClose(t *testing.T) {
run.rm(t, "testdoubleclose") 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")
}