s3-utils/net_rpc_printer.c: print more info on write error
[Samba/gebeck_regimport.git] / source3 / utils / net_idmap_check.c
blob3adc060a7c6620a66d5eacb4d83634171a6be9b8
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.h"
30 #include "net.h"
31 #include "../libcli/security/dom_sid.h"
32 #include "cbuf.h"
33 #include "srprs.h"
34 #include <termios.h>
35 #include "util_tdb.h"
37 static int traverse_commit(struct db_record *diff_rec, void* data);
38 static int traverse_check(struct db_record *rec, void* data);
40 static char* interact_edit(TALLOC_CTX* mem_ctx, const char* str);
41 static int interact_prompt(const char* msg, const char* accept, char def);
43 /* TDB_DATA *******************************************************************/
44 static char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d);
45 static TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr);
46 static TDB_DATA talloc_copy(TALLOC_CTX* mem_ctx, TDB_DATA data);
47 static bool is_empty(TDB_DATA data) {
48 return (data.dsize == 0) || (data.dptr == NULL);
51 /* record *********************************************************************/
53 enum DT {
54 DT_INV = 0,
55 DT_SID, DT_UID, DT_GID,
56 DT_HWM, DT_VER, DT_SEQ,
59 struct record {
60 enum DT key_type, val_type;
61 TDB_DATA key, val;
62 struct dom_sid sid;
63 long unsigned id;
66 static struct record* parse_record(TALLOC_CTX* ctx, TDB_DATA key, TDB_DATA val);
67 static struct record* reverse_record(struct record* rec);
69 static bool is_invalid(const struct record* r) {
70 return (r->key_type == DT_INV) || (r->val_type == DT_INV);
73 static bool is_map(const struct record* r) {
74 return (r->key_type == DT_SID)
75 || (r->key_type == DT_UID) || (r->key_type == DT_GID);
78 /* action *********************************************************************/
80 typedef struct check_action {
81 const char* fmt;
82 const char* name;
83 const char* prompt;
84 const char* answers;
85 char auto_action;
86 char default_action;
87 bool verbose;
88 } check_action;
90 struct check_actions {
91 check_action invalid_record;
92 check_action missing_reverse;
93 check_action invalid_mapping;
94 check_action invalid_edit;
95 check_action record_exists;
96 check_action no_version;
97 check_action wrong_version;
98 check_action invalid_hwm;
99 check_action commit;
100 check_action valid_mapping;
101 check_action valid_other;
102 check_action invalid_diff;
105 static struct check_actions
106 check_actions_init(const struct check_options* opts) {
107 struct check_actions ret = {
108 .invalid_record = (check_action) {
109 .name = "Invalid record",
110 .prompt = "[e]dit/[d]elete/[D]elete all"
111 "/[s]kip/[S]kip all",
112 .answers = "eds",
113 .default_action = 'e',
114 .verbose = true,
116 .missing_reverse = (check_action) {
117 .name = "Missing reverse mapping for",
118 .prompt = "[f]ix/[F]ix all/[e]dit/[d]elete/[D]elete all"
119 "/[s]kip/[S]kip all",
120 .answers = "feds",
121 .default_action = 'f',
122 .verbose = true,
124 .invalid_mapping = (check_action) {
125 .fmt = "%1$s: %2$s -> %3$s\n(%4$s <- %3$s)\n",
126 .name = "Invalid mapping",
127 .prompt = "[e]dit/[d]elete/[D]elete all"
128 "/[s]kip/[S]kip all",
129 .answers = "eds",
130 .default_action = 'd',
131 .verbose = true,
133 .invalid_edit = (check_action) {
134 .name = "Invalid record",
135 .prompt = "[e]dit/[d]elete/[D]elete all"
136 "/[s]kip/[S]kip all",
137 .answers = "eds",
138 .default_action = 'e',
139 .verbose = true,
141 .record_exists = (check_action) {
142 .fmt = "%1$s: %2$s\n-%4$s\n+%3$s\n",
143 .name = "Record exists",
144 .prompt = "[o]verwrite/[O]verwrite all/[e]dit"
145 "/[d]elete/[D]elete all/[s]kip/[S]kip all",
146 .answers = "oeds",
147 .default_action = 'o',
148 .verbose = true,
150 .no_version = (check_action) {
151 .prompt = "[f]ix/[s]kip/[a]bort",
152 .answers = "fsa",
153 .default_action = 'f',
155 .wrong_version = (check_action) {
156 .prompt = "[f]ix/[s]kip/[a]bort",
157 .answers = "fsa",
158 .default_action = 'a',
160 .invalid_hwm = (check_action) {
161 .prompt = "[f]ix/[s]kip",
162 .answers = "fs",
163 .default_action = 'f',
165 .commit = (check_action) {
166 .prompt = "[c]ommit/[l]ist/[s]kip",
167 .answers = "cls",
168 .default_action = 'l',
169 .verbose = true,
171 .valid_mapping = (check_action) {
172 .fmt = "%1$s: %2$s <-> %3$s\n",
173 .name = "Mapping",
174 .auto_action = 's',
175 .verbose = opts->verbose,
177 .valid_other = (check_action) {
178 .name = "Other",
179 .auto_action = 's',
180 .verbose = opts->verbose,
182 .invalid_diff = (check_action) {
183 .prompt = "[s]kip/[S]kip all/[c]ommit/[C]ommit all"
184 "/[a]bort",
185 .answers = "sca",
186 .default_action = 's',
190 if (!opts->repair) {
191 ret.invalid_record.auto_action = 's';
192 ret.missing_reverse.auto_action = 's';
193 ret.invalid_mapping.auto_action = 's';
194 ret.no_version.auto_action = 's';
195 ret.wrong_version.auto_action = 's';
196 ret.invalid_hwm.auto_action = 's';
197 ret.commit.auto_action = 's';
200 if (opts->automatic) {
201 ret.invalid_record.auto_action = 'd'; /* delete */
202 ret.missing_reverse.auto_action = 'f'; /* fix */
203 ret.invalid_mapping.auto_action = 'd'; /* delete */
204 ret.no_version.auto_action = 'f'; /* fix */
205 ret.wrong_version.auto_action = 'a'; /* abort */
206 ret.invalid_hwm.auto_action = 'f'; /* fix */
207 ret.commit.auto_action = 'c'; /* commit */
208 ret.invalid_diff.auto_action = 'a'; /* abort */
209 if (opts->force) {
210 ret.wrong_version.auto_action = 'f'; /* fix */
211 ret.invalid_diff.auto_action = 'c'; /* commit */
214 if (opts->test) {
215 ret.invalid_diff.auto_action = 'c'; /* commit */
216 /* ret.commit.auto_action = 'c';*/ /* commit */
219 return ret;
222 static char get_action(struct check_action* a, struct record* r, TDB_DATA* v) {
223 char ret;
224 if (a->verbose && (r != NULL)) {
225 if (!a->fmt) {
226 d_printf("%s: %s ", a->name, print_data(r, r->key));
227 if (is_map(r)) {
228 d_printf("-> %s\n", print_data(r, r->val));
229 } else if (r->key_type == DT_HWM ||
230 r->key_type == DT_VER ||
231 r->key_type == DT_SEQ)
233 d_printf(": %ld\n", r->id);
234 } else {
235 d_printf("\n");
237 } else {
238 d_printf(a->fmt, a->name,
239 print_data(r, r->key),
240 print_data(r, r->val),
241 (v ? print_data(r, *v) : ""));
245 if (a->auto_action != '\0') {
246 return a->auto_action;
249 ret = interact_prompt(a->prompt, a->answers, a->default_action);
251 if (isupper(ret)) {
252 ret = tolower(ret);
253 a->auto_action = ret;
255 a->default_action = ret;
256 return ret;
259 /* *************************************************************************/
261 typedef struct {
262 TDB_DATA oval, nval;
263 } TDB_DATA_diff;
265 static TDB_DATA pack_diff(TDB_DATA_diff* diff) {
266 return (TDB_DATA) {
267 .dptr = (uint8_t *)diff,
268 .dsize = sizeof(TDB_DATA_diff),
272 static TDB_DATA_diff unpack_diff(TDB_DATA data) {
273 assert(data.dsize == sizeof(TDB_DATA_diff));
274 return *(TDB_DATA_diff*)data.dptr;
277 #define DEBUG_DIFF(LEV,MEM,MSG,KEY,OLD,NEW) \
278 DEBUG(LEV, ("%s: %s\n", MSG, print_data(MEM, KEY))); \
279 if (!is_empty(OLD)) { \
280 DEBUGADD(LEV, ("-%s\n", print_data(MEM, OLD))); \
282 if (!is_empty(NEW)) { \
283 DEBUGADD(LEV, ("+%s\n", print_data(MEM, NEW))); \
286 struct check_ctx {
287 int oflags;
288 char* name;
289 bool transaction;
290 struct db_context *db;
291 struct db_context *diff;
292 struct check_actions action;
294 uint32_t uid_hwm;
295 uint32_t gid_hwm;
297 unsigned n_invalid_record;
298 unsigned n_missing_reverse;
299 unsigned n_invalid_mappping;
300 unsigned n_map;
301 unsigned n_other;
302 unsigned n_diff;
303 struct check_options opts;
307 static void adjust_hwm(struct check_ctx* ctx, const struct record* r);
309 static int add_record(struct check_ctx* ctx, TDB_DATA key, TDB_DATA value)
311 NTSTATUS status;
312 TDB_DATA_diff diff;
313 TALLOC_CTX* mem = talloc_new(ctx->diff);
314 struct db_record* rec = ctx->diff->fetch_locked(ctx->diff, mem, key);
315 if (rec == NULL) {
316 return -1;
318 if (rec->value.dptr == 0) { /* first entry */
319 diff.oval = dbwrap_fetch(ctx->db, ctx->diff, key);
320 } else {
321 diff = unpack_diff(rec->value);
322 talloc_free(diff.nval.dptr);
324 diff.nval = talloc_copy(ctx->diff, value);
326 DEBUG_DIFF(2, mem, "TDB DIFF", key, diff.oval, diff.nval);
328 status = rec->store(rec, pack_diff(&diff), 0);
330 talloc_free(mem);
332 if (!NT_STATUS_IS_OK(status)) {
333 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
334 return -1;
336 ctx->n_diff ++;
337 return 0;
340 static int del_record(struct check_ctx* ctx, TDB_DATA key) {
341 return add_record(ctx, key, tdb_null);
344 static TDB_DATA
345 fetch_record(struct check_ctx* ctx, TALLOC_CTX* mem_ctx, TDB_DATA key)
347 TDB_DATA tmp;
349 if (ctx->diff->fetch(ctx->diff, mem_ctx, key, &tmp) != 0) {
350 DEBUG(0, ("Out of memory!\n"));
351 return tdb_null;
353 if (tmp.dptr != NULL) {
354 TDB_DATA_diff diff = unpack_diff(tmp);
355 TDB_DATA ret = talloc_copy(mem_ctx, diff.nval);
356 talloc_free(tmp.dptr);
357 return ret;
360 if (ctx->db->fetch(ctx->db, mem_ctx, key, &tmp) != 0) {
361 DEBUG(0, ("Out of memory!\n"));
362 return tdb_null;
365 return tmp;
368 static void edit_record(struct record* r) {
369 TALLOC_CTX* mem = talloc_new(r);
370 cbuf* ost = cbuf_new(mem);
371 const char* str;
372 struct record* nr;
373 TDB_DATA key;
374 TDB_DATA val;
375 cbuf_printf(ost, "%s %s\n",
376 print_data(mem, r->key), print_data(mem, r->val));
377 str = interact_edit(mem, cbuf_gets(ost, 0));
378 key = parse_data(mem, &str);
379 val = parse_data(mem, &str);
380 nr = parse_record(talloc_parent(r), key, val);
381 if (nr != NULL) {
382 *r = *nr;
384 talloc_free(mem);
387 static bool check_version(struct check_ctx* ctx) {
388 static const char* key = "IDMAP_VERSION";
389 uint32_t version;
390 bool no_version = !dbwrap_fetch_uint32(ctx->db, key, &version);
391 char action = 's';
392 struct check_actions* act = &ctx->action;
393 if (no_version) {
394 d_printf("No version number, assume 2\n");
395 action = get_action(&act->no_version, NULL, NULL);
396 } else if (version != 2) {
397 d_printf("Wrong version number %d, should be 2\n", version);
398 action = get_action(&act->wrong_version, NULL, NULL);
400 switch (action) {
401 case 's':
402 break;
403 case 'f':
404 SIVAL(&version, 0, 2);
405 add_record(ctx, string_term_tdb_data(key),
406 make_tdb_data((uint8_t *)&version, sizeof(uint32_t)));
407 break;
408 case 'a':
409 return false;
410 default:
411 assert(false);
413 return true;
416 static void check_hwm(struct check_ctx* ctx, const char* key, uint32_t target) {
417 uint32_t hwm;
418 char action = 's';
419 bool found = dbwrap_fetch_uint32(ctx->db, key, &hwm);
420 struct check_actions* act = &ctx->action;
421 if (!found) {
422 d_printf("No %s should be %d\n", key, target);
423 action = get_action(&act->invalid_hwm, NULL, NULL);
424 } else if (target < hwm) {
425 d_printf("Invalid %s %d: should be %d\n", key, hwm, target);
426 action = get_action(&act->invalid_hwm, NULL, NULL);
428 if (action == 'f') {
429 SIVAL(&hwm, 0, target);
430 add_record(ctx, string_term_tdb_data(key),
431 make_tdb_data((uint8_t *)&hwm, sizeof(uint32_t)));
435 int traverse_check(struct db_record *rec, void* data) {
436 struct check_ctx* ctx = (struct check_ctx*)data;
437 struct check_actions* act = &ctx->action;
438 TALLOC_CTX* mem = talloc_new(ctx->diff);
439 struct record* r = parse_record(mem, rec->key, rec->value);
440 char action = 's';
442 if (is_invalid(r)) {
443 action = get_action(&act->invalid_record, r, NULL);
444 ctx->n_invalid_record++;
445 } else if (is_map(r)) {
446 TDB_DATA back = fetch_record(ctx, mem, r->val);
447 if (back.dptr == NULL) {
448 action = get_action(&act->missing_reverse, r, NULL);
449 ctx->n_missing_reverse++;
450 } else if (!tdb_data_equal(r->key, back)) {
451 action = get_action(&act->invalid_mapping, r, &back);
452 ctx->n_invalid_mappping++;
453 } else {
454 if (r->key_type == DT_SID) {
455 action = get_action(&act->valid_mapping, r, NULL);
456 ctx->n_map++;
457 } else {
458 action = get_action(&act->valid_mapping, NULL,
459 NULL);
462 adjust_hwm(ctx, r);
463 } else {
464 action = get_action(&act->valid_other, r, NULL);
465 ctx->n_other++;
468 while (action) {
469 switch (action) {
470 case 's': /* skip */
471 break;
472 case 'd': /* delete */
473 del_record(ctx, rec->key);
474 break;
475 case 'f': /* add reverse mapping */
476 add_record(ctx, rec->value, rec->key);
477 break;
478 case 'e': /* edit */
479 edit_record(r);
480 action = 'o';
481 if (is_invalid(r)) {
482 action = get_action(&act->invalid_edit, r,NULL);
483 continue;
485 if (!tdb_data_equal(rec->key, r->key)) {
486 TDB_DATA oval = fetch_record(ctx, mem, r->key);
487 if (!is_empty(oval) &&
488 !tdb_data_equal(oval, r->val))
490 action = get_action(&act->record_exists,
491 r, &oval);
492 if (action != 'o') {
493 continue;
497 if (is_map(r)) {
498 TDB_DATA okey = fetch_record(ctx, mem, r->val);
499 if (!is_empty(okey) &&
500 !tdb_data_equal(okey, r->key))
502 action = get_action(&act->record_exists,
503 reverse_record(r),
504 &okey);
507 continue;
508 case 'o': /* overwrite */
509 adjust_hwm(ctx, r);
510 if (!tdb_data_equal(rec->key, r->key)) {
511 del_record(ctx, rec->key);
513 add_record(ctx, r->key, r->val);
514 if (is_map(r)) {
515 add_record(ctx, r->val, r->key);
518 action = '\0';
521 talloc_free(mem);
523 return 0;
526 /******************************************************************************/
528 void adjust_hwm(struct check_ctx* ctx, const struct record* r) {
529 enum DT type = (r->key_type == DT_SID) ? r->val_type : r->key_type;
530 if (type == DT_UID) {
531 ctx->uid_hwm = MAX(ctx->uid_hwm, r->id);
532 } else if (type == DT_GID) {
533 ctx->gid_hwm = MAX(ctx->gid_hwm, r->id);
537 TDB_DATA talloc_copy(TALLOC_CTX* mem_ctx, TDB_DATA data) {
538 TDB_DATA ret = {
539 .dptr = (uint8_t *)talloc_size(mem_ctx, data.dsize+1),
540 .dsize = data.dsize
542 if (ret.dptr == NULL) {
543 ret.dsize = 0;
544 } else {
545 memcpy(ret.dptr, data.dptr, data.dsize);
546 ret.dptr[ret.dsize] = '\0';
548 return ret;
551 static bool is_cstr(TDB_DATA str) {
552 return !is_empty(str) && str.dptr[str.dsize-1] == '\0';
555 static bool parse_sid (TDB_DATA str, enum DT* type, struct dom_sid* sid) {
556 struct dom_sid tmp;
557 const char* s = (const char*)str.dptr;
558 if ((s[0] == 'S') && string_to_sid(&tmp, s)) {
559 *sid = tmp;
560 *type = DT_SID;
561 return true;
563 return false;
566 static bool parse_xid(TDB_DATA str, enum DT* type, unsigned long* id) {
567 char c, t;
568 unsigned long tmp;
569 if (sscanf((const char*)str.dptr, "%cID %lu%c", &c, &tmp, &t) == 2) {
570 if (c == 'U') {
571 *id = tmp;
572 *type = DT_UID;
573 return true;
574 } else if (c == 'G') {
575 *id = tmp;
576 *type = DT_GID;
577 return true;
580 return false;
584 struct record*
585 parse_record(TALLOC_CTX* mem_ctx, TDB_DATA key, TDB_DATA val)
587 struct record* ret = talloc_zero(mem_ctx, struct record);
588 if (ret == NULL) {
589 DEBUG(0, ("Out of memory.\n"));
590 return NULL;
592 ret->key = talloc_copy(ret, key);
593 ret->val = talloc_copy(ret, val);
594 if ((ret->key.dptr == NULL && key.dptr != NULL) ||
595 (ret->val.dptr == NULL && val.dptr != NULL))
597 talloc_free(ret);
598 DEBUG(0, ("Out of memory.\n"));
599 return NULL;
601 assert((ret->key_type == DT_INV) && (ret->val_type == DT_INV));
603 if (!is_cstr(key)) {
604 return ret;
606 if (parse_sid(key, &ret->key_type, &ret->sid)) {
607 parse_xid(val, &ret->val_type, &ret->id);
608 } else if (parse_xid(key, &ret->key_type, &ret->id)) {
609 if (is_cstr(val)) {
610 parse_sid(val, &ret->val_type, &ret->sid);
612 } else if (strcmp((const char*)key.dptr, "USER HWM") == 0) {
613 ret->key_type = DT_HWM;
614 if (val.dsize == 4) {
615 ret->id = IVAL(val.dptr,0);
616 ret->val_type = DT_UID;
618 } else if (strcmp((const char*)key.dptr, "GROUP HWM") == 0) {
619 ret->key_type = DT_HWM;
620 if (val.dsize == 4) {
621 ret->id = IVAL(val.dptr,0);
622 ret->val_type = DT_GID;
624 } else if (strcmp((const char*)key.dptr, "IDMAP_VERSION") == 0) {
625 ret->key_type = DT_VER;
626 if (val.dsize == 4) {
627 ret->id = IVAL(val.dptr,0);
628 ret->val_type = DT_VER;
630 } else if (strcmp((const char*)key.dptr, "__db_sequence_number__") == 0) {
631 ret->key_type = DT_SEQ;
632 if (val.dsize == 8) {
633 ret->id = *(uint64_t*)val.dptr;
634 ret->val_type = DT_SEQ;
638 return ret;
641 struct record* reverse_record(struct record* in)
643 return parse_record(talloc_parent(in), in->val, in->key);
647 /******************************************************************************/
649 int interact_prompt(const char* msg, const char* acc, char def) {
650 struct termios old_tio, new_tio;
651 int c;
653 tcgetattr(STDIN_FILENO, &old_tio);
654 new_tio=old_tio;
655 new_tio.c_lflag &=(~ICANON & ~ECHO);
656 tcsetattr(STDIN_FILENO, TCSANOW, &new_tio);
658 do {
659 d_printf("%s? [%c]\n", msg, def);
660 fflush(stdout);
661 c = getchar();
662 if (c == '\n') {
663 c = def;
664 break;
666 else if (strchr(acc, tolower(c)) != NULL) {
667 break;
669 d_printf("Invalid input '%c'\n", c);
670 } while(c != EOF);
671 tcsetattr(STDIN_FILENO, TCSANOW, &old_tio);
672 return c;
675 char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d)
677 if (!is_empty(d)) {
678 char* ret = NULL;
679 cbuf* ost = cbuf_new(mem_ctx);
680 int len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize);
681 if (len != -1) {
682 cbuf_swapptr(ost, &ret, 0);
683 talloc_steal(mem_ctx, ret);
685 talloc_free(ost);
686 return ret;
688 return talloc_strdup(mem_ctx, "<NULL>");
692 TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr) {
693 cbuf* ost = cbuf_new(mem_ctx);
694 TDB_DATA ret = tdb_null;
695 srprs_skipws(ptr);
696 if (srprs_quoted(ptr, ost)) {
697 ret.dsize = cbuf_getpos(ost);
698 ret.dptr = (uint8_t *)talloc_steal(mem_ctx, cbuf_gets(ost,0));
700 talloc_free(ost);
701 return ret;
704 static const char* get_editor(void) {
705 static const char* editor = NULL;
706 if (editor == NULL) {
707 editor = getenv("VISUAL");
708 if (editor == NULL) {
709 editor = getenv("EDITOR");
711 if (editor == NULL) {
712 editor = "vi";
715 return editor;
718 char* interact_edit(TALLOC_CTX* mem_ctx, const char* str) {
719 char fname[] = "/tmp/net_idmap_check.XXXXXX";
720 char buf[128];
721 char* ret = NULL;
722 FILE* file;
724 int fd = mkstemp(fname);
725 if (fd == -1) {
726 DEBUG(0, ("failed to mkstemp %s: %s\n", fname,
727 strerror(errno)));
728 return NULL;
731 file = fdopen(fd, "w");
732 if (!file) {
733 DEBUG(0, ("failed to open %s for writing: %s\n", fname,
734 strerror(errno)));
735 close(fd);
736 unlink(fname);
737 return NULL;
740 fprintf(file, "%s", str);
741 fclose(file);
743 snprintf(buf, sizeof(buf), "%s %s\n", get_editor(), fname);
744 if (system(buf) != 0) {
745 DEBUG(0, ("failed to start editor %s: %s\n", buf,
746 strerror(errno)));
747 unlink(fname);
748 return NULL;
751 file = fopen(fname, "r");
752 if (!file) {
753 DEBUG(0, ("failed to open %s for reading: %s\n", fname,
754 strerror(errno)));
755 unlink(fname);
756 return NULL;
758 while ( fgets(buf, sizeof(buf), file) ) {
759 ret = talloc_strdup_append(ret, buf);
761 fclose(file);
762 unlink(fname);
764 return talloc_steal(mem_ctx, ret);
768 static int traverse_print_diff(struct db_record *rec, void* data) {
769 struct check_ctx* ctx = (struct check_ctx*)data;
770 TDB_DATA key = rec->key;
771 TDB_DATA_diff diff = unpack_diff(rec->value);
772 TALLOC_CTX* mem = talloc_new(ctx->diff);
774 DEBUG_DIFF(0, mem, "DIFF", key, diff.oval, diff.nval);
776 talloc_free(mem);
777 return 0;
781 static int traverse_commit(struct db_record *diff_rec, void* data) {
782 struct check_ctx* ctx = (struct check_ctx*)data;
783 TDB_DATA_diff diff = unpack_diff(diff_rec->value);
784 TDB_DATA key = diff_rec->key;
785 TALLOC_CTX* mem = talloc_new(ctx->diff);
786 int ret = -1;
787 NTSTATUS status;
788 struct check_actions* act = &ctx->action;
790 struct db_record* rec = ctx->db->fetch_locked(ctx->db, mem, key);
791 if (rec == NULL) {
792 goto done;
795 if (!tdb_data_equal(rec->value, diff.oval)) {
796 char action;
798 d_printf("Warning: record has changed: %s\n"
799 "expected: %s got %s\n", print_data(mem, key),
800 print_data(mem, diff.oval),
801 print_data(mem, rec->value));
803 action = get_action(&act->invalid_diff, NULL, NULL);
804 if (action == 's') {
805 ret = 0;
806 goto done;
807 } else if (action == 'a') {
808 goto done;
812 DEBUG_DIFF(0, mem, "Commit", key, diff.oval, diff.nval);
814 if (is_empty(diff.nval)) {
815 status = rec->delete_rec(rec);
816 } else {
817 status = rec->store(rec, diff.nval, 0);
820 if (!NT_STATUS_IS_OK(status)) {
821 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
822 if (!ctx->opts.force) {
823 goto done;
826 ret = 0;
827 done:
828 talloc_free(mem);
829 return ret;
832 static struct check_ctx*
833 check_init(TALLOC_CTX* mem_ctx, const struct check_options* o)
835 struct check_ctx* ctx = talloc_zero(mem_ctx, struct check_ctx);
836 if (ctx == NULL) {
837 DEBUG(0, (_("No memory\n")));
838 return NULL;
841 ctx->diff = db_open_rbt(ctx);
842 if (ctx->diff == NULL) {
843 talloc_free(ctx);
844 DEBUG(0, (_("No memory\n")));
845 return NULL;
848 ctx->action = check_actions_init(o);
849 ctx->opts = *o;
850 return ctx;
853 static bool check_open_db(struct check_ctx* ctx, const char* name, int oflags)
855 if (name == NULL) {
856 d_fprintf(stderr, _("Error: name == NULL in check_open_db().\n"));
857 return false;
860 if (ctx->db != NULL) {
861 if ((ctx->oflags == oflags) && (strcmp(ctx->name, name))) {
862 return true;
863 } else {
864 TALLOC_FREE(ctx->db);
868 ctx->db = db_open(ctx, name, 0, TDB_DEFAULT, oflags, 0);
869 if (ctx->db == NULL) {
870 d_fprintf(stderr,
871 _("Could not open idmap db (%s) for writing: %s\n"),
872 name, strerror(errno));
873 return false;
876 if (ctx->name != name) {
877 TALLOC_FREE(ctx->name);
878 ctx->name = talloc_strdup(ctx, name);
881 ctx->oflags = oflags;
882 return true;
885 static bool check_do_checks(struct check_ctx* ctx)
887 NTSTATUS status;
889 if (!check_version(ctx)) {
890 return false;
893 status = dbwrap_traverse(ctx->db, traverse_check, ctx);
895 if (!NT_STATUS_IS_OK(status)) {
896 DEBUG(0, ("failed to traverse %s\n", ctx->name));
897 return false;
900 check_hwm(ctx, "USER HWM", ctx->uid_hwm + 1);
901 check_hwm(ctx, "GROUP HWM", ctx->gid_hwm + 1);
903 return true;
906 static void check_summary(const struct check_ctx* ctx)
908 d_printf("uid hwm: %d\ngid hwm: %d\n", ctx->uid_hwm, ctx->gid_hwm);
909 d_printf("mappings: %d\nother: %d\n", ctx->n_map, ctx->n_other);
910 d_printf("invalid records: %d\nmissing links: %d\ninvalid links: %d\n",
911 ctx->n_invalid_record, ctx->n_missing_reverse,
912 ctx->n_invalid_mappping);
913 d_printf("%u changes:\n", ctx->n_diff);
916 static bool check_transaction_start(struct check_ctx* ctx) {
917 return (ctx->db->transaction_start(ctx->db) == 0);
920 static bool check_transaction_commit(struct check_ctx* ctx) {
921 return (ctx->db->transaction_commit(ctx->db) == 0);
924 static bool check_transaction_cancel(struct check_ctx* ctx) {
925 return (ctx->db->transaction_cancel(ctx->db) == 0);
929 static void check_diff_list(struct check_ctx* ctx) {
930 NTSTATUS status = dbwrap_traverse(ctx->diff, traverse_print_diff, ctx);
932 if (!NT_STATUS_IS_OK(status)) {
933 DEBUG(0, ("failed to traverse diff\n"));
938 static bool check_commit(struct check_ctx* ctx)
940 struct check_actions* act = &ctx->action;
941 char action;
942 NTSTATUS status = NT_STATUS_OK;
944 check_summary(ctx);
946 if (ctx->n_diff == 0) {
947 return true;
950 while ((action = get_action(&act->commit, NULL, NULL)) == 'l') {
951 check_diff_list(ctx);
953 if (action == 's') {
954 return true;
956 assert(action == 'c');
958 if (!check_open_db(ctx, ctx->name, O_RDWR)) {
959 return false;
962 if (!check_transaction_start(ctx)) {
963 return false;
966 status = dbwrap_traverse(ctx->diff, traverse_commit, ctx);
968 if (!NT_STATUS_IS_OK(status)) {
969 check_transaction_cancel(ctx);
970 return false;
972 if (ctx->opts.test) { //get_action?
973 return check_transaction_cancel(ctx);
974 } else {
975 return check_transaction_commit(ctx);
979 int net_idmap_check_db(const char* db, const struct check_options* o)
981 int ret = -1;
982 TALLOC_CTX* mem_ctx = talloc_stackframe();
983 struct check_ctx* ctx = check_init(mem_ctx, o);
985 if (!o->automatic && !isatty(STDIN_FILENO)) {
986 DEBUG(0, ("Interactive use needs tty, use --auto\n"));
987 goto done;
989 if (o->lock) {
990 if (check_open_db(ctx, db, O_RDWR)
991 && check_transaction_start(ctx))
993 if ( check_do_checks(ctx)
994 && check_commit(ctx)
995 && check_transaction_commit(ctx))
997 ret = 0;
998 } else {
999 check_transaction_cancel(ctx);
1002 } else {
1003 if (check_open_db(ctx, db, O_RDONLY)
1004 && check_do_checks(ctx)
1005 && check_commit(ctx))
1007 ret = 0;
1010 done:
1011 talloc_free(mem_ctx);
1012 return ret;
1016 /*Local Variables:*/
1017 /*mode: c*/
1018 /*End:*/