[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

[PATCH] #1099 ra_svn needs to do checksumming too

From: Eric Gillespie <epg_at_pretzelnet.org>
Date: 2003-02-03 00:44:37 CET

Here is my first crack at fixing this issue. Yes, yes, i know
i'm supposed to be finishing up the add-horkage patch, but when i
found ra_svn was broken yesterday, i found out about ra_svn not
having checksumming and got interested in this first. So sue me!

To test the apply_textdelta changes, change line 243 of editor.c
from:

  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "apply-textdelta", "ccc",
                               b->token,
                               (base_checksum ? base_checksum : ""),
                               (result_checksum ? result_checksum : "")));

to:

  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "apply-textdelta", "ccc",
                               b->token,
                               (base_checksum ? "broke" : ""),
                               (result_checksum ? "ekorb" : "")));

then run basic_tests.py.

To test get_file changes, change line 375 of serve.c
from:

  hex_digest = svn_md5_digest_to_cstring(digest, pool);

to:

  hex_digest = "broke";

then run that svnserve and svn cat a file from it. Those two
tests are how i verified that the new code does what it's
supposed to. This patch passes make svnchec,.

Patch and log message:

Hook ra_svn up with checksumming action.

Big thanks to Greg Hudson for answering innumerable questions
about ra_svn, and suggesting most of how this works. The rest is
inspired by ra_dav's checksumming support.

* subversion/libsvn_ra_svn/client.c: Include <apr_md5.h> and "svn_md5.h".
  (ra_svn_get_file): Read a checksum from the get-file response.
    Calculate a checksum as the file is read, and compare it to the
    provided checksum.

* subversion/libsvn_ra_svn/editor.c: Include <apr_md5.h> and "svn_md5.h".
  (ra_svn_svndiff_close): Read a command response in order to catch
    errors decoding the svndiff (this is where any checksum errors
    found by the receiver will be reported).
  (ra_svn_apply_textdelta): Send base_checksum and result_checksum
    along with the token in the apply-textdelta command (turning NULL
    pointers into "").
  (ra_svn_handle_apply_textdelta): Read base_checksum and
    result_checksum from the sender (turning "" into NULL pointers),
    and pass them along to apply_textdelta. Wrap the svn_stream_close
    call with SVN_CMD_ERR so checksum errors will be reported to the
    sender, otherwise send an empty command response so the sender
    knows it succeeded.

* subversion/svnserve/serve.c: Include <apr_md5.h> and "svn_md5.h".

  (get_file): Get the checksum for the file and send it in the
    get-file response.

Index: subversion/libsvn_ra_svn/client.c
===================================================================
--- subversion/libsvn_ra_svn/client.c (revision 4701)
+++ subversion/libsvn_ra_svn/client.c (working copy)
@@ -24,6 +24,7 @@
 #include <apr_lib.h>
 #include <apr_strings.h>
 #include <apr_network_io.h>
+#include <apr_md5.h>
 
 #include "svn_types.h"
 #include "svn_string.h"
@@ -34,6 +35,7 @@
 #include "svn_config.h"
 #include "svn_ra.h"
 #include "svn_ra_svn.h"
+#include "svn_md5.h"
 
 #include "ra_svn.h"
 
@@ -505,10 +507,15 @@ static svn_error_t *ra_svn_get_file(void
   apr_pool_t *pool = conn->pool;
   svn_ra_svn_item_t *item;
   apr_array_header_t *proplist;
+ unsigned char digest[MD5_DIGESTSIZE];
+ const char *expected_checksum, *hex_digest;
+ apr_md5_ctx_t md5_context;
 
   SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "get-file", "c[r]bb", path,
                                rev, (props != NULL), (stream != NULL)));
- SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "rl", &rev, &proplist));
+ SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "crl",
+ &expected_checksum,
+ &rev, &proplist));
 
   if (fetched_rev)
     *fetched_rev = rev;
@@ -519,6 +526,8 @@ static svn_error_t *ra_svn_get_file(void
   if (!stream)
     return SVN_NO_ERROR;
 
+ apr_md5_init(&md5_context);
+
   /* Read the file's contents. */
   while (1)
     {
@@ -528,10 +537,23 @@ static svn_error_t *ra_svn_get_file(void
                                 "Non-string as part of file contents");
       if (item->u.string->len == 0)
         break;
- if (stream)
- SVN_ERR(svn_stream_write(stream, item->u.string->data,
- &item->u.string->len));
+
+ apr_md5_update(&md5_context, item->u.string->data, item->u.string->len);
+
+ SVN_ERR(svn_stream_write(stream, item->u.string->data,
+ &item->u.string->len));
     }
+
+ apr_md5_final(digest, &md5_context);
+ hex_digest = svn_md5_digest_to_cstring(digest, pool);
+ if (strcmp (hex_digest, expected_checksum) != 0)
+ return svn_error_createf
+ (SVN_ERR_CHECKSUM_MISMATCH, NULL,
+ "ra_svn_get_file: checksum mismatch for '%s':\n"
+ " expected checksum: %s\n"
+ " actual checksum: %s\n",
+ path, expected_checksum, hex_digest);
+
   SVN_ERR(svn_stream_close(stream));
   return SVN_NO_ERROR;
 }
Index: subversion/libsvn_ra_svn/editor.c
===================================================================
--- subversion/libsvn_ra_svn/editor.c (revision 4701)
+++ subversion/libsvn_ra_svn/editor.c (working copy)
@@ -23,6 +23,7 @@
 #include <apr_general.h>
 #include <apr_lib.h>
 #include <apr_strings.h>
