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

[PATCH] pre-update and pre-getfile hook

From: Mathias Weinert <mathias.weinert_at_gfa-net.de>
Date: 2006-02-06 14:11:39 CET

Hi there,

I once wrote a pre-update hook and a pre-getfile hook. They only
work for ra_svn and ra_local until now (at least they work in my
environment). The pre-update hook is used for 'svn update' and
'svn checkout' whereas the pre-getfile hook is used for things
like 'svn cat'.

I mentioned this on this list some months ago but no one seemed
to be interested. But as I received a request for it these days
I decided not only to send it to Srini who asked me for it but
to the dev list, too.

As already said these two hooks work for me and may not work in
other environments. Please regard this just as an example or a
point to start from but not as a complete solution. But may be
there is someone out there who wants to improve this and add it
to Subversion at the end.

The provided patches are built against Subversion 1.3.0.

Both hooks get the same five parameters:
REPOS: local path to repository
REVNUM: revision number requested
TARGET: target path within repository
IP_ADDR: ip address of calling client (if available)
USER: user

I haven't included an example hook script yet. So you have to
write your own one, probably starting with something like
#!/bin/bash
REPOS="$1"
REVNUM="$2"
TARGET="$3"
IP_ADDR="$4"
USER="$5"

Regards

Mathias

--- subversion/include/svn_ra_svn.h.orig 2005-09-23 12:28:00.000000000 +0200
+++ subversion/include/svn_ra_svn.h 2005-09-23 14:22:37.224279000 +0200
@@ -304,6 +304,10 @@
 svn_error_t *svn_ra_svn_write_cmd_failure(svn_ra_svn_conn_t *conn,
                                           apr_pool_t *pool, svn_error_t *err);
 
