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