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