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