1251881Speter/* 2251881Speter * ra_plugin.c : the main RA module for local repository access 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter#include "ra_local.h" 25251881Speter#include "svn_hash.h" 26251881Speter#include "svn_ra.h" 27251881Speter#include "svn_fs.h" 28251881Speter#include "svn_delta.h" 29251881Speter#include "svn_repos.h" 30251881Speter#include "svn_pools.h" 31251881Speter#include "svn_time.h" 32251881Speter#include "svn_props.h" 33251881Speter#include "svn_mergeinfo.h" 34251881Speter#include "svn_path.h" 35251881Speter#include "svn_version.h" 36251881Speter#include "svn_cache_config.h" 37251881Speter 38251881Speter#include "svn_private_config.h" 39251881Speter#include "../libsvn_ra/ra_loader.h" 40251881Speter#include "private/svn_mergeinfo_private.h" 41251881Speter#include "private/svn_repos_private.h" 42251881Speter#include "private/svn_fspath.h" 43251881Speter#include "private/svn_atomic.h" 44262253Speter#include "private/svn_subr_private.h" 45251881Speter 46251881Speter#define APR_WANT_STRFUNC 47251881Speter#include <apr_want.h> 48251881Speter 49251881Speter/*----------------------------------------------------------------*/ 50251881Speter 51251881Speter/*** Miscellaneous helper functions ***/ 52251881Speter 53251881Speter 54251881Speter/* Pool cleanup handler: ensure that the access descriptor of the 55251881Speter filesystem (svn_fs_t *) DATA is set to NULL. */ 56251881Speterstatic apr_status_t 57251881Spetercleanup_access(void *data) 58251881Speter{ 59251881Speter svn_error_t *serr; 60251881Speter svn_fs_t *fs = data; 61251881Speter 62251881Speter serr = svn_fs_set_access(fs, NULL); 63251881Speter 64251881Speter if (serr) 65251881Speter { 66251881Speter apr_status_t apr_err = serr->apr_err; 67251881Speter svn_error_clear(serr); 68251881Speter return apr_err; 69251881Speter } 70251881Speter 71251881Speter return APR_SUCCESS; 72251881Speter} 73251881Speter 74251881Speter 75251881Speter/* Fetch a username for use with SESSION, and store it in SESSION->username. 76251881Speter * 77251881Speter * Allocate the username in SESSION->pool. Use SCRATCH_POOL for temporary 78251881Speter * allocations. */ 79251881Speterstatic svn_error_t * 80251881Speterget_username(svn_ra_session_t *session, 81251881Speter apr_pool_t *scratch_pool) 82251881Speter{ 83251881Speter svn_ra_local__session_baton_t *sess = session->priv; 84251881Speter 85251881Speter /* If we've already found the username don't ask for it again. */ 86251881Speter if (! sess->username) 87251881Speter { 88251881Speter /* Get a username somehow, so we have some svn:author property to 89251881Speter attach to a commit. */ 90251881Speter if (sess->callbacks->auth_baton) 91251881Speter { 92251881Speter void *creds; 93251881Speter svn_auth_cred_username_t *username_creds; 94251881Speter svn_auth_iterstate_t *iterstate; 95251881Speter 96251881Speter SVN_ERR(svn_auth_first_credentials(&creds, &iterstate, 97251881Speter SVN_AUTH_CRED_USERNAME, 98251881Speter sess->uuid, /* realmstring */ 99251881Speter sess->callbacks->auth_baton, 100251881Speter scratch_pool)); 101251881Speter 102251881Speter /* No point in calling next_creds(), since that assumes that the 103251881Speter first_creds() somehow failed to authenticate. But there's no 104251881Speter challenge going on, so we use whatever creds we get back on 105251881Speter the first try. */ 106251881Speter username_creds = creds; 107251881Speter if (username_creds && username_creds->username) 108251881Speter { 109251881Speter sess->username = apr_pstrdup(session->pool, 110251881Speter username_creds->username); 111251881Speter svn_error_clear(svn_auth_save_credentials(iterstate, 112251881Speter scratch_pool)); 113251881Speter } 114251881Speter else 115251881Speter sess->username = ""; 116251881Speter } 117251881Speter else 118251881Speter sess->username = ""; 119251881Speter } 120251881Speter 121251881Speter /* If we have a real username, attach it to the filesystem so that it can 122251881Speter be used to validate locks. Even if there already is a user context 123251881Speter associated, it may contain irrelevant lock tokens, so always create a new. 124251881Speter */ 125251881Speter if (*sess->username) 126251881Speter { 127251881Speter svn_fs_access_t *access_ctx; 128251881Speter 129251881Speter SVN_ERR(svn_fs_create_access(&access_ctx, sess->username, 130251881Speter session->pool)); 131251881Speter SVN_ERR(svn_fs_set_access(sess->fs, access_ctx)); 132251881Speter 133251881Speter /* Make sure this context is disassociated when the pool gets 134251881Speter destroyed. */ 135251881Speter apr_pool_cleanup_register(session->pool, sess->fs, cleanup_access, 136251881Speter apr_pool_cleanup_null); 137251881Speter } 138251881Speter 139251881Speter return SVN_NO_ERROR; 140251881Speter} 141251881Speter 142251881Speter/* Implements an svn_atomic__init_once callback. Sets the FSFS memory 143251881Speter cache size. */ 144251881Speterstatic svn_error_t * 145251881Spetercache_init(void *baton, apr_pool_t *pool) 146251881Speter{ 147251881Speter apr_hash_t *config_hash = baton; 148251881Speter svn_config_t *config = NULL; 149251881Speter const char *memory_cache_size_str; 150251881Speter 151251881Speter if (config_hash) 152251881Speter config = svn_hash_gets(config_hash, SVN_CONFIG_CATEGORY_CONFIG); 153251881Speter svn_config_get(config, &memory_cache_size_str, SVN_CONFIG_SECTION_MISCELLANY, 154251881Speter SVN_CONFIG_OPTION_MEMORY_CACHE_SIZE, NULL); 155251881Speter if (memory_cache_size_str) 156251881Speter { 157251881Speter apr_uint64_t memory_cache_size; 158251881Speter svn_cache_config_t settings = *svn_cache_config_get(); 159251881Speter 160251881Speter SVN_ERR(svn_error_quick_wrap(svn_cstring_atoui64(&memory_cache_size, 161251881Speter memory_cache_size_str), 162251881Speter _("memory-cache-size invalid"))); 163251881Speter settings.cache_size = 1024 * 1024 * memory_cache_size; 164251881Speter svn_cache_config_set(&settings); 165251881Speter } 166251881Speter 167251881Speter return SVN_NO_ERROR; 168251881Speter} 169251881Speter 170251881Speter/*----------------------------------------------------------------*/ 171251881Speter 172251881Speter/*** The reporter vtable needed by do_update() and friends ***/ 173251881Speter 174251881Spetertypedef struct reporter_baton_t 175251881Speter{ 176251881Speter svn_ra_local__session_baton_t *sess; 177251881Speter void *report_baton; 178251881Speter 179251881Speter} reporter_baton_t; 180251881Speter 181251881Speter 182251881Speterstatic void * 183251881Spetermake_reporter_baton(svn_ra_local__session_baton_t *sess, 184251881Speter void *report_baton, 185251881Speter apr_pool_t *pool) 186251881Speter{ 187251881Speter reporter_baton_t *rbaton = apr_palloc(pool, sizeof(*rbaton)); 188251881Speter rbaton->sess = sess; 189251881Speter rbaton->report_baton = report_baton; 190251881Speter return rbaton; 191251881Speter} 192251881Speter 193251881Speter 194251881Speterstatic svn_error_t * 195251881Speterreporter_set_path(void *reporter_baton, 196251881Speter const char *path, 197251881Speter svn_revnum_t revision, 198251881Speter svn_depth_t depth, 199251881Speter svn_boolean_t start_empty, 200251881Speter const char *lock_token, 201251881Speter apr_pool_t *pool) 202251881Speter{ 203251881Speter reporter_baton_t *rbaton = reporter_baton; 204251881Speter return svn_repos_set_path3(rbaton->report_baton, path, 205251881Speter revision, depth, start_empty, lock_token, pool); 206251881Speter} 207251881Speter 208251881Speter 209251881Speterstatic svn_error_t * 210251881Speterreporter_delete_path(void *reporter_baton, 211251881Speter const char *path, 212251881Speter apr_pool_t *pool) 213251881Speter{ 214251881Speter reporter_baton_t *rbaton = reporter_baton; 215251881Speter return svn_repos_delete_path(rbaton->report_baton, path, pool); 216251881Speter} 217251881Speter 218251881Speter 219251881Speterstatic svn_error_t * 220251881Speterreporter_link_path(void *reporter_baton, 221251881Speter const char *path, 222251881Speter const char *url, 223251881Speter svn_revnum_t revision, 224251881Speter svn_depth_t depth, 225251881Speter svn_boolean_t start_empty, 226251881Speter const char *lock_token, 227251881Speter apr_pool_t *pool) 228251881Speter{ 229251881Speter reporter_baton_t *rbaton = reporter_baton; 230251881Speter const char *repos_url = rbaton->sess->repos_url; 231251881Speter const char *relpath = svn_uri_skip_ancestor(repos_url, url, pool); 232251881Speter const char *fs_path; 233251881Speter 234251881Speter if (!relpath) 235251881Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 236251881Speter _("'%s'\n" 237251881Speter "is not the same repository as\n" 238251881Speter "'%s'"), url, rbaton->sess->repos_url); 239251881Speter 240251881Speter /* Convert the relpath to an fspath */ 241251881Speter if (relpath[0] == '\0') 242251881Speter fs_path = "/"; 243251881Speter else 244251881Speter fs_path = apr_pstrcat(pool, "/", relpath, (char *)NULL); 245251881Speter 246251881Speter return svn_repos_link_path3(rbaton->report_baton, path, fs_path, revision, 247251881Speter depth, start_empty, lock_token, pool); 248251881Speter} 249251881Speter 250251881Speter 251251881Speterstatic svn_error_t * 252251881Speterreporter_finish_report(void *reporter_baton, 253251881Speter apr_pool_t *pool) 254251881Speter{ 255251881Speter reporter_baton_t *rbaton = reporter_baton; 256251881Speter return svn_repos_finish_report(rbaton->report_baton, pool); 257251881Speter} 258251881Speter 259251881Speter 260251881Speterstatic svn_error_t * 261251881Speterreporter_abort_report(void *reporter_baton, 262251881Speter apr_pool_t *pool) 263251881Speter{ 264251881Speter reporter_baton_t *rbaton = reporter_baton; 265251881Speter return svn_repos_abort_report(rbaton->report_baton, pool); 266251881Speter} 267251881Speter 268251881Speter 269251881Speterstatic const svn_ra_reporter3_t ra_local_reporter = 270251881Speter{ 271251881Speter reporter_set_path, 272251881Speter reporter_delete_path, 273251881Speter reporter_link_path, 274251881Speter reporter_finish_report, 275251881Speter reporter_abort_report 276251881Speter}; 277251881Speter 278251881Speter 279251881Speter/* ... 280251881Speter * 281251881Speter * Wrap a cancellation editor using SESSION's cancellation function around 282251881Speter * the supplied EDITOR. ### Some callers (via svn_ra_do_update2() etc.) 283251881Speter * don't appear to know that we do this, and are supplying an editor that 284251881Speter * they have already wrapped with the same cancellation editor, so it ends 285251881Speter * up double-wrapped. 286251881Speter * 287251881Speter * Allocate @a *reporter and @a *report_baton in @a result_pool. Use 288251881Speter * @a scratch_pool for temporary allocations. 289251881Speter */ 290251881Speterstatic svn_error_t * 291251881Spetermake_reporter(svn_ra_session_t *session, 292251881Speter const svn_ra_reporter3_t **reporter, 293251881Speter void **report_baton, 294251881Speter svn_revnum_t revision, 295251881Speter const char *target, 296251881Speter const char *other_url, 297251881Speter svn_boolean_t text_deltas, 298251881Speter svn_depth_t depth, 299251881Speter svn_boolean_t send_copyfrom_args, 300251881Speter svn_boolean_t ignore_ancestry, 301251881Speter const svn_delta_editor_t *editor, 302251881Speter void *edit_baton, 303251881Speter apr_pool_t *result_pool, 304251881Speter apr_pool_t *scratch_pool) 305251881Speter{ 306251881Speter svn_ra_local__session_baton_t *sess = session->priv; 307251881Speter void *rbaton; 308251881Speter const char *other_fs_path = NULL; 309251881Speter 310251881Speter /* Get the HEAD revision if one is not supplied. */ 311251881Speter if (! SVN_IS_VALID_REVNUM(revision)) 312251881Speter SVN_ERR(svn_fs_youngest_rev(&revision, sess->fs, scratch_pool)); 313251881Speter 314251881Speter /* If OTHER_URL was provided, validate it and convert it into a 315251881Speter regular filesystem path. */ 316251881Speter if (other_url) 317251881Speter { 318251881Speter const char *other_relpath 319251881Speter = svn_uri_skip_ancestor(sess->repos_url, other_url, scratch_pool); 320251881Speter 321251881Speter /* Sanity check: the other_url better be in the same repository as 322251881Speter the original session url! */ 323251881Speter if (! other_relpath) 324251881Speter return svn_error_createf 325251881Speter (SVN_ERR_RA_ILLEGAL_URL, NULL, 326251881Speter _("'%s'\n" 327251881Speter "is not the same repository as\n" 328251881Speter "'%s'"), other_url, sess->repos_url); 329251881Speter 330251881Speter other_fs_path = apr_pstrcat(scratch_pool, "/", other_relpath, 331251881Speter (char *)NULL); 332251881Speter } 333251881Speter 334251881Speter /* Pass back our reporter */ 335251881Speter *reporter = &ra_local_reporter; 336251881Speter 337251881Speter SVN_ERR(get_username(session, scratch_pool)); 338251881Speter 339251881Speter if (sess->callbacks) 340251881Speter SVN_ERR(svn_delta_get_cancellation_editor(sess->callbacks->cancel_func, 341251881Speter sess->callback_baton, 342251881Speter editor, 343251881Speter edit_baton, 344251881Speter &editor, 345251881Speter &edit_baton, 346251881Speter result_pool)); 347251881Speter 348251881Speter /* Build a reporter baton. */ 349251881Speter SVN_ERR(svn_repos_begin_report3(&rbaton, 350251881Speter revision, 351251881Speter sess->repos, 352251881Speter sess->fs_path->data, 353251881Speter target, 354251881Speter other_fs_path, 355251881Speter text_deltas, 356251881Speter depth, 357251881Speter ignore_ancestry, 358251881Speter send_copyfrom_args, 359251881Speter editor, 360251881Speter edit_baton, 361251881Speter NULL, 362251881Speter NULL, 363251881Speter 1024 * 1024, /* process-local transfers 364251881Speter should be fast */ 365251881Speter result_pool)); 366251881Speter 367251881Speter /* Wrap the report baton given us by the repos layer with our own 368251881Speter reporter baton. */ 369251881Speter *report_baton = make_reporter_baton(sess, rbaton, result_pool); 370251881Speter 371251881Speter return SVN_NO_ERROR; 372251881Speter} 373251881Speter 374251881Speter 375251881Speter/*----------------------------------------------------------------*/ 376251881Speter 377251881Speter/*** Deltification stuff for get_commit_editor() ***/ 378251881Speter 379251881Speterstruct deltify_etc_baton 380251881Speter{ 381251881Speter svn_fs_t *fs; /* the fs to deltify in */ 382251881Speter svn_repos_t *repos; /* repos for unlocking */ 383251881Speter const char *fspath_base; /* fs-path part of split session URL */ 384251881Speter 385251881Speter apr_hash_t *lock_tokens; /* tokens to unlock, if any */ 386251881Speter 387251881Speter svn_commit_callback2_t commit_cb; /* the original callback */ 388251881Speter void *commit_baton; /* the original callback's baton */ 389251881Speter}; 390251881Speter 391251881Speter/* This implements 'svn_commit_callback_t'. Its invokes the original 392251881Speter (wrapped) callback, but also does deltification on the new revision and 393251881Speter possibly unlocks committed paths. 394251881Speter BATON is 'struct deltify_etc_baton *'. */ 395251881Speterstatic svn_error_t * 396251881Speterdeltify_etc(const svn_commit_info_t *commit_info, 397251881Speter void *baton, 398251881Speter apr_pool_t *scratch_pool) 399251881Speter{ 400251881Speter struct deltify_etc_baton *deb = baton; 401251881Speter svn_error_t *err1 = SVN_NO_ERROR; 402251881Speter svn_error_t *err2; 403251881Speter 404251881Speter /* Invoke the original callback first, in case someone's waiting to 405251881Speter know the revision number so they can go off and annotate an 406251881Speter issue or something. */ 407251881Speter if (deb->commit_cb) 408251881Speter err1 = deb->commit_cb(commit_info, deb->commit_baton, scratch_pool); 409251881Speter 410251881Speter /* Maybe unlock the paths. */ 411251881Speter if (deb->lock_tokens) 412251881Speter { 413251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 414251881Speter apr_hash_index_t *hi; 415251881Speter 416251881Speter for (hi = apr_hash_first(scratch_pool, deb->lock_tokens); hi; 417251881Speter hi = apr_hash_next(hi)) 418251881Speter { 419251881Speter const void *relpath = svn__apr_hash_index_key(hi); 420251881Speter const char *token = svn__apr_hash_index_val(hi); 421251881Speter const char *fspath; 422251881Speter 423251881Speter svn_pool_clear(iterpool); 424251881Speter 425251881Speter fspath = svn_fspath__join(deb->fspath_base, relpath, iterpool); 426251881Speter 427251881Speter /* We may get errors here if the lock was broken or stolen 428251881Speter after the commit succeeded. This is fine and should be 429251881Speter ignored. */ 430251881Speter svn_error_clear(svn_repos_fs_unlock(deb->repos, fspath, token, 431251881Speter FALSE, iterpool)); 432251881Speter } 433251881Speter 434251881Speter svn_pool_destroy(iterpool); 435251881Speter } 436251881Speter 437251881Speter /* But, deltification shouldn't be stopped just because someone's 438251881Speter random callback failed, so proceed unconditionally on to 439251881Speter deltification. */ 440251881Speter err2 = svn_fs_deltify_revision(deb->fs, commit_info->revision, scratch_pool); 441251881Speter 442251881Speter return svn_error_compose_create(err1, err2); 443251881Speter} 444251881Speter 445251881Speter 446251881Speter/* If LOCK_TOKENS is not NULL, then copy all tokens into the access context 447251881Speter of FS. The tokens' paths will be prepended with FSPATH_BASE. 448251881Speter 449251881Speter ACCESS_POOL must match (or exceed) the lifetime of the access context 450251881Speter that was associated with FS. Typically, this is the session pool. 451251881Speter 452251881Speter Temporary allocations are made in SCRATCH_POOL. */ 453251881Speterstatic svn_error_t * 454251881Speterapply_lock_tokens(svn_fs_t *fs, 455251881Speter const char *fspath_base, 456251881Speter apr_hash_t *lock_tokens, 457251881Speter apr_pool_t *access_pool, 458251881Speter apr_pool_t *scratch_pool) 459251881Speter{ 460251881Speter if (lock_tokens) 461251881Speter { 462251881Speter svn_fs_access_t *access_ctx; 463251881Speter 464251881Speter SVN_ERR(svn_fs_get_access(&access_ctx, fs)); 465251881Speter 466251881Speter /* If there is no access context, the filesystem will scream if a 467251881Speter lock is needed. */ 468251881Speter if (access_ctx) 469251881Speter { 470251881Speter apr_hash_index_t *hi; 471251881Speter 472251881Speter /* Note: we have no use for an iterpool here since the data 473251881Speter within the loop is copied into ACCESS_POOL. */ 474251881Speter 475251881Speter for (hi = apr_hash_first(scratch_pool, lock_tokens); hi; 476251881Speter hi = apr_hash_next(hi)) 477251881Speter { 478251881Speter const void *relpath = svn__apr_hash_index_key(hi); 479251881Speter const char *token = svn__apr_hash_index_val(hi); 480251881Speter const char *fspath; 481251881Speter 482251881Speter /* The path needs to live as long as ACCESS_CTX. */ 483251881Speter fspath = svn_fspath__join(fspath_base, relpath, access_pool); 484251881Speter 485251881Speter /* The token must live as long as ACCESS_CTX. */ 486251881Speter token = apr_pstrdup(access_pool, token); 487251881Speter 488251881Speter SVN_ERR(svn_fs_access_add_lock_token2(access_ctx, fspath, 489251881Speter token)); 490251881Speter } 491251881Speter } 492251881Speter } 493251881Speter 494251881Speter return SVN_NO_ERROR; 495251881Speter} 496251881Speter 497251881Speter 498251881Speter/*----------------------------------------------------------------*/ 499251881Speter 500251881Speter/*** The RA vtable routines ***/ 501251881Speter 502251881Speter#define RA_LOCAL_DESCRIPTION \ 503251881Speter N_("Module for accessing a repository on local disk.") 504251881Speter 505251881Speterstatic const char * 506262253Spetersvn_ra_local__get_description(apr_pool_t *pool) 507251881Speter{ 508251881Speter return _(RA_LOCAL_DESCRIPTION); 509251881Speter} 510251881Speter 511251881Speterstatic const char * const * 512251881Spetersvn_ra_local__get_schemes(apr_pool_t *pool) 513251881Speter{ 514251881Speter static const char *schemes[] = { "file", NULL }; 515251881Speter 516251881Speter return schemes; 517251881Speter} 518251881Speter 519251881Speter/* Do nothing. 520251881Speter * 521251881Speter * Why is this acceptable? FS warnings used to be used for only 522251881Speter * two things: failures to close BDB repositories and failures to 523251881Speter * interact with memcached in FSFS (new in 1.6). In 1.5 and earlier, 524251881Speter * we did not call svn_fs_set_warning_func in ra_local, which means 525251881Speter * that any BDB-closing failure would have led to abort()s; the fact 526251881Speter * that this hasn't led to huge hues and cries makes it seem likely 527251881Speter * that this just doesn't happen that often, at least not through 528251881Speter * ra_local. And as far as memcached goes, it seems unlikely that 529251881Speter * somebody is going to go through the trouble of setting up and 530251881Speter * running memcached servers but then use ra_local access. So we 531251881Speter * ignore errors here, so that memcached can use the FS warnings API 532251881Speter * without crashing ra_local. 533251881Speter */ 534251881Speterstatic void 535251881Speterignore_warnings(void *baton, 536251881Speter svn_error_t *err) 537251881Speter{ 538251881Speter#ifdef SVN_DEBUG 539251881Speter SVN_DBG(("Ignoring FS warning %d\n", err ? err->apr_err : 0)); 540251881Speter#endif 541251881Speter return; 542251881Speter} 543251881Speter 544251881Speterstatic svn_error_t * 545251881Spetersvn_ra_local__open(svn_ra_session_t *session, 546251881Speter const char **corrected_url, 547251881Speter const char *repos_URL, 548251881Speter const svn_ra_callbacks2_t *callbacks, 549251881Speter void *callback_baton, 550251881Speter apr_hash_t *config, 551251881Speter apr_pool_t *pool) 552251881Speter{ 553251881Speter svn_ra_local__session_baton_t *sess; 554251881Speter const char *fs_path; 555251881Speter static volatile svn_atomic_t cache_init_state = 0; 556251881Speter 557251881Speter /* Initialise the FSFS memory cache size. We can only do this once 558251881Speter so one CONFIG will win the race and all others will be ignored 559251881Speter silently. */ 560251881Speter SVN_ERR(svn_atomic__init_once(&cache_init_state, cache_init, config, pool)); 561251881Speter 562251881Speter /* We don't support redirections in ra-local. */ 563251881Speter if (corrected_url) 564251881Speter *corrected_url = NULL; 565251881Speter 566251881Speter /* Allocate and stash the session_sess args we have already. */ 567251881Speter sess = apr_pcalloc(pool, sizeof(*sess)); 568251881Speter sess->callbacks = callbacks; 569251881Speter sess->callback_baton = callback_baton; 570251881Speter 571251881Speter /* Look through the URL, figure out which part points to the 572251881Speter repository, and which part is the path *within* the 573251881Speter repository. */ 574251881Speter SVN_ERR_W(svn_ra_local__split_URL(&(sess->repos), 575251881Speter &(sess->repos_url), 576251881Speter &fs_path, 577251881Speter repos_URL, 578251881Speter session->pool), 579251881Speter _("Unable to open an ra_local session to URL")); 580251881Speter sess->fs_path = svn_stringbuf_create(fs_path, session->pool); 581251881Speter 582251881Speter /* Cache the filesystem object from the repos here for 583251881Speter convenience. */ 584251881Speter sess->fs = svn_repos_fs(sess->repos); 585251881Speter 586251881Speter /* Ignore FS warnings. */ 587251881Speter svn_fs_set_warning_func(sess->fs, ignore_warnings, NULL); 588251881Speter 589251881Speter /* Cache the repository UUID as well */ 590251881Speter SVN_ERR(svn_fs_get_uuid(sess->fs, &sess->uuid, session->pool)); 591251881Speter 592251881Speter /* Be sure username is NULL so we know to look it up / ask for it */ 593251881Speter sess->username = NULL; 594251881Speter 595251881Speter session->priv = sess; 596251881Speter return SVN_NO_ERROR; 597251881Speter} 598251881Speter 599251881Speterstatic svn_error_t * 600251881Spetersvn_ra_local__reparent(svn_ra_session_t *session, 601251881Speter const char *url, 602251881Speter apr_pool_t *pool) 603251881Speter{ 604251881Speter svn_ra_local__session_baton_t *sess = session->priv; 605251881Speter const char *relpath = svn_uri_skip_ancestor(sess->repos_url, url, pool); 606251881Speter 607251881Speter /* If the new URL isn't the same as our repository root URL, then 608251881Speter let's ensure that it's some child of it. */ 609251881Speter if (! relpath) 610251881Speter return svn_error_createf 611251881Speter (SVN_ERR_RA_ILLEGAL_URL, NULL, 612251881Speter _("URL '%s' is not a child of the session's repository root " 613251881Speter "URL '%s'"), url, sess->repos_url); 614251881Speter 615251881Speter /* Update our FS_PATH sess member to point to our new 616251881Speter relative-URL-turned-absolute-filesystem-path. */ 617251881Speter svn_stringbuf_set(sess->fs_path, 618251881Speter svn_fspath__canonicalize(relpath, pool)); 619251881Speter 620251881Speter return SVN_NO_ERROR; 621251881Speter} 622251881Speter 623251881Speterstatic svn_error_t * 624251881Spetersvn_ra_local__get_session_url(svn_ra_session_t *session, 625251881Speter const char **url, 626251881Speter apr_pool_t *pool) 627251881Speter{ 628251881Speter svn_ra_local__session_baton_t *sess = session->priv; 629251881Speter *url = svn_path_url_add_component2(sess->repos_url, 630251881Speter sess->fs_path->data + 1, 631251881Speter pool); 632251881Speter return SVN_NO_ERROR; 633251881Speter} 634251881Speter 635251881Speterstatic svn_error_t * 636251881Spetersvn_ra_local__get_latest_revnum(svn_ra_session_t *session, 637251881Speter svn_revnum_t *latest_revnum, 638251881Speter apr_pool_t *pool) 639251881Speter{ 640251881Speter svn_ra_local__session_baton_t *sess = session->priv; 641251881Speter return svn_fs_youngest_rev(latest_revnum, sess->fs, pool); 642251881Speter} 643251881Speter 644251881Speterstatic svn_error_t * 645251881Spetersvn_ra_local__get_file_revs(svn_ra_session_t *session, 646251881Speter const char *path, 647251881Speter svn_revnum_t start, 648251881Speter svn_revnum_t end, 649251881Speter svn_boolean_t include_merged_revisions, 650251881Speter svn_file_rev_handler_t handler, 651251881Speter void *handler_baton, 652251881Speter apr_pool_t *pool) 653251881Speter{ 654251881Speter svn_ra_local__session_baton_t *sess = session->priv; 655251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 656251881Speter return svn_repos_get_file_revs2(sess->repos, abs_path, start, end, 657251881Speter include_merged_revisions, NULL, NULL, 658251881Speter handler, handler_baton, pool); 659251881Speter} 660251881Speter 661251881Speterstatic svn_error_t * 662251881Spetersvn_ra_local__get_dated_revision(svn_ra_session_t *session, 663251881Speter svn_revnum_t *revision, 664251881Speter apr_time_t tm, 665251881Speter apr_pool_t *pool) 666251881Speter{ 667251881Speter svn_ra_local__session_baton_t *sess = session->priv; 668251881Speter return svn_repos_dated_revision(revision, sess->repos, tm, pool); 669251881Speter} 670251881Speter 671251881Speter 672251881Speterstatic svn_error_t * 673251881Spetersvn_ra_local__change_rev_prop(svn_ra_session_t *session, 674251881Speter svn_revnum_t rev, 675251881Speter const char *name, 676251881Speter const svn_string_t *const *old_value_p, 677251881Speter const svn_string_t *value, 678251881Speter apr_pool_t *pool) 679251881Speter{ 680251881Speter svn_ra_local__session_baton_t *sess = session->priv; 681251881Speter 682251881Speter SVN_ERR(get_username(session, pool)); 683251881Speter return svn_repos_fs_change_rev_prop4(sess->repos, rev, sess->username, 684251881Speter name, old_value_p, value, TRUE, TRUE, 685251881Speter NULL, NULL, pool); 686251881Speter} 687251881Speter 688251881Speterstatic svn_error_t * 689251881Spetersvn_ra_local__get_uuid(svn_ra_session_t *session, 690251881Speter const char **uuid, 691251881Speter apr_pool_t *pool) 692251881Speter{ 693251881Speter svn_ra_local__session_baton_t *sess = session->priv; 694251881Speter *uuid = sess->uuid; 695251881Speter return SVN_NO_ERROR; 696251881Speter} 697251881Speter 698251881Speterstatic svn_error_t * 699251881Spetersvn_ra_local__get_repos_root(svn_ra_session_t *session, 700251881Speter const char **url, 701251881Speter apr_pool_t *pool) 702251881Speter{ 703251881Speter svn_ra_local__session_baton_t *sess = session->priv; 704251881Speter *url = sess->repos_url; 705251881Speter return SVN_NO_ERROR; 706251881Speter} 707251881Speter 708251881Speterstatic svn_error_t * 709251881Spetersvn_ra_local__rev_proplist(svn_ra_session_t *session, 710251881Speter svn_revnum_t rev, 711251881Speter apr_hash_t **props, 712251881Speter apr_pool_t *pool) 713251881Speter{ 714251881Speter svn_ra_local__session_baton_t *sess = session->priv; 715251881Speter return svn_repos_fs_revision_proplist(props, sess->repos, rev, 716251881Speter NULL, NULL, pool); 717251881Speter} 718251881Speter 719251881Speterstatic svn_error_t * 720251881Spetersvn_ra_local__rev_prop(svn_ra_session_t *session, 721251881Speter svn_revnum_t rev, 722251881Speter const char *name, 723251881Speter svn_string_t **value, 724251881Speter apr_pool_t *pool) 725251881Speter{ 726251881Speter svn_ra_local__session_baton_t *sess = session->priv; 727251881Speter return svn_repos_fs_revision_prop(value, sess->repos, rev, name, 728251881Speter NULL, NULL, pool); 729251881Speter} 730251881Speter 731251881Speterstatic svn_error_t * 732251881Spetersvn_ra_local__get_commit_editor(svn_ra_session_t *session, 733251881Speter const svn_delta_editor_t **editor, 734251881Speter void **edit_baton, 735251881Speter apr_hash_t *revprop_table, 736251881Speter svn_commit_callback2_t callback, 737251881Speter void *callback_baton, 738251881Speter apr_hash_t *lock_tokens, 739251881Speter svn_boolean_t keep_locks, 740251881Speter apr_pool_t *pool) 741251881Speter{ 742251881Speter svn_ra_local__session_baton_t *sess = session->priv; 743251881Speter struct deltify_etc_baton *deb = apr_palloc(pool, sizeof(*deb)); 744251881Speter 745251881Speter /* Prepare the baton for deltify_etc() */ 746251881Speter deb->fs = sess->fs; 747251881Speter deb->repos = sess->repos; 748251881Speter deb->fspath_base = sess->fs_path->data; 749251881Speter if (! keep_locks) 750251881Speter deb->lock_tokens = lock_tokens; 751251881Speter else 752251881Speter deb->lock_tokens = NULL; 753251881Speter deb->commit_cb = callback; 754251881Speter deb->commit_baton = callback_baton; 755251881Speter 756251881Speter SVN_ERR(get_username(session, pool)); 757251881Speter 758251881Speter /* If there are lock tokens to add, do so. */ 759251881Speter SVN_ERR(apply_lock_tokens(sess->fs, sess->fs_path->data, lock_tokens, 760251881Speter session->pool, pool)); 761251881Speter 762251881Speter /* Copy the revprops table so we can add the username. */ 763251881Speter revprop_table = apr_hash_copy(pool, revprop_table); 764251881Speter svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR, 765251881Speter svn_string_create(sess->username, pool)); 766251881Speter svn_hash_sets(revprop_table, SVN_PROP_TXN_CLIENT_COMPAT_VERSION, 767251881Speter svn_string_create(SVN_VER_NUMBER, pool)); 768251881Speter 769251881Speter /* Get the repos commit-editor */ 770251881Speter return svn_repos_get_commit_editor5 771251881Speter (editor, edit_baton, sess->repos, NULL, 772251881Speter svn_path_uri_decode(sess->repos_url, pool), sess->fs_path->data, 773251881Speter revprop_table, deltify_etc, deb, NULL, NULL, pool); 774251881Speter} 775251881Speter 776251881Speter 777251881Speterstatic svn_error_t * 778251881Spetersvn_ra_local__get_mergeinfo(svn_ra_session_t *session, 779251881Speter svn_mergeinfo_catalog_t *catalog, 780251881Speter const apr_array_header_t *paths, 781251881Speter svn_revnum_t revision, 782251881Speter svn_mergeinfo_inheritance_t inherit, 783251881Speter svn_boolean_t include_descendants, 784251881Speter apr_pool_t *pool) 785251881Speter{ 786251881Speter svn_ra_local__session_baton_t *sess = session->priv; 787251881Speter svn_mergeinfo_catalog_t tmp_catalog; 788251881Speter int i; 789251881Speter apr_array_header_t *abs_paths = 790251881Speter apr_array_make(pool, 0, sizeof(const char *)); 791251881Speter 792251881Speter for (i = 0; i < paths->nelts; i++) 793251881Speter { 794251881Speter const char *relative_path = APR_ARRAY_IDX(paths, i, const char *); 795251881Speter APR_ARRAY_PUSH(abs_paths, const char *) = 796251881Speter svn_fspath__join(sess->fs_path->data, relative_path, pool); 797251881Speter } 798251881Speter 799251881Speter SVN_ERR(svn_repos_fs_get_mergeinfo(&tmp_catalog, sess->repos, abs_paths, 800251881Speter revision, inherit, include_descendants, 801251881Speter NULL, NULL, pool)); 802251881Speter if (apr_hash_count(tmp_catalog) > 0) 803251881Speter SVN_ERR(svn_mergeinfo__remove_prefix_from_catalog(catalog, 804251881Speter tmp_catalog, 805251881Speter sess->fs_path->data, 806251881Speter pool)); 807251881Speter else 808251881Speter *catalog = NULL; 809251881Speter 810251881Speter return SVN_NO_ERROR; 811251881Speter} 812251881Speter 813251881Speter 814251881Speterstatic svn_error_t * 815251881Spetersvn_ra_local__do_update(svn_ra_session_t *session, 816251881Speter const svn_ra_reporter3_t **reporter, 817251881Speter void **report_baton, 818251881Speter svn_revnum_t update_revision, 819251881Speter const char *update_target, 820251881Speter svn_depth_t depth, 821251881Speter svn_boolean_t send_copyfrom_args, 822251881Speter svn_boolean_t ignore_ancestry, 823251881Speter const svn_delta_editor_t *update_editor, 824251881Speter void *update_baton, 825251881Speter apr_pool_t *result_pool, 826251881Speter apr_pool_t *scratch_pool) 827251881Speter{ 828251881Speter return make_reporter(session, 829251881Speter reporter, 830251881Speter report_baton, 831251881Speter update_revision, 832251881Speter update_target, 833251881Speter NULL, 834251881Speter TRUE, 835251881Speter depth, 836251881Speter send_copyfrom_args, 837251881Speter ignore_ancestry, 838251881Speter update_editor, 839251881Speter update_baton, 840251881Speter result_pool, scratch_pool); 841251881Speter} 842251881Speter 843251881Speter 844251881Speterstatic svn_error_t * 845251881Spetersvn_ra_local__do_switch(svn_ra_session_t *session, 846251881Speter const svn_ra_reporter3_t **reporter, 847251881Speter void **report_baton, 848251881Speter svn_revnum_t update_revision, 849251881Speter const char *update_target, 850251881Speter svn_depth_t depth, 851251881Speter const char *switch_url, 852251881Speter svn_boolean_t send_copyfrom_args, 853251881Speter svn_boolean_t ignore_ancestry, 854251881Speter const svn_delta_editor_t *update_editor, 855251881Speter void *update_baton, 856251881Speter apr_pool_t *result_pool, 857251881Speter apr_pool_t *scratch_pool) 858251881Speter{ 859251881Speter return make_reporter(session, 860251881Speter reporter, 861251881Speter report_baton, 862251881Speter update_revision, 863251881Speter update_target, 864251881Speter switch_url, 865251881Speter TRUE /* text_deltas */, 866251881Speter depth, 867251881Speter send_copyfrom_args, 868251881Speter ignore_ancestry, 869251881Speter update_editor, 870251881Speter update_baton, 871251881Speter result_pool, scratch_pool); 872251881Speter} 873251881Speter 874251881Speter 875251881Speterstatic svn_error_t * 876251881Spetersvn_ra_local__do_status(svn_ra_session_t *session, 877251881Speter const svn_ra_reporter3_t **reporter, 878251881Speter void **report_baton, 879251881Speter const char *status_target, 880251881Speter svn_revnum_t revision, 881251881Speter svn_depth_t depth, 882251881Speter const svn_delta_editor_t *status_editor, 883251881Speter void *status_baton, 884251881Speter apr_pool_t *pool) 885251881Speter{ 886251881Speter return make_reporter(session, 887251881Speter reporter, 888251881Speter report_baton, 889251881Speter revision, 890251881Speter status_target, 891251881Speter NULL, 892251881Speter FALSE, 893251881Speter depth, 894251881Speter FALSE, 895251881Speter FALSE, 896251881Speter status_editor, 897251881Speter status_baton, 898251881Speter pool, pool); 899251881Speter} 900251881Speter 901251881Speter 902251881Speterstatic svn_error_t * 903251881Spetersvn_ra_local__do_diff(svn_ra_session_t *session, 904251881Speter const svn_ra_reporter3_t **reporter, 905251881Speter void **report_baton, 906251881Speter svn_revnum_t update_revision, 907251881Speter const char *update_target, 908251881Speter svn_depth_t depth, 909251881Speter svn_boolean_t ignore_ancestry, 910251881Speter svn_boolean_t text_deltas, 911251881Speter const char *switch_url, 912251881Speter const svn_delta_editor_t *update_editor, 913251881Speter void *update_baton, 914251881Speter apr_pool_t *pool) 915251881Speter{ 916251881Speter return make_reporter(session, 917251881Speter reporter, 918251881Speter report_baton, 919251881Speter update_revision, 920251881Speter update_target, 921251881Speter switch_url, 922251881Speter text_deltas, 923251881Speter depth, 924251881Speter FALSE, 925251881Speter ignore_ancestry, 926251881Speter update_editor, 927251881Speter update_baton, 928251881Speter pool, pool); 929251881Speter} 930251881Speter 931251881Speter 932251881Speterstruct log_baton 933251881Speter{ 934251881Speter svn_ra_local__session_baton_t *sess; 935251881Speter svn_log_entry_receiver_t real_cb; 936251881Speter void *real_baton; 937251881Speter}; 938251881Speter 939251881Speterstatic svn_error_t * 940251881Speterlog_receiver_wrapper(void *baton, 941251881Speter svn_log_entry_t *log_entry, 942251881Speter apr_pool_t *pool) 943251881Speter{ 944251881Speter struct log_baton *b = baton; 945251881Speter svn_ra_local__session_baton_t *sess = b->sess; 946251881Speter 947251881Speter if (sess->callbacks->cancel_func) 948251881Speter SVN_ERR((sess->callbacks->cancel_func)(sess->callback_baton)); 949251881Speter 950251881Speter /* For consistency with the other RA layers, replace an empty 951251881Speter changed-paths hash with a NULL one. 952251881Speter 953251881Speter ### Should this be done by svn_ra_get_log2() instead, then? */ 954251881Speter if ((log_entry->changed_paths2) 955251881Speter && (apr_hash_count(log_entry->changed_paths2) == 0)) 956251881Speter { 957251881Speter log_entry->changed_paths = NULL; 958251881Speter log_entry->changed_paths2 = NULL; 959251881Speter } 960251881Speter 961251881Speter return b->real_cb(b->real_baton, log_entry, pool); 962251881Speter} 963251881Speter 964251881Speter 965251881Speterstatic svn_error_t * 966251881Spetersvn_ra_local__get_log(svn_ra_session_t *session, 967251881Speter const apr_array_header_t *paths, 968251881Speter svn_revnum_t start, 969251881Speter svn_revnum_t end, 970251881Speter int limit, 971251881Speter svn_boolean_t discover_changed_paths, 972251881Speter svn_boolean_t strict_node_history, 973251881Speter svn_boolean_t include_merged_revisions, 974251881Speter const apr_array_header_t *revprops, 975251881Speter svn_log_entry_receiver_t receiver, 976251881Speter void *receiver_baton, 977251881Speter apr_pool_t *pool) 978251881Speter{ 979251881Speter svn_ra_local__session_baton_t *sess = session->priv; 980251881Speter struct log_baton lb; 981251881Speter apr_array_header_t *abs_paths = 982251881Speter apr_array_make(pool, 0, sizeof(const char *)); 983251881Speter 984251881Speter if (paths) 985251881Speter { 986251881Speter int i; 987251881Speter 988251881Speter for (i = 0; i < paths->nelts; i++) 989251881Speter { 990251881Speter const char *relative_path = APR_ARRAY_IDX(paths, i, const char *); 991251881Speter APR_ARRAY_PUSH(abs_paths, const char *) = 992251881Speter svn_fspath__join(sess->fs_path->data, relative_path, pool); 993251881Speter } 994251881Speter } 995251881Speter 996251881Speter lb.real_cb = receiver; 997251881Speter lb.real_baton = receiver_baton; 998251881Speter lb.sess = sess; 999251881Speter receiver = log_receiver_wrapper; 1000251881Speter receiver_baton = &lb; 1001251881Speter 1002251881Speter return svn_repos_get_logs4(sess->repos, 1003251881Speter abs_paths, 1004251881Speter start, 1005251881Speter end, 1006251881Speter limit, 1007251881Speter discover_changed_paths, 1008251881Speter strict_node_history, 1009251881Speter include_merged_revisions, 1010251881Speter revprops, 1011251881Speter NULL, NULL, 1012251881Speter receiver, 1013251881Speter receiver_baton, 1014251881Speter pool); 1015251881Speter} 1016251881Speter 1017251881Speter 1018251881Speterstatic svn_error_t * 1019251881Spetersvn_ra_local__do_check_path(svn_ra_session_t *session, 1020251881Speter const char *path, 1021251881Speter svn_revnum_t revision, 1022251881Speter svn_node_kind_t *kind, 1023251881Speter apr_pool_t *pool) 1024251881Speter{ 1025251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1026251881Speter svn_fs_root_t *root; 1027251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1028251881Speter 1029251881Speter if (! SVN_IS_VALID_REVNUM(revision)) 1030251881Speter SVN_ERR(svn_fs_youngest_rev(&revision, sess->fs, pool)); 1031251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool)); 1032251881Speter return svn_fs_check_path(kind, root, abs_path, pool); 1033251881Speter} 1034251881Speter 1035251881Speter 1036251881Speterstatic svn_error_t * 1037251881Spetersvn_ra_local__stat(svn_ra_session_t *session, 1038251881Speter const char *path, 1039251881Speter svn_revnum_t revision, 1040251881Speter svn_dirent_t **dirent, 1041251881Speter apr_pool_t *pool) 1042251881Speter{ 1043251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1044251881Speter svn_fs_root_t *root; 1045251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1046251881Speter 1047251881Speter if (! SVN_IS_VALID_REVNUM(revision)) 1048251881Speter SVN_ERR(svn_fs_youngest_rev(&revision, sess->fs, pool)); 1049251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool)); 1050251881Speter 1051251881Speter return svn_repos_stat(dirent, root, abs_path, pool); 1052251881Speter} 1053251881Speter 1054251881Speter 1055251881Speter 1056251881Speter 1057251881Speterstatic svn_error_t * 1058251881Speterget_node_props(apr_hash_t **props, 1059251881Speter apr_array_header_t **inherited_props, 1060251881Speter svn_ra_local__session_baton_t *sess, 1061251881Speter svn_fs_root_t *root, 1062251881Speter const char *path, 1063251881Speter apr_pool_t *result_pool, 1064251881Speter apr_pool_t *scratch_pool) 1065251881Speter{ 1066251881Speter svn_revnum_t cmt_rev; 1067251881Speter const char *cmt_date, *cmt_author; 1068251881Speter 1069251881Speter /* Create a hash with props attached to the fs node. */ 1070251881Speter if (props) 1071251881Speter { 1072251881Speter SVN_ERR(svn_fs_node_proplist(props, root, path, result_pool)); 1073251881Speter } 1074251881Speter 1075251881Speter /* Get inherited properties if requested. */ 1076251881Speter if (inherited_props) 1077251881Speter { 1078251881Speter SVN_ERR(svn_repos_fs_get_inherited_props(inherited_props, root, path, 1079251881Speter NULL, NULL, NULL, 1080251881Speter result_pool, scratch_pool)); 1081251881Speter } 1082251881Speter 1083251881Speter /* Now add some non-tweakable metadata to the hash as well... */ 1084251881Speter 1085251881Speter if (props) 1086251881Speter { 1087251881Speter /* The so-called 'entryprops' with info about CR & friends. */ 1088251881Speter SVN_ERR(svn_repos_get_committed_info(&cmt_rev, &cmt_date, 1089251881Speter &cmt_author, root, path, 1090251881Speter scratch_pool)); 1091251881Speter 1092251881Speter svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_REV, 1093251881Speter svn_string_createf(result_pool, "%ld", cmt_rev)); 1094251881Speter svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_DATE, cmt_date ? 1095251881Speter svn_string_create(cmt_date, result_pool) :NULL); 1096251881Speter svn_hash_sets(*props, SVN_PROP_ENTRY_LAST_AUTHOR, cmt_author ? 1097251881Speter svn_string_create(cmt_author, result_pool) :NULL); 1098251881Speter svn_hash_sets(*props, SVN_PROP_ENTRY_UUID, 1099251881Speter svn_string_create(sess->uuid, result_pool)); 1100251881Speter 1101251881Speter /* We have no 'wcprops' in ra_local, but might someday. */ 1102251881Speter } 1103251881Speter 1104251881Speter return SVN_NO_ERROR; 1105251881Speter} 1106251881Speter 1107251881Speter 1108251881Speter/* Getting just one file. */ 1109251881Speterstatic svn_error_t * 1110251881Spetersvn_ra_local__get_file(svn_ra_session_t *session, 1111251881Speter const char *path, 1112251881Speter svn_revnum_t revision, 1113251881Speter svn_stream_t *stream, 1114251881Speter svn_revnum_t *fetched_rev, 1115251881Speter apr_hash_t **props, 1116251881Speter apr_pool_t *pool) 1117251881Speter{ 1118251881Speter svn_fs_root_t *root; 1119251881Speter svn_stream_t *contents; 1120251881Speter svn_revnum_t youngest_rev; 1121251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1122251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1123251881Speter svn_node_kind_t node_kind; 1124251881Speter 1125251881Speter /* Open the revision's root. */ 1126251881Speter if (! SVN_IS_VALID_REVNUM(revision)) 1127251881Speter { 1128251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest_rev, sess->fs, pool)); 1129251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, youngest_rev, pool)); 1130251881Speter if (fetched_rev != NULL) 1131251881Speter *fetched_rev = youngest_rev; 1132251881Speter } 1133251881Speter else 1134251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool)); 1135251881Speter 1136251881Speter SVN_ERR(svn_fs_check_path(&node_kind, root, abs_path, pool)); 1137251881Speter if (node_kind == svn_node_none) 1138251881Speter { 1139251881Speter return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 1140251881Speter _("'%s' path not found"), abs_path); 1141251881Speter } 1142251881Speter else if (node_kind != svn_node_file) 1143251881Speter { 1144251881Speter return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL, 1145251881Speter _("'%s' is not a file"), abs_path); 1146251881Speter } 1147251881Speter 1148251881Speter if (stream) 1149251881Speter { 1150251881Speter /* Get a stream representing the file's contents. */ 1151251881Speter SVN_ERR(svn_fs_file_contents(&contents, root, abs_path, pool)); 1152251881Speter 1153251881Speter /* Now push data from the fs stream back at the caller's stream. 1154251881Speter Note that this particular RA layer does not computing a 1155251881Speter checksum as we go, and confirming it against the repository's 1156251881Speter checksum when done. That's because it calls 1157251881Speter svn_fs_file_contents() directly, which already checks the 1158251881Speter stored checksum, and all we're doing here is writing bytes in 1159251881Speter a loop. Truly, Nothing Can Go Wrong :-). But RA layers that 1160251881Speter go over a network should confirm the checksum. 1161251881Speter 1162251881Speter Note: we are not supposed to close the passed-in stream, so 1163251881Speter disown the thing. 1164251881Speter */ 1165251881Speter SVN_ERR(svn_stream_copy3(contents, svn_stream_disown(stream, pool), 1166251881Speter sess->callbacks 1167251881Speter ? sess->callbacks->cancel_func : NULL, 1168251881Speter sess->callback_baton, 1169251881Speter pool)); 1170251881Speter } 1171251881Speter 1172251881Speter /* Handle props if requested. */ 1173251881Speter if (props) 1174251881Speter SVN_ERR(get_node_props(props, NULL, sess, root, abs_path, pool, pool)); 1175251881Speter 1176251881Speter return SVN_NO_ERROR; 1177251881Speter} 1178251881Speter 1179251881Speter 1180251881Speter 1181251881Speter/* Getting a directory's entries */ 1182251881Speterstatic svn_error_t * 1183251881Spetersvn_ra_local__get_dir(svn_ra_session_t *session, 1184251881Speter apr_hash_t **dirents, 1185251881Speter svn_revnum_t *fetched_rev, 1186251881Speter apr_hash_t **props, 1187251881Speter const char *path, 1188251881Speter svn_revnum_t revision, 1189251881Speter apr_uint32_t dirent_fields, 1190251881Speter apr_pool_t *pool) 1191251881Speter{ 1192251881Speter svn_fs_root_t *root; 1193251881Speter svn_revnum_t youngest_rev; 1194251881Speter apr_hash_t *entries; 1195251881Speter apr_hash_index_t *hi; 1196251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1197251881Speter apr_pool_t *subpool; 1198251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1199251881Speter 1200251881Speter /* Open the revision's root. */ 1201251881Speter if (! SVN_IS_VALID_REVNUM(revision)) 1202251881Speter { 1203251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest_rev, sess->fs, pool)); 1204251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, youngest_rev, pool)); 1205251881Speter if (fetched_rev != NULL) 1206251881Speter *fetched_rev = youngest_rev; 1207251881Speter } 1208251881Speter else 1209251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool)); 1210251881Speter 1211251881Speter if (dirents) 1212251881Speter { 1213251881Speter /* Get the dir's entries. */ 1214251881Speter SVN_ERR(svn_fs_dir_entries(&entries, root, abs_path, pool)); 1215251881Speter 1216251881Speter /* Loop over the fs dirents, and build a hash of general 1217251881Speter svn_dirent_t's. */ 1218251881Speter *dirents = apr_hash_make(pool); 1219251881Speter subpool = svn_pool_create(pool); 1220251881Speter for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) 1221251881Speter { 1222251881Speter const void *key; 1223251881Speter void *val; 1224251881Speter apr_hash_t *prophash; 1225251881Speter const char *datestring, *entryname, *fullpath; 1226251881Speter svn_fs_dirent_t *fs_entry; 1227251881Speter svn_dirent_t *entry = svn_dirent_create(pool); 1228251881Speter 1229251881Speter svn_pool_clear(subpool); 1230251881Speter 1231251881Speter apr_hash_this(hi, &key, NULL, &val); 1232251881Speter entryname = (const char *) key; 1233251881Speter fs_entry = (svn_fs_dirent_t *) val; 1234251881Speter 1235251881Speter fullpath = svn_dirent_join(abs_path, entryname, subpool); 1236251881Speter 1237251881Speter if (dirent_fields & SVN_DIRENT_KIND) 1238251881Speter { 1239251881Speter /* node kind */ 1240251881Speter entry->kind = fs_entry->kind; 1241251881Speter } 1242251881Speter 1243251881Speter if (dirent_fields & SVN_DIRENT_SIZE) 1244251881Speter { 1245251881Speter /* size */ 1246251881Speter if (entry->kind == svn_node_dir) 1247251881Speter entry->size = 0; 1248251881Speter else 1249251881Speter SVN_ERR(svn_fs_file_length(&(entry->size), root, 1250251881Speter fullpath, subpool)); 1251251881Speter } 1252251881Speter 1253251881Speter if (dirent_fields & SVN_DIRENT_HAS_PROPS) 1254251881Speter { 1255251881Speter /* has_props? */ 1256251881Speter SVN_ERR(svn_fs_node_proplist(&prophash, root, fullpath, 1257251881Speter subpool)); 1258251881Speter entry->has_props = (apr_hash_count(prophash) != 0); 1259251881Speter } 1260251881Speter 1261251881Speter if ((dirent_fields & SVN_DIRENT_TIME) 1262251881Speter || (dirent_fields & SVN_DIRENT_LAST_AUTHOR) 1263251881Speter || (dirent_fields & SVN_DIRENT_CREATED_REV)) 1264251881Speter { 1265251881Speter /* created_rev & friends */ 1266251881Speter SVN_ERR(svn_repos_get_committed_info(&(entry->created_rev), 1267251881Speter &datestring, 1268251881Speter &(entry->last_author), 1269251881Speter root, fullpath, subpool)); 1270251881Speter if (datestring) 1271251881Speter SVN_ERR(svn_time_from_cstring(&(entry->time), datestring, 1272251881Speter pool)); 1273251881Speter if (entry->last_author) 1274251881Speter entry->last_author = apr_pstrdup(pool, entry->last_author); 1275251881Speter } 1276251881Speter 1277251881Speter /* Store. */ 1278251881Speter svn_hash_sets(*dirents, entryname, entry); 1279251881Speter } 1280251881Speter svn_pool_destroy(subpool); 1281251881Speter } 1282251881Speter 1283251881Speter /* Handle props if requested. */ 1284251881Speter if (props) 1285251881Speter SVN_ERR(get_node_props(props, NULL, sess, root, abs_path, pool, pool)); 1286251881Speter 1287251881Speter return SVN_NO_ERROR; 1288251881Speter} 1289251881Speter 1290251881Speter 1291251881Speterstatic svn_error_t * 1292251881Spetersvn_ra_local__get_locations(svn_ra_session_t *session, 1293251881Speter apr_hash_t **locations, 1294251881Speter const char *path, 1295251881Speter svn_revnum_t peg_revision, 1296251881Speter const apr_array_header_t *location_revisions, 1297251881Speter apr_pool_t *pool) 1298251881Speter{ 1299251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1300251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1301251881Speter return svn_repos_trace_node_locations(sess->fs, locations, abs_path, 1302251881Speter peg_revision, location_revisions, 1303251881Speter NULL, NULL, pool); 1304251881Speter} 1305251881Speter 1306251881Speter 1307251881Speterstatic svn_error_t * 1308251881Spetersvn_ra_local__get_location_segments(svn_ra_session_t *session, 1309251881Speter const char *path, 1310251881Speter svn_revnum_t peg_revision, 1311251881Speter svn_revnum_t start_rev, 1312251881Speter svn_revnum_t end_rev, 1313251881Speter svn_location_segment_receiver_t receiver, 1314251881Speter void *receiver_baton, 1315251881Speter apr_pool_t *pool) 1316251881Speter{ 1317251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1318251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1319251881Speter return svn_repos_node_location_segments(sess->repos, abs_path, 1320251881Speter peg_revision, start_rev, end_rev, 1321251881Speter receiver, receiver_baton, 1322251881Speter NULL, NULL, pool); 1323251881Speter} 1324251881Speter 1325251881Speter 1326251881Speterstatic svn_error_t * 1327251881Spetersvn_ra_local__lock(svn_ra_session_t *session, 1328251881Speter apr_hash_t *path_revs, 1329251881Speter const char *comment, 1330251881Speter svn_boolean_t force, 1331251881Speter svn_ra_lock_callback_t lock_func, 1332251881Speter void *lock_baton, 1333251881Speter apr_pool_t *pool) 1334251881Speter{ 1335251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1336251881Speter apr_hash_index_t *hi; 1337251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 1338251881Speter 1339251881Speter /* A username is absolutely required to lock a path. */ 1340251881Speter SVN_ERR(get_username(session, pool)); 1341251881Speter 1342251881Speter for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi)) 1343251881Speter { 1344251881Speter svn_lock_t *lock; 1345251881Speter const void *key; 1346251881Speter const char *path; 1347251881Speter void *val; 1348251881Speter svn_revnum_t *revnum; 1349251881Speter const char *abs_path; 1350251881Speter svn_error_t *err, *callback_err = NULL; 1351251881Speter 1352251881Speter svn_pool_clear(iterpool); 1353251881Speter apr_hash_this(hi, &key, NULL, &val); 1354251881Speter path = key; 1355251881Speter revnum = val; 1356251881Speter 1357251881Speter abs_path = svn_fspath__join(sess->fs_path->data, path, iterpool); 1358251881Speter 1359251881Speter /* This wrapper will call pre- and post-lock hooks. */ 1360251881Speter err = svn_repos_fs_lock(&lock, sess->repos, abs_path, NULL, comment, 1361251881Speter FALSE /* not DAV comment */, 1362251881Speter 0 /* no expiration */, *revnum, force, 1363251881Speter iterpool); 1364251881Speter 1365251881Speter if (err && !SVN_ERR_IS_LOCK_ERROR(err)) 1366251881Speter return err; 1367251881Speter 1368251881Speter if (lock_func) 1369251881Speter callback_err = lock_func(lock_baton, path, TRUE, err ? NULL : lock, 1370251881Speter err, iterpool); 1371251881Speter 1372251881Speter svn_error_clear(err); 1373251881Speter 1374251881Speter if (callback_err) 1375251881Speter return callback_err; 1376251881Speter } 1377251881Speter 1378251881Speter svn_pool_destroy(iterpool); 1379251881Speter 1380251881Speter return SVN_NO_ERROR; 1381251881Speter} 1382251881Speter 1383251881Speter 1384251881Speterstatic svn_error_t * 1385251881Spetersvn_ra_local__unlock(svn_ra_session_t *session, 1386251881Speter apr_hash_t *path_tokens, 1387251881Speter svn_boolean_t force, 1388251881Speter svn_ra_lock_callback_t lock_func, 1389251881Speter void *lock_baton, 1390251881Speter apr_pool_t *pool) 1391251881Speter{ 1392251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1393251881Speter apr_hash_index_t *hi; 1394251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 1395251881Speter 1396251881Speter /* A username is absolutely required to unlock a path. */ 1397251881Speter SVN_ERR(get_username(session, pool)); 1398251881Speter 1399251881Speter for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi)) 1400251881Speter { 1401251881Speter const void *key; 1402251881Speter const char *path; 1403251881Speter void *val; 1404251881Speter const char *abs_path, *token; 1405251881Speter svn_error_t *err, *callback_err = NULL; 1406251881Speter 1407251881Speter svn_pool_clear(iterpool); 1408251881Speter apr_hash_this(hi, &key, NULL, &val); 1409251881Speter path = key; 1410251881Speter /* Since we can't store NULL values in a hash, we turn "" to 1411251881Speter NULL here. */ 1412251881Speter if (strcmp(val, "") != 0) 1413251881Speter token = val; 1414251881Speter else 1415251881Speter token = NULL; 1416251881Speter 1417251881Speter abs_path = svn_fspath__join(sess->fs_path->data, path, iterpool); 1418251881Speter 1419251881Speter /* This wrapper will call pre- and post-unlock hooks. */ 1420251881Speter err = svn_repos_fs_unlock(sess->repos, abs_path, token, force, 1421251881Speter iterpool); 1422251881Speter 1423251881Speter if (err && !SVN_ERR_IS_UNLOCK_ERROR(err)) 1424251881Speter return err; 1425251881Speter 1426251881Speter if (lock_func) 1427251881Speter callback_err = lock_func(lock_baton, path, FALSE, NULL, err, iterpool); 1428251881Speter 1429251881Speter svn_error_clear(err); 1430251881Speter 1431251881Speter if (callback_err) 1432251881Speter return callback_err; 1433251881Speter } 1434251881Speter 1435251881Speter svn_pool_destroy(iterpool); 1436251881Speter 1437251881Speter return SVN_NO_ERROR; 1438251881Speter} 1439251881Speter 1440251881Speter 1441251881Speter 1442251881Speterstatic svn_error_t * 1443251881Spetersvn_ra_local__get_lock(svn_ra_session_t *session, 1444251881Speter svn_lock_t **lock, 1445251881Speter const char *path, 1446251881Speter apr_pool_t *pool) 1447251881Speter{ 1448251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1449251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1450251881Speter return svn_fs_get_lock(lock, sess->fs, abs_path, pool); 1451251881Speter} 1452251881Speter 1453251881Speter 1454251881Speter 1455251881Speterstatic svn_error_t * 1456251881Spetersvn_ra_local__get_locks(svn_ra_session_t *session, 1457251881Speter apr_hash_t **locks, 1458251881Speter const char *path, 1459251881Speter svn_depth_t depth, 1460251881Speter apr_pool_t *pool) 1461251881Speter{ 1462251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1463251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1464251881Speter 1465251881Speter /* Kinda silly to call the repos wrapper, since we have no authz 1466251881Speter func to give it. But heck, why not. */ 1467251881Speter return svn_repos_fs_get_locks2(locks, sess->repos, abs_path, depth, 1468251881Speter NULL, NULL, pool); 1469251881Speter} 1470251881Speter 1471251881Speter 1472251881Speterstatic svn_error_t * 1473251881Spetersvn_ra_local__replay(svn_ra_session_t *session, 1474251881Speter svn_revnum_t revision, 1475251881Speter svn_revnum_t low_water_mark, 1476251881Speter svn_boolean_t send_deltas, 1477251881Speter const svn_delta_editor_t *editor, 1478251881Speter void *edit_baton, 1479251881Speter apr_pool_t *pool) 1480251881Speter{ 1481251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1482251881Speter svn_fs_root_t *root; 1483251881Speter 1484251881Speter SVN_ERR(svn_fs_revision_root(&root, svn_repos_fs(sess->repos), 1485251881Speter revision, pool)); 1486251881Speter return svn_repos_replay2(root, sess->fs_path->data, low_water_mark, 1487251881Speter send_deltas, editor, edit_baton, NULL, NULL, 1488251881Speter pool); 1489251881Speter} 1490251881Speter 1491251881Speter 1492251881Speterstatic svn_error_t * 1493251881Spetersvn_ra_local__replay_range(svn_ra_session_t *session, 1494251881Speter svn_revnum_t start_revision, 1495251881Speter svn_revnum_t end_revision, 1496251881Speter svn_revnum_t low_water_mark, 1497251881Speter svn_boolean_t send_deltas, 1498251881Speter svn_ra_replay_revstart_callback_t revstart_func, 1499251881Speter svn_ra_replay_revfinish_callback_t revfinish_func, 1500251881Speter void *replay_baton, 1501251881Speter apr_pool_t *pool) 1502251881Speter{ 1503251881Speter return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL); 1504251881Speter} 1505251881Speter 1506251881Speter 1507251881Speterstatic svn_error_t * 1508251881Spetersvn_ra_local__has_capability(svn_ra_session_t *session, 1509251881Speter svn_boolean_t *has, 1510251881Speter const char *capability, 1511251881Speter apr_pool_t *pool) 1512251881Speter{ 1513251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1514251881Speter 1515251881Speter if (strcmp(capability, SVN_RA_CAPABILITY_DEPTH) == 0 1516251881Speter || strcmp(capability, SVN_RA_CAPABILITY_LOG_REVPROPS) == 0 1517251881Speter || strcmp(capability, SVN_RA_CAPABILITY_PARTIAL_REPLAY) == 0 1518251881Speter || strcmp(capability, SVN_RA_CAPABILITY_COMMIT_REVPROPS) == 0 1519251881Speter || strcmp(capability, SVN_RA_CAPABILITY_ATOMIC_REVPROPS) == 0 1520251881Speter || strcmp(capability, SVN_RA_CAPABILITY_INHERITED_PROPS) == 0 1521251881Speter || strcmp(capability, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS) == 0 1522251881Speter || strcmp(capability, SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE) == 0 1523251881Speter ) 1524251881Speter { 1525251881Speter *has = TRUE; 1526251881Speter } 1527251881Speter else if (strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO) == 0) 1528251881Speter { 1529251881Speter /* With mergeinfo, the code's capabilities may not reflect the 1530251881Speter repository's, so inquire further. */ 1531251881Speter SVN_ERR(svn_repos_has_capability(sess->repos, has, 1532251881Speter SVN_REPOS_CAPABILITY_MERGEINFO, 1533251881Speter pool)); 1534251881Speter } 1535251881Speter else /* Don't know any other capabilities, so error. */ 1536251881Speter { 1537251881Speter return svn_error_createf 1538251881Speter (SVN_ERR_UNKNOWN_CAPABILITY, NULL, 1539251881Speter _("Don't know anything about capability '%s'"), capability); 1540251881Speter } 1541251881Speter 1542251881Speter return SVN_NO_ERROR; 1543251881Speter} 1544251881Speter 1545251881Speterstatic svn_error_t * 1546251881Spetersvn_ra_local__get_deleted_rev(svn_ra_session_t *session, 1547251881Speter const char *path, 1548251881Speter svn_revnum_t peg_revision, 1549251881Speter svn_revnum_t end_revision, 1550251881Speter svn_revnum_t *revision_deleted, 1551251881Speter apr_pool_t *pool) 1552251881Speter{ 1553251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1554251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1555251881Speter 1556251881Speter SVN_ERR(svn_repos_deleted_rev(sess->fs, 1557251881Speter abs_path, 1558251881Speter peg_revision, 1559251881Speter end_revision, 1560251881Speter revision_deleted, 1561251881Speter pool)); 1562251881Speter 1563251881Speter return SVN_NO_ERROR; 1564251881Speter} 1565251881Speter 1566251881Speterstatic svn_error_t * 1567251881Spetersvn_ra_local__get_inherited_props(svn_ra_session_t *session, 1568251881Speter apr_array_header_t **iprops, 1569251881Speter const char *path, 1570251881Speter svn_revnum_t revision, 1571251881Speter apr_pool_t *result_pool, 1572251881Speter apr_pool_t *scratch_pool) 1573251881Speter{ 1574251881Speter svn_fs_root_t *root; 1575251881Speter svn_revnum_t youngest_rev; 1576251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1577251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, 1578251881Speter scratch_pool); 1579251881Speter svn_node_kind_t node_kind; 1580251881Speter 1581251881Speter /* Open the revision's root. */ 1582251881Speter if (! SVN_IS_VALID_REVNUM(revision)) 1583251881Speter { 1584251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest_rev, sess->fs, scratch_pool)); 1585251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, youngest_rev, 1586251881Speter scratch_pool)); 1587251881Speter } 1588251881Speter else 1589251881Speter { 1590251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, scratch_pool)); 1591251881Speter } 1592251881Speter 1593251881Speter SVN_ERR(svn_fs_check_path(&node_kind, root, abs_path, scratch_pool)); 1594251881Speter if (node_kind == svn_node_none) 1595251881Speter { 1596251881Speter return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 1597251881Speter _("'%s' path not found"), abs_path); 1598251881Speter } 1599251881Speter 1600251881Speter return svn_error_trace(get_node_props(NULL, iprops, sess, root, abs_path, 1601251881Speter result_pool, scratch_pool)); 1602251881Speter} 1603251881Speter 1604251881Speterstatic svn_error_t * 1605251881Spetersvn_ra_local__register_editor_shim_callbacks(svn_ra_session_t *session, 1606251881Speter svn_delta_shim_callbacks_t *callbacks) 1607251881Speter{ 1608251881Speter /* This is currenly a no-op, since we don't provide our own editor, just 1609251881Speter use the one the libsvn_repos hands back to us. */ 1610251881Speter return SVN_NO_ERROR; 1611251881Speter} 1612251881Speter 1613251881Speter 1614251881Speterstatic svn_error_t * 1615251881Spetersvn_ra_local__get_commit_ev2(svn_editor_t **editor, 1616251881Speter svn_ra_session_t *session, 1617251881Speter apr_hash_t *revprops, 1618251881Speter svn_commit_callback2_t commit_cb, 1619251881Speter void *commit_baton, 1620251881Speter apr_hash_t *lock_tokens, 1621251881Speter svn_boolean_t keep_locks, 1622251881Speter svn_ra__provide_base_cb_t provide_base_cb, 1623251881Speter svn_ra__provide_props_cb_t provide_props_cb, 1624251881Speter svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb, 1625251881Speter void *cb_baton, 1626251881Speter svn_cancel_func_t cancel_func, 1627251881Speter void *cancel_baton, 1628251881Speter apr_pool_t *result_pool, 1629251881Speter apr_pool_t *scratch_pool) 1630251881Speter{ 1631251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1632251881Speter struct deltify_etc_baton *deb = apr_palloc(result_pool, sizeof(*deb)); 1633251881Speter 1634251881Speter /* NOTE: the RA callbacks are ignored. We pass everything directly to 1635251881Speter the REPOS editor. */ 1636251881Speter 1637251881Speter /* Prepare the baton for deltify_etc() */ 1638251881Speter deb->fs = sess->fs; 1639251881Speter deb->repos = sess->repos; 1640251881Speter deb->fspath_base = sess->fs_path->data; 1641251881Speter if (! keep_locks) 1642251881Speter deb->lock_tokens = lock_tokens; 1643251881Speter else 1644251881Speter deb->lock_tokens = NULL; 1645251881Speter deb->commit_cb = commit_cb; 1646251881Speter deb->commit_baton = commit_baton; 1647251881Speter 1648251881Speter /* Ensure there is a username (and an FS access context) associated with 1649251881Speter the session and its FS handle. */ 1650251881Speter SVN_ERR(get_username(session, scratch_pool)); 1651251881Speter 1652251881Speter /* If there are lock tokens to add, do so. */ 1653251881Speter SVN_ERR(apply_lock_tokens(sess->fs, sess->fs_path->data, lock_tokens, 1654251881Speter session->pool, scratch_pool)); 1655251881Speter 1656251881Speter /* Copy the REVPROPS and insert the author/username. */ 1657251881Speter revprops = apr_hash_copy(scratch_pool, revprops); 1658251881Speter svn_hash_sets(revprops, SVN_PROP_REVISION_AUTHOR, 1659251881Speter svn_string_create(sess->username, scratch_pool)); 1660251881Speter 1661251881Speter return svn_error_trace(svn_repos__get_commit_ev2( 1662251881Speter editor, sess->repos, NULL /* authz */, 1663251881Speter NULL /* authz_repos_name */, NULL /* authz_user */, 1664251881Speter revprops, 1665251881Speter deltify_etc, deb, cancel_func, cancel_baton, 1666251881Speter result_pool, scratch_pool)); 1667251881Speter} 1668251881Speter 1669251881Speter/*----------------------------------------------------------------*/ 1670251881Speter 1671251881Speterstatic const svn_version_t * 1672251881Speterra_local_version(void) 1673251881Speter{ 1674251881Speter SVN_VERSION_BODY; 1675251881Speter} 1676251881Speter 1677251881Speter/** The ra_vtable **/ 1678251881Speter 1679251881Speterstatic const svn_ra__vtable_t ra_local_vtable = 1680251881Speter{ 1681251881Speter ra_local_version, 1682251881Speter svn_ra_local__get_description, 1683251881Speter svn_ra_local__get_schemes, 1684251881Speter svn_ra_local__open, 1685251881Speter svn_ra_local__reparent, 1686251881Speter svn_ra_local__get_session_url, 1687251881Speter svn_ra_local__get_latest_revnum, 1688251881Speter svn_ra_local__get_dated_revision, 1689251881Speter svn_ra_local__change_rev_prop, 1690251881Speter svn_ra_local__rev_proplist, 1691251881Speter svn_ra_local__rev_prop, 1692251881Speter svn_ra_local__get_commit_editor, 1693251881Speter svn_ra_local__get_file, 1694251881Speter svn_ra_local__get_dir, 1695251881Speter svn_ra_local__get_mergeinfo, 1696251881Speter svn_ra_local__do_update, 1697251881Speter svn_ra_local__do_switch, 1698251881Speter svn_ra_local__do_status, 1699251881Speter svn_ra_local__do_diff, 1700251881Speter svn_ra_local__get_log, 1701251881Speter svn_ra_local__do_check_path, 1702251881Speter svn_ra_local__stat, 1703251881Speter svn_ra_local__get_uuid, 1704251881Speter svn_ra_local__get_repos_root, 1705251881Speter svn_ra_local__get_locations, 1706251881Speter svn_ra_local__get_location_segments, 1707251881Speter svn_ra_local__get_file_revs, 1708251881Speter svn_ra_local__lock, 1709251881Speter svn_ra_local__unlock, 1710251881Speter svn_ra_local__get_lock, 1711251881Speter svn_ra_local__get_locks, 1712251881Speter svn_ra_local__replay, 1713251881Speter svn_ra_local__has_capability, 1714251881Speter svn_ra_local__replay_range, 1715251881Speter svn_ra_local__get_deleted_rev, 1716251881Speter svn_ra_local__register_editor_shim_callbacks, 1717251881Speter svn_ra_local__get_inherited_props, 1718251881Speter svn_ra_local__get_commit_ev2 1719251881Speter}; 1720251881Speter 1721251881Speter 1722251881Speter/*----------------------------------------------------------------*/ 1723251881Speter 1724251881Speter/** The One Public Routine, called by libsvn_ra **/ 1725251881Speter 1726251881Spetersvn_error_t * 1727251881Spetersvn_ra_local__init(const svn_version_t *loader_version, 1728251881Speter const svn_ra__vtable_t **vtable, 1729251881Speter apr_pool_t *pool) 1730251881Speter{ 1731251881Speter static const svn_version_checklist_t checklist[] = 1732251881Speter { 1733251881Speter { "svn_subr", svn_subr_version }, 1734251881Speter { "svn_delta", svn_delta_version }, 1735251881Speter { "svn_repos", svn_repos_version }, 1736251881Speter { "svn_fs", svn_fs_version }, 1737251881Speter { NULL, NULL } 1738251881Speter }; 1739251881Speter 1740251881Speter 1741251881Speter /* Simplified version check to make sure we can safely use the 1742251881Speter VTABLE parameter. The RA loader does a more exhaustive check. */ 1743251881Speter if (loader_version->major != SVN_VER_MAJOR) 1744251881Speter return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL, 1745251881Speter _("Unsupported RA loader version (%d) for " 1746251881Speter "ra_local"), 1747251881Speter loader_version->major); 1748251881Speter 1749262253Speter SVN_ERR(svn_ver_check_list2(ra_local_version(), checklist, svn_ver_equal)); 1750251881Speter 1751251881Speter#ifndef SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL 1752251881Speter /* This assumes that POOL was the pool used to load the dso. */ 1753251881Speter SVN_ERR(svn_fs_initialize(pool)); 1754251881Speter#endif 1755251881Speter 1756251881Speter *vtable = &ra_local_vtable; 1757251881Speter 1758251881Speter return SVN_NO_ERROR; 1759251881Speter} 1760251881Speter 1761251881Speter/* Compatibility wrapper for the 1.1 and before API. */ 1762251881Speter#define NAME "ra_local" 1763251881Speter#define DESCRIPTION RA_LOCAL_DESCRIPTION 1764251881Speter#define VTBL ra_local_vtable 1765251881Speter#define INITFUNC svn_ra_local__init 1766251881Speter#define COMPAT_INITFUNC svn_ra_local_init 1767251881Speter#include "../libsvn_ra/wrapper_template.h" 1768