summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/kernel/pipe
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/kernel/pipe')
-rw-r--r--pkg/sentry/kernel/pipe/BUILD55
-rw-r--r--pkg/sentry/kernel/pipe/node_test.go320
-rw-r--r--pkg/sentry/kernel/pipe/pipe_state_autogen.go227
-rw-r--r--pkg/sentry/kernel/pipe/pipe_test.go140
-rw-r--r--pkg/sentry/kernel/pipe/pipe_unsafe_state_autogen.go3
5 files changed, 230 insertions, 515 deletions
diff --git a/pkg/sentry/kernel/pipe/BUILD b/pkg/sentry/kernel/pipe/BUILD
deleted file mode 100644
index beba6d97d..000000000
--- a/pkg/sentry/kernel/pipe/BUILD
+++ /dev/null
@@ -1,55 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "pipe",
- srcs = [
- "device.go",
- "node.go",
- "pipe.go",
- "pipe_unsafe.go",
- "pipe_util.go",
- "reader.go",
- "reader_writer.go",
- "save_restore.go",
- "vfs.go",
- "writer.go",
- ],
- visibility = ["//pkg/sentry:internal"],
- deps = [
- "//pkg/abi/linux",
- "//pkg/amutex",
- "//pkg/context",
- "//pkg/marshal/primitive",
- "//pkg/safemem",
- "//pkg/sentry/arch",
- "//pkg/sentry/device",
- "//pkg/sentry/fs",
- "//pkg/sentry/fs/fsutil",
- "//pkg/sentry/vfs",
- "//pkg/sync",
- "//pkg/syserror",
- "//pkg/usermem",
- "//pkg/waiter",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
-
-go_test(
- name = "pipe_test",
- size = "small",
- srcs = [
- "node_test.go",
- "pipe_test.go",
- ],
- library = ":pipe",
- deps = [
- "//pkg/context",
- "//pkg/sentry/contexttest",
- "//pkg/sentry/fs",
- "//pkg/syserror",
- "//pkg/usermem",
- "//pkg/waiter",
- ],
-)
diff --git a/pkg/sentry/kernel/pipe/node_test.go b/pkg/sentry/kernel/pipe/node_test.go
deleted file mode 100644
index d6fb0fdb8..000000000
--- a/pkg/sentry/kernel/pipe/node_test.go
+++ /dev/null
@@ -1,320 +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 pipe
-
-import (
- "testing"
- "time"
-
- "gvisor.dev/gvisor/pkg/context"
- "gvisor.dev/gvisor/pkg/sentry/contexttest"
- "gvisor.dev/gvisor/pkg/sentry/fs"
- "gvisor.dev/gvisor/pkg/syserror"
-)
-
-type sleeper struct {
- context.Context
- ch chan struct{}
-}
-
-func newSleeperContext(t *testing.T) context.Context {
- return &sleeper{
- Context: contexttest.Context(t),
- ch: make(chan struct{}),
- }
-}
-
-func (s *sleeper) SleepStart() <-chan struct{} {
- return s.ch
-}
-
-func (s *sleeper) SleepFinish(bool) {
-}
-
-func (s *sleeper) Cancel() {
- s.ch <- struct{}{}
-}
-
-func (s *sleeper) Interrupted() bool {
- return len(s.ch) != 0
-}
-
-type openResult struct {
- *fs.File
- error
-}
-
-var perms fs.FilePermissions = fs.FilePermissions{
- User: fs.PermMask{Read: true, Write: true},
-}
-
-func testOpenOrDie(ctx context.Context, t *testing.T, n fs.InodeOperations, flags fs.FileFlags, doneChan chan<- struct{}) (*fs.File, error) {
- inode := fs.NewMockInode(ctx, fs.NewMockMountSource(nil), fs.StableAttr{Type: fs.Pipe})
- d := fs.NewDirent(ctx, inode, "pipe")
- file, err := n.GetFile(ctx, d, flags)
- if err != nil {
- t.Errorf("open with flags %+v failed: %v", flags, err)
- return nil, err
- }
- if doneChan != nil {
- doneChan <- struct{}{}
- }
- return file, err
-}
-
-func testOpen(ctx context.Context, t *testing.T, n fs.InodeOperations, flags fs.FileFlags, resChan chan<- openResult) (*fs.File, error) {
- inode := fs.NewMockInode(ctx, fs.NewMockMountSource(nil), fs.StableAttr{Type: fs.Pipe})
- d := fs.NewDirent(ctx, inode, "pipe")
- file, err := n.GetFile(ctx, d, flags)
- if resChan != nil {
- resChan <- openResult{file, err}
- }
- return file, err
-}
-
-func newNamedPipe(t *testing.T) *Pipe {
- return NewPipe(true, DefaultPipeSize)
-}
-
-func newAnonPipe(t *testing.T) *Pipe {
- return NewPipe(false, DefaultPipeSize)
-}
-
-// assertRecvBlocks ensures that a recv attempt on c blocks for at least
-// blockDuration. This is useful for checking that a goroutine that is supposed
-// to be executing a blocking operation is actually blocking.
-func assertRecvBlocks(t *testing.T, c <-chan struct{}, blockDuration time.Duration, failMsg string) {
- select {
- case <-c:
- t.Fatalf(failMsg)
- case <-time.After(blockDuration):
- // Ok, blocked for the required duration.
- }
-}
-
-func TestReadOpenBlocksForWriteOpen(t *testing.T) {
- ctx := newSleeperContext(t)
- f := NewInodeOperations(ctx, perms, newNamedPipe(t))
-
- rDone := make(chan struct{})
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true}, rDone)
-
- // Verify that the open for read is blocking.
- assertRecvBlocks(t, rDone, time.Millisecond*100,
- "open for read not blocking with no writers")
-
- wDone := make(chan struct{})
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Write: true}, wDone)
-
- <-wDone
- <-rDone
-}
-
-func TestWriteOpenBlocksForReadOpen(t *testing.T) {
- ctx := newSleeperContext(t)
- f := NewInodeOperations(ctx, perms, newNamedPipe(t))
-
- wDone := make(chan struct{})
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Write: true}, wDone)
-
- // Verify that the open for write is blocking
- assertRecvBlocks(t, wDone, time.Millisecond*100,
- "open for write not blocking with no readers")
-
- rDone := make(chan struct{})
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true}, rDone)
-
- <-rDone
- <-wDone
-}
-
-func TestMultipleWriteOpenDoesntCountAsReadOpen(t *testing.T) {
- ctx := newSleeperContext(t)
- f := NewInodeOperations(ctx, perms, newNamedPipe(t))
-
- rDone1 := make(chan struct{})
- rDone2 := make(chan struct{})
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true}, rDone1)
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true}, rDone2)
-
- assertRecvBlocks(t, rDone1, time.Millisecond*100,
- "open for read didn't block with no writers")
- assertRecvBlocks(t, rDone2, time.Millisecond*100,
- "open for read didn't block with no writers")
-
- wDone := make(chan struct{})
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Write: true}, wDone)
-
- <-wDone
- <-rDone2
- <-rDone1
-}
-
-func TestClosedReaderBlocksWriteOpen(t *testing.T) {
- ctx := newSleeperContext(t)
- f := NewInodeOperations(ctx, perms, newNamedPipe(t))
-
- rFile, _ := testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true, NonBlocking: true}, nil)
- rFile.DecRef(ctx)
-
- wDone := make(chan struct{})
- // This open for write should block because the reader is now gone.
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Write: true}, wDone)
- assertRecvBlocks(t, wDone, time.Millisecond*100,
- "open for write didn't block with no concurrent readers")
-
- // Open for read again. This should unblock the open for write.
- rDone := make(chan struct{})
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true}, rDone)
-
- <-rDone
- <-wDone
-}
-
-func TestReadWriteOpenNeverBlocks(t *testing.T) {
- ctx := newSleeperContext(t)
- f := NewInodeOperations(ctx, perms, newNamedPipe(t))
-
- rwDone := make(chan struct{})
- // Open for read-write never wait for a reader or writer, even if the
- // nonblocking flag is not set.
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true, Write: true, NonBlocking: false}, rwDone)
- <-rwDone
-}
-
-func TestReadWriteOpenUnblocksReadOpen(t *testing.T) {
- ctx := newSleeperContext(t)
- f := NewInodeOperations(ctx, perms, newNamedPipe(t))
-
- rDone := make(chan struct{})
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true}, rDone)
-
- rwDone := make(chan struct{})
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true, Write: true}, rwDone)
-
- <-rwDone
- <-rDone
-}
-
-func TestReadWriteOpenUnblocksWriteOpen(t *testing.T) {
- ctx := newSleeperContext(t)
- f := NewInodeOperations(ctx, perms, newNamedPipe(t))
-
- wDone := make(chan struct{})
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Write: true}, wDone)
-
- rwDone := make(chan struct{})
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true, Write: true}, rwDone)
-
- <-rwDone
- <-wDone
-}
-
-func TestBlockedOpenIsCancellable(t *testing.T) {
- ctx := newSleeperContext(t)
- f := NewInodeOperations(ctx, perms, newNamedPipe(t))
-
- done := make(chan openResult)
- go testOpen(ctx, t, f, fs.FileFlags{Read: true}, done)
- select {
- case <-done:
- t.Fatalf("open for read didn't block with no writers")
- case <-time.After(time.Millisecond * 100):
- // Ok.
- }
-
- ctx.(*sleeper).Cancel()
- // If the cancel on the sleeper didn't work, the open for read would never
- // return.
- res := <-done
- if res.error != syserror.ErrInterrupted {
- t.Fatalf("Cancellation didn't cause GetFile to return fs.ErrInterrupted, got %v.",
- res.error)
- }
-}
-
-func TestNonblockingReadOpenFileNoWriters(t *testing.T) {
- ctx := newSleeperContext(t)
- f := NewInodeOperations(ctx, perms, newNamedPipe(t))
-
- if _, err := testOpen(ctx, t, f, fs.FileFlags{Read: true, NonBlocking: true}, nil); err != nil {
- t.Fatalf("Nonblocking open for read failed with error %v.", err)
- }
-}
-
-func TestNonblockingWriteOpenFileNoReaders(t *testing.T) {
- ctx := newSleeperContext(t)
- f := NewInodeOperations(ctx, perms, newNamedPipe(t))
-
- if _, err := testOpen(ctx, t, f, fs.FileFlags{Write: true, NonBlocking: true}, nil); err != syserror.ENXIO {
- t.Fatalf("Nonblocking open for write failed unexpected error %v.", err)
- }
-}
-
-func TestNonBlockingReadOpenWithWriter(t *testing.T) {
- ctx := newSleeperContext(t)
- f := NewInodeOperations(ctx, perms, newNamedPipe(t))
-
- wDone := make(chan struct{})
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Write: true}, wDone)
-
- // Open for write blocks since there are no readers yet.
- assertRecvBlocks(t, wDone, time.Millisecond*100,
- "Open for write didn't block with no reader.")
-
- if _, err := testOpen(ctx, t, f, fs.FileFlags{Read: true, NonBlocking: true}, nil); err != nil {
- t.Fatalf("Nonblocking open for read failed with error %v.", err)
- }
-
- // Open for write should now be unblocked.
- <-wDone
-}
-
-func TestNonBlockingWriteOpenWithReader(t *testing.T) {
- ctx := newSleeperContext(t)
- f := NewInodeOperations(ctx, perms, newNamedPipe(t))
-
- rDone := make(chan struct{})
- go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true}, rDone)
-
- // Open for write blocked, since no reader yet.
- assertRecvBlocks(t, rDone, time.Millisecond*100,
- "Open for reader didn't block with no writer.")
-
- if _, err := testOpen(ctx, t, f, fs.FileFlags{Write: true, NonBlocking: true}, nil); err != nil {
- t.Fatalf("Nonblocking open for write failed with error %v.", err)
- }
-
- // Open for write should now be unblocked.
- <-rDone
-}
-
-func TestAnonReadOpen(t *testing.T) {
- ctx := newSleeperContext(t)
- f := NewInodeOperations(ctx, perms, newAnonPipe(t))
-
- if _, err := testOpen(ctx, t, f, fs.FileFlags{Read: true}, nil); err != nil {
- t.Fatalf("open anon pipe for read failed: %v", err)
- }
-}
-
-func TestAnonWriteOpen(t *testing.T) {
- ctx := newSleeperContext(t)
- f := NewInodeOperations(ctx, perms, newAnonPipe(t))
-
- if _, err := testOpen(ctx, t, f, fs.FileFlags{Write: true}, nil); err != nil {
- t.Fatalf("open anon pipe for write failed: %v", err)
- }
-}
diff --git a/pkg/sentry/kernel/pipe/pipe_state_autogen.go b/pkg/sentry/kernel/pipe/pipe_state_autogen.go
new file mode 100644
index 000000000..57451e951
--- /dev/null
+++ b/pkg/sentry/kernel/pipe/pipe_state_autogen.go
@@ -0,0 +1,227 @@
+// automatically generated by stateify.
+
+package pipe
+
+import (
+ "gvisor.dev/gvisor/pkg/state"
+)
+
+func (i *inodeOperations) StateTypeName() string {
+ return "pkg/sentry/kernel/pipe.inodeOperations"
+}
+
+func (i *inodeOperations) StateFields() []string {
+ return []string{
+ "InodeSimpleAttributes",
+ "p",
+ }
+}
+
+func (i *inodeOperations) beforeSave() {}
+
+// +checklocksignore
+func (i *inodeOperations) StateSave(stateSinkObject state.Sink) {
+ i.beforeSave()
+ stateSinkObject.Save(0, &i.InodeSimpleAttributes)
+ stateSinkObject.Save(1, &i.p)
+}
+
+func (i *inodeOperations) afterLoad() {}
+
+// +checklocksignore
+func (i *inodeOperations) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &i.InodeSimpleAttributes)
+ stateSourceObject.Load(1, &i.p)
+}
+
+func (p *Pipe) StateTypeName() string {
+ return "pkg/sentry/kernel/pipe.Pipe"
+}
+
+func (p *Pipe) StateFields() []string {
+ return []string{
+ "isNamed",
+ "readers",
+ "writers",
+ "buf",
+ "off",
+ "size",
+ "max",
+ "hadWriter",
+ }
+}
+
+func (p *Pipe) beforeSave() {}
+
+// +checklocksignore
+func (p *Pipe) StateSave(stateSinkObject state.Sink) {
+ p.beforeSave()
+ stateSinkObject.Save(0, &p.isNamed)
+ stateSinkObject.Save(1, &p.readers)
+ stateSinkObject.Save(2, &p.writers)
+ stateSinkObject.Save(3, &p.buf)
+ stateSinkObject.Save(4, &p.off)
+ stateSinkObject.Save(5, &p.size)
+ stateSinkObject.Save(6, &p.max)
+ stateSinkObject.Save(7, &p.hadWriter)
+}
+
+// +checklocksignore
+func (p *Pipe) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &p.isNamed)
+ stateSourceObject.Load(1, &p.readers)
+ stateSourceObject.Load(2, &p.writers)
+ stateSourceObject.Load(3, &p.buf)
+ stateSourceObject.Load(4, &p.off)
+ stateSourceObject.Load(5, &p.size)
+ stateSourceObject.Load(6, &p.max)
+ stateSourceObject.Load(7, &p.hadWriter)
+ stateSourceObject.AfterLoad(p.afterLoad)
+}
+
+func (r *Reader) StateTypeName() string {
+ return "pkg/sentry/kernel/pipe.Reader"
+}
+
+func (r *Reader) StateFields() []string {
+ return []string{
+ "ReaderWriter",
+ }
+}
+
+func (r *Reader) beforeSave() {}
+
+// +checklocksignore
+func (r *Reader) StateSave(stateSinkObject state.Sink) {
+ r.beforeSave()
+ stateSinkObject.Save(0, &r.ReaderWriter)
+}
+
+func (r *Reader) afterLoad() {}
+
+// +checklocksignore
+func (r *Reader) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &r.ReaderWriter)
+}
+
+func (rw *ReaderWriter) StateTypeName() string {
+ return "pkg/sentry/kernel/pipe.ReaderWriter"
+}
+
+func (rw *ReaderWriter) StateFields() []string {
+ return []string{
+ "Pipe",
+ }
+}
+
+func (rw *ReaderWriter) beforeSave() {}
+
+// +checklocksignore
+func (rw *ReaderWriter) StateSave(stateSinkObject state.Sink) {
+ rw.beforeSave()
+ stateSinkObject.Save(0, &rw.Pipe)
+}
+
+func (rw *ReaderWriter) afterLoad() {}
+
+// +checklocksignore
+func (rw *ReaderWriter) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &rw.Pipe)
+}
+
+func (vp *VFSPipe) StateTypeName() string {
+ return "pkg/sentry/kernel/pipe.VFSPipe"
+}
+
+func (vp *VFSPipe) StateFields() []string {
+ return []string{
+ "pipe",
+ }
+}
+
+func (vp *VFSPipe) beforeSave() {}
+
+// +checklocksignore
+func (vp *VFSPipe) StateSave(stateSinkObject state.Sink) {
+ vp.beforeSave()
+ stateSinkObject.Save(0, &vp.pipe)
+}
+
+func (vp *VFSPipe) afterLoad() {}
+
+// +checklocksignore
+func (vp *VFSPipe) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &vp.pipe)
+}
+
+func (fd *VFSPipeFD) StateTypeName() string {
+ return "pkg/sentry/kernel/pipe.VFSPipeFD"
+}
+
+func (fd *VFSPipeFD) StateFields() []string {
+ return []string{
+ "vfsfd",
+ "FileDescriptionDefaultImpl",
+ "DentryMetadataFileDescriptionImpl",
+ "LockFD",
+ "pipe",
+ }
+}
+
+func (fd *VFSPipeFD) beforeSave() {}
+
+// +checklocksignore
+func (fd *VFSPipeFD) StateSave(stateSinkObject state.Sink) {
+ fd.beforeSave()
+ stateSinkObject.Save(0, &fd.vfsfd)
+ stateSinkObject.Save(1, &fd.FileDescriptionDefaultImpl)
+ stateSinkObject.Save(2, &fd.DentryMetadataFileDescriptionImpl)
+ stateSinkObject.Save(3, &fd.LockFD)
+ stateSinkObject.Save(4, &fd.pipe)
+}
+
+func (fd *VFSPipeFD) afterLoad() {}
+
+// +checklocksignore
+func (fd *VFSPipeFD) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &fd.vfsfd)
+ stateSourceObject.Load(1, &fd.FileDescriptionDefaultImpl)
+ stateSourceObject.Load(2, &fd.DentryMetadataFileDescriptionImpl)
+ stateSourceObject.Load(3, &fd.LockFD)
+ stateSourceObject.Load(4, &fd.pipe)
+}
+
+func (w *Writer) StateTypeName() string {
+ return "pkg/sentry/kernel/pipe.Writer"
+}
+
+func (w *Writer) StateFields() []string {
+ return []string{
+ "ReaderWriter",
+ }
+}
+
+func (w *Writer) beforeSave() {}
+
+// +checklocksignore
+func (w *Writer) StateSave(stateSinkObject state.Sink) {
+ w.beforeSave()
+ stateSinkObject.Save(0, &w.ReaderWriter)
+}
+
+func (w *Writer) afterLoad() {}
+
+// +checklocksignore
+func (w *Writer) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &w.ReaderWriter)
+}
+
+func init() {
+ state.Register((*inodeOperations)(nil))
+ state.Register((*Pipe)(nil))
+ state.Register((*Reader)(nil))
+ state.Register((*ReaderWriter)(nil))
+ state.Register((*VFSPipe)(nil))
+ state.Register((*VFSPipeFD)(nil))
+ state.Register((*Writer)(nil))
+}
diff --git a/pkg/sentry/kernel/pipe/pipe_test.go b/pkg/sentry/kernel/pipe/pipe_test.go
deleted file mode 100644
index 867f4a76b..000000000
--- a/pkg/sentry/kernel/pipe/pipe_test.go
+++ /dev/null
@@ -1,140 +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 pipe
-
-import (
- "bytes"
- "testing"
-
- "gvisor.dev/gvisor/pkg/sentry/contexttest"
- "gvisor.dev/gvisor/pkg/syserror"
- "gvisor.dev/gvisor/pkg/usermem"
- "gvisor.dev/gvisor/pkg/waiter"
-)
-
-func TestPipeRW(t *testing.T) {
- ctx := contexttest.Context(t)
- r, w := NewConnectedPipe(ctx, 65536)
- defer r.DecRef(ctx)
- defer w.DecRef(ctx)
-
- msg := []byte("here's some bytes")
- wantN := int64(len(msg))
- n, err := w.Writev(ctx, usermem.BytesIOSequence(msg))
- if n != wantN || err != nil {
- t.Fatalf("Writev: got (%d, %v), wanted (%d, nil)", n, err, wantN)
- }
-
- buf := make([]byte, len(msg))
- n, err = r.Readv(ctx, usermem.BytesIOSequence(buf))
- if n != wantN || err != nil || !bytes.Equal(buf, msg) {
- t.Fatalf("Readv: got (%d, %v) %q, wanted (%d, nil) %q", n, err, buf, wantN, msg)
- }
-}
-
-func TestPipeReadBlock(t *testing.T) {
- ctx := contexttest.Context(t)
- r, w := NewConnectedPipe(ctx, 65536)
- defer r.DecRef(ctx)
- defer w.DecRef(ctx)
-
- n, err := r.Readv(ctx, usermem.BytesIOSequence(make([]byte, 1)))
- if n != 0 || err != syserror.ErrWouldBlock {
- t.Fatalf("Readv: got (%d, %v), wanted (0, %v)", n, err, syserror.ErrWouldBlock)
- }
-}
-
-func TestPipeWriteBlock(t *testing.T) {
- const atomicIOBytes = 2
- const capacity = MinimumPipeSize
-
- ctx := contexttest.Context(t)
- r, w := NewConnectedPipe(ctx, capacity)
- defer r.DecRef(ctx)
- defer w.DecRef(ctx)
-
- msg := make([]byte, capacity+1)
- n, err := w.Writev(ctx, usermem.BytesIOSequence(msg))
- if wantN, wantErr := int64(capacity), syserror.ErrWouldBlock; n != wantN || err != wantErr {
- t.Fatalf("Writev: got (%d, %v), wanted (%d, %v)", n, err, wantN, wantErr)
- }
-}
-
-func TestPipeWriteUntilEnd(t *testing.T) {
- const atomicIOBytes = 2
-
- ctx := contexttest.Context(t)
- r, w := NewConnectedPipe(ctx, atomicIOBytes)
- defer r.DecRef(ctx)
- defer w.DecRef(ctx)
-
- msg := []byte("here's some bytes")
-
- wDone := make(chan struct{}, 0)
- rDone := make(chan struct{}, 0)
- defer func() {
- // Signal the reader to stop and wait until it does so.
- close(wDone)
- <-rDone
- }()
-
- go func() {
- defer close(rDone)
- // Read from r until done is closed.
- ctx := contexttest.Context(t)
- buf := make([]byte, len(msg)+1)
- dst := usermem.BytesIOSequence(buf)
- e, ch := waiter.NewChannelEntry(nil)
- r.EventRegister(&e, waiter.ReadableEvents)
- defer r.EventUnregister(&e)
- for {
- n, err := r.Readv(ctx, dst)
- dst = dst.DropFirst64(n)
- if err == syserror.ErrWouldBlock {
- select {
- case <-ch:
- continue
- case <-wDone:
- // We expect to have 1 byte left in dst since len(buf) ==
- // len(msg)+1.
- if dst.NumBytes() != 1 || !bytes.Equal(buf[:len(msg)], msg) {
- t.Errorf("Reader: got %q (%d bytes remaining), wanted %q", buf, dst.NumBytes(), msg)
- }
- return
- }
- }
- if err != nil {
- t.Errorf("Readv: got unexpected error %v", err)
- return
- }
- }
- }()
-
- src := usermem.BytesIOSequence(msg)
- e, ch := waiter.NewChannelEntry(nil)
- w.EventRegister(&e, waiter.WritableEvents)
- defer w.EventUnregister(&e)
- for src.NumBytes() != 0 {
- n, err := w.Writev(ctx, src)
- src = src.DropFirst64(n)
- if err == syserror.ErrWouldBlock {
- <-ch
- continue
- }
- if err != nil {
- t.Fatalf("Writev: got (%d, %v)", n, err)
- }
- }
-}
diff --git a/pkg/sentry/kernel/pipe/pipe_unsafe_state_autogen.go b/pkg/sentry/kernel/pipe/pipe_unsafe_state_autogen.go
new file mode 100644
index 000000000..d3b40feb4
--- /dev/null
+++ b/pkg/sentry/kernel/pipe/pipe_unsafe_state_autogen.go
@@ -0,0 +1,3 @@
+// automatically generated by stateify.
+
+package pipe