gvisor/pkg/sentry/kernel/fasync/fasync.go

173 lines
4.6 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/fs"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/waiter"
)
// New creates a new FileAsync.
func New() fs.FileAsync {
return &FileAsync{}
}
// 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
// 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
// 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) {
a.mu.Lock()
if !a.registered {
a.mu.Unlock()
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.
a.mu.Unlock()
return
}
c := t.Credentials()
// Logic from sigio_perm in fs/fcntl.c.
if a.requester.EffectiveKUID == 0 ||
a.requester.EffectiveKUID == c.SavedKUID ||
a.requester.EffectiveKUID == c.RealKUID ||
a.requester.RealKUID == c.SavedKUID ||
a.requester.RealKUID == c.RealKUID {
t.SendSignal(kernel.SignalInfoPriv(linux.SIGIO))
}
a.mu.Unlock()
}
// 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.EventIn|waiter.EventOut|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
}