docs: mention --port in nmbd manpage.
[Samba.git] / source3 / utils / net_idmap_check.c
blobe75c8906de18a10a0ebe069b3207f61929ec2c8f
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 "util_tdb.h"
37 #include "interact.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 /* TDB_DATA *******************************************************************/
43 static char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d);
44 static TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr);
46 /* record *********************************************************************/
48 enum DT {
49 DT_INV = 0,
50 DT_SID, DT_UID, DT_GID,
51 DT_HWM, DT_VER, DT_SEQ,
54 struct record {
55 enum DT key_type, val_type;
56 TDB_DATA key, val;
57 struct dom_sid sid;
58 long unsigned id;
61 static struct record* parse_record(TALLOC_CTX* ctx, TDB_DATA key, TDB_DATA val);
62 static struct record* reverse_record(struct record* rec);
64 static bool is_invalid(const struct record* r) {
65 return (r->key_type == DT_INV) || (r->val_type == DT_INV);
68 static bool is_map(const struct record* r) {
69 return (r->key_type == DT_SID)
70 || (r->key_type == DT_UID) || (r->key_type == DT_GID);
73 /* action *********************************************************************/
75 typedef struct check_action {
76 const char* fmt;
77 const char* name;
78 const char* prompt;
79 const char* answers;
80 char auto_action;
81 char default_action;
82 bool verbose;
83 } check_action;
85 struct check_actions {
86 check_action invalid_record;
87 check_action missing_reverse;
88 check_action invalid_mapping;
89 check_action invalid_edit;
90 check_action record_exists;
91 check_action no_version;
92 check_action wrong_version;
93 check_action invalid_hwm;
94 check_action commit;
95 check_action valid_mapping;
96 check_action valid_other;
97 check_action invalid_diff;
100 static struct check_actions
101 check_actions_init(const struct check_options* opts) {
102 struct check_actions ret = {
103 .invalid_record = (check_action) {
104 .name = "Invalid record",
105 .prompt = "[e]dit/[d]elete/[D]elete all"
106 "/[s]kip/[S]kip all",
107 .answers = "eds",
108 .default_action = 'e',
109 .verbose = true,
111 .missing_reverse = (check_action) {
112 .name = "Missing reverse mapping for",
113 .prompt = "[f]ix/[F]ix all/[e]dit/[d]elete/[D]elete all"
114 "/[s]kip/[S]kip all",
115 .answers = "feds",
116 .default_action = 'f',
117 .verbose = true,
119 .invalid_mapping = (check_action) {
120 .fmt = "%1$s: %2$s -> %3$s\n(%4$s <- %3$s)\n",
121 .name = "Invalid mapping",
122 .prompt = "[e]dit/[d]elete/[D]elete all"
123 "/[s]kip/[S]kip all",
124 .answers = "eds",
125 .default_action = 'd',
126 .verbose = true,
128 .invalid_edit = (check_action) {
129 .name = "Invalid record",
130 .prompt = "[e]dit/[d]elete/[D]elete all"
131 "/[s]kip/[S]kip all",
132 .answers = "eds",
133 .default_action = 'e',
134 .verbose = true,
136 .record_exists = (check_action) {
137 .fmt = "%1$s: %2$s\n-%4$s\n+%3$s\n",
138 .name = "Record exists",
139 .prompt = "[o]verwrite/[O]verwrite all/[e]dit"
140 "/[d]elete/[D]elete all/[s]kip/[S]kip all",
141 .answers = "oeds",
142 .default_action = 'o',
143 .verbose = true,
145 .no_version = (check_action) {
146 .prompt = "[f]ix/[s]kip/[a]bort",
147 .answers = "fsa",
148 .default_action = 'f',
150 .wrong_version = (check_action) {
151 .prompt = "[f]ix/[s]kip/[a]bort",
152 .answers = "fsa",
153 .default_action = 'a',
155 .invalid_hwm = (check_action) {
156 .prompt = "[f]ix/[s]kip",
157 .answers = "fs",
158 .default_action = 'f',
160 .commit = (check_action) {
161 .prompt = "[c]ommit/[l]ist/[s]kip",
162 .answers = "cls",
163 .default_action = 'l',
164 .verbose = true,
166 .valid_mapping = (check_action) {
167 .fmt = "%1$s: %2$s <-> %3$s\n",
168 .name = "Mapping",
169 .auto_action = 's',
170 .verbose = opts->verbose,
172 .valid_other = (check_action) {
173 .name = "Other",
174 .auto_action = 's',
175 .verbose = opts->verbose,
177 .invalid_diff = (check_action) {
178 .prompt = "[s]kip/[S]kip all/[c]ommit/[C]ommit all"
179 "/[a]bort",
180 .answers = "sca",
181 .default_action = 's',
185 if (!opts->repair) {
186 ret.invalid_record.auto_action = 's';
187 ret.missing_reverse.auto_action = 's';
188 ret.invalid_mapping.auto_action = 's';
189 ret.no_version.auto_action = 's';
190 ret.wrong_version.auto_action = 's';
191 ret.invalid_hwm.auto_action = 's';
192 ret.commit.auto_action = 's';
195 if (opts->automatic) {
196 ret.invalid_record.auto_action = 'd'; /* delete */
197 ret.missing_reverse.auto_action = 'f'; /* fix */
198 ret.invalid_mapping.auto_action = 'd'; /* delete */
199 ret.no_version.auto_action = 'f'; /* fix */
200 ret.wrong_version.auto_action = 'a'; /* abort */
201 ret.invalid_hwm.auto_action = 'f'; /* fix */
202 ret.commit.auto_action = 'c'; /* commit */
203 ret.invalid_diff.auto_action = 'a'; /* abort */
204 if (opts->force) {
205 ret.wrong_version.auto_action = 'f'; /* fix */
206 ret.invalid_diff.auto_action = 'c'; /* commit */
209 if (opts->test) {
210 ret.invalid_diff.auto_action = 'c'; /* commit */
211 /* ret.commit.auto_action = 'c';*/ /* commit */
214 return ret;
217 static char get_action(struct check_action* a, struct record* r, TDB_DATA* v) {
218 char ret;
219 if (a->verbose && (r != NULL)) {
220 if (!a->fmt) {
221 d_printf("%s: %s ", a->name, print_data(r, r->key));
222 if (is_map(r)) {
223 d_printf("-> %s\n", print_data(r, r->val));
224 } else if (r->key_type == DT_HWM ||
225 r->key_type == DT_VER ||
226 r->key_type == DT_SEQ)
228 d_printf(": %ld\n", r->id);
229 } else {
230 d_printf("\n");
232 } else {
233 d_printf(a->fmt, a->name,
234 print_data(r, r->key),
235 print_data(r, r->val),
236 (v ? print_data(r, *v) : ""));
240 if (a->auto_action != '\0') {
241 return a->auto_action;
244 ret = interact_prompt(a->prompt, a->answers, a->default_action);
246 if (isupper(ret)) {
247 ret = tolower(ret);
248 a->auto_action = ret;
250 a->default_action = ret;
251 return ret;
254 /* *************************************************************************/
256 typedef struct {
257 TDB_DATA oval, nval;
258 } TDB_DATA_diff;
260 static TDB_DATA pack_diff(TDB_DATA_diff* diff) {
261 return (TDB_DATA) {
262 .dptr = (uint8_t *)diff,
263 .dsize = sizeof(TDB_DATA_diff),
267 static TDB_DATA_diff unpack_diff(TDB_DATA data) {
268 assert(data.dsize == sizeof(TDB_DATA_diff));
269 return *(TDB_DATA_diff*)data.dptr;
272 #define DEBUG_DIFF(LEV,MEM,MSG,KEY,OLD,NEW) \
273 DEBUG(LEV, ("%s: %s\n", MSG, print_data(MEM, KEY))); \
274 if (!tdb_data_is_empty(OLD)) { \
275 DEBUGADD(LEV, ("-%s\n", print_data(MEM, OLD))); \
277 if (!tdb_data_is_empty(NEW)) { \
278 DEBUGADD(LEV, ("+%s\n", print_data(MEM, NEW))); \
281 struct check_ctx {
282 int oflags;
283 char* name;
284 bool transaction;
285 struct db_context *db;
286 struct db_context *diff;
287 struct check_actions action;
289 uint32_t uid_hwm;
290 uint32_t gid_hwm;
292 unsigned n_invalid_record;
293 unsigned n_missing_reverse;
294 unsigned n_invalid_mappping;
295 unsigned n_map;
296 unsigned n_other;
297 unsigned n_diff;
298 struct check_options opts;
302 static void adjust_hwm(struct check_ctx* ctx, const struct record* r);
304 static int add_record(struct check_ctx* ctx, TDB_DATA key, TDB_DATA value)
306 NTSTATUS status;
307 TDB_DATA_diff diff;
308 TALLOC_CTX* mem = talloc_new(ctx->diff);
309 TDB_DATA recvalue;
310 struct db_record *rec = dbwrap_fetch_locked(ctx->diff, mem, key);
312 if (rec == NULL) {
313 return -1;
316 recvalue = dbwrap_record_get_value(rec);
318 if (recvalue.dptr == 0) { /* first entry */
319 status = dbwrap_fetch(ctx->db, ctx->diff, key, &diff.oval);
320 if (!NT_STATUS_IS_OK(status)) {
321 diff.oval = tdb_null;
323 } else {
324 diff = unpack_diff(recvalue);
325 talloc_free(diff.nval.dptr);
327 diff.nval = tdb_data_talloc_copy(ctx->diff, value);
329 DEBUG_DIFF(2, mem, "TDB DIFF", key, diff.oval, diff.nval);
331 status = dbwrap_record_store(rec, pack_diff(&diff), 0);
333 talloc_free(mem);
335 if (!NT_STATUS_IS_OK(status)) {
336 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
337 return -1;
339 ctx->n_diff ++;
340 return 0;
343 static int del_record(struct check_ctx* ctx, TDB_DATA key) {
344 return add_record(ctx, key, tdb_null);
347 static TDB_DATA
348 fetch_record(struct check_ctx* ctx, TALLOC_CTX* mem_ctx, TDB_DATA key)
350 TDB_DATA tmp;
351 NTSTATUS status;
353 status = dbwrap_fetch(ctx->diff, mem_ctx, key, &tmp);
355 if (NT_STATUS_IS_OK(status)) {
356 TDB_DATA_diff diff = unpack_diff(tmp);
357 TDB_DATA ret = tdb_data_talloc_copy(mem_ctx, diff.nval);
358 talloc_free(tmp.dptr);
359 return ret;
362 status = dbwrap_fetch(ctx->db, mem_ctx, key, &tmp);
363 if (!NT_STATUS_IS_OK(status)) {
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 NTSTATUS status;
393 char action = 's';
394 struct check_actions* act = &ctx->action;
396 status = dbwrap_fetch_uint32_bystring(ctx->db, key, &version);
397 if (!NT_STATUS_IS_OK(status)) {
398 d_printf("No version number, assume 2\n");
399 action = get_action(&act->no_version, NULL, NULL);
400 } else if (version != 2) {
401 d_printf("Wrong version number %d, should be 2\n", version);
402 action = get_action(&act->wrong_version, NULL, NULL);
404 switch (action) {
405 case 's':
406 break;
407 case 'f':
408 SIVAL(&version, 0, 2);
409 add_record(ctx, string_term_tdb_data(key),
410 make_tdb_data((uint8_t *)&version, sizeof(uint32_t)));
411 break;
412 case 'a':
413 return false;
414 default:
415 assert(false);
417 return true;
420 static void check_hwm(struct check_ctx* ctx, const char* key, uint32_t target) {
421 uint32_t hwm;
422 char action = 's';
423 NTSTATUS status;
424 struct check_actions* act = &ctx->action;
426 status = dbwrap_fetch_uint32_bystring(ctx->db, key, &hwm);
427 if (!NT_STATUS_IS_OK(status)) {
428 d_printf("No %s should be %d\n", key, target);
429 action = get_action(&act->invalid_hwm, NULL, NULL);
430 } else if (target < hwm) {
431 d_printf("Invalid %s %d: should be %d\n", key, hwm, target);
432 action = get_action(&act->invalid_hwm, NULL, NULL);
434 if (action == 'f') {
435 SIVAL(&hwm, 0, target);
436 add_record(ctx, string_term_tdb_data(key),
437 make_tdb_data((uint8_t *)&hwm, sizeof(uint32_t)));
441 int traverse_check(struct db_record *rec, void* data) {
442 struct check_ctx* ctx = (struct check_ctx*)data;
443 struct check_actions* act = &ctx->action;
444 TALLOC_CTX* mem = talloc_new(ctx->diff);
445 TDB_DATA key;
446 TDB_DATA value;
447 struct record *r;
448 char action = 's';
450 key = dbwrap_record_get_key(rec);
451 value = dbwrap_record_get_value(rec);
453 r = parse_record(mem, key, value);
455 if (is_invalid(r)) {
456 action = get_action(&act->invalid_record, r, NULL);
457 ctx->n_invalid_record++;
458 } else if (is_map(r)) {
459 TDB_DATA back = fetch_record(ctx, mem, r->val);
460 if (back.dptr == NULL) {
461 action = get_action(&act->missing_reverse, r, NULL);
462 ctx->n_missing_reverse++;
463 } else if (!tdb_data_equal(r->key, back)) {
464 action = get_action(&act->invalid_mapping, r, &back);
465 ctx->n_invalid_mappping++;
466 } else {
467 if (r->key_type == DT_SID) {
468 action = get_action(&act->valid_mapping, r, NULL);
469 ctx->n_map++;
470 } else {
471 action = get_action(&act->valid_mapping, NULL,
472 NULL);
475 adjust_hwm(ctx, r);
476 } else {
477 action = get_action(&act->valid_other, r, NULL);
478 ctx->n_other++;
481 while (action) {
482 switch (action) {
483 case 's': /* skip */
484 break;
485 case 'd': /* delete */
486 del_record(ctx, key);
487 break;
488 case 'f': /* add reverse mapping */
489 add_record(ctx, value, key);
490 break;
491 case 'e': /* edit */
492 edit_record(r);
493 action = 'o';
494 if (is_invalid(r)) {
495 action = get_action(&act->invalid_edit, r,NULL);
496 continue;
498 if (!tdb_data_equal(key, r->key)) {
499 TDB_DATA oval = fetch_record(ctx, mem, r->key);
500 if (!tdb_data_is_empty(oval) &&
501 !tdb_data_equal(oval, r->val))
503 action = get_action(&act->record_exists,
504 r, &oval);
505 if (action != 'o') {
506 continue;
510 if (is_map(r)) {
511 TDB_DATA okey = fetch_record(ctx, mem, r->val);
512 if (!tdb_data_is_empty(okey) &&
513 !tdb_data_equal(okey, r->key))
515 action = get_action(&act->record_exists,
516 reverse_record(r),
517 &okey);
520 continue;
521 case 'o': /* overwrite */
522 adjust_hwm(ctx, r);
523 if (!tdb_data_equal(key, r->key)) {
524 del_record(ctx, key);
526 add_record(ctx, r->key, r->val);
527 if (is_map(r)) {
528 add_record(ctx, r->val, r->key);
531 action = '\0';
534 talloc_free(mem);
536 return 0;
539 /******************************************************************************/
541 void adjust_hwm(struct check_ctx* ctx, const struct record* r) {
542 enum DT type = (r->key_type == DT_SID) ? r->val_type : r->key_type;
543 if (type == DT_UID) {
544 ctx->uid_hwm = MAX(ctx->uid_hwm, r->id);
545 } else if (type == DT_GID) {
546 ctx->gid_hwm = MAX(ctx->gid_hwm, r->id);
550 static bool is_cstr(TDB_DATA str) {
551 return !tdb_data_is_empty(str) && str.dptr[str.dsize-1] == '\0';
554 static bool parse_sid (TDB_DATA str, enum DT* type, struct dom_sid* sid) {
555 struct dom_sid tmp;
556 const char* s = (const char*)str.dptr;
557 if ((s[0] == 'S') && string_to_sid(&tmp, s)) {
558 *sid = tmp;
559 *type = DT_SID;
560 return true;
562 return false;
565 static bool parse_xid(TDB_DATA str, enum DT* type, unsigned long* id) {
566 char c, t;
567 unsigned long tmp;
568 if (sscanf((const char*)str.dptr, "%cID %lu%c", &c, &tmp, &t) == 2) {
569 if (c == 'U') {
570 *id = tmp;
571 *type = DT_UID;
572 return true;
573 } else if (c == 'G') {
574 *id = tmp;
575 *type = DT_GID;
576 return true;
579 return false;
583 struct record*
584 parse_record(TALLOC_CTX* mem_ctx, TDB_DATA key, TDB_DATA val)
586 struct record* ret = talloc_zero(mem_ctx, struct record);
587 if (ret == NULL) {
588 DEBUG(0, ("Out of memory.\n"));
589 return NULL;
591 ret->key = tdb_data_talloc_copy(ret, key);
592 ret->val = tdb_data_talloc_copy(ret, val);
593 if ((ret->key.dptr == NULL && key.dptr != NULL) ||
594 (ret->val.dptr == NULL && val.dptr != NULL))
596 talloc_free(ret);
597 DEBUG(0, ("Out of memory.\n"));
598 return NULL;
600 assert((ret->key_type == DT_INV) && (ret->val_type == DT_INV));
602 if (!is_cstr(key)) {
603 return ret;
605 if (parse_sid(key, &ret->key_type, &ret->sid)) {
606 parse_xid(val, &ret->val_type, &ret->id);
607 } else if (parse_xid(key, &ret->key_type, &ret->id)) {
608 if (is_cstr(val)) {
609 parse_sid(val, &ret->val_type, &ret->sid);
611 } else if (strcmp((const char*)key.dptr, "USER HWM") == 0) {
612 ret->key_type = DT_HWM;
613 if (val.dsize == 4) {
614 ret->id = IVAL(val.dptr,0);
615 ret->val_type = DT_UID;
617 } else if (strcmp((const char*)key.dptr, "GROUP HWM") == 0) {
618 ret->key_type = DT_HWM;
619 if (val.dsize == 4) {
620 ret->id = IVAL(val.dptr,0);
621 ret->val_type = DT_GID;
623 } else if (strcmp((const char*)key.dptr, "IDMAP_VERSION") == 0) {
624 ret->key_type = DT_VER;
625 if (val.dsize == 4) {
626 ret->id = IVAL(val.dptr,0);
627 ret->val_type = DT_VER;
629 } else if (strcmp((const char*)key.dptr, "__db_sequence_number__") == 0) {
630 ret->key_type = DT_SEQ;
631 if (val.dsize == 8) {
632 ret->id = *(uint64_t*)val.dptr;
633 ret->val_type = DT_SEQ;
637 return ret;
640 struct record* reverse_record(struct record* in)
642 return parse_record(talloc_parent(in), in->val, in->key);
646 /******************************************************************************/
649 char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d)
651 if (!tdb_data_is_empty(d)) {
652 char* ret = NULL;
653 cbuf* ost = cbuf_new(mem_ctx);
654 int len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize);
655 if (len != -1) {
656 cbuf_swapptr(ost, &ret, 0);
657 talloc_steal(mem_ctx, ret);
659 talloc_free(ost);
660 return ret;
662 return talloc_strdup(mem_ctx, "<NULL>");
666 TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr) {
667 cbuf* ost = cbuf_new(mem_ctx);
668 TDB_DATA ret = tdb_null;
669 srprs_skipws(ptr);
670 if (srprs_quoted(ptr, ost)) {
671 ret.dsize = cbuf_getpos(ost);
672 ret.dptr = (uint8_t *)talloc_steal(mem_ctx, cbuf_gets(ost,0));
674 talloc_free(ost);
675 return ret;
678 static int traverse_print_diff(struct db_record *rec, void* data) {
679 struct check_ctx* ctx = (struct check_ctx*)data;
680 TDB_DATA key;
681 TDB_DATA value;
682 TDB_DATA_diff diff;
683 TALLOC_CTX* mem = talloc_new(ctx->diff);
685 key = dbwrap_record_get_key(rec);
686 value = dbwrap_record_get_value(rec);
687 diff = unpack_diff(value);
689 DEBUG_DIFF(0, mem, "DIFF", key, diff.oval, diff.nval);
691 talloc_free(mem);
692 return 0;
696 static int traverse_commit(struct db_record *diff_rec, void* data) {
697 struct check_ctx* ctx = (struct check_ctx*)data;
698 TDB_DATA key;
699 TDB_DATA diff_value;
700 TDB_DATA_diff diff;
701 TDB_DATA value;
702 TALLOC_CTX* mem = talloc_new(ctx->diff);
703 int ret = -1;
704 NTSTATUS status;
705 struct check_actions* act = &ctx->action;
706 struct db_record* rec;
708 key = dbwrap_record_get_key(diff_rec);
709 diff_value = dbwrap_record_get_value(diff_rec);
710 diff = unpack_diff(diff_value);
712 rec = dbwrap_fetch_locked(ctx->db, mem, key);
713 if (rec == NULL) {
714 goto done;
717 value = dbwrap_record_get_value(rec);
719 if (!tdb_data_equal(value, diff.oval)) {
720 char action;
722 d_printf("Warning: record has changed: %s\n"
723 "expected: %s got %s\n", print_data(mem, key),
724 print_data(mem, diff.oval),
725 print_data(mem, value));
727 action = get_action(&act->invalid_diff, NULL, NULL);
728 if (action == 's') {
729 ret = 0;
730 goto done;
731 } else if (action == 'a') {
732 goto done;
736 DEBUG_DIFF(0, mem, "Commit", key, diff.oval, diff.nval);
738 if (tdb_data_is_empty(diff.nval)) {
739 status = dbwrap_record_delete(rec);
740 } else {
741 status = dbwrap_record_store(rec, diff.nval, 0);
744 if (!NT_STATUS_IS_OK(status)) {
745 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
746 if (!ctx->opts.force) {
747 goto done;
750 ret = 0;
751 done:
752 talloc_free(mem);
753 return ret;
756 static struct check_ctx*
757 check_init(TALLOC_CTX* mem_ctx, const struct check_options* o)
759 struct check_ctx* ctx = talloc_zero(mem_ctx, struct check_ctx);
760 if (ctx == NULL) {
761 DEBUG(0, (_("No memory\n")));
762 return NULL;
765 ctx->diff = db_open_rbt(ctx);
766 if (ctx->diff == NULL) {
767 talloc_free(ctx);
768 DEBUG(0, (_("No memory\n")));
769 return NULL;
772 ctx->action = check_actions_init(o);
773 ctx->opts = *o;
774 return ctx;
777 static bool check_open_db(struct check_ctx* ctx, const char* name, int oflags)
779 if (name == NULL) {
780 d_fprintf(stderr, _("Error: name == NULL in check_open_db().\n"));
781 return false;
784 if (ctx->db != NULL) {
785 if ((ctx->oflags == oflags) && (strcmp(ctx->name, name))) {
786 return true;
787 } else {
788 TALLOC_FREE(ctx->db);
792 ctx->db = db_open(ctx, name, 0, TDB_DEFAULT, oflags, 0,
793 DBWRAP_LOCK_ORDER_1);
794 if (ctx->db == NULL) {
795 d_fprintf(stderr,
796 _("Could not open idmap db (%s) for writing: %s\n"),
797 name, strerror(errno));
798 return false;
801 if (ctx->name != name) {
802 TALLOC_FREE(ctx->name);
803 ctx->name = talloc_strdup(ctx, name);
806 ctx->oflags = oflags;
807 return true;
810 static bool check_do_checks(struct check_ctx* ctx)
812 NTSTATUS status;
814 if (!check_version(ctx)) {
815 return false;
818 status = dbwrap_traverse(ctx->db, traverse_check, ctx, NULL);
820 if (!NT_STATUS_IS_OK(status)) {
821 DEBUG(0, ("failed to traverse %s\n", ctx->name));
822 return false;
825 check_hwm(ctx, "USER HWM", ctx->uid_hwm + 1);
826 check_hwm(ctx, "GROUP HWM", ctx->gid_hwm + 1);
828 return true;
831 static void check_summary(const struct check_ctx* ctx)
833 d_printf("uid hwm: %d\ngid hwm: %d\n", ctx->uid_hwm, ctx->gid_hwm);
834 d_printf("mappings: %d\nother: %d\n", ctx->n_map, ctx->n_other);
835 d_printf("invalid records: %d\nmissing links: %d\ninvalid links: %d\n",
836 ctx->n_invalid_record, ctx->n_missing_reverse,
837 ctx->n_invalid_mappping);
838 d_printf("%u changes:\n", ctx->n_diff);
841 static bool check_transaction_start(struct check_ctx* ctx) {
842 return (dbwrap_transaction_start(ctx->db) == 0);
845 static bool check_transaction_commit(struct check_ctx* ctx) {
846 return (dbwrap_transaction_commit(ctx->db) == 0);
849 static bool check_transaction_cancel(struct check_ctx* ctx) {
850 return (dbwrap_transaction_cancel(ctx->db) == 0);
854 static void check_diff_list(struct check_ctx* ctx) {
855 NTSTATUS status = dbwrap_traverse(ctx->diff, traverse_print_diff, ctx, NULL);
857 if (!NT_STATUS_IS_OK(status)) {
858 DEBUG(0, ("failed to traverse diff\n"));
863 static bool check_commit(struct check_ctx* ctx)
865 struct check_actions* act = &ctx->action;
866 char action;
867 NTSTATUS status = NT_STATUS_OK;
869 check_summary(ctx);
871 if (ctx->n_diff == 0) {
872 return true;
875 while ((action = get_action(&act->commit, NULL, NULL)) == 'l') {
876 check_diff_list(ctx);
878 if (action == 's') {
879 return true;
881 assert(action == 'c');
883 if (!check_open_db(ctx, ctx->name, O_RDWR)) {
884 return false;
887 if (!check_transaction_start(ctx)) {
888 return false;
891 status = dbwrap_traverse(ctx->diff, traverse_commit, ctx, NULL);
893 if (!NT_STATUS_IS_OK(status)) {
894 check_transaction_cancel(ctx);
895 return false;
897 if (ctx->opts.test) { /*get_action? */
898 return check_transaction_cancel(ctx);
899 } else {
900 return check_transaction_commit(ctx);
904 int net_idmap_check_db(const char* db, const struct check_options* o)
906 int ret = -1;
907 TALLOC_CTX* mem_ctx = talloc_stackframe();
908 struct check_ctx* ctx = check_init(mem_ctx, o);
910 if (!o->automatic && !isatty(STDIN_FILENO)) {
911 DEBUG(0, ("Interactive use needs tty, use --auto\n"));
912 goto done;
914 if (o->lock) {
915 if (check_open_db(ctx, db, O_RDWR)
916 && check_transaction_start(ctx))
918 if ( check_do_checks(ctx)
919 && check_commit(ctx)
920 && check_transaction_commit(ctx))
922 ret = 0;
923 } else {
924 check_transaction_cancel(ctx);
927 } else {
928 if (check_open_db(ctx, db, O_RDONLY)
929 && check_do_checks(ctx)
930 && check_commit(ctx))
932 ret = 0;
935 done:
936 talloc_free(mem_ctx);
937 return ret;
941 /*Local Variables:*/
942 /*mode: c*/
943 /*End:*/