#define SVN_DEPRECATED
#include <apr_pools.h>
#include <apr_time.h>
#include "svn_pools.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_sorts.h"
#include "svn_hash.h"
#include "svn_types.h"
#include "wc.h"
#include "adm_files.h"
#include "lock.h"
#include "props.h"
#include "wc_db.h"
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
struct svn_wc_adm_access_t
{
const char *path;
const char *abspath;
svn_boolean_t closed;
svn_wc__db_t *db;
svn_boolean_t db_provided;
apr_hash_t *entries_all;
apr_pool_t *pool;
};
static const svn_wc_adm_access_t missing = { 0 };
#define IS_MISSING(lock) ((lock) == &missing)
#define svn_wc__db_is_closed(db) FALSE
svn_error_t *
svn_wc__internal_check_wc(int *wc_format,
svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t check_path,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
err = svn_wc__db_temp_get_format(wc_format, db, local_abspath, scratch_pool);
if (err)
{
svn_node_kind_t kind;
if (err->apr_err != SVN_ERR_WC_MISSING &&
err->apr_err != SVN_ERR_WC_UNSUPPORTED_FORMAT)
return svn_error_trace(err);
svn_error_clear(err);
*wc_format = 0;
SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
if (kind == svn_node_none)
{
return svn_error_createf(APR_ENOENT, NULL, _("'%s' does not exist"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
}
if (*wc_format >= SVN_WC__WC_NG_VERSION)
{
svn_wc__db_status_t db_status;
svn_wc__db_kind_t db_kind;
if (check_path)
{
svn_node_kind_t wc_kind;
SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
if (wc_kind != svn_node_dir)
{
*wc_format = 0;
return SVN_NO_ERROR;
}
}
err = svn_wc__db_read_info(&db_status, &db_kind, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool);
if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
{
svn_error_clear(err);
*wc_format = 0;
return SVN_NO_ERROR;
}
else
SVN_ERR(err);
if (db_kind != svn_wc__db_kind_dir)
{
*wc_format = 0;
return SVN_NO_ERROR;
}
switch (db_status)
{
case svn_wc__db_status_not_present:
case svn_wc__db_status_server_excluded:
case svn_wc__db_status_excluded:
*wc_format = 0;
return SVN_NO_ERROR;
default:
break;
}
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_check_wc2(int *wc_format,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
return svn_error_trace(
svn_wc__internal_check_wc(wc_format, wc_ctx->db, local_abspath, FALSE,
scratch_pool));
}
static svn_error_t *
add_to_shared(svn_wc_adm_access_t *lock, apr_pool_t *scratch_pool)
{
{
svn_wc_adm_access_t *prior = svn_wc__db_temp_get_access(lock->db,
lock->abspath,
scratch_pool);
if (IS_MISSING(prior))
SVN_ERR(svn_wc__db_temp_close_access(lock->db, lock->abspath,
prior, scratch_pool));
}
svn_wc__db_temp_set_access(lock->db, lock->abspath, lock,
scratch_pool);
return SVN_NO_ERROR;
}
static svn_wc_adm_access_t *
get_from_shared(const char *abspath,
svn_wc__db_t *db,
apr_pool_t *scratch_pool)
{
if (db == NULL)
return NULL;
return svn_wc__db_temp_get_access(db, abspath, scratch_pool);
}
static svn_error_t *
close_single(svn_wc_adm_access_t *adm_access,
svn_boolean_t preserve_lock,
apr_pool_t *scratch_pool)
{
svn_boolean_t locked;
if (adm_access->closed)
return SVN_NO_ERROR;
SVN_ERR(svn_wc__db_wclock_owns_lock(&locked, adm_access->db,
adm_access->abspath, TRUE,
scratch_pool));
if (locked)
{
if (!preserve_lock)
{
svn_error_t *err = svn_wc__db_wclock_release(adm_access->db,
adm_access->abspath,
scratch_pool);
if (err)
{
if (svn_wc__adm_area_exists(adm_access->abspath, scratch_pool))
return err;
svn_error_clear(err);
}
}
}
adm_access->closed = TRUE;
SVN_ERR(svn_wc__db_temp_close_access(adm_access->db, adm_access->abspath,
adm_access, scratch_pool));
if (!adm_access->db_provided)
{
apr_hash_t *opened = svn_wc__db_temp_get_all_access(adm_access->db,
scratch_pool);
if (apr_hash_count(opened) == 0)
{
SVN_ERR(svn_wc__db_close(adm_access->db));
adm_access->db = NULL;
}
}
return SVN_NO_ERROR;
}
static apr_status_t
pool_cleanup_locked(void *p)
{
svn_wc_adm_access_t *lock = p;
apr_uint64_t id;
svn_skel_t *work_item;
svn_error_t *err;
if (lock->closed)
return APR_SUCCESS;
if (svn_wc__db_is_closed(lock->db))
{
apr_pool_t *scratch_pool;
svn_wc__db_t *db;
lock->closed = TRUE;
if (!svn_wc__adm_area_exists(lock->abspath, lock->pool))
return APR_SUCCESS;
scratch_pool = svn_pool_create(lock->pool);
err = svn_wc__db_open(&db, NULL , TRUE, TRUE,
scratch_pool, scratch_pool);
if (!err)
{
err = svn_wc__db_wq_fetch_next(&id, &work_item, db, lock->abspath, 0,
scratch_pool, scratch_pool);
if (!err && work_item == NULL)
{
err = svn_wc__db_wclock_release(db, lock->abspath, scratch_pool);
}
}
svn_error_clear(err);
svn_pool_destroy(scratch_pool);
return APR_SUCCESS;
}
err = svn_wc__db_wq_fetch_next(&id, &work_item, lock->db, lock->abspath, 0,
lock->pool, lock->pool);
if (!err)
err = close_single(lock,
work_item != NULL ,
lock->pool);
if (err)
{
apr_status_t apr_err = err->apr_err;
svn_error_clear(err);
return apr_err;
}
return APR_SUCCESS;
}
static apr_status_t
pool_cleanup_readonly(void *data)
{
svn_wc_adm_access_t *lock = data;
svn_error_t *err;
if (lock->closed)
return APR_SUCCESS;
if (svn_wc__db_is_closed(lock->db))
return APR_SUCCESS;
err = close_single(lock, FALSE , lock->pool);
if (err)
{
apr_status_t result = err->apr_err;
svn_error_clear(err);
return result;
}
return APR_SUCCESS;
}
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_locked);
apr_pool_cleanup_kill(lock->pool, lock, pool_cleanup_readonly);
return APR_SUCCESS;
}
static svn_error_t *
adm_access_alloc(svn_wc_adm_access_t **adm_access,
const char *path,
svn_wc__db_t *db,
svn_boolean_t db_provided,
svn_boolean_t write_lock,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
svn_wc_adm_access_t *lock = apr_palloc(result_pool, sizeof(*lock));
lock->closed = FALSE;
lock->entries_all = NULL;
lock->db = db;
lock->db_provided = db_provided;
lock->path = apr_pstrdup(result_pool, path);
lock->pool = result_pool;
SVN_ERR(svn_dirent_get_absolute(&lock->abspath, path, result_pool));
*adm_access = lock;
if (write_lock)
{
svn_boolean_t owns_lock;
SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, path, FALSE,
scratch_pool));
if (!owns_lock
|| svn_wc__adm_retrieve_internal2(db, lock->abspath, scratch_pool))
{
SVN_ERR(svn_wc__db_wclock_obtain(db, lock->abspath, 0, FALSE,
scratch_pool));
}
}
err = add_to_shared(lock, scratch_pool);
if (err)
return svn_error_compose_create(
err,
svn_wc__db_wclock_release(db, lock->abspath, scratch_pool));
apr_pool_cleanup_register(lock->pool, lock,
write_lock
? pool_cleanup_locked
: pool_cleanup_readonly,
pool_cleanup_child);
return SVN_NO_ERROR;
}
static svn_error_t *
probe(svn_wc__db_t *db,
const char **dir,
const char *path,
apr_pool_t *pool)
{
svn_node_kind_t kind;
int wc_format = 0;
SVN_ERR(svn_io_check_path(path, &kind, pool));
if (kind == svn_node_dir)
{
const char *local_abspath;
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
SVN_ERR(svn_wc__internal_check_wc(&wc_format, db, local_abspath,
FALSE, pool));
}
if (kind != svn_node_dir || wc_format == 0)
{
const char *base_name = svn_dirent_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_dirent_local_style(path, pool), base_name);
}
*dir = svn_dirent_dirname(path, pool);
}
else
*dir = path;
return SVN_NO_ERROR;
}
static svn_error_t *
open_single(svn_wc_adm_access_t **adm_access,
const char *path,
svn_boolean_t write_lock,
svn_wc__db_t *db,
svn_boolean_t db_provided,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *local_abspath;
int wc_format = 0;
svn_error_t *err;
svn_wc_adm_access_t *lock;
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
err = svn_wc__internal_check_wc(&wc_format, db, local_abspath, FALSE,
scratch_pool);
if (wc_format == 0 || (err && APR_STATUS_IS_ENOENT(err->apr_err)))
{
return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, err,
_("'%s' is not a working copy"),
svn_dirent_local_style(path, scratch_pool));
}
SVN_ERR(err);
SVN_ERR_ASSERT(wc_format == SVN_WC__VERSION);
SVN_ERR(adm_access_alloc(&lock, path, db, db_provided, write_lock,
result_pool, scratch_pool));
*adm_access = lock;
return SVN_NO_ERROR;
}
static svn_error_t *
adm_available(svn_boolean_t *available,
svn_wc__db_kind_t *kind,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_status_t status;
if (kind)
*kind = svn_wc__db_kind_unknown;
SVN_ERR(svn_wc__db_read_info(&status, kind, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
db, local_abspath, scratch_pool, scratch_pool));
*available = !(status == svn_wc__db_status_server_excluded
|| status == svn_wc__db_status_excluded
|| status == svn_wc__db_status_not_present);
return SVN_NO_ERROR;
}
static svn_error_t *
do_open(svn_wc_adm_access_t **adm_access,
const char *path,
svn_wc__db_t *db,
svn_boolean_t db_provided,
apr_array_header_t *rollback,
svn_boolean_t write_lock,
int levels_to_lock,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc_adm_access_t *lock;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
SVN_ERR(open_single(&lock, path, write_lock, db, db_provided,
result_pool, iterpool));
APR_ARRAY_PUSH(rollback, svn_wc_adm_access_t *) = lock;
if (levels_to_lock != 0)
{
const apr_array_header_t *children;
const char *local_abspath = svn_wc__adm_access_abspath(lock);
int i;
if (levels_to_lock > 0)
levels_to_lock--;
SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
scratch_pool, iterpool));
for (i = 0; i < children->nelts; i++)
{
const char *node_abspath;
svn_wc__db_kind_t kind;
svn_boolean_t available;
const char *name = APR_ARRAY_IDX(children, i, const char *);
svn_pool_clear(iterpool);
if (cancel_func)
SVN_ERR(cancel_func(cancel_baton));
node_abspath = svn_dirent_join(local_abspath, name, iterpool);
SVN_ERR(adm_available(&available,
&kind,
db,
node_abspath,
scratch_pool));
if (kind != svn_wc__db_kind_dir)
continue;
if (available)
{
const char *node_path = svn_dirent_join(path, name, iterpool);
svn_wc_adm_access_t *node_access;
SVN_ERR(do_open(&node_access, node_path, db, db_provided,
rollback, write_lock, levels_to_lock,
cancel_func, cancel_baton,
lock->pool, iterpool));
}
}
}
svn_pool_destroy(iterpool);
*adm_access = lock;
return SVN_NO_ERROR;
}
static svn_error_t *
open_all(svn_wc_adm_access_t **adm_access,
const char *path,
svn_wc__db_t *db,
svn_boolean_t db_provided,
svn_boolean_t write_lock,
int levels_to_lock,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
apr_array_header_t *rollback;
svn_error_t *err;
rollback = apr_array_make(pool, 10, sizeof(svn_wc_adm_access_t *));
err = do_open(adm_access, path, db, db_provided, rollback,
write_lock, levels_to_lock,
cancel_func, cancel_baton, pool, pool);
if (err)
{
int i;
for (i = rollback->nelts; i--; )
{
svn_wc_adm_access_t *lock = APR_ARRAY_IDX(rollback, i,
svn_wc_adm_access_t *);
SVN_ERR_ASSERT(!IS_MISSING(lock));
svn_error_clear(close_single(lock, FALSE , pool));
}
}
return svn_error_trace(err);
}
svn_error_t *
svn_wc_adm_open3(svn_wc_adm_access_t **adm_access,
svn_wc_adm_access_t *associated,
const char *path,
svn_boolean_t write_lock,
int levels_to_lock,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
svn_wc__db_t *db;
svn_boolean_t db_provided;
if (associated)
{
const char *abspath;
svn_wc_adm_access_t *lock;
SVN_ERR(svn_dirent_get_absolute(&abspath, path, pool));
lock = get_from_shared(abspath, associated->db, pool);
if (lock && !IS_MISSING(lock))
return svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
_("Working copy '%s' locked"),
svn_dirent_local_style(path, pool));
db = associated->db;
db_provided = associated->db_provided;
}
else
{
SVN_ERR(svn_wc__db_open(&db, NULL , TRUE, TRUE,
pool, pool));
db_provided = FALSE;
}
return svn_error_trace(open_all(adm_access, path, db, db_provided,
write_lock, levels_to_lock,
cancel_func, cancel_baton, pool));
}
svn_error_t *
svn_wc_adm_probe_open3(svn_wc_adm_access_t **adm_access,
svn_wc_adm_access_t *associated,
const char *path,
svn_boolean_t write_lock,
int levels_to_lock,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
svn_error_t *err;
const char *dir;
if (associated == NULL)
{
svn_wc__db_t *db;
SVN_ERR(svn_wc__db_open(&db,
NULL , TRUE, TRUE, pool, pool));
err = probe(db, &dir, path, pool);
svn_error_clear(svn_wc__db_close(db));
SVN_ERR(err);
}
else
{
SVN_ERR(probe(associated->db, &dir, path, pool));
}
if (dir != path)
levels_to_lock = 0;
err = svn_wc_adm_open3(adm_access, associated, dir, write_lock,
levels_to_lock, cancel_func, cancel_baton, pool);
if (err)
{
svn_error_t *err2;
svn_node_kind_t child_kind;
if ((err2 = svn_io_check_path(path, &child_kind, pool)))
{
svn_error_compose(err, err2);
return err;
}
if ((dir != path)
&& (child_kind == svn_node_dir)
&& (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY))
{
svn_error_clear(err);
return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
_("'%s' is not a working copy"),
svn_dirent_local_style(path, pool));
}
return err;
}
return SVN_NO_ERROR;
}
svn_wc_adm_access_t *
svn_wc__adm_retrieve_internal2(svn_wc__db_t *db,
const char *abspath,
apr_pool_t *scratch_pool)
{
svn_wc_adm_access_t *adm_access = get_from_shared(abspath, db, scratch_pool);
if (IS_MISSING(adm_access))
adm_access = NULL;
return adm_access;
}
svn_error_t *
svn_wc_adm_retrieve(svn_wc_adm_access_t **adm_access,
svn_wc_adm_access_t *associated,
const char *path,
apr_pool_t *pool)
{
const char *local_abspath;
svn_wc__db_kind_t kind = svn_wc__db_kind_unknown;
svn_node_kind_t wckind;
svn_error_t *err;
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
if (strcmp(associated->path, path) == 0)
*adm_access = associated;
else
*adm_access = svn_wc__adm_retrieve_internal2(associated->db, local_abspath,
pool);
if (*adm_access)
return SVN_NO_ERROR;
err = svn_io_check_path(path, &wckind, pool);
if (err)
{
return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, err,
_("Unable to check path existence for '%s'"),
svn_dirent_local_style(path, pool));
}
if (associated)
{
err = svn_wc__db_read_kind(&kind, svn_wc__adm_get_db(associated),
local_abspath, TRUE, pool);
if (err)
{
kind = svn_wc__db_kind_unknown;
svn_error_clear(err);
}
}
if (kind == svn_wc__db_kind_dir && wckind == svn_node_file)
{
err = svn_error_createf(
SVN_ERR_WC_NOT_WORKING_COPY, NULL,
_("Expected '%s' to be a directory but found a file"),
svn_dirent_local_style(path, pool));
return svn_error_create(SVN_ERR_WC_NOT_LOCKED, err, err->message);
}
if (kind != svn_wc__db_kind_dir && kind != svn_wc__db_kind_unknown)
{
err = svn_error_createf(
SVN_ERR_WC_NOT_WORKING_COPY, NULL,
_("Can't retrieve an access baton for non-directory '%s'"),
svn_dirent_local_style(path, pool));
return svn_error_create(SVN_ERR_WC_NOT_LOCKED, err, err->message);
}
if (kind == svn_wc__db_kind_unknown || wckind == svn_node_none)
{
err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("Directory '%s' is missing"),
svn_dirent_local_style(path, pool));
return svn_error_create(SVN_ERR_WC_NOT_LOCKED, err, err->message);
}
return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
_("Working copy '%s' is not locked"),
svn_dirent_local_style(path, pool));
}
svn_error_t *
svn_wc_adm_probe_retrieve(svn_wc_adm_access_t **adm_access,
svn_wc_adm_access_t *associated,
const char *path,
apr_pool_t *pool)
{
const char *dir;
const char *local_abspath;
svn_wc__db_kind_t kind;
svn_error_t *err;
SVN_ERR_ASSERT(associated != NULL);
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
SVN_ERR(svn_wc__db_read_kind(&kind, associated->db, local_abspath, TRUE, pool));
if (kind == svn_wc__db_kind_dir)
dir = path;
else if (kind != svn_wc__db_kind_unknown)
dir = svn_dirent_dirname(path, pool);
else
SVN_ERR(probe(associated->db, &dir, path, pool));
err = svn_wc_adm_retrieve(adm_access, associated, dir, pool);
if (err && err->apr_err == SVN_ERR_WC_NOT_LOCKED)
{
svn_error_clear(err);
SVN_ERR(probe(associated->db, &dir, path, pool));
SVN_ERR(svn_wc_adm_retrieve(adm_access, associated, dir, pool));
}
else
return svn_error_trace(err);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_adm_probe_try3(svn_wc_adm_access_t **adm_access,
svn_wc_adm_access_t *associated,
const char *path,
svn_boolean_t write_lock,
int levels_to_lock,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
svn_error_t *err;
err = svn_wc_adm_probe_retrieve(adm_access, associated, path, pool);
if (err && (err->apr_err == SVN_ERR_WC_NOT_LOCKED))
{
svn_error_clear(err);
err = svn_wc_adm_probe_open3(adm_access, associated,
path, write_lock, levels_to_lock,
cancel_func, cancel_baton,
svn_wc_adm_access_pool(associated));
if (err && (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY))
{
svn_error_clear(err);
*adm_access = NULL;
err = NULL;
}
}
return err;
}
static svn_error_t *
child_is_disjoint(svn_boolean_t *disjoint,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
const char *node_repos_root, *node_repos_relpath, *node_repos_uuid;
const char *parent_repos_root, *parent_repos_relpath, *parent_repos_uuid;
svn_wc__db_status_t parent_status;
const char *parent_abspath, *base;
SVN_ERR(svn_wc__db_is_wcroot(disjoint, db, local_abspath, scratch_pool));
if (*disjoint)
return SVN_NO_ERROR;
svn_dirent_split(&parent_abspath, &base, local_abspath, scratch_pool);
SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, &node_repos_relpath,
&node_repos_root, &node_repos_uuid, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
db, local_abspath,
scratch_pool, scratch_pool));
if (node_repos_relpath == NULL)
{
*disjoint = FALSE;
return SVN_NO_ERROR;
}
SVN_ERR(svn_wc__db_read_info(&parent_status, NULL, NULL,
&parent_repos_relpath, &parent_repos_root,
&parent_repos_uuid, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
db, parent_abspath,
scratch_pool, scratch_pool));
if (parent_repos_relpath == NULL)
{
if (parent_status == svn_wc__db_status_added)
SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, &parent_repos_relpath,
&parent_repos_root,
&parent_repos_uuid,
NULL, NULL, NULL, NULL,
db, parent_abspath,
scratch_pool, scratch_pool));
else
SVN_ERR(svn_wc__db_scan_base_repos(&parent_repos_relpath,
&parent_repos_root,
&parent_repos_uuid,
db, parent_abspath,
scratch_pool, scratch_pool));
}
if (strcmp(parent_repos_root, node_repos_root) != 0 ||
strcmp(parent_repos_uuid, node_repos_uuid) != 0 ||
strcmp(svn_relpath_join(parent_repos_relpath, base, scratch_pool),
node_repos_relpath) != 0)
{
*disjoint = TRUE;
}
else
*disjoint = FALSE;
return SVN_NO_ERROR;
}
static svn_error_t *
open_anchor(svn_wc_adm_access_t **anchor_access,
svn_wc_adm_access_t **target_access,
const char **target,
svn_wc__db_t *db,
svn_boolean_t db_provided,
const char *path,
svn_boolean_t write_lock,
int levels_to_lock,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
const char *base_name = svn_dirent_basename(path, pool);
if (!db_provided)
SVN_ERR(svn_wc__db_open(&db, NULL, TRUE, TRUE,
pool, pool));
if (svn_path_is_empty(path)
|| svn_dirent_is_root(path, strlen(path))
|| ! strcmp(base_name, ".."))
{
SVN_ERR(open_all(anchor_access, path, db, db_provided,
write_lock, levels_to_lock,
cancel_func, cancel_baton, pool));
*target_access = *anchor_access;
*target = "";
}
else
{
svn_error_t *err;
svn_wc_adm_access_t *p_access = NULL;
svn_wc_adm_access_t *t_access = NULL;
const char *parent = svn_dirent_dirname(path, pool);
const char *local_abspath;
svn_error_t *p_access_err = SVN_NO_ERROR;
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
err = open_single(&p_access, parent, write_lock, db, db_provided,
pool, pool);
if (err)
{
const char *abspath = svn_dirent_dirname(local_abspath, pool);
svn_wc_adm_access_t *existing_adm = svn_wc__db_temp_get_access(db, abspath, pool);
if (IS_MISSING(existing_adm))
svn_wc__db_temp_clear_access(db, abspath, pool);
else
SVN_ERR_ASSERT(existing_adm == NULL);
if (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY)
{
svn_error_clear(err);
p_access = NULL;
}
else if (write_lock && (err->apr_err == SVN_ERR_WC_LOCKED
|| APR_STATUS_IS_EACCES(err->apr_err)))
{
svn_error_t *err2 = open_single(&p_access, parent, FALSE,
db, db_provided, pool, pool);
if (err2)
{
svn_error_clear(err2);
return err;
}
p_access_err = err;
}
else
return err;
}
err = open_all(&t_access, path, db, db_provided, write_lock,
levels_to_lock, cancel_func, cancel_baton, pool);
if (err)
{
if (p_access == NULL)
{
svn_error_clear(p_access_err);
return svn_error_trace(err);
}
if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
{
if (p_access)
svn_error_clear(svn_wc_adm_close2(p_access, pool));
svn_error_clear(p_access_err);
return svn_error_trace(err);
}
svn_error_clear(err);
t_access = NULL;
}
if (p_access && t_access)
{
svn_boolean_t disjoint;
err = child_is_disjoint(&disjoint, db, local_abspath, pool);
if (err)
{
svn_error_clear(p_access_err);
svn_error_clear(svn_wc_adm_close2(p_access, pool));
svn_error_clear(svn_wc_adm_close2(t_access, pool));
return svn_error_trace(err);
}
if (disjoint)
{
err = close_single(p_access, FALSE , pool);
if (err)
{
svn_error_clear(p_access_err);
svn_error_clear(svn_wc_adm_close2(t_access, pool));
return svn_error_trace(err);
}
p_access = NULL;
}
}
if (p_access && p_access_err)
{
if (t_access)
svn_error_clear(svn_wc_adm_close2(t_access, pool));
svn_error_clear(svn_wc_adm_close2(p_access, pool));
return svn_error_trace(p_access_err);
}
svn_error_clear(p_access_err);
if (! t_access)
{
svn_boolean_t available;
svn_wc__db_kind_t kind;
err = adm_available(&available, &kind, db, local_abspath, pool);
if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
svn_error_clear(err);
else if (err)
{
svn_error_clear(svn_wc_adm_close2(p_access, pool));
return svn_error_trace(err);
}
}
*anchor_access = p_access ? p_access : t_access;
*target_access = t_access ? t_access : p_access;
if (! p_access)
*target = "";
else
*target = base_name;
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_adm_open_anchor(svn_wc_adm_access_t **anchor_access,
svn_wc_adm_access_t **target_access,
const char **target,
const char *path,
svn_boolean_t write_lock,
int levels_to_lock,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
return svn_error_trace(open_anchor(anchor_access, target_access, target,
NULL, FALSE, path, write_lock,
levels_to_lock, cancel_func,
cancel_baton, pool));
}
static svn_error_t *
do_close(svn_wc_adm_access_t *adm_access,
svn_boolean_t preserve_lock,
apr_pool_t *scratch_pool)
{
svn_wc_adm_access_t *look;
if (adm_access->closed)
return SVN_NO_ERROR;
look = get_from_shared(adm_access->abspath, adm_access->db, scratch_pool);
if (look != NULL)
{
apr_hash_t *opened;
apr_hash_index_t *hi;
opened = svn_wc__db_temp_get_all_access(adm_access->db, scratch_pool);
for (hi = apr_hash_first(scratch_pool, opened);
hi;
hi = apr_hash_next(hi))
{
const char *abspath = svn__apr_hash_index_key(hi);
svn_wc_adm_access_t *child = svn__apr_hash_index_val(hi);
const char *path = child->path;
if (IS_MISSING(child))
{
svn_wc__db_temp_clear_access(adm_access->db, abspath,
scratch_pool);
continue;
}
if (! svn_dirent_is_ancestor(adm_access->path, path)
|| strcmp(adm_access->path, path) == 0)
continue;
SVN_ERR(close_single(child, preserve_lock, scratch_pool));
}
}
return svn_error_trace(close_single(adm_access, preserve_lock,
scratch_pool));
}
svn_error_t *
svn_wc_adm_close2(svn_wc_adm_access_t *adm_access, apr_pool_t *scratch_pool)
{
return svn_error_trace(do_close(adm_access, FALSE, scratch_pool));
}
svn_boolean_t
svn_wc_adm_locked(const svn_wc_adm_access_t *adm_access)
{
svn_boolean_t locked;
apr_pool_t *subpool = svn_pool_create(adm_access->pool);
svn_error_t *err = svn_wc__db_wclock_owns_lock(&locked, adm_access->db,
adm_access->abspath, TRUE,
subpool);
svn_pool_destroy(subpool);
if (err)
{
svn_error_clear(err);
return FALSE;
}
return locked;
}
svn_error_t *
svn_wc__write_check(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_boolean_t locked;
SVN_ERR(svn_wc__db_wclock_owns_lock(&locked, db, local_abspath, FALSE,
scratch_pool));
if (!locked)
return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
_("No write-lock in '%s'"),
svn_dirent_local_style(local_abspath,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_locked2(svn_boolean_t *locked_here,
svn_boolean_t *locked,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
if (locked_here != NULL)
SVN_ERR(svn_wc__db_wclock_owns_lock(locked_here, wc_ctx->db, local_abspath,
FALSE, scratch_pool));
if (locked != NULL)
SVN_ERR(svn_wc__db_wclocked(locked, wc_ctx->db, local_abspath,
scratch_pool));
return SVN_NO_ERROR;
}
const char *
svn_wc_adm_access_path(const svn_wc_adm_access_t *adm_access)
{
return adm_access->path;
}
const char *
svn_wc__adm_access_abspath(const svn_wc_adm_access_t *adm_access)
{
return adm_access->abspath;
}
apr_pool_t *
svn_wc_adm_access_pool(const svn_wc_adm_access_t *adm_access)
{
return adm_access->pool;
}
void
svn_wc__adm_access_set_entries(svn_wc_adm_access_t *adm_access,
apr_hash_t *entries)
{
adm_access->entries_all = entries;
}
apr_hash_t *
svn_wc__adm_access_entries(svn_wc_adm_access_t *adm_access)
{
#ifdef SVN_DISABLE_ENTRY_CACHE
return NULL;
#else
return adm_access->entries_all;
#endif
}
svn_wc__db_t *
svn_wc__adm_get_db(const svn_wc_adm_access_t *adm_access)
{
return adm_access->db;
}
svn_error_t *
svn_wc__acquire_write_lock(const char **lock_root_abspath,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
svn_boolean_t lock_anchor,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc__db_t *db = wc_ctx->db;
svn_wc__db_kind_t kind;
svn_error_t *err;
SVN_ERR(svn_wc__db_read_kind(&kind, wc_ctx->db, local_abspath,
(lock_root_abspath != NULL),
scratch_pool));
if (!lock_root_abspath && kind != svn_wc__db_kind_dir)
return svn_error_createf(SVN_ERR_WC_NOT_DIRECTORY, NULL,
_("Can't obtain lock on non-directory '%s'."),
svn_dirent_local_style(local_abspath,
scratch_pool));
if (lock_anchor && kind == svn_wc__db_kind_dir)
{
svn_boolean_t is_wcroot;
SVN_ERR_ASSERT(lock_root_abspath != NULL);
SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath,
scratch_pool));
if (is_wcroot)
lock_anchor = FALSE;
}
if (lock_anchor)
{
const char *parent_abspath;
svn_wc__db_kind_t parent_kind;
SVN_ERR_ASSERT(lock_root_abspath != NULL);
parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
err = svn_wc__db_read_kind(&parent_kind, db, parent_abspath, TRUE,
scratch_pool);
if (err && SVN_WC__ERR_IS_NOT_CURRENT_WC(err))
{
svn_error_clear(err);
parent_kind = svn_wc__db_kind_unknown;
}
else
SVN_ERR(err);
if (kind == svn_wc__db_kind_dir && parent_kind == svn_wc__db_kind_dir)
{
svn_boolean_t disjoint;
SVN_ERR(child_is_disjoint(&disjoint, wc_ctx->db, local_abspath,
scratch_pool));
if (!disjoint)
local_abspath = parent_abspath;
}
else if (parent_kind == svn_wc__db_kind_dir)
local_abspath = parent_abspath;
else if (kind != svn_wc__db_kind_dir)
return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
_("'%s' is not a working copy"),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
else if (kind != svn_wc__db_kind_dir)
{
local_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
if (kind == svn_wc__db_kind_unknown)
{
SVN_ERR(svn_wc__db_read_kind(&kind, db, local_abspath, FALSE,
scratch_pool));
if (kind != svn_wc__db_kind_dir)
return svn_error_createf(
SVN_ERR_WC_NOT_DIRECTORY, NULL,
_("Can't obtain lock on non-directory '%s'."),
svn_dirent_local_style(local_abspath,
scratch_pool));
}
}
if (lock_root_abspath)
*lock_root_abspath = apr_pstrdup(result_pool, local_abspath);
SVN_ERR(svn_wc__db_wclock_obtain(wc_ctx->db, local_abspath,
-1 ,
FALSE ,
scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__release_write_lock(svn_wc_context_t *wc_ctx,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
apr_uint64_t id;
svn_skel_t *work_item;
SVN_ERR(svn_wc__db_wq_fetch_next(&id, &work_item, wc_ctx->db, local_abspath,
0, scratch_pool, scratch_pool));
if (work_item)
{
return SVN_NO_ERROR;
}
SVN_ERR(svn_wc__db_wclock_release(wc_ctx->db, local_abspath, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__call_with_write_lock(svn_wc__with_write_lock_func_t func,
void *baton,
svn_wc_context_t *wc_ctx,
const char *local_abspath,
svn_boolean_t lock_anchor,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_error_t *err1, *err2;
const char *lock_root_abspath;
SVN_ERR(svn_wc__acquire_write_lock(&lock_root_abspath, wc_ctx, local_abspath,
lock_anchor, scratch_pool, scratch_pool));
err1 = svn_error_trace(func(baton, result_pool, scratch_pool));
err2 = svn_wc__release_write_lock(wc_ctx, lock_root_abspath, scratch_pool);
return svn_error_compose_create(err1, err2);
}