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