1251881Speter/*
2251881Speter *  load_editor.c: The svn_delta_editor_t editor used by svnrdump to
3251881Speter *  load revisions.
4251881Speter *
5251881Speter * ====================================================================
6251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
7251881Speter *    or more contributor license agreements.  See the NOTICE file
8251881Speter *    distributed with this work for additional information
9251881Speter *    regarding copyright ownership.  The ASF licenses this file
10251881Speter *    to you under the Apache License, Version 2.0 (the
11251881Speter *    "License"); you may not use this file except in compliance
12251881Speter *    with the License.  You may obtain a copy of the License at
13251881Speter *
14251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
15251881Speter *
16251881Speter *    Unless required by applicable law or agreed to in writing,
17251881Speter *    software distributed under the License is distributed on an
18251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19251881Speter *    KIND, either express or implied.  See the License for the
20251881Speter *    specific language governing permissions and limitations
21251881Speter *    under the License.
22251881Speter * ====================================================================
23251881Speter */
24251881Speter
25251881Speter#include "svn_cmdline.h"
26251881Speter#include "svn_pools.h"
27251881Speter#include "svn_delta.h"
28251881Speter#include "svn_repos.h"
29251881Speter#include "svn_props.h"
30251881Speter#include "svn_path.h"
31251881Speter#include "svn_ra.h"
32251881Speter#include "svn_subst.h"
33251881Speter#include "svn_io.h"
34251881Speter#include "svn_private_config.h"
35251881Speter#include "private/svn_repos_private.h"
36251881Speter#include "private/svn_ra_private.h"
37251881Speter#include "private/svn_mergeinfo_private.h"
38251881Speter#include "private/svn_fspath.h"
39251881Speter
40251881Speter#include "svnrdump.h"
41251881Speter
42251881Speter#define SVNRDUMP_PROP_LOCK SVN_PROP_PREFIX "rdump-lock"
43251881Speter
44289166Speter#define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
45289166Speter
46251881Speter
47251881Speter/**
48251881Speter * General baton used by the parser functions.
49251881Speter */
50251881Speterstruct parse_baton
51251881Speter{
52251881Speter  /* Commit editor and baton used to transfer loaded revisions to
53251881Speter     the target repository. */
54251881Speter  const svn_delta_editor_t *commit_editor;
55251881Speter  void *commit_edit_baton;
56251881Speter
57251881Speter  /* RA session(s) for committing to the target repository. */
58251881Speter  svn_ra_session_t *session;
59251881Speter  svn_ra_session_t *aux_session;
60251881Speter
61251881Speter  /* To bleep, or not to bleep?  (What kind of question is that?) */
62251881Speter  svn_boolean_t quiet;
63251881Speter
64251881Speter  /* Root URL of the target repository. */
65251881Speter  const char *root_url;
66251881Speter
67251881Speter  /* The "parent directory" of the target repository in which to load.
68251881Speter     (This is essentially the difference between ROOT_URL and
69251881Speter     SESSION's url, and roughly equivalent to the 'svnadmin load
70251881Speter     --parent-dir' option.) */
71251881Speter  const char *parent_dir;
72251881Speter
73251881Speter  /* A mapping of svn_revnum_t * dump stream revisions to their
74251881Speter     corresponding svn_revnum_t * target repository revisions. */
75251881Speter  /* ### See http://subversion.tigris.org/issues/show_bug.cgi?id=3903
76251881Speter     ### for discussion about improving the memory costs of this mapping. */
77251881Speter  apr_hash_t *rev_map;
78251881Speter
79251881Speter  /* The most recent (youngest) revision from the dump stream mapped in
80251881Speter     REV_MAP, or SVN_INVALID_REVNUM if no revisions have been mapped. */
81251881Speter  svn_revnum_t last_rev_mapped;
82251881Speter
83251881Speter  /* The oldest revision loaded from the dump stream, or
84251881Speter     SVN_INVALID_REVNUM if none have been loaded. */
85251881Speter  svn_revnum_t oldest_dumpstream_rev;
86299742Sdim
87299742Sdim  /* An hash containing specific revision properties to skip while
88299742Sdim     loading. */
89299742Sdim  apr_hash_t *skip_revprops;
90251881Speter};
91251881Speter
92251881Speter/**
93251881Speter * Use to wrap the dir_context_t in commit.c so we can keep track of
94299742Sdim * relpath and parent for open_directory and close_directory.
95251881Speter */
96251881Speterstruct directory_baton
97251881Speter{
98251881Speter  void *baton;
99251881Speter  const char *relpath;
100289166Speter
101289166Speter  /* The copy-from source of this directory, no matter whether it is
102289166Speter     copied explicitly (the root node of a copy) or implicitly (being an
103289166Speter     existing child of a copied directory). For a node that is newly
104289166Speter     added (without history), even inside a copied parent, these are
105289166Speter     NULL and SVN_INVALID_REVNUM. */
106289166Speter  const char *copyfrom_path;
107289166Speter  svn_revnum_t copyfrom_rev;
108289166Speter
109251881Speter  struct directory_baton *parent;
110251881Speter};
111251881Speter
112251881Speter/**
113251881Speter * Baton used to represent a node; to be used by the parser
114251881Speter * functions. Contains a link to the revision baton.
115251881Speter */
116251881Speterstruct node_baton
117251881Speter{
118251881Speter  const char *path;
119251881Speter  svn_node_kind_t kind;
120251881Speter  enum svn_node_action action;
121251881Speter
122289166Speter  /* Is this directory explicitly added? If not, then it already existed
123289166Speter     or is a child of a copy. */
124289166Speter  svn_boolean_t is_added;
125289166Speter
126251881Speter  svn_revnum_t copyfrom_rev;
127251881Speter  const char *copyfrom_path;
128289166Speter  const char *copyfrom_url;
129251881Speter
130251881Speter  void *file_baton;
131251881Speter  const char *base_checksum;
132251881Speter
133289166Speter  /* (const char *name) -> (svn_prop_t *) */
134289166Speter  apr_hash_t *prop_changes;
135289166Speter
136251881Speter  struct revision_baton *rb;
137251881Speter};
138251881Speter
139251881Speter/**
140251881Speter * Baton used to represet a revision; used by the parser
141251881Speter * functions. Contains a link to the parser baton.
142251881Speter */
143251881Speterstruct revision_baton
144251881Speter{
145251881Speter  svn_revnum_t rev;
146251881Speter  apr_hash_t *revprop_table;
147251881Speter  apr_int32_t rev_offset;
148251881Speter
149251881Speter  const svn_string_t *datestamp;
150251881Speter  const svn_string_t *author;
151251881Speter
152251881Speter  struct parse_baton *pb;
153251881Speter  struct directory_baton *db;
154251881Speter  apr_pool_t *pool;
155251881Speter};
156251881Speter
157251881Speter
158251881Speter
159251881Speter/* Record the mapping of FROM_REV to TO_REV in REV_MAP, ensuring that
160251881Speter   anything added to the hash is allocated in the hash's pool. */
161251881Speterstatic void
162251881Speterset_revision_mapping(apr_hash_t *rev_map,
163251881Speter                     svn_revnum_t from_rev,
164251881Speter                     svn_revnum_t to_rev)
165251881Speter{
166251881Speter  svn_revnum_t *mapped_revs = apr_palloc(apr_hash_pool_get(rev_map),
167251881Speter                                         sizeof(svn_revnum_t) * 2);
168251881Speter  mapped_revs[0] = from_rev;
169251881Speter  mapped_revs[1] = to_rev;
170251881Speter  apr_hash_set(rev_map, mapped_revs,
171251881Speter               sizeof(svn_revnum_t), mapped_revs + 1);
172251881Speter}
173251881Speter
174251881Speter/* Return the revision to which FROM_REV maps in REV_MAP, or
175251881Speter   SVN_INVALID_REVNUM if no such mapping exists. */
176251881Speterstatic svn_revnum_t
177251881Speterget_revision_mapping(apr_hash_t *rev_map,
178251881Speter                     svn_revnum_t from_rev)
179251881Speter{
180251881Speter  svn_revnum_t *to_rev = apr_hash_get(rev_map, &from_rev,
181251881Speter                                      sizeof(from_rev));
182251881Speter  return to_rev ? *to_rev : SVN_INVALID_REVNUM;
183251881Speter}
184251881Speter
185251881Speter
186251881Speterstatic svn_error_t *
187251881Spetercommit_callback(const svn_commit_info_t *commit_info,
188251881Speter                void *baton,
189251881Speter                apr_pool_t *pool)
190251881Speter{
191251881Speter  struct revision_baton *rb = baton;
192251881Speter  struct parse_baton *pb = rb->pb;
193251881Speter
194251881Speter  /* ### Don't print directly; generate a notification. */
195251881Speter  if (! pb->quiet)
196251881Speter    SVN_ERR(svn_cmdline_printf(pool, "* Loaded revision %ld.\n",
197251881Speter                               commit_info->revision));
198251881Speter
199251881Speter  /* Add the mapping of the dumpstream revision to the committed revision. */
200251881Speter  set_revision_mapping(pb->rev_map, rb->rev, commit_info->revision);
201251881Speter
202251881Speter  /* If the incoming dump stream has non-contiguous revisions (e.g. from
203251881Speter     using svndumpfilter --drop-empty-revs without --renumber-revs) then
204251881Speter     we must account for the missing gaps in PB->REV_MAP.  Otherwise we
205251881Speter     might not be able to map all mergeinfo source revisions to the correct
206251881Speter     revisions in the target repos. */
207251881Speter  if ((pb->last_rev_mapped != SVN_INVALID_REVNUM)
208251881Speter      && (rb->rev != pb->last_rev_mapped + 1))
209251881Speter    {
210251881Speter      svn_revnum_t i;
211251881Speter
212251881Speter      for (i = pb->last_rev_mapped + 1; i < rb->rev; i++)
213251881Speter        {
214251881Speter          set_revision_mapping(pb->rev_map, i, pb->last_rev_mapped);
215251881Speter        }
216251881Speter    }
217251881Speter
218251881Speter  /* Update our "last revision mapped". */
219251881Speter  pb->last_rev_mapped = rb->rev;
220251881Speter
221251881Speter  return SVN_NO_ERROR;
222251881Speter}
223251881Speter
224251881Speter/* Implements `svn_ra__lock_retry_func_t'. */
225251881Speterstatic svn_error_t *
226251881Speterlock_retry_func(void *baton,
227251881Speter                const svn_string_t *reposlocktoken,
228251881Speter                apr_pool_t *pool)
229251881Speter{
230251881Speter  return svn_cmdline_printf(pool,
231251881Speter                            _("Failed to get lock on destination "
232251881Speter                              "repos, currently held by '%s'\n"),
233251881Speter                            reposlocktoken->data);
234251881Speter}
235251881Speter
236251881Speter
237251881Speterstatic svn_error_t *
238251881Speterfetch_base_func(const char **filename,
239251881Speter                void *baton,
240251881Speter                const char *path,
241251881Speter                svn_revnum_t base_revision,
242251881Speter                apr_pool_t *result_pool,
243251881Speter                apr_pool_t *scratch_pool)
244251881Speter{
245251881Speter  struct revision_baton *rb = baton;
246251881Speter  svn_stream_t *fstream;
247251881Speter  svn_error_t *err;
248251881Speter
249251881Speter  if (! SVN_IS_VALID_REVNUM(base_revision))
250251881Speter    base_revision = rb->rev - 1;
251251881Speter
252251881Speter  SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL,
253251881Speter                                 svn_io_file_del_on_pool_cleanup,
254251881Speter                                 result_pool, scratch_pool));
255251881Speter
256251881Speter  err = svn_ra_get_file(rb->pb->aux_session, path, base_revision,
257251881Speter                        fstream, NULL, NULL, scratch_pool);
258251881Speter  if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
259251881Speter    {
260251881Speter      svn_error_clear(err);
261251881Speter      SVN_ERR(svn_stream_close(fstream));
262251881Speter
263251881Speter      *filename = NULL;
264251881Speter      return SVN_NO_ERROR;
265251881Speter    }
266251881Speter  else if (err)
267251881Speter    return svn_error_trace(err);
268251881Speter
269251881Speter  SVN_ERR(svn_stream_close(fstream));
270251881Speter
271251881Speter  return SVN_NO_ERROR;
272251881Speter}
273251881Speter
274251881Speterstatic svn_error_t *
275251881Speterfetch_props_func(apr_hash_t **props,
276251881Speter                 void *baton,
277251881Speter                 const char *path,
278251881Speter                 svn_revnum_t base_revision,
279251881Speter                 apr_pool_t *result_pool,
280251881Speter                 apr_pool_t *scratch_pool)
281251881Speter{
282251881Speter  struct revision_baton *rb = baton;
283251881Speter  svn_node_kind_t node_kind;
284251881Speter
285251881Speter  if (! SVN_IS_VALID_REVNUM(base_revision))
286251881Speter    base_revision = rb->rev - 1;
287251881Speter
288251881Speter  SVN_ERR(svn_ra_check_path(rb->pb->aux_session, path, base_revision,
289251881Speter                            &node_kind, scratch_pool));
290251881Speter
291251881Speter  if (node_kind == svn_node_file)
292251881Speter    {
293251881Speter      SVN_ERR(svn_ra_get_file(rb->pb->aux_session, path, base_revision,
294251881Speter                              NULL, NULL, props, result_pool));
295251881Speter    }
296251881Speter  else if (node_kind == svn_node_dir)
297251881Speter    {
298251881Speter      apr_array_header_t *tmp_props;
299251881Speter
300251881Speter      SVN_ERR(svn_ra_get_dir2(rb->pb->aux_session, NULL, NULL, props, path,
301251881Speter                              base_revision, 0 /* Dirent fields */,
302251881Speter                              result_pool));
303251881Speter      tmp_props = svn_prop_hash_to_array(*props, result_pool);
304251881Speter      SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props,
305251881Speter                                   result_pool));
306251881Speter      *props = svn_prop_array_to_hash(tmp_props, result_pool);
307251881Speter    }
308251881Speter  else
309251881Speter    {
310251881Speter      *props = apr_hash_make(result_pool);
311251881Speter    }
312251881Speter
313251881Speter  return SVN_NO_ERROR;
314251881Speter}
315251881Speter
316251881Speterstatic svn_error_t *
317251881Speterfetch_kind_func(svn_node_kind_t *kind,
318251881Speter                void *baton,
319251881Speter                const char *path,
320251881Speter                svn_revnum_t base_revision,
321251881Speter                apr_pool_t *scratch_pool)
322251881Speter{
323251881Speter  struct revision_baton *rb = baton;
324251881Speter
325251881Speter  if (! SVN_IS_VALID_REVNUM(base_revision))
326251881Speter    base_revision = rb->rev - 1;
327251881Speter
328251881Speter  SVN_ERR(svn_ra_check_path(rb->pb->aux_session, path, base_revision,
329251881Speter                            kind, scratch_pool));
330251881Speter
331251881Speter  return SVN_NO_ERROR;
332251881Speter}
333251881Speter
334251881Speterstatic svn_delta_shim_callbacks_t *
335251881Speterget_shim_callbacks(struct revision_baton *rb,
336251881Speter                   apr_pool_t *pool)
337251881Speter{
338251881Speter  svn_delta_shim_callbacks_t *callbacks =
339251881Speter                        svn_delta_shim_callbacks_default(pool);
340251881Speter
341251881Speter  callbacks->fetch_props_func = fetch_props_func;
342251881Speter  callbacks->fetch_kind_func = fetch_kind_func;
343251881Speter  callbacks->fetch_base_func = fetch_base_func;
344251881Speter  callbacks->fetch_baton = rb;
345251881Speter
346251881Speter  return callbacks;
347251881Speter}
348251881Speter
349251881Speter/* Acquire a lock (of sorts) on the repository associated with the
350251881Speter * given RA SESSION. This lock is just a revprop change attempt in a
351251881Speter * time-delay loop. This function is duplicated by svnsync in
352251881Speter * svnsync/svnsync.c
353251881Speter *
354251881Speter * ### TODO: Make this function more generic and
355251881Speter * expose it through a header for use by other Subversion
356251881Speter * applications to avoid duplication.
357251881Speter */
358251881Speterstatic svn_error_t *
359251881Speterget_lock(const svn_string_t **lock_string_p,
360251881Speter         svn_ra_session_t *session,
361251881Speter         svn_cancel_func_t cancel_func,
362251881Speter         void *cancel_baton,
363251881Speter         apr_pool_t *pool)
364251881Speter{
365251881Speter  svn_boolean_t be_atomic;
366251881Speter
367251881Speter  SVN_ERR(svn_ra_has_capability(session, &be_atomic,
368251881Speter                                SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
369251881Speter                                pool));
370251881Speter  if (! be_atomic)
371251881Speter    {
372251881Speter      /* Pre-1.7 servers can't lock without a race condition.  (Issue #3546) */
373251881Speter      svn_error_t *err =
374251881Speter        svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
375251881Speter                         _("Target server does not support atomic revision "
376251881Speter                           "property edits; consider upgrading it to 1.7."));
377251881Speter      svn_handle_warning2(stderr, err, "svnrdump: ");
378251881Speter      svn_error_clear(err);
379251881Speter    }
380251881Speter
381251881Speter  return svn_ra__get_operational_lock(lock_string_p, NULL, session,
382251881Speter                                      SVNRDUMP_PROP_LOCK, FALSE,
383251881Speter                                      10 /* retries */, lock_retry_func, NULL,
384251881Speter                                      cancel_func, cancel_baton, pool);
385251881Speter}
386251881Speter
387251881Speterstatic svn_error_t *
388251881Speternew_revision_record(void **revision_baton,
389251881Speter                    apr_hash_t *headers,
390251881Speter                    void *parse_baton,
391251881Speter                    apr_pool_t *pool)
392251881Speter{
393251881Speter  struct revision_baton *rb;
394251881Speter  struct parse_baton *pb;
395251881Speter  apr_hash_index_t *hi;
396251881Speter  svn_revnum_t head_rev;
397251881Speter
398251881Speter  rb = apr_pcalloc(pool, sizeof(*rb));
399251881Speter  pb = parse_baton;
400251881Speter  rb->pool = svn_pool_create(pool);
401251881Speter  rb->pb = pb;
402289166Speter  rb->db = NULL;
403251881Speter
404251881Speter  for (hi = apr_hash_first(pool, headers); hi; hi = apr_hash_next(hi))
405251881Speter    {
406299742Sdim      const char *hname = apr_hash_this_key(hi);
407299742Sdim      const char *hval = apr_hash_this_val(hi);
408251881Speter
409251881Speter      if (strcmp(hname, SVN_REPOS_DUMPFILE_REVISION_NUMBER) == 0)
410251881Speter        rb->rev = atoi(hval);
411251881Speter    }
412251881Speter
413251881Speter  SVN_ERR(svn_ra_get_latest_revnum(pb->session, &head_rev, pool));
414251881Speter
415251881Speter  /* FIXME: This is a lame fallback loading multiple segments of dump in
416251881Speter     several separate operations. It is highly susceptible to race conditions.
417251881Speter     Calculate the revision 'offset' for finding copyfrom sources.
418251881Speter     It might be positive or negative. */
419251881Speter  rb->rev_offset = (apr_int32_t) ((rb->rev) - (head_rev + 1));
420251881Speter
421251881Speter  /* Stash the oldest (non-zero) dumpstream revision seen. */
422251881Speter  if ((rb->rev > 0) && (!SVN_IS_VALID_REVNUM(pb->oldest_dumpstream_rev)))
423251881Speter    pb->oldest_dumpstream_rev = rb->rev;
424251881Speter
425251881Speter  /* Set the commit_editor/ commit_edit_baton to NULL and wait for
426251881Speter     them to be created in new_node_record */
427251881Speter  rb->pb->commit_editor = NULL;
428251881Speter  rb->pb->commit_edit_baton = NULL;
429251881Speter  rb->revprop_table = apr_hash_make(rb->pool);
430251881Speter
431251881Speter  *revision_baton = rb;
432251881Speter  return SVN_NO_ERROR;
433251881Speter}
434251881Speter
435251881Speterstatic svn_error_t *
436251881Spetermagic_header_record(int version,
437251881Speter            void *parse_baton,
438251881Speter            apr_pool_t *pool)
439251881Speter{
440251881Speter  return SVN_NO_ERROR;
441251881Speter}
442251881Speter
443251881Speterstatic svn_error_t *
444251881Speteruuid_record(const char *uuid,
445251881Speter            void *parse_baton,
446251881Speter            apr_pool_t *pool)
447251881Speter{
448251881Speter  return SVN_NO_ERROR;
449251881Speter}
450251881Speter
451289166Speter/* Push information about another directory onto the linked list RB->db.
452289166Speter *
453289166Speter * CHILD_BATON is the baton returned by the commit editor. RELPATH is the
454289166Speter * repository-relative path of this directory. IS_ADDED is true iff this
455289166Speter * directory is being added (with or without history). If added with
456289166Speter * history then COPYFROM_PATH/COPYFROM_REV are the copyfrom source, else
457289166Speter * are NULL/SVN_INVALID_REVNUM.
458289166Speter */
459289166Speterstatic void
460289166Speterpush_directory(struct revision_baton *rb,
461289166Speter               void *child_baton,
462289166Speter               const char *relpath,
463289166Speter               svn_boolean_t is_added,
464289166Speter               const char *copyfrom_path,
465289166Speter               svn_revnum_t copyfrom_rev)
466289166Speter{
467289166Speter  struct directory_baton *child_db = apr_pcalloc(rb->pool, sizeof (*child_db));
468289166Speter
469289166Speter  SVN_ERR_ASSERT_NO_RETURN(
470289166Speter    is_added || (copyfrom_path == NULL && copyfrom_rev == SVN_INVALID_REVNUM));
471289166Speter
472289166Speter  /* If this node is an existing (not newly added) child of a copied node,
473289166Speter     calculate where it was copied from. */
474289166Speter  if (!is_added
475289166Speter      && ARE_VALID_COPY_ARGS(rb->db->copyfrom_path, rb->db->copyfrom_rev))
476289166Speter    {
477289166Speter      const char *name = svn_relpath_basename(relpath, NULL);
478289166Speter
479289166Speter      copyfrom_path = svn_relpath_join(rb->db->copyfrom_path, name,
480289166Speter                                       rb->pool);
481289166Speter      copyfrom_rev = rb->db->copyfrom_rev;
482289166Speter    }
483289166Speter
484289166Speter  child_db->baton = child_baton;
485289166Speter  child_db->relpath = relpath;
486289166Speter  child_db->copyfrom_path = copyfrom_path;
487289166Speter  child_db->copyfrom_rev = copyfrom_rev;
488289166Speter  child_db->parent = rb->db;
489289166Speter  rb->db = child_db;
490289166Speter}
491289166Speter
492251881Speterstatic svn_error_t *
493251881Speternew_node_record(void **node_baton,
494251881Speter                apr_hash_t *headers,
495251881Speter                void *revision_baton,
496251881Speter                apr_pool_t *pool)
497251881Speter{
498251881Speter  struct revision_baton *rb = revision_baton;
499251881Speter  const struct svn_delta_editor_t *commit_editor = rb->pb->commit_editor;
500251881Speter  void *commit_edit_baton = rb->pb->commit_edit_baton;
501251881Speter  struct node_baton *nb;
502251881Speter  apr_hash_index_t *hi;
503251881Speter  void *child_baton;
504251881Speter  const char *nb_dirname;
505251881Speter
506251881Speter  nb = apr_pcalloc(rb->pool, sizeof(*nb));
507251881Speter  nb->rb = rb;
508289166Speter  nb->is_added = FALSE;
509251881Speter  nb->copyfrom_path = NULL;
510289166Speter  nb->copyfrom_url = NULL;
511251881Speter  nb->copyfrom_rev = SVN_INVALID_REVNUM;
512289166Speter  nb->prop_changes = apr_hash_make(rb->pool);
513251881Speter
514251881Speter  /* If the creation of commit_editor is pending, create it now and
515251881Speter     open_root on it; also create a top-level directory baton. */
516251881Speter
517251881Speter  if (!commit_editor)
518251881Speter    {
519251881Speter      /* The revprop_table should have been filled in with important
520251881Speter         information like svn:log in set_revision_property. We can now
521251881Speter         use it all this information to create our commit_editor. But
522251881Speter         first, clear revprops that we aren't allowed to set with the
523251881Speter         commit_editor. We'll set them separately using the RA API
524251881Speter         after closing the editor (see close_revision). */
525251881Speter
526251881Speter      svn_hash_sets(rb->revprop_table, SVN_PROP_REVISION_AUTHOR, NULL);
527251881Speter      svn_hash_sets(rb->revprop_table, SVN_PROP_REVISION_DATE, NULL);
528251881Speter
529251881Speter      SVN_ERR(svn_ra__register_editor_shim_callbacks(rb->pb->session,
530251881Speter                                    get_shim_callbacks(rb, rb->pool)));
531251881Speter      SVN_ERR(svn_ra_get_commit_editor3(rb->pb->session, &commit_editor,
532251881Speter                                        &commit_edit_baton, rb->revprop_table,
533251881Speter                                        commit_callback, revision_baton,
534251881Speter                                        NULL, FALSE, rb->pool));
535251881Speter
536251881Speter      rb->pb->commit_editor = commit_editor;
537251881Speter      rb->pb->commit_edit_baton = commit_edit_baton;
538251881Speter
539251881Speter      SVN_ERR(commit_editor->open_root(commit_edit_baton,
540251881Speter                                       rb->rev - rb->rev_offset - 1,
541251881Speter                                       rb->pool, &child_baton));
542251881Speter
543289166Speter      /* child_baton corresponds to the root directory baton here */
544289166Speter      push_directory(rb, child_baton, "", TRUE /*is_added*/,
545289166Speter                     NULL, SVN_INVALID_REVNUM);
546251881Speter    }
547251881Speter
548251881Speter  for (hi = apr_hash_first(rb->pool, headers); hi; hi = apr_hash_next(hi))
549251881Speter    {
550299742Sdim      const char *hname = apr_hash_this_key(hi);
551299742Sdim      const char *hval = apr_hash_this_val(hi);
552251881Speter
553251881Speter      /* Parse the different kinds of headers we can encounter and
554251881Speter         stuff them into the node_baton for writing later */
555251881Speter      if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_PATH) == 0)
556251881Speter        nb->path = apr_pstrdup(rb->pool, hval);
557251881Speter      if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_KIND) == 0)
558251881Speter        nb->kind = strcmp(hval, "file") == 0 ? svn_node_file : svn_node_dir;
559251881Speter      if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_ACTION) == 0)
560251881Speter        {
561251881Speter          if (strcmp(hval, "add") == 0)
562251881Speter            nb->action = svn_node_action_add;
563251881Speter          if (strcmp(hval, "change") == 0)
564251881Speter            nb->action = svn_node_action_change;
565251881Speter          if (strcmp(hval, "delete") == 0)
566251881Speter            nb->action = svn_node_action_delete;
567251881Speter          if (strcmp(hval, "replace") == 0)
568251881Speter            nb->action = svn_node_action_replace;
569251881Speter        }
570251881Speter      if (strcmp(hname, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5) == 0)
571251881Speter        nb->base_checksum = apr_pstrdup(rb->pool, hval);
572251881Speter      if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV) == 0)
573251881Speter        nb->copyfrom_rev = atoi(hval);
574251881Speter      if (strcmp(hname, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH) == 0)
575251881Speter        nb->copyfrom_path = apr_pstrdup(rb->pool, hval);
576251881Speter    }
577251881Speter
578299742Sdim  /* Before handling the new node, ensure depth-first editing order by
579299742Sdim     traversing the directory hierarchy from the old node's to the new
580299742Sdim     node's parent directory. */
581251881Speter  nb_dirname = svn_relpath_dirname(nb->path, pool);
582251881Speter  if (svn_path_compare_paths(nb_dirname,
583251881Speter                             rb->db->relpath) != 0)
584251881Speter    {
585251881Speter      char *ancestor_path;
586251881Speter      apr_size_t residual_close_count;
587251881Speter      apr_array_header_t *residual_open_path;
588251881Speter      int i;
589251881Speter      apr_size_t n;
590251881Speter
591251881Speter      ancestor_path =
592251881Speter        svn_relpath_get_longest_ancestor(nb_dirname,
593251881Speter                                         rb->db->relpath, pool);
594251881Speter      residual_close_count =
595251881Speter        svn_path_component_count(svn_relpath_skip_ancestor(ancestor_path,
596251881Speter                                                           rb->db->relpath));
597251881Speter      residual_open_path =
598251881Speter        svn_path_decompose(svn_relpath_skip_ancestor(ancestor_path,
599251881Speter                                                     nb_dirname), pool);
600251881Speter
601251881Speter      /* First close all as many directories as there are after
602251881Speter         skip_ancestor, and then open fresh directories */
603251881Speter      for (n = 0; n < residual_close_count; n ++)
604251881Speter        {
605251881Speter          /* Don't worry about destroying the actual rb->db object,
606251881Speter             since the pool we're using has the lifetime of one
607251881Speter             revision anyway */
608251881Speter          SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool));
609251881Speter          rb->db = rb->db->parent;
610251881Speter        }
611251881Speter
612251881Speter      for (i = 0; i < residual_open_path->nelts; i ++)
613251881Speter        {
614289166Speter          char *relpath_compose =
615251881Speter            svn_relpath_join(rb->db->relpath,
616251881Speter                             APR_ARRAY_IDX(residual_open_path, i, const char *),
617251881Speter                             rb->pool);
618251881Speter          SVN_ERR(commit_editor->open_directory(relpath_compose,
619251881Speter                                                rb->db->baton,
620251881Speter                                                rb->rev - rb->rev_offset - 1,
621251881Speter                                                rb->pool, &child_baton));
622289166Speter          push_directory(rb, child_baton, relpath_compose, TRUE /*is_added*/,
623289166Speter                         NULL, SVN_INVALID_REVNUM);
624251881Speter        }
625251881Speter    }
626251881Speter
627251881Speter  /* Fix up the copyfrom information in light of mapped revisions and
628251881Speter     non-root load targets, and convert copyfrom path into a full
629251881Speter     URL. */
630251881Speter  if (nb->copyfrom_path && SVN_IS_VALID_REVNUM(nb->copyfrom_rev))
631251881Speter    {
632251881Speter      svn_revnum_t copyfrom_rev;
633251881Speter
634251881Speter      /* Try to find the copyfrom revision in the revision map;
635251881Speter         failing that, fall back to the revision offset approach. */
636251881Speter      copyfrom_rev = get_revision_mapping(rb->pb->rev_map, nb->copyfrom_rev);
637251881Speter      if (! SVN_IS_VALID_REVNUM(copyfrom_rev))
638251881Speter        copyfrom_rev = nb->copyfrom_rev - rb->rev_offset;
639251881Speter
640251881Speter      if (! SVN_IS_VALID_REVNUM(copyfrom_rev))
641251881Speter        return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
642251881Speter                                 _("Relative source revision %ld is not"
643251881Speter                                   " available in current repository"),
644251881Speter                                 copyfrom_rev);
645251881Speter
646251881Speter      nb->copyfrom_rev = copyfrom_rev;
647251881Speter
648251881Speter      if (rb->pb->parent_dir)
649251881Speter        nb->copyfrom_path = svn_relpath_join(rb->pb->parent_dir,
650251881Speter                                             nb->copyfrom_path, rb->pool);
651299742Sdim      /* Convert to a URL, as the commit editor requires. */
652289166Speter      nb->copyfrom_url = svn_path_url_add_component2(rb->pb->root_url,
653251881Speter                                                      nb->copyfrom_path,
654251881Speter                                                      rb->pool);
655251881Speter    }
656251881Speter
657251881Speter
658251881Speter  switch (nb->action)
659251881Speter    {
660251881Speter    case svn_node_action_delete:
661251881Speter    case svn_node_action_replace:
662289166Speter      SVN_ERR(commit_editor->delete_entry(nb->path,
663289166Speter                                          rb->rev - rb->rev_offset - 1,
664251881Speter                                          rb->db->baton, rb->pool));
665251881Speter      if (nb->action == svn_node_action_delete)
666251881Speter        break;
667251881Speter      else
668251881Speter        /* FALL THROUGH */;
669251881Speter    case svn_node_action_add:
670289166Speter      nb->is_added = TRUE;
671251881Speter      switch (nb->kind)
672251881Speter        {
673251881Speter        case svn_node_file:
674251881Speter          SVN_ERR(commit_editor->add_file(nb->path, rb->db->baton,
675289166Speter                                          nb->copyfrom_url,
676251881Speter                                          nb->copyfrom_rev,
677251881Speter                                          rb->pool, &(nb->file_baton)));
678251881Speter          break;
679251881Speter        case svn_node_dir:
680251881Speter          SVN_ERR(commit_editor->add_directory(nb->path, rb->db->baton,
681289166Speter                                               nb->copyfrom_url,
682251881Speter                                               nb->copyfrom_rev,
683251881Speter                                               rb->pool, &child_baton));
684289166Speter          push_directory(rb, child_baton, nb->path, TRUE /*is_added*/,
685289166Speter                         nb->copyfrom_path, nb->copyfrom_rev);
686251881Speter          break;
687251881Speter        default:
688251881Speter          break;
689251881Speter        }
690251881Speter      break;
691251881Speter    case svn_node_action_change:
692251881Speter      switch (nb->kind)
693251881Speter        {
694251881Speter        case svn_node_file:
695251881Speter          SVN_ERR(commit_editor->open_file(nb->path, rb->db->baton,
696251881Speter                                           SVN_INVALID_REVNUM, rb->pool,
697251881Speter                                           &(nb->file_baton)));
698251881Speter          break;
699251881Speter        default:
700251881Speter          SVN_ERR(commit_editor->open_directory(nb->path, rb->db->baton,
701251881Speter                                                rb->rev - rb->rev_offset - 1,
702251881Speter                                                rb->pool, &child_baton));
703289166Speter          push_directory(rb, child_baton, nb->path, FALSE /*is_added*/,
704289166Speter                         NULL, SVN_INVALID_REVNUM);
705251881Speter          break;
706251881Speter        }
707251881Speter      break;
708251881Speter    }
709251881Speter
710251881Speter  *node_baton = nb;
711251881Speter  return SVN_NO_ERROR;
712251881Speter}
713251881Speter
714251881Speterstatic svn_error_t *
715251881Speterset_revision_property(void *baton,
716251881Speter                      const char *name,
717251881Speter                      const svn_string_t *value)
718251881Speter{
719251881Speter  struct revision_baton *rb = baton;
720251881Speter
721251881Speter  SVN_ERR(svn_rdump__normalize_prop(name, &value, rb->pool));
722251881Speter
723251881Speter  SVN_ERR(svn_repos__validate_prop(name, value, rb->pool));
724251881Speter
725251881Speter  if (rb->rev > 0)
726251881Speter    {
727299742Sdim      if (! svn_hash_gets(rb->pb->skip_revprops, name))
728299742Sdim        svn_hash_sets(rb->revprop_table,
729299742Sdim                      apr_pstrdup(rb->pool, name),
730299742Sdim                      svn_string_dup(value, rb->pool));
731251881Speter    }
732299742Sdim  else if (rb->rev_offset == -1
733299742Sdim           && ! svn_hash_gets(rb->pb->skip_revprops, name))
734251881Speter    {
735251881Speter      /* Special case: set revision 0 properties directly (which is
736251881Speter         safe because the commit_editor hasn't been created yet), but
737251881Speter         only when loading into an 'empty' filesystem. */
738251881Speter      SVN_ERR(svn_ra_change_rev_prop2(rb->pb->session, 0,
739251881Speter                                      name, NULL, value, rb->pool));
740251881Speter    }
741251881Speter
742251881Speter  /* Remember any datestamp/ author that passes through (see comment
743251881Speter     in close_revision). */
744251881Speter  if (!strcmp(name, SVN_PROP_REVISION_DATE))
745251881Speter    rb->datestamp = svn_string_dup(value, rb->pool);
746251881Speter  if (!strcmp(name, SVN_PROP_REVISION_AUTHOR))
747251881Speter    rb->author = svn_string_dup(value, rb->pool);
748251881Speter
749251881Speter  return SVN_NO_ERROR;
750251881Speter}
751251881Speter
752251881Speterstatic svn_error_t *
753251881Speterset_node_property(void *baton,
754251881Speter                  const char *name,
755251881Speter                  const svn_string_t *value)
756251881Speter{
757251881Speter  struct node_baton *nb = baton;
758299742Sdim  struct revision_baton *rb = nb->rb;
759299742Sdim  struct parse_baton *pb = rb->pb;
760251881Speter  apr_pool_t *pool = nb->rb->pool;
761289166Speter  svn_prop_t *prop;
762251881Speter
763251881Speter  if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0)
764251881Speter    {
765299742Sdim      svn_string_t *new_value;
766299742Sdim      svn_error_t *err;
767251881Speter
768299742Sdim      err = svn_repos__adjust_mergeinfo_property(&new_value, value,
769299742Sdim                                                 pb->parent_dir,
770299742Sdim                                                 pb->rev_map,
771299742Sdim                                                 pb->oldest_dumpstream_rev,
772299742Sdim                                                 rb->rev_offset,
773299742Sdim                                                 NULL, NULL, /*notify*/
774299742Sdim                                                 pool, pool);
775299742Sdim      if (err)
776251881Speter        {
777299742Sdim          return svn_error_quick_wrap(err,
778299742Sdim                                      _("Invalid svn:mergeinfo value"));
779251881Speter        }
780251881Speter
781299742Sdim      value = new_value;
782251881Speter    }
783251881Speter
784251881Speter  SVN_ERR(svn_rdump__normalize_prop(name, &value, pool));
785251881Speter
786251881Speter  SVN_ERR(svn_repos__validate_prop(name, value, pool));
787251881Speter
788289166Speter  prop = apr_palloc(nb->rb->pool, sizeof (*prop));
789289166Speter  prop->name = apr_pstrdup(pool, name);
790299742Sdim  prop->value = svn_string_dup(value, pool);
791289166Speter  svn_hash_sets(nb->prop_changes, prop->name, prop);
792289166Speter
793251881Speter  return SVN_NO_ERROR;
794251881Speter}
795251881Speter
796251881Speterstatic svn_error_t *
797251881Speterdelete_node_property(void *baton,
798251881Speter                     const char *name)
799251881Speter{
800251881Speter  struct node_baton *nb = baton;
801251881Speter  apr_pool_t *pool = nb->rb->pool;
802289166Speter  svn_prop_t *prop;
803251881Speter
804251881Speter  SVN_ERR(svn_repos__validate_prop(name, NULL, pool));
805251881Speter
806289166Speter  prop = apr_palloc(pool, sizeof (*prop));
807289166Speter  prop->name = apr_pstrdup(pool, name);
808289166Speter  prop->value = NULL;
809289166Speter  svn_hash_sets(nb->prop_changes, prop->name, prop);
810251881Speter
811251881Speter  return SVN_NO_ERROR;
812251881Speter}
813251881Speter
814289166Speter/* Delete all the properties of the node, if any.
815289166Speter *
816289166Speter * The commit editor doesn't have a method to delete a node's properties
817289166Speter * without knowing what they are, so we have to first find out what
818289166Speter * properties the node would have had. If it's copied (explicitly or
819289166Speter * implicitly), we look at the copy source. If it's only being changed,
820289166Speter * we look at the node's current path in the head revision.
821289166Speter */
822251881Speterstatic svn_error_t *
823251881Speterremove_node_props(void *baton)
824251881Speter{
825251881Speter  struct node_baton *nb = baton;
826289166Speter  struct revision_baton *rb = nb->rb;
827251881Speter  apr_pool_t *pool = nb->rb->pool;
828251881Speter  apr_hash_index_t *hi;
829251881Speter  apr_hash_t *props;
830289166Speter  const char *orig_path;
831289166Speter  svn_revnum_t orig_rev;
832251881Speter
833289166Speter  /* Find the path and revision that has the node's original properties */
834289166Speter  if (ARE_VALID_COPY_ARGS(nb->copyfrom_path, nb->copyfrom_rev))
835289166Speter    {
836289166Speter      orig_path = nb->copyfrom_path;
837289166Speter      orig_rev = nb->copyfrom_rev;
838289166Speter    }
839289166Speter  else if (!nb->is_added
840289166Speter           && ARE_VALID_COPY_ARGS(rb->db->copyfrom_path, rb->db->copyfrom_rev))
841289166Speter    {
842289166Speter      /* If this is a dir, then it's described by rb->db;
843289166Speter         if this is a file, then it's a child of the dir in rb->db. */
844289166Speter      orig_path = (nb->kind == svn_node_dir)
845289166Speter                    ? rb->db->copyfrom_path
846289166Speter                    : svn_relpath_join(rb->db->copyfrom_path,
847289166Speter                                       svn_relpath_basename(nb->path, NULL),
848289166Speter                                       rb->pool);
849289166Speter      orig_rev = rb->db->copyfrom_rev;
850289166Speter    }
851289166Speter  else
852289166Speter    {
853289166Speter      /* ### Should we query at a known, fixed, "head" revision number
854289166Speter         instead of passing SVN_INVALID_REVNUM and getting a moving target? */
855289166Speter      orig_path = nb->path;
856289166Speter      orig_rev = SVN_INVALID_REVNUM;
857289166Speter    }
858289166Speter
859251881Speter  if ((nb->action == svn_node_action_add
860251881Speter            || nb->action == svn_node_action_replace)
861289166Speter      && ! ARE_VALID_COPY_ARGS(orig_path, orig_rev))
862251881Speter    /* Add-without-history; no "old" properties to worry about. */
863251881Speter    return SVN_NO_ERROR;
864251881Speter
865251881Speter  if (nb->kind == svn_node_file)
866251881Speter    {
867289166Speter      SVN_ERR(svn_ra_get_file(nb->rb->pb->aux_session,
868289166Speter                              orig_path, orig_rev, NULL, NULL, &props, pool));
869251881Speter    }
870251881Speter  else  /* nb->kind == svn_node_dir */
871251881Speter    {
872251881Speter      SVN_ERR(svn_ra_get_dir2(nb->rb->pb->aux_session, NULL, NULL, &props,
873289166Speter                              orig_path, orig_rev, 0, pool));
874251881Speter    }
875251881Speter
876251881Speter  for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
877251881Speter    {
878299742Sdim      const char *name = apr_hash_this_key(hi);
879251881Speter      svn_prop_kind_t kind = svn_property_kind2(name);
880251881Speter
881251881Speter      if (kind == svn_prop_regular_kind)
882251881Speter        SVN_ERR(set_node_property(nb, name, NULL));
883251881Speter    }
884251881Speter
885251881Speter  return SVN_NO_ERROR;
886251881Speter}
887251881Speter
888251881Speterstatic svn_error_t *
889251881Speterset_fulltext(svn_stream_t **stream,
890251881Speter             void *node_baton)
891251881Speter{
892251881Speter  struct node_baton *nb = node_baton;
893251881Speter  const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor;
894251881Speter  svn_txdelta_window_handler_t handler;
895251881Speter  void *handler_baton;
896251881Speter  apr_pool_t *pool = nb->rb->pool;
897251881Speter
898251881Speter  SVN_ERR(commit_editor->apply_textdelta(nb->file_baton, nb->base_checksum,
899251881Speter                                         pool, &handler, &handler_baton));
900251881Speter  *stream = svn_txdelta_target_push(handler, handler_baton,
901251881Speter                                    svn_stream_empty(pool), pool);
902251881Speter  return SVN_NO_ERROR;
903251881Speter}
904251881Speter
905251881Speterstatic svn_error_t *
906251881Speterapply_textdelta(svn_txdelta_window_handler_t *handler,
907251881Speter                void **handler_baton,
908251881Speter                void *node_baton)
909251881Speter{
910251881Speter  struct node_baton *nb = node_baton;
911251881Speter  const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor;
912251881Speter  apr_pool_t *pool = nb->rb->pool;
913251881Speter
914251881Speter  SVN_ERR(commit_editor->apply_textdelta(nb->file_baton, nb->base_checksum,
915251881Speter                                         pool, handler, handler_baton));
916251881Speter
917251881Speter  return SVN_NO_ERROR;
918251881Speter}
919251881Speter
920251881Speterstatic svn_error_t *
921251881Speterclose_node(void *baton)
922251881Speter{
923251881Speter  struct node_baton *nb = baton;
924251881Speter  const struct svn_delta_editor_t *commit_editor = nb->rb->pb->commit_editor;
925289166Speter  apr_pool_t *pool = nb->rb->pool;
926289166Speter  apr_hash_index_t *hi;
927251881Speter
928289166Speter  for (hi = apr_hash_first(pool, nb->prop_changes);
929289166Speter       hi; hi = apr_hash_next(hi))
930289166Speter    {
931299742Sdim      const char *name = apr_hash_this_key(hi);
932299742Sdim      svn_prop_t *prop = apr_hash_this_val(hi);
933289166Speter
934289166Speter      switch (nb->kind)
935289166Speter        {
936289166Speter        case svn_node_file:
937289166Speter          SVN_ERR(commit_editor->change_file_prop(nb->file_baton,
938289166Speter                                                  name, prop->value, pool));
939289166Speter          break;
940289166Speter        case svn_node_dir:
941289166Speter          SVN_ERR(commit_editor->change_dir_prop(nb->rb->db->baton,
942289166Speter                                                 name, prop->value, pool));
943289166Speter          break;
944289166Speter        default:
945289166Speter          break;
946289166Speter        }
947289166Speter    }
948289166Speter
949251881Speter  /* Pass a file node closure through to the editor *unless* we
950251881Speter     deleted the file (which doesn't require us to open it). */
951251881Speter  if ((nb->kind == svn_node_file) && (nb->file_baton))
952251881Speter    {
953251881Speter      SVN_ERR(commit_editor->close_file(nb->file_baton, NULL, nb->rb->pool));
954251881Speter    }
955251881Speter
956251881Speter  /* The svn_node_dir case is handled in close_revision */
957251881Speter
958251881Speter  return SVN_NO_ERROR;
959251881Speter}
960251881Speter
961251881Speterstatic svn_error_t *
962251881Speterclose_revision(void *baton)
963251881Speter{
964251881Speter  struct revision_baton *rb = baton;
965251881Speter  const svn_delta_editor_t *commit_editor = rb->pb->commit_editor;
966251881Speter  void *commit_edit_baton = rb->pb->commit_edit_baton;
967251881Speter  svn_revnum_t committed_rev = SVN_INVALID_REVNUM;
968251881Speter
969251881Speter  /* Fake revision 0 */
970251881Speter  if (rb->rev == 0)
971251881Speter    {
972251881Speter      /* ### Don't print directly; generate a notification. */
973251881Speter      if (! rb->pb->quiet)
974251881Speter        SVN_ERR(svn_cmdline_printf(rb->pool, "* Loaded revision 0.\n"));
975251881Speter    }
976251881Speter  else if (commit_editor)
977251881Speter    {
978251881Speter      /* Close all pending open directories, and then close the edit
979251881Speter         session itself */
980251881Speter      while (rb->db && rb->db->parent)
981251881Speter        {
982251881Speter          SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool));
983251881Speter          rb->db = rb->db->parent;
984251881Speter        }
985251881Speter      /* root dir's baton */
986251881Speter      SVN_ERR(commit_editor->close_directory(rb->db->baton, rb->pool));
987251881Speter      SVN_ERR(commit_editor->close_edit(commit_edit_baton, rb->pool));
988251881Speter    }
989251881Speter  else
990251881Speter    {
991251881Speter      void *child_baton;
992251881Speter
993251881Speter      /* Legitimate revision with no node information */
994251881Speter      SVN_ERR(svn_ra_get_commit_editor3(rb->pb->session, &commit_editor,
995251881Speter                                        &commit_edit_baton, rb->revprop_table,
996251881Speter                                        commit_callback, baton,
997251881Speter                                        NULL, FALSE, rb->pool));
998251881Speter
999251881Speter      SVN_ERR(commit_editor->open_root(commit_edit_baton,
1000251881Speter                                       rb->rev - rb->rev_offset - 1,
1001251881Speter                                       rb->pool, &child_baton));
1002251881Speter
1003251881Speter      SVN_ERR(commit_editor->close_directory(child_baton, rb->pool));
1004251881Speter      SVN_ERR(commit_editor->close_edit(commit_edit_baton, rb->pool));
1005251881Speter    }
1006251881Speter
1007251881Speter  /* svn_fs_commit_txn() rewrites the datestamp and author properties;
1008251881Speter     we'll rewrite them again by hand after closing the commit_editor.
1009251881Speter     The only time we don't do this is for revision 0 when loaded into
1010251881Speter     a non-empty repository.  */
1011251881Speter  if (rb->rev > 0)
1012251881Speter    {
1013251881Speter      committed_rev = get_revision_mapping(rb->pb->rev_map, rb->rev);
1014251881Speter    }
1015251881Speter  else if (rb->rev_offset == -1)
1016251881Speter    {
1017251881Speter      committed_rev = 0;
1018251881Speter    }
1019251881Speter
1020251881Speter  if (SVN_IS_VALID_REVNUM(committed_rev))
1021251881Speter    {
1022299742Sdim      if (!svn_hash_gets(rb->pb->skip_revprops, SVN_PROP_REVISION_DATE))
1023299742Sdim        {
1024299742Sdim          SVN_ERR(svn_repos__validate_prop(SVN_PROP_REVISION_DATE,
1025299742Sdim                                           rb->datestamp, rb->pool));
1026299742Sdim          SVN_ERR(svn_ra_change_rev_prop2(rb->pb->session, committed_rev,
1027299742Sdim                                          SVN_PROP_REVISION_DATE,
1028299742Sdim                                          NULL, rb->datestamp, rb->pool));
1029299742Sdim        }
1030299742Sdim      if (!svn_hash_gets(rb->pb->skip_revprops, SVN_PROP_REVISION_AUTHOR))
1031299742Sdim        {
1032299742Sdim          SVN_ERR(svn_repos__validate_prop(SVN_PROP_REVISION_AUTHOR,
1033299742Sdim                                           rb->author, rb->pool));
1034299742Sdim          SVN_ERR(svn_ra_change_rev_prop2(rb->pb->session, committed_rev,
1035299742Sdim                                          SVN_PROP_REVISION_AUTHOR,
1036299742Sdim                                          NULL, rb->author, rb->pool));
1037299742Sdim        }
1038251881Speter    }
1039251881Speter
1040251881Speter  svn_pool_destroy(rb->pool);
1041251881Speter
1042251881Speter  return SVN_NO_ERROR;
1043251881Speter}
1044251881Speter
1045251881Spetersvn_error_t *
1046251881Spetersvn_rdump__load_dumpstream(svn_stream_t *stream,
1047251881Speter                           svn_ra_session_t *session,
1048251881Speter                           svn_ra_session_t *aux_session,
1049251881Speter                           svn_boolean_t quiet,
1050299742Sdim                           apr_hash_t *skip_revprops,
1051251881Speter                           svn_cancel_func_t cancel_func,
1052251881Speter                           void *cancel_baton,
1053251881Speter                           apr_pool_t *pool)
1054251881Speter{
1055251881Speter  svn_repos_parse_fns3_t *parser;
1056251881Speter  struct parse_baton *parse_baton;
1057251881Speter  const svn_string_t *lock_string;
1058251881Speter  svn_boolean_t be_atomic;
1059251881Speter  svn_error_t *err;
1060251881Speter  const char *session_url, *root_url, *parent_dir;
1061251881Speter
1062251881Speter  SVN_ERR(svn_ra_has_capability(session, &be_atomic,
1063251881Speter                                SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
1064251881Speter                                pool));
1065251881Speter  SVN_ERR(get_lock(&lock_string, session, cancel_func, cancel_baton, pool));
1066251881Speter  SVN_ERR(svn_ra_get_repos_root2(session, &root_url, pool));
1067251881Speter  SVN_ERR(svn_ra_get_session_url(session, &session_url, pool));
1068251881Speter  SVN_ERR(svn_ra_get_path_relative_to_root(session, &parent_dir,
1069251881Speter                                           session_url, pool));
1070251881Speter
1071251881Speter  parser = apr_pcalloc(pool, sizeof(*parser));
1072251881Speter  parser->magic_header_record = magic_header_record;
1073251881Speter  parser->uuid_record = uuid_record;
1074251881Speter  parser->new_revision_record = new_revision_record;
1075251881Speter  parser->new_node_record = new_node_record;
1076251881Speter  parser->set_revision_property = set_revision_property;
1077251881Speter  parser->set_node_property = set_node_property;
1078251881Speter  parser->delete_node_property = delete_node_property;
1079251881Speter  parser->remove_node_props = remove_node_props;
1080251881Speter  parser->set_fulltext = set_fulltext;
1081251881Speter  parser->apply_textdelta = apply_textdelta;
1082251881Speter  parser->close_node = close_node;
1083251881Speter  parser->close_revision = close_revision;
1084251881Speter
1085251881Speter  parse_baton = apr_pcalloc(pool, sizeof(*parse_baton));
1086251881Speter  parse_baton->session = session;
1087251881Speter  parse_baton->aux_session = aux_session;
1088251881Speter  parse_baton->quiet = quiet;
1089251881Speter  parse_baton->root_url = root_url;
1090251881Speter  parse_baton->parent_dir = parent_dir;
1091251881Speter  parse_baton->rev_map = apr_hash_make(pool);
1092251881Speter  parse_baton->last_rev_mapped = SVN_INVALID_REVNUM;
1093251881Speter  parse_baton->oldest_dumpstream_rev = SVN_INVALID_REVNUM;
1094299742Sdim  parse_baton->skip_revprops = skip_revprops;
1095251881Speter
1096251881Speter  err = svn_repos_parse_dumpstream3(stream, parser, parse_baton, FALSE,
1097251881Speter                                    cancel_func, cancel_baton, pool);
1098251881Speter
1099251881Speter  /* If all goes well, or if we're cancelled cleanly, don't leave a
1100251881Speter     stray lock behind. */
1101251881Speter  if ((! err) || (err && (err->apr_err == SVN_ERR_CANCELLED)))
1102251881Speter    err = svn_error_compose_create(
1103251881Speter              svn_ra__release_operational_lock(session, SVNRDUMP_PROP_LOCK,
1104251881Speter                                               lock_string, pool),
1105251881Speter              err);
1106251881Speter  return err;
1107251881Speter}
1108