You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by gb...@apache.org on 2013/12/08 18:56:53 UTC
svn commit: r1549081 [5/18] - in
/subversion/branches/invoke-diff-cmd-feature: ./ build/ build/ac-macros/
build/generator/ build/generator/templates/ contrib/server-side/
contrib/server-side/svncutter/ subversion/bindings/javahl/native/
subversion/bind...
Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/cat.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/cat.c?rev=1549081&r1=1549080&r2=1549081&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/cat.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/cat.c Sun Dec 8 17:56:46 2013
@@ -176,22 +176,28 @@ svn_client__get_normalized_stream(svn_st
}
svn_error_t *
-svn_client_cat2(svn_stream_t *out,
+svn_client_cat3(apr_hash_t **props,
+ svn_stream_t *out,
const char *path_or_url,
const svn_opt_revision_t *peg_revision,
const svn_opt_revision_t *revision,
+ svn_boolean_t expand_keywords,
svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
svn_ra_session_t *ra_session;
svn_client__pathrev_t *loc;
svn_string_t *eol_style;
svn_string_t *keywords;
- apr_hash_t *props;
+ apr_hash_t *pprops = NULL;
const char *repos_root_url;
svn_stream_t *output = out;
svn_error_t *err;
+ if (!props)
+ props = &pprops;
+
/* ### Inconsistent default revision logic in this command. */
if (peg_revision->kind == svn_opt_revision_unspecified)
{
@@ -213,32 +219,39 @@ svn_client_cat2(svn_stream_t *out,
const char *local_abspath;
svn_stream_t *normal_stream;
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url, pool));
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url,
+ scratch_pool));
SVN_ERR(svn_client__get_normalized_stream(&normal_stream, ctx->wc_ctx,
- local_abspath, revision, TRUE, FALSE,
+ local_abspath, revision,
+ expand_keywords, FALSE,
ctx->cancel_func, ctx->cancel_baton,
- pool, pool));
+ scratch_pool, scratch_pool));
/* We don't promise to close output, so disown it to ensure we don't. */
- output = svn_stream_disown(output, pool);
+ output = svn_stream_disown(output, scratch_pool);
+
+ if (props)
+ SVN_ERR(svn_wc_prop_list2(props, ctx->wc_ctx, local_abspath,
+ result_pool, scratch_pool));
return svn_error_trace(svn_stream_copy3(normal_stream, output,
ctx->cancel_func,
- ctx->cancel_baton, pool));
+ ctx->cancel_baton, scratch_pool));
}
/* Get an RA plugin for this filesystem object. */
SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
path_or_url, NULL,
peg_revision,
- revision, ctx, pool));
+ revision, ctx, scratch_pool));
/* Find the repos root URL */
- SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool));
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, scratch_pool));
/* Grab some properties we need to know in order to figure out if anything
special needs to be done with this file. */
- err = svn_ra_get_file(ra_session, "", loc->rev, NULL, NULL, &props, pool);
+ err = svn_ra_get_file(ra_session, "", loc->rev, NULL, NULL, props,
+ result_pool);
if (err)
{
if (err->apr_err == SVN_ERR_FS_NOT_FILE)
@@ -253,8 +266,8 @@ svn_client_cat2(svn_stream_t *out,
}
}
- eol_style = svn_hash_gets(props, SVN_PROP_EOL_STYLE);
- keywords = svn_hash_gets(props, SVN_PROP_KEYWORDS);
+ eol_style = svn_hash_gets(*props, SVN_PROP_EOL_STYLE);
+ keywords = svn_hash_gets(*props, SVN_PROP_KEYWORDS);
if (eol_style || keywords)
{
@@ -272,33 +285,36 @@ svn_client_cat2(svn_stream_t *out,
}
- if (keywords)
+ if (keywords && expand_keywords)
{
svn_string_t *cmt_rev, *cmt_date, *cmt_author;
apr_time_t when = 0;
- cmt_rev = svn_hash_gets(props, SVN_PROP_ENTRY_COMMITTED_REV);
- cmt_date = svn_hash_gets(props, SVN_PROP_ENTRY_COMMITTED_DATE);
- cmt_author = svn_hash_gets(props, SVN_PROP_ENTRY_LAST_AUTHOR);
+ cmt_rev = svn_hash_gets(*props, SVN_PROP_ENTRY_COMMITTED_REV);
+ cmt_date = svn_hash_gets(*props, SVN_PROP_ENTRY_COMMITTED_DATE);
+ cmt_author = svn_hash_gets(*props, SVN_PROP_ENTRY_LAST_AUTHOR);
if (cmt_date)
- SVN_ERR(svn_time_from_cstring(&when, cmt_date->data, pool));
+ SVN_ERR(svn_time_from_cstring(&when, cmt_date->data, scratch_pool));
SVN_ERR(svn_subst_build_keywords3(&kw, keywords->data,
cmt_rev->data, loc->url,
repos_root_url, when,
cmt_author ?
cmt_author->data : NULL,
- pool));
+ scratch_pool));
}
else
kw = NULL;
/* Interject a translating stream */
- output = svn_subst_stream_translated(svn_stream_disown(out, pool),
- eol_str, FALSE, kw, TRUE, pool);
+ output = svn_subst_stream_translated(svn_stream_disown(out,
+ scratch_pool),
+ eol_str, FALSE, kw, TRUE,
+ scratch_pool);
}
- SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev, output, NULL, NULL, pool));
+ SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev, output, NULL, NULL,
+ scratch_pool));
if (out != output)
/* Close the interjected stream */
Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/cleanup.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/cleanup.c?rev=1549081&r1=1549080&r2=1549081&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/cleanup.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/cleanup.c Sun Dec 8 17:56:46 2013
@@ -46,9 +46,13 @@
struct cleanup_status_walk_baton
{
- svn_boolean_t include_externals;
+ svn_boolean_t break_locks;
+ svn_boolean_t fix_timestamps;
+ svn_boolean_t clear_dav_cache;
+ svn_boolean_t vacuum_pristines;
svn_boolean_t remove_unversioned_items;
svn_boolean_t remove_ignored_items;
+ svn_boolean_t include_externals;
svn_client_ctx_t *ctx;
};
@@ -61,64 +65,46 @@ cleanup_status_walk(void *baton,
static svn_error_t *
do_cleanup(const char *local_abspath,
- svn_boolean_t include_externals,
+ svn_boolean_t break_locks,
+ svn_boolean_t fix_timestamps,
+ svn_boolean_t clear_dav_cache,
+ svn_boolean_t vacuum_pristines,
svn_boolean_t remove_unversioned_items,
svn_boolean_t remove_ignored_items,
+ svn_boolean_t include_externals,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
- svn_error_t *err;
-
- if (remove_unversioned_items || remove_ignored_items)
- {
- svn_boolean_t is_locked_here;
- svn_boolean_t is_locked;
- svn_boolean_t sqlite_exclusive;
- svn_config_t *cfg = ctx->config
- ? svn_hash_gets(ctx->config,
- SVN_CONFIG_CATEGORY_CONFIG)
- : NULL;
-
- /* Check if someone else owns a lock for LOCAL_ABSPATH. */
- SVN_ERR(svn_wc_locked2(&is_locked_here, &is_locked, ctx->wc_ctx,
- local_abspath, scratch_pool));
- if (is_locked && !is_locked_here)
- return svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
- _("Working copy at '%s' is already locked."),
- svn_dirent_local_style(local_abspath,
- scratch_pool));
-
- SVN_ERR(svn_config_get_bool(cfg, &sqlite_exclusive,
- SVN_CONFIG_SECTION_WORKING_COPY,
- SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
- FALSE));
- if (sqlite_exclusive)
- {
- /* Close the db because svn_wc_cleanup3() will try to open it again,
- * which doesn't work if exclusive sqlite locking mode is enabled. */
- SVN_ERR(svn_wc__close_db(local_abspath, ctx->wc_ctx, scratch_pool));
- }
- }
+ SVN_ERR(svn_wc_cleanup4(ctx->wc_ctx,
+ local_abspath,
+ break_locks,
+ fix_timestamps,
+ clear_dav_cache,
+ vacuum_pristines,
+ ctx->cancel_func, ctx->cancel_baton,
+ scratch_pool));
- err = svn_wc_cleanup3(ctx->wc_ctx, local_abspath, ctx->cancel_func,
- ctx->cancel_baton, scratch_pool);
- svn_io_sleep_for_timestamps(local_abspath, scratch_pool);
- if (err)
- return svn_error_trace(err);
+ if (fix_timestamps)
+ svn_io_sleep_for_timestamps(local_abspath, scratch_pool);
if (remove_unversioned_items || remove_ignored_items || include_externals)
{
struct cleanup_status_walk_baton b;
apr_array_header_t *ignores;
- b.include_externals = include_externals;
+ b.break_locks = break_locks;
+ b.fix_timestamps = fix_timestamps;
+ b.clear_dav_cache = clear_dav_cache;
+ b.vacuum_pristines = vacuum_pristines;
b.remove_unversioned_items = remove_unversioned_items;
b.remove_ignored_items = remove_ignored_items;
b.include_externals = include_externals;
b.ctx = ctx;
SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, scratch_pool));
- SVN_ERR(svn_wc_walk_status(ctx->wc_ctx, local_abspath,
+
+ SVN_WC__CALL_WITH_WRITE_LOCK(
+ svn_wc_walk_status(ctx->wc_ctx, local_abspath,
svn_depth_infinity,
TRUE, /* get all */
remove_ignored_items,
@@ -127,7 +113,11 @@ do_cleanup(const char *local_abspath,
cleanup_status_walk, &b,
ctx->cancel_func,
ctx->cancel_baton,
- scratch_pool));
+ scratch_pool),
+ ctx->wc_ctx,
+ local_abspath,
+ FALSE /* lock_anchor */,
+ scratch_pool);
}
return SVN_NO_ERROR;
@@ -161,10 +151,15 @@ cleanup_status_walk(void *baton,
scratch_pool);
}
- err = do_cleanup(local_abspath, b->include_externals,
- b->remove_unversioned_items,
- b->remove_ignored_items,
- b->ctx, scratch_pool);
+ err = do_cleanup(local_abspath,
+ b->break_locks,
+ b->fix_timestamps,
+ b->clear_dav_cache,
+ b->vacuum_pristines,
+ b->remove_unversioned_items,
+ b->remove_ignored_items,
+ TRUE /* include_externals */,
+ b->ctx, scratch_pool);
if (err && err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY)
{
svn_error_clear(err);
@@ -219,22 +214,50 @@ cleanup_status_walk(void *baton,
}
svn_error_t *
-svn_client_cleanup2(const char *path,
+svn_client_cleanup2(const char *dir_abspath,
+ svn_boolean_t break_locks,
+ svn_boolean_t fix_recorded_timestamps,
+ svn_boolean_t clear_dav_cache,
+ svn_boolean_t vacuum_pristines,
svn_boolean_t include_externals,
- svn_boolean_t remove_unversioned_items,
- svn_boolean_t remove_ignored_items,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
- const char *local_abspath;
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
+
+ SVN_ERR(do_cleanup(dir_abspath,
+ break_locks,
+ fix_recorded_timestamps,
+ clear_dav_cache,
+ vacuum_pristines,
+ FALSE /* remove_unversioned_items */,
+ FALSE /* remove_ignored_items */,
+ include_externals,
+ ctx, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_vacuum(const char *dir_abspath,
+ svn_boolean_t remove_unversioned_items,
+ svn_boolean_t remove_ignored_items,
+ svn_boolean_t fix_recorded_timestamps,
+ svn_boolean_t vacuum_pristines,
+ svn_boolean_t include_externals,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
- if (svn_path_is_url(path))
- return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
- _("'%s' is not a local path"), path);
-
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
- SVN_ERR(do_cleanup(local_abspath, include_externals,
- remove_unversioned_items, remove_ignored_items,
+ SVN_ERR(do_cleanup(dir_abspath,
+ FALSE /* break_locks */,
+ fix_recorded_timestamps,
+ FALSE /* clear_dav_cache */,
+ vacuum_pristines,
+ remove_unversioned_items,
+ remove_ignored_items,
+ include_externals,
ctx, scratch_pool));
return SVN_NO_ERROR;
Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/deprecated.c?rev=1549081&r1=1549080&r2=1549081&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/deprecated.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/deprecated.c Sun Dec 8 17:56:46 2013
@@ -2555,6 +2555,21 @@ svn_client_switch(svn_revnum_t *result_r
/*** From cat.c ***/
svn_error_t *
+svn_client_cat2(svn_stream_t *out,
+ const char *path_or_url,
+ const svn_opt_revision_t *peg_revision,
+ const svn_opt_revision_t *revision,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ return svn_client_cat3(NULL /* props */,
+ out, path_or_url, peg_revision, revision,
+ TRUE /* expand_keywords */,
+ ctx, pool, pool);
+}
+
+
+svn_error_t *
svn_client_cat(svn_stream_t *out,
const char *path_or_url,
const svn_opt_revision_t *revision,
@@ -3085,6 +3100,19 @@ svn_client_cleanup(const char *path,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
- return svn_error_trace(svn_client_cleanup2(path, FALSE, FALSE, FALSE, ctx,
- scratch_pool));
+ const char *local_abspath;
+
+ if (svn_path_is_url(path))
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("'%s' is not a local path"), path);
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
+
+ return svn_error_trace(svn_client_cleanup2(local_abspath,
+ TRUE /* break_locks */,
+ TRUE /* fix_recorded_timestamps */,
+ TRUE /* clear_dav_cache */,
+ TRUE /* vacuum_pristines */,
+ FALSE /* include_externals */,
+ ctx, scratch_pool));
}
Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/merge.c?rev=1549081&r1=1549080&r2=1549081&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/merge.c Sun Dec 8 17:56:46 2013
@@ -12681,7 +12681,8 @@ svn_client_get_merging_summary(svn_boole
target_is_wc = (! svn_path_is_url(target_path_or_url))
&& (target_revision->kind == svn_opt_revision_unspecified
- || target_revision->kind == svn_opt_revision_working);
+ || target_revision->kind == svn_opt_revision_working
+ || target_revision->kind == svn_opt_revision_base);
if (target_is_wc)
{
const char *target_abspath;
Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/update.c?rev=1549081&r1=1549080&r2=1549081&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/update.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_client/update.c Sun Dec 8 17:56:46 2013
@@ -383,7 +383,7 @@ update_internal(svn_revnum_t *result_rev
SVN_ERR(svn_ra_get_repos_root2(ra_session, &new_repos_root_url, pool));
/* svn_client_relocate2() will check the uuid */
- SVN_ERR(svn_client_relocate2(anchor_abspath, anchor_url,
+ SVN_ERR(svn_client_relocate2(anchor_abspath, repos_root_url,
new_repos_root_url, ignore_externals,
ctx, pool));
Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_diff/parse-diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_diff/parse-diff.c?rev=1549081&r1=1549080&r2=1549081&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_diff/parse-diff.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_diff/parse-diff.c Sun Dec 8 17:56:46 2013
@@ -386,7 +386,6 @@ svn_diff_hunk_readline_diff_text(svn_dif
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- svn_diff_hunk_t dummy;
svn_stringbuf_t *line;
apr_size_t max_len;
apr_off_t pos;
@@ -416,33 +415,10 @@ svn_diff_hunk_readline_diff_text(svn_dif
if (hunk->patch->reverse)
{
- if (parse_hunk_header(line->data, &dummy, "@@", scratch_pool))
- {
- /* Line is a hunk header, reverse it. */
- line = svn_stringbuf_createf(result_pool,
- "@@ -%lu,%lu +%lu,%lu @@",
- hunk->modified_start,
- hunk->modified_length,
- hunk->original_start,
- hunk->original_length);
- }
- else if (parse_hunk_header(line->data, &dummy, "##", scratch_pool))
- {
- /* Line is a hunk header, reverse it. */
- line = svn_stringbuf_createf(result_pool,
- "## -%lu,%lu +%lu,%lu ##",
- hunk->modified_start,
- hunk->modified_length,
- hunk->original_start,
- hunk->original_length);
- }
- else
- {
- if (line->data[0] == '+')
- line->data[0] = '-';
- else if (line->data[0] == '-')
- line->data[0] = '+';
- }
+ if (line->data[0] == '+')
+ line->data[0] = '-';
+ else if (line->data[0] == '-')
+ line->data[0] = '+';
}
*stringbuf = line;
Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs/editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs/editor.c?rev=1549081&r1=1549080&r2=1549081&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs/editor.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs/editor.c Sun Dec 8 17:56:46 2013
@@ -789,10 +789,11 @@ svn_fs__editor_commit(svn_revnum_t *revi
it placed into RESULT_POOL. */
if (!err)
- err = svn_fs_commit_txn(&inner_conflict_path,
- revision,
- eb->txn,
- scratch_pool);
+ err = svn_fs_commit_txn2(&inner_conflict_path,
+ revision,
+ eb->txn,
+ TRUE,
+ scratch_pool);
if (SVN_IS_VALID_REVNUM(*revision))
{
if (err)
Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs/fs-loader.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs/fs-loader.c?rev=1549081&r1=1549080&r2=1549081&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs/fs-loader.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs/fs-loader.c Sun Dec 8 17:56:46 2013
@@ -44,6 +44,7 @@
#include "svn_string.h"
#include "svn_sorts.h"
+#include "private/svn_atomic.h"
#include "private/svn_fs_private.h"
#include "private/svn_fs_util.h"
#include "private/svn_utf_private.h"
@@ -62,8 +63,9 @@
/* A pool common to all FS objects. See the documentation on the
open/create functions in fs-loader.h and for svn_fs_initialize(). */
-static apr_pool_t *common_pool;
-svn_mutex__t *common_pool_lock;
+static apr_pool_t *common_pool = NULL;
+static svn_mutex__t *common_pool_lock = NULL;
+static svn_atomic_t common_pool_initialized = FALSE;
/* --- Utility functions for the loader --- */
@@ -196,8 +198,7 @@ get_library_vtable_direct(fs_library_vta
unloaded. This function makes a best effort by creating the
common pool as a child of the global pool; the window of failure
due to thread collision is small. */
- if (!common_pool)
- SVN_ERR(svn_fs_initialize(NULL));
+ SVN_ERR(svn_fs_initialize(NULL));
/* Invoke the FS module's initfunc function with the common
pool protected by a lock. */
@@ -280,8 +281,8 @@ get_library_vtable(fs_library_vtable_t *
if (!known)
{
fst = &(*fst)->next;
- if (!common_pool) /* Best-effort init, see get_library_vtable_direct. */
- SVN_ERR(svn_fs_initialize(NULL));
+ /* Best-effort init, see get_library_vtable_direct. */
+ SVN_ERR(svn_fs_initialize(NULL));
SVN_MUTEX__WITH_LOCK(common_pool_lock,
get_or_allocate_third(fst, fs_type));
known = TRUE;
@@ -371,16 +372,15 @@ write_fs_type(const char *path, const ch
static apr_status_t uninit(void *data)
{
common_pool = NULL;
+ common_pool_lock = NULL;
+ common_pool_initialized = 0;
+
return APR_SUCCESS;
}
-svn_error_t *
-svn_fs_initialize(apr_pool_t *pool)
+static svn_error_t *
+synchronized_initialize(void *baton, apr_pool_t *pool)
{
- /* Protect against multiple calls. */
- if (common_pool)
- return SVN_NO_ERROR;
-
common_pool = svn_pool_create(pool);
base_defn.next = NULL;
SVN_ERR(svn_mutex__init(&common_pool_lock, TRUE, common_pool));
@@ -395,6 +395,15 @@ svn_fs_initialize(apr_pool_t *pool)
return SVN_NO_ERROR;
}
+svn_error_t *
+svn_fs_initialize(apr_pool_t *pool)
+{
+ /* Protect against multiple calls. */
+ return svn_error_trace(svn_atomic__init_once(&common_pool_initialized,
+ synchronized_initialize,
+ NULL, pool));
+}
+
/* A default warning handling function. */
static void
default_warning_func(void *baton, svn_error_t *err)
@@ -1518,10 +1527,10 @@ svn_fs_lock(svn_lock_t **lock, svn_fs_t
token, "opaquelocktoken");
for (c = token; *c; c++)
- if (! svn_ctype_isascii(*c))
+ if (! svn_ctype_isascii(*c) || svn_ctype_iscntrl(*c))
return svn_error_createf(SVN_ERR_FS_BAD_LOCK_TOKEN, NULL,
- _("Lock token '%s' is not ASCII "
- "at byte %u"),
+ _("Lock token '%s' is not ASCII or is a "
+ "control character at byte %u"),
token, (unsigned)(c - token));
/* strlen(token) == c - token. */
Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs_base/fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs_base/fs.c?rev=1549081&r1=1549080&r2=1549081&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs_base/fs.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs_base/fs.c Sun Dec 8 17:56:46 2013
@@ -741,12 +741,27 @@ base_create(svn_fs_t *fs,
/* 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. */
Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs_base/fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs_base/fs.h?rev=1549081&r1=1549080&r2=1549081&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs_base/fs.h (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs_base/fs.h Sun Dec 8 17:56:46 2013
@@ -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
Modified: subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs_fs/cached_data.c
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs_fs/cached_data.c?rev=1549081&r1=1549080&r2=1549081&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs_fs/cached_data.c (original)
+++ subversion/branches/invoke-diff-cmd-feature/subversion/libsvn_fs_fs/cached_data.c Sun Dec 8 17:56:46 2013
@@ -26,10 +26,12 @@
#include "svn_hash.h"
#include "svn_ctype.h"
+#include "private/svn_io_private.h"
#include "private/svn_temp_serializer.h"
#include "fs_fs.h"
#include "id.h"
+#include "index.h"
#include "low_level.h"
#include "pack.h"
#include "util.h"
@@ -39,16 +41,21 @@
#include "svn_private_config.h"
+/* forward-declare. See implementation for the docstring */
+static svn_error_t *
+block_read(void **result,
+ svn_fs_t *fs,
+ svn_revnum_t revision,
+ apr_uint64_t item_index,
+ svn_fs_fs__revision_file_t *revision_file,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+
/* Defined this to enable access logging via dgb__log_access
#define SVN_FS_FS__LOG_ACCESS
*/
-/* Data / item types.
- */
-#define SVN_FS_FS__ITEM_TYPE_ANY_REP 1 /* item is a representation. */
-#define SVN_FS_FS__ITEM_TYPE_NODEREV 2 /* item is a noderev */
-#define SVN_FS_FS__ITEM_TYPE_CHANGES 3 /* item is a changed paths list */
-
/* When SVN_FS_FS__LOG_ACCESS has been defined, write a line to console
* showing where REVISION, ITEM_INDEX is located in FS and use ITEM to
* show details on it's contents if not NULL. To support format 6 and
@@ -60,7 +67,7 @@
static svn_error_t *
dbg_log_access(svn_fs_t *fs,
svn_revnum_t revision,
- apr_off_t offset,
+ apr_uint64_t item_index,
void *item,
int item_type,
apr_pool_t *scratch_pool)
@@ -68,14 +75,17 @@ dbg_log_access(svn_fs_t *fs,
/* no-op if this macro is not defined */
#ifdef SVN_FS_FS__LOG_ACCESS
fs_fs_data_t *ffd = fs->fsap_data;
- static const char *types[] = {"<n/a>", "rep ", "node ", "chgs "};
+ apr_off_t end_offset = 0;
+ svn_fs_fs__p2l_entry_t *entry = NULL;
+ static const char *types[] = {"<n/a>", "frep ", "drep ", "fprop", "dprop",
+ "node ", "chgs ", "rep "};
const char *description = "";
const char *type = types[item_type];
const char *pack = "";
- apr_off_t offset_in_rev = offset;
+ apr_off_t offset;
/* determine rev / pack file offset */
- SVN_ERR(svn_fs_fs__item_offset(&offset, fs, revision, offset,
+ SVN_ERR(svn_fs_fs__item_offset(&offset, fs, revision, NULL, item_index,
scratch_pool));
/* constructing the pack file description */
@@ -91,13 +101,13 @@ dbg_log_access(svn_fs_t *fs,
= node->data_rep
? apr_psprintf(scratch_pool, " d=%ld/%" APR_UINT64_T_FMT,
node->data_rep->revision,
- node->data_rep->offset)
+ node->data_rep->item_index)
: "";
const char *prop_rep
= node->prop_rep
? apr_psprintf(scratch_pool, " p=%ld/%" APR_UINT64_T_FMT,
node->prop_rep->revision,
- node->prop_rep->offset)
+ node->prop_rep->item_index)
: "";
description = apr_psprintf(scratch_pool, "%s (pc=%d%s%s)",
node->created_path,
@@ -118,7 +128,7 @@ dbg_log_access(svn_fs_t *fs,
description = apr_psprintf(scratch_pool,
" DELTA against %ld/%" APR_UINT64_T_FMT,
header->base_revision,
- header->base_offset);
+ header->base_item_index);
}
else if (item_type == SVN_FS_FS__ITEM_TYPE_CHANGES && item != NULL)
{
@@ -134,23 +144,53 @@ dbg_log_access(svn_fs_t *fs,
}
}
- printf("%5s%10" APR_UINT64_T_HEX_FMT " %s %7ld %7" APR_UINT64_T_FMT \
- " %s\n",
- pack, (apr_uint64_t)(offset), type, revision, offset_in_rev,
- description);
+ /* some info is only available in format7 repos */
+ if (svn_fs_fs__use_log_addressing(fs, revision))
+ {
+ /* reverse index lookup: get item description in ENTRY */
+ SVN_ERR(svn_fs_fs__p2l_entry_lookup(&entry, fs, revision, offset,
+ scratch_pool));
+ if (entry)
+ {
+ /* more details */
+ end_offset = offset + entry->size;
+ type = types[entry->type];
+ }
+
+ /* line output */
+ printf("%5s%4lx:%04lx -%4lx:%04lx %s %7ld %5"APR_UINT64_T_FMT" %s\n",
+ pack, (long)(offset / ffd->block_size),
+ (long)(offset % ffd->block_size),
+ (long)(end_offset / ffd->block_size),
+ (long)(end_offset % ffd->block_size),
+ type, revision, item_index, description);
+ }
+ else
+ {
+ /* reduced logging for format 6 and earlier */
+ printf("%5s%10" APR_UINT64_T_HEX_FMT " %s %7ld %7" APR_UINT64_T_FMT \
+ " %s\n",
+ pack, (apr_uint64_t)(offset), type, revision, item_index,
+ description);
+ }
#endif
return SVN_NO_ERROR;
}
-/* Convenience wrapper around svn_io_file_aligned_seek. */
+/* Convenience wrapper around svn_io_file_aligned_seek, taking filesystem
+ FS instead of a block size. */
static svn_error_t *
-aligned_seek(apr_file_t *file,
+aligned_seek(svn_fs_t *fs,
+ apr_file_t *file,
+ apr_off_t *buffer_start,
apr_off_t offset,
apr_pool_t *pool)
{
- return svn_error_trace(svn_io_file_aligned_seek(file, 0, NULL, offset,
+ fs_fs_data_t *ffd = fs->fsap_data;
+ return svn_error_trace(svn_io_file_aligned_seek(file, ffd->block_size,
+ buffer_start, offset,
pool));
}
@@ -158,19 +198,22 @@ aligned_seek(apr_file_t *file,
the newly opened file in FILE. Seek to location OFFSET before
returning. Perform temporary allocations in POOL. */
static svn_error_t *
-open_and_seek_revision(apr_file_t **file,
+open_and_seek_revision(svn_fs_fs__revision_file_t **file,
svn_fs_t *fs,
svn_revnum_t rev,
- apr_off_t offset,
+ apr_uint64_t item,
apr_pool_t *pool)
{
- apr_file_t *rev_file;
+ svn_fs_fs__revision_file_t *rev_file;
+ apr_off_t offset = -1;
SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, pool));
SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, rev, pool));
- SVN_ERR(svn_fs_fs__item_offset(&offset, fs, rev, offset, pool));
- SVN_ERR(aligned_seek(rev_file, offset, pool));
+ SVN_ERR(svn_fs_fs__item_offset(&offset, fs, rev_file, rev, NULL, item,
+ pool));
+
+ SVN_ERR(aligned_seek(fs, rev_file->file, NULL, offset, pool));
*file = rev_file;
@@ -181,21 +224,18 @@ open_and_seek_revision(apr_file_t **file
to its position and store the newly opened file in FILE. Perform
temporary allocations in POOL. */
static svn_error_t *
-open_and_seek_transaction(apr_file_t **file,
+open_and_seek_transaction(svn_fs_fs__revision_file_t **file,
svn_fs_t *fs,
representation_t *rep,
apr_pool_t *pool)
{
- apr_file_t *rev_file;
-
- SVN_ERR(svn_io_file_open(&rev_file,
- svn_fs_fs__path_txn_proto_rev(fs, &rep->txn_id,
- pool),
- APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
+ apr_off_t offset;
- SVN_ERR(aligned_seek(rev_file, rep->offset, pool));
+ SVN_ERR(svn_fs_fs__open_proto_rev_file(file, fs, &rep->txn_id, pool));
- *file = rev_file;
+ SVN_ERR(svn_fs_fs__item_offset(&offset, fs, NULL, SVN_INVALID_REVNUM,
+ &rep->txn_id, rep->item_index, pool));
+ SVN_ERR(aligned_seek(fs, (*file)->file, NULL, offset, pool));
return SVN_NO_ERROR;
}
@@ -204,13 +244,13 @@ open_and_seek_transaction(apr_file_t **f
the correct file and seek to the correction location. Store this
file in *FILE_P. Perform any allocations in POOL. */
static svn_error_t *
-open_and_seek_representation(apr_file_t **file_p,
+open_and_seek_representation(svn_fs_fs__revision_file_t **file_p,
svn_fs_t *fs,
representation_t *rep,
apr_pool_t *pool)
{
if (! svn_fs_fs__id_txn_used(&rep->txn_id))
- return open_and_seek_revision(file_p, fs, rep->revision, rep->offset,
+ return open_and_seek_revision(file_p, fs, rep->revision, rep->item_index,
pool);
else
return open_and_seek_transaction(file_p, fs, rep, pool);
@@ -238,16 +278,17 @@ get_node_revision_body(node_revision_t *
const svn_fs_id_t *id,
apr_pool_t *pool)
{
- apr_file_t *revision_file;
svn_error_t *err;
svn_boolean_t is_cached = FALSE;
fs_fs_data_t *ffd = fs->fsap_data;
if (svn_fs_fs__id_is_txn(id))
{
+ apr_file_t *file;
+
/* This is a transaction node-rev. Its storage logic is very
different from that of rev / pack files. */
- err = svn_io_file_open(&revision_file,
+ err = svn_io_file_open(&file,
svn_fs_fs__path_txn_node_rev(fs, id, pool),
APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool);
if (err)
@@ -262,15 +303,17 @@ get_node_revision_body(node_revision_t *
}
SVN_ERR(svn_fs_fs__read_noderev(noderev_p,
- svn_stream_from_aprfile2(revision_file,
+ svn_stream_from_aprfile2(file,
FALSE,
pool),
pool));
}
else
{
+ svn_fs_fs__revision_file_t *revision_file;
+
/* noderevs in rev / pack files can be cached */
- const svn_fs_fs__id_part_t *rev_item = svn_fs_fs__id_rev_offset(id);
+ const svn_fs_fs__id_part_t *rev_item = svn_fs_fs__id_rev_item(id);
pair_cache_key_t key = { 0 };
key.revision = rev_item->revision;
key.second = rev_item->number;
@@ -293,21 +336,36 @@ get_node_revision_body(node_revision_t *
rev_item->revision,
rev_item->number,
pool));
- SVN_ERR(svn_fs_fs__read_noderev(noderev_p,
- svn_stream_from_aprfile2(revision_file,
- FALSE,
- pool),
- pool));
- /* Workaround issue #4031: is-fresh-txn-root in revision files. */
- (*noderev_p)->is_fresh_txn_root = FALSE;
+ if (svn_fs_fs__use_log_addressing(fs, rev_item->revision))
+ {
+ /* block-read will parse the whole block and will also return
+ the one noderev that we need right now. */
+ SVN_ERR(block_read((void **)noderev_p, fs,
+ rev_item->revision,
+ rev_item->number,
+ revision_file,
+ pool,
+ pool));
+ SVN_ERR(svn_fs_fs__close_revision_file(revision_file));
+ }
+ else
+ {
+ /* physical addressing mode reading, parsing and caching */
+ SVN_ERR(svn_fs_fs__read_noderev(noderev_p,
+ revision_file->stream,
+ pool));
+
+ /* Workaround issue #4031: is-fresh-txn-root in revision files. */
+ (*noderev_p)->is_fresh_txn_root = FALSE;
- /* The noderev is not in cache, yet. Add it, if caching has been enabled. */
- if (ffd->node_revision_cache)
- SVN_ERR(svn_cache__set(ffd->node_revision_cache,
- &key,
- *noderev_p,
- pool));
+ /* The noderev is not in cache, yet. Add it, if caching has been enabled. */
+ if (ffd->node_revision_cache)
+ SVN_ERR(svn_cache__set(ffd->node_revision_cache,
+ &key,
+ *noderev_p,
+ pool));
+ }
}
return SVN_NO_ERROR;
@@ -319,7 +377,7 @@ svn_fs_fs__get_node_revision(node_revisi
const svn_fs_id_t *id,
apr_pool_t *pool)
{
- const svn_fs_fs__id_part_t *rev_offset = svn_fs_fs__id_rev_offset(id);
+ const svn_fs_fs__id_part_t *rev_item = svn_fs_fs__id_rev_item(id);
svn_error_t *err = get_node_revision_body(noderev_p, fs, id, pool);
if (err && err->apr_err == SVN_ERR_FS_CORRUPT)
@@ -331,8 +389,8 @@ svn_fs_fs__get_node_revision(node_revisi
}
SVN_ERR(dbg_log_access(fs,
- rev_offset->revision,
- rev_offset->number,
+ rev_item->revision,
+ rev_item->number,
*noderev_p,
SVN_FS_FS__ITEM_TYPE_NODEREV,
pool));
@@ -346,7 +404,7 @@ svn_fs_fs__get_node_revision(node_revisi
temporary variables from POOL. */
static svn_error_t *
get_fs_id_at_offset(svn_fs_id_t **id_p,
- apr_file_t *rev_file,
+ svn_fs_fs__revision_file_t *rev_file,
svn_fs_t *fs,
svn_revnum_t rev,
apr_off_t offset,
@@ -354,10 +412,9 @@ get_fs_id_at_offset(svn_fs_id_t **id_p,
{
node_revision_t *noderev;
- SVN_ERR(aligned_seek(rev_file, offset, pool));
+ SVN_ERR(aligned_seek(fs, rev_file->file, NULL, offset, pool));
SVN_ERR(svn_fs_fs__read_noderev(&noderev,
- svn_stream_from_aprfile2(rev_file, TRUE,
- pool),
+ rev_file->stream,
pool));
/* noderev->id is const, get rid of that */
@@ -434,7 +491,7 @@ get_root_changes_offset(apr_off_t *root_
SVN_ERR(svn_io_file_seek(rev_file, seek_relative, &offset, pool));
trailer->len = trailer->blocksize-1;
- SVN_ERR(aligned_seek(rev_file, offset - trailer->len, pool));
+ SVN_ERR(aligned_seek(fs, rev_file, NULL, offset - trailer->len, pool));
/* Read in this last block, from which we will identify the last line. */
SVN_ERR(svn_io_file_read(rev_file, trailer->data, &trailer->len, pool));
@@ -462,30 +519,37 @@ svn_fs_fs__rev_get_root(svn_fs_id_t **ro
apr_pool_t *pool)
{
fs_fs_data_t *ffd = fs->fsap_data;
+ SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, pool));
- apr_file_t *revision_file;
- apr_off_t root_offset;
- svn_fs_id_t *root_id = NULL;
- svn_boolean_t is_cached;
+ if (svn_fs_fs__use_log_addressing(fs, rev))
+ {
+ *root_id_p = svn_fs_fs__id_create_root(rev, pool);
+ }
+ else
+ {
+ svn_fs_fs__revision_file_t *revision_file;
+ apr_off_t root_offset;
+ svn_fs_id_t *root_id = NULL;
+ svn_boolean_t is_cached;
- SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, pool));
- SVN_ERR(svn_cache__get((void **) root_id_p, &is_cached,
- ffd->rev_root_id_cache, &rev, pool));
- if (is_cached)
- return SVN_NO_ERROR;
+ SVN_ERR(svn_cache__get((void **) root_id_p, &is_cached,
+ ffd->rev_root_id_cache, &rev, pool));
+ if (is_cached)
+ return SVN_NO_ERROR;
- SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&revision_file, fs, rev, pool));
- SVN_ERR(get_root_changes_offset(&root_offset, NULL, revision_file, fs, rev,
- pool));
+ SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&revision_file, fs, rev, pool));
+ SVN_ERR(get_root_changes_offset(&root_offset, NULL,
+ revision_file->file, fs, rev, pool));
- SVN_ERR(get_fs_id_at_offset(&root_id, revision_file, fs, rev,
- root_offset, pool));
+ SVN_ERR(get_fs_id_at_offset(&root_id, revision_file, fs, rev,
+ root_offset, pool));
- SVN_ERR(svn_io_file_close(revision_file, pool));
+ SVN_ERR(svn_fs_fs__close_revision_file(revision_file));
- SVN_ERR(svn_cache__set(ffd->rev_root_id_cache, &rev, root_id, pool));
+ SVN_ERR(svn_cache__set(ffd->rev_root_id_cache, &rev, root_id, pool));
- *root_id_p = root_id;
+ *root_id_p = root_id;
+ }
return SVN_NO_ERROR;
}
@@ -495,10 +559,7 @@ svn_fs_fs__rev_get_root(svn_fs_id_t **ro
typedef struct shared_file_t
{
/* The opened file. NULL while file is not open, yet. */
- apr_file_t *file;
-
- /* Stream wrapper around FILE. NULL while file is not open, yet. */
- svn_stream_t *stream;
+ svn_fs_fs__revision_file_t *rfile;
/* file system to open the file in */
svn_fs_t *fs;
@@ -518,15 +579,15 @@ typedef struct shared_file_t
typedef struct rep_state_t
{
/* shared lazy-open rev/pack file structure */
- shared_file_t *file;
+ shared_file_t *sfile;
/* The txdelta window cache to use or NULL. */
svn_cache__t *window_cache;
/* Caches un-deltified windows. May be NULL. */
svn_cache__t *combined_cache;
/* revision containing the representation */
svn_revnum_t revision;
- /* representation's offset in REVISION */
- apr_uint64_t offset;
+ /* representation's item index in REVISION */
+ apr_uint64_t item_index;
/* length of the header at the start of the rep.
0 iff this is rep is stored in a container
(i.e. does not have a header) */
@@ -541,16 +602,38 @@ typedef struct rep_state_t
int chunk_index; /* number of the window to read */
} rep_state_t;
+/* Simple wrapper around svn_fs_fs__get_file_offset to simplify callers. */
+static svn_error_t *
+get_file_offset(apr_off_t *offset,
+ rep_state_t *rs,
+ apr_pool_t *pool)
+{
+ return svn_error_trace(svn_fs_fs__get_file_offset(offset,
+ rs->sfile->rfile->file,
+ pool));
+}
+
+/* Simple wrapper around svn_io_file_aligned_seek to simplify callers. */
+static svn_error_t *
+rs_aligned_seek(rep_state_t *rs,
+ apr_off_t *buffer_start,
+ apr_off_t offset,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = rs->sfile->fs->fsap_data;
+ return svn_error_trace(svn_io_file_aligned_seek(rs->sfile->rfile->file,
+ ffd->block_size,
+ buffer_start, offset,
+ pool));
+}
+
/* Open FILE->FILE and FILE->STREAM if they haven't been opened, yet. */
static svn_error_t*
auto_open_shared_file(shared_file_t *file)
{
- if (file->file == NULL)
- {
- SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&file->file, file->fs,
- file->revision, file->pool));
- file->stream = svn_stream_from_aprfile2(file->file, TRUE, file->pool);
- }
+ if (file->rfile == NULL)
+ SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&file->rfile, file->fs,
+ file->revision, file->pool));
return SVN_NO_ERROR;
}
@@ -562,8 +645,9 @@ auto_set_start_offset(rep_state_t *rs, a
{
if (rs->start == -1)
{
- SVN_ERR(svn_fs_fs__item_offset(&rs->start, rs->file->fs, rs->revision,
- rs->offset, pool));
+ SVN_ERR(svn_fs_fs__item_offset(&rs->start, rs->sfile->fs,
+ rs->sfile->rfile, rs->revision, NULL,
+ rs->item_index, pool));
rs->start += rs->header_size;
}
@@ -579,9 +663,9 @@ auto_read_diff_version(rep_state_t *rs,
if (rs->ver == -1)
{
char buf[4];
- SVN_ERR(aligned_seek(rs->file->file, rs->start, pool));
- SVN_ERR(svn_io_file_read_full2(rs->file->file, buf, sizeof(buf),
- NULL, NULL, pool));
+ SVN_ERR(rs_aligned_seek(rs, NULL, rs->start, pool));
+ SVN_ERR(svn_io_file_read_full2(rs->sfile->rfile->file, buf,
+ sizeof(buf), NULL, NULL, pool));
/* ### Layering violation */
if (! ((buf[0] == 'S') && (buf[1] == 'V') && (buf[2] == 'N')))
@@ -620,7 +704,7 @@ create_rep_state_body(rep_state_t **rep_
* we can re-use the same, already open file object
*/
svn_boolean_t reuse_shared_file
- = shared_file && *shared_file && (*shared_file)->file
+ = shared_file && *shared_file && (*shared_file)->rfile
&& SVN_IS_VALID_REVNUM((*shared_file)->revision)
&& (*shared_file)->revision < ffd->min_unpacked_rev
&& rep->revision < ffd->min_unpacked_rev
@@ -630,12 +714,12 @@ create_rep_state_body(rep_state_t **rep_
representation_cache_key_t key;
key.revision = rep->revision;
key.is_packed = rep->revision < ffd->min_unpacked_rev;
- key.offset = rep->offset;
+ key.item_index = rep->item_index;
/* continue constructing RS and RA */
rs->size = rep->size;
rs->revision = rep->revision;
- rs->offset = rep->offset;
+ rs->item_index = rep->item_index;
rs->window_cache = ffd->txdelta_window_cache;
rs->combined_cache = ffd->combined_window_cache;
rs->ver = -1;
@@ -649,7 +733,7 @@ create_rep_state_body(rep_state_t **rep_
/* initialize the (shared) FILE member in RS */
if (reuse_shared_file)
{
- rs->file = *shared_file;
+ rs->sfile = *shared_file;
}
else
{
@@ -657,7 +741,7 @@ create_rep_state_body(rep_state_t **rep_
file->revision = rep->revision;
file->pool = pool;
file->fs = fs;
- rs->file = file;
+ rs->sfile = file;
/* remember the current file, if suggested by the caller */
if (shared_file)
@@ -675,35 +759,39 @@ create_rep_state_body(rep_state_t **rep_
/* ... we can re-use the same, already open file object.
* This implies that we don't read from a txn.
*/
- rs->file = *shared_file;
- SVN_ERR(auto_open_shared_file(rs->file));
- SVN_ERR(svn_fs_fs__get_packed_offset(&offset, fs, rep->revision,
- pool));
- SVN_ERR(aligned_seek((*shared_file)->file, offset + rep->offset,
- pool));
+ rs->sfile = *shared_file;
+ SVN_ERR(auto_open_shared_file(rs->sfile));
+ SVN_ERR(svn_fs_fs__item_offset(&offset, fs, rs->sfile->rfile,
+ rep->revision, NULL, rep->item_index,
+ pool));
+ SVN_ERR(rs_aligned_seek(rs, NULL, offset, pool));
}
else
{
/* otherwise, create a new file object. May or may not be
* an in-txn file.
*/
- SVN_ERR(open_and_seek_representation(&rs->file->file, fs, rep,
+ SVN_ERR(open_and_seek_representation(&rs->sfile->rfile, fs, rep,
pool));
- rs->file->stream = svn_stream_from_aprfile2(rs->file->file, TRUE,
- rs->file->pool);
}
- SVN_ERR(svn_fs_fs__read_rep_header(&rh, rs->file->stream, pool));
- SVN_ERR(svn_fs_fs__get_file_offset(&rs->start, rs->file->file, pool));
+ SVN_ERR(svn_fs_fs__read_rep_header(&rh, rs->sfile->rfile->stream, pool));
+ SVN_ERR(get_file_offset(&rs->start, rs, pool));
- /* cache the rep header if appropriate */
+ /* populate the cache if appropriate */
if (! svn_fs_fs__id_txn_used(&rep->txn_id))
- if (ffd->rep_header_cache)
- SVN_ERR(svn_cache__set(ffd->rep_header_cache, &key, rh, pool));
+ {
+ if (svn_fs_fs__use_log_addressing(fs, rep->revision))
+ SVN_ERR(block_read(NULL, fs, rep->revision, rep->item_index,
+ rs->sfile->rfile, pool, pool));
+ else
+ if (ffd->rep_header_cache)
+ SVN_ERR(svn_cache__set(ffd->rep_header_cache, &key, rh, pool));
+ }
}
/* finalize */
- SVN_ERR(dbg_log_access(fs, rep->revision, rep->offset, rh,
+ SVN_ERR(dbg_log_access(fs, rep->revision, rep->item_index, rh,
SVN_FS_FS__ITEM_TYPE_ANY_REP, pool));
rs->header_size = rh->header_size;
@@ -769,12 +857,52 @@ svn_fs_fs__check_rep(representation_t *r
void **hint,
apr_pool_t *pool)
{
- rep_state_t *rs;
- svn_fs_fs__rep_header_t *rep_header;
+ if (svn_fs_fs__use_log_addressing(fs, rep->revision))
+ {
+ svn_error_t *err;
+ apr_off_t offset;
+ svn_fs_fs__p2l_entry_t *entry;
+ svn_boolean_t is_packed;
+
+ svn_fs_fs__revision_file_t rev_file;
+ svn_fs_fs__init_revision_file(&rev_file, fs, rep->revision, pool);
+ SVN_ERR(svn_fs_fs__item_offset(&offset, fs, &rev_file, rep->revision,
+ NULL, rep->item_index, pool));
+
+ is_packed = rev_file.is_packed;
+ err = svn_fs_fs__p2l_entry_lookup(&entry, fs, &rev_file, rep->revision,
+ offset, pool);
+
+ /* retry if the packing state has changed */
+ if (is_packed != rev_file.is_packed)
+ {
+ svn_error_clear(err);
+ return svn_error_trace(svn_fs_fs__check_rep(rep, fs, hint, pool));
+ }
+ else
+ {
+ SVN_ERR(err);
+ }
- /* ### Should this be using read_rep_line() directly? */
- SVN_ERR(create_rep_state(&rs, &rep_header, (shared_file_t**)hint, rep,
- fs, pool));
+ if ( entry == NULL
+ || entry->type < SVN_FS_FS__ITEM_TYPE_FILE_REP
+ || entry->type > SVN_FS_FS__ITEM_TYPE_DIR_PROPS)
+ return svn_error_createf(SVN_ERR_REPOS_CORRUPTED, NULL,
+ _("No representation found at offset %s "
+ "for item %" APR_UINT64_T_FMT
+ " in revision %ld"),
+ apr_off_t_toa(pool, entry->offset),
+ rep->item_index, rep->revision);
+ }
+ else
+ {
+ rep_state_t *rs;
+ svn_fs_fs__rep_header_t *rep_header;
+
+ /* ### Should this be using read_rep_line() directly? */
+ SVN_ERR(create_rep_state(&rs, &rep_header, (shared_file_t**)hint,
+ rep, fs, pool));
+ }
return SVN_NO_ERROR;
}
@@ -812,7 +940,7 @@ svn_fs_fs__rep_chain_length(int *chain_l
sub_pool));
base_rep.revision = header->base_revision;
- base_rep.offset = header->base_offset;
+ base_rep.item_index = header->base_item_index;
base_rep.size = header->base_length;
svn_fs_fs__id_txn_reset(&base_rep.txn_id);
is_delta = header->type == svn_fs_fs__rep_delta;
@@ -891,7 +1019,7 @@ get_window_key(window_cache_key_t *key,
{
assert(rs->revision <= APR_UINT32_MAX);
key->revision = (apr_uint32_t)rs->revision;
- key->offset = rs->offset;
+ key->item_index = rs->item_index;
key->chunk_index = rs->chunk_index;
return key;
@@ -1111,7 +1239,7 @@ build_rep_list(apr_array_header_t **list
}
rep.revision = rep_header->base_revision;
- rep.offset = rep_header->base_offset;
+ rep.item_index = rep_header->base_item_index;
rep.size = rep_header->base_length;
svn_fs_fs__id_txn_reset(&rep.txn_id);
@@ -1177,7 +1305,7 @@ read_delta_window(svn_txdelta_window_t *
apr_off_t end_offset;
SVN_ERR_ASSERT(rs->chunk_index <= this_chunk);
- SVN_ERR(dbg_log_access(rs->file->fs, rs->revision, rs->offset,
+ SVN_ERR(dbg_log_access(rs->sfile->fs, rs->revision, rs->item_index,
NULL, SVN_FS_FS__ITEM_TYPE_ANY_REP, pool));
/* Read the next window. But first, try to find it in the cache. */
@@ -1186,23 +1314,42 @@ read_delta_window(svn_txdelta_window_t *
return SVN_NO_ERROR;
/* someone has to actually read the data from file. Open it */
- SVN_ERR(auto_open_shared_file(rs->file));
+ SVN_ERR(auto_open_shared_file(rs->sfile));
+
+ /* invoke the 'block-read' feature for non-txn data.
+ However, don't do that if we are in the middle of some representation,
+ because the block is unlikely to contain other data. */
+ if ( rs->chunk_index == 0
+ && SVN_IS_VALID_REVNUM(rs->revision)
+ && svn_fs_fs__use_log_addressing(rs->sfile->fs, rs->revision))
+ {
+ SVN_ERR(block_read(NULL, rs->sfile->fs, rs->revision, rs->item_index,
+ rs->sfile->rfile, pool, pool));
+
+ /* reading the whole block probably also provided us with the
+ desired txdelta window */
+ SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached, pool));
+ if (is_cached)
+ return SVN_NO_ERROR;
+ }
+
+ /* data is still not cached -> we need to read it.
+ Make sure we have all the necessary info. */
SVN_ERR(auto_set_start_offset(rs, pool));
SVN_ERR(auto_read_diff_version(rs, pool));
/* RS->FILE may be shared between RS instances -> make sure we point
* to the right data. */
start_offset = rs->start + rs->current;
- SVN_ERR(aligned_seek(rs->file->file, start_offset, pool));
+ SVN_ERR(rs_aligned_seek(rs, NULL, start_offset, pool));
/* Skip windows to reach the current chunk if we aren't there yet. */
while (rs->chunk_index < this_chunk)
{
- SVN_ERR(svn_txdelta_skip_svndiff_window(rs->file->file, rs->ver,
- pool));
+ SVN_ERR(svn_txdelta_skip_svndiff_window(rs->sfile->rfile->file,
+ rs->ver, pool));
rs->chunk_index++;
- SVN_ERR(svn_fs_fs__get_file_offset(&start_offset, rs->file->file,
- pool));
+ SVN_ERR(get_file_offset(&start_offset, rs, pool));
rs->current = start_offset - rs->start;
if (rs->current >= rs->size)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
@@ -1212,9 +1359,9 @@ read_delta_window(svn_txdelta_window_t *
}
/* Actually read the next window. */
- SVN_ERR(svn_txdelta_read_svndiff_window(nwin, rs->file->stream, rs->ver,
- pool));
- SVN_ERR(svn_fs_fs__get_file_offset(&end_offset, rs->file->file, pool));
+ SVN_ERR(svn_txdelta_read_svndiff_window(nwin, rs->sfile->rfile->stream,
+ rs->ver, pool));
+ SVN_ERR(get_file_offset(&end_offset, rs, pool));
rs->current = end_offset - rs->start;
if (rs->current > rs->size)
return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
@@ -1238,15 +1385,15 @@ read_plain_window(svn_stringbuf_t **nwin
/* RS->FILE may be shared between RS instances -> make sure we point
* to the right data. */
- SVN_ERR(auto_open_shared_file(rs->file));
+ SVN_ERR(auto_open_shared_file(rs->sfile));
SVN_ERR(auto_set_start_offset(rs, pool));
offset = rs->start + rs->current;
- SVN_ERR(aligned_seek(rs->file->file, offset, pool));
+ SVN_ERR(rs_aligned_seek(rs, NULL, offset, pool));
/* Read the plain data. */
*nwin = svn_stringbuf_create_ensure(size, pool);
- SVN_ERR(svn_io_file_read_full2(rs->file->file, (*nwin)->data, size,
+ SVN_ERR(svn_io_file_read_full2(rs->sfile->rfile->file, (*nwin)->data, size,
NULL, NULL, pool));
(*nwin)->data[size] = 0;
@@ -1399,13 +1546,13 @@ get_contents(struct rep_read_baton *rb,
if (((apr_off_t) copy_len) > rs->size - rs->current)
copy_len = (apr_size_t) (rs->size - rs->current);
- SVN_ERR(auto_open_shared_file(rs->file));
+ SVN_ERR(auto_open_shared_file(rs->sfile));
SVN_ERR(auto_set_start_offset(rs, rb->pool));
offset = rs->start + rs->current;
- SVN_ERR(aligned_seek(rs->file->file, offset, rb->pool));
- SVN_ERR(svn_io_file_read_full2(rs->file->file, cur, copy_len,
- NULL, NULL, rb->pool));
+ SVN_ERR(rs_aligned_seek(rs, NULL, offset, rb->pool));
+ SVN_ERR(svn_io_file_read_full2(rs->sfile->rfile->file, cur,
+ copy_len, NULL, NULL, rb->pool));
}
rs->current += copy_len;
@@ -1532,7 +1679,7 @@ svn_fs_fs__get_contents(svn_stream_t **c
struct rep_read_baton *rb;
fulltext_cache_key.revision = rep->revision;
- fulltext_cache_key.second = rep->offset;
+ fulltext_cache_key.second = rep->item_index;
if (ffd->fulltext_cache && SVN_IS_VALID_REVNUM(rep->revision)
&& fulltext_size_is_cachable(ffd, len))
{
@@ -1607,7 +1754,7 @@ svn_fs_fs__try_process_file_contents(svn
pair_cache_key_t fulltext_cache_key = { 0 };
fulltext_cache_key.revision = rep->revision;
- fulltext_cache_key.second = rep->offset;
+ fulltext_cache_key.second = rep->item_index;
if (ffd->fulltext_cache && SVN_IS_VALID_REVNUM(rep->revision)
&& fulltext_size_is_cachable(ffd, rep->expanded_size))
{
@@ -1685,7 +1832,7 @@ svn_fs_fs__get_file_delta_stream(svn_txd
if (rep_header->type == svn_fs_fs__rep_self_delta
|| (rep_header->type == svn_fs_fs__rep_delta
&& rep_header->base_revision == source->data_rep->revision
- && rep_header->base_offset == source->data_rep->offset))
+ && rep_header->base_item_index == source->data_rep->item_index))
{
/* Create the delta read baton. */
struct delta_read_baton *drb = apr_pcalloc(pool, sizeof(*drb));
@@ -1696,9 +1843,11 @@ svn_fs_fs__get_file_delta_stream(svn_txd
delta_read_md5_digest, pool);
return SVN_NO_ERROR;
}
- else
- if (rep_state->file->file)
- SVN_ERR(svn_io_file_close(rep_state->file->file, pool));
+ else if (rep_state->sfile->rfile)
+ {
+ SVN_ERR(svn_fs_fs__close_revision_file(rep_state->sfile->rfile));
+ rep_state->sfile->rfile = NULL;
+ }
}
/* Read both fulltexts and construct a delta. */
@@ -1855,7 +2004,7 @@ locate_dir_cache(svn_fs_t *fs,
if (noderev->data_rep)
{
pair_key->revision = noderev->data_rep->revision;
- pair_key->second = noderev->data_rep->offset;
+ pair_key->second = noderev->data_rep->item_index;
*key = pair_key;
}
else
@@ -1985,7 +2134,7 @@ svn_fs_fs__get_proplist(apr_hash_t **pro
pair_cache_key_t key = { 0 };
key.revision = rep->revision;
- key.second = rep->offset;
+ key.second = rep->item_index;
if (ffd->properties_cache && SVN_IS_VALID_REVNUM(rep->revision))
{
svn_boolean_t is_cached;
@@ -2014,53 +2163,543 @@ svn_fs_fs__get_proplist(apr_hash_t **pro
return SVN_NO_ERROR;
}
-/* Fetch the list of change in revision REV in FS and return it in *CHANGES.
- * Allocate the result in POOL.
- */
svn_error_t *
svn_fs_fs__get_changes(apr_array_header_t **changes,
svn_fs_t *fs,
svn_revnum_t rev,
apr_pool_t *pool)
{
- apr_off_t changes_offset;
- apr_file_t *revision_file;
+ apr_off_t changes_offset = -1;
+ svn_fs_fs__revision_file_t *revision_file;
+ svn_boolean_t found;
fs_fs_data_t *ffd = fs->fsap_data;
/* try cache lookup first */
if (ffd->changes_cache)
+ SVN_ERR(svn_cache__get((void **) changes, &found, ffd->changes_cache,
+ &rev, pool));
+
+ if (!found)
{
- svn_boolean_t found;
- SVN_ERR(svn_cache__get((void **) changes, &found, ffd->changes_cache,
- &rev, pool));
- if (found)
+ /* read changes from revision file */
+
+ SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, pool));
+ SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&revision_file, fs, rev,
+ pool));
+
+ if (svn_fs_fs__use_log_addressing(fs, rev))
+ {
+ /* 'block-read' will also provide us with the desired data */
+ SVN_ERR(block_read((void **)changes, fs,
+ rev, SVN_FS_FS__ITEM_INDEX_CHANGES,
+ revision_file, pool, pool));
+ }
+ else
+ {
+ /* physical addressing mode code path */
+ SVN_ERR(get_root_changes_offset(NULL, &changes_offset,
+ revision_file->file, fs, rev,
+ pool));
+
+ SVN_ERR(aligned_seek(fs, revision_file->file, NULL, changes_offset,
+ pool));
+ SVN_ERR(svn_fs_fs__read_changes(changes, revision_file->stream,
+ pool));
+
+ /* cache for future reference */
+
+ if (ffd->changes_cache)
+ SVN_ERR(svn_cache__set(ffd->changes_cache, &rev, *changes, pool));
+ }
+
+ SVN_ERR(svn_fs_fs__close_revision_file(revision_file));
+ }
+
+ SVN_ERR(dbg_log_access(fs, rev, changes_offset, *changes,
+ SVN_FS_FS__ITEM_TYPE_CHANGES, pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* Inialize the representation read state RS for the given REP_HEADER and
+ * p2l index ENTRY. If not NULL, assign FILE and STREAM to RS.
+ * Use POOL for allocations.
+ */
+static svn_error_t *
+init_rep_state(rep_state_t *rs,
+ svn_fs_fs__rep_header_t *rep_header,
+ svn_fs_t *fs,
+ svn_fs_fs__revision_file_t *file,
+ svn_fs_fs__p2l_entry_t* entry,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ shared_file_t *shared_file = apr_pcalloc(pool, sizeof(*shared_file));
+
+ /* this function does not apply to representation containers */
+ SVN_ERR_ASSERT(entry->type >= SVN_FS_FS__ITEM_TYPE_FILE_REP
+ && entry->type <= SVN_FS_FS__ITEM_TYPE_DIR_PROPS);
+
+ shared_file->rfile = file;
+ shared_file->fs = fs;
+ shared_file->revision = entry->item.revision;
+ shared_file->pool = pool;
+
+ rs->sfile = shared_file;
+ rs->revision = entry->item.revision;
+ rs->item_index = entry->item.number;
+ rs->header_size = rep_header->header_size;
+ rs->start = entry->offset + rs->header_size;
+ rs->current = rep_header->type == svn_fs_fs__rep_plain ? 0 : 4;
+ rs->size = entry->size - rep_header->header_size - 7;
+ rs->ver = 1;
+ rs->chunk_index = 0;
+ rs->window_cache = ffd->txdelta_window_cache;
+ rs->combined_cache = ffd->combined_window_cache;
+
+ return SVN_NO_ERROR;
+}
+
+/* Walk through all windows in the representation addressed by RS in FS
+ * (excluding the delta bases) and put those not already cached into the
+ * window caches. As a side effect, return the total sum of all expanded
+ * window sizes in *FULLTEXT_LEN. Use POOL for temporary allocations.
+ */
+static svn_error_t *
+cache_windows(svn_filesize_t *fulltext_len,
+ svn_fs_t *fs,
+ rep_state_t *rs,
+ apr_pool_t *pool)
+{
+ *fulltext_len = 0;
+
+ while (rs->current < rs->size)
+ {
+ svn_txdelta_window_t *window;
+ apr_off_t start_offset = rs->start + rs->current;
+ apr_off_t end_offset;
+
+ /* navigate to & read the current window */
+ SVN_ERR(rs_aligned_seek(rs, NULL, start_offset, pool));
+ SVN_ERR(svn_txdelta_read_svndiff_window(&window,
+ rs->sfile->rfile->stream,
+ rs->ver, pool));
+
+ /* aggregate expanded window size */
+ *fulltext_len += window->tview_len;
+
+ /* determine on-disk window size */
+ SVN_ERR(get_file_offset(&end_offset, rs, pool));
+ rs->current = end_offset - rs->start;
+ if (rs->current > rs->size)
+ return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+ _("Reading one svndiff window read beyond "
+ "the end of the representation"));
+
+ /* cache the window now */
+ SVN_ERR(set_cached_window(window, rs, pool));
+
+ rs->chunk_index++;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Read all txdelta / plain windows following REP_HEADER in FS as described
+ * by ENTRY. Read the data from the already open FILE and the wrapping
+ * STREAM object. Use POOL for allocations.
+ * If caching is not enabled, this is a no-op.
+ */
+static svn_error_t *
+block_read_windows(svn_fs_fs__rep_header_t *rep_header,
+ svn_fs_t *fs,
+ svn_fs_fs__revision_file_t *rev_file,
+ svn_fs_fs__p2l_entry_t* entry,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ rep_state_t rs = { 0 };
+ apr_off_t offset;
+ window_cache_key_t key = { 0 };
+
+ if ( (rep_header->type != svn_fs_fs__rep_plain
+ && !ffd->txdelta_window_cache)
+ || (rep_header->type == svn_fs_fs__rep_plain
+ && !ffd->combined_window_cache))
+ return SVN_NO_ERROR;
+
+ SVN_ERR(init_rep_state(&rs, rep_header, fs, rev_file, entry, pool));
+
+ /* RS->FILE may be shared between RS instances -> make sure we point
+ * to the right data. */
+ offset = rs.start + rs.current;
+ if (rep_header->type == svn_fs_fs__rep_plain)
+ {
+ svn_stringbuf_t *plaintext;
+ svn_boolean_t is_cached;
+
+ /* already in cache? */
+ SVN_ERR(svn_cache__has_key(&is_cached, rs.combined_cache,
+ get_window_key(&key, &rs), pool));
+ if (is_cached)
return SVN_NO_ERROR;
+
+ /* for larger reps, the header may have crossed a block boundary.
+ * make sure we still read blocks properly aligned, i.e. don't use
+ * plain seek here. */
+ SVN_ERR(aligned_seek(fs, rev_file->file, NULL, offset, pool));
+
+ plaintext = svn_stringbuf_create_ensure(rs.size, pool);
+ SVN_ERR(svn_io_file_read_full2(rev_file->file, plaintext->data,
+ rs.size, &plaintext->len, NULL, pool));
+ plaintext->data[plaintext->len] = 0;
+ rs.current += rs.size;
+
+ SVN_ERR(set_cached_combined_window(plaintext, &rs, pool));
+ }
+ else
+ {
+ svn_filesize_t fulltext_len;
+ SVN_ERR(cache_windows(&fulltext_len, fs, &rs, pool));
}
- /* read changes from revision file */
+ return SVN_NO_ERROR;
+}
- SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, pool));
- SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&revision_file, fs, rev, pool));
+/* Try to get the representation header identified by KEY from FS's cache.
+ * If it has not been cached, read it from the current position in STREAM
+ * and put it into the cache (if caching has been enabled for rep headers).
+ * Return the result in *REP_HEADER. Use POOL for allocations.
+ */
+static svn_error_t *
+read_rep_header(svn_fs_fs__rep_header_t **rep_header,
+ svn_fs_t *fs,
+ svn_stream_t *stream,
+ representation_cache_key_t *key,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ svn_boolean_t is_cached = FALSE;
+
+ if (ffd->rep_header_cache)
+ {
+ SVN_ERR(svn_cache__get((void**)rep_header, &is_cached,
+ ffd->rep_header_cache, key, pool));
+ if (is_cached)
+ return SVN_NO_ERROR;
+ }
- SVN_ERR(get_root_changes_offset(NULL, &changes_offset, revision_file, fs,
- rev, pool));
+ SVN_ERR(svn_fs_fs__read_rep_header(rep_header, stream, pool));
- SVN_ERR(aligned_seek(revision_file, changes_offset, pool));
- SVN_ERR(svn_fs_fs__read_changes(changes,
- svn_stream_from_aprfile2(revision_file, TRUE, pool),
- pool));
+ if (ffd->rep_header_cache)
+ SVN_ERR(svn_cache__set(ffd->rep_header_cache, key, *rep_header, pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* Fetch the representation data (header, txdelta / plain windows)
+ * addressed by ENTRY->ITEM in FS and cache it if caches are enabled.
+ * Read the data from the already open FILE and the wrapping
+ * STREAM object. Use POOL for allocations.
+ */
+static svn_error_t *
+block_read_contents(svn_fs_t *fs,
+ svn_fs_fs__revision_file_t *rev_file,
+ svn_fs_fs__p2l_entry_t* entry,
+ apr_pool_t *pool)
+{
+ representation_cache_key_t header_key = { 0 };
+ svn_fs_fs__rep_header_t *rep_header;
+
+ header_key.revision = (apr_int32_t)entry->item.revision;
+ header_key.is_packed = svn_fs_fs__is_packed_rev(fs, header_key.revision);
+ header_key.item_index = entry->item.number;
+
+ SVN_ERR(read_rep_header(&rep_header, fs, rev_file->stream, &header_key,
+ pool));
+ SVN_ERR(block_read_windows(rep_header, fs, rev_file, entry, pool));
+
+ return SVN_NO_ERROR;
+}
- SVN_ERR(svn_io_file_close(revision_file, pool));
+/* For the given FILE in FS and wrapping FILE_STREAM return the stream
+ * object in *STREAM that has the lowest performance overhead for reading
+ * and parsing the item addressed by ENTRY. Use POOL for allocations.
+ */
+static svn_error_t *
+auto_select_stream(svn_stream_t **stream,
+ svn_fs_t *fs,
+ svn_fs_fs__revision_file_t *rev_file,
+ svn_fs_fs__p2l_entry_t* entry,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+
+ /* Item parser might be crossing block boundaries? */
+ if (((entry->offset + entry->size + SVN__LINE_CHUNK_SIZE) ^ entry->offset)
+ >= ffd->block_size)
+ {
+ /* Parsing items that cross block boundaries will cause the file
+ buffer to be re-read and misaligned. So, read the whole block
+ into memory - it must fit anyways since the parsed object will
+ be even larger.
+ */
+ svn_stringbuf_t *text = svn_stringbuf_create_ensure(entry->size, pool);
+ text->len = entry->size;
+ text->data[text->len] = 0;
+ SVN_ERR(svn_io_file_read_full2(rev_file->file, text->data, text->len,
+ NULL, NULL, pool));
+ *stream = svn_stream_from_stringbuf(text, pool);
+ }
+ else
+ {
+ *stream = rev_file->stream;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* If not already cached or if MUST_READ is set, read the changed paths
+ * list addressed by ENTRY in FS and retúrn it in *CHANGES. Cache the
+ * result if caching is enabled. Read the data from the already open
+ * FILE and wrapping FILE_STREAM. Use POOL for allocations.
+ */
+static svn_error_t *
+block_read_changes(apr_array_header_t **changes,
+ svn_fs_t *fs,
+ svn_fs_fs__revision_file_t *rev_file,
+ svn_fs_fs__p2l_entry_t *entry,
+ svn_boolean_t must_read,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ svn_stream_t *stream;
+ if (!must_read && !ffd->changes_cache)
+ return SVN_NO_ERROR;
+
+ /* already in cache? */
+ if (!must_read && ffd->changes_cache)
+ {
+ svn_boolean_t is_cached;
+ SVN_ERR(svn_cache__has_key(&is_cached, ffd->changes_cache,
+ &entry->item.revision, pool));
+ if (is_cached)
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(auto_select_stream(&stream, fs, rev_file, entry, pool));
+
+ /* read changes from revision file */
+
+ SVN_ERR(svn_fs_fs__read_changes(changes, stream, pool));
/* cache for future reference */
if (ffd->changes_cache)
- SVN_ERR(svn_cache__set(ffd->changes_cache, &rev, *changes, pool));
+ SVN_ERR(svn_cache__set(ffd->changes_cache, &entry->item.revision,
+ *changes, pool));
- SVN_ERR(dbg_log_access(fs, rev, changes_offset, *changes,
- SVN_FS_FS__ITEM_TYPE_CHANGES, pool));
+ return SVN_NO_ERROR;
+}
+
+/* If not already cached or if MUST_READ is set, read the nod revision
+ * addressed by ENTRY in FS and retúrn it in *NODEREV_P. Cache the
+ * result if caching is enabled. Read the data from the already open
+ * FILE and wrapping FILE_STREAM. Use POOL for allocations.
+ */
+static svn_error_t *
+block_read_noderev(node_revision_t **noderev_p,
+ svn_fs_t *fs,
+ svn_fs_fs__revision_file_t *rev_file,
+ svn_fs_fs__p2l_entry_t *entry,
+ svn_boolean_t must_read,
+ apr_pool_t *pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ svn_stream_t *stream;
+
+ pair_cache_key_t key = { 0 };
+ key.revision = entry->item.revision;
+ key.second = entry->item.number;
+
+ if (!must_read && !ffd->node_revision_cache)
+ return SVN_NO_ERROR;
+
+ /* already in cache? */
+ if (!must_read && ffd->node_revision_cache)
+ {
+ svn_boolean_t is_cached;
+ SVN_ERR(svn_cache__has_key(&is_cached, ffd->node_revision_cache,
+ &key, pool));
+ if (is_cached)
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(auto_select_stream(&stream, fs, rev_file, entry, pool));
+
+ /* read node rev from revision file */
+
+ SVN_ERR(svn_fs_fs__read_noderev(noderev_p, stream, pool));
+
+ /* Workaround issue #4031: is-fresh-txn-root in revision files. */
+ (*noderev_p)->is_fresh_txn_root = FALSE;
+
+ if (ffd->node_revision_cache)
+ SVN_ERR(svn_cache__set(ffd->node_revision_cache, &key, *noderev_p,
+ pool));
return SVN_NO_ERROR;
}
+/* Read the whole (e.g. 64kB) block containing ITEM_INDEX of REVISION in FS
+ * and put all data into cache. If necessary and depending on heuristics,
+ * neighboring blocks may also get read. The data is being read from
+ * already open REVISION_FILE, which must be the correct rev / pack file
+ * w.r.t. REVISION.
+ *
+ * For noderevs and changed path lists, the item fetched can be allocated
+ * RESULT_POOL and returned in *RESULT. Otherwise, RESULT must be NULL.
+ */
+static svn_error_t *
+block_read(void **result,
+ svn_fs_t *fs,
+ svn_revnum_t revision,
+ apr_uint64_t item_index,
+ svn_fs_fs__revision_file_t *revision_file,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ fs_fs_data_t *ffd = fs->fsap_data;
+ apr_off_t offset, wanted_offset = 0;
+ apr_off_t block_start = 0;
+ apr_array_header_t *entries;
+ int run_count = 0;
+ int i;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+ /* don't try this on transaction protorev files */
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
+
+ /* index lookup: find the OFFSET of the item we *must* read plus (in the
+ * "do-while" block) the list of items in the same block. */
+ SVN_ERR(svn_fs_fs__item_offset(&wanted_offset, fs, revision_file,
+ revision, NULL, item_index, iterpool));
+
+ offset = wanted_offset;
+
+ /* Heuristics:
+ *
+ * Read this block. If the last item crosses the block boundary, read
+ * the next block but stop there. Because cross-boundary items cause
+ * blocks to be read twice, this heuristics will limit this effect to
+ * approx. 50% of blocks, probably less, while providing a sensible
+ * amount of read-ahead.
+ */
+ do
+ {
+ /* fetch list of items in the block surrounding OFFSET */
+ svn_error_t *err
+ = svn_fs_fs__p2l_index_lookup(&entries, fs, revision_file,
+ revision, offset, scratch_pool);
+
+ /* if the revision got packed in the meantime and we still need need
+ * to actually read some item, we retry the whole process */
+ if (err &&
+ revision_file->is_packed != svn_fs_fs__is_packed_rev(fs, revision))
+ {
+ if (result && !*result)
+ {
+ SVN_ERR(svn_fs_fs__reopen_revision_file(revision_file, fs,
+ revision));
+ SVN_ERR(block_read(result, fs, revision, item_index,
+ revision_file, result_pool, scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(err);
+ SVN_ERR(aligned_seek(fs, revision_file->file, &block_start, offset,
+ iterpool));
+
+ /* read all items from the block */
+ for (i = 0; i < entries->nelts; ++i)
+ {
+ svn_boolean_t is_result;
+ apr_pool_t *pool;
+ svn_fs_fs__p2l_entry_t* entry;
+
+ svn_pool_clear(iterpool);
+
+ /* skip empty sections */
+ entry = &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t);
+ if (entry->type == SVN_FS_FS__ITEM_TYPE_UNUSED)
+ continue;
+
+ /* the item / container we were looking for? */
+ is_result = result
+ && entry->offset == wanted_offset
+ && entry->item.revision == revision
+ && entry->item.number == item_index;
+
+ /* select the pool that we want the item to be allocated in */
+ pool = is_result ? result_pool : iterpool;
+
+ /* handle all items that start within this block and are relatively
+ * small (i.e. < block size). Always read the item we need to return.
+ */
+ if (is_result || ( entry->offset >= block_start
+ && entry->size < ffd->block_size))
+ {
+ void *item = NULL;
+ SVN_ERR(svn_io_file_seek(revision_file->file, SEEK_SET,
+ &entry->offset, iterpool));
+ switch (entry->type)
+ {
+ case SVN_FS_FS__ITEM_TYPE_FILE_REP:
+ case SVN_FS_FS__ITEM_TYPE_DIR_REP:
+ case SVN_FS_FS__ITEM_TYPE_FILE_PROPS:
+ case SVN_FS_FS__ITEM_TYPE_DIR_PROPS:
+ SVN_ERR(block_read_contents(fs, revision_file, entry,
+ pool));
+ break;
+
+ case SVN_FS_FS__ITEM_TYPE_NODEREV:
+ if (ffd->node_revision_cache || is_result)
+ SVN_ERR(block_read_noderev((node_revision_t **)&item,
+ fs, revision_file,
+ entry, is_result, pool));
+ break;
+
+ case SVN_FS_FS__ITEM_TYPE_CHANGES:
+ SVN_ERR(block_read_changes((apr_array_header_t **)&item,
+ fs, revision_file,
+ entry, is_result, pool));
+ break;
+
+ default:
+ break;
+ }
+
+ if (is_result)
+ *result = item;
+
+ /* if we crossed a block boundary, read the remainder of
+ * the last block as well */
+ offset = entry->offset + entry->size;
+ if (offset > block_start + ffd->block_size)
+ ++run_count;
+ }
+ }
+
+ }
+ while(run_count++ == 1); /* can only be true once and only if a block
+ * boundary got crossed */
+
+ /* if the caller requested a result, we must have provided one by now */
+ assert(!result || *result);
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}