mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
This renames FDMap to FDTable and drops the kernel.FD type, which had an entire package to itself and didn't serve much use (it was freely cast between types, and served as more of an annoyance than providing any protection.) Based on BenchmarkFDLookupAndDecRef-12, we can expect 5-10 ns per lookup operation, and 10-15 ns per concurrent lookup operation of savings. This also fixes two tangential usage issues with the FDMap. Namely, non-atomic use of NewFDFrom and associated calls to Remove (that are both racy and fail to drop the reference on the underlying file.) PiperOrigin-RevId: 256285890
138 lines
4.2 KiB
Go
138 lines
4.2 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 linux
|
|
|
|
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/syserror"
|
|
)
|
|
|
|
// Sync implements linux system call sync(2).
|
|
func Sync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
|
t.MountNamespace().SyncAll(t)
|
|
// Sync is always successful.
|
|
return 0, nil, nil
|
|
}
|
|
|
|
// Syncfs implements linux system call syncfs(2).
|
|
func Syncfs(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
|
fd := args[0].Int()
|
|
|
|
file := t.GetFile(fd)
|
|
if file == nil {
|
|
return 0, nil, syserror.EBADF
|
|
}
|
|
defer file.DecRef()
|
|
|
|
// Use "sync-the-world" for now, it's guaranteed that fd is at least
|
|
// on the root filesystem.
|
|
return Sync(t, args)
|
|
}
|
|
|
|
// Fsync implements linux syscall fsync(2).
|
|
func Fsync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
|
fd := args[0].Int()
|
|
|
|
file := t.GetFile(fd)
|
|
if file == nil {
|
|
return 0, nil, syserror.EBADF
|
|
}
|
|
defer file.DecRef()
|
|
|
|
err := file.Fsync(t, 0, fs.FileMaxOffset, fs.SyncAll)
|
|
return 0, nil, syserror.ConvertIntr(err, kernel.ERESTARTSYS)
|
|
}
|
|
|
|
// Fdatasync implements linux syscall fdatasync(2).
|
|
//
|
|
// At the moment, it just calls Fsync, which is a big hammer, but correct.
|
|
func Fdatasync(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
|
fd := args[0].Int()
|
|
|
|
file := t.GetFile(fd)
|
|
if file == nil {
|
|
return 0, nil, syserror.EBADF
|
|
}
|
|
defer file.DecRef()
|
|
|
|
err := file.Fsync(t, 0, fs.FileMaxOffset, fs.SyncData)
|
|
return 0, nil, syserror.ConvertIntr(err, kernel.ERESTARTSYS)
|
|
}
|
|
|
|
// SyncFileRange implements linux syscall sync_file_rage(2)
|
|
func SyncFileRange(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
|
var err error
|
|
|
|
fd := args[0].Int()
|
|
offset := args[1].Int64()
|
|
nbytes := args[2].Int64()
|
|
uflags := args[3].Uint()
|
|
|
|
if offset < 0 || offset+nbytes < offset {
|
|
return 0, nil, syserror.EINVAL
|
|
}
|
|
|
|
if uflags&^(linux.SYNC_FILE_RANGE_WAIT_BEFORE|
|
|
linux.SYNC_FILE_RANGE_WRITE|
|
|
linux.SYNC_FILE_RANGE_WAIT_AFTER) != 0 {
|
|
return 0, nil, syserror.EINVAL
|
|
}
|
|
|
|
if nbytes == 0 {
|
|
nbytes = fs.FileMaxOffset
|
|
}
|
|
|
|
file := t.GetFile(fd)
|
|
if file == nil {
|
|
return 0, nil, syserror.EBADF
|
|
}
|
|
defer file.DecRef()
|
|
|
|
// SYNC_FILE_RANGE_WAIT_BEFORE waits upon write-out of all pages in the
|
|
// specified range that have already been submitted to the device
|
|
// driver for write-out before performing any write.
|
|
if uflags&linux.SYNC_FILE_RANGE_WAIT_BEFORE != 0 &&
|
|
uflags&linux.SYNC_FILE_RANGE_WAIT_AFTER == 0 {
|
|
t.Kernel().EmitUnimplementedEvent(t)
|
|
return 0, nil, syserror.ENOSYS
|
|
}
|
|
|
|
// SYNC_FILE_RANGE_WRITE initiates write-out of all dirty pages in the
|
|
// specified range which are not presently submitted write-out.
|
|
//
|
|
// It looks impossible to implement this functionality without a
|
|
// massive rework of the vfs subsystem. file.Fsync() take a file lock
|
|
// for the entire operation, so even if it is running in a go routing,
|
|
// it blocks other file operations instead of flushing data in the
|
|
// background.
|
|
//
|
|
// It should be safe to skipped this flag while nobody uses
|
|
// SYNC_FILE_RANGE_WAIT_BEFORE.
|
|
|
|
// SYNC_FILE_RANGE_WAIT_AFTER waits upon write-out of all pages in the
|
|
// range after performing any write.
|
|
//
|
|
// In Linux, sync_file_range() doesn't writes out the file's
|
|
// meta-data, but fdatasync() does if a file size is changed.
|
|
if uflags&linux.SYNC_FILE_RANGE_WAIT_AFTER != 0 {
|
|
err = file.Fsync(t, offset, fs.FileMaxOffset, fs.SyncData)
|
|
}
|
|
|
|
return 0, nil, syserror.ConvertIntr(err, kernel.ERESTARTSYS)
|
|
}
|