forked from TrueCloudLab/rclone
vfs: funnel all read/write calls through ReadAt/WriteAt
This is in preparation for partial reads for read/write files
This commit is contained in:
parent
8c37262e05
commit
88df5927f9
1 changed files with 75 additions and 61 deletions
|
@ -3,6 +3,7 @@ package vfs
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -19,13 +20,14 @@ import (
|
||||||
// It will be open to a temporary file which, when closed, will be
|
// It will be open to a temporary file which, when closed, will be
|
||||||
// transferred to the remote.
|
// transferred to the remote.
|
||||||
type RWFileHandle struct {
|
type RWFileHandle struct {
|
||||||
fd *os.File
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
closed bool // set if handle has been closed
|
fd *os.File
|
||||||
|
offset int64 // file pointer offset
|
||||||
file *File
|
file *File
|
||||||
d *Dir
|
d *Dir
|
||||||
opened bool
|
|
||||||
flags int // open flags
|
flags int // open flags
|
||||||
|
closed bool // set if handle has been closed
|
||||||
|
opened bool
|
||||||
writeCalled bool // if any Write() methods have been called
|
writeCalled bool // if any Write() methods have been called
|
||||||
changed bool // file contents was changed in any other way
|
changed bool // file contents was changed in any other way
|
||||||
}
|
}
|
||||||
|
@ -353,10 +355,13 @@ func (fh *RWFileHandle) Release() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns the size of the underlying file
|
// _size returns the size of the underlying file
|
||||||
func (fh *RWFileHandle) Size() int64 {
|
//
|
||||||
fh.mu.Lock()
|
// call with the lock held
|
||||||
defer fh.mu.Unlock()
|
//
|
||||||
|
// FIXME what if a file was partially read in - this may return the wrong thing?
|
||||||
|
// FIXME need to make sure we extend the file to the maximum when creating it
|
||||||
|
func (fh *RWFileHandle) _size() int64 {
|
||||||
if !fh.opened {
|
if !fh.opened {
|
||||||
return fh.file.Size()
|
return fh.file.Size()
|
||||||
}
|
}
|
||||||
|
@ -367,6 +372,13 @@ func (fh *RWFileHandle) Size() int64 {
|
||||||
return fi.Size()
|
return fi.Size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size returns the size of the underlying file
|
||||||
|
func (fh *RWFileHandle) Size() int64 {
|
||||||
|
fh.mu.Lock()
|
||||||
|
defer fh.mu.Unlock()
|
||||||
|
return fh._size()
|
||||||
|
}
|
||||||
|
|
||||||
// Stat returns info about the file
|
// Stat returns info about the file
|
||||||
func (fh *RWFileHandle) Stat() (os.FileInfo, error) {
|
func (fh *RWFileHandle) Stat() (os.FileInfo, error) {
|
||||||
fh.mu.Lock()
|
fh.mu.Lock()
|
||||||
|
@ -374,35 +386,36 @@ func (fh *RWFileHandle) Stat() (os.FileInfo, error) {
|
||||||
return fh.file, nil
|
return fh.file, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// readFn is a general purpose read function - pass in a closure to do
|
// _readAt bytes from the file at off
|
||||||
// the actual read
|
//
|
||||||
func (fh *RWFileHandle) readFn(read func() (int, error)) (n int, err error) {
|
// call with lock held
|
||||||
fh.mu.Lock()
|
func (fh *RWFileHandle) _readAt(b []byte, off int64) (n int, err error) {
|
||||||
defer fh.mu.Unlock()
|
|
||||||
if fh.closed {
|
if fh.closed {
|
||||||
return 0, ECLOSED
|
return n, ECLOSED
|
||||||
}
|
}
|
||||||
if fh.flags&accessModeMask == os.O_WRONLY {
|
if fh.flags&accessModeMask == os.O_WRONLY {
|
||||||
return 0, EBADF
|
return n, EBADF
|
||||||
}
|
}
|
||||||
if err = fh.openPending(false); err != nil {
|
if err = fh.openPending(false); err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
return read()
|
return fh.fd.ReadAt(b, off)
|
||||||
}
|
|
||||||
|
|
||||||
// Read bytes from the file
|
|
||||||
func (fh *RWFileHandle) Read(b []byte) (n int, err error) {
|
|
||||||
return fh.readFn(func() (int, error) {
|
|
||||||
return fh.fd.Read(b)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadAt bytes from the file at off
|
// ReadAt bytes from the file at off
|
||||||
func (fh *RWFileHandle) ReadAt(b []byte, off int64) (n int, err error) {
|
func (fh *RWFileHandle) ReadAt(b []byte, off int64) (n int, err error) {
|
||||||
return fh.readFn(func() (int, error) {
|
fh.mu.Lock()
|
||||||
return fh.fd.ReadAt(b, off)
|
defer fh.mu.Unlock()
|
||||||
})
|
return fh._readAt(b, off)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read bytes from the file
|
||||||
|
func (fh *RWFileHandle) Read(b []byte) (n int, err error) {
|
||||||
|
fh.mu.Lock()
|
||||||
|
defer fh.mu.Unlock()
|
||||||
|
n, err = fh._readAt(b, fh.offset)
|
||||||
|
fh.offset += int64(n)
|
||||||
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seek to new file position
|
// Seek to new file position
|
||||||
|
@ -418,66 +431,67 @@ func (fh *RWFileHandle) Seek(offset int64, whence int) (ret int64, err error) {
|
||||||
if err = fh.openPending(false); err != nil {
|
if err = fh.openPending(false); err != nil {
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
return fh.fd.Seek(offset, whence)
|
switch whence {
|
||||||
|
case io.SeekStart:
|
||||||
|
fh.offset = 0
|
||||||
|
case io.SeekEnd:
|
||||||
|
fh.offset = fh._size()
|
||||||
|
}
|
||||||
|
fh.offset += offset
|
||||||
|
// we don't check the offset - the next Read will
|
||||||
|
return fh.offset, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeFn general purpose write call
|
// WriteAt bytes to the file at off
|
||||||
//
|
func (fh *RWFileHandle) _writeAt(b []byte, off int64) (n int, err error) {
|
||||||
// Pass a closure to do the actual write
|
|
||||||
func (fh *RWFileHandle) writeFn(write func() error) (err error) {
|
|
||||||
fh.mu.Lock()
|
|
||||||
defer fh.mu.Unlock()
|
|
||||||
if fh.closed {
|
if fh.closed {
|
||||||
return ECLOSED
|
return n, ECLOSED
|
||||||
}
|
}
|
||||||
if fh.flags&accessModeMask == os.O_RDONLY {
|
if fh.flags&accessModeMask == os.O_RDONLY {
|
||||||
return EBADF
|
return n, EBADF
|
||||||
}
|
}
|
||||||
if err = fh.openPending(false); err != nil {
|
if err = fh.openPending(false); err != nil {
|
||||||
return err
|
return n, err
|
||||||
}
|
}
|
||||||
fh.writeCalled = true
|
fh.writeCalled = true
|
||||||
err = write()
|
|
||||||
if err != nil {
|
if fh.flags&os.O_APPEND != 0 {
|
||||||
return err
|
// if append is set, call Write as WriteAt returns an error if append is set
|
||||||
|
n, err = fh.fd.Write(b)
|
||||||
|
} else {
|
||||||
|
n, err = fh.fd.WriteAt(b, off)
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
fi, err := fh.fd.Stat()
|
fi, err := fh.fd.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to stat cache file")
|
return n, errors.Wrap(err, "failed to stat cache file")
|
||||||
}
|
}
|
||||||
fh.file.setSize(fi.Size())
|
fh.file.setSize(fi.Size())
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write bytes to the file
|
|
||||||
func (fh *RWFileHandle) Write(b []byte) (n int, err error) {
|
|
||||||
err = fh.writeFn(func() error {
|
|
||||||
n, err = fh.fd.Write(b)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteAt bytes to the file at off
|
// WriteAt bytes to the file at off
|
||||||
func (fh *RWFileHandle) WriteAt(b []byte, off int64) (n int, err error) {
|
func (fh *RWFileHandle) WriteAt(b []byte, off int64) (n int, err error) {
|
||||||
if fh.flags&os.O_APPEND != 0 {
|
fh.mu.Lock()
|
||||||
// if append is set, call Write as WriteAt returns an error if append is set
|
defer fh.mu.Unlock()
|
||||||
return fh.Write(b)
|
return fh._writeAt(b, off)
|
||||||
}
|
}
|
||||||
err = fh.writeFn(func() error {
|
|
||||||
n, err = fh.fd.WriteAt(b, off)
|
// Write bytes to the file
|
||||||
return err
|
func (fh *RWFileHandle) Write(b []byte) (n int, err error) {
|
||||||
})
|
fh.mu.Lock()
|
||||||
|
defer fh.mu.Unlock()
|
||||||
|
n, err = fh._writeAt(b, fh.offset)
|
||||||
|
fh.offset += int64(n)
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteString a string to the file
|
// WriteString a string to the file
|
||||||
func (fh *RWFileHandle) WriteString(s string) (n int, err error) {
|
func (fh *RWFileHandle) WriteString(s string) (n int, err error) {
|
||||||
err = fh.writeFn(func() error {
|
return fh.Write([]byte(s))
|
||||||
n, err = fh.fd.WriteString(s)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
return n, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Truncate file to given size
|
// Truncate file to given size
|
||||||
|
|
Loading…
Reference in a new issue