github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/devpts/queue.go (about)

     1  // Copyright 2018 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 devpts
    16  
    17  import (
    18  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    19  	"github.com/SagerNet/gvisor/pkg/context"
    20  	"github.com/SagerNet/gvisor/pkg/marshal/primitive"
    21  	"github.com/SagerNet/gvisor/pkg/safemem"
    22  	"github.com/SagerNet/gvisor/pkg/sentry/arch"
    23  	"github.com/SagerNet/gvisor/pkg/sentry/kernel"
    24  	"github.com/SagerNet/gvisor/pkg/sync"
    25  	"github.com/SagerNet/gvisor/pkg/syserror"
    26  	"github.com/SagerNet/gvisor/pkg/usermem"
    27  	"github.com/SagerNet/gvisor/pkg/waiter"
    28  )
    29  
    30  // waitBufMaxBytes is the maximum size of a wait buffer. It is based on
    31  // TTYB_DEFAULT_MEM_LIMIT.
    32  const waitBufMaxBytes = 131072
    33  
    34  // queue represents one of the input or output queues between a pty master and
    35  // replica. Bytes written to a queue are added to the read buffer until it is
    36  // full, at which point they are written to the wait buffer. Bytes are
    37  // processed (i.e. undergo termios transformations) as they are added to the
    38  // read buffer. The read buffer is readable when its length is nonzero and
    39  // readable is true.
    40  //
    41  // +stateify savable
    42  type queue struct {
    43  	// mu protects everything in queue.
    44  	mu sync.Mutex `state:"nosave"`
    45  
    46  	// readBuf is buffer of data ready to be read when readable is true.
    47  	// This data has been processed.
    48  	readBuf []byte
    49  
    50  	// waitBuf contains data that can't fit into readBuf. It is put here
    51  	// until it can be loaded into the read buffer. waitBuf contains data
    52  	// that hasn't been processed.
    53  	waitBuf    [][]byte
    54  	waitBufLen uint64
    55  
    56  	// readable indicates whether the read buffer can be read from.  In
    57  	// canonical mode, there can be an unterminated line in the read buffer,
    58  	// so readable must be checked.
    59  	readable bool
    60  
    61  	// transform is the the queue's function for transforming bytes
    62  	// entering the queue. For example, transform might convert all '\r's
    63  	// entering the queue to '\n's.
    64  	transformer
    65  }
    66  
    67  // readReadiness returns whether q is ready to be read from.
    68  func (q *queue) readReadiness(t *linux.KernelTermios) waiter.EventMask {
    69  	q.mu.Lock()
    70  	defer q.mu.Unlock()
    71  	if len(q.readBuf) > 0 && q.readable {
    72  		return waiter.ReadableEvents
    73  	}
    74  	return waiter.EventMask(0)
    75  }
    76  
    77  // writeReadiness returns whether q is ready to be written to.
    78  func (q *queue) writeReadiness(t *linux.KernelTermios) waiter.EventMask {
    79  	q.mu.Lock()
    80  	defer q.mu.Unlock()
    81  	if q.waitBufLen < waitBufMaxBytes {
    82  		return waiter.WritableEvents
    83  	}
    84  	return waiter.EventMask(0)
    85  }
    86  
    87  // readableSize writes the number of readable bytes to userspace.
    88  func (q *queue) readableSize(t *kernel.Task, io usermem.IO, args arch.SyscallArguments) error {
    89  	q.mu.Lock()
    90  	defer q.mu.Unlock()
    91  	size := primitive.Int32(0)
    92  	if q.readable {
    93  		size = primitive.Int32(len(q.readBuf))
    94  	}
    95  
    96  	_, err := size.CopyOut(t, args[2].Pointer())
    97  	return err
    98  
    99  }
   100  
   101  // read reads from q to userspace. It returns the number of bytes read as well
   102  // as whether the read caused more readable data to become available (whether
   103  // data was pushed from the wait buffer to the read buffer).
   104  //
   105  // Preconditions: l.termiosMu must be held for reading.
   106  func (q *queue) read(ctx context.Context, dst usermem.IOSequence, l *lineDiscipline) (int64, bool, error) {
   107  	q.mu.Lock()
   108  	defer q.mu.Unlock()
   109  
   110  	if !q.readable {
   111  		return 0, false, syserror.ErrWouldBlock
   112  	}
   113  
   114  	if dst.NumBytes() > canonMaxBytes {
   115  		dst = dst.TakeFirst(canonMaxBytes)
   116  	}
   117  
   118  	n, err := dst.CopyOutFrom(ctx, safemem.ReaderFunc(func(dst safemem.BlockSeq) (uint64, error) {
   119  		src := safemem.BlockSeqOf(safemem.BlockFromSafeSlice(q.readBuf))
   120  		n, err := safemem.CopySeq(dst, src)
   121  		if err != nil {
   122  			return 0, err
   123  		}
   124  		q.readBuf = q.readBuf[n:]
   125  
   126  		// If we read everything, this queue is no longer readable.
   127  		if len(q.readBuf) == 0 {
   128  			q.readable = false
   129  		}
   130  
   131  		return n, nil
   132  	}))
   133  	if err != nil {
   134  		return 0, false, err
   135  	}
   136  
   137  	// Move data from the queue's wait buffer to its read buffer.
   138  	nPushed := q.pushWaitBufLocked(l)
   139  
   140  	return int64(n), nPushed > 0, nil
   141  }
   142  
   143  // write writes to q from userspace.
   144  //
   145  // Preconditions: l.termiosMu must be held for reading.
   146  func (q *queue) write(ctx context.Context, src usermem.IOSequence, l *lineDiscipline) (int64, error) {
   147  	q.mu.Lock()
   148  	defer q.mu.Unlock()
   149  
   150  	// Copy data into the wait buffer.
   151  	n, err := src.CopyInTo(ctx, safemem.WriterFunc(func(src safemem.BlockSeq) (uint64, error) {
   152  		copyLen := src.NumBytes()
   153  		room := waitBufMaxBytes - q.waitBufLen
   154  		// If out of room, return EAGAIN.
   155  		if room == 0 && copyLen > 0 {
   156  			return 0, syserror.ErrWouldBlock
   157  		}
   158  		// Cap the size of the wait buffer.
   159  		if copyLen > room {
   160  			copyLen = room
   161  			src = src.TakeFirst64(room)
   162  		}
   163  		buf := make([]byte, copyLen)
   164  
   165  		// Copy the data into the wait buffer.
   166  		dst := safemem.BlockSeqOf(safemem.BlockFromSafeSlice(buf))
   167  		n, err := safemem.CopySeq(dst, src)
   168  		if err != nil {
   169  			return 0, err
   170  		}
   171  		q.waitBufAppend(buf)
   172  
   173  		return n, nil
   174  	}))
   175  	if err != nil {
   176  		return 0, err
   177  	}
   178  
   179  	// Push data from the wait to the read buffer.
   180  	q.pushWaitBufLocked(l)
   181  
   182  	return n, nil
   183  }
   184  
   185  // writeBytes writes to q from b.
   186  //
   187  // Preconditions: l.termiosMu must be held for reading.
   188  func (q *queue) writeBytes(b []byte, l *lineDiscipline) {
   189  	q.mu.Lock()
   190  	defer q.mu.Unlock()
   191  
   192  	// Write to the wait buffer.
   193  	q.waitBufAppend(b)
   194  	q.pushWaitBufLocked(l)
   195  }
   196  
   197  // pushWaitBufLocked fills the queue's read buffer with data from the wait
   198  // buffer.
   199  //
   200  // Preconditions:
   201  // * l.termiosMu must be held for reading.
   202  // * q.mu must be locked.
   203  func (q *queue) pushWaitBufLocked(l *lineDiscipline) int {
   204  	if q.waitBufLen == 0 {
   205  		return 0
   206  	}
   207  
   208  	// Move data from the wait to the read buffer.
   209  	var total int
   210  	var i int
   211  	for i = 0; i < len(q.waitBuf); i++ {
   212  		n := q.transform(l, q, q.waitBuf[i])
   213  		total += n
   214  		if n != len(q.waitBuf[i]) {
   215  			// The read buffer filled up without consuming the
   216  			// entire buffer.
   217  			q.waitBuf[i] = q.waitBuf[i][n:]
   218  			break
   219  		}
   220  	}
   221  
   222  	// Update wait buffer based on consumed data.
   223  	q.waitBuf = q.waitBuf[i:]
   224  	q.waitBufLen -= uint64(total)
   225  
   226  	return total
   227  }
   228  
   229  // Precondition: q.mu must be locked.
   230  func (q *queue) waitBufAppend(b []byte) {
   231  	q.waitBuf = append(q.waitBuf, b)
   232  	q.waitBufLen += uint64(len(b))
   233  }