]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_ra/util.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_ra / util.c
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 \f
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
41 static const char *
42 get_path(const char *path_or_url,
43          svn_ra_session_t *ra_session,
44          apr_pool_t *pool)
45 {
46   if (path_or_url == NULL)
47     {
48       svn_error_t *err = svn_ra_get_session_url(ra_session, &path_or_url,
49                                                 pool);
50       if (err)
51         {
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. */
54           svn_error_clear(err);
55           return _("<repository>");
56         }
57     }
58   return path_or_url;
59 }
60
61 svn_error_t *
62 svn_ra__assert_mergeinfo_capable_server(svn_ra_session_t *ra_session,
63                                         const char *path_or_url,
64                                         apr_pool_t *pool)
65 {
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)
70     {
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)
75                                   ? path_or_url
76                                   : svn_dirent_local_style(path_or_url, pool));
77     }
78   return SVN_NO_ERROR;
79 }
80
81 svn_error_t *
82 svn_ra__assert_capable_server(svn_ra_session_t *ra_session,
83                               const char *capability,
84                               const char *path_or_url,
85                               apr_pool_t *pool)
86 {
87   if (!strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO))
88     return svn_ra__assert_mergeinfo_capable_server(ra_session, path_or_url,
89                                                    pool);
90
91   else
92     {
93       svn_boolean_t has;
94       SVN_ERR(svn_ra_has_capability(ra_session, &has, capability, pool));
95       if (! has)
96         {
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'"),
100                                  capability,
101                                  svn_path_is_url(path_or_url)
102                                     ? path_or_url
103                                     : svn_dirent_local_style(path_or_url,
104                                                              pool));
105         }
106     }
107   return SVN_NO_ERROR;
108 }
109
110 /* Does ERR mean "the current value of the revprop isn't equal to
111    the *OLD_VALUE_P you gave me"?
112  */
113 static svn_boolean_t is_atomicity_error(svn_error_t *err)
114 {
115   return svn_error_find_cause(err, SVN_ERR_FS_PROP_BASEVALUE_MISMATCH) != NULL;
116 }
117
118 svn_error_t *
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)
123 {
124   svn_string_t *reposlocktoken;
125   svn_boolean_t be_atomic;
126
127   SVN_ERR(svn_ra_has_capability(session, &be_atomic,
128                                 SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
129                                 scratch_pool));
130   SVN_ERR(svn_ra_rev_prop(session, 0, lock_revprop_name,
131                           &reposlocktoken, scratch_pool));
132   if (reposlocktoken && svn_string_compare(reposlocktoken, mylocktoken))
133     {
134       svn_error_t *err;
135
136       err = svn_ra_change_rev_prop2(session, 0, lock_revprop_name,
137                                     be_atomic ? &mylocktoken : NULL, NULL,
138                                     scratch_pool);
139       if (is_atomicity_error(err))
140         {
141           return svn_error_createf(err->apr_err, err,
142                                    _("Lock was stolen by '%s'; unable to "
143                                      "remove it"), reposlocktoken->data);
144         }
145       else
146         SVN_ERR(err);
147     }
148
149   return SVN_NO_ERROR;
150 }
151
152 svn_error_t *
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,
158                              int num_retries,
159                              svn_ra__lock_retry_func_t retry_func,
160                              void *retry_baton,
161                              svn_cancel_func_t cancel_func,
162                              void *cancel_baton,
163                              apr_pool_t *pool)
164 {
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;
169   apr_pool_t *subpool;
170   int i;
171
172   *lock_string_p = NULL;
173   if (stolen_lock_p)
174     *stolen_lock_p = NULL;
175
176   SVN_ERR(svn_ra_has_capability(session, &be_atomic,
177                                 SVN_RA_CAPABILITY_ATOMIC_REVPROPS, pool));
178
179   /* We build a lock token from the local hostname and a UUID.  */
180   apr_err = apr_gethostname(hostname_str, sizeof(hostname_str), pool);
181   if (apr_err)
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));
186
187   /* Ye Olde Retry Loope */
188   subpool = svn_pool_create(pool);
189
190   for (i = 0; i < num_retries; ++i)
191     {
192       svn_error_t *err;
193       const svn_string_t *unset = NULL;
194
195       svn_pool_clear(subpool);
196
197       /* Check for cancellation.  If we're cancelled, don't leave a
198          stray lock behind!  */
199       if (cancel_func)
200         {
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,
205                                                         lock_revprop_name,
206                                                         mylocktoken,
207                                                         subpool),
208                        err);
209           SVN_ERR(err);
210         }
211
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));
215
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
220          sleep and retry. */
221       if (reposlocktoken)
222         {
223           if (svn_string_compare(reposlocktoken, mylocktoken))
224             {
225               *lock_string_p = mylocktoken;
226               return SVN_NO_ERROR;
227             }
228           else if (! steal_lock)
229             {
230               if (retry_func)
231                 SVN_ERR(retry_func(retry_baton, reposlocktoken, subpool));
232               apr_sleep(apr_time_from_sec(1));
233               continue;
234             }
235           else
236             {
237               if (stolen_lock_p)
238                 *stolen_lock_p = svn_string_dup(reposlocktoken, pool);
239               unset = reposlocktoken;
240             }
241         }
242
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)
248         {
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);
253
254           if (be_atomic && err && is_atomicity_error(err))
255             {
256               /* Someone else has the lock.  No problem, we'll loop again. */
257               svn_error_clear(err);
258             }
259           else if (be_atomic && err == SVN_NO_ERROR)
260             {
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.
265               */
266               continue;
267             }
268           else
269             {
270               /* We have a genuine error, or aren't atomic and need
271                  to loop.  */
272               SVN_ERR(err);
273             }
274         }
275     }
276
277   return svn_error_createf(APR_EINVAL, NULL,
278                            _("Couldn't get lock on destination repos "
279                              "after %d attempts"), i);
280 }