mirror of
https://github.com/rclone/rclone.git
synced 2024-11-25 09:41:44 +08:00
mountlib: Make read/write file handles support more standard interfaces
Including Read, ReadAt, Seek, Close for read handles and Write, WriteAt, Close for read handles.
This commit is contained in:
parent
2fac74b517
commit
ca19fd2d7e
|
@ -411,11 +411,10 @@ func (fsys *FS) Read(path string, buff []byte, ofst int64, fh uint64) (n int) {
|
||||||
// Can only read from read file handle
|
// Can only read from read file handle
|
||||||
return -fuse.EIO
|
return -fuse.EIO
|
||||||
}
|
}
|
||||||
data, err := rfh.Read(int64(len(buff)), ofst)
|
n, err := rfh.ReadAt(buff, ofst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return translateError(err)
|
return translateError(err)
|
||||||
}
|
}
|
||||||
n = copy(buff, data)
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,12 +430,11 @@ func (fsys *FS) Write(path string, buff []byte, ofst int64, fh uint64) (n int) {
|
||||||
// Can only write to write file handle
|
// Can only write to write file handle
|
||||||
return -fuse.EIO
|
return -fuse.EIO
|
||||||
}
|
}
|
||||||
// FIXME made Write return int and Read take int since must fit in RAM
|
n, err := wfh.WriteAt(buff, ofst)
|
||||||
n64, err := wfh.Write(buff, ofst)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return translateError(err)
|
return translateError(err)
|
||||||
}
|
}
|
||||||
return int(n64)
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush flushes an open file descriptor or path
|
// Flush flushes an open file descriptor or path
|
||||||
|
|
|
@ -29,14 +29,14 @@ var _ fusefs.HandleReader = (*ReadFileHandle)(nil)
|
||||||
|
|
||||||
// Read from the file handle
|
// Read from the file handle
|
||||||
func (fh *ReadFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) (err error) {
|
func (fh *ReadFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) (err error) {
|
||||||
dataRead := -1
|
var n int
|
||||||
defer fs.Trace(fh, "len=%d, offset=%d", req.Size, req.Offset)("read=%d, err=%v", &dataRead, &err)
|
defer fs.Trace(fh, "len=%d, offset=%d", req.Size, req.Offset)("read=%d, err=%v", &n, &err)
|
||||||
data, err := fh.ReadFileHandle.Read(int64(req.Size), req.Offset)
|
data := make([]byte, req.Size)
|
||||||
|
n, err = fh.ReadFileHandle.ReadAt(data, req.Offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return translateError(err)
|
return translateError(err)
|
||||||
}
|
}
|
||||||
resp.Data = data
|
resp.Data = data[:n]
|
||||||
dataRead = len(data)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ var _ fusefs.HandleWriter = (*WriteFileHandle)(nil)
|
||||||
// Write data to the file handle
|
// Write data to the file handle
|
||||||
func (fh *WriteFileHandle) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) (err error) {
|
func (fh *WriteFileHandle) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) (err error) {
|
||||||
defer fs.Trace(fh, "len=%d, offset=%d", len(req.Data), req.Offset)("written=%d, err=%v", &resp.Size, &err)
|
defer fs.Trace(fh, "len=%d, offset=%d", len(req.Data), req.Offset)("written=%d, err=%v", &resp.Size, &err)
|
||||||
n, err := fh.WriteFileHandle.Write(req.Data, req.Offset)
|
n, err := fh.WriteFileHandle.WriteAt(req.Data, req.Offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return translateError(err)
|
return translateError(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,14 +14,23 @@ type ReadFileHandle struct {
|
||||||
closed bool // set if handle has been closed
|
closed bool // set if handle has been closed
|
||||||
r *fs.Account
|
r *fs.Account
|
||||||
o fs.Object
|
o fs.Object
|
||||||
readCalled bool // set if read has been called
|
readCalled bool // set if read has been called
|
||||||
offset int64
|
offset int64 // offset of read of o
|
||||||
|
roffset int64 // offset of Read() calls
|
||||||
noSeek bool
|
noSeek bool
|
||||||
file *File
|
file *File
|
||||||
hash *fs.MultiHasher
|
hash *fs.MultiHasher
|
||||||
opened bool
|
opened bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check interfaces
|
||||||
|
var (
|
||||||
|
_ io.Reader = (*ReadFileHandle)(nil)
|
||||||
|
_ io.ReaderAt = (*ReadFileHandle)(nil)
|
||||||
|
_ io.Seeker = (*ReadFileHandle)(nil)
|
||||||
|
_ io.Closer = (*ReadFileHandle)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
func newReadFileHandle(f *File, o fs.Object) (*ReadFileHandle, error) {
|
func newReadFileHandle(f *File, o fs.Object) (*ReadFileHandle, error) {
|
||||||
var hash *fs.MultiHasher
|
var hash *fs.MultiHasher
|
||||||
var err error
|
var err error
|
||||||
|
@ -113,36 +122,72 @@ func (fh *ReadFileHandle) seek(offset int64, reopen bool) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read from the file handle
|
// Seek the file
|
||||||
func (fh *ReadFileHandle) Read(reqSize, reqOffset int64) (respData []byte, err error) {
|
func (fh *ReadFileHandle) Seek(offset int64, whence int) (n int64, err error) {
|
||||||
|
size := fh.o.Size()
|
||||||
|
switch whence {
|
||||||
|
case 0:
|
||||||
|
fh.roffset = 0
|
||||||
|
case 2:
|
||||||
|
fh.roffset = size
|
||||||
|
}
|
||||||
|
fh.roffset += offset
|
||||||
|
// we don't check the offset - the next Read will
|
||||||
|
return fh.roffset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAt reads len(p) bytes into p starting at offset off in the
|
||||||
|
// underlying input source. It returns the number of bytes read (0 <=
|
||||||
|
// n <= len(p)) and any error encountered.
|
||||||
|
//
|
||||||
|
// When ReadAt returns n < len(p), it returns a non-nil error
|
||||||
|
// explaining why more bytes were not returned. In this respect,
|
||||||
|
// ReadAt is stricter than Read.
|
||||||
|
//
|
||||||
|
// Even if ReadAt returns n < len(p), it may use all of p as scratch
|
||||||
|
// space during the call. If some data is available but not len(p)
|
||||||
|
// bytes, ReadAt blocks until either all the data is available or an
|
||||||
|
// error occurs. In this respect ReadAt is different from Read.
|
||||||
|
//
|
||||||
|
// If the n = len(p) bytes returned by ReadAt are at the end of the
|
||||||
|
// input source, ReadAt may return either err == EOF or err == nil.
|
||||||
|
//
|
||||||
|
// If ReadAt is reading from an input source with a seek offset,
|
||||||
|
// ReadAt should not affect nor be affected by the underlying seek
|
||||||
|
// offset.
|
||||||
|
//
|
||||||
|
// Clients of ReadAt can execute parallel ReadAt calls on the same
|
||||||
|
// input source.
|
||||||
|
//
|
||||||
|
// Implementations must not retain p.
|
||||||
|
func (fh *ReadFileHandle) ReadAt(p []byte, off int64) (n int, err error) {
|
||||||
fh.mu.Lock()
|
fh.mu.Lock()
|
||||||
defer fh.mu.Unlock()
|
defer fh.mu.Unlock()
|
||||||
err = fh.openPending() // FIXME pending open could be more efficient in the presense of seek (and retried)
|
err = fh.openPending() // FIXME pending open could be more efficient in the presense of seek (and retried)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return 0, err
|
||||||
}
|
}
|
||||||
// fs.Debugf(fh.o, "ReadFileHandle.Read size %d offset %d", reqSize, reqOffset)
|
// fs.Debugf(fh.o, "ReadFileHandle.Read size %d offset %d", reqSize, off)
|
||||||
if fh.closed {
|
if fh.closed {
|
||||||
fs.Errorf(fh.o, "ReadFileHandle.Read error: %v", EBADF)
|
fs.Errorf(fh.o, "ReadFileHandle.Read error: %v", EBADF)
|
||||||
return nil, EBADF
|
return 0, EBADF
|
||||||
}
|
}
|
||||||
doSeek := reqOffset != fh.offset
|
doSeek := off != fh.offset
|
||||||
var n int
|
|
||||||
var newOffset int64
|
var newOffset int64
|
||||||
retries := 0
|
retries := 0
|
||||||
buf := make([]byte, reqSize)
|
reqSize := len(p)
|
||||||
doReopen := false
|
doReopen := false
|
||||||
for {
|
for {
|
||||||
if doSeek {
|
if doSeek {
|
||||||
// Are we attempting to seek beyond the end of the
|
// Are we attempting to seek beyond the end of the
|
||||||
// file - if so just return EOF leaving the underlying
|
// file - if so just return EOF leaving the underlying
|
||||||
// file in an unchanged state.
|
// file in an unchanged state.
|
||||||
if reqOffset >= fh.o.Size() {
|
if off >= fh.o.Size() {
|
||||||
fs.Debugf(fh.o, "ReadFileHandle.Read attempt to read beyond end of file: %d > %d", reqOffset, fh.o.Size())
|
fs.Debugf(fh.o, "ReadFileHandle.Read attempt to read beyond end of file: %d > %d", off, fh.o.Size())
|
||||||
return nil, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
// Otherwise do the seek
|
// Otherwise do the seek
|
||||||
err = fh.seek(reqOffset, doReopen)
|
err = fh.seek(off, doReopen)
|
||||||
} else {
|
} else {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
|
@ -153,7 +198,7 @@ func (fh *ReadFileHandle) Read(reqSize, reqOffset int64) (respData []byte, err e
|
||||||
// One exception to the above is if we fail to fully populate a
|
// 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.
|
// page cache page; a read into page cache is always page aligned.
|
||||||
// Make sure we never serve a partial read, to avoid that.
|
// Make sure we never serve a partial read, to avoid that.
|
||||||
n, err = io.ReadFull(fh.r, buf)
|
n, err = io.ReadFull(fh.r, p)
|
||||||
newOffset = fh.offset + int64(n)
|
newOffset = fh.offset + int64(n)
|
||||||
// if err == nil && rand.Intn(10) == 0 {
|
// if err == nil && rand.Intn(10) == 0 {
|
||||||
// err = errors.New("random error")
|
// err = errors.New("random error")
|
||||||
|
@ -177,19 +222,18 @@ func (fh *ReadFileHandle) Read(reqSize, reqOffset int64) (respData []byte, err e
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fs.Errorf(fh.o, "ReadFileHandle.Read error: %v", err)
|
fs.Errorf(fh.o, "ReadFileHandle.Read error: %v", err)
|
||||||
} else {
|
} else {
|
||||||
respData = buf[:n]
|
|
||||||
fh.offset = newOffset
|
fh.offset = newOffset
|
||||||
// fs.Debugf(fh.o, "ReadFileHandle.Read OK")
|
// fs.Debugf(fh.o, "ReadFileHandle.Read OK")
|
||||||
|
|
||||||
if fh.hash != nil {
|
if fh.hash != nil {
|
||||||
_, err = fh.hash.Write(respData)
|
_, err = fh.hash.Write(p[:n])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fs.Errorf(fh.o, "ReadFileHandle.Read HashError: %v", err)
|
fs.Errorf(fh.o, "ReadFileHandle.Read HashError: %v", err)
|
||||||
return nil, err
|
return 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return respData, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fh *ReadFileHandle) checkHash() error {
|
func (fh *ReadFileHandle) checkHash() error {
|
||||||
|
@ -210,6 +254,38 @@ func (fh *ReadFileHandle) checkHash() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read reads up to len(p) bytes into p. It returns the number of bytes read (0
|
||||||
|
// <= n <= len(p)) and any error encountered. Even if Read returns n < len(p),
|
||||||
|
// it may use all of p as scratch space during the call. If some data is
|
||||||
|
// available but not len(p) bytes, Read conventionally returns what is
|
||||||
|
// available instead of waiting for more.
|
||||||
|
//
|
||||||
|
// When Read encounters an error or end-of-file condition after successfully
|
||||||
|
// reading n > 0 bytes, it returns the number of bytes read. It may return the
|
||||||
|
// (non-nil) error from the same call or return the error (and n == 0) from a
|
||||||
|
// subsequent call. An instance of this general case is that a Reader returning
|
||||||
|
// a non-zero number of bytes at the end of the input stream may return either
|
||||||
|
// err == EOF or err == nil. The next Read should return 0, EOF.
|
||||||
|
//
|
||||||
|
// Callers should always process the n > 0 bytes returned before considering
|
||||||
|
// the error err. Doing so correctly handles I/O errors that happen after
|
||||||
|
// reading some bytes and also both of the allowed EOF behaviors.
|
||||||
|
//
|
||||||
|
// Implementations of Read are discouraged from returning a zero byte count
|
||||||
|
// with a nil error, except when len(p) == 0. Callers should treat a return of
|
||||||
|
// 0 and nil as indicating that nothing happened; in particular it does not
|
||||||
|
// indicate EOF.
|
||||||
|
//
|
||||||
|
// Implementations must not retain p.
|
||||||
|
func (fh *ReadFileHandle) Read(p []byte) (n int, err error) {
|
||||||
|
if fh.roffset >= fh.o.Size() {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
n, err = fh.ReadAt(p, fh.roffset)
|
||||||
|
fh.roffset += int64(n)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
// close the file handle returning EBADF if it has been
|
// close the file handle returning EBADF if it has been
|
||||||
// closed already.
|
// closed already.
|
||||||
//
|
//
|
||||||
|
|
|
@ -21,6 +21,13 @@ type WriteFileHandle struct {
|
||||||
offset int64
|
offset int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check interfaces
|
||||||
|
var (
|
||||||
|
_ io.Writer = (*WriteFileHandle)(nil)
|
||||||
|
_ io.WriterAt = (*WriteFileHandle)(nil)
|
||||||
|
_ io.Closer = (*WriteFileHandle)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
func newWriteFileHandle(d *Dir, f *File, src fs.ObjectInfo) (*WriteFileHandle, error) {
|
func newWriteFileHandle(d *Dir, f *File, src fs.ObjectInfo) (*WriteFileHandle, error) {
|
||||||
fh := &WriteFileHandle{
|
fh := &WriteFileHandle{
|
||||||
remote: src.Remote(),
|
remote: src.Remote(),
|
||||||
|
@ -60,12 +67,23 @@ func (fh *WriteFileHandle) Node() Node {
|
||||||
return fh.file
|
return fh.file
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write data to the file handle
|
// WriteAt writes len(p) bytes from p to the underlying data stream at offset
|
||||||
func (fh *WriteFileHandle) Write(data []byte, offset int64) (written int64, err error) {
|
// off. It returns the number of bytes written from p (0 <= n <= len(p)) and
|
||||||
// fs.Debugf(fh.remote, "WriteFileHandle.Write len=%d", len(data))
|
// any error encountered that caused the write to stop early. WriteAt must
|
||||||
|
// return a non-nil error if it returns n < len(p).
|
||||||
|
//
|
||||||
|
// If WriteAt is writing to a destination with a seek offset, WriteAt should
|
||||||
|
// not affect nor be affected by the underlying seek offset.
|
||||||
|
//
|
||||||
|
// Clients of WriteAt can execute parallel WriteAt calls on the same
|
||||||
|
// destination if the ranges do not overlap.
|
||||||
|
//
|
||||||
|
// Implementations must not retain p.
|
||||||
|
func (fh *WriteFileHandle) WriteAt(p []byte, off int64) (n int, err error) {
|
||||||
|
// fs.Debugf(fh.remote, "WriteFileHandle.Write len=%d", len(p))
|
||||||
fh.mu.Lock()
|
fh.mu.Lock()
|
||||||
defer fh.mu.Unlock()
|
defer fh.mu.Unlock()
|
||||||
if fh.offset != offset {
|
if fh.offset != off {
|
||||||
fs.Errorf(fh.remote, "WriteFileHandle.Write can't seek in file")
|
fs.Errorf(fh.remote, "WriteFileHandle.Write can't seek in file")
|
||||||
return 0, ESPIPE
|
return 0, ESPIPE
|
||||||
}
|
}
|
||||||
|
@ -74,16 +92,27 @@ func (fh *WriteFileHandle) Write(data []byte, offset int64) (written int64, err
|
||||||
return 0, EBADF
|
return 0, EBADF
|
||||||
}
|
}
|
||||||
fh.writeCalled = true
|
fh.writeCalled = true
|
||||||
n, err := fh.pipeWriter.Write(data)
|
n, err = fh.pipeWriter.Write(p)
|
||||||
written = int64(n)
|
fh.offset += int64(n)
|
||||||
fh.offset += written
|
|
||||||
fh.file.setSize(fh.offset)
|
fh.file.setSize(fh.offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fs.Errorf(fh.remote, "WriteFileHandle.Write error: %v", err)
|
fs.Errorf(fh.remote, "WriteFileHandle.Write error: %v", err)
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
// fs.Debugf(fh.remote, "WriteFileHandle.Write OK (%d bytes written)", n)
|
// fs.Debugf(fh.remote, "WriteFileHandle.Write OK (%d bytes written)", n)
|
||||||
return written, nil
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes len(p) bytes from p to the underlying data stream. It returns
|
||||||
|
// the number of bytes written from p (0 <= n <= len(p)) and any error
|
||||||
|
// encountered that caused the write to stop early. Write must return a non-nil
|
||||||
|
// error if it returns n < len(p). Write must not modify the slice data, even
|
||||||
|
// temporarily.
|
||||||
|
//
|
||||||
|
// Implementations must not retain p.
|
||||||
|
func (fh *WriteFileHandle) Write(p []byte) (n int, err error) {
|
||||||
|
// Since we can't seek, just call WriteAt with the current offset
|
||||||
|
return fh.WriteAt(p, fh.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Offset returns the offset of the file pointer
|
// Offset returns the offset of the file pointer
|
||||||
|
|
Loading…
Reference in New Issue
Block a user