Coverage Report

Created: 2023-12-08 06:56

/src/freeradius-server/src/lib/util/dict_fixup.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
6
 *
7
 *   This program is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the Free Software
14
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15
 */
16
17
/** Code to apply fctx and finalisation steps to a dictionary
18
 *
19
 * @file src/lib/util/dict_fixup.c
20
 *
21
 * @copyright 2020 The FreeRADIUS server project
22
 * @copyright 2020 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
23
 */
24
RCSID("$Id: 5224d520c18fa8d7464ede91ee2a0bf3f5f2ea4e $")
25
26
#include <freeradius-devel/util/talloc.h>
27
#include <freeradius-devel/util/file.h>
28
#include "dict_fixup_priv.h"
29
30
/** Common fields for every fixup structure
31
 *
32
 */
33
typedef struct {
34
  fr_dlist_t    entry;      //!< Entry in linked list of fctx.
35
36
  char      *filename;    //!< where the line being fixed up.
37
  int     line;     //!< ditto.
38
} dict_fixup_common_t;
39
40
/** Add an enumeration value to an attribute that wasn't defined at the time the value was parsed
41
 *
42
 */
43
typedef struct {
44
  dict_fixup_common_t common;     //!< Common fields.
45
46
  char      *attribute;   //!< we couldn't find (and will need to resolve later).
47
  char      *name;      //!< Raw enum name.
48
  char      *value;     //!< Raw enum value.  We can't do anything with this until
49
              //!< we know the attribute type, which we only find out later.
50
51
  fr_dict_attr_t const  *parent;    //!< Parent attribute to resolve the 'attribute' string in.
52
} dict_fixup_enumv_t;
53
54
/** Resolve a group reference
55
 *
56
 */
57
typedef struct {
58
  dict_fixup_common_t common;     //!< Common fields.
59
60
  fr_dict_attr_t    *da;      //!< FR_TYPE_GROUP to fix
61
  char      *ref;     //!< the reference name
62
} dict_fixup_group_t;
63
64
/** Clone operation from one tree node to another
65
 *
66
 */
67
typedef struct {
68
  dict_fixup_common_t common;     //!< Common fields.
69
70
  fr_dict_attr_t    *parent;    //!< parent where we add the clone
71
  fr_dict_attr_t    *da;      //!< FR_TYPE_TLV to clone
72
  char      *ref;     //!< the target attribute to clone
73
} dict_fixup_clone_t;
74
75
/** Dictionary attribute namespaces need their hash tables finalised
76
 *
77
 */
78
typedef struct {
79
  dict_fixup_common_t common;     //!< Common fields.
80
81
  fr_hash_table_t   *hash;      //!< We need to finalise.
82
} dict_fixup_hash_t;
83
84
/** Initialise common fields in fixup struct, and add it to a fixup list
85
 *
86
 * @param[in] filename    this fixup relates to.
87
 * @param[in] line    this fixup relates to.
88
 * @param[in] fixup_list  to add fixup to.
89
 * @param[in] common    common header to populate.
90
 * @return
91
 *  - 0 on success.
92
 *  - -1 on out of memory.
93
 */
94
static inline CC_HINT(always_inline) int dict_fixup_common(char const *filename, int line,
95
                 fr_dlist_head_t *fixup_list, dict_fixup_common_t *common)
96
28
{
97
28
  common->filename = talloc_strdup(common, filename);
98
28
  if (!common->filename) {
99
0
    fr_strerror_const("Out of memory");
100
0
    return -1;
101
0
  }
102
28
  common->line = line;
103
104
28
  fr_dlist_insert_tail(fixup_list, common);
105
106
28
  return 0;
107
28
}
108
109
/** Add an enumeration value to an attribute which has not yet been defined
110
 *
111
 * @param[in] fctx    Holds current dictionary parsing information.
112
 * @param[in] filename    this fixup relates to.
113
 * @param[in] line    this fixup relates to.
114
 * @param[in] attr    The OID string pointing to the attribute
115
 *        to add the enumeration value to.
116
 * @param[in] attr_len    The length of the attr string.
117
 * @param[in] name    The name of the enumv.
118
 * @param[in] name_len    Length of the name string.
119
 * @param[in] value   Value string.  This is kept as a string until we know
120
 *        what type we want to transform it into.
121
 * @param[in] value_len   Length of the value string.
122
 * @param[in] parent    of this attribute.
123
 * @return
124
 *  - 0 on success.
125
 *  - -1 on out of memory.
126
 */
