summaryrefslogtreecommitdiffstats
path: root/source3/registry
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source3/registry
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/registry')
-rw-r--r--source3/registry/reg_api.c1063
-rw-r--r--source3/registry/reg_api.h70
-rw-r--r--source3/registry/reg_api_util.c84
-rw-r--r--source3/registry/reg_api_util.h35
-rw-r--r--source3/registry/reg_backend_current_version.c78
-rw-r--r--source3/registry/reg_backend_db.c2270
-rw-r--r--source3/registry/reg_backend_db.h36
-rw-r--r--source3/registry/reg_backend_hkpt_params.c73
-rw-r--r--source3/registry/reg_backend_netlogon_params.c60
-rw-r--r--source3/registry/reg_backend_perflib.c110
-rw-r--r--source3/registry/reg_backend_printing.c281
-rw-r--r--source3/registry/reg_backend_prod_options.c68
-rw-r--r--source3/registry/reg_backend_shares.c165
-rw-r--r--source3/registry/reg_backend_smbconf.c110
-rw-r--r--source3/registry/reg_backend_tcpip_params.c54
-rw-r--r--source3/registry/reg_cachehook.c147
-rw-r--r--source3/registry/reg_cachehook.h29
-rw-r--r--source3/registry/reg_db.h37
-rw-r--r--source3/registry/reg_dispatcher.c264
-rw-r--r--source3/registry/reg_dispatcher.h44
-rw-r--r--source3/registry/reg_format.c830
-rw-r--r--source3/registry/reg_format.h219
-rw-r--r--source3/registry/reg_import.c314
-rw-r--r--source3/registry/reg_import.h199
-rw-r--r--source3/registry/reg_init_basic.c67
-rw-r--r--source3/registry/reg_init_basic.h26
-rw-r--r--source3/registry/reg_init_full.c102
-rw-r--r--source3/registry/reg_init_full.h27
-rw-r--r--source3/registry/reg_init_smbconf.c71
-rw-r--r--source3/registry/reg_init_smbconf.h27
-rw-r--r--source3/registry/reg_objects.c615
-rw-r--r--source3/registry/reg_objects.h74
-rw-r--r--source3/registry/reg_parse.c1090
-rw-r--r--source3/registry/reg_parse.h190
-rw-r--r--source3/registry/reg_parse_dox.cfg1562
-rw-r--r--source3/registry/reg_parse_internal.c392
-rw-r--r--source3/registry/reg_parse_internal.h123
-rw-r--r--source3/registry/reg_parse_prs.c453
-rw-r--r--source3/registry/reg_parse_prs.h80
-rw-r--r--source3/registry/reg_perfcount.c1463
-rw-r--r--source3/registry/reg_perfcount.h34
-rw-r--r--source3/registry/reg_util_internal.c149
-rw-r--r--source3/registry/reg_util_internal.h28
-rw-r--r--source3/registry/reg_util_token.c61
-rw-r--r--source3/registry/reg_util_token.h26
-rw-r--r--source3/registry/regfio.c1993
-rw-r--r--source3/registry/regfio.h233
-rw-r--r--source3/registry/tests/test_regfio.c186
48 files changed, 15712 insertions, 0 deletions
diff --git a/source3/registry/reg_api.c b/source3/registry/reg_api.c
new file mode 100644
index 0000000..ce5beb9
--- /dev/null
+++ b/source3/registry/reg_api.c
@@ -0,0 +1,1063 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Volker Lendecke 2006
+ * Copyright (C) Michael Adam 2007-2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Attempt to wrap the existing API in a more winreg.idl-like way */
+
+/*
+ * Here is a list of winreg.idl functions and corresponding implementations
+ * provided here:
+ *
+ * 0x00 winreg_OpenHKCR
+ * 0x01 winreg_OpenHKCU
+ * 0x02 winreg_OpenHKLM
+ * 0x03 winreg_OpenHKPD
+ * 0x04 winreg_OpenHKU
+ * 0x05 winreg_CloseKey
+ * 0x06 winreg_CreateKey reg_createkey
+ * 0x07 winreg_DeleteKey reg_deletekey
+ * 0x08 winreg_DeleteValue reg_deletevalue
+ * 0x09 winreg_EnumKey reg_enumkey
+ * 0x0a winreg_EnumValue reg_enumvalue
+ * 0x0b winreg_FlushKey
+ * 0x0c winreg_GetKeySecurity reg_getkeysecurity
+ * 0x0d winreg_LoadKey
+ * 0x0e winreg_NotifyChangeKeyValue
+ * 0x0f winreg_OpenKey reg_openkey
+ * 0x10 winreg_QueryInfoKey reg_queryinfokey
+ * 0x11 winreg_QueryValue reg_queryvalue
+ * 0x12 winreg_ReplaceKey
+ * 0x13 winreg_RestoreKey reg_restorekey
+ * 0x14 winreg_SaveKey reg_savekey
+ * 0x15 winreg_SetKeySecurity reg_setkeysecurity
+ * 0x16 winreg_SetValue reg_setvalue
+ * 0x17 winreg_UnLoadKey
+ * 0x18 winreg_InitiateSystemShutdown
+ * 0x19 winreg_AbortSystemShutdown
+ * 0x1a winreg_GetVersion reg_getversion
+ * 0x1b winreg_OpenHKCC
+ * 0x1c winreg_OpenHKDD
+ * 0x1d winreg_QueryMultipleValues reg_querymultiplevalues
+ * 0x1e winreg_InitiateSystemShutdownEx
+ * 0x1f winreg_SaveKeyEx
+ * 0x20 winreg_OpenHKPT
+ * 0x21 winreg_OpenHKPN
+ * 0x22 winreg_QueryMultipleValues2 reg_querymultiplevalues
+ *
+ */
+
+#include "includes.h"
+#include "registry.h"
+#include "reg_api.h"
+#include "reg_cachehook.h"
+#include "reg_backend_db.h"
+#include "reg_dispatcher.h"
+#include "reg_objects.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "reg_parse_internal.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+
+/**********************************************************************
+ * Helper functions
+ **********************************************************************/
+
+static WERROR fill_value_cache(struct registry_key *key)
+{
+ WERROR werr;
+
+ if (key->values != NULL) {
+ if (!reg_values_need_update(key->key, key->values)) {
+ return WERR_OK;
+ }
+ }
+
+ TALLOC_FREE(key->values);
+ werr = regval_ctr_init(key, &(key->values));
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ if (fetch_reg_values(key->key, key->values) == -1) {
+ TALLOC_FREE(key->values);
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR fill_subkey_cache(struct registry_key *key)
+{
+ WERROR werr;
+
+ if (key->subkeys != NULL) {
+ if (!reg_subkeys_need_update(key->key, key->subkeys)) {
+ return WERR_OK;
+ }
+ }
+
+ TALLOC_FREE(key->subkeys);
+ werr = regsubkey_ctr_init(key, &(key->subkeys));
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ if (fetch_reg_keys(key->key, key->subkeys) == -1) {
+ TALLOC_FREE(key->subkeys);
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ return WERR_OK;
+}
+
+static int regkey_destructor(struct registry_key_handle *key)
+{
+ return regdb_close();
+}
+
+static WERROR regkey_open_onelevel(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *name,
+ const struct security_token *token,
+ uint32_t access_desired,
+ struct registry_key **pregkey)
+{
+ WERROR result;
+ struct registry_key *regkey;
+ struct registry_key_handle *key;
+
+ DEBUG(7,("regkey_open_onelevel: name = [%s]\n", name));
+
+ SMB_ASSERT(strchr(name, '\\') == NULL);
+
+ if (!(regkey = talloc_zero(mem_ctx, struct registry_key)) ||
+ !(regkey->token = security_token_duplicate(regkey, token)) ||
+ !(regkey->key = talloc_zero(regkey, struct registry_key_handle)))
+ {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ result = regdb_open();
+ if (!(W_ERROR_IS_OK(result))) {
+ goto done;
+ }
+
+ key = regkey->key;
+ talloc_set_destructor(key, regkey_destructor);
+
+ /* initialization */
+
+ key->type = REG_KEY_GENERIC;
+
+ if (name[0] == '\0') {
+ /*
+ * Open a copy of the parent key
+ */
+ if (!parent) {
+ result = WERR_FILE_NOT_FOUND;
+ goto done;
+ }
+ key->name = talloc_strdup(key, parent->key->name);
+ }
+ else {
+ /*
+ * Normal subkey open
+ */
+ key->name = talloc_asprintf(key, "%s%s%s",
+ parent ? parent->key->name : "",
+ parent ? "\\": "",
+ name);
+ }
+
+ if (key->name == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ /* Tag this as a Performance Counter Key */
+
+ if( strncasecmp_m(key->name, KEY_HKPD, strlen(KEY_HKPD)) == 0 )
+ key->type = REG_KEY_HKPD;
+
+ /* Look up the table of registry I/O operations */
+
+ key->ops = reghook_cache_find( key->name );
+ if (key->ops == NULL) {
+ DEBUG(0,("reg_open_onelevel: Failed to assign "
+ "registry_ops to [%s]\n", key->name ));
+ result = WERR_FILE_NOT_FOUND;
+ goto done;
+ }
+
+ /* FIXME: Existence is currently checked by fetching the subkeys */
+
+ result = fill_subkey_cache(regkey);
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ if ( !regkey_access_check( key, access_desired, &key->access_granted,
+ token ) ) {
+ result = WERR_ACCESS_DENIED;
+ goto done;
+ }
+
+ *pregkey = regkey;
+ result = WERR_OK;
+
+done:
+ if ( !W_ERROR_IS_OK(result) ) {
+ TALLOC_FREE(regkey);
+ }
+
+ return result;
+}
+
+WERROR reg_openhive(TALLOC_CTX *mem_ctx, const char *hive,
+ uint32_t desired_access,
+ const struct security_token *token,
+ struct registry_key **pkey)
+{
+ const struct hive_info *hi;
+ SMB_ASSERT(hive != NULL);
+ SMB_ASSERT(strchr(hive, '\\') == NULL);
+
+ hi = hive_info(hive);
+ if (hi == NULL) {
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ return regkey_open_onelevel(mem_ctx, NULL, hi->short_name, token,
+ desired_access, pkey);
+}
+
+
+/**********************************************************************
+ * The API functions
+ **********************************************************************/
+
+WERROR reg_openkey(TALLOC_CTX *mem_ctx, struct registry_key *parent,
+ const char *name, uint32_t desired_access,
+ struct registry_key **pkey)
+{
+ struct registry_key *direct_parent = parent;
+ WERROR err;
+ char *p, *path;
+ size_t len;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ path = talloc_strdup(frame, name);
+ if (path == NULL) {
+ err = WERR_NOT_ENOUGH_MEMORY;
+ goto error;
+ }
+
+ len = strlen(path);
+
+ if ((len > 0) && (path[len-1] == '\\')) {
+ path[len-1] = '\0';
+ }
+
+ while ((p = strchr(path, '\\')) != NULL) {
+ char *name_component;
+ struct registry_key *tmp;
+
+ name_component = talloc_strndup(frame, path, (p - path));
+ if (name_component == NULL) {
+ err = WERR_NOT_ENOUGH_MEMORY;
+ goto error;
+ }
+
+ err = regkey_open_onelevel(frame, direct_parent,
+ name_component, parent->token,
+ KEY_ENUMERATE_SUB_KEYS, &tmp);
+
+ if (!W_ERROR_IS_OK(err)) {
+ goto error;
+ }
+
+ direct_parent = tmp;
+ path = p+1;
+ }
+
+ err = regkey_open_onelevel(mem_ctx, direct_parent, path, parent->token,
+ desired_access, pkey);
+
+error:
+ talloc_free(frame);
+ return err;
+}
+
+WERROR reg_enumkey(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ uint32_t idx, char **name, NTTIME *last_write_time)
+{
+ WERROR err;
+
+ if (!(key->key->access_granted & KEY_ENUMERATE_SUB_KEYS)) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ err = fill_subkey_cache(key);
+ if (!W_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ if (idx >= regsubkey_ctr_numkeys(key->subkeys)) {
+ return WERR_NO_MORE_ITEMS;
+ }
+
+ if (!(*name = talloc_strdup(mem_ctx,
+ regsubkey_ctr_specific_key(key->subkeys, idx))))
+ {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (last_write_time) {
+ *last_write_time = 0;
+ }
+
+ return WERR_OK;
+}
+
+WERROR reg_enumvalue(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ uint32_t idx, char **pname, struct registry_value **pval)
+{
+ struct registry_value *val;
+ struct regval_blob *blob;
+ WERROR err;
+
+ if (!(key->key->access_granted & KEY_QUERY_VALUE)) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ err = fill_value_cache(key);
+ if (!(W_ERROR_IS_OK(err))) {
+ return err;
+ }
+
+ if (idx >= regval_ctr_numvals(key->values)) {
+ return WERR_NO_MORE_ITEMS;
+ }
+
+ blob = regval_ctr_specific_value(key->values, idx);
+
+ val = talloc_zero(mem_ctx, struct registry_value);
+ if (val == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ val->type = regval_type(blob);
+ val->data = data_blob_talloc(mem_ctx, regval_data_p(blob), regval_size(blob));
+
+ if (pname
+ && !(*pname = talloc_strdup(
+ mem_ctx, regval_name(blob)))) {
+ TALLOC_FREE(val);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ *pval = val;
+ return WERR_OK;
+}
+
+static WERROR reg_enumvalue_nocachefill(TALLOC_CTX *mem_ctx,
+ struct registry_key *key,
+ uint32_t idx, char **pname,
+ struct registry_value **pval)
+{
+ struct registry_value *val;
+ struct regval_blob *blob;
+
+ if (!(key->key->access_granted & KEY_QUERY_VALUE)) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (idx >= regval_ctr_numvals(key->values)) {
+ return WERR_NO_MORE_ITEMS;
+ }
+
+ blob = regval_ctr_specific_value(key->values, idx);
+
+ val = talloc_zero(mem_ctx, struct registry_value);
+ if (val == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ val->type = regval_type(blob);
+ val->data = data_blob_talloc(mem_ctx, regval_data_p(blob), regval_size(blob));
+
+ if (pname
+ && !(*pname = talloc_strdup(
+ mem_ctx, regval_name(blob)))) {
+ TALLOC_FREE(val);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ *pval = val;
+ return WERR_OK;
+}
+
+WERROR reg_queryvalue(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ const char *name, struct registry_value **pval)
+{
+ WERROR err;
+ uint32_t i;
+
+ if (!(key->key->access_granted & KEY_QUERY_VALUE)) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (!(W_ERROR_IS_OK(err = fill_value_cache(key)))) {
+ return err;
+ }
+
+ for (i=0; i < regval_ctr_numvals(key->values); i++) {
+ struct regval_blob *blob;
+ blob = regval_ctr_specific_value(key->values, i);
+ if (strequal(regval_name(blob), name)) {
+ /*
+ * don't use reg_enumvalue here:
+ * re-reading the values from the disk
+ * would change the indexing and break
+ * this function.
+ */
+ return reg_enumvalue_nocachefill(mem_ctx, key, i,
+ NULL, pval);
+ }
+ }
+
+ return WERR_FILE_NOT_FOUND;
+}
+
+WERROR reg_querymultiplevalues(TALLOC_CTX *mem_ctx,
+ struct registry_key *key,
+ uint32_t num_names,
+ const char **names,
+ uint32_t *pnum_vals,
+ struct registry_value **pvals)
+{
+ WERROR err;
+ uint32_t i, n, found = 0;
+ struct registry_value *vals;
+
+ if (num_names == 0) {
+ return WERR_OK;
+ }
+
+ if (!(key->key->access_granted & KEY_QUERY_VALUE)) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (!(W_ERROR_IS_OK(err = fill_value_cache(key)))) {
+ return err;
+ }
+
+ vals = talloc_zero_array(mem_ctx, struct registry_value, num_names);
+ if (vals == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ for (n=0; n < num_names; n++) {
+ for (i=0; i < regval_ctr_numvals(key->values); i++) {
+ struct regval_blob *blob;
+ blob = regval_ctr_specific_value(key->values, i);
+ if (strequal(regval_name(blob), names[n])) {
+ struct registry_value *v;
+ err = reg_enumvalue(mem_ctx, key, i, NULL, &v);
+ if (!W_ERROR_IS_OK(err)) {
+ return err;
+ }
+ vals[n] = *v;
+ found++;
+ }
+ }
+ }
+
+ *pvals = vals;
+ *pnum_vals = found;
+
+ return WERR_OK;
+}
+
+WERROR reg_queryinfokey(struct registry_key *key, uint32_t *num_subkeys,
+ uint32_t *max_subkeylen, uint32_t *max_subkeysize,
+ uint32_t *num_values, uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize, uint32_t *secdescsize,
+ NTTIME *last_changed_time)
+{
+ uint32_t i, max_size;
+ size_t max_len;
+ TALLOC_CTX *mem_ctx;
+ WERROR err;
+ struct security_descriptor *secdesc;
+
+ if (!(key->key->access_granted & KEY_QUERY_VALUE)) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (!W_ERROR_IS_OK(fill_subkey_cache(key)) ||
+ !W_ERROR_IS_OK(fill_value_cache(key))) {
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ max_len = 0;
+ for (i=0; i< regsubkey_ctr_numkeys(key->subkeys); i++) {
+ max_len = MAX(max_len,
+ strlen(regsubkey_ctr_specific_key(key->subkeys, i)));
+ }
+
+ *num_subkeys = regsubkey_ctr_numkeys(key->subkeys);
+ *max_subkeylen = max_len;
+ *max_subkeysize = 0; /* Class length? */
+
+ max_len = 0;
+ max_size = 0;
+ for (i=0; i < regval_ctr_numvals(key->values); i++) {
+ struct regval_blob *blob;
+ blob = regval_ctr_specific_value(key->values, i);
+ max_len = MAX(max_len, strlen(regval_name(blob)));
+ max_size = MAX(max_size, regval_size(blob));
+ }
+
+ *num_values = regval_ctr_numvals(key->values);
+ *max_valnamelen = max_len;
+ *max_valbufsize = max_size;
+
+ if (!(mem_ctx = talloc_new(key))) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ err = regkey_get_secdesc(mem_ctx, key->key, &secdesc);
+ if (!W_ERROR_IS_OK(err)) {
+ TALLOC_FREE(mem_ctx);
+ return err;
+ }
+
+ *secdescsize = ndr_size_security_descriptor(secdesc, 0);
+ TALLOC_FREE(mem_ctx);
+
+ *last_changed_time = 0;
+
+ return WERR_OK;
+}
+
+WERROR reg_createkey(TALLOC_CTX *ctx, struct registry_key *parent,
+ const char *subkeypath, uint32_t desired_access,
+ struct registry_key **pkey,
+ enum winreg_CreateAction *paction)
+{
+ struct registry_key *key = parent;
+ TALLOC_CTX *mem_ctx;
+ char *path, *end;
+ WERROR err;
+ uint32_t access_granted;
+
+ mem_ctx = talloc_new(ctx);
+ if (mem_ctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ path = talloc_strdup(mem_ctx, subkeypath);
+ if (path == NULL) {
+ err = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ err = regdb_transaction_start();
+ if (!W_ERROR_IS_OK(err)) {
+ DEBUG(0, ("reg_createkey: failed to start transaction: %s\n",
+ win_errstr(err)));
+ goto done;
+ }
+
+ while ((end = strchr(path, '\\')) != NULL) {
+ struct registry_key *tmp;
+ enum winreg_CreateAction action;
+
+ *end = '\0';
+
+ err = reg_createkey(mem_ctx, key, path,
+ KEY_ENUMERATE_SUB_KEYS, &tmp, &action);
+ if (!W_ERROR_IS_OK(err)) {
+ goto trans_done;
+ }
+
+ if (key != parent) {
+ TALLOC_FREE(key);
+ }
+
+ key = tmp;
+ path = end+1;
+ }
+
+ /*
+ * At this point, "path" contains the one-element subkey of "key". We
+ * can try to open it.
+ */
+
+ err = reg_openkey(ctx, key, path, desired_access, pkey);
+ if (W_ERROR_IS_OK(err)) {
+ if (paction != NULL) {
+ *paction = REG_OPENED_EXISTING_KEY;
+ }
+ goto trans_done;
+ }
+
+ if (!W_ERROR_EQUAL(err, WERR_FILE_NOT_FOUND)) {
+ /*
+ * Something but "notfound" has happened, so bail out
+ */
+ goto trans_done;
+ }
+
+ /*
+ * We may (e.g. in the iteration) have opened the key with ENUM_SUBKEY.
+ * Instead of re-opening the key with CREATE_SUB_KEY, we simply
+ * duplicate the access check here and skip the expensive full open.
+ */
+ if (!regkey_access_check(key->key, KEY_CREATE_SUB_KEY, &access_granted,
+ key->token))
+ {
+ err = WERR_ACCESS_DENIED;
+ goto trans_done;
+ }
+
+ /*
+ * Actually create the subkey
+ */
+
+ err = create_reg_subkey(key->key, path);
+ if (!W_ERROR_IS_OK(err)) {
+ goto trans_done;
+ }
+
+ /*
+ * Now open the newly created key
+ */
+
+ err = reg_openkey(ctx, key, path, desired_access, pkey);
+ if (W_ERROR_IS_OK(err) && (paction != NULL)) {
+ *paction = REG_CREATED_NEW_KEY;
+ }
+
+trans_done:
+ if (W_ERROR_IS_OK(err)) {
+ err = regdb_transaction_commit();
+ if (!W_ERROR_IS_OK(err)) {
+ DEBUG(0, ("reg_createkey: Error committing transaction: %s\n", win_errstr(err)));
+ }
+ } else {
+ WERROR err1 = regdb_transaction_cancel();
+ if (!W_ERROR_IS_OK(err1)) {
+ DEBUG(0, ("reg_createkey: Error cancelling transaction: %s\n", win_errstr(err1)));
+ }
+ }
+
+ done:
+ TALLOC_FREE(mem_ctx);
+ return err;
+}
+
+static WERROR reg_deletekey_internal(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *path, bool lazy)
+{
+ WERROR err;
+ char *name, *end;
+ struct registry_key *key;
+ name = talloc_strdup(mem_ctx, path);
+ if (name == NULL) {
+ err = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ /* no subkeys - proceed with delete */
+ end = strrchr(name, '\\');
+ if (end != NULL) {
+ *end = '\0';
+
+ err = reg_openkey(mem_ctx, parent, name,
+ KEY_CREATE_SUB_KEY, &key);
+ W_ERROR_NOT_OK_GOTO_DONE(err);
+
+ parent = key;
+ name = end+1;
+ }
+
+ if (name[0] == '\0') {
+ err = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ err = delete_reg_subkey(parent->key, name, lazy);
+
+done:
+ return err;
+}
+
+WERROR reg_deletekey(struct registry_key *parent, const char *path)
+{
+ WERROR err;
+ struct registry_key *key;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ /* check if the key has subkeys */
+ err = reg_openkey(mem_ctx, parent, path, REG_KEY_READ, &key);
+ W_ERROR_NOT_OK_GOTO_DONE(err);
+
+ err = regdb_transaction_start();
+ if (!W_ERROR_IS_OK(err)) {
+ DEBUG(0, ("reg_deletekey: Error starting transaction: %s\n",
+ win_errstr(err)));
+ goto done;
+ }
+
+ err = fill_subkey_cache(key);
+ if (!W_ERROR_IS_OK(err)) {
+ goto trans_done;
+ }
+
+ if (regsubkey_ctr_numkeys(key->subkeys) > 0) {
+ err = WERR_ACCESS_DENIED;
+ goto trans_done;
+ }
+ err = reg_deletekey_internal(mem_ctx, parent, path, false);
+
+trans_done:
+ if (W_ERROR_IS_OK(err)) {
+ err = regdb_transaction_commit();
+ if (!W_ERROR_IS_OK(err)) {
+ DEBUG(0, ("reg_deletekey: Error committing transaction: %s\n", win_errstr(err)));
+ }
+ } else {
+ WERROR err1 = regdb_transaction_cancel();
+ if (!W_ERROR_IS_OK(err1)) {
+ DEBUG(0, ("reg_deletekey: Error cancelling transaction: %s\n", win_errstr(err1)));
+ }
+ }
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return err;
+}
+
+
+WERROR reg_setvalue(struct registry_key *key, const char *name,
+ const struct registry_value *val)
+{
+ struct regval_blob *existing;
+ WERROR err;
+ int res;
+
+ if (!(key->key->access_granted & KEY_SET_VALUE)) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ err = regdb_transaction_start();
+ if (!W_ERROR_IS_OK(err)) {
+ DEBUG(0, ("reg_setvalue: Failed to start transaction: %s\n",
+ win_errstr(err)));
+ return err;
+ }
+
+ err = fill_value_cache(key);
+ if (!W_ERROR_IS_OK(err)) {
+ DEBUG(0, ("reg_setvalue: Error filling value cache: %s\n", win_errstr(err)));
+ goto done;
+ }
+
+ existing = regval_ctr_getvalue(key->values, name);
+
+ if ((existing != NULL) &&
+ (regval_size(existing) == val->data.length) &&
+ (memcmp(regval_data_p(existing), val->data.data,
+ val->data.length) == 0))
+ {
+ err = WERR_OK;
+ goto done;
+ }
+
+ res = regval_ctr_addvalue(key->values, name, val->type,
+ val->data.data, val->data.length);
+
+ if (res == 0) {
+ TALLOC_FREE(key->values);
+ err = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ if (!store_reg_values(key->key, key->values)) {
+ TALLOC_FREE(key->values);
+ DEBUG(0, ("reg_setvalue: store_reg_values failed\n"));
+ err = WERR_REGISTRY_IO_FAILED;
+ goto done;
+ }
+
+ err = WERR_OK;
+
+done:
+ if (W_ERROR_IS_OK(err)) {
+ err = regdb_transaction_commit();
+ if (!W_ERROR_IS_OK(err)) {
+ DEBUG(0, ("reg_setvalue: Error committing transaction: %s\n", win_errstr(err)));
+ }
+ } else {
+ WERROR err1 = regdb_transaction_cancel();
+ if (!W_ERROR_IS_OK(err1)) {
+ DEBUG(0, ("reg_setvalue: Error cancelling transaction: %s\n", win_errstr(err1)));
+ }
+ }
+
+ return err;
+}
+
+static WERROR reg_value_exists(struct registry_key *key, const char *name)
+{
+ struct regval_blob *blob;
+
+ blob = regval_ctr_getvalue(key->values, name);
+
+ if (blob == NULL) {
+ return WERR_FILE_NOT_FOUND;
+ } else {
+ return WERR_OK;
+ }
+}
+
+WERROR reg_deletevalue(struct registry_key *key, const char *name)
+{
+ WERROR err;
+
+ if (!(key->key->access_granted & KEY_SET_VALUE)) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ err = regdb_transaction_start();
+ if (!W_ERROR_IS_OK(err)) {
+ DEBUG(0, ("reg_deletevalue: Failed to start transaction: %s\n",
+ win_errstr(err)));
+ return err;
+ }
+
+ err = fill_value_cache(key);
+ if (!W_ERROR_IS_OK(err)) {
+ DEBUG(0, ("reg_deletevalue; Error filling value cache: %s\n",
+ win_errstr(err)));
+ goto done;
+ }
+
+ err = reg_value_exists(key, name);
+ if (!W_ERROR_IS_OK(err)) {
+ goto done;
+ }
+
+ regval_ctr_delvalue(key->values, name);
+
+ if (!store_reg_values(key->key, key->values)) {
+ TALLOC_FREE(key->values);
+ err = WERR_REGISTRY_IO_FAILED;
+ DEBUG(0, ("reg_deletevalue: store_reg_values failed\n"));
+ goto done;
+ }
+
+ err = WERR_OK;
+
+done:
+ if (W_ERROR_IS_OK(err)) {
+ err = regdb_transaction_commit();
+ if (!W_ERROR_IS_OK(err)) {
+ DEBUG(0, ("reg_deletevalue: Error committing transaction: %s\n", win_errstr(err)));
+ }
+ } else {
+ WERROR err1 = regdb_transaction_cancel();
+ if (!W_ERROR_IS_OK(err1)) {
+ DEBUG(0, ("reg_deletevalue: Error cancelling transaction: %s\n", win_errstr(err1)));
+ }
+ }
+
+ return err;
+}
+
+WERROR reg_getkeysecurity(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ struct security_descriptor **psecdesc)
+{
+ return regkey_get_secdesc(mem_ctx, key->key, psecdesc);
+}
+
+WERROR reg_setkeysecurity(struct registry_key *key,
+ struct security_descriptor *psecdesc)
+{
+ return regkey_set_secdesc(key->key, psecdesc);
+}
+
+WERROR reg_getversion(uint32_t *version)
+{
+ if (version == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ *version = 0x00000005; /* Windows 2000 registry API version */
+ return WERR_OK;
+}
+
+/**********************************************************************
+ * Higher level utility functions
+ **********************************************************************/
+
+WERROR reg_deleteallvalues(struct registry_key *key)
+{
+ WERROR err;
+ uint32_t i;
+
+ if (!(key->key->access_granted & KEY_SET_VALUE)) {
+ return WERR_ACCESS_DENIED;
+ }
+
+ if (!W_ERROR_IS_OK(err = fill_value_cache(key))) {
+ return err;
+ }
+
+ for (i=0; i < regval_ctr_numvals(key->values); i++) {
+ struct regval_blob *blob;
+ blob = regval_ctr_specific_value(key->values, i);
+ regval_ctr_delvalue(key->values, regval_name(blob));
+ }
+
+ if (!store_reg_values(key->key, key->values)) {
+ TALLOC_FREE(key->values);
+ return WERR_REGISTRY_IO_FAILED;
+ }
+
+ return WERR_OK;
+}
+
+/*
+ * Utility function to delete a registry key with all its subkeys.
+ * Note that reg_deletekey returns ACCESS_DENIED when called on a
+ * key that has subkeys.
+ */
+static WERROR reg_deletekey_recursive_internal(struct registry_key *parent,
+ const char *path,
+ bool del_key, bool lazy)
+{
+ WERROR werr;
+ struct registry_key *key;
+ char *subkey_name = NULL;
+ uint32_t i;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ DEBUG(5, ("reg_deletekey_recursive_internal: deleting '%s' from '%s'\n",
+ path, parent->key->name));
+
+ /* recurse through subkeys first */
+ werr = reg_openkey(mem_ctx, parent, path, REG_KEY_ALL, &key);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(3, ("reg_deletekey_recursive_internal: error opening "
+ "subkey '%s' of '%s': '%s'\n",
+ path, parent->key->name, win_errstr(werr)));
+ goto done;
+ }
+
+ werr = fill_subkey_cache(key);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ /*
+ * loop from top to bottom for performance:
+ * this way, we need to rehash the regsubkey containers less
+ */
+ for (i = regsubkey_ctr_numkeys(key->subkeys) ; i > 0; i--) {
+ subkey_name = regsubkey_ctr_specific_key(key->subkeys, i-1);
+ werr = reg_deletekey_recursive_internal(key, subkey_name, true, del_key);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+ }
+
+ if (del_key) {
+ /* now delete the actual key */
+ werr = reg_deletekey_internal(mem_ctx, parent, path, lazy);
+ }
+
+done:
+
+ DEBUG(5, ("reg_deletekey_recursive_internal: done deleting '%s' from "
+ "'%s': %s\n",
+ path, parent->key->name, win_errstr(werr)));
+ TALLOC_FREE(mem_ctx);
+ return werr;
+}
+
+static WERROR reg_deletekey_recursive_trans(struct registry_key *parent,
+ const char *path,
+ bool del_key)
+{
+ WERROR werr;
+
+ werr = regdb_transaction_start();
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, ("reg_deletekey_recursive_trans: "
+ "error starting transaction: %s\n",
+ win_errstr(werr)));
+ return werr;
+ }
+
+ werr = reg_deletekey_recursive_internal(parent, path, del_key, false);
+
+ if (!W_ERROR_IS_OK(werr)) {
+ WERROR werr2;
+ DEBUG(W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND) ? 5 : 1,
+ (__location__ ": failed to delete key '%s' from key "
+ "'%s': %s\n", path, parent->key->name,
+ win_errstr(werr)));
+
+ werr2 = regdb_transaction_cancel();
+ if (!W_ERROR_IS_OK(werr2)) {
+ DEBUG(0, ("reg_deletekey_recursive_trans: "
+ "error cancelling transaction: %s\n",
+ win_errstr(werr2)));
+ /*
+ * return the original werr or the
+ * error from cancelling the transaction?
+ */
+ }
+ } else {
+ werr = regdb_transaction_commit();
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, ("reg_deletekey_recursive_trans: "
+ "error committing transaction: %s\n",
+ win_errstr(werr)));
+ } else {
+ DEBUG(5, ("reg_deletekey_recursive_trans: deleted key '%s' from '%s'\n",
+ path, parent->key->name));
+
+ }
+ }
+
+ return werr;
+}
+
+WERROR reg_deletekey_recursive(struct registry_key *parent,
+ const char *path)
+{
+ return reg_deletekey_recursive_trans(parent, path, true);
+}
+
+WERROR reg_deletesubkeys_recursive(struct registry_key *parent,
+ const char *path)
+{
+ return reg_deletekey_recursive_trans(parent, path, false);
+}
+
diff --git a/source3/registry/reg_api.h b/source3/registry/reg_api.h
new file mode 100644
index 0000000..535e4ff
--- /dev/null
+++ b/source3/registry/reg_api.h
@@ -0,0 +1,70 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Virtual Windows Registry Layer
+ *
+ * Copyright (C) Volker Lendecke 2006
+ * Copyright (C) Michael Adam 2007-2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _REG_API_H
+#define _REG_API_H
+
+WERROR reg_openhive(TALLOC_CTX *mem_ctx, const char *hive,
+ uint32_t desired_access,
+ const struct security_token *token,
+ struct registry_key **pkey);
+WERROR reg_openkey(TALLOC_CTX *mem_ctx, struct registry_key *parent,
+ const char *name, uint32_t desired_access,
+ struct registry_key **pkey);
+WERROR reg_enumkey(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ uint32_t idx, char **name, NTTIME *last_write_time);
+WERROR reg_enumvalue(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ uint32_t idx, char **pname, struct registry_value **pval);
+WERROR reg_queryvalue(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ const char *name, struct registry_value **pval);
+WERROR reg_querymultiplevalues(TALLOC_CTX *mem_ctx,
+ struct registry_key *key,
+ uint32_t num_names,
+ const char **names,
+ uint32_t *pnum_vals,
+ struct registry_value **pvals);
+WERROR reg_queryinfokey(struct registry_key *key, uint32_t *num_subkeys,
+ uint32_t *max_subkeylen, uint32_t *max_subkeysize,
+ uint32_t *num_values, uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize, uint32_t *secdescsize,
+ NTTIME *last_changed_time);
+WERROR reg_createkey(TALLOC_CTX *ctx, struct registry_key *parent,
+ const char *subkeypath, uint32_t desired_access,
+ struct registry_key **pkey,
+ enum winreg_CreateAction *paction);
+WERROR reg_deletekey(struct registry_key *parent, const char *path);
+WERROR reg_setvalue(struct registry_key *key, const char *name,
+ const struct registry_value *val);
+WERROR reg_deletevalue(struct registry_key *key, const char *name);
+WERROR reg_getkeysecurity(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ struct security_descriptor **psecdesc);
+WERROR reg_setkeysecurity(struct registry_key *key,
+ struct security_descriptor *psecdesc);
+WERROR reg_getversion(uint32_t *version);
+WERROR reg_deleteallvalues(struct registry_key *key);
+WERROR reg_deletekey_recursive(struct registry_key *parent,
+ const char *path);
+WERROR reg_deletesubkeys_recursive(struct registry_key *parent,
+ const char *path);
+
+
+#endif /* _REG_API_H */
diff --git a/source3/registry/reg_api_util.c b/source3/registry/reg_api_util.c
new file mode 100644
index 0000000..2eca74e
--- /dev/null
+++ b/source3/registry/reg_api_util.c
@@ -0,0 +1,84 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Volker Lendecke 2006
+ * Copyright (C) Michael Adam 2007-2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Higher level utility functions on top of reg_api.c
+ */
+
+#include "includes.h"
+#include "registry.h"
+#include "reg_api.h"
+#include "reg_api_util.h"
+#include "libcli/registry/util_reg.h"
+
+/**
+ * Utility function to open a complete registry path including the hive prefix.
+ */
+WERROR reg_open_path(TALLOC_CTX *mem_ctx, const char *orig_path,
+ uint32_t desired_access, const struct security_token *token,
+ struct registry_key **pkey)
+{
+ struct registry_key *hive, *key;
+ char *path, *p;
+ WERROR err;
+
+ if (!(path = SMB_STRDUP(orig_path))) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ p = strchr(path, '\\');
+
+ if ((p == NULL) || (p[1] == '\0')) {
+ /*
+ * No key behind the hive, just return the hive
+ */
+
+ err = reg_openhive(mem_ctx, path, desired_access, token,
+ &hive);
+ if (!W_ERROR_IS_OK(err)) {
+ SAFE_FREE(path);
+ return err;
+ }
+ SAFE_FREE(path);
+ *pkey = hive;
+ return WERR_OK;
+ }
+
+ *p = '\0';
+
+ err = reg_openhive(mem_ctx, path, KEY_ENUMERATE_SUB_KEYS, token,
+ &hive);
+ if (!W_ERROR_IS_OK(err)) {
+ SAFE_FREE(path);
+ return err;
+ }
+
+ err = reg_openkey(mem_ctx, hive, p+1, desired_access, &key);
+
+ TALLOC_FREE(hive);
+ SAFE_FREE(path);
+
+ if (!W_ERROR_IS_OK(err)) {
+ return err;
+ }
+
+ *pkey = key;
+ return WERR_OK;
+}
diff --git a/source3/registry/reg_api_util.h b/source3/registry/reg_api_util.h
new file mode 100644
index 0000000..67c77a0
--- /dev/null
+++ b/source3/registry/reg_api_util.h
@@ -0,0 +1,35 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Volker Lendecke 2006
+ * Copyright (C) Michael Adam 2007-2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Higher level utility functions on top of reg_api.c
+ */
+
+#ifndef _REG_API_UTIL_H
+#define _REG_API_UTIL_H
+
+/**
+ * Utility function to open a complete registry path including the hive prefix.
+ */
+WERROR reg_open_path(TALLOC_CTX *mem_ctx, const char *orig_path,
+ uint32_t desired_access, const struct security_token *token,
+ struct registry_key **pkey);
+
+#endif /* _REG_API_UTIL_H */
diff --git a/source3/registry/reg_backend_current_version.c b/source3/registry/reg_backend_current_version.c
new file mode 100644
index 0000000..adf304e
--- /dev/null
+++ b/source3/registry/reg_backend_current_version.c
@@ -0,0 +1,78 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2002-2005
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * CurrentVersion registry backend.
+ *
+ * This is a virtual overlay, dynamically presenting version information.
+ */
+
+#include "includes.h"
+#include "registry.h"
+#include "reg_util_internal.h"
+#include "reg_objects.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+extern struct registry_ops regdb_ops;
+
+#define KEY_CURRENT_VERSION_NORM "HKLM\\SOFTWARE\\MICROSOFT\\WINDOWS NT\\CURRENTVERSION"
+
+static int current_version_fetch_values(const char *key, struct regval_ctr *values)
+{
+ const char *sysroot_string = "c:\\Windows";
+ fstring sysversion;
+ char *path = NULL;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ path = talloc_strdup(ctx, key);
+ if (path == NULL) {
+ return -1;
+ }
+ path = normalize_reg_path(ctx, path);
+ if (path == NULL) {
+ return -1;
+ }
+
+ if (strncmp(path, KEY_CURRENT_VERSION_NORM, strlen(path)) != 0) {
+ return regdb_ops.fetch_values(key, values);
+ }
+
+ regval_ctr_addvalue_sz(values, "SystemRoot", sysroot_string);
+
+ fstr_sprintf(sysversion, "%d.%d", SAMBA_MAJOR_NBT_ANNOUNCE_VERSION,
+ SAMBA_MINOR_NBT_ANNOUNCE_VERSION);
+
+ regval_ctr_addvalue_sz(values, "CurrentVersion", sysversion);
+
+ return regval_ctr_numvals(values);
+}
+
+static int current_version_fetch_subkeys(const char *key,
+ struct regsubkey_ctr *subkey_ctr)
+{
+ return regdb_ops.fetch_subkeys(key, subkey_ctr);
+}
+
+struct registry_ops current_version_reg_ops = {
+ .fetch_values = current_version_fetch_values,
+ .fetch_subkeys = current_version_fetch_subkeys,
+};
diff --git a/source3/registry/reg_backend_db.c b/source3/registry/reg_backend_db.c
new file mode 100644
index 0000000..44b3b5f
--- /dev/null
+++ b/source3/registry/reg_backend_db.c
@@ -0,0 +1,2270 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2002-2005
+ * Copyright (C) Michael Adam 2007-2011
+ * Copyright (C) Gregor Beck 2011
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Implementation of internal registry database functions. */
+
+#include "includes.h"
+#include "system/filesys.h"
+#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"
+#include "util_tdb.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "../libcli/security/secdesc.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+#define REGDB_VERSION_KEYNAME "INFO/version"
+
+static struct db_context *regdb = NULL;
+static int regdb_refcount;
+
+static bool regdb_key_exists(struct db_context *db, const char *key);
+static WERROR regdb_fetch_keys_internal(struct db_context *db, const char *key,
+ struct regsubkey_ctr *ctr);
+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 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);
+
+static WERROR regdb_create_basekey(struct db_context *db, const char *key);
+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_bystring(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
+ a virtual registry db (e.g. printing), then you have to list the deepest path.
+ For example,"HKLM/SOFTWARE/Microsoft/Windows NT/CurrentVersion/Print"
+ allows the reg_db backend to handle everything up to
+ "HKLM/SOFTWARE/Microsoft/Windows NT/CurrentVersion" and then we'll hook
+ the reg_printing backend onto the last component of the path (see
+ KEY_PRINTING_2K in include/rpc_reg.h) --jerry */
+
+static const char *builtin_registry_paths[] = {
+ KEY_PRINTING_2K,
+ KEY_PCC,
+ KEY_PRINTING_PORTS,
+ KEY_PRINTING,
+ KEY_PRINTING "\\Forms",
+ KEY_PRINTING "\\Printers",
+ KEY_PRINTING "\\Environments\\Windows NT x86\\Print Processors\\winprint",
+ KEY_PRINTING "\\Environments\\Windows x64\\Print Processors\\winprint",
+ KEY_SHARES,
+ KEY_EVENTLOG,
+ KEY_SMBCONF,
+ KEY_PERFLIB,
+ KEY_PERFLIB_009,
+ KEY_GROUP_POLICY,
+ KEY_SAMBA_GROUP_POLICY,
+ KEY_GP_MACHINE_POLICY,
+ KEY_GP_MACHINE_WIN_POLICY,
+ KEY_HKCU,
+ KEY_GP_USER_POLICY,
+ KEY_GP_USER_WIN_POLICY,
+ "HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\GPExtensions",
+ "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Print\\Monitors",
+ KEY_PROD_OPTIONS,
+ "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\DefaultUserConfiguration",
+ KEY_TCPIP_PARAMS,
+ KEY_NETLOGON_PARAMS,
+ KEY_HKU,
+ KEY_HKCR,
+ KEY_HKPD,
+ KEY_HKPT,
+ NULL };
+
+struct builtin_regkey_value {
+ const char *path;
+ const char *valuename;
+ uint32_t type;
+ union {
+ const char *string;
+ uint32_t dw_value;
+ } data;
+};
+
+static struct builtin_regkey_value builtin_registry_values[] = {
+ { KEY_PRINTING_PORTS,
+ SAMBA_PRINTER_PORT_NAME, REG_SZ, { "" } },
+ { KEY_PRINTING_2K,
+ "DefaultSpoolDirectory", REG_SZ, { "C:\\Windows\\System32\\Spool\\Printers" } },
+ { KEY_EVENTLOG,
+ "DisplayName", REG_SZ, { "Event Log" } },
+ { KEY_EVENTLOG,
+ "ErrorControl", REG_DWORD, { (char*)0x00000001 } },
+ { NULL, NULL, 0, { NULL } }
+};
+
+static WERROR create_key_recursive(struct db_context *db,
+ char *path,
+ const char *subkey)
+{
+ WERROR werr;
+ char *p;
+
+ if (subkey == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (path == NULL) {
+ return regdb_create_basekey(db, subkey);
+ }
+
+ p = strrchr_m(path, '\\');
+
+ if (p == NULL) {
+ werr = create_key_recursive(db, NULL, path);
+ } else {
+ *p = '\0';
+ werr = create_key_recursive(db, path, p+1);
+ *p = '\\';
+ }
+
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ werr = regdb_create_subkey_internal(db, path, subkey);
+
+done:
+ return werr;
+}
+
+/**
+ * Initialize a key in the registry:
+ * create each component key of the specified path.
+ */
+static WERROR init_registry_key_internal(struct db_context *db,
+ const char *add_path)
+{
+ char *subkey, *key;
+ WERROR werr;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (add_path == NULL) {
+ werr = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ key = talloc_strdup(frame, add_path);
+
+ subkey = strrchr_m(key, '\\');
+ if (subkey == NULL) {
+ subkey = key;
+ key = NULL;
+ } else {
+ *subkey = '\0';
+ subkey++;
+ }
+
+ werr = create_key_recursive(db, key, subkey);
+
+done:
+ talloc_free(frame);
+ return werr;
+}
+
+struct init_registry_key_context {
+ const char *add_path;
+};
+
+static NTSTATUS init_registry_key_action(struct db_context *db,
+ void *private_data)
+{
+ struct init_registry_key_context *init_ctx =
+ (struct init_registry_key_context *)private_data;
+
+ return werror_to_ntstatus(init_registry_key_internal(
+ db, init_ctx->add_path));
+}
+
+/**
+ * Initialize a key in the registry:
+ * create each component key of the specified path,
+ * wrapped in one db transaction.
+ */
+WERROR init_registry_key(const char *add_path)
+{
+ struct init_registry_key_context init_ctx;
+
+ if (regdb_key_exists(regdb, add_path)) {
+ return WERR_OK;
+ }
+
+ init_ctx.add_path = add_path;
+
+ return regdb_trans_do(regdb,
+ init_registry_key_action,
+ &init_ctx);
+}
+
+/***********************************************************************
+ Open the registry data in the tdb
+ ***********************************************************************/
+
+static void regdb_ctr_add_value(struct regval_ctr *ctr,
+ struct builtin_regkey_value *value)
+{
+ switch(value->type) {
+ case REG_DWORD:
+ regval_ctr_addvalue(ctr, value->valuename, REG_DWORD,
+ (uint8_t *)&value->data.dw_value,
+ sizeof(uint32_t));
+ break;
+
+ case REG_SZ:
+ regval_ctr_addvalue_sz(ctr, value->valuename,
+ value->data.string);
+ break;
+
+ default:
+ DEBUG(0, ("regdb_ctr_add_value: invalid value type in "
+ "registry values [%d]\n", value->type));
+ }
+}
+
+static NTSTATUS init_registry_data_action(struct db_context *db,
+ void *private_data)
+{
+ NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct regval_ctr *values;
+ int i;
+
+ /* loop over all of the predefined paths and add each component */
+
+ for (i=0; builtin_registry_paths[i] != NULL; i++) {
+ if (regdb_key_exists(db, builtin_registry_paths[i])) {
+ continue;
+ }
+ status = werror_to_ntstatus(init_registry_key_internal(db,
+ builtin_registry_paths[i]));
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ }
+
+ /* loop over all of the predefined values and add each component */
+
+ for (i=0; builtin_registry_values[i].path != NULL; i++) {
+ WERROR werr;
+
+ werr = regval_ctr_init(frame, &values);
+ if (!W_ERROR_IS_OK(werr)) {
+ status = werror_to_ntstatus(werr);
+ goto done;
+ }
+
+ regdb_fetch_values_internal(db,
+ builtin_registry_values[i].path,
+ values);
+
+ /* preserve existing values across restarts. Only add new ones */
+
+ if (!regval_ctr_value_exists(values,
+ builtin_registry_values[i].valuename))
+ {
+ regdb_ctr_add_value(values,
+ &builtin_registry_values[i]);
+ status = regdb_store_values_internal(db,
+ builtin_registry_values[i].path,
+ values);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ }
+ TALLOC_FREE(values);
+ }
+
+ status = NT_STATUS_OK;
+
+done:
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+WERROR init_registry_data(void)
+{
+ WERROR werr;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct regval_ctr *values;
+ int i;
+
+ /*
+ * First, check for the existence of the needed keys and values.
+ * If all do already exist, we can save the writes.
+ */
+ for (i=0; builtin_registry_paths[i] != NULL; i++) {
+ if (!regdb_key_exists(regdb, builtin_registry_paths[i])) {
+ goto do_init;
+ }
+ }
+
+ for (i=0; builtin_registry_values[i].path != NULL; i++) {
+ werr = regval_ctr_init(frame, &values);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ regdb_fetch_values_internal(regdb,
+ builtin_registry_values[i].path,
+ values);
+ if (!regval_ctr_value_exists(values,
+ builtin_registry_values[i].valuename))
+ {
+ TALLOC_FREE(values);
+ goto do_init;
+ }
+
+ TALLOC_FREE(values);
+ }
+
+ werr = WERR_OK;
+ goto done;
+
+do_init:
+
+ /*
+ * There are potentially quite a few store operations which are all
+ * individually wrapped in tdb transactions. Wrapping them in a single
+ * transaction gives just a single transaction_commit() to actually do
+ * its fsync()s. See tdb/common/transaction.c for info about nested
+ * transaction behaviour.
+ */
+
+ werr = regdb_trans_do(regdb,
+ init_registry_data_action,
+ NULL);
+
+done:
+ TALLOC_FREE(frame);
+ return werr;
+}
+
+static int regdb_normalize_keynames_fn(struct db_record *rec,
+ void *private_data)
+{
+ 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;
+
+ key = dbwrap_record_get_key(rec);
+ if (key.dptr == NULL || key.dsize == 0) {
+ return 0;
+ }
+
+ 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 *)key.dptr,
+ "/",
+ "\\");
+
+ DEBUG(2, ("regdb_normalize_keynames_fn: Convert %s to %s\n",
+ (const char *)key.dptr,
+ keyname));
+
+ /* Delete the original record and store the normalized key */
+ status = dbwrap_record_delete(rec);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("regdb_normalize_keynames_fn: "
+ "tdb_delete for [%s] failed!\n",
+ (const char *)key.dptr));
+ return 1;
+ }
+
+ 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",
+ keyname));
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static WERROR regdb_store_regdb_version(struct db_context *db, uint32_t version)
+{
+ NTSTATUS status;
+ if (db == NULL) {
+ return WERR_CAN_NOT_COMPLETE;
+ }
+
+ status = dbwrap_trans_store_int32_bystring(db, REGDB_VERSION_KEYNAME,
+ version);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("regdb_store_regdb_version: error storing %s = %d: %s\n",
+ REGDB_VERSION_KEYNAME, version, nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ } else {
+ DEBUG(10, ("regdb_store_regdb_version: stored %s = %d\n",
+ REGDB_VERSION_KEYNAME, version));
+ return WERR_OK;
+ }
+}
+
+static WERROR regdb_upgrade_v1_to_v2(struct db_context *db)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ WERROR werr;
+
+ mem_ctx = talloc_stackframe();
+
+ status = dbwrap_traverse(db, regdb_normalize_keynames_fn, db, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ werr = WERR_REGISTRY_IO_FAILED;
+ goto done;
+ }
+
+ werr = regdb_store_regdb_version(db, REGDB_VERSION_V2);
+
+done:
+ talloc_free(mem_ctx);
+ return werr;
+}
+
+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 strlen((char *)d.dptr) == (d.dsize-1);
+}
+
+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);
+ if (!strupper_m(path)) {
+ goto done;
+ }
+
+ 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)
+{
+ 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 (tdb_data_is_empty(key)) {
+ return 0;
+ }
+
+ if (db == NULL) {
+ DEBUG(0, ("regdb_upgrade_v2_to_v3_fn: ERROR: "
+ "NULL db context handed in via private_data\n"));
+ return 1;
+ }
+
+ 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",
+ SSTR(key)));
+
+ status = dbwrap_record_delete(rec);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("regdb_upgrade_v2_to_v3: deleting [%.*s] "
+ "failed!\n", SSTR(key)));
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if ( tdb_data_is_cstr(key) &&
+ hive_info((char*)key.dptr) != NULL )
+ {
+ /*
+ * 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;
+ }
+
+ while (tdb_data_read_cstr(&pos, &subkey)) {
+ found_items++;
+
+ if (!upgrade_v2_to_v3_check_subkeylist(db, path, subkey))
+ {
+ 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;
+}
+
+static WERROR regdb_upgrade_v2_to_v3(struct db_context *db)
+{
+ NTSTATUS status;
+ WERROR werr;
+
+ status = dbwrap_traverse(db, regdb_upgrade_v2_to_v3_fn, db, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ werr = WERR_REGISTRY_IO_FAILED;
+ goto done;
+ }
+
+ werr = regdb_store_regdb_version(db, REGDB_VERSION_V3);
+
+done:
+ return werr;
+}
+
+/***********************************************************************
+ Open the registry database
+ ***********************************************************************/
+
+WERROR regdb_init(void)
+{
+ int32_t vers_id;
+ WERROR werr;
+ NTSTATUS status;
+ char *db_path;
+
+ if (regdb) {
+ DEBUG(10, ("regdb_init: incrementing refcount (%d->%d)\n",
+ regdb_refcount, regdb_refcount+1));
+ regdb_refcount++;
+ return WERR_OK;
+ }
+
+ /*
+ * Clustered Samba can only work as root because we need messaging to
+ * talk to ctdb which only works as root.
+ */
+ if (!uid_wrapper_enabled() && lp_clustering() && geteuid() != 0) {
+ DBG_ERR("Cluster mode requires running as root.\n");
+ return WERR_ACCESS_DENIED;
+ }
+
+ db_path = state_path(talloc_tos(), "registry.tdb");
+ if (db_path == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ regdb = db_open(NULL, db_path, 0,
+ REG_TDB_FLAGS, O_RDWR, 0600,
+ DBWRAP_LOCK_ORDER_1, REG_DBWRAP_FLAGS);
+ if (!regdb) {
+ regdb = db_open(NULL, db_path, 0,
+ REG_TDB_FLAGS, O_RDWR|O_CREAT, 0600,
+ DBWRAP_LOCK_ORDER_1, REG_DBWRAP_FLAGS);
+ if (!regdb) {
+ werr = ntstatus_to_werror(map_nt_error_from_unix(errno));
+ DEBUG(1,("regdb_init: Failed to open registry %s (%s)\n",
+ db_path, strerror(errno) ));
+ TALLOC_FREE(db_path);
+ 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)));
+ TALLOC_FREE(db_path);
+ return werr;
+ }
+
+ DEBUG(10,("regdb_init: Successfully created registry tdb\n"));
+ }
+ TALLOC_FREE(db_path);
+
+ regdb_refcount = 1;
+ DEBUG(10, ("regdb_init: registry db opened. refcount reset (%d)\n",
+ regdb_refcount));
+
+ status = dbwrap_fetch_int32_bystring(regdb, REGDB_VERSION_KEYNAME,
+ &vers_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Reading registry version failed: %s, "
+ "initializing to version %d\n",
+ nt_errstr(status), 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;
+ }
+
+ if (vers_id == REGDB_CODE_VERSION) {
+ return WERR_OK;
+ }
+
+ 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, REGDB_CODE_VERSION));
+ return WERR_CAN_NOT_COMPLETE;
+ }
+
+ if (dbwrap_transaction_start(regdb) != 0) {
+ return WERR_REGISTRY_IO_FAILED;
+ }
+
+ 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)) {
+ dbwrap_transaction_cancel(regdb);
+ return werr;
+ }
+
+ vers_id = REGDB_VERSION_V2;
+ }
+
+ if (vers_id == REGDB_VERSION_V2) {
+ DEBUG(10, ("regdb_init: upgrading registry from version %d "
+ "to %d\n", REGDB_VERSION_V2, REGDB_VERSION_V3));
+
+ werr = regdb_upgrade_v2_to_v3(regdb);
+ if (!W_ERROR_IS_OK(werr)) {
+ dbwrap_transaction_cancel(regdb);
+ return werr;
+ }
+
+ vers_id = REGDB_VERSION_V3;
+ }
+
+ /* future upgrade code should go here */
+
+ if (dbwrap_transaction_commit(regdb) != 0) {
+ return WERR_REGISTRY_IO_FAILED;
+ }
+
+ return WERR_OK;
+}
+
+/***********************************************************************
+ Open the registry. Must already have been initialized by regdb_init()
+ ***********************************************************************/
+
+WERROR regdb_open( void )
+{
+ WERROR result;
+ char *db_path = NULL;
+ int saved_errno;
+
+ if ( regdb ) {
+ DEBUG(10, ("regdb_open: incrementing refcount (%d->%d)\n",
+ regdb_refcount, regdb_refcount+1));
+ regdb_refcount++;
+ result = WERR_OK;
+ goto done;
+ }
+
+ db_path = state_path(talloc_tos(), "registry.tdb");
+ if (db_path == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ become_root();
+
+ regdb = db_open(NULL, db_path, 0,
+ REG_TDB_FLAGS, O_RDWR, 0600,
+ DBWRAP_LOCK_ORDER_1, REG_DBWRAP_FLAGS);
+ saved_errno = errno;
+ unbecome_root();
+ if ( !regdb ) {
+ result = ntstatus_to_werror(map_nt_error_from_unix(saved_errno));
+ DEBUG(0,("regdb_open: Failed to open %s! (%s)\n",
+ db_path, strerror(saved_errno)));
+ goto done;
+ }
+
+ regdb_refcount = 1;
+ DEBUG(10, ("regdb_open: registry db opened. refcount reset (%d)\n",
+ regdb_refcount));
+
+ result = WERR_OK;
+done:
+ TALLOC_FREE(db_path);
+ return result;
+}
+
+/***********************************************************************
+ ***********************************************************************/
+
+int regdb_close( void )
+{
+ if (regdb_refcount == 0) {
+ return 0;
+ }
+
+ regdb_refcount--;
+
+ DEBUG(10, ("regdb_close: decrementing refcount (%d->%d)\n",
+ regdb_refcount+1, regdb_refcount));
+
+ if ( regdb_refcount > 0 )
+ return 0;
+
+ SMB_ASSERT( regdb_refcount >= 0 );
+
+ TALLOC_FREE(regdb);
+ return 0;
+}
+
+WERROR regdb_transaction_start(void)
+{
+ return (dbwrap_transaction_start(regdb) == 0) ?
+ WERR_OK : WERR_REGISTRY_IO_FAILED;
+}
+
+WERROR regdb_transaction_commit(void)
+{
+ return (dbwrap_transaction_commit(regdb) == 0) ?
+ WERR_OK : WERR_REGISTRY_IO_FAILED;
+}
+
+WERROR regdb_transaction_cancel(void)
+{
+ return (dbwrap_transaction_cancel(regdb) == 0) ?
+ WERR_OK : WERR_REGISTRY_IO_FAILED;
+}
+
+/***********************************************************************
+ return the tdb sequence number of the registry tdb.
+ this is an indicator for the content of the registry
+ having changed. it will change upon regdb_init, too, though.
+ ***********************************************************************/
+int regdb_get_seqnum(void)
+{
+ return dbwrap_get_seqnum(regdb);
+}
+
+
+static WERROR regdb_delete_key_with_prefix(struct db_context *db,
+ const char *keyname,
+ const char *prefix)
+{
+ char *path;
+ WERROR werr = WERR_NOT_ENOUGH_MEMORY;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ if (keyname == NULL) {
+ werr = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (prefix == NULL) {
+ path = discard_const_p(char, keyname);
+ } else {
+ path = talloc_asprintf(mem_ctx, "%s\\%s", prefix, keyname);
+ if (path == NULL) {
+ goto done;
+ }
+ }
+
+ path = normalize_reg_path(mem_ctx, path);
+ if (path == NULL) {
+ goto done;
+ }
+
+ werr = ntstatus_to_werror(dbwrap_purge_bystring(db, path));
+
+done:
+ talloc_free(mem_ctx);
+ return werr;
+}
+
+
+static WERROR regdb_delete_values(struct db_context *db, const char *keyname)
+{
+ return regdb_delete_key_with_prefix(db, keyname, REG_VALUE_PREFIX);
+}
+
+static WERROR regdb_delete_secdesc(struct db_context *db, const char *keyname)
+{
+ return regdb_delete_key_with_prefix(db, keyname, REG_SECDESC_PREFIX);
+}
+
+static WERROR regdb_delete_subkeylist(struct db_context *db, const char *keyname)
+{
+ return regdb_delete_key_with_prefix(db, keyname, NULL);
+}
+
+
+static WERROR regdb_delete_key_lists(struct db_context *db, const char *keyname)
+{
+ WERROR werr;
+
+ werr = regdb_delete_values(db, keyname);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(1, (__location__ " Deleting %s\\%s failed: %s\n",
+ REG_VALUE_PREFIX, keyname, win_errstr(werr)));
+ goto done;
+ }
+
+ werr = regdb_delete_secdesc(db, keyname);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(1, (__location__ " Deleting %s\\%s failed: %s\n",
+ REG_SECDESC_PREFIX, keyname, win_errstr(werr)));
+ goto done;
+ }
+
+ werr = regdb_delete_subkeylist(db, keyname);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(1, (__location__ " Deleting %s failed: %s\n",
+ keyname, win_errstr(werr)));
+ goto done;
+ }
+
+done:
+ return werr;
+}
+
+/***********************************************************************
+ Add subkey strings to the registry tdb under a defined key
+ fmt is the same format as tdb_pack except this function only supports
+ fstrings
+ ***********************************************************************/
+
+static WERROR regdb_store_keys_internal2(struct db_context *db,
+ const char *key,
+ struct regsubkey_ctr *ctr)
+{
+ TDB_DATA dbuf;
+ uint8_t *buffer = NULL;
+ uint32_t i = 0;
+ uint32_t len, buflen;
+ uint32_t num_subkeys = regsubkey_ctr_numkeys(ctr);
+ char *keyname = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ WERROR werr;
+
+ if (!key) {
+ werr = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ keyname = talloc_strdup(ctx, key);
+ if (!keyname) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ keyname = normalize_reg_path(ctx, keyname);
+ if (!keyname) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ /* allocate some initial memory */
+
+ buffer = (uint8_t *)SMB_MALLOC(1024);
+ if (buffer == NULL) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ buflen = 1024;
+ len = 0;
+
+ /* store the number of subkeys */
+
+ len += tdb_pack(buffer+len, buflen-len, "d", num_subkeys);
+
+ /* pack all the strings */
+
+ for (i=0; i<num_subkeys; i++) {
+ size_t thistime;
+
+ thistime = tdb_pack(buffer+len, buflen-len, "f",
+ regsubkey_ctr_specific_key(ctr, i));
+ if (len+thistime > buflen) {
+ size_t thistime2;
+ /*
+ * tdb_pack hasn't done anything because of the short
+ * buffer, allocate extra space.
+ */
+ buffer = SMB_REALLOC_ARRAY(buffer, uint8_t,
+ (len+thistime)*2);
+ if(buffer == NULL) {
+ DEBUG(0, ("regdb_store_keys: Failed to realloc "
+ "memory of size [%u]\n",
+ (unsigned int)(len+thistime)*2));
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+ buflen = (len+thistime)*2;
+ thistime2 = tdb_pack(
+ buffer+len, buflen-len, "f",
+ regsubkey_ctr_specific_key(ctr, i));
+ if (thistime2 != thistime) {
+ DEBUG(0, ("tdb_pack failed\n"));
+ werr = WERR_CAN_NOT_COMPLETE;
+ goto done;
+ }
+ }
+ len += thistime;
+ }
+
+ /* finally write out the data */
+
+ dbuf.dptr = buffer;
+ dbuf.dsize = len;
+ werr = ntstatus_to_werror(dbwrap_store_bystring(db, keyname, dbuf,
+ TDB_REPLACE));
+
+done:
+ TALLOC_FREE(ctx);
+ SAFE_FREE(buffer);
+ return werr;
+}
+
+/**
+ * Utility function to store a new empty list of
+ * subkeys of given key specified as parent and subkey name
+ * (thereby creating the key).
+ * If the parent keyname is NULL, then the "subkey" is
+ * interpreted as a base key.
+ * If the subkey list does already exist, it is not modified.
+ *
+ * Must be called from within a transaction.
+ */
+static WERROR regdb_store_subkey_list(struct db_context *db, const char *parent,
+ const char *key)
+{
+ WERROR werr;
+ char *path = NULL;
+ struct regsubkey_ctr *subkeys = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (parent == NULL) {
+ path = talloc_strdup(frame, key);
+ } else {
+ path = talloc_asprintf(frame, "%s\\%s", parent, key);
+ }
+ if (!path) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ werr = regsubkey_ctr_init(frame, &subkeys);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ werr = regdb_fetch_keys_internal(db, path, subkeys);
+ if (W_ERROR_IS_OK(werr)) {
+ /* subkey list exists already - don't modify */
+ goto done;
+ }
+
+ werr = regsubkey_ctr_reinit(subkeys);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ /* create a record with 0 subkeys */
+ werr = regdb_store_keys_internal2(db, path, subkeys);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, ("regdb_store_keys: Failed to store new record for "
+ "key [%s]: %s\n", path, win_errstr(werr)));
+ goto done;
+ }
+
+done:
+ talloc_free(frame);
+ return werr;
+}
+
+/***********************************************************************
+ Store the new subkey record and create any child key records that
+ do not currently exist
+ ***********************************************************************/
+
+struct regdb_store_keys_context {
+ const char *key;
+ struct regsubkey_ctr *ctr;
+};
+
+static NTSTATUS regdb_store_keys_action(struct db_context *db,
+ void *private_data)
+{
+ struct regdb_store_keys_context *store_ctx;
+ WERROR werr;
+ int num_subkeys, i;
+ char *path = NULL;
+ struct regsubkey_ctr *old_subkeys = NULL;
+ char *oldkeyname = NULL;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ store_ctx = (struct regdb_store_keys_context *)private_data;
+
+ /*
+ * Re-fetch the old keys inside the transaction
+ */
+
+ werr = regsubkey_ctr_init(mem_ctx, &old_subkeys);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ werr = regdb_fetch_keys_internal(db, store_ctx->key, old_subkeys);
+ if (!W_ERROR_IS_OK(werr) &&
+ !W_ERROR_EQUAL(werr, WERR_NOT_FOUND))
+ {
+ goto done;
+ }
+
+ /*
+ * Make the store operation as safe as possible without transactions:
+ *
+ * (1) For each subkey removed from ctr compared with old_subkeys:
+ *
+ * (a) First delete the value db entry.
+ *
+ * (b) Next delete the secdesc db record.
+ *
+ * (c) Then delete the subkey list entry.
+ *
+ * (2) Now write the list of subkeys of the parent key,
+ * deleting removed entries and adding new ones.
+ *
+ * (3) Finally create the subkey list entries for the added keys.
+ *
+ * This way if we crash half-way in between deleting the subkeys
+ * and storing the parent's list of subkeys, no old data can pop up
+ * out of the blue when re-adding keys later on.
+ */
+
+ /* (1) delete removed keys' lists (values/secdesc/subkeys) */
+
+ num_subkeys = regsubkey_ctr_numkeys(old_subkeys);
+ for (i=0; i<num_subkeys; i++) {
+ oldkeyname = regsubkey_ctr_specific_key(old_subkeys, i);
+
+ if (regsubkey_ctr_key_exists(store_ctx->ctr, oldkeyname)) {
+ /*
+ * It's still around, don't delete
+ */
+ continue;
+ }
+
+ path = talloc_asprintf(mem_ctx, "%s\\%s", store_ctx->key,
+ oldkeyname);
+ if (!path) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ werr = regdb_delete_key_lists(db, path);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ TALLOC_FREE(path);
+ }
+
+ TALLOC_FREE(old_subkeys);
+
+ /* (2) store the subkey list for the parent */
+
+ werr = regdb_store_keys_internal2(db, store_ctx->key, store_ctx->ctr);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,("regdb_store_keys: Failed to store new subkey list "
+ "for parent [%s]: %s\n", store_ctx->key,
+ win_errstr(werr)));
+ goto done;
+ }
+
+ /* (3) now create records for any subkeys that don't already exist */
+
+ num_subkeys = regsubkey_ctr_numkeys(store_ctx->ctr);
+
+ for (i=0; i<num_subkeys; i++) {
+ const char *subkey;
+
+ subkey = regsubkey_ctr_specific_key(store_ctx->ctr, i);
+
+ werr = regdb_store_subkey_list(db, store_ctx->key, subkey);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+ }
+
+ /*
+ * Update the seqnum in the container to possibly
+ * prevent next read from going to disk
+ */
+ werr = regsubkey_ctr_set_seqnum(store_ctx->ctr, dbwrap_get_seqnum(db));
+
+done:
+ talloc_free(mem_ctx);
+ return werror_to_ntstatus(werr);
+}
+
+static bool regdb_store_keys_internal(struct db_context *db, const char *key,
+ struct regsubkey_ctr *ctr)
+{
+ int num_subkeys, old_num_subkeys, i;
+ struct regsubkey_ctr *old_subkeys = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ WERROR werr;
+ bool ret = false;
+ struct regdb_store_keys_context store_ctx;
+
+ if (!regdb_key_exists(db, key)) {
+ goto done;
+ }
+
+ /*
+ * fetch a list of the old subkeys so we can determine if anything has
+ * changed
+ */
+
+ werr = regsubkey_ctr_init(ctx, &old_subkeys);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,("regdb_store_keys: talloc() failure!\n"));
+ goto done;
+ }
+
+ werr = regdb_fetch_keys_internal(db, key, old_subkeys);
+ if (!W_ERROR_IS_OK(werr) &&
+ !W_ERROR_EQUAL(werr, WERR_NOT_FOUND))
+ {
+ goto done;
+ }
+
+ num_subkeys = regsubkey_ctr_numkeys(ctr);
+ old_num_subkeys = regsubkey_ctr_numkeys(old_subkeys);
+ if ((num_subkeys && old_num_subkeys) &&
+ (num_subkeys == old_num_subkeys)) {
+
+ for (i = 0; i < num_subkeys; i++) {
+ if (strcmp(regsubkey_ctr_specific_key(ctr, i),
+ regsubkey_ctr_specific_key(old_subkeys, i))
+ != 0)
+ {
+ break;
+ }
+ }
+ if (i == num_subkeys) {
+ /*
+ * Nothing changed, no point to even start a tdb
+ * transaction
+ */
+
+ ret = true;
+ goto done;
+ }
+ }
+
+ TALLOC_FREE(old_subkeys);
+
+ store_ctx.key = key;
+ store_ctx.ctr = ctr;
+
+ werr = regdb_trans_do(db,
+ regdb_store_keys_action,
+ &store_ctx);
+
+ ret = W_ERROR_IS_OK(werr);
+
+done:
+ TALLOC_FREE(ctx);
+
+ return ret;
+}
+
+static bool regdb_store_keys(const char *key, struct regsubkey_ctr *ctr)
+{
+ return regdb_store_keys_internal(regdb, key, ctr);
+}
+
+/**
+ * create a subkey of a given key
+ */
+
+struct regdb_create_subkey_context {
+ const char *key;
+ const char *subkey;
+};
+
+static NTSTATUS regdb_create_subkey_action(struct db_context *db,
+ void *private_data)
+{
+ WERROR werr;
+ struct regdb_create_subkey_context *create_ctx;
+ struct regsubkey_ctr *subkeys;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ create_ctx = (struct regdb_create_subkey_context *)private_data;
+
+ werr = regsubkey_ctr_init(mem_ctx, &subkeys);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ werr = regdb_fetch_keys_internal(db, create_ctx->key, subkeys);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ werr = regsubkey_ctr_addkey(subkeys, create_ctx->subkey);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ werr = regdb_store_keys_internal2(db, create_ctx->key, subkeys);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, (__location__ " failed to store new subkey list for "
+ "parent key %s: %s\n", create_ctx->key,
+ win_errstr(werr)));
+ }
+
+ werr = regdb_store_subkey_list(db, create_ctx->key, create_ctx->subkey);
+
+done:
+ talloc_free(mem_ctx);
+ return werror_to_ntstatus(werr);
+}
+
+static WERROR regdb_create_subkey_internal(struct db_context *db,
+ const char *key,
+ const char *subkey)
+{
+ WERROR werr;
+ struct regsubkey_ctr *subkeys;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+ struct regdb_create_subkey_context create_ctx;
+
+ if (!regdb_key_exists(db, key)) {
+ werr = WERR_NOT_FOUND;
+ goto done;
+ }
+
+ werr = regsubkey_ctr_init(mem_ctx, &subkeys);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ werr = regdb_fetch_keys_internal(db, key, subkeys);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ if (regsubkey_ctr_key_exists(subkeys, subkey)) {
+ char *newkey;
+
+ newkey = talloc_asprintf(mem_ctx, "%s\\%s", key, subkey);
+ if (newkey == NULL) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ if (regdb_key_exists(db, newkey)) {
+ werr = WERR_OK;
+ goto done;
+ }
+ }
+
+ talloc_free(subkeys);
+
+ create_ctx.key = key;
+ create_ctx.subkey = subkey;
+
+ werr = regdb_trans_do(db,
+ regdb_create_subkey_action,
+ &create_ctx);
+
+done:
+ talloc_free(mem_ctx);
+ return werr;
+}
+
+static WERROR regdb_create_subkey(const char *key, const char *subkey)
+{
+ return regdb_create_subkey_internal(regdb, key, subkey);
+}
+
+/**
+ * create a base key
+ */
+
+struct regdb_create_basekey_context {
+ const char *key;
+};
+
+static NTSTATUS regdb_create_basekey_action(struct db_context *db,
+ void *private_data)
+{
+ WERROR werr;
+ struct regdb_create_basekey_context *create_ctx;
+
+ create_ctx = (struct regdb_create_basekey_context *)private_data;
+
+ werr = regdb_store_subkey_list(db, NULL, create_ctx->key);
+
+ return werror_to_ntstatus(werr);
+}
+
+static WERROR regdb_create_basekey(struct db_context *db, const char *key)
+{
+ WERROR werr;
+ struct regdb_create_subkey_context create_ctx;
+
+ create_ctx.key = key;
+
+ werr = regdb_trans_do(db,
+ regdb_create_basekey_action,
+ &create_ctx);
+
+ return werr;
+}
+
+/**
+ * create a subkey of a given key
+ */
+
+struct regdb_delete_subkey_context {
+ const char *key;
+ const char *subkey;
+ const char *path;
+ bool lazy;
+};
+
+static NTSTATUS regdb_delete_subkey_action(struct db_context *db,
+ void *private_data)
+{
+ WERROR werr;
+ struct regdb_delete_subkey_context *delete_ctx;
+ struct regsubkey_ctr *subkeys;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ delete_ctx = (struct regdb_delete_subkey_context *)private_data;
+
+ werr = regdb_delete_key_lists(db, delete_ctx->path);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ if (delete_ctx->lazy) {
+ goto done;
+ }
+
+ werr = regsubkey_ctr_init(mem_ctx, &subkeys);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ werr = regdb_fetch_keys_internal(db, delete_ctx->key, subkeys);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ werr = regsubkey_ctr_delkey(subkeys, delete_ctx->subkey);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ werr = regdb_store_keys_internal2(db, delete_ctx->key, subkeys);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, (__location__ " failed to store new subkey_list for "
+ "parent key %s: %s\n", delete_ctx->key,
+ win_errstr(werr)));
+ }
+
+done:
+ talloc_free(mem_ctx);
+ return werror_to_ntstatus(werr);
+}
+
+static WERROR regdb_delete_subkey(const char *key, const char *subkey, bool lazy)
+{
+ WERROR werr;
+ char *path;
+ struct regdb_delete_subkey_context delete_ctx;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ if (!regdb_key_exists(regdb, key)) {
+ werr = WERR_NOT_FOUND;
+ goto done;
+ }
+
+ path = talloc_asprintf(mem_ctx, "%s\\%s", key, subkey);
+ if (path == NULL) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ if (!regdb_key_exists(regdb, path)) {
+ werr = WERR_OK;
+ goto done;
+ }
+
+ delete_ctx.key = key;
+ delete_ctx.subkey = subkey;
+ delete_ctx.path = path;
+ delete_ctx.lazy = lazy;
+
+ werr = regdb_trans_do(regdb,
+ regdb_delete_subkey_action,
+ &delete_ctx);
+
+done:
+ talloc_free(mem_ctx);
+ return werr;
+}
+
+static TDB_DATA regdb_fetch_key_internal(struct db_context *db,
+ TALLOC_CTX *mem_ctx, const char *key)
+{
+ char *path = NULL;
+ TDB_DATA data;
+ NTSTATUS status;
+
+ path = normalize_reg_path(mem_ctx, key);
+ if (!path) {
+ return make_tdb_data(NULL, 0);
+ }
+
+ status = dbwrap_fetch_bystring(db, mem_ctx, path, &data);
+ if (!NT_STATUS_IS_OK(status)) {
+ data = tdb_null;
+ }
+
+ TALLOC_FREE(path);
+ return data;
+}
+
+
+/**
+ * Check for the existence of a key.
+ *
+ * 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)
+{
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+ 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;
+ }
+
+ path = normalize_reg_path(mem_ctx, key);
+ if (path == NULL) {
+ DEBUG(0, ("out of memory! (talloc failed)\n"));
+ goto done;
+ }
+
+ if (*path == '\0') {
+ goto done;
+ }
+
+ value = regdb_fetch_key_internal(db, mem_ctx, path);
+ 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;
+
+ 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);
+ return ret;
+}
+
+
+/***********************************************************************
+ Retrieve an array of strings containing subkeys. Memory should be
+ released by the caller.
+ ***********************************************************************/
+
+static WERROR regdb_fetch_keys_internal(struct db_context *db, const char *key,
+ struct regsubkey_ctr *ctr)
+{
+ WERROR werr;
+ uint32_t num_items;
+ uint8_t *buf;
+ uint32_t buflen, len;
+ uint32_t i;
+ fstring subkeyname;
+ TALLOC_CTX *frame = talloc_stackframe();
+ TDB_DATA value;
+ int seqnum[2], count;
+
+ DEBUG(11,("regdb_fetch_keys: Enter key => [%s]\n", key ? key : "NULL"));
+
+ if (!regdb_key_exists(db, key)) {
+ DEBUG(10, ("key [%s] not found\n", key));
+ werr = WERR_NOT_FOUND;
+ goto done;
+ }
+
+ werr = regsubkey_ctr_reinit(ctr);
+ W_ERROR_NOT_OK_GOTO_DONE(werr);
+
+ count = 0;
+ ZERO_STRUCT(value);
+ seqnum[0] = dbwrap_get_seqnum(db);
+
+ do {
+ count++;
+ TALLOC_FREE(value.dptr);
+ value = regdb_fetch_key_internal(db, frame, key);
+ seqnum[count % 2] = dbwrap_get_seqnum(db);
+
+ } while (seqnum[0] != seqnum[1]);
+
+ if (count > 1) {
+ DEBUG(5, ("regdb_fetch_keys_internal: it took %d attempts to "
+ "fetch key '%s' with constant seqnum\n",
+ count, key));
+ }
+
+ werr = regsubkey_ctr_set_seqnum(ctr, seqnum[0]);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ if (value.dsize == 0 || value.dptr == NULL) {
+ DEBUG(10, ("regdb_fetch_keys: no subkeys found for key [%s]\n",
+ key));
+ goto done;
+ }
+
+ buf = value.dptr;
+ buflen = value.dsize;
+ len = tdb_unpack( buf, buflen, "d", &num_items);
+ if (len == (uint32_t)-1) {
+ werr = WERR_NOT_FOUND;
+ goto done;
+ }
+
+ for (i=0; i<num_items; i++) {
+ int this_len;
+
+ this_len = tdb_unpack(buf+len, buflen-len, "f", subkeyname);
+ if (this_len == -1) {
+ DBG_WARNING("Invalid registry data, "
+ "tdb_unpack failed\n");
+ werr = WERR_INTERNAL_DB_CORRUPTION;
+ goto done;
+ }
+ len += this_len;
+ if (len < this_len) {
+ DBG_WARNING("Invalid registry data, "
+ "integer overflow\n");
+ werr = WERR_INTERNAL_DB_CORRUPTION;
+ goto done;
+ }
+
+ werr = regsubkey_ctr_addkey(ctr, subkeyname);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(5, ("regdb_fetch_keys: regsubkey_ctr_addkey "
+ "failed: %s\n", win_errstr(werr)));
+ num_items = 0;
+ goto done;
+ }
+ }
+
+ DEBUG(11,("regdb_fetch_keys: Exit [%d] items\n", num_items));
+
+done:
+ TALLOC_FREE(frame);
+ return werr;
+}
+
+static int regdb_fetch_keys(const char *key, struct regsubkey_ctr *ctr)
+{
+ WERROR werr;
+
+ werr = regdb_fetch_keys_internal(regdb, key, ctr);
+ if (!W_ERROR_IS_OK(werr)) {
+ return -1;
+ }
+
+ return regsubkey_ctr_numkeys(ctr);
+}
+
+/****************************************************************************
+ Unpack a list of registry values frem the TDB
+ ***************************************************************************/
+
+static int regdb_unpack_values(struct regval_ctr *values,
+ uint8_t *buf,
+ size_t buflen)
+{
+ int this_len;
+ size_t len = 0;
+ uint32_t type;
+ fstring valuename;
+ uint32_t size;
+ uint8_t *data_p;
+ uint32_t num_values = 0;
+ uint32_t i;
+
+ /* loop and unpack the rest of the registry values */
+
+ this_len = tdb_unpack(buf, buflen, "d", &num_values);
+ if (this_len == -1) {
+ DBG_WARNING("Invalid registry data, "
+ "tdb_unpack failed\n");
+ return -1;
+ }
+ len = this_len;
+
+ for ( i=0; i<num_values; i++ ) {
+ /* unpack the next regval */
+
+ type = REG_NONE;
+ size = 0;
+ data_p = NULL;
+ valuename[0] = '\0';
+ this_len = tdb_unpack(buf+len, buflen-len, "fdB",
+ valuename,
+ &type,
+ &size,
+ &data_p);
+ if (this_len == -1) {
+ DBG_WARNING("Invalid registry data, "
+ "tdb_unpack failed\n");
+ return -1;
+ }
+ len += this_len;
+ if (len < (size_t)this_len) {
+ DBG_WARNING("Invalid registry data, "
+ "integer overflow\n");
+ return -1;
+ }
+
+ regval_ctr_addvalue(values, valuename, type,
+ (uint8_t *)data_p, size);
+ SAFE_FREE(data_p); /* 'B' option to tdb_unpack does a malloc() */
+
+ DEBUG(10, ("regdb_unpack_values: value[%d]: name[%s] len[%d]\n",
+ i, valuename, size));
+ }
+
+ return len;
+}
+
+/****************************************************************************
+ Pack all values in all printer keys
+ ***************************************************************************/
+
+static int regdb_pack_values(struct regval_ctr *values, uint8_t *buf, int buflen)
+{
+ int len = 0;
+ int i;
+ struct regval_blob *val;
+ int num_values;
+
+ if ( !values )
+ return 0;
+
+ num_values = regval_ctr_numvals( values );
+
+ /* pack the number of values first */
+
+ len += tdb_pack( buf+len, buflen-len, "d", num_values );
+
+ /* loop over all values */
+
+ for ( i=0; i<num_values; i++ ) {
+ val = regval_ctr_specific_value( values, i );
+ len += tdb_pack(buf+len, buflen-len, "fdB",
+ regval_name(val),
+ regval_type(val),
+ regval_size(val),
+ regval_data_p(val) );
+ }
+
+ return len;
+}
+
+/***********************************************************************
+ Retrieve an array of strings containing subkeys. Memory should be
+ released by the caller.
+ ***********************************************************************/
+
+static int regdb_fetch_values_internal(struct db_context *db, const char* key,
+ struct regval_ctr *values)
+{
+ char *keystr = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ int ret = 0;
+ TDB_DATA value;
+ WERROR werr;
+ int seqnum[2], count;
+
+ DEBUG(10,("regdb_fetch_values: Looking for values of key [%s]\n", key));
+
+ if (!regdb_key_exists(db, key)) {
+ DEBUG(10, ("regb_fetch_values: key [%s] does not exist\n",
+ key));
+ ret = -1;
+ goto done;
+ }
+
+ keystr = talloc_asprintf(ctx, "%s\\%s", REG_VALUE_PREFIX, key);
+ if (!keystr) {
+ goto done;
+ }
+
+ ZERO_STRUCT(value);
+ count = 0;
+ seqnum[0] = dbwrap_get_seqnum(db);
+
+ do {
+ count++;
+ TALLOC_FREE(value.dptr);
+ value = regdb_fetch_key_internal(db, ctx, keystr);
+ seqnum[count % 2] = dbwrap_get_seqnum(db);
+ } while (seqnum[0] != seqnum[1]);
+
+ if (count > 1) {
+ DEBUG(5, ("regdb_fetch_values_internal: it took %d attempts "
+ "to fetch key '%s' with constant seqnum\n",
+ count, key));
+ }
+
+ werr = regval_ctr_set_seqnum(values, seqnum[0]);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ if (!value.dptr) {
+ /* all keys have zero values by default */
+ goto done;
+ }
+
+ ret = regdb_unpack_values(values, value.dptr, value.dsize);
+ if (ret == -1) {
+ DBG_WARNING("regdb_unpack_values failed\n");
+ }
+
+ ret = regval_ctr_numvals(values);
+
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+static int regdb_fetch_values(const char* key, struct regval_ctr *values)
+{
+ return regdb_fetch_values_internal(regdb, key, 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;
+ WERROR werr;
+
+ DEBUG(10,("regdb_store_values: Looking for values of key [%s]\n", key));
+
+ if (!regdb_key_exists(db, key)) {
+ status = NT_STATUS_NOT_FOUND;
+ goto done;
+ }
+
+ if (regval_ctr_numvals(values) == 0) {
+ werr = regdb_delete_values(db, key);
+ if (!W_ERROR_IS_OK(werr)) {
+ status = werror_to_ntstatus(werr);
+ goto done;
+ }
+
+ /*
+ * update the seqnum in the cache to prevent the next read
+ * from going to disk
+ */
+ werr = regval_ctr_set_seqnum(values, dbwrap_get_seqnum(db));
+ status = werror_to_ntstatus(werr);
+ goto done;
+ }
+
+ 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;
+ }
+
+ data.dptr = talloc_array(ctx, uint8_t, len);
+ data.dsize = len;
+
+ len = regdb_pack_values(values, data.dptr, data.dsize);
+
+ SMB_ASSERT( len == data.dsize );
+
+ 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;
+ }
+
+ status = dbwrap_fetch_bystring(db, ctx, keystr, &old_data);
+
+ if (NT_STATUS_IS_OK(status)
+ && (old_data.dptr != NULL)
+ && (old_data.dsize == data.dsize)
+ && (memcmp(old_data.dptr, data.dptr, data.dsize) == 0))
+ {
+ status = NT_STATUS_OK;
+ goto done;
+ }
+
+ status = dbwrap_trans_store_bystring(db, keystr, data, TDB_REPLACE);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("regdb_store_values_internal: error storing: %s\n", nt_errstr(status)));
+ goto done;
+ }
+
+ /*
+ * update the seqnum in the cache to prevent the next read
+ * from going to disk
+ */
+ werr = regval_ctr_set_seqnum(values, dbwrap_get_seqnum(db));
+ status = werror_to_ntstatus(werr);
+
+done:
+ TALLOC_FREE(ctx);
+ 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;
+}
+
+static bool regdb_store_values(const char *key, struct regval_ctr *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,
+ struct security_descriptor **psecdesc)
+{
+ char *tdbkey;
+ TDB_DATA data;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ WERROR err = WERR_OK;
+
+ DEBUG(10, ("regdb_get_secdesc: Getting secdesc of key [%s]\n", key));
+
+ if (!regdb_key_exists(regdb, key)) {
+ err = WERR_FILE_NOT_FOUND;
+ goto done;
+ }
+
+ tdbkey = talloc_asprintf(tmp_ctx, "%s\\%s", REG_SECDESC_PREFIX, key);
+ if (tdbkey == NULL) {
+ err = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ tdbkey = normalize_reg_path(tmp_ctx, tdbkey);
+ if (tdbkey == NULL) {
+ err = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ status = dbwrap_fetch_bystring(regdb, tmp_ctx, tdbkey, &data);
+ if (!NT_STATUS_IS_OK(status)) {
+ err = WERR_FILE_NOT_FOUND;
+ goto done;
+ }
+
+ status = unmarshall_sec_desc(mem_ctx, (uint8_t *)data.dptr, data.dsize,
+ psecdesc);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
+ err = WERR_NOT_ENOUGH_MEMORY;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ err = WERR_REGISTRY_CORRUPT;
+ }
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return err;
+}
+
+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)
+{
+ char *tdbkey;
+ NTSTATUS status;
+ TDB_DATA tdbdata;
+ struct regdb_set_secdesc_ctx *ctx =
+ (struct regdb_set_secdesc_ctx *)private_data;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ tdbkey = talloc_asprintf(frame, "%s\\%s", REG_SECDESC_PREFIX, ctx->key);
+ if (tdbkey == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ tdbkey = normalize_reg_path(frame, tdbkey);
+ if (tdbkey == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ if (ctx->secdesc == NULL) {
+ /* assuming a delete */
+ status = dbwrap_delete_bystring(db, tdbkey);
+ goto done;
+ }
+
+ 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_FILE_NOT_FOUND;
+ goto done;
+ }
+
+ ctx.key = key;
+ ctx.secdesc = secdesc;
+
+ err = regdb_trans_do(regdb, regdb_set_secdesc_action, &ctx);
+
+done:
+ return err;
+}
+
+static bool regdb_subkeys_need_update(struct regsubkey_ctr *subkeys)
+{
+ return (regdb_get_seqnum() != regsubkey_ctr_get_seqnum(subkeys));
+}
+
+static bool regdb_values_need_update(struct regval_ctr *values)
+{
+ return (regdb_get_seqnum() != regval_ctr_get_seqnum(values));
+}
+
+/*
+ * Table of function pointers for default access
+ */
+
+struct registry_ops regdb_ops = {
+ .fetch_subkeys = regdb_fetch_keys,
+ .fetch_values = regdb_fetch_values,
+ .store_subkeys = regdb_store_keys,
+ .store_values = regdb_store_values,
+ .create_subkey = regdb_create_subkey,
+ .delete_subkey = regdb_delete_subkey,
+ .get_secdesc = regdb_get_secdesc,
+ .set_secdesc = regdb_set_secdesc,
+ .subkeys_need_update = regdb_subkeys_need_update,
+ .values_need_update = regdb_values_need_update
+};
diff --git a/source3/registry/reg_backend_db.h b/source3/registry/reg_backend_db.h
new file mode 100644
index 0000000..6c7fb67
--- /dev/null
+++ b/source3/registry/reg_backend_db.h
@@ -0,0 +1,36 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2002-2005
+ * Copyright (C) Michael Adam 2007-2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _REG_BACKEND_DB_H
+#define _REG_BACKEND_DB_H
+
+#include "registry.h"
+
+WERROR init_registry_key(const char *add_path);
+WERROR init_registry_data(void);
+WERROR regdb_init(void);
+WERROR regdb_open( void );
+int regdb_close( void );
+WERROR regdb_transaction_start(void);
+WERROR regdb_transaction_commit(void);
+WERROR regdb_transaction_cancel(void);
+int regdb_get_seqnum(void);
+
+#endif /* _REG_BACKEND_DB_H */
diff --git a/source3/registry/reg_backend_hkpt_params.c b/source3/registry/reg_backend_hkpt_params.c
new file mode 100644
index 0000000..9cd6efe
--- /dev/null
+++ b/source3/registry/reg_backend_hkpt_params.c
@@ -0,0 +1,73 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2002-2005
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * HKPT parameters registry backend.
+ *
+ * This replaces the former dynamic hkpt parameters overlay.
+ */
+
+#include "includes.h"
+#include "registry.h"
+#include "reg_perfcount.h"
+#include "reg_objects.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+extern struct registry_ops regdb_ops;
+
+static int hkpt_params_fetch_values(const char *key, struct regval_ctr *regvals)
+{
+ uint32_t base_index;
+ uint32_t buffer_size;
+ char *buffer = NULL;
+
+ /* This is ALMOST the same as perflib_009_params, but HKPT has
+ a "Counters" entry instead of a "Counter" key. <Grrrr> */
+
+ base_index = reg_perfcount_get_base_index();
+ buffer_size = reg_perfcount_get_counter_names(base_index, &buffer);
+ regval_ctr_addvalue(regvals, "Counters", REG_MULTI_SZ, (uint8_t *)buffer,
+ buffer_size);
+
+ if(buffer_size > 0) {
+ SAFE_FREE(buffer);
+ }
+
+ buffer_size = reg_perfcount_get_counter_help(base_index, &buffer);
+ regval_ctr_addvalue(regvals, "Help", REG_MULTI_SZ, (uint8_t *)buffer, buffer_size);
+ if(buffer_size > 0) {
+ SAFE_FREE(buffer);
+ }
+
+ return regval_ctr_numvals( regvals );
+}
+
+static int hkpt_params_fetch_subkeys(const char *key,
+ struct regsubkey_ctr *subkey_ctr)
+{
+ return regdb_ops.fetch_subkeys(key, subkey_ctr);
+}
+
+struct registry_ops hkpt_params_reg_ops = {
+ .fetch_values = hkpt_params_fetch_values,
+ .fetch_subkeys = hkpt_params_fetch_subkeys,
+};
diff --git a/source3/registry/reg_backend_netlogon_params.c b/source3/registry/reg_backend_netlogon_params.c
new file mode 100644
index 0000000..d7cf3c6
--- /dev/null
+++ b/source3/registry/reg_backend_netlogon_params.c
@@ -0,0 +1,60 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2002-2005
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Netlogon parameters registry backend.
+ *
+ * This replaces the former dynamic netlogon parameters overlay.
+ */
+
+#include "includes.h"
+#include "registry.h"
+#include "reg_objects.h"
+#include "passdb.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+extern struct registry_ops regdb_ops;
+
+static int netlogon_params_fetch_values(const char *key, struct regval_ctr *regvals)
+{
+ uint32_t dwValue;
+
+ if (!pdb_get_account_policy(PDB_POLICY_REFUSE_MACHINE_PW_CHANGE, &dwValue)) {
+ dwValue = 0;
+ }
+
+ regval_ctr_addvalue(regvals, "RefusePasswordChange", REG_DWORD,
+ (uint8_t *)&dwValue, sizeof(dwValue));
+
+ return regval_ctr_numvals(regvals);
+}
+
+static int netlogon_params_fetch_subkeys(const char *key,
+ struct regsubkey_ctr *subkey_ctr)
+{
+ return regdb_ops.fetch_subkeys(key, subkey_ctr);
+}
+
+struct registry_ops netlogon_params_reg_ops = {
+ .fetch_values = netlogon_params_fetch_values,
+ .fetch_subkeys = netlogon_params_fetch_subkeys,
+};
diff --git a/source3/registry/reg_backend_perflib.c b/source3/registry/reg_backend_perflib.c
new file mode 100644
index 0000000..a46c574
--- /dev/null
+++ b/source3/registry/reg_backend_perflib.c
@@ -0,0 +1,110 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2002-2005
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * perflib registry backend.
+ *
+ * This is a virtual overlay, dynamically presenting perflib values.
+ */
+
+#include "includes.h"
+#include "registry.h"
+#include "reg_util_internal.h"
+#include "reg_perfcount.h"
+#include "reg_objects.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+extern struct registry_ops regdb_ops;
+
+#define KEY_PERFLIB_NORM "HKLM\\SOFTWARE\\MICROSOFT\\WINDOWS NT\\CURRENTVERSION\\PERFLIB"
+#define KEY_PERFLIB_009_NORM "HKLM\\SOFTWARE\\MICROSOFT\\WINDOWS NT\\CURRENTVERSION\\PERFLIB\\009"
+
+static int perflib_params(struct regval_ctr *regvals)
+{
+ int base_index = -1;
+ int last_counter = -1;
+ int last_help = -1;
+ int version = 0x00010001;
+
+ base_index = reg_perfcount_get_base_index();
+ regval_ctr_addvalue(regvals, "Base Index", REG_DWORD, (uint8_t *)&base_index, sizeof(base_index));
+ last_counter = reg_perfcount_get_last_counter(base_index);
+ regval_ctr_addvalue(regvals, "Last Counter", REG_DWORD, (uint8_t *)&last_counter, sizeof(last_counter));
+ last_help = reg_perfcount_get_last_help(last_counter);
+ regval_ctr_addvalue(regvals, "Last Help", REG_DWORD, (uint8_t *)&last_help, sizeof(last_help));
+ regval_ctr_addvalue(regvals, "Version", REG_DWORD, (uint8_t *)&version, sizeof(version));
+
+ return regval_ctr_numvals( regvals );
+}
+
+static int perflib_009_params(struct regval_ctr *regvals)
+{
+ int base_index;
+ int buffer_size;
+ char *buffer = NULL;
+
+ base_index = reg_perfcount_get_base_index();
+ buffer_size = reg_perfcount_get_counter_names(base_index, &buffer);
+ regval_ctr_addvalue(regvals, "Counter", REG_MULTI_SZ, (uint8_t *)buffer, buffer_size);
+ if(buffer_size > 0)
+ SAFE_FREE(buffer);
+ buffer_size = reg_perfcount_get_counter_help(base_index, &buffer);
+ regval_ctr_addvalue(regvals, "Help", REG_MULTI_SZ, (uint8_t *)buffer, buffer_size);
+ if(buffer_size > 0)
+ SAFE_FREE(buffer);
+
+ return regval_ctr_numvals( regvals );
+}
+
+static int perflib_fetch_values(const char *key, struct regval_ctr *regvals)
+{
+ char *path = NULL;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ path = talloc_strdup(ctx, key);
+ if (path == NULL) {
+ return -1;
+ }
+ path = normalize_reg_path(ctx, path);
+ if (path == NULL) {
+ return -1;
+ }
+
+ if (strncmp(path, KEY_PERFLIB_NORM, strlen(path)) == 0) {
+ return perflib_params(regvals);
+ } else if (strncmp(path, KEY_PERFLIB_009_NORM, strlen(path)) == 0) {
+ return perflib_009_params(regvals);
+ } else {
+ return 0;
+ }
+}
+
+static int perflib_fetch_subkeys(const char *key,
+ struct regsubkey_ctr *subkey_ctr)
+{
+ return regdb_ops.fetch_subkeys(key, subkey_ctr);
+}
+
+struct registry_ops perflib_reg_ops = {
+ .fetch_values = perflib_fetch_values,
+ .fetch_subkeys = perflib_fetch_subkeys,
+};
diff --git a/source3/registry/reg_backend_printing.c b/source3/registry/reg_backend_printing.c
new file mode 100644
index 0000000..3b5e7bf
--- /dev/null
+++ b/source3/registry/reg_backend_printing.c
@@ -0,0 +1,281 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2002-2005
+ * Copyright (c) Andreas Schneider <asn@samba.org> 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Implementation of registry virtual views for printing information */
+
+#include "includes.h"
+#include "registry.h"
+#include "reg_util_internal.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+extern struct registry_ops regdb_ops;
+
+/* registry paths used in the print_registry[] */
+#define KEY_CONTROL_PRINTERS "HKLM\\SYSTEM\\CURRENTCONTROLSET\\CONTROL\\PRINT\\PRINTERS"
+#define KEY_WINNT_PRINTERS "HKLM\\SOFTWARE\\MICROSOFT\\WINDOWS NT\\CURRENTVERSION\\PRINT\\PRINTERS"
+
+/* callback table for various registry paths below the ones we service in this module */
+
+struct reg_dyn_tree {
+ /* full key path in normalized form */
+ const char *path;
+
+ /* callbscks for fetch/store operations */
+ int ( *fetch_subkeys) ( const char *path, struct regsubkey_ctr *subkeys );
+ bool (*store_subkeys) ( const char *path, struct regsubkey_ctr *subkeys );
+ int (*fetch_values) ( const char *path, struct regval_ctr *values );
+ bool (*store_values) ( const char *path, struct regval_ctr *values );
+};
+
+/*********************************************************************
+ *********************************************************************
+ ** "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT/PRINTERS"
+ ** "HKLM/SOFTWARE/MICROSOFT/WINDOWS NT/CURRENTVERSION/PRINT/PRINTERS"
+ *********************************************************************
+ *********************************************************************/
+
+static char *create_printer_registry_path(TALLOC_CTX *mem_ctx, const char *key) {
+ char *path;
+ char *subkey = NULL;
+
+ path = talloc_strdup(mem_ctx, key);
+ if (path == NULL) {
+ return NULL;
+ }
+
+ path = normalize_reg_path(mem_ctx, path);
+ if (path == NULL) {
+ return NULL;
+ }
+
+ if (strncmp(path, KEY_CONTROL_PRINTERS, strlen(KEY_CONTROL_PRINTERS)) == 0) {
+ subkey = reg_remaining_path(mem_ctx, key + strlen(KEY_CONTROL_PRINTERS));
+ if (subkey == NULL) {
+ return NULL;
+ }
+ return talloc_asprintf(mem_ctx, "%s\\%s", KEY_WINNT_PRINTERS, subkey);
+ }
+
+ return NULL;
+}
+
+/*********************************************************************
+ *********************************************************************/
+
+static int key_printers_fetch_keys( const char *key, struct regsubkey_ctr *subkeys )
+{
+ TALLOC_CTX *ctx = talloc_tos();
+ char *printers_key;
+
+ printers_key = create_printer_registry_path(ctx, key);
+ if (printers_key == NULL) {
+ /* normalize on the 'HKLM\SOFTWARE\....\Print\Printers' key */
+ return regdb_ops.fetch_subkeys(KEY_WINNT_PRINTERS, subkeys);
+ }
+
+ return regdb_ops.fetch_subkeys(printers_key, subkeys);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static bool key_printers_store_keys( const char *key, struct regsubkey_ctr *subkeys )
+{
+ TALLOC_CTX *ctx = talloc_tos();
+ char *printers_key;
+
+ printers_key = create_printer_registry_path(ctx, key);
+ if (printers_key == NULL) {
+ /* normalize on the 'HKLM\SOFTWARE\....\Print\Printers' key */
+ return regdb_ops.store_subkeys(KEY_WINNT_PRINTERS, subkeys);
+ }
+
+ return regdb_ops.store_subkeys(printers_key, subkeys);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static int key_printers_fetch_values(const char *key, struct regval_ctr *values)
+{
+ TALLOC_CTX *ctx = talloc_tos();
+ char *printers_key;
+
+ printers_key = create_printer_registry_path(ctx, key);
+ if (printers_key == NULL) {
+ /* normalize on the 'HKLM\SOFTWARE\....\Print\Printers' key */
+ return regdb_ops.fetch_values(KEY_WINNT_PRINTERS, values);
+ }
+
+ return regdb_ops.fetch_values(printers_key, values);
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static bool key_printers_store_values(const char *key, struct regval_ctr *values)
+{
+ TALLOC_CTX *ctx = talloc_tos();
+ char *printers_key;
+
+ printers_key = create_printer_registry_path(ctx, key);
+ if (printers_key == NULL) {
+ /* normalize on the 'HKLM\SOFTWARE\....\Print\Printers' key */
+ return regdb_ops.store_values(KEY_WINNT_PRINTERS, values);
+ }
+
+ return regdb_ops.store_values(printers_key, values);
+}
+
+/**********************************************************************
+ *********************************************************************
+ ** Structure to hold dispatch table of ops for various printer keys.
+ ** Make sure to always store deeper keys along the same path first so
+ ** we ge a more specific match.
+ *********************************************************************
+ *********************************************************************/
+
+static struct reg_dyn_tree print_registry[] = {
+{ KEY_CONTROL_PRINTERS,
+ &key_printers_fetch_keys,
+ &key_printers_store_keys,
+ &key_printers_fetch_values,
+ &key_printers_store_values },
+
+{ NULL, NULL, NULL, NULL, NULL }
+};
+
+
+/**********************************************************************
+ *********************************************************************
+ ** Main reg_printing interface functions
+ *********************************************************************
+ *********************************************************************/
+
+/***********************************************************************
+ Lookup a key in the print_registry table, returning its index.
+ -1 on failure
+ **********************************************************************/
+
+static int match_registry_path(const char *key)
+{
+ int i;
+ char *path = NULL;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ if ( !key )
+ return -1;
+
+ path = talloc_strdup(ctx, key);
+ if (!path) {
+ return -1;
+ }
+ path = normalize_reg_path(ctx, path);
+ if (!path) {
+ return -1;
+ }
+
+ for ( i=0; print_registry[i].path; i++ ) {
+ if (strncmp( path, print_registry[i].path, strlen(print_registry[i].path) ) == 0 )
+ return i;
+ }
+
+ return -1;
+}
+
+/***********************************************************************
+ **********************************************************************/
+
+static int regprint_fetch_reg_keys( const char *key, struct regsubkey_ctr *subkeys )
+{
+ int i = match_registry_path( key );
+
+ if ( i == -1 )
+ return -1;
+
+ if ( !print_registry[i].fetch_subkeys )
+ return -1;
+
+ return print_registry[i].fetch_subkeys( key, subkeys );
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static bool regprint_store_reg_keys( const char *key, struct regsubkey_ctr *subkeys )
+{
+ int i = match_registry_path( key );
+
+ if ( i == -1 )
+ return False;
+
+ if ( !print_registry[i].store_subkeys )
+ return False;
+
+ return print_registry[i].store_subkeys( key, subkeys );
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static int regprint_fetch_reg_values(const char *key, struct regval_ctr *values)
+{
+ int i = match_registry_path( key );
+
+ if ( i == -1 )
+ return -1;
+
+ /* return 0 values by default since we know the key had
+ to exist because the client opened a handle */
+
+ if ( !print_registry[i].fetch_values )
+ return 0;
+
+ return print_registry[i].fetch_values( key, values );
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+static bool regprint_store_reg_values(const char *key, struct regval_ctr *values)
+{
+ int i = match_registry_path( key );
+
+ if ( i == -1 )
+ return False;
+
+ if ( !print_registry[i].store_values )
+ return False;
+
+ return print_registry[i].store_values( key, values );
+}
+
+/*
+ * Table of function pointers for accessing printing data
+ */
+
+struct registry_ops printing_ops = {
+ .fetch_subkeys = regprint_fetch_reg_keys,
+ .fetch_values = regprint_fetch_reg_values,
+ .store_subkeys = regprint_store_reg_keys,
+ .store_values = regprint_store_reg_values,
+};
diff --git a/source3/registry/reg_backend_prod_options.c b/source3/registry/reg_backend_prod_options.c
new file mode 100644
index 0000000..7bd3f32
--- /dev/null
+++ b/source3/registry/reg_backend_prod_options.c
@@ -0,0 +1,68 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2002-2005
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Product options registry backend.
+ *
+ * This replaces the former dynamic product options overlay.
+ */
+
+#include "includes.h"
+#include "registry.h"
+#include "reg_objects.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+extern struct registry_ops regdb_ops;
+
+static int prod_options_fetch_values(const char *key, struct regval_ctr *regvals)
+{
+ const char *value_ascii = "";
+
+ switch (lp_server_role()) {
+ case ROLE_DOMAIN_PDC:
+ case ROLE_DOMAIN_BDC:
+ case ROLE_IPA_DC:
+ value_ascii = "LanmanNT";
+ break;
+ case ROLE_STANDALONE:
+ value_ascii = "ServerNT";
+ break;
+ case ROLE_DOMAIN_MEMBER:
+ value_ascii = "WinNT";
+ break;
+ }
+
+ regval_ctr_addvalue_sz(regvals, "ProductType", value_ascii);
+
+ return regval_ctr_numvals( regvals );
+}
+
+static int prod_options_fetch_subkeys(const char *key,
+ struct regsubkey_ctr *subkey_ctr)
+{
+ return regdb_ops.fetch_subkeys(key, subkey_ctr);
+}
+
+struct registry_ops prod_options_reg_ops = {
+ .fetch_values = prod_options_fetch_values,
+ .fetch_subkeys = prod_options_fetch_subkeys,
+};
diff --git a/source3/registry/reg_backend_shares.c b/source3/registry/reg_backend_shares.c
new file mode 100644
index 0000000..ffe95a6
--- /dev/null
+++ b/source3/registry/reg_backend_shares.c
@@ -0,0 +1,165 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Implementation of registry virtual views for printing information */
+
+#include "includes.h"
+#include "registry.h"
+#include "reg_objects.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+/**********************************************************************
+ It is safe to assume that every registry path passed into one of
+ the exported functions here begins with KEY_SHARES else
+ these functions would have never been called. This is a small utility
+ function to strip the beginning of the path and make a copy that the
+ caller can modify. Note that the caller is responsible for releasing
+ the memory allocated here.
+ **********************************************************************/
+
+static char* trim_reg_path( const char *path )
+{
+ const char *p;
+ uint16_t key_len = strlen(KEY_SHARES);
+
+ /*
+ * sanity check...this really should never be True.
+ * It is only here to prevent us from accessing outside
+ * the path buffer in the extreme case.
+ */
+
+ if ( strlen(path) < key_len ) {
+ DEBUG(0,("trim_reg_path: Registry path too short! [%s]\n", path));
+ return NULL;
+ }
+
+ p = path + strlen( KEY_SHARES );
+
+ if ( *p == '\\' )
+ p++;
+
+ if ( *p )
+ return SMB_STRDUP(p);
+ else
+ return NULL;
+}
+
+/**********************************************************************
+ Enumerate registry subkey names given a registry path.
+ Caller is responsible for freeing memory to **subkeys
+ *********************************************************************/
+
+static int shares_subkey_info( const char *key, struct regsubkey_ctr *subkey_ctr )
+{
+ char *path;
+ bool top_level = False;
+ int num_subkeys = 0;
+
+ DEBUG(10, ("shares_subkey_info: key=>[%s]\n", key));
+
+ path = trim_reg_path( key );
+
+ /* check to see if we are dealing with the top level key */
+
+ if ( !path )
+ top_level = True;
+
+ if ( top_level ) {
+ num_subkeys = 1;
+ regsubkey_ctr_addkey( subkey_ctr, "Security" );
+ }
+#if 0
+ else
+ num_subkeys = handle_share_subpath( path, subkey_ctr, NULL );
+#endif
+
+ SAFE_FREE( path );
+
+ return num_subkeys;
+}
+
+/**********************************************************************
+ Enumerate registry values given a registry path.
+ Caller is responsible for freeing memory
+ *********************************************************************/
+
+static int shares_value_info(const char *key, struct regval_ctr *val)
+{
+ char *path;
+ bool top_level = False;
+ int num_values = 0;
+
+ DEBUG(10, ("shares_value_info: key=>[%s]\n", key));
+
+ path = trim_reg_path( key );
+
+ /* check to see if we are dealing with the top level key */
+
+ if ( !path )
+ top_level = True;
+
+ /* fill in values from the getprinterdata_printer_server() */
+ if ( top_level )
+ num_values = 0;
+#if 0
+ else
+ num_values = handle_printing_subpath( path, NULL, val );
+#endif
+
+ SAFE_FREE(path);
+
+ return num_values;
+}
+
+/**********************************************************************
+ Stub function which always returns failure since we don't want
+ people storing share information directly via registry calls
+ (for now at least)
+ *********************************************************************/
+
+static bool shares_store_subkey( const char *key, struct regsubkey_ctr *subkeys )
+{
+ return False;
+}
+
+/**********************************************************************
+ Stub function which always returns failure since we don't want
+ people storing share information directly via registry calls
+ (for now at least)
+ *********************************************************************/
+
+static bool shares_store_value(const char *key, struct regval_ctr *val)
+{
+ return False;
+}
+
+/*
+ * Table of function pointers for accessing printing data
+ */
+
+struct registry_ops shares_reg_ops = {
+ .fetch_subkeys = shares_subkey_info,
+ .fetch_values = shares_value_info,
+ .store_subkeys = shares_store_subkey,
+ .store_values = shares_store_value,
+};
+
+
diff --git a/source3/registry/reg_backend_smbconf.c b/source3/registry/reg_backend_smbconf.c
new file mode 100644
index 0000000..001a5f7
--- /dev/null
+++ b/source3/registry/reg_backend_smbconf.c
@@ -0,0 +1,110 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Volker Lendecke 2006
+ * Copyright (C) Michael Adam 2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "registry.h"
+#include "lib/privileges.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+extern struct registry_ops regdb_ops; /* these are the default */
+
+static int smbconf_fetch_keys( const char *key, struct regsubkey_ctr *subkey_ctr )
+{
+ return regdb_ops.fetch_subkeys(key, subkey_ctr);
+}
+
+static bool smbconf_store_keys( const char *key, struct regsubkey_ctr *subkeys )
+{
+ return regdb_ops.store_subkeys(key, subkeys);
+}
+
+static WERROR smbconf_create_subkey(const char *key, const char *subkey)
+{
+ return regdb_ops.create_subkey(key, subkey);
+}
+
+static WERROR smbconf_delete_subkey(const char *key, const char *subkey, bool lazy)
+{
+ return regdb_ops.delete_subkey(key, subkey, lazy);
+}
+
+static int smbconf_fetch_values(const char *key, struct regval_ctr *val)
+{
+ return regdb_ops.fetch_values(key, val);
+}
+
+static bool smbconf_store_values(const char *key, struct regval_ctr *val)
+{
+ return regdb_ops.store_values(key, val);
+}
+
+static bool smbconf_reg_access_check(const char *keyname, uint32_t requested,
+ uint32_t *granted,
+ const struct security_token *token)
+{
+ if (!security_token_has_privilege(token, SEC_PRIV_DISK_OPERATOR)) {
+ return False;
+ }
+
+ *granted = REG_KEY_ALL;
+ return True;
+}
+
+static WERROR smbconf_get_secdesc(TALLOC_CTX *mem_ctx, const char *key,
+ struct security_descriptor **psecdesc)
+{
+ return regdb_ops.get_secdesc(mem_ctx, key, psecdesc);
+}
+
+static WERROR smbconf_set_secdesc(const char *key,
+ struct security_descriptor *secdesc)
+{
+ return regdb_ops.set_secdesc(key, secdesc);
+}
+
+static bool smbconf_subkeys_need_update(struct regsubkey_ctr *subkeys)
+{
+ return regdb_ops.subkeys_need_update(subkeys);
+}
+
+static bool smbconf_values_need_update(struct regval_ctr *values)
+{
+ return regdb_ops.values_need_update(values);
+}
+
+/*
+ * Table of function pointers for accessing smb.conf data
+ */
+
+struct registry_ops smbconf_reg_ops = {
+ .fetch_subkeys = smbconf_fetch_keys,
+ .fetch_values = smbconf_fetch_values,
+ .store_subkeys = smbconf_store_keys,
+ .store_values = smbconf_store_values,
+ .create_subkey = smbconf_create_subkey,
+ .delete_subkey = smbconf_delete_subkey,
+ .reg_access_check = smbconf_reg_access_check,
+ .get_secdesc = smbconf_get_secdesc,
+ .set_secdesc = smbconf_set_secdesc,
+ .subkeys_need_update = smbconf_subkeys_need_update,
+ .values_need_update = smbconf_values_need_update,
+};
diff --git a/source3/registry/reg_backend_tcpip_params.c b/source3/registry/reg_backend_tcpip_params.c
new file mode 100644
index 0000000..dd132df
--- /dev/null
+++ b/source3/registry/reg_backend_tcpip_params.c
@@ -0,0 +1,54 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2002-2005
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * TCP/IP parameters registry backend.
+ *
+ * This replaces the former dynamic tcpip parameters overlay.
+ */
+
+#include "includes.h"
+#include "registry.h"
+#include "reg_objects.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+extern struct registry_ops regdb_ops;
+
+static int tcpip_params_fetch_values(const char *key, struct regval_ctr *regvals)
+{
+ regval_ctr_addvalue_sz(regvals, "Hostname", myhostname());
+
+ regval_ctr_addvalue_sz(regvals, "Domain", get_mydnsdomname(talloc_tos()));
+
+ return regval_ctr_numvals(regvals);
+}
+
+static int tcpip_params_fetch_subkeys(const char *key,
+ struct regsubkey_ctr *subkey_ctr)
+{
+ return regdb_ops.fetch_subkeys(key, subkey_ctr);
+}
+
+struct registry_ops tcpip_params_reg_ops = {
+ .fetch_values = tcpip_params_fetch_values,
+ .fetch_subkeys = tcpip_params_fetch_subkeys,
+};
diff --git a/source3/registry/reg_cachehook.c b/source3/registry/reg_cachehook.c
new file mode 100644
index 0000000..95e77cc
--- /dev/null
+++ b/source3/registry/reg_cachehook.c
@@ -0,0 +1,147 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2002.
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Implementation of registry hook cache tree */
+
+#include "includes.h"
+#include "adt_tree.h"
+#include "registry.h"
+#include "reg_cachehook.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+static struct sorted_tree *cache_tree = NULL;
+extern struct registry_ops regdb_ops; /* these are the default */
+
+static WERROR keyname_to_path(TALLOC_CTX *mem_ctx, const char *keyname,
+ char **path)
+{
+ char *tmp_path = NULL;
+
+ if ((keyname == NULL) || (path == NULL)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ tmp_path = talloc_asprintf(mem_ctx, "\\%s", keyname);
+ if (tmp_path == NULL) {
+ DEBUG(0, ("talloc_asprintf failed!\n"));
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ *path = tmp_path;
+
+ return WERR_OK;
+}
+
+/**********************************************************************
+ Initialize the cache tree if it has not been initialized yet.
+ *********************************************************************/
+
+WERROR reghook_cache_init(void)
+{
+ if (cache_tree != NULL) {
+ return WERR_OK;
+ }
+
+ cache_tree = pathtree_init(&regdb_ops);
+ if (cache_tree == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ DEBUG(10, ("reghook_cache_init: new tree with default "
+ "ops %p for key [%s]\n", (void *)&regdb_ops,
+ KEY_TREE_ROOT));
+ return WERR_OK;
+}
+
+/**********************************************************************
+ Add a new registry hook to the cache. Note that the keyname
+ is not in the exact format that a struct sorted_tree expects.
+ *********************************************************************/
+
+WERROR reghook_cache_add(const char *keyname, struct registry_ops *ops)
+{
+ WERROR werr;
+ char *key = NULL;
+
+ if ((keyname == NULL) || (ops == NULL)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ werr = keyname_to_path(talloc_tos(), keyname, &key);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ DEBUG(10, ("reghook_cache_add: Adding ops %p for key [%s]\n",
+ (void *)ops, key));
+
+ if (!pathtree_add(cache_tree, key, ops))
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ else
+ werr = WERR_OK;
+
+done:
+ TALLOC_FREE(key);
+ return werr;
+}
+
+/**********************************************************************
+ Find a key in the cache.
+ *********************************************************************/
+
+struct registry_ops *reghook_cache_find(const char *keyname)
+{
+ WERROR werr;
+ char *key = NULL;
+ struct registry_ops *ops = NULL;
+
+ if (keyname == NULL) {
+ return NULL;
+ }
+
+ werr = keyname_to_path(talloc_tos(), keyname, &key);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ DEBUG(10,("reghook_cache_find: Searching for keyname [%s]\n", key));
+
+ ops = (struct registry_ops *)pathtree_find(cache_tree, key);
+
+ DEBUG(10, ("reghook_cache_find: found ops %p for key [%s]\n",
+ ops ? (void *)ops : 0, key));
+
+done:
+ TALLOC_FREE(key);
+
+ return ops;
+}
+
+/**********************************************************************
+ Print out the cache tree structure for debugging.
+ *********************************************************************/
+
+void reghook_dump_cache( int debuglevel )
+{
+ DEBUG(debuglevel,("reghook_dump_cache: Starting cache dump now...\n"));
+
+ pathtree_print_keys( cache_tree, debuglevel );
+}
diff --git a/source3/registry/reg_cachehook.h b/source3/registry/reg_cachehook.h
new file mode 100644
index 0000000..7c901c0
--- /dev/null
+++ b/source3/registry/reg_cachehook.h
@@ -0,0 +1,29 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2002.
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _REG_CACHEHOOK_H
+#define _REG_CACHEHOOK_H
+
+WERROR reghook_cache_init(void);
+WERROR reghook_cache_add(const char *keyname, struct registry_ops *ops);
+struct registry_ops *reghook_cache_find(const char *keyname);
+void reghook_dump_cache( int debuglevel );
+
+#endif /* _REG_CACHEHOOK_H */
diff --git a/source3/registry/reg_db.h b/source3/registry/reg_db.h
new file mode 100644
index 0000000..f2f79d2
--- /dev/null
+++ b/source3/registry/reg_db.h
@@ -0,0 +1,37 @@
+/*
+ Parameters for Samba's Internal Registry Database
+
+ Copyright (C) Michael Adam 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _REG_DB_H
+#define _REG_DB_H
+
+#define REG_TDB_FLAGS TDB_SEQNUM
+#define REG_DBWRAP_FLAGS DBWRAP_FLAG_NONE
+
+#define REGDB_VERSION_V1 1 /* first db version with write support */
+#define REGDB_VERSION_V2 2 /* version 2 with normalized keys */
+#define REGDB_VERSION_V3 3 /* different definition of key existence, */
+ /* sorted subkeys cache removed. */
+
+#define REGDB_CODE_VERSION REGDB_VERSION_V3
+
+#define REG_VALUE_PREFIX "SAMBA_REGVAL"
+#define REG_SECDESC_PREFIX "SAMBA_SECDESC"
+#define REG_SORTED_SUBKEYS_PREFIX "SAMBA_SORTED_SUBKEYS"
+
+#endif /* _REG_DB_H */
diff --git a/source3/registry/reg_dispatcher.c b/source3/registry/reg_dispatcher.c
new file mode 100644
index 0000000..ab3fb24
--- /dev/null
+++ b/source3/registry/reg_dispatcher.c
@@ -0,0 +1,264 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2002-2005
+ * Copyright (C) Michael Adam 2006-2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Implementation of registry frontend view functions.
+ * Functions moved from reg_frontend.c to minimize linker deps.
+ */
+
+#include "includes.h"
+#include "system/passwd.h" /* uid_wrapper */
+#include "registry.h"
+#include "reg_dispatcher.h"
+#include "../libcli/security/security.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+static const struct generic_mapping reg_generic_map =
+ { REG_KEY_READ, REG_KEY_WRITE, REG_KEY_EXECUTE, REG_KEY_ALL };
+
+/********************************************************************
+********************************************************************/
+
+static WERROR construct_registry_sd(TALLOC_CTX *ctx, struct security_descriptor **psd)
+{
+ struct security_ace ace[3];
+ size_t i = 0;
+ struct security_descriptor *sd;
+ struct security_acl *theacl;
+ size_t sd_size;
+
+ /* basic access for Everyone */
+
+ init_sec_ace(&ace[i++], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ REG_KEY_READ, 0);
+
+ /* Full Access 'BUILTIN\Administrators' */
+
+ init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators,
+ SEC_ACE_TYPE_ACCESS_ALLOWED, REG_KEY_ALL, 0);
+
+ /* Full Access 'NT Authority\System' */
+
+ init_sec_ace(&ace[i++], &global_sid_System, SEC_ACE_TYPE_ACCESS_ALLOWED,
+ REG_KEY_ALL, 0);
+
+ /* create the security descriptor */
+
+ theacl = make_sec_acl(ctx, NT4_ACL_REVISION, i, ace);
+ if (theacl == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ sd = make_sec_desc(ctx, SD_REVISION, SEC_DESC_SELF_RELATIVE,
+ &global_sid_Builtin_Administrators,
+ &global_sid_System, NULL, theacl,
+ &sd_size);
+ if (sd == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ *psd = sd;
+ return WERR_OK;
+}
+
+/***********************************************************************
+ High level wrapper function for storing registry subkeys
+ ***********************************************************************/
+
+bool store_reg_keys(struct registry_key_handle *key,
+ struct regsubkey_ctr *subkeys)
+{
+ if (key->ops && key->ops->store_subkeys)
+ return key->ops->store_subkeys(key->name, subkeys);
+
+ return false;
+}
+
+/***********************************************************************
+ High level wrapper function for storing registry values
+ ***********************************************************************/
+
+bool store_reg_values(struct registry_key_handle *key, struct regval_ctr *val)
+{
+ if (key->ops && key->ops->store_values)
+ return key->ops->store_values(key->name, val);
+
+ return false;
+}
+
+WERROR create_reg_subkey(struct registry_key_handle *key, const char *subkey)
+{
+ if (key->ops && key->ops->create_subkey) {
+ return key->ops->create_subkey(key->name, subkey);
+ }
+
+ return WERR_NOT_SUPPORTED;
+}
+
+WERROR delete_reg_subkey(struct registry_key_handle *key, const char *subkey, bool lazy)
+{
+ if (key->ops && key->ops->delete_subkey) {
+ return key->ops->delete_subkey(key->name, subkey, lazy);
+ }
+
+ return WERR_NOT_SUPPORTED;
+}
+
+/***********************************************************************
+ High level wrapper function for enumerating registry subkeys
+ Initialize the TALLOC_CTX if necessary
+ ***********************************************************************/
+
+int fetch_reg_keys(struct registry_key_handle *key,
+ struct regsubkey_ctr *subkey_ctr)
+{
+ int result = -1;
+
+ if (key->ops && key->ops->fetch_subkeys)
+ result = key->ops->fetch_subkeys(key->name, subkey_ctr);
+
+ return result;
+}
+
+/***********************************************************************
+ High level wrapper function for enumerating registry values
+ ***********************************************************************/
+
+int fetch_reg_values(struct registry_key_handle *key, struct regval_ctr *val)
+{
+ int result = -1;
+
+ DEBUG(10, ("fetch_reg_values called for key '%s' (ops %p)\n", key->name,
+ (key->ops) ? (void *)key->ops : NULL));
+
+ if (key->ops && key->ops->fetch_values)
+ result = key->ops->fetch_values(key->name, val);
+
+ return result;
+}
+
+/***********************************************************************
+ High level access check for passing the required access mask to the
+ underlying registry backend
+ ***********************************************************************/
+
+bool regkey_access_check(struct registry_key_handle *key, uint32_t requested,
+ uint32_t *granted,
+ const struct security_token *token )
+{
+ struct security_descriptor *sec_desc;
+ NTSTATUS status;
+ WERROR err;
+
+ /* root free-pass, like we have on all other pipes like samr, lsa, etc. */
+ if (root_mode()) {
+ *granted = REG_KEY_ALL;
+ return true;
+ }
+
+ /* use the default security check if the backend has not defined its
+ * own */
+
+ if (key->ops && key->ops->reg_access_check) {
+ return key->ops->reg_access_check(key->name, requested,
+ granted, token);
+ }
+
+ err = regkey_get_secdesc(talloc_tos(), key, &sec_desc);
+
+ if (!W_ERROR_IS_OK(err)) {
+ return false;
+ }
+
+ se_map_generic( &requested, &reg_generic_map );
+
+ status =se_access_check(sec_desc, token, requested, granted);
+ TALLOC_FREE(sec_desc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ return NT_STATUS_IS_OK(status);
+}
+
+WERROR regkey_get_secdesc(TALLOC_CTX *mem_ctx, struct registry_key_handle *key,
+ struct security_descriptor **psecdesc)
+{
+ struct security_descriptor *secdesc;
+ WERROR werr;
+
+ if (key->ops && key->ops->get_secdesc) {
+ werr = key->ops->get_secdesc(mem_ctx, key->name, psecdesc);
+ if (W_ERROR_IS_OK(werr)) {
+ return WERR_OK;
+ }
+ }
+
+ werr = construct_registry_sd(mem_ctx, &secdesc);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ *psecdesc = secdesc;
+ return WERR_OK;
+}
+
+WERROR regkey_set_secdesc(struct registry_key_handle *key,
+ struct security_descriptor *psecdesc)
+{
+ if (key->ops && key->ops->set_secdesc) {
+ return key->ops->set_secdesc(key->name, psecdesc);
+ }
+
+ return WERR_ACCESS_DENIED;
+}
+
+/**
+ * Check whether the in-memory version of the subkyes of a
+ * registry key needs update from disk.
+ */
+bool reg_subkeys_need_update(struct registry_key_handle *key,
+ struct regsubkey_ctr *subkeys)
+{
+ if (key->ops && key->ops->subkeys_need_update)
+ {
+ return key->ops->subkeys_need_update(subkeys);
+ }
+
+ return true;
+}
+
+/**
+ * Check whether the in-memory version of the values of a
+ * registry key needs update from disk.
+ */
+bool reg_values_need_update(struct registry_key_handle *key,
+ struct regval_ctr *values)
+{
+ if (key->ops && key->ops->values_need_update)
+ {
+ return key->ops->values_need_update(values);
+ }
+
+ return true;
+}
+
diff --git a/source3/registry/reg_dispatcher.h b/source3/registry/reg_dispatcher.h
new file mode 100644
index 0000000..76485b3
--- /dev/null
+++ b/source3/registry/reg_dispatcher.h
@@ -0,0 +1,44 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2002-2005
+ * Copyright (C) Michael Adam 2006-2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _REG_DISPATCHER_H
+#define _REG_DISPATCHER_H
+
+bool store_reg_keys(struct registry_key_handle *key,
+ struct regsubkey_ctr *subkeys);
+bool store_reg_values(struct registry_key_handle *key, struct regval_ctr *val);
+WERROR create_reg_subkey(struct registry_key_handle *key, const char *subkey);
+WERROR delete_reg_subkey(struct registry_key_handle *key, const char *subkey, bool lazy);
+int fetch_reg_keys(struct registry_key_handle *key,
+ struct regsubkey_ctr *subkey_ctr);
+int fetch_reg_values(struct registry_key_handle *key, struct regval_ctr *val);
+bool regkey_access_check(struct registry_key_handle *key, uint32_t requested,
+ uint32_t *granted,
+ const struct security_token *token);
+WERROR regkey_get_secdesc(TALLOC_CTX *mem_ctx, struct registry_key_handle *key,
+ struct security_descriptor **psecdesc);
+WERROR regkey_set_secdesc(struct registry_key_handle *key,
+ struct security_descriptor *psecdesc);
+bool reg_subkeys_need_update(struct registry_key_handle *key,
+ struct regsubkey_ctr *subkeys);
+bool reg_values_need_update(struct registry_key_handle *key,
+ struct regval_ctr *values);
+
+#endif /* _REG_DISPATCHER_H */
diff --git a/source3/registry/reg_format.c b/source3/registry/reg_format.c
new file mode 100644
index 0000000..f353f79
--- /dev/null
+++ b/source3/registry/reg_format.c
@@ -0,0 +1,830 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @brief Format dot.reg files
+ * @file reg_format.c
+ * @author Gregor Beck <gb@sernet.de>
+ * @date Sep 2010
+ */
+
+#include "includes.h"
+#include "reg_format.h"
+#include "reg_parse.h"
+#include "reg_parse_internal.h"
+#include "cbuf.h"
+#include "srprs.h"
+#include "registry.h"
+#include "registry/reg_objects.h"
+#include <assert.h>
+
+static void cstr_unescape(char* val)
+{
+ all_string_sub(val, "\\r", "\r", 0);
+ all_string_sub(val, "\\n", "\n", 0);
+ all_string_sub(val, "\\t", "\t", 0);
+ all_string_sub(val, "\\\\", "\\", 0);
+}
+
+/******************************************************************************/
+
+/**
+ * Print value assign to stream.
+ *
+ * @param[out] ost outstream
+ * @param[in] name string
+ *
+ * @return number of bytes written, -1 on error
+ * @see srprs_val_name
+ */
+static int cbuf_print_value_assign(cbuf* ost, const char* name) {
+ size_t ret = 0;
+ int n;
+ if (*name == '\0') {
+ n = cbuf_putc(ost, '@');
+ } else {
+ n = cbuf_print_quoted_string(ost, name);
+ }
+ if (n < 0) {
+ return n;
+ }
+ ret += n;
+
+ n = cbuf_putc(ost, '=');
+ if (n < 0) {
+ return n;
+ }
+ ret += n;
+
+ return ret;
+}
+
+enum fmt_hive {
+ FMT_HIVE_PRESERVE=0,
+ FMT_HIVE_SHORT,
+ FMT_HIVE_LONG
+};
+
+
+struct fmt_key {
+ enum fmt_hive hive_fmt;
+ enum fmt_case hive_case;
+ enum fmt_case key_case;
+ const char* sep;
+};
+
+
+static int
+cbuf_print_hive(cbuf* ost, const char* hive, int len, const struct fmt_key* fmt)
+{
+ if (fmt->hive_fmt != FMT_HIVE_PRESERVE) {
+ const struct hive_info* hinfo = hive_info(hive);
+ if (hinfo == NULL) {
+ DEBUG(0, ("Unknown hive %*s\n", len, hive));
+ } else {
+ switch(fmt->hive_fmt) {
+ case FMT_HIVE_SHORT:
+ hive = hinfo->short_name;
+ len = hinfo->short_name_len;
+ break;
+ case FMT_HIVE_LONG:
+ hive = hinfo->long_name;
+ len = hinfo->long_name_len;
+ break;
+ default:
+ DEBUG(0, ("Unsupported hive format %d\n",
+ (int)fmt->hive_fmt));
+ return -1;
+ }
+ }
+ }
+
+ return cbuf_puts_case(ost, hive, len, fmt->hive_case);
+}
+
+static int
+cbuf_print_keyname(cbuf* ost, const char* key[], int n, const struct fmt_key* fmt)
+{
+ int r;
+ size_t ret = 0;
+ size_t pos = cbuf_getpos(ost);
+ bool hive = true;
+
+ for (; n>0; key++, n--) {
+ const char* start = *key;
+ while(*start != '\0') {
+ const char* end = start;
+ while(*end != '\\' && *end != '\0') {
+ end++;
+ }
+
+ if (hive) {
+ r = cbuf_print_hive(ost, start, end-start, fmt);
+ if (r < 0) {
+ goto fail;
+ }
+
+ ret += r;
+ hive = false;
+ } else {
+ r = cbuf_puts(ost, fmt->sep, -1);
+ if (r < 0) {
+ goto fail;
+ }
+ ret += r;
+
+ r = cbuf_puts_case(ost, start, end-start, fmt->key_case);
+ if (r < 0) {
+ goto fail;
+ }
+ ret += r;
+ }
+
+ while(*end == '\\') {
+ end++;
+ }
+ start = end;
+ }
+ }
+ return ret;
+fail:
+ cbuf_setpos(ost, pos);
+ return r;
+}
+/**@}*/
+
+/**
+ * @defgroup reg_format Format dot.reg file.
+ * @{
+ */
+
+struct reg_format
+{
+ struct reg_parse_callback reg_parse_callback;
+ struct reg_format_callback call;
+ unsigned flags;
+ smb_iconv_t fromUTF16;
+ const char* sep;
+};
+
+int reg_format_value_delete(struct reg_format* f, const char* name)
+{
+ int ret;
+ cbuf* line = cbuf_new(f);
+
+ ret = cbuf_print_value_assign(line, name);
+ if (ret < 0) {
+ goto done;
+ }
+
+ ret = cbuf_putc(line, '-');
+ if (ret < 0 ) {
+ goto done;
+ }
+
+ ret = f->call.writeline(f->call.data, cbuf_gets(line, 0));
+done:
+ talloc_free(line);
+ return ret;
+}
+
+/* Todo: write hex if str contains CR or LF */
+static int
+reg_format_value_sz(struct reg_format* f, const char* name, const char* str)
+{
+ int ret;
+ cbuf* line = cbuf_new(f);
+
+ ret = cbuf_print_value_assign(line, name);
+ if (ret < 0) {
+ goto done;
+ }
+
+ ret = cbuf_print_quoted_string(line, str);
+ if (ret < 0) {
+ goto done;
+ }
+
+ ret = f->call.writeline(f->call.data, cbuf_gets(line, 0));
+
+done:
+ talloc_free(line);
+ return ret;
+}
+
+static int reg_format_value_dw(struct reg_format* f, const char* name, uint32_t dw)
+{
+ int ret;
+ cbuf* line = cbuf_new(f);
+
+ ret = cbuf_print_value_assign(line, name);
+ if (ret < 0) {
+ goto done;
+ }
+
+ ret = cbuf_printf(line, "dword:%08x", dw);
+ if (ret < 0) {
+ goto done;
+ }
+
+ ret = f->call.writeline(f->call.data, cbuf_gets(line, 0));
+done:
+ talloc_free(line);
+ return ret;
+}
+
+static int reg_format_value_hex(struct reg_format* f, const char* name, uint32_t type,
+ const void* data, size_t len)
+{
+ int n;
+ int cpl=0;
+ int ret=0;
+ const unsigned char* ptr;
+
+ cbuf* line = cbuf_new(f);
+
+ n = cbuf_print_value_assign(line, name);
+ if (n < 0) {
+ ret = n;
+ goto done;
+ }
+
+ cpl += n;
+
+ if (type==REG_BINARY && !(f->flags & REG_FMT_HEX_BIN)) {
+ n=cbuf_puts(line, "hex:", -1);
+ } else {
+ n=cbuf_printf(line, "hex(%x):", type);
+ }
+ if (n < 0) {
+ ret = n;
+ goto done;
+ }
+
+ cpl += n;
+
+ for (ptr=(const unsigned char *)data; len>1; len--,ptr++) {
+ n = cbuf_printf(line, "%02x,", (unsigned)(*ptr));
+ if (n < 0) {
+ return n;
+ }
+ cpl += n;
+
+ if ( cpl > 76 ) {
+ n = cbuf_putc(line, '\\');
+ if (n< 0) {
+ return n;
+ }
+
+ n = f->call.writeline(f->call.data, cbuf_gets(line,0));
+ if (n < 0) {
+ ret = n;
+ goto done;
+ }
+ ret += n;
+
+ cbuf_clear(line);
+ cpl = cbuf_puts(line, " ", -1);
+ if (cpl < 0) {
+ ret = cpl;
+ goto done;
+ }
+ }
+ }
+
+ if ( len > 0 ) {
+ n = cbuf_printf(line, "%02x", (unsigned)(*ptr));
+ if (n < 0) {
+ ret = n;
+ goto done;
+ }
+ cpl += n;
+ }
+
+ n = f->call.writeline(f->call.data, cbuf_gets(line,0));
+ if (n < 0) {
+ ret = n;
+ goto done;
+ }
+ ret += n;
+done:
+ talloc_free(line);
+ return ret;
+}
+
+static bool is_zero_terminated_ucs2(const uint8_t* data, size_t len) {
+ const size_t idx = len/sizeof(smb_ucs2_t);
+ const smb_ucs2_t *str = (const smb_ucs2_t*)data;
+
+ if ((len % sizeof(smb_ucs2_t)) != 0) {
+ return false;
+ }
+
+ if (idx == 0) {
+ return false;
+ }
+
+ return (str[idx-1] == 0);
+}
+
+int reg_format_value(struct reg_format* f, const char* name, uint32_t type,
+ const uint8_t* data, size_t len)
+{
+ int ret = 0;
+ void* mem_ctx = talloc_new(f);
+
+ switch (type) {
+ case REG_SZ:
+ if (!(f->flags & REG_FMT_HEX_SZ)
+ && is_zero_terminated_ucs2(data, len))
+ {
+ char* str = NULL;
+ size_t dlen;
+ if (pull_ucs2_talloc(mem_ctx, &str, (const smb_ucs2_t*)data, &dlen)) {
+ ret = reg_format_value_sz(f, name, str);
+ goto done;
+ } else {
+ DEBUG(0, ("reg_format_value %s: "
+ "pull_ucs2_talloc failed"
+ ", try to write hex\n", name));
+ }
+ }
+ break;
+
+ case REG_DWORD:
+ if (!(f->flags & REG_FMT_HEX_SZ) && (len == sizeof(uint32_t))) {
+ uint32_t dw = IVAL(data,0);
+ ret = reg_format_value_dw(f, name, dw);
+ goto done;
+ }
+ break;
+
+ case REG_MULTI_SZ:
+ case REG_EXPAND_SZ:
+ if (f->fromUTF16 && (f->fromUTF16 != ((smb_iconv_t)-1))) {
+ char* str = NULL;
+ size_t dlen = iconvert_talloc(mem_ctx, f->fromUTF16,
+ (const char*)data, len, &str);
+ if (dlen != -1) {
+ ret = reg_format_value_hex(f, name, type, str, dlen);
+ goto done;
+ } else {
+ DEBUG(0, ("reg_format_value %s: "
+ "iconvert_talloc failed"
+ ", try to write hex\n", name));
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ ret = reg_format_value_hex(f, name, type, data, len);
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+
+int reg_format_comment(struct reg_format* f, const char* txt)
+{
+ int ret;
+ cbuf* line = cbuf_new(f);
+
+ ret = cbuf_putc(line,';');
+ if (ret<0) {
+ goto done;
+ }
+
+ ret = cbuf_puts(line, txt, -1);
+ if (ret < 0) {
+ goto done;
+ }
+
+ ret = f->call.writeline(f->call.data, cbuf_gets(line, 0));
+done:
+ talloc_free(line);
+ return ret;
+}
+
+
+/******************************************************************************/
+
+
+
+struct reg_format* reg_format_new(const void* talloc_ctx,
+ struct reg_format_callback cb,
+ const char* str_enc, unsigned flags,
+ const char* sep)
+{
+ static const struct reg_parse_callback reg_parse_callback_default = {
+ .key = (reg_parse_callback_key_t)&reg_format_key,
+ .val = (reg_parse_callback_val_t)&reg_format_value,
+ .val_del = (reg_parse_callback_val_del_t)&reg_format_value_delete,
+ .comment = (reg_parse_callback_comment_t)&reg_format_comment,
+ };
+
+ struct reg_format* f = talloc_zero(talloc_ctx, struct reg_format);
+ if (f == NULL) {
+ return NULL;
+ }
+
+ f->reg_parse_callback = reg_parse_callback_default;
+ f->reg_parse_callback.data = f;
+
+ f->call = cb;
+ f->flags = flags;
+ f->sep = sep;
+
+ if (str_enc && !set_iconv(&f->fromUTF16, str_enc, "UTF-16LE")) {
+ DEBUG(0, ("reg_format_new: failed to set encoding: %s\n",
+ str_enc));
+ goto fail;
+ }
+
+ assert(&f->reg_parse_callback == (struct reg_parse_callback*)f);
+ return f;
+fail:
+ talloc_free(f);
+ return NULL;
+}
+
+int reg_format_set_options(struct reg_format* fmt, const char* options)
+{
+ static const char* DEFAULT ="enc=unix,flags=0,sep=\\";
+
+ int ret = 0;
+ char *key, *val;
+ void* ctx = talloc_new(fmt);
+
+ if (options == NULL) {
+ options = DEFAULT;
+ }
+
+ while (srprs_option(&options, ctx, &key, &val)) {
+ if ((strcmp(key, "enc") == 0) || (strcmp(key, "strenc") == 0)) {
+ if (!set_iconv(&fmt->fromUTF16, val, "UTF-16LE")) {
+ DEBUG(0, ("Failed to set encoding: %s\n", val));
+ ret = -1;
+ }
+ } else if ((strcmp(key, "flags") == 0) && (val != NULL)) {
+ char* end = NULL;
+ if (val != NULL) {
+ fmt->flags = strtol(val, &end, 0);
+ }
+ if ((end==NULL) || (*end != '\0')) {
+ DEBUG(0, ("Invalid flags format: %s\n",
+ val ? val : "<NULL>"));
+ ret = -1;
+ }
+ } else if ((strcmp(key, "sep") == 0) && (val != NULL)) {
+ cstr_unescape(val);
+ fmt->sep = talloc_steal(fmt, val);
+ }
+
+ /* else if (strcmp(key, "hive") == 0) { */
+ /* if (strcmp(val, "short") == 0) { */
+ /* f->hive_fmt = REG_FMT_SHORT_HIVES; */
+ /* } else if (strcmp(val, "long") == 0) { */
+ /* f->hive_fmt = REG_FMT_LONG_HIVES; */
+ /* } else if (strcmp(val, "preserve") == 0) { */
+ /* f->hive_fmt = REG_FMT_PRESERVE_HIVES; */
+ /* } else { */
+ /* DEBUG(0, ("Invalid hive format: %s\n", val)); */
+ /* ret = -1; */
+ /* } */
+ /* } */
+ }
+ talloc_free(ctx);
+ return ret;
+}
+
+int reg_format_key(struct reg_format* f, const char* key[], size_t n, bool del)
+{
+ int ret, r;
+ cbuf* line = cbuf_new(f);
+ struct fmt_key key_fmt = {
+ .key_case = (f->flags >> 4) & 0x0F,
+ .hive_case = (f->flags >> 8) & 0x0F,
+ .hive_fmt = (f->flags >> 12) & 0x0F,
+ .sep = f->sep,
+ };
+
+ ret = cbuf_putc(line, '[');
+ if (ret < 0) {
+ goto done;
+ }
+
+ if (del) {
+ ret = cbuf_putc(line, '-');
+ if (ret < 0) {
+ goto done;
+ }
+ }
+
+ ret = cbuf_print_keyname(line, key, n, &key_fmt);
+ if (ret < 0) {
+ goto done;
+ }
+
+ ret = cbuf_putc(line, ']');
+ if (ret < 0) {
+ goto done;
+ }
+
+ ret = f->call.writeline(f->call.data, "");
+ if (ret < 0) {
+ goto done;
+ }
+
+ r = f->call.writeline(f->call.data, cbuf_gets(line, 0));
+ if (r < 0) {
+ ret = r;
+ goto done;
+ }
+ ret += r;
+
+done:
+ talloc_free(line);
+ return ret;
+}
+
+
+int reg_format_registry_key(struct reg_format* f, struct registry_key* key,
+ bool del)
+{
+ const char *knames[1];
+ knames[0] = key->key->name;
+ return reg_format_key(f, knames, 1, del);
+}
+
+int reg_format_registry_value(struct reg_format* f, const char* name,
+ struct registry_value* val)
+{
+ return reg_format_value(f, name, val->type,
+ val->data.data, val->data.length);
+}
+
+int reg_format_regval_blob(struct reg_format* f, const char* name,
+ struct regval_blob* val)
+{
+
+ return reg_format_value(f,
+ name ? name : regval_name(val),
+ regval_type(val),
+ regval_data_p(val),
+ regval_size(val));
+}
+
+/**@}*/
+
+
+struct reg_format_file
+{
+ FILE* file;
+ const char* encoding;
+ smb_iconv_t fromUnix;
+ char* nl;
+ size_t nl_len;
+};
+
+
+static int reg_format_file_close(struct reg_format* fmt)
+{
+ struct reg_format_file* fmt_ctx
+ = (struct reg_format_file*) fmt->call.data;
+ int ret = 0;
+ FILE* file = fmt_ctx->file;
+
+ if (fmt_ctx->encoding) {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "coding: %s", fmt_ctx->encoding);
+ reg_format_comment(fmt, "Local Variables:");
+ reg_format_comment(fmt, buf);
+ reg_format_comment(fmt, "End:");
+ }
+
+ if (file != NULL) {
+ ret = fclose(file);
+ }
+
+ return ret;
+}
+
+static int reg_format_file_writeline(void* ptr, const char* line)
+{
+ size_t size;
+ char* dst=NULL;
+ struct reg_format_file* fmt_ctx = (struct reg_format_file*)ptr;
+ int ret, r;
+
+ size = iconvert_talloc(ptr, fmt_ctx->fromUnix, line, strlen(line), &dst);
+ if (size == -1 ) {
+ DEBUG(0, ("reg_format_file_writeline: iconvert_talloc failed >%s<\n", line));
+ return -1;
+ }
+
+ ret = fwrite(dst, 1, size, fmt_ctx->file);
+ if (ret < 0) {
+ goto done;
+ }
+
+ r = fwrite(fmt_ctx->nl, 1, fmt_ctx->nl_len, fmt_ctx->file);
+ ret = (r < 0) ? r : ret + r;
+
+done:
+ talloc_free(dst);
+ return ret;
+}
+
+struct reg_format_file_opt {
+ const char* head;
+ const char* nl;
+ const char* enc;
+ bool bom;
+ const char* str_enc;
+ unsigned flags;
+ const char* sep;
+};
+
+static struct reg_format_file_opt reg_format_file_opt(void* mem_ctx, const char* opt)
+{
+ static const struct reg_format_file_opt REG4 = {
+ .head = "REGEDIT4",
+ .nl = "\r\n",
+ .enc = "dos",
+ .str_enc = "dos",
+ .bom = false,
+ .flags = (FMT_HIVE_LONG << 12),
+ .sep = "\\",
+ };
+
+ static const struct reg_format_file_opt REG5 = {
+ .head = "Windows Registry Editor Version 5.00",
+ .nl = "\r\n",
+ .enc = "UTF-16LE",
+ .str_enc = "UTF-16LE",
+ .bom = true,
+ .flags = (FMT_HIVE_LONG << 12),
+ .sep = "\\",
+ };
+
+ struct reg_format_file_opt ret = {
+ .head = REG5.head,
+ .nl = "\n",
+ .enc = "unix",
+ .bom = false,
+ .str_enc = "UTF-16LE",
+ .flags = 0,
+ .sep = "\\",
+ };
+
+ void* tmp_ctx = talloc_new(mem_ctx);
+
+ char *key, *val;
+
+ if (opt == NULL) {
+ goto done;
+ }
+
+ while(srprs_option(&opt, tmp_ctx, &key, &val)) {
+ if (strcmp(key, "enc") == 0) {
+ ret.enc = talloc_steal(mem_ctx, val);
+ ret.str_enc = ret.enc;
+ } else if (strcmp(key, "strenc") == 0) {
+ ret.str_enc = talloc_steal(mem_ctx, val);
+ } else if (strcmp(key, "fileenc") == 0) {
+ ret.enc = talloc_steal(mem_ctx, val);
+ } else if ((strcmp(key, "flags") == 0) && (val != NULL)) {
+ char* end = NULL;
+ if (val != NULL) {
+ ret.flags = strtol(val, &end, 0);
+ }
+ if ((end==NULL) || (*end != '\0')) {
+ DEBUG(0, ("Invalid flags format: %s\n",
+ val ? val : "<NULL>"));
+ }
+ } else if ((strcmp(key, "sep") == 0) && (val != NULL)) {
+ cstr_unescape(val);
+ ret.sep = talloc_steal(mem_ctx, val);
+ } else if (strcmp(key, "head") == 0) {
+ cstr_unescape(val);
+ ret.head = talloc_steal(mem_ctx, val);
+ } else if (strcmp(key, "nl") == 0) {
+ cstr_unescape(val);
+ ret.nl = talloc_steal(mem_ctx, val);
+ } else if (strcmp(key, "bom") == 0) {
+ if (val == NULL) {
+ ret.bom = true;
+ } else {
+ ret.bom = atoi(val);
+ }
+ } else if (strcmp(key, "regedit4") == 0) {
+ ret = REG4;
+ } else if (strcmp(key, "regedit5") == 0) {
+ ret = REG5;
+ }
+ }
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+
+struct reg_format* reg_format_file(const void* talloc_ctx,
+ const char* filename,
+ const char* options)
+{
+ struct reg_format_file* fmt_ctx;
+ struct reg_format* fmt;
+ int ret;
+ struct reg_format_file_opt opt;
+
+ struct reg_format_callback reg_format_cb = {
+ .writeline = &reg_format_file_writeline
+ };
+
+ fmt_ctx = talloc_zero(talloc_ctx, struct reg_format_file);
+ if (fmt_ctx == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ opt = reg_format_file_opt(fmt_ctx, options);
+
+ reg_format_cb.data = fmt_ctx;
+
+ fmt = reg_format_new(talloc_ctx, reg_format_cb,
+ opt.str_enc, opt.flags, opt.sep);
+ if (fmt == NULL) {
+ errno = ENOMEM;
+ talloc_free(fmt_ctx);
+ return NULL;
+ }
+
+ talloc_steal(fmt, fmt_ctx);
+
+ if (!set_iconv(&fmt->fromUTF16, opt.str_enc, "UTF-16LE")) { /* HACK */
+ DEBUG(0, ("reg_format_file: failed to set string encoding %s\n",
+ opt.str_enc));
+ goto fail;
+ }
+
+ if (!set_iconv(&fmt_ctx->fromUnix, opt.enc, "unix")) {
+ DEBUG(0, ("reg_format_file: failed to set file encoding %s\n",
+ opt.enc));
+ goto fail;
+ }
+ fmt_ctx->encoding = talloc_strdup(fmt_ctx, smbreg_get_charset(opt.enc));
+
+ fmt_ctx->file = fopen(filename, "w");
+ if (fmt_ctx->file == NULL) {
+ DEBUG(0, ("reg_format_file: fopen failed: %s\n", strerror(errno)));
+ goto fail;
+ }
+
+ if (setvbuf(fmt_ctx->file, NULL, _IOFBF, 64000) < 0) {
+ DEBUG(0, ("reg_format_file: setvbuf failed: %s\n", strerror(errno)));
+ }
+
+ talloc_set_destructor(fmt, reg_format_file_close);
+
+ fmt_ctx->nl_len = iconvert_talloc(fmt, fmt_ctx->fromUnix, opt.nl, strlen(opt.nl), &fmt_ctx->nl);
+ if (fmt_ctx->nl_len == -1) {
+ DEBUG(0, ("iconvert_talloc failed\n"));
+ goto fail;
+ }
+
+ if (opt.bom) {
+ ret = write_bom(fmt_ctx->file, opt.enc, -1);
+ if (ret < 0) {
+ goto fail;
+ }
+ }
+
+ ret = fmt->call.writeline(fmt->call.data, opt.head);
+ if (ret < 0) {
+ goto fail;
+ }
+
+ return fmt;
+fail:
+ talloc_free(fmt);
+ return NULL;
+}
diff --git a/source3/registry/reg_format.h b/source3/registry/reg_format.h
new file mode 100644
index 0000000..dfeb5b8
--- /dev/null
+++ b/source3/registry/reg_format.h
@@ -0,0 +1,219 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @brief Format registration entries (.reg) files.
+ * A formatter is a talloced incarnation of an opaque struct reg_format.
+ * It is fed with registry key's and value's and emits output by calling
+ * writeline from its reg_format_callback.
+ * @file reg_format.h
+ * @author Gregor Beck <gb@sernet.de>
+ * @date Sep 2010
+ */
+#ifndef __REG_FORMAT_H
+#define __REG_FORMAT_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+struct registry_key;
+struct registry_value;
+struct regval_blob;
+
+
+/**
+ * A Formatter for registration entries (.reg) files.
+ *
+ * It may be used as a reg_parse_callback, so the following is valid:
+ * @code
+ * reg_parse* p = reg_parse_new(mem_ctx,
+ * (reg_parse_callback)reg_format_new(mem_ctx, cb, NULL, 0, "\\"),
+ * NULL, 0);
+ * @endcode
+ * @see reg_parse
+ */
+typedef struct reg_format reg_format;
+
+/**
+ * Prototype for function called to output a line.
+ *
+ * @param private_data
+ * @param line line to write in UNIX charset
+ *
+ * @return number of characters written, < 0 on error
+ *
+ * @see reg_parse
+ */
+typedef int (*reg_format_callback_writeline_t)(void* private_data,
+ const char* line);
+/**
+ * Type handling the output of a reg_format object.
+ * It contains the functions to call and an opaque data pointer.
+ */
+typedef struct reg_format_callback {
+ /**< Function called to write a line */
+ reg_format_callback_writeline_t writeline;
+ void* data; /**< Private data passed to callback function */
+} reg_format_callback;
+
+/**
+ * Create a new reg_format object.
+ *
+ * @param talloc_ctx the talloc parent
+ * @param cb the output handler
+ * @param str_enc the charset of hex encoded strings (REG_MULTI_SZ, REG_EXAND_SZ) if not UTF-16
+ * @param flags
+ * @param sep the separator for subkeys
+ *
+ * @return a talloc'ed reg_format object, NULL on error
+ */
+reg_format* reg_format_new(const void* talloc_ctx,
+ reg_format_callback cb,
+ const char* str_enc,
+ unsigned flags,
+ const char* sep);
+
+/**
+ * Create a new reg_format object, writing to a file.
+ *
+ * @param talloc_ctx the talloc parent
+ * @param filename the file to write to
+ * @param options
+ *
+ * @return a talloc'ed reg_format object, NULL on error
+ */
+reg_format* reg_format_file(const void* talloc_ctx,
+ const char* filename,
+ const char* options);
+
+/**
+ * Format a registry key given as struct registry_key.
+ * Create/Open or Delete
+ *
+ * @param f the formatter.
+ * @param key the key to output.
+ * @param del whether to format the deletion of the key
+ *
+ * @retval >= 0 on success.
+ */
+int reg_format_registry_key(reg_format* f,
+ struct registry_key* key,
+ bool del);
+
+/**
+ * Format a registry value given as struct registry_value.
+ *
+ * @param f the formatter.
+ * @param name the values name
+ * @param val the values value.
+ *
+ * @retval >= 0 on success.
+ */
+int reg_format_registry_value(reg_format* f,
+ const char* name,
+ struct registry_value* val);
+
+/**
+ * Format a registry value given as struct regval_blob.
+ *
+ * @param f the formatter.
+ * @param name the values name, if NULL use val->valname which is limited in size;
+ * @param val the values value.
+ *
+ * @retval >= 0 on success.
+ */
+int reg_format_regval_blob(reg_format* f,
+ const char* name,
+ struct regval_blob* val);
+
+
+/**
+ * Format deletion of a registry value.
+ *
+ * @param f the formatter.
+ * @param name the values name
+ *
+ * @retval >= 0 on success.
+ *
+ * @see reg_parse_callback_val_del_t
+ */
+int reg_format_value_delete(reg_format* f, const char* name);
+
+/**
+ * Format a comment.
+ *
+ * @param f the formatter.
+ * @param txt the comment in UNIX charset, may not contain newlines.
+ *
+ * @retval >= 0 on success.
+ *
+ * @see reg_parse_callback_comment_t
+ */
+int reg_format_comment(reg_format* f, const char* txt);
+
+
+int reg_format_set_options(reg_format* f, const char* options);
+
+
+/* reg_format flags */
+#define REG_FMT_HEX_SZ 1
+#define REG_FMT_HEX_DW 2
+#define REG_FMT_HEX_BIN 4
+#define REG_FMT_HEX_ALL (REG_FMT_HEX_SZ | REG_FMT_HEX_DW | REG_FMT_HEX_BIN);
+#define REG_FMT_LONG_HIVES 16
+#define REG_FMT_SHORT_HIVES 32
+
+/* lowlevel */
+
+/**
+ * Format a registry key.
+ * Create/Open or Delete
+ *
+ * @param f the formatter
+ * @param key the key to output
+ * @param klen number of elements in key
+ * @param del whether to format the deletion of the key
+ *
+ * @retval >= 0 on success.
+ *
+ * @see reg_parse_callback_key_t
+ */
+int reg_format_key(reg_format* f,
+ const char* key[], size_t klen,
+ bool del);
+
+/**
+ * Format a registry value.
+ *
+ * @param f the formatter
+ * @param name the values name
+ * @param type the values type
+ * @param data the values value
+ * @param len the number of bytes of data
+ *
+ * @retval >= 0 on success.
+ *
+ * @see reg_parse_callback_val_hex_t
+ */
+int reg_format_value(reg_format* f,
+ const char* name, uint32_t type,
+ const uint8_t* data, size_t len);
+
+#endif /* __REG_FORMAT_H */
diff --git a/source3/registry/reg_import.c b/source3/registry/reg_import.c
new file mode 100644
index 0000000..aef8645
--- /dev/null
+++ b/source3/registry/reg_import.c
@@ -0,0 +1,314 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Adapter to use reg_parse with the registry api
+ *
+ * Copyright (C) Gregor Beck 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "reg_parse.h"
+#include "reg_import.h"
+#include "registry.h"
+#include "registry/reg_objects.h"
+#include <assert.h>
+
+/* Debuglevel for tracing */
+static const int TL = 2;
+
+struct reg_import {
+ struct reg_parse_callback reg_parse_callback;
+ struct reg_import_callback call;
+ void *open_key;
+};
+
+static int reg_parse_callback_key(struct reg_import *cb_private,
+ const char *key[], size_t n, bool del);
+
+static int reg_parse_callback_val(struct reg_import *cb_private,
+ const char *name, uint32_t type,
+ const uint8_t *data, size_t len);
+
+static int reg_parse_callback_val_registry_value(struct reg_import *cb_private,
+ const char *name,
+ uint32_t type,
+ const uint8_t *data,
+ size_t len);
+
+static int reg_parse_callback_val_regval_blob(struct reg_import *cb_private,
+ const char *name, uint32_t type,
+ const uint8_t *data,
+ size_t len);
+
+static int reg_parse_callback_val_del(struct reg_import *cb_private,
+ const char *name);
+
+static int reg_parse_callback_comment(struct reg_import *cb_private,
+ const char *txt);
+
+
+/*******************************************************************************/
+
+int reg_parse_callback_key(struct reg_import *p,
+ const char *key[], size_t n, bool del)
+{
+ WERROR werr = WERR_OK;
+
+ DEBUG(TL, ("%s: %s\n", __FUNCTION__, key[0]));
+
+ if (p->open_key != NULL) {
+ werr = p->call.closekey(p->call.data, p->open_key);
+ p->open_key = NULL;
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, ("closekey failed: %s\n", win_errstr(werr)));
+ }
+ }
+
+ if (del) {
+ werr = p->call.deletekey(p->call.data, NULL, key[0]);
+ if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)) {
+ /* the key didn't exist, treat as success */
+ werr = WERR_OK;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, ("deletekey %s failed: %s\n",
+ key[0], win_errstr(werr)));
+ }
+ } else {
+ bool existing;
+ werr = p->call.createkey(p->call.data, NULL, key[0],
+ &p->open_key, &existing);
+ if (W_ERROR_IS_OK(werr)) {
+ DEBUG(TL, ("createkey %s %s\n",
+ existing ? "opened" : "created", key[0]));
+ } else {
+ DEBUG(0, ("createkey %s failed: %s\n",
+ key[0], win_errstr(werr)));
+ }
+ }
+
+ return W_ERROR_IS_OK(werr) ? 0 : -1;
+}
+
+#define DEBUG_ADD_HEX(LEV, PTR, LEN) \
+ do { \
+ int i; \
+ const unsigned char* ptr = (const unsigned char*)PTR; \
+ for (i=0; i<LEN; i++) { \
+ DEBUGADD(LEV, ("'%c'(%02x)%s", \
+ isprint(ptr[i]) ? ptr[i] : '.', \
+ (unsigned)ptr[i], \
+ ((i+1 < LEN) && (i+1)%8) \
+ ? ", " : "\n")); \
+ } \
+ } while(0)
+
+/*----------------------------------------------------------------------------*/
+int reg_parse_callback_val(struct reg_import *p,
+ const char *name, uint32_t type,
+ const uint8_t *data, size_t len)
+{
+ WERROR werr = WERR_OK;
+
+ DEBUG(TL, ("%s(%x): >%s< = [%zx]\n", __FUNCTION__, type, name, len));
+ DEBUG_ADD_HEX(TL, data, len);
+
+ werr = p->call.setval.blob(p->call.data, p->open_key, name, type,
+ data, len);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, ("setval %s failed: %s\n",
+ name, win_errstr(werr)));
+ }
+
+ return W_ERROR_IS_OK(werr) ? 0 : -1;
+}
+
+/*----------------------------------------------------------------------------*/
+int reg_parse_callback_val_registry_value(struct reg_import *p,
+ const char *name, uint32_t type,
+ const uint8_t *data, size_t len)
+{
+ WERROR werr = WERR_OK;
+ struct registry_value val = {
+ .type = type,
+ .data = data_blob_talloc(p, data, len),
+ };
+
+ DEBUG(TL, ("%s(%x): >%s< = [%zx]\n", __FUNCTION__, type, name, len));
+ DEBUG_ADD_HEX(TL, data, len);
+
+ werr = p->call.setval.registry_value(p->call.data, p->open_key,
+ name, &val);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, ("setval %s failed: %s\n",
+ name, win_errstr(werr)));
+ }
+
+ data_blob_free(&val.data);
+ return W_ERROR_IS_OK(werr) ? 0 : -1;
+}
+
+/*----------------------------------------------------------------------------*/
+int reg_parse_callback_val_regval_blob(struct reg_import *p,
+ const char *name, uint32_t type,
+ const uint8_t *data, size_t len)
+{
+ WERROR werr = WERR_OK;
+ void* mem_ctx = talloc_new(p);
+ struct regval_blob *v = NULL;
+
+ DEBUG(TL, ("%s(%x): >%s< = [%zx]\n", __FUNCTION__, type, name, len));
+ DEBUG_ADD_HEX(TL, data, len);
+
+ v = regval_compose(mem_ctx, name, type, data, len);
+ if (v == NULL) {
+ DEBUG(0, ("regval_compose %s failed\n", name));
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ werr = p->call.setval.regval_blob(p->call.data, p->open_key, v);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, ("setval %s failed: %s\n",
+ name, win_errstr(werr)));
+ }
+
+done:
+ talloc_free(mem_ctx);
+
+ return W_ERROR_IS_OK(werr) ? 0 : -1;
+}
+
+
+/*----------------------------------------------------------------------------*/
+
+int reg_parse_callback_val_del(struct reg_import *p,
+ const char *name)
+{
+ WERROR werr = WERR_OK;
+
+ DEBUG(TL, ("%s: %s\n", __FUNCTION__, name));
+
+ werr = p->call.deleteval(p->call.data, p->open_key, name);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, ("deleteval %s failed: %s\n",
+ name, win_errstr(werr)));
+ }
+
+ return W_ERROR_IS_OK(werr) ? 0 : -1;
+}
+
+
+int reg_parse_callback_comment(struct reg_import *cb_private,
+ const char *txt)
+{
+ DEBUG(TL, ("%s: %s\n", __FUNCTION__, txt));
+ return 0;
+}
+
+/******************************************************************************/
+static WERROR nop_callback_open(void* private_data,
+ void* parent,
+ const char* name,
+ void** key)
+{
+ return WERR_OK;
+}
+
+static WERROR nop_callback_close(void* private_data, void* key)
+{
+ return WERR_OK;
+}
+
+static WERROR nop_callback_create(void* private_data,
+ void* parent,
+ const char* name,
+ void** key,
+ bool* existing)
+{
+ return WERR_OK;
+}
+
+
+static WERROR nop_callback_del(void* private_data,
+ void* parent,
+ const char* name)
+{
+ return WERR_OK;
+}
+
+struct reg_parse_callback *reg_import_adapter(TALLOC_CTX *talloc_ctx,
+ struct reg_import_callback cb)
+{
+ struct reg_parse_callback *ret;
+ struct reg_import *p = talloc_zero(talloc_ctx, struct reg_import);
+ if (p == NULL) {
+ goto fail;
+ }
+ if (cb.openkey == NULL) {
+ cb.openkey = (reg_import_callback_openkey_t)&nop_callback_open;
+ }
+ if (cb.closekey == NULL) {
+ cb.closekey =
+ (reg_import_callback_closekey_t)&nop_callback_close;
+ }
+ if (cb.createkey == NULL) {
+ cb.createkey =
+ (reg_import_callback_createkey_t)&nop_callback_create;
+ }
+ if (cb.deletekey == NULL) {
+ cb.deletekey =
+ (reg_import_callback_deletekey_t)&nop_callback_del;
+
+ }
+ if (cb.deleteval == NULL) {
+ cb.deleteval =
+ (reg_import_callback_deleteval_t)&nop_callback_del;
+ }
+
+ p->call = cb;
+
+ ret = &p->reg_parse_callback;
+ ret->key = (reg_parse_callback_key_t) &reg_parse_callback_key;
+ ret->val_del = (reg_parse_callback_val_del_t) &reg_parse_callback_val_del;
+ ret->comment = (reg_parse_callback_comment_t) &reg_parse_callback_comment;
+ ret->data = p;
+
+ switch (cb.setval_type) {
+ case BLOB:
+ assert(cb.setval.blob != NULL);
+ ret->val = (reg_parse_callback_val_t) &reg_parse_callback_val;
+ break;
+ case REGISTRY_VALUE:
+ assert(cb.setval.registry_value != NULL);
+ ret->val = (reg_parse_callback_val_t) &reg_parse_callback_val_registry_value;
+ break;
+ case REGVAL_BLOB:
+ assert(cb.setval.regval_blob != NULL);
+ ret->val = (reg_parse_callback_val_t) &reg_parse_callback_val_regval_blob;
+ break;
+ case NONE:
+ ret->val = NULL;
+ break;
+ default:
+ assert(false);
+ }
+
+ assert((struct reg_parse_callback *)p == ret);
+ return ret;
+fail:
+ talloc_free(p);
+ return NULL;
+}
diff --git a/source3/registry/reg_import.h b/source3/registry/reg_import.h
new file mode 100644
index 0000000..31e8753
--- /dev/null
+++ b/source3/registry/reg_import.h
@@ -0,0 +1,199 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @brief Adapter to use reg_parse with the registry api
+ * @file reg_import.h
+ * @author Gregor Beck <gb@sernet.de>
+ * @date Jun 2010
+ */
+
+
+#ifndef REG_IMPORT_H
+#define REG_IMPORT_H
+
+#include "reg_parse.h"
+
+struct registry_value;
+struct regval_blob;
+
+/**
+ * Prototype for function called to open a key.
+ *
+ * @param private_data
+ * @param[in] parent the parent of the key to open, may be NULL
+ * @param[in] name the name of the key relative to parent.
+ * @param[out] key the opened key
+ *
+ * @return WERR_OK on success
+ */
+typedef WERROR (*reg_import_callback_openkey_t) (void* private_data,
+ void* parent,
+ const char* name,
+ void** key);
+
+/**
+ * Prototype for function called to close a key.
+ *
+ * @param private_data
+ * @param key the key to close
+ *
+ * @return WERR_OK on success
+ */
+typedef WERROR (*reg_import_callback_closekey_t) (void* private_data,
+ void* key);
+
+/**
+ * Prototype for function called to create (or open an existing) key.
+ *
+ * @param private_data
+ * @param[in] parent the parent of the key to create, may be NULL
+ * @param[in] name the name of the key relative to parent.
+ * @param[out] key the opened key
+ * @param[out] existing whether we opened an existing key
+ *
+ * @return WERR_OK on success
+ */
+typedef WERROR (*reg_import_callback_createkey_t)(void* private_data,
+ void* parent,
+ const char* name,
+ void** key,
+ bool* existing);
+
+/**
+ * Prototype for function called to delete a key.
+ *
+ * @param private_data
+ * @param parent the parent of the key to delete, may be NULL
+ * @param[in] name the name of the key relative to parent.
+ *
+ * @return WERR_OK on success
+ */
+typedef WERROR (*reg_import_callback_deletekey_t)(void* private_data,
+ void* parent,
+ const char* name);
+
+/**
+ * Prototype for function called to delete a value.
+ *
+ * @param private_data
+ * @param parent the key of the value to delete
+ * @param[in] name the name of the value to delete.
+ *
+ * @return WERR_OK on success
+ */
+typedef WERROR (*reg_import_callback_deleteval_t)(void* private_data,
+ void* parent,
+ const char* name);
+
+/**
+ * Prototype for function called to set a value.
+ *
+ * @param private_data
+ * @param parent the key of the value to set
+ * @param name the name of the value
+ * @param type the type of the value
+ * @param data the value of the value
+ * @param size the number of bytes of data
+ *
+ * @return WERR_OK on success
+ */
+typedef WERROR (*reg_import_callback_setval_blob_t)(void* private_data,
+ void* parent,
+ const char* name,
+ uint32_t type,
+ const uint8_t* data,
+ uint32_t size);
+
+/**
+ * Prototype for function called to set a value given as struct registry_value.
+ *
+ * @param private_data
+ * @param parent the key of the value to set
+ * @param name the name of the value
+ * @param val the value of the value
+ *
+ * @return WERR_OK on success
+ */
+typedef WERROR (*reg_import_callback_setval_registry_value_t) (
+ void* private_data,
+ void* parent,
+ const char* name,
+ const struct registry_value* val);
+
+/**
+ * Prototype for function called to set a value given as struct struct regval_blob.
+ *
+ * @param private_data
+ * @param parent the key of the value to set
+ * @param val the value
+ *
+ * @return WERR_OK on success
+ */
+typedef WERROR (*reg_import_callback_setval_regval_blob_t)(
+ void* private_data,
+ void* parent,
+ const struct regval_blob* val);
+
+/**
+ * Type handling the output of a reg_import object.
+ * It contains the functions to call and an opaque data pointer.
+ */
+struct reg_import_callback {
+ /** Function called to open key */
+ reg_import_callback_openkey_t openkey;
+ /** Function called to close key */
+ reg_import_callback_closekey_t closekey;
+ /** Function called to create or open key */
+ reg_import_callback_createkey_t createkey;
+ /** Function called to delete key */
+ reg_import_callback_deletekey_t deletekey;
+ /** Function called to delete value */
+ reg_import_callback_deleteval_t deleteval;
+
+ /** Function called to set value */
+ union {
+ reg_import_callback_setval_blob_t blob;
+ reg_import_callback_setval_registry_value_t registry_value;
+ reg_import_callback_setval_regval_blob_t regval_blob;
+ } setval;
+ /** Which function is called to set a value */
+ enum {
+ NONE=0, /**< no setval function used */
+ BLOB, /**< @ref reg_import_callback_setval_blob_t blob */
+ REGISTRY_VALUE, /**< @ref reg_import_callback_setval_registry_value_t registry_value */
+ REGVAL_BLOB, /**< @ref reg_import_callback_setval_regval_blob_t regval_blob */
+ } setval_type;
+ void* data; /**< Private data passed to callback function */
+};
+
+
+/**
+ * Create a new reg_import object.
+ * Because its only purpose is to act as an reg_parse_callback the return type
+ * is accordingly.
+ *
+ * @param talloc_ctx the talloc parent
+ * @param cb the output handler
+ *
+ * @return a talloc'ed reg_import object, NULL on error
+ */
+struct reg_parse_callback* reg_import_adapter(TALLOC_CTX *talloc_ctx,
+ struct reg_import_callback cb);
+#endif
diff --git a/source3/registry/reg_init_basic.c b/source3/registry/reg_init_basic.c
new file mode 100644
index 0000000..ea8077f
--- /dev/null
+++ b/source3/registry/reg_init_basic.c
@@ -0,0 +1,67 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Registry helper routines
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "registry.h"
+#include "reg_init_basic.h"
+#include "reg_cachehook.h"
+#include "reg_backend_db.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+WERROR registry_init_common(void)
+{
+ WERROR werr;
+
+ werr = regdb_init();
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, ("Failed to initialize the registry: %s\n",
+ win_errstr(werr)));
+ goto done;
+ }
+
+ werr = reghook_cache_init();
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, ("Failed to initialize the reghook cache: %s\n",
+ win_errstr(werr)));
+ goto done;
+ }
+
+ /* setup the necessary keys and values */
+
+ werr = init_registry_data();
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, ("Failed to initialize data in registry!\n"));
+ }
+
+done:
+ return werr;
+}
+
+WERROR registry_init_basic(void)
+{
+ WERROR werr;
+
+ DEBUG(10, ("registry_init_basic called\n"));
+
+ werr = registry_init_common();
+ regdb_close();
+ return werr;
+}
diff --git a/source3/registry/reg_init_basic.h b/source3/registry/reg_init_basic.h
new file mode 100644
index 0000000..4149b5d
--- /dev/null
+++ b/source3/registry/reg_init_basic.h
@@ -0,0 +1,26 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Registry helper routines
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _REG_INIT_BASIC_H
+#define _REG_INIT_BASIC_H
+
+WERROR registry_init_common(void);
+WERROR registry_init_basic(void);
+
+#endif /* _REG_INIT_BASIC_H */
diff --git a/source3/registry/reg_init_full.c b/source3/registry/reg_init_full.c
new file mode 100644
index 0000000..ca78370
--- /dev/null
+++ b/source3/registry/reg_init_full.c
@@ -0,0 +1,102 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2002-2005
+ * Copyright (C) Michael Adam 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Initialize the registry with all available backends. */
+
+#include "includes.h"
+#include "registry.h"
+#include "reg_cachehook.h"
+#include "reg_backend_db.h"
+#include "reg_init_basic.h"
+#include "reg_init_full.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+extern struct registry_ops printing_ops;
+extern struct registry_ops eventlog_ops;
+extern struct registry_ops shares_reg_ops;
+extern struct registry_ops smbconf_reg_ops;
+extern struct registry_ops netlogon_params_reg_ops;
+extern struct registry_ops prod_options_reg_ops;
+extern struct registry_ops tcpip_params_reg_ops;
+extern struct registry_ops hkpt_params_reg_ops;
+extern struct registry_ops current_version_reg_ops;
+extern struct registry_ops perflib_reg_ops;
+extern struct registry_ops regdb_ops; /* these are the default */
+
+/* array of registry_hook's which are read into a tree for easy access */
+/* #define REG_TDB_ONLY 1 */
+
+struct registry_hook {
+ const char *keyname; /* full path to name of key */
+ struct registry_ops *ops; /* registry function hooks */
+};
+
+struct registry_hook reg_hooks[] = {
+#ifndef REG_TDB_ONLY
+ { KEY_PRINTING "\\Printers", &printing_ops },
+ { KEY_PRINTING_2K, &regdb_ops },
+ { KEY_PRINTING_PORTS, &regdb_ops },
+ { KEY_PCC, &regdb_ops },
+ { KEY_SHARES, &shares_reg_ops },
+ { KEY_SMBCONF, &smbconf_reg_ops },
+ { KEY_NETLOGON_PARAMS, &netlogon_params_reg_ops },
+ { KEY_PROD_OPTIONS, &prod_options_reg_ops },
+ { KEY_TCPIP_PARAMS, &tcpip_params_reg_ops },
+ { KEY_HKPT, &hkpt_params_reg_ops },
+ { KEY_CURRENT_VERSION, &current_version_reg_ops },
+ { KEY_PERFLIB, &perflib_reg_ops },
+#endif
+ { NULL, NULL }
+};
+
+/***********************************************************************
+ Open the registry database and initialize the registry_hook cache
+ with all available backends.
+ ***********************************************************************/
+
+WERROR registry_init_full(void)
+{
+ int i;
+ WERROR werr;
+
+ werr = registry_init_common();
+ if (!W_ERROR_IS_OK(werr)) {
+ goto fail;
+ }
+
+ /* build the cache tree of registry hooks */
+
+ for ( i=0; reg_hooks[i].keyname; i++ ) {
+ werr = reghook_cache_add(reg_hooks[i].keyname, reg_hooks[i].ops);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto fail;
+ }
+ }
+
+ if ( DEBUGLEVEL >= 20 )
+ reghook_dump_cache(20);
+
+fail:
+ /* close and let each smbd open up as necessary */
+ regdb_close();
+ return werr;
+}
diff --git a/source3/registry/reg_init_full.h b/source3/registry/reg_init_full.h
new file mode 100644
index 0000000..8ba327e
--- /dev/null
+++ b/source3/registry/reg_init_full.h
@@ -0,0 +1,27 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Registry helper routines
+ *
+ * Copyright (C) Michael Adam 2007
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _REG_INIT_FULL_H
+#define _REG_INIT_FULL_H
+
+WERROR registry_init_full(void);
+
+#endif /* _REG_INIT_FULL_H */
diff --git a/source3/registry/reg_init_smbconf.c b/source3/registry/reg_init_smbconf.c
new file mode 100644
index 0000000..18ee455
--- /dev/null
+++ b/source3/registry/reg_init_smbconf.c
@@ -0,0 +1,71 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Registry helper routines
+ * Copyright (C) Michael Adam 2007
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "registry.h"
+#include "reg_cachehook.h"
+#include "reg_backend_db.h"
+#include "reg_init_basic.h"
+#include "reg_init_smbconf.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+extern struct registry_ops smbconf_reg_ops;
+
+/*
+ * init the smbconf portion of the registry.
+ * for use in places where not the whole registry is needed,
+ * e.g. utils/net_conf.c and loadparm.c
+ */
+WERROR registry_init_smbconf(const char *keyname)
+{
+ WERROR werr;
+
+ DEBUG(10, ("registry_init_smbconf called\n"));
+
+ if (keyname == NULL) {
+ DEBUG(10, ("registry_init_smbconf: defaulting to key '%s'\n",
+ KEY_SMBCONF));
+ keyname = KEY_SMBCONF;
+ }
+
+ werr = registry_init_common();
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ werr = init_registry_key(keyname);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(1, ("Failed to initialize registry key '%s': %s\n",
+ keyname, win_errstr(werr)));
+ goto done;
+ }
+
+ werr = reghook_cache_add(keyname, &smbconf_reg_ops);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(1, ("Failed to add smbconf reghooks to reghook cache: "
+ "%s\n", win_errstr(werr)));
+ goto done;
+ }
+
+done:
+ regdb_close();
+ return werr;
+}
diff --git a/source3/registry/reg_init_smbconf.h b/source3/registry/reg_init_smbconf.h
new file mode 100644
index 0000000..480d7af
--- /dev/null
+++ b/source3/registry/reg_init_smbconf.h
@@ -0,0 +1,27 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Registry helper routines
+ *
+ * Copyright (C) Michael Adam 2007
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _REG_INIT_SMBCONF_H
+#define _REG_INIT_SMBCONF_H
+
+WERROR registry_init_smbconf(const char *keyname);
+
+#endif /* _REG_INIT_SMBCONF_H */
diff --git a/source3/registry/reg_objects.c b/source3/registry/reg_objects.c
new file mode 100644
index 0000000..20556b1
--- /dev/null
+++ b/source3/registry/reg_objects.c
@@ -0,0 +1,615 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ * Copyright (C) Gerald Carter 2002-2005
+ * Copyright (C) Michael Adam 2007-2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Implementation of registry frontend view functions. */
+
+#include "includes.h"
+#include "registry.h"
+#include "reg_objects.h"
+#include "util_tdb.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_rbt.h"
+#include "../libcli/registry/util_reg.h"
+#include "lib/util/string_wrappers.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+/* low level structure to contain registry values */
+
+struct regval_blob {
+ fstring valuename;
+ uint32_t type;
+ /* this should be encapsulated in an RPC_DATA_BLOB */
+ uint32_t size; /* in bytes */
+ uint8_t *data_p;
+};
+
+/* container for registry values */
+
+struct regval_ctr {
+ uint32_t num_values;
+ struct regval_blob **values;
+ int seqnum;
+};
+
+struct regsubkey_ctr {
+ uint32_t num_subkeys;
+ char **subkeys;
+ struct db_context *subkeys_hash;
+ int seqnum;
+};
+
+/**********************************************************************
+
+ Note that the struct regsubkey_ctr and struct regval_ctr objects *must* be
+ talloc()'d since the methods use the object pointer as the talloc
+ context for internal private data.
+
+ There is no longer a regval_ctr_init() and regval_ctr_destroy()
+ pair of functions. Simply talloc_zero() and TALLOC_FREE() the
+ object.
+
+ **********************************************************************/
+
+WERROR regsubkey_ctr_init(TALLOC_CTX *mem_ctx, struct regsubkey_ctr **ctr)
+{
+ if (ctr == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ *ctr = talloc_zero(mem_ctx, struct regsubkey_ctr);
+ if (*ctr == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ (*ctr)->subkeys_hash = db_open_rbt(*ctr);
+ if ((*ctr)->subkeys_hash == NULL) {
+ talloc_free(*ctr);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ return WERR_OK;
+}
+
+/**
+ * re-initialize the list of subkeys (to the empty list)
+ * in an already allocated regsubkey_ctr
+ */
+
+WERROR regsubkey_ctr_reinit(struct regsubkey_ctr *ctr)
+{
+ if (ctr == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ talloc_free(ctr->subkeys_hash);
+ ctr->subkeys_hash = db_open_rbt(ctr);
+ W_ERROR_HAVE_NO_MEMORY(ctr->subkeys_hash);
+
+ TALLOC_FREE(ctr->subkeys);
+
+ ctr->num_subkeys = 0;
+ ctr->seqnum = 0;
+
+ return WERR_OK;
+}
+
+WERROR regsubkey_ctr_set_seqnum(struct regsubkey_ctr *ctr, int seqnum)
+{
+ if (ctr == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ ctr->seqnum = seqnum;
+
+ return WERR_OK;
+}
+
+int regsubkey_ctr_get_seqnum(struct regsubkey_ctr *ctr)
+{
+ if (ctr == NULL) {
+ return -1;
+ }
+
+ return ctr->seqnum;
+}
+
+static WERROR regsubkey_ctr_hash_keyname(struct regsubkey_ctr *ctr,
+ const char *keyname,
+ uint32_t idx)
+{
+ WERROR werr;
+
+ werr = ntstatus_to_werror(dbwrap_store_bystring_upper(ctr->subkeys_hash,
+ keyname,
+ make_tdb_data((uint8_t *)&idx,
+ sizeof(idx)),
+ TDB_REPLACE));
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(1, ("error hashing new key '%s' in container: %s\n",
+ keyname, win_errstr(werr)));
+ }
+
+ return werr;
+}
+
+static WERROR regsubkey_ctr_unhash_keyname(struct regsubkey_ctr *ctr,
+ const char *keyname)
+{
+ WERROR werr;
+
+ werr = ntstatus_to_werror(dbwrap_delete_bystring_upper(ctr->subkeys_hash,
+ keyname));
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(1, ("error unhashing key '%s' in container: %s\n",
+ keyname, win_errstr(werr)));
+ }
+
+ return werr;
+}
+
+static WERROR regsubkey_ctr_index_for_keyname(struct regsubkey_ctr *ctr,
+ const char *keyname,
+ uint32_t *idx)
+{
+ TDB_DATA data;
+ NTSTATUS status;
+
+ if ((ctr == NULL) || (keyname == NULL)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ status = dbwrap_fetch_bystring_upper(ctr->subkeys_hash, ctr, keyname,
+ &data);
+ if (!NT_STATUS_IS_OK(status)) {
+ return WERR_NOT_FOUND;
+ }
+
+ if (data.dsize != sizeof(*idx)) {
+ talloc_free(data.dptr);
+ return WERR_INVALID_DATATYPE;
+ }
+
+ if (idx != NULL) {
+ memcpy(idx, data.dptr, sizeof(*idx));
+ }
+
+ talloc_free(data.dptr);
+ return WERR_OK;
+}
+
+/***********************************************************************
+ Add a new key to the array
+ **********************************************************************/
+
+WERROR regsubkey_ctr_addkey( struct regsubkey_ctr *ctr, const char *keyname )
+{
+ char **newkeys;
+ WERROR werr;
+
+ if ( !keyname ) {
+ return WERR_OK;
+ }
+
+ /* make sure the keyname is not already there */
+
+ if ( regsubkey_ctr_key_exists( ctr, keyname ) ) {
+ return WERR_OK;
+ }
+
+ if (!(newkeys = talloc_realloc(ctr, ctr->subkeys, char *,
+ ctr->num_subkeys+1))) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ ctr->subkeys = newkeys;
+
+ if (!(ctr->subkeys[ctr->num_subkeys] = talloc_strdup(ctr->subkeys,
+ keyname ))) {
+ /*
+ * Don't shrink the new array again, this wastes a pointer
+ */
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ werr = regsubkey_ctr_hash_keyname(ctr, keyname, ctr->num_subkeys);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ ctr->num_subkeys++;
+
+ return WERR_OK;
+}
+
+ /***********************************************************************
+ Delete a key from the array
+ **********************************************************************/
+
+WERROR regsubkey_ctr_delkey( struct regsubkey_ctr *ctr, const char *keyname )
+{
+ WERROR werr;
+ uint32_t idx, j;
+
+ if (keyname == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ /* make sure the keyname is actually already there */
+
+ werr = regsubkey_ctr_index_for_keyname(ctr, keyname, &idx);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ werr = regsubkey_ctr_unhash_keyname(ctr, keyname);
+ W_ERROR_NOT_OK_RETURN(werr);
+
+ /* update if we have any keys left */
+ ctr->num_subkeys--;
+ if (idx < ctr->num_subkeys) {
+ memmove(&ctr->subkeys[idx], &ctr->subkeys[idx+1],
+ sizeof(char *) * (ctr->num_subkeys - idx));
+
+ /* we have to re-hash rest of the array... :-( */
+ for (j = idx; j < ctr->num_subkeys; j++) {
+ werr = regsubkey_ctr_hash_keyname(ctr, ctr->subkeys[j], j);
+ W_ERROR_NOT_OK_RETURN(werr);
+ }
+ }
+
+ return WERR_OK;
+}
+
+/***********************************************************************
+ Check for the existence of a key
+ **********************************************************************/
+
+bool regsubkey_ctr_key_exists( struct regsubkey_ctr *ctr, const char *keyname )
+{
+ WERROR werr;
+
+ if (!ctr->subkeys) {
+ return False;
+ }
+
+ werr = regsubkey_ctr_index_for_keyname(ctr, keyname, NULL);
+ if (!W_ERROR_IS_OK(werr)) {
+ return false;
+ }
+
+ return true;
+}
+
+/***********************************************************************
+ How many keys does the container hold ?
+ **********************************************************************/
+
+uint32_t regsubkey_ctr_numkeys( struct regsubkey_ctr *ctr )
+{
+ return ctr->num_subkeys;
+}
+
+/***********************************************************************
+ Retrieve a specific key string
+ **********************************************************************/
+
+char* regsubkey_ctr_specific_key( struct regsubkey_ctr *ctr, uint32_t key_index )
+{
+ if ( ! (key_index < ctr->num_subkeys) )
+ return NULL;
+
+ return ctr->subkeys[key_index];
+}
+
+/*
+ * Utility functions for struct regval_ctr
+ */
+
+/**
+ * allocate a regval_ctr structure.
+ */
+WERROR regval_ctr_init(TALLOC_CTX *mem_ctx, struct regval_ctr **ctr)
+{
+ if (ctr == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ *ctr = talloc_zero(mem_ctx, struct regval_ctr);
+ if (*ctr == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ return WERR_OK;
+}
+
+/***********************************************************************
+ How many keys does the container hold ?
+ **********************************************************************/
+
+uint32_t regval_ctr_numvals(struct regval_ctr *ctr)
+{
+ return ctr->num_values;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+uint8_t* regval_data_p(struct regval_blob *val)
+{
+ return val->data_p;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+uint32_t regval_size(struct regval_blob *val)
+{
+ return val->size;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+char* regval_name(struct regval_blob *val)
+{
+ return val->valuename;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+uint32_t regval_type(struct regval_blob *val)
+{
+ return val->type;
+}
+
+/***********************************************************************
+ Retrieve a pointer to a specific value. Caller should dup the structure
+ since this memory will go away when the ctr is free()'d
+ **********************************************************************/
+
+struct regval_blob *regval_ctr_specific_value(struct regval_ctr *ctr,
+ uint32_t idx)
+{
+ if ( !(idx < ctr->num_values) )
+ return NULL;
+
+ return ctr->values[idx];
+}
+
+/***********************************************************************
+ Check for the existence of a value
+ **********************************************************************/
+
+bool regval_ctr_value_exists(struct regval_ctr *ctr, const char *value)
+{
+ uint32_t i;
+
+ for ( i=0; i<ctr->num_values; i++ ) {
+ if ( strequal( ctr->values[i]->valuename, value) )
+ return True;
+ }
+
+ return False;
+}
+
+/**
+ * Get a value by its name
+ */
+struct regval_blob *regval_ctr_value_byname(struct regval_ctr *ctr,
+ const char *value)
+{
+ uint32_t i;
+
+ for (i = 0; i < ctr->num_values; i++) {
+ if (strequal(ctr->values[i]->valuename, value)) {
+ return ctr->values[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+/***********************************************************************
+ * compose a struct regval_blob from input data
+ **********************************************************************/
+
+struct regval_blob *regval_compose(TALLOC_CTX *ctx, const char *name,
+ uint32_t type,
+ const uint8_t *data_p, size_t size)
+{
+ struct regval_blob *regval = talloc(ctx, struct regval_blob);
+
+ if (regval == NULL) {
+ return NULL;
+ }
+
+ fstrcpy(regval->valuename, name);
+ regval->type = type;
+ if (size) {
+ regval->data_p = (uint8_t *)talloc_memdup(regval, data_p, size);
+ if (!regval->data_p) {
+ TALLOC_FREE(regval);
+ return NULL;
+ }
+ } else {
+ regval->data_p = NULL;
+ }
+ regval->size = size;
+
+ return regval;
+}
+
+/***********************************************************************
+ Add a new registry value to the array
+ **********************************************************************/
+
+int regval_ctr_addvalue(struct regval_ctr *ctr, const char *name, uint32_t type,
+ const uint8_t *data_p, size_t size)
+{
+ if ( !name )
+ return ctr->num_values;
+
+ /* Delete the current value (if it exists) and add the new one */
+
+ regval_ctr_delvalue( ctr, name );
+
+ /* allocate a slot in the array of pointers */
+
+ if ( ctr->num_values == 0 ) {
+ ctr->values = talloc( ctr, struct regval_blob *);
+ } else {
+ ctr->values = talloc_realloc(ctr, ctr->values,
+ struct regval_blob *,
+ ctr->num_values+1);
+ }
+
+ if (!ctr->values) {
+ ctr->num_values = 0;
+ return 0;
+ }
+
+ /* allocate a new value and store the pointer in the array */
+
+ ctr->values[ctr->num_values] = regval_compose(ctr, name, type, data_p,
+ size);
+ if (ctr->values[ctr->num_values] == NULL) {
+ ctr->num_values = 0;
+ return 0;
+ }
+ ctr->num_values++;
+
+ return ctr->num_values;
+}
+
+/***********************************************************************
+ Add a new registry SZ value to the array
+ **********************************************************************/
+
+int regval_ctr_addvalue_sz(struct regval_ctr *ctr, const char *name, const char *data)
+{
+ DATA_BLOB blob;
+
+ if (!push_reg_sz(ctr, &blob, data)) {
+ return -1;
+ }
+
+ return regval_ctr_addvalue(ctr, name, REG_SZ,
+ (const uint8_t *)blob.data,
+ blob.length);
+}
+
+/***********************************************************************
+ Add a new registry MULTI_SZ value to the array
+ **********************************************************************/
+
+int regval_ctr_addvalue_multi_sz(struct regval_ctr *ctr, const char *name, const char **data)
+{
+ DATA_BLOB blob;
+
+ if (!push_reg_multi_sz(ctr, &blob, data)) {
+ return -1;
+ }
+
+ return regval_ctr_addvalue(ctr, name, REG_MULTI_SZ,
+ (const uint8_t *)blob.data,
+ blob.length);
+}
+
+/***********************************************************************
+ Add a new registry value to the array
+ **********************************************************************/
+
+int regval_ctr_copyvalue(struct regval_ctr *ctr, struct regval_blob *val)
+{
+ if ( val ) {
+ regval_ctr_addvalue(ctr, val->valuename, val->type,
+ (uint8_t *)val->data_p, val->size);
+ }
+
+ return ctr->num_values;
+}
+
+/***********************************************************************
+ Delete a single value from the registry container.
+ No need to free memory since it is talloc'd.
+ **********************************************************************/
+
+int regval_ctr_delvalue(struct regval_ctr *ctr, const char *name)
+{
+ uint32_t i;
+
+ for ( i=0; i<ctr->num_values; i++ ) {
+ if ( strequal( ctr->values[i]->valuename, name ) )
+ break;
+ }
+
+ /* just return if we don't find it */
+
+ if ( i == ctr->num_values )
+ return ctr->num_values;
+
+ /* If 'i' was not the last element, just shift everything down one */
+ ctr->num_values--;
+ if ( i < ctr->num_values )
+ memmove(&ctr->values[i], &ctr->values[i+1],
+ sizeof(struct regval_blob*)*(ctr->num_values-i));
+
+ return ctr->num_values;
+}
+
+/***********************************************************************
+ Retrieve single value from the registry container.
+ No need to free memory since it is talloc'd.
+ **********************************************************************/
+
+struct regval_blob* regval_ctr_getvalue(struct regval_ctr *ctr,
+ const char *name)
+{
+ uint32_t i;
+
+ /* search for the value */
+
+ for ( i=0; i<ctr->num_values; i++ ) {
+ if ( strequal( ctr->values[i]->valuename, name ) )
+ return ctr->values[i];
+ }
+
+ return NULL;
+}
+
+int regval_ctr_get_seqnum(struct regval_ctr *ctr)
+{
+ if (ctr == NULL) {
+ return -1;
+ }
+
+ return ctr->seqnum;
+}
+
+WERROR regval_ctr_set_seqnum(struct regval_ctr *ctr, int seqnum)
+{
+ if (ctr == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ ctr->seqnum = seqnum;
+
+ return WERR_OK;
+}
diff --git a/source3/registry/reg_objects.h b/source3/registry/reg_objects.h
new file mode 100644
index 0000000..f8a1788
--- /dev/null
+++ b/source3/registry/reg_objects.h
@@ -0,0 +1,74 @@
+/*
+ Samba's Internal Registry objects
+
+ SMB parameters and setup
+ Copyright (C) Gerald Carter 2002-2006.
+ Copyright (C) Michael Adam 2007-2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _REG_OBJECTS_H /* _REG_OBJECTS_H */
+#define _REG_OBJECTS_H
+
+/* low level structure to contain registry values */
+
+struct regval_blob;
+
+/* container for registry values */
+
+struct regval_ctr;
+
+/* container for registry subkey names */
+
+struct regsubkey_ctr;
+
+/* The following definitions come from registry/reg_objects.c */
+
+WERROR regsubkey_ctr_init(TALLOC_CTX *mem_ctx, struct regsubkey_ctr **ctr);
+WERROR regsubkey_ctr_reinit(struct regsubkey_ctr *ctr);
+WERROR regsubkey_ctr_set_seqnum(struct regsubkey_ctr *ctr, int seqnum);
+int regsubkey_ctr_get_seqnum(struct regsubkey_ctr *ctr);
+WERROR regsubkey_ctr_addkey( struct regsubkey_ctr *ctr, const char *keyname );
+WERROR regsubkey_ctr_delkey( struct regsubkey_ctr *ctr, const char *keyname );
+bool regsubkey_ctr_key_exists( struct regsubkey_ctr *ctr, const char *keyname );
+uint32_t regsubkey_ctr_numkeys( struct regsubkey_ctr *ctr );
+char* regsubkey_ctr_specific_key( struct regsubkey_ctr *ctr, uint32_t key_index );
+WERROR regval_ctr_init(TALLOC_CTX *mem_ctx, struct regval_ctr **ctr);
+uint32_t regval_ctr_numvals(struct regval_ctr *ctr);
+uint8_t* regval_data_p(struct regval_blob *val);
+uint32_t regval_size(struct regval_blob *val);
+char* regval_name(struct regval_blob *val);
+uint32_t regval_type(struct regval_blob *val);
+struct regval_blob* regval_ctr_specific_value(struct regval_ctr *ctr,
+ uint32_t idx);
+struct regval_blob *regval_ctr_value_byname(struct regval_ctr *ctr,
+ const char *value);
+bool regval_ctr_value_exists(struct regval_ctr *ctr, const char *value);
+struct regval_blob *regval_compose(TALLOC_CTX *ctx, const char *name,
+ uint32_t type,
+ const uint8_t *data_p, size_t size);
+int regval_ctr_addvalue(struct regval_ctr *ctr, const char *name, uint32_t type,
+ const uint8_t *data_p, size_t size);
+int regval_ctr_addvalue_sz(struct regval_ctr *ctr, const char *name, const char *data);
+int regval_ctr_addvalue_multi_sz(struct regval_ctr *ctr, const char *name, const char **data);
+int regval_ctr_copyvalue(struct regval_ctr *ctr, struct regval_blob *val);
+int regval_ctr_delvalue(struct regval_ctr *ctr, const char *name);
+struct regval_blob* regval_ctr_getvalue(struct regval_ctr *ctr,
+ const char *name);
+int regval_ctr_get_seqnum(struct regval_ctr *ctr);
+WERROR regval_ctr_set_seqnum(struct regval_ctr *ctr, int seqnum);
+
+
+#endif /* _REG_OBJECTS_H */
diff --git a/source3/registry/reg_parse.c b/source3/registry/reg_parse.c
new file mode 100644
index 0000000..b6a7f68
--- /dev/null
+++ b/source3/registry/reg_parse.c
@@ -0,0 +1,1090 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @brief Parser for dot.reg files
+ * @file reg_parse.c
+ * @author Gregor Beck <gb@sernet.de>
+ * @date Jun 2010
+ *
+ */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "cbuf.h"
+#include "srprs.h"
+#include "reg_parse_internal.h"
+#include "reg_parse.h"
+#include "reg_format.h"
+
+#include <stdio.h>
+#include <talloc.h>
+#include <stdbool.h>
+#include <string.h>
+#include <regex.h>
+#include <assert.h>
+#include <stdint.h>
+
+enum reg_parse_state {
+ STATE_DEFAULT,
+ STATE_KEY_OPEN,
+ STATE_VAL_HEX_CONT,
+ STATE_VAL_SZ_CONT
+};
+
+struct reg_parse {
+ struct reg_format_callback reg_format_callback;
+ cbuf* key;
+ cbuf* valname;
+ uint32_t valtype;
+ cbuf* valblob;
+ cbuf* tmp;
+ struct reg_parse_callback call;
+ int ret;
+ int linenum;
+ enum reg_parse_state state;
+ struct reg_parse_options* opt;
+ smb_iconv_t str2UTF16;
+ unsigned flags;
+};
+
+/**
+ * @defgroup action Action
+ * @{
+ */
+static bool act_key(struct reg_parse* p, cbuf* keyname, bool del)
+{
+ const char* name = cbuf_gets(keyname, 0);
+ cbuf_swap(p->key, keyname);
+
+ assert(p->state == STATE_DEFAULT || p->state == STATE_KEY_OPEN);
+ p->state = del ? STATE_DEFAULT : STATE_KEY_OPEN;
+
+ assert(p->call.key);
+ p->ret = p->call.key(p->call.data, &name, 1, del);
+ return p->ret >= 0;
+}
+
+static bool value_callback(struct reg_parse* p)
+{
+ const char* name = cbuf_gets(p->valname,0);
+ const uint8_t* val = (const uint8_t*)cbuf_gets(p->valblob,0);
+ size_t len = cbuf_getpos(p->valblob);
+
+ assert(p->call.val);
+ p->ret = p->call.val(p->call.data, name, p->valtype, val, len);
+ return p->ret >= 0;
+}
+
+static bool act_val_hex(struct reg_parse* p, cbuf* value, bool cont)
+{
+ cbuf_swap(p->valblob, value);
+ assert((p->state == STATE_KEY_OPEN) || (p->state == STATE_VAL_HEX_CONT));
+
+ if (cont) {
+ p->state = STATE_VAL_HEX_CONT;
+ } else {
+ p->state = STATE_KEY_OPEN;
+
+ switch (p->valtype) {
+ case REG_EXPAND_SZ:
+ case REG_MULTI_SZ:
+ if (p->str2UTF16 != NULL) {
+ char* dst = NULL;
+ const char* src = cbuf_gets(p->valblob, 0);
+ const size_t slen = cbuf_getpos(p->valblob);
+ size_t dlen = iconvert_talloc(p,
+ p->str2UTF16,
+ src, slen,
+ &dst);
+ if (dlen != -1) {
+ cbuf_swapptr(p->valblob, &dst, dlen);
+ } else {
+ DEBUG(0, ("iconvert_talloc failed\n"));
+ return false;
+ }
+ talloc_free(dst);
+ }
+ break;
+ default:
+ break;
+ }
+ return value_callback(p);
+ }
+ return true;
+}
+
+static bool act_val_dw(struct reg_parse* p, uint32_t val)
+{
+ assert(p->valtype == REG_DWORD);
+ assert(p->state == STATE_KEY_OPEN);
+
+ cbuf_clear(p->valblob);
+
+ if (cbuf_putdw(p->valblob, val) < 0) {
+ return false;
+ }
+ return value_callback(p);
+}
+
+static bool act_val_sz(struct reg_parse* p, cbuf* value, bool cont)
+{
+ cbuf_swap(p->valblob, value);
+
+ assert(p->valtype == REG_SZ);
+ assert((p->state == STATE_KEY_OPEN) || (p->state == STATE_VAL_SZ_CONT));
+
+ if (cont) {
+ p->state = STATE_VAL_SZ_CONT;
+ } else {
+ char* dst = NULL;
+ size_t dlen;
+ const char* src = cbuf_gets(p->valblob, 0);
+
+ p->state = STATE_KEY_OPEN;
+
+
+ if (convert_string_talloc(p->valblob, CH_UNIX, CH_UTF16LE,
+ src, strlen(src)+1,
+ &dst, &dlen))
+ {
+ cbuf_swapptr(p->valblob, &dst, dlen);
+ } else {
+ DEBUG(0, ("convert_string_talloc failed: >%s<\n"
+ "use it as is\t", src));
+ return false;
+ }
+ talloc_free(dst);
+
+ return value_callback(p);
+ }
+ return true;
+}
+
+static bool act_val_del(struct reg_parse* p)
+{
+ const char* name = cbuf_gets(p->valname, 0);
+
+ assert(p->call.val_del);
+ p->ret = p->call.val_del(p->call.data, name);
+ return p->ret >= 0;
+}
+
+static bool act_comment (struct reg_parse* p, const char* txt)
+{
+ assert(p->call.comment);
+ p->ret = p->call.comment(p->call.data, txt);
+ return p->ret >= 0;
+}
+/**@}*/
+
+
+static int nop_callback_key(void* private_data,
+ const char* key[],
+ size_t klen,
+ bool del)
+{
+ return 0;
+}
+
+static int nop_callback_val(void* private_data,
+ const char* name,
+ uint32_t type,
+ const uint8_t* data,
+ size_t len)
+{
+ return 0;
+}
+
+static int nop_callback_del(void* data, const char* str)
+{
+ return 0;
+}
+
+struct reg_parse* reg_parse_new(const void* ctx,
+ struct reg_parse_callback cb,
+ const char* str_enc, unsigned flags)
+{
+ struct reg_parse* s = talloc_zero(ctx, struct reg_parse);
+ if (s == NULL)
+ return NULL;
+ s->key = cbuf_new(s);
+ s->valname = cbuf_new(s);
+ s->valblob = cbuf_new(s);
+ s->tmp = cbuf_new(s);
+ if ( (s->tmp == NULL) || (s->valblob == NULL)
+ || (s->valname == NULL) || (s->key == NULL) )
+ {
+ goto fail;
+ }
+
+ s->reg_format_callback.writeline = (reg_format_callback_writeline_t)&reg_parse_line;
+ s->reg_format_callback.data = s;
+
+ s->valtype = 0;
+ if (cb.key == NULL) {
+ cb.key = (reg_parse_callback_key_t)&nop_callback_key;
+ }
+ if (cb.val == NULL) {
+ cb.val = (reg_parse_callback_val_t)&nop_callback_val;
+ }
+ if (cb.val_del == NULL) {
+ cb.val_del = (reg_parse_callback_val_del_t)&nop_callback_del;
+ }
+ if (cb.comment == NULL) {
+ cb.comment =
+ (reg_parse_callback_comment_t)&nop_callback_del;
+ }
+
+ s->call = cb;
+ s->linenum = 0;
+ s->state = STATE_DEFAULT;
+ s->flags = flags;
+
+ if (str_enc && !set_iconv(&s->str2UTF16, "UTF-16LE", str_enc)) {
+ DEBUG(0, ("reg_parse_new: failed to set encoding: %s\n",
+ str_enc));
+ goto fail;
+ }
+
+ assert(&s->reg_format_callback == (struct reg_format_callback*)s);
+ return s;
+fail:
+ set_iconv(&s->str2UTF16, NULL, NULL);
+ talloc_free(s);
+ return NULL;
+}
+
+/**
+ * @defgroup parse Parser Primitive
+ * @ingroup internal
+ * @{
+ */
+
+
+static bool srprs_key(const char** ptr, cbuf* key, bool* del)
+{
+ const char* pos = *ptr;
+ const char* closing_bracket_pos = NULL;
+ size_t closing_bracket_idx = 0;
+
+ if (!srprs_skipws(&pos) || !srprs_char(&pos, '[')) {
+ return false;
+ }
+
+ *del = srprs_char(&pos, '-');
+
+ cbuf_clear(key);
+
+ while (true) {
+ while (srprs_charsetinv(&pos, "]\\", key))
+ ;
+
+ switch (*pos) {
+
+ case ']':
+ closing_bracket_idx = cbuf_getpos(key);
+ closing_bracket_pos = pos;
+ cbuf_putc(key, ']');
+ pos++;
+ break;
+
+ case '\\':
+ cbuf_putc(key, '\\');
+ /* n++; */
+ /* cbuf_puts(subkeyidx, cbuf_getpos(key), sizeof(size_t)) */
+ while (srprs_char(&pos,'\\'))
+ ;
+ break;
+
+ case '\0':
+ if (closing_bracket_pos == NULL) {
+ return false;
+ }
+
+ /* remove trailing backslash (if any) */
+ if (*(closing_bracket_pos-1)=='\\') {
+ closing_bracket_idx--;
+ }
+
+ cbuf_setpos(key, closing_bracket_idx);
+ *ptr = closing_bracket_pos+1;
+ return true;
+
+ default:
+ assert(false);
+ }
+ }
+}
+
+static bool srprs_val_name(const char** ptr, cbuf* name)
+{
+ const char* pos = *ptr;
+ const size_t spos = cbuf_getpos(name);
+
+ if ( !srprs_skipws(&pos) ) {
+ goto fail;
+ }
+
+ if ( srprs_char(&pos, '@') ) {
+ cbuf_puts(name, "", -1);
+ }
+ else if (!srprs_quoted_string(&pos, name, NULL)) {
+ goto fail;
+ }
+
+ if (!srprs_skipws(&pos) || !srprs_char(&pos, '=')) {
+ goto fail;
+ }
+
+ *ptr = pos;
+ return true;
+
+fail:
+ cbuf_setpos(name, spos);
+ return false;
+}
+
+static bool srprs_val_dword(const char** ptr, uint32_t* type, uint32_t* val)
+{
+ const char* pos = *ptr;
+
+ if (!srprs_str(&pos, "dword:", -1)) {
+ return false;
+ }
+
+ if (!srprs_hex(&pos, 8, val)) {
+ return false;
+ }
+
+ *type = REG_DWORD;
+ *ptr = pos;
+ return true;
+}
+
+static bool srprs_val_sz(const char** ptr, uint32_t* type, cbuf* val, bool* cont)
+{
+ if (!srprs_quoted_string(ptr, val, cont)) {
+ return false;
+ }
+
+ *type = REG_SZ;
+ return true;
+}
+
+
+static bool srprs_nl_no_eos(const char** ptr, cbuf* str, bool eof)
+{
+ const char* pos = *ptr;
+ const size_t spos = cbuf_getpos(str);
+
+ if( srprs_nl(&pos, str) && (eof || *pos != '\0')) {
+ *ptr = pos;
+ return true;
+ }
+ cbuf_setpos(str, spos);
+ return false;
+}
+
+
+static bool srprs_eol_cont(const char** ptr, bool* cont)
+{
+ const char* pos = *ptr;
+ bool bs = srprs_char(&pos, '\\');
+
+ if (!srprs_eol(&pos, NULL)) {
+ return false;
+ }
+
+ *cont = bs;
+ *ptr = pos;
+ return true;
+}
+
+/* matches the empty string, for zero length lists */
+static bool srprs_val_hex_values(const char** ptr, cbuf* val, bool* cont)
+{
+ const char* pos = *ptr;
+ unsigned u;
+
+ do {
+ if (!srprs_skipws(&pos) || !srprs_hex(&pos, 2, &u) || !srprs_skipws(&pos)) {
+ break;
+ }
+ cbuf_putc(val, (char)u);
+ } while(srprs_char(&pos, ','));
+
+ *ptr = pos;
+
+ if (srprs_skipws(&pos) && srprs_eol_cont(&pos, cont)) {
+ *ptr = pos;
+ }
+
+ return true;
+}
+
+static bool srprs_val_hex(const char** ptr, uint32_t* ptype, cbuf* val,
+ bool* cont)
+{
+ const char* pos = *ptr;
+ uint32_t type;
+
+ if (!srprs_str(&pos, "hex", -1)) {
+ return false;
+ }
+
+ if (srprs_char(&pos, ':')) {
+ type = REG_BINARY;
+ }
+ else if (!srprs_char(&pos, '(') ||
+ !srprs_hex(&pos, 8, &type) ||
+ !srprs_char(&pos,')') ||
+ !srprs_char(&pos, ':'))
+ {
+ return false;
+ }
+
+ if (!srprs_val_hex_values(&pos, val, cont)) {
+ return false;
+ }
+
+ *ptype = type;
+ *ptr = pos;
+ return true;
+}
+
+
+static bool srprs_comment(const char** ptr, cbuf* str)
+{
+ return srprs_char(ptr, ';') && srprs_line(ptr, str);
+}
+
+/**@}*/
+
+int reg_parse_set_options(struct reg_parse* parser, const char* options)
+{
+ static const char* DEFAULT ="enc=unix,flags=0";
+
+ int ret = 0;
+ char *key, *val;
+ void* ctx = talloc_new(parser);
+
+ if (options == NULL) {
+ options = DEFAULT;
+ }
+
+ while (srprs_option(&options, ctx, &key, &val)) {
+ if ((strcmp(key, "enc") == 0) || (strcmp(key, "strenc") == 0)) {
+ } else if ((strcmp(key, "flags") == 0) && (val != NULL)) {
+ char* end = NULL;
+ if (val != NULL) {
+ parser->flags = strtol(val, &end, 0);
+ }
+ if ((end==NULL) || (*end != '\0')) {
+ DEBUG(0, ("Invalid flags format: %s\n",
+ val ? val : "<NULL>"));
+ ret = -1;
+ }
+ }
+ /* else if (strcmp(key, "hive") == 0) { */
+ /* if (strcmp(val, "short") == 0) { */
+ /* f->hive_fmt = REG_FMT_SHORT_HIVES; */
+ /* } else if (strcmp(val, "long") == 0) { */
+ /* f->hive_fmt = REG_FMT_LONG_HIVES; */
+ /* } else if (strcmp(val, "preserve") == 0) { */
+ /* f->hive_fmt = REG_FMT_PRESERVE_HIVES; */
+ /* } else { */
+ /* DEBUG(0, ("Invalid hive format: %s\n", val)); */
+ /* ret = -1; */
+ /* } */
+ /* } */
+ }
+ talloc_free(ctx);
+ return ret;
+}
+
+
+int reg_parse_line(struct reg_parse* parser, const char* line)
+{
+ const char* pos;
+ bool del=false;
+ cbuf* tmp=cbuf_clear(parser->tmp);
+ bool cb_ok = true;
+ bool cont = true;
+
+ if (!line) {
+ return -4;
+ }
+
+ parser->linenum++;
+ pos = line;
+
+ switch (parser->state) {
+ case STATE_VAL_HEX_CONT:
+ if (srprs_val_hex_values(&pos, parser->valblob, &cont)) {
+ cb_ok = act_val_hex(parser, parser->valblob, cont);
+ }
+ goto done;
+ case STATE_VAL_SZ_CONT:
+ if (srprs_quoted_string(&pos, parser->valblob, &cont)) {
+ cb_ok = act_val_sz(parser, parser->valblob, cont);
+ }
+ goto done;
+ default:
+ cont = false;
+ }
+
+ if ( !srprs_skipws(&pos) ) {
+ return -4;
+ }
+
+ /* empty line ?*/
+ if ( srprs_eol(&pos, NULL) ) {
+ return 0;
+ }
+
+ /* key line ?*/
+ else if (srprs_key(&pos, tmp, &del)) {
+ cb_ok = act_key(parser, tmp, del);
+ }
+
+ /* comment line ? */
+ else if (srprs_comment(&pos, tmp)) {
+ cb_ok = act_comment(parser, cbuf_gets(tmp, 0));
+ }
+
+ /* head line */
+ else if ((parser->linenum == 1) && srprs_line(&pos, tmp) ) {
+ /* cb_ok = act_head(parser, cbuf_gets(tmp, 0)); */
+ }
+
+ /* value line ?*/
+ else if (srprs_val_name(&pos, tmp)) {
+ uint32_t dw;
+ cbuf_swap(parser->valname, tmp);
+ cbuf_clear(tmp);
+
+ if (parser->state != STATE_KEY_OPEN) {
+ DEBUG(0, ("value \"%s\" without a key at line: %i\n",
+ cbuf_gets(parser->valname, 0), parser->linenum));
+ return -3;
+ }
+
+ if (!srprs_skipws(&pos)) {
+ return -4;
+ }
+
+ if (srprs_char(&pos, '-')) {
+ cb_ok = act_val_del(parser);
+ }
+ else if (srprs_val_dword(&pos, &parser->valtype, &dw)) {
+ cb_ok = act_val_dw(parser, dw);
+ }
+ else if (srprs_val_sz(&pos, &parser->valtype, tmp, &cont)) {
+ cb_ok = act_val_sz(parser, tmp, cont);
+ }
+ else if (srprs_val_hex(&pos, &parser->valtype, tmp, &cont)){
+ cb_ok = act_val_hex(parser, tmp, cont);
+ }
+ else {
+ DEBUG(0, ("value \"%s\" parse error"
+ "at line: %i pos: %li : %s\n",
+ cbuf_gets(parser->valname, 0), parser->linenum,
+ (long int)(pos-line), pos));
+ return -3;
+ }
+ }
+ else {
+ DEBUG(0, ("unrecognized line %i : %s\n", parser->linenum, line));
+ return -3;
+ }
+
+done:
+ if (!cb_ok)
+ return -2;
+
+ if (!srprs_skipws(&pos) || !srprs_eol(&pos, NULL)) {
+ DEBUG(0, ("trailing garbage at line: %i pos: %li : %s\n",
+ parser->linenum, (long int)(pos-line), pos));
+ return -1;
+ }
+ return 0;
+}
+
+/******************************************************************************/
+/**
+ * @addtogroup misc
+ * @{
+ */
+static bool lookslike_utf16(const char* line, size_t len, bool* little_endian)
+{
+ static const uint16_t M_LE = 0xFF80;
+ static const uint16_t M_BE = 0x80FF;
+ uint16_t mask;
+ bool le;
+
+ size_t l = MIN(len/2, 64);
+ const uint16_t* u = (const uint16_t*)line;
+ size_t i;
+
+ assert(len >= 2);
+
+ if ( u[0] & M_LE ) {
+ le = true;
+ mask = M_LE;
+ } else if ( u[0] & M_BE ) {
+ le = false;
+ mask = M_BE;
+ } else {
+ return false;
+ }
+
+ for (i=1; i<l; i++) {
+ if ( u[i] & mask ) {
+ return false;
+ }
+ }
+
+ *little_endian = le;
+ return true;
+}
+
+static bool lookslike_dos(const char* line, size_t len)
+{
+ size_t i;
+ for (i=0; i<len; i++) {
+ if ( (line[i] == '\0') || (line[i] & 0x80) ) {
+ return false;
+ }
+ if ( (line[i] == '\r') && (i+1 < len) && (line[i+1] == '\n') ) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool guess_charset(const char** ptr,
+ size_t* len,
+ const char** file_enc,
+ const char** str_enc)
+{
+ const char* charset = NULL;
+ const char* pos = *ptr;
+
+ if (*len < 4) {
+ return false;
+ }
+
+ if (srprs_bom(&pos, &charset, NULL)) {
+ size_t declen;
+ if (pos < *ptr) {
+ return false;
+ }
+ declen = (pos - *ptr);
+ if (*len < declen) {
+ return false;
+ }
+ *len -= declen;
+ *ptr = pos;
+ if (*file_enc == NULL) {
+ *file_enc = charset;
+ }
+ else if( strcmp(*file_enc, charset) != 0 ) {
+ DEBUG(0, ("file encoding forced to %s\n",
+ *file_enc));
+ }
+ }
+ else if (*file_enc == NULL) {
+ bool le;
+ if (lookslike_utf16(*ptr, *len, &le)) {
+ *file_enc = le ? "UTF-16LE" : "UTF-16BE";
+ }
+ else if (lookslike_dos(*ptr, *len)) {
+ *file_enc = "dos";
+ }
+ else {
+ *file_enc = "unix";
+ }
+ }
+
+ if ((str_enc != NULL) && (*str_enc == NULL)) {
+ *str_enc = ( strncmp(*ptr, "REGEDIT4", 8) == 0)
+ ? *file_enc
+ : "UTF-16LE";
+ }
+
+ return true;
+}
+/**@}*/
+
+struct reg_parse_fd_opt {
+ const char* file_enc;
+ const char* str_enc;
+ unsigned flags;
+ int fail_level;
+};
+
+static struct reg_parse_fd_opt
+reg_parse_fd_opt(void* mem_ctx, const char* options)
+{
+ struct reg_parse_fd_opt ret = {
+ .file_enc = NULL,
+ .str_enc = NULL,
+ .flags = 0,
+ };
+
+ void* ctx = talloc_new(mem_ctx);
+ char *key, *val;
+
+ if (options == NULL) {
+ goto done;
+ }
+
+ while (srprs_option(&options, ctx, &key, &val)) {
+ if (strcmp(key, "enc") == 0) {
+ ret.file_enc = talloc_steal(mem_ctx, val);
+ ret.str_enc = ret.file_enc;
+ } else if (strcmp(key, "strenc") == 0) {
+ ret.str_enc = talloc_steal(mem_ctx, val);
+ } else if (strcmp(key, "fileenc") == 0) {
+ ret.file_enc = talloc_steal(mem_ctx, val);
+ } else if ((strcmp(key, "flags") == 0) && (val != NULL)) {
+ char* end = NULL;
+ if (val != NULL) {
+ ret.flags = strtol(val, &end, 0);
+ }
+ if ((end==NULL) || (*end != '\0')) {
+ DEBUG(0, ("Invalid format \"%s\": %s\n",
+ key, val ? val : "<NULL>"));
+ }
+ } else if ((strcmp(key, "fail") == 0) && (val != NULL)) {
+ char* end = NULL;
+ if (val != NULL) {
+ ret.fail_level = -strtol(val, &end, 0);
+ }
+ if ((end==NULL) || (*end != '\0')) {
+ DEBUG(0, ("Invalid format \"%s\": %s\n",
+ key, val ? val : "<NULL>"));
+ }
+ }
+ }
+done:
+ talloc_free(ctx);
+ return ret;
+}
+
+static void display_iconv_error_bytes(const char *inbuf, size_t len)
+{
+ size_t i;
+ for (i = 0; i < 4 && i < len; i++) {
+ DEBUGADD(0, ("<%02x>", (unsigned char)inbuf[i]));
+ }
+ DEBUGADD(0, ("\n"));
+}
+
+int reg_parse_fd(int fd, const struct reg_parse_callback* cb, const char* opts)
+{
+ void* mem_ctx = talloc_stackframe();
+ cbuf* line = cbuf_new(mem_ctx);
+ smb_iconv_t cd = (smb_iconv_t)-1;
+ struct reg_parse* parser = NULL;
+ char buf_in[1024];
+ char buf_out[1025] = { 0 };
+ ssize_t nread;
+ const char* iptr;
+ char* optr;
+ size_t ilen;
+ size_t olen;
+ size_t avail_osize = sizeof(buf_out)-1;
+ size_t space_to_read = sizeof(buf_in);
+ int ret = -1;
+ bool eof = false;
+ size_t linecount = 0;
+
+ struct reg_parse_fd_opt opt = reg_parse_fd_opt(mem_ctx, opts);
+
+ if (cb == NULL) {
+ DBG_ERR("NULL callback\n");
+ ret = -1;
+ goto done;
+ }
+
+ nread = read(fd, buf_in, space_to_read);
+ if (nread < 0) {
+ DBG_ERR("read failed: %s\n", strerror(errno));
+ ret = -1;
+ goto done;
+ }
+ if (nread == 0) {
+ /* Empty file. */
+ eof = true;
+ goto done;
+ }
+
+ iptr = buf_in;
+ ilen = nread;
+
+ if (!guess_charset(&iptr, &ilen,
+ &opt.file_enc, &opt.str_enc))
+ {
+ DBG_ERR("reg_parse_fd: failed to guess encoding\n");
+ ret = -1;
+ goto done;
+ }
+
+ if (ilen == 0) {
+ /* File only contained charset info. */
+ eof = true;
+ ret = -1;
+ goto done;
+ }
+
+ DBG_DEBUG("reg_parse_fd: encoding file: %s str: %s\n",
+ opt.file_enc, opt.str_enc);
+
+
+ if (!set_iconv(&cd, "unix", opt.file_enc)) {
+ DBG_ERR("reg_parse_fd: failed to set file encoding %s\n",
+ opt.file_enc);
+ ret = -1;
+ goto done;
+ }
+
+ parser = reg_parse_new(mem_ctx, *cb, opt.str_enc, opt.flags);
+ if (parser == NULL) {
+ ret = -1;
+ goto done;
+ }
+
+ /* Move input data to start of buf_in. */
+ if (iptr > buf_in) {
+ memmove(buf_in, iptr, ilen);
+ iptr = buf_in;
+ }
+
+ optr = buf_out;
+ /* Leave last byte for null termination. */
+ olen = avail_osize;
+
+ /*
+ * We read from buf_in (iptr), iconv converting into
+ * buf_out (optr).
+ */
+
+ while (!eof) {
+ const char *pos;
+ size_t nconv;
+
+ if (olen == 0) {
+ /* We're out of possible room. */
+ DBG_ERR("no room in output buffer\n");
+ ret = -1;
+ goto done;
+ }
+ nconv = smb_iconv(cd, &iptr, &ilen, &optr, &olen);
+ if (nconv == (size_t)-1) {
+ bool valid_err = false;
+ if (errno == EINVAL) {
+ valid_err = true;
+ }
+ if (errno == E2BIG) {
+ valid_err = true;
+ }
+ if (!valid_err) {
+ DBG_ERR("smb_iconv error in file at line %zu: ",
+ linecount);
+ display_iconv_error_bytes(iptr, ilen);
+ ret = -1;
+ goto done;
+ }
+ /*
+ * For valid errors process the
+ * existing buffer then continue.
+ */
+ }
+
+ /*
+ * We know this is safe as we have an extra
+ * enforced zero byte at the end of buf_out.
+ */
+ *optr = '\0';
+ pos = buf_out;
+
+ while (srprs_line(&pos, line) && srprs_nl_no_eos(&pos, line, eof)) {
+ int retval;
+
+ /* Process all lines we got. */
+ retval = reg_parse_line(parser, cbuf_gets(line, 0));
+ if (retval < opt.fail_level) {
+ DBG_ERR("reg_parse_line %zu fail %d\n",
+ linecount,
+ retval);
+ ret = -1;
+ goto done;
+ }
+ cbuf_clear(line);
+ linecount++;
+ }
+ if (pos > buf_out) {
+ /*
+ * The output data we have
+ * processed starts at buf_out
+ * and ends at pos.
+ * The unprocessed output
+ * data starts at pos and
+ * ends at optr.
+ *
+ * <------ sizeof(buf_out) - 1------------->|0|
+ * <--------- avail_osize------------------>|0|
+ * +----------------------+-------+-----------+
+ * | | | |0|
+ * +----------------------+-------+-----------+
+ * ^ ^ ^
+ * | | |
+ * buf_out pos optr
+ */
+ size_t unprocessed_len;
+
+ /* Paranoia checks. */
+ if (optr < pos) {
+ ret = -1;
+ goto done;
+ }
+ unprocessed_len = optr - pos;
+
+ /* Paranoia checks. */
+ if (avail_osize < unprocessed_len) {
+ ret = -1;
+ goto done;
+ }
+ /* Move down any unprocessed data. */
+ memmove(buf_out, pos, unprocessed_len);
+
+ /*
+ * After the move, reset the output length.
+ *
+ * <------ sizeof(buf_out) - 1------------->|0|
+ * <--------- avail_osize------------------>|0|
+ * +----------------------+-------+-----------+
+ * | | |0|
+ * +----------------------+-------+-----------+
+ * ^ ^
+ * | optr
+ * buf_out
+ */
+ optr = buf_out + unprocessed_len;
+ /*
+ * Calculate the new output space available
+ * for iconv.
+ * We already did the paranoia check for this
+ * arithmetic above.
+ */
+ olen = avail_osize - unprocessed_len;
+ }
+
+ /*
+ * Move any unprocessed data to the start of
+ * the input buffer (buf_in).
+ */
+ if (ilen > 0 && iptr > buf_in) {
+ memmove(buf_in, iptr, ilen);
+ }
+
+ /* Is there any space to read more input ? */
+ if (ilen >= sizeof(buf_in)) {
+ /* No space. Nothing was converted. Error. */
+ DBG_ERR("no space in input buffer\n");
+ ret = -1;
+ goto done;
+ }
+
+ space_to_read = sizeof(buf_in) - ilen;
+
+ /* Read the next chunk from the file. */
+ nread = read(fd, buf_in + ilen, space_to_read);
+ if (nread < 0) {
+ DBG_ERR("read failed: %s\n", strerror(errno));
+ ret = -1;
+ goto done;
+ }
+ if (nread == 0) {
+ /* Empty file. */
+ eof = true;
+ continue;
+ }
+
+ /* Paranoia check. */
+ if (nread + ilen < ilen) {
+ ret = -1;
+ goto done;
+ }
+
+ /* Paranoia check. */
+ if (nread + ilen > sizeof(buf_in)) {
+ ret = -1;
+ goto done;
+ }
+
+ iptr = buf_in;
+ ilen = nread + ilen;
+ }
+
+ ret = 0;
+
+done:
+
+ set_iconv(&cd, NULL, NULL);
+ if (parser) {
+ set_iconv(&parser->str2UTF16, NULL, NULL);
+ }
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+int reg_parse_file(const char* fname, const struct reg_parse_callback* cb,
+ const char* opt)
+{
+ int ret = -1;
+ int fd;
+
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
+ DEBUG(0, ("reg_parse_file: open %s failed: %s\n", fname,
+ strerror(errno)));
+ return -1;
+ }
+
+ ret = reg_parse_fd(fd, cb, opt);
+
+ close(fd);
+ return ret;
+}
+
+/* static struct registry_key *find_regkey_by_hnd(pipes_struct *p, */
+/* struct policy_handle *hnd) */
+/* { */
+/* struct registry_key *regkey = NULL; */
+
+/* if(!find_policy_by_hnd(p,hnd,(void **)(void *)&regkey)) { */
+/* DEBUG(2,("find_regkey_index_by_hnd: Registry Key not found: ")); */
+/* return NULL; */
+/* } */
+
+/* return regkey; */
+/* } */
diff --git a/source3/registry/reg_parse.h b/source3/registry/reg_parse.h
new file mode 100644
index 0000000..ba02ec6
--- /dev/null
+++ b/source3/registry/reg_parse.h
@@ -0,0 +1,190 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @brief Parser for registration entries (.reg) files.
+ * A parser is a talloced incarnation of an opaque struct reg_parse.
+ * It is fed with the (.reg) file line by line calling @ref reg_parse_line
+ * and emits output by calling functions from its reg_parse_callback.
+ * @file reg_parse.h
+ * @author Gregor Beck <gb@sernet.de>
+ * @date Jun 2010
+ */
+
+#ifndef REG_PARSE_H
+#define REG_PARSE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/**
+ * Prototype for function called on key found.
+ * The usual action to take is delete the key if del==true, open it if
+ * already existing or create a new one.
+ *
+ * @param private_data
+ * @param key
+ * @param klen number of elements in key
+ * @param del whether to delete the key
+ *
+ * @retval >=0 on success
+ *
+ * @see reg_format_key
+ */
+typedef int (*reg_parse_callback_key_t) (void* private_data,
+ const char* key[],
+ size_t klen,
+ bool del);
+
+/**
+ * Prototype for function called on value found.
+ * The usual action to take is set the value of the last opened key.
+ *
+ * @param private_data
+ * @param name the values name
+ * @param type the values type
+ * @param data the values value
+ * @param len the number of bytes of data
+ *
+ * @retval >=0 on success
+ *
+ * @see reg_format_value
+ */
+typedef int (*reg_parse_callback_val_t) (void* private_data,
+ const char* name,
+ uint32_t type,
+ const uint8_t* data,
+ size_t len);
+
+/**
+ * Prototype for function called on value delete found.
+ * Delete value from the last opened key. It is usually no error if
+ * no such value exist.
+ *
+ * @param private_data
+ * @param name
+ *
+ * @retval >=0 on success
+ *
+ * @see reg_format_value_delete
+ */
+typedef int (*reg_parse_callback_val_del_t) (void* private_data,
+ const char* name);
+
+
+/**
+ * Prototype for function called on comment found.
+ *
+ * @param private_data
+ * @param line comment with marker removed.
+ *
+ * @retval >=0 on success
+ *
+ * @see reg_format_comment
+ */
+typedef int (*reg_parse_callback_comment_t) (void* private_data,
+ const char* line);
+
+/**
+ * Type handling the output of a reg_parse object.
+ * It contains the functions to call and an opaque data pointer.
+ */
+typedef struct reg_parse_callback {
+ reg_parse_callback_key_t key; /**< Function called on key found */
+ reg_parse_callback_val_t val; /**< Function called on value found */
+ /** Function called on value delete found */
+ reg_parse_callback_val_del_t val_del;
+ /** Function called on comment found */
+ reg_parse_callback_comment_t comment;
+ void* data; /**< Private data passed to callback function */
+} reg_parse_callback;
+
+/**
+ * A Parser for a registration entries (.reg) file.
+ *
+ * It may be used as a reg_format_callback, so the following is valid:
+ * @code
+ * reg_format* f = reg_format_new(mem_ctx,
+ * (reg_format_callback)reg_parse_new(mem_ctx, cb, NULL, 0),
+ * NULL, 0, "\\");
+ * @endcode
+ * @see reg_format
+ */
+typedef struct reg_parse reg_parse;
+
+/**
+ * Create a new reg_parse object.
+ *
+ * @param talloc_ctx the talloc parent
+ * @param cb the output handler
+ * @param str_enc the charset of hex encoded strings (REG_MULTI_SZ, REG_EXAND_SZ) if not UTF-16
+ * @param flags
+ *
+ * @return a talloc'ed reg_parse object, NULL on error
+ */
+reg_parse* reg_parse_new(const void* talloc_ctx,
+ reg_parse_callback cb,
+ const char* str_enc,
+ unsigned flags);
+
+/**
+ * Feed one line to the parser.
+ *
+ * @param parser
+ * @param line one line from a (.reg) file, in UNIX charset
+ *
+ * @return 0 on success
+ *
+ * @see reg_format_callback_writeline_t
+ */
+int reg_parse_line(struct reg_parse* parser, const char* line);
+
+
+/**
+ * Parse a (.reg) file, read from a file descriptor.
+ *
+ * @param fd the file descriptor
+ * @param cb the output handler
+ * @param opts
+ *
+ * @return 0 on success
+ */
+int reg_parse_fd(int fd,
+ const reg_parse_callback* cb,
+ const char* opts);
+
+/**
+ * Parse a (.reg) file
+ *
+ * @param filename the file to open
+ * @param cb the output handler
+ * @param opts
+ *
+ * @return 0 on success
+ */
+int reg_parse_file(const char* filename,
+ const reg_parse_callback* cb,
+ const char* opts);
+
+int reg_parse_set_options(reg_parse* parser, const char* opt);
+
+/******************************************************************************/
+
+
+#endif /* REG_PARSE_H */
diff --git a/source3/registry/reg_parse_dox.cfg b/source3/registry/reg_parse_dox.cfg
new file mode 100644
index 0000000..1705350
--- /dev/null
+++ b/source3/registry/reg_parse_dox.cfg
@@ -0,0 +1,1562 @@
+# Doxyfile 1.6.3
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = Registry Import / Export
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = reg_parse.c reg_parse.h \
+ reg_import.c reg_import.h \
+ reg_format.c reg_format.h \
+ reg_parse_internal.c reg_parse_internal.h \
+ net_registry.c net_rpc_registry.c \
+ cbuf.c cbuf.h \
+ srprs.c srprs.h
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be implemented using a PHP enabled web server instead of at the web client using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server based approach is that it scales better to large projects and allows full text search. The disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+# Local Variables:
+# mode: makefile
+# compile-command: "doxygen reg_parse_dox.cfg"
+# End:
diff --git a/source3/registry/reg_parse_internal.c b/source3/registry/reg_parse_internal.c
new file mode 100644
index 0000000..d68fee6
--- /dev/null
+++ b/source3/registry/reg_parse_internal.c
@@ -0,0 +1,392 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Registry helper routines
+ *
+ * Copyright (C) Gregor Beck 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/**
+ * @file reg_parse_internal.h
+ * @author Gregor Beck <gb@sernet.de>
+ * @date Sep 2010
+ * @brief
+ */
+
+#include "reg_parse_internal.h"
+#include "cbuf.h"
+#include "srprs.h"
+#include "registry.h"
+
+size_t iconvert_talloc(const void* ctx,
+ smb_iconv_t cd,
+ const char* src, size_t srclen,
+ char** pdst)
+{
+ size_t dstlen, ret;
+ size_t obytes, ibytes;
+ char *optr, *dst, *tmp;
+ const char* iptr;
+
+ if (cd == NULL || cd == ((smb_iconv_t)-1)) {
+ return -1;
+ }
+
+ dst = *pdst;
+
+ if (dst == NULL) {
+ /*
+ * Allocate an extra two bytes for the
+ * terminating zero.
+ */
+ dstlen = srclen + 2;
+ dst = (char *)talloc_size(ctx, dstlen);
+ if (dst == NULL) {
+ DEBUG(0,("iconver_talloc no mem\n"));
+ return -1;
+ }
+ } else {
+ dstlen = talloc_get_size(dst);
+ }
+convert:
+ iptr = src;
+ ibytes = srclen;
+ optr = dst;
+ obytes = dstlen-2;
+
+ ret = smb_iconv(cd, &iptr, &ibytes, &optr, &obytes);
+
+ if(ret == -1) {
+ const char *reason="unknown error";
+ switch(errno) {
+ case EINVAL:
+ reason="Incomplete multibyte sequence";
+ break;
+ case E2BIG:
+ dstlen = 2*dstlen + 2;
+ tmp = talloc_realloc(ctx, dst, char, dstlen);
+ if (tmp == NULL) {
+ reason="talloc_realloc failed";
+ break;
+ }
+ dst = tmp;
+ goto convert;
+ case EILSEQ:
+ reason="Illegal multibyte sequence";
+ break;
+ }
+ DEBUG(0,("Conversion error: %s(%.80s) %li\n", reason, iptr,
+ (long int)(iptr-src)));
+ talloc_free(dst);
+ return -1;
+ }
+
+ dstlen = (dstlen-2) - obytes;
+
+ SSVAL(dst, dstlen, 0);
+
+ *pdst = dst;
+ return dstlen;
+}
+
+#ifndef HKEY_CURRENT_CONFIG
+#define HKEY_CURRENT_CONFIG 0x80000005
+#endif
+#ifndef HKEY_DYN_DATA
+#define HKEY_DYN_DATA 0x80000006
+#endif
+#ifndef HKEY_PERFORMANCE_TEXT
+#define HKEY_PERFORMANCE_TEXT 0x80000050
+#endif
+#ifndef HKEY_PERFORMANCE_NLSTEXT
+#define HKEY_PERFORMANCE_NLSTEXT 0x80000060
+#endif
+
+#define HIVE_INFO_ENTRY(SHORT,LONG) \
+const struct hive_info HIVE_INFO_##SHORT = { \
+ .handle = LONG, \
+ .short_name = #SHORT, \
+ .short_name_len = sizeof(#SHORT)-1, \
+ .long_name = #LONG, \
+ .long_name_len = sizeof(#LONG)-1, \
+}
+
+HIVE_INFO_ENTRY(HKLM, HKEY_LOCAL_MACHINE);
+HIVE_INFO_ENTRY(HKCU, HKEY_CURRENT_USER);
+HIVE_INFO_ENTRY(HKCR, HKEY_CLASSES_ROOT);
+HIVE_INFO_ENTRY(HKU , HKEY_USERS);
+HIVE_INFO_ENTRY(HKCC, HKEY_CURRENT_CONFIG);
+HIVE_INFO_ENTRY(HKDD, HKEY_DYN_DATA);
+HIVE_INFO_ENTRY(HKPD, HKEY_PERFORMANCE_DATA);
+HIVE_INFO_ENTRY(HKPT, HKEY_PERFORMANCE_TEXT);
+HIVE_INFO_ENTRY(HKPN, HKEY_PERFORMANCE_NLSTEXT);
+#undef HIVE_INFO_ENTRY
+
+const struct hive_info* HIVE_INFO[] = {
+ &HIVE_INFO_HKLM, &HIVE_INFO_HKCU, &HIVE_INFO_HKCR, &HIVE_INFO_HKU,
+ &HIVE_INFO_HKCC, &HIVE_INFO_HKDD, &HIVE_INFO_HKPD, &HIVE_INFO_HKPT,
+ &HIVE_INFO_HKPN, NULL
+};
+
+#define TOINT(A,B) ((int)(A) << 8) + (int)(B)
+
+bool srprs_hive(const char** ptr, const struct hive_info** result)
+{
+ const char* str = *ptr;
+ const struct hive_info* info = NULL;
+ bool long_hive = false;
+
+ if ((toupper(str[0]) != 'H') || (toupper(str[1]) != 'K')
+ || (str[2] == '\0') )
+ {
+ return false;
+ }
+
+ switch ( TOINT(toupper(str[2]), toupper(str[3])) ) {
+ case TOINT('E', 'Y'):
+ if (str[4] == '_') {
+ int i;
+ for (i=0; (info = HIVE_INFO[i]); i++) {
+ if (strncmp(&str[5], &info->long_name[5],
+ info->long_name_len-5) == 0)
+ {
+ long_hive = true;
+ break;
+ }
+ }
+ }
+ break;
+ case TOINT('L', 'M'):
+ info = &HIVE_INFO_HKLM;
+ break;
+ case TOINT('C', 'U'):
+ info = &HIVE_INFO_HKCU;
+ break;
+ case TOINT('C', 'R'):
+ info = &HIVE_INFO_HKCR;
+ break;
+ case TOINT('C', 'C'):
+ info = &HIVE_INFO_HKCC;
+ break;
+ case TOINT('D', 'D'):
+ info = &HIVE_INFO_HKDD;
+ break;
+ case TOINT('P', 'D'):
+ info = &HIVE_INFO_HKPD;
+ break;
+ case TOINT('P', 'T'):
+ info = &HIVE_INFO_HKPT;
+ break;
+ case TOINT('P', 'N'):
+ info = &HIVE_INFO_HKPN;
+ break;
+ default:
+ if (toupper(str[2]) == 'U') {
+ info = &HIVE_INFO_HKU;
+ }
+ break;
+ }
+ if (info != NULL) {
+ if (result != NULL) {
+ *result = info;
+ }
+ *ptr += long_hive ? info->long_name_len : info->short_name_len;
+ return true;
+ }
+ return false;
+}
+
+const struct hive_info* hive_info(const char* name)
+{
+ const struct hive_info* info = NULL;
+ srprs_hive(&name, &info);
+ return info;
+}
+
+const char *smbreg_get_charset(const char *c)
+{
+ if (strcmp(c, "dos") == 0) {
+ return lp_dos_charset();
+ } else if (strcmp(c, "unix") == 0) {
+ return lp_unix_charset();
+ } else {
+ return c;
+ }
+}
+
+bool set_iconv(smb_iconv_t* t, const char* to, const char* from)
+{
+ smb_iconv_t cd = (smb_iconv_t)-1;
+
+ if (to && from) {
+ to = smbreg_get_charset(to);
+ from = smbreg_get_charset(from);
+ cd = smb_iconv_open(to, from);
+ if (cd == ((smb_iconv_t)-1)) {
+ return false;
+ }
+ }
+ if ((*t != (smb_iconv_t)NULL) && (*t != (smb_iconv_t)-1)) {
+ smb_iconv_close(*t);
+ }
+ *t = cd;
+ return true;
+}
+
+/**
+ * Parse option string
+ * @param[in,out] ptr parse position
+ * @param[in] mem_ctx talloc context
+ * @param[out] name ptr 2 value
+ * @param[out] value ptr 2 value
+ * @return true on success
+ */
+bool srprs_option(const char** ptr, const void* mem_ctx, char** name, char** value)
+{
+ const char* pos = *ptr;
+ void* ctx = talloc_new(mem_ctx);
+
+ cbuf* key = cbuf_new(ctx);
+ cbuf* val = NULL;
+
+ while(srprs_charsetinv(&pos, ",= \t\n\r", key))
+ ;
+ if (pos == *ptr) {
+ talloc_free(ctx);
+ return false;
+ }
+
+ if (name != NULL) {
+ *name = talloc_steal(mem_ctx, cbuf_gets(key, 0));
+ }
+
+ if (*pos == '=') {
+ val = cbuf_new(ctx);
+ pos++;
+ if (!srprs_quoted_string(ptr, val, NULL)) {
+ while(srprs_charsetinv(&pos, ", \t\n\r", val))
+ ;
+ }
+ if (value != NULL) {
+ *value = talloc_steal(mem_ctx, cbuf_gets(val, 0));
+ }
+ } else {
+ if (value != NULL) {
+ *value = NULL;
+ }
+ }
+
+ while(srprs_char(&pos, ','))
+ ;
+
+ *ptr = pos;
+ return true;
+}
+
+#define CH_INVALID ((charset_t)-1)
+static const struct {
+ const char* const name;
+ charset_t ctype;
+ int len;
+ uint8_t seq[4];
+} BOM[] = {
+ {"UTF-8", CH_UTF8, 3, {0xEF, 0xBB, 0xBF}},
+ {"UTF-32LE", CH_INVALID, 4, {0xFF, 0xFE, 0x00, 0x00}},
+ {"UTF-16LE", CH_UTF16LE, 2, {0xFF, 0xFE}},
+ {"UTF-16BE", CH_UTF16BE, 2, {0xFE, 0xFF}},
+ {"UTF-32BE", CH_INVALID, 4, {0x00, 0x00, 0xFE, 0xFF}},
+ { .name = NULL }
+};
+
+bool srprs_bom(const char** ptr, const char** name, charset_t* ctype)
+{
+ int i;
+ for (i=0; BOM[i].name; i++) {
+ if (memcmp(*ptr, BOM[i].seq, BOM[i].len) == 0) {
+ break;
+ }
+ }
+
+ if (BOM[i].name != NULL) {
+ DEBUG(0, ("Found Byte Order Mark for : %s\n", BOM[i].name));
+
+ if (name != NULL) {
+ *name = BOM[i].name;
+ }
+
+ if (ctype != NULL) {
+ *ctype = BOM[i].ctype;
+ }
+
+ *ptr += BOM[i].len;
+
+ return true;
+ }
+ return false;
+}
+
+int write_bom(FILE* file, const char* charset, charset_t ctype)
+{
+ int i;
+ if ( charset == NULL ) {
+ for (i=0; BOM[i].name; i++) {
+ if (BOM[i].ctype == ctype) {
+ return fwrite(BOM[i].seq, 1, BOM[i].len, file);
+ }
+ }
+ DEBUG(0, ("No Byte Order Mark for charset_t: %u\n", (unsigned)ctype));
+ } else {
+ for (i=0; BOM[i].name; i++) {
+ if (strcasecmp_m(BOM[i].name, charset) == 0) {
+ return fwrite(BOM[i].seq, 1, BOM[i].len, file);
+ }
+ }
+ DEBUG(0, ("No Byte Order Mark for charset_t: %s\n", charset));
+ }
+ return 0;
+}
+
+
+int cbuf_puts_case(cbuf* s, const char* str, size_t len, enum fmt_case fmt)
+{
+ size_t pos = cbuf_getpos(s);
+ int ret = cbuf_puts(s, str, len);
+ char* ptr = cbuf_gets(s,pos);
+
+ if (ret <= 0) {
+ return ret;
+ }
+
+ switch (fmt) {
+ case FMT_CASE_PRESERVE:
+ break;
+ case FMT_CASE_UPPER:
+ while(*ptr != '\0') {
+ *ptr = toupper(*ptr);
+ ptr++;
+ }
+ break;
+ case FMT_CASE_TITLE:
+ *ptr = toupper(*ptr);
+ ptr++;
+ FALL_THROUGH;
+ case FMT_CASE_LOWER:
+ while(*ptr != '\0') {
+ *ptr = tolower(*ptr);
+ ptr++;
+ }
+ }
+ return ret;
+}
diff --git a/source3/registry/reg_parse_internal.h b/source3/registry/reg_parse_internal.h
new file mode 100644
index 0000000..a896372
--- /dev/null
+++ b/source3/registry/reg_parse_internal.h
@@ -0,0 +1,123 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Registry helper routines
+ *
+ * Copyright (C) Gregor Beck 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @brief Some stuff used by reg_parse and reg_format.
+ * It might be useful elsewhere but need some review of the interfaces.
+ * @file reg_parse_internal.h
+ * @author Gregor Beck <gb@sernet.de>
+ * @date Sep 2010
+ */
+#ifndef __REG_PARSE_INTERNAL_H
+#define __REG_PARSE_INTERNAL_H
+
+#include "includes.h"
+#include "system/iconv.h"
+
+struct cbuf;
+
+#define USE_NATIVE_ICONV
+#if defined USE_NATIVE_ICONV && defined HAVE_NATIVE_ICONV
+# define smb_iconv_t iconv_t
+# define smb_iconv(CD, IPTR, ILEN, OPTR, OLEN) \
+ iconv(CD, discard_const_p(char*, (IPTR)), ILEN, OPTR, OLEN)
+# define smb_iconv_open iconv_open
+# define smb_iconv_close iconv_close
+#endif
+
+size_t iconvert_talloc(const void* ctx,
+ smb_iconv_t cd,
+ const char* src, size_t srclen,
+ char** pdst);
+
+struct hive_info {
+ uint32_t handle;
+ const char* short_name;
+ size_t short_name_len;
+ const char* long_name;
+ size_t long_name_len;
+};
+
+extern const struct hive_info HIVE_INFO_HKLM;
+extern const struct hive_info HIVE_INFO_HKCU;
+extern const struct hive_info HIVE_INFO_HKCR;
+extern const struct hive_info HIVE_INFO_HKU;
+extern const struct hive_info HIVE_INFO_HKCC;
+extern const struct hive_info HIVE_INFO_HKDD;
+extern const struct hive_info HIVE_INFO_HKPD;
+extern const struct hive_info HIVE_INFO_HKPT;
+extern const struct hive_info HIVE_INFO_HKPN;
+
+extern const struct hive_info* HIVE_INFO[];
+
+const struct hive_info* hive_info(const char* name);
+bool srprs_hive(const char** ptr, const struct hive_info** result);
+
+
+
+const char *smbreg_get_charset(const char *c);
+
+bool set_iconv(smb_iconv_t* t, const char* to, const char* from);
+
+/**
+ * Parse option string
+ * @param[in,out] ptr parse position
+ * @param[in] mem_ctx talloc context
+ * @param[out] name ptr 2 value
+ * @param[out] value ptr 2 value
+ * @return true on success
+ */
+bool srprs_option(const char** ptr, const void* mem_ctx, char** name, char** value);
+
+/**
+ * Write Byte Order Mark for \p charset to file.
+ * If \c charset==NULL write BOM for \p ctype.
+ *
+ * @param[in] file file to write to
+ * @param[in] charset
+ * @param[in] ctype
+ *
+ * @return number of bytes written, -1 on error
+ * @todo write to cbuf
+ */
+int write_bom(FILE* file, const char* charset, charset_t ctype);
+
+/**
+ * Parse Byte Order Mark.
+ *
+ * @param[in,out] ptr parse position
+ * @param[out] name name of characterset
+ * @param[out] ctype charset_t
+ *
+ * @return true if found
+ * @ingroup parse bom
+ */
+bool srprs_bom(const char** ptr, const char** name, charset_t* ctype);
+
+enum fmt_case {
+ FMT_CASE_PRESERVE=0,
+ FMT_CASE_UPPER,
+ FMT_CASE_LOWER,
+ FMT_CASE_TITLE
+};
+int cbuf_puts_case(struct cbuf* s, const char* str, size_t len, enum fmt_case fmt);
+
+#endif /* __REG_PARSE_INTERNAL_H */
diff --git a/source3/registry/reg_parse_prs.c b/source3/registry/reg_parse_prs.c
new file mode 100644
index 0000000..8daa7d9
--- /dev/null
+++ b/source3/registry/reg_parse_prs.c
@@ -0,0 +1,453 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba memory buffer functions
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Jeremy Allison 1999
+ Copyright (C) Andrew Bartlett 2003.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "reg_parse_prs.h"
+#include "rpc_dce.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_PARSE
+
+static const char *tab_depth(int level, int depth)
+{
+ if( CHECK_DEBUGLVL(level) ) {
+ dbgtext("%*s", depth*4, "");
+ }
+ return "";
+}
+
+/*******************************************************************
+ Debug output for parsing info
+
+ XXXX side-effect of this function is to increase the debug depth XXXX.
+
+********************************************************************/
+
+void prs_debug(prs_struct *ps, int depth, const char *desc, const char *fn_name)
+{
+ DEBUG(5+depth, ("%s%06x %s %s\n", tab_depth(5+depth,depth), ps->data_offset, fn_name, desc));
+}
+
+/**
+ * Initialise an expandable parse structure.
+ *
+ * @param size Initial buffer size. If >0, a new buffer will be
+ * created with talloc().
+ *
+ * @return False if allocation fails, otherwise True.
+ **/
+
+bool prs_init(prs_struct *ps, uint32_t size, TALLOC_CTX *ctx, bool io)
+{
+ ZERO_STRUCTP(ps);
+ ps->io = io;
+ ps->bigendian_data = RPC_LITTLE_ENDIAN;
+ ps->align = RPC_PARSE_ALIGN;
+ ps->is_dynamic = False;
+ ps->data_offset = 0;
+ ps->buffer_size = 0;
+ ps->data_p = NULL;
+ ps->mem_ctx = ctx;
+
+ if (size != 0) {
+ ps->buffer_size = size;
+ ps->data_p = (char *)talloc_zero_size(ps->mem_ctx, size);
+ if(ps->data_p == NULL) {
+ DEBUG(0,("prs_init: talloc fail for %u bytes.\n", (unsigned int)size));
+ return False;
+ }
+ ps->is_dynamic = True; /* We own this memory. */
+ } else if (MARSHALLING(ps)) {
+ /* If size is zero and we're marshalling we should allocate memory on demand. */
+ ps->is_dynamic = True;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+ Delete the memory in a parse structure - if we own it.
+
+ NOTE: Contrary to the somewhat confusing naming, this function is not
+ intended for freeing memory allocated by prs_alloc_mem().
+ That memory is also attached to the talloc context given by
+ ps->mem_ctx, but is only freed when that talloc context is
+ freed. prs_mem_free() is used to delete "dynamic" memory
+ allocated in marshalling/unmarshalling.
+ ********************************************************************/
+
+void prs_mem_free(prs_struct *ps)
+{
+ if(ps->is_dynamic) {
+ TALLOC_FREE(ps->data_p);
+ }
+ ps->is_dynamic = False;
+ ps->buffer_size = 0;
+ ps->data_offset = 0;
+}
+
+/*******************************************************************
+ Allocate memory when unmarshalling... Always zero clears.
+ ********************************************************************/
+
+#if defined(PARANOID_MALLOC_CHECKER)
+char *prs_alloc_mem_(prs_struct *ps, size_t size, unsigned int count)
+#else
+char *prs_alloc_mem(prs_struct *ps, size_t size, unsigned int count)
+#endif
+{
+ char *ret = NULL;
+
+ if (size && count) {
+ /* We can't call the type-safe version here. */
+ ret = (char *)_talloc_zero_array(ps->mem_ctx, size, count,
+ "parse_prs");
+ }
+ return ret;
+}
+
+/*******************************************************************
+ Return the current talloc context we're using.
+ ********************************************************************/
+
+TALLOC_CTX *prs_get_mem_context(prs_struct *ps)
+{
+ return ps->mem_ctx;
+}
+
+/*******************************************************************
+ Attempt, if needed, to grow a data buffer.
+ Also depends on the data stream mode (io).
+ ********************************************************************/
+
+bool prs_grow(prs_struct *ps, uint32_t extra_space)
+{
+ uint32_t new_size;
+
+ ps->grow_size = MAX(ps->grow_size, ps->data_offset + extra_space);
+
+ if(ps->data_offset + extra_space <= ps->buffer_size)
+ return True;
+
+ /*
+ * We cannot grow the buffer if we're not reading
+ * into the prs_struct, or if we don't own the memory.
+ */
+
+ if(UNMARSHALLING(ps) || !ps->is_dynamic) {
+ DEBUG(0,("prs_grow: Buffer overflow - unable to expand buffer by %u bytes.\n",
+ (unsigned int)extra_space));
+ return False;
+ }
+
+ /*
+ * Decide how much extra space we really need.
+ */
+
+ extra_space -= (ps->buffer_size - ps->data_offset);
+ if(ps->buffer_size == 0) {
+
+ /*
+ * Start with 128 bytes (arbitrary value), enough for small rpc
+ * requests
+ */
+ new_size = MAX(128, extra_space);
+
+ ps->data_p = (char *)talloc_zero_size(ps->mem_ctx, new_size);
+ if(ps->data_p == NULL) {
+ DEBUG(0,("prs_grow: talloc failure for size %u.\n", (unsigned int)new_size));
+ return False;
+ }
+ } else {
+ /*
+ * If the current buffer size is bigger than the space needed,
+ * just double it, else add extra_space. Always keep 64 bytes
+ * more, so that after we added a large blob we don't have to
+ * realloc immediately again.
+ */
+ new_size = MAX(ps->buffer_size*2,
+ ps->buffer_size + extra_space + 64);
+
+ ps->data_p = talloc_realloc(ps->mem_ctx,
+ ps->data_p,
+ char,
+ new_size);
+ if (ps->data_p == NULL) {
+ DEBUG(0,("prs_grow: Realloc failure for size %u.\n",
+ (unsigned int)new_size));
+ return False;
+ }
+
+ memset(&ps->data_p[ps->buffer_size], '\0', (size_t)(new_size - ps->buffer_size));
+ }
+ ps->buffer_size = new_size;
+
+ return True;
+}
+
+/*******************************************************************
+ Get the data pointer (external interface).
+********************************************************************/
+
+char *prs_data_p(prs_struct *ps)
+{
+ return ps->data_p;
+}
+
+/*******************************************************************
+ Get the current data size (external interface).
+ ********************************************************************/
+
+uint32_t prs_data_size(prs_struct *ps)
+{
+ return ps->buffer_size;
+}
+
+/*******************************************************************
+ Fetch the current offset (external interface).
+ ********************************************************************/
+
+uint32_t prs_offset(prs_struct *ps)
+{
+ return ps->data_offset;
+}
+
+/*******************************************************************
+ Set the current offset (external interface).
+ ********************************************************************/
+
+bool prs_set_offset(prs_struct *ps, uint32_t offset)
+{
+ if ((offset > ps->data_offset)
+ && !prs_grow(ps, offset - ps->data_offset)) {
+ return False;
+ }
+
+ ps->data_offset = offset;
+ return True;
+}
+
+/*******************************************************************
+ Append the data from a buffer into a parse_struct.
+ ********************************************************************/
+
+bool prs_copy_data_in(prs_struct *dst, const char *src, uint32_t len)
+{
+ if (len == 0)
+ return True;
+
+ if(!prs_grow(dst, len))
+ return False;
+
+ memcpy(&dst->data_p[dst->data_offset], src, (size_t)len);
+ dst->data_offset += len;
+
+ return True;
+}
+
+/*******************************************************************
+ Align the data_len to a multiple of align bytes - filling with
+ zeros.
+ ********************************************************************/
+
+bool prs_align(prs_struct *ps)
+{
+ uint32_t mod = ps->data_offset & (ps->align-1);
+
+ if (ps->align != 0 && mod != 0) {
+ uint32_t extra_space = (ps->align - mod);
+ if(!prs_grow(ps, extra_space))
+ return False;
+ memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
+ ps->data_offset += extra_space;
+ }
+
+ return True;
+}
+
+/******************************************************************
+ Align on a 8 byte boundary
+ *****************************************************************/
+
+bool prs_align_uint64(prs_struct *ps)
+{
+ bool ret;
+ uint8_t old_align = ps->align;
+
+ ps->align = 8;
+ ret = prs_align(ps);
+ ps->align = old_align;
+
+ return ret;
+}
+
+/*******************************************************************
+ Ensure we can read/write to a given offset.
+ ********************************************************************/
+
+char *prs_mem_get(prs_struct *ps, uint32_t extra_size)
+{
+ if(UNMARSHALLING(ps)) {
+ /*
+ * If reading, ensure that we can read the requested size item.
+ */
+ if (ps->data_offset + extra_size > ps->buffer_size) {
+ DEBUG(0,("prs_mem_get: reading data of size %u would overrun "
+ "buffer by %u bytes.\n",
+ (unsigned int)extra_size,
+ (unsigned int)(ps->data_offset + extra_size - ps->buffer_size) ));
+ return NULL;
+ }
+ } else {
+ /*
+ * Writing - grow the buffer if needed.
+ */
+ if(!prs_grow(ps, extra_size))
+ return NULL;
+ }
+ return &ps->data_p[ps->data_offset];
+}
+
+/*******************************************************************
+ Change the struct type.
+ ********************************************************************/
+
+void prs_switch_type(prs_struct *ps, bool io)
+{
+ if ((ps->io ^ io) == True)
+ ps->io=io;
+}
+
+/*******************************************************************
+ Stream a uint16.
+ ********************************************************************/
+
+bool prs_uint16(const char *name, prs_struct *ps, int depth, uint16_t *data16)
+{
+ char *q = prs_mem_get(ps, sizeof(uint16_t));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data)
+ *data16 = RSVAL(q,0);
+ else
+ *data16 = SVAL(q,0);
+ } else {
+ if (ps->bigendian_data)
+ RSSVAL(q,0,*data16);
+ else
+ SSVAL(q,0,*data16);
+ }
+
+ DEBUGADD(5,("%s%04x %s: %04x\n", tab_depth(5,depth), ps->data_offset, name, *data16));
+
+ ps->data_offset += sizeof(uint16_t);
+
+ return True;
+}
+
+/*******************************************************************
+ Stream a uint32.
+ ********************************************************************/
+
+bool prs_uint32(const char *name, prs_struct *ps, int depth, uint32_t *data32)
+{
+ char *q = prs_mem_get(ps, sizeof(uint32_t));
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ if (ps->bigendian_data)
+ *data32 = RIVAL(q,0);
+ else
+ *data32 = IVAL(q,0);
+ } else {
+ if (ps->bigendian_data)
+ RSIVAL(q,0,*data32);
+ else
+ SIVAL(q,0,*data32);
+ }
+
+ DEBUGADD(5,("%s%04x %s: %08x\n", tab_depth(5,depth), ps->data_offset, name, *data32));
+
+ ps->data_offset += sizeof(uint32_t);
+
+ return True;
+}
+
+/*******************************************************************
+ Stream a uint64_struct
+ ********************************************************************/
+bool prs_uint64(const char *name, prs_struct *ps, int depth, uint64_t *data64)
+{
+ if (UNMARSHALLING(ps)) {
+ uint32_t high, low;
+
+ if (!prs_uint32(name, ps, depth+1, &low))
+ return False;
+
+ if (!prs_uint32(name, ps, depth+1, &high))
+ return False;
+
+ *data64 = ((uint64_t)high << 32) + low;
+
+ return True;
+ } else {
+ uint32_t high = (*data64) >> 32, low = (*data64) & 0xFFFFFFFF;
+ return prs_uint32(name, ps, depth+1, &low) &&
+ prs_uint32(name, ps, depth+1, &high);
+ }
+}
+
+/******************************************************************
+ Stream an array of uint8s. Length is number of uint8s.
+ ********************************************************************/
+
+bool prs_uint8s(bool charmode, const char *name, prs_struct *ps, int depth, uint8_t *data8s, int len)
+{
+ int i;
+ char *q = prs_mem_get(ps, len);
+ if (q == NULL)
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ for (i = 0; i < len; i++)
+ data8s[i] = CVAL(q,i);
+ } else {
+ for (i = 0; i < len; i++)
+ SCVAL(q, i, data8s[i]);
+ }
+
+ DEBUGADD(5,("%s%04x %s: ", tab_depth(5,depth), ps->data_offset ,name));
+ if (charmode)
+ print_asc(5, (unsigned char*)data8s, len);
+ else {
+ for (i = 0; i < len; i++)
+ DEBUGADD(5,("%02x ", data8s[i]));
+ }
+ DEBUGADD(5,("\n"));
+
+ ps->data_offset += len;
+
+ return True;
+}
diff --git a/source3/registry/reg_parse_prs.h b/source3/registry/reg_parse_prs.h
new file mode 100644
index 0000000..ee65f51
--- /dev/null
+++ b/source3/registry/reg_parse_prs.h
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB parameters and setup
+ Copyright (C) Andrew Tridgell 1992-1997
+ Copyright (C) Luke Kenneth Casson Leighton 1996-1997
+ Copyright (C) Paul Ashton 1997
+ Copyright (C) Jeremy Allison 2000-2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _REG_PARSE_PRS_H_
+#define _REG_PARSE_PRS_H_
+
+
+#define prs_init_empty( _ps_, _ctx_, _io_ ) (void) prs_init((_ps_), 0, (_ctx_), (_io_))
+
+typedef struct _prs_struct {
+ bool io; /* parsing in or out of data stream */
+ /*
+ * If the (incoming) data is big-endian. On output we are
+ * always little-endian.
+ */
+ bool bigendian_data;
+ uint8_t align; /* data alignment */
+ bool is_dynamic; /* Do we own this memory or not ? */
+ uint32_t data_offset; /* Current working offset into data. */
+ uint32_t buffer_size; /* Current allocated size of the buffer. */
+ uint32_t grow_size; /* size requested via prs_grow() calls */
+ /* The buffer itself. If "is_dynamic" is true this
+ * MUST BE TALLOC'ed off mem_ctx. */
+ char *data_p;
+ TALLOC_CTX *mem_ctx; /* When unmarshalling, use this.... */
+} prs_struct;
+
+/*
+ * Defines for io member of prs_struct.
+ */
+
+#define MARSHALL 0
+#define UNMARSHALL 1
+
+#define MARSHALLING(ps) (!(ps)->io)
+#define UNMARSHALLING(ps) ((ps)->io)
+
+#define RPC_PARSE_ALIGN 4
+
+void prs_debug(prs_struct *ps, int depth, const char *desc, const char *fn_name);
+bool prs_init(prs_struct *ps, uint32_t size, TALLOC_CTX *ctx, bool io);
+void prs_mem_free(prs_struct *ps);
+char *prs_alloc_mem_(prs_struct *ps, size_t size, unsigned int count);
+char *prs_alloc_mem(prs_struct *ps, size_t size, unsigned int count);
+TALLOC_CTX *prs_get_mem_context(prs_struct *ps);
+bool prs_grow(prs_struct *ps, uint32_t extra_space);
+char *prs_data_p(prs_struct *ps);
+uint32_t prs_data_size(prs_struct *ps);
+uint32_t prs_offset(prs_struct *ps);
+bool prs_set_offset(prs_struct *ps, uint32_t offset);
+bool prs_copy_data_in(prs_struct *dst, const char *src, uint32_t len);
+bool prs_align(prs_struct *ps);
+bool prs_align_uint64(prs_struct *ps);
+char *prs_mem_get(prs_struct *ps, uint32_t extra_size);
+void prs_switch_type(prs_struct *ps, bool io);
+bool prs_uint16(const char *name, prs_struct *ps, int depth, uint16_t *data16);
+bool prs_uint32(const char *name, prs_struct *ps, int depth, uint32_t *data32);
+bool prs_uint64(const char *name, prs_struct *ps, int depth, uint64_t *data64);
+bool prs_uint8s(bool charmode, const char *name, prs_struct *ps, int depth, uint8_t *data8s, int len);
+
+#endif
diff --git a/source3/registry/reg_perfcount.c b/source3/registry/reg_perfcount.c
new file mode 100644
index 0000000..131cc57
--- /dev/null
+++ b/source3/registry/reg_perfcount.c
@@ -0,0 +1,1463 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ *
+ * Copyright (C) Marcin Krzysztof Porwit 2005,
+ * Copyright (C) Gerald (Jerry) Carter 2005.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "../librpc/gen_ndr/perfcount.h"
+#include "registry.h"
+#include "reg_perfcount.h"
+#include "../libcli/registry/util_reg.h"
+#include "util_tdb.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+#define PERFCOUNT_MAX_LEN 256
+
+#define PERFCOUNTDIR "perfmon"
+#define NAMES_DB "names.tdb"
+#define DATA_DB "data.tdb"
+
+struct PERF_OBJECT_TYPE *_reg_perfcount_find_obj(struct PERF_DATA_BLOCK *block, int objind);
+
+/*********************************************************************
+*********************************************************************/
+
+/* returns perfcount path for dbname allocated on talloc_tos */
+static char *counters_directory(const char *dbname)
+{
+ char *dir_path = NULL;
+ char *db_subpath = NULL;
+ char *ret = NULL;
+
+ dir_path = state_path(talloc_tos(), PERFCOUNTDIR);
+ if (dir_path == NULL) {
+ return NULL;
+ }
+
+ if (!directory_create_or_exist(dir_path, 0755)) {
+ TALLOC_FREE(dir_path);
+ return NULL;
+ }
+
+ db_subpath = talloc_asprintf(dir_path, "%s/%s", PERFCOUNTDIR, dbname);
+ if (db_subpath == NULL) {
+ TALLOC_FREE(dir_path);
+ return NULL;
+ }
+
+ ret = state_path(talloc_tos(), db_subpath);
+ TALLOC_FREE(dir_path);
+ return ret;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+uint32_t reg_perfcount_get_base_index(void)
+{
+ char *fname;
+ TDB_CONTEXT *names;
+ TDB_DATA kbuf, dbuf;
+ char key[] = "1";
+ uint32_t retval = 0;
+ char buf[PERFCOUNT_MAX_LEN];
+
+ fname = counters_directory(NAMES_DB);
+ if (fname == NULL) {
+ return 0;
+ }
+
+ names = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444);
+
+ if ( !names ) {
+ DEBUG(2, ("reg_perfcount_get_base_index: unable to open [%s].\n", fname));
+ TALLOC_FREE(fname);
+ return 0;
+ }
+ /* needs to read the value of key "1" from the counter_names.tdb file, as that is
+ where the total number of counters is stored. We're assuming no holes in the
+ enumeration.
+ The format for the counter_names.tdb file is:
+ key value
+ 1 num_counters
+ 2 perf_counter1
+ 3 perf_counter1_help
+ 4 perf_counter2
+ 5 perf_counter2_help
+ even_num perf_counter<even_num>
+ even_num+1 perf_counter<even_num>_help
+ and so on.
+ So last_counter becomes num_counters*2, and last_help will be last_counter+1 */
+ kbuf = string_tdb_data(key);
+ dbuf = tdb_fetch(names, kbuf);
+ if(dbuf.dptr == NULL)
+ {
+ DEBUG(1, ("reg_perfcount_get_base_index: failed to find key \'1\' in [%s].\n", fname));
+ tdb_close(names);
+ TALLOC_FREE(fname);
+ return 0;
+ }
+
+ tdb_close(names);
+ TALLOC_FREE(fname);
+ memset(buf, 0, PERFCOUNT_MAX_LEN);
+ memcpy(buf, dbuf.dptr, dbuf.dsize);
+ retval = (uint32_t)atoi(buf);
+ SAFE_FREE(dbuf.dptr);
+ return retval;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+uint32_t reg_perfcount_get_last_counter(uint32_t base_index)
+{
+ uint32_t retval;
+
+ if(base_index == 0)
+ retval = 0;
+ else
+ retval = base_index * 2;
+
+ return retval;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+uint32_t reg_perfcount_get_last_help(uint32_t last_counter)
+{
+ uint32_t retval;
+
+ if(last_counter == 0)
+ retval = 0;
+ else
+ retval = last_counter + 1;
+
+ return retval;
+}
+
+
+/*********************************************************************
+*********************************************************************/
+
+static uint32_t _reg_perfcount_multi_sz_from_tdb(TDB_CONTEXT *tdb,
+ int keyval,
+ char **retbuf,
+ uint32_t buffer_size)
+{
+ TDB_DATA kbuf, dbuf;
+ char temp[PERFCOUNT_MAX_LEN] = {0};
+ char *buf1 = *retbuf;
+ char *p = NULL;
+ uint32_t working_size = 0;
+ DATA_BLOB name_index, name;
+ bool ok;
+
+ snprintf(temp, sizeof(temp), "%d", keyval);
+ kbuf = string_tdb_data(temp);
+ dbuf = tdb_fetch(tdb, kbuf);
+ if(dbuf.dptr == NULL)
+ {
+ /* If a key isn't there, just bypass it -- this really shouldn't
+ happen unless someone's mucking around with the tdb */
+ DEBUG(3, ("_reg_perfcount_multi_sz_from_tdb: failed to find key [%s] in [%s].\n",
+ temp, tdb_name(tdb)));
+ return buffer_size;
+ }
+ /* First encode the name_index */
+ working_size = (kbuf.dsize + 1)*sizeof(uint16_t);
+ /* SMB_REALLOC frees buf1 on error */
+ p = (char *)SMB_REALLOC(buf1, buffer_size + working_size);
+ if (p == NULL) {
+ buffer_size = 0;
+ return buffer_size;
+ }
+ buf1 = p;
+ ok = push_reg_sz(talloc_tos(), &name_index, (const char *)kbuf.dptr);
+ if (!ok) {
+ SAFE_FREE(buf1);
+ buffer_size = 0;
+ return buffer_size;
+ }
+ memcpy(buf1+buffer_size, (char *)name_index.data, working_size);
+ buffer_size += working_size;
+ /* Now encode the actual name */
+ working_size = (dbuf.dsize + 1)*sizeof(uint16_t);
+ /* SMB_REALLOC frees buf1 on error */
+ p = (char *)SMB_REALLOC(buf1, buffer_size + working_size);
+ if (p == NULL) {
+ buffer_size = 0;
+ return buffer_size;
+ }
+ buf1 = p;
+ memset(temp, 0, sizeof(temp));
+ memcpy(temp, dbuf.dptr, dbuf.dsize);
+ SAFE_FREE(dbuf.dptr);
+ ok = push_reg_sz(talloc_tos(), &name, temp);
+ if (!ok) {
+ SAFE_FREE(buf1);
+ buffer_size = 0;
+ return buffer_size;
+ }
+ memcpy(buf1+buffer_size, (char *)name.data, working_size);
+ buffer_size += working_size;
+
+ *retbuf = buf1;
+
+ return buffer_size;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+uint32_t reg_perfcount_get_counter_help(uint32_t base_index, char **retbuf)
+{
+ char *buf1 = NULL;
+ uint32_t buffer_size = 0;
+ TDB_CONTEXT *names;
+ char *fname;
+ int i;
+
+ if (base_index == 0) {
+ return 0;
+ }
+
+ fname = counters_directory(NAMES_DB);
+ if (fname == NULL) {
+ return 0;
+ }
+
+ names = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444);
+
+ if (names == NULL) {
+ DEBUG(1, ("reg_perfcount_get_counter_help: unable to open [%s].\n", fname));
+ TALLOC_FREE(fname);
+ return 0;
+ }
+ TALLOC_FREE(fname);
+
+ for(i = 1; i <= base_index; i++)
+ {
+ buffer_size = _reg_perfcount_multi_sz_from_tdb(names, (i*2)+1, retbuf, buffer_size);
+ }
+ tdb_close(names);
+
+ /* Now terminate the MULTI_SZ with a double unicode NULL */
+ buf1 = *retbuf;
+ buf1 = (char *)SMB_REALLOC(buf1, buffer_size + 2);
+ if(!buf1) {
+ buffer_size = 0;
+ } else {
+ buf1[buffer_size++] = '\0';
+ buf1[buffer_size++] = '\0';
+ }
+
+ *retbuf = buf1;
+
+ return buffer_size;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+uint32_t reg_perfcount_get_counter_names(uint32_t base_index, char **retbuf)
+{
+ char *buf1 = NULL;
+ uint32_t buffer_size = 0;
+ TDB_CONTEXT *names;
+ char *fname;
+ int i;
+
+ if (base_index == 0) {
+ return 0;
+ }
+
+ fname = counters_directory(NAMES_DB);
+ if (fname == NULL) {
+ return 0;
+ }
+
+ names = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444);
+
+ if (names == NULL) {
+ DEBUG(1, ("reg_perfcount_get_counter_names: unable to open [%s].\n", fname));
+ TALLOC_FREE(fname);
+ return 0;
+ }
+ TALLOC_FREE(fname);
+
+ buffer_size = _reg_perfcount_multi_sz_from_tdb(names, 1, retbuf, buffer_size);
+
+ for(i = 1; i <= base_index; i++)
+ {
+ buffer_size = _reg_perfcount_multi_sz_from_tdb(names, i*2, retbuf, buffer_size);
+ }
+ tdb_close(names);
+
+ /* Now terminate the MULTI_SZ with a double unicode NULL */
+ buf1 = *retbuf;
+ buf1 = (char *)SMB_REALLOC(buf1, buffer_size + 2);
+ if(!buf1) {
+ buffer_size = 0;
+ } else {
+ buf1[buffer_size++] = '\0';
+ buf1[buffer_size++] = '\0';
+ }
+
+ *retbuf=buf1;
+
+ return buffer_size;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static void _reg_perfcount_make_key(TDB_DATA *key,
+ char *buf,
+ int buflen,
+ int key_part1,
+ const char *key_part2)
+{
+ memset(buf, 0, buflen);
+ if(key_part2 != NULL)
+ snprintf(buf, buflen,"%d%s", key_part1, key_part2);
+ else
+ snprintf(buf, buflen, "%d", key_part1);
+
+ *key = string_tdb_data(buf);
+
+ return;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_isparent(TDB_DATA data)
+{
+ if(data.dsize > 0)
+ {
+ if(data.dptr[0] == 'p')
+ return True;
+ else
+ return False;
+ }
+ return False;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_ischild(TDB_DATA data)
+{
+ if(data.dsize > 0)
+ {
+ if(data.dptr[0] == 'c')
+ return True;
+ else
+ return False;
+ }
+ return False;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static uint32_t _reg_perfcount_get_numinst(int objInd, TDB_CONTEXT *names)
+{
+ TDB_DATA key, data;
+ char buf[PERFCOUNT_MAX_LEN];
+
+ _reg_perfcount_make_key(&key, buf, PERFCOUNT_MAX_LEN, objInd, "inst");
+ data = tdb_fetch(names, key);
+
+ if(data.dptr == NULL)
+ return (uint32_t)PERF_NO_INSTANCES;
+
+ memset(buf, 0, PERFCOUNT_MAX_LEN);
+ memcpy(buf, data.dptr, data.dsize);
+ SAFE_FREE(data.dptr);
+ return (uint32_t)atoi(buf);
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_add_instance(struct PERF_OBJECT_TYPE *obj,
+ TALLOC_CTX *mem_ctx,
+ int instInd,
+ TDB_CONTEXT *names);
+
+static bool _reg_perfcount_add_object(struct PERF_DATA_BLOCK *block,
+ TALLOC_CTX *mem_ctx,
+ int num,
+ TDB_DATA data,
+ TDB_CONTEXT *names)
+{
+ int i;
+ bool success = True;
+ struct PERF_OBJECT_TYPE *obj;
+
+ block->objects = (struct PERF_OBJECT_TYPE *)talloc_realloc(mem_ctx,
+ block->objects,
+ struct PERF_OBJECT_TYPE,
+ block->NumObjectTypes+1);
+ if(block->objects == NULL)
+ return False;
+ obj = &(block->objects[block->NumObjectTypes]);
+ memset((void *)&(block->objects[block->NumObjectTypes]), 0, sizeof(struct PERF_OBJECT_TYPE));
+ block->objects[block->NumObjectTypes].ObjectNameTitleIndex = num;
+ block->objects[block->NumObjectTypes].ObjectNameTitlePointer = 0;
+ block->objects[block->NumObjectTypes].ObjectHelpTitleIndex = num+1;
+ block->objects[block->NumObjectTypes].ObjectHelpTitlePointer = 0;
+ block->objects[block->NumObjectTypes].NumCounters = 0;
+ block->objects[block->NumObjectTypes].DefaultCounter = 0;
+ block->objects[block->NumObjectTypes].NumInstances = _reg_perfcount_get_numinst(num, names);
+ block->objects[block->NumObjectTypes].counters = NULL;
+ block->objects[block->NumObjectTypes].instances = NULL;
+ block->objects[block->NumObjectTypes].counter_data.ByteLength = sizeof(uint32_t);
+ block->objects[block->NumObjectTypes].counter_data.data = NULL;
+ block->objects[block->NumObjectTypes].DetailLevel = PERF_DETAIL_NOVICE;
+ block->NumObjectTypes+=1;
+
+ for(i = 0; i < (int)obj->NumInstances; i++) {
+ success = _reg_perfcount_add_instance(obj, mem_ctx, i, names);
+ }
+
+ return success;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_get_counter_data(TDB_DATA key, TDB_DATA *data)
+{
+ TDB_CONTEXT *counters;
+ char *fname;
+
+ fname = counters_directory(DATA_DB);
+ if (fname == NULL) {
+ return false;
+ }
+
+ counters = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444);
+
+ if (counters == NULL) {
+ DEBUG(1, ("reg_perfcount_get_counter_data: unable to open [%s].\n", fname));
+ TALLOC_FREE(fname);
+ return False;
+ }
+ TALLOC_FREE(fname);
+
+ *data = tdb_fetch(counters, key);
+
+ tdb_close(counters);
+
+ return True;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static uint32_t _reg_perfcount_get_size_field(uint32_t CounterType)
+{
+ uint32_t retval;
+
+ retval = CounterType;
+
+ /* First mask out reserved lower 8 bits */
+ retval = retval & 0xFFFFFF00;
+ retval = retval << 22;
+ retval = retval >> 22;
+
+ return retval;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static uint32_t _reg_perfcount_compute_scale(int64_t data)
+{
+ int scale = 0;
+ if(data == 0)
+ return scale;
+ while(data > 100)
+ {
+ data /= 10;
+ scale--;
+ }
+ while(data < 10)
+ {
+ data *= 10;
+ scale++;
+ }
+
+ return (uint32_t)scale;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_get_counter_info(struct PERF_DATA_BLOCK *block,
+ TALLOC_CTX *mem_ctx,
+ int CounterIndex,
+ struct PERF_OBJECT_TYPE *obj,
+ TDB_CONTEXT *names)
+{
+ TDB_DATA key, data;
+ char buf[PERFCOUNT_MAX_LEN];
+ size_t dsize, padding;
+ long int data32, dbuf[2];
+ int64_t data64;
+ uint32_t counter_size;
+
+ obj->counters[obj->NumCounters].DefaultScale = 0;
+ dbuf[0] = dbuf[1] = 0;
+ padding = 0;
+
+ _reg_perfcount_make_key(&key, buf, PERFCOUNT_MAX_LEN, CounterIndex, "type");
+ data = tdb_fetch(names, key);
+ if(data.dptr == NULL)
+ {
+ DEBUG(3, ("_reg_perfcount_get_counter_info: No type data for counter [%d].\n", CounterIndex));
+ return False;
+ }
+ memset(buf, 0, PERFCOUNT_MAX_LEN);
+ memcpy(buf, data.dptr, data.dsize);
+ obj->counters[obj->NumCounters].CounterType = atoi(buf);
+ DEBUG(10, ("_reg_perfcount_get_counter_info: Got type [%d] for counter [%d].\n",
+ obj->counters[obj->NumCounters].CounterType, CounterIndex));
+ SAFE_FREE(data.dptr);
+
+ /* Fetch the actual data */
+ _reg_perfcount_make_key(&key, buf, PERFCOUNT_MAX_LEN, CounterIndex, "");
+ _reg_perfcount_get_counter_data(key, &data);
+ if(data.dptr == NULL)
+ {
+ DEBUG(3, ("_reg_perfcount_get_counter_info: No counter data for counter [%d].\n", CounterIndex));
+ return False;
+ }
+
+ counter_size = _reg_perfcount_get_size_field(obj->counters[obj->NumCounters].CounterType);
+
+ if(counter_size == PERF_SIZE_DWORD)
+ {
+ dsize = sizeof(data32);
+ memset(buf, 0, PERFCOUNT_MAX_LEN);
+ memcpy(buf, data.dptr, data.dsize);
+ data32 = strtol(buf, NULL, 0);
+ if((obj->counters[obj->NumCounters].CounterType & 0x00000F00) == PERF_TYPE_NUMBER)
+ obj->counters[obj->NumCounters].DefaultScale = _reg_perfcount_compute_scale((int64_t)data32);
+ else
+ obj->counters[obj->NumCounters].DefaultScale = 0;
+ dbuf[0] = data32;
+ padding = (dsize - (obj->counter_data.ByteLength%dsize)) % dsize;
+ }
+ else if(counter_size == PERF_SIZE_LARGE)
+ {
+ dsize = sizeof(data64);
+ memset(buf, 0, PERFCOUNT_MAX_LEN);
+ memcpy(buf, data.dptr, data.dsize);
+ data64 = atof(buf);
+ if((obj->counters[obj->NumCounters].CounterType & 0x00000F00) == PERF_TYPE_NUMBER)
+ obj->counters[obj->NumCounters].DefaultScale = _reg_perfcount_compute_scale(data64);
+ else
+ obj->counters[obj->NumCounters].DefaultScale = 0;
+ memcpy((void *)dbuf, (const void *)&data64, dsize);
+ padding = (dsize - (obj->counter_data.ByteLength%dsize)) % dsize;
+ }
+ else /* PERF_SIZE_VARIABLE_LEN */
+ {
+ dsize = data.dsize;
+ memset(buf, 0, PERFCOUNT_MAX_LEN);
+ memcpy(buf, data.dptr, data.dsize);
+ }
+ SAFE_FREE(data.dptr);
+
+ obj->counter_data.ByteLength += dsize + padding;
+ obj->counter_data.data = talloc_realloc(mem_ctx,
+ obj->counter_data.data,
+ uint8_t,
+ obj->counter_data.ByteLength - sizeof(uint32_t));
+ if(obj->counter_data.data == NULL)
+ return False;
+ if(dbuf[0] != 0 || dbuf[1] != 0)
+ {
+ memcpy((void *)(obj->counter_data.data +
+ (obj->counter_data.ByteLength - (sizeof(uint32_t) + dsize))),
+ (const void *)dbuf, dsize);
+ }
+ else
+ {
+ /* Handling PERF_SIZE_VARIABLE_LEN */
+ memcpy((void *)(obj->counter_data.data +
+ (obj->counter_data.ByteLength - (sizeof(uint32_t) + dsize))),
+ (const void *)buf, dsize);
+ }
+ obj->counters[obj->NumCounters].CounterOffset = obj->counter_data.ByteLength - dsize;
+ if(obj->counters[obj->NumCounters].CounterOffset % dsize != 0)
+ {
+ DEBUG(3,("Improperly aligned counter [%d]\n", obj->NumCounters));
+ }
+ obj->counters[obj->NumCounters].CounterSize = dsize;
+
+ return True;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+struct PERF_OBJECT_TYPE *_reg_perfcount_find_obj(struct PERF_DATA_BLOCK *block, int objind)
+{
+ int i;
+
+ struct PERF_OBJECT_TYPE *obj = NULL;
+
+ for(i = 0; i < block->NumObjectTypes; i++)
+ {
+ if(block->objects[i].ObjectNameTitleIndex == objind)
+ {
+ obj = &(block->objects[i]);
+ }
+ }
+
+ return obj;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_add_counter(struct PERF_DATA_BLOCK *block,
+ TALLOC_CTX *mem_ctx,
+ int num,
+ TDB_DATA data,
+ TDB_CONTEXT *names)
+{
+ char *begin, *end, *start, *stop;
+ int parent;
+ struct PERF_OBJECT_TYPE *obj;
+ bool success = True;
+ char buf[PERFCOUNT_MAX_LEN];
+
+ obj = NULL;
+ memset(buf, 0, PERFCOUNT_MAX_LEN);
+ memcpy(buf, data.dptr, data.dsize);
+ begin = strchr(buf, '[');
+ end = strchr(buf, ']');
+ if(begin == NULL || end == NULL)
+ return False;
+ start = begin+1;
+
+ while(start < end) {
+ stop = strchr(start, ',');
+ if(stop == NULL)
+ stop = end;
+ *stop = '\0';
+ parent = atoi(start);
+
+ obj = _reg_perfcount_find_obj(block, parent);
+ if(obj == NULL) {
+ /* At this point we require that the parent object exist.
+ This can probably be handled better at some later time */
+ DEBUG(3, ("_reg_perfcount_add_counter: Could not find parent object [%d] for counter [%d].\n",
+ parent, num));
+ return False;
+ }
+ obj->counters = (struct PERF_COUNTER_DEFINITION *)talloc_realloc(mem_ctx,
+ obj->counters,
+ struct PERF_COUNTER_DEFINITION,
+ obj->NumCounters+1);
+ if(obj->counters == NULL)
+ return False;
+ memset((void *)&(obj->counters[obj->NumCounters]), 0, sizeof(struct PERF_COUNTER_DEFINITION));
+ obj->counters[obj->NumCounters].CounterNameTitleIndex=num;
+ obj->counters[obj->NumCounters].CounterHelpTitleIndex=num+1;
+ obj->counters[obj->NumCounters].DetailLevel = PERF_DETAIL_NOVICE;
+ obj->counters[obj->NumCounters].ByteLength = sizeof(struct PERF_COUNTER_DEFINITION);
+ success = _reg_perfcount_get_counter_info(block, mem_ctx, num, obj, names);
+ obj->NumCounters += 1;
+ start = stop + 1;
+ }
+
+ /* Handle case of Objects/Counters without any counter data, which would suggest
+ that the required instances are not there yet, so change NumInstances from
+ PERF_NO_INSTANCES to 0 */
+
+ return success;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_get_instance_info(struct PERF_INSTANCE_DEFINITION *inst,
+ TALLOC_CTX *mem_ctx,
+ int instId,
+ struct PERF_OBJECT_TYPE *obj,
+ TDB_CONTEXT *names)
+{
+ TDB_DATA key, data;
+ char buf[PERFCOUNT_MAX_LEN] = {0};
+ char temp[32] = {0};
+ smb_ucs2_t *name = NULL;
+ int pad;
+
+ /* First grab the instance data from the data file */
+ snprintf(temp, sizeof(temp), "i%d", instId);
+ _reg_perfcount_make_key(&key, buf, PERFCOUNT_MAX_LEN, obj->ObjectNameTitleIndex, temp);
+ if (!_reg_perfcount_get_counter_data(key, &data)) {
+ DEBUG(3, ("_reg_perfcount_get_counter_data failed\n"));
+ return false;
+ }
+ if(data.dptr == NULL)
+ {
+ DEBUG(3, ("_reg_perfcount_get_instance_info: No instance data for instance [%s].\n",
+ buf));
+ return False;
+ }
+ inst->counter_data.ByteLength = data.dsize + sizeof(inst->counter_data.ByteLength);
+ inst->counter_data.data = talloc_realloc(mem_ctx,
+ inst->counter_data.data,
+ uint8_t,
+ data.dsize);
+ if(inst->counter_data.data == NULL)
+ return False;
+ memset(inst->counter_data.data, 0, data.dsize);
+ memcpy(inst->counter_data.data, data.dptr, data.dsize);
+ SAFE_FREE(data.dptr);
+
+ /* Fetch instance name */
+ snprintf(temp, sizeof(temp), "i%dname", instId);
+ _reg_perfcount_make_key(&key, buf, PERFCOUNT_MAX_LEN, obj->ObjectNameTitleIndex, temp);
+ data = tdb_fetch(names, key);
+ if(data.dptr == NULL)
+ {
+ /* Not actually an error, but possibly unintended? -- just logging FYI */
+ DEBUG(3, ("_reg_perfcount_get_instance_info: No instance name for instance [%s].\n",
+ buf));
+ inst->NameLength = 0;
+ }
+ else
+ {
+ memset(buf, 0, PERFCOUNT_MAX_LEN);
+ memcpy(buf, data.dptr, MIN(PERFCOUNT_MAX_LEN-1,data.dsize));
+ buf[PERFCOUNT_MAX_LEN-1] = '\0';
+ inst->NameLength = rpcstr_push_talloc(mem_ctx, &name, buf);
+ if (inst->NameLength == (uint32_t)-1 || !name) {
+ SAFE_FREE(data.dptr);
+ return False;
+ }
+ inst->data = talloc_realloc(mem_ctx,
+ inst->data,
+ uint8_t,
+ inst->NameLength);
+ if (inst->data == NULL) {
+ SAFE_FREE(data.dptr);
+ return False;
+ }
+ memcpy(inst->data, name, inst->NameLength);
+ SAFE_FREE(data.dptr);
+ }
+
+ inst->ParentObjectTitleIndex = 0;
+ inst->ParentObjectTitlePointer = 0;
+ inst->UniqueID = PERF_NO_UNIQUE_ID;
+ inst->NameOffset = 6 * sizeof(uint32_t);
+
+ inst->ByteLength = inst->NameOffset + inst->NameLength;
+ /* Need to be aligned on a 64-bit boundary here for counter_data */
+ if((pad = (inst->ByteLength % 8)))
+ {
+ pad = 8 - pad;
+ inst->data = talloc_realloc(mem_ctx,
+ inst->data,
+ uint8_t,
+ inst->NameLength + pad);
+ memset(inst->data + inst->NameLength, 0, pad);
+ inst->ByteLength += pad;
+ }
+
+ return True;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_add_instance(struct PERF_OBJECT_TYPE *obj,
+ TALLOC_CTX *mem_ctx,
+ int instInd,
+ TDB_CONTEXT *names)
+{
+ struct PERF_INSTANCE_DEFINITION *inst;
+
+ if(obj->instances == NULL) {
+ obj->instances = talloc_realloc(mem_ctx,
+ obj->instances,
+ struct PERF_INSTANCE_DEFINITION,
+ obj->NumInstances);
+ }
+ if(obj->instances == NULL)
+ return False;
+
+ memset(&(obj->instances[instInd]), 0, sizeof(struct PERF_INSTANCE_DEFINITION));
+ inst = &(obj->instances[instInd]);
+ return _reg_perfcount_get_instance_info(inst, mem_ctx, instInd, obj, names);
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static int _reg_perfcount_assemble_global(struct PERF_DATA_BLOCK *block,
+ TALLOC_CTX *mem_ctx,
+ int base_index,
+ TDB_CONTEXT *names)
+{
+ bool success;
+ int i, j, retval = 0;
+ char keybuf[PERFCOUNT_MAX_LEN];
+ TDB_DATA key, data;
+
+ for(i = 1; i <= base_index; i++)
+ {
+ j = i*2;
+ _reg_perfcount_make_key(&key, keybuf, PERFCOUNT_MAX_LEN, j, "rel");
+ data = tdb_fetch(names, key);
+ if(data.dptr != NULL)
+ {
+ if(_reg_perfcount_isparent(data))
+ success = _reg_perfcount_add_object(block, mem_ctx, j, data, names);
+ else if(_reg_perfcount_ischild(data))
+ success = _reg_perfcount_add_counter(block, mem_ctx, j, data, names);
+ else
+ {
+ DEBUG(3, ("Bogus relationship [%s] for counter [%d].\n", data.dptr, j));
+ success = False;
+ }
+ if(success == False)
+ {
+ DEBUG(3, ("_reg_perfcount_assemble_global: Failed to add new relationship for counter [%d].\n", j));
+ retval = -1;
+ }
+ SAFE_FREE(data.dptr);
+ }
+ else
+ DEBUG(3, ("NULL relationship for counter [%d] using key [%s].\n", j, keybuf));
+ }
+ return retval;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_get_64(uint64_t *retval,
+ TDB_CONTEXT *tdb,
+ int key_part1,
+ const char *key_part2)
+{
+ TDB_DATA key, data;
+ char buf[PERFCOUNT_MAX_LEN];
+
+ _reg_perfcount_make_key(&key, buf, PERFCOUNT_MAX_LEN, key_part1, key_part2);
+
+ data = tdb_fetch(tdb, key);
+ if(data.dptr == NULL)
+ {
+ DEBUG(3,("_reg_perfcount_get_64: No data found for key [%s].\n", key.dptr));
+ return False;
+ }
+
+ memset(buf, 0, PERFCOUNT_MAX_LEN);
+ memcpy(buf, data.dptr, data.dsize);
+ SAFE_FREE(data.dptr);
+
+ *retval = atof(buf);
+
+ return True;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_init_data_block_perf(struct PERF_DATA_BLOCK *block,
+ TDB_CONTEXT *names)
+{
+ uint64_t PerfFreq, PerfTime, PerfTime100nSec;
+ TDB_CONTEXT *counters;
+ bool status = False;
+ char *fname;
+
+ fname = counters_directory(DATA_DB);
+ if (fname == NULL) {
+ return false;
+ }
+
+ counters = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444);
+
+ if (counters == NULL) {
+ DEBUG(1, ("reg_perfcount_init_data_block_perf: unable to open [%s].\n", fname));
+ TALLOC_FREE(fname);
+ return False;
+ }
+ TALLOC_FREE(fname);
+
+ status = _reg_perfcount_get_64(&PerfFreq, names, 0, "PerfFreq");
+ if(status == False)
+ {
+ tdb_close(counters);
+ return status;
+ }
+ memcpy((void *)&(block->PerfFreq), (const void *)&PerfFreq, sizeof(PerfFreq));
+
+ status = _reg_perfcount_get_64(&PerfTime, counters, 0, "PerfTime");
+ if(status == False)
+ {
+ tdb_close(counters);
+ return status;
+ }
+ memcpy((void *)&(block->PerfTime), (const void *)&PerfTime, sizeof(PerfTime));
+
+ status = _reg_perfcount_get_64(&PerfTime100nSec, counters, 0, "PerfTime100nSec");
+ if(status == False)
+ {
+ tdb_close(counters);
+ return status;
+ }
+ memcpy((void *)&(block->PerfTime100nSec), (const void *)&PerfTime100nSec, sizeof(PerfTime100nSec));
+
+ tdb_close(counters);
+ return True;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static bool make_systemtime(struct SYSTEMTIME *systime, struct tm *unixtime)
+{
+ systime->year=unixtime->tm_year+1900;
+ systime->month=unixtime->tm_mon+1;
+ systime->dayofweek=unixtime->tm_wday;
+ systime->day=unixtime->tm_mday;
+ systime->hour=unixtime->tm_hour;
+ systime->minute=unixtime->tm_min;
+ systime->second=unixtime->tm_sec;
+ systime->milliseconds=0;
+
+ return True;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_init_data_block(struct PERF_DATA_BLOCK *block,
+ TALLOC_CTX *mem_ctx, TDB_CONTEXT *names,
+ bool bigendian_data)
+{
+ smb_ucs2_t *temp = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ time_t tm;
+ size_t sz;
+
+ sz = rpcstr_push_talloc(tmp_ctx, &temp, "PERF");
+ if ((sz == -1) || (temp == NULL)) {
+ goto err_out;
+ }
+ memcpy(block->Signature, temp, strlen_w(temp) *2);
+
+ if(bigendian_data)
+ block->LittleEndian = 0;
+ else
+ block->LittleEndian = 1;
+ block->Version = 1;
+ block->Revision = 1;
+ block->TotalByteLength = 0;
+ block->NumObjectTypes = 0;
+ block->DefaultObject = -1;
+ block->objects = NULL;
+ tm = time(NULL);
+ make_systemtime(&(block->SystemTime), gmtime(&tm));
+ _reg_perfcount_init_data_block_perf(block, names);
+
+ sz = rpcstr_push_talloc(tmp_ctx, &temp, lp_netbios_name());
+ if ((sz == -1) || (temp == NULL)) {
+ goto err_out;
+ }
+ block->SystemNameLength = (strlen_w(temp) * 2) + 2;
+ block->data = talloc_zero_array(mem_ctx, uint8_t, block->SystemNameLength + (8 - (block->SystemNameLength % 8)));
+ if (block->data == NULL) {
+ goto err_out;
+ }
+ memcpy(block->data, temp, block->SystemNameLength);
+ block->SystemNameOffset = sizeof(struct PERF_DATA_BLOCK) - sizeof(block->objects) - sizeof(block->data);
+ block->HeaderLength = block->SystemNameOffset + block->SystemNameLength;
+ /* Make sure to adjust for 64-bit alignment for when we finish writing the system name,
+ so that the PERF_OBJECT_TYPE struct comes out 64-bit aligned */
+ block->HeaderLength += 8 - (block->HeaderLength % 8);
+ talloc_free(tmp_ctx);
+
+ return true;
+
+err_out:
+ talloc_free(tmp_ctx);
+ return false;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static uint32_t _reg_perfcount_perf_data_block_fixup(struct PERF_DATA_BLOCK *block, TALLOC_CTX *mem_ctx)
+{
+ int obj, cnt, inst, pad, i;
+ struct PERF_OBJECT_TYPE *object;
+ struct PERF_INSTANCE_DEFINITION *instance;
+ struct PERF_COUNTER_DEFINITION *counter;
+ struct PERF_COUNTER_BLOCK *counter_data;
+ char *temp = NULL, *src_addr, *dst_addr;
+
+ block->TotalByteLength = 0;
+ object = block->objects;
+ for(obj = 0; obj < block->NumObjectTypes; obj++)
+ {
+ object[obj].TotalByteLength = 0;
+ object[obj].DefinitionLength = 0;
+ instance = object[obj].instances;
+ counter = object[obj].counters;
+ for(cnt = 0; cnt < object[obj].NumCounters; cnt++)
+ {
+ object[obj].TotalByteLength += counter[cnt].ByteLength;
+ object[obj].DefinitionLength += counter[cnt].ByteLength;
+ }
+ if(object[obj].NumInstances != PERF_NO_INSTANCES)
+ {
+ for(inst = 0; inst < object[obj].NumInstances; inst++)
+ {
+ instance = &(object[obj].instances[inst]);
+ object[obj].TotalByteLength += instance->ByteLength;
+ counter_data = &(instance->counter_data);
+ counter = &(object[obj].counters[object[obj].NumCounters - 1]);
+ counter_data->ByteLength = counter->CounterOffset + counter->CounterSize + sizeof(counter_data->ByteLength);
+ temp = talloc_realloc(mem_ctx,
+ temp,
+ char,
+ counter_data->ByteLength- sizeof(counter_data->ByteLength));
+ if (temp == NULL) {
+ return 0;
+ }
+ memset(temp, 0, counter_data->ByteLength - sizeof(counter_data->ByteLength));
+ src_addr = (char *)counter_data->data;
+ for(i = 0; i < object[obj].NumCounters; i++)
+ {
+ counter = &(object[obj].counters[i]);
+ dst_addr = temp + counter->CounterOffset - sizeof(counter_data->ByteLength);
+ memcpy(dst_addr, src_addr, counter->CounterSize);
+ src_addr += counter->CounterSize;
+ }
+ /* Make sure to be 64-bit aligned */
+ if((pad = (counter_data->ByteLength % 8)))
+ {
+ pad = 8 - pad;
+ }
+ counter_data->data = talloc_realloc(mem_ctx,
+ counter_data->data,
+ uint8_t,
+ counter_data->ByteLength - sizeof(counter_data->ByteLength) + pad);
+ if (counter_data->data == NULL) {
+ return 0;
+ }
+ memset(counter_data->data, 0, counter_data->ByteLength - sizeof(counter_data->ByteLength) + pad);
+ memcpy(counter_data->data, temp, counter_data->ByteLength - sizeof(counter_data->ByteLength));
+ counter_data->ByteLength += pad;
+ object[obj].TotalByteLength += counter_data->ByteLength;
+ }
+ }
+ else
+ {
+ /* Need to be 64-bit aligned at the end of the counter_data block, so pad counter_data to a 64-bit boundary,
+ so that the next PERF_OBJECT_TYPE can start on a 64-bit alignment */
+ if((pad = (object[obj].counter_data.ByteLength % 8)))
+ {
+ pad = 8 - pad;
+ object[obj].counter_data.data = talloc_realloc(mem_ctx,
+ object[obj].counter_data.data,
+ uint8_t,
+ object[obj].counter_data.ByteLength + pad);
+ memset((void *)(object[obj].counter_data.data + object[obj].counter_data.ByteLength), 0, pad);
+ object[obj].counter_data.ByteLength += pad;
+ }
+ object[obj].TotalByteLength += object[obj].counter_data.ByteLength;
+ }
+ object[obj].HeaderLength = sizeof(*object) - (sizeof(counter) + sizeof(instance) + sizeof(struct PERF_COUNTER_BLOCK));
+ object[obj].TotalByteLength += object[obj].HeaderLength;
+ object[obj].DefinitionLength += object[obj].HeaderLength;
+
+ block->TotalByteLength += object[obj].TotalByteLength;
+ }
+
+ return block->TotalByteLength;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static uint32_t reg_perfcount_get_perf_data_block(uint32_t base_index,
+ TALLOC_CTX *mem_ctx,
+ struct PERF_DATA_BLOCK *block,
+ const char *object_ids,
+ bool bigendian_data)
+{
+ uint32_t buffer_size = 0;
+ char *fname;
+ TDB_CONTEXT *names;
+ int retval = 0;
+
+ fname = counters_directory(NAMES_DB);
+ if (fname == NULL) {
+ return 0;
+ }
+
+ names = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444);
+
+ if(names == NULL)
+ {
+ DEBUG(1, ("reg_perfcount_get_perf_data_block: unable to open [%s].\n", fname));
+ TALLOC_FREE(fname);
+ return 0;
+ }
+ TALLOC_FREE(fname);
+
+ if (!_reg_perfcount_init_data_block(block, mem_ctx, names, bigendian_data)) {
+ DEBUG(0, ("_reg_perfcount_init_data_block failed\n"));
+ tdb_close(names);
+ return 0;
+ }
+
+ retval = _reg_perfcount_assemble_global(block, mem_ctx, base_index, names);
+
+ buffer_size = _reg_perfcount_perf_data_block_fixup(block, mem_ctx);
+
+ tdb_close(names);
+
+ if (retval == -1) {
+ return 0;
+ }
+
+ return buffer_size + block->HeaderLength;
+}
+
+/*******************************************************************
+********************************************************************/
+
+static bool smb_io_system_time(const char *desc, prs_struct *ps, int depth, struct SYSTEMTIME *systime)
+{
+ if(!prs_uint16("year", ps, depth, &systime->year))
+ return False;
+ if(!prs_uint16("month", ps, depth, &systime->month))
+ return False;
+ if(!prs_uint16("dayofweek", ps, depth, &systime->dayofweek))
+ return False;
+ if(!prs_uint16("day", ps, depth, &systime->day))
+ return False;
+ if(!prs_uint16("hour", ps, depth, &systime->hour))
+ return False;
+ if(!prs_uint16("minute", ps, depth, &systime->minute))
+ return False;
+ if(!prs_uint16("second", ps, depth, &systime->second))
+ return False;
+ if(!prs_uint16("milliseconds", ps, depth, &systime->milliseconds))
+ return False;
+
+ return True;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_marshall_perf_data_block(prs_struct *ps, struct PERF_DATA_BLOCK block, int depth)
+{
+ int i;
+ prs_debug(ps, depth, "", "_reg_perfcount_marshall_perf_data_block");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+ for(i = 0; i < 4; i++)
+ {
+ if(!prs_uint16("Signature", ps, depth, &block.Signature[i]))
+ return False;
+ }
+ if(!prs_uint32("Little Endian", ps, depth, &block.LittleEndian))
+ return False;
+ if(!prs_uint32("Version", ps, depth, &block.Version))
+ return False;
+ if(!prs_uint32("Revision", ps, depth, &block.Revision))
+ return False;
+ if(!prs_uint32("TotalByteLength", ps, depth, &block.TotalByteLength))
+ return False;
+ if(!prs_uint32("HeaderLength", ps, depth, &block.HeaderLength))
+ return False;
+ if(!prs_uint32("NumObjectTypes", ps, depth, &block.NumObjectTypes))
+ return False;
+ if(!prs_uint32("DefaultObject", ps, depth, &block.DefaultObject))
+ return False;
+ if(!smb_io_system_time("SystemTime", ps, depth, &block.SystemTime))
+ return False;
+ if(!prs_uint32("Padding", ps, depth, &block.Padding))
+ return False;
+ if(!prs_align_uint64(ps))
+ return False;
+ if(!prs_uint64("PerfTime", ps, depth, &block.PerfTime))
+ return False;
+ if(!prs_uint64("PerfFreq", ps, depth, &block.PerfFreq))
+ return False;
+ if(!prs_uint64("PerfTime100nSec", ps, depth, &block.PerfTime100nSec))
+ return False;
+ if(!prs_uint32("SystemNameLength", ps, depth, &block.SystemNameLength))
+ return False;
+ if(!prs_uint32("SystemNameOffset", ps, depth, &block.SystemNameOffset))
+ return False;
+ /* hack to make sure we're 64-bit aligned at the end of this whole mess */
+ if(!prs_uint8s(False, "SystemName", ps, depth, block.data,
+ block.HeaderLength - block.SystemNameOffset))
+ return False;
+
+ return True;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_marshall_perf_counters(prs_struct *ps,
+ struct PERF_OBJECT_TYPE object,
+ int depth)
+{
+ int cnt;
+ struct PERF_COUNTER_DEFINITION counter;
+
+ prs_debug(ps, depth, "", "_reg_perfcount_marshall_perf_counters");
+ depth++;
+
+ for(cnt = 0; cnt < object.NumCounters; cnt++)
+ {
+ counter = object.counters[cnt];
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("ByteLength", ps, depth, &counter.ByteLength))
+ return False;
+ if(!prs_uint32("CounterNameTitleIndex", ps, depth, &counter.CounterNameTitleIndex))
+ return False;
+ if(!prs_uint32("CounterNameTitlePointer", ps, depth, &counter.CounterNameTitlePointer))
+ return False;
+ if(!prs_uint32("CounterHelpTitleIndex", ps, depth, &counter.CounterHelpTitleIndex))
+ return False;
+ if(!prs_uint32("CounterHelpTitlePointer", ps, depth, &counter.CounterHelpTitlePointer))
+ return False;
+ if(!prs_uint32("DefaultScale", ps, depth, &counter.DefaultScale))
+ return False;
+ if(!prs_uint32("DetailLevel", ps, depth, &counter.DetailLevel))
+ return False;
+ if(!prs_uint32("CounterType", ps, depth, &counter.CounterType))
+ return False;
+ if(!prs_uint32("CounterSize", ps, depth, &counter.CounterSize))
+ return False;
+ if(!prs_uint32("CounterOffset", ps, depth, &counter.CounterOffset))
+ return False;
+ }
+
+ return True;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_marshall_perf_counter_data(prs_struct *ps,
+ struct PERF_COUNTER_BLOCK counter_data,
+ int depth)
+{
+ prs_debug(ps, depth, "", "_reg_perfcount_marshall_perf_counter_data");
+ depth++;
+
+ if(!prs_align_uint64(ps))
+ return False;
+
+ if(!prs_uint32("ByteLength", ps, depth, &counter_data.ByteLength))
+ return False;
+ if(!prs_uint8s(False, "CounterData", ps, depth, counter_data.data, counter_data.ByteLength - sizeof(uint32_t)))
+ return False;
+ if(!prs_align_uint64(ps))
+ return False;
+
+ return True;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_marshall_perf_instances(prs_struct *ps,
+ struct PERF_OBJECT_TYPE object,
+ int depth)
+{
+ struct PERF_INSTANCE_DEFINITION instance;
+ int inst;
+
+ prs_debug(ps, depth, "", "_reg_perfcount_marshall_perf_instances");
+ depth++;
+
+ for(inst = 0; inst < object.NumInstances; inst++)
+ {
+ instance = object.instances[inst];
+
+ if(!prs_align(ps))
+ return False;
+ if(!prs_uint32("ByteLength", ps, depth, &instance.ByteLength))
+ return False;
+ if(!prs_uint32("ParentObjectTitleIndex", ps, depth, &instance.ParentObjectTitleIndex))
+ return False;
+ if(!prs_uint32("ParentObjectTitlePointer", ps, depth, &instance.ParentObjectTitlePointer))
+ return False;
+ if(!prs_uint32("UniqueID", ps, depth, &instance.UniqueID))
+ return False;
+ if(!prs_uint32("NameOffset", ps, depth, &instance.NameOffset))
+ return False;
+ if(!prs_uint32("NameLength", ps, depth, &instance.NameLength))
+ return False;
+ if(!prs_uint8s(False, "InstanceName", ps, depth, instance.data,
+ instance.ByteLength - instance.NameOffset))
+ return False;
+ if(_reg_perfcount_marshall_perf_counter_data(ps, instance.counter_data, depth) == False)
+ return False;
+ }
+
+ return True;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+static bool _reg_perfcount_marshall_perf_objects(prs_struct *ps, struct PERF_DATA_BLOCK block, int depth)
+{
+ int obj;
+
+ struct PERF_OBJECT_TYPE object;
+
+ prs_debug(ps, depth, "", "_reg_perfcount_marshall_perf_objects");
+ depth++;
+
+ for(obj = 0; obj < block.NumObjectTypes; obj++)
+ {
+ object = block.objects[obj];
+
+ if(!prs_align(ps))
+ return False;
+
+ if(!prs_uint32("TotalByteLength", ps, depth, &object.TotalByteLength))
+ return False;
+ if(!prs_uint32("DefinitionLength", ps, depth, &object.DefinitionLength))
+ return False;
+ if(!prs_uint32("HeaderLength", ps, depth, &object.HeaderLength))
+ return False;
+ if(!prs_uint32("ObjectNameTitleIndex", ps, depth, &object.ObjectNameTitleIndex))
+ return False;
+ if(!prs_uint32("ObjectNameTitlePointer", ps, depth, &object.ObjectNameTitlePointer))
+ return False;
+ if(!prs_uint32("ObjectHelpTitleIndex", ps, depth, &object.ObjectHelpTitleIndex))
+ return False;
+ if(!prs_uint32("ObjectHelpTitlePointer", ps, depth, &object.ObjectHelpTitlePointer))
+ return False;
+ if(!prs_uint32("DetailLevel", ps, depth, &object.DetailLevel))
+ return False;
+ if(!prs_uint32("NumCounters", ps, depth, &object.NumCounters))
+ return False;
+ if(!prs_uint32("DefaultCounter", ps, depth, &object.DefaultCounter))
+ return False;
+ if(!prs_uint32("NumInstances", ps, depth, &object.NumInstances))
+ return False;
+ if(!prs_uint32("CodePage", ps, depth, &object.CodePage))
+ return False;
+ if(!prs_align_uint64(ps))
+ return False;
+ if(!prs_uint64("PerfTime", ps, depth, &object.PerfTime))
+ return False;
+ if(!prs_uint64("PerfFreq", ps, depth, &object.PerfFreq))
+ return False;
+
+ /* Now do the counters */
+ /* If no instances, encode counter_data */
+ /* If instances, encode instance plus counter data for each instance */
+ if(_reg_perfcount_marshall_perf_counters(ps, object, depth) == False)
+ return False;
+ if(object.NumInstances == PERF_NO_INSTANCES)
+ {
+ if(_reg_perfcount_marshall_perf_counter_data(ps, object.counter_data, depth) == False)
+ return False;
+ }
+ else
+ {
+ if(_reg_perfcount_marshall_perf_instances(ps, object, depth) == False)
+ return False;
+ }
+ }
+
+ return True;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+WERROR reg_perfcount_get_hkpd(prs_struct *ps, uint32_t max_buf_size, uint32_t *outbuf_len, const char *object_ids)
+{
+ /*
+ * For a detailed description of the layout of this structure,
+ * see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/perfmon/base/performance_data_format.asp
+ *
+ * By 2006-11-23 this link did not work anymore, I found something
+ * promising under
+ * http://msdn2.microsoft.com/en-us/library/aa373105.aspx -- vl
+ */
+ struct PERF_DATA_BLOCK block;
+ uint32_t buffer_size, base_index;
+
+ buffer_size = 0;
+ base_index = reg_perfcount_get_base_index();
+ ZERO_STRUCT(block);
+
+ buffer_size = reg_perfcount_get_perf_data_block(base_index, ps->mem_ctx, &block, object_ids, ps->bigendian_data);
+
+ if(buffer_size < max_buf_size)
+ {
+ *outbuf_len = buffer_size;
+
+ if (!_reg_perfcount_marshall_perf_data_block(ps, block, 0))
+ return WERR_NOT_ENOUGH_MEMORY;
+
+ if (!_reg_perfcount_marshall_perf_objects(ps, block, 0))
+ return WERR_NOT_ENOUGH_MEMORY;
+
+ return WERR_OK;
+ }
+ else
+ {
+ *outbuf_len = max_buf_size;
+ if (!_reg_perfcount_marshall_perf_data_block(ps, block, 0))
+ return WERR_NOT_ENOUGH_MEMORY;
+
+ return WERR_INSUFFICIENT_BUFFER;
+ }
+}
diff --git a/source3/registry/reg_perfcount.h b/source3/registry/reg_perfcount.h
new file mode 100644
index 0000000..eb5cb22
--- /dev/null
+++ b/source3/registry/reg_perfcount.h
@@ -0,0 +1,34 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer
+ *
+ * Copyright (C) Marcin Krzysztof Porwit 2005,
+ * Copyright (C) Gerald (Jerry) Carter 2005.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _REG_PERFCOUNT_H
+#define _REG_PERFCOUNT_H
+
+#include "reg_parse_prs.h"
+
+uint32_t reg_perfcount_get_base_index(void);
+uint32_t reg_perfcount_get_last_counter(uint32_t base_index);
+uint32_t reg_perfcount_get_last_help(uint32_t last_counter);
+uint32_t reg_perfcount_get_counter_help(uint32_t base_index, char **retbuf);
+uint32_t reg_perfcount_get_counter_names(uint32_t base_index, char **retbuf);
+WERROR reg_perfcount_get_hkpd(prs_struct *ps, uint32_t max_buf_size, uint32_t *outbuf_len, const char *object_ids);
+
+#endif /* _REG_PERFCOUNT_H */
diff --git a/source3/registry/reg_util_internal.c b/source3/registry/reg_util_internal.c
new file mode 100644
index 0000000..728ad0b
--- /dev/null
+++ b/source3/registry/reg_util_internal.c
@@ -0,0 +1,149 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer (utility functions)
+ * Copyright (C) Gerald Carter 2002-2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Implementation of registry frontend view functions. */
+
+#include "includes.h"
+#include "registry.h"
+#include "reg_util_internal.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+/***********************************************************************
+ Utility function for splitting the base path of a registry path off
+ by setting base and new_path to the apprapriate offsets within the
+ path.
+
+ WARNING!! Does modify the original string!
+ ***********************************************************************/
+
+bool reg_split_path(char *path, char **base, char **new_path)
+{
+ char *p;
+
+ *new_path = *base = NULL;
+
+ if (!path) {
+ return false;
+ }
+ *base = path;
+
+ p = strchr(path, '\\');
+
+ if ( p ) {
+ *p = '\0';
+ *new_path = p+1;
+ }
+
+ return true;
+}
+
+/***********************************************************************
+ Utility function for splitting the base path of a registry path off
+ by setting base and new_path to the appropriate offsets within the
+ path.
+
+ WARNING!! Does modify the original string!
+ ***********************************************************************/
+
+bool reg_split_key(char *path, char **base, char **key)
+{
+ char *p;
+
+ *key = *base = NULL;
+
+ if (!path) {
+ return false;
+ }
+
+ *base = path;
+
+ p = strrchr(path, '\\');
+
+ if (p) {
+ *p = '\0';
+ *key = p+1;
+ }
+
+ return true;
+}
+
+/**
+ * The full path to the registry key is used as database key.
+ * Leading and trailing '\' characters are stripped.
+ * Key string is also normalized to UPPER case.
+ */
+
+char *normalize_reg_path(TALLOC_CTX *ctx, const char *keyname )
+{
+ char *p;
+ char *nkeyname;
+
+ /* skip leading '\' chars */
+ while (*keyname == '\\') {
+ keyname++;
+ }
+
+ nkeyname = talloc_strdup(ctx, keyname);
+ if (nkeyname == NULL) {
+ return NULL;
+ }
+
+ /* strip trailing '\' chars */
+ p = strrchr(nkeyname, '\\');
+ while ((p != NULL) && (p[1] == '\0')) {
+ *p = '\0';
+ p = strrchr(nkeyname, '\\');
+ }
+
+ if (!strupper_m(nkeyname)) {
+ TALLOC_FREE(nkeyname);
+ return NULL;
+ }
+
+ return nkeyname;
+}
+
+/**********************************************************************
+ move to next non-delimter character
+*********************************************************************/
+
+char *reg_remaining_path(TALLOC_CTX *ctx, const char *key)
+{
+ char *new_path = NULL;
+ char *p = NULL;
+
+ if (!key || !*key) {
+ return NULL;
+ }
+
+ new_path = talloc_strdup(ctx, key);
+ if (!new_path) {
+ return NULL;
+ }
+ /* normalize_reg_path( new_path ); */
+ if (!(p = strchr(new_path, '\\')) ) {
+ p = new_path;
+ } else {
+ p++;
+ }
+
+ return p;
+}
diff --git a/source3/registry/reg_util_internal.h b/source3/registry/reg_util_internal.h
new file mode 100644
index 0000000..0cb370e
--- /dev/null
+++ b/source3/registry/reg_util_internal.h
@@ -0,0 +1,28 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Virtual Windows Registry Layer (utility functions)
+ * Copyright (C) Gerald Carter 2002-2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _REG_UTIL_H
+#define _REG_UTIL_H
+
+bool reg_split_path(char *path, char **base, char **new_path);
+bool reg_split_key(char *path, char **base, char **key);
+char *normalize_reg_path(TALLOC_CTX *ctx, const char *keyname );
+char *reg_remaining_path(TALLOC_CTX *ctx, const char *key);
+
+#endif /* _REG_UTIL_H */
diff --git a/source3/registry/reg_util_token.c b/source3/registry/reg_util_token.c
new file mode 100644
index 0000000..d599b3a
--- /dev/null
+++ b/source3/registry/reg_util_token.c
@@ -0,0 +1,61 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Registry helper routines
+ * Copyright (C) Michael Adam 2007
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "reg_util_token.h"
+#include "../libcli/security/security.h"
+
+/*
+ * create a fake token just with enough rights to
+ * locally access the registry:
+ *
+ * - builtin administrators sid
+ * - disk operators privilege
+ */
+NTSTATUS registry_create_admin_token(TALLOC_CTX *mem_ctx,
+ struct security_token **ptoken)
+{
+ NTSTATUS status;
+ struct security_token *token = NULL;
+
+ if (ptoken == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ token = talloc_zero(mem_ctx, struct security_token);
+ if (token == NULL) {
+ DEBUG(1, ("talloc failed\n"));
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ security_token_set_privilege(token, SEC_PRIV_DISK_OPERATOR);
+
+ status = add_sid_to_array(token, &global_sid_Builtin_Administrators,
+ &token->sids, &token->num_sids);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Error adding builtin administrators sid "
+ "to fake token.\n"));
+ goto done;
+ }
+
+ *ptoken = token;
+
+done:
+ return status;
+}
diff --git a/source3/registry/reg_util_token.h b/source3/registry/reg_util_token.h
new file mode 100644
index 0000000..558c787
--- /dev/null
+++ b/source3/registry/reg_util_token.h
@@ -0,0 +1,26 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Registry helper routines
+ * Copyright (C) Michael Adam 2007
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _REG_UTIL_TOKEN_H
+#define _REG_UTIL_TOKEN_H
+
+NTSTATUS registry_create_admin_token(TALLOC_CTX *mem_ctx,
+ struct security_token **ptoken);
+
+#endif /* _REG_UTIL_TOKEN_H */
diff --git a/source3/registry/regfio.c b/source3/registry/regfio.c
new file mode 100644
index 0000000..e7bb8d1
--- /dev/null
+++ b/source3/registry/regfio.c
@@ -0,0 +1,1993 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Windows NT registry I/O library
+ * Copyright (c) Gerald (Jerry) Carter 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "regfio.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "../libcli/security/security_descriptor.h"
+#include "../libcli/security/secdesc.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+/*******************************************************************
+ *
+ * TODO : Right now this code basically ignores classnames.
+ *
+ ******************************************************************/
+
+#if defined(PARANOID_MALLOC_CHECKER)
+#define PRS_ALLOC_MEM(ps, type, count) (type *)prs_alloc_mem_((ps),sizeof(type),(count))
+#else
+#define PRS_ALLOC_MEM(ps, type, count) (type *)prs_alloc_mem((ps),sizeof(type),(count))
+#endif
+
+/*******************************************************************
+ Reads or writes an NTTIME structure.
+********************************************************************/
+
+static bool smb_io_time(const char *desc, NTTIME *nttime, prs_struct *ps, int depth)
+{
+ uint32_t low, high;
+ if (nttime == NULL)
+ return False;
+
+ prs_debug(ps, depth, desc, "smb_io_time");
+ depth++;
+
+ if(!prs_align(ps))
+ return False;
+
+ if (MARSHALLING(ps)) {
+ low = *nttime & 0xFFFFFFFF;
+ high = *nttime >> 32;
+ }
+
+ if(!prs_uint32("low ", ps, depth, &low)) /* low part */
+ return False;
+ if(!prs_uint32("high", ps, depth, &high)) /* high part */
+ return False;
+
+ if (UNMARSHALLING(ps)) {
+ *nttime = (((uint64_t)high << 32) + low);
+ }
+
+ return True;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static int write_block( REGF_FILE *file, prs_struct *ps, uint32_t offset )
+{
+ int bytes_written, returned;
+ char *buffer = prs_data_p( ps );
+ uint32_t buffer_size = prs_data_size( ps );
+ SMB_STRUCT_STAT sbuf;
+
+ if ( file->fd == -1 )
+ return -1;
+
+ /* check for end of file */
+
+ if (sys_fstat(file->fd, &sbuf, false)) {
+ DEBUG(0,("write_block: stat() failed! (%s)\n", strerror(errno)));
+ return -1;
+ }
+
+ if ( lseek( file->fd, offset, SEEK_SET ) == -1 ) {
+ DEBUG(0,("write_block: lseek() failed! (%s)\n", strerror(errno) ));
+ return -1;
+ }
+
+ bytes_written = returned = 0;
+ while ( bytes_written < buffer_size ) {
+ if ( (returned = write( file->fd, buffer+bytes_written, buffer_size-bytes_written )) == -1 ) {
+ DEBUG(0,("write_block: write() failed! (%s)\n", strerror(errno) ));
+ return False;
+ }
+
+ bytes_written += returned;
+ }
+
+ return bytes_written;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static int read_block( REGF_FILE *file, prs_struct *ps, uint32_t file_offset, uint32_t block_size )
+{
+ int bytes_read, returned;
+ char *buffer;
+ SMB_STRUCT_STAT sbuf;
+
+ /* check for end of file */
+
+ if (sys_fstat(file->fd, &sbuf, false)) {
+ DEBUG(0,("read_block: stat() failed! (%s)\n", strerror(errno)));
+ return -1;
+ }
+
+ if ( (size_t)file_offset >= sbuf.st_ex_size )
+ return -1;
+
+ /* if block_size == 0, we are parsing HBIN records and need
+ to read some of the header to get the block_size from there */
+
+ if ( block_size == 0 ) {
+ char hdr[0x20];
+
+ if ( lseek( file->fd, file_offset, SEEK_SET ) == -1 ) {
+ DEBUG(0,("read_block: lseek() failed! (%s)\n", strerror(errno) ));
+ return -1;
+ }
+
+ returned = read( file->fd, hdr, 0x20 );
+ if ( (returned == -1) || (returned < 0x20) ) {
+ DEBUG(0,("read_block: failed to read in HBIN header. Is the file corrupt?\n"));
+ return -1;
+ }
+
+ /* make sure this is an hbin header */
+
+ if ( strncmp( hdr, "hbin", HBIN_HDR_SIZE ) != 0 ) {
+ DEBUG(0,("read_block: invalid block header!\n"));
+ return -1;
+ }
+
+ block_size = IVAL( hdr, 0x08 );
+ }
+
+ DEBUG(10,("read_block: block_size == 0x%x\n", block_size ));
+
+ /* set the offset, initialize the buffer, and read the block from disk */
+
+ if ( lseek( file->fd, file_offset, SEEK_SET ) == -1 ) {
+ DEBUG(0,("read_block: lseek() failed! (%s)\n", strerror(errno) ));
+ return -1;
+ }
+
+ if (!prs_init( ps, block_size, file->mem_ctx, UNMARSHALL )) {
+ DEBUG(0,("read_block: prs_init() failed! (%s)\n", strerror(errno) ));
+ return -1;
+ }
+ buffer = prs_data_p( ps );
+ bytes_read = returned = 0;
+
+ while ( bytes_read < block_size ) {
+ if ( (returned = read( file->fd, buffer+bytes_read, block_size-bytes_read )) == -1 ) {
+ DEBUG(0,("read_block: read() failed (%s)\n", strerror(errno) ));
+ return False;
+ }
+ if ( (returned == 0) && (bytes_read < block_size) ) {
+ DEBUG(0,("read_block: not a valid registry file ?\n" ));
+ return False;
+ }
+
+ bytes_read += returned;
+ }
+
+ return bytes_read;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool write_hbin_block( REGF_FILE *file, REGF_HBIN *hbin )
+{
+ if ( !hbin->dirty )
+ return True;
+
+ /* write free space record if any is available */
+
+ if ( hbin->free_off != REGF_OFFSET_NONE ) {
+ uint32_t header = 0xffffffff;
+
+ if ( !prs_set_offset( &hbin->ps, hbin->free_off-sizeof(uint32_t) ) )
+ return False;
+ if ( !prs_uint32( "free_size", &hbin->ps, 0, &hbin->free_size ) )
+ return False;
+ if ( !prs_uint32( "free_header", &hbin->ps, 0, &header ) )
+ return False;
+ }
+
+ hbin->dirty = (write_block( file, &hbin->ps, hbin->file_off ) != -1);
+
+ return hbin->dirty;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool hbin_block_close( REGF_FILE *file, REGF_HBIN *hbin )
+{
+ REGF_HBIN *p;
+
+ /* remove the block from the open list and flush it to disk */
+
+ for ( p=file->block_list; p && p!=hbin; p=p->next )
+ ;
+
+ if ( p == hbin ) {
+ DLIST_REMOVE( file->block_list, hbin );
+ }
+ else
+ DEBUG(0,("hbin_block_close: block not in open list!\n"));
+
+ if ( !write_hbin_block( file, hbin ) )
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool prs_regf_block( const char *desc, prs_struct *ps, int depth, REGF_FILE *file )
+{
+ prs_debug(ps, depth, desc, "prs_regf_block");
+ depth++;
+
+ if ( !prs_uint8s( True, "header", ps, depth, (uint8_t *)file->header, sizeof( file->header )) )
+ return False;
+
+ /* yes, these values are always identical so store them only once */
+
+ if ( !prs_uint32( "unknown1", ps, depth, &file->unknown1 ))
+ return False;
+ if ( !prs_uint32( "unknown1 (again)", ps, depth, &file->unknown1 ))
+ return False;
+
+ /* get the modtime */
+
+ if ( !prs_set_offset( ps, 0x0c ) )
+ return False;
+ if ( !smb_io_time( "modtime", &file->mtime, ps, depth ) )
+ return False;
+
+ /* constants */
+
+ if ( !prs_uint32( "unknown2", ps, depth, &file->unknown2 ))
+ return False;
+ if ( !prs_uint32( "unknown3", ps, depth, &file->unknown3 ))
+ return False;
+ if ( !prs_uint32( "unknown4", ps, depth, &file->unknown4 ))
+ return False;
+ if ( !prs_uint32( "unknown5", ps, depth, &file->unknown5 ))
+ return False;
+
+ /* get file offsets */
+
+ if ( !prs_set_offset( ps, 0x24 ) )
+ return False;
+ if ( !prs_uint32( "data_offset", ps, depth, &file->data_offset ))
+ return False;
+ if ( !prs_uint32( "last_block", ps, depth, &file->last_block ))
+ return False;
+
+ /* one more constant */
+
+ if ( !prs_uint32( "unknown6", ps, depth, &file->unknown6 ))
+ return False;
+
+ /* get the checksum */
+
+ if ( !prs_set_offset( ps, 0x01fc ) )
+ return False;
+ if ( !prs_uint32( "checksum", ps, depth, &file->checksum ))
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool prs_hbin_block( const char *desc, prs_struct *ps, int depth, REGF_HBIN *hbin )
+{
+ uint32_t block_size2;
+
+ prs_debug(ps, depth, desc, "prs_hbin_block");
+ depth++;
+
+ if ( !prs_uint8s( True, "header", ps, depth, (uint8_t*)hbin->header, sizeof( hbin->header )) )
+ return False;
+
+ if ( !prs_uint32( "first_hbin_off", ps, depth, &hbin->first_hbin_off ))
+ return False;
+
+ /* The dosreg.cpp comments say that the block size is at 0x1c.
+ According to a WINXP NTUSER.dat file, this is wrong. The block_size
+ is at 0x08 */
+
+ if ( !prs_uint32( "block_size", ps, depth, &hbin->block_size ))
+ return False;
+
+ block_size2 = hbin->block_size;
+ prs_set_offset( ps, 0x1c );
+ if ( !prs_uint32( "block_size2", ps, depth, &block_size2 ))
+ return False;
+
+ if ( MARSHALLING(ps) )
+ hbin->dirty = True;
+
+
+ return True;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool prs_nk_rec( const char *desc, prs_struct *ps, int depth, REGF_NK_REC *nk )
+{
+ uint16_t class_length, name_length;
+ uint32_t start;
+ uint32_t data_size, start_off, end_off;
+ uint32_t unknown_off = REGF_OFFSET_NONE;
+
+ nk->hbin_off = prs_offset( ps );
+ start = nk->hbin_off;
+
+ prs_debug(ps, depth, desc, "prs_nk_rec");
+ depth++;
+
+ /* back up and get the data_size */
+
+ if ( !prs_set_offset( ps, prs_offset(ps)-sizeof(uint32_t)) )
+ return False;
+ start_off = prs_offset( ps );
+ if ( !prs_uint32( "rec_size", ps, depth, &nk->rec_size ))
+ return False;
+
+ if ( !prs_uint8s( True, "header", ps, depth, (uint8_t *)nk->header, sizeof( nk->header )) )
+ return False;
+
+ if ( !prs_uint16( "key_type", ps, depth, &nk->key_type ))
+ return False;
+ if ( !smb_io_time( "mtime", &nk->mtime, ps, depth ))
+ return False;
+
+ if ( !prs_set_offset( ps, start+0x0010 ) )
+ return False;
+ if ( !prs_uint32( "parent_off", ps, depth, &nk->parent_off ))
+ return False;
+ if ( !prs_uint32( "num_subkeys", ps, depth, &nk->num_subkeys ))
+ return False;
+
+ if ( !prs_set_offset( ps, start+0x001c ) )
+ return False;
+ if ( !prs_uint32( "subkeys_off", ps, depth, &nk->subkeys_off ))
+ return False;
+ if ( !prs_uint32( "unknown_off", ps, depth, &unknown_off) )
+ return False;
+
+ if ( !prs_set_offset( ps, start+0x0024 ) )
+ return False;
+ if ( !prs_uint32( "num_values", ps, depth, &nk->num_values ))
+ return False;
+ if ( !prs_uint32( "values_off", ps, depth, &nk->values_off ))
+ return False;
+ if ( !prs_uint32( "sk_off", ps, depth, &nk->sk_off ))
+ return False;
+ if ( !prs_uint32( "classname_off", ps, depth, &nk->classname_off ))
+ return False;
+
+ if ( !prs_uint32( "max_bytes_subkeyname", ps, depth, &nk->max_bytes_subkeyname))
+ return False;
+ if ( !prs_uint32( "max_bytes_subkeyclassname", ps, depth, &nk->max_bytes_subkeyclassname))
+ return False;
+ if ( !prs_uint32( "max_bytes_valuename", ps, depth, &nk->max_bytes_valuename))
+ return False;
+ if ( !prs_uint32( "max_bytes_value", ps, depth, &nk->max_bytes_value))
+ return False;
+ if ( !prs_uint32( "unknown index", ps, depth, &nk->unk_index))
+ return False;
+
+ name_length = nk->keyname ? strlen(nk->keyname) : 0 ;
+ class_length = nk->classname ? strlen(nk->classname) : 0 ;
+ if ( !prs_uint16( "name_length", ps, depth, &name_length ))
+ return False;
+ if ( !prs_uint16( "class_length", ps, depth, &class_length ))
+ return False;
+
+ if ( class_length ) {
+ ;;
+ }
+
+ if ( name_length ) {
+ if ( UNMARSHALLING(ps) ) {
+ if ( !(nk->keyname = PRS_ALLOC_MEM( ps, char, name_length+1 )) )
+ return False;
+ }
+
+ if ( !prs_uint8s( True, "name", ps, depth, (uint8_t *)nk->keyname, name_length) )
+ return False;
+
+ if ( UNMARSHALLING(ps) )
+ nk->keyname[name_length] = '\0';
+ }
+
+ end_off = prs_offset( ps );
+
+ /* data_size must be divisible by 8 and large enough to hold the original record */
+
+ data_size = ((start_off - end_off) & 0xfffffff8 );
+ if ( data_size > nk->rec_size )
+ DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, nk->rec_size));
+
+ if ( MARSHALLING(ps) )
+ nk->hbin->dirty = True;
+
+ return True;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static uint32_t regf_block_checksum( prs_struct *ps )
+{
+ char *buffer = prs_data_p( ps );
+ uint32_t checksum, x;
+ int i;
+
+ /* XOR of all bytes 0x0000 - 0x01FB */
+
+ checksum = x = 0;
+
+ for ( i=0; i<0x01FB; i+=4 ) {
+ x = IVAL(buffer, i );
+ checksum ^= x;
+ }
+
+ return checksum;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool read_regf_block( REGF_FILE *file )
+{
+ prs_struct ps;
+ uint32_t checksum;
+
+ /* grab the first block from the file */
+
+ if ( read_block( file, &ps, 0, REGF_BLOCKSIZE ) == -1 )
+ return False;
+
+ /* parse the block and verify the checksum */
+
+ if ( !prs_regf_block( "regf_header", &ps, 0, file ) )
+ return False;
+
+ checksum = regf_block_checksum( &ps );
+
+ prs_mem_free( &ps );
+
+ if ( file->checksum != checksum && !file->ignore_checksums) {
+ DEBUG(0,("read_regf_block: invalid checksum\n" ));
+ return False;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static REGF_HBIN* read_hbin_block( REGF_FILE *file, off_t offset )
+{
+ REGF_HBIN *hbin;
+ uint32_t record_size, curr_off, block_size, header;
+
+ if ( !(hbin = talloc_zero(file->mem_ctx, REGF_HBIN)) )
+ return NULL;
+ hbin->file_off = offset;
+ hbin->free_off = -1;
+
+ if ( read_block( file, &hbin->ps, offset, 0 ) == -1 )
+ return NULL;
+
+ if ( !prs_hbin_block( "hbin", &hbin->ps, 0, hbin ) )
+ return NULL;
+
+ /* this should be the same thing as hbin->block_size but just in case */
+
+ block_size = prs_data_size( &hbin->ps );
+
+ /* Find the available free space offset. Always at the end,
+ so walk the record list and stop when you get to the end.
+ The end is defined by a record header of 0xffffffff. The
+ previous 4 bytes contains the amount of free space remaining
+ in the hbin block. */
+
+ /* remember that the record_size is in the 4 bytes preceding the record itself */
+
+ if ( !prs_set_offset( &hbin->ps, file->data_offset+HBIN_HDR_SIZE-sizeof(uint32_t) ) )
+ return NULL;
+
+ record_size = 0;
+ header = 0;
+ curr_off = prs_offset( &hbin->ps );
+ while ( header != 0xffffffff ) {
+ /* not done yet so reset the current offset to the
+ next record_size field */
+
+ curr_off = curr_off+record_size;
+
+ /* for some reason the record_size of the last record in
+ an hbin block can extend past the end of the block
+ even though the record fits within the remaining
+ space....aaarrrgggghhhhhh */
+
+ if ( curr_off >= block_size ) {
+ record_size = -1;
+ curr_off = -1;
+ break;
+ }
+
+ if ( !prs_set_offset( &hbin->ps, curr_off) )
+ return NULL;
+
+ if ( !prs_uint32( "rec_size", &hbin->ps, 0, &record_size ) )
+ return NULL;
+ if ( !prs_uint32( "header", &hbin->ps, 0, &header ) )
+ return NULL;
+
+ if (record_size == 0)
+ return NULL;
+
+ if ( record_size & 0x80000000 ) {
+ /* absolute_value(record_size) */
+ record_size = (record_size ^ 0xffffffff) + 1;
+ }
+ }
+
+ /* save the free space offset */
+
+ if ( header == 0xffffffff ) {
+
+ /* account for the fact that the curr_off is 4 bytes behind the actual
+ record header */
+
+ hbin->free_off = curr_off + sizeof(uint32_t);
+ hbin->free_size = record_size;
+ }
+
+ DEBUG(10,("read_hbin_block: free space offset == 0x%x\n", hbin->free_off));
+
+ if ( !prs_set_offset( &hbin->ps, file->data_offset+HBIN_HDR_SIZE ) )
+ return NULL;
+
+ return hbin;
+}
+
+/*******************************************************************
+ Input a random offset and receive the corresponding HBIN
+ block for it
+*******************************************************************/
+
+static bool hbin_contains_offset( REGF_HBIN *hbin, uint32_t offset )
+{
+ if ( !hbin )
+ return False;
+
+ if ( (offset > hbin->first_hbin_off) && (offset < (hbin->first_hbin_off+hbin->block_size)) )
+ return True;
+
+ return False;
+}
+
+/*******************************************************************
+ Input a random offset and receive the corresponding HBIN
+ block for it
+*******************************************************************/
+
+static REGF_HBIN* lookup_hbin_block( REGF_FILE *file, uint32_t offset )
+{
+ REGF_HBIN *hbin = NULL;
+ uint32_t block_off;
+
+ /* start with the open list */
+
+ for ( hbin=file->block_list; hbin; hbin=hbin->next ) {
+ DEBUG(10,("lookup_hbin_block: address = 0x%x [0x%lx]\n", hbin->file_off, (unsigned long)hbin ));
+ if ( hbin_contains_offset( hbin, offset ) )
+ return hbin;
+ }
+
+ if ( !hbin ) {
+ /* start at the beginning */
+
+ block_off = REGF_BLOCKSIZE;
+ do {
+ /* cleanup before the next round */
+ if ( hbin )
+ prs_mem_free( &hbin->ps );
+
+ hbin = read_hbin_block( file, block_off );
+
+ if ( hbin )
+ block_off = hbin->file_off + hbin->block_size;
+
+ } while ( hbin && !hbin_contains_offset( hbin, offset ) );
+ }
+
+ if ( hbin )
+ DLIST_ADD( file->block_list, hbin );
+
+ return hbin;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool prs_hash_rec( const char *desc, prs_struct *ps, int depth, REGF_HASH_REC *hash )
+{
+ prs_debug(ps, depth, desc, "prs_hash_rec");
+ depth++;
+
+ if ( !prs_uint32( "nk_off", ps, depth, &hash->nk_off ))
+ return False;
+ if ( !prs_uint8s( True, "keycheck", ps, depth, hash->keycheck, sizeof( hash->keycheck )) )
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool hbin_prs_lf_records( const char *desc, REGF_HBIN *hbin, int depth, REGF_NK_REC *nk )
+{
+ int i;
+ REGF_LF_REC *lf = &nk->subkeys;
+ uint32_t data_size, start_off, end_off;
+
+ prs_debug(&hbin->ps, depth, desc, "prs_lf_records");
+ depth++;
+
+ /* check if we have anything to do first */
+
+ if ( nk->num_subkeys == 0 )
+ return True;
+
+ /* move to the LF record */
+
+ if ( !prs_set_offset( &hbin->ps, nk->subkeys_off + HBIN_HDR_SIZE - hbin->first_hbin_off ) )
+ return False;
+
+ /* backup and get the data_size */
+
+ if ( !prs_set_offset( &hbin->ps, prs_offset(&hbin->ps)-sizeof(uint32_t)) )
+ return False;
+ start_off = prs_offset( &hbin->ps );
+ if ( !prs_uint32( "rec_size", &hbin->ps, depth, &lf->rec_size ))
+ return False;
+
+ if ( !prs_uint8s( True, "header", &hbin->ps, depth, (uint8_t *)lf->header, sizeof( lf->header )) )
+ return False;
+
+ if ( !prs_uint16( "num_keys", &hbin->ps, depth, &lf->num_keys))
+ return False;
+
+ if ( UNMARSHALLING(&hbin->ps) ) {
+ if (lf->num_keys) {
+ if ( !(lf->hashes = PRS_ALLOC_MEM( &hbin->ps, REGF_HASH_REC, lf->num_keys )) )
+ return False;
+ } else {
+ lf->hashes = NULL;
+ }
+ }
+
+ for ( i=0; i<lf->num_keys; i++ ) {
+ if ( !prs_hash_rec( "hash_rec", &hbin->ps, depth, &lf->hashes[i] ) )
+ return False;
+ }
+
+ end_off = prs_offset( &hbin->ps );
+
+ /* data_size must be divisible by 8 and large enough to hold the original record */
+
+ data_size = ((start_off - end_off) & 0xfffffff8 );
+ if ( data_size > lf->rec_size )
+ DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, lf->rec_size));
+
+ if ( MARSHALLING(&hbin->ps) )
+ hbin->dirty = True;
+
+ return True;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool hbin_prs_sk_rec( const char *desc, REGF_HBIN *hbin, int depth, REGF_SK_REC *sk )
+{
+ prs_struct *ps = &hbin->ps;
+ uint16_t tag = 0xFFFF;
+ uint32_t data_size, start_off, end_off;
+
+
+ prs_debug(ps, depth, desc, "hbin_prs_sk_rec");
+ depth++;
+
+ if ( !prs_set_offset( &hbin->ps, sk->sk_off + HBIN_HDR_SIZE - hbin->first_hbin_off ) )
+ return False;
+
+ /* backup and get the data_size */
+
+ if ( !prs_set_offset( &hbin->ps, prs_offset(&hbin->ps)-sizeof(uint32_t)) )
+ return False;
+ start_off = prs_offset( &hbin->ps );
+ if ( !prs_uint32( "rec_size", &hbin->ps, depth, &sk->rec_size ))
+ return False;
+
+ if ( !prs_uint8s( True, "header", ps, depth, (uint8_t *)sk->header, sizeof( sk->header )) )
+ return False;
+ if ( !prs_uint16( "tag", ps, depth, &tag))
+ return False;
+
+ if ( !prs_uint32( "prev_sk_off", ps, depth, &sk->prev_sk_off))
+ return False;
+ if ( !prs_uint32( "next_sk_off", ps, depth, &sk->next_sk_off))
+ return False;
+ if ( !prs_uint32( "ref_count", ps, depth, &sk->ref_count))
+ return False;
+ if ( !prs_uint32( "size", ps, depth, &sk->size))
+ return False;
+
+ {
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx = prs_get_mem_context(&hbin->ps);
+ DATA_BLOB blob;
+
+ if (MARSHALLING(&hbin->ps)) {
+ status = marshall_sec_desc(mem_ctx,
+ sk->sec_desc,
+ &blob.data, &blob.length);
+ if (!NT_STATUS_IS_OK(status))
+ return False;
+ if (!prs_copy_data_in(&hbin->ps, (const char *)blob.data, blob.length))
+ return False;
+ } else {
+ blob = data_blob_const(
+ prs_data_p(&hbin->ps) + prs_offset(&hbin->ps),
+ prs_data_size(&hbin->ps) - prs_offset(&hbin->ps)
+ );
+ status = unmarshall_sec_desc(mem_ctx,
+ blob.data, blob.length,
+ &sk->sec_desc);
+ if (!NT_STATUS_IS_OK(status))
+ return False;
+ prs_set_offset(&hbin->ps, blob.length);
+ }
+ }
+
+ end_off = prs_offset( &hbin->ps );
+
+ /* data_size must be divisible by 8 and large enough to hold the original record */
+
+ data_size = ((start_off - end_off) & 0xfffffff8 );
+ if ( data_size > sk->rec_size )
+ DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, sk->rec_size));
+
+ if ( MARSHALLING(&hbin->ps) )
+ hbin->dirty = True;
+
+ return True;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool hbin_prs_vk_rec( const char *desc, REGF_HBIN *hbin, int depth, REGF_VK_REC *vk, REGF_FILE *file )
+{
+ uint32_t offset;
+ uint16_t name_length;
+ prs_struct *ps = &hbin->ps;
+ uint32_t data_size, start_off, end_off;
+
+ prs_debug(ps, depth, desc, "prs_vk_rec");
+ depth++;
+
+ /* backup and get the data_size */
+
+ if ( !prs_set_offset( &hbin->ps, prs_offset(&hbin->ps)-sizeof(uint32_t)) )
+ return False;
+ start_off = prs_offset( &hbin->ps );
+ if ( !prs_uint32( "rec_size", &hbin->ps, depth, &vk->rec_size ))
+ return False;
+
+ if ( !prs_uint8s( True, "header", ps, depth, (uint8_t *)vk->header, sizeof( vk->header )) )
+ return False;
+
+ if ( MARSHALLING(&hbin->ps) )
+ name_length = strlen(vk->valuename);
+
+ if ( !prs_uint16( "name_length", ps, depth, &name_length ))
+ return False;
+ if ( !prs_uint32( "data_size", ps, depth, &vk->data_size ))
+ return False;
+ if ( !prs_uint32( "data_off", ps, depth, &vk->data_off ))
+ return False;
+ if ( !prs_uint32( "type", ps, depth, &vk->type))
+ return False;
+ if ( !prs_uint16( "flag", ps, depth, &vk->flag))
+ return False;
+
+ offset = prs_offset( ps );
+ offset += 2; /* skip 2 bytes */
+ prs_set_offset( ps, offset );
+
+ /* get the name */
+
+ if ( vk->flag&VK_FLAG_NAME_PRESENT ) {
+
+ if ( UNMARSHALLING(&hbin->ps) ) {
+ if ( !(vk->valuename = PRS_ALLOC_MEM( ps, char, name_length+1 )))
+ return False;
+ }
+ if ( !prs_uint8s( True, "name", ps, depth, (uint8_t *)vk->valuename, name_length ) )
+ return False;
+ }
+
+ end_off = prs_offset( &hbin->ps );
+
+ /* get the data if necessary */
+
+ if ( vk->data_size != 0 ) {
+ bool charmode = False;
+
+ if ( (vk->type == REG_SZ) || (vk->type == REG_MULTI_SZ) )
+ charmode = True;
+
+ /* the data is stored in the offset if the size <= 4 */
+
+ if ( !(vk->data_size & VK_DATA_IN_OFFSET) ) {
+ REGF_HBIN *hblock = hbin;
+ uint32_t data_rec_size;
+
+ if ( UNMARSHALLING(&hbin->ps) ) {
+ if ( !(vk->data = PRS_ALLOC_MEM( ps, uint8_t, vk->data_size) ) )
+ return False;
+ }
+
+ /* this data can be in another hbin */
+ if ( !hbin_contains_offset( hbin, vk->data_off ) ) {
+ if ( !(hblock = lookup_hbin_block( file, vk->data_off )) )
+ return False;
+ }
+ if ( !(prs_set_offset( &hblock->ps, (vk->data_off+HBIN_HDR_SIZE-hblock->first_hbin_off)-sizeof(uint32_t) )) )
+ return False;
+
+ if ( MARSHALLING(&hblock->ps) ) {
+ data_rec_size = ( (vk->data_size+sizeof(uint32_t)) & 0xfffffff8 ) + 8;
+ data_rec_size = ( data_rec_size - 1 ) ^ 0xFFFFFFFF;
+ }
+ if ( !prs_uint32( "data_rec_size", &hblock->ps, depth, &data_rec_size ))
+ return False;
+ if ( !prs_uint8s( charmode, "data", &hblock->ps, depth, vk->data, vk->data_size) )
+ return False;
+
+ if ( MARSHALLING(&hblock->ps) )
+ hblock->dirty = True;
+ }
+ else {
+ if ( !(vk->data = PRS_ALLOC_MEM( ps, uint8_t, 4 ) ) )
+ return False;
+ SIVAL( vk->data, 0, vk->data_off );
+ }
+
+ }
+
+ /* data_size must be divisible by 8 and large enough to hold the original record */
+
+ data_size = ((start_off - end_off ) & 0xfffffff8 );
+ if ( data_size != vk->rec_size )
+ DEBUG(10,("prs_vk_rec: data_size check failed (0x%x < 0x%x)\n", data_size, vk->rec_size));
+
+ if ( MARSHALLING(&hbin->ps) )
+ hbin->dirty = True;
+
+ return True;
+}
+
+/*******************************************************************
+ read a VK record which is contained in the HBIN block stored
+ in the prs_struct *ps.
+*******************************************************************/
+
+static bool hbin_prs_vk_records( const char *desc, REGF_HBIN *hbin, int depth, REGF_NK_REC *nk, REGF_FILE *file )
+{
+ int i;
+ uint32_t record_size;
+
+ prs_debug(&hbin->ps, depth, desc, "prs_vk_records");
+ depth++;
+
+ /* check if we have anything to do first */
+
+ if ( nk->num_values == 0 )
+ return True;
+
+ if ( UNMARSHALLING(&hbin->ps) ) {
+ if ( !(nk->values = PRS_ALLOC_MEM( &hbin->ps, REGF_VK_REC, nk->num_values ) ) )
+ return False;
+ }
+
+ /* convert the offset to something relative to this HBIN block */
+
+ if ( !prs_set_offset( &hbin->ps, nk->values_off+HBIN_HDR_SIZE-hbin->first_hbin_off-sizeof(uint32_t)) )
+ return False;
+
+ if ( MARSHALLING( &hbin->ps) ) {
+ record_size = ( ( nk->num_values * sizeof(uint32_t) ) & 0xfffffff8 ) + 8;
+ record_size = (record_size - 1) ^ 0xFFFFFFFF;
+ }
+
+ if ( !prs_uint32( "record_size", &hbin->ps, depth, &record_size ) )
+ return False;
+
+ for ( i=0; i<nk->num_values; i++ ) {
+ if ( !prs_uint32( "vk_off", &hbin->ps, depth, &nk->values[i].rec_off ) )
+ return False;
+ }
+
+ for ( i=0; i<nk->num_values; i++ ) {
+ REGF_HBIN *sub_hbin = hbin;
+ uint32_t new_offset;
+
+ if ( !hbin_contains_offset( hbin, nk->values[i].rec_off ) ) {
+ sub_hbin = lookup_hbin_block( file, nk->values[i].rec_off );
+ if ( !sub_hbin ) {
+ DEBUG(0,("hbin_prs_vk_records: Failed to find HBIN block containing offset [0x%x]\n",
+ nk->values[i].hbin_off));
+ return False;
+ }
+ }
+
+ new_offset = nk->values[i].rec_off + HBIN_HDR_SIZE - sub_hbin->first_hbin_off;
+ if ( !prs_set_offset( &sub_hbin->ps, new_offset ) )
+ return False;
+ if ( !hbin_prs_vk_rec( "vk_rec", sub_hbin, depth, &nk->values[i], file ) )
+ return False;
+ }
+
+ if ( MARSHALLING(&hbin->ps) )
+ hbin->dirty = True;
+
+
+ return True;
+}
+
+
+/*******************************************************************
+*******************************************************************/
+
+static REGF_SK_REC* find_sk_record_by_offset( REGF_FILE *file, uint32_t offset )
+{
+ REGF_SK_REC *p_sk;
+
+ for ( p_sk=file->sec_desc_list; p_sk; p_sk=p_sk->next ) {
+ if ( p_sk->sk_off == offset )
+ return p_sk;
+ }
+
+ return NULL;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static REGF_SK_REC* find_sk_record_by_sec_desc( REGF_FILE *file, struct security_descriptor *sd )
+{
+ REGF_SK_REC *p;
+
+ for ( p=file->sec_desc_list; p; p=p->next ) {
+ if ( security_descriptor_equal( p->sec_desc, sd ) )
+ return p;
+ }
+
+ /* failure */
+
+ return NULL;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool hbin_prs_key( REGF_FILE *file, REGF_HBIN *hbin, REGF_NK_REC *nk )
+{
+ int depth = 0;
+ REGF_HBIN *sub_hbin;
+
+ prs_debug(&hbin->ps, depth, "", "prs_key");
+ depth++;
+
+ /* get the initial nk record */
+
+ if ( !prs_nk_rec( "nk_rec", &hbin->ps, depth, nk ))
+ return False;
+
+ /* fill in values */
+
+ if ( nk->num_values && (nk->values_off!=REGF_OFFSET_NONE) ) {
+ sub_hbin = hbin;
+ if ( !hbin_contains_offset( hbin, nk->values_off ) ) {
+ sub_hbin = lookup_hbin_block( file, nk->values_off );
+ if ( !sub_hbin ) {
+ DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing value_list_offset [0x%x]\n",
+ nk->values_off));
+ return False;
+ }
+ }
+
+ if ( !hbin_prs_vk_records( "vk_rec", sub_hbin, depth, nk, file ))
+ return False;
+ }
+
+ /* now get subkeys */
+
+ if ( nk->num_subkeys && (nk->subkeys_off!=REGF_OFFSET_NONE) ) {
+ sub_hbin = hbin;
+ if ( !hbin_contains_offset( hbin, nk->subkeys_off ) ) {
+ sub_hbin = lookup_hbin_block( file, nk->subkeys_off );
+ if ( !sub_hbin ) {
+ DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing subkey_offset [0x%x]\n",
+ nk->subkeys_off));
+ return False;
+ }
+ }
+
+ if ( !hbin_prs_lf_records( "lf_rec", sub_hbin, depth, nk ))
+ return False;
+ }
+
+ /* get the to the security descriptor. First look if we have already parsed it */
+
+ if ( (nk->sk_off!=REGF_OFFSET_NONE) && !( nk->sec_desc = find_sk_record_by_offset( file, nk->sk_off )) ) {
+
+ sub_hbin = hbin;
+ if ( !hbin_contains_offset( hbin, nk->sk_off ) ) {
+ sub_hbin = lookup_hbin_block( file, nk->sk_off );
+ if ( !sub_hbin ) {
+ DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing sk_off [0x%x]\n",
+ nk->sk_off));
+ return False;
+ }
+ }
+
+ if ( !(nk->sec_desc = talloc_zero( file->mem_ctx, REGF_SK_REC )) )
+ return False;
+ nk->sec_desc->sk_off = nk->sk_off;
+ if ( !hbin_prs_sk_rec( "sk_rec", sub_hbin, depth, nk->sec_desc ))
+ return False;
+
+ /* add to the list of security descriptors (ref_count has been read from the files) */
+
+ nk->sec_desc->sk_off = nk->sk_off;
+ DLIST_ADD( file->sec_desc_list, nk->sec_desc );
+ }
+
+ return True;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool next_record( REGF_HBIN *hbin, const char *hdr, bool *eob )
+{
+ uint8_t header[REC_HDR_SIZE];
+ uint32_t record_size;
+ uint32_t curr_off, block_size;
+ bool found = False;
+ prs_struct *ps = &hbin->ps;
+
+ curr_off = prs_offset( ps );
+ if ( curr_off == 0 )
+ prs_set_offset( ps, HBIN_HEADER_REC_SIZE );
+
+ /* assume that the current offset is at the record header
+ and we need to backup to read the record size */
+
+ curr_off -= sizeof(uint32_t);
+
+ block_size = prs_data_size( ps );
+ record_size = 0;
+ memset( header, 0x0, sizeof(uint8_t)*REC_HDR_SIZE );
+ while ( !found ) {
+
+ curr_off = curr_off+record_size;
+ if ( curr_off >= block_size )
+ break;
+
+ if ( !prs_set_offset( &hbin->ps, curr_off) )
+ return False;
+
+ if ( !prs_uint32( "record_size", ps, 0, &record_size ) )
+ return False;
+ if ( !prs_uint8s( True, "header", ps, 0, header, REC_HDR_SIZE ) )
+ return False;
+
+ if (record_size & 0x80000000) {
+ /* absolute_value(record_size) */
+ record_size = (record_size ^ 0xffffffff) + 1;
+ }
+
+ if (record_size < sizeof(REC_HDR_SIZE)) {
+ return false;
+ }
+
+ if (memcmp(header, hdr, REC_HDR_SIZE) == 0) {
+ found = True;
+ curr_off += sizeof(uint32_t);
+ }
+ }
+
+ /* mark prs_struct as done ( at end ) if no more SK records */
+ /* mark end-of-block as True */
+
+ if ( !found ) {
+ prs_set_offset( &hbin->ps, prs_data_size(&hbin->ps) );
+ *eob = True;
+ return False;
+ }
+
+ if ( !prs_set_offset( ps, curr_off ) )
+ return False;
+
+ return True;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool next_nk_record( REGF_FILE *file, REGF_HBIN *hbin, REGF_NK_REC *nk, bool *eob )
+{
+ if ( next_record( hbin, "nk", eob ) && hbin_prs_key( file, hbin, nk ) )
+ return True;
+
+ return False;
+}
+
+/*******************************************************************
+ Initialize the newly created REGF_BLOCK in *file and write the
+ block header to disk
+*******************************************************************/
+
+static bool init_regf_block( REGF_FILE *file )
+{
+ prs_struct ps;
+ bool result = True;
+
+ if ( !prs_init( &ps, REGF_BLOCKSIZE, file->mem_ctx, MARSHALL ) )
+ return False;
+
+ memcpy( file->header, "regf", REGF_HDR_SIZE );
+ file->data_offset = 0x20;
+ file->last_block = 0x1000;
+
+ /* set mod time */
+
+ unix_to_nt_time( &file->mtime, time(NULL) );
+
+ /* hard coded values...no idea what these are ... maybe in time */
+
+ file->unknown1 = 0x2;
+ file->unknown2 = 0x1;
+ file->unknown3 = 0x3;
+ file->unknown4 = 0x0;
+ file->unknown5 = 0x1;
+ file->unknown6 = 0x1;
+
+ /* write header to the buffer */
+
+ if ( !prs_regf_block( "regf_header", &ps, 0, file ) ) {
+ result = False;
+ goto out;
+ }
+
+ /* calculate the checksum, re-marshall data (to include the checksum)
+ and write to disk */
+
+ file->checksum = regf_block_checksum( &ps );
+ prs_set_offset( &ps, 0 );
+ if ( !prs_regf_block( "regf_header", &ps, 0, file ) ) {
+ result = False;
+ goto out;
+ }
+
+ if ( write_block( file, &ps, 0 ) == -1 ) {
+ DEBUG(0,("init_regf_block: Failed to initialize registry header block!\n"));
+ result = False;
+ goto out;
+ }
+
+out:
+ prs_mem_free( &ps );
+
+ return result;
+}
+/*******************************************************************
+ Open the registry file and then read in the REGF block to get the
+ first hbin offset.
+*******************************************************************/
+
+ REGF_FILE* regfio_open( const char *filename, int flags, int mode )
+{
+ REGF_FILE *rb;
+
+ if ( !(rb = SMB_MALLOC_P(REGF_FILE)) ) {
+ DEBUG(0,("ERROR allocating memory\n"));
+ return NULL;
+ }
+ ZERO_STRUCTP( rb );
+ rb->fd = -1;
+ rb->ignore_checksums = false;
+
+ if ( !(rb->mem_ctx = talloc_init( "regfio_open" )) ) {
+ regfio_close( rb );
+ return NULL;
+ }
+
+ rb->open_flags = flags;
+
+ /* open and existing file */
+
+ if ( (rb->fd = open(filename, flags, mode)) == -1 ) {
+ DEBUG(0,("regfio_open: failure to open %s (%s)\n", filename, strerror(errno)));
+ regfio_close( rb );
+ return NULL;
+ }
+
+ /* check if we are creating a new file or overwriting an existing one */
+
+ if ( flags & (O_CREAT|O_TRUNC) ) {
+ if ( !init_regf_block( rb ) ) {
+ DEBUG(0,("regfio_open: Failed to read initial REGF block\n"));
+ regfio_close( rb );
+ return NULL;
+ }
+
+ /* success */
+ return rb;
+ }
+
+ /* read in an existing file */
+
+ if ( !read_regf_block( rb ) ) {
+ DEBUG(0,("regfio_open: Failed to read initial REGF block\n"));
+ regfio_close( rb );
+ return NULL;
+ }
+
+ /* success */
+
+ return rb;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static void regfio_mem_free( REGF_FILE *file )
+{
+ /* free any talloc()'d memory */
+
+ if ( file && file->mem_ctx )
+ talloc_destroy( file->mem_ctx );
+}
+
+/*******************************************************************
+*******************************************************************/
+
+ int regfio_close( REGF_FILE *file )
+{
+ int fd;
+
+ /* cleanup for a file opened for write */
+
+ if ((file->fd != -1) && (file->open_flags & (O_WRONLY|O_RDWR))) {
+ prs_struct ps;
+ REGF_SK_REC *sk;
+
+ /* write of sd list */
+
+ for ( sk=file->sec_desc_list; sk; sk=sk->next ) {
+ hbin_prs_sk_rec( "sk_rec", sk->hbin, 0, sk );
+ }
+
+ /* flush any dirty blocks */
+
+ while ( file->block_list ) {
+ hbin_block_close( file, file->block_list );
+ }
+
+ ZERO_STRUCT( ps );
+
+ unix_to_nt_time( &file->mtime, time(NULL) );
+
+ if ( read_block( file, &ps, 0, REGF_BLOCKSIZE ) != -1 ) {
+ /* now use for writing */
+ prs_switch_type( &ps, MARSHALL );
+
+ /* stream the block once, generate the checksum,
+ and stream it again */
+ prs_set_offset( &ps, 0 );
+ prs_regf_block( "regf_blocK", &ps, 0, file );
+ file->checksum = regf_block_checksum( &ps );
+ prs_set_offset( &ps, 0 );
+ prs_regf_block( "regf_blocK", &ps, 0, file );
+
+ /* now we are ready to write it to disk */
+ if ( write_block( file, &ps, 0 ) == -1 )
+ DEBUG(0,("regfio_close: failed to update the regf header block!\n"));
+ }
+
+ prs_mem_free( &ps );
+ }
+
+ regfio_mem_free( file );
+
+ /* nothing tdo do if there is no open file */
+
+ if (file->fd == -1)
+ return 0;
+
+ fd = file->fd;
+ file->fd = -1;
+ SAFE_FREE( file );
+
+ return close( fd );
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static void regfio_flush( REGF_FILE *file )
+{
+ REGF_HBIN *hbin;
+
+ for ( hbin=file->block_list; hbin; hbin=hbin->next ) {
+ write_hbin_block( file, hbin );
+ }
+}
+
+/*******************************************************************
+ There should be only *one* root key in the registry file based
+ on my experience. --jerry
+*******************************************************************/
+
+REGF_NK_REC* regfio_rootkey( REGF_FILE *file )
+{
+ REGF_NK_REC *nk;
+ REGF_HBIN *hbin;
+ uint32_t offset = REGF_BLOCKSIZE;
+ bool found = False;
+ bool eob;
+
+ if ( !file )
+ return NULL;
+
+ if ( !(nk = talloc_zero( file->mem_ctx, REGF_NK_REC )) ) {
+ DEBUG(0,("regfio_rootkey: talloc() failed!\n"));
+ return NULL;
+ }
+
+ /* scan through the file on HBIN block at a time looking
+ for an NK record with a type == 0x002c.
+ Normally this is the first nk record in the first hbin
+ block (but I'm not assuming that for now) */
+
+ while ( (hbin = read_hbin_block( file, offset )) ) {
+ eob = False;
+
+ while ( !eob) {
+ if ( next_nk_record( file, hbin, nk, &eob ) ) {
+ if ( nk->key_type == NK_TYPE_ROOTKEY ) {
+ found = True;
+ break;
+ }
+ }
+ prs_mem_free( &hbin->ps );
+ }
+
+ if ( found )
+ break;
+
+ offset += hbin->block_size;
+ }
+
+ if ( !found ) {
+ DEBUG(0,("regfio_rootkey: corrupt registry file ? No root key record located\n"));
+ return NULL;
+ }
+
+ DLIST_ADD( file->block_list, hbin );
+
+ return nk;
+}
+
+/*******************************************************************
+ This acts as an iterator over the subkeys defined for a given
+ NK record. Remember that offsets are from the *first* HBIN block.
+*******************************************************************/
+
+ REGF_NK_REC* regfio_fetch_subkey( REGF_FILE *file, REGF_NK_REC *nk )
+{
+ REGF_NK_REC *subkey;
+ REGF_HBIN *hbin;
+ uint32_t nk_offset;
+
+ /* see if there is anything left to report */
+
+ if (nk == NULL ||
+ nk->subkeys.hashes == NULL ||
+ nk->subkey_index >= nk->subkeys.num_keys ||
+ (nk->subkeys_off == REGF_OFFSET_NONE) ||
+ (nk->subkey_index >= nk->num_subkeys)) {
+ return NULL;
+ }
+
+ /* find the HBIN block which should contain the nk record */
+
+ hbin = lookup_hbin_block(file,
+ nk->subkeys.hashes[nk->subkey_index].nk_off);
+ if (hbin == NULL) {
+ DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing offset [0x%x]\n",
+ nk->subkeys.hashes[nk->subkey_index].nk_off));
+ return NULL;
+ }
+
+ nk_offset = nk->subkeys.hashes[nk->subkey_index].nk_off;
+ if ( !prs_set_offset( &hbin->ps, (HBIN_HDR_SIZE + nk_offset - hbin->first_hbin_off) ) )
+ return NULL;
+
+ nk->subkey_index++;
+ if ( !(subkey = talloc_zero( file->mem_ctx, REGF_NK_REC )) )
+ return NULL;
+
+ if ( !hbin_prs_key( file, hbin, subkey ) )
+ return NULL;
+
+ return subkey;
+}
+
+
+/*******************************************************************
+*******************************************************************/
+
+static REGF_HBIN* regf_hbin_allocate( REGF_FILE *file, uint32_t block_size )
+{
+ REGF_HBIN *hbin;
+ SMB_STRUCT_STAT sbuf;
+
+ if ( !(hbin = talloc_zero( file->mem_ctx, REGF_HBIN )) )
+ return NULL;
+
+ memcpy( hbin->header, "hbin", HBIN_HDR_SIZE);
+
+
+ if (sys_fstat(file->fd, &sbuf, false)) {
+ DEBUG(0,("regf_hbin_allocate: stat() failed! (%s)\n", strerror(errno)));
+ return NULL;
+ }
+
+ hbin->file_off = sbuf.st_ex_size;
+
+ hbin->free_off = HBIN_HEADER_REC_SIZE;
+ hbin->free_size = block_size - hbin->free_off + sizeof(uint32_t);
+
+ hbin->block_size = block_size;
+ hbin->first_hbin_off = hbin->file_off - REGF_BLOCKSIZE;
+
+ if ( !prs_init( &hbin->ps, block_size, file->mem_ctx, MARSHALL ) )
+ return NULL;
+
+ if ( !prs_hbin_block( "new_hbin", &hbin->ps, 0, hbin ) )
+ return NULL;
+
+ if ( !write_hbin_block( file, hbin ) )
+ return NULL;
+
+ file->last_block = hbin->file_off;
+
+ return hbin;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static void update_free_space( REGF_HBIN *hbin, uint32_t size_used )
+{
+ hbin->free_off += size_used;
+ hbin->free_size -= size_used;
+
+ if ( hbin->free_off >= hbin->block_size ) {
+ hbin->free_off = REGF_OFFSET_NONE;
+ }
+
+ return;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static REGF_HBIN* find_free_space( REGF_FILE *file, uint32_t size )
+{
+ REGF_HBIN *hbin, *p_hbin;
+ uint32_t block_off;
+ bool cached;
+
+ /* check open block list */
+
+ for ( hbin=file->block_list; hbin!=NULL; hbin=hbin->next ) {
+ /* only check blocks that actually have available space */
+
+ if ( hbin->free_off == REGF_OFFSET_NONE )
+ continue;
+
+ /* check for a large enough available chunk */
+
+ if ( (hbin->block_size - hbin->free_off) >= size ) {
+ DLIST_PROMOTE( file->block_list, hbin );
+ goto done;
+ }
+ }
+
+ /* parse the file until we find a block with
+ enough free space; save the last non-filled hbin */
+
+ block_off = REGF_BLOCKSIZE;
+ do {
+ /* cleanup before the next round */
+ cached = False;
+ if ( hbin )
+ prs_mem_free( &hbin->ps );
+
+ hbin = read_hbin_block( file, block_off );
+
+ if ( hbin ) {
+
+ /* make sure that we don't already have this block in memory */
+
+ for ( p_hbin=file->block_list; p_hbin!=NULL; p_hbin=p_hbin->next ) {
+ if ( p_hbin->file_off == hbin->file_off ) {
+ cached = True;
+ break;
+ }
+ }
+
+ block_off = hbin->file_off + hbin->block_size;
+
+ if ( cached ) {
+ prs_mem_free( &hbin->ps );
+ hbin = NULL;
+ continue;
+ }
+ }
+ /* if (cached block or (new block and not enough free space)) then continue looping */
+ } while ( cached || (hbin && (hbin->free_size < size)) );
+
+ /* no free space; allocate a new one */
+
+ if ( !hbin ) {
+ uint32_t alloc_size;
+
+ /* allocate in multiples of REGF_ALLOC_BLOCK; make sure (size + hbin_header) fits */
+
+ alloc_size = (((size+HBIN_HEADER_REC_SIZE) / REGF_ALLOC_BLOCK ) + 1 ) * REGF_ALLOC_BLOCK;
+
+ if ( !(hbin = regf_hbin_allocate( file, alloc_size )) ) {
+ DEBUG(0,("find_free_space: regf_hbin_allocate() failed!\n"));
+ return NULL;
+ }
+ DLIST_ADD( file->block_list, hbin );
+ }
+
+done:
+ /* set the offset to be ready to write */
+
+ if ( !prs_set_offset( &hbin->ps, hbin->free_off-sizeof(uint32_t) ) )
+ return NULL;
+
+ /* write the record size as a placeholder for now, it should be
+ probably updated by the caller once it all of the data necessary
+ for the record */
+
+ if ( !prs_uint32("allocated_size", &hbin->ps, 0, &size) )
+ return NULL;
+
+ update_free_space( hbin, size );
+
+ return hbin;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static uint32_t sk_record_data_size( struct security_descriptor * sd )
+{
+ uint32_t size, size_mod8;
+
+ size_mod8 = 0;
+
+ /* the record size is sizeof(hdr) + name + static members + data_size_field */
+
+ size = sizeof(uint32_t)*5 + ndr_size_security_descriptor(sd, 0) + sizeof(uint32_t);
+
+ /* multiple of 8 */
+ size_mod8 = size & 0xfffffff8;
+ if ( size_mod8 < size )
+ size_mod8 += 8;
+
+ return size_mod8;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static uint32_t vk_record_data_size( REGF_VK_REC *vk )
+{
+ uint32_t size, size_mod8;
+
+ size_mod8 = 0;
+
+ /* the record size is sizeof(hdr) + name + static members + data_size_field */
+
+ size = REC_HDR_SIZE + (sizeof(uint16_t)*3) + (sizeof(uint32_t)*3) + sizeof(uint32_t);
+
+ if ( vk->valuename )
+ size += strlen(vk->valuename);
+
+ /* multiple of 8 */
+ size_mod8 = size & 0xfffffff8;
+ if ( size_mod8 < size )
+ size_mod8 += 8;
+
+ return size_mod8;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static uint32_t lf_record_data_size( uint32_t num_keys )
+{
+ uint32_t size, size_mod8;
+
+ size_mod8 = 0;
+
+ /* the record size is sizeof(hdr) + num_keys + sizeof of hash_array + data_size_uint32_t */
+
+ size = REC_HDR_SIZE + sizeof(uint16_t) + (sizeof(REGF_HASH_REC) * num_keys) + sizeof(uint32_t);
+
+ /* multiple of 8 */
+ size_mod8 = size & 0xfffffff8;
+ if ( size_mod8 < size )
+ size_mod8 += 8;
+
+ return size_mod8;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static uint32_t nk_record_data_size( REGF_NK_REC *nk )
+{
+ uint32_t size, size_mod8;
+
+ size_mod8 = 0;
+
+ /* the record size is static + length_of_keyname + length_of_classname + data_size_uint32_t */
+
+ size = 0x4c + strlen(nk->keyname) + sizeof(uint32_t);
+
+ if ( nk->classname )
+ size += strlen( nk->classname );
+
+ /* multiple of 8 */
+ size_mod8 = size & 0xfffffff8;
+ if ( size_mod8 < size )
+ size_mod8 += 8;
+
+ return size_mod8;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static bool create_vk_record(REGF_FILE *file, REGF_VK_REC *vk,
+ struct regval_blob *value)
+{
+ char *name = regval_name(value);
+ REGF_HBIN *data_hbin;
+
+ ZERO_STRUCTP( vk );
+
+ memcpy( vk->header, "vk", REC_HDR_SIZE );
+
+ if ( name ) {
+ vk->valuename = talloc_strdup( file->mem_ctx, regval_name(value) );
+ vk->flag = VK_FLAG_NAME_PRESENT;
+ }
+
+ vk->data_size = regval_size( value );
+ vk->type = regval_type( value );
+
+ if ( vk->data_size > sizeof(uint32_t) ) {
+ uint32_t data_size = ( (vk->data_size+sizeof(uint32_t)) & 0xfffffff8 ) + 8;
+
+ vk->data = (uint8_t *)talloc_memdup( file->mem_ctx,
+ regval_data_p(value),
+ vk->data_size );
+ if (vk->data == NULL) {
+ return False;
+ }
+
+ /* go ahead and store the offset....we'll pick this hbin block back up when
+ we stream the data */
+
+ if ((data_hbin = find_free_space(file, data_size )) == NULL) {
+ return False;
+ }
+ vk->data_off = prs_offset( &data_hbin->ps ) + data_hbin->first_hbin_off - HBIN_HDR_SIZE;
+ }
+ else {
+ /* make sure we don't try to copy from a NULL value pointer */
+
+ if ( vk->data_size != 0 )
+ memcpy( &vk->data_off, regval_data_p(value), vk->data_size);
+ vk->data_size |= VK_DATA_IN_OFFSET;
+ }
+
+ return True;
+}
+
+/*******************************************************************
+*******************************************************************/
+
+static int hashrec_cmp( REGF_HASH_REC *h1, REGF_HASH_REC *h2 )
+{
+ return strcasecmp_m( h1->fullname, h2->fullname );
+}
+
+/*******************************************************************
+*******************************************************************/
+
+ REGF_NK_REC* regfio_write_key( REGF_FILE *file, const char *name,
+ struct regval_ctr *values, struct regsubkey_ctr *subkeys,
+ struct security_descriptor *sec_desc, REGF_NK_REC *parent )
+{
+ REGF_NK_REC *nk;
+ REGF_HBIN *vlist_hbin = NULL;
+ uint32_t size;
+
+ if ( !(nk = talloc_zero( file->mem_ctx, REGF_NK_REC )) )
+ return NULL;
+
+ memcpy( nk->header, "nk", REC_HDR_SIZE );
+
+ if ( !parent )
+ nk->key_type = NK_TYPE_ROOTKEY;
+ else
+ nk->key_type = NK_TYPE_NORMALKEY;
+
+ /* store the parent offset (or -1 if a the root key */
+
+ nk->parent_off = parent ? (parent->hbin_off + parent->hbin->file_off - REGF_BLOCKSIZE - HBIN_HDR_SIZE ) : REGF_OFFSET_NONE;
+
+ /* no classname currently */
+
+ nk->classname_off = REGF_OFFSET_NONE;
+ nk->classname = NULL;
+ nk->keyname = talloc_strdup( file->mem_ctx, name );
+
+ /* current modification time */
+
+ unix_to_nt_time( &nk->mtime, time(NULL) );
+
+ /* allocate the record on disk */
+
+ size = nk_record_data_size( nk );
+ nk->rec_size = ( size - 1 ) ^ 0XFFFFFFFF;
+ if ((nk->hbin = find_free_space( file, size )) == NULL) {
+ return NULL;
+ }
+ nk->hbin_off = prs_offset( &nk->hbin->ps );
+
+ /* Update the hash record in the parent */
+
+ if ( parent ) {
+ REGF_HASH_REC *hash = &parent->subkeys.hashes[parent->subkey_index];
+
+ hash->nk_off = prs_offset( &nk->hbin->ps ) + nk->hbin->first_hbin_off - HBIN_HDR_SIZE;
+ memcpy(hash->keycheck, name, MIN(strlen(name),sizeof(uint32_t)));
+ hash->fullname = talloc_strdup( file->mem_ctx, name );
+ parent->subkey_index++;
+
+ /* sort the list by keyname */
+ TYPESAFE_QSORT(parent->subkeys.hashes, parent->subkey_index, hashrec_cmp);
+
+ if ( !hbin_prs_lf_records( "lf_rec", parent->subkeys.hbin, 0, parent ) )
+ return NULL;
+ }
+
+ /* write the security descriptor */
+
+ nk->sk_off = REGF_OFFSET_NONE;
+ if ( sec_desc ) {
+ uint32_t sk_size = sk_record_data_size( sec_desc );
+ REGF_HBIN *sk_hbin;
+
+ /* search for it in the existing list of sd's */
+
+ if ( (nk->sec_desc = find_sk_record_by_sec_desc( file, sec_desc )) == NULL ) {
+ /* not found so add it to the list */
+
+ if (!(sk_hbin = find_free_space( file, sk_size ))) {
+ return NULL;
+ }
+
+ if ( !(nk->sec_desc = talloc_zero( file->mem_ctx, REGF_SK_REC )) )
+ return NULL;
+
+ /* now we have to store the security descriptor in the list and
+ update the offsets */
+
+ memcpy( nk->sec_desc->header, "sk", REC_HDR_SIZE );
+ nk->sec_desc->hbin = sk_hbin;
+ nk->sec_desc->hbin_off = prs_offset( &sk_hbin->ps );
+ nk->sec_desc->sk_off = prs_offset( &sk_hbin->ps ) + sk_hbin->first_hbin_off - HBIN_HDR_SIZE;
+ nk->sec_desc->rec_size = (sk_size-1) ^ 0xFFFFFFFF;
+
+ nk->sec_desc->sec_desc = sec_desc;
+ nk->sec_desc->ref_count = 0;
+
+ /* size value must be self-inclusive */
+ nk->sec_desc->size = ndr_size_security_descriptor(sec_desc, 0)
+ + sizeof(uint32_t);
+
+ DLIST_ADD_END( file->sec_desc_list, nk->sec_desc);
+
+ /* update the offsets for us and the previous sd in the list.
+ if this is the first record, then just set the next and prev
+ offsets to ourself. */
+
+ if ( DLIST_PREV(nk->sec_desc) ) {
+ REGF_SK_REC *prev = DLIST_PREV(nk->sec_desc);
+
+ nk->sec_desc->prev_sk_off = prev->hbin_off + prev->hbin->first_hbin_off - HBIN_HDR_SIZE;
+ prev->next_sk_off = nk->sec_desc->sk_off;
+
+ /* the end must loop around to the front */
+ nk->sec_desc->next_sk_off = file->sec_desc_list->sk_off;
+
+ /* and first must loop around to the tail */
+ file->sec_desc_list->prev_sk_off = nk->sec_desc->sk_off;
+ } else {
+ nk->sec_desc->prev_sk_off = nk->sec_desc->sk_off;
+ nk->sec_desc->next_sk_off = nk->sec_desc->sk_off;
+ }
+ }
+
+ /* bump the reference count +1 */
+
+ nk->sk_off = nk->sec_desc->sk_off;
+ nk->sec_desc->ref_count++;
+ }
+
+ /* write the subkeys */
+
+ nk->subkeys_off = REGF_OFFSET_NONE;
+ if ( (nk->num_subkeys = regsubkey_ctr_numkeys( subkeys )) != 0 ) {
+ uint32_t lf_size = lf_record_data_size( nk->num_subkeys );
+ uint32_t namelen;
+ int i;
+
+ if (!(nk->subkeys.hbin = find_free_space( file, lf_size ))) {
+ return NULL;
+ }
+ nk->subkeys.hbin_off = prs_offset( &nk->subkeys.hbin->ps );
+ nk->subkeys.rec_size = (lf_size-1) ^ 0xFFFFFFFF;
+ nk->subkeys_off = prs_offset( &nk->subkeys.hbin->ps ) + nk->subkeys.hbin->first_hbin_off - HBIN_HDR_SIZE;
+
+ memcpy( nk->subkeys.header, "lf", REC_HDR_SIZE );
+
+ nk->subkeys.num_keys = nk->num_subkeys;
+ if (nk->subkeys.num_keys) {
+ if ( !(nk->subkeys.hashes = talloc_zero_array( file->mem_ctx, REGF_HASH_REC, nk->subkeys.num_keys )) )
+ return NULL;
+ } else {
+ nk->subkeys.hashes = NULL;
+ }
+ nk->subkey_index = 0;
+
+ /* update the max_bytes_subkey{name,classname} fields */
+ for ( i=0; i<nk->num_subkeys; i++ ) {
+ namelen = strlen( regsubkey_ctr_specific_key(subkeys, i) );
+ if ( namelen*2 > nk->max_bytes_subkeyname )
+ nk->max_bytes_subkeyname = namelen * 2;
+ }
+ }
+
+ /* write the values */
+
+ nk->values_off = REGF_OFFSET_NONE;
+ if ( (nk->num_values = regval_ctr_numvals( values )) != 0 ) {
+ uint32_t vlist_size = ( ( nk->num_values * sizeof(uint32_t) ) & 0xfffffff8 ) + 8;
+ int i;
+
+ if (!(vlist_hbin = find_free_space( file, vlist_size ))) {
+ return NULL;
+ }
+ nk->values_off = prs_offset( &vlist_hbin->ps ) + vlist_hbin->first_hbin_off - HBIN_HDR_SIZE;
+
+ if (nk->num_values) {
+ if ( !(nk->values = talloc_array( file->mem_ctx, REGF_VK_REC, nk->num_values )) )
+ return NULL;
+ } else {
+ nk->values = NULL;
+ }
+
+ /* create the vk records */
+
+ for ( i=0; i<nk->num_values; i++ ) {
+ uint32_t vk_size, namelen, datalen;
+ struct regval_blob *r;
+
+ r = regval_ctr_specific_value( values, i );
+ create_vk_record( file, &nk->values[i], r );
+ vk_size = vk_record_data_size( &nk->values[i] );
+ nk->values[i].hbin = find_free_space( file, vk_size );
+ nk->values[i].hbin_off = prs_offset( &nk->values[i].hbin->ps );
+ nk->values[i].rec_size = ( vk_size - 1 ) ^ 0xFFFFFFFF;
+ nk->values[i].rec_off = prs_offset( &nk->values[i].hbin->ps )
+ + nk->values[i].hbin->first_hbin_off
+ - HBIN_HDR_SIZE;
+
+ /* update the max bytes fields if necessary */
+
+ namelen = strlen( regval_name(r) );
+ if ( namelen*2 > nk->max_bytes_valuename )
+ nk->max_bytes_valuename = namelen * 2;
+
+ datalen = regval_size( r );
+ if ( datalen > nk->max_bytes_value )
+ nk->max_bytes_value = datalen;
+ }
+ }
+
+ /* stream the records */
+
+ prs_set_offset( &nk->hbin->ps, nk->hbin_off );
+ if ( !prs_nk_rec( "nk_rec", &nk->hbin->ps, 0, nk ) )
+ return NULL;
+
+ if ( nk->num_values ) {
+ if ( !hbin_prs_vk_records( "vk_records", vlist_hbin, 0, nk, file ) )
+ return NULL;
+ }
+
+
+ regfio_flush( file );
+
+ return nk;
+}
diff --git a/source3/registry/regfio.h b/source3/registry/regfio.h
new file mode 100644
index 0000000..fc77970
--- /dev/null
+++ b/source3/registry/regfio.h
@@ -0,0 +1,233 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Windows NT registry I/O library
+ * Copyright (c) Gerald (Jerry) Carter 2005
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/************************************************************
+ * Most of this information was obtained from
+ * http://www.wednesday.demon.co.uk/dosreg.html
+ * Thanks Nigel!
+ ***********************************************************/
+
+#include "registry/reg_parse_prs.h"
+#include "registry/reg_objects.h"
+
+#ifndef _REGFIO_H
+#define _REGFIO_H
+
+struct regsubkey_ctr;
+
+/* Macros */
+
+#define REGF_BLOCKSIZE 0x1000
+#define REGF_ALLOC_BLOCK 0x1000
+
+/* header sizes for various records */
+
+#define REGF_HDR_SIZE 4
+#define HBIN_HDR_SIZE 4
+#define HBIN_HEADER_REC_SIZE 0x24
+#define REC_HDR_SIZE 2
+
+#define REGF_OFFSET_NONE 0xffffffff
+
+/* Flags for the vk records */
+
+#define VK_FLAG_NAME_PRESENT 0x0001
+#define VK_DATA_IN_OFFSET 0x80000000
+
+/* NK record macros */
+
+#define NK_TYPE_LINKKEY 0x0010
+#define NK_TYPE_NORMALKEY 0x0020
+#define NK_TYPE_ROOTKEY 0x002c
+
+#define HBIN_STORE_REF(x, y) { x->hbin = y; y->ref_count++ };
+#define HBIN_REMOVE_REF(x, y) { x->hbin = NULL; y->ref_count-- /* if the count == 0; we can clean up */ };
+
+
+/* HBIN block */
+struct regf_hbin;
+typedef struct regf_hbin {
+ struct regf_hbin *prev, *next;
+ uint32_t file_off; /* my offset in the registry file */
+ uint32_t free_off; /* offset to free space within the hbin record */
+ uint32_t free_size; /* amount of data left in the block */
+ int ref_count; /* how many active records are pointing to this block (not used currently) */
+
+ char header[HBIN_HDR_SIZE]; /* "hbin" */
+ uint32_t first_hbin_off; /* offset from first hbin block */
+ uint32_t block_size; /* block size of this blockually a multiple of 4096Kb) */
+
+ prs_struct ps; /* data */
+
+ bool dirty; /* has this hbin block been modified? */
+} REGF_HBIN;
+
+/* ??? List -- list of key offsets and hashed names for consistency */
+
+typedef struct {
+ uint32_t nk_off;
+ uint8_t keycheck[sizeof(uint32_t)];
+ char *fullname;
+} REGF_HASH_REC;
+
+typedef struct {
+ REGF_HBIN *hbin; /* pointer to HBIN record (in memory) containing this nk record */
+ uint32_t hbin_off; /* offset from beginning of this hbin block */
+ uint32_t rec_size; /* ((start_offset - end_offset) & 0xfffffff8) */
+
+ char header[REC_HDR_SIZE];
+ uint16_t num_keys;
+ REGF_HASH_REC *hashes;
+} REGF_LF_REC;
+
+/* Key Value */
+
+typedef struct {
+ REGF_HBIN *hbin; /* pointer to HBIN record (in memory) containing this nk record */
+ uint32_t hbin_off; /* offset from beginning of this hbin block */
+ uint32_t rec_size; /* ((start_offset - end_offset) & 0xfffffff8) */
+ uint32_t rec_off; /* offset stored in the value list */
+
+ char header[REC_HDR_SIZE];
+ char *valuename;
+ uint32_t data_size;
+ uint32_t data_off;
+ uint8_t *data;
+ uint32_t type;
+ uint16_t flag;
+} REGF_VK_REC;
+
+
+/* Key Security */
+struct _regf_sk_rec;
+
+typedef struct _regf_sk_rec {
+ struct _regf_sk_rec *next, *prev;
+ REGF_HBIN *hbin; /* pointer to HBIN record (in memory) containing this nk record */
+ uint32_t hbin_off; /* offset from beginning of this hbin block */
+ uint32_t rec_size; /* ((start_offset - end_offset) & 0xfffffff8) */
+
+ uint32_t sk_off; /* offset parsed from NK record used as a key
+ to lookup reference to this SK record */
+
+ char header[REC_HDR_SIZE];
+ uint32_t prev_sk_off;
+ uint32_t next_sk_off;
+ uint32_t ref_count;
+ uint32_t size;
+ struct security_descriptor *sec_desc;
+} REGF_SK_REC;
+
+/* Key Name */
+
+typedef struct {
+ REGF_HBIN *hbin; /* pointer to HBIN record (in memory) containing this nk record */
+ uint32_t hbin_off; /* offset from beginning of this hbin block */
+ uint32_t subkey_index; /* index to next subkey record to return */
+ uint32_t rec_size; /* ((start_offset - end_offset) & 0xfffffff8) */
+
+ /* header information */
+
+ char header[REC_HDR_SIZE];
+ uint16_t key_type;
+ NTTIME mtime;
+ uint32_t parent_off; /* back pointer in registry hive */
+ uint32_t classname_off;
+ char *classname;
+ char *keyname;
+
+ /* max lengths */
+
+ uint32_t max_bytes_subkeyname; /* max subkey name * 2 */
+ uint32_t max_bytes_subkeyclassname; /* max subkey classname length (as if) */
+ uint32_t max_bytes_valuename; /* max valuename * 2 */
+ uint32_t max_bytes_value; /* max value data size */
+
+ /* unknowns */
+
+ uint32_t unk_index; /* nigel says run time index ? */
+
+ /* children */
+
+ uint32_t num_subkeys;
+ uint32_t subkeys_off; /* hash records that point to NK records */
+ uint32_t num_values;
+ uint32_t values_off; /* value lists which point to VK records */
+ uint32_t sk_off; /* offset to SK record */
+
+ /* link in the other records here */
+
+ REGF_LF_REC subkeys;
+ REGF_VK_REC *values;
+ REGF_SK_REC *sec_desc;
+
+} REGF_NK_REC;
+
+/* REGF block */
+
+typedef struct {
+ /* run time information */
+
+ int fd; /* file descriptor */
+ int open_flags; /* flags passed to the open() call */
+ TALLOC_CTX *mem_ctx; /* memory context for run-time file access information */
+ REGF_HBIN *block_list; /* list of open hbin blocks */
+
+ /* file format information */
+
+ char header[REGF_HDR_SIZE]; /* "regf" */
+ uint32_t data_offset; /* offset to record in the first (or any?) hbin block */
+ uint32_t last_block; /* offset to last hbin block in file */
+ uint32_t checksum; /* XOR of bytes 0x0000 - 0x01FB */
+ NTTIME mtime;
+
+ REGF_SK_REC *sec_desc_list; /* list of security descriptors referenced by NK records */
+
+ /* Ignore checksums in input data. Used by fuzzing code to allow more
+ * coverage without having to calculate a valid checksum. The checksums
+ * are merely to detect data corruption and don't provide a security
+ * value.
+ */
+ bool ignore_checksums;
+
+ /* unknowns used to simply writing */
+
+ uint32_t unknown1;
+ uint32_t unknown2;
+ uint32_t unknown3;
+ uint32_t unknown4;
+ uint32_t unknown5;
+ uint32_t unknown6;
+
+} REGF_FILE;
+
+/* Function Declarations */
+
+REGF_FILE* regfio_open( const char *filename, int flags, int mode );
+int regfio_close( REGF_FILE *r );
+
+REGF_NK_REC* regfio_rootkey( REGF_FILE *file );
+REGF_NK_REC* regfio_fetch_subkey( REGF_FILE *file, REGF_NK_REC *nk );
+REGF_NK_REC* regfio_write_key ( REGF_FILE *file, const char *name,
+ struct regval_ctr *values, struct regsubkey_ctr *subkeys,
+ struct security_descriptor *sec_desc, REGF_NK_REC *parent );
+
+
+#endif /* _REGFIO_H */
+
diff --git a/source3/registry/tests/test_regfio.c b/source3/registry/tests/test_regfio.c
new file mode 100644
index 0000000..e835e65
--- /dev/null
+++ b/source3/registry/tests/test_regfio.c
@@ -0,0 +1,186 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Copyright (C) 2019 Michael Hanselmann <public@hansmi.ch>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdint.h>
+#include <cmocka.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "includes.h"
+#include "lib/replace/replace.h"
+#include "system/filesys.h"
+#include "lib/util/samba_util.h"
+#include "registry/regfio.h"
+
+struct test_ctx {
+ char *tmp_regfile;
+ int tmp_regfile_fd;
+ REGF_FILE *rb;
+};
+
+static int setup_context(void **state)
+{
+ struct test_ctx *test_ctx;
+
+ test_ctx = talloc_zero(NULL, struct test_ctx);
+ assert_non_null(test_ctx);
+
+ test_ctx->tmp_regfile_fd = -1;
+
+ *state = test_ctx;
+
+ return 0;
+}
+
+static int setup_context_tempfile(void **state)
+{
+ struct test_ctx *test_ctx;
+ int ret;
+
+ ret = setup_context(state);
+
+ if (ret == 0) {
+ test_ctx = talloc_get_type_abort(*state, struct test_ctx);
+
+ test_ctx->tmp_regfile = talloc_strdup(test_ctx, "/tmp/regfio.XXXXXX");
+ assert_non_null(test_ctx->tmp_regfile);
+
+ test_ctx->tmp_regfile_fd = mkstemp(test_ctx->tmp_regfile);
+ assert_return_code(test_ctx->tmp_regfile_fd, errno);
+ }
+
+ return ret;
+}
+
+static int teardown_context(void **state)
+{
+ struct test_ctx *test_ctx =
+ talloc_get_type_abort(*state, struct test_ctx);
+
+ if (test_ctx->rb) {
+ regfio_close(test_ctx->rb);
+ }
+
+ if (test_ctx->tmp_regfile) {
+ unlink(test_ctx->tmp_regfile);
+ }
+
+ if (test_ctx->tmp_regfile_fd != -1) {
+ close(test_ctx->tmp_regfile_fd);
+ }
+
+ talloc_free(test_ctx);
+
+ return 0;
+}
+
+static void open_testfile(struct test_ctx *test_ctx, const char *filename)
+{
+ char *path;
+
+ path = talloc_asprintf(test_ctx, "%s/testdata/samba3/%s", SRCDIR, filename);
+ assert_non_null(path);
+
+ test_ctx->rb = regfio_open(path, O_RDONLY, 0600);
+ assert_non_null(test_ctx->rb);
+}
+
+static void test_regfio_open_new_file(void **state)
+{
+ struct test_ctx *test_ctx =
+ talloc_get_type_abort(*state, struct test_ctx);
+ REGF_NK_REC *root;
+ struct regval_ctr *values;
+ struct regsubkey_ctr *subkeys;
+ WERROR werr;
+
+ test_ctx->rb = regfio_open(test_ctx->tmp_regfile,
+ O_RDWR | O_CREAT | O_TRUNC, 0600);
+ assert_non_null(test_ctx->rb);
+
+ root = regfio_rootkey(test_ctx->rb);
+ assert_null(root);
+
+ werr = regsubkey_ctr_init(NULL, &subkeys);
+ assert_true(W_ERROR_IS_OK(werr));
+
+ werr = regval_ctr_init(subkeys, &values);
+ assert_true(W_ERROR_IS_OK(werr));
+
+ /* Write root key */
+ regfio_write_key(test_ctx->rb, "", values, subkeys, NULL, NULL);
+
+ root = regfio_rootkey(test_ctx->rb);
+ assert_non_null(root);
+ assert_memory_equal(root->header, "nk", sizeof(root->header));
+ assert_int_equal(root->key_type, NK_TYPE_ROOTKEY);
+}
+
+static void test_regfio_corrupt_hbin(void **state)
+{
+ struct test_ctx *test_ctx =
+ talloc_get_type_abort(*state, struct test_ctx);
+ REGF_NK_REC *root;
+
+ open_testfile(test_ctx, "regfio_corrupt_hbin1.dat");
+
+ root = regfio_rootkey(test_ctx->rb);
+ assert_null(root);
+}
+
+static void test_regfio_corrupt_lf_subkeys(void **state)
+{
+ struct test_ctx *test_ctx =
+ talloc_get_type_abort(*state, struct test_ctx);
+ REGF_NK_REC *root, *subkey;
+
+ open_testfile(test_ctx, "regfio_corrupt_lf_subkeys.dat");
+
+ root = regfio_rootkey(test_ctx->rb);
+ assert_non_null(root);
+
+ root->subkey_index = 0;
+ while ((subkey = regfio_fetch_subkey(test_ctx->rb, root))) {
+ }
+}
+
+int main(void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(test_regfio_open_new_file,
+ setup_context_tempfile,
+ teardown_context),
+ cmocka_unit_test_setup_teardown(test_regfio_corrupt_hbin,
+ setup_context,
+ teardown_context),
+ cmocka_unit_test_setup_teardown(test_regfio_corrupt_lf_subkeys,
+ setup_context,
+ teardown_context),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}