Commit 24136e0d authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Change ReadResult to interface.

Implementations: ReadResultFd{} and ReadResultData{}.
parent c4df48de
......@@ -31,22 +31,22 @@ func CopyFile(srcFs, destFs FileSystem, srcFile, destFile string, context *Conte
if !code.Ok() {
return code
}
res.Read(buf)
data := res.Bytes(buf)
if len(res.Data) == 0 {
if len(data) == 0 {
break
}
n, code := dst.Write(res.Data, off)
n, code := dst.Write(data, off)
if !code.Ok() {
return code
}
if int(n) < len(res.Data) {
if int(n) < len(data) {
return EIO
}
if len(res.Data) < len(buf) {
if len(data) < len(buf) {
break
}
off += int64(len(res.Data))
off += int64(len(data))
}
return OK
}
......@@ -22,7 +22,7 @@ func (f *DefaultFile) String() string {
}
func (f *DefaultFile) Read(buf []byte, off int64) (ReadResult, Status) {
return ReadResult{}, ENOSYS
return &ReadResultData{}, ENOSYS
}
func (f *DefaultFile) Write(data []byte, off int64) (uint32, Status) {
......
......@@ -97,7 +97,7 @@ func (fs *DefaultRawFileSystem) OpenDir(out *raw.OpenOut, header *raw.InHeader,
}
func (fs *DefaultRawFileSystem) Read(header *raw.InHeader, input *raw.ReadIn, buf []byte) (ReadResult, Status) {
return ReadResult{}, ENOSYS
return &ReadResultData{}, ENOSYS
}
func (fs *DefaultRawFileSystem) Release(header *raw.InHeader, input *raw.ReleaseIn) {
......
......@@ -44,9 +44,8 @@ func (f *DataFile) Read(buf []byte, off int64) (res ReadResult, code Status) {
if end > len(f.data) {
end = len(f.data)
}
res.Data = f.data[off:end]
return res, OK
return &ReadResultData{f.data[off:end]}, OK
}
////////////////
......@@ -67,7 +66,7 @@ func (f *DevNullFile) String() string {
}
func (f *DevNullFile) Read(buf []byte, off int64) (ReadResult, Status) {
return ReadResult{}, OK
return &ReadResultData{}, OK
}
func (f *DevNullFile) Write(content []byte, off int64) (uint32, Status) {
......@@ -100,10 +99,10 @@ func (f *LoopbackFile) String() string {
}
func (f *LoopbackFile) Read(buf []byte, off int64) (res ReadResult, code Status) {
return ReadResult{
Fd: f.File.Fd(),
FdOff: off,
FdSize: len(buf),
return &ReadResultFd{
Fd: f.File.Fd(),
Off: off,
Sz: len(buf),
}, OK
}
......
......@@ -28,7 +28,7 @@ func (f *MutableDataFile) Read(buf []byte, off int64) (ReadResult, Status) {
end = len(f.data)
}
return ReadResult{Data: f.data[off:end]}, OK
return &ReadResultData{Data: f.data[off:end]}, OK
}
func (f *MutableDataFile) Write(d []byte, off int64) (uint32, Status) {
......
......@@ -341,7 +341,7 @@ func (ms *MountState) write(req *request) Status {
return OK
}
header := req.serializeHeader(req.flatData.Size())
header := req.serializeHeader(req.flatDataSize())
if ms.Debug {
log.Println(req.OutputDebug())
}
......@@ -354,59 +354,65 @@ func (ms *MountState) write(req *request) Status {
return OK
}
if req.flatData.Size() == 0 {
if req.flatDataSize() == 0 {
_, err := ms.mountFile.Write(header)
return ToStatus(err)
}
if req.flatData.FdSize > 0 {
if err := ms.TrySplice(header, req, req.flatData.Fd, req.flatData.FdSize, req.flatData.FdOff); err == nil {
if req.fdData != nil {
if err := ms.TrySplice(header, req, req.fdData); err == nil {
return OK
} else {
log.Println("TrySplice:", err)
buf := ms.AllocOut(req, uint32(req.flatData.FdSize))
req.flatData.Read(buf)
header = req.serializeHeader(req.flatData.Size())
sz := req.flatDataSize()
buf := ms.AllocOut(req, uint32(sz))
req.flatData = req.fdData.Bytes(buf)
header = req.serializeHeader(req.flatDataSize())
}
}
_, err := Writev(int(ms.mountFile.Fd()), [][]byte{header, req.flatData.Data})
_, err := Writev(int(ms.mountFile.Fd()), [][]byte{header, req.flatData})
return ToStatus(err)
}
func (ms *MountState) TrySplice(header []byte, req *request,
fd uintptr, size int, off int64) error {
finalSplice, err := splice.Get()
func (ms *MountState) TrySplice(header []byte, req *request, fdData *ReadResultFd) error {
pair, err := splice.Get()
if err != nil {
return err
}
defer splice.Done(finalSplice)
defer splice.Done(pair)
total := len(header) + size
if !finalSplice.Grow(total) {
total := len(header) + fdData.Size()
if !pair.Grow(total) {
return fmt.Errorf("splice.Grow failed.")
}
_, err = finalSplice.Write(header)
_, err = pair.Write(header)
if err != nil {
return err
}
var n int
if off < 0 {
n, err = finalSplice.LoadFrom(fd, size)
if fdData.Off < 0 {
n, err = pair.LoadFrom(fdData.Fd, fdData.Size())
} else {
n, err = finalSplice.LoadFromAt(fd, size, off)
n, err = pair.LoadFromAt(fdData.Fd, fdData.Size(), fdData.Off)
}
if err == io.EOF || (err == nil && n < size) {
if err == io.EOF || (err == nil && n < fdData.Size()) {
discard := make([]byte, len(header))
_, err = finalSplice.Read(discard)
_, err = pair.Read(discard)
if err != nil {
return err
}
header = req.serializeHeader(n)
return ms.TrySplice(header, req, finalSplice.ReadFd(), n, -1)
newFd := ReadResultFd{
Fd: pair.ReadFd(),
Off: -1,
Sz: n,
}
return ms.TrySplice(header, req, &newFd)
}
if err != nil {
......@@ -414,11 +420,11 @@ func (ms *MountState) TrySplice(header []byte, req *request,
return err
}
if n != size {
return fmt.Errorf("wrote %d, want %d", n, req.flatData.FdSize)
if n != fdData.Size() {
return fmt.Errorf("wrote %d, want %d", n, fdData.Size())
}
_, err = finalSplice.WriteTo(ms.mountFile.Fd(), total)
_, err = pair.WriteTo(ms.mountFile.Fd(), total)
if err != nil {
return fmt.Errorf("splice write: %v", err)
}
......@@ -461,7 +467,7 @@ func (ms *MountState) writeEntryNotify(parent uint64, name string) Status {
copy(nameBytes, name)
nameBytes[len(nameBytes)-1] = '\000'
req.outData = unsafe.Pointer(entry)
req.flatData.Data = nameBytes
req.flatData = nameBytes
result := ms.write(&req)
if ms.Debug {
......
......@@ -132,7 +132,7 @@ func doReadDir(state *MountState, req *request) {
entries := NewDirEntryList(buf, uint64(in.Offset))
code := state.fileSystem.ReadDir(entries, req.inHeader, in)
req.flatData.Data = entries.Bytes()
req.flatData = entries.Bytes()
req.status = code
}
......@@ -201,7 +201,7 @@ func doGetXAttr(state *MountState, req *request) {
return
}
req.flatData.Data = data
req.flatData = data
}
func doGetAttr(state *MountState, req *request) {
......@@ -232,7 +232,7 @@ func doBatchForget(state *MountState, req *request) {
}
func doReadlink(state *MountState, req *request) {
req.flatData.Data, req.status = state.fileSystem.Readlink(req.inHeader)
req.flatData, req.status = state.fileSystem.Readlink(req.inHeader)
}
func doLookup(state *MountState, req *request) {
......@@ -269,7 +269,15 @@ func doLink(state *MountState, req *request) {
func doRead(state *MountState, req *request) {
in := (*raw.ReadIn)(req.inData)
buf := state.AllocOut(req, in.Size)
req.flatData, req.status = state.fileSystem.Read(req.inHeader, in, buf)
var r ReadResult
r, req.status = state.fileSystem.Read(req.inHeader, in, buf)
if fd, ok := r.(*ReadResultFd); ok {
req.fdData = fd
req.flatData = nil
} else if r != nil {
req.flatData = r.Bytes(buf)
}
}
func doFlush(state *MountState, req *request) {
......
......@@ -5,6 +5,11 @@ import (
"syscall"
)
type ReadResult interface {
Bytes(buf []byte) []byte
Size() int
}
// The result of Read is an array of bytes, but for performance
// reasons, we can also return data as a file-descriptor/offset/size
// tuple. If the backing store for a file is another filesystem, this
......@@ -13,54 +18,49 @@ import (
//
// If at any point, the raw data is needed, ReadResult.Read() will
// load the raw data into the Data member.
type ReadResult struct {
type ReadResultData struct {
// Raw bytes for the read.
Data []byte
}
func (r *ReadResultData) Size() int {
return len(r.Data)
}
func (r *ReadResultData) Bytes(buf []byte) []byte {
return r.Data
}
type ReadResultFd struct {
// If Data is nil and Status OK, splice from the following
// file.
Fd uintptr
// Offset within Fd, or -1 to use current offset.
FdOff int64
Off int64
// Size of data to be loaded. Actual data available may be
// less at the EOF.
FdSize int
}
func (r *ReadResult) Clear() {
*r = ReadResult{}
}
func (r *ReadResult) Size() int {
if r.Data != nil {
return len(r.Data)
}
return r.FdSize
Sz int
}
// Reads raw bytes from file descriptor if necessary, using the passed
// buffer as storage.
func (r *ReadResult) Read(buf []byte) Status {
if r.Data != nil {
return OK
}
if len(buf) < r.FdSize {
return ERANGE
func (r *ReadResultFd) Bytes(buf []byte) []byte {
sz := r.Sz
if len(buf) < sz {
sz = len(buf)
}
n, err := syscall.Pread(int(r.Fd), buf[:r.FdSize], r.FdOff)
n, err := syscall.Pread(int(r.Fd), buf[:sz], r.Off)
if err == io.EOF {
err = nil
}
code := ToStatus(err)
if code.Ok() {
r.Data = buf[:n]
}
r.Fd = 0
r.FdOff = 0
r.FdSize = 0
// TODO - error handling?
return buf[:n]
}
return code
func (r *ReadResultFd) Size() int {
return r.Sz
}
......@@ -26,7 +26,8 @@ type request struct {
// Unstructured data, a pointer to the relevant XxxxOut struct.
outData unsafe.Pointer
status Status
flatData ReadResult
flatData []byte
fdData *ReadResultFd
// Start timestamp for timing info.
startNs int64
......@@ -58,7 +59,8 @@ func (r *request) clear() {
r.filenames = nil
r.outData = nil
r.status = OK
r.flatData.Clear()
r.flatData = nil
r.fdData = nil
r.preWriteNs = 0
r.startNs = 0
r.handler = nil
......@@ -100,16 +102,16 @@ func (r *request) OutputDebug() string {
}
flatStr := ""
if r.flatData.Size() > 0 {
if r.flatDataSize() > 0 {
if r.handler.FileNameOut {
s := strings.TrimRight(string(r.flatData.Data), "\x00")
s := strings.TrimRight(string(r.flatData), "\x00")
flatStr = fmt.Sprintf(" %q", s)
} else {
spl := ""
if r.flatData.FdSize > 0 {
if r.fdData != nil {
spl = " (splice)"
}
flatStr = fmt.Sprintf(" %d bytes data%s\n", r.flatData.Size(), spl)
flatStr = fmt.Sprintf(" %d bytes data%s\n", r.flatDataSize(), spl)
}
}
......@@ -198,3 +200,10 @@ func (r *request) serializeHeader(dataSize int) (header []byte) {
copy(header[sizeOfOutHeader:], asSlice)
return header
}
func (r *request) flatDataSize() int {
if r.fdData != nil {
return r.fdData.Size()
}
return len(r.flatData)
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment