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 char *sig_from_prefix, *sig_to_prefix;
145 apr_size_t index_from, index_to;
146
147 /* Populate our validator callback baton, and call the relocate code. */
148 vb.ctx = ctx;
149 vb.path = wcroot_dir;
150 vb.url_uuids = apr_array_make(pool, 1, sizeof(struct url_uuid_t));
151 vb.pool = pool;
152
153 if (svn_path_is_url(wcroot_dir))
154 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
155 _("'%s' is not a local path"),
156 wcroot_dir);
157
158 SVN_ERR(svn_dirent_get_absolute(&local_abspath, wcroot_dir, pool));
159
160 /* If we're ignoring externals, just relocate and get outta here. */
161 if (ignore_externals)
162 {
163 return svn_error_trace(svn_wc_relocate4(ctx->wc_ctx, local_abspath,
164 from_prefix, to_prefix,
165 validator_func, &vb, pool));
166 }
167
168 /* Fetch our current root URL. */
169 SVN_ERR(svn_client_get_repos_root(&old_repos_root_url, NULL /* uuid */,
170 local_abspath, ctx, pool, pool));
171
172 /* Perform the relocation. */
173 SVN_ERR(svn_wc_relocate4(ctx->wc_ctx, local_abspath, from_prefix, to_prefix,
174 validator_func, &vb, pool));
175
176 /* Now fetch new current root URL. */
177 SVN_ERR(svn_client_get_repos_root(&new_repos_root_url, NULL /* uuid */,
178 local_abspath, ctx, pool, pool));
179
180
181 /* Relocate externals, too (if any). */
182 SVN_ERR(svn_wc__externals_defined_below(&externals_hash,
183 ctx->wc_ctx, local_abspath,
184 pool, pool));
185 if (! apr_hash_count(externals_hash))
186 return SVN_NO_ERROR;
187
188 /* A valid prefix for the main working copy may be too long to be
189 valid for an external. Trim any common trailing characters to
190 leave the significant part that changes. */
191 sig_from_prefix = apr_pstrdup(pool, from_prefix);
192 sig_to_prefix = apr_pstrdup(pool, to_prefix);
193 index_from = strlen(sig_from_prefix);
194 index_to = strlen(sig_to_prefix);
195 while (index_from && index_to
196 && sig_from_prefix[index_from] == sig_to_prefix[index_to])
197 {
198 sig_from_prefix[index_from] = sig_to_prefix[index_to] = '\0';
199 --index_from;
200 --index_to;
201 }
202
203 iterpool = svn_pool_create(pool);
204
205 for (hi = apr_hash_first(pool, externals_hash);
206 hi != NULL;
207 hi = apr_hash_next(hi))
208 {
209 svn_node_kind_t kind;
210 const char *this_abspath = apr_hash_this_key(hi);
211
212 svn_pool_clear(iterpool);
213
214 SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL,
215 ctx->wc_ctx,
216 local_abspath, this_abspath,
217 FALSE, iterpool, iterpool));
218
219 if (kind == svn_node_dir)
220 {
221 const char *this_repos_root_url;
222 svn_error_t *err;
223
224 err = svn_client_get_repos_root(&this_repos_root_url, NULL /* uuid */,
225 this_abspath, ctx, iterpool, iterpool);
226
227 /* Ignore externals that aren't present in the working copy.
228 * This can happen if an external is deleted from disk accidentally,
229 * or if an external is configured on a locally added directory. */
230 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
231 {
232 svn_error_clear(err);
233 continue;
234 }
235 SVN_ERR(err);
236
237 if (strcmp(old_repos_root_url, this_repos_root_url) == 0)
238 SVN_ERR(svn_client_relocate2(this_abspath,
239 sig_from_prefix, sig_to_prefix,
240 FALSE /* ignore_externals */,
241 ctx, iterpool));
242 }
243 }
244
245 svn_pool_destroy(iterpool);
246
247 return SVN_NO_ERROR;
248 }
249