1251881Speter/* load-fs-vtable.c --- dumpstream loader vtable for committing into a 2251881Speter * Subversion filesystem. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter#include "svn_private_config.h" 26251881Speter#include "svn_hash.h" 27251881Speter#include "svn_pools.h" 28251881Speter#include "svn_error.h" 29251881Speter#include "svn_fs.h" 30251881Speter#include "svn_repos.h" 31251881Speter#include "svn_string.h" 32251881Speter#include "svn_props.h" 33251881Speter#include "repos.h" 34251881Speter#include "svn_mergeinfo.h" 35251881Speter#include "svn_checksum.h" 36251881Speter#include "svn_subst.h" 37251881Speter#include "svn_dirent_uri.h" 38251881Speter 39251881Speter#include <apr_lib.h> 40251881Speter 41251881Speter#include "private/svn_fspath.h" 42251881Speter#include "private/svn_dep_compat.h" 43251881Speter#include "private/svn_mergeinfo_private.h" 44289180Speter#include "private/svn_repos_private.h" 45251881Speter 46251881Speter/*----------------------------------------------------------------------*/ 47251881Speter 48251881Speter/** Batons used herein **/ 49251881Speter 50251881Speterstruct parse_baton 51251881Speter{ 52251881Speter svn_repos_t *repos; 53251881Speter svn_fs_t *fs; 54251881Speter 55251881Speter svn_boolean_t use_history; 56251881Speter svn_boolean_t validate_props; 57289180Speter svn_boolean_t ignore_dates; 58251881Speter svn_boolean_t use_pre_commit_hook; 59251881Speter svn_boolean_t use_post_commit_hook; 60251881Speter enum svn_repos_load_uuid uuid_action; 61251881Speter const char *parent_dir; /* repository relpath, or NULL */ 62251881Speter svn_repos_notify_func_t notify_func; 63251881Speter void *notify_baton; 64286506Speter apr_pool_t *notify_pool; /* scratch pool for notifications */ 65251881Speter apr_pool_t *pool; 66251881Speter 67251881Speter /* Start and end (inclusive) of revision range we'll pay attention 68251881Speter to, or a pair of SVN_INVALID_REVNUMs if we're not filtering by 69251881Speter revisions. */ 70251881Speter svn_revnum_t start_rev; 71251881Speter svn_revnum_t end_rev; 72251881Speter 73251881Speter /* A hash mapping copy-from revisions and mergeinfo range revisions 74251881Speter (svn_revnum_t *) in the dump stream to their corresponding revisions 75251881Speter (svn_revnum_t *) in the loaded repository. The hash and its 76251881Speter contents are allocated in POOL. */ 77251881Speter /* ### See http://subversion.tigris.org/issues/show_bug.cgi?id=3903 78251881Speter ### for discussion about improving the memory costs of this mapping. */ 79251881Speter apr_hash_t *rev_map; 80251881Speter 81251881Speter /* The most recent (youngest) revision from the dump stream mapped in 82251881Speter REV_MAP. If no revisions have been mapped yet, this is set to 83251881Speter SVN_INVALID_REVNUM. */ 84251881Speter svn_revnum_t last_rev_mapped; 85251881Speter 86289180Speter /* The oldest revision loaded from the dump stream. If no revisions 87251881Speter have been loaded yet, this is set to SVN_INVALID_REVNUM. */ 88289180Speter svn_revnum_t oldest_dumpstream_rev; 89251881Speter}; 90251881Speter 91251881Speterstruct revision_baton 92251881Speter{ 93289180Speter /* rev num from dump file */ 94251881Speter svn_revnum_t rev; 95251881Speter svn_fs_txn_t *txn; 96251881Speter svn_fs_root_t *txn_root; 97251881Speter 98251881Speter const svn_string_t *datestamp; 99251881Speter 100289180Speter /* (rev num from dump file) minus (rev num to be committed) */ 101251881Speter apr_int32_t rev_offset; 102251881Speter svn_boolean_t skipped; 103251881Speter 104289180Speter /* Array of svn_prop_t with revision properties. */ 105289180Speter apr_array_header_t *revprops; 106289180Speter 107251881Speter struct parse_baton *pb; 108251881Speter apr_pool_t *pool; 109251881Speter}; 110251881Speter 111251881Speterstruct node_baton 112251881Speter{ 113251881Speter const char *path; 114251881Speter svn_node_kind_t kind; 115251881Speter enum svn_node_action action; 116251881Speter svn_checksum_t *base_checksum; /* null, if not available */ 117251881Speter svn_checksum_t *result_checksum; /* null, if not available */ 118251881Speter svn_checksum_t *copy_source_checksum; /* null, if not available */ 119251881Speter 120251881Speter svn_revnum_t copyfrom_rev; 121251881Speter const char *copyfrom_path; 122251881Speter 123251881Speter struct revision_baton *rb; 124251881Speter apr_pool_t *pool; 125251881Speter}; 126251881Speter 127251881Speter 128251881Speter/*----------------------------------------------------------------------*/ 129251881Speter 130251881Speter/* Record the mapping of FROM_REV to TO_REV in REV_MAP, ensuring that 131251881Speter anything added to the hash is allocated in the hash's pool. */ 132251881Speterstatic void 133251881Speterset_revision_mapping(apr_hash_t *rev_map, 134251881Speter svn_revnum_t from_rev, 135251881Speter svn_revnum_t to_rev) 136251881Speter{ 137251881Speter svn_revnum_t *mapped_revs = apr_palloc(apr_hash_pool_get(rev_map), 138251881Speter sizeof(svn_revnum_t) * 2); 139251881Speter mapped_revs[0] = from_rev; 140251881Speter mapped_revs[1] = to_rev; 141251881Speter apr_hash_set(rev_map, mapped_revs, 142251881Speter sizeof(svn_revnum_t), mapped_revs + 1); 143251881Speter} 144251881Speter 145251881Speter/* Return the revision to which FROM_REV maps in REV_MAP, or 146251881Speter SVN_INVALID_REVNUM if no such mapping exists. */ 147251881Speterstatic svn_revnum_t 148251881Speterget_revision_mapping(apr_hash_t *rev_map, 149251881Speter svn_revnum_t from_rev) 150251881Speter{ 151251881Speter svn_revnum_t *to_rev = apr_hash_get(rev_map, &from_rev, 152251881Speter sizeof(from_rev)); 153251881Speter return to_rev ? *to_rev : SVN_INVALID_REVNUM; 154251881Speter} 155251881Speter 156251881Speter 157251881Speter/* Change revision property NAME to VALUE for REVISION in REPOS. If 158251881Speter VALIDATE_PROPS is set, use functions which perform validation of 159251881Speter the property value. Otherwise, bypass those checks. */ 160251881Speterstatic svn_error_t * 161251881Speterchange_rev_prop(svn_repos_t *repos, 162251881Speter svn_revnum_t revision, 163251881Speter const char *name, 164251881Speter const svn_string_t *value, 165251881Speter svn_boolean_t validate_props, 166251881Speter apr_pool_t *pool) 167251881Speter{ 168251881Speter if (validate_props) 169251881Speter return svn_repos_fs_change_rev_prop4(repos, revision, NULL, name, 170251881Speter NULL, value, FALSE, FALSE, 171251881Speter NULL, NULL, pool); 172251881Speter else 173251881Speter return svn_fs_change_rev_prop2(svn_repos_fs(repos), revision, name, 174251881Speter NULL, value, pool); 175251881Speter} 176251881Speter 177251881Speter/* Change property NAME to VALUE for PATH in TXN_ROOT. If 178251881Speter VALIDATE_PROPS is set, use functions which perform validation of 179251881Speter the property value. Otherwise, bypass those checks. */ 180251881Speterstatic svn_error_t * 181251881Speterchange_node_prop(svn_fs_root_t *txn_root, 182251881Speter const char *path, 183251881Speter const char *name, 184251881Speter const svn_string_t *value, 185251881Speter svn_boolean_t validate_props, 186251881Speter apr_pool_t *pool) 187251881Speter{ 188251881Speter if (validate_props) 189251881Speter return svn_repos_fs_change_node_prop(txn_root, path, name, value, pool); 190251881Speter else 191251881Speter return svn_fs_change_node_prop(txn_root, path, name, value, pool); 192251881Speter} 193251881Speter 194251881Speter/* Prepend the mergeinfo source paths in MERGEINFO_ORIG with PARENT_DIR, and 195251881Speter return it in *MERGEINFO_VAL. */ 196251881Speterstatic svn_error_t * 197251881Speterprefix_mergeinfo_paths(svn_string_t **mergeinfo_val, 198251881Speter const svn_string_t *mergeinfo_orig, 199251881Speter const char *parent_dir, 200251881Speter apr_pool_t *pool) 201251881Speter{ 202251881Speter apr_hash_t *prefixed_mergeinfo, *mergeinfo; 203251881Speter apr_hash_index_t *hi; 204251881Speter 205251881Speter SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_orig->data, pool)); 206251881Speter prefixed_mergeinfo = apr_hash_make(pool); 207251881Speter for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) 208251881Speter { 209289180Speter const char *merge_source = apr_hash_this_key(hi); 210289180Speter svn_rangelist_t *rangelist = apr_hash_this_val(hi); 211289180Speter const char *path; 212251881Speter 213289180Speter merge_source = svn_relpath_canonicalize(merge_source, pool); 214251881Speter 215251881Speter /* The svn:mergeinfo property syntax demands a repos abspath */ 216251881Speter path = svn_fspath__canonicalize(svn_relpath_join(parent_dir, 217251881Speter merge_source, pool), 218251881Speter pool); 219251881Speter svn_hash_sets(prefixed_mergeinfo, path, rangelist); 220251881Speter } 221251881Speter return svn_mergeinfo_to_string(mergeinfo_val, prefixed_mergeinfo, pool); 222251881Speter} 223251881Speter 224251881Speter 225251881Speter/* Examine the mergeinfo in INITIAL_VAL, renumber revisions in rangelists 226251881Speter as appropriate, and return the (possibly new) mergeinfo in *FINAL_VAL 227289180Speter (allocated from POOL). 228289180Speter 229289180Speter Adjust any mergeinfo revisions not older than OLDEST_DUMPSTREAM_REV by 230289180Speter using REV_MAP which maps (svn_revnum_t) old rev to (svn_revnum_t) new rev. 231289180Speter 232289180Speter Adjust any mergeinfo revisions older than OLDEST_DUMPSTREAM_REV by 233289180Speter (-OLDER_REVS_OFFSET), dropping any that become <= 0. 234289180Speter */ 235251881Speterstatic svn_error_t * 236251881Speterrenumber_mergeinfo_revs(svn_string_t **final_val, 237251881Speter const svn_string_t *initial_val, 238289180Speter apr_hash_t *rev_map, 239289180Speter svn_revnum_t oldest_dumpstream_rev, 240289180Speter apr_int32_t older_revs_offset, 241251881Speter apr_pool_t *pool) 242251881Speter{ 243251881Speter apr_pool_t *subpool = svn_pool_create(pool); 244251881Speter svn_mergeinfo_t mergeinfo, predates_stream_mergeinfo; 245251881Speter svn_mergeinfo_t final_mergeinfo = apr_hash_make(subpool); 246251881Speter apr_hash_index_t *hi; 247251881Speter 248251881Speter SVN_ERR(svn_mergeinfo_parse(&mergeinfo, initial_val->data, subpool)); 249251881Speter 250251881Speter /* Issue #3020 251251881Speter http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16 252251881Speter Remove mergeinfo older than the oldest revision in the dump stream 253251881Speter and adjust its revisions by the difference between the head rev of 254251881Speter the target repository and the current dump stream rev. */ 255289180Speter if (oldest_dumpstream_rev > 1) 256251881Speter { 257289180Speter /* predates_stream_mergeinfo := mergeinfo that refers to revs before 258289180Speter oldest_dumpstream_rev */ 259251881Speter SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( 260251881Speter &predates_stream_mergeinfo, mergeinfo, 261289180Speter oldest_dumpstream_rev - 1, 0, 262251881Speter TRUE, subpool, subpool)); 263289180Speter /* mergeinfo := mergeinfo that refers to revs >= oldest_dumpstream_rev */ 264251881Speter SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( 265251881Speter &mergeinfo, mergeinfo, 266289180Speter oldest_dumpstream_rev - 1, 0, 267251881Speter FALSE, subpool, subpool)); 268251881Speter SVN_ERR(svn_mergeinfo__adjust_mergeinfo_rangelists( 269251881Speter &predates_stream_mergeinfo, predates_stream_mergeinfo, 270289180Speter -older_revs_offset, subpool, subpool)); 271251881Speter } 272251881Speter else 273251881Speter { 274251881Speter predates_stream_mergeinfo = NULL; 275251881Speter } 276251881Speter 277251881Speter for (hi = apr_hash_first(subpool, mergeinfo); hi; hi = apr_hash_next(hi)) 278251881Speter { 279289180Speter const char *merge_source = apr_hash_this_key(hi); 280289180Speter svn_rangelist_t *rangelist = apr_hash_this_val(hi); 281251881Speter int i; 282251881Speter 283251881Speter /* Possibly renumber revisions in merge source's rangelist. */ 284251881Speter for (i = 0; i < rangelist->nelts; i++) 285251881Speter { 286251881Speter svn_revnum_t rev_from_map; 287251881Speter svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i, 288251881Speter svn_merge_range_t *); 289289180Speter rev_from_map = get_revision_mapping(rev_map, range->start); 290251881Speter if (SVN_IS_VALID_REVNUM(rev_from_map)) 291251881Speter { 292251881Speter range->start = rev_from_map; 293251881Speter } 294289180Speter else if (range->start == oldest_dumpstream_rev - 1) 295251881Speter { 296251881Speter /* Since the start revision of svn_merge_range_t are not 297251881Speter inclusive there is one possible valid start revision that 298289180Speter won't be found in the REV_MAP mapping of load stream 299251881Speter revsions to loaded revisions: The revision immediately 300289180Speter preceding the oldest revision from the load stream. 301251881Speter This is a valid revision for mergeinfo, but not a valid 302289180Speter copy from revision (which REV_MAP also maps for) so it 303251881Speter will never be in the mapping. 304251881Speter 305251881Speter If that is what we have here, then find the mapping for the 306251881Speter oldest rev from the load stream and subtract 1 to get the 307251881Speter renumbered, non-inclusive, start revision. */ 308289180Speter rev_from_map = get_revision_mapping(rev_map, 309289180Speter oldest_dumpstream_rev); 310251881Speter if (SVN_IS_VALID_REVNUM(rev_from_map)) 311251881Speter range->start = rev_from_map - 1; 312251881Speter } 313251881Speter else 314251881Speter { 315251881Speter /* If we can't remap the start revision then don't even bother 316251881Speter trying to remap the end revision. It's possible we might 317251881Speter actually succeed at the latter, which can result in invalid 318251881Speter mergeinfo with a start rev > end rev. If that gets into the 319251881Speter repository then a world of bustage breaks loose anytime that 320251881Speter bogus mergeinfo is parsed. See 321251881Speter http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16. 322251881Speter */ 323251881Speter continue; 324251881Speter } 325251881Speter 326289180Speter rev_from_map = get_revision_mapping(rev_map, range->end); 327251881Speter if (SVN_IS_VALID_REVNUM(rev_from_map)) 328251881Speter range->end = rev_from_map; 329251881Speter } 330251881Speter svn_hash_sets(final_mergeinfo, merge_source, rangelist); 331251881Speter } 332251881Speter 333251881Speter if (predates_stream_mergeinfo) 334289180Speter { 335251881Speter SVN_ERR(svn_mergeinfo_merge2(final_mergeinfo, predates_stream_mergeinfo, 336251881Speter subpool, subpool)); 337289180Speter } 338251881Speter 339286506Speter SVN_ERR(svn_mergeinfo__canonicalize_ranges(final_mergeinfo, subpool)); 340251881Speter 341251881Speter SVN_ERR(svn_mergeinfo_to_string(final_val, final_mergeinfo, pool)); 342251881Speter svn_pool_destroy(subpool); 343251881Speter 344251881Speter return SVN_NO_ERROR; 345251881Speter} 346251881Speter 347251881Speter/*----------------------------------------------------------------------*/ 348251881Speter 349251881Speter/** vtable for doing commits to a fs **/ 350251881Speter 351251881Speter 352289180Speter/* Make a node baton, parsing the relevant HEADERS. 353289180Speter * 354289180Speter * If RB->pb->parent_dir: 355289180Speter * prefix it to NB->path 356289180Speter * prefix it to NB->copyfrom_path (if present) 357289180Speter */ 358251881Speterstatic svn_error_t * 359251881Spetermake_node_baton(struct node_baton **node_baton_p, 360251881Speter apr_hash_t *headers, 361251881Speter struct revision_baton *rb, 362251881Speter apr_pool_t *pool) 363251881Speter{ 364251881Speter struct node_baton *nb = apr_pcalloc(pool, sizeof(*nb)); 365251881Speter const char *val; 366251881Speter 367251881Speter /* Start with sensible defaults. */ 368251881Speter nb->rb = rb; 369251881Speter nb->pool = pool; 370251881Speter nb->kind = svn_node_unknown; 371251881Speter 372251881Speter /* Then add info from the headers. */ 373251881Speter if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_PATH))) 374251881Speter { 375251881Speter val = svn_relpath_canonicalize(val, pool); 376251881Speter if (rb->pb->parent_dir) 377251881Speter nb->path = svn_relpath_join(rb->pb->parent_dir, val, pool); 378251881Speter else 379251881Speter nb->path = val; 380251881Speter } 381251881Speter 382251881Speter if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_KIND))) 383251881Speter { 384251881Speter if (! strcmp(val, "file")) 385251881Speter nb->kind = svn_node_file; 386251881Speter else if (! strcmp(val, "dir")) 387251881Speter nb->kind = svn_node_dir; 388251881Speter } 389251881Speter 390251881Speter nb->action = (enum svn_node_action)(-1); /* an invalid action code */ 391251881Speter if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_ACTION))) 392251881Speter { 393251881Speter if (! strcmp(val, "change")) 394251881Speter nb->action = svn_node_action_change; 395251881Speter else if (! strcmp(val, "add")) 396251881Speter nb->action = svn_node_action_add; 397251881Speter else if (! strcmp(val, "delete")) 398251881Speter nb->action = svn_node_action_delete; 399251881Speter else if (! strcmp(val, "replace")) 400251881Speter nb->action = svn_node_action_replace; 401251881Speter } 402251881Speter 403251881Speter nb->copyfrom_rev = SVN_INVALID_REVNUM; 404251881Speter if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV))) 405251881Speter { 406251881Speter nb->copyfrom_rev = SVN_STR_TO_REV(val); 407251881Speter } 408251881Speter if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH))) 409251881Speter { 410251881Speter val = svn_relpath_canonicalize(val, pool); 411251881Speter if (rb->pb->parent_dir) 412251881Speter nb->copyfrom_path = svn_relpath_join(rb->pb->parent_dir, val, pool); 413251881Speter else 414251881Speter nb->copyfrom_path = val; 415251881Speter } 416251881Speter 417251881Speter if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_CHECKSUM))) 418251881Speter { 419251881Speter SVN_ERR(svn_checksum_parse_hex(&nb->result_checksum, svn_checksum_md5, 420251881Speter val, pool)); 421251881Speter } 422251881Speter 423251881Speter if ((val = svn_hash_gets(headers, 424251881Speter SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_CHECKSUM))) 425251881Speter { 426251881Speter SVN_ERR(svn_checksum_parse_hex(&nb->base_checksum, svn_checksum_md5, val, 427251881Speter pool)); 428251881Speter } 429251881Speter 430251881Speter if ((val = svn_hash_gets(headers, 431251881Speter SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_CHECKSUM))) 432251881Speter { 433251881Speter SVN_ERR(svn_checksum_parse_hex(&nb->copy_source_checksum, 434251881Speter svn_checksum_md5, val, pool)); 435251881Speter } 436251881Speter 437251881Speter /* What's cool about this dump format is that the parser just 438251881Speter ignores any unrecognized headers. :-) */ 439251881Speter 440251881Speter *node_baton_p = nb; 441251881Speter return SVN_NO_ERROR; 442251881Speter} 443251881Speter 444289180Speter/* Make a revision baton, parsing the relevant HEADERS. 445289180Speter * 446289180Speter * Set RB->skipped iff the revision number is outside the range given in PB. 447289180Speter */ 448251881Speterstatic struct revision_baton * 449251881Spetermake_revision_baton(apr_hash_t *headers, 450251881Speter struct parse_baton *pb, 451251881Speter apr_pool_t *pool) 452251881Speter{ 453251881Speter struct revision_baton *rb = apr_pcalloc(pool, sizeof(*rb)); 454251881Speter const char *val; 455251881Speter 456251881Speter rb->pb = pb; 457251881Speter rb->pool = pool; 458251881Speter rb->rev = SVN_INVALID_REVNUM; 459289180Speter rb->revprops = apr_array_make(rb->pool, 8, sizeof(svn_prop_t)); 460251881Speter 461251881Speter if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_REVISION_NUMBER))) 462251881Speter { 463251881Speter rb->rev = SVN_STR_TO_REV(val); 464251881Speter 465251881Speter /* If we're filtering revisions, is this one we'll skip? */ 466251881Speter rb->skipped = (SVN_IS_VALID_REVNUM(pb->start_rev) 467251881Speter && ((rb->rev < pb->start_rev) || 468251881Speter (rb->rev > pb->end_rev))); 469251881Speter } 470251881Speter 471251881Speter return rb; 472251881Speter} 473251881Speter 474251881Speter 475251881Speterstatic svn_error_t * 476251881Speternew_revision_record(void **revision_baton, 477251881Speter apr_hash_t *headers, 478251881Speter void *parse_baton, 479251881Speter apr_pool_t *pool) 480251881Speter{ 481251881Speter struct parse_baton *pb = parse_baton; 482251881Speter struct revision_baton *rb; 483251881Speter svn_revnum_t head_rev; 484251881Speter 485251881Speter rb = make_revision_baton(headers, pb, pool); 486251881Speter 487251881Speter /* ### If we're filtering revisions, and this is one we've skipped, 488251881Speter ### and we've skipped it because it has a revision number younger 489251881Speter ### than the youngest in our acceptable range, then should we 490251881Speter ### just bail out here? */ 491251881Speter /* 492251881Speter if (rb->skipped && (rb->rev > pb->end_rev)) 493251881Speter return svn_error_createf(SVN_ERR_CEASE_INVOCATION, 0, 494251881Speter _("Finished processing acceptable load " 495251881Speter "revision range")); 496251881Speter */ 497251881Speter 498251881Speter SVN_ERR(svn_fs_youngest_rev(&head_rev, pb->fs, pool)); 499251881Speter 500251881Speter /* FIXME: This is a lame fallback loading multiple segments of dump in 501251881Speter several separate operations. It is highly susceptible to race conditions. 502251881Speter Calculate the revision 'offset' for finding copyfrom sources. 503251881Speter It might be positive or negative. */ 504251881Speter rb->rev_offset = (apr_int32_t) ((rb->rev) - (head_rev + 1)); 505251881Speter 506251881Speter if ((rb->rev > 0) && (! rb->skipped)) 507251881Speter { 508251881Speter /* Create a new fs txn. */ 509289180Speter SVN_ERR(svn_fs_begin_txn2(&(rb->txn), pb->fs, head_rev, 510289180Speter SVN_FS_TXN_CLIENT_DATE, pool)); 511251881Speter SVN_ERR(svn_fs_txn_root(&(rb->txn_root), rb->txn, pool)); 512251881Speter 513251881Speter if (pb->notify_func) 514251881Speter { 515286506Speter /* ### TODO: Use proper scratch pool instead of pb->notify_pool */ 516286506Speter svn_repos_notify_t *notify = svn_repos_notify_create( 517286506Speter svn_repos_notify_load_txn_start, 518286506Speter pb->notify_pool); 519286506Speter 520286506Speter notify->old_revision = rb->rev; 521286506Speter pb->notify_func(pb->notify_baton, notify, pb->notify_pool); 522286506Speter svn_pool_clear(pb->notify_pool); 523251881Speter } 524251881Speter 525251881Speter /* Stash the oldest "old" revision committed from the load stream. */ 526289180Speter if (!SVN_IS_VALID_REVNUM(pb->oldest_dumpstream_rev)) 527289180Speter pb->oldest_dumpstream_rev = rb->rev; 528251881Speter } 529251881Speter 530251881Speter /* If we're skipping this revision, try to notify someone. */ 531251881Speter if (rb->skipped && pb->notify_func) 532251881Speter { 533286506Speter /* ### TODO: Use proper scratch pool instead of pb->notify_pool */ 534286506Speter svn_repos_notify_t *notify = svn_repos_notify_create( 535286506Speter svn_repos_notify_load_skipped_rev, 536286506Speter pb->notify_pool); 537286506Speter 538286506Speter notify->old_revision = rb->rev; 539286506Speter pb->notify_func(pb->notify_baton, notify, pb->notify_pool); 540286506Speter svn_pool_clear(pb->notify_pool); 541251881Speter } 542251881Speter 543289180Speter /* If we're parsing revision 0, only the revision props are (possibly) 544251881Speter interesting to us: when loading the stream into an empty 545251881Speter filesystem, then we want new filesystem's revision 0 to have the 546251881Speter same props. Otherwise, we just ignore revision 0 in the stream. */ 547251881Speter 548251881Speter *revision_baton = rb; 549251881Speter return SVN_NO_ERROR; 550251881Speter} 551251881Speter 552251881Speter 553251881Speter 554289180Speter/* Perform a copy or a plain add. 555289180Speter * 556289180Speter * For a copy, also adjust the copy-from rev, check any copy-source checksum, 557289180Speter * and send a notification. 558289180Speter */ 559251881Speterstatic svn_error_t * 560251881Spetermaybe_add_with_history(struct node_baton *nb, 561251881Speter struct revision_baton *rb, 562251881Speter apr_pool_t *pool) 563251881Speter{ 564251881Speter struct parse_baton *pb = rb->pb; 565251881Speter 566251881Speter if ((nb->copyfrom_path == NULL) || (! pb->use_history)) 567251881Speter { 568251881Speter /* Add empty file or dir, without history. */ 569251881Speter if (nb->kind == svn_node_file) 570251881Speter SVN_ERR(svn_fs_make_file(rb->txn_root, nb->path, pool)); 571251881Speter 572251881Speter else if (nb->kind == svn_node_dir) 573251881Speter SVN_ERR(svn_fs_make_dir(rb->txn_root, nb->path, pool)); 574251881Speter } 575251881Speter else 576251881Speter { 577251881Speter /* Hunt down the source revision in this fs. */ 578251881Speter svn_fs_root_t *copy_root; 579251881Speter svn_revnum_t copyfrom_rev; 580251881Speter 581251881Speter /* Try to find the copyfrom revision in the revision map; 582251881Speter failing that, fall back to the revision offset approach. */ 583251881Speter copyfrom_rev = get_revision_mapping(rb->pb->rev_map, nb->copyfrom_rev); 584251881Speter if (! SVN_IS_VALID_REVNUM(copyfrom_rev)) 585251881Speter copyfrom_rev = nb->copyfrom_rev - rb->rev_offset; 586251881Speter 587251881Speter if (! SVN_IS_VALID_REVNUM(copyfrom_rev)) 588251881Speter return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, 589251881Speter _("Relative source revision %ld is not" 590251881Speter " available in current repository"), 591251881Speter copyfrom_rev); 592251881Speter 593251881Speter SVN_ERR(svn_fs_revision_root(©_root, pb->fs, copyfrom_rev, pool)); 594251881Speter 595251881Speter if (nb->copy_source_checksum) 596251881Speter { 597251881Speter svn_checksum_t *checksum; 598251881Speter SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, copy_root, 599251881Speter nb->copyfrom_path, TRUE, pool)); 600251881Speter if (!svn_checksum_match(nb->copy_source_checksum, checksum)) 601251881Speter return svn_checksum_mismatch_err(nb->copy_source_checksum, 602251881Speter checksum, pool, 603251881Speter _("Copy source checksum mismatch on copy from '%s'@%ld\n" 604251881Speter "to '%s' in rev based on r%ld"), 605251881Speter nb->copyfrom_path, copyfrom_rev, nb->path, rb->rev); 606251881Speter } 607251881Speter 608251881Speter SVN_ERR(svn_fs_copy(copy_root, nb->copyfrom_path, 609251881Speter rb->txn_root, nb->path, pool)); 610251881Speter 611251881Speter if (pb->notify_func) 612251881Speter { 613286506Speter /* ### TODO: Use proper scratch pool instead of pb->notify_pool */ 614286506Speter svn_repos_notify_t *notify = svn_repos_notify_create( 615286506Speter svn_repos_notify_load_copied_node, 616286506Speter pb->notify_pool); 617286506Speter 618286506Speter pb->notify_func(pb->notify_baton, notify, pb->notify_pool); 619286506Speter svn_pool_clear(pb->notify_pool); 620251881Speter } 621251881Speter } 622251881Speter 623251881Speter return SVN_NO_ERROR; 624251881Speter} 625251881Speter 626251881Speterstatic svn_error_t * 627251881Spetermagic_header_record(int version, 628251881Speter void *parse_baton, 629251881Speter apr_pool_t *pool) 630251881Speter{ 631251881Speter return SVN_NO_ERROR; 632251881Speter} 633251881Speter 634251881Speterstatic svn_error_t * 635251881Speteruuid_record(const char *uuid, 636251881Speter void *parse_baton, 637251881Speter apr_pool_t *pool) 638251881Speter{ 639251881Speter struct parse_baton *pb = parse_baton; 640251881Speter svn_revnum_t youngest_rev; 641251881Speter 642251881Speter if (pb->uuid_action == svn_repos_load_uuid_ignore) 643251881Speter return SVN_NO_ERROR; 644251881Speter 645251881Speter if (pb->uuid_action != svn_repos_load_uuid_force) 646251881Speter { 647251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest_rev, pb->fs, pool)); 648251881Speter if (youngest_rev != 0) 649251881Speter return SVN_NO_ERROR; 650251881Speter } 651251881Speter 652251881Speter return svn_fs_set_uuid(pb->fs, uuid, pool); 653251881Speter} 654251881Speter 655251881Speterstatic svn_error_t * 656251881Speternew_node_record(void **node_baton, 657251881Speter apr_hash_t *headers, 658251881Speter void *revision_baton, 659251881Speter apr_pool_t *pool) 660251881Speter{ 661251881Speter struct revision_baton *rb = revision_baton; 662251881Speter struct parse_baton *pb = rb->pb; 663251881Speter struct node_baton *nb; 664251881Speter 665251881Speter if (rb->rev == 0) 666251881Speter return svn_error_create(SVN_ERR_STREAM_MALFORMED_DATA, NULL, 667251881Speter _("Malformed dumpstream: " 668251881Speter "Revision 0 must not contain node records")); 669251881Speter 670251881Speter SVN_ERR(make_node_baton(&nb, headers, rb, pool)); 671251881Speter 672251881Speter /* If we're skipping this revision, we're done here. */ 673251881Speter if (rb->skipped) 674251881Speter { 675251881Speter *node_baton = nb; 676251881Speter return SVN_NO_ERROR; 677251881Speter } 678251881Speter 679251881Speter /* Make sure we have an action we recognize. */ 680251881Speter if (nb->action < svn_node_action_change 681251881Speter || nb->action > svn_node_action_replace) 682251881Speter return svn_error_createf(SVN_ERR_STREAM_UNRECOGNIZED_DATA, NULL, 683251881Speter _("Unrecognized node-action on node '%s'"), 684251881Speter nb->path); 685251881Speter 686251881Speter if (pb->notify_func) 687251881Speter { 688286506Speter /* ### TODO: Use proper scratch pool instead of pb->notify_pool */ 689286506Speter svn_repos_notify_t *notify = svn_repos_notify_create( 690286506Speter svn_repos_notify_load_node_start, 691286506Speter pb->notify_pool); 692286506Speter 693286506Speter notify->path = nb->path; 694286506Speter pb->notify_func(pb->notify_baton, notify, pb->notify_pool); 695286506Speter svn_pool_clear(pb->notify_pool); 696251881Speter } 697251881Speter 698251881Speter switch (nb->action) 699251881Speter { 700251881Speter case svn_node_action_change: 701251881Speter break; 702251881Speter 703251881Speter case svn_node_action_delete: 704251881Speter SVN_ERR(svn_fs_delete(rb->txn_root, nb->path, pool)); 705251881Speter break; 706251881Speter 707251881Speter case svn_node_action_add: 708251881Speter SVN_ERR(maybe_add_with_history(nb, rb, pool)); 709251881Speter break; 710251881Speter 711251881Speter case svn_node_action_replace: 712251881Speter SVN_ERR(svn_fs_delete(rb->txn_root, nb->path, pool)); 713251881Speter SVN_ERR(maybe_add_with_history(nb, rb, pool)); 714251881Speter break; 715251881Speter } 716251881Speter 717251881Speter *node_baton = nb; 718251881Speter return SVN_NO_ERROR; 719251881Speter} 720251881Speter 721251881Speterstatic svn_error_t * 722251881Speterset_revision_property(void *baton, 723251881Speter const char *name, 724251881Speter const svn_string_t *value) 725251881Speter{ 726251881Speter struct revision_baton *rb = baton; 727289180Speter struct parse_baton *pb = rb->pb; 728289180Speter svn_boolean_t is_date = strcmp(name, SVN_PROP_REVISION_DATE) == 0; 729289180Speter svn_prop_t *prop; 730251881Speter 731251881Speter /* If we're skipping this revision, we're done here. */ 732251881Speter if (rb->skipped) 733251881Speter return SVN_NO_ERROR; 734251881Speter 735289180Speter /* If we're ignoring dates, and this is one, we're done here. */ 736289180Speter if (is_date && pb->ignore_dates) 737289180Speter return SVN_NO_ERROR; 738251881Speter 739289180Speter /* Collect property changes to apply them in one FS call in 740289180Speter close_revision. */ 741289180Speter prop = &APR_ARRAY_PUSH(rb->revprops, svn_prop_t); 742289180Speter prop->name = apr_pstrdup(rb->pool, name); 743289180Speter prop->value = svn_string_dup(value, rb->pool); 744251881Speter 745289180Speter /* Remember any datestamp that passes through! (See comment in 746289180Speter close_revision() below.) */ 747289180Speter if (is_date) 748289180Speter rb->datestamp = svn_string_dup(value, rb->pool); 749251881Speter 750251881Speter return SVN_NO_ERROR; 751251881Speter} 752251881Speter 753251881Speter 754289180Spetersvn_error_t * 755289180Spetersvn_repos__adjust_mergeinfo_property(svn_string_t **new_value_p, 756289180Speter const svn_string_t *old_value, 757289180Speter const char *parent_dir, 758289180Speter apr_hash_t *rev_map, 759289180Speter svn_revnum_t oldest_dumpstream_rev, 760289180Speter apr_int32_t older_revs_offset, 761289180Speter svn_repos_notify_func_t notify_func, 762289180Speter void *notify_baton, 763289180Speter apr_pool_t *result_pool, 764289180Speter apr_pool_t *scratch_pool) 765286506Speter{ 766286506Speter svn_string_t prop_val = *old_value; 767286506Speter 768286506Speter /* Tolerate mergeinfo with "\r\n" line endings because some 769286506Speter dumpstream sources might contain as much. If so normalize 770289180Speter the line endings to '\n' and notify that we have made this 771286506Speter correction. */ 772286506Speter if (strstr(prop_val.data, "\r")) 773286506Speter { 774286506Speter const char *prop_eol_normalized; 775286506Speter 776286506Speter SVN_ERR(svn_subst_translate_cstring2(prop_val.data, 777286506Speter &prop_eol_normalized, 778286506Speter "\n", /* translate to LF */ 779286506Speter FALSE, /* no repair */ 780286506Speter NULL, /* no keywords */ 781286506Speter FALSE, /* no expansion */ 782286506Speter result_pool)); 783286506Speter prop_val.data = prop_eol_normalized; 784286506Speter prop_val.len = strlen(prop_eol_normalized); 785286506Speter 786289180Speter if (notify_func) 787286506Speter { 788286506Speter svn_repos_notify_t *notify 789286506Speter = svn_repos_notify_create( 790286506Speter svn_repos_notify_load_normalized_mergeinfo, 791289180Speter scratch_pool); 792286506Speter 793289180Speter notify_func(notify_baton, notify, scratch_pool); 794286506Speter } 795286506Speter } 796286506Speter 797286506Speter /* Renumber mergeinfo as appropriate. */ 798289180Speter SVN_ERR(renumber_mergeinfo_revs(new_value_p, &prop_val, 799289180Speter rev_map, oldest_dumpstream_rev, 800289180Speter older_revs_offset, 801286506Speter result_pool)); 802289180Speter 803289180Speter if (parent_dir) 804286506Speter { 805289180Speter /* Prefix the merge source paths with PARENT_DIR. */ 806286506Speter /* ASSUMPTION: All source paths are included in the dump stream. */ 807286506Speter SVN_ERR(prefix_mergeinfo_paths(new_value_p, *new_value_p, 808289180Speter parent_dir, result_pool)); 809286506Speter } 810286506Speter 811286506Speter return SVN_NO_ERROR; 812286506Speter} 813286506Speter 814286506Speter 815286506Speterstatic svn_error_t * 816251881Speterset_node_property(void *baton, 817251881Speter const char *name, 818251881Speter const svn_string_t *value) 819251881Speter{ 820251881Speter struct node_baton *nb = baton; 821251881Speter struct revision_baton *rb = nb->rb; 822251881Speter struct parse_baton *pb = rb->pb; 823251881Speter 824251881Speter /* If we're skipping this revision, we're done here. */ 825251881Speter if (rb->skipped) 826251881Speter return SVN_NO_ERROR; 827251881Speter 828286506Speter /* Adjust mergeinfo. If this fails, presumably because the mergeinfo 829286506Speter property has an ill-formed value, then we must not fail to load 830286506Speter the repository (at least if it's a simple load with no revision 831286506Speter offset adjustments, path changes, etc.) so just warn and leave it 832286506Speter as it is. */ 833251881Speter if (strcmp(name, SVN_PROP_MERGEINFO) == 0) 834251881Speter { 835286506Speter svn_string_t *new_value; 836286506Speter svn_error_t *err; 837251881Speter 838289180Speter err = svn_repos__adjust_mergeinfo_property(&new_value, value, 839289180Speter pb->parent_dir, 840289180Speter pb->rev_map, 841289180Speter pb->oldest_dumpstream_rev, 842289180Speter rb->rev_offset, 843289180Speter pb->notify_func, pb->notify_baton, 844289180Speter nb->pool, pb->notify_pool); 845289180Speter svn_pool_clear(pb->notify_pool); 846286506Speter if (err) 847251881Speter { 848286506Speter if (pb->validate_props) 849286506Speter { 850286506Speter return svn_error_quick_wrap( 851286506Speter err, 852286506Speter _("Invalid svn:mergeinfo value")); 853286506Speter } 854251881Speter if (pb->notify_func) 855251881Speter { 856286506Speter svn_repos_notify_t *notify 857286506Speter = svn_repos_notify_create(svn_repos_notify_warning, 858286506Speter pb->notify_pool); 859286506Speter 860289180Speter notify->warning = svn_repos_notify_warning_invalid_mergeinfo; 861286506Speter notify->warning_str = _("Invalid svn:mergeinfo value; " 862286506Speter "leaving unchanged"); 863286506Speter pb->notify_func(pb->notify_baton, notify, pb->notify_pool); 864286506Speter svn_pool_clear(pb->notify_pool); 865251881Speter } 866286506Speter svn_error_clear(err); 867251881Speter } 868286506Speter else 869251881Speter { 870286506Speter value = new_value; 871251881Speter } 872251881Speter } 873251881Speter 874251881Speter return change_node_prop(rb->txn_root, nb->path, name, value, 875251881Speter pb->validate_props, nb->pool); 876251881Speter} 877251881Speter 878251881Speter 879251881Speterstatic svn_error_t * 880251881Speterdelete_node_property(void *baton, 881251881Speter const char *name) 882251881Speter{ 883251881Speter struct node_baton *nb = baton; 884251881Speter struct revision_baton *rb = nb->rb; 885251881Speter 886251881Speter /* If we're skipping this revision, we're done here. */ 887251881Speter if (rb->skipped) 888251881Speter return SVN_NO_ERROR; 889251881Speter 890251881Speter return change_node_prop(rb->txn_root, nb->path, name, NULL, 891251881Speter rb->pb->validate_props, nb->pool); 892251881Speter} 893251881Speter 894251881Speter 895251881Speterstatic svn_error_t * 896251881Speterremove_node_props(void *baton) 897251881Speter{ 898251881Speter struct node_baton *nb = baton; 899251881Speter struct revision_baton *rb = nb->rb; 900251881Speter apr_hash_t *proplist; 901251881Speter apr_hash_index_t *hi; 902251881Speter 903251881Speter /* If we're skipping this revision, we're done here. */ 904251881Speter if (rb->skipped) 905251881Speter return SVN_NO_ERROR; 906251881Speter 907251881Speter SVN_ERR(svn_fs_node_proplist(&proplist, 908251881Speter rb->txn_root, nb->path, nb->pool)); 909251881Speter 910251881Speter for (hi = apr_hash_first(nb->pool, proplist); hi; hi = apr_hash_next(hi)) 911251881Speter { 912289180Speter const char *key = apr_hash_this_key(hi); 913251881Speter 914251881Speter SVN_ERR(change_node_prop(rb->txn_root, nb->path, key, NULL, 915251881Speter rb->pb->validate_props, nb->pool)); 916251881Speter } 917251881Speter 918251881Speter return SVN_NO_ERROR; 919251881Speter} 920251881Speter 921251881Speter 922251881Speterstatic svn_error_t * 923251881Speterapply_textdelta(svn_txdelta_window_handler_t *handler, 924251881Speter void **handler_baton, 925251881Speter void *node_baton) 926251881Speter{ 927251881Speter struct node_baton *nb = node_baton; 928251881Speter struct revision_baton *rb = nb->rb; 929251881Speter 930251881Speter /* If we're skipping this revision, we're done here. */ 931251881Speter if (rb->skipped) 932251881Speter { 933251881Speter *handler = NULL; 934251881Speter return SVN_NO_ERROR; 935251881Speter } 936251881Speter 937251881Speter return svn_fs_apply_textdelta(handler, handler_baton, 938251881Speter rb->txn_root, nb->path, 939251881Speter svn_checksum_to_cstring(nb->base_checksum, 940251881Speter nb->pool), 941251881Speter svn_checksum_to_cstring(nb->result_checksum, 942251881Speter nb->pool), 943251881Speter nb->pool); 944251881Speter} 945251881Speter 946251881Speter 947251881Speterstatic svn_error_t * 948251881Speterset_fulltext(svn_stream_t **stream, 949251881Speter void *node_baton) 950251881Speter{ 951251881Speter struct node_baton *nb = node_baton; 952251881Speter struct revision_baton *rb = nb->rb; 953251881Speter 954251881Speter /* If we're skipping this revision, we're done here. */ 955251881Speter if (rb->skipped) 956251881Speter { 957251881Speter *stream = NULL; 958251881Speter return SVN_NO_ERROR; 959251881Speter } 960251881Speter 961251881Speter return svn_fs_apply_text(stream, 962251881Speter rb->txn_root, nb->path, 963251881Speter svn_checksum_to_cstring(nb->result_checksum, 964251881Speter nb->pool), 965251881Speter nb->pool); 966251881Speter} 967251881Speter 968251881Speter 969251881Speterstatic svn_error_t * 970251881Speterclose_node(void *baton) 971251881Speter{ 972251881Speter struct node_baton *nb = baton; 973251881Speter struct revision_baton *rb = nb->rb; 974251881Speter struct parse_baton *pb = rb->pb; 975251881Speter 976251881Speter /* If we're skipping this revision, we're done here. */ 977251881Speter if (rb->skipped) 978251881Speter return SVN_NO_ERROR; 979251881Speter 980251881Speter if (pb->notify_func) 981251881Speter { 982286506Speter /* ### TODO: Use proper scratch pool instead of pb->notify_pool */ 983286506Speter svn_repos_notify_t *notify = svn_repos_notify_create( 984286506Speter svn_repos_notify_load_node_done, 985286506Speter pb->notify_pool); 986286506Speter 987286506Speter pb->notify_func(pb->notify_baton, notify, pb->notify_pool); 988286506Speter svn_pool_clear(pb->notify_pool); 989251881Speter } 990251881Speter 991251881Speter return SVN_NO_ERROR; 992251881Speter} 993251881Speter 994251881Speter 995251881Speterstatic svn_error_t * 996251881Speterclose_revision(void *baton) 997251881Speter{ 998251881Speter struct revision_baton *rb = baton; 999251881Speter struct parse_baton *pb = rb->pb; 1000251881Speter const char *conflict_msg = NULL; 1001251881Speter svn_revnum_t committed_rev; 1002251881Speter svn_error_t *err; 1003251881Speter const char *txn_name = NULL; 1004251881Speter apr_hash_t *hooks_env; 1005251881Speter 1006289180Speter /* If we're skipping this revision we're done here. */ 1007289180Speter if (rb->skipped) 1008251881Speter return SVN_NO_ERROR; 1009251881Speter 1010289180Speter if (rb->rev == 0) 1011289180Speter { 1012289180Speter /* Special case: set revision 0 properties when loading into an 1013289180Speter 'empty' filesystem. */ 1014289180Speter svn_revnum_t youngest_rev; 1015289180Speter 1016289180Speter SVN_ERR(svn_fs_youngest_rev(&youngest_rev, pb->fs, rb->pool)); 1017289180Speter 1018289180Speter if (youngest_rev == 0) 1019289180Speter { 1020289180Speter apr_hash_t *orig_props; 1021289180Speter apr_hash_t *new_props; 1022289180Speter apr_array_header_t *diff; 1023289180Speter int i; 1024289180Speter 1025289180Speter SVN_ERR(svn_fs_revision_proplist(&orig_props, pb->fs, 0, rb->pool)); 1026289180Speter new_props = svn_prop_array_to_hash(rb->revprops, rb->pool); 1027289180Speter SVN_ERR(svn_prop_diffs(&diff, new_props, orig_props, rb->pool)); 1028289180Speter 1029289180Speter for (i = 0; i < diff->nelts; i++) 1030289180Speter { 1031289180Speter const svn_prop_t *prop = &APR_ARRAY_IDX(diff, i, svn_prop_t); 1032289180Speter 1033289180Speter SVN_ERR(change_rev_prop(pb->repos, 0, prop->name, prop->value, 1034289180Speter pb->validate_props, rb->pool)); 1035289180Speter } 1036289180Speter } 1037289180Speter 1038289180Speter return SVN_NO_ERROR; 1039289180Speter } 1040289180Speter 1041289180Speter /* If the dumpstream doesn't have an 'svn:date' property and we 1042289180Speter aren't ignoring the dates in the dumpstream altogether, remove 1043289180Speter any 'svn:date' revision property that was set by FS layer when 1044289180Speter the TXN was created. */ 1045289180Speter if (! (pb->ignore_dates || rb->datestamp)) 1046289180Speter { 1047289180Speter svn_prop_t *prop = &APR_ARRAY_PUSH(rb->revprops, svn_prop_t); 1048289180Speter prop->name = SVN_PROP_REVISION_DATE; 1049289180Speter prop->value = NULL; 1050289180Speter } 1051289180Speter 1052289180Speter /* Apply revision property changes. */ 1053289180Speter if (rb->pb->validate_props) 1054289180Speter SVN_ERR(svn_repos_fs_change_txn_props(rb->txn, rb->revprops, rb->pool)); 1055289180Speter else 1056289180Speter SVN_ERR(svn_fs_change_txn_props(rb->txn, rb->revprops, rb->pool)); 1057289180Speter 1058251881Speter /* Get the txn name and hooks environment if they will be needed. */ 1059251881Speter if (pb->use_pre_commit_hook || pb->use_post_commit_hook) 1060251881Speter { 1061251881Speter SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, pb->repos->hooks_env_path, 1062251881Speter rb->pool, rb->pool)); 1063251881Speter 1064251881Speter err = svn_fs_txn_name(&txn_name, rb->txn, rb->pool); 1065251881Speter if (err) 1066251881Speter { 1067251881Speter svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool)); 1068251881Speter return svn_error_trace(err); 1069251881Speter } 1070251881Speter } 1071251881Speter 1072251881Speter /* Run the pre-commit hook, if so commanded. */ 1073251881Speter if (pb->use_pre_commit_hook) 1074251881Speter { 1075251881Speter err = svn_repos__hooks_pre_commit(pb->repos, hooks_env, 1076251881Speter txn_name, rb->pool); 1077251881Speter if (err) 1078251881Speter { 1079251881Speter svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool)); 1080251881Speter return svn_error_trace(err); 1081251881Speter } 1082251881Speter } 1083251881Speter 1084251881Speter /* Commit. */ 1085251881Speter err = svn_fs_commit_txn(&conflict_msg, &committed_rev, rb->txn, rb->pool); 1086251881Speter if (SVN_IS_VALID_REVNUM(committed_rev)) 1087251881Speter { 1088251881Speter if (err) 1089251881Speter { 1090251881Speter /* ### Log any error, but better yet is to rev 1091251881Speter ### close_revision()'s API to allow both committed_rev and err 1092251881Speter ### to be returned, see #3768. */ 1093251881Speter svn_error_clear(err); 1094251881Speter } 1095251881Speter } 1096251881Speter else 1097251881Speter { 1098251881Speter svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool)); 1099251881Speter if (conflict_msg) 1100251881Speter return svn_error_quick_wrap(err, conflict_msg); 1101251881Speter else 1102251881Speter return svn_error_trace(err); 1103251881Speter } 1104251881Speter 1105251881Speter /* Run post-commit hook, if so commanded. */ 1106251881Speter if (pb->use_post_commit_hook) 1107251881Speter { 1108251881Speter if ((err = svn_repos__hooks_post_commit(pb->repos, hooks_env, 1109251881Speter committed_rev, txn_name, 1110251881Speter rb->pool))) 1111251881Speter return svn_error_create 1112251881Speter (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err, 1113251881Speter _("Commit succeeded, but post-commit hook failed")); 1114251881Speter } 1115251881Speter 1116251881Speter /* After a successful commit, must record the dump-rev -> in-repos-rev 1117251881Speter mapping, so that copyfrom instructions in the dump file can look up the 1118251881Speter correct repository revision to copy from. */ 1119251881Speter set_revision_mapping(pb->rev_map, rb->rev, committed_rev); 1120251881Speter 1121251881Speter /* If the incoming dump stream has non-contiguous revisions (e.g. from 1122251881Speter using svndumpfilter --drop-empty-revs without --renumber-revs) then 1123251881Speter we must account for the missing gaps in PB->REV_MAP. Otherwise we 1124251881Speter might not be able to map all mergeinfo source revisions to the correct 1125251881Speter revisions in the target repos. */ 1126251881Speter if ((pb->last_rev_mapped != SVN_INVALID_REVNUM) 1127251881Speter && (rb->rev != pb->last_rev_mapped + 1)) 1128251881Speter { 1129251881Speter svn_revnum_t i; 1130251881Speter 1131251881Speter for (i = pb->last_rev_mapped + 1; i < rb->rev; i++) 1132251881Speter { 1133251881Speter set_revision_mapping(pb->rev_map, i, pb->last_rev_mapped); 1134251881Speter } 1135251881Speter } 1136251881Speter 1137251881Speter /* Update our "last revision mapped". */ 1138251881Speter pb->last_rev_mapped = rb->rev; 1139251881Speter 1140251881Speter /* Deltify the predecessors of paths changed in this revision. */ 1141251881Speter SVN_ERR(svn_fs_deltify_revision(pb->fs, committed_rev, rb->pool)); 1142251881Speter 1143251881Speter if (pb->notify_func) 1144251881Speter { 1145286506Speter /* ### TODO: Use proper scratch pool instead of pb->notify_pool */ 1146286506Speter svn_repos_notify_t *notify = svn_repos_notify_create( 1147286506Speter svn_repos_notify_load_txn_committed, 1148286506Speter pb->notify_pool); 1149286506Speter 1150286506Speter notify->new_revision = committed_rev; 1151286506Speter notify->old_revision = ((committed_rev == rb->rev) 1152251881Speter ? SVN_INVALID_REVNUM 1153251881Speter : rb->rev); 1154286506Speter pb->notify_func(pb->notify_baton, notify, pb->notify_pool); 1155286506Speter svn_pool_clear(pb->notify_pool); 1156251881Speter } 1157251881Speter 1158251881Speter return SVN_NO_ERROR; 1159251881Speter} 1160251881Speter 1161251881Speter 1162251881Speter/*----------------------------------------------------------------------*/ 1163251881Speter 1164251881Speter/** The public routines **/ 1165251881Speter 1166251881Speter 1167251881Spetersvn_error_t * 1168289180Spetersvn_repos_get_fs_build_parser5(const svn_repos_parse_fns3_t **callbacks, 1169251881Speter void **parse_baton, 1170251881Speter svn_repos_t *repos, 1171251881Speter svn_revnum_t start_rev, 1172251881Speter svn_revnum_t end_rev, 1173251881Speter svn_boolean_t use_history, 1174251881Speter svn_boolean_t validate_props, 1175251881Speter enum svn_repos_load_uuid uuid_action, 1176251881Speter const char *parent_dir, 1177289180Speter svn_boolean_t use_pre_commit_hook, 1178289180Speter svn_boolean_t use_post_commit_hook, 1179289180Speter svn_boolean_t ignore_dates, 1180251881Speter svn_repos_notify_func_t notify_func, 1181251881Speter void *notify_baton, 1182251881Speter apr_pool_t *pool) 1183251881Speter{ 1184251881Speter svn_repos_parse_fns3_t *parser = apr_pcalloc(pool, sizeof(*parser)); 1185251881Speter struct parse_baton *pb = apr_pcalloc(pool, sizeof(*pb)); 1186251881Speter 1187251881Speter if (parent_dir) 1188251881Speter parent_dir = svn_relpath_canonicalize(parent_dir, pool); 1189251881Speter 1190251881Speter SVN_ERR_ASSERT((SVN_IS_VALID_REVNUM(start_rev) && 1191251881Speter SVN_IS_VALID_REVNUM(end_rev)) 1192251881Speter || ((! SVN_IS_VALID_REVNUM(start_rev)) && 1193251881Speter (! SVN_IS_VALID_REVNUM(end_rev)))); 1194251881Speter if (SVN_IS_VALID_REVNUM(start_rev)) 1195251881Speter SVN_ERR_ASSERT(start_rev <= end_rev); 1196251881Speter 1197251881Speter parser->magic_header_record = magic_header_record; 1198251881Speter parser->uuid_record = uuid_record; 1199251881Speter parser->new_revision_record = new_revision_record; 1200251881Speter parser->new_node_record = new_node_record; 1201251881Speter parser->set_revision_property = set_revision_property; 1202251881Speter parser->set_node_property = set_node_property; 1203251881Speter parser->remove_node_props = remove_node_props; 1204251881Speter parser->set_fulltext = set_fulltext; 1205251881Speter parser->close_node = close_node; 1206251881Speter parser->close_revision = close_revision; 1207251881Speter parser->delete_node_property = delete_node_property; 1208251881Speter parser->apply_textdelta = apply_textdelta; 1209251881Speter 1210251881Speter pb->repos = repos; 1211251881Speter pb->fs = svn_repos_fs(repos); 1212251881Speter pb->use_history = use_history; 1213251881Speter pb->validate_props = validate_props; 1214251881Speter pb->notify_func = notify_func; 1215251881Speter pb->notify_baton = notify_baton; 1216251881Speter pb->uuid_action = uuid_action; 1217251881Speter pb->parent_dir = parent_dir; 1218251881Speter pb->pool = pool; 1219286506Speter pb->notify_pool = svn_pool_create(pool); 1220251881Speter pb->rev_map = apr_hash_make(pool); 1221289180Speter pb->oldest_dumpstream_rev = SVN_INVALID_REVNUM; 1222251881Speter pb->last_rev_mapped = SVN_INVALID_REVNUM; 1223251881Speter pb->start_rev = start_rev; 1224251881Speter pb->end_rev = end_rev; 1225289180Speter pb->use_pre_commit_hook = use_pre_commit_hook; 1226289180Speter pb->use_post_commit_hook = use_post_commit_hook; 1227289180Speter pb->ignore_dates = ignore_dates; 1228251881Speter 1229251881Speter *callbacks = parser; 1230251881Speter *parse_baton = pb; 1231251881Speter return SVN_NO_ERROR; 1232251881Speter} 1233251881Speter 1234251881Speter 1235251881Spetersvn_error_t * 1236289180Spetersvn_repos_load_fs5(svn_repos_t *repos, 1237251881Speter svn_stream_t *dumpstream, 1238251881Speter svn_revnum_t start_rev, 1239251881Speter svn_revnum_t end_rev, 1240251881Speter enum svn_repos_load_uuid uuid_action, 1241251881Speter const char *parent_dir, 1242251881Speter svn_boolean_t use_pre_commit_hook, 1243251881Speter svn_boolean_t use_post_commit_hook, 1244251881Speter svn_boolean_t validate_props, 1245289180Speter svn_boolean_t ignore_dates, 1246251881Speter svn_repos_notify_func_t notify_func, 1247251881Speter void *notify_baton, 1248251881Speter svn_cancel_func_t cancel_func, 1249251881Speter void *cancel_baton, 1250251881Speter apr_pool_t *pool) 1251251881Speter{ 1252251881Speter const svn_repos_parse_fns3_t *parser; 1253251881Speter void *parse_baton; 1254251881Speter 1255251881Speter /* This is really simple. */ 1256251881Speter 1257289180Speter SVN_ERR(svn_repos_get_fs_build_parser5(&parser, &parse_baton, 1258251881Speter repos, 1259251881Speter start_rev, end_rev, 1260251881Speter TRUE, /* look for copyfrom revs */ 1261251881Speter validate_props, 1262251881Speter uuid_action, 1263251881Speter parent_dir, 1264289180Speter use_pre_commit_hook, 1265289180Speter use_post_commit_hook, 1266289180Speter ignore_dates, 1267251881Speter notify_func, 1268251881Speter notify_baton, 1269251881Speter pool)); 1270251881Speter 1271251881Speter return svn_repos_parse_dumpstream3(dumpstream, parser, parse_baton, FALSE, 1272251881Speter cancel_func, cancel_baton, pool); 1273251881Speter} 1274