s3-docs: Fix typo.
[Samba/gebeck_regimport.git] / source3 / utils / net_idmap_check.c
blob1f54121113088c86525e042816c029914769cad1
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 idmap database.
22 * @author Gregor Beck <gb@sernet.de>
23 * @date Mar 2011
26 #include "net_idmap_check.h"
27 #include "includes.h"
28 #include "system/filesys.h"
29 #include "dbwrap/dbwrap.h"
30 #include "dbwrap/dbwrap_open.h"
31 #include "net.h"
32 #include "../libcli/security/dom_sid.h"
33 #include "cbuf.h"
34 #include "srprs.h"
35 #include <termios.h>
36 #include "util_tdb.h"
38 static int traverse_commit(struct db_record *diff_rec, void* data);
39 static int traverse_check(struct db_record *rec, void* data);
41 static char* interact_edit(TALLOC_CTX* mem_ctx, const char* str);
42 static int interact_prompt(const char* msg, const char* accept, char def);
44 /* TDB_DATA *******************************************************************/
45 static char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d);
46 static TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr);
47 static TDB_DATA talloc_copy(TALLOC_CTX* mem_ctx, TDB_DATA data);
48 static bool is_empty(TDB_DATA data) {
49 return (data.dsize == 0) || (data.dptr == NULL);
52 /* record *********************************************************************/
54 enum DT {
55 DT_INV = 0,
56 DT_SID, DT_UID, DT_GID,
57 DT_HWM, DT_VER, DT_SEQ,
60 struct record {
61 enum DT key_type, val_type;
62 TDB_DATA key, val;
63 struct dom_sid sid;
64 long unsigned id;
67 static struct record* parse_record(TALLOC_CTX* ctx, TDB_DATA key, TDB_DATA val);
68 static struct record* reverse_record(struct record* rec);
70 static bool is_invalid(const struct record* r) {
71 return (r->key_type == DT_INV) || (r->val_type == DT_INV);
74 static bool is_map(const struct record* r) {
75 return (r->key_type == DT_SID)
76 || (r->key_type == DT_UID) || (r->key_type == DT_GID);
79 /* action *********************************************************************/
81 typedef struct check_action {
82 const char* fmt;
83 const char* name;
84 const char* prompt;
85 const char* answers;
86 char auto_action;
87 char default_action;
88 bool verbose;
89 } check_action;
91 struct check_actions {
92 check_action invalid_record;
93 check_action missing_reverse;
94 check_action invalid_mapping;
95 check_action invalid_edit;
96 check_action record_exists;
97 check_action no_version;
98 check_action wrong_version;
99 check_action invalid_hwm;
100 check_action commit;
101 check_action valid_mapping;
102 check_action valid_other;
103 check_action invalid_diff;
106 static struct check_actions
107 check_actions_init(const struct check_options* opts) {
108 struct check_actions ret = {
109 .invalid_record = (check_action) {
110 .name = "Invalid record",
111 .prompt = "[e]dit/[d]elete/[D]elete all"
112 "/[s]kip/[S]kip all",
113 .answers = "eds",
114 .default_action = 'e',
115 .verbose = true,
117 .missing_reverse = (check_action) {
118 .name = "Missing reverse mapping for",
119 .prompt = "[f]ix/[F]ix all/[e]dit/[d]elete/[D]elete all"
120 "/[s]kip/[S]kip all",
121 .answers = "feds",
122 .default_action = 'f',
123 .verbose = true,
125 .invalid_mapping = (check_action) {
126 .fmt = "%1$s: %2$s -> %3$s\n(%4$s <- %3$s)\n",
127 .name = "Invalid mapping",
128 .prompt = "[e]dit/[d]elete/[D]elete all"
129 "/[s]kip/[S]kip all",
130 .answers = "eds",
131 .default_action = 'd',
132 .verbose = true,
134 .invalid_edit = (check_action) {
135 .name = "Invalid record",
136 .prompt = "[e]dit/[d]elete/[D]elete all"
137 "/[s]kip/[S]kip all",
138 .answers = "eds",
139 .default_action = 'e',
140 .verbose = true,
142 .record_exists = (check_action) {
143 .fmt = "%1$s: %2$s\n-%4$s\n+%3$s\n",
144 .name = "Record exists",
145 .prompt = "[o]verwrite/[O]verwrite all/[e]dit"
146 "/[d]elete/[D]elete all/[s]kip/[S]kip all",
147 .answers = "oeds",
148 .default_action = 'o',
149 .verbose = true,
151 .no_version = (check_action) {
152 .prompt = "[f]ix/[s]kip/[a]bort",
153 .answers = "fsa",
154 .default_action = 'f',
156 .wrong_version = (check_action) {
157 .prompt = "[f]ix/[s]kip/[a]bort",
158 .answers = "fsa",
159 .default_action = 'a',
161 .invalid_hwm = (check_action) {
162 .prompt = "[f]ix/[s]kip",
163 .answers = "fs",
164 .default_action = 'f',
166 .commit = (check_action) {
167 .prompt = "[c]ommit/[l]ist/[s]kip",
168 .answers = "cls",
169 .default_action = 'l',
170 .verbose = true,
172 .valid_mapping = (check_action) {
173 .fmt = "%1$s: %2$s <-> %3$s\n",
174 .name = "Mapping",
175 .auto_action = 's',
176 .verbose = opts->verbose,
178 .valid_other = (check_action) {
179 .name = "Other",
180 .auto_action = 's',
181 .verbose = opts->verbose,
183 .invalid_diff = (check_action) {
184 .prompt = "[s]kip/[S]kip all/[c]ommit/[C]ommit all"
185 "/[a]bort",
186 .answers = "sca",
187 .default_action = 's',
191 if (!opts->repair) {
192 ret.invalid_record.auto_action = 's';
193 ret.missing_reverse.auto_action = 's';
194 ret.invalid_mapping.auto_action = 's';
195 ret.no_version.auto_action = 's';
196 ret.wrong_version.auto_action = 's';
197 ret.invalid_hwm.auto_action = 's';
198 ret.commit.auto_action = 's';
201 if (opts->automatic) {
202 ret.invalid_record.auto_action = 'd'; /* delete */
203 ret.missing_reverse.auto_action = 'f'; /* fix */
204 ret.invalid_mapping.auto_action = 'd'; /* delete */
205 ret.no_version.auto_action = 'f'; /* fix */
206 ret.wrong_version.auto_action = 'a'; /* abort */
207 ret.invalid_hwm.auto_action = 'f'; /* fix */
208 ret.commit.auto_action = 'c'; /* commit */
209 ret.invalid_diff.auto_action = 'a'; /* abort */
210 if (opts->force) {
211 ret.wrong_version.auto_action = 'f'; /* fix */
212 ret.invalid_diff.auto_action = 'c'; /* commit */
215 if (opts->test) {
216 ret.invalid_diff.auto_action = 'c'; /* commit */
217 /* ret.commit.auto_action = 'c';*/ /* commit */
220 return ret;
223 static char get_action(struct check_action* a, struct record* r, TDB_DATA* v) {
224 char ret;
225 if (a->verbose && (r != NULL)) {
226 if (!a->fmt) {
227 d_printf("%s: %s ", a->name, print_data(r, r->key));
228 if (is_map(r)) {
229 d_printf("-> %s\n", print_data(r, r->val));
230 } else if (r->key_type == DT_HWM ||
231 r->key_type == DT_VER ||
232 r->key_type == DT_SEQ)
234 d_printf(": %ld\n", r->id);
235 } else {
236 d_printf("\n");
238 } else {
239 d_printf(a->fmt, a->name,
240 print_data(r, r->key),
241 print_data(r, r->val),
242 (v ? print_data(r, *v) : ""));
246 if (a->auto_action != '\0') {
247 return a->auto_action;
250 ret = interact_prompt(a->prompt, a->answers, a->default_action);
252 if (isupper(ret)) {
253 ret = tolower(ret);
254 a->auto_action = ret;
256 a->default_action = ret;
257 return ret;
260 /* *************************************************************************/
262 typedef struct {
263 TDB_DATA oval, nval;
264 } TDB_DATA_diff;
266 static TDB_DATA pack_diff(TDB_DATA_diff* diff) {
267 return (TDB_DATA) {
268 .dptr = (uint8_t *)diff,
269 .dsize = sizeof(TDB_DATA_diff),
273 static TDB_DATA_diff unpack_diff(TDB_DATA data) {
274 assert(data.dsize == sizeof(TDB_DATA_diff));
275 return *(TDB_DATA_diff*)data.dptr;
278 #define DEBUG_DIFF(LEV,MEM,MSG,KEY,OLD,NEW) \
279 DEBUG(LEV, ("%s: %s\n", MSG, print_data(MEM, KEY))); \
280 if (!is_empty(OLD)) { \
281 DEBUGADD(LEV, ("-%s\n", print_data(MEM, OLD))); \
283 if (!is_empty(NEW)) { \
284 DEBUGADD(LEV, ("+%s\n", print_data(MEM, NEW))); \
287 struct check_ctx {
288 int oflags;
289 char* name;
290 bool transaction;
291 struct db_context *db;
292 struct db_context *diff;
293 struct check_actions action;
295 uint32_t uid_hwm;
296 uint32_t gid_hwm;
298 unsigned n_invalid_record;
299 unsigned n_missing_reverse;
300 unsigned n_invalid_mappping;
301 unsigned n_map;
302 unsigned n_other;
303 unsigned n_diff;
304 struct check_options opts;
308 static void adjust_hwm(struct check_ctx* ctx, const struct record* r);
310 static int add_record(struct check_ctx* ctx, TDB_DATA key, TDB_DATA value)
312 NTSTATUS status;
313 TDB_DATA_diff diff;
314 TALLOC_CTX* mem = talloc_new(ctx->diff);
315 struct db_record* rec = ctx->diff->fetch_locked(ctx->diff, mem, key);
316 if (rec == NULL) {
317 return -1;
319 if (rec->value.dptr == 0) { /* first entry */
320 diff.oval = dbwrap_fetch(ctx->db, ctx->diff, key);
321 } else {
322 diff = unpack_diff(rec->value);
323 talloc_free(diff.nval.dptr);
325 diff.nval = talloc_copy(ctx->diff, value);
327 DEBUG_DIFF(2, mem, "TDB DIFF", key, diff.oval, diff.nval);
329 status = rec->store(rec, pack_diff(&diff), 0);
331 talloc_free(mem);
333 if (!NT_STATUS_IS_OK(status)) {
334 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
335 return -1;
337 ctx->n_diff ++;
338 return 0;
341 static int del_record(struct check_ctx* ctx, TDB_DATA key) {
342 return add_record(ctx, key, tdb_null);
345 static TDB_DATA
346 fetch_record(struct check_ctx* ctx, TALLOC_CTX* mem_ctx, TDB_DATA key)
348 TDB_DATA tmp;
350 if (ctx->diff->fetch(ctx->diff, mem_ctx, key, &tmp) != 0) {
351 DEBUG(0, ("Out of memory!\n"));
352 return tdb_null;
354 if (tmp.dptr != NULL) {
355 TDB_DATA_diff diff = unpack_diff(tmp);
356 TDB_DATA ret = talloc_copy(mem_ctx, diff.nval);
357 talloc_free(tmp.dptr);
358 return ret;
361 if (ctx->db->fetch(ctx->db, mem_ctx, key, &tmp) != 0) {
362 DEBUG(0, ("Out of memory!\n"));
363 return tdb_null;
366 return tmp;
369 static void edit_record(struct record* r) {
370 TALLOC_CTX* mem = talloc_new(r);
371 cbuf* ost = cbuf_new(mem);
372 const char* str;
373 struct record* nr;
374 TDB_DATA key;
375 TDB_DATA val;
376 cbuf_printf(ost, "%s %s\n",
377 print_data(mem, r->key), print_data(mem, r->val));
378 str = interact_edit(mem, cbuf_gets(ost, 0));
379 key = parse_data(mem, &str);
380 val = parse_data(mem, &str);
381 nr = parse_record(talloc_parent(r), key, val);
382 if (nr != NULL) {
383 *r = *nr;
385 talloc_free(mem);
388 static bool check_version(struct check_ctx* ctx) {
389 static const char* key = "IDMAP_VERSION";
390 uint32_t version;
391 bool no_version = !dbwrap_fetch_uint32(ctx->db, key, &version);
392 char action = 's';
393 struct check_actions* act = &ctx->action;
394 if (no_version) {
395 d_printf("No version number, assume 2\n");
396 action = get_action(&act->no_version, NULL, NULL);
397 } else if (version != 2) {
398 d_printf("Wrong version number %d, should be 2\n", version);
399 action = get_action(&act->wrong_version, NULL, NULL);
401 switch (action) {
402 case 's':
403 break;
404 case 'f':
405 SIVAL(&version, 0, 2);
406 add_record(ctx, string_term_tdb_data(key),
407 make_tdb_data((uint8_t *)&version, sizeof(uint32_t)));
408 break;
409 case 'a':
410 return false;
411 default:
412 assert(false);
414 return true;
417 static void check_hwm(struct check_ctx* ctx, const char* key, uint32_t target) {
418 uint32_t hwm;
419 char action = 's';
420 bool found = dbwrap_fetch_uint32(ctx->db, key, &hwm);
421 struct check_actions* act = &ctx->action;
422 if (!found) {
423 d_printf("No %s should be %d\n", key, target);
424 action = get_action(&act->invalid_hwm, NULL, NULL);
425 } else if (target < hwm) {
426 d_printf("Invalid %s %d: should be %d\n", key, hwm, target);
427 action = get_action(&act->invalid_hwm, NULL, NULL);
429 if (action == 'f') {
430 SIVAL(&hwm, 0, target);
431 add_record(ctx, string_term_tdb_data(key),
432 make_tdb_data((uint8_t *)&hwm, sizeof(uint32_t)));
436 int traverse_check(struct db_record *rec, void* data) {
437 struct check_ctx* ctx = (struct check_ctx*)data;
438 struct check_actions* act = &ctx->action;
439 TALLOC_CTX* mem = talloc_new(ctx->diff);
440 struct record* r = parse_record(mem, rec->key, rec->value);
441 char action = 's';
443 if (is_invalid(r)) {
444 action = get_action(&act->invalid_record, r, NULL);
445 ctx->n_invalid_record++;
446 } else if (is_map(r)) {
447 TDB_DATA back = fetch_record(ctx, mem, r->val);
448 if (back.dptr == NULL) {
449 action = get_action(&act->missing_reverse, r, NULL);
450 ctx->n_missing_reverse++;
451 } else if (!tdb_data_equal(r->key, back)) {
452 action = get_action(&act->invalid_mapping, r, &back);
453 ctx->n_invalid_mappping++;
454 } else {
455 if (r->key_type == DT_SID) {
456 action = get_action(&act->valid_mapping, r, NULL);
457 ctx->n_map++;
458 } else {
459 action = get_action(&act->valid_mapping, NULL,
460 NULL);
463 adjust_hwm(ctx, r);
464 } else {
465 action = get_action(&act->valid_other, r, NULL);
466 ctx->n_other++;
469 while (action) {
470 switch (action) {
471 case 's': /* skip */
472 break;
473 case 'd': /* delete */
474 del_record(ctx, rec->key);
475 break;
476 case 'f': /* add reverse mapping */
477 add_record(ctx, rec->value, rec->key);
478 break;
479 case 'e': /* edit */
480 edit_record(r);
481 action = 'o';
482 if (is_invalid(r)) {
483 action = get_action(&act->invalid_edit, r,NULL);
484 continue;
486 if (!tdb_data_equal(rec->key, r->key)) {
487 TDB_DATA oval = fetch_record(ctx, mem, r->key);
488 if (!is_empty(oval) &&
489 !tdb_data_equal(oval, r->val))
491 action = get_action(&act->record_exists,
492 r, &oval);
493 if (action != 'o') {
494 continue;
498 if (is_map(r)) {
499 TDB_DATA okey = fetch_record(ctx, mem, r->val);
500 if (!is_empty(okey) &&
501 !tdb_data_equal(okey, r->key))
503 action = get_action(&act->record_exists,
504 reverse_record(r),
505 &okey);
508 continue;
509 case 'o': /* overwrite */
510 adjust_hwm(ctx, r);
511 if (!tdb_data_equal(rec->key, r->key)) {
512 del_record(ctx, rec->key);
514 add_record(ctx, r->key, r->val);
515 if (is_map(r)) {
516 add_record(ctx, r->val, r->key);
519 action = '\0';
522 talloc_free(mem);
524 return 0;
527 /******************************************************************************/
529 void adjust_hwm(struct check_ctx* ctx, const struct record* r) {
530 enum DT type = (r->key_type == DT_SID) ? r->val_type : r->key_type;
531 if (type == DT_UID) {
532 ctx->uid_hwm = MAX(ctx->uid_hwm, r->id);
533 } else if (type == DT_GID) {
534 ctx->gid_hwm = MAX(ctx->gid_hwm, r->id);
538 TDB_DATA talloc_copy(TALLOC_CTX* mem_ctx, TDB_DATA data) {
539 TDB_DATA ret = {
540 .dptr = (uint8_t *)talloc_size(mem_ctx, data.dsize+1),
541 .dsize = data.dsize
543 if (ret.dptr == NULL) {
544 ret.dsize = 0;
545 } else {
546 memcpy(ret.dptr, data.dptr, data.dsize);
547 ret.dptr[ret.dsize] = '\0';
549 return ret;
552 static bool is_cstr(TDB_DATA str) {
553 return !is_empty(str) && str.dptr[str.dsize-1] == '\0';
556 static bool parse_sid (TDB_DATA str, enum DT* type, struct dom_sid* sid) {
557 struct dom_sid tmp;
558 const char* s = (const char*)str.dptr;
559 if ((s[0] == 'S') && string_to_sid(&tmp, s)) {
560 *sid = tmp;
561 *type = DT_SID;
562 return true;
564 return false;
567 static bool parse_xid(TDB_DATA str, enum DT* type, unsigned long* id) {
568 char c, t;
569 unsigned long tmp;
570 if (sscanf((const char*)str.dptr, "%cID %lu%c", &c, &tmp, &t) == 2) {
571 if (c == 'U') {
572 *id = tmp;
573 *type = DT_UID;
574 return true;
575 } else if (c == 'G') {
576 *id = tmp;
577 *type = DT_GID;
578 return true;
581 return false;
585 struct record*
586 parse_record(TALLOC_CTX* mem_ctx, TDB_DATA key, TDB_DATA val)
588 struct record* ret = talloc_zero(mem_ctx, struct record);
589 if (ret == NULL) {
590 DEBUG(0, ("Out of memory.\n"));
591 return NULL;
593 ret->key = talloc_copy(ret, key);
594 ret->val = talloc_copy(ret, val);
595 if ((ret->key.dptr == NULL && key.dptr != NULL) ||
596 (ret->val.dptr == NULL && val.dptr != NULL))
598 talloc_free(ret);
599 DEBUG(0, ("Out of memory.\n"));
600 return NULL;
602 assert((ret->key_type == DT_INV) && (ret->val_type == DT_INV));
604 if (!is_cstr(key)) {
605 return ret;
607 if (parse_sid(key, &ret->key_type, &ret->sid)) {
608 parse_xid(val, &ret->val_type, &ret->id);
609 } else if (parse_xid(key, &ret->key_type, &ret->id)) {
610 if (is_cstr(val)) {
611 parse_sid(val, &ret->val_type, &ret->sid);
613 } else if (strcmp((const char*)key.dptr, "USER HWM") == 0) {
614 ret->key_type = DT_HWM;
615 if (val.dsize == 4) {
616 ret->id = IVAL(val.dptr,0);
617 ret->val_type = DT_UID;
619 } else if (strcmp((const char*)key.dptr, "GROUP HWM") == 0) {
620 ret->key_type = DT_HWM;
621 if (val.dsize == 4) {
622 ret->id = IVAL(val.dptr,0);
623 ret->val_type = DT_GID;
625 } else if (strcmp((const char*)key.dptr, "IDMAP_VERSION") == 0) {
626 ret->key_type = DT_VER;
627 if (val.dsize == 4) {
628 ret->id = IVAL(val.dptr,0);
629 ret->val_type = DT_VER;
631 } else if (strcmp((const char*)key.dptr, "__db_sequence_number__") == 0) {
632 ret->key_type = DT_SEQ;
633 if (val.dsize == 8) {
634 ret->id = *(uint64_t*)val.dptr;
635 ret->val_type = DT_SEQ;
639 return ret;
642 struct record* reverse_record(struct record* in)
644 return parse_record(talloc_parent(in), in->val, in->key);
648 /******************************************************************************/
650 int interact_prompt(const char* msg, const char* acc, char def) {
651 struct termios old_tio, new_tio;
652 int c;
654 tcgetattr(STDIN_FILENO, &old_tio);
655 new_tio=old_tio;
656 new_tio.c_lflag &=(~ICANON & ~ECHO);
657 tcsetattr(STDIN_FILENO, TCSANOW, &new_tio);
659 do {
660 d_printf("%s? [%c]\n", msg, def);
661 fflush(stdout);
662 c = getchar();
663 if (c == '\n') {
664 c = def;
665 break;
667 else if (strchr(acc, tolower(c)) != NULL) {
668 break;
670 d_printf("Invalid input '%c'\n", c);
671 } while(c != EOF);
672 tcsetattr(STDIN_FILENO, TCSANOW, &old_tio);
673 return c;
676 char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d)
678 if (!is_empty(d)) {
679 char* ret = NULL;
680 cbuf* ost = cbuf_new(mem_ctx);
681 int len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize);
682 if (len != -1) {
683 cbuf_swapptr(ost, &ret, 0);
684 talloc_steal(mem_ctx, ret);
686 talloc_free(ost);
687 return ret;
689 return talloc_strdup(mem_ctx, "<NULL>");
693 TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr) {
694 cbuf* ost = cbuf_new(mem_ctx);
695 TDB_DATA ret = tdb_null;
696 srprs_skipws(ptr);
697 if (srprs_quoted(ptr, ost)) {
698 ret.dsize = cbuf_getpos(ost);
699 ret.dptr = (uint8_t *)talloc_steal(mem_ctx, cbuf_gets(ost,0));
701 talloc_free(ost);
702 return ret;
705 static const char* get_editor(void) {
706 static const char* editor = NULL;
707 if (editor == NULL) {
708 editor = getenv("VISUAL");
709 if (editor == NULL) {
710 editor = getenv("EDITOR");
712 if (editor == NULL) {
713 editor = "vi";
716 return editor;
719 char* interact_edit(TALLOC_CTX* mem_ctx, const char* str) {
720 char fname[] = "/tmp/net_idmap_check.XXXXXX";
721 char buf[128];
722 char* ret = NULL;
723 FILE* file;
725 int fd = mkstemp(fname);
726 if (fd == -1) {
727 DEBUG(0, ("failed to mkstemp %s: %s\n", fname,
728 strerror(errno)));
729 return NULL;
732 file = fdopen(fd, "w");
733 if (!file) {
734 DEBUG(0, ("failed to open %s for writing: %s\n", fname,
735 strerror(errno)));
736 close(fd);
737 unlink(fname);
738 return NULL;
741 fprintf(file, "%s", str);
742 fclose(file);
744 snprintf(buf, sizeof(buf), "%s %s\n", get_editor(), fname);
745 if (system(buf) != 0) {
746 DEBUG(0, ("failed to start editor %s: %s\n", buf,
747 strerror(errno)));
748 unlink(fname);
749 return NULL;
752 file = fopen(fname, "r");
753 if (!file) {
754 DEBUG(0, ("failed to open %s for reading: %s\n", fname,
755 strerror(errno)));
756 unlink(fname);
757 return NULL;
759 while ( fgets(buf, sizeof(buf), file) ) {
760 ret = talloc_strdup_append(ret, buf);
762 fclose(file);
763 unlink(fname);
765 return talloc_steal(mem_ctx, ret);
769 static int traverse_print_diff(struct db_record *rec, void* data) {
770 struct check_ctx* ctx = (struct check_ctx*)data;
771 TDB_DATA key = rec->key;
772 TDB_DATA_diff diff = unpack_diff(rec->value);
773 TALLOC_CTX* mem = talloc_new(ctx->diff);
775 DEBUG_DIFF(0, mem, "DIFF", key, diff.oval, diff.nval);
777 talloc_free(mem);
778 return 0;
782 static int traverse_commit(struct db_record *diff_rec, void* data) {
783 struct check_ctx* ctx = (struct check_ctx*)data;
784 TDB_DATA_diff diff = unpack_diff(diff_rec->value);
785 TDB_DATA key = diff_rec->key;
786 TALLOC_CTX* mem = talloc_new(ctx->diff);
787 int ret = -1;
788 NTSTATUS status;
789 struct check_actions* act = &ctx->action;
791 struct db_record* rec = ctx->db->fetch_locked(ctx->db, mem, key);
792 if (rec == NULL) {
793 goto done;
796 if (!tdb_data_equal(rec->value, diff.oval)) {
797 char action;
799 d_printf("Warning: record has changed: %s\n"
800 "expected: %s got %s\n", print_data(mem, key),
801 print_data(mem, diff.oval),
802 print_data(mem, rec->value));
804 action = get_action(&act->invalid_diff, NULL, NULL);
805 if (action == 's') {
806 ret = 0;
807 goto done;
808 } else if (action == 'a') {
809 goto done;
813 DEBUG_DIFF(0, mem, "Commit", key, diff.oval, diff.nval);
815 if (is_empty(diff.nval)) {
816 status = rec->delete_rec(rec);
817 } else {
818 status = rec->store(rec, diff.nval, 0);
821 if (!NT_STATUS_IS_OK(status)) {
822 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
823 if (!ctx->opts.force) {
824 goto done;
827 ret = 0;
828 done:
829 talloc_free(mem);
830 return ret;
833 static struct check_ctx*
834 check_init(TALLOC_CTX* mem_ctx, const struct check_options* o)
836 struct check_ctx* ctx = talloc_zero(mem_ctx, struct check_ctx);
837 if (ctx == NULL) {
838 DEBUG(0, (_("No memory\n")));
839 return NULL;
842 ctx->diff = db_open_rbt(ctx);
843 if (ctx->diff == NULL) {
844 talloc_free(ctx);
845 DEBUG(0, (_("No memory\n")));
846 return NULL;
849 ctx->action = check_actions_init(o);
850 ctx->opts = *o;
851 return ctx;
854 static bool check_open_db(struct check_ctx* ctx, const char* name, int oflags)
856 if (name == NULL) {
857 d_fprintf(stderr, _("Error: name == NULL in check_open_db().\n"));
858 return false;
861 if (ctx->db != NULL) {
862 if ((ctx->oflags == oflags) && (strcmp(ctx->name, name))) {
863 return true;
864 } else {
865 TALLOC_FREE(ctx->db);
869 ctx->db = db_open(ctx, name, 0, TDB_DEFAULT, oflags, 0);
870 if (ctx->db == NULL) {
871 d_fprintf(stderr,
872 _("Could not open idmap db (%s) for writing: %s\n"),
873 name, strerror(errno));
874 return false;
877 if (ctx->name != name) {
878 TALLOC_FREE(ctx->name);
879 ctx->name = talloc_strdup(ctx, name);
882 ctx->oflags = oflags;
883 return true;
886 static bool check_do_checks(struct check_ctx* ctx)
888 NTSTATUS status;
890 if (!check_version(ctx)) {
891 return false;
894 status = dbwrap_traverse(ctx->db, traverse_check, ctx);
896 if (!NT_STATUS_IS_OK(status)) {
897 DEBUG(0, ("failed to traverse %s\n", ctx->name));
898 return false;
901 check_hwm(ctx, "USER HWM", ctx->uid_hwm + 1);
902 check_hwm(ctx, "GROUP HWM", ctx->gid_hwm + 1);
904 return true;
907 static void check_summary(const struct check_ctx* ctx)
909 d_printf("uid hwm: %d\ngid hwm: %d\n", ctx->uid_hwm, ctx->gid_hwm);
910 d_printf("mappings: %d\nother: %d\n", ctx->n_map, ctx->n_other);
911 d_printf("invalid records: %d\nmissing links: %d\ninvalid links: %d\n",
912 ctx->n_invalid_record, ctx->n_missing_reverse,
913 ctx->n_invalid_mappping);
914 d_printf("%u changes:\n", ctx->n_diff);
917 static bool check_transaction_start(struct check_ctx* ctx) {
918 return (ctx->db->transaction_start(ctx->db) == 0);
921 static bool check_transaction_commit(struct check_ctx* ctx) {
922 return (ctx->db->transaction_commit(ctx->db) == 0);
925 static bool check_transaction_cancel(struct check_ctx* ctx) {
926 return (ctx->db->transaction_cancel(ctx->db) == 0);
930 static void check_diff_list(struct check_ctx* ctx) {
931 NTSTATUS status = dbwrap_traverse(ctx->diff, traverse_print_diff, ctx);
933 if (!NT_STATUS_IS_OK(status)) {
934 DEBUG(0, ("failed to traverse diff\n"));
939 static bool check_commit(struct check_ctx* ctx)
941 struct check_actions* act = &ctx->action;
942 char action;
943 NTSTATUS status = NT_STATUS_OK;
945 check_summary(ctx);
947 if (ctx->n_diff == 0) {
948 return true;
951 while ((action = get_action(&act->commit, NULL, NULL)) == 'l') {
952 check_diff_list(ctx);
954 if (action == 's') {
955 return true;
957 assert(action == 'c');
959 if (!check_open_db(ctx, ctx->name, O_RDWR)) {
960 return false;
963 if (!check_transaction_start(ctx)) {
964 return false;
967 status = dbwrap_traverse(ctx->diff, traverse_commit, ctx);
969 if (!NT_STATUS_IS_OK(status)) {
970 check_transaction_cancel(ctx);
971 return false;
973 if (ctx->opts.test) { /*get_action? */
974 return check_transaction_cancel(ctx);
975 } else {
976 return check_transaction_commit(ctx);
980 int net_idmap_check_db(const char* db, const struct check_options* o)
982 int ret = -1;
983 TALLOC_CTX* mem_ctx = talloc_stackframe();
984 struct check_ctx* ctx = check_init(mem_ctx, o);
986 if (!o->automatic && !isatty(STDIN_FILENO)) {
987 DEBUG(0, ("Interactive use needs tty, use --auto\n"));
988 goto done;
990 if (o->lock) {
991 if (check_open_db(ctx, db, O_RDWR)
992 && check_transaction_start(ctx))
994 if ( check_do_checks(ctx)
995 && check_commit(ctx)
996 && check_transaction_commit(ctx))
998 ret = 0;
999 } else {
1000 check_transaction_cancel(ctx);
1003 } else {
1004 if (check_open_db(ctx, db, O_RDONLY)
1005 && check_do_checks(ctx)
1006 && check_commit(ctx))
1008 ret = 0;
1011 done:
1012 talloc_free(mem_ctx);
1013 return ret;
1017 /*Local Variables:*/
1018 /*mode: c*/
1019 /*End:*/