upgrade.c revision 299742
1/* 2 * upgrade.c: wrapper around wc upgrade 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_time.h" 31#include "svn_wc.h" 32#include "svn_client.h" 33#include "svn_config.h" 34#include "svn_dirent_uri.h" 35#include "svn_path.h" 36#include "svn_pools.h" 37#include "client.h" 38#include "svn_props.h" 39 40#include "svn_private_config.h" 41#include "private/svn_wc_private.h" 42 43 44/*** Code. ***/ 45 46/* callback baton for fetch_repos_info */ 47struct repos_info_baton 48{ 49 apr_pool_t *state_pool; 50 svn_client_ctx_t *ctx; 51 const char *last_repos; 52 const char *last_uuid; 53}; 54 55/* svn_wc_upgrade_get_repos_info_t implementation for calling 56 svn_wc_upgrade() from svn_client_upgrade() */ 57static svn_error_t * 58fetch_repos_info(const char **repos_root, 59 const char **repos_uuid, 60 void *baton, 61 const char *url, 62 apr_pool_t *result_pool, 63 apr_pool_t *scratch_pool) 64{ 65 struct repos_info_baton *ri = baton; 66 67 /* The same info is likely to retrieved multiple times (e.g. externals) */ 68 if (ri->last_repos && svn_uri__is_ancestor(ri->last_repos, url)) 69 { 70 *repos_root = apr_pstrdup(result_pool, ri->last_repos); 71 *repos_uuid = apr_pstrdup(result_pool, ri->last_uuid); 72 return SVN_NO_ERROR; 73 } 74 75 SVN_ERR(svn_client_get_repos_root(repos_root, repos_uuid, url, ri->ctx, 76 result_pool, scratch_pool)); 77 78 /* Store data for further calls */ 79 ri->last_repos = apr_pstrdup(ri->state_pool, *repos_root); 80 ri->last_uuid = apr_pstrdup(ri->state_pool, *repos_uuid); 81 82 return SVN_NO_ERROR; 83} 84 85/* Forward definition. Upgrades svn:externals properties in the working copy 86 LOCAL_ABSPATH to the WC-NG storage. INFO_BATON will be used to fetch 87 repository info using fetch_repos_info() function if needed. 88 */ 89static svn_error_t * 90upgrade_externals_from_properties(svn_client_ctx_t *ctx, 91 const char *local_abspath, 92 struct repos_info_baton *info_baton, 93 apr_pool_t *scratch_pool); 94 95svn_error_t * 96svn_client_upgrade(const char *path, 97 svn_client_ctx_t *ctx, 98 apr_pool_t *scratch_pool) 99{ 100 const char *local_abspath; 101 apr_hash_t *externals; 102 struct repos_info_baton info_baton; 103 104 info_baton.state_pool = scratch_pool; 105 info_baton.ctx = ctx; 106 info_baton.last_repos = NULL; 107 info_baton.last_uuid = NULL; 108 109 if (svn_path_is_url(path)) 110 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 111 _("'%s' is not a local path"), path); 112 113 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); 114 SVN_ERR(svn_wc_upgrade(ctx->wc_ctx, local_abspath, 115 fetch_repos_info, &info_baton, 116 ctx->cancel_func, ctx->cancel_baton, 117 ctx->notify_func2, ctx->notify_baton2, 118 scratch_pool)); 119 120 SVN_ERR(svn_wc__externals_defined_below(&externals, 121 ctx->wc_ctx, local_abspath, 122 scratch_pool, scratch_pool)); 123 124 if (apr_hash_count(externals) > 0) 125 { 126 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 127 apr_hash_index_t *hi; 128 129 /* We are upgrading from >= 1.7. No need to upgrade from 130 svn:externals properties. And by that avoiding the removal 131 of recorded externals information (issue #4519) 132 133 Only directory externals need an explicit upgrade */ 134 for (hi = apr_hash_first(scratch_pool, externals); 135 hi; 136 hi = apr_hash_next(hi)) 137 { 138 const char *ext_abspath; 139 svn_node_kind_t kind; 140 141 svn_pool_clear(iterpool); 142 143 ext_abspath = apr_hash_this_key(hi); 144 145 SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL, 146 ctx->wc_ctx, local_abspath, 147 ext_abspath, FALSE, 148 iterpool, iterpool)); 149 150 if (kind == svn_node_dir) 151 { 152 svn_error_t *err = svn_client_upgrade(ext_abspath, ctx, iterpool); 153 154 if (err) 155 { 156 svn_wc_notify_t *notify = 157 svn_wc_create_notify(ext_abspath, 158 svn_wc_notify_failed_external, 159 iterpool); 160 notify->err = err; 161 ctx->notify_func2(ctx->notify_baton2, 162 notify, iterpool); 163 svn_error_clear(err); 164 /* Next external node, please... */ 165 } 166 } 167 } 168 169 svn_pool_destroy(iterpool); 170 } 171 else 172 { 173 /* Upgrading from <= 1.6, or no svn:properties defined. 174 (There is no way to detect the difference from libsvn_client :( ) */ 175 176 SVN_ERR(upgrade_externals_from_properties(ctx, local_abspath, 177 &info_baton, scratch_pool)); 178 } 179 return SVN_NO_ERROR; 180} 181 182static svn_error_t * 183upgrade_externals_from_properties(svn_client_ctx_t *ctx, 184 const char *local_abspath, 185 struct repos_info_baton *info_baton, 186 apr_pool_t *scratch_pool) 187{ 188 apr_hash_index_t *hi; 189 apr_pool_t *iterpool; 190 apr_pool_t *iterpool2; 191 apr_hash_t *externals; 192 svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}}; 193 194 /* Now it's time to upgrade the externals too. We do it after the wc 195 upgrade to avoid that errors in the externals causes the wc upgrade to 196 fail. Thanks to caching the performance penalty of walking the wc a 197 second time shouldn't be too severe */ 198 SVN_ERR(svn_client_propget5(&externals, NULL, SVN_PROP_EXTERNALS, 199 local_abspath, &rev, &rev, NULL, 200 svn_depth_infinity, NULL, ctx, 201 scratch_pool, scratch_pool)); 202 203 iterpool = svn_pool_create(scratch_pool); 204 iterpool2 = svn_pool_create(scratch_pool); 205 206 for (hi = apr_hash_first(scratch_pool, externals); hi; 207 hi = apr_hash_next(hi)) 208 { 209 int i; 210 const char *externals_parent_abspath; 211 const char *externals_parent_url; 212 const char *externals_parent_repos_root_url; 213 const char *externals_parent_repos_relpath; 214 const char *externals_parent = apr_hash_this_key(hi); 215 svn_string_t *external_desc = apr_hash_this_val(hi); 216 apr_array_header_t *externals_p; 217 svn_error_t *err; 218 219 svn_pool_clear(iterpool); 220 externals_p = apr_array_make(iterpool, 1, 221 sizeof(svn_wc_external_item2_t*)); 222 223 /* In this loop, an error causes the respective externals definition, or 224 * the external (inner loop), to be skipped, so that upgrade carries on 225 * with the other externals. */ 226 227 err = svn_dirent_get_absolute(&externals_parent_abspath, 228 externals_parent, iterpool); 229 230 if (!err) 231 err = svn_wc__node_get_repos_info(NULL, 232 &externals_parent_repos_relpath, 233 &externals_parent_repos_root_url, 234 NULL, 235 ctx->wc_ctx, 236 externals_parent_abspath, 237 iterpool, iterpool); 238 239 if (!err) 240 externals_parent_url = svn_path_url_add_component2( 241 externals_parent_repos_root_url, 242 externals_parent_repos_relpath, 243 iterpool); 244 if (!err) 245 err = svn_wc_parse_externals_description3( 246 &externals_p, svn_dirent_dirname(local_abspath, iterpool), 247 external_desc->data, FALSE, iterpool); 248 if (err) 249 { 250 svn_wc_notify_t *notify = 251 svn_wc_create_notify(externals_parent, 252 svn_wc_notify_failed_external, 253 scratch_pool); 254 notify->err = err; 255 256 ctx->notify_func2(ctx->notify_baton2, 257 notify, scratch_pool); 258 259 svn_error_clear(err); 260 261 /* Next externals definition, please... */ 262 continue; 263 } 264 265 for (i = 0; i < externals_p->nelts; i++) 266 { 267 svn_wc_external_item2_t *item; 268 const char *resolved_url; 269 const char *external_abspath; 270 const char *repos_relpath; 271 const char *repos_root_url; 272 const char *repos_uuid; 273 svn_node_kind_t external_kind; 274 svn_revnum_t peg_revision; 275 svn_revnum_t revision; 276 277 item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*); 278 279 svn_pool_clear(iterpool2); 280 external_abspath = svn_dirent_join(externals_parent_abspath, 281 item->target_dir, 282 iterpool2); 283 284 err = svn_wc__resolve_relative_external_url( 285 &resolved_url, 286 item, 287 externals_parent_repos_root_url, 288 externals_parent_url, 289 scratch_pool, scratch_pool); 290 if (err) 291 goto handle_error; 292 293 /* This is a hack. We only need to call svn_wc_upgrade() on external 294 * dirs, as file externals are upgraded along with their defining 295 * WC. Reading the kind will throw an exception on an external dir, 296 * saying that the wc must be upgraded. If it's a file, the lookup 297 * is done in an adm_dir belonging to the defining wc (which has 298 * already been upgraded) and no error is returned. If it doesn't 299 * exist (external that isn't checked out yet), we'll just get 300 * svn_node_none. */ 301 err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx, 302 external_abspath, TRUE, FALSE, iterpool2); 303 if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) 304 { 305 svn_error_clear(err); 306 307 err = svn_client_upgrade(external_abspath, ctx, iterpool2); 308 if (err) 309 goto handle_error; 310 } 311 else if (err) 312 goto handle_error; 313 314 /* The upgrade of any dir should be done now, get the now reliable 315 * kind. */ 316 err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx, external_abspath, 317 TRUE, FALSE, iterpool2); 318 if (err) 319 goto handle_error; 320 321 /* Update the EXTERNALS table according to the root URL, 322 * relpath and uuid known in the upgraded external WC. */ 323 324 /* We should probably have a function that provides all three 325 * of root URL, repos relpath and uuid at once, but here goes... */ 326 327 /* First get the relpath, as that returns SVN_ERR_WC_PATH_NOT_FOUND 328 * when the node is not present in the file system. 329 * svn_wc__node_get_repos_info() would try to derive the URL. */ 330 err = svn_wc__node_get_repos_info(NULL, 331 &repos_relpath, 332 &repos_root_url, 333 &repos_uuid, 334 ctx->wc_ctx, 335 external_abspath, 336 iterpool2, iterpool2); 337 if (err) 338 goto handle_error; 339 340 /* If we haven't got any information from the checked out external, 341 * or if the URL information mismatches the external's definition, 342 * ask fetch_repos_info() to find out the repos root. */ 343 if (0 != strcmp(resolved_url, 344 svn_path_url_add_component2(repos_root_url, 345 repos_relpath, 346 scratch_pool))) 347 { 348 err = fetch_repos_info(&repos_root_url, 349 &repos_uuid, 350 info_baton, 351 resolved_url, 352 scratch_pool, scratch_pool); 353 if (err) 354 goto handle_error; 355 356 repos_relpath = svn_uri_skip_ancestor(repos_root_url, 357 resolved_url, 358 iterpool2); 359 360 /* There's just the URL, no idea what kind the external is. 361 * That's fine, as the external isn't even checked out yet. 362 * The kind will be set during the next 'update'. */ 363 external_kind = svn_node_unknown; 364 } 365 366 if (err) 367 goto handle_error; 368 369 peg_revision = (item->peg_revision.kind == svn_opt_revision_number 370 ? item->peg_revision.value.number 371 : SVN_INVALID_REVNUM); 372 373 revision = (item->revision.kind == svn_opt_revision_number 374 ? item->revision.value.number 375 : SVN_INVALID_REVNUM); 376 377 err = svn_wc__upgrade_add_external_info(ctx->wc_ctx, 378 external_abspath, 379 external_kind, 380 externals_parent, 381 repos_relpath, 382 repos_root_url, 383 repos_uuid, 384 peg_revision, 385 revision, 386 iterpool2); 387handle_error: 388 if (err) 389 { 390 svn_wc_notify_t *notify = 391 svn_wc_create_notify(external_abspath, 392 svn_wc_notify_failed_external, 393 scratch_pool); 394 notify->err = err; 395 ctx->notify_func2(ctx->notify_baton2, 396 notify, scratch_pool); 397 svn_error_clear(err); 398 /* Next external node, please... */ 399 } 400 } 401 } 402 403 svn_pool_destroy(iterpool); 404 svn_pool_destroy(iterpool2); 405 406 return SVN_NO_ERROR; 407} 408