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

proftpd / proftpd / 14562012250

20 Apr 2025 06:03PM UTC coverage: 92.667% (-0.4%) from 93.03%
14562012250

push

github

Castaglia
Fix comment typo/thinko.

47172 of 50905 relevant lines covered (92.67%)

219.52 hits per line

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

85.69
/src/table.c
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 2004-2024 The ProFTPD Project team
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18
 *
19
 * As a special exemption, The ProFTPD Project team and other respective
20
 * copyright holders give permission to link this program with OpenSSL, and
21
 * distribute the resulting executable, without including the source code for
22
 * OpenSSL in the source distribution.
23
 */
24

25
/* Table API implementation */
26

27
#include "conf.h"
28

29
#ifdef PR_USE_OPENSSL
30
#include <openssl/rand.h>
31
#endif /* PR_USE_OPENSSL */
32

33
#define PR_TABLE_DEFAULT_NCHAINS        256
34
#define PR_TABLE_DEFAULT_MAX_ENTS        8192
35
#define PR_TABLE_ENT_POOL_SIZE                64
36

37
struct table_rec {
38
  pool *pool;
39
  unsigned long flags;
40

41
  /* These bytes are randomly generated at table creation time, and
42
   * are used to seed the hashing function, so as to defend/migitate
43
   * against attempts to feed carefully crafted keys which force the
44
   * table into its worst-case performance scenario.
45
   *
46
   * For more information on attacks of this nature, see:
47
   *
48
   *   http://www.cs.rice.edu/~scrosby/hash/CrosbyWallach_UsenixSec2003/
49
   */
50
  unsigned int seed;
51

52
  /* Maximum number of entries that can be stored in this table.  The
53
   * default maximum (PR_TABLE_DEFAULT_MAX_ENTS) is set fairly high.
54
   * This limit is present in order to defend/mitigate against certain abuse
55
   * scenarios.
56
   *
57
   * XXX Note that an additional protective measure can/might be placed on
58
   * the maximum length of a given chain, to detect other types of attacks
59
   * that force the table into the worse-case performance scenario (i.e.
60
   * linear scanning of a long chain).  If such is added, then a Table API
61
   * function should be added for returning the length of the longest chain
62
   * in the table.  Such a function could be used by modules to determine
63
   * if their tables are being abused (or in need of readjustment).
64
   */
65
  unsigned int nmaxents;
66

67
  pr_table_entry_t **chains;
68
  unsigned int nchains;
69
  unsigned int nents;
70

71
  /* List of free structures. */
72
  pr_table_entry_t *free_ents;
73
  pr_table_key_t *free_keys;
74

75
  /* For iterating over all the keys in the entire table. */
76
  pr_table_entry_t *tab_iter_ent;
77

78
  /* For iterating through all of the possible multiple values for a single
79
   * key.  Only used if the PR_TABLE_FL_MULTI_VALUE flag is set.
80
   */
81
  pr_table_entry_t *val_iter_ent;
82

83
  /* Cache of last looked-up entry.  Usage of this field can be enabled
84
   * by using the PR_TABLE_FL_USE_CACHE flag.
85
   */
86
  pr_table_entry_t *cache_ent;
87

88
  /* Table callbacks. */
89
  int (*keycmp)(const void *, size_t, const void *, size_t);
90
  unsigned int (*keyhash)(const void *, size_t);
91
  void (*entinsert)(pr_table_entry_t **, pr_table_entry_t *);
92
  void (*entremove)(pr_table_entry_t **, pr_table_entry_t *);
93
};
94

95
static int handling_signal = FALSE;
96

97
static const char *trace_channel = "table";
98

99
/* Default table callbacks
100
 */
101

102
static int key_cmp(const void *key1, size_t keysz1, const void *key2,
8,153✔
103
    size_t keysz2) {
104
  const char *k1, *k2;
105

106
  if (keysz1 != keysz2) {
8,153✔
107
    return keysz1 < keysz2 ? -1 : 1;
×
108
  }
109

110
  k1 = key1;
8,153✔
111
  k2 = key2;
8,153✔
112

113
  if (keysz1 >= 1) {
8,153✔
114
    /* Basic check of the first character in each key, trying to reduce
115
     * the chances of calling strncmp(3) if not needed.
116
     */
117

118
    if (k1[0] != k2[0]) {
8,153✔
119
      return k1[0] < k2[0] ? -1 : 1;
×
120
    }
121

122
    /* Special case (unlikely, but possible). */
123
    if (keysz1 == 1) {
8,153✔
124
      return 0;
125
    }
126
  }
127

128
  return strncmp((const char *) key1, (const char *) key2, keysz1);
8,152✔
129
}
130

131
/* Use Perl's hashing algorithm by default.
132
 *
133
 * Here's a good article about this hashing algorithm, and about hashing
134
 * functions in general:
135
 *
136
 *  http://www.perl.com/pub/2002/10/01/hashes.html
137
 */
138
static unsigned int key_hash(const void *key, size_t keysz) {
12,418✔
139
  unsigned int i = 0;
12,418✔
140
  size_t sz = !keysz ? strlen((const char *) key) : keysz;
12,418✔
141

142
  while (sz--) {
93,143✔
143
    const char *k = key;
80,725✔
144
    unsigned int c;
145

146
    c = k[sz];
80,725✔
147

148
    if (!handling_signal) {
80,725✔
149
      /* Always handle signals in potentially long-running while loops. */
150
      pr_signals_handle();
80,725✔
151
    }
152

153
    i = (i * 33) + c;
80,725✔
154
  }
155

156
  return i;
12,418✔
157
}
158

159
/* Default insertion is simply to add the given entry to the end of the
160
 * chain.
161
 */
162
static void entry_insert(pr_table_entry_t **h, pr_table_entry_t *e) {
116✔
163
  pr_table_entry_t *ei;
164

165
  if (*h == NULL) {
116✔
166
    return;
167
  }
168

169
  for (ei = *h; ei != NULL && ei->next; ei = ei->next) {
2,978✔
170
  }
171

172
  /* Now, ei points to the last entry in the chain. */
173
  ei->next = e;
116✔
174
  e->prev = ei;
116✔
175
}
176

