1/* fs-wrap.c --- filesystem interface wrappers. 2 * 3 * ==================================================================== 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 * ==================================================================== 21 */ 22 23#include <stdio.h> 24#include <string.h> 25#include <ctype.h> 26 27#include "svn_hash.h" 28#include "svn_pools.h" 29#include "svn_error.h" 30#include "svn_fs.h" 31#include "svn_path.h" 32#include "svn_props.h" 33#include "svn_repos.h" 34#include "svn_time.h" 35#include "svn_sorts.h" 36#include "repos.h" 37#include "svn_private_config.h" 38#include "private/svn_repos_private.h" 39#include "private/svn_utf_private.h" 40#include "private/svn_fspath.h" 41 42 43/*** Commit wrappers ***/ 44 45svn_error_t * 46svn_repos_fs_commit_txn(const char **conflict_p, 47 svn_repos_t *repos, 48 svn_revnum_t *new_rev, 49 svn_fs_txn_t *txn, 50 apr_pool_t *pool) 51{ 52 svn_error_t *err, *err2; 53 const char *txn_name; 54 apr_hash_t *props; 55 apr_pool_t *iterpool; 56 apr_hash_index_t *hi; 57 apr_hash_t *hooks_env; 58 59 *new_rev = SVN_INVALID_REVNUM; 60 61 /* Parse the hooks-env file (if any). */ 62 SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path, 63 pool, pool)); 64 65 /* Run pre-commit hooks. */ 66 SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool)); 67 SVN_ERR(svn_repos__hooks_pre_commit(repos, hooks_env, txn_name, pool)); 68 69 /* Remove any ephemeral transaction properties. */ 70 SVN_ERR(svn_fs_txn_proplist(&props, txn, pool)); 71 iterpool = svn_pool_create(pool); 72 for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) 73 { 74 const void *key; 75 apr_hash_this(hi, &key, NULL, NULL); 76 77 svn_pool_clear(iterpool); 78 79 if (strncmp(key, SVN_PROP_TXN_PREFIX, 80 (sizeof(SVN_PROP_TXN_PREFIX) - 1)) == 0) 81 { 82 SVN_ERR(svn_fs_change_txn_prop(txn, key, NULL, iterpool)); 83 } 84 } 85 svn_pool_destroy(iterpool); 86 87 /* Commit. */ 88 err = svn_fs_commit_txn(conflict_p, new_rev, txn, pool); 89 if (! SVN_IS_VALID_REVNUM(*new_rev)) 90 return err; 91 92 /* Run post-commit hooks. */ 93 if ((err2 = svn_repos__hooks_post_commit(repos, hooks_env, 94 *new_rev, txn_name, pool))) 95 { 96 err2 = svn_error_create 97 (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err2, 98 _("Commit succeeded, but post-commit hook failed")); 99 } 100 101 return svn_error_compose_create(err, err2); 102} 103 104 105 106/*** Transaction creation wrappers. ***/ 107 108 109svn_error_t * 110svn_repos_fs_begin_txn_for_commit2(svn_fs_txn_t **txn_p, 111 svn_repos_t *repos, 112 svn_revnum_t rev, 113 apr_hash_t *revprop_table, 114 apr_pool_t *pool) 115{ 116 apr_array_header_t *revprops; 117 const char *txn_name; 118 svn_string_t *author = svn_hash_gets(revprop_table, SVN_PROP_REVISION_AUTHOR); 119 apr_hash_t *hooks_env; 120 121 /* Parse the hooks-env file (if any). */ 122 SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path, 123 pool, pool)); 124 125 /* Begin the transaction, ask for the fs to do on-the-fly lock checks. 126 We fetch its name, too, so the start-commit hook can use it. */ 127 SVN_ERR(svn_fs_begin_txn2(txn_p, repos->fs, rev, 128 SVN_FS_TXN_CHECK_LOCKS, pool)); 129 SVN_ERR(svn_fs_txn_name(&txn_name, *txn_p, pool)); 130 131 /* We pass the revision properties to the filesystem by adding them 132 as properties on the txn. Later, when we commit the txn, these 133 properties will be copied into the newly created revision. */ 134 revprops = svn_prop_hash_to_array(revprop_table, pool); 135 SVN_ERR(svn_repos_fs_change_txn_props(*txn_p, revprops, pool)); 136 137 /* Run start-commit hooks. */ 138 SVN_ERR(svn_repos__hooks_start_commit(repos, hooks_env, 139 author ? author->data : NULL, 140 repos->client_capabilities, txn_name, 141 pool)); 142 return SVN_NO_ERROR; 143} 144 145 146svn_error_t * 147svn_repos_fs_begin_txn_for_commit(svn_fs_txn_t **txn_p, 148 svn_repos_t *repos, 149 svn_revnum_t rev, 150 const char *author, 151 const char *log_msg, 152 apr_pool_t *pool) 153{ 154 apr_hash_t *revprop_table = apr_hash_make(pool); 155 if (author) 156 svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR, 157 svn_string_create(author, pool)); 158 if (log_msg) 159 svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG, 160 svn_string_create(log_msg, pool)); 161 return svn_repos_fs_begin_txn_for_commit2(txn_p, repos, rev, revprop_table, 162 pool); 163} 164 165 166/*** Property wrappers ***/ 167 168svn_error_t * 169svn_repos__validate_prop(const char *name, 170 const svn_string_t *value, 171 apr_pool_t *pool) 172{ 173 svn_prop_kind_t kind = svn_property_kind2(name); 174 175 /* Allow deleting any property, even a property we don't allow to set. */ 176 if (value == NULL) 177 return SVN_NO_ERROR; 178 179 /* Disallow setting non-regular properties. */ 180 if (kind != svn_prop_regular_kind) 181 return svn_error_createf 182 (SVN_ERR_REPOS_BAD_ARGS, NULL, 183 _("Storage of non-regular property '%s' is disallowed through the " 184 "repository interface, and could indicate a bug in your client"), 185 name); 186 187 /* Validate "svn:" properties. */ 188 if (svn_prop_is_svn_prop(name) && value != NULL) 189 { 190 /* Validate that translated props (e.g., svn:log) are UTF-8 with 191 * LF line endings. */ 192 if (svn_prop_needs_translation(name)) 193 { 194 if (!svn_utf__is_valid(value->data, value->len)) 195 { 196 return svn_error_createf 197 (SVN_ERR_BAD_PROPERTY_VALUE, NULL, 198 _("Cannot accept '%s' property because it is not encoded in " 199 "UTF-8"), name); 200 } 201 202 /* Disallow inconsistent line ending style, by simply looking for 203 * carriage return characters ('\r'). */ 204 if (strchr(value->data, '\r') != NULL) 205 { 206 return svn_error_createf 207 (SVN_ERR_BAD_PROPERTY_VALUE, NULL, 208 _("Cannot accept non-LF line endings in '%s' property"), 209 name); 210 } 211 } 212 213 /* "svn:date" should be a valid date. */ 214 if (strcmp(name, SVN_PROP_REVISION_DATE) == 0) 215 { 216 apr_time_t temp; 217 svn_error_t *err; 218 219 err = svn_time_from_cstring(&temp, value->data, pool); 220 if (err) 221 return svn_error_create(SVN_ERR_BAD_PROPERTY_VALUE, 222 err, NULL); 223 } 224 } 225 226 return SVN_NO_ERROR; 227} 228 229 230/* Verify the mergeinfo property value VALUE and return an error if it 231 * is invalid. The PATH on which that property is set is used for error 232 * messages only. Use SCRATCH_POOL for temporary allocations. */ 233static svn_error_t * 234verify_mergeinfo(const svn_string_t *value, 235 const char *path, 236 apr_pool_t *scratch_pool) 237{ 238 svn_error_t *err; 239 svn_mergeinfo_t mergeinfo; 240 241 /* It's okay to delete svn:mergeinfo. */ 242 if (value == NULL) 243 return SVN_NO_ERROR; 244 245 /* Mergeinfo is UTF-8 encoded so the number of bytes returned by strlen() 246 * should match VALUE->LEN. Prevents trailing garbage in the property. */ 247 if (strlen(value->data) != value->len) 248 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 249 _("Commit rejected because mergeinfo on '%s' " 250 "contains unexpected string terminator"), 251 path); 252 253 err = svn_mergeinfo_parse(&mergeinfo, value->data, scratch_pool); 254 if (err) 255 return svn_error_createf(err->apr_err, err, 256 _("Commit rejected because mergeinfo on '%s' " 257 "is syntactically invalid"), 258 path); 259 return SVN_NO_ERROR; 260} 261 262 263svn_error_t * 264svn_repos_fs_change_node_prop(svn_fs_root_t *root, 265 const char *path, 266 const char *name, 267 const svn_string_t *value, 268 apr_pool_t *pool) 269{ 270 if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0) 271 SVN_ERR(verify_mergeinfo(value, path, pool)); 272 273 /* Validate the property, then call the wrapped function. */ 274 SVN_ERR(svn_repos__validate_prop(name, value, pool)); 275 return svn_fs_change_node_prop(root, path, name, value, pool); 276} 277 278 279svn_error_t * 280svn_repos_fs_change_txn_props(svn_fs_txn_t *txn, 281 const apr_array_header_t *txnprops, 282 apr_pool_t *pool) 283{ 284 int i; 285 286 for (i = 0; i < txnprops->nelts; i++) 287 { 288 svn_prop_t *prop = &APR_ARRAY_IDX(txnprops, i, svn_prop_t); 289 SVN_ERR(svn_repos__validate_prop(prop->name, prop->value, pool)); 290 } 291 292 return svn_fs_change_txn_props(txn, txnprops, pool); 293} 294 295 296svn_error_t * 297svn_repos_fs_change_txn_prop(svn_fs_txn_t *txn, 298 const char *name, 299 const svn_string_t *value, 300 apr_pool_t *pool) 301{ 302 apr_array_header_t *props = apr_array_make(pool, 1, sizeof(svn_prop_t)); 303 svn_prop_t prop; 304 305 prop.name = name; 306 prop.value = value; 307 APR_ARRAY_PUSH(props, svn_prop_t) = prop; 308 309 return svn_repos_fs_change_txn_props(txn, props, pool); 310} 311 312 313svn_error_t * 314svn_repos_fs_change_rev_prop4(svn_repos_t *repos, 315 svn_revnum_t rev, 316 const char *author, 317 const char *name, 318 const svn_string_t *const *old_value_p, 319 const svn_string_t *new_value, 320 svn_boolean_t use_pre_revprop_change_hook, 321 svn_boolean_t use_post_revprop_change_hook, 322 svn_repos_authz_func_t authz_read_func, 323 void *authz_read_baton, 324 apr_pool_t *pool) 325{ 326 svn_repos_revision_access_level_t readability; 327 328 SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev, 329 authz_read_func, authz_read_baton, 330 pool)); 331 332 if (readability == svn_repos_revision_access_full) 333 { 334 const svn_string_t *old_value; 335 char action; 336 apr_hash_t *hooks_env; 337 338 SVN_ERR(svn_repos__validate_prop(name, new_value, pool)); 339 340 /* Fetch OLD_VALUE for svn_fs_change_rev_prop2(). */ 341 if (old_value_p) 342 { 343 old_value = *old_value_p; 344 } 345 else 346 { 347 /* Get OLD_VALUE anyway, in order for ACTION and OLD_VALUE arguments 348 * to the hooks to be accurate. */ 349 svn_string_t *old_value2; 350 351 SVN_ERR(svn_fs_revision_prop(&old_value2, repos->fs, rev, name, pool)); 352 old_value = old_value2; 353 } 354 355 /* Prepare ACTION. */ 356 if (! new_value) 357 action = 'D'; 358 else if (! old_value) 359 action = 'A'; 360 else 361 action = 'M'; 362 363 /* Parse the hooks-env file (if any, and if to be used). */ 364 if (use_pre_revprop_change_hook || use_post_revprop_change_hook) 365 SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path, 366 pool, pool)); 367 368 /* ### currently not passing the old_value to hooks */ 369 if (use_pre_revprop_change_hook) 370 SVN_ERR(svn_repos__hooks_pre_revprop_change(repos, hooks_env, rev, 371 author, name, new_value, 372 action, pool)); 373 374 SVN_ERR(svn_fs_change_rev_prop2(repos->fs, rev, name, 375 &old_value, new_value, pool)); 376 377 if (use_post_revprop_change_hook) 378 SVN_ERR(svn_repos__hooks_post_revprop_change(repos, hooks_env, rev, 379 author, name, old_value, 380 action, pool)); 381 } 382 else /* rev is either unreadable or only partially readable */ 383 { 384 return svn_error_createf 385 (SVN_ERR_AUTHZ_UNREADABLE, NULL, 386 _("Write denied: not authorized to read all of revision %ld"), rev); 387 } 388 389 return SVN_NO_ERROR; 390} 391 392 393svn_error_t * 394svn_repos_fs_revision_prop(svn_string_t **value_p, 395 svn_repos_t *repos, 396 svn_revnum_t rev, 397 const char *propname, 398 svn_repos_authz_func_t authz_read_func, 399 void *authz_read_baton, 400 apr_pool_t *pool) 401{ 402 svn_repos_revision_access_level_t readability; 403 404 SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev, 405 authz_read_func, authz_read_baton, 406 pool)); 407 408 if (readability == svn_repos_revision_access_none) 409 { 410 /* Property? What property? */ 411 *value_p = NULL; 412 } 413 else if (readability == svn_repos_revision_access_partial) 414 { 415 /* Only svn:author and svn:date are fetchable. */ 416 if ((strcmp(propname, SVN_PROP_REVISION_AUTHOR) != 0) 417 && (strcmp(propname, SVN_PROP_REVISION_DATE) != 0)) 418 *value_p = NULL; 419 420 else 421 SVN_ERR(svn_fs_revision_prop(value_p, repos->fs, 422 rev, propname, pool)); 423 } 424 else /* wholly readable revision */ 425 { 426 SVN_ERR(svn_fs_revision_prop(value_p, repos->fs, rev, propname, pool)); 427 } 428 429 return SVN_NO_ERROR; 430} 431 432 433 434svn_error_t * 435svn_repos_fs_revision_proplist(apr_hash_t **table_p, 436 svn_repos_t *repos, 437 svn_revnum_t rev, 438 svn_repos_authz_func_t authz_read_func, 439 void *authz_read_baton, 440 apr_pool_t *pool) 441{ 442 svn_repos_revision_access_level_t readability; 443 444 SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev, 445 authz_read_func, authz_read_baton, 446 pool)); 447 448 if (readability == svn_repos_revision_access_none) 449 { 450 /* Return an empty hash. */ 451 *table_p = apr_hash_make(pool); 452 } 453 else if (readability == svn_repos_revision_access_partial) 454 { 455 apr_hash_t *tmphash; 456 svn_string_t *value; 457 458 /* Produce two property hashtables, both in POOL. */ 459 SVN_ERR(svn_fs_revision_proplist(&tmphash, repos->fs, rev, pool)); 460 *table_p = apr_hash_make(pool); 461 462 /* If they exist, we only copy svn:author and svn:date into the 463 'real' hashtable being returned. */ 464 value = svn_hash_gets(tmphash, SVN_PROP_REVISION_AUTHOR); 465 if (value) 466 svn_hash_sets(*table_p, SVN_PROP_REVISION_AUTHOR, value); 467 468 value = svn_hash_gets(tmphash, SVN_PROP_REVISION_DATE); 469 if (value) 470 svn_hash_sets(*table_p, SVN_PROP_REVISION_DATE, value); 471 } 472 else /* wholly readable revision */ 473 { 474 SVN_ERR(svn_fs_revision_proplist(table_p, repos->fs, rev, pool)); 475 } 476 477 return SVN_NO_ERROR; 478} 479 480svn_error_t * 481svn_repos_fs_lock(svn_lock_t **lock, 482 svn_repos_t *repos, 483 const char *path, 484 const char *token, 485 const char *comment, 486 svn_boolean_t is_dav_comment, 487 apr_time_t expiration_date, 488 svn_revnum_t current_rev, 489 svn_boolean_t steal_lock, 490 apr_pool_t *pool) 491{ 492 svn_error_t *err; 493 svn_fs_access_t *access_ctx = NULL; 494 const char *username = NULL; 495 const char *new_token; 496 apr_array_header_t *paths; 497 apr_hash_t *hooks_env; 498 499 /* Parse the hooks-env file (if any). */ 500 SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path, 501 pool, pool)); 502 503 /* Setup an array of paths in anticipation of the ra layers handling 504 multiple locks in one request (1.3 most likely). This is only 505 used by svn_repos__hooks_post_lock. */ 506 paths = apr_array_make(pool, 1, sizeof(const char *)); 507 APR_ARRAY_PUSH(paths, const char *) = path; 508 509 SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs)); 510 if (access_ctx) 511 SVN_ERR(svn_fs_access_get_username(&username, access_ctx)); 512 513 if (! username) 514 return svn_error_createf 515 (SVN_ERR_FS_NO_USER, NULL, 516 "Cannot lock path '%s', no authenticated username available.", path); 517 518 /* Run pre-lock hook. This could throw error, preventing 519 svn_fs_lock() from happening. */ 520 SVN_ERR(svn_repos__hooks_pre_lock(repos, hooks_env, &new_token, path, 521 username, comment, steal_lock, pool)); 522 if (*new_token) 523 token = new_token; 524 525 /* Lock. */ 526 SVN_ERR(svn_fs_lock(lock, repos->fs, path, token, comment, is_dav_comment, 527 expiration_date, current_rev, steal_lock, pool)); 528 529 /* Run post-lock hook. */ 530 if ((err = svn_repos__hooks_post_lock(repos, hooks_env, 531 paths, username, pool))) 532 return svn_error_create 533 (SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED, err, 534 "Lock succeeded, but post-lock hook failed"); 535 536 return SVN_NO_ERROR; 537} 538 539 540svn_error_t * 541svn_repos_fs_unlock(svn_repos_t *repos, 542 const char *path, 543 const char *token, 544 svn_boolean_t break_lock, 545 apr_pool_t *pool) 546{ 547 svn_error_t *err; 548 svn_fs_access_t *access_ctx = NULL; 549 const char *username = NULL; 550 apr_array_header_t *paths; 551 apr_hash_t *hooks_env; 552 553 /* Parse the hooks-env file (if any). */ 554 SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path, 555 pool, pool)); 556 557 /* Setup an array of paths in anticipation of the ra layers handling 558 multiple locks in one request (1.3 most likely). This is only 559 used by svn_repos__hooks_post_lock. */ 560 paths = apr_array_make(pool, 1, sizeof(const char *)); 561 APR_ARRAY_PUSH(paths, const char *) = path; 562 563 SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs)); 564 if (access_ctx) 565 SVN_ERR(svn_fs_access_get_username(&username, access_ctx)); 566 567 if (! break_lock && ! username) 568 return svn_error_createf 569 (SVN_ERR_FS_NO_USER, NULL, 570 _("Cannot unlock path '%s', no authenticated username available"), 571 path); 572 573 /* Run pre-unlock hook. This could throw error, preventing 574 svn_fs_unlock() from happening. */ 575 SVN_ERR(svn_repos__hooks_pre_unlock(repos, hooks_env, path, username, token, 576 break_lock, pool)); 577 578 /* Unlock. */ 579 SVN_ERR(svn_fs_unlock(repos->fs, path, token, break_lock, pool)); 580 581 /* Run post-unlock hook. */ 582 if ((err = svn_repos__hooks_post_unlock(repos, hooks_env, paths, 583 username, pool))) 584 return svn_error_create 585 (SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED, err, 586 _("Unlock succeeded, but post-unlock hook failed")); 587 588 return SVN_NO_ERROR; 589} 590 591 592struct get_locks_baton_t 593{ 594 svn_fs_t *fs; 595 svn_fs_root_t *head_root; 596 svn_repos_authz_func_t authz_read_func; 597 void *authz_read_baton; 598 apr_hash_t *locks; 599}; 600 601 602/* This implements the svn_fs_get_locks_callback_t interface. */ 603static svn_error_t * 604get_locks_callback(void *baton, 605 svn_lock_t *lock, 606 apr_pool_t *pool) 607{ 608 struct get_locks_baton_t *b = baton; 609 svn_boolean_t readable = TRUE; 610 apr_pool_t *hash_pool = apr_hash_pool_get(b->locks); 611 612 /* If there's auth to deal with, deal with it. */ 613 if (b->authz_read_func) 614 SVN_ERR(b->authz_read_func(&readable, b->head_root, lock->path, 615 b->authz_read_baton, pool)); 616 617 /* If we can read this lock path, add the lock to the return hash. */ 618 if (readable) 619 svn_hash_sets(b->locks, apr_pstrdup(hash_pool, lock->path), 620 svn_lock_dup(lock, hash_pool)); 621 622 return SVN_NO_ERROR; 623} 624 625 626svn_error_t * 627svn_repos_fs_get_locks2(apr_hash_t **locks, 628 svn_repos_t *repos, 629 const char *path, 630 svn_depth_t depth, 631 svn_repos_authz_func_t authz_read_func, 632 void *authz_read_baton, 633 apr_pool_t *pool) 634{ 635 apr_hash_t *all_locks = apr_hash_make(pool); 636 svn_revnum_t head_rev; 637 struct get_locks_baton_t baton; 638 639 SVN_ERR_ASSERT((depth == svn_depth_empty) || 640 (depth == svn_depth_files) || 641 (depth == svn_depth_immediates) || 642 (depth == svn_depth_infinity)); 643 644 SVN_ERR(svn_fs_youngest_rev(&head_rev, repos->fs, pool)); 645 646 /* Populate our callback baton. */ 647 baton.fs = repos->fs; 648 baton.locks = all_locks; 649 baton.authz_read_func = authz_read_func; 650 baton.authz_read_baton = authz_read_baton; 651 SVN_ERR(svn_fs_revision_root(&(baton.head_root), repos->fs, 652 head_rev, pool)); 653 654 /* Get all the locks. */ 655 SVN_ERR(svn_fs_get_locks2(repos->fs, path, depth, 656 get_locks_callback, &baton, pool)); 657 658 *locks = baton.locks; 659 return SVN_NO_ERROR; 660} 661 662 663svn_error_t * 664svn_repos_fs_get_mergeinfo(svn_mergeinfo_catalog_t *mergeinfo, 665 svn_repos_t *repos, 666 const apr_array_header_t *paths, 667 svn_revnum_t rev, 668 svn_mergeinfo_inheritance_t inherit, 669 svn_boolean_t include_descendants, 670 svn_repos_authz_func_t authz_read_func, 671 void *authz_read_baton, 672 apr_pool_t *pool) 673{ 674 /* Here we cast away 'const', but won't try to write through this pointer 675 * without first allocating a new array. */ 676 apr_array_header_t *readable_paths = (apr_array_header_t *) paths; 677 svn_fs_root_t *root; 678 apr_pool_t *iterpool = svn_pool_create(pool); 679 680 if (!SVN_IS_VALID_REVNUM(rev)) 681 SVN_ERR(svn_fs_youngest_rev(&rev, repos->fs, pool)); 682 SVN_ERR(svn_fs_revision_root(&root, repos->fs, rev, pool)); 683 684 /* Filter out unreadable paths before divining merge tracking info. */ 685 if (authz_read_func) 686 { 687 int i; 688 689 for (i = 0; i < paths->nelts; i++) 690 { 691 svn_boolean_t readable; 692 const char *path = APR_ARRAY_IDX(paths, i, char *); 693 svn_pool_clear(iterpool); 694 SVN_ERR(authz_read_func(&readable, root, path, authz_read_baton, 695 iterpool)); 696 if (readable && readable_paths != paths) 697 APR_ARRAY_PUSH(readable_paths, const char *) = path; 698 else if (!readable && readable_paths == paths) 699 { 700 /* Requested paths differ from readable paths. Fork 701 list of readable paths from requested paths. */ 702 int j; 703 readable_paths = apr_array_make(pool, paths->nelts - 1, 704 sizeof(char *)); 705 for (j = 0; j < i; j++) 706 { 707 path = APR_ARRAY_IDX(paths, j, char *); 708 APR_ARRAY_PUSH(readable_paths, const char *) = path; 709 } 710 } 711 } 712 } 713 714 /* We consciously do not perform authz checks on the paths returned 715 in *MERGEINFO, avoiding massive authz overhead which would allow 716 us to protect the name of where a change was merged from, but not 717 the change itself. */ 718 /* ### TODO(reint): ... but how about descendant merged-to paths? */ 719 if (readable_paths->nelts > 0) 720 SVN_ERR(svn_fs_get_mergeinfo2(mergeinfo, root, readable_paths, inherit, 721 include_descendants, TRUE, pool, pool)); 722 else 723 *mergeinfo = apr_hash_make(pool); 724 725 svn_pool_destroy(iterpool); 726 return SVN_NO_ERROR; 727} 728 729struct pack_notify_baton 730{ 731 svn_repos_notify_func_t notify_func; 732 void *notify_baton; 733}; 734 735/* Implements svn_fs_pack_notify_t. */ 736static svn_error_t * 737pack_notify_func(void *baton, 738 apr_int64_t shard, 739 svn_fs_pack_notify_action_t pack_action, 740 apr_pool_t *pool) 741{ 742 struct pack_notify_baton *pnb = baton; 743 svn_repos_notify_t *notify; 744 745 /* Simple conversion works for these values. */ 746 SVN_ERR_ASSERT(pack_action >= svn_fs_pack_notify_start 747 && pack_action <= svn_fs_pack_notify_end_revprop); 748 749 notify = svn_repos_notify_create(pack_action 750 + svn_repos_notify_pack_shard_start 751 - svn_fs_pack_notify_start, 752 pool); 753 notify->shard = shard; 754 pnb->notify_func(pnb->notify_baton, notify, pool); 755 756 return SVN_NO_ERROR; 757} 758 759svn_error_t * 760svn_repos_fs_pack2(svn_repos_t *repos, 761 svn_repos_notify_func_t notify_func, 762 void *notify_baton, 763 svn_cancel_func_t cancel_func, 764 void *cancel_baton, 765 apr_pool_t *pool) 766{ 767 struct pack_notify_baton pnb; 768 769 pnb.notify_func = notify_func; 770 pnb.notify_baton = notify_baton; 771 772 return svn_fs_pack(repos->db_path, 773 notify_func ? pack_notify_func : NULL, 774 notify_func ? &pnb : NULL, 775 cancel_func, cancel_baton, pool); 776} 777 778svn_error_t * 779svn_repos_fs_get_inherited_props(apr_array_header_t **inherited_props_p, 780 svn_fs_root_t *root, 781 const char *path, 782 const char *propname, 783 svn_repos_authz_func_t authz_read_func, 784 void *authz_read_baton, 785 apr_pool_t *result_pool, 786 apr_pool_t *scratch_pool) 787{ 788 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 789 apr_array_header_t *inherited_props; 790 const char *parent_path = path; 791 792 inherited_props = apr_array_make(result_pool, 1, 793 sizeof(svn_prop_inherited_item_t *)); 794 while (!(parent_path[0] == '/' && parent_path[1] == '\0')) 795 { 796 svn_boolean_t allowed = TRUE; 797 apr_hash_t *parent_properties = NULL; 798 799 svn_pool_clear(iterpool); 800 parent_path = svn_fspath__dirname(parent_path, scratch_pool); 801 802 if (authz_read_func) 803 SVN_ERR(authz_read_func(&allowed, root, parent_path, 804 authz_read_baton, iterpool)); 805 if (allowed) 806 { 807 if (propname) 808 { 809 svn_string_t *propval; 810 811 SVN_ERR(svn_fs_node_prop(&propval, root, parent_path, propname, 812 result_pool)); 813 if (propval) 814 { 815 parent_properties = apr_hash_make(result_pool); 816 svn_hash_sets(parent_properties, propname, propval); 817 } 818 } 819 else 820 { 821 SVN_ERR(svn_fs_node_proplist(&parent_properties, root, 822 parent_path, result_pool)); 823 } 824 825 if (parent_properties && apr_hash_count(parent_properties)) 826 { 827 svn_prop_inherited_item_t *i_props = 828 apr_pcalloc(result_pool, sizeof(*i_props)); 829 i_props->path_or_url = 830 apr_pstrdup(result_pool, parent_path + 1); 831 i_props->prop_hash = parent_properties; 832 /* Build the output array in depth-first order. */ 833 svn_sort__array_insert(&i_props, inherited_props, 0); 834 } 835 } 836 } 837 838 svn_pool_destroy(iterpool); 839 840 *inherited_props_p = inherited_props; 841 return SVN_NO_ERROR; 842} 843 844/* 845 * vim:ts=4:sw=2:expandtab:tw=80:fo=tcroq 846 * vim:isk=a-z,A-Z,48-57,_,.,-,> 847 * vim:cino=>1s,e0,n0,f0,{.5s,}0,^-.5s,=.5s,t0,+1s,c3,(0,u0,\:0 848 */ 849