diff options
author | Craig Chi <craigchi@google.com> | 2020-08-20 12:31:52 -0700 |
---|---|---|
committer | Andrei Vagin <avagin@gmail.com> | 2020-09-11 13:35:25 -0700 |
commit | 3bd85840c8f0364083c88d65c2bc1f968069b04e (patch) | |
tree | 44d2cd8c73c85edf2e24e734f35fc7ae85ea7f7b /pkg | |
parent | aad7e25632ee972bd026c83b3881b2166175b4db (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
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/abi/linux/fuse.go | 58 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/fuse/connection.go | 5 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/fuse/init.go | 6 |
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. |