r355: Fix a bunch of compiler warnings in the registry code.
[jelmer/samba4-debian.git] / source / lib / registry / reg_backend_w95 / reg_backend_w95.c
1 /*
2    Samba Unix/Linux SMB client utility libeditreg.c 
3    Copyright (C) 2004 Jelmer Vernooij, jelmer@samba.org
4
5    Backend for Windows '95 registry files. Explanation of file format 
6    comes from http://www.cs.mun.ca/~michael/regutils/.
7
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.
12    
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.
17    
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.  */
21
22 #include "includes.h"
23 #include "lib/registry/common/registry.h"
24
25 /**
26  * The registry starts with a header that contains pointers to 
27  * the rgdb.
28  *
29  * After the main header follows the RGKN header (key index table).
30  * The RGKN keys are listed after each other. They are put into 
31  * blocks, the first having a length of 0x2000 bytes, the others 
32  * being 0x1000 bytes long.
33  *
34  * After the RGKN header follow one or more RGDB blocks. These blocks 
35  * contain keys. A key is followed by its name and its values.
36  *
37  * Values are followed by their name and then their data.
38  *
39  * Basically the idea is that the RGKN contains the associations between 
40  * the keys and the RGDB contains the actual data.
41  */
42
43 typedef unsigned int DWORD;
44 typedef unsigned short WORD;
45
46 typedef struct creg_block {
47         DWORD CREG_ID;          /* CREG */
48         DWORD uk1;
49         DWORD rgdb_offset;
50         DWORD chksum;
51         WORD  num_rgdb;
52         WORD  flags;
53         DWORD uk2;
54         DWORD uk3;
55         DWORD uk4;
56 } CREG_HDR;
57
58 typedef struct rgkn_block {
59         DWORD RGKN_ID;          /* RGKN */
60         DWORD size;
61         DWORD root_offset;
62         DWORD free_offset;
63         DWORD flags;
64         DWORD chksum;
65         DWORD uk1;
66         DWORD uk2;
67 } RGKN_HDR;
68
69 typedef struct reg_id {
70         WORD id;
71         WORD rgdb;
72 } REG_ID;
73
74 typedef struct rgkn_key {
75         DWORD type;                     /* 0x00000000 = normal key, 0x80000000 = free block */
76         DWORD hash;                     /* Contains either hash or size of free blocks that follows */
77         DWORD next_free;
78         DWORD parent_offset;
79         DWORD first_child_offset;
80         DWORD next_offset;
81         REG_ID id;
82 } RGKN_KEY;
83
84
85 typedef struct rgdb_block {
86         DWORD RGDB_ID;          /* RGDB */
87         DWORD size;
88         DWORD unused_size;
89         WORD flags;
90         WORD section;
91         DWORD free_offset;      /* -1 if there is no free space */
92         WORD max_id;
93         WORD first_free_id;
94         DWORD uk1;
95         DWORD chksum;
96 } RGDB_HDR;
97
98 typedef struct rgdb_key {
99         DWORD size;
100         REG_ID id;
101         DWORD used_size;
102         WORD  name_len;
103         WORD  num_values;
104         DWORD uk1;
105 } RGDB_KEY;
106
107 typedef struct rgdb_value {
108         DWORD type;
109         DWORD uk1;
110         WORD name_len;
111         WORD data_len;
112 } RGDB_VALUE;
113
114 typedef struct creg_struct_s {
115         int fd;
116         BOOL modified;
117         char *base;
118         struct stat sbuf;
119         CREG_HDR *creg_hdr;
120         RGKN_HDR *rgkn_hdr;
121         RGDB_KEY ***rgdb_keys;
122 } CREG;
123
124 #define RGKN_START_SIZE 0x2000
125 #define RGKN_INC_SIZE   0x1000
126
127 #define LOCN_RGKN(creg, o) ((RGKN_KEY *)((creg)->base + sizeof(CREG_HDR) + o))
128 #define LOCN_RGDB_BLOCK(creg, o) (((creg)->base + (creg)->creg_hdr->rgdb_offset + o))
129 #define LOCN_RGDB_KEY(creg, rgdb, id) ((RGDB_KEY *)((creg)->rgdb_keys[(rgdb)][(id)]))
130
131 static DWORD str_to_dword(const char *a) {
132     int i;
133     unsigned long ret = 0;
134     for(i = strlen(a)-1; i >= 0; i--) {
135         ret = ret * 0x100 + a[i];
136     }
137     return ret;
138 }
139
140 #if 0 /* unused */
141
142 static DWORD calc_hash(const char *str) {
143         DWORD ret = 0;
144         int i;
145         for(i = 0; str[i] && str[i] != '\\'; i++) {
146                 ret+=toupper(str[i]);
147         }
148         return ret;
149 }
150
151 static void parse_rgkn_block(CREG *creg, off_t start_off, off_t end_off) 
152 {
153         off_t i;
154         for(i = start_off; end_off - i > sizeof(RGKN_KEY); i+= sizeof(RGKN_KEY)) {
155                 RGKN_KEY *key = (RGKN_KEY *)LOCN_RGKN(creg, i);
156                 if(key->type == 0) {
157                         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));
158                 } else if(key->type == 0x80000000) {
159                         DEBUG(3,("free\n"));
160                         i += key->hash;
161                 } else {
162                         DEBUG(0,("Invalid key type in RGKN: %0X\n", key->type));
163                 }
164         }
165 }
166
167 #endif
168
169 static void parse_rgdb_block(CREG *creg, RGDB_HDR *rgdb_hdr)
170 {
171         DWORD used_size = rgdb_hdr->size - rgdb_hdr->unused_size;
172         DWORD offset = 0;
173
174         while(offset < used_size) {
175                 RGDB_KEY *key = (RGDB_KEY *)(((char *)rgdb_hdr) + sizeof(RGDB_HDR) + offset);
176                 
177                 if(!(key->id.id == 0xFFFF && key->id.rgdb == 0xFFFF))creg->rgdb_keys[key->id.rgdb][key->id.id] = key;
178                 offset += key->size;
179         }
180 }
181
182 static WERROR w95_open_root (REG_HANDLE *h, REG_KEY **key)
183 {
184         CREG *creg = h->backend_data;
185         
186         /* First element in rgkn should be root key */
187         *key = reg_key_new_abs("\\", h, LOCN_RGKN(creg, sizeof(RGKN_HDR)));
188         
189         return WERR_OK;
190 }
191
192 static WERROR w95_get_subkey_by_index (REG_KEY *parent, int n, REG_KEY **key)
193 {
194         CREG *creg = parent->handle->backend_data;
195         RGKN_KEY *rgkn_key = parent->backend_data;
196         RGKN_KEY *child;
197         DWORD child_offset;
198         DWORD cur = 0;
199         
200         /* Get id of first child */
201         child_offset = rgkn_key->first_child_offset;
202
203         while(child_offset != 0xFFFFFFFF) {
204                 child = LOCN_RGKN(creg, child_offset);
205
206                 /* n == cur ? return! */
207                 if(cur == n) {
208                         RGDB_KEY *rgdb_key;
209                         char *name;
210                         rgdb_key = LOCN_RGDB_KEY(creg, child->id.rgdb, child->id.id);
211                         if(!rgdb_key) {
212                                 DEBUG(0, ("Can't find %d,%d in RGDB table!\n", child->id.rgdb, child->id.id));
213                                 return WERR_FOOBAR;
214                         }
215                         name = strndup((char *)rgdb_key + sizeof(RGDB_KEY), rgdb_key->name_len);
216                         *key = reg_key_new_rel(name, parent, child);
217                         SAFE_FREE(name);
218                         return WERR_OK;
219                 }
220
221                 cur++;
222                 
223                 child_offset = child->next_offset;
224         }
225
226         return WERR_NO_MORE_ITEMS;
227 }
228
229 static WERROR w95_open_reg (REG_HANDLE *h, const char *location, const char *credentials)
230 {
231         CREG *creg;
232         DWORD creg_id, rgkn_id;
233         DWORD i;
234         DWORD offset;
235
236         creg = talloc_p(h->mem_ctx, CREG);
237         memset(creg, 0, sizeof(CREG));
238         h->backend_data = creg;
239
240         if((creg->fd = open(location, O_RDONLY, 0000)) < 0) {
241                 return WERR_FOOBAR;
242         }
243
244     if (fstat(creg->fd, &creg->sbuf) < 0) {
245                 return WERR_FOOBAR;
246     }
247
248     creg->base = mmap(0, creg->sbuf.st_size, PROT_READ, MAP_SHARED, creg->fd, 0);
249                                                                                                                                               
250     if ((int)creg->base == 1) {
251                 DEBUG(0,("Could not mmap file: %s, %s\n", location, strerror(errno)));
252         return WERR_FOOBAR;
253     }
254
255         creg->creg_hdr = (CREG_HDR *)creg->base;
256
257         if ((creg_id = IVAL(&creg->creg_hdr->CREG_ID,0)) != str_to_dword("CREG")) {
258                 DEBUG(0, ("Unrecognized Windows 95 registry header id: 0x%0X, %s\n", 
259                                   creg_id, location));
260                 return WERR_FOOBAR;
261         }
262
263         creg->rgkn_hdr = (RGKN_HDR *)LOCN_RGKN(creg, 0);
264
265         if ((rgkn_id = IVAL(&creg->rgkn_hdr->RGKN_ID,0)) != str_to_dword("RGKN")) {
266                 DEBUG(0, ("Unrecognized Windows 95 registry key index id: 0x%0X, %s\n", 
267                                   rgkn_id, location));
268                 return WERR_FOOBAR;
269         }
270
271 #if 0   
272         /* If'ed out because we only need to parse this stuff when allocating new 
273          * entries (which we don't do at the moment */
274         /* First parse the 0x2000 long block */
275         parse_rgkn_block(creg, sizeof(RGKN_HDR), 0x2000);
276
277         /* Then parse the other 0x1000 length blocks */
278         for(offset = 0x2000; offset < creg->rgkn_hdr->size; offset+=0x1000) {
279                 parse_rgkn_block(creg, offset, offset+0x1000);
280         }
281 #endif
282
283         creg->rgdb_keys = talloc_array_p(h->mem_ctx, RGDB_KEY **, creg->creg_hdr->num_rgdb);
284
285         offset = 0;
286         DEBUG(3, ("Reading %d rgdb entries\n", creg->creg_hdr->num_rgdb));
287         for(i = 0; i < creg->creg_hdr->num_rgdb; i++) {
288                 RGDB_HDR *rgdb_hdr = (RGDB_HDR *)LOCN_RGDB_BLOCK(creg, offset);
289                 
290                 if(strncmp((char *)&(rgdb_hdr->RGDB_ID), "RGDB", 4)) {
291                         DEBUG(0, ("unrecognized rgdb entry: %4d, %s\n", 
292                                           rgdb_hdr->RGDB_ID, location));
293                         return WERR_FOOBAR;
294                 } else {
295                         DEBUG(3, ("Valid rgdb entry, first free id: %d, max id: %d\n", rgdb_hdr->first_free_id, rgdb_hdr->max_id));
296                 }
297
298
299                 creg->rgdb_keys[i] = talloc_array_p(h->mem_ctx, RGDB_KEY *, rgdb_hdr->max_id+1);
300                 memset(creg->rgdb_keys[i], 0, sizeof(RGDB_KEY *) * (rgdb_hdr->max_id+1));
301
302                 parse_rgdb_block(creg, rgdb_hdr);
303
304                 offset+=rgdb_hdr->size;
305         }
306         
307
308         return WERR_OK;
309 }
310
311 static WERROR w95_close_reg(REG_HANDLE *h)
312 {
313         CREG *creg = h->backend_data;
314         if (creg->base) munmap(creg->base, creg->sbuf.st_size);
315         creg->base = NULL;
316     close(creg->fd);
317         return WERR_OK;
318 }
319
320
321 static WERROR w95_fetch_values(REG_KEY *k, int *count, REG_VAL ***values)
322 {
323         RGKN_KEY *rgkn_key = k->backend_data;
324         DWORD i;
325         DWORD offset = 0;
326         RGDB_KEY *rgdb_key = LOCN_RGDB_KEY((CREG *)k->handle->backend_data, rgkn_key->id.rgdb, rgkn_key->id.id);
327
328         if(!rgdb_key) return WERR_FOOBAR;
329         
330         *count = rgdb_key->num_values;
331         
332         if((*count) == 0) return WERR_OK;
333
334         (*values) = talloc_array_p(k->mem_ctx, REG_VAL *, (*count)+1);
335         for(i = 0; i < rgdb_key->num_values; i++) {
336                 RGDB_VALUE *val = (RGDB_VALUE *)(((char *)rgdb_key) + sizeof(RGDB_KEY) + rgdb_key->name_len + offset);
337                 (*values)[i] = reg_val_new(k, val);
338                 
339                 /* Name */
340                 (*values)[i]->name = talloc_strndup(k->mem_ctx, (char *)val+sizeof(RGDB_VALUE), val->name_len);
341                 
342                 /* Value */
343                 (*values)[i]->data_len = val->data_len;
344                 (*values)[i]->data_blk = talloc_memdup((*values)[i]->mem_ctx, (char *)val+sizeof(RGDB_VALUE)+val->name_len, val->data_len);
345
346                 /* Type */
347                 (*values)[i]->data_type = val->type;
348
349                 offset+=sizeof(RGDB_VALUE) + val->name_len + val->data_len;
350         }
351         
352         return WERR_OK;
353 }
354
355 static struct registry_ops reg_backend_w95 = {
356         .name = "w95",
357         .open_registry = w95_open_reg,
358         .close_registry = w95_close_reg,
359         .open_root_key = w95_open_root,
360         .fetch_values = w95_fetch_values,
361         .get_subkey_by_index = w95_get_subkey_by_index,
362 };
363
364 NTSTATUS reg_w95_init(void)
365 {
366         return register_backend("registry", &reg_backend_w95);
367 }