r174: Win95 registry files (like USER.DAT) can now be partially parsed
[kai/samba.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
31 typedef unsigned int DWORD;
32 typedef unsigned short WORD;
33
34 typedef struct creg_block {
35         DWORD CREG_ID;          /* CREG */
36         DWORD uk1;
37         DWORD rgdb_offset;
38         DWORD chksum;
39         WORD  num_rgdb;
40         WORD  flags;
41         DWORD uk2;
42         DWORD uk3;
43         DWORD uk4;
44 } CREG_HDR;
45
46 typedef struct rgkn_block {
47         DWORD RGKN_ID;          /* RGKN */
48         DWORD size;
49         DWORD root_offset;
50         DWORD free_offset;
51         DWORD flags;
52         DWORD chksum;
53         DWORD uk1;
54         DWORD uk2;
55 } RGKN_HDR;
56
57 typedef struct rgkn_key {
58         DWORD type;
59         DWORD hash;
60         DWORD next_free;
61         DWORD parent;
62         DWORD child;
63         DWORD next;
64         WORD id;
65         WORD rgdb;
66 } RGKN_KEY;
67
68 typedef struct rgdb_block {
69         DWORD RGDB_ID;          /* RGDB */
70         DWORD size;
71         DWORD unused_size;
72         WORD flags;
73         WORD section;
74         DWORD free_offset;      /* -1 if there is no free space */
75         WORD max_id;
76         WORD first_free_id;
77         DWORD uk1;
78         DWORD chksum;
79 } RGDB_HDR;
80
81 typedef struct rgdb_key {
82         DWORD type;
83         DWORD hash;
84         DWORD next_free;
85         DWORD parent;
86         DWORD child;
87         DWORD next;
88         WORD id;
89         WORD rgdb;
90 } RGDB_KEY;
91
92 typedef struct rgdb_value {
93         DWORD type;
94         DWORD uk1;
95         DWORD name_len;
96         DWORD data_len;
97 } RGDB_VALUE;
98
99 typedef struct creg_struct_s {
100         int fd;
101         BOOL modified;
102         char *base;
103         struct stat sbuf;
104         CREG_HDR *creg_hdr;
105         RGKN_HDR *rgkn_hdr;
106         char *rgkn;
107 } CREG;
108
109 static DWORD str_to_dword(const char *a) {
110     int i;
111     unsigned long ret = 0;
112     for(i = strlen(a)-1; i >= 0; i--) {
113         ret = ret * 0x100 + a[i];
114     }
115     return ret;
116 }
117
118 #define LOCN(creg, o) (((creg)->base + sizeof(CREG_HDR) + o))
119
120 static WERROR w95_open_reg (REG_HANDLE *h, const char *location, const char *credentials)
121 {
122         CREG *creg = talloc_p(h->mem_ctx, CREG);
123         DWORD creg_id, rgkn_id;
124         memset(creg, 0, sizeof(CREG));
125         h->backend_data = creg;
126         DWORD i, nfree = 0;
127         DWORD offset;
128
129         if((creg->fd = open(location, O_RDONLY, 0000)) < 0) {
130                 return WERR_FOOBAR;
131         }
132
133     if (fstat(creg->fd, &creg->sbuf) < 0) {
134                 return WERR_FOOBAR;
135     }
136
137     creg->base = mmap(0, creg->sbuf.st_size, PROT_READ, MAP_SHARED, creg->fd, 0);
138                                                                                                                                               
139     if ((int)creg->base == 1) {
140                 DEBUG(0,("Could not mmap file: %s, %s\n", location, strerror(errno)));
141         return WERR_FOOBAR;
142     }
143
144         creg->creg_hdr = (CREG_HDR *)creg->base;
145
146         if ((creg_id = IVAL(&creg->creg_hdr->CREG_ID,0)) != str_to_dword("CREG")) {
147                 DEBUG(0, ("Unrecognized Windows 95 registry header id: 0x%0X, %s\n", 
148                                   creg_id, location));
149                 return WERR_FOOBAR;
150         }
151
152         creg->rgkn_hdr = (RGKN_HDR *)LOCN(creg, 0);
153
154         if ((rgkn_id = IVAL(&creg->rgkn_hdr->RGKN_ID,0)) != str_to_dword("RGKN")) {
155                 DEBUG(0, ("Unrecognized Windows 95 registry key index id: 0x%0X, %s\n", 
156                                   rgkn_id, location));
157                 return WERR_FOOBAR;
158         }
159
160 #if 0 
161         for(i = 0; i < creg->rgkn_hdr->size; i+=sizeof(RGKN_KEY)) {
162                 RGKN_KEY *key = (RGKN_KEY *)LOCN(creg, sizeof(RGKN_HDR) + i);
163                 if(nfree > 0) {
164                         nfree--;
165                 } else if(key->type == 0) {
166                         DEBUG(0,("Not used\n"));
167                         /* Not used */
168                 } else if(key->type == 0x80000000) {
169                         DEBUG(0,("Regular key\n"));
170                         /* Regular key */
171                 } else {
172                         DEBUG(0,("Invalid key type in RGKN: %0X\n", key->type));
173                 }
174         }
175
176         curpos += creg->rgkn_hdr->size + sizeof(RGKN_HDR);
177 #endif
178         offset = creg->rgkn_hdr->size;
179
180         DEBUG(0, ("Reading %d rgdb entries\n", creg->creg_hdr->num_rgdb));
181         for(i = 0; i < creg->creg_hdr->num_rgdb; i++) {
182                 RGDB_HDR *rgdb_hdr = (RGDB_HDR *)LOCN(creg, offset);
183                 
184                 if(strncmp((char *)&(rgdb_hdr->RGDB_ID), "RGDB", 4)) {
185                         DEBUG(0, ("unrecognized rgdb entry: %4s, %s\n", 
186                                           &rgdb_hdr->RGDB_ID, location));
187                         return WERR_FOOBAR;
188                 } else {
189                         DEBUG(0, ("Valid rgdb entry\n"));
190                 }
191
192                 offset+=rgdb_hdr->size;
193         }
194         
195
196         return WERR_OK;
197 }
198
199 static WERROR w95_close_reg(REG_HANDLE *h)
200 {
201         CREG *creg = h->backend_data;
202         if (creg->base) munmap(creg->base, creg->sbuf.st_size);
203         creg->base = NULL;
204     close(creg->fd);
205         return WERR_OK;
206 }
207
208 static struct registry_ops reg_backend_w95 = {
209         .name = "w95",
210         .open_registry = w95_open_reg,
211         .close_registry = w95_close_reg,
212 };
213
214 NTSTATUS reg_w95_init(void)
215 {
216         return register_backend("registry", &reg_backend_w95);
217 }