diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/libsvn_fs_base | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-cf46733632c7279a9fd0fe6ce26f9185a4ae82a9.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/libsvn_fs_base')
-rw-r--r-- | subversion/libsvn_fs_base/bdb/changes-table.c | 81 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/bdb/locks-table.c | 2 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/bdb/strings-table.c | 4 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/dag.c | 29 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/dag.h | 2 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/fs.c | 149 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/fs.h | 6 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/id.c | 7 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/id.h | 6 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/key-gen.c | 36 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/key-gen.h | 7 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/libsvn_fs_base.pc.in | 12 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/lock.c | 148 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/lock.h | 27 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/reps-strings.c | 5 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/revs-txns.c | 45 | ||||
-rw-r--r-- | subversion/libsvn_fs_base/tree.c | 177 |
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(©, 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, |