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"
32 #include "../libcli/security/dom_sid.h"
38 static int traverse_commit(struct db_record
*diff_rec
, void* data
);
39 static int traverse_check(struct db_record
*rec
, void* data
);
41 static char* interact_edit(TALLOC_CTX
* mem_ctx
, const char* str
);
42 static int interact_prompt(const char* msg
, const char* accept
, char def
);
44 /* TDB_DATA *******************************************************************/
45 static char* print_data(TALLOC_CTX
* mem_ctx
, TDB_DATA d
);
46 static TDB_DATA
parse_data(TALLOC_CTX
* mem_ctx
, const char** ptr
);
47 static TDB_DATA
talloc_copy(TALLOC_CTX
* mem_ctx
, TDB_DATA data
);
48 static bool is_empty(TDB_DATA data
) {
49 return (data
.dsize
== 0) || (data
.dptr
== NULL
);
52 /* record *********************************************************************/
56 DT_SID
, DT_UID
, DT_GID
,
57 DT_HWM
, DT_VER
, DT_SEQ
,
61 enum DT key_type
, val_type
;
67 static struct record
* parse_record(TALLOC_CTX
* ctx
, TDB_DATA key
, TDB_DATA val
);
68 static struct record
* reverse_record(struct record
* rec
);
70 static bool is_invalid(const struct record
* r
) {
71 return (r
->key_type
== DT_INV
) || (r
->val_type
== DT_INV
);
74 static bool is_map(const struct record
* r
) {
75 return (r
->key_type
== DT_SID
)
76 || (r
->key_type
== DT_UID
) || (r
->key_type
== DT_GID
);
79 /* action *********************************************************************/
81 typedef struct check_action
{
91 struct check_actions
{
92 check_action invalid_record
;
93 check_action missing_reverse
;
94 check_action invalid_mapping
;
95 check_action invalid_edit
;
96 check_action record_exists
;
97 check_action no_version
;
98 check_action wrong_version
;
99 check_action invalid_hwm
;
101 check_action valid_mapping
;
102 check_action valid_other
;
103 check_action invalid_diff
;
106 static struct check_actions
107 check_actions_init(const struct check_options
* opts
) {
108 struct check_actions ret
= {
109 .invalid_record
= (check_action
) {
110 .name
= "Invalid record",
111 .prompt
= "[e]dit/[d]elete/[D]elete all"
112 "/[s]kip/[S]kip all",
114 .default_action
= 'e',
117 .missing_reverse
= (check_action
) {
118 .name
= "Missing reverse mapping for",
119 .prompt
= "[f]ix/[F]ix all/[e]dit/[d]elete/[D]elete all"
120 "/[s]kip/[S]kip all",
122 .default_action
= 'f',
125 .invalid_mapping
= (check_action
) {
126 .fmt
= "%1$s: %2$s -> %3$s\n(%4$s <- %3$s)\n",
127 .name
= "Invalid mapping",
128 .prompt
= "[e]dit/[d]elete/[D]elete all"
129 "/[s]kip/[S]kip all",
131 .default_action
= 'd',
134 .invalid_edit
= (check_action
) {
135 .name
= "Invalid record",
136 .prompt
= "[e]dit/[d]elete/[D]elete all"
137 "/[s]kip/[S]kip all",
139 .default_action
= 'e',
142 .record_exists
= (check_action
) {
143 .fmt
= "%1$s: %2$s\n-%4$s\n+%3$s\n",
144 .name
= "Record exists",
145 .prompt
= "[o]verwrite/[O]verwrite all/[e]dit"
146 "/[d]elete/[D]elete all/[s]kip/[S]kip all",
148 .default_action
= 'o',
151 .no_version
= (check_action
) {
152 .prompt
= "[f]ix/[s]kip/[a]bort",
154 .default_action
= 'f',
156 .wrong_version
= (check_action
) {
157 .prompt
= "[f]ix/[s]kip/[a]bort",
159 .default_action
= 'a',
161 .invalid_hwm
= (check_action
) {
162 .prompt
= "[f]ix/[s]kip",
164 .default_action
= 'f',
166 .commit
= (check_action
) {
167 .prompt
= "[c]ommit/[l]ist/[s]kip",
169 .default_action
= 'l',
172 .valid_mapping
= (check_action
) {
173 .fmt
= "%1$s: %2$s <-> %3$s\n",
176 .verbose
= opts
->verbose
,
178 .valid_other
= (check_action
) {
181 .verbose
= opts
->verbose
,
183 .invalid_diff
= (check_action
) {
184 .prompt
= "[s]kip/[S]kip all/[c]ommit/[C]ommit all"
187 .default_action
= 's',
192 ret
.invalid_record
.auto_action
= 's';
193 ret
.missing_reverse
.auto_action
= 's';
194 ret
.invalid_mapping
.auto_action
= 's';
195 ret
.no_version
.auto_action
= 's';
196 ret
.wrong_version
.auto_action
= 's';
197 ret
.invalid_hwm
.auto_action
= 's';
198 ret
.commit
.auto_action
= 's';
201 if (opts
->automatic
) {
202 ret
.invalid_record
.auto_action
= 'd'; /* delete */
203 ret
.missing_reverse
.auto_action
= 'f'; /* fix */
204 ret
.invalid_mapping
.auto_action
= 'd'; /* delete */
205 ret
.no_version
.auto_action
= 'f'; /* fix */
206 ret
.wrong_version
.auto_action
= 'a'; /* abort */
207 ret
.invalid_hwm
.auto_action
= 'f'; /* fix */
208 ret
.commit
.auto_action
= 'c'; /* commit */
209 ret
.invalid_diff
.auto_action
= 'a'; /* abort */
211 ret
.wrong_version
.auto_action
= 'f'; /* fix */
212 ret
.invalid_diff
.auto_action
= 'c'; /* commit */
216 ret
.invalid_diff
.auto_action
= 'c'; /* commit */
217 /* ret.commit.auto_action = 'c';*/ /* commit */
223 static char get_action(struct check_action
* a
, struct record
* r
, TDB_DATA
* v
) {
225 if (a
->verbose
&& (r
!= NULL
)) {
227 d_printf("%s: %s ", a
->name
, print_data(r
, r
->key
));
229 d_printf("-> %s\n", print_data(r
, r
->val
));
230 } else if (r
->key_type
== DT_HWM
||
231 r
->key_type
== DT_VER
||
232 r
->key_type
== DT_SEQ
)
234 d_printf(": %ld\n", r
->id
);
239 d_printf(a
->fmt
, a
->name
,
240 print_data(r
, r
->key
),
241 print_data(r
, r
->val
),
242 (v
? print_data(r
, *v
) : ""));
246 if (a
->auto_action
!= '\0') {
247 return a
->auto_action
;
250 ret
= interact_prompt(a
->prompt
, a
->answers
, a
->default_action
);
254 a
->auto_action
= ret
;
256 a
->default_action
= ret
;
260 /* *************************************************************************/
266 static TDB_DATA
pack_diff(TDB_DATA_diff
* diff
) {
268 .dptr
= (uint8_t *)diff
,
269 .dsize
= sizeof(TDB_DATA_diff
),
273 static TDB_DATA_diff
unpack_diff(TDB_DATA data
) {
274 assert(data
.dsize
== sizeof(TDB_DATA_diff
));
275 return *(TDB_DATA_diff
*)data
.dptr
;
278 #define DEBUG_DIFF(LEV,MEM,MSG,KEY,OLD,NEW) \
279 DEBUG(LEV, ("%s: %s\n", MSG, print_data(MEM, KEY))); \
280 if (!is_empty(OLD)) { \
281 DEBUGADD(LEV, ("-%s\n", print_data(MEM, OLD))); \
283 if (!is_empty(NEW)) { \
284 DEBUGADD(LEV, ("+%s\n", print_data(MEM, NEW))); \
291 struct db_context
*db
;
292 struct db_context
*diff
;
293 struct check_actions action
;
298 unsigned n_invalid_record
;
299 unsigned n_missing_reverse
;
300 unsigned n_invalid_mappping
;
304 struct check_options opts
;
308 static void adjust_hwm(struct check_ctx
* ctx
, const struct record
* r
);
310 static int add_record(struct check_ctx
* ctx
, TDB_DATA key
, TDB_DATA value
)
314 TALLOC_CTX
* mem
= talloc_new(ctx
->diff
);
315 struct db_record
* rec
= ctx
->diff
->fetch_locked(ctx
->diff
, mem
, key
);
319 if (rec
->value
.dptr
== 0) { /* first entry */
320 diff
.oval
= dbwrap_fetch(ctx
->db
, ctx
->diff
, key
);
322 diff
= unpack_diff(rec
->value
);
323 talloc_free(diff
.nval
.dptr
);
325 diff
.nval
= talloc_copy(ctx
->diff
, value
);
327 DEBUG_DIFF(2, mem
, "TDB DIFF", key
, diff
.oval
, diff
.nval
);
329 status
= rec
->store(rec
, pack_diff(&diff
), 0);
333 if (!NT_STATUS_IS_OK(status
)) {
334 DEBUG(0, ("could not store record %s\n", nt_errstr(status
)));
341 static int del_record(struct check_ctx
* ctx
, TDB_DATA key
) {
342 return add_record(ctx
, key
, tdb_null
);
346 fetch_record(struct check_ctx
* ctx
, TALLOC_CTX
* mem_ctx
, TDB_DATA key
)
350 if (ctx
->diff
->fetch(ctx
->diff
, mem_ctx
, key
, &tmp
) != 0) {
351 DEBUG(0, ("Out of memory!\n"));
354 if (tmp
.dptr
!= NULL
) {
355 TDB_DATA_diff diff
= unpack_diff(tmp
);
356 TDB_DATA ret
= talloc_copy(mem_ctx
, diff
.nval
);
357 talloc_free(tmp
.dptr
);
361 if (ctx
->db
->fetch(ctx
->db
, mem_ctx
, key
, &tmp
) != 0) {
362 DEBUG(0, ("Out of memory!\n"));
369 static void edit_record(struct record
* r
) {
370 TALLOC_CTX
* mem
= talloc_new(r
);
371 cbuf
* ost
= cbuf_new(mem
);
376 cbuf_printf(ost
, "%s %s\n",
377 print_data(mem
, r
->key
), print_data(mem
, r
->val
));
378 str
= interact_edit(mem
, cbuf_gets(ost
, 0));
379 key
= parse_data(mem
, &str
);
380 val
= parse_data(mem
, &str
);
381 nr
= parse_record(talloc_parent(r
), key
, val
);
388 static bool check_version(struct check_ctx
* ctx
) {
389 static const char* key
= "IDMAP_VERSION";
391 bool no_version
= !dbwrap_fetch_uint32(ctx
->db
, key
, &version
);
393 struct check_actions
* act
= &ctx
->action
;
395 d_printf("No version number, assume 2\n");
396 action
= get_action(&act
->no_version
, NULL
, NULL
);
397 } else if (version
!= 2) {
398 d_printf("Wrong version number %d, should be 2\n", version
);
399 action
= get_action(&act
->wrong_version
, NULL
, NULL
);
405 SIVAL(&version
, 0, 2);
406 add_record(ctx
, string_term_tdb_data(key
),
407 make_tdb_data((uint8_t *)&version
, sizeof(uint32_t)));
417 static void check_hwm(struct check_ctx
* ctx
, const char* key
, uint32_t target
) {
420 bool found
= dbwrap_fetch_uint32(ctx
->db
, key
, &hwm
);
421 struct check_actions
* act
= &ctx
->action
;
423 d_printf("No %s should be %d\n", key
, target
);
424 action
= get_action(&act
->invalid_hwm
, NULL
, NULL
);
425 } else if (target
< hwm
) {
426 d_printf("Invalid %s %d: should be %d\n", key
, hwm
, target
);
427 action
= get_action(&act
->invalid_hwm
, NULL
, NULL
);
430 SIVAL(&hwm
, 0, target
);
431 add_record(ctx
, string_term_tdb_data(key
),
432 make_tdb_data((uint8_t *)&hwm
, sizeof(uint32_t)));
436 int traverse_check(struct db_record
*rec
, void* data
) {
437 struct check_ctx
* ctx
= (struct check_ctx
*)data
;
438 struct check_actions
* act
= &ctx
->action
;
439 TALLOC_CTX
* mem
= talloc_new(ctx
->diff
);
440 struct record
* r
= parse_record(mem
, rec
->key
, rec
->value
);
444 action
= get_action(&act
->invalid_record
, r
, NULL
);
445 ctx
->n_invalid_record
++;
446 } else if (is_map(r
)) {
447 TDB_DATA back
= fetch_record(ctx
, mem
, r
->val
);
448 if (back
.dptr
== NULL
) {
449 action
= get_action(&act
->missing_reverse
, r
, NULL
);
450 ctx
->n_missing_reverse
++;
451 } else if (!tdb_data_equal(r
->key
, back
)) {
452 action
= get_action(&act
->invalid_mapping
, r
, &back
);
453 ctx
->n_invalid_mappping
++;
455 if (r
->key_type
== DT_SID
) {
456 action
= get_action(&act
->valid_mapping
, r
, NULL
);
459 action
= get_action(&act
->valid_mapping
, NULL
,
465 action
= get_action(&act
->valid_other
, r
, NULL
);
473 case 'd': /* delete */
474 del_record(ctx
, rec
->key
);
476 case 'f': /* add reverse mapping */
477 add_record(ctx
, rec
->value
, rec
->key
);
483 action
= get_action(&act
->invalid_edit
, r
,NULL
);
486 if (!tdb_data_equal(rec
->key
, r
->key
)) {
487 TDB_DATA oval
= fetch_record(ctx
, mem
, r
->key
);
488 if (!is_empty(oval
) &&
489 !tdb_data_equal(oval
, r
->val
))
491 action
= get_action(&act
->record_exists
,
499 TDB_DATA okey
= fetch_record(ctx
, mem
, r
->val
);
500 if (!is_empty(okey
) &&
501 !tdb_data_equal(okey
, r
->key
))
503 action
= get_action(&act
->record_exists
,
509 case 'o': /* overwrite */
511 if (!tdb_data_equal(rec
->key
, r
->key
)) {
512 del_record(ctx
, rec
->key
);
514 add_record(ctx
, r
->key
, r
->val
);
516 add_record(ctx
, r
->val
, r
->key
);
527 /******************************************************************************/
529 void adjust_hwm(struct check_ctx
* ctx
, const struct record
* r
) {
530 enum DT type
= (r
->key_type
== DT_SID
) ? r
->val_type
: r
->key_type
;
531 if (type
== DT_UID
) {
532 ctx
->uid_hwm
= MAX(ctx
->uid_hwm
, r
->id
);
533 } else if (type
== DT_GID
) {
534 ctx
->gid_hwm
= MAX(ctx
->gid_hwm
, r
->id
);
538 TDB_DATA
talloc_copy(TALLOC_CTX
* mem_ctx
, TDB_DATA data
) {
540 .dptr
= (uint8_t *)talloc_size(mem_ctx
, data
.dsize
+1),
543 if (ret
.dptr
== NULL
) {
546 memcpy(ret
.dptr
, data
.dptr
, data
.dsize
);
547 ret
.dptr
[ret
.dsize
] = '\0';
552 static bool is_cstr(TDB_DATA str
) {
553 return !is_empty(str
) && str
.dptr
[str
.dsize
-1] == '\0';
556 static bool parse_sid (TDB_DATA str
, enum DT
* type
, struct dom_sid
* sid
) {
558 const char* s
= (const char*)str
.dptr
;
559 if ((s
[0] == 'S') && string_to_sid(&tmp
, s
)) {
567 static bool parse_xid(TDB_DATA str
, enum DT
* type
, unsigned long* id
) {
570 if (sscanf((const char*)str
.dptr
, "%cID %lu%c", &c
, &tmp
, &t
) == 2) {
575 } else if (c
== 'G') {
586 parse_record(TALLOC_CTX
* mem_ctx
, TDB_DATA key
, TDB_DATA val
)
588 struct record
* ret
= talloc_zero(mem_ctx
, struct record
);
590 DEBUG(0, ("Out of memory.\n"));
593 ret
->key
= talloc_copy(ret
, key
);
594 ret
->val
= talloc_copy(ret
, val
);
595 if ((ret
->key
.dptr
== NULL
&& key
.dptr
!= NULL
) ||
596 (ret
->val
.dptr
== NULL
&& val
.dptr
!= NULL
))
599 DEBUG(0, ("Out of memory.\n"));
602 assert((ret
->key_type
== DT_INV
) && (ret
->val_type
== DT_INV
));
607 if (parse_sid(key
, &ret
->key_type
, &ret
->sid
)) {
608 parse_xid(val
, &ret
->val_type
, &ret
->id
);
609 } else if (parse_xid(key
, &ret
->key_type
, &ret
->id
)) {
611 parse_sid(val
, &ret
->val_type
, &ret
->sid
);
613 } else if (strcmp((const char*)key
.dptr
, "USER HWM") == 0) {
614 ret
->key_type
= DT_HWM
;
615 if (val
.dsize
== 4) {
616 ret
->id
= IVAL(val
.dptr
,0);
617 ret
->val_type
= DT_UID
;
619 } else if (strcmp((const char*)key
.dptr
, "GROUP HWM") == 0) {
620 ret
->key_type
= DT_HWM
;
621 if (val
.dsize
== 4) {
622 ret
->id
= IVAL(val
.dptr
,0);
623 ret
->val_type
= DT_GID
;
625 } else if (strcmp((const char*)key
.dptr
, "IDMAP_VERSION") == 0) {
626 ret
->key_type
= DT_VER
;
627 if (val
.dsize
== 4) {
628 ret
->id
= IVAL(val
.dptr
,0);
629 ret
->val_type
= DT_VER
;
631 } else if (strcmp((const char*)key
.dptr
, "__db_sequence_number__") == 0) {
632 ret
->key_type
= DT_SEQ
;
633 if (val
.dsize
== 8) {
634 ret
->id
= *(uint64_t*)val
.dptr
;
635 ret
->val_type
= DT_SEQ
;
642 struct record
* reverse_record(struct record
* in
)
644 return parse_record(talloc_parent(in
), in
->val
, in
->key
);
648 /******************************************************************************/
650 int interact_prompt(const char* msg
, const char* acc
, char def
) {
651 struct termios old_tio
, new_tio
;
654 tcgetattr(STDIN_FILENO
, &old_tio
);
656 new_tio
.c_lflag
&=(~ICANON
& ~ECHO
);
657 tcsetattr(STDIN_FILENO
, TCSANOW
, &new_tio
);
660 d_printf("%s? [%c]\n", msg
, def
);
667 else if (strchr(acc
, tolower(c
)) != NULL
) {
670 d_printf("Invalid input '%c'\n", c
);
672 tcsetattr(STDIN_FILENO
, TCSANOW
, &old_tio
);
676 char* print_data(TALLOC_CTX
* mem_ctx
, TDB_DATA d
)
680 cbuf
* ost
= cbuf_new(mem_ctx
);
681 int len
= cbuf_print_quoted(ost
, (const char*)d
.dptr
, d
.dsize
);
683 cbuf_swapptr(ost
, &ret
, 0);
684 talloc_steal(mem_ctx
, ret
);
689 return talloc_strdup(mem_ctx
, "<NULL>");
693 TDB_DATA
parse_data(TALLOC_CTX
* mem_ctx
, const char** ptr
) {
694 cbuf
* ost
= cbuf_new(mem_ctx
);
695 TDB_DATA ret
= tdb_null
;
697 if (srprs_quoted(ptr
, ost
)) {
698 ret
.dsize
= cbuf_getpos(ost
);
699 ret
.dptr
= (uint8_t *)talloc_steal(mem_ctx
, cbuf_gets(ost
,0));
705 static const char* get_editor(void) {
706 static const char* editor
= NULL
;
707 if (editor
== NULL
) {
708 editor
= getenv("VISUAL");
709 if (editor
== NULL
) {
710 editor
= getenv("EDITOR");
712 if (editor
== NULL
) {
719 char* interact_edit(TALLOC_CTX
* mem_ctx
, const char* str
) {
720 char fname
[] = "/tmp/net_idmap_check.XXXXXX";
725 int fd
= mkstemp(fname
);
727 DEBUG(0, ("failed to mkstemp %s: %s\n", fname
,
732 file
= fdopen(fd
, "w");
734 DEBUG(0, ("failed to open %s for writing: %s\n", fname
,
741 fprintf(file
, "%s", str
);
744 snprintf(buf
, sizeof(buf
), "%s %s\n", get_editor(), fname
);
745 if (system(buf
) != 0) {
746 DEBUG(0, ("failed to start editor %s: %s\n", buf
,
752 file
= fopen(fname
, "r");
754 DEBUG(0, ("failed to open %s for reading: %s\n", fname
,
759 while ( fgets(buf
, sizeof(buf
), file
) ) {
760 ret
= talloc_strdup_append(ret
, buf
);
765 return talloc_steal(mem_ctx
, ret
);
769 static int traverse_print_diff(struct db_record
*rec
, void* data
) {
770 struct check_ctx
* ctx
= (struct check_ctx
*)data
;
771 TDB_DATA key
= rec
->key
;
772 TDB_DATA_diff diff
= unpack_diff(rec
->value
);
773 TALLOC_CTX
* mem
= talloc_new(ctx
->diff
);
775 DEBUG_DIFF(0, mem
, "DIFF", key
, diff
.oval
, diff
.nval
);
782 static int traverse_commit(struct db_record
*diff_rec
, void* data
) {
783 struct check_ctx
* ctx
= (struct check_ctx
*)data
;
784 TDB_DATA_diff diff
= unpack_diff(diff_rec
->value
);
785 TDB_DATA key
= diff_rec
->key
;
786 TALLOC_CTX
* mem
= talloc_new(ctx
->diff
);
789 struct check_actions
* act
= &ctx
->action
;
791 struct db_record
* rec
= ctx
->db
->fetch_locked(ctx
->db
, mem
, key
);
796 if (!tdb_data_equal(rec
->value
, diff
.oval
)) {
799 d_printf("Warning: record has changed: %s\n"
800 "expected: %s got %s\n", print_data(mem
, key
),
801 print_data(mem
, diff
.oval
),
802 print_data(mem
, rec
->value
));
804 action
= get_action(&act
->invalid_diff
, NULL
, NULL
);
808 } else if (action
== 'a') {
813 DEBUG_DIFF(0, mem
, "Commit", key
, diff
.oval
, diff
.nval
);
815 if (is_empty(diff
.nval
)) {
816 status
= rec
->delete_rec(rec
);
818 status
= rec
->store(rec
, diff
.nval
, 0);
821 if (!NT_STATUS_IS_OK(status
)) {
822 DEBUG(0, ("could not store record %s\n", nt_errstr(status
)));
823 if (!ctx
->opts
.force
) {
833 static struct check_ctx
*
834 check_init(TALLOC_CTX
* mem_ctx
, const struct check_options
* o
)
836 struct check_ctx
* ctx
= talloc_zero(mem_ctx
, struct check_ctx
);
838 DEBUG(0, (_("No memory\n")));
842 ctx
->diff
= db_open_rbt(ctx
);
843 if (ctx
->diff
== NULL
) {
845 DEBUG(0, (_("No memory\n")));
849 ctx
->action
= check_actions_init(o
);
854 static bool check_open_db(struct check_ctx
* ctx
, const char* name
, int oflags
)
857 d_fprintf(stderr
, _("Error: name == NULL in check_open_db().\n"));
861 if (ctx
->db
!= NULL
) {
862 if ((ctx
->oflags
== oflags
) && (strcmp(ctx
->name
, name
))) {
865 TALLOC_FREE(ctx
->db
);
869 ctx
->db
= db_open(ctx
, name
, 0, TDB_DEFAULT
, oflags
, 0);
870 if (ctx
->db
== NULL
) {
872 _("Could not open idmap db (%s) for writing: %s\n"),
873 name
, strerror(errno
));
877 if (ctx
->name
!= name
) {
878 TALLOC_FREE(ctx
->name
);
879 ctx
->name
= talloc_strdup(ctx
, name
);
882 ctx
->oflags
= oflags
;
886 static bool check_do_checks(struct check_ctx
* ctx
)
890 if (!check_version(ctx
)) {
894 status
= dbwrap_traverse(ctx
->db
, traverse_check
, ctx
);
896 if (!NT_STATUS_IS_OK(status
)) {
897 DEBUG(0, ("failed to traverse %s\n", ctx
->name
));
901 check_hwm(ctx
, "USER HWM", ctx
->uid_hwm
+ 1);
902 check_hwm(ctx
, "GROUP HWM", ctx
->gid_hwm
+ 1);
907 static void check_summary(const struct check_ctx
* ctx
)
909 d_printf("uid hwm: %d\ngid hwm: %d\n", ctx
->uid_hwm
, ctx
->gid_hwm
);
910 d_printf("mappings: %d\nother: %d\n", ctx
->n_map
, ctx
->n_other
);
911 d_printf("invalid records: %d\nmissing links: %d\ninvalid links: %d\n",
912 ctx
->n_invalid_record
, ctx
->n_missing_reverse
,
913 ctx
->n_invalid_mappping
);
914 d_printf("%u changes:\n", ctx
->n_diff
);
917 static bool check_transaction_start(struct check_ctx
* ctx
) {
918 return (ctx
->db
->transaction_start(ctx
->db
) == 0);
921 static bool check_transaction_commit(struct check_ctx
* ctx
) {
922 return (ctx
->db
->transaction_commit(ctx
->db
) == 0);
925 static bool check_transaction_cancel(struct check_ctx
* ctx
) {
926 return (ctx
->db
->transaction_cancel(ctx
->db
) == 0);
930 static void check_diff_list(struct check_ctx
* ctx
) {
931 NTSTATUS status
= dbwrap_traverse(ctx
->diff
, traverse_print_diff
, ctx
);
933 if (!NT_STATUS_IS_OK(status
)) {
934 DEBUG(0, ("failed to traverse diff\n"));
939 static bool check_commit(struct check_ctx
* ctx
)
941 struct check_actions
* act
= &ctx
->action
;
943 NTSTATUS status
= NT_STATUS_OK
;
947 if (ctx
->n_diff
== 0) {
951 while ((action
= get_action(&act
->commit
, NULL
, NULL
)) == 'l') {
952 check_diff_list(ctx
);
957 assert(action
== 'c');
959 if (!check_open_db(ctx
, ctx
->name
, O_RDWR
)) {
963 if (!check_transaction_start(ctx
)) {
967 status
= dbwrap_traverse(ctx
->diff
, traverse_commit
, ctx
);
969 if (!NT_STATUS_IS_OK(status
)) {
970 check_transaction_cancel(ctx
);
973 if (ctx
->opts
.test
) { /*get_action? */
974 return check_transaction_cancel(ctx
);
976 return check_transaction_commit(ctx
);
980 int net_idmap_check_db(const char* db
, const struct check_options
* o
)
983 TALLOC_CTX
* mem_ctx
= talloc_stackframe();
984 struct check_ctx
* ctx
= check_init(mem_ctx
, o
);
986 if (!o
->automatic
&& !isatty(STDIN_FILENO
)) {
987 DEBUG(0, ("Interactive use needs tty, use --auto\n"));
991 if (check_open_db(ctx
, db
, O_RDWR
)
992 && check_transaction_start(ctx
))
994 if ( check_do_checks(ctx
)
996 && check_transaction_commit(ctx
))
1000 check_transaction_cancel(ctx
);
1004 if (check_open_db(ctx
, db
, O_RDONLY
)
1005 && check_do_checks(ctx
)
1006 && check_commit(ctx
))
1012 talloc_free(mem_ctx
);
1017 /*Local Variables:*/