127
int dict_fixup_enumv(dict_fixup_ctx_t *fctx, char const *filename, int line,
128
         char const *attr, size_t attr_len,
129
         char const *name, size_t name_len,
130
         char const *value, size_t value_len,
131
         fr_dict_attr_t const *parent)
132
1
{
133
1
  dict_fixup_enumv_t *fixup;
134
135
1
  fixup = talloc(fctx->pool, dict_fixup_enumv_t);
136
1
  if (!fixup) {
137
0
  oom:
138
0
    fr_strerror_const("Out of memory");
139
0
    return -1;
140
0
  }
141
1
  *fixup = (dict_fixup_enumv_t) {
142
1
    .attribute = talloc_bstrndup(fixup, attr, attr_len),
143
1
    .name = talloc_bstrndup(fixup, name, name_len),
144
1
    .value = talloc_bstrndup(fixup, value, value_len),
145
1
    .parent = parent
146
1
  };
147
1
  if (!fixup->attribute || !fixup->name || !fixup->value) goto oom;
148
149
1
  return dict_fixup_common(filename, line, &fctx->enumv, &fixup->common);
150
1
}
151
152
/** Add a previously defined enumeration value to an existing attribute
153
 *
154
 * @param[in] fctx    Holds current dictionary parsing information.
155
 * @param[in] fixup   Hash table to fill.
156
 * @return
157
 *  - 0 on success.
158
 *  - -1 on failure.
159
 */
160
static inline CC_HINT(always_inline) int dict_fixup_enumv_apply(UNUSED dict_fixup_ctx_t *fctx, dict_fixup_enumv_t *fixup)
161
1
{
162
1
  fr_dict_attr_t    *da;
163
1
  fr_value_box_t    value = FR_VALUE_BOX_INITIALISER_NULL(value);
164
1
  fr_type_t   type;
165
1
  int     ret;
166
1
  fr_dict_attr_t const  *da_const;
167
168
1
  da_const = fr_dict_attr_by_oid(NULL, fixup->parent, fixup->attribute);
169
1
  if (!da_const) {
170
0
    fr_strerror_printf_push("Failed resolving ATTRIBUTE referenced by VALUE '%s' at %s[%d]",
171
0
          fixup->name, fr_cwd_strip(fixup->common.filename), fixup->common.line);
172
0
    return -1;
173
0
  }
174
1
  da = fr_dict_attr_unconst(da_const);
175
1
  type = da->type;
176
177
1
  if (fr_value_box_from_str(fixup, &value, type, NULL,
178
1
          fixup->value, talloc_array_length(fixup->value) - 1,
179
1
          NULL, false) < 0) {
180
0
    fr_strerror_printf_push("Invalid VALUE '%pV' for attribute '%s' at %s[%d]",
181
0
          fr_box_strvalue_buffer(fixup->value),
182
0
          da->name,
183
0
          fr_cwd_strip(fixup->common.filename), fixup->common.line);
184
0
    return -1;
185
0
  }
186
187
1
  ret = fr_dict_enum_add_name(da, fixup->name, &value, false, false);
188
1
  fr_value_box_clear(&value);
189
190
1
  if (ret < 0) return -1;
191
192
1
  return 0;
193
1
}
194
195
/** Resolve a group reference
196
 *
197
 * This is required as the reference may point to another dictionary which
198
 * hasn't been loaded yet.
199
 *
200
 * @param[in] fctx    Holds current dictionary parsing information.
201
 * @param[in] filename    this fixup relates to.
202
 * @param[in] line    this fixup relates to.
203
 * @param[in] da    The group dictionary attribute.
204
 * @param[in] ref   OID string representing what the group references.
205
 * @param[in] ref_len   Length of the reference string.
206
 * @return
207
 *  - 0 on success.
208
 *  - -1 on out of memory.
209
 */
