1251881Speter/*
2251881Speter * editor.c:  compatibility editors
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#include <apr_pools.h>
25251881Speter
26251881Speter#include "svn_error.h"
27251881Speter#include "svn_pools.h"
28251881Speter#include "svn_ra.h"
29251881Speter#include "svn_delta.h"
30251881Speter#include "svn_dirent_uri.h"
31251881Speter
32251881Speter#include "private/svn_ra_private.h"
33251881Speter#include "private/svn_delta_private.h"
34251881Speter#include "private/svn_editor.h"
35251881Speter
36251881Speter#include "ra_loader.h"
37251881Speter#include "svn_private_config.h"
38251881Speter
39251881Speter
40251881Speterstruct fp_baton {
41251881Speter  svn_ra__provide_props_cb_t provide_props_cb;
42251881Speter  void *cb_baton;
43251881Speter};
44251881Speter
45251881Speterstruct fb_baton {
46251881Speter  svn_ra__provide_base_cb_t provide_base_cb;
47251881Speter  void *cb_baton;
48251881Speter};
49251881Speter
50251881Speter/* The shims currently want a callback that provides props for a given
51251881Speter   REPOS_RELPATH at a given BASE_REVISION. However, the RA Ev2 interface
52251881Speter   has a callback that provides properties for the REPOS_RELPATH from any
53251881Speter   revision, which is returned along with the properties.
54251881Speter
55251881Speter   This is a little shim to map between the prototypes. The base revision
56251881Speter   for the properties is discarded, and the requested revision (from the
57251881Speter   shim code) is ignored.
58251881Speter
59251881Speter   The shim code needs to be updated to allow for an RA-style callback
60251881Speter   to fetch properties.  */
61251881Speterstatic svn_error_t *
62251881Speterfetch_props(apr_hash_t **props,
63251881Speter            void *baton,
64251881Speter            const char *repos_relpath,
65251881Speter            svn_revnum_t base_revision,
66251881Speter            apr_pool_t *result_pool,
67251881Speter            apr_pool_t *scratch_pool)
68251881Speter{
69251881Speter  struct fp_baton *fpb = baton;
70251881Speter  svn_revnum_t unused_revision;
71251881Speter
72251881Speter  /* Ignored: BASE_REVISION.  */
73251881Speter
74251881Speter  return svn_error_trace(fpb->provide_props_cb(props, &unused_revision,
75251881Speter                                               fpb->cb_baton,
76251881Speter                                               repos_relpath,
77251881Speter                                               result_pool, scratch_pool));
78251881Speter}
79251881Speter
80251881Speter/* See note above regarding BASE_REVISION.
81251881Speter   This also pulls down the entire contents of the file stream from the
82251881Speter   RA layer and stores them in a local file, returning the path.
83251881Speter*/
84251881Speterstatic svn_error_t *
85251881Speterfetch_base(const char **filename,
86251881Speter           void *baton,
87251881Speter           const char *repos_relpath,
88251881Speter           svn_revnum_t base_revision,
89251881Speter           apr_pool_t *result_pool,
90251881Speter           apr_pool_t *scratch_pool)
91251881Speter{
92251881Speter  struct fb_baton *fbb = baton;
93251881Speter  svn_revnum_t unused_revision;
94251881Speter  svn_stream_t *contents;
95251881Speter  svn_stream_t *file_stream;
96251881Speter  const char *tmp_filename;
97251881Speter
98251881Speter  /* Ignored: BASE_REVISION.  */
99251881Speter
100251881Speter  SVN_ERR(fbb->provide_base_cb(&contents, &unused_revision, fbb->cb_baton,
101251881Speter                               repos_relpath, result_pool, scratch_pool));
102251881Speter
103251881Speter  SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL,
104251881Speter                                 svn_io_file_del_on_pool_cleanup,
105251881Speter                                 scratch_pool, scratch_pool));
106251881Speter  SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool));
107251881Speter
108251881Speter  *filename = apr_pstrdup(result_pool, tmp_filename);
109251881Speter
110251881Speter
111251881Speter
112251881Speter  return SVN_NO_ERROR;
113251881Speter}
114251881Speter
115251881Speter
116251881Speter
117251881Spetersvn_error_t *
118251881Spetersvn_ra__use_commit_shim(svn_editor_t **editor,
119251881Speter                        svn_ra_session_t *session,
120251881Speter                        apr_hash_t *revprop_table,
121251881Speter                        svn_commit_callback2_t commit_callback,
122251881Speter                        void *commit_baton,
123251881Speter                        apr_hash_t *lock_tokens,
124251881Speter                        svn_boolean_t keep_locks,
125251881Speter                        svn_ra__provide_base_cb_t provide_base_cb,
126251881Speter                        svn_ra__provide_props_cb_t provide_props_cb,
127251881Speter                        svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb,
128251881Speter                        void *cb_baton,
129251881Speter                        svn_cancel_func_t cancel_func,
130251881Speter                        void *cancel_baton,
131251881Speter                        apr_pool_t *result_pool,
132251881Speter                        apr_pool_t *scratch_pool)
133251881Speter{
134251881Speter  const svn_delta_editor_t *deditor;
135251881Speter  void *dedit_baton;
136251881Speter  struct svn_delta__extra_baton *exb;
137251881Speter  svn_delta__unlock_func_t unlock_func;
138251881Speter  void *unlock_baton;
139251881Speter  const char *repos_root;
140251881Speter  const char *session_url;
141251881Speter  const char *base_relpath;
142251881Speter  svn_boolean_t *found_abs_paths;
143251881Speter  struct fp_baton *fpb;
144251881Speter
145251881Speter  /* NOTE: PROVIDE_BASE_CB is currently unused by this shim. In the future,
146251881Speter     we can pass it to the underlying Ev2/Ev1 shim to produce better
147251881Speter     apply_txdelta drives (ie. against a base rather than <empty>).  */
148251881Speter
149251881Speter  /* Fetch the RA provider's Ev1 commit editor.  */
150251881Speter  SVN_ERR(session->vtable->get_commit_editor(session, &deditor, &dedit_baton,
151251881Speter                                             revprop_table,
152251881Speter                                             commit_callback, commit_baton,
153251881Speter                                             lock_tokens, keep_locks,
154251881Speter                                             result_pool));
155251881Speter
156251881Speter  /* Get or calculate the appropriate repos root and base relpath. */
157251881Speter  SVN_ERR(svn_ra_get_repos_root2(session, &repos_root, scratch_pool));
158251881Speter  SVN_ERR(svn_ra_get_session_url(session, &session_url, scratch_pool));
159251881Speter  base_relpath = svn_uri_skip_ancestor(repos_root, session_url, scratch_pool);
160251881Speter
161251881Speter  /* We will assume that when the underlying Ev1 editor is finally driven
162251881Speter     by the shim, that we will not need to prepend "/" to the paths. Place
163251881Speter     this on the heap because it is examined much later. Set to FALSE.  */
164251881Speter  found_abs_paths = apr_pcalloc(result_pool, sizeof(*found_abs_paths));
165251881Speter
166251881Speter  /* The PROVIDE_PROPS_CB callback does not match what the shims want.
167251881Speter     Let's jigger things around a little bit here.  */
168251881Speter  fpb = apr_palloc(result_pool, sizeof(*fpb));
169251881Speter  fpb->provide_props_cb = provide_props_cb;
170251881Speter  fpb->cb_baton = cb_baton;
171251881Speter
172251881Speter  /* Create the Ev2 editor from the Ev1 editor provided by the RA layer.
173251881Speter
174251881Speter     Note: GET_COPYSRC_KIND_CB is compatible in type/semantics with the
175251881Speter     shim's FETCH_KIND_FUNC parameter.  */
176251881Speter  SVN_ERR(svn_delta__editor_from_delta(editor, &exb,
177251881Speter                                       &unlock_func, &unlock_baton,
178251881Speter                                       deditor, dedit_baton,
179251881Speter                                       found_abs_paths,
180251881Speter                                       repos_root, base_relpath,
181251881Speter                                       cancel_func, cancel_baton,
182251881Speter                                       get_copysrc_kind_cb, cb_baton,
183251881Speter                                       fetch_props, fpb,
184251881Speter                                       result_pool, scratch_pool));
185251881Speter
186251881Speter  /* Note: UNLOCK_FUNC and UNLOCK_BATON are unused during commit drives.
187251881Speter     We can safely drop them on the floor.  */
188251881Speter
189251881Speter  /* Since we're (currently) just wrapping an existing Ev1 editor, we have
190251881Speter     to call any start_edit handler it may provide (the shim uses this to
191251881Speter     invoke Ev1's open_root callback).  We've got a couple of options to do
192251881Speter     so: Implement a wrapper editor and call the start_edit callback upon
193251881Speter     the first invocation of any of the underlying editor's functions; or,
194251881Speter     just assume our consumer is going to eventually use the editor it is
195251881Speter     asking for, and call the start edit callback now.  For simplicity's
196251881Speter     sake, we do the latter.  */
197251881Speter  if (exb->start_edit)
198251881Speter    {
199251881Speter      /* Most commit drives pass SVN_INVALID_REVNUM for the revision.
200251881Speter         All calls to svn_delta_path_driver() pass SVN_INVALID_REVNUM,
201251881Speter         so this is fine for any commits done via that function.
202251881Speter
203251881Speter         Notably, the PROPSET command passes a specific revision. Before
204251881Speter         PROPSET can use the RA Ev2 interface, we may need to make this
205251881Speter         revision a parameter.
206251881Speter         ### what are the exact semantics? what is the meaning of the
207251881Speter         ### revision passed to the Ev1->open_root() callback?  */
208251881Speter      SVN_ERR(exb->start_edit(exb->baton, SVN_INVALID_REVNUM));
209251881Speter    }
210251881Speter
211251881Speter  /* Note: EXB also contains a TARGET_REVISION function, but that is not
212251881Speter     used during commit operations. We can safely ignore it. (ie. it is
213251881Speter     in EXB for use by paired-shims)  */
214251881Speter
215251881Speter  return SVN_NO_ERROR;
216251881Speter}
217251881Speter
218251881Speter
219251881Speterstruct wrapped_replay_baton_t {
220251881Speter  svn_ra__replay_revstart_ev2_callback_t revstart_func;
221251881Speter  svn_ra__replay_revfinish_ev2_callback_t revfinish_func;
222251881Speter  void *replay_baton;
223251881Speter
224251881Speter  svn_ra_session_t *session;
225251881Speter
226251881Speter  svn_ra__provide_base_cb_t provide_base_cb;
227251881Speter  svn_ra__provide_props_cb_t provide_props_cb;
228251881Speter  void *cb_baton;
229251881Speter
230251881Speter  /* This will be populated by the revstart wrapper. */
231251881Speter  svn_editor_t *editor;
232251881Speter};
233251881Speter
234251881Speterstatic svn_error_t *
235251881Speterrevstart_func_wrapper(svn_revnum_t revision,
236251881Speter                      void *replay_baton,
237251881Speter                      const svn_delta_editor_t **deditor,
238251881Speter                      void **dedit_baton,
239251881Speter                      apr_hash_t *rev_props,
240251881Speter                      apr_pool_t *result_pool)
241251881Speter{
242251881Speter  struct wrapped_replay_baton_t *wrb = replay_baton;
243251881Speter  const char *repos_root;
244251881Speter  const char *session_url;
245251881Speter  const char *base_relpath;
246251881Speter  svn_boolean_t *found_abs_paths;
247251881Speter  struct fp_baton *fpb;
248251881Speter  struct svn_delta__extra_baton *exb;
249251881Speter
250251881Speter  /* Get the Ev2 editor from the original revstart func. */
251251881Speter  SVN_ERR(wrb->revstart_func(revision, wrb->replay_baton, &wrb->editor,
252251881Speter                             rev_props, result_pool));
253251881Speter
254251881Speter  /* Get or calculate the appropriate repos root and base relpath. */
255251881Speter  SVN_ERR(svn_ra_get_repos_root2(wrb->session, &repos_root, result_pool));
256251881Speter  SVN_ERR(svn_ra_get_session_url(wrb->session, &session_url, result_pool));
257251881Speter  base_relpath = svn_uri_skip_ancestor(repos_root, session_url, result_pool);
258251881Speter
259251881Speter  /* We will assume that when the underlying Ev1 editor is finally driven
260251881Speter     by the shim, that we will not need to prepend "/" to the paths. Place
261251881Speter     this on the heap because it is examined much later. Set to FALSE.  */
262251881Speter  found_abs_paths = apr_pcalloc(result_pool, sizeof(*found_abs_paths));
263251881Speter
264251881Speter  /* The PROVIDE_PROPS_CB callback does not match what the shims want.
265251881Speter     Let's jigger things around a little bit here.  */
266251881Speter  fpb = apr_palloc(result_pool, sizeof(*fpb));
267251881Speter  fpb->provide_props_cb = wrb->provide_props_cb;
268251881Speter  fpb->cb_baton = wrb->cb_baton;
269251881Speter
270251881Speter  /* Create the extra baton. */
271251881Speter  exb = apr_pcalloc(result_pool, sizeof(*exb));
272251881Speter
273251881Speter  /* Create the Ev1 editor from the Ev2 editor provided by the RA layer.
274251881Speter
275251881Speter     Note: GET_COPYSRC_KIND_CB is compatible in type/semantics with the
276251881Speter     shim's FETCH_KIND_FUNC parameter.  */
277251881Speter  SVN_ERR(svn_delta__delta_from_editor(deditor, dedit_baton, wrb->editor,
278251881Speter                                       NULL, NULL,
279251881Speter                                       found_abs_paths,
280251881Speter                                       repos_root, base_relpath,
281251881Speter                                       fetch_props, wrb->cb_baton,
282251881Speter                                       fetch_base, wrb->cb_baton,
283251881Speter                                       exb, result_pool));
284251881Speter
285251881Speter  return SVN_NO_ERROR;
286251881Speter}
287251881Speter
288251881Speterstatic svn_error_t *
289251881Speterrevfinish_func_wrapper(svn_revnum_t revision,
290251881Speter                       void *replay_baton,
291251881Speter                       const svn_delta_editor_t *editor,
292251881Speter                       void *edit_baton,
293251881Speter                       apr_hash_t *rev_props,
294251881Speter                       apr_pool_t *pool)
295251881Speter{
296251881Speter  struct wrapped_replay_baton_t *wrb = replay_baton;
297251881Speter
298251881Speter  SVN_ERR(wrb->revfinish_func(revision, replay_baton, wrb->editor, rev_props,
299251881Speter                              pool));
300251881Speter
301251881Speter  return SVN_NO_ERROR;
302251881Speter}
303251881Speter
304251881Spetersvn_error_t *
305251881Spetersvn_ra__use_replay_range_shim(svn_ra_session_t *session,
306251881Speter                              svn_revnum_t start_revision,
307251881Speter                              svn_revnum_t end_revision,
308251881Speter                              svn_revnum_t low_water_mark,
309251881Speter                              svn_boolean_t send_deltas,
310251881Speter                              svn_ra__replay_revstart_ev2_callback_t revstart_func,
311251881Speter                              svn_ra__replay_revfinish_ev2_callback_t revfinish_func,
312251881Speter                              void *replay_baton,
313251881Speter                              svn_ra__provide_base_cb_t provide_base_cb,
314251881Speter                              svn_ra__provide_props_cb_t provide_props_cb,
315251881Speter                              void *cb_baton,
316251881Speter                              apr_pool_t *scratch_pool)
317251881Speter{
318251881Speter  /* The basic strategy here is to wrap the callback start and finish
319251881Speter     functions to appropriately return an Ev1 editor which is itself wrapped
320251881Speter     from the Ev2 one the provided callbacks will give us. */
321251881Speter
322251881Speter  struct wrapped_replay_baton_t *wrb = apr_pcalloc(scratch_pool, sizeof(*wrb));
323251881Speter
324251881Speter  wrb->revstart_func = revstart_func;
325251881Speter  wrb->revfinish_func = revfinish_func;
326251881Speter  wrb->replay_baton = replay_baton;
327251881Speter  wrb->session = session;
328251881Speter
329251881Speter  wrb->provide_base_cb = provide_base_cb;
330251881Speter  wrb->provide_props_cb = provide_props_cb;
331251881Speter  wrb->cb_baton = cb_baton;
332251881Speter
333251881Speter  return svn_error_trace(svn_ra_replay_range(session, start_revision,
334251881Speter                                             end_revision, low_water_mark,
335251881Speter                                             send_deltas,
336251881Speter                                             revstart_func_wrapper,
337251881Speter                                             revfinish_func_wrapper,
338251881Speter                                             wrb, scratch_pool));
339251881Speter}
340