From 0061d0e4e5d74efce8af8d706437cba3d040cd5f Mon Sep 17 00:00:00 2001 From: "Zyad A. Ali" Date: Tue, 20 Jul 2021 20:31:46 +0200 Subject: Implement queueInode and queueFD in mqfs. Implement inode and file description representing a POSIX message queue, and other utilities needed to implement file operations. Updates #136 --- pkg/sentry/fsimpl/mqfs/queue.go | 145 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 pkg/sentry/fsimpl/mqfs/queue.go (limited to 'pkg/sentry/fsimpl/mqfs/queue.go') diff --git a/pkg/sentry/fsimpl/mqfs/queue.go b/pkg/sentry/fsimpl/mqfs/queue.go new file mode 100644 index 000000000..a8e9bc722 --- /dev/null +++ b/pkg/sentry/fsimpl/mqfs/queue.go @@ -0,0 +1,145 @@ +// 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/errors/linuxerr" + "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/usermem" + "gvisor.dev/gvisor/pkg/waiter" +) + +// queueInode represents an inode for a message queue (/dev/mqueue/[name]). +// +// +stateify savable +type queueInode struct { + kernfs.DynamicBytesFile + + // queue is the message queue backing this inode. + queue *mq.Queue +} + +var _ kernfs.Inode = (*queueInode)(nil) + +// newQueueInode returns a new, initialized queueInode. +func (fs *filesystem) newQueueInode(ctx context.Context, creds *auth.Credentials, q *mq.Queue, perm linux.FileMode) kernfs.Inode { + inode := &queueInode{queue: q} + inode.Init(ctx, creds, linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), q, perm) + return inode +} + +// Keep implements kernfs.Inode.Keep. +func (q *queueInode) Keep() bool { + // Return true so that the fs keeps newly created dentries. This is done + // because inodes returned by root.Lookup are not temporary, they exist + // in the fs, and refer to message queues. + return true +} + +// queueFD implements vfs.FileDescriptionImpl for FD backed by a POSIX message +// queue. It's mostly similar to DynamicBytesFD, but implements more operations. +// +// +stateify savable +type queueFD struct { + vfs.FileDescriptionDefaultImpl + vfs.DynamicBytesFileDescriptionImpl + vfs.LockFD + + vfsfd vfs.FileDescription + inode kernfs.Inode + + // queue is the queue backing this fd. + queue *mq.Queue +} + +// Init initializes a queueFD. Mostly copied from DynamicBytesFD.Init, but uses +// the queueFD as FileDescriptionImpl. +func (fd *queueFD) Init(m *vfs.Mount, d *kernfs.Dentry, data vfs.DynamicBytesSource, locks *vfs.FileLocks, flags uint32) error { + fd.LockFD.Init(locks) + if err := fd.vfsfd.Init(fd, flags, m, d.VFSDentry(), &vfs.FileDescriptionOptions{}); err != nil { + return err + } + fd.inode = d.Inode() + fd.SetDataSource(data) + return nil +} + +// Seek implements vfs.FileDescriptionImpl.Seek. +func (fd *queueFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) { + return fd.DynamicBytesFileDescriptionImpl.Seek(ctx, offset, whence) +} + +// Read implements vfs.FileDescriptionImpl.Read. +func (fd *queueFD) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) { + return fd.DynamicBytesFileDescriptionImpl.Read(ctx, dst, opts) +} + +// PRead implements vfs.FileDescriptionImpl.PRead. +func (fd *queueFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) { + return fd.DynamicBytesFileDescriptionImpl.PRead(ctx, dst, offset, opts) +} + +// Write implements vfs.FileDescriptionImpl.Write. +func (fd *queueFD) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) { + return fd.DynamicBytesFileDescriptionImpl.Write(ctx, src, opts) +} + +// PWrite implements vfs.FileDescriptionImpl.PWrite. +func (fd *queueFD) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) { + return fd.DynamicBytesFileDescriptionImpl.PWrite(ctx, src, offset, opts) +} + +// Release implements vfs.FileDescriptionImpl.Release. +func (fd *queueFD) Release(context.Context) {} + +// Stat implements vfs.FileDescriptionImpl.Stat. +func (fd *queueFD) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) { + fs := fd.vfsfd.VirtualDentry().Mount().Filesystem() + return fd.inode.Stat(ctx, fs, opts) +} + +// SetStat implements vfs.FileDescriptionImpl.SetStat. +func (fd *queueFD) SetStat(context.Context, vfs.SetStatOptions) error { + // DynamicBytesFiles are immutable. + return linuxerr.EPERM +} + +// OnClose implements FileDescriptionImpl.OnClose similar to +// ipc/mqueue.c::mqueue_flush_file. +func (fd *queueFD) OnClose(ctx context.Context) error { + fd.queue.Flush(ctx) + return nil +} + +// Readiness implements waiter.Waitable.Readiness similar to +// ipc/mqueue.c::mqueue_poll_file. +func (fd *queueFD) Readiness(mask waiter.EventMask) waiter.EventMask { + return fd.queue.Readiness(mask) +} + +// EventRegister implements Waitable.EventRegister. +func (fd *queueFD) EventRegister(e *waiter.Entry, mask waiter.EventMask) { + fd.queue.EventRegister(e, mask) +} + +// EventUnregister implements Waitable.EventUnregister. +func (fd *queueFD) EventUnregister(e *waiter.Entry) { + fd.queue.EventUnregister(e) +} -- cgit v1.2.3 From 7508a0efeeef19a3e08e06e80be8258743438412 Mon Sep 17 00:00:00 2001 From: "Zyad A. Ali" Date: Thu, 29 Jul 2021 22:17:26 +0200 Subject: Define mq.View and use it for mqfs.queueFD. View makes it easier to handle O_RDONLY, O_WRONLY, and ORDWR options in mq_open(2). Updates #136 --- pkg/sentry/fsimpl/mqfs/queue.go | 4 ++-- pkg/sentry/kernel/mq/mq.go | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) (limited to 'pkg/sentry/fsimpl/mqfs/queue.go') diff --git a/pkg/sentry/fsimpl/mqfs/queue.go b/pkg/sentry/fsimpl/mqfs/queue.go index a8e9bc722..933dbc6ed 100644 --- a/pkg/sentry/fsimpl/mqfs/queue.go +++ b/pkg/sentry/fsimpl/mqfs/queue.go @@ -65,8 +65,8 @@ type queueFD struct { vfsfd vfs.FileDescription inode kernfs.Inode - // queue is the queue backing this fd. - queue *mq.Queue + // queue is a view into the queue backing this fd. + queue mq.View } // Init initializes a queueFD. Mostly copied from DynamicBytesFD.Init, but uses diff --git a/pkg/sentry/kernel/mq/mq.go b/pkg/sentry/kernel/mq/mq.go index 739ea2f1c..954883c5f 100644 --- a/pkg/sentry/kernel/mq/mq.go +++ b/pkg/sentry/kernel/mq/mq.go @@ -129,6 +129,21 @@ type Queue struct { byteCount uint64 } +// View is a view into a message queue. Views should only be used in file +// descriptions, but not inodes, because we use inodes to retreive the actual +// queue, and only FDs are responsible for providing user functionality. +type View interface { + // TODO: Add Send and Receive when mq_timedsend(2) and mq_timedreceive(2) + // are implemented. + + // Flush checks if the calling process has attached a notification request + // to this queue, if yes, then the request is removed, and another process + // can attach a request. + Flush(ctx context.Context) + + waiter.Waitable +} + // Message holds a message exchanged through a Queue via mq_timedsend(2) and // mq_timedreceive(2), and additional info relating to the message. // @@ -179,9 +194,7 @@ func (q *Queue) Generate(ctx context.Context, buf *bytes.Buffer) error { return nil } -// Flush checks if the calling process has attached a notification request to -// this queue, if yes, then the request is removed, and another process can -// attach a request. +// Flush implements View.Flush. func (q *Queue) Flush(ctx context.Context) { q.mu.Lock() defer q.mu.Unlock() -- cgit v1.2.3