210
int dict_fixup_group(dict_fixup_ctx_t *fctx, char const *filename, int line,
211
         fr_dict_attr_t *da, char const *ref, size_t ref_len)
212
0
{
213
0
  dict_fixup_group_t *fixup;
214
215
0
  fixup = talloc(fctx->pool, dict_fixup_group_t);
216
0
  if (!fixup) {
217
0
    fr_strerror_const("Out of memory");
218
0
    return -1;
219
0
  }
220
0
  *fixup = (dict_fixup_group_t) {
221
0
    .da = da,
222
0
    .ref = talloc_bstrndup(fixup, ref, ref_len)
223
0
  };
224
225
0
  return dict_fixup_common(filename, line, &fctx->group, &fixup->common);
226
0
}
227
228
static fr_dict_attr_t const *dict_find_or_load_reference(fr_dict_t **dict_def, char const *ref, char const *filename, int line)
229
0
{
230
0
  fr_dict_t   *dict;
231
0
  fr_dict_attr_t const  *da;
232
0
  char      *p;
233
0
  ssize_t     slen;
234
235
0
  da = fr_dict_attr_by_oid(NULL, fr_dict_root(*dict_def), ref);
236
0
  if (da) return da;
237
238
  /*
239
   *  The attribute doesn't exist, and the reference
240
   *  isn't in a "PROTO.ATTR" format, die.
241
   */
242
0
  p = strchr(ref, '.');
243
244
  /*
245
   *  Get / skip protocol name.
246
   */
247
0
  slen = dict_by_protocol_substr(NULL,
248
0
               &dict, &FR_SBUFF_IN(ref, strlen(ref)),
249
0
               *dict_def);
250
0
  if (slen <= 0) {
251
0
    fr_dict_t *other;
252
253
0
    if (p) *p = '\0';
254
255
    /*
256
     *  Can't load the dictionary we're loading.
257
     */
258
0
    if (dict == *dict_def) {
259
0
      fr_strerror_printf("Cannot reference parent dictionary %s from within the same dictionary", fr_dict_root(dict)->name);
260
0
      return NULL;
261
0
    }
262
263
0
    if (fr_dict_protocol_afrom_file(&other, ref, NULL, filename) < 0) {
264
0
      return NULL;
265
0
    }
266
267
0
    if (p) *p = '.';
268
269
    /*
270
     *  Grab the protocol name again
271
     */
272
0
    dict = other;
273
0
    if (!p) {
274
0
      *dict_def = other;
275
0
      return other->root;
276
0
    }
277
278
0
    slen = p - ref;
279
0
  }
280
281
0
  if (slen < 0) {
282
0
  invalid_reference:
283
0
    fr_strerror_printf("Invalid attribute reference '%s' at %s[%d]",
284
0
           ref,
285
0
           fr_cwd_strip(filename), line);
286
0
    return NULL;
287
0
  }
288
289
  /*
290
   *  No known dictionary, so we're asked to just
291
   *  use the whole string.  Which we did above.  So
292
   *  either it's a bad ref, OR it's a ref to a
293
   *  dictionary which doesn't exist.
294
   */
295
0
  if (slen == 0) goto invalid_reference;
296
297
  /*
298
   *  Just a bare reference to the protocol.  Use the root.
299
   */
300
0
  if (!ref[slen]) {
301
0
    *dict_def = dict;
302
0
    return fr_dict_root(dict);
303
0
  }
304
305
  /*
306
   *  Look up the attribute.
307
   */
308
0
  da = fr_dict_attr_by_oid(NULL, fr_dict_root(dict), ref + slen + 1);
309
0
  if (!da) {
310
0
    fr_strerror_printf("No such attribute '%s' in reference at %s[%d]",
311
0
           ref + slen + 1, fr_cwd_strip(filename), line);
312
0
    return NULL;
313
0
  }
314
315
0
  *dict_def = dict;
316
0
  return da;
317
0
}
318
319
/** Resolve a group reference
320
 *
321
 * @param[in] fctx    Holds current dictionary parsing information.
322
 * @param[in] fixup   Hash table to fill.
323
 * @return
324
 *  - 0 on success.
325
 *  - -1 on failure.
326
 */
