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