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. */ 90299742Sdim if (sess->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 */ 99299742Sdim sess->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 244299742Sdim fs_path = apr_pstrcat(pool, "/", relpath, SVN_VA_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, 331299742Sdim SVN_VA_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, 363299742Sdim 0, /* Disable zero-copy codepath, because 364299742Sdim RA API users are unaware about the 365299742Sdim zero-copy code path limitation (do 366299742Sdim not access FSFS data structures 367299742Sdim and, hence, caches). See notes 368299742Sdim to svn_repos_begin_report3() for 369299742Sdim additional details. */ 370251881Speter result_pool)); 371251881Speter 372251881Speter /* Wrap the report baton given us by the repos layer with our own 373251881Speter reporter baton. */ 374251881Speter *report_baton = make_reporter_baton(sess, rbaton, result_pool); 375251881Speter 376251881Speter return SVN_NO_ERROR; 377251881Speter} 378251881Speter 379251881Speter 380251881Speter/*----------------------------------------------------------------*/ 381251881Speter 382251881Speter/*** Deltification stuff for get_commit_editor() ***/ 383251881Speter 384251881Speterstruct deltify_etc_baton 385251881Speter{ 386251881Speter svn_fs_t *fs; /* the fs to deltify in */ 387251881Speter svn_repos_t *repos; /* repos for unlocking */ 388251881Speter const char *fspath_base; /* fs-path part of split session URL */ 389251881Speter 390251881Speter apr_hash_t *lock_tokens; /* tokens to unlock, if any */ 391251881Speter 392251881Speter svn_commit_callback2_t commit_cb; /* the original callback */ 393251881Speter void *commit_baton; /* the original callback's baton */ 394251881Speter}; 395251881Speter 396251881Speter/* This implements 'svn_commit_callback_t'. Its invokes the original 397251881Speter (wrapped) callback, but also does deltification on the new revision and 398251881Speter possibly unlocks committed paths. 399251881Speter BATON is 'struct deltify_etc_baton *'. */ 400251881Speterstatic svn_error_t * 401251881Speterdeltify_etc(const svn_commit_info_t *commit_info, 402251881Speter void *baton, 403251881Speter apr_pool_t *scratch_pool) 404251881Speter{ 405251881Speter struct deltify_etc_baton *deb = baton; 406251881Speter svn_error_t *err1 = SVN_NO_ERROR; 407251881Speter svn_error_t *err2; 408251881Speter 409251881Speter /* Invoke the original callback first, in case someone's waiting to 410251881Speter know the revision number so they can go off and annotate an 411251881Speter issue or something. */ 412251881Speter if (deb->commit_cb) 413251881Speter err1 = deb->commit_cb(commit_info, deb->commit_baton, scratch_pool); 414251881Speter 415251881Speter /* Maybe unlock the paths. */ 416251881Speter if (deb->lock_tokens) 417251881Speter { 418299742Sdim apr_pool_t *subpool = svn_pool_create(scratch_pool); 419299742Sdim apr_hash_t *targets = apr_hash_make(subpool); 420251881Speter apr_hash_index_t *hi; 421251881Speter 422299742Sdim for (hi = apr_hash_first(subpool, deb->lock_tokens); hi; 423251881Speter hi = apr_hash_next(hi)) 424251881Speter { 425299742Sdim const void *relpath = apr_hash_this_key(hi); 426299742Sdim const char *token = apr_hash_this_val(hi); 427251881Speter const char *fspath; 428251881Speter 429299742Sdim fspath = svn_fspath__join(deb->fspath_base, relpath, subpool); 430299742Sdim svn_hash_sets(targets, fspath, token); 431299742Sdim } 432251881Speter 433299742Sdim /* We may get errors here if the lock was broken or stolen 434299742Sdim after the commit succeeded. This is fine and should be 435299742Sdim ignored. */ 436299742Sdim svn_error_clear(svn_repos_fs_unlock_many(deb->repos, targets, FALSE, 437299742Sdim NULL, NULL, 438299742Sdim subpool, subpool)); 439251881Speter 440299742Sdim svn_pool_destroy(subpool); 441251881Speter } 442251881Speter 443251881Speter /* But, deltification shouldn't be stopped just because someone's 444251881Speter random callback failed, so proceed unconditionally on to 445251881Speter deltification. */ 446251881Speter err2 = svn_fs_deltify_revision(deb->fs, commit_info->revision, scratch_pool); 447251881Speter 448251881Speter return svn_error_compose_create(err1, err2); 449251881Speter} 450251881Speter 451251881Speter 452251881Speter/* If LOCK_TOKENS is not NULL, then copy all tokens into the access context 453251881Speter of FS. The tokens' paths will be prepended with FSPATH_BASE. 454251881Speter 455251881Speter ACCESS_POOL must match (or exceed) the lifetime of the access context 456251881Speter that was associated with FS. Typically, this is the session pool. 457251881Speter 458251881Speter Temporary allocations are made in SCRATCH_POOL. */ 459251881Speterstatic svn_error_t * 460251881Speterapply_lock_tokens(svn_fs_t *fs, 461251881Speter const char *fspath_base, 462251881Speter apr_hash_t *lock_tokens, 463251881Speter apr_pool_t *access_pool, 464251881Speter apr_pool_t *scratch_pool) 465251881Speter{ 466251881Speter if (lock_tokens) 467251881Speter { 468251881Speter svn_fs_access_t *access_ctx; 469251881Speter 470251881Speter SVN_ERR(svn_fs_get_access(&access_ctx, fs)); 471251881Speter 472251881Speter /* If there is no access context, the filesystem will scream if a 473251881Speter lock is needed. */ 474251881Speter if (access_ctx) 475251881Speter { 476251881Speter apr_hash_index_t *hi; 477251881Speter 478251881Speter /* Note: we have no use for an iterpool here since the data 479251881Speter within the loop is copied into ACCESS_POOL. */ 480251881Speter 481251881Speter for (hi = apr_hash_first(scratch_pool, lock_tokens); hi; 482251881Speter hi = apr_hash_next(hi)) 483251881Speter { 484299742Sdim const void *relpath = apr_hash_this_key(hi); 485299742Sdim const char *token = apr_hash_this_val(hi); 486251881Speter const char *fspath; 487251881Speter 488251881Speter /* The path needs to live as long as ACCESS_CTX. */ 489251881Speter fspath = svn_fspath__join(fspath_base, relpath, access_pool); 490251881Speter 491251881Speter /* The token must live as long as ACCESS_CTX. */ 492251881Speter token = apr_pstrdup(access_pool, token); 493251881Speter 494251881Speter SVN_ERR(svn_fs_access_add_lock_token2(access_ctx, fspath, 495251881Speter token)); 496251881Speter } 497251881Speter } 498251881Speter } 499251881Speter 500251881Speter return SVN_NO_ERROR; 501251881Speter} 502251881Speter 503251881Speter 504251881Speter/*----------------------------------------------------------------*/ 505251881Speter 506251881Speter/*** The RA vtable routines ***/ 507251881Speter 508251881Speter#define RA_LOCAL_DESCRIPTION \ 509251881Speter N_("Module for accessing a repository on local disk.") 510251881Speter 511251881Speterstatic const char * 512262253Spetersvn_ra_local__get_description(apr_pool_t *pool) 513251881Speter{ 514251881Speter return _(RA_LOCAL_DESCRIPTION); 515251881Speter} 516251881Speter 517251881Speterstatic const char * const * 518251881Spetersvn_ra_local__get_schemes(apr_pool_t *pool) 519251881Speter{ 520251881Speter static const char *schemes[] = { "file", NULL }; 521251881Speter 522251881Speter return schemes; 523251881Speter} 524251881Speter 525251881Speter/* Do nothing. 526251881Speter * 527251881Speter * Why is this acceptable? FS warnings used to be used for only 528251881Speter * two things: failures to close BDB repositories and failures to 529251881Speter * interact with memcached in FSFS (new in 1.6). In 1.5 and earlier, 530251881Speter * we did not call svn_fs_set_warning_func in ra_local, which means 531251881Speter * that any BDB-closing failure would have led to abort()s; the fact 532251881Speter * that this hasn't led to huge hues and cries makes it seem likely 533251881Speter * that this just doesn't happen that often, at least not through 534251881Speter * ra_local. And as far as memcached goes, it seems unlikely that 535251881Speter * somebody is going to go through the trouble of setting up and 536251881Speter * running memcached servers but then use ra_local access. So we 537251881Speter * ignore errors here, so that memcached can use the FS warnings API 538251881Speter * without crashing ra_local. 539251881Speter */ 540251881Speterstatic void 541251881Speterignore_warnings(void *baton, 542251881Speter svn_error_t *err) 543251881Speter{ 544251881Speter#ifdef SVN_DEBUG 545299742Sdim SVN_DBG(("Ignoring FS warning %s\n", 546299742Sdim svn_error_symbolic_name(err ? err->apr_err : 0))); 547251881Speter#endif 548251881Speter return; 549251881Speter} 550251881Speter 551299742Sdim#define USER_AGENT "SVN/" SVN_VER_NUMBER " (" SVN_BUILD_TARGET ")" \ 552299742Sdim " ra_local" 553299742Sdim 554251881Speterstatic svn_error_t * 555251881Spetersvn_ra_local__open(svn_ra_session_t *session, 556251881Speter const char **corrected_url, 557251881Speter const char *repos_URL, 558251881Speter const svn_ra_callbacks2_t *callbacks, 559251881Speter void *callback_baton, 560299742Sdim svn_auth_baton_t *auth_baton, 561251881Speter apr_hash_t *config, 562299742Sdim apr_pool_t *result_pool, 563299742Sdim apr_pool_t *scratch_pool) 564251881Speter{ 565299742Sdim const char *client_string; 566251881Speter svn_ra_local__session_baton_t *sess; 567251881Speter const char *fs_path; 568251881Speter static volatile svn_atomic_t cache_init_state = 0; 569299742Sdim apr_pool_t *pool = result_pool; 570251881Speter 571251881Speter /* Initialise the FSFS memory cache size. We can only do this once 572251881Speter so one CONFIG will win the race and all others will be ignored 573251881Speter silently. */ 574251881Speter SVN_ERR(svn_atomic__init_once(&cache_init_state, cache_init, config, pool)); 575251881Speter 576251881Speter /* We don't support redirections in ra-local. */ 577251881Speter if (corrected_url) 578251881Speter *corrected_url = NULL; 579251881Speter 580251881Speter /* Allocate and stash the session_sess args we have already. */ 581251881Speter sess = apr_pcalloc(pool, sizeof(*sess)); 582251881Speter sess->callbacks = callbacks; 583251881Speter sess->callback_baton = callback_baton; 584299742Sdim sess->auth_baton = auth_baton; 585251881Speter 586251881Speter /* Look through the URL, figure out which part points to the 587251881Speter repository, and which part is the path *within* the 588251881Speter repository. */ 589299742Sdim SVN_ERR(svn_ra_local__split_URL(&(sess->repos), 590299742Sdim &(sess->repos_url), 591299742Sdim &fs_path, 592299742Sdim repos_URL, 593299742Sdim session->pool)); 594251881Speter sess->fs_path = svn_stringbuf_create(fs_path, session->pool); 595251881Speter 596251881Speter /* Cache the filesystem object from the repos here for 597251881Speter convenience. */ 598251881Speter sess->fs = svn_repos_fs(sess->repos); 599251881Speter 600251881Speter /* Ignore FS warnings. */ 601251881Speter svn_fs_set_warning_func(sess->fs, ignore_warnings, NULL); 602251881Speter 603251881Speter /* Cache the repository UUID as well */ 604251881Speter SVN_ERR(svn_fs_get_uuid(sess->fs, &sess->uuid, session->pool)); 605251881Speter 606251881Speter /* Be sure username is NULL so we know to look it up / ask for it */ 607251881Speter sess->username = NULL; 608251881Speter 609299742Sdim if (sess->callbacks->get_client_string != NULL) 610299742Sdim SVN_ERR(sess->callbacks->get_client_string(sess->callback_baton, 611299742Sdim &client_string, pool)); 612299742Sdim else 613299742Sdim client_string = NULL; 614299742Sdim 615299742Sdim if (client_string) 616299742Sdim sess->useragent = apr_pstrcat(pool, USER_AGENT " ", 617299742Sdim client_string, SVN_VA_NULL); 618299742Sdim else 619299742Sdim sess->useragent = USER_AGENT; 620299742Sdim 621251881Speter session->priv = sess; 622251881Speter return SVN_NO_ERROR; 623251881Speter} 624251881Speter 625251881Speterstatic svn_error_t * 626299742Sdimsvn_ra_local__dup_session(svn_ra_session_t *new_session, 627299742Sdim svn_ra_session_t *session, 628299742Sdim const char *new_session_url, 629299742Sdim apr_pool_t *result_pool, 630299742Sdim apr_pool_t *scratch_pool) 631299742Sdim{ 632299742Sdim svn_ra_local__session_baton_t *old_sess = session->priv; 633299742Sdim svn_ra_local__session_baton_t *new_sess; 634299742Sdim const char *fs_path; 635299742Sdim 636299742Sdim /* Allocate and stash the session_sess args we have already. */ 637299742Sdim new_sess = apr_pcalloc(result_pool, sizeof(*new_sess)); 638299742Sdim new_sess->callbacks = old_sess->callbacks; 639299742Sdim new_sess->callback_baton = old_sess->callback_baton; 640299742Sdim 641299742Sdim /* ### Re-use existing FS handle? */ 642299742Sdim 643299742Sdim /* Reuse existing code */ 644299742Sdim SVN_ERR(svn_ra_local__split_URL(&(new_sess->repos), 645299742Sdim &(new_sess->repos_url), 646299742Sdim &fs_path, 647299742Sdim new_session_url, 648299742Sdim result_pool)); 649299742Sdim 650299742Sdim new_sess->fs_path = svn_stringbuf_create(fs_path, result_pool); 651299742Sdim 652299742Sdim /* Cache the filesystem object from the repos here for 653299742Sdim convenience. */ 654299742Sdim new_sess->fs = svn_repos_fs(new_sess->repos); 655299742Sdim 656299742Sdim /* Ignore FS warnings. */ 657299742Sdim svn_fs_set_warning_func(new_sess->fs, ignore_warnings, NULL); 658299742Sdim 659299742Sdim /* Cache the repository UUID as well */ 660299742Sdim new_sess->uuid = apr_pstrdup(result_pool, old_sess->uuid); 661299742Sdim 662299742Sdim new_sess->username = old_sess->username 663299742Sdim ? apr_pstrdup(result_pool, old_sess->username) 664299742Sdim : NULL; 665299742Sdim 666299742Sdim new_sess->useragent = apr_pstrdup(result_pool, old_sess->useragent); 667299742Sdim new_session->priv = new_sess; 668299742Sdim 669299742Sdim return SVN_NO_ERROR; 670299742Sdim} 671299742Sdim 672299742Sdimstatic svn_error_t * 673251881Spetersvn_ra_local__reparent(svn_ra_session_t *session, 674251881Speter const char *url, 675251881Speter apr_pool_t *pool) 676251881Speter{ 677251881Speter svn_ra_local__session_baton_t *sess = session->priv; 678251881Speter const char *relpath = svn_uri_skip_ancestor(sess->repos_url, url, pool); 679251881Speter 680251881Speter /* If the new URL isn't the same as our repository root URL, then 681251881Speter let's ensure that it's some child of it. */ 682251881Speter if (! relpath) 683251881Speter return svn_error_createf 684251881Speter (SVN_ERR_RA_ILLEGAL_URL, NULL, 685251881Speter _("URL '%s' is not a child of the session's repository root " 686251881Speter "URL '%s'"), url, sess->repos_url); 687251881Speter 688251881Speter /* Update our FS_PATH sess member to point to our new 689251881Speter relative-URL-turned-absolute-filesystem-path. */ 690251881Speter svn_stringbuf_set(sess->fs_path, 691251881Speter svn_fspath__canonicalize(relpath, pool)); 692251881Speter 693251881Speter return SVN_NO_ERROR; 694251881Speter} 695251881Speter 696251881Speterstatic svn_error_t * 697251881Spetersvn_ra_local__get_session_url(svn_ra_session_t *session, 698251881Speter const char **url, 699251881Speter apr_pool_t *pool) 700251881Speter{ 701251881Speter svn_ra_local__session_baton_t *sess = session->priv; 702251881Speter *url = svn_path_url_add_component2(sess->repos_url, 703251881Speter sess->fs_path->data + 1, 704251881Speter pool); 705251881Speter return SVN_NO_ERROR; 706251881Speter} 707251881Speter 708251881Speterstatic svn_error_t * 709251881Spetersvn_ra_local__get_latest_revnum(svn_ra_session_t *session, 710251881Speter svn_revnum_t *latest_revnum, 711251881Speter apr_pool_t *pool) 712251881Speter{ 713251881Speter svn_ra_local__session_baton_t *sess = session->priv; 714251881Speter return svn_fs_youngest_rev(latest_revnum, sess->fs, pool); 715251881Speter} 716251881Speter 717251881Speterstatic svn_error_t * 718251881Spetersvn_ra_local__get_file_revs(svn_ra_session_t *session, 719251881Speter const char *path, 720251881Speter svn_revnum_t start, 721251881Speter svn_revnum_t end, 722251881Speter svn_boolean_t include_merged_revisions, 723251881Speter svn_file_rev_handler_t handler, 724251881Speter void *handler_baton, 725251881Speter apr_pool_t *pool) 726251881Speter{ 727251881Speter svn_ra_local__session_baton_t *sess = session->priv; 728251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 729251881Speter return svn_repos_get_file_revs2(sess->repos, abs_path, start, end, 730251881Speter include_merged_revisions, NULL, NULL, 731251881Speter handler, handler_baton, pool); 732251881Speter} 733251881Speter 734251881Speterstatic svn_error_t * 735251881Spetersvn_ra_local__get_dated_revision(svn_ra_session_t *session, 736251881Speter svn_revnum_t *revision, 737251881Speter apr_time_t tm, 738251881Speter apr_pool_t *pool) 739251881Speter{ 740251881Speter svn_ra_local__session_baton_t *sess = session->priv; 741251881Speter return svn_repos_dated_revision(revision, sess->repos, tm, pool); 742251881Speter} 743251881Speter 744251881Speter 745251881Speterstatic svn_error_t * 746251881Spetersvn_ra_local__change_rev_prop(svn_ra_session_t *session, 747251881Speter svn_revnum_t rev, 748251881Speter const char *name, 749251881Speter const svn_string_t *const *old_value_p, 750251881Speter const svn_string_t *value, 751251881Speter apr_pool_t *pool) 752251881Speter{ 753251881Speter svn_ra_local__session_baton_t *sess = session->priv; 754251881Speter 755251881Speter SVN_ERR(get_username(session, pool)); 756251881Speter return svn_repos_fs_change_rev_prop4(sess->repos, rev, sess->username, 757251881Speter name, old_value_p, value, TRUE, TRUE, 758251881Speter NULL, NULL, pool); 759251881Speter} 760251881Speter 761251881Speterstatic svn_error_t * 762251881Spetersvn_ra_local__get_uuid(svn_ra_session_t *session, 763251881Speter const char **uuid, 764251881Speter apr_pool_t *pool) 765251881Speter{ 766251881Speter svn_ra_local__session_baton_t *sess = session->priv; 767251881Speter *uuid = sess->uuid; 768251881Speter return SVN_NO_ERROR; 769251881Speter} 770251881Speter 771251881Speterstatic svn_error_t * 772251881Spetersvn_ra_local__get_repos_root(svn_ra_session_t *session, 773251881Speter const char **url, 774251881Speter apr_pool_t *pool) 775251881Speter{ 776251881Speter svn_ra_local__session_baton_t *sess = session->priv; 777251881Speter *url = sess->repos_url; 778251881Speter return SVN_NO_ERROR; 779251881Speter} 780251881Speter 781251881Speterstatic svn_error_t * 782251881Spetersvn_ra_local__rev_proplist(svn_ra_session_t *session, 783251881Speter svn_revnum_t rev, 784251881Speter apr_hash_t **props, 785251881Speter apr_pool_t *pool) 786251881Speter{ 787251881Speter svn_ra_local__session_baton_t *sess = session->priv; 788251881Speter return svn_repos_fs_revision_proplist(props, sess->repos, rev, 789251881Speter NULL, NULL, pool); 790251881Speter} 791251881Speter 792251881Speterstatic svn_error_t * 793251881Spetersvn_ra_local__rev_prop(svn_ra_session_t *session, 794251881Speter svn_revnum_t rev, 795251881Speter const char *name, 796251881Speter svn_string_t **value, 797251881Speter apr_pool_t *pool) 798251881Speter{ 799251881Speter svn_ra_local__session_baton_t *sess = session->priv; 800251881Speter return svn_repos_fs_revision_prop(value, sess->repos, rev, name, 801251881Speter NULL, NULL, pool); 802251881Speter} 803251881Speter 804299742Sdimstruct ccw_baton 805299742Sdim{ 806299742Sdim svn_commit_callback2_t original_callback; 807299742Sdim void *original_baton; 808299742Sdim 809299742Sdim svn_ra_session_t *session; 810299742Sdim}; 811299742Sdim 812299742Sdim/* Wrapper which populates the repos_root field of the commit_info struct */ 813251881Speterstatic svn_error_t * 814299742Sdimcommit_callback_wrapper(const svn_commit_info_t *commit_info, 815299742Sdim void *baton, 816299742Sdim apr_pool_t *scratch_pool) 817299742Sdim{ 818299742Sdim struct ccw_baton *ccwb = baton; 819299742Sdim svn_commit_info_t *ci = svn_commit_info_dup(commit_info, scratch_pool); 820299742Sdim 821299742Sdim SVN_ERR(svn_ra_local__get_repos_root(ccwb->session, &ci->repos_root, 822299742Sdim scratch_pool)); 823299742Sdim 824299742Sdim return svn_error_trace(ccwb->original_callback(ci, ccwb->original_baton, 825299742Sdim scratch_pool)); 826299742Sdim} 827299742Sdim 828299742Sdim 829299742Sdim/* The repository layer does not correctly fill in REPOS_ROOT in 830299742Sdim commit_info, as it doesn't know the url that is used to access 831299742Sdim it. This hooks the callback to fill in the missing pieces. */ 832299742Sdimstatic void 833299742Sdimremap_commit_callback(svn_commit_callback2_t *callback, 834299742Sdim void **callback_baton, 835299742Sdim svn_ra_session_t *session, 836299742Sdim svn_commit_callback2_t original_callback, 837299742Sdim void *original_baton, 838299742Sdim apr_pool_t *result_pool) 839299742Sdim{ 840299742Sdim if (original_callback == NULL) 841299742Sdim { 842299742Sdim *callback = NULL; 843299742Sdim *callback_baton = NULL; 844299742Sdim } 845299742Sdim else 846299742Sdim { 847299742Sdim /* Allocate this in RESULT_POOL, since the callback will be called 848299742Sdim long after this function has returned. */ 849299742Sdim struct ccw_baton *ccwb = apr_palloc(result_pool, sizeof(*ccwb)); 850299742Sdim 851299742Sdim ccwb->session = session; 852299742Sdim ccwb->original_callback = original_callback; 853299742Sdim ccwb->original_baton = original_baton; 854299742Sdim 855299742Sdim *callback = commit_callback_wrapper; 856299742Sdim *callback_baton = ccwb; 857299742Sdim } 858299742Sdim} 859299742Sdim 860299742Sdimstatic svn_error_t * 861251881Spetersvn_ra_local__get_commit_editor(svn_ra_session_t *session, 862251881Speter const svn_delta_editor_t **editor, 863251881Speter void **edit_baton, 864251881Speter apr_hash_t *revprop_table, 865251881Speter svn_commit_callback2_t callback, 866251881Speter void *callback_baton, 867251881Speter apr_hash_t *lock_tokens, 868251881Speter svn_boolean_t keep_locks, 869251881Speter apr_pool_t *pool) 870251881Speter{ 871251881Speter svn_ra_local__session_baton_t *sess = session->priv; 872251881Speter struct deltify_etc_baton *deb = apr_palloc(pool, sizeof(*deb)); 873251881Speter 874299742Sdim /* Set repos_root_url in commit info */ 875299742Sdim remap_commit_callback(&callback, &callback_baton, session, 876299742Sdim callback, callback_baton, pool); 877299742Sdim 878251881Speter /* Prepare the baton for deltify_etc() */ 879251881Speter deb->fs = sess->fs; 880251881Speter deb->repos = sess->repos; 881251881Speter deb->fspath_base = sess->fs_path->data; 882251881Speter if (! keep_locks) 883251881Speter deb->lock_tokens = lock_tokens; 884251881Speter else 885251881Speter deb->lock_tokens = NULL; 886251881Speter deb->commit_cb = callback; 887251881Speter deb->commit_baton = callback_baton; 888251881Speter 889251881Speter SVN_ERR(get_username(session, pool)); 890251881Speter 891251881Speter /* If there are lock tokens to add, do so. */ 892251881Speter SVN_ERR(apply_lock_tokens(sess->fs, sess->fs_path->data, lock_tokens, 893251881Speter session->pool, pool)); 894251881Speter 895251881Speter /* Copy the revprops table so we can add the username. */ 896251881Speter revprop_table = apr_hash_copy(pool, revprop_table); 897251881Speter svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR, 898251881Speter svn_string_create(sess->username, pool)); 899251881Speter svn_hash_sets(revprop_table, SVN_PROP_TXN_CLIENT_COMPAT_VERSION, 900251881Speter svn_string_create(SVN_VER_NUMBER, pool)); 901299742Sdim svn_hash_sets(revprop_table, SVN_PROP_TXN_USER_AGENT, 902299742Sdim svn_string_create(sess->useragent, pool)); 903251881Speter 904251881Speter /* Get the repos commit-editor */ 905251881Speter return svn_repos_get_commit_editor5 906251881Speter (editor, edit_baton, sess->repos, NULL, 907251881Speter svn_path_uri_decode(sess->repos_url, pool), sess->fs_path->data, 908251881Speter revprop_table, deltify_etc, deb, NULL, NULL, pool); 909251881Speter} 910251881Speter 911251881Speter 912251881Speterstatic svn_error_t * 913251881Spetersvn_ra_local__get_mergeinfo(svn_ra_session_t *session, 914251881Speter svn_mergeinfo_catalog_t *catalog, 915251881Speter const apr_array_header_t *paths, 916251881Speter svn_revnum_t revision, 917251881Speter svn_mergeinfo_inheritance_t inherit, 918251881Speter svn_boolean_t include_descendants, 919251881Speter apr_pool_t *pool) 920251881Speter{ 921251881Speter svn_ra_local__session_baton_t *sess = session->priv; 922251881Speter svn_mergeinfo_catalog_t tmp_catalog; 923251881Speter int i; 924251881Speter apr_array_header_t *abs_paths = 925251881Speter apr_array_make(pool, 0, sizeof(const char *)); 926251881Speter 927251881Speter for (i = 0; i < paths->nelts; i++) 928251881Speter { 929251881Speter const char *relative_path = APR_ARRAY_IDX(paths, i, const char *); 930251881Speter APR_ARRAY_PUSH(abs_paths, const char *) = 931251881Speter svn_fspath__join(sess->fs_path->data, relative_path, pool); 932251881Speter } 933251881Speter 934251881Speter SVN_ERR(svn_repos_fs_get_mergeinfo(&tmp_catalog, sess->repos, abs_paths, 935251881Speter revision, inherit, include_descendants, 936251881Speter NULL, NULL, pool)); 937251881Speter if (apr_hash_count(tmp_catalog) > 0) 938251881Speter SVN_ERR(svn_mergeinfo__remove_prefix_from_catalog(catalog, 939251881Speter tmp_catalog, 940251881Speter sess->fs_path->data, 941251881Speter pool)); 942251881Speter else 943251881Speter *catalog = NULL; 944251881Speter 945251881Speter return SVN_NO_ERROR; 946251881Speter} 947251881Speter 948251881Speter 949251881Speterstatic svn_error_t * 950251881Spetersvn_ra_local__do_update(svn_ra_session_t *session, 951251881Speter const svn_ra_reporter3_t **reporter, 952251881Speter void **report_baton, 953251881Speter svn_revnum_t update_revision, 954251881Speter const char *update_target, 955251881Speter svn_depth_t depth, 956251881Speter svn_boolean_t send_copyfrom_args, 957251881Speter svn_boolean_t ignore_ancestry, 958251881Speter const svn_delta_editor_t *update_editor, 959251881Speter void *update_baton, 960251881Speter apr_pool_t *result_pool, 961251881Speter apr_pool_t *scratch_pool) 962251881Speter{ 963251881Speter return make_reporter(session, 964251881Speter reporter, 965251881Speter report_baton, 966251881Speter update_revision, 967251881Speter update_target, 968251881Speter NULL, 969251881Speter TRUE, 970251881Speter depth, 971251881Speter send_copyfrom_args, 972251881Speter ignore_ancestry, 973251881Speter update_editor, 974251881Speter update_baton, 975251881Speter result_pool, scratch_pool); 976251881Speter} 977251881Speter 978251881Speter 979251881Speterstatic svn_error_t * 980251881Spetersvn_ra_local__do_switch(svn_ra_session_t *session, 981251881Speter const svn_ra_reporter3_t **reporter, 982251881Speter void **report_baton, 983251881Speter svn_revnum_t update_revision, 984251881Speter const char *update_target, 985251881Speter svn_depth_t depth, 986251881Speter const char *switch_url, 987251881Speter svn_boolean_t send_copyfrom_args, 988251881Speter svn_boolean_t ignore_ancestry, 989251881Speter const svn_delta_editor_t *update_editor, 990251881Speter void *update_baton, 991251881Speter apr_pool_t *result_pool, 992251881Speter apr_pool_t *scratch_pool) 993251881Speter{ 994251881Speter return make_reporter(session, 995251881Speter reporter, 996251881Speter report_baton, 997251881Speter update_revision, 998251881Speter update_target, 999251881Speter switch_url, 1000251881Speter TRUE /* text_deltas */, 1001251881Speter depth, 1002251881Speter send_copyfrom_args, 1003251881Speter ignore_ancestry, 1004251881Speter update_editor, 1005251881Speter update_baton, 1006251881Speter result_pool, scratch_pool); 1007251881Speter} 1008251881Speter 1009251881Speter 1010251881Speterstatic svn_error_t * 1011251881Spetersvn_ra_local__do_status(svn_ra_session_t *session, 1012251881Speter const svn_ra_reporter3_t **reporter, 1013251881Speter void **report_baton, 1014251881Speter const char *status_target, 1015251881Speter svn_revnum_t revision, 1016251881Speter svn_depth_t depth, 1017251881Speter const svn_delta_editor_t *status_editor, 1018251881Speter void *status_baton, 1019251881Speter apr_pool_t *pool) 1020251881Speter{ 1021251881Speter return make_reporter(session, 1022251881Speter reporter, 1023251881Speter report_baton, 1024251881Speter revision, 1025251881Speter status_target, 1026251881Speter NULL, 1027251881Speter FALSE, 1028251881Speter depth, 1029251881Speter FALSE, 1030251881Speter FALSE, 1031251881Speter status_editor, 1032251881Speter status_baton, 1033251881Speter pool, pool); 1034251881Speter} 1035251881Speter 1036251881Speter 1037251881Speterstatic svn_error_t * 1038251881Spetersvn_ra_local__do_diff(svn_ra_session_t *session, 1039251881Speter const svn_ra_reporter3_t **reporter, 1040251881Speter void **report_baton, 1041251881Speter svn_revnum_t update_revision, 1042251881Speter const char *update_target, 1043251881Speter svn_depth_t depth, 1044251881Speter svn_boolean_t ignore_ancestry, 1045251881Speter svn_boolean_t text_deltas, 1046251881Speter const char *switch_url, 1047251881Speter const svn_delta_editor_t *update_editor, 1048251881Speter void *update_baton, 1049251881Speter apr_pool_t *pool) 1050251881Speter{ 1051251881Speter return make_reporter(session, 1052251881Speter reporter, 1053251881Speter report_baton, 1054251881Speter update_revision, 1055251881Speter update_target, 1056251881Speter switch_url, 1057251881Speter text_deltas, 1058251881Speter depth, 1059251881Speter FALSE, 1060251881Speter ignore_ancestry, 1061251881Speter update_editor, 1062251881Speter update_baton, 1063251881Speter pool, pool); 1064251881Speter} 1065251881Speter 1066251881Speter 1067251881Speterstruct log_baton 1068251881Speter{ 1069251881Speter svn_ra_local__session_baton_t *sess; 1070251881Speter svn_log_entry_receiver_t real_cb; 1071251881Speter void *real_baton; 1072251881Speter}; 1073251881Speter 1074251881Speterstatic svn_error_t * 1075251881Speterlog_receiver_wrapper(void *baton, 1076251881Speter svn_log_entry_t *log_entry, 1077251881Speter apr_pool_t *pool) 1078251881Speter{ 1079251881Speter struct log_baton *b = baton; 1080251881Speter svn_ra_local__session_baton_t *sess = b->sess; 1081251881Speter 1082251881Speter if (sess->callbacks->cancel_func) 1083251881Speter SVN_ERR((sess->callbacks->cancel_func)(sess->callback_baton)); 1084251881Speter 1085251881Speter /* For consistency with the other RA layers, replace an empty 1086251881Speter changed-paths hash with a NULL one. 1087251881Speter 1088251881Speter ### Should this be done by svn_ra_get_log2() instead, then? */ 1089251881Speter if ((log_entry->changed_paths2) 1090251881Speter && (apr_hash_count(log_entry->changed_paths2) == 0)) 1091251881Speter { 1092251881Speter log_entry->changed_paths = NULL; 1093251881Speter log_entry->changed_paths2 = NULL; 1094251881Speter } 1095251881Speter 1096251881Speter return b->real_cb(b->real_baton, log_entry, pool); 1097251881Speter} 1098251881Speter 1099251881Speter 1100251881Speterstatic svn_error_t * 1101251881Spetersvn_ra_local__get_log(svn_ra_session_t *session, 1102251881Speter const apr_array_header_t *paths, 1103251881Speter svn_revnum_t start, 1104251881Speter svn_revnum_t end, 1105251881Speter int limit, 1106251881Speter svn_boolean_t discover_changed_paths, 1107251881Speter svn_boolean_t strict_node_history, 1108251881Speter svn_boolean_t include_merged_revisions, 1109251881Speter const apr_array_header_t *revprops, 1110251881Speter svn_log_entry_receiver_t receiver, 1111251881Speter void *receiver_baton, 1112251881Speter apr_pool_t *pool) 1113251881Speter{ 1114251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1115251881Speter struct log_baton lb; 1116251881Speter apr_array_header_t *abs_paths = 1117251881Speter apr_array_make(pool, 0, sizeof(const char *)); 1118251881Speter 1119251881Speter if (paths) 1120251881Speter { 1121251881Speter int i; 1122251881Speter 1123251881Speter for (i = 0; i < paths->nelts; i++) 1124251881Speter { 1125251881Speter const char *relative_path = APR_ARRAY_IDX(paths, i, const char *); 1126251881Speter APR_ARRAY_PUSH(abs_paths, const char *) = 1127251881Speter svn_fspath__join(sess->fs_path->data, relative_path, pool); 1128251881Speter } 1129251881Speter } 1130251881Speter 1131251881Speter lb.real_cb = receiver; 1132251881Speter lb.real_baton = receiver_baton; 1133251881Speter lb.sess = sess; 1134251881Speter receiver = log_receiver_wrapper; 1135251881Speter receiver_baton = &lb; 1136251881Speter 1137251881Speter return svn_repos_get_logs4(sess->repos, 1138251881Speter abs_paths, 1139251881Speter start, 1140251881Speter end, 1141251881Speter limit, 1142251881Speter discover_changed_paths, 1143251881Speter strict_node_history, 1144251881Speter include_merged_revisions, 1145251881Speter revprops, 1146251881Speter NULL, NULL, 1147251881Speter receiver, 1148251881Speter receiver_baton, 1149251881Speter pool); 1150251881Speter} 1151251881Speter 1152251881Speter 1153251881Speterstatic svn_error_t * 1154251881Spetersvn_ra_local__do_check_path(svn_ra_session_t *session, 1155251881Speter const char *path, 1156251881Speter svn_revnum_t revision, 1157251881Speter svn_node_kind_t *kind, 1158251881Speter apr_pool_t *pool) 1159251881Speter{ 1160251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1161251881Speter svn_fs_root_t *root; 1162251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1163251881Speter 1164251881Speter if (! SVN_IS_VALID_REVNUM(revision)) 1165251881Speter SVN_ERR(svn_fs_youngest_rev(&revision, sess->fs, pool)); 1166251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool)); 1167251881Speter return svn_fs_check_path(kind, root, abs_path, pool); 1168251881Speter} 1169251881Speter 1170251881Speter 1171251881Speterstatic svn_error_t * 1172251881Spetersvn_ra_local__stat(svn_ra_session_t *session, 1173251881Speter const char *path, 1174251881Speter svn_revnum_t revision, 1175251881Speter svn_dirent_t **dirent, 1176251881Speter apr_pool_t *pool) 1177251881Speter{ 1178251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1179251881Speter svn_fs_root_t *root; 1180251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1181251881Speter 1182251881Speter if (! SVN_IS_VALID_REVNUM(revision)) 1183251881Speter SVN_ERR(svn_fs_youngest_rev(&revision, sess->fs, pool)); 1184251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool)); 1185251881Speter 1186251881Speter return svn_repos_stat(dirent, root, abs_path, pool); 1187251881Speter} 1188251881Speter 1189251881Speter 1190251881Speter 1191251881Speter 1192299742Sdim/* Obtain the properties for a node, including its 'entry props */ 1193251881Speterstatic svn_error_t * 1194251881Speterget_node_props(apr_hash_t **props, 1195251881Speter svn_fs_root_t *root, 1196251881Speter const char *path, 1197299742Sdim const char *uuid, 1198251881Speter apr_pool_t *result_pool, 1199251881Speter apr_pool_t *scratch_pool) 1200251881Speter{ 1201251881Speter svn_revnum_t cmt_rev; 1202251881Speter const char *cmt_date, *cmt_author; 1203251881Speter 1204251881Speter /* Create a hash with props attached to the fs node. */ 1205299742Sdim SVN_ERR(svn_fs_node_proplist(props, root, path, result_pool)); 1206251881Speter 1207251881Speter /* Now add some non-tweakable metadata to the hash as well... */ 1208251881Speter 1209299742Sdim /* The so-called 'entryprops' with info about CR & friends. */ 1210299742Sdim SVN_ERR(svn_repos_get_committed_info(&cmt_rev, &cmt_date, 1211299742Sdim &cmt_author, root, path, 1212299742Sdim scratch_pool)); 1213251881Speter 1214299742Sdim svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_REV, 1215299742Sdim svn_string_createf(result_pool, "%ld", cmt_rev)); 1216299742Sdim svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_DATE, cmt_date ? 1217299742Sdim svn_string_create(cmt_date, result_pool) : NULL); 1218299742Sdim svn_hash_sets(*props, SVN_PROP_ENTRY_LAST_AUTHOR, cmt_author ? 1219299742Sdim svn_string_create(cmt_author, result_pool) : NULL); 1220299742Sdim svn_hash_sets(*props, SVN_PROP_ENTRY_UUID, 1221299742Sdim svn_string_create(uuid, result_pool)); 1222251881Speter 1223299742Sdim /* We have no 'wcprops' in ra_local, but might someday. */ 1224251881Speter 1225251881Speter return SVN_NO_ERROR; 1226251881Speter} 1227251881Speter 1228251881Speter 1229251881Speter/* Getting just one file. */ 1230251881Speterstatic svn_error_t * 1231251881Spetersvn_ra_local__get_file(svn_ra_session_t *session, 1232251881Speter const char *path, 1233251881Speter svn_revnum_t revision, 1234251881Speter svn_stream_t *stream, 1235251881Speter svn_revnum_t *fetched_rev, 1236251881Speter apr_hash_t **props, 1237251881Speter apr_pool_t *pool) 1238251881Speter{ 1239251881Speter svn_fs_root_t *root; 1240251881Speter svn_stream_t *contents; 1241251881Speter svn_revnum_t youngest_rev; 1242251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1243251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1244251881Speter svn_node_kind_t node_kind; 1245251881Speter 1246251881Speter /* Open the revision's root. */ 1247251881Speter if (! SVN_IS_VALID_REVNUM(revision)) 1248251881Speter { 1249251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest_rev, sess->fs, pool)); 1250251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, youngest_rev, pool)); 1251251881Speter if (fetched_rev != NULL) 1252251881Speter *fetched_rev = youngest_rev; 1253251881Speter } 1254251881Speter else 1255251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool)); 1256251881Speter 1257251881Speter SVN_ERR(svn_fs_check_path(&node_kind, root, abs_path, pool)); 1258251881Speter if (node_kind == svn_node_none) 1259251881Speter { 1260251881Speter return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 1261251881Speter _("'%s' path not found"), abs_path); 1262251881Speter } 1263251881Speter else if (node_kind != svn_node_file) 1264251881Speter { 1265251881Speter return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL, 1266251881Speter _("'%s' is not a file"), abs_path); 1267251881Speter } 1268251881Speter 1269251881Speter if (stream) 1270251881Speter { 1271251881Speter /* Get a stream representing the file's contents. */ 1272251881Speter SVN_ERR(svn_fs_file_contents(&contents, root, abs_path, pool)); 1273251881Speter 1274251881Speter /* Now push data from the fs stream back at the caller's stream. 1275251881Speter Note that this particular RA layer does not computing a 1276251881Speter checksum as we go, and confirming it against the repository's 1277251881Speter checksum when done. That's because it calls 1278251881Speter svn_fs_file_contents() directly, which already checks the 1279251881Speter stored checksum, and all we're doing here is writing bytes in 1280251881Speter a loop. Truly, Nothing Can Go Wrong :-). But RA layers that 1281251881Speter go over a network should confirm the checksum. 1282251881Speter 1283251881Speter Note: we are not supposed to close the passed-in stream, so 1284251881Speter disown the thing. 1285251881Speter */ 1286251881Speter SVN_ERR(svn_stream_copy3(contents, svn_stream_disown(stream, pool), 1287251881Speter sess->callbacks 1288251881Speter ? sess->callbacks->cancel_func : NULL, 1289251881Speter sess->callback_baton, 1290251881Speter pool)); 1291251881Speter } 1292251881Speter 1293251881Speter /* Handle props if requested. */ 1294251881Speter if (props) 1295299742Sdim SVN_ERR(get_node_props(props, root, abs_path, sess->uuid, pool, pool)); 1296251881Speter 1297251881Speter return SVN_NO_ERROR; 1298251881Speter} 1299251881Speter 1300251881Speter 1301251881Speter 1302251881Speter/* Getting a directory's entries */ 1303251881Speterstatic svn_error_t * 1304251881Spetersvn_ra_local__get_dir(svn_ra_session_t *session, 1305251881Speter apr_hash_t **dirents, 1306251881Speter svn_revnum_t *fetched_rev, 1307251881Speter apr_hash_t **props, 1308251881Speter const char *path, 1309251881Speter svn_revnum_t revision, 1310251881Speter apr_uint32_t dirent_fields, 1311251881Speter apr_pool_t *pool) 1312251881Speter{ 1313251881Speter svn_fs_root_t *root; 1314251881Speter svn_revnum_t youngest_rev; 1315251881Speter apr_hash_t *entries; 1316251881Speter apr_hash_index_t *hi; 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 1320251881Speter /* Open the revision's root. */ 1321251881Speter if (! SVN_IS_VALID_REVNUM(revision)) 1322251881Speter { 1323251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest_rev, sess->fs, pool)); 1324251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, youngest_rev, pool)); 1325251881Speter if (fetched_rev != NULL) 1326251881Speter *fetched_rev = youngest_rev; 1327251881Speter } 1328251881Speter else 1329251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, pool)); 1330251881Speter 1331251881Speter if (dirents) 1332251881Speter { 1333299742Sdim apr_pool_t *iterpool = svn_pool_create(pool); 1334251881Speter /* Get the dir's entries. */ 1335251881Speter SVN_ERR(svn_fs_dir_entries(&entries, root, abs_path, pool)); 1336251881Speter 1337251881Speter /* Loop over the fs dirents, and build a hash of general 1338251881Speter svn_dirent_t's. */ 1339251881Speter *dirents = apr_hash_make(pool); 1340251881Speter for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) 1341251881Speter { 1342251881Speter const void *key; 1343251881Speter void *val; 1344251881Speter const char *datestring, *entryname, *fullpath; 1345251881Speter svn_fs_dirent_t *fs_entry; 1346251881Speter svn_dirent_t *entry = svn_dirent_create(pool); 1347251881Speter 1348299742Sdim svn_pool_clear(iterpool); 1349251881Speter 1350251881Speter apr_hash_this(hi, &key, NULL, &val); 1351251881Speter entryname = (const char *) key; 1352251881Speter fs_entry = (svn_fs_dirent_t *) val; 1353251881Speter 1354299742Sdim fullpath = svn_dirent_join(abs_path, entryname, iterpool); 1355251881Speter 1356251881Speter if (dirent_fields & SVN_DIRENT_KIND) 1357251881Speter { 1358251881Speter /* node kind */ 1359251881Speter entry->kind = fs_entry->kind; 1360251881Speter } 1361251881Speter 1362251881Speter if (dirent_fields & SVN_DIRENT_SIZE) 1363251881Speter { 1364251881Speter /* size */ 1365251881Speter if (entry->kind == svn_node_dir) 1366251881Speter entry->size = 0; 1367251881Speter else 1368251881Speter SVN_ERR(svn_fs_file_length(&(entry->size), root, 1369299742Sdim fullpath, iterpool)); 1370251881Speter } 1371251881Speter 1372251881Speter if (dirent_fields & SVN_DIRENT_HAS_PROPS) 1373251881Speter { 1374251881Speter /* has_props? */ 1375299742Sdim SVN_ERR(svn_fs_node_has_props(&entry->has_props, 1376299742Sdim root, fullpath, 1377299742Sdim iterpool)); 1378251881Speter } 1379251881Speter 1380251881Speter if ((dirent_fields & SVN_DIRENT_TIME) 1381251881Speter || (dirent_fields & SVN_DIRENT_LAST_AUTHOR) 1382251881Speter || (dirent_fields & SVN_DIRENT_CREATED_REV)) 1383251881Speter { 1384251881Speter /* created_rev & friends */ 1385251881Speter SVN_ERR(svn_repos_get_committed_info(&(entry->created_rev), 1386251881Speter &datestring, 1387251881Speter &(entry->last_author), 1388299742Sdim root, fullpath, iterpool)); 1389251881Speter if (datestring) 1390251881Speter SVN_ERR(svn_time_from_cstring(&(entry->time), datestring, 1391251881Speter pool)); 1392251881Speter if (entry->last_author) 1393251881Speter entry->last_author = apr_pstrdup(pool, entry->last_author); 1394251881Speter } 1395251881Speter 1396251881Speter /* Store. */ 1397251881Speter svn_hash_sets(*dirents, entryname, entry); 1398251881Speter } 1399299742Sdim svn_pool_destroy(iterpool); 1400251881Speter } 1401251881Speter 1402251881Speter /* Handle props if requested. */ 1403251881Speter if (props) 1404299742Sdim SVN_ERR(get_node_props(props, root, abs_path, sess->uuid, pool, pool)); 1405251881Speter 1406251881Speter return SVN_NO_ERROR; 1407251881Speter} 1408251881Speter 1409251881Speter 1410251881Speterstatic svn_error_t * 1411251881Spetersvn_ra_local__get_locations(svn_ra_session_t *session, 1412251881Speter apr_hash_t **locations, 1413251881Speter const char *path, 1414251881Speter svn_revnum_t peg_revision, 1415251881Speter const apr_array_header_t *location_revisions, 1416251881Speter apr_pool_t *pool) 1417251881Speter{ 1418251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1419251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1420251881Speter return svn_repos_trace_node_locations(sess->fs, locations, abs_path, 1421251881Speter peg_revision, location_revisions, 1422251881Speter NULL, NULL, pool); 1423251881Speter} 1424251881Speter 1425251881Speter 1426251881Speterstatic svn_error_t * 1427251881Spetersvn_ra_local__get_location_segments(svn_ra_session_t *session, 1428251881Speter const char *path, 1429251881Speter svn_revnum_t peg_revision, 1430251881Speter svn_revnum_t start_rev, 1431251881Speter svn_revnum_t end_rev, 1432251881Speter svn_location_segment_receiver_t receiver, 1433251881Speter void *receiver_baton, 1434251881Speter apr_pool_t *pool) 1435251881Speter{ 1436251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1437251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1438251881Speter return svn_repos_node_location_segments(sess->repos, abs_path, 1439251881Speter peg_revision, start_rev, end_rev, 1440251881Speter receiver, receiver_baton, 1441251881Speter NULL, NULL, pool); 1442251881Speter} 1443251881Speter 1444299742Sdimstruct lock_baton_t { 1445299742Sdim svn_ra_lock_callback_t lock_func; 1446299742Sdim void *lock_baton; 1447299742Sdim const char *fs_path; 1448299742Sdim svn_boolean_t is_lock; 1449299742Sdim svn_error_t *cb_err; 1450299742Sdim}; 1451251881Speter 1452299742Sdim/* Implements svn_fs_lock_callback_t. Used by svn_ra_local__lock and 1453299742Sdim svn_ra_local__unlock to forward to supplied callback and record any 1454299742Sdim callback error. */ 1455251881Speterstatic svn_error_t * 1456299742Sdimlock_cb(void *lock_baton, 1457299742Sdim const char *path, 1458299742Sdim const svn_lock_t *lock, 1459299742Sdim svn_error_t *fs_err, 1460299742Sdim apr_pool_t *pool) 1461299742Sdim{ 1462299742Sdim struct lock_baton_t *b = lock_baton; 1463299742Sdim 1464299742Sdim if (b && !b->cb_err && b->lock_func) 1465299742Sdim { 1466299742Sdim path = svn_fspath__skip_ancestor(b->fs_path, path); 1467299742Sdim b->cb_err = b->lock_func(b->lock_baton, path, b->is_lock, lock, fs_err, 1468299742Sdim pool); 1469299742Sdim } 1470299742Sdim 1471299742Sdim return SVN_NO_ERROR; 1472299742Sdim} 1473299742Sdim 1474299742Sdimstatic svn_error_t * 1475251881Spetersvn_ra_local__lock(svn_ra_session_t *session, 1476251881Speter apr_hash_t *path_revs, 1477251881Speter const char *comment, 1478251881Speter svn_boolean_t force, 1479251881Speter svn_ra_lock_callback_t lock_func, 1480251881Speter void *lock_baton, 1481251881Speter apr_pool_t *pool) 1482251881Speter{ 1483251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1484299742Sdim apr_hash_t *targets = apr_hash_make(pool); 1485251881Speter apr_hash_index_t *hi; 1486299742Sdim svn_error_t *err; 1487299742Sdim struct lock_baton_t baton = {0}; 1488251881Speter 1489251881Speter /* A username is absolutely required to lock a path. */ 1490251881Speter SVN_ERR(get_username(session, pool)); 1491251881Speter 1492251881Speter for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi)) 1493251881Speter { 1494299742Sdim const char *abs_path = svn_fspath__join(sess->fs_path->data, 1495299742Sdim apr_hash_this_key(hi), pool); 1496299742Sdim svn_revnum_t current_rev = *(svn_revnum_t *)apr_hash_this_val(hi); 1497299742Sdim svn_fs_lock_target_t *target = svn_fs_lock_target_create(NULL, 1498299742Sdim current_rev, 1499299742Sdim pool); 1500251881Speter 1501299742Sdim svn_hash_sets(targets, abs_path, target); 1502299742Sdim } 1503251881Speter 1504299742Sdim baton.lock_func = lock_func; 1505299742Sdim baton.lock_baton = lock_baton; 1506299742Sdim baton.fs_path = sess->fs_path->data; 1507299742Sdim baton.is_lock = TRUE; 1508299742Sdim baton.cb_err = SVN_NO_ERROR; 1509251881Speter 1510299742Sdim err = svn_repos_fs_lock_many(sess->repos, targets, comment, 1511299742Sdim FALSE /* not DAV comment */, 1512299742Sdim 0 /* no expiration */, force, 1513299742Sdim lock_cb, &baton, 1514299742Sdim pool, pool); 1515251881Speter 1516299742Sdim if (err && baton.cb_err) 1517299742Sdim svn_error_compose(err, baton.cb_err); 1518299742Sdim else if (!err) 1519299742Sdim err = baton.cb_err; 1520251881Speter 1521299742Sdim return svn_error_trace(err); 1522251881Speter} 1523251881Speter 1524251881Speter 1525251881Speterstatic svn_error_t * 1526251881Spetersvn_ra_local__unlock(svn_ra_session_t *session, 1527251881Speter apr_hash_t *path_tokens, 1528251881Speter svn_boolean_t force, 1529251881Speter svn_ra_lock_callback_t lock_func, 1530251881Speter void *lock_baton, 1531251881Speter apr_pool_t *pool) 1532251881Speter{ 1533251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1534299742Sdim apr_hash_t *targets = apr_hash_make(pool); 1535251881Speter apr_hash_index_t *hi; 1536299742Sdim svn_error_t *err; 1537299742Sdim struct lock_baton_t baton = {0}; 1538251881Speter 1539251881Speter /* A username is absolutely required to unlock a path. */ 1540251881Speter SVN_ERR(get_username(session, pool)); 1541251881Speter 1542251881Speter for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi)) 1543251881Speter { 1544299742Sdim const char *abs_path = svn_fspath__join(sess->fs_path->data, 1545299742Sdim apr_hash_this_key(hi), pool); 1546299742Sdim const char *token = apr_hash_this_val(hi); 1547251881Speter 1548299742Sdim svn_hash_sets(targets, abs_path, token); 1549299742Sdim } 1550251881Speter 1551299742Sdim baton.lock_func = lock_func; 1552299742Sdim baton.lock_baton = lock_baton; 1553299742Sdim baton.fs_path = sess->fs_path->data; 1554299742Sdim baton.is_lock = FALSE; 1555299742Sdim baton.cb_err = SVN_NO_ERROR; 1556251881Speter 1557299742Sdim err = svn_repos_fs_unlock_many(sess->repos, targets, force, lock_cb, &baton, 1558299742Sdim pool, pool); 1559251881Speter 1560299742Sdim if (err && baton.cb_err) 1561299742Sdim svn_error_compose(err, baton.cb_err); 1562299742Sdim else if (!err) 1563299742Sdim err = baton.cb_err; 1564251881Speter 1565299742Sdim return svn_error_trace(err); 1566251881Speter} 1567251881Speter 1568251881Speter 1569251881Speter 1570251881Speterstatic svn_error_t * 1571251881Spetersvn_ra_local__get_lock(svn_ra_session_t *session, 1572251881Speter svn_lock_t **lock, 1573251881Speter const char *path, 1574251881Speter apr_pool_t *pool) 1575251881Speter{ 1576251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1577251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1578251881Speter return svn_fs_get_lock(lock, sess->fs, abs_path, pool); 1579251881Speter} 1580251881Speter 1581251881Speter 1582251881Speter 1583251881Speterstatic svn_error_t * 1584251881Spetersvn_ra_local__get_locks(svn_ra_session_t *session, 1585251881Speter apr_hash_t **locks, 1586251881Speter const char *path, 1587251881Speter svn_depth_t depth, 1588251881Speter apr_pool_t *pool) 1589251881Speter{ 1590251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1591251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1592251881Speter 1593251881Speter /* Kinda silly to call the repos wrapper, since we have no authz 1594251881Speter func to give it. But heck, why not. */ 1595251881Speter return svn_repos_fs_get_locks2(locks, sess->repos, abs_path, depth, 1596251881Speter NULL, NULL, pool); 1597251881Speter} 1598251881Speter 1599251881Speter 1600251881Speterstatic svn_error_t * 1601251881Spetersvn_ra_local__replay(svn_ra_session_t *session, 1602251881Speter svn_revnum_t revision, 1603251881Speter svn_revnum_t low_water_mark, 1604251881Speter svn_boolean_t send_deltas, 1605251881Speter const svn_delta_editor_t *editor, 1606251881Speter void *edit_baton, 1607251881Speter apr_pool_t *pool) 1608251881Speter{ 1609251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1610251881Speter svn_fs_root_t *root; 1611251881Speter 1612251881Speter SVN_ERR(svn_fs_revision_root(&root, svn_repos_fs(sess->repos), 1613251881Speter revision, pool)); 1614251881Speter return svn_repos_replay2(root, sess->fs_path->data, low_water_mark, 1615251881Speter send_deltas, editor, edit_baton, NULL, NULL, 1616251881Speter pool); 1617251881Speter} 1618251881Speter 1619251881Speter 1620251881Speterstatic svn_error_t * 1621251881Spetersvn_ra_local__replay_range(svn_ra_session_t *session, 1622251881Speter svn_revnum_t start_revision, 1623251881Speter svn_revnum_t end_revision, 1624251881Speter svn_revnum_t low_water_mark, 1625251881Speter svn_boolean_t send_deltas, 1626251881Speter svn_ra_replay_revstart_callback_t revstart_func, 1627251881Speter svn_ra_replay_revfinish_callback_t revfinish_func, 1628251881Speter void *replay_baton, 1629251881Speter apr_pool_t *pool) 1630251881Speter{ 1631251881Speter return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL); 1632251881Speter} 1633251881Speter 1634251881Speter 1635251881Speterstatic svn_error_t * 1636251881Spetersvn_ra_local__has_capability(svn_ra_session_t *session, 1637251881Speter svn_boolean_t *has, 1638251881Speter const char *capability, 1639251881Speter apr_pool_t *pool) 1640251881Speter{ 1641251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1642251881Speter 1643251881Speter if (strcmp(capability, SVN_RA_CAPABILITY_DEPTH) == 0 1644251881Speter || strcmp(capability, SVN_RA_CAPABILITY_LOG_REVPROPS) == 0 1645251881Speter || strcmp(capability, SVN_RA_CAPABILITY_PARTIAL_REPLAY) == 0 1646251881Speter || strcmp(capability, SVN_RA_CAPABILITY_COMMIT_REVPROPS) == 0 1647251881Speter || strcmp(capability, SVN_RA_CAPABILITY_ATOMIC_REVPROPS) == 0 1648251881Speter || strcmp(capability, SVN_RA_CAPABILITY_INHERITED_PROPS) == 0 1649251881Speter || strcmp(capability, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS) == 0 1650251881Speter || strcmp(capability, SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE) == 0 1651251881Speter ) 1652251881Speter { 1653251881Speter *has = TRUE; 1654251881Speter } 1655251881Speter else if (strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO) == 0) 1656251881Speter { 1657251881Speter /* With mergeinfo, the code's capabilities may not reflect the 1658251881Speter repository's, so inquire further. */ 1659251881Speter SVN_ERR(svn_repos_has_capability(sess->repos, has, 1660251881Speter SVN_REPOS_CAPABILITY_MERGEINFO, 1661251881Speter pool)); 1662251881Speter } 1663251881Speter else /* Don't know any other capabilities, so error. */ 1664251881Speter { 1665251881Speter return svn_error_createf 1666251881Speter (SVN_ERR_UNKNOWN_CAPABILITY, NULL, 1667251881Speter _("Don't know anything about capability '%s'"), capability); 1668251881Speter } 1669251881Speter 1670251881Speter return SVN_NO_ERROR; 1671251881Speter} 1672251881Speter 1673251881Speterstatic svn_error_t * 1674251881Spetersvn_ra_local__get_deleted_rev(svn_ra_session_t *session, 1675251881Speter const char *path, 1676251881Speter svn_revnum_t peg_revision, 1677251881Speter svn_revnum_t end_revision, 1678251881Speter svn_revnum_t *revision_deleted, 1679251881Speter apr_pool_t *pool) 1680251881Speter{ 1681251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1682251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, pool); 1683251881Speter 1684251881Speter SVN_ERR(svn_repos_deleted_rev(sess->fs, 1685251881Speter abs_path, 1686251881Speter peg_revision, 1687251881Speter end_revision, 1688251881Speter revision_deleted, 1689251881Speter pool)); 1690251881Speter 1691251881Speter return SVN_NO_ERROR; 1692251881Speter} 1693251881Speter 1694251881Speterstatic svn_error_t * 1695251881Spetersvn_ra_local__get_inherited_props(svn_ra_session_t *session, 1696251881Speter apr_array_header_t **iprops, 1697251881Speter const char *path, 1698251881Speter svn_revnum_t revision, 1699251881Speter apr_pool_t *result_pool, 1700251881Speter apr_pool_t *scratch_pool) 1701251881Speter{ 1702251881Speter svn_fs_root_t *root; 1703251881Speter svn_revnum_t youngest_rev; 1704251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1705251881Speter const char *abs_path = svn_fspath__join(sess->fs_path->data, path, 1706251881Speter scratch_pool); 1707251881Speter svn_node_kind_t node_kind; 1708251881Speter 1709251881Speter /* Open the revision's root. */ 1710251881Speter if (! SVN_IS_VALID_REVNUM(revision)) 1711251881Speter { 1712251881Speter SVN_ERR(svn_fs_youngest_rev(&youngest_rev, sess->fs, scratch_pool)); 1713251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, youngest_rev, 1714251881Speter scratch_pool)); 1715251881Speter } 1716251881Speter else 1717251881Speter { 1718251881Speter SVN_ERR(svn_fs_revision_root(&root, sess->fs, revision, scratch_pool)); 1719251881Speter } 1720251881Speter 1721251881Speter SVN_ERR(svn_fs_check_path(&node_kind, root, abs_path, scratch_pool)); 1722251881Speter if (node_kind == svn_node_none) 1723251881Speter { 1724251881Speter return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 1725251881Speter _("'%s' path not found"), abs_path); 1726251881Speter } 1727251881Speter 1728299742Sdim return svn_error_trace( 1729299742Sdim svn_repos_fs_get_inherited_props(iprops, root, abs_path, 1730299742Sdim NULL /* propname */, 1731299742Sdim NULL, NULL /* auth */, 1732299742Sdim result_pool, scratch_pool)); 1733251881Speter} 1734251881Speter 1735251881Speterstatic svn_error_t * 1736251881Spetersvn_ra_local__register_editor_shim_callbacks(svn_ra_session_t *session, 1737251881Speter svn_delta_shim_callbacks_t *callbacks) 1738251881Speter{ 1739251881Speter /* This is currenly a no-op, since we don't provide our own editor, just 1740251881Speter use the one the libsvn_repos hands back to us. */ 1741251881Speter return SVN_NO_ERROR; 1742251881Speter} 1743251881Speter 1744251881Speter 1745251881Speterstatic svn_error_t * 1746251881Spetersvn_ra_local__get_commit_ev2(svn_editor_t **editor, 1747251881Speter svn_ra_session_t *session, 1748251881Speter apr_hash_t *revprops, 1749251881Speter svn_commit_callback2_t commit_cb, 1750251881Speter void *commit_baton, 1751251881Speter apr_hash_t *lock_tokens, 1752251881Speter svn_boolean_t keep_locks, 1753251881Speter svn_ra__provide_base_cb_t provide_base_cb, 1754251881Speter svn_ra__provide_props_cb_t provide_props_cb, 1755251881Speter svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb, 1756251881Speter void *cb_baton, 1757251881Speter svn_cancel_func_t cancel_func, 1758251881Speter void *cancel_baton, 1759251881Speter apr_pool_t *result_pool, 1760251881Speter apr_pool_t *scratch_pool) 1761251881Speter{ 1762251881Speter svn_ra_local__session_baton_t *sess = session->priv; 1763251881Speter struct deltify_etc_baton *deb = apr_palloc(result_pool, sizeof(*deb)); 1764251881Speter 1765299742Sdim remap_commit_callback(&commit_cb, &commit_baton, session, 1766299742Sdim commit_cb, commit_baton, result_pool); 1767299742Sdim 1768251881Speter /* NOTE: the RA callbacks are ignored. We pass everything directly to 1769251881Speter the REPOS editor. */ 1770251881Speter 1771251881Speter /* Prepare the baton for deltify_etc() */ 1772251881Speter deb->fs = sess->fs; 1773251881Speter deb->repos = sess->repos; 1774251881Speter deb->fspath_base = sess->fs_path->data; 1775251881Speter if (! keep_locks) 1776251881Speter deb->lock_tokens = lock_tokens; 1777251881Speter else 1778251881Speter deb->lock_tokens = NULL; 1779251881Speter deb->commit_cb = commit_cb; 1780251881Speter deb->commit_baton = commit_baton; 1781251881Speter 1782251881Speter /* Ensure there is a username (and an FS access context) associated with 1783251881Speter the session and its FS handle. */ 1784251881Speter SVN_ERR(get_username(session, scratch_pool)); 1785251881Speter 1786251881Speter /* If there are lock tokens to add, do so. */ 1787251881Speter SVN_ERR(apply_lock_tokens(sess->fs, sess->fs_path->data, lock_tokens, 1788251881Speter session->pool, scratch_pool)); 1789251881Speter 1790251881Speter /* Copy the REVPROPS and insert the author/username. */ 1791251881Speter revprops = apr_hash_copy(scratch_pool, revprops); 1792251881Speter svn_hash_sets(revprops, SVN_PROP_REVISION_AUTHOR, 1793251881Speter svn_string_create(sess->username, scratch_pool)); 1794251881Speter 1795251881Speter return svn_error_trace(svn_repos__get_commit_ev2( 1796251881Speter editor, sess->repos, NULL /* authz */, 1797251881Speter NULL /* authz_repos_name */, NULL /* authz_user */, 1798251881Speter revprops, 1799251881Speter deltify_etc, deb, cancel_func, cancel_baton, 1800251881Speter result_pool, scratch_pool)); 1801251881Speter} 1802251881Speter 1803251881Speter/*----------------------------------------------------------------*/ 1804251881Speter 1805251881Speterstatic const svn_version_t * 1806251881Speterra_local_version(void) 1807251881Speter{ 1808251881Speter SVN_VERSION_BODY; 1809251881Speter} 1810251881Speter 1811251881Speter/** The ra_vtable **/ 1812251881Speter 1813251881Speterstatic const svn_ra__vtable_t ra_local_vtable = 1814251881Speter{ 1815251881Speter ra_local_version, 1816251881Speter svn_ra_local__get_description, 1817251881Speter svn_ra_local__get_schemes, 1818251881Speter svn_ra_local__open, 1819299742Sdim svn_ra_local__dup_session, 1820251881Speter svn_ra_local__reparent, 1821251881Speter svn_ra_local__get_session_url, 1822251881Speter svn_ra_local__get_latest_revnum, 1823251881Speter svn_ra_local__get_dated_revision, 1824251881Speter svn_ra_local__change_rev_prop, 1825251881Speter svn_ra_local__rev_proplist, 1826251881Speter svn_ra_local__rev_prop, 1827251881Speter svn_ra_local__get_commit_editor, 1828251881Speter svn_ra_local__get_file, 1829251881Speter svn_ra_local__get_dir, 1830251881Speter svn_ra_local__get_mergeinfo, 1831251881Speter svn_ra_local__do_update, 1832251881Speter svn_ra_local__do_switch, 1833251881Speter svn_ra_local__do_status, 1834251881Speter svn_ra_local__do_diff, 1835251881Speter svn_ra_local__get_log, 1836251881Speter svn_ra_local__do_check_path, 1837251881Speter svn_ra_local__stat, 1838251881Speter svn_ra_local__get_uuid, 1839251881Speter svn_ra_local__get_repos_root, 1840251881Speter svn_ra_local__get_locations, 1841251881Speter svn_ra_local__get_location_segments, 1842251881Speter svn_ra_local__get_file_revs, 1843251881Speter svn_ra_local__lock, 1844251881Speter svn_ra_local__unlock, 1845251881Speter svn_ra_local__get_lock, 1846251881Speter svn_ra_local__get_locks, 1847251881Speter svn_ra_local__replay, 1848251881Speter svn_ra_local__has_capability, 1849251881Speter svn_ra_local__replay_range, 1850251881Speter svn_ra_local__get_deleted_rev, 1851251881Speter svn_ra_local__register_editor_shim_callbacks, 1852251881Speter svn_ra_local__get_inherited_props, 1853251881Speter svn_ra_local__get_commit_ev2 1854251881Speter}; 1855251881Speter 1856251881Speter 1857251881Speter/*----------------------------------------------------------------*/ 1858251881Speter 1859251881Speter/** The One Public Routine, called by libsvn_ra **/ 1860251881Speter 1861251881Spetersvn_error_t * 1862251881Spetersvn_ra_local__init(const svn_version_t *loader_version, 1863251881Speter const svn_ra__vtable_t **vtable, 1864251881Speter apr_pool_t *pool) 1865251881Speter{ 1866251881Speter static const svn_version_checklist_t checklist[] = 1867251881Speter { 1868251881Speter { "svn_subr", svn_subr_version }, 1869251881Speter { "svn_delta", svn_delta_version }, 1870251881Speter { "svn_repos", svn_repos_version }, 1871251881Speter { "svn_fs", svn_fs_version }, 1872251881Speter { NULL, NULL } 1873251881Speter }; 1874251881Speter 1875251881Speter 1876251881Speter /* Simplified version check to make sure we can safely use the 1877251881Speter VTABLE parameter. The RA loader does a more exhaustive check. */ 1878251881Speter if (loader_version->major != SVN_VER_MAJOR) 1879251881Speter return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL, 1880251881Speter _("Unsupported RA loader version (%d) for " 1881251881Speter "ra_local"), 1882251881Speter loader_version->major); 1883251881Speter 1884262253Speter SVN_ERR(svn_ver_check_list2(ra_local_version(), checklist, svn_ver_equal)); 1885251881Speter 1886251881Speter#ifndef SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL 1887299742Sdim /* This means the library was loaded as a DSO, so use the DSO pool. */ 1888299742Sdim SVN_ERR(svn_fs_initialize(svn_dso__pool())); 1889251881Speter#endif 1890251881Speter 1891251881Speter *vtable = &ra_local_vtable; 1892251881Speter 1893251881Speter return SVN_NO_ERROR; 1894251881Speter} 1895251881Speter 1896251881Speter/* Compatibility wrapper for the 1.1 and before API. */ 1897251881Speter#define NAME "ra_local" 1898251881Speter#define DESCRIPTION RA_LOCAL_DESCRIPTION 1899251881Speter#define VTBL ra_local_vtable 1900251881Speter#define INITFUNC svn_ra_local__init 1901251881Speter#define COMPAT_INITFUNC svn_ra_local_init 1902251881Speter#include "../libsvn_ra/wrapper_template.h" 1903