s3:net: factor out interact_edit()
[Samba/gebeck_regimport.git] / source3 / utils / net_idmap_check.c
blobde654066ec666d6fca9328671e98de157f57fa67
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);
45 static TDB_DATA talloc_copy(TALLOC_CTX* mem_ctx, TDB_DATA data);
46 static bool is_empty(TDB_DATA data) {
47 return (data.dsize == 0) || (data.dptr == NULL);
50 /* record *********************************************************************/
52 enum DT {
53 DT_INV = 0,
54 DT_SID, DT_UID, DT_GID,
55 DT_HWM, DT_VER, DT_SEQ,
58 struct record {
59 enum DT key_type, val_type;
60 TDB_DATA key, val;
61 struct dom_sid sid;
62 long unsigned id;
65 static struct record* parse_record(TALLOC_CTX* ctx, TDB_DATA key, TDB_DATA val);
66 static struct record* reverse_record(struct record* rec);
68 static bool is_invalid(const struct record* r) {
69 return (r->key_type == DT_INV) || (r->val_type == DT_INV);
72 static bool is_map(const struct record* r) {
73 return (r->key_type == DT_SID)
74 || (r->key_type == DT_UID) || (r->key_type == DT_GID);
77 /* action *********************************************************************/
79 typedef struct check_action {
80 const char* fmt;
81 const char* name;
82 const char* prompt;
83 const char* answers;
84 char auto_action;
85 char default_action;
86 bool verbose;
87 } check_action;
89 struct check_actions {
90 check_action invalid_record;
91 check_action missing_reverse;
92 check_action invalid_mapping;
93 check_action invalid_edit;
94 check_action record_exists;
95 check_action no_version;
96 check_action wrong_version;
97 check_action invalid_hwm;
98 check_action commit;
99 check_action valid_mapping;
100 check_action valid_other;
101 check_action invalid_diff;
104 static struct check_actions
105 check_actions_init(const struct check_options* opts) {
106 struct check_actions ret = {
107 .invalid_record = (check_action) {
108 .name = "Invalid record",
109 .prompt = "[e]dit/[d]elete/[D]elete all"
110 "/[s]kip/[S]kip all",
111 .answers = "eds",
112 .default_action = 'e',
113 .verbose = true,
115 .missing_reverse = (check_action) {
116 .name = "Missing reverse mapping for",
117 .prompt = "[f]ix/[F]ix all/[e]dit/[d]elete/[D]elete all"
118 "/[s]kip/[S]kip all",
119 .answers = "feds",
120 .default_action = 'f',
121 .verbose = true,
123 .invalid_mapping = (check_action) {
124 .fmt = "%1$s: %2$s -> %3$s\n(%4$s <- %3$s)\n",
125 .name = "Invalid mapping",
126 .prompt = "[e]dit/[d]elete/[D]elete all"
127 "/[s]kip/[S]kip all",
128 .answers = "eds",
129 .default_action = 'd',
130 .verbose = true,
132 .invalid_edit = (check_action) {
133 .name = "Invalid record",
134 .prompt = "[e]dit/[d]elete/[D]elete all"
135 "/[s]kip/[S]kip all",
136 .answers = "eds",
137 .default_action = 'e',
138 .verbose = true,
140 .record_exists = (check_action) {
141 .fmt = "%1$s: %2$s\n-%4$s\n+%3$s\n",
142 .name = "Record exists",
143 .prompt = "[o]verwrite/[O]verwrite all/[e]dit"
144 "/[d]elete/[D]elete all/[s]kip/[S]kip all",
145 .answers = "oeds",
146 .default_action = 'o',
147 .verbose = true,
149 .no_version = (check_action) {
150 .prompt = "[f]ix/[s]kip/[a]bort",
151 .answers = "fsa",
152 .default_action = 'f',
154 .wrong_version = (check_action) {
155 .prompt = "[f]ix/[s]kip/[a]bort",
156 .answers = "fsa",
157 .default_action = 'a',
159 .invalid_hwm = (check_action) {
160 .prompt = "[f]ix/[s]kip",
161 .answers = "fs",
162 .default_action = 'f',
164 .commit = (check_action) {
165 .prompt = "[c]ommit/[l]ist/[s]kip",
166 .answers = "cls",
167 .default_action = 'l',
168 .verbose = true,
170 .valid_mapping = (check_action) {
171 .fmt = "%1$s: %2$s <-> %3$s\n",
172 .name = "Mapping",
173 .auto_action = 's',
174 .verbose = opts->verbose,
176 .valid_other = (check_action) {
177 .name = "Other",
178 .auto_action = 's',
179 .verbose = opts->verbose,
181 .invalid_diff = (check_action) {
182 .prompt = "[s]kip/[S]kip all/[c]ommit/[C]ommit all"
183 "/[a]bort",
184 .answers = "sca",
185 .default_action = 's',
189 if (!opts->repair) {
190 ret.invalid_record.auto_action = 's';
191 ret.missing_reverse.auto_action = 's';
192 ret.invalid_mapping.auto_action = 's';
193 ret.no_version.auto_action = 's';
194 ret.wrong_version.auto_action = 's';
195 ret.invalid_hwm.auto_action = 's';
196 ret.commit.auto_action = 's';
199 if (opts->automatic) {
200 ret.invalid_record.auto_action = 'd'; /* delete */
201 ret.missing_reverse.auto_action = 'f'; /* fix */
202 ret.invalid_mapping.auto_action = 'd'; /* delete */
203 ret.no_version.auto_action = 'f'; /* fix */
204 ret.wrong_version.auto_action = 'a'; /* abort */
205 ret.invalid_hwm.auto_action = 'f'; /* fix */
206 ret.commit.auto_action = 'c'; /* commit */
207 ret.invalid_diff.auto_action = 'a'; /* abort */
208 if (opts->force) {
209 ret.wrong_version.auto_action = 'f'; /* fix */
210 ret.invalid_diff.auto_action = 'c'; /* commit */
213 if (opts->test) {
214 ret.invalid_diff.auto_action = 'c'; /* commit */
215 /* ret.commit.auto_action = 'c';*/ /* commit */
218 return ret;
221 static char get_action(struct check_action* a, struct record* r, TDB_DATA* v) {
222 char ret;
223 if (a->verbose && (r != NULL)) {
224 if (!a->fmt) {
225 d_printf("%s: %s ", a->name, print_data(r, r->key));
226 if (is_map(r)) {
227 d_printf("-> %s\n", print_data(r, r->val));
228 } else if (r->key_type == DT_HWM ||
229 r->key_type == DT_VER ||
230 r->key_type == DT_SEQ)
232 d_printf(": %ld\n", r->id);
233 } else {
234 d_printf("\n");
236 } else {
237 d_printf(a->fmt, a->name,
238 print_data(r, r->key),
239 print_data(r, r->val),
240 (v ? print_data(r, *v) : ""));
244 if (a->auto_action != '\0') {
245 return a->auto_action;
248 ret = interact_prompt(a->prompt, a->answers, a->default_action);
250 if (isupper(ret)) {
251 ret = tolower(ret);
252 a->auto_action = ret;
254 a->default_action = ret;
255 return ret;
258 /* *************************************************************************/
260 typedef struct {
261 TDB_DATA oval, nval;
262 } TDB_DATA_diff;
264 static TDB_DATA pack_diff(TDB_DATA_diff* diff) {
265 return (TDB_DATA) {
266 .dptr = (uint8_t *)diff,
267 .dsize = sizeof(TDB_DATA_diff),
271 static TDB_DATA_diff unpack_diff(TDB_DATA data) {
272 assert(data.dsize == sizeof(TDB_DATA_diff));
273 return *(TDB_DATA_diff*)data.dptr;
276 #define DEBUG_DIFF(LEV,MEM,MSG,KEY,OLD,NEW) \
277 DEBUG(LEV, ("%s: %s\n", MSG, print_data(MEM, KEY))); \
278 if (!is_empty(OLD)) { \
279 DEBUGADD(LEV, ("-%s\n", print_data(MEM, OLD))); \
281 if (!is_empty(NEW)) { \
282 DEBUGADD(LEV, ("+%s\n", print_data(MEM, NEW))); \
285 struct check_ctx {
286 int oflags;
287 char* name;
288 bool transaction;
289 struct db_context *db;
290 struct db_context *diff;
291 struct check_actions action;
293 uint32_t uid_hwm;
294 uint32_t gid_hwm;
296 unsigned n_invalid_record;
297 unsigned n_missing_reverse;
298 unsigned n_invalid_mappping;
299 unsigned n_map;
300 unsigned n_other;
301 unsigned n_diff;
302 struct check_options opts;
306 static void adjust_hwm(struct check_ctx* ctx, const struct record* r);
308 static int add_record(struct check_ctx* ctx, TDB_DATA key, TDB_DATA value)
310 NTSTATUS status;
311 TDB_DATA_diff diff;
312 TALLOC_CTX* mem = talloc_new(ctx->diff);
313 TDB_DATA recvalue;
314 struct db_record *rec = dbwrap_fetch_locked(ctx->diff, mem, key);
316 if (rec == NULL) {
317 return -1;
320 recvalue = dbwrap_record_get_value(rec);
322 if (recvalue.dptr == 0) { /* first entry */
323 status = dbwrap_fetch(ctx->db, ctx->diff, key, &diff.oval);
324 if (!NT_STATUS_IS_OK(status)) {
325 diff.oval = tdb_null;
327 } else {
328 diff = unpack_diff(recvalue);
329 talloc_free(diff.nval.dptr);
331 diff.nval = talloc_copy(ctx->diff, value);
333 DEBUG_DIFF(2, mem, "TDB DIFF", key, diff.oval, diff.nval);
335 status = dbwrap_record_store(rec, pack_diff(&diff), 0);
337 talloc_free(mem);
339 if (!NT_STATUS_IS_OK(status)) {
340 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
341 return -1;
343 ctx->n_diff ++;
344 return 0;
347 static int del_record(struct check_ctx* ctx, TDB_DATA key) {
348 return add_record(ctx, key, tdb_null);
351 static TDB_DATA
352 fetch_record(struct check_ctx* ctx, TALLOC_CTX* mem_ctx, TDB_DATA key)
354 TDB_DATA tmp;
355 NTSTATUS status;
357 status = dbwrap_fetch(ctx->diff, mem_ctx, key, &tmp);
358 if (!NT_STATUS_IS_OK(status)) {
359 DEBUG(0, ("Out of memory!\n"));
360 return tdb_null;
362 if (tmp.dptr != NULL) {
363 TDB_DATA_diff diff = unpack_diff(tmp);
364 TDB_DATA ret = talloc_copy(mem_ctx, diff.nval);
365 talloc_free(tmp.dptr);
366 return ret;
369 status = dbwrap_fetch(ctx->db, mem_ctx, key, &tmp);
370 if (!NT_STATUS_IS_OK(status)) {
371 DEBUG(0, ("Out of memory!\n"));
372 return tdb_null;
375 return tmp;
378 static void edit_record(struct record* r) {
379 TALLOC_CTX* mem = talloc_new(r);
380 cbuf* ost = cbuf_new(mem);
381 const char* str;
382 struct record* nr;
383 TDB_DATA key;
384 TDB_DATA val;
385 cbuf_printf(ost, "%s %s\n",
386 print_data(mem, r->key), print_data(mem, r->val));
387 str = interact_edit(mem, cbuf_gets(ost, 0));
388 key = parse_data(mem, &str);
389 val = parse_data(mem, &str);
390 nr = parse_record(talloc_parent(r), key, val);
391 if (nr != NULL) {
392 *r = *nr;
394 talloc_free(mem);
397 static bool check_version(struct check_ctx* ctx) {
398 static const char* key = "IDMAP_VERSION";
399 uint32_t version;
400 NTSTATUS status;
401 char action = 's';
402 struct check_actions* act = &ctx->action;
404 status = dbwrap_fetch_uint32(ctx->db, key, &version);
405 if (!NT_STATUS_IS_OK(status)) {
406 d_printf("No version number, assume 2\n");
407 action = get_action(&act->no_version, NULL, NULL);
408 } else if (version != 2) {
409 d_printf("Wrong version number %d, should be 2\n", version);
410 action = get_action(&act->wrong_version, NULL, NULL);
412 switch (action) {
413 case 's':
414 break;
415 case 'f':
416 SIVAL(&version, 0, 2);
417 add_record(ctx, string_term_tdb_data(key),
418 make_tdb_data((uint8_t *)&version, sizeof(uint32_t)));
419 break;
420 case 'a':
421 return false;
422 default:
423 assert(false);
425 return true;
428 static void check_hwm(struct check_ctx* ctx, const char* key, uint32_t target) {
429 uint32_t hwm;
430 char action = 's';
431 NTSTATUS status;
432 struct check_actions* act = &ctx->action;
434 status = dbwrap_fetch_uint32(ctx->db, key, &hwm);
435 if (!NT_STATUS_IS_OK(status)) {
436 d_printf("No %s should be %d\n", key, target);
437 action = get_action(&act->invalid_hwm, NULL, NULL);
438 } else if (target < hwm) {
439 d_printf("Invalid %s %d: should be %d\n", key, hwm, target);
440 action = get_action(&act->invalid_hwm, NULL, NULL);
442 if (action == 'f') {
443 SIVAL(&hwm, 0, target);
444 add_record(ctx, string_term_tdb_data(key),
445 make_tdb_data((uint8_t *)&hwm, sizeof(uint32_t)));
449 int traverse_check(struct db_record *rec, void* data) {
450 struct check_ctx* ctx = (struct check_ctx*)data;
451 struct check_actions* act = &ctx->action;
452 TALLOC_CTX* mem = talloc_new(ctx->diff);
453 TDB_DATA key;
454 TDB_DATA value;
455 struct record *r;
456 char action = 's';
458 key = dbwrap_record_get_key(rec);
459 value = dbwrap_record_get_value(rec);
461 r = parse_record(mem, key, value);
463 if (is_invalid(r)) {
464 action = get_action(&act->invalid_record, r, NULL);
465 ctx->n_invalid_record++;
466 } else if (is_map(r)) {
467 TDB_DATA back = fetch_record(ctx, mem, r->val);
468 if (back.dptr == NULL) {
469 action = get_action(&act->missing_reverse, r, NULL);
470 ctx->n_missing_reverse++;
471 } else if (!tdb_data_equal(r->key, back)) {
472 action = get_action(&act->invalid_mapping, r, &back);
473 ctx->n_invalid_mappping++;
474 } else {
475 if (r->key_type == DT_SID) {
476 action = get_action(&act->valid_mapping, r, NULL);
477 ctx->n_map++;
478 } else {
479 action = get_action(&act->valid_mapping, NULL,
480 NULL);
483 adjust_hwm(ctx, r);
484 } else {
485 action = get_action(&act->valid_other, r, NULL);
486 ctx->n_other++;
489 while (action) {
490 switch (action) {
491 case 's': /* skip */
492 break;
493 case 'd': /* delete */
494 del_record(ctx, key);
495 break;
496 case 'f': /* add reverse mapping */
497 add_record(ctx, value, key);
498 break;
499 case 'e': /* edit */
500 edit_record(r);
501 action = 'o';
502 if (is_invalid(r)) {
503 action = get_action(&act->invalid_edit, r,NULL);
504 continue;
506 if (!tdb_data_equal(key, r->key)) {
507 TDB_DATA oval = fetch_record(ctx, mem, r->key);
508 if (!is_empty(oval) &&
509 !tdb_data_equal(oval, r->val))
511 action = get_action(&act->record_exists,
512 r, &oval);
513 if (action != 'o') {
514 continue;
518 if (is_map(r)) {
519 TDB_DATA okey = fetch_record(ctx, mem, r->val);
520 if (!is_empty(okey) &&
521 !tdb_data_equal(okey, r->key))
523 action = get_action(&act->record_exists,
524 reverse_record(r),
525 &okey);
528 continue;
529 case 'o': /* overwrite */
530 adjust_hwm(ctx, r);
531 if (!tdb_data_equal(key, r->key)) {
532 del_record(ctx, key);
534 add_record(ctx, r->key, r->val);
535 if (is_map(r)) {
536 add_record(ctx, r->val, r->key);
539 action = '\0';
542 talloc_free(mem);
544 return 0;
547 /******************************************************************************/
549 void adjust_hwm(struct check_ctx* ctx, const struct record* r) {
550 enum DT type = (r->key_type == DT_SID) ? r->val_type : r->key_type;
551 if (type == DT_UID) {
552 ctx->uid_hwm = MAX(ctx->uid_hwm, r->id);
553 } else if (type == DT_GID) {
554 ctx->gid_hwm = MAX(ctx->gid_hwm, r->id);
558 TDB_DATA talloc_copy(TALLOC_CTX* mem_ctx, TDB_DATA data) {
559 TDB_DATA ret = {
560 .dptr = (uint8_t *)talloc_size(mem_ctx, data.dsize+1),
561 .dsize = data.dsize
563 if (ret.dptr == NULL) {
564 ret.dsize = 0;
565 } else {
566 memcpy(ret.dptr, data.dptr, data.dsize);
567 ret.dptr[ret.dsize] = '\0';
569 return ret;
572 static bool is_cstr(TDB_DATA str) {
573 return !is_empty(str) && str.dptr[str.dsize-1] == '\0';
576 static bool parse_sid (TDB_DATA str, enum DT* type, struct dom_sid* sid) {
577 struct dom_sid tmp;
578 const char* s = (const char*)str.dptr;
579 if ((s[0] == 'S') && string_to_sid(&tmp, s)) {
580 *sid = tmp;
581 *type = DT_SID;
582 return true;
584 return false;
587 static bool parse_xid(TDB_DATA str, enum DT* type, unsigned long* id) {
588 char c, t;
589 unsigned long tmp;
590 if (sscanf((const char*)str.dptr, "%cID %lu%c", &c, &tmp, &t) == 2) {
591 if (c == 'U') {
592 *id = tmp;
593 *type = DT_UID;
594 return true;
595 } else if (c == 'G') {
596 *id = tmp;
597 *type = DT_GID;
598 return true;
601 return false;
605 struct record*
606 parse_record(TALLOC_CTX* mem_ctx, TDB_DATA key, TDB_DATA val)
608 struct record* ret = talloc_zero(mem_ctx, struct record);
609 if (ret == NULL) {
610 DEBUG(0, ("Out of memory.\n"));
611 return NULL;
613 ret->key = talloc_copy(ret, key);
614 ret->val = talloc_copy(ret, val);
615 if ((ret->key.dptr == NULL && key.dptr != NULL) ||
616 (ret->val.dptr == NULL && val.dptr != NULL))
618 talloc_free(ret);
619 DEBUG(0, ("Out of memory.\n"));
620 return NULL;
622 assert((ret->key_type == DT_INV) && (ret->val_type == DT_INV));
624 if (!is_cstr(key)) {
625 return ret;
627 if (parse_sid(key, &ret->key_type, &ret->sid)) {
628 parse_xid(val, &ret->val_type, &ret->id);
629 } else if (parse_xid(key, &ret->key_type, &ret->id)) {
630 if (is_cstr(val)) {
631 parse_sid(val, &ret->val_type, &ret->sid);
633 } else if (strcmp((const char*)key.dptr, "USER HWM") == 0) {
634 ret->key_type = DT_HWM;
635 if (val.dsize == 4) {
636 ret->id = IVAL(val.dptr,0);
637 ret->val_type = DT_UID;
639 } else if (strcmp((const char*)key.dptr, "GROUP HWM") == 0) {
640 ret->key_type = DT_HWM;
641 if (val.dsize == 4) {
642 ret->id = IVAL(val.dptr,0);
643 ret->val_type = DT_GID;
645 } else if (strcmp((const char*)key.dptr, "IDMAP_VERSION") == 0) {
646 ret->key_type = DT_VER;
647 if (val.dsize == 4) {
648 ret->id = IVAL(val.dptr,0);
649 ret->val_type = DT_VER;
651 } else if (strcmp((const char*)key.dptr, "__db_sequence_number__") == 0) {
652 ret->key_type = DT_SEQ;
653 if (val.dsize == 8) {
654 ret->id = *(uint64_t*)val.dptr;
655 ret->val_type = DT_SEQ;
659 return ret;
662 struct record* reverse_record(struct record* in)
664 return parse_record(talloc_parent(in), in->val, in->key);
668 /******************************************************************************/
671 char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d)
673 if (!is_empty(d)) {
674 char* ret = NULL;
675 cbuf* ost = cbuf_new(mem_ctx);
676 int len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize);
677 if (len != -1) {
678 cbuf_swapptr(ost, &ret, 0);
679 talloc_steal(mem_ctx, ret);
681 talloc_free(ost);
682 return ret;
684 return talloc_strdup(mem_ctx, "<NULL>");
688 TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr) {
689 cbuf* ost = cbuf_new(mem_ctx);
690 TDB_DATA ret = tdb_null;
691 srprs_skipws(ptr);
692 if (srprs_quoted(ptr, ost)) {
693 ret.dsize = cbuf_getpos(ost);
694 ret.dptr = (uint8_t *)talloc_steal(mem_ctx, cbuf_gets(ost,0));
696 talloc_free(ost);
697 return ret;
700 static int traverse_print_diff(struct db_record *rec, void* data) {
701 struct check_ctx* ctx = (struct check_ctx*)data;
702 TDB_DATA key;
703 TDB_DATA value;
704 TDB_DATA_diff diff;
705 TALLOC_CTX* mem = talloc_new(ctx->diff);
707 key = dbwrap_record_get_key(rec);
708 value = dbwrap_record_get_value(rec);
709 diff = unpack_diff(value);
711 DEBUG_DIFF(0, mem, "DIFF", key, diff.oval, diff.nval);
713 talloc_free(mem);
714 return 0;
718 static int traverse_commit(struct db_record *diff_rec, void* data) {
719 struct check_ctx* ctx = (struct check_ctx*)data;
720 TDB_DATA key;
721 TDB_DATA diff_value;
722 TDB_DATA_diff diff;
723 TDB_DATA value;
724 TALLOC_CTX* mem = talloc_new(ctx->diff);
725 int ret = -1;
726 NTSTATUS status;
727 struct check_actions* act = &ctx->action;
728 struct db_record* rec;
730 key = dbwrap_record_get_key(diff_rec);
731 diff_value = dbwrap_record_get_value(diff_rec);
732 diff = unpack_diff(diff_value);
734 rec = dbwrap_fetch_locked(ctx->db, mem, key);
735 if (rec == NULL) {
736 goto done;
739 value = dbwrap_record_get_value(rec);
741 if (!tdb_data_equal(value, diff.oval)) {
742 char action;
744 d_printf("Warning: record has changed: %s\n"
745 "expected: %s got %s\n", print_data(mem, key),
746 print_data(mem, diff.oval),
747 print_data(mem, value));
749 action = get_action(&act->invalid_diff, NULL, NULL);
750 if (action == 's') {
751 ret = 0;
752 goto done;
753 } else if (action == 'a') {
754 goto done;
758 DEBUG_DIFF(0, mem, "Commit", key, diff.oval, diff.nval);
760 if (is_empty(diff.nval)) {
761 status = dbwrap_record_delete(rec);
762 } else {
763 status = dbwrap_record_store(rec, diff.nval, 0);
766 if (!NT_STATUS_IS_OK(status)) {
767 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
768 if (!ctx->opts.force) {
769 goto done;
772 ret = 0;
773 done:
774 talloc_free(mem);
775 return ret;
778 static struct check_ctx*
779 check_init(TALLOC_CTX* mem_ctx, const struct check_options* o)
781 struct check_ctx* ctx = talloc_zero(mem_ctx, struct check_ctx);
782 if (ctx == NULL) {
783 DEBUG(0, (_("No memory\n")));
784 return NULL;
787 ctx->diff = db_open_rbt(ctx);
788 if (ctx->diff == NULL) {
789 talloc_free(ctx);
790 DEBUG(0, (_("No memory\n")));
791 return NULL;
794 ctx->action = check_actions_init(o);
795 ctx->opts = *o;
796 return ctx;
799 static bool check_open_db(struct check_ctx* ctx, const char* name, int oflags)
801 if (name == NULL) {
802 d_fprintf(stderr, _("Error: name == NULL in check_open_db().\n"));
803 return false;
806 if (ctx->db != NULL) {
807 if ((ctx->oflags == oflags) && (strcmp(ctx->name, name))) {
808 return true;
809 } else {
810 TALLOC_FREE(ctx->db);
814 ctx->db = db_open(ctx, name, 0, TDB_DEFAULT, oflags, 0);
815 if (ctx->db == NULL) {
816 d_fprintf(stderr,
817 _("Could not open idmap db (%s) for writing: %s\n"),
818 name, strerror(errno));
819 return false;
822 if (ctx->name != name) {
823 TALLOC_FREE(ctx->name);
824 ctx->name = talloc_strdup(ctx, name);
827 ctx->oflags = oflags;
828 return true;
831 static bool check_do_checks(struct check_ctx* ctx)
833 NTSTATUS status;
835 if (!check_version(ctx)) {
836 return false;
839 status = dbwrap_traverse(ctx->db, traverse_check, ctx, NULL);
841 if (!NT_STATUS_IS_OK(status)) {
842 DEBUG(0, ("failed to traverse %s\n", ctx->name));
843 return false;
846 check_hwm(ctx, "USER HWM", ctx->uid_hwm + 1);
847 check_hwm(ctx, "GROUP HWM", ctx->gid_hwm + 1);
849 return true;
852 static void check_summary(const struct check_ctx* ctx)
854 d_printf("uid hwm: %d\ngid hwm: %d\n", ctx->uid_hwm, ctx->gid_hwm);
855 d_printf("mappings: %d\nother: %d\n", ctx->n_map, ctx->n_other);
856 d_printf("invalid records: %d\nmissing links: %d\ninvalid links: %d\n",
857 ctx->n_invalid_record, ctx->n_missing_reverse,
858 ctx->n_invalid_mappping);
859 d_printf("%u changes:\n", ctx->n_diff);
862 static bool check_transaction_start(struct check_ctx* ctx) {
863 return (dbwrap_transaction_start(ctx->db) == 0);
866 static bool check_transaction_commit(struct check_ctx* ctx) {
867 return (dbwrap_transaction_commit(ctx->db) == 0);
870 static bool check_transaction_cancel(struct check_ctx* ctx) {
871 return (dbwrap_transaction_cancel(ctx->db) == 0);
875 static void check_diff_list(struct check_ctx* ctx) {
876 NTSTATUS status = dbwrap_traverse(ctx->diff, traverse_print_diff, ctx, NULL);
878 if (!NT_STATUS_IS_OK(status)) {
879 DEBUG(0, ("failed to traverse diff\n"));
884 static bool check_commit(struct check_ctx* ctx)
886 struct check_actions* act = &ctx->action;
887 char action;
888 NTSTATUS status = NT_STATUS_OK;
890 check_summary(ctx);
892 if (ctx->n_diff == 0) {
893 return true;
896 while ((action = get_action(&act->commit, NULL, NULL)) == 'l') {
897 check_diff_list(ctx);
899 if (action == 's') {
900 return true;
902 assert(action == 'c');
904 if (!check_open_db(ctx, ctx->name, O_RDWR)) {
905 return false;
908 if (!check_transaction_start(ctx)) {
909 return false;
912 status = dbwrap_traverse(ctx->diff, traverse_commit, ctx, NULL);
914 if (!NT_STATUS_IS_OK(status)) {
915 check_transaction_cancel(ctx);
916 return false;
918 if (ctx->opts.test) { /*get_action? */
919 return check_transaction_cancel(ctx);
920 } else {
921 return check_transaction_commit(ctx);
925 int net_idmap_check_db(const char* db, const struct check_options* o)
927 int ret = -1;
928 TALLOC_CTX* mem_ctx = talloc_stackframe();
929 struct check_ctx* ctx = check_init(mem_ctx, o);
931 if (!o->automatic && !isatty(STDIN_FILENO)) {
932 DEBUG(0, ("Interactive use needs tty, use --auto\n"));
933 goto done;
935 if (o->lock) {
936 if (check_open_db(ctx, db, O_RDWR)
937 && check_transaction_start(ctx))
939 if ( check_do_checks(ctx)
940 && check_commit(ctx)
941 && check_transaction_commit(ctx))
943 ret = 0;
944 } else {
945 check_transaction_cancel(ctx);
948 } else {
949 if (check_open_db(ctx, db, O_RDONLY)
950 && check_do_checks(ctx)
951 && check_commit(ctx))
953 ret = 0;
956 done:
957 talloc_free(mem_ctx);
958 return ret;
962 /*Local Variables:*/
963 /*mode: c*/
964 /*End:*/