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 }