summaryrefslogtreecommitdiff
path: root/subversion/libsvn_fs_base
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-08-05 16:22:51 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-08-05 16:22:51 +0000
commitcf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch)
treeda27775a2161723ef342e91af41a8b51fedef405 /subversion/libsvn_fs_base
parentbb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff)
downloadsubversion-tarball-cf46733632c7279a9fd0fe6ce26f9185a4ae82a9.tar.gz
Diffstat (limited to 'subversion/libsvn_fs_base')
-rw-r--r--subversion/libsvn_fs_base/bdb/changes-table.c81
-rw-r--r--subversion/libsvn_fs_base/bdb/locks-table.c2
-rw-r--r--subversion/libsvn_fs_base/bdb/strings-table.c4
-rw-r--r--subversion/libsvn_fs_base/dag.c29
-rw-r--r--subversion/libsvn_fs_base/dag.h2
-rw-r--r--subversion/libsvn_fs_base/fs.c149
-rw-r--r--subversion/libsvn_fs_base/fs.h6
-rw-r--r--subversion/libsvn_fs_base/id.c7
-rw-r--r--subversion/libsvn_fs_base/id.h6
-rw-r--r--subversion/libsvn_fs_base/key-gen.c36
-rw-r--r--subversion/libsvn_fs_base/key-gen.h7
-rw-r--r--subversion/libsvn_fs_base/libsvn_fs_base.pc.in12
-rw-r--r--subversion/libsvn_fs_base/lock.c148
-rw-r--r--subversion/libsvn_fs_base/lock.h27
-rw-r--r--subversion/libsvn_fs_base/reps-strings.c5
-rw-r--r--subversion/libsvn_fs_base/revs-txns.c45
-rw-r--r--subversion/libsvn_fs_base/tree.c177
17 files changed, 556 insertions, 187 deletions
diff --git a/subversion/libsvn_fs_base/bdb/changes-table.c b/subversion/libsvn_fs_base/bdb/changes-table.c
index 80ff468..b206318 100644
--- a/subversion/libsvn_fs_base/bdb/changes-table.c
+++ b/subversion/libsvn_fs_base/bdb/changes-table.c
@@ -121,12 +121,32 @@ svn_fs_bdb__changes_delete(svn_fs_t *fs,
return SVN_NO_ERROR;
}
+/* Return a deep FS API type copy of SOURCE in internal format and allocate
+ * the result in RESULT_POOL.
+ */
+static svn_fs_path_change2_t *
+change_to_fs_change(const change_t *change,
+ apr_pool_t *result_pool)
+{
+ svn_fs_path_change2_t *result = svn_fs__path_change_create_internal(
+ svn_fs_base__id_copy(change->noderev_id,
+ result_pool),
+ change->kind,
+ result_pool);
+ result->text_mod = change->text_mod;
+ result->prop_mod = change->prop_mod;
+ result->node_kind = svn_node_unknown;
+ result->copyfrom_known = FALSE;
+
+ return result;
+}
/* Merge the internal-use-only CHANGE into a hash of public-FS
svn_fs_path_change2_t CHANGES, collapsing multiple changes into a
single succinct change per path. */
static svn_error_t *
fold_change(apr_hash_t *changes,
+ apr_hash_t *deletions,
const change_t *change)
{
apr_pool_t *pool = apr_hash_pool_get(changes);
@@ -185,7 +205,7 @@ fold_change(apr_hash_t *changes,
case svn_fs_path_change_reset:
/* A reset here will simply remove the path change from the
hash. */
- old_change = NULL;
+ new_change = NULL;
break;
case svn_fs_path_change_delete:
@@ -194,14 +214,21 @@ fold_change(apr_hash_t *changes,
/* If the path was introduced in this transaction via an
add, and we are deleting it, just remove the path
altogether. */
- old_change = NULL;
+ new_change = NULL;
+ }
+ else if (old_change->change_kind == svn_fs_path_change_replace)
+ {
+ /* A deleting a 'replace' restore the original deletion. */
+ new_change = svn_hash_gets(deletions, path);
+ SVN_ERR_ASSERT(new_change);
}
else
{
/* A deletion overrules all previous changes. */
- old_change->change_kind = svn_fs_path_change_delete;
- old_change->text_mod = change->text_mod;
- old_change->prop_mod = change->prop_mod;
+ new_change = old_change;
+ new_change->change_kind = svn_fs_path_change_delete;
+ new_change->text_mod = change->text_mod;
+ new_change->prop_mod = change->prop_mod;
}
break;
@@ -209,38 +236,33 @@ fold_change(apr_hash_t *changes,
case svn_fs_path_change_replace:
/* An add at this point must be following a previous delete,
so treat it just like a replace. */
- old_change->change_kind = svn_fs_path_change_replace;
- old_change->node_rev_id = svn_fs_base__id_copy(change->noderev_id,
- pool);
- old_change->text_mod = change->text_mod;
- old_change->prop_mod = change->prop_mod;
+
+ new_change = change_to_fs_change(change, pool);
+ new_change->change_kind = svn_fs_path_change_replace;
+
+ /* Remember the original deletion.
+ * Make sure to allocate the hash key in a durable pool. */
+ svn_hash_sets(deletions,
+ apr_pstrdup(apr_hash_pool_get(deletions), path),
+ old_change);
break;
case svn_fs_path_change_modify:
default:
+ new_change = old_change;
if (change->text_mod)
- old_change->text_mod = TRUE;
+ new_change->text_mod = TRUE;
if (change->prop_mod)
- old_change->prop_mod = TRUE;
+ new_change->prop_mod = TRUE;
break;
}
-
- /* Point our new_change to our (possibly modified) old_change. */
- new_change = old_change;
}
else
{
/* This change is new to the hash, so make a new public change
structure from the internal one (in the hash's pool), and dup
the path into the hash's pool, too. */
- new_change = svn_fs__path_change_create_internal(
- svn_fs_base__id_copy(change->noderev_id, pool),
- change->kind,
- pool);
- new_change->text_mod = change->text_mod;
- new_change->prop_mod = change->prop_mod;
- new_change->node_kind = svn_node_unknown;
- new_change->copyfrom_known = FALSE;
+ new_change = change_to_fs_change(change, pool);
path = apr_pstrdup(pool, change->path);
}
@@ -265,6 +287,8 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p,
svn_error_t *err = SVN_NO_ERROR;
apr_hash_t *changes = apr_hash_make(pool);
apr_pool_t *subpool = svn_pool_create(pool);
+ apr_pool_t *iterpool = svn_pool_create(pool);
+ apr_hash_t *deletions = apr_hash_make(subpool);
/* Get a cursor on the first record matching KEY, and then loop over
the records, adding them to the return array. */
@@ -286,11 +310,11 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p,
svn_skel_t *result_skel;
/* Clear the per-iteration subpool. */
- svn_pool_clear(subpool);
+ svn_pool_clear(iterpool);
/* RESULT now contains a change record associated with KEY. We
need to parse that skel into an change_t structure ... */
- result_skel = svn_skel__parse(result.data, result.size, subpool);
+ result_skel = svn_skel__parse(result.data, result.size, iterpool);
if (! result_skel)
{
err = svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
@@ -298,12 +322,12 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p,
key);
goto cleanup;
}
- err = svn_fs_base__parse_change_skel(&change, result_skel, subpool);
+ err = svn_fs_base__parse_change_skel(&change, result_skel, iterpool);
if (err)
goto cleanup;
/* ... and merge it with our return hash. */
- err = fold_change(changes, change);
+ err = fold_change(changes, deletions, change);
if (err)
goto cleanup;
@@ -319,7 +343,7 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p,
{
apr_hash_index_t *hi;
- for (hi = apr_hash_first(subpool, changes);
+ for (hi = apr_hash_first(iterpool, changes);
hi;
hi = apr_hash_next(hi))
{
@@ -347,6 +371,7 @@ svn_fs_bdb__changes_fetch(apr_hash_t **changes_p,
}
/* Destroy the per-iteration subpool. */
+ svn_pool_destroy(iterpool);
svn_pool_destroy(subpool);
/* If there are no (more) change records for this KEY, we're
diff --git a/subversion/libsvn_fs_base/bdb/locks-table.c b/subversion/libsvn_fs_base/bdb/locks-table.c
index a22663f..e81bca8 100644
--- a/subversion/libsvn_fs_base/bdb/locks-table.c
+++ b/subversion/libsvn_fs_base/bdb/locks-table.c
@@ -257,7 +257,7 @@ svn_fs_bdb__locks_get(svn_fs_t *fs,
DB_SET_RANGE);
if (!svn_fspath__is_root(path, strlen(path)))
- lookup_path = apr_pstrcat(pool, path, "/", (char *)NULL);
+ lookup_path = apr_pstrcat(pool, path, "/", SVN_VA_NULL);
lookup_len = strlen(lookup_path);
/* As long as the prefix of the returned KEY matches LOOKUP_PATH we
diff --git a/subversion/libsvn_fs_base/bdb/strings-table.c b/subversion/libsvn_fs_base/bdb/strings-table.c
index f5348e7..e1f4b90 100644
--- a/subversion/libsvn_fs_base/bdb/strings-table.c
+++ b/subversion/libsvn_fs_base/bdb/strings-table.c
@@ -236,9 +236,9 @@ svn_fs_bdb__string_read(svn_fs_t *fs,
{
svn_fs_base__clear_dbt(&result);
result.data = buf + bytes_read;
- result.ulen = *len - bytes_read;
+ result.ulen = (u_int32_t)(*len - bytes_read);
result.doff = (u_int32_t)offset;
- result.dlen = *len - bytes_read;
+ result.dlen = result.ulen;
result.flags |= (DB_DBT_USERMEM | DB_DBT_PARTIAL);
db_err = svn_bdb_dbc_get(cursor, &query, &result, DB_CURRENT);
if (db_err)
diff --git a/subversion/libsvn_fs_base/dag.c b/subversion/libsvn_fs_base/dag.c
index 510ccbb..7c79dc2 100644
--- a/subversion/libsvn_fs_base/dag.c
+++ b/subversion/libsvn_fs_base/dag.c
@@ -1028,12 +1028,14 @@ svn_fs_base__dag_delete_if_mutable(svn_fs_t *fs,
void *val;
svn_fs_dirent_t *dirent;
+ svn_pool_clear(subpool);
apr_hash_this(hi, NULL, NULL, &val);
dirent = val;
SVN_ERR(svn_fs_base__dag_delete_if_mutable(fs, dirent->id,
txn_id, trail,
subpool));
}
+ svn_pool_destroy(subpool);
}
}
@@ -1342,7 +1344,7 @@ svn_fs_base__dag_finalize_edits(dag_node_t *file,
dag_node_t *
-svn_fs_base__dag_dup(dag_node_t *node,
+svn_fs_base__dag_dup(const dag_node_t *node,
apr_pool_t *pool)
{
/* Allocate our new node. */
@@ -1579,10 +1581,10 @@ svn_fs_base__dag_commit_txn(svn_revnum_t *new_rev,
apr_pool_t *pool)
{
revision_t revision;
- svn_string_t date;
apr_hash_t *txnprops;
svn_fs_t *fs = txn->fs;
const char *txn_id = txn->id;
+ const svn_string_t *client_date;
/* Remove any temporary transaction properties initially created by
begin_txn(). */
@@ -1601,16 +1603,27 @@ svn_fs_base__dag_commit_txn(svn_revnum_t *new_rev,
SVN_ERR(svn_fs_base__set_txn_prop
(fs, txn_id, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL, trail, pool));
+ client_date = svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CLIENT_DATE);
+ if (client_date)
+ SVN_ERR(svn_fs_base__set_txn_prop
+ (fs, txn_id, SVN_FS__PROP_TXN_CLIENT_DATE, NULL, trail, pool));
+
/* Promote the unfinished transaction to a committed one. */
SVN_ERR(svn_fs_base__txn_make_committed(fs, txn_id, *new_rev,
trail, pool));
- /* Set a date on the commit. We wait until now to fetch the date,
- so it's definitely newer than any previous revision's date. */
- date.data = svn_time_to_cstring(apr_time_now(), pool);
- date.len = strlen(date.data);
- return svn_fs_base__set_rev_prop(fs, *new_rev, SVN_PROP_REVISION_DATE,
- NULL, &date, trail, pool);
+ if (!client_date || strcmp(client_date->data, "1"))
+ {
+ /* Set a date on the commit if requested. We wait until now to fetch the
+ date, so it's definitely newer than any previous revision's date. */
+ svn_string_t date;
+ date.data = svn_time_to_cstring(apr_time_now(), pool);
+ date.len = strlen(date.data);
+ SVN_ERR(svn_fs_base__set_rev_prop(fs, *new_rev, SVN_PROP_REVISION_DATE,
+ NULL, &date, trail, pool));
+ }
+
+ return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_fs_base/dag.h b/subversion/libsvn_fs_base/dag.h
index 4c50c84..fb963ce 100644
--- a/subversion/libsvn_fs_base/dag.h
+++ b/subversion/libsvn_fs_base/dag.h
@@ -82,7 +82,7 @@ svn_error_t *svn_fs_base__dag_get_node(dag_node_t **node,
/* Return a new dag_node_t object referring to the same node as NODE,
allocated in POOL. */
-dag_node_t *svn_fs_base__dag_dup(dag_node_t *node,
+dag_node_t *svn_fs_base__dag_dup(const dag_node_t *node,
apr_pool_t *pool);
diff --git a/subversion/libsvn_fs_base/fs.c b/subversion/libsvn_fs_base/fs.c
index 4ad9e6f..06dfbaf 100644
--- a/subversion/libsvn_fs_base/fs.c
+++ b/subversion/libsvn_fs_base/fs.c
@@ -65,8 +65,6 @@
#include "../libsvn_fs/fs-loader.h"
#include "private/svn_fs_util.h"
-#include "private/svn_subr_private.h"
-
/* Checking for return values, and reporting errors. */
@@ -473,6 +471,59 @@ bdb_write_config(svn_fs_t *fs)
}
static svn_error_t *
+base_bdb_info_format(int *fs_format,
+ svn_version_t **supports_version,
+ svn_fs_t *fs,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ base_fs_data_t *bfd = fs->fsap_data;
+
+ *fs_format = bfd->format;
+ *supports_version = apr_palloc(result_pool, sizeof(svn_version_t));
+
+ (*supports_version)->major = SVN_VER_MAJOR;
+ (*supports_version)->minor = 0;
+ (*supports_version)->patch = 0;
+ (*supports_version)->tag = "";
+
+ switch (bfd->format)
+ {
+ case 1:
+ break;
+ case 2:
+ (*supports_version)->minor = 4;
+ break;
+ case 3:
+ (*supports_version)->minor = 5;
+ break;
+ case 4:
+ (*supports_version)->minor = 6;
+ break;
+#ifdef SVN_DEBUG
+# if SVN_FS_BASE__FORMAT_NUMBER != 4
+# error "Need to add a 'case' statement here"
+# endif
+#endif
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+base_bdb_info_config_files(apr_array_header_t **files,
+ svn_fs_t *fs,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ *files = apr_array_make(result_pool, 1, sizeof(const char *));
+ APR_ARRAY_PUSH(*files, const char *) = svn_dirent_join(fs->path,
+ BDB_CONFIG_FILE,
+ result_pool);
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
base_bdb_verify_root(svn_fs_root_t *root,
apr_pool_t *scratch_pool)
{
@@ -509,6 +560,9 @@ static fs_vtable_t fs_vtable = {
svn_fs_base__unlock,
svn_fs_base__get_lock,
svn_fs_base__get_locks,
+ base_bdb_info_format,
+ base_bdb_info_config_files,
+ NULL /* info_fsap */,
base_bdb_verify_root,
base_bdb_freeze,
base_bdb_set_errcall,
@@ -675,7 +729,10 @@ populate_opened_fs(svn_fs_t *fs, apr_pool_t *scratch_pool)
}
static svn_error_t *
-base_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,
+base_create(svn_fs_t *fs,
+ const char *path,
+ svn_mutex__t *common_pool_lock,
+ apr_pool_t *pool,
apr_pool_t *common_pool)
{
int format = SVN_FS_BASE__FORMAT_NUMBER;
@@ -684,12 +741,27 @@ base_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,
/* See if compatibility with older versions was explicitly requested. */
if (fs->config)
{
- if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_4_COMPATIBLE))
- format = 1;
- else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_5_COMPATIBLE))
- format = 2;
- else if (svn_hash_gets(fs->config, SVN_FS_CONFIG_PRE_1_6_COMPATIBLE))
- format = 3;
+ svn_version_t *compatible_version;
+ SVN_ERR(svn_fs__compatible_version(&compatible_version, fs->config,
+ pool));
+
+ /* select format number */
+ switch(compatible_version->minor)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3: format = 1;
+ break;
+
+ case 4: format = 2;
+ break;
+
+ case 5: format = 3;
+ break;
+
+ default:format = SVN_FS_BASE__FORMAT_NUMBER;
+ }
}
/* Create the environment and databases. */
@@ -711,8 +783,8 @@ base_create(svn_fs_t *fs, const char *path, apr_pool_t *pool,
return SVN_NO_ERROR;;
error:
- svn_error_clear(cleanup_fs(fs));
- return svn_err;
+ return svn_error_compose_create(svn_err,
+ svn_error_trace(cleanup_fs(fs)));
}
@@ -751,7 +823,10 @@ check_format(int format)
}
static svn_error_t *
-base_open(svn_fs_t *fs, const char *path, apr_pool_t *pool,
+base_open(svn_fs_t *fs,
+ const char *path,
+ svn_mutex__t *common_pool_lock,
+ apr_pool_t *pool,
apr_pool_t *common_pool)
{
int format;
@@ -796,8 +871,8 @@ base_open(svn_fs_t *fs, const char *path, apr_pool_t *pool,
return SVN_NO_ERROR;
error:
- svn_error_clear(cleanup_fs(fs));
- return svn_err;
+ return svn_error_compose_create(svn_err,
+ svn_error_trace(cleanup_fs(fs)));
}
@@ -834,7 +909,10 @@ bdb_recover(const char *path, svn_boolean_t fatal, apr_pool_t *pool)
}
static svn_error_t *
-base_open_for_recovery(svn_fs_t *fs, const char *path, apr_pool_t *pool,
+base_open_for_recovery(svn_fs_t *fs,
+ const char *path,
+ svn_mutex__t *common_pool_lock,
+ apr_pool_t *pool,
apr_pool_t *common_pool)
{
/* Just stash the path in the fs pointer - it's all we really need. */
@@ -844,7 +922,14 @@ base_open_for_recovery(svn_fs_t *fs, const char *path, apr_pool_t *pool,
}
static svn_error_t *
-base_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool,
+base_upgrade(svn_fs_t *fs,
+ const char *path,
+ svn_fs_upgrade_notify_t notify_func,
+ void *notify_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ svn_mutex__t *common_pool_lock,
+ apr_pool_t *pool,
apr_pool_t *common_pool)
{
const char *version_file_path;
@@ -867,6 +952,9 @@ base_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool,
/* Bump the format file's stored version number. */
SVN_ERR(svn_io_write_version_file(version_file_path,
SVN_FS_BASE__FORMAT_NUMBER, pool));
+ if (notify_func)
+ SVN_ERR(notify_func(notify_baton, SVN_FS_BASE__FORMAT_NUMBER,
+ svn_fs_upgrade_format_bumped, pool));
/* Check and see if we need to record the "bump" revision. */
if (old_format_number < SVN_FS_BASE__MIN_FORWARD_DELTAS_FORMAT)
@@ -883,7 +971,7 @@ base_upgrade(svn_fs_t *fs, const char *path, apr_pool_t *pool,
But it's better to use the existing encapsulation of "opening
the filesystem" rather than duplicating (or worse, partially
duplicating) that logic here. */
- SVN_ERR(base_open(fs, path, subpool, common_pool));
+ SVN_ERR(base_open(fs, path, common_pool_lock, subpool, common_pool));
/* Fetch the youngest rev, and record it */
SVN_ERR(svn_fs_base__youngest_rev(&youngest_rev, fs, subpool));
@@ -905,6 +993,7 @@ base_verify(svn_fs_t *fs, const char *path,
void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
+ svn_mutex__t *common_pool_lock,
apr_pool_t *pool,
apr_pool_t *common_pool)
{
@@ -929,6 +1018,7 @@ base_bdb_pack(svn_fs_t *fs,
void *notify_baton,
svn_cancel_func_t cancel,
void *cancel_baton,
+ svn_mutex__t *common_pool_lock,
apr_pool_t *pool,
apr_pool_t *common_pool)
{
@@ -994,7 +1084,7 @@ svn_fs_base__clean_logs(const char *live_path,
{ /* Process unused logs from live area */
int idx;
- apr_pool_t *sub_pool = svn_pool_create(pool);
+ apr_pool_t *subpool = svn_pool_create(pool);
/* Process log files. */
for (idx = 0; idx < logfiles->nelts; idx++)
@@ -1003,9 +1093,9 @@ svn_fs_base__clean_logs(const char *live_path,
const char *live_log_path;
const char *backup_log_path;
- svn_pool_clear(sub_pool);
- live_log_path = svn_dirent_join(live_path, log_file, sub_pool);
- backup_log_path = svn_dirent_join(backup_path, log_file, sub_pool);
+ svn_pool_clear(subpool);
+ live_log_path = svn_dirent_join(live_path, log_file, subpool);
+ backup_log_path = svn_dirent_join(backup_path, log_file, subpool);
{ /* Compare files. No point in using MD5 and wasting CPU cycles as we
got full copies of both logs */
@@ -1022,17 +1112,17 @@ svn_fs_base__clean_logs(const char *live_path,
SVN_ERR(svn_io_files_contents_same_p(&files_match,
live_log_path,
backup_log_path,
- sub_pool));
+ subpool));
/* If log files do not match, go to the next log file. */
if (!files_match)
continue;
}
- SVN_ERR(svn_io_remove_file2(live_log_path, FALSE, sub_pool));
+ SVN_ERR(svn_io_remove_file2(live_log_path, FALSE, subpool));
}
- svn_pool_destroy(sub_pool);
+ svn_pool_destroy(subpool);
}
return SVN_NO_ERROR;
@@ -1201,9 +1291,13 @@ base_hotcopy(svn_fs_t *src_fs,
const char *dest_path,
svn_boolean_t clean_logs,
svn_boolean_t incremental,
+ svn_fs_hotcopy_notify_t notify_func,
+ void *notify_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
- apr_pool_t *pool)
+ svn_mutex__t *common_pool_lock,
+ apr_pool_t *pool,
+ apr_pool_t *common_pool)
{
svn_error_t *err;
u_int32_t pagesize;
@@ -1388,6 +1482,7 @@ base_set_svn_fs_open(svn_fs_t *fs,
svn_error_t *(*svn_fs_open_)(svn_fs_t **,
const char *,
apr_hash_t *,
+ apr_pool_t *,
apr_pool_t *))
{
return SVN_NO_ERROR;
@@ -1409,7 +1504,8 @@ static fs_library_vtable_t library_vtable = {
base_bdb_pack,
base_bdb_logfiles,
svn_fs_base__id_parse,
- base_set_svn_fs_open
+ base_set_svn_fs_open,
+ NULL /* info_fsap_dup */
};
svn_error_t *
@@ -1420,6 +1516,7 @@ svn_fs_base__init(const svn_version_t *loader_version,
{
{ "svn_subr", svn_subr_version },
{ "svn_delta", svn_delta_version },
+ { "svn_fs_util", svn_fs_util__version },
{ NULL, NULL }
};
diff --git a/subversion/libsvn_fs_base/fs.h b/subversion/libsvn_fs_base/fs.h
index 017c898..cc89116 100644
--- a/subversion/libsvn_fs_base/fs.h
+++ b/subversion/libsvn_fs_base/fs.h
@@ -43,7 +43,11 @@ extern "C" {
repository format number, and independent of any other FS back
ends. See the SVN_FS_BASE__MIN_*_FORMAT defines to get a sense of
what changes and features were added in which versions of this
- back-end's format. */
+ back-end's format.
+
+ Note: If you bump this, please update the switch statement in
+ base_create() as well.
+ */
#define SVN_FS_BASE__FORMAT_NUMBER 4
/* Minimum format number that supports representation sharing. This
diff --git a/subversion/libsvn_fs_base/id.c b/subversion/libsvn_fs_base/id.c
index c063d02..a094743 100644
--- a/subversion/libsvn_fs_base/id.c
+++ b/subversion/libsvn_fs_base/id.c
@@ -108,13 +108,14 @@ svn_fs_base__id_check_related(const svn_fs_id_t *a,
}
-int
+svn_fs_node_relation_t
svn_fs_base__id_compare(const svn_fs_id_t *a,
const svn_fs_id_t *b)
{
if (svn_fs_base__id_eq(a, b))
- return 0;
- return (svn_fs_base__id_check_related(a, b) ? 1 : -1);
+ return svn_fs_node_unchanged;
+ return (svn_fs_base__id_check_related(a, b) ? svn_fs_node_common_ancestor
+ : svn_fs_node_unrelated);
}
diff --git a/subversion/libsvn_fs_base/id.h b/subversion/libsvn_fs_base/id.h
index 4cdb45c..9cdc46d 100644
--- a/subversion/libsvn_fs_base/id.h
+++ b/subversion/libsvn_fs_base/id.h
@@ -53,9 +53,9 @@ svn_boolean_t svn_fs_base__id_eq(const svn_fs_id_t *a,
svn_boolean_t svn_fs_base__id_check_related(const svn_fs_id_t *a,
const svn_fs_id_t *b);
-/* Return 0 if A and B are equal, 1 if they are related, -1 otherwise. */
-int svn_fs_base__id_compare(const svn_fs_id_t *a,
- const svn_fs_id_t *b);
+/* Return the noderev relationship between A and B. */
+svn_fs_node_relation_t svn_fs_base__id_compare(const svn_fs_id_t *a,
+ const svn_fs_id_t *b);
/* Create an ID based on NODE_ID, COPY_ID, and TXN_ID, allocated in
POOL. */
diff --git a/subversion/libsvn_fs_base/key-gen.c b/subversion/libsvn_fs_base/key-gen.c
index 411207d..34f0e0f 100644
--- a/subversion/libsvn_fs_base/key-gen.c
+++ b/subversion/libsvn_fs_base/key-gen.c
@@ -39,20 +39,19 @@ void
svn_fs_base__next_key(const char *this, apr_size_t *len, char *next)
{
apr_size_t olen = *len; /* remember the first length */
- int i = olen - 1; /* initial index; we work backwards */
+ apr_size_t i; /* current index */
char c; /* current char */
svn_boolean_t carry = TRUE; /* boolean: do we have a carry or not?
We start with a carry, because we're
incrementing the number, after all. */
- /* Leading zeros are not allowed, except for the string "0". */
- if ((*len > 1) && (this[0] == '0'))
- {
- *len = 0;
- return;
- }
+ /* Empty strings and leading zeros (except for the string "0") are not
+ * allowed. Run our malfunction handler to prevent possible db corruption
+ * from being propagated further. */
+ SVN_ERR_ASSERT_NO_RETURN(olen != 0 && (olen == 1 || this[0] != '0'));
- for (i = (olen - 1); i >= 0; i--)
+ i = olen - 1; /* initial index: we work backwords */
+ while (1729)
{
c = this[i];
@@ -79,6 +78,11 @@ svn_fs_base__next_key(const char *this, apr_size_t *len, char *next)
}
else
next[i] = c;
+
+ if (i == 0)
+ break;
+
+ i--;
}
/* The new length is OLEN, plus 1 if there's a carry out of the
@@ -102,22 +106,6 @@ svn_fs_base__next_key(const char *this, apr_size_t *len, char *next)
}
-int
-svn_fs_base__key_compare(const char *a, const char *b)
-{
- int a_len = strlen(a);
- int b_len = strlen(b);
- int cmp;
-
- if (a_len > b_len)
- return 1;
- if (b_len > a_len)
- return -1;
- cmp = strcmp(a, b);
- return (cmp ? (cmp / abs(cmp)) : 0);
-}
-
-
svn_boolean_t
svn_fs_base__same_keys(const char *a, const char *b)
{
diff --git a/subversion/libsvn_fs_base/key-gen.h b/subversion/libsvn_fs_base/key-gen.h
index e1cd1aa..a0634d3 100644
--- a/subversion/libsvn_fs_base/key-gen.h
+++ b/subversion/libsvn_fs_base/key-gen.h
@@ -78,13 +78,6 @@ extern "C" {
void svn_fs_base__next_key(const char *this, apr_size_t *len, char *next);
-/* Compare two strings A and B as base-36 alphanumeric keys.
- *
- * Return -1, 0, or 1 if A is less than, equal to, or greater than B,
- * respectively.
- */
-int svn_fs_base__key_compare(const char *a, const char *b);
-
/* Compare two strings A and B as base-36 alphanumber keys.
*
* Return TRUE iff both keys are NULL or both keys have the same
diff --git a/subversion/libsvn_fs_base/libsvn_fs_base.pc.in b/subversion/libsvn_fs_base/libsvn_fs_base.pc.in
new file mode 100644
index 0000000..ef44eaf
--- /dev/null
+++ b/subversion/libsvn_fs_base/libsvn_fs_base.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libsvn_fs_base
+Description: Subversion Filesystem Base Library
+Version: @PACKAGE_VERSION@
+Requires: apr-util-@SVN_APR_MAJOR_VERSION@ apr-@SVN_APR_MAJOR_VERSION@
+Requires.private: libsvn_delta libsvn_subr libsvn_fs_util
+Libs: -L${libdir} -lsvn_fs_base @SVN_DB_LIBS@
+Cflags: -I${includedir}
diff --git a/subversion/libsvn_fs_base/lock.c b/subversion/libsvn_fs_base/lock.c
index 79f72cc..1145207 100644
--- a/subversion/libsvn_fs_base/lock.c
+++ b/subversion/libsvn_fs_base/lock.c
@@ -39,6 +39,7 @@
#include "private/svn_fs_util.h"
#include "private/svn_subr_private.h"
#include "private/svn_dep_compat.h"
+#include "revs-txns.h"
/* Add LOCK and its associated LOCK_TOKEN (associated with PATH) as
@@ -70,6 +71,7 @@ delete_lock_and_token(const char *lock_token,
}
+/* The effective arguments for txn_body_lock() below. */
struct lock_args
{
svn_lock_t **lock_p;
@@ -80,9 +82,19 @@ struct lock_args
svn_boolean_t steal_lock;
apr_time_t expiration_date;
svn_revnum_t current_rev;
+ apr_pool_t *result_pool;
};
+/* The body of svn_fs_base__lock(), which see.
+
+ BATON is a 'struct lock_args *' holding the effective arguments.
+ BATON->path is the canonical abspath to lock. Set *BATON->lock_p
+ to the resulting lock. For the other arguments, see
+ svn_fs_lock_many().
+
+ This implements the svn_fs_base__retry_txn() 'body' callback type.
+ */
static svn_error_t *
txn_body_lock(void *baton, trail_t *trail)
{
@@ -91,6 +103,8 @@ txn_body_lock(void *baton, trail_t *trail)
svn_lock_t *existing_lock;
svn_lock_t *lock;
+ *args->lock_p = NULL;
+
SVN_ERR(svn_fs_base__get_path_kind(&kind, args->path, trail, trail->pool));
/* Until we implement directory locks someday, we only allow locks
@@ -194,15 +208,15 @@ txn_body_lock(void *baton, trail_t *trail)
}
/* Create a new lock, and add it to the tables. */
- lock = svn_lock_create(trail->pool);
+ lock = svn_lock_create(args->result_pool);
if (args->token)
- lock->token = apr_pstrdup(trail->pool, args->token);
+ lock->token = apr_pstrdup(args->result_pool, args->token);
else
SVN_ERR(svn_fs_base__generate_lock_token(&(lock->token), trail->fs,
- trail->pool));
- lock->path = apr_pstrdup(trail->pool, args->path);
- lock->owner = apr_pstrdup(trail->pool, trail->fs->access_ctx->username);
- lock->comment = apr_pstrdup(trail->pool, args->comment);
+ args->result_pool));
+ lock->path = args->path; /* Already in result_pool. */
+ lock->owner = apr_pstrdup(args->result_pool, trail->fs->access_ctx->username);
+ lock->comment = apr_pstrdup(args->result_pool, args->comment);
lock->is_dav_comment = args->is_dav_comment;
lock->creation_date = apr_time_now();
lock->expiration_date = args->expiration_date;
@@ -215,31 +229,62 @@ txn_body_lock(void *baton, trail_t *trail)
svn_error_t *
-svn_fs_base__lock(svn_lock_t **lock,
- svn_fs_t *fs,
- const char *path,
- const char *token,
+svn_fs_base__lock(svn_fs_t *fs,
+ apr_hash_t *targets,
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)
+ svn_fs_lock_callback_t lock_callback,
+ void *lock_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- struct lock_args args;
+ apr_hash_index_t *hi;
+ svn_error_t *cb_err = SVN_NO_ERROR;
+ svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_fs__check_fs(fs, TRUE));
+ SVN_ERR(svn_fs_base__youngest_rev(&youngest_rev, fs, scratch_pool));
- args.lock_p = lock;
- args.path = svn_fs__canonicalize_abspath(path, pool);
- args.token = token;
- args.comment = comment;
- args.is_dav_comment = is_dav_comment;
- args.steal_lock = steal_lock;
- args.expiration_date = expiration_date;
- args.current_rev = current_rev;
-
- return svn_fs_base__retry_txn(fs, txn_body_lock, &args, FALSE, pool);
+ for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
+ {
+ struct lock_args args;
+ const char *path = apr_hash_this_key(hi);
+ const svn_fs_lock_target_t *target = apr_hash_this_val(hi);
+ svn_lock_t *lock;
+ svn_error_t *err = NULL;
+
+ svn_pool_clear(iterpool);
+ args.lock_p = &lock;
+ args.path = svn_fs__canonicalize_abspath(path, result_pool);
+ args.token = target->token;
+ args.comment = comment;
+ args.is_dav_comment = is_dav_comment;
+ args.steal_lock = steal_lock;
+ args.expiration_date = expiration_date;
+ args.current_rev = target->current_rev;
+ args.result_pool = result_pool;
+
+ if (SVN_IS_VALID_REVNUM(target->current_rev))
+ {
+ if (target->current_rev > youngest_rev)
+ err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+ _("No such revision %ld"),
+ target->current_rev);
+ }
+
+ if (!err)
+ err = svn_fs_base__retry_txn(fs, txn_body_lock, &args, TRUE,
+ iterpool);
+ if (!cb_err && lock_callback)
+ cb_err = lock_callback(lock_baton, args.path, lock, err, iterpool);
+ svn_error_clear(err);
+ }
+ svn_pool_destroy(iterpool);
+
+ return svn_error_trace(cb_err);
}
@@ -253,11 +298,12 @@ svn_fs_base__generate_lock_token(const char **token,
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:",
- svn_uuid_generate(pool), (char *)NULL);
+ svn_uuid_generate(pool), SVN_VA_NULL);
return SVN_NO_ERROR;
}
+/* The effective arguments for txn_body_unlock() below. */
struct unlock_args
{
const char *path;
@@ -266,6 +312,14 @@ struct unlock_args
};
+/* The body of svn_fs_base__unlock(), which see.
+
+ BATON is a 'struct unlock_args *' holding the effective arguments.
+ BATON->path is the canonical path and BATON->token is the token.
+ For the other arguments, see svn_fs_unlock_many().
+
+ This implements the svn_fs_base__retry_txn() 'body' callback type.
+ */
static svn_error_t *
txn_body_unlock(void *baton, trail_t *trail)
{
@@ -308,19 +362,40 @@ txn_body_unlock(void *baton, trail_t *trail)
svn_error_t *
svn_fs_base__unlock(svn_fs_t *fs,
- const char *path,
- const char *token,
+ apr_hash_t *targets,
svn_boolean_t break_lock,
- apr_pool_t *pool)
+ svn_fs_lock_callback_t lock_callback,
+ void *lock_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- struct unlock_args args;
+ apr_hash_index_t *hi;
+ svn_error_t *cb_err = SVN_NO_ERROR;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
SVN_ERR(svn_fs__check_fs(fs, TRUE));
- args.path = svn_fs__canonicalize_abspath(path, pool);
- args.token = token;
- args.break_lock = break_lock;
- return svn_fs_base__retry_txn(fs, txn_body_unlock, &args, TRUE, pool);
+ for (hi = apr_hash_first(scratch_pool, targets); hi; hi = apr_hash_next(hi))
+ {
+ struct unlock_args args;
+ const char *path = apr_hash_this_key(hi);
+ const char *token = apr_hash_this_val(hi);
+ svn_error_t *err;
+
+ svn_pool_clear(iterpool);
+ args.path = svn_fs__canonicalize_abspath(path, result_pool);
+ args.token = token;
+ args.break_lock = break_lock;
+
+ err = svn_fs_base__retry_txn(fs, txn_body_unlock, &args, TRUE,
+ iterpool);
+ if (!cb_err && lock_callback)
+ cb_err = lock_callback(lock_baton, path, NULL, err, iterpool);
+ svn_error_clear(err);
+ }
+ svn_pool_destroy(iterpool);
+
+ return svn_error_trace(cb_err);
}
@@ -465,8 +540,9 @@ svn_fs_base__get_locks(svn_fs_t *fs,
args.path = svn_fs__canonicalize_abspath(path, pool);
args.depth = depth;
/* Enough for 100+ locks if the comments are small. */
- args.stream = svn_stream__from_spillbuf(4 * 1024 /* blocksize */,
- 64 * 1024 /* maxsize */,
+ args.stream = svn_stream__from_spillbuf(svn_spillbuf__create(4 * 1024 /* blocksize */,
+ 64 * 1024 /* maxsize */,
+ pool),
pool);
SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_locks, &args, FALSE, pool));
@@ -495,12 +571,12 @@ svn_fs_base__get_locks(svn_fs_t *fs,
/* Now read that much into a buffer. */
skel_buf = apr_palloc(pool, skel_len + 1);
- SVN_ERR(svn_stream_read(stream, skel_buf, &skel_len));
+ SVN_ERR(svn_stream_read_full(stream, skel_buf, &skel_len));
skel_buf[skel_len] = '\0';
/* Read the extra newline that follows the skel. */
len = 1;
- SVN_ERR(svn_stream_read(stream, &c, &len));
+ SVN_ERR(svn_stream_read_full(stream, &c, &len));
if (c != '\n')
return svn_error_create(SVN_ERR_MALFORMED_FILE, NULL, NULL);
diff --git a/subversion/libsvn_fs_base/lock.h b/subversion/libsvn_fs_base/lock.h
index 603e78c..3a17ce0 100644
--- a/subversion/libsvn_fs_base/lock.h
+++ b/subversion/libsvn_fs_base/lock.h
@@ -33,34 +33,41 @@ extern "C" {
/* These functions implement part of the FS loader library's fs
- vtables. See the public svn_fs.h for docstrings.*/
+ vtables. */
-svn_error_t *svn_fs_base__lock(svn_lock_t **lock,
- svn_fs_t *fs,
- const char *path,
- const char *token,
+/* See svn_fs_lock(), svn_fs_lock_many(). */
+svn_error_t *svn_fs_base__lock(svn_fs_t *fs,
+ apr_hash_t *targets,
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);
+ svn_fs_lock_callback_t lock_callback,
+ void *lock_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+/* See svn_fs_generate_lock_token(). */
svn_error_t *svn_fs_base__generate_lock_token(const char **token,
svn_fs_t *fs,
apr_pool_t *pool);
+/* See svn_fs_unlock(), svn_fs_unlock_many(). */
svn_error_t *svn_fs_base__unlock(svn_fs_t *fs,
- const char *path,
- const char *token,
+ apr_hash_t *targets,
svn_boolean_t break_lock,
- apr_pool_t *pool);
+ svn_fs_lock_callback_t lock_callback,
+ void *lock_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+/* See svn_fs_get_lock(). */
svn_error_t *svn_fs_base__get_lock(svn_lock_t **lock,
svn_fs_t *fs,
const char *path,
apr_pool_t *pool);
+/* See svn_fs_get_locks2(). */
svn_error_t *
svn_fs_base__get_locks(svn_fs_t *fs,
const char *path,
diff --git a/subversion/libsvn_fs_base/reps-strings.c b/subversion/libsvn_fs_base/reps-strings.c
index 553075d..e88583a 100644
--- a/subversion/libsvn_fs_base/reps-strings.c
+++ b/subversion/libsvn_fs_base/reps-strings.c
@@ -920,7 +920,7 @@ txn_body_read_rep(void *baton, trail_t *trail)
args->rb->md5_checksum)))
return svn_error_create(SVN_ERR_FS_CORRUPT,
svn_checksum_mismatch_err(rep->md5_checksum,
- args->rb->sha1_checksum, trail->pool,
+ args->rb->md5_checksum, trail->pool,
_("MD5 checksum mismatch on representation '%s'"),
args->rb->rep_key),
NULL);
@@ -1224,7 +1224,8 @@ svn_fs_base__rep_contents_read_stream(svn_stream_t **rs_p,
SVN_ERR(rep_read_get_baton(&rb, fs, rep_key, use_trail_for_reads,
trail, pool));
*rs_p = svn_stream_create(rb, pool);
- svn_stream_set_read(*rs_p, rep_read_contents);
+ svn_stream_set_read2(*rs_p, NULL /* only full read support */,
+ rep_read_contents);
return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_fs_base/revs-txns.c b/subversion/libsvn_fs_base/revs-txns.c
index d218843..f1029f4 100644
--- a/subversion/libsvn_fs_base/revs-txns.c
+++ b/subversion/libsvn_fs_base/revs-txns.c
@@ -574,6 +574,10 @@ svn_fs_base__set_txn_prop(svn_fs_t *fs,
txn->proplist = apr_hash_make(pool);
/* Set the property. */
+ if (svn_hash_gets(txn->proplist, SVN_FS__PROP_TXN_CLIENT_DATE)
+ && !strcmp(name, SVN_PROP_REVISION_DATE))
+ svn_hash_sets(txn->proplist, SVN_FS__PROP_TXN_CLIENT_DATE,
+ svn_string_create("1", pool));
svn_hash_sets(txn->proplist, name, value);
/* Now overwrite the transaction. */
@@ -707,6 +711,34 @@ txn_body_begin_txn(void *baton, trail_t *trail)
SVN_ERR(txn_body_change_txn_prop(&cpargs, trail));
}
+ /* Put a datestamp on the newly created txn, so we always know
+ exactly how old it is. (This will help sysadmins identify
+ long-abandoned txns that may need to be manually removed.) Do
+ this before setting CLIENT_DATE so that it is not recorded as an
+ explicit setting. */
+ {
+ struct change_txn_prop_args cpargs;
+ svn_string_t date;
+ cpargs.fs = trail->fs;
+ cpargs.id = txn_id;
+ cpargs.name = SVN_PROP_REVISION_DATE;
+ date.data = svn_time_to_cstring(apr_time_now(), trail->pool);
+ date.len = strlen(date.data);
+ cpargs.value = &date;
+ SVN_ERR(txn_body_change_txn_prop(&cpargs, trail));
+ }
+
+ if (args->flags & SVN_FS_TXN_CLIENT_DATE)
+ {
+ struct change_txn_prop_args cpargs;
+ cpargs.fs = trail->fs;
+ cpargs.id = txn_id;
+ cpargs.name = SVN_FS__PROP_TXN_CLIENT_DATE;
+ cpargs.value = svn_string_create("0", trail->pool);
+
+ SVN_ERR(txn_body_change_txn_prop(&cpargs, trail));
+ }
+
*args->txn_p = make_txn(trail->fs, txn_id, args->base_rev, trail->pool);
return SVN_NO_ERROR;
}
@@ -722,7 +754,6 @@ svn_fs_base__begin_txn(svn_fs_txn_t **txn_p,
{
svn_fs_txn_t *txn;
struct begin_txn_args args;
- svn_string_t date;
SVN_ERR(svn_fs__check_fs(fs, TRUE));
@@ -733,15 +764,7 @@ svn_fs_base__begin_txn(svn_fs_txn_t **txn_p,
*txn_p = txn;
- /* Put a datestamp on the newly created txn, so we always know
- exactly how old it is. (This will help sysadmins identify
- long-abandoned txns that may need to be manually removed.) When
- a txn is promoted to a revision, this property will be
- automatically overwritten with a revision datestamp. */
- date.data = svn_time_to_cstring(apr_time_now(), pool);
- date.len = strlen(date.data);
- return svn_fs_base__change_txn_prop(txn, SVN_PROP_REVISION_DATE,
- &date, pool);
+ return SVN_NO_ERROR;
}
@@ -897,7 +920,7 @@ delete_txn_tree(svn_fs_t *fs,
svn_error_t *err;
/* If this sucker isn't mutable, there's nothing to do. */
- if (svn_fs_base__key_compare(svn_fs_base__id_txn_id(id), txn_id) != 0)
+ if (strcmp(svn_fs_base__id_txn_id(id), txn_id) != 0)
return SVN_NO_ERROR;
/* See if the thing has dirents that need to be recursed upon. If
diff --git a/subversion/libsvn_fs_base/tree.c b/subversion/libsvn_fs_base/tree.c
index 046ab5d..924e7c9 100644
--- a/subversion/libsvn_fs_base/tree.c
+++ b/subversion/libsvn_fs_base/tree.c
@@ -543,7 +543,7 @@ get_copy_inheritance(copy_id_inherit_t *inherit_p,
parent_copy_id = svn_fs_base__id_copy_id(parent_id);
/* Easy out: if this child is already mutable, we have nothing to do. */
- if (svn_fs_base__key_compare(svn_fs_base__id_txn_id(child_id), txn_id) == 0)
+ if (strcmp(svn_fs_base__id_txn_id(child_id), txn_id) == 0)
return SVN_NO_ERROR;
/* If the child and its parent are on the same branch, then the
@@ -560,7 +560,7 @@ get_copy_inheritance(copy_id_inherit_t *inherit_p,
target of any copy, and therefore must be on the same branch as
its parent. */
if ((strcmp(child_copy_id, "0") == 0)
- || (svn_fs_base__key_compare(child_copy_id, parent_copy_id) == 0))
+ || (strcmp(child_copy_id, parent_copy_id) == 0))
{
*inherit_p = copy_id_inherit_parent;
return SVN_NO_ERROR;
@@ -569,7 +569,8 @@ get_copy_inheritance(copy_id_inherit_t *inherit_p,
{
copy_t *copy;
SVN_ERR(svn_fs_bdb__get_copy(&copy, fs, child_copy_id, trail, pool));
- if (svn_fs_base__id_compare(copy->dst_noderev_id, child_id) == -1)
+ if ( svn_fs_base__id_compare(copy->dst_noderev_id, child_id)
+ == svn_fs_node_unrelated)
{
*inherit_p = copy_id_inherit_parent;
return SVN_NO_ERROR;
@@ -1027,6 +1028,30 @@ base_node_id(const svn_fs_id_t **id_p,
return SVN_NO_ERROR;
}
+static svn_error_t *
+base_node_relation(svn_fs_node_relation_t *relation,
+ svn_fs_root_t *root_a, const char *path_a,
+ svn_fs_root_t *root_b, const char *path_b,
+ apr_pool_t *pool)
+{
+ const svn_fs_id_t *id_a, *id_b;
+
+ /* Paths from different repository are never related. */
+ if (root_a->fs != root_b->fs)
+ {
+ *relation = svn_fs_node_unrelated;
+ return SVN_NO_ERROR;
+ }
+
+ /* Naive implementation. */
+ SVN_ERR(base_node_id(&id_a, root_a, path_a, pool));
+ SVN_ERR(base_node_id(&id_b, root_b, path_b, pool));
+
+ *relation = svn_fs_base__id_compare(id_a, id_b);
+
+ return SVN_NO_ERROR;
+}
+
struct node_created_rev_args {
svn_revnum_t revision;
@@ -1212,7 +1237,7 @@ base_node_prop(svn_string_t **value_p,
args.propname = propname;
SVN_ERR(svn_fs_base__retry_txn(root->fs, txn_body_node_prop, &args,
FALSE, scratch_pool));
- *value_p = value ? svn_string_dup(value, pool) : NULL;
+ *value_p = svn_string_dup(value, pool);
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
@@ -1260,6 +1285,21 @@ base_node_proplist(apr_hash_t **table_p,
return SVN_NO_ERROR;
}
+static svn_error_t *
+base_node_has_props(svn_boolean_t *has_props,
+ svn_fs_root_t *root,
+ const char *path,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_t *props;
+
+ SVN_ERR(base_node_proplist(&props, root, path, scratch_pool));
+
+ *has_props = (0 < apr_hash_count(props));
+
+ return SVN_NO_ERROR;
+}
+
struct change_node_prop_args {
svn_fs_root_t *root;
@@ -1369,6 +1409,7 @@ struct things_changed_args
svn_fs_root_t *root2;
const char *path1;
const char *path2;
+ svn_boolean_t strict;
apr_pool_t *pool;
};
@@ -1378,11 +1419,26 @@ txn_body_props_changed(void *baton, trail_t *trail)
{
struct things_changed_args *args = baton;
dag_node_t *node1, *node2;
+ apr_hash_t *proplist1, *proplist2;
SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
- return svn_fs_base__things_different(args->changed_p, NULL,
- node1, node2, trail, trail->pool);
+ SVN_ERR(svn_fs_base__things_different(args->changed_p, NULL,
+ node1, node2, trail, trail->pool));
+
+ /* Is there a potential false positive and do we want to correct it? */
+ if (!args->strict || !*args->changed_p)
+ return SVN_NO_ERROR;
+
+ /* Different representations. They might still have equal contents. */
+ SVN_ERR(svn_fs_base__dag_get_proplist(&proplist1, node1,
+ trail, trail->pool));
+ SVN_ERR(svn_fs_base__dag_get_proplist(&proplist2, node2,
+ trail, trail->pool));
+
+ *args->changed_p = !svn_fs__prop_lists_equal(proplist1, proplist2,
+ trail->pool);
+ return SVN_NO_ERROR;
}
@@ -1392,6 +1448,7 @@ base_props_changed(svn_boolean_t *changed_p,
const char *path1,
svn_fs_root_t *root2,
const char *path2,
+ svn_boolean_t strict,
apr_pool_t *pool)
{
struct things_changed_args args;
@@ -1408,6 +1465,7 @@ base_props_changed(svn_boolean_t *changed_p,
args.path2 = path2;
args.changed_p = changed_p;
args.pool = pool;
+ args.strict = strict;
return svn_fs_base__retry_txn(root1->fs, txn_body_props_changed, &args,
TRUE, pool);
@@ -1561,6 +1619,25 @@ base_dir_entries(apr_hash_t **table_p,
return SVN_NO_ERROR;
}
+static svn_error_t *
+base_dir_optimal_order(apr_array_header_t **ordered_p,
+ svn_fs_root_t *root,
+ apr_hash_t *entries,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ /* 1:1 copy of entries with no differnce in ordering */
+ apr_hash_index_t *hi;
+ apr_array_header_t *result
+ = apr_array_make(result_pool, apr_hash_count(entries),
+ sizeof(svn_fs_dirent_t *));
+ for (hi = apr_hash_first(scratch_pool, entries); hi; hi = apr_hash_next(hi))
+ APR_ARRAY_PUSH(result, svn_fs_dirent_t *) = apr_hash_this_val(hi);
+
+ *ordered_p = result;
+ return SVN_NO_ERROR;
+}
+
/* Merges and commits. */
@@ -3104,7 +3181,8 @@ txn_body_copy(void *baton,
if ((to_parent_path->node)
&& (svn_fs_base__id_compare(svn_fs_base__dag_get_id(from_node),
svn_fs_base__dag_get_id
- (to_parent_path->node)) == 0))
+ (to_parent_path->node))
+ == svn_fs_node_unchanged))
return SVN_NO_ERROR;
if (! from_root->is_txn_root)
@@ -3292,8 +3370,8 @@ txn_body_copied_from(void *baton, trail_t *trail)
return SVN_NO_ERROR;
/* If NODE's copy-ID is the same as that of its predecessor... */
- if (svn_fs_base__key_compare(svn_fs_base__id_copy_id(node_id),
- svn_fs_base__id_copy_id(pred_id)) != 0)
+ if (strcmp(svn_fs_base__id_copy_id(node_id),
+ svn_fs_base__id_copy_id(pred_id)) != 0)
{
/* ... then NODE was either the target of a copy operation,
a copied subtree item. We examine the actual copy record
@@ -3942,11 +4020,53 @@ txn_body_contents_changed(void *baton, trail_t *trail)
{
struct things_changed_args *args = baton;
dag_node_t *node1, *node2;
+ svn_checksum_t *checksum1, *checksum2;
+ svn_stream_t *stream1, *stream2;
+ svn_boolean_t same;
SVN_ERR(get_dag(&node1, args->root1, args->path1, trail, trail->pool));
SVN_ERR(get_dag(&node2, args->root2, args->path2, trail, trail->pool));
- return svn_fs_base__things_different(NULL, args->changed_p,
- node1, node2, trail, trail->pool);
+ SVN_ERR(svn_fs_base__things_different(NULL, args->changed_p,
+ node1, node2, trail, trail->pool));
+
+ /* Is there a potential false positive and do we want to correct it? */
+ if (!args->strict || !*args->changed_p)
+ return SVN_NO_ERROR;
+
+ /* Different representations. They might still have equal contents. */
+
+ /* Compare MD5 checksums. These should be readily accessible. */
+ SVN_ERR(svn_fs_base__dag_file_checksum(&checksum1, svn_checksum_md5,
+ node1, trail, trail->pool));
+ SVN_ERR(svn_fs_base__dag_file_checksum(&checksum2, svn_checksum_md5,
+ node2, trail, trail->pool));
+
+ /* Different MD5 checksums -> different contents */
+ if (!svn_checksum_match(checksum1, checksum2))
+ return SVN_NO_ERROR;
+
+ /* Paranoia. Compare SHA1 checksums because that's the level of
+ confidence we require for e.g. the working copy. */
+ SVN_ERR(svn_fs_base__dag_file_checksum(&checksum1, svn_checksum_sha1,
+ node1, trail, trail->pool));
+ SVN_ERR(svn_fs_base__dag_file_checksum(&checksum2, svn_checksum_sha1,
+ node2, trail, trail->pool));
+
+ /* Different SHA1 checksums -> different contents */
+ if (checksum1 && checksum2)
+ {
+ *args->changed_p = !svn_checksum_match(checksum1, checksum2);
+ return SVN_NO_ERROR;
+ }
+
+ /* SHA1 checksums are not available for very old reps / repositories. */
+ SVN_ERR(svn_fs_base__dag_get_contents(&stream1, node1, trail, trail->pool));
+ SVN_ERR(svn_fs_base__dag_get_contents(&stream2, node2, trail, trail->pool));
+ SVN_ERR(svn_stream_contents_same2(&same, stream1, stream2, trail->pool));
+
+ /* Now, it's definitive. */
+ *args->changed_p = !same;
+ return SVN_NO_ERROR;
}
@@ -3958,6 +4078,7 @@ base_contents_changed(svn_boolean_t *changed_p,
const char *path1,
svn_fs_root_t *root2,
const char *path2,
+ svn_boolean_t strict,
apr_pool_t *pool)
{
struct things_changed_args args;
@@ -3989,6 +4110,7 @@ base_contents_changed(svn_boolean_t *changed_p,
args.path2 = path2;
args.changed_p = changed_p;
args.pool = pool;
+ args.strict = strict;
return svn_fs_base__retry_txn(root1->fs, txn_body_contents_changed, &args,
TRUE, pool);
@@ -4108,7 +4230,8 @@ static svn_error_t *
base_node_history(svn_fs_history_t **history_p,
svn_fs_root_t *root,
const char *path,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
svn_node_kind_t kind;
@@ -4117,15 +4240,16 @@ base_node_history(svn_fs_history_t **history_p,
return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
/* And we require that the path exist in the root. */
- SVN_ERR(base_check_path(&kind, root, path, pool));
+ SVN_ERR(base_check_path(&kind, root, path, scratch_pool));
if (kind == svn_node_none)
return SVN_FS__NOT_FOUND(root, path);
/* Okay, all seems well. Build our history object and return it. */
*history_p = assemble_history(root->fs,
- svn_fs__canonicalize_abspath(path, pool),
+ svn_fs__canonicalize_abspath(path,
+ result_pool),
root->rev, FALSE, NULL,
- SVN_INVALID_REVNUM, pool);
+ SVN_INVALID_REVNUM, result_pool);
return SVN_NO_ERROR;
}
@@ -4298,8 +4422,7 @@ txn_body_history_prev(void *baton, trail_t *trail)
(which is either a real predecessor, or is the node itself
playing the predecessor role to an imaginary mutable successor),
then we need to report a copy. */
- if (svn_fs_base__key_compare(svn_fs_base__id_copy_id(node_id),
- end_copy_id) != 0)
+ if (strcmp(svn_fs_base__id_copy_id(node_id), end_copy_id) != 0)
{
const char *remainder;
dag_node_t *dst_node;
@@ -4376,7 +4499,8 @@ static svn_error_t *
base_history_prev(svn_fs_history_t **prev_history_p,
svn_fs_history_t *history,
svn_boolean_t cross_copies,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
svn_fs_history_t *prev_history = NULL;
base_history_data_t *bhd = history->fsap_data;
@@ -4390,10 +4514,12 @@ base_history_prev(svn_fs_history_t **prev_history_p,
{
if (! bhd->is_interesting)
prev_history = assemble_history(fs, "/", bhd->revision,
- 1, NULL, SVN_INVALID_REVNUM, pool);
+ 1, NULL, SVN_INVALID_REVNUM,
+ result_pool);
else if (bhd->revision > 0)
prev_history = assemble_history(fs, "/", bhd->revision - 1,
- 1, NULL, SVN_INVALID_REVNUM, pool);
+ 1, NULL, SVN_INVALID_REVNUM,
+ result_pool);
}
else
{
@@ -4407,9 +4533,9 @@ base_history_prev(svn_fs_history_t **prev_history_p,
args.prev_history_p = &prev_history;
args.history = prev_history;
args.cross_copies = cross_copies;
- args.pool = pool;
+ args.pool = result_pool;
SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_history_prev, &args,
- FALSE, pool));
+ FALSE, result_pool));
if (! prev_history)
break;
bhd = prev_history->fsap_data;
@@ -5370,20 +5496,23 @@ static root_vtable_t root_vtable = {
base_check_path,
base_node_history,
base_node_id,
+ base_node_relation,
base_node_created_rev,
base_node_origin_rev,
base_node_created_path,
base_delete_node,
+ base_copy,
+ base_revision_link,
base_copied_from,
base_closest_copy,
base_node_prop,
base_node_proplist,
+ base_node_has_props,
base_change_node_prop,
base_props_changed,
base_dir_entries,
+ base_dir_optimal_order,
base_make_dir,
- base_copy,
- base_revision_link,
base_file_length,
base_file_checksum,
base_file_contents,