upgrade.c revision 299742
1/* 2 * upgrade.c: routines for upgrading a working copy 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#include <apr_pools.h> 25 26#include "svn_types.h" 27#include "svn_pools.h" 28#include "svn_dirent_uri.h" 29#include "svn_path.h" 30#include "svn_hash.h" 31 32#include "wc.h" 33#include "adm_files.h" 34#include "conflicts.h" 35#include "entries.h" 36#include "wc_db.h" 37#include "tree_conflicts.h" 38#include "wc-queries.h" /* for STMT_* */ 39#include "workqueue.h" 40#include "token-map.h" 41 42#include "svn_private_config.h" 43#include "private/svn_wc_private.h" 44#include "private/svn_sqlite.h" 45#include "private/svn_token.h" 46 47/* WC-1.0 administrative area extensions */ 48#define SVN_WC__BASE_EXT ".svn-base" /* for text and prop bases */ 49#define SVN_WC__WORK_EXT ".svn-work" /* for working propfiles */ 50#define SVN_WC__REVERT_EXT ".svn-revert" /* for reverting a replaced 51 file */ 52 53/* Old locations for storing "wcprops" (aka "dav cache"). */ 54#define WCPROPS_SUBDIR_FOR_FILES "wcprops" 55#define WCPROPS_FNAME_FOR_DIR "dir-wcprops" 56#define WCPROPS_ALL_DATA "all-wcprops" 57 58/* Old property locations. */ 59#define PROPS_SUBDIR "props" 60#define PROP_BASE_SUBDIR "prop-base" 61#define PROP_BASE_FOR_DIR "dir-prop-base" 62#define PROP_REVERT_FOR_DIR "dir-prop-revert" 63#define PROP_WORKING_FOR_DIR "dir-props" 64 65/* Old textbase location. */ 66#define TEXT_BASE_SUBDIR "text-base" 67 68#define TEMP_DIR "tmp" 69 70/* Old data files that we no longer need/use. */ 71#define ADM_README "README.txt" 72#define ADM_EMPTY_FILE "empty-file" 73#define ADM_LOG "log" 74#define ADM_LOCK "lock" 75 76/* New pristine location */ 77#define PRISTINE_STORAGE_RELPATH "pristine" 78#define PRISTINE_STORAGE_EXT ".svn-base" 79/* Number of characters in a pristine file basename, in WC format <= 28. */ 80#define PRISTINE_BASENAME_OLD_LEN 40 81#define SDB_FILE "wc.db" 82 83 84/* Read the properties from the file at PROPFILE_ABSPATH, returning them 85 as a hash in *PROPS. If the propfile is NOT present, then NULL will 86 be returned in *PROPS. */ 87static svn_error_t * 88read_propfile(apr_hash_t **props, 89 const char *propfile_abspath, 90 apr_pool_t *result_pool, 91 apr_pool_t *scratch_pool) 92{ 93 svn_error_t *err; 94 svn_stream_t *stream; 95 apr_finfo_t finfo; 96 97 err = svn_io_stat(&finfo, propfile_abspath, APR_FINFO_SIZE, scratch_pool); 98 99 if (err 100 && (APR_STATUS_IS_ENOENT(err->apr_err) 101 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) 102 { 103 svn_error_clear(err); 104 105 /* The propfile was not there. Signal with a NULL. */ 106 *props = NULL; 107 return SVN_NO_ERROR; 108 } 109 else 110 SVN_ERR(err); 111 112 /* A 0-bytes file signals an empty property list. 113 (mostly used for revert-props) */ 114 if (finfo.size == 0) 115 { 116 *props = apr_hash_make(result_pool); 117 return SVN_NO_ERROR; 118 } 119 120 SVN_ERR(svn_stream_open_readonly(&stream, propfile_abspath, 121 scratch_pool, scratch_pool)); 122 123 /* ### does this function need to be smarter? will we see zero-length 124 ### files? see props.c::load_props(). there may be more work here. 125 ### need a historic analysis of 1.x property storage. what will we 126 ### actually run into? */ 127 128 /* ### loggy_write_properties() and immediate_install_props() write 129 ### zero-length files for "no props", so we should be a bit smarter 130 ### in here. */ 131 132 /* ### should we be forgiving in here? I say "no". if we can't be sure, 133 ### then we could effectively corrupt the local working copy. */ 134 135 *props = apr_hash_make(result_pool); 136 SVN_ERR(svn_hash_read2(*props, stream, SVN_HASH_TERMINATOR, result_pool)); 137 138 return svn_error_trace(svn_stream_close(stream)); 139} 140 141 142/* Read one proplist (allocated from RESULT_POOL) from STREAM, and place it 143 into ALL_WCPROPS at NAME. */ 144static svn_error_t * 145read_one_proplist(apr_hash_t *all_wcprops, 146 const char *name, 147 svn_stream_t *stream, 148 apr_pool_t *result_pool, 149 apr_pool_t *scratch_pool) 150{ 151 apr_hash_t *proplist; 152 153 proplist = apr_hash_make(result_pool); 154 SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, result_pool)); 155 svn_hash_sets(all_wcprops, name, proplist); 156 157 return SVN_NO_ERROR; 158} 159 160 161/* Read the wcprops from all the files in the admin area of DIR_ABSPATH, 162 returning them in *ALL_WCPROPS. Results are allocated in RESULT_POOL, 163 and temporary allocations are performed in SCRATCH_POOL. */ 164static svn_error_t * 165read_many_wcprops(apr_hash_t **all_wcprops, 166 const char *dir_abspath, 167 apr_pool_t *result_pool, 168 apr_pool_t *scratch_pool) 169{ 170 const char *propfile_abspath; 171 apr_hash_t *wcprops; 172 apr_hash_t *dirents; 173 const char *props_dir_abspath; 174 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 175 apr_hash_index_t *hi; 176 177 *all_wcprops = apr_hash_make(result_pool); 178 179 /* First, look at dir-wcprops. */ 180 propfile_abspath = svn_wc__adm_child(dir_abspath, WCPROPS_FNAME_FOR_DIR, 181 scratch_pool); 182 SVN_ERR(read_propfile(&wcprops, propfile_abspath, result_pool, iterpool)); 183 if (wcprops != NULL) 184 svn_hash_sets(*all_wcprops, SVN_WC_ENTRY_THIS_DIR, wcprops); 185 186 props_dir_abspath = svn_wc__adm_child(dir_abspath, WCPROPS_SUBDIR_FOR_FILES, 187 scratch_pool); 188 189 /* Now walk the wcprops directory. */ 190 SVN_ERR(svn_io_get_dirents3(&dirents, props_dir_abspath, TRUE, 191 scratch_pool, scratch_pool)); 192 193 for (hi = apr_hash_first(scratch_pool, dirents); 194 hi; 195 hi = apr_hash_next(hi)) 196 { 197 const char *name = apr_hash_this_key(hi); 198 199 svn_pool_clear(iterpool); 200 201 propfile_abspath = svn_dirent_join(props_dir_abspath, name, iterpool); 202 203 SVN_ERR(read_propfile(&wcprops, propfile_abspath, 204 result_pool, iterpool)); 205 SVN_ERR_ASSERT(wcprops != NULL); 206 svn_hash_sets(*all_wcprops, apr_pstrdup(result_pool, name), wcprops); 207 } 208 209 svn_pool_destroy(iterpool); 210 return SVN_NO_ERROR; 211} 212 213 214/* For wcprops stored in a single file in this working copy, read that 215 file and return it in *ALL_WCPROPS, allocated in RESULT_POOL. Use 216 SCRATCH_POOL for temporary allocations. */ 217static svn_error_t * 218read_wcprops(apr_hash_t **all_wcprops, 219 const char *dir_abspath, 220 apr_pool_t *result_pool, 221 apr_pool_t *scratch_pool) 222{ 223 svn_stream_t *stream; 224 svn_error_t *err; 225 226 *all_wcprops = apr_hash_make(result_pool); 227 228 err = svn_wc__open_adm_stream(&stream, dir_abspath, 229 WCPROPS_ALL_DATA, 230 scratch_pool, scratch_pool); 231 232 /* A non-existent file means there are no props. */ 233 if (err && APR_STATUS_IS_ENOENT(err->apr_err)) 234 { 235 svn_error_clear(err); 236 return SVN_NO_ERROR; 237 } 238 SVN_ERR(err); 239 240 /* Read the proplist for THIS_DIR. */ 241 SVN_ERR(read_one_proplist(*all_wcprops, SVN_WC_ENTRY_THIS_DIR, stream, 242 result_pool, scratch_pool)); 243 244 /* And now, the children. */ 245 while (1729) 246 { 247 svn_stringbuf_t *line; 248 svn_boolean_t eof; 249 250 SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, result_pool)); 251 if (eof) 252 { 253 if (line->len > 0) 254 return svn_error_createf 255 (SVN_ERR_WC_CORRUPT, NULL, 256 _("Missing end of line in wcprops file for '%s'"), 257 svn_dirent_local_style(dir_abspath, scratch_pool)); 258 break; 259 } 260 SVN_ERR(read_one_proplist(*all_wcprops, line->data, stream, 261 result_pool, scratch_pool)); 262 } 263 264 return svn_error_trace(svn_stream_close(stream)); 265} 266 267/* Return in CHILDREN, the list of all 1.6 versioned subdirectories 268 which also exist on disk as directories. 269 270 If DELETE_DIR is not NULL set *DELETE_DIR to TRUE if the directory 271 should be deleted after migrating to WC-NG, otherwise to FALSE. 272 273 If SKIP_MISSING is TRUE, don't add missing or obstructed subdirectories 274 to the list of children. 275 */ 276static svn_error_t * 277get_versioned_subdirs(apr_array_header_t **children, 278 svn_boolean_t *delete_dir, 279 const char *dir_abspath, 280 svn_boolean_t skip_missing, 281 apr_pool_t *result_pool, 282 apr_pool_t *scratch_pool) 283{ 284 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 285 apr_hash_t *entries; 286 apr_hash_index_t *hi; 287 svn_wc_entry_t *this_dir = NULL; 288 289 *children = apr_array_make(result_pool, 10, sizeof(const char *)); 290 291 SVN_ERR(svn_wc__read_entries_old(&entries, dir_abspath, 292 scratch_pool, iterpool)); 293 for (hi = apr_hash_first(scratch_pool, entries); 294 hi; 295 hi = apr_hash_next(hi)) 296 { 297 const char *name = apr_hash_this_key(hi); 298 const svn_wc_entry_t *entry = apr_hash_this_val(hi); 299 const char *child_abspath; 300 svn_boolean_t hidden; 301 302 /* skip "this dir" */ 303 if (*name == '\0') 304 { 305 this_dir = apr_hash_this_val(hi); 306 continue; 307 } 308 else if (entry->kind != svn_node_dir) 309 continue; 310 311 svn_pool_clear(iterpool); 312 313 /* If a directory is 'hidden' skip it as subdir */ 314 SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry)); 315 if (hidden) 316 continue; 317 318 child_abspath = svn_dirent_join(dir_abspath, name, scratch_pool); 319 320 if (skip_missing) 321 { 322 svn_node_kind_t kind; 323 SVN_ERR(svn_io_check_path(child_abspath, &kind, scratch_pool)); 324 325 if (kind != svn_node_dir) 326 continue; 327 } 328 329 APR_ARRAY_PUSH(*children, const char *) = apr_pstrdup(result_pool, 330 child_abspath); 331 } 332 333 svn_pool_destroy(iterpool); 334 335 if (delete_dir != NULL) 336 { 337 *delete_dir = (this_dir != NULL) 338 && (this_dir->schedule == svn_wc_schedule_delete) 339 && ! this_dir->keep_local; 340 } 341 342 return SVN_NO_ERROR; 343} 344 345 346/* Return in CHILDREN the names of all versioned *files* in SDB that 347 are children of PARENT_RELPATH. These files' existence on disk is 348 not tested. 349 350 This set of children is intended for property upgrades. 351 Subdirectory's properties exist in the subdirs. 352 353 Note that this uses just the SDB to locate children, which means 354 that the children must have been upgraded to wc-ng format. */ 355static svn_error_t * 356get_versioned_files(const apr_array_header_t **children, 357 const char *parent_relpath, 358 svn_sqlite__db_t *sdb, 359 apr_int64_t wc_id, 360 apr_pool_t *result_pool, 361 apr_pool_t *scratch_pool) 362{ 363 svn_sqlite__stmt_t *stmt; 364 apr_array_header_t *child_names; 365 svn_boolean_t have_row; 366 367 /* ### just select 'file' children. do we need 'symlink' in the future? */ 368 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_ALL_FILES)); 369 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, parent_relpath)); 370 371 /* ### 10 is based on Subversion's average of 8.5 files per versioned 372 ### directory in its repository. maybe use a different value? or 373 ### count rows first? */ 374 child_names = apr_array_make(result_pool, 10, sizeof(const char *)); 375 376 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 377 while (have_row) 378 { 379 const char *local_relpath = svn_sqlite__column_text(stmt, 0, 380 result_pool); 381 382 APR_ARRAY_PUSH(child_names, const char *) 383 = svn_relpath_basename(local_relpath, result_pool); 384 385 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 386 } 387 388 *children = child_names; 389 390 return svn_error_trace(svn_sqlite__reset(stmt)); 391} 392 393 394/* Return the path of the old-school administrative lock file 395 associated with LOCAL_DIR_ABSPATH, allocated from RESULT_POOL. */ 396static const char * 397build_lockfile_path(const char *local_dir_abspath, 398 apr_pool_t *result_pool) 399{ 400 return svn_dirent_join_many(result_pool, 401 local_dir_abspath, 402 svn_wc_get_adm_dir(result_pool), 403 ADM_LOCK, 404 SVN_VA_NULL); 405} 406 407 408/* Create a physical lock file in the admin directory for ABSPATH. */ 409static svn_error_t * 410create_physical_lock(const char *abspath, apr_pool_t *scratch_pool) 411{ 412 const char *lock_abspath = build_lockfile_path(abspath, scratch_pool); 413 svn_error_t *err; 414 apr_file_t *file; 415 416 err = svn_io_file_open(&file, lock_abspath, 417 APR_WRITE | APR_CREATE | APR_EXCL, 418 APR_OS_DEFAULT, 419 scratch_pool); 420 421 if (err && APR_STATUS_IS_EEXIST(err->apr_err)) 422 { 423 /* Congratulations, we just stole a physical lock from somebody */ 424 svn_error_clear(err); 425 return SVN_NO_ERROR; 426 } 427 428 return svn_error_trace(err); 429} 430 431 432/* Wipe out all the obsolete files/dirs from the administrative area. */ 433static void 434wipe_obsolete_files(const char *wcroot_abspath, apr_pool_t *scratch_pool) 435{ 436 /* Zap unused files. */ 437 svn_error_clear(svn_io_remove_file2( 438 svn_wc__adm_child(wcroot_abspath, 439 SVN_WC__ADM_FORMAT, 440 scratch_pool), 441 TRUE, scratch_pool)); 442 svn_error_clear(svn_io_remove_file2( 443 svn_wc__adm_child(wcroot_abspath, 444 SVN_WC__ADM_ENTRIES, 445 scratch_pool), 446 TRUE, scratch_pool)); 447 svn_error_clear(svn_io_remove_file2( 448 svn_wc__adm_child(wcroot_abspath, 449 ADM_EMPTY_FILE, 450 scratch_pool), 451 TRUE, scratch_pool)); 452 svn_error_clear(svn_io_remove_file2( 453 svn_wc__adm_child(wcroot_abspath, 454 ADM_README, 455 scratch_pool), 456 TRUE, scratch_pool)); 457 458 /* For formats <= SVN_WC__WCPROPS_MANY_FILES_VERSION, we toss the wcprops 459 for the directory itself, and then all the wcprops for the files. */ 460 svn_error_clear(svn_io_remove_file2( 461 svn_wc__adm_child(wcroot_abspath, 462 WCPROPS_FNAME_FOR_DIR, 463 scratch_pool), 464 TRUE, scratch_pool)); 465 svn_error_clear(svn_io_remove_dir2( 466 svn_wc__adm_child(wcroot_abspath, 467 WCPROPS_SUBDIR_FOR_FILES, 468 scratch_pool), 469 FALSE, NULL, NULL, scratch_pool)); 470 471 /* And for later formats, they are aggregated into one file. */ 472 svn_error_clear(svn_io_remove_file2( 473 svn_wc__adm_child(wcroot_abspath, 474 WCPROPS_ALL_DATA, 475 scratch_pool), 476 TRUE, scratch_pool)); 477 478 /* Remove the old text-base directory and the old text-base files. */ 479 svn_error_clear(svn_io_remove_dir2( 480 svn_wc__adm_child(wcroot_abspath, 481 TEXT_BASE_SUBDIR, 482 scratch_pool), 483 FALSE, NULL, NULL, scratch_pool)); 484 485 /* Remove the old properties files... whole directories at a time. */ 486 svn_error_clear(svn_io_remove_dir2( 487 svn_wc__adm_child(wcroot_abspath, 488 PROPS_SUBDIR, 489 scratch_pool), 490 FALSE, NULL, NULL, scratch_pool)); 491 svn_error_clear(svn_io_remove_dir2( 492 svn_wc__adm_child(wcroot_abspath, 493 PROP_BASE_SUBDIR, 494 scratch_pool), 495 FALSE, NULL, NULL, scratch_pool)); 496 svn_error_clear(svn_io_remove_file2( 497 svn_wc__adm_child(wcroot_abspath, 498 PROP_WORKING_FOR_DIR, 499 scratch_pool), 500 TRUE, scratch_pool)); 501 svn_error_clear(svn_io_remove_file2( 502 svn_wc__adm_child(wcroot_abspath, 503 PROP_BASE_FOR_DIR, 504 scratch_pool), 505 TRUE, scratch_pool)); 506 svn_error_clear(svn_io_remove_file2( 507 svn_wc__adm_child(wcroot_abspath, 508 PROP_REVERT_FOR_DIR, 509 scratch_pool), 510 TRUE, scratch_pool)); 511 512#if 0 513 /* ### this checks for a write-lock, and we are not (always) taking out 514 ### a write lock in all callers. */ 515 SVN_ERR(svn_wc__adm_cleanup_tmp_area(db, wcroot_abspath, iterpool)); 516#endif 517 518 /* Remove the old-style lock file LAST. */ 519 svn_error_clear(svn_io_remove_file2( 520 build_lockfile_path(wcroot_abspath, scratch_pool), 521 TRUE, scratch_pool)); 522} 523 524svn_error_t * 525svn_wc__wipe_postupgrade(const char *dir_abspath, 526 svn_boolean_t whole_admin, 527 svn_cancel_func_t cancel_func, 528 void *cancel_baton, 529 apr_pool_t *scratch_pool) 530{ 531 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 532 apr_array_header_t *subdirs; 533 svn_error_t *err; 534 svn_boolean_t delete_dir; 535 int i; 536 537 if (cancel_func) 538 SVN_ERR((*cancel_func)(cancel_baton)); 539 540 err = get_versioned_subdirs(&subdirs, &delete_dir, dir_abspath, TRUE, 541 scratch_pool, iterpool); 542 if (err) 543 { 544 if (APR_STATUS_IS_ENOENT(err->apr_err)) 545 { 546 /* An unversioned dir is obstructing a versioned dir */ 547 svn_error_clear(err); 548 err = NULL; 549 } 550 svn_pool_destroy(iterpool); 551 return svn_error_trace(err); 552 } 553 for (i = 0; i < subdirs->nelts; ++i) 554 { 555 const char *child_abspath = APR_ARRAY_IDX(subdirs, i, const char *); 556 557 svn_pool_clear(iterpool); 558 SVN_ERR(svn_wc__wipe_postupgrade(child_abspath, TRUE, 559 cancel_func, cancel_baton, iterpool)); 560 } 561 562 /* ### Should we really be ignoring errors here? */ 563 if (whole_admin) 564 svn_error_clear(svn_io_remove_dir2(svn_wc__adm_child(dir_abspath, "", 565 iterpool), 566 TRUE, NULL, NULL, iterpool)); 567 else 568 wipe_obsolete_files(dir_abspath, scratch_pool); 569 570 if (delete_dir) 571 { 572 /* If this was a WC-NG single database copy, this directory wouldn't 573 be here (unless it was deleted with --keep-local) 574 575 If the directory is empty, we can just delete it; if not we 576 keep it. 577 */ 578 svn_error_clear(svn_io_dir_remove_nonrecursive(dir_abspath, iterpool)); 579 } 580 581 svn_pool_destroy(iterpool); 582 583 return SVN_NO_ERROR; 584} 585 586/* Ensure that ENTRY has its REPOS and UUID fields set. These will be 587 used to establish the REPOSITORY row in the new database, and then 588 used within the upgraded entries as they are written into the database. 589 590 If one or both are not available, then it attempts to retrieve this 591 information from REPOS_CACHE. And if that fails from REPOS_INFO_FUNC, 592 passing REPOS_INFO_BATON. 593 Returns a user understandable error using LOCAL_ABSPATH if the 594 information cannot be obtained. */ 595static svn_error_t * 596ensure_repos_info(svn_wc_entry_t *entry, 597 const char *local_abspath, 598 svn_wc_upgrade_get_repos_info_t repos_info_func, 599 void *repos_info_baton, 600 apr_hash_t *repos_cache, 601 apr_pool_t *result_pool, 602 apr_pool_t *scratch_pool) 603{ 604 /* Easy exit. */ 605 if (entry->repos != NULL && entry->uuid != NULL) 606 return SVN_NO_ERROR; 607 608 if ((entry->repos == NULL || entry->uuid == NULL) 609 && entry->url) 610 { 611 apr_hash_index_t *hi; 612 613 for (hi = apr_hash_first(scratch_pool, repos_cache); 614 hi; hi = apr_hash_next(hi)) 615 { 616 if (svn_uri__is_ancestor(apr_hash_this_key(hi), entry->url)) 617 { 618 if (!entry->repos) 619 entry->repos = apr_hash_this_key(hi); 620 621 if (!entry->uuid) 622 entry->uuid = apr_hash_this_val(hi); 623 624 return SVN_NO_ERROR; 625 } 626 } 627 } 628 629 if (entry->repos == NULL && repos_info_func == NULL) 630 return svn_error_createf( 631 SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, 632 _("Working copy '%s' can't be upgraded because the repository root is " 633 "not available and can't be retrieved"), 634 svn_dirent_local_style(local_abspath, scratch_pool)); 635 636 if (entry->uuid == NULL && repos_info_func == NULL) 637 return svn_error_createf( 638 SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, 639 _("Working copy '%s' can't be upgraded because the repository uuid is " 640 "not available and can't be retrieved"), 641 svn_dirent_local_style(local_abspath, scratch_pool)); 642 643 if (entry->url == NULL) 644 return svn_error_createf( 645 SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, 646 _("Working copy '%s' can't be upgraded because it doesn't have a url"), 647 svn_dirent_local_style(local_abspath, scratch_pool)); 648 649 return svn_error_trace((*repos_info_func)(&entry->repos, &entry->uuid, 650 repos_info_baton, 651 entry->url, 652 result_pool, scratch_pool)); 653} 654 655 656/* 657 * Read tree conflict descriptions from @a conflict_data. Set @a *conflicts 658 * to a hash of pointers to svn_wc_conflict_description2_t objects indexed by 659 * svn_wc_conflict_description2_t.local_abspath, all newly allocated in @a 660 * pool. @a dir_path is the path to the working copy directory whose conflicts 661 * are being read. The conflicts read are the tree conflicts on the immediate 662 * child nodes of @a dir_path. Do all allocations in @a pool. 663 * 664 * Note: There were some concerns about this function: 665 * 666 * ### this is BAD. the CONFLICTS structure should not be dependent upon 667 * ### DIR_PATH. each conflict should be labeled with an entry name, not 668 * ### a whole path. (and a path which happens to vary based upon invocation 669 * ### of the user client and these APIs) 670 * 671 * those assumptions were baked into former versions of the data model, so 672 * they have to stick around here. But they have been removed from the 673 * New Way. */ 674static svn_error_t * 675read_tree_conflicts(apr_hash_t **conflicts, 676 const char *conflict_data, 677 const char *dir_path, 678 apr_pool_t *pool) 679{ 680 const svn_skel_t *skel; 681 apr_pool_t *iterpool; 682 683 *conflicts = apr_hash_make(pool); 684 685 if (conflict_data == NULL) 686 return SVN_NO_ERROR; 687 688 skel = svn_skel__parse(conflict_data, strlen(conflict_data), pool); 689 if (skel == NULL) 690 return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, 691 _("Error parsing tree conflict skel")); 692 693 iterpool = svn_pool_create(pool); 694 for (skel = skel->children; skel != NULL; skel = skel->next) 695 { 696 const svn_wc_conflict_description2_t *conflict; 697 698 svn_pool_clear(iterpool); 699 SVN_ERR(svn_wc__deserialize_conflict(&conflict, skel, dir_path, 700 pool, iterpool)); 701 if (conflict != NULL) 702 svn_hash_sets(*conflicts, 703 svn_dirent_basename(conflict->local_abspath, pool), 704 conflict); 705 } 706 svn_pool_destroy(iterpool); 707 708 return SVN_NO_ERROR; 709} 710 711/* */ 712static svn_error_t * 713migrate_single_tree_conflict_data(svn_sqlite__db_t *sdb, 714 const char *tree_conflict_data, 715 apr_int64_t wc_id, 716 const char *local_relpath, 717 apr_pool_t *scratch_pool) 718{ 719 apr_hash_t *conflicts; 720 apr_hash_index_t *hi; 721 apr_pool_t *iterpool; 722 723 SVN_ERR(read_tree_conflicts(&conflicts, tree_conflict_data, local_relpath, 724 scratch_pool)); 725 726 iterpool = svn_pool_create(scratch_pool); 727 for (hi = apr_hash_first(scratch_pool, conflicts); 728 hi; 729 hi = apr_hash_next(hi)) 730 { 731 const svn_wc_conflict_description2_t *conflict = apr_hash_this_val(hi); 732 const char *conflict_relpath; 733 const char *conflict_data; 734 svn_sqlite__stmt_t *stmt; 735 svn_boolean_t have_row; 736 svn_skel_t *skel; 737 738 svn_pool_clear(iterpool); 739 740 conflict_relpath = svn_dirent_join(local_relpath, 741 svn_dirent_basename( 742 conflict->local_abspath, iterpool), 743 iterpool); 744 745 SVN_ERR(svn_wc__serialize_conflict(&skel, conflict, iterpool, iterpool)); 746 conflict_data = svn_skel__unparse(skel, iterpool)->data; 747 748 /* See if we need to update or insert an ACTUAL node. */ 749 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_ACTUAL_NODE)); 750 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, conflict_relpath)); 751 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 752 SVN_ERR(svn_sqlite__reset(stmt)); 753 754 if (have_row) 755 { 756 /* There is an existing ACTUAL row, so just update it. */ 757 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 758 STMT_UPDATE_ACTUAL_CONFLICT)); 759 } 760 else 761 { 762 /* We need to insert an ACTUAL row with the tree conflict data. */ 763 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 764 STMT_INSERT_ACTUAL_CONFLICT)); 765 } 766 767 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wc_id, conflict_relpath, 768 conflict_data)); 769 if (!have_row) 770 SVN_ERR(svn_sqlite__bind_text(stmt, 4, local_relpath)); 771 772 SVN_ERR(svn_sqlite__step_done(stmt)); 773 } 774 775 svn_pool_destroy(iterpool); 776 777 return SVN_NO_ERROR; 778} 779 780 781/* */ 782static svn_error_t * 783migrate_tree_conflict_data(svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 784{ 785 svn_sqlite__stmt_t *stmt; 786 svn_boolean_t have_row; 787 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 788 789 /* Iterate over each node which has a set of tree conflicts, then insert 790 all of them into the new schema. */ 791 792 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 793 STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT)); 794 795 /* Get all the existing tree conflict data. */ 796 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 797 while (have_row) 798 { 799 apr_int64_t wc_id; 800 const char *local_relpath; 801 const char *tree_conflict_data; 802 803 svn_pool_clear(iterpool); 804 805 wc_id = svn_sqlite__column_int64(stmt, 0); 806 local_relpath = svn_sqlite__column_text(stmt, 1, iterpool); 807 tree_conflict_data = svn_sqlite__column_text(stmt, 2, iterpool); 808 809 SVN_ERR(migrate_single_tree_conflict_data(sdb, tree_conflict_data, 810 wc_id, local_relpath, 811 iterpool)); 812 813 /* We don't need to do anything but step over the previously 814 prepared statement. */ 815 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 816 } 817 SVN_ERR(svn_sqlite__reset(stmt)); 818 819 /* Erase all the old tree conflict data. */ 820 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 821 STMT_UPGRADE_21_ERASE_OLD_CONFLICTS)); 822 SVN_ERR(svn_sqlite__step_done(stmt)); 823 824 svn_pool_destroy(iterpool); 825 return SVN_NO_ERROR; 826} 827 828/* ### need much more docco 829 830 ### this function should be called within a sqlite transaction. it makes 831 ### assumptions around this fact. 832 833 Apply the various sets of properties to the database nodes based on 834 their existence/presence, the current state of the node, and the original 835 format of the working copy which provided these property sets. 836*/ 837static svn_error_t * 838upgrade_apply_props(svn_sqlite__db_t *sdb, 839 const char *dir_abspath, 840 const char *local_relpath, 841 apr_hash_t *base_props, 842 apr_hash_t *revert_props, 843 apr_hash_t *working_props, 844 int original_format, 845 apr_int64_t wc_id, 846 apr_pool_t *scratch_pool) 847{ 848 svn_sqlite__stmt_t *stmt; 849 svn_boolean_t have_row; 850 int top_op_depth = -1; 851 int below_op_depth = -1; 852 svn_wc__db_status_t top_presence; 853 svn_wc__db_status_t below_presence; 854 int affected_rows; 855 856 /* ### working_props: use set_props_txn. 857 ### if working_props == NULL, then skip. what if they equal the 858 ### pristine props? we should probably do the compare here. 859 ### 860 ### base props go into WORKING_NODE if avail, otherwise BASE. 861 ### 862 ### revert only goes into BASE. (and WORKING better be there!) 863 864 Prior to 1.4.0 (ORIGINAL_FORMAT < 8), REVERT_PROPS did not exist. If a 865 file was deleted, then a copy (potentially with props) was disallowed 866 and could not replace the deletion. An addition *could* be performed, 867 but that would never bring its own props. 868 869 1.4.0 through 1.4.5 created the concept of REVERT_PROPS, but had a 870 bug in svn_wc_add_repos_file2() whereby a copy-with-props did NOT 871 construct a REVERT_PROPS if the target had no props. Thus, reverting 872 the delete/copy would see no REVERT_PROPS to restore, leaving the 873 props from the copy source intact, and appearing as if they are (now) 874 the base props for the previously-deleted file. (wc corruption) 875 876 1.4.6 ensured that an empty REVERT_PROPS would be established at all 877 times. See issue 2530, and r861670 as starting points. 878 879 We will use ORIGINAL_FORMAT and SVN_WC__NO_REVERT_FILES to determine 880 the handling of our inputs, relative to the state of this node. 881 */ 882 883 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODE_INFO)); 884 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath)); 885 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 886 if (have_row) 887 { 888 top_op_depth = svn_sqlite__column_int(stmt, 0); 889 top_presence = svn_sqlite__column_token(stmt, 3, presence_map); 890 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 891 if (have_row) 892 { 893 below_presence = svn_sqlite__column_token(stmt, 3, presence_map); 894 895 /* There might be an intermediate layer on mixed-revision copies, 896 or when BASE is shadowed */ 897 if (below_presence == svn_wc__db_status_not_present 898 || below_presence == svn_wc__db_status_deleted) 899 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 900 901 if (have_row) 902 { 903 below_presence = svn_sqlite__column_token(stmt, 3, presence_map); 904 below_op_depth = svn_sqlite__column_int(stmt, 0); 905 } 906 } 907 } 908 SVN_ERR(svn_sqlite__reset(stmt)); 909 910 /* Detect the buggy scenario described above. We cannot upgrade this 911 working copy if we have no idea where BASE_PROPS should go. */ 912 if (original_format > SVN_WC__NO_REVERT_FILES 913 && revert_props == NULL 914 && top_op_depth != -1 915 && top_presence == svn_wc__db_status_normal 916 && below_op_depth != -1 917 && below_presence != svn_wc__db_status_not_present) 918 { 919 /* There should be REVERT_PROPS, so it appears that we just ran into 920 the described bug. Sigh. */ 921 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, 922 _("The properties of '%s' are in an " 923 "indeterminate state and cannot be " 924 "upgraded. See issue #2530."), 925 svn_dirent_local_style( 926 svn_dirent_join(dir_abspath, local_relpath, 927 scratch_pool), scratch_pool)); 928 } 929 930 /* Need at least one row, or two rows if there are revert props */ 931 if (top_op_depth == -1 932 || (below_op_depth == -1 && revert_props)) 933 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, 934 _("Insufficient NODES rows for '%s'"), 935 svn_dirent_local_style( 936 svn_dirent_join(dir_abspath, local_relpath, 937 scratch_pool), scratch_pool)); 938 939 /* one row, base props only: upper row gets base props 940 two rows, base props only: lower row gets base props 941 two rows, revert props only: lower row gets revert props 942 two rows, base and revert props: upper row gets base, lower gets revert */ 943 944 945 if (revert_props || below_op_depth == -1) 946 { 947 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 948 STMT_UPDATE_NODE_PROPS)); 949 SVN_ERR(svn_sqlite__bindf(stmt, "isd", 950 wc_id, local_relpath, top_op_depth)); 951 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, base_props, scratch_pool)); 952 SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); 953 954 SVN_ERR_ASSERT(affected_rows == 1); 955 } 956 957 if (below_op_depth != -1) 958 { 959 apr_hash_t *props = revert_props ? revert_props : base_props; 960 961 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 962 STMT_UPDATE_NODE_PROPS)); 963 SVN_ERR(svn_sqlite__bindf(stmt, "isd", 964 wc_id, local_relpath, below_op_depth)); 965 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool)); 966 SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); 967 968 SVN_ERR_ASSERT(affected_rows == 1); 969 } 970 971 /* If there are WORKING_PROPS, then they always go into ACTUAL_NODE. */ 972 if (working_props != NULL 973 && base_props != NULL) 974 { 975 apr_array_header_t *diffs; 976 977 SVN_ERR(svn_prop_diffs(&diffs, working_props, base_props, scratch_pool)); 978 979 if (diffs->nelts == 0) 980 working_props = NULL; /* No differences */ 981 } 982 983 if (working_props != NULL) 984 { 985 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 986 STMT_UPDATE_ACTUAL_PROPS)); 987 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath)); 988 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, working_props, 989 scratch_pool)); 990 SVN_ERR(svn_sqlite__update(&affected_rows, stmt)); 991 992 if (affected_rows == 0) 993 { 994 /* We have to insert a row in ACTUAL */ 995 996 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 997 STMT_INSERT_ACTUAL_PROPS)); 998 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath)); 999 if (*local_relpath != '\0') 1000 SVN_ERR(svn_sqlite__bind_text(stmt, 3, 1001 svn_relpath_dirname(local_relpath, 1002 scratch_pool))); 1003 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, working_props, 1004 scratch_pool)); 1005 return svn_error_trace(svn_sqlite__step_done(stmt)); 1006 } 1007 } 1008 1009 return SVN_NO_ERROR; 1010} 1011 1012 1013struct bump_baton { 1014 const char *wcroot_abspath; 1015}; 1016 1017/* Migrate the properties for one node (LOCAL_ABSPATH). */ 1018static svn_error_t * 1019migrate_node_props(const char *dir_abspath, 1020 const char *new_wcroot_abspath, 1021 const char *name, 1022 svn_sqlite__db_t *sdb, 1023 int original_format, 1024 apr_int64_t wc_id, 1025 apr_pool_t *scratch_pool) 1026{ 1027 const char *base_abspath; /* old name. nowadays: "pristine" */ 1028 const char *revert_abspath; /* old name. nowadays: "BASE" */ 1029 const char *working_abspath; /* old name. nowadays: "ACTUAL" */ 1030 apr_hash_t *base_props; 1031 apr_hash_t *revert_props; 1032 apr_hash_t *working_props; 1033 const char *old_wcroot_abspath 1034 = svn_dirent_get_longest_ancestor(dir_abspath, new_wcroot_abspath, 1035 scratch_pool); 1036 const char *dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath, 1037 dir_abspath); 1038 1039 if (*name == '\0') 1040 { 1041 base_abspath = svn_wc__adm_child(dir_abspath, 1042 PROP_BASE_FOR_DIR, scratch_pool); 1043 revert_abspath = svn_wc__adm_child(dir_abspath, 1044 PROP_REVERT_FOR_DIR, scratch_pool); 1045 working_abspath = svn_wc__adm_child(dir_abspath, 1046 PROP_WORKING_FOR_DIR, scratch_pool); 1047 } 1048 else 1049 { 1050 const char *basedir_abspath; 1051 const char *propsdir_abspath; 1052 1053 propsdir_abspath = svn_wc__adm_child(dir_abspath, PROPS_SUBDIR, 1054 scratch_pool); 1055 basedir_abspath = svn_wc__adm_child(dir_abspath, PROP_BASE_SUBDIR, 1056 scratch_pool); 1057 1058 base_abspath = svn_dirent_join(basedir_abspath, 1059 apr_pstrcat(scratch_pool, 1060 name, 1061 SVN_WC__BASE_EXT, 1062 SVN_VA_NULL), 1063 scratch_pool); 1064 1065 revert_abspath = svn_dirent_join(basedir_abspath, 1066 apr_pstrcat(scratch_pool, 1067 name, 1068 SVN_WC__REVERT_EXT, 1069 SVN_VA_NULL), 1070 scratch_pool); 1071 1072 working_abspath = svn_dirent_join(propsdir_abspath, 1073 apr_pstrcat(scratch_pool, 1074 name, 1075 SVN_WC__WORK_EXT, 1076 SVN_VA_NULL), 1077 scratch_pool); 1078 } 1079 1080 SVN_ERR(read_propfile(&base_props, base_abspath, 1081 scratch_pool, scratch_pool)); 1082 SVN_ERR(read_propfile(&revert_props, revert_abspath, 1083 scratch_pool, scratch_pool)); 1084 SVN_ERR(read_propfile(&working_props, working_abspath, 1085 scratch_pool, scratch_pool)); 1086 1087 return svn_error_trace(upgrade_apply_props( 1088 sdb, new_wcroot_abspath, 1089 svn_relpath_join(dir_relpath, name, scratch_pool), 1090 base_props, revert_props, working_props, 1091 original_format, wc_id, 1092 scratch_pool)); 1093} 1094 1095 1096/* */ 1097static svn_error_t * 1098migrate_props(const char *dir_abspath, 1099 const char *new_wcroot_abspath, 1100 svn_sqlite__db_t *sdb, 1101 int original_format, 1102 apr_int64_t wc_id, 1103 apr_pool_t *scratch_pool) 1104{ 1105 /* General logic here: iterate over all the immediate children of the root 1106 (since we aren't yet in a centralized system), and for any properties that 1107 exist, map them as follows: 1108 1109 if (revert props exist): 1110 revert -> BASE 1111 base -> WORKING 1112 working -> ACTUAL 1113 else if (prop pristine is working [as defined in props.c] ): 1114 base -> WORKING 1115 working -> ACTUAL 1116 else: 1117 base -> BASE 1118 working -> ACTUAL 1119 1120 ### the middle "test" should simply look for a WORKING_NODE row 1121 1122 Note that it is legal for "working" props to be missing. That implies 1123 no local changes to the properties. 1124 */ 1125 const apr_array_header_t *children; 1126 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1127 const char *old_wcroot_abspath 1128 = svn_dirent_get_longest_ancestor(dir_abspath, new_wcroot_abspath, 1129 scratch_pool); 1130 const char *dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath, 1131 dir_abspath); 1132 int i; 1133 1134 /* Migrate the props for "this dir". */ 1135 SVN_ERR(migrate_node_props(dir_abspath, new_wcroot_abspath, "", sdb, 1136 original_format, wc_id, iterpool)); 1137 1138 /* Iterate over all the files in this SDB. */ 1139 SVN_ERR(get_versioned_files(&children, dir_relpath, sdb, wc_id, scratch_pool, 1140 iterpool)); 1141 for (i = 0; i < children->nelts; i++) 1142 { 1143 const char *name = APR_ARRAY_IDX(children, i, const char *); 1144 1145 svn_pool_clear(iterpool); 1146 1147 SVN_ERR(migrate_node_props(dir_abspath, new_wcroot_abspath, 1148 name, sdb, original_format, wc_id, iterpool)); 1149 } 1150 1151 svn_pool_destroy(iterpool); 1152 1153 return SVN_NO_ERROR; 1154} 1155 1156 1157/* If STR ends with SUFFIX and is longer than SUFFIX, return the part of 1158 * STR that comes before SUFFIX; else return NULL. */ 1159static char * 1160remove_suffix(const char *str, const char *suffix, apr_pool_t *result_pool) 1161{ 1162 size_t str_len = strlen(str); 1163 size_t suffix_len = strlen(suffix); 1164 1165 if (str_len > suffix_len 1166 && strcmp(str + str_len - suffix_len, suffix) == 0) 1167 { 1168 return apr_pstrmemdup(result_pool, str, str_len - suffix_len); 1169 } 1170 1171 return NULL; 1172} 1173 1174/* Copy all the text-base files from the administrative area of WC directory 1175 DIR_ABSPATH into the pristine store of SDB which is located in directory 1176 NEW_WCROOT_ABSPATH. 1177 1178 Set *TEXT_BASES_INFO to a new hash, allocated in RESULT_POOL, that maps 1179 (const char *) name of the versioned file to (svn_wc__text_base_info_t *) 1180 information about the pristine text. */ 1181static svn_error_t * 1182migrate_text_bases(apr_hash_t **text_bases_info, 1183 const char *dir_abspath, 1184 const char *new_wcroot_abspath, 1185 svn_sqlite__db_t *sdb, 1186 apr_pool_t *result_pool, 1187 apr_pool_t *scratch_pool) 1188{ 1189 apr_hash_t *dirents; 1190 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1191 apr_hash_index_t *hi; 1192 const char *text_base_dir = svn_wc__adm_child(dir_abspath, 1193 TEXT_BASE_SUBDIR, 1194 scratch_pool); 1195 1196 *text_bases_info = apr_hash_make(result_pool); 1197 1198 /* Iterate over the text-base files */ 1199 SVN_ERR(svn_io_get_dirents3(&dirents, text_base_dir, TRUE, 1200 scratch_pool, scratch_pool)); 1201 for (hi = apr_hash_first(scratch_pool, dirents); hi; 1202 hi = apr_hash_next(hi)) 1203 { 1204 const char *text_base_basename = apr_hash_this_key(hi); 1205 svn_checksum_t *md5_checksum; 1206 svn_checksum_t *sha1_checksum; 1207 1208 svn_pool_clear(iterpool); 1209 1210 /* Calculate its checksums and copy it to the pristine store */ 1211 { 1212 const char *pristine_path; 1213 const char *text_base_path; 1214 const char *temp_path; 1215 svn_sqlite__stmt_t *stmt; 1216 apr_finfo_t finfo; 1217 svn_stream_t *read_stream; 1218 svn_stream_t *result_stream; 1219 1220 text_base_path = svn_dirent_join(text_base_dir, text_base_basename, 1221 iterpool); 1222 1223 /* Create a copy and calculate a checksum in one step */ 1224 SVN_ERR(svn_stream_open_unique(&result_stream, &temp_path, 1225 new_wcroot_abspath, 1226 svn_io_file_del_none, 1227 iterpool, iterpool)); 1228 1229 SVN_ERR(svn_stream_open_readonly(&read_stream, text_base_path, 1230 iterpool, iterpool)); 1231 1232 read_stream = svn_stream_checksummed2(read_stream, &md5_checksum, 1233 NULL, svn_checksum_md5, 1234 TRUE, iterpool); 1235 1236 read_stream = svn_stream_checksummed2(read_stream, &sha1_checksum, 1237 NULL, svn_checksum_sha1, 1238 TRUE, iterpool); 1239 1240 /* This calculates the hash, creates a copy and closes the stream */ 1241 SVN_ERR(svn_stream_copy3(read_stream, result_stream, 1242 NULL, NULL, iterpool)); 1243 1244 SVN_ERR(svn_io_stat(&finfo, text_base_path, APR_FINFO_SIZE, iterpool)); 1245 1246 /* Insert a row into the pristine table. */ 1247 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 1248 STMT_INSERT_OR_IGNORE_PRISTINE)); 1249 SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, iterpool)); 1250 SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, iterpool)); 1251 SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size)); 1252 SVN_ERR(svn_sqlite__insert(NULL, stmt)); 1253 1254 SVN_ERR(svn_wc__db_pristine_get_future_path(&pristine_path, 1255 new_wcroot_abspath, 1256 sha1_checksum, 1257 iterpool, iterpool)); 1258 1259 /* Ensure any sharding directories exist. */ 1260 SVN_ERR(svn_wc__ensure_directory(svn_dirent_dirname(pristine_path, 1261 iterpool), 1262 iterpool)); 1263 1264 /* Now move the file into the pristine store, overwriting 1265 existing files with the same checksum. */ 1266 SVN_ERR(svn_io_file_move(temp_path, pristine_path, iterpool)); 1267 } 1268 1269 /* Add the checksums for this text-base to *TEXT_BASES_INFO. */ 1270 { 1271 const char *versioned_file_name; 1272 svn_boolean_t is_revert_base; 1273 svn_wc__text_base_info_t *info; 1274 svn_wc__text_base_file_info_t *file_info; 1275 1276 /* Determine the versioned file name and whether this is a normal base 1277 * or a revert base. */ 1278 versioned_file_name = remove_suffix(text_base_basename, 1279 SVN_WC__REVERT_EXT, result_pool); 1280 if (versioned_file_name) 1281 { 1282 is_revert_base = TRUE; 1283 } 1284 else 1285 { 1286 versioned_file_name = remove_suffix(text_base_basename, 1287 SVN_WC__BASE_EXT, result_pool); 1288 is_revert_base = FALSE; 1289 } 1290 1291 if (! versioned_file_name) 1292 { 1293 /* Some file that doesn't end with .svn-base or .svn-revert. 1294 No idea why that would be in our administrative area, but 1295 we shouldn't segfault on this case. 1296 1297 Note that we already copied this file in the pristine store, 1298 but the next cleanup will take care of that. 1299 */ 1300 continue; 1301 } 1302 1303 /* Create a new info struct for this versioned file, or fill in the 1304 * existing one if this is the second text-base we've found for it. */ 1305 info = svn_hash_gets(*text_bases_info, versioned_file_name); 1306 if (info == NULL) 1307 info = apr_pcalloc(result_pool, sizeof (*info)); 1308 file_info = (is_revert_base ? &info->revert_base : &info->normal_base); 1309 1310 file_info->sha1_checksum = svn_checksum_dup(sha1_checksum, result_pool); 1311 file_info->md5_checksum = svn_checksum_dup(md5_checksum, result_pool); 1312 svn_hash_sets(*text_bases_info, versioned_file_name, info); 1313 } 1314 } 1315 1316 svn_pool_destroy(iterpool); 1317 1318 return SVN_NO_ERROR; 1319} 1320 1321static svn_error_t * 1322bump_to_20(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1323{ 1324 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_CREATE_NODES)); 1325 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_20)); 1326 return SVN_NO_ERROR; 1327} 1328 1329static svn_error_t * 1330bump_to_21(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1331{ 1332 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_21)); 1333 SVN_ERR(migrate_tree_conflict_data(sdb, scratch_pool)); 1334 return SVN_NO_ERROR; 1335} 1336 1337static svn_error_t * 1338bump_to_22(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1339{ 1340 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_22)); 1341 return SVN_NO_ERROR; 1342} 1343 1344static svn_error_t * 1345bump_to_23(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1346{ 1347 const char *wcroot_abspath = ((struct bump_baton *)baton)->wcroot_abspath; 1348 svn_sqlite__stmt_t *stmt; 1349 svn_boolean_t have_row; 1350 1351 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 1352 STMT_UPGRADE_23_HAS_WORKING_NODES)); 1353 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1354 SVN_ERR(svn_sqlite__reset(stmt)); 1355 if (have_row) 1356 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 1357 _("The working copy at '%s' is format 22 with " 1358 "WORKING nodes; use a format 22 client to " 1359 "diff/revert before using this client"), 1360 wcroot_abspath); 1361 1362 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_23)); 1363 return SVN_NO_ERROR; 1364} 1365 1366static svn_error_t * 1367bump_to_24(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1368{ 1369 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_24)); 1370 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_CREATE_NODES_TRIGGERS)); 1371 return SVN_NO_ERROR; 1372} 1373 1374static svn_error_t * 1375bump_to_25(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1376{ 1377 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_25)); 1378 return SVN_NO_ERROR; 1379} 1380 1381static svn_error_t * 1382bump_to_26(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1383{ 1384 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_26)); 1385 return SVN_NO_ERROR; 1386} 1387 1388static svn_error_t * 1389bump_to_27(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1390{ 1391 const char *wcroot_abspath = ((struct bump_baton *)baton)->wcroot_abspath; 1392 svn_sqlite__stmt_t *stmt; 1393 svn_boolean_t have_row; 1394 1395 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 1396 STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS)); 1397 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1398 SVN_ERR(svn_sqlite__reset(stmt)); 1399 if (have_row) 1400 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 1401 _("The working copy at '%s' is format 26 with " 1402 "conflicts; use a format 26 client to resolve " 1403 "before using this client"), 1404 wcroot_abspath); 1405 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_27)); 1406 return SVN_NO_ERROR; 1407} 1408 1409static svn_error_t * 1410bump_to_28(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1411{ 1412 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_28)); 1413 return SVN_NO_ERROR; 1414} 1415 1416/* If FINFO indicates that ABSPATH names a file, rename it to 1417 * '<ABSPATH>.svn-base'. 1418 * 1419 * Ignore any file whose name is not the expected length, in order to make 1420 * life easier for any developer who runs this code twice or has some 1421 * non-standard files in the pristine directory. 1422 * 1423 * A callback for bump_to_29(), implementing #svn_io_walk_func_t. */ 1424static svn_error_t * 1425rename_pristine_file(void *baton, 1426 const char *abspath, 1427 const apr_finfo_t *finfo, 1428 apr_pool_t *pool) 1429{ 1430 if (finfo->filetype == APR_REG 1431 && (strlen(svn_dirent_basename(abspath, pool)) 1432 == PRISTINE_BASENAME_OLD_LEN)) 1433 { 1434 const char *new_abspath 1435 = apr_pstrcat(pool, abspath, PRISTINE_STORAGE_EXT, SVN_VA_NULL); 1436 1437 SVN_ERR(svn_io_file_rename(abspath, new_abspath, pool)); 1438 } 1439 return SVN_NO_ERROR; 1440} 1441 1442static svn_error_t * 1443upgrade_externals(struct bump_baton *bb, 1444 svn_sqlite__db_t *sdb, 1445 apr_pool_t *scratch_pool) 1446{ 1447 svn_sqlite__stmt_t *stmt; 1448 svn_sqlite__stmt_t *stmt_add; 1449 svn_boolean_t have_row; 1450 apr_pool_t *iterpool; 1451 1452 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 1453 STMT_SELECT_EXTERNAL_PROPERTIES)); 1454 1455 SVN_ERR(svn_sqlite__get_statement(&stmt_add, sdb, 1456 STMT_INSERT_EXTERNAL)); 1457 1458 /* ### For this intermediate upgrade we just assume WC_ID = 1. 1459 ### Before this bump we lost track of externals all the time, 1460 ### so lets keep this easy. */ 1461 SVN_ERR(svn_sqlite__bindf(stmt, "is", (apr_int64_t)1, "")); 1462 1463 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1464 1465 iterpool = svn_pool_create(scratch_pool); 1466 while (have_row) 1467 { 1468 apr_hash_t *props; 1469 const char *externals; 1470 1471 svn_pool_clear(iterpool); 1472 1473 SVN_ERR(svn_sqlite__column_properties(&props, stmt, 0, 1474 iterpool, iterpool)); 1475 1476 externals = svn_prop_get_value(props, SVN_PROP_EXTERNALS); 1477 1478 if (externals) 1479 { 1480 apr_array_header_t *ext; 1481 const char *local_relpath; 1482 const char *local_abspath; 1483 int i; 1484 1485 local_relpath = svn_sqlite__column_text(stmt, 1, NULL); 1486 local_abspath = svn_dirent_join(bb->wcroot_abspath, local_relpath, 1487 iterpool); 1488 1489 SVN_ERR(svn_wc_parse_externals_description3(&ext, local_abspath, 1490 externals, FALSE, 1491 iterpool)); 1492 1493 for (i = 0; i < ext->nelts; i++) 1494 { 1495 const svn_wc_external_item2_t *item; 1496 const char *item_relpath; 1497 1498 item = APR_ARRAY_IDX(ext, i, const svn_wc_external_item2_t *); 1499 item_relpath = svn_relpath_join(local_relpath, item->target_dir, 1500 iterpool); 1501 1502 /* Insert dummy externals definitions: Insert an unknown 1503 external, to make sure it will be cleaned up when it is not 1504 updated on the next update. */ 1505 SVN_ERR(svn_sqlite__bindf(stmt_add, "isssssis", 1506 (apr_int64_t)1, /* wc_id */ 1507 item_relpath, 1508 svn_relpath_dirname(item_relpath, 1509 iterpool), 1510 "normal", 1511 "unknown", 1512 local_relpath, 1513 (apr_int64_t)1, /* repos_id */ 1514 "" /* repos_relpath */)); 1515 SVN_ERR(svn_sqlite__insert(NULL, stmt_add)); 1516 } 1517 } 1518 1519 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1520 } 1521 1522 svn_pool_destroy(iterpool); 1523 return svn_error_trace(svn_sqlite__reset(stmt)); 1524} 1525 1526static svn_error_t * 1527bump_to_29(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1528{ 1529 struct bump_baton *bb = baton; 1530 const char *wcroot_abspath = bb->wcroot_abspath; 1531 const char *pristine_dir_abspath; 1532 1533 /* Rename all pristine files, adding a ".svn-base" suffix. */ 1534 pristine_dir_abspath = svn_dirent_join_many(scratch_pool, wcroot_abspath, 1535 svn_wc_get_adm_dir(scratch_pool), 1536 PRISTINE_STORAGE_RELPATH, 1537 SVN_VA_NULL); 1538 SVN_ERR(svn_io_dir_walk2(pristine_dir_abspath, APR_FINFO_MIN, 1539 rename_pristine_file, NULL, scratch_pool)); 1540 1541 /* Externals */ 1542 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_CREATE_EXTERNALS)); 1543 1544 SVN_ERR(upgrade_externals(bb, sdb, scratch_pool)); 1545 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_29)); 1546 return SVN_NO_ERROR; 1547} 1548 1549svn_error_t * 1550svn_wc__upgrade_conflict_skel_from_raw(svn_skel_t **conflicts, 1551 svn_wc__db_t *db, 1552 const char *wri_abspath, 1553 const char *local_relpath, 1554 const char *conflict_old, 1555 const char *conflict_wrk, 1556 const char *conflict_new, 1557 const char *prej_file, 1558 const char *tree_conflict_data, 1559 apr_size_t tree_conflict_len, 1560 apr_pool_t *result_pool, 1561 apr_pool_t *scratch_pool) 1562{ 1563 svn_skel_t *conflict_data = NULL; 1564 const char *wcroot_abspath; 1565 1566 SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath, 1567 scratch_pool, scratch_pool)); 1568 1569 if (conflict_old || conflict_new || conflict_wrk) 1570 { 1571 const char *old_abspath = NULL; 1572 const char *new_abspath = NULL; 1573 const char *wrk_abspath = NULL; 1574 1575 conflict_data = svn_wc__conflict_skel_create(result_pool); 1576 1577 if (conflict_old) 1578 old_abspath = svn_dirent_join(wcroot_abspath, conflict_old, 1579 scratch_pool); 1580 1581 if (conflict_new) 1582 new_abspath = svn_dirent_join(wcroot_abspath, conflict_new, 1583 scratch_pool); 1584 1585 if (conflict_wrk) 1586 wrk_abspath = svn_dirent_join(wcroot_abspath, conflict_wrk, 1587 scratch_pool); 1588 1589 SVN_ERR(svn_wc__conflict_skel_add_text_conflict(conflict_data, 1590 db, wri_abspath, 1591 wrk_abspath, 1592 old_abspath, 1593 new_abspath, 1594 scratch_pool, 1595 scratch_pool)); 1596 } 1597 1598 if (prej_file) 1599 { 1600 const char *prej_abspath; 1601 1602 if (!conflict_data) 1603 conflict_data = svn_wc__conflict_skel_create(result_pool); 1604 1605 prej_abspath = svn_dirent_join(wcroot_abspath, prej_file, scratch_pool); 1606 1607 SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflict_data, 1608 db, wri_abspath, 1609 prej_abspath, 1610 NULL, NULL, NULL, 1611 apr_hash_make(scratch_pool), 1612 scratch_pool, 1613 scratch_pool)); 1614 } 1615 1616 if (tree_conflict_data) 1617 { 1618 svn_skel_t *tc_skel; 1619 const svn_wc_conflict_description2_t *tc; 1620 const char *local_abspath; 1621 1622 if (!conflict_data) 1623 conflict_data = svn_wc__conflict_skel_create(scratch_pool); 1624 1625 tc_skel = svn_skel__parse(tree_conflict_data, tree_conflict_len, 1626 scratch_pool); 1627 1628 local_abspath = svn_dirent_join(wcroot_abspath, local_relpath, 1629 scratch_pool); 1630 1631 SVN_ERR(svn_wc__deserialize_conflict(&tc, tc_skel, 1632 svn_dirent_dirname(local_abspath, 1633 scratch_pool), 1634 scratch_pool, scratch_pool)); 1635 1636 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(conflict_data, 1637 db, wri_abspath, 1638 tc->reason, 1639 tc->action, 1640 NULL, 1641 scratch_pool, 1642 scratch_pool)); 1643 1644 switch (tc->operation) 1645 { 1646 case svn_wc_operation_update: 1647 default: 1648 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_data, 1649 tc->src_left_version, 1650 tc->src_right_version, 1651 scratch_pool, 1652 scratch_pool)); 1653 break; 1654 case svn_wc_operation_switch: 1655 SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict_data, 1656 tc->src_left_version, 1657 tc->src_right_version, 1658 scratch_pool, 1659 scratch_pool)); 1660 break; 1661 case svn_wc_operation_merge: 1662 SVN_ERR(svn_wc__conflict_skel_set_op_merge(conflict_data, 1663 tc->src_left_version, 1664 tc->src_right_version, 1665 scratch_pool, 1666 scratch_pool)); 1667 break; 1668 } 1669 } 1670 else if (conflict_data) 1671 { 1672 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_data, NULL, NULL, 1673 scratch_pool, 1674 scratch_pool)); 1675 } 1676 1677 *conflicts = conflict_data; 1678 return SVN_NO_ERROR; 1679} 1680 1681/* Helper function to upgrade a single conflict from bump_to_30 */ 1682static svn_error_t * 1683bump_30_upgrade_one_conflict(svn_wc__db_t *wc_db, 1684 const char *wcroot_abspath, 1685 svn_sqlite__stmt_t *stmt, 1686 svn_sqlite__db_t *sdb, 1687 apr_pool_t *scratch_pool) 1688{ 1689 svn_sqlite__stmt_t *stmt_store; 1690 svn_stringbuf_t *skel_data; 1691 svn_skel_t *conflict_data; 1692 apr_int64_t wc_id = svn_sqlite__column_int64(stmt, 0); 1693 const char *local_relpath = svn_sqlite__column_text(stmt, 1, NULL); 1694 const char *conflict_old = svn_sqlite__column_text(stmt, 2, NULL); 1695 const char *conflict_wrk = svn_sqlite__column_text(stmt, 3, NULL); 1696 const char *conflict_new = svn_sqlite__column_text(stmt, 4, NULL); 1697 const char *prop_reject = svn_sqlite__column_text(stmt, 5, NULL); 1698 apr_size_t tree_conflict_size; 1699 const char *tree_conflict_data = svn_sqlite__column_blob(stmt, 6, 1700 &tree_conflict_size, NULL); 1701 1702 SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw(&conflict_data, 1703 wc_db, wcroot_abspath, 1704 local_relpath, 1705 conflict_old, 1706 conflict_wrk, 1707 conflict_new, 1708 prop_reject, 1709 tree_conflict_data, 1710 tree_conflict_size, 1711 scratch_pool, scratch_pool)); 1712 1713 SVN_ERR_ASSERT(conflict_data != NULL); 1714 1715 skel_data = svn_skel__unparse(conflict_data, scratch_pool); 1716 1717 SVN_ERR(svn_sqlite__get_statement(&stmt_store, sdb, 1718 STMT_UPGRADE_30_SET_CONFLICT)); 1719 SVN_ERR(svn_sqlite__bindf(stmt_store, "isb", wc_id, local_relpath, 1720 skel_data->data, skel_data->len)); 1721 SVN_ERR(svn_sqlite__step_done(stmt_store)); 1722 1723 return SVN_NO_ERROR; 1724} 1725 1726static svn_error_t * 1727bump_to_30(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1728{ 1729 struct bump_baton *bb = baton; 1730 svn_boolean_t have_row; 1731 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1732 svn_sqlite__stmt_t *stmt; 1733 svn_wc__db_t *db; /* Read only temp db */ 1734 1735 SVN_ERR(svn_wc__db_open(&db, NULL, TRUE /* open_without_upgrade */, FALSE, 1736 scratch_pool, scratch_pool)); 1737 1738 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 1739 STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE)); 1740 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1741 1742 while (have_row) 1743 { 1744 svn_error_t *err; 1745 svn_pool_clear(iterpool); 1746 1747 err = bump_30_upgrade_one_conflict(db, bb->wcroot_abspath, stmt, sdb, 1748 iterpool); 1749 1750 if (err) 1751 { 1752 return svn_error_trace( 1753 svn_error_compose_create( 1754 err, 1755 svn_sqlite__reset(stmt))); 1756 } 1757 1758 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1759 } 1760 SVN_ERR(svn_sqlite__reset(stmt)); 1761 1762 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_30)); 1763 SVN_ERR(svn_wc__db_close(db)); 1764 return SVN_NO_ERROR; 1765} 1766 1767static svn_error_t * 1768bump_to_31(void *baton, 1769 svn_sqlite__db_t *sdb, 1770 apr_pool_t *scratch_pool) 1771{ 1772 svn_sqlite__stmt_t *stmt, *stmt_mark_switch_roots; 1773 svn_boolean_t have_row; 1774 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1775 apr_array_header_t *empty_iprops = apr_array_make( 1776 scratch_pool, 0, sizeof(svn_prop_inherited_item_t *)); 1777 svn_boolean_t iprops_column_exists = FALSE; 1778 svn_error_t *err; 1779 1780 /* Add the inherited_props column to NODES if it does not yet exist. 1781 * 1782 * When using a format >= 31 client to upgrade from old formats which 1783 * did not yet have a NODES table, the inherited_props column has 1784 * already been created as part of the NODES table. Attemping to add 1785 * the inherited_props column will raise an error in this case, so check 1786 * if the column exists first. 1787 * 1788 * Checking for the existence of a column before ALTER TABLE is not 1789 * possible within SQLite. We need to run a separate query and evaluate 1790 * its result in C first. 1791 */ 1792 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_PRAGMA_TABLE_INFO_NODES)); 1793 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1794 while (have_row) 1795 { 1796 const char *column_name = svn_sqlite__column_text(stmt, 1, NULL); 1797 1798 if (strcmp(column_name, "inherited_props") == 0) 1799 { 1800 iprops_column_exists = TRUE; 1801 break; 1802 } 1803 1804 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1805 } 1806 SVN_ERR(svn_sqlite__reset(stmt)); 1807 if (!iprops_column_exists) 1808 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_31_ALTER_TABLE)); 1809 1810 /* Run additional statements to finalize the upgrade to format 31. */ 1811 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_31_FINALIZE)); 1812 1813 /* Set inherited_props to an empty array for the roots of all 1814 switched subtrees in the WC. This allows subsequent updates 1815 to recognize these roots as needing an iprops cache. */ 1816 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 1817 STMT_UPGRADE_31_SELECT_WCROOT_NODES)); 1818 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1819 1820 err = svn_sqlite__get_statement(&stmt_mark_switch_roots, sdb, 1821 STMT_UPDATE_IPROP); 1822 if (err) 1823 return svn_error_compose_create(err, svn_sqlite__reset(stmt)); 1824 1825 while (have_row) 1826 { 1827 const char *switched_relpath = svn_sqlite__column_text(stmt, 1, NULL); 1828 apr_int64_t wc_id = svn_sqlite__column_int64(stmt, 0); 1829 1830 err = svn_sqlite__bindf(stmt_mark_switch_roots, "is", wc_id, 1831 switched_relpath); 1832 if (!err) 1833 err = svn_sqlite__bind_iprops(stmt_mark_switch_roots, 3, 1834 empty_iprops, iterpool); 1835 if (!err) 1836 err = svn_sqlite__step_done(stmt_mark_switch_roots); 1837 if (!err) 1838 err = svn_sqlite__step(&have_row, stmt); 1839 1840 if (err) 1841 return svn_error_compose_create( 1842 err, 1843 svn_error_compose_create( 1844 /* Reset in either order is OK. */ 1845 svn_sqlite__reset(stmt), 1846 svn_sqlite__reset(stmt_mark_switch_roots))); 1847 } 1848 1849 err = svn_sqlite__reset(stmt_mark_switch_roots); 1850 if (err) 1851 return svn_error_compose_create(err, svn_sqlite__reset(stmt)); 1852 SVN_ERR(svn_sqlite__reset(stmt)); 1853 1854 svn_pool_destroy(iterpool); 1855 1856 return SVN_NO_ERROR; 1857} 1858 1859static svn_error_t * 1860upgrade_apply_dav_cache(svn_sqlite__db_t *sdb, 1861 const char *dir_relpath, 1862 apr_int64_t wc_id, 1863 apr_hash_t *cache_values, 1864 apr_pool_t *scratch_pool) 1865{ 1866 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1867 apr_hash_index_t *hi; 1868 svn_sqlite__stmt_t *stmt; 1869 1870 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 1871 STMT_UPDATE_BASE_NODE_DAV_CACHE)); 1872 1873 /* Iterate over all the wcprops, writing each one to the wc_db. */ 1874 for (hi = apr_hash_first(scratch_pool, cache_values); 1875 hi; 1876 hi = apr_hash_next(hi)) 1877 { 1878 const char *name = apr_hash_this_key(hi); 1879 apr_hash_t *props = apr_hash_this_val(hi); 1880 const char *local_relpath; 1881 1882 svn_pool_clear(iterpool); 1883 1884 local_relpath = svn_relpath_join(dir_relpath, name, iterpool); 1885 1886 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath)); 1887 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, iterpool)); 1888 SVN_ERR(svn_sqlite__step_done(stmt)); 1889 } 1890 1891 svn_pool_destroy(iterpool); 1892 1893 return SVN_NO_ERROR; 1894} 1895 1896 1897struct upgrade_data_t { 1898 svn_sqlite__db_t *sdb; 1899 const char *root_abspath; 1900 apr_int64_t repos_id; 1901 apr_int64_t wc_id; 1902}; 1903 1904/* Upgrade the working copy directory represented by DB/DIR_ABSPATH 1905 from OLD_FORMAT to the wc-ng format (SVN_WC__WC_NG_VERSION)'. 1906 1907 Pass REPOS_INFO_FUNC, REPOS_INFO_BATON and REPOS_CACHE to 1908 ensure_repos_info. Add the found repository root and UUID to 1909 REPOS_CACHE if it doesn't have a cached entry for this 1910 repository. 1911 1912 *DATA refers to the single root db. 1913 1914 Uses SCRATCH_POOL for all temporary allocation. */ 1915static svn_error_t * 1916upgrade_to_wcng(void **dir_baton, 1917 void *parent_baton, 1918 svn_wc__db_t *db, 1919 const char *dir_abspath, 1920 int old_format, 1921 apr_int64_t wc_id, 1922 svn_wc_upgrade_get_repos_info_t repos_info_func, 1923 void *repos_info_baton, 1924 apr_hash_t *repos_cache, 1925 const struct upgrade_data_t *data, 1926 apr_pool_t *result_pool, 1927 apr_pool_t *scratch_pool) 1928{ 1929 const char *logfile_path = svn_wc__adm_child(dir_abspath, ADM_LOG, 1930 scratch_pool); 1931 svn_node_kind_t logfile_on_disk_kind; 1932 apr_hash_t *entries; 1933 svn_wc_entry_t *this_dir; 1934 const char *old_wcroot_abspath, *dir_relpath; 1935 apr_hash_t *text_bases_info; 1936 svn_error_t *err; 1937 1938 /* Don't try to mess with the WC if there are old log files left. */ 1939 1940 /* Is the (first) log file present? */ 1941 SVN_ERR(svn_io_check_path(logfile_path, &logfile_on_disk_kind, 1942 scratch_pool)); 1943 if (logfile_on_disk_kind == svn_node_file) 1944 return svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, 1945 _("Cannot upgrade with existing logs; run a " 1946 "cleanup operation on this working copy using " 1947 "a client version which is compatible with this " 1948 "working copy's format (such as the version " 1949 "you are upgrading from), then retry the " 1950 "upgrade with the current version")); 1951 1952 /* Lock this working copy directory, or steal an existing lock. Do this 1953 BEFORE we read the entries. We don't want another process to modify the 1954 entries after we've read them into memory. */ 1955 SVN_ERR(create_physical_lock(dir_abspath, scratch_pool)); 1956 1957 /* What's going on here? 1958 * 1959 * We're attempting to upgrade an older working copy to the new wc-ng format. 1960 * The semantics and storage mechanisms between the two are vastly different, 1961 * so it's going to be a bit painful. Here's a plan for the operation: 1962 * 1963 * 1) Read the old 'entries' using the old-format reader. 1964 * 1965 * 2) Create the new DB if it hasn't already been created. 1966 * 1967 * 3) Use our compatibility code for writing entries to fill out the (new) 1968 * DB state. Use the remembered checksums, since an entry has only the 1969 * MD5 not the SHA1 checksum, and in the case of a revert-base doesn't 1970 * even have that. 1971 * 1972 * 4) Convert wcprop to the wc-ng format 1973 * 1974 * 5) Migrate regular properties to the WC-NG DB. 1975 */ 1976 1977 /***** ENTRIES - READ *****/ 1978 SVN_ERR(svn_wc__read_entries_old(&entries, dir_abspath, 1979 scratch_pool, scratch_pool)); 1980 1981 this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR); 1982 SVN_ERR(ensure_repos_info(this_dir, dir_abspath, 1983 repos_info_func, repos_info_baton, 1984 repos_cache, 1985 scratch_pool, scratch_pool)); 1986 1987 /* Cache repos UUID pairs for when a subdir doesn't have this information */ 1988 if (!svn_hash_gets(repos_cache, this_dir->repos)) 1989 { 1990 apr_pool_t *hash_pool = apr_hash_pool_get(repos_cache); 1991 1992 svn_hash_sets(repos_cache, 1993 apr_pstrdup(hash_pool, this_dir->repos), 1994 apr_pstrdup(hash_pool, this_dir->uuid)); 1995 } 1996 1997 old_wcroot_abspath = svn_dirent_get_longest_ancestor(dir_abspath, 1998 data->root_abspath, 1999 scratch_pool); 2000 dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath, dir_abspath); 2001 2002 /***** TEXT BASES *****/ 2003 SVN_ERR(migrate_text_bases(&text_bases_info, dir_abspath, data->root_abspath, 2004 data->sdb, scratch_pool, scratch_pool)); 2005 2006 /***** ENTRIES - WRITE *****/ 2007 err = svn_wc__write_upgraded_entries(dir_baton, parent_baton, db, data->sdb, 2008 data->repos_id, data->wc_id, 2009 dir_abspath, data->root_abspath, 2010 entries, text_bases_info, 2011 result_pool, scratch_pool); 2012 if (err && err->apr_err == SVN_ERR_WC_CORRUPT) 2013 return svn_error_quick_wrap(err, 2014 _("This working copy is corrupt and " 2015 "cannot be upgraded. Please check out " 2016 "a new working copy.")); 2017 else 2018 SVN_ERR(err); 2019 2020 /***** WC PROPS *****/ 2021 /* If we don't know precisely where the wcprops are, ignore them. */ 2022 if (old_format != SVN_WC__WCPROPS_LOST) 2023 { 2024 apr_hash_t *all_wcprops; 2025 2026 if (old_format <= SVN_WC__WCPROPS_MANY_FILES_VERSION) 2027 SVN_ERR(read_many_wcprops(&all_wcprops, dir_abspath, 2028 scratch_pool, scratch_pool)); 2029 else 2030 SVN_ERR(read_wcprops(&all_wcprops, dir_abspath, 2031 scratch_pool, scratch_pool)); 2032 2033 SVN_ERR(upgrade_apply_dav_cache(data->sdb, dir_relpath, wc_id, 2034 all_wcprops, scratch_pool)); 2035 } 2036 2037 /* Upgrade all the properties (including "this dir"). 2038 2039 Note: this must come AFTER the entries have been migrated into the 2040 database. The upgrade process needs the children in BASE_NODE and 2041 WORKING_NODE, and to examine the resultant WORKING state. */ 2042 SVN_ERR(migrate_props(dir_abspath, data->root_abspath, data->sdb, old_format, 2043 wc_id, scratch_pool)); 2044 2045 return SVN_NO_ERROR; 2046} 2047 2048const char * 2049svn_wc__version_string_from_format(int wc_format) 2050{ 2051 switch (wc_format) 2052 { 2053 case 4: return "<=1.3"; 2054 case 8: return "1.4"; 2055 case 9: return "1.5"; 2056 case 10: return "1.6"; 2057 case SVN_WC__WC_NG_VERSION: return "1.7"; 2058 } 2059 return _("(unreleased development version)"); 2060} 2061 2062svn_error_t * 2063svn_wc__upgrade_sdb(int *result_format, 2064 const char *wcroot_abspath, 2065 svn_sqlite__db_t *sdb, 2066 int start_format, 2067 apr_pool_t *scratch_pool) 2068{ 2069 struct bump_baton bb; 2070 2071 bb.wcroot_abspath = wcroot_abspath; 2072 2073 if (start_format < SVN_WC__WC_NG_VERSION /* 12 */) 2074 return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL, 2075 _("Working copy '%s' is too old (format %d, " 2076 "created by Subversion %s)"), 2077 svn_dirent_local_style(wcroot_abspath, 2078 scratch_pool), 2079 start_format, 2080 svn_wc__version_string_from_format(start_format)); 2081 2082 /* Early WCNG formats no longer supported. */ 2083 if (start_format < 19) 2084 return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL, 2085 _("Working copy '%s' is an old development " 2086 "version (format %d); to upgrade it, " 2087 "use a format 18 client, then " 2088 "use 'tools/dev/wc-ng/bump-to-19.py', then " 2089 "use the current client"), 2090 svn_dirent_local_style(wcroot_abspath, 2091 scratch_pool), 2092 start_format); 2093 2094 /* ### need lock-out. only one upgrade at a time. note that other code 2095 ### cannot use this un-upgraded database until we finish the upgrade. */ 2096 2097 /* Note: none of these have "break" statements; the fall-through is 2098 intentional. */ 2099 switch (start_format) 2100 { 2101 case 19: 2102 SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_20, &bb, 2103 scratch_pool)); 2104 *result_format = 20; 2105 /* FALLTHROUGH */ 2106 2107 case 20: 2108 SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_21, &bb, 2109 scratch_pool)); 2110 *result_format = 21; 2111 /* FALLTHROUGH */ 2112 2113 case 21: 2114 SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_22, &bb, 2115 scratch_pool)); 2116 *result_format = 22; 2117 /* FALLTHROUGH */ 2118 2119 case 22: 2120 SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_23, &bb, 2121 scratch_pool)); 2122 *result_format = 23; 2123 /* FALLTHROUGH */ 2124 2125 case 23: 2126 SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_24, &bb, 2127 scratch_pool)); 2128 *result_format = 24; 2129 /* FALLTHROUGH */ 2130 2131 case 24: 2132 SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_25, &bb, 2133 scratch_pool)); 2134 *result_format = 25; 2135 /* FALLTHROUGH */ 2136 2137 case 25: 2138 SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_26, &bb, 2139 scratch_pool)); 2140 *result_format = 26; 2141 /* FALLTHROUGH */ 2142 2143 case 26: 2144 SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_27, &bb, 2145 scratch_pool)); 2146 *result_format = 27; 2147 /* FALLTHROUGH */ 2148 2149 case 27: 2150 SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_28, &bb, 2151 scratch_pool)); 2152 *result_format = 28; 2153 /* FALLTHROUGH */ 2154 2155 case 28: 2156 SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_29, &bb, 2157 scratch_pool)); 2158 *result_format = 29; 2159 /* FALLTHROUGH */ 2160 2161 case 29: 2162 SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_30, &bb, 2163 scratch_pool)); 2164 *result_format = 30; 2165 2166 case 30: 2167 SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_31, &bb, 2168 scratch_pool)); 2169 *result_format = 31; 2170 /* FALLTHROUGH */ 2171 /* ### future bumps go here. */ 2172#if 0 2173 case XXX-1: 2174 /* Revamp the recording of tree conflicts. */ 2175 SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_XXX, &bb, 2176 scratch_pool)); 2177 *result_format = XXX; 2178 /* FALLTHROUGH */ 2179#endif 2180 case SVN_WC__VERSION: 2181 /* already upgraded */ 2182 *result_format = SVN_WC__VERSION; 2183 2184 SVN_SQLITE__WITH_LOCK( 2185 svn_wc__db_install_schema_statistics(sdb, scratch_pool), 2186 sdb); 2187 } 2188 2189#ifdef SVN_DEBUG 2190 if (*result_format != start_format) 2191 { 2192 int schema_version; 2193 SVN_ERR(svn_sqlite__read_schema_version(&schema_version, sdb, scratch_pool)); 2194 2195 /* If this assertion fails the schema isn't updated correctly */ 2196 SVN_ERR_ASSERT(schema_version == *result_format); 2197 } 2198#endif 2199 2200 /* Zap anything that might be remaining or escaped our notice. */ 2201 wipe_obsolete_files(wcroot_abspath, scratch_pool); 2202 2203 return SVN_NO_ERROR; 2204} 2205 2206 2207/* */ 2208static svn_error_t * 2209upgrade_working_copy(void *parent_baton, 2210 svn_wc__db_t *db, 2211 const char *dir_abspath, 2212 svn_wc_upgrade_get_repos_info_t repos_info_func, 2213 void *repos_info_baton, 2214 apr_hash_t *repos_cache, 2215 const struct upgrade_data_t *data, 2216 svn_cancel_func_t cancel_func, 2217 void *cancel_baton, 2218 svn_wc_notify_func2_t notify_func, 2219 void *notify_baton, 2220 apr_pool_t *result_pool, 2221 apr_pool_t *scratch_pool) 2222{ 2223 void *dir_baton; 2224 int old_format; 2225 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 2226 apr_array_header_t *subdirs; 2227 svn_error_t *err; 2228 int i; 2229 2230 if (cancel_func) 2231 SVN_ERR(cancel_func(cancel_baton)); 2232 2233 SVN_ERR(svn_wc__db_temp_get_format(&old_format, db, dir_abspath, 2234 iterpool)); 2235 2236 if (old_format >= SVN_WC__WC_NG_VERSION) 2237 { 2238 if (notify_func) 2239 notify_func(notify_baton, 2240 svn_wc_create_notify(dir_abspath, svn_wc_notify_skip, 2241 iterpool), 2242 iterpool); 2243 svn_pool_destroy(iterpool); 2244 return SVN_NO_ERROR; 2245 } 2246 2247 err = get_versioned_subdirs(&subdirs, NULL, dir_abspath, FALSE, 2248 scratch_pool, iterpool); 2249 if (err) 2250 { 2251 if (APR_STATUS_IS_ENOENT(err->apr_err) 2252 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)) 2253 { 2254 /* An unversioned dir is obstructing a versioned dir */ 2255 svn_error_clear(err); 2256 err = NULL; 2257 if (notify_func) 2258 notify_func(notify_baton, 2259 svn_wc_create_notify(dir_abspath, svn_wc_notify_skip, 2260 iterpool), 2261 iterpool); 2262 } 2263 svn_pool_destroy(iterpool); 2264 return err; 2265 } 2266 2267 2268 SVN_ERR(upgrade_to_wcng(&dir_baton, parent_baton, db, dir_abspath, 2269 old_format, data->wc_id, 2270 repos_info_func, repos_info_baton, 2271 repos_cache, data, scratch_pool, iterpool)); 2272 2273 if (notify_func) 2274 notify_func(notify_baton, 2275 svn_wc_create_notify(dir_abspath, svn_wc_notify_upgraded_path, 2276 iterpool), 2277 iterpool); 2278 2279 for (i = 0; i < subdirs->nelts; ++i) 2280 { 2281 const char *child_abspath = APR_ARRAY_IDX(subdirs, i, const char *); 2282 2283 svn_pool_clear(iterpool); 2284 2285 SVN_ERR(upgrade_working_copy(dir_baton, db, child_abspath, 2286 repos_info_func, repos_info_baton, 2287 repos_cache, data, 2288 cancel_func, cancel_baton, 2289 notify_func, notify_baton, 2290 iterpool, iterpool)); 2291 } 2292 2293 svn_pool_destroy(iterpool); 2294 2295 return SVN_NO_ERROR; 2296} 2297 2298 2299/* Return a verbose error if LOCAL_ABSPATH is a not a pre-1.7 working 2300 copy root */ 2301static svn_error_t * 2302is_old_wcroot(const char *local_abspath, 2303 apr_pool_t *scratch_pool) 2304{ 2305 apr_hash_t *entries; 2306 const char *parent_abspath, *name; 2307 svn_wc_entry_t *entry; 2308 svn_error_t *err = svn_wc__read_entries_old(&entries, local_abspath, 2309 scratch_pool, scratch_pool); 2310 if (err) 2311 { 2312 return svn_error_createf( 2313 SVN_ERR_WC_INVALID_OP_ON_CWD, err, 2314 _("Can't upgrade '%s' as it is not a working copy"), 2315 svn_dirent_local_style(local_abspath, scratch_pool)); 2316 } 2317 else if (svn_dirent_is_root(local_abspath, strlen(local_abspath))) 2318 return SVN_NO_ERROR; 2319 2320 svn_dirent_split(&parent_abspath, &name, local_abspath, scratch_pool); 2321 2322 err = svn_wc__read_entries_old(&entries, parent_abspath, 2323 scratch_pool, scratch_pool); 2324 if (err) 2325 { 2326 svn_error_clear(err); 2327 return SVN_NO_ERROR; 2328 } 2329 2330 entry = svn_hash_gets(entries, name); 2331 if (!entry 2332 || entry->absent 2333 || (entry->deleted && entry->schedule != svn_wc_schedule_add) 2334 || entry->depth == svn_depth_exclude) 2335 { 2336 return SVN_NO_ERROR; 2337 } 2338 2339 while (!svn_dirent_is_root(parent_abspath, strlen(parent_abspath))) 2340 { 2341 svn_dirent_split(&parent_abspath, &name, parent_abspath, scratch_pool); 2342 err = svn_wc__read_entries_old(&entries, parent_abspath, 2343 scratch_pool, scratch_pool); 2344 if (err) 2345 { 2346 svn_error_clear(err); 2347 parent_abspath = svn_dirent_join(parent_abspath, name, scratch_pool); 2348 break; 2349 } 2350 entry = svn_hash_gets(entries, name); 2351 if (!entry 2352 || entry->absent 2353 || (entry->deleted && entry->schedule != svn_wc_schedule_add) 2354 || entry->depth == svn_depth_exclude) 2355 { 2356 parent_abspath = svn_dirent_join(parent_abspath, name, scratch_pool); 2357 break; 2358 } 2359 } 2360 2361 return svn_error_createf( 2362 SVN_ERR_WC_INVALID_OP_ON_CWD, NULL, 2363 _("Can't upgrade '%s' as it is not a working copy root," 2364 " the root is '%s'"), 2365 svn_dirent_local_style(local_abspath, scratch_pool), 2366 svn_dirent_local_style(parent_abspath, scratch_pool)); 2367} 2368 2369svn_error_t * 2370svn_wc_upgrade(svn_wc_context_t *wc_ctx, 2371 const char *local_abspath, 2372 svn_wc_upgrade_get_repos_info_t repos_info_func, 2373 void *repos_info_baton, 2374 svn_cancel_func_t cancel_func, 2375 void *cancel_baton, 2376 svn_wc_notify_func2_t notify_func, 2377 void *notify_baton, 2378 apr_pool_t *scratch_pool) 2379{ 2380 svn_wc__db_t *db; 2381 struct upgrade_data_t data = { NULL }; 2382 svn_skel_t *work_item, *work_items = NULL; 2383 const char *pristine_from, *pristine_to, *db_from, *db_to; 2384 apr_hash_t *repos_cache = apr_hash_make(scratch_pool); 2385 svn_wc_entry_t *this_dir; 2386 apr_hash_t *entries; 2387 const char *root_adm_abspath; 2388 svn_error_t *err; 2389 int result_format; 2390 svn_boolean_t bumped_format; 2391 2392 /* Try upgrading a wc-ng-style working copy. */ 2393 SVN_ERR(svn_wc__db_open(&db, NULL /* ### config */, TRUE, FALSE, 2394 scratch_pool, scratch_pool)); 2395 2396 2397 err = svn_wc__db_bump_format(&result_format, &bumped_format, 2398 db, local_abspath, 2399 scratch_pool); 2400 if (err) 2401 { 2402 if (err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED) 2403 { 2404 return svn_error_trace( 2405 svn_error_compose_create( 2406 err, 2407 svn_wc__db_close(db))); 2408 } 2409 2410 svn_error_clear(err); 2411 /* Pre 1.7: Fall through */ 2412 } 2413 else 2414 { 2415 /* Auto-upgrade worked! */ 2416 SVN_ERR(svn_wc__db_close(db)); 2417 2418 SVN_ERR_ASSERT(result_format == SVN_WC__VERSION); 2419 2420 if (bumped_format && notify_func) 2421 { 2422 svn_wc_notify_t *notify; 2423 2424 notify = svn_wc_create_notify(local_abspath, 2425 svn_wc_notify_upgraded_path, 2426 scratch_pool); 2427 2428 notify_func(notify_baton, notify, scratch_pool); 2429 } 2430 2431 return SVN_NO_ERROR; 2432 } 2433 2434 SVN_ERR(is_old_wcroot(local_abspath, scratch_pool)); 2435 2436 /* Given a pre-wcng root some/wc we create a temporary wcng in 2437 some/wc/.svn/tmp/wcng/wc.db and copy the metadata from one to the 2438 other, then the temporary wc.db file gets moved into the original 2439 root. Until the wc.db file is moved the original working copy 2440 remains a pre-wcng and 'cleanup' with an old client will remove 2441 the partial upgrade. Moving the wc.db file creates a wcng, and 2442 'cleanup' with a new client will complete any outstanding 2443 upgrade. */ 2444 2445 SVN_ERR(svn_wc__read_entries_old(&entries, local_abspath, 2446 scratch_pool, scratch_pool)); 2447 2448 this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR); 2449 SVN_ERR(ensure_repos_info(this_dir, local_abspath, repos_info_func, 2450 repos_info_baton, repos_cache, 2451 scratch_pool, scratch_pool)); 2452 2453 /* Cache repos UUID pairs for when a subdir doesn't have this information */ 2454 if (!svn_hash_gets(repos_cache, this_dir->repos)) 2455 svn_hash_sets(repos_cache, 2456 apr_pstrdup(scratch_pool, this_dir->repos), 2457 apr_pstrdup(scratch_pool, this_dir->uuid)); 2458 2459 /* Create the new DB in the temporary root wc/.svn/tmp/wcng/.svn */ 2460 data.root_abspath = svn_dirent_join(svn_wc__adm_child(local_abspath, "tmp", 2461 scratch_pool), 2462 "wcng", scratch_pool); 2463 root_adm_abspath = svn_wc__adm_child(data.root_abspath, "", 2464 scratch_pool); 2465 SVN_ERR(svn_io_remove_dir2(root_adm_abspath, TRUE, NULL, NULL, 2466 scratch_pool)); 2467 SVN_ERR(svn_wc__ensure_directory(root_adm_abspath, scratch_pool)); 2468 2469 /* Create an empty sqlite database for this directory and store it in DB. */ 2470 SVN_ERR(svn_wc__db_upgrade_begin(&data.sdb, 2471 &data.repos_id, &data.wc_id, 2472 db, data.root_abspath, 2473 this_dir->repos, this_dir->uuid, 2474 scratch_pool)); 2475 2476 /* Migrate the entries over to the new database. 2477 ### We need to think about atomicity here. 2478 2479 entries_write_new() writes in current format rather than 2480 f12. Thus, this function bumps a working copy all the way to 2481 current. */ 2482 SVN_ERR(svn_wc__db_wclock_obtain(db, data.root_abspath, 0, FALSE, 2483 scratch_pool)); 2484 2485 SVN_SQLITE__WITH_LOCK( 2486 upgrade_working_copy(NULL, db, local_abspath, 2487 repos_info_func, repos_info_baton, 2488 repos_cache, &data, 2489 cancel_func, cancel_baton, 2490 notify_func, notify_baton, 2491 scratch_pool, scratch_pool), 2492 data.sdb); 2493 2494 /* A workqueue item to move the pristine dir into place */ 2495 pristine_from = svn_wc__adm_child(data.root_abspath, PRISTINE_STORAGE_RELPATH, 2496 scratch_pool); 2497 pristine_to = svn_wc__adm_child(local_abspath, PRISTINE_STORAGE_RELPATH, 2498 scratch_pool); 2499 SVN_ERR(svn_wc__ensure_directory(pristine_from, scratch_pool)); 2500 SVN_ERR(svn_wc__wq_build_file_move(&work_item, db, local_abspath, 2501 pristine_from, pristine_to, 2502 scratch_pool, scratch_pool)); 2503 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 2504 2505 /* A workqueue item to remove pre-wcng metadata */ 2506 SVN_ERR(svn_wc__wq_build_postupgrade(&work_item, scratch_pool)); 2507 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 2508 SVN_ERR(svn_wc__db_wq_add(db, data.root_abspath, work_items, scratch_pool)); 2509 2510 SVN_ERR(svn_wc__db_wclock_release(db, data.root_abspath, scratch_pool)); 2511 SVN_ERR(svn_wc__db_close(db)); 2512 2513 /* Renaming the db file is what makes the pre-wcng into a wcng */ 2514 db_from = svn_wc__adm_child(data.root_abspath, SDB_FILE, scratch_pool); 2515 db_to = svn_wc__adm_child(local_abspath, SDB_FILE, scratch_pool); 2516 SVN_ERR(svn_io_file_rename(db_from, db_to, scratch_pool)); 2517 2518 /* Now we have a working wcng, tidy up the droppings */ 2519 SVN_ERR(svn_wc__db_open(&db, NULL /* ### config */, FALSE, FALSE, 2520 scratch_pool, scratch_pool)); 2521 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, 2522 scratch_pool)); 2523 SVN_ERR(svn_wc__db_close(db)); 2524 2525 /* Should we have the workqueue remove this empty dir? */ 2526 SVN_ERR(svn_io_remove_dir2(data.root_abspath, FALSE, NULL, NULL, 2527 scratch_pool)); 2528 2529 return SVN_NO_ERROR; 2530} 2531 2532svn_error_t * 2533svn_wc__upgrade_add_external_info(svn_wc_context_t *wc_ctx, 2534 const char *local_abspath, 2535 svn_node_kind_t kind, 2536 const char *def_local_abspath, 2537 const char *repos_relpath, 2538 const char *repos_root_url, 2539 const char *repos_uuid, 2540 svn_revnum_t def_peg_revision, 2541 svn_revnum_t def_revision, 2542 apr_pool_t *scratch_pool) 2543{ 2544 svn_node_kind_t db_kind; 2545 switch (kind) 2546 { 2547 case svn_node_dir: 2548 db_kind = svn_node_dir; 2549 break; 2550 2551 case svn_node_file: 2552 db_kind = svn_node_file; 2553 break; 2554 2555 case svn_node_unknown: 2556 db_kind = svn_node_unknown; 2557 break; 2558 2559 default: 2560 SVN_ERR_MALFUNCTION(); 2561 } 2562 2563 SVN_ERR(svn_wc__db_upgrade_insert_external(wc_ctx->db, local_abspath, 2564 db_kind, 2565 svn_dirent_dirname(local_abspath, 2566 scratch_pool), 2567 def_local_abspath, repos_relpath, 2568 repos_root_url, repos_uuid, 2569 def_peg_revision, def_revision, 2570 scratch_pool)); 2571 return SVN_NO_ERROR; 2572} 2573