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