diff options
Diffstat (limited to 'source4/lib/talloc.c')
-rw-r--r-- | source4/lib/talloc.c | 131 |
1 files changed, 75 insertions, 56 deletions
diff --git a/source4/lib/talloc.c b/source4/lib/talloc.c index 4a48347832..31b6584845 100644 --- a/source4/lib/talloc.c +++ b/source4/lib/talloc.c @@ -27,17 +27,32 @@ #include "includes.h" #define MAX_TALLOC_SIZE 0x10000000 -#define TALLOC_MAGIC 0x14082004 -#define TALLOC_MAGIC_FREE 0x3421abcd +#define TALLOC_MAGIC 0xe814ec4f +#define TALLOC_MAGIC_FREE 0x7faebef3 struct talloc_chunk { struct talloc_chunk *next, *prev; struct talloc_chunk *parent, *child; size_t size; uint_t magic; + uint_t ref_count; + int (*destructor)(void *); char *name; }; +/* panic if we get a bad magic value */ +static struct talloc_chunk *talloc_chunk_from_ptr(void *ptr) +{ + struct talloc_chunk *tc = ((struct talloc_chunk *)ptr)-1; + if (tc->magic != TALLOC_MAGIC) { + if (tc->magic == TALLOC_MAGIC_FREE) { + smb_panic("Bad talloc magic value - double free\n"); + } else { + smb_panic("Bad talloc magic value\n"); + } + } + return tc; +} /* Allocate a bit of memory as a child of an existing pointer @@ -57,18 +72,13 @@ void *talloc(void *context, size_t size) tc->size = size; tc->magic = TALLOC_MAGIC; + tc->ref_count = 1; + tc->destructor = NULL; tc->child = NULL; tc->name = NULL; if (context) { - struct talloc_chunk *parent = ((struct talloc_chunk *)context)-1; - - if (parent->magic != TALLOC_MAGIC) { - DEBUG(0,("Bad magic in context - 0x%08x\n", parent->magic)); - free(tc); - smb_panic("Bad magic in talloc context"); - return NULL; - } + struct talloc_chunk *parent = talloc_chunk_from_ptr(context); tc->parent = parent; @@ -86,17 +96,35 @@ void *talloc(void *context, size_t size) /* - add a name to an existing pointer - va_list version + setup a destructor to be called on free of a pointer + the destructor should return 0 on success, or -1 on failure. + if the destructor fails then the free is failed, and the memory can + be continued to be used */ -static void talloc_set_name_v(void *ptr, const char *fmt, va_list ap) +void talloc_set_destructor(void *ptr, int (*destructor)(void *)) { - struct talloc_chunk *tc; + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->destructor = destructor; +} + +/* + increase the reference count on a piece of memory. To decrease the + reference count call talloc_free(), which will free the memory if + the reference count reaches zero +*/ +void talloc_increase_ref_count(void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->ref_count++; +} - tc = ((struct talloc_chunk *)ptr)-1; - if (tc->magic != TALLOC_MAGIC) { - return; - } +/* + add a name to an existing pointer - va_list version +*/ +static void talloc_set_name_v(void *ptr, const char *fmt, va_list ap) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); vasprintf(&tc->name, fmt, ap); } @@ -141,17 +169,14 @@ void *talloc_init(const char *fmt, ...) _PRINTF_ATTRIBUTE(1,2) { va_list ap; void *ptr; - struct talloc_chunk *tc; ptr = talloc(NULL, 0); if (ptr == NULL) { return NULL; } - tc = ((struct talloc_chunk *)ptr)-1; - va_start(ap, fmt); - vasprintf(&tc->name, fmt, ap); + talloc_set_name_v(ptr, fmt, ap); va_end(ap); return ptr; @@ -162,23 +187,36 @@ void *talloc_init(const char *fmt, ...) _PRINTF_ATTRIBUTE(1,2) /* free a talloc pointer. This also frees all child pointers of this pointer recursively + + return 0 if the memory is actually freed, otherwise -1. The memory + will not be freed if the ref_count is > 1 or the destructor (if + any) returns non-zero */ -void talloc_free(void *ptr) +int talloc_free(void *ptr) { struct talloc_chunk *tc; - if (ptr == NULL) return; + if (ptr == NULL) { + return -1; + } - tc = ((struct talloc_chunk *)ptr)-1; + tc = talloc_chunk_from_ptr(ptr); - if (tc->magic != TALLOC_MAGIC) { - DEBUG(0,("Bad talloc magic 0x%08x in talloc_free\n", tc->magic)); - smb_panic("Bad talloc magic in talloc_realloc"); - return; + tc->ref_count--; + if (tc->ref_count != 0) { + return -1; + } + + if (tc->destructor && tc->destructor(ptr) == -1) { + tc->ref_count++; + return -1; } while (tc->child) { - talloc_free(tc->child + 1); + if (talloc_free(tc->child + 1) != 0) { + tc->child->parent = NULL; + break; + } } if (tc->parent) { @@ -195,6 +233,7 @@ void talloc_free(void *ptr) if (tc->name) free(tc->name); free(tc); + return 0; } @@ -218,18 +257,7 @@ void *talloc_realloc(void *ptr, size_t size) return talloc(NULL, size); } - tc = ((struct talloc_chunk *)ptr)-1; - - if (tc->magic != TALLOC_MAGIC) { - if (tc->magic == TALLOC_MAGIC_FREE) { - - DEBUG(0,("Bad talloc magic - magic 0x%08x indicates double-free in talloc_realloc\n", tc->magic)); - smb_panic("Bad talloc magic - double-free - in talloc_realloc"); - } else { - DEBUG(0,("Bad talloc magic 0x%08x in talloc_realloc\n", tc->magic)); - smb_panic("Bad talloc magic in talloc_realloc"); - } - } + tc = talloc_chunk_from_ptr(ptr); /* by resetting magic we catch users of the old memory */ tc->magic = TALLOC_MAGIC_FREE; @@ -270,18 +298,11 @@ void *talloc_steal(void *new_ctx, void *ptr) return NULL; } - tc = ((struct talloc_chunk *)ptr)-1; - new_tc = ((struct talloc_chunk *)new_ctx)-1; + tc = talloc_chunk_from_ptr(ptr); + new_tc = talloc_chunk_from_ptr(new_ctx); - if (tc->magic != TALLOC_MAGIC) { - DEBUG(0,("Bad talloc magic 0x%08x in talloc_steal\n", tc->magic)); - smb_panic("Bad talloc magic in talloc_steal"); - return NULL; - } - if (new_tc->magic != TALLOC_MAGIC) { - DEBUG(0,("Bad new talloc magic 0x%08x in talloc_steal\n", new_tc->magic)); - smb_panic("Bad new talloc magic in talloc_steal"); - return NULL; + if (tc == new_tc) { + return ptr; } if (tc->parent) { @@ -304,12 +325,10 @@ void *talloc_steal(void *new_ctx, void *ptr) /* return the total size of a talloc pool (subtree) */ -off_t talloc_total_size(void *p) +off_t talloc_total_size(void *ptr) { off_t total = 0; - struct talloc_chunk *c, *tc; - - tc = ((struct talloc_chunk *)p)-1; + struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); total = tc->size; for (c=tc->child;c;c=c->next) { |