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. */
46 struct url_uuid_t
47 {
48 const char *root;
49 const char *uuid;
50 };
51
52 struct 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
62 static svn_error_t *
validator_func(void * baton,const char * uuid,const char * url,const char * root_url,apr_pool_t * pool)63 validator_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
130 svn_error_t *
svn_client_relocate2(const char * wcroot_dir,const char * from_prefix,const char * to_prefix,svn_boolean_t ignore_externals,svn_client_ctx_t * ctx,apr_pool_t * pool)131 svn_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