1251881Speter/* 2251881Speter * upgrade.c: wrapper around wc upgrade functionality. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter/* ==================================================================== */ 25251881Speter 26251881Speter 27251881Speter 28251881Speter/*** Includes. ***/ 29251881Speter 30251881Speter#include "svn_time.h" 31251881Speter#include "svn_wc.h" 32251881Speter#include "svn_client.h" 33251881Speter#include "svn_config.h" 34251881Speter#include "svn_dirent_uri.h" 35251881Speter#include "svn_path.h" 36251881Speter#include "svn_pools.h" 37251881Speter#include "client.h" 38251881Speter#include "svn_props.h" 39251881Speter 40251881Speter#include "svn_private_config.h" 41251881Speter#include "private/svn_wc_private.h" 42251881Speter 43251881Speter 44251881Speter/*** Code. ***/ 45251881Speter 46251881Speter/* callback baton for fetch_repos_info */ 47251881Speterstruct repos_info_baton 48251881Speter{ 49251881Speter apr_pool_t *state_pool; 50251881Speter svn_client_ctx_t *ctx; 51251881Speter const char *last_repos; 52251881Speter const char *last_uuid; 53251881Speter}; 54251881Speter 55251881Speter/* svn_wc_upgrade_get_repos_info_t implementation for calling 56251881Speter svn_wc_upgrade() from svn_client_upgrade() */ 57251881Speterstatic svn_error_t * 58251881Speterfetch_repos_info(const char **repos_root, 59251881Speter const char **repos_uuid, 60251881Speter void *baton, 61251881Speter const char *url, 62251881Speter apr_pool_t *result_pool, 63251881Speter apr_pool_t *scratch_pool) 64251881Speter{ 65251881Speter struct repos_info_baton *ri = baton; 66251881Speter 67251881Speter /* The same info is likely to retrieved multiple times (e.g. externals) */ 68251881Speter if (ri->last_repos && svn_uri__is_ancestor(ri->last_repos, url)) 69251881Speter { 70251881Speter *repos_root = apr_pstrdup(result_pool, ri->last_repos); 71251881Speter *repos_uuid = apr_pstrdup(result_pool, ri->last_uuid); 72251881Speter return SVN_NO_ERROR; 73251881Speter } 74251881Speter 75251881Speter SVN_ERR(svn_client_get_repos_root(repos_root, repos_uuid, url, ri->ctx, 76251881Speter result_pool, scratch_pool)); 77251881Speter 78251881Speter /* Store data for further calls */ 79251881Speter ri->last_repos = apr_pstrdup(ri->state_pool, *repos_root); 80251881Speter ri->last_uuid = apr_pstrdup(ri->state_pool, *repos_uuid); 81251881Speter 82251881Speter return SVN_NO_ERROR; 83251881Speter} 84251881Speter 85286506Speter/* Forward definition. Upgrades svn:externals properties in the working copy 86289180Speter LOCAL_ABSPATH to the WC-NG storage. INFO_BATON will be used to fetch 87289180Speter repository info using fetch_repos_info() function if needed. 88286506Speter */ 89286506Speterstatic svn_error_t * 90286506Speterupgrade_externals_from_properties(svn_client_ctx_t *ctx, 91286506Speter const char *local_abspath, 92289180Speter struct repos_info_baton *info_baton, 93286506Speter apr_pool_t *scratch_pool); 94286506Speter 95251881Spetersvn_error_t * 96251881Spetersvn_client_upgrade(const char *path, 97251881Speter svn_client_ctx_t *ctx, 98251881Speter apr_pool_t *scratch_pool) 99251881Speter{ 100251881Speter const char *local_abspath; 101251881Speter apr_hash_t *externals; 102251881Speter struct repos_info_baton info_baton; 103251881Speter 104251881Speter info_baton.state_pool = scratch_pool; 105251881Speter info_baton.ctx = ctx; 106251881Speter info_baton.last_repos = NULL; 107251881Speter info_baton.last_uuid = NULL; 108251881Speter 109251881Speter if (svn_path_is_url(path)) 110251881Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 111251881Speter _("'%s' is not a local path"), path); 112251881Speter 113251881Speter SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); 114251881Speter SVN_ERR(svn_wc_upgrade(ctx->wc_ctx, local_abspath, 115251881Speter fetch_repos_info, &info_baton, 116251881Speter ctx->cancel_func, ctx->cancel_baton, 117251881Speter ctx->notify_func2, ctx->notify_baton2, 118251881Speter scratch_pool)); 119251881Speter 120286506Speter SVN_ERR(svn_wc__externals_defined_below(&externals, 121286506Speter ctx->wc_ctx, local_abspath, 122286506Speter scratch_pool, scratch_pool)); 123286506Speter 124286506Speter if (apr_hash_count(externals) > 0) 125286506Speter { 126286506Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 127286506Speter apr_hash_index_t *hi; 128286506Speter 129286506Speter /* We are upgrading from >= 1.7. No need to upgrade from 130286506Speter svn:externals properties. And by that avoiding the removal 131286506Speter of recorded externals information (issue #4519) 132286506Speter 133286506Speter Only directory externals need an explicit upgrade */ 134286506Speter for (hi = apr_hash_first(scratch_pool, externals); 135286506Speter hi; 136286506Speter hi = apr_hash_next(hi)) 137286506Speter { 138286506Speter const char *ext_abspath; 139286506Speter svn_node_kind_t kind; 140286506Speter 141286506Speter svn_pool_clear(iterpool); 142286506Speter 143289180Speter ext_abspath = apr_hash_this_key(hi); 144286506Speter 145286506Speter SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL, 146286506Speter ctx->wc_ctx, local_abspath, 147286506Speter ext_abspath, FALSE, 148286506Speter iterpool, iterpool)); 149286506Speter 150286506Speter if (kind == svn_node_dir) 151286506Speter { 152286506Speter svn_error_t *err = svn_client_upgrade(ext_abspath, ctx, iterpool); 153286506Speter 154286506Speter if (err) 155286506Speter { 156286506Speter svn_wc_notify_t *notify = 157286506Speter svn_wc_create_notify(ext_abspath, 158286506Speter svn_wc_notify_failed_external, 159286506Speter iterpool); 160286506Speter notify->err = err; 161286506Speter ctx->notify_func2(ctx->notify_baton2, 162286506Speter notify, iterpool); 163286506Speter svn_error_clear(err); 164286506Speter /* Next external node, please... */ 165286506Speter } 166286506Speter } 167286506Speter } 168286506Speter 169286506Speter svn_pool_destroy(iterpool); 170286506Speter } 171286506Speter else 172286506Speter { 173286506Speter /* Upgrading from <= 1.6, or no svn:properties defined. 174286506Speter (There is no way to detect the difference from libsvn_client :( ) */ 175286506Speter 176286506Speter SVN_ERR(upgrade_externals_from_properties(ctx, local_abspath, 177289180Speter &info_baton, scratch_pool)); 178286506Speter } 179286506Speter return SVN_NO_ERROR; 180286506Speter} 181286506Speter 182362181Sdim/* Helper for upgrade_externals_from_properties: upgrades one external ITEM 183362181Sdim in EXTERNALS_PARENT. Uses SCRATCH_POOL for temporary allocations. */ 184286506Speterstatic svn_error_t * 185362181Sdimupgrade_external_item(svn_client_ctx_t *ctx, 186362181Sdim const char *externals_parent_abspath, 187362181Sdim const char *externals_parent_url, 188362181Sdim const char *externals_parent_repos_root_url, 189362181Sdim svn_wc_external_item2_t *item, 190362181Sdim struct repos_info_baton *info_baton, 191362181Sdim apr_pool_t *scratch_pool) 192362181Sdim{ 193362181Sdim const char *resolved_url; 194362181Sdim const char *external_abspath; 195362181Sdim const char *repos_relpath; 196362181Sdim const char *repos_root_url; 197362181Sdim const char *repos_uuid; 198362181Sdim svn_node_kind_t external_kind; 199362181Sdim svn_revnum_t peg_revision; 200362181Sdim svn_revnum_t revision; 201362181Sdim svn_error_t *err; 202362181Sdim 203362181Sdim external_abspath = svn_dirent_join(externals_parent_abspath, 204362181Sdim item->target_dir, 205362181Sdim scratch_pool); 206362181Sdim 207362181Sdim SVN_ERR(svn_wc__resolve_relative_external_url( 208362181Sdim &resolved_url, 209362181Sdim item, 210362181Sdim externals_parent_repos_root_url, 211362181Sdim externals_parent_url, 212362181Sdim scratch_pool, scratch_pool)); 213362181Sdim 214362181Sdim /* This is a hack. We only need to call svn_wc_upgrade() on external 215362181Sdim * dirs, as file externals are upgraded along with their defining 216362181Sdim * WC. Reading the kind will throw an exception on an external dir, 217362181Sdim * saying that the wc must be upgraded. If it's a file, the lookup 218362181Sdim * is done in an adm_dir belonging to the defining wc (which has 219362181Sdim * already been upgraded) and no error is returned. If it doesn't 220362181Sdim * exist (external that isn't checked out yet), we'll just get 221362181Sdim * svn_node_none. */ 222362181Sdim err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx, 223362181Sdim external_abspath, TRUE, FALSE, scratch_pool); 224362181Sdim if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) 225362181Sdim { 226362181Sdim svn_error_clear(err); 227362181Sdim 228362181Sdim SVN_ERR(svn_client_upgrade(external_abspath, ctx, scratch_pool)); 229362181Sdim } 230362181Sdim else if (err) 231362181Sdim return svn_error_trace(err); 232362181Sdim 233362181Sdim /* The upgrade of any dir should be done now, get the now reliable 234362181Sdim * kind. */ 235362181Sdim SVN_ERR(svn_wc_read_kind2(&external_kind, ctx->wc_ctx, external_abspath, 236362181Sdim TRUE, FALSE, scratch_pool)); 237362181Sdim 238362181Sdim /* Update the EXTERNALS table according to the root URL, 239362181Sdim * relpath and uuid known in the upgraded external WC. */ 240362181Sdim 241362181Sdim /* We should probably have a function that provides all three 242362181Sdim * of root URL, repos relpath and uuid at once, but here goes... */ 243362181Sdim 244362181Sdim /* First get the relpath, as that returns SVN_ERR_WC_PATH_NOT_FOUND 245362181Sdim * when the node is not present in the file system. 246362181Sdim * svn_wc__node_get_repos_info() would try to derive the URL. */ 247362181Sdim SVN_ERR(svn_wc__node_get_repos_info(NULL, 248362181Sdim &repos_relpath, 249362181Sdim &repos_root_url, 250362181Sdim &repos_uuid, 251362181Sdim ctx->wc_ctx, 252362181Sdim external_abspath, 253362181Sdim scratch_pool, scratch_pool)); 254362181Sdim 255362181Sdim /* If we haven't got any information from the checked out external, 256362181Sdim * or if the URL information mismatches the external's definition, 257362181Sdim * ask fetch_repos_info() to find out the repos root. */ 258362181Sdim if (0 != strcmp(resolved_url, 259362181Sdim svn_path_url_add_component2(repos_root_url, 260362181Sdim repos_relpath, 261362181Sdim scratch_pool))) 262362181Sdim { 263362181Sdim SVN_ERR(fetch_repos_info(&repos_root_url, &repos_uuid, info_baton, 264362181Sdim resolved_url, scratch_pool, scratch_pool)); 265362181Sdim 266362181Sdim repos_relpath = svn_uri_skip_ancestor(repos_root_url, 267362181Sdim resolved_url, 268362181Sdim scratch_pool); 269362181Sdim 270362181Sdim /* There's just the URL, no idea what kind the external is. 271362181Sdim * That's fine, as the external isn't even checked out yet. 272362181Sdim * The kind will be set during the next 'update'. */ 273362181Sdim external_kind = svn_node_unknown; 274362181Sdim } 275362181Sdim 276362181Sdim peg_revision = (item->peg_revision.kind == svn_opt_revision_number 277362181Sdim ? item->peg_revision.value.number 278362181Sdim : SVN_INVALID_REVNUM); 279362181Sdim 280362181Sdim revision = (item->revision.kind == svn_opt_revision_number 281362181Sdim ? item->revision.value.number 282362181Sdim : SVN_INVALID_REVNUM); 283362181Sdim 284362181Sdim SVN_ERR(svn_wc__upgrade_add_external_info(ctx->wc_ctx, 285362181Sdim external_abspath, 286362181Sdim external_kind, 287362181Sdim externals_parent_abspath, 288362181Sdim repos_relpath, 289362181Sdim repos_root_url, 290362181Sdim repos_uuid, 291362181Sdim peg_revision, 292362181Sdim revision, 293362181Sdim scratch_pool)); 294362181Sdim 295362181Sdim return SVN_NO_ERROR; 296362181Sdim} 297362181Sdim 298362181Sdimstatic svn_error_t * 299286506Speterupgrade_externals_from_properties(svn_client_ctx_t *ctx, 300286506Speter const char *local_abspath, 301289180Speter struct repos_info_baton *info_baton, 302286506Speter apr_pool_t *scratch_pool) 303286506Speter{ 304286506Speter apr_hash_index_t *hi; 305286506Speter apr_pool_t *iterpool; 306362181Sdim apr_pool_t *inner_iterpool; 307286506Speter apr_hash_t *externals; 308286506Speter svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}}; 309286506Speter 310251881Speter /* Now it's time to upgrade the externals too. We do it after the wc 311251881Speter upgrade to avoid that errors in the externals causes the wc upgrade to 312251881Speter fail. Thanks to caching the performance penalty of walking the wc a 313251881Speter second time shouldn't be too severe */ 314251881Speter SVN_ERR(svn_client_propget5(&externals, NULL, SVN_PROP_EXTERNALS, 315251881Speter local_abspath, &rev, &rev, NULL, 316251881Speter svn_depth_infinity, NULL, ctx, 317251881Speter scratch_pool, scratch_pool)); 318251881Speter 319251881Speter iterpool = svn_pool_create(scratch_pool); 320362181Sdim inner_iterpool = svn_pool_create(scratch_pool); 321251881Speter 322251881Speter for (hi = apr_hash_first(scratch_pool, externals); hi; 323251881Speter hi = apr_hash_next(hi)) 324251881Speter { 325251881Speter int i; 326251881Speter const char *externals_parent_url; 327251881Speter const char *externals_parent_repos_root_url; 328251881Speter const char *externals_parent_repos_relpath; 329362181Sdim const char *externals_parent_abspath = apr_hash_this_key(hi); 330289180Speter svn_string_t *external_desc = apr_hash_this_val(hi); 331251881Speter apr_array_header_t *externals_p; 332251881Speter svn_error_t *err; 333251881Speter 334251881Speter svn_pool_clear(iterpool); 335362181Sdim 336362181Sdim /* svn_client_propget5() has API promise to return absolute paths. */ 337362181Sdim SVN_ERR_ASSERT(svn_dirent_is_absolute(externals_parent_abspath)); 338362181Sdim 339251881Speter externals_p = apr_array_make(iterpool, 1, 340251881Speter sizeof(svn_wc_external_item2_t*)); 341251881Speter 342251881Speter /* In this loop, an error causes the respective externals definition, or 343251881Speter * the external (inner loop), to be skipped, so that upgrade carries on 344251881Speter * with the other externals. */ 345362181Sdim err = svn_wc__node_get_repos_info(NULL, 346362181Sdim &externals_parent_repos_relpath, 347362181Sdim &externals_parent_repos_root_url, 348362181Sdim NULL, 349362181Sdim ctx->wc_ctx, 350362181Sdim externals_parent_abspath, 351362181Sdim iterpool, iterpool); 352251881Speter 353251881Speter if (!err) 354362181Sdim { 355362181Sdim err = svn_wc_parse_externals_description3( 356362181Sdim &externals_p, svn_dirent_dirname(local_abspath, iterpool), 357362181Sdim external_desc->data, FALSE, iterpool); 358362181Sdim } 359251881Speter 360251881Speter if (err) 361251881Speter { 362251881Speter svn_wc_notify_t *notify = 363362181Sdim svn_wc_create_notify(externals_parent_abspath, 364251881Speter svn_wc_notify_failed_external, 365251881Speter scratch_pool); 366251881Speter notify->err = err; 367251881Speter 368251881Speter ctx->notify_func2(ctx->notify_baton2, 369251881Speter notify, scratch_pool); 370251881Speter 371251881Speter svn_error_clear(err); 372251881Speter 373251881Speter /* Next externals definition, please... */ 374251881Speter continue; 375251881Speter } 376251881Speter 377362181Sdim externals_parent_url = svn_path_url_add_component2( 378362181Sdim externals_parent_repos_root_url, 379362181Sdim externals_parent_repos_relpath, 380362181Sdim iterpool); 381362181Sdim 382251881Speter for (i = 0; i < externals_p->nelts; i++) 383251881Speter { 384251881Speter svn_wc_external_item2_t *item; 385251881Speter 386251881Speter item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*); 387251881Speter 388362181Sdim svn_pool_clear(inner_iterpool); 389362181Sdim err = upgrade_external_item(ctx, externals_parent_abspath, 390362181Sdim externals_parent_url, 391362181Sdim externals_parent_repos_root_url, 392362181Sdim item, info_baton, inner_iterpool); 393251881Speter 394251881Speter if (err) 395251881Speter { 396251881Speter svn_wc_notify_t *notify = 397362181Sdim svn_wc_create_notify(svn_dirent_join(externals_parent_abspath, 398362181Sdim item->target_dir, 399362181Sdim inner_iterpool), 400251881Speter svn_wc_notify_failed_external, 401251881Speter scratch_pool); 402251881Speter notify->err = err; 403251881Speter ctx->notify_func2(ctx->notify_baton2, 404251881Speter notify, scratch_pool); 405251881Speter svn_error_clear(err); 406251881Speter /* Next external node, please... */ 407251881Speter } 408251881Speter } 409251881Speter } 410251881Speter 411362181Sdim svn_pool_destroy(inner_iterpool); 412251881Speter svn_pool_destroy(iterpool); 413251881Speter 414251881Speter return SVN_NO_ERROR; 415251881Speter} 416