github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/socket/unix/transport/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 transport 16 17 import ( 18 "github.com/nicocha30/gvisor-ligolo/pkg/context" 19 "github.com/nicocha30/gvisor-ligolo/pkg/syserr" 20 "github.com/nicocha30/gvisor-ligolo/pkg/waiter" 21 ) 22 23 // queue is a buffer queue. 24 // 25 // +stateify savable 26 type queue struct { 27 queueRefs 28 29 ReaderQueue *waiter.Queue 30 WriterQueue *waiter.Queue 31 32 mu queueMutex `state:"nosave"` 33 closed bool 34 unread bool 35 used int64 36 limit int64 37 dataList messageList 38 } 39 40 // Close closes q for reading and writing. It is immediately not writable and 41 // will become unreadable when no more data is pending. 42 // 43 // Both the read and write queues must be notified after closing: 44 // q.ReaderQueue.Notify(waiter.ReadableEvents) 45 // q.WriterQueue.Notify(waiter.WritableEvents) 46 func (q *queue) Close() { 47 q.mu.Lock() 48 q.closed = true 49 q.mu.Unlock() 50 } 51 52 // Reset empties the queue and Releases all of the Entries. 53 // 54 // Both the read and write queues must be notified after resetting: 55 // q.ReaderQueue.Notify(waiter.ReadableEvents) 56 // q.WriterQueue.Notify(waiter.WritableEvents) 57 func (q *queue) Reset(ctx context.Context) { 58 q.mu.Lock() 59 dataList := q.dataList 60 q.dataList.Reset() 61 q.used = 0 62 q.mu.Unlock() 63 64 for cur := dataList.Front(); cur != nil; cur = cur.Next() { 65 cur.Release(ctx) 66 } 67 } 68 69 // DecRef implements RefCounter.DecRef. 70 func (q *queue) DecRef(ctx context.Context) { 71 q.queueRefs.DecRef(func() { 72 // We don't need to notify after resetting because no one cares about 73 // this queue after all references have been dropped. 74 q.Reset(ctx) 75 }) 76 } 77 78 // IsReadable determines if q is currently readable. 79 func (q *queue) IsReadable() bool { 80 q.mu.Lock() 81 defer q.mu.Unlock() 82 83 return q.closed || q.dataList.Front() != nil 84 } 85 86 // bufWritable returns true if there is space for writing. 87 // 88 // N.B. Linux only considers a unix socket "writable" if >75% of the buffer is 89 // free. 90 // 91 // See net/unix/af_unix.c:unix_writeable. 92 func (q *queue) bufWritable() bool { 93 return 4*q.used < q.limit 94 } 95 96 // IsWritable determines if q is currently writable. 97 func (q *queue) IsWritable() bool { 98 q.mu.Lock() 99 defer q.mu.Unlock() 100 101 return q.closed || q.bufWritable() 102 } 103 104 // Enqueue adds an entry to the data queue if room is available. 105 // 106 // If discardEmpty is true and there are zero bytes of data, the packet is 107 // dropped. 108 // 109 // If truncate is true, Enqueue may truncate the message before enqueuing it. 110 // Otherwise, the entire message must fit. If l is less than the size of data, 111 // err indicates why. 112 // 113 // If notify is true, ReaderQueue.Notify must be called: 114 // q.ReaderQueue.Notify(waiter.ReadableEvents) 115 func (q *queue) Enqueue(ctx context.Context, data [][]byte, c ControlMessages, from Address, discardEmpty bool, truncate bool) (l int64, notify bool, err *syserr.Error) { 116 q.mu.Lock() 117 118 if q.closed { 119 q.mu.Unlock() 120 return 0, false, syserr.ErrClosedForSend 121 } 122 123 for _, d := range data { 124 l += int64(len(d)) 125 } 126 if discardEmpty && l == 0 { 127 q.mu.Unlock() 128 c.Release(ctx) 129 return 0, false, nil 130 } 131 132 free := q.limit - q.used 133 134 if l > free && truncate { 135 if free <= 0 { 136 // Message can't fit right now. 137 q.mu.Unlock() 138 return 0, false, syserr.ErrWouldBlock 139 } 140 141 l = free 142 err = syserr.ErrWouldBlock 143 } 144 145 if l > q.limit { 146 // Message is too big to ever fit. 147 q.mu.Unlock() 148 return 0, false, syserr.ErrMessageTooLong 149 } 150 151 if l > free { 152 // Message can't fit right now, and could not be truncated. 153 q.mu.Unlock() 154 return 0, false, syserr.ErrWouldBlock 155 } 156 157 // Aggregate l bytes of data. This will truncate the data if l is less than 158 // the total bytes held in data. 159 v := make([]byte, l) 160 for i, b := 0, v; i < len(data) && len(b) > 0; i++ { 161 n := copy(b, data[i]) 162 b = b[n:] 163 } 164 165 notify = q.dataList.Front() == nil 166 q.used += l 167 q.dataList.PushBack(&message{ 168 Data: v, 169 Control: c, 170 Address: from, 171 }) 172 173 q.mu.Unlock() 174 175 return l, notify, err 176 } 177 178 // Dequeue removes the first entry in the data queue, if one exists. 179 // 180 // If notify is true, WriterQueue.Notify must be called: 181 // q.WriterQueue.Notify(waiter.WritableEvents) 182 func (q *queue) Dequeue() (e *message, notify bool, err *syserr.Error) { 183 q.mu.Lock() 184 185 if q.dataList.Front() == nil { 186 err := syserr.ErrWouldBlock 187 if q.closed { 188 err = syserr.ErrClosedForReceive 189 if q.unread { 190 err = syserr.ErrConnectionReset 191 } 192 } 193 q.mu.Unlock() 194 195 return nil, false, err 196 } 197 198 notify = !q.bufWritable() 199 200 e = q.dataList.Front() 201 q.dataList.Remove(e) 202 q.used -= e.Length() 203 204 notify = notify && q.bufWritable() 205 206 q.mu.Unlock() 207 208 return e, notify, nil 209 } 210 211 // Peek returns the first entry in the data queue, if one exists. 212 func (q *queue) Peek() (*message, *syserr.Error) { 213 q.mu.Lock() 214 defer q.mu.Unlock() 215 216 if q.dataList.Front() == nil { 217 err := syserr.ErrWouldBlock 218 if q.closed { 219 if err = syserr.ErrClosedForReceive; q.unread { 220 err = syserr.ErrConnectionReset 221 } 222 } 223 return nil, err 224 } 225 226 return q.dataList.Front().Peek(), nil 227 } 228 229 // QueuedSize returns the number of bytes currently in the queue, that is, the 230 // number of readable bytes. 231 func (q *queue) QueuedSize() int64 { 232 q.mu.Lock() 233 defer q.mu.Unlock() 234 return q.used 235 } 236 237 // MaxQueueSize returns the maximum number of bytes storable in the queue. 238 func (q *queue) MaxQueueSize() int64 { 239 q.mu.Lock() 240 defer q.mu.Unlock() 241 return q.limit 242 } 243 244 // SetMaxQueueSize sets the maximum number of bytes storable in the queue. 245 func (q *queue) SetMaxQueueSize(v int64) { 246 q.mu.Lock() 247 defer q.mu.Unlock() 248 q.limit = v 249 } 250 251 // CloseUnread sets flag to indicate that the peer is closed (not shutdown) 252 // with unread data. So if read on this queue shall return ECONNRESET error. 253 func (q *queue) CloseUnread() { 254 q.mu.Lock() 255 defer q.mu.Unlock() 256 q.unread = true 257 }