259 lines
7.0 KiB
Go
259 lines
7.0 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/sentry/arch"
|
|
"gvisor.dev/gvisor/pkg/sentry/fs"
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
|
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
|
"gvisor.dev/gvisor/pkg/sync"
|
|
"gvisor.dev/gvisor/pkg/syserror"
|
|
"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 fs.FileAsync with the given file
|
|
// descriptor.
|
|
func New(fd int) func() fs.FileAsync {
|
|
return func() fs.FileAsync {
|
|
return &FileAsync{fd: fd}
|
|
}
|
|
}
|
|
|
|
// NewVFS2 returns a function that creates a new vfs.FileAsync with the given
|
|
// file descriptor.
|
|
func NewVFS2(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 registeration 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 sync.Mutex `state:"nosave"`
|
|
|
|
// mu protects all following fields.
|
|
//
|
|
// Lock ordering: e.mu, mu.
|
|
mu sync.Mutex `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
|
|
}
|
|
|
|
// Callback sends a signal.
|
|
func (a *FileAsync) Callback(e *waiter.Entry, mask waiter.EventMask) {
|
|
a.mu.Lock()
|
|
defer a.mu.Unlock()
|
|
if !a.registered {
|
|
return
|
|
}
|
|
t := a.recipientT
|
|
tg := a.recipientTG
|
|
if a.recipientPG != nil {
|
|
tg = a.recipientPG.Originator()
|
|
}
|
|
if tg != nil {
|
|
t = tg.Leader()
|
|
}
|
|
if t == nil {
|
|
// No recipient has been registered.
|
|
return
|
|
}
|
|
c := t.Credentials()
|
|
// Logic from sigio_perm in fs/fcntl.c.
|
|
permCheck := (a.requester.EffectiveKUID == 0 ||
|
|
a.requester.EffectiveKUID == c.SavedKUID ||
|
|
a.requester.EffectiveKUID == c.RealKUID ||
|
|
a.requester.RealKUID == c.SavedKUID ||
|
|
a.requester.RealKUID == c.RealKUID)
|
|
if !permCheck {
|
|
return
|
|
}
|
|
signalInfo := &arch.SignalInfo{
|
|
Signo: int32(linux.SIGIO),
|
|
Code: arch.SignalInfoKernel,
|
|
}
|
|
if a.signal != 0 {
|
|
signalInfo.Signo = int32(a.signal)
|
|
signalInfo.SetFD(uint32(a.fd))
|
|
var band int64
|
|
for m, bandCode := range bandTable {
|
|
if m&mask != 0 {
|
|
band |= bandCode
|
|
}
|
|
}
|
|
signalInfo.SetBand(band)
|
|
}
|
|
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) {
|
|
a.regMu.Lock()
|
|
defer a.regMu.Unlock()
|
|
a.mu.Lock()
|
|
|
|
if a.registered {
|
|
a.mu.Unlock()
|
|
panic("registering already registered file")
|
|
}
|
|
|
|
if a.e.Callback == nil {
|
|
a.e.Callback = a
|
|
}
|
|
a.registered = true
|
|
|
|
a.mu.Unlock()
|
|
w.EventRegister(&a.e, waiter.ReadableEvents|waiter.WritableEvents|waiter.EventErr|waiter.EventHUp)
|
|
}
|
|
|
|
// 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 syserror.EINVAL
|
|
}
|
|
a.mu.Lock()
|
|
defer a.mu.Unlock()
|
|
a.signal = signal
|
|
return nil
|
|
}
|