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_private_config.h" 35251881Speter#include "svn_mergeinfo.h" 36251881Speter#include "svn_checksum.h" 37251881Speter#include "svn_subst.h" 38251881Speter#include "svn_ctype.h" 39251881Speter#include "svn_dirent_uri.h" 40251881Speter 41251881Speter#include <apr_lib.h> 42251881Speter 43251881Speter#include "private/svn_fspath.h" 44251881Speter#include "private/svn_dep_compat.h" 45251881Speter#include "private/svn_mergeinfo_private.h" 46251881Speter 47251881Speter/*----------------------------------------------------------------------*/ 48251881Speter 49251881Speter/** Batons used herein **/ 50251881Speter 51251881Speterstruct parse_baton 52251881Speter{ 53251881Speter svn_repos_t *repos; 54251881Speter svn_fs_t *fs; 55251881Speter 56251881Speter svn_boolean_t use_history; 57251881Speter svn_boolean_t validate_props; 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; 64251881Speter svn_repos_notify_t *notify; 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 86251881Speter /* The oldest old revision loaded from the dump stream. If no revisions 87251881Speter have been loaded yet, this is set to SVN_INVALID_REVNUM. */ 88251881Speter svn_revnum_t oldest_old_rev; 89251881Speter}; 90251881Speter 91251881Speterstruct revision_baton 92251881Speter{ 93251881Speter svn_revnum_t rev; 94251881Speter svn_fs_txn_t *txn; 95251881Speter svn_fs_root_t *txn_root; 96251881Speter 97251881Speter const svn_string_t *datestamp; 98251881Speter 99251881Speter apr_int32_t rev_offset; 100251881Speter svn_boolean_t skipped; 101251881Speter 102251881Speter struct parse_baton *pb; 103251881Speter apr_pool_t *pool; 104251881Speter}; 105251881Speter 106251881Speterstruct node_baton 107251881Speter{ 108251881Speter const char *path; 109251881Speter svn_node_kind_t kind; 110251881Speter enum svn_node_action action; 111251881Speter svn_checksum_t *base_checksum; /* null, if not available */ 112251881Speter svn_checksum_t *result_checksum; /* null, if not available */ 113251881Speter svn_checksum_t *copy_source_checksum; /* null, if not available */ 114251881Speter 115251881Speter svn_revnum_t copyfrom_rev; 116251881Speter const char *copyfrom_path; 117251881Speter 118251881Speter struct revision_baton *rb; 119251881Speter apr_pool_t *pool; 120251881Speter}; 121251881Speter 122251881Speter 123251881Speter/*----------------------------------------------------------------------*/ 124251881Speter 125251881Speter/* Record the mapping of FROM_REV to TO_REV in REV_MAP, ensuring that 126251881Speter anything added to the hash is allocated in the hash's pool. */ 127251881Speterstatic void 128251881Speterset_revision_mapping(apr_hash_t *rev_map, 129251881Speter svn_revnum_t from_rev, 130251881Speter svn_revnum_t to_rev) 131251881Speter{ 132251881Speter svn_revnum_t *mapped_revs = apr_palloc(apr_hash_pool_get(rev_map), 133251881Speter sizeof(svn_revnum_t) * 2); 134251881Speter mapped_revs[0] = from_rev; 135251881Speter mapped_revs[1] = to_rev; 136251881Speter apr_hash_set(rev_map, mapped_revs, 137251881Speter sizeof(svn_revnum_t), mapped_revs + 1); 138251881Speter} 139251881Speter 140251881Speter/* Return the revision to which FROM_REV maps in REV_MAP, or 141251881Speter SVN_INVALID_REVNUM if no such mapping exists. */ 142251881Speterstatic svn_revnum_t 143251881Speterget_revision_mapping(apr_hash_t *rev_map, 144251881Speter svn_revnum_t from_rev) 145251881Speter{ 146251881Speter svn_revnum_t *to_rev = apr_hash_get(rev_map, &from_rev, 147251881Speter sizeof(from_rev)); 148251881Speter return to_rev ? *to_rev : SVN_INVALID_REVNUM; 149251881Speter} 150251881Speter 151251881Speter 152251881Speter/* Change revision property NAME to VALUE for REVISION in REPOS. If 153251881Speter VALIDATE_PROPS is set, use functions which perform validation of 154251881Speter the property value. Otherwise, bypass those checks. */ 155251881Speterstatic svn_error_t * 156251881Speterchange_rev_prop(svn_repos_t *repos, 157251881Speter svn_revnum_t revision, 158251881Speter const char *name, 159251881Speter const svn_string_t *value, 160251881Speter svn_boolean_t validate_props, 161251881Speter apr_pool_t *pool) 162251881Speter{ 163251881Speter if (validate_props) 164251881Speter return svn_repos_fs_change_rev_prop4(repos, revision, NULL, name, 165251881Speter NULL, value, FALSE, FALSE, 166251881Speter NULL, NULL, pool); 167251881Speter else 168251881Speter return svn_fs_change_rev_prop2(svn_repos_fs(repos), revision, name, 169251881Speter NULL, value, pool); 170251881Speter} 171251881Speter 172251881Speter/* Change property NAME to VALUE for PATH in TXN_ROOT. If 173251881Speter VALIDATE_PROPS is set, use functions which perform validation of 174251881Speter the property value. Otherwise, bypass those checks. */ 175251881Speterstatic svn_error_t * 176251881Speterchange_node_prop(svn_fs_root_t *txn_root, 177251881Speter const char *path, 178251881Speter const char *name, 179251881Speter const svn_string_t *value, 180251881Speter svn_boolean_t validate_props, 181251881Speter apr_pool_t *pool) 182251881Speter{ 183251881Speter if (validate_props) 184251881Speter return svn_repos_fs_change_node_prop(txn_root, path, name, value, pool); 185251881Speter else 186251881Speter return svn_fs_change_node_prop(txn_root, path, name, value, pool); 187251881Speter} 188251881Speter 189251881Speter/* Prepend the mergeinfo source paths in MERGEINFO_ORIG with PARENT_DIR, and 190251881Speter return it in *MERGEINFO_VAL. */ 191251881Speter/* ### FIXME: Consider somehow sharing code with 192251881Speter ### svnrdump/load_editor.c:prefix_mergeinfo_paths() */ 193251881Speterstatic svn_error_t * 194251881Speterprefix_mergeinfo_paths(svn_string_t **mergeinfo_val, 195251881Speter const svn_string_t *mergeinfo_orig, 196251881Speter const char *parent_dir, 197251881Speter apr_pool_t *pool) 198251881Speter{ 199251881Speter apr_hash_t *prefixed_mergeinfo, *mergeinfo; 200251881Speter apr_hash_index_t *hi; 201251881Speter void *rangelist; 202251881Speter 203251881Speter SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_orig->data, pool)); 204251881Speter prefixed_mergeinfo = apr_hash_make(pool); 205251881Speter for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) 206251881Speter { 207251881Speter const void *key; 208251881Speter const char *path, *merge_source; 209251881Speter 210251881Speter apr_hash_this(hi, &key, NULL, &rangelist); 211251881Speter merge_source = svn_relpath_canonicalize(key, pool); 212251881Speter 213251881Speter /* The svn:mergeinfo property syntax demands a repos abspath */ 214251881Speter path = svn_fspath__canonicalize(svn_relpath_join(parent_dir, 215251881Speter merge_source, pool), 216251881Speter pool); 217251881Speter svn_hash_sets(prefixed_mergeinfo, path, rangelist); 218251881Speter } 219251881Speter return svn_mergeinfo_to_string(mergeinfo_val, prefixed_mergeinfo, pool); 220251881Speter} 221251881Speter 222251881Speter 223251881Speter/* Examine the mergeinfo in INITIAL_VAL, renumber revisions in rangelists 224251881Speter as appropriate, and return the (possibly new) mergeinfo in *FINAL_VAL 225251881Speter (allocated from POOL). */ 226251881Speter/* ### FIXME: Consider somehow sharing code with 227251881Speter ### svnrdump/load_editor.c:renumber_mergeinfo_revs() */ 228251881Speterstatic svn_error_t * 229251881Speterrenumber_mergeinfo_revs(svn_string_t **final_val, 230251881Speter const svn_string_t *initial_val, 231251881Speter struct revision_baton *rb, 232251881Speter apr_pool_t *pool) 233251881Speter{ 234251881Speter apr_pool_t *subpool = svn_pool_create(pool); 235251881Speter svn_mergeinfo_t mergeinfo, predates_stream_mergeinfo; 236251881Speter svn_mergeinfo_t final_mergeinfo = apr_hash_make(subpool); 237251881Speter apr_hash_index_t *hi; 238251881Speter 239251881Speter SVN_ERR(svn_mergeinfo_parse(&mergeinfo, initial_val->data, subpool)); 240251881Speter 241251881Speter /* Issue #3020 242251881Speter http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16 243251881Speter Remove mergeinfo older than the oldest revision in the dump stream 244251881Speter and adjust its revisions by the difference between the head rev of 245251881Speter the target repository and the current dump stream rev. */ 246251881Speter if (rb->pb->oldest_old_rev > 1) 247251881Speter { 248251881Speter SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( 249251881Speter &predates_stream_mergeinfo, mergeinfo, 250251881Speter rb->pb->oldest_old_rev - 1, 0, 251251881Speter TRUE, subpool, subpool)); 252251881Speter SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( 253251881Speter &mergeinfo, mergeinfo, 254251881Speter rb->pb->oldest_old_rev - 1, 0, 255251881Speter FALSE, subpool, subpool)); 256251881Speter SVN_ERR(svn_mergeinfo__adjust_mergeinfo_rangelists( 257251881Speter &predates_stream_mergeinfo, predates_stream_mergeinfo, 258251881Speter -rb->rev_offset, subpool, subpool)); 259251881Speter } 260251881Speter else 261251881Speter { 262251881Speter predates_stream_mergeinfo = NULL; 263251881Speter } 264251881Speter 265251881Speter for (hi = apr_hash_first(subpool, mergeinfo); hi; hi = apr_hash_next(hi)) 266251881Speter { 267251881Speter const char *merge_source; 268251881Speter svn_rangelist_t *rangelist; 269251881Speter struct parse_baton *pb = rb->pb; 270251881Speter int i; 271251881Speter const void *key; 272251881Speter void *val; 273251881Speter 274251881Speter apr_hash_this(hi, &key, NULL, &val); 275251881Speter merge_source = key; 276251881Speter rangelist = val; 277251881Speter 278251881Speter /* Possibly renumber revisions in merge source's rangelist. */ 279251881Speter for (i = 0; i < rangelist->nelts; i++) 280251881Speter { 281251881Speter svn_revnum_t rev_from_map; 282251881Speter svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i, 283251881Speter svn_merge_range_t *); 284251881Speter rev_from_map = get_revision_mapping(pb->rev_map, range->start); 285251881Speter if (SVN_IS_VALID_REVNUM(rev_from_map)) 286251881Speter { 287251881Speter range->start = rev_from_map; 288251881Speter } 289251881Speter else if (range->start == pb->oldest_old_rev - 1) 290251881Speter { 291251881Speter /* Since the start revision of svn_merge_range_t are not 292251881Speter inclusive there is one possible valid start revision that 293251881Speter won't be found in the PB->REV_MAP mapping of load stream 294251881Speter revsions to loaded revisions: The revision immediately 295251881Speter preceeding the oldest revision from the load stream. 296251881Speter This is a valid revision for mergeinfo, but not a valid 297251881Speter copy from revision (which PB->REV_MAP also maps for) so it 298251881Speter will never be in the mapping. 299251881Speter 300251881Speter If that is what we have here, then find the mapping for the 301251881Speter oldest rev from the load stream and subtract 1 to get the 302251881Speter renumbered, non-inclusive, start revision. */ 303251881Speter rev_from_map = get_revision_mapping(pb->rev_map, 304251881Speter pb->oldest_old_rev); 305251881Speter if (SVN_IS_VALID_REVNUM(rev_from_map)) 306251881Speter range->start = rev_from_map - 1; 307251881Speter } 308251881Speter else 309251881Speter { 310251881Speter /* If we can't remap the start revision then don't even bother 311251881Speter trying to remap the end revision. It's possible we might 312251881Speter actually succeed at the latter, which can result in invalid 313251881Speter mergeinfo with a start rev > end rev. If that gets into the 314251881Speter repository then a world of bustage breaks loose anytime that 315251881Speter bogus mergeinfo is parsed. See 316251881Speter http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16. 317251881Speter */ 318251881Speter continue; 319251881Speter } 320251881Speter 321251881Speter rev_from_map = get_revision_mapping(pb->rev_map, range->end); 322251881Speter if (SVN_IS_VALID_REVNUM(rev_from_map)) 323251881Speter range->end = rev_from_map; 324251881Speter } 325251881Speter svn_hash_sets(final_mergeinfo, merge_source, rangelist); 326251881Speter } 327251881Speter 328251881Speter if (predates_stream_mergeinfo) 329251881Speter SVN_ERR(svn_mergeinfo_merge2(final_mergeinfo, predates_stream_mergeinfo, 330251881Speter subpool, subpool)); 331251881Speter 332251881Speter SVN_ERR(svn_mergeinfo_sort(final_mergeinfo, subpool)); 333251881Speter 334251881Speter /* Mergeinfo revision sources for r0 and r1 are invalid; you can't merge r0 335251881Speter or r1. However, svndumpfilter can be abused to produce r1 merge source 336251881Speter revs. So if we encounter any, then strip them out, no need to put them 337251881Speter into the load target. */ 338251881Speter SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(&final_mergeinfo, 339251881Speter final_mergeinfo, 340251881Speter 1, 0, FALSE, 341251881Speter subpool, subpool)); 342251881Speter 343251881Speter SVN_ERR(svn_mergeinfo_to_string(final_val, final_mergeinfo, pool)); 344251881Speter svn_pool_destroy(subpool); 345251881Speter 346251881Speter return SVN_NO_ERROR; 347251881Speter} 348251881Speter 349251881Speter/*----------------------------------------------------------------------*/ 350251881Speter 351251881Speter/** vtable for doing commits to a fs **/ 352251881Speter 353251881Speter 354251881Speterstatic svn_error_t * 355251881Spetermake_node_baton(struct node_baton **node_baton_p, 356251881Speter apr_hash_t *headers, 357251881Speter struct revision_baton *rb, 358251881Speter apr_pool_t *pool) 359251881Speter{ 360251881Speter struct node_baton *nb = apr_pcalloc(pool, sizeof(*nb)); 361251881Speter const char *val; 362251881Speter 363251881Speter /* Start with sensible defaults. */ 364251881Speter nb->rb = rb; 365251881Speter nb->pool = pool; 366251881Speter nb->kind = svn_node_unknown; 367251881Speter 368251881Speter /* Then add info from the headers. */ 369251881Speter if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_PATH))) 370251881Speter { 371251881Speter val = svn_relpath_canonicalize(val, pool); 372251881Speter if (rb->pb->parent_dir) 373251881Speter nb->path = svn_relpath_join(rb->pb->parent_dir, val, pool); 374251881Speter else 375251881Speter nb->path = val; 376251881Speter } 377251881Speter 378251881Speter if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_KIND))) 379251881Speter { 380251881Speter if (! strcmp(val, "file")) 381251881Speter nb->kind = svn_node_file; 382251881Speter else if (! strcmp(val, "dir")) 383251881Speter nb->kind = svn_node_dir; 384251881Speter } 385251881Speter 386251881Speter nb->action = (enum svn_node_action)(-1); /* an invalid action code */ 387251881Speter if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_ACTION))) 388251881Speter { 389251881Speter if (! strcmp(val, "change")) 390251881Speter nb->action = svn_node_action_change; 391251881Speter else if (! strcmp(val, "add")) 392251881Speter nb->action = svn_node_action_add; 393251881Speter else if (! strcmp(val, "delete")) 394251881Speter nb->action = svn_node_action_delete; 395251881Speter else if (! strcmp(val, "replace")) 396251881Speter nb->action = svn_node_action_replace; 397251881Speter } 398251881Speter 399251881Speter nb->copyfrom_rev = SVN_INVALID_REVNUM; 400251881Speter if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV))) 401251881Speter { 402251881Speter nb->copyfrom_rev = SVN_STR_TO_REV(val); 403251881Speter } 404251881Speter if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH))) 405251881Speter { 406251881Speter val = svn_relpath_canonicalize(val, pool); 407251881Speter if (rb->pb->parent_dir) 408251881Speter nb->copyfrom_path = svn_relpath_join(rb->pb->parent_dir, val, pool); 409251881Speter else 410251881Speter nb->copyfrom_path = val; 411251881Speter } 412251881Speter 413251881Speter if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_CHECKSUM))) 414251881Speter { 415251881Speter SVN_ERR(svn_checksum_parse_hex(&nb->result_checksum, svn_checksum_md5, 416251881Speter val, pool)); 417251881Speter } 418251881Speter 419251881Speter if ((val = svn_hash_gets(headers, 420251881Speter SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_CHECKSUM))) 421251881Speter { 422251881Speter SVN_ERR(svn_checksum_parse_hex(&nb->base_checksum, svn_checksum_md5, val, 423251881Speter pool)); 424251881Speter } 425251881Speter 426251881Speter if ((val = svn_hash_gets(headers, 427251881Speter SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_CHECKSUM))) 428251881Speter { 429251881Speter SVN_ERR(svn_checksum_parse_hex(&nb->copy_source_checksum, 430251881Speter svn_checksum_md5, val, pool)); 431251881Speter } 432251881Speter 433251881Speter /* What's cool about this dump format is that the parser just 434251881Speter ignores any unrecognized headers. :-) */ 435251881Speter 436251881Speter *node_baton_p = nb; 437251881Speter return SVN_NO_ERROR; 438251881Speter} 439251881Speter 440251881Speterstatic struct revision_baton * 441251881Spetermake_revision_baton(apr_hash_t *headers, 442251881Speter struct parse_baton *pb, 443251881Speter apr_pool_t *pool) 444251881Speter{ 445251881Speter struct revision_baton *rb = apr_pcalloc(pool, sizeof(*rb)); 446251881Speter const char *val; 447251881Speter 448251881Speter rb->pb = pb; 449251881Speter rb->pool = pool; 450251881Speter rb->rev = SVN_INVALID_REVNUM; 451251881Speter 452251881Speter if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_REVISION_NUMBER))) 453251881Speter { 454251881Speter rb->rev = SVN_STR_TO_REV(val); 455251881Speter 456251881Speter /* If we're filtering revisions, is this one we'll skip? */ 457251881Speter rb->skipped = (SVN_IS_VALID_REVNUM(pb->start_rev) 458251881Speter && ((rb->rev < pb->start_rev) || 459251881Speter (rb->rev > pb->end_rev))); 460251881Speter } 461251881Speter 462251881Speter return rb; 463251881Speter} 464251881Speter 465251881Speter 466251881Speterstatic svn_error_t * 467251881Speternew_revision_record(void **revision_baton, 468251881Speter apr_hash_t *headers, 469251881Speter void *parse_baton, 470251881Speter apr_pool_t *pool) 471251881Speter{ 472251881Speter struct parse_baton *pb = parse_baton; 473251881Speter struct revision_baton *rb; 474251881Speter svn_revnum_t head_rev; 475251881Speter 476251881Speter rb = make_revision_baton(headers, pb, pool); 477251881Speter 478251881Speter /* ### If we're filtering revisions, and this is one we've skipped, 479251881Speter ### and we've skipped it because it has a revision number younger 480251881Speter ### than the youngest in our acceptable range, then should we 481251881Speter ### just bail out here? */ 482251881Speter /* 483251881Speter if (rb->skipped && (rb->rev > pb->end_rev)) 484251881Speter return svn_error_createf(SVN_ERR_CEASE_INVOCATION, 0, 485251881Speter _("Finished processing acceptable load " 486251881Speter "revision range")); 487251881Speter */ 488251881Speter 489251881Speter SVN_ERR(svn_fs_youngest_rev(&head_rev, pb->fs, pool)); 490251881Speter 491251881Speter /* FIXME: This is a lame fallback loading multiple segments of dump in 492251881Speter several separate operations. It is highly susceptible to race conditions. 493251881Speter Calculate the revision 'offset' for finding copyfrom sources. 494251881Speter It might be positive or negative. */ 495251881Speter rb->rev_offset = (apr_int32_t) ((rb->rev) - (head_rev + 1)); 496251881Speter 497251881Speter if ((rb->rev > 0) && (! rb->skipped)) 498251881Speter { 499251881Speter /* Create a new fs txn. */ 500251881Speter SVN_ERR(svn_fs_begin_txn2(&(rb->txn), pb->fs, head_rev, 0, pool)); 501251881Speter SVN_ERR(svn_fs_txn_root(&(rb->txn_root), rb->txn, pool)); 502251881Speter 503251881Speter if (pb->notify_func) 504251881Speter { 505251881Speter pb->notify->action = svn_repos_notify_load_txn_start; 506251881Speter pb->notify->old_revision = rb->rev; 507251881Speter pb->notify_func(pb->notify_baton, pb->notify, rb->pool); 508251881Speter } 509251881Speter 510251881Speter /* Stash the oldest "old" revision committed from the load stream. */ 511251881Speter if (!SVN_IS_VALID_REVNUM(pb->oldest_old_rev)) 512251881Speter pb->oldest_old_rev = rb->rev; 513251881Speter } 514251881Speter 515251881Speter /* If we're skipping this revision, try to notify someone. */ 516251881Speter if (rb->skipped && pb->notify_func) 517251881Speter { 518251881Speter pb->notify->action = svn_repos_notify_load_skipped_rev; 519251881Speter pb->notify->old_revision = rb->rev; 520251881Speter pb->notify_func(pb->notify_baton, pb->notify, rb->pool); 521251881Speter } 522251881Speter 523251881Speter /* If we're parsing revision 0, only the revision are (possibly) 524251881Speter interesting to us: when loading the stream into an empty 525251881Speter filesystem, then we want new filesystem's revision 0 to have the 526251881Speter same props. Otherwise, we just ignore revision 0 in the stream. */ 527251881Speter 528251881Speter *revision_baton = rb; 529251881Speter return SVN_NO_ERROR; 530251881Speter} 531251881Speter 532251881Speter 533251881Speter 534251881Speter/* Factorized helper func for new_node_record() */ 535251881Speterstatic svn_error_t * 536251881Spetermaybe_add_with_history(struct node_baton *nb, 537251881Speter struct revision_baton *rb, 538251881Speter apr_pool_t *pool) 539251881Speter{ 540251881Speter struct parse_baton *pb = rb->pb; 541251881Speter 542251881Speter if ((nb->copyfrom_path == NULL) || (! pb->use_history)) 543251881Speter { 544251881Speter /* Add empty file or dir, without history. */ 545251881Speter if (nb->kind == svn_node_file) 546251881Speter SVN_ERR(svn_fs_make_file(rb->txn_root, nb->path, pool)); 547251881Speter 548251881Speter else if (nb->kind == svn_node_dir) 549251881Speter SVN_ERR(svn_fs_make_dir(rb->txn_root, nb->path, pool)); 550251881Speter } 551251881Speter else 552251881Speter { 553251881Speter /* Hunt down the source revision in this fs. */ 554251881Speter svn_fs_root_t *copy_root; 555251881Speter svn_revnum_t copyfrom_rev; 556251881Speter 557251881Speter /* Try to find the copyfrom revision in the revision map; 558251881Speter failing that, fall back to the revision offset approach. */ 559251881Speter copyfrom_rev = get_revision_mapping(rb->pb->rev_map, nb->copyfrom_rev); 560251881Speter if (! SVN_IS_VALID_REVNUM(copyfrom_rev)) 561251881Speter copyfrom_rev = nb->copyfrom_rev - rb->rev_offset; 562251881Speter 563251881Speter if (! SVN_IS_VALID_REVNUM(copyfrom_rev)) 564251881Speter return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, 565251881Speter _("Relative source revision %ld is not" 566251881Speter " available in current repository"), 567251881Speter copyfrom_rev); 568251881Speter 569251881Speter SVN_ERR(svn_fs_revision_root(©_root, pb->fs, copyfrom_rev, pool)); 570251881Speter 571251881Speter if (nb->copy_source_checksum) 572251881Speter { 573251881Speter svn_checksum_t *checksum; 574251881Speter SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, copy_root, 575251881Speter nb->copyfrom_path, TRUE, pool)); 576251881Speter if (!svn_checksum_match(nb->copy_source_checksum, checksum)) 577251881Speter return svn_checksum_mismatch_err(nb->copy_source_checksum, 578251881Speter checksum, pool, 579251881Speter _("Copy source checksum mismatch on copy from '%s'@%ld\n" 580251881Speter "to '%s' in rev based on r%ld"), 581251881Speter nb->copyfrom_path, copyfrom_rev, nb->path, rb->rev); 582251881Speter } 583251881Speter 584251881Speter SVN_ERR(svn_fs_copy(copy_root, nb->copyfrom_path, 585251881Speter rb->txn_root, nb->path, pool)); 586251881Speter 587251881Speter if (pb->notify_func) 588251881Speter { 589251881Speter pb->notify->action = svn_repos_notify_load_copied_node; 590251881Speter pb->notify_func(pb->notify_baton, pb->notify, rb->pool); 591251881Speter } 592251881Speter } 593251881Speter 594251881Speter return SVN_NO_ERROR; 595251881Speter} 596251881Speter 597251881Speterstatic svn_error_t * 598251881Spetermagic_header_record(int version, 599251881Speter void *parse_baton, 600251881Speter apr_pool_t *pool) 601251881Speter{ 602251881Speter return SVN_NO_ERROR; 603251881Speter} 604251881Speter 605251881Speterstatic svn_error_t * 606251881Speteruuid_record(const char *uuid, 607251881Speter void *parse_baton, 608251881Speter apr_pool_t *pool) 609251881Speter{ 610251881Speter struct parse_baton *pb = parse_baton; 611251881Speter svn_revnum_t youngest_rev; 612251881Speter 613251881Speter if (pb->uuid_action == svn_repos_load_uuid_ignore) 614251881Speter return SVN_NO_ERROR; 615251881Speter 616251881Speter if (pb->uuid_action != svn_repos_load_uuid_force) 617251881Speter { 618251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest_rev, pb->fs, pool)); 619251881Speter if (youngest_rev != 0) 620251881Speter return SVN_NO_ERROR; 621251881Speter } 622251881Speter 623251881Speter return svn_fs_set_uuid(pb->fs, uuid, pool); 624251881Speter} 625251881Speter 626251881Speterstatic svn_error_t * 627251881Speternew_node_record(void **node_baton, 628251881Speter apr_hash_t *headers, 629251881Speter void *revision_baton, 630251881Speter apr_pool_t *pool) 631251881Speter{ 632251881Speter struct revision_baton *rb = revision_baton; 633251881Speter struct parse_baton *pb = rb->pb; 634251881Speter struct node_baton *nb; 635251881Speter 636251881Speter if (rb->rev == 0) 637251881Speter return svn_error_create(SVN_ERR_STREAM_MALFORMED_DATA, NULL, 638251881Speter _("Malformed dumpstream: " 639251881Speter "Revision 0 must not contain node records")); 640251881Speter 641251881Speter SVN_ERR(make_node_baton(&nb, headers, rb, pool)); 642251881Speter 643251881Speter /* If we're skipping this revision, we're done here. */ 644251881Speter if (rb->skipped) 645251881Speter { 646251881Speter *node_baton = nb; 647251881Speter return SVN_NO_ERROR; 648251881Speter } 649251881Speter 650251881Speter /* Make sure we have an action we recognize. */ 651251881Speter if (nb->action < svn_node_action_change 652251881Speter || nb->action > svn_node_action_replace) 653251881Speter return svn_error_createf(SVN_ERR_STREAM_UNRECOGNIZED_DATA, NULL, 654251881Speter _("Unrecognized node-action on node '%s'"), 655251881Speter nb->path); 656251881Speter 657251881Speter if (pb->notify_func) 658251881Speter { 659251881Speter pb->notify->action = svn_repos_notify_load_node_start; 660251881Speter pb->notify->node_action = nb->action; 661251881Speter pb->notify->path = nb->path; 662251881Speter pb->notify_func(pb->notify_baton, pb->notify, rb->pool); 663251881Speter } 664251881Speter 665251881Speter switch (nb->action) 666251881Speter { 667251881Speter case svn_node_action_change: 668251881Speter break; 669251881Speter 670251881Speter case svn_node_action_delete: 671251881Speter SVN_ERR(svn_fs_delete(rb->txn_root, nb->path, pool)); 672251881Speter break; 673251881Speter 674251881Speter case svn_node_action_add: 675251881Speter SVN_ERR(maybe_add_with_history(nb, rb, pool)); 676251881Speter break; 677251881Speter 678251881Speter case svn_node_action_replace: 679251881Speter SVN_ERR(svn_fs_delete(rb->txn_root, nb->path, pool)); 680251881Speter SVN_ERR(maybe_add_with_history(nb, rb, pool)); 681251881Speter break; 682251881Speter } 683251881Speter 684251881Speter *node_baton = nb; 685251881Speter return SVN_NO_ERROR; 686251881Speter} 687251881Speter 688251881Speterstatic svn_error_t * 689251881Speterset_revision_property(void *baton, 690251881Speter const char *name, 691251881Speter const svn_string_t *value) 692251881Speter{ 693251881Speter struct revision_baton *rb = baton; 694251881Speter 695251881Speter /* If we're skipping this revision, we're done here. */ 696251881Speter if (rb->skipped) 697251881Speter return SVN_NO_ERROR; 698251881Speter 699251881Speter if (rb->rev > 0) 700251881Speter { 701251881Speter if (rb->pb->validate_props) 702251881Speter SVN_ERR(svn_repos_fs_change_txn_prop(rb->txn, name, value, rb->pool)); 703251881Speter else 704251881Speter SVN_ERR(svn_fs_change_txn_prop(rb->txn, name, value, rb->pool)); 705251881Speter 706251881Speter /* Remember any datestamp that passes through! (See comment in 707251881Speter close_revision() below.) */ 708251881Speter if (! strcmp(name, SVN_PROP_REVISION_DATE)) 709251881Speter rb->datestamp = svn_string_dup(value, rb->pool); 710251881Speter } 711251881Speter else if (rb->rev == 0) 712251881Speter { 713251881Speter /* Special case: set revision 0 properties when loading into an 714251881Speter 'empty' filesystem. */ 715251881Speter struct parse_baton *pb = rb->pb; 716251881Speter svn_revnum_t youngest_rev; 717251881Speter 718251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest_rev, pb->fs, rb->pool)); 719251881Speter 720251881Speter if (youngest_rev == 0) 721251881Speter SVN_ERR(change_rev_prop(pb->repos, 0, name, value, 722251881Speter pb->validate_props, rb->pool)); 723251881Speter } 724251881Speter 725251881Speter return SVN_NO_ERROR; 726251881Speter} 727251881Speter 728251881Speter 729251881Speterstatic svn_error_t * 730251881Speterset_node_property(void *baton, 731251881Speter const char *name, 732251881Speter const svn_string_t *value) 733251881Speter{ 734251881Speter struct node_baton *nb = baton; 735251881Speter struct revision_baton *rb = nb->rb; 736251881Speter struct parse_baton *pb = rb->pb; 737251881Speter 738251881Speter /* If we're skipping this revision, we're done here. */ 739251881Speter if (rb->skipped) 740251881Speter return SVN_NO_ERROR; 741251881Speter 742251881Speter if (strcmp(name, SVN_PROP_MERGEINFO) == 0) 743251881Speter { 744251881Speter svn_string_t *renumbered_mergeinfo; 745251881Speter /* ### Need to cast away const. We cannot change the declaration of 746251881Speter * ### this function since it is part of svn_repos_parse_fns2_t. */ 747251881Speter svn_string_t *prop_val = (svn_string_t *)value; 748251881Speter 749251881Speter /* Tolerate mergeinfo with "\r\n" line endings because some 750251881Speter dumpstream sources might contain as much. If so normalize 751251881Speter the line endings to '\n' and make a notification to 752251881Speter PARSE_BATON->FEEDBACK_STREAM that we have made this 753251881Speter correction. */ 754251881Speter if (strstr(prop_val->data, "\r")) 755251881Speter { 756251881Speter const char *prop_eol_normalized; 757251881Speter 758251881Speter SVN_ERR(svn_subst_translate_cstring2(prop_val->data, 759251881Speter &prop_eol_normalized, 760251881Speter "\n", /* translate to LF */ 761251881Speter FALSE, /* no repair */ 762251881Speter NULL, /* no keywords */ 763251881Speter FALSE, /* no expansion */ 764251881Speter nb->pool)); 765251881Speter prop_val->data = prop_eol_normalized; 766251881Speter prop_val->len = strlen(prop_eol_normalized); 767251881Speter 768251881Speter if (pb->notify_func) 769251881Speter { 770251881Speter pb->notify->action = svn_repos_notify_load_normalized_mergeinfo; 771251881Speter pb->notify_func(pb->notify_baton, pb->notify, nb->pool); 772251881Speter } 773251881Speter } 774251881Speter 775251881Speter /* Renumber mergeinfo as appropriate. */ 776251881Speter SVN_ERR(renumber_mergeinfo_revs(&renumbered_mergeinfo, prop_val, rb, 777251881Speter nb->pool)); 778251881Speter value = renumbered_mergeinfo; 779251881Speter if (pb->parent_dir) 780251881Speter { 781251881Speter /* Prefix the merge source paths with PB->parent_dir. */ 782251881Speter /* ASSUMPTION: All source paths are included in the dump stream. */ 783251881Speter svn_string_t *mergeinfo_val; 784251881Speter SVN_ERR(prefix_mergeinfo_paths(&mergeinfo_val, value, 785251881Speter pb->parent_dir, nb->pool)); 786251881Speter value = mergeinfo_val; 787251881Speter } 788251881Speter } 789251881Speter 790251881Speter return change_node_prop(rb->txn_root, nb->path, name, value, 791251881Speter pb->validate_props, nb->pool); 792251881Speter} 793251881Speter 794251881Speter 795251881Speterstatic svn_error_t * 796251881Speterdelete_node_property(void *baton, 797251881Speter const char *name) 798251881Speter{ 799251881Speter struct node_baton *nb = baton; 800251881Speter struct revision_baton *rb = nb->rb; 801251881Speter 802251881Speter /* If we're skipping this revision, we're done here. */ 803251881Speter if (rb->skipped) 804251881Speter return SVN_NO_ERROR; 805251881Speter 806251881Speter return change_node_prop(rb->txn_root, nb->path, name, NULL, 807251881Speter rb->pb->validate_props, nb->pool); 808251881Speter} 809251881Speter 810251881Speter 811251881Speterstatic svn_error_t * 812251881Speterremove_node_props(void *baton) 813251881Speter{ 814251881Speter struct node_baton *nb = baton; 815251881Speter struct revision_baton *rb = nb->rb; 816251881Speter apr_hash_t *proplist; 817251881Speter apr_hash_index_t *hi; 818251881Speter 819251881Speter /* If we're skipping this revision, we're done here. */ 820251881Speter if (rb->skipped) 821251881Speter return SVN_NO_ERROR; 822251881Speter 823251881Speter SVN_ERR(svn_fs_node_proplist(&proplist, 824251881Speter rb->txn_root, nb->path, nb->pool)); 825251881Speter 826251881Speter for (hi = apr_hash_first(nb->pool, proplist); hi; hi = apr_hash_next(hi)) 827251881Speter { 828251881Speter const void *key; 829251881Speter 830251881Speter apr_hash_this(hi, &key, NULL, NULL); 831251881Speter SVN_ERR(change_node_prop(rb->txn_root, nb->path, key, NULL, 832251881Speter rb->pb->validate_props, nb->pool)); 833251881Speter } 834251881Speter 835251881Speter return SVN_NO_ERROR; 836251881Speter} 837251881Speter 838251881Speter 839251881Speterstatic svn_error_t * 840251881Speterapply_textdelta(svn_txdelta_window_handler_t *handler, 841251881Speter void **handler_baton, 842251881Speter void *node_baton) 843251881Speter{ 844251881Speter struct node_baton *nb = node_baton; 845251881Speter struct revision_baton *rb = nb->rb; 846251881Speter 847251881Speter /* If we're skipping this revision, we're done here. */ 848251881Speter if (rb->skipped) 849251881Speter { 850251881Speter *handler = NULL; 851251881Speter return SVN_NO_ERROR; 852251881Speter } 853251881Speter 854251881Speter return svn_fs_apply_textdelta(handler, handler_baton, 855251881Speter rb->txn_root, nb->path, 856251881Speter svn_checksum_to_cstring(nb->base_checksum, 857251881Speter nb->pool), 858251881Speter svn_checksum_to_cstring(nb->result_checksum, 859251881Speter nb->pool), 860251881Speter nb->pool); 861251881Speter} 862251881Speter 863251881Speter 864251881Speterstatic svn_error_t * 865251881Speterset_fulltext(svn_stream_t **stream, 866251881Speter void *node_baton) 867251881Speter{ 868251881Speter struct node_baton *nb = node_baton; 869251881Speter struct revision_baton *rb = nb->rb; 870251881Speter 871251881Speter /* If we're skipping this revision, we're done here. */ 872251881Speter if (rb->skipped) 873251881Speter { 874251881Speter *stream = NULL; 875251881Speter return SVN_NO_ERROR; 876251881Speter } 877251881Speter 878251881Speter return svn_fs_apply_text(stream, 879251881Speter rb->txn_root, nb->path, 880251881Speter svn_checksum_to_cstring(nb->result_checksum, 881251881Speter nb->pool), 882251881Speter nb->pool); 883251881Speter} 884251881Speter 885251881Speter 886251881Speterstatic svn_error_t * 887251881Speterclose_node(void *baton) 888251881Speter{ 889251881Speter struct node_baton *nb = baton; 890251881Speter struct revision_baton *rb = nb->rb; 891251881Speter struct parse_baton *pb = rb->pb; 892251881Speter 893251881Speter /* If we're skipping this revision, we're done here. */ 894251881Speter if (rb->skipped) 895251881Speter return SVN_NO_ERROR; 896251881Speter 897251881Speter if (pb->notify_func) 898251881Speter { 899251881Speter pb->notify->action = svn_repos_notify_load_node_done; 900251881Speter pb->notify_func(pb->notify_baton, pb->notify, rb->pool); 901251881Speter } 902251881Speter 903251881Speter return SVN_NO_ERROR; 904251881Speter} 905251881Speter 906251881Speter 907251881Speterstatic svn_error_t * 908251881Speterclose_revision(void *baton) 909251881Speter{ 910251881Speter struct revision_baton *rb = baton; 911251881Speter struct parse_baton *pb = rb->pb; 912251881Speter const char *conflict_msg = NULL; 913251881Speter svn_revnum_t committed_rev; 914251881Speter svn_error_t *err; 915251881Speter const char *txn_name = NULL; 916251881Speter apr_hash_t *hooks_env; 917251881Speter 918251881Speter /* If we're skipping this revision or it has an invalid revision 919251881Speter number, we're done here. */ 920251881Speter if (rb->skipped || (rb->rev <= 0)) 921251881Speter return SVN_NO_ERROR; 922251881Speter 923251881Speter /* Get the txn name and hooks environment if they will be needed. */ 924251881Speter if (pb->use_pre_commit_hook || pb->use_post_commit_hook) 925251881Speter { 926251881Speter SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, pb->repos->hooks_env_path, 927251881Speter rb->pool, rb->pool)); 928251881Speter 929251881Speter err = svn_fs_txn_name(&txn_name, rb->txn, rb->pool); 930251881Speter if (err) 931251881Speter { 932251881Speter svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool)); 933251881Speter return svn_error_trace(err); 934251881Speter } 935251881Speter } 936251881Speter 937251881Speter /* Run the pre-commit hook, if so commanded. */ 938251881Speter if (pb->use_pre_commit_hook) 939251881Speter { 940251881Speter err = svn_repos__hooks_pre_commit(pb->repos, hooks_env, 941251881Speter txn_name, rb->pool); 942251881Speter if (err) 943251881Speter { 944251881Speter svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool)); 945251881Speter return svn_error_trace(err); 946251881Speter } 947251881Speter } 948251881Speter 949251881Speter /* Commit. */ 950251881Speter err = svn_fs_commit_txn(&conflict_msg, &committed_rev, rb->txn, rb->pool); 951251881Speter if (SVN_IS_VALID_REVNUM(committed_rev)) 952251881Speter { 953251881Speter if (err) 954251881Speter { 955251881Speter /* ### Log any error, but better yet is to rev 956251881Speter ### close_revision()'s API to allow both committed_rev and err 957251881Speter ### to be returned, see #3768. */ 958251881Speter svn_error_clear(err); 959251881Speter } 960251881Speter } 961251881Speter else 962251881Speter { 963251881Speter svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool)); 964251881Speter if (conflict_msg) 965251881Speter return svn_error_quick_wrap(err, conflict_msg); 966251881Speter else 967251881Speter return svn_error_trace(err); 968251881Speter } 969251881Speter 970251881Speter /* Run post-commit hook, if so commanded. */ 971251881Speter if (pb->use_post_commit_hook) 972251881Speter { 973251881Speter if ((err = svn_repos__hooks_post_commit(pb->repos, hooks_env, 974251881Speter committed_rev, txn_name, 975251881Speter rb->pool))) 976251881Speter return svn_error_create 977251881Speter (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err, 978251881Speter _("Commit succeeded, but post-commit hook failed")); 979251881Speter } 980251881Speter 981251881Speter /* After a successful commit, must record the dump-rev -> in-repos-rev 982251881Speter mapping, so that copyfrom instructions in the dump file can look up the 983251881Speter correct repository revision to copy from. */ 984251881Speter set_revision_mapping(pb->rev_map, rb->rev, committed_rev); 985251881Speter 986251881Speter /* If the incoming dump stream has non-contiguous revisions (e.g. from 987251881Speter using svndumpfilter --drop-empty-revs without --renumber-revs) then 988251881Speter we must account for the missing gaps in PB->REV_MAP. Otherwise we 989251881Speter might not be able to map all mergeinfo source revisions to the correct 990251881Speter revisions in the target repos. */ 991251881Speter if ((pb->last_rev_mapped != SVN_INVALID_REVNUM) 992251881Speter && (rb->rev != pb->last_rev_mapped + 1)) 993251881Speter { 994251881Speter svn_revnum_t i; 995251881Speter 996251881Speter for (i = pb->last_rev_mapped + 1; i < rb->rev; i++) 997251881Speter { 998251881Speter set_revision_mapping(pb->rev_map, i, pb->last_rev_mapped); 999251881Speter } 1000251881Speter } 1001251881Speter 1002251881Speter /* Update our "last revision mapped". */ 1003251881Speter pb->last_rev_mapped = rb->rev; 1004251881Speter 1005251881Speter /* Deltify the predecessors of paths changed in this revision. */ 1006251881Speter SVN_ERR(svn_fs_deltify_revision(pb->fs, committed_rev, rb->pool)); 1007251881Speter 1008251881Speter /* Grrr, svn_fs_commit_txn rewrites the datestamp property to the 1009251881Speter current clock-time. We don't want that, we want to preserve 1010251881Speter history exactly. Good thing revision props aren't versioned! 1011251881Speter Note that if rb->datestamp is NULL, that's fine -- if the dump 1012251881Speter data doesn't carry a datestamp, we want to preserve that fact in 1013251881Speter the load. */ 1014251881Speter SVN_ERR(change_rev_prop(pb->repos, committed_rev, SVN_PROP_REVISION_DATE, 1015251881Speter rb->datestamp, pb->validate_props, rb->pool)); 1016251881Speter 1017251881Speter if (pb->notify_func) 1018251881Speter { 1019251881Speter pb->notify->action = svn_repos_notify_load_txn_committed; 1020251881Speter pb->notify->new_revision = committed_rev; 1021251881Speter pb->notify->old_revision = ((committed_rev == rb->rev) 1022251881Speter ? SVN_INVALID_REVNUM 1023251881Speter : rb->rev); 1024251881Speter pb->notify_func(pb->notify_baton, pb->notify, rb->pool); 1025251881Speter } 1026251881Speter 1027251881Speter return SVN_NO_ERROR; 1028251881Speter} 1029251881Speter 1030251881Speter 1031251881Speter/*----------------------------------------------------------------------*/ 1032251881Speter 1033251881Speter/** The public routines **/ 1034251881Speter 1035251881Speter 1036251881Spetersvn_error_t * 1037251881Spetersvn_repos_get_fs_build_parser4(const svn_repos_parse_fns3_t **callbacks, 1038251881Speter void **parse_baton, 1039251881Speter svn_repos_t *repos, 1040251881Speter svn_revnum_t start_rev, 1041251881Speter svn_revnum_t end_rev, 1042251881Speter svn_boolean_t use_history, 1043251881Speter svn_boolean_t validate_props, 1044251881Speter enum svn_repos_load_uuid uuid_action, 1045251881Speter const char *parent_dir, 1046251881Speter svn_repos_notify_func_t notify_func, 1047251881Speter void *notify_baton, 1048251881Speter apr_pool_t *pool) 1049251881Speter{ 1050251881Speter svn_repos_parse_fns3_t *parser = apr_pcalloc(pool, sizeof(*parser)); 1051251881Speter struct parse_baton *pb = apr_pcalloc(pool, sizeof(*pb)); 1052251881Speter 1053251881Speter if (parent_dir) 1054251881Speter parent_dir = svn_relpath_canonicalize(parent_dir, pool); 1055251881Speter 1056251881Speter SVN_ERR_ASSERT((SVN_IS_VALID_REVNUM(start_rev) && 1057251881Speter SVN_IS_VALID_REVNUM(end_rev)) 1058251881Speter || ((! SVN_IS_VALID_REVNUM(start_rev)) && 1059251881Speter (! SVN_IS_VALID_REVNUM(end_rev)))); 1060251881Speter if (SVN_IS_VALID_REVNUM(start_rev)) 1061251881Speter SVN_ERR_ASSERT(start_rev <= end_rev); 1062251881Speter 1063251881Speter parser->magic_header_record = magic_header_record; 1064251881Speter parser->uuid_record = uuid_record; 1065251881Speter parser->new_revision_record = new_revision_record; 1066251881Speter parser->new_node_record = new_node_record; 1067251881Speter parser->set_revision_property = set_revision_property; 1068251881Speter parser->set_node_property = set_node_property; 1069251881Speter parser->remove_node_props = remove_node_props; 1070251881Speter parser->set_fulltext = set_fulltext; 1071251881Speter parser->close_node = close_node; 1072251881Speter parser->close_revision = close_revision; 1073251881Speter parser->delete_node_property = delete_node_property; 1074251881Speter parser->apply_textdelta = apply_textdelta; 1075251881Speter 1076251881Speter pb->repos = repos; 1077251881Speter pb->fs = svn_repos_fs(repos); 1078251881Speter pb->use_history = use_history; 1079251881Speter pb->validate_props = validate_props; 1080251881Speter pb->notify_func = notify_func; 1081251881Speter pb->notify_baton = notify_baton; 1082251881Speter pb->notify = svn_repos_notify_create(svn_repos_notify_load_txn_start, pool); 1083251881Speter pb->uuid_action = uuid_action; 1084251881Speter pb->parent_dir = parent_dir; 1085251881Speter pb->pool = pool; 1086251881Speter pb->rev_map = apr_hash_make(pool); 1087251881Speter pb->oldest_old_rev = SVN_INVALID_REVNUM; 1088251881Speter pb->last_rev_mapped = SVN_INVALID_REVNUM; 1089251881Speter pb->start_rev = start_rev; 1090251881Speter pb->end_rev = end_rev; 1091251881Speter 1092251881Speter *callbacks = parser; 1093251881Speter *parse_baton = pb; 1094251881Speter return SVN_NO_ERROR; 1095251881Speter} 1096251881Speter 1097251881Speter 1098251881Speter 1099251881Spetersvn_error_t * 1100251881Spetersvn_repos_load_fs4(svn_repos_t *repos, 1101251881Speter svn_stream_t *dumpstream, 1102251881Speter svn_revnum_t start_rev, 1103251881Speter svn_revnum_t end_rev, 1104251881Speter enum svn_repos_load_uuid uuid_action, 1105251881Speter const char *parent_dir, 1106251881Speter svn_boolean_t use_pre_commit_hook, 1107251881Speter svn_boolean_t use_post_commit_hook, 1108251881Speter svn_boolean_t validate_props, 1109251881Speter svn_repos_notify_func_t notify_func, 1110251881Speter void *notify_baton, 1111251881Speter svn_cancel_func_t cancel_func, 1112251881Speter void *cancel_baton, 1113251881Speter apr_pool_t *pool) 1114251881Speter{ 1115251881Speter const svn_repos_parse_fns3_t *parser; 1116251881Speter void *parse_baton; 1117251881Speter struct parse_baton *pb; 1118251881Speter 1119251881Speter /* This is really simple. */ 1120251881Speter 1121251881Speter SVN_ERR(svn_repos_get_fs_build_parser4(&parser, &parse_baton, 1122251881Speter repos, 1123251881Speter start_rev, end_rev, 1124251881Speter TRUE, /* look for copyfrom revs */ 1125251881Speter validate_props, 1126251881Speter uuid_action, 1127251881Speter parent_dir, 1128251881Speter notify_func, 1129251881Speter notify_baton, 1130251881Speter pool)); 1131251881Speter 1132251881Speter /* Heh. We know this is a parse_baton. This file made it. So 1133251881Speter cast away, and set our hook booleans. */ 1134251881Speter pb = parse_baton; 1135251881Speter pb->use_pre_commit_hook = use_pre_commit_hook; 1136251881Speter pb->use_post_commit_hook = use_post_commit_hook; 1137251881Speter 1138251881Speter return svn_repos_parse_dumpstream3(dumpstream, parser, parse_baton, FALSE, 1139251881Speter cancel_func, cancel_baton, pool); 1140251881Speter} 1141