+/** Get remote IP address from connection. */
+svn_error_t *svn_ra_svn_get_ip(svn_ra_svn_conn_t *conn, char **ip_addr,
+ apr_pool_t *pool);
+
 /** Set @a *editor and @a *edit_baton to an editor which will pass editing
  * operations over the network, using @a conn and @a pool.
  *
--- subversion/include/svn_repos.h.orig 2005-09-23 12:28:02.000000000 +0200
+++ subversion/include/svn_repos.h 2005-09-23 14:22:37.318029000 +0200
@@ -332,6 +332,11 @@
 const char *svn_repos_post_revprop_change_hook (svn_repos_t *repos,
                                                 apr_pool_t *pool);
 
+/** Return the path to @a repos's pre-update hook, allocated in @a pool. */
+const char *svn_repos_pre_update_hook (svn_repos_t *repos, apr_pool_t *pool);
+
+/** Return the path to @a repos's pre-getfile hook, allocated in @a pool. */
+const char *svn_repos_pre_getfile_hook (svn_repos_t *repos, apr_pool_t *pool);
 
 /** @defgroup svn_repos_lock_hooks paths to lock hooks
  * @{
@@ -405,6 +410,7 @@
 svn_error_t *
 svn_repos_begin_report (void **report_baton,
                         svn_revnum_t revnum,
+ const char *ip_addr,
                         const char *username,
                         svn_repos_t *repos,
                         const char *fs_base,
--- subversion/libsvn_ra_local/ra_plugin.c.orig 2005-10-20 10:36:18.000000000 +0200
+++ subversion/libsvn_ra_local/ra_plugin.c 2005-10-20 11:11:57.158266800 +0200
@@ -588,6 +588,7 @@
   /* Build a reporter baton. */
   SVN_ERR (svn_repos_begin_report (&rbaton,
                                    revision,
+ "localhost",
                                    sbaton->username,
                                    sbaton->repos,
                                    sbaton->fs_path,
@@ -906,6 +907,18 @@
   else
     SVN_ERR (svn_fs_revision_root (&root, sbaton->fs, revision, pool));
 
+ svn_error_t *err = svn_repos__hooks_pre_getfile (sbaton->repos, revision,
+ abs_path, "localhost",
+ get_username (session, pool),
+ pool);
+ if (err!=SVN_NO_ERROR)
+ {
+ return svn_error_createf(SVN_ERR_REPOS_DISABLED_FEATURE, NULL, "%s\n%s",
+ "Operation has been cancelled by server;\n"
+ "perhaps you do not have sufficiant rights\n"
+ "Details:", err->message);
+ }
+
   if (stream)
     {
       /* Get a stream representing the file's contents. */
--- subversion/libsvn_ra_svn/marshal.c.orig 2005-09-23 12:30:10.000000000 +0200
+++ subversion/libsvn_ra_svn/marshal.c 2005-09-23 14:22:37.521154000 +0200
@@ -908,3 +908,21 @@
   SVN_ERR(svn_ra_svn_end_list(conn, pool));
   return SVN_NO_ERROR;
 }
+
+svn_error_t *svn_ra_svn_get_ip(svn_ra_svn_conn_t *conn, char **ip_addr,
+ apr_pool_t *pool)
+{
+ if (conn && conn->sock)
+ {
+ apr_status_t status;
+ apr_sockaddr_t *sa;
+ status = apr_socket_addr_get (&sa, APR_REMOTE, conn->sock);
+ if (status==APR_SUCCESS)
+ status = apr_sockaddr_ip_get (ip_addr, sa);
+ if (status==APR_SUCCESS)
+ return SVN_NO_ERROR;
+ }
+ // don't generate an error, just save message in ip_addr
+ *ip_addr = apr_psprintf (pool, "<unknown>" );
+ return SVN_NO_ERROR;
+}
--- subversion/libsvn_repos/hooks.c.orig 2005-08-18 09:50:38.000000000 +0200
+++ subversion/libsvn_repos/hooks.c 2005-08-18 10:25:17.875239600 +0200
@@ -416,6 +416,75 @@
 }
 
 
+svn_error_t *
+svn_repos__hooks_pre_update (svn_repos_t *repos,
+ svn_revnum_t revnum,
+ const char *target,
+ const char *ip_addr,
+ const char *user,
+ apr_pool_t *pool)
+{
+ const char *hook = svn_repos_pre_update_hook (repos, pool);
+ svn_boolean_t broken_link;
+
+ if ((hook = check_hook_cmd (hook, &broken_link, pool)) && broken_link)
+ {
+ return hook_symlink_error (hook);
+ }
+ else if (hook)
+ {
+ const char *args[6];
+
+ args[0] = hook;
+ args[1] = svn_repos_path (repos, pool);
+ args[2] = apr_psprintf (pool, "%ld", revnum);
+ args[3] = target;
+ args[4] = ip_addr;
+ args[5] = user ? user : "<anonymous>";
+ args[6] = NULL;
+
+ SVN_ERR (run_hook_cmd (SVN_REPOS__HOOK_PRE_UPDATE,
+ hook, args, TRUE, NULL, pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_repos__hooks_pre_getfile (svn_repos_t *repos,
+ svn_revnum_t revnum,
+ const char *target,
+ const char *ip_addr,
+ const char *user,
+ apr_pool_t *pool)
+{
+ const char *hook = svn_repos_pre_getfile_hook (repos, pool);
+ svn_boolean_t broken_link;
+
+ if ((hook = check_hook_cmd (hook, &broken_link, pool)) && broken_link)
+ {
+ return hook_symlink_error (hook);
+ }
+ else if (hook)
+ {
+ const char *args[6];
+
+ args[0] = hook;
+ args[1] = svn_repos_path (repos, pool);
+ args[2] = apr_psprintf (pool, "%ld", revnum);
+ args[3] = target;
+ args[4] = ip_addr;
+ args[5] = user ? user : "<anonymous>";
+ args[6] = NULL;
+
+ SVN_ERR (run_hook_cmd (SVN_REPOS__HOOK_PRE_GETFILE,
+ hook, args, TRUE, NULL, pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
 
 svn_error_t *
 svn_repos__hooks_pre_lock (svn_repos_t *repos,
--- subversion/libsvn_repos/reporter.c.orig 2005-08-18 09:50:44.000000000 +0200
+++ subversion/libsvn_repos/reporter.c 2005-08-18 10:25:17.984613900 +0200
@@ -1006,6 +1006,7 @@
 svn_error_t *
 svn_repos_begin_report (void **report_baton,
                         svn_revnum_t revnum,
+ const char *ip_addr,
                         const char *username,
                         svn_repos_t *repos,
                         const char *fs_base,
@@ -1023,6 +1024,17 @@
   report_baton_t *b;
   const char *tempdir, *dummy;
 
+ /* Run the pre-update hook via direct call (not wrapped) */
+ const char *fs_target = switch_path ? switch_path
+ : svn_path_join (fs_base, s_operand, pool);
+ svn_error_t *err = svn_repos__hooks_pre_update (repos, revnum, fs_target,
+ ip_addr, username, pool);
+ if (err!=SVN_NO_ERROR)
+ return svn_error_createf(SVN_ERR_REPOS_DISABLED_FEATURE, NULL, "%s\n%s",
+ "Operation has been cancelled by server;\n"
+ "perhaps you do not have sufficiant rights\n"
+ "Details:", err->message);
+
   /* Build a reporter baton. Copy strings in case the caller doesn't
      keep track of them. */
   b = apr_palloc (pool, sizeof (*b));
--- subversion/libsvn_repos/repos.c.orig 2005-04-26 08:36:19.000000000 +0200
+++ subversion/libsvn_repos/repos.c 2005-05-09 08:43:20.873677000 +0200
@@ -155,6 +155,20 @@
 }
 
 
+const char *
+svn_repos_pre_update_hook (svn_repos_t *repos, apr_pool_t *pool)
+{
+ return svn_path_join (repos->hook_path, SVN_REPOS__HOOK_PRE_UPDATE, pool);
+}
+
+
+const char *
+svn_repos_pre_getfile_hook (svn_repos_t *repos, apr_pool_t *pool)
+{
+ return svn_path_join (repos->hook_path, SVN_REPOS__HOOK_PRE_GETFILE, pool);
+}
+
+
 static svn_error_t *
 create_repos_dir (const char *path, apr_pool_t *pool)
 {
--- subversion/libsvn_repos/repos.h.orig 2005-09-30 11:19:12.000000000 +0200
+++ subversion/libsvn_repos/repos.h 2005-09-30 12:19:28.837117900 +0200
@@ -57,6 +57,8 @@
 #define SVN_REPOS__HOOK_WRITE_SENTINEL "write-sentinels"
 #define SVN_REPOS__HOOK_PRE_REVPROP_CHANGE "pre-revprop-change"
 #define SVN_REPOS__HOOK_POST_REVPROP_CHANGE "post-revprop-change"
+#define SVN_REPOS__HOOK_PRE_UPDATE "pre-update"
+#define SVN_REPOS__HOOK_PRE_GETFILE "pre-getfile"
 #define SVN_REPOS__HOOK_PRE_LOCK "pre-lock"
 #define SVN_REPOS__HOOK_POST_LOCK "post-lock"
 #define SVN_REPOS__HOOK_PRE_UNLOCK "pre-unlock"
@@ -183,6 +185,26 @@
                                       char action,
                                       apr_pool_t *pool);
 
+/* Run the pre-update hook for REPOS. Use POOL for any temporary
+ allocations. If the hook fails, return SVN_ERR_REPOS_HOOK_FAILURE.*/
+svn_error_t *
+svn_repos__hooks_pre_update (svn_repos_t *repos,
+ svn_revnum_t revnum,
+ const char *target,
+ const char *ip_addr,
+ const char *user,
+ apr_pool_t *pool);
+
+/* Run the pre-getfile hook for REPOS. Use POOL for any temporary
+ allocations. If the hook fails, return SVN_ERR_REPOS_HOOK_FAILURE.*/
+svn_error_t *
+svn_repos__hooks_pre_getfile (svn_repos_t *repos,
+ svn_revnum_t revnum,
+ const char *target,
+ const char *ip_addr,
+ const char *user,
+ apr_pool_t *pool);
+
 /* Run the pre-lock hook for REPOS. Use POOL for any temporary
    allocations. If the hook fails, return SVN_ERR_REPOS_HOOK_FAILURE.
 
--- subversion/mod_dav_svn/update.c.orig 2005-09-27 20:51:42.000000000 +0200
+++ subversion/mod_dav_svn/update.c 2006-01-17 10:22:26.745612700 +0100
@@ -1257,7 +1257,7 @@
   editor->close_file = upd_close_file;
   editor->absent_file = upd_absent_file;
   editor->close_edit = upd_close_edit;
- if ((serr = svn_repos_begin_report(&rbaton, revnum, repos->username,
+ if ((serr = svn_repos_begin_report(&rbaton, revnum, "", repos->username,
                                      repos->repos,
                                      src_path, target,
                                      dst_path,
--- subversion/svnserve/serve.c.orig 2005-11-02 23:47:08.000000000 +0100
+++ subversion/svnserve/serve.c 2005-11-22 10:34:04.375364000 +0100
@@ -547,10 +547,13 @@
   /* Make an svn_repos report baton. Tell it to drive the network editor
    * when the report is complete. */
   svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL);
- SVN_CMD_ERR(svn_repos_begin_report(&report_baton, rev, b->user, b->repos,
- b->fs_path, target, tgt_path, text_deltas,
- recurse, ignore_ancestry, editor,
- edit_baton, authz_check_access_cb, b, pool));
+ char *ip_addr;
+ err = svn_ra_svn_get_ip (conn, &ip_addr, pool);
+ SVN_CMD_ERR(svn_repos_begin_report(&report_baton, rev, ip_addr, b->user,
+ b->repos, b->fs_path, target, tgt_path,
+ text_deltas, recurse, ignore_ancestry,
+ editor, edit_baton,
+ authz_check_access_cb, b, pool));
 
   rb.sb = b;
   rb.repos_url = svn_path_uri_decode(b->repos_url, pool);
@@ -977,6 +980,23 @@
   if (!SVN_IS_VALID_REVNUM(rev))
     SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool));
 
+ char *ip_addr;
+ err = svn_ra_svn_get_ip (conn, &ip_addr, pool);
+ err = svn_repos__hooks_pre_getfile (b->repos, rev, full_path,
+ ip_addr, b->user, pool);
+ if (err!=SVN_NO_ERROR)
+ {
+ err = svn_error_createf(SVN_ERR_REPOS_DISABLED_FEATURE, NULL, "%s\n%s",
+ "Operation has been cancelled by server;\n"
+ "perhaps you do not have sufficiant rights\n"
+ "Details:", err->message);
+ svn_error_t *write_err = svn_ra_svn_write_cmd_failure(conn, pool, err);
+ svn_error_clear(err);
+ if (write_err)
+ return write_err;
+ return SVN_NO_ERROR;
+ }
+
   /* 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));
--- subversion/tests/libsvn_repos/repos-test.c.orig 2005-09-23 12:37:30.000000000 +0200
+++ subversion/tests/libsvn_repos/repos-test.c 2005-09-23 14:22:38.193029000 +0200
@@ -1011,9 +1011,9 @@
     SVN_ERR (create_rmlocks_editor (&editor, &edit_baton, &removed, subpool));
 
     /* Report what we have. */
- SVN_ERR (svn_repos_begin_report (&report_baton, 1, "user1", repos, "/", "",
- NULL, FALSE, TRUE, FALSE, editor,
- edit_baton, NULL, NULL, subpool));
+ SVN_ERR (svn_repos_begin_report (&report_baton, 1, "127.0.0.1", "user1",
+ repos, "/", "", NULL, FALSE, TRUE, FALSE,
+ editor, edit_baton, NULL, NULL, subpool));
     SVN_ERR (svn_repos_set_path2 (report_baton, "", 1, FALSE, NULL,
                                   subpool));
     SVN_ERR (svn_repos_set_path2 (report_baton, "iota", 1, FALSE, l1->token,

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Mon Feb 6 14:14:12 2006

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