github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/kernel/pipe/pipe_util.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package pipe
    16  
    17  import (
    18  	"io"
    19  	"math"
    20  
    21  	"golang.org/x/sys/unix"
    22  	"github.com/metacubex/gvisor/pkg/abi/linux"
    23  	"github.com/metacubex/gvisor/pkg/context"
    24  	"github.com/metacubex/gvisor/pkg/errors/linuxerr"
    25  	"github.com/metacubex/gvisor/pkg/marshal/primitive"
    26  	"github.com/metacubex/gvisor/pkg/safemem"
    27  	"github.com/metacubex/gvisor/pkg/sentry/arch"
    28  	"github.com/metacubex/gvisor/pkg/usermem"
    29  	"github.com/metacubex/gvisor/pkg/waiter"
    30  )
    31  
    32  // This file contains Pipe file functionality that is tied to neither VFS nor
    33  // the old fs architecture.
    34  
    35  // Release cleans up the pipe's state.
    36  func (p *Pipe) Release(context.Context) {
    37  	p.rClose()
    38  	p.wClose()
    39  
    40  	// Wake up readers and writers.
    41  	p.queue.Notify(waiter.ReadableEvents | waiter.WritableEvents)
    42  }
    43  
    44  // Read reads from the Pipe into dst.
    45  func (p *Pipe) Read(ctx context.Context, dst usermem.IOSequence) (int64, error) {
    46  	n, err := p.read(dst.NumBytes(), func(srcs safemem.BlockSeq) (uint64, error) {
    47  		var done uint64
    48  		for !srcs.IsEmpty() {
    49  			src := srcs.Head()
    50  			n, err := dst.CopyOut(ctx, src.ToSlice())
    51  			done += uint64(n)
    52  			if err != nil {
    53  				return done, err
    54  			}
    55  			dst = dst.DropFirst(n)
    56  			srcs = srcs.Tail()
    57  		}
    58  		return done, nil
    59  	}, true /* removeFromSrc */)
    60  	if n > 0 {
    61  		p.queue.Notify(waiter.WritableEvents)
    62  	}
    63  	return n, err
    64  }
    65  
    66  func (p *Pipe) read(count int64, f func(srcs safemem.BlockSeq) (uint64, error), removeFromSrc bool) (int64, error) {
    67  	p.mu.Lock()
    68  	defer p.mu.Unlock()
    69  	n, err := p.peekLocked(0, count, f)
    70  	if n > 0 && removeFromSrc {
    71  		p.consumeLocked(n)
    72  	}
    73  	return n, err
    74  }
    75  
    76  // WriteTo writes to w from the Pipe.
    77  func (p *Pipe) WriteTo(ctx context.Context, w io.Writer, count int64, dup bool) (int64, error) {
    78  	n, err := p.read(count, func(srcs safemem.BlockSeq) (uint64, error) {
    79  		return safemem.FromIOWriter{w}.WriteFromBlocks(srcs)
    80  	}, !dup /* removeFromSrc */)
    81  	if n > 0 && !dup {
    82  		p.queue.Notify(waiter.WritableEvents)
    83  	}
    84  	return n, err
    85  }
    86  
    87  // Write writes to the Pipe from src.
    88  func (p *Pipe) Write(ctx context.Context, src usermem.IOSequence) (int64, error) {
    89  	n, err := p.write(src.NumBytes(), func(dsts safemem.BlockSeq) (uint64, error) {
    90  		var done uint64
    91  		for !dsts.IsEmpty() {
    92  			dst := dsts.Head()
    93  			n, err := src.CopyIn(ctx, dst.ToSlice())
    94  			done += uint64(n)
    95  			if err != nil {
    96  				return done, err
    97  			}
    98  			src = src.DropFirst(n)
    99  			dsts = dsts.Tail()
   100  		}
   101  		return done, nil
   102  	})
   103  	if n > 0 {
   104  		p.queue.Notify(waiter.ReadableEvents)
   105  	}
   106  	if linuxerr.Equals(linuxerr.EPIPE, err) {
   107  		// If we are returning EPIPE send SIGPIPE to the task.
   108  		if sendSig := linux.SignalNoInfoFuncFromContext(ctx); sendSig != nil {
   109  			sendSig(linux.SIGPIPE)
   110  		}
   111  	}
   112  	return n, err
   113  }
   114  
   115  func (p *Pipe) write(count int64, f func(safemem.BlockSeq) (uint64, error)) (int64, error) {
   116  	p.mu.Lock()
   117  	defer p.mu.Unlock()
   118  	return p.writeLocked(count, f)
   119  }
   120  
   121  // ReadFrom reads from r to the Pipe.
   122  func (p *Pipe) ReadFrom(ctx context.Context, r io.Reader, count int64) (int64, error) {
   123  	n, err := p.write(count, func(dsts safemem.BlockSeq) (uint64, error) {
   124  		return safemem.FromIOReader{r}.ReadToBlocks(dsts)
   125  	})
   126  	if n > 0 {
   127  		p.queue.Notify(waiter.ReadableEvents)
   128  	}
   129  	return n, err
   130  }
   131  
   132  // Readiness returns the ready events in the underlying pipe.
   133  func (p *Pipe) Readiness(mask waiter.EventMask) waiter.EventMask {
   134  	return p.rwReadiness() & mask
   135  }
   136  
   137  // Ioctl implements ioctls on the Pipe.
   138  func (p *Pipe) Ioctl(ctx context.Context, io usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) {
   139  	// Switch on ioctl request.
   140  	switch int(args[1].Int()) {
   141  	case linux.FIONREAD:
   142  		v := p.queued()
   143  		if v > math.MaxInt32 {
   144  			v = math.MaxInt32 // Silently truncate.
   145  		}
   146  		// Copy result to userspace.
   147  		iocc := usermem.IOCopyContext{
   148  			IO:  io,
   149  			Ctx: ctx,
   150  			Opts: usermem.IOOpts{
   151  				AddressSpaceActive: true,
   152  			},
   153  		}
   154  		_, err := primitive.CopyInt32Out(&iocc, args[2].Pointer(), int32(v))
   155  		return 0, err
   156  	default:
   157  		return 0, unix.ENOTTY
   158  	}
   159  }