177
/* Default removal is simply to remove the entry from the chain. */
178
static void entry_remove(pr_table_entry_t **h, pr_table_entry_t *e) {
85✔
179

180
  if (e->next) {
85✔
181
    e->next->prev = e->prev;
1✔
182
  }
183

184
  if (e->prev) {
85✔
185
    e->prev->next = e->next;
×
186

187
  } else {
188
    /* This entry is the head. */
189
    *h = e->next;
85✔
190
  }
191

192
  e->prev = e->next = NULL;
85✔
193
}
85✔
194

195
/* Table key management
196
 */
197

198
static pr_table_key_t *tab_key_alloc(pr_table_t *tab) {
199
  pr_table_key_t *k;
200

201
  /* Try to find a free key on the free list first... */
202
  if (tab->free_keys) {
1,992✔
203
    k = tab->free_keys;
38✔
204
    tab->free_keys = k->next;
38✔
205
    k->next = NULL;
38✔
206

207
    return k;
208
  }
209

210
  /* ...otherwise, allocate a new key. */
211
  k = pcalloc(tab->pool, sizeof(pr_table_key_t));
1,954✔
212

213
  return k;
214
}
215

216
static void tab_key_free(pr_table_t *tab, pr_table_key_t *k) {
85✔
217
  /* Clear everything from the given key. */
218
  memset(k, 0, sizeof(pr_table_key_t));
85✔
219

220
  /* Add this key to the table's free list. */
221
  if (tab->free_keys) {
85✔
222
    pr_table_key_t *i = tab->free_keys;
223

224
    /* Scan to the end of the list. */
225
    while (i->next != NULL) {
7✔
226
      if (!handling_signal) {
×
227
        pr_signals_handle();
×
228
      }
229

230
      i = i->next;
×
231
    }
232

233
    i->next = k;
7✔
234

235
  } else {
236
    tab->free_keys = k;
78✔
237
  }
238
}
85✔
239

240
/* Table entry management
241
 */
242

243
static pr_table_entry_t *tab_entry_alloc(pr_table_t *tab) {
244
  pr_table_entry_t *e;
245

246
  /* Try to find a free entry on the free list first... */
247
  if (tab->free_ents) {
2,002✔
248
    e = tab->free_ents;
38✔
249
    tab->free_ents = e->next;
38✔
250
    e->next = NULL;
38✔
251

252
    return e;
253
  }
254

255
  /* ...otherwise, allocate a new entry. */
256
  e = pcalloc(tab->pool, sizeof(pr_table_entry_t));
1,964✔
257

258
  return e;
259
}
260

261
static void tab_entry_free(pr_table_t *tab, pr_table_entry_t *e) {
85✔
262
  /* Clear everything from the given entry. */
263
  memset(e, 0, sizeof(pr_table_entry_t));
85✔
264

265
  /* Add this entry to the table's free list. */
266
  if (tab->free_ents) {
85✔
267
    pr_table_entry_t *i = tab->free_ents;
268

269
    /* Scan to the end of the list. */
270
    while (i->next != NULL) {
7✔
271
      if (!handling_signal) {
×
272
        pr_signals_handle();
×
273
      }
274

275
      i = i->next;
×
276
    }
277

278
    i->next = e;
7✔
279

280
  } else {
281
    tab->free_ents = e;
78✔
282
  }
283
}
85✔
284

285
static void tab_entry_insert(pr_table_t *tab, pr_table_entry_t *e) {
1,993✔
286
  pr_table_entry_t *h = tab->chains[e->idx];
1,993✔
287

288
  if (h &&
3,986✔
289
      h != e) {
1,993✔
290

291
    /* Only insert the entry if the head we found is different from the
292
     * the given entry.  There is an edge case when the entry being added
293
     * is the head of a new chain.
294
     */
295
    tab->entinsert(&h, e);
116✔
296
    tab->chains[e->idx] = h;
116✔
297
  }
298

299
  e->key->nents++;
1,993✔
300
  tab->nents++;
1,993✔
301
}
1,993✔
302

303
static pr_table_entry_t *tab_entry_next(pr_table_t *tab) {
23✔
304
  pr_table_entry_t *ent = NULL;
23✔
305

306
  if (tab->tab_iter_ent) {
23✔
307
    ent = tab->tab_iter_ent->next;
10✔
308

309
    if (!ent) {
10✔
310
      register unsigned int i;
311

312
      /* Reset ent to be NULL, so that if we don't find a populated chain,
313
       * we properly return NULL to the caller.
314
       */
315
      ent = NULL;
10✔
316

317
      /* Skip to the next populated chain. */
318
      for (i = tab->tab_iter_ent->idx + 1; i < tab->nchains; i++) {
1,281✔
319
        if (tab->chains[i]) {
1,272✔
320
          ent = tab->chains[i];
321
          break;
322
        }
323
      }
324
    }
325

326
  } else {
327
    register unsigned int i;
328

329
    /* Find the first non-empty chain. */
330
    for (i = 0; i < tab->nchains; i++) {
1,913✔
331
      if (tab->chains[i]) {
1,924✔
332
        ent = tab->chains[i];
333
        break;
334
      }
335
    }
336
  }
337

338
  tab->tab_iter_ent = ent;
23✔
339
  return ent;
23✔
340
}
341

342
static void tab_entry_remove(pr_table_t *tab, pr_table_entry_t *e) {
85✔
343
  pr_table_entry_t *h = NULL;
85✔
344

345
  h = tab->chains[e->idx];
85✔
346
  tab->entremove(&h, e);
85✔
347
  tab->chains[e->idx] = h;
85✔
348

349
  e->key->nents--;
85✔
350
  if (e->key->nents == 0) {
85✔
351
    tab_key_free(tab, e->key);
85✔
352
    e->key = NULL;
85✔
353
  }
354

355
  tab->nents--;
85✔
356
}
85✔
357

