mirror of
https://github.com/rclone/rclone.git
synced 2024-11-22 16:34:30 +01:00
mount: make files opened for read seekable - fixes #707
This commit is contained in:
parent
aef2ac5c04
commit
265f5b77a7
@ -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 {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user