gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/fsimpl/mqfs/registry.go (about)

     1  // Copyright 2021 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package mqfs
    16  
    17  import (
    18  	"gvisor.dev/gvisor/pkg/abi/linux"
    19  	"gvisor.dev/gvisor/pkg/context"
    20  	"gvisor.dev/gvisor/pkg/errors/linuxerr"
    21  	"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
    22  	"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
    23  	"gvisor.dev/gvisor/pkg/sentry/kernel/mq"
    24  	"gvisor.dev/gvisor/pkg/sentry/vfs"
    25  )
    26  
    27  const (
    28  	maxCachedDentries = 1000
    29  )
    30  
    31  // RegistryImpl implements mq.RegistryImpl. It implements the interface using
    32  // the message queue filesystem, and is provided to mq.Registry at
    33  // initialization.
    34  //
    35  // RegistryImpl is not thread-safe, so it is the responsibility of the user
    36  // (the containing mq.Registry) to protect using a lock.
    37  //
    38  // +stateify savable
    39  type RegistryImpl struct {
    40  	// root is the root dentry of the mq filesystem. Its main usage is to
    41  	// retrieve the root inode, which we use to add, remove, and lookup message
    42  	// queues.
    43  	//
    44  	// We hold a reference on root and release when the registry is destroyed.
    45  	root *kernfs.Dentry
    46  
    47  	// fs is the filesystem backing this registry, used mainly to initialize
    48  	// new inodes.
    49  	fs *filesystem
    50  
    51  	// mount is the mount point used for this filesystem.
    52  	mount *vfs.Mount
    53  }
    54  
    55  // NewRegistryImpl returns a new, initialized RegistryImpl, and takes a
    56  // reference on root.
    57  func NewRegistryImpl(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials) (*RegistryImpl, error) {
    58  	devMinor, err := vfsObj.GetAnonBlockDevMinor()
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	fs := &filesystem{
    64  		devMinor:   devMinor,
    65  		Filesystem: kernfs.Filesystem{MaxCachedDentries: maxCachedDentries},
    66  	}
    67  	fs.VFSFilesystem().Init(vfsObj, &FilesystemType{}, fs)
    68  	vfsfs := fs.VFSFilesystem()
    69  	// NewDisconnectedMount will obtain a ref on dentry and vfsfs which is
    70  	// transferred to mount. vfsfs was initiated with 1 ref already. So get rid
    71  	// of the extra ref.
    72  	defer vfsfs.DecRef(ctx)
    73  
    74  	// dentry is initialized with 1 ref which is transferred to fs.
    75  	var dentry kernfs.Dentry
    76  	dentry.InitRoot(&fs.Filesystem, fs.newRootInode(ctx, creds))
    77  
    78  	mount := vfsObj.NewDisconnectedMount(vfsfs, dentry.VFSDentry(), &vfs.MountOptions{})
    79  
    80  	return &RegistryImpl{
    81  		root:  &dentry,
    82  		fs:    fs,
    83  		mount: mount,
    84  	}, nil
    85  }
    86  
    87  // Get implements mq.RegistryImpl.Get.
    88  func (r *RegistryImpl) Get(ctx context.Context, name string, access mq.AccessType, block bool, flags uint32) (*vfs.FileDescription, bool, error) {
    89  	inode, err := r.root.Inode().(*rootInode).Lookup(ctx, name)
    90  	if err != nil {
    91  		return nil, false, nil
    92  	}
    93  
    94  	qInode := inode.(*queueInode)
    95  	if !qInode.queue.HasPermissions(auth.CredentialsFromContext(ctx), perm(access)) {
    96  		// "The queue exists, but the caller does not have permission to
    97  		//  open it in the specified mode."
    98  		return nil, false, linuxerr.EACCES
    99  	}
   100  
   101  	fd, err := r.newFD(ctx, qInode.queue, qInode, access, block, flags)
   102  	if err != nil {
   103  		return nil, false, err
   104  	}
   105  	return fd, true, nil
   106  }
   107  
   108  // New implements mq.RegistryImpl.New.
   109  func (r *RegistryImpl) New(ctx context.Context, name string, q *mq.Queue, access mq.AccessType, block bool, perm linux.FileMode, flags uint32) (*vfs.FileDescription, error) {
   110  	root := r.root.Inode().(*rootInode)
   111  	qInode := r.fs.newQueueInode(ctx, auth.CredentialsFromContext(ctx), q, perm).(*queueInode)
   112  	err := root.Insert(name, qInode)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	return r.newFD(ctx, q, qInode, access, block, flags)
   117  }
   118  
   119  // Unlink implements mq.RegistryImpl.Unlink.
   120  func (r *RegistryImpl) Unlink(ctx context.Context, name string) error {
   121  	creds := auth.CredentialsFromContext(ctx)
   122  	if err := r.root.Inode().CheckPermissions(ctx, creds, vfs.MayWrite|vfs.MayExec); err != nil {
   123  		return err
   124  	}
   125  
   126  	root := r.root.Inode().(*rootInode)
   127  	inode, err := root.Lookup(ctx, name)
   128  	if err != nil {
   129  		return err
   130  	}
   131  	defer inode.DecRef(ctx)
   132  	return root.Unlink(ctx, name, inode)
   133  }
   134  
   135  // Destroy implements mq.RegistryImpl.Destroy.
   136  func (r *RegistryImpl) Destroy(ctx context.Context) {
   137  	r.root.DecRef(ctx)
   138  	r.mount.DecRef(ctx)
   139  }
   140  
   141  // newFD returns a new file description created using the given queue and inode.
   142  func (r *RegistryImpl) newFD(ctx context.Context, q *mq.Queue, inode *queueInode, access mq.AccessType, block bool, flags uint32) (*vfs.FileDescription, error) {
   143  	view, err := mq.NewView(q, access, block)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	var dentry kernfs.Dentry
   149  	dentry.Init(&r.fs.Filesystem, inode)
   150  	defer dentry.DecRef(ctx)
   151  
   152  	fd := &queueFD{queue: view}
   153  	err = fd.Init(r.mount, &dentry, inode.queue, inode.Locks(), flags)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  	return &fd.vfsfd, nil
   158  }
   159  
   160  // perm returns a permission mask created using given flags.
   161  func perm(access mq.AccessType) vfs.AccessTypes {
   162  	switch access {
   163  	case mq.ReadWrite:
   164  		return vfs.MayRead | vfs.MayWrite
   165  	case mq.WriteOnly:
   166  		return vfs.MayWrite
   167  	case mq.ReadOnly:
   168  		return vfs.MayRead
   169  	default:
   170  		return 0 // Can't happen, see NewView.
   171  	}
   172  }