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(&copy_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