s3:registry: make regdb_fetch_keys() static
[abartlet/samba.git/.git] / source3 / registry / reg_backend_db.c
index 57d6d3980c9fc3d61d8d43bb3136fd74f8eb7634..3ac8d9459973f19281e95fcaa74254796b32e353 100644 (file)
@@ -26,6 +26,7 @@
 #include "registry.h"
 #include "reg_db.h"
 #include "reg_util_internal.h"
+#include "reg_parse_internal.h"
 #include "reg_backend_db.h"
 #include "reg_objects.h"
 #include "nt_printing.h"
@@ -37,6 +38,8 @@
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_REGISTRY
 
+#define REGDB_VERSION_KEYNAME "INFO/version"
+
 static struct db_context *regdb = NULL;
 static int regdb_refcount;
 
@@ -47,8 +50,8 @@ static bool regdb_store_keys_internal(struct db_context *db, const char *key,
                                      struct regsubkey_ctr *ctr);
 static int regdb_fetch_values_internal(struct db_context *db, const char* key,
                                       struct regval_ctr *values);
-static bool regdb_store_values_internal(struct db_context *db, const char *key,
-                                       struct regval_ctr *values);
+static NTSTATUS regdb_store_values_internal(struct db_context *db, const char *key,
+                                           struct regval_ctr *values);
 static WERROR regdb_store_subkey_list(struct db_context *db, const char *parent,
                                      const char *key);
 
@@ -57,6 +60,54 @@ static WERROR regdb_create_subkey_internal(struct db_context *db,
                                           const char *key,
                                           const char *subkey);
 
+
+struct regdb_trans_ctx {
+       NTSTATUS (*action)(struct db_context *, void *);
+       void *private_data;
+};
+
+static NTSTATUS regdb_trans_do_action(struct db_context *db, void *private_data)
+{
+       NTSTATUS status;
+       int32_t version_id;
+       struct regdb_trans_ctx *ctx = (struct regdb_trans_ctx *)private_data;
+
+       status = dbwrap_fetch_int32(db, REGDB_VERSION_KEYNAME, &version_id);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("ERROR: could not fetch registry db version: %s. "
+                         "Denying access.\n", nt_errstr(status)));
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       if (version_id != REGDB_CODE_VERSION) {
+               DEBUG(0, ("ERROR: changed registry version %d found while "
+                         "trying to write to the registry. Version %d "
+                         "expected.  Denying access.\n",
+                         version_id, REGDB_CODE_VERSION));
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       status = ctx->action(db,  ctx->private_data);
+       return status;
+}
+
+static WERROR regdb_trans_do(struct db_context *db,
+                            NTSTATUS (*action)(struct db_context *, void *),
+                            void *private_data)
+{
+       NTSTATUS status;
+       struct regdb_trans_ctx ctx;
+
+
+       ctx.action = action;
+       ctx.private_data = private_data;
+
+       status = dbwrap_trans_do(db, regdb_trans_do_action, &ctx);
+
+       return ntstatus_to_werror(status);
+}
+
 /* List the deepest path into the registry.  All part components will be created.*/
 
 /* If you want to have a part of the path controlled by the tdb and part by
@@ -218,9 +269,9 @@ WERROR init_registry_key(const char *add_path)
 
        init_ctx.add_path = add_path;
 
-       return ntstatus_to_werror(dbwrap_trans_do(regdb,
-                                                 init_registry_key_action,
-                                                 &init_ctx));
+       return regdb_trans_do(regdb,
+                             init_registry_key_action,
+                             &init_ctx);
 }
 
 /***********************************************************************
@@ -291,9 +342,12 @@ static NTSTATUS init_registry_data_action(struct db_context *db,
                {
                        regdb_ctr_add_value(values,
                                            &builtin_registry_values[i]);
-                       regdb_store_values_internal(db,
+                       status = regdb_store_values_internal(db,
                                        builtin_registry_values[i].path,
                                        values);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               goto done;
+                       }
                }
                TALLOC_FREE(values);
        }
@@ -353,9 +407,9 @@ do_init:
         * transaction behaviour.
         */
 
-       werr = ntstatus_to_werror(dbwrap_trans_do(regdb,
-                                                 init_registry_data_action,
-                                                 NULL));
+       werr = regdb_trans_do(regdb,
+                             init_registry_data_action,
+                             NULL);
 
 done:
        TALLOC_FREE(frame);
