s3:netlookup: make use of cli_credentials_init_anon()
[bbaumbach/samba-autobuild/.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
46 /* record *********************************************************************/
47
48 enum DT {
49         DT_INV = 0,
50         DT_SID, DT_UID, DT_GID,
51         DT_HWM, DT_VER, DT_SEQ,
52 };
53
54 struct record {
55         enum DT key_type, val_type;
56         TDB_DATA key, val;
57         struct dom_sid sid;
58         long unsigned  id;
59 };
60
61 static struct record* parse_record(TALLOC_CTX* ctx, TDB_DATA key, TDB_DATA val);
62 static struct record* reverse_record(struct record* rec);
63
64 static bool is_invalid(const struct record* r) {
65         return (r->key_type == DT_INV) || (r->val_type == DT_INV);
66 }
67
68 static bool is_map(const struct record* r) {
69         return (r->key_type == DT_SID)
70                 || (r->key_type == DT_UID) || (r->key_type == DT_GID);
71 }
72
73 /* action *********************************************************************/
74
75 typedef 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
87 struct check_actions {
88         check_action invalid_record;
89         check_action missing_reverse;
90         check_action invalid_mapping;
91         check_action invalid_edit;
92         check_action record_exists;
93         check_action no_version;
94         check_action wrong_version;
95         check_action invalid_hwm;
96         check_action commit;
97         check_action valid_mapping;
98         check_action valid_other;
99         check_action invalid_diff;
100 };
101
102 static 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
113 static 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
124 static 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
134 static struct check_actions
135 check_actions_init(const struct check_options* opts) {
136         struct check_actions ret = {
137                 .invalid_record = (check_action) {
138                         .name = "Invalid record",
139                         .prompt = "[e]dit/[d]elete/[D]elete all"
140                         "/[s]kip/[S]kip all",
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
251 static 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
287 typedef struct {
288         TDB_DATA oval, nval;
289 } TDB_DATA_diff;
290
291 static 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
298 static TDB_DATA_diff unpack_diff(TDB_DATA data) {
299         assert(data.dsize == sizeof(TDB_DATA_diff));
300         return *(TDB_DATA_diff*)data.dptr;
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
312 struct 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
333 static void adjust_hwm(struct check_ctx* ctx, const struct record* r);
334
335 static 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
374 static int del_record(struct check_ctx* ctx, TDB_DATA key) {
375         return add_record(ctx, key, tdb_null);
376 }
377
378 static TDB_DATA
379 fetch_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
401 static 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
420 static 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
451 static 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
472 int traverse_check(struct db_record *rec, void* data) {
473         struct check_ctx* ctx = (struct check_ctx*)data;
474         struct check_actions* act = &ctx->action;
475         TALLOC_CTX* mem = talloc_new(ctx->diff);
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
572 void adjust_hwm(struct check_ctx* ctx, const struct record* r) {
573         enum DT type = (r->key_type == DT_SID) ? r->val_type : r->key_type;
574         if (type == DT_UID) {
575                 ctx->uid_hwm = MAX(ctx->uid_hwm, r->id);
576         } else if (type == DT_GID) {
577                 ctx->gid_hwm = MAX(ctx->gid_hwm, r->id);
578         }
579 }
580
581 static bool is_cstr(TDB_DATA str) {
582         return !tdb_data_is_empty(str) && str.dptr[str.dsize-1] == '\0';
583 }
584
585 static 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
596 static 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
614 struct record*
615 parse_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
671 struct record* reverse_record(struct record* in)
672 {
673         return parse_record(talloc_parent(in), in->val, in->key);
674 }
675
676
677 /******************************************************************************/
678
679
680 char* 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
697 TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr) {
698         cbuf* ost = cbuf_new(mem_ctx);
699         TDB_DATA ret = tdb_null;
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
709 static 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
727 static 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;
782 done:
783         talloc_free(mem);
784         return  ret;
785 }
786
787 static struct check_ctx*
788 check_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
808 static 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
841 static 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
862 static 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
872 static bool check_transaction_start(struct check_ctx* ctx) {
873         return (dbwrap_transaction_start(ctx->db) == 0);
874 }
875
876 static bool check_transaction_commit(struct check_ctx* ctx) {
877         return (dbwrap_transaction_commit(ctx->db) == 0);
878 }
879
880 static bool check_transaction_cancel(struct check_ctx* ctx) {
881         return (dbwrap_transaction_cancel(ctx->db) == 0);
882 }
883
884
885 static 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
894 static 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
935 int 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         }
966 done:
967         talloc_free(mem_ctx);
968         return ret;
969 }
970
971
972 /*Local Variables:*/
973 /*mode: c*/
974 /*End:*/