1289177Speter/* 2289177Speter * mtcc.c -- Multi Command Context implementation. This allows 3289177Speter * performing many operations without a working copy. 4289177Speter * 5289177Speter * ==================================================================== 6289177Speter * Licensed to the Apache Software Foundation (ASF) under one 7289177Speter * or more contributor license agreements. See the NOTICE file 8289177Speter * distributed with this work for additional information 9289177Speter * regarding copyright ownership. The ASF licenses this file 10289177Speter * to you under the Apache License, Version 2.0 (the 11289177Speter * "License"); you may not use this file except in compliance 12289177Speter * with the License. You may obtain a copy of the License at 13289177Speter * 14289177Speter * http://www.apache.org/licenses/LICENSE-2.0 15289177Speter * 16289177Speter * Unless required by applicable law or agreed to in writing, 17289177Speter * software distributed under the License is distributed on an 18289177Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19289177Speter * KIND, either express or implied. See the License for the 20289177Speter * specific language governing permissions and limitations 21289177Speter * under the License. 22289177Speter * ==================================================================== 23289177Speter */ 24289177Speter 25289177Speter#include "svn_dirent_uri.h" 26289177Speter#include "svn_hash.h" 27289177Speter#include "svn_path.h" 28289177Speter#include "svn_props.h" 29289177Speter#include "svn_pools.h" 30289177Speter#include "svn_subst.h" 31289177Speter 32289177Speter#include "private/svn_client_mtcc.h" 33289177Speter 34289177Speter 35289177Speter#include "svn_private_config.h" 36289177Speter 37289177Speter#include "client.h" 38289177Speter 39289177Speter#include <assert.h> 40289177Speter 41289177Speter#define SVN_PATH_IS_EMPTY(s) ((s)[0] == '\0') 42289177Speter 43289177Speter/* The kind of operation to perform in an mtcc_op_t */ 44289177Spetertypedef enum mtcc_kind_t 45289177Speter{ 46289177Speter OP_OPEN_DIR, 47289177Speter OP_OPEN_FILE, 48289177Speter OP_ADD_DIR, 49289177Speter OP_ADD_FILE, 50289177Speter OP_DELETE 51289177Speter} mtcc_kind_t; 52289177Speter 53289177Spetertypedef struct mtcc_op_t 54289177Speter{ 55289177Speter const char *name; /* basename of operation */ 56289177Speter mtcc_kind_t kind; /* editor operation */ 57289177Speter 58289177Speter apr_array_header_t *children; /* List of mtcc_op_t * */ 59289177Speter 60289177Speter const char *src_relpath; /* For ADD_DIR, ADD_FILE */ 61289177Speter svn_revnum_t src_rev; /* For ADD_DIR, ADD_FILE */ 62289177Speter svn_stream_t *src_stream; /* For ADD_FILE, OPEN_FILE */ 63289177Speter svn_checksum_t *src_checksum; /* For ADD_FILE, OPEN_FILE */ 64289177Speter svn_stream_t *base_stream; /* For ADD_FILE, OPEN_FILE */ 65289177Speter const svn_checksum_t *base_checksum; /* For ADD_FILE, OPEN_FILE */ 66289177Speter 67289177Speter apr_array_header_t *prop_mods; /* For all except DELETE 68289177Speter List of svn_prop_t */ 69289177Speter 70289177Speter svn_boolean_t performed_stat; /* Verified kind with repository */ 71289177Speter} mtcc_op_t; 72289177Speter 73289177Speter/* Check if the mtcc doesn't contain any modifications yet */ 74289177Speter#define MTCC_UNMODIFIED(mtcc) \ 75289177Speter ((mtcc->root_op->kind == OP_OPEN_DIR \ 76289177Speter || mtcc->root_op->kind == OP_OPEN_FILE) \ 77289177Speter && (mtcc->root_op->prop_mods == NULL \ 78289177Speter || !mtcc->root_op->prop_mods->nelts) \ 79289177Speter && (mtcc->root_op->children == NULL \ 80289177Speter || !mtcc->root_op->children->nelts)) 81289177Speter 82289177Speterstruct svn_client__mtcc_t 83289177Speter{ 84289177Speter apr_pool_t *pool; 85289177Speter svn_revnum_t head_revision; 86289177Speter svn_revnum_t base_revision; 87289177Speter 88289177Speter svn_ra_session_t *ra_session; 89289177Speter svn_client_ctx_t *ctx; 90289177Speter 91289177Speter mtcc_op_t *root_op; 92289177Speter}; 93289177Speter 94289177Speterstatic mtcc_op_t * 95289177Spetermtcc_op_create(const char *name, 96289177Speter svn_boolean_t add, 97289177Speter svn_boolean_t directory, 98289177Speter apr_pool_t *result_pool) 99289177Speter{ 100289177Speter mtcc_op_t *op; 101289177Speter 102289177Speter op = apr_pcalloc(result_pool, sizeof(*op)); 103289177Speter op->name = name ? apr_pstrdup(result_pool, name) : ""; 104289177Speter 105289177Speter if (add) 106289177Speter op->kind = directory ? OP_ADD_DIR : OP_ADD_FILE; 107289177Speter else 108289177Speter op->kind = directory ? OP_OPEN_DIR : OP_OPEN_FILE; 109289177Speter 110289177Speter if (directory) 111289177Speter op->children = apr_array_make(result_pool, 4, sizeof(mtcc_op_t *)); 112289177Speter 113289177Speter op->src_rev = SVN_INVALID_REVNUM; 114289177Speter 115289177Speter return op; 116289177Speter} 117289177Speter 118289177Speterstatic svn_error_t * 119289177Spetermtcc_op_find(mtcc_op_t **op, 120289177Speter svn_boolean_t *created, 121289177Speter const char *relpath, 122289177Speter mtcc_op_t *base_op, 123289177Speter svn_boolean_t find_existing, 124289177Speter svn_boolean_t find_deletes, 125289177Speter svn_boolean_t create_file, 126289177Speter apr_pool_t *result_pool, 127289177Speter apr_pool_t *scratch_pool) 128289177Speter{ 129289177Speter const char *name; 130289177Speter const char *child; 131289177Speter int i; 132289177Speter 133289177Speter assert(svn_relpath_is_canonical(relpath)); 134289177Speter if (created) 135289177Speter *created = FALSE; 136289177Speter 137289177Speter if (SVN_PATH_IS_EMPTY(relpath)) 138289177Speter { 139289177Speter if (find_existing) 140289177Speter *op = base_op; 141289177Speter else 142289177Speter *op = NULL; 143289177Speter 144289177Speter return SVN_NO_ERROR; 145289177Speter } 146289177Speter 147289177Speter child = strchr(relpath, '/'); 148289177Speter 149289177Speter if (child) 150289177Speter { 151289177Speter name = apr_pstrmemdup(scratch_pool, relpath, (child-relpath)); 152289177Speter child++; /* Skip '/' */ 153289177Speter } 154289177Speter else 155289177Speter name = relpath; 156289177Speter 157289177Speter if (!base_op->children) 158289177Speter { 159289177Speter if (!created) 160289177Speter { 161289177Speter *op = NULL; 162289177Speter return SVN_NO_ERROR; 163289177Speter } 164289177Speter else 165289177Speter return svn_error_createf(SVN_ERR_FS_NOT_DIRECTORY, NULL, 166289177Speter _("Can't operate on '%s' because '%s' is not a " 167289177Speter "directory"), 168289177Speter name, base_op->name); 169289177Speter } 170289177Speter 171289177Speter for (i = base_op->children->nelts-1; i >= 0 ; i--) 172289177Speter { 173289177Speter mtcc_op_t *cop; 174289177Speter 175289177Speter cop = APR_ARRAY_IDX(base_op->children, i, mtcc_op_t *); 176289177Speter 177289177Speter if (! strcmp(cop->name, name) 178289177Speter && (find_deletes || cop->kind != OP_DELETE)) 179289177Speter { 180289177Speter return svn_error_trace( 181289177Speter mtcc_op_find(op, created, child ? child : "", cop, 182289177Speter find_existing, find_deletes, create_file, 183289177Speter result_pool, scratch_pool)); 184289177Speter } 185289177Speter } 186289177Speter 187289177Speter if (!created) 188289177Speter { 189289177Speter *op = NULL; 190289177Speter return SVN_NO_ERROR; 191289177Speter } 192289177Speter 193289177Speter { 194289177Speter mtcc_op_t *cop; 195289177Speter 196289177Speter cop = mtcc_op_create(name, FALSE, child || !create_file, result_pool); 197289177Speter 198289177Speter APR_ARRAY_PUSH(base_op->children, mtcc_op_t *) = cop; 199289177Speter 200289177Speter if (!child) 201289177Speter { 202289177Speter *op = cop; 203289177Speter *created = TRUE; 204289177Speter return SVN_NO_ERROR; 205289177Speter } 206289177Speter 207289177Speter return svn_error_trace( 208289177Speter mtcc_op_find(op, created, child, cop, find_existing, 209289177Speter find_deletes, create_file, 210289177Speter result_pool, scratch_pool)); 211289177Speter } 212289177Speter} 213289177Speter 214289177Speter/* Gets the original repository location of RELPATH, checking things 215289177Speter like copies, moves, etc. */ 216289177Speterstatic svn_error_t * 217289177Speterget_origin(svn_boolean_t *done, 218289177Speter const char **origin_relpath, 219289177Speter svn_revnum_t *rev, 220289177Speter mtcc_op_t *op, 221289177Speter const char *relpath, 222289177Speter apr_pool_t *result_pool, 223289177Speter apr_pool_t *scratch_pool) 224289177Speter{ 225289177Speter const char *child; 226289177Speter const char *name; 227289177Speter if (SVN_PATH_IS_EMPTY(relpath)) 228289177Speter { 229289177Speter if (op->kind == OP_ADD_DIR || op->kind == OP_ADD_FILE) 230289177Speter *done = TRUE; 231289177Speter *origin_relpath = op->src_relpath 232289177Speter ? apr_pstrdup(result_pool, op->src_relpath) 233289177Speter : NULL; 234289177Speter *rev = op->src_rev; 235289177Speter return SVN_NO_ERROR; 236289177Speter } 237289177Speter 238289177Speter child = strchr(relpath, '/'); 239289177Speter if (child) 240289177Speter { 241289177Speter name = apr_pstrmemdup(scratch_pool, relpath, child-relpath); 242289177Speter child++; /* Skip '/' */ 243289177Speter } 244289177Speter else 245289177Speter name = relpath; 246289177Speter 247289177Speter if (op->children && op->children->nelts) 248289177Speter { 249289177Speter int i; 250289177Speter 251289177Speter for (i = op->children->nelts-1; i >= 0; i--) 252289177Speter { 253289177Speter mtcc_op_t *cop; 254289177Speter 255289177Speter cop = APR_ARRAY_IDX(op->children, i, mtcc_op_t *); 256289177Speter 257289177Speter if (! strcmp(cop->name, name)) 258289177Speter { 259289177Speter if (cop->kind == OP_DELETE) 260289177Speter { 261289177Speter *done = TRUE; 262289177Speter return SVN_NO_ERROR; 263289177Speter } 264289177Speter 265289177Speter SVN_ERR(get_origin(done, origin_relpath, rev, 266289177Speter cop, child ? child : "", 267289177Speter result_pool, scratch_pool)); 268289177Speter 269289177Speter if (*origin_relpath || *done) 270289177Speter return SVN_NO_ERROR; 271289177Speter 272289177Speter break; 273289177Speter } 274289177Speter } 275289177Speter } 276289177Speter 277289177Speter if (op->kind == OP_ADD_DIR || op->kind == OP_ADD_FILE) 278289177Speter { 279289177Speter *done = TRUE; 280289177Speter if (op->src_relpath) 281289177Speter { 282289177Speter *origin_relpath = svn_relpath_join(op->src_relpath, relpath, 283289177Speter result_pool); 284289177Speter *rev = op->src_rev; 285289177Speter } 286289177Speter } 287289177Speter 288289177Speter return SVN_NO_ERROR; 289289177Speter} 290289177Speter 291289177Speter/* Obtains the original repository location for an mtcc relpath as 292289177Speter *ORIGIN_RELPATH @ *REV, if it has one. If it has not and IGNORE_ENOENT 293289177Speter is TRUE report *ORIGIN_RELPATH as NULL, otherwise return an error */ 294289177Speterstatic svn_error_t * 295289177Spetermtcc_get_origin(const char **origin_relpath, 296289177Speter svn_revnum_t *rev, 297289177Speter const char *relpath, 298289177Speter svn_boolean_t ignore_enoent, 299289177Speter svn_client__mtcc_t *mtcc, 300289177Speter apr_pool_t *result_pool, 301289177Speter apr_pool_t *scratch_pool) 302289177Speter{ 303289177Speter svn_boolean_t done = FALSE; 304289177Speter 305289177Speter *origin_relpath = NULL; 306289177Speter *rev = SVN_INVALID_REVNUM; 307289177Speter 308289177Speter SVN_ERR(get_origin(&done, origin_relpath, rev, mtcc->root_op, relpath, 309289177Speter result_pool, scratch_pool)); 310289177Speter 311289177Speter if (!*origin_relpath && !done) 312289177Speter { 313289177Speter *origin_relpath = apr_pstrdup(result_pool, relpath); 314289177Speter *rev = mtcc->base_revision; 315289177Speter } 316289177Speter else if (!ignore_enoent) 317289177Speter { 318289177Speter return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 319289177Speter _("No origin found for node at '%s'"), 320289177Speter relpath); 321289177Speter } 322289177Speter 323289177Speter return SVN_NO_ERROR; 324289177Speter} 325289177Speter 326289177Spetersvn_error_t * 327289177Spetersvn_client__mtcc_create(svn_client__mtcc_t **mtcc, 328289177Speter const char *anchor_url, 329289177Speter svn_revnum_t base_revision, 330289177Speter svn_client_ctx_t *ctx, 331289177Speter apr_pool_t *result_pool, 332289177Speter apr_pool_t *scratch_pool) 333289177Speter{ 334289177Speter apr_pool_t *mtcc_pool; 335289177Speter 336289177Speter mtcc_pool = svn_pool_create(result_pool); 337289177Speter 338289177Speter *mtcc = apr_pcalloc(mtcc_pool, sizeof(**mtcc)); 339289177Speter (*mtcc)->pool = mtcc_pool; 340289177Speter 341289177Speter (*mtcc)->root_op = mtcc_op_create(NULL, FALSE, TRUE, mtcc_pool); 342289177Speter 343289177Speter (*mtcc)->ctx = ctx; 344289177Speter 345289177Speter SVN_ERR(svn_client_open_ra_session2(&(*mtcc)->ra_session, anchor_url, 346289177Speter NULL /* wri_abspath */, ctx, 347289177Speter mtcc_pool, scratch_pool)); 348289177Speter 349289177Speter SVN_ERR(svn_ra_get_latest_revnum((*mtcc)->ra_session, &(*mtcc)->head_revision, 350289177Speter scratch_pool)); 351289177Speter 352289177Speter if (SVN_IS_VALID_REVNUM(base_revision)) 353289177Speter (*mtcc)->base_revision = base_revision; 354289177Speter else 355289177Speter (*mtcc)->base_revision = (*mtcc)->head_revision; 356289177Speter 357289177Speter if ((*mtcc)->base_revision > (*mtcc)->head_revision) 358289177Speter return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, 359289177Speter _("No such revision %ld (HEAD is %ld)"), 360289177Speter base_revision, (*mtcc)->head_revision); 361289177Speter 362289177Speter return SVN_NO_ERROR; 363289177Speter} 364289177Speter 365289177Speterstatic svn_error_t * 366289177Speterupdate_copy_src(mtcc_op_t *op, 367289177Speter const char *add_relpath, 368289177Speter apr_pool_t *result_pool) 369289177Speter{ 370289177Speter int i; 371289177Speter 372289177Speter if (op->src_relpath) 373289177Speter op->src_relpath = svn_relpath_join(add_relpath, op->src_relpath, 374289177Speter result_pool); 375289177Speter 376289177Speter if (!op->children) 377289177Speter return SVN_NO_ERROR; 378289177Speter 379289177Speter for (i = 0; i < op->children->nelts; i++) 380289177Speter { 381289177Speter mtcc_op_t *cop; 382289177Speter 383289177Speter cop = APR_ARRAY_IDX(op->children, i, mtcc_op_t *); 384289177Speter 385289177Speter SVN_ERR(update_copy_src(cop, add_relpath, result_pool)); 386289177Speter } 387289177Speter 388289177Speter return SVN_NO_ERROR; 389289177Speter} 390289177Speter 391289177Speterstatic svn_error_t * 392289177Spetermtcc_reparent(const char *new_anchor_url, 393289177Speter svn_client__mtcc_t *mtcc, 394289177Speter apr_pool_t *scratch_pool) 395289177Speter{ 396289177Speter const char *session_url; 397289177Speter const char *up; 398289177Speter 399289177Speter SVN_ERR(svn_ra_get_session_url(mtcc->ra_session, &session_url, 400289177Speter scratch_pool)); 401289177Speter 402289177Speter up = svn_uri_skip_ancestor(new_anchor_url, session_url, scratch_pool); 403289177Speter 404289177Speter if (! up) 405289177Speter { 406289177Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 407289177Speter _("'%s' is not an ancestor of '%s'"), 408289177Speter new_anchor_url, session_url); 409289177Speter } 410289177Speter else if (!*up) 411289177Speter { 412289177Speter return SVN_NO_ERROR; /* Same url */ 413289177Speter } 414289177Speter 415289177Speter /* Update copy origins recursively...:( */ 416289177Speter SVN_ERR(update_copy_src(mtcc->root_op, up, mtcc->pool)); 417289177Speter 418289177Speter SVN_ERR(svn_ra_reparent(mtcc->ra_session, new_anchor_url, scratch_pool)); 419289177Speter 420289177Speter /* Create directory open operations for new ancestors */ 421289177Speter while (*up) 422289177Speter { 423289177Speter mtcc_op_t *root_op; 424289177Speter 425289177Speter mtcc->root_op->name = svn_relpath_basename(up, mtcc->pool); 426289177Speter up = svn_relpath_dirname(up, scratch_pool); 427289177Speter 428289177Speter root_op = mtcc_op_create(NULL, FALSE, TRUE, mtcc->pool); 429289177Speter 430289177Speter APR_ARRAY_PUSH(root_op->children, mtcc_op_t *) = mtcc->root_op; 431289177Speter 432289177Speter mtcc->root_op = root_op; 433289177Speter } 434289177Speter 435289177Speter return SVN_NO_ERROR; 436289177Speter} 437289177Speter 438289177Speter/* Check if it is safe to create a new node at NEW_RELPATH. Return a proper 439289177Speter error if it is not */ 440289177Speterstatic svn_error_t * 441289177Spetermtcc_verify_create(svn_client__mtcc_t *mtcc, 442289177Speter const char *new_relpath, 443289177Speter apr_pool_t *scratch_pool) 444289177Speter{ 445289177Speter svn_node_kind_t kind; 446289177Speter 447289177Speter if (*new_relpath || !MTCC_UNMODIFIED(mtcc)) 448289177Speter { 449289177Speter mtcc_op_t *op; 450289177Speter 451289177Speter SVN_ERR(mtcc_op_find(&op, NULL, new_relpath, mtcc->root_op, TRUE, FALSE, 452289177Speter FALSE, mtcc->pool, scratch_pool)); 453289177Speter 454289177Speter if (op) 455289177Speter return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL, 456369302Sdim _("Path '%s' already exists, or was created " 457369302Sdim "by an earlier operation"), 458289177Speter new_relpath); 459289177Speter 460289177Speter SVN_ERR(mtcc_op_find(&op, NULL, new_relpath, mtcc->root_op, TRUE, TRUE, 461289177Speter FALSE, mtcc->pool, scratch_pool)); 462289177Speter 463289177Speter if (op) 464289177Speter return SVN_NO_ERROR; /* Node is explicitly deleted. We can replace */ 465289177Speter } 466289177Speter 467289177Speter /* mod_dav_svn used to allow overwriting existing directories. Let's hide 468289177Speter that for users of this api */ 469289177Speter SVN_ERR(svn_client__mtcc_check_path(&kind, new_relpath, FALSE, 470289177Speter mtcc, scratch_pool)); 471289177Speter 472289177Speter if (kind != svn_node_none) 473289177Speter return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL, 474289177Speter _("Path '%s' already exists"), 475289177Speter new_relpath); 476289177Speter 477289177Speter return SVN_NO_ERROR; 478289177Speter} 479289177Speter 480289177Speter 481289177Spetersvn_error_t * 482289177Spetersvn_client__mtcc_add_add_file(const char *relpath, 483289177Speter svn_stream_t *src_stream, 484289177Speter const svn_checksum_t *src_checksum, 485289177Speter svn_client__mtcc_t *mtcc, 486289177Speter apr_pool_t *scratch_pool) 487289177Speter{ 488289177Speter mtcc_op_t *op; 489289177Speter svn_boolean_t created; 490289177Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath) && src_stream); 491289177Speter 492289177Speter SVN_ERR(mtcc_verify_create(mtcc, relpath, scratch_pool)); 493289177Speter 494289177Speter if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc)) 495289177Speter { 496289177Speter /* Turn the root operation into a file addition */ 497289177Speter op = mtcc->root_op; 498289177Speter } 499289177Speter else 500289177Speter { 501289177Speter SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, FALSE, 502289177Speter TRUE, mtcc->pool, scratch_pool)); 503289177Speter 504289177Speter if (!op || !created) 505289177Speter { 506289177Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 507289177Speter _("Can't add file at '%s'"), 508289177Speter relpath); 509289177Speter } 510289177Speter } 511289177Speter 512289177Speter op->kind = OP_ADD_FILE; 513289177Speter op->src_stream = src_stream; 514289177Speter op->src_checksum = src_checksum ? svn_checksum_dup(src_checksum, mtcc->pool) 515289177Speter : NULL; 516289177Speter 517289177Speter return SVN_NO_ERROR; 518289177Speter} 519289177Speter 520289177Spetersvn_error_t * 521289177Spetersvn_client__mtcc_add_copy(const char *src_relpath, 522289177Speter svn_revnum_t revision, 523289177Speter const char *dst_relpath, 524289177Speter svn_client__mtcc_t *mtcc, 525289177Speter apr_pool_t *scratch_pool) 526289177Speter{ 527289177Speter mtcc_op_t *op; 528289177Speter svn_boolean_t created; 529289177Speter svn_node_kind_t kind; 530289177Speter 531289177Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath) 532289177Speter && svn_relpath_is_canonical(dst_relpath)); 533289177Speter 534289177Speter if (! SVN_IS_VALID_REVNUM(revision)) 535289177Speter revision = mtcc->head_revision; 536289177Speter else if (revision > mtcc->head_revision) 537289177Speter { 538289177Speter return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, 539289177Speter _("No such revision %ld"), revision); 540289177Speter } 541289177Speter 542289177Speter SVN_ERR(mtcc_verify_create(mtcc, dst_relpath, scratch_pool)); 543289177Speter 544289177Speter /* Subversion requires the kind of a copy */ 545289177Speter SVN_ERR(svn_ra_check_path(mtcc->ra_session, src_relpath, revision, &kind, 546289177Speter scratch_pool)); 547289177Speter 548289177Speter if (kind != svn_node_dir && kind != svn_node_file) 549289177Speter { 550289177Speter return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 551289177Speter _("Path '%s' not found in revision %ld"), 552289177Speter src_relpath, revision); 553289177Speter } 554289177Speter 555289177Speter SVN_ERR(mtcc_op_find(&op, &created, dst_relpath, mtcc->root_op, FALSE, FALSE, 556289177Speter (kind == svn_node_file), mtcc->pool, scratch_pool)); 557289177Speter 558289177Speter if (!op || !created) 559289177Speter { 560289177Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 561289177Speter _("Can't add node at '%s'"), 562289177Speter dst_relpath); 563289177Speter } 564289177Speter 565289177Speter op->kind = (kind == svn_node_file) ? OP_ADD_FILE : OP_ADD_DIR; 566289177Speter op->src_relpath = apr_pstrdup(mtcc->pool, src_relpath); 567289177Speter op->src_rev = revision; 568289177Speter 569289177Speter return SVN_NO_ERROR; 570289177Speter} 571289177Speter 572362181Sdim/* Check if this operation contains at least one change that is not a 573362181Sdim plain delete */ 574362181Sdimstatic svn_boolean_t 575362181Sdimmtcc_op_contains_non_delete(const mtcc_op_t *op) 576289177Speter{ 577362181Sdim if (op->kind != OP_OPEN_DIR && op->kind != OP_OPEN_FILE 578362181Sdim && op->kind != OP_DELETE) 579362181Sdim { 580362181Sdim return TRUE; 581362181Sdim } 582362181Sdim 583362181Sdim if (op->prop_mods && op->prop_mods->nelts) 584362181Sdim return TRUE; 585362181Sdim 586362181Sdim if (op->src_stream) 587362181Sdim return TRUE; 588362181Sdim 589362181Sdim if (op->children) 590362181Sdim { 591362181Sdim int i; 592362181Sdim 593362181Sdim for (i = 0; i < op->children->nelts; i++) 594362181Sdim { 595362181Sdim const mtcc_op_t *c_op = APR_ARRAY_IDX(op->children, i, 596362181Sdim const mtcc_op_t *); 597362181Sdim 598362181Sdim if (mtcc_op_contains_non_delete(c_op)) 599362181Sdim return TRUE; 600362181Sdim } 601362181Sdim } 602362181Sdim return FALSE; 603362181Sdim} 604362181Sdim 605362181Sdimstatic svn_error_t * 606362181Sdimmtcc_add_delete(const char *relpath, 607362181Sdim svn_boolean_t for_move, 608362181Sdim svn_client__mtcc_t *mtcc, 609362181Sdim apr_pool_t *scratch_pool) 610362181Sdim{ 611289177Speter mtcc_op_t *op; 612289177Speter svn_boolean_t created; 613289177Speter svn_node_kind_t kind; 614289177Speter 615289177Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); 616289177Speter 617289177Speter SVN_ERR(svn_client__mtcc_check_path(&kind, relpath, FALSE, 618289177Speter mtcc, scratch_pool)); 619289177Speter 620289177Speter if (kind == svn_node_none) 621289177Speter return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 622289177Speter _("Can't delete node at '%s' as it " 623289177Speter "does not exist"), 624289177Speter relpath); 625289177Speter 626289177Speter if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc)) 627289177Speter { 628289177Speter /* Turn root operation into delete */ 629289177Speter op = mtcc->root_op; 630289177Speter } 631289177Speter else 632289177Speter { 633289177Speter SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, TRUE, 634289177Speter TRUE, mtcc->pool, scratch_pool)); 635289177Speter 636362181Sdim if (!for_move && !op && !created) 637362181Sdim { 638362181Sdim /* Allow deleting directories, that are unmodified except for 639362181Sdim one or more deleted descendants */ 640362181Sdim 641362181Sdim SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, TRUE, 642362181Sdim FALSE, FALSE, mtcc->pool, scratch_pool)); 643362181Sdim 644362181Sdim if (op && mtcc_op_contains_non_delete(op)) 645362181Sdim op = NULL; 646362181Sdim else 647362181Sdim created = TRUE; 648362181Sdim } 649362181Sdim 650289177Speter if (!op || !created) 651289177Speter { 652289177Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 653289177Speter _("Can't delete node at '%s'"), 654289177Speter relpath); 655289177Speter } 656289177Speter } 657289177Speter 658289177Speter op->kind = OP_DELETE; 659289177Speter op->children = NULL; 660289177Speter op->prop_mods = NULL; 661289177Speter 662289177Speter return SVN_NO_ERROR; 663289177Speter} 664289177Speter 665289177Spetersvn_error_t * 666362181Sdimsvn_client__mtcc_add_delete(const char *relpath, 667362181Sdim svn_client__mtcc_t *mtcc, 668362181Sdim apr_pool_t *scratch_pool) 669362181Sdim{ 670362181Sdim return svn_error_trace(mtcc_add_delete(relpath, FALSE, mtcc, scratch_pool)); 671362181Sdim} 672362181Sdim 673362181Sdimsvn_error_t * 674289177Spetersvn_client__mtcc_add_mkdir(const char *relpath, 675289177Speter svn_client__mtcc_t *mtcc, 676289177Speter apr_pool_t *scratch_pool) 677289177Speter{ 678289177Speter mtcc_op_t *op; 679289177Speter svn_boolean_t created; 680289177Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); 681289177Speter 682289177Speter SVN_ERR(mtcc_verify_create(mtcc, relpath, scratch_pool)); 683289177Speter 684289177Speter if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc)) 685289177Speter { 686289177Speter /* Turn the root of the operation in an MKDIR */ 687289177Speter mtcc->root_op->kind = OP_ADD_DIR; 688289177Speter 689289177Speter return SVN_NO_ERROR; 690289177Speter } 691289177Speter 692289177Speter SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, FALSE, 693289177Speter FALSE, mtcc->pool, scratch_pool)); 694289177Speter 695289177Speter if (!op || !created) 696289177Speter { 697289177Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 698289177Speter _("Can't create directory at '%s'"), 699289177Speter relpath); 700289177Speter } 701289177Speter 702289177Speter op->kind = OP_ADD_DIR; 703289177Speter 704289177Speter return SVN_NO_ERROR; 705289177Speter} 706289177Speter 707289177Spetersvn_error_t * 708289177Spetersvn_client__mtcc_add_move(const char *src_relpath, 709289177Speter const char *dst_relpath, 710289177Speter svn_client__mtcc_t *mtcc, 711289177Speter apr_pool_t *scratch_pool) 712289177Speter{ 713289177Speter const char *origin_relpath; 714289177Speter svn_revnum_t origin_rev; 715289177Speter 716289177Speter SVN_ERR(mtcc_get_origin(&origin_relpath, &origin_rev, 717289177Speter src_relpath, FALSE, mtcc, 718289177Speter scratch_pool, scratch_pool)); 719289177Speter 720289177Speter SVN_ERR(svn_client__mtcc_add_copy(src_relpath, mtcc->base_revision, 721289177Speter dst_relpath, mtcc, scratch_pool)); 722362181Sdim SVN_ERR(mtcc_add_delete(src_relpath, TRUE, mtcc, scratch_pool)); 723289177Speter 724289177Speter return SVN_NO_ERROR; 725289177Speter} 726289177Speter 727289177Speter/* Baton for mtcc_prop_getter */ 728289177Speterstruct mtcc_prop_get_baton 729289177Speter{ 730289177Speter svn_client__mtcc_t *mtcc; 731289177Speter const char *relpath; 732289177Speter svn_cancel_func_t cancel_func; 733289177Speter void *cancel_baton; 734289177Speter}; 735289177Speter 736289177Speter/* Implements svn_wc_canonicalize_svn_prop_get_file_t */ 737289177Speterstatic svn_error_t * 738289177Spetermtcc_prop_getter(const svn_string_t **mime_type, 739289177Speter svn_stream_t *stream, 740289177Speter void *baton, 741289177Speter apr_pool_t *pool) 742289177Speter{ 743289177Speter struct mtcc_prop_get_baton *mpgb = baton; 744289177Speter const char *origin_relpath; 745289177Speter svn_revnum_t origin_rev; 746289177Speter apr_hash_t *props = NULL; 747289177Speter 748289177Speter mtcc_op_t *op; 749289177Speter 750289177Speter if (mime_type) 751289177Speter *mime_type = NULL; 752289177Speter 753289177Speter /* Check if we have the information locally */ 754289177Speter SVN_ERR(mtcc_op_find(&op, NULL, mpgb->relpath, mpgb->mtcc->root_op, TRUE, 755289177Speter FALSE, FALSE, pool, pool)); 756289177Speter 757289177Speter if (op) 758289177Speter { 759289177Speter if (mime_type) 760289177Speter { 761289177Speter int i; 762289177Speter 763289177Speter for (i = 0; op->prop_mods && i < op->prop_mods->nelts; i++) 764289177Speter { 765289177Speter const svn_prop_t *mod = &APR_ARRAY_IDX(op->prop_mods, i, 766289177Speter svn_prop_t); 767289177Speter 768289177Speter if (! strcmp(mod->name, SVN_PROP_MIME_TYPE)) 769289177Speter { 770289177Speter *mime_type = svn_string_dup(mod->value, pool); 771289177Speter mime_type = NULL; 772362181Sdim break; 773289177Speter } 774289177Speter } 775289177Speter } 776289177Speter 777289177Speter if (stream && op->src_stream) 778289177Speter { 779289177Speter svn_stream_mark_t *mark; 780289177Speter svn_error_t *err; 781289177Speter 782289177Speter /* Is the source stream capable of being read multiple times? */ 783289177Speter err = svn_stream_mark(op->src_stream, &mark, pool); 784289177Speter 785289177Speter if (err && err->apr_err != SVN_ERR_STREAM_SEEK_NOT_SUPPORTED) 786289177Speter return svn_error_trace(err); 787289177Speter svn_error_clear(err); 788289177Speter 789289177Speter if (!err) 790289177Speter { 791289177Speter err = svn_stream_copy3(svn_stream_disown(op->src_stream, pool), 792289177Speter svn_stream_disown(stream, pool), 793289177Speter mpgb->cancel_func, mpgb->cancel_baton, 794289177Speter pool); 795289177Speter 796289177Speter SVN_ERR(svn_error_compose_create( 797289177Speter err, 798289177Speter svn_stream_seek(op->src_stream, mark))); 799289177Speter } 800289177Speter /* else: ### Create tempfile? */ 801289177Speter 802289177Speter stream = NULL; /* Stream is handled */ 803289177Speter } 804289177Speter } 805289177Speter 806289177Speter if (!stream && !mime_type) 807289177Speter return SVN_NO_ERROR; 808289177Speter 809289177Speter SVN_ERR(mtcc_get_origin(&origin_relpath, &origin_rev, mpgb->relpath, TRUE, 810289177Speter mpgb->mtcc, pool, pool)); 811289177Speter 812289177Speter if (!origin_relpath) 813289177Speter return SVN_NO_ERROR; /* Nothing to fetch at repository */ 814289177Speter 815289177Speter SVN_ERR(svn_ra_get_file(mpgb->mtcc->ra_session, origin_relpath, origin_rev, 816289177Speter stream, NULL, mime_type ? &props : NULL, pool)); 817289177Speter 818289177Speter if (mime_type && props) 819289177Speter *mime_type = svn_hash_gets(props, SVN_PROP_MIME_TYPE); 820289177Speter 821289177Speter return SVN_NO_ERROR; 822289177Speter} 823289177Speter 824289177Spetersvn_error_t * 825289177Spetersvn_client__mtcc_add_propset(const char *relpath, 826289177Speter const char *propname, 827289177Speter const svn_string_t *propval, 828289177Speter svn_boolean_t skip_checks, 829289177Speter svn_client__mtcc_t *mtcc, 830289177Speter apr_pool_t *scratch_pool) 831289177Speter{ 832289177Speter mtcc_op_t *op; 833289177Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); 834289177Speter 835289177Speter if (! svn_prop_name_is_valid(propname)) 836289177Speter return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL, 837289177Speter _("Bad property name: '%s'"), propname); 838289177Speter 839289177Speter if (svn_prop_is_known_svn_rev_prop(propname)) 840289177Speter return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL, 841289177Speter _("Revision property '%s' not allowed " 842289177Speter "in this context"), propname); 843289177Speter 844289177Speter if (svn_property_kind2(propname) == svn_prop_wc_kind) 845289177Speter return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL, 846289177Speter _("'%s' is a wcprop, thus not accessible " 847289177Speter "to clients"), propname); 848289177Speter 849289177Speter if (!skip_checks && svn_prop_needs_translation(propname)) 850289177Speter { 851289177Speter svn_string_t *translated_value; 852289177Speter SVN_ERR_W(svn_subst_translate_string2(&translated_value, NULL, 853289177Speter NULL, propval, 854289177Speter NULL, FALSE, 855289177Speter scratch_pool, scratch_pool), 856289177Speter _("Error normalizing property value")); 857289177Speter 858289177Speter propval = translated_value; 859289177Speter } 860289177Speter 861289177Speter if (propval && svn_prop_is_svn_prop(propname)) 862289177Speter { 863289177Speter struct mtcc_prop_get_baton mpbg; 864289177Speter svn_node_kind_t kind; 865289177Speter SVN_ERR(svn_client__mtcc_check_path(&kind, relpath, FALSE, mtcc, 866289177Speter scratch_pool)); 867289177Speter 868289177Speter mpbg.mtcc = mtcc; 869289177Speter mpbg.relpath = relpath; 870289177Speter mpbg.cancel_func = mtcc->ctx->cancel_func; 871289177Speter mpbg.cancel_baton = mtcc->ctx->cancel_baton; 872289177Speter 873289177Speter SVN_ERR(svn_wc_canonicalize_svn_prop(&propval, propname, propval, 874289177Speter relpath, kind, skip_checks, 875289177Speter mtcc_prop_getter, &mpbg, 876289177Speter scratch_pool)); 877289177Speter } 878289177Speter 879289177Speter if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc)) 880289177Speter { 881289177Speter svn_node_kind_t kind; 882289177Speter 883289177Speter /* Probing the node for an unmodified root will fix the node type to 884289177Speter a file if necessary */ 885289177Speter 886289177Speter SVN_ERR(svn_client__mtcc_check_path(&kind, relpath, FALSE, 887289177Speter mtcc, scratch_pool)); 888289177Speter 889289177Speter if (kind == svn_node_none) 890289177Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 891289177Speter _("Can't set properties at not existing '%s'"), 892289177Speter relpath); 893289177Speter 894289177Speter op = mtcc->root_op; 895289177Speter } 896289177Speter else 897289177Speter { 898289177Speter SVN_ERR(mtcc_op_find(&op, NULL, relpath, mtcc->root_op, TRUE, FALSE, 899289177Speter FALSE, mtcc->pool, scratch_pool)); 900289177Speter 901289177Speter if (!op) 902289177Speter { 903289177Speter svn_node_kind_t kind; 904289177Speter svn_boolean_t created; 905289177Speter 906289177Speter /* ### TODO: Check if this node is within a newly copied directory, 907289177Speter and update origin values accordingly */ 908289177Speter 909289177Speter SVN_ERR(svn_client__mtcc_check_path(&kind, relpath, FALSE, 910289177Speter mtcc, scratch_pool)); 911289177Speter 912289177Speter if (kind == svn_node_none) 913289177Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 914289177Speter _("Can't set properties at not existing '%s'"), 915289177Speter relpath); 916289177Speter 917289177Speter SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, TRUE, FALSE, 918289177Speter (kind != svn_node_dir), 919289177Speter mtcc->pool, scratch_pool)); 920289177Speter 921289177Speter SVN_ERR_ASSERT(op != NULL); 922289177Speter } 923289177Speter } 924289177Speter 925289177Speter if (!op->prop_mods) 926289177Speter op->prop_mods = apr_array_make(mtcc->pool, 4, sizeof(svn_prop_t)); 927289177Speter 928289177Speter { 929289177Speter svn_prop_t propchange; 930289177Speter propchange.name = apr_pstrdup(mtcc->pool, propname); 931289177Speter 932289177Speter if (propval) 933289177Speter propchange.value = svn_string_dup(propval, mtcc->pool); 934289177Speter else 935289177Speter propchange.value = NULL; 936289177Speter 937289177Speter APR_ARRAY_PUSH(op->prop_mods, svn_prop_t) = propchange; 938289177Speter } 939289177Speter 940289177Speter return SVN_NO_ERROR; 941289177Speter} 942289177Speter 943289177Spetersvn_error_t * 944289177Spetersvn_client__mtcc_add_update_file(const char *relpath, 945289177Speter svn_stream_t *src_stream, 946289177Speter const svn_checksum_t *src_checksum, 947289177Speter svn_stream_t *base_stream, 948289177Speter const svn_checksum_t *base_checksum, 949289177Speter svn_client__mtcc_t *mtcc, 950289177Speter apr_pool_t *scratch_pool) 951289177Speter{ 952289177Speter mtcc_op_t *op; 953289177Speter svn_boolean_t created; 954289177Speter svn_node_kind_t kind; 955289177Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath) && src_stream); 956289177Speter 957289177Speter SVN_ERR(svn_client__mtcc_check_path(&kind, relpath, FALSE, 958289177Speter mtcc, scratch_pool)); 959289177Speter 960289177Speter if (kind != svn_node_file) 961289177Speter return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL, 962289177Speter _("Can't update '%s' because it is not a file"), 963289177Speter relpath); 964289177Speter 965289177Speter SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, TRUE, FALSE, 966289177Speter TRUE, mtcc->pool, scratch_pool)); 967289177Speter 968289177Speter if (!op 969289177Speter || (op->kind != OP_OPEN_FILE && op->kind != OP_ADD_FILE) 970289177Speter || (op->src_stream != NULL)) 971289177Speter { 972289177Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 973289177Speter _("Can't update file at '%s'"), relpath); 974289177Speter } 975289177Speter 976289177Speter op->src_stream = src_stream; 977289177Speter op->src_checksum = src_checksum ? svn_checksum_dup(src_checksum, mtcc->pool) 978289177Speter : NULL; 979289177Speter 980289177Speter op->base_stream = base_stream; 981289177Speter op->base_checksum = base_checksum ? svn_checksum_dup(base_checksum, 982289177Speter mtcc->pool) 983289177Speter : NULL; 984289177Speter 985289177Speter return SVN_NO_ERROR; 986289177Speter} 987289177Speter 988289177Spetersvn_error_t * 989289177Spetersvn_client__mtcc_check_path(svn_node_kind_t *kind, 990289177Speter const char *relpath, 991289177Speter svn_boolean_t check_repository, 992289177Speter svn_client__mtcc_t *mtcc, 993289177Speter apr_pool_t *scratch_pool) 994289177Speter{ 995289177Speter const char *origin_relpath; 996289177Speter svn_revnum_t origin_rev; 997289177Speter mtcc_op_t *op; 998289177Speter 999289177Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath)); 1000289177Speter 1001289177Speter if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc) 1002289177Speter && !mtcc->root_op->performed_stat) 1003289177Speter { 1004289177Speter /* We know nothing about the root. Perhaps it is a file? */ 1005289177Speter SVN_ERR(svn_ra_check_path(mtcc->ra_session, "", mtcc->base_revision, 1006289177Speter kind, scratch_pool)); 1007289177Speter 1008289177Speter mtcc->root_op->performed_stat = TRUE; 1009289177Speter if (*kind == svn_node_file) 1010289177Speter { 1011289177Speter mtcc->root_op->kind = OP_OPEN_FILE; 1012289177Speter mtcc->root_op->children = NULL; 1013289177Speter } 1014289177Speter return SVN_NO_ERROR; 1015289177Speter } 1016289177Speter 1017289177Speter SVN_ERR(mtcc_op_find(&op, NULL, relpath, mtcc->root_op, TRUE, FALSE, 1018289177Speter FALSE, mtcc->pool, scratch_pool)); 1019289177Speter 1020289177Speter if (!op || (check_repository && !op->performed_stat)) 1021289177Speter { 1022289177Speter SVN_ERR(mtcc_get_origin(&origin_relpath, &origin_rev, 1023289177Speter relpath, TRUE, mtcc, 1024289177Speter scratch_pool, scratch_pool)); 1025289177Speter 1026289177Speter if (!origin_relpath) 1027289177Speter *kind = svn_node_none; 1028289177Speter else 1029289177Speter SVN_ERR(svn_ra_check_path(mtcc->ra_session, origin_relpath, 1030289177Speter origin_rev, kind, scratch_pool)); 1031289177Speter 1032289177Speter if (op && *kind == svn_node_dir) 1033289177Speter { 1034289177Speter if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR) 1035289177Speter op->performed_stat = TRUE; 1036289177Speter else if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE) 1037289177Speter return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL, 1038289177Speter _("Can't perform file operation " 1039289177Speter "on '%s' as it is not a file"), 1040289177Speter relpath); 1041289177Speter } 1042289177Speter else if (op && *kind == svn_node_file) 1043289177Speter { 1044289177Speter if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE) 1045289177Speter op->performed_stat = TRUE; 1046289177Speter else if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR) 1047289177Speter return svn_error_createf(SVN_ERR_FS_NOT_DIRECTORY, NULL, 1048289177Speter _("Can't perform directory operation " 1049289177Speter "on '%s' as it is not a directory"), 1050289177Speter relpath); 1051289177Speter } 1052289177Speter else if (op && (op->kind == OP_OPEN_DIR || op->kind == OP_OPEN_FILE)) 1053289177Speter { 1054289177Speter return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 1055289177Speter _("Can't open '%s' as it does not exist"), 1056289177Speter relpath); 1057289177Speter } 1058289177Speter 1059289177Speter return SVN_NO_ERROR; 1060289177Speter } 1061289177Speter 1062289177Speter /* op != NULL */ 1063289177Speter if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR) 1064289177Speter { 1065289177Speter *kind = svn_node_dir; 1066289177Speter return SVN_NO_ERROR; 1067289177Speter } 1068289177Speter else if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE) 1069289177Speter { 1070289177Speter *kind = svn_node_file; 1071289177Speter return SVN_NO_ERROR; 1072289177Speter } 1073289177Speter SVN_ERR_MALFUNCTION(); /* No other kinds defined as delete is filtered */ 1074289177Speter} 1075289177Speter 1076289177Speterstatic svn_error_t * 1077289177Spetercommit_properties(const svn_delta_editor_t *editor, 1078289177Speter const mtcc_op_t *op, 1079289177Speter void *node_baton, 1080289177Speter apr_pool_t *scratch_pool) 1081289177Speter{ 1082289177Speter int i; 1083289177Speter apr_pool_t *iterpool; 1084289177Speter 1085289177Speter if (!op->prop_mods || op->prop_mods->nelts == 0) 1086289177Speter return SVN_NO_ERROR; 1087289177Speter 1088289177Speter iterpool = svn_pool_create(scratch_pool); 1089289177Speter for (i = 0; i < op->prop_mods->nelts; i++) 1090289177Speter { 1091289177Speter const svn_prop_t *mod = &APR_ARRAY_IDX(op->prop_mods, i, svn_prop_t); 1092289177Speter 1093289177Speter svn_pool_clear(iterpool); 1094289177Speter 1095289177Speter if (op->kind == OP_ADD_DIR || op->kind == OP_OPEN_DIR) 1096289177Speter SVN_ERR(editor->change_dir_prop(node_baton, mod->name, mod->value, 1097289177Speter iterpool)); 1098289177Speter else if (op->kind == OP_ADD_FILE || op->kind == OP_OPEN_FILE) 1099289177Speter SVN_ERR(editor->change_file_prop(node_baton, mod->name, mod->value, 1100289177Speter iterpool)); 1101289177Speter } 1102289177Speter 1103289177Speter svn_pool_destroy(iterpool); 1104289177Speter return SVN_NO_ERROR; 1105289177Speter} 1106289177Speter 1107289177Speter/* Handles updating a file to a delta editor and then closes it */ 1108289177Speterstatic svn_error_t * 1109289177Spetercommit_file(const svn_delta_editor_t *editor, 1110289177Speter mtcc_op_t *op, 1111289177Speter void *file_baton, 1112289177Speter const char *session_url, 1113289177Speter const char *relpath, 1114289177Speter svn_client_ctx_t *ctx, 1115289177Speter apr_pool_t *scratch_pool) 1116289177Speter{ 1117289177Speter const char *text_checksum = NULL; 1118289177Speter svn_checksum_t *src_checksum = op->src_checksum; 1119289177Speter SVN_ERR(commit_properties(editor, op, file_baton, scratch_pool)); 1120289177Speter 1121289177Speter if (op->src_stream) 1122289177Speter { 1123289177Speter const char *base_checksum = NULL; 1124289177Speter apr_pool_t *txdelta_pool = scratch_pool; 1125289177Speter svn_txdelta_window_handler_t window_handler; 1126289177Speter void *handler_baton; 1127289177Speter svn_stream_t *src_stream = op->src_stream; 1128289177Speter 1129289177Speter if (op->base_checksum && op->base_checksum->kind == svn_checksum_md5) 1130289177Speter base_checksum = svn_checksum_to_cstring(op->base_checksum, scratch_pool); 1131289177Speter 1132289177Speter /* ### TODO: Future enhancement: Allocate in special pool and send 1133289177Speter files after the true edit operation, like a wc commit */ 1134289177Speter SVN_ERR(editor->apply_textdelta(file_baton, base_checksum, txdelta_pool, 1135289177Speter &window_handler, &handler_baton)); 1136289177Speter 1137289177Speter if (ctx->notify_func2) 1138289177Speter { 1139289177Speter svn_wc_notify_t *notify; 1140289177Speter 1141289177Speter notify = svn_wc_create_notify_url( 1142289177Speter svn_path_url_add_component2(session_url, relpath, 1143289177Speter scratch_pool), 1144289177Speter svn_wc_notify_commit_postfix_txdelta, 1145289177Speter scratch_pool); 1146289177Speter 1147289177Speter notify->path = relpath; 1148289177Speter notify->kind = svn_node_file; 1149289177Speter 1150289177Speter ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool); 1151289177Speter } 1152289177Speter 1153289177Speter if (window_handler != svn_delta_noop_window_handler) 1154289177Speter { 1155289177Speter if (!src_checksum || src_checksum->kind != svn_checksum_md5) 1156289177Speter src_stream = svn_stream_checksummed2(src_stream, &src_checksum, NULL, 1157289177Speter svn_checksum_md5, 1158289177Speter TRUE, scratch_pool); 1159289177Speter 1160289177Speter if (!op->base_stream) 1161289177Speter SVN_ERR(svn_txdelta_send_stream(src_stream, 1162289177Speter window_handler, handler_baton, NULL, 1163289177Speter scratch_pool)); 1164289177Speter else 1165289177Speter SVN_ERR(svn_txdelta_run(op->base_stream, src_stream, 1166289177Speter window_handler, handler_baton, 1167289177Speter svn_checksum_md5, NULL, 1168289177Speter ctx->cancel_func, ctx->cancel_baton, 1169289177Speter scratch_pool, scratch_pool)); 1170289177Speter } 1171289177Speter 1172289177Speter SVN_ERR(svn_stream_close(src_stream)); 1173289177Speter if (op->base_stream) 1174289177Speter SVN_ERR(svn_stream_close(op->base_stream)); 1175289177Speter } 1176289177Speter 1177289177Speter if (src_checksum && src_checksum->kind == svn_checksum_md5) 1178289177Speter text_checksum = svn_checksum_to_cstring(src_checksum, scratch_pool); 1179289177Speter 1180289177Speter return svn_error_trace(editor->close_file(file_baton, text_checksum, 1181289177Speter scratch_pool)); 1182289177Speter} 1183289177Speter 1184289177Speter/* Handles updating a directory to a delta editor and then closes it */ 1185289177Speterstatic svn_error_t * 1186289177Spetercommit_directory(const svn_delta_editor_t *editor, 1187289177Speter mtcc_op_t *op, 1188289177Speter const char *relpath, 1189289177Speter svn_revnum_t base_rev, 1190289177Speter void *dir_baton, 1191289177Speter const char *session_url, 1192289177Speter svn_client_ctx_t *ctx, 1193289177Speter apr_pool_t *scratch_pool) 1194289177Speter{ 1195289177Speter SVN_ERR(commit_properties(editor, op, dir_baton, scratch_pool)); 1196289177Speter 1197289177Speter if (op->children && op->children->nelts > 0) 1198289177Speter { 1199289177Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1200289177Speter int i; 1201289177Speter 1202289177Speter for (i = 0; i < op->children->nelts; i++) 1203289177Speter { 1204289177Speter mtcc_op_t *cop; 1205289177Speter const char * child_relpath; 1206289177Speter void *child_baton; 1207289177Speter 1208289177Speter cop = APR_ARRAY_IDX(op->children, i, mtcc_op_t *); 1209289177Speter 1210289177Speter svn_pool_clear(iterpool); 1211289177Speter 1212289177Speter if (ctx->cancel_func) 1213289177Speter SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); 1214289177Speter 1215289177Speter child_relpath = svn_relpath_join(relpath, cop->name, iterpool); 1216289177Speter 1217289177Speter switch (cop->kind) 1218289177Speter { 1219289177Speter case OP_DELETE: 1220289177Speter SVN_ERR(editor->delete_entry(child_relpath, base_rev, 1221289177Speter dir_baton, iterpool)); 1222289177Speter break; 1223289177Speter 1224289177Speter case OP_ADD_DIR: 1225289177Speter SVN_ERR(editor->add_directory(child_relpath, dir_baton, 1226289177Speter cop->src_relpath 1227289177Speter ? svn_path_url_add_component2( 1228289177Speter session_url, 1229289177Speter cop->src_relpath, 1230289177Speter iterpool) 1231289177Speter : NULL, 1232289177Speter cop->src_rev, 1233289177Speter iterpool, &child_baton)); 1234289177Speter SVN_ERR(commit_directory(editor, cop, child_relpath, 1235289177Speter SVN_INVALID_REVNUM, child_baton, 1236289177Speter session_url, ctx, iterpool)); 1237289177Speter break; 1238289177Speter case OP_OPEN_DIR: 1239289177Speter SVN_ERR(editor->open_directory(child_relpath, dir_baton, 1240289177Speter base_rev, iterpool, &child_baton)); 1241289177Speter SVN_ERR(commit_directory(editor, cop, child_relpath, 1242289177Speter base_rev, child_baton, 1243289177Speter session_url, ctx, iterpool)); 1244289177Speter break; 1245289177Speter 1246289177Speter case OP_ADD_FILE: 1247289177Speter SVN_ERR(editor->add_file(child_relpath, dir_baton, 1248289177Speter cop->src_relpath 1249289177Speter ? svn_path_url_add_component2( 1250289177Speter session_url, 1251289177Speter cop->src_relpath, 1252289177Speter iterpool) 1253289177Speter : NULL, 1254289177Speter cop->src_rev, 1255289177Speter iterpool, &child_baton)); 1256289177Speter SVN_ERR(commit_file(editor, cop, child_baton, 1257289177Speter session_url, child_relpath, ctx, iterpool)); 1258289177Speter break; 1259289177Speter case OP_OPEN_FILE: 1260289177Speter SVN_ERR(editor->open_file(child_relpath, dir_baton, base_rev, 1261289177Speter iterpool, &child_baton)); 1262289177Speter SVN_ERR(commit_file(editor, cop, child_baton, 1263289177Speter session_url, child_relpath, ctx, iterpool)); 1264289177Speter break; 1265289177Speter 1266289177Speter default: 1267289177Speter SVN_ERR_MALFUNCTION(); 1268289177Speter } 1269289177Speter } 1270289177Speter } 1271289177Speter 1272289177Speter return svn_error_trace(editor->close_directory(dir_baton, scratch_pool)); 1273289177Speter} 1274289177Speter 1275289177Speter 1276289177Speter/* Helper function to recursively create svn_client_commit_item3_t items 1277289177Speter to provide to the log message callback */ 1278289177Speterstatic svn_error_t * 1279289177Speteradd_commit_items(mtcc_op_t *op, 1280289177Speter const char *session_url, 1281289177Speter const char *url, 1282289177Speter apr_array_header_t *commit_items, 1283289177Speter apr_pool_t *result_pool, 1284289177Speter apr_pool_t *scratch_pool) 1285289177Speter{ 1286289177Speter if ((op->kind != OP_OPEN_DIR && op->kind != OP_OPEN_FILE) 1287289177Speter || (op->prop_mods && op->prop_mods->nelts) 1288289177Speter || (op->src_stream)) 1289289177Speter { 1290289177Speter svn_client_commit_item3_t *item; 1291289177Speter 1292289177Speter item = svn_client_commit_item3_create(result_pool); 1293289177Speter 1294289177Speter item->path = NULL; 1295289177Speter if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR) 1296289177Speter item->kind = svn_node_dir; 1297289177Speter else if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE) 1298289177Speter item->kind = svn_node_file; 1299289177Speter else 1300289177Speter item->kind = svn_node_unknown; 1301289177Speter 1302289177Speter item->url = apr_pstrdup(result_pool, url); 1303289177Speter item->session_relpath = svn_uri_skip_ancestor(session_url, item->url, 1304289177Speter result_pool); 1305289177Speter 1306289177Speter if (op->src_relpath) 1307289177Speter { 1308289177Speter item->copyfrom_url = svn_path_url_add_component2(session_url, 1309289177Speter op->src_relpath, 1310289177Speter result_pool); 1311289177Speter item->copyfrom_rev = op->src_rev; 1312289177Speter item->state_flags |= SVN_CLIENT_COMMIT_ITEM_IS_COPY; 1313289177Speter } 1314289177Speter else 1315289177Speter item->copyfrom_rev = SVN_INVALID_REVNUM; 1316289177Speter 1317289177Speter if (op->kind == OP_ADD_DIR || op->kind == OP_ADD_FILE) 1318289177Speter item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD; 1319289177Speter else if (op->kind == OP_DELETE) 1320289177Speter item->state_flags = SVN_CLIENT_COMMIT_ITEM_DELETE; 1321289177Speter /* else item->state_flags = 0; */ 1322289177Speter 1323289177Speter if (op->prop_mods && op->prop_mods->nelts) 1324289177Speter item->state_flags |= SVN_CLIENT_COMMIT_ITEM_PROP_MODS; 1325289177Speter 1326289177Speter if (op->src_stream) 1327289177Speter item->state_flags |= SVN_CLIENT_COMMIT_ITEM_TEXT_MODS; 1328289177Speter 1329289177Speter APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; 1330289177Speter } 1331289177Speter 1332289177Speter if (op->children && op->children->nelts) 1333289177Speter { 1334289177Speter int i; 1335289177Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1336289177Speter 1337289177Speter for (i = 0; i < op->children->nelts; i++) 1338289177Speter { 1339289177Speter mtcc_op_t *cop; 1340289177Speter const char * child_url; 1341289177Speter 1342289177Speter cop = APR_ARRAY_IDX(op->children, i, mtcc_op_t *); 1343289177Speter 1344289177Speter svn_pool_clear(iterpool); 1345289177Speter 1346289177Speter child_url = svn_path_url_add_component2(url, cop->name, iterpool); 1347289177Speter 1348289177Speter SVN_ERR(add_commit_items(cop, session_url, child_url, commit_items, 1349289177Speter result_pool, iterpool)); 1350289177Speter } 1351289177Speter 1352289177Speter svn_pool_destroy(iterpool); 1353289177Speter } 1354289177Speter 1355289177Speter return SVN_NO_ERROR; 1356289177Speter} 1357289177Speter 1358289177Spetersvn_error_t * 1359289177Spetersvn_client__mtcc_commit(apr_hash_t *revprop_table, 1360289177Speter svn_commit_callback2_t commit_callback, 1361289177Speter void *commit_baton, 1362289177Speter svn_client__mtcc_t *mtcc, 1363289177Speter apr_pool_t *scratch_pool) 1364289177Speter{ 1365289177Speter const svn_delta_editor_t *editor; 1366289177Speter void *edit_baton; 1367289177Speter void *root_baton; 1368289177Speter apr_hash_t *commit_revprops; 1369289177Speter svn_node_kind_t kind; 1370289177Speter svn_error_t *err; 1371289177Speter const char *session_url; 1372289177Speter const char *log_msg; 1373289177Speter 1374289177Speter if (MTCC_UNMODIFIED(mtcc)) 1375289177Speter { 1376289177Speter /* No changes -> no revision. Easy out */ 1377289177Speter svn_pool_destroy(mtcc->pool); 1378289177Speter return SVN_NO_ERROR; 1379289177Speter } 1380289177Speter 1381289177Speter SVN_ERR(svn_ra_get_session_url(mtcc->ra_session, &session_url, scratch_pool)); 1382289177Speter 1383289177Speter if (mtcc->root_op->kind != OP_OPEN_DIR) 1384289177Speter { 1385289177Speter const char *name; 1386289177Speter 1387289177Speter svn_uri_split(&session_url, &name, session_url, scratch_pool); 1388289177Speter 1389289177Speter if (*name) 1390289177Speter { 1391289177Speter SVN_ERR(mtcc_reparent(session_url, mtcc, scratch_pool)); 1392289177Speter 1393289177Speter SVN_ERR(svn_ra_reparent(mtcc->ra_session, session_url, scratch_pool)); 1394289177Speter } 1395289177Speter } 1396289177Speter 1397289177Speter /* Create new commit items and add them to the array. */ 1398289177Speter if (SVN_CLIENT__HAS_LOG_MSG_FUNC(mtcc->ctx)) 1399289177Speter { 1400289177Speter svn_client_commit_item3_t *item; 1401289177Speter const char *tmp_file; 1402289177Speter apr_array_header_t *commit_items 1403289177Speter = apr_array_make(scratch_pool, 32, sizeof(item)); 1404289177Speter 1405289177Speter SVN_ERR(add_commit_items(mtcc->root_op, session_url, session_url, 1406289177Speter commit_items, scratch_pool, scratch_pool)); 1407289177Speter 1408289177Speter SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items, 1409289177Speter mtcc->ctx, scratch_pool)); 1410289177Speter 1411289177Speter if (! log_msg) 1412289177Speter return SVN_NO_ERROR; 1413289177Speter } 1414289177Speter else 1415289177Speter log_msg = ""; 1416289177Speter 1417289177Speter SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table, 1418289177Speter log_msg, mtcc->ctx, scratch_pool)); 1419289177Speter 1420289177Speter /* Ugly corner case: The ra session might have died while we were waiting 1421289177Speter for the callback */ 1422289177Speter 1423289177Speter err = svn_ra_check_path(mtcc->ra_session, "", mtcc->base_revision, &kind, 1424289177Speter scratch_pool); 1425289177Speter 1426289177Speter if (err) 1427289177Speter { 1428289177Speter svn_error_t *err2 = svn_client_open_ra_session2(&mtcc->ra_session, 1429289177Speter session_url, 1430289177Speter NULL, mtcc->ctx, 1431289177Speter mtcc->pool, 1432289177Speter scratch_pool); 1433289177Speter 1434289177Speter if (err2) 1435289177Speter { 1436289177Speter svn_pool_destroy(mtcc->pool); 1437289177Speter return svn_error_trace(svn_error_compose_create(err, err2)); 1438289177Speter } 1439289177Speter svn_error_clear(err); 1440289177Speter 1441289177Speter SVN_ERR(svn_ra_check_path(mtcc->ra_session, "", 1442289177Speter mtcc->base_revision, &kind, scratch_pool)); 1443289177Speter } 1444289177Speter 1445289177Speter if (kind != svn_node_dir) 1446289177Speter return svn_error_createf(SVN_ERR_FS_NOT_DIRECTORY, NULL, 1447289177Speter _("Can't commit to '%s' because it " 1448289177Speter "is not a directory"), 1449289177Speter session_url); 1450289177Speter 1451289177Speter /* Beware that the editor object must not live longer than the MTCC. 1452289177Speter Otherwise, txn objects etc. in EDITOR may live longer than their 1453289177Speter respective FS objects. So, we can't use SCRATCH_POOL here. */ 1454289177Speter SVN_ERR(svn_ra_get_commit_editor3(mtcc->ra_session, &editor, &edit_baton, 1455289177Speter commit_revprops, 1456289177Speter commit_callback, commit_baton, 1457289177Speter NULL /* lock_tokens */, 1458289177Speter FALSE /* keep_locks */, 1459289177Speter mtcc->pool)); 1460289177Speter 1461289177Speter err = editor->open_root(edit_baton, mtcc->base_revision, scratch_pool, &root_baton); 1462289177Speter 1463289177Speter if (!err) 1464289177Speter err = commit_directory(editor, mtcc->root_op, "", mtcc->base_revision, 1465289177Speter root_baton, session_url, mtcc->ctx, scratch_pool); 1466289177Speter 1467289177Speter if (!err) 1468289177Speter { 1469289177Speter if (mtcc->ctx->notify_func2) 1470289177Speter { 1471289177Speter svn_wc_notify_t *notify; 1472289177Speter notify = svn_wc_create_notify_url(session_url, 1473289177Speter svn_wc_notify_commit_finalizing, 1474289177Speter scratch_pool); 1475289177Speter mtcc->ctx->notify_func2(mtcc->ctx->notify_baton2, notify, 1476289177Speter scratch_pool); 1477289177Speter } 1478289177Speter SVN_ERR(editor->close_edit(edit_baton, scratch_pool)); 1479289177Speter } 1480289177Speter else 1481289177Speter err = svn_error_compose_create(err, 1482289177Speter editor->abort_edit(edit_baton, scratch_pool)); 1483289177Speter 1484289177Speter svn_pool_destroy(mtcc->pool); 1485289177Speter 1486289177Speter return svn_error_trace(err); 1487289177Speter} 1488