1251881Speter/* 2251881Speter * editor.c: Editor for modifying FS transactions 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_types.h" 27251881Speter#include "svn_error.h" 28251881Speter#include "svn_pools.h" 29251881Speter#include "svn_fs.h" 30251881Speter#include "svn_props.h" 31251881Speter#include "svn_path.h" 32251881Speter 33251881Speter#include "svn_private_config.h" 34251881Speter 35251881Speter#include "fs-loader.h" 36251881Speter 37251881Speter#include "private/svn_fspath.h" 38251881Speter#include "private/svn_fs_private.h" 39251881Speter#include "private/svn_editor.h" 40251881Speter 41251881Speter 42251881Speterstruct edit_baton { 43251881Speter /* The transaction associated with this editor. */ 44251881Speter svn_fs_txn_t *txn; 45251881Speter 46251881Speter /* Has this editor been completed? */ 47251881Speter svn_boolean_t completed; 48251881Speter 49251881Speter /* We sometimes need the cancellation beyond what svn_editor_t provides */ 50251881Speter svn_cancel_func_t cancel_func; 51251881Speter void *cancel_baton; 52251881Speter 53251881Speter /* The pool that the txn lives within. When we create a ROOT, it will 54251881Speter be allocated within a subpool of this. The root will be closed in 55251881Speter complete/abort and that subpool will be destroyed. 56251881Speter 57251881Speter This pool SHOULD NOT be used for any allocations. */ 58251881Speter apr_pool_t *txn_pool; 59251881Speter 60251881Speter /* This is the root from the txn. Use get_root() to fetch/create this 61251881Speter member as appropriate. */ 62251881Speter svn_fs_root_t *root; 63251881Speter}; 64251881Speter 65251881Speter#define FSPATH(relpath, pool) apr_pstrcat(pool, "/", relpath, NULL) 66251881Speter#define UNUSED(x) ((void)(x)) 67251881Speter 68251881Speter 69251881Speterstatic svn_error_t * 70251881Speterget_root(svn_fs_root_t **root, 71251881Speter struct edit_baton *eb) 72251881Speter{ 73251881Speter if (eb->root == NULL) 74251881Speter SVN_ERR(svn_fs_txn_root(&eb->root, eb->txn, eb->txn_pool)); 75251881Speter *root = eb->root; 76251881Speter return SVN_NO_ERROR; 77251881Speter} 78251881Speter 79251881Speter 80251881Speter/* Apply each property in PROPS to the node at FSPATH in ROOT. */ 81251881Speterstatic svn_error_t * 82251881Speteradd_new_props(svn_fs_root_t *root, 83251881Speter const char *fspath, 84251881Speter apr_hash_t *props, 85251881Speter apr_pool_t *scratch_pool) 86251881Speter{ 87251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 88251881Speter apr_hash_index_t *hi; 89251881Speter 90251881Speter /* ### it would be nice to have svn_fs_set_node_props(). but since we 91251881Speter ### don't... add each property to the node. this is a new node, so 92251881Speter ### we don't need to worry about deleting props. just adding. */ 93251881Speter 94251881Speter for (hi = apr_hash_first(scratch_pool, props); hi; 95251881Speter hi = apr_hash_next(hi)) 96251881Speter { 97251881Speter const char *name = svn__apr_hash_index_key(hi); 98251881Speter const svn_string_t *value = svn__apr_hash_index_val(hi); 99251881Speter 100251881Speter svn_pool_clear(iterpool); 101251881Speter 102251881Speter SVN_ERR(svn_fs_change_node_prop(root, fspath, name, value, iterpool)); 103251881Speter } 104251881Speter 105251881Speter svn_pool_destroy(iterpool); 106251881Speter return SVN_NO_ERROR; 107251881Speter} 108251881Speter 109251881Speter 110251881Speterstatic svn_error_t * 111251881Speteralter_props(svn_fs_root_t *root, 112251881Speter const char *fspath, 113251881Speter apr_hash_t *props, 114251881Speter apr_pool_t *scratch_pool) 115251881Speter{ 116251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 117251881Speter apr_hash_t *old_props; 118251881Speter apr_array_header_t *propdiffs; 119251881Speter int i; 120251881Speter 121251881Speter SVN_ERR(svn_fs_node_proplist(&old_props, root, fspath, scratch_pool)); 122251881Speter 123251881Speter SVN_ERR(svn_prop_diffs(&propdiffs, props, old_props, scratch_pool)); 124251881Speter 125251881Speter for (i = 0; i < propdiffs->nelts; ++i) 126251881Speter { 127251881Speter const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t); 128251881Speter 129251881Speter svn_pool_clear(iterpool); 130251881Speter 131251881Speter /* Add, change, or delete properties. */ 132251881Speter SVN_ERR(svn_fs_change_node_prop(root, fspath, prop->name, prop->value, 133251881Speter iterpool)); 134251881Speter } 135251881Speter 136251881Speter svn_pool_destroy(iterpool); 137251881Speter return SVN_NO_ERROR; 138251881Speter} 139251881Speter 140251881Speter 141251881Speterstatic svn_error_t * 142251881Speterset_text(svn_fs_root_t *root, 143251881Speter const char *fspath, 144251881Speter const svn_checksum_t *checksum, 145251881Speter svn_stream_t *contents, 146251881Speter svn_cancel_func_t cancel_func, 147251881Speter void *cancel_baton, 148251881Speter apr_pool_t *scratch_pool) 149251881Speter{ 150251881Speter svn_stream_t *fs_contents; 151251881Speter 152251881Speter /* ### We probably don't have an MD5 checksum, so no digest is available 153251881Speter ### for svn_fs_apply_text() to validate. It would be nice to have an 154251881Speter ### FS API that takes our CHECKSUM/CONTENTS pair (and PROPS!). */ 155251881Speter SVN_ERR(svn_fs_apply_text(&fs_contents, root, fspath, 156251881Speter NULL /* result_checksum */, 157251881Speter scratch_pool)); 158251881Speter SVN_ERR(svn_stream_copy3(contents, fs_contents, 159251881Speter cancel_func, cancel_baton, 160251881Speter scratch_pool)); 161251881Speter 162251881Speter return SVN_NO_ERROR; 163251881Speter} 164251881Speter 165251881Speter 166251881Speter/* The caller wants to modify REVISION of FSPATH. Is that allowed? */ 167251881Speterstatic svn_error_t * 168251881Spetercan_modify(svn_fs_root_t *txn_root, 169251881Speter const char *fspath, 170251881Speter svn_revnum_t revision, 171251881Speter apr_pool_t *scratch_pool) 172251881Speter{ 173251881Speter svn_revnum_t created_rev; 174251881Speter 175251881Speter /* Out-of-dateness check: compare the created-rev of the node 176251881Speter in the txn against the created-rev of FSPATH. */ 177251881Speter SVN_ERR(svn_fs_node_created_rev(&created_rev, txn_root, fspath, 178251881Speter scratch_pool)); 179251881Speter 180251881Speter /* Uncommitted nodes (eg. a descendent of a copy/move/rotate destination) 181251881Speter have no (committed) revision number. Let the caller go ahead and 182251881Speter modify these nodes. 183251881Speter 184251881Speter Note: strictly speaking, they might be performing an "illegal" edit 185251881Speter in certain cases, but let's just assume they're Good Little Boys. 186251881Speter 187251881Speter If CREATED_REV is invalid, that means it's already mutable in the 188251881Speter txn, which means it has already passed this out-of-dateness check. 189251881Speter (Usually, this happens when looking at a parent directory of an 190251881Speter already-modified node) */ 191251881Speter if (!SVN_IS_VALID_REVNUM(created_rev)) 192251881Speter return SVN_NO_ERROR; 193251881Speter 194251881Speter /* If the node is immutable (has a revision), then the caller should 195251881Speter have supplied a valid revision number [that they expect to change]. 196251881Speter The checks further below will determine the out-of-dateness of the 197251881Speter specified revision. */ 198251881Speter /* ### ugh. descendents of copy/move/rotate destinations carry along 199251881Speter ### their original immutable state and (thus) a valid CREATED_REV. 200251881Speter ### but they are logically uncommitted, so the caller will pass 201251881Speter ### SVN_INVALID_REVNUM. (technically, the caller could provide 202251881Speter ### ORIGINAL_REV, but that is semantically incorrect for the Ev2 203251881Speter ### API). 204251881Speter ### 205251881Speter ### for now, we will assume the caller knows what they are doing 206251881Speter ### and an invalid revision implies such a descendent. in the 207251881Speter ### future, we could examine the ancestor chain looking for a 208251881Speter ### copy/move/rotate-here node and allow the modification (and the 209251881Speter ### converse: if no such ancestor, the caller must specify the 210251881Speter ### correct/intended revision to modify). 211251881Speter */ 212251881Speter#if 1 213251881Speter if (!SVN_IS_VALID_REVNUM(revision)) 214251881Speter return SVN_NO_ERROR; 215251881Speter#else 216251881Speter if (!SVN_IS_VALID_REVNUM(revision)) 217251881Speter /* ### use a custom error code? */ 218251881Speter return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, 219251881Speter _("Revision for modifying '%s' is required"), 220251881Speter fspath); 221251881Speter#endif 222251881Speter 223251881Speter if (revision < created_rev) 224251881Speter { 225251881Speter /* We asked to change a node that is *older* than what we found 226251881Speter in the transaction. The client is out of date. */ 227251881Speter return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL, 228251881Speter _("'%s' is out of date; try updating"), 229251881Speter fspath); 230251881Speter } 231251881Speter 232251881Speter if (revision > created_rev) 233251881Speter { 234251881Speter /* We asked to change a node that is *newer* than what we found 235251881Speter in the transaction. Given that the transaction was based off 236251881Speter of 'youngest', then either: 237251881Speter - the caller asked to modify a future node 238251881Speter - the caller has committed more revisions since this txn 239251881Speter was constructed, and is asking to modify a node in one 240251881Speter of those new revisions. 241251881Speter In either case, the node may not have changed in those new 242251881Speter revisions; use the node's ID to determine this case. */ 243251881Speter const svn_fs_id_t *txn_noderev_id; 244251881Speter svn_fs_root_t *rev_root; 245251881Speter const svn_fs_id_t *new_noderev_id; 246251881Speter 247251881Speter /* The ID of the node that we would be modifying in the txn */ 248251881Speter SVN_ERR(svn_fs_node_id(&txn_noderev_id, txn_root, fspath, 249251881Speter scratch_pool)); 250251881Speter 251251881Speter /* Get the ID from the future/new revision. */ 252251881Speter SVN_ERR(svn_fs_revision_root(&rev_root, svn_fs_root_fs(txn_root), 253251881Speter revision, scratch_pool)); 254251881Speter SVN_ERR(svn_fs_node_id(&new_noderev_id, rev_root, fspath, 255251881Speter scratch_pool)); 256251881Speter svn_fs_close_root(rev_root); 257251881Speter 258251881Speter /* Has the target node changed in the future? */ 259251881Speter if (svn_fs_compare_ids(txn_noderev_id, new_noderev_id) != 0) 260251881Speter { 261251881Speter /* Restarting the commit will base the txn on the future/new 262251881Speter revision, allowing the modification at REVISION. */ 263251881Speter /* ### use a custom error code */ 264251881Speter return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL, 265251881Speter _("'%s' has been modified since the " 266251881Speter "commit began (restart the commit)"), 267251881Speter fspath); 268251881Speter } 269251881Speter } 270251881Speter 271251881Speter return SVN_NO_ERROR; 272251881Speter} 273251881Speter 274251881Speter 275251881Speter/* Can we create a node at FSPATH in TXN_ROOT? If something already exists 276251881Speter at that path, then the client MAY be out of date. We then have to see if 277251881Speter the path was created/modified in this transaction. IOW, it is new and 278251881Speter can be replaced without problem. 279251881Speter 280251881Speter Note: the editor protocol disallows double-modifications. This is to 281251881Speter ensure somebody does not accidentally overwrite another file due to 282251881Speter being out-of-date. */ 283251881Speterstatic svn_error_t * 284251881Spetercan_create(svn_fs_root_t *txn_root, 285251881Speter const char *fspath, 286251881Speter apr_pool_t *scratch_pool) 287251881Speter{ 288251881Speter svn_node_kind_t kind; 289251881Speter const char *cur_fspath; 290251881Speter 291251881Speter SVN_ERR(svn_fs_check_path(&kind, txn_root, fspath, scratch_pool)); 292251881Speter if (kind == svn_node_none) 293251881Speter return SVN_NO_ERROR; 294251881Speter 295251881Speter /* ### I'm not sure if this works perfectly. We might have an ancestor 296251881Speter ### that was modified as a result of a change on a cousin. We might 297251881Speter ### misinterpret that as a *-here node which brought along this 298251881Speter ### child. Need to write a test to verify. We may also be able to 299251881Speter ### test the ancestor to determine if it has been *-here in this 300251881Speter ### txn, or just a simple modification. */ 301251881Speter 302251881Speter /* Are any of the parents copied/moved/rotated-here? */ 303251881Speter for (cur_fspath = fspath; 304251881Speter strlen(cur_fspath) > 1; /* not the root */ 305251881Speter cur_fspath = svn_fspath__dirname(cur_fspath, scratch_pool)) 306251881Speter { 307251881Speter svn_revnum_t created_rev; 308251881Speter 309251881Speter SVN_ERR(svn_fs_node_created_rev(&created_rev, txn_root, cur_fspath, 310251881Speter scratch_pool)); 311251881Speter if (!SVN_IS_VALID_REVNUM(created_rev)) 312251881Speter { 313251881Speter /* The node has no created revision, meaning it is uncommitted. 314251881Speter Thus, it was created in this transaction, or it has already 315251881Speter been modified in some way (implying it has already passed a 316251881Speter modification check. */ 317251881Speter /* ### verify the node has been *-here ?? */ 318251881Speter return SVN_NO_ERROR; 319251881Speter } 320251881Speter } 321251881Speter 322251881Speter return svn_error_createf(SVN_ERR_FS_OUT_OF_DATE, NULL, 323251881Speter _("'%s' already exists, so may be out" 324251881Speter " of date; try updating"), 325251881Speter fspath); 326251881Speter} 327251881Speter 328251881Speter 329251881Speter/* This implements svn_editor_cb_add_directory_t */ 330251881Speterstatic svn_error_t * 331251881Speteradd_directory_cb(void *baton, 332251881Speter const char *relpath, 333251881Speter const apr_array_header_t *children, 334251881Speter apr_hash_t *props, 335251881Speter svn_revnum_t replaces_rev, 336251881Speter apr_pool_t *scratch_pool) 337251881Speter{ 338251881Speter struct edit_baton *eb = baton; 339251881Speter const char *fspath = FSPATH(relpath, scratch_pool); 340251881Speter svn_fs_root_t *root; 341251881Speter 342251881Speter /* Note: we ignore CHILDREN. We have no "incomplete" state to worry about, 343251881Speter so we don't need to be aware of what children will be created. */ 344251881Speter 345251881Speter SVN_ERR(get_root(&root, eb)); 346251881Speter 347251881Speter if (SVN_IS_VALID_REVNUM(replaces_rev)) 348251881Speter { 349251881Speter SVN_ERR(can_modify(root, fspath, replaces_rev, scratch_pool)); 350251881Speter SVN_ERR(svn_fs_delete(root, fspath, scratch_pool)); 351251881Speter } 352251881Speter else 353251881Speter { 354251881Speter SVN_ERR(can_create(root, fspath, scratch_pool)); 355251881Speter } 356251881Speter 357251881Speter SVN_ERR(svn_fs_make_dir(root, fspath, scratch_pool)); 358251881Speter SVN_ERR(add_new_props(root, fspath, props, scratch_pool)); 359251881Speter 360251881Speter return SVN_NO_ERROR; 361251881Speter} 362251881Speter 363251881Speter 364251881Speter/* This implements svn_editor_cb_add_file_t */ 365251881Speterstatic svn_error_t * 366251881Speteradd_file_cb(void *baton, 367251881Speter const char *relpath, 368251881Speter const svn_checksum_t *checksum, 369251881Speter svn_stream_t *contents, 370251881Speter apr_hash_t *props, 371251881Speter svn_revnum_t replaces_rev, 372251881Speter apr_pool_t *scratch_pool) 373251881Speter{ 374251881Speter struct edit_baton *eb = baton; 375251881Speter const char *fspath = FSPATH(relpath, scratch_pool); 376251881Speter svn_fs_root_t *root; 377251881Speter 378251881Speter SVN_ERR(get_root(&root, eb)); 379251881Speter 380251881Speter if (SVN_IS_VALID_REVNUM(replaces_rev)) 381251881Speter { 382251881Speter SVN_ERR(can_modify(root, fspath, replaces_rev, scratch_pool)); 383251881Speter SVN_ERR(svn_fs_delete(root, fspath, scratch_pool)); 384251881Speter } 385251881Speter else 386251881Speter { 387251881Speter SVN_ERR(can_create(root, fspath, scratch_pool)); 388251881Speter } 389251881Speter 390251881Speter SVN_ERR(svn_fs_make_file(root, fspath, scratch_pool)); 391251881Speter 392251881Speter SVN_ERR(set_text(root, fspath, checksum, contents, 393251881Speter eb->cancel_func, eb->cancel_baton, scratch_pool)); 394251881Speter SVN_ERR(add_new_props(root, fspath, props, scratch_pool)); 395251881Speter 396251881Speter return SVN_NO_ERROR; 397251881Speter} 398251881Speter 399251881Speter 400251881Speter/* This implements svn_editor_cb_add_symlink_t */ 401251881Speterstatic svn_error_t * 402251881Speteradd_symlink_cb(void *baton, 403251881Speter const char *relpath, 404251881Speter const char *target, 405251881Speter apr_hash_t *props, 406251881Speter svn_revnum_t replaces_rev, 407251881Speter apr_pool_t *scratch_pool) 408251881Speter{ 409251881Speter struct edit_baton *eb = baton; 410251881Speter const char *fspath = FSPATH(relpath, scratch_pool); 411251881Speter svn_fs_root_t *root; 412251881Speter 413251881Speter SVN_ERR(get_root(&root, eb)); 414251881Speter 415251881Speter if (SVN_IS_VALID_REVNUM(replaces_rev)) 416251881Speter { 417251881Speter SVN_ERR(can_modify(root, fspath, replaces_rev, scratch_pool)); 418251881Speter SVN_ERR(svn_fs_delete(root, fspath, scratch_pool)); 419251881Speter } 420251881Speter else 421251881Speter { 422251881Speter SVN_ERR(can_create(root, fspath, scratch_pool)); 423251881Speter } 424251881Speter 425251881Speter /* ### we probably need to construct a file with specific contents 426251881Speter ### (until the FS grows some symlink APIs) */ 427251881Speter#if 0 428251881Speter SVN_ERR(svn_fs_make_file(root, fspath, scratch_pool)); 429251881Speter SVN_ERR(svn_fs_apply_text(&fs_contents, root, fspath, 430251881Speter NULL /* result_checksum */, 431251881Speter scratch_pool)); 432251881Speter /* ### SVN_ERR(svn_stream_printf(fs_contents, ..., scratch_pool)); */ 433251881Speter apr_hash_set(props, SVN_PROP_SPECIAL, APR_HASH_KEY_STRING, 434251881Speter SVN_PROP_SPECIAL_VALUE); 435251881Speter 436251881Speter SVN_ERR(add_new_props(root, fspath, props, scratch_pool)); 437251881Speter#endif 438251881Speter 439251881Speter SVN__NOT_IMPLEMENTED(); 440251881Speter} 441251881Speter 442251881Speter 443251881Speter/* This implements svn_editor_cb_add_absent_t */ 444251881Speterstatic svn_error_t * 445251881Speteradd_absent_cb(void *baton, 446251881Speter const char *relpath, 447251881Speter svn_node_kind_t kind, 448251881Speter svn_revnum_t replaces_rev, 449251881Speter apr_pool_t *scratch_pool) 450251881Speter{ 451251881Speter /* This is a programming error. Code should not attempt to create these 452251881Speter kinds of nodes within the FS. */ 453251881Speter /* ### use a custom error code */ 454251881Speter return svn_error_create( 455251881Speter SVN_ERR_UNSUPPORTED_FEATURE, NULL, 456251881Speter _("The filesystem does not support 'absent' nodes")); 457251881Speter} 458251881Speter 459251881Speter 460251881Speter/* This implements svn_editor_cb_alter_directory_t */ 461251881Speterstatic svn_error_t * 462251881Speteralter_directory_cb(void *baton, 463251881Speter const char *relpath, 464251881Speter svn_revnum_t revision, 465251881Speter const apr_array_header_t *children, 466251881Speter apr_hash_t *props, 467251881Speter apr_pool_t *scratch_pool) 468251881Speter{ 469251881Speter struct edit_baton *eb = baton; 470251881Speter const char *fspath = FSPATH(relpath, scratch_pool); 471251881Speter svn_fs_root_t *root; 472251881Speter 473251881Speter /* Note: we ignore CHILDREN. We have no "incomplete" state to worry about, 474251881Speter so we don't need to be aware of what children will be created. */ 475251881Speter 476251881Speter SVN_ERR(get_root(&root, eb)); 477251881Speter SVN_ERR(can_modify(root, fspath, revision, scratch_pool)); 478251881Speter 479251881Speter if (props) 480251881Speter SVN_ERR(alter_props(root, fspath, props, scratch_pool)); 481251881Speter 482251881Speter return SVN_NO_ERROR; 483251881Speter} 484251881Speter 485251881Speter 486251881Speter/* This implements svn_editor_cb_alter_file_t */ 487251881Speterstatic svn_error_t * 488251881Speteralter_file_cb(void *baton, 489251881Speter const char *relpath, 490251881Speter svn_revnum_t revision, 491251881Speter apr_hash_t *props, 492251881Speter const svn_checksum_t *checksum, 493251881Speter svn_stream_t *contents, 494251881Speter apr_pool_t *scratch_pool) 495251881Speter{ 496251881Speter struct edit_baton *eb = baton; 497251881Speter const char *fspath = FSPATH(relpath, scratch_pool); 498251881Speter svn_fs_root_t *root; 499251881Speter 500251881Speter SVN_ERR(get_root(&root, eb)); 501251881Speter SVN_ERR(can_modify(root, fspath, revision, scratch_pool)); 502251881Speter 503251881Speter if (contents != NULL) 504251881Speter { 505251881Speter SVN_ERR_ASSERT(checksum != NULL); 506251881Speter SVN_ERR(set_text(root, fspath, checksum, contents, 507251881Speter eb->cancel_func, eb->cancel_baton, scratch_pool)); 508251881Speter } 509251881Speter 510251881Speter if (props != NULL) 511251881Speter { 512251881Speter SVN_ERR(alter_props(root, fspath, props, scratch_pool)); 513251881Speter } 514251881Speter 515251881Speter return SVN_NO_ERROR; 516251881Speter} 517251881Speter 518251881Speter 519251881Speter/* This implements svn_editor_cb_alter_symlink_t */ 520251881Speterstatic svn_error_t * 521251881Speteralter_symlink_cb(void *baton, 522251881Speter const char *relpath, 523251881Speter svn_revnum_t revision, 524251881Speter apr_hash_t *props, 525251881Speter const char *target, 526251881Speter apr_pool_t *scratch_pool) 527251881Speter{ 528251881Speter struct edit_baton *eb = baton; 529251881Speter 530251881Speter UNUSED(eb); SVN__NOT_IMPLEMENTED(); 531251881Speter} 532251881Speter 533251881Speter 534251881Speter/* This implements svn_editor_cb_delete_t */ 535251881Speterstatic svn_error_t * 536251881Speterdelete_cb(void *baton, 537251881Speter const char *relpath, 538251881Speter svn_revnum_t revision, 539251881Speter apr_pool_t *scratch_pool) 540251881Speter{ 541251881Speter struct edit_baton *eb = baton; 542251881Speter const char *fspath = FSPATH(relpath, scratch_pool); 543251881Speter svn_fs_root_t *root; 544251881Speter 545251881Speter SVN_ERR(get_root(&root, eb)); 546251881Speter SVN_ERR(can_modify(root, fspath, revision, scratch_pool)); 547251881Speter 548251881Speter SVN_ERR(svn_fs_delete(root, fspath, scratch_pool)); 549251881Speter 550251881Speter return SVN_NO_ERROR; 551251881Speter} 552251881Speter 553251881Speter 554251881Speter/* This implements svn_editor_cb_copy_t */ 555251881Speterstatic svn_error_t * 556251881Spetercopy_cb(void *baton, 557251881Speter const char *src_relpath, 558251881Speter svn_revnum_t src_revision, 559251881Speter const char *dst_relpath, 560251881Speter svn_revnum_t replaces_rev, 561251881Speter apr_pool_t *scratch_pool) 562251881Speter{ 563251881Speter struct edit_baton *eb = baton; 564251881Speter const char *src_fspath = FSPATH(src_relpath, scratch_pool); 565251881Speter const char *dst_fspath = FSPATH(dst_relpath, scratch_pool); 566251881Speter svn_fs_root_t *root; 567251881Speter svn_fs_root_t *src_root; 568251881Speter 569251881Speter SVN_ERR(get_root(&root, eb)); 570251881Speter 571251881Speter /* Check if we can we replace the maybe-specified destination (revision). */ 572251881Speter if (SVN_IS_VALID_REVNUM(replaces_rev)) 573251881Speter { 574251881Speter SVN_ERR(can_modify(root, dst_fspath, replaces_rev, scratch_pool)); 575251881Speter SVN_ERR(svn_fs_delete(root, dst_fspath, scratch_pool)); 576251881Speter } 577251881Speter else 578251881Speter { 579251881Speter SVN_ERR(can_create(root, dst_fspath, scratch_pool)); 580251881Speter } 581251881Speter 582251881Speter SVN_ERR(svn_fs_revision_root(&src_root, svn_fs_root_fs(root), src_revision, 583251881Speter scratch_pool)); 584251881Speter SVN_ERR(svn_fs_copy(src_root, src_fspath, root, dst_fspath, scratch_pool)); 585251881Speter svn_fs_close_root(src_root); 586251881Speter 587251881Speter return SVN_NO_ERROR; 588251881Speter} 589251881Speter 590251881Speter 591251881Speter/* This implements svn_editor_cb_move_t */ 592251881Speterstatic svn_error_t * 593251881Spetermove_cb(void *baton, 594251881Speter const char *src_relpath, 595251881Speter svn_revnum_t src_revision, 596251881Speter const char *dst_relpath, 597251881Speter svn_revnum_t replaces_rev, 598251881Speter apr_pool_t *scratch_pool) 599251881Speter{ 600251881Speter struct edit_baton *eb = baton; 601251881Speter const char *src_fspath = FSPATH(src_relpath, scratch_pool); 602251881Speter const char *dst_fspath = FSPATH(dst_relpath, scratch_pool); 603251881Speter svn_fs_root_t *root; 604251881Speter svn_fs_root_t *src_root; 605251881Speter 606251881Speter SVN_ERR(get_root(&root, eb)); 607251881Speter 608251881Speter /* Check if we delete the specified source (revision), and can we replace 609251881Speter the maybe-specified destination (revision). */ 610251881Speter SVN_ERR(can_modify(root, src_fspath, src_revision, scratch_pool)); 611251881Speter if (SVN_IS_VALID_REVNUM(replaces_rev)) 612251881Speter { 613251881Speter SVN_ERR(can_modify(root, dst_fspath, replaces_rev, scratch_pool)); 614251881Speter SVN_ERR(svn_fs_delete(root, dst_fspath, scratch_pool)); 615251881Speter } 616251881Speter else 617251881Speter { 618251881Speter SVN_ERR(can_create(root, dst_fspath, scratch_pool)); 619251881Speter } 620251881Speter 621251881Speter /* ### would be nice to have svn_fs_move() */ 622251881Speter 623251881Speter /* Copy the src to the dst. */ 624251881Speter SVN_ERR(svn_fs_revision_root(&src_root, svn_fs_root_fs(root), src_revision, 625251881Speter scratch_pool)); 626251881Speter SVN_ERR(svn_fs_copy(src_root, src_fspath, root, dst_fspath, scratch_pool)); 627251881Speter svn_fs_close_root(src_root); 628251881Speter 629251881Speter /* Notice: we're deleting the src repos path from the dst root. */ 630251881Speter SVN_ERR(svn_fs_delete(root, src_fspath, scratch_pool)); 631251881Speter 632251881Speter return SVN_NO_ERROR; 633251881Speter} 634251881Speter 635251881Speter 636251881Speter/* This implements svn_editor_cb_rotate_t */ 637251881Speterstatic svn_error_t * 638251881Speterrotate_cb(void *baton, 639251881Speter const apr_array_header_t *relpaths, 640251881Speter const apr_array_header_t *revisions, 641251881Speter apr_pool_t *scratch_pool) 642251881Speter{ 643251881Speter struct edit_baton *eb = baton; 644251881Speter 645251881Speter UNUSED(eb); SVN__NOT_IMPLEMENTED(); 646251881Speter} 647251881Speter 648251881Speter 649251881Speter/* This implements svn_editor_cb_complete_t */ 650251881Speterstatic svn_error_t * 651251881Spetercomplete_cb(void *baton, 652251881Speter apr_pool_t *scratch_pool) 653251881Speter{ 654251881Speter struct edit_baton *eb = baton; 655251881Speter 656251881Speter /* Watch out for a following call to svn_fs_editor_commit(). Note that 657251881Speter we are likely here because svn_fs_editor_commit() was called, and it 658251881Speter invoked svn_editor_complete(). */ 659251881Speter eb->completed = TRUE; 660251881Speter 661251881Speter if (eb->root != NULL) 662251881Speter { 663251881Speter svn_fs_close_root(eb->root); 664251881Speter eb->root = NULL; 665251881Speter } 666251881Speter 667251881Speter return SVN_NO_ERROR; 668251881Speter} 669251881Speter 670251881Speter 671251881Speter/* This implements svn_editor_cb_abort_t */ 672251881Speterstatic svn_error_t * 673251881Speterabort_cb(void *baton, 674251881Speter apr_pool_t *scratch_pool) 675251881Speter{ 676251881Speter struct edit_baton *eb = baton; 677251881Speter svn_error_t *err; 678251881Speter 679251881Speter /* Don't allow a following call to svn_fs_editor_commit(). */ 680251881Speter eb->completed = TRUE; 681251881Speter 682251881Speter if (eb->root != NULL) 683251881Speter { 684251881Speter svn_fs_close_root(eb->root); 685251881Speter eb->root = NULL; 686251881Speter } 687251881Speter 688251881Speter /* ### should we examine the error and attempt svn_fs_purge_txn() ? */ 689251881Speter err = svn_fs_abort_txn(eb->txn, scratch_pool); 690251881Speter 691251881Speter /* For safety, clear the now-useless txn. */ 692251881Speter eb->txn = NULL; 693251881Speter 694251881Speter return svn_error_trace(err); 695251881Speter} 696251881Speter 697251881Speter 698251881Speterstatic svn_error_t * 699251881Spetermake_editor(svn_editor_t **editor, 700251881Speter svn_fs_txn_t *txn, 701251881Speter svn_cancel_func_t cancel_func, 702251881Speter void *cancel_baton, 703251881Speter apr_pool_t *result_pool, 704251881Speter apr_pool_t *scratch_pool) 705251881Speter{ 706251881Speter static const svn_editor_cb_many_t editor_cbs = { 707251881Speter add_directory_cb, 708251881Speter add_file_cb, 709251881Speter add_symlink_cb, 710251881Speter add_absent_cb, 711251881Speter alter_directory_cb, 712251881Speter alter_file_cb, 713251881Speter alter_symlink_cb, 714251881Speter delete_cb, 715251881Speter copy_cb, 716251881Speter move_cb, 717251881Speter rotate_cb, 718251881Speter complete_cb, 719251881Speter abort_cb 720251881Speter }; 721251881Speter struct edit_baton *eb = apr_pcalloc(result_pool, sizeof(*eb)); 722251881Speter 723251881Speter eb->txn = txn; 724251881Speter eb->cancel_func = cancel_func; 725251881Speter eb->cancel_baton = cancel_baton; 726251881Speter eb->txn_pool = result_pool; 727251881Speter 728251881Speter SVN_ERR(svn_editor_create(editor, eb, cancel_func, cancel_baton, 729251881Speter result_pool, scratch_pool)); 730251881Speter SVN_ERR(svn_editor_setcb_many(*editor, &editor_cbs, scratch_pool)); 731251881Speter 732251881Speter return SVN_NO_ERROR; 733251881Speter} 734251881Speter 735251881Speter 736251881Spetersvn_error_t * 737251881Spetersvn_fs__editor_create(svn_editor_t **editor, 738251881Speter const char **txn_name, 739251881Speter svn_fs_t *fs, 740251881Speter apr_uint32_t flags, 741251881Speter svn_cancel_func_t cancel_func, 742251881Speter void *cancel_baton, 743251881Speter apr_pool_t *result_pool, 744251881Speter apr_pool_t *scratch_pool) 745251881Speter{ 746251881Speter svn_revnum_t revision; 747251881Speter svn_fs_txn_t *txn; 748251881Speter 749251881Speter SVN_ERR(svn_fs_youngest_rev(&revision, fs, scratch_pool)); 750251881Speter SVN_ERR(svn_fs_begin_txn2(&txn, fs, revision, flags, result_pool)); 751251881Speter SVN_ERR(svn_fs_txn_name(txn_name, txn, result_pool)); 752251881Speter return svn_error_trace(make_editor(editor, txn, 753251881Speter cancel_func, cancel_baton, 754251881Speter result_pool, scratch_pool)); 755251881Speter} 756251881Speter 757251881Speter 758251881Spetersvn_error_t * 759251881Spetersvn_fs__editor_create_for(svn_editor_t **editor, 760251881Speter svn_fs_t *fs, 761251881Speter const char *txn_name, 762251881Speter svn_cancel_func_t cancel_func, 763251881Speter void *cancel_baton, 764251881Speter apr_pool_t *result_pool, 765251881Speter apr_pool_t *scratch_pool) 766251881Speter{ 767251881Speter svn_fs_txn_t *txn; 768251881Speter 769251881Speter SVN_ERR(svn_fs_open_txn(&txn, fs, txn_name, result_pool)); 770251881Speter return svn_error_trace(make_editor(editor, txn, 771251881Speter cancel_func, cancel_baton, 772251881Speter result_pool, scratch_pool)); 773251881Speter} 774251881Speter 775251881Speter 776251881Spetersvn_error_t * 777251881Spetersvn_fs__editor_commit(svn_revnum_t *revision, 778251881Speter svn_error_t **post_commit_err, 779251881Speter const char **conflict_path, 780251881Speter svn_editor_t *editor, 781251881Speter apr_pool_t *result_pool, 782251881Speter apr_pool_t *scratch_pool) 783251881Speter{ 784251881Speter struct edit_baton *eb = svn_editor_get_baton(editor); 785251881Speter const char *inner_conflict_path; 786251881Speter svn_error_t *err = NULL; 787251881Speter 788251881Speter /* make sure people are using the correct sequencing. */ 789251881Speter if (eb->completed) 790251881Speter return svn_error_create(SVN_ERR_FS_INCORRECT_EDITOR_COMPLETION, 791251881Speter NULL, NULL); 792251881Speter 793251881Speter *revision = SVN_INVALID_REVNUM; 794251881Speter *post_commit_err = NULL; 795251881Speter *conflict_path = NULL; 796251881Speter 797251881Speter /* Clean up internal resources (eg. eb->root). This also allows the 798251881Speter editor infrastructure to know this editor is "complete". */ 799251881Speter err = svn_editor_complete(editor); 800251881Speter 801251881Speter /* Note: docco for svn_fs_commit_txn() states that CONFLICT_PATH will 802251881Speter be allocated in the txn's pool. But it lies. Regardless, we want 803251881Speter it placed into RESULT_POOL. */ 804251881Speter 805251881Speter if (!err) 806251881Speter err = svn_fs_commit_txn(&inner_conflict_path, 807251881Speter revision, 808251881Speter eb->txn, 809251881Speter scratch_pool); 810251881Speter if (SVN_IS_VALID_REVNUM(*revision)) 811251881Speter { 812251881Speter if (err) 813251881Speter { 814251881Speter /* Case 3. ERR is a post-commit (cleanup) error. */ 815251881Speter 816251881Speter /* Pass responsibility via POST_COMMIT_ERR. */ 817251881Speter *post_commit_err = err; 818251881Speter err = SVN_NO_ERROR; 819251881Speter } 820251881Speter /* else: Case 1. */ 821251881Speter } 822251881Speter else 823251881Speter { 824251881Speter SVN_ERR_ASSERT(err != NULL); 825251881Speter if (err->apr_err == SVN_ERR_FS_CONFLICT) 826251881Speter { 827251881Speter /* Case 2. */ 828251881Speter 829251881Speter /* Copy this into the correct pool (see note above). */ 830251881Speter *conflict_path = apr_pstrdup(result_pool, inner_conflict_path); 831251881Speter 832251881Speter /* Return sucess. The caller should inspect CONFLICT_PATH to 833251881Speter determine this particular case. */ 834251881Speter svn_error_clear(err); 835251881Speter err = SVN_NO_ERROR; 836251881Speter } 837251881Speter /* else: Case 4. */ 838251881Speter 839251881Speter /* Abort the TXN. Nobody wants to use it. */ 840251881Speter /* ### should we examine the error and attempt svn_fs_purge_txn() ? */ 841251881Speter err = svn_error_compose_create( 842251881Speter err, 843251881Speter svn_fs_abort_txn(eb->txn, scratch_pool)); 844251881Speter } 845251881Speter 846251881Speter /* For safety, clear the now-useless txn. */ 847251881Speter eb->txn = NULL; 848251881Speter 849251881Speter return svn_error_trace(err); 850251881Speter} 851