2 Unix SMB/CIFS implementation.
3 Reading registry patch files
5 Copyright (C) Jelmer Vernooij 2004-2007
6 Copyright (C) Wilco Baan Hofman 2006
7 Copyright (C) Matthias Dieter Wallnöfer 2008-2010
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "lib/registry/registry.h"
25 #include "system/filesys.h"
28 _PUBLIC_ WERROR
reg_preg_diff_load(int fd
,
29 const struct reg_diff_callbacks
*callbacks
,
32 _PUBLIC_ WERROR
reg_dotreg_diff_load(int fd
,
33 const struct reg_diff_callbacks
*callbacks
,
37 * Generate difference between two keys
39 WERROR
reg_generate_diff_key(struct registry_key
*oldkey
,
40 struct registry_key
*newkey
,
42 const struct reg_diff_callbacks
*callbacks
,
46 struct registry_key
*t1
= NULL
, *t2
= NULL
;
49 WERROR error
, error1
, error2
;
50 TALLOC_CTX
*mem_ctx
= talloc_init("writediff");
51 uint32_t old_num_subkeys
, old_num_values
,
52 new_num_subkeys
, new_num_values
;
55 error
= reg_key_get_info(mem_ctx
, oldkey
, NULL
,
56 &old_num_subkeys
, &old_num_values
,
57 NULL
, NULL
, NULL
, NULL
);
58 if (!W_ERROR_IS_OK(error
)) {
59 DEBUG(0, ("Error occurred while getting key info: %s\n",
69 /* Subkeys that were changed or deleted */
70 for (i
= 0; i
< old_num_subkeys
; i
++) {
71 error1
= reg_key_get_subkey_by_index(mem_ctx
, oldkey
, i
,
72 &keyname1
, NULL
, NULL
);
73 if (!W_ERROR_IS_OK(error1
)) {
74 DEBUG(0, ("Error occurred while getting subkey by index: %s\n",
80 error2
= reg_open_key(mem_ctx
, newkey
, keyname1
, &t2
);
82 error2
= WERR_BADFILE
;
86 if (!W_ERROR_IS_OK(error2
) && !W_ERROR_EQUAL(error2
, WERR_BADFILE
)) {
87 DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
93 /* if "error2" is going to be "WERR_BADFILE", then newkey */
94 /* didn't have such a subkey and therefore add a del diff */
95 tmppath
= talloc_asprintf(mem_ctx
, "%s\\%s", path
, keyname1
);
96 if (tmppath
== NULL
) {
97 DEBUG(0, ("Out of memory\n"));
101 if (!W_ERROR_IS_OK(error2
))
102 callbacks
->del_key(callback_data
, tmppath
);
104 /* perform here also the recursive invocation */
105 error1
= reg_open_key(mem_ctx
, oldkey
, keyname1
, &t1
);
106 if (!W_ERROR_IS_OK(error1
)) {
107 DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
108 win_errstr(error1
)));
109 talloc_free(mem_ctx
);
112 reg_generate_diff_key(t1
, t2
, tmppath
, callbacks
, callback_data
);
114 talloc_free(tmppath
);
117 if (newkey
!= NULL
) {
118 error
= reg_key_get_info(mem_ctx
, newkey
, NULL
,
119 &new_num_subkeys
, &new_num_values
,
120 NULL
, NULL
, NULL
, NULL
);
121 if (!W_ERROR_IS_OK(error
)) {
122 DEBUG(0, ("Error occurred while getting key info: %s\n",
124 talloc_free(mem_ctx
);
132 /* Subkeys that were added */
133 for(i
= 0; i
< new_num_subkeys
; i
++) {
134 error1
= reg_key_get_subkey_by_index(mem_ctx
, newkey
, i
,
135 &keyname1
, NULL
, NULL
);
136 if (!W_ERROR_IS_OK(error1
)) {
137 DEBUG(0, ("Error occurred while getting subkey by index: %s\n",
138 win_errstr(error1
)));
139 talloc_free(mem_ctx
);
143 if (oldkey
!= NULL
) {
144 error2
= reg_open_key(mem_ctx
, oldkey
, keyname1
, &t1
);
146 if (W_ERROR_IS_OK(error2
))
149 error2
= WERR_BADFILE
;
153 if (!W_ERROR_EQUAL(error2
, WERR_BADFILE
)) {
154 DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
155 win_errstr(error2
)));
156 talloc_free(mem_ctx
);
160 /* oldkey didn't have such a subkey, add a add diff */
161 tmppath
= talloc_asprintf(mem_ctx
, "%s\\%s", path
, keyname1
);
162 if (tmppath
== NULL
) {
163 DEBUG(0, ("Out of memory\n"));
164 talloc_free(mem_ctx
);
167 callbacks
->add_key(callback_data
, tmppath
);
169 /* perform here also the recursive invocation */
170 error1
= reg_open_key(mem_ctx
, newkey
, keyname1
, &t2
);
171 if (!W_ERROR_IS_OK(error1
)) {
172 DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
173 win_errstr(error1
)));
174 talloc_free(mem_ctx
);
177 reg_generate_diff_key(t1
, t2
, tmppath
, callbacks
, callback_data
);
179 talloc_free(tmppath
);
182 /* Values that were added or changed */
183 for(i
= 0; i
< new_num_values
; i
++) {
185 uint32_t type1
, type2
;
186 DATA_BLOB contents1
= { NULL
, 0 }, contents2
= { NULL
, 0 };
188 error1
= reg_key_get_value_by_index(mem_ctx
, newkey
, i
,
189 &name
, &type1
, &contents1
);
190 if (!W_ERROR_IS_OK(error1
)) {
191 DEBUG(0, ("Unable to get value by index: %s\n",
192 win_errstr(error1
)));
193 talloc_free(mem_ctx
);
197 if (oldkey
!= NULL
) {
198 error2
= reg_key_get_value_by_name(mem_ctx
, oldkey
,
202 error2
= WERR_BADFILE
;
204 if (!W_ERROR_IS_OK(error2
)
205 && !W_ERROR_EQUAL(error2
, WERR_BADFILE
)) {
206 DEBUG(0, ("Error occurred while getting value by name: %s\n",
207 win_errstr(error2
)));
208 talloc_free(mem_ctx
);
212 if (W_ERROR_IS_OK(error2
)
213 && (data_blob_cmp(&contents1
, &contents2
) == 0)
214 && (type1
== type2
)) {
215 talloc_free(discard_const_p(char, name
));
216 talloc_free(contents1
.data
);
217 talloc_free(contents2
.data
);
221 callbacks
->set_value(callback_data
, path
, name
,
224 talloc_free(discard_const_p(char, name
));
225 talloc_free(contents1
.data
);
226 talloc_free(contents2
.data
);
229 /* Values that were deleted */
230 for (i
= 0; i
< old_num_values
; i
++) {
233 DATA_BLOB contents
= { NULL
, 0 };
235 error1
= reg_key_get_value_by_index(mem_ctx
, oldkey
, i
, &name
,
237 if (!W_ERROR_IS_OK(error1
)) {
238 DEBUG(0, ("Unable to get value by index: %s\n",
239 win_errstr(error1
)));
240 talloc_free(mem_ctx
);
245 error2
= reg_key_get_value_by_name(mem_ctx
, newkey
,
246 name
, &type
, &contents
);
248 error2
= WERR_BADFILE
;
250 if (W_ERROR_IS_OK(error2
)) {
251 talloc_free(discard_const_p(char, name
));
252 talloc_free(contents
.data
);
256 if (!W_ERROR_EQUAL(error2
, WERR_BADFILE
)) {
257 DEBUG(0, ("Error occurred while getting value by name: %s\n",
258 win_errstr(error2
)));
259 talloc_free(mem_ctx
);
263 callbacks
->del_value(callback_data
, path
, name
);
265 talloc_free(discard_const_p(char, name
));
266 talloc_free(contents
.data
);
269 talloc_free(mem_ctx
);
274 * Generate diff between two registry contexts
276 _PUBLIC_ WERROR
reg_generate_diff(struct registry_context
*ctx1
,
277 struct registry_context
*ctx2
,
278 const struct reg_diff_callbacks
*callbacks
,
284 for (i
= 0; reg_predefined_keys
[i
].name
; i
++) {
285 struct registry_key
*r1
= NULL
, *r2
= NULL
;
287 error
= reg_get_predefined_key(ctx1
,
288 reg_predefined_keys
[i
].handle
, &r1
);
289 if (!W_ERROR_IS_OK(error
) &&
290 !W_ERROR_EQUAL(error
, WERR_BADFILE
)) {
291 DEBUG(0, ("Unable to open hive %s for backend 1\n",
292 reg_predefined_keys
[i
].name
));
296 error
= reg_get_predefined_key(ctx2
,
297 reg_predefined_keys
[i
].handle
, &r2
);
298 if (!W_ERROR_IS_OK(error
) &&
299 !W_ERROR_EQUAL(error
, WERR_BADFILE
)) {
300 DEBUG(0, ("Unable to open hive %s for backend 2\n",
301 reg_predefined_keys
[i
].name
));
305 /* if "r1" is NULL (old hive) and "r2" isn't (new hive) then
306 * the hive doesn't exist yet and we have to generate an add
308 if ((r1
== NULL
) && (r2
!= NULL
)) {
309 callbacks
->add_key(callback_data
,
310 reg_predefined_keys
[i
].name
);
312 /* if "r1" isn't NULL (old hive) and "r2" is (new hive) then
313 * the hive shouldn't exist anymore and we have to generate a
315 if ((r1
!= NULL
) && (r2
== NULL
)) {
316 callbacks
->del_key(callback_data
,
317 reg_predefined_keys
[i
].name
);
320 error
= reg_generate_diff_key(r1
, r2
,
321 reg_predefined_keys
[i
].name
, callbacks
,
323 if (!W_ERROR_IS_OK(error
)) {
324 DEBUG(0, ("Unable to determine diff: %s\n",
329 if (callbacks
->done
!= NULL
) {
330 callbacks
->done(callback_data
);
338 _PUBLIC_ WERROR
reg_diff_load(const char *filename
,
339 const struct reg_diff_callbacks
*callbacks
,
345 fd
= open(filename
, O_RDONLY
, 0);
347 DEBUG(0, ("Error opening registry patch file `%s'\n",
349 return WERR_GENERAL_FAILURE
;
352 if (read(fd
, &hdr
, 4) != 4) {
353 DEBUG(0, ("Error reading registry patch file `%s'\n",
356 return WERR_GENERAL_FAILURE
;
359 /* Reset position in file */
360 lseek(fd
, 0, SEEK_SET
);
361 #if 0 /* These backends are not supported yet. */
362 if (strncmp(hdr
, "CREG", 4) == 0) {
363 /* Must be a W9x CREG Config.pol file */
364 return reg_creg_diff_load(diff
, fd
);
365 } else if (strncmp(hdr
, "regf", 4) == 0) {
366 /* Must be a REGF NTConfig.pol file */
367 return reg_regf_diff_load(diff
, fd
);
370 if (strncmp(hdr
, "PReg", 4) == 0) {
371 /* Must be a GPO Registry.pol file */
372 return reg_preg_diff_load(fd
, callbacks
, callback_data
);
374 /* Must be a normal .REG file */
375 return reg_dotreg_diff_load(fd
, callbacks
, callback_data
);
380 * The reg_diff_apply functions
382 static WERROR
reg_diff_apply_add_key(void *_ctx
, const char *key_name
)
384 struct registry_context
*ctx
= (struct registry_context
*)_ctx
;
385 struct registry_key
*tmp
;
389 /* Recursively create the path */
390 buf
= talloc_strdup(ctx
, key_name
);
391 W_ERROR_HAVE_NO_MEMORY(buf
);
394 while (*buf_ptr
++ != '\0' ) {
395 if (*buf_ptr
== '\\') {
397 error
= reg_key_add_abs(ctx
, ctx
, buf
, 0, NULL
, &tmp
);
399 if (!W_ERROR_EQUAL(error
, WERR_ALREADY_EXISTS
) &&
400 !W_ERROR_IS_OK(error
)) {
401 DEBUG(0, ("Error adding new key '%s': %s\n",
402 key_name
, win_errstr(error
)));
413 error
= reg_key_add_abs(ctx
, ctx
, key_name
, 0, NULL
, &tmp
);
415 if (!W_ERROR_EQUAL(error
, WERR_ALREADY_EXISTS
) &&
416 !W_ERROR_IS_OK(error
)) {
417 DEBUG(0, ("Error adding new key '%s': %s\n",
418 key_name
, win_errstr(error
)));
426 static WERROR
reg_diff_apply_del_key(void *_ctx
, const char *key_name
)
428 struct registry_context
*ctx
= (struct registry_context
*)_ctx
;
430 /* We can't proof here for success, because a common superkey could */
431 /* have been deleted before the subkey's (diff order). This removed */
432 /* therefore all children recursively and the "WERR_BADFILE" result is */
435 reg_key_del_abs(ctx
, key_name
);
440 static WERROR
reg_diff_apply_set_value(void *_ctx
, const char *path
,
441 const char *value_name
,
442 uint32_t value_type
, DATA_BLOB value
)
444 struct registry_context
*ctx
= (struct registry_context
*)_ctx
;
445 struct registry_key
*tmp
;
449 error
= reg_open_key_abs(ctx
, ctx
, path
, &tmp
);
451 if (W_ERROR_EQUAL(error
, WERR_BADFILE
)) {
452 DEBUG(0, ("Error opening key '%s'\n", path
));
457 error
= reg_val_set(tmp
, value_name
,
459 if (!W_ERROR_IS_OK(error
)) {
460 DEBUG(0, ("Error setting value '%s'\n", value_name
));
469 static WERROR
reg_diff_apply_del_value(void *_ctx
, const char *key_name
,
470 const char *value_name
)
472 struct registry_context
*ctx
= (struct registry_context
*)_ctx
;
473 struct registry_key
*tmp
;
477 error
= reg_open_key_abs(ctx
, ctx
, key_name
, &tmp
);
479 if (!W_ERROR_IS_OK(error
)) {
480 DEBUG(0, ("Error opening key '%s'\n", key_name
));
484 error
= reg_del_value(ctx
, tmp
, value_name
);
485 if (!W_ERROR_IS_OK(error
)) {
486 DEBUG(0, ("Error deleting value '%s'\n", value_name
));
495 static WERROR
reg_diff_apply_del_all_values(void *_ctx
, const char *key_name
)
497 struct registry_context
*ctx
= (struct registry_context
*)_ctx
;
498 struct registry_key
*key
;
500 const char *value_name
;
502 error
= reg_open_key_abs(ctx
, ctx
, key_name
, &key
);
504 if (!W_ERROR_IS_OK(error
)) {
505 DEBUG(0, ("Error opening key '%s'\n", key_name
));
509 W_ERROR_NOT_OK_RETURN(reg_key_get_info(ctx
, key
, NULL
,
510 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
));
512 while (W_ERROR_IS_OK(reg_key_get_value_by_index(
513 ctx
, key
, 0, &value_name
, NULL
, NULL
))) {
514 error
= reg_del_value(ctx
, key
, value_name
);
515 if (!W_ERROR_IS_OK(error
)) {
516 DEBUG(0, ("Error deleting value '%s'\n", value_name
));
519 talloc_free(discard_const_p(char, value_name
));
528 * Apply diff to a registry context
530 _PUBLIC_ WERROR
reg_diff_apply(struct registry_context
*ctx
,
531 const char *filename
)
533 struct reg_diff_callbacks callbacks
;
535 callbacks
.add_key
= reg_diff_apply_add_key
;
536 callbacks
.del_key
= reg_diff_apply_del_key
;
537 callbacks
.set_value
= reg_diff_apply_set_value
;
538 callbacks
.del_value
= reg_diff_apply_del_value
;
539 callbacks
.del_all_values
= reg_diff_apply_del_all_values
;
540 callbacks
.done
= NULL
;
542 return reg_diff_load(filename
, &callbacks
, ctx
);