mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Handle EOF properly in splice/sendfile.
Use HandleIOErrorVFS2 instead of custom error handling. PiperOrigin-RevId: 333227581
This commit is contained in:
@@ -17,6 +17,7 @@ package pipe
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
|
||||
@@ -215,7 +216,7 @@ func (p *Pipe) readLocked(ctx context.Context, ops readOps) (int64, error) {
|
||||
if p.view.Size() == 0 {
|
||||
if !p.HasWriters() {
|
||||
// There are no writers, return EOF.
|
||||
return 0, nil
|
||||
return 0, io.EOF
|
||||
}
|
||||
return 0, syserror.ErrWouldBlock
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/pipe"
|
||||
slinux "gvisor.dev/gvisor/pkg/sentry/syscalls/linux"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
@@ -146,11 +147,6 @@ func Splice(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
|
||||
panic("at least one end of splice must be a pipe")
|
||||
}
|
||||
|
||||
if n == 0 && err == io.EOF {
|
||||
// We reached the end of the file. Eat the error and exit the loop.
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
if n != 0 || err != syserror.ErrWouldBlock || nonBlock {
|
||||
break
|
||||
}
|
||||
@@ -171,15 +167,16 @@ func Splice(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
|
||||
}
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
return 0, nil, err
|
||||
if n != 0 {
|
||||
// On Linux, inotify behavior is not very consistent with splice(2). We try
|
||||
// our best to emulate Linux for very basic calls to splice, where for some
|
||||
// reason, events are generated for output files, but not input files.
|
||||
outFile.Dentry().InotifyWithParent(t, linux.IN_MODIFY, 0, vfs.PathEvent)
|
||||
}
|
||||
|
||||
// On Linux, inotify behavior is not very consistent with splice(2). We try
|
||||
// our best to emulate Linux for very basic calls to splice, where for some
|
||||
// reason, events are generated for output files, but not input files.
|
||||
outFile.Dentry().InotifyWithParent(t, linux.IN_MODIFY, 0, vfs.PathEvent)
|
||||
return uintptr(n), nil, nil
|
||||
// We can only pass a single file to handleIOError, so pick inFile arbitrarily.
|
||||
// This is used only for debugging purposes.
|
||||
return uintptr(n), nil, slinux.HandleIOErrorVFS2(t, n != 0, err, syserror.ERESTARTSYS, "splice", outFile)
|
||||
}
|
||||
|
||||
// Tee implements Linux syscall tee(2).
|
||||
@@ -251,11 +248,20 @@ func Tee(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallCo
|
||||
break
|
||||
}
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, nil, err
|
||||
|
||||
if n != 0 {
|
||||
outFile.Dentry().InotifyWithParent(t, linux.IN_MODIFY, 0, vfs.PathEvent)
|
||||
|
||||
// If a partial write is completed, the error is dropped. Log it here.
|
||||
if err != nil && err != io.EOF && err != syserror.ErrWouldBlock {
|
||||
log.Debugf("tee completed a partial write with error: %v", err)
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
outFile.Dentry().InotifyWithParent(t, linux.IN_MODIFY, 0, vfs.PathEvent)
|
||||
return uintptr(n), nil, nil
|
||||
|
||||
// We can only pass a single file to handleIOError, so pick inFile arbitrarily.
|
||||
// This is used only for debugging purposes.
|
||||
return uintptr(n), nil, slinux.HandleIOErrorVFS2(t, n != 0, err, syserror.ERESTARTSYS, "tee", inFile)
|
||||
}
|
||||
|
||||
// Sendfile implements linux system call sendfile(2).
|
||||
@@ -348,11 +354,6 @@ func Sendfile(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
for n < count {
|
||||
var spliceN int64
|
||||
spliceN, err = outPipeFD.SpliceFromNonPipe(t, inFile, offset, count)
|
||||
if spliceN == 0 && err == io.EOF {
|
||||
// We reached the end of the file. Eat the error and exit the loop.
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
if offset != -1 {
|
||||
offset += spliceN
|
||||
}
|
||||
@@ -375,13 +376,6 @@ func Sendfile(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
} else {
|
||||
readN, err = inFile.Read(t, usermem.BytesIOSequence(buf), vfs.ReadOptions{})
|
||||
}
|
||||
if readN == 0 && err != nil {
|
||||
if err == io.EOF {
|
||||
// We reached the end of the file. Eat the error before exiting the loop.
|
||||
err = nil
|
||||
}
|
||||
break
|
||||
}
|
||||
n += readN
|
||||
|
||||
// Write all of the bytes that we read. This may need
|
||||
@@ -432,13 +426,20 @@ func Sendfile(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
|
||||
}
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
return 0, nil, err
|
||||
if n != 0 {
|
||||
inFile.Dentry().InotifyWithParent(t, linux.IN_ACCESS, 0, vfs.PathEvent)
|
||||
outFile.Dentry().InotifyWithParent(t, linux.IN_MODIFY, 0, vfs.PathEvent)
|
||||
|
||||
if err != nil && err != io.EOF && err != syserror.ErrWouldBlock {
|
||||
// If a partial write is completed, the error is dropped. Log it here.
|
||||
log.Debugf("sendfile completed a partial write with error: %v", err)
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
inFile.Dentry().InotifyWithParent(t, linux.IN_ACCESS, 0, vfs.PathEvent)
|
||||
outFile.Dentry().InotifyWithParent(t, linux.IN_MODIFY, 0, vfs.PathEvent)
|
||||
return uintptr(n), nil, nil
|
||||
// We can only pass a single file to handleIOError, so pick inFile arbitrarily.
|
||||
// This is used only for debugging purposes.
|
||||
return uintptr(n), nil, slinux.HandleIOErrorVFS2(t, n != 0, err, syserror.ERESTARTSYS, "sendfile", inFile)
|
||||
}
|
||||
|
||||
// dualWaiter is used to wait on one or both vfs.FileDescriptions. It is not
|
||||
|
||||
Reference in New Issue
Block a user