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