358
static unsigned int tab_get_seed(void) {
359
  unsigned int seed = 0;
2,029✔
360
#ifndef PR_USE_OPENSSL
361
  int fd = -1;
362
  ssize_t nread = 0;
363
#endif /* Not PR_USE_OPENSSL */
364

365
#ifdef PR_USE_OPENSSL
366
  RAND_bytes((unsigned char *) &seed, sizeof(seed));
2,029✔
367
#else
368
  /* Try reading from /dev/urandom, if present.  Use non-blocking IO, so that
369
   * we do not block/wait; many platforms alias /dev/urandom to /dev/random.
370
   */
371
  fd = open("/dev/urandom", O_RDONLY|O_NONBLOCK);
372
  if (fd >= 0) {
373
    nread = read(fd, &seed, sizeof(seed));
374
    (void) close(fd);
375
  }
376

377
  if (nread < 0) {
378
    time_t now = time(NULL);
379

380
    /* No /dev/urandom present (e.g. we might be in a chroot) or not able
381
     * to read the needed amount of data, but we still want a seed.  Fallback
382
     * to using rand(3), PID, and current time.
383
     */
384
    seed = (unsigned int) (rand() ^ getpid() ^ now);
385
  }
386
#endif /* PR_USE_OPENSSL */
387

388
  return seed;
2,029✔
389
}
390

391
/* Public Table API
392
 */
393

394
int pr_table_kadd(pr_table_t *tab, const void *key_data, size_t key_datasz,
2,004✔
395
    const void *value_data, size_t value_datasz) {
396
  unsigned int h = 0, idx = 0;
2,004✔
397
  pr_table_entry_t *e = NULL, *n = NULL;
2,004✔
398

399
  if (tab == NULL ||
4,008✔
400
      key_data == NULL ||
4,008✔
401
      (value_datasz > 0 && value_data == NULL)) {
2,004✔
402
    errno = EINVAL;
1✔
403
    return -1;
1✔
404
  }
405

406
  if (tab->nents == tab->nmaxents) {
2,003✔
407
    /* Table is full. */
408
    errno = ENOSPC;
1✔
409
    return -1;
1✔
410
  }
411

412
  /* Don't forget to add in the random seed data. */
413
  h = tab->keyhash(key_data, key_datasz) + tab->seed;
2,002✔
414

415
  /* The index of the chain to use is the hash value modulo the number
416
   * of chains.
417
   */
418
  idx = h % tab->nchains;
2,002✔
419

420
  /* Allocate a new entry for the given values. */
421
  n = tab_entry_alloc(tab);
4,004✔
422
  n->value_data = value_data;
2,002✔
423
  n->value_datasz = value_datasz;
2,002✔
424
  n->idx = idx;
2,002✔
425

426
  /* Find the current chain entry at this index. */
427
  e = tab->chains[idx];
2,002✔
428
  if (e != NULL) {
2,002✔
429
    pr_table_entry_t *ei;
430

431
    /* There is a chain at this index.  Next step is to see if any entry
432
     * on this chain has the exact same key.  If so, increase the entry ref
433
     * count on that key, otherwise, create a new key.
434
     */
435

436
    for (ei = e; ei; ei = ei->next) {
2,978✔
437
      if (ei->key->hash != h) {
2,987✔
438
        continue;
2,977✔
439
      }
440

441
      /* Hash collision.  Now check if the key data that was hashed
442
       * is identical.  If so, we have multiple values for the same key.
443
       */
444

445
      if (tab->keycmp(ei->key->key_data, ei->key->key_datasz,
10✔
446
          key_data, key_datasz) == 0) {
447

448
        /* Check if this table allows multivalues. */
449
        if (!(tab->flags & PR_TABLE_FL_MULTI_VALUE)) {
10✔
450
          errno = EEXIST;
9✔
451
          return -1;
9✔
452
        }
453

454
        n->key = ei->key;
1✔
455
      }
456
    }
457

458
  } else {
459

460
    /* This new entry becomes the head of this chain. */
461
    tab->chains[idx] = n;
1,877✔
462
  }
463

464
  if (!n->key) {
1,993✔
465
    pr_table_key_t *k;
466

467
    /* Allocate a new key. */
468
    k = tab_key_alloc(tab);
3,984✔
469

470
    k->key_data = (void *) key_data;
1,992✔
471
    k->key_datasz = key_datasz;
1,992✔
472
    k->hash = h;
1,992✔
473
    k->nents = 0;
1,992✔
474

475
    n->key = k;
1,992✔
476
  }
477

478
  tab_entry_insert(tab, n);
1,993✔
479
  return 0;
1,993✔
480
}
481

482
int pr_table_kexists(pr_table_t *tab, const void *key_data, size_t key_datasz) {
2,677✔
483
  unsigned int h, idx;
484
  pr_table_entry_t *head, *ent;
485

486
  if (tab == NULL ||
5,354✔
487
      key_data == NULL) {
2,677✔
488
    errno = EINVAL;
2✔
489
    return -1;
2✔
490
  }
491

492
  if (tab->nents == 0) {
2,675✔
493
    errno = ENOENT;
870✔
494
    return -1;
870✔
495
  }
496

497
  if (tab->flags & PR_TABLE_FL_USE_CACHE) {
1,805✔
498
    /* Has the caller already wanted to lookup this same key previously?
499
     * If so, reuse that lookup if we can.  In this case, "same key" means
500
     * the _exact same pointer_, not identical data.
501
     */
502
    if (tab->cache_ent &&
×
503
        tab->cache_ent->key->key_data == key_data) {
×
504
      return tab->cache_ent->key->nents;
×
505
    }
506
  }
507

508
  /* Don't forget to add in the random seed data. */
509
  h = tab->keyhash(key_data, key_datasz) + tab->seed;
1,805✔
510

511
  idx = h % tab->nchains;
1,805✔
512
  head = tab->chains[idx];
1,805✔
513
  if (head == NULL) {
1,805✔
514
    tab->cache_ent = NULL;
543✔
515
    return 0;
543✔
516
  }
517

518
  for (ent = head; ent; ent = ent->next) {
6✔
519
    if (ent->key == NULL ||
2,530✔
520
        ent->key->hash != h) {
1,265✔
521
      continue;
6✔
522
    }
523

524
    /* Matching hashes.  Now to see if the keys themselves match. */
525
    if (tab->keycmp(ent->key->key_data, ent->key->key_datasz,
1,259✔
526
        key_data, key_datasz) == 0) {
527

528
      if (tab->flags & PR_TABLE_FL_USE_CACHE) {
1,259✔
529
        tab->cache_ent = ent;
×
530
      }
531

532
      return ent->key->nents;
1,259✔
533
    }
534
  }
535

536
  tab->cache_ent = NULL;
3✔
537

538
  errno = EINVAL;
3✔
539
  return 0;
3✔
540
}
541

