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