2016-07-18 06:03:23 +08:00
|
|
|
// +build linux darwin freebsd
|
|
|
|
|
|
|
|
package mount
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"bazil.org/fuse"
|
|
|
|
fusefs "bazil.org/fuse/fs"
|
|
|
|
"github.com/ncw/rclone/fs"
|
|
|
|
"golang.org/x/net/context"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ReadFileHandle is an open for read file handle on a File
|
|
|
|
type ReadFileHandle struct {
|
|
|
|
mu sync.Mutex
|
|
|
|
closed bool // set if handle has been closed
|
|
|
|
r io.ReadCloser
|
|
|
|
o fs.Object
|
|
|
|
readCalled bool // set if read has been called
|
2016-09-11 05:25:26 +08:00
|
|
|
offset int64
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func newReadFileHandle(o fs.Object) (*ReadFileHandle, error) {
|
|
|
|
r, err := o.Open()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-12-01 04:18:14 +08:00
|
|
|
fh := &ReadFileHandle{
|
2016-07-18 06:03:23 +08:00
|
|
|
o: o,
|
2016-12-01 04:18:14 +08:00
|
|
|
r: fs.NewAccount(r, o), // account and buffer the transfer
|
|
|
|
}
|
2016-12-01 16:49:47 +08:00
|
|
|
fs.Stats.Transferring(fh.o.Remote())
|
2016-12-01 04:18:14 +08:00
|
|
|
return fh, nil
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check interface satisfied
|
|
|
|
var _ fusefs.Handle = (*ReadFileHandle)(nil)
|
|
|
|
|
|
|
|
// Check interface satisfied
|
|
|
|
var _ fusefs.HandleReader = (*ReadFileHandle)(nil)
|
|
|
|
|
2016-09-11 05:25:26 +08:00
|
|
|
// seek to a new offset
|
2016-10-31 01:46:00 +08:00
|
|
|
//
|
|
|
|
// Must be called with fh.mu held
|
2016-09-11 05:25:26 +08:00
|
|
|
func (fh *ReadFileHandle) seek(offset int64) error {
|
2016-10-21 00:47:33 +08:00
|
|
|
// Can we seek it directly?
|
|
|
|
if do, ok := fh.r.(io.Seeker); ok {
|
|
|
|
fs.Debug(fh.o, "ReadFileHandle.seek from %d to %d (io.Seeker)", fh.offset, offset)
|
2016-10-22 19:07:51 +08:00
|
|
|
_, err := do.Seek(offset, 0)
|
2016-10-21 00:47:33 +08:00
|
|
|
if err != nil {
|
|
|
|
fs.Debug(fh.o, "ReadFileHandle.Read io.Seeker failed: %v", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fs.Debug(fh.o, "ReadFileHandle.seek from %d to %d", fh.offset, offset)
|
|
|
|
// if not re-open with a seek
|
|
|
|
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)
|
|
|
|
}
|
2016-12-01 04:18:14 +08:00
|
|
|
fh.r = fs.NewAccount(r, fh.o) // account and buffer the transfer
|
2016-09-11 05:25:26 +08:00
|
|
|
}
|
2016-10-18 03:20:07 +08:00
|
|
|
fh.offset = offset
|
2016-09-11 05:25:26 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-07-18 06:03:23 +08:00
|
|
|
// Read from the file handle
|
|
|
|
func (fh *ReadFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
|
2016-10-27 16:57:52 +08:00
|
|
|
fh.mu.Lock()
|
|
|
|
defer fh.mu.Unlock()
|
2016-09-11 05:25:26 +08:00
|
|
|
fs.Debug(fh.o, "ReadFileHandle.Read size %d offset %d", req.Size, req.Offset)
|
2016-07-18 06:03:23 +08:00
|
|
|
if fh.closed {
|
|
|
|
fs.ErrorLog(fh.o, "ReadFileHandle.Read error: %v", errClosedFileHandle)
|
|
|
|
return errClosedFileHandle
|
|
|
|
}
|
2016-09-11 05:25:26 +08:00
|
|
|
if req.Offset != fh.offset {
|
2016-11-05 17:59:36 +08:00
|
|
|
// Are we attempting to seek beyond the end of the
|
|
|
|
// file - if so just return EOF leaving the underlying
|
|
|
|
// file in an unchanged state.
|
|
|
|
if req.Offset >= fh.o.Size() {
|
|
|
|
fs.Debug(fh.o, "ReadFileHandle.Read attempt to read beyond end of file: %d > %d", req.Offset, fh.o.Size())
|
|
|
|
resp.Data = nil
|
|
|
|
return nil
|
|
|
|
}
|
2016-09-11 05:25:26 +08:00
|
|
|
err := fh.seek(req.Offset)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if req.Size > 0 {
|
|
|
|
fh.readCalled = true
|
|
|
|
}
|
2016-07-18 06:03:23 +08:00
|
|
|
// One exception to the above is if we fail to fully populate a
|
|
|
|
// page cache page; a read into page cache is always page aligned.
|
|
|
|
// Make sure we never serve a partial read, to avoid that.
|
|
|
|
buf := make([]byte, req.Size)
|
|
|
|
n, err := io.ReadFull(fh.r, buf)
|
|
|
|
if err == io.ErrUnexpectedEOF || err == io.EOF {
|
|
|
|
err = nil
|
|
|
|
}
|
|
|
|
resp.Data = buf[:n]
|
2016-09-11 05:25:26 +08:00
|
|
|
fh.offset += int64(n)
|
2016-07-18 06:03:23 +08:00
|
|
|
if err != nil {
|
2016-09-11 05:25:26 +08:00
|
|
|
fs.ErrorLog(fh.o, "ReadFileHandle.Read error: %v", err)
|
2016-07-18 06:03:23 +08:00
|
|
|
} else {
|
2016-09-11 05:25:26 +08:00
|
|
|
fs.Debug(fh.o, "ReadFileHandle.Read OK")
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// close the file handle returning errClosedFileHandle if it has been
|
|
|
|
// closed already.
|
|
|
|
//
|
|
|
|
// Must be called with fh.mu held
|
|
|
|
func (fh *ReadFileHandle) close() error {
|
|
|
|
if fh.closed {
|
|
|
|
return errClosedFileHandle
|
|
|
|
}
|
|
|
|
fh.closed = true
|
2016-12-01 16:49:47 +08:00
|
|
|
fs.Stats.DoneTransferring(fh.o.Remote(), true)
|
2016-07-18 06:03:23 +08:00
|
|
|
return fh.r.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check interface satisfied
|
|
|
|
var _ fusefs.HandleFlusher = (*ReadFileHandle)(nil)
|
|
|
|
|
|
|
|
// Flush is called each time the file or directory is closed.
|
|
|
|
// Because there can be multiple file descriptors referring to a
|
|
|
|
// single opened file, Flush can be called multiple times.
|
|
|
|
func (fh *ReadFileHandle) Flush(ctx context.Context, req *fuse.FlushRequest) error {
|
|
|
|
fh.mu.Lock()
|
|
|
|
defer fh.mu.Unlock()
|
|
|
|
fs.Debug(fh.o, "ReadFileHandle.Flush")
|
|
|
|
|
2016-09-11 05:32:30 +08:00
|
|
|
// Ignore the Flush as there is nothing we can sensibly do and
|
|
|
|
// it seems quite common for Flush to be called from
|
|
|
|
// different threads each of which have read some data.
|
|
|
|
if false {
|
|
|
|
// If Read hasn't been called then ignore the Flush - Release
|
|
|
|
// will pick it up
|
|
|
|
if !fh.readCalled {
|
|
|
|
fs.Debug(fh.o, "ReadFileHandle.Flush ignoring flush on unread handle")
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
err := fh.close()
|
|
|
|
if err != nil {
|
|
|
|
fs.ErrorLog(fh.o, "ReadFileHandle.Flush error: %v", err)
|
|
|
|
return err
|
|
|
|
}
|
2016-07-18 06:03:23 +08:00
|
|
|
}
|
|
|
|
fs.Debug(fh.o, "ReadFileHandle.Flush OK")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ fusefs.HandleReleaser = (*ReadFileHandle)(nil)
|
|
|
|
|
|
|
|
// Release is called when we are finished with the file handle
|
|
|
|
//
|
|
|
|
// It isn't called directly from userspace so the error is ignored by
|
|
|
|
// the kernel
|
|
|
|
func (fh *ReadFileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
|
|
|
|
fh.mu.Lock()
|
|
|
|
defer fh.mu.Unlock()
|
|
|
|
if fh.closed {
|
|
|
|
fs.Debug(fh.o, "ReadFileHandle.Release nothing to do")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
fs.Debug(fh.o, "ReadFileHandle.Release closing")
|
|
|
|
err := fh.close()
|
|
|
|
if err != nil {
|
|
|
|
fs.ErrorLog(fh.o, "ReadFileHandle.Release error: %v", err)
|
|
|
|
} else {
|
|
|
|
fs.Debug(fh.o, "ReadFileHandle.Release OK")
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|