• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

proftpd / proftpd / 29618379545

17 Jul 2026 10:35PM UTC coverage: 92.427% (-0.6%) from 93.032%
29618379545

push

github

web-flow
When populating the names of the quotatab limit/tally tables, use our own `sstrncpy()` function.

48818 of 52818 relevant lines covered (92.43%)

244.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.79
/src/pool.c
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 1997, 1998 Public Flood Software
4
 * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
5
 * Copyright (c) 2001-2026 The ProFTPD Project team
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
19
 *
20
 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
21
 * and other respective copyright holders give permission to link this program
22
 * with OpenSSL, and distribute the resulting executable, without including
23
 * the source code for OpenSSL in the source distribution.
24
 */
25

26
/* Resource allocation code */
27

28
#include "conf.h"
29

30
/* Manage free storage blocks */
31

32
union align {
33
  char *cp;
34
  void (*f)(void);
35
  long l;
36
  FILE *fp;
37
  double d;
38
};
39

40
#define CLICK_SZ (sizeof(union align))
41

42
union block_hdr {
43
  union align a;
44

45
  /* Padding */
46
#if defined(_LP64) || defined(__LP64__)
47
  char pad[32];
48
#endif
49

50
  /* Actual header */
51
  struct {
52
    void *endp;
53
    union block_hdr *next;
54
    void *first_avail;
55
  } h;
56
};
57

58
static union block_hdr *block_freelist = NULL;
59

60
/* Statistics */
61
static unsigned int stat_malloc = 0;        /* incr when malloc required */
62
static unsigned int stat_freehit = 0;        /* incr when freelist used */
63

64
static const char *trace_channel = "pool";
65

66
/* Debug flags */
67
static int debug_flags = 0;
68

69
#if defined(PR_USE_DEVEL)
70
static void oom_printf(const char *fmt, ...) {
×
71
  char buf[PR_TUNABLE_BUFFER_SIZE];
72
  va_list msg;
73

74
  memset(buf, '\0', sizeof(buf));
×
75

76
  va_start(msg, fmt);
×
77
  pr_vsnprintf(buf, sizeof(buf), fmt, msg);
×
78
  va_end(msg);
×
79

80
  buf[sizeof(buf)-1] = '\0';
×
81
  fprintf(stderr, "%s\n", buf);
×
82
}
×
83
#endif /* PR_USE_DEVEL */
84

85
/* Lowest level memory allocation functions
86
 */
87

88
static void null_alloc(void) {
×
89
  pr_log_pri(PR_LOG_ALERT, "Out of memory!");
×
90
#if defined(PR_USE_DEVEL)
91
  if (debug_flags & PR_POOL_DEBUG_FL_OOM_DUMP_POOLS) {
×
92
    pr_pool_debug_memory(oom_printf);
93
  }
94
#endif /* PR_USE_DEVEL */
95

96
  exit(1);
×
97
}
98

99
static void *smalloc(size_t size) {
11,934✔
100
  void *res;
101

102
  if (size == 0) {
11,934✔
103
    /* Avoid zero-length malloc(); on non-POSIX systems, the behavior is
104
     * not dependable.  And on POSIX systems, malloc(3) might still return
105
     * a "unique pointer" for a zero-length allocation (or NULL).
106
     *
107
     * Either way, a zero-length allocation request here means that someone
108
     * is doing something they should not be doing.
109
     */
110
    null_alloc();
×
111
  }
112

113
  res = malloc(size);
11,934✔
114
  if (res == NULL) {
11,934✔
115
    null_alloc();
×
116
  }
117

118
  return res;
11,934✔
119
}
120

121
/* Grab a completely new block from the system pool.  Relies on malloc()
122
 * to return truly aligned memory.
123
 */
124
static union block_hdr *malloc_block(size_t size) {
125
  union block_hdr *blok =
11,934✔
126
    (union block_hdr *) smalloc(size + sizeof(union block_hdr));
11,934✔
127

128
  blok->h.next = NULL;
11,934✔
129
  blok->h.first_avail = (char *) (blok + 1);
11,934✔
130
  blok->h.endp = size + (char *) blok->h.first_avail;
11,934✔
131

132
  return blok;
133
}
134

