util.c revision 251881
1251881Speter/* 2251881Speter * util.c: Repository access utility routines. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter/* ==================================================================== */ 25251881Speter 26251881Speter/*** Includes. ***/ 27251881Speter#include <apr_pools.h> 28251881Speter#include <apr_network_io.h> 29251881Speter 30251881Speter#include "svn_types.h" 31251881Speter#include "svn_pools.h" 32251881Speter#include "svn_error.h" 33251881Speter#include "svn_error_codes.h" 34251881Speter#include "svn_dirent_uri.h" 35251881Speter#include "svn_path.h" 36251881Speter#include "svn_ra.h" 37251881Speter 38251881Speter#include "svn_private_config.h" 39251881Speter#include "private/svn_ra_private.h" 40251881Speter 41251881Spetersvn_error_t * 42251881Spetersvn_ra__assert_mergeinfo_capable_server(svn_ra_session_t *ra_session, 43251881Speter const char *path_or_url, 44251881Speter apr_pool_t *pool) 45251881Speter{ 46251881Speter svn_boolean_t mergeinfo_capable; 47251881Speter SVN_ERR(svn_ra_has_capability(ra_session, &mergeinfo_capable, 48251881Speter SVN_RA_CAPABILITY_MERGEINFO, pool)); 49251881Speter if (! mergeinfo_capable) 50251881Speter { 51251881Speter if (path_or_url == NULL) 52251881Speter { 53251881Speter svn_error_t *err = svn_ra_get_session_url(ra_session, &path_or_url, 54251881Speter pool); 55251881Speter if (err) 56251881Speter { 57251881Speter /* The SVN_ERR_UNSUPPORTED_FEATURE error is more important, 58251881Speter so dummy up the session's URL and chuck this error. */ 59251881Speter svn_error_clear(err); 60251881Speter path_or_url = "<repository>"; 61251881Speter } 62251881Speter } 63251881Speter return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 64251881Speter _("Retrieval of mergeinfo unsupported by '%s'"), 65251881Speter svn_path_is_url(path_or_url) 66251881Speter ? path_or_url 67251881Speter : svn_dirent_local_style(path_or_url, pool)); 68251881Speter } 69251881Speter return SVN_NO_ERROR; 70251881Speter} 71251881Speter 72251881Speter/* Does ERR mean "the current value of the revprop isn't equal to 73251881Speter the *OLD_VALUE_P you gave me"? 74251881Speter */ 75251881Speterstatic svn_boolean_t is_atomicity_error(svn_error_t *err) 76251881Speter{ 77251881Speter return svn_error_find_cause(err, SVN_ERR_FS_PROP_BASEVALUE_MISMATCH) != NULL; 78251881Speter} 79251881Speter 80251881Spetersvn_error_t * 81251881Spetersvn_ra__release_operational_lock(svn_ra_session_t *session, 82251881Speter const char *lock_revprop_name, 83251881Speter const svn_string_t *mylocktoken, 84251881Speter apr_pool_t *scratch_pool) 85251881Speter{ 86251881Speter svn_string_t *reposlocktoken; 87251881Speter svn_boolean_t be_atomic; 88251881Speter 89251881Speter SVN_ERR(svn_ra_has_capability(session, &be_atomic, 90251881Speter SVN_RA_CAPABILITY_ATOMIC_REVPROPS, 91251881Speter scratch_pool)); 92251881Speter SVN_ERR(svn_ra_rev_prop(session, 0, lock_revprop_name, 93251881Speter &reposlocktoken, scratch_pool)); 94251881Speter if (reposlocktoken && svn_string_compare(reposlocktoken, mylocktoken)) 95251881Speter { 96251881Speter svn_error_t *err; 97251881Speter 98251881Speter err = svn_ra_change_rev_prop2(session, 0, lock_revprop_name, 99251881Speter be_atomic ? &mylocktoken : NULL, NULL, 100251881Speter scratch_pool); 101251881Speter if (is_atomicity_error(err)) 102251881Speter { 103251881Speter return svn_error_createf(err->apr_err, err, 104251881Speter _("Lock was stolen by '%s'; unable to " 105251881Speter "remove it"), reposlocktoken->data); 106251881Speter } 107251881Speter else 108251881Speter SVN_ERR(err); 109251881Speter } 110251881Speter 111251881Speter return SVN_NO_ERROR; 112251881Speter} 113251881Speter 114251881Spetersvn_error_t * 115251881Spetersvn_ra__get_operational_lock(const svn_string_t **lock_string_p, 116251881Speter const svn_string_t **stolen_lock_p, 117251881Speter svn_ra_session_t *session, 118251881Speter const char *lock_revprop_name, 119251881Speter svn_boolean_t steal_lock, 120251881Speter int num_retries, 121251881Speter svn_ra__lock_retry_func_t retry_func, 122251881Speter void *retry_baton, 123251881Speter svn_cancel_func_t cancel_func, 124251881Speter void *cancel_baton, 125251881Speter apr_pool_t *pool) 126251881Speter{ 127251881Speter char hostname_str[APRMAXHOSTLEN + 1] = { 0 }; 128251881Speter svn_string_t *mylocktoken, *reposlocktoken; 129251881Speter apr_status_t apr_err; 130251881Speter svn_boolean_t be_atomic; 131251881Speter apr_pool_t *subpool; 132251881Speter int i; 133251881Speter 134251881Speter *lock_string_p = NULL; 135251881Speter if (stolen_lock_p) 136251881Speter *stolen_lock_p = NULL; 137251881Speter 138251881Speter SVN_ERR(svn_ra_has_capability(session, &be_atomic, 139251881Speter SVN_RA_CAPABILITY_ATOMIC_REVPROPS, pool)); 140251881Speter 141251881Speter /* We build a lock token from the local hostname and a UUID. */ 142251881Speter apr_err = apr_gethostname(hostname_str, sizeof(hostname_str), pool); 143251881Speter if (apr_err) 144251881Speter return svn_error_wrap_apr(apr_err, 145251881Speter _("Unable to determine local hostname")); 146251881Speter mylocktoken = svn_string_createf(pool, "%s:%s", hostname_str, 147251881Speter svn_uuid_generate(pool)); 148251881Speter 149251881Speter /* Ye Olde Retry Loope */ 150251881Speter subpool = svn_pool_create(pool); 151251881Speter 152251881Speter for (i = 0; i < num_retries; ++i) 153251881Speter { 154251881Speter svn_error_t *err; 155251881Speter const svn_string_t *unset = NULL; 156251881Speter 157251881Speter svn_pool_clear(subpool); 158251881Speter 159251881Speter /* Check for cancellation. If we're cancelled, don't leave a 160251881Speter stray lock behind! */ 161251881Speter if (cancel_func) 162251881Speter { 163251881Speter err = cancel_func(cancel_baton); 164251881Speter if (err && err->apr_err == SVN_ERR_CANCELLED) 165251881Speter return svn_error_compose_create( 166251881Speter svn_ra__release_operational_lock(session, 167251881Speter lock_revprop_name, 168251881Speter mylocktoken, 169251881Speter subpool), 170251881Speter err); 171251881Speter SVN_ERR(err); 172251881Speter } 173251881Speter 174251881Speter /* Ask the repository for the value of the LOCK_REVPROP_NAME. */ 175251881Speter SVN_ERR(svn_ra_rev_prop(session, 0, lock_revprop_name, 176251881Speter &reposlocktoken, subpool)); 177251881Speter 178251881Speter /* Did we get a value from the repository? We'll check to see 179251881Speter if it matches our token. If so, we call it success. If not 180251881Speter and we're told to steal locks, we remember the existing lock 181251881Speter token and fall through to the locking code; othewise, we 182251881Speter sleep and retry. */ 183251881Speter if (reposlocktoken) 184251881Speter { 185251881Speter if (svn_string_compare(reposlocktoken, mylocktoken)) 186251881Speter { 187251881Speter *lock_string_p = mylocktoken; 188251881Speter return SVN_NO_ERROR; 189251881Speter } 190251881Speter else if (! steal_lock) 191251881Speter { 192251881Speter if (retry_func) 193251881Speter SVN_ERR(retry_func(retry_baton, reposlocktoken, subpool)); 194251881Speter apr_sleep(apr_time_from_sec(1)); 195251881Speter continue; 196251881Speter } 197251881Speter else 198251881Speter { 199251881Speter if (stolen_lock_p) 200251881Speter *stolen_lock_p = svn_string_dup(reposlocktoken, pool); 201251881Speter unset = reposlocktoken; 202251881Speter } 203251881Speter } 204251881Speter 205251881Speter /* No lock value in the repository, or we plan to steal it? 206251881Speter Well, if we've got a spare iteration, we'll try to set the 207251881Speter lock. (We use the spare iteration to verify that we still 208251881Speter have the lock after setting it.) */ 209251881Speter if (i < num_retries - 1) 210251881Speter { 211251881Speter /* Except in the very last iteration, try to set the lock. */ 212251881Speter err = svn_ra_change_rev_prop2(session, 0, lock_revprop_name, 213251881Speter be_atomic ? &unset : NULL, 214251881Speter mylocktoken, subpool); 215251881Speter 216251881Speter if (be_atomic && err && is_atomicity_error(err)) 217251881Speter { 218251881Speter /* Someone else has the lock. No problem, we'll loop again. */ 219251881Speter svn_error_clear(err); 220251881Speter } 221251881Speter else if (be_atomic && err == SVN_NO_ERROR) 222251881Speter { 223251881Speter /* Yay! We have the lock! However, for compatibility 224251881Speter with concurrent processes that don't support 225251881Speter atomicity, loop anyway to double-check that they 226251881Speter haven't overwritten our lock. 227251881Speter */ 228251881Speter continue; 229251881Speter } 230251881Speter else 231251881Speter { 232251881Speter /* We have a genuine error, or aren't atomic and need 233251881Speter to loop. */ 234251881Speter SVN_ERR(err); 235251881Speter } 236251881Speter } 237251881Speter } 238251881Speter 239251881Speter return svn_error_createf(APR_EINVAL, NULL, 240251881Speter _("Couldn't get lock on destination repos " 241251881Speter "after %d attempts"), i); 242251881Speter} 243