};
static size_t dict_attr_ext_table_len = NUM_ELEMENTS(dict_attr_ext_table);
+/** Fixup name pointer on realloc
+ *
+ */
+static int fr_dict_attr_ext_name_fixup(UNUSED int ext,
+ TALLOC_CTX *chunk,
+ void *ext_ptr, UNUSED size_t ext_ptr_len)
+{
+ fr_dict_attr_t *da = talloc_get_type_abort(chunk, fr_dict_attr_t);
+
+ da->name = ext_ptr;
+
+ return 0;
+}
+
/** Copy all enumeration values from one attribute to another
*
*/
-static void *fr_dict_attr_ext_enumv_copy(void **chunk_p, int ext, void *ext_ptr, UNUSED size_t ext_len)
+static int fr_dict_attr_ext_enumv_copy(UNUSED int ext,
+ TALLOC_CTX *chunk,
+ void *dst_ext_ptr, UNUSED size_t dst_ext_len,
+ void *src_ext_ptr, UNUSED size_t src_ext_len)
{
- fr_dict_attr_t **da_p = (fr_dict_attr_t **)chunk_p;
- fr_dict_attr_ext_enumv_t *new_ext, *old_ext;
+ fr_dict_attr_t *da_src = talloc_get_type_abort(chunk, fr_dict_attr_t);
+ fr_dict_attr_ext_enumv_t *dst_ext = dst_ext_ptr, *src_ext = src_ext_ptr;
fr_hash_iter_t iter;
fr_dict_enum_t *enumv;
- bool has_child = fr_dict_attr_is_key_field(*da_p);
+ bool has_child = fr_dict_attr_is_key_field(da_src);
- new_ext = dict_attr_ext_alloc(da_p, ext);
- if (!new_ext) return NULL;
-
- old_ext = ext_ptr;
-
- if (!old_ext->value_by_name && !old_ext->name_by_value) {
- memset(new_ext, 0, sizeof(*new_ext));
- return new_ext;
+ if (!src_ext->value_by_name && !src_ext->name_by_value) {
+ memset(dst_ext, 0, sizeof(*dst_ext));
+ return 0;
}
/*
* Add all the enumeration values from
* the old attribute to the new attribute.
*/
- for (enumv = fr_hash_table_iter_init(old_ext->value_by_name, &iter);
+ for (enumv = fr_hash_table_iter_init(src_ext->value_by_name, &iter);
enumv;
- enumv = fr_hash_table_iter_next(old_ext->value_by_name, &iter)) {
+ enumv = fr_hash_table_iter_next(src_ext->value_by_name, &iter)) {
fr_dict_attr_t const *child_struct;
if (!has_child) {
/*
* Fixme - Child struct copying is probably wrong
*/
- if (dict_attr_enum_add_name(*da_p, enumv->name, enumv->value, true, true, child_struct) < 0) {
- return NULL;
- }
+ if (dict_attr_enum_add_name(da_src, enumv->name, enumv->value,
+ true, true, child_struct) < 0) return -1;
}
- return new_ext;
+ return 0;
}
/** Rediscover the parent of this attribute, and cache it
*
*/
-static void *fr_dict_attr_ext_vendor_copy(void **chunk_p, int ext, void *ext_ptr, UNUSED size_t ext_len)
+static int fr_dict_attr_ext_vendor_copy(UNUSED int ext,
+ TALLOC_CTX *chunk,
+ void *dst_ext_ptr, UNUSED size_t dst_ext_len,
+ void *src_ext_ptr, UNUSED size_t src_ext_len)
{
- fr_dict_attr_t **da_p = (fr_dict_attr_t **)chunk_p;
- fr_dict_attr_ext_vendor_t *new_ext, *old_ext = ext_ptr;
+ fr_dict_attr_t *da_src = talloc_get_type_abort(chunk, fr_dict_attr_t);
+ fr_dict_attr_ext_vendor_t *dst_ext = dst_ext_ptr, *src_ext = src_ext_ptr;
fr_dict_attr_t const **da_stack;
- fr_dict_attr_t const *old_vendor = old_ext->vendor;
+ fr_dict_attr_t const *old_vendor = src_ext->vendor;
fr_dict_attr_t const *new_vendor, *da;
+ if (!old_vendor) {
+ dst_ext->vendor = NULL;
+ return 0;
+ }
+
/*
* If we have a da stack, see if we can
* find a vendor at the same depth as
* the old depth.
*/
- da_stack = fr_dict_attr_da_stack(*da_p);
+ da_stack = fr_dict_attr_da_stack(da_src);
if (da_stack) {
new_vendor = da_stack[old_vendor->depth];
if ((new_vendor->type == old_vendor->type) && (new_vendor->attr == old_vendor->attr)) {
- new_ext = dict_attr_ext_alloc(da_p, ext);
- if (!new_ext) return NULL;
-
- new_ext->vendor = new_vendor;
- return new_ext;
+ dst_ext->vendor = new_vendor;
+ return 0;
}
}
* Theoretically the attribute could
* have been moved to a different depth.
*/
- for (da = (*da_p)->parent; da; da = da->parent) {
+ for (da = da_src->parent; da; da = da->parent) {
if ((da->type == old_vendor->type) && (da->attr == old_vendor->attr)) {
- new_ext = dict_attr_ext_alloc(da_p, ext);
- if (!new_ext) return NULL;
-
- new_ext->vendor = da;
- return new_ext;
+ dst_ext->vendor = da;
+ return 0;
}
}
- return NULL;
+ return -1;
}
/** Holds additional information about extension structures
[FR_DICT_ATTR_EXT_NAME] = {
.min = sizeof(char),
.has_hdr = true,
+ .fixup = fr_dict_attr_ext_name_fixup,
.can_copy = false, /* Name may change, and we can only set it once */
},
[FR_DICT_ATTR_EXT_CHILDREN] = {
#include <freeradius-devel/util/table.h>
#include <freeradius-devel/util/talloc.h>
-#define CHUNK_EXT_PTR(_chunk, _chunk_ext, _ext) ((void *)((chunk_ext[_ext] * FR_EXT_ALIGNMENT) + ((uintptr_t)(_chunk))))
-#define CHUNK_EXT(_chunk, _offset) ((uint8_t *)(((uintptr_t)_chunk) + (_offset)))
-
/** Add a variable length extension to a talloc chunk
*
* This is used to build a structure from a primary struct type and one or more
size_t offset;
- fr_ext_info_t const *info;
+ fr_ext_info_t const *info = &def->info[ext];
void *n_chunk, *chunk = *chunk_p;
- uint8_t *chunk_ext;
+ uint8_t *ext_offsets;
uint8_t *ext_ptr;
char const *type;
- chunk_ext = CHUNK_EXT(*chunk_p, def->offset_of_exts);
- if (chunk_ext[ext]) return CHUNK_EXT_PTR(*chunk_p, chunk_ext, ext);
+ ext_offsets = fr_ext_offsets(def, *chunk_p);
+ if (ext_offsets[ext]) return fr_ext_ptr(*chunk_p, ext_offsets[ext], info->has_hdr);
- info = &def->info[ext];
- if (info->has_hdr) hdr_len = sizeof(dict_ext_hdr_t); /* Add space for a length prefix */
+ if (info->has_hdr) hdr_len = sizeof(fr_ext_hdr_t); /* Add space for a length prefix */
/*
* Packing the offsets into a uint8_t array
* UINT8_MAX * FR_EXT_ALIGNMENT.
*/
chunk_len = talloc_get_size(chunk);
- offset = (chunk_len + hdr_len) / FR_EXT_ALIGNMENT;
+ offset = ROUND_UP_DIV(chunk_len, FR_EXT_ALIGNMENT);
if (unlikely(offset > UINT8_MAX)) {
fr_strerror_printf("Insufficient space remaining for extensions");
return NULL;
* record it and set it back again.
*/
type = talloc_get_name(chunk);
- n_chunk = talloc_realloc_size(NULL, chunk, chunk_len + hdr_len + aligned_len);
+ n_chunk = talloc_realloc_size(NULL, chunk, (offset * FR_EXT_ALIGNMENT) + hdr_len + aligned_len);
if (!n_chunk) {
fr_strerror_printf("Failed reallocing %s (%s). Tried to realloc %zu bytes -> %zu bytes",
type, fr_syserror(errno), chunk_len, chunk_len + aligned_len);
return NULL;
}
talloc_set_name_const(n_chunk, type);
- *chunk_p = n_chunk;
- chunk_ext = CHUNK_EXT(*chunk_p, def->offset_of_exts);
- chunk_ext[ext] = (uint8_t)offset;
+ ext_offsets = fr_ext_offsets(def, n_chunk);
+ ext_offsets[ext] = (uint8_t)offset;
ext_ptr = ((uint8_t *)n_chunk) + chunk_len;
+ *chunk_p = n_chunk;
+
if (info->has_hdr) {
- dict_ext_hdr_t *ext_hdr = (dict_ext_hdr_t *)ext_ptr;
+ fr_ext_hdr_t *ext_hdr = (fr_ext_hdr_t *)ext_ptr;
ext_hdr->len = ext_len; /* Record the real size */
return &ext_hdr->data; /* Pointer to the data portion */
* - 0 if no extension exists or is of zero length.
* - >0 the length of the extension.
*/
-size_t fr_ext_len(fr_ext_t const *def, void const *chunk, int ext)
+size_t fr_ext_len(fr_ext_t const *def, TALLOC_CTX const *chunk, int ext)
{
uint8_t offset;
fr_ext_info_t const *info;
- dict_ext_hdr_t *ext_hdr;
- uint8_t *chunk_ext;
+ fr_ext_hdr_t *ext_hdr;
+ uint8_t *ext_offsets;
- chunk_ext = CHUNK_EXT(chunk, def->offset_of_exts);
- offset = chunk_ext[ext];
+ ext_offsets = fr_ext_offsets(def, chunk);
+ offset = ext_offsets[ext];
if (!offset) return 0;
info = &def->info[ext];
- if (!info->has_hdr) return info->min; /* Fixed size */
+ if (!info->has_hdr) return info->min; /* Fixed size */
- ext_hdr = (dict_ext_hdr_t *)((uintptr_t)chunk) + ((offset * FR_EXT_ALIGNMENT) - sizeof(dict_ext_hdr_t));
+ ext_hdr = fr_ext_ptr(chunk, offset, false); /* false as we're getting the header */
return ext_hdr->len;
}
/** Copy extension data from one attribute to another
*
* @param[in] def Extension definitions.
- * @param[in,out] chunk_out to copy extension to.
- * Under certain circumstances the value of *chunk_out will
+ * @param[in,out] chunk_dst to copy extension to.
+ * Under certain circumstances the value of *chunk_dst will
* be changed to point to a new memory block.
* All cached copies of the previous pointer should be
* updated.
- * @param[in] chunk_in to copy extension from.
+ * @param[in] chunk_src to copy extension from.
* @param[in] ext to copy.
* @return
* - NULL if we failed to allocate an extension structure.
* - A pointer to the offset of the extension in da_out.
*/
-void *fr_ext_copy(fr_ext_t const *def, void **chunk_out, void const *chunk_in, int ext)
+void *fr_ext_copy(fr_ext_t const *def, TALLOC_CTX **chunk_dst, TALLOC_CTX const *chunk_src, int ext)
{
- void *ext_ptr, *new_ext_ptr;
- uint8_t *chunk_ext;
- size_t ext_len;
+ int i;
+ uint8_t *ext_src_offsets = fr_ext_offsets(def, chunk_src);
+ uint8_t *ext_dst_offsets;
+ void *ext_src_ptr, *ext_dst_ptr;
+ fr_ext_info_t const *info = &def->info[ext];
- fr_ext_info_t const *info;
-
- info = &def->info[ext];
if (!info->can_copy) {
fr_strerror_printf("Extension cannot be copied");
return NULL;
}
- chunk_ext = CHUNK_EXT(chunk_in, def->offset_of_exts);
- ext_ptr = CHUNK_EXT_PTR(chunk_in, chunk_ext, ext);
- if (!ext_ptr) return NULL;
+ if (!ext_src_offsets[ext]) return NULL;
- ext_len = fr_ext_len(def, chunk_in, ext);
+ ext_src_ptr = fr_ext_ptr(chunk_src, ext_src_offsets[ext], info->has_hdr);
+
+ if (info->alloc) {
+ ext_dst_ptr = info->alloc(def, chunk_dst, ext,
+ ext_src_ptr,
+ fr_ext_len(def, chunk_src, ext));
/*
- * Use the special copy function.
- * Its responsible for allocating the extension in the
- * destination attribute.
+ * If there's no special alloc function
+ * we just allocate a chunk of the same
+ * size.
*/
- if (info->copy) return info->copy(chunk_out, ext, ext_ptr, ext_len);
+ } else {
+ ext_dst_ptr = fr_ext_alloc_size(def, chunk_dst, ext,
+ fr_ext_len(def, chunk_src, ext));
+ }
+ if (info->copy) {
+ info->copy(ext,
+ *chunk_dst,
+ ext_dst_ptr, fr_ext_len(def, *chunk_dst, ext),
+ ext_src_ptr, fr_ext_len(def, chunk_src, ext));
/*
- * If there's no special function
- * just memcpy the data over.
+ * If there's no special copy function
+ * we just copy the data from the old
+ * extension to the new one.
*/
- new_ext_ptr = fr_ext_alloc_size(def, chunk_out, ext, ext_len);
- if (!new_ext_ptr) return NULL;
- memcpy(new_ext_ptr, ext_ptr, ext_len);
+ } else {
+ memcpy(ext_dst_ptr, ext_src_ptr, fr_ext_len(def, *chunk_dst, ext));
+ }
+
+ /*
+ * Call any fixup functions
+ */
+ ext_dst_offsets = fr_ext_offsets(def, *chunk_dst);
+ for (i = 0; i < def->max; i++) {
+ if (i == ext) continue;
+
+ if (!ext_dst_offsets[i]) continue;
+
+ if (info->fixup &&
+ info->fixup(i, *chunk_dst,
+ fr_ext_ptr(*chunk_dst, ext_dst_offsets[i], info->has_hdr),
+ fr_ext_len(def, *chunk_dst, i)) < 0) return NULL;
+ }
- return new_ext_ptr;
+ return ext_dst_ptr;
}
/** Copy all the extensions from one attribute to another
*
* @param[in] def Extension definitions.
- * @param[in,out] chunk_out to copy extensions to.
- * Under certain circumstances the value of *chunk_out will
+ * @param[in,out] chunk_dst to copy extensions to.
+ * Under certain circumstances the value of *chunk_dst will
* be changed to point to a new memory block.
* All cached copies of the previous pointer should be
* updated.
- * @param[in] chunk_in to copy extensions from.
+ * @param[in] chunk_src to copy extensions from.
* @return
* - 0 on success.
* - -1 if a copy operation failed.
*/
-int fr_ext_copy_all(fr_ext_t const *def, void **chunk_out, void const *chunk_in)
+int fr_ext_copy_all(fr_ext_t const *def, TALLOC_CTX **chunk_dst, TALLOC_CTX const *chunk_src)
{
int i;
- uint8_t *ext_in = CHUNK_EXT(chunk_in, def->offset_of_exts);
+ uint8_t *ext_src_offsets = fr_ext_offsets(def, chunk_src); /* old chunk array */
+ uint8_t *ext_dst_offsets = fr_ext_offsets(def, *chunk_dst); /* new chunk array */
+ bool ext_copied[def->max];
+ /*
+ * Do the operation in two phases.
+ *
+ * Phase 1 allocates space for all the extensions.
+ */
+ for (i = 0; i < def->max; i++) {
+ fr_ext_info_t const *info = &def->info[i];
+
+ if (!ext_src_offsets[i] || ext_dst_offsets[i] || !info->can_copy) {
+ no_copy:
+ ext_copied[i] = false;
+ continue;
+ }
+
+ if (info->alloc) {
+ if (!info->alloc(def, chunk_dst, i,
+ fr_ext_ptr(chunk_src, ext_src_offsets[i], info->has_hdr),
+ fr_ext_len(def, chunk_src, i))) goto no_copy;
+ /*
+ * If there's no special alloc function
+ * we just allocate a chunk of the same
+ * size.
+ */
+ } else {
+ fr_ext_alloc_size(def, chunk_dst, i, fr_ext_len(def, chunk_src, i));
+ }
+ ext_copied[i] = true;
+ ext_dst_offsets = fr_ext_offsets(def, *chunk_dst); /* Grab new offsets, chunk might have changed */
+ }
+
+ /*
+ * Phase 2 populates the extension memory.
+ *
+ * We do this in two phases to avoid invalidating
+ * any pointers from extensions back to the extended
+ * talloc chunk.
+ */
for (i = 0; i < def->max; i++) {
- if (!ext_in[i] || !def->info[i].can_copy) continue;
- if (!fr_ext_copy(def, chunk_out, chunk_in, i)) return -1;
+ fr_ext_info_t const *info = &def->info[i];
+
+ if (!ext_src_offsets[i] || !ext_dst_offsets[i]) continue;
+
+ if (!ext_copied[i]) {
+ if (info->fixup &&
+ info->fixup(i, *chunk_dst,
+ fr_ext_ptr(*chunk_dst, ext_dst_offsets[i], info->has_hdr),
+ fr_ext_len(def, *chunk_dst, i)) < 0) return -1;
+ continue;
+ }
+ if (!info->can_copy) continue;
+
+ if (info->copy) {
+ if (info->copy(i,
+ *chunk_dst,
+ fr_ext_ptr(*chunk_dst, ext_dst_offsets[i], info->has_hdr),
+ fr_ext_len(def, *chunk_dst, i),
+ fr_ext_ptr(chunk_src, ext_src_offsets[i], info->has_hdr),
+ fr_ext_len(def, chunk_src, i)) < 0) return -1;
+ /*
+ * If there's no special copy function
+ * we just copy the data from the old
+ * extension to the new one.
+ */
+ } else {
+ memcpy(fr_ext_ptr(*chunk_dst, ext_dst_offsets[i], info->has_hdr),
+ fr_ext_ptr(chunk_src, ext_src_offsets[i], info->has_hdr),
+ fr_ext_len(def, *chunk_dst, i));
+ }
}
return 0;
FR_FAULT_LOG("%s ext total_len=%zu", name, talloc_get_size(chunk));
for (i = 0; i < (int)def->max; i++) {
- uint8_t *chunk_ext = CHUNK_EXT(chunk, def->offset_of_exts);
- if (chunk_ext[i]) {
- void *ext = CHUNK_EXT_PTR(chunk, chunk_ext, i);
+ uint8_t *ext_offsets = fr_ext_offsets(def, chunk);
+ if (ext_offsets[i]) {
+ void *ext = fr_ext_ptr(chunk, ext_offsets[i], def[i].info->has_hdr);
size_t ext_len = fr_ext_len(def, chunk, i);
-
char const *ext_name = fr_table_ordered_str_by_num(def->name_table,
*def->name_table_len,
i, "<INVALID>");
# define FR_EXT_ALIGNMENT sizeof(uint64_t)
#endif
-/** Function for fixing up extensions after they're copied
+typedef struct fr_ext_s fr_ext_t;
+
+/** Function for pre-allocating extension memory for extensions before they're copied
+ *
+ * @param[in] def Extension definitions.
+ * @param[in,out] dst_chunk_p to add extensions to.
+ * @param[in] ext that's being copied.
+ * @param[in] src_ext_ptr Pointer for the src extension.
+ * @param[in] src_ext_len Length of the src extension.
+ * @return
+ * - NULL on error.
+ * - Pointer to the new extension on success.
+ */
+typedef void *(* fr_ext_alloc_t)(fr_ext_t const *def, TALLOC_CTX **dst_chunk_p,
+ int ext, void *src_ext_ptr, size_t src_ext_len);
+
+/** Function for re-populating extensions after they're copied
+ *
+ * @param[in] ext that's being copied.
+ * @param[in] chunk Talloc chunk we're copying to.
+ * @param[in] dst_ext_ptr Pointer to the dst extension to populate.
+ * @param[in] dst_ext_len The length of the dst extension.
+ * @param[in] src_ext_ptr Pointer for the src extension.
+ * @param[in] src_ext_len Length of the src extension.
+ * @return
+ * - NULL on error.
+ * - Pointer to the new extension on success.
+ */
+typedef int (* fr_ext_copy_t)(int ext, TALLOC_CTX *chunk,
+ void *dst_ext_ptr, size_t dst_ext_len,
+ void *src_ext_ptr, size_t src_ext_len);
+
+/** Function for re-establishing internal consistency on realloc
+ *
+ * In some cases the chunk may cache a pointer to an extension.
+ * On realloc this pointer may be invalidated. This provides a
+ * callback to fixup consistency issues after a realloc.
*
+ * @param[in] ext that's being copied.
+ * @param[in] chunk Talloc chunk.
+ * @param[in] ext_ptr Pointer to the extension to fixup.
+ * @param[in] ext_len The length of the extension to fixup.
+ * @return
+ * - NULL on error.
+ * - Pointer to the new extension on success.
*/
-typedef void *(* fr_ext_copy_t)(void **chunk_p, int ext, void *ext_ptr, size_t ext_len);
+typedef int (* fr_ext_fixup_t)(int ext, TALLOC_CTX *chunk,
+ void *ext_ptr, size_t ext_len);
/** Additional information for a given extension
*/
///< the extension data to record the exact length
///< of the extension.
bool can_copy; //!< Copying this extension between structs is allowed.
+
+ fr_ext_alloc_t alloc; //!< Override the normal alloc operation with a callback.
fr_ext_copy_t copy; //!< Override the normal copy operation with a callback.
+ fr_ext_fixup_t fixup; //!< Callback for fixing up internal consistency issues.
} fr_ext_info_t;
/** Structure to define a set of extensions
*
*/
-typedef struct {
+struct fr_ext_s {
size_t offset_of_exts; //!< Where in the extended struct the extensions array starts.
fr_table_num_ordered_t const *name_table; //!< String identifiers for the extensions.
size_t *name_table_len; //!< How many extensions there are in the table.
int max; //!< The highest extension value.
fr_ext_info_t const info[]; //!< Additional information about each extension.
-} fr_ext_t;
+};
/** Optional extension header struct
*
typedef struct {
size_t len; //!< Length of extension data.
uint8_t data[]; //!< Extension data
-} CC_HINT(aligned(FR_EXT_ALIGNMENT)) dict_ext_hdr_t;
+} CC_HINT(aligned(FR_EXT_ALIGNMENT)) fr_ext_hdr_t;
+
+static inline CC_HINT(always_inline) uint8_t *fr_ext_offsets(fr_ext_t const *def, TALLOC_CTX const *chunk)
+{
+ return (uint8_t *)(((uintptr_t)chunk) + def->offset_of_exts);
+}
-/** Return a pointer to the specified extension structure
+/** Return a pointer to an extension in a chunk
*
- * @param[in] _ptr to fetch extension for.
- * @param[in] _field Array of extensions.
- * @param[in] _ext to retrieve.
*/
-#define FR_EXT_PTR(_ptr, _field, _ext) ((void *)(((_ptr)->_field[_ext] * FR_EXT_ALIGNMENT) + ((uintptr_t)(_ptr))))
+static inline CC_HINT(always_inline) void *fr_ext_ptr(TALLOC_CTX const *chunk, size_t offset, bool has_hdr)
+{
+ uintptr_t out;
+
+ out = (uintptr_t)chunk; /* chunk start */
+ out += offset * FR_EXT_ALIGNMENT; /* offset described by the extension */
+ out += sizeof(fr_ext_hdr_t) * (has_hdr == true); /* data field offset by length header */
+
+ return (void *)out;
+}
-void *fr_ext_alloc_size(fr_ext_t const *def, void **chunk_p, int ext, size_t ext_len);
+void *fr_ext_alloc_size(fr_ext_t const *def, TALLOC_CTX **chunk_p, int ext, size_t ext_len);
-size_t fr_ext_len(fr_ext_t const *def, void const *chunk_in, int ext);
+size_t fr_ext_len(fr_ext_t const *def, TALLOC_CTX const *chunk_in, int ext);
-void *fr_ext_copy(fr_ext_t const *def, void **chunk_out, void const *chunk_in, int ext);
+void *fr_ext_copy(fr_ext_t const *def, TALLOC_CTX **chunk_out, TALLOC_CTX const *chunk_in, int ext);
-int fr_ext_copy_all(fr_ext_t const *def, void **chunk_out, void const *chunk_in);
+int fr_ext_copy_all(fr_ext_t const *def, TALLOC_CTX **chunk_out, TALLOC_CTX const *chunk_in);
-void fr_ext_debug(fr_ext_t const *def, char const *name, void const *chunk);
+void fr_ext_debug(fr_ext_t const *def, char const *name, TALLOC_CTX const *chunk);
#ifdef __cplusplus
}