Coverage Report

Created: 2025-08-03 06:36

/src/frr/lib/buffer.c
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * Buffering of output and input.
4
 * Copyright (C) 1998 Kunihiro Ishiguro
5
 */
6
7
#include <zebra.h>
8
9
#include "memory.h"
10
#include "buffer.h"
11
#include "log.h"
12
#include "network.h"
13
#include "lib_errors.h"
14
15
#include <stddef.h>
16
17
DEFINE_MTYPE_STATIC(LIB, BUFFER, "Buffer");
18
DEFINE_MTYPE_STATIC(LIB, BUFFER_DATA, "Buffer data");
19
20
/* Buffer master. */
21
struct buffer {
22
  /* Data list. */
23
  struct buffer_data *head;
24
  struct buffer_data *tail;
25
26
  /* Size of each buffer_data chunk. */
27
  size_t size;
28
};
29
30
/* Data container. */
31
struct buffer_data {
32
  struct buffer_data *next;
33
34
  /* Location to add new data. */
35
  size_t cp;
36
37
  /* Pointer to data not yet flushed. */
38
  size_t sp;
39
40
  /* Actual data stream (variable length). */
41
  unsigned char data[]; /* real dimension is buffer->size */
42
};
43
44
/* It should always be true that: 0 <= sp <= cp <= size */
45
46
/* Default buffer size (used if none specified).  It is rounded up to the
47
   next page boundary. */
48
2
#define BUFFER_SIZE_DEFAULT   4096
49
50
0
#define BUFFER_DATA_FREE(D) XFREE(MTYPE_BUFFER_DATA, (D))
51
52
/* Make new buffer. */
53
struct buffer *buffer_new(size_t size)
54
4
{
55
4
  struct buffer *b;
56
57
4
  b = XCALLOC(MTYPE_BUFFER, sizeof(struct buffer));
58
59
4
  if (size)
60
0
    b->size = size;
61
4
  else {
62
4
    static size_t default_size;
63
4
    if (!default_size) {
64
2
      long pgsz = sysconf(_SC_PAGESIZE);
65
2
      default_size = ((((BUFFER_SIZE_DEFAULT - 1) / pgsz) + 1)
66
2
          * pgsz);
67
2
    }
68
4
    b->size = default_size;
69
4
  }
70
71
4
  return b;
72
4
}
73
74
/* Free buffer. */
75
void buffer_free(struct buffer *b)
76
0
{
77
0
  buffer_reset(b);
78
0
  XFREE(MTYPE_BUFFER, b);
79
0
}
80
81
/* Make string clone. */
82
char *buffer_getstr(struct buffer *b)
83
0
{
84
0
  size_t totlen = 0;
85
0
  struct buffer_data *data;
86
0
  char *s;
87
0
  char *p;
88
89
0
  for (data = b->head; data; data = data->next)
90
0
    totlen += data->cp - data->sp;
91
0
  if (!(s = XMALLOC(MTYPE_TMP, totlen + 1)))
92
0
    return NULL;
93
0
  p = s;
94
0
  for (data = b->head; data; data = data->next) {
95
0
    memcpy(p, data->data + data->sp, data->cp - data->sp);
96
0
    p += data->cp - data->sp;
97
0
  }
98
0
  *p = '\0';
99
0
  return s;
100
0
}
101
102
/* Clear and free all allocated data. */
103
void buffer_reset(struct buffer *b)
104
0
{
105
0
  struct buffer_data *data;
106
0
  struct buffer_data *next;
107
108
0
  for (data = b->head; data; data = next) {
109
0
    next = data->next;
110
0
    BUFFER_DATA_FREE(data);
111
0
  }
112
0
  b->head = b->tail = NULL;
113
0
}
114
115
/* Add buffer_data to the end of buffer. */
116
static struct buffer_data *buffer_add(struct buffer *b)
117
0
{
118
0
  struct buffer_data *d;
119
120
0
  d = XMALLOC(MTYPE_BUFFER_DATA,
121
0
        offsetof(struct buffer_data, data) + b->size);
122
0
  d->cp = d->sp = 0;
123
0
  d->next = NULL;
124
125
0
  if (b->tail)
126
0
    b->tail->next = d;
127
0
  else
128
0
    b->head = d;
129
0
  b->tail = d;
130
131
0
  return d;
132
0
}
133
134
/* Write data to buffer. */
135
void buffer_put(struct buffer *b, const void *p, size_t size)
136
0
{
137
0
  struct buffer_data *data = b->tail;
138
0
  const char *ptr = p;
139
140
  /* We use even last one byte of data buffer. */
141
0
  while (size) {
142
0
    size_t chunk;
143
144
    /* If there is no data buffer add it. */
145
0
    if (data == NULL || data->cp == b->size)
146
0
      data = buffer_add(b);
147
148
0
    chunk = ((size <= (b->size - data->cp)) ? size
149
0
              : (b->size - data->cp));
150
0
    memcpy((data->data + data->cp), ptr, chunk);
151
0
    size -= chunk;
152
0
    ptr += chunk;
153
0
    data->cp += chunk;
154
0
  }
155
0
}
156
157
/* Insert character into the buffer. */
158
void buffer_putc(struct buffer *b, uint8_t c)
159
0
{
160
0
  buffer_put(b, &c, 1);
161
0
}
162
163
/* Put string to the buffer. */
164
void buffer_putstr(struct buffer *b, const char *c)
165
0
{
166
0
  buffer_put(b, c, strlen(c));
167
0
}
168
169
/* Expand \n to \r\n */
170
void buffer_put_crlf(struct buffer *b, const void *origp, size_t origsize)
171
0
{
172
0
  struct buffer_data *data = b->tail;
173
0
  const char *p = origp, *end = p + origsize, *lf;
174
0
  size_t size;
175
176
0
  lf = memchr(p, '\n', end - p);
177
178
  /* We use even last one byte of data buffer. */
179
0
  while (p < end) {
180
0
    size_t avail, chunk;
181
182
    /* If there is no data buffer add it. */
183
0
    if (data == NULL || data->cp == b->size)
184
0
      data = buffer_add(b);
185
186
0
    size = (lf ? lf : end) - p;
187
0
    avail = b->size - data->cp;
188
189
0
    chunk = (size <= avail) ? size : avail;
190
0
    memcpy(data->data + data->cp, p, chunk);
191
192
0
    p += chunk;
193
0
    data->cp += chunk;
194
195
0
    if (lf && size <= avail) {
196
      /* we just copied up to (including) a '\n' */
197
0
      if (data->cp == b->size)
198
0
        data = buffer_add(b);
199
0
      data->data[data->cp++] = '\r';
200
0
      if (data->cp == b->size)
201
0
        data = buffer_add(b);
202
0
      data->data[data->cp++] = '\n';
203
204
0
      p++;
205
0
      lf = memchr(p, '\n', end - p);
206
0
    }
207
0
  }
208
0
}
209
210
/* Keep flushing data to the fd until the buffer is empty or an error is
211
   encountered or the operation would block. */
