1251881Speter/* repos.c : repository creation; shared and exclusive repository locking 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 <apr_pools.h> 24251881Speter#include <apr_file_io.h> 25251881Speter 26251881Speter#include "svn_pools.h" 27251881Speter#include "svn_error.h" 28251881Speter#include "svn_dirent_uri.h" 29251881Speter#include "svn_path.h" 30251881Speter#include "svn_utf.h" 31251881Speter#include "svn_time.h" 32251881Speter#include "svn_fs.h" 33251881Speter#include "svn_ra.h" /* for SVN_RA_CAPABILITY_* */ 34251881Speter#include "svn_repos.h" 35251881Speter#include "svn_hash.h" 36251881Speter#include "svn_version.h" 37251881Speter#include "svn_config.h" 38251881Speter 39251881Speter#include "private/svn_repos_private.h" 40251881Speter#include "private/svn_subr_private.h" 41251881Speter#include "svn_private_config.h" /* for SVN_TEMPLATE_ROOT_DIR */ 42251881Speter 43251881Speter#include "repos.h" 44251881Speter 45251881Speter/* Used to terminate lines in large multi-line string literals. */ 46251881Speter#define NL APR_EOL_STR 47251881Speter 48251881Speter 49251881Speter/* Path accessor functions. */ 50251881Speter 51251881Speter 52251881Speterconst char * 53251881Spetersvn_repos_path(svn_repos_t *repos, apr_pool_t *pool) 54251881Speter{ 55251881Speter return apr_pstrdup(pool, repos->path); 56251881Speter} 57251881Speter 58251881Speter 59251881Speterconst char * 60251881Spetersvn_repos_db_env(svn_repos_t *repos, apr_pool_t *pool) 61251881Speter{ 62251881Speter return apr_pstrdup(pool, repos->db_path); 63251881Speter} 64251881Speter 65251881Speter 66251881Speterconst char * 67251881Spetersvn_repos_conf_dir(svn_repos_t *repos, apr_pool_t *pool) 68251881Speter{ 69251881Speter return apr_pstrdup(pool, repos->conf_path); 70251881Speter} 71251881Speter 72251881Speter 73251881Speterconst char * 74251881Spetersvn_repos_svnserve_conf(svn_repos_t *repos, apr_pool_t *pool) 75251881Speter{ 76251881Speter return svn_dirent_join(repos->conf_path, SVN_REPOS__CONF_SVNSERVE_CONF, pool); 77251881Speter} 78251881Speter 79251881Speter 80251881Speterconst char * 81251881Spetersvn_repos_lock_dir(svn_repos_t *repos, apr_pool_t *pool) 82251881Speter{ 83251881Speter return apr_pstrdup(pool, repos->lock_path); 84251881Speter} 85251881Speter 86251881Speter 87251881Speterconst char * 88251881Spetersvn_repos_db_lockfile(svn_repos_t *repos, apr_pool_t *pool) 89251881Speter{ 90251881Speter return svn_dirent_join(repos->lock_path, SVN_REPOS__DB_LOCKFILE, pool); 91251881Speter} 92251881Speter 93251881Speter 94251881Speterconst char * 95251881Spetersvn_repos_db_logs_lockfile(svn_repos_t *repos, apr_pool_t *pool) 96251881Speter{ 97251881Speter return svn_dirent_join(repos->lock_path, SVN_REPOS__DB_LOGS_LOCKFILE, pool); 98251881Speter} 99251881Speter 100251881Speterconst char * 101251881Spetersvn_repos_hook_dir(svn_repos_t *repos, apr_pool_t *pool) 102251881Speter{ 103251881Speter return apr_pstrdup(pool, repos->hook_path); 104251881Speter} 105251881Speter 106251881Speter 107251881Speterconst char * 108251881Spetersvn_repos_start_commit_hook(svn_repos_t *repos, apr_pool_t *pool) 109251881Speter{ 110251881Speter return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_START_COMMIT, pool); 111251881Speter} 112251881Speter 113251881Speter 114251881Speterconst char * 115251881Spetersvn_repos_pre_commit_hook(svn_repos_t *repos, apr_pool_t *pool) 116251881Speter{ 117251881Speter return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_PRE_COMMIT, pool); 118251881Speter} 119251881Speter 120251881Speter 121251881Speterconst char * 122251881Spetersvn_repos_pre_lock_hook(svn_repos_t *repos, apr_pool_t *pool) 123251881Speter{ 124251881Speter return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_PRE_LOCK, pool); 125251881Speter} 126251881Speter 127251881Speter 128251881Speterconst char * 129251881Spetersvn_repos_pre_unlock_hook(svn_repos_t *repos, apr_pool_t *pool) 130251881Speter{ 131251881Speter return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_PRE_UNLOCK, pool); 132251881Speter} 133251881Speter 134251881Speterconst char * 135251881Spetersvn_repos_post_lock_hook(svn_repos_t *repos, apr_pool_t *pool) 136251881Speter{ 137251881Speter return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_POST_LOCK, pool); 138251881Speter} 139251881Speter 140251881Speter 141251881Speterconst char * 142251881Spetersvn_repos_post_unlock_hook(svn_repos_t *repos, apr_pool_t *pool) 143251881Speter{ 144251881Speter return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_POST_UNLOCK, pool); 145251881Speter} 146251881Speter 147251881Speter 148251881Speterconst char * 149251881Spetersvn_repos_post_commit_hook(svn_repos_t *repos, apr_pool_t *pool) 150251881Speter{ 151251881Speter return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_POST_COMMIT, pool); 152251881Speter} 153251881Speter 154251881Speter 155251881Speterconst char * 156251881Spetersvn_repos_pre_revprop_change_hook(svn_repos_t *repos, apr_pool_t *pool) 157251881Speter{ 158251881Speter return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_PRE_REVPROP_CHANGE, 159251881Speter pool); 160251881Speter} 161251881Speter 162251881Speter 163251881Speterconst char * 164251881Spetersvn_repos_post_revprop_change_hook(svn_repos_t *repos, apr_pool_t *pool) 165251881Speter{ 166251881Speter return svn_dirent_join(repos->hook_path, SVN_REPOS__HOOK_POST_REVPROP_CHANGE, 167251881Speter pool); 168251881Speter} 169251881Speter 170251881Speterstatic svn_error_t * 171251881Spetercreate_repos_dir(const char *path, apr_pool_t *pool) 172251881Speter{ 173251881Speter svn_error_t *err; 174251881Speter 175251881Speter err = svn_io_dir_make(path, APR_OS_DEFAULT, pool); 176251881Speter if (err && (APR_STATUS_IS_EEXIST(err->apr_err))) 177251881Speter { 178251881Speter svn_boolean_t is_empty; 179251881Speter 180251881Speter svn_error_clear(err); 181251881Speter 182251881Speter SVN_ERR(svn_io_dir_empty(&is_empty, path, pool)); 183251881Speter 184251881Speter if (is_empty) 185251881Speter err = NULL; 186251881Speter else 187251881Speter err = svn_error_createf(SVN_ERR_DIR_NOT_EMPTY, 0, 188251881Speter _("'%s' exists and is non-empty"), 189251881Speter svn_dirent_local_style(path, pool)); 190251881Speter } 191251881Speter 192251881Speter return svn_error_trace(err); 193251881Speter} 194251881Speter 195251881Speterstatic const char * bdb_lock_file_contents = 196251881Speter "DB lock file, representing locks on the versioned filesystem." NL 197251881Speter "" NL 198251881Speter "All accessors -- both readers and writers -- of the repository's" NL 199251881Speter "Berkeley DB environment take out shared locks on this file, and" NL 200251881Speter "each accessor removes its lock when done. If and when the DB" NL 201251881Speter "recovery procedure is run, the recovery code takes out an" NL 202251881Speter "exclusive lock on this file, so we can be sure no one else is" NL 203251881Speter "using the DB during the recovery." NL 204251881Speter "" NL 205251881Speter "You should never have to edit or remove this file." NL; 206251881Speter 207251881Speterstatic const char * bdb_logs_lock_file_contents = 208251881Speter "DB logs lock file, representing locks on the versioned filesystem logs." NL 209251881Speter "" NL 210251881Speter "All log manipulators of the repository's Berkeley DB environment" NL 211251881Speter "take out exclusive locks on this file to ensure that only one" NL 212251881Speter "accessor manipulates the logs at a time." NL 213251881Speter "" NL 214251881Speter "You should never have to edit or remove this file." NL; 215251881Speter 216251881Speterstatic const char * pre12_compat_unneeded_file_contents = 217251881Speter "This file is not used by Subversion 1.3.x or later." NL 218251881Speter "However, its existence is required for compatibility with" NL 219251881Speter "Subversion 1.2.x or earlier." NL; 220251881Speter 221251881Speter/* Create the DB logs lockfile. */ 222251881Speterstatic svn_error_t * 223251881Spetercreate_db_logs_lock(svn_repos_t *repos, apr_pool_t *pool) { 224251881Speter const char *contents; 225251881Speter const char *lockfile_path; 226251881Speter 227251881Speter lockfile_path = svn_repos_db_logs_lockfile(repos, pool); 228251881Speter if (strcmp(repos->fs_type, SVN_FS_TYPE_BDB) == 0) 229251881Speter contents = bdb_logs_lock_file_contents; 230251881Speter else 231251881Speter contents = pre12_compat_unneeded_file_contents; 232251881Speter 233251881Speter SVN_ERR_W(svn_io_file_create(lockfile_path, contents, pool), 234251881Speter _("Creating db logs lock file")); 235251881Speter 236251881Speter return SVN_NO_ERROR; 237251881Speter} 238251881Speter 239251881Speter/* Create the DB lockfile. */ 240251881Speterstatic svn_error_t * 241251881Spetercreate_db_lock(svn_repos_t *repos, apr_pool_t *pool) { 242251881Speter const char *contents; 243251881Speter const char *lockfile_path; 244251881Speter 245251881Speter lockfile_path = svn_repos_db_lockfile(repos, pool); 246251881Speter if (strcmp(repos->fs_type, SVN_FS_TYPE_BDB) == 0) 247251881Speter contents = bdb_lock_file_contents; 248251881Speter else 249251881Speter contents = pre12_compat_unneeded_file_contents; 250251881Speter 251251881Speter SVN_ERR_W(svn_io_file_create(lockfile_path, contents, pool), 252251881Speter _("Creating db lock file")); 253251881Speter 254251881Speter return SVN_NO_ERROR; 255251881Speter} 256251881Speter 257251881Speterstatic svn_error_t * 258251881Spetercreate_locks(svn_repos_t *repos, apr_pool_t *pool) 259251881Speter{ 260251881Speter /* Create the locks directory. */ 261251881Speter SVN_ERR_W(create_repos_dir(repos->lock_path, pool), 262251881Speter _("Creating lock dir")); 263251881Speter 264251881Speter SVN_ERR(create_db_lock(repos, pool)); 265251881Speter return create_db_logs_lock(repos, pool); 266251881Speter} 267251881Speter 268251881Speter 269251881Speter#define HOOKS_ENVIRONMENT_TEXT \ 270251881Speter "# The hook program typically does not inherit the environment of" NL \ 271251881Speter "# its parent process. For example, a common problem is for the" NL \ 272251881Speter "# PATH environment variable to not be set to its usual value, so" NL \ 273251881Speter "# that subprograms fail to launch unless invoked via absolute path." NL \ 274251881Speter "# If you're having unexpected problems with a hook program, the" NL \ 275251881Speter "# culprit may be unusual (or missing) environment variables." NL 276251881Speter 277251881Speter#define PREWRITTEN_HOOKS_TEXT \ 278251881Speter "# For more examples and pre-written hooks, see those in" NL \ 279251881Speter "# the Subversion repository at" NL \ 280251881Speter "# http://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/ and" NL \ 281251881Speter "# http://svn.apache.org/repos/asf/subversion/trunk/contrib/hook-scripts/" NL 282251881Speter 283251881Speter 284251881Speterstatic svn_error_t * 285251881Spetercreate_hooks(svn_repos_t *repos, apr_pool_t *pool) 286251881Speter{ 287251881Speter const char *this_path, *contents; 288251881Speter 289251881Speter /* Create the hook directory. */ 290251881Speter SVN_ERR_W(create_repos_dir(repos->hook_path, pool), 291251881Speter _("Creating hook directory")); 292251881Speter 293251881Speter /*** Write a default template for each standard hook file. */ 294251881Speter 295251881Speter /* Start-commit hook. */ 296251881Speter { 297251881Speter this_path = apr_psprintf(pool, "%s%s", 298251881Speter svn_repos_start_commit_hook(repos, pool), 299251881Speter SVN_REPOS__HOOK_DESC_EXT); 300251881Speter 301251881Speter#define SCRIPT_NAME SVN_REPOS__HOOK_START_COMMIT 302251881Speter 303251881Speter contents = 304251881Speter"#!/bin/sh" NL 305251881Speter"" NL 306251881Speter"# START-COMMIT HOOK" NL 307251881Speter"#" NL 308251881Speter"# The start-commit hook is invoked immediately after a Subversion txn is" NL 309251881Speter"# created and populated with initial revprops in the process of doing a" NL 310251881Speter"# commit. Subversion runs this hook by invoking a program (script, " NL 311251881Speter"# executable, binary, etc.) named '"SCRIPT_NAME"' (for which this file" NL 312251881Speter"# is a template) with the following ordered arguments:" NL 313251881Speter"#" NL 314251881Speter"# [1] REPOS-PATH (the path to this repository)" NL 315251881Speter"# [2] USER (the authenticated user attempting to commit)" NL 316251881Speter"# [3] CAPABILITIES (a colon-separated list of capabilities reported" NL 317251881Speter"# by the client; see note below)" NL 318251881Speter"# [4] TXN-NAME (the name of the commit txn just created)" NL 319251881Speter"#" NL 320251881Speter"# Note: The CAPABILITIES parameter is new in Subversion 1.5, and 1.5" NL 321251881Speter"# clients will typically report at least the \"" \ 322251881Speter SVN_RA_CAPABILITY_MERGEINFO "\" capability." NL 323251881Speter"# If there are other capabilities, then the list is colon-separated," NL 324251881Speter"# e.g.: \"" SVN_RA_CAPABILITY_MERGEINFO ":some-other-capability\" " \ 325251881Speter "(the order is undefined)." NL 326251881Speter"#" NL 327251881Speter"# Note: The TXN-NAME parameter is new in Subversion 1.8. Prior to version" NL 328251881Speter"# 1.8, the start-commit hook was invoked before the commit txn was even" NL 329251881Speter"# created, so the ability to inspect the commit txn and its metadata from" NL 330251881Speter"# within the start-commit hook was not possible." NL 331251881Speter"# " NL 332251881Speter"# The list is self-reported by the client. Therefore, you should not" NL 333251881Speter"# make security assumptions based on the capabilities list, nor should" NL 334251881Speter"# you assume that clients reliably report every capability they have." NL 335251881Speter"#" NL 336251881Speter"# The working directory for this hook program's invocation is undefined," NL 337251881Speter"# so the program should set one explicitly if it cares." NL 338251881Speter"#" NL 339251881Speter"# If the hook program exits with success, the commit continues; but" NL 340251881Speter"# if it exits with failure (non-zero), the commit is stopped before" NL 341251881Speter"# a Subversion txn is created, and STDERR is returned to the client." NL 342251881Speter"#" NL 343251881Speter"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 344251881Speter"# invoke other programs to do the real work, though it may do the" NL 345251881Speter"# work itself too." NL 346251881Speter"#" NL 347251881Speter"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 348251881Speter"# invoke it (typically the user httpd runs as), and that user must" NL 349251881Speter"# have filesystem-level permission to access the repository." NL 350251881Speter"#" NL 351251881Speter"# On a Windows system, you should name the hook program" NL 352251881Speter"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 353251881Speter"# but the basic idea is the same." NL 354251881Speter"# " NL 355251881SpeterHOOKS_ENVIRONMENT_TEXT 356251881Speter"# " NL 357251881Speter"# Here is an example hook script, for a Unix /bin/sh interpreter." NL 358251881SpeterPREWRITTEN_HOOKS_TEXT 359251881Speter"" NL 360251881Speter"" NL 361251881Speter"REPOS=\"$1\"" NL 362251881Speter"USER=\"$2\"" NL 363251881Speter"" NL 364251881Speter"commit-allower.pl --repository \"$REPOS\" --user \"$USER\" || exit 1" NL 365251881Speter"special-auth-check.py --user \"$USER\" --auth-level 3 || exit 1" NL 366251881Speter"" NL 367251881Speter"# All checks passed, so allow the commit." NL 368251881Speter"exit 0" NL; 369251881Speter 370251881Speter#undef SCRIPT_NAME 371251881Speter 372251881Speter SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 373251881Speter _("Creating start-commit hook")); 374251881Speter 375251881Speter SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 376251881Speter } /* end start-commit hook */ 377251881Speter 378251881Speter /* Pre-commit hook. */ 379251881Speter { 380251881Speter this_path = apr_psprintf(pool, "%s%s", 381251881Speter svn_repos_pre_commit_hook(repos, pool), 382251881Speter SVN_REPOS__HOOK_DESC_EXT); 383251881Speter 384251881Speter#define SCRIPT_NAME SVN_REPOS__HOOK_PRE_COMMIT 385251881Speter 386251881Speter contents = 387251881Speter"#!/bin/sh" NL 388251881Speter"" NL 389251881Speter"# PRE-COMMIT HOOK" NL 390251881Speter"#" NL 391251881Speter"# The pre-commit hook is invoked before a Subversion txn is" NL 392251881Speter"# committed. Subversion runs this hook by invoking a program" NL 393251881Speter"# (script, executable, binary, etc.) named '"SCRIPT_NAME"' (for which" NL 394251881Speter"# this file is a template), with the following ordered arguments:" NL 395251881Speter"#" NL 396251881Speter"# [1] REPOS-PATH (the path to this repository)" NL 397251881Speter"# [2] TXN-NAME (the name of the txn about to be committed)" NL 398251881Speter"#" NL 399251881Speter"# [STDIN] LOCK-TOKENS ** the lock tokens are passed via STDIN." NL 400251881Speter"#" NL 401251881Speter"# If STDIN contains the line \"LOCK-TOKENS:\\n\" (the \"\\n\" denotes a" NL 402251881Speter"# single newline), the lines following it are the lock tokens for" NL 403251881Speter"# this commit. The end of the list is marked by a line containing" NL 404251881Speter"# only a newline character." NL 405251881Speter"#" NL 406251881Speter"# Each lock token line consists of a URI-escaped path, followed" NL 407251881Speter"# by the separator character '|', followed by the lock token string," NL 408251881Speter"# followed by a newline." NL 409251881Speter"#" NL 410251881Speter"# The default working directory for the invocation is undefined, so" NL 411251881Speter"# the program should set one explicitly if it cares." NL 412251881Speter"#" NL 413251881Speter"# If the hook program exits with success, the txn is committed; but" NL 414251881Speter"# if it exits with failure (non-zero), the txn is aborted, no commit" NL 415251881Speter"# takes place, and STDERR is returned to the client. The hook" NL 416251881Speter"# program can use the 'svnlook' utility to help it examine the txn." NL 417251881Speter"#" NL 418251881Speter"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 419251881Speter"# invoke other programs to do the real work, though it may do the" NL 420251881Speter"# work itself too." NL 421251881Speter"#" NL 422251881Speter"# *** NOTE: THE HOOK PROGRAM MUST NOT MODIFY THE TXN, EXCEPT ***" NL 423251881Speter"# *** FOR REVISION PROPERTIES (like svn:log or svn:author). ***" NL 424251881Speter"#" NL 425251881Speter"# This is why we recommend using the read-only 'svnlook' utility." NL 426251881Speter"# In the future, Subversion may enforce the rule that pre-commit" NL 427251881Speter"# hooks should not modify the versioned data in txns, or else come" NL 428251881Speter"# up with a mechanism to make it safe to do so (by informing the" NL 429251881Speter"# committing client of the changes). However, right now neither" NL 430251881Speter"# mechanism is implemented, so hook writers just have to be careful." NL 431251881Speter"#" NL 432251881Speter"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 433251881Speter"# invoke it (typically the user httpd runs as), and that user must" NL 434251881Speter"# have filesystem-level permission to access the repository." NL 435251881Speter"#" NL 436251881Speter"# On a Windows system, you should name the hook program" NL 437251881Speter"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 438251881Speter"# but the basic idea is the same." NL 439251881Speter"#" NL 440251881SpeterHOOKS_ENVIRONMENT_TEXT 441251881Speter"# " NL 442251881Speter"# Here is an example hook script, for a Unix /bin/sh interpreter." NL 443251881SpeterPREWRITTEN_HOOKS_TEXT 444251881Speter"" NL 445251881Speter"" NL 446251881Speter"REPOS=\"$1\"" NL 447251881Speter"TXN=\"$2\"" NL 448251881Speter"" NL 449251881Speter"# Make sure that the log message contains some text." NL 450251881Speter"SVNLOOK=" SVN_BINDIR "/svnlook" NL 451251881Speter"$SVNLOOK log -t \"$TXN\" \"$REPOS\" | \\" NL 452251881Speter" grep \"[a-zA-Z0-9]\" > /dev/null || exit 1" NL 453251881Speter"" NL 454251881Speter"# Check that the author of this commit has the rights to perform" NL 455251881Speter"# the commit on the files and directories being modified." NL 456251881Speter"commit-access-control.pl \"$REPOS\" \"$TXN\" commit-access-control.cfg || exit 1" 457251881Speter NL 458251881Speter"" NL 459251881Speter"# All checks passed, so allow the commit." NL 460251881Speter"exit 0" NL; 461251881Speter 462251881Speter#undef SCRIPT_NAME 463251881Speter 464251881Speter SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 465251881Speter _("Creating pre-commit hook")); 466251881Speter 467251881Speter SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 468251881Speter } /* end pre-commit hook */ 469251881Speter 470251881Speter 471251881Speter /* Pre-revprop-change hook. */ 472251881Speter { 473251881Speter this_path = apr_psprintf(pool, "%s%s", 474251881Speter svn_repos_pre_revprop_change_hook(repos, pool), 475251881Speter SVN_REPOS__HOOK_DESC_EXT); 476251881Speter 477251881Speter#define SCRIPT_NAME SVN_REPOS__HOOK_PRE_REVPROP_CHANGE 478251881Speter 479251881Speter contents = 480251881Speter"#!/bin/sh" NL 481251881Speter"" NL 482251881Speter"# PRE-REVPROP-CHANGE HOOK" NL 483251881Speter"#" NL 484251881Speter"# The pre-revprop-change hook is invoked before a revision property" NL 485251881Speter"# is added, modified or deleted. Subversion runs this hook by invoking" NL 486251881Speter"# a program (script, executable, binary, etc.) named '"SCRIPT_NAME"'" NL 487251881Speter"# (for which this file is a template), with the following ordered" NL 488251881Speter"# arguments:" NL 489251881Speter"#" NL 490251881Speter"# [1] REPOS-PATH (the path to this repository)" NL 491251881Speter"# [2] REV (the revision being tweaked)" NL 492251881Speter"# [3] USER (the username of the person tweaking the property)" NL 493251881Speter"# [4] PROPNAME (the property being set on the revision)" NL 494251881Speter"# [5] ACTION (the property is being 'A'dded, 'M'odified, or 'D'eleted)" 495251881Speter NL 496251881Speter"#" NL 497251881Speter"# [STDIN] PROPVAL ** the new property value is passed via STDIN." NL 498251881Speter"#" NL 499251881Speter"# If the hook program exits with success, the propchange happens; but" NL 500251881Speter"# if it exits with failure (non-zero), the propchange doesn't happen." NL 501251881Speter"# The hook program can use the 'svnlook' utility to examine the " NL 502251881Speter"# existing value of the revision property." NL 503251881Speter"#" NL 504251881Speter"# WARNING: unlike other hooks, this hook MUST exist for revision" NL 505251881Speter"# properties to be changed. If the hook does not exist, Subversion " NL 506251881Speter"# will behave as if the hook were present, but failed. The reason" NL 507251881Speter"# for this is that revision properties are UNVERSIONED, meaning that" NL 508251881Speter"# a successful propchange is destructive; the old value is gone" NL 509251881Speter"# forever. We recommend the hook back up the old value somewhere." NL 510251881Speter"#" NL 511251881Speter"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 512251881Speter"# invoke other programs to do the real work, though it may do the" NL 513251881Speter"# work itself too." NL 514251881Speter"#" NL 515251881Speter"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 516251881Speter"# invoke it (typically the user httpd runs as), and that user must" NL 517251881Speter"# have filesystem-level permission to access the repository." NL 518251881Speter"#" NL 519251881Speter"# On a Windows system, you should name the hook program" NL 520251881Speter"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 521251881Speter"# but the basic idea is the same." NL 522251881Speter"#" NL 523251881SpeterHOOKS_ENVIRONMENT_TEXT 524251881Speter"# " NL 525251881Speter"# Here is an example hook script, for a Unix /bin/sh interpreter." NL 526251881SpeterPREWRITTEN_HOOKS_TEXT 527251881Speter"" NL 528251881Speter"" NL 529251881Speter"REPOS=\"$1\"" NL 530251881Speter"REV=\"$2\"" NL 531251881Speter"USER=\"$3\"" NL 532251881Speter"PROPNAME=\"$4\"" NL 533251881Speter"ACTION=\"$5\"" NL 534251881Speter"" NL 535251881Speter"if [ \"$ACTION\" = \"M\" -a \"$PROPNAME\" = \"svn:log\" ]; then exit 0; fi" NL 536251881Speter"" NL 537251881Speter"echo \"Changing revision properties other than svn:log is prohibited\" >&2" NL 538251881Speter"exit 1" NL; 539251881Speter 540251881Speter#undef SCRIPT_NAME 541251881Speter 542251881Speter SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 543251881Speter _("Creating pre-revprop-change hook")); 544251881Speter 545251881Speter SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 546251881Speter } /* end pre-revprop-change hook */ 547251881Speter 548251881Speter 549251881Speter /* Pre-lock hook. */ 550251881Speter { 551251881Speter this_path = apr_psprintf(pool, "%s%s", 552251881Speter svn_repos_pre_lock_hook(repos, pool), 553251881Speter SVN_REPOS__HOOK_DESC_EXT); 554251881Speter 555251881Speter#define SCRIPT_NAME SVN_REPOS__HOOK_PRE_LOCK 556251881Speter 557251881Speter contents = 558251881Speter"#!/bin/sh" NL 559251881Speter"" NL 560251881Speter"# PRE-LOCK HOOK" NL 561251881Speter"#" NL 562251881Speter"# The pre-lock hook is invoked before an exclusive lock is" NL 563251881Speter"# created. Subversion runs this hook by invoking a program " NL 564251881Speter"# (script, executable, binary, etc.) named '"SCRIPT_NAME"' (for which" NL 565251881Speter"# this file is a template), with the following ordered arguments:" NL 566251881Speter"#" NL 567251881Speter"# [1] REPOS-PATH (the path to this repository)" NL 568251881Speter"# [2] PATH (the path in the repository about to be locked)" NL 569251881Speter"# [3] USER (the user creating the lock)" NL 570251881Speter"# [4] COMMENT (the comment of the lock)" NL 571251881Speter"# [5] STEAL-LOCK (1 if the user is trying to steal the lock, else 0)" NL 572251881Speter"#" NL 573251881Speter"# If the hook program outputs anything on stdout, the output string will" NL 574251881Speter"# be used as the lock token for this lock operation. If you choose to use" NL 575251881Speter"# this feature, you must guarantee the tokens generated are unique across" NL 576251881Speter"# the repository each time." NL 577251881Speter"#" NL 578251881Speter"# The default working directory for the invocation is undefined, so" NL 579251881Speter"# the program should set one explicitly if it cares." NL 580251881Speter"#" NL 581251881Speter"# If the hook program exits with success, the lock is created; but" NL 582251881Speter"# if it exits with failure (non-zero), the lock action is aborted" NL 583251881Speter"# and STDERR is returned to the client." NL 584251881Speter"" NL 585251881Speter"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 586251881Speter"# invoke other programs to do the real work, though it may do the" NL 587251881Speter"# work itself too." NL 588251881Speter"#" NL 589251881Speter"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 590251881Speter"# invoke it (typically the user httpd runs as), and that user must" NL 591251881Speter"# have filesystem-level permission to access the repository." NL 592251881Speter"#" NL 593251881Speter"# On a Windows system, you should name the hook program" NL 594251881Speter"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 595251881Speter"# but the basic idea is the same." NL 596251881Speter"#" NL 597251881Speter"# Here is an example hook script, for a Unix /bin/sh interpreter:" NL 598251881Speter"" NL 599251881Speter"REPOS=\"$1\"" NL 600251881Speter"PATH=\"$2\"" NL 601251881Speter"USER=\"$3\"" NL 602251881Speter"COMMENT=\"$4\"" NL 603251881Speter"STEAL=\"$5\"" NL 604251881Speter"" NL 605251881Speter"# If a lock exists and is owned by a different person, don't allow it" NL 606251881Speter"# to be stolen (e.g., with 'svn lock --force ...')." NL 607251881Speter"" NL 608251881Speter"# (Maybe this script could send email to the lock owner?)" NL 609251881Speter"SVNLOOK=" SVN_BINDIR "/svnlook" NL 610251881Speter"GREP=/bin/grep" NL 611251881Speter"SED=/bin/sed" NL 612251881Speter"" NL 613251881Speter"LOCK_OWNER=`$SVNLOOK lock \"$REPOS\" \"$PATH\" | \\" NL 614251881Speter" $GREP '^Owner: ' | $SED 's/Owner: //'`" NL 615251881Speter"" NL 616251881Speter"# If we get no result from svnlook, there's no lock, allow the lock to" NL 617251881Speter"# happen:" NL 618251881Speter"if [ \"$LOCK_OWNER\" = \"\" ]; then" NL 619251881Speter" exit 0" NL 620251881Speter"fi" NL 621251881Speter"" NL 622251881Speter"# If the person locking matches the lock's owner, allow the lock to" NL 623251881Speter"# happen:" NL 624251881Speter"if [ \"$LOCK_OWNER\" = \"$USER\" ]; then" NL 625251881Speter" exit 0" NL 626251881Speter"fi" NL 627251881Speter"" NL 628251881Speter"# Otherwise, we've got an owner mismatch, so return failure:" NL 629251881Speter"echo \"Error: $PATH already locked by ${LOCK_OWNER}.\" 1>&2" NL 630251881Speter"exit 1" NL; 631251881Speter 632251881Speter#undef SCRIPT_NAME 633251881Speter 634251881Speter SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 635251881Speter "Creating pre-lock hook"); 636251881Speter 637251881Speter SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 638251881Speter } /* end pre-lock hook */ 639251881Speter 640251881Speter 641251881Speter /* Pre-unlock hook. */ 642251881Speter { 643251881Speter this_path = apr_psprintf(pool, "%s%s", 644251881Speter svn_repos_pre_unlock_hook(repos, pool), 645251881Speter SVN_REPOS__HOOK_DESC_EXT); 646251881Speter 647251881Speter#define SCRIPT_NAME SVN_REPOS__HOOK_PRE_UNLOCK 648251881Speter 649251881Speter contents = 650251881Speter"#!/bin/sh" NL 651251881Speter"" NL 652251881Speter"# PRE-UNLOCK HOOK" NL 653251881Speter"#" NL 654251881Speter"# The pre-unlock hook is invoked before an exclusive lock is" NL 655251881Speter"# destroyed. Subversion runs this hook by invoking a program " NL 656251881Speter"# (script, executable, binary, etc.) named '"SCRIPT_NAME"' (for which" NL 657251881Speter"# this file is a template), with the following ordered arguments:" NL 658251881Speter"#" NL 659251881Speter"# [1] REPOS-PATH (the path to this repository)" NL 660251881Speter"# [2] PATH (the path in the repository about to be unlocked)" NL 661251881Speter"# [3] USER (the user destroying the lock)" NL 662251881Speter"# [4] TOKEN (the lock token to be destroyed)" NL 663251881Speter"# [5] BREAK-UNLOCK (1 if the user is breaking the lock, else 0)" NL 664251881Speter"#" NL 665251881Speter"# The default working directory for the invocation is undefined, so" NL 666251881Speter"# the program should set one explicitly if it cares." NL 667251881Speter"#" NL 668251881Speter"# If the hook program exits with success, the lock is destroyed; but" NL 669251881Speter"# if it exits with failure (non-zero), the unlock action is aborted" NL 670251881Speter"# and STDERR is returned to the client." NL 671251881Speter"" NL 672251881Speter"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 673251881Speter"# invoke other programs to do the real work, though it may do the" NL 674251881Speter"# work itself too." NL 675251881Speter"#" NL 676251881Speter"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 677251881Speter"# invoke it (typically the user httpd runs as), and that user must" NL 678251881Speter"# have filesystem-level permission to access the repository." NL 679251881Speter"#" NL 680251881Speter"# On a Windows system, you should name the hook program" NL 681251881Speter"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 682251881Speter"# but the basic idea is the same." NL 683251881Speter"#" NL 684251881Speter"# Here is an example hook script, for a Unix /bin/sh interpreter:" NL 685251881Speter"" NL 686251881Speter"REPOS=\"$1\"" NL 687251881Speter"PATH=\"$2\"" NL 688251881Speter"USER=\"$3\"" NL 689251881Speter"TOKEN=\"$4\"" NL 690251881Speter"BREAK=\"$5\"" NL 691251881Speter"" NL 692251881Speter"# If a lock is owned by a different person, don't allow it be broken." NL 693251881Speter"# (Maybe this script could send email to the lock owner?)" NL 694251881Speter"" NL 695251881Speter"SVNLOOK=" SVN_BINDIR "/svnlook" NL 696251881Speter"GREP=/bin/grep" NL 697251881Speter"SED=/bin/sed" NL 698251881Speter"" NL 699251881Speter"LOCK_OWNER=`$SVNLOOK lock \"$REPOS\" \"$PATH\" | \\" NL 700251881Speter" $GREP '^Owner: ' | $SED 's/Owner: //'`" NL 701251881Speter"" NL 702251881Speter"# If we get no result from svnlook, there's no lock, return success:" NL 703251881Speter"if [ \"$LOCK_OWNER\" = \"\" ]; then" NL 704251881Speter" exit 0" NL 705251881Speter"fi" NL 706251881Speter"" NL 707251881Speter"# If the person unlocking matches the lock's owner, return success:" NL 708251881Speter"if [ \"$LOCK_OWNER\" = \"$USER\" ]; then" NL 709251881Speter" exit 0" NL 710251881Speter"fi" NL 711251881Speter"" NL 712251881Speter"# Otherwise, we've got an owner mismatch, so return failure:" NL 713251881Speter"echo \"Error: $PATH locked by ${LOCK_OWNER}.\" 1>&2" NL 714251881Speter"exit 1" NL; 715251881Speter 716251881Speter#undef SCRIPT_NAME 717251881Speter 718251881Speter SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 719251881Speter "Creating pre-unlock hook"); 720251881Speter 721251881Speter SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 722251881Speter } /* end pre-unlock hook */ 723251881Speter 724251881Speter 725251881Speter 726251881Speter /* Post-commit hook. */ 727251881Speter { 728251881Speter this_path = apr_psprintf(pool, "%s%s", 729251881Speter svn_repos_post_commit_hook(repos, pool), 730251881Speter SVN_REPOS__HOOK_DESC_EXT); 731251881Speter 732251881Speter#define SCRIPT_NAME SVN_REPOS__HOOK_POST_COMMIT 733251881Speter 734251881Speter contents = 735251881Speter"#!/bin/sh" NL 736251881Speter"" NL 737251881Speter"# POST-COMMIT HOOK" NL 738251881Speter"#" NL 739251881Speter"# The post-commit hook is invoked after a commit. Subversion runs" NL 740251881Speter"# this hook by invoking a program (script, executable, binary, etc.)" NL 741251881Speter"# named '"SCRIPT_NAME"' (for which this file is a template) with the " NL 742251881Speter"# following ordered arguments:" NL 743251881Speter"#" NL 744251881Speter"# [1] REPOS-PATH (the path to this repository)" NL 745251881Speter"# [2] REV (the number of the revision just committed)" NL 746251881Speter"# [3] TXN-NAME (the name of the transaction that has become REV)" NL 747251881Speter"#" NL 748251881Speter"# The default working directory for the invocation is undefined, so" NL 749251881Speter"# the program should set one explicitly if it cares." NL 750251881Speter"#" NL 751251881Speter"# Because the commit has already completed and cannot be undone," NL 752251881Speter"# the exit code of the hook program is ignored. The hook program" NL 753251881Speter"# can use the 'svnlook' utility to help it examine the" NL 754251881Speter"# newly-committed tree." NL 755251881Speter"#" NL 756251881Speter"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 757251881Speter"# invoke other programs to do the real work, though it may do the" NL 758251881Speter"# work itself too." NL 759251881Speter"#" NL 760251881Speter"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 761251881Speter"# invoke it (typically the user httpd runs as), and that user must" NL 762251881Speter"# have filesystem-level permission to access the repository." NL 763251881Speter"#" NL 764251881Speter"# On a Windows system, you should name the hook program" NL 765251881Speter"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 766251881Speter"# but the basic idea is the same." NL 767251881Speter"# " NL 768251881SpeterHOOKS_ENVIRONMENT_TEXT 769251881Speter"# " NL 770251881Speter"# Here is an example hook script, for a Unix /bin/sh interpreter." NL 771251881SpeterPREWRITTEN_HOOKS_TEXT 772251881Speter"" NL 773251881Speter"" NL 774251881Speter"REPOS=\"$1\"" NL 775251881Speter"REV=\"$2\"" NL 776251881Speter"TXN_NAME=\"$3\"" NL 777251881Speter NL 778251881Speter"mailer.py commit \"$REPOS\" \"$REV\" /path/to/mailer.conf" NL; 779251881Speter 780251881Speter#undef SCRIPT_NAME 781251881Speter 782251881Speter SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 783251881Speter _("Creating post-commit hook")); 784251881Speter 785251881Speter SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 786251881Speter } /* end post-commit hook */ 787251881Speter 788251881Speter 789251881Speter /* Post-lock hook. */ 790251881Speter { 791251881Speter this_path = apr_psprintf(pool, "%s%s", 792251881Speter svn_repos_post_lock_hook(repos, pool), 793251881Speter SVN_REPOS__HOOK_DESC_EXT); 794251881Speter 795251881Speter#define SCRIPT_NAME SVN_REPOS__HOOK_POST_LOCK 796251881Speter 797251881Speter contents = 798251881Speter"#!/bin/sh" NL 799251881Speter"" NL 800251881Speter"# POST-LOCK HOOK" NL 801251881Speter"#" NL 802251881Speter"# The post-lock hook is run after a path is locked. Subversion runs" NL 803251881Speter"# this hook by invoking a program (script, executable, binary, etc.)" NL 804251881Speter"# named '"SCRIPT_NAME"' (for which this file is a template) with the " NL 805251881Speter"# following ordered arguments:" NL 806251881Speter"#" NL 807251881Speter"# [1] REPOS-PATH (the path to this repository)" NL 808251881Speter"# [2] USER (the user who created the lock)" NL 809251881Speter"#" NL 810251881Speter"# The paths that were just locked are passed to the hook via STDIN (as" NL 811251881Speter"# of Subversion 1.2, only one path is passed per invocation, but the" NL 812251881Speter"# plan is to pass all locked paths at once, so the hook program" NL 813251881Speter"# should be written accordingly)." NL 814251881Speter"#" NL 815251881Speter"# The default working directory for the invocation is undefined, so" NL 816251881Speter"# the program should set one explicitly if it cares." NL 817251881Speter"#" NL 818251881Speter"# Because the lock has already been created and cannot be undone," NL 819251881Speter"# the exit code of the hook program is ignored. The hook program" NL 820251881Speter"# can use the 'svnlook' utility to help it examine the" NL 821251881Speter"# newly-created lock." NL 822251881Speter"#" NL 823251881Speter"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 824251881Speter"# invoke other programs to do the real work, though it may do the" NL 825251881Speter"# work itself too." NL 826251881Speter"#" NL 827251881Speter"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 828251881Speter"# invoke it (typically the user httpd runs as), and that user must" NL 829251881Speter"# have filesystem-level permission to access the repository." NL 830251881Speter"#" NL 831251881Speter"# On a Windows system, you should name the hook program" NL 832251881Speter"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 833251881Speter"# but the basic idea is the same." NL 834251881Speter"# " NL 835251881Speter"# Here is an example hook script, for a Unix /bin/sh interpreter:" NL 836251881Speter"" NL 837251881Speter"REPOS=\"$1\"" NL 838251881Speter"USER=\"$2\"" NL 839251881Speter"" NL 840251881Speter"# Send email to interested parties, let them know a lock was created:" NL 841251881Speter"mailer.py lock \"$REPOS\" \"$USER\" /path/to/mailer.conf" NL; 842251881Speter 843251881Speter#undef SCRIPT_NAME 844251881Speter 845251881Speter SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 846251881Speter "Creating post-lock hook"); 847251881Speter 848251881Speter SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 849251881Speter } /* end post-lock hook */ 850251881Speter 851251881Speter 852251881Speter /* Post-unlock hook. */ 853251881Speter { 854251881Speter this_path = apr_psprintf(pool, "%s%s", 855251881Speter svn_repos_post_unlock_hook(repos, pool), 856251881Speter SVN_REPOS__HOOK_DESC_EXT); 857251881Speter 858251881Speter#define SCRIPT_NAME SVN_REPOS__HOOK_POST_UNLOCK 859251881Speter 860251881Speter contents = 861251881Speter"#!/bin/sh" NL 862251881Speter"" NL 863251881Speter"# POST-UNLOCK HOOK" NL 864251881Speter"#" NL 865251881Speter"# The post-unlock hook runs after a path is unlocked. Subversion runs" NL 866251881Speter"# this hook by invoking a program (script, executable, binary, etc.)" NL 867251881Speter"# named '"SCRIPT_NAME"' (for which this file is a template) with the " NL 868251881Speter"# following ordered arguments:" NL 869251881Speter"#" NL 870251881Speter"# [1] REPOS-PATH (the path to this repository)" NL 871251881Speter"# [2] USER (the user who destroyed the lock)" NL 872251881Speter"#" NL 873251881Speter"# The paths that were just unlocked are passed to the hook via STDIN" NL 874251881Speter"# (as of Subversion 1.2, only one path is passed per invocation, but" NL 875251881Speter"# the plan is to pass all unlocked paths at once, so the hook program" NL 876251881Speter"# should be written accordingly)." NL 877251881Speter"#" NL 878251881Speter"# The default working directory for the invocation is undefined, so" NL 879251881Speter"# the program should set one explicitly if it cares." NL 880251881Speter"#" NL 881251881Speter"# Because the lock has already been destroyed and cannot be undone," NL 882251881Speter"# the exit code of the hook program is ignored." NL 883251881Speter"#" NL 884251881Speter"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 885251881Speter"# invoke other programs to do the real work, though it may do the" NL 886251881Speter"# work itself too." NL 887251881Speter"#" NL 888251881Speter"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 889251881Speter"# invoke it (typically the user httpd runs as), and that user must" NL 890251881Speter"# have filesystem-level permission to access the repository." NL 891251881Speter"#" NL 892251881Speter"# On a Windows system, you should name the hook program" NL 893251881Speter"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 894251881Speter"# but the basic idea is the same." NL 895251881Speter"# " NL 896251881Speter"# Here is an example hook script, for a Unix /bin/sh interpreter:" NL 897251881Speter"" NL 898251881Speter"REPOS=\"$1\"" NL 899251881Speter"USER=\"$2\"" NL 900251881Speter"" NL 901251881Speter"# Send email to interested parties, let them know a lock was removed:" NL 902251881Speter"mailer.py unlock \"$REPOS\" \"$USER\" /path/to/mailer.conf" NL; 903251881Speter 904251881Speter#undef SCRIPT_NAME 905251881Speter 906251881Speter SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 907251881Speter "Creating post-unlock hook"); 908251881Speter 909251881Speter SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 910251881Speter } /* end post-unlock hook */ 911251881Speter 912251881Speter 913251881Speter /* Post-revprop-change hook. */ 914251881Speter { 915251881Speter this_path = apr_psprintf(pool, "%s%s", 916251881Speter svn_repos_post_revprop_change_hook(repos, pool), 917251881Speter SVN_REPOS__HOOK_DESC_EXT); 918251881Speter 919251881Speter#define SCRIPT_NAME SVN_REPOS__HOOK_POST_REVPROP_CHANGE 920251881Speter 921251881Speter contents = 922251881Speter"#!/bin/sh" NL 923251881Speter"" NL 924251881Speter"# POST-REVPROP-CHANGE HOOK" NL 925251881Speter"#" NL 926251881Speter"# The post-revprop-change hook is invoked after a revision property" NL 927251881Speter"# has been added, modified or deleted. Subversion runs this hook by" NL 928251881Speter"# invoking a program (script, executable, binary, etc.) named" NL 929251881Speter"# '"SCRIPT_NAME"' (for which this file is a template), with the" NL 930251881Speter"# following ordered arguments:" NL 931251881Speter"#" NL 932251881Speter"# [1] REPOS-PATH (the path to this repository)" NL 933251881Speter"# [2] REV (the revision that was tweaked)" NL 934251881Speter"# [3] USER (the username of the person tweaking the property)" NL 935251881Speter"# [4] PROPNAME (the property that was changed)" NL 936251881Speter"# [5] ACTION (the property was 'A'dded, 'M'odified, or 'D'eleted)" NL 937251881Speter"#" NL 938251881Speter"# [STDIN] PROPVAL ** the old property value is passed via STDIN." NL 939251881Speter"#" NL 940251881Speter"# Because the propchange has already completed and cannot be undone," NL 941251881Speter"# the exit code of the hook program is ignored. The hook program" NL 942251881Speter"# can use the 'svnlook' utility to help it examine the" NL 943251881Speter"# new property value." NL 944251881Speter"#" NL 945251881Speter"# On a Unix system, the normal procedure is to have '"SCRIPT_NAME"'" NL 946251881Speter"# invoke other programs to do the real work, though it may do the" NL 947251881Speter"# work itself too." NL 948251881Speter"#" NL 949251881Speter"# Note that '"SCRIPT_NAME"' must be executable by the user(s) who will" NL 950251881Speter"# invoke it (typically the user httpd runs as), and that user must" NL 951251881Speter"# have filesystem-level permission to access the repository." NL 952251881Speter"#" NL 953251881Speter"# On a Windows system, you should name the hook program" NL 954251881Speter"# '"SCRIPT_NAME".bat' or '"SCRIPT_NAME".exe'," NL 955251881Speter"# but the basic idea is the same." NL 956251881Speter"# " NL 957251881SpeterHOOKS_ENVIRONMENT_TEXT 958251881Speter"# " NL 959251881Speter"# Here is an example hook script, for a Unix /bin/sh interpreter." NL 960251881SpeterPREWRITTEN_HOOKS_TEXT 961251881Speter"" NL 962251881Speter"" NL 963251881Speter"REPOS=\"$1\"" NL 964251881Speter"REV=\"$2\"" NL 965251881Speter"USER=\"$3\"" NL 966251881Speter"PROPNAME=\"$4\"" NL 967251881Speter"ACTION=\"$5\"" NL 968251881Speter"" NL 969251881Speter"mailer.py propchange2 \"$REPOS\" \"$REV\" \"$USER\" \"$PROPNAME\" " 970251881Speter"\"$ACTION\" /path/to/mailer.conf" NL; 971251881Speter 972251881Speter#undef SCRIPT_NAME 973251881Speter 974251881Speter SVN_ERR_W(svn_io_file_create(this_path, contents, pool), 975251881Speter _("Creating post-revprop-change hook")); 976251881Speter 977251881Speter SVN_ERR(svn_io_set_file_executable(this_path, TRUE, FALSE, pool)); 978251881Speter } /* end post-revprop-change hook */ 979251881Speter 980251881Speter return SVN_NO_ERROR; 981251881Speter} 982251881Speter 983251881Speterstatic svn_error_t * 984251881Spetercreate_conf(svn_repos_t *repos, apr_pool_t *pool) 985251881Speter{ 986251881Speter SVN_ERR_W(create_repos_dir(repos->conf_path, pool), 987251881Speter _("Creating conf directory")); 988251881Speter 989251881Speter /* Write a default template for svnserve.conf. */ 990251881Speter { 991251881Speter static const char * const svnserve_conf_contents = 992251881Speter"### This file controls the configuration of the svnserve daemon, if you" NL 993251881Speter"### use it to allow access to this repository. (If you only allow" NL 994251881Speter"### access through http: and/or file: URLs, then this file is" NL 995251881Speter"### irrelevant.)" NL 996251881Speter"" NL 997251881Speter"### Visit http://subversion.apache.org/ for more information." NL 998251881Speter"" NL 999251881Speter"[general]" NL 1000251881Speter"### The anon-access and auth-access options control access to the" NL 1001251881Speter"### repository for unauthenticated (a.k.a. anonymous) users and" NL 1002251881Speter"### authenticated users, respectively." NL 1003251881Speter"### Valid values are \"write\", \"read\", and \"none\"." NL 1004251881Speter"### Setting the value to \"none\" prohibits both reading and writing;" NL 1005251881Speter"### \"read\" allows read-only access, and \"write\" allows complete " NL 1006251881Speter"### read/write access to the repository." NL 1007251881Speter"### The sample settings below are the defaults and specify that anonymous" NL 1008251881Speter"### users have read-only access to the repository, while authenticated" NL 1009251881Speter"### users have read and write access to the repository." NL 1010251881Speter"# anon-access = read" NL 1011251881Speter"# auth-access = write" NL 1012251881Speter"### The password-db option controls the location of the password" NL 1013251881Speter"### database file. Unless you specify a path starting with a /," NL 1014251881Speter"### the file's location is relative to the directory containing" NL 1015251881Speter"### this configuration file." NL 1016251881Speter"### If SASL is enabled (see below), this file will NOT be used." NL 1017251881Speter"### Uncomment the line below to use the default password file." NL 1018251881Speter"# password-db = passwd" NL 1019251881Speter"### The authz-db option controls the location of the authorization" NL 1020251881Speter"### rules for path-based access control. Unless you specify a path" NL 1021251881Speter"### starting with a /, the file's location is relative to the" NL 1022251881Speter"### directory containing this file. The specified path may be a" NL 1023251881Speter"### repository relative URL (^/) or an absolute file:// URL to a text" NL 1024251881Speter"### file in a Subversion repository. If you don't specify an authz-db," NL 1025251881Speter"### no path-based access control is done." NL 1026251881Speter"### Uncomment the line below to use the default authorization file." NL 1027251881Speter"# authz-db = " SVN_REPOS__CONF_AUTHZ NL 1028251881Speter"### The groups-db option controls the location of the groups file." NL 1029251881Speter"### Unless you specify a path starting with a /, the file's location is" NL 1030251881Speter"### relative to the directory containing this file. The specified path" NL 1031251881Speter"### may be a repository relative URL (^/) or an absolute file:// URL to a" NL 1032251881Speter"### text file in a Subversion repository." NL 1033251881Speter"# groups-db = " SVN_REPOS__CONF_GROUPS NL 1034251881Speter"### This option specifies the authentication realm of the repository." NL 1035251881Speter"### If two repositories have the same authentication realm, they should" NL 1036251881Speter"### have the same password database, and vice versa. The default realm" NL 1037251881Speter"### is repository's uuid." NL 1038251881Speter"# realm = My First Repository" NL 1039251881Speter"### The force-username-case option causes svnserve to case-normalize" NL 1040251881Speter"### usernames before comparing them against the authorization rules in the" NL 1041251881Speter"### authz-db file configured above. Valid values are \"upper\" (to upper-" NL 1042251881Speter"### case the usernames), \"lower\" (to lowercase the usernames), and" NL 1043251881Speter"### \"none\" (to compare usernames as-is without case conversion, which" NL 1044251881Speter"### is the default behavior)." NL 1045251881Speter"# force-username-case = none" NL 1046251881Speter"### The hooks-env options specifies a path to the hook script environment " NL 1047251881Speter"### configuration file. This option overrides the per-repository default" NL 1048251881Speter"### and can be used to configure the hook script environment for multiple " NL 1049251881Speter"### repositories in a single file, if an absolute path is specified." NL 1050251881Speter"### Unless you specify an absolute path, the file's location is relative" NL 1051251881Speter"### to the directory containing this file." NL 1052251881Speter"# hooks-env = " SVN_REPOS__CONF_HOOKS_ENV NL 1053251881Speter"" NL 1054251881Speter"[sasl]" NL 1055251881Speter"### This option specifies whether you want to use the Cyrus SASL" NL 1056251881Speter"### library for authentication. Default is false." NL 1057251881Speter"### This section will be ignored if svnserve is not built with Cyrus" NL 1058251881Speter"### SASL support; to check, run 'svnserve --version' and look for a line" NL 1059251881Speter"### reading 'Cyrus SASL authentication is available.'" NL 1060251881Speter"# use-sasl = true" NL 1061251881Speter"### These options specify the desired strength of the security layer" NL 1062251881Speter"### that you want SASL to provide. 0 means no encryption, 1 means" NL 1063251881Speter"### integrity-checking only, values larger than 1 are correlated" NL 1064251881Speter"### to the effective key length for encryption (e.g. 128 means 128-bit" NL 1065251881Speter"### encryption). The values below are the defaults." NL 1066251881Speter"# min-encryption = 0" NL 1067251881Speter"# max-encryption = 256" NL; 1068251881Speter 1069251881Speter SVN_ERR_W(svn_io_file_create(svn_repos_svnserve_conf(repos, pool), 1070251881Speter svnserve_conf_contents, pool), 1071251881Speter _("Creating svnserve.conf file")); 1072251881Speter } 1073251881Speter 1074251881Speter { 1075251881Speter static const char * const passwd_contents = 1076251881Speter"### This file is an example password file for svnserve." NL 1077251881Speter"### Its format is similar to that of svnserve.conf. As shown in the" NL 1078251881Speter"### example below it contains one section labelled [users]." NL 1079251881Speter"### The name and password for each user follow, one account per line." NL 1080251881Speter"" NL 1081251881Speter"[users]" NL 1082251881Speter"# harry = harryssecret" NL 1083251881Speter"# sally = sallyssecret" NL; 1084251881Speter 1085251881Speter SVN_ERR_W(svn_io_file_create(svn_dirent_join(repos->conf_path, 1086251881Speter SVN_REPOS__CONF_PASSWD, 1087251881Speter pool), 1088251881Speter passwd_contents, pool), 1089251881Speter _("Creating passwd file")); 1090251881Speter } 1091251881Speter 1092251881Speter { 1093251881Speter static const char * const authz_contents = 1094251881Speter"### This file is an example authorization file for svnserve." NL 1095251881Speter"### Its format is identical to that of mod_authz_svn authorization" NL 1096251881Speter"### files." NL 1097251881Speter"### As shown below each section defines authorizations for the path and" NL 1098251881Speter"### (optional) repository specified by the section name." NL 1099251881Speter"### The authorizations follow. An authorization line can refer to:" NL 1100251881Speter"### - a single user," NL 1101251881Speter"### - a group of users defined in a special [groups] section," NL 1102251881Speter"### - an alias defined in a special [aliases] section," NL 1103251881Speter"### - all authenticated users, using the '$authenticated' token," NL 1104251881Speter"### - only anonymous users, using the '$anonymous' token," NL 1105251881Speter"### - anyone, using the '*' wildcard." NL 1106251881Speter"###" NL 1107251881Speter"### A match can be inverted by prefixing the rule with '~'. Rules can" NL 1108251881Speter"### grant read ('r') access, read-write ('rw') access, or no access" NL 1109251881Speter"### ('')." NL 1110251881Speter"" NL 1111251881Speter"[aliases]" NL 1112251881Speter"# joe = /C=XZ/ST=Dessert/L=Snake City/O=Snake Oil, Ltd./OU=Research Institute/CN=Joe Average" NL 1113251881Speter"" NL 1114251881Speter"[groups]" NL 1115251881Speter"# harry_and_sally = harry,sally" NL 1116251881Speter"# harry_sally_and_joe = harry,sally,&joe" NL 1117251881Speter"" NL 1118251881Speter"# [/foo/bar]" NL 1119251881Speter"# harry = rw" NL 1120251881Speter"# &joe = r" NL 1121251881Speter"# * =" NL 1122251881Speter"" NL 1123251881Speter"# [repository:/baz/fuz]" NL 1124251881Speter"# @harry_and_sally = rw" NL 1125251881Speter"# * = r" NL; 1126251881Speter 1127251881Speter SVN_ERR_W(svn_io_file_create(svn_dirent_join(repos->conf_path, 1128251881Speter SVN_REPOS__CONF_AUTHZ, 1129251881Speter pool), 1130251881Speter authz_contents, pool), 1131251881Speter _("Creating authz file")); 1132251881Speter } 1133251881Speter 1134251881Speter { 1135251881Speter static const char * const hooks_env_contents = 1136251881Speter"### This file is an example hook script environment configuration file." NL 1137251881Speter"### Hook scripts run in an empty environment by default." NL 1138251881Speter"### As shown below each section defines environment variables for a" NL 1139251881Speter"### particular hook script. The [default] section defines environment" NL 1140251881Speter"### variables for all hook scripts, unless overridden by a hook-specific" NL 1141251881Speter"### section." NL 1142251881Speter"" NL 1143251881Speter"### This example configures a UTF-8 locale for all hook scripts, so that " NL 1144251881Speter"### special characters, such as umlauts, may be printed to stderr." NL 1145251881Speter"### If UTF-8 is used with a mod_dav_svn server, the SVNUseUTF8 option must" NL 1146251881Speter"### also be set to 'yes' in httpd.conf." NL 1147251881Speter"### With svnserve, the LANG environment variable of the svnserve process" NL 1148251881Speter"### must be set to the same value as given here." NL 1149251881Speter"[default]" NL 1150251881Speter"LANG = en_US.UTF-8" NL 1151251881Speter"" NL 1152251881Speter"### This sets the PATH environment variable for the pre-commit hook." NL 1153251881Speter"[pre-commit]" NL 1154251881Speter"PATH = /usr/local/bin:/usr/bin:/usr/sbin" NL; 1155251881Speter 1156251881Speter SVN_ERR_W(svn_io_file_create(svn_dirent_join(repos->conf_path, 1157251881Speter SVN_REPOS__CONF_HOOKS_ENV \ 1158251881Speter SVN_REPOS__HOOK_DESC_EXT, 1159251881Speter pool), 1160251881Speter hooks_env_contents, pool), 1161251881Speter _("Creating hooks-env file")); 1162251881Speter } 1163251881Speter 1164251881Speter return SVN_NO_ERROR; 1165251881Speter} 1166251881Speter 1167251881Spetersvn_error_t * 1168251881Spetersvn_repos_hooks_setenv(svn_repos_t *repos, 1169251881Speter const char *hooks_env_path, 1170251881Speter apr_pool_t *scratch_pool) 1171251881Speter{ 1172251881Speter if (hooks_env_path == NULL) 1173251881Speter repos->hooks_env_path = svn_dirent_join(repos->conf_path, 1174251881Speter SVN_REPOS__CONF_HOOKS_ENV, 1175251881Speter repos->pool); 1176251881Speter else if (!svn_dirent_is_absolute(hooks_env_path)) 1177251881Speter repos->hooks_env_path = svn_dirent_join(repos->conf_path, 1178251881Speter hooks_env_path, 1179251881Speter repos->pool); 1180251881Speter else 1181251881Speter repos->hooks_env_path = apr_pstrdup(repos->pool, hooks_env_path); 1182251881Speter 1183251881Speter return SVN_NO_ERROR; 1184251881Speter} 1185251881Speter 1186251881Speter/* Allocate and return a new svn_repos_t * object, initializing the 1187251881Speter directory pathname members based on PATH, and initializing the 1188251881Speter REPOSITORY_CAPABILITIES member. 1189251881Speter The members FS, FORMAT, and FS_TYPE are *not* initialized (they are null), 1190251881Speter and it is the caller's responsibility to fill them in if needed. */ 1191251881Speterstatic svn_repos_t * 1192251881Spetercreate_svn_repos_t(const char *path, apr_pool_t *pool) 1193251881Speter{ 1194251881Speter svn_repos_t *repos = apr_pcalloc(pool, sizeof(*repos)); 1195251881Speter 1196251881Speter repos->path = apr_pstrdup(pool, path); 1197251881Speter repos->db_path = svn_dirent_join(path, SVN_REPOS__DB_DIR, pool); 1198251881Speter repos->conf_path = svn_dirent_join(path, SVN_REPOS__CONF_DIR, pool); 1199251881Speter repos->hook_path = svn_dirent_join(path, SVN_REPOS__HOOK_DIR, pool); 1200251881Speter repos->lock_path = svn_dirent_join(path, SVN_REPOS__LOCK_DIR, pool); 1201251881Speter repos->hooks_env_path = NULL; 1202251881Speter repos->repository_capabilities = apr_hash_make(pool); 1203251881Speter repos->pool = pool; 1204251881Speter 1205251881Speter return repos; 1206251881Speter} 1207251881Speter 1208251881Speter 1209251881Speterstatic svn_error_t * 1210251881Spetercreate_repos_structure(svn_repos_t *repos, 1211251881Speter const char *path, 1212251881Speter apr_hash_t *fs_config, 1213251881Speter apr_pool_t *pool) 1214251881Speter{ 1215251881Speter /* Create the top-level repository directory. */ 1216251881Speter SVN_ERR_W(create_repos_dir(path, pool), 1217251881Speter _("Could not create top-level directory")); 1218251881Speter 1219251881Speter /* Create the DAV sandbox directory if pre-1.4 or pre-1.5-compatible. */ 1220251881Speter if (fs_config 1221251881Speter && (svn_hash_gets(fs_config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE) 1222251881Speter || svn_hash_gets(fs_config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE))) 1223251881Speter { 1224251881Speter const char *dav_path = svn_dirent_join(repos->path, 1225251881Speter SVN_REPOS__DAV_DIR, pool); 1226251881Speter SVN_ERR_W(create_repos_dir(dav_path, pool), 1227251881Speter _("Creating DAV sandbox dir")); 1228251881Speter } 1229251881Speter 1230251881Speter /* Create the lock directory. */ 1231251881Speter SVN_ERR(create_locks(repos, pool)); 1232251881Speter 1233251881Speter /* Create the hooks directory. */ 1234251881Speter SVN_ERR(create_hooks(repos, pool)); 1235251881Speter 1236251881Speter /* Create the conf directory. */ 1237251881Speter SVN_ERR(create_conf(repos, pool)); 1238251881Speter 1239251881Speter /* Write the top-level README file. */ 1240251881Speter { 1241251881Speter const char * const readme_header = 1242251881Speter "This is a Subversion repository; use the 'svnadmin' and 'svnlook' " NL 1243251881Speter "tools to examine it. Do not add, delete, or modify files here " NL 1244251881Speter "unless you know how to avoid corrupting the repository." NL 1245251881Speter "" NL; 1246251881Speter const char * const readme_bdb_insert = 1247251881Speter "The directory \"" SVN_REPOS__DB_DIR "\" contains a Berkeley DB environment." NL 1248251881Speter "you may need to tweak the values in \"" SVN_REPOS__DB_DIR "/DB_CONFIG\" to match the" NL 1249251881Speter "requirements of your site." NL 1250251881Speter "" NL; 1251251881Speter const char * const readme_footer = 1252251881Speter "Visit http://subversion.apache.org/ for more information." NL; 1253251881Speter apr_file_t *f; 1254251881Speter apr_size_t written; 1255251881Speter 1256251881Speter SVN_ERR(svn_io_file_open(&f, 1257251881Speter svn_dirent_join(path, SVN_REPOS__README, pool), 1258251881Speter (APR_WRITE | APR_CREATE | APR_EXCL), 1259251881Speter APR_OS_DEFAULT, pool)); 1260251881Speter 1261251881Speter SVN_ERR(svn_io_file_write_full(f, readme_header, strlen(readme_header), 1262251881Speter &written, pool)); 1263251881Speter if (strcmp(repos->fs_type, SVN_FS_TYPE_BDB) == 0) 1264251881Speter SVN_ERR(svn_io_file_write_full(f, readme_bdb_insert, 1265251881Speter strlen(readme_bdb_insert), 1266251881Speter &written, pool)); 1267251881Speter SVN_ERR(svn_io_file_write_full(f, readme_footer, strlen(readme_footer), 1268251881Speter &written, pool)); 1269251881Speter 1270251881Speter return svn_io_file_close(f, pool); 1271251881Speter } 1272251881Speter} 1273251881Speter 1274251881Speter 1275251881Speter/* There is, at present, nothing within the direct responsibility 1276251881Speter of libsvn_repos which requires locking. For historical compatibility 1277251881Speter reasons, the BDB libsvn_fs backend does not do its own locking, expecting 1278251881Speter libsvn_repos to do the locking for it. Here we take care of that 1279251881Speter backend-specific requirement. 1280251881Speter The kind of lock is controlled by EXCLUSIVE and NONBLOCKING. 1281251881Speter The lock is scoped to POOL. */ 1282251881Speterstatic svn_error_t * 1283251881Speterlock_repos(svn_repos_t *repos, 1284251881Speter svn_boolean_t exclusive, 1285251881Speter svn_boolean_t nonblocking, 1286251881Speter apr_pool_t *pool) 1287251881Speter{ 1288251881Speter if (strcmp(repos->fs_type, SVN_FS_TYPE_BDB) == 0) 1289251881Speter { 1290251881Speter svn_error_t *err; 1291251881Speter const char *lockfile_path = svn_repos_db_lockfile(repos, pool); 1292251881Speter 1293251881Speter err = svn_io_file_lock2(lockfile_path, exclusive, nonblocking, pool); 1294251881Speter if (err != NULL && APR_STATUS_IS_EAGAIN(err->apr_err)) 1295251881Speter return svn_error_trace(err); 1296251881Speter SVN_ERR_W(err, _("Error opening db lockfile")); 1297251881Speter } 1298251881Speter return SVN_NO_ERROR; 1299251881Speter} 1300251881Speter 1301251881Speter 1302251881Spetersvn_error_t * 1303251881Spetersvn_repos_create(svn_repos_t **repos_p, 1304251881Speter const char *path, 1305251881Speter const char *unused_1, 1306251881Speter const char *unused_2, 1307251881Speter apr_hash_t *config, 1308251881Speter apr_hash_t *fs_config, 1309251881Speter apr_pool_t *pool) 1310251881Speter{ 1311251881Speter svn_repos_t *repos; 1312251881Speter svn_error_t *err; 1313251881Speter const char *root_path; 1314251881Speter const char *local_abspath; 1315251881Speter 1316251881Speter /* Allocate a repository object, filling in the format we will create. */ 1317251881Speter repos = create_svn_repos_t(path, pool); 1318251881Speter repos->format = SVN_REPOS__FORMAT_NUMBER; 1319251881Speter 1320251881Speter /* Discover the type of the filesystem we are about to create. */ 1321251881Speter repos->fs_type = svn_hash__get_cstring(fs_config, SVN_FS_CONFIG_FS_TYPE, 1322251881Speter DEFAULT_FS_TYPE); 1323251881Speter if (svn_hash__get_bool(fs_config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE, FALSE)) 1324251881Speter repos->format = SVN_REPOS__FORMAT_NUMBER_LEGACY; 1325251881Speter 1326251881Speter /* Don't create a repository inside another repository. */ 1327251881Speter SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); 1328251881Speter root_path = svn_repos_find_root_path(local_abspath, pool); 1329251881Speter if (root_path != NULL) 1330251881Speter { 1331251881Speter if (strcmp(root_path, local_abspath) == 0) 1332251881Speter return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL, 1333251881Speter _("'%s' is an existing repository"), 1334251881Speter svn_dirent_local_style(root_path, pool)); 1335251881Speter else 1336251881Speter return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL, 1337251881Speter _("'%s' is a subdirectory of an existing " 1338251881Speter "repository " "rooted at '%s'"), 1339251881Speter svn_dirent_local_style(local_abspath, pool), 1340251881Speter svn_dirent_local_style(root_path, pool)); 1341251881Speter } 1342251881Speter 1343251881Speter /* Create the various files and subdirectories for the repository. */ 1344251881Speter SVN_ERR_W(create_repos_structure(repos, path, fs_config, pool), 1345251881Speter _("Repository creation failed")); 1346251881Speter 1347251881Speter /* Lock if needed. */ 1348251881Speter SVN_ERR(lock_repos(repos, FALSE, FALSE, pool)); 1349251881Speter 1350251881Speter /* Create an environment for the filesystem. */ 1351251881Speter if ((err = svn_fs_create(&repos->fs, repos->db_path, fs_config, pool))) 1352251881Speter { 1353251881Speter /* If there was an error making the filesytem, e.g. unknown/supported 1354251881Speter * filesystem type. Clean up after ourselves. Yes this is safe because 1355251881Speter * create_repos_structure will fail if the path existed before we started 1356251881Speter * so we can't accidentally remove a directory that previously existed. 1357251881Speter */ 1358251881Speter 1359251881Speter return svn_error_trace( 1360251881Speter svn_error_compose_create( 1361251881Speter err, 1362251881Speter svn_io_remove_dir2(path, FALSE, NULL, NULL, pool))); 1363251881Speter } 1364251881Speter 1365251881Speter /* This repository is ready. Stamp it with a format number. */ 1366251881Speter SVN_ERR(svn_io_write_version_file 1367251881Speter (svn_dirent_join(path, SVN_REPOS__FORMAT, pool), 1368251881Speter repos->format, pool)); 1369251881Speter 1370251881Speter *repos_p = repos; 1371251881Speter return SVN_NO_ERROR; 1372251881Speter} 1373251881Speter 1374251881Speter 1375251881Speter/* Check if @a path is the root of a repository by checking if the 1376251881Speter * path contains the expected files and directories. Return TRUE 1377251881Speter * on errors (which would be permission errors, probably) so that 1378251881Speter * we the user will see them after we try to open the repository 1379251881Speter * for real. */ 1380251881Speterstatic svn_boolean_t 1381251881Spetercheck_repos_path(const char *path, 1382251881Speter apr_pool_t *pool) 1383251881Speter{ 1384251881Speter svn_node_kind_t kind; 1385251881Speter svn_error_t *err; 1386251881Speter 1387251881Speter err = svn_io_check_path(svn_dirent_join(path, SVN_REPOS__FORMAT, pool), 1388251881Speter &kind, pool); 1389251881Speter if (err) 1390251881Speter { 1391251881Speter svn_error_clear(err); 1392251881Speter return TRUE; 1393251881Speter } 1394251881Speter if (kind != svn_node_file) 1395251881Speter return FALSE; 1396251881Speter 1397251881Speter /* Check the db/ subdir, but allow it to be a symlink (Subversion 1398251881Speter works just fine if it's a symlink). */ 1399251881Speter err = svn_io_check_resolved_path 1400251881Speter (svn_dirent_join(path, SVN_REPOS__DB_DIR, pool), &kind, pool); 1401251881Speter if (err) 1402251881Speter { 1403251881Speter svn_error_clear(err); 1404251881Speter return TRUE; 1405251881Speter } 1406251881Speter if (kind != svn_node_dir) 1407251881Speter return FALSE; 1408251881Speter 1409251881Speter return TRUE; 1410251881Speter} 1411251881Speter 1412251881Speter 1413251881Speter/* Verify that REPOS's format is suitable. 1414251881Speter Use POOL for temporary allocation. */ 1415251881Speterstatic svn_error_t * 1416251881Spetercheck_repos_format(svn_repos_t *repos, 1417251881Speter apr_pool_t *pool) 1418251881Speter{ 1419251881Speter int format; 1420251881Speter const char *format_path; 1421251881Speter 1422251881Speter format_path = svn_dirent_join(repos->path, SVN_REPOS__FORMAT, pool); 1423251881Speter SVN_ERR(svn_io_read_version_file(&format, format_path, pool)); 1424251881Speter 1425251881Speter if (format != SVN_REPOS__FORMAT_NUMBER && 1426251881Speter format != SVN_REPOS__FORMAT_NUMBER_LEGACY) 1427251881Speter { 1428251881Speter return svn_error_createf 1429251881Speter (SVN_ERR_REPOS_UNSUPPORTED_VERSION, NULL, 1430251881Speter _("Expected repository format '%d' or '%d'; found format '%d'"), 1431251881Speter SVN_REPOS__FORMAT_NUMBER_LEGACY, SVN_REPOS__FORMAT_NUMBER, 1432251881Speter format); 1433251881Speter } 1434251881Speter 1435251881Speter repos->format = format; 1436251881Speter 1437251881Speter return SVN_NO_ERROR; 1438251881Speter} 1439251881Speter 1440251881Speter 1441251881Speter/* Set *REPOS_P to a repository at PATH which has been opened. 1442251881Speter See lock_repos() above regarding EXCLUSIVE and NONBLOCKING. 1443251881Speter OPEN_FS indicates whether the Subversion filesystem should be opened, 1444251881Speter the handle being placed into repos->fs. 1445251881Speter Do all allocation in POOL. */ 1446251881Speterstatic svn_error_t * 1447251881Speterget_repos(svn_repos_t **repos_p, 1448251881Speter const char *path, 1449251881Speter svn_boolean_t exclusive, 1450251881Speter svn_boolean_t nonblocking, 1451251881Speter svn_boolean_t open_fs, 1452251881Speter apr_hash_t *fs_config, 1453251881Speter apr_pool_t *pool) 1454251881Speter{ 1455251881Speter svn_repos_t *repos; 1456251881Speter 1457251881Speter /* Allocate a repository object. */ 1458251881Speter repos = create_svn_repos_t(path, pool); 1459251881Speter 1460251881Speter /* Verify the validity of our repository format. */ 1461251881Speter SVN_ERR(check_repos_format(repos, pool)); 1462251881Speter 1463251881Speter /* Discover the FS type. */ 1464251881Speter SVN_ERR(svn_fs_type(&repos->fs_type, repos->db_path, pool)); 1465251881Speter 1466251881Speter /* Lock if needed. */ 1467251881Speter SVN_ERR(lock_repos(repos, exclusive, nonblocking, pool)); 1468251881Speter 1469251881Speter /* Open up the filesystem only after obtaining the lock. */ 1470251881Speter if (open_fs) 1471251881Speter SVN_ERR(svn_fs_open(&repos->fs, repos->db_path, fs_config, pool)); 1472251881Speter 1473251881Speter#ifdef SVN_DEBUG_CRASH_AT_REPOS_OPEN 1474251881Speter /* If $PATH/config/debug-abort exists, crash the server here. 1475251881Speter This debugging feature can be used to test client recovery 1476251881Speter when the server crashes. 1477251881Speter 1478251881Speter See: Issue #4274 */ 1479251881Speter { 1480251881Speter svn_node_kind_t kind; 1481251881Speter svn_error_t *err = svn_io_check_path( 1482251881Speter svn_dirent_join(repos->conf_path, "debug-abort", pool), 1483251881Speter &kind, pool); 1484251881Speter svn_error_clear(err); 1485251881Speter if (!err && kind == svn_node_file) 1486251881Speter SVN_ERR_MALFUNCTION_NO_RETURN(); 1487251881Speter } 1488251881Speter#endif /* SVN_DEBUG_CRASH_AT_REPOS_OPEN */ 1489251881Speter 1490251881Speter *repos_p = repos; 1491251881Speter return SVN_NO_ERROR; 1492251881Speter} 1493251881Speter 1494251881Speter 1495251881Speter 1496251881Speterconst char * 1497251881Spetersvn_repos_find_root_path(const char *path, 1498251881Speter apr_pool_t *pool) 1499251881Speter{ 1500251881Speter const char *candidate = path; 1501251881Speter const char *decoded; 1502251881Speter svn_error_t *err; 1503251881Speter 1504251881Speter while (1) 1505251881Speter { 1506251881Speter /* Try to decode the path, so we don't fail if it contains characters 1507251881Speter that aren't supported by the OS filesystem. The subversion fs 1508251881Speter isn't restricted by the OS filesystem character set. */ 1509251881Speter err = svn_path_cstring_from_utf8(&decoded, candidate, pool); 1510251881Speter if (!err && check_repos_path(candidate, pool)) 1511251881Speter break; 1512251881Speter svn_error_clear(err); 1513251881Speter 1514251881Speter if (svn_path_is_empty(candidate) || 1515251881Speter svn_dirent_is_root(candidate, strlen(candidate))) 1516251881Speter return NULL; 1517251881Speter 1518251881Speter candidate = svn_dirent_dirname(candidate, pool); 1519251881Speter } 1520251881Speter 1521251881Speter return candidate; 1522251881Speter} 1523251881Speter 1524251881Speter 1525251881Spetersvn_error_t * 1526251881Spetersvn_repos_open2(svn_repos_t **repos_p, 1527251881Speter const char *path, 1528251881Speter apr_hash_t *fs_config, 1529251881Speter apr_pool_t *pool) 1530251881Speter{ 1531251881Speter /* Fetch a repository object initialized with a shared read/write 1532251881Speter lock on the database. */ 1533251881Speter 1534251881Speter return get_repos(repos_p, path, FALSE, FALSE, TRUE, fs_config, pool); 1535251881Speter} 1536251881Speter 1537251881Speter 1538251881Spetersvn_error_t * 1539251881Spetersvn_repos_upgrade2(const char *path, 1540251881Speter svn_boolean_t nonblocking, 1541251881Speter svn_repos_notify_func_t notify_func, 1542251881Speter void *notify_baton, 1543251881Speter apr_pool_t *pool) 1544251881Speter{ 1545251881Speter svn_repos_t *repos; 1546251881Speter const char *format_path; 1547251881Speter int format; 1548251881Speter apr_pool_t *subpool = svn_pool_create(pool); 1549251881Speter 1550251881Speter /* Fetch a repository object; for the Berkeley DB backend, it is 1551251881Speter initialized with an EXCLUSIVE lock on the database. This will at 1552251881Speter least prevent others from trying to read or write to it while we 1553251881Speter run recovery. (Other backends should do their own locking; see 1554251881Speter lock_repos.) */ 1555251881Speter SVN_ERR(get_repos(&repos, path, TRUE, nonblocking, FALSE, NULL, subpool)); 1556251881Speter 1557251881Speter if (notify_func) 1558251881Speter { 1559251881Speter /* We notify *twice* here, because there are two different logistical 1560251881Speter actions occuring. */ 1561251881Speter svn_repos_notify_t *notify = svn_repos_notify_create( 1562251881Speter svn_repos_notify_mutex_acquired, subpool); 1563251881Speter notify_func(notify_baton, notify, subpool); 1564251881Speter 1565251881Speter notify->action = svn_repos_notify_upgrade_start; 1566251881Speter notify_func(notify_baton, notify, subpool); 1567251881Speter } 1568251881Speter 1569251881Speter /* Try to overwrite with its own contents. We do this only to 1570251881Speter verify that we can, because we don't want to actually bump the 1571251881Speter format of the repository until our underlying filesystem claims 1572251881Speter to have been upgraded correctly. */ 1573251881Speter format_path = svn_dirent_join(repos->path, SVN_REPOS__FORMAT, subpool); 1574251881Speter SVN_ERR(svn_io_read_version_file(&format, format_path, subpool)); 1575251881Speter SVN_ERR(svn_io_write_version_file(format_path, format, subpool)); 1576251881Speter 1577251881Speter /* Try to upgrade the filesystem. */ 1578251881Speter SVN_ERR(svn_fs_upgrade(repos->db_path, subpool)); 1579251881Speter 1580251881Speter /* Now overwrite our format file with the latest version. */ 1581251881Speter SVN_ERR(svn_io_write_version_file(format_path, SVN_REPOS__FORMAT_NUMBER, 1582251881Speter subpool)); 1583251881Speter 1584251881Speter /* Close shop and free the subpool, to release the exclusive lock. */ 1585251881Speter svn_pool_destroy(subpool); 1586251881Speter 1587251881Speter return SVN_NO_ERROR; 1588251881Speter} 1589251881Speter 1590251881Speter 1591251881Spetersvn_error_t * 1592251881Spetersvn_repos_delete(const char *path, 1593251881Speter apr_pool_t *pool) 1594251881Speter{ 1595251881Speter const char *db_path = svn_dirent_join(path, SVN_REPOS__DB_DIR, pool); 1596251881Speter 1597251881Speter /* Delete the filesystem environment... */ 1598251881Speter SVN_ERR(svn_fs_delete_fs(db_path, pool)); 1599251881Speter 1600251881Speter /* ...then blow away everything else. */ 1601251881Speter return svn_io_remove_dir2(path, FALSE, NULL, NULL, pool); 1602251881Speter} 1603251881Speter 1604251881Speter 1605251881Speter/* Repository supports the capability. */ 1606251881Speterstatic const char *capability_yes = "yes"; 1607251881Speter/* Repository does not support the capability. */ 1608251881Speterstatic const char *capability_no = "no"; 1609251881Speter 1610251881Spetersvn_error_t * 1611251881Spetersvn_repos_has_capability(svn_repos_t *repos, 1612251881Speter svn_boolean_t *has, 1613251881Speter const char *capability, 1614251881Speter apr_pool_t *pool) 1615251881Speter{ 1616251881Speter const char *val = svn_hash_gets(repos->repository_capabilities, capability); 1617251881Speter 1618251881Speter if (val == capability_yes) 1619251881Speter { 1620251881Speter *has = TRUE; 1621251881Speter } 1622251881Speter else if (val == capability_no) 1623251881Speter { 1624251881Speter *has = FALSE; 1625251881Speter } 1626251881Speter /* Else don't know, so investigate. */ 1627251881Speter else if (strcmp(capability, SVN_REPOS_CAPABILITY_MERGEINFO) == 0) 1628251881Speter { 1629251881Speter svn_error_t *err; 1630251881Speter svn_fs_root_t *root; 1631251881Speter svn_mergeinfo_catalog_t ignored; 1632251881Speter apr_array_header_t *paths = apr_array_make(pool, 1, 1633251881Speter sizeof(char *)); 1634251881Speter 1635251881Speter SVN_ERR(svn_fs_revision_root(&root, repos->fs, 0, pool)); 1636251881Speter APR_ARRAY_PUSH(paths, const char *) = ""; 1637251881Speter err = svn_fs_get_mergeinfo2(&ignored, root, paths, FALSE, FALSE, 1638251881Speter TRUE, pool, pool); 1639251881Speter 1640251881Speter if (err) 1641251881Speter { 1642251881Speter if (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE) 1643251881Speter { 1644251881Speter svn_error_clear(err); 1645251881Speter svn_hash_sets(repos->repository_capabilities, 1646251881Speter SVN_REPOS_CAPABILITY_MERGEINFO, capability_no); 1647251881Speter *has = FALSE; 1648251881Speter } 1649251881Speter else if (err->apr_err == SVN_ERR_FS_NOT_FOUND) 1650251881Speter { 1651251881Speter /* Mergeinfo requests use relative paths, and anyway we're 1652251881Speter in r0, so we're likely to get this error -- but it 1653251881Speter means the repository supports mergeinfo! */ 1654251881Speter svn_error_clear(err); 1655251881Speter svn_hash_sets(repos->repository_capabilities, 1656251881Speter SVN_REPOS_CAPABILITY_MERGEINFO, capability_yes); 1657251881Speter *has = TRUE; 1658251881Speter } 1659251881Speter else 1660251881Speter { 1661251881Speter return svn_error_trace(err); 1662251881Speter } 1663251881Speter } 1664251881Speter else 1665251881Speter { 1666251881Speter svn_hash_sets(repos->repository_capabilities, 1667251881Speter SVN_REPOS_CAPABILITY_MERGEINFO, capability_yes); 1668251881Speter *has = TRUE; 1669251881Speter } 1670251881Speter } 1671251881Speter else 1672251881Speter { 1673251881Speter return svn_error_createf(SVN_ERR_UNKNOWN_CAPABILITY, 0, 1674251881Speter _("unknown capability '%s'"), capability); 1675251881Speter } 1676251881Speter 1677251881Speter return SVN_NO_ERROR; 1678251881Speter} 1679251881Speter 1680251881Spetersvn_fs_t * 1681251881Spetersvn_repos_fs(svn_repos_t *repos) 1682251881Speter{ 1683251881Speter if (! repos) 1684251881Speter return NULL; 1685251881Speter return repos->fs; 1686251881Speter} 1687251881Speter 1688251881Speter 1689251881Speter/* For historical reasons, for the Berkeley DB backend, this code uses 1690251881Speter * repository locking, which is motivated by the need to support the 1691251881Speter * Berkeley DB error DB_RUN_RECOVERY. (FSFS takes care of locking 1692251881Speter * itself, inside its implementation of svn_fs_recover.) Here's how 1693251881Speter * it works: 1694251881Speter * 1695251881Speter * Every accessor of a repository's database takes out a shared lock 1696251881Speter * on the repository -- both readers and writers get shared locks, and 1697251881Speter * there can be an unlimited number of shared locks simultaneously. 1698251881Speter * 1699251881Speter * Sometimes, a db access returns the error DB_RUN_RECOVERY. When 1700251881Speter * this happens, we need to run svn_fs_recover() on the db 1701251881Speter * with no other accessors present. So we take out an exclusive lock 1702251881Speter * on the repository. From the moment we request the exclusive lock, 1703251881Speter * no more shared locks are granted, and when the last shared lock 1704251881Speter * disappears, the exclusive lock is granted. As soon as we get it, 1705251881Speter * we can run recovery. 1706251881Speter * 1707251881Speter * We assume that once any berkeley call returns DB_RUN_RECOVERY, they 1708251881Speter * all do, until recovery is run. 1709251881Speter */ 1710251881Speter 1711251881Spetersvn_error_t * 1712251881Spetersvn_repos_recover4(const char *path, 1713251881Speter svn_boolean_t nonblocking, 1714251881Speter svn_repos_notify_func_t notify_func, 1715251881Speter void *notify_baton, 1716251881Speter svn_cancel_func_t cancel_func, 1717251881Speter void * cancel_baton, 1718251881Speter apr_pool_t *pool) 1719251881Speter{ 1720251881Speter svn_repos_t *repos; 1721251881Speter apr_pool_t *subpool = svn_pool_create(pool); 1722251881Speter 1723251881Speter /* Fetch a repository object; for the Berkeley DB backend, it is 1724251881Speter initialized with an EXCLUSIVE lock on the database. This will at 1725251881Speter least prevent others from trying to read or write to it while we 1726251881Speter run recovery. (Other backends should do their own locking; see 1727251881Speter lock_repos.) */ 1728251881Speter SVN_ERR(get_repos(&repos, path, TRUE, nonblocking, 1729251881Speter FALSE, /* don't try to open the db yet. */ 1730251881Speter NULL, 1731251881Speter subpool)); 1732251881Speter 1733251881Speter if (notify_func) 1734251881Speter { 1735251881Speter /* We notify *twice* here, because there are two different logistical 1736251881Speter actions occuring. */ 1737251881Speter svn_repos_notify_t *notify = svn_repos_notify_create( 1738251881Speter svn_repos_notify_mutex_acquired, subpool); 1739251881Speter notify_func(notify_baton, notify, subpool); 1740251881Speter 1741251881Speter notify->action = svn_repos_notify_recover_start; 1742251881Speter notify_func(notify_baton, notify, subpool); 1743251881Speter } 1744251881Speter 1745251881Speter /* Recover the database to a consistent state. */ 1746251881Speter SVN_ERR(svn_fs_recover(repos->db_path, cancel_func, cancel_baton, subpool)); 1747251881Speter 1748251881Speter /* Close shop and free the subpool, to release the exclusive lock. */ 1749251881Speter svn_pool_destroy(subpool); 1750251881Speter 1751251881Speter return SVN_NO_ERROR; 1752251881Speter} 1753251881Speter 1754251881Speterstruct freeze_baton_t { 1755251881Speter apr_array_header_t *paths; 1756251881Speter int counter; 1757251881Speter svn_repos_freeze_func_t freeze_func; 1758251881Speter void *freeze_baton; 1759251881Speter}; 1760251881Speter 1761251881Speterstatic svn_error_t * 1762251881Spetermulti_freeze(void *baton, 1763251881Speter apr_pool_t *pool) 1764251881Speter{ 1765251881Speter struct freeze_baton_t *fb = baton; 1766251881Speter 1767251881Speter if (fb->counter == fb->paths->nelts) 1768251881Speter { 1769251881Speter SVN_ERR(fb->freeze_func(fb->freeze_baton, pool)); 1770251881Speter return SVN_NO_ERROR; 1771251881Speter } 1772251881Speter else 1773251881Speter { 1774251881Speter /* Using a subpool as the only way to unlock the repos lock used 1775251881Speter by BDB is to clear the pool used to take the lock. */ 1776251881Speter apr_pool_t *subpool = svn_pool_create(pool); 1777251881Speter const char *path = APR_ARRAY_IDX(fb->paths, fb->counter, const char *); 1778251881Speter svn_repos_t *repos; 1779251881Speter 1780251881Speter ++fb->counter; 1781251881Speter 1782251881Speter SVN_ERR(get_repos(&repos, path, 1783251881Speter TRUE /* exclusive (only applies to BDB) */, 1784251881Speter FALSE /* non-blocking */, 1785251881Speter FALSE /* open-fs */, 1786251881Speter NULL, subpool)); 1787251881Speter 1788251881Speter 1789251881Speter if (strcmp(repos->fs_type, SVN_FS_TYPE_BDB) == 0) 1790251881Speter { 1791251881Speter svn_error_t *err = multi_freeze(fb, subpool); 1792251881Speter 1793251881Speter svn_pool_destroy(subpool); 1794251881Speter 1795251881Speter return err; 1796251881Speter } 1797251881Speter else 1798251881Speter { 1799251881Speter SVN_ERR(svn_fs_open(&repos->fs, repos->db_path, NULL, subpool)); 1800251881Speter SVN_ERR(svn_fs_freeze(svn_repos_fs(repos), multi_freeze, fb, 1801251881Speter subpool)); 1802251881Speter } 1803251881Speter 1804251881Speter svn_pool_destroy(subpool); 1805251881Speter } 1806251881Speter 1807251881Speter return SVN_NO_ERROR; 1808251881Speter} 1809251881Speter 1810251881Speter/* For BDB we fall back on BDB's repos layer lock which means that the 1811251881Speter repository is unreadable while frozen. 1812251881Speter 1813251881Speter For FSFS we delegate to the FS layer which uses the FSFS write-lock 1814251881Speter and an SQLite reserved lock which means the repository is readable 1815251881Speter while frozen. */ 1816251881Spetersvn_error_t * 1817251881Spetersvn_repos_freeze(apr_array_header_t *paths, 1818251881Speter svn_repos_freeze_func_t freeze_func, 1819251881Speter void *freeze_baton, 1820251881Speter apr_pool_t *pool) 1821251881Speter{ 1822251881Speter struct freeze_baton_t fb; 1823251881Speter 1824251881Speter fb.paths = paths; 1825251881Speter fb.counter = 0; 1826251881Speter fb.freeze_func = freeze_func; 1827251881Speter fb.freeze_baton = freeze_baton; 1828251881Speter 1829251881Speter SVN_ERR(multi_freeze(&fb, pool)); 1830251881Speter 1831251881Speter return SVN_NO_ERROR; 1832251881Speter} 1833251881Speter 1834251881Spetersvn_error_t *svn_repos_db_logfiles(apr_array_header_t **logfiles, 1835251881Speter const char *path, 1836251881Speter svn_boolean_t only_unused, 1837251881Speter apr_pool_t *pool) 1838251881Speter{ 1839251881Speter svn_repos_t *repos; 1840251881Speter int i; 1841251881Speter 1842251881Speter SVN_ERR(get_repos(&repos, path, 1843251881Speter FALSE, FALSE, 1844251881Speter FALSE, /* Do not open fs. */ 1845251881Speter NULL, 1846251881Speter pool)); 1847251881Speter 1848251881Speter SVN_ERR(svn_fs_berkeley_logfiles(logfiles, 1849251881Speter svn_repos_db_env(repos, pool), 1850251881Speter only_unused, 1851251881Speter pool)); 1852251881Speter 1853251881Speter /* Loop, printing log files. */ 1854251881Speter for (i = 0; i < (*logfiles)->nelts; i++) 1855251881Speter { 1856251881Speter const char ** log_file = &(APR_ARRAY_IDX(*logfiles, i, const char *)); 1857251881Speter *log_file = svn_dirent_join(SVN_REPOS__DB_DIR, *log_file, pool); 1858251881Speter } 1859251881Speter 1860251881Speter return SVN_NO_ERROR; 1861251881Speter} 1862251881Speter 1863251881Speter/* Baton for hotcopy_structure(). */ 1864251881Speterstruct hotcopy_ctx_t { 1865251881Speter const char *dest; /* target location to construct */ 1866251881Speter size_t src_len; /* len of the source path*/ 1867251881Speter 1868251881Speter /* As in svn_repos_hotcopy2() */ 1869251881Speter svn_boolean_t incremental; 1870251881Speter svn_cancel_func_t cancel_func; 1871251881Speter void *cancel_baton; 1872251881Speter}; 1873251881Speter 1874251881Speter/* Copy the repository structure of PATH to BATON->DEST, with exception of 1875251881Speter * @c SVN_REPOS__DB_DIR, @c SVN_REPOS__LOCK_DIR and @c SVN_REPOS__FORMAT; 1876251881Speter * those directories and files are handled separately. 1877251881Speter * 1878251881Speter * BATON is a (struct hotcopy_ctx_t *). BATON->SRC_LEN is the length 1879251881Speter * of PATH. 1880251881Speter * 1881251881Speter * Implements svn_io_walk_func_t. 1882251881Speter */ 1883251881Speterstatic svn_error_t *hotcopy_structure(void *baton, 1884251881Speter const char *path, 1885251881Speter const apr_finfo_t *finfo, 1886251881Speter apr_pool_t *pool) 1887251881Speter{ 1888251881Speter const struct hotcopy_ctx_t *ctx = baton; 1889251881Speter const char *sub_path; 1890251881Speter const char *target; 1891251881Speter 1892251881Speter if (ctx->cancel_func) 1893251881Speter SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); 1894251881Speter 1895251881Speter if (strlen(path) == ctx->src_len) 1896251881Speter { 1897251881Speter sub_path = ""; 1898251881Speter } 1899251881Speter else 1900251881Speter { 1901251881Speter sub_path = &path[ctx->src_len+1]; 1902251881Speter 1903251881Speter /* Check if we are inside db directory and if so skip it */ 1904251881Speter if (svn_path_compare_paths 1905251881Speter (svn_dirent_get_longest_ancestor(SVN_REPOS__DB_DIR, sub_path, pool), 1906251881Speter SVN_REPOS__DB_DIR) == 0) 1907251881Speter return SVN_NO_ERROR; 1908251881Speter 1909251881Speter if (svn_path_compare_paths 1910251881Speter (svn_dirent_get_longest_ancestor(SVN_REPOS__LOCK_DIR, sub_path, 1911251881Speter pool), 1912251881Speter SVN_REPOS__LOCK_DIR) == 0) 1913251881Speter return SVN_NO_ERROR; 1914251881Speter 1915251881Speter if (svn_path_compare_paths 1916251881Speter (svn_dirent_get_longest_ancestor(SVN_REPOS__FORMAT, sub_path, pool), 1917251881Speter SVN_REPOS__FORMAT) == 0) 1918251881Speter return SVN_NO_ERROR; 1919251881Speter } 1920251881Speter 1921251881Speter target = svn_dirent_join(ctx->dest, sub_path, pool); 1922251881Speter 1923251881Speter if (finfo->filetype == APR_DIR) 1924251881Speter { 1925251881Speter svn_error_t *err; 1926251881Speter 1927251881Speter err = create_repos_dir(target, pool); 1928251881Speter if (ctx->incremental && err && err->apr_err == SVN_ERR_DIR_NOT_EMPTY) 1929251881Speter { 1930251881Speter svn_error_clear(err); 1931251881Speter err = SVN_NO_ERROR; 1932251881Speter } 1933251881Speter return svn_error_trace(err); 1934251881Speter } 1935251881Speter else if (finfo->filetype == APR_REG) 1936251881Speter return svn_io_copy_file(path, target, TRUE, pool); 1937251881Speter else if (finfo->filetype == APR_LNK) 1938251881Speter return svn_io_copy_link(path, target, pool); 1939251881Speter else 1940251881Speter return SVN_NO_ERROR; 1941251881Speter} 1942251881Speter 1943251881Speter 1944251881Speter/** Obtain a lock on db logs lock file. Create one if it does not exist. 1945251881Speter */ 1946251881Speterstatic svn_error_t * 1947251881Speterlock_db_logs_file(svn_repos_t *repos, 1948251881Speter svn_boolean_t exclusive, 1949251881Speter apr_pool_t *pool) 1950251881Speter{ 1951251881Speter const char * lock_file = svn_repos_db_logs_lockfile(repos, pool); 1952251881Speter 1953251881Speter /* Try to create a lock file, in case if it is missing. As in case of the 1954251881Speter repositories created before hotcopy functionality. */ 1955251881Speter svn_error_clear(create_db_logs_lock(repos, pool)); 1956251881Speter 1957251881Speter return svn_io_file_lock2(lock_file, exclusive, FALSE, pool); 1958251881Speter} 1959251881Speter 1960251881Speter 1961251881Speter/* Make a copy of a repository with hot backup of fs. */ 1962251881Spetersvn_error_t * 1963251881Spetersvn_repos_hotcopy2(const char *src_path, 1964251881Speter const char *dst_path, 1965251881Speter svn_boolean_t clean_logs, 1966251881Speter svn_boolean_t incremental, 1967251881Speter svn_cancel_func_t cancel_func, 1968251881Speter void *cancel_baton, 1969251881Speter apr_pool_t *pool) 1970251881Speter{ 1971251881Speter svn_repos_t *src_repos; 1972251881Speter svn_repos_t *dst_repos; 1973251881Speter struct hotcopy_ctx_t hotcopy_context; 1974251881Speter svn_error_t *err; 1975251881Speter const char *src_abspath; 1976251881Speter const char *dst_abspath; 1977251881Speter 1978251881Speter SVN_ERR(svn_dirent_get_absolute(&src_abspath, src_path, pool)); 1979251881Speter SVN_ERR(svn_dirent_get_absolute(&dst_abspath, dst_path, pool)); 1980251881Speter if (strcmp(src_abspath, dst_abspath) == 0) 1981251881Speter return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 1982251881Speter _("Hotcopy source and destination are equal")); 1983251881Speter 1984251881Speter /* Try to open original repository */ 1985251881Speter SVN_ERR(get_repos(&src_repos, src_abspath, 1986251881Speter FALSE, FALSE, 1987251881Speter FALSE, /* don't try to open the db yet. */ 1988251881Speter NULL, 1989251881Speter pool)); 1990251881Speter 1991251881Speter /* If we are going to clean logs, then get an exclusive lock on 1992251881Speter db-logs.lock, to ensure that no one else will work with logs. 1993251881Speter 1994251881Speter If we are just copying, then get a shared lock to ensure that 1995251881Speter no one else will clean logs while we copying them */ 1996251881Speter 1997251881Speter SVN_ERR(lock_db_logs_file(src_repos, clean_logs, pool)); 1998251881Speter 1999251881Speter /* Copy the repository to a new path, with exception of 2000251881Speter specially handled directories */ 2001251881Speter 2002251881Speter hotcopy_context.dest = dst_abspath; 2003251881Speter hotcopy_context.src_len = strlen(src_abspath); 2004251881Speter hotcopy_context.incremental = incremental; 2005251881Speter hotcopy_context.cancel_func = cancel_func; 2006251881Speter hotcopy_context.cancel_baton = cancel_baton; 2007251881Speter SVN_ERR(svn_io_dir_walk2(src_abspath, 2008251881Speter 0, 2009251881Speter hotcopy_structure, 2010251881Speter &hotcopy_context, 2011251881Speter pool)); 2012251881Speter 2013251881Speter /* Prepare dst_repos object so that we may create locks, 2014251881Speter so that we may open repository */ 2015251881Speter 2016251881Speter dst_repos = create_svn_repos_t(dst_abspath, pool); 2017251881Speter dst_repos->fs_type = src_repos->fs_type; 2018251881Speter dst_repos->format = src_repos->format; 2019251881Speter 2020251881Speter err = create_locks(dst_repos, pool); 2021251881Speter if (err) 2022251881Speter { 2023251881Speter if (incremental && err->apr_err == SVN_ERR_DIR_NOT_EMPTY) 2024251881Speter svn_error_clear(err); 2025251881Speter else 2026251881Speter return svn_error_trace(err); 2027251881Speter } 2028251881Speter 2029251881Speter err = svn_io_dir_make_sgid(dst_repos->db_path, APR_OS_DEFAULT, pool); 2030251881Speter if (err) 2031251881Speter { 2032251881Speter if (incremental && APR_STATUS_IS_EEXIST(err->apr_err)) 2033251881Speter svn_error_clear(err); 2034251881Speter else 2035251881Speter return svn_error_trace(err); 2036251881Speter } 2037251881Speter 2038251881Speter /* Exclusively lock the new repository. 2039251881Speter No one should be accessing it at the moment */ 2040251881Speter SVN_ERR(lock_repos(dst_repos, TRUE, FALSE, pool)); 2041251881Speter 2042251881Speter SVN_ERR(svn_fs_hotcopy2(src_repos->db_path, dst_repos->db_path, 2043251881Speter clean_logs, incremental, 2044251881Speter cancel_func, cancel_baton, pool)); 2045251881Speter 2046251881Speter /* Destination repository is ready. Stamp it with a format number. */ 2047251881Speter return svn_io_write_version_file 2048251881Speter (svn_dirent_join(dst_repos->path, SVN_REPOS__FORMAT, pool), 2049251881Speter dst_repos->format, pool); 2050251881Speter} 2051251881Speter 2052251881Spetersvn_error_t * 2053251881Spetersvn_repos_hotcopy(const char *src_path, 2054251881Speter const char *dst_path, 2055251881Speter svn_boolean_t clean_logs, 2056251881Speter apr_pool_t *pool) 2057251881Speter{ 2058251881Speter return svn_error_trace(svn_repos_hotcopy2(src_path, dst_path, clean_logs, 2059251881Speter FALSE, NULL, NULL, pool)); 2060251881Speter} 2061251881Speter 2062251881Speter/* Return the library version number. */ 2063251881Speterconst svn_version_t * 2064251881Spetersvn_repos_version(void) 2065251881Speter{ 2066251881Speter SVN_VERSION_BODY; 2067251881Speter} 2068251881Speter 2069251881Speter 2070251881Speter 2071251881Spetersvn_error_t * 2072251881Spetersvn_repos_stat(svn_dirent_t **dirent, 2073251881Speter svn_fs_root_t *root, 2074251881Speter const char *path, 2075251881Speter apr_pool_t *pool) 2076251881Speter{ 2077251881Speter svn_node_kind_t kind; 2078251881Speter svn_dirent_t *ent; 2079251881Speter const char *datestring; 2080251881Speter apr_hash_t *prophash; 2081251881Speter 2082251881Speter SVN_ERR(svn_fs_check_path(&kind, root, path, pool)); 2083251881Speter 2084251881Speter if (kind == svn_node_none) 2085251881Speter { 2086251881Speter *dirent = NULL; 2087251881Speter return SVN_NO_ERROR; 2088251881Speter } 2089251881Speter 2090251881Speter ent = svn_dirent_create(pool); 2091251881Speter ent->kind = kind; 2092251881Speter 2093251881Speter if (kind == svn_node_file) 2094251881Speter SVN_ERR(svn_fs_file_length(&(ent->size), root, path, pool)); 2095251881Speter 2096251881Speter SVN_ERR(svn_fs_node_proplist(&prophash, root, path, pool)); 2097251881Speter if (apr_hash_count(prophash) > 0) 2098251881Speter ent->has_props = TRUE; 2099251881Speter 2100251881Speter SVN_ERR(svn_repos_get_committed_info(&(ent->created_rev), 2101251881Speter &datestring, 2102251881Speter &(ent->last_author), 2103251881Speter root, path, pool)); 2104251881Speter if (datestring) 2105251881Speter SVN_ERR(svn_time_from_cstring(&(ent->time), datestring, pool)); 2106251881Speter 2107251881Speter *dirent = ent; 2108251881Speter return SVN_NO_ERROR; 2109251881Speter} 2110251881Speter 2111251881Spetersvn_error_t * 2112251881Spetersvn_repos_remember_client_capabilities(svn_repos_t *repos, 2113251881Speter const apr_array_header_t *capabilities) 2114251881Speter{ 2115251881Speter repos->client_capabilities = capabilities; 2116251881Speter return SVN_NO_ERROR; 2117251881Speter} 2118251881Speter 2119251881Spetersvn_error_t * 2120251881Spetersvn_repos__fs_type(const char **fs_type, 2121251881Speter const char *repos_path, 2122251881Speter apr_pool_t *pool) 2123251881Speter{ 2124251881Speter svn_repos_t repos; 2125251881Speter repos.path = (char*)repos_path; 2126251881Speter 2127251881Speter SVN_ERR(check_repos_format(&repos, pool)); 2128251881Speter 2129251881Speter return svn_fs_type(fs_type, 2130251881Speter svn_dirent_join(repos_path, SVN_REPOS__DB_DIR, pool), 2131251881Speter pool); 2132251881Speter} 2133