source: vendor/current/source3/utils/net_idmap_check.c

Last change on this file was 988, checked in by Silvan Scherrer, 8 years ago

Samba Server: update vendor to version 4.4.3

File size: 23.1 KB
Line 
1/*
2 * Samba Unix/Linux SMB client library
3 *
4 * Copyright (C) Gregor Beck 2011
5 *
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.
10 *
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.
15 *
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/>.
18 */
19
20/**
21 * @brief Check the idmap database.
22 * @author Gregor Beck <gb@sernet.de>
23 * @date Mar 2011
24 */
25
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"
38
39static int traverse_commit(struct db_record *diff_rec, void* data);
40static int traverse_check(struct db_record *rec, void* data);
41
42/* TDB_DATA *******************************************************************/
43static char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d);
44static TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr);
45
46/* record *********************************************************************/
47
48enum DT {
49 DT_INV = 0,
50 DT_SID, DT_UID, DT_GID,
51 DT_HWM, DT_VER, DT_SEQ,
52};
53
54struct record {
55 enum DT key_type, val_type;
56 TDB_DATA key, val;
57 struct dom_sid sid;
58 long unsigned id;
59};
60
61static struct record* parse_record(TALLOC_CTX* ctx, TDB_DATA key, TDB_DATA val);
62static struct record* reverse_record(struct record* rec);
63
64static bool is_invalid(const struct record* r) {
65 return (r->key_type == DT_INV) || (r->val_type == DT_INV);
66}
67
68static 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);
71}
72
73/* action *********************************************************************/
74
75typedef struct check_action {
76 void (*fmt)(struct check_action *a,
77 struct record *r,
78 TDB_DATA *v);
79 const char* name;
80 const char* prompt;
81 const char* answers;
82 char auto_action;
83 char default_action;
84 bool verbose;
85} check_action;
86
87struct 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;
96 check_action commit;
97 check_action valid_mapping;
98 check_action valid_other;
99 check_action invalid_diff;
100};
101
102static void invalid_mapping_fmt(struct check_action *a,
103 struct record *r,
104 TDB_DATA *v)
105{
106 d_printf("%1$s: %2$s -> %3$s\n(%4$s <- %3$s)\n",
107 a->name,
108 print_data(r, r->key),
109 print_data(r, r->val),
110 (v ? print_data(r, *v) : ""));
111}
112
113static void record_exists_fmt(struct check_action *a,
114 struct record *r,
115 TDB_DATA *v)
116{
117 d_printf("%1$s: %2$s\n-%4$s\n+%3$s\n",
118 a->name,
119 print_data(r, r->key),
120 print_data(r, r->val),
121 (v ? print_data(r, *v) : ""));
122}
123
124static void valid_mapping_fmt(struct check_action *a,
125 struct record *r,
126 TDB_DATA *v)
127{
128 d_printf("%1$s: %2$s <-> %3$s\n",
129 a->name,
130 print_data(r, r->key),
131 print_data(r, r->val));
132}
133
134static struct check_actions
135check_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",
141 .answers = "eds",
142 .default_action = 'e',
143 .verbose = true,
144 },
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",
149 .answers = "feds",
150 .default_action = 'f',
151 .verbose = true,
152 },
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",
158 .answers = "eds",
159 .default_action = 'd',
160 .verbose = true,
161 },
162 .invalid_edit = (check_action) {
163 .name = "Invalid record",
164 .prompt = "[e]dit/[d]elete/[D]elete all"
165 "/[s]kip/[S]kip all",
166 .answers = "eds",
167 .default_action = 'e',
168 .verbose = true,
169 },
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",
175 .answers = "oeds",
176 .default_action = 'o',
177 .verbose = true,
178 },
179 .no_version = (check_action) {
180 .prompt = "[f]ix/[s]kip/[a]bort",
181 .answers = "fsa",
182 .default_action = 'f',
183 },
184 .wrong_version = (check_action) {
185 .prompt = "[f]ix/[s]kip/[a]bort",
186 .answers = "fsa",
187 .default_action = 'a',
188 },
189 .invalid_hwm = (check_action) {
190 .prompt = "[f]ix/[s]kip",
191 .answers = "fs",
192 .default_action = 'f',
193 },
194 .commit = (check_action) {
195 .prompt = "[c]ommit/[l]ist/[s]kip",
196 .answers = "cls",
197 .default_action = 'l',
198 .verbose = true,
199 },
200 .valid_mapping = (check_action) {
201 .fmt = valid_mapping_fmt,
202 .name = "Mapping",
203 .auto_action = 's',
204 .verbose = opts->verbose,
205 },
206 .valid_other = (check_action) {
207 .name = "Other",
208 .auto_action = 's',
209 .verbose = opts->verbose,
210 },
211 .invalid_diff = (check_action) {
212 .prompt = "[s]kip/[S]kip all/[c]ommit/[C]ommit all"
213 "/[a]bort",
214 .answers = "sca",
215 .default_action = 's',
216 },
217 };
218
219 if (!opts->repair) {
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';
227 }
228
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 */
238 if (opts->force) {
239 ret.wrong_version.auto_action = 'f'; /* fix */
240 ret.invalid_diff.auto_action = 'c'; /* commit */
241 }
242 }
243 if (opts->test) {
244 ret.invalid_diff.auto_action = 'c'; /* commit */
245/* ret.commit.auto_action = 'c';*/ /* commit */
246 }
247
248 return ret;
249}
250
251static char get_action(struct check_action* a, struct record* r, TDB_DATA* v) {
252 char ret;
253 if (a->verbose && (r != NULL)) {
254 if (!a->fmt) {
255 d_printf("%s: %s ", a->name, print_data(r, r->key));
256 if (is_map(r)) {
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)
261 {
262 d_printf(": %ld\n", r->id);
263 } else {
264 d_printf("\n");
265 }
266 } else {
267 a->fmt(a, r, v);
268 }
269 }
270
271 if (a->auto_action != '\0') {
272 return a->auto_action;
273 }
274
275 ret = interact_prompt(a->prompt, a->answers, a->default_action);
276
277 if (isupper(ret)) {
278 ret = tolower(ret);
279 a->auto_action = ret;
280 }
281 a->default_action = ret;
282 return ret;
283}
284
285/* *************************************************************************/
286
287typedef struct {
288 TDB_DATA oval, nval;
289} TDB_DATA_diff;
290
291static TDB_DATA pack_diff(TDB_DATA_diff* diff) {
292 return (TDB_DATA) {
293 .dptr = (uint8_t *)diff,
294 .dsize = sizeof(TDB_DATA_diff),
295 };
296}
297
298static TDB_DATA_diff unpack_diff(TDB_DATA data) {
299 assert(data.dsize == sizeof(TDB_DATA_diff));
300 return *(TDB_DATA_diff*)data.dptr;
301}
302
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))); \
307 } \
308 if (!tdb_data_is_empty(NEW)) { \
309 DEBUGADD(LEV, ("+%s\n", print_data(MEM, NEW))); \
310 }
311
312struct check_ctx {
313 int oflags;
314 char* name;
315 bool transaction;
316 struct db_context *db;
317 struct db_context *diff;
318 struct check_actions action;
319
320 uint32_t uid_hwm;
321 uint32_t gid_hwm;
322
323 unsigned n_invalid_record;
324 unsigned n_missing_reverse;
325 unsigned n_invalid_mappping;
326 unsigned n_map;
327 unsigned n_other;
328 unsigned n_diff;
329 struct check_options opts;
330};
331
332
333static void adjust_hwm(struct check_ctx* ctx, const struct record* r);
334
335static int add_record(struct check_ctx* ctx, TDB_DATA key, TDB_DATA value)
336{
337 NTSTATUS status;
338 TDB_DATA_diff diff;
339 TALLOC_CTX* mem = talloc_new(ctx->diff);
340 TDB_DATA recvalue;
341 struct db_record *rec = dbwrap_fetch_locked(ctx->diff, mem, key);
342
343 if (rec == NULL) {
344 return -1;
345 }
346
347 recvalue = dbwrap_record_get_value(rec);
348
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;
353 }
354 } else {
355 diff = unpack_diff(recvalue);
356 talloc_free(diff.nval.dptr);
357 }
358 diff.nval = tdb_data_talloc_copy(ctx->diff, value);
359
360 DEBUG_DIFF(2, mem, "TDB DIFF", key, diff.oval, diff.nval);
361
362 status = dbwrap_record_store(rec, pack_diff(&diff), 0);
363
364 talloc_free(mem);
365
366 if (!NT_STATUS_IS_OK(status)) {
367 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
368 return -1;
369 }
370 ctx->n_diff ++;
371 return 0;
372}
373
374static int del_record(struct check_ctx* ctx, TDB_DATA key) {
375 return add_record(ctx, key, tdb_null);
376}
377
378static TDB_DATA
379fetch_record(struct check_ctx* ctx, TALLOC_CTX* mem_ctx, TDB_DATA key)
380{
381 TDB_DATA tmp;
382 NTSTATUS status;
383
384 status = dbwrap_fetch(ctx->diff, mem_ctx, key, &tmp);
385
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);
390 return ret;
391 }
392
393 status = dbwrap_fetch(ctx->db, mem_ctx, key, &tmp);
394 if (!NT_STATUS_IS_OK(status)) {
395 return tdb_null;
396 }
397
398 return tmp;
399}
400
401static void edit_record(struct record* r) {
402 TALLOC_CTX* mem = talloc_new(r);
403 cbuf* ost = cbuf_new(mem);
404 const char* str;
405 struct record* nr;
406 TDB_DATA key;
407 TDB_DATA val;
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);
414 if (nr != NULL) {
415 *r = *nr;
416 }
417 talloc_free(mem);
418}
419
420static bool check_version(struct check_ctx* ctx) {
421 static const char* key = "IDMAP_VERSION";
422 uint32_t version;
423 NTSTATUS status;
424 char action = 's';
425 struct check_actions* act = &ctx->action;
426
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);
434 }
435 switch (action) {
436 case 's':
437 break;
438 case 'f':
439 SIVAL(&version, 0, 2);
440 add_record(ctx, string_term_tdb_data(key),
441 make_tdb_data((uint8_t *)&version, sizeof(uint32_t)));
442 break;
443 case 'a':
444 return false;
445 default:
446 assert(false);
447 }
448 return true;
449}
450
451static void check_hwm(struct check_ctx* ctx, const char* key, uint32_t target) {
452 uint32_t hwm;
453 char action = 's';
454 NTSTATUS status;
455 struct check_actions* act = &ctx->action;
456
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);
464 }
465 if (action == 'f') {
466 SIVAL(&hwm, 0, target);
467 add_record(ctx, string_term_tdb_data(key),
468 make_tdb_data((uint8_t *)&hwm, sizeof(uint32_t)));
469 }
470}
471
472int 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);
476 TDB_DATA key;
477 TDB_DATA value;
478 struct record *r;
479 char action = 's';
480
481 key = dbwrap_record_get_key(rec);
482 value = dbwrap_record_get_value(rec);
483
484 r = parse_record(mem, key, value);
485
486 if (is_invalid(r)) {
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++;
497 } else {
498 if (r->key_type == DT_SID) {
499 action = get_action(&act->valid_mapping, r, NULL);
500 ctx->n_map++;
501 } else {
502 action = get_action(&act->valid_mapping, NULL,
503 NULL);
504 }
505 }
506 adjust_hwm(ctx, r);
507 } else {
508 action = get_action(&act->valid_other, r, NULL);
509 ctx->n_other++;
510 }
511
512 while (action) {
513 switch (action) {
514 case 's': /* skip */
515 break;
516 case 'd': /* delete */
517 del_record(ctx, key);
518 break;
519 case 'f': /* add reverse mapping */
520 add_record(ctx, value, key);
521 break;
522 case 'e': /* edit */
523 edit_record(r);
524 action = 'o';
525 if (is_invalid(r)) {
526 action = get_action(&act->invalid_edit, r,NULL);
527 continue;
528 }
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))
533 {
534 action = get_action(&act->record_exists,
535 r, &oval);
536 if (action != 'o') {
537 continue;
538 }
539 }
540 }
541 if (is_map(r)) {
542 TDB_DATA okey = fetch_record(ctx, mem, r->val);
543 if (!tdb_data_is_empty(okey) &&
544 !tdb_data_equal(okey, r->key))
545 {
546 action = get_action(&act->record_exists,
547 reverse_record(r),
548 &okey);
549 }
550 }
551 continue;
552 case 'o': /* overwrite */
553 adjust_hwm(ctx, r);
554 if (!tdb_data_equal(key, r->key)) {
555 del_record(ctx, key);
556 }
557 add_record(ctx, r->key, r->val);
558 if (is_map(r)) {
559 add_record(ctx, r->val, r->key);
560 }
561 }
562 action = '\0';
563 };
564
565 talloc_free(mem);
566
567 return 0;
568}
569
570/******************************************************************************/
571
572void 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);
578 }
579}
580
581static bool is_cstr(TDB_DATA str) {
582 return !tdb_data_is_empty(str) && str.dptr[str.dsize-1] == '\0';
583}
584
585static bool parse_sid (TDB_DATA str, enum DT* type, struct dom_sid* sid) {
586 struct dom_sid tmp;
587 const char* s = (const char*)str.dptr;
588 if ((s[0] == 'S') && string_to_sid(&tmp, s)) {
589 *sid = tmp;
590 *type = DT_SID;
591 return true;
592 }
593 return false;
594}
595
596static bool parse_xid(TDB_DATA str, enum DT* type, unsigned long* id) {
597 char c, t;
598 unsigned long tmp;
599 if (sscanf((const char*)str.dptr, "%cID %lu%c", &c, &tmp, &t) == 2) {
600 if (c == 'U') {
601 *id = tmp;
602 *type = DT_UID;
603 return true;
604 } else if (c == 'G') {
605 *id = tmp;
606 *type = DT_GID;
607 return true;
608 }
609 }
610 return false;
611}
612
613
614struct record*
615parse_record(TALLOC_CTX* mem_ctx, TDB_DATA key, TDB_DATA val)
616{
617 struct record* ret = talloc_zero(mem_ctx, struct record);
618 if (ret == NULL) {
619 DEBUG(0, ("Out of memory.\n"));
620 return NULL;
621 }
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))
626 {
627 talloc_free(ret);
628 DEBUG(0, ("Out of memory.\n"));
629 return NULL;
630 }
631 assert((ret->key_type == DT_INV) && (ret->val_type == DT_INV));
632
633 if (!is_cstr(key)) {
634 return ret;
635 }
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)) {
639 if (is_cstr(val)) {
640 parse_sid(val, &ret->val_type, &ret->sid);
641 }
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;
647 }
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;
653 }
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;
659 }
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;
665 }
666 }
667
668 return ret;
669}
670
671struct record* reverse_record(struct record* in)
672{
673 return parse_record(talloc_parent(in), in->val, in->key);
674}
675
676
677/******************************************************************************/
678
679
680char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d)
681{
682 if (!tdb_data_is_empty(d)) {
683 char* ret = NULL;
684 cbuf* ost = cbuf_new(mem_ctx);
685 int len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize);
686 if (len != -1) {
687 cbuf_swapptr(ost, &ret, 0);
688 talloc_steal(mem_ctx, ret);
689 }
690 talloc_free(ost);
691 return ret;
692 }
693 return talloc_strdup(mem_ctx, "<NULL>");
694}
695
696
697TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr) {
698 cbuf* ost = cbuf_new(mem_ctx);
699 TDB_DATA ret = tdb_null;
700 srprs_skipws(ptr);
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));
704 }
705 talloc_free(ost);
706 return ret;
707}
708
709static int traverse_print_diff(struct db_record *rec, void* data) {
710 struct check_ctx* ctx = (struct check_ctx*)data;
711 TDB_DATA key;
712 TDB_DATA value;
713 TDB_DATA_diff diff;
714 TALLOC_CTX* mem = talloc_new(ctx->diff);
715
716 key = dbwrap_record_get_key(rec);
717 value = dbwrap_record_get_value(rec);
718 diff = unpack_diff(value);
719
720 DEBUG_DIFF(0, mem, "DIFF", key, diff.oval, diff.nval);
721
722 talloc_free(mem);
723 return 0;
724}
725
726
727static int traverse_commit(struct db_record *diff_rec, void* data) {
728 struct check_ctx* ctx = (struct check_ctx*)data;
729 TDB_DATA key;
730 TDB_DATA diff_value;
731 TDB_DATA_diff diff;
732 TDB_DATA value;
733 TALLOC_CTX* mem = talloc_new(ctx->diff);
734 int ret = -1;
735 NTSTATUS status;
736 struct check_actions* act = &ctx->action;
737 struct db_record* rec;
738
739 key = dbwrap_record_get_key(diff_rec);
740 diff_value = dbwrap_record_get_value(diff_rec);
741 diff = unpack_diff(diff_value);
742
743 rec = dbwrap_fetch_locked(ctx->db, mem, key);
744 if (rec == NULL) {
745 goto done;
746 }
747
748 value = dbwrap_record_get_value(rec);
749
750 if (!tdb_data_equal(value, diff.oval)) {
751 char action;
752
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));
757
758 action = get_action(&act->invalid_diff, NULL, NULL);
759 if (action == 's') {
760 ret = 0;
761 goto done;
762 } else if (action == 'a') {
763 goto done;
764 }
765 }
766
767 DEBUG_DIFF(0, mem, "Commit", key, diff.oval, diff.nval);
768
769 if (tdb_data_is_empty(diff.nval)) {
770 status = dbwrap_record_delete(rec);
771 } else {
772 status = dbwrap_record_store(rec, diff.nval, 0);
773 }
774
775 if (!NT_STATUS_IS_OK(status)) {
776 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
777 if (!ctx->opts.force) {
778 goto done;
779 }
780 }
781 ret = 0;
782done:
783 talloc_free(mem);
784 return ret;
785}
786
787static struct check_ctx*
788check_init(TALLOC_CTX* mem_ctx, const struct check_options* o)
789{
790 struct check_ctx* ctx = talloc_zero(mem_ctx, struct check_ctx);
791 if (ctx == NULL) {
792 DEBUG(0, (_("No memory\n")));
793 return NULL;
794 }
795
796 ctx->diff = db_open_rbt(ctx);
797 if (ctx->diff == NULL) {
798 talloc_free(ctx);
799 DEBUG(0, (_("No memory\n")));
800 return NULL;
801 }
802
803 ctx->action = check_actions_init(o);
804 ctx->opts = *o;
805 return ctx;
806}
807
808static bool check_open_db(struct check_ctx* ctx, const char* name, int oflags)
809{
810 if (name == NULL) {
811 d_fprintf(stderr, _("Error: name == NULL in check_open_db().\n"));
812 return false;
813 }
814
815 if (ctx->db != NULL) {
816 if ((ctx->oflags == oflags) && (strcmp(ctx->name, name))) {
817 return true;
818 } else {
819 TALLOC_FREE(ctx->db);
820 }
821 }
822
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) {
826 d_fprintf(stderr,
827 _("Could not open idmap db (%s) for writing: %s\n"),
828 name, strerror(errno));
829 return false;
830 }
831
832 if (ctx->name != name) {
833 TALLOC_FREE(ctx->name);
834 ctx->name = talloc_strdup(ctx, name);
835 }
836
837 ctx->oflags = oflags;
838 return true;
839}
840
841static bool check_do_checks(struct check_ctx* ctx)
842{
843 NTSTATUS status;
844
845 if (!check_version(ctx)) {
846 return false;
847 }
848
849 status = dbwrap_traverse(ctx->db, traverse_check, ctx, NULL);
850
851 if (!NT_STATUS_IS_OK(status)) {
852 DEBUG(0, ("failed to traverse %s\n", ctx->name));
853 return false;
854 }
855
856 check_hwm(ctx, "USER HWM", ctx->uid_hwm + 1);
857 check_hwm(ctx, "GROUP HWM", ctx->gid_hwm + 1);
858
859 return true;
860}
861
862static void check_summary(const struct check_ctx* ctx)
863{
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);
870}
871
872static bool check_transaction_start(struct check_ctx* ctx) {
873 return (dbwrap_transaction_start(ctx->db) == 0);
874}
875
876static bool check_transaction_commit(struct check_ctx* ctx) {
877 return (dbwrap_transaction_commit(ctx->db) == 0);
878}
879
880static bool check_transaction_cancel(struct check_ctx* ctx) {
881 return (dbwrap_transaction_cancel(ctx->db) == 0);
882}
883
884
885static void check_diff_list(struct check_ctx* ctx) {
886 NTSTATUS status = dbwrap_traverse(ctx->diff, traverse_print_diff, ctx, NULL);
887
888 if (!NT_STATUS_IS_OK(status)) {
889 DEBUG(0, ("failed to traverse diff\n"));
890 }
891
892}
893
894static bool check_commit(struct check_ctx* ctx)
895{
896 struct check_actions* act = &ctx->action;
897 char action;
898 NTSTATUS status = NT_STATUS_OK;
899
900 check_summary(ctx);
901
902 if (ctx->n_diff == 0) {
903 return true;
904 }
905
906 while ((action = get_action(&act->commit, NULL, NULL)) == 'l') {
907 check_diff_list(ctx);
908 }
909 if (action == 's') {
910 return true;
911 }
912 assert(action == 'c');
913
914 if (!check_open_db(ctx, ctx->name, O_RDWR)) {
915 return false;
916 }
917
918 if (!check_transaction_start(ctx)) {
919 return false;
920 }
921
922 status = dbwrap_traverse(ctx->diff, traverse_commit, ctx, NULL);
923
924 if (!NT_STATUS_IS_OK(status)) {
925 check_transaction_cancel(ctx);
926 return false;
927 }
928 if (ctx->opts.test) { /*get_action? */
929 return check_transaction_cancel(ctx);
930 } else {
931 return check_transaction_commit(ctx);
932 }
933}
934
935int net_idmap_check_db(const char* db, const struct check_options* o)
936{
937 int ret = -1;
938 TALLOC_CTX* mem_ctx = talloc_stackframe();
939 struct check_ctx* ctx = check_init(mem_ctx, o);
940
941 if (!o->automatic && !isatty(STDIN_FILENO)) {
942 DEBUG(0, ("Interactive use needs tty, use --auto\n"));
943 goto done;
944 }
945 if (o->lock) {
946 if (check_open_db(ctx, db, O_RDWR)
947 && check_transaction_start(ctx))
948 {
949 if ( check_do_checks(ctx)
950 && check_commit(ctx)
951 && check_transaction_commit(ctx))
952 {
953 ret = 0;
954 } else {
955 check_transaction_cancel(ctx);
956 }
957 }
958 } else {
959 if (check_open_db(ctx, db, O_RDONLY)
960 && check_do_checks(ctx)
961 && check_commit(ctx))
962 {
963 ret = 0;
964 }
965 }
966done:
967 talloc_free(mem_ctx);
968 return ret;
969}
970
971
972/*Local Variables:*/
973/*mode: c*/
974/*End:*/
Note: See TracBrowser for help on using the repository browser.