212
buffer_status_t buffer_flush_all(struct buffer *b, int fd)
213
0
{
214
0
  buffer_status_t ret;
215
0
  struct buffer_data *head;
216
0
  size_t head_sp;
217
218
0
  if (!b->head)
219
0
    return BUFFER_EMPTY;
220
0
  head_sp = (head = b->head)->sp;
221
  /* Flush all data. */
222
0
  while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) {
223
0
    if ((b->head == head) && (head_sp == head->sp)
224
0
        && (errno != EINTR))
225
      /* No data was flushed, so kernel buffer must be full.
226
       */
227
0
      return ret;
228
0
    head_sp = (head = b->head)->sp;
229
0
  }
230
231
0
  return ret;
232
0
}
233
234
/* Flush enough data to fill a terminal window of the given scene (used only
235
   by vty telnet interface). */
236
buffer_status_t buffer_flush_window(struct buffer *b, int fd, int width,
237
            int height, int erase_flag,
238
            int no_more_flag)
239
0
{
240
0
  int nbytes;
241
0
  int iov_alloc;
242
0
  int iov_index;
243
0
  struct iovec *iov;
244
0
  struct iovec small_iov[3];
245
0
  char more[] = " --More-- ";
246
0
  char erase[] = {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
247
0
      0x08, 0x08, ' ',  ' ',  ' ',  ' ',  ' ',  ' ',
248
0
      ' ',  ' ',  ' ',  ' ',  0x08, 0x08, 0x08, 0x08,
249
0
      0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
250
0
  struct buffer_data *data;
251
0
  int column;
252
253
0
  if (!b->head)
254
0
    return BUFFER_EMPTY;
255
256
0
  if (height < 1)
257
0
    height = 1;
258
0
  else if (height >= 2)
259
0
    height--;
260
0
  if (width < 1)
261
0
    width = 1;
262
263
  /* For erase and more data add two to b's buffer_data count.*/
264
0
  if (b->head->next == NULL) {
265
0
    iov_alloc = array_size(small_iov);
266
0
    iov = small_iov;
267
0
  } else {
268
0
    iov_alloc = ((height * (width + 2)) / b->size) + 10;
269
0
    iov = XMALLOC(MTYPE_TMP, iov_alloc * sizeof(*iov));
270
0
  }
271
0
  iov_index = 0;
272
273
  /* Previously print out is performed. */
274
0
  if (erase_flag) {
275
0
    iov[iov_index].iov_base = erase;
276
0
    iov[iov_index].iov_len = sizeof(erase);
277
0
    iov_index++;
278
0
  }
279
280
  /* Output data. */
281
0
  column = 1; /* Column position of next character displayed. */
282
0
  for (data = b->head; data && (height > 0); data = data->next) {
283
0
    size_t cp;
284
285
0
    cp = data->sp;
286
0
    while ((cp < data->cp) && (height > 0)) {
287
      /* Calculate lines remaining and column position after
288
         displaying
289
         this character. */
290
0
      if (data->data[cp] == '\r')
291
0
        column = 1;
292
0
      else if ((data->data[cp] == '\n')
293
0
         || (column == width)) {
294
0
        column = 1;
295
0
        height--;
296
0
      } else
297
0
        column++;
298
0
      cp++;
299
0
    }
300
0
    iov[iov_index].iov_base = (char *)(data->data + data->sp);
301
0
    iov[iov_index++].iov_len = cp - data->sp;
302
0
    data->sp = cp;
303
304
0
    if (iov_index == iov_alloc)
305
    /* This should not ordinarily happen. */
306
0
    {
307
0
      iov_alloc *= 2;
308
0
      if (iov != small_iov) {
309
0
        iov = XREALLOC(MTYPE_TMP, iov,
310
0
                 iov_alloc * sizeof(*iov));
311
0
      } else {
312
        /* This should absolutely never occur. */
313
0
        flog_err_sys(
314
0
          EC_LIB_SYSTEM_CALL,
315
0
          "%s: corruption detected: iov_small overflowed; head %p, tail %p, head->next %p",
316
0
          __func__, (void *)b->head,
317
0
          (void *)b->tail, (void *)b->head->next);
318
0
        iov = XMALLOC(MTYPE_TMP,
319
0
                iov_alloc * sizeof(*iov));
320
0
        memcpy(iov, small_iov, sizeof(small_iov));
321
0
      }
322
0
    }
323
0
  }
324
325
  /* In case of `more' display need. */
326
0
  if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) {
327
0
    iov[iov_index].iov_base = more;
328
0
    iov[iov_index].iov_len = sizeof(more);
329
0
    iov_index++;
330
0
  }
331
332
333
0
#ifdef IOV_MAX
334
  /* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
335
     example: Solaris2.6 are defined IOV_MAX size at 16.     */
336
0
  {
337
0
    struct iovec *c_iov = iov;
338
0
    nbytes = 0; /* Make sure it's initialized. */
339
340
0
    while (iov_index > 0) {
341
0
      int iov_size;
342
343
0
      iov_size =
344
0
        ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
345
0
      nbytes = writev(fd, c_iov, iov_size);
346
0
      if (nbytes < 0) {
347
0
        flog_err(EC_LIB_SOCKET,
348
0
           "%s: writev to fd %d failed: %s",
349
0
           __func__, fd, safe_strerror(errno));
350
0
        break;
351
0
      }
352
353
      /* move pointer io-vector */
354
0
      c_iov += iov_size;
355
0
      iov_index -= iov_size;
356
0
    }
357
0
  }
358
#else  /* IOV_MAX */
359
  nbytes = writev(fd, iov, iov_index);
360
  if (nbytes < 0)
361
    flog_err(EC_LIB_SOCKET, "%s: writev to fd %d failed: %s",
362
       __func__, fd, safe_strerror(errno));
363
#endif /* IOV_MAX */
364
365
  /* Free printed buffer data. */
366
0
  while (b->head && (b->head->sp == b->head->cp)) {
367
0
    struct buffer_data *del;
368
0
    if (!(b->head = (del = b->head)->next))
369
0
      b->tail = NULL;
370
0
    BUFFER_DATA_FREE(del);
371
0
  }
372
373
0
  if (iov != small_iov)
374
0
    XFREE(MTYPE_TMP, iov);
375
376
0
  return (nbytes < 0) ? BUFFER_ERROR
377
0
          : (b->head ? BUFFER_PENDING : BUFFER_EMPTY);
378
0
}
379
380
/* This function (unlike other buffer_flush* functions above) is designed
381
to work with non-blocking sockets.  It does not attempt to write out
382
all of the queued data, just a "big" chunk.  It returns 0 if it was
383
able to empty out the buffers completely, 1 if more flushing is
384
required later, or -1 on a fatal write error. */
385
buffer_status_t buffer_flush_available(struct buffer *b, int fd)
386
0
{
387
388
/* These are just reasonable values to make sure a significant amount of
389
data is written.  There's no need to go crazy and try to write it all
390
in one shot. */
391
0
#ifdef IOV_MAX
392
0
#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
393
#else
394
#define MAX_CHUNKS 16
395
#endif
396
0
#define MAX_FLUSH 131072
397
398
0
  struct buffer_data *d;
399
0
  size_t written;
400
0
  struct iovec iov[MAX_CHUNKS];
401
0
  size_t iovcnt = 0;
402
0
  size_t nbyte = 0;
403
404
0
  if (fd < 0)
405
0
    return BUFFER_ERROR;
406
407
0
  for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
408
0
       d = d->next, iovcnt++) {
409
0
    iov[iovcnt].iov_base = d->data + d->sp;
410
0
    nbyte += (iov[iovcnt].iov_len = d->cp - d->sp);
411
0
  }
