mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Move HostConnectedEndpoint and SCMConnectedEndpoint to transport package.
This is needed so that connectioned endpoint in the transport package can use this endpoint to implement host FD based binded endpoints. I had to simplify some other dependencies to make this possible. - Removed uniqueid's dependency on transport package completely. - Removed SCMConnectedEndpoint and HostConnectedEndpoint's dependency on control package so they could be moved to transport. control already depends on transport. - scmRights struct from fsimpl/host/control.go had to be moved into transport so that HostConnectedEndpoint could be implemented. But scmRights.Fill() could not be moved because it inherently depends on making vfs.FileDescriptions which depends on vfs which in turn depends on transport. So now that scmRights -> vfs.FD conversion happens in the syscall package. PiperOrigin-RevId: 413839350
This commit is contained in:
@@ -156,9 +156,7 @@ func NewSocketWithDirent(ctx context.Context, d *fs.Dirent, f *fd.FD, flags fs.F
|
||||
f.Release()
|
||||
|
||||
e.Init()
|
||||
|
||||
ep := transport.NewExternal(ctx, e.stype, uniqueid.GlobalProviderFromContext(ctx), &q, e, e)
|
||||
|
||||
ep := transport.NewExternal(e.stype, uniqueid.GlobalProviderFromContext(ctx), &q, e, e)
|
||||
return unixsocket.NewWithDirent(ctx, d, ep, e.stype, flags), nil
|
||||
}
|
||||
|
||||
@@ -188,9 +186,7 @@ func newSocket(ctx context.Context, orgfd int, saveable bool) (*fs.File, error)
|
||||
|
||||
e.srfd = srfd
|
||||
e.Init()
|
||||
|
||||
ep := transport.NewExternal(ctx, e.stype, uniqueid.GlobalProviderFromContext(ctx), &q, e, e)
|
||||
|
||||
ep := transport.NewExternal(e.stype, uniqueid.GlobalProviderFromContext(ctx), &q, e, e)
|
||||
return unixsocket.New(ctx, ep, e.stype), nil
|
||||
}
|
||||
|
||||
@@ -397,4 +393,4 @@ func (c *ConnectedEndpoint) SetReceiveBufferSize(v int64) (newSz int64) {
|
||||
return atomic.LoadInt64(&c.sndbuf)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../fsimpl/host/socket.go)
|
||||
// LINT.ThenChange(../../socket/unix/transport/host.go)
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/p9"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/host"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
@@ -106,14 +105,14 @@ func (e *endpoint) UnidirectionalConnect(ctx context.Context) (transport.Connect
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (e *endpoint) newConnectedEndpoint(ctx context.Context, sockType linux.SockType, queue *waiter.Queue) (*host.SCMConnectedEndpoint, *syserr.Error) {
|
||||
func (e *endpoint) newConnectedEndpoint(ctx context.Context, sockType linux.SockType, queue *waiter.Queue) (*transport.SCMConnectedEndpoint, *syserr.Error) {
|
||||
if e.dentry.fs.opts.lisaEnabled {
|
||||
hostSockFD, err := e.dentry.controlFDLisa.Connect(ctx, sockType)
|
||||
if err != nil {
|
||||
return nil, syserr.ErrConnectionRefused
|
||||
}
|
||||
|
||||
c, serr := host.NewSCMEndpoint(ctx, hostSockFD, queue, e.path)
|
||||
c, serr := transport.NewSCMEndpoint(hostSockFD, queue, e.path)
|
||||
if serr != nil {
|
||||
unix.Close(hostSockFD)
|
||||
log.Warningf("Gofer returned invalid host socket for BidirectionalConnect; file %+v sockType %d: %v", e.dentry.file, sockType, serr)
|
||||
@@ -131,7 +130,7 @@ func (e *endpoint) newConnectedEndpoint(ctx context.Context, sockType linux.Sock
|
||||
return nil, syserr.ErrConnectionRefused
|
||||
}
|
||||
|
||||
c, serr := host.NewSCMEndpoint(ctx, hostFile.FD(), queue, e.path)
|
||||
c, serr := transport.NewSCMEndpoint(hostFile.FD(), queue, e.path)
|
||||
if serr != nil {
|
||||
hostFile.Close()
|
||||
log.Warningf("Gofer returned invalid host socket for BidirectionalConnect; file %+v sockType %d: %v", e.dentry.file, sockType, serr)
|
||||
|
||||
@@ -14,30 +14,14 @@ go_template_instance(
|
||||
},
|
||||
)
|
||||
|
||||
go_template_instance(
|
||||
name = "connected_endpoint_refs",
|
||||
out = "connected_endpoint_refs.go",
|
||||
package = "host",
|
||||
prefix = "ConnectedEndpoint",
|
||||
template = "//pkg/refsvfs2:refs_template",
|
||||
types = {
|
||||
"T": "ConnectedEndpoint",
|
||||
},
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "host",
|
||||
srcs = [
|
||||
"connected_endpoint_refs.go",
|
||||
"control.go",
|
||||
"host.go",
|
||||
"host_unsafe.go",
|
||||
"inode_refs.go",
|
||||
"ioctl_unsafe.go",
|
||||
"save_restore.go",
|
||||
"socket.go",
|
||||
"socket_iovec.go",
|
||||
"socket_unsafe.go",
|
||||
"tty.go",
|
||||
"util.go",
|
||||
"util_unsafe.go",
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package host
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/control"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
type scmRights struct {
|
||||
fds []int
|
||||
}
|
||||
|
||||
func newSCMRights(fds []int) control.SCMRightsVFS2 {
|
||||
return &scmRights{fds}
|
||||
}
|
||||
|
||||
// Files implements control.SCMRights.Files.
|
||||
func (c *scmRights) Files(ctx context.Context, max int) (control.RightsFilesVFS2, bool) {
|
||||
n := max
|
||||
var trunc bool
|
||||
if l := len(c.fds); n > l {
|
||||
n = l
|
||||
} else if n < l {
|
||||
trunc = true
|
||||
}
|
||||
|
||||
rf := control.RightsFilesVFS2(fdsToFiles(ctx, c.fds[:n]))
|
||||
|
||||
// Only consume converted FDs (fdsToFiles may convert fewer than n FDs).
|
||||
c.fds = c.fds[len(rf):]
|
||||
return rf, trunc
|
||||
}
|
||||
|
||||
// Clone implements transport.RightsControlMessage.Clone.
|
||||
func (c *scmRights) Clone() transport.RightsControlMessage {
|
||||
// Host rights never need to be cloned.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Release implements transport.RightsControlMessage.Release.
|
||||
func (c *scmRights) Release(ctx context.Context) {
|
||||
for _, fd := range c.fds {
|
||||
unix.Close(fd)
|
||||
}
|
||||
c.fds = nil
|
||||
}
|
||||
|
||||
// If an error is encountered, only files created before the error will be
|
||||
// returned. This is what Linux does.
|
||||
func fdsToFiles(ctx context.Context, fds []int) []*vfs.FileDescription {
|
||||
files := make([]*vfs.FileDescription, 0, len(fds))
|
||||
for _, fd := range fds {
|
||||
// Get flags. We do it here because they may be modified
|
||||
// by subsequent functions.
|
||||
fileFlags, _, errno := unix.Syscall(unix.SYS_FCNTL, uintptr(fd), unix.F_GETFL, 0)
|
||||
if errno != 0 {
|
||||
ctx.Warningf("Error retrieving host FD flags: %v", error(errno))
|
||||
break
|
||||
}
|
||||
|
||||
// Create the file backed by hostFD.
|
||||
file, err := NewFD(ctx, kernel.KernelFromContext(ctx).HostMount(), fd, &NewFDOptions{})
|
||||
if err != nil {
|
||||
ctx.Warningf("Error creating file from host FD: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
if err := file.SetStatusFlags(ctx, auth.CredentialsFromContext(ctx), uint32(fileFlags&linux.O_NONBLOCK)); err != nil {
|
||||
ctx.Warningf("Error setting flags on host FD file: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
files = append(files, file)
|
||||
}
|
||||
return files
|
||||
}
|
||||
@@ -36,6 +36,8 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
unixsocket "gvisor.dev/gvisor/pkg/sentry/socket/unix"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
|
||||
"gvisor.dev/gvisor/pkg/sentry/uniqueid"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
@@ -627,6 +629,19 @@ func (i *inode) open(ctx context.Context, d *kernfs.Dentry, mnt *vfs.Mount, file
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new host-backed endpoint from the given fd and its corresponding
|
||||
// notification queue.
|
||||
func newEndpoint(ctx context.Context, hostFD int, queue *waiter.Queue) (transport.Endpoint, error) {
|
||||
// Set up an external transport.Endpoint using the host fd.
|
||||
addr := fmt.Sprintf("hostfd:[%d]", hostFD)
|
||||
e, err := transport.NewHostConnectedEndpoint(hostFD, addr)
|
||||
if err != nil {
|
||||
return nil, err.ToError()
|
||||
}
|
||||
ep := transport.NewExternal(e.SockType(), uniqueid.GlobalProviderFromContext(ctx), queue, e, e)
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
// fileDescription is embedded by host fd implementations of FileDescriptionImpl.
|
||||
//
|
||||
// +stateify savable
|
||||
|
||||
@@ -68,10 +68,3 @@ func (i *inode) afterLoad() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (c *ConnectedEndpoint) afterLoad() {
|
||||
if err := c.initFromOptions(); err != nil {
|
||||
panic(fmt.Sprintf("initFromOptions failed: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,11 +126,3 @@ func PackRightsVFS2(t *kernel.Task, rights SCMRightsVFS2, cloexec bool, buf []by
|
||||
align := t.Arch().Width()
|
||||
return putCmsg(buf, flags, linux.SCM_RIGHTS, align, fds)
|
||||
}
|
||||
|
||||
// NewVFS2 creates default control messages if needed.
|
||||
func NewVFS2(t *kernel.Task, socketOrEndpoint interface{}, rights SCMRightsVFS2) transport.ControlMessages {
|
||||
return transport.ControlMessages{
|
||||
Credentials: makeCreds(t, socketOrEndpoint),
|
||||
Rights: rights,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,17 @@ go_template_instance(
|
||||
},
|
||||
)
|
||||
|
||||
go_template_instance(
|
||||
name = "host_connected_endpoint_refs",
|
||||
out = "host_connected_endpoint_refs.go",
|
||||
package = "transport",
|
||||
prefix = "HostConnectedEndpoint",
|
||||
template = "//pkg/refsvfs2:refs_template",
|
||||
types = {
|
||||
"T": "HostConnectedEndpoint",
|
||||
},
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "transport",
|
||||
srcs = [
|
||||
@@ -33,8 +44,13 @@ go_library(
|
||||
"connectioned_state.go",
|
||||
"connectionless.go",
|
||||
"connectionless_state.go",
|
||||
"host.go",
|
||||
"host_connected_endpoint_refs.go",
|
||||
"host_iovec.go",
|
||||
"host_unsafe.go",
|
||||
"queue.go",
|
||||
"queue_refs.go",
|
||||
"save_restore.go",
|
||||
"transport_message_list.go",
|
||||
"unix.go",
|
||||
],
|
||||
@@ -42,15 +58,21 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/context",
|
||||
"//pkg/errors/linuxerr",
|
||||
"//pkg/fdnotifier",
|
||||
"//pkg/ilist",
|
||||
"//pkg/log",
|
||||
"//pkg/refs",
|
||||
"//pkg/refsvfs2",
|
||||
"//pkg/sentry/hostfd",
|
||||
"//pkg/sentry/inet",
|
||||
"//pkg/sentry/uniqueid",
|
||||
"//pkg/sync",
|
||||
"//pkg/syserr",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/buffer",
|
||||
"//pkg/unet",
|
||||
"//pkg/waiter",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -17,19 +17,13 @@ package transport
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/uniqueid"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// UniqueIDProvider generates a sequence of unique identifiers useful for,
|
||||
// among other things, lock ordering.
|
||||
type UniqueIDProvider interface {
|
||||
// UniqueID returns a new unique identifier.
|
||||
UniqueID() uint64
|
||||
}
|
||||
|
||||
// A ConnectingEndpoint is a connectioned unix endpoint that is attempting to
|
||||
// establish a bidirectional connection with a BoundEndpoint.
|
||||
type ConnectingEndpoint interface {
|
||||
@@ -96,7 +90,7 @@ type connectionedEndpoint struct {
|
||||
id uint64
|
||||
|
||||
// idGenerator is used to generate new unique endpoint identifiers.
|
||||
idGenerator UniqueIDProvider
|
||||
idGenerator uniqueid.Provider
|
||||
|
||||
// stype is used by connecting sockets to ensure that they are the
|
||||
// same type. The value is typically either tcpip.SockSeqpacket or
|
||||
@@ -117,11 +111,11 @@ var (
|
||||
)
|
||||
|
||||
// NewConnectioned creates a new unbound connectionedEndpoint.
|
||||
func NewConnectioned(ctx context.Context, stype linux.SockType, uid UniqueIDProvider) Endpoint {
|
||||
func NewConnectioned(ctx context.Context, stype linux.SockType, uid uniqueid.Provider) Endpoint {
|
||||
return newConnectioned(ctx, stype, uid)
|
||||
}
|
||||
|
||||
func newConnectioned(ctx context.Context, stype linux.SockType, uid UniqueIDProvider) *connectionedEndpoint {
|
||||
func newConnectioned(ctx context.Context, stype linux.SockType, uid uniqueid.Provider) *connectionedEndpoint {
|
||||
ep := &connectionedEndpoint{
|
||||
baseEndpoint: baseEndpoint{Queue: &waiter.Queue{}},
|
||||
id: uid.UniqueID(),
|
||||
@@ -136,7 +130,7 @@ func newConnectioned(ctx context.Context, stype linux.SockType, uid UniqueIDProv
|
||||
}
|
||||
|
||||
// NewPair allocates a new pair of connected unix-domain connectionedEndpoints.
|
||||
func NewPair(ctx context.Context, stype linux.SockType, uid UniqueIDProvider) (Endpoint, Endpoint) {
|
||||
func NewPair(ctx context.Context, stype linux.SockType, uid uniqueid.Provider) (Endpoint, Endpoint) {
|
||||
a := newConnectioned(ctx, stype, uid)
|
||||
b := newConnectioned(ctx, stype, uid)
|
||||
|
||||
@@ -169,7 +163,7 @@ func NewPair(ctx context.Context, stype linux.SockType, uid UniqueIDProvider) (E
|
||||
|
||||
// NewExternal creates a new externally backed Endpoint. It behaves like a
|
||||
// socketpair.
|
||||
func NewExternal(ctx context.Context, stype linux.SockType, uid UniqueIDProvider, queue *waiter.Queue, receiver Receiver, connected ConnectedEndpoint) Endpoint {
|
||||
func NewExternal(stype linux.SockType, uid uniqueid.Provider, queue *waiter.Queue, receiver Receiver, connected ConnectedEndpoint) Endpoint {
|
||||
ep := &connectionedEndpoint{
|
||||
baseEndpoint: baseEndpoint{Queue: queue, receiver: receiver, connected: connected},
|
||||
id: uid.UniqueID(),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
// Copyright 2021 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package host
|
||||
package transport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -24,9 +24,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/fdnotifier"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/control"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
|
||||
"gvisor.dev/gvisor/pkg/sentry/uniqueid"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
@@ -34,32 +31,37 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// Create a new host-backed endpoint from the given fd and its corresponding
|
||||
// notification queue.
|
||||
func newEndpoint(ctx context.Context, hostFD int, queue *waiter.Queue) (transport.Endpoint, error) {
|
||||
// Set up an external transport.Endpoint using the host fd.
|
||||
addr := fmt.Sprintf("hostfd:[%d]", hostFD)
|
||||
e, err := NewConnectedEndpoint(hostFD, addr)
|
||||
if err != nil {
|
||||
return nil, err.ToError()
|
||||
}
|
||||
ep := transport.NewExternal(ctx, e.stype, uniqueid.GlobalProviderFromContext(ctx), queue, e, e)
|
||||
return ep, nil
|
||||
// SCMRights implements RightsControlMessage with host FDs.
|
||||
type SCMRights struct {
|
||||
FDs []int
|
||||
}
|
||||
|
||||
// ConnectedEndpoint is an implementation of transport.ConnectedEndpoint and
|
||||
// transport.Receiver. It is backed by a host fd that was imported at sentry
|
||||
// startup. This fd is shared with a hostfs inode, which retains ownership of
|
||||
// it.
|
||||
// Clone implements RightsControlMessage.Clone.
|
||||
func (c *SCMRights) Clone() RightsControlMessage {
|
||||
// Host rights never need to be cloned.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Release implements RightsControlMessage.Release.
|
||||
func (c *SCMRights) Release(ctx context.Context) {
|
||||
for _, fd := range c.FDs {
|
||||
unix.Close(fd)
|
||||
}
|
||||
c.FDs = nil
|
||||
}
|
||||
|
||||
// HostConnectedEndpoint is an implementation of ConnectedEndpoint and
|
||||
// Receiver. It is backed by a host fd that was imported at sentry startup.
|
||||
// This fd is shared with a hostfs inode, which retains ownership of it.
|
||||
//
|
||||
// ConnectedEndpoint is saveable, since we expect that the host will provide
|
||||
// the same fd upon restore.
|
||||
// HostConnectedEndpoint is saveable, since we expect that the host will
|
||||
// provide the same fd upon restore.
|
||||
//
|
||||
// As of this writing, we only allow Unix sockets to be imported.
|
||||
//
|
||||
// +stateify savable
|
||||
type ConnectedEndpoint struct {
|
||||
ConnectedEndpointRefs
|
||||
type HostConnectedEndpoint struct {
|
||||
HostConnectedEndpointRefs
|
||||
|
||||
// mu protects fd below.
|
||||
mu sync.RWMutex `state:"nosave"`
|
||||
@@ -82,14 +84,14 @@ type ConnectedEndpoint struct {
|
||||
stype linux.SockType
|
||||
}
|
||||
|
||||
// init performs initialization required for creating new ConnectedEndpoints and
|
||||
// for restoring them.
|
||||
func (c *ConnectedEndpoint) init() *syserr.Error {
|
||||
// init performs initialization required for creating new
|
||||
// HostConnectedEndpoints and for restoring them.
|
||||
func (c *HostConnectedEndpoint) init() *syserr.Error {
|
||||
c.InitRefs()
|
||||
return c.initFromOptions()
|
||||
}
|
||||
|
||||
func (c *ConnectedEndpoint) initFromOptions() *syserr.Error {
|
||||
func (c *HostConnectedEndpoint) initFromOptions() *syserr.Error {
|
||||
family, err := unix.GetsockoptInt(c.fd, unix.SOL_SOCKET, unix.SO_DOMAIN)
|
||||
if err != nil {
|
||||
return syserr.FromError(err)
|
||||
@@ -120,14 +122,14 @@ func (c *ConnectedEndpoint) initFromOptions() *syserr.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewConnectedEndpoint creates a new ConnectedEndpoint backed by a host fd
|
||||
// imported at sentry startup,
|
||||
// NewHostConnectedEndpoint creates a new HostConnectedEndpoint backed by a
|
||||
// host fd imported at sentry startup.
|
||||
//
|
||||
// The caller is responsible for calling Init(). Additionaly, Release needs to
|
||||
// be called twice because ConnectedEndpoint is both a transport.Receiver and
|
||||
// transport.ConnectedEndpoint.
|
||||
func NewConnectedEndpoint(hostFD int, addr string) (*ConnectedEndpoint, *syserr.Error) {
|
||||
e := ConnectedEndpoint{
|
||||
// The caller is responsible for calling Init(). Additionally, Release needs to
|
||||
// be called twice because HostConnectedEndpoint is both a Receiver and
|
||||
// HostConnectedEndpoint.
|
||||
func NewHostConnectedEndpoint(hostFD int, addr string) (*HostConnectedEndpoint, *syserr.Error) {
|
||||
e := HostConnectedEndpoint{
|
||||
fd: hostFD,
|
||||
addr: addr,
|
||||
}
|
||||
@@ -136,13 +138,18 @@ func NewConnectedEndpoint(hostFD int, addr string) (*ConnectedEndpoint, *syserr.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ConnectedEndpointRefs start off with a single reference. We need two.
|
||||
// HostConnectedEndpointRefs start off with a single reference. We need two.
|
||||
e.IncRef()
|
||||
return &e, nil
|
||||
}
|
||||
|
||||
// Send implements transport.ConnectedEndpoint.Send.
|
||||
func (c *ConnectedEndpoint) Send(ctx context.Context, data [][]byte, controlMessages transport.ControlMessages, from tcpip.FullAddress) (int64, bool, *syserr.Error) {
|
||||
// SockType returns the underlying socket type.
|
||||
func (c *HostConnectedEndpoint) SockType() linux.SockType {
|
||||
return c.stype
|
||||
}
|
||||
|
||||
// Send implements ConnectedEndpoint.Send.
|
||||
func (c *HostConnectedEndpoint) Send(ctx context.Context, data [][]byte, controlMessages ControlMessages, from tcpip.FullAddress) (int64, bool, *syserr.Error) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
@@ -172,11 +179,11 @@ func (c *ConnectedEndpoint) Send(ctx context.Context, data [][]byte, controlMess
|
||||
return n, false, syserr.FromError(err)
|
||||
}
|
||||
|
||||
// SendNotify implements transport.ConnectedEndpoint.SendNotify.
|
||||
func (c *ConnectedEndpoint) SendNotify() {}
|
||||
// SendNotify implements ConnectedEndpoint.SendNotify.
|
||||
func (c *HostConnectedEndpoint) SendNotify() {}
|
||||
|
||||
// CloseSend implements transport.ConnectedEndpoint.CloseSend.
|
||||
func (c *ConnectedEndpoint) CloseSend() {
|
||||
// CloseSend implements ConnectedEndpoint.CloseSend.
|
||||
func (c *HostConnectedEndpoint) CloseSend() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
@@ -187,30 +194,30 @@ func (c *ConnectedEndpoint) CloseSend() {
|
||||
}
|
||||
}
|
||||
|
||||
// CloseNotify implements transport.ConnectedEndpoint.CloseNotify.
|
||||
func (c *ConnectedEndpoint) CloseNotify() {}
|
||||
// CloseNotify implements ConnectedEndpoint.CloseNotify.
|
||||
func (c *HostConnectedEndpoint) CloseNotify() {}
|
||||
|
||||
// Writable implements transport.ConnectedEndpoint.Writable.
|
||||
func (c *ConnectedEndpoint) Writable() bool {
|
||||
// Writable implements ConnectedEndpoint.Writable.
|
||||
func (c *HostConnectedEndpoint) Writable() bool {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
return fdnotifier.NonBlockingPoll(int32(c.fd), waiter.WritableEvents)&waiter.WritableEvents != 0
|
||||
}
|
||||
|
||||
// Passcred implements transport.ConnectedEndpoint.Passcred.
|
||||
func (c *ConnectedEndpoint) Passcred() bool {
|
||||
// Passcred implements ConnectedEndpoint.Passcred.
|
||||
func (c *HostConnectedEndpoint) Passcred() bool {
|
||||
// We don't support credential passing for host sockets.
|
||||
return false
|
||||
}
|
||||
|
||||
// GetLocalAddress implements transport.ConnectedEndpoint.GetLocalAddress.
|
||||
func (c *ConnectedEndpoint) GetLocalAddress() (tcpip.FullAddress, tcpip.Error) {
|
||||
// GetLocalAddress implements ConnectedEndpoint.GetLocalAddress.
|
||||
func (c *HostConnectedEndpoint) GetLocalAddress() (tcpip.FullAddress, tcpip.Error) {
|
||||
return tcpip.FullAddress{Addr: tcpip.Address(c.addr)}, nil
|
||||
}
|
||||
|
||||
// EventUpdate implements transport.ConnectedEndpoint.EventUpdate.
|
||||
func (c *ConnectedEndpoint) EventUpdate() {
|
||||
// EventUpdate implements ConnectedEndpoint.EventUpdate.
|
||||
func (c *HostConnectedEndpoint) EventUpdate() {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
if c.fd != -1 {
|
||||
@@ -218,8 +225,8 @@ func (c *ConnectedEndpoint) EventUpdate() {
|
||||
}
|
||||
}
|
||||
|
||||
// Recv implements transport.Receiver.Recv.
|
||||
func (c *ConnectedEndpoint) Recv(ctx context.Context, data [][]byte, creds bool, numRights int, peek bool) (int64, int64, transport.ControlMessages, bool, tcpip.FullAddress, bool, *syserr.Error) {
|
||||
// Recv implements Receiver.Recv.
|
||||
func (c *HostConnectedEndpoint) Recv(ctx context.Context, data [][]byte, creds bool, numRights int, peek bool) (int64, int64, ControlMessages, bool, tcpip.FullAddress, bool, *syserr.Error) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
@@ -238,7 +245,7 @@ func (c *ConnectedEndpoint) Recv(ctx context.Context, data [][]byte, creds bool,
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
return 0, 0, transport.ControlMessages{}, false, tcpip.FullAddress{}, false, syserr.FromError(err)
|
||||
return 0, 0, ControlMessages{}, false, tcpip.FullAddress{}, false, syserr.FromError(err)
|
||||
}
|
||||
|
||||
// There is no need for the callee to call RecvNotify because fdReadVec uses
|
||||
@@ -251,25 +258,25 @@ func (c *ConnectedEndpoint) Recv(ctx context.Context, data [][]byte, creds bool,
|
||||
|
||||
// Avoid extra allocations in the case where there isn't any control data.
|
||||
if len(cm) == 0 {
|
||||
return rl, ml, transport.ControlMessages{}, cTrunc, tcpip.FullAddress{Addr: tcpip.Address(c.addr)}, false, nil
|
||||
return rl, ml, ControlMessages{}, cTrunc, tcpip.FullAddress{Addr: tcpip.Address(c.addr)}, false, nil
|
||||
}
|
||||
|
||||
fds, err := cm.ExtractFDs()
|
||||
if err != nil {
|
||||
return 0, 0, transport.ControlMessages{}, false, tcpip.FullAddress{}, false, syserr.FromError(err)
|
||||
return 0, 0, ControlMessages{}, false, tcpip.FullAddress{}, false, syserr.FromError(err)
|
||||
}
|
||||
|
||||
if len(fds) == 0 {
|
||||
return rl, ml, transport.ControlMessages{}, cTrunc, tcpip.FullAddress{Addr: tcpip.Address(c.addr)}, false, nil
|
||||
return rl, ml, ControlMessages{}, cTrunc, tcpip.FullAddress{Addr: tcpip.Address(c.addr)}, false, nil
|
||||
}
|
||||
return rl, ml, control.NewVFS2(nil, nil, newSCMRights(fds)), cTrunc, tcpip.FullAddress{Addr: tcpip.Address(c.addr)}, false, nil
|
||||
return rl, ml, ControlMessages{Rights: &SCMRights{fds}}, cTrunc, tcpip.FullAddress{Addr: tcpip.Address(c.addr)}, false, nil
|
||||
}
|
||||
|
||||
// RecvNotify implements transport.Receiver.RecvNotify.
|
||||
func (c *ConnectedEndpoint) RecvNotify() {}
|
||||
// RecvNotify implements Receiver.RecvNotify.
|
||||
func (c *HostConnectedEndpoint) RecvNotify() {}
|
||||
|
||||
// CloseRecv implements transport.Receiver.CloseRecv.
|
||||
func (c *ConnectedEndpoint) CloseRecv() {
|
||||
// CloseRecv implements Receiver.CloseRecv.
|
||||
func (c *HostConnectedEndpoint) CloseRecv() {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
@@ -280,47 +287,46 @@ func (c *ConnectedEndpoint) CloseRecv() {
|
||||
}
|
||||
}
|
||||
|
||||
// Readable implements transport.Receiver.Readable.
|
||||
func (c *ConnectedEndpoint) Readable() bool {
|
||||
// Readable implements Receiver.Readable.
|
||||
func (c *HostConnectedEndpoint) Readable() bool {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
return fdnotifier.NonBlockingPoll(int32(c.fd), waiter.ReadableEvents)&waiter.ReadableEvents != 0
|
||||
}
|
||||
|
||||
// SendQueuedSize implements transport.Receiver.SendQueuedSize.
|
||||
func (c *ConnectedEndpoint) SendQueuedSize() int64 {
|
||||
// SendQueuedSize implements Receiver.SendQueuedSize.
|
||||
func (c *HostConnectedEndpoint) SendQueuedSize() int64 {
|
||||
// TODO(gvisor.dev/issue/273): SendQueuedSize isn't supported for host
|
||||
// sockets because we don't allow the sentry to call ioctl(2).
|
||||
return -1
|
||||
}
|
||||
|
||||
// RecvQueuedSize implements transport.Receiver.RecvQueuedSize.
|
||||
func (c *ConnectedEndpoint) RecvQueuedSize() int64 {
|
||||
// RecvQueuedSize implements Receiver.RecvQueuedSize.
|
||||
func (c *HostConnectedEndpoint) RecvQueuedSize() int64 {
|
||||
// TODO(gvisor.dev/issue/273): RecvQueuedSize isn't supported for host
|
||||
// sockets because we don't allow the sentry to call ioctl(2).
|
||||
return -1
|
||||
}
|
||||
|
||||
// SendMaxQueueSize implements transport.Receiver.SendMaxQueueSize.
|
||||
func (c *ConnectedEndpoint) SendMaxQueueSize() int64 {
|
||||
// SendMaxQueueSize implements Receiver.SendMaxQueueSize.
|
||||
func (c *HostConnectedEndpoint) SendMaxQueueSize() int64 {
|
||||
return atomic.LoadInt64(&c.sndbuf)
|
||||
}
|
||||
|
||||
// RecvMaxQueueSize implements transport.Receiver.RecvMaxQueueSize.
|
||||
func (c *ConnectedEndpoint) RecvMaxQueueSize() int64 {
|
||||
// RecvMaxQueueSize implements Receiver.RecvMaxQueueSize.
|
||||
func (c *HostConnectedEndpoint) RecvMaxQueueSize() int64 {
|
||||
// N.B. Unix sockets don't use the receive buffer. We'll claim it is
|
||||
// the same size as the send buffer.
|
||||
return atomic.LoadInt64(&c.sndbuf)
|
||||
}
|
||||
|
||||
func (c *ConnectedEndpoint) destroyLocked() {
|
||||
func (c *HostConnectedEndpoint) destroyLocked() {
|
||||
c.fd = -1
|
||||
}
|
||||
|
||||
// Release implements transport.ConnectedEndpoint.Release and
|
||||
// transport.Receiver.Release.
|
||||
func (c *ConnectedEndpoint) Release(ctx context.Context) {
|
||||
// Release implements ConnectedEndpoint.Release and Receiver.Release.
|
||||
func (c *HostConnectedEndpoint) Release(ctx context.Context) {
|
||||
c.DecRef(func() {
|
||||
c.mu.Lock()
|
||||
c.destroyLocked()
|
||||
@@ -328,18 +334,18 @@ func (c *ConnectedEndpoint) Release(ctx context.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// CloseUnread implements transport.ConnectedEndpoint.CloseUnread.
|
||||
func (c *ConnectedEndpoint) CloseUnread() {}
|
||||
// CloseUnread implements ConnectedEndpoint.CloseUnread.
|
||||
func (c *HostConnectedEndpoint) CloseUnread() {}
|
||||
|
||||
// SetSendBufferSize implements transport.ConnectedEndpoint.SetSendBufferSize.
|
||||
func (c *ConnectedEndpoint) SetSendBufferSize(v int64) (newSz int64) {
|
||||
// SetSendBufferSize implements ConnectedEndpoint.SetSendBufferSize.
|
||||
func (c *HostConnectedEndpoint) SetSendBufferSize(v int64) (newSz int64) {
|
||||
// gVisor does not permit setting of SO_SNDBUF for host backed unix
|
||||
// domain sockets.
|
||||
return atomic.LoadInt64(&c.sndbuf)
|
||||
}
|
||||
|
||||
// SetReceiveBufferSize implements transport.ConnectedEndpoint.SetReceiveBufferSize.
|
||||
func (c *ConnectedEndpoint) SetReceiveBufferSize(v int64) (newSz int64) {
|
||||
// SetReceiveBufferSize implements ConnectedEndpoint.SetReceiveBufferSize.
|
||||
func (c *HostConnectedEndpoint) SetReceiveBufferSize(v int64) (newSz int64) {
|
||||
// gVisor does not permit setting of SO_RCVBUF for host backed unix
|
||||
// domain sockets. Receive buffer does not have any effect for unix
|
||||
// sockets and we claim to be the same as send buffer.
|
||||
@@ -347,13 +353,13 @@ func (c *ConnectedEndpoint) SetReceiveBufferSize(v int64) (newSz int64) {
|
||||
}
|
||||
|
||||
// SCMConnectedEndpoint represents an endpoint backed by a host fd that was
|
||||
// passed through a gofer Unix socket. It resembles ConnectedEndpoint, with the
|
||||
// passed through a gofer Unix socket. It resembles HostConnectedEndpoint, with the
|
||||
// following differences:
|
||||
// - SCMConnectedEndpoint is not saveable, because the host cannot guarantee
|
||||
// the same descriptor number across S/R.
|
||||
// - SCMConnectedEndpoint holds ownership of its fd and notification queue.
|
||||
type SCMConnectedEndpoint struct {
|
||||
ConnectedEndpoint
|
||||
HostConnectedEndpoint
|
||||
|
||||
queue *waiter.Queue
|
||||
}
|
||||
@@ -363,8 +369,7 @@ func (e *SCMConnectedEndpoint) Init() error {
|
||||
return fdnotifier.AddFD(int32(e.fd), e.queue)
|
||||
}
|
||||
|
||||
// Release implements transport.ConnectedEndpoint.Release and
|
||||
// transport.Receiver.Release.
|
||||
// Release implements ConnectedEndpoint.Release and Receiver.Release.
|
||||
func (e *SCMConnectedEndpoint) Release(ctx context.Context) {
|
||||
e.DecRef(func() {
|
||||
e.mu.Lock()
|
||||
@@ -381,11 +386,11 @@ func (e *SCMConnectedEndpoint) Release(ctx context.Context) {
|
||||
// was passed through a Unix socket.
|
||||
//
|
||||
// The caller is responsible for calling Init(). Additionaly, Release needs to
|
||||
// be called twice because ConnectedEndpoint is both a transport.Receiver and
|
||||
// transport.ConnectedEndpoint.
|
||||
func NewSCMEndpoint(ctx context.Context, hostFD int, queue *waiter.Queue, addr string) (*SCMConnectedEndpoint, *syserr.Error) {
|
||||
// be called twice because ConnectedEndpoint is both a Receiver and
|
||||
// ConnectedEndpoint.
|
||||
func NewSCMEndpoint(hostFD int, queue *waiter.Queue, addr string) (*SCMConnectedEndpoint, *syserr.Error) {
|
||||
e := SCMConnectedEndpoint{
|
||||
ConnectedEndpoint: ConnectedEndpoint{
|
||||
HostConnectedEndpoint: HostConnectedEndpoint{
|
||||
fd: hostFD,
|
||||
addr: addr,
|
||||
},
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package host
|
||||
package transport
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package host
|
||||
package transport
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2021 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package transport
|
||||
|
||||
import "fmt"
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (c *HostConnectedEndpoint) afterLoad() {
|
||||
if err := c.initFromOptions(); err != nil {
|
||||
panic(fmt.Sprintf("initFromOptions failed: %v", err))
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,7 @@ go_library(
|
||||
"//pkg/sentry/fs/lock",
|
||||
"//pkg/sentry/fsbridge",
|
||||
"//pkg/sentry/fsimpl/eventfd",
|
||||
"//pkg/sentry/fsimpl/host",
|
||||
"//pkg/sentry/fsimpl/pipefs",
|
||||
"//pkg/sentry/fsimpl/signalfd",
|
||||
"//pkg/sentry/fsimpl/timerfd",
|
||||
@@ -77,5 +78,6 @@ go_library(
|
||||
"//pkg/syserr",
|
||||
"//pkg/usermem",
|
||||
"//pkg/waiter",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -15,15 +15,20 @@
|
||||
package vfs2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/marshal"
|
||||
"gvisor.dev/gvisor/pkg/marshal/primitive"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/host"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/control"
|
||||
@@ -744,6 +749,48 @@ func RecvMMsg(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
return uintptr(count), nil, nil
|
||||
}
|
||||
|
||||
func getSCMRightsVFS2(t *kernel.Task, rights transport.RightsControlMessage) control.SCMRightsVFS2 {
|
||||
switch v := rights.(type) {
|
||||
case control.SCMRightsVFS2:
|
||||
return v
|
||||
case *transport.SCMRights:
|
||||
rf := control.RightsFilesVFS2(fdsToHostFiles(t, v.FDs))
|
||||
return &rf
|
||||
default:
|
||||
panic(fmt.Sprintf("rights of type %T must be *transport.SCMRights or implement SCMRightsVFS2", rights))
|
||||
}
|
||||
}
|
||||
|
||||
// If an error is encountered, only files created before the error will be
|
||||
// returned. This is what Linux does.
|
||||
func fdsToHostFiles(ctx context.Context, fds []int) []*vfs.FileDescription {
|
||||
files := make([]*vfs.FileDescription, 0, len(fds))
|
||||
for _, fd := range fds {
|
||||
// Get flags. We do it here because they may be modified
|
||||
// by subsequent functions.
|
||||
fileFlags, _, errno := unix.Syscall(unix.SYS_FCNTL, uintptr(fd), unix.F_GETFL, 0)
|
||||
if errno != 0 {
|
||||
ctx.Warningf("Error retrieving host FD flags: %v", error(errno))
|
||||
break
|
||||
}
|
||||
|
||||
// Create the file backed by hostFD.
|
||||
file, err := host.NewFD(ctx, kernel.KernelFromContext(ctx).HostMount(), fd, &host.NewFDOptions{})
|
||||
if err != nil {
|
||||
ctx.Warningf("Error creating file from host FD: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
if err := file.SetStatusFlags(ctx, auth.CredentialsFromContext(ctx), uint32(fileFlags&linux.O_NONBLOCK)); err != nil {
|
||||
ctx.Warningf("Error setting flags on host FD file: %v", err)
|
||||
break
|
||||
}
|
||||
|
||||
files = append(files, file)
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
func recvSingleMsg(t *kernel.Task, s socket.SocketVFS2, msgPtr hostarch.Addr, flags int32, haveDeadline bool, deadline ktime.Time) (uintptr, error) {
|
||||
// Capture the message header and io vectors.
|
||||
var msg MessageHeader64
|
||||
@@ -800,6 +847,7 @@ func recvSingleMsg(t *kernel.Task, s socket.SocketVFS2, msgPtr hostarch.Addr, fl
|
||||
}
|
||||
|
||||
if cms.Unix.Rights != nil {
|
||||
cms.Unix.Rights = getSCMRightsVFS2(t, cms.Unix.Rights)
|
||||
controlData, mflags = control.PackRightsVFS2(t, cms.Unix.Rights.(control.SCMRightsVFS2), flags&linux.MSG_CMSG_CLOEXEC != 0, controlData, mflags)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,5 @@ go_library(
|
||||
name = "uniqueid",
|
||||
srcs = ["context.go"],
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
deps = [
|
||||
"//pkg/context",
|
||||
"//pkg/sentry/socket/unix/transport",
|
||||
],
|
||||
deps = ["//pkg/context"],
|
||||
)
|
||||
|
||||
@@ -18,7 +18,6 @@ package uniqueid
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
|
||||
)
|
||||
|
||||
// contextID is the kernel package's type for context.Context.Value keys.
|
||||
@@ -38,14 +37,21 @@ const (
|
||||
CtxInotifyCookie
|
||||
)
|
||||
|
||||
// Provider generates a sequence of unique identifiers useful for,
|
||||
// among other things, lock ordering.
|
||||
type Provider interface {
|
||||
// UniqueID returns a new unique identifier.
|
||||
UniqueID() uint64
|
||||
}
|
||||
|
||||
// GlobalFromContext returns a system-wide unique identifier from ctx.
|
||||
func GlobalFromContext(ctx context.Context) uint64 {
|
||||
return ctx.Value(CtxGlobalUniqueID).(uint64)
|
||||
}
|
||||
|
||||
// GlobalProviderFromContext returns a system-wide unique identifier from ctx.
|
||||
func GlobalProviderFromContext(ctx context.Context) transport.UniqueIDProvider {
|
||||
return ctx.Value(CtxGlobalUniqueIDProvider).(transport.UniqueIDProvider)
|
||||
func GlobalProviderFromContext(ctx context.Context) Provider {
|
||||
return ctx.Value(CtxGlobalUniqueIDProvider).(Provider)
|
||||
}
|
||||
|
||||
// InotifyCookie generates a unique inotify event cookie from ctx.
|
||||
|
||||
Reference in New Issue
Block a user