gvisor/pkg/sentry/kernel/fd_table_unsafe.go

104 lines
2.6 KiB
Go

// Copyright 2018 Google LLC
//
// 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 kernel
import (
"sync/atomic"
"unsafe"
"gvisor.dev/gvisor/pkg/sentry/fs"
)
type descriptorTable struct {
// slice is a *[]unsafe.Pointer, where each element is actually
// *descriptor object, updated atomically.
//
// Changes to the slice itself requiring holding FDTable.mu.
slice unsafe.Pointer `state:".(map[int32]*descriptor)"`
}
// init initializes the table.
func (f *FDTable) init() {
var slice []unsafe.Pointer // Empty slice.
atomic.StorePointer(&f.slice, unsafe.Pointer(&slice))
}
// get gets a file entry.
//
// The boolean indicates whether this was in range.
//
//go:nosplit
func (f *FDTable) get(fd int32) (*fs.File, FDFlags, bool) {
slice := *(*[]unsafe.Pointer)(atomic.LoadPointer(&f.slice))
if fd >= int32(len(slice)) {
return nil, FDFlags{}, false
}
d := (*descriptor)(atomic.LoadPointer(&slice[fd]))
if d == nil {
return nil, FDFlags{}, true
}
return d.file, d.flags, true
}
// set sets an entry.
//
// This handles accounting changes, as well as acquiring and releasing the
// reference needed by the table iff the file is different.
//
// Precondition: mu must be held.
func (f *FDTable) set(fd int32, file *fs.File, flags FDFlags) {
slice := *(*[]unsafe.Pointer)(atomic.LoadPointer(&f.slice))
// Grow the table as required.
if last := int32(len(slice)); fd >= last {
end := fd + 1
if end < 2*last {
end = 2 * last
}
slice = append(slice, make([]unsafe.Pointer, end-last)...)
atomic.StorePointer(&f.slice, unsafe.Pointer(&slice))
}
// Create the new element.
var d *descriptor
if file != nil {
d = &descriptor{
file: file,
flags: flags,
}
}
// Update the single element.
orig := (*descriptor)(atomic.SwapPointer(&slice[fd], unsafe.Pointer(d)))
// Acquire a table reference.
if file != nil && (orig == nil || file != orig.file) {
file.IncRef()
}
// Drop the table reference.
if orig != nil && file != orig.file {
f.drop(orig.file)
}
// Adjust used.
switch {
case orig == nil && file != nil:
atomic.AddInt32(&f.used, 1)
case orig != nil && file == nil:
atomic.AddInt32(&f.used, -1)
}
}