summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorCraig Chi <craigchi@google.com>2020-08-20 12:31:52 -0700
committerAndrei Vagin <avagin@gmail.com>2020-09-11 13:35:25 -0700
commit3bd85840c8f0364083c88d65c2bc1f968069b04e (patch)
tree44d2cd8c73c85edf2e24e734f35fc7ae85ea7f7b
parentaad7e25632ee972bd026c83b3881b2166175b4db (diff)
Support multiple FUSE kernel versions of FUSE_INIT response struct
The fuse_init_out struct changes in different FUSE kernel versions. A FUSE server may implement older versions of fuse_init_out, but they share common attributes from the beginning. Implement variable-length marshallable interface to support older versions of ABI. Fixes #3707
-rw-r--r--pkg/abi/linux/fuse.go58
-rw-r--r--pkg/sentry/fsimpl/fuse/connection.go5
-rw-r--r--pkg/sentry/fsimpl/fuse/init.go6
3 files changed, 66 insertions, 3 deletions
diff --git a/pkg/abi/linux/fuse.go b/pkg/abi/linux/fuse.go
index d105c5176..ca4ee5e80 100644
--- a/pkg/abi/linux/fuse.go
+++ b/pkg/abi/linux/fuse.go
@@ -15,6 +15,7 @@
package linux
import (
+ "gvisor.dev/gvisor/pkg/usermem"
"gvisor.dev/gvisor/tools/go_marshal/marshal"
"gvisor.dev/gvisor/tools/go_marshal/primitive"
)
@@ -262,6 +263,63 @@ type FUSEInitOut struct {
_ [8]uint32
}
+// FUSEInitRes is a variable-length wrapper of FUSEInitOut. The FUSE server may
+// implement older version of FUSE protocol, which contains a FUSEInitOut with
+// less attributes.
+//
+// Dynamically-sized objects cannot be marshalled.
+type FUSEInitRes struct {
+ marshal.StubMarshallable
+
+ // InitOut contains the response from the FUSE server.
+ InitOut FUSEInitOut
+
+ // Len is the total length of bytes of the response.
+ Len uint32
+}
+
+// UnMarshalBytes deserializes src to the InitOut attribute in a FUSEInitRes.
+func (r *FUSEInitRes) UnmarshalBytes(src []byte) {
+ out := &r.InitOut
+
+ // Introduced before FUSE kernel version 7.13.
+ out.Major = uint32(usermem.ByteOrder.Uint32(src[:4]))
+ src = src[4:]
+ out.Minor = uint32(usermem.ByteOrder.Uint32(src[:4]))
+ src = src[4:]
+ out.MaxReadahead = uint32(usermem.ByteOrder.Uint32(src[:4]))
+ src = src[4:]
+ out.Flags = uint32(usermem.ByteOrder.Uint32(src[:4]))
+ src = src[4:]
+ out.MaxBackground = uint16(usermem.ByteOrder.Uint16(src[:2]))
+ src = src[2:]
+ out.CongestionThreshold = uint16(usermem.ByteOrder.Uint16(src[:2]))
+ src = src[2:]
+ out.MaxWrite = uint32(usermem.ByteOrder.Uint32(src[:4]))
+ src = src[4:]
+
+ // Introduced in FUSE kernel version 7.23.
+ if len(src) >= 4 {
+ out.TimeGran = uint32(usermem.ByteOrder.Uint32(src[:4]))
+ src = src[4:]
+ }
+ // Introduced in FUSE kernel version 7.28.
+ if len(src) >= 2 {
+ out.MaxPages = uint16(usermem.ByteOrder.Uint16(src[:2]))
+ src = src[2:]
+ }
+ // Introduced in FUSE kernel version 7.31.
+ if len(src) >= 2 {
+ out.MapAlignment = uint16(usermem.ByteOrder.Uint16(src[:2]))
+ src = src[2:]
+ }
+}
+
+// SizeBytes is the size of the payload of the FUSE_INIT response.
+func (r *FUSEInitRes) SizeBytes() int {
+ return int(r.Len)
+}
+
// FUSEGetAttrIn is the request sent by the kernel to the daemon,
// to get the attribute of a inode.
//
diff --git a/pkg/sentry/fsimpl/fuse/connection.go b/pkg/sentry/fsimpl/fuse/connection.go
index f7d1a5c52..0e91bb18e 100644
--- a/pkg/sentry/fsimpl/fuse/connection.go
+++ b/pkg/sentry/fsimpl/fuse/connection.go
@@ -345,6 +345,11 @@ func (r *Response) Error() error {
return error(sysErrNo)
}
+// DataLen returns the size of the response without the header.
+func (r *Response) DataLen() uint32 {
+ return r.hdr.Len - uint32(r.hdr.SizeBytes())
+}
+
// UnmarshalPayload unmarshals the response data into m.
func (r *Response) UnmarshalPayload(m marshal.Marshallable) error {
hdrLen := r.hdr.SizeBytes()
diff --git a/pkg/sentry/fsimpl/fuse/init.go b/pkg/sentry/fsimpl/fuse/init.go
index 2ff2542b6..6384cbbdb 100644
--- a/pkg/sentry/fsimpl/fuse/init.go
+++ b/pkg/sentry/fsimpl/fuse/init.go
@@ -76,12 +76,12 @@ func (conn *connection) InitRecv(res *Response, hasSysAdminCap bool) error {
return err
}
- var out linux.FUSEInitOut
- if err := res.UnmarshalPayload(&out); err != nil {
+ initRes := linux.FUSEInitRes{Len: res.DataLen()}
+ if err := res.UnmarshalPayload(&initRes); err != nil {
return err
}
- return conn.initProcessReply(&out, hasSysAdminCap)
+ return conn.initProcessReply(&initRes.InitOut, hasSysAdminCap)
}
// Process the FUSE_INIT reply from the FUSE server.