gvisor/pkg/sentry/syscalls/linux/sys_epoll.go

172 lines
4.8 KiB
Go
Raw Normal View History

// Copyright 2018 Google Inc.
//
// 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 (
"syscall"
"gvisor.googlesource.com/gvisor/pkg/sentry/arch"
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/epoll"
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs"
"gvisor.googlesource.com/gvisor/pkg/sentry/syscalls"
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
"gvisor.googlesource.com/gvisor/pkg/syserror"
"gvisor.googlesource.com/gvisor/pkg/waiter"
)
// EpollCreate1 implements the epoll_create1(2) linux syscall.
func EpollCreate1(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
flags := args[0].Int()
if flags & ^syscall.EPOLL_CLOEXEC != 0 {
return 0, nil, syserror.EINVAL
}
closeOnExec := flags&syscall.EPOLL_CLOEXEC != 0
fd, err := syscalls.CreateEpoll(t, closeOnExec)
if err != nil {
return 0, nil, err
}
return uintptr(fd), nil, nil
}
// EpollCreate implements the epoll_create(2) linux syscall.
func EpollCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
size := args[0].Int()
if size <= 0 {
return 0, nil, syserror.EINVAL
}
fd, err := syscalls.CreateEpoll(t, false)
if err != nil {
return 0, nil, err
}
return uintptr(fd), nil, nil
}
// EpollCtl implements the epoll_ctl(2) linux syscall.
func EpollCtl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
epfd := kdefs.FD(args[0].Int())
op := args[1].Int()
fd := kdefs.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 != syscall.EPOLL_CTL_DEL {
var e syscall.EpollEvent
if _, err := t.CopyIn(eventAddr, &e); err != nil {
return 0, nil, err
}
if e.Events&syscall.EPOLLONESHOT != 0 {
flags |= epoll.OneShot
}
// syscall.EPOLLET is incorrectly generated as a negative number
// in Go, see https://github.com/golang/go/issues/5328 for
// details.
if e.Events&-syscall.EPOLLET != 0 {
flags |= epoll.EdgeTriggered
}
mask = waiter.EventMask(e.Events)
data[0] = e.Fd
data[1] = e.Pad
}
// Perform the requested operations.
switch op {
case syscall.EPOLL_CTL_ADD:
// See fs/eventpoll.c.
mask |= waiter.EventHUp | waiter.EventErr
return 0, nil, syscalls.AddEpoll(t, epfd, fd, flags, mask, data)
case syscall.EPOLL_CTL_DEL:
return 0, nil, syscalls.RemoveEpoll(t, epfd, fd)
case syscall.EPOLL_CTL_MOD:
// Same as EPOLL_CTL_ADD.
mask |= waiter.EventHUp | waiter.EventErr
return 0, nil, syscalls.UpdateEpoll(t, epfd, fd, flags, mask, data)
default:
return 0, nil, syserror.EINVAL
}
}
// copyOutEvents copies epoll events from the kernel to user memory.
func copyOutEvents(t *kernel.Task, addr usermem.Addr, e []epoll.Event) error {
const itemLen = 12
if _, ok := addr.AddLength(uint64(len(e)) * itemLen); !ok {
return syserror.EFAULT
}
b := t.CopyScratchBuffer(itemLen)
for i := range e {
usermem.ByteOrder.PutUint32(b[0:], e[i].Events)
usermem.ByteOrder.PutUint32(b[4:], uint32(e[i].Data[0]))
usermem.ByteOrder.PutUint32(b[8:], uint32(e[i].Data[1]))
if _, err := t.CopyOutBytes(addr, b); err != nil {
return err
}
addr += itemLen
}
return nil
}
// EpollWait implements the epoll_wait(2) linux syscall.
func EpollWait(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
epfd := kdefs.FD(args[0].Int())
eventsAddr := args[1].Pointer()
maxEvents := int(args[2].Int())
timeout := int(args[3].Int())
r, err := syscalls.WaitEpoll(t, epfd, maxEvents, timeout)
if err != nil {
return 0, nil, syserror.ConvertIntr(err, syserror.EINTR)
}
if len(r) != 0 {
if err := copyOutEvents(t, eventsAddr, r); err != nil {
return 0, nil, err
}
}
return uintptr(len(r)), nil, nil
}
// EpollPwait implements the epoll_pwait(2) linux syscall.
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)
}
return EpollWait(t, args)
}