327
static inline CC_HINT(always_inline) int dict_fixup_group_apply(UNUSED dict_fixup_ctx_t *fctx, dict_fixup_group_t *fixup)
328
0
{
329
0
  fr_dict_attr_t const *da;
330
0
  fr_dict_t *dict = fr_dict_unconst(fr_dict_by_da(fixup->da));
331
332
  /*
333
   *
334
   *  We avoid refcount loops by using the "autoref"
335
   *  table.  If a "group" attribute refers to a
336
   *  dictionary which does not exist, we load it,
337
   *  increment its reference count, and add it to
338
   *  the autoref table.
339
   *
340
   *  If a group attribute refers to a dictionary
341
   *  which does exist, we check that dictionaries
342
   *  "autoref" table.  If OUR dictionary is there,
343
   *  then we do nothing else.  That dictionary
344
   *  points to us via refcounts, so we can safely
345
   *  point to it.  The refcounts ensure that we
346
   *  won't be free'd before the other one is
347
   *  free'd.
348
   *
349
   *  If our dictionary is NOT in the other
350
   *  dictionaries autoref table, then it was loaded
351
   *  via some other method.  We increment its
352
   *  refcount, and add it to our autoref table.
353
   *
354
   *  Then when this dictionary is being free'd, we
355
   *  also free the dictionaries in our autoref
356
   *  table.
357
   */
358
0
  da = dict_find_or_load_reference(&dict, fixup->ref, fixup->common.filename, fixup->common.line);
359
0
  if (!da) return -1;
360
361
0
  if (da->type != FR_TYPE_TLV) {
362
0
    fr_strerror_printf("References MUST be to attributes of type 'tlv' at %s[%d]",
363
0
           fr_cwd_strip(fixup->common.filename), fixup->common.line);
364
0
    return -1;
365
0
  }
366
367
0
  if (fr_dict_attr_ref(da)) {
368
0
    fr_strerror_printf("References MUST NOT refer to an ATTRIBUTE which also has 'ref=...' at %s[%d]",
369
0
           fr_cwd_strip(fixup->common.filename), fixup->common.line);
370
0
    return -1;
371
0
  }
372
0
  dict_attr_ref_set(fixup->da, da);
373
374
0
  return 0;
375
0
}
376
377
/** Clone one area of a tree into another
378
 *
379
 * These must be processed later to ensure that we've finished building an
380
 * attribute by the time it has been cloned.
381
 *
382
 * @param[in] fctx    Holds current dictionary parsing information.
383
 * @param[in] filename    this fixup relates to.
384
 * @param[in] line    this fixup relates to.
385
 * @param[in] parent    for the cloned attribute.
386
 * @param[in] da    The group dictionary attribute.
387
 * @param[in] ref   OID string representing what the group references.
388
 * @param[in] ref_len   Length of the reference string.
389
 * @return
390
 *  - 0 on success.
391
 *  - -1 on out of memory.
392
 */
393
int dict_fixup_clone(dict_fixup_ctx_t *fctx, char const *filename, int line,
394
         fr_dict_attr_t *parent, fr_dict_attr_t *da,
395
         char const *ref, size_t ref_len)
