relocate.c revision 299742
1/*
2 * relocate.c:  wrapper around wc relocation functionality.
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
27
28/*** Includes. ***/
29
30#include "svn_wc.h"
31#include "svn_client.h"
32#include "svn_pools.h"
33#include "svn_error.h"
34#include "svn_dirent_uri.h"
35#include "svn_path.h"
36#include "client.h"
37
38#include "private/svn_wc_private.h"
39
40#include "svn_private_config.h"
41
42
43/*** Code. ***/
44
45/* Repository root and UUID for a repository. */
46struct url_uuid_t
47{
48  const char *root;
49  const char *uuid;
50};
51
52struct validator_baton_t
53{
54  svn_client_ctx_t *ctx;
55  const char *path;
56  apr_array_header_t *url_uuids;
57  apr_pool_t *pool;
58
59};
60
61
62static svn_error_t *
63validator_func(void *baton,
64               const char *uuid,
65               const char *url,
66               const char *root_url,
67               apr_pool_t *pool)
68{
69  struct validator_baton_t *b = baton;
70  struct url_uuid_t *url_uuid = NULL;
71  const char *disable_checks;
72
73  apr_array_header_t *uuids = b->url_uuids;
74  int i;
75
76  for (i = 0; i < uuids->nelts; ++i)
77    {
78      struct url_uuid_t *uu = &APR_ARRAY_IDX(uuids, i,
79                                             struct url_uuid_t);
80      if (svn_uri__is_ancestor(uu->root, url))
81        {
82          url_uuid = uu;
83          break;
84        }
85    }
86
87  disable_checks = getenv("SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_RELOCATE_VALIDATION");
88  if (disable_checks && (strcmp(disable_checks, "yes") == 0))
89    {
90      /* Lie about URL_UUID's components, claiming they match the
91         expectations of the validation code below.  */
92      url_uuid = apr_pcalloc(pool, sizeof(*url_uuid));
93      url_uuid->root = apr_pstrdup(pool, root_url);
94      url_uuid->uuid = apr_pstrdup(pool, uuid);
95    }
96
97  /* We use an RA session in a subpool to get the UUID of the
98     repository at the new URL so we can force the RA session to close
99     by destroying the subpool. */
100  if (! url_uuid)
101    {
102      apr_pool_t *sesspool = svn_pool_create(pool);
103
104      url_uuid = &APR_ARRAY_PUSH(uuids, struct url_uuid_t);
105      SVN_ERR(svn_client_get_repos_root(&url_uuid->root,
106                                        &url_uuid->uuid,
107                                        url, b->ctx,
108                                        pool, sesspool));
109
110      svn_pool_destroy(sesspool);
111    }
112
113  /* Make sure the url is a repository root if desired. */
114  if (root_url
115      && strcmp(root_url, url_uuid->root) != 0)
116    return svn_error_createf(SVN_ERR_CLIENT_INVALID_RELOCATION, NULL,
117                             _("'%s' is not the root of the repository"),
118                             url);
119
120  /* Make sure the UUIDs match. */
121  if (uuid && strcmp(uuid, url_uuid->uuid) != 0)
122    return svn_error_createf
123      (SVN_ERR_CLIENT_INVALID_RELOCATION, NULL,
124       _("The repository at '%s' has uuid '%s', but the WC has '%s'"),
125       url, url_uuid->uuid, uuid);
126
127  return SVN_NO_ERROR;
128}
129
130svn_error_t *
131svn_client_relocate2(const char *wcroot_dir,
132                     const char *from_prefix,
133                     const char *to_prefix,
134                     svn_boolean_t ignore_externals,
135                     svn_client_ctx_t *ctx,
136                     apr_pool_t *pool)
137{
138  struct validator_baton_t vb;
139  const char *local_abspath;
140  apr_hash_t *externals_hash = NULL;
141  apr_hash_index_t *hi;
142  apr_pool_t *iterpool = NULL;
143  const char *old_repos_root_url, *new_repos_root_url;
144
145  /* Populate our validator callback baton, and call the relocate code. */
146  vb.ctx = ctx;
147  vb.path = wcroot_dir;
148  vb.url_uuids = apr_array_make(pool, 1, sizeof(struct url_uuid_t));
149  vb.pool = pool;
150
151  if (svn_path_is_url(wcroot_dir))
152    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
153                             _("'%s' is not a local path"),
154                             wcroot_dir);
155
156  SVN_ERR(svn_dirent_get_absolute(&local_abspath, wcroot_dir, pool));
157
158  /* If we're ignoring externals, just relocate and get outta here. */
159  if (ignore_externals)
160    {
161      return svn_error_trace(svn_wc_relocate4(ctx->wc_ctx, local_abspath,
162                                              from_prefix, to_prefix,
163                                              validator_func, &vb, pool));
164    }
165
166  /* Fetch our current root URL. */
167  SVN_ERR(svn_client_get_repos_root(&old_repos_root_url, NULL /* uuid */,
168                                    local_abspath, ctx, pool, pool));
169
170  /* Perform the relocation. */
171  SVN_ERR(svn_wc_relocate4(ctx->wc_ctx, local_abspath, from_prefix, to_prefix,
172                           validator_func, &vb, pool));
173
174  /* Now fetch new current root URL. */
175  SVN_ERR(svn_client_get_repos_root(&new_repos_root_url, NULL /* uuid */,
176                                    local_abspath, ctx, pool, pool));
177
178
179  /* Relocate externals, too (if any). */
180  SVN_ERR(svn_wc__externals_defined_below(&externals_hash,
181                                          ctx->wc_ctx, local_abspath,
182                                          pool, pool));
183  if (! apr_hash_count(externals_hash))
184    return SVN_NO_ERROR;
185
186  iterpool = svn_pool_create(pool);
187
188  for (hi = apr_hash_first(pool, externals_hash);
189       hi != NULL;
190       hi = apr_hash_next(hi))
191    {
192      svn_node_kind_t kind;
193      const char *this_abspath = apr_hash_this_key(hi);
194
195      svn_pool_clear(iterpool);
196
197      SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL,
198                                         ctx->wc_ctx,
199                                         local_abspath, this_abspath,
200                                         FALSE, iterpool, iterpool));
201
202      if (kind == svn_node_dir)
203        {
204          const char *this_repos_root_url;
205          svn_error_t *err;
206
207          err = svn_client_get_repos_root(&this_repos_root_url, NULL /* uuid */,
208                                          this_abspath, ctx, iterpool, iterpool);
209
210          /* Ignore externals that aren't present in the working copy.
211           * This can happen if an external is deleted from disk accidentally,
212           * or if an external is configured on a locally added directory. */
213          if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
214            {
215              svn_error_clear(err);
216              continue;
217            }
218          SVN_ERR(err);
219
220          if (strcmp(old_repos_root_url, this_repos_root_url) == 0)
221            SVN_ERR(svn_client_relocate2(this_abspath, from_prefix, to_prefix,
222                                         FALSE /* ignore_externals */,
223                                         ctx, iterpool));
224        }
225    }
226
227  svn_pool_destroy(iterpool);
228
229  return SVN_NO_ERROR;
230}
231