mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Delete VFS1 syscall handlers.
Directly use VFS2 syscall handlers. No need to override VFS2 handlers. Updates #1624 PiperOrigin-RevId: 488448348
This commit is contained in:
@@ -5,7 +5,6 @@ package(licenses = ["notice"])
|
||||
go_library(
|
||||
name = "syscalls",
|
||||
srcs = [
|
||||
"epoll.go",
|
||||
"syscalls.go",
|
||||
],
|
||||
visibility = ["//:sandbox"],
|
||||
@@ -14,8 +13,5 @@ go_library(
|
||||
"//pkg/errors/linuxerr",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/epoll",
|
||||
"//pkg/sentry/kernel/time",
|
||||
"//pkg/waiter",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
// 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 syscalls
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/epoll"
|
||||
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// CreateEpoll implements the epoll_create(2) linux syscall.
|
||||
func CreateEpoll(t *kernel.Task, closeOnExec bool) (int32, error) {
|
||||
file := epoll.NewEventPoll(t)
|
||||
defer file.DecRef(t)
|
||||
|
||||
fd, err := t.NewFDFrom(0, file, kernel.FDFlags{
|
||||
CloseOnExec: closeOnExec,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
// AddEpoll implements the epoll_ctl(2) linux syscall when op is EPOLL_CTL_ADD.
|
||||
func AddEpoll(t *kernel.Task, epfd int32, fd int32, flags epoll.EntryFlags, mask waiter.EventMask, userData [2]int32) error {
|
||||
// Get epoll from the file descriptor.
|
||||
epollfile := t.GetFile(epfd)
|
||||
if epollfile == nil {
|
||||
return linuxerr.EBADF
|
||||
}
|
||||
defer epollfile.DecRef(t)
|
||||
|
||||
// Get the target file id.
|
||||
file := t.GetFile(fd)
|
||||
if file == nil {
|
||||
return linuxerr.EBADF
|
||||
}
|
||||
defer file.DecRef(t)
|
||||
|
||||
// Extract the epollPoll operations.
|
||||
e, ok := epollfile.FileOperations.(*epoll.EventPoll)
|
||||
if !ok {
|
||||
return linuxerr.EBADF
|
||||
}
|
||||
|
||||
// Try to add the entry.
|
||||
return e.AddEntry(epoll.FileIdentifier{file, fd}, flags, mask, userData)
|
||||
}
|
||||
|
||||
// UpdateEpoll implements the epoll_ctl(2) linux syscall when op is EPOLL_CTL_MOD.
|
||||
func UpdateEpoll(t *kernel.Task, epfd int32, fd int32, flags epoll.EntryFlags, mask waiter.EventMask, userData [2]int32) error {
|
||||
// Get epoll from the file descriptor.
|
||||
epollfile := t.GetFile(epfd)
|
||||
if epollfile == nil {
|
||||
return linuxerr.EBADF
|
||||
}
|
||||
defer epollfile.DecRef(t)
|
||||
|
||||
// Get the target file id.
|
||||
file := t.GetFile(fd)
|
||||
if file == nil {
|
||||
return linuxerr.EBADF
|
||||
}
|
||||
defer file.DecRef(t)
|
||||
|
||||
// Extract the epollPoll operations.
|
||||
e, ok := epollfile.FileOperations.(*epoll.EventPoll)
|
||||
if !ok {
|
||||
return linuxerr.EBADF
|
||||
}
|
||||
|
||||
// Try to update the entry.
|
||||
return e.UpdateEntry(epoll.FileIdentifier{file, fd}, flags, mask, userData)
|
||||
}
|
||||
|
||||
// RemoveEpoll implements the epoll_ctl(2) linux syscall when op is EPOLL_CTL_DEL.
|
||||
func RemoveEpoll(t *kernel.Task, epfd int32, fd int32) error {
|
||||
// Get epoll from the file descriptor.
|
||||
epollfile := t.GetFile(epfd)
|
||||
if epollfile == nil {
|
||||
return linuxerr.EBADF
|
||||
}
|
||||
defer epollfile.DecRef(t)
|
||||
|
||||
// Get the target file id.
|
||||
file := t.GetFile(fd)
|
||||
if file == nil {
|
||||
return linuxerr.EBADF
|
||||
}
|
||||
defer file.DecRef(t)
|
||||
|
||||
// Extract the epollPoll operations.
|
||||
e, ok := epollfile.FileOperations.(*epoll.EventPoll)
|
||||
if !ok {
|
||||
return linuxerr.EBADF
|
||||
}
|
||||
|
||||
// Try to remove the entry.
|
||||
return e.RemoveEntry(t, epoll.FileIdentifier{file, fd})
|
||||
}
|
||||
|
||||
// WaitEpoll implements the epoll_wait(2) linux syscall.
|
||||
func WaitEpoll(t *kernel.Task, fd int32, max int, timeoutInNanos int64) ([]linux.EpollEvent, error) {
|
||||
// Get epoll from the file descriptor.
|
||||
epollfile := t.GetFile(fd)
|
||||
if epollfile == nil {
|
||||
return nil, linuxerr.EBADF
|
||||
}
|
||||
defer epollfile.DecRef(t)
|
||||
|
||||
// Extract the epollPoll operations.
|
||||
e, ok := epollfile.FileOperations.(*epoll.EventPoll)
|
||||
if !ok {
|
||||
return nil, linuxerr.EBADF
|
||||
}
|
||||
|
||||
// Try to read events and return right away if we got them or if the
|
||||
// caller requested a non-blocking "wait".
|
||||
r := e.ReadEvents(max)
|
||||
if len(r) != 0 || timeoutInNanos == 0 {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// We'll have to wait. Set up the timer if a timeout was specified and
|
||||
// and register with the epoll object for readability events.
|
||||
var haveDeadline bool
|
||||
var deadline ktime.Time
|
||||
if timeoutInNanos > 0 {
|
||||
timeoutDur := time.Duration(timeoutInNanos) * time.Nanosecond
|
||||
deadline = t.Kernel().MonotonicClock().Now().Add(timeoutDur)
|
||||
haveDeadline = true
|
||||
}
|
||||
|
||||
w, ch := waiter.NewChannelEntry(waiter.ReadableEvents)
|
||||
e.EventRegister(&w)
|
||||
defer e.EventUnregister(&w)
|
||||
|
||||
// Try to read the events again until we succeed, timeout or get
|
||||
// interrupted.
|
||||
for {
|
||||
r = e.ReadEvents(max)
|
||||
if len(r) != 0 {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
|
||||
if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,8 @@ go_library(
|
||||
name = "linux",
|
||||
srcs = [
|
||||
"error.go",
|
||||
"flags.go",
|
||||
"linux64.go",
|
||||
"path.go",
|
||||
"points.go",
|
||||
"sigset.go",
|
||||
"sys_aio.go",
|
||||
@@ -21,17 +21,19 @@ go_library(
|
||||
"sys_getdents.go",
|
||||
"sys_identity.go",
|
||||
"sys_inotify.go",
|
||||
"sys_lseek.go",
|
||||
"sys_iouring.go",
|
||||
"sys_membarrier.go",
|
||||
"sys_mempolicy.go",
|
||||
"sys_mmap.go",
|
||||
"sys_mount.go",
|
||||
"sys_mq.go",
|
||||
"sys_msgqueue.go",
|
||||
"sys_pipe.go",
|
||||
"sys_poll.go",
|
||||
"sys_prctl.go",
|
||||
"sys_process_vm.go",
|
||||
"sys_random.go",
|
||||
"sys_read.go",
|
||||
"sys_read_write.go",
|
||||
"sys_rlimit.go",
|
||||
"sys_rseq.go",
|
||||
"sys_rusage.go",
|
||||
@@ -55,7 +57,6 @@ go_library(
|
||||
"sys_tls_amd64.go",
|
||||
"sys_tls_arm64.go",
|
||||
"sys_utsname.go",
|
||||
"sys_write.go",
|
||||
"sys_xattr.go",
|
||||
"timespec.go",
|
||||
],
|
||||
@@ -64,9 +65,12 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/abi",
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/bits",
|
||||
"//pkg/bpf",
|
||||
"//pkg/context",
|
||||
"//pkg/errors/linuxerr",
|
||||
"//pkg/fspath",
|
||||
"//pkg/gohacks",
|
||||
"//pkg/hostarch",
|
||||
"//pkg/log",
|
||||
"//pkg/marshal",
|
||||
@@ -75,23 +79,24 @@ go_library(
|
||||
"//pkg/rand",
|
||||
"//pkg/safemem",
|
||||
"//pkg/sentry/arch",
|
||||
"//pkg/sentry/fs",
|
||||
"//pkg/sentry/fs/anon",
|
||||
"//pkg/sentry/fs/lock",
|
||||
"//pkg/sentry/fs/timerfd",
|
||||
"//pkg/sentry/fs/tmpfs",
|
||||
"//pkg/sentry/fsbridge",
|
||||
"//pkg/sentry/fsimpl/eventfd",
|
||||
"//pkg/sentry/fsimpl/host",
|
||||
"//pkg/sentry/fsimpl/iouringfs",
|
||||
"//pkg/sentry/fsimpl/pipefs",
|
||||
"//pkg/sentry/fsimpl/signalfd",
|
||||
"//pkg/sentry/fsimpl/timerfd",
|
||||
"//pkg/sentry/fsimpl/tmpfs",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/epoll",
|
||||
"//pkg/sentry/kernel/eventfd",
|
||||
"//pkg/sentry/kernel/fasync",
|
||||
"//pkg/sentry/kernel/ipc",
|
||||
"//pkg/sentry/kernel/mq",
|
||||
"//pkg/sentry/kernel/msgqueue",
|
||||
"//pkg/sentry/kernel/pipe",
|
||||
"//pkg/sentry/kernel/sched",
|
||||
"//pkg/sentry/kernel/shm",
|
||||
"//pkg/sentry/kernel/signalfd",
|
||||
"//pkg/sentry/kernel/time",
|
||||
"//pkg/sentry/limits",
|
||||
"//pkg/sentry/loader",
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
"gvisor.dev/gvisor/pkg/metric"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
@@ -40,11 +39,11 @@ func incrementPartialResultMetric() {
|
||||
metric.WeirdnessMetric.Increment("partial_result")
|
||||
}
|
||||
|
||||
// HandleIOErrorVFS2 handles special error cases for partial results. For some
|
||||
// HandleIOError handles special error cases for partial results. For some
|
||||
// errors, we may consume the error and return only the partial read/write.
|
||||
//
|
||||
// op and f are used only for panics.
|
||||
func HandleIOErrorVFS2(ctx context.Context, partialResult bool, ioerr, intr error, op string, f *vfs.FileDescription) error {
|
||||
func HandleIOError(ctx context.Context, partialResult bool, ioerr, intr error, op string, f *vfs.FileDescription) error {
|
||||
known, err := handleIOErrorImpl(ctx, partialResult, ioerr, intr, op)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -60,24 +59,6 @@ func HandleIOErrorVFS2(ctx context.Context, partialResult bool, ioerr, intr erro
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleIOError handles special error cases for partial results. For some
|
||||
// errors, we may consume the error and return only the partial read/write.
|
||||
//
|
||||
// op and f are used only for panics.
|
||||
func handleIOError(ctx context.Context, partialResult bool, ioerr, intr error, op string, f *fs.File) error {
|
||||
known, err := handleIOErrorImpl(ctx, partialResult, ioerr, intr, op)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !known {
|
||||
// An unknown error is encountered with a partial read/write.
|
||||
name, _ := f.Dirent.FullName(nil /* ignore chroot */)
|
||||
log.Traceback("Invalid request partialResult %v and err (type %T) %v for %s operation on %q, %T", partialResult, ioerr, ioerr, op, name, f.FileOperations)
|
||||
partialResultOnce.Do(incrementPartialResultMetric)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleIOError handles special error cases for partial results. For some
|
||||
// errors, we may consume the error and return only the partial read/write.
|
||||
//
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
// 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/fs"
|
||||
)
|
||||
|
||||
// flagsToPermissions returns a Permissions object from Linux flags.
|
||||
// This includes truncate permission if O_TRUNC is set in the mask.
|
||||
func flagsToPermissions(mask uint) (p fs.PermMask) {
|
||||
if mask&linux.O_TRUNC != 0 {
|
||||
p.Write = true
|
||||
}
|
||||
switch mask & linux.O_ACCMODE {
|
||||
case linux.O_WRONLY:
|
||||
p.Write = true
|
||||
case linux.O_RDWR:
|
||||
p.Write = true
|
||||
p.Read = true
|
||||
case linux.O_RDONLY:
|
||||
p.Read = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// linuxToFlags converts Linux file flags to a FileFlags object.
|
||||
func linuxToFlags(mask uint) fs.FileFlags {
|
||||
return fs.FileFlags{
|
||||
Direct: mask&linux.O_DIRECT != 0,
|
||||
DSync: mask&(linux.O_DSYNC|linux.O_SYNC) != 0,
|
||||
Sync: mask&linux.O_SYNC != 0,
|
||||
NonBlocking: mask&linux.O_NONBLOCK != 0,
|
||||
Read: (mask & linux.O_ACCMODE) != linux.O_WRONLY,
|
||||
Write: (mask & linux.O_ACCMODE) != linux.O_RDONLY,
|
||||
Append: mask&linux.O_APPEND != 0,
|
||||
Directory: mask&linux.O_DIRECTORY != 0,
|
||||
Async: mask&linux.O_ASYNC != 0,
|
||||
LargeFile: mask&linux.O_LARGEFILE != 0,
|
||||
Truncate: mask&linux.O_TRUNC != 0,
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package linux provides syscall tables for amd64 Linux.
|
||||
// Package linux provides syscall tables for amd64 and arm64 Linux.
|
||||
package linux
|
||||
|
||||
import (
|
||||
@@ -54,21 +54,21 @@ var AMD64 = &kernel.SyscallTable{
|
||||
Table: map[uintptr]kernel.Syscall{
|
||||
0: syscalls.SupportedPoint("read", Read, PointRead),
|
||||
1: syscalls.Supported("write", Write),
|
||||
2: syscalls.PartiallySupportedPoint("open", Open, PointOpen, "Options O_DIRECT, O_NOATIME, O_PATH, O_TMPFILE, O_SYNC are not supported.", nil),
|
||||
3: syscalls.Supported("close", Close),
|
||||
2: syscalls.SupportedPoint("open", Open, PointOpen),
|
||||
3: syscalls.SupportedPoint("close", Close, PointClose),
|
||||
4: syscalls.Supported("stat", Stat),
|
||||
5: syscalls.Supported("fstat", Fstat),
|
||||
6: syscalls.Supported("lstat", Lstat),
|
||||
7: syscalls.Supported("poll", Poll),
|
||||
8: syscalls.Supported("lseek", Lseek),
|
||||
9: syscalls.PartiallySupported("mmap", Mmap, "Generally supported with exceptions. Options MAP_FIXED_NOREPLACE, MAP_SHARED_VALIDATE, MAP_SYNC MAP_GROWSDOWN, MAP_HUGETLB are not supported.", nil),
|
||||
9: syscalls.Supported("mmap", Mmap),
|
||||
10: syscalls.Supported("mprotect", Mprotect),
|
||||
11: syscalls.Supported("munmap", Munmap),
|
||||
12: syscalls.Supported("brk", Brk),
|
||||
13: syscalls.Supported("rt_sigaction", RtSigaction),
|
||||
14: syscalls.Supported("rt_sigprocmask", RtSigprocmask),
|
||||
15: syscalls.Supported("rt_sigreturn", RtSigreturn),
|
||||
16: syscalls.PartiallySupported("ioctl", Ioctl, "Only a few ioctls are implemented for backing devices and file systems.", nil),
|
||||
16: syscalls.Supported("ioctl", Ioctl),
|
||||
17: syscalls.Supported("pread64", Pread64),
|
||||
18: syscalls.Supported("pwrite64", Pwrite64),
|
||||
19: syscalls.Supported("readv", Readv),
|
||||
@@ -93,21 +93,21 @@ var AMD64 = &kernel.SyscallTable{
|
||||
38: syscalls.Supported("setitimer", Setitimer),
|
||||
39: syscalls.Supported("getpid", Getpid),
|
||||
40: syscalls.Supported("sendfile", Sendfile),
|
||||
41: syscalls.PartiallySupported("socket", Socket, "Limited support for AF_NETLINK, NETLINK_ROUTE sockets. Limited support for SOCK_RAW.", nil),
|
||||
41: syscalls.SupportedPoint("socket", Socket, PointSocket),
|
||||
42: syscalls.SupportedPoint("connect", Connect, PointConnect),
|
||||
43: syscalls.SupportedPoint("accept", Accept, PointAccept),
|
||||
44: syscalls.Supported("sendto", SendTo),
|
||||
45: syscalls.Supported("recvfrom", RecvFrom),
|
||||
46: syscalls.Supported("sendmsg", SendMsg),
|
||||
47: syscalls.PartiallySupported("recvmsg", RecvMsg, "Not all flags and control messages are supported.", nil),
|
||||
48: syscalls.PartiallySupported("shutdown", Shutdown, "Not all flags and control messages are supported.", nil),
|
||||
49: syscalls.PartiallySupportedPoint("bind", Bind, PointBind, "Autobind for abstract Unix sockets is not supported.", nil),
|
||||
47: syscalls.Supported("recvmsg", RecvMsg),
|
||||
48: syscalls.Supported("shutdown", Shutdown),
|
||||
49: syscalls.SupportedPoint("bind", Bind, PointBind),
|
||||
50: syscalls.Supported("listen", Listen),
|
||||
51: syscalls.Supported("getsockname", GetSockName),
|
||||
52: syscalls.Supported("getpeername", GetPeerName),
|
||||
53: syscalls.SupportedPoint("socketpair", SocketPair, PointSocketpair),
|
||||
54: syscalls.PartiallySupported("setsockopt", SetSockOpt, "Not all socket options are supported.", nil),
|
||||
55: syscalls.PartiallySupported("getsockopt", GetSockOpt, "Not all socket options are supported.", nil),
|
||||
54: syscalls.Supported("setsockopt", SetSockOpt),
|
||||
55: syscalls.Supported("getsockopt", GetSockOpt),
|
||||
56: syscalls.PartiallySupportedPoint("clone", Clone, PointClone, "Mount namespace (CLONE_NEWNS) not supported. Options CLONE_PARENT, CLONE_SYSVSEM not supported.", nil),
|
||||
57: syscalls.SupportedPoint("fork", Fork, PointFork),
|
||||
58: syscalls.SupportedPoint("vfork", Vfork, PointVfork),
|
||||
@@ -124,10 +124,10 @@ var AMD64 = &kernel.SyscallTable{
|
||||
69: syscalls.Supported("msgsnd", Msgsnd),
|
||||
70: syscalls.Supported("msgrcv", Msgrcv),
|
||||
71: syscalls.Supported("msgctl", Msgctl),
|
||||
72: syscalls.PartiallySupportedPoint("fcntl", Fcntl, PointFcntl, "Not all options are supported.", nil),
|
||||
73: syscalls.PartiallySupported("flock", Flock, "Locks are held within the sandbox only.", nil),
|
||||
74: syscalls.PartiallySupported("fsync", Fsync, "Full data flush is not guaranteed at this time.", nil),
|
||||
75: syscalls.PartiallySupported("fdatasync", Fdatasync, "Full data flush is not guaranteed at this time.", nil),
|
||||
72: syscalls.SupportedPoint("fcntl", Fcntl, PointFcntl),
|
||||
73: syscalls.Supported("flock", Flock),
|
||||
74: syscalls.Supported("fsync", Fsync),
|
||||
75: syscalls.Supported("fdatasync", Fdatasync),
|
||||
76: syscalls.Supported("truncate", Truncate),
|
||||
77: syscalls.Supported("ftruncate", Ftruncate),
|
||||
78: syscalls.Supported("getdents", Getdents),
|
||||
@@ -137,13 +137,13 @@ var AMD64 = &kernel.SyscallTable{
|
||||
82: syscalls.Supported("rename", Rename),
|
||||
83: syscalls.Supported("mkdir", Mkdir),
|
||||
84: syscalls.Supported("rmdir", Rmdir),
|
||||
85: syscalls.Supported("creat", Creat),
|
||||
86: syscalls.PartiallySupported("link", Link, "Limited support with Gofer. Link count and linked files may get out of sync because gVisor is not aware of external hardlinks.", nil),
|
||||
85: syscalls.SupportedPoint("creat", Creat, PointCreat),
|
||||
86: syscalls.Supported("link", Link),
|
||||
87: syscalls.Supported("unlink", Unlink),
|
||||
88: syscalls.Supported("symlink", Symlink),
|
||||
89: syscalls.Supported("readlink", Readlink),
|
||||
90: syscalls.Supported("chmod", Chmod),
|
||||
91: syscalls.PartiallySupported("fchmod", Fchmod, "Options S_ISUID and S_ISGID not supported.", nil),
|
||||
91: syscalls.Supported("fchmod", Fchmod),
|
||||
92: syscalls.Supported("chown", Chown),
|
||||
93: syscalls.Supported("fchown", Fchown),
|
||||
94: syscalls.Supported("lchown", Lchown),
|
||||
@@ -185,12 +185,12 @@ var AMD64 = &kernel.SyscallTable{
|
||||
130: syscalls.Supported("rt_sigsuspend", RtSigsuspend),
|
||||
131: syscalls.Supported("sigaltstack", Sigaltstack),
|
||||
132: syscalls.Supported("utime", Utime),
|
||||
133: syscalls.PartiallySupported("mknod", Mknod, "Device creation is not generally supported. Only regular file and FIFO creation are supported.", nil),
|
||||
133: syscalls.Supported("mknod", Mknod),
|
||||
134: syscalls.Error("uselib", linuxerr.ENOSYS, "Obsolete", nil),
|
||||
135: syscalls.ErrorWithEvent("personality", linuxerr.EINVAL, "Unable to change personality.", nil),
|
||||
136: syscalls.ErrorWithEvent("ustat", linuxerr.ENOSYS, "Needs filesystem support.", nil),
|
||||
137: syscalls.PartiallySupported("statfs", Statfs, "Depends on the backing file system implementation.", nil),
|
||||
138: syscalls.PartiallySupported("fstatfs", Fstatfs, "Depends on the backing file system implementation.", nil),
|
||||
137: syscalls.Supported("statfs", Statfs),
|
||||
138: syscalls.Supported("fstatfs", Fstatfs),
|
||||
139: syscalls.ErrorWithEvent("sysfs", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/165"}),
|
||||
140: syscalls.PartiallySupported("getpriority", Getpriority, "Stub implementation.", nil),
|
||||
141: syscalls.PartiallySupported("setpriority", Setpriority, "Stub implementation.", nil),
|
||||
@@ -207,18 +207,18 @@ var AMD64 = &kernel.SyscallTable{
|
||||
152: syscalls.PartiallySupported("munlockall", Munlockall, "Stub implementation. The sandbox lacks appropriate permissions.", nil),
|
||||
153: syscalls.CapError("vhangup", linux.CAP_SYS_TTY_CONFIG, "", nil),
|
||||
154: syscalls.Error("modify_ldt", linuxerr.EPERM, "", nil),
|
||||
155: syscalls.Error("pivot_root", linuxerr.EPERM, "", nil),
|
||||
155: syscalls.Supported("pivot_root", PivotRoot),
|
||||
156: syscalls.Error("sysctl", linuxerr.EPERM, "Deprecated. Use /proc/sys instead.", nil),
|
||||
157: syscalls.PartiallySupported("prctl", Prctl, "Not all options are supported.", nil),
|
||||
158: syscalls.PartiallySupported("arch_prctl", ArchPrctl, "Options ARCH_GET_GS, ARCH_SET_GS not supported.", nil),
|
||||
159: syscalls.CapError("adjtimex", linux.CAP_SYS_TIME, "", nil),
|
||||
160: syscalls.PartiallySupported("setrlimit", Setrlimit, "Not all rlimits are enforced.", nil),
|
||||
161: syscalls.SupportedPoint("chroot", Chroot, PointChroot),
|
||||
162: syscalls.PartiallySupported("sync", Sync, "Full data flush is not guaranteed at this time.", nil),
|
||||
162: syscalls.Supported("sync", Sync),
|
||||
163: syscalls.CapError("acct", linux.CAP_SYS_PACCT, "", nil),
|
||||
164: syscalls.CapError("settimeofday", linux.CAP_SYS_TIME, "", nil),
|
||||
165: syscalls.PartiallySupported("mount", Mount, "Not all options or file systems are supported.", nil),
|
||||
166: syscalls.PartiallySupported("umount2", Umount2, "Not all options or file systems are supported.", nil),
|
||||
165: syscalls.Supported("mount", Mount),
|
||||
166: syscalls.Supported("umount2", Umount2),
|
||||
167: syscalls.CapError("swapon", linux.CAP_SYS_ADMIN, "", nil),
|
||||
168: syscalls.CapError("swapoff", linux.CAP_SYS_ADMIN, "", nil),
|
||||
169: syscalls.CapError("reboot", linux.CAP_SYS_BOOT, "", nil),
|
||||
@@ -240,18 +240,18 @@ var AMD64 = &kernel.SyscallTable{
|
||||
185: syscalls.Error("security", linuxerr.ENOSYS, "Not implemented in Linux.", nil),
|
||||
186: syscalls.Supported("gettid", Gettid),
|
||||
187: syscalls.Supported("readahead", Readahead),
|
||||
188: syscalls.PartiallySupported("setxattr", SetXattr, "Only supported for tmpfs.", nil),
|
||||
189: syscalls.PartiallySupported("lsetxattr", LSetXattr, "Only supported for tmpfs.", nil),
|
||||
190: syscalls.PartiallySupported("fsetxattr", FSetXattr, "Only supported for tmpfs.", nil),
|
||||
191: syscalls.PartiallySupported("getxattr", GetXattr, "Only supported for tmpfs.", nil),
|
||||
192: syscalls.PartiallySupported("lgetxattr", LGetXattr, "Only supported for tmpfs.", nil),
|
||||
193: syscalls.PartiallySupported("fgetxattr", FGetXattr, "Only supported for tmpfs.", nil),
|
||||
194: syscalls.PartiallySupported("listxattr", ListXattr, "Only supported for tmpfs", nil),
|
||||
195: syscalls.PartiallySupported("llistxattr", LListXattr, "Only supported for tmpfs", nil),
|
||||
196: syscalls.PartiallySupported("flistxattr", FListXattr, "Only supported for tmpfs", nil),
|
||||
197: syscalls.PartiallySupported("removexattr", RemoveXattr, "Only supported for tmpfs", nil),
|
||||
198: syscalls.PartiallySupported("lremovexattr", LRemoveXattr, "Only supported for tmpfs", nil),
|
||||
199: syscalls.PartiallySupported("fremovexattr", FRemoveXattr, "Only supported for tmpfs", nil),
|
||||
188: syscalls.Supported("setxattr", SetXattr),
|
||||
189: syscalls.Supported("lsetxattr", Lsetxattr),
|
||||
190: syscalls.Supported("fsetxattr", Fsetxattr),
|
||||
191: syscalls.Supported("getxattr", GetXattr),
|
||||
192: syscalls.Supported("lgetxattr", Lgetxattr),
|
||||
193: syscalls.Supported("fgetxattr", Fgetxattr),
|
||||
194: syscalls.Supported("listxattr", ListXattr),
|
||||
195: syscalls.Supported("llistxattr", Llistxattr),
|
||||
196: syscalls.Supported("flistxattr", Flistxattr),
|
||||
197: syscalls.Supported("removexattr", RemoveXattr),
|
||||
198: syscalls.Supported("lremovexattr", Lremovexattr),
|
||||
199: syscalls.Supported("fremovexattr", Fremovexattr),
|
||||
200: syscalls.Supported("tkill", Tkill),
|
||||
201: syscalls.Supported("time", Time),
|
||||
202: syscalls.PartiallySupported("futex", Futex, "Robust futexes not supported.", nil),
|
||||
@@ -273,7 +273,7 @@ var AMD64 = &kernel.SyscallTable{
|
||||
218: syscalls.Supported("set_tid_address", SetTidAddress),
|
||||
219: syscalls.Supported("restart_syscall", RestartSyscall),
|
||||
220: syscalls.Supported("semtimedop", Semtimedop),
|
||||
221: syscalls.PartiallySupported("fadvise64", Fadvise64, "Not all options are supported.", nil),
|
||||
221: syscalls.PartiallySupported("fadvise64", Fadvise64, "The syscall is 'supported', but ignores all provided advice.", nil),
|
||||
222: syscalls.Supported("timer_create", TimerCreate),
|
||||
223: syscalls.Supported("timer_settime", TimerSettime),
|
||||
224: syscalls.Supported("timer_gettime", TimerGettime),
|
||||
@@ -292,8 +292,8 @@ var AMD64 = &kernel.SyscallTable{
|
||||
237: syscalls.PartiallySupported("mbind", Mbind, "Stub implementation. Only a single NUMA node is advertised, and mempolicy is ignored accordingly, but mbind() will succeed and has effects reflected by get_mempolicy.", []string{"gvisor.dev/issue/262"}),
|
||||
238: syscalls.PartiallySupported("set_mempolicy", SetMempolicy, "Stub implementation.", nil),
|
||||
239: syscalls.PartiallySupported("get_mempolicy", GetMempolicy, "Stub implementation.", nil),
|
||||
240: syscalls.ErrorWithEvent("mq_open", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921)
|
||||
241: syscalls.ErrorWithEvent("mq_unlink", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921)
|
||||
240: syscalls.Supported("mq_open", MqOpen),
|
||||
241: syscalls.Supported("mq_unlink", MqUnlink),
|
||||
242: syscalls.ErrorWithEvent("mq_timedsend", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921)
|
||||
243: syscalls.ErrorWithEvent("mq_timedreceive", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921)
|
||||
244: syscalls.ErrorWithEvent("mq_notify", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921)
|
||||
@@ -305,19 +305,19 @@ var AMD64 = &kernel.SyscallTable{
|
||||
250: syscalls.Error("keyctl", linuxerr.EACCES, "Not available to user.", nil),
|
||||
251: syscalls.CapError("ioprio_set", linux.CAP_SYS_ADMIN, "", nil), // requires cap_sys_nice or cap_sys_admin (depending)
|
||||
252: syscalls.CapError("ioprio_get", linux.CAP_SYS_ADMIN, "", nil), // requires cap_sys_nice or cap_sys_admin (depending)
|
||||
253: syscalls.PartiallySupportedPoint("inotify_init", InotifyInit, PointInotifyInit, "Inotify events are only available inside the sandbox. Hard links are treated as different watch targets in gofer fs.", nil),
|
||||
254: syscalls.PartiallySupportedPoint("inotify_add_watch", InotifyAddWatch, PointInotifyAddWatch, "Inotify events are only available inside the sandbox. Hard links are treated as different watch targets in gofer fs.", nil),
|
||||
255: syscalls.PartiallySupportedPoint("inotify_rm_watch", InotifyRmWatch, PointInotifyRmWatch, "Inotify events are only available inside the sandbox. Hard links are treated as different watch targets in gofer fs.", nil),
|
||||
253: syscalls.PartiallySupportedPoint("inotify_init", InotifyInit, PointInotifyInit, "inotify events are only available inside the sandbox.", nil),
|
||||
254: syscalls.PartiallySupportedPoint("inotify_add_watch", InotifyAddWatch, PointInotifyAddWatch, "inotify events are only available inside the sandbox.", nil),
|
||||
255: syscalls.PartiallySupportedPoint("inotify_rm_watch", InotifyRmWatch, PointInotifyRmWatch, "inotify events are only available inside the sandbox.", nil),
|
||||
256: syscalls.CapError("migrate_pages", linux.CAP_SYS_NICE, "", nil),
|
||||
257: syscalls.SupportedPoint("openat", Openat, PointOpenat),
|
||||
258: syscalls.Supported("mkdirat", Mkdirat),
|
||||
259: syscalls.Supported("mknodat", Mknodat),
|
||||
260: syscalls.Supported("fchownat", Fchownat),
|
||||
261: syscalls.Supported("futimesat", Futimesat),
|
||||
262: syscalls.Supported("fstatat", Fstatat),
|
||||
262: syscalls.Supported("newfstatat", Newfstatat),
|
||||
263: syscalls.Supported("unlinkat", Unlinkat),
|
||||
264: syscalls.Supported("renameat", Renameat),
|
||||
265: syscalls.PartiallySupported("linkat", Linkat, "See link(2).", nil),
|
||||
265: syscalls.Supported("linkat", Linkat),
|
||||
266: syscalls.Supported("symlinkat", Symlinkat),
|
||||
267: syscalls.Supported("readlinkat", Readlinkat),
|
||||
268: syscalls.Supported("fchmodat", Fchmodat),
|
||||
@@ -329,37 +329,37 @@ var AMD64 = &kernel.SyscallTable{
|
||||
274: syscalls.Supported("get_robust_list", GetRobustList),
|
||||
275: syscalls.Supported("splice", Splice),
|
||||
276: syscalls.Supported("tee", Tee),
|
||||
277: syscalls.PartiallySupported("sync_file_range", SyncFileRange, "Full data flush is not guaranteed at this time.", nil),
|
||||
277: syscalls.Supported("sync_file_range", SyncFileRange),
|
||||
278: syscalls.ErrorWithEvent("vmsplice", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/138"}), // TODO(b/29354098)
|
||||
279: syscalls.CapError("move_pages", linux.CAP_SYS_NICE, "", nil), // requires cap_sys_nice (mostly)
|
||||
280: syscalls.Supported("utimensat", Utimensat),
|
||||
281: syscalls.Supported("epoll_pwait", EpollPwait),
|
||||
282: syscalls.PartiallySupportedPoint("signalfd", Signalfd, PointSignalfd, "Semantics are slightly different.", []string{"gvisor.dev/issue/139"}),
|
||||
282: syscalls.SupportedPoint("signalfd", Signalfd, PointSignalfd),
|
||||
283: syscalls.SupportedPoint("timerfd_create", TimerfdCreate, PointTimerfdCreate),
|
||||
284: syscalls.SupportedPoint("eventfd", Eventfd, PointEventfd),
|
||||
285: syscalls.PartiallySupported("fallocate", Fallocate, "Not all options are supported.", nil),
|
||||
286: syscalls.SupportedPoint("timerfd_settime", TimerfdSettime, PointTimerfdSettime),
|
||||
287: syscalls.SupportedPoint("timerfd_gettime", TimerfdGettime, PointTimerfdGettime),
|
||||
288: syscalls.SupportedPoint("accept4", Accept4, PointAccept4),
|
||||
289: syscalls.PartiallySupportedPoint("signalfd4", Signalfd4, PointSignalfd4, "Semantics are slightly different.", []string{"gvisor.dev/issue/139"}),
|
||||
289: syscalls.SupportedPoint("signalfd4", Signalfd4, PointSignalfd4),
|
||||
290: syscalls.SupportedPoint("eventfd2", Eventfd2, PointEventfd2),
|
||||
291: syscalls.Supported("epoll_create1", EpollCreate1),
|
||||
292: syscalls.SupportedPoint("dup3", Dup3, PointDup3),
|
||||
293: syscalls.SupportedPoint("pipe2", Pipe2, PointPipe2),
|
||||
294: syscalls.PartiallySupportedPoint("inotify_init1", InotifyInit1, PointInotifyInit1, "Inotify events are only available inside the sandbox. Hard links are treated as different watch targets in gofer fs.", nil),
|
||||
294: syscalls.PartiallySupportedPoint("inotify_init1", InotifyInit1, PointInotifyInit1, "inotify events are only available inside the sandbox.", nil),
|
||||
295: syscalls.Supported("preadv", Preadv),
|
||||
296: syscalls.Supported("pwritev", Pwritev),
|
||||
297: syscalls.Supported("rt_tgsigqueueinfo", RtTgsigqueueinfo),
|
||||
298: syscalls.ErrorWithEvent("perf_event_open", linuxerr.ENODEV, "No support for perf counters", nil),
|
||||
299: syscalls.PartiallySupported("recvmmsg", RecvMMsg, "Not all flags and control messages are supported.", nil),
|
||||
299: syscalls.Supported("recvmmsg", RecvMMsg),
|
||||
300: syscalls.ErrorWithEvent("fanotify_init", linuxerr.ENOSYS, "Needs CONFIG_FANOTIFY", nil),
|
||||
301: syscalls.ErrorWithEvent("fanotify_mark", linuxerr.ENOSYS, "Needs CONFIG_FANOTIFY", nil),
|
||||
302: syscalls.SupportedPoint("prlimit64", Prlimit64, PointPrlimit64),
|
||||
303: syscalls.Error("name_to_handle_at", linuxerr.EOPNOTSUPP, "Not supported by gVisor filesystems", nil),
|
||||
304: syscalls.Error("open_by_handle_at", linuxerr.EOPNOTSUPP, "Not supported by gVisor filesystems", nil),
|
||||
305: syscalls.CapError("clock_adjtime", linux.CAP_SYS_TIME, "", nil),
|
||||
306: syscalls.PartiallySupported("syncfs", Syncfs, "Depends on backing file system.", nil),
|
||||
307: syscalls.PartiallySupported("sendmmsg", SendMMsg, "Not all flags and control messages are supported.", nil),
|
||||
306: syscalls.Supported("syncfs", Syncfs),
|
||||
307: syscalls.Supported("sendmmsg", SendMMsg),
|
||||
308: syscalls.ErrorWithEvent("setns", linuxerr.EOPNOTSUPP, "Needs filesystem support", []string{"gvisor.dev/issue/140"}), // TODO(b/29354995)
|
||||
309: syscalls.Supported("getcpu", Getcpu),
|
||||
310: syscalls.ErrorWithEvent("process_vm_readv", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/158"}),
|
||||
@@ -368,7 +368,7 @@ var AMD64 = &kernel.SyscallTable{
|
||||
313: syscalls.CapError("finit_module", linux.CAP_SYS_MODULE, "", nil),
|
||||
314: syscalls.ErrorWithEvent("sched_setattr", linuxerr.ENOSYS, "gVisor does not implement a scheduler.", []string{"gvisor.dev/issue/264"}), // TODO(b/118902272)
|
||||
315: syscalls.ErrorWithEvent("sched_getattr", linuxerr.ENOSYS, "gVisor does not implement a scheduler.", []string{"gvisor.dev/issue/264"}), // TODO(b/118902272)
|
||||
316: syscalls.ErrorWithEvent("renameat2", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/263"}), // TODO(b/118902772)
|
||||
316: syscalls.Supported("renameat2", Renameat2),
|
||||
317: syscalls.Supported("seccomp", Seccomp),
|
||||
318: syscalls.Supported("getrandom", GetRandom),
|
||||
319: syscalls.Supported("memfd_create", MemfdCreate),
|
||||
@@ -383,7 +383,7 @@ var AMD64 = &kernel.SyscallTable{
|
||||
// of Linux after 4.4.
|
||||
326: syscalls.ErrorWithEvent("copy_file_range", linuxerr.ENOSYS, "", nil),
|
||||
327: syscalls.Supported("preadv2", Preadv2),
|
||||
328: syscalls.PartiallySupported("pwritev2", Pwritev2, "Flag RWF_HIPRI is not supported.", nil),
|
||||
328: syscalls.Supported("pwritev2", Pwritev2),
|
||||
329: syscalls.ErrorWithEvent("pkey_mprotect", linuxerr.ENOSYS, "", nil),
|
||||
330: syscalls.ErrorWithEvent("pkey_alloc", linuxerr.ENOSYS, "", nil),
|
||||
331: syscalls.ErrorWithEvent("pkey_free", linuxerr.ENOSYS, "", nil),
|
||||
@@ -393,8 +393,8 @@ var AMD64 = &kernel.SyscallTable{
|
||||
|
||||
// Linux skips ahead to syscall 424 to sync numbers between arches.
|
||||
424: syscalls.ErrorWithEvent("pidfd_send_signal", linuxerr.ENOSYS, "", nil),
|
||||
425: syscalls.ErrorWithEvent("io_uring_setup", linuxerr.ENOSYS, "", nil),
|
||||
426: syscalls.ErrorWithEvent("io_uring_enter", linuxerr.ENOSYS, "", nil),
|
||||
425: syscalls.PartiallySupported("io_uring_setup", IOUringSetup, "Not all flags and functionality supported.", nil),
|
||||
426: syscalls.PartiallySupported("io_uring_enter", IOUringEnter, "Not all flags and functionality supported.", nil),
|
||||
427: syscalls.ErrorWithEvent("io_uring_register", linuxerr.ENOSYS, "", nil),
|
||||
428: syscalls.ErrorWithEvent("open_tree", linuxerr.ENOSYS, "", nil),
|
||||
429: syscalls.ErrorWithEvent("move_mount", linuxerr.ENOSYS, "", nil),
|
||||
@@ -405,6 +405,7 @@ var AMD64 = &kernel.SyscallTable{
|
||||
434: syscalls.ErrorWithEvent("pidfd_open", linuxerr.ENOSYS, "", nil),
|
||||
435: syscalls.ErrorWithEvent("clone3", linuxerr.ENOSYS, "", nil),
|
||||
436: syscalls.Supported("close_range", CloseRange),
|
||||
439: syscalls.Supported("faccessat2", Faccessat2),
|
||||
441: syscalls.Supported("epoll_pwait2", EpollPwait2),
|
||||
},
|
||||
Emulate: map[hostarch.Addr]uintptr{
|
||||
@@ -435,18 +436,18 @@ var ARM64 = &kernel.SyscallTable{
|
||||
2: syscalls.PartiallySupported("io_submit", IoSubmit, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}),
|
||||
3: syscalls.PartiallySupported("io_cancel", IoCancel, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}),
|
||||
4: syscalls.PartiallySupported("io_getevents", IoGetevents, "Generally supported with exceptions. User ring optimizations are not implemented.", []string{"gvisor.dev/issue/204"}),
|
||||
5: syscalls.PartiallySupported("setxattr", SetXattr, "Only supported for tmpfs.", nil),
|
||||
6: syscalls.PartiallySupported("lsetxattr", LSetXattr, "Only supported for tmpfs.", nil),
|
||||
7: syscalls.PartiallySupported("fsetxattr", FSetXattr, "Only supported for tmpfs.", nil),
|
||||
8: syscalls.PartiallySupported("getxattr", GetXattr, "Only supported for tmpfs.", nil),
|
||||
9: syscalls.PartiallySupported("lgetxattr", LGetXattr, "Only supported for tmpfs.", nil),
|
||||
10: syscalls.PartiallySupported("fgetxattr", FGetXattr, "Only supported for tmpfs.", nil),
|
||||
11: syscalls.PartiallySupported("listxattr", ListXattr, "Only supported for tmpfs", nil),
|
||||
12: syscalls.PartiallySupported("llistxattr", LListXattr, "Only supported for tmpfs", nil),
|
||||
13: syscalls.PartiallySupported("flistxattr", FListXattr, "Only supported for tmpfs", nil),
|
||||
14: syscalls.PartiallySupported("removexattr", RemoveXattr, "Only supported for tmpfs", nil),
|
||||
15: syscalls.PartiallySupported("lremovexattr", LRemoveXattr, "Only supported for tmpfs", nil),
|
||||
16: syscalls.PartiallySupported("fremovexattr", FRemoveXattr, "Only supported for tmpfs", nil),
|
||||
5: syscalls.Supported("setxattr", SetXattr),
|
||||
6: syscalls.Supported("lsetxattr", Lsetxattr),
|
||||
7: syscalls.Supported("fsetxattr", Fsetxattr),
|
||||
8: syscalls.Supported("getxattr", GetXattr),
|
||||
9: syscalls.Supported("lgetxattr", Lgetxattr),
|
||||
10: syscalls.Supported("fgetxattr", Fgetxattr),
|
||||
11: syscalls.Supported("listxattr", ListXattr),
|
||||
12: syscalls.Supported("llistxattr", Llistxattr),
|
||||
13: syscalls.Supported("flistxattr", Flistxattr),
|
||||
14: syscalls.Supported("removexattr", RemoveXattr),
|
||||
15: syscalls.Supported("lremovexattr", Lremovexattr),
|
||||
16: syscalls.Supported("fremovexattr", Fremovexattr),
|
||||
17: syscalls.Supported("getcwd", Getcwd),
|
||||
18: syscalls.CapError("lookup_dcookie", linux.CAP_SYS_ADMIN, "", nil),
|
||||
19: syscalls.SupportedPoint("eventfd2", Eventfd2, PointEventfd2),
|
||||
@@ -455,26 +456,26 @@ var ARM64 = &kernel.SyscallTable{
|
||||
22: syscalls.Supported("epoll_pwait", EpollPwait),
|
||||
23: syscalls.SupportedPoint("dup", Dup, PointDup),
|
||||
24: syscalls.SupportedPoint("dup3", Dup3, PointDup3),
|
||||
25: syscalls.PartiallySupportedPoint("fcntl", Fcntl, PointFcntl, "Not all options are supported.", nil),
|
||||
26: syscalls.PartiallySupportedPoint("inotify_init1", InotifyInit1, PointInotifyInit1, "Inotify events are only available inside the sandbox. Hard links are treated as different watch targets in gofer fs.", nil),
|
||||
27: syscalls.PartiallySupportedPoint("inotify_add_watch", InotifyAddWatch, PointInotifyAddWatch, "Inotify events are only available inside the sandbox. Hard links are treated as different watch targets in gofer fs.", nil),
|
||||
28: syscalls.PartiallySupportedPoint("inotify_rm_watch", InotifyRmWatch, PointInotifyRmWatch, "Inotify events are only available inside the sandbox. Hard links are treated as different watch targets in gofer fs.", nil),
|
||||
29: syscalls.PartiallySupported("ioctl", Ioctl, "Only a few ioctls are implemented for backing devices and file systems.", nil),
|
||||
25: syscalls.SupportedPoint("fcntl", Fcntl, PointFcntl),
|
||||
26: syscalls.PartiallySupportedPoint("inotify_init1", InotifyInit1, PointInotifyInit1, "inotify events are only available inside the sandbox.", nil),
|
||||
27: syscalls.PartiallySupportedPoint("inotify_add_watch", InotifyAddWatch, PointInotifyAddWatch, "inotify events are only available inside the sandbox.", nil),
|
||||
28: syscalls.PartiallySupportedPoint("inotify_rm_watch", InotifyRmWatch, PointInotifyRmWatch, "inotify events are only available inside the sandbox.", nil),
|
||||
29: syscalls.Supported("ioctl", Ioctl),
|
||||
30: syscalls.CapError("ioprio_set", linux.CAP_SYS_ADMIN, "", nil), // requires cap_sys_nice or cap_sys_admin (depending)
|
||||
31: syscalls.CapError("ioprio_get", linux.CAP_SYS_ADMIN, "", nil), // requires cap_sys_nice or cap_sys_admin (depending)
|
||||
32: syscalls.PartiallySupported("flock", Flock, "Locks are held within the sandbox only.", nil),
|
||||
32: syscalls.Supported("flock", Flock),
|
||||
33: syscalls.Supported("mknodat", Mknodat),
|
||||
34: syscalls.Supported("mkdirat", Mkdirat),
|
||||
35: syscalls.Supported("unlinkat", Unlinkat),
|
||||
36: syscalls.Supported("symlinkat", Symlinkat),
|
||||
37: syscalls.Supported("linkat", Linkat),
|
||||
38: syscalls.Supported("renameat", Renameat),
|
||||
39: syscalls.PartiallySupported("umount2", Umount2, "Not all options or file systems are supported.", nil),
|
||||
40: syscalls.PartiallySupported("mount", Mount, "Not all options or file systems are supported.", nil),
|
||||
41: syscalls.Error("pivot_root", linuxerr.EPERM, "", nil),
|
||||
39: syscalls.Supported("umount2", Umount2),
|
||||
40: syscalls.Supported("mount", Mount),
|
||||
41: syscalls.Supported("pivot_root", PivotRoot),
|
||||
42: syscalls.Error("nfsservctl", linuxerr.ENOSYS, "Removed after Linux 3.1.", nil),
|
||||
43: syscalls.PartiallySupported("statfs", Statfs, "Depends on the backing file system implementation.", nil),
|
||||
44: syscalls.PartiallySupported("fstatfs", Fstatfs, "Depends on the backing file system implementation.", nil),
|
||||
43: syscalls.Supported("statfs", Statfs),
|
||||
44: syscalls.Supported("fstatfs", Fstatfs),
|
||||
45: syscalls.Supported("truncate", Truncate),
|
||||
46: syscalls.Supported("ftruncate", Ftruncate),
|
||||
47: syscalls.PartiallySupported("fallocate", Fallocate, "Not all options are supported.", nil),
|
||||
@@ -482,12 +483,12 @@ var ARM64 = &kernel.SyscallTable{
|
||||
49: syscalls.SupportedPoint("chdir", Chdir, PointChdir),
|
||||
50: syscalls.SupportedPoint("fchdir", Fchdir, PointFchdir),
|
||||
51: syscalls.SupportedPoint("chroot", Chroot, PointChroot),
|
||||
52: syscalls.PartiallySupported("fchmod", Fchmod, "Options S_ISUID and S_ISGID not supported.", nil),
|
||||
52: syscalls.Supported("fchmod", Fchmod),
|
||||
53: syscalls.Supported("fchmodat", Fchmodat),
|
||||
54: syscalls.Supported("fchownat", Fchownat),
|
||||
55: syscalls.Supported("fchown", Fchown),
|
||||
56: syscalls.SupportedPoint("openat", Openat, PointOpenat),
|
||||
57: syscalls.Supported("close", Close),
|
||||
57: syscalls.SupportedPoint("close", Close, PointClose),
|
||||
58: syscalls.CapError("vhangup", linux.CAP_SYS_TTY_CONFIG, "", nil),
|
||||
59: syscalls.SupportedPoint("pipe2", Pipe2, PointPipe2),
|
||||
60: syscalls.CapError("quotactl", linux.CAP_SYS_ADMIN, "", nil), // requires cap_sys_admin for most operations
|
||||
@@ -504,17 +505,17 @@ var ARM64 = &kernel.SyscallTable{
|
||||
71: syscalls.Supported("sendfile", Sendfile),
|
||||
72: syscalls.Supported("pselect", Pselect),
|
||||
73: syscalls.Supported("ppoll", Ppoll),
|
||||
74: syscalls.PartiallySupportedPoint("signalfd4", Signalfd4, PointSignalfd4, "Semantics are slightly different.", []string{"gvisor.dev/issue/139"}),
|
||||
74: syscalls.SupportedPoint("signalfd4", Signalfd4, PointSignalfd4),
|
||||
75: syscalls.ErrorWithEvent("vmsplice", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/138"}), // TODO(b/29354098)
|
||||
76: syscalls.Supported("splice", Splice),
|
||||
77: syscalls.Supported("tee", Tee),
|
||||
78: syscalls.Supported("readlinkat", Readlinkat),
|
||||
79: syscalls.Supported("fstatat", Fstatat),
|
||||
79: syscalls.Supported("newfstatat", Newfstatat),
|
||||
80: syscalls.Supported("fstat", Fstat),
|
||||
81: syscalls.PartiallySupported("sync", Sync, "Full data flush is not guaranteed at this time.", nil),
|
||||
82: syscalls.PartiallySupported("fsync", Fsync, "Full data flush is not guaranteed at this time.", nil),
|
||||
83: syscalls.PartiallySupported("fdatasync", Fdatasync, "Full data flush is not guaranteed at this time.", nil),
|
||||
84: syscalls.PartiallySupported("sync_file_range", SyncFileRange, "Full data flush is not guaranteed at this time.", nil),
|
||||
81: syscalls.Supported("sync", Sync),
|
||||
82: syscalls.Supported("fsync", Fsync),
|
||||
83: syscalls.Supported("fdatasync", Fdatasync),
|
||||
84: syscalls.Supported("sync_file_range", SyncFileRange),
|
||||
85: syscalls.SupportedPoint("timerfd_create", TimerfdCreate, PointTimerfdCreate),
|
||||
86: syscalls.SupportedPoint("timerfd_settime", TimerfdSettime, PointTimerfdSettime),
|
||||
87: syscalls.SupportedPoint("timerfd_gettime", TimerfdGettime, PointTimerfdGettime),
|
||||
@@ -610,8 +611,8 @@ var ARM64 = &kernel.SyscallTable{
|
||||
177: syscalls.Supported("getegid", Getegid),
|
||||
178: syscalls.Supported("gettid", Gettid),
|
||||
179: syscalls.PartiallySupported("sysinfo", Sysinfo, "Fields loads, sharedram, bufferram, totalswap, freeswap, totalhigh, freehigh not supported.", nil),
|
||||
180: syscalls.ErrorWithEvent("mq_open", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921)
|
||||
181: syscalls.ErrorWithEvent("mq_unlink", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921)
|
||||
180: syscalls.Supported("mq_open", MqOpen),
|
||||
181: syscalls.Supported("mq_unlink", MqUnlink),
|
||||
182: syscalls.ErrorWithEvent("mq_timedsend", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921)
|
||||
183: syscalls.ErrorWithEvent("mq_timedreceive", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921)
|
||||
184: syscalls.ErrorWithEvent("mq_notify", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/136"}), // TODO(b/29354921)
|
||||
@@ -628,9 +629,9 @@ var ARM64 = &kernel.SyscallTable{
|
||||
195: syscalls.PartiallySupported("shmctl", Shmctl, "Options SHM_LOCK, SHM_UNLOCK are not supported.", nil),
|
||||
196: syscalls.PartiallySupported("shmat", Shmat, "Option SHM_RND is not supported.", nil),
|
||||
197: syscalls.Supported("shmdt", Shmdt),
|
||||
198: syscalls.PartiallySupported("socket", Socket, "Limited support for AF_NETLINK, NETLINK_ROUTE sockets. Limited support for SOCK_RAW.", nil),
|
||||
198: syscalls.SupportedPoint("socket", Socket, PointSocket),
|
||||
199: syscalls.SupportedPoint("socketpair", SocketPair, PointSocketpair),
|
||||
200: syscalls.PartiallySupportedPoint("bind", Bind, PointBind, "Autobind for abstract Unix sockets is not supported.", nil),
|
||||
200: syscalls.SupportedPoint("bind", Bind, PointBind),
|
||||
201: syscalls.Supported("listen", Listen),
|
||||
202: syscalls.SupportedPoint("accept", Accept, PointAccept),
|
||||
203: syscalls.SupportedPoint("connect", Connect, PointConnect),
|
||||
@@ -638,11 +639,11 @@ var ARM64 = &kernel.SyscallTable{
|
||||
205: syscalls.Supported("getpeername", GetPeerName),
|
||||
206: syscalls.Supported("sendto", SendTo),
|
||||
207: syscalls.Supported("recvfrom", RecvFrom),
|
||||
208: syscalls.PartiallySupported("setsockopt", SetSockOpt, "Not all socket options are supported.", nil),
|
||||
209: syscalls.PartiallySupported("getsockopt", GetSockOpt, "Not all socket options are supported.", nil),
|
||||
210: syscalls.PartiallySupported("shutdown", Shutdown, "Not all flags and control messages are supported.", nil),
|
||||
208: syscalls.Supported("setsockopt", SetSockOpt),
|
||||
209: syscalls.Supported("getsockopt", GetSockOpt),
|
||||
210: syscalls.Supported("shutdown", Shutdown),
|
||||
211: syscalls.Supported("sendmsg", SendMsg),
|
||||
212: syscalls.PartiallySupported("recvmsg", RecvMsg, "Not all flags and control messages are supported.", nil),
|
||||
212: syscalls.Supported("recvmsg", RecvMsg),
|
||||
213: syscalls.Supported("readahead", Readahead),
|
||||
214: syscalls.Supported("brk", Brk),
|
||||
215: syscalls.Supported("munmap", Munmap),
|
||||
@@ -652,7 +653,7 @@ var ARM64 = &kernel.SyscallTable{
|
||||
219: syscalls.Error("keyctl", linuxerr.EACCES, "Not available to user.", nil),
|
||||
220: syscalls.PartiallySupportedPoint("clone", Clone, PointClone, "Mount namespace (CLONE_NEWNS) not supported. Options CLONE_PARENT, CLONE_SYSVSEM not supported.", nil),
|
||||
221: syscalls.SupportedPoint("execve", Execve, PointExecve),
|
||||
222: syscalls.PartiallySupported("mmap", Mmap, "Generally supported with exceptions. Options MAP_FIXED_NOREPLACE, MAP_SHARED_VALIDATE, MAP_SYNC MAP_GROWSDOWN, MAP_HUGETLB are not supported.", nil),
|
||||
222: syscalls.Supported("mmap", Mmap),
|
||||
223: syscalls.PartiallySupported("fadvise64", Fadvise64, "Not all options are supported.", nil),
|
||||
224: syscalls.CapError("swapon", linux.CAP_SYS_ADMIN, "", nil),
|
||||
225: syscalls.CapError("swapoff", linux.CAP_SYS_ADMIN, "", nil),
|
||||
@@ -673,7 +674,7 @@ var ARM64 = &kernel.SyscallTable{
|
||||
240: syscalls.Supported("rt_tgsigqueueinfo", RtTgsigqueueinfo),
|
||||
241: syscalls.ErrorWithEvent("perf_event_open", linuxerr.ENODEV, "No support for perf counters", nil),
|
||||
242: syscalls.SupportedPoint("accept4", Accept4, PointAccept4),
|
||||
243: syscalls.PartiallySupported("recvmmsg", RecvMMsg, "Not all flags and control messages are supported.", nil),
|
||||
243: syscalls.Supported("recvmmsg", RecvMMsg),
|
||||
260: syscalls.Supported("wait4", Wait4),
|
||||
261: syscalls.SupportedPoint("prlimit64", Prlimit64, PointPrlimit64),
|
||||
262: syscalls.ErrorWithEvent("fanotify_init", linuxerr.ENOSYS, "Needs CONFIG_FANOTIFY", nil),
|
||||
@@ -681,16 +682,16 @@ var ARM64 = &kernel.SyscallTable{
|
||||
264: syscalls.Error("name_to_handle_at", linuxerr.EOPNOTSUPP, "Not supported by gVisor filesystems", nil),
|
||||
265: syscalls.Error("open_by_handle_at", linuxerr.EOPNOTSUPP, "Not supported by gVisor filesystems", nil),
|
||||
266: syscalls.CapError("clock_adjtime", linux.CAP_SYS_TIME, "", nil),
|
||||
267: syscalls.PartiallySupported("syncfs", Syncfs, "Depends on backing file system.", nil),
|
||||
267: syscalls.Supported("syncfs", Syncfs),
|
||||
268: syscalls.ErrorWithEvent("setns", linuxerr.EOPNOTSUPP, "Needs filesystem support", []string{"gvisor.dev/issue/140"}), // TODO(b/29354995)
|
||||
269: syscalls.PartiallySupported("sendmmsg", SendMMsg, "Not all flags and control messages are supported.", nil),
|
||||
269: syscalls.Supported("sendmmsg", SendMMsg),
|
||||
270: syscalls.ErrorWithEvent("process_vm_readv", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/158"}),
|
||||
271: syscalls.ErrorWithEvent("process_vm_writev", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/158"}),
|
||||
272: syscalls.CapError("kcmp", linux.CAP_SYS_PTRACE, "", nil),
|
||||
273: syscalls.CapError("finit_module", linux.CAP_SYS_MODULE, "", nil),
|
||||
274: syscalls.ErrorWithEvent("sched_setattr", linuxerr.ENOSYS, "gVisor does not implement a scheduler.", []string{"gvisor.dev/issue/264"}), // TODO(b/118902272)
|
||||
275: syscalls.ErrorWithEvent("sched_getattr", linuxerr.ENOSYS, "gVisor does not implement a scheduler.", []string{"gvisor.dev/issue/264"}), // TODO(b/118902272)
|
||||
276: syscalls.ErrorWithEvent("renameat2", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/263"}), // TODO(b/118902772)
|
||||
276: syscalls.Supported("renameat2", Renameat2),
|
||||
277: syscalls.Supported("seccomp", Seccomp),
|
||||
278: syscalls.Supported("getrandom", GetRandom),
|
||||
279: syscalls.Supported("memfd_create", MemfdCreate),
|
||||
@@ -703,7 +704,7 @@ var ARM64 = &kernel.SyscallTable{
|
||||
// Syscalls after 284 are "backports" from versions of Linux after 4.4.
|
||||
285: syscalls.ErrorWithEvent("copy_file_range", linuxerr.ENOSYS, "", nil),
|
||||
286: syscalls.Supported("preadv2", Preadv2),
|
||||
287: syscalls.PartiallySupported("pwritev2", Pwritev2, "Flag RWF_HIPRI is not supported.", nil),
|
||||
287: syscalls.Supported("pwritev2", Pwritev2),
|
||||
288: syscalls.ErrorWithEvent("pkey_mprotect", linuxerr.ENOSYS, "", nil),
|
||||
289: syscalls.ErrorWithEvent("pkey_alloc", linuxerr.ENOSYS, "", nil),
|
||||
290: syscalls.ErrorWithEvent("pkey_free", linuxerr.ENOSYS, "", nil),
|
||||
@@ -713,8 +714,8 @@ var ARM64 = &kernel.SyscallTable{
|
||||
|
||||
// Linux skips ahead to syscall 424 to sync numbers between arches.
|
||||
424: syscalls.ErrorWithEvent("pidfd_send_signal", linuxerr.ENOSYS, "", nil),
|
||||
425: syscalls.ErrorWithEvent("io_uring_setup", linuxerr.ENOSYS, "", nil),
|
||||
426: syscalls.ErrorWithEvent("io_uring_enter", linuxerr.ENOSYS, "", nil),
|
||||
425: syscalls.PartiallySupported("io_uring_setup", IOUringSetup, "Not all flags and functionality supported.", nil),
|
||||
426: syscalls.PartiallySupported("io_uring_enter", IOUringEnter, "Not all flags and functionality supported.", nil),
|
||||
427: syscalls.ErrorWithEvent("io_uring_register", linuxerr.ENOSYS, "", nil),
|
||||
428: syscalls.ErrorWithEvent("open_tree", linuxerr.ENOSYS, "", nil),
|
||||
429: syscalls.ErrorWithEvent("move_mount", linuxerr.ENOSYS, "", nil),
|
||||
@@ -725,6 +726,7 @@ var ARM64 = &kernel.SyscallTable{
|
||||
434: syscalls.ErrorWithEvent("pidfd_open", linuxerr.ENOSYS, "", nil),
|
||||
435: syscalls.ErrorWithEvent("clone3", linuxerr.ENOSYS, "", nil),
|
||||
436: syscalls.Supported("close_range", CloseRange),
|
||||
439: syscalls.Supported("faccessat2", Faccessat2),
|
||||
441: syscalls.Supported("epoll_pwait2", EpollPwait2),
|
||||
},
|
||||
Emulate: map[hostarch.Addr]uintptr{},
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package vfs2
|
||||
package linux
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
@@ -21,11 +21,11 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/marshal/primitive"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/eventfd"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/eventfd"
|
||||
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
||||
"gvisor.dev/gvisor/pkg/sentry/mm"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
|
||||
@@ -218,116 +218,6 @@ func IoCancel(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
return 0, nil, linuxerr.ENOSYS
|
||||
}
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
func getAIOCallback(t *kernel.Task, file *fs.File, cbAddr hostarch.Addr, cb *linux.IOCallback, ioseq usermem.IOSequence, actx *mm.AIOContext, eventFile *fs.File) kernel.AIOCallback {
|
||||
return func(ctx context.Context) {
|
||||
if actx.Dead() {
|
||||
actx.CancelPendingRequest()
|
||||
return
|
||||
}
|
||||
ev := &linux.IOEvent{
|
||||
Data: cb.Data,
|
||||
Obj: uint64(cbAddr),
|
||||
}
|
||||
|
||||
var err error
|
||||
switch cb.OpCode {
|
||||
case linux.IOCB_CMD_PREAD, linux.IOCB_CMD_PREADV:
|
||||
ev.Result, err = file.Preadv(ctx, ioseq, cb.Offset)
|
||||
case linux.IOCB_CMD_PWRITE, linux.IOCB_CMD_PWRITEV:
|
||||
ev.Result, err = file.Pwritev(ctx, ioseq, cb.Offset)
|
||||
case linux.IOCB_CMD_FSYNC:
|
||||
err = file.Fsync(ctx, 0, fs.FileMaxOffset, fs.SyncAll)
|
||||
case linux.IOCB_CMD_FDSYNC:
|
||||
err = file.Fsync(ctx, 0, fs.FileMaxOffset, fs.SyncData)
|
||||
}
|
||||
|
||||
// Update the result.
|
||||
if err != nil {
|
||||
err = handleIOError(t, ev.Result != 0 /* partial */, err, nil /* never interrupted */, "aio", file)
|
||||
ev.Result = -int64(kernel.ExtractErrno(err, 0))
|
||||
}
|
||||
|
||||
file.DecRef(ctx)
|
||||
|
||||
// Queue the result for delivery.
|
||||
actx.FinishRequest(ev)
|
||||
|
||||
// Notify the event file if one was specified. This needs to happen
|
||||
// *after* queueing the result to avoid racing with the thread we may
|
||||
// wake up.
|
||||
if eventFile != nil {
|
||||
eventFile.FileOperations.(*eventfd.EventOperations).Signal(1)
|
||||
eventFile.DecRef(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// submitCallback processes a single callback.
|
||||
func submitCallback(t *kernel.Task, id uint64, cb *linux.IOCallback, cbAddr hostarch.Addr) error {
|
||||
file := t.GetFile(cb.FD)
|
||||
if file == nil {
|
||||
// File not found.
|
||||
return linuxerr.EBADF
|
||||
}
|
||||
defer file.DecRef(t)
|
||||
|
||||
// Was there an eventFD? Extract it.
|
||||
var eventFile *fs.File
|
||||
if cb.Flags&linux.IOCB_FLAG_RESFD != 0 {
|
||||
eventFile = t.GetFile(cb.ResFD)
|
||||
if eventFile == nil {
|
||||
// Bad FD.
|
||||
return linuxerr.EBADF
|
||||
}
|
||||
defer eventFile.DecRef(t)
|
||||
|
||||
// Check that it is an eventfd.
|
||||
if _, ok := eventFile.FileOperations.(*eventfd.EventOperations); !ok {
|
||||
// Not an event FD.
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
ioseq, err := memoryFor(t, cb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check offset for reads/writes.
|
||||
switch cb.OpCode {
|
||||
case linux.IOCB_CMD_PREAD, linux.IOCB_CMD_PREADV, linux.IOCB_CMD_PWRITE, linux.IOCB_CMD_PWRITEV:
|
||||
if cb.Offset < 0 {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare the request.
|
||||
ctx, ok := t.MemoryManager().LookupAIOContext(t, id)
|
||||
if !ok {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
if err := ctx.Prepare(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if eventFile != nil {
|
||||
// The request is set. Make sure there's a ref on the file.
|
||||
//
|
||||
// This is necessary when the callback executes on completion,
|
||||
// which is also what will release this reference.
|
||||
eventFile.IncRef()
|
||||
}
|
||||
|
||||
// Perform the request asynchronously.
|
||||
file.IncRef()
|
||||
t.QueueAIO(getAIOCallback(t, file, cbAddr, cb, ioseq, ctx, eventFile))
|
||||
|
||||
// All set.
|
||||
return nil
|
||||
}
|
||||
|
||||
// IoSubmit implements linux syscall io_submit(2).
|
||||
func IoSubmit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
id := args[0].Uint64()
|
||||
@@ -360,7 +250,6 @@ func IoSubmit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
// Copy in this callback.
|
||||
var cb linux.IOCallback
|
||||
if _, err := cb.CopyIn(t, cbAddr); err != nil {
|
||||
|
||||
if i > 0 {
|
||||
// Some have been successful.
|
||||
return uintptr(i), nil, nil
|
||||
@@ -386,4 +275,110 @@ func IoSubmit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
return uintptr(nrEvents), nil, nil
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/aio.go)
|
||||
// submitCallback processes a single callback.
|
||||
func submitCallback(t *kernel.Task, id uint64, cb *linux.IOCallback, cbAddr hostarch.Addr) error {
|
||||
if cb.Reserved2 != 0 {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
|
||||
fd := t.GetFileVFS2(cb.FD)
|
||||
if fd == nil {
|
||||
return linuxerr.EBADF
|
||||
}
|
||||
defer fd.DecRef(t)
|
||||
|
||||
// Was there an eventFD? Extract it.
|
||||
var eventFD *vfs.FileDescription
|
||||
if cb.Flags&linux.IOCB_FLAG_RESFD != 0 {
|
||||
eventFD = t.GetFileVFS2(cb.ResFD)
|
||||
if eventFD == nil {
|
||||
return linuxerr.EBADF
|
||||
}
|
||||
defer eventFD.DecRef(t)
|
||||
|
||||
// Check that it is an eventfd.
|
||||
if _, ok := eventFD.Impl().(*eventfd.EventFileDescription); !ok {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
ioseq, err := memoryFor(t, cb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check offset for reads/writes.
|
||||
switch cb.OpCode {
|
||||
case linux.IOCB_CMD_PREAD, linux.IOCB_CMD_PREADV, linux.IOCB_CMD_PWRITE, linux.IOCB_CMD_PWRITEV:
|
||||
if cb.Offset < 0 {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare the request.
|
||||
aioCtx, ok := t.MemoryManager().LookupAIOContext(t, id)
|
||||
if !ok {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
if err := aioCtx.Prepare(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if eventFD != nil {
|
||||
// The request is set. Make sure there's a ref on the file.
|
||||
//
|
||||
// This is necessary when the callback executes on completion,
|
||||
// which is also what will release this reference.
|
||||
eventFD.IncRef()
|
||||
}
|
||||
|
||||
// Perform the request asynchronously.
|
||||
fd.IncRef()
|
||||
t.QueueAIO(getAIOCallback(t, fd, eventFD, cbAddr, cb, ioseq, aioCtx))
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAIOCallback(t *kernel.Task, fd, eventFD *vfs.FileDescription, cbAddr hostarch.Addr, cb *linux.IOCallback, ioseq usermem.IOSequence, aioCtx *mm.AIOContext) kernel.AIOCallback {
|
||||
return func(ctx context.Context) {
|
||||
// Release references after completing the callback.
|
||||
defer fd.DecRef(ctx)
|
||||
if eventFD != nil {
|
||||
defer eventFD.DecRef(ctx)
|
||||
}
|
||||
|
||||
if aioCtx.Dead() {
|
||||
aioCtx.CancelPendingRequest()
|
||||
return
|
||||
}
|
||||
ev := &linux.IOEvent{
|
||||
Data: cb.Data,
|
||||
Obj: uint64(cbAddr),
|
||||
}
|
||||
|
||||
var err error
|
||||
switch cb.OpCode {
|
||||
case linux.IOCB_CMD_PREAD, linux.IOCB_CMD_PREADV:
|
||||
ev.Result, err = fd.PRead(ctx, ioseq, cb.Offset, vfs.ReadOptions{})
|
||||
case linux.IOCB_CMD_PWRITE, linux.IOCB_CMD_PWRITEV:
|
||||
ev.Result, err = fd.PWrite(ctx, ioseq, cb.Offset, vfs.WriteOptions{})
|
||||
case linux.IOCB_CMD_FSYNC, linux.IOCB_CMD_FDSYNC:
|
||||
err = fd.Sync(ctx)
|
||||
}
|
||||
|
||||
// Update the result.
|
||||
if err != nil {
|
||||
err = HandleIOError(ctx, ev.Result != 0 /* partial */, err, nil /* never interrupted */, "aio", fd)
|
||||
ev.Result = -int64(kernel.ExtractErrno(err, 0))
|
||||
}
|
||||
|
||||
// Queue the result for delivery.
|
||||
aioCtx.FinishRequest(ev)
|
||||
|
||||
// Notify the event file if one was specified. This needs to happen
|
||||
// *after* queueing the result to avoid racing with the thread we may
|
||||
// wake up.
|
||||
if eventFD != nil {
|
||||
eventFD.Impl().(*eventfd.EventFileDescription).Signal(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
// Copyright 2020 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.
|
||||
@@ -15,142 +15,200 @@
|
||||
package linux
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/epoll"
|
||||
"gvisor.dev/gvisor/pkg/sentry/syscalls"
|
||||
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// LINT.IfChange
|
||||
var sizeofEpollEvent = (*linux.EpollEvent)(nil).SizeBytes()
|
||||
|
||||
// EpollCreate1 implements the epoll_create1(2) linux syscall.
|
||||
// EpollCreate1 implements Linux syscall epoll_create1(2).
|
||||
func EpollCreate1(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
flags := args[0].Int()
|
||||
if flags & ^linux.EPOLL_CLOEXEC != 0 {
|
||||
if flags&^linux.EPOLL_CLOEXEC != 0 {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
closeOnExec := flags&linux.EPOLL_CLOEXEC != 0
|
||||
fd, err := syscalls.CreateEpoll(t, closeOnExec)
|
||||
file, err := t.Kernel().VFS().NewEpollInstanceFD(t)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer file.DecRef(t)
|
||||
|
||||
fd, err := t.NewFDFromVFS2(0, file, kernel.FDFlags{
|
||||
CloseOnExec: flags&linux.EPOLL_CLOEXEC != 0,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return uintptr(fd), nil, nil
|
||||
}
|
||||
|
||||
// EpollCreate implements the epoll_create(2) linux syscall.
|
||||
// EpollCreate implements Linux syscall epoll_create(2).
|
||||
func EpollCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
size := args[0].Int()
|
||||
|
||||
// "Since Linux 2.6.8, the size argument is ignored, but must be greater
|
||||
// than zero" - epoll_create(2)
|
||||
if size <= 0 {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
fd, err := syscalls.CreateEpoll(t, false)
|
||||
file, err := t.Kernel().VFS().NewEpollInstanceFD(t)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer file.DecRef(t)
|
||||
|
||||
fd, err := t.NewFDFromVFS2(0, file, kernel.FDFlags{})
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return uintptr(fd), nil, nil
|
||||
}
|
||||
|
||||
// EpollCtl implements the epoll_ctl(2) linux syscall.
|
||||
// EpollCtl implements Linux syscall epoll_ctl(2).
|
||||
func EpollCtl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
epfd := args[0].Int()
|
||||
op := args[1].Int()
|
||||
fd := args[2].Int()
|
||||
eventAddr := args[3].Pointer()
|
||||
|
||||
// Capture the event state if needed.
|
||||
flags := epoll.EntryFlags(0)
|
||||
mask := waiter.EventMask(0)
|
||||
var data [2]int32
|
||||
if op != linux.EPOLL_CTL_DEL {
|
||||
var e linux.EpollEvent
|
||||
if _, err := e.CopyIn(t, eventAddr); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
if e.Events&linux.EPOLLONESHOT != 0 {
|
||||
flags |= epoll.OneShot
|
||||
}
|
||||
|
||||
if e.Events&linux.EPOLLET != 0 {
|
||||
flags |= epoll.EdgeTriggered
|
||||
}
|
||||
|
||||
mask = waiter.EventMaskFromLinux(e.Events)
|
||||
data = e.Data
|
||||
epfile := t.GetFileVFS2(epfd)
|
||||
if epfile == nil {
|
||||
return 0, nil, linuxerr.EBADF
|
||||
}
|
||||
defer epfile.DecRef(t)
|
||||
ep, ok := epfile.Impl().(*vfs.EpollInstance)
|
||||
if !ok {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
file := t.GetFileVFS2(fd)
|
||||
if file == nil {
|
||||
return 0, nil, linuxerr.EBADF
|
||||
}
|
||||
defer file.DecRef(t)
|
||||
if epfile == file {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
// Perform the requested operations.
|
||||
var event linux.EpollEvent
|
||||
switch op {
|
||||
case linux.EPOLL_CTL_ADD:
|
||||
// See fs/eventpoll.c.
|
||||
mask |= waiter.EventHUp | waiter.EventErr
|
||||
return 0, nil, syscalls.AddEpoll(t, epfd, fd, flags, mask, data)
|
||||
if _, err := event.CopyIn(t, eventAddr); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return 0, nil, ep.AddInterest(file, fd, event)
|
||||
case linux.EPOLL_CTL_DEL:
|
||||
return 0, nil, syscalls.RemoveEpoll(t, epfd, fd)
|
||||
return 0, nil, ep.DeleteInterest(file, fd)
|
||||
case linux.EPOLL_CTL_MOD:
|
||||
// Same as EPOLL_CTL_ADD.
|
||||
mask |= waiter.EventHUp | waiter.EventErr
|
||||
return 0, nil, syscalls.UpdateEpoll(t, epfd, fd, flags, mask, data)
|
||||
if _, err := event.CopyIn(t, eventAddr); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return 0, nil, ep.ModifyInterest(file, fd, event)
|
||||
default:
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
func waitEpoll(t *kernel.Task, fd int32, eventsAddr hostarch.Addr, max int, timeoutInNanos int64) (uintptr, *kernel.SyscallControl, error) {
|
||||
r, err := syscalls.WaitEpoll(t, fd, max, timeoutInNanos)
|
||||
if err != nil {
|
||||
return 0, nil, linuxerr.ConvertIntr(err, linuxerr.EINTR)
|
||||
func waitEpoll(t *kernel.Task, epfd int32, eventsAddr hostarch.Addr, maxEvents int, timeoutInNanos int64) (uintptr, *kernel.SyscallControl, error) {
|
||||
var _EP_MAX_EVENTS = math.MaxInt32 / sizeofEpollEvent // Linux: fs/eventpoll.c:EP_MAX_EVENTS
|
||||
if maxEvents <= 0 || maxEvents > _EP_MAX_EVENTS {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
if len(r) != 0 {
|
||||
if _, err := linux.CopyEpollEventSliceOut(t, eventsAddr, r); err != nil {
|
||||
epfile := t.GetFileVFS2(epfd)
|
||||
if epfile == nil {
|
||||
return 0, nil, linuxerr.EBADF
|
||||
}
|
||||
defer epfile.DecRef(t)
|
||||
ep, ok := epfile.Impl().(*vfs.EpollInstance)
|
||||
if !ok {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
// Allocate space for a few events on the stack for the common case in
|
||||
// which we don't have too many events.
|
||||
var (
|
||||
eventsArr [16]linux.EpollEvent
|
||||
ch chan struct{}
|
||||
haveDeadline bool
|
||||
deadline ktime.Time
|
||||
)
|
||||
for {
|
||||
events := ep.ReadEvents(eventsArr[:0], maxEvents)
|
||||
if len(events) != 0 {
|
||||
copiedBytes, err := linux.CopyEpollEventSliceOut(t, eventsAddr, events)
|
||||
copiedEvents := copiedBytes / sizeofEpollEvent // rounded down
|
||||
if copiedEvents != 0 {
|
||||
return uintptr(copiedEvents), nil, nil
|
||||
}
|
||||
return 0, nil, err
|
||||
}
|
||||
if timeoutInNanos == 0 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
// In the first iteration of this loop, register with the epoll
|
||||
// instance for readability events, but then immediately continue the
|
||||
// loop since we need to retry ReadEvents() before blocking. In all
|
||||
// subsequent iterations, block until events are available, the timeout
|
||||
// expires, or an interrupt arrives.
|
||||
if ch == nil {
|
||||
var w waiter.Entry
|
||||
w, ch = waiter.NewChannelEntry(waiter.ReadableEvents)
|
||||
if err := epfile.EventRegister(&w); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer epfile.EventUnregister(&w)
|
||||
} else {
|
||||
// Set up the timer if a timeout was specified.
|
||||
if timeoutInNanos > 0 && !haveDeadline {
|
||||
timeoutDur := time.Duration(timeoutInNanos) * time.Nanosecond
|
||||
deadline = t.Kernel().MonotonicClock().Now().Add(timeoutDur)
|
||||
haveDeadline = true
|
||||
}
|
||||
if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
|
||||
if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
|
||||
err = nil
|
||||
}
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return uintptr(len(r)), nil, nil
|
||||
|
||||
}
|
||||
|
||||
// EpollWait implements the epoll_wait(2) linux syscall.
|
||||
// EpollWait implements Linux syscall epoll_wait(2).
|
||||
func EpollWait(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
epfd := args[0].Int()
|
||||
eventsAddr := args[1].Pointer()
|
||||
maxEvents := int(args[2].Int())
|
||||
// Convert milliseconds to nanoseconds.
|
||||
timeoutInNanos := int64(args[3].Int()) * 1000000
|
||||
|
||||
return waitEpoll(t, epfd, eventsAddr, maxEvents, timeoutInNanos)
|
||||
}
|
||||
|
||||
// EpollPwait implements the epoll_pwait(2) linux syscall.
|
||||
// EpollPwait implements Linux syscall epoll_pwait(2).
|
||||
func EpollPwait(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
maskAddr := args[4].Pointer()
|
||||
maskSize := uint(args[5].Uint())
|
||||
|
||||
if maskAddr != 0 {
|
||||
mask, err := CopyInSigSet(t, maskAddr, maskSize)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
oldmask := t.SignalMask()
|
||||
t.SetSignalMask(mask)
|
||||
t.SetSavedSignalMask(oldmask)
|
||||
if err := setTempSignalSet(t, maskAddr, maskSize); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
return EpollWait(t, args)
|
||||
}
|
||||
|
||||
// EpollPwait2 implements the epoll_pwait(2) linux syscall.
|
||||
// EpollPwait2 implements Linux syscall epoll_pwait(2).
|
||||
func EpollPwait2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
epfd := args[0].Int()
|
||||
eventsAddr := args[1].Pointer()
|
||||
@@ -162,26 +220,16 @@ func EpollPwait2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
|
||||
|
||||
var timeoutInNanos int64 = -1
|
||||
if haveTimeout {
|
||||
timeout, err := copyTimespecIn(t, timeoutPtr)
|
||||
if err != nil {
|
||||
var timeout linux.Timespec
|
||||
if _, err := timeout.CopyIn(t, timeoutPtr); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
timeoutInNanos = timeout.ToNsec()
|
||||
|
||||
}
|
||||
|
||||
if maskAddr != 0 {
|
||||
mask, err := CopyInSigSet(t, maskAddr, maskSize)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
oldmask := t.SignalMask()
|
||||
t.SetSignalMask(mask)
|
||||
t.SetSavedSignalMask(oldmask)
|
||||
if err := setTempSignalSet(t, maskAddr, maskSize); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
return waitEpoll(t, epfd, eventsAddr, maxEvents, timeoutInNanos)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/epoll.go)
|
||||
|
||||
@@ -18,14 +18,13 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/eventfd"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/eventfd"
|
||||
)
|
||||
|
||||
// Eventfd2 implements linux syscall eventfd2(2).
|
||||
func Eventfd2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
initVal := args[0].Int()
|
||||
initVal := uint64(args[0].Uint())
|
||||
flags := uint(args[1].Uint())
|
||||
allOps := uint(linux.EFD_SEMAPHORE | linux.EFD_NONBLOCK | linux.EFD_CLOEXEC)
|
||||
|
||||
@@ -33,13 +32,19 @@ func Eventfd2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
event := eventfd.New(t, uint64(initVal), flags&linux.EFD_SEMAPHORE != 0)
|
||||
event.SetFlags(fs.SettableFileFlags{
|
||||
NonBlocking: flags&linux.EFD_NONBLOCK != 0,
|
||||
})
|
||||
defer event.DecRef(t)
|
||||
vfsObj := t.Kernel().VFS()
|
||||
fileFlags := uint32(linux.O_RDWR)
|
||||
if flags&linux.EFD_NONBLOCK != 0 {
|
||||
fileFlags |= linux.O_NONBLOCK
|
||||
}
|
||||
semMode := flags&linux.EFD_SEMAPHORE != 0
|
||||
eventfd, err := eventfd.New(t, vfsObj, initVal, semMode, fileFlags)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer eventfd.DecRef(t)
|
||||
|
||||
fd, err := t.NewFDFrom(0, event, kernel.FDFlags{
|
||||
fd, err := t.NewFDFromVFS2(0, eventfd, kernel.FDFlags{
|
||||
CloseOnExec: flags&linux.EFD_CLOEXEC != 0,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
+1052
-1679
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
// Copyright 2020 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.
|
||||
@@ -15,240 +15,185 @@
|
||||
package linux
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
)
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// Getdents implements linux syscall getdents(2) for 64bit systems.
|
||||
// Getdents implements Linux syscall getdents(2).
|
||||
func Getdents(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
fd := args[0].Int()
|
||||
addr := args[1].Pointer()
|
||||
size := int(args[2].Uint())
|
||||
|
||||
minSize := int(smallestDirent(t.Arch()))
|
||||
if size < minSize {
|
||||
// size is smaller than smallest possible dirent.
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
n, err := getdents(t, fd, addr, size, (*dirent).Serialize)
|
||||
return n, nil, err
|
||||
return getdents(t, args, false /* isGetdents64 */)
|
||||
}
|
||||
|
||||
// Getdents64 implements linux syscall getdents64(2).
|
||||
// Getdents64 implements Linux syscall getdents64(2).
|
||||
func Getdents64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
return getdents(t, args, true /* isGetdents64 */)
|
||||
}
|
||||
|
||||
// DirentStructBytesWithoutName is enough to fit (struct linux_dirent) and
|
||||
// (struct linux_dirent64) without accounting for the name parameter.
|
||||
const DirentStructBytesWithoutName = 8 + 8 + 2 + 1 + 1
|
||||
|
||||
func getdents(t *kernel.Task, args arch.SyscallArguments, isGetdents64 bool) (uintptr, *kernel.SyscallControl, error) {
|
||||
fd := args[0].Int()
|
||||
addr := args[1].Pointer()
|
||||
size := int(args[2].Uint())
|
||||
|
||||
minSize := int(smallestDirent64(t.Arch()))
|
||||
if size < minSize {
|
||||
// size is smaller than smallest possible dirent.
|
||||
if size < DirentStructBytesWithoutName {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
n, err := getdents(t, fd, addr, size, (*dirent).Serialize64)
|
||||
return n, nil, err
|
||||
}
|
||||
|
||||
// getdents implements the core of getdents(2)/getdents64(2).
|
||||
// f is the syscall implementation dirent serialization function.
|
||||
func getdents(t *kernel.Task, fd int32, addr hostarch.Addr, size int, f func(*dirent, io.Writer) (int, error)) (uintptr, error) {
|
||||
dir := t.GetFile(fd)
|
||||
if dir == nil {
|
||||
return 0, linuxerr.EBADF
|
||||
file := t.GetFileVFS2(fd)
|
||||
if file == nil {
|
||||
return 0, nil, linuxerr.EBADF
|
||||
}
|
||||
defer dir.DecRef(t)
|
||||
defer file.DecRef(t)
|
||||
|
||||
w := &usermem.IOReadWriter{
|
||||
Ctx: t,
|
||||
IO: t.MemoryManager(),
|
||||
Addr: addr,
|
||||
Opts: usermem.IOOpts{
|
||||
AddressSpaceActive: true,
|
||||
},
|
||||
// We want to be sure of the allowed buffer size before calling IterDirents,
|
||||
// because this function depends on IterDirents saving state of which dirent
|
||||
// was the last one that was successfully operated on.
|
||||
allowedSize, err := t.MemoryManager().EnsurePMAsExist(t, addr, int64(size), usermem.IOOpts{
|
||||
AddressSpaceActive: true,
|
||||
})
|
||||
if allowedSize == 0 {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
ds := newDirentSerializer(f, w, t.Arch(), size)
|
||||
rerr := dir.Readdir(t, ds)
|
||||
cb := getGetdentsCallback(t, int(allowedSize), size, isGetdents64)
|
||||
err = file.IterDirents(t, cb)
|
||||
n, _ := t.CopyOutBytes(addr, cb.buf[:cb.copied])
|
||||
|
||||
switch err := handleIOError(t, ds.Written() > 0, rerr, linuxerr.ERESTARTSYS, "getdents", dir); err {
|
||||
case nil:
|
||||
dir.Dirent.InotifyEvent(linux.IN_ACCESS, 0)
|
||||
return uintptr(ds.Written()), nil
|
||||
case io.EOF:
|
||||
return 0, nil
|
||||
default:
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
putGetdentsCallback(cb)
|
||||
|
||||
// oldDirentHdr is a fixed sized header matching the fixed size fields found in
|
||||
// the old linux dirent struct.
|
||||
//
|
||||
// +marshal
|
||||
type oldDirentHdr struct {
|
||||
Ino uint64
|
||||
Off uint64
|
||||
Reclen uint16 `marshal:"unaligned"` // Struct ends mid-word.
|
||||
}
|
||||
|
||||
// direntHdr is a fixed sized header matching the fixed size fields found in the
|
||||
// new linux dirent struct.
|
||||
//
|
||||
// +marshal
|
||||
type direntHdr struct {
|
||||
OldHdr oldDirentHdr
|
||||
Typ uint8 `marshal:"unaligned"` // Struct ends mid-word.
|
||||
}
|
||||
|
||||
// dirent contains the data pointed to by a new linux dirent struct.
|
||||
type dirent struct {
|
||||
Hdr direntHdr
|
||||
Name []byte
|
||||
}
|
||||
|
||||
// newDirent returns a dirent from an fs.InodeOperationsInfo.
|
||||
func newDirent(width uint, name string, attr fs.DentAttr, offset uint64) *dirent {
|
||||
d := &dirent{
|
||||
Hdr: direntHdr{
|
||||
OldHdr: oldDirentHdr{
|
||||
Ino: attr.InodeID,
|
||||
Off: offset,
|
||||
},
|
||||
Typ: fs.ToDirentType(attr.Type),
|
||||
},
|
||||
Name: []byte(name),
|
||||
}
|
||||
d.Hdr.OldHdr.Reclen = d.padRec(int(width))
|
||||
return d
|
||||
}
|
||||
|
||||
// smallestDirent returns the size of the smallest possible dirent using
|
||||
// the old linux dirent format.
|
||||
func smallestDirent(a *arch.Context64) uint {
|
||||
d := dirent{}
|
||||
return uint(d.Hdr.OldHdr.SizeBytes()) + a.Width() + 1
|
||||
}
|
||||
|
||||
// smallestDirent64 returns the size of the smallest possible dirent using
|
||||
// the new linux dirent format.
|
||||
func smallestDirent64(a *arch.Context64) uint {
|
||||
d := dirent{}
|
||||
return uint(d.Hdr.SizeBytes()) + a.Width()
|
||||
}
|
||||
|
||||
// padRec pads the name field until the rec length is a multiple of the width,
|
||||
// which must be a power of 2. It returns the padded rec length.
|
||||
func (d *dirent) padRec(width int) uint16 {
|
||||
a := d.Hdr.SizeBytes() + len(d.Name)
|
||||
r := (a + width) &^ (width - 1)
|
||||
padding := r - a
|
||||
d.Name = append(d.Name, make([]byte, padding)...)
|
||||
return uint16(r)
|
||||
}
|
||||
|
||||
// Serialize64 serializes a Dirent struct to a byte slice, keeping the new
|
||||
// linux dirent format. Returns the number of bytes serialized or an error.
|
||||
func (d *dirent) Serialize64(w io.Writer) (int, error) {
|
||||
n1, err := d.Hdr.WriteTo(w)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n2, err := w.Write(d.Name)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(n1) + n2, nil
|
||||
}
|
||||
|
||||
// Serialize serializes a Dirent struct to a byte slice, using the old linux
|
||||
// dirent format.
|
||||
// Returns the number of bytes serialized or an error.
|
||||
func (d *dirent) Serialize(w io.Writer) (int, error) {
|
||||
n1, err := d.Hdr.OldHdr.WriteTo(w)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n2, err := w.Write(d.Name)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n3, err := w.Write([]byte{d.Hdr.Typ})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(n1) + n2 + n3, nil
|
||||
}
|
||||
|
||||
// direntSerializer implements fs.InodeOperationsInfoSerializer, serializing dirents to an
|
||||
// io.Writer.
|
||||
type direntSerializer struct {
|
||||
serialize func(*dirent, io.Writer) (int, error)
|
||||
w io.Writer
|
||||
// width is the arch native value width.
|
||||
width uint
|
||||
// offset is the current dirent offset.
|
||||
offset uint64
|
||||
// written is the total bytes serialized.
|
||||
written int
|
||||
// size is the size of the buffer to serialize into.
|
||||
size int
|
||||
}
|
||||
|
||||
func newDirentSerializer(f func(d *dirent, w io.Writer) (int, error), w io.Writer, ac *arch.Context64, size int) *direntSerializer {
|
||||
return &direntSerializer{
|
||||
serialize: f,
|
||||
w: w,
|
||||
width: ac.Width(),
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
// CopyOut implements fs.InodeOperationsInfoSerializer.CopyOut.
|
||||
// It serializes and writes the fs.DentAttr to the direntSerializer io.Writer.
|
||||
func (ds *direntSerializer) CopyOut(name string, attr fs.DentAttr) error {
|
||||
ds.offset++
|
||||
|
||||
d := newDirent(ds.width, name, attr, ds.offset)
|
||||
|
||||
// Serialize dirent into a temp buffer.
|
||||
var b bytes.Buffer
|
||||
n, err := ds.serialize(d, &b)
|
||||
if err != nil {
|
||||
ds.offset--
|
||||
return err
|
||||
// Only report an error in case we didn't copy anything.
|
||||
// If we did manage to give _something_ to the caller then the correct
|
||||
// behaviour is to return success.
|
||||
if n == 0 {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
// Check that we have enough room remaining to write the dirent.
|
||||
if n > (ds.size - ds.written) {
|
||||
ds.offset--
|
||||
return io.EOF
|
||||
return uintptr(n), nil, nil
|
||||
}
|
||||
|
||||
type getdentsCallback struct {
|
||||
t *kernel.Task
|
||||
buf []byte
|
||||
copied int
|
||||
userReportedSize int
|
||||
isGetdents64 bool
|
||||
}
|
||||
|
||||
var getdentsCallbackPool = sync.Pool{
|
||||
New: func() any {
|
||||
return &getdentsCallback{}
|
||||
},
|
||||
}
|
||||
|
||||
func getGetdentsCallback(t *kernel.Task, size int, userReportedSize int, isGetdents64 bool) *getdentsCallback {
|
||||
cb := getdentsCallbackPool.Get().(*getdentsCallback)
|
||||
buf := cb.buf
|
||||
if cap(buf) < size {
|
||||
buf = make([]byte, size)
|
||||
} else {
|
||||
buf = buf[:size]
|
||||
}
|
||||
|
||||
// Write out the temp buffer.
|
||||
if _, err := b.WriteTo(ds.w); err != nil {
|
||||
ds.offset--
|
||||
return err
|
||||
*cb = getdentsCallback{
|
||||
t: t,
|
||||
buf: buf,
|
||||
copied: 0,
|
||||
userReportedSize: userReportedSize,
|
||||
isGetdents64: isGetdents64,
|
||||
}
|
||||
return cb
|
||||
}
|
||||
|
||||
func putGetdentsCallback(cb *getdentsCallback) {
|
||||
cb.t = nil
|
||||
cb.buf = cb.buf[:0]
|
||||
getdentsCallbackPool.Put(cb)
|
||||
}
|
||||
|
||||
// Handle implements vfs.IterDirentsCallback.Handle.
|
||||
func (cb *getdentsCallback) Handle(dirent vfs.Dirent) error {
|
||||
remaining := len(cb.buf) - cb.copied
|
||||
if cb.isGetdents64 {
|
||||
// struct linux_dirent64 {
|
||||
// ino64_t d_ino; /* 64-bit inode number */
|
||||
// off64_t d_off; /* 64-bit offset to next structure */
|
||||
// unsigned short d_reclen; /* Size of this dirent */
|
||||
// unsigned char d_type; /* File type */
|
||||
// char d_name[]; /* Filename (null-terminated) */
|
||||
// };
|
||||
size := DirentStructBytesWithoutName + len(dirent.Name)
|
||||
size = (size + 7) &^ 7 // round up to multiple of 8
|
||||
if size > remaining {
|
||||
// This is only needed to imitate Linux, since it writes out to the user
|
||||
// as it's iterating over dirs. We don't do that because we can't take
|
||||
// the mm.mappingMu while holding the filesystem mutex.
|
||||
if cb.copied == 0 && cb.userReportedSize >= size {
|
||||
return linuxerr.EFAULT
|
||||
}
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
buf := cb.buf[cb.copied : cb.copied+size]
|
||||
hostarch.ByteOrder.PutUint64(buf[0:8], dirent.Ino)
|
||||
hostarch.ByteOrder.PutUint64(buf[8:16], uint64(dirent.NextOff))
|
||||
hostarch.ByteOrder.PutUint16(buf[16:18], uint16(size))
|
||||
buf[18] = dirent.Type
|
||||
copy(buf[19:], dirent.Name)
|
||||
// Zero out all remaining bytes in buf, including the NUL terminator
|
||||
// after dirent.Name.
|
||||
bufTail := buf[19+len(dirent.Name):]
|
||||
for i := range bufTail {
|
||||
bufTail[i] = 0
|
||||
}
|
||||
cb.copied += size
|
||||
} else {
|
||||
// struct linux_dirent {
|
||||
// unsigned long d_ino; /* Inode number */
|
||||
// unsigned long d_off; /* Offset to next linux_dirent */
|
||||
// unsigned short d_reclen; /* Length of this linux_dirent */
|
||||
// char d_name[]; /* Filename (null-terminated) */
|
||||
// /* length is actually (d_reclen - 2 -
|
||||
// offsetof(struct linux_dirent, d_name)) */
|
||||
// /*
|
||||
// char pad; // Zero padding byte
|
||||
// char d_type; // File type (only since Linux
|
||||
// // 2.6.4); offset is (d_reclen - 1)
|
||||
// */
|
||||
// };
|
||||
if cb.t.Arch().Width() != 8 {
|
||||
panic(fmt.Sprintf("unsupported sizeof(unsigned long): %d", cb.t.Arch().Width()))
|
||||
}
|
||||
size := DirentStructBytesWithoutName + len(dirent.Name)
|
||||
size = (size + 7) &^ 7 // round up to multiple of sizeof(long)
|
||||
if size > remaining {
|
||||
if cb.copied == 0 && cb.userReportedSize >= size {
|
||||
return linuxerr.EFAULT
|
||||
}
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
buf := cb.buf[cb.copied : cb.copied+size]
|
||||
hostarch.ByteOrder.PutUint64(buf[0:8], dirent.Ino)
|
||||
hostarch.ByteOrder.PutUint64(buf[8:16], uint64(dirent.NextOff))
|
||||
hostarch.ByteOrder.PutUint16(buf[16:18], uint16(size))
|
||||
copy(buf[18:], dirent.Name)
|
||||
// Zero out all remaining bytes in buf, including the NUL terminator
|
||||
// after dirent.Name and the zero padding byte between the name and
|
||||
// dirent type.
|
||||
bufTail := buf[18+len(dirent.Name) : size-1]
|
||||
for i := range bufTail {
|
||||
bufTail[i] = 0
|
||||
}
|
||||
buf[size-1] = dirent.Type
|
||||
cb.copied += size
|
||||
}
|
||||
|
||||
ds.written += n
|
||||
return nil
|
||||
}
|
||||
|
||||
// Written returns the total number of bytes written.
|
||||
func (ds *direntSerializer) Written() int {
|
||||
return ds.written
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/getdents.go)
|
||||
|
||||
@@ -18,31 +18,26 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs/anon"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
const allFlags = int(linux.IN_NONBLOCK | linux.IN_CLOEXEC)
|
||||
const allFlags = linux.IN_NONBLOCK | linux.IN_CLOEXEC
|
||||
|
||||
// InotifyInit1 implements the inotify_init1() syscalls.
|
||||
func InotifyInit1(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
flags := int(args[0].Int())
|
||||
|
||||
flags := args[0].Int()
|
||||
if flags&^allFlags != 0 {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
dirent := fs.NewDirent(t, anon.NewInode(t), "inotify")
|
||||
fileFlags := fs.FileFlags{
|
||||
Read: true,
|
||||
Write: true,
|
||||
NonBlocking: flags&linux.IN_NONBLOCK != 0,
|
||||
ino, err := vfs.NewInotifyFD(t, t.Kernel().VFS(), uint32(flags))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
n := fs.NewFile(t, dirent, fileFlags, fs.NewInotify(t))
|
||||
defer n.DecRef(t)
|
||||
defer ino.DecRef(t)
|
||||
|
||||
fd, err := t.NewFDFrom(0, n, kernel.FDFlags{
|
||||
fd, err := t.NewFDFromVFS2(0, ino, kernel.FDFlags{
|
||||
CloseOnExec: flags&linux.IN_CLOEXEC != 0,
|
||||
})
|
||||
|
||||
@@ -61,21 +56,21 @@ func InotifyInit(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.S
|
||||
|
||||
// fdToInotify resolves an fd to an inotify object. If successful, the file will
|
||||
// have an extra ref and the caller is responsible for releasing the ref.
|
||||
func fdToInotify(t *kernel.Task, fd int32) (*fs.Inotify, *fs.File, error) {
|
||||
file := t.GetFile(fd)
|
||||
if file == nil {
|
||||
func fdToInotify(t *kernel.Task, fd int32) (*vfs.Inotify, *vfs.FileDescription, error) {
|
||||
f := t.GetFileVFS2(fd)
|
||||
if f == nil {
|
||||
// Invalid fd.
|
||||
return nil, nil, linuxerr.EBADF
|
||||
}
|
||||
|
||||
ino, ok := file.FileOperations.(*fs.Inotify)
|
||||
ino, ok := f.Impl().(*vfs.Inotify)
|
||||
if !ok {
|
||||
// Not an inotify fd.
|
||||
file.DecRef(t)
|
||||
f.DecRef(t)
|
||||
return nil, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
return ino, file, nil
|
||||
return ino, f, nil
|
||||
}
|
||||
|
||||
// InotifyAddWatch implements the inotify_add_watch() syscall.
|
||||
@@ -84,39 +79,44 @@ func InotifyAddWatch(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kern
|
||||
addr := args[1].Pointer()
|
||||
mask := args[2].Uint()
|
||||
|
||||
// "IN_DONT_FOLLOW: Don't dereference pathname if it is a symbolic link."
|
||||
// -- inotify(7)
|
||||
resolve := mask&linux.IN_DONT_FOLLOW == 0
|
||||
|
||||
// "EINVAL: The given event mask contains no valid events."
|
||||
// -- inotify_add_watch(2)
|
||||
if validBits := mask & linux.ALL_INOTIFY_BITS; validBits == 0 {
|
||||
if mask&linux.ALL_INOTIFY_BITS == 0 {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
ino, file, err := fdToInotify(t, fd)
|
||||
// "IN_DONT_FOLLOW: Don't dereference pathname if it is a symbolic link."
|
||||
// -- inotify(7)
|
||||
follow := followFinalSymlink
|
||||
if mask&linux.IN_DONT_FOLLOW != 0 {
|
||||
follow = nofollowFinalSymlink
|
||||
}
|
||||
|
||||
ino, f, err := fdToInotify(t, fd)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer file.DecRef(t)
|
||||
defer f.DecRef(t)
|
||||
|
||||
path, _, err := copyInPath(t, addr, false /* allowEmpty */)
|
||||
path, err := copyInPath(t, addr)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if mask&linux.IN_ONLYDIR != 0 {
|
||||
path.Dir = true
|
||||
}
|
||||
tpop, err := getTaskPathOperation(t, linux.AT_FDCWD, path, disallowEmptyPath, follow)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer tpop.Release(t)
|
||||
d, err := t.Kernel().VFS().GetDentryAt(t, t.Credentials(), &tpop.pop, &vfs.GetDentryOptions{})
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer d.DecRef(t)
|
||||
|
||||
err = fileOpOn(t, linux.AT_FDCWD, path, resolve, func(root *fs.Dirent, dirent *fs.Dirent, _ uint) error {
|
||||
// "IN_ONLYDIR: Only watch pathname if it is a directory." -- inotify(7)
|
||||
if onlyDir := mask&linux.IN_ONLYDIR != 0; onlyDir && !fs.IsDir(dirent.Inode.StableAttr) {
|
||||
return linuxerr.ENOTDIR
|
||||
}
|
||||
|
||||
// Copy out to the return frame.
|
||||
fd = ino.AddWatch(dirent, mask)
|
||||
|
||||
return nil
|
||||
})
|
||||
return uintptr(fd), nil, err // Return from the existing value.
|
||||
return uintptr(ino.AddWatch(d.Dentry(), mask)), nil, nil
|
||||
}
|
||||
|
||||
// InotifyRmWatch implements the inotify_rm_watch() syscall.
|
||||
@@ -124,10 +124,10 @@ func InotifyRmWatch(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kerne
|
||||
fd := args[0].Int()
|
||||
wd := args[1].Int()
|
||||
|
||||
ino, file, err := fdToInotify(t, fd)
|
||||
ino, f, err := fdToInotify(t, fd)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer file.DecRef(t)
|
||||
defer f.DecRef(t)
|
||||
return 0, nil, ino.RmWatch(t, wd)
|
||||
}
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package vfs2
|
||||
package linux
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
@@ -1,58 +0,0 @@
|
||||
// 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/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
)
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// Lseek implements linux syscall lseek(2).
|
||||
func Lseek(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
fd := args[0].Int()
|
||||
offset := args[1].Int64()
|
||||
whence := args[2].Int()
|
||||
|
||||
file := t.GetFile(fd)
|
||||
if file == nil {
|
||||
return 0, nil, linuxerr.EBADF
|
||||
}
|
||||
defer file.DecRef(t)
|
||||
|
||||
var sw fs.SeekWhence
|
||||
switch whence {
|
||||
case 0:
|
||||
sw = fs.SeekSet
|
||||
case 1:
|
||||
sw = fs.SeekCurrent
|
||||
case 2:
|
||||
sw = fs.SeekEnd
|
||||
default:
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
offset, serr := file.Seek(t, sw, offset)
|
||||
err := handleIOError(t, false /* partialResult */, serr, linuxerr.ERESTARTSYS, "lseek", file)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return uintptr(offset), nil, err
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/read_write.go)
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/memmap"
|
||||
"gvisor.dev/gvisor/pkg/sentry/mm"
|
||||
@@ -35,9 +36,7 @@ func Brk(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallCo
|
||||
return uintptr(addr), nil, nil
|
||||
}
|
||||
|
||||
// LINT.IfChange
|
||||
|
||||
// Mmap implements linux syscall mmap(2).
|
||||
// Mmap implements Linux syscall mmap(2).
|
||||
func Mmap(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
prot := args[2].Int()
|
||||
flags := args[3].Int()
|
||||
@@ -81,19 +80,18 @@ func Mmap(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
|
||||
|
||||
if !anon {
|
||||
// Convert the passed FD to a file reference.
|
||||
file := t.GetFile(fd)
|
||||
file := t.GetFileVFS2(fd)
|
||||
if file == nil {
|
||||
return 0, nil, linuxerr.EBADF
|
||||
}
|
||||
defer file.DecRef(t)
|
||||
|
||||
flags := file.Flags()
|
||||
// mmap unconditionally requires that the FD is readable.
|
||||
if !flags.Read {
|
||||
if !file.IsReadable() {
|
||||
return 0, nil, linuxerr.EACCES
|
||||
}
|
||||
// MAP_SHARED requires that the FD be writable for PROT_WRITE.
|
||||
if shared && !flags.Write {
|
||||
if shared && !file.IsWritable() {
|
||||
opts.MaxPerms.Write = false
|
||||
}
|
||||
|
||||
@@ -101,22 +99,22 @@ func Mmap(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallC
|
||||
return 0, nil, err
|
||||
}
|
||||
} else if shared {
|
||||
// Back shared anonymous mappings with a special mappable.
|
||||
// Back shared anonymous mappings with an anonymous tmpfs file.
|
||||
opts.Offset = 0
|
||||
m, err := mm.NewSharedAnonMappable(opts.Length, t.Kernel())
|
||||
file, err := tmpfs.NewZeroFile(t, t.Credentials(), t.Kernel().ShmMount(), opts.Length)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
opts.MappingIdentity = m // transfers ownership of m to opts
|
||||
opts.Mappable = m
|
||||
defer file.DecRef(t)
|
||||
if err := file.ConfigureMMap(t, &opts); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
rv, err := t.MemoryManager().MMap(t, opts)
|
||||
return uintptr(rv), nil, err
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/mmap.go)
|
||||
|
||||
// Munmap implements linux syscall munmap(2).
|
||||
func Munmap(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
return 0, nil, t.MemoryManager().MUnmap(t, args[0].Pointer(), args[1].Uint64())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
// Copyright 2020 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.
|
||||
@@ -16,11 +16,13 @@ package linux
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/bits"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
// Mount implements Linux syscall mount(2).
|
||||
@@ -31,21 +33,76 @@ func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
flags := args[3].Uint64()
|
||||
dataAddr := args[4].Pointer()
|
||||
|
||||
// Must have CAP_SYS_ADMIN in the current mount namespace's associated user
|
||||
// namespace.
|
||||
creds := t.Credentials()
|
||||
if !creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, t.MountNamespaceVFS2().Owner) {
|
||||
return 0, nil, linuxerr.EPERM
|
||||
}
|
||||
|
||||
// Ignore magic value that was required before Linux 2.4.
|
||||
if flags&linux.MS_MGC_MSK == linux.MS_MGC_VAL {
|
||||
flags = flags &^ linux.MS_MGC_MSK
|
||||
}
|
||||
|
||||
// Silently allow MS_NOSUID, since we don't implement set-id bits anyway.
|
||||
const unsupported = linux.MS_REMOUNT | linux.MS_SLAVE |
|
||||
linux.MS_UNBINDABLE | linux.MS_MOVE | linux.MS_REC | linux.MS_NODIRATIME |
|
||||
linux.MS_STRICTATIME
|
||||
|
||||
// Linux just allows passing any flags to mount(2) - it won't fail when
|
||||
// unknown or unsupported flags are passed. Since we don't implement
|
||||
// everything, we fail explicitly on flags that are unimplemented.
|
||||
if flags&(unsupported) != 0 {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
// For null-terminated strings related to mount(2), Linux copies in at most
|
||||
// a page worth of data. See fs/namespace.c:copy_mount_string().
|
||||
targetPath, err := copyInPath(t, targetAddr)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
target, err := getTaskPathOperation(t, linux.AT_FDCWD, targetPath, disallowEmptyPath, nofollowFinalSymlink)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer target.Release(t)
|
||||
|
||||
if flags&linux.MS_BIND == linux.MS_BIND {
|
||||
var sourcePath fspath.Path
|
||||
sourcePath, err = copyInPath(t, sourceAddr)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
var sourceTpop taskPathOperation
|
||||
sourceTpop, err = getTaskPathOperation(t, linux.AT_FDCWD, sourcePath, disallowEmptyPath, nofollowFinalSymlink)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer sourceTpop.Release(t)
|
||||
_, err = t.Kernel().VFS().BindAt(t, creds, &sourceTpop.pop, &target.pop)
|
||||
return 0, nil, err
|
||||
}
|
||||
const propagationFlags = linux.MS_SHARED | linux.MS_PRIVATE | linux.MS_SLAVE | linux.MS_UNBINDABLE
|
||||
if propFlag := flags & propagationFlags; propFlag != 0 {
|
||||
// Check if flags is a power of 2. If not then more than one flag is set.
|
||||
if !bits.IsPowerOfTwo64(propFlag) {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
propType := vfs.PropagationTypeFromLinux(propFlag)
|
||||
return 0, nil, t.Kernel().VFS().SetMountPropagationAt(t, creds, &target.pop, propType)
|
||||
}
|
||||
|
||||
// Only copy in source, fstype, and data if we are doing a normal mount.
|
||||
source, err := t.CopyInString(sourceAddr, hostarch.PageSize)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
fsType, err := t.CopyInString(typeAddr, hostarch.PageSize)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
sourcePath, _, err := copyInPath(t, sourceAddr, true /* allowEmpty */)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
targetPath, _, err := copyInPath(t, targetAddr, false /* allowEmpty */)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
data := ""
|
||||
if dataAddr != 0 {
|
||||
// In Linux, a full page is always copied in regardless of null
|
||||
@@ -57,69 +114,25 @@ func Mount(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore magic value that was required before Linux 2.4.
|
||||
if flags&linux.MS_MGC_MSK == linux.MS_MGC_VAL {
|
||||
flags = flags &^ linux.MS_MGC_MSK
|
||||
}
|
||||
|
||||
// Must have CAP_SYS_ADMIN in the mount namespace's associated user
|
||||
// namespace.
|
||||
if !t.HasCapabilityIn(linux.CAP_SYS_ADMIN, t.MountNamespace().UserNamespace()) {
|
||||
return 0, nil, linuxerr.EPERM
|
||||
}
|
||||
|
||||
const unsupportedOps = linux.MS_REMOUNT | linux.MS_BIND |
|
||||
linux.MS_SHARED | linux.MS_PRIVATE | linux.MS_SLAVE |
|
||||
linux.MS_UNBINDABLE | linux.MS_MOVE
|
||||
|
||||
// Silently allow MS_NOSUID, since we don't implement set-id bits
|
||||
// anyway.
|
||||
const unsupportedFlags = linux.MS_NODEV |
|
||||
linux.MS_NODIRATIME | linux.MS_STRICTATIME
|
||||
|
||||
// Linux just allows passing any flags to mount(2) - it won't fail when
|
||||
// unknown or unsupported flags are passed. Since we don't implement
|
||||
// everything, we fail explicitly on flags that are unimplemented.
|
||||
if flags&(unsupportedOps|unsupportedFlags) != 0 {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
rsys, ok := fs.FindFilesystem(fsType)
|
||||
if !ok {
|
||||
return 0, nil, linuxerr.ENODEV
|
||||
}
|
||||
if !rsys.AllowUserMount() {
|
||||
return 0, nil, linuxerr.EPERM
|
||||
}
|
||||
|
||||
var superFlags fs.MountSourceFlags
|
||||
var opts vfs.MountOptions
|
||||
if flags&linux.MS_NOATIME == linux.MS_NOATIME {
|
||||
superFlags.NoAtime = true
|
||||
}
|
||||
if flags&linux.MS_RDONLY == linux.MS_RDONLY {
|
||||
superFlags.ReadOnly = true
|
||||
opts.Flags.NoATime = true
|
||||
}
|
||||
if flags&linux.MS_NOEXEC == linux.MS_NOEXEC {
|
||||
superFlags.NoExec = true
|
||||
opts.Flags.NoExec = true
|
||||
}
|
||||
|
||||
rootInode, err := rsys.Mount(t, sourcePath, superFlags, data, nil)
|
||||
if err != nil {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
if flags&linux.MS_NODEV == linux.MS_NODEV {
|
||||
opts.Flags.NoDev = true
|
||||
}
|
||||
|
||||
if err := fileOpOn(t, linux.AT_FDCWD, targetPath, true /* resolve */, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
|
||||
// Mount will take a reference on rootInode if successful.
|
||||
return t.MountNamespace().Mount(t, d, rootInode)
|
||||
}); err != nil {
|
||||
// Something went wrong. Drop our ref on rootInode before
|
||||
// returning the error.
|
||||
rootInode.DecRef(t)
|
||||
return 0, nil, err
|
||||
if flags&linux.MS_NOSUID == linux.MS_NOSUID {
|
||||
opts.Flags.NoSUID = true
|
||||
}
|
||||
|
||||
return 0, nil, nil
|
||||
if flags&linux.MS_RDONLY == linux.MS_RDONLY {
|
||||
opts.ReadOnly = true
|
||||
}
|
||||
opts.GetFilesystemOptions.Data = data
|
||||
_, err = t.Kernel().VFS().MountAt(t, creds, source, &target.pop, fsType, &opts)
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
// Umount2 implements Linux syscall umount2(2).
|
||||
@@ -127,28 +140,33 @@ func Umount2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
|
||||
addr := args[0].Pointer()
|
||||
flags := args[1].Int()
|
||||
|
||||
// Must have CAP_SYS_ADMIN in the mount namespace's associated user
|
||||
// namespace.
|
||||
//
|
||||
// Currently, this is always the init task's user namespace.
|
||||
creds := t.Credentials()
|
||||
if !creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, t.MountNamespaceVFS2().Owner) {
|
||||
return 0, nil, linuxerr.EPERM
|
||||
}
|
||||
|
||||
const unsupported = linux.MNT_FORCE | linux.MNT_EXPIRE
|
||||
if flags&unsupported != 0 {
|
||||
return 0, nil, linuxerr.EINVAL
|
||||
}
|
||||
|
||||
path, _, err := copyInPath(t, addr, false /* allowEmpty */)
|
||||
path, err := copyInPath(t, addr)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
tpop, err := getTaskPathOperation(t, linux.AT_FDCWD, path, disallowEmptyPath, shouldFollowFinalSymlink(flags&linux.UMOUNT_NOFOLLOW == 0))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer tpop.Release(t)
|
||||
|
||||
// Must have CAP_SYS_ADMIN in the mount namespace's associated user
|
||||
// namespace.
|
||||
//
|
||||
// Currently, this is always the init task's user namespace.
|
||||
if !t.HasCapabilityIn(linux.CAP_SYS_ADMIN, t.MountNamespace().UserNamespace()) {
|
||||
return 0, nil, linuxerr.EPERM
|
||||
opts := vfs.UmountOptions{
|
||||
Flags: uint32(flags &^ linux.UMOUNT_NOFOLLOW),
|
||||
}
|
||||
|
||||
resolve := flags&linux.UMOUNT_NOFOLLOW != linux.UMOUNT_NOFOLLOW
|
||||
detachOnly := flags&linux.MNT_DETACH == linux.MNT_DETACH
|
||||
|
||||
return 0, nil, fileOpOn(t, linux.AT_FDCWD, path, resolve, func(root *fs.Dirent, d *fs.Dirent, _ uint) error {
|
||||
return t.MountNamespace().Unmount(t, d, detachOnly)
|
||||
})
|
||||
return 0, nil, t.Kernel().VFS().UmountAt(t, creds, &tpop.pop, &opts)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package vfs2
|
||||
package linux
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
// Copyright 2020 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.
|
||||
@@ -20,59 +20,48 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/marshal/primitive"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/pipefs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/pipe"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
// LINT.IfChange
|
||||
// Pipe implements Linux syscall pipe(2).
|
||||
func Pipe(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
addr := args[0].Pointer()
|
||||
return 0, nil, pipe2(t, addr, 0)
|
||||
}
|
||||
|
||||
// pipe2 implements the actual system call with flags.
|
||||
func pipe2(t *kernel.Task, addr hostarch.Addr, flags uint) (uintptr, error) {
|
||||
// Pipe2 implements Linux syscall pipe2(2).
|
||||
func Pipe2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
addr := args[0].Pointer()
|
||||
flags := args[1].Int()
|
||||
return 0, nil, pipe2(t, addr, flags)
|
||||
}
|
||||
|
||||
func pipe2(t *kernel.Task, addr hostarch.Addr, flags int32) error {
|
||||
if flags&^(linux.O_NONBLOCK|linux.O_CLOEXEC) != 0 {
|
||||
return 0, linuxerr.EINVAL
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
r, w, err := pipefs.NewConnectedPipeFDs(t, t.Kernel().PipeMount(), uint32(flags&linux.O_NONBLOCK))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r, w := pipe.NewConnectedPipe(t, pipe.DefaultPipeSize)
|
||||
|
||||
r.SetFlags(linuxToFlags(flags).Settable())
|
||||
defer r.DecRef(t)
|
||||
|
||||
w.SetFlags(linuxToFlags(flags).Settable())
|
||||
defer w.DecRef(t)
|
||||
|
||||
fds, err := t.NewFDs(0, []*fs.File{r, w}, kernel.FDFlags{
|
||||
fds, err := t.NewFDsVFS2(0, []*vfs.FileDescription{r, w}, kernel.FDFlags{
|
||||
CloseOnExec: flags&linux.O_CLOEXEC != 0,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := primitive.CopyInt32SliceOut(t, addr, fds); err != nil {
|
||||
for _, fd := range fds {
|
||||
if file, _ := t.FDTable().Remove(t, fd); file != nil {
|
||||
if _, file := t.FDTable().Remove(t, fd); file != nil {
|
||||
file.DecRef(t)
|
||||
}
|
||||
}
|
||||
return 0, err
|
||||
return err
|
||||
}
|
||||
return 0, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pipe implements linux syscall pipe(2).
|
||||
func Pipe(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
addr := args[0].Pointer()
|
||||
|
||||
n, err := pipe2(t, addr, 0)
|
||||
return n, nil, err
|
||||
}
|
||||
|
||||
// Pipe2 implements linux syscall pipe2(2).
|
||||
func Pipe2(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
addr := args[0].Pointer()
|
||||
flags := uint(args[1].Uint())
|
||||
|
||||
n, err := pipe2(t, addr, flags)
|
||||
return n, nil, err
|
||||
}
|
||||
|
||||
// LINT.ThenChange(vfs2/pipe.go)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
// Copyright 2020 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.
|
||||
@@ -15,20 +15,23 @@
|
||||
package linux
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
|
||||
"gvisor.dev/gvisor/pkg/sentry/limits"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// fileCap is the maximum allowable files for poll & select.
|
||||
// fileCap is the maximum allowable files for poll & select. This has no
|
||||
// equivalent in Linux; it exists in gVisor since allocation failure in Go is
|
||||
// unrecoverable.
|
||||
const fileCap = 1024 * 1024
|
||||
|
||||
// Masks for "readable", "writable", and "exceptional" events as defined by
|
||||
@@ -47,9 +50,9 @@ const (
|
||||
selectExceptEvents = linux.POLLPRI
|
||||
)
|
||||
|
||||
// pollState tracks the associated file descriptor and waiter of a PollFD.
|
||||
// pollState tracks the associated file description and waiter of a PollFD.
|
||||
type pollState struct {
|
||||
file *fs.File
|
||||
file *vfs.FileDescription
|
||||
waiter waiter.Entry
|
||||
}
|
||||
|
||||
@@ -57,16 +60,16 @@ type pollState struct {
|
||||
// stored in pfd.FD. If a channel is passed in, the waiter entry in "state" is
|
||||
// used to register with the file for event notifications, and a reference to
|
||||
// the file is stored in "state".
|
||||
func initReadiness(t *kernel.Task, pfd *linux.PollFD, state *pollState, ch chan struct{}) {
|
||||
func initReadiness(t *kernel.Task, pfd *linux.PollFD, state *pollState, ch chan struct{}) error {
|
||||
if pfd.FD < 0 {
|
||||
pfd.REvents = 0
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
file := t.GetFile(pfd.FD)
|
||||
file := t.GetFileVFS2(pfd.FD)
|
||||
if file == nil {
|
||||
pfd.REvents = linux.POLLNVAL
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
if ch == nil {
|
||||
@@ -74,11 +77,14 @@ func initReadiness(t *kernel.Task, pfd *linux.PollFD, state *pollState, ch chan
|
||||
} else {
|
||||
state.file = file
|
||||
state.waiter.Init(waiter.ChannelNotifier(ch), waiter.EventMaskFromLinux(uint32(pfd.Events)))
|
||||
file.EventRegister(&state.waiter)
|
||||
if err := file.EventRegister(&state.waiter); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
r := file.Readiness(waiter.EventMaskFromLinux(uint32(pfd.Events)))
|
||||
pfd.REvents = int16(r.ToLinux()) & pfd.Events
|
||||
return nil
|
||||
}
|
||||
|
||||
// releaseState releases all the pollState in "state".
|
||||
@@ -110,7 +116,9 @@ func pollBlock(t *kernel.Task, pfd []linux.PollFD, timeout time.Duration) (time.
|
||||
defer releaseState(t, state)
|
||||
n := uintptr(0)
|
||||
for i := range pfd {
|
||||
initReadiness(t, &pfd[i], &state[i], ch)
|
||||
if err := initReadiness(t, &pfd[i], &state[i], ch); err != nil {
|
||||
return timeout, 0, err
|
||||
}
|
||||
if pfd[i].REvents != 0 {
|
||||
n++
|
||||
ch = nil
|
||||
@@ -121,12 +129,12 @@ func pollBlock(t *kernel.Task, pfd []linux.PollFD, timeout time.Duration) (time.
|
||||
return timeout, n, nil
|
||||
}
|
||||
|
||||
forever := timeout < 0
|
||||
haveTimeout := timeout >= 0
|
||||
|
||||
for n == 0 {
|
||||
var err error
|
||||
// Wait for a notification.
|
||||
timeout, err = t.BlockWithTimeout(ch, !forever, timeout)
|
||||
timeout, err = t.BlockWithTimeout(ch, haveTimeout, timeout)
|
||||
if err != nil {
|
||||
if linuxerr.Equals(linuxerr.ETIMEDOUT, err) {
|
||||
err = nil
|
||||
@@ -262,7 +270,7 @@ func doSelect(t *kernel.Task, nfds int, readFDs, writeFDs, exceptFDs hostarch.Ad
|
||||
// immediately to ensure we don't leak. Note, another thread
|
||||
// might be about to close fd. This is racy, but that's
|
||||
// OK. Linux is racy in the same way.
|
||||
file := t.GetFile(fd)
|
||||
file := t.GetFileVFS2(fd)
|
||||
if file == nil {
|
||||
return 0, linuxerr.EBADF
|
||||
}
|
||||
@@ -371,7 +379,8 @@ func copyOutTimespecRemaining(t *kernel.Task, startNs ktime.Time, timeout time.D
|
||||
}
|
||||
remaining := timeoutRemaining(t, startNs, timeout)
|
||||
tsRemaining := linux.NsecToTimespec(remaining.Nanoseconds())
|
||||
return copyTimespecOut(t, timespecAddr, &tsRemaining)
|
||||
_, err := tsRemaining.CopyOut(t, timespecAddr)
|
||||
return err
|
||||
}
|
||||
|
||||
// copyOutTimevalRemaining copies the time remaining in timeout to timevalAddr.
|
||||
@@ -383,7 +392,8 @@ func copyOutTimevalRemaining(t *kernel.Task, startNs ktime.Time, timeout time.Du
|
||||
}
|
||||
remaining := timeoutRemaining(t, startNs, timeout)
|
||||
tvRemaining := linux.NsecToTimeval(remaining.Nanoseconds())
|
||||
return copyTimevalOut(t, timevalAddr, &tvRemaining)
|
||||
_, err := tvRemaining.CopyOut(t, timevalAddr)
|
||||
return err
|
||||
}
|
||||
|
||||
// pollRestartBlock encapsulates the state required to restart poll(2) via
|
||||
@@ -442,15 +452,8 @@ func Ppoll(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
startNs = t.Kernel().MonotonicClock().Now()
|
||||
}
|
||||
|
||||
if maskAddr != 0 {
|
||||
mask, err := CopyInSigSet(t, maskAddr, maskSize)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
oldmask := t.SignalMask()
|
||||
t.SetSignalMask(mask)
|
||||
t.SetSavedSignalMask(oldmask)
|
||||
if err := setTempSignalSet(t, maskAddr, maskSize); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
_, n, err := doPoll(t, pfdAddr, nfds, timeout)
|
||||
@@ -480,8 +483,8 @@ func Select(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
|
||||
// Use a negative Duration to indicate "no timeout".
|
||||
timeout := time.Duration(-1)
|
||||
if timevalAddr != 0 {
|
||||
timeval, err := copyTimevalIn(t, timevalAddr)
|
||||
if err != nil {
|
||||
var timeval linux.Timeval
|
||||
if _, err := timeval.CopyIn(t, timevalAddr); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if timeval.Sec < 0 || timeval.Usec < 0 {
|
||||
@@ -499,6 +502,12 @@ func Select(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
|
||||
return n, nil, err
|
||||
}
|
||||
|
||||
// +marshal
|
||||
type sigSetWithSize struct {
|
||||
sigsetAddr uint64
|
||||
sizeofSigset uint64
|
||||
}
|
||||
|
||||
// Pselect implements linux syscall pselect(2).
|
||||
func Pselect(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
nfds := int(args[0].Int()) // select(2) uses an int.
|
||||
@@ -519,19 +528,15 @@ func Pselect(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
|
||||
}
|
||||
|
||||
if maskWithSizeAddr != 0 {
|
||||
maskAddr, size, err := copyInSigSetWithSize(t, maskWithSizeAddr)
|
||||
if err != nil {
|
||||
if t.Arch().Width() != 8 {
|
||||
panic(fmt.Sprintf("unsupported sizeof(void*): %d", t.Arch().Width()))
|
||||
}
|
||||
var maskStruct sigSetWithSize
|
||||
if _, err := maskStruct.CopyIn(t, maskWithSizeAddr); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
if maskAddr != 0 {
|
||||
mask, err := CopyInSigSet(t, maskAddr, size)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
oldmask := t.SignalMask()
|
||||
t.SetSignalMask(mask)
|
||||
t.SetSavedSignalMask(oldmask)
|
||||
if err := setTempSignalSet(t, hostarch.Addr(maskStruct.sigsetAddr), uint(maskStruct.sizeofSigset)); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,3 +548,21 @@ func Pselect(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysca
|
||||
}
|
||||
return n, nil, err
|
||||
}
|
||||
|
||||
func setTempSignalSet(t *kernel.Task, maskAddr hostarch.Addr, maskSize uint) error {
|
||||
if maskAddr == 0 {
|
||||
return nil
|
||||
}
|
||||
if maskSize != linux.SignalSetSize {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
var mask linux.SignalSet
|
||||
if _, err := mask.CopyIn(t, maskAddr); err != nil {
|
||||
return err
|
||||
}
|
||||
mask &^= kernel.UnblockableSignals
|
||||
oldmask := t.SignalMask()
|
||||
t.SetSignalMask(mask)
|
||||
t.SetSavedSignalMask(oldmask)
|
||||
return nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user