source: branches/samba-3.5.x/source4/lib/registry/patchfile.c

Last change on this file was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

File size: 13.0 KB
Line 
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 Copyright (C) Matthias Dieter Wallnöfer 2008
8
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.
13
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.
18
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/>.
21*/
22
23#include "includes.h"
24#include "lib/registry/registry.h"
25#include "system/filesys.h"
26
27
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);
32
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);
37
38/*
39 * Generate difference between two keys
40 */
41WERROR 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)
46{
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;
55
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;
65 }
66 } else {
67 old_num_subkeys = 0;
68 old_num_values = 0;
69 }
70
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;
79 }
80
81 if (newkey != NULL) {
82 error2 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
83 } else {
84 error2 = WERR_BADFILE;
85 t2 = NULL;
86 }
87
88 if (!W_ERROR_IS_OK(error2) && !W_ERROR_EQUAL(error2, WERR_BADFILE)) {
89 DEBUG(0, ("Error occured while getting subkey by name: %s\n",
90 win_errstr(error2)));
91 talloc_free(mem_ctx);
92 return error2;
93 }
94
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);
100
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 occured while getting subkey by name: %s\n",
105 win_errstr(error1)));
106 talloc_free(mem_ctx);
107 return error1;
108 }
109 reg_generate_diff_key(t1, t2, tmppath, callbacks, callback_data);
110
111 talloc_free(tmppath);
112 }
113
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;
123 }
124 } else {
125 new_num_subkeys = 0;
126 new_num_values = 0;
127 }
128
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;
138 }
139
140 if (oldkey != NULL) {
141 error2 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
142
143 if (W_ERROR_IS_OK(error2))
144 continue;
145 } else {
146 error2 = WERR_BADFILE;
147 t1 = NULL;
148 }
149
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;
155 }
156
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);
160
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 occured while getting subkey by name: %s\n",
165 win_errstr(error1)));
166 talloc_free(mem_ctx);
167 return error1;
168 }
169 reg_generate_diff_key(t1, t2, tmppath, callbacks, callback_data);
170
171 talloc_free(tmppath);
172 }
173
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;
179
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;
187 }
188
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;
195
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;
202 }
203
204 if (W_ERROR_IS_OK(error2)
205 && (data_blob_cmp(&contents1, &contents2) == 0)
206 && (type1 == type2))
207 continue;
208
209 callbacks->set_value(callback_data, path, name,
210 type1, contents1);
211 }
212
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;
218
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;
226 }
227
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;
233
234 if (W_ERROR_IS_OK(error2))
235 continue;
236
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;
242 }
243
244 callbacks->del_value(callback_data, path, name);
245 }
246
247 talloc_free(mem_ctx);
248 return WERR_OK;
249}
250
251/**
252 * Generate diff between two registry contexts
253 */
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)
258{
259 int i;
260 WERROR error;
261
262 for (i = 0; reg_predefined_keys[i].name; i++) {
263 struct registry_key *r1 = NULL, *r2 = NULL;
264
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;
272 }
273
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;
281 }
282
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;
290 }
291 }
292 if (callbacks->done != NULL) {
293 callbacks->done(callback_data);
294 }
295 return WERR_OK;
296}
297
298/**
299 * Load diff file
300 */
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)
305{
306 int fd;
307 char hdr[4];
308
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;
314 }
315
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;
321 }
322
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);
340 }
341}
342
343/**
344 * The reg_diff_apply functions
345 */
346static WERROR reg_diff_apply_add_key(void *_ctx, const char *key_name)
347{
348 struct registry_context *ctx = (struct registry_context *)_ctx;
349 struct registry_key *tmp;
350 char *buf, *buf_ptr;
351 WERROR error;
352
353 /* Recursively create the path */
354 buf = talloc_strdup(ctx, key_name);
355 buf_ptr = buf;
356
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);
361
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;
367 }
368 *buf_ptr++ = '\\';
369 }
370 }
371
372 /* Add the key */
373 error = reg_key_add_abs(ctx, ctx, key_name, 0, NULL, &tmp);
374
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;
380 }
381 return WERR_OK;
382}
383
384static WERROR reg_diff_apply_del_key(void *_ctx, const char *key_name)
385{
386 struct registry_context *ctx = (struct registry_context *)_ctx;
387
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 childs recursively and the "WERR_BADFILE" result is */
391 /* expected. */
392
393 reg_key_del_abs(ctx, key_name);
394
395 return WERR_OK;
396}
397
398static WERROR reg_diff_apply_set_value(void *_ctx, const char *path,
399 const char *value_name,
400 uint32_t value_type, DATA_BLOB value)
401{
402 struct registry_context *ctx = (struct registry_context *)_ctx;
403 struct registry_key *tmp;
404 WERROR error;
405
406 /* Open key */
407 error = reg_open_key_abs(ctx, ctx, path, &tmp);
408
409 if (W_ERROR_EQUAL(error, WERR_BADFILE)) {
410 DEBUG(0, ("Error opening key '%s'\n", path));
411 return error;
412 }
413
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;
420 }
421
422 return WERR_OK;
423}
424
425static WERROR reg_diff_apply_del_value(void *_ctx, const char *key_name,
426 const char *value_name)
427{
428 struct registry_context *ctx = (struct registry_context *)_ctx;
429 struct registry_key *tmp;
430 WERROR error;
431
432 /* Open key */
433 error = reg_open_key_abs(ctx, ctx, key_name, &tmp);
434
435 if (!W_ERROR_IS_OK(error)) {
436 DEBUG(0, ("Error opening key '%s'\n", key_name));
437 return error;
438 }
439
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;
444 }
445
446
447 return WERR_OK;
448}
449
450static WERROR reg_diff_apply_del_all_values(void *_ctx, const char *key_name)
451{
452 struct registry_context *ctx = (struct registry_context *)_ctx;
453 struct registry_key *key;
454 WERROR error;
455 const char* value_name;
456
457 error = reg_open_key_abs(ctx, ctx, key_name, &key);
458
459 if (!W_ERROR_IS_OK(error)) {
460 DEBUG(0, ("Error opening key '%s'\n", key_name));
461 return error;
462 }
463
464 W_ERROR_NOT_OK_RETURN(reg_key_get_info(ctx, key, NULL,
465 NULL, NULL, NULL, NULL, NULL, NULL));
466
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;
473 }
474 }
475
476 return WERR_OK;
477}
478
479/**
480 * Apply diff to a registry context
481 */
482_PUBLIC_ WERROR reg_diff_apply(struct registry_context *ctx,
483 struct smb_iconv_convenience *iconv_convenience,
484 const char *filename)
485{
486 struct reg_diff_callbacks callbacks;
487
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;
494
495 return reg_diff_load(filename, iconv_convenience,
496 &callbacks, ctx);
497}
Note: See TracBrowser for help on using the repository browser.