135
static void chk_on_blk_list(union block_hdr *blok, union block_hdr *free_blk,
17,691✔
136
    const char *pool_tag) {
137

138
#if defined(PR_USE_DEVEL)
139
  /* Debug code */
140

141
  while (free_blk) {
132,374✔
142
    if (free_blk != blok) {
96,992✔
143
      free_blk = free_blk->h.next;
96,992✔
144
      continue;
96,992✔
145
    }
146

147
    pr_log_pri(PR_LOG_WARNING, "fatal: DEBUG: Attempt to free already free "
×
148
     "block in pool '%s'", pool_tag ? pool_tag : "<unnamed>");
149
    exit(1);
×
150
  }
151
#endif /* PR_USE_DEVEL */
152
}
17,691✔
153

154
/* Free a chain of blocks -- _must_ call with alarms blocked. */
155

156
static void free_blocks(union block_hdr *blok, const char *pool_tag) {
28,636✔
157
  /* Puts new blocks at head of block list, point next pointer of
158
   * last block in chain to free blocks we already had.
159
   */
160

161
  union block_hdr *old_free_list = block_freelist;
28,636✔
162

163
  if (blok == NULL) {
28,636✔
164
    /* Don't free an empty pool. */
165
    return;
166
  }
167

168
  block_freelist = blok;
16,546✔
169

170
  /* Adjust first_avail pointers */
171

172
  while (blok->h.next) {
34,237✔
173
    chk_on_blk_list(blok, old_free_list, pool_tag);
1,145✔
174
    blok->h.first_avail = (char *) (blok + 1);
1,145✔
175
    blok = blok->h.next;
1,145✔
176
  }
177

178
  chk_on_blk_list(blok, old_free_list, pool_tag);
16,546✔
179
  blok->h.first_avail = (char *) (blok + 1);
16,546✔
180
  blok->h.next = old_free_list;
16,546✔
181
}
182

183
/* Get a new block, from the free list if possible, otherwise malloc a new
184
 * one.  minsz is the requested size of the block to be allocated.
185
 * If exact is TRUE, then minsz is the exact size of the allocated block;
186
 * otherwise, the allocated size will be rounded up from minsz to the nearest
187
 * multiple of BLOCK_MINFREE.
188
 *
189
 * Important: BLOCK ALARMS BEFORE CALLING
190
 */
191

192
static union block_hdr *new_block(size_t minsz, int exact) {
18,461✔
193
  union block_hdr **lastptr = &block_freelist;
18,461✔
194
  union block_hdr *blok = block_freelist;
18,461✔
195

196
  if (exact == FALSE) {
18,461✔
197
    if (minsz == 0) {
17,519✔
198
      minsz = 1;
13,892✔
199
    }
200

201
    minsz = 1 + ((minsz - 1) / BLOCK_MINFREE);
17,519✔
202
    minsz *= BLOCK_MINFREE;
17,519✔
203
  }
204

205
  /* Check if we have anything of the requested size on our free list first...
206
   */
207
  while (blok) {
18,634✔
208
    size_t availsz;
209

210
    availsz = ((char *) blok->h.endp - (char *) blok->h.first_avail);
6,700✔
211
    if (minsz <= availsz) {
6,700✔
212
      *lastptr = blok->h.next;
6,527✔
213
      blok->h.next = NULL;
6,527✔
214

215
      stat_freehit++;
6,527✔
216
      return blok;
6,527✔
217
    }
218

219
    lastptr = &blok->h.next;
173✔
220
    blok = blok->h.next;
173✔
221
  }
222

223
  /* Nope...damn.  Have to malloc() a new one. */
224
  stat_malloc++;
11,934✔
225
  return malloc_block(minsz);
11,934✔
226
}
227

228
struct cleanup;
229

230
static void run_cleanups(struct cleanup *c);
231

232
/* Pool internal and management */
233

234
struct pool_rec {
235
  union block_hdr *first;
236
  union block_hdr *last;
237
  struct cleanup *cleanups;
238
  struct pool_rec *sub_pools;
239
  struct pool_rec *sub_next;
240
  struct pool_rec *sub_prev;
241
  struct pool_rec *parent;
242
  char *free_first_avail;
243
  const char *tag;
244
};
245

246
pool *permanent_pool = NULL;
247
pool *global_config_pool = NULL;
248

249
/* Each pool structure is allocated in the start of it's own first block,
250
 * so there is a need to know how many bytes that is (once properly
251
 * aligned).
252
 */
253

