util.c revision 251886
1/* 2 * util.c: Repository access utility routines. 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24/* ==================================================================== */ 25 26/*** Includes. ***/ 27#include <apr_pools.h> 28#include <apr_network_io.h> 29 30#include "svn_types.h" 31#include "svn_pools.h" 32#include "svn_error.h" 33#include "svn_error_codes.h" 34#include "svn_dirent_uri.h" 35#include "svn_path.h" 36#include "svn_ra.h" 37 38#include "svn_private_config.h" 39#include "private/svn_ra_private.h" 40 41svn_error_t * 42svn_ra__assert_mergeinfo_capable_server(svn_ra_session_t *ra_session, 43 const char *path_or_url, 44 apr_pool_t *pool) 45{ 46 svn_boolean_t mergeinfo_capable; 47 SVN_ERR(svn_ra_has_capability(ra_session, &mergeinfo_capable, 48 SVN_RA_CAPABILITY_MERGEINFO, pool)); 49 if (! mergeinfo_capable) 50 { 51 if (path_or_url == NULL) 52 { 53 svn_error_t *err = svn_ra_get_session_url(ra_session, &path_or_url, 54 pool); 55 if (err) 56 { 57 /* The SVN_ERR_UNSUPPORTED_FEATURE error is more important, 58 so dummy up the session's URL and chuck this error. */ 59 svn_error_clear(err); 60 path_or_url = "<repository>"; 61 } 62 } 63 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 64 _("Retrieval of mergeinfo unsupported by '%s'"), 65 svn_path_is_url(path_or_url) 66 ? path_or_url 67 : svn_dirent_local_style(path_or_url, pool)); 68 } 69 return SVN_NO_ERROR; 70} 71 72/* Does ERR mean "the current value of the revprop isn't equal to 73 the *OLD_VALUE_P you gave me"? 74 */ 75static svn_boolean_t is_atomicity_error(svn_error_t *err) 76{ 77 return svn_error_find_cause(err, SVN_ERR_FS_PROP_BASEVALUE_MISMATCH) != NULL; 78} 79 80svn_error_t * 81svn_ra__release_operational_lock(svn_ra_session_t *session, 82 const char *lock_revprop_name, 83 const svn_string_t *mylocktoken, 84 apr_pool_t *scratch_pool) 85{ 86 svn_string_t *reposlocktoken; 87 svn_boolean_t be_atomic; 88 89 SVN_ERR(svn_ra_has_capability(session, &be_atomic, 90 SVN_RA_CAPABILITY_ATOMIC_REVPROPS, 91 scratch_pool)); 92 SVN_ERR(svn_ra_rev_prop(session, 0, lock_revprop_name, 93 &reposlocktoken, scratch_pool)); 94 if (reposlocktoken && svn_string_compare(reposlocktoken, mylocktoken)) 95 { 96 svn_error_t *err; 97 98 err = svn_ra_change_rev_prop2(session, 0, lock_revprop_name, 99 be_atomic ? &mylocktoken : NULL, NULL, 100 scratch_pool); 101 if (is_atomicity_error(err)) 102 { 103 return svn_error_createf(err->apr_err, err, 104 _("Lock was stolen by '%s'; unable to " 105 "remove it"), reposlocktoken->data); 106 } 107 else 108 SVN_ERR(err); 109 } 110 111 return SVN_NO_ERROR; 112} 113 114svn_error_t * 115svn_ra__get_operational_lock(const svn_string_t **lock_string_p, 116 const svn_string_t **stolen_lock_p, 117 svn_ra_session_t *session, 118 const char *lock_revprop_name, 119 svn_boolean_t steal_lock, 120 int num_retries, 121 svn_ra__lock_retry_func_t retry_func, 122 void *retry_baton, 123 svn_cancel_func_t cancel_func, 124 void *cancel_baton, 125 apr_pool_t *pool) 126{ 127 char hostname_str[APRMAXHOSTLEN + 1] = { 0 }; 128 svn_string_t *mylocktoken, *reposlocktoken; 129 apr_status_t apr_err; 130 svn_boolean_t be_atomic; 131 apr_pool_t *subpool; 132 int i; 133 134 *lock_string_p = NULL; 135 if (stolen_lock_p) 136 *stolen_lock_p = NULL; 137 138 SVN_ERR(svn_ra_has_capability(session, &be_atomic, 139 SVN_RA_CAPABILITY_ATOMIC_REVPROPS, pool)); 140 141 /* We build a lock token from the local hostname and a UUID. */ 142 apr_err = apr_gethostname(hostname_str, sizeof(hostname_str), pool); 143 if (apr_err) 144 return svn_error_wrap_apr(apr_err, 145 _("Unable to determine local hostname")); 146 mylocktoken = svn_string_createf(pool, "%s:%s", hostname_str, 147 svn_uuid_generate(pool)); 148 149 /* Ye Olde Retry Loope */ 150 subpool = svn_pool_create(pool); 151 152 for (i = 0; i < num_retries; ++i) 153 { 154 svn_error_t *err; 155 const svn_string_t *unset = NULL; 156 157 svn_pool_clear(subpool); 158 159 /* Check for cancellation. If we're cancelled, don't leave a 160 stray lock behind! */ 161 if (cancel_func) 162 { 163 err = cancel_func(cancel_baton); 164 if (err && err->apr_err == SVN_ERR_CANCELLED) 165 return svn_error_compose_create( 166 svn_ra__release_operational_lock(session, 167 lock_revprop_name, 168 mylocktoken, 169 subpool), 170 err); 171 SVN_ERR(err); 172 } 173 174 /* Ask the repository for the value of the LOCK_REVPROP_NAME. */ 175 SVN_ERR(svn_ra_rev_prop(session, 0, lock_revprop_name, 176 &reposlocktoken, subpool)); 177 178 /* Did we get a value from the repository? We'll check to see 179 if it matches our token. If so, we call it success. If not 180 and we're told to steal locks, we remember the existing lock 181 token and fall through to the locking code; othewise, we 182 sleep and retry. */ 183 if (reposlocktoken) 184 { 185 if (svn_string_compare(reposlocktoken, mylocktoken)) 186 { 187 *lock_string_p = mylocktoken; 188 return SVN_NO_ERROR; 189 } 190 else if (! steal_lock) 191 { 192 if (retry_func) 193 SVN_ERR(retry_func(retry_baton, reposlocktoken, subpool)); 194 apr_sleep(apr_time_from_sec(1)); 195 continue; 196 } 197 else 198 { 199 if (stolen_lock_p) 200 *stolen_lock_p = svn_string_dup(reposlocktoken, pool); 201 unset = reposlocktoken; 202 } 203 } 204 205 /* No lock value in the repository, or we plan to steal it? 206 Well, if we've got a spare iteration, we'll try to set the 207 lock. (We use the spare iteration to verify that we still 208 have the lock after setting it.) */ 209 if (i < num_retries - 1) 210 { 211 /* Except in the very last iteration, try to set the lock. */ 212 err = svn_ra_change_rev_prop2(session, 0, lock_revprop_name, 213 be_atomic ? &unset : NULL, 214 mylocktoken, subpool); 215 216 if (be_atomic && err && is_atomicity_error(err)) 217 { 218 /* Someone else has the lock. No problem, we'll loop again. */ 219 svn_error_clear(err); 220 } 221 else if (be_atomic && err == SVN_NO_ERROR) 222 { 223 /* Yay! We have the lock! However, for compatibility 224 with concurrent processes that don't support 225 atomicity, loop anyway to double-check that they 226 haven't overwritten our lock. 227 */ 228 continue; 229 } 230 else 231 { 232 /* We have a genuine error, or aren't atomic and need 233 to loop. */ 234 SVN_ERR(err); 235 } 236 } 237 } 238 239 return svn_error_createf(APR_EINVAL, NULL, 240 _("Couldn't get lock on destination repos " 241 "after %d attempts"), i); 242} 243