Files
visor/pkg/sentry/fsimpl/kernfs/dynamic_bytes_file.go
Shailend ChandandgVisor bot c8a8d4a815 cgroup2: Support the nsdelegate mount option
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
2026-07-27 16:30:46 -07:00

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)
}