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)