396
27
{
397
27
  dict_fixup_clone_t *fixup;
398
399
  /*
400
   *  Delay type checks until we've loaded all of the
401
   *  dictionaries.  This means that errors are produced
402
   *  later, but that shouldn't matter for the default
403
   *  dictionaries.  They're supposed to work.
404
   */
405
27
  fixup = talloc(fctx->pool, dict_fixup_clone_t);
406
27
  if (!fixup) {
407
0
    fr_strerror_const("Out of memory");
408
0
    return -1;
409
0
  }
410
27
  *fixup = (dict_fixup_clone_t) {
411
27
    .parent = parent,
412
27
    .da = da,
413
27
    .ref = talloc_bstrndup(fixup, ref, ref_len)
414
27
  };
415
416
27
  return dict_fixup_common(filename, line, &fctx->clone, &fixup->common);
417
27
}
418
419
/** Clone one are of a tree into another
420
 *
421
 * @param[in] fctx    Holds current dictionary parsing information.
422
 * @param[in] fixup   Containing source/destination of the clone.
423
 * @return
424
 *  - 0 on success.
425
 *  - -1 on failure.
426
 */
427
static inline CC_HINT(always_inline) int dict_fixup_clone_apply(UNUSED dict_fixup_ctx_t *fctx, dict_fixup_clone_t *fixup)
428
27
{
429
27
  fr_dict_attr_t const  *da;
430
27
  fr_dict_attr_t    *cloned;
431
27
  fr_dict_t   *dict = fr_dict_unconst(fr_dict_by_da(fixup->da));
432
433
  /*
434
   *  We can't clone our parents.
435
   */
436
27
  da = fr_dict_attr_by_oid(NULL, fr_dict_root(dict), fixup->ref);
437
27
  if (da) {
438
    /*
439
     *  The referenced DA is higher than the one we're
440
     *  creating.  Ensure it's not a parent.
441
     */
442
27
    if (da->depth < fixup->da->depth) {
443
0
      fr_dict_attr_t const *parent;
444
445
0
      for (parent = fixup->da->parent; !parent->flags.is_root; parent = parent->parent) {
446
0
        if (parent == da) {
447
0
          fr_strerror_printf("References MUST NOT refer to a parent attribute %s at %s[%d]",
448
0
                 parent->name, fr_cwd_strip(fixup->common.filename), fixup->common.line);
449
0
          return -1;
450
0
        }
451
0
      }
452
0
    }
453
27
  } else {
454
0
    da = dict_find_or_load_reference(&dict, fixup->ref, fixup->common.filename, fixup->common.line);
455
0
    if (!da) return -1;
456
0
  }
457
458
27
  if (fr_dict_attr_ref(da)) {
459
0
    fr_strerror_printf("References MUST NOT refer to an ATTRIBUTE which itself has a 'ref=...' at %s[%d]",
460
0
           fr_cwd_strip(fixup->common.filename), fixup->common.line);
461
0
    return -1;
462
0
  }
463
464
  /*
465
   *  If the attributes are of different types, then we have
466
   *  to _manually_ clone the values.  This means looping
467
   *  over the ref da, and _casting_ the values to the new
468
   *  data type.  If the cast succeeds, we add the value.
469
   *  Otherwise we don't
470
   *
471
   *  We do this if the source type is a leaf node, AND the
472
   *  types are different, or the destination has no
473
   *  children.
474
   */
475
27
  if (!fr_type_is_non_leaf(fixup->da->type) &&
476
27
      ((da->type != fixup->da->type) || !dict_attr_children(da))) {
477
1
    int copied;
478
479
    /*
480
     *  Only TLV and STRUCT types can be the source or destination of clones.
481
     *
482
     *  Leaf types can be cloned, even if they're
483
     *  different types.  But only if they don't have
484
     *  children (i.e. key fields).
485
     */
486
1
    if (fr_type_is_non_leaf(da->type) || fr_type_is_non_leaf(fixup->da->type) ||
487
1
        dict_attr_children(da) || dict_attr_children(fixup->da)) {
488
0
      fr_strerror_printf("Reference MUST be to a simple data type of type '%s' at %s[%d]",
489
0
             fr_type_to_str(fixup->da->type),
490
0
             fr_cwd_strip(fixup->common.filename), fixup->common.line);
491
0
      return -1;
492
0
    }
493
494
    /*
495
     *  We copy all of the VALUEs over from the source
496
     *  da by hand, by casting them.
497
     *
498
     *  We have to do this work manually because we
499
     *  can't call dict_attr_acopy(), as that function
500
     *  copies the VALUE with the *source* data type,
501
     *  where we need the *destination* data type.
502
     */
503
1
    copied = dict_attr_acopy_enumv(fixup->da, da);
504
1
    if (copied < 0) return -1;
505
506
1
    if (!copied) {
507
0
      fr_strerror_printf("Reference copied no VALUEs from type type '%s' at %s[%d]",
508
0
             fr_type_to_str(fixup->da->type),
509
0
             fr_cwd_strip(fixup->common.filename), fixup->common.line);
510
0
      return -1;
511
0
    }
512
513
    /*
514
     *  We don't need to copy any children, so leave
515
     *  fixup->da in the dictionary.
516
     */
517
1
    return 0;
518
1
  }
519
520
  /*
521
   *  Can't clone KEY fields directly, you MUST clone the parent struct.
522
   */
523
26
  if (!fr_type_is_non_leaf(da->type) || fr_dict_attr_is_key_field(da) || fr_dict_attr_is_key_field(fixup->da)) {
524
0
    fr_strerror_printf("Invalid reference from '%s' to %s", fixup->ref, da->name);
525
0
    return -1;
526
0
  }
527
528
  /*
529
   *  Copy the source attribute, but with a
530
   *  new name and a new attribute number.
531
   */
532
26
  cloned = dict_attr_acopy(dict->pool, da, fixup->da->name);
533
26
  if (!cloned) {
534
0
    fr_strerror_printf("Failed copying attribute '%s' to %s", da->name, fixup->ref);
535
0
    return -1;
536
0
  }
537
538
26
  cloned->attr = fixup->da->attr;
539
26
  cloned->parent = fixup->parent; /* we need to re-parent this attribute */
540
26
  cloned->depth = cloned->parent->depth + 1;
541
542
  /*
543
   *  Copy any pre-existing children over.
544
   */
545
26
  if (dict_attr_children(fixup->da)) {
546
2
    if (dict_attr_acopy_children(dict, cloned, fixup->da) < 0) {
547
0
      fr_strerror_printf("Failed copying attribute '%s' from children of %s", da->name, fixup->ref);
548
0
      return -1;
549
0
    }
550
2
  }
551
552
  /*
553
   *  Copy children of the DA we're cloning.
554
   */
555
26
  if (dict_attr_children(da)) {
556
26
    if (dict_attr_acopy_children(dict, cloned, da) < 0) {
557
0
      fr_strerror_printf("Failed copying attribute '%s' from children of %s", da->name, fixup->ref);
558
0
      return -1;
559
0
    }
560
561
26
    if (dict_attr_child_add(fr_dict_attr_unconst(fixup->parent), cloned) < 0) {
562
0
      fr_strerror_printf("Failed adding attribute %s", da->name);
563
0
      talloc_free(cloned);
564
0
      return -1;
565
0
    }
566
26
  }
567
568
26
  if (dict_attr_add_to_namespace(fixup->parent, cloned) < 0) return -1;
569
570
26
  return 0;
571
26
}
572
573
/** Initialise a fixup ctx
574
 *
575
 * @param[in] ctx to allocate the fixup pool in.
576
 * @param[in] fctx  to initialise.
577
 * @return
578
 *  - 0 on success.
579
 *  - -1 on failure.
580
 */
