s3-utils: net - Fix one error/usage message
[Samba/gebeck_regimport.git] / source3 / utils / net_registry_check.c
blob22d18a68afab6f1a713b7f9c9459a53667de86af
1 /*
2 * Samba Unix/Linux SMB client library
4 * Copyright (C) Gregor Beck 2011
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 /**
21 * @brief Check the registry database.
22 * @author Gregor Beck <gb@sernet.de>
23 * @date Mar 2011
26 #include "net_registry_check.h"
28 #include "includes.h"
29 #include "system/filesys.h"
30 #include "lib/dbwrap/dbwrap.h"
31 #include "lib/dbwrap/dbwrap_open.h"
32 #include "lib/dbwrap/dbwrap_rbt.h"
33 #include "net.h"
34 #include "libcli/security/dom_sid.h"
35 #include "libcli/security/secdesc.h"
36 #include "cbuf.h"
37 #include "srprs.h"
38 #include <termios.h>
39 #include "util_tdb.h"
40 #include "registry/reg_db.h"
41 #include "libcli/registry/util_reg.h"
42 #include "registry/reg_parse_internal.h"
43 #include "interact.h"
46 check tree:
47 + every key has a subkeylist
48 + every key is referenced by the subkeylist of its parent
49 check path:
50 + starts with valid hive
51 + UTF-8 (option to convert ???)
52 + only uppercase
53 + separator ???
54 check value:
55 + REG_DWORD has size 4
56 + REG_QWORD has size 8
57 + STRINGS are zero terminated UTF-16
60 struct regval {
61 char *name;
62 uint32_t type;
63 DATA_BLOB data;
66 struct regkey {
67 char *name;
68 char *path;
69 bool has_subkeylist;
70 bool needs_update;
71 struct regkey *parent;
72 size_t nsubkeys;
73 struct regkey **subkeys;
74 size_t nvalues;
75 struct regval **values;
76 struct security_descriptor *sd;
79 struct check_ctx {
80 char *fname;
81 struct check_options opt;
83 uint32_t version;
84 char sep;
85 struct db_context *idb;
86 struct db_context *odb;
88 struct regkey *root; /*dummy key to hold all basekeys*/
89 struct db_context *reg;
90 struct db_context *del;
92 bool transaction;
93 char auto_action;
94 char default_action;
97 static void* talloc_array_append(void *mem_ctx, void* array[], void *ptr)
99 size_t size = array ? talloc_array_length(array) : 1;
100 void **tmp = talloc_realloc(mem_ctx, array, void*, size + 1);
101 if (tmp == NULL) {
102 talloc_free(array);
103 return NULL;
105 tmp[size-1] = ptr;
106 tmp[size] = NULL;
107 return tmp;
110 static void regkey_add_subkey(struct regkey *key, struct regkey *subkey)
112 key->subkeys = (struct regkey**)
113 talloc_array_append(key, (void**)key->subkeys, subkey);
114 if (key->subkeys != NULL) {
115 key->nsubkeys++;
119 static struct regval* regval_copy(TALLOC_CTX *mem_ctx, const struct regval *val)
121 struct regval *ret = talloc_zero(mem_ctx, struct regval);
122 if (ret == NULL) {
123 goto fail;
126 ret->name = talloc_strdup(ret, val->name);
127 if (ret->name == NULL) {
128 goto fail;
131 ret->data = data_blob_dup_talloc(ret, val->data);
132 if (ret->data.data == NULL) {
133 goto fail;
136 ret->type = val->type;
138 return ret;
139 fail:
140 talloc_free(ret);
141 return NULL;
144 static void regkey_add_regval(struct regkey *key, struct regval *val)
146 key->values = (struct regval**)
147 talloc_array_append(key, (void**)key->values, val);
148 if (key->values != NULL) {
149 key->nvalues++;
153 static bool tdb_data_read_uint32(TDB_DATA *buf, uint32_t *result)
155 const size_t len = sizeof(uint32_t);
156 if (buf->dsize >= len) {
157 *result = IVAL(buf->dptr, 0);
158 buf->dptr += len;
159 buf->dsize -= len;
160 return true;
162 return false;
165 static bool tdb_data_read_cstr(TDB_DATA *buf, char **result)
167 const size_t len = strnlen((char*)buf->dptr, buf->dsize) + 1;
168 if (buf->dsize >= len) {
169 *result = (char*)buf->dptr;
170 buf->dptr += len;
171 buf->dsize -= len;
172 return true;
174 return false;
177 static bool tdb_data_read_blob(TDB_DATA *buf, DATA_BLOB *result)
179 TDB_DATA tmp = *buf;
180 uint32_t len;
181 if (!tdb_data_read_uint32(&tmp, &len)) {
182 return false;
184 if (tmp.dsize >= len) {
185 *buf = tmp;
186 result->data = tmp.dptr;
187 result->length = len;
188 buf->dptr += len;
189 buf->dsize -= len;
190 return true;
192 return false;
195 static bool tdb_data_read_regval(TDB_DATA *buf, struct regval *result)
197 TDB_DATA tmp = *buf;
198 struct regval value;
199 if (!tdb_data_read_cstr(&tmp, &value.name)
200 || !tdb_data_read_uint32(&tmp, &value.type)
201 || !tdb_data_read_blob(&tmp, &value.data))
203 return false;
205 *buf = tmp;
206 *result = value;
207 return true;
210 static bool tdb_data_is_empty(TDB_DATA d) {
211 return (d.dptr == NULL) || (d.dsize == 0);
214 static bool tdb_data_is_cstr(TDB_DATA d) {
215 if (tdb_data_is_empty(d) || (d.dptr[d.dsize-1] != '\0')) {
216 return false;
218 return rawmemchr(d.dptr, '\0') == &d.dptr[d.dsize-1];
221 static char* tdb_data_print(TALLOC_CTX *mem_ctx, TDB_DATA d)
223 if (!tdb_data_is_empty(d)) {
224 char *ret = NULL;
225 cbuf *ost = cbuf_new(mem_ctx);
226 int len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize);
227 if (len != -1) {
228 cbuf_swapptr(ost, &ret, 0);
229 talloc_steal(mem_ctx, ret);
231 talloc_free(ost);
232 return ret;
234 return talloc_strdup(mem_ctx, "<NULL>");
238 static TDB_DATA cbuf_make_tdb_data(cbuf *b)
240 return make_tdb_data((void*)cbuf_gets(b, 0), cbuf_getpos(b));
243 static void remove_all(char *str, char c)
245 char *out=str;
246 while (*str) {
247 if (*str != c) {
248 *out = *str;
249 out++;
251 str++;
253 *out = '\0';
256 static char* parent_path(const char *path, char sep)
258 const char *p = strrchr(path, sep);
259 return p ? talloc_strndup(talloc_tos(), path, p-path) : NULL;
262 /* return the regkey corresponding to path, create if not yet existing */
263 static struct regkey*
264 check_ctx_lookup_key(struct check_ctx *ctx, const char *path) {
265 struct regkey *ret = NULL;
266 NTSTATUS status;
267 TDB_DATA val;
269 if ( path == NULL) {
270 return ctx->root;
273 status = dbwrap_fetch(ctx->reg, ctx, string_term_tdb_data(path), &val);
274 if (!NT_STATUS_IS_OK(status)) {
275 return NULL;
277 if (val.dptr != NULL) {
278 if (ctx->opt.verbose) {
279 printf("Open: %s\n", path);
281 ret = *(struct regkey**)val.dptr;
282 } else {
283 /* not yet existing, create */
284 char *pp;
285 if (ctx->opt.verbose) {
286 printf("New: %s\n", path);
288 ret = talloc_zero(ctx, struct regkey);
289 if (ret == NULL) {
290 DEBUG(0, ("Out of memory!\n"));
291 goto done;
293 ret->path = talloc_strdup(ret, path);
295 pp = parent_path(path, ctx->sep);
296 ret->parent = check_ctx_lookup_key(ctx, pp);
297 regkey_add_subkey(ret->parent, ret);
298 TALLOC_FREE(pp);
300 /* the dummy root key has no subkeylist so set the name */
301 if (ret->parent == ctx->root) {
302 ret->name = talloc_strdup(ret, path);
305 dbwrap_store(ctx->reg, string_term_tdb_data(path),
306 make_tdb_data((void*)&ret, sizeof(ret)), 0);
308 done:
309 talloc_free(val.dptr);
310 return ret;
313 static struct check_ctx* check_ctx_create(TALLOC_CTX *mem_ctx, const char *db,
314 const struct check_options *opt)
316 struct check_ctx *ctx = talloc_zero(mem_ctx, struct check_ctx);
318 ctx->opt = *opt;
319 ctx->reg = db_open_rbt(ctx);
320 ctx->del = db_open_rbt(ctx);
321 ctx->root = talloc_zero(ctx, struct regkey);
322 ctx->fname = talloc_strdup(ctx, db);
324 if (opt->automatic && (opt->output == NULL)) {
325 ctx->opt.repair = true;
326 ctx->opt.output = ctx->fname;
329 if (opt->repair) {
330 if (opt->output) {
331 d_fprintf(stderr, "You can not specify --output "
332 "with --repair\n");
333 goto fail;
334 } else {
335 ctx->opt.output = ctx->fname;
339 ctx->default_action = 'r';
340 return ctx;
341 fail:
342 talloc_free(ctx);
343 return NULL;
346 static bool check_ctx_open_output(struct check_ctx *ctx)
348 int oflags = O_RDWR | O_CREAT ;
350 if (ctx->opt.output == NULL) {
351 return true;
354 if (!ctx->opt.repair) {
355 if (!ctx->opt.wipe) {
356 oflags |= O_EXCL;
358 ctx->opt.wipe = true;
361 ctx->odb = db_open(ctx, ctx->opt.output, 0, TDB_DEFAULT, oflags, 0644);
362 if (ctx->odb == NULL) {
363 d_fprintf(stderr,
364 _("Could not open db (%s) for writing: %s\n"),
365 ctx->opt.output, strerror(errno));
366 return false;
368 return true;
372 static bool check_ctx_open_input(struct check_ctx *ctx) {
373 ctx->idb = db_open(ctx, ctx->fname, 0, TDB_DEFAULT, O_RDONLY, 0);
374 if (ctx->idb == NULL) {
375 d_fprintf(stderr,
376 _("Could not open db (%s) for reading: %s\n"),
377 ctx->fname, strerror(errno));
378 return false;
380 return true;
383 static bool check_ctx_transaction_start(struct check_ctx *ctx) {
384 if (ctx->odb == NULL) {
385 return true;
387 if (dbwrap_transaction_start(ctx->odb) != 0) {
388 DEBUG(0, ("transaction_start failed\n"));
389 return false;
391 ctx->transaction = true;
392 return true;
395 static void check_ctx_transaction_stop(struct check_ctx *ctx, bool ok) {
396 if (!ctx->transaction) {
397 return;
399 if (!ctx->opt.test && ok) {
400 d_printf("Commiting changes\n");
401 if (dbwrap_transaction_commit(ctx->odb) != 0) {
402 DEBUG(0, ("transaction_commit failed\n"));
404 } else {
405 d_printf("Discarding changes\n");
406 dbwrap_transaction_cancel(ctx->odb);
410 static bool read_info(struct check_ctx *ctx, const char *key, TDB_DATA val)
412 if (val.dsize==sizeof(uint32_t) && strcmp(key, "version")==0) {
413 uint32_t v = IVAL(val.dptr, 0);
414 printf("INFO: %s = %d\n", key, v);
415 return true;
417 printf("INFO: %s = <invalid>\n", key);
418 return false;
421 static bool is_all_upper(const char *str) {
422 bool ret;
423 char *tmp = talloc_strdup(talloc_tos(), str);
424 strupper_m(tmp);
425 ret = (strcmp(tmp, str) == 0);
426 talloc_free(tmp);
427 return ret;
430 static void move_to_back(struct regkey *key, struct regkey *subkey)
432 struct regkey **ptr;
433 size_t nidx;
435 DEBUG(5, ("Move to back subkey \"%s\" of \"%s\"\n",
436 subkey->path, key->path));
438 for (ptr=key->subkeys; *ptr != subkey; ptr++)
441 nidx = ptr + 1 - key->subkeys;
442 memmove(ptr, ptr+1, (key->nsubkeys - nidx) * sizeof(*ptr));
444 key->subkeys[key->nsubkeys-1] = subkey;
447 static void set_subkey_name(struct check_ctx *ctx, struct regkey *key,
448 const char *name, int nlen)
450 char *path = key->path;
451 TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
452 char *p;
453 struct regkey *subkey;
454 char *nname = talloc_strndup(mem_ctx, name, nlen);
455 remove_all(nname, ctx->sep);
457 if (strncmp(name, nname, nlen) != 0) {
458 /* XXX interaction: delete/edit */
459 printf("Warning: invalid name: \"%s\" replace with \"%s\"\n",
460 name, nname);
461 key->needs_update = true;
463 p = talloc_asprintf_strupper_m(mem_ctx, "%s%c%s",
464 path, ctx->sep, nname);
465 subkey = check_ctx_lookup_key(ctx, p);
466 if (subkey->name) {
467 bool do_replace = false;
469 if (strcmp(subkey->name, nname) != 0) {
470 int action;
471 char default_action;
473 if (is_all_upper(nname)) {
474 default_action = 'o';
475 } else {
476 default_action = 'n';
479 printf("Conflicting subkey names of [%s]: "
480 "old: \"%s\", new: \"%s\"\n",
481 key->path, subkey->name, nname);
483 if (ctx->opt.output == NULL || ctx->opt.automatic) {
484 action = default_action;
485 } else {
486 do {
487 action = interact_prompt(
488 "choose spelling [o]ld, [n]ew,"
489 "or [e]dit", "one",
490 default_action);
491 if (action == 'e') {
492 printf("Sorry, edit is not yet "
493 "implemented here...\n");
495 } while (action == 'e');
498 if (action == 'n') {
499 do_replace = true;
503 if (do_replace) {
504 if (ctx->opt.verbose) {
505 printf("Replacing name: %s: \"%s\""
506 " -> \"%s\"\n", path,
507 subkey->name, nname);
509 TALLOC_FREE(subkey->name);
510 subkey->name = talloc_steal(subkey, nname);
511 key->needs_update = true;
513 } else {
514 if (ctx->opt.verbose) {
515 printf("Set name: %s: \"%s\"\n", path, nname);
517 subkey->name = talloc_steal(subkey, nname);
520 move_to_back(key, subkey);
521 TALLOC_FREE(mem_ctx);
524 static void
525 read_subkeys(struct check_ctx *ctx, const char *path, TDB_DATA val, bool update)
527 uint32_t num_items, found_items = 0;
528 char *subkey;
529 struct regkey *key = check_ctx_lookup_key(ctx, path);
531 key->needs_update |= update;
533 /* printf("SUBKEYS: %s\n", path); */
534 if (key->has_subkeylist) {
535 printf("Duplicate subkeylist \"%s\"\n",
536 path);
537 found_items = key->nsubkeys;
540 /* exists as defined by regdb_key_exists() */
541 key->has_subkeylist = true;
543 /* name is set if a key is referenced by the */
544 /* subkeylist of its parent. */
546 if (!tdb_data_read_uint32(&val, &num_items) ) {
547 printf("Invalid subkeylist: \"%s\"\n", path);
548 return;
551 while (tdb_data_read_cstr(&val, &subkey)) {
552 /* printf(" SUBKEY: %s\n", subkey); */
553 set_subkey_name(ctx, key, subkey, strlen(subkey));
554 found_items++;
557 if (val.dsize != 0) {
558 printf("Subkeylist of \"%s\": trailing: \"%.*s\"\n",
559 path, (int)val.dsize, val.dptr);
560 /* ask: best effort, delete or edit?*/
561 set_subkey_name(ctx, key, (char*)val.dptr, val.dsize);
562 found_items++;
563 key->needs_update = true;
566 if (num_items != found_items) {
567 printf("Subkeylist of \"%s\": invalid number of subkeys, "
568 "expected: %d got: %d\n", path, num_items, found_items);
569 key->needs_update = true;
574 static void read_values(struct check_ctx *ctx, const char *path, TDB_DATA val)
576 struct regkey *key = check_ctx_lookup_key(ctx, path);
577 uint32_t num_items, found_items;
578 struct regval value;
580 /* printf("VALUES: %s\n", path); */
582 if (!tdb_data_read_uint32(&val, &num_items) ) {
583 printf("Invalid valuelist: \"%s\"\n", path);
584 return;
587 found_items=0;
588 while (tdb_data_read_regval(&val, &value)) {
589 /* printf(" VAL: %s type: %s(%d) length: %d\n", value.name, */
590 /* str_regtype(value.type), value.type, */
591 /* (int)value.data.length); */
592 regkey_add_regval(key, regval_copy(key, &value));
593 found_items++;
596 if (num_items != found_items) {
597 printf("Valuelist of \"%s\": invalid number of values, "
598 "expected: %d got: %d\n", path, num_items, found_items);
599 key->needs_update = true;
602 if (val.dsize != 0) {
603 printf("Valuelist of \"%s\": trailing: \"%*s\"\n", path,
604 (int)val.dsize, val.dptr);
605 key->needs_update = true;
606 /* XXX best effort ??? */
607 /* ZERO_STRUCT(value); */
608 /* if (tdb_data_read_cstr(&val, &value.name) */
609 /* && tdb_data_read_uint32(&val, &value.type)) */
610 /* { */
611 /* uint32_t len = -1; */
612 /* tdb_data_read_uint32(&val, &len); */
613 /* ... */
614 /* found_items ++; */
615 /* regkey_add_regval(key, regval_copy(key, value)); */
616 /* } */
618 if (found_items == 0) {
619 printf("Valuelist of \"%s\" empty\n", path);
620 key->needs_update = true;
624 static bool read_sorted(struct check_ctx *ctx, const char *path, TDB_DATA val)
626 if (ctx->version >= 3) {
627 return false;
630 if ((val.dptr == NULL) || (val.dsize<4)) {
631 return false;
634 /* ToDo: check */
635 /* struct regkey *key = check_ctx_lookup_key(ctx, path); */
636 /* printf("SORTED: %s\n", path); */
637 return true;
640 static bool read_sd(struct check_ctx *ctx, const char *path, TDB_DATA val)
642 NTSTATUS status;
643 struct regkey *key = check_ctx_lookup_key(ctx, path);
644 /* printf("SD: %s\n", path); */
646 status = unmarshall_sec_desc(key, val.dptr, val.dsize, &key->sd);
647 if (!NT_STATUS_IS_OK(status)) {
648 DEBUG(0, ("Failed to read SD of %s: %s\n",
649 path, nt_errstr(status)));
651 return true;
654 static bool srprs_path(const char **ptr, const char* prefix, char sep,
655 const char **ppath)
657 const char *path, *pos = *ptr;
658 if (prefix != NULL) {
659 if (!srprs_str(&pos, prefix, -1) || !srprs_char(&pos, sep) ) {
660 return false;
663 path = pos;
664 if ( !srprs_hive(&pos, NULL) ) {
665 return false;
667 if ( !srprs_eos(&pos) && !srprs_char(&pos, sep) ) {
668 return false;
670 *ppath = path;
671 *ptr = rawmemchr(pos, '\0');
672 return true;
675 /* Fixme: this dosn't work in the general multibyte char case.
676 see string_replace()
678 static bool normalize_path_internal(char* path, char sep) {
679 size_t len = strlen(path);
680 const char *orig = talloc_strndup(talloc_tos(), path, len);
681 char *optr = path, *iptr = path;
682 bool changed;
684 while (*iptr == sep ) {
685 iptr++;
687 while (*iptr) {
688 *optr = *iptr;
689 if (*iptr == sep) {
690 while (*iptr == sep) {
691 iptr++;
693 if (*iptr) {
694 optr++;
696 } else {
697 iptr++;
698 optr++;
701 *optr = '\0';
703 strupper_m(path);
704 changed = (strcmp(orig, path) != 0);
705 talloc_free(discard_const(orig));
706 return changed;
709 static bool normalize_path(char* path, char sep) {
710 static const char* SEPS = "\\/";
711 char* firstsep = strpbrk(path, SEPS);
712 bool wrong_sep = (firstsep && (*firstsep != sep));
714 assert (strchr(SEPS, sep));
716 if (wrong_sep) {
717 string_replace(path, *firstsep, sep);
719 return normalize_path_internal(path, sep) || wrong_sep;
722 static int check_tdb_action(struct db_record *rec, void *check_ctx)
724 struct check_ctx *ctx = (struct check_ctx*)check_ctx;
725 TALLOC_CTX *frame = talloc_stackframe();
726 TDB_DATA val = dbwrap_record_get_value(rec);
727 TDB_DATA rec_key = dbwrap_record_get_key(rec);
728 char *key;
729 bool invalid_path = false;
730 bool once_more;
731 bool first_iter = true;
733 if (!tdb_data_is_cstr(rec_key)) {
734 printf("Key is not zero terminated: \"%.*s\"\ntry to go on.\n",
735 (int)rec_key.dsize, rec_key.dptr);
736 invalid_path = true;
738 key = talloc_strndup(frame, (char*)rec_key.dptr, rec_key.dsize);
740 do {
741 const char *path, *pos = key;
742 once_more = false;
744 if (srprs_str(&pos, "INFO/", -1)) {
745 if ( read_info(ctx, pos, val) ) {
746 break;
748 invalid_path = true;
749 /* ask: mark invalid */
750 } else if (srprs_str(&pos, "__db_sequence_number__", -1)) {
751 printf("Skip key: \"%.*s\"\n",
752 (int)rec_key.dsize, rec_key.dptr);
753 /* skip: do nothing + break */
754 break;
756 } else if (normalize_path(key, ctx->sep)) {
757 printf("Unnormal key: \"%.*s\"\n",
758 (int)rec_key.dsize, rec_key.dptr);
759 printf("Normalize to: \"%s\"\n", key);
760 invalid_path = true;
761 } else if (srprs_path(&pos, NULL,
762 ctx->sep, &path))
764 read_subkeys(ctx, path, val, invalid_path);
765 break;
766 } else if (srprs_path(&pos, REG_VALUE_PREFIX,
767 ctx->sep, &path))
769 read_values(ctx, path, val);
770 break;
771 } else if (srprs_path(&pos, REG_SECDESC_PREFIX,
772 ctx->sep, &path))
774 read_sd(ctx, path, val);
775 break;
776 } else if (srprs_path(&pos, REG_SORTED_SUBKEYS_PREFIX,
777 ctx->sep, &path))
779 if (!read_sorted(ctx, path, val)) {
780 /* delete: mark invalid + break */
781 printf("Invalid sorted subkeys for: \"%s\"\n", path);
782 invalid_path = true;
783 key = NULL;
785 break;
786 } else {
787 printf("Unrecognized key: \"%.*s\"\n",
788 (int)rec_key.dsize, rec_key.dptr);
789 invalid_path = true;
792 if (invalid_path) {
793 int action;
794 if (ctx->opt.output == NULL) {
795 action = first_iter ? 'r' : 's';
796 } else if (ctx->opt.automatic) {
797 action = first_iter ? 'r' : 'd';
798 } else if (ctx->auto_action != '\0') {
799 action = ctx->auto_action;
800 } else {
801 action = interact_prompt("[s]kip,[S]kip all,"
802 "[d]elete,[D]elete all"
803 ",[e]dit,[r]etry"
804 , "sder",
805 ctx->default_action);
807 if (isupper(action)) {
808 action = tolower(action);
809 ctx->auto_action = action;
811 ctx->default_action = action;
812 switch (action) {
813 case 's': /* skip */
814 invalid_path = false;
815 break;
816 case 'd': /* delete */
817 invalid_path = true;
818 key = NULL;
819 break;
820 case 'e': /* edit */ {
821 char *p = interact_edit(frame, key);
822 if (p) {
823 talloc_free(key);
824 key = p;
826 } /* fall through */
827 case 'r': /* retry */
828 once_more = true;
829 break;
832 first_iter = false;
833 } while (once_more);
835 if (invalid_path) {
836 dbwrap_store(ctx->del, rec_key, string_term_tdb_data(key), 0);
839 talloc_free(frame);
840 return 0;
843 static bool get_version(struct check_ctx *ctx) {
844 static const uint32_t curr_version = REGDB_CODE_VERSION;
845 uint32_t version = ctx->opt.version ? ctx->opt.version : curr_version;
846 uint32_t info_version = 0;
847 NTSTATUS status;
849 status = dbwrap_fetch_uint32(ctx->idb, "INFO/version", &info_version);
850 if (!NT_STATUS_IS_OK(status)) {
851 printf("Warning: no INFO/version found!\n");
852 /* info_version = guess_version(ctx); */
855 if (ctx->opt.version) {
856 version = ctx->opt.version;
857 } else if (ctx->opt.implicit_db) {
858 version = curr_version;
859 } else {
860 version = info_version;
863 if (!version) {
864 printf("Couldn't determine registry format version, "
865 "specify with --reg-version\n");
866 return false;
870 if ( version != info_version ) {
871 if (ctx->opt.force || !ctx->opt.repair) {
872 printf("Warning: overwrite registry format "
873 "version %d with %d\n", info_version, version);
874 } else {
875 printf("Warning: found registry format version %d but "
876 "expected %d\n", info_version, version);
877 return false;
881 ctx->version = version;
882 ctx->sep = (version > 1) ? '\\' : '/';
884 return true;
887 static bool
888 dbwrap_store_verbose(struct db_context *db, const char *key, TDB_DATA nval)
890 TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
891 TDB_DATA oval;
892 NTSTATUS status;
894 status = dbwrap_fetch_bystring(db, mem_ctx, key, &oval);
895 if (!NT_STATUS_IS_OK(status)) {
896 printf ("store %s failed to fetch old value: %s\n", key,
897 nt_errstr(status));
898 goto done;
901 if (!tdb_data_is_empty(oval) && !tdb_data_equal(nval, oval)) {
902 printf("store %s:\n"
903 " overwrite: %s\n"
904 " with: %s\n",
905 key, tdb_data_print(mem_ctx, oval),
906 tdb_data_print(mem_ctx, nval));
909 status = dbwrap_store_bystring(db, key, nval, 0);
910 if (!NT_STATUS_IS_OK(status)) {
911 printf ("store %s failed: %s\n", key, nt_errstr(status));
914 done:
915 talloc_free(mem_ctx);
916 return NT_STATUS_IS_OK(status);
920 static int cmp_keynames(char **p1, char **p2)
922 return strcasecmp_m(*p1, *p2);
925 static bool
926 write_subkeylist(struct db_context *db, struct regkey *key, char sep)
928 cbuf *buf = cbuf_new(talloc_tos());
929 int i;
930 bool ret;
932 cbuf_putdw(buf, key->nsubkeys);
934 for (i=0; i < key->nsubkeys; i++) {
935 struct regkey *subkey = key->subkeys[i];
936 const char *name = subkey->name;
937 if (name == NULL) {
938 printf("Warning: no explicite name for key %s\n",
939 subkey->path);
940 name = strrchr_m(subkey->path, sep);
941 assert(name);
942 name ++;
944 cbuf_puts(buf, name, -1);
945 cbuf_putc(buf, '\0');
948 ret = dbwrap_store_verbose(db, key->path, cbuf_make_tdb_data(buf));
950 talloc_free(buf);
951 return ret;
954 static bool write_sorted(struct db_context *db, struct regkey *key, char sep)
956 cbuf *buf = cbuf_new(talloc_tos());
957 char *path;
958 int i;
959 bool ret = false;
960 char **sorted = talloc_zero_array(buf, char*, key->nsubkeys);
961 int offset = (1 + key->nsubkeys) * sizeof(uint32_t);
963 for (i=0; i < key->nsubkeys; i++) {
964 sorted[i] = talloc_strdup_upper(sorted, key->subkeys[i]->name);
966 TYPESAFE_QSORT(sorted, key->nsubkeys, cmp_keynames);
968 cbuf_putdw(buf, key->nsubkeys);
969 for (i=0; i < key->nsubkeys; i++) {
970 cbuf_putdw(buf, offset);
971 offset += strlen(sorted[i]) + 1;
973 for (i=0; i < key->nsubkeys; i++) {
974 cbuf_puts(buf, sorted[i], -1);
975 cbuf_putc(buf, '\0');
978 path = talloc_asprintf(buf, "%s%c%s", REG_SORTED_SUBKEYS_PREFIX, sep,
979 key->path);
980 if (path == NULL) {
981 DEBUG(0, ("Out of memory!\n"));
982 goto done;
985 ret = dbwrap_store_verbose(db, path, cbuf_make_tdb_data(buf));
986 done:
987 talloc_free(buf);
988 return ret;
991 static bool write_values(struct db_context *db, struct regkey *key, char sep)
993 cbuf *buf = cbuf_new(talloc_tos());
994 char *path;
995 int i;
996 bool ret = false;
998 cbuf_putdw(buf, key->nvalues);
999 for (i=0; i < key->nvalues; i++) {
1000 struct regval *val = key->values[i];
1001 cbuf_puts(buf, val->name, -1);
1002 cbuf_putc(buf, '\0');
1003 cbuf_putdw(buf, val->type);
1004 cbuf_putdw(buf, val->data.length);
1005 cbuf_puts(buf, (void*)val->data.data, val->data.length);
1008 path = talloc_asprintf(buf, "%s%c%s", REG_VALUE_PREFIX, sep, key->path);
1009 if (path == NULL) {
1010 DEBUG(0, ("Out of memory!\n"));
1011 goto done;
1014 ret = dbwrap_store_verbose(db, path, cbuf_make_tdb_data(buf));
1015 done:
1016 talloc_free(buf);
1017 return ret;
1020 static bool write_sd(struct db_context *db, struct regkey *key, char sep)
1022 TDB_DATA sd;
1023 NTSTATUS status;
1024 char *path;
1025 bool ret = false;
1026 TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
1028 status = marshall_sec_desc(mem_ctx, key->sd, &sd.dptr, &sd.dsize);
1029 if (!NT_STATUS_IS_OK(status)) {
1030 printf("marshall sec desc %s failed: %s\n",
1031 key->path, nt_errstr(status));
1032 goto done;
1034 path = talloc_asprintf(mem_ctx, "%s%c%s", REG_SECDESC_PREFIX,
1035 sep, key->path);
1036 if (path == NULL) {
1037 DEBUG(0, ("Out of memory!\n"));
1038 goto done;
1041 ret = dbwrap_store_verbose(db, path, sd);
1042 done:
1043 talloc_free(mem_ctx);
1044 return ret;
1048 static int check_write_db_action(struct db_record *rec, void *check_ctx)
1050 struct check_ctx *ctx = (struct check_ctx*)check_ctx;
1051 TDB_DATA rec_val = dbwrap_record_get_value(rec);
1052 struct regkey *key = *(struct regkey**)rec_val.dptr;
1053 TALLOC_CTX *frame = talloc_stackframe();
1055 /* write subkeylist */
1056 if ((ctx->version > 2) || (key->nsubkeys > 0) || (key->has_subkeylist)) {
1057 write_subkeylist(ctx->odb, key, ctx->sep);
1060 /* write sorted subkeys */
1061 if ((ctx->version < 3) && (key->nsubkeys > 0)) {
1062 write_sorted(ctx->odb, key, ctx->sep);
1065 /* write value list */
1066 if (key->nvalues > 0) {
1067 write_values(ctx->odb, key, ctx->sep);
1070 /* write sd */
1071 if (key->sd) {
1072 write_sd(ctx->odb, key, ctx->sep);
1075 talloc_free(frame);
1076 return 0;
1079 static int fix_tree_action(struct db_record *rec, void *check_ctx)
1081 struct check_ctx *ctx = (struct check_ctx*)check_ctx;
1082 TDB_DATA rec_key = dbwrap_record_get_key(rec);
1083 TDB_DATA rec_val = dbwrap_record_get_value(rec);
1084 struct regkey* key = *(struct regkey**)rec_val.dptr;
1085 if (ctx->opt.verbose) {
1086 printf("Check Tree: %s\n", key->path);
1089 assert (strncmp(key->path, (char*)rec_key.dptr, rec_key.dsize) == 0);
1091 /* assert(dbwrap_exists(ctx->db, string_term_tdb_data(key->path)) */
1092 /* == key->exists); */
1094 if (key->needs_update) {
1095 printf("Update key: \"%s\"\n", key->path);
1096 if ((ctx->version > 2) || (key->nsubkeys > 0)) {
1097 write_subkeylist(ctx->odb, key, ctx->sep);
1099 if ((ctx->version <= 2) && (key->nsubkeys > 0)) {
1100 write_sorted(ctx->odb, key, ctx->sep);
1102 if (key->nvalues > 0) {
1103 write_values(ctx->odb, key, ctx->sep);
1105 if (key->sd) {
1106 write_sd(ctx->odb, key, ctx->sep);
1108 } else if (!key->has_subkeylist) {
1109 if ((ctx->version > 2) || (key->nsubkeys > 0)) {
1110 printf("Missing subkeylist: %s\n", key->path);
1111 write_subkeylist(ctx->odb, key, ctx->sep);
1115 if (key->name == NULL && key->parent->has_subkeylist) {
1116 printf("Key not referenced by the its parents subkeylist: %s\n",
1117 key->path);
1118 write_subkeylist(ctx->odb, key->parent, ctx->sep);
1121 /* XXX check that upcase(name) matches last part of path ??? */
1123 return 0;
1127 /* give the same warnings as fix_tree_action */
1128 static int check_tree_action(struct db_record *rec, void *check_ctx)
1130 struct check_ctx *ctx = (struct check_ctx*)check_ctx;
1131 TDB_DATA rec_key = dbwrap_record_get_key(rec);
1132 TDB_DATA rec_val = dbwrap_record_get_value(rec);
1133 struct regkey* key = *(struct regkey**)rec_val.dptr;
1134 if (ctx->opt.verbose) {
1135 printf("Check Tree: %s\n", key->path);
1138 assert (strncmp(key->path, (char*)rec_key.dptr, rec_key.dsize) == 0);
1140 if (!key->has_subkeylist) {
1141 if ((ctx->version > 2) || (key->nsubkeys > 0)) {
1142 printf("Missing subkeylist: %s\n", key->path);
1146 if (key->name == NULL && key->parent->has_subkeylist) {
1147 printf("Key not referenced by the its parents subkeylist: %s\n",
1148 key->path);
1151 return 0;
1154 static int delete_invalid_action(struct db_record *rec, void* check_ctx)
1156 NTSTATUS status;
1157 struct check_ctx *ctx = (struct check_ctx*)check_ctx;
1158 TDB_DATA rec_key = dbwrap_record_get_key(rec);
1159 TDB_DATA rec_val = dbwrap_record_get_value(rec);
1162 printf("Delete key: \"%.*s\"",(int)rec_key.dsize, rec_key.dptr);
1163 if (rec_val.dsize > 0) {
1164 printf(" in favour of \"%s\"\n", rec_val.dptr);
1165 } else {
1166 putc('\n', stdout);
1169 status = dbwrap_delete(ctx->odb, rec_key);
1170 if (!NT_STATUS_IS_OK(status)) {
1171 d_printf("delete key \"%.*s\" failed!\n",
1172 (int)rec_key.dsize, rec_key.dptr);
1173 return -1;
1175 return 0;
1178 static bool check_ctx_check_tree(struct check_ctx *ctx) {
1179 NTSTATUS status;
1181 status = dbwrap_traverse(ctx->reg, check_tree_action, ctx, NULL);
1182 if (!NT_STATUS_IS_OK(status)) {
1183 DEBUG(0, ("check traverse failed: %s\n",
1184 nt_errstr(status)));
1185 return false;
1187 return true;
1189 static bool check_ctx_fix_inplace(struct check_ctx *ctx) {
1190 NTSTATUS status;
1191 status = dbwrap_traverse(ctx->reg, fix_tree_action, ctx, NULL);
1192 if (!NT_STATUS_IS_OK(status)) {
1193 DEBUG(0, ("fix traverse failed: %s\n",
1194 nt_errstr(status)));
1195 return false;
1198 status = dbwrap_traverse(ctx->del, delete_invalid_action, ctx, NULL);
1199 if (!NT_STATUS_IS_OK(status)) {
1200 DEBUG(0, ("delete traverse failed: %s\n",
1201 nt_errstr(status)));
1202 return false;
1204 return true;
1207 static bool check_ctx_write_new_db(struct check_ctx *ctx) {
1208 NTSTATUS status;
1210 assert(ctx->odb);
1212 if (ctx->opt.wipe) {
1213 int ret = dbwrap_wipe(ctx->odb);
1214 if (ret != 0) {
1215 DEBUG(0, ("wiping %s failed\n",
1216 ctx->opt.output));
1217 return false;
1221 status = dbwrap_traverse(ctx->reg, check_write_db_action, ctx, NULL);
1222 if (!NT_STATUS_IS_OK(status)) {
1223 DEBUG(0, ("traverse2 failed: %s\n", nt_errstr(status)));
1224 return false;
1227 status = dbwrap_store_uint32(ctx->odb,
1228 "INFO/version", ctx->version);
1229 if (!NT_STATUS_IS_OK(status)) {
1230 DEBUG(0, ("write version failed: %s\n", nt_errstr(status)));
1231 return false;
1233 return true;
1236 int net_registry_check_db(const char *name, const struct check_options *opt)
1238 NTSTATUS status;
1239 int ret = -1;
1240 struct check_ctx *ctx = check_ctx_create(talloc_tos(), name, opt);
1241 if (ctx==NULL) {
1242 goto done;
1245 d_printf("Check database: %s\n", name);
1247 /* 1. open output RW */
1248 if (!check_ctx_open_output(ctx)) {
1249 goto done;
1252 /* 2. open input RO */
1253 if (!check_ctx_open_input(ctx)) {
1254 goto done;
1257 if (opt->lock && !check_ctx_transaction_start(ctx)) {
1258 goto done;
1261 if (!get_version(ctx)) {
1262 goto done;
1265 status = dbwrap_traverse_read(ctx->idb, check_tdb_action, ctx, NULL);
1266 if (!NT_STATUS_IS_OK(status)) {
1267 DEBUG(0, ("check traverse failed: %s\n", nt_errstr(status)));
1268 goto done;
1271 if (!opt->lock && !check_ctx_transaction_start(ctx)) {
1272 goto done;
1275 if (ctx->opt.repair && !ctx->opt.wipe) {
1276 if (!check_ctx_fix_inplace(ctx)) {
1277 goto done;
1279 } else {
1280 if (!check_ctx_check_tree(ctx)) {
1281 goto done;
1283 if (ctx->odb) {
1284 if (!check_ctx_write_new_db(ctx)) {
1285 goto done;
1289 ret = 0;
1290 done:
1291 check_ctx_transaction_stop(ctx, ret == 0);
1293 talloc_free(ctx);
1294 return ret;
1297 /*Local Variables:*/
1298 /*mode: c*/
1299 /*End:*/