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