1/* repos.c : repository creation; shared and exclusive repository locking 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 <apr_pools.h> 24#include <apr_file_io.h> 25 26#include "svn_pools.h" 27#include "svn_error.h" 28#include "svn_dirent_uri.h" 29#include "svn_path.h" 30#include "svn_utf.h" 31#include "svn_time.h" 32#include "svn_fs.h" 33#include "svn_ra.h" /* for SVN_RA_CAPABILITY_* */ 34#include "svn_repos.h" 35#include "svn_hash.h" 36#include "svn_version.h" 37#include "svn_config.h" 38 39#include "private/svn_repos_private.h" 40#include "private/svn_subr_private.h" 41#include "svn_private_config.h" /* for SVN_TEMPLATE_ROOT_DIR */ 42 43#include "repos.h" 44 45/* Used to terminate lines in large multi-line string literals. */ 46#define NL APR_EOL_STR 47 48 49/* Path accessor functions. */ 50 51 52const char * 53svn_repos_path(svn_repos_t *repos, apr_pool_t *pool) 54{ 55 return apr_pstrdup(pool, repos->path); 56} 57 58 59const char * 60svn_repos_db_env(svn_repos_t *repos, apr_pool_t *pool) 61{ 62 return apr_pstrdup(pool, repos->db_path); 63} 64 65 66const char * 67svn_repos_conf_dir(svn_repos_t *repos, apr_pool_t *pool) 68{ 69 return apr_pstrdup(pool, repos->conf_path); 70} 71 72 73const char * 74svn_repos_svnserve_conf(svn_repos_t *repos, apr_pool_t *pool) 75{ 76 return svn_dirent_join(repos->conf_path, SVN_REPOS__CONF_SVNSERVE_CONF, pool); 77} 78 79 80const char * 81svn_repos_lock_dir(svn_repos_t *repos, apr_pool_t *pool) 82{ 83 return apr_pstrdup(pool, repos->lock_path); 84} 85 86 87const char * 88svn_repos_db_lockfile(svn_repos_t *repos, apr_pool_t *pool) 89{ 90 return svn_dirent_join(repos->lock_path, SVN_REPOS__DB_LOCKFILE, pool); 91} 92 93 94const char * 95svn_repos_db_logs_lockfile(svn_repos_t *repos, apr_pool_t *pool) 96{ 97 return svn_dirent_join(repos->lock_path, SVN_REPOS__DB_LOGS_LOCKFILE, pool); 98} 99 100const char * 101svn_repos_hook_dir(svn_repos_t *repos, apr_pool_t *pool) 102{ 103 return apr_pstrdup(pool, repos->hook_path); 104} 105 106 107const char * 108svn_repos_start_commit_hook(svn_repos_t *repos, apr_pool_t *pool) 109{ 110 return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_START_COMMIT, pool); 111} 112 113 114const char * 115svn_repos_pre_commit_hook(svn_repos_t *repos, apr_pool_t *pool) 116{ 117 return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_PRE_COMMIT, pool); 118} 119 120 121const char * 122svn_repos_pre_lock_hook(svn_repos_t *repos, apr_pool_t *pool) 123{ 124 return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_PRE_LOCK, pool); 125} 126 127 128const char * 129svn_repos_pre_unlock_hook(svn_repos_t *repos, apr_pool_t *pool) 130{ 131 return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_PRE_UNLOCK, pool); 132} 133 134const char * 135svn_repos_post_lock_hook(svn_repos_t *repos, apr_pool_t *pool) 136{ 137 return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_POST_LOCK, pool); 138} 139 140 141const char * 142svn_repos_post_unlock_hook(svn_repos_t *repos, apr_pool_t *pool) 143{ 144 return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_POST_UNLOCK, pool); 145} 146 147 148const char * 149svn_repos_post_commit_hook(svn_repos_t *repos, apr_pool_t *pool) 150{ 151 return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_POST_COMMIT, pool); 152} 153 154 155const char * 156svn_repos_pre_revprop_change_hook(svn_repos_t *repos, apr_pool_t *pool) 157{ 158 return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_PRE_REVPROP_CHANGE, 159 pool); 160} 161 162 163const char * 164svn_repos_post_revprop_change_hook(svn_repos_t *repos, apr_pool_t *pool) 165{ 166 return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_POST_REVPROP_CHANGE, 167 pool); 168} 169 170static svn_error_t * 171create_repos_dir(const char *path, apr_pool_t *pool) 172{ 173 svn_error_t *err; 174 175 err = svn_io_dir_make(path, APR_OS_DEFAULT, pool); 176 if (err && (APR_STATUS_IS_EEXIST(err->apr_err))) 177 { 178 svn_boolean_t is_empty; 179 180 svn_error_clear(err); 181 182 SVN_ERR(svn_io_dir_empty(&is_empty, path, pool)); 183 184 if (is_empty) 185 err = NULL; 186 else 187 err = svn_error_createf(SVN_ERR_DIR_NOT_EMPTY, 0, 188 _("'%s' exists and is non-empty"), 189 svn_dirent_local_style(path, pool)); 190 } 191 192 return svn_error_trace(err); 193} 194 195static const char * bdb_lock_file_contents = 196 "DB lock file, representing locks on the versioned filesystem." NL 197 "" NL 198 "All accessors -- both readers and writers -- of the repository's" NL 199 "Berkeley DB environment take out shared locks on this file, and" NL 200 "each accessor removes its lock when done. If and when the DB" NL 201 "recovery procedure is run, the recovery code takes out an" NL 202 "exclusive lock on this file, so we can be sure no one else is" NL 203 "using the DB during the recovery." NL 204 "" NL 205 "You should never have to edit or remove this file." NL; 206 207static const char * bdb_logs_lock_file_contents = 208 "DB logs lock file, representing locks on the versioned filesystem logs." NL 209 "" NL 210 "All log manipulators of the repository's Berkeley DB environment" NL 211 "take out exclusive locks on this file to ensure that only one" NL 212 "accessor manipulates the logs at a time." NL 213 "" NL 214 "You should never have to edit or remove this file." NL; 215 216static const char * pre12_compat_unneeded_file_contents = 217 "This file is not used by Subversion 1.3.x or later." NL 218 "However, its existence is required for compatibility with" NL 219 "Subversion 1.2.x or earlier." NL; 220 221/* Create the DB logs lockfile. */ 222static svn_error_t * 223create_db_logs_lock(svn_repos_t *repos, apr_pool_t *pool) { 224 const char *contents; 225 const char *lockfile_path; 226 227 lockfile_path = svn_repos_db_logs_lockfile(repos, pool); 228 if (strcmp(repos->fs_type, SVN_FS_TYPE_BDB) == 0) 229 contents = bdb_logs_lock_file_contents; 230 else 231 contents = pre12_compat_unneeded_file_contents; 232 233 SVN_ERR_W(svn_io_file_create(lockfile_path, contents, pool), 234 _("Creating db logs lock file")); 235 236 return SVN_NO_ERROR; 237} 238 239/* Create the DB lockfile. */ 240static svn_error_t * 241create_db_lock(svn_repos_t *repos, apr_pool_t *pool) { 242 const char *contents; 243 const char *lockfile_path; 244 245 lockfile_path = svn_repos_db_lockfile(repos, pool); 246 if (strcmp(repos->fs_type, SVN_FS_TYPE_BDB) == 0) 247 contents = bdb_lock_file_contents; 248 else 249 contents = pre12_compat_unneeded_file_contents; 250 251 SVN_ERR_W(svn_io_file_create(lockfile_path, contents, pool), 252 _("Creating db lock file")); 253 254 return SVN_NO_ERROR; 255} 256 257static svn_error_t * 258create_locks(svn_repos_t *repos, apr_pool_t *pool) 259{ 260 /* Create the locks directory. */ 261 SVN_ERR_W(create_repos_dir(repos->lock_path, pool), 262 _("Creating lock dir")); 263 264 SVN_ERR(create_db_lock(repos, pool)); 265 return create_db_logs_lock(repos, pool); 266} 267 268 269#define HOOKS_ENVIRONMENT_TEXT \ 270 "# The hook program typically does not inherit the environment of" NL \ 271 "# its parent process. For example, a common problem is for the" NL \ 272 "# PATH environment variable to not be set to its usual value, so" NL \ 273 "# that subprograms fail to launch unless invoked via absolute path." NL \ 274 "# If you're having unexpected problems with a hook program, the" NL \ 275 "# culprit may be unusual (or missing) environment variables." NL 276 277#define PREWRITTEN_HOOKS_TEXT \ 278 "# For more examples and pre-written hooks, see those in" NL \ 279 "# the Subversion repository at" NL \ 280 "# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and" NL \ 281 "# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/" NL 282 283 284static svn_error_t * 285create_hooks(svn_repos_t *repos, apr_pool_t *pool) 286{ 287 const char *this_path, *contents; 288 289 /* Create the hook directory. */ 290 SVN_ERR_W(create_repos_dir(repos->hook_path, pool), 291 _("Creating hook directory")); 292 293 /*** Write a default template for each standard hook file. */ 294 295 /* Start-commit hook. */ 296 { 297 this_path = apr_psprintf(pool, "%s%s", 298 svn_repos_start_commit_hook(repos, pool), 299 SVN_REPOS__HOOK_DESC_EXT); 300 301#define SCRIPT_NAME SVN_REPOS__HOOK_START_COMMIT 302 303 contents = 304"#!/bin/sh" NL 305"" NL 306"# START-COMMIT HOOK" NL 307"#" NL 308"# The start-commit hook is invoked immediately after a Subversion txn is" NL 309"# created and populated with initial revprops in the process of doing a" NL 310"# commit. Subversion runs this hook by invoking a program (script, " NL 311"# executable, binary, etc.) named '"SCRIPT_NAME"' (for which this file" NL 312"# is a template) with the following ordered arguments:" NL 313"#" NL 314"# [1] REPOS-PATH (the path to this repository)" NL 315"# [2] USER (the authenticated user attempting to commit)" NL 316"# [3] CAPABILITIES (a colon-separated list of capabilities reported" NL 317"# by the client; see note below)" NL 318"# [4] TXN-NAME (the name of the commit txn just created)" NL 319"#" NL 320"# Note: The CAPABILITIES parameter is new in Subversion 1.5, and 1.5" NL 321"# clients will typically report at least the \"" \ 322 SVN_RA_CAPABILITY_MERGEINFO "\" capability." NL 323"# If there are other capabilities, then the list is colon-separated," NL 324"# e.g.: \"" SVN_RA_CAPABILITY_MERGEINFO ":some-other-capability\" " \ 325 "(the order is undefined)." NL 326"#" NL 327"# Note: The TXN-NAME parameter is new in Subversion 1.8. Prior to version" NL 328"# 1.8, the start-commit hook was invoked before the commit txn was even" NL 329"# created, so the ability to inspect the commit txn and its metadata from" NL 330"# within the start-commit hook was not possible." NL 331"# " NL 332"# The list is self-reported by the client. Therefore, you should not" NL 333"# make security assumptions based on the capabilities list, nor should" NL 334"# you assume that clients reliably report every capability they have." NL 335"#" NL 336"# The working directory for this hook program's invocation is undefined," NL 337"# so the program should set one explicitly if it cares." NL 338"#" NL 339"# If the hook program exits with success, the commit continues; but" NL 340"# if it exits with failure (non-zero), the commit is stopped before" NL 341"# a Subversion txn is created, and STDERR is returned to the client." NL 342"#" NL 343"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 344"# invoke other programs to do the real work, though it may do the" NL 345"# work itself too." NL 346"#" NL 347"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 348"# invoke it (typically the user httpd runs as), and that user must" NL 349"# have filesystem-level permission to access the repository." NL 350"#" NL 351"# On a Windows system, you should name the hook program" NL 352"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 353"# but the basic idea is the same." NL 354"# " NL 355HOOKS_ENVIRONMENT_TEXT 356"# " NL 357"# Here is an example hook script, for a Unix /bin/sh interpreter." NL 358PREWRITTEN_HOOKS_TEXT 359"" NL 360"" NL 361"REPOS=\"$1\"" NL 362"USER=\"$2\"" NL 363"" NL 364"commit-allower.pl --repository \"$REPOS\" --user \"$USER\" || exit 1" NL 365"special-auth-check.py --user \"$USER\" --auth-level 3 || exit 1" NL 366"" NL 367"# All checks passed, so allow the commit." NL 368"exit 0" NL; 369 370#undef SCRIPT_NAME 371 372 SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 373 _("Creating start-commit hook")); 374 375 SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 376 } /* end start-commit hook */ 377 378 /* Pre-commit hook. */ 379 { 380 this_path = apr_psprintf(pool, "%s%s", 381 svn_repos_pre_commit_hook(repos, pool), 382 SVN_REPOS__HOOK_DESC_EXT); 383 384#define SCRIPT_NAME SVN_REPOS__HOOK_PRE_COMMIT 385 386 contents = 387"#!/bin/sh" NL 388"" NL 389"# PRE-COMMIT HOOK" NL 390"#" NL 391"# The pre-commit hook is invoked before a Subversion txn is" NL 392"# committed. Subversion runs this hook by invoking a program" NL 393"# (script, executable, binary, etc.) named '"SCRIPT_NAME"' (for which" NL 394"# this file is a template), with the following ordered arguments:" NL 395"#" NL 396"# [1] REPOS-PATH (the path to this repository)" NL 397"# [2] TXN-NAME (the name of the txn about to be committed)" NL 398"#" NL 399"# [STDIN] LOCK-TOKENS ** the lock tokens are passed via STDIN." NL 400"#" NL 401"# If STDIN contains the line \"LOCK-TOKENS:\\n\" (the \"\\n\" denotes a" NL 402"# single newline), the lines following it are the lock tokens for" NL 403"# this commit. The end of the list is marked by a line containing" NL 404"# only a newline character." NL 405"#" NL 406"# Each lock token line consists of a URI-escaped path, followed" NL 407"# by the separator character '|', followed by the lock token string," NL 408"# followed by a newline." NL 409"#" NL 410"# The default working directory for the invocation is undefined, so" NL 411"# the program should set one explicitly if it cares." NL 412"#" NL 413"# If the hook program exits with success, the txn is committed; but" NL 414"# if it exits with failure (non-zero), the txn is aborted, no commit" NL 415"# takes place, and STDERR is returned to the client. The hook" NL 416"# program can use the 'svnlook' utility to help it examine the txn." NL 417"#" NL 418"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 419"# invoke other programs to do the real work, though it may do the" NL 420"# work itself too." NL 421"#" NL 422"# *** NOTE: THE HOOK PROGRAM MUST NOT MODIFY THE TXN, EXCEPT ***" NL 423"# *** FOR REVISION PROPERTIES (like svn:log or svn:author). ***" NL 424"#" NL 425"# This is why we recommend using the read-only 'svnlook' utility." NL 426"# In the future, Subversion may enforce the rule that pre-commit" NL 427"# hooks should not modify the versioned data in txns, or else come" NL 428"# up with a mechanism to make it safe to do so (by informing the" NL 429"# committing client of the changes). However, right now neither" NL 430"# mechanism is implemented, so hook writers just have to be careful." NL 431"#" NL 432"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 433"# invoke it (typically the user httpd runs as), and that user must" NL 434"# have filesystem-level permission to access the repository." NL 435"#" NL 436"# On a Windows system, you should name the hook program" NL 437"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 438"# but the basic idea is the same." NL 439"#" NL 440HOOKS_ENVIRONMENT_TEXT 441"# " NL 442"# Here is an example hook script, for a Unix /bin/sh interpreter." NL 443PREWRITTEN_HOOKS_TEXT 444"" NL 445"" NL 446"REPOS=\"$1\"" NL 447"TXN=\"$2\"" NL 448"" NL 449"# Make sure that the log message contains some text." NL 450"SVNLOOK=" SVN_BINDIR "/svnlook" NL 451"$SVNLOOK log -t \"$TXN\" \"$REPOS\" | \\" NL 452" grep \"[a-zA-Z0-9]\" > /dev/null || exit 1" NL 453"" NL 454"# Check that the author of this commit has the rights to perform" NL 455"# the commit on the files and directories being modified." NL 456"commit-access-control.pl \"$REPOS\" \"$TXN\" commit-access-control.cfg || exit 1" 457 NL 458"" NL 459"# All checks passed, so allow the commit." NL 460"exit 0" NL; 461 462#undef SCRIPT_NAME 463 464 SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 465 _("Creating pre-commit hook")); 466 467 SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 468 } /* end pre-commit hook */ 469 470 471 /* Pre-revprop-change hook. */ 472 { 473 this_path = apr_psprintf(pool, "%s%s", 474 svn_repos_pre_revprop_change_hook(repos, pool), 475 SVN_REPOS__HOOK_DESC_EXT); 476 477#define SCRIPT_NAME SVN_REPOS__HOOK_PRE_REVPROP_CHANGE 478 479 contents = 480"#!/bin/sh" NL 481"" NL 482"# PRE-REVPROP-CHANGE HOOK" NL 483"#" NL 484"# The pre-revprop-change hook is invoked before a revision property" NL 485"# is added, modified or deleted. Subversion runs this hook by invoking" NL 486"# a program (script, executable, binary, etc.) named '"SCRIPT_NAME"'" NL 487"# (for which this file is a template), with the following ordered" NL 488"# arguments:" NL 489"#" NL 490"# [1] REPOS-PATH (the path to this repository)" NL 491"# [2] REV (the revision being tweaked)" NL 492"# [3] USER (the username of the person tweaking the property)" NL 493"# [4] PROPNAME (the property being set on the revision)" NL 494"# [5] ACTION (the property is being 'A'dded, 'M'odified, or 'D'eleted)" 495 NL 496"#" NL 497"# [STDIN] PROPVAL ** the new property value is passed via STDIN." NL 498"#" NL 499"# If the hook program exits with success, the propchange happens; but" NL 500"# if it exits with failure (non-zero), the propchange doesn't happen." NL 501"# The hook program can use the 'svnlook' utility to examine the " NL 502"# existing value of the revision property." NL 503"#" NL 504"# WARNING: unlike other hooks, this hook MUST exist for revision" NL 505"# properties to be changed. If the hook does not exist, Subversion " NL 506"# will behave as if the hook were present, but failed. The reason" NL 507"# for this is that revision properties are UNVERSIONED, meaning that" NL 508"# a successful propchange is destructive; the old value is gone" NL 509"# forever. We recommend the hook back up the old value somewhere." NL 510"#" NL 511"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 512"# invoke other programs to do the real work, though it may do the" NL 513"# work itself too." NL 514"#" NL 515"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 516"# invoke it (typically the user httpd runs as), and that user must" NL 517"# have filesystem-level permission to access the repository." NL 518"#" NL 519"# On a Windows system, you should name the hook program" NL 520"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 521"# but the basic idea is the same." NL 522"#" NL 523HOOKS_ENVIRONMENT_TEXT 524"# " NL 525"# Here is an example hook script, for a Unix /bin/sh interpreter." NL 526PREWRITTEN_HOOKS_TEXT 527"" NL 528"" NL 529"REPOS=\"$1\"" NL 530"REV=\"$2\"" NL 531"USER=\"$3\"" NL 532"PROPNAME=\"$4\"" NL 533"ACTION=\"$5\"" NL 534"" NL 535"if [ \"$ACTION\" = \"M\" -a \"$PROPNAME\" = \"svn:log\" ]; then exit 0; fi" NL 536"" NL 537"echo \"Changing revision properties other than svn:log is prohibited\" >&2" NL 538"exit 1" NL; 539 540#undef SCRIPT_NAME 541 542 SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 543 _("Creating pre-revprop-change hook")); 544 545 SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 546 } /* end pre-revprop-change hook */ 547 548 549 /* Pre-lock hook. */ 550 { 551 this_path = apr_psprintf(pool, "%s%s", 552 svn_repos_pre_lock_hook(repos, pool), 553 SVN_REPOS__HOOK_DESC_EXT); 554 555#define SCRIPT_NAME SVN_REPOS__HOOK_PRE_LOCK 556 557 contents = 558"#!/bin/sh" NL 559"" NL 560"# PRE-LOCK HOOK" NL 561"#" NL 562"# The pre-lock hook is invoked before an exclusive lock is" NL 563"# created. Subversion runs this hook by invoking a program " NL 564"# (script, executable, binary, etc.) named '"SCRIPT_NAME"' (for which" NL 565"# this file is a template), with the following ordered arguments:" NL 566"#" NL 567"# [1] REPOS-PATH (the path to this repository)" NL 568"# [2] PATH (the path in the repository about to be locked)" NL 569"# [3] USER (the user creating the lock)" NL 570"# [4] COMMENT (the comment of the lock)" NL 571"# [5] STEAL-LOCK (1 if the user is trying to steal the lock, else 0)" NL 572"#" NL 573"# If the hook program outputs anything on stdout, the output string will" NL 574"# be used as the lock token for this lock operation. If you choose to use" NL 575"# this feature, you must guarantee the tokens generated are unique across" NL 576"# the repository each time." NL 577"#" NL 578"# The default working directory for the invocation is undefined, so" NL 579"# the program should set one explicitly if it cares." NL 580"#" NL 581"# If the hook program exits with success, the lock is created; but" NL 582"# if it exits with failure (non-zero), the lock action is aborted" NL 583"# and STDERR is returned to the client." NL 584"" NL 585"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 586"# invoke other programs to do the real work, though it may do the" NL 587"# work itself too." NL 588"#" NL 589"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 590"# invoke it (typically the user httpd runs as), and that user must" NL 591"# have filesystem-level permission to access the repository." NL 592"#" NL 593"# On a Windows system, you should name the hook program" NL 594"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 595"# but the basic idea is the same." NL 596"#" NL 597"# Here is an example hook script, for a Unix /bin/sh interpreter:" NL 598"" NL 599"REPOS=\"$1\"" NL 600"PATH=\"$2\"" NL 601"USER=\"$3\"" NL 602"COMMENT=\"$4\"" NL 603"STEAL=\"$5\"" NL 604"" NL 605"# If a lock exists and is owned by a different person, don't allow it" NL 606"# to be stolen (e.g., with 'svn lock --force ...')." NL 607"" NL 608"# (Maybe this script could send email to the lock owner?)" NL 609"SVNLOOK=" SVN_BINDIR "/svnlook" NL 610"GREP=/bin/grep" NL 611"SED=/bin/sed" NL 612"" NL 613"LOCK_OWNER=`$SVNLOOK lock \"$REPOS\" \"$PATH\" | \\" NL 614" $GREP '^Owner: ' | $SED 's/Owner: //'`" NL 615"" NL 616"# If we get no result from svnlook, there's no lock, allow the lock to" NL 617"# happen:" NL 618"if [ \"$LOCK_OWNER\" = \"\" ]; then" NL 619" exit 0" NL 620"fi" NL 621"" NL 622"# If the person locking matches the lock's owner, allow the lock to" NL 623"# happen:" NL 624"if [ \"$LOCK_OWNER\" = \"$USER\" ]; then" NL 625" exit 0" NL 626"fi" NL 627"" NL 628"# Otherwise, we've got an owner mismatch, so return failure:" NL 629"echo \"Error: $PATH already locked by ${LOCK_OWNER}.\" 1>&2" NL 630"exit 1" NL; 631 632#undef SCRIPT_NAME 633 634 SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 635 "Creating pre-lock hook"); 636 637 SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 638 } /* end pre-lock hook */ 639 640 641 /* Pre-unlock hook. */ 642 { 643 this_path = apr_psprintf(pool, "%s%s", 644 svn_repos_pre_unlock_hook(repos, pool), 645 SVN_REPOS__HOOK_DESC_EXT); 646 647#define SCRIPT_NAME SVN_REPOS__HOOK_PRE_UNLOCK 648 649 contents = 650"#!/bin/sh" NL 651"" NL 652"# PRE-UNLOCK HOOK" NL 653"#" NL 654"# The pre-unlock hook is invoked before an exclusive lock is" NL 655"# destroyed. Subversion runs this hook by invoking a program " NL 656"# (script, executable, binary, etc.) named '"SCRIPT_NAME"' (for which" NL 657"# this file is a template), with the following ordered arguments:" NL 658"#" NL 659"# [1] REPOS-PATH (the path to this repository)" NL 660"# [2] PATH (the path in the repository about to be unlocked)" NL 661"# [3] USER (the user destroying the lock)" NL 662"# [4] TOKEN (the lock token to be destroyed)" NL 663"# [5] BREAK-UNLOCK (1 if the user is breaking the lock, else 0)" NL 664"#" NL 665"# The default working directory for the invocation is undefined, so" NL 666"# the program should set one explicitly if it cares." NL 667"#" NL 668"# If the hook program exits with success, the lock is destroyed; but" NL 669"# if it exits with failure (non-zero), the unlock action is aborted" NL 670"# and STDERR is returned to the client." NL 671"" NL 672"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 673"# invoke other programs to do the real work, though it may do the" NL 674"# work itself too." NL 675"#" NL 676"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 677"# invoke it (typically the user httpd runs as), and that user must" NL 678"# have filesystem-level permission to access the repository." NL 679"#" NL 680"# On a Windows system, you should name the hook program" NL 681"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 682"# but the basic idea is the same." NL 683"#" NL 684"# Here is an example hook script, for a Unix /bin/sh interpreter:" NL 685"" NL 686"REPOS=\"$1\"" NL 687"PATH=\"$2\"" NL 688"USER=\"$3\"" NL 689"TOKEN=\"$4\"" NL 690"BREAK=\"$5\"" NL 691"" NL 692"# If a lock is owned by a different person, don't allow it be broken." NL 693"# (Maybe this script could send email to the lock owner?)" NL 694"" NL 695"SVNLOOK=" SVN_BINDIR "/svnlook" NL 696"GREP=/bin/grep" NL 697"SED=/bin/sed" NL 698"" NL 699"LOCK_OWNER=`$SVNLOOK lock \"$REPOS\" \"$PATH\" | \\" NL 700" $GREP '^Owner: ' | $SED 's/Owner: //'`" NL 701"" NL 702"# If we get no result from svnlook, there's no lock, return success:" NL 703"if [ \"$LOCK_OWNER\" = \"\" ]; then" NL 704" exit 0" NL 705"fi" NL 706"" NL 707"# If the person unlocking matches the lock's owner, return success:" NL 708"if [ \"$LOCK_OWNER\" = \"$USER\" ]; then" NL 709" exit 0" NL 710"fi" NL 711"" NL 712"# Otherwise, we've got an owner mismatch, so return failure:" NL 713"echo \"Error: $PATH locked by ${LOCK_OWNER}.\" 1>&2" NL 714"exit 1" NL; 715 716#undef SCRIPT_NAME 717 718 SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 719 "Creating pre-unlock hook"); 720 721 SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 722 } /* end pre-unlock hook */ 723 724 725 726 /* Post-commit hook. */ 727 { 728 this_path = apr_psprintf(pool, "%s%s", 729 svn_repos_post_commit_hook(repos, pool), 730 SVN_REPOS__HOOK_DESC_EXT); 731 732#define SCRIPT_NAME SVN_REPOS__HOOK_POST_COMMIT 733 734 contents = 735"#!/bin/sh" NL 736"" NL 737"# POST-COMMIT HOOK" NL 738"#" NL 739"# The post-commit hook is invoked after a commit. Subversion runs" NL 740"# this hook by invoking a program (script, executable, binary, etc.)" NL 741"# named '"SCRIPT_NAME"' (for which this file is a template) with the " NL 742"# following ordered arguments:" NL 743"#" NL 744"# [1] REPOS-PATH (the path to this repository)" NL 745"# [2] REV (the number of the revision just committed)" NL 746"# [3] TXN-NAME (the name of the transaction that has become REV)" NL 747"#" NL 748"# The default working directory for the invocation is undefined, so" NL 749"# the program should set one explicitly if it cares." NL 750"#" NL 751"# Because the commit has already completed and cannot be undone," NL 752"# the exit code of the hook program is ignored. The hook program" NL 753"# can use the 'svnlook' utility to help it examine the" NL 754"# newly-committed tree." NL 755"#" NL 756"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 757"# invoke other programs to do the real work, though it may do the" NL 758"# work itself too." NL 759"#" NL 760"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 761"# invoke it (typically the user httpd runs as), and that user must" NL 762"# have filesystem-level permission to access the repository." NL 763"#" NL 764"# On a Windows system, you should name the hook program" NL 765"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 766"# but the basic idea is the same." NL 767"# " NL 768HOOKS_ENVIRONMENT_TEXT 769"# " NL 770"# Here is an example hook script, for a Unix /bin/sh interpreter." NL 771PREWRITTEN_HOOKS_TEXT 772"" NL 773"" NL 774"REPOS=\"$1\"" NL 775"REV=\"$2\"" NL 776"TXN_NAME=\"$3\"" NL 777 NL 778"mailer.py commit \"$REPOS\" \"$REV\" /path/to/mailer.conf" NL; 779 780#undef SCRIPT_NAME 781 782 SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 783 _("Creating post-commit hook")); 784 785 SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 786 } /* end post-commit hook */ 787 788 789 /* Post-lock hook. */ 790 { 791 this_path = apr_psprintf(pool, "%s%s", 792 svn_repos_post_lock_hook(repos, pool), 793 SVN_REPOS__HOOK_DESC_EXT); 794 795#define SCRIPT_NAME SVN_REPOS__HOOK_POST_LOCK 796 797 contents = 798"#!/bin/sh" NL 799"" NL 800"# POST-LOCK HOOK" NL 801"#" NL 802"# The post-lock hook is run after a path is locked. Subversion runs" NL 803"# this hook by invoking a program (script, executable, binary, etc.)" NL 804"# named '"SCRIPT_NAME"' (for which this file is a template) with the " NL 805"# following ordered arguments:" NL 806"#" NL 807"# [1] REPOS-PATH (the path to this repository)" NL 808"# [2] USER (the user who created the lock)" NL 809"#" NL 810"# The paths that were just locked are passed to the hook via STDIN (as" NL 811"# of Subversion 1.2, only one path is passed per invocation, but the" NL 812"# plan is to pass all locked paths at once, so the hook program" NL 813"# should be written accordingly)." NL 814"#" NL 815"# The default working directory for the invocation is undefined, so" NL 816"# the program should set one explicitly if it cares." NL 817"#" NL 818"# Because the lock has already been created and cannot be undone," NL 819"# the exit code of the hook program is ignored. The hook program" NL 820"# can use the 'svnlook' utility to help it examine the" NL 821"# newly-created lock." NL 822"#" NL 823"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 824"# invoke other programs to do the real work, though it may do the" NL 825"# work itself too." NL 826"#" NL 827"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 828"# invoke it (typically the user httpd runs as), and that user must" NL 829"# have filesystem-level permission to access the repository." NL 830"#" NL 831"# On a Windows system, you should name the hook program" NL 832"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 833"# but the basic idea is the same." NL 834"# " NL 835"# Here is an example hook script, for a Unix /bin/sh interpreter:" NL 836"" NL 837"REPOS=\"$1\"" NL 838"USER=\"$2\"" NL 839"" NL 840"# Send email to interested parties, let them know a lock was created:" NL 841"mailer.py lock \"$REPOS\" \"$USER\" /path/to/mailer.conf" NL; 842 843#undef SCRIPT_NAME 844 845 SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 846 "Creating post-lock hook"); 847 848 SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 849 } /* end post-lock hook */ 850 851 852 /* Post-unlock hook. */ 853 { 854 this_path = apr_psprintf(pool, "%s%s", 855 svn_repos_post_unlock_hook(repos, pool), 856 SVN_REPOS__HOOK_DESC_EXT); 857 858#define SCRIPT_NAME SVN_REPOS__HOOK_POST_UNLOCK 859 860 contents = 861"#!/bin/sh" NL 862"" NL 863"# POST-UNLOCK HOOK" NL 864"#" NL 865"# The post-unlock hook runs after a path is unlocked. Subversion runs" NL 866"# this hook by invoking a program (script, executable, binary, etc.)" NL 867"# named '"SCRIPT_NAME"' (for which this file is a template) with the " NL 868"# following ordered arguments:" NL 869"#" NL 870"# [1] REPOS-PATH (the path to this repository)" NL 871"# [2] USER (the user who destroyed the lock)" NL 872"#" NL 873"# The paths that were just unlocked are passed to the hook via STDIN" NL 874"# (as of Subversion 1.2, only one path is passed per invocation, but" NL 875"# the plan is to pass all unlocked paths at once, so the hook program" NL 876"# should be written accordingly)." NL 877"#" NL 878"# The default working directory for the invocation is undefined, so" NL 879"# the program should set one explicitly if it cares." NL 880"#" NL 881"# Because the lock has already been destroyed and cannot be undone," NL 882"# the exit code of the hook program is ignored." NL 883"#" NL 884"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 885"# invoke other programs to do the real work, though it may do the" NL 886"# work itself too." NL 887"#" NL 888"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 889"# invoke it (typically the user httpd runs as), and that user must" NL 890"# have filesystem-level permission to access the repository." NL 891"#" NL 892"# On a Windows system, you should name the hook program" NL 893"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 894"# but the basic idea is the same." NL 895"# " NL 896"# Here is an example hook script, for a Unix /bin/sh interpreter:" NL 897"" NL 898"REPOS=\"$1\"" NL 899"USER=\"$2\"" NL 900"" NL 901"# Send email to interested parties, let them know a lock was removed:" NL 902"mailer.py unlock \"$REPOS\" \"$USER\" /path/to/mailer.conf" NL; 903 904#undef SCRIPT_NAME 905 906 SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 907 "Creating post-unlock hook"); 908 909 SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 910 } /* end post-unlock hook */ 911 912 913 /* Post-revprop-change hook. */ 914 { 915 this_path = apr_psprintf(pool, "%s%s", 916 svn_repos_post_revprop_change_hook(repos, pool), 917 SVN_REPOS__HOOK_DESC_EXT); 918 919#define SCRIPT_NAME SVN_REPOS__HOOK_POST_REVPROP_CHANGE 920 921 contents = 922"#!/bin/sh" NL 923"" NL 924"# POST-REVPROP-CHANGE HOOK" NL 925"#" NL 926"# The post-revprop-change hook is invoked after a revision property" NL 927"# has been added, modified or deleted. Subversion runs this hook by" NL 928"# invoking a program (script, executable, binary, etc.) named" NL 929"# '"SCRIPT_NAME"' (for which this file is a template), with the" NL 930"# following ordered arguments:" NL 931"#" NL 932"# [1] REPOS-PATH (the path to this repository)" NL 933"# [2] REV (the revision that was tweaked)" NL 934"# [3] USER (the username of the person tweaking the property)" NL 935"# [4] PROPNAME (the property that was changed)" NL 936"# [5] ACTION (the property was 'A'dded, 'M'odified, or 'D'eleted)" NL 937"#" NL 938"# [STDIN] PROPVAL ** the old property value is passed via STDIN." NL 939"#" NL 940"# Because the propchange has already completed and cannot be undone," NL 941"# the exit code of the hook program is ignored. The hook program" NL 942"# can use the 'svnlook' utility to help it examine the" NL 943"# new property value." NL 944"#" NL 945"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 946"# invoke other programs to do the real work, though it may do the" NL 947"# work itself too." NL 948"#" NL 949"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 950"# invoke it (typically the user httpd runs as), and that user must" NL 951"# have filesystem-level permission to access the repository." NL 952"#" NL 953"# On a Windows system, you should name the hook program" NL 954"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 955"# but the basic idea is the same." NL 956"# " NL 957HOOKS_ENVIRONMENT_TEXT 958"# " NL 959"# Here is an example hook script, for a Unix /bin/sh interpreter." NL 960PREWRITTEN_HOOKS_TEXT 961"" NL 962"" NL 963"REPOS=\"$1\"" NL 964"REV=\"$2\"" NL 965"USER=\"$3\"" NL 966"PROPNAME=\"$4\"" NL 967"ACTION=\"$5\"" NL 968"" NL 969"mailer.py propchange2 \"$REPOS\" \"$REV\" \"$USER\" \"$PROPNAME\" " 970"\"$ACTION\" /path/to/mailer.conf" NL; 971 972#undef SCRIPT_NAME 973 974 SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 975 _("Creating post-revprop-change hook")); 976 977 SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 978 } /* end post-revprop-change hook */ 979 980 return SVN_NO_ERROR; 981} 982 983static svn_error_t * 984create_conf(svn_repos_t *repos, apr_pool_t *pool) 985{ 986 SVN_ERR_W(create_repos_dir(repos->conf_path, pool), 987 _("Creating conf directory")); 988 989 /* Write a default template for svnserve.conf. */ 990 { 991 static const char * const svnserve_conf_contents = 992"### This file controls the configuration of the svnserve daemon, if you" NL 993"### use it to allow access to this repository. (If you only allow" NL 994"### access through http: and/or file: URLs, then this file is" NL 995"### irrelevant.)" NL 996"" NL 997"### Visit http://subversion.apache.org/ for more information." NL 998"" NL 999"[general]" NL 1000"### The anon-access and auth-access options control access to the" NL 1001"### repository for unauthenticated (a.k.a. anonymous) users and" NL 1002"### authenticated users, respectively." NL 1003"### Valid values are \"write\", \"read\", and \"none\"." NL 1004"### Setting the value to \"none\" prohibits both reading and writing;" NL 1005"### \"read\" allows read-only access, and \"write\" allows complete " NL 1006"### read/write access to the repository." NL 1007"### The sample settings below are the defaults and specify that anonymous" NL 1008"### users have read-only access to the repository, while authenticated" NL 1009"### users have read and write access to the repository." NL 1010"# anon-access = read" NL 1011"# auth-access = write" NL 1012"### The password-db option controls the location of the password" NL 1013"### database file. Unless you specify a path starting with a /," NL 1014"### the file's location is relative to the directory containing" NL 1015"### this configuration file." NL 1016"### If SASL is enabled (see below), this file will NOT be used." NL 1017"### Uncomment the line below to use the default password file." NL 1018"# password-db = passwd" NL 1019"### The authz-db option controls the location of the authorization" NL 1020"### rules for path-based access control. Unless you specify a path" NL 1021"### starting with a /, the file's location is relative to the" NL 1022"### directory containing this file. The specified path may be a" NL 1023"### repository relative URL (^/) or an absolute file:// URL to a text" NL 1024"### file in a Subversion repository. If you don't specify an authz-db," NL 1025"### no path-based access control is done." NL 1026"### Uncomment the line below to use the default authorization file." NL 1027"# authz-db = " SVN_REPOS__CONF_AUTHZ NL 1028"### The groups-db option controls the location of the groups file." NL 1029"### Unless you specify a path starting with a /, the file's location is" NL 1030"### relative to the directory containing this file. The specified path" NL 1031"### may be a repository relative URL (^/) or an absolute file:// URL to a" NL 1032"### text file in a Subversion repository." NL 1033"# groups-db = " SVN_REPOS__CONF_GROUPS NL 1034"### This option specifies the authentication realm of the repository." NL 1035"### If two repositories have the same authentication realm, they should" NL 1036"### have the same password database, and vice versa. The default realm" NL 1037"### is repository's uuid." NL 1038"# realm = My First Repository" NL 1039"### The force-username-case option causes svnserve to case-normalize" NL 1040"### usernames before comparing them against the authorization rules in the" NL 1041"### authz-db file configured above. Valid values are \"upper\" (to upper-" NL 1042"### case the usernames), \"lower\" (to lowercase the usernames), and" NL 1043"### \"none\" (to compare usernames as-is without case conversion, which" NL 1044"### is the default behavior)." NL 1045"# force-username-case = none" NL 1046"### The hooks-env options specifies a path to the hook script environment " NL 1047"### configuration file. This option overrides the per-repository default" NL 1048"### and can be used to configure the hook script environment for multiple " NL 1049"### repositories in a single file, if an absolute path is specified." NL 1050"### Unless you specify an absolute path, the file's location is relative" NL 1051"### to the directory containing this file." NL 1052"# hooks-env = " SVN_REPOS__CONF_HOOKS_ENV NL 1053"" NL 1054"[sasl]" NL 1055"### This option specifies whether you want to use the Cyrus SASL" NL 1056"### library for authentication. Default is false." NL 1057"### This section will be ignored if svnserve is not built with Cyrus" NL 1058"### SASL support; to check, run 'svnserve --version' and look for a line" NL 1059"### reading 'Cyrus SASL authentication is available.'" NL 1060"# use-sasl = true" NL 1061"### These options specify the desired strength of the security layer" NL 1062"### that you want SASL to provide. 0 means no encryption, 1 means" NL 1063"### integrity-checking only, values larger than 1 are correlated" NL 1064"### to the effective key length for encryption (e.g. 128 means 128-bit" NL 1065"### encryption). The values below are the defaults." NL 1066"# min-encryption = 0" NL 1067"# max-encryption = 256" NL; 1068 1069 SVN_ERR_W(svn_io_file_create(svn_repos_svnserve_conf(repos, pool), 1070 svnserve_conf_contents, pool), 1071 _("Creating svnserve.conf file")); 1072 } 1073 1074 { 1075 static const char * const passwd_contents = 1076"### This file is an example password file for svnserve." NL 1077"### Its format is similar to that of svnserve.conf. As shown in the" NL 1078"### example below it contains one section labelled [users]." NL 1079"### The name and password for each user follow, one account per line." NL 1080"" NL 1081"[users]" NL 1082"# harry = harryssecret" NL 1083"# sally = sallyssecret" NL; 1084 1085 SVN_ERR_W(svn_io_file_create(svn_dirent_join(repos->conf_path, 1086 SVN_REPOS__CONF_PASSWD, 1087 pool), 1088 passwd_contents, pool), 1089 _("Creating passwd file")); 1090 } 1091 1092 { 1093 static const char * const authz_contents = 1094"### This file is an example authorization file for svnserve." NL 1095"### Its format is identical to that of mod_authz_svn authorization" NL 1096"### files." NL 1097"### As shown below each section defines authorizations for the path and" NL 1098"### (optional) repository specified by the section name." NL 1099"### The authorizations follow. An authorization line can refer to:" NL 1100"### - a single user," NL 1101"### - a group of users defined in a special [groups] section," NL 1102"### - an alias defined in a special [aliases] section," NL 1103"### - all authenticated users, using the '$authenticated' token," NL 1104"### - only anonymous users, using the '$anonymous' token," NL 1105"### - anyone, using the '*' wildcard." NL 1106"###" NL 1107"### A match can be inverted by prefixing the rule with '~'. Rules can" NL 1108"### grant read ('r') access, read-write ('rw') access, or no access" NL 1109"### ('')." NL 1110"" NL 1111"[aliases]" NL 1112"# joe = /C=XZ/ST=Dessert/L=Snake City/O=Snake Oil, Ltd./OU=Research Institute/CN=Joe Average" NL 1113"" NL 1114"[groups]" NL 1115"# harry_and_sally = harry,sally" NL 1116"# harry_sally_and_joe = harry,sally,&joe" NL 1117"" NL 1118"# [/foo/bar]" NL 1119"# harry = rw" NL 1120"# &joe = r" NL 1121"# * =" NL 1122"" NL 1123"# [repository:/baz/fuz]" NL 1124"# @harry_and_sally = rw" NL 1125"# * = r" NL; 1126 1127 SVN_ERR_W(svn_io_file_create(svn_dirent_join(repos->conf_path, 1128 SVN_REPOS__CONF_AUTHZ, 1129 pool), 1130 authz_contents, pool), 1131 _("Creating authz file")); 1132 } 1133 1134 { 1135 static const char * const hooks_env_contents = 1136"### This file is an example hook script environment configuration file." NL 1137"### Hook scripts run in an empty environment by default." NL 1138"### As shown below each section defines environment variables for a" NL 1139"### particular hook script. The [default] section defines environment" NL 1140"### variables for all hook scripts, unless overridden by a hook-specific" NL 1141"### section." NL 1142"" NL 1143"### This example configures a UTF-8 locale for all hook scripts, so that " NL 1144"### special characters, such as umlauts, may be printed to stderr." NL 1145"### If UTF-8 is used with a mod_dav_svn server, the SVNUseUTF8 option must" NL 1146"### also be set to 'yes' in httpd.conf." NL 1147"### With svnserve, the LANG environment variable of the svnserve process" NL 1148"### must be set to the same value as given here." NL 1149"[default]" NL 1150"LANG = en_US.UTF-8" NL 1151"" NL 1152"### This sets the PATH environment variable for the pre-commit hook." NL 1153"[pre-commit]" NL 1154"PATH = /usr/local/bin:/usr/bin:/usr/sbin" NL; 1155 1156 SVN_ERR_W(svn_io_file_create(svn_dirent_join(repos->conf_path, 1157 SVN_REPOS__CONF_HOOKS_ENV \ 1158 SVN_REPOS__HOOK_DESC_EXT, 1159 pool), 1160 hooks_env_contents, pool), 1161 _("Creating hooks-env file")); 1162 } 1163 1164 return SVN_NO_ERROR; 1165} 1166 1167svn_error_t * 1168svn_repos_hooks_setenv(svn_repos_t *repos, 1169 const char *hooks_env_path, 1170 apr_pool_t *scratch_pool) 1171{ 1172 if (hooks_env_path == NULL) 1173 repos->hooks_env_path = svn_dirent_join(repos->conf_path, 1174 SVN_REPOS__CONF_HOOKS_ENV, 1175 repos->pool); 1176 else if (!svn_dirent_is_absolute(hooks_env_path)) 1177 repos->hooks_env_path = svn_dirent_join(repos->conf_path, 1178 hooks_env_path, 1179 repos->pool); 1180 else 1181 repos->hooks_env_path = apr_pstrdup(repos->pool, hooks_env_path); 1182 1183 return SVN_NO_ERROR; 1184} 1185 1186/* Allocate and return a new svn_repos_t * object, initializing the 1187 directory pathname members based on PATH, and initializing the 1188 REPOSITORY_CAPABILITIES member. 1189 The members FS, FORMAT, and FS_TYPE are *not* initialized (they are null), 1190 and it is the caller's responsibility to fill them in if needed. */ 1191static svn_repos_t * 1192create_svn_repos_t(const char *path, apr_pool_t *pool) 1193{ 1194 svn_repos_t *repos = apr_pcalloc(pool, sizeof(*repos)); 1195 1196 repos->path = apr_pstrdup(pool, path); 1197 repos->db_path = svn_dirent_join(path, SVN_REPOS__DB_DIR, pool); 1198 repos->conf_path = svn_dirent_join(path, SVN_REPOS__CONF_DIR, pool); 1199 repos->hook_path = svn_dirent_join(path, SVN_REPOS__HOOK_DIR, pool); 1200 repos->lock_path = svn_dirent_join(path, SVN_REPOS__LOCK_DIR, pool); 1201 repos->hooks_env_path = NULL; 1202 repos->repository_capabilities = apr_hash_make(pool); 1203 repos->pool = pool; 1204 1205 return repos; 1206} 1207 1208 1209static svn_error_t * 1210create_repos_structure(svn_repos_t *repos, 1211 const char *path, 1212 apr_hash_t *fs_config, 1213 apr_pool_t *pool) 1214{ 1215 /* Create the top-level repository directory. */ 1216 SVN_ERR_W(create_repos_dir(path, pool), 1217 _("Could not create top-level directory")); 1218 1219 /* Create the DAV sandbox directory if pre-1.4 or pre-1.5-compatible. */ 1220 if (fs_config 1221 && (svn_hash_gets(fs_config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE) 1222 || svn_hash_gets(fs_config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE))) 1223 { 1224 const char *dav_path = svn_dirent_join(repos->path, 1225 SVN_REPOS__DAV_DIR, pool); 1226 SVN_ERR_W(create_repos_dir(dav_path, pool), 1227 _("Creating DAV sandbox dir")); 1228 } 1229 1230 /* Create the lock directory. */ 1231 SVN_ERR(create_locks(repos, pool)); 1232 1233 /* Create the hooks directory. */ 1234 SVN_ERR(create_hooks(repos, pool)); 1235 1236 /* Create the conf directory. */ 1237 SVN_ERR(create_conf(repos, pool)); 1238 1239 /* Write the top-level README file. */ 1240 { 1241 const char * const readme_header = 1242 "This is a Subversion repository; use the 'svnadmin' and 'svnlook' " NL 1243 "tools to examine it. Do not add, delete, or modify files here " NL 1244 "unless you know how to avoid corrupting the repository." NL 1245 "" NL; 1246 const char * const readme_bdb_insert = 1247 "The directory \"" SVN_REPOS__DB_DIR "\" contains a Berkeley DB environment." NL 1248 "you may need to tweak the values in \"" SVN_REPOS__DB_DIR "/DB_CONFIG\" to match the" NL 1249 "requirements of your site." NL 1250 "" NL; 1251 const char * const readme_footer = 1252 "Visit http://subversion.apache.org/ for more information." NL; 1253 apr_file_t *f; 1254 apr_size_t written; 1255 1256 SVN_ERR(svn_io_file_open(&f, 1257 svn_dirent_join(path, SVN_REPOS__README, pool), 1258 (APR_WRITE | APR_CREATE | APR_EXCL), 1259 APR_OS_DEFAULT, pool)); 1260 1261 SVN_ERR(svn_io_file_write_full(f, readme_header, strlen(readme_header), 1262 &written, pool)); 1263 if (strcmp(repos->fs_type, SVN_FS_TYPE_BDB) == 0) 1264 SVN_ERR(svn_io_file_write_full(f, readme_bdb_insert, 1265 strlen(readme_bdb_insert), 1266 &written, pool)); 1267 SVN_ERR(svn_io_file_write_full(f, readme_footer, strlen(readme_footer), 1268 &written, pool)); 1269 1270 return svn_io_file_close(f, pool); 1271 } 1272} 1273 1274 1275/* There is, at present, nothing within the direct responsibility 1276 of libsvn_repos which requires locking. For historical compatibility 1277 reasons, the BDB libsvn_fs backend does not do its own locking, expecting 1278 libsvn_repos to do the locking for it. Here we take care of that 1279 backend-specific requirement. 1280 The kind of lock is controlled by EXCLUSIVE and NONBLOCKING. 1281 The lock is scoped to POOL. */ 1282static svn_error_t * 1283lock_repos(svn_repos_t *repos, 1284 svn_boolean_t exclusive, 1285 svn_boolean_t nonblocking, 1286 apr_pool_t *pool) 1287{ 1288 if (strcmp(repos->fs_type, SVN_FS_TYPE_BDB) == 0) 1289 { 1290 svn_error_t *err; 1291 const char *lockfile_path = svn_repos_db_lockfile(repos, pool); 1292 1293 err = svn_io_file_lock2(lockfile_path, exclusive, nonblocking, pool); 1294 if (err != NULL && APR_STATUS_IS_EAGAIN(err->apr_err)) 1295 return svn_error_trace(err); 1296 SVN_ERR_W(err, _("Error opening db lockfile")); 1297 } 1298 return SVN_NO_ERROR; 1299} 1300 1301 1302svn_error_t * 1303svn_repos_create(svn_repos_t **repos_p, 1304 const char *path, 1305 const char *unused_1, 1306 const char *unused_2, 1307 apr_hash_t *config, 1308 apr_hash_t *fs_config, 1309 apr_pool_t *pool) 1310{ 1311 svn_repos_t *repos; 1312 svn_error_t *err; 1313 const char *root_path; 1314 const char *local_abspath; 1315 1316 /* Allocate a repository object, filling in the format we will create. */ 1317 repos = create_svn_repos_t(path, pool); 1318 repos->format = SVN_REPOS__FORMAT_NUMBER; 1319 1320 /* Discover the type of the filesystem we are about to create. */ 1321 repos->fs_type = svn_hash__get_cstring(fs_config, SVN_FS_CONFIG_FS_TYPE, 1322 DEFAULT_FS_TYPE); 1323 if (svn_hash__get_bool(fs_config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE, FALSE)) 1324 repos->format = SVN_REPOS__FORMAT_NUMBER_LEGACY; 1325 1326 /* Don't create a repository inside another repository. */ 1327 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); 1328 root_path = svn_repos_find_root_path(local_abspath, pool); 1329 if (root_path != NULL) 1330 { 1331 if (strcmp(root_path, local_abspath) == 0) 1332 return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL, 1333 _("'%s' is an existing repository"), 1334 svn_dirent_local_style(root_path, pool)); 1335 else 1336 return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL, 1337 _("'%s' is a subdirectory of an existing " 1338 "repository " "rooted at '%s'"), 1339 svn_dirent_local_style(local_abspath, pool), 1340 svn_dirent_local_style(root_path, pool)); 1341 } 1342 1343 /* Create the various files and subdirectories for the repository. */ 1344 SVN_ERR_W(create_repos_structure(repos, path, fs_config, pool), 1345 _("Repository creation failed")); 1346 1347 /* Lock if needed. */ 1348 SVN_ERR(lock_repos(repos, FALSE, FALSE, pool)); 1349 1350 /* Create an environment for the filesystem. */ 1351 if ((err = svn_fs_create(&repos->fs, repos->db_path, fs_config, pool))) 1352 { 1353 /* If there was an error making the filesytem, e.g. unknown/supported 1354 * filesystem type. Clean up after ourselves. Yes this is safe because 1355 * create_repos_structure will fail if the path existed before we started 1356 * so we can't accidentally remove a directory that previously existed. 1357 */ 1358 1359 return svn_error_trace( 1360 svn_error_compose_create( 1361 err, 1362 svn_io_remove_dir2(path, FALSE, NULL, NULL, pool))); 1363 } 1364 1365 /* This repository is ready. Stamp it with a format number. */ 1366 SVN_ERR(svn_io_write_version_file 1367 (svn_dirent_join(path, SVN_REPOS__FORMAT, pool), 1368 repos->format, pool)); 1369 1370 *repos_p = repos; 1371 return SVN_NO_ERROR; 1372} 1373 1374 1375/* Check if @a path is the root of a repository by checking if the 1376 * path contains the expected files and directories. Return TRUE 1377 * on errors (which would be permission errors, probably) so that 1378 * we the user will see them after we try to open the repository 1379 * for real. */ 1380static svn_boolean_t 1381check_repos_path(const char *path, 1382 apr_pool_t *pool) 1383{ 1384 svn_node_kind_t kind; 1385 svn_error_t *err; 1386 1387 err = svn_io_check_path(svn_dirent_join(path, SVN_REPOS__FORMAT, pool), 1388 &kind, pool); 1389 if (err) 1390 { 1391 svn_error_clear(err); 1392 return TRUE; 1393 } 1394 if (kind != svn_node_file) 1395 return FALSE; 1396 1397 /* Check the db/ subdir, but allow it to be a symlink (Subversion 1398 works just fine if it's a symlink). */ 1399 err = svn_io_check_resolved_path 1400 (svn_dirent_join(path, SVN_REPOS__DB_DIR, pool), &kind, pool); 1401 if (err) 1402 { 1403 svn_error_clear(err); 1404 return TRUE; 1405 } 1406 if (kind != svn_node_dir) 1407 return FALSE; 1408 1409 return TRUE; 1410} 1411 1412 1413/* Verify that REPOS's format is suitable. 1414 Use POOL for temporary allocation. */ 1415static svn_error_t * 1416check_repos_format(svn_repos_t *repos, 1417 apr_pool_t *pool) 1418{ 1419 int format; 1420 const char *format_path; 1421 1422 format_path = svn_dirent_join(repos->path, SVN_REPOS__FORMAT, pool); 1423 SVN_ERR(svn_io_read_version_file(&format, format_path, pool)); 1424 1425 if (format != SVN_REPOS__FORMAT_NUMBER && 1426 format != SVN_REPOS__FORMAT_NUMBER_LEGACY) 1427 { 1428 return svn_error_createf 1429 (SVN_ERR_REPOS_UNSUPPORTED_VERSION, NULL, 1430 _("Expected repository format '%d' or '%d'; found format '%d'"), 1431 SVN_REPOS__FORMAT_NUMBER_LEGACY, SVN_REPOS__FORMAT_NUMBER, 1432 format); 1433 } 1434 1435 repos->format = format; 1436 1437 return SVN_NO_ERROR; 1438} 1439 1440 1441/* Set *REPOS_P to a repository at PATH which has been opened. 1442 See lock_repos() above regarding EXCLUSIVE and NONBLOCKING. 1443 OPEN_FS indicates whether the Subversion filesystem should be opened, 1444 the handle being placed into repos->fs. 1445 Do all allocation in POOL. */ 1446static svn_error_t * 1447get_repos(svn_repos_t **repos_p, 1448 const char *path, 1449 svn_boolean_t exclusive, 1450 svn_boolean_t nonblocking, 1451 svn_boolean_t open_fs, 1452 apr_hash_t *fs_config, 1453 apr_pool_t *pool) 1454{ 1455 svn_repos_t *repos; 1456 1457 /* Allocate a repository object. */ 1458 repos = create_svn_repos_t(path, pool); 1459 1460 /* Verify the validity of our repository format. */ 1461 SVN_ERR(check_repos_format(repos, pool)); 1462 1463 /* Discover the FS type. */ 1464 SVN_ERR(svn_fs_type(&repos->fs_type, repos->db_path, pool)); 1465 1466 /* Lock if needed. */ 1467 SVN_ERR(lock_repos(repos, exclusive, nonblocking, pool)); 1468 1469 /* Open up the filesystem only after obtaining the lock. */ 1470 if (open_fs) 1471 SVN_ERR(svn_fs_open(&repos->fs, repos->db_path, fs_config, pool)); 1472 1473#ifdef SVN_DEBUG_CRASH_AT_REPOS_OPEN 1474 /* If $PATH/config/debug-abort exists, crash the server here. 1475 This debugging feature can be used to test client recovery 1476 when the server crashes. 1477 1478 See: Issue #4274 */ 1479 { 1480 svn_node_kind_t kind; 1481 svn_error_t *err = svn_io_check_path( 1482 svn_dirent_join(repos->conf_path, "debug-abort", pool), 1483 &kind, pool); 1484 svn_error_clear(err); 1485 if (!err && kind == svn_node_file) 1486 SVN_ERR_MALFUNCTION_NO_RETURN(); 1487 } 1488#endif /* SVN_DEBUG_CRASH_AT_REPOS_OPEN */ 1489 1490 *repos_p = repos; 1491 return SVN_NO_ERROR; 1492} 1493 1494 1495 1496const char * 1497svn_repos_find_root_path(const char *path, 1498 apr_pool_t *pool) 1499{ 1500 const char *candidate = path; 1501 const char *decoded; 1502 svn_error_t *err; 1503 1504 while (1) 1505 { 1506 /* Try to decode the path, so we don't fail if it contains characters 1507 that aren't supported by the OS filesystem. The subversion fs 1508 isn't restricted by the OS filesystem character set. */ 1509 err = svn_path_cstring_from_utf8(&decoded, candidate, pool); 1510 if (!err && check_repos_path(candidate, pool)) 1511 break; 1512 svn_error_clear(err); 1513 1514 if (svn_path_is_empty(candidate) || 1515 svn_dirent_is_root(candidate, strlen(candidate))) 1516 return NULL; 1517 1518 candidate = svn_dirent_dirname(candidate, pool); 1519 } 1520 1521 return candidate; 1522} 1523 1524 1525svn_error_t * 1526svn_repos_open2(svn_repos_t **repos_p, 1527 const char *path, 1528 apr_hash_t *fs_config, 1529 apr_pool_t *pool) 1530{ 1531 /* Fetch a repository object initialized with a shared read/write 1532 lock on the database. */ 1533 1534 return get_repos(repos_p, path, FALSE, FALSE, TRUE, fs_config, pool); 1535} 1536 1537 1538svn_error_t * 1539svn_repos_upgrade2(const char *path, 1540 svn_boolean_t nonblocking, 1541 svn_repos_notify_func_t notify_func, 1542 void *notify_baton, 1543 apr_pool_t *pool) 1544{ 1545 svn_repos_t *repos; 1546 const char *format_path; 1547 int format; 1548 apr_pool_t *subpool = svn_pool_create(pool); 1549 1550 /* Fetch a repository object; for the Berkeley DB backend, it is 1551 initialized with an EXCLUSIVE lock on the database. This will at 1552 least prevent others from trying to read or write to it while we 1553 run recovery. (Other backends should do their own locking; see 1554 lock_repos.) */ 1555 SVN_ERR(get_repos(&repos, path, TRUE, nonblocking, FALSE, NULL, subpool)); 1556 1557 if (notify_func) 1558 { 1559 /* We notify *twice* here, because there are two different logistical 1560 actions occuring. */ 1561 svn_repos_notify_t *notify = svn_repos_notify_create( 1562 svn_repos_notify_mutex_acquired, subpool); 1563 notify_func(notify_baton, notify, subpool); 1564 1565 notify->action = svn_repos_notify_upgrade_start; 1566 notify_func(notify_baton, notify, subpool); 1567 } 1568 1569 /* Try to overwrite with its own contents. We do this only to 1570 verify that we can, because we don't want to actually bump the 1571 format of the repository until our underlying filesystem claims 1572 to have been upgraded correctly. */ 1573 format_path = svn_dirent_join(repos->path, SVN_REPOS__FORMAT, subpool); 1574 SVN_ERR(svn_io_read_version_file(&format, format_path, subpool)); 1575 SVN_ERR(svn_io_write_version_file(format_path, format, subpool)); 1576 1577 /* Try to upgrade the filesystem. */ 1578 SVN_ERR(svn_fs_upgrade(repos->db_path, subpool)); 1579 1580 /* Now overwrite our format file with the latest version. */ 1581 SVN_ERR(svn_io_write_version_file(format_path, SVN_REPOS__FORMAT_NUMBER, 1582 subpool)); 1583 1584 /* Close shop and free the subpool, to release the exclusive lock. */ 1585 svn_pool_destroy(subpool); 1586 1587 return SVN_NO_ERROR; 1588} 1589 1590 1591svn_error_t * 1592svn_repos_delete(const char *path, 1593 apr_pool_t *pool) 1594{ 1595 const char *db_path = svn_dirent_join(path, SVN_REPOS__DB_DIR, pool); 1596 1597 /* Delete the filesystem environment... */ 1598 SVN_ERR(svn_fs_delete_fs(db_path, pool)); 1599 1600 /* ...then blow away everything else. */ 1601 return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool); 1602} 1603 1604 1605/* Repository supports the capability. */ 1606static const char *capability_yes = "yes"; 1607/* Repository does not support the capability. */ 1608static const char *capability_no = "no"; 1609 1610svn_error_t * 1611svn_repos_has_capability(svn_repos_t *repos, 1612 svn_boolean_t *has, 1613 const char *capability, 1614 apr_pool_t *pool) 1615{ 1616 const char *val = svn_hash_gets(repos->repository_capabilities, capability); 1617 1618 if (val == capability_yes) 1619 { 1620 *has = TRUE; 1621 } 1622 else if (val == capability_no) 1623 { 1624 *has = FALSE; 1625 } 1626 /* Else don't know, so investigate. */ 1627 else if (strcmp(capability, SVN_REPOS_CAPABILITY_MERGEINFO) == 0) 1628 { 1629 svn_error_t *err; 1630 svn_fs_root_t *root; 1631 svn_mergeinfo_catalog_t ignored; 1632 apr_array_header_t *paths = apr_array_make(pool, 1, 1633 sizeof(char *)); 1634 1635 SVN_ERR(svn_fs_revision_root(&root, repos->fs, 0, pool)); 1636 APR_ARRAY_PUSH(paths, const char *) = ""; 1637 err = svn_fs_get_mergeinfo2(&ignored, root, paths, FALSE, FALSE, 1638 TRUE, pool, pool); 1639 1640 if (err) 1641 { 1642 if (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE) 1643 { 1644 svn_error_clear(err); 1645 svn_hash_sets(repos->repository_capabilities, 1646 SVN_REPOS_CAPABILITY_MERGEINFO, capability_no); 1647 *has = FALSE; 1648 } 1649 else if (err->apr_err == SVN_ERR_FS_NOT_FOUND) 1650 { 1651 /* Mergeinfo requests use relative paths, and anyway we're 1652 in r0, so we're likely to get this error -- but it 1653 means the repository supports mergeinfo! */ 1654 svn_error_clear(err); 1655 svn_hash_sets(repos->repository_capabilities, 1656 SVN_REPOS_CAPABILITY_MERGEINFO, capability_yes); 1657 *has = TRUE; 1658 } 1659 else 1660 { 1661 return svn_error_trace(err); 1662 } 1663 } 1664 else 1665 { 1666 svn_hash_sets(repos->repository_capabilities, 1667 SVN_REPOS_CAPABILITY_MERGEINFO, capability_yes); 1668 *has = TRUE; 1669 } 1670 } 1671 else 1672 { 1673 return svn_error_createf(SVN_ERR_UNKNOWN_CAPABILITY, 0, 1674 _("unknown capability '%s'"), capability); 1675 } 1676 1677 return SVN_NO_ERROR; 1678} 1679 1680svn_fs_t * 1681svn_repos_fs(svn_repos_t *repos) 1682{ 1683 if (! repos) 1684 return NULL; 1685 return repos->fs; 1686} 1687 1688 1689/* For historical reasons, for the Berkeley DB backend, this code uses 1690 * repository locking, which is motivated by the need to support the 1691 * Berkeley DB error DB_RUN_RECOVERY. (FSFS takes care of locking 1692 * itself, inside its implementation of svn_fs_recover.) Here's how 1693 * it works: 1694 * 1695 * Every accessor of a repository's database takes out a shared lock 1696 * on the repository -- both readers and writers get shared locks, and 1697 * there can be an unlimited number of shared locks simultaneously. 1698 * 1699 * Sometimes, a db access returns the error DB_RUN_RECOVERY. When 1700 * this happens, we need to run svn_fs_recover() on the db 1701 * with no other accessors present. So we take out an exclusive lock 1702 * on the repository. From the moment we request the exclusive lock, 1703 * no more shared locks are granted, and when the last shared lock 1704 * disappears, the exclusive lock is granted. As soon as we get it, 1705 * we can run recovery. 1706 * 1707 * We assume that once any berkeley call returns DB_RUN_RECOVERY, they 1708 * all do, until recovery is run. 1709 */ 1710 1711svn_error_t * 1712svn_repos_recover4(const char *path, 1713 svn_boolean_t nonblocking, 1714 svn_repos_notify_func_t notify_func, 1715 void *notify_baton, 1716 svn_cancel_func_t cancel_func, 1717 void * cancel_baton, 1718 apr_pool_t *pool) 1719{ 1720 svn_repos_t *repos; 1721 apr_pool_t *subpool = svn_pool_create(pool); 1722 1723 /* Fetch a repository object; for the Berkeley DB backend, it is 1724 initialized with an EXCLUSIVE lock on the database. This will at 1725 least prevent others from trying to read or write to it while we 1726 run recovery. (Other backends should do their own locking; see 1727 lock_repos.) */ 1728 SVN_ERR(get_repos(&repos, path, TRUE, nonblocking, 1729 FALSE, /* don't try to open the db yet. */ 1730 NULL, 1731 subpool)); 1732 1733 if (notify_func) 1734 { 1735 /* We notify *twice* here, because there are two different logistical 1736 actions occuring. */ 1737 svn_repos_notify_t *notify = svn_repos_notify_create( 1738 svn_repos_notify_mutex_acquired, subpool); 1739 notify_func(notify_baton, notify, subpool); 1740 1741 notify->action = svn_repos_notify_recover_start; 1742 notify_func(notify_baton, notify, subpool); 1743 } 1744 1745 /* Recover the database to a consistent state. */ 1746 SVN_ERR(svn_fs_recover(repos->db_path, cancel_func, cancel_baton, subpool)); 1747 1748 /* Close shop and free the subpool, to release the exclusive lock. */ 1749 svn_pool_destroy(subpool); 1750 1751 return SVN_NO_ERROR; 1752} 1753 1754struct freeze_baton_t { 1755 apr_array_header_t *paths; 1756 int counter; 1757 svn_repos_freeze_func_t freeze_func; 1758 void *freeze_baton; 1759}; 1760 1761static svn_error_t * 1762multi_freeze(void *baton, 1763 apr_pool_t *pool) 1764{ 1765 struct freeze_baton_t *fb = baton; 1766 1767 if (fb->counter == fb->paths->nelts) 1768 { 1769 SVN_ERR(fb->freeze_func(fb->freeze_baton, pool)); 1770 return SVN_NO_ERROR; 1771 } 1772 else 1773 { 1774 /* Using a subpool as the only way to unlock the repos lock used 1775 by BDB is to clear the pool used to take the lock. */ 1776 apr_pool_t *subpool = svn_pool_create(pool); 1777 const char *path = APR_ARRAY_IDX(fb->paths, fb->counter, const char *); 1778 svn_repos_t *repos; 1779 1780 ++fb->counter; 1781 1782 SVN_ERR(get_repos(&repos, path, 1783 TRUE /* exclusive (only applies to BDB) */, 1784 FALSE /* non-blocking */, 1785 FALSE /* open-fs */, 1786 NULL, subpool)); 1787 1788 1789 if (strcmp(repos->fs_type, SVN_FS_TYPE_BDB) == 0) 1790 { 1791 svn_error_t *err = multi_freeze(fb, subpool); 1792 1793 svn_pool_destroy(subpool); 1794 1795 return err; 1796 } 1797 else 1798 { 1799 SVN_ERR(svn_fs_open(&repos->fs, repos->db_path, NULL, subpool)); 1800 SVN_ERR(svn_fs_freeze(svn_repos_fs(repos), multi_freeze, fb, 1801 subpool)); 1802 } 1803 1804 svn_pool_destroy(subpool); 1805 } 1806 1807 return SVN_NO_ERROR; 1808} 1809 1810/* For BDB we fall back on BDB's repos layer lock which means that the 1811 repository is unreadable while frozen. 1812 1813 For FSFS we delegate to the FS layer which uses the FSFS write-lock 1814 and an SQLite reserved lock which means the repository is readable 1815 while frozen. */ 1816svn_error_t * 1817svn_repos_freeze(apr_array_header_t *paths, 1818 svn_repos_freeze_func_t freeze_func, 1819 void *freeze_baton, 1820 apr_pool_t *pool) 1821{ 1822 struct freeze_baton_t fb; 1823 1824 fb.paths = paths; 1825 fb.counter = 0; 1826 fb.freeze_func = freeze_func; 1827 fb.freeze_baton = freeze_baton; 1828 1829 SVN_ERR(multi_freeze(&fb, pool)); 1830 1831 return SVN_NO_ERROR; 1832} 1833 1834svn_error_t *svn_repos_db_logfiles(apr_array_header_t **logfiles, 1835 const char *path, 1836 svn_boolean_t only_unused, 1837 apr_pool_t *pool) 1838{ 1839 svn_repos_t *repos; 1840 int i; 1841 1842 SVN_ERR(get_repos(&repos, path, 1843 FALSE, FALSE, 1844 FALSE, /* Do not open fs. */ 1845 NULL, 1846 pool)); 1847 1848 SVN_ERR(svn_fs_berkeley_logfiles(logfiles, 1849 svn_repos_db_env(repos, pool), 1850 only_unused, 1851 pool)); 1852 1853 /* Loop, printing log files. */ 1854 for (i = 0; i < (*logfiles)->nelts; i++) 1855 { 1856 const char ** log_file = &(APR_ARRAY_IDX(*logfiles, i, const char *)); 1857 *log_file = svn_dirent_join(SVN_REPOS__DB_DIR, *log_file, pool); 1858 } 1859 1860 return SVN_NO_ERROR; 1861} 1862 1863/* Baton for hotcopy_structure(). */ 1864struct hotcopy_ctx_t { 1865 const char *dest; /* target location to construct */ 1866 size_t src_len; /* len of the source path*/ 1867 1868 /* As in svn_repos_hotcopy2() */ 1869 svn_boolean_t incremental; 1870 svn_cancel_func_t cancel_func; 1871 void *cancel_baton; 1872}; 1873 1874/* Copy the repository structure of PATH to BATON->DEST, with exception of 1875 * @c SVN_REPOS__DB_DIR, @c SVN_REPOS__LOCK_DIR and @c SVN_REPOS__FORMAT; 1876 * those directories and files are handled separately. 1877 * 1878 * BATON is a (struct hotcopy_ctx_t *). BATON->SRC_LEN is the length 1879 * of PATH. 1880 * 1881 * Implements svn_io_walk_func_t. 1882 */ 1883static svn_error_t *hotcopy_structure(void *baton, 1884 const char *path, 1885 const apr_finfo_t *finfo, 1886 apr_pool_t *pool) 1887{ 1888 const struct hotcopy_ctx_t *ctx = baton; 1889 const char *sub_path; 1890 const char *target; 1891 1892 if (ctx->cancel_func) 1893 SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); 1894 1895 if (strlen(path) == ctx->src_len) 1896 { 1897 sub_path = ""; 1898 } 1899 else 1900 { 1901 sub_path = &path[ctx->src_len+1]; 1902 1903 /* Check if we are inside db directory and if so skip it */ 1904 if (svn_path_compare_paths 1905 (svn_dirent_get_longest_ancestor(SVN_REPOS__DB_DIR, sub_path, pool), 1906 SVN_REPOS__DB_DIR) == 0) 1907 return SVN_NO_ERROR; 1908 1909 if (svn_path_compare_paths 1910 (svn_dirent_get_longest_ancestor(SVN_REPOS__LOCK_DIR, sub_path, 1911 pool), 1912 SVN_REPOS__LOCK_DIR) == 0) 1913 return SVN_NO_ERROR; 1914 1915 if (svn_path_compare_paths 1916 (svn_dirent_get_longest_ancestor(SVN_REPOS__FORMAT, sub_path, pool), 1917 SVN_REPOS__FORMAT) == 0) 1918 return SVN_NO_ERROR; 1919 } 1920 1921 target = svn_dirent_join(ctx->dest, sub_path, pool); 1922 1923 if (finfo->filetype == APR_DIR) 1924 { 1925 svn_error_t *err; 1926 1927 err = create_repos_dir(target, pool); 1928 if (ctx->incremental && err && err->apr_err == SVN_ERR_DIR_NOT_EMPTY) 1929 { 1930 svn_error_clear(err); 1931 err = SVN_NO_ERROR; 1932 } 1933 return svn_error_trace(err); 1934 } 1935 else if (finfo->filetype == APR_REG) 1936 return svn_io_copy_file(path, target, TRUE, pool); 1937 else if (finfo->filetype == APR_LNK) 1938 return svn_io_copy_link(path, target, pool); 1939 else 1940 return SVN_NO_ERROR; 1941} 1942 1943 1944/** Obtain a lock on db logs lock file. Create one if it does not exist. 1945 */ 1946static svn_error_t * 1947lock_db_logs_file(svn_repos_t *repos, 1948 svn_boolean_t exclusive, 1949 apr_pool_t *pool) 1950{ 1951 const char * lock_file = svn_repos_db_logs_lockfile(repos, pool); 1952 1953 /* Try to create a lock file, in case if it is missing. As in case of the 1954 repositories created before hotcopy functionality. */ 1955 svn_error_clear(create_db_logs_lock(repos, pool)); 1956 1957 return svn_io_file_lock2(lock_file, exclusive, FALSE, pool); 1958} 1959 1960 1961/* Make a copy of a repository with hot backup of fs. */ 1962svn_error_t * 1963svn_repos_hotcopy2(const char *src_path, 1964 const char *dst_path, 1965 svn_boolean_t clean_logs, 1966 svn_boolean_t incremental, 1967 svn_cancel_func_t cancel_func, 1968 void *cancel_baton, 1969 apr_pool_t *pool) 1970{ 1971 svn_repos_t *src_repos; 1972 svn_repos_t *dst_repos; 1973 struct hotcopy_ctx_t hotcopy_context; 1974 svn_error_t *err; 1975 const char *src_abspath; 1976 const char *dst_abspath; 1977 1978 SVN_ERR(svn_dirent_get_absolute(&src_abspath, src_path, pool)); 1979 SVN_ERR(svn_dirent_get_absolute(&dst_abspath, dst_path, pool)); 1980 if (strcmp(src_abspath, dst_abspath) == 0) 1981 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 1982 _("Hotcopy source and destination are equal")); 1983 1984 /* Try to open original repository */ 1985 SVN_ERR(get_repos(&src_repos, src_abspath, 1986 FALSE, FALSE, 1987 FALSE, /* don't try to open the db yet. */ 1988 NULL, 1989 pool)); 1990 1991 /* If we are going to clean logs, then get an exclusive lock on 1992 db-logs.lock, to ensure that no one else will work with logs. 1993 1994 If we are just copying, then get a shared lock to ensure that 1995 no one else will clean logs while we copying them */ 1996 1997 SVN_ERR(lock_db_logs_file(src_repos, clean_logs, pool)); 1998 1999 /* Copy the repository to a new path, with exception of 2000 specially handled directories */ 2001 2002 hotcopy_context.dest = dst_abspath; 2003 hotcopy_context.src_len = strlen(src_abspath); 2004 hotcopy_context.incremental = incremental; 2005 hotcopy_context.cancel_func = cancel_func; 2006 hotcopy_context.cancel_baton = cancel_baton; 2007 SVN_ERR(svn_io_dir_walk2(src_abspath, 2008 0, 2009 hotcopy_structure, 2010 &hotcopy_context, 2011 pool)); 2012 2013 /* Prepare dst_repos object so that we may create locks, 2014 so that we may open repository */ 2015 2016 dst_repos = create_svn_repos_t(dst_abspath, pool); 2017 dst_repos->fs_type = src_repos->fs_type; 2018 dst_repos->format = src_repos->format; 2019 2020 err = create_locks(dst_repos, pool); 2021 if (err) 2022 { 2023 if (incremental && err->apr_err == SVN_ERR_DIR_NOT_EMPTY) 2024 svn_error_clear(err); 2025 else 2026 return svn_error_trace(err); 2027 } 2028 2029 err = svn_io_dir_make_sgid(dst_repos->db_path, APR_OS_DEFAULT, pool); 2030 if (err) 2031 { 2032 if (incremental && APR_STATUS_IS_EEXIST(err->apr_err)) 2033 svn_error_clear(err); 2034 else 2035 return svn_error_trace(err); 2036 } 2037 2038 /* Exclusively lock the new repository. 2039 No one should be accessing it at the moment */ 2040 SVN_ERR(lock_repos(dst_repos, TRUE, FALSE, pool)); 2041 2042 SVN_ERR(svn_fs_hotcopy2(src_repos->db_path, dst_repos->db_path, 2043 clean_logs, incremental, 2044 cancel_func, cancel_baton, pool)); 2045 2046 /* Destination repository is ready. Stamp it with a format number. */ 2047 return svn_io_write_version_file 2048 (svn_dirent_join(dst_repos->path, SVN_REPOS__FORMAT, pool), 2049 dst_repos->format, pool); 2050} 2051 2052svn_error_t * 2053svn_repos_hotcopy(const char *src_path, 2054 const char *dst_path, 2055 svn_boolean_t clean_logs, 2056 apr_pool_t *pool) 2057{ 2058 return svn_error_trace(svn_repos_hotcopy2(src_path, dst_path, clean_logs, 2059 FALSE, NULL, NULL, pool)); 2060} 2061 2062/* Return the library version number. */ 2063const svn_version_t * 2064svn_repos_version(void) 2065{ 2066 SVN_VERSION_BODY; 2067} 2068 2069 2070 2071svn_error_t * 2072svn_repos_stat(svn_dirent_t **dirent, 2073 svn_fs_root_t *root, 2074 const char *path, 2075 apr_pool_t *pool) 2076{ 2077 svn_node_kind_t kind; 2078 svn_dirent_t *ent; 2079 const char *datestring; 2080 apr_hash_t *prophash; 2081 2082 SVN_ERR(svn_fs_check_path(&kind, root, path, pool)); 2083 2084 if (kind == svn_node_none) 2085 { 2086 *dirent = NULL; 2087 return SVN_NO_ERROR; 2088 } 2089 2090 ent = svn_dirent_create(pool); 2091 ent->kind = kind; 2092 2093 if (kind == svn_node_file) 2094 SVN_ERR(svn_fs_file_length(&(ent->size), root, path, pool)); 2095 2096 SVN_ERR(svn_fs_node_proplist(&prophash, root, path, pool)); 2097 if (apr_hash_count(prophash) > 0) 2098 ent->has_props = TRUE; 2099 2100 SVN_ERR(svn_repos_get_committed_info(&(ent->created_rev), 2101 &datestring, 2102 &(ent->last_author), 2103 root, path, pool)); 2104 if (datestring) 2105 SVN_ERR(svn_time_from_cstring(&(ent->time), datestring, pool)); 2106 2107 *dirent = ent; 2108 return SVN_NO_ERROR; 2109} 2110 2111svn_error_t * 2112svn_repos_remember_client_capabilities(svn_repos_t *repos, 2113 const apr_array_header_t *capabilities) 2114{ 2115 repos->client_capabilities = capabilities; 2116 return SVN_NO_ERROR; 2117} 2118 2119svn_error_t * 2120svn_repos__fs_type(const char **fs_type, 2121 const char *repos_path, 2122 apr_pool_t *pool) 2123{ 2124 svn_repos_t repos; 2125 repos.path = (char*)repos_path; 2126 2127 SVN_ERR(check_repos_format(&repos, pool)); 2128 2129 return svn_fs_type(fs_type, 2130 svn_dirent_join(repos_path, SVN_REPOS__DB_DIR, pool), 2131 pool); 2132} 2133