581
int dict_fixup_init(TALLOC_CTX *ctx, dict_fixup_ctx_t *fctx)
582
34
{
583
34
  if (fctx->pool) return 0;
584
585
26
  fr_dlist_talloc_init(&fctx->enumv, dict_fixup_enumv_t, common.entry);
586
26
  fr_dlist_talloc_init(&fctx->group, dict_fixup_group_t, common.entry);
587
26
  fr_dlist_talloc_init(&fctx->clone, dict_fixup_clone_t, common.entry);
588
589
26
  fctx->pool = talloc_pool(ctx, DICT_FIXUP_POOL_SIZE);
590
26
  if (!fctx->pool) return -1;
591
592
26
  return 0;
593
26
}
594
595
/** Apply all outstanding fixes to a set of dictionaries
596
 *
597
 */
598
int dict_fixup_apply(dict_fixup_ctx_t *fctx)
599
34
{
600
601
102
#define APPLY_FIXUP(_fctx, _list, _func, _type) \
602
102
do { \
603
102
  _type *_fixup; \
604
130
  while ((_fixup = fr_dlist_head(&(_fctx)->_list))) { \
605
28
    if (_func(_fctx, _fixup) < 0) return -1; \
606
28
    fr_dlist_remove(&(_fctx)->_list, _fixup); \
607
28
    talloc_free(_fixup); \
608
28
  } \
609
102
} while (0)
610
611
  /*
612
   *  Apply all the fctx in order
613
   *
614
   *  - Enumerations first as they have no dependencies
615
   *  - Group references next, as group attributes may be cloned.
616
   *  - Clones last as all other references and additions should
617
   *    be applied before cloning.
618
   *  - Hash table fctx last.
619
   */
620
34
  APPLY_FIXUP(fctx, enumv, dict_fixup_enumv_apply, dict_fixup_enumv_t);
621
34
  APPLY_FIXUP(fctx, group, dict_fixup_group_apply, dict_fixup_group_t);
622
34
  APPLY_FIXUP(fctx, clone, dict_fixup_clone_apply, dict_fixup_clone_t);
623
624
34
  TALLOC_FREE(fctx->pool);
625
626
34
  return 0;
627
34
}
628
629
/** Fixup all hash tables in the dictionary so they're suitable for threaded access
630
 *
631
 */
