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:
Nick Craig-Wood 2020-03-19 15:54:53 +00:00
parent 8c37262e05
commit 88df5927f9

View file

@ -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