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
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 struct smb_iconv_convenience
*iconv_convenience
,
30 const struct reg_diff_callbacks
*callbacks
,
33 _PUBLIC_ WERROR
reg_dotreg_diff_load(int fd
,
34 struct smb_iconv_convenience
*iconv_convenience
,
35 const struct reg_diff_callbacks
*callbacks
,
39 * Generate difference between two keys
41 WERROR
reg_generate_diff_key(struct registry_key
*oldkey
,
42 struct registry_key
*newkey
,
44 const struct reg_diff_callbacks
*callbacks
,
48 struct registry_key
*t1
= NULL
, *t2
= NULL
;
51 WERROR error
, error1
, error2
;
52 TALLOC_CTX
*mem_ctx
= talloc_init("writediff");
53 uint32_t old_num_subkeys
, old_num_values
,
54 new_num_subkeys
, new_num_values
;
57 error
= reg_key_get_info(mem_ctx
, oldkey
, NULL
,
58 &old_num_subkeys
, &old_num_values
,
59 NULL
, NULL
, NULL
, NULL
);
60 if (!W_ERROR_IS_OK(error
)) {
61 DEBUG(0, ("Error occurred while getting key info: %s\n",
71 /* Subkeys that were changed or deleted */
72 for (i
= 0; i
< old_num_subkeys
; i
++) {
73 error1
= reg_key_get_subkey_by_index(mem_ctx
, oldkey
, i
,
74 &keyname1
, NULL
, NULL
);
75 if (!W_ERROR_IS_OK(error1
)) {
76 DEBUG(0, ("Error occurred while getting subkey by index: %s\n",
82 error2
= reg_open_key(mem_ctx
, newkey
, keyname1
, &t2
);
84 error2
= WERR_BADFILE
;
88 if (!W_ERROR_IS_OK(error2
) && !W_ERROR_EQUAL(error2
, WERR_BADFILE
)) {
89 DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
95 /* if "error2" is going to be "WERR_BADFILE", then newkey */
96 /* didn't have such a subkey and therefore add a del diff */
97 tmppath
= talloc_asprintf(mem_ctx
, "%s\\%s", path
, keyname1
);
98 if (!W_ERROR_IS_OK(error2
))
99 callbacks
->del_key(callback_data
, tmppath
);
101 /* perform here also the recursive invocation */
102 error1
= reg_open_key(mem_ctx
, oldkey
, keyname1
, &t1
);
103 if (!W_ERROR_IS_OK(error1
)) {
104 DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
105 win_errstr(error1
)));
106 talloc_free(mem_ctx
);
109 reg_generate_diff_key(t1
, t2
, tmppath
, callbacks
, callback_data
);
111 talloc_free(tmppath
);
114 if (newkey
!= NULL
) {
115 error
= reg_key_get_info(mem_ctx
, newkey
, NULL
,
116 &new_num_subkeys
, &new_num_values
,
117 NULL
, NULL
, NULL
, NULL
);
118 if (!W_ERROR_IS_OK(error
)) {
119 DEBUG(0, ("Error occurred while getting key info: %s\n",
121 talloc_free(mem_ctx
);
129 /* Subkeys that were added */
130 for(i
= 0; i
< new_num_subkeys
; i
++) {
131 error1
= reg_key_get_subkey_by_index(mem_ctx
, newkey
, i
,
132 &keyname1
, NULL
, NULL
);
133 if (!W_ERROR_IS_OK(error1
)) {
134 DEBUG(0, ("Error occurred while getting subkey by index: %s\n",
135 win_errstr(error1
)));
136 talloc_free(mem_ctx
);
140 if (oldkey
!= NULL
) {
141 error2
= reg_open_key(mem_ctx
, oldkey
, keyname1
, &t1
);
143 if (W_ERROR_IS_OK(error2
))
146 error2
= WERR_BADFILE
;
150 if (!W_ERROR_EQUAL(error2
, WERR_BADFILE
)) {
151 DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
152 win_errstr(error2
)));
153 talloc_free(mem_ctx
);
157 /* oldkey didn't have such a subkey, add add diff */
158 tmppath
= talloc_asprintf(mem_ctx
, "%s\\%s", path
, keyname1
);
159 callbacks
->add_key(callback_data
, tmppath
);
161 /* perform here also the recursive invocation */
162 error1
= reg_open_key(mem_ctx
, newkey
, keyname1
, &t2
);
163 if (!W_ERROR_IS_OK(error1
)) {
164 DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
165 win_errstr(error1
)));
166 talloc_free(mem_ctx
);
169 reg_generate_diff_key(t1
, t2
, tmppath
, callbacks
, callback_data
);
171 talloc_free(tmppath
);
174 /* Values that were added or changed */
175 for(i
= 0; i
< new_num_values
; i
++) {
177 uint32_t type1
, type2
;
178 DATA_BLOB contents1
, contents2
;
180 error1
= reg_key_get_value_by_index(mem_ctx
, newkey
, i
,
181 &name
, &type1
, &contents1
);
182 if (!W_ERROR_IS_OK(error1
)) {
183 DEBUG(0, ("Unable to get value by index: %s\n",
184 win_errstr(error1
)));
185 talloc_free(mem_ctx
);
189 if (oldkey
!= NULL
) {
190 error2
= reg_key_get_value_by_name(mem_ctx
, oldkey
,
194 error2
= WERR_BADFILE
;
196 if (!W_ERROR_IS_OK(error2
)
197 && !W_ERROR_EQUAL(error2
, WERR_BADFILE
)) {
198 DEBUG(0, ("Error occurred while getting value by name: %s\n",
199 win_errstr(error2
)));
200 talloc_free(mem_ctx
);
204 if (W_ERROR_IS_OK(error2
)
205 && (data_blob_cmp(&contents1
, &contents2
) == 0)
209 callbacks
->set_value(callback_data
, path
, name
,
213 /* Values that were deleted */
214 for (i
= 0; i
< old_num_values
; i
++) {
219 error1
= reg_key_get_value_by_index(mem_ctx
, oldkey
, i
, &name
,
221 if (!W_ERROR_IS_OK(error1
)) {
222 DEBUG(0, ("Unable to get value by index: %s\n",
223 win_errstr(error1
)));
224 talloc_free(mem_ctx
);
229 error2
= reg_key_get_value_by_name(mem_ctx
, newkey
,
230 name
, &type
, &contents
);
232 error2
= WERR_BADFILE
;
234 if (W_ERROR_IS_OK(error2
))
237 if (!W_ERROR_EQUAL(error2
, WERR_BADFILE
)) {
238 DEBUG(0, ("Error occurred while getting value by name: %s\n",
239 win_errstr(error2
)));
240 talloc_free(mem_ctx
);
244 callbacks
->del_value(callback_data
, path
, name
);
247 talloc_free(mem_ctx
);
252 * Generate diff between two registry contexts
254 _PUBLIC_ WERROR
reg_generate_diff(struct registry_context
*ctx1
,
255 struct registry_context
*ctx2
,
256 const struct reg_diff_callbacks
*callbacks
,
262 for (i
= 0; reg_predefined_keys
[i
].name
; i
++) {
263 struct registry_key
*r1
= NULL
, *r2
= NULL
;
265 error
= reg_get_predefined_key(ctx1
,
266 reg_predefined_keys
[i
].handle
, &r1
);
267 if (!W_ERROR_IS_OK(error
) &&
268 !W_ERROR_EQUAL(error
, WERR_BADFILE
)) {
269 DEBUG(0, ("Unable to open hive %s for backend 1\n",
270 reg_predefined_keys
[i
].name
));
274 error
= reg_get_predefined_key(ctx2
,
275 reg_predefined_keys
[i
].handle
, &r2
);
276 if (!W_ERROR_IS_OK(error
) &&
277 !W_ERROR_EQUAL(error
, WERR_BADFILE
)) {
278 DEBUG(0, ("Unable to open hive %s for backend 2\n",
279 reg_predefined_keys
[i
].name
));
283 error
= reg_generate_diff_key(r1
, r2
,
284 reg_predefined_keys
[i
].name
, callbacks
,
286 if (!W_ERROR_IS_OK(error
)) {
287 DEBUG(0, ("Unable to determine diff: %s\n",
292 if (callbacks
->done
!= NULL
) {
293 callbacks
->done(callback_data
);
301 _PUBLIC_ WERROR
reg_diff_load(const char *filename
,
302 struct smb_iconv_convenience
*iconv_convenience
,
303 const struct reg_diff_callbacks
*callbacks
,
309 fd
= open(filename
, O_RDONLY
, 0);
311 DEBUG(0, ("Error opening registry patch file `%s'\n",
313 return WERR_GENERAL_FAILURE
;
316 if (read(fd
, &hdr
, 4) != 4) {
317 DEBUG(0, ("Error reading registry patch file `%s'\n",
320 return WERR_GENERAL_FAILURE
;
323 /* Reset position in file */
324 lseek(fd
, 0, SEEK_SET
);
325 #if 0 /* These backends are not supported yet. */
326 if (strncmp(hdr
, "CREG", 4) == 0) {
327 /* Must be a W9x CREG Config.pol file */
328 return reg_creg_diff_load(diff
, fd
);
329 } else if (strncmp(hdr
, "regf", 4) == 0) {
330 /* Must be a REGF NTConfig.pol file */
331 return reg_regf_diff_load(diff
, fd
);
334 if (strncmp(hdr
, "PReg", 4) == 0) {
335 /* Must be a GPO Registry.pol file */
336 return reg_preg_diff_load(fd
, iconv_convenience
, callbacks
, callback_data
);
338 /* Must be a normal .REG file */
339 return reg_dotreg_diff_load(fd
, iconv_convenience
, callbacks
, callback_data
);
344 * The reg_diff_apply functions
346 static WERROR
reg_diff_apply_add_key(void *_ctx
, const char *key_name
)
348 struct registry_context
*ctx
= (struct registry_context
*)_ctx
;
349 struct registry_key
*tmp
;
353 /* Recursively create the path */
354 buf
= talloc_strdup(ctx
, key_name
);
357 while (*buf_ptr
++ != '\0' ) {
358 if (*buf_ptr
== '\\') {
360 error
= reg_key_add_abs(ctx
, ctx
, buf
, 0, NULL
, &tmp
);
362 if (!W_ERROR_EQUAL(error
, WERR_ALREADY_EXISTS
) &&
363 !W_ERROR_IS_OK(error
)) {
364 DEBUG(0, ("Error adding new key '%s': %s\n",
365 key_name
, win_errstr(error
)));
373 error
= reg_key_add_abs(ctx
, ctx
, key_name
, 0, NULL
, &tmp
);
375 if (!W_ERROR_EQUAL(error
, WERR_ALREADY_EXISTS
) &&
376 !W_ERROR_IS_OK(error
)) {
377 DEBUG(0, ("Error adding new key '%s': %s\n",
378 key_name
, win_errstr(error
)));
384 static WERROR
reg_diff_apply_del_key(void *_ctx
, const char *key_name
)
386 struct registry_context
*ctx
= (struct registry_context
*)_ctx
;
388 /* We can't proof here for success, because a common superkey could */
389 /* have been deleted before the subkey's (diff order). This removed */
390 /* therefore all children recursively and the "WERR_BADFILE" result is */
393 reg_key_del_abs(ctx
, key_name
);
398 static WERROR
reg_diff_apply_set_value(void *_ctx
, const char *path
,
399 const char *value_name
,
400 uint32_t value_type
, DATA_BLOB value
)
402 struct registry_context
*ctx
= (struct registry_context
*)_ctx
;
403 struct registry_key
*tmp
;
407 error
= reg_open_key_abs(ctx
, ctx
, path
, &tmp
);
409 if (W_ERROR_EQUAL(error
, WERR_BADFILE
)) {
410 DEBUG(0, ("Error opening key '%s'\n", path
));
415 error
= reg_val_set(tmp
, value_name
,
417 if (!W_ERROR_IS_OK(error
)) {
418 DEBUG(0, ("Error setting value '%s'\n", value_name
));
425 static WERROR
reg_diff_apply_del_value(void *_ctx
, const char *key_name
,
426 const char *value_name
)
428 struct registry_context
*ctx
= (struct registry_context
*)_ctx
;
429 struct registry_key
*tmp
;
433 error
= reg_open_key_abs(ctx
, ctx
, key_name
, &tmp
);
435 if (!W_ERROR_IS_OK(error
)) {
436 DEBUG(0, ("Error opening key '%s'\n", key_name
));
440 error
= reg_del_value(tmp
, value_name
);
441 if (!W_ERROR_IS_OK(error
)) {
442 DEBUG(0, ("Error deleting value '%s'\n", value_name
));
450 static WERROR
reg_diff_apply_del_all_values(void *_ctx
, const char *key_name
)
452 struct registry_context
*ctx
= (struct registry_context
*)_ctx
;
453 struct registry_key
*key
;
455 const char* value_name
;
457 error
= reg_open_key_abs(ctx
, ctx
, key_name
, &key
);
459 if (!W_ERROR_IS_OK(error
)) {
460 DEBUG(0, ("Error opening key '%s'\n", key_name
));
464 W_ERROR_NOT_OK_RETURN(reg_key_get_info(ctx
, key
, NULL
,
465 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
));
467 while (W_ERROR_IS_OK(reg_key_get_value_by_index(
468 ctx
, key
, 0, &value_name
, NULL
, NULL
))) {
469 error
= reg_del_value(key
, value_name
);
470 if (!W_ERROR_IS_OK(error
)) {
471 DEBUG(0, ("Error deleting value '%s'\n", value_name
));
480 * Apply diff to a registry context
482 _PUBLIC_ WERROR
reg_diff_apply(struct registry_context
*ctx
,
483 struct smb_iconv_convenience
*iconv_convenience
,
484 const char *filename
)
486 struct reg_diff_callbacks callbacks
;
488 callbacks
.add_key
= reg_diff_apply_add_key
;
489 callbacks
.del_key
= reg_diff_apply_del_key
;
490 callbacks
.set_value
= reg_diff_apply_set_value
;
491 callbacks
.del_value
= reg_diff_apply_del_value
;
492 callbacks
.del_all_values
= reg_diff_apply_del_all_values
;
493 callbacks
.done
= NULL
;
495 return reg_diff_load(filename
, iconv_convenience
,