632
static int _dict_attr_fixup_hash_tables(fr_dict_attr_t const *da, UNUSED void *uctx)
633
0
{
634
0
  {
635
0
    fr_dict_attr_ext_enumv_t *ext;
636
637
0
    ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_ENUMV);
638
0
    if (ext) {
639
0
      if (ext->value_by_name) fr_hash_table_fill(ext->value_by_name);
640
0
      if (ext->name_by_value) fr_hash_table_fill(ext->name_by_value);
641
0
    }
642
0
  }
643
644
0
  {
645
0
    fr_hash_table_t *hash;
646
647
0
    hash = dict_attr_namespace(da);
648
0
    if (hash) fr_hash_table_fill(hash);
649
0
  }
650
651
0
  return 0;
652
0
}
653
654
/** Walk a dictionary finalising the hash tables in all attributes with a distinct namespace
655
 *
656
 * @param[in] dict  to finalise namespaces for.
657
 */
658
void dict_hash_tables_finalise(fr_dict_t *dict)
659
0
{
660
0
  fr_dict_attr_t *root = fr_dict_attr_unconst(fr_dict_root(dict));
661
662
0
  (void)_dict_attr_fixup_hash_tables(root, NULL);
663
664
0
  fr_dict_walk(root, _dict_attr_fixup_hash_tables, NULL);
665
666
  /*
667
   *  Walk over all of the hash tables to ensure they're
668
   *  initialized.  We do this because the threads may perform
669
   *  lookups, and we don't want multi-threaded re-ordering
670
   *  of the table entries.  That would be bad.
671
   */
672
0
  fr_hash_table_fill(dict->vendors_by_name);
673
0
  fr_hash_table_fill(dict->vendors_by_num);
674
0
}