summaryrefslogtreecommitdiff
path: root/subversion/mod_dav_svn/deadprops.c
diff options
context:
space:
mode:
authorJames McCoy <jamessan@debian.org>2018-07-31 22:26:52 -0400
committerJames McCoy <jamessan@debian.org>2018-07-31 22:26:52 -0400
commite20a507113ff1126aeb4a97b806390ea377fe292 (patch)
tree0260b3a40387d7f994fbadaf22f1e9d3c080b09f /subversion/mod_dav_svn/deadprops.c
parentc64debffb81d2fa17e9a72af7199ccf88b3cc556 (diff)
New upstream version 1.10.2
Diffstat (limited to 'subversion/mod_dav_svn/deadprops.c')
-rw-r--r--subversion/mod_dav_svn/deadprops.c815
1 files changed, 815 insertions, 0 deletions
diff --git a/subversion/mod_dav_svn/deadprops.c b/subversion/mod_dav_svn/deadprops.c
new file mode 100644
index 0000000..3d79e86
--- /dev/null
+++ b/subversion/mod_dav_svn/deadprops.c
@@ -0,0 +1,815 @@
+/*
+ * deadprops.c: mod_dav_svn provider functions for "dead properties"
+ * (properties implemented by Subversion or its users,
+ * not as part of the WebDAV specification).
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#include <apr_hash.h>
+
+#include <httpd.h>
+#include <mod_dav.h>
+
+#include "svn_hash.h"
+#include "svn_xml.h"
+#include "svn_pools.h"
+#include "svn_dav.h"
+#include "svn_base64.h"
+#include "svn_props.h"
+#include "private/svn_log.h"
+
+#include "dav_svn.h"
+
+
+struct dav_db {
+ const dav_resource *resource;
+ apr_pool_t *p;
+
+ /* the resource's properties that we are sequencing over */
+ apr_hash_t *props;
+ apr_hash_index_t *hi;
+
+ /* used for constructing repos-local names for properties */
+ svn_stringbuf_t *work;
+
+ /* passed to svn_repos_ funcs that fetch revprops. */
+ svn_repos_authz_func_t authz_read_func;
+ void *authz_read_baton;
+};
+
+
+struct dav_deadprop_rollback {
+ int dummy;
+};
+
+
+/* retrieve the "right" string to use as a repos path */
+static const char *
+get_repos_path(struct dav_resource_private *info)
+{
+ return info->repos_path;
+}
+
+
+/* construct the repos-local name for the given DAV property name */
+static void
+get_repos_propname(dav_db *db,
+ const dav_prop_name *name,
+ const char **repos_propname)
+{
+ if (strcmp(name->ns, SVN_DAV_PROP_NS_SVN) == 0)
+ {
+ /* recombine the namespace ("svn:") and the name. */
+ svn_stringbuf_set(db->work, SVN_PROP_PREFIX);
+ svn_stringbuf_appendcstr(db->work, name->name);
+ *repos_propname = db->work->data;
+ }
+ else if (strcmp(name->ns, SVN_DAV_PROP_NS_CUSTOM) == 0)
+ {
+ /* the name of a custom prop is just the name -- no ns URI */
+ *repos_propname = name->name;
+ }
+ else
+ {
+ *repos_propname = NULL;
+ }
+}
+
+
+static dav_error *
+get_value(dav_db *db, const dav_prop_name *name, svn_string_t **pvalue)
+{
+ const char *propname;
+ svn_error_t *serr;
+
+ /* get the repos-local name */
+ get_repos_propname(db, name, &propname);
+
+ if (propname == NULL)
+ {
+ /* we know these are not present. */
+ *pvalue = NULL;
+ return NULL;
+ }
+
+ /* If db->props exists, then use it to obtain property value. */
+ if (db->props)
+ {
+ *pvalue = svn_hash_gets(db->props, propname);
+ return NULL;
+ }
+
+ /* We've got three different types of properties (node, txn, and
+ revision), and we've got two different protocol versions to deal
+ with. Let's try to make some sense of this, shall we?
+
+ HTTP v1:
+ working baseline ('wbl') resource -> txn prop change
+ non-working, baselined resource ('bln') -> rev prop change [*]
+ working, non-baselined resource ('wrk') -> node prop change
+
+ HTTP v2:
+ transaction resource ('txn') -> txn prop change
+ revision resource ('rev') -> rev prop change
+ transaction root resource ('txr') -> node prop change
+
+ [*] This is a violation of the DeltaV spec (### see issue #916).
+
+ */
+
+ if (db->resource->baselined)
+ {
+ if (db->resource->type == DAV_RESOURCE_TYPE_WORKING)
+ serr = svn_fs_txn_prop(pvalue, db->resource->info->root.txn,
+ propname, db->p);
+ else
+ serr = svn_repos_fs_revision_prop(pvalue,
+ db->resource->info->repos->repos,
+ db->resource->info->root.rev,
+ propname, db->authz_read_func,
+ db->authz_read_baton, db->p);
+ }
+ else if (db->resource->info->restype == DAV_SVN_RESTYPE_TXN_COLLECTION)
+ {
+ serr = svn_fs_txn_prop(pvalue, db->resource->info->root.txn,
+ propname, db->p);
+ }
+ else
+ {
+ serr = svn_fs_node_prop(pvalue, db->resource->info->root.root,
+ get_repos_path(db->resource->info),
+ propname, db->p);
+ }
+
+ if (serr != NULL)
+ return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+ "could not fetch a property",
+ db->resource->pool);
+
+ return NULL;
+}
+
+
+static svn_error_t *
+change_txn_prop(svn_fs_txn_t *txn,
+ const char *propname,
+ const svn_string_t *value,
+ apr_pool_t *scratch_pool)
+{
+ if (strcmp(propname, SVN_PROP_REVISION_AUTHOR) == 0)
+ return svn_error_create(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
+ "Attempted to modify 'svn:author' property "
+ "on a transaction");
+
+ SVN_ERR(svn_repos_fs_change_txn_prop(txn, propname, value, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+
+static dav_error *
+save_value(dav_db *db, const dav_prop_name *name,
+ const svn_string_t *const *old_value_p,
+ const svn_string_t *value)
+{
+ const char *propname;
+ svn_error_t *serr;
+ const dav_resource *resource = db->resource;
+ apr_pool_t *subpool;
+
+ /* get the repos-local name */
+ get_repos_propname(db, name, &propname);
+
+ if (propname == NULL)
+ {
+ if (resource->info->repos->autoversioning)
+ /* ignore the unknown namespace of the incoming prop. */
+ propname = name->name;
+ else
+ return dav_svn__new_error(db->p, HTTP_CONFLICT, 0, 0,
+ "Properties may only be defined in the "
+ SVN_DAV_PROP_NS_SVN " and "
+ SVN_DAV_PROP_NS_CUSTOM " namespaces.");
+ }
+
+ /* We've got three different types of properties (node, txn, and
+ revision), and we've got two different protocol versions to deal
+ with. Let's try to make some sense of this, shall we?
+
+ HTTP v1:
+ working baseline ('wbl') resource -> txn prop change
+ non-working, baselined resource ('bln') -> rev prop change [*]
+ working, non-baselined resource ('wrk') -> node prop change
+
+ HTTP v2:
+ transaction resource ('txn') -> txn prop change
+ revision resource ('rev') -> rev prop change
+ transaction root resource ('txr') -> node prop change
+
+ [*] This is a violation of the DeltaV spec (### see issue #916).
+
+ */
+
+ /* A subpool to cope with mod_dav making multiple calls, e.g. during
+ PROPPATCH with multiple values. */
+ subpool = svn_pool_create(resource->pool);
+ if (resource->baselined)
+ {
+ if (resource->working)
+ {
+ serr = change_txn_prop(resource->info->root.txn, propname,
+ value, subpool);
+ }
+ else
+ {
+ serr = svn_repos_fs_change_rev_prop4(resource->info->repos->repos,
+ resource->info->root.rev,
+ resource->info->repos->username,
+ propname, old_value_p, value,
+ TRUE, TRUE,
+ db->authz_read_func,
+ db->authz_read_baton,
+ subpool);
+
+ /* Prepare any hook failure message to get sent over the wire */
+ if (serr)
+ {
+ svn_error_t *purged_serr = svn_error_purge_tracing(serr);
+ if (purged_serr->apr_err == SVN_ERR_REPOS_HOOK_FAILURE)
+ purged_serr->message = apr_xml_quote_string
+ (purged_serr->pool,
+ purged_serr->message, 1);
+
+ /* mod_dav doesn't handle the returned error very well, it
+ generates its own generic error that will be returned to
+ the client. Cache the detailed error here so that it can
+ be returned a second time when the rollback mechanism
+ triggers. */
+ resource->info->revprop_error = svn_error_dup(purged_serr);
+ }
+
+ /* Tell the logging subsystem about the revprop change. */
+ dav_svn__operational_log(resource->info,
+ svn_log__change_rev_prop(
+ resource->info->root.rev,
+ propname, subpool));
+ }
+ }
+ else if (resource->info->restype == DAV_SVN_RESTYPE_TXN_COLLECTION)
+ {
+ serr = change_txn_prop(resource->info->root.txn, propname,
+ value, subpool);
+ }
+ else
+ {
+ serr = svn_repos_fs_change_node_prop(resource->info->root.root,
+ get_repos_path(resource->info),
+ propname, value, subpool);
+ }
+ svn_pool_destroy(subpool);
+
+ if (serr != NULL)
+ return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+ NULL, resource->pool);
+
+ /* a change to the props was made; make sure our cached copy is gone */
+ db->props = NULL;
+
+ return NULL;
+}
+
+
+static dav_error *
+db_open(apr_pool_t *p,
+ const dav_resource *resource,
+ int ro,
+ dav_db **pdb)
+{
+ dav_db *db;
+ dav_svn__authz_read_baton *arb;
+
+ /* Some resource types do not have deadprop databases.
+ Specifically: REGULAR, VERSION, WORKING, and our custom
+ transaction and transaction root resources have them. (SVN does
+ not have WORKSPACE resources, and isn't covered here.) */
+
+ if (resource->type == DAV_RESOURCE_TYPE_HISTORY
+ || resource->type == DAV_RESOURCE_TYPE_ACTIVITY
+ || (resource->type == DAV_RESOURCE_TYPE_PRIVATE
+ && resource->info->restype != DAV_SVN_RESTYPE_TXN_COLLECTION
+ && resource->info->restype != DAV_SVN_RESTYPE_TXNROOT_COLLECTION))
+ {
+ *pdb = NULL;
+ return NULL;
+ }
+
+ /* If the DB is being opened R/W, and this isn't a working resource, then
+ we have a problem! */
+ if ((! ro)
+ && resource->type != DAV_RESOURCE_TYPE_WORKING
+ && resource->type != DAV_RESOURCE_TYPE_PRIVATE
+ && resource->info->restype != DAV_SVN_RESTYPE_TXN_COLLECTION)
+ {
+ /* ### Exception: in violation of deltaV, we *are* allowing a
+ baseline resource to receive a proppatch, as a way of
+ changing unversioned rev props. Remove this someday: see IZ #916. */
+ if (! (resource->baselined
+ && resource->type == DAV_RESOURCE_TYPE_VERSION))
+ return dav_svn__new_error(p, HTTP_CONFLICT, 0, 0,
+ "Properties may only be changed on working "
+ "resources.");
+ }
+
+ db = apr_pcalloc(p, sizeof(*db));
+
+ db->resource = resource;
+ db->p = svn_pool_create(p);
+
+ /* ### temp hack */
+ db->work = svn_stringbuf_create_empty(db->p);
+
+ /* make our path-based authz callback available to svn_repos_* funcs. */
+ arb = apr_pcalloc(p, sizeof(*arb));
+ arb->r = resource->info->r;
+ arb->repos = resource->info->repos;
+ db->authz_read_baton = arb;
+ db->authz_read_func = dav_svn__authz_read_func(arb);
+
+ /* ### use RO and node's mutable status to look for an error? */
+
+ *pdb = db;
+
+ return NULL;
+}
+
+
+static void
+db_close(dav_db *db)
+{
+ svn_pool_destroy(db->p);
+}
+
+
+static dav_error *
+db_define_namespaces(dav_db *db, dav_xmlns_info *xi)
+{
+ dav_xmlns_add(xi, "S", SVN_DAV_PROP_NS_SVN);
+ dav_xmlns_add(xi, "C", SVN_DAV_PROP_NS_CUSTOM);
+ dav_xmlns_add(xi, "V", SVN_DAV_PROP_NS_DAV);
+
+ /* ### we don't have any other possible namespaces right now. */
+
+ return NULL;
+}
+
+static dav_error *
+db_output_value(dav_db *db,
+ const dav_prop_name *name,
+ dav_xmlns_info *xi,
+ apr_text_header *phdr,
+ int *found)
+{
+ const char *prefix;
+ const char *s;
+ svn_string_t *propval;
+ dav_error *err;
+ apr_pool_t *pool = db->resource->pool;
+
+ if ((err = get_value(db, name, &propval)) != NULL)
+ return err;
+
+ /* return whether the prop was found, then punt or handle it. */
+ *found = (propval != NULL);
+ if (propval == NULL)
+ return NULL;
+
+ if (strcmp(name->ns, SVN_DAV_PROP_NS_CUSTOM) == 0)
+ prefix = "C:";
+ else
+ prefix = "S:";
+
+ if (propval->len == 0)
+ {
+ /* empty value. add an empty elem. */
+ s = apr_psprintf(pool, "<%s%s/>" DEBUG_CR, prefix, name->name);
+ apr_text_append(pool, phdr, s);
+ }
+ else
+ {
+ /* add <prefix:name [V:encoding="base64"]>value</prefix:name> */
+ const char *xml_safe;
+ const char *encoding = "";
+
+ /* Ensure XML-safety of our property values before sending them
+ across the wire. */
+ if (! svn_xml_is_xml_safe(propval->data, propval->len))
+ {
+ const svn_string_t *enc_propval
+ = svn_base64_encode_string2(propval, TRUE, pool);
+ xml_safe = enc_propval->data;
+ encoding = " V:encoding=\"base64\"";
+ }
+ else
+ {
+ svn_stringbuf_t *xmlval = NULL;
+ svn_xml_escape_cdata_string(&xmlval, propval, pool);
+ xml_safe = xmlval->data;
+ }
+
+ s = apr_psprintf(pool, "<%s%s%s>", prefix, name->name, encoding);
+ apr_text_append(pool, phdr, s);
+
+ /* the value is in our pool which means it has the right lifetime. */
+ /* ### at least, per the current mod_dav architecture/API */
+ apr_text_append(pool, phdr, xml_safe);
+
+ s = apr_psprintf(pool, "</%s%s>" DEBUG_CR, prefix, name->name);
+ apr_text_append(pool, phdr, s);
+ }
+
+ return NULL;
+}
+
+
+static dav_error *
+db_map_namespaces(dav_db *db,
+ const apr_array_header_t *namespaces,
+ dav_namespace_map **mapping)
+{
+ /* we don't need a namespace mapping right now. nothing to do */
+ return NULL;
+}
+
+
+static dav_error *
+decode_property_value(const svn_string_t **out_propval_p,
+ svn_boolean_t *absent,
+ const svn_string_t *maybe_encoded_propval,
+ const apr_xml_elem *elem,
+ apr_pool_t *pool)
+{
+ apr_xml_attr *attr = elem->attr;
+
+ /* Default: no "encoding" attribute. */
+ *absent = FALSE;
+ *out_propval_p = maybe_encoded_propval;
+
+ /* Check for special encodings of the property value. */
+ while (attr)
+ {
+ if (strcmp(attr->name, "encoding") == 0) /* ### namespace check? */
+ {
+ const char *enc_type = attr->value;
+
+ /* Handle known encodings here. */
+ if (enc_type && (strcmp(enc_type, "base64") == 0))
+ *out_propval_p = svn_base64_decode_string(maybe_encoded_propval,
+ pool);
+ else
+ return dav_svn__new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, 0,
+ "Unknown property encoding");
+ break;
+ }
+
+ if (strcmp(attr->name, SVN_DAV__OLD_VALUE__ABSENT) == 0)
+ {
+ /* ### parse attr->value */
+ *absent = TRUE;
+ *out_propval_p = NULL;
+ }
+
+ /* Next attribute, please. */
+ attr = attr->next;
+ }
+
+ return NULL;
+}
+
+static dav_error *
+db_store(dav_db *db,
+ const dav_prop_name *name,
+ const apr_xml_elem *elem,
+ dav_namespace_map *mapping)
+{
+ const svn_string_t *const *old_propval_p;
+ const svn_string_t *old_propval;
+ const svn_string_t *propval;
+ svn_boolean_t absent;
+ apr_pool_t *pool = db->p;
+ dav_error *derr;
+
+ /* SVN sends property values as a big blob of bytes. Thus, there should be
+ no child elements of the property-name element. That also means that
+ the entire contents of the blob is located in elem->first_cdata. The
+ dav_xml_get_cdata() will figure it all out for us, but (normally) it
+ should be awfully fast and not need to copy any data. */
+
+ propval = svn_string_create
+ (dav_xml_get_cdata(elem, pool, 0 /* strip_white */), pool);
+
+ derr = decode_property_value(&propval, &absent, propval, elem, pool);
+ if (derr)
+ return derr;
+
+ if (absent && ! elem->first_child)
+ /* ### better error check */
+ return dav_svn__new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, 0,
+ apr_psprintf(pool,
+ "'%s' cannot be specified on the "
+ "value without specifying an "
+ "expectation",
+ SVN_DAV__OLD_VALUE__ABSENT));
+
+ /* ### namespace check? */
+ if (elem->first_child && !strcmp(elem->first_child->name, SVN_DAV__OLD_VALUE))
+ {
+ /* Parse OLD_PROPVAL. */
+ old_propval = svn_string_create(dav_xml_get_cdata(elem->first_child, pool,
+ 0 /* strip_white */),
+ pool);
+ derr = decode_property_value(&old_propval, &absent,
+ old_propval, elem->first_child, pool);
+ if (derr)
+ return derr;
+
+ old_propval_p = (const svn_string_t *const *) &old_propval;
+ }
+ else
+ old_propval_p = NULL;
+
+
+ return save_value(db, name, old_propval_p, propval);
+}
+
+
+static dav_error *
+db_remove(dav_db *db, const dav_prop_name *name)
+{
+ svn_error_t *serr;
+ const char *propname;
+ apr_pool_t *subpool;
+
+ /* get the repos-local name */
+ get_repos_propname(db, name, &propname);
+
+ /* ### non-svn props aren't in our repos, so punt for now */
+ if (propname == NULL)
+ return NULL;
+
+ /* A subpool to cope with mod_dav making multiple calls, e.g. during
+ PROPPATCH with multiple values. */
+ subpool = svn_pool_create(db->resource->pool);
+
+ /* Working Baseline or Working (Version) Resource */
+ if (db->resource->baselined)
+ if (db->resource->working)
+ serr = change_txn_prop(db->resource->info->root.txn, propname,
+ NULL, subpool);
+ else
+ /* ### VIOLATING deltaV: you can't proppatch a baseline, it's
+ not a working resource! But this is how we currently
+ (hackily) allow the svn client to change unversioned rev
+ props. See issue #916. */
+ serr = svn_repos_fs_change_rev_prop4(db->resource->info->repos->repos,
+ db->resource->info->root.rev,
+ db->resource->info->repos->username,
+ propname, NULL, NULL, TRUE, TRUE,
+ db->authz_read_func,
+ db->authz_read_baton,
+ subpool);
+ else
+ serr = svn_repos_fs_change_node_prop(db->resource->info->root.root,
+ get_repos_path(db->resource->info),
+ propname, NULL, subpool);
+ svn_pool_destroy(subpool);
+ if (serr != NULL)
+ return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+ "could not remove a property",
+ db->resource->pool);
+
+ /* a change to the props was made; make sure our cached copy is gone */
+ db->props = NULL;
+
+ return NULL;
+}
+
+
+static int
+db_exists(dav_db *db, const dav_prop_name *name)
+{
+ const char *propname;
+ svn_string_t *propval;
+ svn_error_t *serr;
+ int retval;
+
+ /* get the repos-local name */
+ get_repos_propname(db, name, &propname);
+
+ /* ### non-svn props aren't in our repos */
+ if (propname == NULL)
+ return 0;
+
+ /* Working Baseline, Baseline, or (Working) Version resource */
+ if (db->resource->baselined)
+ if (db->resource->type == DAV_RESOURCE_TYPE_WORKING)
+ serr = svn_fs_txn_prop(&propval, db->resource->info->root.txn,
+ propname, db->p);
+ else
+ serr = svn_repos_fs_revision_prop(&propval,
+ db->resource->info->repos->repos,
+ db->resource->info->root.rev,
+ propname,
+ db->authz_read_func,
+ db->authz_read_baton, db->p);
+ else
+ serr = svn_fs_node_prop(&propval, db->resource->info->root.root,
+ get_repos_path(db->resource->info),
+ propname, db->p);
+
+ /* ### try and dispose of the value? */
+
+ retval = (serr == NULL && propval != NULL);
+ svn_error_clear(serr);
+ return retval;
+}
+
+static void get_name(dav_db *db, dav_prop_name *pname)
+{
+ if (db->hi == NULL)
+ {
+ pname->ns = pname->name = NULL;
+ }
+ else
+ {
+ const char *name = apr_hash_this_key(db->hi);
+
+#define PREFIX_LEN (sizeof(SVN_PROP_PREFIX) - 1)
+ if (strncmp(name, SVN_PROP_PREFIX, PREFIX_LEN) == 0)
+#undef PREFIX_LEN
+ {
+ pname->ns = SVN_DAV_PROP_NS_SVN;
+ pname->name = name + 4;
+ }
+ else
+ {
+ pname->ns = SVN_DAV_PROP_NS_CUSTOM;
+ pname->name = name;
+ }
+ }
+}
+
+
+static dav_error *
+db_first_name(dav_db *db, dav_prop_name *pname)
+{
+ /* for operational logging */
+ const char *action = NULL;
+
+ /* if we don't have a copy of the properties, then get one */
+ if (db->props == NULL)
+ {
+ svn_error_t *serr;
+
+ /* Working Baseline, Baseline, or (Working) Version resource */
+ if (db->resource->baselined)
+ {
+ if (db->resource->type == DAV_RESOURCE_TYPE_WORKING)
+ serr = svn_fs_txn_proplist(&db->props,
+ db->resource->info->root.txn,
+ db->p);
+ else
+ {
+ action = svn_log__rev_proplist(db->resource->info->root.rev,
+ db->resource->pool);
+ serr = svn_repos_fs_revision_proplist
+ (&db->props,
+ db->resource->info->repos->repos,
+ db->resource->info->root.rev,
+ db->authz_read_func,
+ db->authz_read_baton,
+ db->p);
+ }
+ }
+ else
+ {
+ serr = svn_fs_node_proplist(&db->props,
+ db->resource->info->root.root,
+ get_repos_path(db->resource->info),
+ db->p);
+
+ if (! serr)
+ {
+ if (db->resource->collection)
+ action = svn_log__get_dir(db->resource->info->repos_path,
+ db->resource->info->root.rev,
+ FALSE, TRUE, 0, db->resource->pool);
+ else
+ action = svn_log__get_file(db->resource->info->repos_path,
+ db->resource->info->root.rev,
+ FALSE, TRUE, db->resource->pool);
+ }
+ }
+ if (serr != NULL)
+ return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+ "could not begin sequencing through "
+ "properties",
+ db->resource->pool);
+ }
+
+ /* begin the iteration over the hash */
+ db->hi = apr_hash_first(db->p, db->props);
+
+ /* fetch the first key */
+ get_name(db, pname);
+
+ /* If we have a high-level action to log, do so. */
+ if (action != NULL)
+ dav_svn__operational_log(db->resource->info, action);
+
+ return NULL;
+}
+
+
+static dav_error *
+db_next_name(dav_db *db, dav_prop_name *pname)
+{
+ /* skip to the next hash entry */
+ if (db->hi != NULL)
+ db->hi = apr_hash_next(db->hi);
+
+ /* fetch the key */
+ get_name(db, pname);
+
+ return NULL;
+}
+
+
+static dav_error *
+db_get_rollback(dav_db *db,
+ const dav_prop_name *name,
+ dav_deadprop_rollback **prollback)
+{
+ /* This gets called by mod_dav in preparation for a revprop change.
+ mod_dav_svn doesn't need to make any changes during rollback, but
+ we want the rollback mechanism to trigger. Making changes in
+ response to post-revprop-change hook errors would be positively
+ wrong. */
+
+ *prollback = apr_palloc(db->p, sizeof(dav_deadprop_rollback));
+
+ return NULL;
+}
+
+
+static dav_error *
+db_apply_rollback(dav_db *db, dav_deadprop_rollback *rollback)
+{
+ dav_error *derr;
+
+ if (! db->resource->info->revprop_error)
+ return NULL;
+
+ /* Returning the original revprop change error here will cause this
+ detailed error to get returned to the client in preference to the
+ more generic error created by mod_dav. */
+ derr = dav_svn__convert_err(db->resource->info->revprop_error,
+ HTTP_INTERNAL_SERVER_ERROR, NULL,
+ db->resource->pool);
+ db->resource->info->revprop_error = NULL;
+
+ return derr;
+}
+
+
+const dav_hooks_propdb dav_svn__hooks_propdb = {
+ db_open,
+ db_close,
+ db_define_namespaces,
+ db_output_value,
+ db_map_namespaces,
+ db_store,
+ db_remove,
+ db_exists,
+ db_first_name,
+ db_next_name,
+ db_get_rollback,
+ db_apply_rollback,
+};