diff options
Diffstat (limited to 'subversion/libsvn_ra/util.c')
-rw-r--r-- | subversion/libsvn_ra/util.c | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/subversion/libsvn_ra/util.c b/subversion/libsvn_ra/util.c new file mode 100644 index 000000000000..47e48657c2a7 --- /dev/null +++ b/subversion/libsvn_ra/util.c @@ -0,0 +1,242 @@ +/* + * util.c: Repository access utility routines. + * + * ==================================================================== + * 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. + * ==================================================================== + */ + +/* ==================================================================== */ + +/*** Includes. ***/ +#include <apr_pools.h> +#include <apr_network_io.h> + +#include "svn_types.h" +#include "svn_pools.h" +#include "svn_error.h" +#include "svn_error_codes.h" +#include "svn_dirent_uri.h" +#include "svn_path.h" +#include "svn_ra.h" + +#include "svn_private_config.h" +#include "private/svn_ra_private.h" + +svn_error_t * +svn_ra__assert_mergeinfo_capable_server(svn_ra_session_t *ra_session, + const char *path_or_url, + apr_pool_t *pool) +{ + svn_boolean_t mergeinfo_capable; + SVN_ERR(svn_ra_has_capability(ra_session, &mergeinfo_capable, + SVN_RA_CAPABILITY_MERGEINFO, pool)); + if (! mergeinfo_capable) + { + if (path_or_url == NULL) + { + svn_error_t *err = svn_ra_get_session_url(ra_session, &path_or_url, + pool); + if (err) + { + /* The SVN_ERR_UNSUPPORTED_FEATURE error is more important, + so dummy up the session's URL and chuck this error. */ + svn_error_clear(err); + path_or_url = "<repository>"; + } + } + return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, + _("Retrieval of mergeinfo unsupported by '%s'"), + svn_path_is_url(path_or_url) + ? path_or_url + : svn_dirent_local_style(path_or_url, pool)); + } + return SVN_NO_ERROR; +} + +/* Does ERR mean "the current value of the revprop isn't equal to + the *OLD_VALUE_P you gave me"? + */ +static svn_boolean_t is_atomicity_error(svn_error_t *err) +{ + return svn_error_find_cause(err, SVN_ERR_FS_PROP_BASEVALUE_MISMATCH) != NULL; +} + +svn_error_t * +svn_ra__release_operational_lock(svn_ra_session_t *session, + const char *lock_revprop_name, + const svn_string_t *mylocktoken, + apr_pool_t *scratch_pool) +{ + svn_string_t *reposlocktoken; + svn_boolean_t be_atomic; + + SVN_ERR(svn_ra_has_capability(session, &be_atomic, + SVN_RA_CAPABILITY_ATOMIC_REVPROPS, + scratch_pool)); + SVN_ERR(svn_ra_rev_prop(session, 0, lock_revprop_name, + &reposlocktoken, scratch_pool)); + if (reposlocktoken && svn_string_compare(reposlocktoken, mylocktoken)) + { + svn_error_t *err; + + err = svn_ra_change_rev_prop2(session, 0, lock_revprop_name, + be_atomic ? &mylocktoken : NULL, NULL, + scratch_pool); + if (is_atomicity_error(err)) + { + return svn_error_createf(err->apr_err, err, + _("Lock was stolen by '%s'; unable to " + "remove it"), reposlocktoken->data); + } + else + SVN_ERR(err); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_ra__get_operational_lock(const svn_string_t **lock_string_p, + const svn_string_t **stolen_lock_p, + svn_ra_session_t *session, + const char *lock_revprop_name, + svn_boolean_t steal_lock, + int num_retries, + svn_ra__lock_retry_func_t retry_func, + void *retry_baton, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *pool) +{ + char hostname_str[APRMAXHOSTLEN + 1] = { 0 }; + svn_string_t *mylocktoken, *reposlocktoken; + apr_status_t apr_err; + svn_boolean_t be_atomic; + apr_pool_t *subpool; + int i; + + *lock_string_p = NULL; + if (stolen_lock_p) + *stolen_lock_p = NULL; + + SVN_ERR(svn_ra_has_capability(session, &be_atomic, + SVN_RA_CAPABILITY_ATOMIC_REVPROPS, pool)); + + /* We build a lock token from the local hostname and a UUID. */ + apr_err = apr_gethostname(hostname_str, sizeof(hostname_str), pool); + if (apr_err) + return svn_error_wrap_apr(apr_err, + _("Unable to determine local hostname")); + mylocktoken = svn_string_createf(pool, "%s:%s", hostname_str, + svn_uuid_generate(pool)); + + /* Ye Olde Retry Loope */ + subpool = svn_pool_create(pool); + + for (i = 0; i < num_retries; ++i) + { + svn_error_t *err; + const svn_string_t *unset = NULL; + + svn_pool_clear(subpool); + + /* Check for cancellation. If we're cancelled, don't leave a + stray lock behind! */ + if (cancel_func) + { + err = cancel_func(cancel_baton); + if (err && err->apr_err == SVN_ERR_CANCELLED) + return svn_error_compose_create( + svn_ra__release_operational_lock(session, + lock_revprop_name, + mylocktoken, + subpool), + err); + SVN_ERR(err); + } + + /* Ask the repository for the value of the LOCK_REVPROP_NAME. */ + SVN_ERR(svn_ra_rev_prop(session, 0, lock_revprop_name, + &reposlocktoken, subpool)); + + /* Did we get a value from the repository? We'll check to see + if it matches our token. If so, we call it success. If not + and we're told to steal locks, we remember the existing lock + token and fall through to the locking code; othewise, we + sleep and retry. */ + if (reposlocktoken) + { + if (svn_string_compare(reposlocktoken, mylocktoken)) + { + *lock_string_p = mylocktoken; + return SVN_NO_ERROR; + } + else if (! steal_lock) + { + if (retry_func) + SVN_ERR(retry_func(retry_baton, reposlocktoken, subpool)); + apr_sleep(apr_time_from_sec(1)); + continue; + } + else + { + if (stolen_lock_p) + *stolen_lock_p = svn_string_dup(reposlocktoken, pool); + unset = reposlocktoken; + } + } + + /* No lock value in the repository, or we plan to steal it? + Well, if we've got a spare iteration, we'll try to set the + lock. (We use the spare iteration to verify that we still + have the lock after setting it.) */ + if (i < num_retries - 1) + { + /* Except in the very last iteration, try to set the lock. */ + err = svn_ra_change_rev_prop2(session, 0, lock_revprop_name, + be_atomic ? &unset : NULL, + mylocktoken, subpool); + + if (be_atomic && err && is_atomicity_error(err)) + { + /* Someone else has the lock. No problem, we'll loop again. */ + svn_error_clear(err); + } + else if (be_atomic && err == SVN_NO_ERROR) + { + /* Yay! We have the lock! However, for compatibility + with concurrent processes that don't support + atomicity, loop anyway to double-check that they + haven't overwritten our lock. + */ + continue; + } + else + { + /* We have a genuine error, or aren't atomic and need + to loop. */ + SVN_ERR(err); + } + } + } + + return svn_error_createf(APR_EINVAL, NULL, + _("Couldn't get lock on destination repos " + "after %d attempts"), i); +} |