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 85svn_error_t * 86svn_client_upgrade(const char *path, 87 svn_client_ctx_t *ctx, 88 apr_pool_t *scratch_pool) 89{ 90 const char *local_abspath; 91 apr_hash_t *externals; 92 apr_hash_index_t *hi; 93 apr_pool_t *iterpool; 94 apr_pool_t *iterpool2; 95 svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}}; 96 struct repos_info_baton info_baton; 97 98 info_baton.state_pool = scratch_pool; 99 info_baton.ctx = ctx; 100 info_baton.last_repos = NULL; 101 info_baton.last_uuid = NULL; 102 103 if (svn_path_is_url(path)) 104 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 105 _("'%s' is not a local path"), path); 106 107 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); 108 SVN_ERR(svn_wc_upgrade(ctx->wc_ctx, local_abspath, 109 fetch_repos_info, &info_baton, 110 ctx->cancel_func, ctx->cancel_baton, 111 ctx->notify_func2, ctx->notify_baton2, 112 scratch_pool)); 113 114 /* Now it's time to upgrade the externals too. We do it after the wc 115 upgrade to avoid that errors in the externals causes the wc upgrade to 116 fail. Thanks to caching the performance penalty of walking the wc a 117 second time shouldn't be too severe */ 118 SVN_ERR(svn_client_propget5(&externals, NULL, SVN_PROP_EXTERNALS, 119 local_abspath, &rev, &rev, NULL, 120 svn_depth_infinity, NULL, ctx, 121 scratch_pool, scratch_pool)); 122 123 iterpool = svn_pool_create(scratch_pool); 124 iterpool2 = svn_pool_create(scratch_pool); 125 126 for (hi = apr_hash_first(scratch_pool, externals); hi; 127 hi = apr_hash_next(hi)) 128 { 129 int i; 130 const char *externals_parent_abspath; 131 const char *externals_parent_url; 132 const char *externals_parent_repos_root_url; 133 const char *externals_parent_repos_relpath; 134 const char *externals_parent = svn__apr_hash_index_key(hi); 135 svn_string_t *external_desc = svn__apr_hash_index_val(hi); 136 apr_array_header_t *externals_p; 137 svn_error_t *err; 138 139 svn_pool_clear(iterpool); 140 externals_p = apr_array_make(iterpool, 1, 141 sizeof(svn_wc_external_item2_t*)); 142 143 /* In this loop, an error causes the respective externals definition, or 144 * the external (inner loop), to be skipped, so that upgrade carries on 145 * with the other externals. */ 146 147 err = svn_dirent_get_absolute(&externals_parent_abspath, 148 externals_parent, iterpool); 149 150 if (!err) 151 err = svn_wc__node_get_repos_info(NULL, 152 &externals_parent_repos_relpath, 153 &externals_parent_repos_root_url, 154 NULL, 155 ctx->wc_ctx, 156 externals_parent_abspath, 157 iterpool, iterpool); 158 159 if (!err) 160 externals_parent_url = svn_path_url_add_component2( 161 externals_parent_repos_root_url, 162 externals_parent_repos_relpath, 163 iterpool); 164 if (!err) 165 err = svn_wc_parse_externals_description3( 166 &externals_p, svn_dirent_dirname(path, iterpool), 167 external_desc->data, FALSE, iterpool); 168 if (err) 169 { 170 svn_wc_notify_t *notify = 171 svn_wc_create_notify(externals_parent, 172 svn_wc_notify_failed_external, 173 scratch_pool); 174 notify->err = err; 175 176 ctx->notify_func2(ctx->notify_baton2, 177 notify, scratch_pool); 178 179 svn_error_clear(err); 180 181 /* Next externals definition, please... */ 182 continue; 183 } 184 185 for (i = 0; i < externals_p->nelts; i++) 186 { 187 svn_wc_external_item2_t *item; 188 const char *resolved_url; 189 const char *external_abspath; 190 const char *repos_relpath; 191 const char *repos_root_url; 192 const char *repos_uuid; 193 svn_node_kind_t external_kind; 194 svn_revnum_t peg_revision; 195 svn_revnum_t revision; 196 197 item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*); 198 199 svn_pool_clear(iterpool2); 200 external_abspath = svn_dirent_join(externals_parent_abspath, 201 item->target_dir, 202 iterpool2); 203 204 err = svn_wc__resolve_relative_external_url( 205 &resolved_url, 206 item, 207 externals_parent_repos_root_url, 208 externals_parent_url, 209 scratch_pool, scratch_pool); 210 if (err) 211 goto handle_error; 212 213 /* This is a hack. We only need to call svn_wc_upgrade() on external 214 * dirs, as file externals are upgraded along with their defining 215 * WC. Reading the kind will throw an exception on an external dir, 216 * saying that the wc must be upgraded. If it's a file, the lookup 217 * is done in an adm_dir belonging to the defining wc (which has 218 * already been upgraded) and no error is returned. If it doesn't 219 * exist (external that isn't checked out yet), we'll just get 220 * svn_node_none. */ 221 err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx, 222 external_abspath, TRUE, FALSE, iterpool2); 223 if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) 224 { 225 svn_error_clear(err); 226 227 err = svn_client_upgrade(external_abspath, ctx, iterpool2); 228 if (err) 229 goto handle_error; 230 } 231 else if (err) 232 goto handle_error; 233 234 /* The upgrade of any dir should be done now, get the now reliable 235 * kind. */ 236 err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx, external_abspath, 237 TRUE, FALSE, iterpool2); 238 if (err) 239 goto handle_error; 240 241 /* Update the EXTERNALS table according to the root URL, 242 * relpath and uuid known in the upgraded external WC. */ 243 244 /* We should probably have a function that provides all three 245 * of root URL, repos relpath and uuid at once, but here goes... */ 246 247 /* First get the relpath, as that returns SVN_ERR_WC_PATH_NOT_FOUND 248 * when the node is not present in the file system. 249 * svn_wc__node_get_repos_info() would try to derive the URL. */ 250 err = svn_wc__node_get_repos_info(NULL, 251 &repos_relpath, 252 &repos_root_url, 253 &repos_uuid, 254 ctx->wc_ctx, 255 external_abspath, 256 iterpool2, iterpool2); 257 if (err) 258 goto handle_error; 259 260 /* If we haven't got any information from the checked out external, 261 * or if the URL information mismatches the external's definition, 262 * ask fetch_repos_info() to find out the repos root. */ 263 if (0 != strcmp(resolved_url, 264 svn_path_url_add_component2(repos_root_url, 265 repos_relpath, 266 scratch_pool))) 267 { 268 err = fetch_repos_info(&repos_root_url, 269 &repos_uuid, 270 &info_baton, 271 resolved_url, 272 scratch_pool, scratch_pool); 273 if (err) 274 goto handle_error; 275 276 repos_relpath = svn_uri_skip_ancestor(repos_root_url, 277 resolved_url, 278 iterpool2); 279 280 /* There's just the URL, no idea what kind the external is. 281 * That's fine, as the external isn't even checked out yet. 282 * The kind will be set during the next 'update'. */ 283 external_kind = svn_node_unknown; 284 } 285 286 if (err) 287 goto handle_error; 288 289 peg_revision = (item->peg_revision.kind == svn_opt_revision_number 290 ? item->peg_revision.value.number 291 : SVN_INVALID_REVNUM); 292 293 revision = (item->revision.kind == svn_opt_revision_number 294 ? item->revision.value.number 295 : SVN_INVALID_REVNUM); 296 297 err = svn_wc__upgrade_add_external_info(ctx->wc_ctx, 298 external_abspath, 299 external_kind, 300 externals_parent, 301 repos_relpath, 302 repos_root_url, 303 repos_uuid, 304 peg_revision, 305 revision, 306 iterpool2); 307handle_error: 308 if (err) 309 { 310 svn_wc_notify_t *notify = 311 svn_wc_create_notify(external_abspath, 312 svn_wc_notify_failed_external, 313 scratch_pool); 314 notify->err = err; 315 ctx->notify_func2(ctx->notify_baton2, 316 notify, scratch_pool); 317 svn_error_clear(err); 318 /* Next external node, please... */ 319 } 320 } 321 } 322 323 svn_pool_destroy(iterpool); 324 svn_pool_destroy(iterpool2); 325 326 return SVN_NO_ERROR; 327} 328