github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/fsimpl/fuse/request_response.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package fuse
    16  
    17  import (
    18  	"golang.org/x/sys/unix"
    19  	"github.com/nicocha30/gvisor-ligolo/pkg/abi/linux"
    20  	"github.com/nicocha30/gvisor-ligolo/pkg/context"
    21  	"github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr"
    22  	"github.com/nicocha30/gvisor-ligolo/pkg/hostarch"
    23  	"github.com/nicocha30/gvisor-ligolo/pkg/log"
    24  	"github.com/nicocha30/gvisor-ligolo/pkg/marshal"
    25  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/auth"
    26  )
    27  
    28  // fuseInitRes is a variable-length wrapper of linux.FUSEInitOut. The FUSE
    29  // server may implement an older version of FUSE protocol, which contains a
    30  // linux.FUSEInitOut with less attributes.
    31  //
    32  // +marshal dynamic
    33  type fuseInitRes struct {
    34  	// initOut contains the response from the FUSE server.
    35  	initOut linux.FUSEInitOut
    36  
    37  	// initLen is the total length of bytes of the response.
    38  	initLen uint32
    39  }
    40  
    41  func (r *fuseInitRes) MarshalBytes(src []byte) []byte {
    42  	panic("Unimplemented, fuseInitRes should never be marshalled")
    43  }
    44  
    45  // UnmarshalBytes deserializes src to the initOut attribute in a fuseInitRes.
    46  func (r *fuseInitRes) UnmarshalBytes(src []byte) []byte {
    47  	out := &r.initOut
    48  
    49  	// Introduced before FUSE kernel version 7.13.
    50  	out.Major = uint32(hostarch.ByteOrder.Uint32(src[:4]))
    51  	src = src[4:]
    52  	out.Minor = uint32(hostarch.ByteOrder.Uint32(src[:4]))
    53  	src = src[4:]
    54  	out.MaxReadahead = uint32(hostarch.ByteOrder.Uint32(src[:4]))
    55  	src = src[4:]
    56  	out.Flags = uint32(hostarch.ByteOrder.Uint32(src[:4]))
    57  	src = src[4:]
    58  	out.MaxBackground = uint16(hostarch.ByteOrder.Uint16(src[:2]))
    59  	src = src[2:]
    60  	out.CongestionThreshold = uint16(hostarch.ByteOrder.Uint16(src[:2]))
    61  	src = src[2:]
    62  	out.MaxWrite = uint32(hostarch.ByteOrder.Uint32(src[:4]))
    63  	src = src[4:]
    64  
    65  	// Introduced in FUSE kernel version 7.23.
    66  	if len(src) >= 4 {
    67  		out.TimeGran = uint32(hostarch.ByteOrder.Uint32(src[:4]))
    68  		src = src[4:]
    69  	}
    70  	// Introduced in FUSE kernel version 7.28.
    71  	if len(src) >= 2 {
    72  		out.MaxPages = uint16(hostarch.ByteOrder.Uint16(src[:2]))
    73  		src = src[2:]
    74  	}
    75  	return src
    76  }
    77  
    78  // SizeBytes is the size of the payload of the FUSE_INIT response.
    79  func (r *fuseInitRes) SizeBytes() int {
    80  	return int(r.initLen)
    81  }
    82  
    83  // Ordinary requests have even IDs, while interrupts IDs are odd.
    84  // Used to increment the unique ID for each FUSE request.
    85  var reqIDStep uint64 = 2
    86  
    87  // Request represents a FUSE operation request that hasn't been sent to the
    88  // server yet.
    89  //
    90  // +stateify savable
    91  type Request struct {
    92  	requestEntry
    93  
    94  	id   linux.FUSEOpID
    95  	hdr  *linux.FUSEHeaderIn
    96  	data []byte
    97  
    98  	// If this request is async.
    99  	async bool
   100  	// If we don't care its response.
   101  	// Manually set by the caller.
   102  	noReply bool
   103  }
   104  
   105  // NewRequest creates a new request that can be sent to the FUSE server.
   106  func (conn *connection) NewRequest(creds *auth.Credentials, pid uint32, ino uint64, opcode linux.FUSEOpcode, payload marshal.Marshallable) *Request {
   107  	conn.fd.mu.Lock()
   108  	defer conn.fd.mu.Unlock()
   109  	conn.fd.nextOpID += linux.FUSEOpID(reqIDStep)
   110  
   111  	hdr := linux.FUSEHeaderIn{
   112  		Len:    linux.SizeOfFUSEHeaderIn + uint32(payload.SizeBytes()),
   113  		Opcode: opcode,
   114  		Unique: conn.fd.nextOpID,
   115  		NodeID: ino,
   116  		UID:    uint32(creds.EffectiveKUID),
   117  		GID:    uint32(creds.EffectiveKGID),
   118  		PID:    pid,
   119  	}
   120  
   121  	buf := make([]byte, hdr.Len)
   122  
   123  	hdr.MarshalUnsafe(buf[:linux.SizeOfFUSEHeaderIn])
   124  	payload.MarshalUnsafe(buf[linux.SizeOfFUSEHeaderIn:])
   125  
   126  	return &Request{
   127  		id:   hdr.Unique,
   128  		hdr:  &hdr,
   129  		data: buf,
   130  	}
   131  }
   132  
   133  // futureResponse represents an in-flight request, that may or may not have
   134  // completed yet. Convert it to a resolved Response by calling Resolve, but note
   135  // that this may block.
   136  //
   137  // +stateify savable
   138  type futureResponse struct {
   139  	opcode linux.FUSEOpcode
   140  	ch     chan struct{}
   141  	hdr    *linux.FUSEHeaderOut
   142  	data   []byte
   143  
   144  	// If this request is async.
   145  	async bool
   146  }
   147  
   148  // newFutureResponse creates a future response to a FUSE request.
   149  func newFutureResponse(req *Request) *futureResponse {
   150  	return &futureResponse{
   151  		opcode: req.hdr.Opcode,
   152  		ch:     make(chan struct{}),
   153  		async:  req.async,
   154  	}
   155  }
   156  
   157  // resolve blocks the task until the server responds to its corresponding request,
   158  // then returns a resolved response.
   159  func (f *futureResponse) resolve(b context.Blocker) (*Response, error) {
   160  	// Return directly for async requests.
   161  	if f.async {
   162  		return nil, nil
   163  	}
   164  
   165  	if err := b.Block(f.ch); err != nil {
   166  		return nil, err
   167  	}
   168  
   169  	return f.getResponse(), nil
   170  }
   171  
   172  // getResponse creates a Response from the data the futureResponse has.
   173  func (f *futureResponse) getResponse() *Response {
   174  	return &Response{
   175  		opcode: f.opcode,
   176  		hdr:    *f.hdr,
   177  		data:   f.data,
   178  	}
   179  }
   180  
   181  // Response represents an actual response from the server, including the
   182  // response payload.
   183  //
   184  // +stateify savable
   185  type Response struct {
   186  	opcode linux.FUSEOpcode
   187  	hdr    linux.FUSEHeaderOut
   188  	data   []byte
   189  }
   190  
   191  // Error returns the error of the FUSE call.
   192  func (r *Response) Error() error {
   193  	errno := r.hdr.Error
   194  	if errno >= 0 {
   195  		return nil
   196  	}
   197  
   198  	sysErrNo := unix.Errno(-errno)
   199  	return error(sysErrNo)
   200  }
   201  
   202  // DataLen returns the size of the response without the header.
   203  func (r *Response) DataLen() uint32 {
   204  	return r.hdr.Len - uint32(r.hdr.SizeBytes())
   205  }
   206  
   207  // UnmarshalPayload unmarshals the response data into m.
   208  func (r *Response) UnmarshalPayload(m marshal.Marshallable) error {
   209  	hdrLen := r.hdr.SizeBytes()
   210  	haveDataLen := r.hdr.Len - uint32(hdrLen)
   211  	wantDataLen := uint32(m.SizeBytes())
   212  
   213  	if haveDataLen < wantDataLen {
   214  		log.Warningf("fusefs: Payload too small. Minimum data length required: %d, but got data length %d", wantDataLen, haveDataLen)
   215  		return linuxerr.EINVAL
   216  
   217  	}
   218  
   219  	// The response data is empty unless there is some payload. And so, doesn't
   220  	// need to be unmarshalled.
   221  	if r.data == nil {
   222  		return nil
   223  	}
   224  
   225  	m.UnmarshalUnsafe(r.data[hdrLen:])
   226  	return nil
   227  }