mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Return FDs in RegistryImpl functions and use Views.
Update RegistryImpl functions to return file descriptions, instead of queues, and use Views in queue inodes. Updates #136
This commit is contained in:
@@ -29,6 +29,7 @@ go_library(
|
||||
"//pkg/context",
|
||||
"//pkg/errors/linuxerr",
|
||||
"//pkg/refsvfs2",
|
||||
"//pkg/sentry/fs",
|
||||
"//pkg/sentry/fsimpl/kernfs",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/ipc",
|
||||
|
||||
@@ -17,6 +17,8 @@ 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/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/mq"
|
||||
@@ -78,20 +80,32 @@ func NewRegistryImpl(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Lookup implements mq.RegistryImpl.Lookup.
|
||||
func (r *RegistryImpl) Lookup(ctx context.Context, name string) *mq.Queue {
|
||||
// Get implements mq.RegistryImpl.Get.
|
||||
func (r *RegistryImpl) Get(ctx context.Context, name string, rOnly, wOnly, readWrite, block bool, flags uint32) (*vfs.FileDescription, bool, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
inode, err := r.lookup(ctx, name)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, false, nil
|
||||
}
|
||||
return inode.(*queueInode).queue
|
||||
|
||||
qInode := inode.(*queueInode)
|
||||
if !qInode.queue.HasPermissions(auth.CredentialsFromContext(ctx), perm(rOnly, wOnly, readWrite)) {
|
||||
// "The queue exists, but the caller does not have permission to
|
||||
// open it in the specified mode."
|
||||
return nil, false, linuxerr.EACCES
|
||||
}
|
||||
|
||||
fd, err := r.newFD(qInode.queue, qInode, rOnly, wOnly, readWrite, block, flags)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return fd, true, nil
|
||||
}
|
||||
|
||||
// New implements mq.RegistryImpl.New.
|
||||
func (r *RegistryImpl) New(ctx context.Context, name string, q *mq.Queue, perm linux.FileMode) (*vfs.FileDescription, error) {
|
||||
func (r *RegistryImpl) New(ctx context.Context, name string, q *mq.Queue, rOnly, wOnly, readWrite, block bool, perm linux.FileMode, flags uint32) (*vfs.FileDescription, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
@@ -101,13 +115,7 @@ func (r *RegistryImpl) New(ctx context.Context, name string, q *mq.Queue, perm l
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fd := &queueFD{queue: q}
|
||||
err = fd.Init(r.mount, r.root, q, qInode.Locks(), 0 /* flags */)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fd.vfsfd, nil
|
||||
return r.newFD(q, qInode, rOnly, wOnly, readWrite, block, flags)
|
||||
}
|
||||
|
||||
// Unlink implements mq.RegistryImpl.Unlink.
|
||||
@@ -115,6 +123,11 @@ func (r *RegistryImpl) Unlink(ctx context.Context, name string) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
creds := auth.CredentialsFromContext(ctx)
|
||||
if err := r.root.Inode().CheckPermissions(ctx, creds, vfs.MayWrite|vfs.MayExec); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
root := r.root.Inode().(*rootInode)
|
||||
inode, err := r.lookup(ctx, name)
|
||||
if err != nil {
|
||||
@@ -123,6 +136,11 @@ func (r *RegistryImpl) Unlink(ctx context.Context, name string) error {
|
||||
return root.Unlink(ctx, name, inode)
|
||||
}
|
||||
|
||||
// Destroy implements mq.RegistryImpl.Destroy.
|
||||
func (r *RegistryImpl) Destroy(ctx context.Context) {
|
||||
r.root.DecRef(ctx)
|
||||
}
|
||||
|
||||
// lookup retreives a kernfs.Inode using a name.
|
||||
//
|
||||
// Precondition: r.mu must be held.
|
||||
@@ -135,7 +153,34 @@ func (r *RegistryImpl) lookup(ctx context.Context, name string) (kernfs.Inode, e
|
||||
return lookup, nil
|
||||
}
|
||||
|
||||
// Destroy implements mq.RegistryImpl.Destroy.
|
||||
func (r *RegistryImpl) Destroy(ctx context.Context) {
|
||||
r.root.DecRef(ctx)
|
||||
// newFD returns a new file description created using the given queue and inode.
|
||||
func (r *RegistryImpl) newFD(q *mq.Queue, inode *queueInode, rOnly, wOnly, readWrite, block bool, flags uint32) (*vfs.FileDescription, error) {
|
||||
view, err := mq.NewView(q, rOnly, wOnly, readWrite, block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var dentry kernfs.Dentry
|
||||
dentry.Init(&r.fs.Filesystem, inode)
|
||||
|
||||
fd := &queueFD{queue: view}
|
||||
err = fd.Init(r.mount, &dentry, inode.queue, inode.Locks(), flags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fd.vfsfd, nil
|
||||
}
|
||||
|
||||
// perm returns a permission mask created using given flags.
|
||||
func perm(rOnly, wOnly, readWrite bool) fs.PermMask {
|
||||
switch {
|
||||
case readWrite:
|
||||
return fs.PermMask{Read: true, Write: true}
|
||||
case wOnly:
|
||||
return fs.PermMask{Write: true}
|
||||
case rOnly:
|
||||
return fs.PermMask{Read: true}
|
||||
default:
|
||||
return fs.PermMask{} // Can't happen, see NewView.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,9 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/context",
|
||||
"//pkg/errors/linuxerr",
|
||||
"//pkg/sentry/fs",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/vfs",
|
||||
"//pkg/sync",
|
||||
"//pkg/waiter",
|
||||
|
||||
@@ -21,13 +21,16 @@ import (
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
const (
|
||||
MaxName = 255 // Maximum size for a queue name.
|
||||
maxPriority = linux.MQ_PRIO_MAX - 1 // Highest possible message priority.
|
||||
)
|
||||
|
||||
@@ -48,18 +51,20 @@ type Registry struct {
|
||||
// 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
|
||||
// Get searchs for a queue with the given name, if it exists, the queue is
|
||||
// used to create a new FD, return it and return true. If the queue doesn't
|
||||
// exist, return false and no error. An error is returned if creation fails.
|
||||
Get(ctx context.Context, name string, rOnly, wOnly, readWrite, block bool, flags uint32) (*vfs.FileDescription, bool, error)
|
||||
|
||||
// New creates a new inode and file description using the given queue,
|
||||
// inserts the inode into the filesystem tree with the given name, and
|
||||
// inserts the inode into the filesystem tree using 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)
|
||||
New(ctx context.Context, name string, q *Queue, rOnly, wOnly, readWrite, block bool, perm linux.FileMode, flags uint32) (*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
|
||||
Unlink(ctx context.Context, name string) error
|
||||
|
||||
// Destroy destroys the registry.
|
||||
Destroy(context.Context)
|
||||
@@ -144,6 +149,43 @@ type View interface {
|
||||
waiter.Waitable
|
||||
}
|
||||
|
||||
// ReaderWriter provides a send and receive view into a queue.
|
||||
type ReaderWriter struct {
|
||||
*Queue
|
||||
|
||||
block bool
|
||||
}
|
||||
|
||||
// Reader provides a send-only view into a queue.
|
||||
type Reader struct {
|
||||
*Queue
|
||||
|
||||
block bool
|
||||
}
|
||||
|
||||
// Writer provides a receive-only view into a queue.
|
||||
type Writer struct {
|
||||
*Queue
|
||||
|
||||
block bool
|
||||
}
|
||||
|
||||
// NewView creates a new view into a queue and returns it.
|
||||
func NewView(q *Queue, rOnly, wOnly, readWrite, block bool) (View, error) {
|
||||
switch {
|
||||
case readWrite:
|
||||
return ReaderWriter{Queue: q, block: block}, nil
|
||||
case wOnly:
|
||||
return Writer{Queue: q, block: block}, nil
|
||||
case rOnly:
|
||||
return Reader{Queue: q, block: block}, nil
|
||||
default:
|
||||
// This case can't happen, due to O_RDONLY flag being 0 and O_WRONLY
|
||||
// being 1, so one of them must be true.
|
||||
return nil, linuxerr.EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
// Message holds a message exchanged through a Queue via mq_timedsend(2) and
|
||||
// mq_timedreceive(2), and additional info relating to the message.
|
||||
//
|
||||
@@ -243,3 +285,15 @@ func (q *Queue) EventUnregister(e *waiter.Entry) {
|
||||
q.senders.EventUnregister(e)
|
||||
q.receivers.EventUnregister(e)
|
||||
}
|
||||
|
||||
// HasPermissions returns true if the given credentials meet the access
|
||||
// permissions required by the queue.
|
||||
func (q *Queue) HasPermissions(creds *auth.Credentials, req fs.PermMask) bool {
|
||||
p := q.perms.Other
|
||||
if q.owner.UID == creds.EffectiveKUID {
|
||||
p = q.perms.User
|
||||
} else if creds.InGroup(q.owner.GID) {
|
||||
p = q.perms.Group
|
||||
}
|
||||
return p.SupersetOf(req)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user