412
413
0
  if (!nbyte)
414
    /* No data to flush: should we issue a warning message? */
415
0
    return BUFFER_EMPTY;
416
417
  /* only place where written should be sign compared */
418
0
  if ((ssize_t)(written = writev(fd, iov, iovcnt)) < 0) {
419
0
    if (ERRNO_IO_RETRY(errno))
420
      /* Calling code should try again later. */
421
0
      return BUFFER_PENDING;
422
0
    flog_err(EC_LIB_SOCKET, "%s: write error on fd %d: %s",
423
0
       __func__, fd, safe_strerror(errno));
424
0
    return BUFFER_ERROR;
425
0
  }
426
427
  /* Free printed buffer data. */
428
0
  while (written > 0) {
429
0
    if (!(d = b->head)) {
430
0
      flog_err(
431
0
        EC_LIB_DEVELOPMENT,
432
0
        "%s: corruption detected: buffer queue empty, but written is %lu",
433
0
        __func__, (unsigned long)written);
434
0
      break;
435
0
    }
436
0
    if (written < d->cp - d->sp) {
437
0
      d->sp += written;
438
0
      return BUFFER_PENDING;
439
0
    }
440
441
0
    written -= (d->cp - d->sp);
442
0
    if (!(b->head = d->next))
443
0
      b->tail = NULL;
444
0
    BUFFER_DATA_FREE(d);
445
0
  }
446
447
0
  return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
448
449
0
#undef MAX_CHUNKS
450
0
#undef MAX_FLUSH
451
0
}
452
453
buffer_status_t buffer_write(struct buffer *b, int fd, const void *p,
454
           size_t size)
455
0
{
456
0
  ssize_t nbytes;
457
458
0
  if (b->head)
459
    /* Buffer is not empty, so do not attempt to write the new data.
460
     */
461
0
    nbytes = 0;
462
0
  else {
463
0
    nbytes = write(fd, p, size);
464
0
    if (nbytes < 0) {
465
0
      if (ERRNO_IO_RETRY(errno))
466
0
        nbytes = 0;
467
0
      else {
468
0
        flog_err(EC_LIB_SOCKET,
469
0
           "%s: write error on fd %d: %s",
470
0
           __func__, fd, safe_strerror(errno));
471
0
        return BUFFER_ERROR;
472
0
      }
473
0
    }
474
0
  }
475
  /* Add any remaining data to the buffer. */
476
0
  {
477
0
    size_t written = nbytes;
478
0
    if (written < size)
479
0
      buffer_put(b, ((const char *)p) + written,
480
0
           size - written);
481
0
  }
482
0
  return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
483
0
}