📄 lock.c
字号:
/*
* lock.c: routines for locking working copy subdirectories.
*
* ====================================================================
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://subversion.tigris.org/license-1.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
*
* This software consists of voluntary contributions made by many
* individuals. For exact contribution history, see the revision
* history and logs, available at http://subversion.tigris.org/.
* ====================================================================
*/
#include <assert.h>
#include <apr_pools.h>
#include <apr_time.h>
#include "svn_pools.h"
#include "svn_path.h"
#include "wc.h"
#include "adm_files.h"
#include "questions.h"
#include "svn_private_config.h"
struct svn_wc_adm_access_t
{
/* PATH to directory which contains the administrative area */
const char *path;
enum svn_wc__adm_access_type {
/* SVN_WC__ADM_ACCESS_UNLOCKED indicates no lock is held allowing
read-only access */
svn_wc__adm_access_unlocked,
/* SVN_WC__ADM_ACCESS_WRITE_LOCK indicates that a write lock is held
allowing read-write access */
svn_wc__adm_access_write_lock,
/* SVN_WC__ADM_ACCESS_CLOSED indicates that the baton has been
closed. */
svn_wc__adm_access_closed
} type;
/* LOCK_EXISTS is set TRUE when the write lock exists */
svn_boolean_t lock_exists;
/* SET_OWNER is TRUE if SET is allocated from this access baton */
svn_boolean_t set_owner;
/* The working copy format version number for the directory */
int wc_format;
/* SET is a hash of svn_wc_adm_access_t* keyed on char* representing the
path to directories that are open. */
apr_hash_t *set;
/* ENTRIES is the cached entries for PATH, without those in state
deleted. ENTRIES_HIDDEN is the cached entries including those in
state deleted or state absent. Either may be NULL. */
apr_hash_t *entries;
apr_hash_t *entries_hidden;
/* POOL is used to allocate cached items, they need to persist for the
lifetime of this access baton */
apr_pool_t *pool;
};
/* This is a placeholder used in the set hash to represent missing
directories. Only its address is important, it contains no useful
data. */
static svn_wc_adm_access_t missing;
static svn_error_t *
do_close (svn_wc_adm_access_t *adm_access, svn_boolean_t preserve_lock);
/* Maybe upgrade the working copy directory represented by ADM_ACCESS
to the latest 'SVN_WC__VERSION'. ADM_ACCESS must contain a write
lock. Use POOL for all temporary allocation.
Not all upgrade paths are necessarily supported. For example,
upgrading a version 1 working copy results in an error.
Sometimes the format file can contain "0" while the administrative
directory is being constructed; calling this on a format 0 working
copy has no effect and returns no error. */
static svn_error_t *
maybe_upgrade_format (svn_wc_adm_access_t *adm_access, apr_pool_t *pool)
{
SVN_ERR (svn_wc__check_format (adm_access->wc_format,
adm_access->path,
pool));
/* We can upgrade all formats that are accepted by
svn_wc__check_format. */
if (adm_access->wc_format != SVN_WC__VERSION)
{
const char *path = svn_wc__adm_path (adm_access->path, FALSE, pool,
SVN_WC__ADM_FORMAT, NULL);
SVN_ERR (svn_io_write_version_file (path, SVN_WC__VERSION, pool));
adm_access->wc_format = SVN_WC__VERSION;
}
return SVN_NO_ERROR;
}
/* Create a physical lock file in the admin directory for ADM_ACCESS. Wait
up to WAIT_FOR seconds if the lock already exists retrying every
second.
Note: most callers of this function determine the wc_format for the
lock soon afterwards. We recommend calling maybe_upgrade_format()
as soon as you have the wc_format for a lock, since that's a good
opportunity to drag old working directories into the modern era. */
static svn_error_t *
create_lock (svn_wc_adm_access_t *adm_access, int wait_for, apr_pool_t *pool)
{
svn_error_t *err;
for (;;)
{
err = svn_wc__make_adm_thing (adm_access, SVN_WC__ADM_LOCK,
svn_node_file, APR_OS_DEFAULT, 0, pool);
if (err)
{
if (APR_STATUS_IS_EEXIST(err->apr_err))
{
svn_error_clear (err);
if (wait_for <= 0)
break;
wait_for--;
apr_sleep (apr_time_from_sec(1)); /* micro-seconds */
}
else
return err;
}
else
return SVN_NO_ERROR;
}
return svn_error_createf (SVN_ERR_WC_LOCKED, NULL,
_("Working copy '%s' locked"),
svn_path_local_style (adm_access->path, pool));
}
/* Remove the physical lock in the admin directory for PATH. It is
acceptable for the administrative area to have disappeared, such as when
the directory is removed from the working copy. It is an error for the
lock to have disappeared if the administrative area still exists. */
static svn_error_t *
remove_lock (const char *path, apr_pool_t *pool)
{
svn_error_t *err = svn_wc__remove_adm_file (path, pool, SVN_WC__ADM_LOCK,
NULL);
if (err)
{
if (svn_wc__adm_path_exists (path, FALSE, pool, NULL))
return err;
svn_error_clear (err);
}
return SVN_NO_ERROR;
}
/* An APR pool cleanup handler. This handles access batons that have not
been closed when their pool gets destroyed. The physical locks
associated with such batons remain in the working copy. */
static apr_status_t
pool_cleanup (void *p)
{
svn_wc_adm_access_t *lock = p;
svn_boolean_t cleanup;
svn_error_t *err;
err = svn_wc__adm_is_cleanup_required (&cleanup, lock, lock->pool);
if (!err)
err = do_close (lock, cleanup);
/* ### Is this the correct way to handle the error? */
if (err)
{
apr_status_t apr_err = err->apr_err;
svn_error_clear (err);
return apr_err;
}
else
return APR_SUCCESS;
}
/* An APR pool cleanup handler. This is a child handler, it removes the
main pool handler. */
static apr_status_t
pool_cleanup_child (void *p)
{
svn_wc_adm_access_t *lock = p;
apr_pool_cleanup_kill (lock->pool, lock, pool_cleanup);
return APR_SUCCESS;
}
/* Allocate from POOL, intialise and return an access baton. TYPE and PATH
are used to initialise the baton. */
static svn_wc_adm_access_t *
adm_access_alloc (enum svn_wc__adm_access_type type,
const char *path,
apr_pool_t *pool)
{
svn_wc_adm_access_t *lock = apr_palloc (pool, sizeof (*lock));
lock->type = type;
lock->entries = NULL;
lock->entries_hidden = NULL;
lock->wc_format = 0;
lock->set = NULL;
lock->lock_exists = FALSE;
lock->set_owner = FALSE;
lock->path = apr_pstrdup (pool, path);
lock->pool = pool;
return lock;
}
static void
adm_ensure_set (svn_wc_adm_access_t *adm_access)
{
if (! adm_access->set)
{
adm_access->set_owner = TRUE;
adm_access->set = apr_hash_make (adm_access->pool);
apr_hash_set (adm_access->set, adm_access->path, APR_HASH_KEY_STRING,
adm_access);
}
}
static svn_error_t *
probe (const char **dir,
const char *path,
int *wc_format,
apr_pool_t *pool)
{
svn_node_kind_t kind;
SVN_ERR (svn_io_check_path (path, &kind, pool));
if (kind == svn_node_dir)
SVN_ERR (svn_wc_check_wc (path, wc_format, pool));
else
*wc_format = 0;
/* a "version" of 0 means a non-wc directory */
if (kind != svn_node_dir || *wc_format == 0)
{
/* Passing a path ending in "." or ".." to svn_path_dirname() is
probably always a bad idea; certainly it is in this case.
Unfortunately, svn_path_dirname()'s current signature can't
return an error, so we have to insert the protection in this
caller, as making the larger API change would be very
destabilizing right now (just before 1.0). See issue #1617. */
const char *base_name = svn_path_basename (path, pool);
if ((strcmp (base_name, "..") == 0)
|| (strcmp (base_name, ".") == 0))
{
return svn_error_createf
(SVN_ERR_WC_BAD_PATH, NULL,
_("Path '%s' ends in '%s', "
"which is unsupported for this operation"),
svn_path_local_style (path, pool), base_name);
}
*dir = svn_path_dirname (path, pool);
}
else
*dir = path;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__adm_steal_write_lock (svn_wc_adm_access_t **adm_access,
svn_wc_adm_access_t *associated,
const char *path,
apr_pool_t *pool)
{
svn_error_t *err;
svn_wc_adm_access_t *lock = adm_access_alloc (svn_wc__adm_access_write_lock,
path, pool);
err = create_lock (lock, 0, pool);
if (err)
{
if (err->apr_err == SVN_ERR_WC_LOCKED)
svn_error_clear (err); /* Steal existing lock */
else
return err;
}
if (associated)
{
adm_ensure_set (associated);
lock->set = associated->set;
apr_hash_set (lock->set, lock->path, APR_HASH_KEY_STRING, lock);
}
/* We have a write lock. If the working copy has an old
format, this is the time to upgrade it. */
SVN_ERR (svn_wc_check_wc (path, &lock->wc_format, pool));
SVN_ERR (maybe_upgrade_format (lock, pool));
lock->lock_exists = TRUE;
*adm_access = lock;
return SVN_NO_ERROR;
}
/* This is essentially the guts of svn_wc_adm_open2, with the additional
* parameter UNDER_CONSTRUCTION that gets set TRUE only when locking the
* admin directory during initial creation.
*/
static svn_error_t *
do_open (svn_wc_adm_access_t **adm_access,
svn_wc_adm_access_t *associated,
const char *path,
svn_boolean_t write_lock,
int depth,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -