Index: subversion/include/svn_error_codes.h =================================================================== --- subversion/include/svn_error_codes.h (revision 7614) +++ subversion/include/svn_error_codes.h (working copy) @@ -581,6 +581,10 @@ SVN_ERRDEF (SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED, SVN_ERR_RA_LOCAL_CATEGORY_START + 1, "Couldn't open a repository") + + SVN_ERRDEF (SVN_ERR_RA_LOCAL_REPOS_LOCK_FAILED, + SVN_ERR_RA_LOCAL_CATEGORY_START + 2, + "Couldn't lock a repository") /* ra_svn errors */ SVN_ERRDEF (SVN_ERR_RA_SVN_CMD_ERR, Index: subversion/libsvn_ra_local/ra_plugin.c =================================================================== --- subversion/libsvn_ra_local/ra_plugin.c (revision 7614) +++ subversion/libsvn_ra_local/ra_plugin.c (working copy) @@ -131,6 +131,173 @@ /** The RA plugin routines **/ +/* 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; +} + +/* Locking mechanism if requested */ +static svn_error_t * +svn_ra_local__lock (svn_ra_local__session_baton_t * session) +{ + /* Return an error code on failure */ + svn_error_t *err = SVN_NO_ERROR; + const static apr_time_t zero_time = 0; + 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; + + const char * repos_root = session->fs_path; + const apr_pool_t *pool = session->pool; + + if (zero_time == lock_time) + lock_time = apr_time_now(); + + lock_file_name = (char *)apr_palloc (pool, strlen(repos_root) + 9); + sprintf (lock_file_name, "%s/svn.lck", repos_root); + + apr_uid_current (&userid, &groupid, pool); + apr_uid_name_get (&username, userid, pool); + + /* Generate what should be in the lock file */ + lock_file_contents = (char *)apr_palloc (pool, strlen(username) + 64); + sprintf (lock_file_contents, "%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_RA_LOCAL_REPOS_LOCK_FAILED, err, + "Repository at '%s' is currently locked by another user", session->repository_URL); + } + else + { + if (!create_lock_file (lock_file_name, lock_file_contents, pool)) + return svn_error_createf + (SVN_ERR_RA_LOCAL_REPOS_LOCK_FAILED, err, + "Unable to create lock file for repository '%s'", session->repository_URL); + + /* 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_RA_LOCAL_REPOS_LOCK_FAILED, err, + "Race condition at repository '%s'! Aborting", session->repository_URL); + } + + return SVN_NO_ERROR; +} + +/* Determine if this is a 'lock' URL, in which case we */ +/* xlate the lock:// to a file:// and return request */ +static svn_boolean_t +svn_ra_local__is_lock (svn_ra_local__session_baton_t * session, + const char * repos_URL) +{ + svn_boolean_t lock; + + lock = strncmp (repos_URL, "lock://", 7) == 0; + + if (lock) + strncpy ((char*) repos_URL, "file", 4); + + return lock; +} static svn_error_t * svn_ra_local__open (void **session_baton, @@ -147,6 +314,9 @@ session = apr_pcalloc (pool, sizeof(*session)); session->pool = pool; session->repository_URL = repos_URL; + + /* Save lock request */ + session->lock = svn_ra_local__is_lock (session,repos_URL); /* Look through the URL, figure out which part points to the repository, and which part is the path *within* the @@ -197,6 +367,11 @@ session->username = apr_pstrdup (pool, username_creds->username); } + /* Attempt the lock if requested, error out on failure */ + /* Attempt lock after authorization passed */ + if (session->lock) + SVN_ERR (svn_ra_local__lock(session)); + *session_baton = session; return SVN_NO_ERROR; } @@ -856,6 +1031,9 @@ { apr_hash_set (hash, "file", APR_HASH_KEY_STRING, &ra_local_plugin); + /* lock protocol also triggers file protocol w/ additional feature */ + apr_hash_set (hash, "lock", APR_HASH_KEY_STRING, &ra_local_plugin); + /* ben sez: todo: check that abi_version >=1. */ return SVN_NO_ERROR; Index: subversion/libsvn_ra_local/ra_local.h =================================================================== --- subversion/libsvn_ra_local/ra_local.h (revision 7614) +++ subversion/libsvn_ra_local/ra_local.h (working copy) @@ -67,6 +67,7 @@ /* Callback stuff. */ const svn_ra_callbacks_t *callbacks; void *callback_baton; + svn_boolean_t lock; } svn_ra_local__session_baton_t;