2 * util.c: Repository access utility routines.
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
24 /* ==================================================================== */
27 #include <apr_pools.h>
28 #include <apr_network_io.h>
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"
38 #include "svn_private_config.h"
39 #include "private/svn_ra_private.h"
42 get_path(const char *path_or_url,
43 svn_ra_session_t *ra_session,
46 if (path_or_url == NULL)
48 svn_error_t *err = svn_ra_get_session_url(ra_session, &path_or_url,
52 /* The SVN_ERR_UNSUPPORTED_FEATURE error in the caller is more
53 important, so dummy up the session's URL and chuck this error. */
55 return _("<repository>");
62 svn_ra__assert_mergeinfo_capable_server(svn_ra_session_t *ra_session,
63 const char *path_or_url,
66 svn_boolean_t mergeinfo_capable;
67 SVN_ERR(svn_ra_has_capability(ra_session, &mergeinfo_capable,
68 SVN_RA_CAPABILITY_MERGEINFO, pool));
69 if (! mergeinfo_capable)
71 path_or_url = get_path(path_or_url, ra_session, pool);
72 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
73 _("Retrieval of mergeinfo unsupported by '%s'"),
74 svn_path_is_url(path_or_url)
76 : svn_dirent_local_style(path_or_url, pool));
82 svn_ra__assert_capable_server(svn_ra_session_t *ra_session,
83 const char *capability,
84 const char *path_or_url,
87 if (!strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO))
88 return svn_ra__assert_mergeinfo_capable_server(ra_session, path_or_url,
94 SVN_ERR(svn_ra_has_capability(ra_session, &has, capability, pool));
97 path_or_url = get_path(path_or_url, ra_session, pool);
98 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
99 _("The '%s' feature is not supported by '%s'"),
101 svn_path_is_url(path_or_url)
103 : svn_dirent_local_style(path_or_url,
110 /* Does ERR mean "the current value of the revprop isn't equal to
111 the *OLD_VALUE_P you gave me"?
113 static svn_boolean_t is_atomicity_error(svn_error_t *err)
115 return svn_error_find_cause(err, SVN_ERR_FS_PROP_BASEVALUE_MISMATCH) != NULL;
119 svn_ra__release_operational_lock(svn_ra_session_t *session,
120 const char *lock_revprop_name,
121 const svn_string_t *mylocktoken,
122 apr_pool_t *scratch_pool)
124 svn_string_t *reposlocktoken;
125 svn_boolean_t be_atomic;
127 SVN_ERR(svn_ra_has_capability(session, &be_atomic,
128 SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
130 SVN_ERR(svn_ra_rev_prop(session, 0, lock_revprop_name,
131 &reposlocktoken, scratch_pool));
132 if (reposlocktoken && svn_string_compare(reposlocktoken, mylocktoken))
136 err = svn_ra_change_rev_prop2(session, 0, lock_revprop_name,
137 be_atomic ? &mylocktoken : NULL, NULL,
139 if (is_atomicity_error(err))
141 return svn_error_createf(err->apr_err, err,
142 _("Lock was stolen by '%s'; unable to "
143 "remove it"), reposlocktoken->data);
153 svn_ra__get_operational_lock(const svn_string_t **lock_string_p,
154 const svn_string_t **stolen_lock_p,
155 svn_ra_session_t *session,
156 const char *lock_revprop_name,
157 svn_boolean_t steal_lock,
159 svn_ra__lock_retry_func_t retry_func,
161 svn_cancel_func_t cancel_func,
165 char hostname_str[APRMAXHOSTLEN + 1] = { 0 };
166 svn_string_t *mylocktoken, *reposlocktoken;
167 apr_status_t apr_err;
168 svn_boolean_t be_atomic;
172 *lock_string_p = NULL;
174 *stolen_lock_p = NULL;
176 SVN_ERR(svn_ra_has_capability(session, &be_atomic,
177 SVN_RA_CAPABILITY_ATOMIC_REVPROPS, pool));
179 /* We build a lock token from the local hostname and a UUID. */
180 apr_err = apr_gethostname(hostname_str, sizeof(hostname_str), pool);
182 return svn_error_wrap_apr(apr_err,
183 _("Unable to determine local hostname"));
184 mylocktoken = svn_string_createf(pool, "%s:%s", hostname_str,
185 svn_uuid_generate(pool));
187 /* Ye Olde Retry Loope */
188 subpool = svn_pool_create(pool);
190 for (i = 0; i < num_retries; ++i)
193 const svn_string_t *unset = NULL;
195 svn_pool_clear(subpool);
197 /* Check for cancellation. If we're cancelled, don't leave a
198 stray lock behind! */
201 err = cancel_func(cancel_baton);
202 if (err && err->apr_err == SVN_ERR_CANCELLED)
203 return svn_error_compose_create(
204 svn_ra__release_operational_lock(session,
212 /* Ask the repository for the value of the LOCK_REVPROP_NAME. */
213 SVN_ERR(svn_ra_rev_prop(session, 0, lock_revprop_name,
214 &reposlocktoken, subpool));
216 /* Did we get a value from the repository? We'll check to see
217 if it matches our token. If so, we call it success. If not
218 and we're told to steal locks, we remember the existing lock
219 token and fall through to the locking code; othewise, we
223 if (svn_string_compare(reposlocktoken, mylocktoken))
225 *lock_string_p = mylocktoken;
228 else if (! steal_lock)
231 SVN_ERR(retry_func(retry_baton, reposlocktoken, subpool));
232 apr_sleep(apr_time_from_sec(1));
238 *stolen_lock_p = svn_string_dup(reposlocktoken, pool);
239 unset = reposlocktoken;
243 /* No lock value in the repository, or we plan to steal it?
244 Well, if we've got a spare iteration, we'll try to set the
245 lock. (We use the spare iteration to verify that we still
246 have the lock after setting it.) */
247 if (i < num_retries - 1)
249 /* Except in the very last iteration, try to set the lock. */
250 err = svn_ra_change_rev_prop2(session, 0, lock_revprop_name,
251 be_atomic ? &unset : NULL,
252 mylocktoken, subpool);
254 if (be_atomic && err && is_atomicity_error(err))
256 /* Someone else has the lock. No problem, we'll loop again. */
257 svn_error_clear(err);
259 else if (be_atomic && err == SVN_NO_ERROR)
261 /* Yay! We have the lock! However, for compatibility
262 with concurrent processes that don't support
263 atomicity, loop anyway to double-check that they
264 haven't overwritten our lock.
270 /* We have a genuine error, or aren't atomic and need
277 return svn_error_createf(APR_EINVAL, NULL,
278 _("Couldn't get lock on destination repos "
279 "after %d attempts"), i);