2 Samba Unix/Linux SMB client utility libeditreg.c
3 Copyright (C) 2004 Jelmer Vernooij, jelmer@samba.org
5 Backend for Windows '95 registry files. Explanation of file format
6 comes from http://www.cs.mun.ca/~michael/regutils/.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
24 #include "system/shmem.h"
27 * The registry starts with a header that contains pointers to
30 * After the main header follows the RGKN header (key index table).
31 * The RGKN keys are listed after each other. They are put into
32 * blocks, the first having a length of 0x2000 bytes, the others
33 * being 0x1000 bytes long.
35 * After the RGKN header follow one or more RGDB blocks. These blocks
36 * contain keys. A key is followed by its name and its values.
38 * Values are followed by their name and then their data.
40 * Basically the idea is that the RGKN contains the associations between
41 * the keys and the RGDB contains the actual data.
45 typedef unsigned short WORD
;
47 typedef struct creg_block
{
48 DWORD CREG_ID
; /* CREG */
59 typedef struct rgkn_block
{
60 DWORD RGKN_ID
; /* RGKN */
70 typedef struct reg_id
{
75 typedef struct rgkn_key
{
76 DWORD type
; /* 0x00000000 = normal key, 0x80000000 = free block */
77 DWORD hash
; /* Contains either hash or size of free blocks that follows */
80 DWORD first_child_offset
;
86 typedef struct rgdb_block
{
87 DWORD RGDB_ID
; /* RGDB */
92 DWORD free_offset
; /* -1 if there is no free space */
99 typedef struct rgdb_key
{
108 typedef struct rgdb_value
{
115 typedef struct creg_struct_s
{
122 RGDB_KEY
***rgdb_keys
;
125 #define RGKN_START_SIZE 0x2000
126 #define RGKN_INC_SIZE 0x1000
128 #define LOCN_RGKN(creg, o) ((RGKN_KEY *)((creg)->base + sizeof(CREG_HDR) + o))
129 #define LOCN_RGDB_BLOCK(creg, o) (((creg)->base + (creg)->creg_hdr->rgdb_offset + o))
130 #define LOCN_RGDB_KEY(creg, rgdb, id) ((RGDB_KEY *)((creg)->rgdb_keys[(rgdb)][(id)]))
132 static DWORD
str_to_dword(const char *a
) {
134 unsigned long ret
= 0;
135 for(i
= strlen(a
)-1; i
>= 0; i
--) {
136 ret
= ret
* 0x100 + a
[i
];
143 static DWORD
calc_hash(const char *str
) {
146 for(i
= 0; str
[i
] && str
[i
] != '\\'; i
++) {
147 ret
+=toupper(str
[i
]);
152 static void parse_rgkn_block(CREG
*creg
, off_t start_off
, off_t end_off
)
155 for(i
= start_off
; end_off
- i
> sizeof(RGKN_KEY
); i
+= sizeof(RGKN_KEY
)) {
156 RGKN_KEY
*key
= (RGKN_KEY
*)LOCN_RGKN(creg
, i
);
158 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
));
159 } else if(key
->type
== 0x80000000) {
163 DEBUG(0,("Invalid key type in RGKN: %0X\n", key
->type
));
170 static void parse_rgdb_block(CREG
*creg
, RGDB_HDR
*rgdb_hdr
)
172 DWORD used_size
= rgdb_hdr
->size
- rgdb_hdr
->unused_size
;
175 while(offset
< used_size
) {
176 RGDB_KEY
*key
= (RGDB_KEY
*)(((char *)rgdb_hdr
) + sizeof(RGDB_HDR
) + offset
);
178 if(!(key
->id
.id
== 0xFFFF && key
->id
.rgdb
== 0xFFFF))creg
->rgdb_keys
[key
->id
.rgdb
][key
->id
.id
] = key
;
183 static WERROR
w95_open_reg (TALLOC_CTX
*mem_ctx
, struct registry_hive
*h
, struct registry_key
**root
)
186 DWORD creg_id
, rgkn_id
;
190 creg
= talloc_p(mem_ctx
, CREG
);
191 memset(creg
, 0, sizeof(CREG
));
192 h
->backend_data
= creg
;
194 if((creg
->fd
= open(h
->location
, O_RDONLY
, 0000)) < 0) {
198 if (fstat(creg
->fd
, &creg
->sbuf
) < 0) {
202 creg
->base
= mmap(0, creg
->sbuf
.st_size
, PROT_READ
, MAP_SHARED
, creg
->fd
, 0);
204 if ((int)creg
->base
== 1) {
205 DEBUG(0,("Could not mmap file: %s, %s\n", h
->location
, strerror(errno
)));
209 creg
->creg_hdr
= (CREG_HDR
*)creg
->base
;
211 if ((creg_id
= IVAL(&creg
->creg_hdr
->CREG_ID
,0)) != str_to_dword("CREG")) {
212 DEBUG(0, ("Unrecognized Windows 95 registry header id: 0x%0X, %s\n",
213 creg_id
, h
->location
));
217 creg
->rgkn_hdr
= (RGKN_HDR
*)LOCN_RGKN(creg
, 0);
219 if ((rgkn_id
= IVAL(&creg
->rgkn_hdr
->RGKN_ID
,0)) != str_to_dword("RGKN")) {
220 DEBUG(0, ("Unrecognized Windows 95 registry key index id: 0x%0X, %s\n",
221 rgkn_id
, h
->location
));
226 /* If'ed out because we only need to parse this stuff when allocating new
227 * entries (which we don't do at the moment */
228 /* First parse the 0x2000 long block */
229 parse_rgkn_block(creg
, sizeof(RGKN_HDR
), 0x2000);
231 /* Then parse the other 0x1000 length blocks */
232 for(offset
= 0x2000; offset
< creg
->rgkn_hdr
->size
; offset
+=0x1000) {
233 parse_rgkn_block(creg
, offset
, offset
+0x1000);
237 creg
->rgdb_keys
= talloc_array_p(mem_ctx
, RGDB_KEY
**, creg
->creg_hdr
->num_rgdb
);
240 DEBUG(3, ("Reading %d rgdb entries\n", creg
->creg_hdr
->num_rgdb
));
241 for(i
= 0; i
< creg
->creg_hdr
->num_rgdb
; i
++) {
242 RGDB_HDR
*rgdb_hdr
= (RGDB_HDR
*)LOCN_RGDB_BLOCK(creg
, offset
);
244 if(strncmp((char *)&(rgdb_hdr
->RGDB_ID
), "RGDB", 4)) {
245 DEBUG(0, ("unrecognized rgdb entry: %4d, %s\n",
246 rgdb_hdr
->RGDB_ID
, h
->location
));
249 DEBUG(3, ("Valid rgdb entry, first free id: %d, max id: %d\n", rgdb_hdr
->first_free_id
, rgdb_hdr
->max_id
));
253 creg
->rgdb_keys
[i
] = talloc_array_p(mem_ctx
, RGDB_KEY
*, rgdb_hdr
->max_id
+1);
254 memset(creg
->rgdb_keys
[i
], 0, sizeof(RGDB_KEY
*) * (rgdb_hdr
->max_id
+1));
256 parse_rgdb_block(creg
, rgdb_hdr
);
258 offset
+=rgdb_hdr
->size
;
261 /* First element in rgkn should be root key */
262 *root
= talloc_p(mem_ctx
, struct registry_key
);
263 (*root
)->name
= NULL
;
264 (*root
)->backend_data
= LOCN_RGKN(creg
, sizeof(RGKN_HDR
));
269 static WERROR
w95_get_subkey_by_index (TALLOC_CTX
*mem_ctx
, struct registry_key
*parent
, int n
, struct registry_key
**key
)
271 CREG
*creg
= parent
->hive
->backend_data
;
272 RGKN_KEY
*rgkn_key
= parent
->backend_data
;
277 /* Get id of first child */
278 child_offset
= rgkn_key
->first_child_offset
;
280 while(child_offset
!= 0xFFFFFFFF) {
281 child
= LOCN_RGKN(creg
, child_offset
);
283 /* n == cur ? return! */
286 rgdb_key
= LOCN_RGDB_KEY(creg
, child
->id
.rgdb
, child
->id
.id
);
288 DEBUG(0, ("Can't find %d,%d in RGDB table!\n", child
->id
.rgdb
, child
->id
.id
));
291 *key
= talloc_p(mem_ctx
, struct registry_key
);
292 (*key
)->backend_data
= child
;
293 (*key
)->name
= talloc_strndup(mem_ctx
, (char *)rgdb_key
+ sizeof(RGDB_KEY
), rgdb_key
->name_len
);
299 child_offset
= child
->next_offset
;
302 return WERR_NO_MORE_ITEMS
;
305 static WERROR
w95_num_values(struct registry_key
*k
, int *count
)
307 RGKN_KEY
*rgkn_key
= k
->backend_data
;
308 RGDB_KEY
*rgdb_key
= LOCN_RGDB_KEY((CREG
*)k
->hive
->backend_data
, rgkn_key
->id
.rgdb
, rgkn_key
->id
.id
);
310 if(!rgdb_key
) return WERR_FOOBAR
;
312 *count
= rgdb_key
->num_values
;
317 static WERROR
w95_get_value_by_id(TALLOC_CTX
*mem_ctx
, struct registry_key
*k
, int idx
, struct registry_value
**value
)
319 RGKN_KEY
*rgkn_key
= k
->backend_data
;
322 RGDB_KEY
*rgdb_key
= LOCN_RGDB_KEY((CREG
*)k
->hive
->backend_data
, rgkn_key
->id
.rgdb
, rgkn_key
->id
.id
);
325 if(!rgdb_key
) return WERR_FOOBAR
;
327 if(idx
>= rgdb_key
->num_values
) return WERR_NO_MORE_ITEMS
;
329 for(i
= 0; i
< idx
; i
++) {
330 curval
= (RGDB_VALUE
*)(((char *)rgdb_key
) + sizeof(RGDB_KEY
) + rgdb_key
->name_len
+ offset
);
331 offset
+=sizeof(RGDB_VALUE
) + curval
->name_len
+ curval
->data_len
;
334 *value
= talloc_p(mem_ctx
, struct registry_value
);
335 (*value
)->backend_data
= curval
;
336 (*value
)->name
= talloc_strndup(mem_ctx
, (char *)curval
+sizeof(RGDB_VALUE
), curval
->name_len
);
338 (*value
)->data_len
= curval
->data_len
;
339 (*value
)->data_blk
= talloc_memdup(mem_ctx
, (char *)curval
+sizeof(RGDB_VALUE
)+curval
->name_len
, curval
->data_len
);
340 (*value
)->data_type
= curval
->type
;
345 static struct registry_operations reg_backend_w95
= {
347 .open_hive
= w95_open_reg
,
348 .get_value_by_index
= w95_get_value_by_id
,
349 .num_values
= w95_num_values
,
350 .get_subkey_by_index
= w95_get_subkey_by_index
,
353 NTSTATUS
registry_w95_init(void)
355 return registry_register(®_backend_w95
);