254
#define POOL_HDR_CLICKS (1 + ((sizeof(struct pool_rec) - 1) / CLICK_SZ))
255
#define POOL_HDR_BYTES (POOL_HDR_CLICKS * CLICK_SZ)
256

257
static unsigned long blocks_in_block_list(union block_hdr *blok) {
258
  unsigned long count = 0;
259

260
  while (blok) {
171✔
261
    count++;
99✔
262
    blok = blok->h.next;
99✔
263
  }
264

265
  return count;
266
}
267

268
static unsigned long bytes_in_block_list(union block_hdr *blok) {
269
  unsigned long size = 0;
63✔
270

271
  while (blok) {
171✔
272
    size += ((char *) blok->h.endp - (char *) (blok + 1));
99✔
273
    blok = blok->h.next;
99✔
274
  }
275

276
  return size;
277
}
278

279
static unsigned int subpools_in_pool(pool *p) {
139✔
280
  unsigned int count = 0;
139✔
281
  pool *iter;
282

283
  if (p->sub_pools == NULL) {
139✔
284
    return 0;
285
  }
286

287
  for (iter = p->sub_pools; iter; iter = iter->sub_next) {
76✔
288
    /* Count one for the current subpool (iter). */
289
    count += (subpools_in_pool(iter) + 1);
76✔
290
  }
291

292
  return count;
293
}
294

295
/* Visit all pools, starting with the top-level permanent pool, walking the
296
 * hierarchy.
297
 */
298
static unsigned long visit_pools(pool *p, unsigned long level,
46✔
299
    void (*visit)(const pr_pool_info_t *, void *), void *user_data) {
300
  unsigned long total_bytes = 0;
46✔
301

302
  if (p == NULL) {
46✔
303
    return 0;
304
  }
305

306
  for (; p; p = p->sub_next) {
63✔
307
    unsigned long byte_count = 0, block_count = 0;
63✔
308
    unsigned int subpool_count = 0;
63✔
309
    pr_pool_info_t pinfo;
310

311
    byte_count = bytes_in_block_list(p->first);
63✔
312
    block_count = blocks_in_block_list(p->first);
63✔
313
    subpool_count = subpools_in_pool(p);
63✔
314

315
    total_bytes += byte_count;
63✔
316

317
    memset(&pinfo, 0, sizeof(pinfo));
63✔
318
    pinfo.have_pool_info = TRUE;
63✔
319
    pinfo.tag = p->tag;
63✔
320
    pinfo.ptr = p;
63✔
321
    pinfo.byte_count = byte_count;
63✔
322
    pinfo.block_count = block_count;
63✔
323
    pinfo.subpool_count = subpool_count;
63✔
324
    pinfo.level = level;
63✔
325

326
    visit(&pinfo, user_data);
63✔
327

328
    /* Recurse */
329
    if (p->sub_pools) {
63✔
330
      total_bytes += visit_pools(p->sub_pools, level + 1, visit, user_data);
35✔
331
    }
332
  }
333

334
  return total_bytes;
335
}
336

337
static void pool_printf(const char *fmt, ...) {
64✔
338
  char buf[PR_TUNABLE_BUFFER_SIZE];
339
  va_list msg;
340

341
  memset(buf, '\0', sizeof(buf));
64✔
342

343
  va_start(msg, fmt);
64✔
344
  pr_vsnprintf(buf, sizeof(buf), fmt, msg);
64✔
345
  va_end(msg);
64✔
346

347
  buf[sizeof(buf)-1] = '\0';
64✔
348
  pr_trace_msg(trace_channel, 5, "%s", buf);
64✔
349
}
64✔
350

