r24667: Finally merge the registry improvements that Wilco Baan Hofman and I have
[samba.git] / source4 / lib / registry / patchfile.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Reading registry patch files
4    
5    Copyright (C) Jelmer Vernooij 2004-2007
6    Copyright (C) Wilco Baan Hofman 2006
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 3 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, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "lib/registry/patchfile.h"
24 #include "lib/registry/registry.h"
25 #include "system/filesys.h"
26
27
28 _PUBLIC_ WERROR reg_preg_diff_load(int fd, const struct reg_diff_callbacks *callbacks, void *callback_data);
29
30 _PUBLIC_ WERROR reg_dotreg_diff_load(int fd, const struct reg_diff_callbacks *callbacks, void *callback_data);
31
32 /*
33  * Generate difference between two keys
34  */
35 WERROR reg_generate_diff_key(struct registry_key *oldkey, 
36                                                          struct registry_key *newkey,
37                                                          const char *path,
38                                                          const struct reg_diff_callbacks *callbacks,
39                                                          void *callback_data)
40 {
41         int i;
42         struct registry_key *t1, *t2;
43         char *tmppath;
44         const char *keyname1;
45         WERROR error, error1, error2;
46         TALLOC_CTX *mem_ctx = talloc_init("writediff");
47         uint32_t old_num_subkeys, old_num_values,
48                          new_num_subkeys, new_num_values;
49
50         if (oldkey != NULL) {
51                 error = reg_key_get_info(mem_ctx, oldkey, NULL, &old_num_subkeys, &old_num_values,
52                                                                  NULL);
53                 if (!W_ERROR_IS_OK(error)) {
54                         DEBUG(0, ("Error occured while getting key info: %s\n", 
55                                           win_errstr(error)));
56                         return error;
57                 }
58         } else {
59                 old_num_subkeys = 0;
60                 old_num_values = 0;
61         }
62
63         /* Subkeys that were deleted */
64         for (i = 0; i < old_num_subkeys; i++) {
65                 error1 = reg_key_get_subkey_by_index(mem_ctx, oldkey, i, &keyname1,
66                                                                                          NULL, NULL);
67                 if (!W_ERROR_IS_OK(error1)) {
68                         DEBUG(0, ("Error occured while getting subkey by index: %s\n", 
69                                           win_errstr(error2)));
70                         continue;
71                 }
72
73                 if (newkey != NULL) {
74                         error2 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
75
76                         if (W_ERROR_IS_OK(error2))
77                                 continue;
78                 } else {
79                         error2 = WERR_DEST_NOT_FOUND;
80                         t2 = NULL;
81                 }
82
83                 if (!W_ERROR_EQUAL(error2, WERR_DEST_NOT_FOUND)) {
84                         DEBUG(0, ("Error occured while getting subkey by name: %s\n", 
85                                           win_errstr(error2)));
86                         talloc_free(mem_ctx);
87                         return error2;
88                 }
89
90                 /* newkey didn't have such a subkey, add del diff */
91                 tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
92                 callbacks->del_key(callback_data, tmppath);
93                 talloc_free(tmppath);   
94         }
95
96         if (newkey != NULL) {
97                 error = reg_key_get_info(mem_ctx, newkey, NULL, &new_num_subkeys, &new_num_values,
98                                                                  NULL);
99                 if (!W_ERROR_IS_OK(error)) {
100                         DEBUG(0, ("Error occured while getting key info: %s\n", 
101                                           win_errstr(error)));
102                         return error;
103                 }
104         } else {
105                 new_num_subkeys = 0;
106                 new_num_values = 0;
107         }
108
109         /* Subkeys that were added */
110         for(i = 0; i < new_num_subkeys; i++) {
111                 error1 = reg_key_get_subkey_by_index(mem_ctx, newkey, i, &keyname1, 
112                                                                                          NULL, NULL);
113                 if (!W_ERROR_IS_OK(error1)) {
114                         DEBUG(0, ("Error occured while getting subkey by index: %s\n", 
115                                           win_errstr(error1)));
116                         talloc_free(mem_ctx);
117                         return error1;
118                 }
119
120                 if (oldkey != NULL) {
121                         error2 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
122                                 
123                         if (W_ERROR_IS_OK(error2))
124                                 continue;
125                 } else {
126                         t1 = NULL;
127                         error2 = WERR_DEST_NOT_FOUND;
128                 }
129                         
130                 if (!W_ERROR_EQUAL(error2, WERR_DEST_NOT_FOUND)) {
131                         DEBUG(0, ("Error occured while getting subkey by name: %s\n", 
132                                           win_errstr(error2)));
133                         talloc_free(mem_ctx);
134                         return error2;
135                 }
136
137                 /* oldkey didn't have such a subkey, add add diff */
138                 tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
139                 callbacks->add_key(callback_data, tmppath);
140
141                 W_ERROR_NOT_OK_RETURN(
142                                 reg_open_key(mem_ctx, newkey, keyname1, &t2));
143
144                 reg_generate_diff_key(t1, t2, tmppath, callbacks, callback_data);
145                 talloc_free(tmppath);
146         }
147
148         /* Values that were changed */
149         for(i = 0; i < new_num_values; i++) {
150                 const char *name;
151                 uint32_t type1, type2;
152                 DATA_BLOB contents1, contents2;
153
154                 error1 = reg_key_get_value_by_index(mem_ctx, newkey, i, 
155                                                                                         &name, &type1, &contents1);
156                 if (!W_ERROR_IS_OK(error1)) {
157                         DEBUG(0, ("Unable to get key by index: %s\n", 
158                                           win_errstr(error1)));
159                         talloc_free(mem_ctx);
160                         return error1;
161                 }
162
163                 if (oldkey != NULL) {
164                         error2 = reg_key_get_value_by_name(mem_ctx, oldkey, name, 
165                                                                                    &type2, &contents2);
166                 } else 
167                         error2 = WERR_DEST_NOT_FOUND;
168         
169                 if(!W_ERROR_IS_OK(error2) && 
170                    !W_ERROR_EQUAL(error2, WERR_DEST_NOT_FOUND)) {
171                         DEBUG(0, ("Error occured while getting value by name: %s\n", 
172                                           win_errstr(error2)));
173                         talloc_free(mem_ctx);
174                         return error2;
175                 }
176
177                 if (W_ERROR_IS_OK(error2) && data_blob_cmp(&contents1, &contents2) == 0)
178                         continue;
179
180                 callbacks->set_value(callback_data, path, name, type1, contents1);
181         }
182
183         /* Values that were deleted */
184         for (i = 0; i < old_num_values; i++) {
185                 const char *name;
186                 error1 = reg_key_get_value_by_index(mem_ctx, oldkey, i, &name, 
187                                                                                         NULL, NULL);
188                 if (!W_ERROR_IS_OK(error1)) {
189                         DEBUG(0, ("Error ocurred getting value by index: %s\n", 
190                                           win_errstr(error1)));
191                         talloc_free(mem_ctx);
192                         return error1;
193                 }
194
195                 error2 = reg_key_get_value_by_name(mem_ctx, newkey, name, NULL, 
196                                                                                    NULL);
197
198                 if (W_ERROR_IS_OK(error2))
199                         continue;
200
201                 if (!W_ERROR_EQUAL(error2, WERR_DEST_NOT_FOUND)) {
202                         DEBUG(0, ("Error occured while getting value by name: %s\n", 
203                                           win_errstr(error2)));
204                         return error2;
205                 }
206
207                 callbacks->del_value(callback_data, path, name);
208         }
209
210         talloc_free(mem_ctx);
211         return WERR_OK;
212 }
213
214 /**
215  * Generate diff between two registry contexts 
216  */
217 _PUBLIC_ WERROR reg_generate_diff(struct registry_context *ctx1, 
218                                   struct registry_context *ctx2, 
219                                   const struct reg_diff_callbacks *callbacks,
220                                   void *callback_data)
221 {
222         int i;
223         WERROR error;
224
225         for(i = HKEY_FIRST; i <= HKEY_LAST; i++) {
226                 struct registry_key *r1 = NULL, *r2 = NULL;
227                 error = reg_get_predefined_key(ctx1, i, &r1);
228                 if (!W_ERROR_IS_OK(error) && !W_ERROR_EQUAL(error, WERR_NOT_FOUND)) {
229                         DEBUG(0, ("Unable to open hive %s for backend 1\n", reg_get_predef_name(i)));
230                 }
231                 
232                 error = reg_get_predefined_key(ctx2, i, &r2);
233                 if (!W_ERROR_IS_OK(error) && !W_ERROR_EQUAL(error, WERR_NOT_FOUND)) {
234                         DEBUG(0, ("Unable to open hive %s for backend 2\n", reg_get_predef_name(i)));
235                 }
236
237                 if (r1 == NULL && r2 == NULL)
238                         continue;
239
240                 error = reg_generate_diff_key(r1, r2, reg_get_predef_name(i), callbacks, callback_data);
241                 if (!W_ERROR_IS_OK(error)) {
242                         DEBUG(0, ("Unable to determine diff: %s\n", win_errstr(error)));
243                         return error;
244                 }
245         }
246         if (callbacks->done != NULL) {
247                 callbacks->done(callback_data);
248         }
249         return WERR_OK;
250 }
251
252 /**
253  * Load diff file
254  */
255 _PUBLIC_ WERROR reg_diff_load(const char *filename, const struct reg_diff_callbacks *callbacks, void *callback_data)
256 {
257         int fd;
258         char hdr[4];
259         
260         fd = open(filename, O_RDONLY, 0);
261         if (fd == -1) {
262                 DEBUG(0, ("Error opening registry patch file `%s'\n", filename));
263                 return WERR_GENERAL_FAILURE;
264         }
265
266         if (read(fd, &hdr, 4) != 4) {
267                 DEBUG(0, ("Error reading registry patch file `%s'\n", filename));
268                 return WERR_GENERAL_FAILURE;
269         }
270
271         /* Reset position in file */
272         lseek(fd, 0, SEEK_SET);
273 #if 0
274         if (strncmp(hdr, "CREG", 4) == 0) {
275                 /* Must be a W9x CREG Config.pol file */
276                 return reg_creg_diff_load(diff, fd);
277         } else if (strncmp(hdr, "regf", 4) == 0) {
278                 /* Must be a REGF NTConfig.pol file */
279                 return reg_regf_diff_load(diff, fd);
280         } else 
281 #endif 
282         if (strncmp(hdr, "PReg", 4) == 0) {
283                 /* Must be a GPO Registry.pol file */
284                 return reg_preg_diff_load(fd, callbacks, callback_data);
285         } else {
286                 /* Must be a normal .REG file */
287                 return reg_dotreg_diff_load(fd, callbacks, callback_data);
288         }
289 }
290
291 /**
292  * The reg_diff_apply functions
293  */
294 static WERROR reg_diff_apply_add_key(void *_ctx, const char *key_name) 
295 {
296         struct registry_context *ctx = _ctx;
297         struct registry_key *tmp;
298         WERROR error;
299
300         error = reg_key_add_abs(ctx, ctx, key_name, 0, NULL, &tmp);
301
302         if (!W_ERROR_EQUAL(error, WERR_ALREADY_EXISTS) && !W_ERROR_IS_OK(error)) {
303                 DEBUG(0, ("Error adding new key '%s': %s\n", key_name, win_errstr(error)));
304                 return error;
305         }
306         return WERR_OK;
307 }
308
309 static WERROR reg_diff_apply_del_key(void *_ctx, const char *key_name) 
310 {
311         struct registry_context *ctx = _ctx;
312         WERROR error;
313
314         error = reg_key_del_abs(ctx, key_name);
315
316         if(!W_ERROR_IS_OK(error)) {
317                 DEBUG(0, ("Unable to delete key '%s'\n", key_name));
318                 return error;
319         }
320         
321         return WERR_OK;
322 }
323
324 static WERROR reg_diff_apply_set_value(void *_ctx, const char *path, const char *value_name, uint32_t value_type, DATA_BLOB value)
325 {
326         struct registry_context *ctx = _ctx;
327         struct registry_key *tmp;
328         WERROR error;
329         
330         /* Open key */
331         error = reg_open_key_abs(ctx, ctx, path, &tmp);
332
333         if (W_ERROR_EQUAL(error, WERR_DEST_NOT_FOUND)) {
334                 DEBUG(0, ("Error opening key '%s'\n", path));
335                 return error;
336         }
337
338         /* Set value */
339         error = reg_val_set(tmp, value_name, 
340                                  value_type, value);
341         if (!W_ERROR_IS_OK(error)) {
342                 DEBUG(0, ("Error setting value '%s'\n", value_name));
343                 return error;
344         }       
345         
346         return WERR_OK;
347 }
348
349 static WERROR reg_diff_apply_del_value (void *_ctx, const char *key_name, const char *value_name)
350 {
351         struct registry_context *ctx = _ctx;
352         struct registry_key *tmp;
353         WERROR error;
354         
355         /* Open key */
356         error = reg_open_key_abs(ctx, ctx, key_name, &tmp);
357
358         if (!W_ERROR_IS_OK(error)) {
359                 DEBUG(0, ("Error opening key '%s'\n", key_name));
360                 return error;
361         }
362
363         error = reg_del_value(tmp, value_name);
364         if (!W_ERROR_IS_OK(error)) {
365                 DEBUG(0, ("Error deleting value '%s'\n", value_name));
366                 return error;
367         }
368         
369
370         return WERR_OK;
371 }
372
373 static WERROR reg_diff_apply_del_all_values(void *_ctx, const char *key_name)
374 {
375         struct registry_context *ctx = _ctx;
376         struct registry_key *key;
377         WERROR error;
378         int i;
379         uint32_t num_values;
380
381         error = reg_open_key_abs(ctx, ctx, key_name, &key);
382
383         if (!W_ERROR_IS_OK(error)) {
384                 DEBUG(0, ("Error opening key '%s'\n", key_name));
385                 return error;
386         }
387
388         W_ERROR_NOT_OK_RETURN(reg_key_get_info(ctx, key, 
389                                                                                    NULL, 
390                                                                                    NULL,
391                                                                                    &num_values, 
392                                                                                    NULL));
393
394         for (i = 0; i < num_values; i++) {
395                 const char *name;
396                 W_ERROR_NOT_OK_RETURN(reg_key_get_value_by_index(ctx, key, i, &name, 
397                                                                                                                  NULL, NULL));
398                 W_ERROR_NOT_OK_RETURN(reg_del_value(key, name));
399         }
400
401         return WERR_OK;
402 }
403
404 /** 
405  * Apply diff to a registry context 
406  */
407 _PUBLIC_ WERROR reg_diff_apply (const char *filename, struct registry_context *ctx)
408 {
409         struct reg_diff_callbacks callbacks;
410
411         callbacks.add_key = reg_diff_apply_add_key;
412         callbacks.del_key = reg_diff_apply_del_key;
413         callbacks.set_value = reg_diff_apply_set_value;
414         callbacks.del_value = reg_diff_apply_del_value;
415         callbacks.del_all_values = reg_diff_apply_del_all_values;
416         callbacks.done = NULL;
417
418         return reg_diff_load(filename, &callbacks, ctx);
419 }