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 41253734Speterstatic const char * 42253734Speterget_path(const char *path_or_url, 43253734Speter svn_ra_session_t *ra_session, 44253734Speter apr_pool_t *pool) 45253734Speter{ 46253734Speter if (path_or_url == NULL) 47253734Speter { 48253734Speter svn_error_t *err = svn_ra_get_session_url(ra_session, &path_or_url, 49253734Speter pool); 50253734Speter if (err) 51253734Speter { 52253734Speter /* The SVN_ERR_UNSUPPORTED_FEATURE error in the caller is more 53253734Speter important, so dummy up the session's URL and chuck this error. */ 54253734Speter svn_error_clear(err); 55253734Speter return _("<repository>"); 56253734Speter } 57253734Speter } 58253734Speter return path_or_url; 59253734Speter} 60253734Speter 61251881Spetersvn_error_t * 62251881Spetersvn_ra__assert_mergeinfo_capable_server(svn_ra_session_t *ra_session, 63251881Speter const char *path_or_url, 64251881Speter apr_pool_t *pool) 65251881Speter{ 66251881Speter svn_boolean_t mergeinfo_capable; 67251881Speter SVN_ERR(svn_ra_has_capability(ra_session, &mergeinfo_capable, 68251881Speter SVN_RA_CAPABILITY_MERGEINFO, pool)); 69251881Speter if (! mergeinfo_capable) 70251881Speter { 71253734Speter path_or_url = get_path(path_or_url, ra_session, pool); 72251881Speter return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 73251881Speter _("Retrieval of mergeinfo unsupported by '%s'"), 74251881Speter svn_path_is_url(path_or_url) 75251881Speter ? path_or_url 76251881Speter : svn_dirent_local_style(path_or_url, pool)); 77251881Speter } 78251881Speter return SVN_NO_ERROR; 79251881Speter} 80251881Speter 81253734Spetersvn_error_t * 82253734Spetersvn_ra__assert_capable_server(svn_ra_session_t *ra_session, 83253734Speter const char *capability, 84253734Speter const char *path_or_url, 85253734Speter apr_pool_t *pool) 86253734Speter{ 87253734Speter if (!strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO)) 88253734Speter return svn_ra__assert_mergeinfo_capable_server(ra_session, path_or_url, 89253734Speter pool); 90253734Speter 91253734Speter else 92253734Speter { 93253734Speter svn_boolean_t has; 94253734Speter SVN_ERR(svn_ra_has_capability(ra_session, &has, capability, pool)); 95253734Speter if (! has) 96253734Speter { 97253734Speter path_or_url = get_path(path_or_url, ra_session, pool); 98253734Speter return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 99253734Speter _("The '%s' feature is not supported by '%s'"), 100253734Speter capability, 101253734Speter svn_path_is_url(path_or_url) 102253734Speter ? path_or_url 103253734Speter : svn_dirent_local_style(path_or_url, 104253734Speter pool)); 105253734Speter } 106253734Speter } 107253734Speter return SVN_NO_ERROR; 108253734Speter} 109253734Speter 110251881Speter/* Does ERR mean "the current value of the revprop isn't equal to 111251881Speter the *OLD_VALUE_P you gave me"? 112251881Speter */ 113251881Speterstatic svn_boolean_t is_atomicity_error(svn_error_t *err) 114251881Speter{ 115251881Speter return svn_error_find_cause(err, SVN_ERR_FS_PROP_BASEVALUE_MISMATCH) != NULL; 116251881Speter} 117251881Speter 118251881Spetersvn_error_t * 119251881Spetersvn_ra__release_operational_lock(svn_ra_session_t *session, 120251881Speter const char *lock_revprop_name, 121251881Speter const svn_string_t *mylocktoken, 122251881Speter apr_pool_t *scratch_pool) 123251881Speter{ 124251881Speter svn_string_t *reposlocktoken; 125251881Speter svn_boolean_t be_atomic; 126251881Speter 127251881Speter SVN_ERR(svn_ra_has_capability(session, &be_atomic, 128251881Speter SVN_RA_CAPABILITY_ATOMIC_REVPROPS, 129251881Speter scratch_pool)); 130251881Speter SVN_ERR(svn_ra_rev_prop(session, 0, lock_revprop_name, 131251881Speter &reposlocktoken, scratch_pool)); 132251881Speter if (reposlocktoken && svn_string_compare(reposlocktoken, mylocktoken)) 133251881Speter { 134251881Speter svn_error_t *err; 135251881Speter 136251881Speter err = svn_ra_change_rev_prop2(session, 0, lock_revprop_name, 137251881Speter be_atomic ? &mylocktoken : NULL, NULL, 138251881Speter scratch_pool); 139251881Speter if (is_atomicity_error(err)) 140251881Speter { 141251881Speter return svn_error_createf(err->apr_err, err, 142251881Speter _("Lock was stolen by '%s'; unable to " 143251881Speter "remove it"), reposlocktoken->data); 144251881Speter } 145251881Speter else 146251881Speter SVN_ERR(err); 147251881Speter } 148251881Speter 149251881Speter return SVN_NO_ERROR; 150251881Speter} 151251881Speter 152251881Spetersvn_error_t * 153251881Spetersvn_ra__get_operational_lock(const svn_string_t **lock_string_p, 154251881Speter const svn_string_t **stolen_lock_p, 155251881Speter svn_ra_session_t *session, 156251881Speter const char *lock_revprop_name, 157251881Speter svn_boolean_t steal_lock, 158251881Speter int num_retries, 159251881Speter svn_ra__lock_retry_func_t retry_func, 160251881Speter void *retry_baton, 161251881Speter svn_cancel_func_t cancel_func, 162251881Speter void *cancel_baton, 163251881Speter apr_pool_t *pool) 164251881Speter{ 165251881Speter char hostname_str[APRMAXHOSTLEN + 1] = { 0 }; 166251881Speter svn_string_t *mylocktoken, *reposlocktoken; 167251881Speter apr_status_t apr_err; 168251881Speter svn_boolean_t be_atomic; 169251881Speter apr_pool_t *subpool; 170251881Speter int i; 171251881Speter 172251881Speter *lock_string_p = NULL; 173251881Speter if (stolen_lock_p) 174251881Speter *stolen_lock_p = NULL; 175251881Speter 176251881Speter SVN_ERR(svn_ra_has_capability(session, &be_atomic, 177251881Speter SVN_RA_CAPABILITY_ATOMIC_REVPROPS, pool)); 178251881Speter 179251881Speter /* We build a lock token from the local hostname and a UUID. */ 180251881Speter apr_err = apr_gethostname(hostname_str, sizeof(hostname_str), pool); 181251881Speter if (apr_err) 182251881Speter return svn_error_wrap_apr(apr_err, 183251881Speter _("Unable to determine local hostname")); 184251881Speter mylocktoken = svn_string_createf(pool, "%s:%s", hostname_str, 185251881Speter svn_uuid_generate(pool)); 186251881Speter 187251881Speter /* Ye Olde Retry Loope */ 188251881Speter subpool = svn_pool_create(pool); 189251881Speter 190251881Speter for (i = 0; i < num_retries; ++i) 191251881Speter { 192251881Speter svn_error_t *err; 193251881Speter const svn_string_t *unset = NULL; 194251881Speter 195251881Speter svn_pool_clear(subpool); 196251881Speter 197251881Speter /* Check for cancellation. If we're cancelled, don't leave a 198251881Speter stray lock behind! */ 199251881Speter if (cancel_func) 200251881Speter { 201251881Speter err = cancel_func(cancel_baton); 202251881Speter if (err && err->apr_err == SVN_ERR_CANCELLED) 203251881Speter return svn_error_compose_create( 204251881Speter svn_ra__release_operational_lock(session, 205251881Speter lock_revprop_name, 206251881Speter mylocktoken, 207251881Speter subpool), 208251881Speter err); 209251881Speter SVN_ERR(err); 210251881Speter } 211251881Speter 212251881Speter /* Ask the repository for the value of the LOCK_REVPROP_NAME. */ 213251881Speter SVN_ERR(svn_ra_rev_prop(session, 0, lock_revprop_name, 214251881Speter &reposlocktoken, subpool)); 215251881Speter 216251881Speter /* Did we get a value from the repository? We'll check to see 217251881Speter if it matches our token. If so, we call it success. If not 218251881Speter and we're told to steal locks, we remember the existing lock 219251881Speter token and fall through to the locking code; othewise, we 220251881Speter sleep and retry. */ 221251881Speter if (reposlocktoken) 222251881Speter { 223251881Speter if (svn_string_compare(reposlocktoken, mylocktoken)) 224251881Speter { 225251881Speter *lock_string_p = mylocktoken; 226251881Speter return SVN_NO_ERROR; 227251881Speter } 228251881Speter else if (! steal_lock) 229251881Speter { 230251881Speter if (retry_func) 231251881Speter SVN_ERR(retry_func(retry_baton, reposlocktoken, subpool)); 232251881Speter apr_sleep(apr_time_from_sec(1)); 233251881Speter continue; 234251881Speter } 235251881Speter else 236251881Speter { 237251881Speter if (stolen_lock_p) 238251881Speter *stolen_lock_p = svn_string_dup(reposlocktoken, pool); 239251881Speter unset = reposlocktoken; 240251881Speter } 241251881Speter } 242251881Speter 243251881Speter /* No lock value in the repository, or we plan to steal it? 244251881Speter Well, if we've got a spare iteration, we'll try to set the 245251881Speter lock. (We use the spare iteration to verify that we still 246251881Speter have the lock after setting it.) */ 247251881Speter if (i < num_retries - 1) 248251881Speter { 249251881Speter /* Except in the very last iteration, try to set the lock. */ 250251881Speter err = svn_ra_change_rev_prop2(session, 0, lock_revprop_name, 251251881Speter be_atomic ? &unset : NULL, 252251881Speter mylocktoken, subpool); 253251881Speter 254251881Speter if (be_atomic && err && is_atomicity_error(err)) 255251881Speter { 256251881Speter /* Someone else has the lock. No problem, we'll loop again. */ 257251881Speter svn_error_clear(err); 258251881Speter } 259251881Speter else if (be_atomic && err == SVN_NO_ERROR) 260251881Speter { 261251881Speter /* Yay! We have the lock! However, for compatibility 262251881Speter with concurrent processes that don't support 263251881Speter atomicity, loop anyway to double-check that they 264251881Speter haven't overwritten our lock. 265251881Speter */ 266251881Speter continue; 267251881Speter } 268251881Speter else 269251881Speter { 270251881Speter /* We have a genuine error, or aren't atomic and need 271251881Speter to loop. */ 272251881Speter SVN_ERR(err); 273251881Speter } 274251881Speter } 275251881Speter } 276251881Speter 277251881Speter return svn_error_createf(APR_EINVAL, NULL, 278251881Speter _("Couldn't get lock on destination repos " 279251881Speter "after %d attempts"), i); 280251881Speter} 281