diff options
-rw-r--r-- | pkg/sentry/fsimpl/mqfs/BUILD | 2 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/mqfs/mqfs.go | 4 | ||||
-rw-r--r-- | pkg/sentry/fsimpl/mqfs/registry.go | 121 | ||||
-rw-r--r-- | pkg/sentry/kernel/BUILD | 1 | ||||
-rw-r--r-- | pkg/sentry/kernel/ipc_namespace.go | 25 | ||||
-rw-r--r-- | pkg/sentry/kernel/mq/BUILD | 1 | ||||
-rw-r--r-- | pkg/sentry/kernel/mq/mq.go | 51 |
7 files changed, 202 insertions, 3 deletions
diff --git a/pkg/sentry/fsimpl/mqfs/BUILD b/pkg/sentry/fsimpl/mqfs/BUILD index 6b22ffabd..6892c6c25 100644 --- a/pkg/sentry/fsimpl/mqfs/BUILD +++ b/pkg/sentry/fsimpl/mqfs/BUILD @@ -20,6 +20,7 @@ go_library( "mqfs.go", "root.go", "queue.go", + "registry.go", "root_inode_refs.go", ], visibility = ["//pkg/sentry:internal"], @@ -32,6 +33,7 @@ go_library( "//pkg/sentry/kernel/auth", "//pkg/sentry/kernel/mq", "//pkg/sentry/vfs", + "//pkg/sync", "//pkg/usermem", "//pkg/waiter", ], diff --git a/pkg/sentry/fsimpl/mqfs/mqfs.go b/pkg/sentry/fsimpl/mqfs/mqfs.go index 18bc66134..a92012deb 100644 --- a/pkg/sentry/fsimpl/mqfs/mqfs.go +++ b/pkg/sentry/fsimpl/mqfs/mqfs.go @@ -28,7 +28,7 @@ import ( ) const ( - fsName = "mqueue" + Name = "mqueue" defaultMaxCachedDentries = uint64(1000) ) @@ -39,7 +39,7 @@ type FilesystemType struct{} // Name implements vfs.FilesystemType.Name. func (FilesystemType) Name() string { - return fsName + return Name } // Release implements vfs.FilesystemType.Release. diff --git a/pkg/sentry/fsimpl/mqfs/registry.go b/pkg/sentry/fsimpl/mqfs/registry.go new file mode 100644 index 000000000..3875b39ee --- /dev/null +++ b/pkg/sentry/fsimpl/mqfs/registry.go @@ -0,0 +1,121 @@ +// 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 mqfs + +import ( + "gvisor.dev/gvisor/pkg/abi/linux" + "gvisor.dev/gvisor/pkg/context" + "gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs" + "gvisor.dev/gvisor/pkg/sentry/kernel/auth" + "gvisor.dev/gvisor/pkg/sentry/kernel/mq" + "gvisor.dev/gvisor/pkg/sentry/vfs" + "gvisor.dev/gvisor/pkg/sync" +) + +// RegistryImpl implements mq.RegistryImpl. It implements the interface using +// the message queue filesystem, and is provided to mq.Registry at +// initialization. +// +// +stateify savable +type RegistryImpl struct { + // mu protects all fields below. + mu sync.Mutex + + // root is the root dentry of the mq filesystem. Its main usage is to + // retreive the root inode, which we use to add, remove, and lookup message + // queues. + // + // We hold a reference on root and release when the registry is destroyed. + root *kernfs.Dentry + + // fs is the filesystem backing this registry, used mainly to initialize + // new inodes. + fs *filesystem + + // mount is the mount point used for this filesystem. + mount *vfs.Mount +} + +// NewRegistryImpl returns a new, initialized RegistryImpl, and takes a +// reference on root. +func NewRegistryImpl(root *kernfs.Dentry, fs *filesystem) *RegistryImpl { + root.IncRef() + return &RegistryImpl{ + root: root, + fs: fs, + } +} + +// Lookup implements mq.RegistryImpl.Lookup. +func (r *RegistryImpl) Lookup(ctx context.Context, name string) *mq.Queue { + r.mu.Lock() + defer r.mu.Unlock() + + inode, err := r.lookup(ctx, name) + if err != nil { + return nil + } + return inode.(*queueInode).queue +} + +// New implements mq.RegistryImpl.New. +func (r *RegistryImpl) New(ctx context.Context, name string, q *mq.Queue, perm linux.FileMode) (*vfs.FileDescription, error) { + r.mu.Lock() + defer r.mu.Unlock() + + root := r.root.Inode().(*rootInode) + qInode := r.fs.newQueueInode(ctx, auth.CredentialsFromContext(ctx), q, perm).(*queueInode) + err := root.Insert(name, qInode) + if err != nil { + return nil, err + } + + fd := &queueFD{queue: q} + err = fd.Init(r.mount, r.root, qInode.data, &qInode.locks, 0 /* flags */) + if err != nil { + return nil, err + } + return fd.VFSFileDescription(), nil +} + +// Unlink implements mq.RegistryImpl.Unlink. +func (r *RegistryImpl) Unlink(ctx context.Context, name string) error { + r.mu.Lock() + defer r.mu.Unlock() + + root := r.root.Inode().(*rootInode) + inode, err := r.lookup(ctx, name) + if err != nil { + return err + } + return root.Unlink(ctx, name, inode) +} + +// lookup retreives a kernfs.Inode using a name. +// +// Precondition: r.mu must be held. +func (r *RegistryImpl) lookup(ctx context.Context, name string) (kernfs.Inode, error) { + inode := r.root.Inode().(*rootInode) + lookup, err := inode.Lookup(ctx, name) + if err != nil { + return nil, err + } + return lookup, nil +} + +// Destroy implements mq.RegistryImpl.Destroy. +func (r *RegistryImpl) Destroy(ctx context.Context) { + r.root.DecRef(ctx) +} diff --git a/pkg/sentry/kernel/BUILD b/pkg/sentry/kernel/BUILD index c0f13bf52..e91338da7 100644 --- a/pkg/sentry/kernel/BUILD +++ b/pkg/sentry/kernel/BUILD @@ -257,6 +257,7 @@ go_library( "//pkg/sentry/kernel/auth", "//pkg/sentry/kernel/epoll", "//pkg/sentry/kernel/futex", + "//pkg/sentry/kernel/mq", "//pkg/sentry/kernel/msgqueue", "//pkg/sentry/kernel/sched", "//pkg/sentry/kernel/semaphore", diff --git a/pkg/sentry/kernel/ipc_namespace.go b/pkg/sentry/kernel/ipc_namespace.go index 0b101b1bb..aa9c3fb31 100644 --- a/pkg/sentry/kernel/ipc_namespace.go +++ b/pkg/sentry/kernel/ipc_namespace.go @@ -17,6 +17,7 @@ package kernel import ( "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" + "gvisor.dev/gvisor/pkg/sentry/kernel/mq" "gvisor.dev/gvisor/pkg/sentry/kernel/msgqueue" "gvisor.dev/gvisor/pkg/sentry/kernel/semaphore" "gvisor.dev/gvisor/pkg/sentry/kernel/shm" @@ -31,9 +32,17 @@ type IPCNamespace struct { // User namespace which owns this IPC namespace. Immutable. userNS *auth.UserNamespace + // System V utilities. queues *msgqueue.Registry semaphores *semaphore.Registry shms *shm.Registry + + // posixQueues is a POSIX message queue registry. + // + // posixQueues is somewhat equivelant to Linux's ipc_namespace.mq_mnt. + // Unlike SysV utilities, mq.Registry is not map-based, but is backed by + // a virtual filesystem. + posixQueues *mq.Registry } // NewIPCNamespace creates a new IPC namespace. @@ -63,10 +72,26 @@ func (i *IPCNamespace) ShmRegistry() *shm.Registry { return i.shms } +// SetPosixQueues sets value of posixQueues if the value is currently nil, +// otherwise returns without doing anything. +func (i *IPCNamespace) SetPosixQueues(r *mq.Registry) { + if i.posixQueues == nil { + i.posixQueues = r + } +} + +// PosixQueues returns the posix message queue registry for this namespace. +func (i *IPCNamespace) PosixQueues() *mq.Registry { + return i.posixQueues +} + // DecRef implements refsvfs2.RefCounter.DecRef. func (i *IPCNamespace) DecRef(ctx context.Context) { i.IPCNamespaceRefs.DecRef(func() { i.shms.Release(ctx) + if i.posixQueues != nil { + i.posixQueues.Destroy(ctx) + } }) } diff --git a/pkg/sentry/kernel/mq/BUILD b/pkg/sentry/kernel/mq/BUILD index b4e17b582..7b00b8346 100644 --- a/pkg/sentry/kernel/mq/BUILD +++ b/pkg/sentry/kernel/mq/BUILD @@ -26,6 +26,7 @@ go_library( "//pkg/abi/linux", "//pkg/context", "//pkg/sentry/fs", + "//pkg/sentry/vfs", "//pkg/sync", "//pkg/waiter", ], diff --git a/pkg/sentry/kernel/mq/mq.go b/pkg/sentry/kernel/mq/mq.go index 29a46e8a9..be46f78c8 100644 --- a/pkg/sentry/kernel/mq/mq.go +++ b/pkg/sentry/kernel/mq/mq.go @@ -22,6 +22,7 @@ import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/sentry/fs" + "gvisor.dev/gvisor/pkg/sentry/vfs" "gvisor.dev/gvisor/pkg/sync" "gvisor.dev/gvisor/pkg/waiter" ) @@ -30,6 +31,54 @@ const ( maxPriority = linux.MQ_PRIO_MAX - 1 // Highest possible message priority. ) +// Registry is a POSIX message queue registry. +// +// Unlike SysV utilities, Registry is not map-based. It uses a provided +// RegistryImpl backed by a virtual filesystem to implement registry operations. +// +// +stateify savable +type Registry struct { + // impl is an implementation of several message queue utilities needed by + // the registry. impl should be provided by mqfs. + impl RegistryImpl +} + +// RegistryImpl defines utilities needed by a Registry to provide actual +// registry implementation. It works mainly as an abstraction layer used by +// Registry to avoid dealing directly with the filesystem. RegistryImpl should +// be implemented by mqfs and provided to Registry at initialization. +type RegistryImpl interface { + // Lookup returns the queue with the given name, nil if non exists. + Lookup(context.Context, string) *Queue + + // New creates a new inode and file description using the given queue, + // inserts the inode into the filesystem tree with the given name, and + // returns the file description. An error is returned if creation fails, or + // if the name already exists. + New(context.Context, string, *Queue, linux.FileMode) (*vfs.FileDescription, error) + + // Unlink removes the queue with given name from the registry, and returns + // an error if the name doesn't exist. + Unlink(context.Context, string) error + + // Destroy destroys the registry. + Destroy(context.Context) +} + +// NewRegistry returns a new, initialized message queue registry. NewRegistry +// should be called when a new message queue filesystem is created, once per +// IPCNamespace. +func NewRegistry(impl RegistryImpl) *Registry { + return &Registry{ + impl: impl, + } +} + +// Destroy destroys the registry and releases all held references. +func (r *Registry) Destroy(ctx context.Context) { + r.impl.Destroy(ctx) +} + // Queue represents a POSIX message queue. // // +stateify savable @@ -103,7 +152,7 @@ type Subscriber struct { } // Generate implements vfs.DynamicBytesSource.Generate. Queue is used as a -// dynamic bytes source for mqfs's queueInode. +// DynamicBytesSource for mqfs's queueInode. func (q *Queue) Generate(ctx context.Context, buf *bytes.Buffer) error { q.mu.Lock() defer q.mu.Unlock() |