s3:dbwrap: change the dbwrap_traverse() wrapper to return the count in an additional...
[Samba/gebeck_regimport.git] / source3 / utils / net_idmap_check.c
blob17389a53bb2c3617562918eb3fbc9d6829fa298c
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 "dbwrap/dbwrap_rbt.h"
32 #include "net.h"
33 #include "../libcli/security/dom_sid.h"
34 #include "cbuf.h"
35 #include "srprs.h"
36 #include <termios.h>
37 #include "util_tdb.h"
39 static int traverse_commit(struct db_record *diff_rec, void* data);
40 static int traverse_check(struct db_record *rec, void* data);
42 static char* interact_edit(TALLOC_CTX* mem_ctx, const char* str);
43 static int interact_prompt(const char* msg, const char* accept, char def);
45 /* TDB_DATA *******************************************************************/
46 static char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d);
47 static TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr);
48 static TDB_DATA talloc_copy(TALLOC_CTX* mem_ctx, TDB_DATA data);
49 static bool is_empty(TDB_DATA data) {
50 return (data.dsize == 0) || (data.dptr == NULL);
53 /* record *********************************************************************/
55 enum DT {
56 DT_INV = 0,
57 DT_SID, DT_UID, DT_GID,
58 DT_HWM, DT_VER, DT_SEQ,
61 struct record {
62 enum DT key_type, val_type;
63 TDB_DATA key, val;
64 struct dom_sid sid;
65 long unsigned id;
68 static struct record* parse_record(TALLOC_CTX* ctx, TDB_DATA key, TDB_DATA val);
69 static struct record* reverse_record(struct record* rec);
71 static bool is_invalid(const struct record* r) {
72 return (r->key_type == DT_INV) || (r->val_type == DT_INV);
75 static bool is_map(const struct record* r) {
76 return (r->key_type == DT_SID)
77 || (r->key_type == DT_UID) || (r->key_type == DT_GID);
80 /* action *********************************************************************/
82 typedef struct check_action {
83 const char* fmt;
84 const char* name;
85 const char* prompt;
86 const char* answers;
87 char auto_action;
88 char default_action;
89 bool verbose;
90 } check_action;
92 struct check_actions {
93 check_action invalid_record;
94 check_action missing_reverse;
95 check_action invalid_mapping;
96 check_action invalid_edit;
97 check_action record_exists;
98 check_action no_version;
99 check_action wrong_version;
100 check_action invalid_hwm;
101 check_action commit;
102 check_action valid_mapping;
103 check_action valid_other;
104 check_action invalid_diff;
107 static struct check_actions
108 check_actions_init(const struct check_options* opts) {
109 struct check_actions ret = {
110 .invalid_record = (check_action) {
111 .name = "Invalid record",
112 .prompt = "[e]dit/[d]elete/[D]elete all"
113 "/[s]kip/[S]kip all",
114 .answers = "eds",
115 .default_action = 'e',
116 .verbose = true,
118 .missing_reverse = (check_action) {
119 .name = "Missing reverse mapping for",
120 .prompt = "[f]ix/[F]ix all/[e]dit/[d]elete/[D]elete all"
121 "/[s]kip/[S]kip all",
122 .answers = "feds",
123 .default_action = 'f',
124 .verbose = true,
126 .invalid_mapping = (check_action) {
127 .fmt = "%1$s: %2$s -> %3$s\n(%4$s <- %3$s)\n",
128 .name = "Invalid mapping",
129 .prompt = "[e]dit/[d]elete/[D]elete all"
130 "/[s]kip/[S]kip all",
131 .answers = "eds",
132 .default_action = 'd',
133 .verbose = true,
135 .invalid_edit = (check_action) {
136 .name = "Invalid record",
137 .prompt = "[e]dit/[d]elete/[D]elete all"
138 "/[s]kip/[S]kip all",
139 .answers = "eds",
140 .default_action = 'e',
141 .verbose = true,
143 .record_exists = (check_action) {
144 .fmt = "%1$s: %2$s\n-%4$s\n+%3$s\n",
145 .name = "Record exists",
146 .prompt = "[o]verwrite/[O]verwrite all/[e]dit"
147 "/[d]elete/[D]elete all/[s]kip/[S]kip all",
148 .answers = "oeds",
149 .default_action = 'o',
150 .verbose = true,
152 .no_version = (check_action) {
153 .prompt = "[f]ix/[s]kip/[a]bort",
154 .answers = "fsa",
155 .default_action = 'f',
157 .wrong_version = (check_action) {
158 .prompt = "[f]ix/[s]kip/[a]bort",
159 .answers = "fsa",
160 .default_action = 'a',
162 .invalid_hwm = (check_action) {
163 .prompt = "[f]ix/[s]kip",
164 .answers = "fs",
165 .default_action = 'f',
167 .commit = (check_action) {
168 .prompt = "[c]ommit/[l]ist/[s]kip",
169 .answers = "cls",
170 .default_action = 'l',
171 .verbose = true,
173 .valid_mapping = (check_action) {
174 .fmt = "%1$s: %2$s <-> %3$s\n",
175 .name = "Mapping",
176 .auto_action = 's',
177 .verbose = opts->verbose,
179 .valid_other = (check_action) {
180 .name = "Other",
181 .auto_action = 's',
182 .verbose = opts->verbose,
184 .invalid_diff = (check_action) {
185 .prompt = "[s]kip/[S]kip all/[c]ommit/[C]ommit all"
186 "/[a]bort",
187 .answers = "sca",
188 .default_action = 's',
192 if (!opts->repair) {
193 ret.invalid_record.auto_action = 's';
194 ret.missing_reverse.auto_action = 's';
195 ret.invalid_mapping.auto_action = 's';
196 ret.no_version.auto_action = 's';
197 ret.wrong_version.auto_action = 's';
198 ret.invalid_hwm.auto_action = 's';
199 ret.commit.auto_action = 's';
202 if (opts->automatic) {
203 ret.invalid_record.auto_action = 'd'; /* delete */
204 ret.missing_reverse.auto_action = 'f'; /* fix */
205 ret.invalid_mapping.auto_action = 'd'; /* delete */
206 ret.no_version.auto_action = 'f'; /* fix */
207 ret.wrong_version.auto_action = 'a'; /* abort */
208 ret.invalid_hwm.auto_action = 'f'; /* fix */
209 ret.commit.auto_action = 'c'; /* commit */
210 ret.invalid_diff.auto_action = 'a'; /* abort */
211 if (opts->force) {
212 ret.wrong_version.auto_action = 'f'; /* fix */
213 ret.invalid_diff.auto_action = 'c'; /* commit */
216 if (opts->test) {
217 ret.invalid_diff.auto_action = 'c'; /* commit */
218 /* ret.commit.auto_action = 'c';*/ /* commit */
221 return ret;
224 static char get_action(struct check_action* a, struct record* r, TDB_DATA* v) {
225 char ret;
226 if (a->verbose && (r != NULL)) {
227 if (!a->fmt) {
228 d_printf("%s: %s ", a->name, print_data(r, r->key));
229 if (is_map(r)) {
230 d_printf("-> %s\n", print_data(r, r->val));
231 } else if (r->key_type == DT_HWM ||
232 r->key_type == DT_VER ||
233 r->key_type == DT_SEQ)
235 d_printf(": %ld\n", r->id);
236 } else {
237 d_printf("\n");
239 } else {
240 d_printf(a->fmt, a->name,
241 print_data(r, r->key),
242 print_data(r, r->val),
243 (v ? print_data(r, *v) : ""));
247 if (a->auto_action != '\0') {
248 return a->auto_action;
251 ret = interact_prompt(a->prompt, a->answers, a->default_action);
253 if (isupper(ret)) {
254 ret = tolower(ret);
255 a->auto_action = ret;
257 a->default_action = ret;
258 return ret;
261 /* *************************************************************************/
263 typedef struct {
264 TDB_DATA oval, nval;
265 } TDB_DATA_diff;
267 static TDB_DATA pack_diff(TDB_DATA_diff* diff) {
268 return (TDB_DATA) {
269 .dptr = (uint8_t *)diff,
270 .dsize = sizeof(TDB_DATA_diff),
274 static TDB_DATA_diff unpack_diff(TDB_DATA data) {
275 assert(data.dsize == sizeof(TDB_DATA_diff));
276 return *(TDB_DATA_diff*)data.dptr;
279 #define DEBUG_DIFF(LEV,MEM,MSG,KEY,OLD,NEW) \
280 DEBUG(LEV, ("%s: %s\n", MSG, print_data(MEM, KEY))); \
281 if (!is_empty(OLD)) { \
282 DEBUGADD(LEV, ("-%s\n", print_data(MEM, OLD))); \
284 if (!is_empty(NEW)) { \
285 DEBUGADD(LEV, ("+%s\n", print_data(MEM, NEW))); \
288 struct check_ctx {
289 int oflags;
290 char* name;
291 bool transaction;
292 struct db_context *db;
293 struct db_context *diff;
294 struct check_actions action;
296 uint32_t uid_hwm;
297 uint32_t gid_hwm;
299 unsigned n_invalid_record;
300 unsigned n_missing_reverse;
301 unsigned n_invalid_mappping;
302 unsigned n_map;
303 unsigned n_other;
304 unsigned n_diff;
305 struct check_options opts;
309 static void adjust_hwm(struct check_ctx* ctx, const struct record* r);
311 static int add_record(struct check_ctx* ctx, TDB_DATA key, TDB_DATA value)
313 NTSTATUS status;
314 TDB_DATA_diff diff;
315 TALLOC_CTX* mem = talloc_new(ctx->diff);
316 struct db_record* rec = ctx->diff->fetch_locked(ctx->diff, mem, key);
317 if (rec == NULL) {
318 return -1;
320 if (rec->value.dptr == 0) { /* first entry */
321 diff.oval = dbwrap_fetch(ctx->db, ctx->diff, key);
322 } else {
323 diff = unpack_diff(rec->value);
324 talloc_free(diff.nval.dptr);
326 diff.nval = talloc_copy(ctx->diff, value);
328 DEBUG_DIFF(2, mem, "TDB DIFF", key, diff.oval, diff.nval);
330 status = rec->store(rec, pack_diff(&diff), 0);
332 talloc_free(mem);
334 if (!NT_STATUS_IS_OK(status)) {
335 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
336 return -1;
338 ctx->n_diff ++;
339 return 0;
342 static int del_record(struct check_ctx* ctx, TDB_DATA key) {
343 return add_record(ctx, key, tdb_null);
346 static TDB_DATA
347 fetch_record(struct check_ctx* ctx, TALLOC_CTX* mem_ctx, TDB_DATA key)
349 TDB_DATA tmp;
351 if (ctx->diff->fetch(ctx->diff, mem_ctx, key, &tmp) != 0) {
352 DEBUG(0, ("Out of memory!\n"));
353 return tdb_null;
355 if (tmp.dptr != NULL) {
356 TDB_DATA_diff diff = unpack_diff(tmp);
357 TDB_DATA ret = talloc_copy(mem_ctx, diff.nval);
358 talloc_free(tmp.dptr);
359 return ret;
362 if (ctx->db->fetch(ctx->db, mem_ctx, key, &tmp) != 0) {
363 DEBUG(0, ("Out of memory!\n"));
364 return tdb_null;
367 return tmp;
370 static void edit_record(struct record* r) {
371 TALLOC_CTX* mem = talloc_new(r);
372 cbuf* ost = cbuf_new(mem);
373 const char* str;
374 struct record* nr;
375 TDB_DATA key;
376 TDB_DATA val;
377 cbuf_printf(ost, "%s %s\n",
378 print_data(mem, r->key), print_data(mem, r->val));
379 str = interact_edit(mem, cbuf_gets(ost, 0));
380 key = parse_data(mem, &str);
381 val = parse_data(mem, &str);
382 nr = parse_record(talloc_parent(r), key, val);
383 if (nr != NULL) {
384 *r = *nr;
386 talloc_free(mem);
389 static bool check_version(struct check_ctx* ctx) {
390 static const char* key = "IDMAP_VERSION";
391 uint32_t version;
392 bool no_version = !dbwrap_fetch_uint32(ctx->db, key, &version);
393 char action = 's';
394 struct check_actions* act = &ctx->action;
395 if (no_version) {
396 d_printf("No version number, assume 2\n");
397 action = get_action(&act->no_version, NULL, NULL);
398 } else if (version != 2) {
399 d_printf("Wrong version number %d, should be 2\n", version);
400 action = get_action(&act->wrong_version, NULL, NULL);
402 switch (action) {
403 case 's':
404 break;
405 case 'f':
406 SIVAL(&version, 0, 2);
407 add_record(ctx, string_term_tdb_data(key),
408 make_tdb_data((uint8_t *)&version, sizeof(uint32_t)));
409 break;
410 case 'a':
411 return false;
412 default:
413 assert(false);
415 return true;
418 static void check_hwm(struct check_ctx* ctx, const char* key, uint32_t target) {
419 uint32_t hwm;
420 char action = 's';
421 bool found = dbwrap_fetch_uint32(ctx->db, key, &hwm);
422 struct check_actions* act = &ctx->action;
423 if (!found) {
424 d_printf("No %s should be %d\n", key, target);
425 action = get_action(&act->invalid_hwm, NULL, NULL);
426 } else if (target < hwm) {
427 d_printf("Invalid %s %d: should be %d\n", key, hwm, target);
428 action = get_action(&act->invalid_hwm, NULL, NULL);
430 if (action == 'f') {
431 SIVAL(&hwm, 0, target);
432 add_record(ctx, string_term_tdb_data(key),
433 make_tdb_data((uint8_t *)&hwm, sizeof(uint32_t)));
437 int traverse_check(struct db_record *rec, void* data) {
438 struct check_ctx* ctx = (struct check_ctx*)data;
439 struct check_actions* act = &ctx->action;
440 TALLOC_CTX* mem = talloc_new(ctx->diff);
441 struct record* r = parse_record(mem, rec->key, rec->value);
442 char action = 's';
444 if (is_invalid(r)) {
445 action = get_action(&act->invalid_record, r, NULL);
446 ctx->n_invalid_record++;
447 } else if (is_map(r)) {
448 TDB_DATA back = fetch_record(ctx, mem, r->val);
449 if (back.dptr == NULL) {
450 action = get_action(&act->missing_reverse, r, NULL);
451 ctx->n_missing_reverse++;
452 } else if (!tdb_data_equal(r->key, back)) {
453 action = get_action(&act->invalid_mapping, r, &back);
454 ctx->n_invalid_mappping++;
455 } else {
456 if (r->key_type == DT_SID) {
457 action = get_action(&act->valid_mapping, r, NULL);
458 ctx->n_map++;
459 } else {
460 action = get_action(&act->valid_mapping, NULL,
461 NULL);
464 adjust_hwm(ctx, r);
465 } else {
466 action = get_action(&act->valid_other, r, NULL);
467 ctx->n_other++;
470 while (action) {
471 switch (action) {
472 case 's': /* skip */
473 break;
474 case 'd': /* delete */
475 del_record(ctx, rec->key);
476 break;
477 case 'f': /* add reverse mapping */
478 add_record(ctx, rec->value, rec->key);
479 break;
480 case 'e': /* edit */
481 edit_record(r);
482 action = 'o';
483 if (is_invalid(r)) {
484 action = get_action(&act->invalid_edit, r,NULL);
485 continue;
487 if (!tdb_data_equal(rec->key, r->key)) {
488 TDB_DATA oval = fetch_record(ctx, mem, r->key);
489 if (!is_empty(oval) &&
490 !tdb_data_equal(oval, r->val))
492 action = get_action(&act->record_exists,
493 r, &oval);
494 if (action != 'o') {
495 continue;
499 if (is_map(r)) {
500 TDB_DATA okey = fetch_record(ctx, mem, r->val);
501 if (!is_empty(okey) &&
502 !tdb_data_equal(okey, r->key))
504 action = get_action(&act->record_exists,
505 reverse_record(r),
506 &okey);
509 continue;
510 case 'o': /* overwrite */
511 adjust_hwm(ctx, r);
512 if (!tdb_data_equal(rec->key, r->key)) {
513 del_record(ctx, rec->key);
515 add_record(ctx, r->key, r->val);
516 if (is_map(r)) {
517 add_record(ctx, r->val, r->key);
520 action = '\0';
523 talloc_free(mem);
525 return 0;
528 /******************************************************************************/
530 void adjust_hwm(struct check_ctx* ctx, const struct record* r) {
531 enum DT type = (r->key_type == DT_SID) ? r->val_type : r->key_type;
532 if (type == DT_UID) {
533 ctx->uid_hwm = MAX(ctx->uid_hwm, r->id);
534 } else if (type == DT_GID) {
535 ctx->gid_hwm = MAX(ctx->gid_hwm, r->id);
539 TDB_DATA talloc_copy(TALLOC_CTX* mem_ctx, TDB_DATA data) {
540 TDB_DATA ret = {
541 .dptr = (uint8_t *)talloc_size(mem_ctx, data.dsize+1),
542 .dsize = data.dsize
544 if (ret.dptr == NULL) {
545 ret.dsize = 0;
546 } else {
547 memcpy(ret.dptr, data.dptr, data.dsize);
548 ret.dptr[ret.dsize] = '\0';
550 return ret;
553 static bool is_cstr(TDB_DATA str) {
554 return !is_empty(str) && str.dptr[str.dsize-1] == '\0';
557 static bool parse_sid (TDB_DATA str, enum DT* type, struct dom_sid* sid) {
558 struct dom_sid tmp;
559 const char* s = (const char*)str.dptr;
560 if ((s[0] == 'S') && string_to_sid(&tmp, s)) {
561 *sid = tmp;
562 *type = DT_SID;
563 return true;
565 return false;
568 static bool parse_xid(TDB_DATA str, enum DT* type, unsigned long* id) {
569 char c, t;
570 unsigned long tmp;
571 if (sscanf((const char*)str.dptr, "%cID %lu%c", &c, &tmp, &t) == 2) {
572 if (c == 'U') {
573 *id = tmp;
574 *type = DT_UID;
575 return true;
576 } else if (c == 'G') {
577 *id = tmp;
578 *type = DT_GID;
579 return true;
582 return false;
586 struct record*
587 parse_record(TALLOC_CTX* mem_ctx, TDB_DATA key, TDB_DATA val)
589 struct record* ret = talloc_zero(mem_ctx, struct record);
590 if (ret == NULL) {
591 DEBUG(0, ("Out of memory.\n"));
592 return NULL;
594 ret->key = talloc_copy(ret, key);
595 ret->val = talloc_copy(ret, val);
596 if ((ret->key.dptr == NULL && key.dptr != NULL) ||
597 (ret->val.dptr == NULL && val.dptr != NULL))
599 talloc_free(ret);
600 DEBUG(0, ("Out of memory.\n"));
601 return NULL;
603 assert((ret->key_type == DT_INV) && (ret->val_type == DT_INV));
605 if (!is_cstr(key)) {
606 return ret;
608 if (parse_sid(key, &ret->key_type, &ret->sid)) {
609 parse_xid(val, &ret->val_type, &ret->id);
610 } else if (parse_xid(key, &ret->key_type, &ret->id)) {
611 if (is_cstr(val)) {
612 parse_sid(val, &ret->val_type, &ret->sid);
614 } else if (strcmp((const char*)key.dptr, "USER HWM") == 0) {
615 ret->key_type = DT_HWM;
616 if (val.dsize == 4) {
617 ret->id = IVAL(val.dptr,0);
618 ret->val_type = DT_UID;
620 } else if (strcmp((const char*)key.dptr, "GROUP HWM") == 0) {
621 ret->key_type = DT_HWM;
622 if (val.dsize == 4) {
623 ret->id = IVAL(val.dptr,0);
624 ret->val_type = DT_GID;
626 } else if (strcmp((const char*)key.dptr, "IDMAP_VERSION") == 0) {
627 ret->key_type = DT_VER;
628 if (val.dsize == 4) {
629 ret->id = IVAL(val.dptr,0);
630 ret->val_type = DT_VER;
632 } else if (strcmp((const char*)key.dptr, "__db_sequence_number__") == 0) {
633 ret->key_type = DT_SEQ;
634 if (val.dsize == 8) {
635 ret->id = *(uint64_t*)val.dptr;
636 ret->val_type = DT_SEQ;
640 return ret;
643 struct record* reverse_record(struct record* in)
645 return parse_record(talloc_parent(in), in->val, in->key);
649 /******************************************************************************/
651 int interact_prompt(const char* msg, const char* acc, char def) {
652 struct termios old_tio, new_tio;
653 int c;
655 tcgetattr(STDIN_FILENO, &old_tio);
656 new_tio=old_tio;
657 new_tio.c_lflag &=(~ICANON & ~ECHO);
658 tcsetattr(STDIN_FILENO, TCSANOW, &new_tio);
660 do {
661 d_printf("%s? [%c]\n", msg, def);
662 fflush(stdout);
663 c = getchar();
664 if (c == '\n') {
665 c = def;
666 break;
668 else if (strchr(acc, tolower(c)) != NULL) {
669 break;
671 d_printf("Invalid input '%c'\n", c);
672 } while(c != EOF);
673 tcsetattr(STDIN_FILENO, TCSANOW, &old_tio);
674 return c;
677 char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d)
679 if (!is_empty(d)) {
680 char* ret = NULL;
681 cbuf* ost = cbuf_new(mem_ctx);
682 int len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize);
683 if (len != -1) {
684 cbuf_swapptr(ost, &ret, 0);
685 talloc_steal(mem_ctx, ret);
687 talloc_free(ost);
688 return ret;
690 return talloc_strdup(mem_ctx, "<NULL>");
694 TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr) {
695 cbuf* ost = cbuf_new(mem_ctx);
696 TDB_DATA ret = tdb_null;
697 srprs_skipws(ptr);
698 if (srprs_quoted(ptr, ost)) {
699 ret.dsize = cbuf_getpos(ost);
700 ret.dptr = (uint8_t *)talloc_steal(mem_ctx, cbuf_gets(ost,0));
702 talloc_free(ost);
703 return ret;
706 static const char* get_editor(void) {
707 static const char* editor = NULL;
708 if (editor == NULL) {
709 editor = getenv("VISUAL");
710 if (editor == NULL) {
711 editor = getenv("EDITOR");
713 if (editor == NULL) {
714 editor = "vi";
717 return editor;
720 char* interact_edit(TALLOC_CTX* mem_ctx, const char* str) {
721 char fname[] = "/tmp/net_idmap_check.XXXXXX";
722 char buf[128];
723 char* ret = NULL;
724 FILE* file;
726 int fd = mkstemp(fname);
727 if (fd == -1) {
728 DEBUG(0, ("failed to mkstemp %s: %s\n", fname,
729 strerror(errno)));
730 return NULL;
733 file = fdopen(fd, "w");
734 if (!file) {
735 DEBUG(0, ("failed to open %s for writing: %s\n", fname,
736 strerror(errno)));
737 close(fd);
738 unlink(fname);
739 return NULL;
742 fprintf(file, "%s", str);
743 fclose(file);
745 snprintf(buf, sizeof(buf), "%s %s\n", get_editor(), fname);
746 if (system(buf) != 0) {
747 DEBUG(0, ("failed to start editor %s: %s\n", buf,
748 strerror(errno)));
749 unlink(fname);
750 return NULL;
753 file = fopen(fname, "r");
754 if (!file) {
755 DEBUG(0, ("failed to open %s for reading: %s\n", fname,
756 strerror(errno)));
757 unlink(fname);
758 return NULL;
760 while ( fgets(buf, sizeof(buf), file) ) {
761 ret = talloc_strdup_append(ret, buf);
763 fclose(file);
764 unlink(fname);
766 return talloc_steal(mem_ctx, ret);
770 static int traverse_print_diff(struct db_record *rec, void* data) {
771 struct check_ctx* ctx = (struct check_ctx*)data;
772 TDB_DATA key = rec->key;
773 TDB_DATA_diff diff = unpack_diff(rec->value);
774 TALLOC_CTX* mem = talloc_new(ctx->diff);
776 DEBUG_DIFF(0, mem, "DIFF", key, diff.oval, diff.nval);
778 talloc_free(mem);
779 return 0;
783 static int traverse_commit(struct db_record *diff_rec, void* data) {
784 struct check_ctx* ctx = (struct check_ctx*)data;
785 TDB_DATA_diff diff = unpack_diff(diff_rec->value);
786 TDB_DATA key = diff_rec->key;
787 TALLOC_CTX* mem = talloc_new(ctx->diff);
788 int ret = -1;
789 NTSTATUS status;
790 struct check_actions* act = &ctx->action;
792 struct db_record* rec = ctx->db->fetch_locked(ctx->db, mem, key);
793 if (rec == NULL) {
794 goto done;
797 if (!tdb_data_equal(rec->value, diff.oval)) {
798 char action;
800 d_printf("Warning: record has changed: %s\n"
801 "expected: %s got %s\n", print_data(mem, key),
802 print_data(mem, diff.oval),
803 print_data(mem, rec->value));
805 action = get_action(&act->invalid_diff, NULL, NULL);
806 if (action == 's') {
807 ret = 0;
808 goto done;
809 } else if (action == 'a') {
810 goto done;
814 DEBUG_DIFF(0, mem, "Commit", key, diff.oval, diff.nval);
816 if (is_empty(diff.nval)) {
817 status = rec->delete_rec(rec);
818 } else {
819 status = rec->store(rec, diff.nval, 0);
822 if (!NT_STATUS_IS_OK(status)) {
823 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
824 if (!ctx->opts.force) {
825 goto done;
828 ret = 0;
829 done:
830 talloc_free(mem);
831 return ret;
834 static struct check_ctx*
835 check_init(TALLOC_CTX* mem_ctx, const struct check_options* o)
837 struct check_ctx* ctx = talloc_zero(mem_ctx, struct check_ctx);
838 if (ctx == NULL) {
839 DEBUG(0, (_("No memory\n")));
840 return NULL;
843 ctx->diff = db_open_rbt(ctx);
844 if (ctx->diff == NULL) {
845 talloc_free(ctx);
846 DEBUG(0, (_("No memory\n")));
847 return NULL;
850 ctx->action = check_actions_init(o);
851 ctx->opts = *o;
852 return ctx;
855 static bool check_open_db(struct check_ctx* ctx, const char* name, int oflags)
857 if (name == NULL) {
858 d_fprintf(stderr, _("Error: name == NULL in check_open_db().\n"));
859 return false;
862 if (ctx->db != NULL) {
863 if ((ctx->oflags == oflags) && (strcmp(ctx->name, name))) {
864 return true;
865 } else {
866 TALLOC_FREE(ctx->db);
870 ctx->db = db_open(ctx, name, 0, TDB_DEFAULT, oflags, 0);
871 if (ctx->db == NULL) {
872 d_fprintf(stderr,
873 _("Could not open idmap db (%s) for writing: %s\n"),
874 name, strerror(errno));
875 return false;
878 if (ctx->name != name) {
879 TALLOC_FREE(ctx->name);
880 ctx->name = talloc_strdup(ctx, name);
883 ctx->oflags = oflags;
884 return true;
887 static bool check_do_checks(struct check_ctx* ctx)
889 NTSTATUS status;
891 if (!check_version(ctx)) {
892 return false;
895 status = dbwrap_traverse(ctx->db, traverse_check, ctx, NULL);
897 if (!NT_STATUS_IS_OK(status)) {
898 DEBUG(0, ("failed to traverse %s\n", ctx->name));
899 return false;
902 check_hwm(ctx, "USER HWM", ctx->uid_hwm + 1);
903 check_hwm(ctx, "GROUP HWM", ctx->gid_hwm + 1);
905 return true;
908 static void check_summary(const struct check_ctx* ctx)
910 d_printf("uid hwm: %d\ngid hwm: %d\n", ctx->uid_hwm, ctx->gid_hwm);
911 d_printf("mappings: %d\nother: %d\n", ctx->n_map, ctx->n_other);
912 d_printf("invalid records: %d\nmissing links: %d\ninvalid links: %d\n",
913 ctx->n_invalid_record, ctx->n_missing_reverse,
914 ctx->n_invalid_mappping);
915 d_printf("%u changes:\n", ctx->n_diff);
918 static bool check_transaction_start(struct check_ctx* ctx) {
919 return (ctx->db->transaction_start(ctx->db) == 0);
922 static bool check_transaction_commit(struct check_ctx* ctx) {
923 return (ctx->db->transaction_commit(ctx->db) == 0);
926 static bool check_transaction_cancel(struct check_ctx* ctx) {
927 return (ctx->db->transaction_cancel(ctx->db) == 0);
931 static void check_diff_list(struct check_ctx* ctx) {
932 NTSTATUS status = dbwrap_traverse(ctx->diff, traverse_print_diff, ctx, NULL);
934 if (!NT_STATUS_IS_OK(status)) {
935 DEBUG(0, ("failed to traverse diff\n"));
940 static bool check_commit(struct check_ctx* ctx)
942 struct check_actions* act = &ctx->action;
943 char action;
944 NTSTATUS status = NT_STATUS_OK;
946 check_summary(ctx);
948 if (ctx->n_diff == 0) {
949 return true;
952 while ((action = get_action(&act->commit, NULL, NULL)) == 'l') {
953 check_diff_list(ctx);
955 if (action == 's') {
956 return true;
958 assert(action == 'c');
960 if (!check_open_db(ctx, ctx->name, O_RDWR)) {
961 return false;
964 if (!check_transaction_start(ctx)) {
965 return false;
968 status = dbwrap_traverse(ctx->diff, traverse_commit, ctx, NULL);
970 if (!NT_STATUS_IS_OK(status)) {
971 check_transaction_cancel(ctx);
972 return false;
974 if (ctx->opts.test) { /*get_action? */
975 return check_transaction_cancel(ctx);
976 } else {
977 return check_transaction_commit(ctx);
981 int net_idmap_check_db(const char* db, const struct check_options* o)
983 int ret = -1;
984 TALLOC_CTX* mem_ctx = talloc_stackframe();
985 struct check_ctx* ctx = check_init(mem_ctx, o);
987 if (!o->automatic && !isatty(STDIN_FILENO)) {
988 DEBUG(0, ("Interactive use needs tty, use --auto\n"));
989 goto done;
991 if (o->lock) {
992 if (check_open_db(ctx, db, O_RDWR)
993 && check_transaction_start(ctx))
995 if ( check_do_checks(ctx)
996 && check_commit(ctx)
997 && check_transaction_commit(ctx))
999 ret = 0;
1000 } else {
1001 check_transaction_cancel(ctx);
1004 } else {
1005 if (check_open_db(ctx, db, O_RDONLY)
1006 && check_do_checks(ctx)
1007 && check_commit(ctx))
1009 ret = 0;
1012 done:
1013 talloc_free(mem_ctx);
1014 return ret;
1018 /*Local Variables:*/
1019 /*mode: c*/
1020 /*End:*/