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