2 * ra_plugin.c : the main RA module for local repository access
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
28 #include "svn_delta.h"
29 #include "svn_repos.h"
30 #include "svn_pools.h"
32 #include "svn_props.h"
33 #include "svn_mergeinfo.h"
35 #include "svn_version.h"
36 #include "svn_cache_config.h"
38 #include "svn_private_config.h"
39 #include "../libsvn_ra/ra_loader.h"
40 #include "private/svn_mergeinfo_private.h"
41 #include "private/svn_repos_private.h"
42 #include "private/svn_fspath.h"
43 #include "private/svn_atomic.h"
44 #include "private/svn_subr_private.h"
46 #define APR_WANT_STRFUNC
49 /*----------------------------------------------------------------*/
51 /*** Miscellaneous helper functions ***/
54 /* Pool cleanup handler: ensure that the access descriptor of the
55 filesystem (svn_fs_t *) DATA is set to NULL. */
57 cleanup_access(void *data)
62 serr = svn_fs_set_access(fs, NULL);
66 apr_status_t apr_err = serr->apr_err;
67 svn_error_clear(serr);
75 /* Fetch a username for use with SESSION, and store it in SESSION->username.
77 * Allocate the username in SESSION->pool. Use SCRATCH_POOL for temporary
80 get_username(svn_ra_session_t *session,
81 apr_pool_t *scratch_pool)
83 svn_ra_local__session_baton_t *sess = session->priv;
85 /* If we've already found the username don't ask for it again. */
88 /* Get a username somehow, so we have some svn:author property to
89 attach to a commit. */
93 svn_auth_cred_username_t *username_creds;
94 svn_auth_iterstate_t *iterstate;
96 SVN_ERR(svn_auth_first_credentials(&creds, &iterstate,
97 SVN_AUTH_CRED_USERNAME,
98 sess->uuid, /* realmstring */
102 /* No point in calling next_creds(), since that assumes that the
103 first_creds() somehow failed to authenticate. But there's no
104 challenge going on, so we use whatever creds we get back on
106 username_creds = creds;
107 if (username_creds && username_creds->username)
109 sess->username = apr_pstrdup(session->pool,
110 username_creds->username);
111 svn_error_clear(svn_auth_save_credentials(iterstate,
121 /* If we have a real username, attach it to the filesystem so that it can
122 be used to validate locks. Even if there already is a user context
123 associated, it may contain irrelevant lock tokens, so always create a new.
127 svn_fs_access_t *access_ctx;
129 SVN_ERR(svn_fs_create_access(&access_ctx, sess->username,
131 SVN_ERR(svn_fs_set_access(sess->fs, access_ctx));
133 /* Make sure this context is disassociated when the pool gets
135 apr_pool_cleanup_register(session->pool, sess->fs, cleanup_access,
136 apr_pool_cleanup_null);
142 /* Implements an svn_atomic__init_once callback. Sets the FSFS memory
145 cache_init(void *baton, apr_pool_t *pool)
147 apr_hash_t *config_hash = baton;
148 svn_config_t *config = NULL;
149 const char *memory_cache_size_str;
152 config = svn_hash_gets(config_hash, SVN_CONFIG_CATEGORY_CONFIG);
153 svn_config_get(config, &memory_cache_size_str, SVN_CONFIG_SECTION_MISCELLANY,
154 SVN_CONFIG_OPTION_MEMORY_CACHE_SIZE, NULL);
155 if (memory_cache_size_str)
157 apr_uint64_t memory_cache_size;
158 svn_cache_config_t settings = *svn_cache_config_get();
160 SVN_ERR(svn_error_quick_wrap(svn_cstring_atoui64(&memory_cache_size,
161 memory_cache_size_str),
162 _("memory-cache-size invalid")));
163 settings.cache_size = 1024 * 1024 * memory_cache_size;
164 svn_cache_config_set(&settings);
170 /*----------------------------------------------------------------*/
172 /*** The reporter vtable needed by do_update() and friends ***/
174 typedef struct reporter_baton_t
176 svn_ra_local__session_baton_t *sess;
183 make_reporter_baton(svn_ra_local__session_baton_t *sess,
187 reporter_baton_t *rbaton = apr_palloc(pool, sizeof(*rbaton));
189 rbaton->report_baton = report_baton;
195 reporter_set_path(void *reporter_baton,
197 svn_revnum_t revision,
199 svn_boolean_t start_empty,
200 const char *lock_token,
203 reporter_baton_t *rbaton = reporter_baton;
204 return svn_repos_set_path3(rbaton->report_baton, path,
205 revision, depth, start_empty, lock_token, pool);
210 reporter_delete_path(void *reporter_baton,
214 reporter_baton_t *rbaton = reporter_baton;
215 return svn_repos_delete_path(rbaton->report_baton, path, pool);
220 reporter_link_path(void *reporter_baton,
223 svn_revnum_t revision,
225 svn_boolean_t start_empty,
226 const char *lock_token,
229 reporter_baton_t *rbaton = reporter_baton;
230 const char *repos_url = rbaton->sess->repos_url;
231 const char *relpath = svn_uri_skip_ancestor(repos_url, url, pool);
235 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
237 "is not the same repository as\n"
238 "'%s'"), url, rbaton->sess->repos_url);
240 /* Convert the relpath to an fspath */
241 if (relpath[0] == '\0')
244 fs_path = apr_pstrcat(pool, "/", relpath, SVN_VA_NULL);
246 return svn_repos_link_path3(rbaton->report_baton, path, fs_path, revision,
247 depth, start_empty, lock_token, pool);
252 reporter_finish_report(void *reporter_baton,
255 reporter_baton_t *rbaton = reporter_baton;
256 return svn_repos_finish_report(rbaton->report_baton, pool);
261 reporter_abort_report(void *reporter_baton,
264 reporter_baton_t *rbaton = reporter_baton;
265 return svn_repos_abort_report(rbaton->report_baton, pool);
269 static const svn_ra_reporter3_t ra_local_reporter =
272 reporter_delete_path,
274 reporter_finish_report,
275 reporter_abort_report
281 * Wrap a cancellation editor using SESSION's cancellation function around
282 * the supplied EDITOR. ### Some callers (via svn_ra_do_update2() etc.)
283 * don't appear to know that we do this, and are supplying an editor that
284 * they have already wrapped with the same cancellation editor, so it ends
287 * Allocate @a *reporter and @a *report_baton in @a result_pool. Use
288 * @a scratch_pool for temporary allocations.
291 make_reporter(svn_ra_session_t *session,
292 const svn_ra_reporter3_t **reporter,
294 svn_revnum_t revision,
296 const char *other_url,
297 svn_boolean_t text_deltas,
299 svn_boolean_t send_copyfrom_args,
300 svn_boolean_t ignore_ancestry,
301 const svn_delta_editor_t *editor,
303 apr_pool_t *result_pool,
304 apr_pool_t *scratch_pool)
306 svn_ra_local__session_baton_t *sess = session->priv;
308 const char *other_fs_path = NULL;
310 /* Get the HEAD revision if one is not supplied. */
311 if (! SVN_IS_VALID_REVNUM(revision))
312 SVN_ERR(svn_fs_youngest_rev(&revision, sess->fs, scratch_pool));
314 /* If OTHER_URL was provided, validate it and convert it into a
315 regular filesystem path. */
318 const char *other_relpath
319 = svn_uri_skip_ancestor(sess->repos_url, other_url, scratch_pool);
321 /* Sanity check: the other_url better be in the same repository as
322 the original session url! */
324 return svn_error_createf
325 (SVN_ERR_RA_ILLEGAL_URL, NULL,
327 "is not the same repository as\n"
328 "'%s'"), other_url, sess->repos_url);
330 other_fs_path = apr_pstrcat(scratch_pool, "/", other_relpath,
334 /* Pass back our reporter */
335 *reporter = &ra_local_reporter;
337 SVN_ERR(get_username(session, scratch_pool));
340 SVN_ERR(svn_delta_get_cancellation_editor(sess->callbacks->cancel_func,
341 sess->callback_baton,
348 /* Build a reporter baton. */
349 SVN_ERR(svn_repos_begin_report3(&rbaton,
363 0, /* Disable zero-copy codepath, because
364 RA API users are unaware about the
365 zero-copy code path limitation (do
366 not access FSFS data structures
367 and, hence, caches). See notes
368 to svn_repos_begin_report3() for
369 additional details. */
372 /* Wrap the report baton given us by the repos layer with our own
374 *report_baton = make_reporter_baton(sess, rbaton, result_pool);
380 /*----------------------------------------------------------------*/
382 /*** Deltification stuff for get_commit_editor() ***/
384 struct deltify_etc_baton
386 svn_fs_t *fs; /* the fs to deltify in */
387 svn_repos_t *repos; /* repos for unlocking */
388 const char *fspath_base; /* fs-path part of split session URL */
390 apr_hash_t *lock_tokens; /* tokens to unlock, if any */
392 svn_commit_callback2_t commit_cb; /* the original callback */
393 void *commit_baton; /* the original callback's baton */
396 /* This implements 'svn_commit_callback_t'. Its invokes the original
397 (wrapped) callback, but also does deltification on the new revision and
398 possibly unlocks committed paths.
399 BATON is 'struct deltify_etc_baton *'. */
401 deltify_etc(const svn_commit_info_t *commit_info,
403 apr_pool_t *scratch_pool)
405 struct deltify_etc_baton *deb = baton;
406 svn_error_t *err1 = SVN_NO_ERROR;
409 /* Invoke the original callback first, in case someone's waiting to
410 know the revision number so they can go off and annotate an
411 issue or something. */
413 err1 = deb->commit_cb(commit_info, deb->commit_baton, scratch_pool);
415 /* Maybe unlock the paths. */
416 if (deb->lock_tokens)
418 apr_pool_t *subpool = svn_pool_create(scratch_pool);
419 apr_hash_t *targets = apr_hash_make(subpool);
420 apr_hash_index_t *hi;
422 for (hi = apr_hash_first(subpool, deb->lock_tokens); hi;
423 hi = apr_hash_next(hi))
425 const void *relpath = apr_hash_this_key(hi);
426 const char *token = apr_hash_this_val(hi);
429 fspath = svn_fspath__join(deb->fspath_base, relpath, subpool);
430 svn_hash_sets(targets, fspath, token);
433 /* We may get errors here if the lock was broken or stolen
434 after the commit succeeded. This is fine and should be
436 svn_error_clear(svn_repos_fs_unlock_many(deb->repos, targets, FALSE,
440 svn_pool_destroy(subpool);
443 /* But, deltification shouldn't be stopped just because someone's
444 random callback failed, so proceed unconditionally on to
446 err2 = svn_fs_deltify_revision(deb->fs, commit_info->revision, scratch_pool);
448 return svn_error_compose_create(err1, err2);
452 /* If LOCK_TOKENS is not NULL, then copy all tokens into the access context
453 of FS. The tokens' paths will be prepended with FSPATH_BASE.
455 ACCESS_POOL must match (or exceed) the lifetime of the access context
456 that was associated with FS. Typically, this is the session pool.
458 Temporary allocations are made in SCRATCH_POOL. */
460 apply_lock_tokens(svn_fs_t *fs,
461 const char *fspath_base,
462 apr_hash_t *lock_tokens,
463 apr_pool_t *access_pool,
464 apr_pool_t *scratch_pool)
468 svn_fs_access_t *access_ctx;
470 SVN_ERR(svn_fs_get_access(&access_ctx, fs));
472 /* If there is no access context, the filesystem will scream if a
476 apr_hash_index_t *hi;
478 /* Note: we have no use for an iterpool here since the data
479 within the loop is copied into ACCESS_POOL. */
481 for (hi = apr_hash_first(scratch_pool, lock_tokens); hi;
482 hi = apr_hash_next(hi))
484 const void *relpath = apr_hash_this_key(hi);
485 const char *token = apr_hash_this_val(hi);
488 /* The path needs to live as long as ACCESS_CTX. */
489 fspath = svn_fspath__join(fspath_base, relpath, access_pool);
491 /* The token must live as long as ACCESS_CTX. */
492 token = apr_pstrdup(access_pool, token);
494 SVN_ERR(svn_fs_access_add_lock_token2(access_ctx, fspath,
504 /*----------------------------------------------------------------*/
506 /*** The RA vtable routines ***/
508 #define RA_LOCAL_DESCRIPTION \
509 N_("Module for accessing a repository on local disk.")
512 svn_ra_local__get_description(apr_pool_t *pool)
514 return _(RA_LOCAL_DESCRIPTION);
517 static const char * const *
518 svn_ra_local__get_schemes(apr_pool_t *pool)
520 static const char *schemes[] = { "file", NULL };
527 * Why is this acceptable? FS warnings used to be used for only
528 * two things: failures to close BDB repositories and failures to
529 * interact with memcached in FSFS (new in 1.6). In 1.5 and earlier,
530 * we did not call svn_fs_set_warning_func in ra_local, which means
531 * that any BDB-closing failure would have led to abort()s; the fact
532 * that this hasn't led to huge hues and cries makes it seem likely
533 * that this just doesn't happen that often, at least not through
534 * ra_local. And as far as memcached goes, it seems unlikely that
535 * somebody is going to go through the trouble of setting up and
536 * running memcached servers but then use ra_local access. So we
537 * ignore errors here, so that memcached can use the FS warnings API
538 * without crashing ra_local.
541 ignore_warnings(void *baton,
545 SVN_DBG(("Ignoring FS warning %s\n",
546 svn_error_symbolic_name(err ? err->apr_err : 0)));
551 #define USER_AGENT "SVN/" SVN_VER_NUMBER " (" SVN_BUILD_TARGET ")" \
555 svn_ra_local__open(svn_ra_session_t *session,
556 const char **corrected_url,
557 const char **redirect_url,
558 const char *repos_URL,
559 const svn_ra_callbacks2_t *callbacks,
560 void *callback_baton,
561 svn_auth_baton_t *auth_baton,
563 apr_pool_t *result_pool,
564 apr_pool_t *scratch_pool)
566 const char *client_string;
567 svn_ra_local__session_baton_t *sess;
569 static volatile svn_atomic_t cache_init_state = 0;
570 apr_pool_t *pool = result_pool;
572 /* Initialise the FSFS memory cache size. We can only do this once
573 so one CONFIG will win the race and all others will be ignored
575 SVN_ERR(svn_atomic__init_once(&cache_init_state, cache_init, config, pool));
577 /* We don't support redirections in ra-local. */
579 *corrected_url = NULL;
581 *redirect_url = NULL;
583 /* Allocate and stash the session_sess args we have already. */
584 sess = apr_pcalloc(pool, sizeof(*sess));
585 sess->callbacks = callbacks;
586 sess->callback_baton = callback_baton;
587 sess->auth_baton = auth_baton;
589 /* Look through the URL, figure out which part points to the
590 repository, and which part is the path *within* the
592 SVN_ERR(svn_ra_local__split_URL(&(sess->repos),
597 sess->fs_path = svn_stringbuf_create(fs_path, session->pool);
599 /* Cache the filesystem object from the repos here for
601 sess->fs = svn_repos_fs(sess->repos);
603 /* Ignore FS warnings. */
604 svn_fs_set_warning_func(sess->fs, ignore_warnings, NULL);
606 /* Cache the repository UUID as well */
607 SVN_ERR(svn_fs_get_uuid(sess->fs, &sess->uuid, session->pool));
609 /* Be sure username is NULL so we know to look it up / ask for it */
610 sess->username = NULL;
612 if (sess->callbacks->get_client_string != NULL)
613 SVN_ERR(sess->callbacks->get_client_string(sess->callback_baton,
614 &client_string, pool));
616 client_string = NULL;
619 sess->useragent = apr_pstrcat(pool, USER_AGENT " ",
620 client_string, SVN_VA_NULL);
622 sess->useragent = USER_AGENT;
624 session->priv = sess;
629 svn_ra_local__dup_session(svn_ra_session_t *new_session,
630 svn_ra_session_t *session,
631 const char *new_session_url,
632 apr_pool_t *result_pool,
633 apr_pool_t *scratch_pool)
635 svn_ra_local__session_baton_t *old_sess = session->priv;
636 svn_ra_local__session_baton_t *new_sess;
639 /* Allocate and stash the session_sess args we have already. */
640 new_sess = apr_pcalloc(result_pool, sizeof(*new_sess));
641 new_sess->callbacks = old_sess->callbacks;
642 new_sess->callback_baton = old_sess->callback_baton;
644 /* ### Re-use existing FS handle? */
646 /* Reuse existing code */
647 SVN_ERR(svn_ra_local__split_URL(&(new_sess->repos),
648 &(new_sess->repos_url),
653 new_sess->fs_path = svn_stringbuf_create(fs_path, result_pool);
655 /* Cache the filesystem object from the repos here for
657 new_sess->fs = svn_repos_fs(new_sess->repos);
659 /* Ignore FS warnings. */
660 svn_fs_set_warning_func(new_sess->fs, ignore_warnings, NULL);
662 /* Cache the repository UUID as well */
663 new_sess->uuid = apr_pstrdup(result_pool, old_sess->uuid);
665 new_sess->username = old_sess->username
666 ? apr_pstrdup(result_pool, old_sess->username)
669 new_sess->useragent = apr_pstrdup(result_pool, old_sess->useragent);
670 new_session->priv = new_sess;
676 svn_ra_local__reparent(svn_ra_session_t *session,
680 svn_ra_local__session_baton_t *sess = session->priv;
681 const char *relpath = svn_uri_skip_ancestor(sess->repos_url, url, pool);
683 /* If the new URL isn't the same as our repository root URL, then
684 let's ensure that it's some child of it. */
686 return svn_error_createf
687 (SVN_ERR_RA_ILLEGAL_URL, NULL,
688 _("URL '%s' is not a child of the session's repository root "
689 "URL '%s'"), url, sess->repos_url);
691 /* Update our FS_PATH sess member to point to our new
692 relative-URL-turned-absolute-filesystem-path. */
693 svn_stringbuf_set(sess->fs_path,
694 svn_fspath__canonicalize(relpath, pool));
700 svn_ra_local__get_session_url(svn_ra_session_t *session,
704 svn_ra_local__session_baton_t *sess = session->priv;
705 *url = svn_path_url_add_component2(sess->repos_url,
706 sess->fs_path->data + 1,
712 svn_ra_local__get_latest_revnum(svn_ra_session_t *session,
713 svn_revnum_t *latest_revnum,
716 svn_ra_local__session_baton_t *sess = session->priv;
717 return svn_fs_youngest_rev(latest_revnum, sess->fs, pool);
721 svn_ra_local__get_file_revs(svn_ra_session_t *session,
725 svn_boolean_t include_merged_revisions,
726 svn_file_rev_handler_t handler,
730 svn_ra_local__session_baton_t *sess = session->priv;
731 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
732 return svn_repos_get_file_revs2(sess->repos, abs_path, start, end,
733 include_merged_revisions, NULL, NULL,
734 handler, handler_baton, pool);
738 svn_ra_local__get_dated_revision(svn_ra_session_t *session,
739 svn_revnum_t *revision,
743 svn_ra_local__session_baton_t *sess = session->priv;
744 return svn_repos_dated_revision(revision, sess->repos, tm, pool);
749 svn_ra_local__change_rev_prop(svn_ra_session_t *session,
752 const svn_string_t *const *old_value_p,
753 const svn_string_t *value,
756 svn_ra_local__session_baton_t *sess = session->priv;
758 SVN_ERR(get_username(session, pool));
759 return svn_repos_fs_change_rev_prop4(sess->repos, rev, sess->username,
760 name, old_value_p, value, TRUE, TRUE,
765 svn_ra_local__get_uuid(svn_ra_session_t *session,
769 svn_ra_local__session_baton_t *sess = session->priv;
775 svn_ra_local__get_repos_root(svn_ra_session_t *session,
779 svn_ra_local__session_baton_t *sess = session->priv;
780 *url = sess->repos_url;
785 svn_ra_local__rev_proplist(svn_ra_session_t *session,
790 svn_ra_local__session_baton_t *sess = session->priv;
791 return svn_repos_fs_revision_proplist(props, sess->repos, rev,
796 svn_ra_local__rev_prop(svn_ra_session_t *session,
799 svn_string_t **value,
802 svn_ra_local__session_baton_t *sess = session->priv;
803 return svn_repos_fs_revision_prop(value, sess->repos, rev, name,
809 svn_commit_callback2_t original_callback;
810 void *original_baton;
812 svn_ra_session_t *session;
815 /* Wrapper which populates the repos_root field of the commit_info struct */
817 commit_callback_wrapper(const svn_commit_info_t *commit_info,
819 apr_pool_t *scratch_pool)
821 struct ccw_baton *ccwb = baton;
822 svn_commit_info_t *ci = svn_commit_info_dup(commit_info, scratch_pool);
824 SVN_ERR(svn_ra_local__get_repos_root(ccwb->session, &ci->repos_root,
827 return svn_error_trace(ccwb->original_callback(ci, ccwb->original_baton,
832 /* The repository layer does not correctly fill in REPOS_ROOT in
833 commit_info, as it doesn't know the url that is used to access
834 it. This hooks the callback to fill in the missing pieces. */
836 remap_commit_callback(svn_commit_callback2_t *callback,
837 void **callback_baton,
838 svn_ra_session_t *session,
839 svn_commit_callback2_t original_callback,
840 void *original_baton,
841 apr_pool_t *result_pool)
843 if (original_callback == NULL)
846 *callback_baton = NULL;
850 /* Allocate this in RESULT_POOL, since the callback will be called
851 long after this function has returned. */
852 struct ccw_baton *ccwb = apr_palloc(result_pool, sizeof(*ccwb));
854 ccwb->session = session;
855 ccwb->original_callback = original_callback;
856 ccwb->original_baton = original_baton;
858 *callback = commit_callback_wrapper;
859 *callback_baton = ccwb;
864 svn_ra_local__get_commit_editor(svn_ra_session_t *session,
865 const svn_delta_editor_t **editor,
867 apr_hash_t *revprop_table,
868 svn_commit_callback2_t callback,
869 void *callback_baton,
870 apr_hash_t *lock_tokens,
871 svn_boolean_t keep_locks,
874 svn_ra_local__session_baton_t *sess = session->priv;
875 struct deltify_etc_baton *deb = apr_palloc(pool, sizeof(*deb));
877 /* Set repos_root_url in commit info */
878 remap_commit_callback(&callback, &callback_baton, session,
879 callback, callback_baton, pool);
881 /* Prepare the baton for deltify_etc() */
883 deb->repos = sess->repos;
884 deb->fspath_base = sess->fs_path->data;
886 deb->lock_tokens = lock_tokens;
888 deb->lock_tokens = NULL;
889 deb->commit_cb = callback;
890 deb->commit_baton = callback_baton;
892 SVN_ERR(get_username(session, pool));
894 /* If there are lock tokens to add, do so. */
895 SVN_ERR(apply_lock_tokens(sess->fs, sess->fs_path->data, lock_tokens,
896 session->pool, pool));
898 /* Copy the revprops table so we can add the username. */
899 revprop_table = apr_hash_copy(pool, revprop_table);
900 svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR,
901 svn_string_create(sess->username, pool));
902 svn_hash_sets(revprop_table, SVN_PROP_TXN_CLIENT_COMPAT_VERSION,
903 svn_string_create(SVN_VER_NUMBER, pool));
904 svn_hash_sets(revprop_table, SVN_PROP_TXN_USER_AGENT,
905 svn_string_create(sess->useragent, pool));
907 /* Get the repos commit-editor */
908 return svn_repos_get_commit_editor5
909 (editor, edit_baton, sess->repos, NULL,
910 svn_path_uri_decode(sess->repos_url, pool), sess->fs_path->data,
911 revprop_table, deltify_etc, deb, NULL, NULL, pool);
915 /* Implements svn_repos_mergeinfo_receiver_t.
916 * It add MERGEINFO for PATH to the svn_mergeinfo_catalog_t BATON.
919 mergeinfo_receiver(const char *path,
920 svn_mergeinfo_t mergeinfo,
922 apr_pool_t *scratch_pool)
924 svn_mergeinfo_catalog_t catalog = baton;
925 apr_pool_t *result_pool = apr_hash_pool_get(catalog);
926 apr_size_t len = strlen(path);
928 apr_hash_set(catalog,
929 apr_pstrmemdup(result_pool, path, len),
931 svn_mergeinfo_dup(mergeinfo, result_pool));
938 svn_ra_local__get_mergeinfo(svn_ra_session_t *session,
939 svn_mergeinfo_catalog_t *catalog,
940 const apr_array_header_t *paths,
941 svn_revnum_t revision,
942 svn_mergeinfo_inheritance_t inherit,
943 svn_boolean_t include_descendants,
946 svn_ra_local__session_baton_t *sess = session->priv;
947 svn_mergeinfo_catalog_t tmp_catalog = svn_hash__make(pool);
949 apr_array_header_t *abs_paths =
950 apr_array_make(pool, 0, sizeof(const char *));
952 for (i = 0; i < paths->nelts; i++)
954 const char *relative_path = APR_ARRAY_IDX(paths, i, const char *);
955 APR_ARRAY_PUSH(abs_paths, const char *) =
956 svn_fspath__join(sess->fs_path->data, relative_path, pool);
959 SVN_ERR(svn_repos_fs_get_mergeinfo2(sess->repos, abs_paths, revision,
960 inherit, include_descendants,
962 mergeinfo_receiver, tmp_catalog,
964 if (apr_hash_count(tmp_catalog) > 0)
965 SVN_ERR(svn_mergeinfo__remove_prefix_from_catalog(catalog,
977 svn_ra_local__do_update(svn_ra_session_t *session,
978 const svn_ra_reporter3_t **reporter,
980 svn_revnum_t update_revision,
981 const char *update_target,
983 svn_boolean_t send_copyfrom_args,
984 svn_boolean_t ignore_ancestry,
985 const svn_delta_editor_t *update_editor,
987 apr_pool_t *result_pool,
988 apr_pool_t *scratch_pool)
990 return make_reporter(session,
1002 result_pool, scratch_pool);
1006 static svn_error_t *
1007 svn_ra_local__do_switch(svn_ra_session_t *session,
1008 const svn_ra_reporter3_t **reporter,
1009 void **report_baton,
1010 svn_revnum_t update_revision,
1011 const char *update_target,
1013 const char *switch_url,
1014 svn_boolean_t send_copyfrom_args,
1015 svn_boolean_t ignore_ancestry,
1016 const svn_delta_editor_t *update_editor,
1018 apr_pool_t *result_pool,
1019 apr_pool_t *scratch_pool)
1021 return make_reporter(session,
1027 TRUE /* text_deltas */,
1033 result_pool, scratch_pool);
1037 static svn_error_t *
1038 svn_ra_local__do_status(svn_ra_session_t *session,
1039 const svn_ra_reporter3_t **reporter,
1040 void **report_baton,
1041 const char *status_target,
1042 svn_revnum_t revision,
1044 const svn_delta_editor_t *status_editor,
1048 return make_reporter(session,
1064 static svn_error_t *
1065 svn_ra_local__do_diff(svn_ra_session_t *session,
1066 const svn_ra_reporter3_t **reporter,
1067 void **report_baton,
1068 svn_revnum_t update_revision,
1069 const char *update_target,
1071 svn_boolean_t ignore_ancestry,
1072 svn_boolean_t text_deltas,
1073 const char *switch_url,
1074 const svn_delta_editor_t *update_editor,
1078 return make_reporter(session,
1096 svn_ra_local__session_baton_t *sess;
1097 svn_log_entry_receiver_t real_cb;
1101 static svn_error_t *
1102 log_receiver_wrapper(void *baton,
1103 svn_log_entry_t *log_entry,
1106 struct log_baton *b = baton;
1107 svn_ra_local__session_baton_t *sess = b->sess;
1109 if (sess->callbacks->cancel_func)
1110 SVN_ERR((sess->callbacks->cancel_func)(sess->callback_baton));
1112 /* For consistency with the other RA layers, replace an empty
1113 changed-paths hash with a NULL one.
1115 ### Should this be done by svn_ra_get_log2() instead, then? */
1116 if ((log_entry->changed_paths2)
1117 && (apr_hash_count(log_entry->changed_paths2) == 0))
1119 log_entry->changed_paths = NULL;
1120 log_entry->changed_paths2 = NULL;
1123 return b->real_cb(b->real_baton, log_entry, pool);
1127 static svn_error_t *
1128 svn_ra_local__get_log(svn_ra_session_t *session,
1129 const apr_array_header_t *paths,
1133 svn_boolean_t discover_changed_paths,
1134 svn_boolean_t strict_node_history,
1135 svn_boolean_t include_merged_revisions,
1136 const apr_array_header_t *revprops,
1137 svn_log_entry_receiver_t receiver,
1138 void *receiver_baton,
1141 svn_ra_local__session_baton_t *sess = session->priv;
1142 struct log_baton lb;
1143 apr_array_header_t *abs_paths =
1144 apr_array_make(pool, 0, sizeof(const char *));
1150 for (i = 0; i < paths->nelts; i++)
1152 const char *relative_path = APR_ARRAY_IDX(paths, i, const char *);
1153 APR_ARRAY_PUSH(abs_paths, const char *) =
1154 svn_fspath__join(sess->fs_path->data, relative_path, pool);
1158 lb.real_cb = receiver;
1159 lb.real_baton = receiver_baton;
1161 receiver = log_receiver_wrapper;
1162 receiver_baton = &lb;
1164 return svn_repos__get_logs_compat(sess->repos,
1169 discover_changed_paths,
1170 strict_node_history,
1171 include_merged_revisions,
1180 static svn_error_t *
1181 svn_ra_local__do_check_path(svn_ra_session_t *session,
1183 svn_revnum_t revision,
1184 svn_node_kind_t *kind,
1187 svn_ra_local__session_baton_t *sess = session->priv;
1188 svn_fs_root_t *root;
1189 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1191 if (! SVN_IS_VALID_REVNUM(revision))
1192 SVN_ERR(svn_fs_youngest_rev(&revision, sess->fs, pool));
1193 SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool));
1194 return svn_fs_check_path(kind, root, abs_path, pool);
1198 static svn_error_t *
1199 svn_ra_local__stat(svn_ra_session_t *session,
1201 svn_revnum_t revision,
1202 svn_dirent_t **dirent,
1205 svn_ra_local__session_baton_t *sess = session->priv;
1206 svn_fs_root_t *root;
1207 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1209 if (! SVN_IS_VALID_REVNUM(revision))
1210 SVN_ERR(svn_fs_youngest_rev(&revision, sess->fs, pool));
1211 SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool));
1213 return svn_repos_stat(dirent, root, abs_path, pool);
1219 /* Obtain the properties for a node, including its 'entry props */
1220 static svn_error_t *
1221 get_node_props(apr_hash_t **props,
1222 svn_fs_root_t *root,
1225 apr_pool_t *result_pool,
1226 apr_pool_t *scratch_pool)
1228 svn_revnum_t cmt_rev;
1229 const char *cmt_date, *cmt_author;
1231 /* Create a hash with props attached to the fs node. */
1232 SVN_ERR(svn_fs_node_proplist(props, root, path, result_pool));
1234 /* Now add some non-tweakable metadata to the hash as well... */
1236 /* The so-called 'entryprops' with info about CR & friends. */
1237 SVN_ERR(svn_repos_get_committed_info(&cmt_rev, &cmt_date,
1238 &cmt_author, root, path,
1241 svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_REV,
1242 svn_string_createf(result_pool, "%ld", cmt_rev));
1243 svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_DATE, cmt_date ?
1244 svn_string_create(cmt_date, result_pool) : NULL);
1245 svn_hash_sets(*props, SVN_PROP_ENTRY_LAST_AUTHOR, cmt_author ?
1246 svn_string_create(cmt_author, result_pool) : NULL);
1247 svn_hash_sets(*props, SVN_PROP_ENTRY_UUID,
1248 svn_string_create(uuid, result_pool));
1250 /* We have no 'wcprops' in ra_local, but might someday. */
1252 return SVN_NO_ERROR;
1256 /* Getting just one file. */
1257 static svn_error_t *
1258 svn_ra_local__get_file(svn_ra_session_t *session,
1260 svn_revnum_t revision,
1261 svn_stream_t *stream,
1262 svn_revnum_t *fetched_rev,
1266 svn_fs_root_t *root;
1267 svn_stream_t *contents;
1268 svn_revnum_t youngest_rev;
1269 svn_ra_local__session_baton_t *sess = session->priv;
1270 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1271 svn_node_kind_t node_kind;
1273 /* Open the revision's root. */
1274 if (! SVN_IS_VALID_REVNUM(revision))
1276 SVN_ERR(svn_fs_youngest_rev(&youngest_rev, sess->fs, pool));
1277 SVN_ERR(svn_fs_revision_root(&root, sess->fs, youngest_rev, pool));
1278 if (fetched_rev != NULL)
1279 *fetched_rev = youngest_rev;
1282 SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool));
1284 SVN_ERR(svn_fs_check_path(&node_kind, root, abs_path, pool));
1285 if (node_kind == svn_node_none)
1287 return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
1288 _("'%s' path not found"), abs_path);
1290 else if (node_kind != svn_node_file)
1292 return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
1293 _("'%s' is not a file"), abs_path);
1298 /* Get a stream representing the file's contents. */
1299 SVN_ERR(svn_fs_file_contents(&contents, root, abs_path, pool));
1301 /* Now push data from the fs stream back at the caller's stream.
1302 Note that this particular RA layer does not computing a
1303 checksum as we go, and confirming it against the repository's
1304 checksum when done. That's because it calls
1305 svn_fs_file_contents() directly, which already checks the
1306 stored checksum, and all we're doing here is writing bytes in
1307 a loop. Truly, Nothing Can Go Wrong :-). But RA layers that
1308 go over a network should confirm the checksum.
1310 Note: we are not supposed to close the passed-in stream, so
1313 SVN_ERR(svn_stream_copy3(contents, svn_stream_disown(stream, pool),
1315 ? sess->callbacks->cancel_func : NULL,
1316 sess->callback_baton,
1320 /* Handle props if requested. */
1322 SVN_ERR(get_node_props(props, root, abs_path, sess->uuid, pool, pool));
1324 return SVN_NO_ERROR;
1329 /* Getting a directory's entries */
1330 static svn_error_t *
1331 svn_ra_local__get_dir(svn_ra_session_t *session,
1332 apr_hash_t **dirents,
1333 svn_revnum_t *fetched_rev,
1336 svn_revnum_t revision,
1337 apr_uint32_t dirent_fields,
1340 svn_fs_root_t *root;
1341 svn_revnum_t youngest_rev;
1342 apr_hash_t *entries;
1343 apr_hash_index_t *hi;
1344 svn_ra_local__session_baton_t *sess = session->priv;
1345 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1347 /* Open the revision's root. */
1348 if (! SVN_IS_VALID_REVNUM(revision))
1350 SVN_ERR(svn_fs_youngest_rev(&youngest_rev, sess->fs, pool));
1351 SVN_ERR(svn_fs_revision_root(&root, sess->fs, youngest_rev, pool));
1352 if (fetched_rev != NULL)
1353 *fetched_rev = youngest_rev;
1356 SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool));
1360 apr_pool_t *iterpool = svn_pool_create(pool);
1361 /* Get the dir's entries. */
1362 SVN_ERR(svn_fs_dir_entries(&entries, root, abs_path, pool));
1364 /* Loop over the fs dirents, and build a hash of general
1366 *dirents = apr_hash_make(pool);
1367 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
1371 const char *datestring, *entryname, *fullpath;
1372 svn_fs_dirent_t *fs_entry;
1373 svn_dirent_t *entry = svn_dirent_create(pool);
1375 svn_pool_clear(iterpool);
1377 apr_hash_this(hi, &key, NULL, &val);
1378 entryname = (const char *) key;
1379 fs_entry = (svn_fs_dirent_t *) val;
1381 fullpath = svn_dirent_join(abs_path, entryname, iterpool);
1383 if (dirent_fields & SVN_DIRENT_KIND)
1386 entry->kind = fs_entry->kind;
1389 if (dirent_fields & SVN_DIRENT_SIZE)
1392 if (fs_entry->kind == svn_node_dir)
1393 entry->size = SVN_INVALID_FILESIZE;
1395 SVN_ERR(svn_fs_file_length(&(entry->size), root,
1396 fullpath, iterpool));
1399 if (dirent_fields & SVN_DIRENT_HAS_PROPS)
1402 SVN_ERR(svn_fs_node_has_props(&entry->has_props,
1407 if ((dirent_fields & SVN_DIRENT_TIME)
1408 || (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
1409 || (dirent_fields & SVN_DIRENT_CREATED_REV))
1411 /* created_rev & friends */
1412 SVN_ERR(svn_repos_get_committed_info(&(entry->created_rev),
1414 &(entry->last_author),
1415 root, fullpath, iterpool));
1417 SVN_ERR(svn_time_from_cstring(&(entry->time), datestring,
1419 if (entry->last_author)
1420 entry->last_author = apr_pstrdup(pool, entry->last_author);
1424 svn_hash_sets(*dirents, entryname, entry);
1426 svn_pool_destroy(iterpool);
1429 /* Handle props if requested. */
1431 SVN_ERR(get_node_props(props, root, abs_path, sess->uuid, pool, pool));
1433 return SVN_NO_ERROR;
1437 static svn_error_t *
1438 svn_ra_local__get_locations(svn_ra_session_t *session,
1439 apr_hash_t **locations,
1441 svn_revnum_t peg_revision,
1442 const apr_array_header_t *location_revisions,
1445 svn_ra_local__session_baton_t *sess = session->priv;
1446 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1447 return svn_repos_trace_node_locations(sess->fs, locations, abs_path,
1448 peg_revision, location_revisions,
1453 static svn_error_t *
1454 svn_ra_local__get_location_segments(svn_ra_session_t *session,
1456 svn_revnum_t peg_revision,
1457 svn_revnum_t start_rev,
1458 svn_revnum_t end_rev,
1459 svn_location_segment_receiver_t receiver,
1460 void *receiver_baton,
1463 svn_ra_local__session_baton_t *sess = session->priv;
1464 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1465 return svn_repos_node_location_segments(sess->repos, abs_path,
1466 peg_revision, start_rev, end_rev,
1467 receiver, receiver_baton,
1471 struct lock_baton_t {
1472 svn_ra_lock_callback_t lock_func;
1474 const char *fs_path;
1475 svn_boolean_t is_lock;
1476 svn_error_t *cb_err;
1479 /* Implements svn_fs_lock_callback_t. Used by svn_ra_local__lock and
1480 svn_ra_local__unlock to forward to supplied callback and record any
1482 static svn_error_t *
1483 lock_cb(void *lock_baton,
1485 const svn_lock_t *lock,
1486 svn_error_t *fs_err,
1489 struct lock_baton_t *b = lock_baton;
1491 if (b && !b->cb_err && b->lock_func)
1493 path = svn_fspath__skip_ancestor(b->fs_path, path);
1494 b->cb_err = b->lock_func(b->lock_baton, path, b->is_lock, lock, fs_err,
1498 return SVN_NO_ERROR;
1501 static svn_error_t *
1502 svn_ra_local__lock(svn_ra_session_t *session,
1503 apr_hash_t *path_revs,
1504 const char *comment,
1505 svn_boolean_t force,
1506 svn_ra_lock_callback_t lock_func,
1510 svn_ra_local__session_baton_t *sess = session->priv;
1511 apr_hash_t *targets = apr_hash_make(pool);
1512 apr_hash_index_t *hi;
1514 struct lock_baton_t baton = {0};
1516 /* A username is absolutely required to lock a path. */
1517 SVN_ERR(get_username(session, pool));
1519 for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi))
1521 const char *abs_path = svn_fspath__join(sess->fs_path->data,
1522 apr_hash_this_key(hi), pool);
1523 svn_revnum_t current_rev = *(svn_revnum_t *)apr_hash_this_val(hi);
1524 svn_fs_lock_target_t *target = svn_fs_lock_target_create(NULL,
1528 svn_hash_sets(targets, abs_path, target);
1531 baton.lock_func = lock_func;
1532 baton.lock_baton = lock_baton;
1533 baton.fs_path = sess->fs_path->data;
1534 baton.is_lock = TRUE;
1535 baton.cb_err = SVN_NO_ERROR;
1537 err = svn_repos_fs_lock_many(sess->repos, targets, comment,
1538 FALSE /* not DAV comment */,
1539 0 /* no expiration */, force,
1543 if (err && baton.cb_err)
1544 svn_error_compose(err, baton.cb_err);
1548 return svn_error_trace(err);
1552 static svn_error_t *
1553 svn_ra_local__unlock(svn_ra_session_t *session,
1554 apr_hash_t *path_tokens,
1555 svn_boolean_t force,
1556 svn_ra_lock_callback_t lock_func,
1560 svn_ra_local__session_baton_t *sess = session->priv;
1561 apr_hash_t *targets = apr_hash_make(pool);
1562 apr_hash_index_t *hi;
1564 struct lock_baton_t baton = {0};
1566 /* A username is absolutely required to unlock a path. */
1567 SVN_ERR(get_username(session, pool));
1569 for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi))
1571 const char *abs_path = svn_fspath__join(sess->fs_path->data,
1572 apr_hash_this_key(hi), pool);
1573 const char *token = apr_hash_this_val(hi);
1575 svn_hash_sets(targets, abs_path, token);
1578 baton.lock_func = lock_func;
1579 baton.lock_baton = lock_baton;
1580 baton.fs_path = sess->fs_path->data;
1581 baton.is_lock = FALSE;
1582 baton.cb_err = SVN_NO_ERROR;
1584 err = svn_repos_fs_unlock_many(sess->repos, targets, force, lock_cb, &baton,
1587 if (err && baton.cb_err)
1588 svn_error_compose(err, baton.cb_err);
1592 return svn_error_trace(err);
1597 static svn_error_t *
1598 svn_ra_local__get_lock(svn_ra_session_t *session,
1603 svn_ra_local__session_baton_t *sess = session->priv;
1604 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1605 return svn_fs_get_lock(lock, sess->fs, abs_path, pool);
1610 static svn_error_t *
1611 svn_ra_local__get_locks(svn_ra_session_t *session,
1617 svn_ra_local__session_baton_t *sess = session->priv;
1618 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1620 /* Kinda silly to call the repos wrapper, since we have no authz
1621 func to give it. But heck, why not. */
1622 return svn_repos_fs_get_locks2(locks, sess->repos, abs_path, depth,
1627 static svn_error_t *
1628 svn_ra_local__replay(svn_ra_session_t *session,
1629 svn_revnum_t revision,
1630 svn_revnum_t low_water_mark,
1631 svn_boolean_t send_deltas,
1632 const svn_delta_editor_t *editor,
1636 svn_ra_local__session_baton_t *sess = session->priv;
1637 svn_fs_root_t *root;
1639 SVN_ERR(svn_fs_revision_root(&root, svn_repos_fs(sess->repos),
1641 return svn_repos_replay2(root, sess->fs_path->data, low_water_mark,
1642 send_deltas, editor, edit_baton, NULL, NULL,
1647 static svn_error_t *
1648 svn_ra_local__replay_range(svn_ra_session_t *session,
1649 svn_revnum_t start_revision,
1650 svn_revnum_t end_revision,
1651 svn_revnum_t low_water_mark,
1652 svn_boolean_t send_deltas,
1653 svn_ra_replay_revstart_callback_t revstart_func,
1654 svn_ra_replay_revfinish_callback_t revfinish_func,
1658 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
1662 static svn_error_t *
1663 svn_ra_local__has_capability(svn_ra_session_t *session,
1665 const char *capability,
1668 svn_ra_local__session_baton_t *sess = session->priv;
1670 if (strcmp(capability, SVN_RA_CAPABILITY_DEPTH) == 0
1671 || strcmp(capability, SVN_RA_CAPABILITY_LOG_REVPROPS) == 0
1672 || strcmp(capability, SVN_RA_CAPABILITY_PARTIAL_REPLAY) == 0
1673 || strcmp(capability, SVN_RA_CAPABILITY_COMMIT_REVPROPS) == 0
1674 || strcmp(capability, SVN_RA_CAPABILITY_ATOMIC_REVPROPS) == 0
1675 || strcmp(capability, SVN_RA_CAPABILITY_INHERITED_PROPS) == 0
1676 || strcmp(capability, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS) == 0
1677 || strcmp(capability, SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE) == 0
1678 || strcmp(capability, SVN_RA_CAPABILITY_LIST) == 0
1683 else if (strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO) == 0)
1685 /* With mergeinfo, the code's capabilities may not reflect the
1686 repository's, so inquire further. */
1687 SVN_ERR(svn_repos_has_capability(sess->repos, has,
1688 SVN_REPOS_CAPABILITY_MERGEINFO,
1691 else /* Don't know any other capabilities, so error. */
1693 return svn_error_createf
1694 (SVN_ERR_UNKNOWN_CAPABILITY, NULL,
1695 _("Don't know anything about capability '%s'"), capability);
1698 return SVN_NO_ERROR;
1701 static svn_error_t *
1702 svn_ra_local__get_deleted_rev(svn_ra_session_t *session,
1704 svn_revnum_t peg_revision,
1705 svn_revnum_t end_revision,
1706 svn_revnum_t *revision_deleted,
1709 svn_ra_local__session_baton_t *sess = session->priv;
1710 const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool);
1712 SVN_ERR(svn_repos_deleted_rev(sess->fs,
1719 return SVN_NO_ERROR;
1722 static svn_error_t *
1723 svn_ra_local__get_inherited_props(svn_ra_session_t *session,
1724 apr_array_header_t **iprops,
1726 svn_revnum_t revision,
1727 apr_pool_t *result_pool,
1728 apr_pool_t *scratch_pool)
1730 svn_fs_root_t *root;
1731 svn_ra_local__session_baton_t *sess = session->priv;
1732 const char *abs_path = svn_fspath__join(sess->fs_path->data, path,
1734 svn_node_kind_t node_kind;
1736 /* Open the revision's root. */
1737 SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, scratch_pool));
1739 SVN_ERR(svn_fs_check_path(&node_kind, root, abs_path, scratch_pool));
1740 if (node_kind == svn_node_none)
1742 return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
1743 _("'%s' path not found"), abs_path);
1746 return svn_error_trace(
1747 svn_repos_fs_get_inherited_props(iprops, root, abs_path,
1748 NULL /* propname */,
1749 NULL, NULL /* auth */,
1750 result_pool, scratch_pool));
1753 static svn_error_t *
1754 svn_ra_local__register_editor_shim_callbacks(svn_ra_session_t *session,
1755 svn_delta_shim_callbacks_t *callbacks)
1757 /* This is currenly a no-op, since we don't provide our own editor, just
1758 use the one the libsvn_repos hands back to us. */
1759 return SVN_NO_ERROR;
1763 static svn_error_t *
1764 svn_ra_local__get_commit_ev2(svn_editor_t **editor,
1765 svn_ra_session_t *session,
1766 apr_hash_t *revprops,
1767 svn_commit_callback2_t commit_cb,
1769 apr_hash_t *lock_tokens,
1770 svn_boolean_t keep_locks,
1771 svn_ra__provide_base_cb_t provide_base_cb,
1772 svn_ra__provide_props_cb_t provide_props_cb,
1773 svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb,
1775 svn_cancel_func_t cancel_func,
1777 apr_pool_t *result_pool,
1778 apr_pool_t *scratch_pool)
1780 svn_ra_local__session_baton_t *sess = session->priv;
1781 struct deltify_etc_baton *deb = apr_palloc(result_pool, sizeof(*deb));
1783 remap_commit_callback(&commit_cb, &commit_baton, session,
1784 commit_cb, commit_baton, result_pool);
1786 /* NOTE: the RA callbacks are ignored. We pass everything directly to
1787 the REPOS editor. */
1789 /* Prepare the baton for deltify_etc() */
1791 deb->repos = sess->repos;
1792 deb->fspath_base = sess->fs_path->data;
1794 deb->lock_tokens = lock_tokens;
1796 deb->lock_tokens = NULL;
1797 deb->commit_cb = commit_cb;
1798 deb->commit_baton = commit_baton;
1800 /* Ensure there is a username (and an FS access context) associated with
1801 the session and its FS handle. */
1802 SVN_ERR(get_username(session, scratch_pool));
1804 /* If there are lock tokens to add, do so. */
1805 SVN_ERR(apply_lock_tokens(sess->fs, sess->fs_path->data, lock_tokens,
1806 session->pool, scratch_pool));
1808 /* Copy the REVPROPS and insert the author/username. */
1809 revprops = apr_hash_copy(scratch_pool, revprops);
1810 svn_hash_sets(revprops, SVN_PROP_REVISION_AUTHOR,
1811 svn_string_create(sess->username, scratch_pool));
1813 return svn_error_trace(svn_repos__get_commit_ev2(
1814 editor, sess->repos, NULL /* authz */,
1815 NULL /* authz_repos_name */, NULL /* authz_user */,
1817 deltify_etc, deb, cancel_func, cancel_baton,
1818 result_pool, scratch_pool));
1821 /* Trivially forward repos-layer callbacks to RA-layer callbacks.
1822 * Their signatures are the same. */
1823 typedef struct dirent_receiver_baton_t
1825 svn_ra_dirent_receiver_t receiver;
1826 void *receiver_baton;
1827 } dirent_receiver_baton_t;
1829 static svn_error_t *
1830 dirent_receiver(const char *rel_path,
1831 svn_dirent_t *dirent,
1835 dirent_receiver_baton_t *b = baton;
1836 return b->receiver(rel_path, dirent, b->receiver_baton, pool);
1839 static svn_error_t *
1840 svn_ra_local__list(svn_ra_session_t *session,
1842 svn_revnum_t revision,
1843 const apr_array_header_t *patterns,
1845 apr_uint32_t dirent_fields,
1846 svn_ra_dirent_receiver_t receiver,
1847 void *receiver_baton,
1850 svn_ra_local__session_baton_t *sess = session->priv;
1851 svn_fs_root_t *root;
1852 svn_boolean_t path_info_only = (dirent_fields & ~SVN_DIRENT_KIND) == 0;
1854 dirent_receiver_baton_t baton;
1855 baton.receiver = receiver;
1856 baton.receiver_baton = receiver_baton;
1858 SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool));
1859 path = svn_dirent_join(sess->fs_path->data, path, pool);
1860 return svn_error_trace(svn_repos_list(root, path, patterns, depth,
1861 path_info_only, NULL, NULL,
1862 dirent_receiver, &baton,
1864 ? sess->callbacks->cancel_func
1866 sess->callback_baton, pool));
1869 /*----------------------------------------------------------------*/
1871 static const svn_version_t *
1872 ra_local_version(void)
1877 /** The ra_vtable **/
1879 static const svn_ra__vtable_t ra_local_vtable =
1882 svn_ra_local__get_description,
1883 svn_ra_local__get_schemes,
1885 svn_ra_local__dup_session,
1886 svn_ra_local__reparent,
1887 svn_ra_local__get_session_url,
1888 svn_ra_local__get_latest_revnum,
1889 svn_ra_local__get_dated_revision,
1890 svn_ra_local__change_rev_prop,
1891 svn_ra_local__rev_proplist,
1892 svn_ra_local__rev_prop,
1893 svn_ra_local__get_commit_editor,
1894 svn_ra_local__get_file,
1895 svn_ra_local__get_dir,
1896 svn_ra_local__get_mergeinfo,
1897 svn_ra_local__do_update,
1898 svn_ra_local__do_switch,
1899 svn_ra_local__do_status,
1900 svn_ra_local__do_diff,
1901 svn_ra_local__get_log,
1902 svn_ra_local__do_check_path,
1904 svn_ra_local__get_uuid,
1905 svn_ra_local__get_repos_root,
1906 svn_ra_local__get_locations,
1907 svn_ra_local__get_location_segments,
1908 svn_ra_local__get_file_revs,
1910 svn_ra_local__unlock,
1911 svn_ra_local__get_lock,
1912 svn_ra_local__get_locks,
1913 svn_ra_local__replay,
1914 svn_ra_local__has_capability,
1915 svn_ra_local__replay_range,
1916 svn_ra_local__get_deleted_rev,
1917 svn_ra_local__get_inherited_props,
1918 NULL /* set_svn_ra_open */,
1919 svn_ra_local__list ,
1920 svn_ra_local__register_editor_shim_callbacks,
1921 svn_ra_local__get_commit_ev2,
1922 NULL /* replay_range_ev2 */
1926 /*----------------------------------------------------------------*/
1928 /** The One Public Routine, called by libsvn_ra **/
1931 svn_ra_local__init(const svn_version_t *loader_version,
1932 const svn_ra__vtable_t **vtable,
1935 static const svn_version_checklist_t checklist[] =
1937 { "svn_subr", svn_subr_version },
1938 { "svn_delta", svn_delta_version },
1939 { "svn_repos", svn_repos_version },
1940 { "svn_fs", svn_fs_version },
1945 /* Simplified version check to make sure we can safely use the
1946 VTABLE parameter. The RA loader does a more exhaustive check. */
1947 if (loader_version->major != SVN_VER_MAJOR)
1948 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
1949 _("Unsupported RA loader version (%d) for "
1951 loader_version->major);
1953 SVN_ERR(svn_ver_check_list2(ra_local_version(), checklist, svn_ver_equal));
1955 #ifndef SVN_LIBSVN_RA_LINKS_RA_LOCAL
1956 /* This means the library was loaded as a DSO, so use the DSO pool. */
1957 SVN_ERR(svn_fs_initialize(svn_dso__pool()));
1960 *vtable = &ra_local_vtable;
1962 return SVN_NO_ERROR;
1965 /* Compatibility wrapper for the 1.1 and before API. */
1966 #define NAME "ra_local"
1967 #define DESCRIPTION RA_LOCAL_DESCRIPTION
1968 #define VTBL ra_local_vtable
1969 #define INITFUNC svn_ra_local__init
1970 #define COMPAT_INITFUNC svn_ra_local_init
1971 #include "../libsvn_ra/wrapper_template.h"