542
const void *pr_table_kget(pr_table_t *tab, const void *key_data,
8,282✔
543
    size_t key_datasz, size_t *value_datasz) {
544
  unsigned int h = 0;
8,282✔
545
  pr_table_entry_t *head = NULL, *ent = NULL;
8,282✔
546

547
  if (tab == NULL) {
8,282✔
548
    errno = EINVAL;
1✔
549
    return NULL;
1✔
550
  }
551

552
  /* Use a NULL key as a way of rewinding the per-key lookup. */
553
  if (key_data == NULL) {
8,281✔
554
    tab->cache_ent = NULL;
4✔
555
    tab->val_iter_ent = NULL;
4✔
556

557
    errno = ENOENT;
4✔
558
    return NULL;
4✔
559
  }
560

561
  if (tab->nents == 0) {
8,277✔
562
    tab->cache_ent = NULL;
810✔
563
    tab->val_iter_ent = NULL;
810✔
564

565
    errno = ENOENT;
810✔
566
    return NULL;
810✔
567
  }
568

569
  /* Don't forget to add in the random seed data. */
570
  h = tab->keyhash(key_data, key_datasz) + tab->seed;
7,467✔
571

572
  /* Has the caller already looked up this same key previously?
573
   * If so, continue the lookup where we left off.  In this case,
574
   * "same key" means the _exact same pointer_, not identical data.
575
   */
576
  if (tab->val_iter_ent &&
7,467✔
577
      tab->val_iter_ent->key->key_data == key_data) {
×
578
    head = tab->val_iter_ent->next;
×
579

580
  } else if ((tab->flags & PR_TABLE_FL_USE_CACHE) &&
7,470✔
581
             tab->cache_ent &&
4✔
582
             tab->cache_ent->key->key_data == key_data) {
1✔
583

584
     /* If the cached lookup entry matches, we'll use it. */
585
     head = tab->cache_ent->next;
1✔
586

587
  } else {
588
    unsigned int idx = h % tab->nchains;
7,466✔
589
    head = tab->chains[idx];
7,466✔
590
  }
591

592
  if (head == NULL) {
7,467✔
593
    tab->cache_ent = NULL;
1,846✔
594
    tab->val_iter_ent = NULL;
1,846✔
595

596
    errno = ENOENT;
1,846✔
597
    return NULL;
1,846✔
598
  }
599

600
  for (ent = head; ent; ent = ent->next) {
88✔
601
    if (ent->key == NULL ||
11,408✔
602
        ent->key->hash != h) {
5,704✔
603
      continue;
88✔
604
    }
605

606
    /* Matching hashes.  Now to see if the keys themselves match. */
607
    if (tab->keycmp(ent->key->key_data, ent->key->key_datasz,
5,616✔
608
        key_data, key_datasz) == 0) {
609

610
      if (tab->flags & PR_TABLE_FL_USE_CACHE) {
5,616✔
611
        tab->cache_ent = ent;
2✔
612
      }
613

614
      if (tab->flags & PR_TABLE_FL_MULTI_VALUE) {
5,616✔
615
        tab->val_iter_ent = ent;
×
616
      }
617

618
      if (value_datasz) {
5,616✔
619
        *value_datasz = ent->value_datasz;
9✔
620
      }
621

622
      return ent->value_data;
5,616✔
623
    }
624
  }
625

626
  tab->cache_ent = NULL;
5✔
627
  tab->val_iter_ent = NULL;
5✔
628

629
  errno = ENOENT;
5✔
630
  return NULL;
5✔
631
}
632

633
const void *pr_table_kremove(pr_table_t *tab, const void *key_data,
78✔
634
    size_t key_datasz, size_t *value_datasz) {
635
  unsigned int h = 0, idx = 0;
78✔
636
  pr_table_entry_t *head = NULL, *ent = NULL;
78✔
637

638
  if (tab == NULL ||
156✔
639
      key_data == NULL) {
78✔
640
    errno = EINVAL;
2✔
641
    return NULL;
2✔
642
  }
643

644
  if (tab->nents == 0) {
76✔
645
    errno = ENOENT;
14✔
646
    return NULL;
14✔
647
  }
648

649
  /* Has the caller already wanted to lookup this same key previously?
650
   * If so, reuse that lookup if we can.  In this case, "same key" means
651
   * the _exact same pointer_, not identical data.
652
   */
653
  if ((tab->flags & PR_TABLE_FL_USE_CACHE) &&
63✔
654
      tab->cache_ent &&
2✔
655
      tab->cache_ent->key->key_data == key_data) {
1✔
656
    const void *value_data;
657

658
    value_data = tab->cache_ent->value_data;
1✔
659
    if (value_datasz) {
1✔
660
      *value_datasz = tab->cache_ent->value_datasz;
×
661
    }
662

663
    tab_entry_remove(tab, tab->cache_ent);
1✔
664
    tab_entry_free(tab, tab->cache_ent);
1✔
665
    tab->cache_ent = NULL;
1✔
666

667
    return value_data;
1✔
668
  }
669

670
  /* Don't forget to add in the random seed data. */
671
  h = tab->keyhash(key_data, key_datasz) + tab->seed;
61✔
672

673
  idx = h % tab->nchains;
61✔
674
  head = tab->chains[idx];
61✔
675
  if (head == NULL) {
61✔
676
    tab->cache_ent = NULL;
×
677

678
    errno = ENOENT;
×
679
    return NULL;
×
680
  }
681

682
  for (ent = head; ent; ent = ent->next) {
×
683
    if (ent->key == NULL ||
122✔
684
        ent->key->hash != h) {
61✔
685
      continue;
×
686
    }
687

688
    /* Matching hashes.  Now to see if the keys themselves match. */
689
    if (tab->keycmp(ent->key->key_data, ent->key->key_datasz,
61✔
690
        key_data, key_datasz) == 0) {
691
      const void *value_data;
692

693
      value_data = ent->value_data;
61✔
694
      if (value_datasz) {
61✔
695
        *value_datasz = ent->value_datasz;
1✔
696
      }
697

698
      tab_entry_remove(tab, ent);
61✔
699
      tab_entry_free(tab, ent);
61✔
700
      tab->cache_ent = NULL;
61✔
701

702
      return value_data;
61✔
703
    }
704
  }
705

706
  tab->cache_ent = NULL;
×
707

708
  errno = EINVAL;
×
709
  return NULL;
×
710
}
711

