Index: subversion/include/svn_fs.h =================================================================== --- subversion/include/svn_fs.h (revision 6521) +++ subversion/include/svn_fs.h (working copy) @@ -1327,18 +1327,30 @@ /** Populate @a *uuid with the UUID associated with @a fs. */ svn_error_t * -svn_fs_get_uuid(svn_fs_t *fs, - const char **uuid, - apr_pool_t *pool); +svn_fs_get_uuid (svn_fs_t *fs, + const char **uuid, + apr_pool_t *pool); /** Associate @a *uuid with @a fs. */ svn_error_t * -svn_fs_set_uuid(svn_fs_t *fs, - const char *uuid, - apr_pool_t *pool); +svn_fs_set_uuid (svn_fs_t *fs, + const char *uuid, + apr_pool_t *pool); + +/* Verify */ + +/** Verifies all the checksummed data stored in the filesystem associated + * with @ fs (file contents, file and directory property lists, and + * directory entries lists). */ +svn_error_t * +svn_fs_verify_contents (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 6521) +++ 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,82 @@ return SVN_NO_ERROR; } + + +svn_error_t * +svn_fs__bdb_walk_reps (svn_fs_t *fs, + svn_error_t *(*rep_handler) (svn_fs_t *fs, + const char *rep_key, + trail_t *trail), + 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; + const char *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; + + /* Change key into a null-terminated string for use by the handler */ + rep_key = apr_pstrmemdup (subpool, key.data, key.size); + + /* Run the handler */ + error = (*rep_handler) (fs, rep_key, 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 6521) +++ subversion/libsvn_fs/bdb/reps-table.h (working copy) @@ -74,6 +74,17 @@ trail_t *trail); + +/*** Walking the representation table. ***/ + +/* 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_error_t *(*rep_handler) (svn_fs_t *fs, + const char *rep_key, + trail_t *trail), + trail_t *trail); + #ifdef __cplusplus } #endif /* __cplusplus */ Index: subversion/libsvn_fs/fs.c =================================================================== --- subversion/libsvn_fs/fs.c (revision 6521) +++ 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,66 @@ +/* Verifying data integrity */ + +/* A simple callback function used when walking the reps */ +static svn_error_t * +verify_reps_handler (svn_fs_t *fs, + const char *rep_key, + trail_t *trail) +{ + svn_stream_t *read_stream; + apr_size_t len; + apr_pool_t *subpool; + char *block; + + /* It is expected that this function will perform the checksum as the + chunks are being read in. */ + SVN_ERR (svn_fs__rep_contents_read_stream (&read_stream, fs, rep_key, + TRUE, trail, trail->pool)); + + subpool = svn_pool_create (trail->pool); + block = apr_palloc (subpool, SVN_STREAM_CHUNK_SIZE); + + /* Iterate through the entire rep and rely on read_stream to + flag an error on an invalid checksum. */ + do + { + len = SVN_STREAM_CHUNK_SIZE; + SVN_ERR (svn_stream_read (read_stream, block, &len)); + } + while (len); + + svn_pool_destroy (subpool); + + return SVN_NO_ERROR; +} + +/* A simple callback function used when initiating the + walking of the repository */ +static svn_error_t * +txn_body_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_contents (svn_fs_t *fs, + apr_pool_t *pool) +{ + SVN_ERR (svn_fs__retry_txn (fs, txn_body_verify_reps, fs, pool)); + + return SVN_NO_ERROR; +} + + + /* Miscellany */ const char * Index: subversion/libsvn_fs/reps-strings.h =================================================================== --- subversion/libsvn_fs/reps-strings.h (revision 6521) +++ subversion/libsvn_fs/reps-strings.h (working copy) @@ -103,7 +103,9 @@ If USE_TRAIL_FOR_READS is TRUE, the stream's reads are part of TRAIL; otherwise, each read happens in an internal, one-off - trail (though TRAIL is still required). POOL may be TRAIL->pool. */ + trail (though TRAIL is still required). POOL may be TRAIL->pool. + + Note: this function will verify the checksum as chunks are being read. */ svn_error_t * svn_fs__rep_contents_read_stream (svn_stream_t **rs_p, svn_fs_t *fs, Index: subversion/svnadmin/main.c =================================================================== --- subversion/svnadmin/main.c (revision 6521) +++ 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_contents (fs, pool)); + + return SVN_NO_ERROR; +} + /** Main. **/