r181: Parsing windows '95 registry files now works (including values)
authorJelmer Vernooij <jelmer@samba.org>
Mon, 12 Apr 2004 17:43:22 +0000 (17:43 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 17:51:15 +0000 (12:51 -0500)
(This used to be commit 4d6ce648567060b9922343971d7aafd545341439)

source4/lib/registry/common/reg_interface.c
source4/lib/registry/reg_backend_w95/reg_backend_w95.c

index 917b03cf32f83d94e131f0d3feb84e01c8044601..696a504d878c13489002bd1b4f2a8e8750d0d682 100644 (file)
@@ -268,7 +268,10 @@ WERROR reg_key_get_subkey_by_index(REG_KEY *key, int idx, REG_KEY **subkey)
 {
        if(!key) return WERR_INVALID_PARAM;
 
-       if(!key->handle->functions->get_subkey_by_index) {
+       if(key->handle->functions->get_subkey_by_index) {
+               WERROR status = key->handle->functions->get_subkey_by_index(key, idx, subkey);
+               if(!NT_STATUS_IS_OK(status)) return status;
+       } else if(key->handle->functions->fetch_subkeys) {
                if(!key->cache_subkeys) 
                        key->handle->functions->fetch_subkeys(key, &key->cache_subkeys_count, &key->cache_subkeys);
 
@@ -278,8 +281,7 @@ WERROR reg_key_get_subkey_by_index(REG_KEY *key, int idx, REG_KEY **subkey)
                        return WERR_NO_MORE_ITEMS;
                }
        } else {
-               WERROR status = key->handle->functions->get_subkey_by_index(key, idx, subkey);
-               if(!NT_STATUS_IS_OK(status)) return status;
+               return WERR_NOT_SUPPORTED;
        }
 
        (*subkey)->path = talloc_asprintf((*subkey)->mem_ctx, "%s%s%s", key->path, key->path[strlen(key->path)-1] == '\\'?"":"\\", (*subkey)->name);
index 8b20b830e147697bc8e0ce78b9b517de8f44df7c..0b6b6db358c30d8102a8a44f565e7f5d67fb18b8 100644 (file)
  * The registry starts with a header that contains pointers to 
  * the rgdb.
  *
- * After the main header follows the RGKN header (key index table) */
+ * 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 unsigned int DWORD;
 typedef unsigned short WORD;
@@ -54,17 +66,22 @@ typedef struct rgkn_block {
        DWORD uk2;
 } RGKN_HDR;
 
-typedef struct rgkn_key {
-       DWORD type;
-       DWORD hash;
-       DWORD next_free;
-       DWORD parent;
-       DWORD child;
-       DWORD next;
+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;
@@ -79,21 +96,19 @@ typedef struct rgdb_block {
 } RGDB_HDR;
 
 typedef struct rgdb_key {
-       DWORD type;
-       DWORD hash;
-       DWORD next_free;
-       DWORD parent;
-       DWORD child;
-       DWORD next;
-       WORD id;
-       WORD rgdb;
+       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;
-       DWORD name_len;
-       DWORD data_len;
+       WORD name_len;
+       WORD data_len;
 } RGDB_VALUE;
 
 typedef struct creg_struct_s {
@@ -103,9 +118,16 @@ typedef struct creg_struct_s {
        struct stat sbuf;
        CREG_HDR *creg_hdr;
        RGKN_HDR *rgkn_hdr;
-       char *rgkn;
+       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;
@@ -115,7 +137,86 @@ static DWORD str_to_dword(const char *a) {
     return ret;
 }
 
-#define LOCN(creg, o) (((creg)->base + sizeof(CREG_HDR) + o))
+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, 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));
+               }
+       }
+}
+
+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_root (REG_HANDLE *h, REG_KEY **key)
+{
+       CREG *creg = h->backend_data;
+       
+       /* First element in rgkn should be root key */
+       *key = reg_key_new_abs("\\", h, LOCN_RGKN(creg, sizeof(RGKN_HDR)));
+       
+       return WERR_OK;
+}
+
+static WERROR w95_get_subkey_by_index (REG_KEY *parent, int n, REG_KEY **key)
+{
+       CREG *creg = parent->handle->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;
+                       char *name;
+                       rgdb_key = LOCN_RGDB_KEY(creg, child->id.rgdb, child->id.id);
+                       name = strndup((char *)rgdb_key + sizeof(RGDB_KEY), rgdb_key->name_len);
+                       *key = reg_key_new_rel(name, parent, child);
+                       SAFE_FREE(name);
+                       return WERR_OK;
+               }
+
+               cur++;
+               
+               child_offset = child->next_offset;
+       }
+
+       return WERR_NO_MORE_ITEMS;
+}
 
 static WERROR w95_open_reg (REG_HANDLE *h, const char *location, const char *credentials)
 {
@@ -124,7 +225,7 @@ static WERROR w95_open_reg (REG_HANDLE *h, const char *location, const char *cre
        memset(creg, 0, sizeof(CREG));
        h->backend_data = creg;
        DWORD i, nfree = 0;
-       DWORD offset;
+       DWORD offset, end_offset;
 
        if((creg->fd = open(location, O_RDONLY, 0000)) < 0) {
                return WERR_FOOBAR;
@@ -149,7 +250,7 @@ static WERROR w95_open_reg (REG_HANDLE *h, const char *location, const char *cre
                return WERR_FOOBAR;
        }
 
-       creg->rgkn_hdr = (RGKN_HDR *)LOCN(creg, 0);
+       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", 
@@ -157,38 +258,39 @@ static WERROR w95_open_reg (REG_HANDLE *h, const char *location, const char *cre
                return WERR_FOOBAR;
        }
 
-#if 0 
-       for(i = 0; i < creg->rgkn_hdr->size; i+=sizeof(RGKN_KEY)) {
-               RGKN_KEY *key = (RGKN_KEY *)LOCN(creg, sizeof(RGKN_HDR) + i);
-               if(nfree > 0) {
-                       nfree--;
-               } else if(key->type == 0) {
-                       DEBUG(0,("Not used\n"));
-                       /* Not used */
-               } else if(key->type == 0x80000000) {
-                       DEBUG(0,("Regular key\n"));
-                       /* Regular key */
-               } else {
-                       DEBUG(0,("Invalid key type in RGKN: %0X\n", key->type));
-               }
-       }
+#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);
 
-       curpos += creg->rgkn_hdr->size + sizeof(RGKN_HDR);
+       /* 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
-       offset = creg->rgkn_hdr->size;
 
-       DEBUG(0, ("Reading %d rgdb entries\n", creg->creg_hdr->num_rgdb));
+       creg->rgdb_keys = talloc_array_p(h->mem_ctx, 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(creg, offset);
+               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: %4s, %s\n", 
                                          &rgdb_hdr->RGDB_ID, location));
                        return WERR_FOOBAR;
                } else {
-                       DEBUG(0, ("Valid rgdb entry\n"));
+                       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->mem_ctx, 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;
        }
        
@@ -205,10 +307,49 @@ static WERROR w95_close_reg(REG_HANDLE *h)
        return WERR_OK;
 }
 
+
+static WERROR w95_fetch_values(REG_KEY *k, int *count, REG_VAL ***values)
+{
+       RGKN_KEY *rgkn_key = k->backend_data;
+       RGDB_VALUE *val;
+       DWORD i;
+       DWORD offset = 0;
+       RGDB_KEY *rgdb_key = LOCN_RGDB_KEY((CREG *)k->handle->backend_data, rgkn_key->id.rgdb, rgkn_key->id.id);
+
+       if(!rgdb_key) return WERR_FOOBAR;
+       
+       *count = rgdb_key->num_values;
+       
+       if((*count) == 0) return WERR_OK;
+
+       (*values) = talloc_array_p(k->mem_ctx, REG_VAL *, (*count)+1);
+       for(i = 0; i < rgdb_key->num_values; i++) {
+               RGDB_VALUE *val = (RGDB_VALUE *)(((char *)rgdb_key) + sizeof(RGDB_KEY) + rgdb_key->name_len + offset);
+               (*values)[i] = reg_val_new(k, val);
+               
+               /* Name */
+               (*values)[i]->name = talloc_strndup(k->mem_ctx, (char *)val+sizeof(RGDB_VALUE), val->name_len);
+               
+               /* Value */
+               (*values)[i]->data_len = val->data_len;
+               (*values)[i]->data_blk = talloc_memdup((*values)[i]->mem_ctx, (char *)val+sizeof(RGDB_VALUE)+val->name_len, val->data_len);
+
+               /* Type */
+               (*values)[i]->data_type = val->type;
+
+               offset+=sizeof(RGDB_VALUE) + val->name_len + val->data_len;
+       }
+       
+       return WERR_OK;
+}
+
 static struct registry_ops reg_backend_w95 = {
        .name = "w95",
        .open_registry = w95_open_reg,
        .close_registry = w95_close_reg,
+       .open_root_key = w95_open_root,
+       .fetch_values = w95_fetch_values,
+       .get_subkey_by_index = w95_get_subkey_by_index,
 };
 
 NTSTATUS reg_w95_init(void)