712
int pr_table_kset(pr_table_t *tab, const void *key_data, size_t key_datasz,
1,223✔
713
    const void *value_data, size_t value_datasz) {
714
  unsigned int h;
715
  pr_table_entry_t *head, *ent;
716

717
  /* XXX Should callers be allowed to set NULL values for keys? */
718

719
  if (tab == NULL ||
2,446✔
720
      key_data == NULL ||
2,446✔
721
      (value_datasz > 0 && value_data == NULL)) {
1,223✔
722
    errno = EINVAL;
1✔
723
    return -1;
1✔
724
  }
725

726
  if (tab->nents == 0) {
1,222✔
727
    errno = ENOENT;
1✔
728
    return -1;
1✔
729
  }
730

731
  /* Don't forget to add in the random seed data. */
732
  h = tab->keyhash(key_data, key_datasz) + tab->seed;
1,221✔
733

734
  /* Has the caller already looked up this same key previously?
735
   * If so, continue the lookup where we left off.  In this case,
736
   * "same key" means the _exact same pointer_, not identical data.
737
   */
738
  if (tab->val_iter_ent &&
1,221✔
739
      tab->val_iter_ent->key->key_data == key_data) {
×
740
    head = tab->val_iter_ent->next;
×
741

742
  } else if ((tab->flags & PR_TABLE_FL_USE_CACHE) &&
1,221✔
743
             tab->cache_ent &&
×
744
             tab->cache_ent->key->key_data == key_data) {
×
745

746
     /* If the cached lookup entry matches, we'll use it. */
747
     head = tab->cache_ent->next;
×
748

749
  } else {
750
    unsigned int idx = h % tab->nchains;
1,221✔
751
    head = tab->chains[idx];
1,221✔
752
  }
753

754
  if (head == NULL) {
1,221✔
755
    tab->cache_ent = NULL;
×
756
    tab->val_iter_ent = NULL;
×
757

758
    errno = ENOENT;
×
759
    return -1;
×
760
  }
761

762
  for (ent = head; ent; ent = ent->next) {
3✔
763
    if (ent->key == NULL ||
2,448✔
764
        ent->key->hash != h) {
1,224✔
765
      continue;
3✔
766
    }
767

768
    /* Matching hashes.  Now to see if the keys themselves match. */
769
    if (tab->keycmp(ent->key->key_data, ent->key->key_datasz,
1,221✔
770
        key_data, key_datasz) == 0) {
771

772
      if (ent->value_data == value_data) {
1,221✔
773
        errno = EEXIST;
×
774
        return -1;
×
775
      }
776

777
      ent->value_data = value_data;
1,221✔
778
      ent->value_datasz = value_datasz;
1,221✔
779

780
      if (tab->flags & PR_TABLE_FL_USE_CACHE) {
1,221✔
781
        tab->cache_ent = ent;
×
782
      }
783

784
      if (tab->flags & PR_TABLE_FL_MULTI_VALUE) {
1,221✔
785
        tab->val_iter_ent = ent;
×
786
      }
787

788
      return 0;
789
    }
790
  }
791

792
  tab->cache_ent = NULL;
×
793
  tab->val_iter_ent = NULL;
×
794

795
  errno = EINVAL;
×
796
  return -1;
×
797
}
798

799
int pr_table_add(pr_table_t *tab, const char *key_data, const void *value_data,
1,884✔
800
    size_t value_datasz) {
801

802
  if (tab == NULL ||
3,768✔
803
      key_data == NULL) {
1,884✔
804
    errno = EINVAL;
3✔
805
    return -1;
3✔
806
  }
807

808
  if (value_data &&
3,762✔
809
      value_datasz == 0) {
1,881✔
810
    value_datasz = strlen((char *) value_data) + 1;
29✔
811
  }
812

813
  return pr_table_kadd(tab, key_data, strlen(key_data) + 1, value_data,
1,881✔
814
    value_datasz);
815
}
816

817
int pr_table_add_dup(pr_table_t *tab, const char *key_data,
22✔
818
    const void *value_data, size_t value_datasz) {
819
  void *dup_data;
820

821
  if (tab == NULL ||
44✔
822
      key_data == NULL) {
22✔
823
    errno = EINVAL;
2✔
824
    return -1;
2✔
825
  }
826

827
  if (value_data == NULL) {
20✔
828
    if (value_datasz == 0) {
3✔
829
      return 0;
830
    }
831

832
    errno = EINVAL;
1✔
833
    return -1;
1✔
834
  }
835

836
  if (value_data != NULL &&
17✔
837
      value_datasz == 0) {
17✔
838
    value_datasz = strlen((const char *) value_data) + 1;
15✔
839
  }
840

841
  dup_data = pcalloc(tab->pool, value_datasz);
17✔
842
  memcpy(dup_data, value_data, value_datasz);
17✔
843

844
  return pr_table_add(tab, key_data, dup_data, value_datasz);
17✔
845
}
846

