Fixed the patchfile tests and tidy up the patchfile backends.
[Samba.git] / source4 / lib / registry / patchfile.c
blobeaeef341bbace37a988ab4e8610e3163b72f0737
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
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.
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.
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/>.
22 #include "includes.h"
23 #include "lib/registry/registry.h"
24 #include "system/filesys.h"
25 #include "param/param.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 occured while getting key info: %s\n",
62 win_errstr(error)));
63 return error;
65 } else {
66 old_num_subkeys = 0;
67 old_num_values = 0;
70 /* Subkeys that were deleted */
71 for (i = 0; i < old_num_subkeys; i++) {
72 error1 = reg_key_get_subkey_by_index(mem_ctx, oldkey, i,
73 &keyname1,
74 NULL, NULL);
75 if (!W_ERROR_IS_OK(error1)) {
76 DEBUG(0, ("Error occured while getting subkey by index: %s\n",
77 win_errstr(error2)));
78 continue;
81 if (newkey != NULL) {
82 error2 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
84 if (W_ERROR_IS_OK(error2))
85 continue;
86 } else {
87 error2 = WERR_BADFILE;
88 t2 = NULL;
91 if (!W_ERROR_EQUAL(error2, WERR_BADFILE)) {
92 DEBUG(0, ("Error occured while getting subkey by name: %s\n",
93 win_errstr(error2)));
94 talloc_free(mem_ctx);
95 return error2;
98 /* newkey didn't have such a subkey, add del diff */
99 tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
100 callbacks->del_key(callback_data, tmppath);
101 talloc_free(tmppath);
104 if (newkey != NULL) {
105 error = reg_key_get_info(mem_ctx, newkey, NULL,
106 &new_num_subkeys, &new_num_values,
107 NULL, NULL, NULL, NULL);
108 if (!W_ERROR_IS_OK(error)) {
109 DEBUG(0, ("Error occured while getting key info: %s\n",
110 win_errstr(error)));
111 return error;
113 } else {
114 new_num_subkeys = 0;
115 new_num_values = 0;
118 /* Subkeys that were added */
119 for(i = 0; i < new_num_subkeys; i++) {
120 error1 = reg_key_get_subkey_by_index(mem_ctx, newkey,
121 i, &keyname1,
122 NULL, NULL);
123 if (!W_ERROR_IS_OK(error1)) {
124 DEBUG(0, ("Error occured while getting subkey by index: %s\n",
125 win_errstr(error1)));
126 talloc_free(mem_ctx);
127 return error1;
130 if (oldkey != NULL) {
131 error2 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
133 if (W_ERROR_IS_OK(error2))
134 continue;
135 } else {
136 t1 = NULL;
137 error2 = WERR_BADFILE;
140 if (!W_ERROR_EQUAL(error2, WERR_BADFILE)) {
141 DEBUG(0, ("Error occured while getting subkey by name: %s\n",
142 win_errstr(error2)));
143 talloc_free(mem_ctx);
144 return error2;
147 /* oldkey didn't have such a subkey, add add diff */
148 tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
149 callbacks->add_key(callback_data, tmppath);
151 W_ERROR_NOT_OK_RETURN(
152 reg_open_key(mem_ctx, newkey, keyname1, &t2));
154 reg_generate_diff_key(t1, t2, tmppath,
155 callbacks, callback_data);
156 talloc_free(tmppath);
159 /* Values that were changed */
160 for(i = 0; i < new_num_values; i++) {
161 const char *name;
162 uint32_t type1, type2;
163 DATA_BLOB contents1, contents2;
165 error1 = reg_key_get_value_by_index(mem_ctx, newkey, i,
166 &name, &type1, &contents1);
167 if (!W_ERROR_IS_OK(error1)) {
168 DEBUG(0, ("Unable to get key by index: %s\n",
169 win_errstr(error1)));
170 talloc_free(mem_ctx);
171 return error1;
174 if (oldkey != NULL) {
175 error2 = reg_key_get_value_by_name(mem_ctx, oldkey,
176 name, &type2,
177 &contents2);
178 } else
179 error2 = WERR_BADFILE;
181 if(!W_ERROR_IS_OK(error2) &&
182 !W_ERROR_EQUAL(error2, WERR_BADFILE)) {
183 DEBUG(0, ("Error occured while getting value by name: %s\n",
184 win_errstr(error2)));
185 talloc_free(mem_ctx);
186 return error2;
189 if (W_ERROR_IS_OK(error2) &&
190 data_blob_cmp(&contents1, &contents2) == 0)
191 continue;
193 callbacks->set_value(callback_data, path, name,
194 type1, contents1);
197 /* Values that were deleted */
198 for (i = 0; i < old_num_values; i++) {
199 const char *name;
200 error1 = reg_key_get_value_by_index(mem_ctx, oldkey, i, &name,
201 NULL, NULL);
202 if (!W_ERROR_IS_OK(error1)) {
203 DEBUG(0, ("Error ocurred getting value by index: %s\n",
204 win_errstr(error1)));
205 talloc_free(mem_ctx);
206 return error1;
209 error2 = reg_key_get_value_by_name(mem_ctx, newkey, name, NULL,
210 NULL);
212 if (W_ERROR_IS_OK(error2))
213 continue;
215 if (!W_ERROR_EQUAL(error2, WERR_BADFILE)) {
216 DEBUG(0, ("Error occured while getting value by name: %s\n",
217 win_errstr(error2)));
218 return error2;
221 callbacks->del_value(callback_data, path, name);
224 talloc_free(mem_ctx);
225 return WERR_OK;
229 * Generate diff between two registry contexts
231 _PUBLIC_ WERROR reg_generate_diff(struct registry_context *ctx1,
232 struct registry_context *ctx2,
233 const struct reg_diff_callbacks *callbacks,
234 void *callback_data)
236 int i;
237 WERROR error;
239 for(i = HKEY_FIRST; i <= HKEY_LAST; i++) {
240 struct registry_key *r1 = NULL, *r2 = NULL;
241 error = reg_get_predefined_key(ctx1, i, &r1);
242 if (!W_ERROR_IS_OK(error) &&
243 !W_ERROR_EQUAL(error, WERR_BADFILE)) {
244 DEBUG(0, ("Unable to open hive %s for backend 1\n",
245 reg_get_predef_name(i)));
248 error = reg_get_predefined_key(ctx2, i, &r2);
249 if (!W_ERROR_IS_OK(error) &&
250 !W_ERROR_EQUAL(error, WERR_BADFILE)) {
251 DEBUG(0, ("Unable to open hive %s for backend 2\n",
252 reg_get_predef_name(i)));
255 if (r1 == NULL && r2 == NULL)
256 continue;
258 error = reg_generate_diff_key(r1, r2, reg_get_predef_name(i),
259 callbacks, callback_data);
260 if (!W_ERROR_IS_OK(error)) {
261 DEBUG(0, ("Unable to determine diff: %s\n",
262 win_errstr(error)));
263 return error;
266 if (callbacks->done != NULL) {
267 callbacks->done(callback_data);
269 return WERR_OK;
273 * Load diff file
275 _PUBLIC_ WERROR reg_diff_load(const char *filename,
276 struct smb_iconv_convenience *iconv_convenience,
277 const struct reg_diff_callbacks *callbacks,
278 void *callback_data)
280 int fd;
281 char hdr[4];
283 fd = open(filename, O_RDONLY, 0);
284 if (fd == -1) {
285 DEBUG(0, ("Error opening registry patch file `%s'\n",
286 filename));
287 return WERR_GENERAL_FAILURE;
290 if (read(fd, &hdr, 4) != 4) {
291 DEBUG(0, ("Error reading registry patch file `%s'\n",
292 filename));
293 return WERR_GENERAL_FAILURE;
296 /* Reset position in file */
297 lseek(fd, 0, SEEK_SET);
298 #if 0 /* These backends are not supported yet. */
299 if (strncmp(hdr, "CREG", 4) == 0) {
300 /* Must be a W9x CREG Config.pol file */
301 return reg_creg_diff_load(diff, fd);
302 } else if (strncmp(hdr, "regf", 4) == 0) {
303 /* Must be a REGF NTConfig.pol file */
304 return reg_regf_diff_load(diff, fd);
305 } else
306 #endif
307 if (strncmp(hdr, "PReg", 4) == 0) {
308 /* Must be a GPO Registry.pol file */
309 return reg_preg_diff_load(fd, iconv_convenience, callbacks, callback_data);
310 } else {
311 /* Must be a normal .REG file */
312 return reg_dotreg_diff_load(fd, iconv_convenience, callbacks, callback_data);
317 * The reg_diff_apply functions
319 static WERROR reg_diff_apply_add_key(void *_ctx, const char *key_name)
321 struct registry_context *ctx = (struct registry_context *)_ctx;
322 struct registry_key *tmp;
323 WERROR error;
325 error = reg_key_add_abs(ctx, ctx, key_name, 0, NULL, &tmp);
327 if (!W_ERROR_EQUAL(error, WERR_ALREADY_EXISTS) &&
328 !W_ERROR_IS_OK(error)) {
329 DEBUG(0, ("Error adding new key '%s': %s\n",
330 key_name, win_errstr(error)));
331 return error;
333 return WERR_OK;
336 static WERROR reg_diff_apply_del_key(void *_ctx, const char *key_name)
338 struct registry_context *ctx = (struct registry_context *)_ctx;
339 WERROR error;
341 error = reg_key_del_abs(ctx, key_name);
343 if(!W_ERROR_IS_OK(error)) {
344 DEBUG(0, ("Unable to delete key '%s'\n", key_name));
345 return error;
348 return WERR_OK;
351 static WERROR reg_diff_apply_set_value(void *_ctx, const char *path,
352 const char *value_name,
353 uint32_t value_type, DATA_BLOB value)
355 struct registry_context *ctx = (struct registry_context *)_ctx;
356 struct registry_key *tmp;
357 WERROR error;
359 /* Open key */
360 error = reg_open_key_abs(ctx, ctx, path, &tmp);
362 if (W_ERROR_EQUAL(error, WERR_BADFILE)) {
363 DEBUG(0, ("Error opening key '%s'\n", path));
364 return error;
367 /* Set value */
368 error = reg_val_set(tmp, value_name,
369 value_type, value);
370 if (!W_ERROR_IS_OK(error)) {
371 DEBUG(0, ("Error setting value '%s'\n", value_name));
372 return error;
375 return WERR_OK;
378 static WERROR reg_diff_apply_del_value(void *_ctx, const char *key_name,
379 const char *value_name)
381 struct registry_context *ctx = (struct registry_context *)_ctx;
382 struct registry_key *tmp;
383 WERROR error;
385 /* Open key */
386 error = reg_open_key_abs(ctx, ctx, key_name, &tmp);
388 if (!W_ERROR_IS_OK(error)) {
389 DEBUG(0, ("Error opening key '%s'\n", key_name));
390 return error;
393 error = reg_del_value(tmp, value_name);
394 if (!W_ERROR_IS_OK(error)) {
395 DEBUG(0, ("Error deleting value '%s'\n", value_name));
396 return error;
400 return WERR_OK;
403 static WERROR reg_diff_apply_del_all_values(void *_ctx, const char *key_name)
405 struct registry_context *ctx = (struct registry_context *)_ctx;
406 struct registry_key *key;
407 WERROR error;
408 int i;
409 uint32_t num_values;
411 error = reg_open_key_abs(ctx, ctx, key_name, &key);
413 if (!W_ERROR_IS_OK(error)) {
414 DEBUG(0, ("Error opening key '%s'\n", key_name));
415 return error;
418 W_ERROR_NOT_OK_RETURN(reg_key_get_info(ctx, key, NULL,
419 NULL, &num_values, NULL, NULL, NULL, NULL));
421 for (i = 0; i < num_values; i++) {
422 const char *name;
423 W_ERROR_NOT_OK_RETURN(reg_key_get_value_by_index(ctx, key, i,
424 &name,
425 NULL, NULL));
426 W_ERROR_NOT_OK_RETURN(reg_del_value(key, name));
429 return WERR_OK;
433 * Apply diff to a registry context
435 _PUBLIC_ WERROR reg_diff_apply(struct registry_context *ctx, const char *filename)
437 struct reg_diff_callbacks callbacks;
439 callbacks.add_key = reg_diff_apply_add_key;
440 callbacks.del_key = reg_diff_apply_del_key;
441 callbacks.set_value = reg_diff_apply_set_value;
442 callbacks.del_value = reg_diff_apply_del_value;
443 callbacks.del_all_values = reg_diff_apply_del_all_values;
444 callbacks.done = NULL;
446 return reg_diff_load(filename, lp_iconv_convenience(global_loadparm),
447 &callbacks, ctx);