@@ -368,38 +422,50 @@ static int regdb_normalize_keynames_fn(struct db_record *rec,
        TALLOC_CTX *mem_ctx = talloc_tos();
        const char *keyname;
        NTSTATUS status;
+       TDB_DATA key;
+       TDB_DATA value;
+       struct db_context *db = (struct db_context *)private_data;
 
-       if (rec->key.dptr == NULL || rec->key.dsize == 0) {
+       key = dbwrap_record_get_key(rec);
+       if (key.dptr == NULL || key.dsize == 0) {
                return 0;
        }
 
-       keyname = strchr((const char *) rec->key.dptr, '/');
-       if (keyname) {
-               struct db_record new_rec;
+       value = dbwrap_record_get_value(rec);
+
+       if (db == NULL) {
+               DEBUG(0, ("regdb_normalize_keynames_fn: ERROR: "
+                         "NULL db context handed in via private_data\n"));
+               return 1;
+       }
+
+       if (strncmp((const char *)key.dptr, REGDB_VERSION_KEYNAME,
+           strlen(REGDB_VERSION_KEYNAME)) == 0)
+       {
+               return 0;
+       }
 
+       keyname = strchr((const char *)key.dptr, '/');
+       if (keyname) {
                keyname = talloc_string_sub(mem_ctx,
-                                           (const char *) rec->key.dptr,
+                                           (const char *)key.dptr,
                                            "/",
                                            "\\");
 
                DEBUG(2, ("regdb_normalize_keynames_fn: Convert %s to %s\n",
-                         (const char *) rec->key.dptr,
+                         (const char *)key.dptr,
                          keyname));
 
-               new_rec.value = rec->value;
-               new_rec.key = string_term_tdb_data(keyname);
-               new_rec.private_data = rec->private_data;
-
                /* Delete the original record and store the normalized key */
-               status = rec->delete_rec(rec);
+               status = dbwrap_record_delete(rec);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(0,("regdb_normalize_keynames_fn: "
                                 "tdb_delete for [%s] failed!\n",
-                                rec->key.dptr));
+                                (const char *)key.dptr));
                        return 1;
                }
 
-               status = rec->store(&new_rec, new_rec.value, TDB_REPLACE);
+               status = dbwrap_store_bystring(db, keyname, value, TDB_REPLACE);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(0,("regdb_normalize_keynames_fn: "
                                 "failed to store new record for [%s]!\n",
@@ -414,20 +480,18 @@ static int regdb_normalize_keynames_fn(struct db_record *rec,
 static WERROR regdb_store_regdb_version(struct db_context *db, uint32_t version)
 {
        NTSTATUS status;
-       const char *version_keyname = "INFO/version";
-
        if (db == NULL) {
                return WERR_CAN_NOT_COMPLETE;
        }
 
-       status = dbwrap_trans_store_int32(db, version_keyname, version);
+       status = dbwrap_trans_store_int32(db, REGDB_VERSION_KEYNAME, version);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(1, ("regdb_store_regdb_version: error storing %s = %d: %s\n",
-                         version_keyname, version, nt_errstr(status)));
+                         REGDB_VERSION_KEYNAME, version, nt_errstr(status)));
                return ntstatus_to_werror(status);
        } else {
                DEBUG(10, ("regdb_store_regdb_version: stored %s = %d\n",
-                         version_keyname, version));
+                         REGDB_VERSION_KEYNAME, version));
                return WERR_OK;
        }
 }
@@ -435,95 +499,194 @@ static WERROR regdb_store_regdb_version(struct db_context *db, uint32_t version)
 static WERROR regdb_upgrade_v1_to_v2(struct db_context *db)
 {
        TALLOC_CTX *mem_ctx;
-       int rc;
+       NTSTATUS status;
        WERROR werr;
 
        mem_ctx = talloc_stackframe();
 
-       rc = regdb->traverse(db, regdb_normalize_keynames_fn, mem_ctx);
+       status = dbwrap_traverse(db, regdb_normalize_keynames_fn, db, NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               werr = WERR_REG_IO_FAILURE;
+               goto done;
+       }
 
+       werr = regdb_store_regdb_version(db, REGDB_VERSION_V2);
+
+done:
        talloc_free(mem_ctx);
+       return werr;
+}
 
-       if (rc < 0) {
-               return WERR_REG_IO_FAILURE;
+static bool tdb_data_read_uint32(TDB_DATA *buf, uint32_t *result)
+{
+       const size_t len = sizeof(uint32_t);
+       if (buf->dsize >= len) {
+               *result = IVAL(buf->dptr, 0);
+               buf->dptr += len;
+               buf->dsize -= len;
+               return true;
+       }
+       return false;
+}
+
+static bool tdb_data_read_cstr(TDB_DATA *buf, char **result)
+{
+       const size_t len = strnlen((char*)buf->dptr, buf->dsize) + 1;
+       if (buf->dsize >= len) {
+               *result = (char*)buf->dptr;
+               buf->dptr += len;
+               buf->dsize -= len;
+               return true;
+       }
+       return false;
+}
+
+static bool tdb_data_is_cstr(TDB_DATA d) {
+       if (tdb_data_is_empty(d) || (d.dptr[d.dsize-1] != '\0')) {
+               return false;
        }
+       return strchr((char *)d.dptr, '\0') == (char *)&d.dptr[d.dsize-1];
+}
 
-       werr = regdb_store_regdb_version(db, REGVER_V2);
-       return werr;
+static bool upgrade_v2_to_v3_check_subkeylist(struct db_context *db,
+                                             const char *key,
+                                             const char *subkey)
+{
+       static uint32_t zero = 0;
+       static TDB_DATA empty_subkey_list = {
+               .dptr = (unsigned char*)&zero,
+               .dsize = sizeof(uint32_t),
+       };
+       bool success = false;
+       char *path = talloc_asprintf(talloc_tos(), "%s\\%s", key, subkey);
+       strupper_m(path);
+
+       if (!dbwrap_exists(db, string_term_tdb_data(path))) {
+               NTSTATUS status;
+
+               DEBUG(10, ("regdb_upgrade_v2_to_v3: writing subkey list [%s]\n",
+                          path));
+
+               status = dbwrap_store_bystring(db, path, empty_subkey_list,
+                                              TDB_INSERT);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0, ("regdb_upgrade_v2_to_v3: writing subkey list "
+                                 "[%s] failed\n", path));
+                       goto done;
+               }
+       }
+       success = true;
+done:
+       talloc_free(path);
+       return success;
 }
 
