summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/syscalls
diff options
context:
space:
mode:
authorDean Deng <deandeng@google.com>2020-08-27 16:52:21 -0700
committerAndrei Vagin <avagin@gmail.com>2020-09-09 17:53:10 -0700
commitbb089f9c9075a78e8bde7ff946bac77badc08894 (patch)
tree86026686e411a273239a8a5637853194c6e97022 /pkg/sentry/syscalls
parent05166f14c93323d6279987ae3fe9a803ad188ade (diff)
Fix vfs2 pipe behavior when splicing to a non-pipe.
Fixes *.sh Java runtime tests, where splice()-ing from a pipe to /dev/zero would not actually empty the pipe. There was no guarantee that the data would actually be consumed on a splice operation unless the output file's implementation of Write/PWrite actually called VFSPipeFD.CopyIn. Now, whatever bytes are "written" are consumed regardless of whether CopyIn is called or not. Furthermore, the number of bytes in the IOSequence for reads is now capped at the amount of data actually available. Before, splicing to /dev/zero would always return the requested splice size without taking the actual available data into account. This change also refactors the case where an input file is spliced into an output pipe so that it follows a similar pattern, which is arguably cleaner anyway. Updates #3576. PiperOrigin-RevId: 328843954
Diffstat (limited to 'pkg/sentry/syscalls')
-rw-r--r--pkg/sentry/syscalls/linux/vfs2/splice.go35
1 files changed, 16 insertions, 19 deletions
diff --git a/pkg/sentry/syscalls/linux/vfs2/splice.go b/pkg/sentry/syscalls/linux/vfs2/splice.go
index 75bfa2c79..192411393 100644
--- a/pkg/sentry/syscalls/linux/vfs2/splice.go
+++ b/pkg/sentry/syscalls/linux/vfs2/splice.go
@@ -131,18 +131,14 @@ func Splice(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
case inIsPipe && outIsPipe:
n, err = pipe.Splice(t, outPipeFD, inPipeFD, count)
case inIsPipe:
+ n, err = inPipeFD.SpliceToNonPipe(t, outFile, outOffset, count)
if outOffset != -1 {
- n, err = outFile.PWrite(t, inPipeFD.IOSequence(count), outOffset, vfs.WriteOptions{})
outOffset += n
- } else {
- n, err = outFile.Write(t, inPipeFD.IOSequence(count), vfs.WriteOptions{})
}
case outIsPipe:
+ n, err = outPipeFD.SpliceFromNonPipe(t, inFile, inOffset, count)
if inOffset != -1 {
- n, err = inFile.PRead(t, outPipeFD.IOSequence(count), inOffset, vfs.ReadOptions{})
inOffset += n
- } else {
- n, err = inFile.Read(t, outPipeFD.IOSequence(count), vfs.ReadOptions{})
}
default:
panic("not possible")
@@ -341,17 +337,15 @@ func Sendfile(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
if outIsPipe {
for n < count {
var spliceN int64
- if offset != -1 {
- spliceN, err = inFile.PRead(t, outPipeFD.IOSequence(count), offset, vfs.ReadOptions{})
- offset += spliceN
- } else {
- spliceN, err = inFile.Read(t, outPipeFD.IOSequence(count), vfs.ReadOptions{})
- }
+ 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
+ }
n += spliceN
if err == syserror.ErrWouldBlock && !nonBlock {
err = dw.waitForBoth(t)
@@ -371,19 +365,18 @@ 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 == io.EOF {
- // We reached the end of the file. Eat the error and exit the loop.
- err = nil
+ 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
- if err != nil {
- break
- }
// Write all of the bytes that we read. This may need
// multiple write calls to complete.
- wbuf := buf[:n]
+ wbuf := buf[:readN]
for len(wbuf) > 0 {
var writeN int64
writeN, err = outFile.Write(t, usermem.BytesIOSequence(wbuf), vfs.WriteOptions{})
@@ -398,6 +391,10 @@ func Sendfile(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc
notWritten := int64(len(wbuf))
n -= notWritten
if offset != -1 {
+ // TODO(gvisor.dev/issue/3779): The inFile offset will be incorrect if we
+ // roll back, because it has already been advanced by the full amount.
+ // Merely seeking on inFile does not work, because there may be concurrent
+ // file operations.
offset -= notWritten
}
break