mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
This mount option turns cgroup namespaces into delegation boundaries, which results in the following two behaviors: First, resource control. A cgroup's resource-control files carve out the parent's resources, so they belong to the delegater, not to the subtree's occupants. Accordingly, tasks in a non-init cgroup namespace may no longer write to interface files of their own namespace root. The delegatable files (cgroup.procs, cgroup.subtree_control) are exempt. Subtree tasks cannot change what share of resources the subtree receives, but remain free to organize processes and distribute that share within it. Second, containment. Migration (including via CLONE_INTO_CGROUP) now requires that both the source and destination cgroups be descendants of the cgroupns root of the task performing the migration, failing with ENOENT otherwise (actually it is the cgroupns of the task that opened the fd for the migration file: this detail forces me to snapshot the cgroupns against the fd, increasing the size of this patch). PiperOrigin-RevId: 954892289
273 lines
9.4 KiB
Go
273 lines
9.4 KiB
Go
// Copyright 2019 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 kernfs
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"gvisor.dev/gvisor/pkg/abi/linux"
|
|
"gvisor.dev/gvisor/pkg/atomicbitops"
|
|
"gvisor.dev/gvisor/pkg/context"
|
|
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
|
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
|
"gvisor.dev/gvisor/pkg/usermem"
|
|
"gvisor.dev/gvisor/pkg/waiter"
|
|
)
|
|
|
|
// DataSourceProvider represents a dynamic data source provider for DynamicBytesFile.
|
|
type DataSourceProvider interface {
|
|
DataSource(ctx context.Context) (vfs.DynamicBytesSource, error)
|
|
}
|
|
|
|
// DynamicBytesFile implements kernfs.Inode and represents a read-only file
|
|
// whose contents are backed by a vfs.DynamicBytesSource. If data additionally
|
|
// implements vfs.WritableDynamicBytesSource, the file also supports dispatching
|
|
// writes to the implementer, but note that this will not update the source data.
|
|
//
|
|
// Must be instantiated with NewDynamicBytesFile or initialized with Init
|
|
// before first use.
|
|
//
|
|
// +stateify savable
|
|
type DynamicBytesFile struct {
|
|
InodeAttrs
|
|
InodeNoStatFS
|
|
InodeNoopRefCount
|
|
InodeNotAnonymous
|
|
InodeNotDirectory
|
|
InodeNotSymlink
|
|
InodeWatches
|
|
InodeFSOwned
|
|
|
|
locks vfs.FileLocks
|
|
// data can additionally implement vfs.WritableDynamicBytesSource to support
|
|
// writes. This field cannot be changed to a different bytes source after
|
|
// Init.
|
|
data vfs.DynamicBytesSource
|
|
|
|
// dataSourceProvider is used to get the data source when the file is opened.
|
|
// If it is nil, the data source is fixed to the value in the data field.
|
|
dataSourceProvider DataSourceProvider
|
|
}
|
|
|
|
var _ Inode = (*DynamicBytesFile)(nil)
|
|
|
|
// Init initializes a dynamic bytes file.
|
|
func (f *DynamicBytesFile) Init(ctx context.Context, creds *auth.Credentials, devMajor, devMinor uint32, ino uint64, data vfs.DynamicBytesSource, perm linux.FileMode) {
|
|
if perm&^linux.PermissionsMask != 0 {
|
|
panic(fmt.Sprintf("Only permission mask must be set: %x", perm&linux.PermissionsMask))
|
|
}
|
|
f.InodeAttrs.Init(ctx, creds, devMajor, devMinor, ino, linux.ModeRegular|perm)
|
|
f.data = data
|
|
}
|
|
|
|
// InitWithIDs initializes a dynamic bytes file with the given owner UIDs and GIDs.
|
|
func (f *DynamicBytesFile) InitWithIDs(ctx context.Context, uid auth.KUID, gid auth.KGID, devMajor, devMinor uint32, ino uint64, data vfs.DynamicBytesSource, perm linux.FileMode) {
|
|
if perm&^linux.PermissionsMask != 0 {
|
|
panic(fmt.Sprintf("Only permission mask must be set: %x", perm&linux.PermissionsMask))
|
|
}
|
|
f.InodeAttrs.InitWithIDs(ctx, uid, gid, devMajor, devMinor, ino, linux.ModeRegular|perm)
|
|
f.data = data
|
|
}
|
|
|
|
// Open implements Inode.Open.
|
|
func (f *DynamicBytesFile) Open(ctx context.Context, rp *vfs.ResolvingPath, d *Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
|
|
data, err := f.Data(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if src, ok := data.(vfs.PollableDynamicBytesSource); ok {
|
|
if poller := src.GetDynamicBytesPoller(ctx); poller != nil {
|
|
// Pollable fd
|
|
fd := &PollableDynamicBytesFD{}
|
|
if err := fd.Init(rp.Mount(), d, data, &f.locks, opts.Flags, rp.Credentials(), poller); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &fd.vfsfd, nil
|
|
}
|
|
}
|
|
|
|
// Non-pollable fd
|
|
fd := &DynamicBytesFD{}
|
|
if err := fd.Init(rp.Mount(), d, data, &f.locks, opts.Flags, rp.Credentials()); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &fd.vfsfd, nil
|
|
}
|
|
|
|
// SetStat implements Inode.SetStat. By default DynamicBytesFile doesn't allow
|
|
// inode attributes to be changed. Override SetStat() making it call
|
|
// f.InodeAttrs to allow it.
|
|
func (*DynamicBytesFile) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
|
|
return linuxerr.EPERM
|
|
}
|
|
|
|
// Locks returns the file locks for this file.
|
|
func (f *DynamicBytesFile) Locks() *vfs.FileLocks {
|
|
return &f.locks
|
|
}
|
|
|
|
// SetDataSourceProvider sets a DataSourceProvider for the file.
|
|
func (f *DynamicBytesFile) SetDataSourceProvider(p DataSourceProvider) {
|
|
f.dataSourceProvider = p
|
|
}
|
|
|
|
// Data returns the underlying data source.
|
|
func (f *DynamicBytesFile) Data(ctx context.Context) (vfs.DynamicBytesSource, error) {
|
|
if f.dataSourceProvider != nil {
|
|
return f.dataSourceProvider.DataSource(ctx)
|
|
}
|
|
return f.data, nil
|
|
}
|
|
|
|
// DynamicBytesFD implements vfs.FileDescriptionImpl for an FD backed by a
|
|
// DynamicBytesFile.
|
|
//
|
|
// Must be initialized with Init before first use.
|
|
//
|
|
// +stateify savable
|
|
type DynamicBytesFD struct {
|
|
vfs.FileDescriptionDefaultImpl
|
|
vfs.DynamicBytesFileDescriptionImpl
|
|
vfs.LockFD
|
|
|
|
vfsfd vfs.FileDescription
|
|
inode Inode
|
|
}
|
|
|
|
// Init initializes a DynamicBytesFD.
|
|
func (fd *DynamicBytesFD) Init(m *vfs.Mount, d *Dentry, data vfs.DynamicBytesSource, locks *vfs.FileLocks, flags uint32, creds *auth.Credentials) error {
|
|
return fd.InitWithImpl(fd, m, d, data, locks, flags, creds)
|
|
}
|
|
|
|
// InitWithImpl initializes a DynamicBytesFD, registering impl as the
|
|
// vfs.FileDescriptionImpl. Use it when embedding DynamicBytesFD in another
|
|
// file description implementation: pass the outer implementation so that its
|
|
// method overrides are honored.
|
|
func (fd *DynamicBytesFD) InitWithImpl(impl vfs.FileDescriptionImpl, m *vfs.Mount, d *Dentry, data vfs.DynamicBytesSource, locks *vfs.FileLocks, flags uint32, creds *auth.Credentials) error {
|
|
fd.LockFD.Init(locks)
|
|
if err := fd.vfsfd.Init(impl, flags, creds, m, d.VFSDentry(),
|
|
&vfs.FileDescriptionOptions{
|
|
DenySpliceIn: true,
|
|
},
|
|
); err != nil {
|
|
return err
|
|
}
|
|
fd.inode = d.inode
|
|
fd.DynamicBytesFileDescriptionImpl.Init(&fd.vfsfd, data)
|
|
return nil
|
|
}
|
|
|
|
// VFSFileDescription returns a pointer to the vfs.FileDescription
|
|
// representing this object.
|
|
func (fd *DynamicBytesFD) VFSFileDescription() *vfs.FileDescription {
|
|
return &fd.vfsfd
|
|
}
|
|
|
|
// Seek implements vfs.FileDescriptionImpl.Seek.
|
|
func (fd *DynamicBytesFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
|
|
return fd.DynamicBytesFileDescriptionImpl.Seek(ctx, offset, whence)
|
|
}
|
|
|
|
// Read implements vfs.FileDescriptionImpl.Read.
|
|
func (fd *DynamicBytesFD) 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 *DynamicBytesFD) 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 *DynamicBytesFD) 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 *DynamicBytesFD) 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 *DynamicBytesFD) Release(context.Context) {}
|
|
|
|
// Stat implements vfs.FileDescriptionImpl.Stat.
|
|
func (fd *DynamicBytesFD) 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 *DynamicBytesFD) SetStat(context.Context, vfs.SetStatOptions) error {
|
|
// DynamicBytesFiles are immutable.
|
|
return linuxerr.EPERM
|
|
}
|
|
|
|
// PollableDynamicBytesFD represents a DynamicBytesFD that can be monitored
|
|
// for changes using select, poll, or epoll.
|
|
//
|
|
// +stateify savable
|
|
type PollableDynamicBytesFD struct {
|
|
DynamicBytesFD
|
|
|
|
poller *vfs.DynamicBytesPoller
|
|
pollSnapshot atomicbitops.Uint64
|
|
}
|
|
|
|
// Init initializes a PollableDynamicBytesFD.
|
|
func (fd *PollableDynamicBytesFD) Init(m *vfs.Mount, d *Dentry, data vfs.DynamicBytesSource, locks *vfs.FileLocks, flags uint32, creds *auth.Credentials, poller *vfs.DynamicBytesPoller) error {
|
|
fd.LockFD.Init(locks)
|
|
if err := fd.vfsfd.Init(fd, flags, creds, m, d.VFSDentry(),
|
|
&vfs.FileDescriptionOptions{DenySpliceIn: true}); err != nil {
|
|
return err
|
|
}
|
|
fd.inode = d.inode
|
|
fd.DynamicBytesFileDescriptionImpl.Init(&fd.vfsfd, data)
|
|
fd.poller = poller
|
|
fd.pollSnapshot.Store(poller.Seq())
|
|
return nil
|
|
}
|
|
|
|
// Epollable implements FileDescriptionImpl.Epollable.
|
|
func (fd *PollableDynamicBytesFD) Epollable() bool {
|
|
return true
|
|
}
|
|
|
|
// Readiness implements waiter.Waitable.Readiness.
|
|
func (fd *PollableDynamicBytesFD) Readiness(mask waiter.EventMask) waiter.EventMask {
|
|
cur := fd.poller.Seq()
|
|
events := waiter.ReadableEvents | waiter.WritableEvents
|
|
if cur != fd.pollSnapshot.Load() {
|
|
fd.pollSnapshot.Store(cur)
|
|
events = waiter.ReadableEvents | vfs.DynamicBytesPollEvents
|
|
}
|
|
return events & mask
|
|
}
|
|
|
|
// EventRegister implements waiter.Waitable.EventRegister.
|
|
func (fd *PollableDynamicBytesFD) EventRegister(e *waiter.Entry) error {
|
|
fd.poller.EventRegister(e)
|
|
return nil
|
|
}
|
|
|
|
// EventUnregister implements waiter.Waitable.EventUnregister.
|
|
func (fd *PollableDynamicBytesFD) EventUnregister(e *waiter.Entry) {
|
|
fd.poller.EventUnregister(e)
|
|
}
|