Alien-SVN
view release on metacpan or search on metacpan
src/subversion/subversion/libsvn_fs_fs/lock.c view on Meta::CPAN
const char *digest_path,
walk_digests_callback_t walk_digests_func,
void *walk_digests_baton,
svn_boolean_t have_write_lock,
apr_pool_t *pool)
{
apr_hash_index_t *hi;
apr_hash_t *children;
apr_pool_t *subpool;
svn_lock_t *lock;
/* First, send up any locks in the current digest file. */
SVN_ERR(read_digest_file(&children, &lock, fs_path, digest_path, pool));
SVN_ERR(walk_digests_func(walk_digests_baton, fs_path, digest_path,
children, lock,
have_write_lock, pool));
/* Now, recurse on this thing's child entries (if any; bail otherwise). */
if (! apr_hash_count(children))
return SVN_NO_ERROR;
subpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, children); hi; hi = apr_hash_next(hi))
{
const char *digest = svn__apr_hash_index_key(hi);
svn_pool_clear(subpool);
SVN_ERR(walk_digest_files
(fs_path, digest_path_from_digest(fs_path, digest, subpool),
walk_digests_func, walk_digests_baton, have_write_lock, subpool));
}
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
/* A recursive function that calls GET_LOCKS_FUNC/GET_LOCKS_BATON for
all locks in and under PATH in FS.
HAVE_WRITE_LOCK should be true if the caller (directly or indirectly)
has the FS write lock. */
static svn_error_t *
walk_locks(svn_fs_t *fs,
const char *digest_path,
svn_fs_get_locks_callback_t get_locks_func,
void *get_locks_baton,
svn_boolean_t have_write_lock,
apr_pool_t *pool)
{
struct walk_locks_baton wlb;
wlb.get_locks_func = get_locks_func;
wlb.get_locks_baton = get_locks_baton;
wlb.fs = fs;
SVN_ERR(walk_digest_files(fs->path, digest_path, locks_walker, &wlb,
have_write_lock, pool));
return SVN_NO_ERROR;
}
/* Utility function: verify that a lock can be used. Interesting
errors returned from this function:
SVN_ERR_FS_NO_USER: No username attached to FS.
SVN_ERR_FS_LOCK_OWNER_MISMATCH: FS's username doesn't match LOCK's owner.
SVN_ERR_FS_BAD_LOCK_TOKEN: FS doesn't hold matching lock-token for LOCK.
*/
static svn_error_t *
verify_lock(svn_fs_t *fs,
svn_lock_t *lock,
apr_pool_t *pool)
{
if ((! fs->access_ctx) || (! fs->access_ctx->username))
return svn_error_createf
(SVN_ERR_FS_NO_USER, NULL,
_("Cannot verify lock on path '%s'; no username available"),
lock->path);
else if (strcmp(fs->access_ctx->username, lock->owner) != 0)
return svn_error_createf
(SVN_ERR_FS_LOCK_OWNER_MISMATCH, NULL,
_("User '%s' does not own lock on path '%s' (currently locked by '%s')"),
fs->access_ctx->username, lock->path, lock->owner);
else if (svn_hash_gets(fs->access_ctx->lock_tokens, lock->token) == NULL)
return svn_error_createf
(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
_("Cannot verify lock on path '%s'; no matching lock-token available"),
lock->path);
return SVN_NO_ERROR;
}
/* This implements the svn_fs_get_locks_callback_t interface, where
BATON is just an svn_fs_t object. */
static svn_error_t *
get_locks_callback(void *baton,
svn_lock_t *lock,
apr_pool_t *pool)
{
return verify_lock(baton, lock, pool);
}
/* The main routine for lock enforcement, used throughout libsvn_fs_fs. */
svn_error_t *
svn_fs_fs__allow_locked_operation(const char *path,
svn_fs_t *fs,
svn_boolean_t recurse,
svn_boolean_t have_write_lock,
apr_pool_t *pool)
{
path = svn_fs__canonicalize_abspath(path, pool);
if (recurse)
{
/* Discover all locks at or below the path. */
const char *digest_path;
SVN_ERR(digest_path_from_path(&digest_path, fs->path, path, pool));
SVN_ERR(walk_locks(fs, digest_path, get_locks_callback,
fs, have_write_lock, pool));
}
else
{
/* Discover and verify any lock attached to the path. */
svn_lock_t *lock;
SVN_ERR(get_lock_helper(fs, &lock, path, have_write_lock, pool));
if (lock)
SVN_ERR(verify_lock(fs, lock, pool));
}
return SVN_NO_ERROR;
}
/* Baton used for lock_body below. */
struct lock_baton {
svn_lock_t **lock_p;
svn_fs_t *fs;
const char *path;
const char *token;
const char *comment;
svn_boolean_t is_dav_comment;
apr_time_t expiration_date;
svn_revnum_t current_rev;
svn_boolean_t steal_lock;
apr_pool_t *pool;
};
/* This implements the svn_fs_fs__with_write_lock() 'body' callback
type, and assumes that the write lock is held.
BATON is a 'struct lock_baton *'. */
static svn_error_t *
lock_body(void *baton, apr_pool_t *pool)
{
struct lock_baton *lb = baton;
svn_node_kind_t kind;
svn_lock_t *existing_lock;
svn_lock_t *lock;
svn_fs_root_t *root;
svn_revnum_t youngest;
const char *rev_0_path;
/* Until we implement directory locks someday, we only allow locks
on files or non-existent paths. */
/* Use fs->vtable->foo instead of svn_fs_foo to avoid circular
library dependencies, which are not portable. */
SVN_ERR(lb->fs->vtable->youngest_rev(&youngest, lb->fs, pool));
SVN_ERR(lb->fs->vtable->revision_root(&root, lb->fs, youngest, pool));
SVN_ERR(svn_fs_fs__check_path(&kind, root, lb->path, pool));
if (kind == svn_node_dir)
return SVN_FS__ERR_NOT_FILE(lb->fs, lb->path);
/* While our locking implementation easily supports the locking of
nonexistent paths, we deliberately choose not to allow such madness. */
if (kind == svn_node_none)
{
if (SVN_IS_VALID_REVNUM(lb->current_rev))
return svn_error_createf(
SVN_ERR_FS_OUT_OF_DATE, NULL,
_("Path '%s' doesn't exist in HEAD revision"),
lb->path);
else
return svn_error_createf(
SVN_ERR_FS_NOT_FOUND, NULL,
_("Path '%s' doesn't exist in HEAD revision"),
lb->path);
}
/* We need to have a username attached to the fs. */
if (!lb->fs->access_ctx || !lb->fs->access_ctx->username)
return SVN_FS__ERR_NO_USER(lb->fs);
/* Is the caller attempting to lock an out-of-date working file? */
if (SVN_IS_VALID_REVNUM(lb->current_rev))
{
svn_revnum_t created_rev;
SVN_ERR(svn_fs_fs__node_created_rev(&created_rev, root, lb->path,
pool));
/* SVN_INVALID_REVNUM means the path doesn't exist. So
apparently somebody is trying to lock something in their
working copy, but somebody else has deleted the thing
from HEAD. That counts as being 'out of date'. */
if (! SVN_IS_VALID_REVNUM(created_rev))
return svn_error_createf
(SVN_ERR_FS_OUT_OF_DATE, NULL,
_("Path '%s' doesn't exist in HEAD revision"), lb->path);
if (lb->current_rev < created_rev)
return svn_error_createf
(SVN_ERR_FS_OUT_OF_DATE, NULL,
_("Lock failed: newer version of '%s' exists"), lb->path);
}
/* If the caller provided a TOKEN, we *really* need to see
if a lock already exists with that token, and if so, verify that
the lock's path matches PATH. Otherwise we run the risk of
breaking the 1-to-1 mapping of lock tokens to locked paths. */
/* ### TODO: actually do this check. This is tough, because the
schema doesn't supply a lookup-by-token mechanism. */
/* Is the path already locked?
Note that this next function call will automatically ignore any
errors about {the path not existing as a key, the path's token
not existing as a key, the lock just having been expired}. And
that's totally fine. Any of these three errors are perfectly
acceptable to ignore; it means that the path is now free and
clear for locking, because the fsfs funcs just cleared out both
of the tables for us. */
SVN_ERR(get_lock_helper(lb->fs, &existing_lock, lb->path, TRUE, pool));
if (existing_lock)
{
if (! lb->steal_lock)
{
/* Sorry, the path is already locked. */
return SVN_FS__ERR_PATH_ALREADY_LOCKED(lb->fs, existing_lock);
}
else
{
/* STEAL_LOCK was passed, so fs_username is "stealing" the
lock from lock->owner. Destroy the existing lock. */
SVN_ERR(delete_lock(lb->fs, existing_lock, pool));
}
}
/* Create our new lock, and add it to the tables.
Ensure that the lock is created in the correct pool. */
lock = svn_lock_create(lb->pool);
if (lb->token)
lock->token = apr_pstrdup(lb->pool, lb->token);
else
SVN_ERR(svn_fs_fs__generate_lock_token(&(lock->token), lb->fs,
lb->pool));
lock->path = apr_pstrdup(lb->pool, lb->path);
lock->owner = apr_pstrdup(lb->pool, lb->fs->access_ctx->username);
lock->comment = apr_pstrdup(lb->pool, lb->comment);
lock->is_dav_comment = lb->is_dav_comment;
lock->creation_date = apr_time_now();
lock->expiration_date = lb->expiration_date;
SVN_ERR(svn_fs_fs__path_rev_absolute(&rev_0_path, lb->fs, 0, pool));
SVN_ERR(set_lock(lb->fs->path, lock, rev_0_path, pool));
*lb->lock_p = lock;
return SVN_NO_ERROR;
}
/* Baton used for unlock_body below. */
struct unlock_baton {
svn_fs_t *fs;
const char *path;
const char *token;
svn_boolean_t break_lock;
};
/* This implements the svn_fs_fs__with_write_lock() 'body' callback
type, and assumes that the write lock is held.
BATON is a 'struct unlock_baton *'. */
static svn_error_t *
unlock_body(void *baton, apr_pool_t *pool)
{
struct unlock_baton *ub = baton;
svn_lock_t *lock;
/* This could return SVN_ERR_FS_BAD_LOCK_TOKEN or SVN_ERR_FS_LOCK_EXPIRED. */
SVN_ERR(get_lock(&lock, ub->fs, ub->path, TRUE, TRUE, pool));
/* Unless breaking the lock, we do some checks. */
if (! ub->break_lock)
{
/* Sanity check: the incoming token should match lock->token. */
if (strcmp(ub->token, lock->token) != 0)
return SVN_FS__ERR_NO_SUCH_LOCK(ub->fs, lock->path);
/* There better be a username attached to the fs. */
if (! (ub->fs->access_ctx && ub->fs->access_ctx->username))
return SVN_FS__ERR_NO_USER(ub->fs);
/* And that username better be the same as the lock's owner. */
if (strcmp(ub->fs->access_ctx->username, lock->owner) != 0)
return SVN_FS__ERR_LOCK_OWNER_MISMATCH(
ub->fs, ub->fs->access_ctx->username, lock->owner);
}
/* Remove lock and lock token files. */
return delete_lock(ub->fs, lock, pool);
}
/*** Public API implementations ***/
svn_error_t *
svn_fs_fs__lock(svn_lock_t **lock_p,
svn_fs_t *fs,
const char *path,
const char *token,
const char *comment,
svn_boolean_t is_dav_comment,
apr_time_t expiration_date,
svn_revnum_t current_rev,
svn_boolean_t steal_lock,
apr_pool_t *pool)
{
struct lock_baton lb;
SVN_ERR(svn_fs__check_fs(fs, TRUE));
path = svn_fs__canonicalize_abspath(path, pool);
lb.lock_p = lock_p;
lb.fs = fs;
lb.path = path;
lb.token = token;
lb.comment = comment;
lb.is_dav_comment = is_dav_comment;
lb.expiration_date = expiration_date;
lb.current_rev = current_rev;
lb.steal_lock = steal_lock;
lb.pool = pool;
return svn_fs_fs__with_write_lock(fs, lock_body, &lb, pool);
}
svn_error_t *
svn_fs_fs__generate_lock_token(const char **token,
svn_fs_t *fs,
apr_pool_t *pool)
{
SVN_ERR(svn_fs__check_fs(fs, TRUE));
/* Notice that 'fs' is currently unused. But perhaps someday, we'll
want to use the fs UUID + some incremented number? For now, we
generate a URI that matches the DAV RFC. We could change this to
some other URI scheme someday, if we wish. */
*token = apr_pstrcat(pool, "opaquelocktoken:",
( run in 0.763 second using v1.01-cache-2.11-cpan-e1769b4cff6 )