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 const svn_delta_editor_t *editor, 987 void *edit_baton, 988 void *parent_baton, 989 void *callback_baton, 990 const char *path, 991 apr_pool_t *pool) 992{ 993 SVN_ERR(svn_path_check_valid(path, pool)); 994 return editor->add_directory(path, parent_baton, NULL, 995 SVN_INVALID_REVNUM, pool, dir_baton); 996} 997 998/* Append URL, and all it's non-existent parent directories, to TARGETS. 999 Use TEMPPOOL for temporary allocations and POOL for any additions to 1000 TARGETS. */ 1001static svn_error_t * 1002add_url_parents(svn_ra_session_t *ra_session, 1003 const char *url, 1004 apr_array_header_t *targets, 1005 apr_pool_t *temppool, 1006 apr_pool_t *pool) 1007{ 1008 svn_node_kind_t kind; 1009 const char *parent_url = svn_uri_dirname(url, pool); 1010 1011 SVN_ERR(svn_ra_reparent(ra_session, parent_url, temppool)); 1012 SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind, 1013 temppool)); 1014 1015 if (kind == svn_node_none) 1016 SVN_ERR(add_url_parents(ra_session, parent_url, targets, temppool, pool)); 1017 1018 APR_ARRAY_PUSH(targets, const char *) = url; 1019 1020 return SVN_NO_ERROR; 1021} 1022 1023static svn_error_t * 1024mkdir_urls(const apr_array_header_t *urls, 1025 svn_boolean_t make_parents, 1026 const apr_hash_t *revprop_table, 1027 svn_commit_callback2_t commit_callback, 1028 void *commit_baton, 1029 svn_client_ctx_t *ctx, 1030 apr_pool_t *pool) 1031{ 1032 svn_ra_session_t *ra_session = NULL; 1033 const svn_delta_editor_t *editor; 1034 void *edit_baton; 1035 const char *log_msg; 1036 apr_array_header_t *targets; 1037 apr_hash_t *targets_hash; 1038 apr_hash_t *commit_revprops; 1039 svn_error_t *err; 1040 const char *common; 1041 int i; 1042 1043 /* Find any non-existent parent directories */ 1044 if (make_parents) 1045 { 1046 apr_array_header_t *all_urls = apr_array_make(pool, urls->nelts, 1047 sizeof(const char *)); 1048 const char *first_url = APR_ARRAY_IDX(urls, 0, const char *); 1049 apr_pool_t *iterpool = svn_pool_create(pool); 1050 1051 SVN_ERR(svn_client_open_ra_session2(&ra_session, first_url, NULL, 1052 ctx, pool, iterpool)); 1053 1054 for (i = 0; i < urls->nelts; i++) 1055 { 1056 const char *url = APR_ARRAY_IDX(urls, i, const char *); 1057 1058 svn_pool_clear(iterpool); 1059 SVN_ERR(add_url_parents(ra_session, url, all_urls, iterpool, pool)); 1060 } 1061 1062 svn_pool_destroy(iterpool); 1063 1064 urls = all_urls; 1065 } 1066 1067 /* Condense our list of mkdir targets. */ 1068 SVN_ERR(svn_uri_condense_targets(&common, &targets, urls, FALSE, 1069 pool, pool)); 1070 1071 /*Remove duplicate targets introduced by make_parents with more targets. */ 1072 SVN_ERR(svn_hash_from_cstring_keys(&targets_hash, targets, pool)); 1073 SVN_ERR(svn_hash_keys(&targets, targets_hash, pool)); 1074 1075 if (! targets->nelts) 1076 { 1077 const char *bname; 1078 svn_uri_split(&common, &bname, common, pool); 1079 APR_ARRAY_PUSH(targets, const char *) = bname; 1080 1081 if (*bname == '\0') 1082 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 1083 _("There is no valid URI above '%s'"), 1084 common); 1085 } 1086 else 1087 { 1088 svn_boolean_t resplit = FALSE; 1089 1090 /* We can't "mkdir" the root of an editor drive, so if one of 1091 our targets is the empty string, we need to back everything 1092 up by a path component. */ 1093 for (i = 0; i < targets->nelts; i++) 1094 { 1095 const char *path = APR_ARRAY_IDX(targets, i, const char *); 1096 if (! *path) 1097 { 1098 resplit = TRUE; 1099 break; 1100 } 1101 } 1102 if (resplit) 1103 { 1104 const char *bname; 1105 1106 svn_uri_split(&common, &bname, common, pool); 1107 1108 if (*bname == '\0') 1109 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 1110 _("There is no valid URI above '%s'"), 1111 common); 1112 1113 for (i = 0; i < targets->nelts; i++) 1114 { 1115 const char *path = APR_ARRAY_IDX(targets, i, const char *); 1116 path = svn_relpath_join(bname, path, pool); 1117 APR_ARRAY_IDX(targets, i, const char *) = path; 1118 } 1119 } 1120 } 1121 1122 svn_sort__array(targets, svn_sort_compare_paths); 1123 1124 /* ### This reparent may be problematic in limited-authz-to-common-parent 1125 ### scenarios (compare issue #3242). See also issue #3649. */ 1126 if (ra_session) 1127 SVN_ERR(svn_ra_reparent(ra_session, common, pool)); 1128 1129 /* Create new commit items and add them to the array. */ 1130 if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx)) 1131 { 1132 svn_client_commit_item3_t *item; 1133 const char *tmp_file; 1134 apr_array_header_t *commit_items 1135 = apr_array_make(pool, targets->nelts, sizeof(item)); 1136 1137 for (i = 0; i < targets->nelts; i++) 1138 { 1139 const char *path = APR_ARRAY_IDX(targets, i, const char *); 1140 1141 item = svn_client_commit_item3_create(pool); 1142 item->url = svn_path_url_add_component2(common, path, pool); 1143 item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD; 1144 APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; 1145 } 1146 1147 SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items, 1148 ctx, pool)); 1149 1150 if (! log_msg) 1151 return SVN_NO_ERROR; 1152 } 1153 else 1154 log_msg = ""; 1155 1156 SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table, 1157 log_msg, ctx, pool)); 1158 1159 /* Open an RA session for the URL. Note that we don't have a local 1160 directory, nor a place to put temp files. */ 1161 if (!ra_session) 1162 SVN_ERR(svn_client_open_ra_session2(&ra_session, common, NULL, ctx, 1163 pool, pool)); 1164 else 1165 SVN_ERR(svn_ra_reparent(ra_session, common, pool)); 1166 1167 1168 /* Fetch RA commit editor */ 1169 SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session, 1170 svn_client__get_shim_callbacks(ctx->wc_ctx, NULL, 1171 pool))); 1172 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton, 1173 commit_revprops, 1174 commit_callback, 1175 commit_baton, 1176 NULL, TRUE, /* No lock tokens */ 1177 pool)); 1178 1179 /* Call the path-based editor driver. */ 1180 err = svn_error_trace( 1181 svn_delta_path_driver3(editor, edit_baton, targets, TRUE, 1182 path_driver_cb_func, NULL, pool)); 1183 1184 if (err) 1185 { 1186 /* At least try to abort the edit (and fs txn) before throwing err. */ 1187 return svn_error_compose_create( 1188 err, 1189 svn_error_trace(editor->abort_edit(edit_baton, pool))); 1190 } 1191 1192 if (ctx->notify_func2) 1193 { 1194 svn_wc_notify_t *notify; 1195 notify = svn_wc_create_notify_url(common, 1196 svn_wc_notify_commit_finalizing, 1197 pool); 1198 ctx->notify_func2(ctx->notify_baton2, notify, pool); 1199 } 1200 1201 /* Close the edit. */ 1202 return svn_error_trace(editor->close_edit(edit_baton, pool)); 1203} 1204 1205 1206 1207svn_error_t * 1208svn_client__make_local_parents(const char *local_abspath, 1209 svn_boolean_t make_parents, 1210 svn_client_ctx_t *ctx, 1211 apr_pool_t *scratch_pool) 1212{ 1213 svn_error_t *err; 1214 svn_node_kind_t orig_kind; 1215 SVN_ERR(svn_io_check_path(local_abspath, &orig_kind, scratch_pool)); 1216 if (make_parents) 1217 SVN_ERR(svn_io_make_dir_recursively(local_abspath, scratch_pool)); 1218 else 1219 SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT, scratch_pool)); 1220 1221 err = svn_client_add5(local_abspath, svn_depth_empty, FALSE, FALSE, FALSE, 1222 make_parents, ctx, scratch_pool); 1223 1224 /* If we created a new directory, but couldn't add it to version 1225 control, then delete it. */ 1226 if (err && (orig_kind == svn_node_none)) 1227 { 1228 err = svn_error_compose_create(err, 1229 svn_io_remove_dir2(local_abspath, FALSE, 1230 NULL, NULL, 1231 scratch_pool)); 1232 } 1233 1234 return svn_error_trace(err); 1235} 1236 1237 1238svn_error_t * 1239svn_client_mkdir4(const apr_array_header_t *paths, 1240 svn_boolean_t make_parents, 1241 const apr_hash_t *revprop_table, 1242 svn_commit_callback2_t commit_callback, 1243 void *commit_baton, 1244 svn_client_ctx_t *ctx, 1245 apr_pool_t *pool) 1246{ 1247 if (! paths->nelts) 1248 return SVN_NO_ERROR; 1249 1250 SVN_ERR(svn_client__assert_homogeneous_target_type(paths)); 1251 1252 if (svn_path_is_url(APR_ARRAY_IDX(paths, 0, const char *))) 1253 { 1254 SVN_ERR(mkdir_urls(paths, make_parents, revprop_table, commit_callback, 1255 commit_baton, ctx, pool)); 1256 } 1257 else 1258 { 1259 /* This is a regular "mkdir" + "svn add" */ 1260 apr_pool_t *iterpool = svn_pool_create(pool); 1261 int i; 1262 1263 for (i = 0; i < paths->nelts; i++) 1264 { 1265 const char *path = APR_ARRAY_IDX(paths, i, const char *); 1266 1267 svn_pool_clear(iterpool); 1268 1269 /* See if the user wants us to stop. */ 1270 if (ctx->cancel_func) 1271 SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); 1272 1273 SVN_ERR(svn_dirent_get_absolute(&path, path, iterpool)); 1274 1275 SVN_ERR(svn_client__make_local_parents(path, make_parents, ctx, 1276 iterpool)); 1277 } 1278 svn_pool_destroy(iterpool); 1279 } 1280 1281 return SVN_NO_ERROR; 1282} 1283