Index: subversion/include/svn_error_codes.h =================================================================== --- subversion/include/svn_error_codes.h (revision 7627) +++ subversion/include/svn_error_codes.h (working copy) @@ -507,6 +507,10 @@ SVN_ERR_REPOS_CATEGORY_START + 7, "Error running post-commit hook") + SVN_ERRDEF (SVN_ERR_REPOS_LOCK_FAILED, + SVN_ERR_REPOS_CATEGORY_START + 8, + "Error trying to lock repository") + /* generic ra errors */ SVN_ERRDEF (SVN_ERR_RA_ILLEGAL_URL, Index: subversion/libsvn_repos/repos.c =================================================================== --- subversion/libsvn_repos/repos.c (revision 7627) +++ subversion/libsvn_repos/repos.c (working copy) @@ -1035,7 +1035,6 @@ } - const char * svn_repos_find_root_path (const char *path, apr_pool_t *pool) @@ -1055,11 +1054,173 @@ } +/* True on success, False on failure */ +static svn_boolean_t +lock_file_cleanup (void * arg) +{ + return apr_file_remove ((char *)arg, NULL) ? TRUE : FALSE; +} + +/* True on success, False on failure */ +static svn_boolean_t +check_if_lock_file_exists (const char *lock_file_name, + apr_pool_t *pool) +{ + apr_file_t *lock_file = NULL; + + apr_file_open (&lock_file, + lock_file_name, + APR_READ, + APR_OS_DEFAULT, + pool); + + if (NULL != lock_file) + { + apr_file_close(lock_file); + return TRUE; + } + + return FALSE; +} + +/* True on success, False on failure */ +static svn_boolean_t +check_if_we_own_lock_file (const char *lock_file_name, + const char *lock_file_contents, + apr_pool_t *pool) +{ + apr_size_t lock_file_contents_length = strlen (lock_file_contents); + + /* Try to create our lock file */ + apr_file_t *lock_file = NULL; + + apr_file_open (&lock_file, + lock_file_name, + APR_READ, + APR_OS_DEFAULT, + pool); + + if (NULL != lock_file) + { + /* See if we are the person who created the lock file */ + apr_finfo_t lock_file_info; + char *current_lockfile_contents; + apr_size_t lock_file_size; + + apr_file_info_get (&lock_file_info, APR_FINFO_SIZE, lock_file); + lock_file_size = lock_file_info.size; + + current_lockfile_contents = (char *)apr_pcalloc(pool, lock_file_size + 1); + apr_file_read (lock_file, current_lockfile_contents, &lock_file_size); + + apr_file_close (lock_file); + + if (strcmp (current_lockfile_contents, lock_file_contents) == 0) + return TRUE; + } + + return FALSE; +} + + +/* True on success, False on failure */ +static svn_boolean_t +create_lock_file (const char *lock_file_name, + const char *lock_file_contents, + apr_pool_t *pool) +{ + apr_size_t lock_file_contents_length = strlen (lock_file_contents); + apr_file_t *lock_file = NULL; + + apr_file_open (&lock_file, + lock_file_name, + APR_READ | APR_WRITE | APR_CREATE | APR_EXCL, + APR_OS_DEFAULT, + pool); + + if (NULL != lock_file) + { + apr_file_write (lock_file, lock_file_contents, &lock_file_contents_length); + apr_file_close (lock_file); + return TRUE; + } + + return FALSE; +} + + +static svn_error_t * +perform_conditional_external_locking (const char *path, + apr_pool_t *pool) +{ + svn_error_t * err = SVN_NO_ERROR; + svn_config_t *cfgp; + const char * config_lock_file_name = apr_psprintf (pool, "%s/conf/lock.conf", path); + const char *valuep; + + SVN_ERR (svn_config_read (&cfgp, config_lock_file_name, FALSE, pool)); + + svn_config_get (cfgp, &valuep, "section", "option", SVN_CONFIG_FALSE); + + if (!strcmp (valuep, SVN_CONFIG_TRUE)) + { + const static apr_time_t zero_time = 0; + static apr_time_t lock_time = 0; + + char *lock_file_name; + char *lock_file_contents; + apr_uid_t userid; + apr_gid_t groupid; + char *username = NULL; + + if (zero_time == lock_time) + lock_time = apr_time_now (); + + lock_file_name = apr_psprintf (pool, "%s/svn.lck", path); + + apr_uid_current (&userid, &groupid, pool); + apr_uid_name_get (&username, userid, pool); + + /* Generate what should be in the lock file */ + lock_file_contents = apr_psprintf (pool, "%s/%d", username, lock_time); + + if (check_if_lock_file_exists (lock_file_name, pool)) + { + if (!check_if_we_own_lock_file (lock_file_name, lock_file_contents, pool)) + return svn_error_createf + (SVN_ERR_REPOS_LOCK_FAILED, err, + "Repository at '%s' is currently locked by another user", path); + } + else + { + if (!create_lock_file (lock_file_name, lock_file_contents, pool)) + return svn_error_createf + (SVN_ERR_REPOS_LOCK_FAILED, err, + "Unable to create lock file for repository '%s'", path); + + /* Sleep for two seconds, and then see if the lock file has been corrupted */ + apr_sleep (2000000); + + if (check_if_we_own_lock_file (lock_file_name, lock_file_contents, pool)) + apr_pool_cleanup_register (pool, lock_file_name, lock_file_cleanup, NULL); + else + return svn_error_createf + (SVN_ERR_REPOS_LOCK_FAILED, err, + "Race condition at repository '%s'! Aborting", path); + } + } + + return SVN_NO_ERROR; +} + + svn_error_t * svn_repos_open (svn_repos_t **repos_p, const char *path, apr_pool_t *pool) { + SVN_ERR ( perform_conditional_external_locking(path, pool)); + /* Fetch a repository object initialized with a shared read/write lock on the database. */