1251881Speter/* fs-wrap.c --- filesystem interface wrappers. 2251881Speter * 3251881Speter * ==================================================================== 4251881Speter * Licensed to the Apache Software Foundation (ASF) under one 5251881Speter * or more contributor license agreements. See the NOTICE file 6251881Speter * distributed with this work for additional information 7251881Speter * regarding copyright ownership. The ASF licenses this file 8251881Speter * to you under the Apache License, Version 2.0 (the 9251881Speter * "License"); you may not use this file except in compliance 10251881Speter * with the License. You may obtain a copy of the License at 11251881Speter * 12251881Speter * http://www.apache.org/licenses/LICENSE-2.0 13251881Speter * 14251881Speter * Unless required by applicable law or agreed to in writing, 15251881Speter * software distributed under the License is distributed on an 16251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17251881Speter * KIND, either express or implied. See the License for the 18251881Speter * specific language governing permissions and limitations 19251881Speter * under the License. 20251881Speter * ==================================================================== 21251881Speter */ 22251881Speter 23251881Speter#include <stdio.h> 24251881Speter#include <string.h> 25251881Speter#include <ctype.h> 26251881Speter 27251881Speter#include "svn_hash.h" 28251881Speter#include "svn_pools.h" 29251881Speter#include "svn_error.h" 30251881Speter#include "svn_fs.h" 31251881Speter#include "svn_path.h" 32251881Speter#include "svn_props.h" 33251881Speter#include "svn_repos.h" 34251881Speter#include "svn_time.h" 35251881Speter#include "svn_sorts.h" 36251881Speter#include "repos.h" 37251881Speter#include "svn_private_config.h" 38251881Speter#include "private/svn_repos_private.h" 39251881Speter#include "private/svn_utf_private.h" 40251881Speter#include "private/svn_fspath.h" 41251881Speter 42251881Speter 43251881Speter/*** Commit wrappers ***/ 44251881Speter 45251881Spetersvn_error_t * 46251881Spetersvn_repos_fs_commit_txn(const char **conflict_p, 47251881Speter svn_repos_t *repos, 48251881Speter svn_revnum_t *new_rev, 49251881Speter svn_fs_txn_t *txn, 50251881Speter apr_pool_t *pool) 51251881Speter{ 52251881Speter svn_error_t *err, *err2; 53251881Speter const char *txn_name; 54251881Speter apr_hash_t *props; 55251881Speter apr_pool_t *iterpool; 56251881Speter apr_hash_index_t *hi; 57251881Speter apr_hash_t *hooks_env; 58251881Speter 59251881Speter *new_rev = SVN_INVALID_REVNUM; 60251881Speter 61251881Speter /* Parse the hooks-env file (if any). */ 62251881Speter SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path, 63251881Speter pool, pool)); 64251881Speter 65251881Speter /* Run pre-commit hooks. */ 66251881Speter SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool)); 67251881Speter SVN_ERR(svn_repos__hooks_pre_commit(repos, hooks_env, txn_name, pool)); 68251881Speter 69251881Speter /* Remove any ephemeral transaction properties. */ 70251881Speter SVN_ERR(svn_fs_txn_proplist(&props, txn, pool)); 71251881Speter iterpool = svn_pool_create(pool); 72251881Speter for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) 73251881Speter { 74251881Speter const void *key; 75251881Speter apr_hash_this(hi, &key, NULL, NULL); 76251881Speter 77251881Speter svn_pool_clear(iterpool); 78251881Speter 79251881Speter if (strncmp(key, SVN_PROP_TXN_PREFIX, 80251881Speter (sizeof(SVN_PROP_TXN_PREFIX) - 1)) == 0) 81251881Speter { 82251881Speter SVN_ERR(svn_fs_change_txn_prop(txn, key, NULL, iterpool)); 83251881Speter } 84251881Speter } 85251881Speter svn_pool_destroy(iterpool); 86251881Speter 87251881Speter /* Commit. */ 88251881Speter err = svn_fs_commit_txn(conflict_p, new_rev, txn, pool); 89251881Speter if (! SVN_IS_VALID_REVNUM(*new_rev)) 90251881Speter return err; 91251881Speter 92251881Speter /* Run post-commit hooks. */ 93251881Speter if ((err2 = svn_repos__hooks_post_commit(repos, hooks_env, 94251881Speter *new_rev, txn_name, pool))) 95251881Speter { 96251881Speter err2 = svn_error_create 97251881Speter (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err2, 98251881Speter _("Commit succeeded, but post-commit hook failed")); 99251881Speter } 100251881Speter 101251881Speter return svn_error_compose_create(err, err2); 102251881Speter} 103251881Speter 104251881Speter 105251881Speter 106251881Speter/*** Transaction creation wrappers. ***/ 107251881Speter 108251881Speter 109251881Spetersvn_error_t * 110251881Spetersvn_repos_fs_begin_txn_for_commit2(svn_fs_txn_t **txn_p, 111251881Speter svn_repos_t *repos, 112251881Speter svn_revnum_t rev, 113251881Speter apr_hash_t *revprop_table, 114251881Speter apr_pool_t *pool) 115251881Speter{ 116251881Speter apr_array_header_t *revprops; 117251881Speter const char *txn_name; 118251881Speter svn_string_t *author = svn_hash_gets(revprop_table, SVN_PROP_REVISION_AUTHOR); 119251881Speter apr_hash_t *hooks_env; 120251881Speter 121251881Speter /* Parse the hooks-env file (if any). */ 122251881Speter SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path, 123251881Speter pool, pool)); 124251881Speter 125251881Speter /* Begin the transaction, ask for the fs to do on-the-fly lock checks. 126251881Speter We fetch its name, too, so the start-commit hook can use it. */ 127251881Speter SVN_ERR(svn_fs_begin_txn2(txn_p, repos->fs, rev, 128251881Speter SVN_FS_TXN_CHECK_LOCKS, pool)); 129251881Speter SVN_ERR(svn_fs_txn_name(&txn_name, *txn_p, pool)); 130251881Speter 131251881Speter /* We pass the revision properties to the filesystem by adding them 132251881Speter as properties on the txn. Later, when we commit the txn, these 133251881Speter properties will be copied into the newly created revision. */ 134251881Speter revprops = svn_prop_hash_to_array(revprop_table, pool); 135251881Speter SVN_ERR(svn_repos_fs_change_txn_props(*txn_p, revprops, pool)); 136251881Speter 137251881Speter /* Run start-commit hooks. */ 138251881Speter SVN_ERR(svn_repos__hooks_start_commit(repos, hooks_env, 139251881Speter author ? author->data : NULL, 140251881Speter repos->client_capabilities, txn_name, 141251881Speter pool)); 142251881Speter return SVN_NO_ERROR; 143251881Speter} 144251881Speter 145251881Speter 146251881Spetersvn_error_t * 147251881Spetersvn_repos_fs_begin_txn_for_commit(svn_fs_txn_t **txn_p, 148251881Speter svn_repos_t *repos, 149251881Speter svn_revnum_t rev, 150251881Speter const char *author, 151251881Speter const char *log_msg, 152251881Speter apr_pool_t *pool) 153251881Speter{ 154251881Speter apr_hash_t *revprop_table = apr_hash_make(pool); 155251881Speter if (author) 156251881Speter svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR, 157251881Speter svn_string_create(author, pool)); 158251881Speter if (log_msg) 159251881Speter svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG, 160251881Speter svn_string_create(log_msg, pool)); 161251881Speter return svn_repos_fs_begin_txn_for_commit2(txn_p, repos, rev, revprop_table, 162251881Speter pool); 163251881Speter} 164251881Speter 165251881Speter 166251881Speter/*** Property wrappers ***/ 167251881Speter 168251881Spetersvn_error_t * 169251881Spetersvn_repos__validate_prop(const char *name, 170251881Speter const svn_string_t *value, 171251881Speter apr_pool_t *pool) 172251881Speter{ 173251881Speter svn_prop_kind_t kind = svn_property_kind2(name); 174251881Speter 175253734Speter /* Allow deleting any property, even a property we don't allow to set. */ 176253734Speter if (value == NULL) 177253734Speter return SVN_NO_ERROR; 178253734Speter 179251881Speter /* Disallow setting non-regular properties. */ 180251881Speter if (kind != svn_prop_regular_kind) 181251881Speter return svn_error_createf 182251881Speter (SVN_ERR_REPOS_BAD_ARGS, NULL, 183251881Speter _("Storage of non-regular property '%s' is disallowed through the " 184251881Speter "repository interface, and could indicate a bug in your client"), 185251881Speter name); 186251881Speter 187251881Speter /* Validate "svn:" properties. */ 188251881Speter if (svn_prop_is_svn_prop(name) && value != NULL) 189251881Speter { 190251881Speter /* Validate that translated props (e.g., svn:log) are UTF-8 with 191251881Speter * LF line endings. */ 192251881Speter if (svn_prop_needs_translation(name)) 193251881Speter { 194251881Speter if (!svn_utf__is_valid(value->data, value->len)) 195251881Speter { 196251881Speter return svn_error_createf 197251881Speter (SVN_ERR_BAD_PROPERTY_VALUE, NULL, 198251881Speter _("Cannot accept '%s' property because it is not encoded in " 199251881Speter "UTF-8"), name); 200251881Speter } 201251881Speter 202251881Speter /* Disallow inconsistent line ending style, by simply looking for 203251881Speter * carriage return characters ('\r'). */ 204251881Speter if (strchr(value->data, '\r') != NULL) 205251881Speter { 206251881Speter return svn_error_createf 207251881Speter (SVN_ERR_BAD_PROPERTY_VALUE, NULL, 208251881Speter _("Cannot accept non-LF line endings in '%s' property"), 209251881Speter name); 210251881Speter } 211251881Speter } 212251881Speter 213251881Speter /* "svn:date" should be a valid date. */ 214251881Speter if (strcmp(name, SVN_PROP_REVISION_DATE) == 0) 215251881Speter { 216251881Speter apr_time_t temp; 217251881Speter svn_error_t *err; 218251881Speter 219251881Speter err = svn_time_from_cstring(&temp, value->data, pool); 220251881Speter if (err) 221251881Speter return svn_error_create(SVN_ERR_BAD_PROPERTY_VALUE, 222251881Speter err, NULL); 223251881Speter } 224251881Speter } 225251881Speter 226251881Speter return SVN_NO_ERROR; 227251881Speter} 228251881Speter 229251881Speter 230251881Speter/* Verify the mergeinfo property value VALUE and return an error if it 231251881Speter * is invalid. The PATH on which that property is set is used for error 232251881Speter * messages only. Use SCRATCH_POOL for temporary allocations. */ 233251881Speterstatic svn_error_t * 234251881Speterverify_mergeinfo(const svn_string_t *value, 235251881Speter const char *path, 236251881Speter apr_pool_t *scratch_pool) 237251881Speter{ 238251881Speter svn_error_t *err; 239251881Speter svn_mergeinfo_t mergeinfo; 240251881Speter 241251881Speter /* It's okay to delete svn:mergeinfo. */ 242251881Speter if (value == NULL) 243251881Speter return SVN_NO_ERROR; 244251881Speter 245251881Speter /* Mergeinfo is UTF-8 encoded so the number of bytes returned by strlen() 246251881Speter * should match VALUE->LEN. Prevents trailing garbage in the property. */ 247251881Speter if (strlen(value->data) != value->len) 248251881Speter return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 249251881Speter _("Commit rejected because mergeinfo on '%s' " 250251881Speter "contains unexpected string terminator"), 251251881Speter path); 252251881Speter 253251881Speter err = svn_mergeinfo_parse(&mergeinfo, value->data, scratch_pool); 254251881Speter if (err) 255251881Speter return svn_error_createf(err->apr_err, err, 256251881Speter _("Commit rejected because mergeinfo on '%s' " 257251881Speter "is syntactically invalid"), 258251881Speter path); 259251881Speter return SVN_NO_ERROR; 260251881Speter} 261251881Speter 262251881Speter 263251881Spetersvn_error_t * 264251881Spetersvn_repos_fs_change_node_prop(svn_fs_root_t *root, 265251881Speter const char *path, 266251881Speter const char *name, 267251881Speter const svn_string_t *value, 268251881Speter apr_pool_t *pool) 269251881Speter{ 270251881Speter if (value && strcmp(name, SVN_PROP_MERGEINFO) == 0) 271251881Speter SVN_ERR(verify_mergeinfo(value, path, pool)); 272251881Speter 273251881Speter /* Validate the property, then call the wrapped function. */ 274251881Speter SVN_ERR(svn_repos__validate_prop(name, value, pool)); 275251881Speter return svn_fs_change_node_prop(root, path, name, value, pool); 276251881Speter} 277251881Speter 278251881Speter 279251881Spetersvn_error_t * 280251881Spetersvn_repos_fs_change_txn_props(svn_fs_txn_t *txn, 281251881Speter const apr_array_header_t *txnprops, 282251881Speter apr_pool_t *pool) 283251881Speter{ 284251881Speter int i; 285251881Speter 286251881Speter for (i = 0; i < txnprops->nelts; i++) 287251881Speter { 288251881Speter svn_prop_t *prop = &APR_ARRAY_IDX(txnprops, i, svn_prop_t); 289251881Speter SVN_ERR(svn_repos__validate_prop(prop->name, prop->value, pool)); 290251881Speter } 291251881Speter 292251881Speter return svn_fs_change_txn_props(txn, txnprops, pool); 293251881Speter} 294251881Speter 295251881Speter 296251881Spetersvn_error_t * 297251881Spetersvn_repos_fs_change_txn_prop(svn_fs_txn_t *txn, 298251881Speter const char *name, 299251881Speter const svn_string_t *value, 300251881Speter apr_pool_t *pool) 301251881Speter{ 302251881Speter apr_array_header_t *props = apr_array_make(pool, 1, sizeof(svn_prop_t)); 303251881Speter svn_prop_t prop; 304251881Speter 305251881Speter prop.name = name; 306251881Speter prop.value = value; 307251881Speter APR_ARRAY_PUSH(props, svn_prop_t) = prop; 308251881Speter 309251881Speter return svn_repos_fs_change_txn_props(txn, props, pool); 310251881Speter} 311251881Speter 312251881Speter 313251881Spetersvn_error_t * 314251881Spetersvn_repos_fs_change_rev_prop4(svn_repos_t *repos, 315251881Speter svn_revnum_t rev, 316251881Speter const char *author, 317251881Speter const char *name, 318251881Speter const svn_string_t *const *old_value_p, 319251881Speter const svn_string_t *new_value, 320251881Speter svn_boolean_t use_pre_revprop_change_hook, 321251881Speter svn_boolean_t use_post_revprop_change_hook, 322251881Speter svn_repos_authz_func_t authz_read_func, 323251881Speter void *authz_read_baton, 324251881Speter apr_pool_t *pool) 325251881Speter{ 326251881Speter svn_repos_revision_access_level_t readability; 327251881Speter 328251881Speter SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev, 329251881Speter authz_read_func, authz_read_baton, 330251881Speter pool)); 331251881Speter 332251881Speter if (readability == svn_repos_revision_access_full) 333251881Speter { 334251881Speter const svn_string_t *old_value; 335251881Speter char action; 336251881Speter apr_hash_t *hooks_env; 337251881Speter 338251881Speter SVN_ERR(svn_repos__validate_prop(name, new_value, pool)); 339251881Speter 340251881Speter /* Fetch OLD_VALUE for svn_fs_change_rev_prop2(). */ 341251881Speter if (old_value_p) 342251881Speter { 343251881Speter old_value = *old_value_p; 344251881Speter } 345251881Speter else 346251881Speter { 347251881Speter /* Get OLD_VALUE anyway, in order for ACTION and OLD_VALUE arguments 348251881Speter * to the hooks to be accurate. */ 349251881Speter svn_string_t *old_value2; 350251881Speter 351251881Speter SVN_ERR(svn_fs_revision_prop(&old_value2, repos->fs, rev, name, pool)); 352251881Speter old_value = old_value2; 353251881Speter } 354251881Speter 355251881Speter /* Prepare ACTION. */ 356251881Speter if (! new_value) 357251881Speter action = 'D'; 358251881Speter else if (! old_value) 359251881Speter action = 'A'; 360251881Speter else 361251881Speter action = 'M'; 362251881Speter 363251881Speter /* Parse the hooks-env file (if any, and if to be used). */ 364251881Speter if (use_pre_revprop_change_hook || use_post_revprop_change_hook) 365251881Speter SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path, 366251881Speter pool, pool)); 367251881Speter 368251881Speter /* ### currently not passing the old_value to hooks */ 369251881Speter if (use_pre_revprop_change_hook) 370251881Speter SVN_ERR(svn_repos__hooks_pre_revprop_change(repos, hooks_env, rev, 371251881Speter author, name, new_value, 372251881Speter action, pool)); 373251881Speter 374251881Speter SVN_ERR(svn_fs_change_rev_prop2(repos->fs, rev, name, 375251881Speter &old_value, new_value, pool)); 376251881Speter 377251881Speter if (use_post_revprop_change_hook) 378251881Speter SVN_ERR(svn_repos__hooks_post_revprop_change(repos, hooks_env, rev, 379251881Speter author, name, old_value, 380251881Speter action, pool)); 381251881Speter } 382251881Speter else /* rev is either unreadable or only partially readable */ 383251881Speter { 384251881Speter return svn_error_createf 385251881Speter (SVN_ERR_AUTHZ_UNREADABLE, NULL, 386251881Speter _("Write denied: not authorized to read all of revision %ld"), rev); 387251881Speter } 388251881Speter 389251881Speter return SVN_NO_ERROR; 390251881Speter} 391251881Speter 392251881Speter 393251881Spetersvn_error_t * 394251881Spetersvn_repos_fs_revision_prop(svn_string_t **value_p, 395251881Speter svn_repos_t *repos, 396251881Speter svn_revnum_t rev, 397251881Speter const char *propname, 398251881Speter svn_repos_authz_func_t authz_read_func, 399251881Speter void *authz_read_baton, 400251881Speter apr_pool_t *pool) 401251881Speter{ 402251881Speter svn_repos_revision_access_level_t readability; 403251881Speter 404251881Speter SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev, 405251881Speter authz_read_func, authz_read_baton, 406251881Speter pool)); 407251881Speter 408251881Speter if (readability == svn_repos_revision_access_none) 409251881Speter { 410251881Speter /* Property? What property? */ 411251881Speter *value_p = NULL; 412251881Speter } 413251881Speter else if (readability == svn_repos_revision_access_partial) 414251881Speter { 415251881Speter /* Only svn:author and svn:date are fetchable. */ 416251881Speter if ((strcmp(propname, SVN_PROP_REVISION_AUTHOR) != 0) 417251881Speter && (strcmp(propname, SVN_PROP_REVISION_DATE) != 0)) 418251881Speter *value_p = NULL; 419251881Speter 420251881Speter else 421251881Speter SVN_ERR(svn_fs_revision_prop(value_p, repos->fs, 422251881Speter rev, propname, pool)); 423251881Speter } 424251881Speter else /* wholly readable revision */ 425251881Speter { 426251881Speter SVN_ERR(svn_fs_revision_prop(value_p, repos->fs, rev, propname, pool)); 427251881Speter } 428251881Speter 429251881Speter return SVN_NO_ERROR; 430251881Speter} 431251881Speter 432251881Speter 433251881Speter 434251881Spetersvn_error_t * 435251881Spetersvn_repos_fs_revision_proplist(apr_hash_t **table_p, 436251881Speter svn_repos_t *repos, 437251881Speter svn_revnum_t rev, 438251881Speter svn_repos_authz_func_t authz_read_func, 439251881Speter void *authz_read_baton, 440251881Speter apr_pool_t *pool) 441251881Speter{ 442251881Speter svn_repos_revision_access_level_t readability; 443251881Speter 444251881Speter SVN_ERR(svn_repos_check_revision_access(&readability, repos, rev, 445251881Speter authz_read_func, authz_read_baton, 446251881Speter pool)); 447251881Speter 448251881Speter if (readability == svn_repos_revision_access_none) 449251881Speter { 450251881Speter /* Return an empty hash. */ 451251881Speter *table_p = apr_hash_make(pool); 452251881Speter } 453251881Speter else if (readability == svn_repos_revision_access_partial) 454251881Speter { 455251881Speter apr_hash_t *tmphash; 456251881Speter svn_string_t *value; 457251881Speter 458251881Speter /* Produce two property hashtables, both in POOL. */ 459251881Speter SVN_ERR(svn_fs_revision_proplist(&tmphash, repos->fs, rev, pool)); 460251881Speter *table_p = apr_hash_make(pool); 461251881Speter 462251881Speter /* If they exist, we only copy svn:author and svn:date into the 463251881Speter 'real' hashtable being returned. */ 464251881Speter value = svn_hash_gets(tmphash, SVN_PROP_REVISION_AUTHOR); 465251881Speter if (value) 466251881Speter svn_hash_sets(*table_p, SVN_PROP_REVISION_AUTHOR, value); 467251881Speter 468251881Speter value = svn_hash_gets(tmphash, SVN_PROP_REVISION_DATE); 469251881Speter if (value) 470251881Speter svn_hash_sets(*table_p, SVN_PROP_REVISION_DATE, value); 471251881Speter } 472251881Speter else /* wholly readable revision */ 473251881Speter { 474251881Speter SVN_ERR(svn_fs_revision_proplist(table_p, repos->fs, rev, pool)); 475251881Speter } 476251881Speter 477251881Speter return SVN_NO_ERROR; 478251881Speter} 479251881Speter 480251881Spetersvn_error_t * 481251881Spetersvn_repos_fs_lock(svn_lock_t **lock, 482251881Speter svn_repos_t *repos, 483251881Speter const char *path, 484251881Speter const char *token, 485251881Speter const char *comment, 486251881Speter svn_boolean_t is_dav_comment, 487251881Speter apr_time_t expiration_date, 488251881Speter svn_revnum_t current_rev, 489251881Speter svn_boolean_t steal_lock, 490251881Speter apr_pool_t *pool) 491251881Speter{ 492251881Speter svn_error_t *err; 493251881Speter svn_fs_access_t *access_ctx = NULL; 494251881Speter const char *username = NULL; 495251881Speter const char *new_token; 496251881Speter apr_array_header_t *paths; 497251881Speter apr_hash_t *hooks_env; 498251881Speter 499251881Speter /* Parse the hooks-env file (if any). */ 500251881Speter SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path, 501251881Speter pool, pool)); 502251881Speter 503251881Speter /* Setup an array of paths in anticipation of the ra layers handling 504251881Speter multiple locks in one request (1.3 most likely). This is only 505251881Speter used by svn_repos__hooks_post_lock. */ 506251881Speter paths = apr_array_make(pool, 1, sizeof(const char *)); 507251881Speter APR_ARRAY_PUSH(paths, const char *) = path; 508251881Speter 509251881Speter SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs)); 510251881Speter if (access_ctx) 511251881Speter SVN_ERR(svn_fs_access_get_username(&username, access_ctx)); 512251881Speter 513251881Speter if (! username) 514251881Speter return svn_error_createf 515251881Speter (SVN_ERR_FS_NO_USER, NULL, 516251881Speter "Cannot lock path '%s', no authenticated username available.", path); 517251881Speter 518251881Speter /* Run pre-lock hook. This could throw error, preventing 519251881Speter svn_fs_lock() from happening. */ 520251881Speter SVN_ERR(svn_repos__hooks_pre_lock(repos, hooks_env, &new_token, path, 521251881Speter username, comment, steal_lock, pool)); 522251881Speter if (*new_token) 523251881Speter token = new_token; 524251881Speter 525251881Speter /* Lock. */ 526251881Speter SVN_ERR(svn_fs_lock(lock, repos->fs, path, token, comment, is_dav_comment, 527251881Speter expiration_date, current_rev, steal_lock, pool)); 528251881Speter 529251881Speter /* Run post-lock hook. */ 530251881Speter if ((err = svn_repos__hooks_post_lock(repos, hooks_env, 531251881Speter paths, username, pool))) 532251881Speter return svn_error_create 533251881Speter (SVN_ERR_REPOS_POST_LOCK_HOOK_FAILED, err, 534251881Speter "Lock succeeded, but post-lock hook failed"); 535251881Speter 536251881Speter return SVN_NO_ERROR; 537251881Speter} 538251881Speter 539251881Speter 540251881Spetersvn_error_t * 541251881Spetersvn_repos_fs_unlock(svn_repos_t *repos, 542251881Speter const char *path, 543251881Speter const char *token, 544251881Speter svn_boolean_t break_lock, 545251881Speter apr_pool_t *pool) 546251881Speter{ 547251881Speter svn_error_t *err; 548251881Speter svn_fs_access_t *access_ctx = NULL; 549251881Speter const char *username = NULL; 550251881Speter apr_array_header_t *paths; 551251881Speter apr_hash_t *hooks_env; 552251881Speter 553251881Speter /* Parse the hooks-env file (if any). */ 554251881Speter SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, repos->hooks_env_path, 555251881Speter pool, pool)); 556251881Speter 557251881Speter /* Setup an array of paths in anticipation of the ra layers handling 558251881Speter multiple locks in one request (1.3 most likely). This is only 559251881Speter used by svn_repos__hooks_post_lock. */ 560251881Speter paths = apr_array_make(pool, 1, sizeof(const char *)); 561251881Speter APR_ARRAY_PUSH(paths, const char *) = path; 562251881Speter 563251881Speter SVN_ERR(svn_fs_get_access(&access_ctx, repos->fs)); 564251881Speter if (access_ctx) 565251881Speter SVN_ERR(svn_fs_access_get_username(&username, access_ctx)); 566251881Speter 567251881Speter if (! break_lock && ! username) 568251881Speter return svn_error_createf 569251881Speter (SVN_ERR_FS_NO_USER, NULL, 570251881Speter _("Cannot unlock path '%s', no authenticated username available"), 571251881Speter path); 572251881Speter 573251881Speter /* Run pre-unlock hook. This could throw error, preventing 574251881Speter svn_fs_unlock() from happening. */ 575251881Speter SVN_ERR(svn_repos__hooks_pre_unlock(repos, hooks_env, path, username, token, 576251881Speter break_lock, pool)); 577251881Speter 578251881Speter /* Unlock. */ 579251881Speter SVN_ERR(svn_fs_unlock(repos->fs, path, token, break_lock, pool)); 580251881Speter 581251881Speter /* Run post-unlock hook. */ 582251881Speter if ((err = svn_repos__hooks_post_unlock(repos, hooks_env, paths, 583251881Speter username, pool))) 584251881Speter return svn_error_create 585251881Speter (SVN_ERR_REPOS_POST_UNLOCK_HOOK_FAILED, err, 586251881Speter _("Unlock succeeded, but post-unlock hook failed")); 587251881Speter 588251881Speter return SVN_NO_ERROR; 589251881Speter} 590251881Speter 591251881Speter 592251881Speterstruct get_locks_baton_t 593251881Speter{ 594251881Speter svn_fs_t *fs; 595251881Speter svn_fs_root_t *head_root; 596251881Speter svn_repos_authz_func_t authz_read_func; 597251881Speter void *authz_read_baton; 598251881Speter apr_hash_t *locks; 599251881Speter}; 600251881Speter 601251881Speter 602251881Speter/* This implements the svn_fs_get_locks_callback_t interface. */ 603251881Speterstatic svn_error_t * 604251881Speterget_locks_callback(void *baton, 605251881Speter svn_lock_t *lock, 606251881Speter apr_pool_t *pool) 607251881Speter{ 608251881Speter struct get_locks_baton_t *b = baton; 609251881Speter svn_boolean_t readable = TRUE; 610251881Speter apr_pool_t *hash_pool = apr_hash_pool_get(b->locks); 611251881Speter 612251881Speter /* If there's auth to deal with, deal with it. */ 613251881Speter if (b->authz_read_func) 614251881Speter SVN_ERR(b->authz_read_func(&readable, b->head_root, lock->path, 615251881Speter b->authz_read_baton, pool)); 616251881Speter 617251881Speter /* If we can read this lock path, add the lock to the return hash. */ 618251881Speter if (readable) 619251881Speter svn_hash_sets(b->locks, apr_pstrdup(hash_pool, lock->path), 620251881Speter svn_lock_dup(lock, hash_pool)); 621251881Speter 622251881Speter return SVN_NO_ERROR; 623251881Speter} 624251881Speter 625251881Speter 626251881Spetersvn_error_t * 627251881Spetersvn_repos_fs_get_locks2(apr_hash_t **locks, 628251881Speter svn_repos_t *repos, 629251881Speter const char *path, 630251881Speter svn_depth_t depth, 631251881Speter svn_repos_authz_func_t authz_read_func, 632251881Speter void *authz_read_baton, 633251881Speter apr_pool_t *pool) 634251881Speter{ 635251881Speter apr_hash_t *all_locks = apr_hash_make(pool); 636251881Speter svn_revnum_t head_rev; 637251881Speter struct get_locks_baton_t baton; 638251881Speter 639251881Speter SVN_ERR_ASSERT((depth == svn_depth_empty) || 640251881Speter (depth == svn_depth_files) || 641251881Speter (depth == svn_depth_immediates) || 642251881Speter (depth == svn_depth_infinity)); 643251881Speter 644251881Speter SVN_ERR(svn_fs_youngest_rev(&head_rev, repos->fs, pool)); 645251881Speter 646251881Speter /* Populate our callback baton. */ 647251881Speter baton.fs = repos->fs; 648251881Speter baton.locks = all_locks; 649251881Speter baton.authz_read_func = authz_read_func; 650251881Speter baton.authz_read_baton = authz_read_baton; 651251881Speter SVN_ERR(svn_fs_revision_root(&(baton.head_root), repos->fs, 652251881Speter head_rev, pool)); 653251881Speter 654251881Speter /* Get all the locks. */ 655251881Speter SVN_ERR(svn_fs_get_locks2(repos->fs, path, depth, 656251881Speter get_locks_callback, &baton, pool)); 657251881Speter 658251881Speter *locks = baton.locks; 659251881Speter return SVN_NO_ERROR; 660251881Speter} 661251881Speter 662251881Speter 663251881Spetersvn_error_t * 664251881Spetersvn_repos_fs_get_mergeinfo(svn_mergeinfo_catalog_t *mergeinfo, 665251881Speter svn_repos_t *repos, 666251881Speter const apr_array_header_t *paths, 667251881Speter svn_revnum_t rev, 668251881Speter svn_mergeinfo_inheritance_t inherit, 669251881Speter svn_boolean_t include_descendants, 670251881Speter svn_repos_authz_func_t authz_read_func, 671251881Speter void *authz_read_baton, 672251881Speter apr_pool_t *pool) 673251881Speter{ 674251881Speter /* Here we cast away 'const', but won't try to write through this pointer 675251881Speter * without first allocating a new array. */ 676251881Speter apr_array_header_t *readable_paths = (apr_array_header_t *) paths; 677251881Speter svn_fs_root_t *root; 678251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 679251881Speter 680251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 681251881Speter SVN_ERR(svn_fs_youngest_rev(&rev, repos->fs, pool)); 682251881Speter SVN_ERR(svn_fs_revision_root(&root, repos->fs, rev, pool)); 683251881Speter 684251881Speter /* Filter out unreadable paths before divining merge tracking info. */ 685251881Speter if (authz_read_func) 686251881Speter { 687251881Speter int i; 688251881Speter 689251881Speter for (i = 0; i < paths->nelts; i++) 690251881Speter { 691251881Speter svn_boolean_t readable; 692251881Speter const char *path = APR_ARRAY_IDX(paths, i, char *); 693251881Speter svn_pool_clear(iterpool); 694251881Speter SVN_ERR(authz_read_func(&readable, root, path, authz_read_baton, 695251881Speter iterpool)); 696251881Speter if (readable && readable_paths != paths) 697251881Speter APR_ARRAY_PUSH(readable_paths, const char *) = path; 698251881Speter else if (!readable && readable_paths == paths) 699251881Speter { 700251881Speter /* Requested paths differ from readable paths. Fork 701251881Speter list of readable paths from requested paths. */ 702251881Speter int j; 703251881Speter readable_paths = apr_array_make(pool, paths->nelts - 1, 704251881Speter sizeof(char *)); 705251881Speter for (j = 0; j < i; j++) 706251881Speter { 707251881Speter path = APR_ARRAY_IDX(paths, j, char *); 708251881Speter APR_ARRAY_PUSH(readable_paths, const char *) = path; 709251881Speter } 710251881Speter } 711251881Speter } 712251881Speter } 713251881Speter 714251881Speter /* We consciously do not perform authz checks on the paths returned 715251881Speter in *MERGEINFO, avoiding massive authz overhead which would allow 716251881Speter us to protect the name of where a change was merged from, but not 717251881Speter the change itself. */ 718251881Speter /* ### TODO(reint): ... but how about descendant merged-to paths? */ 719251881Speter if (readable_paths->nelts > 0) 720251881Speter SVN_ERR(svn_fs_get_mergeinfo2(mergeinfo, root, readable_paths, inherit, 721251881Speter include_descendants, TRUE, pool, pool)); 722251881Speter else 723251881Speter *mergeinfo = apr_hash_make(pool); 724251881Speter 725251881Speter svn_pool_destroy(iterpool); 726251881Speter return SVN_NO_ERROR; 727251881Speter} 728251881Speter 729251881Speterstruct pack_notify_baton 730251881Speter{ 731251881Speter svn_repos_notify_func_t notify_func; 732251881Speter void *notify_baton; 733251881Speter}; 734251881Speter 735251881Speter/* Implements svn_fs_pack_notify_t. */ 736251881Speterstatic svn_error_t * 737251881Speterpack_notify_func(void *baton, 738251881Speter apr_int64_t shard, 739251881Speter svn_fs_pack_notify_action_t pack_action, 740251881Speter apr_pool_t *pool) 741251881Speter{ 742251881Speter struct pack_notify_baton *pnb = baton; 743251881Speter svn_repos_notify_t *notify; 744251881Speter 745251881Speter /* Simple conversion works for these values. */ 746251881Speter SVN_ERR_ASSERT(pack_action >= svn_fs_pack_notify_start 747251881Speter && pack_action <= svn_fs_pack_notify_end_revprop); 748251881Speter 749251881Speter notify = svn_repos_notify_create(pack_action 750251881Speter + svn_repos_notify_pack_shard_start 751251881Speter - svn_fs_pack_notify_start, 752251881Speter pool); 753251881Speter notify->shard = shard; 754251881Speter pnb->notify_func(pnb->notify_baton, notify, pool); 755251881Speter 756251881Speter return SVN_NO_ERROR; 757251881Speter} 758251881Speter 759251881Spetersvn_error_t * 760251881Spetersvn_repos_fs_pack2(svn_repos_t *repos, 761251881Speter svn_repos_notify_func_t notify_func, 762251881Speter void *notify_baton, 763251881Speter svn_cancel_func_t cancel_func, 764251881Speter void *cancel_baton, 765251881Speter apr_pool_t *pool) 766251881Speter{ 767251881Speter struct pack_notify_baton pnb; 768251881Speter 769251881Speter pnb.notify_func = notify_func; 770251881Speter pnb.notify_baton = notify_baton; 771251881Speter 772251881Speter return svn_fs_pack(repos->db_path, 773251881Speter notify_func ? pack_notify_func : NULL, 774251881Speter notify_func ? &pnb : NULL, 775251881Speter cancel_func, cancel_baton, pool); 776251881Speter} 777251881Speter 778251881Spetersvn_error_t * 779251881Spetersvn_repos_fs_get_inherited_props(apr_array_header_t **inherited_props_p, 780251881Speter svn_fs_root_t *root, 781251881Speter const char *path, 782251881Speter const char *propname, 783251881Speter svn_repos_authz_func_t authz_read_func, 784251881Speter void *authz_read_baton, 785251881Speter apr_pool_t *result_pool, 786251881Speter apr_pool_t *scratch_pool) 787251881Speter{ 788251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 789251881Speter apr_array_header_t *inherited_props; 790251881Speter const char *parent_path = path; 791251881Speter 792251881Speter inherited_props = apr_array_make(result_pool, 1, 793251881Speter sizeof(svn_prop_inherited_item_t *)); 794251881Speter while (!(parent_path[0] == '/' && parent_path[1] == '\0')) 795251881Speter { 796251881Speter svn_boolean_t allowed = TRUE; 797251881Speter apr_hash_t *parent_properties = NULL; 798251881Speter 799251881Speter svn_pool_clear(iterpool); 800251881Speter parent_path = svn_fspath__dirname(parent_path, scratch_pool); 801251881Speter 802251881Speter if (authz_read_func) 803251881Speter SVN_ERR(authz_read_func(&allowed, root, parent_path, 804251881Speter authz_read_baton, iterpool)); 805251881Speter if (allowed) 806251881Speter { 807251881Speter if (propname) 808251881Speter { 809251881Speter svn_string_t *propval; 810251881Speter 811251881Speter SVN_ERR(svn_fs_node_prop(&propval, root, parent_path, propname, 812251881Speter result_pool)); 813251881Speter if (propval) 814251881Speter { 815251881Speter parent_properties = apr_hash_make(result_pool); 816251881Speter svn_hash_sets(parent_properties, propname, propval); 817251881Speter } 818251881Speter } 819251881Speter else 820251881Speter { 821251881Speter SVN_ERR(svn_fs_node_proplist(&parent_properties, root, 822251881Speter parent_path, result_pool)); 823251881Speter } 824251881Speter 825251881Speter if (parent_properties && apr_hash_count(parent_properties)) 826251881Speter { 827251881Speter svn_prop_inherited_item_t *i_props = 828251881Speter apr_pcalloc(result_pool, sizeof(*i_props)); 829251881Speter i_props->path_or_url = 830251881Speter apr_pstrdup(result_pool, parent_path + 1); 831251881Speter i_props->prop_hash = parent_properties; 832251881Speter /* Build the output array in depth-first order. */ 833251881Speter svn_sort__array_insert(&i_props, inherited_props, 0); 834251881Speter } 835251881Speter } 836251881Speter } 837251881Speter 838251881Speter svn_pool_destroy(iterpool); 839251881Speter 840251881Speter *inherited_props_p = inherited_props; 841251881Speter return SVN_NO_ERROR; 842251881Speter} 843251881Speter 844251881Speter/* 845251881Speter * vim:ts=4:sw=2:expandtab:tw=80:fo=tcroq 846251881Speter * vim:isk=a-z,A-Z,48-57,_,.,-,> 847251881Speter * vim:cino=>1s,e0,n0,f0,{.5s,}0,^-.5s,=.5s,t0,+1s,c3,(0,u0,\:0 848251881Speter */ 849