847
pr_table_t *pr_table_nalloc(pool *p, int flags, unsigned int nchains) {
2,032✔
848
  pr_table_t *tab;
849
  pool *tab_pool;
850

851
  if (p == NULL ||
4,064✔
852
      nchains == 0) {
2,032✔
853
    errno = EINVAL;
3✔
854
    return NULL;
3✔
855
  }
856

857
  tab_pool = make_sub_pool(p);
2,029✔
858
  pr_pool_tag(tab_pool, "table pool");
2,029✔
859

860
  tab = pcalloc(tab_pool, sizeof(pr_table_t));
2,029✔
861
  tab->pool = tab_pool;
2,029✔
862
  tab->flags = flags;
2,029✔
863
  tab->nchains = nchains;
2,029✔
864
  tab->chains = pcalloc(tab_pool,
2,029✔
865
    sizeof(pr_table_entry_t *) * tab->nchains);
2,029✔
866

867
  tab->keycmp = key_cmp;
2,029✔
868
  tab->keyhash = key_hash;
2,029✔
869
  tab->entinsert = entry_insert;
2,029✔
870
  tab->entremove = entry_remove;
2,029✔
871

872
  tab->seed = tab_get_seed();
2,029✔
873
  tab->nmaxents = PR_TABLE_DEFAULT_MAX_ENTS;
2,029✔
874

875
  return tab;
2,029✔
876
}
877

878
pr_table_t *pr_table_alloc(pool *p, int flags) {
1,633✔
879
  return pr_table_nalloc(p, flags, PR_TABLE_DEFAULT_NCHAINS);
1,633✔
880
}
881

882
int pr_table_count(pr_table_t *tab) {
2,444✔
883
  if (tab == NULL) {
2,444✔
884
    errno = EINVAL;
1,549✔
885
    return -1;
1,549✔
886
  }
887

888
  return tab->nents;
895✔
889
}
890

891
int pr_table_do(pr_table_t *tab, int (*cb)(const void *key_data,
6✔
892
    size_t key_datasz, const void *value_data, size_t value_datasz,
893
    void *user_data), void *user_data, int flags) {
894
  register unsigned int i;
895

896
  if (tab == NULL ||
12✔
897
      cb == NULL) {
6✔
898
    errno = EINVAL;
2✔
899
    return -1;
2✔
900
  }
901

902
  if (tab->nents == 0) {
4✔
903
    return 0;
904
  }
905

906
  for (i = 0; i < tab->nchains; i++) {
528✔
907
    pr_table_entry_t *ent;
908

909
    ent = tab->chains[i];
529✔
910
    while (ent != NULL) {
1,062✔
911
      pr_table_entry_t *next_ent;
912
      int res;
913

914
      next_ent = ent->next;
5✔
915

916
      if (!handling_signal) {
5✔
917
        pr_signals_handle();
5✔
918
      }
919

920
      res = cb(ent->key->key_data, ent->key->key_datasz, ent->value_data,
5✔
921
        ent->value_datasz, user_data);
922
      if (res < 0 &&
8✔
923
          !(flags & PR_TABLE_DO_FL_ALL)) {
3✔
924
        errno = EPERM;
1✔
925
        return -1;
1✔
926
      }
927

928
      ent = next_ent;
929
    }
930
  }
931

932
  return 0;
933
}
934

935
int pr_table_empty(pr_table_t *tab) {
108✔
936
  register unsigned int i;
937

938
  if (tab == NULL) {
108✔
939
    errno = EINVAL;
1✔
940
    return -1;
1✔
941
  }
942

943
  if (tab->nents == 0) {
107✔
944
    return 0;
945
  }
946

947
  for (i = 0; i < tab->nchains; i++) {
5,121✔
948
    pr_table_entry_t *e;
949

950
    e = tab->chains[i];
5,121✔
951
    while (e != NULL) {
10,265✔
952
      if (!handling_signal) {
23✔
953
        pr_signals_handle();
23✔
954
      }
955

956
      tab_entry_remove(tab, e);
23✔
957
      tab_entry_free(tab, e);
23✔
958

959
      e = tab->chains[i];
23✔
960
    }
961

962
    tab->chains[i] = NULL;
5,121✔
963
  }
964

965
  return 0;
966
}
967

968
int pr_table_exists(pr_table_t *tab, const char *key_data) {
2,666✔
969
  if (tab == NULL ||
5,332✔
970
      key_data == NULL) {
2,666✔
971
    errno = EINVAL;
3✔
972
    return -1;
3✔
973
  }
974

975
  return pr_table_kexists(tab, key_data, strlen(key_data) + 1);
2,663✔
976
}
977

978
int pr_table_free(pr_table_t *tab) {
106✔
979

980
  if (tab == NULL) {
106✔
981
    errno = EINVAL;
1✔
982
    return -1;
1✔
983
  }
984

985
  if (tab->nents != 0) {
105✔
986
    errno = EPERM;
1✔
987
    return -1;
1✔
988
  }
989

990
  destroy_pool(tab->pool);
104✔
991
  return 0;
104✔
992
}
993

994
const void *pr_table_get(pr_table_t *tab, const char *key_data,
9,851✔
995
    size_t *value_datasz) {
996
  size_t key_datasz = 0;
9,851✔
997

998
  if (tab == NULL) {
9,851✔
999
    errno = EINVAL;
1,592✔
1000
    return NULL;
1,592✔
1001
  }
1002

1003
  if (key_data) {
8,259✔
1004
    key_datasz = strlen(key_data) + 1;
8,255✔
1005
  }
1006

1007
  return pr_table_kget(tab, key_data, key_datasz, value_datasz);
8,259✔
1008
}
1009