351
static void pool_visitf(const pr_pool_info_t *pinfo, void *user_data) {
46✔
352
  void (*debugf)(const char *, ...) = user_data;
46✔
353

354
  if (pinfo->have_pool_info) {
46✔
355

356
    /* The emitted message is:
357
     *
358
     *  <pool-tag> [pool-ptr] (n B, m L, r P)
359
     *
360
     * where n is the number of bytes (B), m is the number of allocated blocks
361
     * in the pool list (L), and r is the number of sub-pools (P).
362
     */
363

364
    if (pinfo->level == 0) {
34✔
365
      debugf("%s [%p] (%lu B, %lu L, %u P)",
12✔
366
        pinfo->tag ? pinfo->tag : "<unnamed>", pinfo->ptr,
6✔
367
        pinfo->byte_count, pinfo->block_count, pinfo->subpool_count);
368

369
    } else {
370
      char indent_text[80] = "";
28✔
371

372
      if (pinfo->level > 1) {
28✔
373
        memset(indent_text, ' ', sizeof(indent_text)-1);
13✔
374

375
        if ((pinfo->level - 1) * 3 >= sizeof(indent_text)) {
13✔
376
          indent_text[sizeof(indent_text)-1] = 0;
377

378
        } else {
379
          indent_text[(pinfo->level - 1) * 3] = '\0';
13✔
380
        }
381
      }
382

383
      debugf("%s + %s [%p] (%lu B, %lu L, %u P)", indent_text,
56✔
384
        pinfo->tag ? pinfo->tag : "<unnamed>", pinfo->ptr,
28✔
385
        pinfo->byte_count, pinfo->block_count, pinfo->subpool_count);
386
    }
387
  }
388

389
  if (pinfo->have_freelist_info) {
46✔
390
    debugf("Free block list: %lu bytes", pinfo->freelist_byte_count);
6✔
391
  }
392

393
  if (pinfo->have_total_info) {
46✔
394
    debugf("Total %lu bytes allocated", pinfo->total_byte_count);
6✔
395
    debugf("%lu blocks allocated", pinfo->total_blocks_allocated);
6✔
396
    debugf("%lu blocks reused", pinfo->total_blocks_reused);
6✔
397
  }
398
}
46✔
399

400
void pr_pool_debug_memory(void (*debugf)(const char *, ...)) {
6✔
401
  if (debugf == NULL) {
6✔
402
    debugf = pool_printf;
6✔
403
  }
404

405
  debugf("Memory pool allocation:");
6✔
406
  pr_pool_debug_memory2(pool_visitf, debugf);
6✔
407
}
6✔
408

409
void pr_pool_debug_memory2(void (*visit)(const pr_pool_info_t *, void *),
12✔
410
    void *user_data) {
411
  unsigned long freelist_byte_count = 0, freelist_block_count = 0,
12✔
412
    total_byte_count = 0;
12✔
413
  pr_pool_info_t pinfo;
414

415
  if (visit == NULL) {
12✔
416
    return;
1✔
417
  }
418

419
  /* Per pool */
420
  total_byte_count = visit_pools(permanent_pool, 0, visit, user_data);
11✔
421

422
  /* Free list */
423
  if (block_freelist) {
11✔
424
    freelist_byte_count = bytes_in_block_list(block_freelist);
425
    freelist_block_count = blocks_in_block_list(block_freelist);
426
  }
427

428
  memset(&pinfo, 0, sizeof(pinfo));
11✔
429
  pinfo.have_freelist_info = TRUE;
11✔
430
  pinfo.freelist_byte_count = freelist_byte_count;
11✔
431
  pinfo.freelist_block_count = freelist_block_count;
11✔
432

433
  visit(&pinfo, user_data);
11✔
434

435
  /* Totals */
436
  memset(&pinfo, 0, sizeof(pinfo));
11✔
437
  pinfo.have_total_info = TRUE;
11✔
438
  pinfo.total_byte_count = total_byte_count;
11✔
439
  pinfo.total_blocks_allocated = stat_malloc;
11✔
440
  pinfo.total_blocks_reused = stat_freehit;
11✔
441

442
  visit(&pinfo, user_data);
11✔
443
}
444

445
int pr_pool_debug_set_flags(int flags) {
2✔
446
  if (flags < 0) {
2✔
447
    errno = EINVAL;
1✔
448
    return -1;
1✔
449
  }
450

451
  debug_flags = flags;
1✔
452
  return 0;
1✔
453
}
454

455
void pr_pool_tag(pool *p, const char *tag) {
13,801✔
456
  if (p == NULL ||
27,618✔
457
      tag == NULL) {
13,801✔
458
    return;
459
  }
460

461
  p->tag = tag;
13,815✔
462
}
463

464
const char *pr_pool_get_tag(pool *p) {
3✔
465
  if (p == NULL) {
3✔
466
    errno = EINVAL;
1✔
467
    return NULL;
1✔
468
  }
469

470
  return p->tag;
2✔
471
}
472

