summaryrefslogtreecommitdiff
path: root/src/verify.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/verify.c')
-rw-r--r--src/verify.c130
1 files changed, 130 insertions, 0 deletions
diff --git a/src/verify.c b/src/verify.c
new file mode 100644
index 0000000..1959012
--- /dev/null
+++ b/src/verify.c
@@ -0,0 +1,130 @@
+/*
+** Copyright (c) 2007 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*******************************************************************************
+**
+** This file contains code used to help verify the integrity of
+** the repository.
+**
+** This file primarily implements the verify_before_commit() interface.
+** Any function can call verify_before_commit() with a record id (RID)
+** as an argument. Then before the next change to the database commits,
+** this routine will reach in and check that the record can be extracted
+** correctly from the BLOB table.
+*/
+#include "config.h"
+#include "verify.h"
+#include <assert.h>
+
+/*
+** Load the record identify by rid. Make sure we can reproduce it
+** without error.
+**
+** Panic if anything goes wrong. If this procedure returns it means
+** that everything is OK.
+*/
+static void verify_rid(int rid){
+ Blob uuid, content;
+ if( content_size(rid, 0)<0 ){
+ return; /* No way to verify phantoms */
+ }
+ blob_zero(&uuid);
+ db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d", rid);
+ if( !hname_validate(blob_buffer(&uuid), blob_size(&uuid)) ){
+ fossil_fatal("not a valid rid: %d", rid);
+ }
+ if( content_get(rid, &content) ){
+ if( !hname_verify_hash(&content, blob_buffer(&uuid), blob_size(&uuid)) ){
+ fossil_panic("hash of rid %d does not match its uuid (%b)",
+ rid, &uuid);
+ }
+ blob_reset(&content);
+ }
+ blob_reset(&uuid);
+}
+
+/*
+** The following bag holds the rid for every record that needs
+** to be verified.
+*/
+static Bag toVerify;
+static int inFinalVerify = 0;
+
+/*
+** This routine is called just prior to each commit operation.
+**
+** Invoke verify_rid() on every record that has been added or modified
+** in the repository, in order to make sure that the repository is sane.
+*/
+static int verify_at_commit(void){
+ int rid;
+ content_clear_cache(0);
+ inFinalVerify = 1;
+ rid = bag_first(&toVerify);
+ while( rid>0 ){
+ verify_rid(rid);
+ rid = bag_next(&toVerify, rid);
+ }
+ bag_clear(&toVerify);
+ inFinalVerify = 0;
+ return 0;
+}
+
+/*
+** Arrange to verify a particular record prior to committing.
+**
+** If the record rid is less than 1, then just initialize the
+** verification system but do not record anything as needing
+** verification.
+*/
+void verify_before_commit(int rid){
+ static int isInit = 0;
+ if( !isInit ){
+ db_commit_hook(verify_at_commit, 1000);
+ isInit = 1;
+ }
+ assert( !inFinalVerify );
+ if( rid>0 ){
+ bag_insert(&toVerify, rid);
+ }
+}
+
+/*
+** Cancel all pending verification operations.
+*/
+void verify_cancel(void){
+ bag_clear(&toVerify);
+}
+
+/*
+** COMMAND: test-verify-all
+**
+** Verify all records in the repository.
+*/
+void verify_all_cmd(void){
+ Stmt q;
+ int cnt = 0;
+ db_must_be_within_tree();
+ db_prepare(&q, "SELECT rid FROM blob");
+ while( db_step(&q)==SQLITE_ROW ){
+ int rid = db_column_int(&q, 0);
+ verify_before_commit(rid);
+ cnt++;
+ assert( bag_count(&toVerify)==cnt );
+ }
+ db_finalize(&q);
+ verify_at_commit();
+ assert( bag_count(&toVerify)==0 );
+}