2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
// Package pipe provides a pipe implementation.
|
2018-04-27 17:37:02 +00:00
|
|
|
package pipe
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"sync"
|
|
|
|
"sync/atomic"
|
|
|
|
"syscall"
|
|
|
|
|
|
|
|
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
|
|
|
|
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
|
|
|
|
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
|
|
|
|
"gvisor.googlesource.com/gvisor/pkg/syserror"
|
|
|
|
"gvisor.googlesource.com/gvisor/pkg/waiter"
|
|
|
|
)
|
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
const (
|
|
|
|
// MinimumPipeSize is a hard limit of the minimum size of a pipe.
|
|
|
|
MinimumPipeSize = 64 << 10
|
|
|
|
|
|
|
|
// DefaultPipeSize is the system-wide default size of a pipe in bytes.
|
|
|
|
DefaultPipeSize = MinimumPipeSize
|
|
|
|
|
|
|
|
// MaximumPipeSize is a hard limit on the maximum size of a pipe.
|
|
|
|
MaximumPipeSize = 8 << 20
|
|
|
|
)
|
|
|
|
|
|
|
|
// Sizer is an interface for setting and getting the size of a pipe.
|
|
|
|
//
|
|
|
|
// It is implemented by Pipe and, through embedding, all other types.
|
|
|
|
type Sizer interface {
|
|
|
|
// PipeSize returns the pipe capacity in bytes.
|
|
|
|
PipeSize() int64
|
|
|
|
|
|
|
|
// SetPipeSize sets the new pipe capacity in bytes.
|
|
|
|
//
|
|
|
|
// The new size is returned (which may be capped).
|
|
|
|
SetPipeSize(int64) (int64, error)
|
|
|
|
}
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
// Pipe is an encapsulation of a platform-independent pipe.
|
|
|
|
// It manages a buffered byte queue shared between a reader/writer
|
|
|
|
// pair.
|
2018-08-02 17:41:44 +00:00
|
|
|
//
|
|
|
|
// +stateify savable
|
2018-04-27 17:37:02 +00:00
|
|
|
type Pipe struct {
|
|
|
|
waiter.Queue `state:"nosave"`
|
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
// isNamed indicates whether this is a named pipe.
|
|
|
|
//
|
|
|
|
// This value is immutable.
|
2018-04-27 17:37:02 +00:00
|
|
|
isNamed bool
|
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
// atomicIOBytes is the maximum number of bytes that the pipe will
|
|
|
|
// guarantee atomic reads or writes atomically.
|
|
|
|
//
|
|
|
|
// This value is immutable.
|
|
|
|
atomicIOBytes int64
|
|
|
|
|
|
|
|
// The number of active readers for this pipe.
|
|
|
|
//
|
|
|
|
// Access atomically.
|
|
|
|
readers int32
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
// The number of active writes for this pipe.
|
|
|
|
//
|
|
|
|
// Access atomically.
|
|
|
|
writers int32
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
// mu protects all pipe internal state below.
|
|
|
|
mu sync.Mutex `state:"nosave"`
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
// data is the buffer queue of pipe contents.
|
|
|
|
//
|
|
|
|
// This is protected by mu.
|
|
|
|
data bufferList
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
// max is the maximum size of the pipe in bytes. When this max has been
|
|
|
|
// reached, writers will get EWOULDBLOCK.
|
|
|
|
//
|
|
|
|
// This is protected by mu.
|
|
|
|
max int64
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
// size is the current size of the pipe in bytes.
|
|
|
|
//
|
|
|
|
// This is protected by mu.
|
|
|
|
size int64
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
// hadWriter indicates if this pipe ever had a writer. Note that this
|
|
|
|
// does not necessarily indicate there is *currently* a writer, just
|
|
|
|
// that there has been a writer at some point since the pipe was
|
|
|
|
// created.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
2019-05-22 03:11:26 +00:00
|
|
|
// This is protected by mu.
|
2018-04-27 17:37:02 +00:00
|
|
|
hadWriter bool
|
|
|
|
}
|
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
// NewPipe initializes and returns a pipe.
|
|
|
|
//
|
|
|
|
// N.B. The size and atomicIOBytes will be bounded.
|
|
|
|
func NewPipe(ctx context.Context, isNamed bool, sizeBytes, atomicIOBytes int64) *Pipe {
|
|
|
|
if sizeBytes < MinimumPipeSize {
|
|
|
|
sizeBytes = MinimumPipeSize
|
|
|
|
}
|
|
|
|
if sizeBytes > MaximumPipeSize {
|
|
|
|
sizeBytes = MaximumPipeSize
|
|
|
|
}
|
|
|
|
if atomicIOBytes <= 0 {
|
|
|
|
atomicIOBytes = 1
|
|
|
|
}
|
|
|
|
if atomicIOBytes > sizeBytes {
|
|
|
|
atomicIOBytes = sizeBytes
|
|
|
|
}
|
2019-06-04 19:57:41 +00:00
|
|
|
return &Pipe{
|
2018-04-27 17:37:02 +00:00
|
|
|
isNamed: isNamed,
|
|
|
|
max: sizeBytes,
|
|
|
|
atomicIOBytes: atomicIOBytes,
|
|
|
|
}
|
2019-06-04 19:57:41 +00:00
|
|
|
}
|
2018-04-27 17:37:02 +00:00
|
|
|
|
2019-06-04 19:57:41 +00:00
|
|
|
// NewConnectedPipe initializes a pipe and returns a pair of objects
|
|
|
|
// representing the read and write ends of the pipe.
|
|
|
|
func NewConnectedPipe(ctx context.Context, sizeBytes, atomicIOBytes int64) (*fs.File, *fs.File) {
|
|
|
|
p := NewPipe(ctx, false /* isNamed */, sizeBytes, atomicIOBytes)
|
|
|
|
|
|
|
|
// Build an fs.Dirent for the pipe which will be shared by both
|
|
|
|
// returned files.
|
2019-01-15 04:33:29 +00:00
|
|
|
perms := fs.FilePermissions{
|
|
|
|
User: fs.PermMask{Read: true, Write: true},
|
|
|
|
}
|
|
|
|
iops := NewInodeOperations(ctx, perms, p)
|
2018-04-27 17:37:02 +00:00
|
|
|
ino := pipeDevice.NextIno()
|
|
|
|
sattr := fs.StableAttr{
|
|
|
|
Type: fs.Pipe,
|
|
|
|
DeviceID: pipeDevice.DeviceID(),
|
|
|
|
InodeID: ino,
|
|
|
|
BlockSize: int64(atomicIOBytes),
|
|
|
|
}
|
2019-01-15 04:33:29 +00:00
|
|
|
ms := fs.NewPseudoMountSource()
|
2019-06-04 19:57:41 +00:00
|
|
|
d := fs.NewDirent(fs.NewInode(iops, ms, sattr), fmt.Sprintf("pipe:[%d]", ino))
|
|
|
|
// The p.Open calls below will each take a reference on the Dirent. We
|
|
|
|
// must drop the one we already have.
|
|
|
|
defer d.DecRef()
|
|
|
|
return p.Open(ctx, d, fs.FileFlags{Read: true}), p.Open(ctx, d, fs.FileFlags{Write: true})
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
// Open opens the pipe and returns a new file.
|
|
|
|
//
|
|
|
|
// Precondition: at least one of flags.Read or flags.Write must be set.
|
2019-06-04 19:57:41 +00:00
|
|
|
func (p *Pipe) Open(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) *fs.File {
|
2019-05-22 03:11:26 +00:00
|
|
|
switch {
|
|
|
|
case flags.Read && flags.Write:
|
|
|
|
p.rOpen()
|
|
|
|
p.wOpen()
|
2019-06-04 19:57:41 +00:00
|
|
|
return fs.NewFile(ctx, d, flags, &ReaderWriter{
|
2019-05-22 03:11:26 +00:00
|
|
|
Pipe: p,
|
|
|
|
})
|
|
|
|
case flags.Read:
|
|
|
|
p.rOpen()
|
2019-06-04 19:57:41 +00:00
|
|
|
return fs.NewFile(ctx, d, flags, &Reader{
|
2019-05-22 03:11:26 +00:00
|
|
|
ReaderWriter: ReaderWriter{Pipe: p},
|
|
|
|
})
|
|
|
|
case flags.Write:
|
|
|
|
p.wOpen()
|
2019-06-04 19:57:41 +00:00
|
|
|
return fs.NewFile(ctx, d, flags, &Writer{
|
2019-05-22 03:11:26 +00:00
|
|
|
ReaderWriter: ReaderWriter{Pipe: p},
|
|
|
|
})
|
|
|
|
default:
|
|
|
|
// Precondition violated.
|
|
|
|
panic("invalid pipe flags")
|
|
|
|
}
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// read reads data from the pipe into dst and returns the number of bytes
|
|
|
|
// read, or returns ErrWouldBlock if the pipe is empty.
|
2019-05-22 03:11:26 +00:00
|
|
|
//
|
|
|
|
// Precondition: this pipe must have readers.
|
2018-04-27 17:37:02 +00:00
|
|
|
func (p *Pipe) read(ctx context.Context, dst usermem.IOSequence) (int64, error) {
|
|
|
|
// Don't block for a zero-length read even if the pipe is empty.
|
|
|
|
if dst.NumBytes() == 0 {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
2019-05-22 03:11:26 +00:00
|
|
|
|
|
|
|
// Is the pipe empty?
|
2018-04-27 17:37:02 +00:00
|
|
|
if p.size == 0 {
|
|
|
|
if !p.HasWriters() {
|
|
|
|
// There are no writers, return EOF.
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
return 0, syserror.ErrWouldBlock
|
|
|
|
}
|
2019-05-22 03:11:26 +00:00
|
|
|
|
|
|
|
// Limit how much we consume.
|
|
|
|
if dst.NumBytes() > p.size {
|
|
|
|
dst = dst.TakeFirst64(p.size)
|
|
|
|
}
|
|
|
|
|
|
|
|
done := int64(0)
|
|
|
|
for dst.NumBytes() > 0 {
|
|
|
|
// Pop the first buffer.
|
|
|
|
first := p.data.Front()
|
|
|
|
if first == nil {
|
|
|
|
break
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2019-05-22 03:11:26 +00:00
|
|
|
|
|
|
|
// Copy user data.
|
|
|
|
n, err := dst.CopyOutFrom(ctx, first)
|
|
|
|
done += int64(n)
|
|
|
|
p.size -= n
|
|
|
|
dst = dst.DropFirst64(n)
|
|
|
|
|
|
|
|
// Empty buffer?
|
|
|
|
if first.Empty() {
|
|
|
|
// Push to the free list.
|
|
|
|
p.data.Remove(first)
|
|
|
|
bufferPool.Put(first)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle errors.
|
|
|
|
if err != nil {
|
|
|
|
return done, err
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-22 03:11:26 +00:00
|
|
|
|
|
|
|
return done, nil
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// write writes data from sv into the pipe and returns the number of bytes
|
|
|
|
// written. If no bytes are written because the pipe is full (or has less than
|
|
|
|
// atomicIOBytes free capacity), write returns ErrWouldBlock.
|
2019-05-22 03:11:26 +00:00
|
|
|
//
|
|
|
|
// Precondition: this pipe must have writers.
|
2018-04-27 17:37:02 +00:00
|
|
|
func (p *Pipe) write(ctx context.Context, src usermem.IOSequence) (int64, error) {
|
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
// Can't write to a pipe with no readers.
|
2018-04-27 17:37:02 +00:00
|
|
|
if !p.HasReaders() {
|
|
|
|
return 0, syscall.EPIPE
|
|
|
|
}
|
|
|
|
|
|
|
|
// POSIX requires that a write smaller than atomicIOBytes (PIPE_BUF) be
|
2019-05-22 03:11:26 +00:00
|
|
|
// atomic, but requires no atomicity for writes larger than this.
|
|
|
|
wanted := src.NumBytes()
|
|
|
|
if avail := p.max - p.size; wanted > avail {
|
|
|
|
if wanted <= p.atomicIOBytes {
|
2018-04-27 17:37:02 +00:00
|
|
|
return 0, syserror.ErrWouldBlock
|
|
|
|
}
|
2019-05-22 03:11:26 +00:00
|
|
|
// Limit to the available capacity.
|
|
|
|
src = src.TakeFirst64(avail)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
done := int64(0)
|
|
|
|
for src.NumBytes() > 0 {
|
|
|
|
// Need a new buffer?
|
|
|
|
last := p.data.Back()
|
|
|
|
if last == nil || last.Full() {
|
|
|
|
// Add a new buffer to the data list.
|
|
|
|
last = newBuffer()
|
|
|
|
p.data.PushBack(last)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy user data.
|
|
|
|
n, err := src.CopyInTo(ctx, last)
|
|
|
|
done += int64(n)
|
2018-04-27 17:37:02 +00:00
|
|
|
p.size += n
|
2019-05-22 03:11:26 +00:00
|
|
|
src = src.DropFirst64(n)
|
|
|
|
|
|
|
|
// Handle errors.
|
|
|
|
if err != nil {
|
|
|
|
return done, err
|
|
|
|
}
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2019-05-22 03:11:26 +00:00
|
|
|
if wanted > done {
|
2018-04-27 17:37:02 +00:00
|
|
|
// Partial write due to full pipe.
|
2019-05-22 03:11:26 +00:00
|
|
|
return done, syserror.ErrWouldBlock
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
2019-05-22 03:11:26 +00:00
|
|
|
|
|
|
|
return done, nil
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// rOpen signals a new reader of the pipe.
|
|
|
|
func (p *Pipe) rOpen() {
|
|
|
|
atomic.AddInt32(&p.readers, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// wOpen signals a new writer of the pipe.
|
|
|
|
func (p *Pipe) wOpen() {
|
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
p.hadWriter = true
|
|
|
|
atomic.AddInt32(&p.writers, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// rClose signals that a reader has closed their end of the pipe.
|
|
|
|
func (p *Pipe) rClose() {
|
|
|
|
newReaders := atomic.AddInt32(&p.readers, -1)
|
|
|
|
if newReaders < 0 {
|
|
|
|
panic(fmt.Sprintf("Refcounting bug, pipe has negative readers: %v", newReaders))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// wClose signals that a writer has closed their end of the pipe.
|
|
|
|
func (p *Pipe) wClose() {
|
|
|
|
newWriters := atomic.AddInt32(&p.writers, -1)
|
|
|
|
if newWriters < 0 {
|
|
|
|
panic(fmt.Sprintf("Refcounting bug, pipe has negative writers: %v.", newWriters))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasReaders returns whether the pipe has any active readers.
|
|
|
|
func (p *Pipe) HasReaders() bool {
|
|
|
|
return atomic.LoadInt32(&p.readers) > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasWriters returns whether the pipe has any active writers.
|
|
|
|
func (p *Pipe) HasWriters() bool {
|
|
|
|
return atomic.LoadInt32(&p.writers) > 0
|
|
|
|
}
|
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
// rReadinessLocked calculates the read readiness.
|
|
|
|
//
|
|
|
|
// Precondition: mu must be held.
|
2018-04-27 17:37:02 +00:00
|
|
|
func (p *Pipe) rReadinessLocked() waiter.EventMask {
|
|
|
|
ready := waiter.EventMask(0)
|
|
|
|
if p.HasReaders() && p.data.Front() != nil {
|
|
|
|
ready |= waiter.EventIn
|
|
|
|
}
|
|
|
|
if !p.HasWriters() && p.hadWriter {
|
2018-05-07 23:37:08 +00:00
|
|
|
// POLLHUP must be suppressed until the pipe has had at least one writer
|
2018-04-27 17:37:02 +00:00
|
|
|
// at some point. Otherwise a reader thread may poll and immediately get
|
|
|
|
// a POLLHUP before the writer ever opens the pipe, which the reader may
|
|
|
|
// interpret as the writer opening then closing the pipe.
|
|
|
|
ready |= waiter.EventHUp
|
|
|
|
}
|
|
|
|
return ready
|
|
|
|
}
|
|
|
|
|
|
|
|
// rReadiness returns a mask that states whether the read end of the pipe is
|
|
|
|
// ready for reading.
|
|
|
|
func (p *Pipe) rReadiness() waiter.EventMask {
|
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
return p.rReadinessLocked()
|
|
|
|
}
|
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
// wReadinessLocked calculates the write readiness.
|
|
|
|
//
|
|
|
|
// Precondition: mu must be held.
|
2018-04-27 17:37:02 +00:00
|
|
|
func (p *Pipe) wReadinessLocked() waiter.EventMask {
|
|
|
|
ready := waiter.EventMask(0)
|
|
|
|
if p.HasWriters() && p.size < p.max {
|
|
|
|
ready |= waiter.EventOut
|
|
|
|
}
|
|
|
|
if !p.HasReaders() {
|
|
|
|
ready |= waiter.EventErr
|
|
|
|
}
|
|
|
|
return ready
|
|
|
|
}
|
|
|
|
|
|
|
|
// wReadiness returns a mask that states whether the write end of the pipe
|
|
|
|
// is ready for writing.
|
|
|
|
func (p *Pipe) wReadiness() waiter.EventMask {
|
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
return p.wReadinessLocked()
|
|
|
|
}
|
|
|
|
|
|
|
|
// rwReadiness returns a mask that states whether a read-write handle to the
|
|
|
|
// pipe is ready for IO.
|
|
|
|
func (p *Pipe) rwReadiness() waiter.EventMask {
|
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
return p.rReadinessLocked() | p.wReadinessLocked()
|
|
|
|
}
|
|
|
|
|
2019-05-22 03:11:26 +00:00
|
|
|
// queued returns the amount of queued data.
|
|
|
|
func (p *Pipe) queued() int64 {
|
2018-04-27 17:37:02 +00:00
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
return p.size
|
|
|
|
}
|
2019-05-22 03:11:26 +00:00
|
|
|
|
|
|
|
// PipeSize implements PipeSizer.PipeSize.
|
|
|
|
func (p *Pipe) PipeSize() int64 {
|
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
return p.max
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetPipeSize implements PipeSize.SetPipeSize.
|
|
|
|
func (p *Pipe) SetPipeSize(size int64) (int64, error) {
|
|
|
|
if size < 0 {
|
|
|
|
return 0, syserror.EINVAL
|
|
|
|
}
|
|
|
|
if size < MinimumPipeSize {
|
|
|
|
size = MinimumPipeSize // Per spec.
|
|
|
|
}
|
|
|
|
if size > MaximumPipeSize {
|
|
|
|
return 0, syserror.EPERM
|
|
|
|
}
|
|
|
|
p.mu.Lock()
|
|
|
|
defer p.mu.Unlock()
|
|
|
|
if size < p.size {
|
|
|
|
return 0, syserror.EBUSY
|
|
|
|
}
|
|
|
|
p.max = size
|
|
|
|
return size, nil
|
|
|
|
}
|