add.c revision 302408
1/* 2 * add.c: wrappers around wc add/mkdir functionality. 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24/* ==================================================================== */ 25 26 27 28/*** Includes. ***/ 29 30#include <string.h> 31#include <apr_lib.h> 32#include <apr_fnmatch.h> 33#include "svn_wc.h" 34#include "svn_client.h" 35#include "svn_string.h" 36#include "svn_pools.h" 37#include "svn_error.h" 38#include "svn_dirent_uri.h" 39#include "svn_path.h" 40#include "svn_io.h" 41#include "svn_config.h" 42#include "svn_props.h" 43#include "svn_hash.h" 44#include "svn_sorts.h" 45#include "client.h" 46#include "svn_ctype.h" 47 48#include "private/svn_client_private.h" 49#include "private/svn_wc_private.h" 50#include "private/svn_ra_private.h" 51#include "private/svn_sorts_private.h" 52#include "private/svn_magic.h" 53 54#include "svn_private_config.h" 55 56 57 58/*** Code. ***/ 59 60/* Remove leading and trailing white space from a C string, in place. */ 61static void 62trim_string(char **pstr) 63{ 64 char *str = *pstr; 65 size_t i; 66 67 while (svn_ctype_isspace(*str)) 68 str++; 69 *pstr = str; 70 i = strlen(str); 71 while ((i > 0) && svn_ctype_isspace(str[i-1])) 72 i--; 73 str[i] = '\0'; 74} 75 76/* Remove leading and trailing single- or double quotes from a C string, 77 * in place. */ 78static void 79unquote_string(char **pstr) 80{ 81 char *str = *pstr; 82 size_t i = strlen(str); 83 84 if (i > 0 && ((*str == '"' && str[i - 1] == '"') || 85 (*str == '\'' && str[i - 1] == '\''))) 86 { 87 str[i - 1] = '\0'; 88 str++; 89 } 90 *pstr = str; 91} 92 93/* Split PROPERTY and store each individual value in PROPS. 94 Allocates from POOL. */ 95static void 96split_props(apr_array_header_t **props, 97 const char *property, 98 apr_pool_t *pool) 99{ 100 apr_array_header_t *temp_props; 101 char *new_prop; 102 int i = 0; 103 int j = 0; 104 105 temp_props = apr_array_make(pool, 4, sizeof(char *)); 106 new_prop = apr_palloc(pool, strlen(property)+1); 107 108 for (i = 0; property[i] != '\0'; i++) 109 { 110 if (property[i] != ';') 111 { 112 new_prop[j] = property[i]; 113 j++; 114 } 115 else if (property[i] == ';') 116 { 117 /* ";;" becomes ";" */ 118 if (property[i+1] == ';') 119 { 120 new_prop[j] = ';'; 121 j++; 122 i++; 123 } 124 else 125 { 126 new_prop[j] = '\0'; 127 APR_ARRAY_PUSH(temp_props, char *) = new_prop; 128 new_prop += j + 1; 129 j = 0; 130 } 131 } 132 } 133 new_prop[j] = '\0'; 134 APR_ARRAY_PUSH(temp_props, char *) = new_prop; 135 *props = temp_props; 136} 137 138/* PROPVALS is a hash mapping char * property names to const char * property 139 values. PROPERTIES can be empty but not NULL. 140 141 If FILENAME doesn't match the filename pattern PATTERN case insensitively, 142 the do nothing. Otherwise for each 'name':'value' pair in PROPVALS, add 143 a new entry mappying 'name' to a svn_string_t * wrapping the 'value' in 144 PROPERTIES. The svn_string_t is allocated in the pool used to allocate 145 PROPERTIES, but the char *'s from PROPVALS are re-used in PROPERTIES. 146 If PROPVALS contains a 'svn:mime-type' mapping, then set *MIMETYPE to 147 the mapped value. Likewise if PROPVALS contains a mapping for 148 svn:executable, then set *HAVE_EXECUTABLE to TRUE. 149 150 Use SCRATCH_POOL for temporary allocations. 151*/ 152static void 153get_auto_props_for_pattern(apr_hash_t *properties, 154 const char **mimetype, 155 svn_boolean_t *have_executable, 156 const char *filename, 157 const char *pattern, 158 apr_hash_t *propvals, 159 apr_pool_t *scratch_pool) 160{ 161 apr_hash_index_t *hi; 162 163 /* check if filename matches and return if it doesn't */ 164 if (apr_fnmatch(pattern, filename, 165 APR_FNM_CASE_BLIND) == APR_FNM_NOMATCH) 166 return; 167 168 for (hi = apr_hash_first(scratch_pool, propvals); 169 hi != NULL; 170 hi = apr_hash_next(hi)) 171 { 172 const char *propname = apr_hash_this_key(hi); 173 const char *propval = apr_hash_this_val(hi); 174 svn_string_t *propval_str = 175 svn_string_create_empty(apr_hash_pool_get(properties)); 176 177 propval_str->data = propval; 178 propval_str->len = strlen(propval); 179 180 svn_hash_sets(properties, propname, propval_str); 181 if (strcmp(propname, SVN_PROP_MIME_TYPE) == 0) 182 *mimetype = propval; 183 else if (strcmp(propname, SVN_PROP_EXECUTABLE) == 0) 184 *have_executable = TRUE; 185 } 186} 187 188svn_error_t * 189svn_client__get_paths_auto_props(apr_hash_t **properties, 190 const char **mimetype, 191 const char *path, 192 svn_magic__cookie_t *magic_cookie, 193 apr_hash_t *autoprops, 194 svn_client_ctx_t *ctx, 195 apr_pool_t *result_pool, 196 apr_pool_t *scratch_pool) 197{ 198 apr_hash_index_t *hi; 199 svn_boolean_t have_executable = FALSE; 200 201 *properties = apr_hash_make(result_pool); 202 *mimetype = NULL; 203 204 if (autoprops) 205 { 206 for (hi = apr_hash_first(scratch_pool, autoprops); 207 hi != NULL; 208 hi = apr_hash_next(hi)) 209 { 210 const char *pattern = apr_hash_this_key(hi); 211 apr_hash_t *propvals = apr_hash_this_val(hi); 212 213 get_auto_props_for_pattern(*properties, mimetype, &have_executable, 214 svn_dirent_basename(path, scratch_pool), 215 pattern, propvals, scratch_pool); 216 } 217 } 218 219 /* if mimetype has not been set check the file */ 220 if (! *mimetype) 221 { 222 SVN_ERR(svn_io_detect_mimetype2(mimetype, path, ctx->mimetypes_map, 223 result_pool)); 224 225 /* If we got no mime-type, or if it is "application/octet-stream", 226 * try to get the mime-type from libmagic. */ 227 if (magic_cookie && 228 (!*mimetype || 229 strcmp(*mimetype, "application/octet-stream") == 0)) 230 { 231 const char *magic_mimetype; 232 233 /* Since libmagic usually treats UTF-16 files as "text/plain", 234 * svn_magic__detect_binary_mimetype() will return NULL for such 235 * files. This is fine for now since we currently don't support 236 * UTF-16-encoded text files (issue #2194). 237 * Once we do support UTF-16 this code path will fail to detect 238 * them as text unless the svn_io_detect_mimetype2() call above 239 * returns "text/plain" for them. */ 240 SVN_ERR(svn_magic__detect_binary_mimetype(&magic_mimetype, 241 path, magic_cookie, 242 result_pool, 243 scratch_pool)); 244 if (magic_mimetype) 245 *mimetype = magic_mimetype; 246 } 247 248 if (*mimetype) 249 apr_hash_set(*properties, SVN_PROP_MIME_TYPE, 250 strlen(SVN_PROP_MIME_TYPE), 251 svn_string_create(*mimetype, result_pool)); 252 } 253 254 /* if executable has not been set check the file */ 255 if (! have_executable) 256 { 257 svn_boolean_t executable = FALSE; 258 SVN_ERR(svn_io_is_file_executable(&executable, path, scratch_pool)); 259 if (executable) 260 apr_hash_set(*properties, SVN_PROP_EXECUTABLE, 261 strlen(SVN_PROP_EXECUTABLE), 262 svn_string_create_empty(result_pool)); 263 } 264 265 return SVN_NO_ERROR; 266} 267 268/* Only call this if the on-disk node kind is a file. */ 269static svn_error_t * 270add_file(const char *local_abspath, 271 svn_magic__cookie_t *magic_cookie, 272 apr_hash_t *autoprops, 273 svn_boolean_t no_autoprops, 274 svn_client_ctx_t *ctx, 275 apr_pool_t *pool) 276{ 277 apr_hash_t *properties; 278 const char *mimetype; 279 svn_node_kind_t kind; 280 svn_boolean_t is_special; 281 282 /* Check to see if this is a special file. */ 283 SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special, pool)); 284 285 /* Determine the properties that the file should have */ 286 if (is_special) 287 { 288 mimetype = NULL; 289 properties = apr_hash_make(pool); 290 svn_hash_sets(properties, SVN_PROP_SPECIAL, 291 svn_string_create(SVN_PROP_BOOLEAN_TRUE, pool)); 292 } 293 else 294 { 295 apr_hash_t *file_autoprops = NULL; 296 297 /* Get automatic properties */ 298 /* If we are setting autoprops grab the inherited svn:auto-props and 299 config file auto-props for this file if we haven't already got them 300 when iterating over the file's unversioned parents. */ 301 if (!no_autoprops) 302 { 303 if (autoprops == NULL) 304 SVN_ERR(svn_client__get_all_auto_props( 305 &file_autoprops, svn_dirent_dirname(local_abspath,pool), 306 ctx, pool, pool)); 307 else 308 file_autoprops = autoprops; 309 } 310 311 /* This may fail on write-only files: 312 we open them to estimate file type. */ 313 SVN_ERR(svn_client__get_paths_auto_props(&properties, &mimetype, 314 local_abspath, magic_cookie, 315 file_autoprops, ctx, pool, 316 pool)); 317 } 318 319 /* Add the file */ 320 SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, local_abspath, properties, 321 FALSE /* skip checks */, 322 ctx->notify_func2, ctx->notify_baton2, pool)); 323 324 return SVN_NO_ERROR; 325} 326 327/* Schedule directory DIR_ABSPATH, and some of the tree under it, for 328 * addition. DEPTH is the depth at this point in the descent (it may 329 * be changed for recursive calls). 330 * 331 * If DIR_ABSPATH (or any item below DIR_ABSPATH) is already scheduled for 332 * addition, add will fail and return an error unless FORCE is TRUE. 333 * 334 * Use MAGIC_COOKIE (which may be NULL) to detect the mime-type of files 335 * if necessary. 336 * 337 * If not NULL, CONFIG_AUTOPROPS is a hash representing the config file and 338 * svn:auto-props autoprops which apply to DIR_ABSPATH. It maps 339 * const char * file patterns to another hash which maps const char * 340 * property names to const char *property values. If CONFIG_AUTOPROPS is 341 * NULL and the config file and svn:auto-props autoprops are required by this 342 * function, then such will be obtained. 343 * 344 * If IGNORES is not NULL, then it is an array of const char * ignore patterns 345 * that apply to any children of DIR_ABSPATH. If REFRESH_IGNORES is TRUE, then 346 * the passed in value of IGNORES (if any) is itself ignored and this function 347 * will gather all ignore patterns applicable to DIR_ABSPATH itself (allocated in 348 * RESULT_POOL). Any recursive calls to this function get the refreshed ignore 349 * patterns. If IGNORES is NULL and REFRESH_IGNORES is FALSE, then all children of DIR_ABSPATH 350 * are unconditionally added. 351 * 352 * If CTX->CANCEL_FUNC is non-null, call it with CTX->CANCEL_BATON to allow 353 * the user to cancel the operation. 354 * 355 * Use SCRATCH_POOL for temporary allocations. 356 */ 357static svn_error_t * 358add_dir_recursive(const char *dir_abspath, 359 svn_depth_t depth, 360 svn_boolean_t force, 361 svn_boolean_t no_autoprops, 362 svn_magic__cookie_t *magic_cookie, 363 apr_hash_t *config_autoprops, 364 svn_boolean_t refresh_ignores, 365 apr_array_header_t *ignores, 366 svn_client_ctx_t *ctx, 367 apr_pool_t *result_pool, 368 apr_pool_t *scratch_pool) 369{ 370 svn_error_t *err; 371 apr_pool_t *iterpool; 372 apr_hash_t *dirents; 373 apr_hash_index_t *hi; 374 svn_boolean_t entry_exists = FALSE; 375 376 /* Check cancellation; note that this catches recursive calls too. */ 377 if (ctx->cancel_func) 378 SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); 379 380 iterpool = svn_pool_create(scratch_pool); 381 382 /* Add this directory to revision control. */ 383 err = svn_wc_add_from_disk3(ctx->wc_ctx, dir_abspath, NULL /*props*/, 384 FALSE /* skip checks */, 385 ctx->notify_func2, ctx->notify_baton2, 386 iterpool); 387 if (err) 388 { 389 if (err->apr_err == SVN_ERR_ENTRY_EXISTS && force) 390 { 391 svn_error_clear(err); 392 entry_exists = TRUE; 393 } 394 else if (err) 395 { 396 return svn_error_trace(err); 397 } 398 } 399 400 /* Fetch ignores after adding to handle ignores on the directory itself 401 and ancestors via the single db optimization in libsvn_wc */ 402 if (refresh_ignores) 403 SVN_ERR(svn_wc_get_ignores2(&ignores, ctx->wc_ctx, dir_abspath, 404 ctx->config, result_pool, iterpool)); 405 406 /* If DIR_ABSPATH is the root of an unversioned subtree then get the 407 following "autoprops": 408 409 1) Explicit and inherited svn:auto-props properties on 410 DIR_ABSPATH 411 2) auto-props from the CTX->CONFIG hash 412 413 Since this set of autoprops applies to all unversioned children of 414 DIR_ABSPATH, we will pass these along to any recursive calls to 415 add_dir_recursive() and calls to add_file() below. Thus sparing 416 these callees from looking up the same information. */ 417 if (!entry_exists && config_autoprops == NULL) 418 { 419 SVN_ERR(svn_client__get_all_auto_props(&config_autoprops, dir_abspath, 420 ctx, scratch_pool, iterpool)); 421 } 422 423 SVN_ERR(svn_io_get_dirents3(&dirents, dir_abspath, TRUE, scratch_pool, 424 iterpool)); 425 426 /* Read the directory entries one by one and add those things to 427 version control. */ 428 for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi)) 429 { 430 const char *name = apr_hash_this_key(hi); 431 svn_io_dirent2_t *dirent = apr_hash_this_val(hi); 432 const char *abspath; 433 434 svn_pool_clear(iterpool); 435 436 /* Check cancellation so you can cancel during an 437 * add of a directory with lots of files. */ 438 if (ctx->cancel_func) 439 SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); 440 441 /* Skip over SVN admin directories. */ 442 if (svn_wc_is_adm_dir(name, iterpool)) 443 continue; 444 445 if (ignores 446 && svn_wc_match_ignore_list(name, ignores, iterpool)) 447 continue; 448 449 /* Construct the full path of the entry. */ 450 abspath = svn_dirent_join(dir_abspath, name, iterpool); 451 452 /* Recurse on directories; add files; ignore the rest. */ 453 if (dirent->kind == svn_node_dir && depth >= svn_depth_immediates) 454 { 455 svn_depth_t depth_below_here = depth; 456 if (depth == svn_depth_immediates) 457 depth_below_here = svn_depth_empty; 458 459 /* When DIR_ABSPATH is the root of an unversioned subtree then 460 it and all of its children have the same set of ignores. So 461 save any recursive calls the extra work of finding the same 462 set of ignores. */ 463 if (refresh_ignores && !entry_exists) 464 refresh_ignores = FALSE; 465 466 SVN_ERR(add_dir_recursive(abspath, depth_below_here, 467 force, no_autoprops, 468 magic_cookie, config_autoprops, 469 refresh_ignores, ignores, ctx, 470 result_pool, iterpool)); 471 } 472 else if ((dirent->kind == svn_node_file || dirent->special) 473 && depth >= svn_depth_files) 474 { 475 err = add_file(abspath, magic_cookie, config_autoprops, 476 no_autoprops, ctx, iterpool); 477 if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force) 478 svn_error_clear(err); 479 else 480 SVN_ERR(err); 481 } 482 } 483 484 /* Destroy the per-iteration pool. */ 485 svn_pool_destroy(iterpool); 486 487 return SVN_NO_ERROR; 488} 489 490/* This structure is used as baton for collecting the config entries 491 in the auto-props section and any inherited svn:auto-props 492 properties. 493*/ 494typedef struct collect_auto_props_baton_t 495{ 496 /* the hash table for storing the property name/value pairs */ 497 apr_hash_t *autoprops; 498 499 /* a pool used for allocating memory */ 500 apr_pool_t *result_pool; 501} collect_auto_props_baton_t; 502 503/* Implements svn_config_enumerator2_t callback. 504 505 For one auto-props config entry (NAME, VALUE), stash a copy of 506 NAME and VALUE, allocated in BATON->POOL, in BATON->AUTOPROP. 507 BATON must point to an collect_auto_props_baton_t. 508*/ 509static svn_boolean_t 510all_auto_props_collector(const char *name, 511 const char *value, 512 void *baton, 513 apr_pool_t *pool) 514{ 515 collect_auto_props_baton_t *autoprops_baton = baton; 516 apr_array_header_t *autoprops; 517 int i; 518 519 /* nothing to do here without a value */ 520 if (*value == 0) 521 return TRUE; 522 523 split_props(&autoprops, value, pool); 524 525 for (i = 0; i < autoprops->nelts; i ++) 526 { 527 size_t len; 528 const char *this_value; 529 char *property = APR_ARRAY_IDX(autoprops, i, char *); 530 char *equal_sign = strchr(property, '='); 531 532 if (equal_sign) 533 { 534 *equal_sign = '\0'; 535 equal_sign++; 536 trim_string(&equal_sign); 537 unquote_string(&equal_sign); 538 this_value = equal_sign; 539 } 540 else 541 { 542 this_value = ""; 543 } 544 trim_string(&property); 545 len = strlen(property); 546 547 if (len > 0) 548 { 549 apr_hash_t *pattern_hash = svn_hash_gets(autoprops_baton->autoprops, 550 name); 551 svn_string_t *propval; 552 553 /* Force reserved boolean property values to '*'. */ 554 if (svn_prop_is_boolean(property)) 555 { 556 /* SVN_PROP_EXECUTABLE, SVN_PROP_NEEDS_LOCK, SVN_PROP_SPECIAL */ 557 propval = svn_string_create("*", autoprops_baton->result_pool); 558 } 559 else 560 { 561 propval = svn_string_create(this_value, 562 autoprops_baton->result_pool); 563 } 564 565 if (!pattern_hash) 566 { 567 pattern_hash = apr_hash_make(autoprops_baton->result_pool); 568 svn_hash_sets(autoprops_baton->autoprops, 569 apr_pstrdup(autoprops_baton->result_pool, name), 570 pattern_hash); 571 } 572 svn_hash_sets(pattern_hash, 573 apr_pstrdup(autoprops_baton->result_pool, property), 574 propval->data); 575 } 576 } 577 return TRUE; 578} 579 580/* Go up the directory tree from LOCAL_ABSPATH, looking for a versioned 581 * directory. If found, return its path in *EXISTING_PARENT_ABSPATH. 582 * Otherwise, return SVN_ERR_CLIENT_NO_VERSIONED_PARENT. */ 583static svn_error_t * 584find_existing_parent(const char **existing_parent_abspath, 585 svn_client_ctx_t *ctx, 586 const char *local_abspath, 587 apr_pool_t *result_pool, 588 apr_pool_t *scratch_pool) 589{ 590 svn_node_kind_t kind; 591 const char *parent_abspath; 592 svn_wc_context_t *wc_ctx = ctx->wc_ctx; 593 594 SVN_ERR(svn_wc_read_kind2(&kind, wc_ctx, local_abspath, 595 FALSE, FALSE, scratch_pool)); 596 597 if (kind == svn_node_dir) 598 { 599 *existing_parent_abspath = apr_pstrdup(result_pool, local_abspath); 600 return SVN_NO_ERROR; 601 } 602 603 if (svn_dirent_is_root(local_abspath, strlen(local_abspath))) 604 return svn_error_create(SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL, NULL); 605 606 if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, scratch_pool), 607 scratch_pool)) 608 return svn_error_createf(SVN_ERR_RESERVED_FILENAME_SPECIFIED, NULL, 609 _("'%s' ends in a reserved name"), 610 svn_dirent_local_style(local_abspath, 611 scratch_pool)); 612 613 parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool); 614 615 if (ctx->cancel_func) 616 SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); 617 618 SVN_ERR(find_existing_parent(existing_parent_abspath, ctx, parent_abspath, 619 result_pool, scratch_pool)); 620 621 return SVN_NO_ERROR; 622} 623 624svn_error_t * 625svn_client__get_all_auto_props(apr_hash_t **autoprops, 626 const char *path_or_url, 627 svn_client_ctx_t *ctx, 628 apr_pool_t *result_pool, 629 apr_pool_t *scratch_pool) 630{ 631 int i; 632 apr_array_header_t *inherited_config_auto_props; 633 apr_hash_t *props; 634 svn_opt_revision_t rev; 635 svn_string_t *config_auto_prop; 636 svn_boolean_t use_autoprops; 637 collect_auto_props_baton_t autoprops_baton; 638 svn_error_t *err = NULL; 639 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 640 svn_boolean_t target_is_url = svn_path_is_url(path_or_url); 641 svn_config_t *cfg = ctx->config ? svn_hash_gets(ctx->config, 642 SVN_CONFIG_CATEGORY_CONFIG) 643 : NULL; 644 *autoprops = apr_hash_make(result_pool); 645 autoprops_baton.result_pool = result_pool; 646 autoprops_baton.autoprops = *autoprops; 647 648 649 /* Are "traditional" auto-props enabled? If so grab them from the 650 config. This is our starting set auto-props, which may be overriden 651 by svn:auto-props. */ 652 SVN_ERR(svn_config_get_bool(cfg, &use_autoprops, 653 SVN_CONFIG_SECTION_MISCELLANY, 654 SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, FALSE)); 655 if (use_autoprops) 656 svn_config_enumerate2(cfg, SVN_CONFIG_SECTION_AUTO_PROPS, 657 all_auto_props_collector, &autoprops_baton, 658 scratch_pool); 659 660 /* Convert the config file setting (if any) into a hash mapping file 661 patterns to as hash of prop-->val mappings. */ 662 if (svn_path_is_url(path_or_url)) 663 rev.kind = svn_opt_revision_head; 664 else 665 rev.kind = svn_opt_revision_working; 666 667 /* If PATH_OR_URL is a WC path, then it might be unversioned, in which case 668 we find it's nearest versioned parent. */ 669 do 670 { 671 err = svn_client_propget5(&props, &inherited_config_auto_props, 672 SVN_PROP_INHERITABLE_AUTO_PROPS, path_or_url, 673 &rev, &rev, NULL, svn_depth_empty, NULL, ctx, 674 scratch_pool, iterpool); 675 if (err) 676 { 677 if (target_is_url || err->apr_err != SVN_ERR_UNVERSIONED_RESOURCE) 678 return svn_error_trace(err); 679 680 svn_error_clear(err); 681 err = NULL; 682 SVN_ERR(find_existing_parent(&path_or_url, ctx, path_or_url, 683 scratch_pool, iterpool)); 684 } 685 else 686 { 687 break; 688 } 689 } 690 while (err == NULL); 691 692 /* Stash any explicit PROPS for PARENT_PATH into the inherited props array, 693 since these are actually inherited props for LOCAL_ABSPATH. */ 694 config_auto_prop = svn_hash_gets(props, path_or_url); 695 696 if (config_auto_prop) 697 { 698 svn_prop_inherited_item_t *new_iprop = 699 apr_palloc(scratch_pool, sizeof(*new_iprop)); 700 new_iprop->path_or_url = path_or_url; 701 new_iprop->prop_hash = apr_hash_make(scratch_pool); 702 svn_hash_sets(new_iprop->prop_hash, SVN_PROP_INHERITABLE_AUTO_PROPS, 703 config_auto_prop); 704 APR_ARRAY_PUSH(inherited_config_auto_props, 705 svn_prop_inherited_item_t *) = new_iprop; 706 } 707 708 for (i = 0; i < inherited_config_auto_props->nelts; i++) 709 { 710 svn_prop_inherited_item_t *elt = APR_ARRAY_IDX( 711 inherited_config_auto_props, i, svn_prop_inherited_item_t *); 712 const svn_string_t *propval = 713 svn_hash_gets(elt->prop_hash, SVN_PROP_INHERITABLE_AUTO_PROPS); 714 715 { 716 const char *ch = propval->data; 717 svn_stringbuf_t *config_auto_prop_pattern; 718 svn_stringbuf_t *config_auto_prop_val; 719 720 svn_pool_clear(iterpool); 721 722 config_auto_prop_pattern = svn_stringbuf_create_empty(iterpool); 723 config_auto_prop_val = svn_stringbuf_create_empty(iterpool); 724 725 /* Parse svn:auto-props value. */ 726 while (*ch != '\0') 727 { 728 svn_stringbuf_setempty(config_auto_prop_pattern); 729 svn_stringbuf_setempty(config_auto_prop_val); 730 731 /* Parse the file pattern. */ 732 while (*ch != '\0' && *ch != '=' && *ch != '\n') 733 { 734 svn_stringbuf_appendbyte(config_auto_prop_pattern, *ch); 735 ch++; 736 } 737 738 svn_stringbuf_strip_whitespace(config_auto_prop_pattern); 739 740 /* Parse the auto-prop group. */ 741 while (*ch != '\0' && *ch != '\n') 742 { 743 svn_stringbuf_appendbyte(config_auto_prop_val, *ch); 744 ch++; 745 } 746 747 /* Strip leading '=' and whitespace from auto-prop group. */ 748 if (config_auto_prop_val->data[0] == '=') 749 svn_stringbuf_remove(config_auto_prop_val, 0, 1); 750 svn_stringbuf_strip_whitespace(config_auto_prop_val); 751 752 all_auto_props_collector(config_auto_prop_pattern->data, 753 config_auto_prop_val->data, 754 &autoprops_baton, 755 scratch_pool); 756 757 /* Skip to next line if any. */ 758 while (*ch != '\0' && *ch != '\n') 759 ch++; 760 if (*ch == '\n') 761 ch++; 762 } 763 } 764 } 765 766 svn_pool_destroy(iterpool); 767 768 return SVN_NO_ERROR; 769} 770 771/* The main logic of the public svn_client_add5. 772 * 773 * EXISTING_PARENT_ABSPATH is the absolute path to the first existing 774 * parent directory of local_abspath. If not NULL, all missing parents 775 * of LOCAL_ABSPATH must be created before LOCAL_ABSPATH can be added. */ 776static svn_error_t * 777add(const char *local_abspath, 778 svn_depth_t depth, 779 svn_boolean_t force, 780 svn_boolean_t no_ignore, 781 svn_boolean_t no_autoprops, 782 const char *existing_parent_abspath, 783 svn_client_ctx_t *ctx, 784 apr_pool_t *scratch_pool) 785{ 786 svn_node_kind_t kind; 787 svn_error_t *err; 788 svn_magic__cookie_t *magic_cookie; 789 apr_array_header_t *ignores = NULL; 790 791 SVN_ERR(svn_magic__init(&magic_cookie, ctx->config, scratch_pool)); 792 793 if (existing_parent_abspath) 794 { 795 const char *parent_abspath; 796 const char *child_relpath; 797 apr_array_header_t *components; 798 int i; 799 apr_pool_t *iterpool; 800 801 parent_abspath = existing_parent_abspath; 802 child_relpath = svn_dirent_is_child(existing_parent_abspath, 803 local_abspath, NULL); 804 components = svn_path_decompose(child_relpath, scratch_pool); 805 iterpool = svn_pool_create(scratch_pool); 806 for (i = 0; i < components->nelts - 1; i++) 807 { 808 const char *component; 809 svn_node_kind_t disk_kind; 810 811 svn_pool_clear(iterpool); 812 813 if (ctx->cancel_func) 814 SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); 815 816 component = APR_ARRAY_IDX(components, i, const char *); 817 parent_abspath = svn_dirent_join(parent_abspath, component, 818 scratch_pool); 819 SVN_ERR(svn_io_check_path(parent_abspath, &disk_kind, iterpool)); 820 if (disk_kind != svn_node_none && disk_kind != svn_node_dir) 821 return svn_error_createf(SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL, 822 _("'%s' prevents creating parent of '%s'"), 823 parent_abspath, local_abspath); 824 825 SVN_ERR(svn_io_make_dir_recursively(parent_abspath, scratch_pool)); 826 SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, parent_abspath, 827 NULL /*props*/, 828 FALSE /* skip checks */, 829 ctx->notify_func2, ctx->notify_baton2, 830 scratch_pool)); 831 } 832 svn_pool_destroy(iterpool); 833 } 834 835 SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); 836 if (kind == svn_node_dir) 837 { 838 /* We use add_dir_recursive for all directory targets 839 and pass depth along no matter what it is, so that the 840 target's depth will be set correctly. */ 841 err = add_dir_recursive(local_abspath, depth, force, 842 no_autoprops, magic_cookie, NULL, 843 !no_ignore, ignores, ctx, 844 scratch_pool, scratch_pool); 845 } 846 else if (kind == svn_node_file) 847 err = add_file(local_abspath, magic_cookie, NULL, 848 no_autoprops, ctx, scratch_pool); 849 else if (kind == svn_node_none) 850 { 851 svn_boolean_t tree_conflicted; 852 853 /* Provide a meaningful error message if the node does not exist 854 * on disk but is a tree conflict victim. */ 855 err = svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted, 856 ctx->wc_ctx, local_abspath, 857 scratch_pool); 858 if (err) 859 svn_error_clear(err); 860 else if (tree_conflicted) 861 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, 862 _("'%s' is an existing item in conflict; " 863 "please mark the conflict as resolved " 864 "before adding a new item here"), 865 svn_dirent_local_style(local_abspath, 866 scratch_pool)); 867 868 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 869 _("'%s' not found"), 870 svn_dirent_local_style(local_abspath, 871 scratch_pool)); 872 } 873 else 874 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 875 _("Unsupported node kind for path '%s'"), 876 svn_dirent_local_style(local_abspath, 877 scratch_pool)); 878 879 /* Ignore SVN_ERR_ENTRY_EXISTS when FORCE is set. */ 880 if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force) 881 { 882 svn_error_clear(err); 883 err = SVN_NO_ERROR; 884 } 885 return svn_error_trace(err); 886} 887 888 889 890svn_error_t * 891svn_client_add5(const char *path, 892 svn_depth_t depth, 893 svn_boolean_t force, 894 svn_boolean_t no_ignore, 895 svn_boolean_t no_autoprops, 896 svn_boolean_t add_parents, 897 svn_client_ctx_t *ctx, 898 apr_pool_t *scratch_pool) 899{ 900 const char *parent_abspath; 901 const char *local_abspath; 902 const char *existing_parent_abspath; 903 svn_boolean_t is_wc_root; 904 svn_error_t *err; 905 906 if (svn_path_is_url(path)) 907 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 908 _("'%s' is not a local path"), path); 909 910 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); 911 912 /* See if we're being asked to add a wc-root. That's typically not 913 okay, unless we're in "force" mode. svn_wc__is_wcroot() 914 will return TRUE even if LOCAL_ABSPATH is a *symlink* to a working 915 copy root, which is a scenario we want to treat differently. */ 916 err = svn_wc__is_wcroot(&is_wc_root, ctx->wc_ctx, local_abspath, 917 scratch_pool); 918 if (err) 919 { 920 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND 921 && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) 922 { 923 return svn_error_trace(err); 924 } 925 926 svn_error_clear(err); 927 err = NULL; /* SVN_NO_ERROR */ 928 is_wc_root = FALSE; 929 } 930 if (is_wc_root) 931 { 932#ifdef HAVE_SYMLINK 933 svn_node_kind_t disk_kind; 934 svn_boolean_t is_special; 935 936 SVN_ERR(svn_io_check_special_path(local_abspath, &disk_kind, &is_special, 937 scratch_pool)); 938 939 /* A symlink can be an unversioned target and a wcroot. Lets try to add 940 the symlink, which can't be a wcroot. */ 941 if (is_special) 942 is_wc_root = FALSE; 943 else 944#endif 945 { 946 if (! force) 947 return svn_error_createf( 948 SVN_ERR_ENTRY_EXISTS, NULL, 949 _("'%s' is already under version control"), 950 svn_dirent_local_style(local_abspath, 951 scratch_pool)); 952 } 953 } 954 955 if (is_wc_root) 956 parent_abspath = local_abspath; /* We will only add children */ 957 else 958 parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool); 959 960 existing_parent_abspath = NULL; 961 if (add_parents && !is_wc_root) 962 { 963 apr_pool_t *subpool; 964 const char *existing_parent_abspath2; 965 966 subpool = svn_pool_create(scratch_pool); 967 SVN_ERR(find_existing_parent(&existing_parent_abspath2, ctx, 968 parent_abspath, scratch_pool, subpool)); 969 if (strcmp(existing_parent_abspath2, parent_abspath) != 0) 970 existing_parent_abspath = existing_parent_abspath2; 971 svn_pool_destroy(subpool); 972 } 973 974 SVN_WC__CALL_WITH_WRITE_LOCK( 975 add(local_abspath, depth, force, no_ignore, no_autoprops, 976 existing_parent_abspath, ctx, scratch_pool), 977 ctx->wc_ctx, (existing_parent_abspath ? existing_parent_abspath 978 : parent_abspath), 979 FALSE /* lock_anchor */, scratch_pool); 980 return SVN_NO_ERROR; 981} 982 983 984static svn_error_t * 985path_driver_cb_func(void **dir_baton, 986 void *parent_baton, 987 void *callback_baton, 988 const char *path, 989 apr_pool_t *pool) 990{ 991 const svn_delta_editor_t *editor = callback_baton; 992 SVN_ERR(svn_path_check_valid(path, pool)); 993 return editor->add_directory(path, parent_baton, NULL, 994 SVN_INVALID_REVNUM, pool, dir_baton); 995} 996 997/* Append URL, and all it's non-existent parent directories, to TARGETS. 998 Use TEMPPOOL for temporary allocations and POOL for any additions to 999 TARGETS. */ 1000static svn_error_t * 1001add_url_parents(svn_ra_session_t *ra_session, 1002 const char *url, 1003 apr_array_header_t *targets, 1004 apr_pool_t *temppool, 1005 apr_pool_t *pool) 1006{ 1007 svn_node_kind_t kind; 1008 const char *parent_url = svn_uri_dirname(url, pool); 1009 1010 SVN_ERR(svn_ra_reparent(ra_session, parent_url, temppool)); 1011 SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind, 1012 temppool)); 1013 1014 if (kind == svn_node_none) 1015 SVN_ERR(add_url_parents(ra_session, parent_url, targets, temppool, pool)); 1016 1017 APR_ARRAY_PUSH(targets, const char *) = url; 1018 1019 return SVN_NO_ERROR; 1020} 1021 1022static svn_error_t * 1023mkdir_urls(const apr_array_header_t *urls, 1024 svn_boolean_t make_parents, 1025 const apr_hash_t *revprop_table, 1026 svn_commit_callback2_t commit_callback, 1027 void *commit_baton, 1028 svn_client_ctx_t *ctx, 1029 apr_pool_t *pool) 1030{ 1031 svn_ra_session_t *ra_session = NULL; 1032 const svn_delta_editor_t *editor; 1033 void *edit_baton; 1034 const char *log_msg; 1035 apr_array_header_t *targets; 1036 apr_hash_t *targets_hash; 1037 apr_hash_t *commit_revprops; 1038 svn_error_t *err; 1039 const char *common; 1040 int i; 1041 1042 /* Find any non-existent parent directories */ 1043 if (make_parents) 1044 { 1045 apr_array_header_t *all_urls = apr_array_make(pool, urls->nelts, 1046 sizeof(const char *)); 1047 const char *first_url = APR_ARRAY_IDX(urls, 0, const char *); 1048 apr_pool_t *iterpool = svn_pool_create(pool); 1049 1050 SVN_ERR(svn_client_open_ra_session2(&ra_session, first_url, NULL, 1051 ctx, pool, iterpool)); 1052 1053 for (i = 0; i < urls->nelts; i++) 1054 { 1055 const char *url = APR_ARRAY_IDX(urls, i, const char *); 1056 1057 svn_pool_clear(iterpool); 1058 SVN_ERR(add_url_parents(ra_session, url, all_urls, iterpool, pool)); 1059 } 1060 1061 svn_pool_destroy(iterpool); 1062 1063 urls = all_urls; 1064 } 1065 1066 /* Condense our list of mkdir targets. */ 1067 SVN_ERR(svn_uri_condense_targets(&common, &targets, urls, FALSE, 1068 pool, pool)); 1069 1070 /*Remove duplicate targets introduced by make_parents with more targets. */ 1071 SVN_ERR(svn_hash_from_cstring_keys(&targets_hash, targets, pool)); 1072 SVN_ERR(svn_hash_keys(&targets, targets_hash, pool)); 1073 1074 if (! targets->nelts) 1075 { 1076 const char *bname; 1077 svn_uri_split(&common, &bname, common, pool); 1078 APR_ARRAY_PUSH(targets, const char *) = bname; 1079 1080 if (*bname == '\0') 1081 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 1082 _("There is no valid URI above '%s'"), 1083 common); 1084 } 1085 else 1086 { 1087 svn_boolean_t resplit = FALSE; 1088 1089 /* We can't "mkdir" the root of an editor drive, so if one of 1090 our targets is the empty string, we need to back everything 1091 up by a path component. */ 1092 for (i = 0; i < targets->nelts; i++) 1093 { 1094 const char *path = APR_ARRAY_IDX(targets, i, const char *); 1095 if (! *path) 1096 { 1097 resplit = TRUE; 1098 break; 1099 } 1100 } 1101 if (resplit) 1102 { 1103 const char *bname; 1104 1105 svn_uri_split(&common, &bname, common, pool); 1106 1107 if (*bname == '\0') 1108 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 1109 _("There is no valid URI above '%s'"), 1110 common); 1111 1112 for (i = 0; i < targets->nelts; i++) 1113 { 1114 const char *path = APR_ARRAY_IDX(targets, i, const char *); 1115 path = svn_relpath_join(bname, path, pool); 1116 APR_ARRAY_IDX(targets, i, const char *) = path; 1117 } 1118 } 1119 } 1120 1121 svn_sort__array(targets, svn_sort_compare_paths); 1122 1123 /* ### This reparent may be problematic in limited-authz-to-common-parent 1124 ### scenarios (compare issue #3242). See also issue #3649. */ 1125 if (ra_session) 1126 SVN_ERR(svn_ra_reparent(ra_session, common, pool)); 1127 1128 /* Create new commit items and add them to the array. */ 1129 if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx)) 1130 { 1131 svn_client_commit_item3_t *item; 1132 const char *tmp_file; 1133 apr_array_header_t *commit_items 1134 = apr_array_make(pool, targets->nelts, sizeof(item)); 1135 1136 for (i = 0; i < targets->nelts; i++) 1137 { 1138 const char *path = APR_ARRAY_IDX(targets, i, const char *); 1139 1140 item = svn_client_commit_item3_create(pool); 1141 item->url = svn_path_url_add_component2(common, path, pool); 1142 item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD; 1143 APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; 1144 } 1145 1146 SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items, 1147 ctx, pool)); 1148 1149 if (! log_msg) 1150 return SVN_NO_ERROR; 1151 } 1152 else 1153 log_msg = ""; 1154 1155 SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table, 1156 log_msg, ctx, pool)); 1157 1158 /* Open an RA session for the URL. Note that we don't have a local 1159 directory, nor a place to put temp files. */ 1160 if (!ra_session) 1161 SVN_ERR(svn_client_open_ra_session2(&ra_session, common, NULL, ctx, 1162 pool, pool)); 1163 else 1164 SVN_ERR(svn_ra_reparent(ra_session, common, pool)); 1165 1166 1167 /* Fetch RA commit editor */ 1168 SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session, 1169 svn_client__get_shim_callbacks(ctx->wc_ctx, NULL, 1170 pool))); 1171 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton, 1172 commit_revprops, 1173 commit_callback, 1174 commit_baton, 1175 NULL, TRUE, /* No lock tokens */ 1176 pool)); 1177 1178 /* Call the path-based editor driver. */ 1179 err = svn_error_trace( 1180 svn_delta_path_driver2(editor, edit_baton, targets, TRUE, 1181 path_driver_cb_func, (void *)editor, pool)); 1182 1183 if (err) 1184 { 1185 /* At least try to abort the edit (and fs txn) before throwing err. */ 1186 return svn_error_compose_create( 1187 err, 1188 svn_error_trace(editor->abort_edit(edit_baton, pool))); 1189 } 1190 1191 if (ctx->notify_func2) 1192 { 1193 svn_wc_notify_t *notify; 1194 notify = svn_wc_create_notify_url(common, 1195 svn_wc_notify_commit_finalizing, 1196 pool); 1197 ctx->notify_func2(ctx->notify_baton2, notify, pool); 1198 } 1199 1200 /* Close the edit. */ 1201 return svn_error_trace(editor->close_edit(edit_baton, pool)); 1202} 1203 1204 1205 1206svn_error_t * 1207svn_client__make_local_parents(const char *local_abspath, 1208 svn_boolean_t make_parents, 1209 svn_client_ctx_t *ctx, 1210 apr_pool_t *scratch_pool) 1211{ 1212 svn_error_t *err; 1213 svn_node_kind_t orig_kind; 1214 SVN_ERR(svn_io_check_path(local_abspath, &orig_kind, scratch_pool)); 1215 if (make_parents) 1216 SVN_ERR(svn_io_make_dir_recursively(local_abspath, scratch_pool)); 1217 else 1218 SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT, scratch_pool)); 1219 1220 err = svn_client_add5(local_abspath, svn_depth_empty, FALSE, FALSE, FALSE, 1221 make_parents, ctx, scratch_pool); 1222 1223 /* If we created a new directory, but couldn't add it to version 1224 control, then delete it. */ 1225 if (err && (orig_kind == svn_node_none)) 1226 { 1227 err = svn_error_compose_create(err, 1228 svn_io_remove_dir2(local_abspath, FALSE, 1229 NULL, NULL, 1230 scratch_pool)); 1231 } 1232 1233 return svn_error_trace(err); 1234} 1235 1236 1237svn_error_t * 1238svn_client_mkdir4(const apr_array_header_t *paths, 1239 svn_boolean_t make_parents, 1240 const apr_hash_t *revprop_table, 1241 svn_commit_callback2_t commit_callback, 1242 void *commit_baton, 1243 svn_client_ctx_t *ctx, 1244 apr_pool_t *pool) 1245{ 1246 if (! paths->nelts) 1247 return SVN_NO_ERROR; 1248 1249 SVN_ERR(svn_client__assert_homogeneous_target_type(paths)); 1250 1251 if (svn_path_is_url(APR_ARRAY_IDX(paths, 0, const char *))) 1252 { 1253 SVN_ERR(mkdir_urls(paths, make_parents, revprop_table, commit_callback, 1254 commit_baton, ctx, pool)); 1255 } 1256 else 1257 { 1258 /* This is a regular "mkdir" + "svn add" */ 1259 apr_pool_t *iterpool = svn_pool_create(pool); 1260 int i; 1261 1262 for (i = 0; i < paths->nelts; i++) 1263 { 1264 const char *path = APR_ARRAY_IDX(paths, i, const char *); 1265 1266 svn_pool_clear(iterpool); 1267 1268 /* See if the user wants us to stop. */ 1269 if (ctx->cancel_func) 1270 SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); 1271 1272 SVN_ERR(svn_dirent_get_absolute(&path, path, iterpool)); 1273 1274 SVN_ERR(svn_client__make_local_parents(path, make_parents, ctx, 1275 iterpool)); 1276 } 1277 svn_pool_destroy(iterpool); 1278 } 1279 1280 return SVN_NO_ERROR; 1281} 1282