Commit d87cbd06 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Move request type into separate file.

parent 00e91a48
......@@ -4,6 +4,7 @@ include $(GOROOT)/src/Make.inc
TARG=github.com/hanwen/go-fuse/fuse
GOFILES=misc.go\
request.go \
fuse.go\
direntry.go\
mount.go\
......
// Code that handles the control loop, and en/decoding messages
// to/from the kernel. Dispatches calls into RawFileSystem.
package fuse
import (
"bytes"
"fmt"
"log"
"os"
"reflect"
"syscall"
"time"
"unsafe"
)
// TODO make generic option setting.
const (
// bufSize should be a power of two to minimize lossage in
// BufferPool.
......@@ -22,83 +14,8 @@ const (
maxRead = bufSize - PAGESIZE
)
type request struct {
inputBuf []byte
// These split up inputBuf.
inHeader *InHeader // generic header
inData unsafe.Pointer // per op data
arg []byte // flat data.
// Unstructured data, a pointer to the relevant XxxxOut struct.
outData unsafe.Pointer
status Status
flatData []byte
// Header + structured data for what we send back to the kernel.
// May be followed by flatData.
outHeaderBytes []byte
// Start timestamp for timing info.
startNs int64
preWriteNs int64
}
func (me *request) filename() string {
return string(me.arg[:len(me.arg)-1])
}
func (me *request) filenames(count int) []string {
names := bytes.Split(me.arg[:len(me.arg)-1], []byte{0}, count)
nameStrings := make([]string, len(names))
for i, n := range names {
nameStrings[i] = string(n)
}
return nameStrings
}
func (me *request) InputDebug(h *operationHandler) string {
val := " "
if h.DecodeIn != nil {
val = fmt.Sprintf(" data: %v ", h.DecodeIn(me.inData))
}
names := ""
if h.FileNames > 0 {
names = fmt.Sprintf("names: %v", me.filenames(h.FileNames))
}
return fmt.Sprintf("Dispatch: %v, NodeId: %v.%v%v",
me.inHeader.opcode, me.inHeader.NodeId, val, names)
}
func (me *request) OutputDebug(h *operationHandler) string {
var val interface{}
if h.DecodeOut != nil {
val = h.DecodeOut(me.outData)
}
dataStr := ""
if val != nil {
dataStr = fmt.Sprintf("%v", val)
}
max := 1024
if len(dataStr) > max {
dataStr = dataStr[:max] + fmt.Sprintf(" ...trimmed (response size %d)", len(me.outHeaderBytes))
}
flatStr := ""
if len(me.flatData) > 0 {
flatStr = fmt.Sprintf(" %d bytes data\n", len(me.flatData))
}
return fmt.Sprintf("Serialize: %v code: %v value: %v%v",
me.inHeader.opcode, me.status, dataStr, flatStr)
}
////////////////////////////////////////////////////////////////
// State related to this mount point.
// MountState contains the logic for reading from the FUSE device and
// translating it to RawFileSystem interface calls.
type MountState struct {
// Empty if unmounted.
mountPoint string
......@@ -193,9 +110,6 @@ func (me *MountState) BufferPoolStats() string {
return me.buffers.String()
}
////////////////////////////////////////////////////////////////
// Logic for the control loop.
func (me *MountState) newRequest(oldReq *request) *request {
if oldReq != nil {
me.buffers.FreeBuffer(oldReq.flatData)
......@@ -224,7 +138,7 @@ func (me *MountState) readRequest(req *request) os.Error {
return err
}
func (me *MountState) discardRequest(req *request) {
func (me *MountState) recordStats(req *request) {
if me.LatencyMap != nil {
endNs := time.Nanoseconds()
dt := endNs - req.startNs
......@@ -237,8 +151,9 @@ func (me *MountState) discardRequest(req *request) {
}
}
// Normally, callers should run Loop() and wait for FUSE to exit, but
// tests will want to run this in a goroutine.
// Loop initiates the FUSE loop. Normally, callers should run Loop()
// and wait for it to exit, but tests will want to run this in a
// goroutine.
//
// If threaded is given, each filesystem operation executes in a
// separate goroutine.
......@@ -256,7 +171,6 @@ func (me *MountState) Loop(threaded bool) {
}
func (me *MountState) loop() {
// See fuse_kern_chan_receive()
var lastReq *request
for {
req := me.newRequest(lastReq)
......@@ -267,7 +181,6 @@ func (me *MountState) loop() {
// Retry.
if errNo == syscall.ENOENT {
me.discardRequest(req)
continue
}
......@@ -284,95 +197,33 @@ func (me *MountState) loop() {
log.Printf("Failed to read from fuse conn: %v", err)
break
}
me.handle(req)
me.handleRequest(req)
}
}
func (me *MountState) handleRequest(req *request) {
defer me.recordStats(req)
func (me *MountState) chopMessage(req *request) *operationHandler {
inHSize := unsafe.Sizeof(InHeader{})
if len(req.inputBuf) < inHSize {
log.Printf("Short read for input header: %v", req.inputBuf)
return nil
}
req.inHeader = (*InHeader)(unsafe.Pointer(&req.inputBuf[0]))
req.arg = req.inputBuf[inHSize:]
handler := getHandler(req.inHeader.opcode)
if handler == nil || handler.Func == nil {
msg := "Unimplemented"
if handler == nil {
msg = "Unknown"
}
log.Printf("%s opcode %v", msg, req.inHeader.opcode)
req.status = ENOSYS
return handler
}
if len(req.arg) < handler.InputSize {
log.Printf("Short read for %v: %v", req.inHeader.opcode, req.arg)
req.status = EIO
return handler
}
if handler.InputSize > 0 {
req.inData = unsafe.Pointer(&req.arg[0])
req.arg = req.arg[handler.InputSize:]
}
return handler
}
func (me *MountState) handle(req *request) {
defer me.discardRequest(req)
handler := me.chopMessage(req)
if handler == nil {
req.parse()
if req.handler == nil {
return
}
if req.status == OK {
me.dispatch(req, handler)
if me.Debug {
log.Println(req.InputDebug())
}
req.handler.Func(me, req)
}
// If we try to write OK, nil, we will get
// error: writer: Writev [[16 0 0 0 0 0 0 0 17 0 0 0 0 0 0 0]]
// failed, err: writev: no such file or directory
if req.inHeader.opcode != _OP_FORGET {
serialize(req, handler, me.Debug)
me.Write(req)
}
}
func (me *MountState) dispatch(req *request, handler *operationHandler) {
req.serialize()
if me.Debug {
log.Println(req.InputDebug(handler))
}
handler.Func(me, req)
}
// Thanks to Andrew Gerrand for this hack.
func asSlice(ptr unsafe.Pointer, byteCount int) []byte {
h := &reflect.SliceHeader{uintptr(ptr), byteCount, byteCount}
return *(*[]byte)(unsafe.Pointer(h))
}
func serialize(req *request, handler *operationHandler, debug bool) {
dataLength := handler.OutputSize
if req.outData == nil || req.status != OK {
dataLength = 0
log.Println(req.OutputDebug())
}
sizeOfOutHeader := unsafe.Sizeof(OutHeader{})
req.outHeaderBytes = make([]byte, sizeOfOutHeader+dataLength)
outHeader := (*OutHeader)(unsafe.Pointer(&req.outHeaderBytes[0]))
outHeader.Unique = req.inHeader.Unique
outHeader.Status = -req.status
outHeader.Length = uint32(sizeOfOutHeader + dataLength + len(req.flatData))
copy(req.outHeaderBytes[sizeOfOutHeader:], asSlice(req.outData, dataLength))
if debug {
log.Println(req.OutputDebug(handler))
me.Write(req)
}
}
......@@ -7,6 +7,7 @@ import (
"fmt"
"log"
"math"
"reflect"
"regexp"
"syscall"
"unsafe"
......@@ -147,3 +148,9 @@ func MyPID() string {
v, _ := os.Readlink("/proc/self")
return v
}
// Thanks to Andrew Gerrand for this hack.
func asSlice(ptr unsafe.Pointer, byteCount int) []byte {
h := &reflect.SliceHeader{uintptr(ptr), byteCount, byteCount}
return *(*[]byte)(unsafe.Pointer(h))
}
package fuse
import (
"fmt"
"bytes"
"log"
"unsafe"
)
type request struct {
inputBuf []byte
// These split up inputBuf.
inHeader *InHeader // generic header
inData unsafe.Pointer // per op data
arg []byte // flat data.
// Unstructured data, a pointer to the relevant XxxxOut struct.
outData unsafe.Pointer
status Status
flatData []byte
// Header + structured data for what we send back to the kernel.
// May be followed by flatData.
outHeaderBytes []byte
// Start timestamp for timing info.
startNs int64
preWriteNs int64
// All information pertaining to opcode of this request.
handler *operationHandler
}
func (me *request) filename() string {
return string(me.arg[:len(me.arg)-1])
}
func (me *request) filenames(count int) []string {
names := bytes.Split(me.arg[:len(me.arg)-1], []byte{0}, count)
nameStrings := make([]string, len(names))
for i, n := range names {
nameStrings[i] = string(n)
}
return nameStrings
}
func (me *request) InputDebug() string {
val := " "
if me.handler.DecodeIn != nil {
val = fmt.Sprintf(" data: %v ", me.handler.DecodeIn(me.inData))
}
names := ""
if me.handler.FileNames > 0 {
names = fmt.Sprintf("names: %v", me.filenames(me.handler.FileNames))
}
return fmt.Sprintf("Dispatch: %v, NodeId: %v.%v%v",
me.inHeader.opcode, me.inHeader.NodeId, val, names)
}
func (me *request) OutputDebug() string {
var val interface{}
if me.handler.DecodeOut != nil {
val = me.handler.DecodeOut(me.outData)
}
dataStr := ""
if val != nil {
dataStr = fmt.Sprintf("%v", val)
}
max := 1024
if len(dataStr) > max {
dataStr = dataStr[:max] + fmt.Sprintf(" ...trimmed (response size %d)", len(me.outHeaderBytes))
}
flatStr := ""
if len(me.flatData) > 0 {
flatStr = fmt.Sprintf(" %d bytes data\n", len(me.flatData))
}
return fmt.Sprintf("Serialize: %v code: %v value: %v%v",
me.inHeader.opcode, me.status, dataStr, flatStr)
}
func (req *request) parse() {
inHSize := unsafe.Sizeof(InHeader{})
if len(req.inputBuf) < inHSize {
log.Printf("Short read for input header: %v", req.inputBuf)
return
}
req.inHeader = (*InHeader)(unsafe.Pointer(&req.inputBuf[0]))
req.arg = req.inputBuf[inHSize:]
req.handler = getHandler(req.inHeader.opcode)
if req.handler == nil || req.handler.Func == nil {
msg := "Unimplemented"
if req.handler == nil {
msg = "Unknown"
}
log.Printf("%s opcode %v", msg, req.inHeader.opcode)
req.status = ENOSYS
return
}
if len(req.arg) < req.handler.InputSize {
log.Printf("Short read for %v: %v", req.inHeader.opcode, req.arg)
req.status = EIO
return
}
if req.handler.InputSize > 0 {
req.inData = unsafe.Pointer(&req.arg[0])
req.arg = req.arg[req.handler.InputSize:]
}
}
func (req *request) serialize() {
dataLength := req.handler.OutputSize
if req.outData == nil || req.status != OK {
dataLength = 0
}
sizeOfOutHeader := unsafe.Sizeof(OutHeader{})
req.outHeaderBytes = make([]byte, sizeOfOutHeader+dataLength)
outHeader := (*OutHeader)(unsafe.Pointer(&req.outHeaderBytes[0]))
outHeader.Unique = req.inHeader.Unique
outHeader.Status = -req.status
outHeader.Length = uint32(sizeOfOutHeader + dataLength + len(req.flatData))
copy(req.outHeaderBytes[sizeOfOutHeader:], asSlice(req.outData, dataLength))
}
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