Remove yet more global_loadparm instances.
[jelmer/samba4-debian.git] / source / lib / registry / patchfile.c
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
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.
12
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.
17
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/>.
20 */
21
22 #include "includes.h"
23 #include "lib/registry/patchfile.h"
24 #include "lib/registry/registry.h"
25 #include "system/filesys.h"
26 #include "param/param.h"
27
28
29 _PUBLIC_ WERROR reg_preg_diff_load(int fd,
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  */
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)
46 {
47         int i;
48         struct registry_key *t1, *t2;
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 occured while getting key info: %s\n",
62                                 win_errstr(error)));
63                         return error;
64                 }
65         } else {
66                 old_num_subkeys = 0;
67                 old_num_values = 0;
68         }
69
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;
79                 }
80
81                 if (newkey != NULL) {
82                         error2 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
83
84                         if (W_ERROR_IS_OK(error2))
85                                 continue;
86                 } else {
87                         error2 = WERR_BADFILE;
88                         t2 = NULL;
89                 }
90
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;
96                 }
97
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);
102         }
103
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;
112                 }
113         } else {
114                 new_num_subkeys = 0;
115                 new_num_values = 0;
116         }
117
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;
128                 }
129
130                 if (oldkey != NULL) {
131                         error2 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
132
133                         if (W_ERROR_IS_OK(error2))
134                                 continue;
135                 } else {
136                         t1 = NULL;
137                         error2 = WERR_BADFILE;
138                 }
139
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;
145                 }
146
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);
150
151                 W_ERROR_NOT_OK_RETURN(
152                                 reg_open_key(mem_ctx, newkey, keyname1, &t2));
153
154                 reg_generate_diff_key(t1, t2, tmppath,
155                                       callbacks, callback_data);
156                 talloc_free(tmppath);
157         }
158
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;
164
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;
172                 }
173
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;
180
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;
187                 }
188
189                 if (W_ERROR_IS_OK(error2) &&
190                     data_blob_cmp(&contents1, &contents2) == 0)
191                         continue;
192
193                 callbacks->set_value(callback_data, path, name,
194                                      type1, contents1);
195         }
196
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;
207                 }
208
209                 error2 = reg_key_get_value_by_name(mem_ctx, newkey, name, NULL,
210                                                    NULL);
211
212                 if (W_ERROR_IS_OK(error2))
213                         continue;
214
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;
219                 }
220
221                 callbacks->del_value(callback_data, path, name);
222         }
223
224         talloc_free(mem_ctx);
225         return WERR_OK;
226 }
227
228 /**
229  * Generate diff between two registry contexts
230  */
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)
235 {
236         int i;
237         WERROR error;
238
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)));
246                 }
247
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)));
253                 }
254
255                 if (r1 == NULL && r2 == NULL)
256                         continue;
257
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;
264                 }
265         }
266         if (callbacks->done != NULL) {
267                 callbacks->done(callback_data);
268         }
269         return WERR_OK;
270 }
271
272 /**
273  * Load diff file
274  */
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)
279 {
280         int fd;
281         char hdr[4];
282
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;
288         }
289
290         if (read(fd, &hdr, 4) != 4) {
291                 DEBUG(0, ("Error reading registry patch file `%s'\n",
292                         filename));
293                 return WERR_GENERAL_FAILURE;
294         }
295
296         /* Reset position in file */
297         lseek(fd, 0, SEEK_SET);
298 #if 0
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, callbacks, callback_data);
310         } else {
311                 /* Must be a normal .REG file */
312                 return reg_dotreg_diff_load(fd, iconv_convenience, callbacks, callback_data);
313         }
314 }
315
316 /**
317  * The reg_diff_apply functions
318  */
319 static WERROR reg_diff_apply_add_key(void *_ctx, const char *key_name)
320 {
321         struct registry_context *ctx = (struct registry_context *)_ctx;
322         struct registry_key *tmp;
323         WERROR error;
324
325         error = reg_key_add_abs(ctx, ctx, key_name, 0, NULL, &tmp);
326
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;
332         }
333         return WERR_OK;
334 }
335
336 static WERROR reg_diff_apply_del_key(void *_ctx, const char *key_name)
337 {
338         struct registry_context *ctx = (struct registry_context *)_ctx;
339         WERROR error;
340
341         error = reg_key_del_abs(ctx, key_name);
342
343         if(!W_ERROR_IS_OK(error)) {
344                 DEBUG(0, ("Unable to delete key '%s'\n", key_name));
345                 return error;
346         }
347
348         return WERR_OK;
349 }
350
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)
354 {
355         struct registry_context *ctx = (struct registry_context *)_ctx;
356         struct registry_key *tmp;
357         WERROR error;
358
359         /* Open key */
360         error = reg_open_key_abs(ctx, ctx, path, &tmp);
361
362         if (W_ERROR_EQUAL(error, WERR_BADFILE)) {
363                 DEBUG(0, ("Error opening key '%s'\n", path));
364                 return error;
365         }
366
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;
373         }
374
375         return WERR_OK;
376 }
377
378 static WERROR reg_diff_apply_del_value(void *_ctx, const char *key_name,
379                                        const char *value_name)
380 {
381         struct registry_context *ctx = (struct registry_context *)_ctx;
382         struct registry_key *tmp;
383         WERROR error;
384
385         /* Open key */
386         error = reg_open_key_abs(ctx, ctx, key_name, &tmp);
387
388         if (!W_ERROR_IS_OK(error)) {
389                 DEBUG(0, ("Error opening key '%s'\n", key_name));
390                 return error;
391         }
392
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;
397         }
398
399
400         return WERR_OK;
401 }
402
403 static WERROR reg_diff_apply_del_all_values(void *_ctx, const char *key_name)
404 {
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;
410
411         error = reg_open_key_abs(ctx, ctx, key_name, &key);
412
413         if (!W_ERROR_IS_OK(error)) {
414                 DEBUG(0, ("Error opening key '%s'\n", key_name));
415                 return error;
416         }
417
418         W_ERROR_NOT_OK_RETURN(reg_key_get_info(ctx, key, NULL,
419                                NULL, &num_values, NULL, NULL, NULL, NULL));
420
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));
427         }
428
429         return WERR_OK;
430 }
431
432 /**
433  * Apply diff to a registry context
434  */
435 _PUBLIC_ WERROR reg_diff_apply(struct registry_context *ctx, const char *filename)
436 {
437         struct reg_diff_callbacks callbacks;
438
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;
445
446         return reg_diff_load(filename, lp_iconv_convenience(global_loadparm), 
447                              &callbacks, ctx);
448 }