266 lines
6.1 KiB
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)
|
|
}
|