473
/* Release the entire free block list */
474
static void pool_release_free_block_list(void) {
16✔
475
  union block_hdr *blok = NULL, *next = NULL;
16✔
476

477
  pr_alarms_block();
16✔
478

479
  for (blok = block_freelist; blok; blok = next) {
166✔
480
    next = blok->h.next;
150✔
481
    free(blok);
150✔
482
  }
483
  block_freelist = NULL;
16✔
484

485
  pr_alarms_unblock();
16✔
486
}
16✔
487

488
struct pool_rec *make_sub_pool(struct pool_rec *p) {
13,892✔
489
  union block_hdr *blok;
490
  pool *new_pool;
491

492
  pr_alarms_block();
13,892✔
493

494
  blok = new_block(0, FALSE);
13,892✔
495

496
  new_pool = (pool *) blok->h.first_avail;
13,892✔
497
  blok->h.first_avail = POOL_HDR_BYTES + (char *) blok->h.first_avail;
13,892✔
498

499
  memset(new_pool, 0, sizeof(struct pool_rec));
13,892✔
500
  new_pool->free_first_avail = blok->h.first_avail;
13,892✔
501
  new_pool->first = new_pool->last = blok;
13,892✔
502

503
  if (p != NULL) {
13,892✔
504
    new_pool->parent = p;
12,860✔
505
    new_pool->sub_next = p->sub_pools;
12,860✔
506

507
    if (new_pool->sub_next != NULL) {
12,860✔
508
      new_pool->sub_next->sub_prev = new_pool;
9,597✔
509
    }
510

511
    p->sub_pools = new_pool;
12,860✔
512
  }
513

514
  pr_alarms_unblock();
13,892✔
515

516
  return new_pool;
13,892✔
517
}
518

519
struct pool_rec *pr_pool_create_sz(struct pool_rec *p, size_t sz) {
940✔
520
  union block_hdr *blok;
521
  pool *new_pool;
522

523
  pr_alarms_block();
940✔
524

525
  blok = new_block(sz + POOL_HDR_BYTES, TRUE);
940✔
526

527
  new_pool = (pool *) blok->h.first_avail;
940✔
528
  blok->h.first_avail = POOL_HDR_BYTES + (char *) blok->h.first_avail;
940✔
529

530
  memset(new_pool, 0, sizeof(struct pool_rec));
940✔
531
  new_pool->free_first_avail = blok->h.first_avail;
940✔
532
  new_pool->first = new_pool->last = blok;
940✔
533

534
  if (p != NULL) {
940✔
535
    new_pool->parent = p;
934✔
536
    new_pool->sub_next = p->sub_pools;
934✔
537

538
    if (new_pool->sub_next != NULL) {
934✔
539
      new_pool->sub_next->sub_prev = new_pool;
64✔
540
    }
541

542
    p->sub_pools = new_pool;
934✔
543
  }
544

545
  pr_alarms_unblock();
940✔
546

547
  return new_pool;
940✔
548
}
549

550
/* Initialize the pool system by creating the base permanent_pool. */
551

552
void init_pools(void) {
16✔
553
  if (permanent_pool == NULL) {
16✔
554
    permanent_pool = make_sub_pool(NULL);
16✔
555
  }
556

557
  pr_pool_tag(permanent_pool, "permanent_pool");
32✔
558
}
16✔
559

560
void free_pools(void) {
16✔
561
  destroy_pool(permanent_pool);
16✔
562
  permanent_pool = NULL;
16✔
563
  session.pool = NULL;
16✔
564
  session.notes = NULL;
16✔
565
  pool_release_free_block_list();
16✔
566
}
16✔
567

568
static void clear_pool(struct pool_rec *p) {
14,318✔
569

570
  /* Sanity check. */
571
  if (p == NULL) {
14,318✔
572
    return;
573
  }
574

575
  pr_alarms_block();
14,318✔
576

577
  /* Run through any cleanups. */
578
  run_cleanups(p->cleanups);
28,636✔
579
  p->cleanups = NULL;
14,318✔
580

581
  /* Destroy subpools. */
582
  while (p->sub_pools != NULL) {
33,579✔
583
    destroy_pool(p->sub_pools);
4,943✔
584
  }
585

586
  p->sub_pools = NULL;
14,318✔
587

588
  free_blocks(p->first->h.next, p->tag);
14,318✔
589
  p->first->h.next = NULL;
14,318✔
590

591
  p->last = p->first;
14,318✔
592
  p->first->h.first_avail = p->free_first_avail;
14,318✔
593

594
  p->tag = NULL;
14,318✔
595
  pr_alarms_unblock();
14,318✔
596
}
597

