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/>.
21 * @brief Check the idmap database.
22 * @author Gregor Beck <gb@sernet.de>
26 #include "net_idmap_check.h"
28 #include "system/filesys.h"
29 #include "dbwrap/dbwrap.h"
30 #include "dbwrap/dbwrap_open.h"
31 #include "dbwrap/dbwrap_rbt.h"
33 #include "../libcli/security/dom_sid.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 *********************************************************************/
50 DT_SID
, DT_UID
, DT_GID
,
51 DT_HWM
, DT_VER
, DT_SEQ
,
55 enum DT key_type
, val_type
;
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 void (*fmt
)(struct check_action
*a
,
87 struct check_actions
{
88 check_action invalid_record
;
89 check_action missing_reverse
;
90 check_action invalid_mapping
;
91 check_action invalid_edit
;
92 check_action record_exists
;
93 check_action no_version
;
94 check_action wrong_version
;
95 check_action invalid_hwm
;
97 check_action valid_mapping
;
98 check_action valid_other
;
99 check_action invalid_diff
;
102 static void invalid_mapping_fmt(struct check_action
*a
,
106 d_printf("%1$s: %2$s -> %3$s\n(%4$s <- %3$s)\n",
108 print_data(r
, r
->key
),
109 print_data(r
, r
->val
),
110 (v
? print_data(r
, *v
) : ""));
113 static void record_exists_fmt(struct check_action
*a
,
117 d_printf("%1$s: %2$s\n-%4$s\n+%3$s\n",
119 print_data(r
, r
->key
),
120 print_data(r
, r
->val
),
121 (v
? print_data(r
, *v
) : ""));
124 static void valid_mapping_fmt(struct check_action
*a
,
128 d_printf("%1$s: %2$s <-> %3$s\n",
130 print_data(r
, r
->key
),
131 print_data(r
, r
->val
));
134 static struct check_actions
135 check_actions_init(const struct check_options
* opts
) {
136 struct check_actions ret
= {
137 .invalid_record
= (check_action
) {
138 .name
= "Invalid record",
139 .prompt
= "[e]dit/[d]elete/[D]elete all"
140 "/[s]kip/[S]kip all",
142 .default_action
= 'e',
145 .missing_reverse
= (check_action
) {
146 .name
= "Missing reverse mapping for",
147 .prompt
= "[f]ix/[F]ix all/[e]dit/[d]elete/[D]elete all"
148 "/[s]kip/[S]kip all",
150 .default_action
= 'f',
153 .invalid_mapping
= (check_action
) {
154 .fmt
= invalid_mapping_fmt
,
155 .name
= "Invalid mapping",
156 .prompt
= "[e]dit/[d]elete/[D]elete all"
157 "/[s]kip/[S]kip all",
159 .default_action
= 'd',
162 .invalid_edit
= (check_action
) {
163 .name
= "Invalid record",
164 .prompt
= "[e]dit/[d]elete/[D]elete all"
165 "/[s]kip/[S]kip all",
167 .default_action
= 'e',
170 .record_exists
= (check_action
) {
171 .fmt
= record_exists_fmt
,
172 .name
= "Record exists",
173 .prompt
= "[o]verwrite/[O]verwrite all/[e]dit"
174 "/[d]elete/[D]elete all/[s]kip/[S]kip all",
176 .default_action
= 'o',
179 .no_version
= (check_action
) {
180 .prompt
= "[f]ix/[s]kip/[a]bort",
182 .default_action
= 'f',
184 .wrong_version
= (check_action
) {
185 .prompt
= "[f]ix/[s]kip/[a]bort",
187 .default_action
= 'a',
189 .invalid_hwm
= (check_action
) {
190 .prompt
= "[f]ix/[s]kip",
192 .default_action
= 'f',
194 .commit
= (check_action
) {
195 .prompt
= "[c]ommit/[l]ist/[s]kip",
197 .default_action
= 'l',
200 .valid_mapping
= (check_action
) {
201 .fmt
= valid_mapping_fmt
,
204 .verbose
= opts
->verbose
,
206 .valid_other
= (check_action
) {
209 .verbose
= opts
->verbose
,
211 .invalid_diff
= (check_action
) {
212 .prompt
= "[s]kip/[S]kip all/[c]ommit/[C]ommit all"
215 .default_action
= 's',
220 ret
.invalid_record
.auto_action
= 's';
221 ret
.missing_reverse
.auto_action
= 's';
222 ret
.invalid_mapping
.auto_action
= 's';
223 ret
.no_version
.auto_action
= 's';
224 ret
.wrong_version
.auto_action
= 's';
225 ret
.invalid_hwm
.auto_action
= 's';
226 ret
.commit
.auto_action
= 's';
229 if (opts
->automatic
) {
230 ret
.invalid_record
.auto_action
= 'd'; /* delete */
231 ret
.missing_reverse
.auto_action
= 'f'; /* fix */
232 ret
.invalid_mapping
.auto_action
= 'd'; /* delete */
233 ret
.no_version
.auto_action
= 'f'; /* fix */
234 ret
.wrong_version
.auto_action
= 'a'; /* abort */
235 ret
.invalid_hwm
.auto_action
= 'f'; /* fix */
236 ret
.commit
.auto_action
= 'c'; /* commit */
237 ret
.invalid_diff
.auto_action
= 'a'; /* abort */
239 ret
.wrong_version
.auto_action
= 'f'; /* fix */
240 ret
.invalid_diff
.auto_action
= 'c'; /* commit */
244 ret
.invalid_diff
.auto_action
= 'c'; /* commit */
245 /* ret.commit.auto_action = 'c';*/ /* commit */
251 static char get_action(struct check_action
* a
, struct record
* r
, TDB_DATA
* v
) {
253 if (a
->verbose
&& (r
!= NULL
)) {
255 d_printf("%s: %s ", a
->name
, print_data(r
, r
->key
));
257 d_printf("-> %s\n", print_data(r
, r
->val
));
258 } else if (r
->key_type
== DT_HWM
||
259 r
->key_type
== DT_VER
||
260 r
->key_type
== DT_SEQ
)
262 d_printf(": %ld\n", r
->id
);
271 if (a
->auto_action
!= '\0') {
272 return a
->auto_action
;
275 ret
= interact_prompt(a
->prompt
, a
->answers
, a
->default_action
);
279 a
->auto_action
= ret
;
281 a
->default_action
= ret
;
285 /* *************************************************************************/
291 static TDB_DATA
pack_diff(TDB_DATA_diff
* diff
) {
293 .dptr
= (uint8_t *)diff
,
294 .dsize
= sizeof(TDB_DATA_diff
),
298 static TDB_DATA_diff
unpack_diff(TDB_DATA data
) {
299 assert(data
.dsize
== sizeof(TDB_DATA_diff
));
300 return *(TDB_DATA_diff
*)data
.dptr
;
303 #define DEBUG_DIFF(LEV,MEM,MSG,KEY,OLD,NEW) \
304 DEBUG(LEV, ("%s: %s\n", MSG, print_data(MEM, KEY))); \
305 if (!tdb_data_is_empty(OLD)) { \
306 DEBUGADD(LEV, ("-%s\n", print_data(MEM, OLD))); \
308 if (!tdb_data_is_empty(NEW)) { \
309 DEBUGADD(LEV, ("+%s\n", print_data(MEM, NEW))); \
316 struct db_context
*db
;
317 struct db_context
*diff
;
318 struct check_actions action
;
323 unsigned n_invalid_record
;
324 unsigned n_missing_reverse
;
325 unsigned n_invalid_mappping
;
329 struct check_options opts
;
333 static void adjust_hwm(struct check_ctx
* ctx
, const struct record
* r
);
335 static int add_record(struct check_ctx
* ctx
, TDB_DATA key
, TDB_DATA value
)
339 TALLOC_CTX
* mem
= talloc_new(ctx
->diff
);
341 struct db_record
*rec
= dbwrap_fetch_locked(ctx
->diff
, mem
, key
);
347 recvalue
= dbwrap_record_get_value(rec
);
349 if (recvalue
.dptr
== 0) { /* first entry */
350 status
= dbwrap_fetch(ctx
->db
, ctx
->diff
, key
, &diff
.oval
);
351 if (!NT_STATUS_IS_OK(status
)) {
352 diff
.oval
= tdb_null
;
355 diff
= unpack_diff(recvalue
);
356 talloc_free(diff
.nval
.dptr
);
358 diff
.nval
= tdb_data_talloc_copy(ctx
->diff
, value
);
360 DEBUG_DIFF(2, mem
, "TDB DIFF", key
, diff
.oval
, diff
.nval
);
362 status
= dbwrap_record_store(rec
, pack_diff(&diff
), 0);
366 if (!NT_STATUS_IS_OK(status
)) {
367 DEBUG(0, ("could not store record %s\n", nt_errstr(status
)));
374 static int del_record(struct check_ctx
* ctx
, TDB_DATA key
) {
375 return add_record(ctx
, key
, tdb_null
);
379 fetch_record(struct check_ctx
* ctx
, TALLOC_CTX
* mem_ctx
, TDB_DATA key
)
384 status
= dbwrap_fetch(ctx
->diff
, mem_ctx
, key
, &tmp
);
386 if (NT_STATUS_IS_OK(status
)) {
387 TDB_DATA_diff diff
= unpack_diff(tmp
);
388 TDB_DATA ret
= tdb_data_talloc_copy(mem_ctx
, diff
.nval
);
389 talloc_free(tmp
.dptr
);
393 status
= dbwrap_fetch(ctx
->db
, mem_ctx
, key
, &tmp
);
394 if (!NT_STATUS_IS_OK(status
)) {
401 static void edit_record(struct record
* r
) {
402 TALLOC_CTX
* mem
= talloc_new(r
);
403 cbuf
* ost
= cbuf_new(mem
);
408 cbuf_printf(ost
, "%s %s\n",
409 print_data(mem
, r
->key
), print_data(mem
, r
->val
));
410 str
= interact_edit(mem
, cbuf_gets(ost
, 0));
411 key
= parse_data(mem
, &str
);
412 val
= parse_data(mem
, &str
);
413 nr
= parse_record(talloc_parent(r
), key
, val
);
420 static bool check_version(struct check_ctx
* ctx
) {
421 static const char* key
= "IDMAP_VERSION";
425 struct check_actions
* act
= &ctx
->action
;
427 status
= dbwrap_fetch_uint32_bystring(ctx
->db
, key
, &version
);
428 if (!NT_STATUS_IS_OK(status
)) {
429 d_printf("No version number, assume 2\n");
430 action
= get_action(&act
->no_version
, NULL
, NULL
);
431 } else if (version
!= 2) {
432 d_printf("Wrong version number %d, should be 2\n", version
);
433 action
= get_action(&act
->wrong_version
, NULL
, NULL
);
439 SIVAL(&version
, 0, 2);
440 add_record(ctx
, string_term_tdb_data(key
),
441 make_tdb_data((uint8_t *)&version
, sizeof(uint32_t)));
451 static void check_hwm(struct check_ctx
* ctx
, const char* key
, uint32_t target
) {
455 struct check_actions
* act
= &ctx
->action
;
457 status
= dbwrap_fetch_uint32_bystring(ctx
->db
, key
, &hwm
);
458 if (!NT_STATUS_IS_OK(status
)) {
459 d_printf("No %s should be %d\n", key
, target
);
460 action
= get_action(&act
->invalid_hwm
, NULL
, NULL
);
461 } else if (target
< hwm
) {
462 d_printf("Invalid %s %d: should be %d\n", key
, hwm
, target
);
463 action
= get_action(&act
->invalid_hwm
, NULL
, NULL
);
466 SIVAL(&hwm
, 0, target
);
467 add_record(ctx
, string_term_tdb_data(key
),
468 make_tdb_data((uint8_t *)&hwm
, sizeof(uint32_t)));
472 int traverse_check(struct db_record
*rec
, void* data
) {
473 struct check_ctx
* ctx
= (struct check_ctx
*)data
;
474 struct check_actions
* act
= &ctx
->action
;
475 TALLOC_CTX
* mem
= talloc_new(ctx
->diff
);
481 key
= dbwrap_record_get_key(rec
);
482 value
= dbwrap_record_get_value(rec
);
484 r
= parse_record(mem
, key
, value
);
487 action
= get_action(&act
->invalid_record
, r
, NULL
);
488 ctx
->n_invalid_record
++;
489 } else if (is_map(r
)) {
490 TDB_DATA back
= fetch_record(ctx
, mem
, r
->val
);
491 if (back
.dptr
== NULL
) {
492 action
= get_action(&act
->missing_reverse
, r
, NULL
);
493 ctx
->n_missing_reverse
++;
494 } else if (!tdb_data_equal(r
->key
, back
)) {
495 action
= get_action(&act
->invalid_mapping
, r
, &back
);
496 ctx
->n_invalid_mappping
++;
498 if (r
->key_type
== DT_SID
) {
499 action
= get_action(&act
->valid_mapping
, r
, NULL
);
502 action
= get_action(&act
->valid_mapping
, NULL
,
508 action
= get_action(&act
->valid_other
, r
, NULL
);
516 case 'd': /* delete */
517 del_record(ctx
, key
);
519 case 'f': /* add reverse mapping */
520 add_record(ctx
, value
, key
);
526 action
= get_action(&act
->invalid_edit
, r
,NULL
);
529 if (!tdb_data_equal(key
, r
->key
)) {
530 TDB_DATA oval
= fetch_record(ctx
, mem
, r
->key
);
531 if (!tdb_data_is_empty(oval
) &&
532 !tdb_data_equal(oval
, r
->val
))
534 action
= get_action(&act
->record_exists
,
542 TDB_DATA okey
= fetch_record(ctx
, mem
, r
->val
);
543 if (!tdb_data_is_empty(okey
) &&
544 !tdb_data_equal(okey
, r
->key
))
546 action
= get_action(&act
->record_exists
,
552 case 'o': /* overwrite */
554 if (!tdb_data_equal(key
, r
->key
)) {
555 del_record(ctx
, key
);
557 add_record(ctx
, r
->key
, r
->val
);
559 add_record(ctx
, r
->val
, r
->key
);
570 /******************************************************************************/
572 void adjust_hwm(struct check_ctx
* ctx
, const struct record
* r
) {
573 enum DT type
= (r
->key_type
== DT_SID
) ? r
->val_type
: r
->key_type
;
574 if (type
== DT_UID
) {
575 ctx
->uid_hwm
= MAX(ctx
->uid_hwm
, r
->id
);
576 } else if (type
== DT_GID
) {
577 ctx
->gid_hwm
= MAX(ctx
->gid_hwm
, r
->id
);
581 static bool is_cstr(TDB_DATA str
) {
582 return !tdb_data_is_empty(str
) && str
.dptr
[str
.dsize
-1] == '\0';
585 static bool parse_sid (TDB_DATA str
, enum DT
* type
, struct dom_sid
* sid
) {
587 const char* s
= (const char*)str
.dptr
;
588 if ((s
[0] == 'S') && string_to_sid(&tmp
, s
)) {
596 static bool parse_xid(TDB_DATA str
, enum DT
* type
, unsigned long* id
) {
599 if (sscanf((const char*)str
.dptr
, "%cID %lu%c", &c
, &tmp
, &t
) == 2) {
604 } else if (c
== 'G') {
615 parse_record(TALLOC_CTX
* mem_ctx
, TDB_DATA key
, TDB_DATA val
)
617 struct record
* ret
= talloc_zero(mem_ctx
, struct record
);
619 DEBUG(0, ("Out of memory.\n"));
622 ret
->key
= tdb_data_talloc_copy(ret
, key
);
623 ret
->val
= tdb_data_talloc_copy(ret
, val
);
624 if ((ret
->key
.dptr
== NULL
&& key
.dptr
!= NULL
) ||
625 (ret
->val
.dptr
== NULL
&& val
.dptr
!= NULL
))
628 DEBUG(0, ("Out of memory.\n"));
631 assert((ret
->key_type
== DT_INV
) && (ret
->val_type
== DT_INV
));
636 if (parse_sid(key
, &ret
->key_type
, &ret
->sid
)) {
637 parse_xid(val
, &ret
->val_type
, &ret
->id
);
638 } else if (parse_xid(key
, &ret
->key_type
, &ret
->id
)) {
640 parse_sid(val
, &ret
->val_type
, &ret
->sid
);
642 } else if (strcmp((const char*)key
.dptr
, "USER HWM") == 0) {
643 ret
->key_type
= DT_HWM
;
644 if (val
.dsize
== 4) {
645 ret
->id
= IVAL(val
.dptr
,0);
646 ret
->val_type
= DT_UID
;
648 } else if (strcmp((const char*)key
.dptr
, "GROUP HWM") == 0) {
649 ret
->key_type
= DT_HWM
;
650 if (val
.dsize
== 4) {
651 ret
->id
= IVAL(val
.dptr
,0);
652 ret
->val_type
= DT_GID
;
654 } else if (strcmp((const char*)key
.dptr
, "IDMAP_VERSION") == 0) {
655 ret
->key_type
= DT_VER
;
656 if (val
.dsize
== 4) {
657 ret
->id
= IVAL(val
.dptr
,0);
658 ret
->val_type
= DT_VER
;
660 } else if (strcmp((const char*)key
.dptr
, "__db_sequence_number__") == 0) {
661 ret
->key_type
= DT_SEQ
;
662 if (val
.dsize
== 8) {
663 ret
->id
= *(uint64_t*)val
.dptr
;
664 ret
->val_type
= DT_SEQ
;
671 struct record
* reverse_record(struct record
* in
)
673 return parse_record(talloc_parent(in
), in
->val
, in
->key
);
677 /******************************************************************************/
680 char* print_data(TALLOC_CTX
* mem_ctx
, TDB_DATA d
)
682 if (!tdb_data_is_empty(d
)) {
684 cbuf
* ost
= cbuf_new(mem_ctx
);
685 int len
= cbuf_print_quoted(ost
, (const char*)d
.dptr
, d
.dsize
);
687 cbuf_swapptr(ost
, &ret
, 0);
688 talloc_steal(mem_ctx
, ret
);
693 return talloc_strdup(mem_ctx
, "<NULL>");
697 TDB_DATA
parse_data(TALLOC_CTX
* mem_ctx
, const char** ptr
) {
698 cbuf
* ost
= cbuf_new(mem_ctx
);
699 TDB_DATA ret
= tdb_null
;
701 if (srprs_quoted(ptr
, ost
)) {
702 ret
.dsize
= cbuf_getpos(ost
);
703 ret
.dptr
= (uint8_t *)talloc_steal(mem_ctx
, cbuf_gets(ost
,0));
709 static int traverse_print_diff(struct db_record
*rec
, void* data
) {
710 struct check_ctx
* ctx
= (struct check_ctx
*)data
;
714 TALLOC_CTX
* mem
= talloc_new(ctx
->diff
);
716 key
= dbwrap_record_get_key(rec
);
717 value
= dbwrap_record_get_value(rec
);
718 diff
= unpack_diff(value
);
720 DEBUG_DIFF(0, mem
, "DIFF", key
, diff
.oval
, diff
.nval
);
727 static int traverse_commit(struct db_record
*diff_rec
, void* data
) {
728 struct check_ctx
* ctx
= (struct check_ctx
*)data
;
733 TALLOC_CTX
* mem
= talloc_new(ctx
->diff
);
736 struct check_actions
* act
= &ctx
->action
;
737 struct db_record
* rec
;
739 key
= dbwrap_record_get_key(diff_rec
);
740 diff_value
= dbwrap_record_get_value(diff_rec
);
741 diff
= unpack_diff(diff_value
);
743 rec
= dbwrap_fetch_locked(ctx
->db
, mem
, key
);
748 value
= dbwrap_record_get_value(rec
);
750 if (!tdb_data_equal(value
, diff
.oval
)) {
753 d_printf("Warning: record has changed: %s\n"
754 "expected: %s got %s\n", print_data(mem
, key
),
755 print_data(mem
, diff
.oval
),
756 print_data(mem
, value
));
758 action
= get_action(&act
->invalid_diff
, NULL
, NULL
);
762 } else if (action
== 'a') {
767 DEBUG_DIFF(0, mem
, "Commit", key
, diff
.oval
, diff
.nval
);
769 if (tdb_data_is_empty(diff
.nval
)) {
770 status
= dbwrap_record_delete(rec
);
772 status
= dbwrap_record_store(rec
, diff
.nval
, 0);
775 if (!NT_STATUS_IS_OK(status
)) {
776 DEBUG(0, ("could not store record %s\n", nt_errstr(status
)));
777 if (!ctx
->opts
.force
) {
787 static struct check_ctx
*
788 check_init(TALLOC_CTX
* mem_ctx
, const struct check_options
* o
)
790 struct check_ctx
* ctx
= talloc_zero(mem_ctx
, struct check_ctx
);
792 DEBUG(0, (_("No memory\n")));
796 ctx
->diff
= db_open_rbt(ctx
);
797 if (ctx
->diff
== NULL
) {
799 DEBUG(0, (_("No memory\n")));
803 ctx
->action
= check_actions_init(o
);
808 static bool check_open_db(struct check_ctx
* ctx
, const char* name
, int oflags
)
811 d_fprintf(stderr
, _("Error: name == NULL in check_open_db().\n"));
815 if (ctx
->db
!= NULL
) {
816 if ((ctx
->oflags
== oflags
) && (strcmp(ctx
->name
, name
))) {
819 TALLOC_FREE(ctx
->db
);
823 ctx
->db
= db_open(ctx
, name
, 0, TDB_DEFAULT
, oflags
, 0,
824 DBWRAP_LOCK_ORDER_1
, DBWRAP_FLAG_NONE
);
825 if (ctx
->db
== NULL
) {
827 _("Could not open idmap db (%s) for writing: %s\n"),
828 name
, strerror(errno
));
832 if (ctx
->name
!= name
) {
833 TALLOC_FREE(ctx
->name
);
834 ctx
->name
= talloc_strdup(ctx
, name
);
837 ctx
->oflags
= oflags
;
841 static bool check_do_checks(struct check_ctx
* ctx
)
845 if (!check_version(ctx
)) {
849 status
= dbwrap_traverse(ctx
->db
, traverse_check
, ctx
, NULL
);
851 if (!NT_STATUS_IS_OK(status
)) {
852 DEBUG(0, ("failed to traverse %s\n", ctx
->name
));
856 check_hwm(ctx
, "USER HWM", ctx
->uid_hwm
+ 1);
857 check_hwm(ctx
, "GROUP HWM", ctx
->gid_hwm
+ 1);
862 static void check_summary(const struct check_ctx
* ctx
)
864 d_printf("uid hwm: %d\ngid hwm: %d\n", ctx
->uid_hwm
, ctx
->gid_hwm
);
865 d_printf("mappings: %d\nother: %d\n", ctx
->n_map
, ctx
->n_other
);
866 d_printf("invalid records: %d\nmissing links: %d\ninvalid links: %d\n",
867 ctx
->n_invalid_record
, ctx
->n_missing_reverse
,
868 ctx
->n_invalid_mappping
);
869 d_printf("%u changes:\n", ctx
->n_diff
);
872 static bool check_transaction_start(struct check_ctx
* ctx
) {
873 return (dbwrap_transaction_start(ctx
->db
) == 0);
876 static bool check_transaction_commit(struct check_ctx
* ctx
) {
877 return (dbwrap_transaction_commit(ctx
->db
) == 0);
880 static bool check_transaction_cancel(struct check_ctx
* ctx
) {
881 return (dbwrap_transaction_cancel(ctx
->db
) == 0);
885 static void check_diff_list(struct check_ctx
* ctx
) {
886 NTSTATUS status
= dbwrap_traverse(ctx
->diff
, traverse_print_diff
, ctx
, NULL
);
888 if (!NT_STATUS_IS_OK(status
)) {
889 DEBUG(0, ("failed to traverse diff\n"));
894 static bool check_commit(struct check_ctx
* ctx
)
896 struct check_actions
* act
= &ctx
->action
;
898 NTSTATUS status
= NT_STATUS_OK
;
902 if (ctx
->n_diff
== 0) {
906 while ((action
= get_action(&act
->commit
, NULL
, NULL
)) == 'l') {
907 check_diff_list(ctx
);
912 assert(action
== 'c');
914 if (!check_open_db(ctx
, ctx
->name
, O_RDWR
)) {
918 if (!check_transaction_start(ctx
)) {
922 status
= dbwrap_traverse(ctx
->diff
, traverse_commit
, ctx
, NULL
);
924 if (!NT_STATUS_IS_OK(status
)) {
925 check_transaction_cancel(ctx
);
928 if (ctx
->opts
.test
) { /*get_action? */
929 return check_transaction_cancel(ctx
);
931 return check_transaction_commit(ctx
);
935 int net_idmap_check_db(const char* db
, const struct check_options
* o
)
938 TALLOC_CTX
* mem_ctx
= talloc_stackframe();
939 struct check_ctx
* ctx
= check_init(mem_ctx
, o
);
941 if (!o
->automatic
&& !isatty(STDIN_FILENO
)) {
942 DEBUG(0, ("Interactive use needs tty, use --auto\n"));
946 if (check_open_db(ctx
, db
, O_RDWR
)
947 && check_transaction_start(ctx
))
949 if ( check_do_checks(ctx
)
951 && check_transaction_commit(ctx
))
955 check_transaction_cancel(ctx
);
959 if (check_open_db(ctx
, db
, O_RDONLY
)
960 && check_do_checks(ctx
)
961 && check_commit(ctx
))
967 talloc_free(mem_ctx
);