smbd: Don't store in-memory only flags in locking.tdb
[Samba.git] / source3 / utils / net_idmap_check.c
blob4174238298dd2883d4c1168cce7fc494c637b3cf
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);
47 /* record *********************************************************************/
49 enum DT {
50 DT_INV = 0,
51 DT_SID, DT_UID, DT_GID,
52 DT_HWM, DT_VER, DT_SEQ,
55 struct record {
56 enum DT key_type, val_type;
57 TDB_DATA key, val;
58 struct dom_sid sid;
59 long unsigned id;
62 static struct record* parse_record(TALLOC_CTX* ctx, TDB_DATA key, TDB_DATA val);
63 static struct record* reverse_record(struct record* rec);
65 static bool is_invalid(const struct record* r) {
66 return (r->key_type == DT_INV) || (r->val_type == DT_INV);
69 static bool is_map(const struct record* r) {
70 return (r->key_type == DT_SID)
71 || (r->key_type == DT_UID) || (r->key_type == DT_GID);
74 /* action *********************************************************************/
76 typedef struct check_action {
77 const char* fmt;
78 const char* name;
79 const char* prompt;
80 const char* answers;
81 char auto_action;
82 char default_action;
83 bool verbose;
84 } check_action;
86 struct check_actions {
87 check_action invalid_record;
88 check_action missing_reverse;
89 check_action invalid_mapping;
90 check_action invalid_edit;
91 check_action record_exists;
92 check_action no_version;
93 check_action wrong_version;
94 check_action invalid_hwm;
95 check_action commit;
96 check_action valid_mapping;
97 check_action valid_other;
98 check_action invalid_diff;
101 static struct check_actions
102 check_actions_init(const struct check_options* opts) {
103 struct check_actions ret = {
104 .invalid_record = (check_action) {
105 .name = "Invalid record",
106 .prompt = "[e]dit/[d]elete/[D]elete all"
107 "/[s]kip/[S]kip all",
108 .answers = "eds",
109 .default_action = 'e',
110 .verbose = true,
112 .missing_reverse = (check_action) {
113 .name = "Missing reverse mapping for",
114 .prompt = "[f]ix/[F]ix all/[e]dit/[d]elete/[D]elete all"
115 "/[s]kip/[S]kip all",
116 .answers = "feds",
117 .default_action = 'f',
118 .verbose = true,
120 .invalid_mapping = (check_action) {
121 .fmt = "%1$s: %2$s -> %3$s\n(%4$s <- %3$s)\n",
122 .name = "Invalid mapping",
123 .prompt = "[e]dit/[d]elete/[D]elete all"
124 "/[s]kip/[S]kip all",
125 .answers = "eds",
126 .default_action = 'd',
127 .verbose = true,
129 .invalid_edit = (check_action) {
130 .name = "Invalid record",
131 .prompt = "[e]dit/[d]elete/[D]elete all"
132 "/[s]kip/[S]kip all",
133 .answers = "eds",
134 .default_action = 'e',
135 .verbose = true,
137 .record_exists = (check_action) {
138 .fmt = "%1$s: %2$s\n-%4$s\n+%3$s\n",
139 .name = "Record exists",
140 .prompt = "[o]verwrite/[O]verwrite all/[e]dit"
141 "/[d]elete/[D]elete all/[s]kip/[S]kip all",
142 .answers = "oeds",
143 .default_action = 'o',
144 .verbose = true,
146 .no_version = (check_action) {
147 .prompt = "[f]ix/[s]kip/[a]bort",
148 .answers = "fsa",
149 .default_action = 'f',
151 .wrong_version = (check_action) {
152 .prompt = "[f]ix/[s]kip/[a]bort",
153 .answers = "fsa",
154 .default_action = 'a',
156 .invalid_hwm = (check_action) {
157 .prompt = "[f]ix/[s]kip",
158 .answers = "fs",
159 .default_action = 'f',
161 .commit = (check_action) {
162 .prompt = "[c]ommit/[l]ist/[s]kip",
163 .answers = "cls",
164 .default_action = 'l',
165 .verbose = true,
167 .valid_mapping = (check_action) {
168 .fmt = "%1$s: %2$s <-> %3$s\n",
169 .name = "Mapping",
170 .auto_action = 's',
171 .verbose = opts->verbose,
173 .valid_other = (check_action) {
174 .name = "Other",
175 .auto_action = 's',
176 .verbose = opts->verbose,
178 .invalid_diff = (check_action) {
179 .prompt = "[s]kip/[S]kip all/[c]ommit/[C]ommit all"
180 "/[a]bort",
181 .answers = "sca",
182 .default_action = 's',
186 if (!opts->repair) {
187 ret.invalid_record.auto_action = 's';
188 ret.missing_reverse.auto_action = 's';
189 ret.invalid_mapping.auto_action = 's';
190 ret.no_version.auto_action = 's';
191 ret.wrong_version.auto_action = 's';
192 ret.invalid_hwm.auto_action = 's';
193 ret.commit.auto_action = 's';
196 if (opts->automatic) {
197 ret.invalid_record.auto_action = 'd'; /* delete */
198 ret.missing_reverse.auto_action = 'f'; /* fix */
199 ret.invalid_mapping.auto_action = 'd'; /* delete */
200 ret.no_version.auto_action = 'f'; /* fix */
201 ret.wrong_version.auto_action = 'a'; /* abort */
202 ret.invalid_hwm.auto_action = 'f'; /* fix */
203 ret.commit.auto_action = 'c'; /* commit */
204 ret.invalid_diff.auto_action = 'a'; /* abort */
205 if (opts->force) {
206 ret.wrong_version.auto_action = 'f'; /* fix */
207 ret.invalid_diff.auto_action = 'c'; /* commit */
210 if (opts->test) {
211 ret.invalid_diff.auto_action = 'c'; /* commit */
212 /* ret.commit.auto_action = 'c';*/ /* commit */
215 return ret;
218 static char get_action(struct check_action* a, struct record* r, TDB_DATA* v) {
219 char ret;
220 if (a->verbose && (r != NULL)) {
221 if (!a->fmt) {
222 d_printf("%s: %s ", a->name, print_data(r, r->key));
223 if (is_map(r)) {
224 d_printf("-> %s\n", print_data(r, r->val));
225 } else if (r->key_type == DT_HWM ||
226 r->key_type == DT_VER ||
227 r->key_type == DT_SEQ)
229 d_printf(": %ld\n", r->id);
230 } else {
231 d_printf("\n");
233 } else {
234 d_printf(a->fmt, a->name,
235 print_data(r, r->key),
236 print_data(r, r->val),
237 (v ? print_data(r, *v) : ""));
241 if (a->auto_action != '\0') {
242 return a->auto_action;
245 ret = interact_prompt(a->prompt, a->answers, a->default_action);
247 if (isupper(ret)) {
248 ret = tolower(ret);
249 a->auto_action = ret;
251 a->default_action = ret;
252 return ret;
255 /* *************************************************************************/
257 typedef struct {
258 TDB_DATA oval, nval;
259 } TDB_DATA_diff;
261 static TDB_DATA pack_diff(TDB_DATA_diff* diff) {
262 return (TDB_DATA) {
263 .dptr = (uint8_t *)diff,
264 .dsize = sizeof(TDB_DATA_diff),
268 static TDB_DATA_diff unpack_diff(TDB_DATA data) {
269 assert(data.dsize == sizeof(TDB_DATA_diff));
270 return *(TDB_DATA_diff*)data.dptr;
273 #define DEBUG_DIFF(LEV,MEM,MSG,KEY,OLD,NEW) \
274 DEBUG(LEV, ("%s: %s\n", MSG, print_data(MEM, KEY))); \
275 if (!tdb_data_is_empty(OLD)) { \
276 DEBUGADD(LEV, ("-%s\n", print_data(MEM, OLD))); \
278 if (!tdb_data_is_empty(NEW)) { \
279 DEBUGADD(LEV, ("+%s\n", print_data(MEM, NEW))); \
282 struct check_ctx {
283 int oflags;
284 char* name;
285 bool transaction;
286 struct db_context *db;
287 struct db_context *diff;
288 struct check_actions action;
290 uint32_t uid_hwm;
291 uint32_t gid_hwm;
293 unsigned n_invalid_record;
294 unsigned n_missing_reverse;
295 unsigned n_invalid_mappping;
296 unsigned n_map;
297 unsigned n_other;
298 unsigned n_diff;
299 struct check_options opts;
303 static void adjust_hwm(struct check_ctx* ctx, const struct record* r);
305 static int add_record(struct check_ctx* ctx, TDB_DATA key, TDB_DATA value)
307 NTSTATUS status;
308 TDB_DATA_diff diff;
309 TALLOC_CTX* mem = talloc_new(ctx->diff);
310 TDB_DATA recvalue;
311 struct db_record *rec = dbwrap_fetch_locked(ctx->diff, mem, key);
313 if (rec == NULL) {
314 return -1;
317 recvalue = dbwrap_record_get_value(rec);
319 if (recvalue.dptr == 0) { /* first entry */
320 status = dbwrap_fetch(ctx->db, ctx->diff, key, &diff.oval);
321 if (!NT_STATUS_IS_OK(status)) {
322 diff.oval = tdb_null;
324 } else {
325 diff = unpack_diff(recvalue);
326 talloc_free(diff.nval.dptr);
328 diff.nval = talloc_copy(ctx->diff, value);
330 DEBUG_DIFF(2, mem, "TDB DIFF", key, diff.oval, diff.nval);
332 status = dbwrap_record_store(rec, pack_diff(&diff), 0);
334 talloc_free(mem);
336 if (!NT_STATUS_IS_OK(status)) {
337 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
338 return -1;
340 ctx->n_diff ++;
341 return 0;
344 static int del_record(struct check_ctx* ctx, TDB_DATA key) {
345 return add_record(ctx, key, tdb_null);
348 static TDB_DATA
349 fetch_record(struct check_ctx* ctx, TALLOC_CTX* mem_ctx, TDB_DATA key)
351 TDB_DATA tmp;
352 NTSTATUS status;
354 status = dbwrap_fetch(ctx->diff, mem_ctx, key, &tmp);
356 if (NT_STATUS_IS_OK(status)) {
357 TDB_DATA_diff diff = unpack_diff(tmp);
358 TDB_DATA ret = talloc_copy(mem_ctx, diff.nval);
359 talloc_free(tmp.dptr);
360 return ret;
363 status = dbwrap_fetch(ctx->db, mem_ctx, key, &tmp);
364 if (!NT_STATUS_IS_OK(status)) {
365 return tdb_null;
368 return tmp;
371 static void edit_record(struct record* r) {
372 TALLOC_CTX* mem = talloc_new(r);
373 cbuf* ost = cbuf_new(mem);
374 const char* str;
375 struct record* nr;
376 TDB_DATA key;
377 TDB_DATA val;
378 cbuf_printf(ost, "%s %s\n",
379 print_data(mem, r->key), print_data(mem, r->val));
380 str = interact_edit(mem, cbuf_gets(ost, 0));
381 key = parse_data(mem, &str);
382 val = parse_data(mem, &str);
383 nr = parse_record(talloc_parent(r), key, val);
384 if (nr != NULL) {
385 *r = *nr;
387 talloc_free(mem);
390 static bool check_version(struct check_ctx* ctx) {
391 static const char* key = "IDMAP_VERSION";
392 uint32_t version;
393 NTSTATUS status;
394 char action = 's';
395 struct check_actions* act = &ctx->action;
397 status = dbwrap_fetch_uint32_bystring(ctx->db, key, &version);
398 if (!NT_STATUS_IS_OK(status)) {
399 d_printf("No version number, assume 2\n");
400 action = get_action(&act->no_version, NULL, NULL);
401 } else if (version != 2) {
402 d_printf("Wrong version number %d, should be 2\n", version);
403 action = get_action(&act->wrong_version, NULL, NULL);
405 switch (action) {
406 case 's':
407 break;
408 case 'f':
409 SIVAL(&version, 0, 2);
410 add_record(ctx, string_term_tdb_data(key),
411 make_tdb_data((uint8_t *)&version, sizeof(uint32_t)));
412 break;
413 case 'a':
414 return false;
415 default:
416 assert(false);
418 return true;
421 static void check_hwm(struct check_ctx* ctx, const char* key, uint32_t target) {
422 uint32_t hwm;
423 char action = 's';
424 NTSTATUS status;
425 struct check_actions* act = &ctx->action;
427 status = dbwrap_fetch_uint32_bystring(ctx->db, key, &hwm);
428 if (!NT_STATUS_IS_OK(status)) {
429 d_printf("No %s should be %d\n", key, target);
430 action = get_action(&act->invalid_hwm, NULL, NULL);
431 } else if (target < hwm) {
432 d_printf("Invalid %s %d: should be %d\n", key, hwm, target);
433 action = get_action(&act->invalid_hwm, NULL, NULL);
435 if (action == 'f') {
436 SIVAL(&hwm, 0, target);
437 add_record(ctx, string_term_tdb_data(key),
438 make_tdb_data((uint8_t *)&hwm, sizeof(uint32_t)));
442 int traverse_check(struct db_record *rec, void* data) {
443 struct check_ctx* ctx = (struct check_ctx*)data;
444 struct check_actions* act = &ctx->action;
445 TALLOC_CTX* mem = talloc_new(ctx->diff);
446 TDB_DATA key;
447 TDB_DATA value;
448 struct record *r;
449 char action = 's';
451 key = dbwrap_record_get_key(rec);
452 value = dbwrap_record_get_value(rec);
454 r = parse_record(mem, key, value);
456 if (is_invalid(r)) {
457 action = get_action(&act->invalid_record, r, NULL);
458 ctx->n_invalid_record++;
459 } else if (is_map(r)) {
460 TDB_DATA back = fetch_record(ctx, mem, r->val);
461 if (back.dptr == NULL) {
462 action = get_action(&act->missing_reverse, r, NULL);
463 ctx->n_missing_reverse++;
464 } else if (!tdb_data_equal(r->key, back)) {
465 action = get_action(&act->invalid_mapping, r, &back);
466 ctx->n_invalid_mappping++;
467 } else {
468 if (r->key_type == DT_SID) {
469 action = get_action(&act->valid_mapping, r, NULL);
470 ctx->n_map++;
471 } else {
472 action = get_action(&act->valid_mapping, NULL,
473 NULL);
476 adjust_hwm(ctx, r);
477 } else {
478 action = get_action(&act->valid_other, r, NULL);
479 ctx->n_other++;
482 while (action) {
483 switch (action) {
484 case 's': /* skip */
485 break;
486 case 'd': /* delete */
487 del_record(ctx, key);
488 break;
489 case 'f': /* add reverse mapping */
490 add_record(ctx, value, key);
491 break;
492 case 'e': /* edit */
493 edit_record(r);
494 action = 'o';
495 if (is_invalid(r)) {
496 action = get_action(&act->invalid_edit, r,NULL);
497 continue;
499 if (!tdb_data_equal(key, r->key)) {
500 TDB_DATA oval = fetch_record(ctx, mem, r->key);
501 if (!tdb_data_is_empty(oval) &&
502 !tdb_data_equal(oval, r->val))
504 action = get_action(&act->record_exists,
505 r, &oval);
506 if (action != 'o') {
507 continue;
511 if (is_map(r)) {
512 TDB_DATA okey = fetch_record(ctx, mem, r->val);
513 if (!tdb_data_is_empty(okey) &&
514 !tdb_data_equal(okey, r->key))
516 action = get_action(&act->record_exists,
517 reverse_record(r),
518 &okey);
521 continue;
522 case 'o': /* overwrite */
523 adjust_hwm(ctx, r);
524 if (!tdb_data_equal(key, r->key)) {
525 del_record(ctx, key);
527 add_record(ctx, r->key, r->val);
528 if (is_map(r)) {
529 add_record(ctx, r->val, r->key);
532 action = '\0';
535 talloc_free(mem);
537 return 0;
540 /******************************************************************************/
542 void adjust_hwm(struct check_ctx* ctx, const struct record* r) {
543 enum DT type = (r->key_type == DT_SID) ? r->val_type : r->key_type;
544 if (type == DT_UID) {
545 ctx->uid_hwm = MAX(ctx->uid_hwm, r->id);
546 } else if (type == DT_GID) {
547 ctx->gid_hwm = MAX(ctx->gid_hwm, r->id);
551 TDB_DATA talloc_copy(TALLOC_CTX* mem_ctx, TDB_DATA data) {
552 TDB_DATA ret = {
553 .dptr = (uint8_t *)talloc_size(mem_ctx, data.dsize+1),
554 .dsize = data.dsize
556 if (ret.dptr == NULL) {
557 ret.dsize = 0;
558 } else {
559 memcpy(ret.dptr, data.dptr, data.dsize);
560 ret.dptr[ret.dsize] = '\0';
562 return ret;
565 static bool is_cstr(TDB_DATA str) {
566 return !tdb_data_is_empty(str) && str.dptr[str.dsize-1] == '\0';
569 static bool parse_sid (TDB_DATA str, enum DT* type, struct dom_sid* sid) {
570 struct dom_sid tmp;
571 const char* s = (const char*)str.dptr;
572 if ((s[0] == 'S') && string_to_sid(&tmp, s)) {
573 *sid = tmp;
574 *type = DT_SID;
575 return true;
577 return false;
580 static bool parse_xid(TDB_DATA str, enum DT* type, unsigned long* id) {
581 char c, t;
582 unsigned long tmp;
583 if (sscanf((const char*)str.dptr, "%cID %lu%c", &c, &tmp, &t) == 2) {
584 if (c == 'U') {
585 *id = tmp;
586 *type = DT_UID;
587 return true;
588 } else if (c == 'G') {
589 *id = tmp;
590 *type = DT_GID;
591 return true;
594 return false;
598 struct record*
599 parse_record(TALLOC_CTX* mem_ctx, TDB_DATA key, TDB_DATA val)
601 struct record* ret = talloc_zero(mem_ctx, struct record);
602 if (ret == NULL) {
603 DEBUG(0, ("Out of memory.\n"));
604 return NULL;
606 ret->key = talloc_copy(ret, key);
607 ret->val = talloc_copy(ret, val);
608 if ((ret->key.dptr == NULL && key.dptr != NULL) ||
609 (ret->val.dptr == NULL && val.dptr != NULL))
611 talloc_free(ret);
612 DEBUG(0, ("Out of memory.\n"));
613 return NULL;
615 assert((ret->key_type == DT_INV) && (ret->val_type == DT_INV));
617 if (!is_cstr(key)) {
618 return ret;
620 if (parse_sid(key, &ret->key_type, &ret->sid)) {
621 parse_xid(val, &ret->val_type, &ret->id);
622 } else if (parse_xid(key, &ret->key_type, &ret->id)) {
623 if (is_cstr(val)) {
624 parse_sid(val, &ret->val_type, &ret->sid);
626 } else if (strcmp((const char*)key.dptr, "USER HWM") == 0) {
627 ret->key_type = DT_HWM;
628 if (val.dsize == 4) {
629 ret->id = IVAL(val.dptr,0);
630 ret->val_type = DT_UID;
632 } else if (strcmp((const char*)key.dptr, "GROUP HWM") == 0) {
633 ret->key_type = DT_HWM;
634 if (val.dsize == 4) {
635 ret->id = IVAL(val.dptr,0);
636 ret->val_type = DT_GID;
638 } else if (strcmp((const char*)key.dptr, "IDMAP_VERSION") == 0) {
639 ret->key_type = DT_VER;
640 if (val.dsize == 4) {
641 ret->id = IVAL(val.dptr,0);
642 ret->val_type = DT_VER;
644 } else if (strcmp((const char*)key.dptr, "__db_sequence_number__") == 0) {
645 ret->key_type = DT_SEQ;
646 if (val.dsize == 8) {
647 ret->id = *(uint64_t*)val.dptr;
648 ret->val_type = DT_SEQ;
652 return ret;
655 struct record* reverse_record(struct record* in)
657 return parse_record(talloc_parent(in), in->val, in->key);
661 /******************************************************************************/
664 char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d)
666 if (!tdb_data_is_empty(d)) {
667 char* ret = NULL;
668 cbuf* ost = cbuf_new(mem_ctx);
669 int len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize);
670 if (len != -1) {
671 cbuf_swapptr(ost, &ret, 0);
672 talloc_steal(mem_ctx, ret);
674 talloc_free(ost);
675 return ret;
677 return talloc_strdup(mem_ctx, "<NULL>");
681 TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr) {
682 cbuf* ost = cbuf_new(mem_ctx);
683 TDB_DATA ret = tdb_null;
684 srprs_skipws(ptr);
685 if (srprs_quoted(ptr, ost)) {
686 ret.dsize = cbuf_getpos(ost);
687 ret.dptr = (uint8_t *)talloc_steal(mem_ctx, cbuf_gets(ost,0));
689 talloc_free(ost);
690 return ret;
693 static int traverse_print_diff(struct db_record *rec, void* data) {
694 struct check_ctx* ctx = (struct check_ctx*)data;
695 TDB_DATA key;
696 TDB_DATA value;
697 TDB_DATA_diff diff;
698 TALLOC_CTX* mem = talloc_new(ctx->diff);
700 key = dbwrap_record_get_key(rec);
701 value = dbwrap_record_get_value(rec);
702 diff = unpack_diff(value);
704 DEBUG_DIFF(0, mem, "DIFF", key, diff.oval, diff.nval);
706 talloc_free(mem);
707 return 0;
711 static int traverse_commit(struct db_record *diff_rec, void* data) {
712 struct check_ctx* ctx = (struct check_ctx*)data;
713 TDB_DATA key;
714 TDB_DATA diff_value;
715 TDB_DATA_diff diff;
716 TDB_DATA value;
717 TALLOC_CTX* mem = talloc_new(ctx->diff);
718 int ret = -1;
719 NTSTATUS status;
720 struct check_actions* act = &ctx->action;
721 struct db_record* rec;
723 key = dbwrap_record_get_key(diff_rec);
724 diff_value = dbwrap_record_get_value(diff_rec);
725 diff = unpack_diff(diff_value);
727 rec = dbwrap_fetch_locked(ctx->db, mem, key);
728 if (rec == NULL) {
729 goto done;
732 value = dbwrap_record_get_value(rec);
734 if (!tdb_data_equal(value, diff.oval)) {
735 char action;
737 d_printf("Warning: record has changed: %s\n"
738 "expected: %s got %s\n", print_data(mem, key),
739 print_data(mem, diff.oval),
740 print_data(mem, value));
742 action = get_action(&act->invalid_diff, NULL, NULL);
743 if (action == 's') {
744 ret = 0;
745 goto done;
746 } else if (action == 'a') {
747 goto done;
751 DEBUG_DIFF(0, mem, "Commit", key, diff.oval, diff.nval);
753 if (tdb_data_is_empty(diff.nval)) {
754 status = dbwrap_record_delete(rec);
755 } else {
756 status = dbwrap_record_store(rec, diff.nval, 0);
759 if (!NT_STATUS_IS_OK(status)) {
760 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
761 if (!ctx->opts.force) {
762 goto done;
765 ret = 0;
766 done:
767 talloc_free(mem);
768 return ret;
771 static struct check_ctx*
772 check_init(TALLOC_CTX* mem_ctx, const struct check_options* o)
774 struct check_ctx* ctx = talloc_zero(mem_ctx, struct check_ctx);
775 if (ctx == NULL) {
776 DEBUG(0, (_("No memory\n")));
777 return NULL;
780 ctx->diff = db_open_rbt(ctx);
781 if (ctx->diff == NULL) {
782 talloc_free(ctx);
783 DEBUG(0, (_("No memory\n")));
784 return NULL;
787 ctx->action = check_actions_init(o);
788 ctx->opts = *o;
789 return ctx;
792 static bool check_open_db(struct check_ctx* ctx, const char* name, int oflags)
794 if (name == NULL) {
795 d_fprintf(stderr, _("Error: name == NULL in check_open_db().\n"));
796 return false;
799 if (ctx->db != NULL) {
800 if ((ctx->oflags == oflags) && (strcmp(ctx->name, name))) {
801 return true;
802 } else {
803 TALLOC_FREE(ctx->db);
807 ctx->db = db_open(ctx, name, 0, TDB_DEFAULT, oflags, 0,
808 DBWRAP_LOCK_ORDER_1);
809 if (ctx->db == NULL) {
810 d_fprintf(stderr,
811 _("Could not open idmap db (%s) for writing: %s\n"),
812 name, strerror(errno));
813 return false;
816 if (ctx->name != name) {
817 TALLOC_FREE(ctx->name);
818 ctx->name = talloc_strdup(ctx, name);
821 ctx->oflags = oflags;
822 return true;
825 static bool check_do_checks(struct check_ctx* ctx)
827 NTSTATUS status;
829 if (!check_version(ctx)) {
830 return false;
833 status = dbwrap_traverse(ctx->db, traverse_check, ctx, NULL);
835 if (!NT_STATUS_IS_OK(status)) {
836 DEBUG(0, ("failed to traverse %s\n", ctx->name));
837 return false;
840 check_hwm(ctx, "USER HWM", ctx->uid_hwm + 1);
841 check_hwm(ctx, "GROUP HWM", ctx->gid_hwm + 1);
843 return true;
846 static void check_summary(const struct check_ctx* ctx)
848 d_printf("uid hwm: %d\ngid hwm: %d\n", ctx->uid_hwm, ctx->gid_hwm);
849 d_printf("mappings: %d\nother: %d\n", ctx->n_map, ctx->n_other);
850 d_printf("invalid records: %d\nmissing links: %d\ninvalid links: %d\n",
851 ctx->n_invalid_record, ctx->n_missing_reverse,
852 ctx->n_invalid_mappping);
853 d_printf("%u changes:\n", ctx->n_diff);
856 static bool check_transaction_start(struct check_ctx* ctx) {
857 return (dbwrap_transaction_start(ctx->db) == 0);
860 static bool check_transaction_commit(struct check_ctx* ctx) {
861 return (dbwrap_transaction_commit(ctx->db) == 0);
864 static bool check_transaction_cancel(struct check_ctx* ctx) {
865 return (dbwrap_transaction_cancel(ctx->db) == 0);
869 static void check_diff_list(struct check_ctx* ctx) {
870 NTSTATUS status = dbwrap_traverse(ctx->diff, traverse_print_diff, ctx, NULL);
872 if (!NT_STATUS_IS_OK(status)) {
873 DEBUG(0, ("failed to traverse diff\n"));
878 static bool check_commit(struct check_ctx* ctx)
880 struct check_actions* act = &ctx->action;
881 char action;
882 NTSTATUS status = NT_STATUS_OK;
884 check_summary(ctx);
886 if (ctx->n_diff == 0) {
887 return true;
890 while ((action = get_action(&act->commit, NULL, NULL)) == 'l') {
891 check_diff_list(ctx);
893 if (action == 's') {
894 return true;
896 assert(action == 'c');
898 if (!check_open_db(ctx, ctx->name, O_RDWR)) {
899 return false;
902 if (!check_transaction_start(ctx)) {
903 return false;
906 status = dbwrap_traverse(ctx->diff, traverse_commit, ctx, NULL);
908 if (!NT_STATUS_IS_OK(status)) {
909 check_transaction_cancel(ctx);
910 return false;
912 if (ctx->opts.test) { /*get_action? */
913 return check_transaction_cancel(ctx);
914 } else {
915 return check_transaction_commit(ctx);
919 int net_idmap_check_db(const char* db, const struct check_options* o)
921 int ret = -1;
922 TALLOC_CTX* mem_ctx = talloc_stackframe();
923 struct check_ctx* ctx = check_init(mem_ctx, o);
925 if (!o->automatic && !isatty(STDIN_FILENO)) {
926 DEBUG(0, ("Interactive use needs tty, use --auto\n"));
927 goto done;
929 if (o->lock) {
930 if (check_open_db(ctx, db, O_RDWR)
931 && check_transaction_start(ctx))
933 if ( check_do_checks(ctx)
934 && check_commit(ctx)
935 && check_transaction_commit(ctx))
937 ret = 0;
938 } else {
939 check_transaction_cancel(ctx);
942 } else {
943 if (check_open_db(ctx, db, O_RDONLY)
944 && check_do_checks(ctx)
945 && check_commit(ctx))
947 ret = 0;
950 done:
951 talloc_free(mem_ctx);
952 return ret;
956 /*Local Variables:*/
957 /*mode: c*/
958 /*End:*/