598
void destroy_pool(pool *p) {
14,337✔
599
  if (p == NULL) {
14,337✔
600
    return;
601
  }
602

603
  pr_alarms_block();
14,318✔
604

605
  if (p->parent != NULL) {
14,318✔
606
    if (p->parent->sub_pools == p) {
13,393✔
607
      p->parent->sub_pools = p->sub_next;
12,464✔
608
    }
609

610
    if (p->sub_prev != NULL) {
13,393✔
611
      p->sub_prev->sub_next = p->sub_next;
929✔
612
    }
613

614
    if (p->sub_next != NULL) {
13,393✔
615
      p->sub_next->sub_prev = p->sub_prev;
9,105✔
616
    }
617
  }
618

619
  clear_pool(p);
14,318✔
620
  free_blocks(p->first, p->tag);
14,318✔
621

622
  pr_alarms_unblock();
14,318✔
623

624
#if defined(PR_DEVEL_NO_POOL_FREELIST)
625
  /* If configured explicitly to do so, call free(3) on the freelist after
626
   * a pool is destroyed.  This can be useful for tracking down use-after-free
627
   * and other memory issues using libraries such as dmalloc.
628
   */
629
  pool_release_free_block_list();
630
#endif /* PR_DEVEL_NO_POOL_FREELIST */
631
}
632

633
/* Allocation interface...
634
 */
635

636
static void *alloc_pool(struct pool_rec *p, size_t reqsz, int exact) {
38,608✔
637
  /* Round up requested size to an even number of aligned units */
638
  size_t nclicks = 1 + ((reqsz - 1) / CLICK_SZ);
38,608✔
639
  size_t sz = nclicks * CLICK_SZ;
38,608✔
640
  union block_hdr *blok;
641
  char *first_avail, *new_first_avail;
642

643
  if (p == NULL) {
38,608✔
644
    errno = EINVAL;
1✔
645
    return NULL;
1✔
646
  }
647

648
  /* For performance, see if space is available in the most recently
649
   * allocated block.
650
   */
651

652
  blok = p->last;
38,607✔
653
  if (blok == NULL) {
38,607✔
654
    errno = EINVAL;
×
655
    return NULL;
×
656
  }
657

658
  first_avail = blok->h.first_avail;
38,607✔
659

660
  if (reqsz == 0) {
38,607✔
661
    /* Don't try to allocate memory of zero length.
662
     *
663
     * This should NOT happen normally; if it does, by returning NULL we
664
     * almost guarantee a null pointer dereference.
665
     */
666
    errno = EINVAL;
2✔
667
    return NULL;
2✔
668
  }
669

670
  new_first_avail = first_avail + sz;
38,605✔
671

672
  if (new_first_avail <= (char *) blok->h.endp) {
38,605✔
673
    blok->h.first_avail = new_first_avail;
34,976✔
674
    return (void *) first_avail;
34,976✔
675
  }
676

677
  /* Need a new one that's big enough */
678
  pr_alarms_block();
3,629✔
679

680
  blok = new_block(sz, exact);
3,629✔
681
  p->last->h.next = blok;
3,629✔
682
  p->last = blok;
3,629✔
683

684
  first_avail = blok->h.first_avail;
3,629✔
685
  blok->h.first_avail = sz + (char *) blok->h.first_avail;
3,629✔
686

687
  pr_alarms_unblock();
3,629✔
688
  return (void *) first_avail;
3,629✔
689
}
690

691
void *palloc(struct pool_rec *p, size_t sz) {
7,946✔
692
  return alloc_pool(p, sz, FALSE);
38,602✔
693
}
694

695
void *pallocsz(struct pool_rec *p, size_t sz) {
4✔
696
  return alloc_pool(p, sz, TRUE);
6✔
697
}
698

699
void *pcalloc(struct pool_rec *p, size_t sz) {
30,263✔
700
  void *res;
701

702
  if (p == NULL ||
60,526✔
703
      sz == 0) {
30,263✔
704
    errno = EINVAL;
3✔
705
    return NULL;
3✔
706
  }
707

708
  res = palloc(p, sz);
30,260✔
709
  memset(res, '\0', sz);
30,260✔
710

711
  return res;
30,260✔
712
}
713

