Spelling fixes for source4/lib/registry.
[Samba/cd1.git] / source4 / lib / registry / patchfile.c
blob0631e760cb9a6608fbef41839293761e7675567b
1 /*
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/>.
23 #include "includes.h"
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,
31 void *callback_data);
33 _PUBLIC_ WERROR reg_dotreg_diff_load(int fd,
34 struct smb_iconv_convenience *iconv_convenience,
35 const struct reg_diff_callbacks *callbacks,
36 void *callback_data);
39 * Generate difference between two keys
41 WERROR reg_generate_diff_key(struct registry_key *oldkey,
42 struct registry_key *newkey,
43 const char *path,
44 const struct reg_diff_callbacks *callbacks,
45 void *callback_data)
47 int i;
48 struct registry_key *t1 = NULL, *t2 = NULL;
49 char *tmppath;
50 const char *keyname1;
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;
56 if (oldkey != NULL) {
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",
62 win_errstr(error)));
63 talloc_free(mem_ctx);
64 return error;
66 } else {
67 old_num_subkeys = 0;
68 old_num_values = 0;
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",
77 win_errstr(error1)));
78 continue;
81 if (newkey != NULL) {
82 error2 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
83 } else {
84 error2 = WERR_BADFILE;
85 t2 = NULL;
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",
90 win_errstr(error2)));
91 talloc_free(mem_ctx);
92 return error2;
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);
107 return error1;
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",
120 win_errstr(error)));
121 talloc_free(mem_ctx);
122 return error;
124 } else {
125 new_num_subkeys = 0;
126 new_num_values = 0;
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);
137 return error1;
140 if (oldkey != NULL) {
141 error2 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
143 if (W_ERROR_IS_OK(error2))
144 continue;
145 } else {
146 error2 = WERR_BADFILE;
147 t1 = NULL;
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);
154 return error2;
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);
167 return error1;
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++) {
176 const char *name;
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);
186 return error1;
189 if (oldkey != NULL) {
190 error2 = reg_key_get_value_by_name(mem_ctx, oldkey,
191 name, &type2,
192 &contents2);
193 } else
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);
201 return error2;
204 if (W_ERROR_IS_OK(error2)
205 && (data_blob_cmp(&contents1, &contents2) == 0)
206 && (type1 == type2))
207 continue;
209 callbacks->set_value(callback_data, path, name,
210 type1, contents1);
213 /* Values that were deleted */
214 for (i = 0; i < old_num_values; i++) {
215 const char *name;
216 uint32_t type;
217 DATA_BLOB contents;
219 error1 = reg_key_get_value_by_index(mem_ctx, oldkey, i, &name,
220 &type, &contents);
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);
225 return error1;
228 if (newkey != NULL)
229 error2 = reg_key_get_value_by_name(mem_ctx, newkey,
230 name, &type, &contents);
231 else
232 error2 = WERR_BADFILE;
234 if (W_ERROR_IS_OK(error2))
235 continue;
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);
241 return error2;
244 callbacks->del_value(callback_data, path, name);
247 talloc_free(mem_ctx);
248 return WERR_OK;
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,
257 void *callback_data)
259 int i;
260 WERROR error;
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));
271 continue;
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));
280 continue;
283 error = reg_generate_diff_key(r1, r2,
284 reg_predefined_keys[i].name, callbacks,
285 callback_data);
286 if (!W_ERROR_IS_OK(error)) {
287 DEBUG(0, ("Unable to determine diff: %s\n",
288 win_errstr(error)));
289 return error;
292 if (callbacks->done != NULL) {
293 callbacks->done(callback_data);
295 return WERR_OK;
299 * Load diff file
301 _PUBLIC_ WERROR reg_diff_load(const char *filename,
302 struct smb_iconv_convenience *iconv_convenience,
303 const struct reg_diff_callbacks *callbacks,
304 void *callback_data)
306 int fd;
307 char hdr[4];
309 fd = open(filename, O_RDONLY, 0);
310 if (fd == -1) {
311 DEBUG(0, ("Error opening registry patch file `%s'\n",
312 filename));
313 return WERR_GENERAL_FAILURE;
316 if (read(fd, &hdr, 4) != 4) {
317 DEBUG(0, ("Error reading registry patch file `%s'\n",
318 filename));
319 close(fd);
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);
332 } else
333 #endif
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);
337 } else {
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;
350 char *buf, *buf_ptr;
351 WERROR error;
353 /* Recursively create the path */
354 buf = talloc_strdup(ctx, key_name);
355 buf_ptr = buf;
357 while (*buf_ptr++ != '\0' ) {
358 if (*buf_ptr == '\\') {
359 *buf_ptr = '\0';
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)));
366 return error;
368 *buf_ptr++ = '\\';
372 /* Add the key */
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)));
379 return error;
381 return WERR_OK;
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 */
391 /* expected. */
393 reg_key_del_abs(ctx, key_name);
395 return WERR_OK;
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;
404 WERROR error;
406 /* Open key */
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));
411 return error;
414 /* Set value */
415 error = reg_val_set(tmp, value_name,
416 value_type, value);
417 if (!W_ERROR_IS_OK(error)) {
418 DEBUG(0, ("Error setting value '%s'\n", value_name));
419 return error;
422 return WERR_OK;
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;
430 WERROR error;
432 /* Open key */
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));
437 return error;
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));
443 return error;
447 return WERR_OK;
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;
454 WERROR error;
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));
461 return error;
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));
472 return error;
476 return WERR_OK;
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,
496 &callbacks, ctx);