diff options
author | Volker Lendecke <vl@samba.org> | 2010-03-13 19:05:38 +0100 |
---|---|---|
committer | Volker Lendecke <vl@samba.org> | 2010-03-13 20:20:37 +0100 |
commit | cfc44d244152609e17a26db85bbbf827955958a7 (patch) | |
tree | 4c01b3f7ec585f7d06895b69b50164de16041b9d /source3 | |
parent | 5eeb1fc44737a3b788fb84945966ded108caf3cf (diff) | |
download | samba-cfc44d244152609e17a26db85bbbf827955958a7.tar.gz samba-cfc44d244152609e17a26db85bbbf827955958a7.tar.bz2 samba-cfc44d244152609e17a26db85bbbf827955958a7.zip |
s3: Make tdb_wrap_open more robust
This hides the use of talloc_reference from the caller, making it impossible to
wrongly call talloc_free() on the result.
Diffstat (limited to 'source3')
-rw-r--r-- | source3/include/util_tdb.h | 2 | ||||
-rw-r--r-- | source3/lib/util_tdb.c | 128 |
2 files changed, 87 insertions, 43 deletions
diff --git a/source3/include/util_tdb.h b/source3/include/util_tdb.h index 80b95921d7..9666fc979a 100644 --- a/source3/include/util_tdb.h +++ b/source3/include/util_tdb.h @@ -28,8 +28,6 @@ struct tdb_wrap { struct tdb_context *tdb; - const char *name; - struct tdb_wrap *next, *prev; }; int tdb_chainlock_with_timeout( struct tdb_context *tdb, TDB_DATA key, diff --git a/source3/lib/util_tdb.c b/source3/lib/util_tdb.c index af0573e68e..fe2f231a71 100644 --- a/source3/lib/util_tdb.c +++ b/source3/lib/util_tdb.c @@ -523,75 +523,121 @@ static void tdb_wrap_log(TDB_CONTEXT *tdb, enum tdb_debug_level level, } } -static struct tdb_wrap *tdb_list; +struct tdb_wrap_private { + struct tdb_context *tdb; + const char *name; + struct tdb_wrap_private *next, *prev; +}; + +static struct tdb_wrap_private *tdb_list; /* destroy the last connection to a tdb */ -static int tdb_wrap_destructor(struct tdb_wrap *w) +static int tdb_wrap_private_destructor(struct tdb_wrap_private *w) { tdb_close(w->tdb); DLIST_REMOVE(tdb_list, w); return 0; } -/* - wrapped connection to a tdb database - to close just talloc_free() the tdb_wrap pointer - */ -struct tdb_wrap *tdb_wrap_open(TALLOC_CTX *mem_ctx, - const char *name, int hash_size, int tdb_flags, - int open_flags, mode_t mode) +static struct tdb_wrap_private *tdb_wrap_private_open(TALLOC_CTX *mem_ctx, + const char *name, + int hash_size, + int tdb_flags, + int open_flags, + mode_t mode) { - struct tdb_wrap *w; + struct tdb_wrap_private *result; struct tdb_logging_context log_ctx; - log_ctx.log_fn = tdb_wrap_log; - - if (!lp_use_mmap()) - tdb_flags |= TDB_NOMMAP; - - for (w=tdb_list;w;w=w->next) { - if (strcmp(name, w->name) == 0) { - /* - * Yes, talloc_reference is exactly what we want - * here. Otherwise we would have to implement our own - * reference counting. - */ - return talloc_reference(mem_ctx, w); - } - } - w = talloc(mem_ctx, struct tdb_wrap); - if (w == NULL) { + result = talloc(mem_ctx, struct tdb_wrap_private); + if (result == NULL) { return NULL; } + result->name = talloc_strdup(result, name); + if (result->name == NULL) { + goto fail; + } - if (!(w->name = talloc_strdup(w, name))) { - talloc_free(w); - return NULL; + log_ctx.log_fn = tdb_wrap_log; + + if (!lp_use_mmap()) { + tdb_flags |= TDB_NOMMAP; } if ((hash_size == 0) && (name != NULL)) { - const char *base = strrchr_m(name, '/'); + const char *base; + base = strrchr_m(name, '/'); + if (base != NULL) { base += 1; - } - else { + } else { base = name; } hash_size = lp_parm_int(-1, "tdb_hashsize", base, 0); } - w->tdb = tdb_open_ex(name, hash_size, tdb_flags, - open_flags, mode, &log_ctx, NULL); - if (w->tdb == NULL) { - talloc_free(w); - return NULL; + result->tdb = tdb_open_ex(name, hash_size, tdb_flags, + open_flags, mode, &log_ctx, NULL); + if (result->tdb == NULL) { + goto fail; } + talloc_set_destructor(result, tdb_wrap_private_destructor); + DLIST_ADD(tdb_list, result); + return result; + +fail: + TALLOC_FREE(result); + return NULL; +} + +/* + wrapped connection to a tdb database + to close just talloc_free() the tdb_wrap pointer + */ +struct tdb_wrap *tdb_wrap_open(TALLOC_CTX *mem_ctx, + const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode) +{ + struct tdb_wrap *result; + struct tdb_wrap_private *w; - talloc_set_destructor(w, tdb_wrap_destructor); + result = talloc(mem_ctx, struct tdb_wrap); + if (result == NULL) { + return NULL; + } - DLIST_ADD(tdb_list, w); + for (w=tdb_list;w;w=w->next) { + if (strcmp(name, w->name) == 0) { + break; + } + } - return w; + if (w == NULL) { + w = tdb_wrap_private_open(result, name, hash_size, tdb_flags, + open_flags, mode); + } else { + /* + * Correctly use talloc_reference: The tdb will be + * closed when "w" is being freed. The caller never + * sees "w", so an incorrect use of talloc_free(w) + * instead of calling talloc_unlink is not possible. + * To avoid having to refcount ourselves, "w" will + * have multiple parents that hang off all the + * tdb_wrap's being returned from here. Those parents + * can be freed without problem. + */ + if (talloc_reference(result, w) == NULL) { + goto fail; + } + } + if (w == NULL) { + goto fail; + } + result->tdb = w->tdb; + return result; +fail: + TALLOC_FREE(result); + return NULL; } NTSTATUS map_nt_error_from_tdb(enum TDB_ERROR err) |