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"
31 #include "../libcli/security/dom_sid.h"
36 static int traverse_commit(struct db_record *diff_rec, void* data);
37 static int traverse_check(struct db_record *rec, void* data);
39 static char* interact_edit(TALLOC_CTX* mem_ctx, const char* str);
40 static int interact_prompt(const char* msg, const char* accept, char def);
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 *********************************************************************/
54 DT_SID, DT_UID, DT_GID,
59 enum DT key_type, val_type;
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 {
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;
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",
112 .default_action = 'e',
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",
120 .default_action = 'f',
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",
129 .default_action = 'd',
132 .invalid_edit = (check_action) {
133 .name = "Invalid record",
134 .prompt = "[e]dit/[d]elete/[D]elete all"
135 "/[s]kip/[S]kip all",
137 .default_action = 'e',
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",
146 .default_action = 'o',
149 .no_version = (check_action) {
150 .prompt = "[f]ix/[s]kip/[a]bort",
152 .default_action = 'f',
154 .wrong_version = (check_action) {
155 .prompt = "[f]ix/[s]kip/[a]bort",
157 .default_action = 'a',
159 .invalid_hwm = (check_action) {
160 .prompt = "[f]ix/[s]kip",
162 .default_action = 'f',
164 .commit = (check_action) {
165 .prompt = "[c]ommit/[l]ist/[s]kip",
167 .default_action = 'l',
170 .valid_mapping = (check_action) {
171 .fmt = "%1$s: %2$s <-> %3$s\n",
174 .verbose = opts->verbose,
176 .valid_other = (check_action) {
179 .verbose = opts->verbose,
181 .invalid_diff = (check_action) {
182 .prompt = "[s]kip/[S]kip all/[c]ommit/[C]ommit all"
185 .default_action = 's',
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 */
209 ret.wrong_version.auto_action = 'f'; /* fix */
210 ret.invalid_diff.auto_action = 'c'; /* commit */
214 ret.invalid_diff.auto_action = 'c'; /* commit */
215 /* ret.commit.auto_action = 'c';*/ /* commit */
221 static char get_action(struct check_action* a, struct record* r, TDB_DATA* v) {
223 if (a->verbose && (r != NULL)) {
225 d_printf("%s: %s ", a->name, print_data(r, r->key));
227 d_printf("-> %s\n", print_data(r, r->val));
229 d_printf(": %ld\n", r->id);
232 d_printf(a->fmt, a->name,
233 print_data(r, r->key),
234 print_data(r, r->val),
235 (v ? print_data(r, *v) : ""));
239 if (a->auto_action != '\0') {
240 return a->auto_action;
243 ret = interact_prompt(a->prompt, a->answers, a->default_action);
247 a->auto_action = ret;
249 a->default_action = ret;
253 /* *************************************************************************/
259 static TDB_DATA pack_diff(TDB_DATA_diff* diff) {
261 .dptr = (uint8_t *)diff,
262 .dsize = sizeof(TDB_DATA_diff),
266 static TDB_DATA_diff unpack_diff(TDB_DATA data) {
267 assert(data.dsize == sizeof(TDB_DATA_diff));
268 return *(TDB_DATA_diff*)data.dptr;
271 #define DEBUG_DIFF(LEV,MEM,MSG,KEY,OLD,NEW) \
272 DEBUG(LEV, ("%s: %s\n", MSG, print_data(MEM, KEY))); \
273 if (!is_empty(OLD)) { \
274 DEBUGADD(LEV, ("-%s\n", print_data(MEM, OLD))); \
276 if (!is_empty(NEW)) { \
277 DEBUGADD(LEV, ("+%s\n", print_data(MEM, NEW))); \
284 struct db_context *db;
285 struct db_context *diff;
286 struct check_actions action;
291 unsigned n_invalid_record;
292 unsigned n_missing_reverse;
293 unsigned n_invalid_mappping;
297 struct check_options opts;
301 static void adjust_hwm(struct check_ctx* ctx, const struct record* r);
303 static int add_record(struct check_ctx* ctx, TDB_DATA key, TDB_DATA value)
307 TALLOC_CTX* mem = talloc_new(ctx->diff);
308 struct db_record* rec = ctx->diff->fetch_locked(ctx->diff, mem, key);
312 if (rec->value.dptr == 0) { /* first entry */
313 diff.oval = dbwrap_fetch(ctx->db, ctx->diff, key);
315 diff = unpack_diff(rec->value);
316 talloc_free(diff.nval.dptr);
318 diff.nval = talloc_copy(ctx->diff, value);
320 DEBUG_DIFF(2, mem, "TDB DIFF", key, diff.oval, diff.nval);
322 status = rec->store(rec, pack_diff(&diff), 0);
326 if (!NT_STATUS_IS_OK(status)) {
327 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
334 static int del_record(struct check_ctx* ctx, TDB_DATA key) {
335 return add_record(ctx, key, tdb_null);
339 fetch_record(struct check_ctx* ctx, TALLOC_CTX* mem_ctx, TDB_DATA key)
343 if (ctx->diff->fetch(ctx->diff, mem_ctx, key, &tmp) == -1) {
344 DEBUG(0, ("Out of memory!\n"));
347 if (tmp.dptr != NULL) {
348 TDB_DATA_diff diff = unpack_diff(tmp);
349 TDB_DATA ret = talloc_copy(mem_ctx, diff.nval);
350 talloc_free(tmp.dptr);
354 if (ctx->db->fetch(ctx->db, mem_ctx, key, &tmp) == -1) {
355 DEBUG(0, ("Out of memory!\n"));
362 static void edit_record(struct record* r) {
363 TALLOC_CTX* mem = talloc_new(r);
364 cbuf* ost = cbuf_new(mem);
369 cbuf_printf(ost, "%s %s\n",
370 print_data(mem, r->key), print_data(mem, r->val));
371 str = interact_edit(mem, cbuf_gets(ost, 0));
372 key = parse_data(mem, &str);
373 val = parse_data(mem, &str);
374 nr = parse_record(talloc_parent(r), key, val);
381 static bool check_version(struct check_ctx* ctx) {
382 static const char* key = "IDMAP_VERSION";
384 bool no_version = !dbwrap_fetch_uint32(ctx->db, key, &version);
386 struct check_actions* act = &ctx->action;
388 d_printf("No version number, assume 2\n");
389 action = get_action(&act->no_version, NULL, NULL);
390 } else if (version != 2) {
391 d_printf("Wrong version number %d, should be 2\n", version);
392 action = get_action(&act->wrong_version, NULL, NULL);
398 SIVAL(&version, 0, 2);
399 add_record(ctx, string_term_tdb_data(key),
400 make_tdb_data((uint8_t *)&version, sizeof(uint32_t)));
410 static void check_hwm(struct check_ctx* ctx, const char* key, uint32_t target) {
413 bool found = dbwrap_fetch_uint32(ctx->db, key, &hwm);
414 struct check_actions* act = &ctx->action;
416 d_printf("No %s should be %d\n", key, target);
417 action = get_action(&act->invalid_hwm, NULL, NULL);
418 } else if (target < hwm) {
419 d_printf("Invalid %s %d: should be %d\n", key, hwm, target);
420 action = get_action(&act->invalid_hwm, NULL, NULL);
423 SIVAL(&hwm, 0, target);
424 add_record(ctx, string_term_tdb_data(key),
425 make_tdb_data((uint8_t *)&hwm, sizeof(uint32_t)));
429 int traverse_check(struct db_record *rec, void* data) {
430 struct check_ctx* ctx = (struct check_ctx*)data;
431 struct check_actions* act = &ctx->action;
432 TALLOC_CTX* mem = talloc_new(ctx->diff);
433 struct record* r = parse_record(mem, rec->key, rec->value);
437 action = get_action(&act->invalid_record, r, NULL);
438 ctx->n_invalid_record++;
439 } else if (is_map(r)) {
440 TDB_DATA back = fetch_record(ctx, mem, r->val);
441 if (back.dptr == NULL) {
442 action = get_action(&act->missing_reverse, r, NULL);
443 ctx->n_missing_reverse++;
444 } else if (!tdb_data_equal(r->key, back)) {
445 action = get_action(&act->invalid_mapping, r, &back);
446 ctx->n_invalid_mappping++;
448 if (r->key_type == DT_SID) {
449 action = get_action(&act->valid_mapping, r, NULL);
452 action = get_action(&act->valid_mapping, NULL,
458 action = get_action(&act->valid_other, r, NULL);
466 case 'd': /* delete */
467 del_record(ctx, rec->key);
469 case 'f': /* add reverse mapping */
470 add_record(ctx, rec->value, rec->key);
476 action = get_action(&act->invalid_edit, r,NULL);
479 if (!tdb_data_equal(rec->key, r->key)) {
480 TDB_DATA oval = fetch_record(ctx, mem, r->key);
481 if (!is_empty(oval) &&
482 !tdb_data_equal(oval, r->val))
484 action = get_action(&act->record_exists,
492 TDB_DATA okey = fetch_record(ctx, mem, r->val);
493 if (!is_empty(okey) &&
494 !tdb_data_equal(okey, r->key))
496 action = get_action(&act->record_exists,
502 case 'o': /* overwrite */
504 if (!tdb_data_equal(rec->key, r->key)) {
505 del_record(ctx, rec->key);
507 add_record(ctx, r->key, r->val);
509 add_record(ctx, r->val, r->key);
520 /******************************************************************************/
522 void adjust_hwm(struct check_ctx* ctx, const struct record* r) {
523 enum DT type = (r->key_type == DT_SID) ? r->val_type : r->key_type;
524 if (type == DT_UID) {
525 ctx->uid_hwm = MAX(ctx->uid_hwm, r->id);
526 } else if (type == DT_GID) {
527 ctx->gid_hwm = MAX(ctx->gid_hwm, r->id);
531 TDB_DATA talloc_copy(TALLOC_CTX* mem_ctx, TDB_DATA data) {
533 .dptr = (uint8_t *)talloc_size(mem_ctx, data.dsize+1),
536 if (ret.dptr == NULL) {
539 memcpy(ret.dptr, data.dptr, data.dsize);
540 ret.dptr[ret.dsize] = '\0';
545 static bool is_cstr(TDB_DATA str) {
546 return !is_empty(str) && str.dptr[str.dsize-1] == '\0';
549 static bool parse_sid (TDB_DATA str, enum DT* type, struct dom_sid* sid) {
551 const char* s = (const char*)str.dptr;
552 if ((s[0] == 'S') && string_to_sid(&tmp, s)) {
560 static bool parse_xid(TDB_DATA str, enum DT* type, unsigned long* id) {
563 if (sscanf((const char*)str.dptr, "%cID %lu%c", &c, &tmp, &t) == 2) {
568 } else if (c == 'G') {
579 parse_record(TALLOC_CTX* mem_ctx, TDB_DATA key, TDB_DATA val)
581 struct record* ret = talloc_zero(mem_ctx, struct record);
583 DEBUG(0, ("Out of memory.\n"));
586 ret->key = talloc_copy(ret, key);
587 ret->val = talloc_copy(ret, val);
588 if ((ret->key.dptr == NULL && key.dptr != NULL) ||
589 (ret->val.dptr == NULL && val.dptr != NULL))
592 DEBUG(0, ("Out of memory.\n"));
595 assert((ret->key_type == DT_INV) && (ret->val_type == DT_INV));
600 if (parse_sid(key, &ret->key_type, &ret->sid)) {
601 parse_xid(val, &ret->val_type, &ret->id);
602 } else if (parse_xid(key, &ret->key_type, &ret->id)) {
604 parse_sid(val, &ret->val_type, &ret->sid);
606 } else if (strcmp((const char*)key.dptr, "USER HWM") == 0) {
607 ret->key_type = DT_HWM;
608 if (val.dsize == 4) {
609 ret->id = IVAL(val.dptr,0);
610 ret->val_type = DT_UID;
612 } else if (strcmp((const char*)key.dptr, "GROUP 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_GID;
618 } else if (strcmp((const char*)key.dptr, "IDMAP_VERSION") == 0) {
619 ret->key_type = DT_VER;
620 if (val.dsize == 4) {
621 ret->id = IVAL(val.dptr,0);
622 ret->val_type = DT_VER;
629 struct record* reverse_record(struct record* in)
631 return parse_record(talloc_parent(in), in->val, in->key);
635 /******************************************************************************/
637 int interact_prompt(const char* msg, const char* acc, char def) {
638 struct termios old_tio, new_tio;
641 tcgetattr(STDIN_FILENO, &old_tio);
643 new_tio.c_lflag &=(~ICANON & ~ECHO);
644 tcsetattr(STDIN_FILENO, TCSANOW, &new_tio);
647 d_printf("%s? [%c]\n", msg, def);
654 else if (strchr(acc, tolower(c)) != NULL) {
657 d_printf("Invalid input '%c'\n", c);
659 tcsetattr(STDIN_FILENO, TCSANOW, &old_tio);
663 char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d)
667 cbuf* ost = cbuf_new(mem_ctx);
668 int len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize);
670 cbuf_swapptr(ost, &ret, 0);
671 talloc_steal(mem_ctx, ret);
676 return talloc_strdup(mem_ctx, "<NULL>");
680 TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr) {
681 cbuf* ost = cbuf_new(mem_ctx);
682 TDB_DATA ret = tdb_null;
684 if (srprs_quoted(ptr, ost)) {
685 ret.dsize = cbuf_getpos(ost);
686 ret.dptr = (uint8_t *)talloc_steal(mem_ctx, cbuf_gets(ost,0));
692 static const char* get_editor(void) {
693 static const char* editor = NULL;
694 if (editor == NULL) {
695 editor = getenv("VISUAL");
696 if (editor == NULL) {
697 editor = getenv("EDITOR");
699 if (editor == NULL) {
706 char* interact_edit(TALLOC_CTX* mem_ctx, const char* str) {
707 char fname[] = "/tmp/net_idmap_check.XXXXXX";
712 int fd = mkstemp(fname);
714 DEBUG(0, ("failed to mkstemp %s: %s\n", fname,
719 file = fdopen(fd, "w");
721 DEBUG(0, ("failed to open %s for writing: %s\n", fname,
728 fprintf(file, "%s", str);
731 snprintf(buf, sizeof(buf), "%s %s\n", get_editor(), fname);
732 if (system(buf) != 0) {
733 DEBUG(0, ("failed to start editor %s: %s\n", buf,
739 file = fopen(fname, "r");
741 DEBUG(0, ("failed to open %s for reading: %s\n", fname,
746 while ( fgets(buf, sizeof(buf), file) ) {
747 ret = talloc_strdup_append(ret, buf);
752 return talloc_steal(mem_ctx, ret);
756 static int traverse_print_diff(struct db_record *rec, void* data) {
757 struct check_ctx* ctx = (struct check_ctx*)data;
758 TDB_DATA key = rec->key;
759 TDB_DATA_diff diff = unpack_diff(rec->value);
760 TALLOC_CTX* mem = talloc_new(ctx->diff);
762 DEBUG_DIFF(0, mem, "DIFF", key, diff.oval, diff.nval);
769 static int traverse_commit(struct db_record *diff_rec, void* data) {
770 struct check_ctx* ctx = (struct check_ctx*)data;
771 TDB_DATA_diff diff = unpack_diff(diff_rec->value);
772 TDB_DATA key = diff_rec->key;
773 TALLOC_CTX* mem = talloc_new(ctx->diff);
776 struct check_actions* act = &ctx->action;
778 struct db_record* rec = ctx->db->fetch_locked(ctx->db, mem, key);
783 if (!tdb_data_equal(rec->value, diff.oval)) {
786 d_printf("Warning: record has changed: %s\n"
787 "expected: %s got %s\n", print_data(mem, key),
788 print_data(mem, diff.oval),
789 print_data(mem, rec->value));
791 action = get_action(&act->invalid_diff, NULL, NULL);
795 } else if (action == 'a') {
800 DEBUG_DIFF(0, mem, "Commit", key, diff.oval, diff.nval);
802 if (is_empty(diff.nval)) {
803 status = rec->delete_rec(rec);
805 status = rec->store(rec, diff.nval, 0);
808 if (!NT_STATUS_IS_OK(status)) {
809 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
810 if (!ctx->opts.force) {
820 static struct check_ctx*
821 check_init(TALLOC_CTX* mem_ctx, const struct check_options* o)
823 struct check_ctx* ctx = talloc_zero(mem_ctx, struct check_ctx);
825 DEBUG(0, (_("No memory\n")));
829 ctx->diff = db_open_rbt(ctx);
830 if (ctx->diff == NULL) {
832 DEBUG(0, (_("No memory\n")));
836 ctx->action = check_actions_init(o);
841 static bool check_open_db(struct check_ctx* ctx, const char* name, int oflags)
844 d_fprintf(stderr, _("Error: name == NULL in check_open_db().\n"));
848 if (ctx->db != NULL) {
849 if ((ctx->oflags == oflags) && (strcmp(ctx->name, name))) {
852 TALLOC_FREE(ctx->db);
856 ctx->db = db_open(ctx, name, 0, TDB_DEFAULT, oflags, 0);
857 if (ctx->db == NULL) {
859 _("Could not open idmap db (%s) for writing: %s\n"),
860 name, strerror(errno));
864 if (ctx->name != name) {
865 TALLOC_FREE(ctx->name);
866 ctx->name = talloc_strdup(ctx, name);
869 ctx->oflags = oflags;
873 static bool check_do_checks(struct check_ctx* ctx)
877 if (!check_version(ctx)) {
881 status = dbwrap_traverse(ctx->db, traverse_check, ctx);
883 if (!NT_STATUS_IS_OK(status)) {
884 DEBUG(0, ("failed to traverse %s\n", ctx->name));
888 check_hwm(ctx, "USER HWM", ctx->uid_hwm + 1);
889 check_hwm(ctx, "GROUP HWM", ctx->gid_hwm + 1);
894 static void check_summary(const struct check_ctx* ctx)
896 d_printf("uid hwm: %d\ngid hwm: %d\n", ctx->uid_hwm, ctx->gid_hwm);
897 d_printf("mappings: %d\nother: %d\n", ctx->n_map, ctx->n_other);
898 d_printf("invalid records: %d\nmissing links: %d\ninvalid links: %d\n",
899 ctx->n_invalid_record, ctx->n_missing_reverse,
900 ctx->n_invalid_mappping);
901 d_printf("%u changes:\n", ctx->n_diff);
904 static bool check_transaction_start(struct check_ctx* ctx) {
905 return (ctx->db->transaction_start(ctx->db) == 0);
908 static bool check_transaction_commit(struct check_ctx* ctx) {
909 return (ctx->db->transaction_commit(ctx->db) == 0);
912 static bool check_transaction_cancel(struct check_ctx* ctx) {
913 return (ctx->db->transaction_cancel(ctx->db) == 0);
917 static void check_diff_list(struct check_ctx* ctx) {
918 NTSTATUS status = dbwrap_traverse(ctx->diff, traverse_print_diff, ctx);
920 if (!NT_STATUS_IS_OK(status)) {
921 DEBUG(0, ("failed to traverse diff\n"));
926 static bool check_commit(struct check_ctx* ctx)
928 struct check_actions* act = &ctx->action;
930 NTSTATUS status = NT_STATUS_OK;
934 if (ctx->n_diff == 0) {
938 while ((action = get_action(&act->commit, NULL, NULL)) == 'l') {
939 check_diff_list(ctx);
944 assert(action == 'c');
946 if (!check_open_db(ctx, ctx->name, O_RDWR)) {
950 if (!check_transaction_start(ctx)) {
954 status = dbwrap_traverse(ctx->diff, traverse_commit, ctx);
956 if (!NT_STATUS_IS_OK(status)) {
957 check_transaction_cancel(ctx);
960 if (ctx->opts.test) { //get_action?
961 return check_transaction_cancel(ctx);
963 return check_transaction_commit(ctx);
967 int net_idmap_check_db(const char* db, const struct check_options* o)
970 TALLOC_CTX* mem_ctx = talloc_stackframe();
971 struct check_ctx* ctx = check_init(mem_ctx, o);
973 if (!o->automatic && !isatty(STDIN_FILENO)) {
974 DEBUG(0, ("Interactive use needs tty, use --auto\n"));
978 if (check_open_db(ctx, db, O_RDWR)
979 && check_transaction_start(ctx))
981 if ( check_do_checks(ctx)
983 && check_transaction_commit(ctx))
987 check_transaction_cancel(ctx);
991 if (check_open_db(ctx, db, O_RDONLY)
992 && check_do_checks(ctx)
993 && check_commit(ctx))
999 talloc_free(mem_ctx);
1004 /*Local Variables:*/