714
void *pcallocsz(struct pool_rec *p, size_t sz) {
4✔
715
  void *res;
716

717
  if (p == NULL ||
8✔
718
      sz == 0) {
4✔
719
    errno = EINVAL;
2✔
720
    return NULL;
2✔
721
  }
722

723
  res = pallocsz(p, sz);
2✔
724
  memset(res, '\0', sz);
2✔
725

726
  return res;
2✔
727
}
728

729
/* Array functions */
730

731
array_header *make_array(pool *p, unsigned int nelts, size_t elt_size) {
397✔
732
  array_header *res;
733

734
  if (p == NULL ||
794✔
735
      elt_size == 0) {
397✔
736
    errno = EINVAL;
3✔
737
    return NULL;
3✔
738
  }
739

740
  res = palloc(p, sizeof(array_header));
394✔
741

742
  if (nelts < 1) {
394✔
743
    nelts = 1;
121✔
744
  }
745

746
  res->elts = pcalloc(p, nelts * elt_size);
394✔
747
  res->pool = p;
394✔
748
  res->elt_size = elt_size;
394✔
749
  res->nelts = 0;
394✔
750
  res->nalloc = nelts;
394✔
751

752
  return res;
394✔
753
}
754

755
void clear_array(array_header *arr) {
5✔
756
  if (arr == NULL) {
5✔
757
    return;
758
  }
759

760
  arr->elts = pcalloc(arr->pool, arr->nalloc * arr->elt_size);
4✔
761
  arr->nelts = 0;
4✔
762
}
763

764
void *push_array(array_header *arr) {
695✔
765
  if (arr == NULL) {
695✔
766
    errno = EINVAL;
1✔
767
    return NULL;
1✔
768
  }
769

770
  if (arr->nelts == arr->nalloc) {
694✔
771
    char *new_data = pcalloc(arr->pool, arr->nalloc * arr->elt_size * 2);
207✔
772

773
    memcpy(new_data, arr->elts, arr->nalloc * arr->elt_size);
414✔
774
    arr->elts = new_data;
207✔
775
    arr->nalloc *= 2;
207✔
776
  }
777

778
  ++arr->nelts;
694✔
779
  return ((char *) arr->elts) + (arr->elt_size * (arr->nelts - 1));
694✔
780
}
781

782
int array_cat2(array_header *dst, const array_header *src) {
16✔
783
  size_t elt_size;
784

785
  if (dst == NULL ||
32✔
786
      src == NULL) {
16✔
787
    errno = EINVAL;
6✔
788
    return -1;
6✔
789
  }
790

791
  elt_size = dst->elt_size;
10✔
792

793
  if (dst->nelts + src->nelts > dst->nalloc) {
10✔
794
    size_t new_size;
795
    char *new_data;
796

797
    new_size = dst->nalloc * 2;
6✔
798
    if (new_size == 0) {
6✔
799
      ++new_size;
×
800
    }
801

802
    while ((dst->nelts + src->nelts) > new_size) {
14✔
803
      new_size *= 2;
8✔
804
    }
805

806
    new_data = pcalloc(dst->pool, elt_size * new_size);
6✔
807
    memcpy(new_data, dst->elts, dst->nalloc * elt_size);
12✔
808

809
    dst->elts = new_data;
6✔
810
    dst->nalloc = new_size;
6✔
811
  }
812

813
  memcpy(((char *) dst->elts) + (dst->nelts * elt_size), (char *) src->elts,
20✔
814
         elt_size * src->nelts);
10✔
815
  dst->nelts += src->nelts;
10✔
816

817
  return 0;
10✔
818
}
819

820
void array_cat(array_header *dst, const array_header *src) {
9✔
821
  (void) array_cat2(dst, src);
10✔
822
}
9✔
823

824
array_header *copy_array(pool *p, const array_header *arr) {
8✔
825
  array_header *res;
826

827
  if (p == NULL ||
16✔
828
      arr == NULL) {
8✔
829
    errno = EINVAL;
4✔
830
    return NULL;
4✔
831
  }
832

833
  res = make_array(p, arr->nalloc, arr->elt_size);
4✔
834

835
  if (arr->nelts > 0) {
4✔
836
    memcpy(res->elts, arr->elts, arr->elt_size * arr->nelts);
4✔
837
  }
838

839
  res->nelts = arr->nelts;
4✔
840
  return res;
4✔
841
}
842