+static bool upgrade_v2_to_v3_check_parent(struct db_context *db,
+                                         const char *key)
+{
+       const char *sep = strrchr_m(key, '\\');
+       if (sep != NULL) {
+               char *pkey = talloc_strndup(talloc_tos(), key, sep-key);
+               if (!dbwrap_exists(db, string_term_tdb_data(pkey))) {
+                       DEBUG(0, ("regdb_upgrade_v2_to_v3: missing subkey list "
+                                 "[%s]\nrun \"net registry check\"\n", pkey));
+               }
+               talloc_free(pkey);
+       }
+       return true;
+}
+
+
+#define IS_EQUAL(d,s) (((d).dsize == strlen(s)+1) &&   \
+                      (strcmp((char*)(d).dptr, (s)) == 0))
+#define STARTS_WITH(d,s) (((d).dsize > strlen(s)) &&                   \
+                         (strncmp((char*)(d).dptr, (s), strlen(s)) == 0))
+#define SSTR(d) (int)(d).dsize , (char*)(d).dptr
+
+
 static int regdb_upgrade_v2_to_v3_fn(struct db_record *rec, void *private_data)
 {
-       const char *keyname;
-       fstring subkeyname;
-       NTSTATUS status;
-       WERROR werr;
-       uint8_t *buf;
-       uint32_t buflen, len;
-       uint32_t num_items;
-       uint32_t i;
+       struct db_context *db = (struct db_context *)private_data;
+       TDB_DATA key = dbwrap_record_get_key(rec);
+       TDB_DATA val = dbwrap_record_get_value(rec);
 
-       if (rec->key.dptr == NULL || rec->key.dsize == 0) {
+       if (tdb_data_is_empty(key)) {
                return 0;
        }
 
-       keyname = (const char *)rec->key.dptr;
+       if (db == NULL) {
+               DEBUG(0, ("regdb_upgrade_v2_to_v3_fn: ERROR: "
+                         "NULL db context handed in via private_data\n"));
+               return 1;
+       }
 
-       if (strncmp(keyname, REG_SORTED_SUBKEYS_PREFIX,
-                   strlen(REG_SORTED_SUBKEYS_PREFIX)) == 0)
+       if (IS_EQUAL(key, REGDB_VERSION_KEYNAME) ||
+           STARTS_WITH(key, REG_VALUE_PREFIX) ||
+           STARTS_WITH(key, REG_SECDESC_PREFIX))
        {
+               DEBUG(10, ("regdb_upgrade_v2_to_v3: skipping [%.*s]\n",
+                          SSTR(key)));
+               return 0;
+       }
+
+       if (STARTS_WITH(key, REG_SORTED_SUBKEYS_PREFIX)) {
+               NTSTATUS status;
                /* Delete the deprecated sorted subkeys cache. */
 
-               DEBUG(10, ("regdb_upgrade_v2_to_v3: deleting [%s]\n", keyname));
+               DEBUG(10, ("regdb_upgrade_v2_to_v3: deleting [%.*s]\n",
+                          SSTR(key)));
 
-               status = rec->delete_rec(rec);
+               status = dbwrap_record_delete(rec);
                if (!NT_STATUS_IS_OK(status)) {
-                       DEBUG(0, ("regdb_upgrade_v2_to_v3: tdb_delete for [%s] "
-                                 "failed!\n", keyname));
+                       DEBUG(0, ("regdb_upgrade_v2_to_v3: deleting [%.*s] "
+                                 "failed!\n", SSTR(key)));
                        return 1;
                }
 
                return 0;
        }
 
-       if (strncmp(keyname, REG_VALUE_PREFIX, strlen(REG_VALUE_PREFIX)) == 0) {
-               DEBUG(10, ("regdb_upgrade_v2_to_v3: skipping [%s]\n", keyname));
-               return 0;
-       }
-
-       if (strncmp(keyname, REG_SECDESC_PREFIX,
-                   strlen(REG_SECDESC_PREFIX)) == 0)
+       if ( tdb_data_is_cstr(key) &&
+            hive_info((char*)key.dptr) != NULL )
        {
-               DEBUG(10, ("regdb_upgrade_v2_to_v3: skipping [%s]\n", keyname));
-               return 0;
-       }
-
-       /*
-        * Found a regular subkey list record.
-        * Walk the list and create the list record for those
-        * subkeys that don't already have one.
-        */
-       DEBUG(10, ("regdb_upgrade_v2_to_v3: scanning subkey list of [%s]\n",
-                  keyname));
+               /*
+                * Found a regular subkey list record.
+                * Walk the list and create the list record for those
+                * subkeys that don't already have one.
+                */
+               TDB_DATA pos = val;
+               char *subkey, *path = (char*)key.dptr;
+               uint32_t num_items, found_items = 0;
+
+
+               DEBUG(10, ("regdb_upgrade_v2_to_v3: scanning subkeylist of "
+                          "[%s]\n", path));
+
+               if (!tdb_data_read_uint32(&pos, &num_items)) {
+                       /* invalid or empty - skip */
+                       return 0;
+               }
 
-       buf = rec->value.dptr;
-       buflen = rec->value.dsize;
+               while (tdb_data_read_cstr(&pos, &subkey)) {
+                       found_items++;
 
-       len = tdb_unpack(buf, buflen, "d", &num_items);
-       if (len == (uint32_t)-1) {
-               /* invalid or empty - skip */
-               return 0;
-       }
+                       if (!upgrade_v2_to_v3_check_subkeylist(db, path, subkey))
+                       {
+                               return 1;
+                       }
 
-       for (i=0; i<num_items; i++) {
-               len += tdb_unpack(buf+len, buflen-len, "f", subkeyname);
-               DEBUG(10, ("regdb_upgrade_v2_to_v3: "
-                          "writing subkey list for [%s\\%s]\n",
-                          keyname, subkeyname));
-               werr = regdb_store_subkey_list(regdb, keyname, subkeyname);
-               if (!W_ERROR_IS_OK(werr)) {
-                       return 1;
+                       if (!upgrade_v2_to_v3_check_parent(db, path)) {
+                               return 1;
+                       }
+               }
+               if (found_items != num_items) {
+                       DEBUG(0, ("regdb_upgrade_v2_to_v3: inconsistent subkey "
+                                 "list [%s]\nrun \"net registry check\"\n",
+                                 path));
                }
+       } else {
+               DEBUG(10, ("regdb_upgrade_v2_to_v3: skipping invalid [%.*s]\n"
+                          "run \"net registry check\"\n", SSTR(key)));
        }
 
        return 0;
@@ -531,20 +694,18 @@ static int regdb_upgrade_v2_to_v3_fn(struct db_record *rec, void *private_data)
 
 static WERROR regdb_upgrade_v2_to_v3(struct db_context *db)
 {
-       int rc;
+       NTSTATUS status;
        WERROR werr;
-       TALLOC_CTX *frame = talloc_stackframe();
 
-       rc = regdb->traverse(db, regdb_upgrade_v2_to_v3_fn, frame);
-       if (rc < 0) {
+       status = dbwrap_traverse(db, regdb_upgrade_v2_to_v3_fn, db, NULL);
+       if (!NT_STATUS_IS_OK(status)) {
                werr = WERR_REG_IO_FAILURE;
                goto done;
        }
 
-       werr = regdb_store_regdb_version(db, REGVER_V3);
+       werr = regdb_store_regdb_version(db, REGDB_VERSION_V3);
 
 done:
-       talloc_free(frame);
        return werr;
 }
 
@@ -554,9 +715,9 @@ done:
 
 WERROR regdb_init(void)
 {
-       const char *vstring = "INFO/version";
-       uint32 vers_id, expected_version;
+       int32_t vers_id;
        WERROR werr;
+       NTSTATUS status;
 
        if (regdb) {
                DEBUG(10, ("regdb_init: incrementing refcount (%d->%d)\n",
@@ -566,10 +727,12 @@ WERROR regdb_init(void)
        }
 
        regdb = db_open(NULL, state_path("registry.tdb"), 0,
-                             REG_TDB_FLAGS, O_RDWR, 0600);
+                       REG_TDB_FLAGS, O_RDWR, 0600,
+                       DBWRAP_LOCK_ORDER_1);
        if (!regdb) {
                regdb = db_open(NULL, state_path("registry.tdb"), 0,
-                                     REG_TDB_FLAGS, O_RDWR|O_CREAT, 0600);
+                               REG_TDB_FLAGS, O_RDWR|O_CREAT, 0600,
+                               DBWRAP_LOCK_ORDER_1);
                if (!regdb) {
                        werr = ntstatus_to_werror(map_nt_error_from_unix(errno));
                        DEBUG(1,("regdb_init: Failed to open registry %s (%s)\n",
@@ -577,6 +740,13 @@ WERROR regdb_init(void)
                        return werr;
                }
 
+               werr = regdb_store_regdb_version(regdb, REGDB_CODE_VERSION);
+               if (!W_ERROR_IS_OK(werr)) {
+                       DEBUG(1, ("regdb_init: Failed to store version: %s\n",
+                                 win_errstr(werr)));
+                       return werr;
+               }
+
                DEBUG(10,("regdb_init: Successfully created registry tdb\n"));
        }
 
@@ -584,58 +754,74 @@ WERROR regdb_init(void)
        DEBUG(10, ("regdb_init: registry db openend. refcount reset (%d)\n",
                   regdb_refcount));
 
-       expected_version = REGVER_V3;
-
-       vers_id = dbwrap_fetch_int32(regdb, vstring);
-       if (vers_id == -1) {
+       status = dbwrap_fetch_int32(regdb, REGDB_VERSION_KEYNAME, &vers_id);
+       if (!NT_STATUS_IS_OK(status)) {
                DEBUG(10, ("regdb_init: registry version uninitialized "
                           "(got %d), initializing to version %d\n",
-                          vers_id, expected_version));
+                          vers_id, REGDB_VERSION_V1));
+
+               /*
+                * There was a regdb format version prior to version 1
+                * which did not store a INFO/version key. The format
+                * of this version was identical to version 1 except for
+                * the lack of the sorted subkey cache records.
+                * Since these are disposable, we can safely assume version
+                * 1 if no INFO/version key is found and run the db through
+                * the whole chain of upgrade. If the database was not
+                * initialized, this does not harm. If it was the unversioned
+                * version ("0"), then it do the right thing with the records.
+                */
+               werr = regdb_store_regdb_version(regdb, REGDB_VERSION_V1);
+               if (!W_ERROR_IS_OK(werr)) {
+                       return werr;
+               }
+               vers_id = REGDB_VERSION_V1;
+       }
 
-               werr = regdb_store_regdb_version(regdb, expected_version);
-               return werr;
+       if (vers_id == REGDB_CODE_VERSION) {
+               return WERR_OK;
        }
 
-       if (vers_id > expected_version || vers_id == 0) {
-               DEBUG(1, ("regdb_init: unknown registry version %d "
+       if (vers_id > REGDB_CODE_VERSION || vers_id == 0) {
+               DEBUG(0, ("regdb_init: unknown registry version %d "
                          "(code version = %d), refusing initialization\n",
-                         vers_id, expected_version));
+                         vers_id, REGDB_CODE_VERSION));
                return WERR_CAN_NOT_COMPLETE;
        }
 
-       if (regdb->transaction_start(regdb) != 0) {
+       if (dbwrap_transaction_start(regdb) != 0) {
                return WERR_REG_IO_FAILURE;
        }
 
-       if (vers_id == REGVER_V1) {
-               DEBUG(10, ("regdb_init: upgrading registry fromversion %d "
-                          "to %d\n", REGVER_V1, REGVER_V2));
+       if (vers_id == REGDB_VERSION_V1) {
+               DEBUG(10, ("regdb_init: upgrading registry from version %d "
+                          "to %d\n", REGDB_VERSION_V1, REGDB_VERSION_V2));
 
                werr = regdb_upgrade_v1_to_v2(regdb);
                if (!W_ERROR_IS_OK(werr)) {
-                       regdb->transaction_cancel(regdb);
+                       dbwrap_transaction_cancel(regdb);
                        return werr;
                }
 
-               vers_id = REGVER_V2;
+               vers_id = REGDB_VERSION_V2;
        }
 
-       if (vers_id == REGVER_V2) {
+       if (vers_id == REGDB_VERSION_V2) {
                DEBUG(10, ("regdb_init: upgrading registry from version %d "
-                          "to %d\n", REGVER_V2, REGVER_V3));
+                          "to %d\n", REGDB_VERSION_V2, REGDB_VERSION_V3));
 
                werr = regdb_upgrade_v2_to_v3(regdb);
                if (!W_ERROR_IS_OK(werr)) {
-                       regdb->transaction_cancel(regdb);
+                       dbwrap_transaction_cancel(regdb);
                        return werr;
                }
 
-               vers_id = REGVER_V3;
+               vers_id = REGDB_VERSION_V3;
        }
 
        /* future upgrade code should go here */
 
-       if (regdb->transaction_commit(regdb) != 0) {
+       if (dbwrap_transaction_commit(regdb) != 0) {
                return WERR_REG_IO_FAILURE;
        }
 
@@ -660,7 +846,8 @@ WERROR regdb_open( void )
        become_root();
 
        regdb = db_open(NULL, state_path("registry.tdb"), 0,
-                             REG_TDB_FLAGS, O_RDWR, 0600);
+                       REG_TDB_FLAGS, O_RDWR, 0600,
+                       DBWRAP_LOCK_ORDER_1);
        if ( !regdb ) {
                result = ntstatus_to_werror( map_nt_error_from_unix( errno ) );
                DEBUG(0,("regdb_open: Failed to open %s! (%s)\n",
@@ -701,19 +888,19 @@ int regdb_close( void )
 
 WERROR regdb_transaction_start(void)
 {
-       return (regdb->transaction_start(regdb) == 0) ?
+       return (dbwrap_transaction_start(regdb) == 0) ?
                WERR_OK : WERR_REG_IO_FAILURE;
 }
 
 WERROR regdb_transaction_commit(void)
 {
-       return (regdb->transaction_commit(regdb) == 0) ?
+       return (dbwrap_transaction_commit(regdb) == 0) ?
                WERR_OK : WERR_REG_IO_FAILURE;
 }
 
 WERROR regdb_transaction_cancel(void)
 {
-       return (regdb->transaction_cancel(regdb) == 0) ?
+       return (dbwrap_transaction_cancel(regdb) == 0) ?
                WERR_OK : WERR_REG_IO_FAILURE;
 }
 
@@ -724,7 +911,7 @@ WERROR regdb_transaction_cancel(void)
  ***********************************************************************/
 int regdb_get_seqnum(void)
 {
-       return regdb->get_seqnum(regdb);
+       return dbwrap_get_seqnum(regdb);
 }
 
 
@@ -1141,9 +1328,9 @@ static bool regdb_store_keys_internal(struct db_context *db, const char *key,
        store_ctx.key = key;
        store_ctx.ctr = ctr;
 
-       werr = ntstatus_to_werror(dbwrap_trans_do(db,
-                                                 regdb_store_keys_action,
-                                                 &store_ctx));
+       werr = regdb_trans_do(db,
+                             regdb_store_keys_action,
+                             &store_ctx);
 
        ret = W_ERROR_IS_OK(werr);
 
@@ -1153,7 +1340,7 @@ done:
        return ret;
 }
 
-bool regdb_store_keys(const char *key, struct regsubkey_ctr *ctr)
+static bool regdb_store_keys(const char *key, struct regsubkey_ctr *ctr)
 {
        return regdb_store_keys_internal(regdb, key, ctr);
 }
@@ -1221,8 +1408,18 @@ static WERROR regdb_create_subkey_internal(struct db_context *db,
        W_ERROR_NOT_OK_GOTO_DONE(werr);
 
        if (regsubkey_ctr_key_exists(subkeys, subkey)) {
-               werr = WERR_OK;
-               goto done;
+               char *newkey;
+
+               newkey = talloc_asprintf(mem_ctx, "%s\\%s", key, subkey);
+               if (newkey == NULL) {
+                       werr = WERR_NOMEM;
+                       goto done;
+               }
+
+               if (regdb_key_exists(db, newkey)) {
+                       werr = WERR_OK;
+                       goto done;
+               }
        }
 
        talloc_free(subkeys);
@@ -1230,9 +1427,9 @@ static WERROR regdb_create_subkey_internal(struct db_context *db,
        create_ctx.key = key;
        create_ctx.subkey = subkey;
 
-       werr = ntstatus_to_werror(dbwrap_trans_do(db,
-                                                 regdb_create_subkey_action,
-                                                 &create_ctx));
+       werr = regdb_trans_do(db,
+                             regdb_create_subkey_action,
+                             &create_ctx);
 
 done:
        talloc_free(mem_ctx);
@@ -1272,9 +1469,9 @@ static WERROR regdb_create_basekey(struct db_context *db, const char *key)
 
        create_ctx.key = key;
 
-       werr = ntstatus_to_werror(dbwrap_trans_do(db,
-                                                 regdb_create_basekey_action,
-                                                 &create_ctx));
+       werr = regdb_trans_do(db,
+                             regdb_create_basekey_action,
+                             &create_ctx);
 
        return werr;
 }
@@ -1356,9 +1553,9 @@ static WERROR regdb_delete_subkey(const char *key, const char *subkey, bool lazy
        delete_ctx.path = path;
        delete_ctx.lazy = lazy;
 
-       werr = ntstatus_to_werror(dbwrap_trans_do(regdb,
-                                                 regdb_delete_subkey_action,
-                                                 &delete_ctx));
+       werr = regdb_trans_do(regdb,
+                             regdb_delete_subkey_action,
+                             &delete_ctx);
 
 done:
        talloc_free(mem_ctx);
@@ -1370,13 +1567,17 @@ static TDB_DATA regdb_fetch_key_internal(struct db_context *db,
 {
        char *path = NULL;
        TDB_DATA data;
+       NTSTATUS status;
 
        path = normalize_reg_path(mem_ctx, key);
        if (!path) {
                return make_tdb_data(NULL, 0);
        }
 
-       data = dbwrap_fetch_bystring(db, mem_ctx, path);
+       status = dbwrap_fetch_bystring(db, mem_ctx, path, &data);
+       if (!NT_STATUS_IS_OK(status)) {
+               data = tdb_null;
+       }
 
        TALLOC_FREE(path);
        return data;
@@ -1389,6 +1590,11 @@ static TDB_DATA regdb_fetch_key_internal(struct db_context *db,
  * Existence of a key is authoritatively defined by
  * the existence of the record that contains the list
  * of its subkeys.
+ *
+ * Return false, if the record does not match the correct
+ * structure of an initial 4-byte counter and then a
+ * list of the corresponding number of zero-terminated
+ * strings.
  */
 static bool regdb_key_exists(struct db_context *db, const char *key)
 {
@@ -1396,6 +1602,10 @@ static bool regdb_key_exists(struct db_context *db, const char *key)
        TDB_DATA value;
        bool ret = false;
        char *path;
+       uint32_t buflen;
+       const char *buf;
+       uint32_t num_items, i;
+       int32_t len;
 
        if (key == NULL) {
                goto done;
@@ -1412,7 +1622,69 @@ static bool regdb_key_exists(struct db_context *db, const char *key)
        }
 
        value = regdb_fetch_key_internal(db, mem_ctx, path);
-       ret = (value.dptr != NULL);
+       if (value.dptr == NULL) {
+               goto done;
+       }
+
+       if (value.dsize == 0) {
+               DEBUG(10, ("regdb_key_exists: subkeylist-record for key "
+                         "[%s] is empty: Could be a deleted record in a "
+                         "clustered (ctdb) environment?\n",
+                         path));
+               goto done;
+       }
+
+       len = tdb_unpack(value.dptr, value.dsize, "d", &num_items);
+       if (len == (int32_t)-1) {
+               DEBUG(1, ("regdb_key_exists: ERROR: subkeylist-record for key "
+                         "[%s] is invalid: Could not parse initial 4-byte "
+                         "counter. record data length is %u.\n",
+                         path, (unsigned int)value.dsize));
+               goto done;
+       }
+
+       /*
+        * Note: the tdb_unpack check above implies that len <= value.dsize
+        */
+       buflen = value.dsize - len;
+       buf = (const char *)value.dptr + len;
+
+       len = 0;
+
+       for (i = 0; i < num_items; i++) {
+               if (buflen == 0) {
+                       break;
+               }
+               len = strnlen(buf, buflen) + 1;
+               if (buflen < len) {
+                       DEBUG(1, ("regdb_key_exists: ERROR: subkeylist-record "
+                                 "for key [%s] is corrupt: %u items expected, "
+                                 "item number %u is not zero terminated.\n",
+                                 path, num_items, i+1));
+                       goto done;
+               }
+
+               buf += len;
+               buflen -= len;
+       }
+
+       if (buflen > 0) {
+               DEBUG(1, ("regdb_key_exists: ERROR: subkeylist-record for key "
+                         "[%s] is corrupt: %u items expected and found, but "
+                         "the record contains additional %u bytes\n",
+                         path, num_items, buflen));
+               goto done;
+       }
+
+       if (i < num_items) {
+               DEBUG(1, ("regdb_key_exists: ERROR: subkeylist-record for key "
+                         "[%s] is corrupt: %u items expected, but only %u "
+                         "items found.\n",
+                         path, num_items, i+1));
+               goto done;
+       }
+
+       ret = true;
 
 done:
        TALLOC_FREE(mem_ctx);
@@ -1448,7 +1720,7 @@ static WERROR regdb_fetch_keys_internal(struct db_context *db, const char *key,
        werr = regsubkey_ctr_reinit(ctr);
        W_ERROR_NOT_OK_GOTO_DONE(werr);
 
-       werr = regsubkey_ctr_set_seqnum(ctr, db->get_seqnum(db));
+       werr = regsubkey_ctr_set_seqnum(ctr, dbwrap_get_seqnum(db));
        W_ERROR_NOT_OK_GOTO_DONE(werr);
 
        value = regdb_fetch_key_internal(db, frame, key);
@@ -1485,7 +1757,7 @@ done:
        return werr;
 }
 
-int regdb_fetch_keys(const char *key, struct regsubkey_ctr *ctr)
+static int regdb_fetch_keys(const char *key, struct regsubkey_ctr *ctr)
 {
        WERROR werr;
 
@@ -1597,7 +1869,7 @@ static int regdb_fetch_values_internal(struct db_context *db, const char* key,
                goto done;
        }
 
-       werr = regval_ctr_set_seqnum(values, db->get_seqnum(db));
+       werr = regval_ctr_set_seqnum(values, dbwrap_get_seqnum(db));
        W_ERROR_NOT_OK_GOTO_DONE(werr);
 
        value = regdb_fetch_key_internal(db, ctx, keystr);
@@ -1620,27 +1892,34 @@ int regdb_fetch_values(const char* key, struct regval_ctr *values)
        return regdb_fetch_values_internal(regdb, key, values);
 }
 
-static bool regdb_store_values_internal(struct db_context *db, const char *key,
-                                       struct regval_ctr *values)
+static NTSTATUS regdb_store_values_internal(struct db_context *db,
+                                           const char *key,
+                                           struct regval_ctr *values)
 {
        TDB_DATA old_data, data;
        char *keystr = NULL;
        TALLOC_CTX *ctx = talloc_stackframe();
        int len;
        NTSTATUS status;
-       bool result = false;
 
        DEBUG(10,("regdb_store_values: Looking for value of key [%s] \n", key));
 
        if (!regdb_key_exists(db, key)) {
+               status = NT_STATUS_NOT_FOUND;
                goto done;
        }
 
+       if (regval_ctr_numvals(values) == 0) {
+               WERROR werr = regdb_delete_values(db, key);
+               return werror_to_ntstatus(werr);
+       }
+
        ZERO_STRUCT(data);
 
        len = regdb_pack_values(values, data.dptr, data.dsize);
        if (len <= 0) {
                DEBUG(0,("regdb_store_values: unable to pack values. len <= 0\n"));
+               status = NT_STATUS_UNSUCCESSFUL;
                goto done;
        }
 
@@ -1653,35 +1932,61 @@ static bool regdb_store_values_internal(struct db_context *db, const char *key,
 
        keystr = talloc_asprintf(ctx, "%s\\%s", REG_VALUE_PREFIX, key );
        if (!keystr) {
+               status = NT_STATUS_NO_MEMORY;
                goto done;
        }
        keystr = normalize_reg_path(ctx, keystr);
        if (!keystr) {
+               status = NT_STATUS_NO_MEMORY;
                goto done;
        }
 
-       old_data = dbwrap_fetch_bystring(db, ctx, keystr);
+       status = dbwrap_fetch_bystring(db, ctx, keystr, &old_data);
 
-       if ((old_data.dptr != NULL)
+       if (NT_STATUS_IS_OK(status)
+           && (old_data.dptr != NULL)
            && (old_data.dsize == data.dsize)
            && (memcmp(old_data.dptr, data.dptr, data.dsize) == 0))
        {
-               result = true;
+               status = NT_STATUS_OK;
                goto done;
        }
 
        status = dbwrap_trans_store_bystring(db, keystr, data, TDB_REPLACE);
 
-       result = NT_STATUS_IS_OK(status);
-
 done:
        TALLOC_FREE(ctx);
-       return result;
+       return status;
+}
+
+struct regdb_store_values_ctx {
+       const char *key;
+       struct regval_ctr *values;
+};
+
+static NTSTATUS regdb_store_values_action(struct db_context *db,
+                                         void *private_data)
+{
+       NTSTATUS status;
+       struct regdb_store_values_ctx *ctx =
+               (struct regdb_store_values_ctx *)private_data;
+
+       status = regdb_store_values_internal(db, ctx->key, ctx->values);
+
+       return status;
 }
 
 bool regdb_store_values(const char *key, struct regval_ctr *values)
 {
-       return regdb_store_values_internal(regdb, key, values);
+       WERROR werr;
+       struct regdb_store_values_ctx ctx;
+
+       ctx.key = key;
+       ctx.values = values;
+
+       werr = regdb_trans_do(regdb, regdb_store_values_action, &ctx);
+
+       return W_ERROR_IS_OK(werr);
 }
 
 static WERROR regdb_get_secdesc(TALLOC_CTX *mem_ctx, const char *key,
@@ -1712,8 +2017,8 @@ static WERROR regdb_get_secdesc(TALLOC_CTX *mem_ctx, const char *key,
                goto done;
        }
 
-       data = dbwrap_fetch_bystring(regdb, tmp_ctx, tdbkey);
-       if (data.dptr == NULL) {
+       status = dbwrap_fetch_bystring(regdb, tmp_ctx, tdbkey, &data);
+       if (!NT_STATUS_IS_OK(status)) {
                err = WERR_BADFILE;
                goto done;
        }
@@ -1732,47 +2037,69 @@ done:
        return err;
 }
 
-static WERROR regdb_set_secdesc(const char *key,
-                               struct security_descriptor *secdesc)
+struct regdb_set_secdesc_ctx {
+       const char *key;
+       struct security_descriptor *secdesc;
+};
+
+static NTSTATUS regdb_set_secdesc_action(struct db_context *db,
+                                        void *private_data)
 {
-       TALLOC_CTX *mem_ctx = talloc_stackframe();
        char *tdbkey;
-       WERROR err = WERR_NOMEM;
+       NTSTATUS status;
        TDB_DATA tdbdata;
+       struct regdb_set_secdesc_ctx *ctx =
+               (struct regdb_set_secdesc_ctx *)private_data;
+       TALLOC_CTX *frame = talloc_stackframe();
 
-       if (!regdb_key_exists(regdb, key)) {
-               err = WERR_BADFILE;
+       tdbkey = talloc_asprintf(frame, "%s\\%s", REG_SECDESC_PREFIX, ctx->key);
+       if (tdbkey == NULL) {
+               status = NT_STATUS_NO_MEMORY;
                goto done;
        }
 
-       tdbkey = talloc_asprintf(mem_ctx, "%s\\%s", REG_SECDESC_PREFIX, key);
+       tdbkey = normalize_reg_path(frame, tdbkey);
        if (tdbkey == NULL) {
+               status = NT_STATUS_NO_MEMORY;
                goto done;
        }
 
-       tdbkey = normalize_reg_path(mem_ctx, tdbkey);
-       if (tdbkey == NULL) {
-               err = WERR_NOMEM;
+       if (ctx->secdesc == NULL) {
+               /* assuming a delete */
+               status = dbwrap_delete_bystring(db, tdbkey);
                goto done;
        }
 
-       if (secdesc == NULL) {
-               /* assuming a delete */
-               err = ntstatus_to_werror(dbwrap_trans_delete_bystring(regdb,
-                                                                     tdbkey));
+       status = marshall_sec_desc(frame, ctx->secdesc, &tdbdata.dptr,
+                                  &tdbdata.dsize);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       status = dbwrap_store_bystring(db, tdbkey, tdbdata, 0);
+
+done:
+       TALLOC_FREE(frame);
+       return status;
+}
+
+static WERROR regdb_set_secdesc(const char *key,
+                               struct security_descriptor *secdesc)
+{
+       WERROR err;
+       struct regdb_set_secdesc_ctx ctx;
+
+       if (!regdb_key_exists(regdb, key)) {
+               err = WERR_BADFILE;
                goto done;
        }
 
-       err = ntstatus_to_werror(marshall_sec_desc(mem_ctx, secdesc,
-                                                  &tdbdata.dptr,
-                                                  &tdbdata.dsize));
-       W_ERROR_NOT_OK_GOTO_DONE(err);
+       ctx.key = key;
+       ctx.secdesc = secdesc;
 
-       err = ntstatus_to_werror(dbwrap_trans_store_bystring(regdb, tdbkey,
-                                                            tdbdata, 0));
+       err = regdb_trans_do(regdb, regdb_set_secdesc_action, &ctx);
 
- done:
-       TALLOC_FREE(mem_ctx);
+done:
        return err;
 }