3f9f3f53d96d32f685064c61c7a8c165c34050b2
[kai/samba.git] / source3 / utils / net_idmap_check.c
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
39 static int traverse_commit(struct db_record *diff_rec, void* data);
40 static int traverse_check(struct db_record *rec, void* data);
41
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
47 /* record *********************************************************************/
48
49 enum DT {
50         DT_INV = 0,
51         DT_SID, DT_UID, DT_GID,
52         DT_HWM, DT_VER, DT_SEQ,
53 };
54
55 struct record {
56         enum DT key_type, val_type;
57         TDB_DATA key, val;
58         struct dom_sid sid;
59         long unsigned  id;
60 };
61
62 static struct record* parse_record(TALLOC_CTX* ctx, TDB_DATA key, TDB_DATA val);
63 static struct record* reverse_record(struct record* rec);
64
65 static bool is_invalid(const struct record* r) {
66         return (r->key_type == DT_INV) || (r->val_type == DT_INV);
67 }
68
69 static bool is_map(const struct record* r) {
70         return (r->key_type == DT_SID)
71                 || (r->key_type == DT_UID) || (r->key_type == DT_GID);
72 }
73
74 /* action *********************************************************************/
75
76 typedef struct check_action {
77         const char* fmt;
78         const char* name;
79         const char* prompt;
80         const char* answers;
81         char auto_action;
82         char default_action;
83         bool verbose;
84 } check_action;
85
86 struct check_actions {
87         check_action invalid_record;
88         check_action missing_reverse;
89         check_action invalid_mapping;
90         check_action invalid_edit;
91         check_action record_exists;
92         check_action no_version;
93         check_action wrong_version;
94         check_action invalid_hwm;
95         check_action commit;
96         check_action valid_mapping;
97         check_action valid_other;
98         check_action invalid_diff;
99 };
100
101 static struct check_actions
102 check_actions_init(const struct check_options* opts) {
103         struct check_actions ret = {
104                 .invalid_record = (check_action) {
105                         .name = "Invalid record",
106                         .prompt = "[e]dit/[d]elete/[D]elete all"
107                         "/[s]kip/[S]kip all",
108                         .answers = "eds",
109                         .default_action = 'e',
110                         .verbose = true,
111                 },
112                 .missing_reverse = (check_action) {
113                         .name = "Missing reverse mapping for",
114                         .prompt = "[f]ix/[F]ix all/[e]dit/[d]elete/[D]elete all"
115                         "/[s]kip/[S]kip all",
116                         .answers = "feds",
117                         .default_action = 'f',
118                         .verbose = true,
119                 },
120                 .invalid_mapping = (check_action) {
121                         .fmt = "%1$s: %2$s -> %3$s\n(%4$s <- %3$s)\n",
122                         .name = "Invalid mapping",
123                         .prompt = "[e]dit/[d]elete/[D]elete all"
124                         "/[s]kip/[S]kip all",
125                         .answers = "eds",
126                         .default_action = 'd',
127                         .verbose = true,
128                 },
129                 .invalid_edit = (check_action) {
130                         .name = "Invalid record",
131                         .prompt = "[e]dit/[d]elete/[D]elete all"
132                         "/[s]kip/[S]kip all",
133                         .answers = "eds",
134                         .default_action = 'e',
135                         .verbose = true,
136                 },
137                 .record_exists = (check_action) {
138                         .fmt = "%1$s: %2$s\n-%4$s\n+%3$s\n",
139                         .name = "Record exists",
140                         .prompt = "[o]verwrite/[O]verwrite all/[e]dit"
141                         "/[d]elete/[D]elete all/[s]kip/[S]kip all",
142                         .answers = "oeds",
143                         .default_action = 'o',
144                         .verbose = true,
145                 },
146                 .no_version = (check_action) {
147                         .prompt = "[f]ix/[s]kip/[a]bort",
148                         .answers = "fsa",
149                         .default_action = 'f',
150                 },
151                 .wrong_version = (check_action) {
152                         .prompt = "[f]ix/[s]kip/[a]bort",
153                         .answers = "fsa",
154                         .default_action = 'a',
155                 },
156                 .invalid_hwm = (check_action) {
157                         .prompt = "[f]ix/[s]kip",
158                         .answers = "fs",
159                         .default_action = 'f',
160                 },
161                 .commit = (check_action) {
162                         .prompt = "[c]ommit/[l]ist/[s]kip",
163                         .answers = "cls",
164                         .default_action = 'l',
165                         .verbose = true,
166                 },
167                 .valid_mapping = (check_action) {
168                         .fmt = "%1$s: %2$s <-> %3$s\n",
169                         .name = "Mapping",
170                         .auto_action = 's',
171                         .verbose = opts->verbose,
172                 },
173                 .valid_other = (check_action) {
174                         .name = "Other",
175                         .auto_action = 's',
176                         .verbose = opts->verbose,
177                 },
178                 .invalid_diff = (check_action) {
179                         .prompt = "[s]kip/[S]kip all/[c]ommit/[C]ommit all"
180                         "/[a]bort",
181                         .answers = "sca",
182                         .default_action = 's',
183                 },
184         };
185
186         if (!opts->repair) {
187                 ret.invalid_record.auto_action  = 's';
188                 ret.missing_reverse.auto_action = 's';
189                 ret.invalid_mapping.auto_action = 's';
190                 ret.no_version.auto_action      = 's';
191                 ret.wrong_version.auto_action   = 's';
192                 ret.invalid_hwm.auto_action     = 's';
193                 ret.commit.auto_action          = 's';
194         }
195
196         if (opts->automatic) {
197                 ret.invalid_record.auto_action  = 'd'; /* delete */
198                 ret.missing_reverse.auto_action = 'f'; /* fix */
199                 ret.invalid_mapping.auto_action = 'd'; /* delete */
200                 ret.no_version.auto_action      = 'f'; /* fix */
201                 ret.wrong_version.auto_action   = 'a'; /* abort */
202                 ret.invalid_hwm.auto_action     = 'f'; /* fix */
203                 ret.commit.auto_action          = 'c'; /* commit */
204                 ret.invalid_diff.auto_action    = 'a'; /* abort */
205                 if (opts->force) {
206                         ret.wrong_version.auto_action   = 'f'; /* fix */
207                         ret.invalid_diff.auto_action    = 'c'; /* commit */
208                 }
209         }
210         if (opts->test) {
211                 ret.invalid_diff.auto_action    = 'c'; /* commit */
212 /*              ret.commit.auto_action          = 'c';*/ /* commit */
213         }
214
215         return ret;
216 }
217
218 static char get_action(struct check_action* a, struct record* r, TDB_DATA* v) {
219         char ret;
220         if (a->verbose && (r != NULL)) {
221                 if (!a->fmt) {
222                         d_printf("%s: %s ", a->name, print_data(r, r->key));
223                         if (is_map(r)) {
224                                 d_printf("-> %s\n", print_data(r, r->val));
225                         } else if (r->key_type == DT_HWM ||
226                                    r->key_type == DT_VER ||
227                                    r->key_type == DT_SEQ)
228                         {
229                                 d_printf(": %ld\n", r->id);
230                         } else {
231                                 d_printf("\n");
232                         }
233                 } else {
234                         d_printf(a->fmt, a->name,
235                                  print_data(r, r->key),
236                                  print_data(r, r->val),
237                                  (v ? print_data(r, *v) : ""));
238                 }
239         }
240
241         if (a->auto_action != '\0') {
242                 return a->auto_action;
243         }
244
245         ret = interact_prompt(a->prompt, a->answers, a->default_action);
246
247         if (isupper(ret)) {
248                 ret = tolower(ret);
249                 a->auto_action = ret;
250         }
251         a->default_action = ret;
252         return ret;
253 }
254
255 /*  *************************************************************************/
256
257 typedef struct {
258         TDB_DATA oval, nval;
259 } TDB_DATA_diff;
260
261 static TDB_DATA pack_diff(TDB_DATA_diff* diff) {
262         return (TDB_DATA) {
263                 .dptr = (uint8_t *)diff,
264                 .dsize = sizeof(TDB_DATA_diff),
265         };
266 }
267
268 static TDB_DATA_diff unpack_diff(TDB_DATA data) {
269         assert(data.dsize == sizeof(TDB_DATA_diff));
270         return *(TDB_DATA_diff*)data.dptr;
271 }
272
273 #define DEBUG_DIFF(LEV,MEM,MSG,KEY,OLD,NEW)                     \
274         DEBUG(LEV, ("%s: %s\n", MSG, print_data(MEM, KEY)));    \
275         if (!tdb_data_is_empty(OLD)) {                          \
276                 DEBUGADD(LEV, ("-%s\n", print_data(MEM, OLD))); \
277         }                                                       \
278         if (!tdb_data_is_empty(NEW)) {                          \
279                 DEBUGADD(LEV, ("+%s\n", print_data(MEM, NEW))); \
280         }
281
282 struct check_ctx {
283         int oflags;
284         char* name;
285         bool transaction;
286         struct db_context *db;
287         struct db_context *diff;
288         struct check_actions action;
289
290         uint32_t uid_hwm;
291         uint32_t gid_hwm;
292
293         unsigned n_invalid_record;
294         unsigned n_missing_reverse;
295         unsigned n_invalid_mappping;
296         unsigned n_map;
297         unsigned n_other;
298         unsigned n_diff;
299         struct check_options opts;
300 };
301
302
303 static void adjust_hwm(struct check_ctx* ctx, const struct record* r);
304
305 static int add_record(struct check_ctx* ctx, TDB_DATA key, TDB_DATA value)
306 {
307         NTSTATUS status;
308         TDB_DATA_diff diff;
309         TALLOC_CTX* mem = talloc_new(ctx->diff);
310         TDB_DATA recvalue;
311         struct db_record *rec = dbwrap_fetch_locked(ctx->diff, mem, key);
312
313         if (rec == NULL) {
314                 return -1;
315         }
316
317         recvalue = dbwrap_record_get_value(rec);
318
319         if (recvalue.dptr == 0) { /* first entry */
320                 status = dbwrap_fetch(ctx->db, ctx->diff, key, &diff.oval);
321                 if (!NT_STATUS_IS_OK(status)) {
322                         diff.oval = tdb_null;
323                 }
324         } else {
325                 diff = unpack_diff(recvalue);
326                 talloc_free(diff.nval.dptr);
327         }
328         diff.nval = talloc_copy(ctx->diff, value);
329
330         DEBUG_DIFF(2, mem, "TDB DIFF", key, diff.oval, diff.nval);
331
332         status = dbwrap_record_store(rec, pack_diff(&diff), 0);
333
334         talloc_free(mem);
335
336         if (!NT_STATUS_IS_OK(status)) {
337                 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
338                 return -1;
339         }
340         ctx->n_diff ++;
341         return 0;
342 }
343
344 static int del_record(struct check_ctx* ctx, TDB_DATA key) {
345         return add_record(ctx, key, tdb_null);
346 }
347
348 static TDB_DATA
349 fetch_record(struct check_ctx* ctx, TALLOC_CTX* mem_ctx, TDB_DATA key)
350 {
351         TDB_DATA tmp;
352         NTSTATUS status;
353
354         status = dbwrap_fetch(ctx->diff, mem_ctx, key, &tmp);
355
356         if (NT_STATUS_IS_OK(status)) {
357                 TDB_DATA_diff diff = unpack_diff(tmp);
358                 TDB_DATA ret = talloc_copy(mem_ctx, diff.nval);
359                 talloc_free(tmp.dptr);
360                 return ret;
361         }
362
363         status = dbwrap_fetch(ctx->db, mem_ctx, key, &tmp);
364         if (!NT_STATUS_IS_OK(status)) {
365                 return tdb_null;
366         }
367
368         return tmp;
369 }
370
371 static void edit_record(struct record* r) {
372         TALLOC_CTX* mem = talloc_new(r);
373         cbuf* ost = cbuf_new(mem);
374         const char* str;
375         struct record* nr;
376         TDB_DATA key;
377         TDB_DATA val;
378         cbuf_printf(ost, "%s %s\n",
379                     print_data(mem, r->key), print_data(mem, r->val));
380         str = interact_edit(mem, cbuf_gets(ost, 0));
381         key = parse_data(mem, &str);
382         val = parse_data(mem, &str);
383         nr = parse_record(talloc_parent(r), key, val);
384         if (nr != NULL) {
385                 *r = *nr;
386         }
387         talloc_free(mem);
388 }
389
390 static bool check_version(struct check_ctx* ctx) {
391         static const char* key = "IDMAP_VERSION";
392         uint32_t version;
393         NTSTATUS status;
394         char action = 's';
395         struct check_actions* act = &ctx->action;
396
397         status = dbwrap_fetch_uint32(ctx->db, key, &version);
398         if (!NT_STATUS_IS_OK(status)) {
399                 d_printf("No version number, assume 2\n");
400                 action = get_action(&act->no_version, NULL, NULL);
401         } else if (version != 2) {
402                 d_printf("Wrong version number %d, should be 2\n", version);
403                 action = get_action(&act->wrong_version, NULL, NULL);
404         }
405         switch (action) {
406         case 's':
407                 break;
408         case 'f':
409                 SIVAL(&version, 0, 2);
410                 add_record(ctx, string_term_tdb_data(key),
411                            make_tdb_data((uint8_t *)&version, sizeof(uint32_t)));
412                 break;
413         case 'a':
414                 return false;
415         default:
416                 assert(false);
417         }
418         return true;
419 }
420
421 static void check_hwm(struct check_ctx* ctx, const char* key, uint32_t target) {
422         uint32_t hwm;
423         char action = 's';
424         NTSTATUS status;
425         struct check_actions* act = &ctx->action;
426
427         status = dbwrap_fetch_uint32(ctx->db, key, &hwm);
428         if (!NT_STATUS_IS_OK(status)) {
429                 d_printf("No %s should be %d\n", key, target);
430                 action = get_action(&act->invalid_hwm, NULL, NULL);
431         } else if (target < hwm) {
432                 d_printf("Invalid %s %d: should be %d\n", key, hwm, target);
433                 action = get_action(&act->invalid_hwm, NULL, NULL);
434         }
435         if (action == 'f') {
436                 SIVAL(&hwm, 0, target);
437                 add_record(ctx, string_term_tdb_data(key),
438                            make_tdb_data((uint8_t *)&hwm, sizeof(uint32_t)));
439         }
440 }
441
442 int traverse_check(struct db_record *rec, void* data) {
443         struct check_ctx* ctx = (struct check_ctx*)data;
444         struct check_actions* act = &ctx->action;
445         TALLOC_CTX* mem = talloc_new(ctx->diff);
446         TDB_DATA key;
447         TDB_DATA value;
448         struct record *r;
449         char action = 's';
450
451         key = dbwrap_record_get_key(rec);
452         value = dbwrap_record_get_value(rec);
453
454         r = parse_record(mem, key, value);
455
456         if (is_invalid(r)) {
457                 action = get_action(&act->invalid_record, r, NULL);
458                 ctx->n_invalid_record++;
459         } else if (is_map(r)) {
460                 TDB_DATA back = fetch_record(ctx, mem, r->val);
461                 if (back.dptr == NULL) {
462                         action = get_action(&act->missing_reverse, r, NULL);
463                         ctx->n_missing_reverse++;
464                 } else if (!tdb_data_equal(r->key, back)) {
465                         action = get_action(&act->invalid_mapping, r, &back);
466                         ctx->n_invalid_mappping++;
467                 } else {
468                         if (r->key_type == DT_SID) {
469                                 action = get_action(&act->valid_mapping, r, NULL);
470                                 ctx->n_map++;
471                         } else {
472                                 action = get_action(&act->valid_mapping, NULL,
473                                                     NULL);
474                         }
475                 }
476                 adjust_hwm(ctx, r);
477         } else {
478                 action = get_action(&act->valid_other, r, NULL);
479                 ctx->n_other++;
480         }
481
482         while (action) {
483                 switch (action) {
484                 case 's': /* skip */
485                         break;
486                 case 'd': /* delete */
487                         del_record(ctx, key);
488                         break;
489                 case 'f': /* add reverse mapping */
490                         add_record(ctx, value, key);
491                         break;
492                 case 'e': /* edit */
493                         edit_record(r);
494                         action = 'o';
495                         if (is_invalid(r)) {
496                                 action = get_action(&act->invalid_edit, r,NULL);
497                                 continue;
498                         }
499                         if (!tdb_data_equal(key, r->key)) {
500                                 TDB_DATA oval = fetch_record(ctx, mem, r->key);
501                                 if (!tdb_data_is_empty(oval) &&
502                                     !tdb_data_equal(oval, r->val))
503                                 {
504                                         action = get_action(&act->record_exists,
505                                                             r, &oval);
506                                         if (action != 'o') {
507                                                 continue;
508                                         }
509                                 }
510                         }
511                         if (is_map(r)) {
512                                 TDB_DATA okey = fetch_record(ctx, mem, r->val);
513                                 if (!tdb_data_is_empty(okey) &&
514                                     !tdb_data_equal(okey, r->key))
515                                 {
516                                         action = get_action(&act->record_exists,
517                                                             reverse_record(r),
518                                                             &okey);
519                                 }
520                         }
521                         continue;
522                 case 'o': /* overwrite */
523                         adjust_hwm(ctx, r);
524                         if (!tdb_data_equal(key, r->key)) {
525                                 del_record(ctx, key);
526                         }
527                         add_record(ctx, r->key, r->val);
528                         if (is_map(r)) {
529                                 add_record(ctx, r->val, r->key);
530                         }
531                 }
532                 action = '\0';
533         };
534
535         talloc_free(mem);
536
537         return 0;
538 }
539
540 /******************************************************************************/
541
542 void adjust_hwm(struct check_ctx* ctx, const struct record* r) {
543         enum DT type = (r->key_type == DT_SID) ? r->val_type : r->key_type;
544         if (type == DT_UID) {
545                 ctx->uid_hwm = MAX(ctx->uid_hwm, r->id);
546         } else if (type == DT_GID) {
547                 ctx->gid_hwm = MAX(ctx->gid_hwm, r->id);
548         }
549 }
550
551 TDB_DATA talloc_copy(TALLOC_CTX* mem_ctx, TDB_DATA data) {
552         TDB_DATA ret = {
553                 .dptr  = (uint8_t *)talloc_size(mem_ctx, data.dsize+1),
554                 .dsize = data.dsize
555         };
556         if (ret.dptr == NULL) {
557                 ret.dsize = 0;
558         } else {
559                 memcpy(ret.dptr, data.dptr, data.dsize);
560                 ret.dptr[ret.dsize] = '\0';
561         }
562         return ret;
563 }
564
565 static bool is_cstr(TDB_DATA str) {
566         return !tdb_data_is_empty(str) && str.dptr[str.dsize-1] == '\0';
567 }
568
569 static bool parse_sid (TDB_DATA str, enum DT* type, struct dom_sid* sid) {
570         struct dom_sid tmp;
571         const char* s = (const char*)str.dptr;
572         if ((s[0] == 'S') && string_to_sid(&tmp, s)) {
573                 *sid = tmp;
574                 *type = DT_SID;
575                 return true;
576         }
577         return false;
578 }
579
580 static bool parse_xid(TDB_DATA str, enum DT* type, unsigned long* id) {
581         char c, t;
582         unsigned long tmp;
583         if (sscanf((const char*)str.dptr, "%cID %lu%c", &c, &tmp, &t) == 2) {
584                 if (c == 'U') {
585                         *id = tmp;
586                         *type = DT_UID;
587                         return true;
588                 } else if (c == 'G') {
589                         *id = tmp;
590                         *type = DT_GID;
591                         return true;
592                 }
593         }
594         return false;
595 }
596
597
598 struct record*
599 parse_record(TALLOC_CTX* mem_ctx, TDB_DATA key, TDB_DATA val)
600 {
601         struct record* ret = talloc_zero(mem_ctx, struct record);
602         if (ret == NULL) {
603                 DEBUG(0, ("Out of memory.\n"));
604                 return NULL;
605         }
606         ret->key = talloc_copy(ret, key);
607         ret->val = talloc_copy(ret, val);
608         if ((ret->key.dptr == NULL && key.dptr != NULL) ||
609             (ret->val.dptr == NULL && val.dptr != NULL))
610         {
611                 talloc_free(ret);
612                 DEBUG(0, ("Out of memory.\n"));
613                 return NULL;
614         }
615         assert((ret->key_type == DT_INV) && (ret->val_type == DT_INV));
616
617         if (!is_cstr(key)) {
618                 return ret;
619         }
620         if (parse_sid(key, &ret->key_type, &ret->sid)) {
621                 parse_xid(val, &ret->val_type, &ret->id);
622         } else if (parse_xid(key, &ret->key_type, &ret->id)) {
623                 if (is_cstr(val)) {
624                         parse_sid(val, &ret->val_type, &ret->sid);
625                 }
626         } else if (strcmp((const char*)key.dptr, "USER HWM") == 0) {
627                 ret->key_type = DT_HWM;
628                 if (val.dsize == 4) {
629                         ret->id = IVAL(val.dptr,0);
630                         ret->val_type = DT_UID;
631                 }
632         } else if (strcmp((const char*)key.dptr, "GROUP HWM") == 0) {
633                 ret->key_type = DT_HWM;
634                 if (val.dsize == 4) {
635                         ret->id = IVAL(val.dptr,0);
636                         ret->val_type = DT_GID;
637                 }
638         } else if (strcmp((const char*)key.dptr, "IDMAP_VERSION") == 0) {
639                 ret->key_type = DT_VER;
640                 if (val.dsize == 4) {
641                         ret->id = IVAL(val.dptr,0);
642                         ret->val_type = DT_VER;
643                 }
644         } else if (strcmp((const char*)key.dptr, "__db_sequence_number__") == 0) {
645                 ret->key_type = DT_SEQ;
646                 if (val.dsize == 8) {
647                         ret->id = *(uint64_t*)val.dptr;
648                         ret->val_type = DT_SEQ;
649                 }
650         }
651
652         return ret;
653 }
654
655 struct record* reverse_record(struct record* in)
656 {
657         return parse_record(talloc_parent(in), in->val, in->key);
658 }
659
660
661 /******************************************************************************/
662
663
664 char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d)
665 {
666         if (!tdb_data_is_empty(d)) {
667                 char* ret = NULL;
668                 cbuf* ost = cbuf_new(mem_ctx);
669                 int len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize);
670                 if (len != -1) {
671                         cbuf_swapptr(ost, &ret, 0);
672                         talloc_steal(mem_ctx, ret);
673                 }
674                 talloc_free(ost);
675                 return ret;
676         }
677         return talloc_strdup(mem_ctx, "<NULL>");
678 }
679
680
681 TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr) {
682         cbuf* ost = cbuf_new(mem_ctx);
683         TDB_DATA ret = tdb_null;
684         srprs_skipws(ptr);
685         if (srprs_quoted(ptr, ost)) {
686                 ret.dsize = cbuf_getpos(ost);
687                 ret.dptr = (uint8_t *)talloc_steal(mem_ctx, cbuf_gets(ost,0));
688         }
689         talloc_free(ost);
690         return ret;
691 }
692
693 static int traverse_print_diff(struct db_record *rec, void* data) {
694         struct check_ctx* ctx = (struct check_ctx*)data;
695         TDB_DATA key;
696         TDB_DATA value;
697         TDB_DATA_diff diff;
698         TALLOC_CTX* mem = talloc_new(ctx->diff);
699
700         key = dbwrap_record_get_key(rec);
701         value = dbwrap_record_get_value(rec);
702         diff = unpack_diff(value);
703
704         DEBUG_DIFF(0, mem, "DIFF", key, diff.oval, diff.nval);
705
706         talloc_free(mem);
707         return 0;
708 }
709
710
711 static int traverse_commit(struct db_record *diff_rec, void* data) {
712         struct check_ctx* ctx = (struct check_ctx*)data;
713         TDB_DATA key;
714         TDB_DATA diff_value;
715         TDB_DATA_diff diff;
716         TDB_DATA value;
717         TALLOC_CTX* mem = talloc_new(ctx->diff);
718         int ret = -1;
719         NTSTATUS status;
720         struct check_actions* act = &ctx->action;
721         struct db_record* rec;
722
723         key = dbwrap_record_get_key(diff_rec);
724         diff_value = dbwrap_record_get_value(diff_rec);
725         diff = unpack_diff(diff_value);
726
727         rec = dbwrap_fetch_locked(ctx->db, mem, key);
728         if (rec == NULL) {
729                 goto done;
730         }
731
732         value = dbwrap_record_get_value(rec);
733
734         if (!tdb_data_equal(value, diff.oval)) {
735                 char action;
736
737                 d_printf("Warning: record has changed: %s\n"
738                          "expected: %s got %s\n", print_data(mem, key),
739                          print_data(mem, diff.oval),
740                          print_data(mem, value));
741
742                 action = get_action(&act->invalid_diff, NULL, NULL);
743                 if (action == 's') {
744                         ret = 0;
745                         goto done;
746                 } else if (action == 'a') {
747                         goto done;
748                 }
749         }
750
751         DEBUG_DIFF(0, mem, "Commit", key, diff.oval, diff.nval);
752
753         if (tdb_data_is_empty(diff.nval)) {
754                 status = dbwrap_record_delete(rec);
755         } else {
756                 status = dbwrap_record_store(rec, diff.nval, 0);
757         }
758
759         if (!NT_STATUS_IS_OK(status)) {
760                 DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
761                 if (!ctx->opts.force) {
762                         goto done;
763                 }
764         }
765         ret = 0;
766 done:
767         talloc_free(mem);
768         return  ret;
769 }
770
771 static struct check_ctx*
772 check_init(TALLOC_CTX* mem_ctx, const struct check_options* o)
773 {
774         struct check_ctx* ctx = talloc_zero(mem_ctx, struct check_ctx);
775         if (ctx == NULL) {
776                 DEBUG(0, (_("No memory\n")));
777                 return NULL;
778         }
779
780         ctx->diff = db_open_rbt(ctx);
781         if (ctx->diff == NULL) {
782                 talloc_free(ctx);
783                 DEBUG(0, (_("No memory\n")));
784                 return NULL;
785         }
786
787         ctx->action = check_actions_init(o);
788         ctx->opts = *o;
789         return ctx;
790 }
791
792 static bool check_open_db(struct check_ctx* ctx, const char* name, int oflags)
793 {
794         if (name == NULL) {
795                 d_fprintf(stderr, _("Error: name == NULL in check_open_db().\n"));
796                 return false;
797         }
798
799         if (ctx->db != NULL) {
800                 if ((ctx->oflags == oflags) && (strcmp(ctx->name, name))) {
801                         return true;
802                 } else {
803                         TALLOC_FREE(ctx->db);
804                 }
805         }
806
807         ctx->db = db_open(ctx, name, 0, TDB_DEFAULT, oflags, 0);
808         if (ctx->db == NULL) {
809                 d_fprintf(stderr,
810                           _("Could not open idmap db (%s) for writing: %s\n"),
811                           name, strerror(errno));
812                 return false;
813         }
814
815         if (ctx->name != name) {
816                 TALLOC_FREE(ctx->name);
817                 ctx->name = talloc_strdup(ctx, name);
818         }
819
820         ctx->oflags = oflags;
821         return true;
822 }
823
824 static bool check_do_checks(struct check_ctx* ctx)
825 {
826         NTSTATUS status;
827
828         if (!check_version(ctx)) {
829                 return false;
830         }
831
832         status = dbwrap_traverse(ctx->db, traverse_check, ctx, NULL);
833
834         if (!NT_STATUS_IS_OK(status)) {
835                 DEBUG(0, ("failed to traverse %s\n", ctx->name));
836                 return false;
837         }
838
839         check_hwm(ctx, "USER HWM", ctx->uid_hwm + 1);
840         check_hwm(ctx, "GROUP HWM", ctx->gid_hwm + 1);
841
842         return true;
843 }
844
845 static void check_summary(const struct check_ctx* ctx)
846 {
847         d_printf("uid hwm: %d\ngid hwm: %d\n", ctx->uid_hwm, ctx->gid_hwm);
848         d_printf("mappings: %d\nother: %d\n", ctx->n_map, ctx->n_other);
849         d_printf("invalid records: %d\nmissing links: %d\ninvalid links: %d\n",
850                  ctx->n_invalid_record, ctx->n_missing_reverse,
851                  ctx->n_invalid_mappping);
852         d_printf("%u changes:\n", ctx->n_diff);
853 }
854
855 static bool check_transaction_start(struct check_ctx* ctx) {
856         return (dbwrap_transaction_start(ctx->db) == 0);
857 }
858
859 static bool check_transaction_commit(struct check_ctx* ctx) {
860         return (dbwrap_transaction_commit(ctx->db) == 0);
861 }
862
863 static bool check_transaction_cancel(struct check_ctx* ctx) {
864         return (dbwrap_transaction_cancel(ctx->db) == 0);
865 }
866
867
868 static void check_diff_list(struct check_ctx* ctx) {
869         NTSTATUS status = dbwrap_traverse(ctx->diff, traverse_print_diff, ctx, NULL);
870
871         if (!NT_STATUS_IS_OK(status)) {
872                 DEBUG(0, ("failed to traverse diff\n"));
873         }
874
875 }
876
877 static bool check_commit(struct check_ctx* ctx)
878 {
879         struct check_actions* act = &ctx->action;
880         char action;
881         NTSTATUS status = NT_STATUS_OK;
882
883         check_summary(ctx);
884
885         if (ctx->n_diff == 0) {
886                 return true;
887         }
888
889         while ((action = get_action(&act->commit, NULL, NULL)) == 'l') {
890                 check_diff_list(ctx);
891         }
892         if (action == 's') {
893                 return true;
894         }
895         assert(action == 'c');
896
897         if (!check_open_db(ctx, ctx->name, O_RDWR)) {
898                 return false;
899         }
900
901         if (!check_transaction_start(ctx)) {
902                 return false;
903         }
904
905         status = dbwrap_traverse(ctx->diff, traverse_commit, ctx, NULL);
906
907         if (!NT_STATUS_IS_OK(status)) {
908                 check_transaction_cancel(ctx);
909                 return false;
910         }
911         if (ctx->opts.test) { /*get_action? */
912                 return check_transaction_cancel(ctx);
913         } else {
914                 return check_transaction_commit(ctx);
915         }
916 }
917
918 int net_idmap_check_db(const char* db, const struct check_options* o)
919 {
920         int ret = -1;
921         TALLOC_CTX* mem_ctx = talloc_stackframe();
922         struct check_ctx* ctx = check_init(mem_ctx, o);
923
924         if (!o->automatic && !isatty(STDIN_FILENO)) {
925                 DEBUG(0, ("Interactive use needs tty, use --auto\n"));
926                 goto done;
927         }
928         if (o->lock) {
929                 if (check_open_db(ctx, db, O_RDWR)
930                     && check_transaction_start(ctx))
931                 {
932                         if ( check_do_checks(ctx)
933                              && check_commit(ctx)
934                              && check_transaction_commit(ctx))
935                         {
936                                 ret = 0;
937                         } else {
938                                 check_transaction_cancel(ctx);
939                         }
940                 }
941         } else {
942                 if (check_open_db(ctx, db, O_RDONLY)
943                     && check_do_checks(ctx)
944                     && check_commit(ctx))
945                 {
946                         ret = 0;
947                 }
948         }
949 done:
950         talloc_free(mem_ctx);
951         return ret;
952 }
953
954
955 /*Local Variables:*/
956 /*mode: c*/
957 /*End:*/