github.com/hanwen/go-fuse@v1.0.0/fuse/request.go (about)

     1  // Copyright 2016 the Go-FUSE Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package fuse
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"log"
    11  	"strings"
    12  	"time"
    13  	"unsafe"
    14  )
    15  
    16  var sizeOfOutHeader = unsafe.Sizeof(OutHeader{})
    17  var zeroOutBuf [outputHeaderSize]byte
    18  
    19  type request struct {
    20  	inputBuf []byte
    21  
    22  	// These split up inputBuf.
    23  	inHeader *InHeader      // generic header
    24  	inData   unsafe.Pointer // per op data
    25  	arg      []byte         // flat data.
    26  
    27  	filenames []string // filename arguments
    28  
    29  	// Output data.
    30  	status   Status
    31  	flatData []byte
    32  	fdData   *readResultFd
    33  
    34  	// In case of read, keep read result here so we can call
    35  	// Done() on it.
    36  	readResult ReadResult
    37  
    38  	// Start timestamp for timing info.
    39  	startTime time.Time
    40  
    41  	// All information pertaining to opcode of this request.
    42  	handler *operationHandler
    43  
    44  	// Request storage. For large inputs and outputs, use data
    45  	// obtained through bufferpool.
    46  	bufferPoolInputBuf  []byte
    47  	bufferPoolOutputBuf []byte
    48  
    49  	// For small pieces of data, we use the following inlines
    50  	// arrays:
    51  	//
    52  	// Output header and structured data.
    53  	outBuf [outputHeaderSize]byte
    54  
    55  	// Input, if small enough to fit here.
    56  	smallInputBuf [128]byte
    57  
    58  	context Context
    59  }
    60  
    61  func (r *request) clear() {
    62  	r.inputBuf = nil
    63  	r.inHeader = nil
    64  	r.inData = nil
    65  	r.arg = nil
    66  	r.filenames = nil
    67  	r.status = OK
    68  	r.flatData = nil
    69  	r.fdData = nil
    70  	r.startTime = time.Time{}
    71  	r.handler = nil
    72  	r.readResult = nil
    73  }
    74  
    75  func (r *request) InputDebug() string {
    76  	val := ""
    77  	if r.handler.DecodeIn != nil {
    78  		val = fmt.Sprintf("%v ", Print(r.handler.DecodeIn(r.inData)))
    79  	}
    80  
    81  	names := ""
    82  	if r.filenames != nil {
    83  		names = fmt.Sprintf("%q", r.filenames)
    84  	}
    85  
    86  	if len(r.arg) > 0 {
    87  		names += fmt.Sprintf(" %db", len(r.arg))
    88  	}
    89  
    90  	return fmt.Sprintf("rx %d: %s i%d %s%s",
    91  		r.inHeader.Unique, operationName(r.inHeader.Opcode), r.inHeader.NodeId,
    92  		val, names)
    93  }
    94  
    95  func (r *request) OutputDebug() string {
    96  	var dataStr string
    97  	if r.handler.DecodeOut != nil && r.handler.OutputSize > 0 {
    98  		dataStr = Print(r.handler.DecodeOut(r.outData()))
    99  	}
   100  
   101  	max := 1024
   102  	if len(dataStr) > max {
   103  		dataStr = dataStr[:max] + fmt.Sprintf(" ...trimmed")
   104  	}
   105  
   106  	flatStr := ""
   107  	if r.flatDataSize() > 0 {
   108  		if r.handler.FileNameOut {
   109  			s := strings.TrimRight(string(r.flatData), "\x00")
   110  			flatStr = fmt.Sprintf(" %q", s)
   111  		} else {
   112  			spl := ""
   113  			if r.fdData != nil {
   114  				spl = " (fd data)"
   115  			} else {
   116  				l := len(r.flatData)
   117  				s := ""
   118  				if l > 8 {
   119  					l = 8
   120  					s = "..."
   121  				}
   122  				spl = fmt.Sprintf(" %q%s", r.flatData[:l], s)
   123  			}
   124  			flatStr = fmt.Sprintf(" %db data%s", r.flatDataSize(), spl)
   125  		}
   126  	}
   127  
   128  	extraStr := dataStr + flatStr
   129  	if extraStr != "" {
   130  		extraStr = ", " + extraStr
   131  	}
   132  	return fmt.Sprintf("tx %d:     %v%s",
   133  		r.inHeader.Unique, r.status, extraStr)
   134  }
   135  
   136  // setInput returns true if it takes ownership of the argument, false if not.
   137  func (r *request) setInput(input []byte) bool {
   138  	if len(input) < len(r.smallInputBuf) {
   139  		copy(r.smallInputBuf[:], input)
   140  		r.inputBuf = r.smallInputBuf[:len(input)]
   141  		return false
   142  	}
   143  	r.inputBuf = input
   144  	r.bufferPoolInputBuf = input[:cap(input)]
   145  
   146  	return true
   147  }
   148  
   149  func (r *request) parse() {
   150  	inHSize := int(unsafe.Sizeof(InHeader{}))
   151  	if len(r.inputBuf) < inHSize {
   152  		log.Printf("Short read for input header: %v", r.inputBuf)
   153  		return
   154  	}
   155  
   156  	r.inHeader = (*InHeader)(unsafe.Pointer(&r.inputBuf[0]))
   157  	r.arg = r.inputBuf[:]
   158  
   159  	r.handler = getHandler(r.inHeader.Opcode)
   160  	if r.handler == nil {
   161  		log.Printf("Unknown opcode %d", r.inHeader.Opcode)
   162  		r.status = ENOSYS
   163  		return
   164  	}
   165  
   166  	if len(r.arg) < int(r.handler.InputSize) {
   167  		log.Printf("Short read for %v: %v", operationName(r.inHeader.Opcode), r.arg)
   168  		r.status = EIO
   169  		return
   170  	}
   171  
   172  	if r.handler.InputSize > 0 {
   173  		r.inData = unsafe.Pointer(&r.arg[0])
   174  		r.arg = r.arg[r.handler.InputSize:]
   175  	} else {
   176  		r.arg = r.arg[inHSize:]
   177  	}
   178  
   179  	count := r.handler.FileNames
   180  	if count > 0 {
   181  		if count == 1 && r.inHeader.Opcode == _OP_SETXATTR {
   182  			// SETXATTR is special: the only opcode with a file name AND a
   183  			// binary argument.
   184  			splits := bytes.SplitN(r.arg, []byte{0}, 2)
   185  			r.filenames = []string{string(splits[0])}
   186  		} else if count == 1 {
   187  			r.filenames = []string{string(r.arg[:len(r.arg)-1])}
   188  		} else {
   189  			names := bytes.SplitN(r.arg[:len(r.arg)-1], []byte{0}, count)
   190  			r.filenames = make([]string, len(names))
   191  			for i, n := range names {
   192  				r.filenames[i] = string(n)
   193  			}
   194  			if len(names) != count {
   195  				log.Println("filename argument mismatch", names, count)
   196  				r.status = EIO
   197  			}
   198  		}
   199  	}
   200  
   201  	copy(r.outBuf[:r.handler.OutputSize+sizeOfOutHeader],
   202  		zeroOutBuf[:r.handler.OutputSize+sizeOfOutHeader])
   203  }
   204  
   205  func (r *request) outData() unsafe.Pointer {
   206  	return unsafe.Pointer(&r.outBuf[sizeOfOutHeader])
   207  }
   208  
   209  // serializeHeader serializes the response header. The header points
   210  // to an internal buffer of the receiver.
   211  func (r *request) serializeHeader(flatDataSize int) (header []byte) {
   212  	dataLength := r.handler.OutputSize
   213  	if r.status > OK {
   214  		dataLength = 0
   215  	}
   216  
   217  	// [GET|LIST]XATTR is two opcodes in one: get/list xattr size (return
   218  	// structured GetXAttrOut, no flat data) and get/list xattr data
   219  	// (return no structured data, but only flat data)
   220  	if r.inHeader.Opcode == _OP_GETXATTR || r.inHeader.Opcode == _OP_LISTXATTR {
   221  		if (*GetXAttrIn)(r.inData).Size != 0 {
   222  			dataLength = 0
   223  		}
   224  	}
   225  
   226  	header = r.outBuf[:sizeOfOutHeader+dataLength]
   227  	o := (*OutHeader)(unsafe.Pointer(&header[0]))
   228  	o.Unique = r.inHeader.Unique
   229  	o.Status = int32(-r.status)
   230  	o.Length = uint32(
   231  		int(sizeOfOutHeader) + int(dataLength) + flatDataSize)
   232  	return header
   233  }
   234  
   235  func (r *request) flatDataSize() int {
   236  	if r.fdData != nil {
   237  		return r.fdData.Size()
   238  	}
   239  	return len(r.flatData)
   240  }