843
/* copy an array that is assumed to consist solely of strings */
844
array_header *copy_array_str(pool *p, const array_header *arr) {
4✔
845
  register unsigned int i;
846
  array_header *res;
847

848
  if (p == NULL ||
8✔
849
      arr == NULL) {
4✔
850
    errno = EINVAL;
3✔
851
    return NULL;
3✔
852
  }
853

854
  res = copy_array(p, arr);
1✔
855

856
  for (i = 0; i < arr->nelts; i++) {
3✔
857
    ((char **) res->elts)[i] = pstrdup(p, ((char **) res->elts)[i]);
2✔
858
  }
859

860
  return res;
861
}
862

863
array_header *copy_array_hdr(pool *p, const array_header *arr) {
5✔
864
  array_header *res;
865

866
  if (p == NULL ||
10✔
867
      arr == NULL) {
5✔
868
    errno = EINVAL;
3✔
869
    return NULL;
3✔
870
  }
871

872
  res = palloc(p, sizeof(array_header));
2✔
873

874
  res->elts = arr->elts;
2✔
875
  res->pool = p;
2✔
876
  res->elt_size = arr->elt_size;
2✔
877
  res->nelts = arr->nelts;
2✔
878
  res->nalloc = arr->nelts;                /* Force overflow on push */
2✔
879

880
  return res;
2✔
881
}
882

883
array_header *append_arrays(pool *p, const array_header *first,
8✔
884
    const array_header *second) {
885
  array_header *res;
886

887
  if (p == NULL ||
16✔
888
      first == NULL ||
10✔
889
      second == NULL) {
890
    errno = EINVAL;
7✔
891
    return NULL;
7✔
892
  }
893

894
  res = copy_array_hdr(p, first);
1✔
895

896
  array_cat(res, second);
1✔
897
  return res;
1✔
898
}
899

900
/* Generic cleanups */
901

902
typedef struct cleanup {
903
  void *user_data;
904
  void (*cleanup_cb)(void *);
905
  struct cleanup *next;
906

907
} cleanup_t;
908

909
void register_cleanup2(pool *p, void *user_data, void (*cleanup_cb)(void*)) {
1,734✔
910
  cleanup_t *c;
911

912
  if (p == NULL) {
1,739✔
913
    return;
914
  }
915

916
  c = pcalloc(p, sizeof(cleanup_t));
1,693✔
917
  c->user_data = user_data;
1,693✔
918
  c->cleanup_cb = cleanup_cb;
1,693✔
919

920
  /* Add this cleanup to the given pool's list of cleanups. */
921
  c->next = p->cleanups;
1,693✔
922
  p->cleanups = c;
1,693✔
923
}
924

925
void register_cleanup(pool *p, void *user_data, void (*plain_cleanup_cb)(void*),
5✔
926
    void (*child_cleanup_cb)(void *)) {
927
  (void) child_cleanup_cb;
928
  register_cleanup2(p, user_data, plain_cleanup_cb);
5✔
929
}
5✔
930

931
void unregister_cleanup(pool *p, void *user_data, void (*cleanup_cb)(void *)) {
3✔
932
  cleanup_t *c, **lastp;
933

934
  if (p == NULL) {
3✔
935
    return;
936
  }
937

938
  c = p->cleanups;
2✔
939
  lastp = &p->cleanups;
2✔
940

941
  while (c != NULL) {
4✔
942
    if (c->user_data == user_data &&
4✔
943
        (c->cleanup_cb == cleanup_cb || cleanup_cb == NULL)) {
3✔
944

945
      /* Remove the given cleanup by pointing the previous next pointer to
946
       * the matching cleanup's next pointer.
947
       */
948
      *lastp = c->next;
2✔
949
      break;
2✔
950
    }
951

952
    lastp = &c->next;
×
953
    c = c->next;
×
954
  }
955
}
956

957
static void run_cleanups(cleanup_t *c) {
958
  while (c != NULL) {
15,922✔
959
    if (c->cleanup_cb) {
1,604✔
960
      (*c->cleanup_cb)(c->user_data);
1,602✔
961
    }
962

963
    c = c->next;
1,604✔
964
  }
965
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc