buildkit/vendor/github.com/hanwen/go-fuse/v2/fuse/request.go

266 lines
6.1 KiB
Go

// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fuse
import (
"bytes"
"fmt"
"log"
"strings"
"time"
"unsafe"
)
var sizeOfOutHeader = unsafe.Sizeof(OutHeader{})
var zeroOutBuf [outputHeaderSize]byte
type request struct {
inflightIndex int
cancel chan struct{}
// written under Server.reqMu
interrupted bool
inputBuf []byte
// These split up inputBuf.
inHeader *InHeader // generic header
inData unsafe.Pointer // per op data
arg []byte // flat data.
filenames []string // filename arguments
// Output data.
status Status
flatData []byte
fdData *readResultFd
// In case of read, keep read result here so we can call
// Done() on it.
readResult ReadResult
// Start timestamp for timing info.
startTime time.Time
// All information pertaining to opcode of this request.
handler *operationHandler
// Request storage. For large inputs and outputs, use data
// obtained through bufferpool.
bufferPoolInputBuf []byte
bufferPoolOutputBuf []byte
// For small pieces of data, we use the following inlines
// arrays:
//
// Output header and structured data.
outBuf [outputHeaderSize]byte
// Input, if small enough to fit here.
smallInputBuf [128]byte
}
func (r *request) clear() {
r.inputBuf = nil
r.inHeader = nil
r.inData = nil
r.arg = nil
r.filenames = nil
r.status = OK
r.flatData = nil
r.fdData = nil
r.startTime = time.Time{}
r.handler = nil
r.readResult = nil
}
func (r *request) InputDebug() string {
val := ""
if r.handler != nil && r.handler.DecodeIn != nil {
val = fmt.Sprintf("%v ", Print(r.handler.DecodeIn(r.inData)))
}
names := ""
if r.filenames != nil {
names = fmt.Sprintf("%q", r.filenames)
}
if l := len(r.arg); l > 0 {
data := ""
if len(r.filenames) == 0 {
dots := ""
if l > 8 {
l = 8
dots = "..."
}
data = fmt.Sprintf("%q%s", r.arg[:l], dots)
}
names += fmt.Sprintf("%s %db", data, len(r.arg))
}
return fmt.Sprintf("rx %d: %s n%d %s%s",
r.inHeader.Unique, operationName(r.inHeader.Opcode), r.inHeader.NodeId,
val, names)
}
func (r *request) OutputDebug() string {
var dataStr string
if r.handler != nil && r.handler.DecodeOut != nil && r.handler.OutputSize > 0 {
dataStr = Print(r.handler.DecodeOut(r.outData()))
}
max := 1024
if len(dataStr) > max {
dataStr = dataStr[:max] + fmt.Sprintf(" ...trimmed")
}
flatStr := ""
if r.flatDataSize() > 0 {
if r.handler != nil && r.handler.FileNameOut {
s := strings.TrimRight(string(r.flatData), "\x00")
flatStr = fmt.Sprintf(" %q", s)
} else {
spl := ""
if r.fdData != nil {
spl = " (fd data)"
} else {
l := len(r.flatData)
s := ""
if l > 8 {
l = 8
s = "..."
}
spl = fmt.Sprintf(" %q%s", r.flatData[:l], s)
}
flatStr = fmt.Sprintf(" %db data%s", r.flatDataSize(), spl)
}
}
extraStr := dataStr + flatStr
if extraStr != "" {
extraStr = ", " + extraStr
}
return fmt.Sprintf("tx %d: %v%s",
r.inHeader.Unique, r.status, extraStr)
}
// setInput returns true if it takes ownership of the argument, false if not.
func (r *request) setInput(input []byte) bool {
if len(input) < len(r.smallInputBuf) {
copy(r.smallInputBuf[:], input)
r.inputBuf = r.smallInputBuf[:len(input)]
return false
}
r.inputBuf = input
r.bufferPoolInputBuf = input[:cap(input)]
return true
}
func (r *request) parseHeader() Status {
if len(r.inputBuf) < int(unsafe.Sizeof(InHeader{})) {
log.Printf("Short read for input header: %v", r.inputBuf)
return EINVAL
}
r.inHeader = (*InHeader)(unsafe.Pointer(&r.inputBuf[0]))
return OK
}
func (r *request) parse() {
r.arg = r.inputBuf[:]
r.handler = getHandler(r.inHeader.Opcode)
if r.handler == nil {
log.Printf("Unknown opcode %d", r.inHeader.Opcode)
r.status = ENOSYS
return
}
if len(r.arg) < int(r.handler.InputSize) {
log.Printf("Short read for %v: %v", operationName(r.inHeader.Opcode), r.arg)
r.status = EIO
return
}
if r.handler.InputSize > 0 {
r.inData = unsafe.Pointer(&r.arg[0])
r.arg = r.arg[r.handler.InputSize:]
} else {
r.arg = r.arg[unsafe.Sizeof(InHeader{}):]
}
count := r.handler.FileNames
if count > 0 {
if count == 1 && r.inHeader.Opcode == _OP_SETXATTR {
// SETXATTR is special: the only opcode with a file name AND a
// binary argument.
splits := bytes.SplitN(r.arg, []byte{0}, 2)
r.filenames = []string{string(splits[0])}
} else if count == 1 {
r.filenames = []string{string(r.arg[:len(r.arg)-1])}
} else {
names := bytes.SplitN(r.arg[:len(r.arg)-1], []byte{0}, count)
r.filenames = make([]string, len(names))
for i, n := range names {
r.filenames[i] = string(n)
}
if len(names) != count {
log.Println("filename argument mismatch", names, count)
r.status = EIO
}
}
}
copy(r.outBuf[:r.handler.OutputSize+sizeOfOutHeader],
zeroOutBuf[:r.handler.OutputSize+sizeOfOutHeader])
}
func (r *request) outData() unsafe.Pointer {
return unsafe.Pointer(&r.outBuf[sizeOfOutHeader])
}
// serializeHeader serializes the response header. The header points
// to an internal buffer of the receiver.
func (r *request) serializeHeader(flatDataSize int) (header []byte) {
var dataLength uintptr
if r.handler != nil {
dataLength = r.handler.OutputSize
}
if r.status > OK {
// only do this for positive status; negative status
// is used for notification.
dataLength = 0
}
// [GET|LIST]XATTR is two opcodes in one: get/list xattr size (return
// structured GetXAttrOut, no flat data) and get/list xattr data
// (return no structured data, but only flat data)
if r.inHeader.Opcode == _OP_GETXATTR || r.inHeader.Opcode == _OP_LISTXATTR {
if (*GetXAttrIn)(r.inData).Size != 0 {
dataLength = 0
}
}
header = r.outBuf[:sizeOfOutHeader+dataLength]
o := (*OutHeader)(unsafe.Pointer(&header[0]))
o.Unique = r.inHeader.Unique
o.Status = int32(-r.status)
o.Length = uint32(
int(sizeOfOutHeader) + int(dataLength) + flatDataSize)
return header
}
func (r *request) flatDataSize() int {
if r.fdData != nil {
return r.fdData.Size()
}
return len(r.flatData)
}