1010
const void *pr_table_knext(pr_table_t *tab, size_t *key_datasz) {
24✔
1011
  pr_table_entry_t *ent, *prev;
1012

1013
  if (tab == NULL) {
24✔
1014
    errno = EINVAL;
1✔
1015
    return NULL;
1✔
1016
  }
1017

1018
  prev = tab->tab_iter_ent;
23✔
1019

1020
  ent = tab_entry_next(tab);
23✔
1021
  while (ent != NULL) {
46✔
1022
    if (!handling_signal) {
12✔
1023
      pr_signals_handle();
12✔
1024
    }
1025

1026
    if (prev &&
13✔
1027
        ent->key == prev->key) {
1✔
1028
      ent = tab_entry_next(tab);
×
1029
      continue;
×
1030
    }
1031

1032
    break;
1033
  }
1034

1035
  if (ent == NULL) {
23✔
1036
    errno = EPERM;
11✔
1037
    return NULL;
11✔
1038
  }
1039

1040
  if (key_datasz != NULL) {
12✔
1041
    *key_datasz = ent->key->key_datasz;
3✔
1042
  }
1043

1044
  return ent->key->key_data;
12✔
1045
}
1046

1047
const void *pr_table_next(pr_table_t *tab) {
19✔
1048
  return pr_table_knext(tab, NULL);
19✔
1049
}
1050

1051
const void *pr_table_remove(pr_table_t *tab, const char *key_data,
79✔
1052
    size_t *value_datasz) {
1053

1054
  if (tab == NULL ||
158✔
1055
      key_data == NULL) {
79✔
1056
    errno = EINVAL;
8✔
1057
    return NULL;
8✔
1058
  }
1059

1060
  return pr_table_kremove(tab, key_data, strlen(key_data) + 1, value_datasz);
71✔
1061
}
1062

1063
int pr_table_rewind(pr_table_t *tab) {
26✔
1064
  if (tab == NULL) {
26✔
1065
    errno = EINVAL;
1✔
1066
    return -1;
1✔
1067
  }
1068

1069
  tab->tab_iter_ent = NULL;
25✔
1070
  return 0;
25✔
1071
}
1072

1073
int pr_table_set(pr_table_t *tab, const char *key_data, const void *value_data,
1,225✔
1074
    size_t value_datasz) {
1075

1076
  if (tab == NULL ||
2,450✔
1077
      key_data == NULL) {
1,225✔
1078
    errno = EINVAL;
2✔
1079
    return -1;
2✔
1080
  }
1081

1082
  if (value_data &&
2,446✔
1083
      value_datasz == 0) {
1,223✔
1084
    value_datasz = strlen((const char *) value_data) + 1;
1✔
1085
  }
1086

1087
  return pr_table_kset(tab, key_data, strlen(key_data) + 1, value_data,
1,223✔
1088
    value_datasz);
1089
}
1090

1091
int pr_table_ctl(pr_table_t *tab, int cmd, void *arg) {
83✔
1092

1093
  if (tab == NULL) {
83✔
1094
    errno = EINVAL;
1✔
1095
    return -1;
1✔
1096
  }
1097

1098
  /* Set control operations can only be performed on an empty table. */
1099
  if (tab->nents != 0) {
82✔
1100
    errno = EPERM;
3✔
1101
    return -1;
3✔
1102
  }
1103

1104
  switch (cmd) {
79✔
1105
    case PR_TABLE_CTL_SET_KEY_HASH:
14✔
1106
      tab->keyhash = arg ?
14✔
1107
        (unsigned int (*)(const void *, size_t)) arg :
14✔
1108
        (unsigned int (*)(const void *, size_t)) key_hash;
1109
      return 0;
14✔
1110

1111
    case PR_TABLE_CTL_SET_FLAGS:
2✔
1112
      if (arg == NULL) {
2✔
1113
        errno = EINVAL;
1✔
1114
        return -1;
1✔
1115
      }
1116

1117
      tab->flags = *((unsigned long *) arg);
1✔
1118
      return 0;
1✔
1119

1120
    case PR_TABLE_CTL_SET_KEY_CMP:
13✔
1121
      tab->keycmp = arg ?
13✔
1122
        (int (*)(const void *, size_t, const void *, size_t)) arg :
13✔
1123
        (int (*)(const void *, size_t, const void *, size_t)) key_cmp;
1124
      return 0;
13✔
1125

1126
    case PR_TABLE_CTL_SET_ENT_INSERT:
1✔
1127
      tab->entinsert = arg ?
1✔
1128
        (void (*)(pr_table_entry_t **, pr_table_entry_t *)) arg :
1✔
1129
        (void (*)(pr_table_entry_t **, pr_table_entry_t *)) entry_insert;
1130
      return 0;
1✔
1131

1132
    case PR_TABLE_CTL_SET_ENT_REMOVE:
1✔
1133
      tab->entremove = arg ?
1✔
1134
        (void (*)(pr_table_entry_t **, pr_table_entry_t *)) arg :
1✔
1135
        (void (*)(pr_table_entry_t **, pr_table_entry_t *)) entry_remove;
1136
      return 0;
1✔
1137

1138
    case PR_TABLE_CTL_SET_NCHAINS: {
3✔
1139
      unsigned int new_nchains;
1140

1141
      if (arg == NULL) {
3✔
1142
        errno = EINVAL;
1✔
1143
        return -1;
1✔
1144
      }
1145

1146
      new_nchains = *((unsigned int *) arg);
2✔
1147
      if (new_nchains == 0) {
2✔
1148
        errno = EINVAL;
1✔
1149
        return -1;
1✔
1150
      }
1151

1152
      tab->nchains = new_nchains;
1✔
1153

1154
      /* Note: by not freeing the memory of the previously allocated
1155
       * chains, this constitutes a minor leak of the table's memory pool.
1156
       */
1157
      tab->chains = pcalloc(tab->pool,
1✔
1158
        sizeof(pr_table_entry_t *) * tab->nchains);
1✔
1159
      return 0;
1✔
1160
    }
1161

1162
    case PR_TABLE_CTL_SET_MAX_ENTS: {
44✔
1163
      unsigned int nmaxents;
1164

1165
      nmaxents = *((unsigned int *) arg);
44✔
1166

1167
      if (nmaxents == 0) {
44✔
1168
        errno = EINVAL;
1✔
1169
        return -1;
1✔
1170
      }
1171

1172
      /* If the given maximum is less than the number of entries currently
1173
       * in the table, we have to return an error; we do not want to get
1174
       * into the business of table truncation just yet.
1175
       */
1176
      if (tab->nents > nmaxents) {
1177
        errno = EPERM;
1178
        return -1;
1179
      }
1180

1181
      tab->nmaxents = nmaxents;
43✔
1182
      return 0;
43✔
1183
    }
1184

1185
    default:
1✔
1186
      errno = EINVAL;
1✔
1187
  }
1188

1189
  return -1;
1✔
1190
}
1191

