Files
Andrei Vagin 5f4abad306 Fix a few typos
It is an idea of running codespell as part of our presubmit checks.
Before enabling it for new changes, let's fix what it has found.

Signed-off-by: Andrei Vagin <avagin@gmail.com>
2023-10-25 12:13:42 -07:00

253 lines
6.9 KiB
Go

// Copyright 2018 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 fasync provides FIOASYNC related functionality.
package fasync
import (
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/waiter"
)
// Table to convert waiter event masks into si_band siginfo codes.
// Taken from fs/fcntl.c:band_table.
var bandTable = map[waiter.EventMask]int64{
// POLL_IN
waiter.EventIn: linux.EPOLLIN | linux.EPOLLRDNORM,
// POLL_OUT
waiter.EventOut: linux.EPOLLOUT | linux.EPOLLWRNORM | linux.EPOLLWRBAND,
// POLL_ERR
waiter.EventErr: linux.EPOLLERR,
// POLL_PRI
waiter.EventPri: linux.EPOLLPRI | linux.EPOLLRDBAND,
// POLL_HUP
waiter.EventHUp: linux.EPOLLHUP | linux.EPOLLERR,
}
// New returns a function that creates a new vfs.FileAsync with the given
// file descriptor.
func New(fd int) func() vfs.FileAsync {
return func() vfs.FileAsync {
return &FileAsync{fd: fd}
}
}
// FileAsync sends signals when the registered file is ready for IO.
//
// +stateify savable
type FileAsync struct {
// e is immutable after first use (which is protected by mu below).
e waiter.Entry
// fd is the file descriptor to notify about.
// It is immutable, set at allocation time. This matches Linux semantics in
// fs/fcntl.c:fasync_helper.
// The fd value is passed to the signal recipient in siginfo.si_fd.
fd int
// regMu protects registration and unregistration actions on e.
//
// regMu must be held while registration decisions are being made
// through the registration action itself.
//
// Lock ordering: regMu, mu.
regMu regMutex `state:"nosave"`
// mu protects all following fields.
//
// Lock ordering: e.mu, mu.
mu fileMutex `state:"nosave"`
requester *auth.Credentials
registered bool
// signal is the signal to deliver upon I/O being available.
// The default value ("zero signal") means the default SIGIO signal will be
// delivered.
signal linux.Signal
// Only one of the following is allowed to be non-nil.
recipientPG *kernel.ProcessGroup
recipientTG *kernel.ThreadGroup
recipientT *kernel.Task
}
// NotifyEvent implements waiter.EventListener.NotifyEvent.
func (a *FileAsync) NotifyEvent(mask waiter.EventMask) {
a.mu.Lock()
if !a.registered {
a.mu.Unlock()
return
}
// Read all the required fields which are lock protected from FileAsync
// and release the lock.
t := a.recipientT
tg := a.recipientTG
creds := a.requester
sig := a.signal
if a.recipientPG != nil {
tg = a.recipientPG.Originator()
}
a.mu.Unlock()
if tg != nil {
t = tg.Leader()
}
if t == nil {
// No recipient has been registered.
return
}
tCreds := t.Credentials()
// Logic from sigio_perm in fs/fcntl.c.
permCheck := (creds.EffectiveKUID == 0 ||
creds.EffectiveKUID == tCreds.SavedKUID ||
creds.EffectiveKUID == tCreds.RealKUID ||
creds.RealKUID == tCreds.SavedKUID ||
creds.RealKUID == tCreds.RealKUID)
if !permCheck {
return
}
signalInfo := &linux.SignalInfo{
Signo: int32(linux.SIGIO),
Code: linux.SI_KERNEL,
}
if sig != 0 {
signalInfo.Signo = int32(sig)
signalInfo.SetFD(uint32(a.fd))
var band int64
for m, bandCode := range bandTable {
if m&mask != 0 {
band |= bandCode
}
}
signalInfo.SetBand(band)
}
if tg != nil {
t.SendGroupSignal(signalInfo)
} else {
t.SendSignal(signalInfo)
}
}
// Register sets the file which will be monitored for IO events.
//
// The file must not be currently registered.
func (a *FileAsync) Register(w waiter.Waitable) error {
a.regMu.Lock()
defer a.regMu.Unlock()
a.mu.Lock()
if a.registered {
a.mu.Unlock()
panic("registering already registered file")
}
a.e.Init(a, waiter.ReadableEvents|waiter.WritableEvents|waiter.EventErr|waiter.EventHUp)
a.registered = true
a.mu.Unlock()
return w.EventRegister(&a.e)
}
// Unregister stops monitoring a file.
//
// The file must be currently registered.
func (a *FileAsync) Unregister(w waiter.Waitable) {
a.regMu.Lock()
defer a.regMu.Unlock()
a.mu.Lock()
if !a.registered {
a.mu.Unlock()
panic("unregistering unregistered file")
}
a.registered = false
a.mu.Unlock()
w.EventUnregister(&a.e)
}
// Owner returns who is currently getting signals. All return values will be
// nil if no one is set to receive signals.
func (a *FileAsync) Owner() (*kernel.Task, *kernel.ThreadGroup, *kernel.ProcessGroup) {
a.mu.Lock()
defer a.mu.Unlock()
return a.recipientT, a.recipientTG, a.recipientPG
}
// SetOwnerTask sets the owner (who will receive signals) to a specified task.
// Only this owner will receive signals.
func (a *FileAsync) SetOwnerTask(requester *kernel.Task, recipient *kernel.Task) {
a.mu.Lock()
defer a.mu.Unlock()
a.requester = requester.Credentials()
a.recipientT = recipient
a.recipientTG = nil
a.recipientPG = nil
}
// SetOwnerThreadGroup sets the owner (who will receive signals) to a specified
// thread group. Only this owner will receive signals.
func (a *FileAsync) SetOwnerThreadGroup(requester *kernel.Task, recipient *kernel.ThreadGroup) {
a.mu.Lock()
defer a.mu.Unlock()
a.requester = requester.Credentials()
a.recipientT = nil
a.recipientTG = recipient
a.recipientPG = nil
}
// SetOwnerProcessGroup sets the owner (who will receive signals) to a
// specified process group. Only this owner will receive signals.
func (a *FileAsync) SetOwnerProcessGroup(requester *kernel.Task, recipient *kernel.ProcessGroup) {
a.mu.Lock()
defer a.mu.Unlock()
a.requester = requester.Credentials()
a.recipientT = nil
a.recipientTG = nil
a.recipientPG = recipient
}
// ClearOwner unsets the current signal recipient.
func (a *FileAsync) ClearOwner() {
a.mu.Lock()
defer a.mu.Unlock()
a.requester = nil
a.recipientT = nil
a.recipientTG = nil
a.recipientPG = nil
}
// Signal returns which signal will be sent to the signal recipient.
// A value of zero means the signal to deliver wasn't customized, which means
// the default signal (SIGIO) will be delivered.
func (a *FileAsync) Signal() linux.Signal {
a.mu.Lock()
defer a.mu.Unlock()
return a.signal
}
// SetSignal overrides which signal to send when I/O is available.
// The default behavior can be reset by specifying signal zero, which means
// to send SIGIO.
func (a *FileAsync) SetSignal(signal linux.Signal) error {
if signal != 0 && !signal.IsValid() {
return linuxerr.EINVAL
}
a.mu.Lock()
defer a.mu.Unlock()
a.signal = signal
return nil
}