#define SVN_WC__I_AM_WC_DB
#include <assert.h>
#include "svn_dirent_uri.h"
#include "wc.h"
#include "adm_files.h"
#include "wc_db_private.h"
#include "wc-queries.h"
#include "svn_private_config.h"
#define SDB_FILE "wc.db"
#define UNKNOWN_WC_ID ((apr_int64_t) -1)
#define FORMAT_FROM_SDB (-1)
static svn_error_t *
get_old_version(int *version,
const char *abspath,
apr_pool_t *scratch_pool)
{
svn_error_t *err;
const char *format_file_path;
svn_node_kind_t kind;
format_file_path = svn_wc__adm_child(abspath, SVN_WC__ADM_ENTRIES,
scratch_pool);
SVN_ERR(svn_io_check_path(format_file_path, &kind, scratch_pool));
if (kind == svn_node_none)
{
*version = 0;
return SVN_NO_ERROR;
}
err = svn_io_read_version_file(version, format_file_path, scratch_pool);
if (err == NULL)
return SVN_NO_ERROR;
if (err->apr_err != SVN_ERR_BAD_VERSION_FILE_FORMAT
&& !APR_STATUS_IS_ENOENT(err->apr_err)
&& !APR_STATUS_IS_ENOTDIR(err->apr_err))
return svn_error_createf(SVN_ERR_WC_MISSING, err, _("'%s' does not exist"),
svn_dirent_local_style(abspath, scratch_pool));
svn_error_clear(err);
format_file_path = svn_wc__adm_child(abspath, SVN_WC__ADM_FORMAT,
scratch_pool);
err = svn_io_read_version_file(version, format_file_path, scratch_pool);
if (err == NULL)
return SVN_NO_ERROR;
svn_error_clear(err);
*version = 0;
return SVN_NO_ERROR;
}
static svn_error_t *
get_path_kind(svn_node_kind_t *kind,
svn_boolean_t *is_symlink,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
if (db->parse_cache.abspath
&& strcmp(db->parse_cache.abspath->data, local_abspath) == 0)
{
*kind = db->parse_cache.kind;
*is_symlink = db->parse_cache.is_symlink;
return SVN_NO_ERROR;
}
if (!db->parse_cache.abspath)
{
db->parse_cache.abspath = svn_stringbuf_create(local_abspath,
db->state_pool);
}
else
{
svn_stringbuf_set(db->parse_cache.abspath, local_abspath);
}
SVN_ERR(svn_io_check_special_path(local_abspath, kind,
is_symlink, scratch_pool));
db->parse_cache.kind = *kind;
db->parse_cache.is_symlink = *is_symlink;
return SVN_NO_ERROR;
}
static svn_error_t *
verify_no_work(svn_sqlite__db_t *sdb)
{
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_LOOK_FOR_WORK));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
SVN_ERR(svn_sqlite__reset(stmt));
if (have_row)
return svn_error_create(SVN_ERR_WC_CLEANUP_REQUIRED, NULL,
NULL );
return SVN_NO_ERROR;
}
static apr_status_t
close_wcroot(void *data)
{
svn_wc__db_wcroot_t *wcroot = data;
svn_error_t *err;
SVN_ERR_ASSERT_NO_RETURN(wcroot->sdb != NULL);
err = svn_sqlite__close(wcroot->sdb);
wcroot->sdb = NULL;
if (err)
{
apr_status_t result = err->apr_err;
svn_error_clear(err);
return result;
}
return APR_SUCCESS;
}
svn_error_t *
svn_wc__db_open(svn_wc__db_t **db,
const svn_config_t *config,
svn_boolean_t auto_upgrade,
svn_boolean_t enforce_empty_wq,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
*db = apr_pcalloc(result_pool, sizeof(**db));
(*db)->config = config;
(*db)->auto_upgrade = auto_upgrade;
(*db)->enforce_empty_wq = enforce_empty_wq;
(*db)->dir_data = apr_hash_make(result_pool);
(*db)->state_pool = result_pool;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_close(svn_wc__db_t *db)
{
apr_pool_t *scratch_pool = db->state_pool;
apr_hash_t *roots = apr_hash_make(scratch_pool);
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, db->dir_data);
hi;
hi = apr_hash_next(hi))
{
svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
const char *local_abspath = svn__apr_hash_index_key(hi);
if (wcroot->sdb)
apr_hash_set(roots, wcroot->abspath, APR_HASH_KEY_STRING, wcroot);
apr_hash_set(db->dir_data, local_abspath, APR_HASH_KEY_STRING, NULL);
}
return svn_error_trace(svn_wc__db_close_many_wcroots(roots, db->state_pool,
scratch_pool));
}
svn_error_t *
svn_wc__db_pdh_create_wcroot(svn_wc__db_wcroot_t **wcroot,
const char *wcroot_abspath,
svn_sqlite__db_t *sdb,
apr_int64_t wc_id,
int format,
svn_boolean_t auto_upgrade,
svn_boolean_t enforce_empty_wq,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
if (sdb != NULL)
SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
SVN_ERR_ASSERT(format >= 1);
if (format < 4)
{
return svn_error_createf(
SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
_("Working copy format of '%s' is too old (%d); "
"please check out your working copy again"),
svn_dirent_local_style(wcroot_abspath, scratch_pool), format);
}
if (format > SVN_WC__VERSION)
{
return svn_error_createf(
SVN_ERR_WC_UNSUPPORTED_FORMAT, NULL,
_("This client is too old to work with the working copy at\n"
"'%s' (format %d).\n"
"You need to get a newer Subversion client. For more details, see\n"
" http://subversion.apache.org/faq.html#working-copy-format-change\n"
),
svn_dirent_local_style(wcroot_abspath, scratch_pool),
format);
}
if (format >= SVN_WC__HAS_WORK_QUEUE
&& (enforce_empty_wq || (format < SVN_WC__VERSION && auto_upgrade)))
{
svn_error_t *err = verify_no_work(sdb);
if (err)
{
if (err->apr_err == SVN_ERR_WC_CLEANUP_REQUIRED
&& format < SVN_WC__VERSION && auto_upgrade)
err = svn_error_quick_wrap(err, _("Cleanup with an older 1.7 "
"client before upgrading with "
"this client"));
return svn_error_trace(err);
}
}
if (format < SVN_WC__VERSION && auto_upgrade)
SVN_ERR(svn_wc__upgrade_sdb(&format, wcroot_abspath, sdb, format,
scratch_pool));
*wcroot = apr_palloc(result_pool, sizeof(**wcroot));
(*wcroot)->abspath = wcroot_abspath;
(*wcroot)->sdb = sdb;
(*wcroot)->wc_id = wc_id;
(*wcroot)->format = format;
(*wcroot)->owned_locks = apr_array_make(result_pool, 8,
sizeof(svn_wc__db_wclock_t));
(*wcroot)->access_cache = apr_hash_make(result_pool);
if (sdb != NULL)
apr_pool_cleanup_register(result_pool, *wcroot, close_wcroot,
apr_pool_cleanup_null);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_close_many_wcroots(apr_hash_t *roots,
apr_pool_t *state_pool,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, roots); hi; hi = apr_hash_next(hi))
{
svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
apr_status_t result;
result = apr_pool_cleanup_run(state_pool, wcroot, close_wcroot);
if (result != APR_SUCCESS)
return svn_error_wrap_apr(result, NULL);
}
return SVN_NO_ERROR;
}
static const char *
compute_relpath(const svn_wc__db_wcroot_t *wcroot,
const char *local_abspath,
apr_pool_t *result_pool)
{
const char *relpath = svn_dirent_is_child(wcroot->abspath, local_abspath,
result_pool);
if (relpath == NULL)
return "";
return relpath;
}
svn_error_t *
svn_wc__db_wcroot_parse_local_abspath(svn_wc__db_wcroot_t **wcroot,
const char **local_relpath,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *local_dir_abspath;
const char *original_abspath = local_abspath;
svn_node_kind_t kind;
const char *build_relpath;
svn_wc__db_wcroot_t *probe_wcroot;
svn_wc__db_wcroot_t *found_wcroot = NULL;
const char *scan_abspath;
svn_sqlite__db_t *sdb;
svn_boolean_t moved_upwards = FALSE;
svn_boolean_t always_check = FALSE;
svn_boolean_t is_symlink;
int wc_format = 0;
const char *adm_relpath;
probe_wcroot = apr_hash_get(db->dir_data, local_abspath,
APR_HASH_KEY_STRING);
if (probe_wcroot != NULL)
{
*wcroot = probe_wcroot;
*local_relpath = compute_relpath(probe_wcroot, local_abspath,
result_pool);
return SVN_NO_ERROR;
}
SVN_ERR(get_path_kind(&kind, &is_symlink, db, local_abspath, scratch_pool));
if (kind != svn_node_dir || is_symlink)
{
svn_dirent_split(&local_dir_abspath, &build_relpath, local_abspath,
scratch_pool);
probe_wcroot = apr_hash_get(db->dir_data, local_dir_abspath,
APR_HASH_KEY_STRING);
if (probe_wcroot != NULL)
{
const char *dir_relpath;
*wcroot = probe_wcroot;
dir_relpath = compute_relpath(probe_wcroot, local_dir_abspath,
NULL);
*local_relpath = svn_relpath_join(dir_relpath,
build_relpath,
result_pool);
return SVN_NO_ERROR;
}
if (kind == svn_node_none)
always_check = TRUE;
local_abspath = local_dir_abspath;
}
else
{
build_relpath = "";
local_dir_abspath = local_abspath;
}
adm_relpath = svn_wc_get_adm_dir(scratch_pool);
while (TRUE)
{
svn_error_t *err;
svn_node_kind_t adm_subdir_kind;
const char *adm_subdir = svn_dirent_join(local_abspath, adm_relpath,
scratch_pool);
SVN_ERR(svn_io_check_path(adm_subdir, &adm_subdir_kind, scratch_pool));
if (adm_subdir_kind == svn_node_dir)
{
err = svn_wc__db_util_open_db(&sdb, local_abspath, SDB_FILE,
svn_sqlite__mode_readwrite, NULL,
db->state_pool, scratch_pool);
if (err == NULL)
{
#ifdef SVN_DEBUG
SVN_ERR(svn_sqlite__exec_statements(sdb,
STMT_VERIFICATION_TRIGGERS));
#endif
break;
}
if (err->apr_err != SVN_ERR_SQLITE_ERROR
&& !APR_STATUS_IS_ENOENT(err->apr_err))
return svn_error_trace(err);
svn_error_clear(err);
if (!moved_upwards || always_check)
{
SVN_ERR(get_old_version(&wc_format, local_abspath,
scratch_pool));
if (wc_format != 0)
break;
}
}
if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
{
if (is_symlink)
{
svn_node_kind_t resolved_kind;
local_abspath = original_abspath;
SVN_ERR(svn_io_check_resolved_path(local_abspath,
&resolved_kind,
scratch_pool));
if (resolved_kind == svn_node_dir)
{
found_wcroot = apr_hash_get(db->dir_data, local_abspath,
APR_HASH_KEY_STRING);
if (found_wcroot)
break;
try_symlink_as_dir:
kind = svn_node_dir;
is_symlink = FALSE;
moved_upwards = FALSE;
local_dir_abspath = local_abspath;
build_relpath = "";
continue;
}
}
return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
_("'%s' is not a working copy"),
svn_dirent_local_style(original_abspath,
scratch_pool));
}
local_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
moved_upwards = TRUE;
found_wcroot = apr_hash_get(db->dir_data, local_abspath,
APR_HASH_KEY_STRING);
if (found_wcroot != NULL)
break;
}
if (found_wcroot != NULL)
{
*wcroot = found_wcroot;
}
else if (wc_format == 0)
{
apr_int64_t wc_id;
svn_error_t *err;
err = svn_wc__db_util_fetch_wc_id(&wc_id, sdb, scratch_pool);
if (err)
{
if (err->apr_err == SVN_ERR_WC_CORRUPT)
return svn_error_quick_wrap(
err, apr_psprintf(scratch_pool,
_("Missing a row in WCROOT for '%s'."),
svn_dirent_local_style(original_abspath,
scratch_pool)));
return svn_error_trace(err);
}
err = svn_wc__db_pdh_create_wcroot(wcroot,
apr_pstrdup(db->state_pool, local_abspath),
sdb, wc_id, FORMAT_FROM_SDB,
db->auto_upgrade, db->enforce_empty_wq,
db->state_pool, scratch_pool);
if (err && err->apr_err == SVN_ERR_WC_UNSUPPORTED_FORMAT && is_symlink)
{
svn_error_clear(err);
*wcroot = NULL;
}
else
SVN_ERR(err);
}
else
{
SVN_ERR(svn_wc__db_pdh_create_wcroot(wcroot,
apr_pstrdup(db->state_pool, local_abspath),
NULL, UNKNOWN_WC_ID, wc_format,
db->auto_upgrade, db->enforce_empty_wq,
db->state_pool, scratch_pool));
}
if (*wcroot)
{
const char *dir_relpath;
dir_relpath = compute_relpath(*wcroot, local_dir_abspath, NULL);
*local_relpath = svn_relpath_join(dir_relpath, build_relpath, result_pool);
}
if (is_symlink)
{
svn_boolean_t retry_if_dir = FALSE;
svn_wc__db_status_t status;
svn_boolean_t conflicted;
svn_error_t *err;
if (*wcroot)
{
err = svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, &conflicted,
NULL, NULL, NULL, NULL, NULL,
NULL, *wcroot, *local_relpath,
scratch_pool, scratch_pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
&& !SVN_WC__ERR_IS_NOT_CURRENT_WC(err))
return svn_error_trace(err);
svn_error_clear(err);
retry_if_dir = TRUE;
}
else
{
retry_if_dir = ((status == svn_wc__db_status_not_present ||
status == svn_wc__db_status_excluded ||
status == svn_wc__db_status_server_excluded)
&& !conflicted);
}
}
else
retry_if_dir = TRUE;
if (retry_if_dir)
{
svn_node_kind_t resolved_kind;
SVN_ERR(svn_io_check_resolved_path(original_abspath,
&resolved_kind,
scratch_pool));
if (resolved_kind == svn_node_dir)
{
local_abspath = original_abspath;
goto try_symlink_as_dir;
}
}
}
apr_hash_set(db->dir_data,
apr_pstrdup(db->state_pool, local_dir_abspath),
APR_HASH_KEY_STRING,
*wcroot);
if (!moved_upwards)
{
return SVN_NO_ERROR;
}
scan_abspath = local_dir_abspath;
do
{
const char *parent_dir = svn_dirent_dirname(scan_abspath, scratch_pool);
svn_wc__db_wcroot_t *parent_wcroot;
parent_wcroot = apr_hash_get(db->dir_data, parent_dir,
APR_HASH_KEY_STRING);
if (parent_wcroot == NULL)
{
apr_hash_set(db->dir_data, apr_pstrdup(db->state_pool, parent_dir),
APR_HASH_KEY_STRING, *wcroot);
}
scan_abspath = parent_dir;
}
while (strcmp(scan_abspath, local_abspath) != 0);
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__db_drop_root(svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
svn_wc__db_wcroot_t *root_wcroot = apr_hash_get(db->dir_data, local_abspath,
APR_HASH_KEY_STRING);
apr_hash_index_t *hi;
apr_status_t result;
if (!root_wcroot)
return SVN_NO_ERROR;
if (strcmp(root_wcroot->abspath, local_abspath) != 0)
return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
_("'%s' is not a working copy root"),
svn_dirent_local_style(local_abspath,
scratch_pool));
for (hi = apr_hash_first(scratch_pool, db->dir_data);
hi;
hi = apr_hash_next(hi))
{
svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
if (wcroot == root_wcroot)
apr_hash_set(db->dir_data,
svn__apr_hash_index_key(hi), APR_HASH_KEY_STRING, NULL);
}
result = apr_pool_cleanup_run(db->state_pool, root_wcroot, close_wcroot);
if (result != APR_SUCCESS)
return svn_error_wrap_apr(result, NULL);
return SVN_NO_ERROR;
}