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