1192
void *pr_table_pcalloc(pr_table_t *tab, size_t sz) {
43✔
1193
  if (tab == NULL ||
86✔
1194
      sz == 0) {
43✔
1195
    errno = EINVAL;
3✔
1196
    return NULL;
3✔
1197
  }
1198

1199
  return pcalloc(tab->pool, sz);
40✔
1200
}
1201

1202
static void table_printf(const char *fmt, ...) {
2✔
1203
  char buf[PR_TUNABLE_BUFFER_SIZE+1];
1204
  va_list msg;
1205

1206
  memset(buf, '\0', sizeof(buf));
2✔
1207
  va_start(msg, fmt);
2✔
1208
  pr_vsnprintf(buf, sizeof(buf)-1, fmt, msg);
2✔
1209
  va_end(msg);
2✔
1210

1211
  buf[sizeof(buf)-1] = '\0';
2✔
1212
  pr_trace_msg(trace_channel, 19, "dump: %s", buf);
2✔
1213
}
2✔
1214

1215
float pr_table_load(pr_table_t *tab) {
2✔
1216
  float load_factor;
1217

1218
  if (tab == NULL) {
2✔
1219
    errno = EINVAL;
1✔
1220
    return -1.0;
1✔
1221
  }
1222

1223
  load_factor = (tab->nents / tab->nchains);
1✔
1224
  return load_factor;
1✔
1225
}
1226

1227
static int tab_copy_cb(const void *key_data, size_t key_datasz,
×
1228
    const void *value_data, size_t value_datasz, void *user_data) {
1229
  int res;
1230
  pr_table_t *dst_tab;
1231

1232
  dst_tab = user_data;
×
1233

1234
  res = pr_table_kexists(dst_tab, key_data, key_datasz);
×
1235
  if (res > 0) {
×
1236
    res = pr_table_kset(dst_tab, key_data, key_datasz, value_data,
×
1237
      value_datasz);
1238

1239
  } else {
1240
    res = pr_table_kadd(dst_tab, key_data, key_datasz, value_data,
×
1241
      value_datasz);
1242
  }
1243

1244
  return res;
×
1245
}
1246

1247
int pr_table_copy(pr_table_t *dst_tab, pr_table_t *src_tab, int flags) {
×
1248
  int res;
1249

1250
  if (dst_tab == NULL ||
×
1251
      src_tab == NULL) {
×
1252
    errno = EINVAL;
×
1253
    return -1;
×
1254
  }
1255

1256
  /* Future flags may support a DEEP_COPY flag, which would use the
1257
   * dst_tab->pool.
1258
   */
1259
  res = pr_table_do(src_tab, tab_copy_cb, dst_tab, PR_TABLE_DO_FL_ALL);
×
1260
  return res;
×
1261
}
1262

1263
void pr_table_dump(void (*dumpf)(const char *fmt, ...), pr_table_t *tab) {
5✔
1264
  register unsigned int i;
1265

1266
  if (tab == NULL) {
5✔
1267
    return;
1268
  }
1269

1270
  if (dumpf == NULL) {
3✔
1271
    dumpf = table_printf;
1✔
1272
  }
1273

1274
  if (tab->flags == 0) {
3✔
1275
    dumpf("%s", "[table flags]: None");
3✔
1276

1277
  } else {
1278
    if ((tab->flags & PR_TABLE_FL_MULTI_VALUE) &&
×
1279
        (tab->flags & PR_TABLE_FL_USE_CACHE)) {
1280
      dumpf("%s", "[table flags]: MultiValue, UseCache");
×
1281

1282
    } else {
1283
      if (tab->flags & PR_TABLE_FL_MULTI_VALUE) {
×
1284
        dumpf("%s", "[table flags]: MultiValue");
×
1285
      }
1286

1287
      if (tab->flags & PR_TABLE_FL_USE_CACHE) {
×
1288
        dumpf("%s", "[table flags]: UseCache");
×
1289
      }
1290
    }
1291
  }
1292

1293
  if (tab->nents == 0) {
3✔
1294
    dumpf("[empty table]");
3✔
1295
    return;
3✔
1296
  }
1297

1298
  dumpf("[table count]: %u", tab->nents);
×
1299
  for (i = 0; i < tab->nchains; i++) {
×
1300
    register unsigned int j = 0;
×
1301
    pr_table_entry_t *ent = tab->chains[i];
×
1302

1303
    while (ent) {
×
1304
      if (!handling_signal) {
×
1305
        pr_signals_handle();
×
1306
      }
1307

1308
      dumpf("[hash %u (%u chains) chain %u#%u] '%s' => '%s' (%u)",
×
1309
        ent->key->hash, tab->nchains, i, j++, ent->key->key_data,
×
1310
        ent->value_data, ent->value_datasz);
1311
      ent = ent->next;
×
1312
    }
1313
  }
1314
}
1315

1316
int table_handling_signal(int do_handle) {
×
1317
  if (do_handle == TRUE ||
×
1318
      do_handle == FALSE) {
1319
    handling_signal = do_handle;
×
1320
    return 0;
×
1321
  }
1322

1323
  errno = EINVAL;
×
1324
  return -1;
×
1325
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc