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  }