+#include <apr_md5.h>
 
 #include <assert.h>
 
@@ -32,6 +33,7 @@
 #include "svn_delta.h"
 #include "svn_ra_svn.h"
 #include "svn_pools.h"
+#include "svn_md5.h"
 
 #include "ra_svn.h"
 
@@ -222,7 +224,10 @@ static svn_error_t *ra_svn_svndiff_close
 {
   ra_svn_baton_t *b = baton;
 
- return svn_ra_svn_write_cstring(b->conn, b->pool, "");
+ SVN_ERR(svn_ra_svn_write_cstring(b->conn, b->pool, ""));
+
+ /* Check for errors decoding the svndiff. */
+ return svn_ra_svn_read_cmd_response(b->conn, b->pool, "");
 }
 
 static svn_error_t *ra_svn_apply_textdelta(void *file_baton,
@@ -237,8 +242,10 @@ static svn_error_t *ra_svn_apply_textdel
   svn_boolean_t wanted;
 
   /* Tell the other side we're starting a text delta. */
- SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "apply-textdelta", "c",
- b->token));
+ SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "apply-textdelta", "ccc",
+ b->token,
+ (base_checksum ? base_checksum : ""),
+ (result_checksum ? result_checksum : "")));
   SVN_ERR(svn_ra_svn_read_cmd_response(b->conn, pool, "b", &wanted));
 
   if (wanted)
@@ -559,12 +566,21 @@ static svn_error_t *ra_svn_handle_apply_
   svn_stream_t *stream;
   apr_pool_t *subpool;
   svn_ra_svn_item_t *item;
+ char *base_checksum, *result_checksum;
 
   /* Parse arguments, make the editor call, and respond. */
- SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c", &token));
+ SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "ccc", &token,
+ &base_checksum, &result_checksum));
+
+ if (base_checksum[0] == '\0')
+ base_checksum = NULL;
+ if (result_checksum[0] == '\0')
+ result_checksum = NULL;
+
   SVN_CMD_ERR(lookup_token(ds, token, &entry, pool));
   /* ### todo#510: convert to new apply_text interface. */
- SVN_CMD_ERR(ds->editor->apply_textdelta(entry->baton, NULL, NULL,
+ SVN_CMD_ERR(ds->editor->apply_textdelta(entry->baton,
+ base_checksum, result_checksum,
                                           pool, &wh, &wh_baton));
   SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "b", (wh != NULL)));
 
@@ -592,7 +608,8 @@ static svn_error_t *ra_svn_handle_apply_
       apr_pool_clear(subpool);
     }
   apr_pool_destroy(subpool);
- SVN_ERR(svn_stream_close(stream));
+ SVN_CMD_ERR(svn_stream_close(stream));
+ SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, ""));
   return SVN_NO_ERROR;
 }
 
Index: subversion/svnserve/serve.c
===================================================================
--- subversion/svnserve/serve.c (revision 4701)
+++ subversion/svnserve/serve.c (working copy)
@@ -26,6 +26,7 @@
 #include <apr_network_io.h>
 #include <apr_user.h>
 #include <apr_file_info.h>
+#include <apr_md5.h>
 
 #include <svn_types.h>
 #include <svn_string.h>
@@ -37,6 +38,7 @@
 #include <svn_path.h>
 #include <svn_time.h>
 #include <svn_utf.h>
+#include <svn_md5.h>
 
 #include "server.h"
 
@@ -349,7 +351,7 @@ static svn_error_t *get_file(svn_ra_svn_
                              apr_array_header_t *params, void *baton)
 {
   server_baton_t *b = baton;
- const char *path, *full_path;
+ const char *path, *full_path, *hex_digest;
   svn_revnum_t rev;
   svn_fs_root_t *root;
   svn_stream_t *contents;
@@ -358,6 +360,7 @@ static svn_error_t *get_file(svn_ra_svn_
   char buf[4096];
   apr_size_t len;
   svn_boolean_t want_props, want_contents;
+ unsigned char digest[MD5_DIGESTSIZE];
 
   /* Parse arguments. */
   SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c[r]bb", &path, &rev,
@@ -368,6 +371,8 @@ static svn_error_t *get_file(svn_ra_svn_
 
   /* Fetch the properties and a stream for the contents. */
   SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool));
+ SVN_CMD_ERR(svn_fs_file_md5_checksum(digest, root, full_path, pool));
+ hex_digest = svn_md5_digest_to_cstring(digest, pool);
   if (want_props)
     SVN_CMD_ERR(get_props(&props, root, full_path, pool));
   if (want_contents)
@@ -377,6 +382,7 @@ static svn_error_t *get_file(svn_ra_svn_
   SVN_ERR(svn_ra_svn_start_list(conn, pool));
   SVN_ERR(svn_ra_svn_write_word(conn, pool, "success"));
   SVN_ERR(svn_ra_svn_start_list(conn, pool));
+ SVN_ERR(svn_ra_svn_write_cstring(conn, pool, hex_digest));
   SVN_ERR(svn_ra_svn_write_number(conn, pool, rev));
   SVN_ERR(write_proplist(conn, pool, props));
   SVN_ERR(svn_ra_svn_end_list(conn, pool));

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Mon Feb 3 00:45:32 2003

This is an archived mail posted to the Subversion Dev mailing list.