summaryrefslogtreecommitdiff
path: root/source4/lib/registry/reg_backend_w95.c
diff options
context:
space:
mode:
authorJelmer Vernooij <jelmer@samba.org>2004-12-10 20:07:04 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 13:06:29 -0500
commit444a86792471c0bef33dde15c7a4a33e16a951b4 (patch)
tree075b0cdd27acb80470a4e90e99598cbcea3b082c /source4/lib/registry/reg_backend_w95.c
parent60c69445f6fa86b27bd697cc8af52b582f620725 (diff)
downloadsamba-444a86792471c0bef33dde15c7a4a33e16a951b4.tar.gz
samba-444a86792471c0bef33dde15c7a4a33e16a951b4.tar.bz2
samba-444a86792471c0bef33dde15c7a4a33e16a951b4.zip
r4132: - Bunch of rather large fixes in the registry
- Added some README files Not everything works yet, e.g. the EnumValue test appears to be broken. (This used to be commit c169e86c1f52763b83e77e509f89cb91f9b69071)
Diffstat (limited to 'source4/lib/registry/reg_backend_w95.c')
-rw-r--r--source4/lib/registry/reg_backend_w95.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/source4/lib/registry/reg_backend_w95.c b/source4/lib/registry/reg_backend_w95.c
new file mode 100644
index 0000000000..a94ad86c2c
--- /dev/null
+++ b/source4/lib/registry/reg_backend_w95.c
@@ -0,0 +1,356 @@
+/*
+ Samba Unix/Linux SMB client utility libeditreg.c
+ Copyright (C) 2004 Jelmer Vernooij, jelmer@samba.org
+
+ Backend for Windows '95 registry files. Explanation of file format
+ comes from http://www.cs.mun.ca/~michael/regutils/.
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "includes.h"
+#include "registry.h"
+#include "system/shmem.h"
+
+/**
+ * The registry starts with a header that contains pointers to
+ * the rgdb.
+ *
+ * After the main header follows the RGKN header (key index table).
+ * The RGKN keys are listed after each other. They are put into
+ * blocks, the first having a length of 0x2000 bytes, the others
+ * being 0x1000 bytes long.
+ *
+ * After the RGKN header follow one or more RGDB blocks. These blocks
+ * contain keys. A key is followed by its name and its values.
+ *
+ * Values are followed by their name and then their data.
+ *
+ * Basically the idea is that the RGKN contains the associations between
+ * the keys and the RGDB contains the actual data.
+ */
+
+typedef uint_t DWORD;
+typedef unsigned short WORD;
+
+typedef struct creg_block {
+ DWORD CREG_ID; /* CREG */
+ DWORD uk1;
+ DWORD rgdb_offset;
+ DWORD chksum;
+ WORD num_rgdb;
+ WORD flags;
+ DWORD uk2;
+ DWORD uk3;
+ DWORD uk4;
+} CREG_HDR;
+
+typedef struct rgkn_block {
+ DWORD RGKN_ID; /* RGKN */
+ DWORD size;
+ DWORD root_offset;
+ DWORD free_offset;
+ DWORD flags;
+ DWORD chksum;
+ DWORD uk1;
+ DWORD uk2;
+} RGKN_HDR;
+
+typedef struct reg_id {
+ WORD id;
+ WORD rgdb;
+} REG_ID;
+
+typedef struct rgkn_key {
+ DWORD type; /* 0x00000000 = normal key, 0x80000000 = free block */
+ DWORD hash; /* Contains either hash or size of free blocks that follows */
+ DWORD next_free;
+ DWORD parent_offset;
+ DWORD first_child_offset;
+ DWORD next_offset;
+ REG_ID id;
+} RGKN_KEY;
+
+
+typedef struct rgdb_block {
+ DWORD RGDB_ID; /* RGDB */
+ DWORD size;
+ DWORD unused_size;
+ WORD flags;
+ WORD section;
+ DWORD free_offset; /* -1 if there is no free space */
+ WORD max_id;
+ WORD first_free_id;
+ DWORD uk1;
+ DWORD chksum;
+} RGDB_HDR;
+
+typedef struct rgdb_key {
+ DWORD size;
+ REG_ID id;
+ DWORD used_size;
+ WORD name_len;
+ WORD num_values;
+ DWORD uk1;
+} RGDB_KEY;
+
+typedef struct rgdb_value {
+ DWORD type;
+ DWORD uk1;
+ WORD name_len;
+ WORD data_len;
+} RGDB_VALUE;
+
+typedef struct creg_struct_s {
+ int fd;
+ BOOL modified;
+ char *base;
+ struct stat sbuf;
+ CREG_HDR *creg_hdr;
+ RGKN_HDR *rgkn_hdr;
+ RGDB_KEY ***rgdb_keys;
+} CREG;
+
+#define RGKN_START_SIZE 0x2000
+#define RGKN_INC_SIZE 0x1000
+
+#define LOCN_RGKN(creg, o) ((RGKN_KEY *)((creg)->base + sizeof(CREG_HDR) + o))
+#define LOCN_RGDB_BLOCK(creg, o) (((creg)->base + (creg)->creg_hdr->rgdb_offset + o))
+#define LOCN_RGDB_KEY(creg, rgdb, id) ((RGDB_KEY *)((creg)->rgdb_keys[(rgdb)][(id)]))
+
+static DWORD str_to_dword(const char *a) {
+ int i;
+ unsigned long ret = 0;
+ for(i = strlen(a)-1; i >= 0; i--) {
+ ret = ret * 0x100 + a[i];
+ }
+ return ret;
+}
+
+#if 0 /* unused */
+
+static DWORD calc_hash(const char *str) {
+ DWORD ret = 0;
+ int i;
+ for(i = 0; str[i] && str[i] != '\\'; i++) {
+ ret+=toupper(str[i]);
+ }
+ return ret;
+}
+
+static void parse_rgkn_block(CREG *creg, off_t start_off, off_t end_off)
+{
+ off_t i;
+ for(i = start_off; end_off - i > sizeof(RGKN_KEY); i+= sizeof(RGKN_KEY)) {
+ RGKN_KEY *key = (RGKN_KEY *)LOCN_RGKN(creg, i);
+ if(key->type == 0) {
+ DEBUG(4,("Regular, id: %d, %d, parent: %x, firstchild: %x, next: %x hash: %lX\n", key->id.id, key->id.rgdb, key->parent_offset, key->first_child_offset, key->next_offset, (long)key->hash));
+ } else if(key->type == 0x80000000) {
+ DEBUG(3,("free\n"));
+ i += key->hash;
+ } else {
+ DEBUG(0,("Invalid key type in RGKN: %0X\n", key->type));
+ }
+ }
+}
+
+#endif
+
+static void parse_rgdb_block(CREG *creg, RGDB_HDR *rgdb_hdr)
+{
+ DWORD used_size = rgdb_hdr->size - rgdb_hdr->unused_size;
+ DWORD offset = 0;
+
+ while(offset < used_size) {
+ RGDB_KEY *key = (RGDB_KEY *)(((char *)rgdb_hdr) + sizeof(RGDB_HDR) + offset);
+
+ if(!(key->id.id == 0xFFFF && key->id.rgdb == 0xFFFF))creg->rgdb_keys[key->id.rgdb][key->id.id] = key;
+ offset += key->size;
+ }
+}
+
+static WERROR w95_open_reg (struct registry_hive *h, struct registry_key **root)
+{
+ CREG *creg;
+ DWORD creg_id, rgkn_id;
+ DWORD i;
+ DWORD offset;
+
+ creg = talloc_p(h, CREG);
+ memset(creg, 0, sizeof(CREG));
+ h->backend_data = creg;
+
+ if((creg->fd = open(h->location, O_RDONLY, 0000)) < 0) {
+ return WERR_FOOBAR;
+ }
+
+ if (fstat(creg->fd, &creg->sbuf) < 0) {
+ return WERR_FOOBAR;
+ }
+
+ creg->base = mmap(0, creg->sbuf.st_size, PROT_READ, MAP_SHARED, creg->fd, 0);
+
+ if ((int)creg->base == 1) {
+ DEBUG(0,("Could not mmap file: %s, %s\n", h->location, strerror(errno)));
+ return WERR_FOOBAR;
+ }
+
+ creg->creg_hdr = (CREG_HDR *)creg->base;
+
+ if ((creg_id = IVAL(&creg->creg_hdr->CREG_ID,0)) != str_to_dword("CREG")) {
+ DEBUG(0, ("Unrecognized Windows 95 registry header id: 0x%0X, %s\n",
+ creg_id, h->location));
+ return WERR_FOOBAR;
+ }
+
+ creg->rgkn_hdr = (RGKN_HDR *)LOCN_RGKN(creg, 0);
+
+ if ((rgkn_id = IVAL(&creg->rgkn_hdr->RGKN_ID,0)) != str_to_dword("RGKN")) {
+ DEBUG(0, ("Unrecognized Windows 95 registry key index id: 0x%0X, %s\n",
+ rgkn_id, h->location));
+ return WERR_FOOBAR;
+ }
+
+#if 0
+ /* If'ed out because we only need to parse this stuff when allocating new
+ * entries (which we don't do at the moment */
+ /* First parse the 0x2000 long block */
+ parse_rgkn_block(creg, sizeof(RGKN_HDR), 0x2000);
+
+ /* Then parse the other 0x1000 length blocks */
+ for(offset = 0x2000; offset < creg->rgkn_hdr->size; offset+=0x1000) {
+ parse_rgkn_block(creg, offset, offset+0x1000);
+ }
+#endif
+
+ creg->rgdb_keys = talloc_array_p(h, RGDB_KEY **, creg->creg_hdr->num_rgdb);
+
+ offset = 0;
+ DEBUG(3, ("Reading %d rgdb entries\n", creg->creg_hdr->num_rgdb));
+ for(i = 0; i < creg->creg_hdr->num_rgdb; i++) {
+ RGDB_HDR *rgdb_hdr = (RGDB_HDR *)LOCN_RGDB_BLOCK(creg, offset);
+
+ if(strncmp((char *)&(rgdb_hdr->RGDB_ID), "RGDB", 4)) {
+ DEBUG(0, ("unrecognized rgdb entry: %4d, %s\n",
+ rgdb_hdr->RGDB_ID, h->location));
+ return WERR_FOOBAR;
+ } else {
+ DEBUG(3, ("Valid rgdb entry, first free id: %d, max id: %d\n", rgdb_hdr->first_free_id, rgdb_hdr->max_id));
+ }
+
+
+ creg->rgdb_keys[i] = talloc_array_p(h, RGDB_KEY *, rgdb_hdr->max_id+1);
+ memset(creg->rgdb_keys[i], 0, sizeof(RGDB_KEY *) * (rgdb_hdr->max_id+1));
+
+ parse_rgdb_block(creg, rgdb_hdr);
+
+ offset+=rgdb_hdr->size;
+ }
+
+ /* First element in rgkn should be root key */
+ *root = talloc_p(h, struct registry_key);
+ (*root)->name = NULL;
+ (*root)->backend_data = LOCN_RGKN(creg, sizeof(RGKN_HDR));
+
+ return WERR_OK;
+}
+
+static WERROR w95_get_subkey_by_index (TALLOC_CTX *mem_ctx, struct registry_key *parent, int n, struct registry_key **key)
+{
+ CREG *creg = parent->hive->backend_data;
+ RGKN_KEY *rgkn_key = parent->backend_data;
+ RGKN_KEY *child;
+ DWORD child_offset;
+ DWORD cur = 0;
+
+ /* Get id of first child */
+ child_offset = rgkn_key->first_child_offset;
+
+ while(child_offset != 0xFFFFFFFF) {
+ child = LOCN_RGKN(creg, child_offset);
+
+ /* n == cur ? return! */
+ if(cur == n) {
+ RGDB_KEY *rgdb_key;
+ rgdb_key = LOCN_RGDB_KEY(creg, child->id.rgdb, child->id.id);
+ if(!rgdb_key) {
+ DEBUG(0, ("Can't find %d,%d in RGDB table!\n", child->id.rgdb, child->id.id));
+ return WERR_FOOBAR;
+ }
+ *key = talloc_p(mem_ctx, struct registry_key);
+ (*key)->backend_data = child;
+ (*key)->name = talloc_strndup(mem_ctx, (char *)rgdb_key + sizeof(RGDB_KEY), rgdb_key->name_len);
+ return WERR_OK;
+ }
+
+ cur++;
+
+ child_offset = child->next_offset;
+ }
+
+ return WERR_NO_MORE_ITEMS;
+}
+
+static WERROR w95_num_values(struct registry_key *k, int *count)
+{
+ RGKN_KEY *rgkn_key = k->backend_data;
+ RGDB_KEY *rgdb_key = LOCN_RGDB_KEY((CREG *)k->hive->backend_data, rgkn_key->id.rgdb, rgkn_key->id.id);
+
+ if(!rgdb_key) return WERR_FOOBAR;
+
+ *count = rgdb_key->num_values;
+
+ return WERR_OK;
+}
+
+static WERROR w95_get_value_by_id(TALLOC_CTX *mem_ctx, struct registry_key *k, int idx, struct registry_value **value)
+{
+ RGKN_KEY *rgkn_key = k->backend_data;
+ DWORD i;
+ DWORD offset = 0;
+ RGDB_KEY *rgdb_key = LOCN_RGDB_KEY((CREG *)k->hive->backend_data, rgkn_key->id.rgdb, rgkn_key->id.id);
+ RGDB_VALUE *curval = NULL;
+
+ if(!rgdb_key) return WERR_FOOBAR;
+
+ if(idx >= rgdb_key->num_values) return WERR_NO_MORE_ITEMS;
+
+ for(i = 0; i < idx; i++) {
+ curval = (RGDB_VALUE *)(((char *)rgdb_key) + sizeof(RGDB_KEY) + rgdb_key->name_len + offset);
+ offset+=sizeof(RGDB_VALUE) + curval->name_len + curval->data_len;
+ }
+
+ *value = talloc_p(mem_ctx, struct registry_value);
+ (*value)->backend_data = curval;
+ (*value)->name = talloc_strndup(mem_ctx, (char *)curval+sizeof(RGDB_VALUE), curval->name_len);
+
+ (*value)->data_len = curval->data_len;
+ (*value)->data_blk = talloc_memdup(mem_ctx, (char *)curval+sizeof(RGDB_VALUE)+curval->name_len, curval->data_len);
+ (*value)->data_type = curval->type;
+
+ return WERR_OK;
+}
+
+static struct hive_operations reg_backend_w95 = {
+ .name = "w95",
+ .open_hive = w95_open_reg,
+ .get_value_by_index = w95_get_value_by_id,
+ .num_values = w95_num_values,
+ .get_subkey_by_index = w95_get_subkey_by_index,
+};
+
+NTSTATUS registry_w95_init(void)
+{
+ return registry_register(&reg_backend_w95);
+}