1251881Speter/* 2251881Speter * upgrade.c: routines for upgrading a working copy 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter#include <apr_pools.h> 25251881Speter 26251881Speter#include "svn_types.h" 27251881Speter#include "svn_pools.h" 28251881Speter#include "svn_dirent_uri.h" 29251881Speter#include "svn_path.h" 30251881Speter#include "svn_hash.h" 31251881Speter 32251881Speter#include "wc.h" 33251881Speter#include "adm_files.h" 34251881Speter#include "conflicts.h" 35251881Speter#include "entries.h" 36251881Speter#include "wc_db.h" 37251881Speter#include "tree_conflicts.h" 38251881Speter#include "wc-queries.h" /* for STMT_* */ 39251881Speter#include "workqueue.h" 40251881Speter 41251881Speter#include "svn_private_config.h" 42251881Speter#include "private/svn_wc_private.h" 43251881Speter#include "private/svn_sqlite.h" 44251881Speter#include "private/svn_token.h" 45251881Speter 46251881Speter/* WC-1.0 administrative area extensions */ 47251881Speter#define SVN_WC__BASE_EXT ".svn-base" /* for text and prop bases */ 48251881Speter#define SVN_WC__WORK_EXT ".svn-work" /* for working propfiles */ 49251881Speter#define SVN_WC__REVERT_EXT ".svn-revert" /* for reverting a replaced 50251881Speter file */ 51251881Speter 52251881Speter/* Old locations for storing "wcprops" (aka "dav cache"). */ 53251881Speter#define WCPROPS_SUBDIR_FOR_FILES "wcprops" 54251881Speter#define WCPROPS_FNAME_FOR_DIR "dir-wcprops" 55251881Speter#define WCPROPS_ALL_DATA "all-wcprops" 56251881Speter 57251881Speter/* Old property locations. */ 58251881Speter#define PROPS_SUBDIR "props" 59251881Speter#define PROP_BASE_SUBDIR "prop-base" 60251881Speter#define PROP_BASE_FOR_DIR "dir-prop-base" 61251881Speter#define PROP_REVERT_FOR_DIR "dir-prop-revert" 62251881Speter#define PROP_WORKING_FOR_DIR "dir-props" 63251881Speter 64251881Speter/* Old textbase location. */ 65251881Speter#define TEXT_BASE_SUBDIR "text-base" 66251881Speter 67251881Speter#define TEMP_DIR "tmp" 68251881Speter 69251881Speter/* Old data files that we no longer need/use. */ 70251881Speter#define ADM_README "README.txt" 71251881Speter#define ADM_EMPTY_FILE "empty-file" 72251881Speter#define ADM_LOG "log" 73251881Speter#define ADM_LOCK "lock" 74251881Speter 75251881Speter/* New pristine location */ 76251881Speter#define PRISTINE_STORAGE_RELPATH "pristine" 77251881Speter#define PRISTINE_STORAGE_EXT ".svn-base" 78251881Speter/* Number of characters in a pristine file basename, in WC format <= 28. */ 79251881Speter#define PRISTINE_BASENAME_OLD_LEN 40 80251881Speter#define SDB_FILE "wc.db" 81251881Speter 82251881Speter 83251881Speter/* Read the properties from the file at PROPFILE_ABSPATH, returning them 84251881Speter as a hash in *PROPS. If the propfile is NOT present, then NULL will 85251881Speter be returned in *PROPS. */ 86251881Speterstatic svn_error_t * 87251881Speterread_propfile(apr_hash_t **props, 88251881Speter const char *propfile_abspath, 89251881Speter apr_pool_t *result_pool, 90251881Speter apr_pool_t *scratch_pool) 91251881Speter{ 92251881Speter svn_error_t *err; 93251881Speter svn_stream_t *stream; 94251881Speter apr_finfo_t finfo; 95251881Speter 96251881Speter err = svn_io_stat(&finfo, propfile_abspath, APR_FINFO_SIZE, scratch_pool); 97251881Speter 98251881Speter if (err 99251881Speter && (APR_STATUS_IS_ENOENT(err->apr_err) 100251881Speter || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) 101251881Speter { 102251881Speter svn_error_clear(err); 103251881Speter 104251881Speter /* The propfile was not there. Signal with a NULL. */ 105251881Speter *props = NULL; 106251881Speter return SVN_NO_ERROR; 107251881Speter } 108251881Speter else 109251881Speter SVN_ERR(err); 110251881Speter 111251881Speter /* A 0-bytes file signals an empty property list. 112251881Speter (mostly used for revert-props) */ 113251881Speter if (finfo.size == 0) 114251881Speter { 115251881Speter *props = apr_hash_make(result_pool); 116251881Speter return SVN_NO_ERROR; 117251881Speter } 118251881Speter 119251881Speter SVN_ERR(svn_stream_open_readonly(&stream, propfile_abspath, 120251881Speter scratch_pool, scratch_pool)); 121251881Speter 122251881Speter /* ### does this function need to be smarter? will we see zero-length 123251881Speter ### files? see props.c::load_props(). there may be more work here. 124251881Speter ### need a historic analysis of 1.x property storage. what will we 125251881Speter ### actually run into? */ 126251881Speter 127251881Speter /* ### loggy_write_properties() and immediate_install_props() write 128251881Speter ### zero-length files for "no props", so we should be a bit smarter 129251881Speter ### in here. */ 130251881Speter 131251881Speter /* ### should we be forgiving in here? I say "no". if we can't be sure, 132251881Speter ### then we could effectively corrupt the local working copy. */ 133251881Speter 134251881Speter *props = apr_hash_make(result_pool); 135251881Speter SVN_ERR(svn_hash_read2(*props, stream, SVN_HASH_TERMINATOR, result_pool)); 136251881Speter 137251881Speter return svn_error_trace(svn_stream_close(stream)); 138251881Speter} 139251881Speter 140251881Speter 141251881Speter/* Read one proplist (allocated from RESULT_POOL) from STREAM, and place it 142251881Speter into ALL_WCPROPS at NAME. */ 143251881Speterstatic svn_error_t * 144251881Speterread_one_proplist(apr_hash_t *all_wcprops, 145251881Speter const char *name, 146251881Speter svn_stream_t *stream, 147251881Speter apr_pool_t *result_pool, 148251881Speter apr_pool_t *scratch_pool) 149251881Speter{ 150251881Speter apr_hash_t *proplist; 151251881Speter 152251881Speter proplist = apr_hash_make(result_pool); 153251881Speter SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, result_pool)); 154251881Speter svn_hash_sets(all_wcprops, name, proplist); 155251881Speter 156251881Speter return SVN_NO_ERROR; 157251881Speter} 158251881Speter 159251881Speter 160251881Speter/* Read the wcprops from all the files in the admin area of DIR_ABSPATH, 161251881Speter returning them in *ALL_WCPROPS. Results are allocated in RESULT_POOL, 162251881Speter and temporary allocations are performed in SCRATCH_POOL. */ 163251881Speterstatic svn_error_t * 164251881Speterread_many_wcprops(apr_hash_t **all_wcprops, 165251881Speter const char *dir_abspath, 166251881Speter apr_pool_t *result_pool, 167251881Speter apr_pool_t *scratch_pool) 168251881Speter{ 169251881Speter const char *propfile_abspath; 170251881Speter apr_hash_t *wcprops; 171251881Speter apr_hash_t *dirents; 172251881Speter const char *props_dir_abspath; 173251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 174251881Speter apr_hash_index_t *hi; 175251881Speter 176251881Speter *all_wcprops = apr_hash_make(result_pool); 177251881Speter 178251881Speter /* First, look at dir-wcprops. */ 179251881Speter propfile_abspath = svn_wc__adm_child(dir_abspath, WCPROPS_FNAME_FOR_DIR, 180251881Speter scratch_pool); 181251881Speter SVN_ERR(read_propfile(&wcprops, propfile_abspath, result_pool, iterpool)); 182251881Speter if (wcprops != NULL) 183251881Speter svn_hash_sets(*all_wcprops, SVN_WC_ENTRY_THIS_DIR, wcprops); 184251881Speter 185251881Speter props_dir_abspath = svn_wc__adm_child(dir_abspath, WCPROPS_SUBDIR_FOR_FILES, 186251881Speter scratch_pool); 187251881Speter 188251881Speter /* Now walk the wcprops directory. */ 189251881Speter SVN_ERR(svn_io_get_dirents3(&dirents, props_dir_abspath, TRUE, 190251881Speter scratch_pool, scratch_pool)); 191251881Speter 192251881Speter for (hi = apr_hash_first(scratch_pool, dirents); 193251881Speter hi; 194251881Speter hi = apr_hash_next(hi)) 195251881Speter { 196251881Speter const char *name = svn__apr_hash_index_key(hi); 197251881Speter 198251881Speter svn_pool_clear(iterpool); 199251881Speter 200251881Speter propfile_abspath = svn_dirent_join(props_dir_abspath, name, iterpool); 201251881Speter 202251881Speter SVN_ERR(read_propfile(&wcprops, propfile_abspath, 203251881Speter result_pool, iterpool)); 204251881Speter SVN_ERR_ASSERT(wcprops != NULL); 205251881Speter svn_hash_sets(*all_wcprops, apr_pstrdup(result_pool, name), wcprops); 206251881Speter } 207251881Speter 208251881Speter svn_pool_destroy(iterpool); 209251881Speter return SVN_NO_ERROR; 210251881Speter} 211251881Speter 212251881Speter 213251881Speter/* For wcprops stored in a single file in this working copy, read that 214251881Speter file and return it in *ALL_WCPROPS, allocated in RESULT_POOL. Use 215251881Speter SCRATCH_POOL for temporary allocations. */ 216251881Speterstatic svn_error_t * 217251881Speterread_wcprops(apr_hash_t **all_wcprops, 218251881Speter const char *dir_abspath, 219251881Speter apr_pool_t *result_pool, 220251881Speter apr_pool_t *scratch_pool) 221251881Speter{ 222251881Speter svn_stream_t *stream; 223251881Speter svn_error_t *err; 224251881Speter 225251881Speter *all_wcprops = apr_hash_make(result_pool); 226251881Speter 227251881Speter err = svn_wc__open_adm_stream(&stream, dir_abspath, 228251881Speter WCPROPS_ALL_DATA, 229251881Speter scratch_pool, scratch_pool); 230251881Speter 231251881Speter /* A non-existent file means there are no props. */ 232251881Speter if (err && APR_STATUS_IS_ENOENT(err->apr_err)) 233251881Speter { 234251881Speter svn_error_clear(err); 235251881Speter return SVN_NO_ERROR; 236251881Speter } 237251881Speter SVN_ERR(err); 238251881Speter 239251881Speter /* Read the proplist for THIS_DIR. */ 240251881Speter SVN_ERR(read_one_proplist(*all_wcprops, SVN_WC_ENTRY_THIS_DIR, stream, 241251881Speter result_pool, scratch_pool)); 242251881Speter 243251881Speter /* And now, the children. */ 244251881Speter while (1729) 245251881Speter { 246251881Speter svn_stringbuf_t *line; 247251881Speter svn_boolean_t eof; 248251881Speter 249251881Speter SVN_ERR(svn_stream_readline(stream, &line, "\n", &eof, result_pool)); 250251881Speter if (eof) 251251881Speter { 252251881Speter if (line->len > 0) 253251881Speter return svn_error_createf 254251881Speter (SVN_ERR_WC_CORRUPT, NULL, 255251881Speter _("Missing end of line in wcprops file for '%s'"), 256251881Speter svn_dirent_local_style(dir_abspath, scratch_pool)); 257251881Speter break; 258251881Speter } 259251881Speter SVN_ERR(read_one_proplist(*all_wcprops, line->data, stream, 260251881Speter result_pool, scratch_pool)); 261251881Speter } 262251881Speter 263251881Speter return svn_error_trace(svn_stream_close(stream)); 264251881Speter} 265251881Speter 266251881Speter/* Return in CHILDREN, the list of all 1.6 versioned subdirectories 267251881Speter which also exist on disk as directories. 268251881Speter 269251881Speter If DELETE_DIR is not NULL set *DELETE_DIR to TRUE if the directory 270251881Speter should be deleted after migrating to WC-NG, otherwise to FALSE. 271251881Speter 272251881Speter If SKIP_MISSING is TRUE, don't add missing or obstructed subdirectories 273251881Speter to the list of children. 274251881Speter */ 275251881Speterstatic svn_error_t * 276251881Speterget_versioned_subdirs(apr_array_header_t **children, 277251881Speter svn_boolean_t *delete_dir, 278251881Speter const char *dir_abspath, 279251881Speter svn_boolean_t skip_missing, 280251881Speter apr_pool_t *result_pool, 281251881Speter apr_pool_t *scratch_pool) 282251881Speter{ 283251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 284251881Speter apr_hash_t *entries; 285251881Speter apr_hash_index_t *hi; 286251881Speter svn_wc_entry_t *this_dir = NULL; 287251881Speter 288251881Speter *children = apr_array_make(result_pool, 10, sizeof(const char *)); 289251881Speter 290251881Speter SVN_ERR(svn_wc__read_entries_old(&entries, dir_abspath, 291251881Speter scratch_pool, iterpool)); 292251881Speter for (hi = apr_hash_first(scratch_pool, entries); 293251881Speter hi; 294251881Speter hi = apr_hash_next(hi)) 295251881Speter { 296251881Speter const char *name = svn__apr_hash_index_key(hi); 297251881Speter const svn_wc_entry_t *entry = svn__apr_hash_index_val(hi); 298251881Speter const char *child_abspath; 299251881Speter svn_boolean_t hidden; 300251881Speter 301251881Speter /* skip "this dir" */ 302251881Speter if (*name == '\0') 303251881Speter { 304251881Speter this_dir = svn__apr_hash_index_val(hi); 305251881Speter continue; 306251881Speter } 307251881Speter else if (entry->kind != svn_node_dir) 308251881Speter continue; 309251881Speter 310251881Speter svn_pool_clear(iterpool); 311251881Speter 312251881Speter /* If a directory is 'hidden' skip it as subdir */ 313251881Speter SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry)); 314251881Speter if (hidden) 315251881Speter continue; 316251881Speter 317251881Speter child_abspath = svn_dirent_join(dir_abspath, name, scratch_pool); 318251881Speter 319251881Speter if (skip_missing) 320251881Speter { 321251881Speter svn_node_kind_t kind; 322251881Speter SVN_ERR(svn_io_check_path(child_abspath, &kind, scratch_pool)); 323251881Speter 324251881Speter if (kind != svn_node_dir) 325251881Speter continue; 326251881Speter } 327251881Speter 328251881Speter APR_ARRAY_PUSH(*children, const char *) = apr_pstrdup(result_pool, 329251881Speter child_abspath); 330251881Speter } 331251881Speter 332251881Speter svn_pool_destroy(iterpool); 333251881Speter 334251881Speter if (delete_dir != NULL) 335251881Speter { 336251881Speter *delete_dir = (this_dir != NULL) 337251881Speter && (this_dir->schedule == svn_wc_schedule_delete) 338251881Speter && ! this_dir->keep_local; 339251881Speter } 340251881Speter 341251881Speter return SVN_NO_ERROR; 342251881Speter} 343251881Speter 344251881Speter 345251881Speter/* Return in CHILDREN the names of all versioned *files* in SDB that 346251881Speter are children of PARENT_RELPATH. These files' existence on disk is 347251881Speter not tested. 348251881Speter 349251881Speter This set of children is intended for property upgrades. 350251881Speter Subdirectory's properties exist in the subdirs. 351251881Speter 352251881Speter Note that this uses just the SDB to locate children, which means 353251881Speter that the children must have been upgraded to wc-ng format. */ 354251881Speterstatic svn_error_t * 355251881Speterget_versioned_files(const apr_array_header_t **children, 356251881Speter const char *parent_relpath, 357251881Speter svn_sqlite__db_t *sdb, 358251881Speter apr_int64_t wc_id, 359251881Speter apr_pool_t *result_pool, 360251881Speter apr_pool_t *scratch_pool) 361251881Speter{ 362251881Speter svn_sqlite__stmt_t *stmt; 363251881Speter apr_array_header_t *child_names; 364251881Speter svn_boolean_t have_row; 365251881Speter 366251881Speter /* ### just select 'file' children. do we need 'symlink' in the future? */ 367251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_ALL_FILES)); 368251881Speter SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, parent_relpath)); 369251881Speter 370251881Speter /* ### 10 is based on Subversion's average of 8.5 files per versioned 371251881Speter ### directory in its repository. maybe use a different value? or 372251881Speter ### count rows first? */ 373251881Speter child_names = apr_array_make(result_pool, 10, sizeof(const char *)); 374251881Speter 375251881Speter SVN_ERR(svn_sqlite__step(&have_row, stmt)); 376251881Speter while (have_row) 377251881Speter { 378251881Speter const char *local_relpath = svn_sqlite__column_text(stmt, 0, 379251881Speter result_pool); 380251881Speter 381251881Speter APR_ARRAY_PUSH(child_names, const char *) 382251881Speter = svn_relpath_basename(local_relpath, result_pool); 383251881Speter 384251881Speter SVN_ERR(svn_sqlite__step(&have_row, stmt)); 385251881Speter } 386251881Speter 387251881Speter *children = child_names; 388251881Speter 389251881Speter return svn_error_trace(svn_sqlite__reset(stmt)); 390251881Speter} 391251881Speter 392251881Speter 393251881Speter/* Return the path of the old-school administrative lock file 394251881Speter associated with LOCAL_DIR_ABSPATH, allocated from RESULT_POOL. */ 395251881Speterstatic const char * 396251881Speterbuild_lockfile_path(const char *local_dir_abspath, 397251881Speter apr_pool_t *result_pool) 398251881Speter{ 399251881Speter return svn_dirent_join_many(result_pool, 400251881Speter local_dir_abspath, 401251881Speter svn_wc_get_adm_dir(result_pool), 402251881Speter ADM_LOCK, 403251881Speter NULL); 404251881Speter} 405251881Speter 406251881Speter 407251881Speter/* Create a physical lock file in the admin directory for ABSPATH. */ 408251881Speterstatic svn_error_t * 409251881Spetercreate_physical_lock(const char *abspath, apr_pool_t *scratch_pool) 410251881Speter{ 411251881Speter const char *lock_abspath = build_lockfile_path(abspath, scratch_pool); 412251881Speter svn_error_t *err; 413251881Speter apr_file_t *file; 414251881Speter 415251881Speter err = svn_io_file_open(&file, lock_abspath, 416251881Speter APR_WRITE | APR_CREATE | APR_EXCL, 417251881Speter APR_OS_DEFAULT, 418251881Speter scratch_pool); 419251881Speter 420251881Speter if (err && APR_STATUS_IS_EEXIST(err->apr_err)) 421251881Speter { 422251881Speter /* Congratulations, we just stole a physical lock from somebody */ 423251881Speter svn_error_clear(err); 424251881Speter return SVN_NO_ERROR; 425251881Speter } 426251881Speter 427251881Speter return svn_error_trace(err); 428251881Speter} 429251881Speter 430251881Speter 431251881Speter/* Wipe out all the obsolete files/dirs from the administrative area. */ 432251881Speterstatic void 433251881Speterwipe_obsolete_files(const char *wcroot_abspath, apr_pool_t *scratch_pool) 434251881Speter{ 435251881Speter /* Zap unused files. */ 436251881Speter svn_error_clear(svn_io_remove_file2( 437251881Speter svn_wc__adm_child(wcroot_abspath, 438251881Speter SVN_WC__ADM_FORMAT, 439251881Speter scratch_pool), 440251881Speter TRUE, scratch_pool)); 441251881Speter svn_error_clear(svn_io_remove_file2( 442251881Speter svn_wc__adm_child(wcroot_abspath, 443251881Speter SVN_WC__ADM_ENTRIES, 444251881Speter scratch_pool), 445251881Speter TRUE, scratch_pool)); 446251881Speter svn_error_clear(svn_io_remove_file2( 447251881Speter svn_wc__adm_child(wcroot_abspath, 448251881Speter ADM_EMPTY_FILE, 449251881Speter scratch_pool), 450251881Speter TRUE, scratch_pool)); 451251881Speter svn_error_clear(svn_io_remove_file2( 452251881Speter svn_wc__adm_child(wcroot_abspath, 453251881Speter ADM_README, 454251881Speter scratch_pool), 455251881Speter TRUE, scratch_pool)); 456251881Speter 457251881Speter /* For formats <= SVN_WC__WCPROPS_MANY_FILES_VERSION, we toss the wcprops 458251881Speter for the directory itself, and then all the wcprops for the files. */ 459251881Speter svn_error_clear(svn_io_remove_file2( 460251881Speter svn_wc__adm_child(wcroot_abspath, 461251881Speter WCPROPS_FNAME_FOR_DIR, 462251881Speter scratch_pool), 463251881Speter TRUE, scratch_pool)); 464251881Speter svn_error_clear(svn_io_remove_dir2( 465251881Speter svn_wc__adm_child(wcroot_abspath, 466251881Speter WCPROPS_SUBDIR_FOR_FILES, 467251881Speter scratch_pool), 468251881Speter FALSE, NULL, NULL, scratch_pool)); 469251881Speter 470251881Speter /* And for later formats, they are aggregated into one file. */ 471251881Speter svn_error_clear(svn_io_remove_file2( 472251881Speter svn_wc__adm_child(wcroot_abspath, 473251881Speter WCPROPS_ALL_DATA, 474251881Speter scratch_pool), 475251881Speter TRUE, scratch_pool)); 476251881Speter 477251881Speter /* Remove the old text-base directory and the old text-base files. */ 478251881Speter svn_error_clear(svn_io_remove_dir2( 479251881Speter svn_wc__adm_child(wcroot_abspath, 480251881Speter TEXT_BASE_SUBDIR, 481251881Speter scratch_pool), 482251881Speter FALSE, NULL, NULL, scratch_pool)); 483251881Speter 484251881Speter /* Remove the old properties files... whole directories at a time. */ 485251881Speter svn_error_clear(svn_io_remove_dir2( 486251881Speter svn_wc__adm_child(wcroot_abspath, 487251881Speter PROPS_SUBDIR, 488251881Speter scratch_pool), 489251881Speter FALSE, NULL, NULL, scratch_pool)); 490251881Speter svn_error_clear(svn_io_remove_dir2( 491251881Speter svn_wc__adm_child(wcroot_abspath, 492251881Speter PROP_BASE_SUBDIR, 493251881Speter scratch_pool), 494251881Speter FALSE, NULL, NULL, scratch_pool)); 495251881Speter svn_error_clear(svn_io_remove_file2( 496251881Speter svn_wc__adm_child(wcroot_abspath, 497251881Speter PROP_WORKING_FOR_DIR, 498251881Speter scratch_pool), 499251881Speter TRUE, scratch_pool)); 500251881Speter svn_error_clear(svn_io_remove_file2( 501251881Speter svn_wc__adm_child(wcroot_abspath, 502251881Speter PROP_BASE_FOR_DIR, 503251881Speter scratch_pool), 504251881Speter TRUE, scratch_pool)); 505251881Speter svn_error_clear(svn_io_remove_file2( 506251881Speter svn_wc__adm_child(wcroot_abspath, 507251881Speter PROP_REVERT_FOR_DIR, 508251881Speter scratch_pool), 509251881Speter TRUE, scratch_pool)); 510251881Speter 511251881Speter#if 0 512251881Speter /* ### this checks for a write-lock, and we are not (always) taking out 513251881Speter ### a write lock in all callers. */ 514251881Speter SVN_ERR(svn_wc__adm_cleanup_tmp_area(db, wcroot_abspath, iterpool)); 515251881Speter#endif 516251881Speter 517251881Speter /* Remove the old-style lock file LAST. */ 518251881Speter svn_error_clear(svn_io_remove_file2( 519251881Speter build_lockfile_path(wcroot_abspath, scratch_pool), 520251881Speter TRUE, scratch_pool)); 521251881Speter} 522251881Speter 523251881Spetersvn_error_t * 524251881Spetersvn_wc__wipe_postupgrade(const char *dir_abspath, 525251881Speter svn_boolean_t whole_admin, 526251881Speter svn_cancel_func_t cancel_func, 527251881Speter void *cancel_baton, 528251881Speter apr_pool_t *scratch_pool) 529251881Speter{ 530251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 531251881Speter apr_array_header_t *subdirs; 532251881Speter svn_error_t *err; 533251881Speter svn_boolean_t delete_dir; 534251881Speter int i; 535251881Speter 536251881Speter if (cancel_func) 537251881Speter SVN_ERR((*cancel_func)(cancel_baton)); 538251881Speter 539251881Speter err = get_versioned_subdirs(&subdirs, &delete_dir, dir_abspath, TRUE, 540251881Speter scratch_pool, iterpool); 541251881Speter if (err) 542251881Speter { 543251881Speter if (APR_STATUS_IS_ENOENT(err->apr_err)) 544251881Speter { 545251881Speter /* An unversioned dir is obstructing a versioned dir */ 546251881Speter svn_error_clear(err); 547251881Speter err = NULL; 548251881Speter } 549251881Speter svn_pool_destroy(iterpool); 550251881Speter return svn_error_trace(err); 551251881Speter } 552251881Speter for (i = 0; i < subdirs->nelts; ++i) 553251881Speter { 554251881Speter const char *child_abspath = APR_ARRAY_IDX(subdirs, i, const char *); 555251881Speter 556251881Speter svn_pool_clear(iterpool); 557251881Speter SVN_ERR(svn_wc__wipe_postupgrade(child_abspath, TRUE, 558251881Speter cancel_func, cancel_baton, iterpool)); 559251881Speter } 560251881Speter 561251881Speter /* ### Should we really be ignoring errors here? */ 562251881Speter if (whole_admin) 563251881Speter svn_error_clear(svn_io_remove_dir2(svn_wc__adm_child(dir_abspath, "", 564251881Speter iterpool), 565251881Speter TRUE, NULL, NULL, iterpool)); 566251881Speter else 567251881Speter wipe_obsolete_files(dir_abspath, scratch_pool); 568251881Speter 569251881Speter if (delete_dir) 570251881Speter { 571251881Speter /* If this was a WC-NG single database copy, this directory wouldn't 572251881Speter be here (unless it was deleted with --keep-local) 573251881Speter 574251881Speter If the directory is empty, we can just delete it; if not we 575251881Speter keep it. 576251881Speter */ 577251881Speter svn_error_clear(svn_io_dir_remove_nonrecursive(dir_abspath, iterpool)); 578251881Speter } 579251881Speter 580251881Speter svn_pool_destroy(iterpool); 581251881Speter 582251881Speter return SVN_NO_ERROR; 583251881Speter} 584251881Speter 585251881Speter/* Ensure that ENTRY has its REPOS and UUID fields set. These will be 586251881Speter used to establish the REPOSITORY row in the new database, and then 587251881Speter used within the upgraded entries as they are written into the database. 588251881Speter 589251881Speter If one or both are not available, then it attempts to retrieve this 590251881Speter information from REPOS_CACHE. And if that fails from REPOS_INFO_FUNC, 591251881Speter passing REPOS_INFO_BATON. 592251881Speter Returns a user understandable error using LOCAL_ABSPATH if the 593251881Speter information cannot be obtained. */ 594251881Speterstatic svn_error_t * 595251881Speterensure_repos_info(svn_wc_entry_t *entry, 596251881Speter const char *local_abspath, 597251881Speter svn_wc_upgrade_get_repos_info_t repos_info_func, 598251881Speter void *repos_info_baton, 599251881Speter apr_hash_t *repos_cache, 600251881Speter apr_pool_t *result_pool, 601251881Speter apr_pool_t *scratch_pool) 602251881Speter{ 603251881Speter /* Easy exit. */ 604251881Speter if (entry->repos != NULL && entry->uuid != NULL) 605251881Speter return SVN_NO_ERROR; 606251881Speter 607251881Speter if ((entry->repos == NULL || entry->uuid == NULL) 608251881Speter && entry->url) 609251881Speter { 610251881Speter apr_hash_index_t *hi; 611251881Speter 612251881Speter for (hi = apr_hash_first(scratch_pool, repos_cache); 613251881Speter hi; hi = apr_hash_next(hi)) 614251881Speter { 615251881Speter if (svn_uri__is_ancestor(svn__apr_hash_index_key(hi), entry->url)) 616251881Speter { 617251881Speter if (!entry->repos) 618251881Speter entry->repos = svn__apr_hash_index_key(hi); 619251881Speter 620251881Speter if (!entry->uuid) 621251881Speter entry->uuid = svn__apr_hash_index_val(hi); 622251881Speter 623251881Speter return SVN_NO_ERROR; 624251881Speter } 625251881Speter } 626251881Speter } 627251881Speter 628251881Speter if (entry->repos == NULL && repos_info_func == NULL) 629251881Speter return svn_error_createf( 630251881Speter SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, 631251881Speter _("Working copy '%s' can't be upgraded because the repository root is " 632251881Speter "not available and can't be retrieved"), 633251881Speter svn_dirent_local_style(local_abspath, scratch_pool)); 634251881Speter 635251881Speter if (entry->uuid == NULL && repos_info_func == NULL) 636251881Speter return svn_error_createf( 637251881Speter SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, 638251881Speter _("Working copy '%s' can't be upgraded because the repository uuid is " 639251881Speter "not available and can't be retrieved"), 640251881Speter svn_dirent_local_style(local_abspath, scratch_pool)); 641251881Speter 642251881Speter if (entry->url == NULL) 643251881Speter return svn_error_createf( 644251881Speter SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, 645251881Speter _("Working copy '%s' can't be upgraded because it doesn't have a url"), 646251881Speter svn_dirent_local_style(local_abspath, scratch_pool)); 647251881Speter 648251881Speter return svn_error_trace((*repos_info_func)(&entry->repos, &entry->uuid, 649251881Speter repos_info_baton, 650251881Speter entry->url, 651251881Speter result_pool, scratch_pool)); 652251881Speter} 653251881Speter 654251881Speter 655251881Speter/* 656251881Speter * Read tree conflict descriptions from @a conflict_data. Set @a *conflicts 657251881Speter * to a hash of pointers to svn_wc_conflict_description2_t objects indexed by 658251881Speter * svn_wc_conflict_description2_t.local_abspath, all newly allocated in @a 659251881Speter * pool. @a dir_path is the path to the working copy directory whose conflicts 660251881Speter * are being read. The conflicts read are the tree conflicts on the immediate 661251881Speter * child nodes of @a dir_path. Do all allocations in @a pool. 662251881Speter * 663251881Speter * Note: There were some concerns about this function: 664251881Speter * 665251881Speter * ### this is BAD. the CONFLICTS structure should not be dependent upon 666251881Speter * ### DIR_PATH. each conflict should be labeled with an entry name, not 667251881Speter * ### a whole path. (and a path which happens to vary based upon invocation 668251881Speter * ### of the user client and these APIs) 669251881Speter * 670251881Speter * those assumptions were baked into former versions of the data model, so 671251881Speter * they have to stick around here. But they have been removed from the 672251881Speter * New Way. */ 673251881Speterstatic svn_error_t * 674251881Speterread_tree_conflicts(apr_hash_t **conflicts, 675251881Speter const char *conflict_data, 676251881Speter const char *dir_path, 677251881Speter apr_pool_t *pool) 678251881Speter{ 679251881Speter const svn_skel_t *skel; 680251881Speter apr_pool_t *iterpool; 681251881Speter 682251881Speter *conflicts = apr_hash_make(pool); 683251881Speter 684251881Speter if (conflict_data == NULL) 685251881Speter return SVN_NO_ERROR; 686251881Speter 687251881Speter skel = svn_skel__parse(conflict_data, strlen(conflict_data), pool); 688251881Speter if (skel == NULL) 689251881Speter return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, 690251881Speter _("Error parsing tree conflict skel")); 691251881Speter 692251881Speter iterpool = svn_pool_create(pool); 693251881Speter for (skel = skel->children; skel != NULL; skel = skel->next) 694251881Speter { 695251881Speter const svn_wc_conflict_description2_t *conflict; 696251881Speter 697251881Speter svn_pool_clear(iterpool); 698251881Speter SVN_ERR(svn_wc__deserialize_conflict(&conflict, skel, dir_path, 699251881Speter pool, iterpool)); 700251881Speter if (conflict != NULL) 701251881Speter svn_hash_sets(*conflicts, 702251881Speter svn_dirent_basename(conflict->local_abspath, pool), 703251881Speter conflict); 704251881Speter } 705251881Speter svn_pool_destroy(iterpool); 706251881Speter 707251881Speter return SVN_NO_ERROR; 708251881Speter} 709251881Speter 710251881Speter/* */ 711251881Speterstatic svn_error_t * 712251881Spetermigrate_single_tree_conflict_data(svn_sqlite__db_t *sdb, 713251881Speter const char *tree_conflict_data, 714251881Speter apr_int64_t wc_id, 715251881Speter const char *local_relpath, 716251881Speter apr_pool_t *scratch_pool) 717251881Speter{ 718251881Speter apr_hash_t *conflicts; 719251881Speter apr_hash_index_t *hi; 720251881Speter apr_pool_t *iterpool; 721251881Speter 722251881Speter SVN_ERR(read_tree_conflicts(&conflicts, tree_conflict_data, local_relpath, 723251881Speter scratch_pool)); 724251881Speter 725251881Speter iterpool = svn_pool_create(scratch_pool); 726251881Speter for (hi = apr_hash_first(scratch_pool, conflicts); 727251881Speter hi; 728251881Speter hi = apr_hash_next(hi)) 729251881Speter { 730251881Speter const svn_wc_conflict_description2_t *conflict = 731251881Speter svn__apr_hash_index_val(hi); 732251881Speter const char *conflict_relpath; 733251881Speter const char *conflict_data; 734251881Speter svn_sqlite__stmt_t *stmt; 735251881Speter svn_boolean_t have_row; 736251881Speter svn_skel_t *skel; 737251881Speter 738251881Speter svn_pool_clear(iterpool); 739251881Speter 740251881Speter conflict_relpath = svn_dirent_join(local_relpath, 741251881Speter svn_dirent_basename( 742251881Speter conflict->local_abspath, iterpool), 743251881Speter iterpool); 744251881Speter 745251881Speter SVN_ERR(svn_wc__serialize_conflict(&skel, conflict, iterpool, iterpool)); 746251881Speter conflict_data = svn_skel__unparse(skel, iterpool)->data; 747251881Speter 748251881Speter /* See if we need to update or insert an ACTUAL node. */ 749251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_ACTUAL_NODE)); 750251881Speter SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, conflict_relpath)); 751251881Speter SVN_ERR(svn_sqlite__step(&have_row, stmt)); 752251881Speter SVN_ERR(svn_sqlite__reset(stmt)); 753251881Speter 754251881Speter if (have_row) 755251881Speter { 756251881Speter /* There is an existing ACTUAL row, so just update it. */ 757251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 758251881Speter STMT_UPDATE_ACTUAL_CONFLICT_DATA)); 759251881Speter } 760251881Speter else 761251881Speter { 762251881Speter /* We need to insert an ACTUAL row with the tree conflict data. */ 763251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 764251881Speter STMT_INSERT_ACTUAL_CONFLICT_DATA)); 765251881Speter } 766251881Speter 767251881Speter SVN_ERR(svn_sqlite__bindf(stmt, "iss", wc_id, conflict_relpath, 768251881Speter conflict_data)); 769251881Speter if (!have_row) 770251881Speter SVN_ERR(svn_sqlite__bind_text(stmt, 4, local_relpath)); 771251881Speter 772251881Speter SVN_ERR(svn_sqlite__step_done(stmt)); 773251881Speter } 774251881Speter 775251881Speter svn_pool_destroy(iterpool); 776251881Speter 777251881Speter return SVN_NO_ERROR; 778251881Speter} 779251881Speter 780251881Speter 781251881Speter/* */ 782251881Speterstatic svn_error_t * 783251881Spetermigrate_tree_conflict_data(svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 784251881Speter{ 785251881Speter svn_sqlite__stmt_t *stmt; 786251881Speter svn_boolean_t have_row; 787251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 788251881Speter 789251881Speter /* Iterate over each node which has a set of tree conflicts, then insert 790251881Speter all of them into the new schema. */ 791251881Speter 792251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 793251881Speter STMT_UPGRADE_21_SELECT_OLD_TREE_CONFLICT)); 794251881Speter 795251881Speter /* Get all the existing tree conflict data. */ 796251881Speter SVN_ERR(svn_sqlite__step(&have_row, stmt)); 797251881Speter while (have_row) 798251881Speter { 799251881Speter apr_int64_t wc_id; 800251881Speter const char *local_relpath; 801251881Speter const char *tree_conflict_data; 802251881Speter 803251881Speter svn_pool_clear(iterpool); 804251881Speter 805251881Speter wc_id = svn_sqlite__column_int64(stmt, 0); 806251881Speter local_relpath = svn_sqlite__column_text(stmt, 1, iterpool); 807251881Speter tree_conflict_data = svn_sqlite__column_text(stmt, 2, iterpool); 808251881Speter 809251881Speter SVN_ERR(migrate_single_tree_conflict_data(sdb, tree_conflict_data, 810251881Speter wc_id, local_relpath, 811251881Speter iterpool)); 812251881Speter 813251881Speter /* We don't need to do anything but step over the previously 814251881Speter prepared statement. */ 815251881Speter SVN_ERR(svn_sqlite__step(&have_row, stmt)); 816251881Speter } 817251881Speter SVN_ERR(svn_sqlite__reset(stmt)); 818251881Speter 819251881Speter /* Erase all the old tree conflict data. */ 820251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 821251881Speter STMT_UPGRADE_21_ERASE_OLD_CONFLICTS)); 822251881Speter SVN_ERR(svn_sqlite__step_done(stmt)); 823251881Speter 824251881Speter svn_pool_destroy(iterpool); 825251881Speter return SVN_NO_ERROR; 826251881Speter} 827251881Speter 828251881Speter 829251881Speterstruct bump_baton { 830251881Speter const char *wcroot_abspath; 831251881Speter}; 832251881Speter 833251881Speter/* Migrate the properties for one node (LOCAL_ABSPATH). */ 834251881Speterstatic svn_error_t * 835251881Spetermigrate_node_props(const char *dir_abspath, 836251881Speter const char *new_wcroot_abspath, 837251881Speter const char *name, 838251881Speter svn_sqlite__db_t *sdb, 839251881Speter int original_format, 840251881Speter apr_int64_t wc_id, 841251881Speter apr_pool_t *scratch_pool) 842251881Speter{ 843251881Speter const char *base_abspath; /* old name. nowadays: "pristine" */ 844251881Speter const char *revert_abspath; /* old name. nowadays: "BASE" */ 845251881Speter const char *working_abspath; /* old name. nowadays: "ACTUAL" */ 846251881Speter apr_hash_t *base_props; 847251881Speter apr_hash_t *revert_props; 848251881Speter apr_hash_t *working_props; 849251881Speter const char *old_wcroot_abspath 850251881Speter = svn_dirent_get_longest_ancestor(dir_abspath, new_wcroot_abspath, 851251881Speter scratch_pool); 852251881Speter const char *dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath, 853251881Speter dir_abspath); 854251881Speter 855251881Speter if (*name == '\0') 856251881Speter { 857251881Speter base_abspath = svn_wc__adm_child(dir_abspath, 858251881Speter PROP_BASE_FOR_DIR, scratch_pool); 859251881Speter revert_abspath = svn_wc__adm_child(dir_abspath, 860251881Speter PROP_REVERT_FOR_DIR, scratch_pool); 861251881Speter working_abspath = svn_wc__adm_child(dir_abspath, 862251881Speter PROP_WORKING_FOR_DIR, scratch_pool); 863251881Speter } 864251881Speter else 865251881Speter { 866251881Speter const char *basedir_abspath; 867251881Speter const char *propsdir_abspath; 868251881Speter 869251881Speter propsdir_abspath = svn_wc__adm_child(dir_abspath, PROPS_SUBDIR, 870251881Speter scratch_pool); 871251881Speter basedir_abspath = svn_wc__adm_child(dir_abspath, PROP_BASE_SUBDIR, 872251881Speter scratch_pool); 873251881Speter 874251881Speter base_abspath = svn_dirent_join(basedir_abspath, 875251881Speter apr_pstrcat(scratch_pool, 876251881Speter name, 877251881Speter SVN_WC__BASE_EXT, 878251881Speter (char *)NULL), 879251881Speter scratch_pool); 880251881Speter 881251881Speter revert_abspath = svn_dirent_join(basedir_abspath, 882251881Speter apr_pstrcat(scratch_pool, 883251881Speter name, 884251881Speter SVN_WC__REVERT_EXT, 885251881Speter (char *)NULL), 886251881Speter scratch_pool); 887251881Speter 888251881Speter working_abspath = svn_dirent_join(propsdir_abspath, 889251881Speter apr_pstrcat(scratch_pool, 890251881Speter name, 891251881Speter SVN_WC__WORK_EXT, 892251881Speter (char *)NULL), 893251881Speter scratch_pool); 894251881Speter } 895251881Speter 896251881Speter SVN_ERR(read_propfile(&base_props, base_abspath, 897251881Speter scratch_pool, scratch_pool)); 898251881Speter SVN_ERR(read_propfile(&revert_props, revert_abspath, 899251881Speter scratch_pool, scratch_pool)); 900251881Speter SVN_ERR(read_propfile(&working_props, working_abspath, 901251881Speter scratch_pool, scratch_pool)); 902251881Speter 903251881Speter return svn_error_trace(svn_wc__db_upgrade_apply_props( 904251881Speter sdb, new_wcroot_abspath, 905251881Speter svn_relpath_join(dir_relpath, name, scratch_pool), 906251881Speter base_props, revert_props, working_props, 907251881Speter original_format, wc_id, 908251881Speter scratch_pool)); 909251881Speter} 910251881Speter 911251881Speter 912251881Speter/* */ 913251881Speterstatic svn_error_t * 914251881Spetermigrate_props(const char *dir_abspath, 915251881Speter const char *new_wcroot_abspath, 916251881Speter svn_sqlite__db_t *sdb, 917251881Speter int original_format, 918251881Speter apr_int64_t wc_id, 919251881Speter apr_pool_t *scratch_pool) 920251881Speter{ 921251881Speter /* General logic here: iterate over all the immediate children of the root 922251881Speter (since we aren't yet in a centralized system), and for any properties that 923251881Speter exist, map them as follows: 924251881Speter 925251881Speter if (revert props exist): 926251881Speter revert -> BASE 927251881Speter base -> WORKING 928251881Speter working -> ACTUAL 929251881Speter else if (prop pristine is working [as defined in props.c] ): 930251881Speter base -> WORKING 931251881Speter working -> ACTUAL 932251881Speter else: 933251881Speter base -> BASE 934251881Speter working -> ACTUAL 935251881Speter 936251881Speter ### the middle "test" should simply look for a WORKING_NODE row 937251881Speter 938251881Speter Note that it is legal for "working" props to be missing. That implies 939251881Speter no local changes to the properties. 940251881Speter */ 941251881Speter const apr_array_header_t *children; 942251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 943251881Speter const char *old_wcroot_abspath 944251881Speter = svn_dirent_get_longest_ancestor(dir_abspath, new_wcroot_abspath, 945251881Speter scratch_pool); 946251881Speter const char *dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath, 947251881Speter dir_abspath); 948251881Speter int i; 949251881Speter 950251881Speter /* Migrate the props for "this dir". */ 951251881Speter SVN_ERR(migrate_node_props(dir_abspath, new_wcroot_abspath, "", sdb, 952251881Speter original_format, wc_id, iterpool)); 953251881Speter 954251881Speter /* Iterate over all the files in this SDB. */ 955251881Speter SVN_ERR(get_versioned_files(&children, dir_relpath, sdb, wc_id, scratch_pool, 956251881Speter iterpool)); 957251881Speter for (i = 0; i < children->nelts; i++) 958251881Speter { 959251881Speter const char *name = APR_ARRAY_IDX(children, i, const char *); 960251881Speter 961251881Speter svn_pool_clear(iterpool); 962251881Speter 963251881Speter SVN_ERR(migrate_node_props(dir_abspath, new_wcroot_abspath, 964251881Speter name, sdb, original_format, wc_id, iterpool)); 965251881Speter } 966251881Speter 967251881Speter svn_pool_destroy(iterpool); 968251881Speter 969251881Speter return SVN_NO_ERROR; 970251881Speter} 971251881Speter 972251881Speter 973251881Speter/* If STR ends with SUFFIX and is longer than SUFFIX, return the part of 974251881Speter * STR that comes before SUFFIX; else return NULL. */ 975251881Speterstatic char * 976251881Speterremove_suffix(const char *str, const char *suffix, apr_pool_t *result_pool) 977251881Speter{ 978251881Speter size_t str_len = strlen(str); 979251881Speter size_t suffix_len = strlen(suffix); 980251881Speter 981251881Speter if (str_len > suffix_len 982251881Speter && strcmp(str + str_len - suffix_len, suffix) == 0) 983251881Speter { 984251881Speter return apr_pstrmemdup(result_pool, str, str_len - suffix_len); 985251881Speter } 986251881Speter 987251881Speter return NULL; 988251881Speter} 989251881Speter 990251881Speter/* Copy all the text-base files from the administrative area of WC directory 991251881Speter DIR_ABSPATH into the pristine store of SDB which is located in directory 992251881Speter NEW_WCROOT_ABSPATH. 993251881Speter 994251881Speter Set *TEXT_BASES_INFO to a new hash, allocated in RESULT_POOL, that maps 995251881Speter (const char *) name of the versioned file to (svn_wc__text_base_info_t *) 996251881Speter information about the pristine text. */ 997251881Speterstatic svn_error_t * 998251881Spetermigrate_text_bases(apr_hash_t **text_bases_info, 999251881Speter const char *dir_abspath, 1000251881Speter const char *new_wcroot_abspath, 1001251881Speter svn_sqlite__db_t *sdb, 1002251881Speter apr_pool_t *result_pool, 1003251881Speter apr_pool_t *scratch_pool) 1004251881Speter{ 1005251881Speter apr_hash_t *dirents; 1006251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1007251881Speter apr_hash_index_t *hi; 1008251881Speter const char *text_base_dir = svn_wc__adm_child(dir_abspath, 1009251881Speter TEXT_BASE_SUBDIR, 1010251881Speter scratch_pool); 1011251881Speter 1012251881Speter *text_bases_info = apr_hash_make(result_pool); 1013251881Speter 1014251881Speter /* Iterate over the text-base files */ 1015251881Speter SVN_ERR(svn_io_get_dirents3(&dirents, text_base_dir, TRUE, 1016251881Speter scratch_pool, scratch_pool)); 1017251881Speter for (hi = apr_hash_first(scratch_pool, dirents); hi; 1018251881Speter hi = apr_hash_next(hi)) 1019251881Speter { 1020251881Speter const char *text_base_basename = svn__apr_hash_index_key(hi); 1021251881Speter svn_checksum_t *md5_checksum; 1022251881Speter svn_checksum_t *sha1_checksum; 1023251881Speter 1024251881Speter svn_pool_clear(iterpool); 1025251881Speter 1026251881Speter /* Calculate its checksums and copy it to the pristine store */ 1027251881Speter { 1028251881Speter const char *pristine_path; 1029251881Speter const char *text_base_path; 1030251881Speter const char *temp_path; 1031251881Speter svn_sqlite__stmt_t *stmt; 1032251881Speter apr_finfo_t finfo; 1033251881Speter svn_stream_t *read_stream; 1034251881Speter svn_stream_t *result_stream; 1035251881Speter 1036251881Speter text_base_path = svn_dirent_join(text_base_dir, text_base_basename, 1037251881Speter iterpool); 1038251881Speter 1039251881Speter /* Create a copy and calculate a checksum in one step */ 1040251881Speter SVN_ERR(svn_stream_open_unique(&result_stream, &temp_path, 1041251881Speter new_wcroot_abspath, 1042251881Speter svn_io_file_del_none, 1043251881Speter iterpool, iterpool)); 1044251881Speter 1045251881Speter SVN_ERR(svn_stream_open_readonly(&read_stream, text_base_path, 1046251881Speter iterpool, iterpool)); 1047251881Speter 1048251881Speter read_stream = svn_stream_checksummed2(read_stream, &md5_checksum, 1049251881Speter NULL, svn_checksum_md5, 1050251881Speter TRUE, iterpool); 1051251881Speter 1052251881Speter read_stream = svn_stream_checksummed2(read_stream, &sha1_checksum, 1053251881Speter NULL, svn_checksum_sha1, 1054251881Speter TRUE, iterpool); 1055251881Speter 1056251881Speter /* This calculates the hash, creates a copy and closes the stream */ 1057251881Speter SVN_ERR(svn_stream_copy3(read_stream, result_stream, 1058251881Speter NULL, NULL, iterpool)); 1059251881Speter 1060251881Speter SVN_ERR(svn_io_stat(&finfo, text_base_path, APR_FINFO_SIZE, iterpool)); 1061251881Speter 1062251881Speter /* Insert a row into the pristine table. */ 1063251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 1064251881Speter STMT_INSERT_OR_IGNORE_PRISTINE)); 1065251881Speter SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, iterpool)); 1066251881Speter SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, iterpool)); 1067251881Speter SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size)); 1068251881Speter SVN_ERR(svn_sqlite__insert(NULL, stmt)); 1069251881Speter 1070251881Speter SVN_ERR(svn_wc__db_pristine_get_future_path(&pristine_path, 1071251881Speter new_wcroot_abspath, 1072251881Speter sha1_checksum, 1073251881Speter iterpool, iterpool)); 1074251881Speter 1075251881Speter /* Ensure any sharding directories exist. */ 1076251881Speter SVN_ERR(svn_wc__ensure_directory(svn_dirent_dirname(pristine_path, 1077251881Speter iterpool), 1078251881Speter iterpool)); 1079251881Speter 1080251881Speter /* Now move the file into the pristine store, overwriting 1081251881Speter existing files with the same checksum. */ 1082251881Speter SVN_ERR(svn_io_file_move(temp_path, pristine_path, iterpool)); 1083251881Speter } 1084251881Speter 1085251881Speter /* Add the checksums for this text-base to *TEXT_BASES_INFO. */ 1086251881Speter { 1087251881Speter const char *versioned_file_name; 1088251881Speter svn_boolean_t is_revert_base; 1089251881Speter svn_wc__text_base_info_t *info; 1090251881Speter svn_wc__text_base_file_info_t *file_info; 1091251881Speter 1092251881Speter /* Determine the versioned file name and whether this is a normal base 1093251881Speter * or a revert base. */ 1094251881Speter versioned_file_name = remove_suffix(text_base_basename, 1095251881Speter SVN_WC__REVERT_EXT, result_pool); 1096251881Speter if (versioned_file_name) 1097251881Speter { 1098251881Speter is_revert_base = TRUE; 1099251881Speter } 1100251881Speter else 1101251881Speter { 1102251881Speter versioned_file_name = remove_suffix(text_base_basename, 1103251881Speter SVN_WC__BASE_EXT, result_pool); 1104251881Speter is_revert_base = FALSE; 1105251881Speter } 1106251881Speter 1107251881Speter if (! versioned_file_name) 1108251881Speter { 1109251881Speter /* Some file that doesn't end with .svn-base or .svn-revert. 1110251881Speter No idea why that would be in our administrative area, but 1111251881Speter we shouldn't segfault on this case. 1112251881Speter 1113251881Speter Note that we already copied this file in the pristine store, 1114251881Speter but the next cleanup will take care of that. 1115251881Speter */ 1116251881Speter continue; 1117251881Speter } 1118251881Speter 1119251881Speter /* Create a new info struct for this versioned file, or fill in the 1120251881Speter * existing one if this is the second text-base we've found for it. */ 1121251881Speter info = svn_hash_gets(*text_bases_info, versioned_file_name); 1122251881Speter if (info == NULL) 1123251881Speter info = apr_pcalloc(result_pool, sizeof (*info)); 1124251881Speter file_info = (is_revert_base ? &info->revert_base : &info->normal_base); 1125251881Speter 1126251881Speter file_info->sha1_checksum = svn_checksum_dup(sha1_checksum, result_pool); 1127251881Speter file_info->md5_checksum = svn_checksum_dup(md5_checksum, result_pool); 1128251881Speter svn_hash_sets(*text_bases_info, versioned_file_name, info); 1129251881Speter } 1130251881Speter } 1131251881Speter 1132251881Speter svn_pool_destroy(iterpool); 1133251881Speter 1134251881Speter return SVN_NO_ERROR; 1135251881Speter} 1136251881Speter 1137251881Speterstatic svn_error_t * 1138251881Speterbump_to_20(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1139251881Speter{ 1140251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_CREATE_NODES)); 1141251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_20)); 1142251881Speter return SVN_NO_ERROR; 1143251881Speter} 1144251881Speter 1145251881Speterstatic svn_error_t * 1146251881Speterbump_to_21(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1147251881Speter{ 1148251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_21)); 1149251881Speter SVN_ERR(migrate_tree_conflict_data(sdb, scratch_pool)); 1150251881Speter return SVN_NO_ERROR; 1151251881Speter} 1152251881Speter 1153251881Speterstatic svn_error_t * 1154251881Speterbump_to_22(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1155251881Speter{ 1156251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_22)); 1157251881Speter return SVN_NO_ERROR; 1158251881Speter} 1159251881Speter 1160251881Speterstatic svn_error_t * 1161251881Speterbump_to_23(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1162251881Speter{ 1163251881Speter const char *wcroot_abspath = ((struct bump_baton *)baton)->wcroot_abspath; 1164251881Speter svn_sqlite__stmt_t *stmt; 1165251881Speter svn_boolean_t have_row; 1166251881Speter 1167251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 1168251881Speter STMT_UPGRADE_23_HAS_WORKING_NODES)); 1169251881Speter SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1170251881Speter SVN_ERR(svn_sqlite__reset(stmt)); 1171251881Speter if (have_row) 1172251881Speter return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 1173251881Speter _("The working copy at '%s' is format 22 with " 1174251881Speter "WORKING nodes; use a format 22 client to " 1175251881Speter "diff/revert before using this client"), 1176251881Speter wcroot_abspath); 1177251881Speter 1178251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_23)); 1179251881Speter return SVN_NO_ERROR; 1180251881Speter} 1181251881Speter 1182251881Speterstatic svn_error_t * 1183251881Speterbump_to_24(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1184251881Speter{ 1185251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_24)); 1186251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_CREATE_NODES_TRIGGERS)); 1187251881Speter return SVN_NO_ERROR; 1188251881Speter} 1189251881Speter 1190251881Speterstatic svn_error_t * 1191251881Speterbump_to_25(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1192251881Speter{ 1193251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_25)); 1194251881Speter return SVN_NO_ERROR; 1195251881Speter} 1196251881Speter 1197251881Speterstatic svn_error_t * 1198251881Speterbump_to_26(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1199251881Speter{ 1200251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_26)); 1201251881Speter return SVN_NO_ERROR; 1202251881Speter} 1203251881Speter 1204251881Speterstatic svn_error_t * 1205251881Speterbump_to_27(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1206251881Speter{ 1207251881Speter const char *wcroot_abspath = ((struct bump_baton *)baton)->wcroot_abspath; 1208251881Speter svn_sqlite__stmt_t *stmt; 1209251881Speter svn_boolean_t have_row; 1210251881Speter 1211251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 1212251881Speter STMT_UPGRADE_27_HAS_ACTUAL_NODES_CONFLICTS)); 1213251881Speter SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1214251881Speter SVN_ERR(svn_sqlite__reset(stmt)); 1215251881Speter if (have_row) 1216251881Speter return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 1217251881Speter _("The working copy at '%s' is format 26 with " 1218251881Speter "conflicts; use a format 26 client to resolve " 1219251881Speter "before using this client"), 1220251881Speter wcroot_abspath); 1221251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_27)); 1222251881Speter return SVN_NO_ERROR; 1223251881Speter} 1224251881Speter 1225251881Speterstatic svn_error_t * 1226251881Speterbump_to_28(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1227251881Speter{ 1228251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_28)); 1229251881Speter return SVN_NO_ERROR; 1230251881Speter} 1231251881Speter 1232251881Speter/* If FINFO indicates that ABSPATH names a file, rename it to 1233251881Speter * '<ABSPATH>.svn-base'. 1234251881Speter * 1235251881Speter * Ignore any file whose name is not the expected length, in order to make 1236251881Speter * life easier for any developer who runs this code twice or has some 1237251881Speter * non-standard files in the pristine directory. 1238251881Speter * 1239251881Speter * A callback for bump_to_29(), implementing #svn_io_walk_func_t. */ 1240251881Speterstatic svn_error_t * 1241251881Speterrename_pristine_file(void *baton, 1242251881Speter const char *abspath, 1243251881Speter const apr_finfo_t *finfo, 1244251881Speter apr_pool_t *pool) 1245251881Speter{ 1246251881Speter if (finfo->filetype == APR_REG 1247251881Speter && (strlen(svn_dirent_basename(abspath, pool)) 1248251881Speter == PRISTINE_BASENAME_OLD_LEN)) 1249251881Speter { 1250251881Speter const char *new_abspath 1251251881Speter = apr_pstrcat(pool, abspath, PRISTINE_STORAGE_EXT, (char *)NULL); 1252251881Speter 1253251881Speter SVN_ERR(svn_io_file_rename(abspath, new_abspath, pool)); 1254251881Speter } 1255251881Speter return SVN_NO_ERROR; 1256251881Speter} 1257251881Speter 1258251881Speterstatic svn_error_t * 1259251881Speterupgrade_externals(struct bump_baton *bb, 1260251881Speter svn_sqlite__db_t *sdb, 1261251881Speter apr_pool_t *scratch_pool) 1262251881Speter{ 1263251881Speter svn_sqlite__stmt_t *stmt; 1264251881Speter svn_sqlite__stmt_t *stmt_add; 1265251881Speter svn_boolean_t have_row; 1266251881Speter apr_pool_t *iterpool; 1267251881Speter 1268251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 1269251881Speter STMT_SELECT_EXTERNAL_PROPERTIES)); 1270251881Speter 1271251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt_add, sdb, 1272251881Speter STMT_INSERT_EXTERNAL)); 1273251881Speter 1274251881Speter /* ### For this intermediate upgrade we just assume WC_ID = 1. 1275251881Speter ### Before this bump we lost track of externals all the time, 1276251881Speter ### so lets keep this easy. */ 1277251881Speter SVN_ERR(svn_sqlite__bindf(stmt, "is", (apr_int64_t)1, "")); 1278251881Speter 1279251881Speter SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1280251881Speter 1281251881Speter iterpool = svn_pool_create(scratch_pool); 1282251881Speter while (have_row) 1283251881Speter { 1284251881Speter apr_hash_t *props; 1285251881Speter const char *externals; 1286251881Speter 1287251881Speter svn_pool_clear(iterpool); 1288251881Speter 1289251881Speter SVN_ERR(svn_sqlite__column_properties(&props, stmt, 0, 1290251881Speter iterpool, iterpool)); 1291251881Speter 1292251881Speter externals = svn_prop_get_value(props, SVN_PROP_EXTERNALS); 1293251881Speter 1294251881Speter if (externals) 1295251881Speter { 1296251881Speter apr_array_header_t *ext; 1297251881Speter const char *local_relpath; 1298251881Speter const char *local_abspath; 1299251881Speter int i; 1300251881Speter 1301251881Speter local_relpath = svn_sqlite__column_text(stmt, 1, NULL); 1302251881Speter local_abspath = svn_dirent_join(bb->wcroot_abspath, local_relpath, 1303251881Speter iterpool); 1304251881Speter 1305251881Speter SVN_ERR(svn_wc_parse_externals_description3(&ext, local_abspath, 1306251881Speter externals, FALSE, 1307251881Speter iterpool)); 1308251881Speter 1309251881Speter for (i = 0; i < ext->nelts; i++) 1310251881Speter { 1311251881Speter const svn_wc_external_item2_t *item; 1312251881Speter const char *item_relpath; 1313251881Speter 1314251881Speter item = APR_ARRAY_IDX(ext, i, const svn_wc_external_item2_t *); 1315251881Speter item_relpath = svn_relpath_join(local_relpath, item->target_dir, 1316251881Speter iterpool); 1317251881Speter 1318251881Speter /* Insert dummy externals definitions: Insert an unknown 1319251881Speter external, to make sure it will be cleaned up when it is not 1320251881Speter updated on the next update. */ 1321251881Speter SVN_ERR(svn_sqlite__bindf(stmt_add, "isssssis", 1322251881Speter (apr_int64_t)1, /* wc_id */ 1323251881Speter item_relpath, 1324251881Speter svn_relpath_dirname(item_relpath, 1325251881Speter iterpool), 1326251881Speter "normal", 1327251881Speter "unknown", 1328251881Speter local_relpath, 1329251881Speter (apr_int64_t)1, /* repos_id */ 1330251881Speter "" /* repos_relpath */)); 1331251881Speter SVN_ERR(svn_sqlite__insert(NULL, stmt_add)); 1332251881Speter } 1333251881Speter } 1334251881Speter 1335251881Speter SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1336251881Speter } 1337251881Speter 1338251881Speter svn_pool_destroy(iterpool); 1339251881Speter return svn_error_trace(svn_sqlite__reset(stmt)); 1340251881Speter} 1341251881Speter 1342251881Speterstatic svn_error_t * 1343251881Speterbump_to_29(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1344251881Speter{ 1345251881Speter struct bump_baton *bb = baton; 1346251881Speter const char *wcroot_abspath = bb->wcroot_abspath; 1347251881Speter const char *pristine_dir_abspath; 1348251881Speter 1349251881Speter /* Rename all pristine files, adding a ".svn-base" suffix. */ 1350251881Speter pristine_dir_abspath = svn_dirent_join_many(scratch_pool, wcroot_abspath, 1351251881Speter svn_wc_get_adm_dir(scratch_pool), 1352251881Speter PRISTINE_STORAGE_RELPATH, NULL); 1353251881Speter SVN_ERR(svn_io_dir_walk2(pristine_dir_abspath, APR_FINFO_MIN, 1354251881Speter rename_pristine_file, NULL, scratch_pool)); 1355251881Speter 1356251881Speter /* Externals */ 1357251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_CREATE_EXTERNALS)); 1358251881Speter 1359251881Speter SVN_ERR(upgrade_externals(bb, sdb, scratch_pool)); 1360251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_29)); 1361251881Speter return SVN_NO_ERROR; 1362251881Speter} 1363251881Speter 1364251881Spetersvn_error_t * 1365251881Spetersvn_wc__upgrade_conflict_skel_from_raw(svn_skel_t **conflicts, 1366251881Speter svn_wc__db_t *db, 1367251881Speter const char *wri_abspath, 1368251881Speter const char *local_relpath, 1369251881Speter const char *conflict_old, 1370251881Speter const char *conflict_wrk, 1371251881Speter const char *conflict_new, 1372251881Speter const char *prej_file, 1373251881Speter const char *tree_conflict_data, 1374251881Speter apr_size_t tree_conflict_len, 1375251881Speter apr_pool_t *result_pool, 1376251881Speter apr_pool_t *scratch_pool) 1377251881Speter{ 1378251881Speter svn_skel_t *conflict_data = NULL; 1379251881Speter const char *wcroot_abspath; 1380251881Speter 1381251881Speter SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath, 1382251881Speter scratch_pool, scratch_pool)); 1383251881Speter 1384251881Speter if (conflict_old || conflict_new || conflict_wrk) 1385251881Speter { 1386251881Speter const char *old_abspath = NULL; 1387251881Speter const char *new_abspath = NULL; 1388251881Speter const char *wrk_abspath = NULL; 1389251881Speter 1390251881Speter conflict_data = svn_wc__conflict_skel_create(result_pool); 1391251881Speter 1392251881Speter if (conflict_old) 1393251881Speter old_abspath = svn_dirent_join(wcroot_abspath, conflict_old, 1394251881Speter scratch_pool); 1395251881Speter 1396251881Speter if (conflict_new) 1397251881Speter new_abspath = svn_dirent_join(wcroot_abspath, conflict_new, 1398251881Speter scratch_pool); 1399251881Speter 1400251881Speter if (conflict_wrk) 1401251881Speter wrk_abspath = svn_dirent_join(wcroot_abspath, conflict_wrk, 1402251881Speter scratch_pool); 1403251881Speter 1404251881Speter SVN_ERR(svn_wc__conflict_skel_add_text_conflict(conflict_data, 1405251881Speter db, wri_abspath, 1406251881Speter wrk_abspath, 1407251881Speter old_abspath, 1408251881Speter new_abspath, 1409251881Speter scratch_pool, 1410251881Speter scratch_pool)); 1411251881Speter } 1412251881Speter 1413251881Speter if (prej_file) 1414251881Speter { 1415251881Speter const char *prej_abspath; 1416251881Speter 1417251881Speter if (!conflict_data) 1418251881Speter conflict_data = svn_wc__conflict_skel_create(result_pool); 1419251881Speter 1420251881Speter prej_abspath = svn_dirent_join(wcroot_abspath, prej_file, scratch_pool); 1421251881Speter 1422251881Speter SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflict_data, 1423251881Speter db, wri_abspath, 1424251881Speter prej_abspath, 1425251881Speter NULL, NULL, NULL, 1426251881Speter apr_hash_make(scratch_pool), 1427251881Speter scratch_pool, 1428251881Speter scratch_pool)); 1429251881Speter } 1430251881Speter 1431251881Speter if (tree_conflict_data) 1432251881Speter { 1433251881Speter svn_skel_t *tc_skel; 1434251881Speter const svn_wc_conflict_description2_t *tc; 1435251881Speter const char *local_abspath; 1436251881Speter 1437251881Speter if (!conflict_data) 1438251881Speter conflict_data = svn_wc__conflict_skel_create(scratch_pool); 1439251881Speter 1440251881Speter tc_skel = svn_skel__parse(tree_conflict_data, tree_conflict_len, 1441251881Speter scratch_pool); 1442251881Speter 1443251881Speter local_abspath = svn_dirent_join(wcroot_abspath, local_relpath, 1444251881Speter scratch_pool); 1445251881Speter 1446251881Speter SVN_ERR(svn_wc__deserialize_conflict(&tc, tc_skel, 1447251881Speter svn_dirent_dirname(local_abspath, 1448251881Speter scratch_pool), 1449251881Speter scratch_pool, scratch_pool)); 1450251881Speter 1451251881Speter SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(conflict_data, 1452251881Speter db, wri_abspath, 1453251881Speter tc->reason, 1454251881Speter tc->action, 1455251881Speter NULL, 1456251881Speter scratch_pool, 1457251881Speter scratch_pool)); 1458251881Speter 1459251881Speter switch (tc->operation) 1460251881Speter { 1461251881Speter case svn_wc_operation_update: 1462251881Speter default: 1463251881Speter SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_data, 1464251881Speter tc->src_left_version, 1465251881Speter tc->src_right_version, 1466251881Speter scratch_pool, 1467251881Speter scratch_pool)); 1468251881Speter break; 1469251881Speter case svn_wc_operation_switch: 1470251881Speter SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict_data, 1471251881Speter tc->src_left_version, 1472251881Speter tc->src_right_version, 1473251881Speter scratch_pool, 1474251881Speter scratch_pool)); 1475251881Speter break; 1476251881Speter case svn_wc_operation_merge: 1477251881Speter SVN_ERR(svn_wc__conflict_skel_set_op_merge(conflict_data, 1478251881Speter tc->src_left_version, 1479251881Speter tc->src_right_version, 1480251881Speter scratch_pool, 1481251881Speter scratch_pool)); 1482251881Speter break; 1483251881Speter } 1484251881Speter } 1485251881Speter else if (conflict_data) 1486251881Speter { 1487251881Speter SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_data, NULL, NULL, 1488251881Speter scratch_pool, 1489251881Speter scratch_pool)); 1490251881Speter } 1491251881Speter 1492251881Speter *conflicts = conflict_data; 1493251881Speter return SVN_NO_ERROR; 1494251881Speter} 1495251881Speter 1496251881Speter/* Helper function to upgrade a single conflict from bump_to_30 */ 1497251881Speterstatic svn_error_t * 1498251881Speterbump_30_upgrade_one_conflict(svn_wc__db_t *wc_db, 1499251881Speter const char *wcroot_abspath, 1500251881Speter svn_sqlite__stmt_t *stmt, 1501251881Speter svn_sqlite__db_t *sdb, 1502251881Speter apr_pool_t *scratch_pool) 1503251881Speter{ 1504251881Speter svn_sqlite__stmt_t *stmt_store; 1505251881Speter svn_stringbuf_t *skel_data; 1506251881Speter svn_skel_t *conflict_data; 1507251881Speter apr_int64_t wc_id = svn_sqlite__column_int64(stmt, 0); 1508251881Speter const char *local_relpath = svn_sqlite__column_text(stmt, 1, NULL); 1509251881Speter const char *conflict_old = svn_sqlite__column_text(stmt, 2, NULL); 1510251881Speter const char *conflict_wrk = svn_sqlite__column_text(stmt, 3, NULL); 1511251881Speter const char *conflict_new = svn_sqlite__column_text(stmt, 4, NULL); 1512251881Speter const char *prop_reject = svn_sqlite__column_text(stmt, 5, NULL); 1513251881Speter apr_size_t tree_conflict_size; 1514251881Speter const char *tree_conflict_data = svn_sqlite__column_blob(stmt, 6, 1515251881Speter &tree_conflict_size, NULL); 1516251881Speter 1517251881Speter SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw(&conflict_data, 1518251881Speter wc_db, wcroot_abspath, 1519251881Speter local_relpath, 1520251881Speter conflict_old, 1521251881Speter conflict_wrk, 1522251881Speter conflict_new, 1523251881Speter prop_reject, 1524251881Speter tree_conflict_data, 1525251881Speter tree_conflict_size, 1526251881Speter scratch_pool, scratch_pool)); 1527251881Speter 1528251881Speter SVN_ERR_ASSERT(conflict_data != NULL); 1529251881Speter 1530251881Speter skel_data = svn_skel__unparse(conflict_data, scratch_pool); 1531251881Speter 1532251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt_store, sdb, 1533251881Speter STMT_UPGRADE_30_SET_CONFLICT)); 1534251881Speter SVN_ERR(svn_sqlite__bindf(stmt_store, "isb", wc_id, local_relpath, 1535251881Speter skel_data->data, skel_data->len)); 1536251881Speter SVN_ERR(svn_sqlite__step_done(stmt_store)); 1537251881Speter 1538251881Speter return SVN_NO_ERROR; 1539251881Speter} 1540251881Speter 1541251881Speterstatic svn_error_t * 1542251881Speterbump_to_30(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool) 1543251881Speter{ 1544251881Speter struct bump_baton *bb = baton; 1545251881Speter svn_boolean_t have_row; 1546251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1547251881Speter svn_sqlite__stmt_t *stmt; 1548251881Speter svn_wc__db_t *db; /* Read only temp db */ 1549251881Speter 1550251881Speter SVN_ERR(svn_wc__db_open(&db, NULL, TRUE /* open_without_upgrade */, FALSE, 1551251881Speter scratch_pool, scratch_pool)); 1552251881Speter 1553251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 1554251881Speter STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE)); 1555251881Speter SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1556251881Speter 1557251881Speter while (have_row) 1558251881Speter { 1559251881Speter svn_error_t *err; 1560251881Speter svn_pool_clear(iterpool); 1561251881Speter 1562251881Speter err = bump_30_upgrade_one_conflict(db, bb->wcroot_abspath, stmt, sdb, 1563251881Speter iterpool); 1564251881Speter 1565251881Speter if (err) 1566251881Speter { 1567251881Speter return svn_error_trace( 1568251881Speter svn_error_compose_create( 1569251881Speter err, 1570251881Speter svn_sqlite__reset(stmt))); 1571251881Speter } 1572251881Speter 1573251881Speter SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1574251881Speter } 1575251881Speter SVN_ERR(svn_sqlite__reset(stmt)); 1576251881Speter 1577251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_30)); 1578251881Speter SVN_ERR(svn_wc__db_close(db)); 1579251881Speter return SVN_NO_ERROR; 1580251881Speter} 1581251881Speter 1582251881Speterstatic svn_error_t * 1583251881Speterbump_to_31(void *baton, 1584251881Speter svn_sqlite__db_t *sdb, 1585251881Speter apr_pool_t *scratch_pool) 1586251881Speter{ 1587251881Speter svn_sqlite__stmt_t *stmt, *stmt_mark_switch_roots; 1588251881Speter svn_boolean_t have_row; 1589251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1590251881Speter apr_array_header_t *empty_iprops = apr_array_make( 1591251881Speter scratch_pool, 0, sizeof(svn_prop_inherited_item_t *)); 1592251881Speter svn_boolean_t iprops_column_exists = FALSE; 1593251881Speter svn_error_t *err; 1594251881Speter 1595251881Speter /* Add the inherited_props column to NODES if it does not yet exist. 1596251881Speter * 1597251881Speter * When using a format >= 31 client to upgrade from old formats which 1598251881Speter * did not yet have a NODES table, the inherited_props column has 1599251881Speter * already been created as part of the NODES table. Attemping to add 1600251881Speter * the inherited_props column will raise an error in this case, so check 1601251881Speter * if the column exists first. 1602251881Speter * 1603251881Speter * Checking for the existence of a column before ALTER TABLE is not 1604251881Speter * possible within SQLite. We need to run a separate query and evaluate 1605251881Speter * its result in C first. 1606251881Speter */ 1607251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_PRAGMA_TABLE_INFO_NODES)); 1608251881Speter SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1609251881Speter while (have_row) 1610251881Speter { 1611251881Speter const char *column_name = svn_sqlite__column_text(stmt, 1, NULL); 1612251881Speter 1613251881Speter if (strcmp(column_name, "inherited_props") == 0) 1614251881Speter { 1615251881Speter iprops_column_exists = TRUE; 1616251881Speter break; 1617251881Speter } 1618251881Speter 1619251881Speter SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1620251881Speter } 1621251881Speter SVN_ERR(svn_sqlite__reset(stmt)); 1622251881Speter if (!iprops_column_exists) 1623251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_31_ALTER_TABLE)); 1624251881Speter 1625251881Speter /* Run additional statements to finalize the upgrade to format 31. */ 1626251881Speter SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_31_FINALIZE)); 1627251881Speter 1628251881Speter /* Set inherited_props to an empty array for the roots of all 1629251881Speter switched subtrees in the WC. This allows subsequent updates 1630251881Speter to recognize these roots as needing an iprops cache. */ 1631251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, 1632251881Speter STMT_UPGRADE_31_SELECT_WCROOT_NODES)); 1633251881Speter SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1634251881Speter 1635251881Speter err = svn_sqlite__get_statement(&stmt_mark_switch_roots, sdb, 1636251881Speter STMT_UPDATE_IPROP); 1637251881Speter if (err) 1638251881Speter return svn_error_compose_create(err, svn_sqlite__reset(stmt)); 1639251881Speter 1640251881Speter while (have_row) 1641251881Speter { 1642251881Speter const char *switched_relpath = svn_sqlite__column_text(stmt, 1, NULL); 1643251881Speter apr_int64_t wc_id = svn_sqlite__column_int64(stmt, 0); 1644251881Speter 1645251881Speter err = svn_sqlite__bindf(stmt_mark_switch_roots, "is", wc_id, 1646251881Speter switched_relpath); 1647251881Speter if (!err) 1648251881Speter err = svn_sqlite__bind_iprops(stmt_mark_switch_roots, 3, 1649251881Speter empty_iprops, iterpool); 1650251881Speter if (!err) 1651251881Speter err = svn_sqlite__step_done(stmt_mark_switch_roots); 1652251881Speter if (!err) 1653251881Speter err = svn_sqlite__step(&have_row, stmt); 1654251881Speter 1655251881Speter if (err) 1656251881Speter return svn_error_compose_create( 1657251881Speter err, 1658251881Speter svn_error_compose_create( 1659251881Speter /* Reset in either order is OK. */ 1660251881Speter svn_sqlite__reset(stmt), 1661251881Speter svn_sqlite__reset(stmt_mark_switch_roots))); 1662251881Speter } 1663251881Speter 1664251881Speter err = svn_sqlite__reset(stmt_mark_switch_roots); 1665251881Speter if (err) 1666251881Speter return svn_error_compose_create(err, svn_sqlite__reset(stmt)); 1667251881Speter SVN_ERR(svn_sqlite__reset(stmt)); 1668251881Speter 1669251881Speter svn_pool_destroy(iterpool); 1670251881Speter 1671251881Speter return SVN_NO_ERROR; 1672251881Speter} 1673251881Speter 1674251881Speter 1675251881Speterstruct upgrade_data_t { 1676251881Speter svn_sqlite__db_t *sdb; 1677251881Speter const char *root_abspath; 1678251881Speter apr_int64_t repos_id; 1679251881Speter apr_int64_t wc_id; 1680251881Speter}; 1681251881Speter 1682251881Speter/* Upgrade the working copy directory represented by DB/DIR_ABSPATH 1683251881Speter from OLD_FORMAT to the wc-ng format (SVN_WC__WC_NG_VERSION)'. 1684251881Speter 1685251881Speter Pass REPOS_INFO_FUNC, REPOS_INFO_BATON and REPOS_CACHE to 1686251881Speter ensure_repos_info. Add the found repository root and UUID to 1687251881Speter REPOS_CACHE if it doesn't have a cached entry for this 1688251881Speter repository. 1689251881Speter 1690251881Speter *DATA refers to the single root db. 1691251881Speter 1692251881Speter Uses SCRATCH_POOL for all temporary allocation. */ 1693251881Speterstatic svn_error_t * 1694251881Speterupgrade_to_wcng(void **dir_baton, 1695251881Speter void *parent_baton, 1696251881Speter svn_wc__db_t *db, 1697251881Speter const char *dir_abspath, 1698251881Speter int old_format, 1699251881Speter apr_int64_t wc_id, 1700251881Speter svn_wc_upgrade_get_repos_info_t repos_info_func, 1701251881Speter void *repos_info_baton, 1702251881Speter apr_hash_t *repos_cache, 1703251881Speter const struct upgrade_data_t *data, 1704251881Speter apr_pool_t *result_pool, 1705251881Speter apr_pool_t *scratch_pool) 1706251881Speter{ 1707251881Speter const char *logfile_path = svn_wc__adm_child(dir_abspath, ADM_LOG, 1708251881Speter scratch_pool); 1709251881Speter svn_node_kind_t logfile_on_disk_kind; 1710251881Speter apr_hash_t *entries; 1711251881Speter svn_wc_entry_t *this_dir; 1712251881Speter const char *old_wcroot_abspath, *dir_relpath; 1713251881Speter apr_hash_t *text_bases_info; 1714251881Speter svn_error_t *err; 1715251881Speter 1716251881Speter /* Don't try to mess with the WC if there are old log files left. */ 1717251881Speter 1718251881Speter /* Is the (first) log file present? */ 1719251881Speter SVN_ERR(svn_io_check_path(logfile_path, &logfile_on_disk_kind, 1720251881Speter scratch_pool)); 1721251881Speter if (logfile_on_disk_kind == svn_node_file) 1722251881Speter return svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL, 1723251881Speter _("Cannot upgrade with existing logs; run a " 1724251881Speter "cleanup operation on this working copy using " 1725251881Speter "a client version which is compatible with this " 1726251881Speter "working copy's format (such as the version " 1727251881Speter "you are upgrading from), then retry the " 1728251881Speter "upgrade with the current version")); 1729251881Speter 1730251881Speter /* Lock this working copy directory, or steal an existing lock. Do this 1731251881Speter BEFORE we read the entries. We don't want another process to modify the 1732251881Speter entries after we've read them into memory. */ 1733251881Speter SVN_ERR(create_physical_lock(dir_abspath, scratch_pool)); 1734251881Speter 1735251881Speter /* What's going on here? 1736251881Speter * 1737251881Speter * We're attempting to upgrade an older working copy to the new wc-ng format. 1738251881Speter * The semantics and storage mechanisms between the two are vastly different, 1739251881Speter * so it's going to be a bit painful. Here's a plan for the operation: 1740251881Speter * 1741251881Speter * 1) Read the old 'entries' using the old-format reader. 1742251881Speter * 1743251881Speter * 2) Create the new DB if it hasn't already been created. 1744251881Speter * 1745251881Speter * 3) Use our compatibility code for writing entries to fill out the (new) 1746251881Speter * DB state. Use the remembered checksums, since an entry has only the 1747251881Speter * MD5 not the SHA1 checksum, and in the case of a revert-base doesn't 1748251881Speter * even have that. 1749251881Speter * 1750251881Speter * 4) Convert wcprop to the wc-ng format 1751251881Speter * 1752251881Speter * 5) Migrate regular properties to the WC-NG DB. 1753251881Speter */ 1754251881Speter 1755251881Speter /***** ENTRIES - READ *****/ 1756251881Speter SVN_ERR(svn_wc__read_entries_old(&entries, dir_abspath, 1757251881Speter scratch_pool, scratch_pool)); 1758251881Speter 1759251881Speter this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR); 1760251881Speter SVN_ERR(ensure_repos_info(this_dir, dir_abspath, 1761251881Speter repos_info_func, repos_info_baton, 1762251881Speter repos_cache, 1763251881Speter scratch_pool, scratch_pool)); 1764251881Speter 1765251881Speter /* Cache repos UUID pairs for when a subdir doesn't have this information */ 1766251881Speter if (!svn_hash_gets(repos_cache, this_dir->repos)) 1767251881Speter { 1768251881Speter apr_pool_t *hash_pool = apr_hash_pool_get(repos_cache); 1769251881Speter 1770251881Speter svn_hash_sets(repos_cache, 1771251881Speter apr_pstrdup(hash_pool, this_dir->repos), 1772251881Speter apr_pstrdup(hash_pool, this_dir->uuid)); 1773251881Speter } 1774251881Speter 1775251881Speter old_wcroot_abspath = svn_dirent_get_longest_ancestor(dir_abspath, 1776251881Speter data->root_abspath, 1777251881Speter scratch_pool); 1778251881Speter dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath, dir_abspath); 1779251881Speter 1780251881Speter /***** TEXT BASES *****/ 1781251881Speter SVN_ERR(migrate_text_bases(&text_bases_info, dir_abspath, data->root_abspath, 1782251881Speter data->sdb, scratch_pool, scratch_pool)); 1783251881Speter 1784251881Speter /***** ENTRIES - WRITE *****/ 1785251881Speter err = svn_wc__write_upgraded_entries(dir_baton, parent_baton, db, data->sdb, 1786251881Speter data->repos_id, data->wc_id, 1787251881Speter dir_abspath, data->root_abspath, 1788251881Speter entries, text_bases_info, 1789251881Speter result_pool, scratch_pool); 1790251881Speter if (err && err->apr_err == SVN_ERR_WC_CORRUPT) 1791251881Speter return svn_error_quick_wrap(err, 1792251881Speter _("This working copy is corrupt and " 1793251881Speter "cannot be upgraded. Please check out " 1794251881Speter "a new working copy.")); 1795251881Speter else 1796251881Speter SVN_ERR(err); 1797251881Speter 1798251881Speter /***** WC PROPS *****/ 1799251881Speter /* If we don't know precisely where the wcprops are, ignore them. */ 1800251881Speter if (old_format != SVN_WC__WCPROPS_LOST) 1801251881Speter { 1802251881Speter apr_hash_t *all_wcprops; 1803251881Speter 1804251881Speter if (old_format <= SVN_WC__WCPROPS_MANY_FILES_VERSION) 1805251881Speter SVN_ERR(read_many_wcprops(&all_wcprops, dir_abspath, 1806251881Speter scratch_pool, scratch_pool)); 1807251881Speter else 1808251881Speter SVN_ERR(read_wcprops(&all_wcprops, dir_abspath, 1809251881Speter scratch_pool, scratch_pool)); 1810251881Speter 1811251881Speter SVN_ERR(svn_wc__db_upgrade_apply_dav_cache(data->sdb, dir_relpath, 1812251881Speter all_wcprops, scratch_pool)); 1813251881Speter } 1814251881Speter 1815251881Speter /* Upgrade all the properties (including "this dir"). 1816251881Speter 1817251881Speter Note: this must come AFTER the entries have been migrated into the 1818251881Speter database. The upgrade process needs the children in BASE_NODE and 1819251881Speter WORKING_NODE, and to examine the resultant WORKING state. */ 1820251881Speter SVN_ERR(migrate_props(dir_abspath, data->root_abspath, data->sdb, old_format, 1821251881Speter wc_id, scratch_pool)); 1822251881Speter 1823251881Speter return SVN_NO_ERROR; 1824251881Speter} 1825251881Speter 1826251881Speterconst char * 1827251881Spetersvn_wc__version_string_from_format(int wc_format) 1828251881Speter{ 1829251881Speter switch (wc_format) 1830251881Speter { 1831251881Speter case 4: return "<=1.3"; 1832251881Speter case 8: return "1.4"; 1833251881Speter case 9: return "1.5"; 1834251881Speter case 10: return "1.6"; 1835251881Speter case SVN_WC__WC_NG_VERSION: return "1.7"; 1836251881Speter } 1837251881Speter return _("(unreleased development version)"); 1838251881Speter} 1839251881Speter 1840251881Spetersvn_error_t * 1841251881Spetersvn_wc__upgrade_sdb(int *result_format, 1842251881Speter const char *wcroot_abspath, 1843251881Speter svn_sqlite__db_t *sdb, 1844251881Speter int start_format, 1845251881Speter apr_pool_t *scratch_pool) 1846251881Speter{ 1847251881Speter struct bump_baton bb; 1848251881Speter 1849251881Speter bb.wcroot_abspath = wcroot_abspath; 1850251881Speter 1851251881Speter if (start_format < SVN_WC__WC_NG_VERSION /* 12 */) 1852251881Speter return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL, 1853251881Speter _("Working copy '%s' is too old (format %d, " 1854251881Speter "created by Subversion %s)"), 1855251881Speter svn_dirent_local_style(wcroot_abspath, 1856251881Speter scratch_pool), 1857251881Speter start_format, 1858251881Speter svn_wc__version_string_from_format(start_format)); 1859251881Speter 1860251881Speter /* Early WCNG formats no longer supported. */ 1861251881Speter if (start_format < 19) 1862251881Speter return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL, 1863251881Speter _("Working copy '%s' is an old development " 1864251881Speter "version (format %d); to upgrade it, " 1865251881Speter "use a format 18 client, then " 1866251881Speter "use 'tools/dev/wc-ng/bump-to-19.py', then " 1867251881Speter "use the current client"), 1868251881Speter svn_dirent_local_style(wcroot_abspath, 1869251881Speter scratch_pool), 1870251881Speter start_format); 1871251881Speter 1872251881Speter /* ### need lock-out. only one upgrade at a time. note that other code 1873251881Speter ### cannot use this un-upgraded database until we finish the upgrade. */ 1874251881Speter 1875251881Speter /* Note: none of these have "break" statements; the fall-through is 1876251881Speter intentional. */ 1877251881Speter switch (start_format) 1878251881Speter { 1879251881Speter case 19: 1880251881Speter SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_20, &bb, 1881251881Speter scratch_pool)); 1882251881Speter *result_format = 20; 1883251881Speter /* FALLTHROUGH */ 1884251881Speter 1885251881Speter case 20: 1886251881Speter SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_21, &bb, 1887251881Speter scratch_pool)); 1888251881Speter *result_format = 21; 1889251881Speter /* FALLTHROUGH */ 1890251881Speter 1891251881Speter case 21: 1892251881Speter SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_22, &bb, 1893251881Speter scratch_pool)); 1894251881Speter *result_format = 22; 1895251881Speter /* FALLTHROUGH */ 1896251881Speter 1897251881Speter case 22: 1898251881Speter SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_23, &bb, 1899251881Speter scratch_pool)); 1900251881Speter *result_format = 23; 1901251881Speter /* FALLTHROUGH */ 1902251881Speter 1903251881Speter case 23: 1904251881Speter SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_24, &bb, 1905251881Speter scratch_pool)); 1906251881Speter *result_format = 24; 1907251881Speter /* FALLTHROUGH */ 1908251881Speter 1909251881Speter case 24: 1910251881Speter SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_25, &bb, 1911251881Speter scratch_pool)); 1912251881Speter *result_format = 25; 1913251881Speter /* FALLTHROUGH */ 1914251881Speter 1915251881Speter case 25: 1916251881Speter SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_26, &bb, 1917251881Speter scratch_pool)); 1918251881Speter *result_format = 26; 1919251881Speter /* FALLTHROUGH */ 1920251881Speter 1921251881Speter case 26: 1922251881Speter SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_27, &bb, 1923251881Speter scratch_pool)); 1924251881Speter *result_format = 27; 1925251881Speter /* FALLTHROUGH */ 1926251881Speter 1927251881Speter case 27: 1928251881Speter SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_28, &bb, 1929251881Speter scratch_pool)); 1930251881Speter *result_format = 28; 1931251881Speter /* FALLTHROUGH */ 1932251881Speter 1933251881Speter case 28: 1934251881Speter SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_29, &bb, 1935251881Speter scratch_pool)); 1936251881Speter *result_format = 29; 1937251881Speter /* FALLTHROUGH */ 1938251881Speter 1939251881Speter case 29: 1940251881Speter SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_30, &bb, 1941251881Speter scratch_pool)); 1942251881Speter *result_format = 30; 1943251881Speter 1944251881Speter case 30: 1945251881Speter SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_31, &bb, 1946251881Speter scratch_pool)); 1947251881Speter *result_format = 31; 1948251881Speter /* FALLTHROUGH */ 1949251881Speter /* ### future bumps go here. */ 1950251881Speter#if 0 1951251881Speter case XXX-1: 1952251881Speter /* Revamp the recording of tree conflicts. */ 1953251881Speter SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_XXX, &bb, 1954251881Speter scratch_pool)); 1955251881Speter *result_format = XXX; 1956251881Speter /* FALLTHROUGH */ 1957251881Speter#endif 1958251881Speter case SVN_WC__VERSION: 1959251881Speter /* already upgraded */ 1960251881Speter *result_format = SVN_WC__VERSION; 1961262253Speter 1962262253Speter SVN_SQLITE__WITH_LOCK( 1963262253Speter svn_wc__db_install_schema_statistics(sdb, scratch_pool), 1964262253Speter sdb); 1965251881Speter } 1966251881Speter 1967251881Speter#ifdef SVN_DEBUG 1968251881Speter if (*result_format != start_format) 1969251881Speter { 1970251881Speter int schema_version; 1971251881Speter SVN_ERR(svn_sqlite__read_schema_version(&schema_version, sdb, scratch_pool)); 1972251881Speter 1973251881Speter /* If this assertion fails the schema isn't updated correctly */ 1974251881Speter SVN_ERR_ASSERT(schema_version == *result_format); 1975251881Speter } 1976251881Speter#endif 1977251881Speter 1978251881Speter /* Zap anything that might be remaining or escaped our notice. */ 1979251881Speter wipe_obsolete_files(wcroot_abspath, scratch_pool); 1980251881Speter 1981251881Speter return SVN_NO_ERROR; 1982251881Speter} 1983251881Speter 1984251881Speter 1985251881Speter/* */ 1986251881Speterstatic svn_error_t * 1987251881Speterupgrade_working_copy(void *parent_baton, 1988251881Speter svn_wc__db_t *db, 1989251881Speter const char *dir_abspath, 1990251881Speter svn_wc_upgrade_get_repos_info_t repos_info_func, 1991251881Speter void *repos_info_baton, 1992251881Speter apr_hash_t *repos_cache, 1993251881Speter const struct upgrade_data_t *data, 1994251881Speter svn_cancel_func_t cancel_func, 1995251881Speter void *cancel_baton, 1996251881Speter svn_wc_notify_func2_t notify_func, 1997251881Speter void *notify_baton, 1998251881Speter apr_pool_t *result_pool, 1999251881Speter apr_pool_t *scratch_pool) 2000251881Speter{ 2001251881Speter void *dir_baton; 2002251881Speter int old_format; 2003251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 2004251881Speter apr_array_header_t *subdirs; 2005251881Speter svn_error_t *err; 2006251881Speter int i; 2007251881Speter 2008251881Speter if (cancel_func) 2009251881Speter SVN_ERR(cancel_func(cancel_baton)); 2010251881Speter 2011251881Speter SVN_ERR(svn_wc__db_temp_get_format(&old_format, db, dir_abspath, 2012251881Speter iterpool)); 2013251881Speter 2014251881Speter if (old_format >= SVN_WC__WC_NG_VERSION) 2015251881Speter { 2016251881Speter if (notify_func) 2017251881Speter notify_func(notify_baton, 2018251881Speter svn_wc_create_notify(dir_abspath, svn_wc_notify_skip, 2019251881Speter iterpool), 2020251881Speter iterpool); 2021251881Speter svn_pool_destroy(iterpool); 2022251881Speter return SVN_NO_ERROR; 2023251881Speter } 2024251881Speter 2025251881Speter err = get_versioned_subdirs(&subdirs, NULL, dir_abspath, FALSE, 2026251881Speter scratch_pool, iterpool); 2027251881Speter if (err) 2028251881Speter { 2029251881Speter if (APR_STATUS_IS_ENOENT(err->apr_err) 2030251881Speter || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)) 2031251881Speter { 2032251881Speter /* An unversioned dir is obstructing a versioned dir */ 2033251881Speter svn_error_clear(err); 2034251881Speter err = NULL; 2035251881Speter if (notify_func) 2036251881Speter notify_func(notify_baton, 2037251881Speter svn_wc_create_notify(dir_abspath, svn_wc_notify_skip, 2038251881Speter iterpool), 2039251881Speter iterpool); 2040251881Speter } 2041251881Speter svn_pool_destroy(iterpool); 2042251881Speter return err; 2043251881Speter } 2044251881Speter 2045251881Speter 2046251881Speter SVN_ERR(upgrade_to_wcng(&dir_baton, parent_baton, db, dir_abspath, 2047251881Speter old_format, data->wc_id, 2048251881Speter repos_info_func, repos_info_baton, 2049251881Speter repos_cache, data, scratch_pool, iterpool)); 2050251881Speter 2051251881Speter if (notify_func) 2052251881Speter notify_func(notify_baton, 2053251881Speter svn_wc_create_notify(dir_abspath, svn_wc_notify_upgraded_path, 2054251881Speter iterpool), 2055251881Speter iterpool); 2056251881Speter 2057251881Speter for (i = 0; i < subdirs->nelts; ++i) 2058251881Speter { 2059251881Speter const char *child_abspath = APR_ARRAY_IDX(subdirs, i, const char *); 2060251881Speter 2061251881Speter svn_pool_clear(iterpool); 2062251881Speter 2063251881Speter SVN_ERR(upgrade_working_copy(dir_baton, db, child_abspath, 2064251881Speter repos_info_func, repos_info_baton, 2065251881Speter repos_cache, data, 2066251881Speter cancel_func, cancel_baton, 2067251881Speter notify_func, notify_baton, 2068251881Speter iterpool, iterpool)); 2069251881Speter } 2070251881Speter 2071251881Speter svn_pool_destroy(iterpool); 2072251881Speter 2073251881Speter return SVN_NO_ERROR; 2074251881Speter} 2075251881Speter 2076251881Speter 2077251881Speter/* Return a verbose error if LOCAL_ABSPATH is a not a pre-1.7 working 2078251881Speter copy root */ 2079251881Speterstatic svn_error_t * 2080251881Speteris_old_wcroot(const char *local_abspath, 2081251881Speter apr_pool_t *scratch_pool) 2082251881Speter{ 2083251881Speter apr_hash_t *entries; 2084251881Speter const char *parent_abspath, *name; 2085251881Speter svn_wc_entry_t *entry; 2086251881Speter svn_error_t *err = svn_wc__read_entries_old(&entries, local_abspath, 2087251881Speter scratch_pool, scratch_pool); 2088251881Speter if (err) 2089251881Speter { 2090251881Speter return svn_error_createf( 2091251881Speter SVN_ERR_WC_INVALID_OP_ON_CWD, err, 2092251881Speter _("Can't upgrade '%s' as it is not a working copy"), 2093251881Speter svn_dirent_local_style(local_abspath, scratch_pool)); 2094251881Speter } 2095251881Speter else if (svn_dirent_is_root(local_abspath, strlen(local_abspath))) 2096251881Speter return SVN_NO_ERROR; 2097251881Speter 2098251881Speter svn_dirent_split(&parent_abspath, &name, local_abspath, scratch_pool); 2099251881Speter 2100251881Speter err = svn_wc__read_entries_old(&entries, parent_abspath, 2101251881Speter scratch_pool, scratch_pool); 2102251881Speter if (err) 2103251881Speter { 2104251881Speter svn_error_clear(err); 2105251881Speter return SVN_NO_ERROR; 2106251881Speter } 2107251881Speter 2108251881Speter entry = svn_hash_gets(entries, name); 2109251881Speter if (!entry 2110251881Speter || entry->absent 2111251881Speter || (entry->deleted && entry->schedule != svn_wc_schedule_add) 2112251881Speter || entry->depth == svn_depth_exclude) 2113251881Speter { 2114251881Speter return SVN_NO_ERROR; 2115251881Speter } 2116251881Speter 2117251881Speter while (!svn_dirent_is_root(parent_abspath, strlen(parent_abspath))) 2118251881Speter { 2119251881Speter svn_dirent_split(&parent_abspath, &name, parent_abspath, scratch_pool); 2120251881Speter err = svn_wc__read_entries_old(&entries, parent_abspath, 2121251881Speter scratch_pool, scratch_pool); 2122251881Speter if (err) 2123251881Speter { 2124251881Speter svn_error_clear(err); 2125251881Speter parent_abspath = svn_dirent_join(parent_abspath, name, scratch_pool); 2126251881Speter break; 2127251881Speter } 2128251881Speter entry = svn_hash_gets(entries, name); 2129251881Speter if (!entry 2130251881Speter || entry->absent 2131251881Speter || (entry->deleted && entry->schedule != svn_wc_schedule_add) 2132251881Speter || entry->depth == svn_depth_exclude) 2133251881Speter { 2134251881Speter parent_abspath = svn_dirent_join(parent_abspath, name, scratch_pool); 2135251881Speter break; 2136251881Speter } 2137251881Speter } 2138251881Speter 2139251881Speter return svn_error_createf( 2140251881Speter SVN_ERR_WC_INVALID_OP_ON_CWD, NULL, 2141251881Speter _("Can't upgrade '%s' as it is not a working copy root," 2142251881Speter " the root is '%s'"), 2143251881Speter svn_dirent_local_style(local_abspath, scratch_pool), 2144251881Speter svn_dirent_local_style(parent_abspath, scratch_pool)); 2145251881Speter} 2146251881Speter 2147251881Speter/* Data for upgrade_working_copy_txn(). */ 2148251881Spetertypedef struct upgrade_working_copy_baton_t 2149251881Speter{ 2150251881Speter svn_wc__db_t *db; 2151251881Speter const char *dir_abspath; 2152251881Speter svn_wc_upgrade_get_repos_info_t repos_info_func; 2153251881Speter void *repos_info_baton; 2154251881Speter apr_hash_t *repos_cache; 2155251881Speter const struct upgrade_data_t *data; 2156251881Speter svn_cancel_func_t cancel_func; 2157251881Speter void *cancel_baton; 2158251881Speter svn_wc_notify_func2_t notify_func; 2159251881Speter void *notify_baton; 2160251881Speter apr_pool_t *result_pool; 2161251881Speter} upgrade_working_copy_baton_t; 2162251881Speter 2163251881Speter 2164251881Speter/* Helper for svn_wc_upgrade. Implements svn_sqlite__transaction_callback_t */ 2165251881Speterstatic svn_error_t * 2166251881Speterupgrade_working_copy_txn(void *baton, 2167251881Speter svn_sqlite__db_t *sdb, 2168251881Speter apr_pool_t *scratch_pool) 2169251881Speter{ 2170251881Speter upgrade_working_copy_baton_t *b = baton; 2171251881Speter 2172251881Speter /* Upgrade the pre-wcng into a wcng in a temporary location. */ 2173251881Speter return(upgrade_working_copy(NULL, b->db, b->dir_abspath, 2174251881Speter b->repos_info_func, b->repos_info_baton, 2175251881Speter b->repos_cache, b->data, 2176251881Speter b->cancel_func, b->cancel_baton, 2177251881Speter b->notify_func, b->notify_baton, 2178251881Speter b->result_pool, scratch_pool)); 2179251881Speter} 2180251881Speter 2181251881Spetersvn_error_t * 2182251881Spetersvn_wc_upgrade(svn_wc_context_t *wc_ctx, 2183251881Speter const char *local_abspath, 2184251881Speter svn_wc_upgrade_get_repos_info_t repos_info_func, 2185251881Speter void *repos_info_baton, 2186251881Speter svn_cancel_func_t cancel_func, 2187251881Speter void *cancel_baton, 2188251881Speter svn_wc_notify_func2_t notify_func, 2189251881Speter void *notify_baton, 2190251881Speter apr_pool_t *scratch_pool) 2191251881Speter{ 2192251881Speter svn_wc__db_t *db; 2193251881Speter struct upgrade_data_t data = { NULL }; 2194251881Speter svn_skel_t *work_item, *work_items = NULL; 2195251881Speter const char *pristine_from, *pristine_to, *db_from, *db_to; 2196251881Speter apr_hash_t *repos_cache = apr_hash_make(scratch_pool); 2197251881Speter svn_wc_entry_t *this_dir; 2198251881Speter apr_hash_t *entries; 2199251881Speter const char *root_adm_abspath; 2200251881Speter upgrade_working_copy_baton_t cb_baton; 2201251881Speter svn_error_t *err; 2202251881Speter int result_format; 2203253734Speter svn_boolean_t bumped_format; 2204251881Speter 2205251881Speter /* Try upgrading a wc-ng-style working copy. */ 2206251881Speter SVN_ERR(svn_wc__db_open(&db, NULL /* ### config */, TRUE, FALSE, 2207251881Speter scratch_pool, scratch_pool)); 2208251881Speter 2209251881Speter 2210253734Speter err = svn_wc__db_bump_format(&result_format, &bumped_format, 2211253734Speter db, local_abspath, 2212251881Speter scratch_pool); 2213251881Speter if (err) 2214251881Speter { 2215251881Speter if (err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED) 2216251881Speter { 2217251881Speter return svn_error_trace( 2218251881Speter svn_error_compose_create( 2219251881Speter err, 2220251881Speter svn_wc__db_close(db))); 2221251881Speter } 2222251881Speter 2223251881Speter svn_error_clear(err); 2224251881Speter /* Pre 1.7: Fall through */ 2225251881Speter } 2226251881Speter else 2227251881Speter { 2228251881Speter /* Auto-upgrade worked! */ 2229251881Speter SVN_ERR(svn_wc__db_close(db)); 2230251881Speter 2231251881Speter SVN_ERR_ASSERT(result_format == SVN_WC__VERSION); 2232251881Speter 2233253734Speter if (bumped_format && notify_func) 2234253734Speter { 2235253734Speter svn_wc_notify_t *notify; 2236253734Speter 2237253734Speter notify = svn_wc_create_notify(local_abspath, 2238253734Speter svn_wc_notify_upgraded_path, 2239253734Speter scratch_pool); 2240253734Speter 2241253734Speter notify_func(notify_baton, notify, scratch_pool); 2242253734Speter } 2243253734Speter 2244251881Speter return SVN_NO_ERROR; 2245251881Speter } 2246251881Speter 2247251881Speter SVN_ERR(is_old_wcroot(local_abspath, scratch_pool)); 2248251881Speter 2249251881Speter /* Given a pre-wcng root some/wc we create a temporary wcng in 2250251881Speter some/wc/.svn/tmp/wcng/wc.db and copy the metadata from one to the 2251251881Speter other, then the temporary wc.db file gets moved into the original 2252251881Speter root. Until the wc.db file is moved the original working copy 2253251881Speter remains a pre-wcng and 'cleanup' with an old client will remove 2254251881Speter the partial upgrade. Moving the wc.db file creates a wcng, and 2255251881Speter 'cleanup' with a new client will complete any outstanding 2256251881Speter upgrade. */ 2257251881Speter 2258251881Speter SVN_ERR(svn_wc__read_entries_old(&entries, local_abspath, 2259251881Speter scratch_pool, scratch_pool)); 2260251881Speter 2261251881Speter this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR); 2262251881Speter SVN_ERR(ensure_repos_info(this_dir, local_abspath, repos_info_func, 2263251881Speter repos_info_baton, repos_cache, 2264251881Speter scratch_pool, scratch_pool)); 2265251881Speter 2266251881Speter /* Cache repos UUID pairs for when a subdir doesn't have this information */ 2267251881Speter if (!svn_hash_gets(repos_cache, this_dir->repos)) 2268251881Speter svn_hash_sets(repos_cache, 2269251881Speter apr_pstrdup(scratch_pool, this_dir->repos), 2270251881Speter apr_pstrdup(scratch_pool, this_dir->uuid)); 2271251881Speter 2272251881Speter /* Create the new DB in the temporary root wc/.svn/tmp/wcng/.svn */ 2273251881Speter data.root_abspath = svn_dirent_join(svn_wc__adm_child(local_abspath, "tmp", 2274251881Speter scratch_pool), 2275251881Speter "wcng", scratch_pool); 2276251881Speter root_adm_abspath = svn_wc__adm_child(data.root_abspath, "", 2277251881Speter scratch_pool); 2278251881Speter SVN_ERR(svn_io_remove_dir2(root_adm_abspath, TRUE, NULL, NULL, 2279251881Speter scratch_pool)); 2280251881Speter SVN_ERR(svn_wc__ensure_directory(root_adm_abspath, scratch_pool)); 2281251881Speter 2282251881Speter /* Create an empty sqlite database for this directory and store it in DB. */ 2283251881Speter SVN_ERR(svn_wc__db_upgrade_begin(&data.sdb, 2284251881Speter &data.repos_id, &data.wc_id, 2285251881Speter db, data.root_abspath, 2286251881Speter this_dir->repos, this_dir->uuid, 2287251881Speter scratch_pool)); 2288251881Speter 2289251881Speter /* Migrate the entries over to the new database. 2290251881Speter ### We need to think about atomicity here. 2291251881Speter 2292251881Speter entries_write_new() writes in current format rather than 2293251881Speter f12. Thus, this function bumps a working copy all the way to 2294251881Speter current. */ 2295251881Speter SVN_ERR(svn_wc__db_wclock_obtain(db, data.root_abspath, 0, FALSE, 2296251881Speter scratch_pool)); 2297251881Speter 2298251881Speter cb_baton.db = db; 2299251881Speter cb_baton.dir_abspath = local_abspath; 2300251881Speter cb_baton.repos_info_func = repos_info_func; 2301251881Speter cb_baton.repos_info_baton = repos_info_baton; 2302251881Speter cb_baton.repos_cache = repos_cache; 2303251881Speter cb_baton.data = &data; 2304251881Speter cb_baton.cancel_func = cancel_func; 2305251881Speter cb_baton.cancel_baton = cancel_baton; 2306251881Speter cb_baton.notify_func = notify_func; 2307251881Speter cb_baton.notify_baton = notify_baton; 2308251881Speter cb_baton.result_pool = scratch_pool; 2309251881Speter 2310251881Speter SVN_ERR(svn_sqlite__with_lock(data.sdb, 2311251881Speter upgrade_working_copy_txn, 2312251881Speter &cb_baton, 2313251881Speter scratch_pool)); 2314251881Speter 2315251881Speter /* A workqueue item to move the pristine dir into place */ 2316251881Speter pristine_from = svn_wc__adm_child(data.root_abspath, PRISTINE_STORAGE_RELPATH, 2317251881Speter scratch_pool); 2318251881Speter pristine_to = svn_wc__adm_child(local_abspath, PRISTINE_STORAGE_RELPATH, 2319251881Speter scratch_pool); 2320251881Speter SVN_ERR(svn_wc__ensure_directory(pristine_from, scratch_pool)); 2321251881Speter SVN_ERR(svn_wc__wq_build_file_move(&work_item, db, local_abspath, 2322251881Speter pristine_from, pristine_to, 2323251881Speter scratch_pool, scratch_pool)); 2324251881Speter work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 2325251881Speter 2326251881Speter /* A workqueue item to remove pre-wcng metadata */ 2327251881Speter SVN_ERR(svn_wc__wq_build_postupgrade(&work_item, scratch_pool)); 2328251881Speter work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 2329251881Speter SVN_ERR(svn_wc__db_wq_add(db, data.root_abspath, work_items, scratch_pool)); 2330251881Speter 2331251881Speter SVN_ERR(svn_wc__db_wclock_release(db, data.root_abspath, scratch_pool)); 2332251881Speter SVN_ERR(svn_wc__db_close(db)); 2333251881Speter 2334251881Speter /* Renaming the db file is what makes the pre-wcng into a wcng */ 2335251881Speter db_from = svn_wc__adm_child(data.root_abspath, SDB_FILE, scratch_pool); 2336251881Speter db_to = svn_wc__adm_child(local_abspath, SDB_FILE, scratch_pool); 2337251881Speter SVN_ERR(svn_io_file_rename(db_from, db_to, scratch_pool)); 2338251881Speter 2339251881Speter /* Now we have a working wcng, tidy up the droppings */ 2340251881Speter SVN_ERR(svn_wc__db_open(&db, NULL /* ### config */, FALSE, FALSE, 2341251881Speter scratch_pool, scratch_pool)); 2342251881Speter SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton, 2343251881Speter scratch_pool)); 2344251881Speter SVN_ERR(svn_wc__db_close(db)); 2345251881Speter 2346251881Speter /* Should we have the workqueue remove this empty dir? */ 2347251881Speter SVN_ERR(svn_io_remove_dir2(data.root_abspath, FALSE, NULL, NULL, 2348251881Speter scratch_pool)); 2349251881Speter 2350251881Speter return SVN_NO_ERROR; 2351251881Speter} 2352251881Speter 2353251881Spetersvn_error_t * 2354251881Spetersvn_wc__upgrade_add_external_info(svn_wc_context_t *wc_ctx, 2355251881Speter const char *local_abspath, 2356251881Speter svn_node_kind_t kind, 2357251881Speter const char *def_local_abspath, 2358251881Speter const char *repos_relpath, 2359251881Speter const char *repos_root_url, 2360251881Speter const char *repos_uuid, 2361251881Speter svn_revnum_t def_peg_revision, 2362251881Speter svn_revnum_t def_revision, 2363251881Speter apr_pool_t *scratch_pool) 2364251881Speter{ 2365251881Speter svn_node_kind_t db_kind; 2366251881Speter switch (kind) 2367251881Speter { 2368251881Speter case svn_node_dir: 2369251881Speter db_kind = svn_node_dir; 2370251881Speter break; 2371251881Speter 2372251881Speter case svn_node_file: 2373251881Speter db_kind = svn_node_file; 2374251881Speter break; 2375251881Speter 2376251881Speter case svn_node_unknown: 2377251881Speter db_kind = svn_node_unknown; 2378251881Speter break; 2379251881Speter 2380251881Speter default: 2381251881Speter SVN_ERR_MALFUNCTION(); 2382251881Speter } 2383251881Speter 2384251881Speter SVN_ERR(svn_wc__db_upgrade_insert_external(wc_ctx->db, local_abspath, 2385251881Speter db_kind, 2386251881Speter svn_dirent_dirname(local_abspath, 2387251881Speter scratch_pool), 2388251881Speter def_local_abspath, repos_relpath, 2389251881Speter repos_root_url, repos_uuid, 2390251881Speter def_peg_revision, def_revision, 2391251881Speter scratch_pool)); 2392251881Speter return SVN_NO_ERROR; 2393251881Speter} 2394