xref: /freebsd-13-stable/contrib/subversion/subversion/libsvn_client/relocate.c (revision 7725780a600dd28ae2c61f2d51c76d41a070958b)
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