monotone

monotone Mtn Source Tree

Root/database_check.cc

1// Copyright (C) 2005 Derek Scherger <derek@echologic.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include <map>
11#include <set>
12
13#include "app_state.hh"
14#include "constants.hh"
15#include "database_check.hh"
16#include "keys.hh"
17#include "revision.hh"
18#include "ui.hh"
19#include "vocab.hh"
20#include "transforms.hh"
21#include "cert.hh"
22
23// the database has roughly the following structure
24//
25// certs
26// |
27// +---+---+
28// | |
29// keys revisions
30// |
31// rosters
32// |
33// files
34//
35
36using std::logic_error;
37using std::map;
38using std::multimap;
39using std::set;
40using std::string;
41using std::vector;
42
43struct checked_cert {
44 revision<cert> rcert;
45 bool found_key;
46 bool good_sig;
47
48 checked_cert(revision<cert> const & c): rcert(c), found_key(false), good_sig(false) {}
49};
50
51struct checked_key {
52 bool found; // found public keypair id in db
53 size_t sigs; // number of signatures by this key
54
55 base64<rsa_pub_key> pub_encoded;
56
57 checked_key(): found(false), sigs(0) {}
58};
59
60struct checked_file {
61 bool found; // found in db, retrieved and verified sha1 hash
62 size_t roster_refs; // number of roster references to this file
63
64 checked_file(): found(false), roster_refs(0) {}
65};
66
67struct checked_roster {
68 bool found; // found in db, retrieved and verified sha1 hash
69 size_t revision_refs; // number of revision references to this roster
70 size_t missing_files; // number of missing files referenced by this roster
71 size_t missing_mark_revs; // number of missing revisions referenced in node markings by this roster
72
73 bool parseable; // read_roster_and_marking does not throw
74 bool normalized; // write_roster_and_marking( read_roster_and_marking(dat) ) == dat
75
76 manifest_id man_id; // manifest id of this roster's public part
77
78 checked_roster():
79 found(false), revision_refs(0),
80 missing_files(0), missing_mark_revs(0),
81 parseable(false), normalized(false), man_id() {}
82};
83
84// the number of times a revision is referenced (revision_refs)
85// should match the number of times it is listed as a parent in
86// the ancestry cache (ancestry_parent_refs)
87//
88// the number of parents a revision has should match the number
89// of times it is listed as a child in the ancestry cache
90// (ancestry_child_refs)
91
92struct checked_revision {
93 bool found; // found in db, retrieved and verified sha1 hash
94 size_t revision_refs; // number of references to this revision from other revisions
95 size_t ancestry_parent_refs; // number of references to this revision by ancestry parent
96 size_t ancestry_child_refs; // number of references to this revision by ancestry child
97 size_t marking_refs; // number of references to this revision by roster markings
98
99 bool found_roster_link; // the revision->roster link for this revision exists
100 bool found_roster; // the roster for this revision exists
101 bool manifest_mismatch; // manifest doesn't match the roster for this revision
102 bool incomplete_roster; // the roster for this revision is missing files
103 size_t missing_manifests; // number of missing manifests referenced by this revision
104 size_t missing_revisions; // number of missing revisions referenced by this revision
105
106 size_t cert_refs; // number of references to this revision by revision certs;
107
108 bool parseable; // read_revision does not throw
109 bool normalized; // write_revision( read_revision(dat) ) == dat
110
111 string history_error;
112
113 set<revision_id> parents;
114 vector<checked_cert> checked_certs;
115
116 checked_revision():
117 found(false),
118 revision_refs(0), ancestry_parent_refs(0), ancestry_child_refs(0),
119 marking_refs(0),
120 found_roster(false), manifest_mismatch(false), incomplete_roster(false),
121 missing_manifests(0), missing_revisions(0),
122 cert_refs(0), parseable(false), normalized(false) {}
123};
124
125/*
126 * check integrity of the SQLite database
127 */
128static void
129check_db_integrity_check(app_state & app )
130{
131 L(FL("asking sqlite to check db integrity"));
132 E(app.db.check_integrity(),
133 F("file structure is corrupted; cannot check further"));
134}
135
136static void
137check_files(app_state & app, map<file_id, checked_file> & checked_files)
138{
139 set<file_id> files;
140
141 app.db.get_file_ids(files);
142 L(FL("checking %d files") % files.size());
143
144 ticker ticks(_("files"), "f", files.size()/70+1);
145
146 for (set<file_id>::const_iterator i = files.begin();
147 i != files.end(); ++i)
148 {
149 L(FL("checking file %s") % *i);
150 file_data data;
151 app.db.get_file_version(*i, data);
152 checked_files[*i].found = true;
153 ++ticks;
154 }
155
156 I(checked_files.size() == files.size());
157}
158
159// first phase of roster checking, checks manifest-related parts of the
160// roster, and general parsability/normalisation
161static void
162check_rosters_manifest(app_state & app,
163 map<roster_id, checked_roster> & checked_rosters,
164 map<revision_id, checked_revision> & checked_revisions,
165 set<manifest_id> & found_manifests,
166 map<file_id, checked_file> & checked_files)
167{
168 set<roster_id> rosters;
169
170 app.db.get_roster_ids(rosters);
171 L(FL("checking %d rosters, manifest pass") % rosters.size());
172
173 ticker ticks(_("rosters"), "r", rosters.size()/70+1);
174
175 for (set<roster_id>::const_iterator i = rosters.begin();
176 i != rosters.end(); ++i)
177 {
178
179 L(FL("checking roster %s") % *i);
180 roster_data dat;
181 app.db.get_roster_version(*i, dat);
182 checked_rosters[*i].found = true;
183
184 roster_t ros;
185 marking_map mm;
186 try
187 {
188 read_roster_and_marking(dat, ros, mm);
189 }
190 catch (logic_error & e)
191 {
192 L(FL("error parsing roster %s: %s") % *i % e.what());
193 checked_rosters[*i].parseable = false;
194 continue;
195 }
196 checked_rosters[*i].parseable = true;
197
198 // normalisation check
199 {
200 roster_id norm_ident;
201 roster_data norm_data;
202 write_roster_and_marking(ros, mm, norm_data);
203 calculate_ident(norm_data, norm_ident);
204 if (norm_ident == *i)
205 checked_rosters[*i].normalized = true;
206 }
207
208 manifest_id man_id;
209 calculate_ident(ros, man_id);
210 checked_rosters[*i].man_id = man_id;
211 found_manifests.insert(man_id);
212
213 for (node_map::const_iterator n = ros.all_nodes().begin();
214 n != ros.all_nodes().end(); n++)
215 {
216
217 if (is_file_t(n->second))
218 {
219 file_id fid = downcast_to_file_t(n->second)->content;
220 checked_files[fid].roster_refs++;
221 if (!checked_files[fid].found)
222 checked_rosters[*i].missing_files++;
223 }
224 }
225
226 ++ticks;
227 }
228 I(checked_rosters.size() == rosters.size());
229}
230
231// second phase of roster checking. examine the marking of a roster, checking
232// that the referenced revisions exist.
233static void
234check_rosters_marking(app_state & app,
235 map<roster_id, checked_roster> & checked_rosters,
236 map<revision_id, checked_revision> & checked_revisions)
237{
238 L(FL("checking %d rosters, marking pass") % checked_rosters.size());
239
240 ticker ticks(_("markings"), "m", checked_rosters.size()/70+1);
241
242 for (map<roster_id, checked_roster>::const_iterator i
243 = checked_rosters.begin(); i != checked_rosters.end(); i++)
244 {
245 roster_id ros_id = i->first;
246 L(FL("checking roster %s") % i->first);
247 if (!i->second.parseable)
248 continue;
249
250 roster_data dat;
251 app.db.get_roster_version(ros_id, dat);
252
253 roster_t ros;
254 marking_map mm;
255 read_roster_and_marking(dat, ros, mm);
256
257 for (node_map::const_iterator n = ros.all_nodes().begin();
258 n != ros.all_nodes().end(); n++)
259 {
260 // lots of revisions that must exist
261 marking_t mark = mm[n->first];
262 checked_revisions[mark.birth_revision].marking_refs++;
263 if (!checked_revisions[mark.birth_revision].found)
264 checked_rosters[ros_id].missing_mark_revs++;
265
266 for (set<revision_id>::const_iterator r = mark.parent_name.begin();
267 r != mark.parent_name.end(); r++)
268 {
269 checked_revisions[*r].marking_refs++;
270 if (!checked_revisions[*r].found)
271 checked_rosters[ros_id].missing_mark_revs++;
272 }
273
274 for (set<revision_id>::const_iterator r = mark.file_content.begin();
275 r != mark.file_content.end(); r++)
276 {
277 checked_revisions[*r].marking_refs++;
278 if (!checked_revisions[*r].found)
279 checked_rosters[ros_id].missing_mark_revs++;
280 }
281
282 for (map<attr_key,set<revision_id> >::const_iterator attr =
283 mark.attrs.begin(); attr != mark.attrs.end(); attr++)
284 for (set<revision_id>::const_iterator r = attr->second.begin();
285 r != attr->second.end(); r++)
286 {
287 checked_revisions[*r].marking_refs++;
288 if (!checked_revisions[*r].found)
289 checked_rosters[ros_id].missing_mark_revs++;
290 }
291 }
292 ++ticks;
293 }
294}
295
296static void
297check_roster_links(app_state & app,
298 map<revision_id, checked_revision> & checked_revisions,
299 map<roster_id, checked_roster> & checked_rosters,
300 size_t & unreferenced_roster_links,
301 size_t & missing_rosters)
302{
303 unreferenced_roster_links = 0;
304
305 map<revision_id, roster_id> links;
306 app.db.get_roster_links(links);
307
308 for (map<revision_id, roster_id>::const_iterator i = links.begin();
309 i != links.end(); ++i)
310 {
311 revision_id rev(i->first);
312 roster_id ros(i->second);
313
314 map<revision_id, checked_revision>::const_iterator j
315 = checked_revisions.find(rev);
316 if (j == checked_revisions.end() || (!j->second.found))
317 ++unreferenced_roster_links;
318
319 map<roster_id, checked_roster>::const_iterator k
320 = checked_rosters.find(ros);
321 if (k == checked_rosters.end() || (!k->second.found))
322 ++missing_rosters;
323 }
324}
325
326
327static void
328check_revisions(app_state & app,
329 map<revision_id, checked_revision> & checked_revisions,
330 map<roster_id, checked_roster> & checked_rosters,
331 set<manifest_id> const & found_manifests)
332{
333 set<revision_id> revisions;
334
335 app.db.get_revision_ids(revisions);
336 L(FL("checking %d revisions") % revisions.size());
337
338 ticker ticks(_("revisions"), "r", revisions.size()/70+1);
339
340 for (set<revision_id>::const_iterator i = revisions.begin();
341 i != revisions.end(); ++i)
342 {
343 L(FL("checking revision %s") % *i);
344 revision_data data;
345 app.db.get_revision(*i, data);
346 checked_revisions[*i].found = true;
347
348 revision_t rev;
349 try
350 {
351 read_revision(data, rev);
352 }
353 catch (logic_error & e)
354 {
355 L(FL("error parsing revision %s: %s") % *i % e.what());
356 checked_revisions[*i].parseable = false;
357 continue;
358 }
359 checked_revisions[*i].parseable = true;
360
361 // normalisation check
362 revision_id norm_ident;
363 revision_data norm_data;
364 write_revision(rev, norm_data);
365 calculate_ident(norm_data, norm_ident);
366 if (norm_ident == *i)
367 checked_revisions[*i].normalized = true;
368
369 // roster checks
370 if (app.db.roster_link_exists_for_revision(*i))
371 {
372 roster_id ros_id;
373 checked_revisions[*i].found_roster_link = true;
374 app.db.get_roster_id_for_revision(*i, ros_id);
375 if (app.db.roster_exists_for_revision(*i))
376 {
377 checked_revisions[*i].found_roster = true;
378 I(checked_rosters[ros_id].found);
379 checked_rosters[ros_id].revision_refs++;
380 if (!(rev.new_manifest == checked_rosters[ros_id].man_id))
381 checked_revisions[*i].manifest_mismatch = true;
382 if (checked_rosters[ros_id].missing_files > 0)
383 checked_revisions[*i].incomplete_roster = true;
384 }
385 }
386
387 if (found_manifests.find(rev.new_manifest) == found_manifests.end())
388 checked_revisions[*i].missing_manifests++;
389
390 for (edge_map::const_iterator edge = rev.edges.begin();
391 edge != rev.edges.end(); ++edge)
392 {
393 // ignore [] -> [...] revisions
394
395 // delay checking parents until we've processed all revisions
396 if (!null_id(edge_old_revision(edge)))
397 {
398 checked_revisions[edge_old_revision(edge)].revision_refs++;
399 checked_revisions[*i].parents.insert(edge_old_revision(edge));
400 }
401
402 // also check that change_sets applied to old manifests == new
403 // manifests (which might be a merge)
404 }
405
406 ++ticks;
407 }
408
409 // now check for parent revision existence and problems
410
411 for (map<revision_id, checked_revision>::iterator
412 revision = checked_revisions.begin();
413 revision != checked_revisions.end(); ++revision)
414 {
415 for (set<revision_id>::const_iterator p = revision->second.parents.begin();
416 p != revision->second.parents.end(); ++p)
417 {
418 if (!checked_revisions[*p].found)
419 revision->second.missing_revisions++;
420 }
421 }
422
423 L(FL("checked %d revisions after starting with %d")
424 % checked_revisions.size()
425 % revisions.size());
426}
427
428static void
429check_ancestry(app_state & app,
430 map<revision_id, checked_revision> & checked_revisions)
431{
432 multimap<revision_id, revision_id> graph;
433
434 app.db.get_revision_ancestry(graph);
435 L(FL("checking %d ancestry edges") % graph.size());
436
437 ticker ticks(_("ancestry"), "a", graph.size()/70+1);
438
439 // checked revision has set of parents
440 // graph has revision and associated parents
441 // these two representations of the graph should agree!
442
443 set<revision_id> seen;
444 for (multimap<revision_id, revision_id>::const_iterator i = graph.begin();
445 i != graph.end(); ++i)
446 {
447 // ignore the [] -> [...] edges here too
448 if (!null_id(i->first))
449 {
450 checked_revisions[i->first].ancestry_parent_refs++;
451
452 if (!null_id(i->second))
453 checked_revisions[i->second].ancestry_child_refs++;
454 }
455
456 ++ticks;
457 }
458}
459
460static void
461check_keys(app_state & app,
462 map<rsa_keypair_id, checked_key> & checked_keys)
463{
464 vector<rsa_keypair_id> pubkeys;
465
466 app.db.get_public_keys(pubkeys);
467
468 L(FL("checking %d public keys") % pubkeys.size());
469
470 ticker ticks(_("keys"), "k", 1);
471
472 for (vector<rsa_keypair_id>::const_iterator i = pubkeys.begin();
473 i != pubkeys.end(); ++i)
474 {
475 app.db.get_key(*i, checked_keys[*i].pub_encoded);
476 checked_keys[*i].found = true;
477 ++ticks;
478 }
479
480}
481
482static void
483check_certs(app_state & app,
484 map<revision_id, checked_revision> & checked_revisions,
485 map<rsa_keypair_id, checked_key> & checked_keys,
486 size_t & total_certs)
487{
488
489 vector< revision<cert> > certs;
490 app.db.get_revision_certs(certs);
491
492 total_certs = certs.size();
493
494 L(FL("checking %d revision certs") % certs.size());
495
496 ticker ticks(_("certs"), "c", certs.size()/70+1);
497
498 for (vector< revision<cert> >::const_iterator i = certs.begin();
499 i != certs.end(); ++i)
500 {
501 checked_cert checked(*i);
502 checked.found_key = checked_keys[i->inner().key].found;
503
504 if (checked.found_key)
505 {
506 string signed_text;
507 cert_signable_text(i->inner(), signed_text);
508 checked.good_sig = check_signature(app, i->inner().key,
509 checked_keys[i->inner().key].pub_encoded,
510 signed_text, i->inner().sig);
511 }
512
513 checked_keys[i->inner().key].sigs++;
514 checked_revisions[i->inner().ident].checked_certs.push_back(checked);
515
516 ++ticks;
517 }
518}
519
520static void
521report_files(map<file_id, checked_file> const & checked_files,
522 size_t & missing_files,
523 size_t & unreferenced_files)
524{
525 for (map<file_id, checked_file>::const_iterator
526 i = checked_files.begin(); i != checked_files.end(); ++i)
527 {
528 checked_file file = i->second;
529
530 if (!file.found)
531 {
532 missing_files++;
533 P(F("file %s missing (%d manifest references)")
534 % i->first % file.roster_refs);
535 }
536
537 if (file.roster_refs == 0)
538 {
539 unreferenced_files++;
540 P(F("file %s unreferenced") % i->first);
541 }
542
543 }
544}
545
546static void
547report_rosters(map<roster_id, checked_roster> const & checked_rosters,
548 size_t & unreferenced_rosters,
549 size_t & incomplete_rosters,
550 size_t & non_parseable_rosters,
551 size_t & non_normalized_rosters)
552{
553 for (map<roster_id, checked_roster>::const_iterator
554 i = checked_rosters.begin(); i != checked_rosters.end(); ++i)
555 {
556 checked_roster roster = i->second;
557
558 if (roster.revision_refs == 0)
559 {
560 unreferenced_rosters++;
561 P(F("roster %s unreferenced") % i->first);
562 }
563
564 if (roster.missing_files > 0)
565 {
566 incomplete_rosters++;
567 P(F("roster %s incomplete (%d missing files)")
568 % i->first % roster.missing_files);
569 }
570
571 if (roster.missing_mark_revs > 0)
572 {
573 incomplete_rosters++;
574 P(F("roster %s incomplete (%d missing revisions)")
575 % i->first % roster.missing_mark_revs);
576 }
577
578 if (!roster.parseable)
579 {
580 non_parseable_rosters++;
581 P(F("roster %s is not parseable (perhaps with unnormalized paths?)")
582 % i->first);
583 }
584
585 if (roster.parseable && !roster.normalized)
586 {
587 non_normalized_rosters++;
588 P(F("roster %s is not in normalized form")
589 % i->first);
590 }
591 }
592}
593
594static void
595report_revisions(map<revision_id, checked_revision> const & checked_revisions,
596 size_t & missing_revisions,
597 size_t & incomplete_revisions,
598 size_t & mismatched_parents,
599 size_t & mismatched_children,
600 size_t & manifest_mismatch,
601 size_t & bad_history,
602 size_t & non_parseable_revisions,
603 size_t & non_normalized_revisions)
604{
605 for (map<revision_id, checked_revision>::const_iterator
606 i = checked_revisions.begin(); i != checked_revisions.end(); ++i)
607 {
608 checked_revision revision = i->second;
609
610 if (!revision.found)
611 {
612 missing_revisions++;
613 P(F("revision %s missing (%d revision references; %d cert references; %d parent references; %d child references; %d roster references)")
614 % i->first % revision.revision_refs % revision.cert_refs % revision.ancestry_parent_refs
615 % revision.ancestry_child_refs % revision.marking_refs);
616 }
617
618 if (revision.missing_manifests > 0)
619 {
620 incomplete_revisions++;
621 P(F("revision %s incomplete (%d missing manifests)")
622 % i->first % revision.missing_manifests);
623 }
624
625 if (revision.missing_revisions > 0)
626 {
627 incomplete_revisions++;
628 P(F("revision %s incomplete (%d missing revisions)")
629 % i->first % revision.missing_revisions);
630 }
631
632 if (!revision.found_roster_link)
633 {
634 incomplete_revisions++;
635 P(F("revision %s incomplete (missing roster link)") % i->first);
636 }
637
638 if (!revision.found_roster)
639 {
640 incomplete_revisions++;
641 P(F("revision %s incomplete (missing roster)") % i->first);
642 }
643
644 if (revision.manifest_mismatch)
645 {
646 manifest_mismatch++;
647 P(F("revision %s mismatched roster and manifest") % i->first);
648 }
649
650 if (revision.incomplete_roster)
651 {
652 incomplete_revisions++;
653 P(F("revision %s incomplete (incomplete roster)") % i->first);
654 }
655
656 if (revision.ancestry_parent_refs != revision.revision_refs)
657 {
658 mismatched_parents++;
659 P(F("revision %s mismatched parents (%d ancestry parents; %d revision refs)")
660 % i->first
661 % revision.ancestry_parent_refs
662 % revision.revision_refs );
663 }
664
665 if (revision.ancestry_child_refs != revision.parents.size())
666 {
667 mismatched_children++;
668 P(F("revision %s mismatched children (%d ancestry children; %d parents)")
669 % i->first
670 % revision.ancestry_child_refs
671 % revision.parents.size() );
672 }
673
674 if (!revision.history_error.empty())
675 {
676 bad_history++;
677 string tmp = revision.history_error;
678 if (tmp[tmp.length() - 1] == '\n')
679 tmp.erase(tmp.length() - 1);
680 P(F("revision %s has bad history (%s)")
681 % i->first % tmp);
682 }
683
684 if (!revision.parseable)
685 {
686 non_parseable_revisions++;
687 P(F("revision %s is not parseable (perhaps with unnormalized paths?)")
688 % i->first);
689 }
690
691 if (revision.parseable && !revision.normalized)
692 {
693 non_normalized_revisions++;
694 P(F("revision %s is not in normalized form")
695 % i->first);
696 }
697 }
698}
699
700static void
701report_keys(map<rsa_keypair_id, checked_key> const & checked_keys,
702 size_t & missing_keys)
703{
704 for (map<rsa_keypair_id, checked_key>::const_iterator
705 i = checked_keys.begin(); i != checked_keys.end(); ++i)
706 {
707 checked_key key = i->second;
708
709 if (key.found)
710 {
711 L(FL("key %s signed %d certs")
712 % i->first
713 % key.sigs);
714 }
715 else
716 {
717 missing_keys++;
718 P(F("key %s missing (signed %d certs)")
719 % i->first
720 % key.sigs);
721 }
722 }
723}
724
725static void
726report_certs(map<revision_id, checked_revision> const & checked_revisions,
727 size_t & missing_certs,
728 size_t & mismatched_certs,
729 size_t & unchecked_sigs,
730 size_t & bad_sigs)
731{
732 set<cert_name> cnames;
733
734 cnames.insert(cert_name(author_cert_name));
735 cnames.insert(cert_name(branch_cert_name));
736 cnames.insert(cert_name(changelog_cert_name));
737 cnames.insert(cert_name(date_cert_name));
738
739 for (map<revision_id, checked_revision>::const_iterator
740 i = checked_revisions.begin(); i != checked_revisions.end(); ++i)
741 {
742 checked_revision revision = i->second;
743 map<cert_name, size_t> cert_counts;
744
745 for (vector<checked_cert>::const_iterator checked = revision.checked_certs.begin();
746 checked != revision.checked_certs.end(); ++checked)
747 {
748 if (!checked->found_key)
749 {
750 unchecked_sigs++;
751 P(F("revision %s unchecked signature in %s cert from missing key %s")
752 % i->first
753 % checked->rcert.inner().name
754 % checked->rcert.inner().key);
755 }
756 else if (!checked->good_sig)
757 {
758 bad_sigs++;
759 P(F("revision %s bad signature in %s cert from key %s")
760 % i->first
761 % checked->rcert.inner().name
762 % checked->rcert.inner().key);
763 }
764
765 cert_counts[checked->rcert.inner().name]++;
766 }
767
768 for (set<cert_name>::const_iterator n = cnames.begin();
769 n != cnames.end(); ++n)
770 {
771 if (revision.found && cert_counts[*n] == 0)
772 {
773 missing_certs++;
774 P(F("revision %s missing %s cert") % i->first % *n);
775 }
776 }
777
778 if (cert_counts[cert_name(author_cert_name)] != cert_counts[cert_name(changelog_cert_name)] ||
779 cert_counts[cert_name(author_cert_name)] != cert_counts[cert_name(date_cert_name)] ||
780 cert_counts[cert_name(date_cert_name)] != cert_counts[cert_name(changelog_cert_name)])
781 {
782 mismatched_certs++;
783 P(F("revision %s mismatched certs (%d authors %d dates %d changelogs)")
784 % i->first
785 % cert_counts[cert_name(author_cert_name)]
786 % cert_counts[cert_name(date_cert_name)]
787 % cert_counts[cert_name(changelog_cert_name)]);
788 }
789
790 }
791}
792
793void
794check_db(app_state & app)
795{
796 map<file_id, checked_file> checked_files;
797 set<manifest_id> found_manifests;
798 map<roster_id, checked_roster> checked_rosters;
799 map<revision_id, checked_revision> checked_revisions;
800 map<rsa_keypair_id, checked_key> checked_keys;
801
802 size_t missing_files = 0;
803 size_t unreferenced_files = 0;
804
805 size_t missing_rosters = 0;
806 size_t unreferenced_rosters = 0;
807 size_t incomplete_rosters = 0;
808 size_t non_parseable_rosters = 0;
809 size_t non_normalized_rosters = 0;
810 size_t unreferenced_roster_links = 0;
811
812 size_t missing_revisions = 0;
813 size_t incomplete_revisions = 0;
814 size_t mismatched_parents = 0;
815 size_t mismatched_children = 0;
816 size_t bad_history = 0;
817 size_t non_parseable_revisions = 0;
818 size_t non_normalized_revisions = 0;
819
820 size_t missing_keys = 0;
821
822 size_t total_certs = 0;
823 size_t missing_certs = 0;
824 size_t mismatched_certs = 0;
825 size_t manifest_mismatch = 0;
826 size_t unchecked_sigs = 0;
827 size_t bad_sigs = 0;
828
829 check_db_integrity_check(app);
830 check_files(app, checked_files);
831 check_rosters_manifest(app, checked_rosters, checked_revisions,
832 found_manifests, checked_files);
833 check_revisions(app, checked_revisions, checked_rosters, found_manifests);
834 check_rosters_marking(app, checked_rosters, checked_revisions);
835 check_roster_links(app, checked_revisions, checked_rosters,
836 unreferenced_roster_links,
837 missing_rosters);
838 check_ancestry(app, checked_revisions);
839 check_keys(app, checked_keys);
840 check_certs(app, checked_revisions, checked_keys, total_certs);
841
842 report_files(checked_files, missing_files, unreferenced_files);
843
844 report_rosters(checked_rosters,
845 unreferenced_rosters,
846 incomplete_rosters,
847 non_parseable_rosters,
848 non_normalized_rosters);
849
850 report_revisions(checked_revisions,
851 missing_revisions, incomplete_revisions,
852 mismatched_parents, mismatched_children,
853 manifest_mismatch,
854 bad_history, non_parseable_revisions,
855 non_normalized_revisions);
856
857 report_keys(checked_keys, missing_keys);
858
859 report_certs(checked_revisions,
860 missing_certs, mismatched_certs,
861 unchecked_sigs, bad_sigs);
862
863 // NOTE: any new sorts of problems need to have added:
864 // -- a message here, that tells the use about them
865 // -- entries in one _or both_ of the sums calculated at the end
866 // -- an entry added to the manual, which describes in detail why the
867 // error occurs and what it means to the user
868
869 if (missing_files > 0)
870 W(F("%d missing files") % missing_files);
871 if (unreferenced_files > 0)
872 W(F("%d unreferenced files") % unreferenced_files);
873
874 if (unreferenced_rosters > 0)
875 W(F("%d unreferenced rosters") % unreferenced_rosters);
876 if (incomplete_rosters > 0)
877 W(F("%d incomplete rosters") % incomplete_rosters);
878 if (non_parseable_rosters > 0)
879 W(F("%d rosters not parseable (perhaps with invalid paths)")
880 % non_parseable_rosters);
881 if (non_normalized_rosters > 0)
882 W(F("%d rosters not in normalized form") % non_normalized_rosters);
883
884 if (missing_revisions > 0)
885 W(F("%d missing revisions") % missing_revisions);
886 if (incomplete_revisions > 0)
887 W(F("%d incomplete revisions") % incomplete_revisions);
888 if (mismatched_parents > 0)
889 W(F("%d mismatched parents") % mismatched_parents);
890 if (mismatched_children > 0)
891 W(F("%d mismatched children") % mismatched_children);
892 if (bad_history > 0)
893 W(F("%d revisions with bad history") % bad_history);
894 if (non_parseable_revisions > 0)
895 W(F("%d revisions not parseable (perhaps with invalid paths)")
896 % non_parseable_revisions);
897 if (non_normalized_revisions > 0)
898 W(F("%d revisions not in normalized form") % non_normalized_revisions);
899
900
901 if (unreferenced_roster_links > 0)
902 W(F("%d unreferenced roster links") % unreferenced_roster_links);
903
904 if (missing_rosters > 0)
905 W(F("%d missing rosters") % missing_rosters);
906
907
908 if (missing_keys > 0)
909 W(F("%d missing keys") % missing_keys);
910
911 if (missing_certs > 0)
912 W(F("%d missing certs") % missing_certs);
913 if (mismatched_certs > 0)
914 W(F("%d mismatched certs") % mismatched_certs);
915 if (unchecked_sigs > 0)
916 W(F("%d unchecked signatures due to missing keys") % unchecked_sigs);
917 if (bad_sigs > 0)
918 W(F("%d bad signatures") % bad_sigs);
919
920 size_t total = missing_files + unreferenced_files +
921 unreferenced_rosters + incomplete_rosters +
922 non_parseable_rosters + non_normalized_rosters +
923 missing_revisions + incomplete_revisions +
924 non_parseable_revisions + non_normalized_revisions +
925 mismatched_parents + mismatched_children +
926 bad_history +
927 unreferenced_roster_links + missing_rosters +
928 missing_certs + mismatched_certs +
929 unchecked_sigs + bad_sigs +
930 missing_keys;
931 // unreferenced files and rosters and mismatched certs are not actually
932 // serious errors; odd, but nothing will break.
933 size_t serious = missing_files +
934 incomplete_rosters + missing_rosters +
935 non_parseable_rosters + non_normalized_rosters +
936 missing_revisions + incomplete_revisions +
937 non_parseable_revisions + non_normalized_revisions +
938 mismatched_parents + mismatched_children + manifest_mismatch +
939 bad_history +
940 missing_certs +
941 unchecked_sigs + bad_sigs +
942 missing_keys;
943
944 P(F("check complete: %d files; %d rosters; %d revisions; %d keys; %d certs")
945 % checked_files.size()
946 % checked_rosters.size()
947 % checked_revisions.size()
948 % checked_keys.size()
949 % total_certs);
950 P(F("total problems detected: %d (%d serious)") % total % serious);
951 if (serious)
952 E(false, F("serious problems detected"));
953 else if (total)
954 P(F("minor problems detected"));
955 else
956 P(F("database is good"));
957}
958
959// Local Variables:
960// mode: C++
961// fill-column: 76
962// c-file-style: "gnu"
963// indent-tabs-mode: nil
964// End:
965// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status