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