Index: subversion/include/svn_fs.h =================================================================== --- subversion/include/svn_fs.h (revision 6495) +++ subversion/include/svn_fs.h (working copy) @@ -1338,7 +1338,17 @@ const char *uuid, apr_pool_t *pool); + +/* Verify */ + +/** Verify the filesystem data associated with @ fs. */ +svn_error_t * +svn_fs_verify(svn_fs_t *fs, + apr_pool_t *pool); + + + /* Non-historical properties. */ /* [[Yes, do tell.]] */ Index: subversion/libsvn_fs/bdb/reps-table.c =================================================================== --- subversion/libsvn_fs/bdb/reps-table.c (revision 6495) +++ subversion/libsvn_fs/bdb/reps-table.c (working copy) @@ -26,6 +26,7 @@ #include "bdb-err.h" #include "reps-table.h" #include "strings-table.h" +#include "svn_pools.h" @@ -191,3 +192,76 @@ return SVN_NO_ERROR; } + + +svn_error_t *svn_fs__bdb_walk_reps (svn_fs_t *fs, + svn_fs__bdb_rep_handler_t rep_handler, + trail_t *trail) +{ + apr_size_t const next_id_key_len = strlen (svn_fs__next_key_key); + apr_pool_t *subpool = svn_pool_create (trail->pool); + + DBC *cursor; + DBT key, value; + int db_err, db_c_err; + + /* Create a database cursor to list the transaction names. */ + SVN_ERR (BDB_WRAP (fs, "reading transaction list (opening cursor)", + fs->representations->cursor (fs->representations, trail->db_txn, + &cursor, 0))); + /* Build a null-terminated array of keys in the transactions table. */ + for (db_err = cursor->c_get (cursor, + svn_fs__result_dbt (&key), + svn_fs__result_dbt (&value), + DB_FIRST); + db_err == 0; + db_err = cursor->c_get (cursor, + svn_fs__result_dbt (&key), + svn_fs__result_dbt (&value), + DB_NEXT)) + { + svn_fs__representation_t *rep; + skel_t *rep_skel; + svn_error_t *error; + svn_string_t *rep_key; + + /* Clear the per-iteration subpool */ + svn_pool_clear (subpool); + + /* Track the memory alloc'd for fetching the key and value here + so that when the containing pool is cleared, this memory is + freed. */ + svn_fs__track_dbt (&key, subpool); + svn_fs__track_dbt (&value, subpool); + + /* Ignore the "next-id" key. */ + if (key.size == next_id_key_len + && 0 == memcmp (key.data, svn_fs__next_key_key, next_id_key_len)) + continue; + + /* Turn the key into a null-terminated string to be used by the handler */ + rep_key = svn_string_ncreate(key.data, key.size, subpool); + + /* Run the handler */ + error = (*rep_handler)(fs, rep_key->data, trail); + if(error) + { + cursor->c_close(cursor); + return error; + } + } + + /* Check for errors, but close the cursor first. */ + db_c_err = cursor->c_close (cursor); + if (db_err != DB_NOTFOUND) + { + SVN_ERR (BDB_WRAP (fs, "reading representations (walking rows)", + db_err)); + } + SVN_ERR (BDB_WRAP (fs, "reading representations (closing cursor)", + db_c_err)); + /* Destroy the per-iteration subpool */ + svn_pool_destroy (subpool); + + return SVN_NO_ERROR; +} Index: subversion/libsvn_fs/bdb/reps-table.h =================================================================== --- subversion/libsvn_fs/bdb/reps-table.h (revision 6495) +++ subversion/libsvn_fs/bdb/reps-table.h (working copy) @@ -74,6 +74,20 @@ trail_t *trail); + +/*** Walking the representation table. ***/ + +typedef svn_error_t * (*svn_fs__bdb_rep_handler_t) + (svn_fs_t *fs, + const char *rep_key, + trail_t *trail); + +/* Walks the reps table calling rep_handler for each key/value + pair that is found. */ +svn_error_t *svn_fs__bdb_walk_reps(svn_fs_t *fs, + svn_fs__bdb_rep_handler_t rep_handler, + trail_t *trail); + #ifdef __cplusplus } #endif /* __cplusplus */ Index: subversion/libsvn_fs/fs.c =================================================================== --- subversion/libsvn_fs/fs.c (revision 6495) +++ subversion/libsvn_fs/fs.c (working copy) @@ -33,6 +33,8 @@ #include "err.h" #include "dag.h" #include "svn_private_config.h" +#include "reps-strings.h" +#include "svn_string.h" #include "bdb/bdb-err.h" #include "bdb/bdb_compat.h" @@ -719,6 +721,45 @@ +/* Verifying data integrity */ + +/* A simple callback function used when walking the reps */ +svn_error_t * +verify_reps_handler (svn_fs_t *fs, + const char *rep_key, + trail_t *trail) +{ + svn_string_t str; + + SVN_ERR (svn_fs__rep_contents (&str, fs, rep_key, trail)); + + return SVN_NO_ERROR; +} + +/* A simple callback function used when initiating the + walking of the repository */ +svn_error_t * +verify_reps (void *baton, + trail_t *trail) +{ + svn_fs_t *fs = baton; + + SVN_ERR (svn_fs__bdb_walk_reps(fs, verify_reps_handler, trail)); + + return SVN_NO_ERROR; +} + +/* The exposed function used to kick off the verification process */ +svn_error_t * +svn_fs_verify(svn_fs_t *fs, + apr_pool_t *pool) +{ + SVN_ERR (svn_fs__retry_txn (fs, verify_reps, fs, pool)); + + return SVN_NO_ERROR; +} + + /* Miscellany */ const char * Index: subversion/svnadmin/main.c =================================================================== --- subversion/svnadmin/main.c (revision 6495) +++ subversion/svnadmin/main.c (working copy) @@ -65,7 +65,8 @@ subcommand_lstxns, subcommand_recover, subcommand_rmtxns, - subcommand_setlog; + subcommand_setlog, + subcommand_verify; enum { @@ -211,6 +212,10 @@ "(Note that revision properties are not historied, so this command\n" "will permanently overwrite the previous log message.)\n", {'r'} }, + {"verify", subcommand_verify, {0}, + "usage: svnadmin verify REPOS_PATH\n\n" + "Verifies the data stored in the repository.\n", + {0} }, { NULL, NULL, {0}, NULL, {0} } }; @@ -596,6 +601,23 @@ } +/* This implements `svn_opt_subcommand_t'. */ +static svn_error_t * +subcommand_verify (apr_getopt_t *os, void *baton, apr_pool_t *pool) +{ + + struct svnadmin_opt_state *opt_state = baton; + svn_repos_t *repos; + svn_fs_t *fs; + + SVN_ERR (svn_repos_open (&repos, opt_state->repository_path, pool)); + fs = svn_repos_fs (repos); + + SVN_ERR (svn_fs_verify(fs, pool)); + + return SVN_NO_ERROR; +} + /** Main. **/