locking_commands.c [plain text]
#include "svn_client.h"
#include "client.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_xml.h"
#include "svn_pools.h"
#include "svn_private_config.h"
#include "private/svn_client_private.h"
#include "private/svn_wc_private.h"
struct lock_baton
{
const char *base_path;
apr_hash_t *urls_to_paths;
svn_client_ctx_t *ctx;
apr_pool_t *pool;
};
static svn_error_t *
store_locks_callback(void *baton,
const char *rel_url,
svn_boolean_t do_lock,
const svn_lock_t *lock,
svn_error_t *ra_err, apr_pool_t *pool)
{
struct lock_baton *lb = baton;
svn_wc_notify_t *notify;
notify = svn_wc_create_notify(rel_url,
do_lock
? (ra_err
? svn_wc_notify_failed_lock
: svn_wc_notify_locked)
: (ra_err
? svn_wc_notify_failed_unlock
: svn_wc_notify_unlocked),
pool);
notify->lock = lock;
notify->err = ra_err;
if (lb->base_path)
{
char *path = apr_hash_get(lb->urls_to_paths, rel_url,
APR_HASH_KEY_STRING);
const char *local_abspath;
SVN_ERR(svn_dirent_get_absolute(&local_abspath,
svn_dirent_join(lb->base_path,
path, pool),
pool));
notify->path = local_abspath;
notify->path_prefix = lb->base_path;
if (do_lock)
{
if (!ra_err)
{
SVN_ERR(svn_wc_add_lock2(lb->ctx->wc_ctx, local_abspath, lock,
lb->pool));
notify->lock_state = svn_wc_notify_lock_state_locked;
}
else
notify->lock_state = svn_wc_notify_lock_state_unchanged;
}
else
{
if (!ra_err ||
(ra_err && (ra_err->apr_err != SVN_ERR_FS_LOCK_OWNER_MISMATCH)))
{
SVN_ERR(svn_wc_remove_lock2(lb->ctx->wc_ctx, local_abspath,
lb->pool));
notify->lock_state = svn_wc_notify_lock_state_unlocked;
}
else
notify->lock_state = svn_wc_notify_lock_state_unchanged;
}
}
else
notify->url = rel_url;
if (lb->ctx->notify_func2)
lb->ctx->notify_func2(lb->ctx->notify_baton2, notify, pool);
return SVN_NO_ERROR;
}
static svn_error_t *
condense_targets(const char **common_parent,
apr_array_header_t **target_relpaths,
const apr_array_header_t *targets,
svn_boolean_t targets_are_uris,
svn_boolean_t remove_redundancies,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
if (targets_are_uris)
{
SVN_ERR(svn_uri_condense_targets(common_parent, target_relpaths,
targets, remove_redundancies,
result_pool, scratch_pool));
}
else
{
SVN_ERR(svn_dirent_condense_targets(common_parent, target_relpaths,
targets, remove_redundancies,
result_pool, scratch_pool));
}
if (apr_is_empty_array(*target_relpaths))
{
const char *base_name;
if (targets_are_uris)
{
svn_uri_split(common_parent, &base_name,
*common_parent, result_pool);
}
else
{
svn_dirent_split(common_parent, &base_name,
*common_parent, result_pool);
}
APR_ARRAY_PUSH(*target_relpaths, const char *) = base_name;
}
return SVN_NO_ERROR;
}
static svn_error_t *
organize_lock_targets(const char **common_parent_url,
const char **base_dir,
apr_hash_t **rel_targets_p,
apr_hash_t **rel_fs_paths_p,
const apr_array_header_t *targets,
svn_boolean_t do_lock,
svn_boolean_t force,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *common_url = NULL;
const char *common_dirent = NULL;
apr_hash_t *rel_targets_ret = apr_hash_make(result_pool);
apr_hash_t *rel_fs_paths = NULL;
apr_array_header_t *rel_targets;
svn_boolean_t url_mode;
int i;
SVN_ERR_ASSERT(targets->nelts);
SVN_ERR(svn_client__assert_homogeneous_target_type(targets));
url_mode = svn_path_is_url(APR_ARRAY_IDX(targets, 0, const char *));
if (url_mode)
{
svn_revnum_t *invalid_revnum =
apr_palloc(result_pool, sizeof(*invalid_revnum));
*invalid_revnum = SVN_INVALID_REVNUM;
SVN_ERR(condense_targets(&common_url, &rel_targets, targets,
TRUE, TRUE, result_pool, scratch_pool));
if (! (common_url && *common_url))
return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("No common parent found, unable to operate "
"on disjoint arguments"));
for (i = 0; i < rel_targets->nelts; i++)
{
apr_hash_set(rel_targets_ret,
APR_ARRAY_IDX(rel_targets, i, const char *),
APR_HASH_KEY_STRING,
do_lock ? (const void *)invalid_revnum
: (const void *)"");
}
}
else
{
apr_array_header_t *rel_urls, *target_urls;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
SVN_ERR(condense_targets(&common_dirent, &rel_targets, targets,
FALSE, TRUE, result_pool, scratch_pool));
if (! (common_dirent && *common_dirent))
return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("No common parent found, unable to operate "
"on disjoint arguments"));
target_urls = apr_array_make(scratch_pool, rel_targets->nelts,
sizeof(const char *));
for (i = 0; i < rel_targets->nelts; i++)
{
const char *rel_target, *local_abspath, *target_url;
svn_pool_clear(iterpool);
rel_target = APR_ARRAY_IDX(rel_targets, i, const char *);
local_abspath = svn_dirent_join(common_dirent, rel_target, iterpool);
SVN_ERR(svn_wc__node_get_url(&target_url, ctx->wc_ctx, local_abspath,
scratch_pool, iterpool));
if (! target_url)
return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
_("'%s' has no URL"),
svn_dirent_local_style(local_abspath,
iterpool));
APR_ARRAY_PUSH(target_urls, const char *) = target_url;
}
SVN_ERR(condense_targets(&common_url, &rel_urls, target_urls,
TRUE, FALSE, result_pool, scratch_pool));
if (! (common_url && *common_url))
return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Unable to lock/unlock across multiple "
"repositories"));
rel_fs_paths = apr_hash_make(result_pool);
for (i = 0; i < rel_targets->nelts; i++)
{
const char *rel_target, *rel_url, *abs_path;
svn_pool_clear(iterpool);
rel_target = APR_ARRAY_IDX(rel_targets, i, const char *);
rel_url = APR_ARRAY_IDX(rel_urls, i, const char *);
apr_hash_set(rel_fs_paths, rel_url, APR_HASH_KEY_STRING,
apr_pstrdup(result_pool, rel_target));
abs_path = svn_dirent_join(common_dirent, rel_target, iterpool);
if (do_lock)
{
svn_revnum_t *revnum;
revnum = apr_palloc(result_pool, sizeof(* revnum));
SVN_ERR(svn_wc__node_get_base_rev(revnum, ctx->wc_ctx,
abs_path, result_pool));
apr_hash_set(rel_targets_ret, rel_url,
APR_HASH_KEY_STRING, revnum);
}
else
{
const char *lock_token = NULL;
if (! force)
{
SVN_ERR(svn_wc__node_get_lock_info(&lock_token, NULL, NULL,
NULL, ctx->wc_ctx,
abs_path, result_pool,
iterpool));
if (! lock_token)
return svn_error_createf(
SVN_ERR_CLIENT_MISSING_LOCK_TOKEN, NULL,
_("'%s' is not locked in this working copy"),
abs_path);
}
apr_hash_set(rel_targets_ret, rel_url, APR_HASH_KEY_STRING,
lock_token ? lock_token : "");
}
}
svn_pool_destroy(iterpool);
}
*common_parent_url = common_url;
*base_dir = common_dirent;
*rel_targets_p = rel_targets_ret;
*rel_fs_paths_p = rel_fs_paths;
return SVN_NO_ERROR;
}
static svn_error_t *
fetch_tokens(svn_ra_session_t *ra_session, apr_hash_t *path_tokens,
apr_pool_t *pool)
{
apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi))
{
const char *path = svn__apr_hash_index_key(hi);
svn_lock_t *lock;
svn_pool_clear(iterpool);
SVN_ERR(svn_ra_get_lock(ra_session, &lock, path, iterpool));
if (! lock)
return svn_error_createf
(SVN_ERR_CLIENT_MISSING_LOCK_TOKEN, NULL,
_("'%s' is not locked"), path);
apr_hash_set(path_tokens, path, APR_HASH_KEY_STRING,
apr_pstrdup(pool, lock->token));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_client_lock(const apr_array_header_t *targets,
const char *comment,
svn_boolean_t steal_lock,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *base_dir;
const char *base_dir_abspath = NULL;
const char *common_parent_url;
svn_ra_session_t *ra_session;
apr_hash_t *path_revs, *urls_to_paths;
struct lock_baton cb;
if (apr_is_empty_array(targets))
return SVN_NO_ERROR;
if (comment)
{
if (! svn_xml_is_xml_safe(comment, strlen(comment)))
return svn_error_create
(SVN_ERR_XML_UNESCAPABLE_DATA, NULL,
_("Lock comment contains illegal characters"));
}
SVN_ERR(organize_lock_targets(&common_parent_url, &base_dir, &path_revs,
&urls_to_paths, targets, TRUE, steal_lock,
ctx, pool, pool));
if (base_dir)
SVN_ERR(svn_dirent_get_absolute(&base_dir_abspath, base_dir, pool));
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL,
common_parent_url, base_dir,
NULL, FALSE, FALSE,
ctx, pool));
cb.base_path = base_dir;
cb.urls_to_paths = urls_to_paths;
cb.ctx = ctx;
cb.pool = pool;
SVN_ERR(svn_ra_lock(ra_session, path_revs, comment,
steal_lock, store_locks_callback, &cb, pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_client_unlock(const apr_array_header_t *targets,
svn_boolean_t break_lock,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
const char *base_dir;
const char *base_dir_abspath = NULL;
const char *common_parent_url;
svn_ra_session_t *ra_session;
apr_hash_t *path_tokens, *urls_to_paths;
struct lock_baton cb;
if (apr_is_empty_array(targets))
return SVN_NO_ERROR;
SVN_ERR(organize_lock_targets(&common_parent_url, &base_dir, &path_tokens,
&urls_to_paths, targets, FALSE, break_lock,
ctx, pool, pool));
if (base_dir)
SVN_ERR(svn_dirent_get_absolute(&base_dir_abspath, base_dir, pool));
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL,
common_parent_url,
base_dir_abspath, NULL, FALSE,
FALSE, ctx, pool));
if (! base_dir && !break_lock)
SVN_ERR(fetch_tokens(ra_session, path_tokens, pool));
cb.base_path = base_dir;
cb.urls_to_paths = urls_to_paths;
cb.ctx = ctx;
cb.pool = pool;
SVN_ERR(svn_ra_unlock(ra_session, path_tokens, break_lock,
store_locks_callback, &cb, pool));
return SVN_NO_ERROR;
}